From d0bdb374a04f93644758439c9d8fad1e81415f13 Mon Sep 17 00:00:00 2001 From: shunf4 Date: Wed, 6 Oct 2021 18:47:59 +0800 Subject: [PATCH 001/137] add flag -fno-sized-deallocation, eliminating _ZdlPvm --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8632b38..da89682 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,6 +118,7 @@ target_compile_options(squawk PRIVATE "-Wall;-Wextra" "$<$:-g>" "$<$:-O3>" + "-fno-sized-deallocation" # for eliminating _ZdlPvm ) endif(CMAKE_COMPILER_IS_GNUCXX) From 67e5f9744ef1fc94e347bf3f3684312c56c98ea6 Mon Sep 17 00:00:00 2001 From: shunf4 Date: Wed, 6 Oct 2021 18:50:00 +0800 Subject: [PATCH 002/137] fix ci macos matrix item --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 9b20f3b..30a8125 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -65,7 +65,7 @@ for: - matrix: only: - - image: macOS + - image: macOS-Mojave install: - brew install lmdb imagemagick boost From a53126d8bca5fc9a6a4848c63e7fe5b241b86ba0 Mon Sep 17 00:00:00 2001 From: shunf4 Date: Wed, 6 Oct 2021 22:04:29 +0800 Subject: [PATCH 003/137] messages may have the same timestamp, put MDB_DUPSORT flag with order db --- core/archive.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/archive.cpp b/core/archive.cpp index 2582ff9..bfb4b20 100644 --- a/core/archive.cpp +++ b/core/archive.cpp @@ -64,7 +64,7 @@ void Core::Archive::open(const QString& account) MDB_txn *txn; mdb_txn_begin(environment, NULL, 0, &txn); mdb_dbi_open(txn, "main", MDB_CREATE, &main); - mdb_dbi_open(txn, "order", MDB_CREATE | MDB_INTEGERKEY, &order); + mdb_dbi_open(txn, "order", MDB_CREATE | MDB_INTEGERKEY | MDB_INTEGERDUP | MDB_DUPSORT, &order); mdb_dbi_open(txn, "stats", MDB_CREATE, &stats); mdb_dbi_open(txn, "avatars", MDB_CREATE, &avatars); mdb_dbi_open(txn, "sid", MDB_CREATE, &sid); From ebeb4089ebf49c1509a0a782cbcc6468643aacb4 Mon Sep 17 00:00:00 2001 From: shunf4 Date: Wed, 6 Oct 2021 22:45:10 +0800 Subject: [PATCH 004/137] add fallback icons for buttons --- ui/squawk.ui | 20 +++++++++++--------- ui/utils/badge.cpp | 7 ++++++- ui/widgets/conversation.cpp | 8 ++++++-- ui/widgets/conversation.ui | 20 +++++++++++--------- ui/widgets/messageline/preview.cpp | 7 +++++++ ui/widgets/vcard/vcard.ui | 14 +++++++------- 6 files changed, 48 insertions(+), 28 deletions(-) diff --git a/ui/squawk.ui b/ui/squawk.ui index f6cb300..a4d0258 100644 --- a/ui/squawk.ui +++ b/ui/squawk.ui @@ -184,8 +184,8 @@ - - .. + + :/images/fallback/dark/big/group.svg:/images/fallback/dark/big/group.svg Accounts @@ -193,8 +193,8 @@ - - .. + + :/images/fallback/dark/big/edit-none.svg:/images/fallback/dark/big/edit-none.svg Quit @@ -205,8 +205,8 @@ false - - .. + + :/images/fallback/dark/big/add.svg:/images/fallback/dark/big/add.svg Add contact @@ -217,14 +217,16 @@ false - - .. + + :/images/fallback/dark/big/group-new.svg:/images/fallback/dark/big/group-new.svg Add conference - + + + diff --git a/ui/utils/badge.cpp b/ui/utils/badge.cpp index ef15bd2..7575afc 100644 --- a/ui/utils/badge.cpp +++ b/ui/utils/badge.cpp @@ -32,7 +32,12 @@ Badge::Badge(const QString& p_id, const QString& p_text, const QIcon& icon, QWid setFrameShadow(QFrame::Raised); image->setPixmap(icon.pixmap(25, 25)); - closeButton->setIcon(QIcon::fromTheme("tab-close")); + QIcon tabCloseIcon = QIcon::fromTheme("tab-close"); + if (tabCloseIcon.isNull()) { + tabCloseIcon.addFile(QString::fromUtf8(":/images/fallback/dark/big/edit-none.svg"), QSize(), QIcon::Normal, QIcon::Off); + } + closeButton->setIcon(tabCloseIcon); + closeButton->setMaximumHeight(25); closeButton->setMaximumWidth(25); diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index d003551..1276ff9 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -255,8 +255,12 @@ void Conversation::addAttachedFile(const QString& path) QMimeDatabase db; QMimeType type = db.mimeTypeForFile(path); QFileInfo info(path); - - Badge* badge = new Badge(path, info.fileName(), QIcon::fromTheme(type.iconName())); + + QIcon fileIcon = QIcon::fromTheme(type.iconName()); + if (fileIcon.isNull()) { + fileIcon.addFile(QString::fromUtf8(":/images/fallback/dark/big/mail-attachment.svg"), QSize(), QIcon::Normal, QIcon::Off); + } + Badge* badge = new Badge(path, info.fileName(), fileIcon); connect(badge, &Badge::close, this, &Conversation::onBadgeClose); try { diff --git a/ui/widgets/conversation.ui b/ui/widgets/conversation.ui index bb38666..483375a 100644 --- a/ui/widgets/conversation.ui +++ b/ui/widgets/conversation.ui @@ -271,8 +271,8 @@ - - .. + + :/images/fallback/dark/big/unfavorite.svg:/images/fallback/dark/big/unfavorite.svg true @@ -298,8 +298,8 @@ - - .. + + :/images/fallback/dark/big/mail-attachment.svg:/images/fallback/dark/big/mail-attachment.svg true @@ -312,8 +312,8 @@ - - .. + + :/images/fallback/dark/big/clean.svg:/images/fallback/dark/big/clean.svg true @@ -332,8 +332,8 @@ - - .. + + :/images/fallback/dark/big/send.svg:/images/fallback/dark/big/send.svg true @@ -419,6 +419,8 @@ p, li { white-space: pre-wrap; } - + + + diff --git a/ui/widgets/messageline/preview.cpp b/ui/widgets/messageline/preview.cpp index a64c036..e54fce6 100644 --- a/ui/widgets/messageline/preview.cpp +++ b/ui/widgets/messageline/preview.cpp @@ -164,6 +164,9 @@ void Preview::applyNewSize() break; default: { QIcon icon = QIcon::fromTheme(info.mime.iconName()); + if (icon.isNull()) { + icon.addFile(QString::fromUtf8(":/images/fallback/dark/big/mail-attachment.svg"), QSize(), QIcon::Normal, QIcon::Off); + } widget->setPixmap(icon.pixmap(actualSize)); widget->resize(actualSize); } @@ -264,6 +267,10 @@ void Preview::initializeElements() break; default: { QIcon icon = QIcon::fromTheme(info.mime.iconName()); + if (icon.isNull()) { + icon.addFile(QString::fromUtf8(":/images/fallback/dark/big/mail-attachment.svg"), QSize(), QIcon::Normal, QIcon::Off); + } + widget = new QLabel(parent); widget->setPixmap(icon.pixmap(actualSize)); widget->show(); diff --git a/ui/widgets/vcard/vcard.ui b/ui/widgets/vcard/vcard.ui index 26db8f9..b71d262 100644 --- a/ui/widgets/vcard/vcard.ui +++ b/ui/widgets/vcard/vcard.ui @@ -482,8 +482,8 @@ - - .. + + :/images/fallback/dark/big/user.svg:/images/fallback/dark/big/user.svg @@ -852,8 +852,8 @@ - - .. + + :/images/fallback/dark/big/edit-rename.svg:/images/fallback/dark/big/edit-rename.svg Set avatar @@ -861,8 +861,8 @@ - - .. + + :/images/fallback/dark/big/clean.svg:/images/fallback/dark/big/clean.svg Clear avatar @@ -886,7 +886,7 @@ description - + From 5862f1552ba15bd31daf492f21811c5a1b1a4cab Mon Sep 17 00:00:00 2001 From: shunf4 Date: Wed, 6 Oct 2021 22:52:20 +0800 Subject: [PATCH 005/137] don't save settings on quit, if readSettings() not finished --- core/squawk.cpp | 69 ++++++++++++++++++++++++++----------------------- core/squawk.h | 1 + 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/core/squawk.cpp b/core/squawk.cpp index 6b8af49..d989afc 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -27,7 +27,8 @@ Core::Squawk::Squawk(QObject* parent): accounts(), amap(), network(), - waitingForAccounts(0) + waitingForAccounts(0), + isInitialized(false) #ifdef WITH_KWALLET ,kwallet() #endif @@ -66,39 +67,42 @@ void Core::Squawk::stop() { qDebug("Stopping squawk core.."); network.stop(); - QSettings settings; - settings.beginGroup("core"); - settings.beginWriteArray("accounts"); - SimpleCrypt crypto(passwordHash); - for (std::deque::size_type i = 0; i < accounts.size(); ++i) { - settings.setArrayIndex(i); - Account* acc = accounts[i]; - - Shared::AccountPassword ap = acc->getPasswordType(); - QString password; - - switch (ap) { - case Shared::AccountPassword::plain: - password = acc->getPassword(); - break; - case Shared::AccountPassword::jammed: - password = crypto.encryptToString(acc->getPassword()); - break; - default: - break; + + if (isInitialized) { + QSettings settings; + settings.beginGroup("core"); + settings.beginWriteArray("accounts"); + SimpleCrypt crypto(passwordHash); + for (std::deque::size_type i = 0; i < accounts.size(); ++i) { + settings.setArrayIndex(i); + Account* acc = accounts[i]; + + Shared::AccountPassword ap = acc->getPasswordType(); + QString password; + + switch (ap) { + case Shared::AccountPassword::plain: + password = acc->getPassword(); + break; + case Shared::AccountPassword::jammed: + password = crypto.encryptToString(acc->getPassword()); + break; + default: + break; + } + + settings.setValue("name", acc->getName()); + settings.setValue("server", acc->getServer()); + settings.setValue("login", acc->getLogin()); + settings.setValue("password", password); + settings.setValue("resource", acc->getResource()); + settings.setValue("passwordType", static_cast(ap)); } - - settings.setValue("name", acc->getName()); - settings.setValue("server", acc->getServer()); - settings.setValue("login", acc->getLogin()); - settings.setValue("password", password); - settings.setValue("resource", acc->getResource()); - settings.setValue("passwordType", static_cast(ap)); + settings.endArray(); + settings.endGroup(); + + settings.sync(); } - settings.endArray(); - settings.endGroup(); - - settings.sync(); emit quit(); } @@ -108,6 +112,7 @@ void Core::Squawk::start() qDebug("Starting squawk core.."); readSettings(); + isInitialized = true; network.start(); } diff --git a/core/squawk.h b/core/squawk.h index 338eb40..74b1e1a 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -133,6 +133,7 @@ private: Shared::Availability state; NetworkAccess network; uint8_t waitingForAccounts; + bool isInitialized; #ifdef WITH_KWALLET PSE::KWallet kwallet; From d20fd84d391f7645400c6abb0966097e28a3cc58 Mon Sep 17 00:00:00 2001 From: shunf4 Date: Wed, 6 Oct 2021 22:55:23 +0800 Subject: [PATCH 006/137] respect password type when adding account, preventing loading bad password --- core/squawk.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/squawk.cpp b/core/squawk.cpp index 6b8af49..34cd694 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -118,8 +118,9 @@ void Core::Squawk::newAccountRequest(const QMap& map) QString server = map.value("server").toString(); QString password = map.value("password").toString(); QString resource = map.value("resource").toString(); + int passwordType = map.value("passwordType").toInt(); - addAccount(login, server, password, name, resource, Shared::AccountPassword::plain); + addAccount(login, server, password, name, resource, Shared::Global::fromInt(passwordType)); } void Core::Squawk::addAccount( From a24e8382d1a52db4aa73bd8883055960714684d4 Mon Sep 17 00:00:00 2001 From: shunf4 Date: Wed, 6 Oct 2021 23:01:11 +0800 Subject: [PATCH 007/137] correctly retrieve latest archived messages per XEP-0313 --- core/account.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/account.cpp b/core/account.cpp index 6784674..035299b 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -469,6 +469,14 @@ void Core::Account::onContactNeedHistory(const QString& before, const QString& a query.setAfter(after); } } + if (before.size() == 0 && after.size() == 0) { + // https://xmpp.org/extensions/xep-0313.html#sect-idm46556759682304 + // To request the page at the end of the archive + // (i.e. the most recent messages), include just an + // empty element in the RSM part of the query. + // As defined by RSM, this will return the last page of the archive. + query.setBefore(""); + } qDebug() << "Remote query for" << contact->jid << "from" << after << ", to" << before; } From 3a70df21f87f69ce0ea881d47adfb9026779e6d8 Mon Sep 17 00:00:00 2001 From: shunf4 Date: Wed, 6 Oct 2021 23:09:18 +0800 Subject: [PATCH 008/137] feat: paste image in chat --- ui/widgets/conversation.cpp | 45 ++++++++++++++++++++++++++++++++++++- ui/widgets/conversation.h | 5 +++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index d003551..f0a6be6 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -20,6 +20,7 @@ #include "ui_conversation.h" #include +#include #include #include #include @@ -27,6 +28,9 @@ #include #include #include +#include +#include +#include Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, const QString pJid, const QString pRes, QWidget* parent): QWidget(parent), @@ -47,6 +51,7 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, delegate(new MessageDelegate(this)), manualSliderChange(false), tsb(QApplication::style()->styleHint(QStyle::SH_ScrollBar_Transient) == 1), + pasteImageAction(nullptr), shadow(10, 1, Qt::black, this), contextMenu(new QMenu()) { @@ -75,6 +80,7 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, statusLabel = m_ui->statusLabel; connect(&ker, &KeyEnterReceiver::enterPressed, this, &Conversation::onEnterPressed); + connect(&ker, &KeyEnterReceiver::imagePasted, this, &Conversation::onImagePasted); connect(m_ui->sendButton, &QPushButton::clicked, this, &Conversation::onEnterPressed); connect(m_ui->attachButton, &QPushButton::clicked, this, &Conversation::onAttach); connect(m_ui->clearButton, &QPushButton::clicked, this, &Conversation::onClearButton); @@ -82,7 +88,20 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, this, &Conversation::onTextEditDocSizeChanged); m_ui->messageEditor->installEventFilter(&ker); - + + QAction* pasteImageAction = new QAction(tr("Paste Image"), this); + connect(pasteImageAction, &QAction::triggered, this, &Conversation::onImagePasted); + + m_ui->messageEditor->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_ui->messageEditor, &QTextEdit::customContextMenuRequested, this, [this, pasteImageAction](const QPoint &pos) { + pasteImageAction->setEnabled(Conversation::checkClipboardImage()); + + QMenu *editorMenu = m_ui->messageEditor->createStandardContextMenu(); + editorMenu->addSeparator(); + editorMenu->addAction(pasteImageAction); + + editorMenu->exec(this->m_ui->messageEditor->mapToGlobal(pos)); + }); //line->setAutoFillBackground(false); //if (testAttribute(Qt::WA_TranslucentBackground)) { @@ -183,10 +202,20 @@ bool KeyEnterReceiver::eventFilter(QObject* obj, QEvent* event) } } } + if (k == Qt::Key_V && key->modifiers() & Qt::CTRL) { + if (Conversation::checkClipboardImage()) { + emit imagePasted(); + return true; + } + } } return QObject::eventFilter(obj, event); } +bool Conversation::checkClipboardImage() { + return !QApplication::clipboard()->image().isNull(); +} + QString Conversation::getPalResource() const { return activePalResource; @@ -218,6 +247,20 @@ void Conversation::onEnterPressed() } } +void Conversation::onImagePasted() +{ + QImage image = QApplication::clipboard()->image(); + if (image.isNull()) { + return; + } + QTemporaryFile *tempFile = new QTemporaryFile(QDir::tempPath() + QStringLiteral("/squawk_img_attach_XXXXXX.png"), QApplication::instance()); + tempFile->open(); + image.save(tempFile, "PNG"); + tempFile->close(); + qDebug() << "image on paste temp file: " << tempFile->fileName(); + addAttachedFile(tempFile->fileName()); +} + void Conversation::onAttach() { QFileDialog* d = new QFileDialog(this, tr("Chose a file to send")); diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index b0eb745..5f5d69a 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -60,6 +60,7 @@ protected: signals: void enterPressed(); + void imagePasted(); }; class Conversation : public QWidget @@ -77,6 +78,7 @@ public: void setPalResource(const QString& res); virtual void setAvatar(const QString& path); void setFeedFrames(bool top, bool right, bool bottom, bool left); + static bool checkClipboardImage(); signals: void sendMessage(const Shared::Message& message); @@ -102,6 +104,7 @@ protected: protected slots: void onEnterPressed(); + void onImagePasted(); void onAttach(); void onFileSelected(); void onBadgeClose(); @@ -133,6 +136,8 @@ protected: bool manualSliderChange; bool tsb; //transient scroll bars + QAction* pasteImageAction; + ShadowOverlay shadow; QMenu* contextMenu; }; From 50d710de04bfe5f3bffc660a4baae84a10e8b3a1 Mon Sep 17 00:00:00 2001 From: shunf4 Date: Wed, 13 Oct 2021 11:35:43 +0800 Subject: [PATCH 009/137] remove ./signalcatcher_win32.cpp --- signalcatcher_win32.cpp | 42 ----------------------------------------- 1 file changed, 42 deletions(-) delete mode 100644 signalcatcher_win32.cpp diff --git a/signalcatcher_win32.cpp b/signalcatcher_win32.cpp deleted file mode 100644 index ca7b5a2..0000000 --- a/signalcatcher_win32.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2021 Shunf4 - * - * 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 "signalcatcher.h" -#include - -SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent): - QObject(parent), - app(p_app) -{ -} - -SignalCatcher::~SignalCatcher() -{} - -void SignalCatcher::handleSigInt() -{ -} - -void SignalCatcher::intSignalHandler(int unused) -{ -} - -int SignalCatcher::setup_unix_signal_handlers() -{ - return 0; -} From 52551c1ce0be0a223e388d339389b7a55b159b6c Mon Sep 17 00:00:00 2001 From: shunf4 Date: Wed, 13 Oct 2021 20:06:13 +0800 Subject: [PATCH 010/137] pasteImageAction should be a class member; refactor messageEditor's context menu callback into a member function --- ui/widgets/conversation.cpp | 27 ++++++++++++++------------- ui/widgets/conversation.h | 1 + 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index f0a6be6..3f07a2c 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -51,7 +51,7 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, delegate(new MessageDelegate(this)), manualSliderChange(false), tsb(QApplication::style()->styleHint(QStyle::SH_ScrollBar_Transient) == 1), - pasteImageAction(nullptr), + pasteImageAction(new QAction(tr("Paste Image"), this)), shadow(10, 1, Qt::black, this), contextMenu(new QMenu()) { @@ -88,21 +88,11 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, this, &Conversation::onTextEditDocSizeChanged); m_ui->messageEditor->installEventFilter(&ker); + m_ui->messageEditor->setContextMenuPolicy(Qt::CustomContextMenu); - QAction* pasteImageAction = new QAction(tr("Paste Image"), this); + connect(m_ui->messageEditor, &QTextEdit::customContextMenuRequested, this, &Conversation::onMessageEditorContext); connect(pasteImageAction, &QAction::triggered, this, &Conversation::onImagePasted); - m_ui->messageEditor->setContextMenuPolicy(Qt::CustomContextMenu); - connect(m_ui->messageEditor, &QTextEdit::customContextMenuRequested, this, [this, pasteImageAction](const QPoint &pos) { - pasteImageAction->setEnabled(Conversation::checkClipboardImage()); - - QMenu *editorMenu = m_ui->messageEditor->createStandardContextMenu(); - editorMenu->addSeparator(); - editorMenu->addAction(pasteImageAction); - - editorMenu->exec(this->m_ui->messageEditor->mapToGlobal(pos)); - }); - //line->setAutoFillBackground(false); //if (testAttribute(Qt::WA_TranslucentBackground)) { //m_ui->scrollArea->setAutoFillBackground(false); @@ -486,3 +476,14 @@ void Conversation::onFeedContext(const QPoint& pos) } } } + +void Conversation::onMessageEditorContext(const QPoint& pos) +{ + pasteImageAction->setEnabled(Conversation::checkClipboardImage()); + + QMenu *editorMenu = m_ui->messageEditor->createStandardContextMenu(); + editorMenu->addSeparator(); + editorMenu->addAction(pasteImageAction); + + editorMenu->exec(this->m_ui->messageEditor->mapToGlobal(pos)); +} diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index 5f5d69a..6b5b4bb 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -114,6 +114,7 @@ protected slots: void onFeedMessage(const Shared::Message& msg); void positionShadow(); void onFeedContext(const QPoint &pos); + void onMessageEditorContext(const QPoint &pos); public: const bool isMuc; From 39f2f3d975a1ce38144ef1992506416a934e5005 Mon Sep 17 00:00:00 2001 From: shunf4 Date: Sat, 16 Oct 2021 00:20:31 +0800 Subject: [PATCH 011/137] feat: copy pasted image file to download folder after successful upload --- core/networkaccess.cpp | 27 ++++++++++++++++++++++++++- core/networkaccess.h | 2 +- core/squawk.h | 2 +- ui/squawk.cpp | 2 +- ui/squawk.h | 2 +- ui/widgets/conversation.cpp | 4 ++++ 6 files changed, 34 insertions(+), 5 deletions(-) diff --git a/core/networkaccess.cpp b/core/networkaccess.cpp index 69fe812..c2cd65d 100644 --- a/core/networkaccess.cpp +++ b/core/networkaccess.cpp @@ -378,7 +378,32 @@ void Core::NetworkAccess::onUploadFinished() qDebug() << "upload success for" << url; storage.addFile(upl->messages, upl->url, upl->path); - emit uploadFileComplete(upl->messages, upl->url); + emit uploadFileComplete(upl->messages, upl->url, upl->path); + + // Copy file to Download folder if it is a temp file. See Conversation::onImagePasted. + if (upl->path.startsWith(QDir::tempPath() + QStringLiteral("/squawk_img_attach_")) && upl->path.endsWith(".png")) { + QString err = ""; + QString downloadDirPath = prepareDirectory(upl->messages.front().jid); + if (downloadDirPath.size() > 0) { + QString newPath = downloadDirPath + "/" + upl->path.mid(QDir::tempPath().length() + 1); + + // Copy {TEMPDIR}/squawk_img_attach_XXXXXX.png to Download folder + bool copyResult = QFile::copy(upl->path, newPath); + + if (copyResult) { + // Change storage + storage.setPath(upl->url, newPath); + } else { + err = "copying to " + newPath + " failed"; + } + } else { + err = "Couldn't prepare a directory for file"; + } + + if (err.size() != 0) { + qDebug() << "failed to copy temporary upload file " << upl->path << " to download folder:" << err; + } + } } upl->reply->deleteLater(); diff --git a/core/networkaccess.h b/core/networkaccess.h index 75c189c..89d0633 100644 --- a/core/networkaccess.h +++ b/core/networkaccess.h @@ -58,7 +58,7 @@ public: signals: void loadFileProgress(const std::list& msgs, qreal value, bool up); void loadFileError(const std::list& msgs, const QString& text, bool up); - void uploadFileComplete(const std::list& msgs, const QString& url); + void uploadFileComplete(const std::list& msgs, const QString& url, const QString& path); void downloadFileComplete(const std::list& msgs, const QString& path); public slots: diff --git a/core/squawk.h b/core/squawk.h index 338eb40..3715afe 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -82,7 +82,7 @@ signals: void fileError(const std::list msgs, const QString& error, bool up); void fileProgress(const std::list msgs, qreal value, bool up); void fileDownloadComplete(const std::list msgs, const QString& path); - void fileUploadComplete(const std::list msgs, const QString& path); + void fileUploadComplete(const std::list msgs, const QString& url, const QString& path); void responseVCard(const QString& jid, const Shared::VCard& card); void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap& data); diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 6a0a676..406ee45 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -405,7 +405,7 @@ void Squawk::fileError(const std::list msgs, const QString& rosterModel.fileError(msgs, error, up); } -void Squawk::fileUploadComplete(const std::list msgs, const QString& path) +void Squawk::fileUploadComplete(const std::list msgs, const QString& url, const QString& path) { rosterModel.fileComplete(msgs, true); } diff --git a/ui/squawk.h b/ui/squawk.h index 28389fa..cb93259 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -107,7 +107,7 @@ public slots: void fileError(const std::list msgs, const QString& error, bool up); void fileProgress(const std::list msgs, qreal value, bool up); void fileDownloadComplete(const std::list msgs, const QString& path); - void fileUploadComplete(const std::list msgs, const QString& path); + void fileUploadComplete(const std::list msgs, const QString& url, const QString& path); void responseVCard(const QString& jid, const Shared::VCard& card); void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap& data); void requestPassword(const QString& account); diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 3f07a2c..fcf28c3 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -249,6 +249,10 @@ void Conversation::onImagePasted() tempFile->close(); qDebug() << "image on paste temp file: " << tempFile->fileName(); addAttachedFile(tempFile->fileName()); + + // The file, if successfully uploaded, will be copied to Download folder. + // On application closing, this temporary file will be automatically removed by Qt. + // See Core::NetworkAccess::onUploadFinished. } void Conversation::onAttach() From 7130e674c4d105d3ddefa064ffbf0eb2ef630d20 Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 5 Jan 2022 22:29:34 +0300 Subject: [PATCH 012/137] some warnings fixed, new way of drawing avatars in message line --- core/account.cpp | 5 +- core/adapterFuctions.cpp | 2 +- core/conference.cpp | 2 +- ui/models/accounts.cpp | 5 +- ui/widgets/messageline/feedview.cpp | 56 ++++++++++++++++++++-- ui/widgets/messageline/feedview.h | 1 + ui/widgets/messageline/messagedelegate.cpp | 10 ++-- 7 files changed, 65 insertions(+), 16 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index 035299b..a923690 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -601,6 +601,9 @@ void Core::Account::onClientError(QXmppClient::Error err) errorText = "Policy violation"; break; #endif + default: + errorText = "Unknown Error"; + break; } errorType = "Client stream error"; @@ -837,7 +840,6 @@ void Core::Account::uploadVCard(const Shared::VCard& card) QXmppVCardIq iq; initializeQXmppVCard(iq, card); - bool avatarChanged = false; if (card.getAvatarType() != Shared::Avatar::empty) { QString newPath = card.getAvatarPath(); QString oldPath = getAvatarPath(); @@ -859,7 +861,6 @@ void Core::Account::uploadVCard(const Shared::VCard& card) } } else { data = avatar.readAll(); - avatarChanged = true; } } else { if (avatarType.size() > 0) { diff --git a/core/adapterFuctions.cpp b/core/adapterFuctions.cpp index e2559d8..3d84dfb 100644 --- a/core/adapterFuctions.cpp +++ b/core/adapterFuctions.cpp @@ -264,7 +264,7 @@ void Core::initializeQXmppVCard(QXmppVCardIq& iq, const Shared::VCard& card) { phone.setType(phone.type() | QXmppVCardPhone::Preferred); } } - for (const std::pair& phone : phones) { + for (const std::pair& phone : phones) { phs.push_back(phone.second); } diff --git a/core/conference.cpp b/core/conference.cpp index cda19fd..55280e2 100644 --- a/core/conference.cpp +++ b/core/conference.cpp @@ -356,7 +356,7 @@ Shared::VCard Core::Conference::handleResponseVCard(const QXmppVCardIq& card, co QMap Core::Conference::getAllAvatars() const { QMap result; - for (const std::pair& pair : exParticipants) { + for (const std::pair& pair : exParticipants) { result.insert(pair.first, avatarPath(pair.first) + "." + pair.second.type); } return result; diff --git a/ui/models/accounts.cpp b/ui/models/accounts.cpp index f5ffce8..4343481 100644 --- a/ui/models/accounts.cpp +++ b/ui/models/accounts.cpp @@ -97,7 +97,10 @@ void Models::Accounts::addAccount(Account* account) void Models::Accounts::onAccountChanged(Item* item, int row, int col) { - if (row < accs.size()) { + if (row < 0) { + return; + } + if (static_cast::size_type>(row) < accs.size()) { Account* acc = getAccount(row); if (item != acc) { return; //it means the signal is emitted by one of accounts' children, not exactly him, this model has no interest in that diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index 7bdfb9e..618ecfb 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -31,6 +31,10 @@ constexpr int approximateSingleMessageHeight = 20; constexpr int progressSize = 70; constexpr int dateDeviderMargin = 10; +constexpr int avatarHeight = 50; +constexpr int margin = 6; +constexpr int halfMargin = 3; + const std::set FeedView::geometryChangingRoles = { Models::MessageFeed::Attach, Models::MessageFeed::Text, @@ -334,6 +338,20 @@ void FeedView::paintEvent(QPaintEvent* event) drawDateDevider(option.rect.bottom(), lastDate, painter); } lastDate = currentDate; + + + if ((option.rect.y() < 1) || (index.row() == m->rowCount() - 1)) { + drawAvatar(index, option, painter); + } else { + QString mySender = index.data(Models::MessageFeed::Sender).toString(); + QModelIndex prevIndex = m->index(index.row() + 1, 0, rootIndex()); + if ( + (prevIndex.data(Models::MessageFeed::Sender).toString() != mySender) || + (prevIndex.data(Models::MessageFeed::Date).toDateTime().daysTo(currentDate) != 0) + ) { + drawAvatar(index, option, painter); + } + } } if (!lastDate.isNull() && inZone) { //if after drawing all messages there is still space drawDateDevider(option.rect.bottom(), lastDate, painter); @@ -344,10 +362,6 @@ void FeedView::paintEvent(QPaintEvent* event) del->endClearWidgets(); clearWidgetsMode = false; } - - if (event->rect().height() == vp->height()) { - // draw the blurred drop shadow... - } } void FeedView::drawDateDevider(int top, const QDateTime& date, QPainter& painter) @@ -360,6 +374,40 @@ void FeedView::drawDateDevider(int top, const QDateTime& date, QPainter& painter painter.restore(); } +void FeedView::drawAvatar(const QModelIndex& index, const QStyleOptionViewItem& option, QPainter& painter) +{ + int currentRow = index.row(); + int y = option.rect.y(); + bool firstAttempt = true; + QString mySender = index.data(Models::MessageFeed::Sender).toString(); + QDateTime currentDate = index.data(Models::MessageFeed::Date).toDateTime(); + QIcon icon(index.data(Models::MessageFeed::Avatar).toString()); + while (y < 0 && currentRow > 0) { + QRect rect; + if (firstAttempt) { + firstAttempt = false; + rect = option.rect; + } else { + QModelIndex ci = model()->index(currentRow, 0, rootIndex()); + if ( + (ci.data(Models::MessageFeed::Sender).toString() != mySender) || + (ci.data(Models::MessageFeed::Date).toDateTime().daysTo(currentDate) != 0) + ) { + break; + } + rect = visualRect(ci); + } + y = std::min(0, rect.bottom() - margin - avatarHeight); + --currentRow; + } + if (index.data(Models::MessageFeed::SentByMe).toBool()) { + painter.drawPixmap(option.rect.width() - avatarHeight - margin, y + halfMargin, icon.pixmap(avatarHeight, avatarHeight)); + } else { + painter.drawPixmap(margin, y + halfMargin, icon.pixmap(avatarHeight, avatarHeight)); + } +} + + void FeedView::verticalScrollbarValueChanged(int value) { vo = verticalScrollBar()->maximum() - value; diff --git a/ui/widgets/messageline/feedview.h b/ui/widgets/messageline/feedview.h index 5e08946..e3e57b7 100644 --- a/ui/widgets/messageline/feedview.h +++ b/ui/widgets/messageline/feedview.h @@ -74,6 +74,7 @@ private: bool tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* model, uint32_t totalHeight); void positionProgress(); void drawDateDevider(int top, const QDateTime& date, QPainter& painter); + void drawAvatar(const QModelIndex& index, const QStyleOptionViewItem& option, QPainter& painter); private: struct Hint { diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index 649230e..0fe1ed0 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -104,13 +104,7 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio painter->fillRect(option.rect, option.palette.brush(QPalette::Inactive, QPalette::Highlight)); } - QIcon icon(data.avatar); - - if (data.sentByMe) { - painter->drawPixmap(option.rect.width() - avatarHeight - margin, option.rect.y() + margin / 2, icon.pixmap(avatarHeight, avatarHeight)); - } else { - painter->drawPixmap(margin, option.rect.y() + margin / 2, icon.pixmap(avatarHeight, avatarHeight)); - } + QStyleOptionViewItem opt = option; QRect messageRect = option.rect.adjusted(margin, margin / 2, -(avatarHeight + 2 * margin), -margin / 2); @@ -163,6 +157,7 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio break; //but it's a possible performance problem case Models::uploading: paintPreview(data, painter, opt); + [[fallthrough]]; case Models::downloading: paintBar(getBar(data), painter, data.sentByMe, opt); break; @@ -268,6 +263,7 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel break; case Models::uploading: messageSize.rheight() += Preview::calculateAttachSize(attach.localPath, messageRect).height() + textMargin; + [[fallthrough]]; case Models::downloading: messageSize.rheight() += barHeight + textMargin; break; From 9ac0ca10f34046b9774659b772ff97d2b029bc73 Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 7 Jan 2022 17:02:49 +0300 Subject: [PATCH 013/137] avatar painting is returned to delegate; sender names now are not painted in every message --- ui/widgets/messageline/feedview.cpp | 48 --------- ui/widgets/messageline/feedview.h | 1 - ui/widgets/messageline/messagedelegate.cpp | 113 ++++++++++++++++----- ui/widgets/messageline/messagedelegate.h | 4 + 4 files changed, 93 insertions(+), 73 deletions(-) diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index 618ecfb..1296324 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -338,20 +338,6 @@ void FeedView::paintEvent(QPaintEvent* event) drawDateDevider(option.rect.bottom(), lastDate, painter); } lastDate = currentDate; - - - if ((option.rect.y() < 1) || (index.row() == m->rowCount() - 1)) { - drawAvatar(index, option, painter); - } else { - QString mySender = index.data(Models::MessageFeed::Sender).toString(); - QModelIndex prevIndex = m->index(index.row() + 1, 0, rootIndex()); - if ( - (prevIndex.data(Models::MessageFeed::Sender).toString() != mySender) || - (prevIndex.data(Models::MessageFeed::Date).toDateTime().daysTo(currentDate) != 0) - ) { - drawAvatar(index, option, painter); - } - } } if (!lastDate.isNull() && inZone) { //if after drawing all messages there is still space drawDateDevider(option.rect.bottom(), lastDate, painter); @@ -374,40 +360,6 @@ void FeedView::drawDateDevider(int top, const QDateTime& date, QPainter& painter painter.restore(); } -void FeedView::drawAvatar(const QModelIndex& index, const QStyleOptionViewItem& option, QPainter& painter) -{ - int currentRow = index.row(); - int y = option.rect.y(); - bool firstAttempt = true; - QString mySender = index.data(Models::MessageFeed::Sender).toString(); - QDateTime currentDate = index.data(Models::MessageFeed::Date).toDateTime(); - QIcon icon(index.data(Models::MessageFeed::Avatar).toString()); - while (y < 0 && currentRow > 0) { - QRect rect; - if (firstAttempt) { - firstAttempt = false; - rect = option.rect; - } else { - QModelIndex ci = model()->index(currentRow, 0, rootIndex()); - if ( - (ci.data(Models::MessageFeed::Sender).toString() != mySender) || - (ci.data(Models::MessageFeed::Date).toDateTime().daysTo(currentDate) != 0) - ) { - break; - } - rect = visualRect(ci); - } - y = std::min(0, rect.bottom() - margin - avatarHeight); - --currentRow; - } - if (index.data(Models::MessageFeed::SentByMe).toBool()) { - painter.drawPixmap(option.rect.width() - avatarHeight - margin, y + halfMargin, icon.pixmap(avatarHeight, avatarHeight)); - } else { - painter.drawPixmap(margin, y + halfMargin, icon.pixmap(avatarHeight, avatarHeight)); - } -} - - void FeedView::verticalScrollbarValueChanged(int value) { vo = verticalScrollBar()->maximum() - value; diff --git a/ui/widgets/messageline/feedview.h b/ui/widgets/messageline/feedview.h index e3e57b7..5e08946 100644 --- a/ui/widgets/messageline/feedview.h +++ b/ui/widgets/messageline/feedview.h @@ -74,7 +74,6 @@ private: bool tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* model, uint32_t totalHeight); void positionProgress(); void drawDateDevider(int top, const QDateTime& date, QPainter& painter); - void drawAvatar(const QModelIndex& index, const QStyleOptionViewItem& option, QPainter& painter); private: struct Hint { diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index 0fe1ed0..0cf449a 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "messagedelegate.h" #include "messagefeed.h" @@ -104,7 +105,10 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio painter->fillRect(option.rect, option.palette.brush(QPalette::Inactive, QPalette::Highlight)); } - + bool ntds = needToDrawSender(index, data); + if (ntds || option.rect.y() < 1) { + paintAvatar(data, index, option, painter); + } QStyleOptionViewItem opt = option; QRect messageRect = option.rect.adjusted(margin, margin / 2, -(avatarHeight + 2 * margin), -margin / 2); @@ -122,13 +126,19 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio messageSize = bodyMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.text).size(); bodySize = messageSize; } - messageSize.rheight() += nickMetrics.lineSpacing(); + + if (ntds) { + messageSize.rheight() += nickMetrics.lineSpacing(); + } messageSize.rheight() += dateMetrics.height(); QString dateString = data.date.toLocalTime().toString("hh:mm"); + if (messageSize.width() < opt.rect.width()) { - QSize senderSize = nickMetrics.boundingRect(messageRect, 0, data.sender).size(); - if (senderSize.width() > messageSize.width()) { - messageSize.setWidth(senderSize.width()); + if (ntds) { + QSize senderSize = nickMetrics.boundingRect(messageRect, 0, data.sender).size(); + if (senderSize.width() > messageSize.width()) { + messageSize.setWidth(senderSize.width()); + } } QSize dateSize = dateMetrics.boundingRect(messageRect, 0, dateString).size(); int addition = 0; @@ -147,9 +157,11 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio } QRect rect; - painter->setFont(nickFont); - painter->drawText(opt.rect, opt.displayAlignment, data.sender, &rect); - opt.rect.adjust(0, rect.height() + textMargin, 0, 0); + if (ntds) { + painter->setFont(nickFont); + painter->drawText(opt.rect, opt.displayAlignment, data.sender, &rect); + opt.rect.adjust(0, rect.height() + textMargin, 0, 0); + } painter->save(); switch (data.attach.state) { case Models::none: @@ -244,25 +256,75 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio } } +void MessageDelegate::paintAvatar(const Models::FeedItem& data, const QModelIndex& index, const QStyleOptionViewItem& option, QPainter* painter) const +{ + int currentRow = index.row(); + int y = option.rect.y(); + bool firstAttempt = true; + QIcon icon(data.avatar); + while (y < 0 && currentRow > 0) { + QRect rect; + if (firstAttempt) { + firstAttempt = false; + rect = option.rect; + } else { + QModelIndex ci = index.siblingAtRow(currentRow); + if ( + (ci.data(Models::MessageFeed::Sender).toString() != data.sender) || + (ci.data(Models::MessageFeed::Date).toDateTime().daysTo(data.date) != 0) + ) { + break; + } + //TODO this is really bad, but for now I have no idea how else can I access the view; + const QAbstractItemView* view = static_cast(option.styleObject); + rect = view->visualRect(ci); + } + y = std::min(0, rect.bottom() - margin - avatarHeight); + --currentRow; + } + if (data.sentByMe) { + painter->drawPixmap(option.rect.width() - avatarHeight - margin, y + margin / 2, icon.pixmap(avatarHeight, avatarHeight)); + } else { + painter->drawPixmap(margin, y + margin / 2, icon.pixmap(avatarHeight, avatarHeight)); + } +} + +bool MessageDelegate::needToDrawAvatar(const QModelIndex& index, const Models::FeedItem& data, const QStyleOptionViewItem& option) const +{ + return (option.rect.y() < 1) || needToDrawSender(index, data); +} + +bool MessageDelegate::needToDrawSender(const QModelIndex& index, const Models::FeedItem& data) const +{ + if (index.row() == index.model()->rowCount() - 1) { + return true; + } else { + QModelIndex prevIndex = index.siblingAtRow(index.row() + 1); + + return (prevIndex.data(Models::MessageFeed::Sender).toString() != data.sender) || + (prevIndex.data(Models::MessageFeed::Date).toDateTime().daysTo(data.date) != 0); + } +} + + QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { QRect messageRect = option.rect.adjusted(0, margin / 2, -(avatarHeight + 3 * margin), -margin / 2); QStyleOptionViewItem opt = option; opt.rect = messageRect; - QVariant va = index.data(Models::MessageFeed::Attach); - Models::Attachment attach = qvariant_cast(va); - QString body = index.data(Models::MessageFeed::Text).toString(); + QVariant vi = index.data(Models::MessageFeed::Bulk); + Models::FeedItem data = qvariant_cast(vi); QSize messageSize(0, 0); - if (body.size() > 0) { - messageSize = bodyMetrics.boundingRect(messageRect, Qt::TextWordWrap, body).size(); + if (data.text.size() > 0) { + messageSize = bodyMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.text).size(); messageSize.rheight() += textMargin; } - switch (attach.state) { + switch (data.attach.state) { case Models::none: break; case Models::uploading: - messageSize.rheight() += Preview::calculateAttachSize(attach.localPath, messageRect).height() + textMargin; + messageSize.rheight() += Preview::calculateAttachSize(data.attach.localPath, messageRect).height() + textMargin; [[fallthrough]]; case Models::downloading: messageSize.rheight() += barHeight + textMargin; @@ -272,25 +334,28 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel break; case Models::ready: case Models::local: - messageSize.rheight() += Preview::calculateAttachSize(attach.localPath, messageRect).height() + textMargin; + messageSize.rheight() += Preview::calculateAttachSize(data.attach.localPath, messageRect).height() + textMargin; break; case Models::errorDownload: messageSize.rheight() += buttonHeight + textMargin; - messageSize.rheight() += dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, attach.error).size().height() + textMargin; + messageSize.rheight() += dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.attach.error).size().height() + textMargin; break; case Models::errorUpload: - messageSize.rheight() += Preview::calculateAttachSize(attach.localPath, messageRect).height() + textMargin; - messageSize.rheight() += dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, attach.error).size().height() + textMargin; + messageSize.rheight() += Preview::calculateAttachSize(data.attach.localPath, messageRect).height() + textMargin; + messageSize.rheight() += dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.attach.error).size().height() + textMargin; break; } - messageSize.rheight() += nickMetrics.lineSpacing(); - messageSize.rheight() += textMargin; + if (needToDrawSender(index, data)) { + messageSize.rheight() += nickMetrics.lineSpacing(); + messageSize.rheight() += textMargin; + } + messageSize.rheight() += dateMetrics.height() > statusIconSize ? dateMetrics.height() : statusIconSize; - if (messageSize.height() < avatarHeight) { - messageSize.setHeight(avatarHeight); - } +// if (messageSize.height() < avatarHeight) { +// messageSize.setHeight(avatarHeight); +// } messageSize.rheight() += margin; diff --git a/ui/widgets/messageline/messagedelegate.h b/ui/widgets/messageline/messagedelegate.h index 7403285..5792e01 100644 --- a/ui/widgets/messageline/messagedelegate.h +++ b/ui/widgets/messageline/messagedelegate.h @@ -65,12 +65,16 @@ protected: void paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const; void paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const; void paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const; + void paintAvatar(const Models::FeedItem& data, const QModelIndex& index, const QStyleOptionViewItem& option, QPainter* painter) const; QPushButton* getButton(const Models::FeedItem& data) const; QProgressBar* getBar(const Models::FeedItem& data) const; QLabel* getStatusIcon(const Models::FeedItem& data) const; QLabel* getPencilIcon(const Models::FeedItem& data) const; QLabel* getBody(const Models::FeedItem& data) const; void clearHelperWidget(const Models::FeedItem& data) const; + + bool needToDrawAvatar(const QModelIndex& index, const Models::FeedItem& data, const QStyleOptionViewItem& option) const; + bool needToDrawSender(const QModelIndex& index, const Models::FeedItem& data) const; protected slots: void onButtonPushed() const; From 8a2658e4fcf0f6e8559057d05152982c21a90e41 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 9 Jan 2022 01:28:29 +0300 Subject: [PATCH 014/137] message bubbles, avatar rounding, roster adjusments --- ui/squawk.cpp | 6 +- ui/squawk.ui | 23 +++- ui/widgets/conversation.cpp | 24 +++- ui/widgets/conversation.h | 8 ++ ui/widgets/messageline/feedview.cpp | 4 - ui/widgets/messageline/messagedelegate.cpp | 133 +++++++++++++++------ ui/widgets/messageline/messagedelegate.h | 9 +- 7 files changed, 158 insertions(+), 49 deletions(-) diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 406ee45..800f02a 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -39,7 +39,11 @@ Squawk::Squawk(QWidget *parent) : m_ui->setupUi(this); m_ui->roster->setModel(&rosterModel); m_ui->roster->setContextMenuPolicy(Qt::CustomContextMenu); - m_ui->roster->setColumnWidth(1, 30); + if (QApplication::style()->styleHint(QStyle::SH_ScrollBar_Transient) == 1) { + m_ui->roster->setColumnWidth(1, 52); + } else { + m_ui->roster->setColumnWidth(1, 26); + } m_ui->roster->setIconSize(QSize(20, 20)); m_ui->roster->header()->setStretchLastSection(false); m_ui->roster->header()->setSectionResizeMode(0, QHeaderView::Stretch); diff --git a/ui/squawk.ui b/ui/squawk.ui index a4d0258..01ffbba 100644 --- a/ui/squawk.ui +++ b/ui/squawk.ui @@ -73,6 +73,9 @@ 0 + + 0 + @@ -96,16 +99,31 @@ true + + true + false false + + 10 + + + false + + + + 0 + 30 + + false @@ -115,6 +133,9 @@ -1 + + true + @@ -162,7 +183,7 @@ 0 0 718 - 27 + 32 diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index ea2f722..69eac19 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -25,12 +25,16 @@ #include #include #include -#include #include #include #include #include #include +#include + +#include + +constexpr QSize avatarSize(50, 50); Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, const QString pJid, const QString pRes, QWidget* parent): QWidget(parent), @@ -348,11 +352,25 @@ void Conversation::onClearButton() void Conversation::setAvatar(const QString& path) { + QPixmap pixmap; if (path.size() == 0) { - m_ui->avatar->setPixmap(Shared::icon("user", true).pixmap(QSize(50, 50))); + pixmap = Shared::icon("user", true).pixmap(avatarSize); } else { - m_ui->avatar->setPixmap(path); + pixmap = QPixmap(path).scaled(avatarSize); } + + + QPixmap result(avatarSize); + result.fill(Qt::transparent); + QPainter painter(&result); + painter.setRenderHint(QPainter::Antialiasing); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + QPainterPath maskPath; + maskPath.addEllipse(0, 0, avatarSize.width(), avatarSize.height()); + painter.setClipPath(maskPath); + painter.drawPixmap(0, 0, pixmap); + + m_ui->avatar->setPixmap(result); } void Conversation::onTextEditDocSizeChanged(const QSizeF& size) diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index 6b5b4bb..a758b2c 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -141,6 +141,14 @@ protected: ShadowOverlay shadow; QMenu* contextMenu; + +private: + static bool painterInitialized; + static QPainterPath* avatarMask; + static QPixmap* avatarPixmap; + static QPainter* avatarPainter; + + }; #endif // CONVERSATION_H diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index 1296324..d83ca6b 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -31,10 +31,6 @@ constexpr int approximateSingleMessageHeight = 20; constexpr int progressSize = 70; constexpr int dateDeviderMargin = 10; -constexpr int avatarHeight = 50; -constexpr int margin = 6; -constexpr int halfMargin = 3; - const std::set FeedView::geometryChangingRoles = { Models::MessageFeed::Attach, Models::MessageFeed::Text, diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index 0cf449a..5dd0dce 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -29,6 +30,11 @@ constexpr int avatarHeight = 50; constexpr int margin = 6; constexpr int textMargin = 2; constexpr int statusIconSize = 16; +constexpr float nickFontMultiplier = 1.1; +constexpr float dateFontMultiplier = 0.8; + +constexpr int bubbleMargin = 6; +constexpr int bubbleBorderRadius = 3; MessageDelegate::MessageDelegate(QObject* parent): QStyledItemDelegate(parent), @@ -101,9 +107,10 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio painter->save(); painter->setRenderHint(QPainter::Antialiasing, true); - if (option.state & QStyle::State_MouseOver) { - painter->fillRect(option.rect, option.palette.brush(QPalette::Inactive, QPalette::Highlight)); - } +// if (option.state & QStyle::State_MouseOver) { +// painter->fillRect(option.rect, option.palette.brush(QPalette::Inactive, QPalette::Highlight)); +// } + bool ntds = needToDrawSender(index, data); if (ntds || option.rect.y() < 1) { @@ -118,6 +125,9 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio } else { opt.displayAlignment = Qt::AlignRight | Qt::AlignTop; } + + QPoint bubbleBegin = messageRect.topLeft(); + messageRect.adjust(bubbleMargin, bubbleMargin, -bubbleMargin, -bubbleMargin / 2); opt.rect = messageRect; QSize messageSize(0, 0); @@ -127,9 +137,6 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio bodySize = messageSize; } - if (ntds) { - messageSize.rheight() += nickMetrics.lineSpacing(); - } messageSize.rheight() += dateMetrics.height(); QString dateString = data.date.toLocalTime().toString("hh:mm"); @@ -155,46 +162,64 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio } else { messageSize.setWidth(opt.rect.width()); } - - QRect rect; - if (ntds) { - painter->setFont(nickFont); - painter->drawText(opt.rect, opt.displayAlignment, data.sender, &rect); - opt.rect.adjust(0, rect.height() + textMargin, 0, 0); - } + painter->save(); + + int storedY = opt.rect.y(); + if (ntds) { + opt.rect.adjust(0, nickMetrics.lineSpacing() + textMargin, 0, 0); + } + + int attWidth(0); switch (data.attach.state) { case Models::none: clearHelperWidget(data); //i can't imagine the situation where it's gonna be needed break; //but it's a possible performance problem case Models::uploading: - paintPreview(data, painter, opt); + attWidth = std::max(paintPreview(data, painter, opt), attWidth); [[fallthrough]]; case Models::downloading: - paintBar(getBar(data), painter, data.sentByMe, opt); + messageSize.setWidth(opt.rect.width()); + messageSize.rheight() += barHeight + textMargin + opt.rect.y() - storedY; + paintBubble(data, painter, messageSize, opt, bubbleBegin); + attWidth = std::max(paintBar(getBar(data), painter, data.sentByMe, opt), attWidth); break; case Models::remote: - paintButton(getButton(data), painter, data.sentByMe, opt); + attWidth = std::max(paintButton(getButton(data), painter, data.sentByMe, opt), attWidth); break; case Models::ready: case Models::local: clearHelperWidget(data); - paintPreview(data, painter, opt); + attWidth = std::max(paintPreview(data, painter, opt), attWidth); break; case Models::errorDownload: { - paintButton(getButton(data), painter, data.sentByMe, opt); - paintComment(data, painter, opt); + attWidth = std::max(paintButton(getButton(data), painter, data.sentByMe, opt), attWidth); + attWidth = std::max(paintComment(data, painter, opt), attWidth); } break; case Models::errorUpload:{ clearHelperWidget(data); - paintPreview(data, painter, opt); - paintComment(data, painter, opt); + attWidth = std::max(paintPreview(data, painter, opt), attWidth); + attWidth = std::max(paintComment(data, painter, opt), attWidth); } break; } painter->restore(); + if (data.attach.state != Models::uploading && data.attach.state != Models::downloading) { + messageSize.rheight() += opt.rect.y() - storedY; + messageSize.setWidth(std::max(attWidth, messageSize.width())); + paintBubble(data, painter, messageSize, opt, bubbleBegin); + } + + QRect rect; + if (ntds) { + painter->setFont(nickFont); + int storedY2 = opt.rect.y(); + opt.rect.setY(storedY); + painter->drawText(opt.rect, opt.displayAlignment, data.sender, &rect); + opt.rect.setY(storedY2); + } int messageLeft = INT16_MAX; int messageRight = opt.rect.x() + messageSize.width(); @@ -256,6 +281,23 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio } } +void MessageDelegate::paintBubble(const Models::FeedItem& data, QPainter* painter, const QSize& messageSize, QStyleOptionViewItem& option, QPoint bubbleBegin) const +{ + painter->save(); + if (data.sentByMe) { + bubbleBegin.setX(option.rect.topRight().x() - messageSize.width() - bubbleMargin); + painter->setBrush(option.palette.brush(QPalette::Inactive, QPalette::Highlight)); + } else { + painter->setBrush(option.palette.brush(QPalette::Window)); + } + QSize bubbleAddition(2 * bubbleMargin, 1.5 * bubbleMargin); + QRect bubble(bubbleBegin, messageSize + bubbleAddition); + painter->setPen(Qt::NoPen); + painter->drawRoundedRect(bubble, bubbleBorderRadius, bubbleBorderRadius); + painter->restore(); +} + + void MessageDelegate::paintAvatar(const Models::FeedItem& data, const QModelIndex& index, const QStyleOptionViewItem& option, QPainter* painter) const { int currentRow = index.row(); @@ -282,11 +324,22 @@ void MessageDelegate::paintAvatar(const Models::FeedItem& data, const QModelInde y = std::min(0, rect.bottom() - margin - avatarHeight); --currentRow; } + + QPixmap pixmap = icon.pixmap(avatarHeight, avatarHeight); + QPainterPath path; + int ax; + if (data.sentByMe) { - painter->drawPixmap(option.rect.width() - avatarHeight - margin, y + margin / 2, icon.pixmap(avatarHeight, avatarHeight)); + ax = option.rect.width() - avatarHeight - margin; } else { - painter->drawPixmap(margin, y + margin / 2, icon.pixmap(avatarHeight, avatarHeight)); + ax = margin; } + + path.addEllipse(ax, y + margin / 2, avatarHeight, avatarHeight); + painter->save(); + painter->setClipPath(path); + painter->drawPixmap(ax, y + margin / 2, pixmap); + painter->restore(); } bool MessageDelegate::needToDrawAvatar(const QModelIndex& index, const Models::FeedItem& data, const QStyleOptionViewItem& option) const @@ -309,7 +362,7 @@ bool MessageDelegate::needToDrawSender(const QModelIndex& index, const Models::F QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { - QRect messageRect = option.rect.adjusted(0, margin / 2, -(avatarHeight + 3 * margin), -margin / 2); + QRect messageRect = option.rect.adjusted(bubbleMargin, margin / 2 + bubbleMargin, -(avatarHeight + 3 * margin + bubbleMargin), -(margin + bubbleMargin) / 2); QStyleOptionViewItem opt = option; opt.rect = messageRect; QVariant vi = index.data(Models::MessageFeed::Bulk); @@ -347,10 +400,10 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel } if (needToDrawSender(index, data)) { - messageSize.rheight() += nickMetrics.lineSpacing(); - messageSize.rheight() += textMargin; + messageSize.rheight() += nickMetrics.lineSpacing() + textMargin; } + messageSize.rheight() += bubbleMargin + bubbleMargin / 2; messageSize.rheight() += dateMetrics.height() > statusIconSize ? dateMetrics.height() : statusIconSize; // if (messageSize.height() < avatarHeight) { @@ -372,17 +425,17 @@ void MessageDelegate::initializeFonts(const QFont& font) float ndps = nickFont.pointSizeF(); if (ndps != -1) { - nickFont.setPointSizeF(ndps * 1.2); + nickFont.setPointSizeF(ndps * nickFontMultiplier); } else { - nickFont.setPointSize(nickFont.pointSize() + 2); + nickFont.setPointSize(nickFont.pointSize() * nickFontMultiplier); } dateFont.setItalic(true); float dps = dateFont.pointSizeF(); if (dps != -1) { - dateFont.setPointSizeF(dps * 0.8); + dateFont.setPointSizeF(dps * dateFontMultiplier); } else { - dateFont.setPointSize(dateFont.pointSize() - 2); + dateFont.setPointSize(dateFont.pointSize() * dateFontMultiplier); } bodyMetrics = QFontMetrics(bodyFont); @@ -400,11 +453,11 @@ bool MessageDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, cons return QStyledItemDelegate::editorEvent(event, model, option, index); } -void MessageDelegate::paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const +int MessageDelegate::paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const { QPoint start; if (sentByMe) { - start = {option.rect.width() - btn->width(), option.rect.top()}; + start = {option.rect.x() + option.rect.width() - btn->width(), option.rect.top()}; } else { start = option.rect.topLeft(); } @@ -415,9 +468,10 @@ void MessageDelegate::paintButton(QPushButton* btn, QPainter* painter, bool sent btn->show(); option.rect.adjust(0, buttonHeight + textMargin, 0, 0); + return btn->width(); } -void MessageDelegate::paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const +int MessageDelegate::paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const { painter->setFont(dateFont); QColor q = painter->pen().color(); @@ -426,9 +480,11 @@ void MessageDelegate::paintComment(const Models::FeedItem& data, QPainter* paint QRect rect; painter->drawText(option.rect, option.displayAlignment, data.attach.error, &rect); option.rect.adjust(0, rect.height() + textMargin, 0, 0); + + return rect.width(); } -void MessageDelegate::paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const +int MessageDelegate::paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const { QPoint start = option.rect.topLeft(); bar->resize(option.rect.width(), barHeight); @@ -437,9 +493,11 @@ void MessageDelegate::paintBar(QProgressBar* bar, QPainter* painter, bool sentBy bar->render(painter, QPoint(), QRegion(), QWidget::DrawChildren); option.rect.adjust(0, barHeight + textMargin, 0, 0); + + return option.rect.width(); } -void MessageDelegate::paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const +int MessageDelegate::paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const { Preview* preview = 0; std::map::iterator itr = previews->find(data.id); @@ -458,7 +516,10 @@ void MessageDelegate::paintPreview(const Models::FeedItem& data, QPainter* paint emit invalidPath(data.id); //or deleted. This signal notifies the model, and the model notifies the core, preview can } //handle being invalid for as long as I need and can be even become valid again with a new path - option.rect.adjust(0, preview->size().height() + textMargin, 0, 0); + QSize pSize(preview->size()); + option.rect.adjust(0, pSize.height() + textMargin, 0, 0); + + return pSize.width(); } QPushButton * MessageDelegate::getButton(const Models::FeedItem& data) const diff --git a/ui/widgets/messageline/messagedelegate.h b/ui/widgets/messageline/messagedelegate.h index 5792e01..87a79c9 100644 --- a/ui/widgets/messageline/messagedelegate.h +++ b/ui/widgets/messageline/messagedelegate.h @@ -61,11 +61,12 @@ signals: void invalidPath(const QString& messageId) const; protected: - void paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const; - void paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const; - void paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const; - void paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const; + int paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const; + int paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const; + int paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const; + int paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const; void paintAvatar(const Models::FeedItem& data, const QModelIndex& index, const QStyleOptionViewItem& option, QPainter* painter) const; + void paintBubble(const Models::FeedItem& data, QPainter* painter, const QSize& messageSize, QStyleOptionViewItem& option, QPoint bubbleBegin) const; QPushButton* getButton(const Models::FeedItem& data) const; QProgressBar* getBar(const Models::FeedItem& data) const; QLabel* getStatusIcon(const Models::FeedItem& data) const; From 4d3ba6b11f5704cf09c404e2307dada7ac665322 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 9 Jan 2022 17:32:23 +0300 Subject: [PATCH 015/137] 0.2.0 finalization --- CHANGELOG.md | 13 +- README.md | 4 +- core/main.cpp | 2 +- packaging/Archlinux/PKGBUILD | 4 +- shared/global.cpp | 2 +- shared/global.h | 1 + ui/widgets/messageline/feedview.cpp | 58 ++++-- ui/widgets/messageline/feedview.h | 3 + ui/widgets/messageline/messagedelegate.cpp | 220 ++++++++------------- ui/widgets/messageline/messagedelegate.h | 6 +- ui/widgets/messageline/preview.cpp | 55 ++++-- ui/widgets/messageline/preview.h | 5 +- 12 files changed, 194 insertions(+), 179 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf90231..09c382a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,19 +1,28 @@ # Changelog -## Squawk 0.2.0 (Unreleased) +## Squawk 0.2.0 (Jan 10, 2022) ### Bug fixes - carbon copies switches on again after reconnection - requesting the history of the current chat after reconnection - global availability (in drop down list) gets restored after reconnection - status icon in active chat changes when presence of the pen pal changes - infinite progress when open the dialogue with something that has no history to show +- fallback icons for buttons, when no supported theme is installed (shunf4) +- better handling messages with no id (shunf4) +- removed dependency: uuid, now it's on Qt (shunf4) +- better requesting latest history (shunf4) ### Improvements - slightly reduced the traffic on the startup by not requesting history of all MUCs -- completely rewritten message feed, now it works way faster +- completely rewritten message feed, now it works way faster and looks cooler - OPTIONAL RUNTIME dependency: "KIO Widgets" that is supposed to allow you to open a file in your default file manager - show in folder now is supposed to try it's best to show file in folder, even you don't have KIO installed - once uploaded local files don't get second time uploaded - the remote URL is reused +- way better compilation time (vae) + +### New features +- pasting images from clipboard to attachment (shunf4) +- possible compilation for windows and macOS (shunf4) ## Squawk 0.1.5 (Jul 29, 2020) ### Bug fixes diff --git a/README.md b/README.md index e94972f..ff36f3c 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@ [![AUR version](https://img.shields.io/aur/version/squawk?style=flat-square)](https://aur.archlinux.org/packages/squawk/) [![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me) -![Squawk screenshot](https://macaw.me/images/squawk/0.1.4.png) +![Squawk screenshot](https://macaw.me/images/squawk/0.2.0.png) ### Prerequisites - QT 5.12 *(lower versions might work but it wasn't tested)* - lmdb -- CMake 3.0 or higher +- CMake 3.3 or higher - qxmpp 1.1.0 or higher - KDE Frameworks: kwallet (optional) - KDE Frameworks: KIO (optional) diff --git a/core/main.cpp b/core/main.cpp index f63d4f8..d6eea3e 100644 --- a/core/main.cpp +++ b/core/main.cpp @@ -54,7 +54,7 @@ int main(int argc, char *argv[]) #endif QApplication::setApplicationName("squawk"); QApplication::setApplicationDisplayName("Squawk"); - QApplication::setApplicationVersion("0.1.5"); + QApplication::setApplicationVersion("0.2.0"); QTranslator qtTranslator; qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); diff --git a/packaging/Archlinux/PKGBUILD b/packaging/Archlinux/PKGBUILD index 20fea99..68e1558 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Yury Gubich pkgname=squawk -pkgver=0.1.5 +pkgver=0.2.0 pkgrel=1 pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)" arch=('i686' 'x86_64') @@ -11,7 +11,7 @@ makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools') optdepends=('kwallet: secure password storage (requires rebuild)') source=("$pkgname-$pkgver.tar.gz") -sha256sums=('e1a4c88be9f0481d2aa21078faf42fd0e9d66b490b6d8af82827d441cb58df25') +sha256sums=('8e93d3dbe1fc35cfecb7783af409c6a264244d11609b2241d4fe77d43d068419') build() { cd "$srcdir/squawk" cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release diff --git a/shared/global.cpp b/shared/global.cpp index d6f2169..5f3b4ed 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -144,7 +144,7 @@ Shared::Global::FileInfo Shared::Global::getFileInfo(const QString& path) size = defaultIconFileInfoHeight; } - itr = instance->fileCache.insert(std::make_pair(path, FileInfo({info.fileName(), size, type, p}))).first; + itr = instance->fileCache.insert(std::make_pair(path, FileInfo({info.absoluteFilePath(), info.fileName(), size, type, p}))).first; } return itr->second; diff --git a/shared/global.h b/shared/global.h index 03cf84d..b1ae59c 100644 --- a/shared/global.h +++ b/shared/global.h @@ -55,6 +55,7 @@ namespace Shared { animation }; + QString path; QString name; QSize size; QMimeType mime; diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index d83ca6b..34f9400 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -43,6 +43,7 @@ FeedView::FeedView(QWidget* parent): QAbstractItemView(parent), hints(), vo(0), + elementMargin(0), specialDelegate(false), specialModel(false), clearWidgetsMode(false), @@ -106,7 +107,7 @@ QRect FeedView::visualRect(const QModelIndex& index) const } else { const Hint& hint = hints.at(row); const QWidget* vp = viewport(); - return QRect(0, vp->height() - hint.height - hint.offset + vo, vp->width(), hint.height); + return QRect(hint.x, vp->height() - hint.height - hint.offset + vo, hint.width, hint.height); } } @@ -198,7 +199,7 @@ void FeedView::updateGeometries() option.rect.setWidth(layoutBounds.width()); hints.clear(); - uint32_t previousOffset = 0; + uint32_t previousOffset = elementMargin; QDateTime lastDate; for (int i = 0, size = m->rowCount(); i < size; ++i) { QModelIndex index = m->index(i, 0, rootIndex()); @@ -206,19 +207,32 @@ void FeedView::updateGeometries() if (i > 0) { if (currentDate.daysTo(lastDate) > 0) { previousOffset += dividerMetrics.height() + dateDeviderMargin * 2; + } else { + previousOffset += elementMargin; } } lastDate = currentDate; - int height = itemDelegate(index)->sizeHint(option, index).height(); + QSize messageSize = itemDelegate(index)->sizeHint(option, index); + uint32_t offsetX(0); + if (specialDelegate) { + if (index.data(Models::MessageFeed::SentByMe).toBool()) { + offsetX = layoutBounds.width() - messageSize.width() - MessageDelegate::avatarHeight - MessageDelegate::margin * 2; + } else { + offsetX = MessageDelegate::avatarHeight + MessageDelegate::margin * 2; + } + } + hints.emplace_back(Hint({ false, previousOffset, - static_cast(height) + static_cast(messageSize.height()), + static_cast(messageSize.width()), + offsetX })); - previousOffset += height; + previousOffset += messageSize.height(); } - int totalHeight = previousOffset - layoutBounds.height(); + int totalHeight = previousOffset - layoutBounds.height() + dividerMetrics.height() + dateDeviderMargin * 2; if (modelState != Models::MessageFeed::complete) { totalHeight += progressSize; } @@ -240,7 +254,7 @@ void FeedView::updateGeometries() bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* m, uint32_t totalHeight) { - uint32_t previousOffset = 0; + uint32_t previousOffset = elementMargin; bool success = true; QDateTime lastDate; for (int i = 0, size = m->rowCount(); i < size; ++i) { @@ -249,21 +263,39 @@ bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewIt if (i > 0) { if (currentDate.daysTo(lastDate) > 0) { previousOffset += dateDeviderMargin * 2 + dividerMetrics.height(); + } else { + previousOffset += elementMargin; } } lastDate = currentDate; - int height = itemDelegate(index)->sizeHint(option, index).height(); + QSize messageSize = itemDelegate(index)->sizeHint(option, index); - if (previousOffset + height > totalHeight) { + if (previousOffset + messageSize.height() + elementMargin > totalHeight) { success = false; break; } + + uint32_t offsetX(0); + if (specialDelegate) { + if (index.data(Models::MessageFeed::SentByMe).toBool()) { + offsetX = option.rect.width() - messageSize.width() - MessageDelegate::avatarHeight - MessageDelegate::margin * 2; + } else { + offsetX = MessageDelegate::avatarHeight + MessageDelegate::margin * 2; + } + } hints.emplace_back(Hint({ false, previousOffset, - static_cast(height) + static_cast(messageSize.height()), + static_cast(messageSize.width()), + offsetX })); - previousOffset += height; + previousOffset += messageSize.height(); + } + + previousOffset += dateDeviderMargin * 2 + dividerMetrics.height(); + if (previousOffset > totalHeight) { + success = false; } return success; @@ -336,7 +368,7 @@ void FeedView::paintEvent(QPaintEvent* event) lastDate = currentDate; } if (!lastDate.isNull() && inZone) { //if after drawing all messages there is still space - drawDateDevider(option.rect.bottom(), lastDate, painter); + drawDateDevider(option.rect.top() - dateDeviderMargin * 2 - dividerMetrics.height(), lastDate, painter); } if (clearWidgetsMode && specialDelegate) { @@ -423,10 +455,12 @@ void FeedView::setItemDelegate(QAbstractItemDelegate* delegate) MessageDelegate* del = dynamic_cast(delegate); if (del) { specialDelegate = true; + elementMargin = MessageDelegate::margin; connect(del, &MessageDelegate::buttonPushed, this, &FeedView::onMessageButtonPushed); connect(del, &MessageDelegate::invalidPath, this, &FeedView::onMessageInvalidPath); } else { specialDelegate = false; + elementMargin = 0; } } diff --git a/ui/widgets/messageline/feedview.h b/ui/widgets/messageline/feedview.h index 5e08946..8bcd913 100644 --- a/ui/widgets/messageline/feedview.h +++ b/ui/widgets/messageline/feedview.h @@ -80,9 +80,12 @@ private: bool dirty; uint32_t offset; uint32_t height; + uint32_t width; + uint32_t x; }; std::deque hints; int vo; + int elementMargin; bool specialDelegate; bool specialModel; bool clearWidgetsMode; diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index 5dd0dce..d692752 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -26,8 +26,8 @@ #include "messagedelegate.h" #include "messagefeed.h" -constexpr int avatarHeight = 50; -constexpr int margin = 6; +int MessageDelegate::avatarHeight(50); +int MessageDelegate::margin(6); constexpr int textMargin = 2; constexpr int statusIconSize = 16; constexpr float nickFontMultiplier = 1.1; @@ -45,6 +45,7 @@ MessageDelegate::MessageDelegate(QObject* parent): nickMetrics(nickFont), dateMetrics(dateFont), buttonHeight(0), + buttonWidth(0), barHeight(0), buttons(new std::map()), bars(new std::map()), @@ -55,8 +56,9 @@ MessageDelegate::MessageDelegate(QObject* parent): idsToKeep(new std::set()), clearingWidgets(false) { - QPushButton btn; + QPushButton btn(QCoreApplication::translate("MessageLine", "Download")); buttonHeight = btn.sizeHint().height(); + buttonWidth = btn.sizeHint().width(); QProgressBar bar; barHeight = bar.sizeHint().height(); @@ -107,141 +109,79 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio painter->save(); painter->setRenderHint(QPainter::Antialiasing, true); -// if (option.state & QStyle::State_MouseOver) { -// painter->fillRect(option.rect, option.palette.brush(QPalette::Inactive, QPalette::Highlight)); -// } - - + paintBubble(data, painter, option); bool ntds = needToDrawSender(index, data); if (ntds || option.rect.y() < 1) { paintAvatar(data, index, option, painter); } QStyleOptionViewItem opt = option; - QRect messageRect = option.rect.adjusted(margin, margin / 2, -(avatarHeight + 2 * margin), -margin / 2); + opt.rect = option.rect.adjusted(bubbleMargin, bubbleMargin, -bubbleMargin, -bubbleMargin / 2); if (!data.sentByMe) { opt.displayAlignment = Qt::AlignLeft | Qt::AlignTop; - messageRect.adjust(avatarHeight + margin, 0, avatarHeight + margin, 0); } else { opt.displayAlignment = Qt::AlignRight | Qt::AlignTop; } - QPoint bubbleBegin = messageRect.topLeft(); - messageRect.adjust(bubbleMargin, bubbleMargin, -bubbleMargin, -bubbleMargin / 2); - opt.rect = messageRect; - - QSize messageSize(0, 0); QSize bodySize(0, 0); if (data.text.size() > 0) { - messageSize = bodyMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.text).size(); - bodySize = messageSize; - } - - messageSize.rheight() += dateMetrics.height(); - QString dateString = data.date.toLocalTime().toString("hh:mm"); - - if (messageSize.width() < opt.rect.width()) { - if (ntds) { - QSize senderSize = nickMetrics.boundingRect(messageRect, 0, data.sender).size(); - if (senderSize.width() > messageSize.width()) { - messageSize.setWidth(senderSize.width()); - } - } - QSize dateSize = dateMetrics.boundingRect(messageRect, 0, dateString).size(); - int addition = 0; - - if (data.correction.corrected) { - addition += margin + statusIconSize; - } - if (data.sentByMe) { - addition += margin + statusIconSize; - } - if (dateSize.width() + addition > messageSize.width()) { - messageSize.setWidth(dateSize.width() + addition); - } - } else { - messageSize.setWidth(opt.rect.width()); - } - - painter->save(); - - int storedY = opt.rect.y(); - if (ntds) { - opt.rect.adjust(0, nickMetrics.lineSpacing() + textMargin, 0, 0); - } - - int attWidth(0); - switch (data.attach.state) { - case Models::none: - clearHelperWidget(data); //i can't imagine the situation where it's gonna be needed - break; //but it's a possible performance problem - case Models::uploading: - attWidth = std::max(paintPreview(data, painter, opt), attWidth); - [[fallthrough]]; - case Models::downloading: - messageSize.setWidth(opt.rect.width()); - messageSize.rheight() += barHeight + textMargin + opt.rect.y() - storedY; - paintBubble(data, painter, messageSize, opt, bubbleBegin); - attWidth = std::max(paintBar(getBar(data), painter, data.sentByMe, opt), attWidth); - break; - case Models::remote: - attWidth = std::max(paintButton(getButton(data), painter, data.sentByMe, opt), attWidth); - break; - case Models::ready: - case Models::local: - clearHelperWidget(data); - attWidth = std::max(paintPreview(data, painter, opt), attWidth); - break; - case Models::errorDownload: { - attWidth = std::max(paintButton(getButton(data), painter, data.sentByMe, opt), attWidth); - attWidth = std::max(paintComment(data, painter, opt), attWidth); - } - - break; - case Models::errorUpload:{ - clearHelperWidget(data); - attWidth = std::max(paintPreview(data, painter, opt), attWidth); - attWidth = std::max(paintComment(data, painter, opt), attWidth); - } - break; - } - painter->restore(); - if (data.attach.state != Models::uploading && data.attach.state != Models::downloading) { - messageSize.rheight() += opt.rect.y() - storedY; - messageSize.setWidth(std::max(attWidth, messageSize.width())); - paintBubble(data, painter, messageSize, opt, bubbleBegin); + bodySize = bodyMetrics.boundingRect(opt.rect, Qt::TextWordWrap, data.text).size(); } QRect rect; if (ntds) { painter->setFont(nickFont); - int storedY2 = opt.rect.y(); - opt.rect.setY(storedY); painter->drawText(opt.rect, opt.displayAlignment, data.sender, &rect); - opt.rect.setY(storedY2); + opt.rect.adjust(0, nickMetrics.lineSpacing() + textMargin, 0, 0); } + + painter->save(); + switch (data.attach.state) { + case Models::none: + clearHelperWidget(data); //i can't imagine the situation where it's gonna be needed + break; //but it's a possible performance problem + case Models::uploading: + paintPreview(data, painter, opt); + [[fallthrough]]; + case Models::downloading: + paintBar(getBar(data), painter, data.sentByMe, opt); + break; + case Models::remote: + paintButton(getButton(data), painter, data.sentByMe, opt); + break; + case Models::ready: + case Models::local: + clearHelperWidget(data); + paintPreview(data, painter, opt); + break; + case Models::errorDownload: { + paintButton(getButton(data), painter, data.sentByMe, opt); + paintComment(data, painter, opt); + } + + break; + case Models::errorUpload:{ + clearHelperWidget(data); + paintPreview(data, painter, opt); + paintComment(data, painter, opt); + } + break; + } + painter->restore(); - int messageLeft = INT16_MAX; - int messageRight = opt.rect.x() + messageSize.width(); QWidget* vp = static_cast(painter->device()); if (data.text.size() > 0) { QLabel* body = getBody(data); body->setParent(vp); - body->setMaximumWidth(bodySize.width()); - body->setMinimumWidth(bodySize.width()); - body->setMinimumHeight(bodySize.height()); - body->setMaximumHeight(bodySize.height()); - body->setAlignment(opt.displayAlignment); - messageLeft = opt.rect.x(); - if (data.sentByMe) { - messageLeft = opt.rect.topRight().x() - bodySize.width(); - } - body->move(messageLeft, opt.rect.y()); + body->setMinimumSize(bodySize); + body->setMaximumSize(bodySize); + body->move(opt.rect.left(), opt.rect.y()); body->show(); opt.rect.adjust(0, bodySize.height() + textMargin, 0, 0); } painter->setFont(dateFont); QColor q = painter->pen().color(); + QString dateString = data.date.toLocalTime().toString("hh:mm"); q.setAlpha(180); painter->setPen(q); painter->drawText(opt.rect, opt.displayAlignment, dateString, &rect); @@ -250,7 +190,7 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio QLabel* statusIcon = getStatusIcon(data); statusIcon->setParent(vp); - statusIcon->move(opt.rect.topRight().x() - messageSize.width(), currentY); + statusIcon->move(opt.rect.left(), currentY); statusIcon->show(); opt.rect.adjust(0, statusIconSize + textMargin, 0, 0); @@ -261,9 +201,9 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio pencilIcon->setParent(vp); if (data.sentByMe) { - pencilIcon->move(opt.rect.topRight().x() - messageSize.width() + statusIconSize + margin, currentY); + pencilIcon->move(opt.rect.left() + statusIconSize + margin, currentY); } else { - pencilIcon->move(messageRight - statusIconSize - margin, currentY); + pencilIcon->move(opt.rect.right() - statusIconSize - margin, currentY); } pencilIcon->show(); } else { @@ -281,19 +221,16 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio } } -void MessageDelegate::paintBubble(const Models::FeedItem& data, QPainter* painter, const QSize& messageSize, QStyleOptionViewItem& option, QPoint bubbleBegin) const +void MessageDelegate::paintBubble(const Models::FeedItem& data, QPainter* painter, const QStyleOptionViewItem& option) const { painter->save(); if (data.sentByMe) { - bubbleBegin.setX(option.rect.topRight().x() - messageSize.width() - bubbleMargin); painter->setBrush(option.palette.brush(QPalette::Inactive, QPalette::Highlight)); } else { painter->setBrush(option.palette.brush(QPalette::Window)); } - QSize bubbleAddition(2 * bubbleMargin, 1.5 * bubbleMargin); - QRect bubble(bubbleBegin, messageSize + bubbleAddition); painter->setPen(Qt::NoPen); - painter->drawRoundedRect(bubble, bubbleBorderRadius, bubbleBorderRadius); + painter->drawRoundedRect(option.rect, bubbleBorderRadius, bubbleBorderRadius); painter->restore(); } @@ -330,7 +267,7 @@ void MessageDelegate::paintAvatar(const Models::FeedItem& data, const QModelInde int ax; if (data.sentByMe) { - ax = option.rect.width() - avatarHeight - margin; + ax = option.rect.x() + option.rect.width() + margin; } else { ax = margin; } @@ -381,36 +318,51 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel [[fallthrough]]; case Models::downloading: messageSize.rheight() += barHeight + textMargin; + messageSize.setWidth(messageRect.width()); break; case Models::remote: messageSize.rheight() += buttonHeight + textMargin; + messageSize.setWidth(std::max(messageSize.width(), buttonWidth)); break; case Models::ready: - case Models::local: - messageSize.rheight() += Preview::calculateAttachSize(data.attach.localPath, messageRect).height() + textMargin; + case Models::local: { + QSize aSize = Preview::calculateAttachSize(data.attach.localPath, messageRect); + messageSize.rheight() += aSize.height() + textMargin; + messageSize.setWidth(std::max(messageSize.width(), aSize.width())); + } break; - case Models::errorDownload: - messageSize.rheight() += buttonHeight + textMargin; - messageSize.rheight() += dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.attach.error).size().height() + textMargin; + case Models::errorDownload: { + QSize commentSize = dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.attach.error).size(); + messageSize.rheight() += commentSize.height() + buttonHeight + textMargin * 2; + messageSize.setWidth(std::max(messageSize.width(), std::max(commentSize.width(), buttonWidth))); + } break; - case Models::errorUpload: - messageSize.rheight() += Preview::calculateAttachSize(data.attach.localPath, messageRect).height() + textMargin; - messageSize.rheight() += dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.attach.error).size().height() + textMargin; + case Models::errorUpload: { + QSize aSize = Preview::calculateAttachSize(data.attach.localPath, messageRect); + QSize commentSize = dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.attach.error).size(); + messageSize.rheight() += aSize.height() + commentSize.height() + textMargin * 2; + messageSize.setWidth(std::max(messageSize.width(), std::max(commentSize.width(), aSize.width()))); + } break; } if (needToDrawSender(index, data)) { - messageSize.rheight() += nickMetrics.lineSpacing() + textMargin; + QSize senderSize = nickMetrics.boundingRect(messageRect, 0, data.sender).size(); + messageSize.rheight() += senderSize.height() + textMargin; + messageSize.setWidth(std::max(senderSize.width(), messageSize.width())); } - messageSize.rheight() += bubbleMargin + bubbleMargin / 2; - messageSize.rheight() += dateMetrics.height() > statusIconSize ? dateMetrics.height() : statusIconSize; - -// if (messageSize.height() < avatarHeight) { -// messageSize.setHeight(avatarHeight); -// } - - messageSize.rheight() += margin; + QString dateString = data.date.toLocalTime().toString("hh:mm"); + QSize dateSize = dateMetrics.boundingRect(messageRect, 0, dateString).size(); + messageSize.rheight() += bubbleMargin * 1.5; + messageSize.rheight() += dateSize.height() > statusIconSize ? dateSize.height() : statusIconSize; + + int statusWidth = dateSize.width() + statusIconSize + margin; + if (data.correction.corrected) { + statusWidth += statusIconSize + margin; + } + messageSize.setWidth(std::max(statusWidth, messageSize.width())); + messageSize.rwidth() += 2 * bubbleMargin; return messageSize; } @@ -508,7 +460,7 @@ int MessageDelegate::paintPreview(const Models::FeedItem& data, QPainter* painte preview->actualize(data.attach.localPath, size, option.rect.topLeft()); } else { QWidget* vp = static_cast(painter->device()); - preview = new Preview(data.attach.localPath, size, option.rect.topLeft(), data.sentByMe, vp); + preview = new Preview(data.attach.localPath, size, option.rect.topLeft(), vp); previews->insert(std::make_pair(data.id, preview)); } diff --git a/ui/widgets/messageline/messagedelegate.h b/ui/widgets/messageline/messagedelegate.h index 87a79c9..b58a1bb 100644 --- a/ui/widgets/messageline/messagedelegate.h +++ b/ui/widgets/messageline/messagedelegate.h @@ -55,6 +55,9 @@ public: bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) override; void endClearWidgets(); void beginClearWidgets(); + + static int avatarHeight; + static int margin; signals: void buttonPushed(const QString& messageId) const; @@ -66,7 +69,7 @@ protected: int paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const; int paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const; void paintAvatar(const Models::FeedItem& data, const QModelIndex& index, const QStyleOptionViewItem& option, QPainter* painter) const; - void paintBubble(const Models::FeedItem& data, QPainter* painter, const QSize& messageSize, QStyleOptionViewItem& option, QPoint bubbleBegin) const; + void paintBubble(const Models::FeedItem& data, QPainter* painter, const QStyleOptionViewItem& option) const; QPushButton* getButton(const Models::FeedItem& data) const; QProgressBar* getBar(const Models::FeedItem& data) const; QLabel* getStatusIcon(const Models::FeedItem& data) const; @@ -94,6 +97,7 @@ private: QFontMetrics dateMetrics; int buttonHeight; + int buttonWidth; int barHeight; std::map* buttons; diff --git a/ui/widgets/messageline/preview.cpp b/ui/widgets/messageline/preview.cpp index e54fce6..137293f 100644 --- a/ui/widgets/messageline/preview.cpp +++ b/ui/widgets/messageline/preview.cpp @@ -25,9 +25,8 @@ constexpr int maxAttachmentHeight = 500; QFont Preview::font; QFontMetrics Preview::metrics(Preview::font); -Preview::Preview(const QString& pPath, const QSize& pMaxSize, const QPoint& pos, bool pRight, QWidget* pParent): +Preview::Preview(const QString& pPath, const QSize& pMaxSize, const QPoint& pos, QWidget* pParent): info(Shared::Global::getFileInfo(pPath)), - path(pPath), maxSize(pMaxSize), actualSize(constrainAttachSize(info.size, maxSize)), cachedLabelSize(0, 0), @@ -37,8 +36,7 @@ Preview::Preview(const QString& pPath, const QSize& pMaxSize, const QPoint& pos, parent(pParent), movie(0), fileReachable(true), - actualPreview(false), - right(pRight) + actualPreview(false) { initializeElements(); @@ -104,9 +102,6 @@ void Preview::actualize(const QString& newPath, const QSize& newSize, const QPoi } } else if (maxSizeChanged) { applyNewMaxSize(); - if (right) { - positionChanged = true; - } } if (positionChanged || !actualPreview) { positionElements(); @@ -135,9 +130,6 @@ void Preview::setSize(const QSize& newSize) } if (maxSizeChanged || !actualPreview) { applyNewMaxSize(); - if (right) { - positionElements(); - } } } } @@ -146,7 +138,7 @@ void Preview::applyNewSize() { switch (info.preview) { case Shared::Global::FileInfo::Preview::picture: { - QImageReader img(path); + QImageReader img(info.path); if (!img.canRead()) { delete widget; fileReachable = false; @@ -216,9 +208,8 @@ void Preview::setPosition(const QPoint& newPoint) bool Preview::setPath(const QString& newPath) { - if (path != newPath) { - path = newPath; - info = Shared::Global::getFileInfo(path); + if (info.path != newPath) { + info = Shared::Global::getFileInfo(newPath); actualSize = constrainAttachSize(info.size, maxSize); clean(); initializeElements(); @@ -235,7 +226,7 @@ void Preview::initializeElements() { switch (info.preview) { case Shared::Global::FileInfo::Preview::picture: { - QImageReader img(path); + QImageReader img(info.path); if (!img.canRead()) { fileReachable = false; } else { @@ -248,7 +239,7 @@ void Preview::initializeElements() } break; case Shared::Global::FileInfo::Preview::animation:{ - movie = new QMovie(path); + movie = new QMovie(info.path); QObject::connect(movie, &QMovie::error, std::bind(&Preview::handleQMovieError, this, std::placeholders::_1) ); @@ -289,9 +280,6 @@ void Preview::initializeElements() void Preview::positionElements() { int start = position.x(); - if (right) { - start += maxSize.width() - size().width(); - } widget->move(start, position.y()); if (!actualPreview) { int x = start + actualSize.width() + margin; @@ -300,11 +288,36 @@ void Preview::positionElements() } } +bool Preview::canVisualize(const Shared::Global::FileInfo& info) +{ + switch (info.preview) { + case Shared::Global::FileInfo::Preview::picture: { + QImageReader img(info.path); + return img.canRead(); + } + break; + case Shared::Global::FileInfo::Preview::animation:{ + QMovie movie(info.path); + return movie.isValid(); + } + break; + default: { + return false; + } + } +} + QSize Preview::calculateAttachSize(const QString& path, const QRect& bounds) { Shared::Global::FileInfo info = Shared::Global::getFileInfo(path); - - return constrainAttachSize(info.size, bounds.size()); + QSize constrained = constrainAttachSize(info.size, bounds.size()); + if (!canVisualize(info)) { + int maxLabelWidth = bounds.width() - info.size.width() - margin; + QString elidedName = metrics.elidedText(info.name, Qt::ElideMiddle, maxLabelWidth); + int labelWidth = metrics.boundingRect(elidedName).size().width(); + constrained.rwidth() += margin + labelWidth; + } + return constrained; } QSize Preview::constrainAttachSize(QSize src, QSize bounds) diff --git a/ui/widgets/messageline/preview.h b/ui/widgets/messageline/preview.h index 004ed45..5165137 100644 --- a/ui/widgets/messageline/preview.h +++ b/ui/widgets/messageline/preview.h @@ -38,7 +38,7 @@ */ class Preview { public: - Preview(const QString& pPath, const QSize& pMaxSize, const QPoint& pos, bool pRight, QWidget* parent); + Preview(const QString& pPath, const QSize& pMaxSize, const QPoint& pos, QWidget* parent); ~Preview(); void actualize(const QString& newPath, const QSize& newSize, const QPoint& newPoint); @@ -51,6 +51,7 @@ public: static void initializeFont(const QFont& newFont); static QSize constrainAttachSize(QSize src, QSize bounds); static QSize calculateAttachSize(const QString& path, const QRect& bounds); + static bool canVisualize(const Shared::Global::FileInfo& info); static QFont font; static QFontMetrics metrics; @@ -64,7 +65,6 @@ private: private: Shared::Global::FileInfo info; - QString path; QSize maxSize; QSize actualSize; QSize cachedLabelSize; @@ -75,7 +75,6 @@ private: QMovie* movie; bool fileReachable; bool actualPreview; - bool right; }; #endif // PREVIEW_H From 296328f12dd9296a8a8d092b4fcd6f8332a15403 Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 11 Jan 2022 23:50:42 +0300 Subject: [PATCH 016/137] a bit of polish --- CMakeLists.txt | 21 ++++++++++++++------- README.md | 5 +++-- packaging/Archlinux/PKGBUILD | 4 ++-- ui/widgets/messageline/feedview.cpp | 8 +++----- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index da89682..b104667 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.4) -project(squawk VERSION 0.1.6 LANGUAGES CXX) +project(squawk VERSION 0.2.0 LANGUAGES CXX) cmake_policy(SET CMP0076 NEW) cmake_policy(SET CMP0079 NEW) @@ -32,6 +32,7 @@ option(WITH_KIO "Build KIO support module" ON) # Dependencies ## Qt +set(QT_VERSION_MAJOR 5) find_package(Qt5 COMPONENTS Widgets DBus Gui Xml Network Core REQUIRED) find_package(Boost COMPONENTS) @@ -114,12 +115,18 @@ endif () message("Build type: ${CMAKE_BUILD_TYPE}") if(CMAKE_COMPILER_IS_GNUCXX) -target_compile_options(squawk PRIVATE - "-Wall;-Wextra" - "$<$:-g>" - "$<$:-O3>" - "-fno-sized-deallocation" # for eliminating _ZdlPvm - ) + set (COMPILE_OPTIONS -fno-sized-deallocation) # for eliminating _ZdlPvm + if (CMAKE_BUILD_TYPE STREQUAL "Release") + list(APPEND COMPILE_OPTIONS -O3) + endif() + if (CMAKE_BUILD_TYPE STREQUAL Debug) + list(APPEND COMPILE_OPTIONS -g) + list(APPEND COMPILE_OPTIONS -Wall) + list(APPEND COMPILE_OPTIONS -Wextra) + endif() + + message("Compilation options: " ${COMPILE_OPTIONS}) + target_compile_options(squawk PRIVATE ${COMPILE_OPTIONS}) endif(CMAKE_COMPILER_IS_GNUCXX) add_subdirectory(core) diff --git a/README.md b/README.md index ff36f3c..0af201f 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,12 @@ - QT 5.12 *(lower versions might work but it wasn't tested)* - lmdb -- CMake 3.3 or higher +- CMake 3.4 or higher - qxmpp 1.1.0 or higher - KDE Frameworks: kwallet (optional) - KDE Frameworks: KIO (optional) -- Boost +- Boost (just one little hpp from there) +- Imagemagick (for compilation, to rasterize an SVG logo) ### Getting diff --git a/packaging/Archlinux/PKGBUILD b/packaging/Archlinux/PKGBUILD index 68e1558..0d2aaaa 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -7,8 +7,8 @@ arch=('i686' 'x86_64') url="https://git.macaw.me/blue/squawk" license=('GPL3') depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdb' 'qxmpp>=1.1.0') -makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools') -optdepends=('kwallet: secure password storage (requires rebuild)') +makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools' 'boost') +optdepends=('kwallet: secure password storage (requires rebuild)' 'kio: better show in folder action (requires rebuild)') source=("$pkgname-$pkgver.tar.gz") sha256sums=('8e93d3dbe1fc35cfecb7783af409c6a264244d11609b2241d4fe77d43d068419') diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index 34f9400..de7f56f 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -255,7 +255,6 @@ void FeedView::updateGeometries() bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* m, uint32_t totalHeight) { uint32_t previousOffset = elementMargin; - bool success = true; QDateTime lastDate; for (int i = 0, size = m->rowCount(); i < size; ++i) { QModelIndex index = m->index(i, 0, rootIndex()); @@ -271,8 +270,7 @@ bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewIt QSize messageSize = itemDelegate(index)->sizeHint(option, index); if (previousOffset + messageSize.height() + elementMargin > totalHeight) { - success = false; - break; + return false; } uint32_t offsetX(0); @@ -295,10 +293,10 @@ bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewIt previousOffset += dateDeviderMargin * 2 + dividerMetrics.height(); if (previousOffset > totalHeight) { - success = false; + return false; } - return success; + return true; } From 62a59eb7a15f466ad737e9dc0ed6850d3d6a3d50 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 15 Jan 2022 15:36:49 +0300 Subject: [PATCH 017/137] Added logs for Shura to help me to debug a download attachment issue --- CHANGELOG.md | 7 +++++++ CMakeLists.txt | 2 +- core/archive.cpp | 1 + core/handlers/messagehandler.cpp | 3 +++ core/main.cpp | 2 +- ui/widgets/messageline/messagefeed.cpp | 2 +- 6 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09c382a..4052647 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## Squawk 0.2.1 (UNRELEASED) +### Bug fixes + +### Improvements + +### New features + ## Squawk 0.2.0 (Jan 10, 2022) ### Bug fixes - carbon copies switches on again after reconnection diff --git a/CMakeLists.txt b/CMakeLists.txt index b104667..cd9d793 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.4) -project(squawk VERSION 0.2.0 LANGUAGES CXX) +project(squawk VERSION 0.2.1 LANGUAGES CXX) cmake_policy(SET CMP0076 NEW) cmake_policy(SET CMP0079 NEW) diff --git a/core/archive.cpp b/core/archive.cpp index 2ca409c..cb65a53 100644 --- a/core/archive.cpp +++ b/core/archive.cpp @@ -123,6 +123,7 @@ bool Core::Archive::addElement(const Shared::Message& message) if (!opened) { throw Closed("addElement", jid.toStdString()); } + qDebug() << "Adding message with id " << message.getId(); QByteArray ba; QDataStream ds(&ba, QIODevice::WriteOnly); message.serialize(ds); diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index 33b3458..eb840f8 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -80,6 +80,7 @@ bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgo Contact* cnt = acc->rh->getContact(jid); if (cnt == 0) { cnt = acc->rh->addOutOfRosterContact(jid); + qDebug() << "appending message" << sMsg.getId() << "to an out of roster contact"; } if (outgoing) { if (forwarded) { @@ -162,6 +163,7 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp id = source.id(); } target.setStanzaId(source.stanzaId()); + qDebug() << "initializing message with originId:" << source.originId() << ", id:" << source.id() << ", stansaId:" << source.stanzaId(); #else id = source.id(); #endif @@ -170,6 +172,7 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp if (messageId.size() == 0) { target.generateRandomId(); //TODO out of desperation, I need at least a random ID messageId = target.getId(); + qDebug() << "Had do initialize a message with no id, assigning autogenerated" << messageId; } target.setFrom(source.from()); target.setTo(source.to()); diff --git a/core/main.cpp b/core/main.cpp index d6eea3e..7c94a12 100644 --- a/core/main.cpp +++ b/core/main.cpp @@ -54,7 +54,7 @@ int main(int argc, char *argv[]) #endif QApplication::setApplicationName("squawk"); QApplication::setApplicationDisplayName("Squawk"); - QApplication::setApplicationVersion("0.2.0"); + QApplication::setApplicationVersion("0.2.1"); QTranslator qtTranslator; qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); diff --git a/ui/widgets/messageline/messagefeed.cpp b/ui/widgets/messageline/messagefeed.cpp index 733cf1d..4803dce 100644 --- a/ui/widgets/messageline/messagefeed.cpp +++ b/ui/widgets/messageline/messagefeed.cpp @@ -86,7 +86,7 @@ void Models::MessageFeed::addMessage(const Shared::Message& msg) emit newMessage(msg); if (observersAmount == 0 && !msg.getForwarded()) { //not to notify when the message is delivered by the carbon copy - unreadMessages->insert(msg.getId()); //cuz it could be my own one or the one I read on another device + unreadMessages->insert(id); //cuz it could be my own one or the one I read on another device emit unreadMessagesCountChanged(); emit unnoticedMessage(msg); } From 6bee149e6b13d340483654316a3d391bfa6b29a0 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 16 Jan 2022 22:54:57 +0300 Subject: [PATCH 018/137] started to work on settings --- ui/squawk.cpp | 28 ++++++- ui/squawk.h | 6 +- ui/squawk.ui | 9 ++ ui/widgets/CMakeLists.txt | 1 + ui/widgets/settings/CMakeLists.txt | 7 ++ ui/widgets/settings/settings.cpp | 14 ++++ ui/widgets/settings/settings.h | 26 ++++++ ui/widgets/settings/settings.ui | 119 +++++++++++++++++++++++++++ ui/widgets/settings/settingslist.cpp | 27 ++++++ ui/widgets/settings/settingslist.h | 25 ++++++ 10 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 ui/widgets/settings/CMakeLists.txt create mode 100644 ui/widgets/settings/settings.cpp create mode 100644 ui/widgets/settings/settings.h create mode 100644 ui/widgets/settings/settings.ui create mode 100644 ui/widgets/settings/settingslist.cpp create mode 100644 ui/widgets/settings/settingslist.h diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 800f02a..4d22b34 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -25,6 +25,7 @@ Squawk::Squawk(QWidget *parent) : QMainWindow(parent), m_ui(new Ui::Squawk), accounts(0), + preferences(0), rosterModel(), conversations(), contextMenu(new QMenu()), @@ -55,6 +56,7 @@ Squawk::Squawk(QWidget *parent) : m_ui->comboBox->setCurrentIndex(static_cast(Shared::Availability::offline)); connect(m_ui->actionAccounts, &QAction::triggered, this, &Squawk::onAccounts); + connect(m_ui->actionPreferences, &QAction::triggered, this, &Squawk::onPreferences); connect(m_ui->actionAddContact, &QAction::triggered, this, &Squawk::onNewContact); connect(m_ui->actionAddConference, &QAction::triggered, this, &Squawk::onNewConference); connect(m_ui->actionQuit, &QAction::triggered, this, &Squawk::close); @@ -117,6 +119,22 @@ void Squawk::onAccounts() } } +void Squawk::onPreferences() +{ + if (preferences == 0) { + preferences = new Settings(); + preferences->setAttribute(Qt::WA_DeleteOnClose); + connect(preferences, &Settings::destroyed, this, &Squawk::onPreferencesClosed); + + preferences->show(); + } else { + preferences->show(); + preferences->raise(); + preferences->activateWindow(); + } +} + + void Squawk::onAccountsSizeChanged(unsigned int size) { if (size > 0) { @@ -173,6 +191,9 @@ void Squawk::closeEvent(QCloseEvent* event) if (accounts != 0) { accounts->close(); } + if (preferences != 0) { + preferences->close(); + } for (Conversations::const_iterator itr = conversations.begin(), end = conversations.end(); itr != end; ++itr) { disconnect(itr->second, &Conversation::destroyed, this, &Squawk::onConversationClosed); @@ -190,11 +211,16 @@ void Squawk::closeEvent(QCloseEvent* event) } -void Squawk::onAccountsClosed(QObject* parent) +void Squawk::onAccountsClosed() { accounts = 0; } +void Squawk::onPreferencesClosed() +{ + preferences = 0; +} + void Squawk::newAccount(const QMap& account) { rosterModel.addAccount(account); diff --git a/ui/squawk.h b/ui/squawk.h index cb93259..26dc0c9 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -38,6 +38,7 @@ #include "widgets/joinconference.h" #include "models/roster.h" #include "widgets/vcard/vcard.h" +#include "widgets/settings/settings.h" #include "shared/shared.h" @@ -117,6 +118,7 @@ private: QScopedPointer m_ui; Accounts* accounts; + Settings* preferences; Models::Roster rosterModel; Conversations conversations; QMenu* contextMenu; @@ -136,12 +138,14 @@ protected slots: private slots: void onAccounts(); + void onPreferences(); void onNewContact(); void onNewConference(); void onNewContactAccepted(); void onJoinConferenceAccepted(); void onAccountsSizeChanged(unsigned int size); - void onAccountsClosed(QObject* parent = 0); + void onAccountsClosed(); + void onPreferencesClosed(); void onConversationClosed(QObject* parent = 0); void onVCardClosed(); void onVCardSave(const Shared::VCard& card, const QString& account); diff --git a/ui/squawk.ui b/ui/squawk.ui index 01ffbba..840dfee 100644 --- a/ui/squawk.ui +++ b/ui/squawk.ui @@ -191,6 +191,7 @@ Settings + @@ -245,6 +246,14 @@ Add conference + + + + + + Preferences + + diff --git a/ui/widgets/CMakeLists.txt b/ui/widgets/CMakeLists.txt index c7e47e0..f3a2afe 100644 --- a/ui/widgets/CMakeLists.txt +++ b/ui/widgets/CMakeLists.txt @@ -22,3 +22,4 @@ target_sources(squawk PRIVATE add_subdirectory(vcard) add_subdirectory(messageline) +add_subdirectory(settings) diff --git a/ui/widgets/settings/CMakeLists.txt b/ui/widgets/settings/CMakeLists.txt new file mode 100644 index 0000000..9f0fa76 --- /dev/null +++ b/ui/widgets/settings/CMakeLists.txt @@ -0,0 +1,7 @@ +target_sources(squawk PRIVATE + settingslist.h + settingslist.cpp + settings.h + settings.cpp + settings.ui +) diff --git a/ui/widgets/settings/settings.cpp b/ui/widgets/settings/settings.cpp new file mode 100644 index 0000000..0397d0c --- /dev/null +++ b/ui/widgets/settings/settings.cpp @@ -0,0 +1,14 @@ +#include "settings.h" +#include "ui_settings.h" + +Settings::Settings(QWidget* parent): + QWidget(parent), + m_ui(new Ui::Settings()) +{ + m_ui->setupUi(this); +} + +Settings::~Settings() +{ +} + diff --git a/ui/widgets/settings/settings.h b/ui/widgets/settings/settings.h new file mode 100644 index 0000000..61129d8 --- /dev/null +++ b/ui/widgets/settings/settings.h @@ -0,0 +1,26 @@ +#ifndef SETTINGS_H +#define SETTINGS_H + +#include +#include + +namespace Ui +{ +class Settings; +} + +/** + * @todo write docs + */ +class Settings : public QWidget +{ + Q_OBJECT +public: + Settings(QWidget* parent = nullptr); + ~Settings(); + +private: + QScopedPointer m_ui; +}; + +#endif // SETTINGS_H diff --git a/ui/widgets/settings/settings.ui b/ui/widgets/settings/settings.ui new file mode 100644 index 0000000..f5c4680 --- /dev/null +++ b/ui/widgets/settings/settings.ui @@ -0,0 +1,119 @@ + + + Settings + + + + 0 + 0 + 520 + 363 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + 0 + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustToContents + + + QAbstractItemView::NoEditTriggers + + + false + + + QAbstractItemView::NoDragDrop + + + QAbstractItemView::ScrollPerPixel + + + QListView::Static + + + QListView::TopToBottom + + + false + + + QListView::Adjust + + + QListView::Batched + + + QListView::IconMode + + + true + + + Qt::AlignHCenter + + + 0 + + + + General + + + + .. + + + ItemIsSelectable|ItemIsEnabled + + + + + Appearance + + + + .. + + + ItemIsSelectable|ItemIsEnabled + + + + + + + + + SettingsList + QListWidget +
ui/widgets/settings/settingslist.h
+
+
+ + +
diff --git a/ui/widgets/settings/settingslist.cpp b/ui/widgets/settings/settingslist.cpp new file mode 100644 index 0000000..1925632 --- /dev/null +++ b/ui/widgets/settings/settingslist.cpp @@ -0,0 +1,27 @@ +#include "settingslist.h" + +SettingsList::SettingsList(QWidget* parent): + QListWidget(parent), + lastWidth(0) +{ +} + +SettingsList::~SettingsList() +{ +} + +QStyleOptionViewItem SettingsList::viewOptions() const +{ + QStyleOptionViewItem option = QListWidget::viewOptions(); + if (!iconSize().isValid()) { + option.decorationSize.setWidth(lastWidth); + } + option.rect.setWidth(lastWidth); + return option; +} + +void SettingsList::resizeEvent(QResizeEvent* event) +{ + lastWidth = event->size().width(); + QListWidget::resizeEvent(event); +} diff --git a/ui/widgets/settings/settingslist.h b/ui/widgets/settings/settingslist.h new file mode 100644 index 0000000..9621c67 --- /dev/null +++ b/ui/widgets/settings/settingslist.h @@ -0,0 +1,25 @@ +#ifndef UI_SETTINGSLIST_H +#define UI_SETTINGSLIST_H + +#include +#include + +/** + * @todo write docs + */ +class SettingsList : public QListWidget +{ + Q_OBJECT +public: + SettingsList(QWidget* parent = nullptr); + ~SettingsList(); + +protected: + QStyleOptionViewItem viewOptions() const override; + void resizeEvent(QResizeEvent * event) override; + +private: + int lastWidth; +}; + +#endif // UI_SETTINGSLIST_H From 841e526e59fd7a12d6835de1bdee8967aaa3b5a1 Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 17 Jan 2022 23:52:07 +0300 Subject: [PATCH 019/137] just some toying with designer --- ui/widgets/settings/settings.ui | 64 +++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/ui/widgets/settings/settings.ui b/ui/widgets/settings/settings.ui index f5c4680..ca9946e 100644 --- a/ui/widgets/settings/settings.ui +++ b/ui/widgets/settings/settings.ui @@ -6,7 +6,7 @@ 0 0 - 520 + 465 363 @@ -21,13 +21,51 @@ 0 - 0 + 7 0 - + + + + Apply + + + + + + + + + + Ok + + + + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + 120 + 16777215 + + QFrame::NoFrame @@ -105,6 +143,26 @@ + + + + + + + General + + + + + + + Cancel + + + + + + From a8a7ce2538887e6a4875ffa7f68a3f2859bd7dab Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 19 Jan 2022 23:46:42 +0300 Subject: [PATCH 020/137] some more thoughts about settings widgets --- ui/widgets/settings/CMakeLists.txt | 6 ++ ui/widgets/settings/pageappearance.cpp | 13 +++ ui/widgets/settings/pageappearance.h | 26 +++++ ui/widgets/settings/pageappearance.ui | 28 ++++++ ui/widgets/settings/pagegeneral.cpp | 13 +++ ui/widgets/settings/pagegeneral.h | 26 +++++ ui/widgets/settings/pagegeneral.ui | 28 ++++++ ui/widgets/settings/settings.cpp | 10 ++ ui/widgets/settings/settings.h | 4 + ui/widgets/settings/settings.ui | 127 ++++++++++++++++--------- ui/widgets/settings/settingslist.cpp | 11 +++ ui/widgets/settings/settingslist.h | 1 + 12 files changed, 248 insertions(+), 45 deletions(-) create mode 100644 ui/widgets/settings/pageappearance.cpp create mode 100644 ui/widgets/settings/pageappearance.h create mode 100644 ui/widgets/settings/pageappearance.ui create mode 100644 ui/widgets/settings/pagegeneral.cpp create mode 100644 ui/widgets/settings/pagegeneral.h create mode 100644 ui/widgets/settings/pagegeneral.ui diff --git a/ui/widgets/settings/CMakeLists.txt b/ui/widgets/settings/CMakeLists.txt index 9f0fa76..e100bfe 100644 --- a/ui/widgets/settings/CMakeLists.txt +++ b/ui/widgets/settings/CMakeLists.txt @@ -4,4 +4,10 @@ target_sources(squawk PRIVATE settings.h settings.cpp settings.ui + pagegeneral.h + pagegeneral.cpp + pagegeneral.ui + pageappearance.h + pageappearance.cpp + pageappearance.ui ) diff --git a/ui/widgets/settings/pageappearance.cpp b/ui/widgets/settings/pageappearance.cpp new file mode 100644 index 0000000..725f452 --- /dev/null +++ b/ui/widgets/settings/pageappearance.cpp @@ -0,0 +1,13 @@ +#include "pageappearance.h" +#include "ui_pageappearance.h" + +PageAppearance::PageAppearance(QWidget* parent): + QWidget(parent), + m_ui(new Ui::PageAppearance()) +{ + m_ui->setupUi(this); +} + +PageAppearance::~PageAppearance() +{ +} diff --git a/ui/widgets/settings/pageappearance.h b/ui/widgets/settings/pageappearance.h new file mode 100644 index 0000000..85d45a1 --- /dev/null +++ b/ui/widgets/settings/pageappearance.h @@ -0,0 +1,26 @@ +#ifndef PAGEAPPEARANCE_H +#define PAGEAPPEARANCE_H + +#include +#include + +namespace Ui +{ +class PageAppearance; +} + +/** + * @todo write docs + */ +class PageAppearance : public QWidget +{ + Q_OBJECT +public: + PageAppearance(QWidget* parent = nullptr); + ~PageAppearance(); + +private: + QScopedPointer m_ui; +}; + +#endif // PAGEAPPEARANCE_H diff --git a/ui/widgets/settings/pageappearance.ui b/ui/widgets/settings/pageappearance.ui new file mode 100644 index 0000000..1199347 --- /dev/null +++ b/ui/widgets/settings/pageappearance.ui @@ -0,0 +1,28 @@ + + + PageAppearance + + + + 0 + 0 + 400 + 300 + + + + + + + Theme + + + + + + + + + + + diff --git a/ui/widgets/settings/pagegeneral.cpp b/ui/widgets/settings/pagegeneral.cpp new file mode 100644 index 0000000..e448f80 --- /dev/null +++ b/ui/widgets/settings/pagegeneral.cpp @@ -0,0 +1,13 @@ +#include "pagegeneral.h" +#include "ui_pagegeneral.h" + +PageGeneral::PageGeneral(QWidget* parent): + QWidget(parent), + m_ui(new Ui::PageGeneral()) +{ + m_ui->setupUi(this); +} + +PageGeneral::~PageGeneral() +{ +} diff --git a/ui/widgets/settings/pagegeneral.h b/ui/widgets/settings/pagegeneral.h new file mode 100644 index 0000000..77c0c3a --- /dev/null +++ b/ui/widgets/settings/pagegeneral.h @@ -0,0 +1,26 @@ +#ifndef PAGEGENERAL_H +#define PAGEGENERAL_H + +#include +#include + +namespace Ui +{ +class PageGeneral; +} + +/** + * @todo write docs + */ +class PageGeneral : public QWidget +{ + Q_OBJECT +public: + PageGeneral(QWidget* parent = nullptr); + ~PageGeneral(); + +private: + QScopedPointer m_ui; +}; + +#endif // PAGEGENERAL_H diff --git a/ui/widgets/settings/pagegeneral.ui b/ui/widgets/settings/pagegeneral.ui new file mode 100644 index 0000000..9921715 --- /dev/null +++ b/ui/widgets/settings/pagegeneral.ui @@ -0,0 +1,28 @@ + + + PageGeneral + + + + 0 + 0 + 400 + 300 + + + + + + + Downloads path + + + + + + + + + + + diff --git a/ui/widgets/settings/settings.cpp b/ui/widgets/settings/settings.cpp index 0397d0c..cdcf0cc 100644 --- a/ui/widgets/settings/settings.cpp +++ b/ui/widgets/settings/settings.cpp @@ -6,9 +6,19 @@ Settings::Settings(QWidget* parent): m_ui(new Ui::Settings()) { m_ui->setupUi(this); + + connect(m_ui->list, &QListWidget::currentItemChanged, this, &Settings::onCurrentPageChanged); } Settings::~Settings() { } +void Settings::onCurrentPageChanged(QListWidgetItem* current) +{ + if (current != nullptr) { + m_ui->header->setText(current->text()); + + m_ui->content->setCurrentIndex(m_ui->list->currentRow()); + } +} diff --git a/ui/widgets/settings/settings.h b/ui/widgets/settings/settings.h index 61129d8..f961e08 100644 --- a/ui/widgets/settings/settings.h +++ b/ui/widgets/settings/settings.h @@ -2,6 +2,7 @@ #define SETTINGS_H #include +#include #include namespace Ui @@ -19,6 +20,9 @@ public: Settings(QWidget* parent = nullptr); ~Settings(); +protected slots: + void onCurrentPageChanged(QListWidgetItem* current); + private: QScopedPointer m_ui; }; diff --git a/ui/widgets/settings/settings.ui b/ui/widgets/settings/settings.ui index ca9946e..fe092dc 100644 --- a/ui/widgets/settings/settings.ui +++ b/ui/widgets/settings/settings.ui @@ -6,7 +6,7 @@ 0 0 - 465 + 502 363 @@ -21,33 +21,13 @@ 0 - 7 + 0 0 - - - - Apply - - - - - - - - - - Ok - - - - - - - - + + 0 @@ -66,12 +46,12 @@ 16777215 + + + QFrame::NoFrame - - 0 - Qt::ScrollBarAlwaysOff @@ -103,7 +83,7 @@ QListView::Adjust - QListView::Batched + QListView::SinglePass QListView::IconMode @@ -143,24 +123,71 @@ - - - - - - - General - - - - - - - Cancel - - - + + + + + 0 + 0 + + + + + + Apply + + + + .. + + + + + + + Cancel + + + + .. + + + + + + + Ok + + + + .. + + + + + + + font-size: 14pt; + + + General + + + + + + + + 0 + 0 + + + + + + + @@ -171,6 +198,16 @@ QListWidget
ui/widgets/settings/settingslist.h
+ + PageGeneral + QWidget +
ui/widgets/settings/pagegeneral.h
+
+ + PageAppearance + QWidget +
ui/widgets/settings/pageappearance.h
+
diff --git a/ui/widgets/settings/settingslist.cpp b/ui/widgets/settings/settingslist.cpp index 1925632..3a5e2cb 100644 --- a/ui/widgets/settings/settingslist.cpp +++ b/ui/widgets/settings/settingslist.cpp @@ -4,6 +4,7 @@ SettingsList::SettingsList(QWidget* parent): QListWidget(parent), lastWidth(0) { + } SettingsList::~SettingsList() @@ -25,3 +26,13 @@ void SettingsList::resizeEvent(QResizeEvent* event) lastWidth = event->size().width(); QListWidget::resizeEvent(event); } + +QRect SettingsList::visualRect(const QModelIndex& index) const +{ + QRect res = QListWidget::visualRect(index); + if (index.isValid()) { + res.setWidth(lastWidth); + } + return res; +} + diff --git a/ui/widgets/settings/settingslist.h b/ui/widgets/settings/settingslist.h index 9621c67..a51fc3a 100644 --- a/ui/widgets/settings/settingslist.h +++ b/ui/widgets/settings/settingslist.h @@ -17,6 +17,7 @@ public: protected: QStyleOptionViewItem viewOptions() const override; void resizeEvent(QResizeEvent * event) override; + QRect visualRect(const QModelIndex & index) const override; private: int lastWidth; From c708c33a92153e820cd37f266bf8995682148404 Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 21 Jan 2022 22:02:50 +0300 Subject: [PATCH 021/137] basic theme changing --- core/main.cpp | 11 ++++++- shared/global.cpp | 1 + shared/global.h | 5 +++- ui/widgets/settings/pageappearance.cpp | 31 +++++++++++++++++++- ui/widgets/settings/pageappearance.h | 11 +++++++ ui/widgets/settings/pagegeneral.h | 4 +++ ui/widgets/settings/settings.cpp | 40 +++++++++++++++++++++++++- ui/widgets/settings/settings.h | 9 ++++++ 8 files changed, 108 insertions(+), 4 deletions(-) diff --git a/core/main.cpp b/core/main.cpp index 7c94a12..03827d3 100644 --- a/core/main.cpp +++ b/core/main.cpp @@ -55,7 +55,7 @@ int main(int argc, char *argv[]) QApplication::setApplicationName("squawk"); QApplication::setApplicationDisplayName("Squawk"); QApplication::setApplicationVersion("0.2.1"); - + QTranslator qtTranslator; qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); app.installTranslator(&qtTranslator); @@ -88,6 +88,15 @@ int main(int argc, char *argv[]) QApplication::setWindowIcon(icon); new Shared::Global(); //translates enums + + QSettings settings; + QVariant vtheme = settings.value("theme"); + if (vtheme.isValid()) { + QString theme = vtheme.toString().toLower(); + if (theme != "system") { + QApplication::setStyle(theme); + } + } Squawk w; w.show(); diff --git a/shared/global.cpp b/shared/global.cpp index 5f3b4ed..7ee9599 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -84,6 +84,7 @@ Shared::Global::Global(): tr("Squawk is going to query you for the password on every start of the program", "AccountPasswordDescription"), tr("Your password is going to be stored in KDE wallet storage (KWallet). You're going to be queried for permissions", "AccountPasswordDescription") }), + defaultSystemStyle(QApplication::style()->objectName()), pluginSupport({ {"KWallet", false}, {"openFileManagerWindowJob", false} diff --git a/shared/global.h b/shared/global.h index b1ae59c..a87b9bc 100644 --- a/shared/global.h +++ b/shared/global.h @@ -27,7 +27,8 @@ #include #include -#include +#include +#include #include #include #include @@ -84,6 +85,8 @@ namespace Shared { const std::deque accountPassword; const std::deque accountPasswordDescription; + + const QString defaultSystemStyle; static bool supported(const QString& pluginName); static void setSupported(const QString& pluginName, bool support); diff --git a/ui/widgets/settings/pageappearance.cpp b/ui/widgets/settings/pageappearance.cpp index 725f452..2fb8dc8 100644 --- a/ui/widgets/settings/pageappearance.cpp +++ b/ui/widgets/settings/pageappearance.cpp @@ -1,13 +1,42 @@ #include "pageappearance.h" #include "ui_pageappearance.h" +#include + PageAppearance::PageAppearance(QWidget* parent): QWidget(parent), - m_ui(new Ui::PageAppearance()) + m_ui(new Ui::PageAppearance()), + styles() { m_ui->setupUi(this); + + m_ui->themeInput->addItem(tr("System")); + styles.push_back("system"); + QStringList themes = QStyleFactory::keys(); + for (const QString& key : themes) { + m_ui->themeInput->addItem(key); + styles.push_back(key); + } + + QSettings settings; + QVariant vtheme = settings.value("theme"); + if (vtheme.isValid()) { + QString theme = vtheme.toString(); + m_ui->themeInput->setCurrentText(theme); + } else { + m_ui->themeInput->setCurrentText("System"); + } + + connect(m_ui->themeInput, qOverload(&QComboBox::currentIndexChanged), this, &PageAppearance::onThemeChanged); } PageAppearance::~PageAppearance() { } + +void PageAppearance::onThemeChanged(int index) +{ + if (index >= 0) { + emit variableModified("theme", styles[index]); + } +} diff --git a/ui/widgets/settings/pageappearance.h b/ui/widgets/settings/pageappearance.h index 85d45a1..9cb1830 100644 --- a/ui/widgets/settings/pageappearance.h +++ b/ui/widgets/settings/pageappearance.h @@ -3,6 +3,10 @@ #include #include +#include +#include +#include +#include namespace Ui { @@ -19,8 +23,15 @@ public: PageAppearance(QWidget* parent = nullptr); ~PageAppearance(); +signals: + void variableModified(const QString& key, const QVariant& value); + +protected slots: + void onThemeChanged(int index); + private: QScopedPointer m_ui; + std::vector styles; }; #endif // PAGEAPPEARANCE_H diff --git a/ui/widgets/settings/pagegeneral.h b/ui/widgets/settings/pagegeneral.h index 77c0c3a..b8c30c5 100644 --- a/ui/widgets/settings/pagegeneral.h +++ b/ui/widgets/settings/pagegeneral.h @@ -3,6 +3,7 @@ #include #include +#include namespace Ui { @@ -19,6 +20,9 @@ public: PageGeneral(QWidget* parent = nullptr); ~PageGeneral(); +signals: + void variableModified(const QString& key, const QVariant& value); + private: QScopedPointer m_ui; }; diff --git a/ui/widgets/settings/settings.cpp b/ui/widgets/settings/settings.cpp index cdcf0cc..75a7780 100644 --- a/ui/widgets/settings/settings.cpp +++ b/ui/widgets/settings/settings.cpp @@ -3,11 +3,19 @@ Settings::Settings(QWidget* parent): QWidget(parent), - m_ui(new Ui::Settings()) + m_ui(new Ui::Settings()), + modifiedSettings() { m_ui->setupUi(this); connect(m_ui->list, &QListWidget::currentItemChanged, this, &Settings::onCurrentPageChanged); + + connect(m_ui->General, &PageGeneral::variableModified, this, &Settings::onVariableModified); + connect(m_ui->Appearance, &PageAppearance::variableModified, this, &Settings::onVariableModified); + + connect(m_ui->applyButton, &QPushButton::clicked, this, &Settings::apply); + connect(m_ui->okButton, &QPushButton::clicked, this, &Settings::confirm); + connect(m_ui->cancelButton, &QPushButton::clicked, this, &Settings::close); } Settings::~Settings() @@ -22,3 +30,33 @@ void Settings::onCurrentPageChanged(QListWidgetItem* current) m_ui->content->setCurrentIndex(m_ui->list->currentRow()); } } + +void Settings::onVariableModified(const QString& key, const QVariant& value) +{ + modifiedSettings[key] = value; +} + +void Settings::apply() +{ + QSettings settings; + for (const std::pair& pair: modifiedSettings) { + if (pair.first == "theme") { + QString theme = pair.second.toString(); + if (theme.toLower() == "system") { + QApplication::setStyle(Shared::Global::getInstance()->defaultSystemStyle); + } else { + QApplication::setStyle(theme); + } + } + + settings.setValue(pair.first, pair.second); + } + + modifiedSettings.clear(); +} + +void Settings::confirm() +{ + apply(); + close(); +} diff --git a/ui/widgets/settings/settings.h b/ui/widgets/settings/settings.h index f961e08..5031139 100644 --- a/ui/widgets/settings/settings.h +++ b/ui/widgets/settings/settings.h @@ -4,6 +4,9 @@ #include #include #include +#include + +#include "shared/global.h" namespace Ui { @@ -20,11 +23,17 @@ public: Settings(QWidget* parent = nullptr); ~Settings(); +public slots: + void apply(); + void confirm(); + protected slots: void onCurrentPageChanged(QListWidgetItem* current); + void onVariableModified(const QString& key, const QVariant& value); private: QScopedPointer m_ui; + std::map modifiedSettings; }; #endif // SETTINGS_H From 802e2f11a1a990dc720490ce098c864ee21edf61 Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 25 Jan 2022 23:35:55 +0300 Subject: [PATCH 022/137] may be a bit better quit handling --- core/main.cpp | 21 ++++++++++++--------- core/signalcatcher.cpp | 2 +- core/signalcatcher.h | 3 +++ ui/squawk.cpp | 4 ++++ ui/squawk.h | 3 +-- ui/widgets/settings/pageappearance.cpp | 15 +++++++++++++++ ui/widgets/settings/pageappearance.ui | 10 ++++++++++ 7 files changed, 46 insertions(+), 12 deletions(-) diff --git a/core/main.cpp b/core/main.cpp index 03827d3..c7bb820 100644 --- a/core/main.cpp +++ b/core/main.cpp @@ -105,11 +105,15 @@ int main(int argc, char *argv[]) QThread* coreThread = new QThread(); squawk->moveToThread(coreThread); + QObject::connect(&sc, &SignalCatcher::interrupt, &app, &QApplication::closeAllWindows); + QObject::connect(coreThread, &QThread::started, squawk, &Core::Squawk::start); - QObject::connect(&app, &QApplication::aboutToQuit, squawk, &Core::Squawk::stop); - QObject::connect(&app, &QApplication::aboutToQuit, &w, &QMainWindow::close); - QObject::connect(squawk, &Core::Squawk::quit, coreThread, &QThread::quit); - QObject::connect(coreThread, &QThread::finished, squawk, &Core::Squawk::deleteLater); + QObject::connect(&app, &QApplication::lastWindowClosed, squawk, &Core::Squawk::stop); + QObject::connect(&app, &QApplication::lastWindowClosed, &w, &Squawk::writeSettings); + //QObject::connect(&app, &QApplication::aboutToQuit, &w, &QMainWindow::close); + QObject::connect(squawk, &Core::Squawk::quit, squawk, &Core::Squawk::deleteLater); + QObject::connect(squawk, &Core::Squawk::quit, coreThread, &QThread::quit, Qt::QueuedConnection); + QObject::connect(coreThread, &QThread::finished, &app, &QApplication::quit, Qt::QueuedConnection); QObject::connect(&w, &Squawk::newAccountRequest, squawk, &Core::Squawk::newAccountRequest); QObject::connect(&w, &Squawk::modifyAccountRequest, squawk, &Core::Squawk::modifyAccountRequest); @@ -169,12 +173,11 @@ int main(int argc, char *argv[]) QObject::connect(squawk, &Core::Squawk::ready, &w, &Squawk::readSettings); coreThread->start(); - int result = app.exec(); - - w.writeSettings(); - coreThread->wait(500); //TODO hate doing that but settings for some reason don't get saved to the disk - + + if (coreThread->isRunning()) { + coreThread->wait(); + } return result; } diff --git a/core/signalcatcher.cpp b/core/signalcatcher.cpp index dcdcb88..046c67e 100644 --- a/core/signalcatcher.cpp +++ b/core/signalcatcher.cpp @@ -50,7 +50,7 @@ void SignalCatcher::handleSigInt() char tmp; ssize_t s = ::read(sigintFd[1], &tmp, sizeof(tmp)); - app->quit(); + emit interrupt(); snInt->setEnabled(true); } diff --git a/core/signalcatcher.h b/core/signalcatcher.h index 7d75260..0c8e757 100644 --- a/core/signalcatcher.h +++ b/core/signalcatcher.h @@ -33,6 +33,9 @@ public: static void intSignalHandler(int unused); +signals: + void interrupt(); + public slots: void handleSigInt(); diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 4d22b34..7acda3d 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -883,6 +883,10 @@ void Squawk::writeSettings() } settings.endGroup(); settings.endGroup(); + + settings.sync(); + + qDebug() << "Saved settings"; } void Squawk::onItemCollepsed(const QModelIndex& index) diff --git a/ui/squawk.h b/ui/squawk.h index 26dc0c9..b419057 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -54,8 +54,6 @@ public: explicit Squawk(QWidget *parent = nullptr); ~Squawk() override; - void writeSettings(); - signals: void newAccountRequest(const QMap&); void modifyAccountRequest(const QString&, const QMap&); @@ -84,6 +82,7 @@ signals: void localPathInvalid(const QString& path); public slots: + void writeSettings(); void readSettings(); void newAccount(const QMap& account); void changeAccount(const QString& account, const QMap& data); diff --git a/ui/widgets/settings/pageappearance.cpp b/ui/widgets/settings/pageappearance.cpp index 2fb8dc8..1c558f2 100644 --- a/ui/widgets/settings/pageappearance.cpp +++ b/ui/widgets/settings/pageappearance.cpp @@ -2,6 +2,10 @@ #include "ui_pageappearance.h" #include +#include +#include + +static const QStringList filters = {"*.colors"}; PageAppearance::PageAppearance(QWidget* parent): QWidget(parent), @@ -28,6 +32,17 @@ PageAppearance::PageAppearance(QWidget* parent): } connect(m_ui->themeInput, qOverload(&QComboBox::currentIndexChanged), this, &PageAppearance::onThemeChanged); + + m_ui->colorInput->addItem(tr("System")); + const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, "color-schemes", QStandardPaths::LocateDirectory); + QStringList schemeFiles; + for (const QString &dir : dirs) { + const QStringList fileNames = QDir(dir).entryList(filters); + for (const QString &file : fileNames) { + m_ui->colorInput->addItem(dir + QDir::separator() + file); + } + } + } PageAppearance::~PageAppearance() diff --git a/ui/widgets/settings/pageappearance.ui b/ui/widgets/settings/pageappearance.ui index 1199347..afcb3a8 100644 --- a/ui/widgets/settings/pageappearance.ui +++ b/ui/widgets/settings/pageappearance.ui @@ -21,6 +21,16 @@ + + + + + + + Color scheme + + + From 0ff9f12157a8223de3d324ed1e4e82c5166df624 Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 26 Jan 2022 23:53:44 +0300 Subject: [PATCH 023/137] new optional KDE Frameworks plugin to support system color schemes --- CMakeLists.txt | 19 +++++++++++++ core/main.cpp | 5 +++- plugins/CMakeLists.txt | 6 ++++ plugins/colorschemetools.cpp | 39 ++++++++++++++++++++++++++ shared/global.cpp | 37 +++++++++++++++++++++++- shared/global.h | 11 ++++++++ ui/widgets/settings/pageappearance.cpp | 21 ++++++++------ ui/widgets/settings/pageappearance.h | 2 ++ 8 files changed, 130 insertions(+), 10 deletions(-) create mode 100644 plugins/colorschemetools.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index cd9d793..717cf91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}) option(SYSTEM_QXMPP "Use system qxmpp lib" ON) option(WITH_KWALLET "Build KWallet support module" ON) option(WITH_KIO "Build KIO support module" ON) +option(WITH_KCONFIG "Build KConfig support module" ON) # Dependencies ## Qt @@ -90,6 +91,24 @@ if (WITH_KWALLET) endif () endif () +if (WITH_KCONFIG) + find_package(KF5Config CONFIG) + if (NOT KF5Config_FOUND) + set(WITH_KCONFIG OFF) + message("KConfig package wasn't found, KConfig support modules wouldn't be built") + else() + find_package(KF5ConfigWidgets CONFIG) + if (NOT KF5ConfigWidgets_FOUND) + set(WITH_KCONFIG OFF) + message("KConfigWidgets package wasn't found, KConfigWidgets support modules wouldn't be built") + else() + target_compile_definitions(squawk PRIVATE WITH_KCONFIG) + message("Building with support of KConfig") + message("Building with support of KConfigWidgets") + endif() + endif() +endif() + ## Signal (TODO) # find_package(Signal REQUIRED) diff --git a/core/main.cpp b/core/main.cpp index c7bb820..03de307 100644 --- a/core/main.cpp +++ b/core/main.cpp @@ -176,7 +176,10 @@ int main(int argc, char *argv[]) int result = app.exec(); if (coreThread->isRunning()) { - coreThread->wait(); + //coreThread->wait(); + //todo if I uncomment that, the app will no quit if it has reconnected at least once + //it feels like a symptom of something badly desinged in the core coreThread + //need to investigate; } return result; diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 84fc09b..5a5a41d 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -2,3 +2,9 @@ if (WITH_KIO) add_library(openFileManagerWindowJob SHARED openfilemanagerwindowjob.cpp) target_link_libraries(openFileManagerWindowJob PRIVATE KF5::KIOWidgets) endif () + +if (WITH_KCONFIG) + add_library(colorSchemeTools SHARED colorschemetools.cpp) + target_link_libraries(colorSchemeTools PRIVATE KF5::ConfigCore) + target_link_libraries(colorSchemeTools PRIVATE KF5::ConfigWidgets) +endif() diff --git a/plugins/colorschemetools.cpp b/plugins/colorschemetools.cpp new file mode 100644 index 0000000..0ad8e24 --- /dev/null +++ b/plugins/colorschemetools.cpp @@ -0,0 +1,39 @@ +#include +#include + +#include +#include + +QPixmap createPixmap(int size, const QBrush& window, const QBrush& button, const QBrush& view, const QBrush& selection); + +extern "C" QIcon* createPreview(const QString& path) { + KSharedConfigPtr schemeConfig = KSharedConfig::openConfig(path); + QIcon* result = new QIcon(); + KColorScheme activeWindow(QPalette::Active, KColorScheme::Window, schemeConfig); + KColorScheme activeButton(QPalette::Active, KColorScheme::Button, schemeConfig); + KColorScheme activeView(QPalette::Active, KColorScheme::View, schemeConfig); + KColorScheme activeSelection(QPalette::Active, KColorScheme::Selection, schemeConfig); + + result->addPixmap(createPixmap(16, activeWindow.background(), activeButton.background(), activeView.background(), activeSelection.background())); + result->addPixmap(createPixmap(24, activeWindow.background(), activeButton.background(), activeView.background(), activeSelection.background())); + + return result; +} + +extern "C" void deletePreview(QIcon* icon) { + delete icon; +} + +QPixmap createPixmap(int size, const QBrush& window, const QBrush& button, const QBrush& view, const QBrush& selection) { + QPixmap pix(size, size); + pix.fill(Qt::black); + QPainter p; + p.begin(&pix); + const int itemSize = size / 2 - 1; + p.fillRect(1, 1, itemSize, itemSize, window); + p.fillRect(1 + itemSize, 1, itemSize, itemSize, button); + p.fillRect(1, 1 + itemSize, itemSize, itemSize, view); + p.fillRect(1 + itemSize, 1 + itemSize, itemSize, itemSize, selection); + p.end(); + return pix; +} diff --git a/shared/global.cpp b/shared/global.cpp index 7ee9599..7dafed6 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -28,6 +28,12 @@ QLibrary Shared::Global::openFileManagerWindowJob("openFileManagerWindowJob"); Shared::Global::HighlightInFileManager Shared::Global::hfm = 0; #endif +#ifdef WITH_KCONFIG +QLibrary Shared::Global::colorSchemeTools("colorSchemeTools"); +Shared::Global::CreatePreview Shared::Global::createPreview = 0; +Shared::Global::DeletePreview Shared::Global::deletePreview = 0; +#endif + Shared::Global::Global(): availability({ tr("Online", "Availability"), @@ -87,7 +93,8 @@ Shared::Global::Global(): defaultSystemStyle(QApplication::style()->objectName()), pluginSupport({ {"KWallet", false}, - {"openFileManagerWindowJob", false} + {"openFileManagerWindowJob", false}, + {"colorSchemeTools", false} }), fileCache() { @@ -111,6 +118,22 @@ Shared::Global::Global(): qDebug() << "KIO::OpenFileManagerWindow support disabled: couldn't load the library" << openFileManagerWindowJob.errorString(); } #endif + +#ifdef WITH_KCONFIG + colorSchemeTools.load(); + if (colorSchemeTools.isLoaded()) { + createPreview = (CreatePreview) colorSchemeTools.resolve("createPreview"); + deletePreview = (DeletePreview) colorSchemeTools.resolve("deletePreview"); + if (createPreview && deletePreview) { + setSupported("colorSchemeTools", true); + qDebug() << "Color Schemes support enabled"; + } else { + qDebug() << "Color Schemes support disabled: couldn't resolve required methods in the library"; + } + } else { + qDebug() << "Color Schemes support disabled: couldn't load the library" << colorSchemeTools.errorString(); + } +#endif } @@ -276,6 +299,18 @@ void Shared::Global::highlightInFileManager(const QString& path) } } +QIcon Shared::Global::createThemePreview(const QString& path) +{ + if (supported("colorSchemeTools")) { + QIcon* icon = createPreview(path); + QIcon localIcon = *icon; + deletePreview(icon); + return localIcon; + } else { + return QIcon(); + } +} + #define FROM_INT_INPL(Enum) \ template<> \ diff --git a/shared/global.h b/shared/global.h index a87b9bc..bd3c9a6 100644 --- a/shared/global.h +++ b/shared/global.h @@ -95,6 +95,7 @@ namespace Shared { static FileInfo getFileInfo(const QString& path); static void highlightInFileManager(const QString& path); + static QIcon createThemePreview(const QString& path); template static T fromInt(int src); @@ -128,6 +129,16 @@ namespace Shared { static HighlightInFileManager hfm; #endif + +#ifdef WITH_KCONFIG + static QLibrary colorSchemeTools; + + typedef QIcon* (*CreatePreview)(const QString&); + typedef void (*DeletePreview)(QIcon*); + + static CreatePreview createPreview; + static DeletePreview deletePreview; +#endif }; } diff --git a/ui/widgets/settings/pageappearance.cpp b/ui/widgets/settings/pageappearance.cpp index 1c558f2..57da7aa 100644 --- a/ui/widgets/settings/pageappearance.cpp +++ b/ui/widgets/settings/pageappearance.cpp @@ -33,16 +33,19 @@ PageAppearance::PageAppearance(QWidget* parent): connect(m_ui->themeInput, qOverload(&QComboBox::currentIndexChanged), this, &PageAppearance::onThemeChanged); - m_ui->colorInput->addItem(tr("System")); - const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, "color-schemes", QStandardPaths::LocateDirectory); - QStringList schemeFiles; - for (const QString &dir : dirs) { - const QStringList fileNames = QDir(dir).entryList(filters); - for (const QString &file : fileNames) { - m_ui->colorInput->addItem(dir + QDir::separator() + file); + if (Shared::Global::supported("colorSchemeTools")) { + m_ui->colorInput->addItem(tr("System")); + const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, "color-schemes", QStandardPaths::LocateDirectory); + QStringList schemeFiles; + for (const QString &dir : dirs) { + const QStringList fileNames = QDir(dir).entryList(filters); + for (const QString &file : fileNames) { + m_ui->colorInput->addItem(Shared::Global::createThemePreview(dir + QDir::separator() + file), file); + } } + } else { + m_ui->colorInput->setEnabled(false); } - } PageAppearance::~PageAppearance() @@ -55,3 +58,5 @@ void PageAppearance::onThemeChanged(int index) emit variableModified("theme", styles[index]); } } + + diff --git a/ui/widgets/settings/pageappearance.h b/ui/widgets/settings/pageappearance.h index 9cb1830..b31f3c1 100644 --- a/ui/widgets/settings/pageappearance.h +++ b/ui/widgets/settings/pageappearance.h @@ -8,6 +8,8 @@ #include #include +#include "shared/global.h" + namespace Ui { class PageAppearance; From da19eb86bbb9fa6e52189e44524bf8d16f73b84b Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 27 Jan 2022 20:44:32 +0300 Subject: [PATCH 024/137] color theme setting is now working --- CHANGELOG.md | 5 ++ README.md | 3 ++ core/main.cpp | 21 ++++++-- .../wrappers/CMakeLists.txt | 2 + packaging/Archlinux/PKGBUILD | 5 +- plugins/CMakeLists.txt | 4 ++ plugins/colorschemetools.cpp | 13 +++++ shared/global.cpp | 39 +++++++++++++- shared/global.h | 8 +++ ui/widgets/settings/pageappearance.cpp | 54 +++++++++++++------ ui/widgets/settings/pageappearance.h | 2 + ui/widgets/settings/pageappearance.ui | 8 +-- ui/widgets/settings/settings.cpp | 11 ++-- 13 files changed, 142 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4052647..7b1d75a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,15 @@ ## Squawk 0.2.1 (UNRELEASED) ### Bug fixes +- build in release mode now no longer spams warnings +- build now correctly installs all build plugin libs ### Improvements ### New features +- the settings are here! You con config different stuff from there +- now it's possible to set up different qt styles from settings +- if you have KConfig nad KConfigWidgets packages installed - you can chose from global color schemes ## Squawk 0.2.0 (Jan 10, 2022) ### Bug fixes diff --git a/README.md b/README.md index 0af201f..486d4fe 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ - qxmpp 1.1.0 or higher - KDE Frameworks: kwallet (optional) - KDE Frameworks: KIO (optional) +- KDE Frameworks: KConfig (optional) +- KDE Frameworks: KConfigWidgets (optional) - Boost (just one little hpp from there) - Imagemagick (for compilation, to rasterize an SVG logo) @@ -92,6 +94,7 @@ Here is the list of keys you can pass to configuration phase of `cmake ..`. - `SYSTEM_QXMPP` - `True` tries to link against `qxmpp` installed in the system, `False` builds bundled `qxmpp` library (default is `True`) - `WITH_KWALLET` - `True` builds the `KWallet` capability module if `KWallet` is installed and if not goes to `False`. `False` disables `KWallet` support (default is `True`) - `WITH_KIO` - `True` builds the `KIO` capability module if `KIO` is installed and if not goes to `False`. `False` disables `KIO` support (default is `True`) +- `WITH_KCONFIG` - `True` builds the `KConfig` and `KConfigWidgets` capability module if such packages are installed and if not goes to `False`. `False` disables `KConfig` and `KConfigWidgets` support (default is `True`) ## License diff --git a/core/main.cpp b/core/main.cpp index 03de307..cdfca7e 100644 --- a/core/main.cpp +++ b/core/main.cpp @@ -90,13 +90,24 @@ int main(int argc, char *argv[]) new Shared::Global(); //translates enums QSettings settings; - QVariant vtheme = settings.value("theme"); - if (vtheme.isValid()) { - QString theme = vtheme.toString().toLower(); - if (theme != "system") { - QApplication::setStyle(theme); + QVariant vs = settings.value("style"); + if (vs.isValid()) { + QString style = vs.toString().toLower(); + if (style != "system") { + Shared::Global::setStyle(style); } } + if (Shared::Global::supported("colorSchemeTools")) { + QVariant vt = settings.value("theme"); + if (vt.isValid()) { + QString theme = vt.toString(); + if (theme.toLower() != "system") { + Shared::Global::setTheme(theme); + } + } + } + + Squawk w; w.show(); diff --git a/core/passwordStorageEngines/wrappers/CMakeLists.txt b/core/passwordStorageEngines/wrappers/CMakeLists.txt index 6d486c0..e8420da 100644 --- a/core/passwordStorageEngines/wrappers/CMakeLists.txt +++ b/core/passwordStorageEngines/wrappers/CMakeLists.txt @@ -1,2 +1,4 @@ add_library(kwalletWrapper SHARED kwallet.cpp) target_link_libraries(kwalletWrapper PRIVATE KF5::Wallet) + +install(TARGETS kwalletWrapper LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/packaging/Archlinux/PKGBUILD b/packaging/Archlinux/PKGBUILD index 0d2aaaa..a8da388 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -8,7 +8,10 @@ url="https://git.macaw.me/blue/squawk" license=('GPL3') depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdb' 'qxmpp>=1.1.0') makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools' 'boost') -optdepends=('kwallet: secure password storage (requires rebuild)' 'kio: better show in folder action (requires rebuild)') +optdepends=('kwallet: secure password storage (requires rebuild)' + 'kconfig: system themes support (requires rebuild)' + 'kconfigwidgets: system themes support (requires rebuild)' + 'kio: better show in folder action (requires rebuild)') source=("$pkgname-$pkgver.tar.gz") sha256sums=('8e93d3dbe1fc35cfecb7783af409c6a264244d11609b2241d4fe77d43d068419') diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 5a5a41d..388c258 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,10 +1,14 @@ if (WITH_KIO) add_library(openFileManagerWindowJob SHARED openfilemanagerwindowjob.cpp) target_link_libraries(openFileManagerWindowJob PRIVATE KF5::KIOWidgets) + + install(TARGETS openFileManagerWindowJob LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif () if (WITH_KCONFIG) add_library(colorSchemeTools SHARED colorschemetools.cpp) target_link_libraries(colorSchemeTools PRIVATE KF5::ConfigCore) target_link_libraries(colorSchemeTools PRIVATE KF5::ConfigWidgets) + + install(TARGETS colorSchemeTools LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() diff --git a/plugins/colorschemetools.cpp b/plugins/colorschemetools.cpp index 0ad8e24..0288b28 100644 --- a/plugins/colorschemetools.cpp +++ b/plugins/colorschemetools.cpp @@ -1,7 +1,9 @@ #include #include +#include #include +#include #include QPixmap createPixmap(int size, const QBrush& window, const QBrush& button, const QBrush& view, const QBrush& selection); @@ -24,6 +26,17 @@ extern "C" void deletePreview(QIcon* icon) { delete icon; } +extern "C" void colorSchemeName(const QString& path, QString& answer) { + KSharedConfigPtr config = KSharedConfig::openConfig(path); + KConfigGroup group(config, QStringLiteral("General")); + answer = group.readEntry("Name", QFileInfo(path).baseName()); +} + +extern "C" void createPalette(const QString& path, QPalette& answer) { + KSharedConfigPtr config = KSharedConfig::openConfig(path); + answer = KColorScheme::createApplicationPalette(config); +} + QPixmap createPixmap(int size, const QBrush& window, const QBrush& button, const QBrush& view, const QBrush& selection) { QPixmap pix(size, size); pix.fill(Qt::black); diff --git a/shared/global.cpp b/shared/global.cpp index 7dafed6..122bc79 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -32,6 +32,8 @@ Shared::Global::HighlightInFileManager Shared::Global::hfm = 0; QLibrary Shared::Global::colorSchemeTools("colorSchemeTools"); Shared::Global::CreatePreview Shared::Global::createPreview = 0; Shared::Global::DeletePreview Shared::Global::deletePreview = 0; +Shared::Global::ColorSchemeName Shared::Global::colorSchemeName = 0; +Shared::Global::CreatePalette Shared::Global::createPalette = 0; #endif Shared::Global::Global(): @@ -91,6 +93,7 @@ Shared::Global::Global(): tr("Your password is going to be stored in KDE wallet storage (KWallet). You're going to be queried for permissions", "AccountPasswordDescription") }), defaultSystemStyle(QApplication::style()->objectName()), + defaultSystemPalette(QApplication::palette()), pluginSupport({ {"KWallet", false}, {"openFileManagerWindowJob", false}, @@ -124,7 +127,9 @@ Shared::Global::Global(): if (colorSchemeTools.isLoaded()) { createPreview = (CreatePreview) colorSchemeTools.resolve("createPreview"); deletePreview = (DeletePreview) colorSchemeTools.resolve("deletePreview"); - if (createPreview && deletePreview) { + colorSchemeName = (ColorSchemeName) colorSchemeTools.resolve("colorSchemeName"); + createPalette = (CreatePalette) colorSchemeTools.resolve("createPalette"); + if (createPreview && deletePreview && colorSchemeName && createPalette) { setSupported("colorSchemeTools", true); qDebug() << "Color Schemes support enabled"; } else { @@ -311,6 +316,38 @@ QIcon Shared::Global::createThemePreview(const QString& path) } } +QString Shared::Global::getColorSchemeName(const QString& path) +{ + if (supported("colorSchemeTools")) { + QString res; + colorSchemeName(path, res); + return res; + } else { + return ""; + } +} + +void Shared::Global::setTheme(const QString& path) +{ + if (supported("colorSchemeTools")) { + if (path.toLower() == "system") { + QApplication::setPalette(getInstance()->defaultSystemPalette); + } else { + QPalette pallete; + createPalette(path, pallete); + QApplication::setPalette(pallete); + } + } +} + +void Shared::Global::setStyle(const QString& style) +{ + if (style.toLower() == "system") { + QApplication::setStyle(getInstance()->defaultSystemStyle); + } else { + QApplication::setStyle(style); + } +} #define FROM_INT_INPL(Enum) \ template<> \ diff --git a/shared/global.h b/shared/global.h index bd3c9a6..2056639 100644 --- a/shared/global.h +++ b/shared/global.h @@ -87,6 +87,7 @@ namespace Shared { const std::deque accountPasswordDescription; const QString defaultSystemStyle; + const QPalette defaultSystemPalette; static bool supported(const QString& pluginName); static void setSupported(const QString& pluginName, bool support); @@ -96,6 +97,9 @@ namespace Shared { static FileInfo getFileInfo(const QString& path); static void highlightInFileManager(const QString& path); static QIcon createThemePreview(const QString& path); + static QString getColorSchemeName(const QString& path); + static void setTheme(const QString& path); + static void setStyle(const QString& style); template static T fromInt(int src); @@ -135,9 +139,13 @@ namespace Shared { typedef QIcon* (*CreatePreview)(const QString&); typedef void (*DeletePreview)(QIcon*); + typedef void (*ColorSchemeName)(const QString&, QString&); + typedef void (*CreatePalette)(const QString&, QPalette&); static CreatePreview createPreview; static DeletePreview deletePreview; + static ColorSchemeName colorSchemeName; + static CreatePalette createPalette; #endif }; } diff --git a/ui/widgets/settings/pageappearance.cpp b/ui/widgets/settings/pageappearance.cpp index 57da7aa..f2bf53e 100644 --- a/ui/widgets/settings/pageappearance.cpp +++ b/ui/widgets/settings/pageappearance.cpp @@ -10,41 +10,59 @@ static const QStringList filters = {"*.colors"}; PageAppearance::PageAppearance(QWidget* parent): QWidget(parent), m_ui(new Ui::PageAppearance()), - styles() + styles(), + themes() { m_ui->setupUi(this); - m_ui->themeInput->addItem(tr("System")); + m_ui->styleInput->addItem(tr("System")); styles.push_back("system"); - QStringList themes = QStyleFactory::keys(); - for (const QString& key : themes) { - m_ui->themeInput->addItem(key); + QStringList systemStyles = QStyleFactory::keys(); + for (const QString& key : systemStyles) { + m_ui->styleInput->addItem(key); styles.push_back(key); } QSettings settings; - QVariant vtheme = settings.value("theme"); - if (vtheme.isValid()) { - QString theme = vtheme.toString(); - m_ui->themeInput->setCurrentText(theme); + QVariant vs = settings.value("style"); + if (vs.isValid()) { + m_ui->styleInput->setCurrentText(vs.toString()); } else { - m_ui->themeInput->setCurrentText("System"); + m_ui->styleInput->setCurrentText(tr("System")); } - connect(m_ui->themeInput, qOverload(&QComboBox::currentIndexChanged), this, &PageAppearance::onThemeChanged); + connect(m_ui->styleInput, qOverload(&QComboBox::currentIndexChanged), this, &PageAppearance::onStyleChanged); if (Shared::Global::supported("colorSchemeTools")) { - m_ui->colorInput->addItem(tr("System")); + themes.push_back("system"); + m_ui->themeInput->addItem(tr("System")); const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, "color-schemes", QStandardPaths::LocateDirectory); QStringList schemeFiles; + QString currentTheme(tr("System")); + QString requestedTheme(""); + QVariant vtheme = settings.value("theme"); + if (vtheme.isValid()) { + requestedTheme = vtheme.toString(); + } for (const QString &dir : dirs) { const QStringList fileNames = QDir(dir).entryList(filters); for (const QString &file : fileNames) { - m_ui->colorInput->addItem(Shared::Global::createThemePreview(dir + QDir::separator() + file), file); + QString path = dir + QDir::separator() + file; + themes.push_back(path); + QString themeName = Shared::Global::getColorSchemeName(path); + m_ui->themeInput->addItem(Shared::Global::createThemePreview(path), themeName); + + if (path == requestedTheme) { + currentTheme = themeName; + } } } + + m_ui->themeInput->setCurrentText(currentTheme); + + connect(m_ui->themeInput, qOverload(&QComboBox::currentIndexChanged), this, &PageAppearance::onThemeChanged); } else { - m_ui->colorInput->setEnabled(false); + m_ui->themeInput->setEnabled(false); } } @@ -55,8 +73,14 @@ PageAppearance::~PageAppearance() void PageAppearance::onThemeChanged(int index) { if (index >= 0) { - emit variableModified("theme", styles[index]); + emit variableModified("theme", themes[index]); } } +void PageAppearance::onStyleChanged(int index) +{ + if (index >= 0) { + emit variableModified("style", styles[index]); + } +} diff --git a/ui/widgets/settings/pageappearance.h b/ui/widgets/settings/pageappearance.h index b31f3c1..80efd85 100644 --- a/ui/widgets/settings/pageappearance.h +++ b/ui/widgets/settings/pageappearance.h @@ -29,11 +29,13 @@ signals: void variableModified(const QString& key, const QVariant& value); protected slots: + void onStyleChanged(int index); void onThemeChanged(int index); private: QScopedPointer m_ui; std::vector styles; + std::vector themes; }; #endif // PAGEAPPEARANCE_H diff --git a/ui/widgets/settings/pageappearance.ui b/ui/widgets/settings/pageappearance.ui index afcb3a8..570eefa 100644 --- a/ui/widgets/settings/pageappearance.ui +++ b/ui/widgets/settings/pageappearance.ui @@ -12,20 +12,20 @@ - + Theme - + - + - + Color scheme diff --git a/ui/widgets/settings/settings.cpp b/ui/widgets/settings/settings.cpp index 75a7780..3ab38bb 100644 --- a/ui/widgets/settings/settings.cpp +++ b/ui/widgets/settings/settings.cpp @@ -40,13 +40,10 @@ void Settings::apply() { QSettings settings; for (const std::pair& pair: modifiedSettings) { - if (pair.first == "theme") { - QString theme = pair.second.toString(); - if (theme.toLower() == "system") { - QApplication::setStyle(Shared::Global::getInstance()->defaultSystemStyle); - } else { - QApplication::setStyle(theme); - } + if (pair.first == "style") { + Shared::Global::setStyle(pair.second.toString()); + } else if (pair.first == "theme") { + Shared::Global::setTheme(pair.second.toString()); } settings.setValue(pair.first, pair.second); From 243edff8bdf13fa04a00c35a05a3bb6a374cbfc3 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 17 Feb 2022 20:26:15 +0300 Subject: [PATCH 025/137] first thoughts about downloads path changing --- core/CMakeLists.txt | 1 + core/main.cpp | 11 ++++++- core/networkaccess.cpp | 13 ++++++-- core/networkaccess.h | 3 ++ core/utils/CMakeLists.txt | 4 +++ core/utils/pathcheck.cpp | 47 +++++++++++++++++++++++++++++ core/utils/pathcheck.h | 21 +++++++++++++ shared/utils.h | 2 -- ui/widgets/settings/pagegeneral.cpp | 3 ++ ui/widgets/settings/pagegeneral.h | 1 + ui/widgets/settings/pagegeneral.ui | 26 +++++++++++++--- 11 files changed, 122 insertions(+), 10 deletions(-) create mode 100644 core/utils/CMakeLists.txt create mode 100644 core/utils/pathcheck.cpp create mode 100644 core/utils/pathcheck.h diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 9369cb7..1836349 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -32,3 +32,4 @@ target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS}) add_subdirectory(handlers) add_subdirectory(passwordStorageEngines) +add_subdirectory(utils) diff --git a/core/main.cpp b/core/main.cpp index cdfca7e..570bad4 100644 --- a/core/main.cpp +++ b/core/main.cpp @@ -21,6 +21,8 @@ #include "../ui/squawk.h" #include "signalcatcher.h" #include "squawk.h" +#include "utils/pathcheck.h" + #include #include #include @@ -28,6 +30,7 @@ #include #include #include +#include int main(int argc, char *argv[]) { @@ -106,7 +109,13 @@ int main(int argc, char *argv[]) } } } - + QString path = Utils::downloadsPathCheck(); + if (path.size() > 0) { + settings.setValue("downloadsPath", path); + } else { + qDebug() << "couldn't initialize directory for downloads, quitting"; + return -1; + } Squawk w; diff --git a/core/networkaccess.cpp b/core/networkaccess.cpp index c2cd65d..48d26aa 100644 --- a/core/networkaccess.cpp +++ b/core/networkaccess.cpp @@ -28,8 +28,11 @@ Core::NetworkAccess::NetworkAccess(QObject* parent): manager(0), storage("fileURLStorage"), downloads(), - uploads() + uploads(), + currentPath() { + QSettings settings; + currentPath = settings.value("downloadsPath").toString(); } Core::NetworkAccess::~NetworkAccess() @@ -515,8 +518,7 @@ bool Core::NetworkAccess::checkAndAddToUploading(const QString& acc, const QStri QString Core::NetworkAccess::prepareDirectory(const QString& jid) { - QString path = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); - path += "/" + QApplication::applicationName(); + QString path = currentPath; if (jid.size() > 0) { path += "/" + jid; } @@ -563,3 +565,8 @@ std::list Core::NetworkAccess::reportPathInvalid(const QStr { return storage.deletedFile(path); } + +void Core::NetworkAccess::moveFilesDirectory(const QString& newPath) +{ + +} diff --git a/core/networkaccess.h b/core/networkaccess.h index 89d0633..cf24fc4 100644 --- a/core/networkaccess.h +++ b/core/networkaccess.h @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -65,6 +66,7 @@ public slots: void downladFile(const QString& url); void registerFile(const QString& url, const QString& account, const QString& jid, const QString& id); void registerFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id); + void moveFilesDirectory(const QString& newPath); private: void startDownload(const std::list& msgs, const QString& url); @@ -87,6 +89,7 @@ private: UrlStorage storage; std::map downloads; std::map uploads; + QString currentPath; struct Transfer { std::list messages; diff --git a/core/utils/CMakeLists.txt b/core/utils/CMakeLists.txt new file mode 100644 index 0000000..6722da7 --- /dev/null +++ b/core/utils/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(squawk PRIVATE + pathcheck.h + pathcheck.cpp +) diff --git a/core/utils/pathcheck.cpp b/core/utils/pathcheck.cpp new file mode 100644 index 0000000..3f1b86a --- /dev/null +++ b/core/utils/pathcheck.cpp @@ -0,0 +1,47 @@ +#include "pathcheck.h" + +QString Utils::downloadsPathCheck() +{ + QSettings settings; + QVariant dpv = settings.value("downloadsPath"); + QString path; + if (!dpv.isValid()) { + path = defaultDownloadsPath(); + qDebug() << "no downloadsPath variable in config, using default" << path; + path = getCanonicalWritablePath(path); + return path; + } else { + path = dpv.toString(); + path = getCanonicalWritablePath(path); + if (path.size() == 0) { + path = defaultDownloadsPath(); + qDebug() << "falling back to the default downloads path" << path; + path = getCanonicalWritablePath(path); + } + return path; + } +} + +QString Utils::defaultDownloadsPath() +{ + return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/" + QApplication::applicationName(); +} + +QString Utils::getCanonicalWritablePath(const QString& path) +{ + QDir location(path); + if (!location.exists()) { + bool res = location.mkpath(location.canonicalPath()); + if (!res) { + qDebug() << "couldn't create directory" << path; + return ""; + } + } + QFileInfo info(location.canonicalPath()); + if (info.isWritable()) { + return location.canonicalPath(); + } else { + qDebug() << "directory" << path << "is not writable"; + return ""; + } +} diff --git a/core/utils/pathcheck.h b/core/utils/pathcheck.h new file mode 100644 index 0000000..8618012 --- /dev/null +++ b/core/utils/pathcheck.h @@ -0,0 +1,21 @@ +#ifndef PATHCHECK_H +#define PATHCHECK_H + +#include +#include +#include +#include +#include +#include +#include + +namespace Utils { + +QString downloadsPathCheck(); +QString defaultDownloadsPath(); + +QString getCanonicalWritablePath(const QString& path); + +} + +#endif // PATHCHECK_H diff --git a/shared/utils.h b/shared/utils.h index 6dcb141..564e2e6 100644 --- a/shared/utils.h +++ b/shared/utils.h @@ -24,8 +24,6 @@ #include #include -// #include "KIO/OpenFileManagerWindowJob" - #include namespace Shared { diff --git a/ui/widgets/settings/pagegeneral.cpp b/ui/widgets/settings/pagegeneral.cpp index e448f80..56cb610 100644 --- a/ui/widgets/settings/pagegeneral.cpp +++ b/ui/widgets/settings/pagegeneral.cpp @@ -6,6 +6,9 @@ PageGeneral::PageGeneral(QWidget* parent): m_ui(new Ui::PageGeneral()) { m_ui->setupUi(this); + + QSettings settings; + m_ui->downloadsPathInput->setText(settings.value("downloadsPath").toString()); } PageGeneral::~PageGeneral() diff --git a/ui/widgets/settings/pagegeneral.h b/ui/widgets/settings/pagegeneral.h index b8c30c5..cb89bfa 100644 --- a/ui/widgets/settings/pagegeneral.h +++ b/ui/widgets/settings/pagegeneral.h @@ -4,6 +4,7 @@ #include #include #include +#include namespace Ui { diff --git a/ui/widgets/settings/pagegeneral.ui b/ui/widgets/settings/pagegeneral.ui index 9921715..e010980 100644 --- a/ui/widgets/settings/pagegeneral.ui +++ b/ui/widgets/settings/pagegeneral.ui @@ -11,15 +11,33 @@ - - + + Downloads path - - + + + + 6 + + + + + false + + + + + + + PushButton + + + + From d8b5ccb2dadb1cb64dd4c4927ea99ecda7e0deeb Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 19 Feb 2022 00:27:09 +0300 Subject: [PATCH 026/137] downloaded files now stored with squawk:// prefix, that way I can move downloads folder without messing up the database --- core/CMakeLists.txt | 1 - core/handlers/messagehandler.cpp | 13 ++++++- core/handlers/messagehandler.h | 2 +- core/main.cpp | 4 +- core/networkaccess.cpp | 45 ++++++++++++++-------- core/networkaccess.h | 1 + core/utils/CMakeLists.txt | 4 -- shared/CMakeLists.txt | 2 + {core/utils => shared}/pathcheck.cpp | 26 ++++++++----- {core/utils => shared}/pathcheck.h | 6 ++- ui/widgets/conversation.cpp | 2 +- ui/widgets/conversation.h | 1 + ui/widgets/messageline/messagedelegate.cpp | 11 +++--- ui/widgets/messageline/messagedelegate.h | 1 + 14 files changed, 75 insertions(+), 44 deletions(-) delete mode 100644 core/utils/CMakeLists.txt rename {core/utils => shared}/pathcheck.cpp (59%) rename {core/utils => shared}/pathcheck.h (66%) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 1836349..9369cb7 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -32,4 +32,3 @@ target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS}) add_subdirectory(handlers) add_subdirectory(passwordStorageEngines) -add_subdirectory(utils) diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index eb840f8..fc05a67 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -298,6 +298,14 @@ void Core::MessageHandler::performSending(Shared::Message data, bool newMessage) data.setTime(sendTime); } changes.insert("stamp", sendTime); + + //sometimes (when the image is pasted with ctrl+v) + //I start sending message with one path, then copy it to downloads directory + //so, the final path changes. Let's assume it changes always since it costs me close to nothing + QString attachPath = data.getAttachPath(); + if (attachPath.size() > 0) { + changes.insert("attachPath", attachPath); + } if (ri != 0) { if (newMessage) { @@ -439,14 +447,15 @@ void Core::MessageHandler::handleUploadError(const QString& jid, const QString& }); } -void Core::MessageHandler::onUploadFileComplete(const std::list& msgs, const QString& path) +void Core::MessageHandler::onUploadFileComplete(const std::list& msgs, const QString& url, const QString& path) { for (const Shared::MessageInfo& info : msgs) { if (info.account == acc->getName()) { RosterItem* ri = acc->rh->getRosterItem(info.jid); if (ri != 0) { Shared::Message msg = ri->getMessage(info.messageId); - sendMessageWithLocalUploadedFile(msg, path, false); + msg.setAttachPath(path); + sendMessageWithLocalUploadedFile(msg, url, false); } else { qDebug() << "A signal received about complete upload to" << acc->name << "for pal" << info.jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send"; } diff --git a/core/handlers/messagehandler.h b/core/handlers/messagehandler.h index 4eb9265..b88c46a 100644 --- a/core/handlers/messagehandler.h +++ b/core/handlers/messagehandler.h @@ -57,7 +57,7 @@ public slots: void onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot); void onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request); void onDownloadFileComplete(const std::list& msgs, const QString& path); - void onUploadFileComplete(const std::list& msgs, const QString& path); + void onUploadFileComplete(const std::list& msgs, const QString& url, const QString& path); void onLoadFileError(const std::list& msgs, const QString& path, bool up); void requestChangeMessage(const QString& jid, const QString& messageId, const QMap& data); diff --git a/core/main.cpp b/core/main.cpp index 570bad4..9a10062 100644 --- a/core/main.cpp +++ b/core/main.cpp @@ -18,10 +18,10 @@ #include "../shared/global.h" #include "../shared/messageinfo.h" +#include "../shared/pathcheck.h" #include "../ui/squawk.h" #include "signalcatcher.h" #include "squawk.h" -#include "utils/pathcheck.h" #include #include @@ -109,7 +109,7 @@ int main(int argc, char *argv[]) } } } - QString path = Utils::downloadsPathCheck(); + QString path = Shared::downloadsPathCheck(); if (path.size() > 0) { settings.setValue("downloadsPath", path); } else { diff --git a/core/networkaccess.cpp b/core/networkaccess.cpp index 48d26aa..25fafc8 100644 --- a/core/networkaccess.cpp +++ b/core/networkaccess.cpp @@ -305,7 +305,7 @@ void Core::NetworkAccess::onDownloadFinished() if (path.size() > 0) { path = checkFileName(fileName, path); - QFile file(path); + QFile file(Shared::resolvePath(path)); if (file.open(QIODevice::WriteOnly)) { file.write(dwn->reply->readAll()); file.close(); @@ -379,23 +379,20 @@ void Core::NetworkAccess::onUploadFinished() Transfer* upl = itr->second; if (upl->success) { qDebug() << "upload success for" << url; - - storage.addFile(upl->messages, upl->url, upl->path); - emit uploadFileComplete(upl->messages, upl->url, upl->path); // Copy file to Download folder if it is a temp file. See Conversation::onImagePasted. - if (upl->path.startsWith(QDir::tempPath() + QStringLiteral("/squawk_img_attach_")) && upl->path.endsWith(".png")) { + if (upl->path.startsWith(QDir::tempPath() + QDir::separator() + QStringLiteral("squawk_img_attach_")) && upl->path.endsWith(".png")) { QString err = ""; QString downloadDirPath = prepareDirectory(upl->messages.front().jid); if (downloadDirPath.size() > 0) { - QString newPath = downloadDirPath + "/" + upl->path.mid(QDir::tempPath().length() + 1); + QString newPath = downloadDirPath + QDir::separator() + upl->path.mid(QDir::tempPath().length() + 1); // Copy {TEMPDIR}/squawk_img_attach_XXXXXX.png to Download folder - bool copyResult = QFile::copy(upl->path, newPath); + bool copyResult = QFile::copy(upl->path, Shared::resolvePath(newPath)); if (copyResult) { // Change storage - storage.setPath(upl->url, newPath); + upl->path = newPath; } else { err = "copying to " + newPath + " failed"; } @@ -407,6 +404,9 @@ void Core::NetworkAccess::onUploadFinished() qDebug() << "failed to copy temporary upload file " << upl->path << " to download folder:" << err; } } + + storage.addFile(upl->messages, upl->url, upl->path); + emit uploadFileComplete(upl->messages, upl->url, upl->path); } upl->reply->deleteLater(); @@ -519,9 +519,12 @@ bool Core::NetworkAccess::checkAndAddToUploading(const QString& acc, const QStri QString Core::NetworkAccess::prepareDirectory(const QString& jid) { QString path = currentPath; + QString addition; if (jid.size() > 0) { - path += "/" + jid; + addition = jid; + path += QDir::separator() + jid; } + QDir location(path); if (!location.exists()) { @@ -529,10 +532,10 @@ QString Core::NetworkAccess::prepareDirectory(const QString& jid) if (!res) { return ""; } else { - return path; + return "squawk://" + addition; } } - return path; + return "squawk://" + addition; } QString Core::NetworkAccess::checkFileName(const QString& name, const QString& path) @@ -546,14 +549,17 @@ QString Core::NetworkAccess::checkFileName(const QString& name, const QString& p suffix += "." + (*sItr); } QString postfix(""); - QFileInfo proposedName(path + "/" + realName + suffix); + QString resolvedPath = Shared::resolvePath(path); + QString count(""); + QFileInfo proposedName(resolvedPath + QDir::separator() + realName + count + suffix); + int counter = 0; while (proposedName.exists()) { - QString count = QString("(") + std::to_string(++counter).c_str() + ")"; - proposedName = QFileInfo(path + "/" + realName + count + suffix); + count = QString("(") + std::to_string(++counter).c_str() + ")"; + proposedName = QFileInfo(resolvedPath + QDir::separator() + realName + count + suffix); } - - return proposedName.absoluteFilePath(); + + return path + QDir::separator() + realName + count + suffix; } QString Core::NetworkAccess::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id) @@ -568,5 +574,10 @@ std::list Core::NetworkAccess::reportPathInvalid(const QStr void Core::NetworkAccess::moveFilesDirectory(const QString& newPath) { - + QDir dir; + if (dir.rename(currentPath, newPath)) { + currentPath = newPath; + } else { + qDebug() << "couldn't move downloads directory, most probably downloads will be broken"; + } } diff --git a/core/networkaccess.h b/core/networkaccess.h index cf24fc4..0b7bb7d 100644 --- a/core/networkaccess.h +++ b/core/networkaccess.h @@ -31,6 +31,7 @@ #include #include "urlstorage.h" +#include "shared/pathcheck.h" namespace Core { diff --git a/core/utils/CMakeLists.txt b/core/utils/CMakeLists.txt deleted file mode 100644 index 6722da7..0000000 --- a/core/utils/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -target_sources(squawk PRIVATE - pathcheck.h - pathcheck.cpp -) diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index a36b516..0ab7dbd 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -16,4 +16,6 @@ target_sources(squawk PRIVATE utils.h vcard.cpp vcard.h + pathcheck.cpp + pathcheck.h ) diff --git a/core/utils/pathcheck.cpp b/shared/pathcheck.cpp similarity index 59% rename from core/utils/pathcheck.cpp rename to shared/pathcheck.cpp index 3f1b86a..0850742 100644 --- a/core/utils/pathcheck.cpp +++ b/shared/pathcheck.cpp @@ -1,6 +1,7 @@ #include "pathcheck.h" -QString Utils::downloadsPathCheck() +QRegularExpression squawk("^squawk:\\/\\/"); +QString Shared::downloadsPathCheck() { QSettings settings; QVariant dpv = settings.value("downloadsPath"); @@ -8,40 +9,47 @@ QString Utils::downloadsPathCheck() if (!dpv.isValid()) { path = defaultDownloadsPath(); qDebug() << "no downloadsPath variable in config, using default" << path; - path = getCanonicalWritablePath(path); + path = getAbsoluteWritablePath(path); return path; } else { path = dpv.toString(); - path = getCanonicalWritablePath(path); + path = getAbsoluteWritablePath(path); if (path.size() == 0) { path = defaultDownloadsPath(); qDebug() << "falling back to the default downloads path" << path; - path = getCanonicalWritablePath(path); + path = getAbsoluteWritablePath(path); } return path; } } -QString Utils::defaultDownloadsPath() +QString Shared::defaultDownloadsPath() { return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + "/" + QApplication::applicationName(); } -QString Utils::getCanonicalWritablePath(const QString& path) +QString Shared::getAbsoluteWritablePath(const QString& path) { QDir location(path); if (!location.exists()) { - bool res = location.mkpath(location.canonicalPath()); + bool res = location.mkpath(location.absolutePath()); if (!res) { qDebug() << "couldn't create directory" << path; return ""; } } - QFileInfo info(location.canonicalPath()); + QFileInfo info(location.absolutePath()); if (info.isWritable()) { - return location.canonicalPath(); + return location.absolutePath(); } else { qDebug() << "directory" << path << "is not writable"; return ""; } } + +QString Shared::resolvePath(QString path) +{ + QSettings settings; + QVariant dpv = settings.value("downloadsPath"); + return path.replace(squawk, dpv.toString() + "/"); +} diff --git a/core/utils/pathcheck.h b/shared/pathcheck.h similarity index 66% rename from core/utils/pathcheck.h rename to shared/pathcheck.h index 8618012..6e7cd39 100644 --- a/core/utils/pathcheck.h +++ b/shared/pathcheck.h @@ -8,13 +8,15 @@ #include #include #include +#include -namespace Utils { +namespace Shared { QString downloadsPathCheck(); QString defaultDownloadsPath(); -QString getCanonicalWritablePath(const QString& path); +QString getAbsoluteWritablePath(const QString& path); +QString resolvePath(QString path); } diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 69eac19..1c82024 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -483,7 +483,7 @@ void Conversation::onFeedContext(const QPoint& pos) }); } - QString path = item->getAttachPath(); + QString path = Shared::resolvePath(item->getAttachPath()); if (path.size() > 0) { showMenu = true; QAction* open = contextMenu->addAction(Shared::icon("document-preview"), tr("Open")); diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index a758b2c..76a8dd5 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -33,6 +33,7 @@ #include "shared/order.h" #include "shared/icons.h" #include "shared/utils.h" +#include "shared/pathcheck.h" #include "ui/models/account.h" #include "ui/models/roster.h" diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index d692752..22e8dcb 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -314,7 +314,7 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel case Models::none: break; case Models::uploading: - messageSize.rheight() += Preview::calculateAttachSize(data.attach.localPath, messageRect).height() + textMargin; + messageSize.rheight() += Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), messageRect).height() + textMargin; [[fallthrough]]; case Models::downloading: messageSize.rheight() += barHeight + textMargin; @@ -326,7 +326,7 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel break; case Models::ready: case Models::local: { - QSize aSize = Preview::calculateAttachSize(data.attach.localPath, messageRect); + QSize aSize = Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), messageRect); messageSize.rheight() += aSize.height() + textMargin; messageSize.setWidth(std::max(messageSize.width(), aSize.width())); } @@ -338,7 +338,7 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel } break; case Models::errorUpload: { - QSize aSize = Preview::calculateAttachSize(data.attach.localPath, messageRect); + QSize aSize = Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), messageRect); QSize commentSize = dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.attach.error).size(); messageSize.rheight() += aSize.height() + commentSize.height() + textMargin * 2; messageSize.setWidth(std::max(messageSize.width(), std::max(commentSize.width(), aSize.width()))); @@ -455,12 +455,13 @@ int MessageDelegate::paintPreview(const Models::FeedItem& data, QPainter* painte std::map::iterator itr = previews->find(data.id); QSize size = option.rect.size(); + QString path = Shared::resolvePath(data.attach.localPath); if (itr != previews->end()) { preview = itr->second; - preview->actualize(data.attach.localPath, size, option.rect.topLeft()); + preview->actualize(path, size, option.rect.topLeft()); } else { QWidget* vp = static_cast(painter->device()); - preview = new Preview(data.attach.localPath, size, option.rect.topLeft(), vp); + preview = new Preview(path, size, option.rect.topLeft(), vp); previews->insert(std::make_pair(data.id, preview)); } diff --git a/ui/widgets/messageline/messagedelegate.h b/ui/widgets/messageline/messagedelegate.h index b58a1bb..9225412 100644 --- a/ui/widgets/messageline/messagedelegate.h +++ b/ui/widgets/messageline/messagedelegate.h @@ -33,6 +33,7 @@ #include "shared/icons.h" #include "shared/global.h" #include "shared/utils.h" +#include "shared/pathcheck.h" #include "preview.h" From 73b1b58a966469ec5b28ebc9899fcebf1df567b3 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 19 Feb 2022 21:31:49 +0300 Subject: [PATCH 027/137] Downloads folder now is movable --- core/handlers/messagehandler.cpp | 6 +++- core/handlers/messagehandler.h | 1 + core/main.cpp | 1 + core/networkaccess.cpp | 18 +++++++---- core/squawk.cpp | 6 ++++ core/squawk.h | 1 + shared/pathcheck.cpp | 23 +++++++++++++++ shared/pathcheck.h | 3 ++ ui/squawk.cpp | 1 + ui/squawk.h | 1 + ui/widgets/settings/pagegeneral.cpp | 46 ++++++++++++++++++++++++++++- ui/widgets/settings/pagegeneral.h | 7 +++++ ui/widgets/settings/pagegeneral.ui | 2 +- ui/widgets/settings/settings.cpp | 12 +++++++- ui/widgets/settings/settings.h | 5 ++++ ui/widgets/settings/settings.ui | 20 +++++++++++-- 16 files changed, 141 insertions(+), 12 deletions(-) diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index fc05a67..1e89dd6 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -304,7 +304,11 @@ void Core::MessageHandler::performSending(Shared::Message data, bool newMessage) //so, the final path changes. Let's assume it changes always since it costs me close to nothing QString attachPath = data.getAttachPath(); if (attachPath.size() > 0) { - changes.insert("attachPath", attachPath); + QString squawkified = Shared::squawkifyPath(attachPath); + changes.insert("attachPath", squawkified); + if (attachPath != squawkified) { + data.setAttachPath(squawkified); + } } if (ri != 0) { diff --git a/core/handlers/messagehandler.h b/core/handlers/messagehandler.h index b88c46a..4f03484 100644 --- a/core/handlers/messagehandler.h +++ b/core/handlers/messagehandler.h @@ -29,6 +29,7 @@ #include #include +#include namespace Core { diff --git a/core/main.cpp b/core/main.cpp index 9a10062..79ca648 100644 --- a/core/main.cpp +++ b/core/main.cpp @@ -160,6 +160,7 @@ int main(int argc, char *argv[]) QObject::connect(&w, &Squawk::uploadVCard, squawk, &Core::Squawk::uploadVCard); QObject::connect(&w, &Squawk::responsePassword, squawk, &Core::Squawk::responsePassword); QObject::connect(&w, &Squawk::localPathInvalid, squawk, &Core::Squawk::onLocalPathInvalid); + QObject::connect(&w, &Squawk::changeDownloadsPath, squawk, &Core::Squawk::changeDownloadsPath); QObject::connect(squawk, &Core::Squawk::newAccount, &w, &Squawk::newAccount); QObject::connect(squawk, &Core::Squawk::addContact, &w, &Squawk::addContact); diff --git a/core/networkaccess.cpp b/core/networkaccess.cpp index 25fafc8..7c55e19 100644 --- a/core/networkaccess.cpp +++ b/core/networkaccess.cpp @@ -438,10 +438,10 @@ void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTot QString Core::NetworkAccess::getFileRemoteUrl(const QString& path) { - QString p; + QString p = Shared::squawkifyPath(path); try { - p = storage.getUrl(path); + p = storage.getUrl(p); } catch (const Archive::NotFound& err) { } catch (...) { @@ -574,10 +574,16 @@ std::list Core::NetworkAccess::reportPathInvalid(const QStr void Core::NetworkAccess::moveFilesDirectory(const QString& newPath) { - QDir dir; - if (dir.rename(currentPath, newPath)) { - currentPath = newPath; - } else { + QDir dir(currentPath); + bool success = true; + qDebug() << "moving" << currentPath << "to" << newPath; + for (QFileInfo fileInfo : dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System)) { + QString fileName = fileInfo.fileName(); + success = dir.rename(fileName, newPath + QDir::separator() + fileName) && success; + } + + if (!success) { qDebug() << "couldn't move downloads directory, most probably downloads will be broken"; } + currentPath = newPath; } diff --git a/core/squawk.cpp b/core/squawk.cpp index 69d2eef..9f2b445 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -785,3 +785,9 @@ void Core::Squawk::onLocalPathInvalid(const QString& path) } } } + +void Core::Squawk::changeDownloadsPath(const QString& path) +{ + network.moveFilesDirectory(path); +} + diff --git a/core/squawk.h b/core/squawk.h index 5db9fa8..738a957 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -123,6 +123,7 @@ public slots: void uploadVCard(const QString& account, const Shared::VCard& card); void responsePassword(const QString& account, const QString& password); void onLocalPathInvalid(const QString& path); + void changeDownloadsPath(const QString& path); private: typedef std::deque Accounts; diff --git a/shared/pathcheck.cpp b/shared/pathcheck.cpp index 0850742..1929387 100644 --- a/shared/pathcheck.cpp +++ b/shared/pathcheck.cpp @@ -53,3 +53,26 @@ QString Shared::resolvePath(QString path) QVariant dpv = settings.value("downloadsPath"); return path.replace(squawk, dpv.toString() + "/"); } + +QString Shared::squawkifyPath(QString path) +{ + QSettings settings; + QString current = settings.value("downloadsPath").toString(); + + if (path.startsWith(current)) { + path.replace(0, current.size() + 1, "squawk://"); + } + + return path; +} + +bool Shared::isSubdirectoryOfSettings(const QString& path) +{ + + QSettings settings; + QDir oldPath(settings.value("downloadsPath").toString()); + QDir newPath(path); + + return newPath.canonicalPath().startsWith(oldPath.canonicalPath()); +} + diff --git a/shared/pathcheck.h b/shared/pathcheck.h index 6e7cd39..62dcaeb 100644 --- a/shared/pathcheck.h +++ b/shared/pathcheck.h @@ -13,10 +13,13 @@ namespace Shared { QString downloadsPathCheck(); +QString downloadsPathCheck(QString path); QString defaultDownloadsPath(); QString getAbsoluteWritablePath(const QString& path); QString resolvePath(QString path); +QString squawkifyPath(QString path); +bool isSubdirectoryOfSettings(const QString& path); } diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 7acda3d..e24640a 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -125,6 +125,7 @@ void Squawk::onPreferences() preferences = new Settings(); preferences->setAttribute(Qt::WA_DeleteOnClose); connect(preferences, &Settings::destroyed, this, &Squawk::onPreferencesClosed); + connect(preferences, &Settings::changeDownloadsPath, this, &Squawk::changeDownloadsPath); preferences->show(); } else { diff --git a/ui/squawk.h b/ui/squawk.h index b419057..7551f66 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -80,6 +80,7 @@ signals: void uploadVCard(const QString& account, const Shared::VCard& card); void responsePassword(const QString& account, const QString& password); void localPathInvalid(const QString& path); + void changeDownloadsPath(const QString& path); public slots: void writeSettings(); diff --git a/ui/widgets/settings/pagegeneral.cpp b/ui/widgets/settings/pagegeneral.cpp index 56cb610..a546bd0 100644 --- a/ui/widgets/settings/pagegeneral.cpp +++ b/ui/widgets/settings/pagegeneral.cpp @@ -3,14 +3,58 @@ PageGeneral::PageGeneral(QWidget* parent): QWidget(parent), - m_ui(new Ui::PageGeneral()) + m_ui(new Ui::PageGeneral()), + dialog(nullptr) { m_ui->setupUi(this); QSettings settings; m_ui->downloadsPathInput->setText(settings.value("downloadsPath").toString()); + connect(m_ui->downloadsPathButton, &QPushButton::clicked, this, &PageGeneral::onBrowseButtonClicked); } PageGeneral::~PageGeneral() { + if (dialog != nullptr) { + dialog->deleteLater(); + } +} + +void PageGeneral::onBrowseButtonClicked() +{ + if (dialog == nullptr) { + QSettings settings; + dialog = new QFileDialog(this, tr("Select where downloads folder is going to be"), settings.value("downloadsPath").toString()); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->setAcceptMode(QFileDialog::AcceptSave); //I find it the most convinient way + dialog->setFileMode(QFileDialog::AnyFile); //Otherwise the directory is supposed to be + dialog->setOption(QFileDialog::ShowDirsOnly, true); //selected and not to be navigated + dialog->setOption(QFileDialog::DontConfirmOverwrite, true); + dialog->setModal(true); + connect(dialog, &QFileDialog::accepted, this, &PageGeneral::onDialogAccepted); + connect(dialog, &QFileDialog::destroyed, this, &PageGeneral::onDialogDestroyed); + dialog->show(); + } else { + dialog->show(); + dialog->raise(); + dialog->activateWindow(); + } +} + +void PageGeneral::onDialogAccepted() +{ + QStringList files = dialog->selectedFiles(); + QString path; + if (files.size() > 0) { + path = files[0]; + } else { + path = dialog->directory().canonicalPath(); + } + m_ui->downloadsPathInput->setText(path); + emit variableModified("downloadsPath", path); +} + +void PageGeneral::onDialogDestroyed() +{ + dialog = nullptr; } diff --git a/ui/widgets/settings/pagegeneral.h b/ui/widgets/settings/pagegeneral.h index cb89bfa..ec00bba 100644 --- a/ui/widgets/settings/pagegeneral.h +++ b/ui/widgets/settings/pagegeneral.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace Ui { @@ -24,8 +25,14 @@ public: signals: void variableModified(const QString& key, const QVariant& value); +private slots: + void onBrowseButtonClicked(); + void onDialogAccepted(); + void onDialogDestroyed(); + private: QScopedPointer m_ui; + QFileDialog* dialog; }; #endif // PAGEGENERAL_H diff --git a/ui/widgets/settings/pagegeneral.ui b/ui/widgets/settings/pagegeneral.ui index e010980..e412668 100644 --- a/ui/widgets/settings/pagegeneral.ui +++ b/ui/widgets/settings/pagegeneral.ui @@ -33,7 +33,7 @@ - PushButton + Browse diff --git a/ui/widgets/settings/settings.cpp b/ui/widgets/settings/settings.cpp index 3ab38bb..27401bb 100644 --- a/ui/widgets/settings/settings.cpp +++ b/ui/widgets/settings/settings.cpp @@ -42,11 +42,21 @@ void Settings::apply() for (const std::pair& pair: modifiedSettings) { if (pair.first == "style") { Shared::Global::setStyle(pair.second.toString()); + settings.setValue(pair.first, pair.second); } else if (pair.first == "theme") { Shared::Global::setTheme(pair.second.toString()); + settings.setValue(pair.first, pair.second); + } else if (pair.first == "downloadsPath") { + QString path = pair.second.toString(); + if (!Shared::isSubdirectoryOfSettings(path)) { + path = Shared::getAbsoluteWritablePath(path); + if (path.size() > 0) { + settings.setValue(pair.first, path); + emit changeDownloadsPath(path); + } + } } - settings.setValue(pair.first, pair.second); } modifiedSettings.clear(); diff --git a/ui/widgets/settings/settings.h b/ui/widgets/settings/settings.h index 5031139..5a6b37c 100644 --- a/ui/widgets/settings/settings.h +++ b/ui/widgets/settings/settings.h @@ -5,8 +5,10 @@ #include #include #include +#include #include "shared/global.h" +#include "shared/pathcheck.h" namespace Ui { @@ -23,6 +25,9 @@ public: Settings(QWidget* parent = nullptr); ~Settings(); +signals: + void changeDownloadsPath(const QString& path); + public slots: void apply(); void confirm(); diff --git a/ui/widgets/settings/settings.ui b/ui/widgets/settings/settings.ui index fe092dc..d97a3f2 100644 --- a/ui/widgets/settings/settings.ui +++ b/ui/widgets/settings/settings.ui @@ -10,6 +10,9 @@ 363 + + Preferences + 0 @@ -183,10 +186,23 @@ 0 - - + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + From 0823b35148d690d60c12ec5a19821cfe1f9fbe1b Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 20 Feb 2022 22:10:09 +0300 Subject: [PATCH 028/137] removed unused old message line files, first thoughts on message edition --- CHANGELOG.md | 2 + ui/utils/badge.cpp | 81 +++- ui/utils/badge.h | 10 + ui/widgets/conversation.cpp | 8 +- ui/widgets/conversation.h | 6 + ui/widgets/conversation.ui | 34 +- ui/widgets/messageline/CMakeLists.txt | 4 - ui/widgets/messageline/message.cpp | 344 ----------------- ui/widgets/messageline/message.h | 103 ----- ui/widgets/messageline/messageline.cpp | 504 ------------------------- ui/widgets/messageline/messageline.h | 108 ------ 11 files changed, 121 insertions(+), 1083 deletions(-) delete mode 100644 ui/widgets/messageline/message.cpp delete mode 100644 ui/widgets/messageline/message.h delete mode 100644 ui/widgets/messageline/messageline.cpp delete mode 100644 ui/widgets/messageline/messageline.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b1d75a..8a6fdfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,13 @@ - build now correctly installs all build plugin libs ### Improvements +- reduced amount of places where platform specific path separator is used ### New features - the settings are here! You con config different stuff from there - now it's possible to set up different qt styles from settings - if you have KConfig nad KConfigWidgets packages installed - you can chose from global color schemes +- it's possible now to chose a folder where squawk is going to store downloaded files ## Squawk 0.2.0 (Jan 10, 2022) ### Bug fixes diff --git a/ui/utils/badge.cpp b/ui/utils/badge.cpp index 7575afc..b3b321a 100644 --- a/ui/utils/badge.cpp +++ b/ui/utils/badge.cpp @@ -26,32 +26,62 @@ Badge::Badge(const QString& p_id, const QString& p_text, const QIcon& icon, QWid closeButton(new QPushButton()), layout(new QHBoxLayout(this)) { - setBackgroundRole(QPalette::Base); - //setAutoFillBackground(true); - setFrameStyle(QFrame::StyledPanel); - setFrameShadow(QFrame::Raised); + createMandatoryComponents(); image->setPixmap(icon.pixmap(25, 25)); - QIcon tabCloseIcon = QIcon::fromTheme("tab-close"); - if (tabCloseIcon.isNull()) { - tabCloseIcon.addFile(QString::fromUtf8(":/images/fallback/dark/big/edit-none.svg"), QSize(), QIcon::Normal, QIcon::Off); - } - closeButton->setIcon(tabCloseIcon); - closeButton->setMaximumHeight(25); - closeButton->setMaximumWidth(25); - layout->addWidget(image); layout->addWidget(text); layout->addWidget(closeButton); - - layout->setContentsMargins(2, 2, 2, 2); - - connect(closeButton, &QPushButton::clicked, this, &Badge::close); +} + +Badge::Badge(QWidget* parent): + QFrame(parent), + id(Shared::generateUUID()), + image(nullptr), + text(nullptr), + closeButton(new QPushButton()), + layout(new QHBoxLayout(this)) +{ + createMandatoryComponents(); + + layout->addWidget(closeButton); +} + +void Badge::setIcon(const QIcon& icon) +{ + if (image == nullptr) { + image = new QLabel(); + image->setPixmap(icon.pixmap(25, 25)); + layout->insertWidget(0, image); + } else { + image->setPixmap(icon.pixmap(25, 25)); + } +} + +void Badge::setText(const QString& p_text) +{ + if (text == nullptr) { + text = new QLabel(p_text); + int index = 0; + if (image != nullptr) { + index = 1; + } + layout->insertWidget(index, text); + } else { + text->setText(p_text); + } } Badge::~Badge() { + if (image != nullptr) { + delete image; + } + if (text != nullptr) { + delete text; + } + delete closeButton; } bool Badge::Comparator::operator()(const Badge* a, const Badge* b) const @@ -63,3 +93,22 @@ bool Badge::Comparator::operator()(const Badge& a, const Badge& b) const { return a.id < b.id; } + +void Badge::createMandatoryComponents() +{ + setBackgroundRole(QPalette::Base); + //setAutoFillBackground(true); + setFrameStyle(QFrame::StyledPanel); + setFrameShadow(QFrame::Raised); + + QIcon tabCloseIcon = QIcon::fromTheme("tab-close"); + if (tabCloseIcon.isNull()) { + tabCloseIcon.addFile(QString::fromUtf8(":/images/fallback/dark/big/edit-none.svg"), QSize(), QIcon::Normal, QIcon::Off); + } + closeButton->setIcon(tabCloseIcon); + + closeButton->setMaximumHeight(25); + closeButton->setMaximumWidth(25); + layout->setContentsMargins(2, 2, 2, 2); + connect(closeButton, &QPushButton::clicked, this, &Badge::close); +} diff --git a/ui/utils/badge.h b/ui/utils/badge.h index 93a7f00..52f4747 100644 --- a/ui/utils/badge.h +++ b/ui/utils/badge.h @@ -25,6 +25,8 @@ #include #include +#include "shared/utils.h" + /** * @todo write docs */ @@ -33,9 +35,14 @@ class Badge : public QFrame Q_OBJECT public: Badge(const QString& id, const QString& text, const QIcon& icon, QWidget* parent = nullptr); + Badge(QWidget* parent = nullptr); ~Badge(); const QString id; + +public: + void setText(const QString& text); + void setIcon(const QIcon& icon); signals: void close(); @@ -45,6 +52,9 @@ private: QLabel* text; QPushButton* closeButton; QHBoxLayout* layout; + +private: + void createMandatoryComponents(); public: struct Comparator { diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 1c82024..07c599e 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -57,7 +57,8 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, tsb(QApplication::style()->styleHint(QStyle::SH_ScrollBar_Transient) == 1), pasteImageAction(new QAction(tr("Paste Image"), this)), shadow(10, 1, Qt::black, this), - contextMenu(new QMenu()) + contextMenu(new QMenu()), + currentAction(CurrentAction::none) { m_ui->setupUi(this); @@ -108,6 +109,9 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, //line->setMyName(acc->getName()); initializeOverlay(); + + m_ui->currentActionBadge->setVisible(false);; +// m_ui->currentActionBadge->setText(tr("Editing message...")); } Conversation::~Conversation() @@ -237,7 +241,7 @@ void Conversation::onEnterPressed() element->feed->registerUpload(msg.getId()); emit sendMessage(msg); } - clearAttachedFiles(); + clearAttachedFiles(); } } diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index 76a8dd5..c43d7a0 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -121,6 +121,11 @@ public: const bool isMuc; protected: + enum class CurrentAction { + none, + edit + }; + Models::Account* account; Models::Element* element; QString palJid; @@ -142,6 +147,7 @@ protected: ShadowOverlay shadow; QMenu* contextMenu; + CurrentAction currentAction; private: static bool painterInitialized; diff --git a/ui/widgets/conversation.ui b/ui/widgets/conversation.ui index 483375a..ce9ad66 100644 --- a/ui/widgets/conversation.ui +++ b/ui/widgets/conversation.ui @@ -279,6 +279,29 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + @@ -400,8 +423,8 @@ background-color: transparent <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Liberation Sans'; font-size:10pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> +</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Liberation Sans'; font-size:10pt;"><br /></p></body></html>
false @@ -419,6 +442,13 @@ p, li { white-space: pre-wrap; } + + + Badge + QFrame +
ui/utils/badge.h
+
+
diff --git a/ui/widgets/messageline/CMakeLists.txt b/ui/widgets/messageline/CMakeLists.txt index 7cace9d..7a850da 100644 --- a/ui/widgets/messageline/CMakeLists.txt +++ b/ui/widgets/messageline/CMakeLists.txt @@ -1,14 +1,10 @@ target_sources(squawk PRIVATE messagedelegate.cpp messagedelegate.h - #messageline.cpp - #messageline.h preview.cpp preview.h messagefeed.cpp messagefeed.h feedview.cpp feedview.h - #message.cpp - #message.h ) diff --git a/ui/widgets/messageline/message.cpp b/ui/widgets/messageline/message.cpp deleted file mode 100644 index 7a004bb..0000000 --- a/ui/widgets/messageline/message.cpp +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * 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 "message.h" -#include -#include -#include -#include -#include - -Message::Message(const Shared::Message& source, bool p_outgoing, const QString& p_sender, const QString& avatarPath, QWidget* parent): - QWidget(parent), - outgoing(p_outgoing), - msg(source), - body(new QWidget()), - statusBar(new QWidget()), - bodyLayout(new QVBoxLayout(body)), - layout(new QHBoxLayout(this)), - date(new QLabel(msg.getTime().toLocalTime().toString())), - sender(new QLabel(p_sender)), - text(new QLabel()), - shadow(new QGraphicsDropShadowEffect()), - button(0), - file(0), - progress(0), - fileComment(new QLabel()), - statusIcon(0), - editedLabel(0), - avatar(new Image(avatarPath.size() == 0 ? Shared::iconPath("user", true) : avatarPath, 60)), - hasButton(false), - hasProgress(false), - hasFile(false), - commentAdded(false), - hasStatusIcon(false), - hasEditedLabel(false) -{ - setContentsMargins(0, 0, 0, 0); - layout->setContentsMargins(10, 5, 10, 5); - body->setBackgroundRole(QPalette::AlternateBase); - body->setAutoFillBackground(true); - - QString bd = Shared::processMessageBody(msg.getBody()); - text->setTextFormat(Qt::RichText); - text->setText(bd);; - text->setTextInteractionFlags(text->textInteractionFlags() | Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse); - text->setWordWrap(true); - text->setOpenExternalLinks(true); - if (bd.size() == 0) { - text->hide(); - } - - QFont dFont = date->font(); - dFont.setItalic(true); - dFont.setPointSize(dFont.pointSize() - 2); - date->setFont(dFont); - - QFont f; - f.setBold(true); - sender->setFont(f); - - bodyLayout->addWidget(sender); - bodyLayout->addWidget(text); - - shadow->setBlurRadius(10); - shadow->setXOffset(1); - shadow->setYOffset(1); - shadow->setColor(Qt::black); - body->setGraphicsEffect(shadow); - avatar->setMaximumHeight(60); - avatar->setMaximumWidth(60); - - statusBar->setContentsMargins(0, 0, 0, 0); - QHBoxLayout* statusLay = new QHBoxLayout(); - statusLay->setContentsMargins(0, 0, 0, 0); - statusBar->setLayout(statusLay); - - if (outgoing) { - sender->setAlignment(Qt::AlignRight); - date->setAlignment(Qt::AlignRight); - statusIcon = new QLabel(); - setState(); - statusLay->addWidget(statusIcon); - statusLay->addWidget(date); - layout->addStretch(); - layout->addWidget(body); - layout->addWidget(avatar); - hasStatusIcon = true; - } else { - layout->addWidget(avatar); - layout->addWidget(body); - layout->addStretch(); - statusLay->addWidget(date); - } - if (msg.getEdited()) { - setEdited(); - } - - bodyLayout->addWidget(statusBar); - layout->setAlignment(avatar, Qt::AlignTop); -} - -Message::~Message() -{ - if (!commentAdded) { - delete fileComment; - } - //delete body; //not sure if I should delete it here, it's probably already owned by the infrastructure and gonna die with the rest of the widget - //delete avatar; -} - -QString Message::getId() const -{ - return msg.getId(); -} - -QString Message::getSenderJid() const -{ - return msg.getFromJid(); -} - -QString Message::getSenderResource() const -{ - return msg.getFromResource(); -} - -QString Message::getFileUrl() const -{ - return msg.getOutOfBandUrl(); -} - -void Message::setSender(const QString& p_sender) -{ - sender->setText(p_sender); -} - -void Message::addButton(const QIcon& icon, const QString& buttonText, const QString& tooltip) -{ - hideFile(); - hideProgress(); - if (!hasButton) { - hideComment(); - if (msg.getBody() == msg.getOutOfBandUrl()) { - text->setText(""); - text->hide(); - } - button = new QPushButton(icon, buttonText); - button->setToolTip(tooltip); - connect(button, &QPushButton::clicked, this, &Message::buttonClicked); - bodyLayout->insertWidget(2, button); - hasButton = true; - } -} - -void Message::setProgress(qreal value) -{ - hideFile(); - hideButton(); - if (!hasProgress) { - hideComment(); - if (msg.getBody() == msg.getOutOfBandUrl()) { - text->setText(""); - text->hide(); - } - progress = new QProgressBar(); - progress->setRange(0, 100); - bodyLayout->insertWidget(2, progress); - hasProgress = true; - } - progress->setValue(value * 100); -} - -void Message::showFile(const QString& path) -{ - hideButton(); - hideProgress(); - if (!hasFile) { - hideComment(); - if (msg.getBody() == msg.getOutOfBandUrl()) { - text->setText(""); - text->hide(); - } - QMimeDatabase db; - QMimeType type = db.mimeTypeForFile(path); - QStringList parts = type.name().split("/"); - QString big = parts.front(); - QFileInfo info(path); - if (big == "image") { - file = new Image(path); - } else { - file = new QLabel(); - file->setPixmap(QIcon::fromTheme(type.iconName()).pixmap(50)); - file->setAlignment(Qt::AlignCenter); - showComment(info.fileName(), true); - } - file->setContextMenuPolicy(Qt::ActionsContextMenu); - QAction* openAction = new QAction(QIcon::fromTheme("document-new-from-template"), tr("Open"), file); - connect(openAction, &QAction::triggered, [path]() { //TODO need to get rid of this shame - QDesktopServices::openUrl(QUrl::fromLocalFile(path)); - }); - file->addAction(openAction); - bodyLayout->insertWidget(2, file); - hasFile = true; - } -} - -void Message::hideComment() -{ - if (commentAdded) { - bodyLayout->removeWidget(fileComment); - fileComment->hide(); - fileComment->setWordWrap(false); - commentAdded = false; - } -} - -void Message::hideButton() -{ - if (hasButton) { - button->deleteLater(); - button = 0; - hasButton = false; - } -} - -void Message::hideFile() -{ - if (hasFile) { - file->deleteLater(); - file = 0; - hasFile = false; - } -} - -void Message::hideProgress() -{ - if (hasProgress) { - progress->deleteLater(); - progress = 0; - hasProgress = false;; - } -} -void Message::showComment(const QString& comment, bool wordWrap) -{ - if (!commentAdded) { - int index = 2; - if (hasFile) { - index++; - } - if (hasButton) { - index++; - } - if (hasProgress) { - index++; - } - bodyLayout->insertWidget(index, fileComment); - fileComment->show(); - commentAdded = true; - } - fileComment->setWordWrap(wordWrap); - fileComment->setText(comment); -} - -const Shared::Message & Message::getMessage() const -{ - return msg; -} - -void Message::setAvatarPath(const QString& p_path) -{ - if (p_path.size() == 0) { - avatar->setPath(Shared::iconPath("user", true)); - } else { - avatar->setPath(p_path); - } -} - -bool Message::change(const QMap& data) -{ - bool idChanged = msg.change(data); - - QString body = msg.getBody(); - QString bd = Shared::processMessageBody(body); - if (body.size() > 0) { - text->setText(bd); - text->show(); - } else { - text->setText(body); - text->hide(); - } - if (msg.getEdited()) { - setEdited(); - } - if (hasStatusIcon) { - setState(); - } - - - return idChanged; -} - -void Message::setEdited() -{ - if (!hasEditedLabel) { - editedLabel = new QLabel(); - hasEditedLabel = true; - QIcon q(Shared::icon("edit-rename")); - editedLabel->setPixmap(q.pixmap(12, 12)); - QHBoxLayout* statusLay = static_cast(statusBar->layout()); - statusLay->insertWidget(1, editedLabel); - } - editedLabel->setToolTip("Last time edited: " + msg.getLastModified().toLocalTime().toString() - + "\nOriginal message: " + msg.getOriginalBody()); -} - -void Message::setState() -{ - Shared::Message::State state = msg.getState(); - QIcon q(Shared::icon(Shared::messageStateThemeIcons[static_cast(state)])); - QString tt = Shared::Global::getName(state); - if (state == Shared::Message::State::error) { - QString errText = msg.getErrorText(); - if (errText.size() > 0) { - tt += ": " + errText; - } - } - statusIcon->setToolTip(tt); - statusIcon->setPixmap(q.pixmap(12, 12)); -} - diff --git a/ui/widgets/messageline/message.h b/ui/widgets/messageline/message.h deleted file mode 100644 index eef93a1..0000000 --- a/ui/widgets/messageline/message.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * 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 MESSAGE_H -#define MESSAGE_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "shared/message.h" -#include "shared/icons.h" -#include "shared/global.h" -#include "shared/utils.h" -#include "resizer.h" -#include "image.h" - -/** - * @todo write docs - */ -class Message : public QWidget -{ - Q_OBJECT -public: - Message(const Shared::Message& source, bool outgoing, const QString& sender, const QString& avatarPath = "", QWidget* parent = nullptr); - ~Message(); - - void setSender(const QString& sender); - QString getId() const; - QString getSenderJid() const; - QString getSenderResource() const; - QString getFileUrl() const; - const Shared::Message& getMessage() const; - - void addButton(const QIcon& icon, const QString& buttonText, const QString& tooltip = ""); - void showComment(const QString& comment, bool wordWrap = false); - void hideComment(); - void showFile(const QString& path); - void setProgress(qreal value); - void setAvatarPath(const QString& p_path); - bool change(const QMap& data); - - bool const outgoing; - -signals: - void buttonClicked(); - -private: - Shared::Message msg; - QWidget* body; - QWidget* statusBar; - QVBoxLayout* bodyLayout; - QHBoxLayout* layout; - QLabel* date; - QLabel* sender; - QLabel* text; - QGraphicsDropShadowEffect* shadow; - QPushButton* button; - QLabel* file; - QProgressBar* progress; - QLabel* fileComment; - QLabel* statusIcon; - QLabel* editedLabel; - Image* avatar; - bool hasButton; - bool hasProgress; - bool hasFile; - bool commentAdded; - bool hasStatusIcon; - bool hasEditedLabel; - -private: - void hideButton(); - void hideProgress(); - void hideFile(); - void setState(); - void setEdited(); -}; - -#endif // MESSAGE_H diff --git a/ui/widgets/messageline/messageline.cpp b/ui/widgets/messageline/messageline.cpp deleted file mode 100644 index fec0037..0000000 --- a/ui/widgets/messageline/messageline.cpp +++ /dev/null @@ -1,504 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * 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 "messageline.h" -#include -#include -#include - -MessageLine::MessageLine(bool p_room, QWidget* parent): - QWidget(parent), - messageIndex(), - messageOrder(), - myMessages(), - palMessages(), - uploadPaths(), - palAvatars(), - exPalAvatars(), - layout(new QVBoxLayout(this)), - myName(), - myAvatarPath(), - palNames(), - uploading(), - downloading(), - room(p_room), - busyShown(false), - progress() -{ - setContentsMargins(0, 0, 0, 0); - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(0); - layout->addStretch(); -} - -MessageLine::~MessageLine() -{ - for (Index::const_iterator itr = messageIndex.begin(), end = messageIndex.end(); itr != end; ++itr) { - delete itr->second; - } -} - -MessageLine::Position MessageLine::message(const Shared::Message& msg, bool forceOutgoing) -{ - QString id = msg.getId(); - Index::iterator itr = messageIndex.find(id); - if (itr != messageIndex.end()) { - qDebug() << "received more then one message with the same id, skipping yet the new one"; - return invalid; - } - - QString sender; - QString aPath; - bool outgoing; - - if (forceOutgoing) { - sender = myName; - aPath = myAvatarPath; - outgoing = true; - } else { - if (room) { - if (msg.getFromResource() == myName) { - sender = myName; - aPath = myAvatarPath; - outgoing = true; - } else { - sender = msg.getFromResource(); - std::map::iterator aItr = palAvatars.find(sender); - if (aItr != palAvatars.end()) { - aPath = aItr->second; - } else { - aItr = exPalAvatars.find(sender); - if (aItr != exPalAvatars.end()) { - aPath = aItr->second; - } - } - outgoing = false; - } - } else { - if (msg.getOutgoing()) { - sender = myName; - aPath = myAvatarPath; - outgoing = true; - } else { - QString jid = msg.getFromJid(); - std::map::iterator itr = palNames.find(jid); - if (itr != palNames.end()) { - sender = itr->second; - } else { - sender = jid; - } - - std::map::iterator aItr = palAvatars.find(jid); - if (aItr != palAvatars.end()) { - aPath = aItr->second; - } - - outgoing = false; - } - } - } - - Message* message = new Message(msg, outgoing, sender, aPath); - - std::pair result = messageOrder.insert(std::make_pair(msg.getTime(), message)); - if (!result.second) { - qDebug() << "Error appending a message into a message list - seems like the time of that message exactly matches the time of some other message, can't put them in order, skipping yet"; - delete message; - return invalid; - } - if (outgoing) { - myMessages.insert(std::make_pair(id, message)); - } else { - QString senderId; - if (room) { - senderId = sender; - } else { - senderId = msg.getFromJid(); - } - - std::map::iterator pItr = palMessages.find(senderId); - if (pItr == palMessages.end()) { - pItr = palMessages.insert(std::make_pair(senderId, Index())).first; - } - pItr->second.insert(std::make_pair(id, message)); - } - messageIndex.insert(std::make_pair(id, message)); - unsigned long index = std::distance(messageOrder.begin(), result.first); //need to make with binary indexed tree - Position res = invalid; - if (index == 0) { - res = beggining; - } else if (index == messageIndex.size() - 1) { - res = end; - } else { - res = middle; - } - - if (busyShown) { - index += 1; - } - - - if (res == end) { - layout->addWidget(message); - } else { - layout->insertWidget(index + 1, message); - } - - if (msg.hasOutOfBandUrl()) { - emit requestLocalFile(msg.getId(), msg.getOutOfBandUrl()); - connect(message, &Message::buttonClicked, this, &MessageLine::onDownload); - } - - return res; -} - -void MessageLine::changeMessage(const QString& id, const QMap& data) -{ - Index::const_iterator itr = messageIndex.find(id); - if (itr != messageIndex.end()) { - Message* msg = itr->second; - if (msg->change(data)) { //if ID changed (stanza in replace of another) - QString newId = msg->getId(); //need to updated IDs of that message in all maps - messageIndex.erase(itr); - messageIndex.insert(std::make_pair(newId, msg)); - if (msg->outgoing) { - QString senderId; - if (room) { - senderId = msg->getSenderResource(); - } else { - senderId = msg->getSenderJid(); - } - - std::map::iterator pItr = palMessages.find(senderId); - if (pItr != palMessages.end()) { - Index::const_iterator sItr = pItr->second.find(id); - if (sItr != pItr->second.end()) { - pItr->second.erase(sItr); - pItr->second.insert(std::make_pair(newId, msg)); - } else { - qDebug() << "Was trying to replace message in open conversations, couldn't find it among pal's messages, probably an error"; - } - } else { - qDebug() << "Was trying to replace message in open conversations, couldn't find pal messages, probably an error"; - } - } else { - Index::const_iterator mItr = myMessages.find(id); - if (mItr != myMessages.end()) { - myMessages.erase(mItr); - myMessages.insert(std::make_pair(newId, msg)); - } else { - qDebug() << "Was trying to replace message in open conversations, couldn't find it among my messages, probably an error"; - } - } - } - } -} - -void MessageLine::onDownload() -{ - Message* msg = static_cast(sender()); - QString messageId = msg->getId(); - Index::const_iterator itr = downloading.find(messageId); - if (itr == downloading.end()) { - downloading.insert(std::make_pair(messageId, msg)); - msg->setProgress(0); - msg->showComment(tr("Downloading...")); - emit downloadFile(messageId, msg->getFileUrl()); - } else { - qDebug() << "An attempt to initiate download for already downloading file" << msg->getFileUrl() << ", skipping"; - } -} - -void MessageLine::setMyName(const QString& name) -{ - myName = name; - for (Index::const_iterator itr = myMessages.begin(), end = myMessages.end(); itr != end; ++itr) { - itr->second->setSender(name); - } -} - -void MessageLine::setPalName(const QString& jid, const QString& name) -{ - std::map::iterator itr = palNames.find(jid); - if (itr == palNames.end()) { - palNames.insert(std::make_pair(jid, name)); - } else { - itr->second = name; - } - - std::map::iterator pItr = palMessages.find(jid); - if (pItr != palMessages.end()) { - for (Index::const_iterator itr = pItr->second.begin(), end = pItr->second.end(); itr != end; ++itr) { - itr->second->setSender(name); - } - } -} - -void MessageLine::setPalAvatar(const QString& jid, const QString& path) -{ - std::map::iterator itr = palAvatars.find(jid); - if (itr == palAvatars.end()) { - palAvatars.insert(std::make_pair(jid, path)); - - std::map::const_iterator eitr = exPalAvatars.find(jid); - if (eitr != exPalAvatars.end()) { - exPalAvatars.erase(eitr); - } - } else { - itr->second = path; - } - - std::map::iterator pItr = palMessages.find(jid); - if (pItr != palMessages.end()) { - for (Index::const_iterator itr = pItr->second.begin(), end = pItr->second.end(); itr != end; ++itr) { - itr->second->setAvatarPath(path); - } - } -} - -void MessageLine::dropPalAvatar(const QString& jid) -{ - std::map::iterator itr = palAvatars.find(jid); - if (itr != palAvatars.end()) { - palAvatars.erase(itr); - } - - std::map::const_iterator eitr = exPalAvatars.find(jid); - if (eitr != exPalAvatars.end()) { - exPalAvatars.erase(eitr); - } - - std::map::iterator pItr = palMessages.find(jid); - if (pItr != palMessages.end()) { - for (Index::const_iterator itr = pItr->second.begin(), end = pItr->second.end(); itr != end; ++itr) { - itr->second->setAvatarPath(""); - } - } -} - -void MessageLine::movePalAvatarToEx(const QString& name) -{ - std::map::iterator itr = palAvatars.find(name); - if (itr != palAvatars.end()) { - std::map::iterator eitr = exPalAvatars.find(name); - if (eitr != exPalAvatars.end()) { - eitr->second = itr->second; - } else { - exPalAvatars.insert(std::make_pair(name, itr->second)); - } - - palAvatars.erase(itr); - } -} - -void MessageLine::resizeEvent(QResizeEvent* event) -{ - QWidget::resizeEvent(event); - emit resize(event->size().height() - event->oldSize().height()); -} - - -QString MessageLine::firstMessageId() const -{ - if (messageOrder.size() == 0) { - return ""; - } else { - return messageOrder.begin()->second->getId(); - } -} - -void MessageLine::showBusyIndicator() -{ - if (!busyShown) { - layout->insertWidget(0, &progress); - progress.start(); - busyShown = true; - } -} - -void MessageLine::hideBusyIndicator() -{ - if (busyShown) { - progress.stop(); - layout->removeWidget(&progress); - busyShown = false; - } -} - -void MessageLine::fileProgress(const QString& messageId, qreal progress) -{ - Index::const_iterator itr = messageIndex.find(messageId); - if (itr == messageIndex.end()) { - //TODO may be some logging, that's not normal - } else { - itr->second->setProgress(progress); - } -} - -void MessageLine::responseLocalFile(const QString& messageId, const QString& path) -{ - Index::const_iterator itr = messageIndex.find(messageId); - if (itr == messageIndex.end()) { - - } else { - Index::const_iterator uItr = uploading.find(messageId); - if (path.size() > 0) { - Index::const_iterator dItr = downloading.find(messageId); - if (dItr != downloading.end()) { - downloading.erase(dItr); - itr->second->showFile(path); - } else { - if (uItr != uploading.end()) { - uploading.erase(uItr); - std::map::const_iterator muItr = uploadPaths.find(messageId); - if (muItr != uploadPaths.end()) { - uploadPaths.erase(muItr); - } - Shared::Message msg = itr->second->getMessage(); - removeMessage(messageId); - msg.setCurrentTime(); - message(msg); - itr = messageIndex.find(messageId); - itr->second->showFile(path); - } else { - itr->second->showFile(path); //then it is already cached file - } - } - } else { - if (uItr == uploading.end()) { - const Shared::Message& msg = itr->second->getMessage(); - itr->second->addButton(QIcon::fromTheme("download"), tr("Download"), "" + msg.getOutOfBandUrl() + ""); - itr->second->showComment(tr("Push the button to download the file")); - } else { - qDebug() << "An unhandled state for file uploading - empty path"; - } - } - } -} - -void MessageLine::removeMessage(const QString& messageId) -{ - Index::const_iterator itr = messageIndex.find(messageId); - if (itr != messageIndex.end()) { - Message* ui = itr->second; - const Shared::Message& msg = ui->getMessage(); - messageIndex.erase(itr); - Order::const_iterator oItr = messageOrder.find(msg.getTime()); - if (oItr != messageOrder.end()) { - messageOrder.erase(oItr); - } else { - qDebug() << "An attempt to remove message from messageLine, but it wasn't found in order"; - } - if (msg.getOutgoing()) { - Index::const_iterator mItr = myMessages.find(messageId); - if (mItr != myMessages.end()) { - myMessages.erase(mItr); - } else { - qDebug() << "Error removing message: it seems to be outgoing yet it wasn't found in outgoing messages"; - } - } else { - if (room) { - - } else { - QString jid = msg.getFromJid(); - std::map::iterator pItr = palMessages.find(jid); - if (pItr != palMessages.end()) { - Index& pMsgs = pItr->second; - Index::const_iterator pmitr = pMsgs.find(messageId); - if (pmitr != pMsgs.end()) { - pMsgs.erase(pmitr); - } else { - qDebug() << "Error removing message: it seems to be incoming yet it wasn't found among messages from that penpal"; - } - } - } - } - ui->deleteLater(); - qDebug() << "message" << messageId << "has been removed"; - } else { - qDebug() << "An attempt to remove non existing message from messageLine"; - } -} - -void MessageLine::fileError(const QString& messageId, const QString& error) -{ - Index::const_iterator itr = downloading.find(messageId); - if (itr == downloading.end()) { - Index::const_iterator itr = uploading.find(messageId); - if (itr == uploading.end()) { - //TODO may be some logging, that's not normal - } else { - itr->second->showComment(tr("Error uploading file: %1\nYou can try again").arg(QCoreApplication::translate("NetworkErrors", error.toLatin1())), true); - itr->second->addButton(QIcon::fromTheme("upload"), tr("Upload")); - } - } else { - const Shared::Message& msg = itr->second->getMessage(); - itr->second->addButton(QIcon::fromTheme("download"), tr("Download"), "" + msg.getOutOfBandUrl() + ""); - itr->second->showComment(tr("Error downloading file: %1\nYou can try again").arg(QCoreApplication::translate("NetworkErrors", error.toLatin1())), true); - } -} - -void MessageLine::appendMessageWithUpload(const Shared::Message& msg, const QString& path) -{ - appendMessageWithUploadNoSiganl(msg, path); - emit uploadFile(msg, path); -} - -void MessageLine::appendMessageWithUploadNoSiganl(const Shared::Message& msg, const QString& path) -{ - message(msg, true); - QString id = msg.getId(); - Message* ui = messageIndex.find(id)->second; - connect(ui, &Message::buttonClicked, this, &MessageLine::onUpload); //this is in case of retry; - ui->setProgress(0); - ui->showComment(tr("Uploading...")); - uploading.insert(std::make_pair(id, ui)); - uploadPaths.insert(std::make_pair(id, path)); -} - - -void MessageLine::onUpload() -{ - //TODO retry -} - -void MessageLine::setMyAvatarPath(const QString& p_path) -{ - if (myAvatarPath != p_path) { - myAvatarPath = p_path; - for (std::pair pair : myMessages) { - pair.second->setAvatarPath(myAvatarPath); - } - } -} - -void MessageLine::setExPalAvatars(const std::map& data) -{ - exPalAvatars = data; - - for (const std::pair& pair : palMessages) { - if (palAvatars.find(pair.first) == palAvatars.end()) { - std::map::const_iterator eitr = exPalAvatars.find(pair.first); - if (eitr != exPalAvatars.end()) { - for (const std::pair& mp : pair.second) { - mp.second->setAvatarPath(eitr->second); - } - } - } - } -} diff --git a/ui/widgets/messageline/messageline.h b/ui/widgets/messageline/messageline.h deleted file mode 100644 index a0a7b6c..0000000 --- a/ui/widgets/messageline/messageline.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * 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 MESSAGELINE_H -#define MESSAGELINE_H - -#include -#include -#include -#include -#include -#include - -#include "shared/message.h" -#include "message.h" -#include "progress.h" - -class MessageLine : public QWidget -{ - Q_OBJECT -public: - enum Position { - beggining, - middle, - end, - invalid - }; - MessageLine(bool p_room, QWidget* parent = 0); - ~MessageLine(); - - Position message(const Shared::Message& msg, bool forceOutgoing = false); - void setMyName(const QString& name); - void setPalName(const QString& jid, const QString& name); - QString firstMessageId() const; - void showBusyIndicator(); - void hideBusyIndicator(); - void responseLocalFile(const QString& messageId, const QString& path); - void fileError(const QString& messageId, const QString& error); - void fileProgress(const QString& messageId, qreal progress); - void appendMessageWithUpload(const Shared::Message& msg, const QString& path); - void appendMessageWithUploadNoSiganl(const Shared::Message& msg, const QString& path); - void removeMessage(const QString& messageId); - void setMyAvatarPath(const QString& p_path); - void setPalAvatar(const QString& jid, const QString& path); - void dropPalAvatar(const QString& jid); - void changeMessage(const QString& id, const QMap& data); - void setExPalAvatars(const std::map& data); - void movePalAvatarToEx(const QString& name); - -signals: - void resize(int amount); - void downloadFile(const QString& messageId, const QString& url); - void uploadFile(const Shared::Message& msg, const QString& path); - void requestLocalFile(const QString& messageId, const QString& url); - -protected: - void resizeEvent(QResizeEvent * event) override; - -protected: - void onDownload(); - void onUpload(); - -private: - struct Comparator { - bool operator()(const Shared::Message& a, const Shared::Message& b) const { - return a.getTime() < b.getTime(); - } - bool operator()(const Shared::Message* a, const Shared::Message* b) const { - return a->getTime() < b->getTime(); - } - }; - typedef std::map Order; - typedef std::map Index; - Index messageIndex; - Order messageOrder; - Index myMessages; - std::map palMessages; - std::map uploadPaths; - std::map palAvatars; - std::map exPalAvatars; - QVBoxLayout* layout; - - QString myName; - QString myAvatarPath; - std::map palNames; - Index uploading; - Index downloading; - bool room; - bool busyShown; - Progress progress; -}; - -#endif // MESSAGELINE_H From bf4a27f35d2761485c201083d210ce3aca11559c Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 27 Mar 2022 22:05:31 +0300 Subject: [PATCH 029/137] Bug with the edited message fixed, some further work on message correction --- shared/message.cpp | 2 +- ui/models/roster.cpp | 2 ++ ui/widgets/conversation.cpp | 30 ++++++++++++++++++++++++-- ui/widgets/conversation.h | 1 + ui/widgets/messageline/messagefeed.cpp | 12 +++++++++++ ui/widgets/messageline/messagefeed.h | 17 +++++++++++++++ 6 files changed, 61 insertions(+), 3 deletions(-) diff --git a/shared/message.cpp b/shared/message.cpp index e6b47b2..f3f6b45 100644 --- a/shared/message.cpp +++ b/shared/message.cpp @@ -406,7 +406,7 @@ bool Shared::Message::change(const QMap& data) if (!edited || lastModified < correctionDate) { originalMessage = body; lastModified = correctionDate; - setBody(body); + setBody(b); setEdited(true); } } diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index 2d5f99f..588fb1d 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -549,6 +549,8 @@ void Models::Roster::changeMessage(const QString& account, const QString& jid, c Element* el = getElement({account, jid}); if (el != NULL) { el->changeMessage(id, data); + } else { + qDebug() << "A request to change a message of the contact " << jid << " in the account " << account << " but it wasn't found"; } } diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 07c599e..608faf3 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -110,7 +110,7 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, initializeOverlay(); - m_ui->currentActionBadge->setVisible(false);; + m_ui->currentActionBadge->setVisible(false); // m_ui->currentActionBadge->setText(tr("Editing message...")); } @@ -476,10 +476,10 @@ void Conversation::onFeedContext(const QPoint& pos) Shared::Message* item = static_cast(index.internalPointer()); contextMenu->clear(); + QString id = item->getId(); bool showMenu = false; if (item->getState() == Shared::Message::State::error) { showMenu = true; - QString id = item->getId(); QAction* resend = contextMenu->addAction(Shared::icon("view-refresh"), tr("Try sending again")); connect(resend, &QAction::triggered, [this, id]() { element->feed->registerUpload(id); @@ -500,6 +500,12 @@ void Conversation::onFeedContext(const QPoint& pos) Shared::Global::highlightInFileManager(path); }); } + + if (item->getOutgoing()) { + showMenu = true; + QAction* edit = contextMenu->addAction(Shared::icon("edit-rename"), tr("Edit")); + connect(edit, &QAction::triggered, this, std::bind(&Conversation::onMessageEditRequested, this, id)); + } if (showMenu) { contextMenu->popup(feed->viewport()->mapToGlobal(pos)); @@ -517,3 +523,23 @@ void Conversation::onMessageEditorContext(const QPoint& pos) editorMenu->exec(this->m_ui->messageEditor->mapToGlobal(pos)); } + +void Conversation::onMessageEditRequested(const QString& id) +{ + if (currentAction == CurrentAction::edit) { + //todo; + } + + try { + Shared::Message msg = element->feed->getMessage(id); + + m_ui->currentActionBadge->setVisible(true); + m_ui->currentActionBadge->setText(tr("Editing message...")); + currentAction = CurrentAction::edit; + m_ui->messageEditor->setText(msg.getBody()); + + } catch (const Models::MessageFeed::NotFound& e) { + qDebug() << "The message requested to be edited was not found" << e.getMessage().c_str(); + qDebug() << "Ignoring"; + } +} diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index c43d7a0..4bccdfc 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -116,6 +116,7 @@ protected slots: void positionShadow(); void onFeedContext(const QPoint &pos); void onMessageEditorContext(const QPoint &pos); + void onMessageEditRequested(const QString& id); public: const bool isMuc; diff --git a/ui/widgets/messageline/messagefeed.cpp b/ui/widgets/messageline/messagefeed.cpp index 4803dce..33fbdd4 100644 --- a/ui/widgets/messageline/messagefeed.cpp +++ b/ui/widgets/messageline/messagefeed.cpp @@ -224,8 +224,20 @@ std::set Models::MessageFeed::detectChanges(c void Models::MessageFeed::removeMessage(const QString& id) { + //todo; } +Shared::Message Models::MessageFeed::getMessage(const QString& id) +{ + StorageById::iterator itr = indexById.find(id); + if (itr == indexById.end()) { + throw NotFound(id.toStdString(), rosterItem->getJid().toStdString(), rosterItem->getAccountName().toStdString()); + } + + return **itr; +} + + QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const { int i = index.row(); diff --git a/ui/widgets/messageline/messagefeed.h b/ui/widgets/messageline/messagefeed.h index 2273b15..c9701ae 100644 --- a/ui/widgets/messageline/messagefeed.h +++ b/ui/widgets/messageline/messagefeed.h @@ -32,6 +32,7 @@ #include #include +#include namespace Models { @@ -55,6 +56,7 @@ public: void addMessage(const Shared::Message& msg); void changeMessage(const QString& id, const QMap& data); void removeMessage(const QString& id); + Shared::Message getMessage(const QString& id); QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; @@ -103,6 +105,21 @@ public: Error, Bulk }; + + class NotFound: + public Utils::Exception + { + public: + NotFound(const std::string& k, const std::string& j, const std::string& acc):Exception(), key(k), jid(j), account(acc){} + + std::string getMessage() const { + return "Message with id " + key + " wasn't found in messageFeed " + account + " of the chat with " + jid; + } + private: + std::string key; + std::string jid; + std::string account; + }; protected: bool sentByMe(const Shared::Message& msg) const; From 788c6ca5567563322d3d5afd7943e672e7cdc0f1 Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 28 Mar 2022 23:25:33 +0300 Subject: [PATCH 030/137] now it's possible to fix your messages --- CHANGELOG.md | 2 + core/account.cpp | 4 + core/account.h | 1 + core/handlers/messagehandler.cpp | 192 ++++++++++++++++++++----------- core/handlers/messagehandler.h | 8 +- core/main.cpp | 1 + core/squawk.cpp | 11 ++ core/squawk.h | 1 + shared/message.cpp | 4 +- ui/squawk.cpp | 12 ++ ui/squawk.h | 2 + ui/widgets/conversation.cpp | 44 ++++--- ui/widgets/conversation.h | 7 +- 13 files changed, 204 insertions(+), 85 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a6fdfa..efb159c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Bug fixes - build in release mode now no longer spams warnings - build now correctly installs all build plugin libs +- a bug where the correction message was received, the indication was on but the text didn't actually change ### Improvements - reduced amount of places where platform specific path separator is used @@ -13,6 +14,7 @@ - now it's possible to set up different qt styles from settings - if you have KConfig nad KConfigWidgets packages installed - you can chose from global color schemes - it's possible now to chose a folder where squawk is going to store downloaded files +- now you can correct your message ## Squawk 0.2.0 (Jan 10, 2022) ### Bug fixes diff --git a/core/account.cpp b/core/account.cpp index a923690..91d0f2b 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -935,3 +935,7 @@ void Core::Account::requestChangeMessage(const QString& jid, const QString& mess void Core::Account::resendMessage(const QString& jid, const QString& id) { mh->resendMessage(jid, id);} + +void Core::Account::replaceMessage(const QString& originalId, const Shared::Message& data) { + mh->sendMessage(data, false, originalId);} + diff --git a/core/account.h b/core/account.h index 5ba834c..664b547 100644 --- a/core/account.h +++ b/core/account.h @@ -104,6 +104,7 @@ public: void addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin); void uploadVCard(const Shared::VCard& card); void resendMessage(const QString& jid, const QString& id); + void replaceMessage(const QString& originalId, const Shared::Message& data); public slots: void connect(); diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index 1e89dd6..559bee3 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -41,10 +41,10 @@ void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) handled = handleGroupMessage(msg); break; case QXmppMessage::Error: { - QString id = msg.id(); - std::map::const_iterator itr = pendingStateMessages.find(id); - if (itr != pendingStateMessages.end()) { - QString jid = itr->second; + std::tuple ids = getOriginalPendingMessageId(msg.id()); + if (std::get<0>(ids)) { + QString id = std::get<1>(ids); + QString jid = std::get<2>(ids); RosterItem* cnt = acc->rh->getRosterItem(jid); QMap cData = { {"state", static_cast(Shared::Message::State::error)}, @@ -53,9 +53,7 @@ void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) if (cnt != 0) { cnt->changeMessage(id, cData); } - ; emit acc->changeMessage(jid, id, cData); - pendingStateMessages.erase(itr); handled = true; } else { qDebug() << "received a message with type \"Error\", not sure what to do with it now, skipping"; @@ -111,7 +109,6 @@ bool Core::MessageHandler::handleGroupMessage(const QXmppMessage& msg, bool outg { const QString& body(msg.body()); if (body.size() != 0) { - QString id = msg.id(); Shared::Message sMsg(Shared::Message::groupChat); initializeMessage(sMsg, msg, outgoing, forwarded, guessing); @@ -121,12 +118,11 @@ bool Core::MessageHandler::handleGroupMessage(const QXmppMessage& msg, bool outg return false; } - std::map::const_iterator pItr = pendingStateMessages.find(id); - if (pItr != pendingStateMessages.end()) { + std::tuple ids = getOriginalPendingMessageId(msg.id()); + if (std::get<0>(ids)) { QMap cData = {{"state", static_cast(Shared::Message::State::delivered)}}; - cnt->changeMessage(id, cData); - pendingStateMessages.erase(pItr); - emit acc->changeMessage(jid, id, cData); + cnt->changeMessage(std::get<1>(ids), cData); + emit acc->changeMessage(std::get<2>(ids), std::get<1>(ids), cData); } else { QString oId = msg.replaceId(); if (oId.size() > 0) { @@ -227,53 +223,70 @@ void Core::MessageHandler::onCarbonMessageSent(const QXmppMessage& msg) handleChatMessage(msg, true, true); } -void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& id) +std::tuple Core::MessageHandler::getOriginalPendingMessageId(const QString& id) { + std::tuple result({false, "", ""}); std::map::const_iterator itr = pendingStateMessages.find(id); if (itr != pendingStateMessages.end()) { - QMap cData = {{"state", static_cast(Shared::Message::State::delivered)}}; - RosterItem* ri = acc->rh->getRosterItem(itr->second); - if (ri != 0) { - ri->changeMessage(id, cData); + std::get<0>(result) = true; + std::get<2>(result) = itr->second; + + std::map::const_iterator itrC = pendingCorrectionMessages.find(id); + if (itrC != pendingCorrectionMessages.end()) { + std::get<1>(result) = itrC->second; + pendingCorrectionMessages.erase(itrC); + } else { + std::get<1>(result) = itr->first; } - emit acc->changeMessage(itr->second, id, cData); + pendingStateMessages.erase(itr); } + + return result; } -void Core::MessageHandler::sendMessage(const Shared::Message& data, bool newMessage) +void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& id) { - if (data.getOutOfBandUrl().size() == 0 && data.getAttachPath().size() > 0) { - prepareUpload(data, newMessage); - } else { - performSending(data, newMessage); + std::tuple ids = getOriginalPendingMessageId(id); + if (std::get<0>(ids)) { + QMap cData = {{"state", static_cast(Shared::Message::State::delivered)}}; + RosterItem* ri = acc->rh->getRosterItem(std::get<2>(ids)); + + if (ri != 0) { + ri->changeMessage(std::get<1>(ids), cData); + } + emit acc->changeMessage(std::get<2>(ids), std::get<1>(ids), cData); } } -void Core::MessageHandler::performSending(Shared::Message data, bool newMessage) +void Core::MessageHandler::sendMessage(const Shared::Message& data, bool newMessage, QString originalId) +{ + if (data.getOutOfBandUrl().size() == 0 && data.getAttachPath().size() > 0) { + pendingCorrectionMessages.insert(std::make_pair(data.getId(), originalId)); + prepareUpload(data, newMessage); + } else { + performSending(data, originalId, newMessage); + } +} + +void Core::MessageHandler::performSending(Shared::Message data, const QString& originalId, bool newMessage) { QString jid = data.getPenPalJid(); QString id = data.getId(); - QString oob = data.getOutOfBandUrl(); + qDebug() << "Sending message with id:" << id; + if (originalId.size() > 0) { + qDebug() << "To replace one with id:" << originalId; + } RosterItem* ri = acc->rh->getRosterItem(jid); bool sent = false; - QMap changes; + if (newMessage && originalId.size() > 0) { + newMessage = false; + } QDateTime sendTime = QDateTime::currentDateTimeUtc(); if (acc->state == Shared::ConnectionState::connected) { - QXmppMessage msg(acc->getFullJid(), data.getTo(), data.getBody(), data.getThread()); - -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) - msg.setOriginId(id); -#endif - msg.setId(id); - msg.setType(static_cast(data.getType())); //it is safe here, my type is compatible - msg.setOutOfBandUrl(oob); - msg.setReceiptRequested(true); - msg.setStamp(sendTime); + QXmppMessage msg(createPacket(data, sendTime, originalId)); sent = acc->client.sendPacket(msg); - //sent = false; - if (sent) { data.setState(Shared::Message::State::sent); } else { @@ -286,6 +299,39 @@ void Core::MessageHandler::performSending(Shared::Message data, bool newMessage) data.setErrorText("You are is offline or reconnecting"); } + QMap changes(getChanges(data, sendTime, newMessage, originalId)); + + QString realId; + if (originalId.size() > 0) { + realId = originalId; + } else { + realId = id; + } + if (ri != 0) { + if (newMessage) { + ri->appendMessageToArchive(data); + } else { + ri->changeMessage(realId, changes); + } + if (sent) { + pendingStateMessages.insert(std::make_pair(id, jid)); + if (originalId.size() > 0) { + pendingCorrectionMessages.insert(std::make_pair(id, originalId)); + } + } else { + pendingStateMessages.erase(id); + pendingCorrectionMessages.erase(id); + } + } + + emit acc->changeMessage(jid, realId, changes); +} + +QMap Core::MessageHandler::getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const +{ + QMap changes; + + QString oob = data.getOutOfBandUrl(); Shared::Message::State mstate = data.getState(); changes.insert("state", static_cast(mstate)); if (mstate == Shared::Message::State::error) { @@ -295,9 +341,12 @@ void Core::MessageHandler::performSending(Shared::Message data, bool newMessage) changes.insert("outOfBandUrl", oob); } if (newMessage) { - data.setTime(sendTime); + data.setTime(time); } - changes.insert("stamp", sendTime); + if (originalId.size() > 0) { + changes.insert("body", data.getBody()); + } + changes.insert("stamp", time); //sometimes (when the image is pasted with ctrl+v) //I start sending message with one path, then copy it to downloads directory @@ -310,21 +359,29 @@ void Core::MessageHandler::performSending(Shared::Message data, bool newMessage) data.setAttachPath(squawkified); } } - - if (ri != 0) { - if (newMessage) { - ri->appendMessageToArchive(data); - } else { - ri->changeMessage(id, changes); - } - if (sent) { - pendingStateMessages.insert(std::make_pair(id, jid)); - } else { - pendingStateMessages.erase(id); - } + + return changes; +} + +QXmppMessage Core::MessageHandler::createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const +{ + QXmppMessage msg(acc->getFullJid(), data.getTo(), data.getBody(), data.getThread()); + QString id(data.getId()); + + if (originalId.size() > 0) { + msg.setReplaceId(originalId); } - - emit acc->changeMessage(jid, id, changes); + +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) + msg.setOriginId(id); +#endif + msg.setId(id); + msg.setType(static_cast(data.getType())); //it is safe here, my type is compatible + msg.setOutOfBandUrl(data.getOutOfBandUrl()); + msg.setReceiptRequested(true); + msg.setStamp(time); + + return msg; } void Core::MessageHandler::prepareUpload(const Shared::Message& data, bool newMessage) @@ -444,7 +501,8 @@ void Core::MessageHandler::onLoadFileError(const std::list& void Core::MessageHandler::handleUploadError(const QString& jid, const QString& messageId, const QString& errorText) { emit acc->uploadFileError(jid, messageId, "Error requesting slot to upload file: " + errorText); - pendingStateMessages.erase(jid); + pendingStateMessages.erase(messageId); + pendingCorrectionMessages.erase(messageId); requestChangeMessage(jid, messageId, { {"state", static_cast(Shared::Message::State::error)}, {"errorText", errorText} @@ -473,11 +531,11 @@ void Core::MessageHandler::sendMessageWithLocalUploadedFile(Shared::Message msg, if (msg.getBody().size() == 0) { //not sure why, but most messages do that msg.setBody(url); //they duplicate oob in body, some of them wouldn't even show an attachment if you don't do that } - performSending(msg, newMessage); + performSending(msg, pendingCorrectionMessages.at(msg.getId()), newMessage); //TODO removal/progress update } -static const std::set allowerToChangeKeys({ +static const std::set allowedToChangeKeys({ "attachPath", "outOfBandUrl", "state", @@ -490,12 +548,12 @@ void Core::MessageHandler::requestChangeMessage(const QString& jid, const QStrin if (cnt != 0) { bool allSupported = true; QString unsupportedString; - for (QMap::const_iterator itr = data.begin(); itr != data.end(); ++itr) { //I need all this madness - if (allowerToChangeKeys.count(itr.key()) != 1) { //to not allow this method - allSupported = false; //to make a message to look like if it was edited - unsupportedString = itr.key(); //basically I needed to control who exaclty calls this method - break; //because the underlying tech assumes that the change is initiated by user - } //not by system + for (QMap::const_iterator itr = data.begin(); itr != data.end(); ++itr) { //I need all this madness + if (allowedToChangeKeys.count(itr.key()) != 1) { //to not allow this method + allSupported = false; //to make a message to look like if it was edited + unsupportedString = itr.key(); //basically I needed to control who exaclty calls this method + break; //because the underlying tech assumes that + } //the change is initiated by user, not by system } if (allSupported) { cnt->changeMessage(messageId, data); @@ -514,7 +572,13 @@ void Core::MessageHandler::resendMessage(const QString& jid, const QString& id) try { Shared::Message msg = cnt->getMessage(id); if (msg.getState() == Shared::Message::State::error) { - sendMessage(msg, false); + if (msg.getEdited()){ + QString originalId = msg.getId(); + msg.generateRandomId(); + sendMessage(msg, false, originalId); + } else { + sendMessage(msg, false); + } } else { qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message seems to have been normally sent, this method was made to retry sending failed to be sent messages, skipping"; } diff --git a/core/handlers/messagehandler.h b/core/handlers/messagehandler.h index 4f03484..1ab2d0d 100644 --- a/core/handlers/messagehandler.h +++ b/core/handlers/messagehandler.h @@ -46,7 +46,7 @@ public: MessageHandler(Account* account); public: - void sendMessage(const Shared::Message& data, bool newMessage = true); + void sendMessage(const Shared::Message& data, bool newMessage = true, QString originalId = ""); void initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing = false, bool forwarded = false, bool guessing = false) const; void resendMessage(const QString& jid, const QString& id); @@ -67,13 +67,17 @@ private: bool handleGroupMessage(const QXmppMessage& msg, bool outgoing = false, bool forwarded = false, bool guessing = false); void logMessage(const QXmppMessage& msg, const QString& reason = "Message wasn't handled: "); void sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url, bool newMessage = true); - void performSending(Shared::Message data, bool newMessage = true); + void performSending(Shared::Message data, const QString& originalId, bool newMessage = true); void prepareUpload(const Shared::Message& data, bool newMessage = true); void handleUploadError(const QString& jid, const QString& messageId, const QString& errorText); + QXmppMessage createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const; + QMap getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const; + std::tuple getOriginalPendingMessageId(const QString& id); private: Account* acc; std::map pendingStateMessages; //key is message id, value is JID + std::map pendingCorrectionMessages; //key is new mesage, value is originalOne std::deque> uploadingSlotsQueue; }; diff --git a/core/main.cpp b/core/main.cpp index 79ca648..f842c80 100644 --- a/core/main.cpp +++ b/core/main.cpp @@ -142,6 +142,7 @@ int main(int argc, char *argv[]) QObject::connect(&w, &Squawk::disconnectAccount, squawk, &Core::Squawk::disconnectAccount); QObject::connect(&w, &Squawk::changeState, squawk, &Core::Squawk::changeState); QObject::connect(&w, &Squawk::sendMessage, squawk,&Core::Squawk::sendMessage); + QObject::connect(&w, &Squawk::replaceMessage, squawk,&Core::Squawk::replaceMessage); QObject::connect(&w, &Squawk::resendMessage, squawk,&Core::Squawk::resendMessage); QObject::connect(&w, &Squawk::requestArchive, squawk, &Core::Squawk::requestArchive); QObject::connect(&w, &Squawk::subscribeContact, squawk, &Core::Squawk::subscribeContact); diff --git a/core/squawk.cpp b/core/squawk.cpp index 9f2b445..af131d5 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -341,6 +341,17 @@ void Core::Squawk::sendMessage(const QString& account, const Shared::Message& da itr->second->sendMessage(data); } +void Core::Squawk::replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data) +{ + AccountsMap::const_iterator itr = amap.find(account); + if (itr == amap.end()) { + qDebug() << "An attempt to replace a message with non existing account" << account << ", skipping"; + return; + } + + itr->second->replaceMessage(originalId, data); +} + void Core::Squawk::resendMessage(const QString& account, const QString& jid, const QString& id) { AccountsMap::const_iterator itr = amap.find(account); diff --git a/core/squawk.h b/core/squawk.h index 738a957..6cd251f 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -101,6 +101,7 @@ public slots: void changeState(Shared::Availability state); void sendMessage(const QString& account, const Shared::Message& data); + void replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data); void resendMessage(const QString& account, const QString& jid, const QString& id); void requestArchive(const QString& account, const QString& jid, int count, const QString& before); diff --git a/shared/message.cpp b/shared/message.cpp index f3f6b45..0e1b3c5 100644 --- a/shared/message.cpp +++ b/shared/message.cpp @@ -404,7 +404,9 @@ bool Shared::Message::change(const QMap& data) correctionDate = QDateTime::currentDateTimeUtc(); //in case there is no information about time of this correction it's applied } if (!edited || lastModified < correctionDate) { - originalMessage = body; + if (!edited) { + originalMessage = body; + } lastModified = correctionDate; setBody(b); setEdited(true); diff --git a/ui/squawk.cpp b/ui/squawk.cpp index e24640a..3ebb6a5 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -497,6 +497,17 @@ void Squawk::onConversationMessage(const Shared::Message& msg) emit sendMessage(acc, msg); } +void Squawk::onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg) +{ + Conversation* conv = static_cast(sender()); + QString acc = conv->getAccount(); + + rosterModel.changeMessage(acc, msg.getPenPalJid(), originalId, { + {"state", static_cast(Shared::Message::State::pending)} + }); + emit replaceMessage(acc, originalId, msg); +} + void Squawk::onConversationResend(const QString& id) { Conversation* conv = static_cast(sender()); @@ -958,6 +969,7 @@ void Squawk::subscribeConversation(Conversation* conv) { connect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed); connect(conv, &Conversation::sendMessage, this, &Squawk::onConversationMessage); + connect(conv, &Conversation::replaceMessage, this, &Squawk::onConversationReplaceMessage); connect(conv, &Conversation::resendMessage, this, &Squawk::onConversationResend); connect(conv, &Conversation::notifyableMessage, this, &Squawk::notify); } diff --git a/ui/squawk.h b/ui/squawk.h index 7551f66..7bd2e10 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -62,6 +62,7 @@ signals: void disconnectAccount(const QString&); void changeState(Shared::Availability state); void sendMessage(const QString& account, const Shared::Message& data); + void replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data); void resendMessage(const QString& account, const QString& jid, const QString& id); void requestArchive(const QString& account, const QString& jid, int count, const QString& before); void subscribeContact(const QString& account, const QString& jid, const QString& reason); @@ -153,6 +154,7 @@ private slots: void onComboboxActivated(int index); void onRosterItemDoubleClicked(const QModelIndex& item); void onConversationMessage(const Shared::Message& msg); + void onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg); void onConversationResend(const QString& id); void onRequestArchive(const QString& account, const QString& jid, const QString& before); void onRosterContextMenu(const QPoint& point); diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 608faf3..02aefb4 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -58,7 +58,8 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, pasteImageAction(new QAction(tr("Paste Image"), this)), shadow(10, 1, Qt::black, this), contextMenu(new QMenu()), - currentAction(CurrentAction::none) + currentAction(CurrentAction::none), + currentMessageId() { m_ui->setupUi(this); @@ -84,11 +85,11 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, statusIcon = m_ui->statusIcon; statusLabel = m_ui->statusLabel; - connect(&ker, &KeyEnterReceiver::enterPressed, this, &Conversation::onEnterPressed); + connect(&ker, &KeyEnterReceiver::enterPressed, this, qOverload<>(&Conversation::initiateMessageSending)); connect(&ker, &KeyEnterReceiver::imagePasted, this, &Conversation::onImagePasted); - connect(m_ui->sendButton, &QPushButton::clicked, this, &Conversation::onEnterPressed); + connect(m_ui->sendButton, &QPushButton::clicked, this, qOverload<>(&Conversation::initiateMessageSending)); connect(m_ui->attachButton, &QPushButton::clicked, this, &Conversation::onAttach); - connect(m_ui->clearButton, &QPushButton::clicked, this, &Conversation::onClearButton); + connect(m_ui->clearButton, &QPushButton::clicked, this, &Conversation::clear); connect(m_ui->messageEditor->document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged, this, &Conversation::onTextEditDocSizeChanged); @@ -98,6 +99,9 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, connect(m_ui->messageEditor, &QTextEdit::customContextMenuRequested, this, &Conversation::onMessageEditorContext); connect(pasteImageAction, &QAction::triggered, this, &Conversation::onImagePasted); + connect(m_ui->currentActionBadge, &Badge::close, this, &Conversation::clear); + m_ui->currentActionBadge->setVisible(false); + //line->setAutoFillBackground(false); //if (testAttribute(Qt::WA_TranslucentBackground)) { //m_ui->scrollArea->setAutoFillBackground(false); @@ -109,9 +113,6 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, //line->setMyName(acc->getName()); initializeOverlay(); - - m_ui->currentActionBadge->setVisible(false); -// m_ui->currentActionBadge->setText(tr("Editing message...")); } Conversation::~Conversation() @@ -224,24 +225,33 @@ void Conversation::setPalResource(const QString& res) activePalResource = res; } -void Conversation::onEnterPressed() +void Conversation::initiateMessageSending() { QString body(m_ui->messageEditor->toPlainText()); if (body.size() > 0) { - m_ui->messageEditor->clear(); Shared::Message msg = createMessage(); msg.setBody(body); - emit sendMessage(msg); + initiateMessageSending(msg); } if (filesToAttach.size() > 0) { for (Badge* badge : filesToAttach) { Shared::Message msg = createMessage(); msg.setAttachPath(badge->id); element->feed->registerUpload(msg.getId()); - emit sendMessage(msg); + initiateMessageSending(msg); } - clearAttachedFiles(); + } + clear(); +} + +void Conversation::initiateMessageSending(const Shared::Message& msg) +{ + if (currentAction == CurrentAction::edit) { + emit replaceMessage(currentMessageId, msg); + currentAction = CurrentAction::none; + } else { + emit sendMessage(msg); } } @@ -348,8 +358,11 @@ void Conversation::clearAttachedFiles() filesLayout->setContentsMargins(0, 0, 0, 0); } -void Conversation::onClearButton() +void Conversation::clear() { + currentMessageId.clear(); + currentAction = CurrentAction::none; + m_ui->currentActionBadge->setVisible(false); clearAttachedFiles(); m_ui->messageEditor->clear(); } @@ -526,13 +539,12 @@ void Conversation::onMessageEditorContext(const QPoint& pos) void Conversation::onMessageEditRequested(const QString& id) { - if (currentAction == CurrentAction::edit) { - //todo; - } + clear(); try { Shared::Message msg = element->feed->getMessage(id); + currentMessageId = id; m_ui->currentActionBadge->setVisible(true); m_ui->currentActionBadge->setText(tr("Editing message...")); currentAction = CurrentAction::edit; diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index 4bccdfc..743df71 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -83,6 +83,7 @@ public: signals: void sendMessage(const Shared::Message& message); + void replaceMessage(const QString& originalId, const Shared::Message& message); void resendMessage(const QString& id); void requestArchive(const QString& before); void shown(); @@ -104,12 +105,13 @@ protected: virtual void onMessage(const Shared::Message& msg); protected slots: - void onEnterPressed(); + void initiateMessageSending(); + void initiateMessageSending(const Shared::Message& msg); void onImagePasted(); void onAttach(); void onFileSelected(); void onBadgeClose(); - void onClearButton(); + void clear(); void onTextEditDocSizeChanged(const QSizeF& size); void onAccountChanged(Models::Item* item, int row, int col); void onFeedMessage(const Shared::Message& msg); @@ -149,6 +151,7 @@ protected: ShadowOverlay shadow; QMenu* contextMenu; CurrentAction currentAction; + QString currentMessageId; private: static bool painterInitialized; From 5f6691067a18e503caf84122d59b59409bfbb5b4 Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 29 Mar 2022 19:05:24 +0300 Subject: [PATCH 031/137] minor bugfixes about message body, automatic focus and that quirk with font becomming bigger --- CHANGELOG.md | 3 +++ ui/widgets/conversation.cpp | 9 ++++++++- ui/widgets/conversation.h | 1 + ui/widgets/conversation.ui | 6 +++++- ui/widgets/messageline/messagedelegate.cpp | 1 + 5 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efb159c..83c759e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,12 @@ - build in release mode now no longer spams warnings - build now correctly installs all build plugin libs - a bug where the correction message was received, the indication was on but the text didn't actually change +- message body now doesn't intecept context menu from the whole message +- message input now doesn't increase font when you remove everything from it ### Improvements - reduced amount of places where platform specific path separator is used +- now message input is automatically focused when you open a dialog or a room ### New features - the settings are here! You con config different stuff from there diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 02aefb4..da3d187 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -94,7 +94,6 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, this, &Conversation::onTextEditDocSizeChanged); m_ui->messageEditor->installEventFilter(&ker); - m_ui->messageEditor->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_ui->messageEditor, &QTextEdit::customContextMenuRequested, this, &Conversation::onMessageEditorContext); connect(pasteImageAction, &QAction::triggered, this, &Conversation::onImagePasted); @@ -555,3 +554,11 @@ void Conversation::onMessageEditRequested(const QString& id) qDebug() << "Ignoring"; } } + +void Conversation::showEvent(QShowEvent* event) +{ + QWidget::showEvent(event); + + emit shown(); + m_ui->messageEditor->setFocus(); +} diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index 743df71..0c44bd9 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -103,6 +103,7 @@ protected: void dropEvent(QDropEvent* event) override; void initializeOverlay(); virtual void onMessage(const Shared::Message& msg); + virtual void showEvent(QShowEvent * event) override; protected slots: void initiateMessageSending(); diff --git a/ui/widgets/conversation.ui b/ui/widgets/conversation.ui index ce9ad66..1f8b483 100644 --- a/ui/widgets/conversation.ui +++ b/ui/widgets/conversation.ui @@ -402,6 +402,9 @@ 30
+ + Qt::CustomContextMenu + false @@ -424,7 +427,7 @@ background-color: transparent <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Liberation Sans'; font-size:10pt;"><br /></p></body></html> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> false @@ -447,6 +450,7 @@ p, li { white-space: pre-wrap; } Badge QFrame
ui/utils/badge.h
+ 1 diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index 22e8dcb..15a5e46 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -582,6 +582,7 @@ QLabel * MessageDelegate::getBody(const Models::FeedItem& data) const } else { result = new QLabel(); result->setFont(bodyFont); + result->setContextMenuPolicy(Qt::NoContextMenu); result->setWordWrap(true); result->setOpenExternalLinks(true); result->setTextInteractionFlags(result->textInteractionFlags() | Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse); From 1fcd403dbaa841ceb2f3f70bfad439440810bd30 Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 1 Apr 2022 00:32:22 +0300 Subject: [PATCH 032/137] testing, solved unhandled exception, conditions to restrict old message to be edited, license un some files that used to miss them --- core/handlers/messagehandler.cpp | 6 +++++- core/networkaccess.cpp | 2 +- .../wrappers/kwallet.cpp | 18 ++++++++++++++++++ plugins/colorschemetools.cpp | 18 ++++++++++++++++++ plugins/openfilemanagerwindowjob.cpp | 18 ++++++++++++++++++ shared/exception.cpp | 3 ++- shared/pathcheck.cpp | 18 ++++++++++++++++++ shared/pathcheck.h | 18 ++++++++++++++++++ ui/widgets/conversation.cpp | 3 ++- ui/widgets/settings/pageappearance.cpp | 18 ++++++++++++++++++ ui/widgets/settings/pageappearance.h | 18 ++++++++++++++++++ ui/widgets/settings/pagegeneral.cpp | 18 ++++++++++++++++++ ui/widgets/settings/pagegeneral.h | 18 ++++++++++++++++++ ui/widgets/settings/settings.cpp | 18 ++++++++++++++++++ ui/widgets/settings/settings.h | 18 ++++++++++++++++++ ui/widgets/settings/settingslist.cpp | 18 ++++++++++++++++++ ui/widgets/settings/settingslist.h | 18 ++++++++++++++++++ 17 files changed, 244 insertions(+), 4 deletions(-) diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index 559bee3..0555873 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -233,7 +233,11 @@ std::tuple Core::MessageHandler::getOriginalPendingMessa std::map::const_iterator itrC = pendingCorrectionMessages.find(id); if (itrC != pendingCorrectionMessages.end()) { - std::get<1>(result) = itrC->second; + if (itrC->second.size() > 0) { + std::get<1>(result) = itrC->second; + } else { + std::get<1>(result) = itr->first; + } pendingCorrectionMessages.erase(itrC); } else { std::get<1>(result) = itr->first; diff --git a/core/networkaccess.cpp b/core/networkaccess.cpp index 7c55e19..22bb7a2 100644 --- a/core/networkaccess.cpp +++ b/core/networkaccess.cpp @@ -443,7 +443,7 @@ QString Core::NetworkAccess::getFileRemoteUrl(const QString& path) try { p = storage.getUrl(p); } catch (const Archive::NotFound& err) { - + p = ""; } catch (...) { throw; } diff --git a/core/passwordStorageEngines/wrappers/kwallet.cpp b/core/passwordStorageEngines/wrappers/kwallet.cpp index f5e7cb5..d899985 100644 --- a/core/passwordStorageEngines/wrappers/kwallet.cpp +++ b/core/passwordStorageEngines/wrappers/kwallet.cpp @@ -1,3 +1,21 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 extern "C" KWallet::Wallet* openWallet(const QString &name, WId w, KWallet::Wallet::OpenType ot = KWallet::Wallet::Synchronous) { diff --git a/plugins/colorschemetools.cpp b/plugins/colorschemetools.cpp index 0288b28..ea2c23e 100644 --- a/plugins/colorschemetools.cpp +++ b/plugins/colorschemetools.cpp @@ -1,3 +1,21 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 #include #include diff --git a/plugins/openfilemanagerwindowjob.cpp b/plugins/openfilemanagerwindowjob.cpp index 904fbcf..4335410 100644 --- a/plugins/openfilemanagerwindowjob.cpp +++ b/plugins/openfilemanagerwindowjob.cpp @@ -1,3 +1,21 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 #include #include diff --git a/shared/exception.cpp b/shared/exception.cpp index 342593c..3dee9b3 100644 --- a/shared/exception.cpp +++ b/shared/exception.cpp @@ -28,5 +28,6 @@ Utils::Exception::~Exception() const char* Utils::Exception::what() const noexcept( true ) { - return getMessage().c_str(); + std::string* msg = new std::string(getMessage()); + return msg->c_str(); } diff --git a/shared/pathcheck.cpp b/shared/pathcheck.cpp index 1929387..c32f96c 100644 --- a/shared/pathcheck.cpp +++ b/shared/pathcheck.cpp @@ -1,3 +1,21 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "pathcheck.h" QRegularExpression squawk("^squawk:\\/\\/"); diff --git a/shared/pathcheck.h b/shared/pathcheck.h index 62dcaeb..3ca612b 100644 --- a/shared/pathcheck.h +++ b/shared/pathcheck.h @@ -1,3 +1,21 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 PATHCHECK_H #define PATHCHECK_H diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index da3d187..ab5f0c5 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -513,7 +513,8 @@ void Conversation::onFeedContext(const QPoint& pos) }); } - if (item->getOutgoing()) { + //the only mandatory condition - is for the message to be outgoing, the rest is just a good intention on the server + if (item->getOutgoing() && index.row() < 100 && item->getTime().daysTo(QDateTime::currentDateTimeUtc()) < 20) { showMenu = true; QAction* edit = contextMenu->addAction(Shared::icon("edit-rename"), tr("Edit")); connect(edit, &QAction::triggered, this, std::bind(&Conversation::onMessageEditRequested, this, id)); diff --git a/ui/widgets/settings/pageappearance.cpp b/ui/widgets/settings/pageappearance.cpp index f2bf53e..64d6de4 100644 --- a/ui/widgets/settings/pageappearance.cpp +++ b/ui/widgets/settings/pageappearance.cpp @@ -1,3 +1,21 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "pageappearance.h" #include "ui_pageappearance.h" diff --git a/ui/widgets/settings/pageappearance.h b/ui/widgets/settings/pageappearance.h index 80efd85..c182ea2 100644 --- a/ui/widgets/settings/pageappearance.h +++ b/ui/widgets/settings/pageappearance.h @@ -1,3 +1,21 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 PAGEAPPEARANCE_H #define PAGEAPPEARANCE_H diff --git a/ui/widgets/settings/pagegeneral.cpp b/ui/widgets/settings/pagegeneral.cpp index a546bd0..9ed46a2 100644 --- a/ui/widgets/settings/pagegeneral.cpp +++ b/ui/widgets/settings/pagegeneral.cpp @@ -1,3 +1,21 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "pagegeneral.h" #include "ui_pagegeneral.h" diff --git a/ui/widgets/settings/pagegeneral.h b/ui/widgets/settings/pagegeneral.h index ec00bba..7f58d71 100644 --- a/ui/widgets/settings/pagegeneral.h +++ b/ui/widgets/settings/pagegeneral.h @@ -1,3 +1,21 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 PAGEGENERAL_H #define PAGEGENERAL_H diff --git a/ui/widgets/settings/settings.cpp b/ui/widgets/settings/settings.cpp index 27401bb..cf5e905 100644 --- a/ui/widgets/settings/settings.cpp +++ b/ui/widgets/settings/settings.cpp @@ -1,3 +1,21 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "settings.h" #include "ui_settings.h" diff --git a/ui/widgets/settings/settings.h b/ui/widgets/settings/settings.h index 5a6b37c..689e0ce 100644 --- a/ui/widgets/settings/settings.h +++ b/ui/widgets/settings/settings.h @@ -1,3 +1,21 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 SETTINGS_H #define SETTINGS_H diff --git a/ui/widgets/settings/settingslist.cpp b/ui/widgets/settings/settingslist.cpp index 3a5e2cb..ee2e3ed 100644 --- a/ui/widgets/settings/settingslist.cpp +++ b/ui/widgets/settings/settingslist.cpp @@ -1,3 +1,21 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "settingslist.h" SettingsList::SettingsList(QWidget* parent): diff --git a/ui/widgets/settings/settingslist.h b/ui/widgets/settings/settingslist.h index a51fc3a..64c9d57 100644 --- a/ui/widgets/settings/settingslist.h +++ b/ui/widgets/settings/settingslist.h @@ -1,3 +1,21 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 UI_SETTINGSLIST_H #define UI_SETTINGSLIST_H From 62f02c18d785582e493a8aedd9a46413ab14212a Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 2 Apr 2022 15:34:36 +0300 Subject: [PATCH 033/137] now you can't edit messages with attachments: no other client actually allowes that, and if I edit they don't handle it properly anyway --- ui/widgets/conversation.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index ab5f0c5..4e5e007 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -513,8 +513,9 @@ void Conversation::onFeedContext(const QPoint& pos) }); } + bool hasAttach = item->getAttachPath() > 0 || item->getOutOfBandUrl() > 0; //the only mandatory condition - is for the message to be outgoing, the rest is just a good intention on the server - if (item->getOutgoing() && index.row() < 100 && item->getTime().daysTo(QDateTime::currentDateTimeUtc()) < 20) { + if (item->getOutgoing() && !hasAttach && index.row() < 100 && item->getTime().daysTo(QDateTime::currentDateTimeUtc()) < 20) { showMenu = true; QAction* edit = contextMenu->addAction(Shared::icon("edit-rename"), tr("Edit")); connect(edit, &QAction::triggered, this, std::bind(&Conversation::onMessageEditRequested, this, id)); @@ -549,6 +550,10 @@ void Conversation::onMessageEditRequested(const QString& id) m_ui->currentActionBadge->setText(tr("Editing message...")); currentAction = CurrentAction::edit; m_ui->messageEditor->setText(msg.getBody()); + QString path = msg.getAttachPath(); + if (path.size() > 0) { + addAttachedFile(path); + } } catch (const Models::MessageFeed::NotFound& e) { qDebug() << "The message requested to be edited was not found" << e.getMessage().c_str(); From 4786388822f74f225e993ac1d0f86cb2b5df9f3b Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 2 Apr 2022 15:53:23 +0300 Subject: [PATCH 034/137] 0.2.1 --- CHANGELOG.md | 7 ++++--- packaging/Archlinux/PKGBUILD | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83c759e..a6445ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Squawk 0.2.1 (UNRELEASED) +## Squawk 0.2.1 (Apr 02, 2022) ### Bug fixes - build in release mode now no longer spams warnings - build now correctly installs all build plugin libs @@ -11,12 +11,13 @@ ### Improvements - reduced amount of places where platform specific path separator is used - now message input is automatically focused when you open a dialog or a room +- what() method on unhandled exception now actually tells what happened ### New features - the settings are here! You con config different stuff from there - now it's possible to set up different qt styles from settings -- if you have KConfig nad KConfigWidgets packages installed - you can chose from global color schemes -- it's possible now to chose a folder where squawk is going to store downloaded files +- if you have KConfig nad KConfigWidgets packages installed - you can choose from global color schemes +- it's possible now to choose a folder where squawk is going to store downloaded files - now you can correct your message ## Squawk 0.2.0 (Jan 10, 2022) diff --git a/packaging/Archlinux/PKGBUILD b/packaging/Archlinux/PKGBUILD index a8da388..899f058 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Yury Gubich pkgname=squawk -pkgver=0.2.0 +pkgver=0.2.1 pkgrel=1 pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)" arch=('i686' 'x86_64') @@ -14,7 +14,7 @@ optdepends=('kwallet: secure password storage (requires rebuild)' 'kio: better show in folder action (requires rebuild)') source=("$pkgname-$pkgver.tar.gz") -sha256sums=('8e93d3dbe1fc35cfecb7783af409c6a264244d11609b2241d4fe77d43d068419') +sha256sums=('c00dad1e441601acabb5200dc394f53abfc9876f3902a7dd4ad2fee3232ee84d') build() { cd "$srcdir/squawk" cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release @@ -22,5 +22,5 @@ build() { } package() { cd "$srcdir/squawk" - DESTDIR="$pkgdir/" cmake --build . --target install + DESTDIR="$pkgdir/" cmake --build . --target install } From 4baa3bccbf80d4b861661d4986b94a4114f9edba Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 2 Apr 2022 16:09:11 +0300 Subject: [PATCH 035/137] new screenshot --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 486d4fe..5845c46 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![AUR version](https://img.shields.io/aur/version/squawk?style=flat-square)](https://aur.archlinux.org/packages/squawk/) [![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me) -![Squawk screenshot](https://macaw.me/images/squawk/0.2.0.png) +![Squawk screenshot](https://macaw.me/images/squawk/0.2.1.png) ### Prerequisites From 27377e0ec51fad161165649bd0fe92d23f25bbd0 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 3 Apr 2022 23:53:46 +0300 Subject: [PATCH 036/137] first attempt to make About window --- CHANGELOG.md | 8 ++ CMakeLists.txt | 2 +- core/main.cpp | 18 +--- ui/squawk.cpp | 98 ++++++++++++-------- ui/squawk.h | 4 + ui/squawk.ui | 15 ++- ui/widgets/CMakeLists.txt | 3 + ui/widgets/about.cpp | 29 ++++++ ui/widgets/about.h | 43 +++++++++ ui/widgets/about.ui | 186 ++++++++++++++++++++++++++++++++++++++ 10 files changed, 354 insertions(+), 52 deletions(-) create mode 100644 ui/widgets/about.cpp create mode 100644 ui/widgets/about.h create mode 100644 ui/widgets/about.ui diff --git a/CHANGELOG.md b/CHANGELOG.md index a6445ec..f563c85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Squawk 0.2.2 (UNRELEASED) +### Bug fixes + +### Improvements + +### New features + + ## Squawk 0.2.1 (Apr 02, 2022) ### Bug fixes - build in release mode now no longer spams warnings diff --git a/CMakeLists.txt b/CMakeLists.txt index 717cf91..7d0ee7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.4) -project(squawk VERSION 0.2.1 LANGUAGES CXX) +project(squawk VERSION 0.2.2 LANGUAGES CXX) cmake_policy(SET CMP0076 NEW) cmake_policy(SET CMP0079 NEW) diff --git a/core/main.cpp b/core/main.cpp index f842c80..7d3c3ab 100644 --- a/core/main.cpp +++ b/core/main.cpp @@ -45,19 +45,11 @@ int main(int argc, char *argv[]) QApplication app(argc, argv); SignalCatcher sc(&app); -#ifdef Q_OS_WIN - // Windows need an organization name for QSettings to work - // https://doc.qt.io/qt-5/qsettings.html#basic-usage - { - const QString& orgName = QApplication::organizationName(); - if (orgName.isNull() || orgName.isEmpty()) { - QApplication::setOrganizationName("squawk"); - } - } -#endif + QApplication::setApplicationName("squawk"); + QApplication::setOrganizationName("macaw.me"); QApplication::setApplicationDisplayName("Squawk"); - QApplication::setApplicationVersion("0.2.1"); + QApplication::setApplicationVersion("0.2.2"); QTranslator qtTranslator; qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); @@ -199,8 +191,8 @@ int main(int argc, char *argv[]) if (coreThread->isRunning()) { //coreThread->wait(); - //todo if I uncomment that, the app will no quit if it has reconnected at least once - //it feels like a symptom of something badly desinged in the core coreThread + //todo if I uncomment that, the app will not quit if it has reconnected at least once + //it feels like a symptom of something badly desinged in the core thread //need to investigate; } diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 3ebb6a5..4594c01 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -24,16 +24,17 @@ Squawk::Squawk(QWidget *parent) : QMainWindow(parent), m_ui(new Ui::Squawk), - accounts(0), - preferences(0), + accounts(nullptr), + preferences(nullptr), + about(nullptr), rosterModel(), conversations(), contextMenu(new QMenu()), dbus("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus()), vCards(), requestedAccountsForPasswords(), - prompt(0), - currentConversation(0), + prompt(nullptr), + currentConversation(nullptr), restoreSelection(), needToRestore(false) { @@ -72,6 +73,7 @@ Squawk::Squawk(QWidget *parent) : connect(&rosterModel, &Models::Roster::fileDownloadRequest, this, &Squawk::fileDownloadRequest); connect(&rosterModel, &Models::Roster::localPathInvalid, this, &Squawk::localPathInvalid); connect(contextMenu, &QMenu::aboutToHide, this, &Squawk::onContextAboutToHide); + connect(m_ui->actionAboutSquawk, &QAction::triggered, this, &Squawk::onAboutSquawkCalled); //m_ui->mainToolBar->addWidget(m_ui->comboBox); if (testAttribute(Qt::WA_TranslucentBackground)) { @@ -101,7 +103,7 @@ Squawk::~Squawk() { void Squawk::onAccounts() { - if (accounts == 0) { + if (accounts == nullptr) { accounts = new Accounts(rosterModel.accountsModel); accounts->setAttribute(Qt::WA_DeleteOnClose); connect(accounts, &Accounts::destroyed, this, &Squawk::onAccountsClosed); @@ -121,7 +123,7 @@ void Squawk::onAccounts() void Squawk::onPreferences() { - if (preferences == 0) { + if (preferences == nullptr) { preferences = new Settings(); preferences->setAttribute(Qt::WA_DeleteOnClose); connect(preferences, &Settings::destroyed, this, &Squawk::onPreferencesClosed); @@ -189,12 +191,15 @@ void Squawk::onJoinConferenceAccepted() void Squawk::closeEvent(QCloseEvent* event) { - if (accounts != 0) { + if (accounts != nullptr) { accounts->close(); } - if (preferences != 0) { + if (preferences != nullptr) { preferences->close(); } + if (about != nullptr) { + about->close(); + } for (Conversations::const_iterator itr = conversations.begin(), end = conversations.end(); itr != end; ++itr) { disconnect(itr->second, &Conversation::destroyed, this, &Squawk::onConversationClosed); @@ -214,12 +219,12 @@ void Squawk::closeEvent(QCloseEvent* event) void Squawk::onAccountsClosed() { - accounts = 0; + accounts = nullptr; } void Squawk::onPreferencesClosed() { - preferences = 0; + preferences = nullptr; } void Squawk::newAccount(const QMap& account) @@ -342,10 +347,10 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) if (node->type == Models::Item::reference) { node = static_cast(node)->dereference(); } - Models::Contact* contact = 0; - Models::Room* room = 0; + Models::Contact* contact = nullptr; + Models::Room* room = nullptr; QString res; - Models::Roster::ElId* id = 0; + Models::Roster::ElId* id = nullptr; switch (node->type) { case Models::Item::contact: contact = static_cast(node); @@ -365,17 +370,17 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) break; } - if (id != 0) { + if (id != nullptr) { Conversations::const_iterator itr = conversations.find(*id); Models::Account* acc = rosterModel.getAccount(id->account); - Conversation* conv = 0; + Conversation* conv = nullptr; bool created = false; if (itr != conversations.end()) { conv = itr->second; - } else if (contact != 0) { + } else if (contact != nullptr) { created = true; conv = new Chat(acc, contact); - } else if (room != 0) { + } else if (room != nullptr) { created = true; conv = new Room(acc, room); @@ -384,7 +389,7 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) } } - if (conv != 0) { + if (conv != nullptr) { if (created) { conv->setAttribute(Qt::WA_DeleteOnClose); subscribeConversation(conv); @@ -543,9 +548,9 @@ void Squawk::removeAccount(const QString& account) } } - if (currentConversation != 0 && currentConversation->getAccount() == account) { + if (currentConversation != nullptr && currentConversation->getAccount() == account) { currentConversation->deleteLater(); - currentConversation = 0; + currentConversation = nullptr; m_ui->filler->show(); } @@ -710,7 +715,7 @@ void Squawk::onRosterContextMenu(const QPoint& point) connect(unsub, &QAction::triggered, [this, id]() { emit setRoomAutoJoin(id.account, id.name, false); if (conversations.find(id) == conversations.end() - && (currentConversation == 0 || currentConversation->getId() != id) + && (currentConversation == nullptr || currentConversation->getId() != id) ) { //to leave the room if it's not opened in a conversation window emit setRoomJoined(id.account, id.name, false); } @@ -721,7 +726,7 @@ void Squawk::onRosterContextMenu(const QPoint& point) connect(unsub, &QAction::triggered, [this, id]() { emit setRoomAutoJoin(id.account, id.name, true); if (conversations.find(id) == conversations.end() - && (currentConversation == 0 || currentConversation->getId() != id) + && (currentConversation == nullptr || currentConversation->getId() != id) ) { //to join the room if it's not already joined emit setRoomJoined(id.account, id.name, true); } @@ -928,7 +933,7 @@ void Squawk::requestPassword(const QString& account) void Squawk::checkNextAccountForPassword() { - if (prompt == 0 && requestedAccountsForPasswords.size() > 0) { + if (prompt == nullptr && requestedAccountsForPasswords.size() > 0) { prompt = new QInputDialog(this); QString accName = requestedAccountsForPasswords.front(); connect(prompt, &QDialog::accepted, this, &Squawk::onPasswordPromptAccepted); @@ -951,7 +956,7 @@ void Squawk::onPasswordPromptAccepted() void Squawk::onPasswordPromptDone() { prompt->deleteLater(); - prompt = 0; + prompt = nullptr; requestedAccountsForPasswords.pop_front(); checkNextAccountForPassword(); } @@ -986,10 +991,10 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn if (node->type == Models::Item::reference) { node = static_cast(node)->dereference(); } - Models::Contact* contact = 0; - Models::Room* room = 0; + Models::Contact* contact = nullptr; + Models::Room* room = nullptr; QString res; - Models::Roster::ElId* id = 0; + Models::Roster::ElId* id = nullptr; bool hasContext = true; switch (node->type) { case Models::Item::contact: @@ -1018,7 +1023,7 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn } if (hasContext && QGuiApplication::mouseButtons() & Qt::RightButton) { - if (id != 0) { + if (id != nullptr) { delete id; } needToRestore = true; @@ -1026,10 +1031,10 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn return; } - if (id != 0) { - if (currentConversation != 0) { + if (id != nullptr) { + if (currentConversation != nullptr) { if (currentConversation->getId() == *id) { - if (contact != 0) { + if (contact != nullptr) { currentConversation->setPalResource(res); } return; @@ -1041,9 +1046,9 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn } Models::Account* acc = rosterModel.getAccount(id->account); - if (contact != 0) { + if (contact != nullptr) { currentConversation = new Chat(acc, contact); - } else if (room != 0) { + } else if (room != nullptr) { currentConversation = new Room(acc, room); if (!room->getJoined()) { @@ -1064,16 +1069,16 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn delete id; } else { - if (currentConversation != 0) { + if (currentConversation != nullptr) { currentConversation->deleteLater(); - currentConversation = 0; + currentConversation = nullptr; m_ui->filler->show(); } } } else { - if (currentConversation != 0) { + if (currentConversation != nullptr) { currentConversation->deleteLater(); - currentConversation = 0; + currentConversation = nullptr; m_ui->filler->show(); } } @@ -1086,3 +1091,22 @@ void Squawk::onContextAboutToHide() m_ui->roster->selectionModel()->setCurrentIndex(restoreSelection, QItemSelectionModel::ClearAndSelect); } } + +void Squawk::onAboutSquawkCalled() +{ + if (about == nullptr) { + about = new About(); + about->setAttribute(Qt::WA_DeleteOnClose); + connect(about, &Settings::destroyed, this, &Squawk::onAboutSquawkClosed); + about->show(); + } else { + about->raise(); + about->activateWindow(); + about->show(); + } +} + +void Squawk::onAboutSquawkClosed() +{ + about = nullptr; +} diff --git a/ui/squawk.h b/ui/squawk.h index 7bd2e10..95c5ce3 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -39,6 +39,7 @@ #include "models/roster.h" #include "widgets/vcard/vcard.h" #include "widgets/settings/settings.h" +#include "widgets/about.h" #include "shared/shared.h" @@ -120,6 +121,7 @@ private: Accounts* accounts; Settings* preferences; + About* about; Models::Roster rosterModel; Conversations conversations; QMenu* contextMenu; @@ -163,6 +165,8 @@ private slots: void onPasswordPromptRejected(); void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous); void onContextAboutToHide(); + void onAboutSquawkCalled(); + void onAboutSquawkClosed(); void onUnnoticedMessage(const QString& account, const Shared::Message& msg); diff --git a/ui/squawk.ui b/ui/squawk.ui index 840dfee..a8b0730 100644 --- a/ui/squawk.ui +++ b/ui/squawk.ui @@ -201,8 +201,15 @@ + + + Help + + + + @@ -248,12 +255,18 @@ - + + .. Preferences + + + About Squawk + + diff --git a/ui/widgets/CMakeLists.txt b/ui/widgets/CMakeLists.txt index f3a2afe..7ba83d2 100644 --- a/ui/widgets/CMakeLists.txt +++ b/ui/widgets/CMakeLists.txt @@ -18,6 +18,9 @@ target_sources(squawk PRIVATE newcontact.ui room.cpp room.h + about.cpp + about.h + about.ui ) add_subdirectory(vcard) diff --git a/ui/widgets/about.cpp b/ui/widgets/about.cpp new file mode 100644 index 0000000..4631065 --- /dev/null +++ b/ui/widgets/about.cpp @@ -0,0 +1,29 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "about.h" +#include "ui_about.h" + +About::About(QWidget* parent): + QWidget(parent), + m_ui(new Ui::About) +{ + m_ui->setupUi(this); + m_ui->versionValue->setText(QApplication::applicationVersion()); + setWindowFlag(Qt::Tool); +} + +About::~About() = default; diff --git a/ui/widgets/about.h b/ui/widgets/about.h new file mode 100644 index 0000000..89d879d --- /dev/null +++ b/ui/widgets/about.h @@ -0,0 +1,43 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 ABOUT_H +#define ABOUT_H + +#include +#include +#include + +namespace Ui +{ +class About; +} + +/** + * @todo write docs + */ +class About : public QWidget +{ + Q_OBJECT +public: + About(QWidget* parent = nullptr); + ~About(); + +private: + QScopedPointer m_ui; +}; + +#endif // ABOUT_H diff --git a/ui/widgets/about.ui b/ui/widgets/about.ui new file mode 100644 index 0000000..ab54df5 --- /dev/null +++ b/ui/widgets/about.ui @@ -0,0 +1,186 @@ + + + About + + + + 0 + 0 + 334 + 321 + + + + About Squawk + + + + 0 + + + + + + 12 + + + + Squawk + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 0 + + + + About + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + XMPP (jabber) messenger + + + + + + + (c) 2019 - 2022, Yury Gubich + + + + + + + <a href="https://git.macaw.me/blue/squawk">Project site</a> + + + Qt::RichText + + + true + + + + + + + <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">License: GNU General Public License version 3</a> + + + Qt::RichText + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + 0.0.0 + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Version + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + 50 + 50 + + + + + + + :/images/logo.svg + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + + + From 9f746d203b6af51431587c38f81870b83cc3081f Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 4 Apr 2022 23:49:01 +0300 Subject: [PATCH 037/137] new tab in About: components --- ui/widgets/about.cpp | 15 +- ui/widgets/about.h | 1 + ui/widgets/about.ui | 317 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 289 insertions(+), 44 deletions(-) diff --git a/ui/widgets/about.cpp b/ui/widgets/about.cpp index 4631065..3366284 100644 --- a/ui/widgets/about.cpp +++ b/ui/widgets/about.cpp @@ -16,13 +16,26 @@ #include "about.h" #include "ui_about.h" +#include + +static const std::string QXMPP_VERSION_PATCH(std::to_string(QXMPP_VERSION & 0xff)); +static const std::string QXMPP_VERSION_MINOR(std::to_string((QXMPP_VERSION & 0xff00) >> 8)); +static const std::string QXMPP_VERSION_MAJOR(std::to_string(QXMPP_VERSION >> 16)); +static const QString QXMPP_VERSION_STRING = QString::fromStdString(QXMPP_VERSION_MAJOR + "." + QXMPP_VERSION_MINOR + "." + QXMPP_VERSION_PATCH); About::About(QWidget* parent): QWidget(parent), - m_ui(new Ui::About) + m_ui(new Ui::About), + license(nullptr) { m_ui->setupUi(this); m_ui->versionValue->setText(QApplication::applicationVersion()); + m_ui->qtVersionValue->setText(qVersion()); + m_ui->qtBuiltAgainstVersion->setText(tr("(built against %1)").arg(QT_VERSION_STR)); + + m_ui->qxmppVersionValue->setText(QXmppVersion()); + m_ui->qxmppBuiltAgainstVersion->setText(tr("(built against %1)").arg(QXMPP_VERSION_STRING)); + setWindowFlag(Qt::Tool); } diff --git a/ui/widgets/about.h b/ui/widgets/about.h index 89d879d..e28b362 100644 --- a/ui/widgets/about.h +++ b/ui/widgets/about.h @@ -38,6 +38,7 @@ public: private: QScopedPointer m_ui; + QWidget* license; }; #endif // ABOUT_H diff --git a/ui/widgets/about.ui b/ui/widgets/about.ui index ab54df5..58c136b 100644 --- a/ui/widgets/about.ui +++ b/ui/widgets/about.ui @@ -6,8 +6,8 @@ 0 0 - 334 - 321 + 354 + 349
@@ -17,6 +17,22 @@ 0 + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + @@ -32,6 +48,28 @@ + + + + + 50 + 50 + + + + + + + :/images/logo.svg + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + @@ -120,31 +158,239 @@ + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + true + + + Components + + + + + 0 + 0 + 334 + 240 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 2 + + + 0 + + + + + + true + + + + Version + + + + + + + + true + + + + 0.0.0 + + + + + + + + true + + + + (built against 0.0.0) + + + + + + + + 75 + true + + + + Qt + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <a href="https://www.qt.io/">www.qt.io</a> + + + true + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 2 + + + 0 + + + + + + true + + + + Version + + + + + + + + true + + + + 0.0.0 + + + + + + + + true + + + + (built against 0.0.0) + + + + + + + + 75 + true + + + + QXmpp + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <a href="https://github.com/qxmpp-project/qxmpp">github.com/qxmpp-project/qxmpp</a> + + + true + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + - - - - 0.0.0 - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - @@ -155,25 +401,10 @@ - - - - - 50 - 50 - - + + - - - - :/images/logo.svg - - - true - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + 0.0.0 From 1b66fda3181b38bf4864fb1591291e3391aa03fb Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 5 Apr 2022 22:00:56 +0300 Subject: [PATCH 038/137] License is now can be viewed locally, some organization name packaging issies --- CMakeLists.txt | 2 ++ LICENSE.md | 6 ++-- core/main.cpp | 2 +- translations/CMakeLists.txt | 2 +- ui/squawk.cpp | 3 +- ui/widgets/about.cpp | 68 ++++++++++++++++++++++++++++++++++++- ui/widgets/about.h | 7 ++++ 7 files changed, 82 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d0ee7f..85aa98a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,6 +159,8 @@ add_subdirectory(ui) # Install the executable install(TARGETS squawk DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(FILES README.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk) +install(FILES LICENSE.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk) if (CMAKE_BUILD_TYPE STREQUAL "Release") if (APPLE) diff --git a/LICENSE.md b/LICENSE.md index 85c7c69..32b38d4 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -595,17 +595,17 @@ pointer to where the full notice is found. Copyright (C) - + 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 . diff --git a/core/main.cpp b/core/main.cpp index 7d3c3ab..4fbb1f7 100644 --- a/core/main.cpp +++ b/core/main.cpp @@ -54,7 +54,7 @@ int main(int argc, char *argv[]) QTranslator qtTranslator; qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); app.installTranslator(&qtTranslator); - + QTranslator myappTranslator; QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); bool found = false; diff --git a/translations/CMakeLists.txt b/translations/CMakeLists.txt index 86d2a8c..eee4f98 100644 --- a/translations/CMakeLists.txt +++ b/translations/CMakeLists.txt @@ -6,6 +6,6 @@ set(TS_FILES ) qt5_add_translation(QM_FILES ${TS_FILES}) add_custom_target(translations ALL DEPENDS ${QM_FILES}) -install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/squawk/l10n) +install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk/l10n) add_dependencies(${CMAKE_PROJECT_NAME} translations) diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 4594c01..4c7320b 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -1098,12 +1098,11 @@ void Squawk::onAboutSquawkCalled() about = new About(); about->setAttribute(Qt::WA_DeleteOnClose); connect(about, &Settings::destroyed, this, &Squawk::onAboutSquawkClosed); - about->show(); } else { about->raise(); about->activateWindow(); - about->show(); } + about->show(); } void Squawk::onAboutSquawkClosed() diff --git a/ui/widgets/about.cpp b/ui/widgets/about.cpp index 3366284..3782a94 100644 --- a/ui/widgets/about.cpp +++ b/ui/widgets/about.cpp @@ -17,6 +17,7 @@ #include "about.h" #include "ui_about.h" #include +#include static const std::string QXMPP_VERSION_PATCH(std::to_string(QXMPP_VERSION & 0xff)); static const std::string QXMPP_VERSION_MINOR(std::to_string((QXMPP_VERSION & 0xff00) >> 8)); @@ -37,6 +38,71 @@ About::About(QWidget* parent): m_ui->qxmppBuiltAgainstVersion->setText(tr("(built against %1)").arg(QXMPP_VERSION_STRING)); setWindowFlag(Qt::Tool); + + connect(m_ui->licenceLink, &QLabel::linkActivated, this, &About::onLicenseActivated); } -About::~About() = default; +About::~About() { + if (license != nullptr) { + license->deleteLater(); + } +}; + +void About::onLicenseActivated() +{ + if (license == nullptr) { + QFile file; + bool found = false; + QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); + for (const QString& path : shares) { + file.setFileName(path + "/LICENSE.md"); + + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { + found = true; + break; + } + } + if (!found) { + qDebug() << "couldn't read license file, bailing"; + return; + } + + license = new QWidget(); + license->setWindowTitle(tr("License")); + QVBoxLayout* layout = new QVBoxLayout(license); + QLabel* text = new QLabel(license); + QScrollArea* area = new QScrollArea(license); + text->setTextFormat(Qt::MarkdownText); + text->setWordWrap(true); + text->setOpenExternalLinks(true); + text->setMargin(5); + area->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + + layout->addWidget(area); + license->setAttribute(Qt::WA_DeleteOnClose); + connect(license, &QWidget::destroyed, this, &About::onLicenseClosed); + + QTextStream in(&file); + QString line; + QString licenseText(""); + while (!in.atEnd()) { + line = in.readLine(); + licenseText.append(line + "\n"); + } + text->setText(licenseText); + file.close(); + + area->setWidget(text); + + } else { + license->raise(); + license->activateWindow(); + } + + license->show(); +} + +void About::onLicenseClosed() +{ + license = nullptr; +} diff --git a/ui/widgets/about.h b/ui/widgets/about.h index e28b362..1506b7f 100644 --- a/ui/widgets/about.h +++ b/ui/widgets/about.h @@ -20,6 +20,9 @@ #include #include #include +#include +#include +#include namespace Ui { @@ -36,6 +39,10 @@ public: About(QWidget* parent = nullptr); ~About(); +protected slots: + void onLicenseActivated(); + void onLicenseClosed(); + private: QScopedPointer m_ui; QWidget* license; From 82d54ba4df869981559ba835c836c62112f8f642 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 7 Apr 2022 18:26:43 +0300 Subject: [PATCH 039/137] Report bugs tab and thanks to tab in about widget --- ui/widgets/about.ui | 271 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 267 insertions(+), 4 deletions(-) diff --git a/ui/widgets/about.ui b/ui/widgets/about.ui index 58c136b..e7b9ce4 100644 --- a/ui/widgets/about.ui +++ b/ui/widgets/about.ui @@ -6,8 +6,8 @@ 0 0 - 354 - 349 + 375 + 290 @@ -88,6 +88,9 @@ 0 + + false + About @@ -176,8 +179,8 @@ 0 0 - 334 - 240 + 355 + 181 @@ -389,6 +392,266 @@ + + + Report Bugs + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Please report any bug you find! +To report bugs you can use: + + + + + + + <a href="https://git.macaw.me/blue/squawk/issues">Project bug tracker</> + + + true + + + + + + + XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) + + + true + + + + + + + E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + true + + + Thanks To + + + + + 0 + 0 + 355 + 181 + + + + + 10 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 75 + true + + + + Vae + + + + + + + + true + + + + Major refactoring, bug fixes, constructive criticism + + + true + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 75 + true + + + + Shunf4 + + + + + + + + true + + + + Major refactoring, bug fixes, build adaptations for Windows and MacOS + + + true + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 75 + true + + + + Bruno F. Fontes + + + + + + + + true + + + + Brazilian Portuguese translation + + + true + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + From 69e0c88d8d278925c005418fca0b2caa33ce0405 Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 8 Apr 2022 19:18:15 +0300 Subject: [PATCH 040/137] account refactoring, pep support discovery started --- core/CMakeLists.txt | 3 +- core/account.cpp | 521 +++++------------- core/account.h | 19 +- ...apterFuctions.cpp => adapterfunctions.cpp} | 8 +- core/adapterfunctions.h | 32 ++ core/handlers/CMakeLists.txt | 2 + core/handlers/messagehandler.cpp | 2 +- core/handlers/rosterhandler.cpp | 15 +- core/handlers/rosterhandler.h | 2 + core/handlers/vcardhandler.cpp | 312 +++++++++++ core/handlers/vcardhandler.h | 65 +++ core/rosteritem.h | 1 + 12 files changed, 588 insertions(+), 394 deletions(-) rename core/{adapterFuctions.cpp => adapterfunctions.cpp} (98%) create mode 100644 core/adapterfunctions.h create mode 100644 core/handlers/vcardhandler.cpp create mode 100644 core/handlers/vcardhandler.h diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 9369cb7..8b6fa69 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -6,7 +6,8 @@ endif(WIN32) target_sources(squawk PRIVATE account.cpp account.h - adapterFuctions.cpp + adapterfunctions.cpp + adapterfunctions.h archive.cpp archive.h conference.cpp diff --git a/core/account.cpp b/core/account.cpp index 91d0f2b..3d782cd 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -41,13 +41,12 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& rcpm(new QXmppMessageReceiptManager()), reconnectScheduled(false), reconnectTimer(new QTimer), - avatarHash(), - avatarType(), - ownVCardRequestInProgress(false), network(p_net), passwordType(Shared::AccountPassword::plain), + pepSupport(false), mh(new MessageHandler(this)), - rh(new RosterHandler(this)) + rh(new RosterHandler(this)), + vh(new VCardHandler(this)) { config.setUser(p_login); config.setDomain(p_server); @@ -73,10 +72,6 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& client.addExtension(mm); client.addExtension(bm); - - QObject::connect(vm, &QXmppVCardManager::vCardReceived, this, &Account::onVCardReceived); - //QObject::connect(&vm, &QXmppVCardManager::clientVCardReceived, this, &Account::onOwnVCardReceived); //for some reason it doesn't work, launching from common handler - client.addExtension(um); QObject::connect(um, &QXmppUploadRequestManager::slotReceived, mh, &MessageHandler::onUploadSlotReceived); QObject::connect(um, &QXmppUploadRequestManager::requestFailed, mh, &MessageHandler::onUploadSlotRequestFailed); @@ -91,52 +86,6 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& client.addExtension(rcpm); QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived); - - QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); - path += "/" + name; - QDir dir(path); - - if (!dir.exists()) { - bool res = dir.mkpath(path); - if (!res) { - qDebug() << "Couldn't create a cache directory for account" << name; - throw 22; - } - } - - QFile* avatar = new QFile(path + "/avatar.png"); - QString type = "png"; - if (!avatar->exists()) { - delete avatar; - avatar = new QFile(path + "/avatar.jpg"); - type = "jpg"; - if (!avatar->exists()) { - delete avatar; - avatar = new QFile(path + "/avatar.jpeg"); - type = "jpeg"; - if (!avatar->exists()) { - delete avatar; - avatar = new QFile(path + "/avatar.gif"); - type = "gif"; - } - } - } - - if (avatar->exists()) { - if (avatar->open(QFile::ReadOnly)) { - QCryptographicHash sha1(QCryptographicHash::Sha1); - sha1.addData(avatar); - avatarHash = sha1.result(); - avatarType = type; - } - } - if (avatarType.size() != 0) { - presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto); - presence.setPhotoHash(avatarHash.toUtf8()); - } else { - presence.setVCardUpdateType(QXmppPresence::VCardUpdateNotReady); - } - reconnectTimer->setSingleShot(true); QObject::connect(reconnectTimer, &QTimer::timeout, this, &Account::onReconnectTimer); @@ -160,6 +109,7 @@ Account::~Account() QObject::disconnect(network, &NetworkAccess::downloadFileComplete, mh, &MessageHandler::onDownloadFileComplete); QObject::disconnect(network, &NetworkAccess::loadFileError, mh, &MessageHandler::onLoadFileError); + delete vh; delete mh; delete rh; @@ -264,36 +214,6 @@ void Core::Account::reconnect() } } -QString Core::Account::getName() const { - return name;} - -QString Core::Account::getLogin() const { - return config.user();} - -QString Core::Account::getPassword() const { - return config.password();} - -QString Core::Account::getServer() const { - return config.domain();} - -Shared::AccountPassword Core::Account::getPasswordType() const { - return passwordType;} - -void Core::Account::setPasswordType(Shared::AccountPassword pt) { - passwordType = pt; } - -void Core::Account::setLogin(const QString& p_login) { - config.setUser(p_login);} - -void Core::Account::setName(const QString& p_name) { - name = p_name;} - -void Core::Account::setPassword(const QString& p_password) { - config.setPassword(p_password);} - -void Core::Account::setServer(const QString& p_server) { - config.setDomain(p_server);} - Shared::Availability Core::Account::getAvailability() const { if (state == Shared::ConnectionState::connected) { @@ -325,32 +245,11 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) QString jid = comps.front().toLower(); QString resource = comps.back(); - QString myJid = getLogin() + "@" + getServer(); - - if (jid == myJid) { + if (jid == getBareJid()) { if (resource == getResource()) { emit availabilityChanged(static_cast(p_presence.availableStatusType())); } else { - if (!ownVCardRequestInProgress) { - switch (p_presence.vCardUpdateType()) { - case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo - break; - case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here - break; - case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any - if (avatarType.size() > 0) { - vm->requestClientVCard(); - ownVCardRequestInProgress = true; - } - break; - case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load - if (avatarHash != p_presence.photoHash()) { - vm->requestClientVCard(); - ownVCardRequestInProgress = true; - } - break; - } - } + vh->handleOtherPresenceOfMyAccountChange(p_presence); } } else { RosterItem* item = rh->getRosterItem(jid); @@ -392,18 +291,6 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) } } -QString Core::Account::getResource() const { - return config.resource();} - -void Core::Account::setResource(const QString& p_resource) { - config.setResource(p_resource);} - -QString Core::Account::getFullJid() const { - return getLogin() + "@" + getServer() + "/" + getResource();} - -void Core::Account::sendMessage(const Shared::Message& data) { - mh->sendMessage(data);} - void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMessage& msg) { if (msg.id().size() > 0 && (msg.body().size() > 0 || msg.outOfBandUrl().size() > 0)) { @@ -667,6 +554,151 @@ void Core::Account::setRoomJoined(const QString& jid, bool joined) conf->setJoined(joined); } +void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items) +{ + if (items.from() == getServer()) { + std::set needToRequest; + qDebug() << "Server items list received for account " << name << ":"; + for (QXmppDiscoveryIq::Item item : items.items()) { + QString jid = item.jid(); + if (jid != getServer()) { + qDebug() << " Node" << jid; + needToRequest.insert(jid); + } else { + qDebug() << " " << item.node().toStdString().c_str(); + } + } + + for (const QString& jid : needToRequest) { + dm->requestInfo(jid); + } + } +} + +void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) +{ + if (info.from() == getServer()) { + bool enableCC = false; + qDebug() << "Server info received for account" << name; + QStringList features = info.features(); + qDebug() << "List of supported features of the server " << getServer() << ":"; + for (const QString& feature : features) { + qDebug() << " " << feature.toStdString().c_str(); + if (feature == "urn:xmpp:carbons:2") { + enableCC = true; + } + } + + if (enableCC) { + qDebug() << "Enabling carbon copies for account" << name; + cm->setCarbonsEnabled(true); + } + + qDebug() << "Requesting account" << name << "capabilities"; + dm->requestInfo(getBareJid()); + } else if (info.from() == getBareJid()) { + qDebug() << "Received capabilities for account" << name << ":"; + QList identities = info.identities(); + bool pepSupported = false; + for (const QXmppDiscoveryIq::Identity& identity : identities) { + QString type = identity.type(); + qDebug() << " " << identity.category() << type; + if (type == "pep") { + pepSupported = true; + } + } + rh->setPepSupport(pepSupported); + } else { + qDebug() << "Received info for account" << name << "about" << info.from(); + QList identities = info.identities(); + for (const QXmppDiscoveryIq::Identity& identity : identities) { + qDebug() << " " << identity.name() << identity.category() << identity.type(); + } + } +} + +void Core::Account::handleDisconnection() +{ + cm->setCarbonsEnabled(false); + rh->handleOffline(); + vh->handleOffline(); + archiveQueries.clear(); +} + +void Core::Account::onContactHistoryResponse(const std::list& list, bool last) +{ + RosterItem* contact = static_cast(sender()); + + qDebug() << "Collected history for contact " << contact->jid << list.size() << "elements"; + if (last) { + qDebug() << "The response contains the first accounted message"; + } + emit responseArchive(contact->jid, list, last); +} + +QString Core::Account::getResource() const { + return config.resource();} + +void Core::Account::setResource(const QString& p_resource) { + config.setResource(p_resource);} + +QString Core::Account::getBareJid() const { + return getLogin() + "@" + getServer();} + +QString Core::Account::getFullJid() const { + return getBareJid() + "/" + getResource();} + +QString Core::Account::getName() const { + return name;} + +QString Core::Account::getLogin() const { + return config.user();} + +QString Core::Account::getPassword() const { + return config.password();} + +QString Core::Account::getServer() const { + return config.domain();} + +Shared::AccountPassword Core::Account::getPasswordType() const { + return passwordType;} + +void Core::Account::setPasswordType(Shared::AccountPassword pt) { + passwordType = pt; } + +void Core::Account::setLogin(const QString& p_login) { + config.setUser(p_login);} + +void Core::Account::setName(const QString& p_name) { + name = p_name;} + +void Core::Account::setPassword(const QString& p_password) { + config.setPassword(p_password);} + +void Core::Account::setServer(const QString& p_server) { + config.setDomain(p_server);} + +void Core::Account::sendMessage(const Shared::Message& data) { + mh->sendMessage(data);} + +void Core::Account::requestChangeMessage(const QString& jid, const QString& messageId, const QMap& data){ + mh->requestChangeMessage(jid, messageId, data);} + +void Core::Account::resendMessage(const QString& jid, const QString& id) { + mh->resendMessage(jid, id);} + +void Core::Account::replaceMessage(const QString& originalId, const Shared::Message& data) { + mh->sendMessage(data, false, originalId);} + +void Core::Account::requestVCard(const QString& jid) { + vh->requestVCard(jid);} + +void Core::Account::uploadVCard(const Shared::VCard& card) { + vh->uploadVCard(card);} + +QString Core::Account::getAvatarPath() const { + return vh->getAvatarPath();} + void Core::Account::removeRoomRequest(const QString& jid){ rh->removeRoomRequest(jid);} @@ -688,254 +720,3 @@ void Core::Account::renameContactRequest(const QString& jid, const QString& newN rm->renameItem(jid, newName); } } - -void Core::Account::onVCardReceived(const QXmppVCardIq& card) -{ - QString id = card.from(); - QStringList comps = id.split("/"); - QString jid = comps.front().toLower(); - QString resource(""); - if (comps.size() > 1) { - resource = comps.back(); - } - pendingVCardRequests.erase(id); - RosterItem* item = rh->getRosterItem(jid); - - if (item == 0) { - if (jid == getLogin() + "@" + getServer()) { - onOwnVCardReceived(card); - } else { - qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping"; - } - return; - } - - Shared::VCard vCard = item->handleResponseVCard(card, resource); - - emit receivedVCard(jid, vCard); -} - -void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card) -{ - QByteArray ava = card.photo(); - bool avaChanged = false; - QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + name + "/"; - if (ava.size() > 0) { - QCryptographicHash sha1(QCryptographicHash::Sha1); - sha1.addData(ava); - QString newHash(sha1.result()); - QMimeDatabase db; - QMimeType newType = db.mimeTypeForData(ava); - if (avatarType.size() > 0) { - if (avatarHash != newHash) { - QString oldPath = path + "avatar." + avatarType; - QFile oldAvatar(oldPath); - bool oldToRemove = false; - if (oldAvatar.exists()) { - if (oldAvatar.rename(oldPath + ".bak")) { - oldToRemove = true; - } else { - qDebug() << "Received new avatar for account" << name << "but can't get rid of the old one, doing nothing"; - } - } - QFile newAvatar(path + "avatar." + newType.preferredSuffix()); - if (newAvatar.open(QFile::WriteOnly)) { - newAvatar.write(ava); - newAvatar.close(); - avatarHash = newHash; - avatarType = newType.preferredSuffix(); - avaChanged = true; - } else { - qDebug() << "Received new avatar for account" << name << "but can't save it"; - if (oldToRemove) { - qDebug() << "rolling back to the old avatar"; - if (!oldAvatar.rename(oldPath)) { - qDebug() << "Couldn't roll back to the old avatar in account" << name; - } - } - } - } - } else { - QFile newAvatar(path + "avatar." + newType.preferredSuffix()); - if (newAvatar.open(QFile::WriteOnly)) { - newAvatar.write(ava); - newAvatar.close(); - avatarHash = newHash; - avatarType = newType.preferredSuffix(); - avaChanged = true; - } else { - qDebug() << "Received new avatar for account" << name << "but can't save it"; - } - } - } else { - if (avatarType.size() > 0) { - QFile oldAvatar(path + "avatar." + avatarType); - if (!oldAvatar.remove()) { - qDebug() << "Received vCard for account" << name << "without avatar, but can't get rid of the file, doing nothing"; - } else { - avatarType = ""; - avatarHash = ""; - avaChanged = true; - } - } - } - - if (avaChanged) { - QMap change; - if (avatarType.size() > 0) { - presence.setPhotoHash(avatarHash.toUtf8()); - presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto); - change.insert("avatarPath", path + "avatar." + avatarType); - } else { - presence.setPhotoHash(""); - presence.setVCardUpdateType(QXmppPresence::VCardUpdateNoPhoto); - change.insert("avatarPath", ""); - } - client.setClientPresence(presence); - emit changed(change); - } - - ownVCardRequestInProgress = false; - - Shared::VCard vCard; - initializeVCard(vCard, card); - - if (avatarType.size() > 0) { - vCard.setAvatarType(Shared::Avatar::valid); - vCard.setAvatarPath(path + "avatar." + avatarType); - } else { - vCard.setAvatarType(Shared::Avatar::empty); - } - - emit receivedVCard(getLogin() + "@" + getServer(), vCard); -} - -QString Core::Account::getAvatarPath() const -{ - if (avatarType.size() == 0) { - return ""; - } else { - return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + name + "/" + "avatar." + avatarType; - } -} - -void Core::Account::requestVCard(const QString& jid) -{ - if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) { - qDebug() << "requesting vCard" << jid; - if (jid == getLogin() + "@" + getServer()) { - if (!ownVCardRequestInProgress) { - vm->requestClientVCard(); - ownVCardRequestInProgress = true; - } - } else { - vm->requestVCard(jid); - pendingVCardRequests.insert(jid); - } - } -} - -void Core::Account::uploadVCard(const Shared::VCard& card) -{ - QXmppVCardIq iq; - initializeQXmppVCard(iq, card); - - if (card.getAvatarType() != Shared::Avatar::empty) { - QString newPath = card.getAvatarPath(); - QString oldPath = getAvatarPath(); - QByteArray data; - QString type; - if (newPath != oldPath) { - QFile avatar(newPath); - if (!avatar.open(QFile::ReadOnly)) { - qDebug() << "An attempt to upload new vCard to account" << name - << "but it wasn't possible to read file" << newPath - << "which was supposed to be new avatar, uploading old avatar"; - if (avatarType.size() > 0) { - QFile oA(oldPath); - if (!oA.open(QFile::ReadOnly)) { - qDebug() << "Couldn't read old avatar of account" << name << ", uploading empty avatar"; - } else { - data = oA.readAll(); - } - } - } else { - data = avatar.readAll(); - } - } else { - if (avatarType.size() > 0) { - QFile oA(oldPath); - if (!oA.open(QFile::ReadOnly)) { - qDebug() << "Couldn't read old avatar of account" << name << ", uploading empty avatar"; - } else { - data = oA.readAll(); - } - } - } - - if (data.size() > 0) { - QMimeDatabase db; - type = db.mimeTypeForData(data).name(); - iq.setPhoto(data); - iq.setPhotoType(type); - } - } - - vm->setClientVCard(iq); - onOwnVCardReceived(iq); -} - -void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items) -{ - for (QXmppDiscoveryIq::Item item : items.items()) { - if (item.jid() != getServer()) { - dm->requestInfo(item.jid()); - } - } -} - -void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) -{ - qDebug() << "Discovery info received for account" << name; - if (info.from() == getServer()) { - if (info.features().contains("urn:xmpp:carbons:2")) { - qDebug() << "Enabling carbon copies for account" << name; - cm->setCarbonsEnabled(true); - } - } -} - -void Core::Account::handleDisconnection() -{ - cm->setCarbonsEnabled(false); - rh->handleOffline(); - archiveQueries.clear(); - pendingVCardRequests.clear(); - Shared::VCard vCard; //just to show, that there is now more pending request - for (const QString& jid : pendingVCardRequests) { - emit receivedVCard(jid, vCard); //need to show it better in the future, like with an error - } - pendingVCardRequests.clear(); - ownVCardRequestInProgress = false; -} - -void Core::Account::onContactHistoryResponse(const std::list& list, bool last) -{ - RosterItem* contact = static_cast(sender()); - - qDebug() << "Collected history for contact " << contact->jid << list.size() << "elements"; - if (last) { - qDebug() << "The response contains the first accounted message"; - } - emit responseArchive(contact->jid, list, last); -} - -void Core::Account::requestChangeMessage(const QString& jid, const QString& messageId, const QMap& data){ - mh->requestChangeMessage(jid, messageId, data);} - -void Core::Account::resendMessage(const QString& jid, const QString& id) { - mh->resendMessage(jid, id);} - -void Core::Account::replaceMessage(const QString& originalId, const Shared::Message& data) { - mh->sendMessage(data, false, originalId);} - diff --git a/core/account.h b/core/account.h index 664b547..c8e6e41 100644 --- a/core/account.h +++ b/core/account.h @@ -39,7 +39,6 @@ #include #include #include -#include #include #include @@ -50,6 +49,7 @@ #include "handlers/messagehandler.h" #include "handlers/rosterhandler.h" +#include "handlers/vcardhandler.h" namespace Core { @@ -59,6 +59,7 @@ class Account : public QObject Q_OBJECT friend class MessageHandler; friend class RosterHandler; + friend class VCardHandler; public: Account( const QString& p_login, @@ -76,6 +77,8 @@ public: QString getPassword() const; QString getResource() const; QString getAvatarPath() const; + QString getBareJid() const; + QString getFullJid() const; Shared::Availability getAvailability() const; Shared::AccountPassword getPasswordType() const; @@ -86,7 +89,6 @@ public: void setResource(const QString& p_resource); void setAvailability(Shared::Availability avail); void setPasswordType(Shared::AccountPassword pt); - QString getFullJid() const; void sendMessage(const Shared::Message& data); void requestArchive(const QString& jid, int count, const QString& before); void subscribeToContact(const QString& jid, const QString& reason); @@ -157,16 +159,13 @@ private: bool reconnectScheduled; QTimer* reconnectTimer; - std::set pendingVCardRequests; - - QString avatarHash; - QString avatarType; - bool ownVCardRequestInProgress; NetworkAccess* network; Shared::AccountPassword passwordType; + bool pepSupport; MessageHandler* mh; RosterHandler* rh; + VCardHandler* vh; private slots: void onClientStateChange(QXmppClient::State state); @@ -179,9 +178,6 @@ private slots: void onMamResultsReceived(const QString &queryId, const QXmppResultSetReply &resultSetReply, bool complete); void onMamLog(QXmppLogger::MessageType type, const QString &msg); - - void onVCardReceived(const QXmppVCardIq& card); - void onOwnVCardReceived(const QXmppVCardIq& card); void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items); void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info); @@ -191,9 +187,6 @@ private: void handleDisconnection(); void onReconnectTimer(); }; - -void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card); -void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard); } diff --git a/core/adapterFuctions.cpp b/core/adapterfunctions.cpp similarity index 98% rename from core/adapterFuctions.cpp rename to core/adapterfunctions.cpp index 3d84dfb..eec5a9f 100644 --- a/core/adapterFuctions.cpp +++ b/core/adapterfunctions.cpp @@ -1,5 +1,5 @@ /* - * Squawk messenger. + * Squawk messenger. * Copyright (C) 2019 Yury Gubich * * This program is free software: you can redistribute it and/or modify @@ -15,10 +15,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef CORE_ADAPTER_FUNCTIONS_H -#define CORE_ADAPTER_FUNCTIONS_H -#include "account.h" +#include "adapterfunctions.h" void Core::initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card) { @@ -271,5 +269,3 @@ void Core::initializeQXmppVCard(QXmppVCardIq& iq, const Shared::VCard& card) { iq.setEmails(emails); iq.setPhones(phs); } - -#endif // CORE_ADAPTER_FUNCTIONS_H diff --git a/core/adapterfunctions.h b/core/adapterfunctions.h new file mode 100644 index 0000000..6e50a75 --- /dev/null +++ b/core/adapterfunctions.h @@ -0,0 +1,32 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 CORE_ADAPTER_FUNCTIONS_H +#define CORE_ADAPTER_FUNCTIONS_H + +#include +#include + +namespace Core { + +void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card); +void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard); + +} + + +#endif // CORE_ADAPTER_FUNCTIONS_H diff --git a/core/handlers/CMakeLists.txt b/core/handlers/CMakeLists.txt index 6da2ef3..fb67953 100644 --- a/core/handlers/CMakeLists.txt +++ b/core/handlers/CMakeLists.txt @@ -3,4 +3,6 @@ target_sources(squawk PRIVATE messagehandler.h rosterhandler.cpp rosterhandler.h + vcardhandler.cpp + vcardhandler.h ) diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index 0555873..b6d32b9 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -176,7 +176,7 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp target.setForwarded(forwarded); if (guessing) { - if (target.getFromJid() == acc->getLogin() + "@" + acc->getServer()) { + if (target.getFromJid() == acc->getBareJid()) { outgoing = true; } else { outgoing = false; diff --git a/core/handlers/rosterhandler.cpp b/core/handlers/rosterhandler.cpp index ce5f1b7..6a233d6 100644 --- a/core/handlers/rosterhandler.cpp +++ b/core/handlers/rosterhandler.cpp @@ -26,7 +26,8 @@ Core::RosterHandler::RosterHandler(Core::Account* account): conferences(), groups(), queuedContacts(), - outOfRosterContacts() + outOfRosterContacts(), + pepSupport(false) { connect(acc->rm, &QXmppRosterManager::rosterReceived, this, &RosterHandler::onRosterReceived); connect(acc->rm, &QXmppRosterManager::itemAdded, this, &RosterHandler::onRosterItemAdded); @@ -51,8 +52,7 @@ Core::RosterHandler::~RosterHandler() void Core::RosterHandler::onRosterReceived() { - acc->vm->requestClientVCard(); //TODO need to make sure server actually supports vCards - acc->ownVCardRequestInProgress = true; + acc->requestVCard(acc->getBareJid()); //TODO need to make sure server actually supports vCards QStringList bj = acc->rm->getRosterBareJids(); for (int i = 0; i < bj.size(); ++i) { @@ -588,4 +588,13 @@ void Core::RosterHandler::handleOffline() pair.second->clearArchiveRequests(); pair.second->downgradeDatabaseState(); } + setPepSupport(false); +} + + +void Core::RosterHandler::setPepSupport(bool support) +{ + if (pepSupport != support) { + pepSupport = support; + } } diff --git a/core/handlers/rosterhandler.h b/core/handlers/rosterhandler.h index b1dfc45..02bbc98 100644 --- a/core/handlers/rosterhandler.h +++ b/core/handlers/rosterhandler.h @@ -64,6 +64,7 @@ public: void storeConferences(); void clearConferences(); + void setPepSupport(bool support); private slots: void onRosterReceived(); @@ -107,6 +108,7 @@ private: std::map> groups; std::map queuedContacts; std::set outOfRosterContacts; + bool pepSupport; }; } diff --git a/core/handlers/vcardhandler.cpp b/core/handlers/vcardhandler.cpp new file mode 100644 index 0000000..2a8d65c --- /dev/null +++ b/core/handlers/vcardhandler.cpp @@ -0,0 +1,312 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "vcardhandler.h" +#include "core/account.h" + +Core::VCardHandler::VCardHandler(Account* account): + QObject(), + acc(account), + ownVCardRequestInProgress(false), + pendingVCardRequests(), + avatarHash(), + avatarType() +{ + connect(acc->vm, &QXmppVCardManager::vCardReceived, this, &VCardHandler::onVCardReceived); + //for some reason it doesn't work, launching from common handler + //connect(acc->vm, &QXmppVCardManager::clientVCardReceived, this, &VCardHandler::onOwnVCardReceived); + + QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); + path += "/" + acc->name; + QDir dir(path); + + if (!dir.exists()) { + bool res = dir.mkpath(path); + if (!res) { + qDebug() << "Couldn't create a cache directory for account" << acc->name; + throw 22; + } + } + + QFile* avatar = new QFile(path + "/avatar.png"); + QString type = "png"; + if (!avatar->exists()) { + delete avatar; + avatar = new QFile(path + "/avatar.jpg"); + type = "jpg"; + if (!avatar->exists()) { + delete avatar; + avatar = new QFile(path + "/avatar.jpeg"); + type = "jpeg"; + if (!avatar->exists()) { + delete avatar; + avatar = new QFile(path + "/avatar.gif"); + type = "gif"; + } + } + } + + if (avatar->exists()) { + if (avatar->open(QFile::ReadOnly)) { + QCryptographicHash sha1(QCryptographicHash::Sha1); + sha1.addData(avatar); + avatarHash = sha1.result(); + avatarType = type; + } + } + if (avatarType.size() != 0) { + acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto); + acc->presence.setPhotoHash(avatarHash.toUtf8()); + } else { + acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateNotReady); + } +} + +Core::VCardHandler::~VCardHandler() +{ + +} + +void Core::VCardHandler::onVCardReceived(const QXmppVCardIq& card) +{ + QString id = card.from(); + QStringList comps = id.split("/"); + QString jid = comps.front().toLower(); + QString resource(""); + if (comps.size() > 1) { + resource = comps.back(); + } + pendingVCardRequests.erase(id); + RosterItem* item = acc->rh->getRosterItem(jid); + + if (item == 0) { + if (jid == acc->getBareJid()) { + onOwnVCardReceived(card); + } else { + qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping"; + } + return; + } + + Shared::VCard vCard = item->handleResponseVCard(card, resource); + + emit acc->receivedVCard(jid, vCard); +} + +void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card) +{ + QByteArray ava = card.photo(); + bool avaChanged = false; + QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/"; + if (ava.size() > 0) { + QCryptographicHash sha1(QCryptographicHash::Sha1); + sha1.addData(ava); + QString newHash(sha1.result()); + QMimeDatabase db; + QMimeType newType = db.mimeTypeForData(ava); + if (avatarType.size() > 0) { + if (avatarHash != newHash) { + QString oldPath = path + "avatar." + avatarType; + QFile oldAvatar(oldPath); + bool oldToRemove = false; + if (oldAvatar.exists()) { + if (oldAvatar.rename(oldPath + ".bak")) { + oldToRemove = true; + } else { + qDebug() << "Received new avatar for account" << acc->name << "but can't get rid of the old one, doing nothing"; + } + } + QFile newAvatar(path + "avatar." + newType.preferredSuffix()); + if (newAvatar.open(QFile::WriteOnly)) { + newAvatar.write(ava); + newAvatar.close(); + avatarHash = newHash; + avatarType = newType.preferredSuffix(); + avaChanged = true; + } else { + qDebug() << "Received new avatar for account" << acc->name << "but can't save it"; + if (oldToRemove) { + qDebug() << "rolling back to the old avatar"; + if (!oldAvatar.rename(oldPath)) { + qDebug() << "Couldn't roll back to the old avatar in account" << acc->name; + } + } + } + } + } else { + QFile newAvatar(path + "avatar." + newType.preferredSuffix()); + if (newAvatar.open(QFile::WriteOnly)) { + newAvatar.write(ava); + newAvatar.close(); + avatarHash = newHash; + avatarType = newType.preferredSuffix(); + avaChanged = true; + } else { + qDebug() << "Received new avatar for account" << acc->name << "but can't save it"; + } + } + } else { + if (avatarType.size() > 0) { + QFile oldAvatar(path + "avatar." + avatarType); + if (!oldAvatar.remove()) { + qDebug() << "Received vCard for account" << acc->name << "without avatar, but can't get rid of the file, doing nothing"; + } else { + avatarType = ""; + avatarHash = ""; + avaChanged = true; + } + } + } + + if (avaChanged) { + QMap change; + if (avatarType.size() > 0) { + acc->presence.setPhotoHash(avatarHash.toUtf8()); + acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto); + change.insert("avatarPath", path + "avatar." + avatarType); + } else { + acc->presence.setPhotoHash(""); + acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateNoPhoto); + change.insert("avatarPath", ""); + } + acc->client.setClientPresence(acc->presence); + emit acc->changed(change); + } + + ownVCardRequestInProgress = false; + + Shared::VCard vCard; + initializeVCard(vCard, card); + + if (avatarType.size() > 0) { + vCard.setAvatarType(Shared::Avatar::valid); + vCard.setAvatarPath(path + "avatar." + avatarType); + } else { + vCard.setAvatarType(Shared::Avatar::empty); + } + + emit acc->receivedVCard(acc->getBareJid(), vCard); +} + +void Core::VCardHandler::handleOffline() +{ + pendingVCardRequests.clear(); + Shared::VCard vCard; //just to show, that there is now more pending request + for (const QString& jid : pendingVCardRequests) { + emit acc->receivedVCard(jid, vCard); //need to show it better in the future, like with an error + } + pendingVCardRequests.clear(); + ownVCardRequestInProgress = false; +} + +void Core::VCardHandler::requestVCard(const QString& jid) +{ + if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) { + qDebug() << "requesting vCard" << jid; + if (jid == acc->getBareJid()) { + if (!ownVCardRequestInProgress) { + acc->vm->requestClientVCard(); + ownVCardRequestInProgress = true; + } + } else { + acc->vm->requestVCard(jid); + pendingVCardRequests.insert(jid); + } + } +} + +void Core::VCardHandler::handleOtherPresenceOfMyAccountChange(const QXmppPresence& p_presence) +{ + if (!ownVCardRequestInProgress) { + switch (p_presence.vCardUpdateType()) { + case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo + break; + case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here + break; + case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any + if (avatarType.size() > 0) { + acc->vm->requestClientVCard(); + ownVCardRequestInProgress = true; + } + break; + case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load + if (avatarHash != p_presence.photoHash()) { + acc->vm->requestClientVCard(); + ownVCardRequestInProgress = true; + } + break; + } + } +} + +void Core::VCardHandler::uploadVCard(const Shared::VCard& card) +{ + QXmppVCardIq iq; + initializeQXmppVCard(iq, card); + + if (card.getAvatarType() != Shared::Avatar::empty) { + QString newPath = card.getAvatarPath(); + QString oldPath = getAvatarPath(); + QByteArray data; + QString type; + if (newPath != oldPath) { + QFile avatar(newPath); + if (!avatar.open(QFile::ReadOnly)) { + qDebug() << "An attempt to upload new vCard to account" << acc->name + << "but it wasn't possible to read file" << newPath + << "which was supposed to be new avatar, uploading old avatar"; + if (avatarType.size() > 0) { + QFile oA(oldPath); + if (!oA.open(QFile::ReadOnly)) { + qDebug() << "Couldn't read old avatar of account" << acc->name << ", uploading empty avatar"; + } else { + data = oA.readAll(); + } + } + } else { + data = avatar.readAll(); + } + } else { + if (avatarType.size() > 0) { + QFile oA(oldPath); + if (!oA.open(QFile::ReadOnly)) { + qDebug() << "Couldn't read old avatar of account" << acc->name << ", uploading empty avatar"; + } else { + data = oA.readAll(); + } + } + } + + if (data.size() > 0) { + QMimeDatabase db; + type = db.mimeTypeForData(data).name(); + iq.setPhoto(data); + iq.setPhotoType(type); + } + } + + acc->vm->setClientVCard(iq); + onOwnVCardReceived(iq); +} + +QString Core::VCardHandler::getAvatarPath() const +{ + if (avatarType.size() == 0) { + return ""; + } else { + return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/" + "avatar." + avatarType; + } +} diff --git a/core/handlers/vcardhandler.h b/core/handlers/vcardhandler.h new file mode 100644 index 0000000..4febb69 --- /dev/null +++ b/core/handlers/vcardhandler.h @@ -0,0 +1,65 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 CORE_VCARDHANDLER_H +#define CORE_VCARDHANDLER_H + +#include + +#include +#include + +#include + +#include +#include + +/** + * @todo write docs + */ + +namespace Core { + +class Account; + +class VCardHandler : public QObject +{ + Q_OBJECT +public: + VCardHandler(Account* account); + ~VCardHandler(); + + void handleOffline(); + void requestVCard(const QString& jid); + void handleOtherPresenceOfMyAccountChange(const QXmppPresence& p_presence); + void uploadVCard(const Shared::VCard& card); + QString getAvatarPath() const; + +private slots: + void onVCardReceived(const QXmppVCardIq& card); + void onOwnVCardReceived(const QXmppVCardIq& card); + +private: + Account* acc; + + bool ownVCardRequestInProgress; + std::set pendingVCardRequests; + QString avatarHash; + QString avatarType; +}; +} + +#endif // CORE_VCARDHANDLER_H diff --git a/core/rosteritem.h b/core/rosteritem.h index 237a46a..d422e3f 100644 --- a/core/rosteritem.h +++ b/core/rosteritem.h @@ -35,6 +35,7 @@ #include "shared/message.h" #include "shared/vcard.h" #include "archive.h" +#include "adapterfunctions.h" namespace Core { From 2c26c7e264922532b37ed94eb366472c85c75062 Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 11 Apr 2022 18:45:12 +0300 Subject: [PATCH 041/137] ui squawk refactoring --- ui/CMakeLists.txt | 8 +- ui/dialogqueue.cpp | 148 +++++++++++++++++++++++++ ui/dialogqueue.h | 92 +++++++++++++++ ui/squawk.cpp | 49 +------- ui/squawk.h | 10 +- ui/widgets/CMakeLists.txt | 7 +- ui/widgets/accounts/CMakeLists.txt | 8 ++ ui/widgets/{ => accounts}/account.cpp | 0 ui/widgets/{ => accounts}/account.h | 0 ui/widgets/{ => accounts}/account.ui | 0 ui/widgets/{ => accounts}/accounts.cpp | 0 ui/widgets/{ => accounts}/accounts.h | 2 +- ui/widgets/{ => accounts}/accounts.ui | 0 13 files changed, 263 insertions(+), 61 deletions(-) create mode 100644 ui/dialogqueue.cpp create mode 100644 ui/dialogqueue.h create mode 100644 ui/widgets/accounts/CMakeLists.txt rename ui/widgets/{ => accounts}/account.cpp (100%) rename ui/widgets/{ => accounts}/account.h (100%) rename ui/widgets/{ => accounts}/account.ui (100%) rename ui/widgets/{ => accounts}/accounts.cpp (100%) rename ui/widgets/{ => accounts}/accounts.h (98%) rename ui/widgets/{ => accounts}/accounts.ui (100%) diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 36207b6..fcbb24c 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -1,4 +1,10 @@ -target_sources(squawk PRIVATE squawk.cpp squawk.h squawk.ui) +target_sources(squawk PRIVATE + squawk.cpp + squawk.h + squawk.ui + dialogqueue.cpp + dialogqueue.h +) add_subdirectory(models) add_subdirectory(utils) diff --git a/ui/dialogqueue.cpp b/ui/dialogqueue.cpp new file mode 100644 index 0000000..1887b28 --- /dev/null +++ b/ui/dialogqueue.cpp @@ -0,0 +1,148 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "dialogqueue.h" +#include "squawk.h" +#include + +DialogQueue::DialogQueue(Squawk* p_squawk): + QObject(), + currentSource(), + currentAction(none), + queue(), + collection(queue.get<0>()), + sequence(queue.get<1>()), + prompt(nullptr), + squawk(p_squawk) +{ +} + +DialogQueue::~DialogQueue() +{ +} + +bool DialogQueue::addAction(const QString& source, DialogQueue::Action action) +{ + if (action == none) { + return false; + } + if (currentAction == none) { + currentAction = action; + currentSource = source; + performNextAction(); + return true; + } else { + if (currentAction != action || currentSource != source) { + std::pair result = queue.emplace(source, action); + return result.second; + } else { + return false; + } + } +} + +bool DialogQueue::cancelAction(const QString& source, DialogQueue::Action action) +{ + if (source == currentSource && action == currentAction) { + actionDone(); + return true; + } else { + Collection::iterator itr = collection.find(ActionId{source, action}); + if (itr != collection.end()) { + collection.erase(itr); + return true; + } else { + return false; + } + } +} + +void DialogQueue::performNextAction() +{ + switch (currentAction) { + case none: + actionDone(); + break; + case askPassword: + prompt = new QInputDialog(squawk); + connect(prompt, &QDialog::accepted, this, &DialogQueue::onPropmtAccepted); + connect(prompt, &QDialog::rejected, this, &DialogQueue::onPropmtRejected); + prompt->setInputMode(QInputDialog::TextInput); + prompt->setTextEchoMode(QLineEdit::Password); + prompt->setLabelText(tr("Input the password for account %1").arg(currentSource)); + prompt->setWindowTitle(tr("Password for account %1").arg(currentSource)); + prompt->setTextValue(""); + prompt->exec(); + } +} + +void DialogQueue::onPropmtAccepted() +{ + switch (currentAction) { + case none: + break; + case askPassword: + emit squawk->responsePassword(currentSource, prompt->textValue()); + break; + } + actionDone(); +} + +void DialogQueue::onPropmtRejected() +{ + switch (currentAction) { + case none: + break; + case askPassword: + emit squawk->responsePassword(currentSource, prompt->textValue()); + break; + } + actionDone(); +} + +void DialogQueue::actionDone() +{ + prompt->deleteLater(); + prompt = nullptr; + + if (queue.empty()) { + currentAction = none; + currentSource = ""; + } else { + Sequence::iterator itr = sequence.begin(); + currentAction = itr->action; + currentSource = itr->source; + sequence.erase(itr); + performNextAction(); + } +} + +DialogQueue::ActionId::ActionId(const QString& p_source, DialogQueue::Action p_action): + source(p_source), + action(p_action) {} + +bool DialogQueue::ActionId::operator < (const DialogQueue::ActionId& other) const +{ + if (action == other.action) { + return source < other.source; + } else { + return action < other.action; + } +} + +DialogQueue::ActionId::ActionId(const DialogQueue::ActionId& other): + source(other.source), + action(other.action) {} diff --git a/ui/dialogqueue.h b/ui/dialogqueue.h new file mode 100644 index 0000000..c5bf011 --- /dev/null +++ b/ui/dialogqueue.h @@ -0,0 +1,92 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 DIALOGQUEUE_H +#define DIALOGQUEUE_H + +#include +#include + +#include +#include +#include + +/** + * @todo write docs + */ + +class Squawk; + +class DialogQueue : public QObject +{ + Q_OBJECT +public: + enum Action { + none, + askPassword + }; + + DialogQueue(Squawk* squawk); + ~DialogQueue(); + + bool addAction(const QString& source, Action action); + bool cancelAction(const QString& source, Action action); + +private: + void performNextAction(); + void actionDone(); + +private slots: + void onPropmtAccepted(); + void onPropmtRejected(); + +private: + QString currentSource; + Action currentAction; + + struct ActionId { + public: + ActionId(const QString& p_source, Action p_action); + ActionId(const ActionId& other); + + const QString source; + const Action action; + + bool operator < (const ActionId& other) const; + }; + + typedef boost::multi_index_container < + ActionId, + boost::multi_index::indexed_by < + boost::multi_index::ordered_unique < + boost::multi_index::identity + >, + boost::multi_index::sequenced<> + > + > Queue; + + typedef Queue::nth_index<0>::type Collection; + typedef Queue::nth_index<1>::type Sequence; + + Queue queue; + Collection& collection; + Sequence& sequence; + + QInputDialog* prompt; + Squawk* squawk; +}; + +#endif // DIALOGQUEUE_H diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 4c7320b..335b8d0 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -27,13 +27,12 @@ Squawk::Squawk(QWidget *parent) : accounts(nullptr), preferences(nullptr), about(nullptr), + dialogueQueue(this), rosterModel(), conversations(), contextMenu(new QMenu()), dbus("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus()), vCards(), - requestedAccountsForPasswords(), - prompt(nullptr), currentConversation(nullptr), restoreSelection(), needToRestore(false) @@ -925,50 +924,8 @@ void Squawk::onItemCollepsed(const QModelIndex& index) } } -void Squawk::requestPassword(const QString& account) -{ - requestedAccountsForPasswords.push_back(account); - checkNextAccountForPassword(); -} - -void Squawk::checkNextAccountForPassword() -{ - if (prompt == nullptr && requestedAccountsForPasswords.size() > 0) { - prompt = new QInputDialog(this); - QString accName = requestedAccountsForPasswords.front(); - connect(prompt, &QDialog::accepted, this, &Squawk::onPasswordPromptAccepted); - connect(prompt, &QDialog::rejected, this, &Squawk::onPasswordPromptRejected); - prompt->setInputMode(QInputDialog::TextInput); - prompt->setTextEchoMode(QLineEdit::Password); - prompt->setLabelText(tr("Input the password for account %1").arg(accName)); - prompt->setWindowTitle(tr("Password for account %1").arg(accName)); - prompt->setTextValue(""); - prompt->exec(); - } -} - -void Squawk::onPasswordPromptAccepted() -{ - emit responsePassword(requestedAccountsForPasswords.front(), prompt->textValue()); - onPasswordPromptDone(); -} - -void Squawk::onPasswordPromptDone() -{ - prompt->deleteLater(); - prompt = nullptr; - requestedAccountsForPasswords.pop_front(); - checkNextAccountForPassword(); -} - -void Squawk::onPasswordPromptRejected() -{ - //for now it's the same on reject and on accept, but one day I'm gonna make - //"Asking for the password again on the authentication failure" feature - //and here I'll be able to break the circle of password requests - emit responsePassword(requestedAccountsForPasswords.front(), prompt->textValue()); - onPasswordPromptDone(); -} +void Squawk::requestPassword(const QString& account) { + dialogueQueue.addAction(account, DialogQueue::askPassword);} void Squawk::subscribeConversation(Conversation* conv) { diff --git a/ui/squawk.h b/ui/squawk.h index 95c5ce3..6ee666c 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -31,7 +31,7 @@ #include #include -#include "widgets/accounts.h" +#include "widgets/accounts/accounts.h" #include "widgets/chat.h" #include "widgets/room.h" #include "widgets/newcontact.h" @@ -40,6 +40,7 @@ #include "widgets/vcard/vcard.h" #include "widgets/settings/settings.h" #include "widgets/about.h" +#include "dialogqueue.h" #include "shared/shared.h" @@ -122,13 +123,12 @@ private: Accounts* accounts; Settings* preferences; About* about; + DialogQueue dialogueQueue; Models::Roster rosterModel; Conversations conversations; QMenu* contextMenu; QDBusInterface dbus; std::map vCards; - std::deque requestedAccountsForPasswords; - QInputDialog* prompt; Conversation* currentConversation; QModelIndex restoreSelection; bool needToRestore; @@ -161,8 +161,6 @@ private slots: void onRequestArchive(const QString& account, const QString& jid, const QString& before); void onRosterContextMenu(const QPoint& point); void onItemCollepsed(const QModelIndex& index); - void onPasswordPromptAccepted(); - void onPasswordPromptRejected(); void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous); void onContextAboutToHide(); void onAboutSquawkCalled(); @@ -171,8 +169,6 @@ private slots: void onUnnoticedMessage(const QString& account, const Shared::Message& msg); private: - void checkNextAccountForPassword(); - void onPasswordPromptDone(); void subscribeConversation(Conversation* conv); }; diff --git a/ui/widgets/CMakeLists.txt b/ui/widgets/CMakeLists.txt index 7ba83d2..21d9504 100644 --- a/ui/widgets/CMakeLists.txt +++ b/ui/widgets/CMakeLists.txt @@ -1,10 +1,4 @@ target_sources(squawk PRIVATE - account.cpp - account.h - account.ui - accounts.cpp - accounts.h - accounts.ui chat.cpp chat.h conversation.cpp @@ -26,3 +20,4 @@ target_sources(squawk PRIVATE add_subdirectory(vcard) add_subdirectory(messageline) add_subdirectory(settings) +add_subdirectory(accounts) diff --git a/ui/widgets/accounts/CMakeLists.txt b/ui/widgets/accounts/CMakeLists.txt new file mode 100644 index 0000000..ad2f117 --- /dev/null +++ b/ui/widgets/accounts/CMakeLists.txt @@ -0,0 +1,8 @@ +target_sources(squawk PRIVATE + account.cpp + account.h + account.ui + accounts.cpp + accounts.h + accounts.ui + ) diff --git a/ui/widgets/account.cpp b/ui/widgets/accounts/account.cpp similarity index 100% rename from ui/widgets/account.cpp rename to ui/widgets/accounts/account.cpp diff --git a/ui/widgets/account.h b/ui/widgets/accounts/account.h similarity index 100% rename from ui/widgets/account.h rename to ui/widgets/accounts/account.h diff --git a/ui/widgets/account.ui b/ui/widgets/accounts/account.ui similarity index 100% rename from ui/widgets/account.ui rename to ui/widgets/accounts/account.ui diff --git a/ui/widgets/accounts.cpp b/ui/widgets/accounts/accounts.cpp similarity index 100% rename from ui/widgets/accounts.cpp rename to ui/widgets/accounts/accounts.cpp diff --git a/ui/widgets/accounts.h b/ui/widgets/accounts/accounts.h similarity index 98% rename from ui/widgets/accounts.h rename to ui/widgets/accounts/accounts.h index 9fd0b57..6d5eb95 100644 --- a/ui/widgets/accounts.h +++ b/ui/widgets/accounts/accounts.h @@ -24,7 +24,7 @@ #include #include "account.h" -#include "../models/accounts.h" +#include "ui/models/accounts.h" namespace Ui { diff --git a/ui/widgets/accounts.ui b/ui/widgets/accounts/accounts.ui similarity index 100% rename from ui/widgets/accounts.ui rename to ui/widgets/accounts/accounts.ui From f64e5c2df079aedcfb452be226930339f5fdac72 Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 12 Apr 2022 23:33:10 +0300 Subject: [PATCH 042/137] account connect/disconnect now activate/deactivate, it's a bit less contraversial; async account password asking new concept --- core/account.cpp | 62 +++++++--- core/account.h | 7 ++ core/squawk.cpp | 194 +++++++++++++++---------------- core/squawk.h | 6 +- ui/dialogqueue.cpp | 2 +- ui/models/account.cpp | 26 ++++- ui/models/account.h | 4 + ui/models/accounts.cpp | 4 + ui/models/roster.cpp | 12 ++ ui/squawk.cpp | 74 +++--------- ui/widgets/accounts/account.cpp | 1 + ui/widgets/accounts/account.ui | 45 ++++--- ui/widgets/accounts/accounts.cpp | 11 +- 13 files changed, 248 insertions(+), 200 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index 3d782cd..4d0480f 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -22,7 +22,7 @@ using namespace Core; -Account::Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, NetworkAccess* p_net, QObject* parent): +Account::Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, bool p_active, NetworkAccess* p_net, QObject* parent): QObject(parent), name(p_name), archiveQueries(), @@ -44,6 +44,8 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& network(p_net), passwordType(Shared::AccountPassword::plain), pepSupport(false), + active(p_active), + notReadyPassword(false), mh(new MessageHandler(this)), rh(new RosterHandler(this)), vh(new VCardHandler(this)) @@ -89,13 +91,15 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& reconnectTimer->setSingleShot(true); QObject::connect(reconnectTimer, &QTimer::timeout, this, &Account::onReconnectTimer); -// QXmppLogger* logger = new QXmppLogger(this); -// logger->setLoggingType(QXmppLogger::SignalLogging); -// client.setLogger(logger); -// -// QObject::connect(logger, &QXmppLogger::message, this, [](QXmppLogger::MessageType type, const QString& text){ -// qDebug() << text; -// }); + if (name == "Test") { + QXmppLogger* logger = new QXmppLogger(this); + logger->setLoggingType(QXmppLogger::SignalLogging); + client.setLogger(logger); + + QObject::connect(logger, &QXmppLogger::message, this, [](QXmppLogger::MessageType type, const QString& text){ + qDebug() << text; + }); + } } Account::~Account() @@ -135,7 +139,12 @@ void Core::Account::connect() reconnectTimer->stop(); } if (state == Shared::ConnectionState::disconnected) { - client.connectToServer(config, presence); + if (notReadyPassword) { + emit needPassword(); + } else { + client.connectToServer(config, presence); + } + } else { qDebug("An attempt to connect an account which is already connected, skipping"); } @@ -205,12 +214,14 @@ void Core::Account::onClientStateChange(QXmppClient::State st) void Core::Account::reconnect() { - if (state == Shared::ConnectionState::connected && !reconnectScheduled) { - reconnectScheduled = true; - reconnectTimer->start(500); - client.disconnectFromServer(); - } else { - qDebug() << "An attempt to reconnect account" << getName() << "which was not connected"; + if (!reconnectScheduled) { //TODO define behavior if It was connection or disconnecting + if (state == Shared::ConnectionState::connected) { + reconnectScheduled = true; + reconnectTimer->start(500); + client.disconnectFromServer(); + } else { + qDebug() << "An attempt to reconnect account" << getName() << "which was not connected"; + } } } @@ -636,6 +647,19 @@ void Core::Account::onContactHistoryResponse(const std::list& l emit responseArchive(contact->jid, list, last); } +bool Core::Account::getActive() const { + return active;} + +void Core::Account::setActive(bool p_active) { + if (active != p_active) { + active = p_active; + + emit changed({ + {"active", active} + }); + } +} + QString Core::Account::getResource() const { return config.resource();} @@ -673,7 +697,9 @@ void Core::Account::setName(const QString& p_name) { name = p_name;} void Core::Account::setPassword(const QString& p_password) { - config.setPassword(p_password);} + config.setPassword(p_password); + notReadyPassword = false; +} void Core::Account::setServer(const QString& p_server) { config.setDomain(p_server);} @@ -720,3 +746,7 @@ void Core::Account::renameContactRequest(const QString& jid, const QString& newN rm->renameItem(jid, newName); } } + +void Core::Account::invalidatePassword() { + notReadyPassword = true;} + diff --git a/core/account.h b/core/account.h index c8e6e41..aa65b27 100644 --- a/core/account.h +++ b/core/account.h @@ -66,6 +66,7 @@ public: const QString& p_server, const QString& p_password, const QString& p_name, + bool p_active, NetworkAccess* p_net, QObject* parent = 0); ~Account(); @@ -81,6 +82,7 @@ public: QString getFullJid() const; Shared::Availability getAvailability() const; Shared::AccountPassword getPasswordType() const; + bool getActive() const; void setName(const QString& p_name); void setLogin(const QString& p_login); @@ -90,6 +92,7 @@ public: void setAvailability(Shared::Availability avail); void setPasswordType(Shared::AccountPassword pt); void sendMessage(const Shared::Message& data); + void setActive(bool p_active); void requestArchive(const QString& jid, int count, const QString& before); void subscribeToContact(const QString& jid, const QString& reason); void unsubscribeFromContact(const QString& jid, const QString& reason); @@ -107,6 +110,7 @@ public: void uploadVCard(const Shared::VCard& card); void resendMessage(const QString& jid, const QString& id); void replaceMessage(const QString& originalId, const Shared::Message& data); + void invalidatePassword(); public slots: void connect(); @@ -139,6 +143,7 @@ signals: void receivedVCard(const QString& jid, const Shared::VCard& card); void uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap headers); void uploadFileError(const QString& jid, const QString& messageId, const QString& error); + void needPassword(); private: QString name; @@ -162,6 +167,8 @@ private: NetworkAccess* network; Shared::AccountPassword passwordType; bool pepSupport; + bool active; + bool notReadyPassword; MessageHandler* mh; RosterHandler* rh; diff --git a/core/squawk.cpp b/core/squawk.cpp index af131d5..d594553 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -26,8 +26,8 @@ Core::Squawk::Squawk(QObject* parent): QObject(parent), accounts(), amap(), + state(Shared::Availability::offline), network(), - waitingForAccounts(0), isInitialized(false) #ifdef WITH_KWALLET ,kwallet() @@ -42,7 +42,7 @@ Core::Squawk::Squawk(QObject* parent): if (kwallet.supportState() == PSE::KWallet::success) { connect(&kwallet, &PSE::KWallet::opened, this, &Squawk::onWalletOpened); connect(&kwallet, &PSE::KWallet::rejectPassword, this, &Squawk::onWalletRejectPassword); - connect(&kwallet, &PSE::KWallet::responsePassword, this, &Squawk::onWalletResponsePassword); + connect(&kwallet, &PSE::KWallet::responsePassword, this, &Squawk::responsePassword); Shared::Global::setSupported("KWallet", true); } @@ -97,6 +97,7 @@ void Core::Squawk::stop() settings.setValue("password", password); settings.setValue("resource", acc->getResource()); settings.setValue("passwordType", static_cast(ap)); + settings.setValue("active", acc->getActive()); } settings.endArray(); settings.endGroup(); @@ -124,8 +125,9 @@ void Core::Squawk::newAccountRequest(const QMap& map) QString password = map.value("password").toString(); QString resource = map.value("resource").toString(); int passwordType = map.value("passwordType").toInt(); + bool active = map.value("active").toBool(); - addAccount(login, server, password, name, resource, Shared::Global::fromInt(passwordType)); + addAccount(login, server, password, name, resource, active, Shared::Global::fromInt(passwordType)); } void Core::Squawk::addAccount( @@ -133,13 +135,13 @@ void Core::Squawk::addAccount( const QString& server, const QString& password, const QString& name, - const QString& resource, - Shared::AccountPassword passwordType -) + const QString& resource, + bool active, + Shared::AccountPassword passwordType) { QSettings settings; - Account* acc = new Account(login, server, password, name, &network); + Account* acc = new Account(login, server, password, name, active, &network); acc->setResource(resource); acc->setPasswordType(passwordType); accounts.push_back(acc); @@ -148,6 +150,8 @@ void Core::Squawk::addAccount( connect(acc, &Account::connectionStateChanged, this, &Squawk::onAccountConnectionStateChanged); connect(acc, &Account::changed, this, &Squawk::onAccountChanged); connect(acc, &Account::error, this, &Squawk::onAccountError); + connect(acc, &Account::needPassword, this, &Squawk::onAccountNeedPassword); + connect(acc, &Account::availabilityChanged, this, &Squawk::onAccountAvailabilityChanged); connect(acc, &Account::addContact, this, &Squawk::onAccountAddContact); connect(acc, &Account::addGroup, this, &Squawk::onAccountAddGroup); @@ -185,20 +189,42 @@ void Core::Squawk::addAccount( {"offline", QVariant::fromValue(Shared::Availability::offline)}, {"error", ""}, {"avatarPath", acc->getAvatarPath()}, - {"passwordType", QVariant::fromValue(passwordType)} + {"passwordType", QVariant::fromValue(passwordType)}, + {"active", active} }; emit newAccount(map); + + switch (passwordType) { + case Shared::AccountPassword::alwaysAsk: + case Shared::AccountPassword::kwallet: + acc->invalidatePassword(); + break; + default: + break; + } + + if (state != Shared::Availability::offline) { + acc->setAvailability(state); + if (acc->getActive()) { + acc->connect(); + } + } } void Core::Squawk::changeState(Shared::Availability p_state) { if (state != p_state) { + for (std::deque::iterator itr = accounts.begin(), end = accounts.end(); itr != end; ++itr) { + Account* acc = *itr; + acc->setAvailability(p_state); + if (state == Shared::Availability::offline && acc->getActive()) { + acc->connect(); + } + } state = p_state; - } - - for (std::deque::iterator itr = accounts.begin(), end = accounts.end(); itr != end; ++itr) { - (*itr)->setAvailability(state); + + emit stateChanged(p_state); } } @@ -209,7 +235,10 @@ void Core::Squawk::connectAccount(const QString& account) qDebug("An attempt to connect non existing account, skipping"); return; } - itr->second->connect(); + itr->second->setActive(true); + if (state != Shared::Availability::offline) { + itr->second->connect(); + } } void Core::Squawk::disconnectAccount(const QString& account) @@ -220,6 +249,7 @@ void Core::Squawk::disconnectAccount(const QString& account) return; } + itr->second->setActive(false); itr->second->disconnect(); } @@ -227,7 +257,7 @@ void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_sta { Account* acc = static_cast(sender()); emit changeAccount(acc->getName(), {{"state", QVariant::fromValue(p_state)}}); - + #ifdef WITH_KWALLET if (p_state == Shared::ConnectionState::connected) { if (acc->getPasswordType() == Shared::AccountPassword::kwallet && kwallet.supportState() == PSE::KWallet::success) { @@ -235,33 +265,6 @@ void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_sta } } #endif - - Accounts::const_iterator itr = accounts.begin(); - bool es = true; - bool ea = true; - Shared::ConnectionState cs = (*itr)->getState(); - Shared::Availability av = (*itr)->getAvailability(); - itr++; - for (Accounts::const_iterator end = accounts.end(); itr != end; itr++) { - Account* item = *itr; - if (item->getState() != cs) { - es = false; - } - if (item->getAvailability() != av) { - ea = false; - } - } - - if (es) { - if (cs == Shared::ConnectionState::disconnected) { - state = Shared::Availability::offline; - emit stateChanged(state); - } else if (ea) { - state = av; - emit stateChanged(state); - } - } - } void Core::Squawk::onAccountAddContact(const QString& jid, const QString& group, const QMap& data) @@ -416,8 +419,15 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMapreconnect(); + bool activeChanged = false; + mItr = map.find("active"); + if (mItr == map.end() || mItr->toBool() == acc->getActive()) { + if (needToReconnect && st != Shared::ConnectionState::disconnected) { + acc->reconnect(); + } + } else { + acc->setActive(mItr->toBool()); + activeChanged = true; } mItr = map.find("login"); @@ -454,6 +464,10 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMapgetActive() && state != Shared::Availability::offline) { + acc->connect(); + } + emit changeAccount(name, map); } @@ -675,85 +689,62 @@ void Core::Squawk::uploadVCard(const QString& account, const Shared::VCard& card itr->second->uploadVCard(card); } -void Core::Squawk::responsePassword(const QString& account, const QString& password) -{ - AccountsMap::const_iterator itr = amap.find(account); - if (itr == amap.end()) { - qDebug() << "An attempt to set password to non existing account" << account << ", skipping"; - return; - } - itr->second->setPassword(password); - emit changeAccount(account, {{"password", password}}); - accountReady(); -} - void Core::Squawk::readSettings() { QSettings settings; settings.beginGroup("core"); int size = settings.beginReadArray("accounts"); - waitingForAccounts = size; for (int i = 0; i < size; ++i) { settings.setArrayIndex(i); - parseAccount( + Shared::AccountPassword passwordType = + Shared::Global::fromInt( + settings.value("passwordType", static_cast(Shared::AccountPassword::plain)).toInt() + ); + + QString password = settings.value("password", "").toString(); + if (passwordType == Shared::AccountPassword::jammed) { + SimpleCrypt crypto(passwordHash); + password = crypto.decryptToString(password); + } + + addAccount( settings.value("login").toString(), settings.value("server").toString(), - settings.value("password", "").toString(), + password, settings.value("name").toString(), settings.value("resource").toString(), - Shared::Global::fromInt(settings.value("passwordType", static_cast(Shared::AccountPassword::plain)).toInt()) + settings.value("active").toBool(), + passwordType ); } settings.endArray(); settings.endGroup(); + + qDebug() << "Squawk core is ready"; + emit ready(); } -void Core::Squawk::accountReady() +void Core::Squawk::onAccountNeedPassword() { - --waitingForAccounts; - - if (waitingForAccounts == 0) { - emit ready(); - } -} - -void Core::Squawk::parseAccount( - const QString& login, - const QString& server, - const QString& password, - const QString& name, - const QString& resource, - Shared::AccountPassword passwordType -) -{ - switch (passwordType) { - case Shared::AccountPassword::plain: - addAccount(login, server, password, name, resource, passwordType); - accountReady(); - break; - case Shared::AccountPassword::jammed: { - SimpleCrypt crypto(passwordHash); - QString decrypted = crypto.decryptToString(password); - addAccount(login, server, decrypted, name, resource, passwordType); - accountReady(); - } - break; - case Shared::AccountPassword::alwaysAsk: - addAccount(login, server, QString(), name, resource, passwordType); - emit requestPassword(name); + Account* acc = static_cast(sender()); + switch (acc->getPasswordType()) { + case Shared::AccountPassword::alwaysAsk: + emit requestPassword(acc->getName()); break; case Shared::AccountPassword::kwallet: { - addAccount(login, server, QString(), name, resource, passwordType); #ifdef WITH_KWALLET if (kwallet.supportState() == PSE::KWallet::success) { - kwallet.requestReadPassword(name); + kwallet.requestReadPassword(acc->getName()); } else { #endif - emit requestPassword(name); + emit requestPassword(acc->getName()); #ifdef WITH_KWALLET } #endif + break; } + default: + break; //should never happen; } } @@ -762,16 +753,19 @@ void Core::Squawk::onWalletRejectPassword(const QString& login) emit requestPassword(login); } -void Core::Squawk::onWalletResponsePassword(const QString& login, const QString& password) +void Core::Squawk::responsePassword(const QString& account, const QString& password) { - AccountsMap::const_iterator itr = amap.find(login); + AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { - qDebug() << "An attempt to set password to non existing account" << login << ", skipping"; + qDebug() << "An attempt to set password to non existing account" << account << ", skipping"; return; } - itr->second->setPassword(password); - emit changeAccount(login, {{"password", password}}); - accountReady(); + Account* acc = itr->second; + acc->setPassword(password); + emit changeAccount(account, {{"password", password}}); + if (state != Shared::Availability::offline && acc->getActive()) { + acc->connect(); + } } void Core::Squawk::onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText) diff --git a/core/squawk.h b/core/squawk.h index 6cd251f..6cb3115 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -134,7 +134,6 @@ private: AccountsMap amap; Shared::Availability state; NetworkAccess network; - uint8_t waitingForAccounts; bool isInitialized; #ifdef WITH_KWALLET @@ -148,6 +147,7 @@ private slots: const QString& password, const QString& name, const QString& resource, + bool active, Shared::AccountPassword passwordType ); @@ -172,22 +172,22 @@ private slots: void onAccountChangeRoomPresence(const QString& jid, const QString& nick, const QMap& data); void onAccountRemoveRoomPresence(const QString& jid, const QString& nick); void onAccountChangeMessage(const QString& jid, const QString& id, const QMap& data); + void onAccountNeedPassword(); void onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText); void onWalletOpened(bool success); - void onWalletResponsePassword(const QString& login, const QString& password); void onWalletRejectPassword(const QString& login); private: void readSettings(); - void accountReady(); void parseAccount( const QString& login, const QString& server, const QString& password, const QString& name, const QString& resource, + bool active, Shared::AccountPassword passwordType ); diff --git a/ui/dialogqueue.cpp b/ui/dialogqueue.cpp index 1887b28..f5be82b 100644 --- a/ui/dialogqueue.cpp +++ b/ui/dialogqueue.cpp @@ -107,7 +107,7 @@ void DialogQueue::onPropmtRejected() case none: break; case askPassword: - emit squawk->responsePassword(currentSource, prompt->textValue()); + emit squawk->disconnectAccount(currentSource); break; } actionDone(); diff --git a/ui/models/account.cpp b/ui/models/account.cpp index 43cb3ed..cf1efb4 100644 --- a/ui/models/account.cpp +++ b/ui/models/account.cpp @@ -32,7 +32,8 @@ Models::Account::Account(const QMap& data, Models::Item* pare state(Shared::ConnectionState::disconnected), availability(Shared::Availability::offline), passwordType(Shared::AccountPassword::plain), - wasEverConnected(false) + wasEverConnected(false), + active(false) { QMap::const_iterator sItr = data.find("state"); if (sItr != data.end()) { @@ -46,6 +47,10 @@ Models::Account::Account(const QMap& data, Models::Item* pare if (pItr != data.end()) { setPasswordType(pItr.value().toUInt()); } + QMap::const_iterator acItr = data.find("active"); + if (acItr != data.end()) { + setActive(acItr.value().toBool()); + } } Models::Account::~Account() @@ -176,6 +181,8 @@ QVariant Models::Account::data(int column) const return avatarPath; case 9: return Shared::Global::getName(passwordType); + case 10: + return active; default: return QVariant(); } @@ -183,7 +190,7 @@ QVariant Models::Account::data(int column) const int Models::Account::columnCount() const { - return 10; + return 11; } void Models::Account::update(const QString& field, const QVariant& value) @@ -208,6 +215,8 @@ void Models::Account::update(const QString& field, const QVariant& value) setAvatarPath(value.toString()); } else if (field == "passwordType") { setPasswordType(value.toUInt()); + } else if (field == "active") { + setActive(value.toBool()); } } @@ -281,3 +290,16 @@ void Models::Account::setPasswordType(unsigned int pt) { setPasswordType(Shared::Global::fromInt(pt)); } + +bool Models::Account::getActive() const +{ + return active; +} + +void Models::Account::setActive(bool p_active) +{ + if (active != p_active) { + active = p_active; + changed(10); + } +} diff --git a/ui/models/account.h b/ui/models/account.h index 3d2310f..ab2b629 100644 --- a/ui/models/account.h +++ b/ui/models/account.h @@ -58,6 +58,9 @@ namespace Models { void setAvatarPath(const QString& path); QString getAvatarPath() const; + + void setActive(bool active); + bool getActive() const; void setAvailability(Shared::Availability p_avail); void setAvailability(unsigned int p_avail); @@ -91,6 +94,7 @@ namespace Models { Shared::Availability availability; Shared::AccountPassword passwordType; bool wasEverConnected; + bool active; protected slots: void toOfflineState() override; diff --git a/ui/models/accounts.cpp b/ui/models/accounts.cpp index 4343481..463ab40 100644 --- a/ui/models/accounts.cpp +++ b/ui/models/accounts.cpp @@ -48,6 +48,10 @@ QVariant Models::Accounts::data (const QModelIndex& index, int role) const answer = Shared::connectionStateIcon(accs[index.row()]->getState()); } break; + case Qt::ForegroundRole: + if (!accs[index.row()]->getActive()) { + answer = qApp->palette().brush(QPalette::Disabled, QPalette::Text); + } default: break; } diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index 588fb1d..1355fe3 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -276,6 +276,18 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const break; } break; + case Qt::ForegroundRole: + switch (item->type) { + case Item::account: { + Account* acc = static_cast(item); + if (!acc->getActive()) { + result = qApp->palette().brush(QPalette::Disabled, QPalette::Text); + } + } + break; + default: + break; + } default: break; } diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 335b8d0..3a3d1d9 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -234,29 +234,7 @@ void Squawk::newAccount(const QMap& account) void Squawk::onComboboxActivated(int index) { Shared::Availability av = Shared::Global::fromInt(index); - if (av != Shared::Availability::offline) { - int size = rosterModel.accountsModel->rowCount(QModelIndex()); - if (size > 0) { - emit changeState(av); - for (int i = 0; i < size; ++i) { - Models::Account* acc = rosterModel.accountsModel->getAccount(i); - if (acc->getState() == Shared::ConnectionState::disconnected) { - emit connectAccount(acc->getName()); - } - } - } else { - m_ui->comboBox->setCurrentIndex(static_cast(Shared::Availability::offline)); - } - } else { - emit changeState(av); - int size = rosterModel.accountsModel->rowCount(QModelIndex()); - for (int i = 0; i != size; ++i) { - Models::Account* acc = rosterModel.accountsModel->getAccount(i); - if (acc->getState() != Shared::ConnectionState::disconnected) { - emit disconnectAccount(acc->getName()); - } - } - } + emit changeState(av); } void Squawk::changeAccount(const QString& account, const QMap& data) @@ -573,17 +551,12 @@ void Squawk::onRosterContextMenu(const QPoint& point) hasMenu = true; QString name = acc->getName(); - if (acc->getState() != Shared::ConnectionState::disconnected) { - QAction* con = contextMenu->addAction(Shared::icon("network-disconnect"), tr("Disconnect")); - con->setEnabled(active); - connect(con, &QAction::triggered, [this, name]() { - emit disconnectAccount(name); - }); + if (acc->getActive()) { + QAction* con = contextMenu->addAction(Shared::icon("network-disconnect"), tr("Deactivate")); + connect(con, &QAction::triggered, std::bind(&Squawk::disconnectAccount, this, name)); } else { - QAction* con = contextMenu->addAction(Shared::icon("network-connect"), tr("Connect")); - connect(con, &QAction::triggered, [this, name]() { - emit connectAccount(name); - }); + QAction* con = contextMenu->addAction(Shared::icon("network-connect"), tr("Activate")); + connect(con, &QAction::triggered, std::bind(&Squawk::connectAccount, this, name)); } QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard")); @@ -591,11 +564,7 @@ void Squawk::onRosterContextMenu(const QPoint& point) connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, name, acc->getBareJid(), true)); QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove")); - remove->setEnabled(active); - connect(remove, &QAction::triggered, [this, name]() { - emit removeAccount(name); - }); - + connect(remove, &QAction::triggered, std::bind(&Squawk::removeAccount, this, name)); } break; case Models::Item::contact: { @@ -839,20 +808,16 @@ void Squawk::readSettings() { QSettings settings; settings.beginGroup("ui"); - + int avail; if (settings.contains("availability")) { - int avail = settings.value("availability").toInt(); - m_ui->comboBox->setCurrentIndex(avail); - emit stateChanged(Shared::Global::fromInt(avail)); - - int size = settings.beginReadArray("connectedAccounts"); - for (int i = 0; i < size; ++i) { - settings.setArrayIndex(i); - emit connectAccount(settings.value("name").toString()); //TODO this is actually not needed, stateChanged event already connects everything you have - } // need to fix that - settings.endArray(); + avail = settings.value("availability").toInt(); + } else { + avail = static_cast(Shared::Availability::online); } settings.endGroup(); + m_ui->comboBox->setCurrentIndex(avail); + + emit changeState(Shared::Global::fromInt(avail)); } void Squawk::writeSettings() @@ -867,19 +832,10 @@ void Squawk::writeSettings() settings.setValue("splitter", m_ui->splitter->saveState()); settings.setValue("availability", m_ui->comboBox->currentIndex()); - settings.beginWriteArray("connectedAccounts"); - int size = rosterModel.accountsModel->rowCount(QModelIndex()); - for (int i = 0; i < size; ++i) { - Models::Account* acc = rosterModel.accountsModel->getAccount(i); - if (acc->getState() != Shared::ConnectionState::disconnected) { - settings.setArrayIndex(i); - settings.setValue("name", acc->getName()); - } - } - settings.endArray(); 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); diff --git a/ui/widgets/accounts/account.cpp b/ui/widgets/accounts/account.cpp index ba3af6b..164af6c 100644 --- a/ui/widgets/accounts/account.cpp +++ b/ui/widgets/accounts/account.cpp @@ -53,6 +53,7 @@ QMap Account::value() const map["name"] = m_ui->name->text(); map["resource"] = m_ui->resource->text(); map["passwordType"] = m_ui->passwordType->currentIndex(); + map["active"] = m_ui->active->isChecked(); return map; } diff --git a/ui/widgets/accounts/account.ui b/ui/widgets/accounts/account.ui index a1879bc..b7f9f26 100644 --- a/ui/widgets/accounts/account.ui +++ b/ui/widgets/accounts/account.ui @@ -7,7 +7,7 @@ 0 0 438 - 342 + 345 @@ -34,7 +34,7 @@ 6 - + Your account login @@ -44,14 +44,14 @@ - + Server - + A server address of your account. Like 404.city or macaw.me @@ -61,21 +61,21 @@ - + Login - + Password - + Password of your account @@ -97,14 +97,14 @@ - + Name - + Just a name how would you call this account, doesn't affect anything @@ -114,14 +114,14 @@ - + Resource - + A resource name like "Home" or "Work" @@ -131,17 +131,17 @@ - + Password storage - + - + @@ -157,6 +157,23 @@ + + + + Active + + + + + + + enable + + + true + + + diff --git a/ui/widgets/accounts/accounts.cpp b/ui/widgets/accounts/accounts.cpp index 7f4a135..82a8ca0 100644 --- a/ui/widgets/accounts/accounts.cpp +++ b/ui/widgets/accounts/accounts.cpp @@ -83,7 +83,8 @@ void Accounts::onEditButton() {"server", mAcc->getServer()}, {"name", mAcc->getName()}, {"resource", mAcc->getResource()}, - {"passwordType", QVariant::fromValue(mAcc->getPasswordType())} + {"passwordType", QVariant::fromValue(mAcc->getPasswordType())}, + {"active", mAcc->getActive()} }); acc->lockId(); connect(acc, &Account::accepted, this, &Accounts::onAccountAccepted); @@ -118,17 +119,17 @@ void Accounts::updateConnectButton() bool allConnected = true; for (int i = 0; i < selectionSize && allConnected; ++i) { const Models::Account* mAcc = model->getAccount(sm->selectedRows().at(i).row()); - allConnected = mAcc->getState() == Shared::ConnectionState::connected; + allConnected = allConnected && mAcc->getActive(); } if (allConnected) { toDisconnect = true; - m_ui->connectButton->setText(tr("Disconnect")); + m_ui->connectButton->setText(tr("Deactivate")); } else { toDisconnect = false; - m_ui->connectButton->setText(tr("Connect")); + m_ui->connectButton->setText(tr("Activate")); } } else { - m_ui->connectButton->setText(tr("Connect")); + m_ui->connectButton->setText(tr("Activate")); toDisconnect = false; m_ui->connectButton->setEnabled(false); } From ce686e121b7f29b3fe5d9c05c85f205185734089 Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 13 Apr 2022 22:02:48 +0300 Subject: [PATCH 043/137] account removal bugfix, some testing --- CHANGELOG.md | 9 ++++++++- core/squawk.cpp | 12 ++++++++---- ui/squawk.cpp | 2 +- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f563c85..410ff11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,18 @@ ## Squawk 0.2.2 (UNRELEASED) ### Bug fixes +- now when you remove an account it actually gets removed +- segfault on unitialized Availability in some rare occesions ### Improvements +- there is a way to disable an account and it wouldn't connect when you change availability +- if you cancel password query an account becomes inactive and doesn't annoy you anymore +- if you filled password field and chose KWallet as a storage Squawk wouldn't ask you again for the same password +- if left the password field empty and chose KWallet as a storage Squawk will try to get that passord from KWallet before asking you to input it +- accounts now connect to the server asyncronously - if one is stopped on password prompt another is connecting ### New features - +- new "About" window with links, license, gratitudes ## Squawk 0.2.1 (Apr 02, 2022) ### Bug fixes diff --git a/core/squawk.cpp b/core/squawk.cpp index d594553..49a2b34 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -139,8 +139,10 @@ void Core::Squawk::addAccount( bool active, Shared::AccountPassword passwordType) { - QSettings settings; - + if (amap.count(name) > 0) { + qDebug() << "An attempt to add account" << name << "but an account with such name already exist, ignoring"; + return; + } Account* acc = new Account(login, server, password, name, active, &network); acc->setResource(resource); acc->setPasswordType(passwordType); @@ -198,8 +200,10 @@ void Core::Squawk::addAccount( switch (passwordType) { case Shared::AccountPassword::alwaysAsk: case Shared::AccountPassword::kwallet: - acc->invalidatePassword(); - break; + if (password == "") { + acc->invalidatePassword(); + break; + } default: break; } diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 3a3d1d9..a447458 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -564,7 +564,7 @@ void Squawk::onRosterContextMenu(const QPoint& point) connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, name, acc->getBareJid(), true)); QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove")); - connect(remove, &QAction::triggered, std::bind(&Squawk::removeAccount, this, name)); + connect(remove, &QAction::triggered, std::bind(&Squawk::removeAccountRequest, this, name)); } break; case Models::Item::contact: { From 8f949277f68be1f4baeb282b3c483948400dd6ca Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 14 Apr 2022 11:13:27 +0300 Subject: [PATCH 044/137] actual pasword reasking on failed authentication --- CHANGELOG.md | 1 + core/account.cpp | 7 ++ core/account.h | 8 ++ core/squawk.cpp | 25 +++- core/squawk.h | 2 +- ui/dialogqueue.cpp | 50 ++++++-- ui/dialogqueue.h | 9 +- ui/squawk.cpp | 10 +- ui/squawk.h | 4 +- ui/widgets/accounts/CMakeLists.txt | 3 + ui/widgets/accounts/credentialsprompt.cpp | 60 +++++++++ ui/widgets/accounts/credentialsprompt.h | 52 ++++++++ ui/widgets/accounts/credentialsprompt.ui | 144 ++++++++++++++++++++++ 13 files changed, 347 insertions(+), 28 deletions(-) create mode 100644 ui/widgets/accounts/credentialsprompt.cpp create mode 100644 ui/widgets/accounts/credentialsprompt.h create mode 100644 ui/widgets/accounts/credentialsprompt.ui diff --git a/CHANGELOG.md b/CHANGELOG.md index 410ff11..55ef1f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ ### New features - new "About" window with links, license, gratitudes +- if the authentication failed Squawk will ask againg for your password and login ## Squawk 0.2.1 (Apr 02, 2022) ### Bug fixes diff --git a/core/account.cpp b/core/account.cpp index 4d0480f..3b9d7ec 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -43,6 +43,7 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& reconnectTimer(new QTimer), network(p_net), passwordType(Shared::AccountPassword::plain), + lastError(Error::none), pepSupport(false), active(p_active), notReadyPassword(false), @@ -183,6 +184,7 @@ void Core::Account::onClientStateChange(QXmppClient::State st) dm->requestItems(getServer()); dm->requestInfo(getServer()); } + lastError = Error::none; emit connectionStateChanged(state); } } else { @@ -415,6 +417,7 @@ void Core::Account::onClientError(QXmppClient::Error err) qDebug() << "Error"; QString errorText; QString errorType; + lastError = Error::other; switch (err) { case QXmppClient::SocketError: errorText = client.socketErrorString(); @@ -456,6 +459,7 @@ void Core::Account::onClientError(QXmppClient::Error err) break; case QXmppStanza::Error::NotAuthorized: errorText = "Authentication error"; + lastError = Error::authentication; break; #if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 3, 0) case QXmppStanza::Error::PaymentRequired: @@ -750,3 +754,6 @@ void Core::Account::renameContactRequest(const QString& jid, const QString& newN void Core::Account::invalidatePassword() { notReadyPassword = true;} +Core::Account::Error Core::Account::getLastError() const { + return lastError;} + diff --git a/core/account.h b/core/account.h index aa65b27..2c9ec70 100644 --- a/core/account.h +++ b/core/account.h @@ -61,6 +61,12 @@ class Account : public QObject friend class RosterHandler; friend class VCardHandler; public: + enum class Error { + authentication, + other, + none + }; + Account( const QString& p_login, const QString& p_server, @@ -82,6 +88,7 @@ public: QString getFullJid() const; Shared::Availability getAvailability() const; Shared::AccountPassword getPasswordType() const; + Error getLastError() const; bool getActive() const; void setName(const QString& p_name); @@ -166,6 +173,7 @@ private: NetworkAccess* network; Shared::AccountPassword passwordType; + Error lastError; bool pepSupport; bool active; bool notReadyPassword; diff --git a/core/squawk.cpp b/core/squawk.cpp index 49a2b34..0f8fe9f 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -260,7 +260,10 @@ void Core::Squawk::disconnectAccount(const QString& account) void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_state) { Account* acc = static_cast(sender()); - emit changeAccount(acc->getName(), {{"state", QVariant::fromValue(p_state)}}); + emit changeAccount(acc->getName(), { + {"state", QVariant::fromValue(p_state)}, + {"error", ""} + }); #ifdef WITH_KWALLET if (p_state == Shared::ConnectionState::connected) { @@ -398,6 +401,7 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMapgetState(); QMap::const_iterator mItr; bool needToReconnect = false; + bool wentReconnecting = false; mItr = map.find("login"); if (mItr != map.end()) { @@ -428,6 +432,7 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMaptoBool() == acc->getActive()) { if (needToReconnect && st != Shared::ConnectionState::disconnected) { acc->reconnect(); + wentReconnecting = true; } } else { acc->setActive(mItr->toBool()); @@ -468,8 +473,12 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMapgetActive() && state != Shared::Availability::offline) { - acc->connect(); + if (state != Shared::Availability::offline) { + if (activeChanged && acc->getActive()) { + acc->connect(); + } else if (!wentReconnecting && acc->getActive() && acc->getLastError() == Account::Error::authentication) { + acc->connect(); + } } emit changeAccount(name, map); @@ -479,6 +488,10 @@ void Core::Squawk::onAccountError(const QString& text) { Account* acc = static_cast(sender()); emit changeAccount(acc->getName(), {{"error", text}}); + + if (acc->getLastError() == Account::Error::authentication) { + emit requestPassword(acc->getName(), true); + } } void Core::Squawk::removeAccountRequest(const QString& name) @@ -733,7 +746,7 @@ void Core::Squawk::onAccountNeedPassword() Account* acc = static_cast(sender()); switch (acc->getPasswordType()) { case Shared::AccountPassword::alwaysAsk: - emit requestPassword(acc->getName()); + emit requestPassword(acc->getName(), false); break; case Shared::AccountPassword::kwallet: { #ifdef WITH_KWALLET @@ -741,7 +754,7 @@ void Core::Squawk::onAccountNeedPassword() kwallet.requestReadPassword(acc->getName()); } else { #endif - emit requestPassword(acc->getName()); + emit requestPassword(acc->getName(), false); #ifdef WITH_KWALLET } #endif @@ -754,7 +767,7 @@ void Core::Squawk::onAccountNeedPassword() void Core::Squawk::onWalletRejectPassword(const QString& login) { - emit requestPassword(login); + emit requestPassword(login, false); } void Core::Squawk::responsePassword(const QString& account, const QString& password) diff --git a/core/squawk.h b/core/squawk.h index 6cb3115..c82b1c8 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -86,7 +86,7 @@ signals: void responseVCard(const QString& jid, const Shared::VCard& card); void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap& data); - void requestPassword(const QString& account); + void requestPassword(const QString& account, bool authernticationError); public slots: void start(); diff --git a/ui/dialogqueue.cpp b/ui/dialogqueue.cpp index f5be82b..02f8688 100644 --- a/ui/dialogqueue.cpp +++ b/ui/dialogqueue.cpp @@ -76,16 +76,31 @@ void DialogQueue::performNextAction() case none: actionDone(); break; - case askPassword: - prompt = new QInputDialog(squawk); - connect(prompt, &QDialog::accepted, this, &DialogQueue::onPropmtAccepted); - connect(prompt, &QDialog::rejected, this, &DialogQueue::onPropmtRejected); - prompt->setInputMode(QInputDialog::TextInput); - prompt->setTextEchoMode(QLineEdit::Password); - prompt->setLabelText(tr("Input the password for account %1").arg(currentSource)); - prompt->setWindowTitle(tr("Password for account %1").arg(currentSource)); - prompt->setTextValue(""); - prompt->exec(); + case askPassword: { + QInputDialog* dialog = new QInputDialog(squawk); + prompt = dialog; + connect(dialog, &QDialog::accepted, this, &DialogQueue::onPropmtAccepted); + connect(dialog, &QDialog::rejected, this, &DialogQueue::onPropmtRejected); + dialog->setInputMode(QInputDialog::TextInput); + dialog->setTextEchoMode(QLineEdit::Password); + dialog->setLabelText(tr("Input the password for account %1").arg(currentSource)); + dialog->setWindowTitle(tr("Password for account %1").arg(currentSource)); + dialog->setTextValue(""); + dialog->exec(); + } + break; + case askCredentials: { + CredentialsPrompt* dialog = new CredentialsPrompt(squawk); + prompt = dialog; + connect(dialog, &QDialog::accepted, this, &DialogQueue::onPropmtAccepted); + connect(dialog, &QDialog::rejected, this, &DialogQueue::onPropmtRejected); + Models::Account* acc = squawk->rosterModel.getAccount(currentSource); + dialog->setAccount(currentSource); + dialog->setLogin(acc->getLogin()); + dialog->setPassword(acc->getPassword()); + dialog->exec(); + } + break; } } @@ -94,8 +109,18 @@ void DialogQueue::onPropmtAccepted() switch (currentAction) { case none: break; - case askPassword: - emit squawk->responsePassword(currentSource, prompt->textValue()); + case askPassword: { + QInputDialog* dialog = static_cast(prompt); + emit squawk->responsePassword(currentSource, dialog->textValue()); + } + break; + case askCredentials: { + CredentialsPrompt* dialog = static_cast(prompt); + emit squawk->modifyAccountRequest(currentSource, { + {"login", dialog->getLogin()}, + {"password", dialog->getPassword()} + }); + } break; } actionDone(); @@ -107,6 +132,7 @@ void DialogQueue::onPropmtRejected() case none: break; case askPassword: + case askCredentials: emit squawk->disconnectAccount(currentSource); break; } diff --git a/ui/dialogqueue.h b/ui/dialogqueue.h index c5bf011..bfc1f21 100644 --- a/ui/dialogqueue.h +++ b/ui/dialogqueue.h @@ -24,9 +24,7 @@ #include #include -/** - * @todo write docs - */ +#include class Squawk; @@ -36,7 +34,8 @@ class DialogQueue : public QObject public: enum Action { none, - askPassword + askPassword, + askCredentials }; DialogQueue(Squawk* squawk); @@ -85,7 +84,7 @@ private: Collection& collection; Sequence& sequence; - QInputDialog* prompt; + QDialog* prompt; Squawk* squawk; }; diff --git a/ui/squawk.cpp b/ui/squawk.cpp index a447458..a0f16b2 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -880,8 +880,14 @@ void Squawk::onItemCollepsed(const QModelIndex& index) } } -void Squawk::requestPassword(const QString& account) { - dialogueQueue.addAction(account, DialogQueue::askPassword);} +void Squawk::requestPassword(const QString& account, bool authenticationError) { + if (authenticationError) { + dialogueQueue.addAction(account, DialogQueue::askCredentials); + } else { + dialogueQueue.addAction(account, DialogQueue::askPassword); + } + +} void Squawk::subscribeConversation(Conversation* conv) { diff --git a/ui/squawk.h b/ui/squawk.h index 6ee666c..5a77f17 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -51,7 +51,7 @@ class Squawk; class Squawk : public QMainWindow { Q_OBJECT - + friend class DialogQueue; public: explicit Squawk(QWidget *parent = nullptr); ~Squawk() override; @@ -114,7 +114,7 @@ public slots: void fileUploadComplete(const std::list msgs, const QString& url, const QString& path); void responseVCard(const QString& jid, const Shared::VCard& card); void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap& data); - void requestPassword(const QString& account); + void requestPassword(const QString& account, bool authenticationError); private: typedef std::map Conversations; diff --git a/ui/widgets/accounts/CMakeLists.txt b/ui/widgets/accounts/CMakeLists.txt index ad2f117..970985d 100644 --- a/ui/widgets/accounts/CMakeLists.txt +++ b/ui/widgets/accounts/CMakeLists.txt @@ -5,4 +5,7 @@ target_sources(squawk PRIVATE accounts.cpp accounts.h accounts.ui + credentialsprompt.cpp + credentialsprompt.h + credentialsprompt.ui ) diff --git a/ui/widgets/accounts/credentialsprompt.cpp b/ui/widgets/accounts/credentialsprompt.cpp new file mode 100644 index 0000000..3d1bafa --- /dev/null +++ b/ui/widgets/accounts/credentialsprompt.cpp @@ -0,0 +1,60 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "credentialsprompt.h" +#include "ui_credentialsprompt.h" + +CredentialsPrompt::CredentialsPrompt(QWidget* parent): + QDialog(parent), + m_ui(new Ui::CredentialsPrompt), + title(), + message() +{ + m_ui->setupUi(this); + + title = windowTitle(); + message = m_ui->message->text(); +} + +CredentialsPrompt::~CredentialsPrompt() +{ +} + +void CredentialsPrompt::setAccount(const QString& account) +{ + m_ui->message->setText(message.arg(account)); + setWindowTitle(title.arg(account)); +} + +QString CredentialsPrompt::getLogin() const +{ + return m_ui->login->text(); +} + +QString CredentialsPrompt::getPassword() const +{ + return m_ui->password->text(); +} + +void CredentialsPrompt::setLogin(const QString& login) +{ + m_ui->login->setText(login); +} + +void CredentialsPrompt::setPassword(const QString& password) +{ + m_ui->password->setText(password); +} diff --git a/ui/widgets/accounts/credentialsprompt.h b/ui/widgets/accounts/credentialsprompt.h new file mode 100644 index 0000000..ce9a791 --- /dev/null +++ b/ui/widgets/accounts/credentialsprompt.h @@ -0,0 +1,52 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 CREDENTIALSPROMPT_H +#define CREDENTIALSPROMPT_H + +#include +#include + +namespace Ui +{ +class CredentialsPrompt; +} + +/** + * @todo write docs + */ +class CredentialsPrompt : public QDialog +{ + Q_OBJECT + +public: + CredentialsPrompt(QWidget* parent = nullptr); + ~CredentialsPrompt(); + + void setAccount(const QString& account); + void setLogin(const QString& login); + void setPassword(const QString& password); + + QString getLogin() const; + QString getPassword() const; + +private: + QScopedPointer m_ui; + QString title; + QString message; +}; + +#endif // CREDENTIALSPROMPT_H diff --git a/ui/widgets/accounts/credentialsprompt.ui b/ui/widgets/accounts/credentialsprompt.ui new file mode 100644 index 0000000..2ad4d8d --- /dev/null +++ b/ui/widgets/accounts/credentialsprompt.ui @@ -0,0 +1,144 @@ + + + CredentialsPrompt + + + + 0 + 0 + 318 + 229 + + + + Authentication error: %1 + + + + + + true + + + + 0 + 0 + + + + Couldn't authenticate account %1: login or password is icorrect. +Would you like to check them and try again? + + + Qt::AlignCenter + + + true + + + + + + + + + Login + + + + + + + Your account login (without @server.domain) + + + + + + + Password + + + + + + + Your password + + + Qt::ImhHiddenText|Qt::ImhNoAutoUppercase|Qt::ImhNoPredictiveText|Qt::ImhSensitiveData + + + + + + QLineEdit::Password + + + false + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + CredentialsPrompt + accept() + + + 20 + 20 + + + 20 + 20 + + + + + buttonBox + rejected() + CredentialsPrompt + reject() + + + 20 + 20 + + + 20 + 20 + + + + + From 51ac1ac709042cb61f122860de5f47c8f82e8e63 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 17 Apr 2022 14:58:46 +0300 Subject: [PATCH 045/137] first attempt --- ui/utils/CMakeLists.txt | 2 + ui/utils/textmeter.cpp | 233 +++++++++++++++++++++ ui/utils/textmeter.h | 68 ++++++ ui/widgets/messageline/messagedelegate.cpp | 11 +- ui/widgets/messageline/messagedelegate.h | 2 + 5 files changed, 311 insertions(+), 5 deletions(-) create mode 100644 ui/utils/textmeter.cpp create mode 100644 ui/utils/textmeter.h diff --git a/ui/utils/CMakeLists.txt b/ui/utils/CMakeLists.txt index b46d30d..823287d 100644 --- a/ui/utils/CMakeLists.txt +++ b/ui/utils/CMakeLists.txt @@ -15,4 +15,6 @@ target_sources(squawk PRIVATE resizer.h shadowoverlay.cpp shadowoverlay.h + textmeter.cpp + textmeter.h ) diff --git a/ui/utils/textmeter.cpp b/ui/utils/textmeter.cpp new file mode 100644 index 0000000..51c6d54 --- /dev/null +++ b/ui/utils/textmeter.cpp @@ -0,0 +1,233 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "textmeter.h" +#include +#include + +TextMeter::TextMeter(): + base(), + fonts() +{ +} + +TextMeter::~TextMeter() +{ +} + +void TextMeter::initializeFonts(const QFont& font) +{ + fonts.clear(); + QList supported = base.writingSystems(font.family()); + std::set sup; + std::set added({font.family()}); + for (const QFontDatabase::WritingSystem& system : supported) { + sup.insert(system); + } + fonts.push_back(QFontMetrics(font)); + QString style = base.styleString(font); + + QList systems = base.writingSystems(); + for (const QFontDatabase::WritingSystem& system : systems) { + if (sup.count(system) == 0) { + QStringList families = base.families(system); + if (!families.empty() && added.count(families.first()) == 0) { + QString family(families.first()); + QFont nfont = base.font(family, style, font.pointSize()); + if (added.count(nfont.family()) == 0) { + added.insert(family); + fonts.push_back(QFontMetrics(nfont)); + qDebug() << "Added font" << nfont.family() << "for" << system; + } + } + } + } +} + +QSize TextMeter::boundingSize(const QString& text, const QSize& limits) const +{ +// QString str("ridiculus mus. Suspendisse potenti. Cras pretium venenatis enim, faucibus accumsan ex"); +// bool first = true; +// int width = 0; +// QStringList list = str.split(" "); +// QFontMetrics m = fonts.front(); +// for (const QString& word : list) { +// if (first) { +// first = false; +// } else { +// width += m.horizontalAdvance(QChar::Space); +// } +// width += m.horizontalAdvance(word); +// for (const QChar& ch : word) { +// width += m.horizontalAdvance(ch); +// } +// } +// qDebug() << "together:" << m.horizontalAdvance(str); +// qDebug() << "apart:" << width; +// I cant measure or wrap text this way, this simple example shows that even this gives differen result +// The Qt implementation under it is thousands and thousands lines of code in QTextEngine +// I simply can't get though it + + if (text.size() == 0) { + return QSize (0, 0); + } + Helper current(limits.width()); + for (const QChar& ch : text) { + if (newLine(ch)) { + current.computeNewWord(); + if (current.height == 0) { + current.height = fonts.front().lineSpacing(); + } + current.beginNewLine(); + } else if (visible(ch)) { + bool found = false; + for (const QFontMetrics& metrics : fonts) { + if (metrics.inFont(ch)) { + current.computeChar(ch, metrics); + found = true; + break; + } + } + + if (!found) { + current.computeChar(ch, fonts.front()); + } + } + } + current.computeNewWord(); + current.beginNewLine(); + + int& height = current.size.rheight(); + if (height > 0) { + height -= fonts.front().leading(); + } + + return current.size; +} + +void TextMeter::Helper::computeChar(const QChar& ch, const QFontMetrics& metrics) +{ + int ha = metrics.horizontalAdvance(ch); + if (newWord(ch)) { + if (printOnLineBreak(ch)) { + if (!lineOverflow(metrics, ha, ch)){ + computeNewWord(); + } + } else { + computeNewWord(); + delayedSpaceWidth = ha; + lastSpace = ch; + } + } else { + lineOverflow(metrics, ha, ch); + } +} + +void TextMeter::Helper::computeNewLine(const QFontMetrics& metrics, int horizontalAdvance, const QChar& ch) +{ + if (wordBeganWithTheLine) { + text = word.chopped(1); + width = wordWidth - horizontalAdvance; + height = wordHeight; + } + if (width != metrics.horizontalAdvance(text)) { + qDebug() << "Kerning Error" << width - metrics.horizontalAdvance(text); + } + beginNewLine(); + if (wordBeganWithTheLine) { + word = ch; + wordWidth = horizontalAdvance; + wordHeight = metrics.lineSpacing(); + } + + wordBeganWithTheLine = true; + delayedSpaceWidth = 0; + lastSpace = QChar::Null; +} + +void TextMeter::Helper::beginNewLine() +{ + size.rheight() += height; + size.rwidth() = qMax(size.width(), width); + qDebug() << text; + text = ""; + width = 0; + height = 0; +} + +void TextMeter::Helper::computeNewWord() +{ + width += wordWidth + delayedSpaceWidth; + height = qMax(height, wordHeight); + if (lastSpace != QChar::Null) { + text += lastSpace; + } + text += word; + word = ""; + wordWidth = 0; + wordHeight = 0; + delayedSpaceWidth = 0; + lastSpace = QChar::Null; + wordBeganWithTheLine = false; +} + +bool TextMeter::Helper::lineOverflow(const QFontMetrics& metrics, int horizontalAdvance, const QChar& ch) +{ + wordHeight = qMax(wordHeight, metrics.lineSpacing()); + wordWidth += horizontalAdvance; + word += ch; + if (width + delayedSpaceWidth + wordWidth > maxWidth) { + computeNewLine(metrics, horizontalAdvance, ch); + return true; + } + return false; +} + + +bool TextMeter::newLine(const QChar& ch) +{ + return ch == QChar::LineFeed; +} + +bool TextMeter::newWord(const QChar& ch) +{ + return ch.isSpace() || ch.isPunct(); +} + +bool TextMeter::printOnLineBreak(const QChar& ch) +{ + return ch != QChar::Space; +} + +bool TextMeter::visible(const QChar& ch) +{ + return true; +} + +TextMeter::Helper::Helper(int p_maxWidth): + width(0), + height(0), + wordWidth(0), + wordHeight(0), + delayedSpaceWidth(0), + maxWidth(p_maxWidth), + wordBeganWithTheLine(true), + text(""), + word(""), + lastSpace(QChar::Null), + size(0, 0) +{ +} diff --git a/ui/utils/textmeter.h b/ui/utils/textmeter.h new file mode 100644 index 0000000..243d989 --- /dev/null +++ b/ui/utils/textmeter.h @@ -0,0 +1,68 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 TEXTMETER_H +#define TEXTMETER_H + +#include +#include + +#include +#include +#include +#include + +class TextMeter +{ +public: + TextMeter(); + ~TextMeter(); + void initializeFonts(const QFont& font); + QSize boundingSize(const QString& text, const QSize& limits) const; + +private: + QFontDatabase base; + std::list fonts; + + struct Helper { + Helper(int maxWidth); + int width; + int height; + int wordWidth; + int wordHeight; + int delayedSpaceWidth; + int maxWidth; + bool wordBeganWithTheLine; + QString text; + QString word; + QChar lastSpace; + QSize size; + + void computeNewLine(const QFontMetrics& metrics, int horizontalAdvance, const QChar& ch); + void computeChar(const QChar& ch, const QFontMetrics& metrics); + void computeNewWord(); + void beginNewLine(); + bool lineOverflow(const QFontMetrics& metrics, int horizontalAdvance, const QChar& ch); + }; + + static bool newLine(const QChar& ch); + static bool newWord(const QChar& ch); + static bool visible(const QChar& ch); + static bool printOnLineBreak(const QChar& ch); + +}; + +#endif // TEXTMETER_H diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index 15a5e46..4ddecee 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -42,6 +42,7 @@ MessageDelegate::MessageDelegate(QObject* parent): nickFont(), dateFont(), bodyMetrics(bodyFont), + bodyMeter(), nickMetrics(nickFont), dateMetrics(dateFont), buttonHeight(0), @@ -123,10 +124,7 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio opt.displayAlignment = Qt::AlignRight | Qt::AlignTop; } - QSize bodySize(0, 0); - if (data.text.size() > 0) { - bodySize = bodyMetrics.boundingRect(opt.rect, Qt::TextWordWrap, data.text).size(); - } + QSize bodySize = bodyMeter.boundingSize(data.text, opt.rect.size()); QRect rect; if (ntds) { @@ -306,7 +304,7 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel Models::FeedItem data = qvariant_cast(vi); QSize messageSize(0, 0); if (data.text.size() > 0) { - messageSize = bodyMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.text).size(); + messageSize = bodyMeter.boundingSize(data.text, messageRect.size()); messageSize.rheight() += textMargin; } @@ -390,9 +388,12 @@ void MessageDelegate::initializeFonts(const QFont& font) dateFont.setPointSize(dateFont.pointSize() * dateFontMultiplier); } + bodyFont.setKerning(false); bodyMetrics = QFontMetrics(bodyFont); nickMetrics = QFontMetrics(nickFont); dateMetrics = QFontMetrics(dateFont); + + bodyMeter.initializeFonts(bodyFont); Preview::initializeFont(bodyFont); } diff --git a/ui/widgets/messageline/messagedelegate.h b/ui/widgets/messageline/messagedelegate.h index 9225412..38ec0ee 100644 --- a/ui/widgets/messageline/messagedelegate.h +++ b/ui/widgets/messageline/messagedelegate.h @@ -34,6 +34,7 @@ #include "shared/global.h" #include "shared/utils.h" #include "shared/pathcheck.h" +#include "ui/utils/textmeter.h" #include "preview.h" @@ -94,6 +95,7 @@ private: QFont nickFont; QFont dateFont; QFontMetrics bodyMetrics; + TextMeter bodyMeter; QFontMetrics nickMetrics; QFontMetrics dateMetrics; From 4c20a314f050236fbcb79fa2d43322ec9045eaa1 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 17 Apr 2022 16:25:15 +0300 Subject: [PATCH 046/137] a crash fix on one of archive corner cases --- CHANGELOG.md | 1 + core/rosteritem.cpp | 28 ++++++++++++++++++---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55ef1f1..4daf652 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Bug fixes - now when you remove an account it actually gets removed - segfault on unitialized Availability in some rare occesions +- fixed crash when you open a dialog with someone that has only error messages in archive ### Improvements - there is a way to disable an account and it wouldn't connect when you change availability diff --git a/core/rosteritem.cpp b/core/rosteritem.cpp index b1951d6..1b8d1e6 100644 --- a/core/rosteritem.cpp +++ b/core/rosteritem.cpp @@ -124,15 +124,19 @@ void Core::RosterItem::nextRequest() if (requestedCount != -1) { bool last = false; if (archiveState == beginning || archiveState == complete) { - QString firstId = archive->oldestId(); - if (responseCache.size() == 0) { - if (requestedBefore == firstId) { - last = true; - } - } else { - if (responseCache.front().getId() == firstId) { - last = true; + try { + QString firstId = archive->oldestId(); + if (responseCache.size() == 0) { + if (requestedBefore == firstId) { + last = true; + } + } else { + if (responseCache.front().getId() == firstId) { + last = true; + } } + } catch (const Archive::Empty& e) { + last = true; } } else if (archiveState == empty && responseCache.size() == 0) { last = true; @@ -171,8 +175,12 @@ void Core::RosterItem::performRequest(int count, const QString& before) requestCache.emplace_back(requestedCount, before); requestedCount = -1; } - Shared::Message msg = archive->newest(); - emit needHistory("", getId(msg), msg.getTime()); + try { + Shared::Message msg = archive->newest(); + emit needHistory("", getId(msg), msg.getTime()); + } catch (const Archive::Empty& e) { //this can happen when the only message in archive is not server stored (error, for example) + emit needHistory(before, ""); + } } break; case end: From 18859cb960aa6de0dbb393186392b3449b5177ff Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 18 Apr 2022 19:54:42 +0300 Subject: [PATCH 047/137] first ideas for notifications --- shared/global.cpp | 35 +++++++++++++++++++++++++++++++++++ shared/global.h | 14 ++++++++++++-- ui/models/roster.cpp | 4 ++-- ui/models/roster.h | 4 ++-- ui/squawk.cpp | 31 ++----------------------------- ui/squawk.h | 5 ++--- 6 files changed, 55 insertions(+), 38 deletions(-) diff --git a/shared/global.cpp b/shared/global.cpp index 122bc79..14ae90d 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -19,6 +19,7 @@ #include "global.h" #include "enums.h" +#include "ui/models/roster.h" Shared::Global* Shared::Global::instance = 0; const std::set Shared::Global::supportedImagesExts = {"png", "jpg", "webp", "jpeg", "gif", "svg"}; @@ -94,6 +95,8 @@ Shared::Global::Global(): }), defaultSystemStyle(QApplication::style()->objectName()), defaultSystemPalette(QApplication::palette()), + rosterModel(new Models::Roster()), + dbus("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus()), pluginSupport({ {"KWallet", false}, {"openFileManagerWindowJob", false}, @@ -349,6 +352,38 @@ void Shared::Global::setStyle(const QString& style) } } +void Shared::Global::notify(const QString& account, const Shared::Message& msg) +{ + QString name = QString(instance->rosterModel->getContactName(account, msg.getPenPalJid())); + QString path = QString(instance->rosterModel->getContactIconPath(account, msg.getPenPalJid(), msg.getPenPalResource())); + QVariantList args; + args << QString(QCoreApplication::applicationName()); + args << QVariant(QVariant::UInt); //TODO some normal id + if (path.size() > 0) { + args << path; + } else { + args << QString("mail-message"); //TODO should here better be unknown user icon? + } + if (msg.getType() == Shared::Message::groupChat) { + args << msg.getFromResource() + " from " + name; + } else { + args << name; + } + + QString body(msg.getBody()); + QString oob(msg.getOutOfBandUrl()); + if (body == oob) { + body = tr("Attached file"); + } + + args << body; + args << QStringList(); + args << QVariantMap(); + args << 3000; + instance->dbus.callWithArgumentList(QDBus::AutoDetect, "Notify", args); +} + + #define FROM_INT_INPL(Enum) \ template<> \ Enum Shared::Global::fromInt(int src) \ diff --git a/shared/global.h b/shared/global.h index 2056639..fcd8105 100644 --- a/shared/global.h +++ b/shared/global.h @@ -42,12 +42,18 @@ #include #include #include +#include + +class Squawk; +namespace Models { + class Roster; +} namespace Shared { class Global { Q_DECLARE_TR_FUNCTIONS(Global) - + friend class ::Squawk; public: struct FileInfo { enum class Preview { @@ -64,7 +70,9 @@ namespace Shared { }; Global(); - + + static void notify(const QString& account, const Shared::Message& msg); + static Global* getInstance(); static QString getName(Availability av); static QString getName(ConnectionState cs); @@ -122,6 +130,8 @@ namespace Shared { private: static Global* instance; + Models::Roster* rosterModel; + QDBusInterface dbus; std::map pluginSupport; std::map fileCache; diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index 1355fe3..e5ada43 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -763,7 +763,7 @@ void Models::Roster::removeAccount(const QString& account) acc->deleteLater(); } -QString Models::Roster::getContactName(const QString& account, const QString& jid) +QString Models::Roster::getContactName(const QString& account, const QString& jid) const { ElId id(account, jid); std::map::const_iterator cItr = contacts.find(id); @@ -907,7 +907,7 @@ bool Models::Roster::groupHasContact(const QString& account, const QString& grou } } -QString Models::Roster::getContactIconPath(const QString& account, const QString& jid, const QString& resource) +QString Models::Roster::getContactIconPath(const QString& account, const QString& jid, const QString& resource) const { ElId id(account, jid); std::map::const_iterator cItr = contacts.find(id); diff --git a/ui/models/roster.h b/ui/models/roster.h index 08d5afc..28f4d30 100644 --- a/ui/models/roster.h +++ b/ui/models/roster.h @@ -65,7 +65,7 @@ public: void addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data); void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data); void removeRoomParticipant(const QString& account, const QString& jid, const QString& name); - QString getContactName(const QString& account, const QString& jid); + QString getContactName(const QString& account, const QString& jid) const; QVariant data ( const QModelIndex& index, int role ) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; @@ -77,7 +77,7 @@ public: std::deque groupList(const QString& account) const; bool groupHasContact(const QString& account, const QString& group, const QString& contactJID) const; - QString getContactIconPath(const QString& account, const QString& jid, const QString& resource); + QString getContactIconPath(const QString& account, const QString& jid, const QString& resource) const; Account* getAccount(const QString& name); QModelIndex getAccountIndex(const QString& name); QModelIndex getGroupIndex(const QString& account, const QString& name); diff --git a/ui/squawk.cpp b/ui/squawk.cpp index a0f16b2..a08f38b 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -28,10 +28,9 @@ Squawk::Squawk(QWidget *parent) : preferences(nullptr), about(nullptr), dialogueQueue(this), - rosterModel(), + rosterModel(*(Shared::Global::getInstance()->rosterModel)), conversations(), contextMenu(new QMenu()), - dbus("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus()), vCards(), currentConversation(nullptr), restoreSelection(), @@ -441,33 +440,7 @@ void Squawk::changeMessage(const QString& account, const QString& jid, const QSt void Squawk::notify(const QString& account, const Shared::Message& msg) { - QString name = QString(rosterModel.getContactName(account, msg.getPenPalJid())); - QString path = QString(rosterModel.getContactIconPath(account, msg.getPenPalJid(), msg.getPenPalResource())); - QVariantList args; - args << QString(QCoreApplication::applicationName()); - args << QVariant(QVariant::UInt); //TODO some normal id - if (path.size() > 0) { - args << path; - } else { - args << QString("mail-message"); //TODO should here better be unknown user icon? - } - if (msg.getType() == Shared::Message::groupChat) { - args << msg.getFromResource() + " from " + name; - } else { - args << name; - } - - QString body(msg.getBody()); - QString oob(msg.getOutOfBandUrl()); - if (body == oob) { - body = tr("Attached file"); - } - - args << body; - args << QStringList(); - args << QVariantMap(); - args << 3000; - dbus.callWithArgumentList(QDBus::AutoDetect, "Notify", args); + Shared::Global::notify(account, msg); } void Squawk::onConversationMessage(const Shared::Message& msg) diff --git a/ui/squawk.h b/ui/squawk.h index 5a77f17..aa52153 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -43,6 +42,7 @@ #include "dialogqueue.h" #include "shared/shared.h" +#include "shared/global.h" namespace Ui { class Squawk; @@ -124,10 +124,9 @@ private: Settings* preferences; About* about; DialogQueue dialogueQueue; - Models::Roster rosterModel; + Models::Roster& rosterModel; Conversations conversations; QMenu* contextMenu; - QDBusInterface dbus; std::map vCards; Conversation* currentConversation; QModelIndex restoreSelection; From 83cb2201752e225b0bfb9ce5d85caafa3e8cf97e Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 19 Apr 2022 20:24:41 +0300 Subject: [PATCH 048/137] better notification sending, edited message now modifies notification (or sends), little structure change --- core/CMakeLists.txt | 7 +------ core/networkaccess.h | 2 +- core/rosteritem.h | 2 +- core/storage/CMakeLists.txt | 8 ++++++++ core/{ => storage}/archive.cpp | 0 core/{ => storage}/archive.h | 0 core/{ => storage}/storage.cpp | 0 core/{ => storage}/storage.h | 0 core/{ => storage}/urlstorage.cpp | 0 core/{ => storage}/urlstorage.h | 0 shared/global.cpp | 14 ++++++++++---- ui/models/roster.cpp | 8 ++++---- ui/widgets/messageline/messagefeed.cpp | 6 ++++++ 13 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 core/storage/CMakeLists.txt rename core/{ => storage}/archive.cpp (100%) rename core/{ => storage}/archive.h (100%) rename core/{ => storage}/storage.cpp (100%) rename core/{ => storage}/storage.h (100%) rename core/{ => storage}/urlstorage.cpp (100%) rename core/{ => storage}/urlstorage.h (100%) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 8b6fa69..8f49b11 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -8,8 +8,6 @@ target_sources(squawk PRIVATE account.h adapterfunctions.cpp adapterfunctions.h - archive.cpp - archive.h conference.cpp conference.h contact.cpp @@ -23,13 +21,10 @@ target_sources(squawk PRIVATE signalcatcher.h squawk.cpp squawk.h - storage.cpp - storage.h - urlstorage.cpp - urlstorage.h ) target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS}) add_subdirectory(handlers) +add_subdirectory(storage) add_subdirectory(passwordStorageEngines) diff --git a/core/networkaccess.h b/core/networkaccess.h index 0b7bb7d..6ddfa99 100644 --- a/core/networkaccess.h +++ b/core/networkaccess.h @@ -30,7 +30,7 @@ #include -#include "urlstorage.h" +#include "storage/urlstorage.h" #include "shared/pathcheck.h" namespace Core { diff --git a/core/rosteritem.h b/core/rosteritem.h index d422e3f..5f99017 100644 --- a/core/rosteritem.h +++ b/core/rosteritem.h @@ -34,7 +34,7 @@ #include "shared/enums.h" #include "shared/message.h" #include "shared/vcard.h" -#include "archive.h" +#include "storage/archive.h" #include "adapterfunctions.h" namespace Core { diff --git a/core/storage/CMakeLists.txt b/core/storage/CMakeLists.txt new file mode 100644 index 0000000..4c263d5 --- /dev/null +++ b/core/storage/CMakeLists.txt @@ -0,0 +1,8 @@ +target_sources(squawk PRIVATE + archive.cpp + archive.h + storage.cpp + storage.h + urlstorage.cpp + urlstorage.h +) diff --git a/core/archive.cpp b/core/storage/archive.cpp similarity index 100% rename from core/archive.cpp rename to core/storage/archive.cpp diff --git a/core/archive.h b/core/storage/archive.h similarity index 100% rename from core/archive.h rename to core/storage/archive.h diff --git a/core/storage.cpp b/core/storage/storage.cpp similarity index 100% rename from core/storage.cpp rename to core/storage/storage.cpp diff --git a/core/storage.h b/core/storage/storage.h similarity index 100% rename from core/storage.h rename to core/storage/storage.h diff --git a/core/urlstorage.cpp b/core/storage/urlstorage.cpp similarity index 100% rename from core/urlstorage.cpp rename to core/storage/urlstorage.cpp diff --git a/core/urlstorage.h b/core/storage/urlstorage.h similarity index 100% rename from core/urlstorage.h rename to core/storage/urlstorage.h diff --git a/shared/global.cpp b/shared/global.cpp index 14ae90d..be660bd 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -357,8 +357,9 @@ void Shared::Global::notify(const QString& account, const Shared::Message& msg) QString name = QString(instance->rosterModel->getContactName(account, msg.getPenPalJid())); QString path = QString(instance->rosterModel->getContactIconPath(account, msg.getPenPalJid(), msg.getPenPalResource())); QVariantList args; - args << QString(QCoreApplication::applicationName()); - args << QVariant(QVariant::UInt); //TODO some normal id + args << QString(); + + args << qHash(msg.getId()); if (path.size() > 0) { args << path; } else { @@ -378,8 +379,13 @@ void Shared::Global::notify(const QString& account, const Shared::Message& msg) args << body; args << QStringList(); - args << QVariantMap(); - args << 3000; + args << QVariantMap({ + {"desktop-entry", QString(QCoreApplication::applicationName())}, + {"category", QString("message")}, + // {"sound-file", "/path/to/macaw/squawk"}, + {"sound-name", QString("message-new-instant")} + }); + args << -1; instance->dbus.callWithArgumentList(QDBus::AutoDetect, "Notify", args); } diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index e5ada43..b96ddda 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -801,10 +801,10 @@ void Models::Roster::addRoom(const QString& account, const QString jid, const QM } Room* room = new Room(acc, jid, data); - connect(room, &Contact::requestArchive, this, &Roster::onElementRequestArchive); - connect(room, &Contact::fileDownloadRequest, this, &Roster::fileDownloadRequest); - connect(room, &Contact::unnoticedMessage, this, &Roster::unnoticedMessage); - connect(room, &Contact::localPathInvalid, this, &Roster::localPathInvalid); + connect(room, &Room::requestArchive, this, &Roster::onElementRequestArchive); + connect(room, &Room::fileDownloadRequest, this, &Roster::fileDownloadRequest); + connect(room, &Room::unnoticedMessage, this, &Roster::unnoticedMessage); + connect(room, &Room::localPathInvalid, this, &Roster::localPathInvalid); rooms.insert(std::make_pair(id, room)); acc->appendChild(room); } diff --git a/ui/widgets/messageline/messagefeed.cpp b/ui/widgets/messageline/messagefeed.cpp index 33fbdd4..521e981 100644 --- a/ui/widgets/messageline/messagefeed.cpp +++ b/ui/widgets/messageline/messagefeed.cpp @@ -163,6 +163,12 @@ void Models::MessageFeed::changeMessage(const QString& id, const QMapgetForwarded() && changeRoles.count(MessageRoles::Text) > 0) { + unreadMessages->insert(id); + emit unreadMessagesCountChanged(); + emit unnoticedMessage(*msg); + } } } From 721d3a1a89fd4daf4b0ab0643aa48f3155f23954 Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 22 Apr 2022 18:26:18 +0300 Subject: [PATCH 049/137] refactoring: UI squawk now belongs to a new class, it enables me doing trayed mode, when main window is destroyed --- CMakeLists.txt | 1 + core/CMakeLists.txt | 1 - core/main.cpp | 201 -------------- main/CMakeLists.txt | 7 + main/application.cpp | 476 ++++++++++++++++++++++++++++++++ main/application.h | 111 ++++++++ {ui => main}/dialogqueue.cpp | 31 ++- {ui => main}/dialogqueue.h | 18 +- main/main.cpp | 139 ++++++++++ shared/global.cpp | 40 --- shared/global.h | 11 - ui/CMakeLists.txt | 2 - ui/models/roster.cpp | 46 +++- ui/models/roster.h | 10 +- ui/squawk.cpp | 509 ++++++----------------------------- ui/squawk.h | 74 ++--- 16 files changed, 908 insertions(+), 769 deletions(-) delete mode 100644 core/main.cpp create mode 100644 main/CMakeLists.txt create mode 100644 main/application.cpp create mode 100644 main/application.h rename {ui => main}/dialogqueue.cpp (87%) rename {ui => main}/dialogqueue.h (83%) create mode 100644 main/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 85aa98a..75011d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,6 +148,7 @@ if(CMAKE_COMPILER_IS_GNUCXX) target_compile_options(squawk PRIVATE ${COMPILE_OPTIONS}) endif(CMAKE_COMPILER_IS_GNUCXX) +add_subdirectory(main) add_subdirectory(core) add_subdirectory(external/simpleCrypt) add_subdirectory(packaging) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 8f49b11..6c7a3b5 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -12,7 +12,6 @@ target_sources(squawk PRIVATE conference.h contact.cpp contact.h - main.cpp networkaccess.cpp networkaccess.h rosteritem.cpp diff --git a/core/main.cpp b/core/main.cpp deleted file mode 100644 index 4fbb1f7..0000000 --- a/core/main.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * 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 "../shared/global.h" -#include "../shared/messageinfo.h" -#include "../shared/pathcheck.h" -#include "../ui/squawk.h" -#include "signalcatcher.h" -#include "squawk.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -int main(int argc, char *argv[]) -{ - qRegisterMetaType("Shared::Message"); - qRegisterMetaType("Shared::MessageInfo"); - qRegisterMetaType("Shared::VCard"); - qRegisterMetaType>("std::list"); - qRegisterMetaType>("std::list"); - qRegisterMetaType>("QSet"); - qRegisterMetaType("Shared::ConnectionState"); - qRegisterMetaType("Shared::Availability"); - - QApplication app(argc, argv); - SignalCatcher sc(&app); - - QApplication::setApplicationName("squawk"); - QApplication::setOrganizationName("macaw.me"); - QApplication::setApplicationDisplayName("Squawk"); - QApplication::setApplicationVersion("0.2.2"); - - QTranslator qtTranslator; - qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); - app.installTranslator(&qtTranslator); - - QTranslator myappTranslator; - QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); - bool found = false; - for (QString share : shares) { - found = myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n"); - if (found) { - break; - } - } - if (!found) { - myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath()); - } - - app.installTranslator(&myappTranslator); - - 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); - - new Shared::Global(); //translates enums - - QSettings settings; - QVariant vs = settings.value("style"); - if (vs.isValid()) { - QString style = vs.toString().toLower(); - if (style != "system") { - Shared::Global::setStyle(style); - } - } - if (Shared::Global::supported("colorSchemeTools")) { - QVariant vt = settings.value("theme"); - if (vt.isValid()) { - QString theme = vt.toString(); - if (theme.toLower() != "system") { - Shared::Global::setTheme(theme); - } - } - } - QString path = Shared::downloadsPathCheck(); - if (path.size() > 0) { - settings.setValue("downloadsPath", path); - } else { - qDebug() << "couldn't initialize directory for downloads, quitting"; - return -1; - } - - - Squawk w; - w.show(); - - Core::Squawk* squawk = new Core::Squawk(); - QThread* coreThread = new QThread(); - squawk->moveToThread(coreThread); - - QObject::connect(&sc, &SignalCatcher::interrupt, &app, &QApplication::closeAllWindows); - - QObject::connect(coreThread, &QThread::started, squawk, &Core::Squawk::start); - QObject::connect(&app, &QApplication::lastWindowClosed, squawk, &Core::Squawk::stop); - QObject::connect(&app, &QApplication::lastWindowClosed, &w, &Squawk::writeSettings); - //QObject::connect(&app, &QApplication::aboutToQuit, &w, &QMainWindow::close); - QObject::connect(squawk, &Core::Squawk::quit, squawk, &Core::Squawk::deleteLater); - QObject::connect(squawk, &Core::Squawk::quit, coreThread, &QThread::quit, Qt::QueuedConnection); - QObject::connect(coreThread, &QThread::finished, &app, &QApplication::quit, Qt::QueuedConnection); - - QObject::connect(&w, &Squawk::newAccountRequest, squawk, &Core::Squawk::newAccountRequest); - QObject::connect(&w, &Squawk::modifyAccountRequest, squawk, &Core::Squawk::modifyAccountRequest); - QObject::connect(&w, &Squawk::removeAccountRequest, squawk, &Core::Squawk::removeAccountRequest); - QObject::connect(&w, &Squawk::connectAccount, squawk, &Core::Squawk::connectAccount); - QObject::connect(&w, &Squawk::disconnectAccount, squawk, &Core::Squawk::disconnectAccount); - QObject::connect(&w, &Squawk::changeState, squawk, &Core::Squawk::changeState); - QObject::connect(&w, &Squawk::sendMessage, squawk,&Core::Squawk::sendMessage); - QObject::connect(&w, &Squawk::replaceMessage, squawk,&Core::Squawk::replaceMessage); - QObject::connect(&w, &Squawk::resendMessage, squawk,&Core::Squawk::resendMessage); - QObject::connect(&w, &Squawk::requestArchive, squawk, &Core::Squawk::requestArchive); - QObject::connect(&w, &Squawk::subscribeContact, squawk, &Core::Squawk::subscribeContact); - QObject::connect(&w, &Squawk::unsubscribeContact, squawk, &Core::Squawk::unsubscribeContact); - QObject::connect(&w, &Squawk::addContactRequest, squawk, &Core::Squawk::addContactRequest); - QObject::connect(&w, &Squawk::removeContactRequest, squawk, &Core::Squawk::removeContactRequest); - QObject::connect(&w, &Squawk::setRoomJoined, squawk, &Core::Squawk::setRoomJoined); - QObject::connect(&w, &Squawk::setRoomAutoJoin, squawk, &Core::Squawk::setRoomAutoJoin); - QObject::connect(&w, &Squawk::removeRoomRequest, squawk, &Core::Squawk::removeRoomRequest); - QObject::connect(&w, &Squawk::addRoomRequest, squawk, &Core::Squawk::addRoomRequest); - QObject::connect(&w, &Squawk::fileDownloadRequest, squawk, &Core::Squawk::fileDownloadRequest); - QObject::connect(&w, &Squawk::addContactToGroupRequest, squawk, &Core::Squawk::addContactToGroupRequest); - QObject::connect(&w, &Squawk::removeContactFromGroupRequest, squawk, &Core::Squawk::removeContactFromGroupRequest); - QObject::connect(&w, &Squawk::renameContactRequest, squawk, &Core::Squawk::renameContactRequest); - QObject::connect(&w, &Squawk::requestVCard, squawk, &Core::Squawk::requestVCard); - QObject::connect(&w, &Squawk::uploadVCard, squawk, &Core::Squawk::uploadVCard); - QObject::connect(&w, &Squawk::responsePassword, squawk, &Core::Squawk::responsePassword); - QObject::connect(&w, &Squawk::localPathInvalid, squawk, &Core::Squawk::onLocalPathInvalid); - QObject::connect(&w, &Squawk::changeDownloadsPath, squawk, &Core::Squawk::changeDownloadsPath); - - QObject::connect(squawk, &Core::Squawk::newAccount, &w, &Squawk::newAccount); - QObject::connect(squawk, &Core::Squawk::addContact, &w, &Squawk::addContact); - QObject::connect(squawk, &Core::Squawk::changeAccount, &w, &Squawk::changeAccount); - QObject::connect(squawk, &Core::Squawk::removeAccount, &w, &Squawk::removeAccount); - QObject::connect(squawk, &Core::Squawk::addGroup, &w, &Squawk::addGroup); - QObject::connect(squawk, &Core::Squawk::removeGroup, &w, &Squawk::removeGroup); - QObject::connect(squawk, qOverload(&Core::Squawk::removeContact), - &w, qOverload(&Squawk::removeContact)); - QObject::connect(squawk, qOverload(&Core::Squawk::removeContact), - &w, qOverload(&Squawk::removeContact)); - QObject::connect(squawk, &Core::Squawk::changeContact, &w, &Squawk::changeContact); - QObject::connect(squawk, &Core::Squawk::addPresence, &w, &Squawk::addPresence); - QObject::connect(squawk, &Core::Squawk::removePresence, &w, &Squawk::removePresence); - QObject::connect(squawk, &Core::Squawk::stateChanged, &w, &Squawk::stateChanged); - QObject::connect(squawk, &Core::Squawk::accountMessage, &w, &Squawk::accountMessage); - QObject::connect(squawk, &Core::Squawk::changeMessage, &w, &Squawk::changeMessage); - QObject::connect(squawk, &Core::Squawk::responseArchive, &w, &Squawk::responseArchive); - QObject::connect(squawk, &Core::Squawk::addRoom, &w, &Squawk::addRoom); - QObject::connect(squawk, &Core::Squawk::changeRoom, &w, &Squawk::changeRoom); - QObject::connect(squawk, &Core::Squawk::removeRoom, &w, &Squawk::removeRoom); - QObject::connect(squawk, &Core::Squawk::addRoomParticipant, &w, &Squawk::addRoomParticipant); - QObject::connect(squawk, &Core::Squawk::changeRoomParticipant, &w, &Squawk::changeRoomParticipant); - QObject::connect(squawk, &Core::Squawk::removeRoomParticipant, &w, &Squawk::removeRoomParticipant); - QObject::connect(squawk, &Core::Squawk::fileDownloadComplete, &w, &Squawk::fileDownloadComplete); - QObject::connect(squawk, &Core::Squawk::fileUploadComplete, &w, &Squawk::fileUploadComplete); - QObject::connect(squawk, &Core::Squawk::fileProgress, &w, &Squawk::fileProgress); - QObject::connect(squawk, &Core::Squawk::fileError, &w, &Squawk::fileError); - QObject::connect(squawk, &Core::Squawk::responseVCard, &w, &Squawk::responseVCard); - QObject::connect(squawk, &Core::Squawk::requestPassword, &w, &Squawk::requestPassword); - QObject::connect(squawk, &Core::Squawk::ready, &w, &Squawk::readSettings); - - coreThread->start(); - int result = app.exec(); - - if (coreThread->isRunning()) { - //coreThread->wait(); - //todo if I uncomment that, the app will not quit if it has reconnected at least once - //it feels like a symptom of something badly desinged in the core thread - //need to investigate; - } - - return result; -} - diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt new file mode 100644 index 0000000..3c23932 --- /dev/null +++ b/main/CMakeLists.txt @@ -0,0 +1,7 @@ +target_sources(squawk PRIVATE + main.cpp + application.cpp + application.h + dialogqueue.cpp + dialogqueue.h +) diff --git a/main/application.cpp b/main/application.cpp new file mode 100644 index 0000000..f6ffe07 --- /dev/null +++ b/main/application.cpp @@ -0,0 +1,476 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "application.h" + +Application::Application(Core::Squawk* p_core): + QObject(), + availability(Shared::Availability::offline), + core(p_core), + squawk(nullptr), + notifications("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus()), + roster(), + conversations(), + dialogueQueue(roster), + nowQuitting(false), + destroyingSquawk(false) +{ + connect(&roster, &Models::Roster::unnoticedMessage, this, &Application::notify); + + + //connecting myself to the backed + connect(this, &Application::changeState, core, &Core::Squawk::changeState); + connect(this, &Application::setRoomJoined, core, &Core::Squawk::setRoomJoined); + connect(this, &Application::setRoomAutoJoin, core, &Core::Squawk::setRoomAutoJoin); + connect(this, &Application::subscribeContact, core, &Core::Squawk::subscribeContact); + connect(this, &Application::unsubscribeContact, core, &Core::Squawk::unsubscribeContact); + connect(this, &Application::replaceMessage, core, &Core::Squawk::replaceMessage); + connect(this, &Application::sendMessage, core, &Core::Squawk::sendMessage); + connect(this, &Application::resendMessage, core, &Core::Squawk::resendMessage); + connect(&roster, &Models::Roster::requestArchive, + std::bind(&Core::Squawk::requestArchive, core, std::placeholders::_1, std::placeholders::_2, 20, std::placeholders::_3)); + + connect(&dialogueQueue, &DialogQueue::modifyAccountRequest, core, &Core::Squawk::modifyAccountRequest); + connect(&dialogueQueue, &DialogQueue::responsePassword, core, &Core::Squawk::responsePassword); + connect(&dialogueQueue, &DialogQueue::disconnectAccount, core, &Core::Squawk::disconnectAccount); + + connect(&roster, &Models::Roster::fileDownloadRequest, core, &Core::Squawk::fileDownloadRequest); + connect(&roster, &Models::Roster::localPathInvalid, core, &Core::Squawk::onLocalPathInvalid); + + + //coonecting backend to myself + connect(core, &Core::Squawk::stateChanged, this, &Application::stateChanged); + + connect(core, &Core::Squawk::accountMessage, &roster, &Models::Roster::addMessage); + connect(core, &Core::Squawk::responseArchive, &roster, &Models::Roster::responseArchive); + connect(core, &Core::Squawk::changeMessage, &roster, &Models::Roster::changeMessage); + + connect(core, &Core::Squawk::newAccount, &roster, &Models::Roster::addAccount); + 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::addGroup, this, &Application::addGroup); + connect(core, &Core::Squawk::removeGroup, &roster, &Models::Roster::removeGroup); + connect(core, qOverload(&Core::Squawk::removeContact), + &roster, qOverload(&Models::Roster::removeContact)); + connect(core, qOverload(&Core::Squawk::removeContact), + &roster, qOverload(&Models::Roster::removeContact)); + connect(core, &Core::Squawk::changeContact, &roster, &Models::Roster::changeContact); + connect(core, &Core::Squawk::addPresence, &roster, &Models::Roster::addPresence); + connect(core, &Core::Squawk::removePresence, &roster, &Models::Roster::removePresence); + + connect(core, &Core::Squawk::addRoom, &roster, &Models::Roster::addRoom); + connect(core, &Core::Squawk::changeRoom, &roster, &Models::Roster::changeRoom); + connect(core, &Core::Squawk::removeRoom, &roster, &Models::Roster::removeRoom); + connect(core, &Core::Squawk::addRoomParticipant, &roster, &Models::Roster::addRoomParticipant); + connect(core, &Core::Squawk::changeRoomParticipant, &roster, &Models::Roster::changeRoomParticipant); + connect(core, &Core::Squawk::removeRoomParticipant, &roster, &Models::Roster::removeRoomParticipant); + + + connect(core, &Core::Squawk::fileDownloadComplete, std::bind(&Models::Roster::fileComplete, &roster, std::placeholders::_1, false)); + connect(core, &Core::Squawk::fileUploadComplete, std::bind(&Models::Roster::fileComplete, &roster, std::placeholders::_1, true)); + connect(core, &Core::Squawk::fileProgress, &roster, &Models::Roster::fileProgress); + connect(core, &Core::Squawk::fileError, &roster, &Models::Roster::fileError); + + connect(core, &Core::Squawk::requestPassword, this, &Application::requestPassword); + connect(core, &Core::Squawk::ready, this, &Application::readSettings); + +} + +Application::~Application() {} + +void Application::quit() +{ + if (!nowQuitting) { + nowQuitting = true; + emit quitting(); + + writeSettings(); + for (Conversations::const_iterator itr = conversations.begin(), end = conversations.end(); itr != end; ++itr) { + disconnect(itr->second, &Conversation::destroyed, this, &Application::onConversationClosed); + itr->second->close(); + } + conversations.clear(); + dialogueQueue.quit(); + + if (squawk != nullptr) { + squawk->close(); + } + if (!destroyingSquawk) { + checkForTheLastWindow(); + } + } +} + +void Application::checkForTheLastWindow() +{ + if (QApplication::topLevelWidgets().size() > 0) { + emit readyToQuit(); + } else { + connect(qApp, &QApplication::lastWindowClosed, this, &Application::readyToQuit); + } +} + +void Application::createMainWindow() +{ + if (squawk == nullptr) { + squawk = new Squawk(roster); + + connect(squawk, &Squawk::notify, this, &Application::notify); + connect(squawk, &Squawk::changeSubscription, this, &Application::changeSubscription); + connect(squawk, &Squawk::openedConversation, this, &Application::onSquawkOpenedConversation); + connect(squawk, &Squawk::openConversation, this, &Application::openConversation); + connect(squawk, &Squawk::changeState, this, &Application::setState); + connect(squawk, &Squawk::closing, this, &Application::onSquawkClosing); + + connect(squawk, &Squawk::modifyAccountRequest, core, &Core::Squawk::modifyAccountRequest); + connect(squawk, &Squawk::newAccountRequest, core, &Core::Squawk::newAccountRequest); + connect(squawk, &Squawk::removeAccountRequest, core, &Core::Squawk::removeAccountRequest); + connect(squawk, &Squawk::connectAccount, core, &Core::Squawk::connectAccount); + connect(squawk, &Squawk::disconnectAccount, core, &Core::Squawk::disconnectAccount); + + connect(squawk, &Squawk::addContactRequest, core, &Core::Squawk::addContactRequest); + connect(squawk, &Squawk::removeContactRequest, core, &Core::Squawk::removeContactRequest); + connect(squawk, &Squawk::removeRoomRequest, core, &Core::Squawk::removeRoomRequest); + connect(squawk, &Squawk::addRoomRequest, core, &Core::Squawk::addRoomRequest); + connect(squawk, &Squawk::addContactToGroupRequest, core, &Core::Squawk::addContactToGroupRequest); + connect(squawk, &Squawk::removeContactFromGroupRequest, core, &Core::Squawk::removeContactFromGroupRequest); + connect(squawk, &Squawk::renameContactRequest, core, &Core::Squawk::renameContactRequest); + connect(squawk, &Squawk::requestVCard, core, &Core::Squawk::requestVCard); + connect(squawk, &Squawk::uploadVCard, core, &Core::Squawk::uploadVCard); + connect(squawk, &Squawk::changeDownloadsPath, core, &Core::Squawk::changeDownloadsPath); + + connect(core, &Core::Squawk::responseVCard, squawk, &Squawk::responseVCard); + + dialogueQueue.setParentWidnow(squawk); + squawk->stateChanged(availability); + squawk->show(); + } +} + +void Application::onSquawkClosing() +{ + dialogueQueue.setParentWidnow(nullptr); + + disconnect(core, &Core::Squawk::responseVCard, squawk, &Squawk::responseVCard); + + destroyingSquawk = true; + squawk->deleteLater(); + squawk = nullptr; + + //for now + quit(); +} + +void Application::onSquawkDestroyed() { + destroyingSquawk = false; + if (nowQuitting) { + checkForTheLastWindow(); + } +} + + +void Application::notify(const QString& account, const Shared::Message& msg) +{ + QString name = QString(roster.getContactName(account, msg.getPenPalJid())); + QString path = QString(roster.getContactIconPath(account, msg.getPenPalJid(), msg.getPenPalResource())); + QVariantList args; + args << QString(); + + args << qHash(msg.getId()); + if (path.size() > 0) { + args << path; + } else { + args << QString("mail-message"); //TODO should here better be unknown user icon? + } + if (msg.getType() == Shared::Message::groupChat) { + args << msg.getFromResource() + " from " + name; + } else { + args << name; + } + + QString body(msg.getBody()); + QString oob(msg.getOutOfBandUrl()); + if (body == oob) { + body = tr("Attached file"); + } + + args << body; + args << QStringList(); + args << QVariantMap({ + {"desktop-entry", QString(QCoreApplication::applicationName())}, + {"category", QString("message")}, + // {"sound-file", "/path/to/macaw/squawk"}, + {"sound-name", QString("message-new-instant")} + }); + args << -1; + notifications.callWithArgumentList(QDBus::AutoDetect, "Notify", args); + + if (squawk != nullptr) { + QApplication::alert(squawk); + } +} + +void Application::setState(Shared::Availability p_availability) +{ + if (availability != p_availability) { + availability = p_availability; + emit changeState(availability); + } +} + +void Application::stateChanged(Shared::Availability state) +{ + availability = state; + if (squawk != nullptr) { + squawk->stateChanged(state); + } +} + +void Application::readSettings() +{ + QSettings settings; + settings.beginGroup("ui"); + int avail; + if (settings.contains("availability")) { + avail = settings.value("availability").toInt(); + } else { + avail = static_cast(Shared::Availability::online); + } + settings.endGroup(); + + setState(Shared::Global::fromInt(avail)); + createMainWindow(); +} + +void Application::writeSettings() +{ + QSettings settings; + settings.setValue("availability", static_cast(availability)); +} + +void Application::requestPassword(const QString& account, bool authenticationError) { + if (authenticationError) { + dialogueQueue.addAction(account, DialogQueue::askCredentials); + } else { + dialogueQueue.addAction(account, DialogQueue::askPassword); + } + +} +void Application::onConversationClosed() +{ + Conversation* conv = static_cast(sender()); + Models::Roster::ElId id(conv->getAccount(), conv->getJid()); + Conversations::const_iterator itr = conversations.find(id); + if (itr != conversations.end()) { + conversations.erase(itr); + } + if (conv->isMuc) { + Room* room = static_cast(conv); + if (!room->autoJoined()) { + emit setRoomJoined(id.account, id.name, false); + } + } +} + +void Application::changeSubscription(const Models::Roster::ElId& id, bool subscribe) +{ + Models::Item::Type type = roster.getContactType(id); + + switch (type) { + case Models::Item::contact: + if (subscribe) { + emit subscribeContact(id.account, id.name, ""); + } else { + emit unsubscribeContact(id.account, id.name, ""); + } + break; + case Models::Item::room: + setRoomAutoJoin(id.account, id.name, subscribe); + if (!isConverstationOpened(id)) { + emit setRoomJoined(id.account, id.name, subscribe); + } + break; + default: + break; + } +} + +void Application::subscribeConversation(Conversation* conv) +{ + connect(conv, &Conversation::destroyed, this, &Application::onConversationClosed); + connect(conv, &Conversation::sendMessage, this, &Application::onConversationMessage); + connect(conv, &Conversation::replaceMessage, this, &Application::onConversationReplaceMessage); + connect(conv, &Conversation::resendMessage, this, &Application::onConversationResend); + connect(conv, &Conversation::notifyableMessage, this, &Application::notify); +} + +void Application::openConversation(const Models::Roster::ElId& id, const QString& resource) +{ + Conversations::const_iterator itr = conversations.find(id); + Models::Account* acc = roster.getAccount(id.account); + Conversation* conv = nullptr; + bool created = false; + if (itr != conversations.end()) { + conv = itr->second; + } else { + Models::Element* el = roster.getElement(id); + if (el != NULL) { + if (el->type == Models::Item::room) { + created = true; + Models::Room* room = static_cast(el); + conv = new Room(acc, room); + if (!room->getJoined()) { + emit setRoomJoined(id.account, id.name, true); + } + } else if (el->type == Models::Item::contact) { + created = true; + conv = new Chat(acc, static_cast(el)); + } + } + } + + if (conv != nullptr) { + if (created) { + conv->setAttribute(Qt::WA_DeleteOnClose); + subscribeConversation(conv); + conversations.insert(std::make_pair(id, conv)); + } + + conv->show(); + conv->raise(); + conv->activateWindow(); + + if (resource.size() > 0) { + conv->setPalResource(resource); + } + } +} + +void Application::onConversationMessage(const Shared::Message& msg) +{ + Conversation* conv = static_cast(sender()); + QString acc = conv->getAccount(); + + roster.addMessage(acc, msg); + emit sendMessage(acc, msg); +} + +void Application::onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg) +{ + Conversation* conv = static_cast(sender()); + QString acc = conv->getAccount(); + + roster.changeMessage(acc, msg.getPenPalJid(), originalId, { + {"state", static_cast(Shared::Message::State::pending)} + }); + emit replaceMessage(acc, originalId, msg); +} + +void Application::onConversationResend(const QString& id) +{ + Conversation* conv = static_cast(sender()); + QString acc = conv->getAccount(); + QString jid = conv->getJid(); + + emit resendMessage(acc, jid, id); +} + +void Application::onSquawkOpenedConversation() { + subscribeConversation(squawk->currentConversation); + Models::Roster::ElId id = squawk->currentConversationId(); + + const Models::Element* el = roster.getElementConst(id); + if (el != NULL && el->isRoom() && !static_cast(el)->getJoined()) { + emit setRoomJoined(id.account, id.name, true); + } +} + +void Application::removeAccount(const QString& account) +{ + Conversations::const_iterator itr = conversations.begin(); + while (itr != conversations.end()) { + if (itr->first.account == account) { + Conversations::const_iterator lItr = itr; + ++itr; + Conversation* conv = lItr->second; + disconnect(conv, &Conversation::destroyed, this, &Application::onConversationClosed); + conv->close(); + conversations.erase(lItr); + } else { + ++itr; + } + } + + if (squawk != nullptr && squawk->currentConversationId().account == account) { + squawk->closeCurrentConversation(); + } + + roster.removeAccount(account); +} + +void Application::changeAccount(const QString& account, const QMap& data) +{ + for (QMap::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) { + QString attr = itr.key(); + roster.updateAccount(account, attr, *itr); + } +} + +void Application::addContact(const QString& account, const QString& jid, const QString& group, const QMap& 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); + + 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); + if (settings.value(name + "/expanded", false).toBool()) { + squawk->expand(roster.getGroupIndex(account, name)); + } + } + settings.endGroup(); + settings.endGroup(); + settings.endGroup(); + } +} + +bool Application::isConverstationOpened(const Models::Roster::ElId& id) const { + return (conversations.count(id) > 0) || (squawk != nullptr && squawk->currentConversationId() == id);} diff --git a/main/application.h b/main/application.h new file mode 100644 index 0000000..15adce7 --- /dev/null +++ b/main/application.h @@ -0,0 +1,111 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 APPLICATION_H +#define APPLICATION_H + +#include + +#include +#include + +#include "dialogqueue.h" +#include "core/squawk.h" + +#include "ui/squawk.h" +#include "ui/models/roster.h" +#include "ui/widgets/conversation.h" + +#include "shared/message.h" +#include "shared/enums.h" + +/** + * @todo write docs + */ +class Application : public QObject +{ + Q_OBJECT +public: + Application(Core::Squawk* core); + ~Application(); + + bool isConverstationOpened(const Models::Roster::ElId& id) const; + +signals: + void sendMessage(const QString& account, const Shared::Message& data); + void replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data); + void resendMessage(const QString& account, const QString& jid, const QString& id); + + void changeState(Shared::Availability state); + + void setRoomJoined(const QString& account, const QString& jid, bool joined); + void setRoomAutoJoin(const QString& account, const QString& jid, bool joined); + void subscribeContact(const QString& account, const QString& jid, const QString& reason); + void unsubscribeContact(const QString& account, const QString& jid, const QString& reason); + + void quitting(); + void readyToQuit(); + +public slots: + void readSettings(); + void quit(); + +protected slots: + void notify(const QString& account, const Shared::Message& msg); + void setState(Shared::Availability availability); + + void changeAccount(const QString& account, const QMap& data); + void removeAccount(const QString& account); + 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& data); + + void requestPassword(const QString& account, bool authenticationError); + + void writeSettings(); + +private slots: + void onConversationClosed(); + void changeSubscription(const Models::Roster::ElId& id, bool subscribe); + void onSquawkOpenedConversation(); + void onConversationMessage(const Shared::Message& msg); + void onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg); + void onConversationResend(const QString& id); + void stateChanged(Shared::Availability state); + void onSquawkClosing(); + void onSquawkDestroyed(); + +private: + void createMainWindow(); + void subscribeConversation(Conversation* conv); + void checkForTheLastWindow(); + +private: + typedef std::map Conversations; + + Shared::Availability availability; + Core::Squawk* core; + Squawk* squawk; + QDBusInterface notifications; + Models::Roster roster; + Conversations conversations; + DialogQueue dialogueQueue; + bool nowQuitting; + bool destroyingSquawk; +}; + +#endif // APPLICATION_H diff --git a/ui/dialogqueue.cpp b/main/dialogqueue.cpp similarity index 87% rename from ui/dialogqueue.cpp rename to main/dialogqueue.cpp index 02f8688..d7b4570 100644 --- a/ui/dialogqueue.cpp +++ b/main/dialogqueue.cpp @@ -15,10 +15,9 @@ // along with this program. If not, see . #include "dialogqueue.h" -#include "squawk.h" #include -DialogQueue::DialogQueue(Squawk* p_squawk): +DialogQueue::DialogQueue(const Models::Roster& p_roster): QObject(), currentSource(), currentAction(none), @@ -26,7 +25,8 @@ DialogQueue::DialogQueue(Squawk* p_squawk): collection(queue.get<0>()), sequence(queue.get<1>()), prompt(nullptr), - squawk(p_squawk) + parent(nullptr), + roster(p_roster) { } @@ -34,6 +34,19 @@ DialogQueue::~DialogQueue() { } +void DialogQueue::quit() +{ + queue.clear(); + if (currentAction != none) { + actionDone(); + } +} + +void DialogQueue::setParentWidnow(QMainWindow* p_parent) +{ + parent = p_parent; +} + bool DialogQueue::addAction(const QString& source, DialogQueue::Action action) { if (action == none) { @@ -77,7 +90,7 @@ void DialogQueue::performNextAction() actionDone(); break; case askPassword: { - QInputDialog* dialog = new QInputDialog(squawk); + QInputDialog* dialog = new QInputDialog(parent); prompt = dialog; connect(dialog, &QDialog::accepted, this, &DialogQueue::onPropmtAccepted); connect(dialog, &QDialog::rejected, this, &DialogQueue::onPropmtRejected); @@ -90,11 +103,11 @@ void DialogQueue::performNextAction() } break; case askCredentials: { - CredentialsPrompt* dialog = new CredentialsPrompt(squawk); + CredentialsPrompt* dialog = new CredentialsPrompt(parent); prompt = dialog; connect(dialog, &QDialog::accepted, this, &DialogQueue::onPropmtAccepted); connect(dialog, &QDialog::rejected, this, &DialogQueue::onPropmtRejected); - Models::Account* acc = squawk->rosterModel.getAccount(currentSource); + const Models::Account* acc = roster.getAccountConst(currentSource); dialog->setAccount(currentSource); dialog->setLogin(acc->getLogin()); dialog->setPassword(acc->getPassword()); @@ -111,12 +124,12 @@ void DialogQueue::onPropmtAccepted() break; case askPassword: { QInputDialog* dialog = static_cast(prompt); - emit squawk->responsePassword(currentSource, dialog->textValue()); + emit responsePassword(currentSource, dialog->textValue()); } break; case askCredentials: { CredentialsPrompt* dialog = static_cast(prompt); - emit squawk->modifyAccountRequest(currentSource, { + emit modifyAccountRequest(currentSource, { {"login", dialog->getLogin()}, {"password", dialog->getPassword()} }); @@ -133,7 +146,7 @@ void DialogQueue::onPropmtRejected() break; case askPassword: case askCredentials: - emit squawk->disconnectAccount(currentSource); + emit disconnectAccount(currentSource); break; } actionDone(); diff --git a/ui/dialogqueue.h b/main/dialogqueue.h similarity index 83% rename from ui/dialogqueue.h rename to main/dialogqueue.h index bfc1f21..b0da9dc 100644 --- a/ui/dialogqueue.h +++ b/main/dialogqueue.h @@ -19,14 +19,14 @@ #include #include +#include #include #include #include #include - -class Squawk; +#include class DialogQueue : public QObject { @@ -38,12 +38,21 @@ public: askCredentials }; - DialogQueue(Squawk* squawk); + DialogQueue(const Models::Roster& roster); ~DialogQueue(); bool addAction(const QString& source, Action action); bool cancelAction(const QString& source, Action action); +signals: + void modifyAccountRequest(const QString&, const QMap&); + void responsePassword(const QString& account, const QString& password); + void disconnectAccount(const QString&); + +public: + void setParentWidnow(QMainWindow* parent); + void quit(); + private: void performNextAction(); void actionDone(); @@ -85,7 +94,8 @@ private: Sequence& sequence; QDialog* prompt; - Squawk* squawk; + QMainWindow* parent; + const Models::Roster& roster; }; #endif // DIALOGQUEUE_H diff --git a/main/main.cpp b/main/main.cpp new file mode 100644 index 0000000..77719a2 --- /dev/null +++ b/main/main.cpp @@ -0,0 +1,139 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "shared/global.h" +#include "shared/messageinfo.h" +#include "shared/pathcheck.h" +#include "main/application.h" +#include "core/signalcatcher.h" +#include "core/squawk.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + qRegisterMetaType("Shared::Message"); + qRegisterMetaType("Shared::MessageInfo"); + qRegisterMetaType("Shared::VCard"); + qRegisterMetaType>("std::list"); + qRegisterMetaType>("std::list"); + qRegisterMetaType>("QSet"); + qRegisterMetaType("Shared::ConnectionState"); + qRegisterMetaType("Shared::Availability"); + + QApplication app(argc, argv); + SignalCatcher sc(&app); + + QApplication::setApplicationName("squawk"); + QApplication::setOrganizationName("macaw.me"); + QApplication::setApplicationDisplayName("Squawk"); + QApplication::setApplicationVersion("0.2.2"); + + QTranslator qtTranslator; + qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); + app.installTranslator(&qtTranslator); + + QTranslator myappTranslator; + QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); + bool found = false; + for (QString share : shares) { + found = myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n"); + if (found) { + break; + } + } + if (!found) { + myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath()); + } + + app.installTranslator(&myappTranslator); + + 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); + + new Shared::Global(); //translates enums + + QSettings settings; + QVariant vs = settings.value("style"); + if (vs.isValid()) { + QString style = vs.toString().toLower(); + if (style != "system") { + Shared::Global::setStyle(style); + } + } + if (Shared::Global::supported("colorSchemeTools")) { + QVariant vt = settings.value("theme"); + if (vt.isValid()) { + QString theme = vt.toString(); + if (theme.toLower() != "system") { + Shared::Global::setTheme(theme); + } + } + } + QString path = Shared::downloadsPathCheck(); + if (path.size() > 0) { + settings.setValue("downloadsPath", path); + } else { + qDebug() << "couldn't initialize directory for downloads, quitting"; + return -1; + } + + Core::Squawk* squawk = new Core::Squawk(); + QThread* coreThread = new QThread(); + squawk->moveToThread(coreThread); + + Application application(squawk); + + QObject::connect(&sc, &SignalCatcher::interrupt, &application, &Application::quit); + + QObject::connect(coreThread, &QThread::started, squawk, &Core::Squawk::start); + QObject::connect(&application, &Application::quitting, squawk, &Core::Squawk::stop); + //QObject::connect(&app, &QApplication::aboutToQuit, &w, &QMainWindow::close); + QObject::connect(squawk, &Core::Squawk::quit, squawk, &Core::Squawk::deleteLater); + QObject::connect(squawk, &Core::Squawk::destroyed, coreThread, &QThread::quit, Qt::QueuedConnection); + QObject::connect(coreThread, &QThread::finished, &app, &QApplication::quit, Qt::QueuedConnection); + + coreThread->start(); + int result = app.exec(); + + if (coreThread->isRunning()) { + //coreThread->wait(); + //todo if I uncomment that, the app will not quit if it has reconnected at least once + //it feels like a symptom of something badly desinged in the core thread + //need to investigate; + } + + return result; +} + diff --git a/shared/global.cpp b/shared/global.cpp index be660bd..6519952 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -95,8 +95,6 @@ Shared::Global::Global(): }), defaultSystemStyle(QApplication::style()->objectName()), defaultSystemPalette(QApplication::palette()), - rosterModel(new Models::Roster()), - dbus("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus()), pluginSupport({ {"KWallet", false}, {"openFileManagerWindowJob", false}, @@ -352,44 +350,6 @@ void Shared::Global::setStyle(const QString& style) } } -void Shared::Global::notify(const QString& account, const Shared::Message& msg) -{ - QString name = QString(instance->rosterModel->getContactName(account, msg.getPenPalJid())); - QString path = QString(instance->rosterModel->getContactIconPath(account, msg.getPenPalJid(), msg.getPenPalResource())); - QVariantList args; - args << QString(); - - args << qHash(msg.getId()); - if (path.size() > 0) { - args << path; - } else { - args << QString("mail-message"); //TODO should here better be unknown user icon? - } - if (msg.getType() == Shared::Message::groupChat) { - args << msg.getFromResource() + " from " + name; - } else { - args << name; - } - - QString body(msg.getBody()); - QString oob(msg.getOutOfBandUrl()); - if (body == oob) { - body = tr("Attached file"); - } - - args << body; - args << QStringList(); - args << QVariantMap({ - {"desktop-entry", QString(QCoreApplication::applicationName())}, - {"category", QString("message")}, - // {"sound-file", "/path/to/macaw/squawk"}, - {"sound-name", QString("message-new-instant")} - }); - args << -1; - instance->dbus.callWithArgumentList(QDBus::AutoDetect, "Notify", args); -} - - #define FROM_INT_INPL(Enum) \ template<> \ Enum Shared::Global::fromInt(int src) \ diff --git a/shared/global.h b/shared/global.h index fcd8105..ebed931 100644 --- a/shared/global.h +++ b/shared/global.h @@ -42,18 +42,11 @@ #include #include #include -#include - -class Squawk; -namespace Models { - class Roster; -} namespace Shared { class Global { Q_DECLARE_TR_FUNCTIONS(Global) - friend class ::Squawk; public: struct FileInfo { enum class Preview { @@ -71,8 +64,6 @@ namespace Shared { Global(); - static void notify(const QString& account, const Shared::Message& msg); - static Global* getInstance(); static QString getName(Availability av); static QString getName(ConnectionState cs); @@ -130,8 +121,6 @@ namespace Shared { private: static Global* instance; - Models::Roster* rosterModel; - QDBusInterface dbus; std::map pluginSupport; std::map fileCache; diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index fcbb24c..296c289 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -2,8 +2,6 @@ target_sources(squawk PRIVATE squawk.cpp squawk.h squawk.ui - dialogqueue.cpp - dialogqueue.h ) add_subdirectory(models) diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index b96ddda..fef3e43 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -927,11 +927,29 @@ QString Models::Roster::getContactIconPath(const QString& account, const QString return path; } -Models::Account * Models::Roster::getAccount(const QString& name) +Models::Account * Models::Roster::getAccount(const QString& name) { + return const_cast(getAccountConst(name));} + +const Models::Account * Models::Roster::getAccountConst(const QString& name) const { + return accounts.at(name);} + +const Models::Element * Models::Roster::getElementConst(const Models::Roster::ElId& id) const { - return accounts.find(name)->second; + std::map::const_iterator cItr = contacts.find(id); + + if (cItr != contacts.end()) { + return cItr->second; + } else { + std::map::const_iterator rItr = rooms.find(id); + if (rItr != rooms.end()) { + return rItr->second; + } + } + + return NULL; } + QModelIndex Models::Roster::getAccountIndex(const QString& name) { std::map::const_iterator itr = accounts.find(name); @@ -1005,20 +1023,20 @@ void Models::Roster::fileError(const std::list& msgs, const Models::Element * Models::Roster::getElement(const Models::Roster::ElId& id) { - std::map::iterator cItr = contacts.find(id); - - if (cItr != contacts.end()) { - return cItr->second; - } else { - std::map::iterator rItr = rooms.find(id); - if (rItr != rooms.end()) { - return rItr->second; - } - } - - return NULL; + return const_cast(getElementConst(id)); } +Models::Item::Type Models::Roster::getContactType(const Models::Roster::ElId& id) const +{ + const Models::Element* el = getElementConst(id); + if (el == NULL) { + return Item::root; + } + + return el->type; +} + + void Models::Roster::onAccountReconnected() { Account* acc = static_cast(sender()); diff --git a/ui/models/roster.h b/ui/models/roster.h index 28f4d30..60adf13 100644 --- a/ui/models/roster.h +++ b/ui/models/roster.h @@ -46,6 +46,7 @@ public: Roster(QObject* parent = 0); ~Roster(); +public slots: void addAccount(const QMap &data); void updateAccount(const QString& account, const QString& field, const QVariant& value); void removeAccount(const QString& account); @@ -65,7 +66,12 @@ public: void addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data); void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data); void removeRoomParticipant(const QString& account, const QString& jid, const QString& name); + +public: QString getContactName(const QString& account, const QString& jid) const; + Item::Type getContactType(const Models::Roster::ElId& id) const; + const Element* getElementConst(const ElId& id) const; + Element* getElement(const ElId& id); QVariant data ( const QModelIndex& index, int role ) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; @@ -79,6 +85,7 @@ public: bool groupHasContact(const QString& account, const QString& group, const QString& contactJID) const; 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); void responseArchive(const QString& account, const QString& jid, const std::list& list, bool last); @@ -95,9 +102,6 @@ signals: void unnoticedMessage(const QString& account, const Shared::Message& msg); void localPathInvalid(const QString& path); -private: - Element* getElement(const ElId& id); - private slots: void onAccountDataChanged(const QModelIndex& tl, const QModelIndex& br, const QVector& roles); void onAccountReconnected(); diff --git a/ui/squawk.cpp b/ui/squawk.cpp index a08f38b..434b442 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -21,15 +21,13 @@ #include #include -Squawk::Squawk(QWidget *parent) : +Squawk::Squawk(Models::Roster& p_rosterModel, QWidget *parent) : QMainWindow(parent), m_ui(new Ui::Squawk), accounts(nullptr), preferences(nullptr), about(nullptr), - dialogueQueue(this), - rosterModel(*(Shared::Global::getInstance()->rosterModel)), - conversations(), + rosterModel(p_rosterModel), contextMenu(new QMenu()), vCards(), currentConversation(nullptr), @@ -64,12 +62,8 @@ Squawk::Squawk(QWidget *parent) : connect(m_ui->roster, &QTreeView::customContextMenuRequested, this, &Squawk::onRosterContextMenu); connect(m_ui->roster, &QTreeView::collapsed, this, &Squawk::onItemCollepsed); connect(m_ui->roster->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &Squawk::onRosterSelectionChanged); - connect(&rosterModel, &Models::Roster::unnoticedMessage, this, &Squawk::onUnnoticedMessage); connect(rosterModel.accountsModel, &Models::Accounts::sizeChanged, this, &Squawk::onAccountsSizeChanged); - connect(&rosterModel, &Models::Roster::requestArchive, this, &Squawk::onRequestArchive); - connect(&rosterModel, &Models::Roster::fileDownloadRequest, this, &Squawk::fileDownloadRequest); - connect(&rosterModel, &Models::Roster::localPathInvalid, this, &Squawk::localPathInvalid); connect(contextMenu, &QMenu::aboutToHide, this, &Squawk::onContextAboutToHide); connect(m_ui->actionAboutSquawk, &QAction::triggered, this, &Squawk::onAboutSquawkCalled); //m_ui->mainToolBar->addWidget(m_ui->comboBox); @@ -199,36 +193,25 @@ void Squawk::closeEvent(QCloseEvent* event) about->close(); } - for (Conversations::const_iterator itr = conversations.begin(), end = conversations.end(); itr != end; ++itr) { - disconnect(itr->second, &Conversation::destroyed, this, &Squawk::onConversationClosed); - itr->second->close(); - } - conversations.clear(); - for (std::map::const_iterator itr = vCards.begin(), end = vCards.end(); itr != end; ++itr) { disconnect(itr->second, &VCard::destroyed, this, &Squawk::onVCardClosed); itr->second->close(); } vCards.clear(); - + writeSettings(); + emit closing();; + QMainWindow::closeEvent(event); } +void Squawk::onAccountsClosed() { + accounts = nullptr;} -void Squawk::onAccountsClosed() -{ - accounts = nullptr; -} +void Squawk::onPreferencesClosed() { + preferences = nullptr;} -void Squawk::onPreferencesClosed() -{ - preferences = nullptr; -} - -void Squawk::newAccount(const QMap& account) -{ - rosterModel.addAccount(account); -} +void Squawk::onAboutSquawkClosed() { + about = nullptr;} void Squawk::onComboboxActivated(int index) { @@ -236,85 +219,11 @@ void Squawk::onComboboxActivated(int index) emit changeState(av); } -void Squawk::changeAccount(const QString& account, const QMap& data) -{ - for (QMap::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) { - QString attr = itr.key(); - rosterModel.updateAccount(account, attr, *itr); - } -} +void Squawk::expand(const QModelIndex& index) { + m_ui->roster->expand(index);} -void Squawk::addContact(const QString& account, const QString& jid, const QString& group, const QMap& data) -{ - rosterModel.addContact(account, jid, group, data); - - QSettings settings; - settings.beginGroup("ui"); - settings.beginGroup("roster"); - settings.beginGroup(account); - if (settings.value("expanded", false).toBool()) { - QModelIndex ind = rosterModel.getAccountIndex(account); - m_ui->roster->expand(ind); - } - settings.endGroup(); - settings.endGroup(); - settings.endGroup(); -} - -void Squawk::addGroup(const QString& account, const QString& name) -{ - rosterModel.addGroup(account, name); - - QSettings settings; - settings.beginGroup("ui"); - settings.beginGroup("roster"); - settings.beginGroup(account); - if (settings.value("expanded", false).toBool()) { - QModelIndex ind = rosterModel.getAccountIndex(account); - m_ui->roster->expand(ind); - if (settings.value(name + "/expanded", false).toBool()) { - m_ui->roster->expand(rosterModel.getGroupIndex(account, name)); - } - } - settings.endGroup(); - settings.endGroup(); - settings.endGroup(); -} - -void Squawk::removeGroup(const QString& account, const QString& name) -{ - rosterModel.removeGroup(account, name); -} - -void Squawk::changeContact(const QString& account, const QString& jid, const QMap& data) -{ - rosterModel.changeContact(account, jid, data); -} - -void Squawk::removeContact(const QString& account, const QString& jid) -{ - rosterModel.removeContact(account, jid); -} - -void Squawk::removeContact(const QString& account, const QString& jid, const QString& group) -{ - rosterModel.removeContact(account, jid, group); -} - -void Squawk::addPresence(const QString& account, const QString& jid, const QString& name, const QMap& data) -{ - rosterModel.addPresence(account, jid, name, data); -} - -void Squawk::removePresence(const QString& account, const QString& jid, const QString& name) -{ - rosterModel.removePresence(account, jid, name); -} - -void Squawk::stateChanged(Shared::Availability state) -{ - m_ui->comboBox->setCurrentIndex(static_cast(state)); -} +void Squawk::stateChanged(Shared::Availability state) { + m_ui->comboBox->setCurrentIndex(static_cast(state));} void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) { @@ -325,186 +234,33 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) } Models::Contact* contact = nullptr; Models::Room* room = nullptr; - QString res; - Models::Roster::ElId* id = nullptr; switch (node->type) { case Models::Item::contact: contact = static_cast(node); - id = new Models::Roster::ElId(contact->getAccountName(), contact->getJid()); + emit openConversation(Models::Roster::ElId(contact->getAccountName(), contact->getJid())); break; case Models::Item::presence: contact = static_cast(node->parentItem()); - id = new Models::Roster::ElId(contact->getAccountName(), contact->getJid()); - res = node->getName(); + emit openConversation(Models::Roster::ElId(contact->getAccountName(), contact->getJid()), node->getName()); break; case Models::Item::room: room = static_cast(node); - id = new Models::Roster::ElId(room->getAccountName(), room->getJid()); + emit openConversation(Models::Roster::ElId(room->getAccountName(), room->getJid())); break; default: m_ui->roster->expand(item); break; } - - if (id != nullptr) { - Conversations::const_iterator itr = conversations.find(*id); - Models::Account* acc = rosterModel.getAccount(id->account); - Conversation* conv = nullptr; - bool created = false; - if (itr != conversations.end()) { - conv = itr->second; - } else if (contact != nullptr) { - created = true; - conv = new Chat(acc, contact); - } else if (room != nullptr) { - created = true; - conv = new Room(acc, room); - - if (!room->getJoined()) { - emit setRoomJoined(id->account, id->name, true); - } - } - - if (conv != nullptr) { - if (created) { - conv->setAttribute(Qt::WA_DeleteOnClose); - subscribeConversation(conv); - conversations.insert(std::make_pair(*id, conv)); - } - - conv->show(); - conv->raise(); - conv->activateWindow(); - - if (res.size() > 0) { - conv->setPalResource(res); - } - } - - delete id; - } } } -void Squawk::onConversationClosed(QObject* parent) +void Squawk::closeCurrentConversation() { - Conversation* conv = static_cast(sender()); - Models::Roster::ElId id(conv->getAccount(), conv->getJid()); - Conversations::const_iterator itr = conversations.find(id); - if (itr != conversations.end()) { - conversations.erase(itr); - } - if (conv->isMuc) { - Room* room = static_cast(conv); - if (!room->autoJoined()) { - emit setRoomJoined(id.account, id.name, false); - } - } -} - -void Squawk::fileProgress(const std::list msgs, qreal value, bool up) -{ - rosterModel.fileProgress(msgs, value, up); -} - -void Squawk::fileDownloadComplete(const std::list msgs, const QString& path) -{ - rosterModel.fileComplete(msgs, false); -} - -void Squawk::fileError(const std::list msgs, const QString& error, bool up) -{ - rosterModel.fileError(msgs, error, up); -} - -void Squawk::fileUploadComplete(const std::list msgs, const QString& url, const QString& path) -{ - rosterModel.fileComplete(msgs, true); -} - -void Squawk::accountMessage(const QString& account, const Shared::Message& data) -{ - rosterModel.addMessage(account, data); -} - -void Squawk::onUnnoticedMessage(const QString& account, const Shared::Message& msg) -{ - notify(account, msg); //Telegram does this way - notifies even if the app is visible - QApplication::alert(this); -} - -void Squawk::changeMessage(const QString& account, const QString& jid, const QString& id, const QMap& data) -{ - rosterModel.changeMessage(account, jid, id, data); -} - -void Squawk::notify(const QString& account, const Shared::Message& msg) -{ - Shared::Global::notify(account, msg); -} - -void Squawk::onConversationMessage(const Shared::Message& msg) -{ - Conversation* conv = static_cast(sender()); - QString acc = conv->getAccount(); - - rosterModel.addMessage(acc, msg); - emit sendMessage(acc, msg); -} - -void Squawk::onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg) -{ - Conversation* conv = static_cast(sender()); - QString acc = conv->getAccount(); - - rosterModel.changeMessage(acc, msg.getPenPalJid(), originalId, { - {"state", static_cast(Shared::Message::State::pending)} - }); - emit replaceMessage(acc, originalId, msg); -} - -void Squawk::onConversationResend(const QString& id) -{ - Conversation* conv = static_cast(sender()); - QString acc = conv->getAccount(); - QString jid = conv->getJid(); - - emit resendMessage(acc, jid, id); -} - -void Squawk::onRequestArchive(const QString& account, const QString& jid, const QString& before) -{ - emit requestArchive(account, jid, 20, before); //TODO amount as a settings value -} - -void Squawk::responseArchive(const QString& account, const QString& jid, const std::list& list, bool last) -{ - rosterModel.responseArchive(account, jid, list, last); -} - -void Squawk::removeAccount(const QString& account) -{ - Conversations::const_iterator itr = conversations.begin(); - while (itr != conversations.end()) { - if (itr->first.account == account) { - Conversations::const_iterator lItr = itr; - ++itr; - Conversation* conv = lItr->second; - disconnect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed); - conv->close(); - conversations.erase(lItr); - } else { - ++itr; - } - } - - if (currentConversation != nullptr && currentConversation->getAccount() == account) { + if (currentConversation != nullptr) { currentConversation->deleteLater(); currentConversation = nullptr; m_ui->filler->show(); } - - rosterModel.removeAccount(account); } void Squawk::onRosterContextMenu(const QPoint& point) @@ -542,13 +298,13 @@ void Squawk::onRosterContextMenu(const QPoint& point) break; case Models::Item::contact: { Models::Contact* cnt = static_cast(item); + Models::Roster::ElId id(cnt->getAccountName(), cnt->getJid()); + QString cntName = cnt->getName(); hasMenu = true; QAction* dialog = contextMenu->addAction(Shared::icon("mail-message"), tr("Open dialog")); dialog->setEnabled(active); - connect(dialog, &QAction::triggered, [this, index]() { - onRosterItemDoubleClicked(index); - }); + connect(dialog, &QAction::triggered, std::bind(&Squawk::onRosterItemDoubleClicked, this, index)); Shared::SubscriptionState state = cnt->getState(); switch (state) { @@ -556,9 +312,7 @@ void Squawk::onRosterContextMenu(const QPoint& point) case Shared::SubscriptionState::to: { QAction* unsub = contextMenu->addAction(Shared::icon("news-unsubscribe"), tr("Unsubscribe")); unsub->setEnabled(active); - connect(unsub, &QAction::triggered, [this, cnt]() { - emit unsubscribeContact(cnt->getAccountName(), cnt->getJid(), ""); - }); + connect(unsub, &QAction::triggered, std::bind(&Squawk::changeSubscription, this, id, false)); } break; case Shared::SubscriptionState::from: @@ -566,75 +320,68 @@ void Squawk::onRosterContextMenu(const QPoint& point) case Shared::SubscriptionState::none: { QAction* sub = contextMenu->addAction(Shared::icon("news-subscribe"), tr("Subscribe")); sub->setEnabled(active); - connect(sub, &QAction::triggered, [this, cnt]() { - emit subscribeContact(cnt->getAccountName(), cnt->getJid(), ""); - }); + connect(sub, &QAction::triggered, std::bind(&Squawk::changeSubscription, this, id, true)); } } - QString accName = cnt->getAccountName(); - QString cntJID = cnt->getJid(); - QString cntName = cnt->getName(); QAction* rename = contextMenu->addAction(Shared::icon("edit-rename"), tr("Rename")); rename->setEnabled(active); - connect(rename, &QAction::triggered, [this, cntName, accName, cntJID]() { + connect(rename, &QAction::triggered, [this, cntName, id]() { QInputDialog* dialog = new QInputDialog(this); - connect(dialog, &QDialog::accepted, [this, dialog, cntName, accName, cntJID]() { + connect(dialog, &QDialog::accepted, [this, dialog, cntName, id]() { QString newName = dialog->textValue(); if (newName != cntName) { - emit renameContactRequest(accName, cntJID, newName); + emit renameContactRequest(id.account, id.name, newName); } dialog->deleteLater(); }); connect(dialog, &QDialog::rejected, dialog, &QObject::deleteLater); dialog->setInputMode(QInputDialog::TextInput); - dialog->setLabelText(tr("Input new name for %1\nor leave it empty for the contact \nto be displayed as %1").arg(cntJID)); - dialog->setWindowTitle(tr("Renaming %1").arg(cntJID)); + dialog->setLabelText(tr("Input new name for %1\nor leave it empty for the contact \nto be displayed as %1").arg(id.name)); + dialog->setWindowTitle(tr("Renaming %1").arg(id.name)); dialog->setTextValue(cntName); dialog->exec(); }); QMenu* groupsMenu = contextMenu->addMenu(Shared::icon("group"), tr("Groups")); - std::deque groupList = rosterModel.groupList(accName); + std::deque groupList = rosterModel.groupList(id.account); for (QString groupName : groupList) { QAction* gr = groupsMenu->addAction(groupName); gr->setCheckable(true); - gr->setChecked(rosterModel.groupHasContact(accName, groupName, cntJID)); + gr->setChecked(rosterModel.groupHasContact(id.account, groupName, id.name)); gr->setEnabled(active); - connect(gr, &QAction::toggled, [this, accName, groupName, cntJID](bool checked) { + connect(gr, &QAction::toggled, [this, groupName, id](bool checked) { if (checked) { - emit addContactToGroupRequest(accName, cntJID, groupName); + emit addContactToGroupRequest(id.account, id.name, groupName); } else { - emit removeContactFromGroupRequest(accName, cntJID, groupName); + emit removeContactFromGroupRequest(id.account, id.name, groupName); } }); } QAction* newGroup = groupsMenu->addAction(Shared::icon("group-new"), tr("New group")); newGroup->setEnabled(active); - connect(newGroup, &QAction::triggered, [this, accName, cntJID]() { + connect(newGroup, &QAction::triggered, [this, id]() { QInputDialog* dialog = new QInputDialog(this); - connect(dialog, &QDialog::accepted, [this, dialog, accName, cntJID]() { - emit addContactToGroupRequest(accName, cntJID, dialog->textValue()); + connect(dialog, &QDialog::accepted, [this, dialog, id]() { + emit addContactToGroupRequest(id.account, id.name, dialog->textValue()); dialog->deleteLater(); }); connect(dialog, &QDialog::rejected, dialog, &QObject::deleteLater); dialog->setInputMode(QInputDialog::TextInput); dialog->setLabelText(tr("New group name")); - dialog->setWindowTitle(tr("Add %1 to a new group").arg(cntJID)); + dialog->setWindowTitle(tr("Add %1 to a new group").arg(id.name)); dialog->exec(); }); QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard")); card->setEnabled(active); - connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, accName, cnt->getJid(), false)); + connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, id.account, id.name, false)); QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove")); remove->setEnabled(active); - connect(remove, &QAction::triggered, [this, cnt]() { - emit removeContactRequest(cnt->getAccountName(), cnt->getJid()); - }); + connect(remove, &QAction::triggered, std::bind(&Squawk::removeContactRequest, this, id.account, id.name)); } break; @@ -653,32 +400,16 @@ void Squawk::onRosterContextMenu(const QPoint& point) if (room->getAutoJoin()) { QAction* unsub = contextMenu->addAction(Shared::icon("news-unsubscribe"), tr("Unsubscribe")); unsub->setEnabled(active); - connect(unsub, &QAction::triggered, [this, id]() { - emit setRoomAutoJoin(id.account, id.name, false); - if (conversations.find(id) == conversations.end() - && (currentConversation == nullptr || currentConversation->getId() != id) - ) { //to leave the room if it's not opened in a conversation window - emit setRoomJoined(id.account, id.name, false); - } - }); + connect(unsub, &QAction::triggered, std::bind(&Squawk::changeSubscription, this, id, false)); } else { - QAction* unsub = contextMenu->addAction(Shared::icon("news-subscribe"), tr("Subscribe")); - unsub->setEnabled(active); - connect(unsub, &QAction::triggered, [this, id]() { - emit setRoomAutoJoin(id.account, id.name, true); - if (conversations.find(id) == conversations.end() - && (currentConversation == nullptr || currentConversation->getId() != id) - ) { //to join the room if it's not already joined - emit setRoomJoined(id.account, id.name, true); - } - }); + QAction* sub = contextMenu->addAction(Shared::icon("news-subscribe"), tr("Subscribe")); + sub->setEnabled(active); + connect(sub, &QAction::triggered, std::bind(&Squawk::changeSubscription, this, id, true)); } QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove")); remove->setEnabled(active); - connect(remove, &QAction::triggered, [this, id]() { - emit removeRoomRequest(id.account, id.name); - }); + connect(remove, &QAction::triggered, std::bind(&Squawk::removeRoomRequest, this, id.account, id.name)); } break; default: @@ -690,36 +421,6 @@ void Squawk::onRosterContextMenu(const QPoint& point) } } -void Squawk::addRoom(const QString& account, const QString jid, const QMap& data) -{ - rosterModel.addRoom(account, jid, data); -} - -void Squawk::changeRoom(const QString& account, const QString jid, const QMap& data) -{ - rosterModel.changeRoom(account, jid, data); -} - -void Squawk::removeRoom(const QString& account, const QString jid) -{ - rosterModel.removeRoom(account, jid); -} - -void Squawk::addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data) -{ - rosterModel.addRoomParticipant(account, jid, name, data); -} - -void Squawk::changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data) -{ - rosterModel.changeRoomParticipant(account, jid, name, data); -} - -void Squawk::removeRoomParticipant(const QString& account, const QString& jid, const QString& name) -{ - rosterModel.removeRoomParticipant(account, jid, name); -} - void Squawk::responseVCard(const QString& jid, const Shared::VCard& card) { std::map::const_iterator itr = vCards.find(jid); @@ -777,61 +478,40 @@ void Squawk::onVCardSave(const Shared::VCard& card, const QString& account) widget->deleteLater(); } -void Squawk::readSettings() -{ - QSettings settings; - settings.beginGroup("ui"); - int avail; - if (settings.contains("availability")) { - avail = settings.value("availability").toInt(); - } else { - avail = static_cast(Shared::Availability::online); - } - settings.endGroup(); - m_ui->comboBox->setCurrentIndex(avail); - - emit changeState(Shared::Global::fromInt(avail)); -} - void Squawk::writeSettings() { QSettings settings; settings.beginGroup("ui"); - settings.beginGroup("window"); - settings.setValue("geometry", saveGeometry()); - settings.setValue("state", saveState()); - settings.endGroup(); - - settings.setValue("splitter", m_ui->splitter->saveState()); - - settings.setValue("availability", m_ui->comboBox->currentIndex()); - - 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 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.beginGroup("window"); + settings.setValue("geometry", saveGeometry()); + settings.setValue("state", saveState()); + 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 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.endGroup(); settings.sync(); - - qDebug() << "Saved settings"; } void Squawk::onItemCollepsed(const QModelIndex& index) @@ -853,24 +533,6 @@ void Squawk::onItemCollepsed(const QModelIndex& index) } } -void Squawk::requestPassword(const QString& account, bool authenticationError) { - if (authenticationError) { - dialogueQueue.addAction(account, DialogQueue::askCredentials); - } else { - dialogueQueue.addAction(account, DialogQueue::askPassword); - } - -} - -void Squawk::subscribeConversation(Conversation* conv) -{ - connect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed); - connect(conv, &Conversation::sendMessage, this, &Squawk::onConversationMessage); - connect(conv, &Conversation::replaceMessage, this, &Squawk::onConversationReplaceMessage); - connect(conv, &Conversation::resendMessage, this, &Squawk::onConversationResend); - connect(conv, &Conversation::notifyableMessage, this, &Squawk::notify); -} - void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous) { if (restoreSelection.isValid() && restoreSelection == current) { @@ -942,16 +604,12 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn currentConversation = new Chat(acc, contact); } else if (room != nullptr) { currentConversation = new Room(acc, room); - - if (!room->getJoined()) { - emit setRoomJoined(id->account, id->name, true); - } } if (!testAttribute(Qt::WA_TranslucentBackground)) { currentConversation->setFeedFrames(true, false, true, true); } - subscribeConversation(currentConversation); + emit openedConversation(); if (res.size() > 0) { currentConversation->setPalResource(res); @@ -961,18 +619,10 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn delete id; } else { - if (currentConversation != nullptr) { - currentConversation->deleteLater(); - currentConversation = nullptr; - m_ui->filler->show(); - } + closeCurrentConversation(); } } else { - if (currentConversation != nullptr) { - currentConversation->deleteLater(); - currentConversation = nullptr; - m_ui->filler->show(); - } + closeCurrentConversation(); } } @@ -997,7 +647,12 @@ void Squawk::onAboutSquawkCalled() about->show(); } -void Squawk::onAboutSquawkClosed() +Models::Roster::ElId Squawk::currentConversationId() const { - about = nullptr; + if (currentConversation == nullptr) { + return Models::Roster::ElId(); + } else { + return Models::Roster::ElId(currentConversation->getAccount(), currentConversation->getJid()); + } } + diff --git a/ui/squawk.h b/ui/squawk.h index aa52153..5ffe090 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -39,7 +39,6 @@ #include "widgets/vcard/vcard.h" #include "widgets/settings/settings.h" #include "widgets/about.h" -#include "dialogqueue.h" #include "shared/shared.h" #include "shared/global.h" @@ -48,84 +47,57 @@ namespace Ui { class Squawk; } +class Application; + class Squawk : public QMainWindow { Q_OBJECT - friend class DialogQueue; + friend class Application; public: - explicit Squawk(QWidget *parent = nullptr); + explicit Squawk(Models::Roster& rosterModel, QWidget *parent = nullptr); ~Squawk() override; signals: + void closing(); void newAccountRequest(const QMap&); - void modifyAccountRequest(const QString&, const QMap&); void removeAccountRequest(const QString&); void connectAccount(const QString&); void disconnectAccount(const QString&); void changeState(Shared::Availability state); - void sendMessage(const QString& account, const Shared::Message& data); - void replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data); - void resendMessage(const QString& account, const QString& jid, const QString& id); - void requestArchive(const QString& account, const QString& jid, int count, const QString& before); - void subscribeContact(const QString& account, const QString& jid, const QString& reason); - void unsubscribeContact(const QString& account, const QString& jid, const QString& reason); void removeContactRequest(const QString& account, const QString& jid); void addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet& groups); void addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName); void removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName); void renameContactRequest(const QString& account, const QString& jid, const QString& newName); - void setRoomJoined(const QString& account, const QString& jid, bool joined); - void setRoomAutoJoin(const QString& account, const QString& jid, bool joined); void addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin); void removeRoomRequest(const QString& account, const QString& jid); - void fileDownloadRequest(const QString& url); void requestVCard(const QString& account, const QString& jid); void uploadVCard(const QString& account, const Shared::VCard& card); - void responsePassword(const QString& account, const QString& password); - void localPathInvalid(const QString& path); void changeDownloadsPath(const QString& path); + + void notify(const QString& account, const Shared::Message& msg); + void changeSubscription(const Models::Roster::ElId& id, bool subscribe); + void openedConversation(); + void openConversation(const Models::Roster::ElId& id, const QString& resource = ""); + + void modifyAccountRequest(const QString&, const QMap&); +public: + Models::Roster::ElId currentConversationId() const; + void closeCurrentConversation(); + public slots: void writeSettings(); - void readSettings(); - void newAccount(const QMap& account); - void changeAccount(const QString& account, const QMap& data); - void removeAccount(const QString& account); - void addGroup(const QString& account, const QString& name); - void removeGroup(const QString& account, const QString& name); - void addContact(const QString& account, const QString& jid, const QString& group, const QMap& data); - void removeContact(const QString& account, const QString& jid, const QString& group); - void removeContact(const QString& account, const QString& jid); - void changeContact(const QString& account, const QString& jid, const QMap& data); - void addPresence(const QString& account, const QString& jid, const QString& name, const QMap& data); - void removePresence(const QString& account, const QString& jid, const QString& name); void stateChanged(Shared::Availability state); - void accountMessage(const QString& account, const Shared::Message& data); - void responseArchive(const QString& account, const QString& jid, const std::list& list, bool last); - void addRoom(const QString& account, const QString jid, const QMap& data); - void changeRoom(const QString& account, const QString jid, const QMap& data); - void removeRoom(const QString& account, const QString jid); - void addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data); - void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data); - void removeRoomParticipant(const QString& account, const QString& jid, const QString& name); - void fileError(const std::list msgs, const QString& error, bool up); - void fileProgress(const std::list msgs, qreal value, bool up); - void fileDownloadComplete(const std::list msgs, const QString& path); - void fileUploadComplete(const std::list msgs, const QString& url, const QString& path); void responseVCard(const QString& jid, const Shared::VCard& card); - void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap& data); - void requestPassword(const QString& account, bool authenticationError); private: - typedef std::map Conversations; QScopedPointer m_ui; Accounts* accounts; Settings* preferences; About* about; - DialogQueue dialogueQueue; Models::Roster& rosterModel; - Conversations conversations; QMenu* contextMenu; std::map vCards; Conversation* currentConversation; @@ -134,9 +106,7 @@ private: protected: void closeEvent(QCloseEvent * event) override; - -protected slots: - void notify(const QString& account, const Shared::Message& msg); + void expand(const QModelIndex& index); private slots: void onAccounts(); @@ -148,27 +118,17 @@ private slots: void onAccountsSizeChanged(unsigned int size); void onAccountsClosed(); void onPreferencesClosed(); - void onConversationClosed(QObject* parent = 0); void onVCardClosed(); void onVCardSave(const Shared::VCard& card, const QString& account); void onActivateVCard(const QString& account, const QString& jid, bool edition = false); void onComboboxActivated(int index); void onRosterItemDoubleClicked(const QModelIndex& item); - void onConversationMessage(const Shared::Message& msg); - void onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg); - void onConversationResend(const QString& id); - void onRequestArchive(const QString& account, const QString& jid, const QString& before); void onRosterContextMenu(const QPoint& point); void onItemCollepsed(const QModelIndex& index); void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous); void onContextAboutToHide(); void onAboutSquawkCalled(); void onAboutSquawkClosed(); - - void onUnnoticedMessage(const QString& account, const Shared::Message& msg); - -private: - void subscribeConversation(Conversation* conv); }; #endif // SQUAWK_H From 3916aec358cb9ec81e7d55930bdc3ae55c38bc79 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 23 Apr 2022 16:58:08 +0300 Subject: [PATCH 050/137] unread messages count now is displayed on the launcher icon --- main/application.cpp | 18 ++++++++++++++++-- main/application.h | 1 + main/main.cpp | 1 + ui/models/element.cpp | 1 + ui/models/element.h | 1 + ui/models/roster.cpp | 13 +++++++++++++ ui/models/roster.h | 2 ++ 7 files changed, 35 insertions(+), 2 deletions(-) diff --git a/main/application.cpp b/main/application.cpp index f6ffe07..696ec03 100644 --- a/main/application.cpp +++ b/main/application.cpp @@ -21,7 +21,7 @@ Application::Application(Core::Squawk* p_core): availability(Shared::Availability::offline), core(p_core), squawk(nullptr), - notifications("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus()), + notifications("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications"), roster(), conversations(), dialogueQueue(roster), @@ -29,6 +29,7 @@ Application::Application(Core::Squawk* p_core): destroyingSquawk(false) { connect(&roster, &Models::Roster::unnoticedMessage, this, &Application::notify); + connect(&roster, &Models::Roster::unreadMessagesCountChanged, this, &Application::unreadMessagesCountChanged); //connecting myself to the backed @@ -100,6 +101,7 @@ void Application::quit() emit quitting(); writeSettings(); + unreadMessagesCountChanged(0); //this notification persist in the desktop, for now I'll zero it on quit not to confuse people for (Conversations::const_iterator itr = conversations.begin(), end = conversations.end(); itr != end; ++itr) { disconnect(itr->second, &Conversation::destroyed, this, &Application::onConversationClosed); itr->second->close(); @@ -212,7 +214,7 @@ void Application::notify(const QString& account, const Shared::Message& msg) args << body; args << QStringList(); args << QVariantMap({ - {"desktop-entry", QString(QCoreApplication::applicationName())}, + {"desktop-entry", qApp->desktopFileName()}, {"category", QString("message")}, // {"sound-file", "/path/to/macaw/squawk"}, {"sound-name", QString("message-new-instant")} @@ -225,6 +227,18 @@ void Application::notify(const QString& account, const Shared::Message& msg) } } +void Application::unreadMessagesCountChanged(int count) +{ + QDBusMessage signal = QDBusMessage::createSignal("/", "com.canonical.Unity.LauncherEntry", "Update"); + signal << qApp->desktopFileName() + QLatin1String(".desktop"); + signal << QVariantMap ({ + {"count-visible", count != 0}, + {"count", count} + }); + QDBusConnection::sessionBus().send(signal); +} + + void Application::setState(Shared::Availability p_availability) { if (availability != p_availability) { diff --git a/main/application.h b/main/application.h index 15adce7..7d70877 100644 --- a/main/application.h +++ b/main/application.h @@ -65,6 +65,7 @@ public slots: protected slots: void notify(const QString& account, const Shared::Message& msg); + void unreadMessagesCountChanged(int count); void setState(Shared::Availability availability); void changeAccount(const QString& account, const QMap& data); diff --git a/main/main.cpp b/main/main.cpp index 77719a2..60b3c83 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -50,6 +50,7 @@ int main(int argc, char *argv[]) QApplication::setOrganizationName("macaw.me"); QApplication::setApplicationDisplayName("Squawk"); QApplication::setApplicationVersion("0.2.2"); + app.setDesktopFileName("squawk"); QTranslator qtTranslator; qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); diff --git a/ui/models/element.cpp b/ui/models/element.cpp index 4e741a4..0c709ab 100644 --- a/ui/models/element.cpp +++ b/ui/models/element.cpp @@ -171,6 +171,7 @@ void Models::Element::fileError(const QString& messageId, const QString& error, void Models::Element::onFeedUnreadMessagesCountChanged() { + emit unreadMessagesCountChanged(); if (type == contact) { changed(4); } else if (type == room) { diff --git a/ui/models/element.h b/ui/models/element.h index 94d67cb..dd90ea1 100644 --- a/ui/models/element.h +++ b/ui/models/element.h @@ -52,6 +52,7 @@ signals: void requestArchive(const QString& before); void fileDownloadRequest(const QString& url); void unnoticedMessage(const QString& account, const Shared::Message& msg); + void unreadMessagesCountChanged(); void localPathInvalid(const QString& path); protected: diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index fef3e43..b2caf6b 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -463,6 +463,7 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons connect(contact, &Contact::fileDownloadRequest, this, &Roster::fileDownloadRequest); connect(contact, &Contact::unnoticedMessage, this, &Roster::unnoticedMessage); connect(contact, &Contact::localPathInvalid, this, &Roster::localPathInvalid); + connect(contact, &Contact::unreadMessagesCountChanged, this, &Roster::recalculateUnreadMessages); contacts.insert(std::make_pair(id, contact)); } else { contact = itr->second; @@ -805,6 +806,7 @@ void Models::Roster::addRoom(const QString& account, const QString jid, const QM connect(room, &Room::fileDownloadRequest, this, &Roster::fileDownloadRequest); connect(room, &Room::unnoticedMessage, this, &Roster::unnoticedMessage); connect(room, &Room::localPathInvalid, this, &Roster::localPathInvalid); + connect(room, &Room::unreadMessagesCountChanged, this, &Roster::recalculateUnreadMessages); rooms.insert(std::make_pair(id, room)); acc->appendChild(room); } @@ -1049,3 +1051,14 @@ void Models::Roster::onAccountReconnected() } } +void Models::Roster::recalculateUnreadMessages() +{ + int count(0); + for (const std::pair& pair : contacts) { + count += pair.second->getMessagesCount(); + } + for (const std::pair& pair : rooms) { + count += pair.second->getMessagesCount(); + } + emit unreadMessagesCountChanged(count); +} diff --git a/ui/models/roster.h b/ui/models/roster.h index 60adf13..249947b 100644 --- a/ui/models/roster.h +++ b/ui/models/roster.h @@ -99,6 +99,7 @@ public: signals: void requestArchive(const QString& account, const QString& jid, const QString& before); void fileDownloadRequest(const QString& url); + void unreadMessagesCountChanged(int count); void unnoticedMessage(const QString& account, const Shared::Message& msg); void localPathInvalid(const QString& path); @@ -113,6 +114,7 @@ private slots: void onChildIsAboutToBeMoved(Item* source, int first, int last, Item* destination, int newIndex); void onChildMoved(); void onElementRequestArchive(const QString& before); + void recalculateUnreadMessages(); private: Item* root; From e58213b2943871f4013dedc4c8577fd373437270 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 24 Apr 2022 18:52:29 +0300 Subject: [PATCH 051/137] Now notifications have actions! Some more usefull functions to roster model --- CHANGELOG.md | 2 + main/application.cpp | 87 +++++++++++++++++++++++--- main/application.h | 6 ++ ui/models/contact.cpp | 10 +++ ui/models/contact.h | 1 + ui/models/element.cpp | 5 ++ ui/models/element.h | 1 + ui/models/room.cpp | 10 +++ ui/models/room.h | 1 + ui/models/roster.cpp | 83 +++++++++++++++++++----- ui/models/roster.h | 4 +- ui/squawk.cpp | 5 ++ ui/squawk.h | 1 + ui/widgets/messageline/messagefeed.cpp | 18 ++++-- ui/widgets/messageline/messagefeed.h | 1 + 15 files changed, 205 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4daf652..241d61d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ ### New features - new "About" window with links, license, gratitudes - if the authentication failed Squawk will ask againg for your password and login +- now there is an amount of unread messages showing on top of Squawk launcher icon +- notifications now have buttons to open a conversation or to mark that message as read ## Squawk 0.2.1 (Apr 02, 2022) ### Bug fixes diff --git a/main/application.cpp b/main/application.cpp index 696ec03..ddf17b3 100644 --- a/main/application.cpp +++ b/main/application.cpp @@ -26,7 +26,8 @@ Application::Application(Core::Squawk* p_core): conversations(), dialogueQueue(roster), nowQuitting(false), - destroyingSquawk(false) + destroyingSquawk(false), + storage() { connect(&roster, &Models::Roster::unnoticedMessage, this, &Application::notify); connect(&roster, &Models::Roster::unreadMessagesCountChanged, this, &Application::unreadMessagesCountChanged); @@ -90,6 +91,23 @@ Application::Application(Core::Squawk* p_core): connect(core, &Core::Squawk::requestPassword, this, &Application::requestPassword); connect(core, &Core::Squawk::ready, this, &Application::readSettings); + QDBusConnection sys = QDBusConnection::sessionBus(); + sys.connect( + "org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + "NotificationClosed", + this, + SLOT(onNotificationClosed(quint32, quint32)) + ); + sys.connect( + "org.freedesktop.Notifications", + "/org/freedesktop/Notifications", + "org.freedesktop.Notifications", + "ActionInvoked", + this, + SLOT(onNotificationInvoked(quint32, const QString&)) + ); } Application::~Application() {} @@ -188,12 +206,14 @@ void Application::onSquawkDestroyed() { void Application::notify(const QString& account, const Shared::Message& msg) { - QString name = QString(roster.getContactName(account, msg.getPenPalJid())); - QString path = QString(roster.getContactIconPath(account, msg.getPenPalJid(), msg.getPenPalResource())); + QString jid = msg.getPenPalJid(); + QString name = QString(roster.getContactName(account, jid)); + QString path = QString(roster.getContactIconPath(account, jid, msg.getPenPalResource())); QVariantList args; args << QString(); - args << qHash(msg.getId()); + uint32_t notificationId = qHash(msg.getId()); + args << notificationId; if (path.size() > 0) { args << path; } else { @@ -212,7 +232,10 @@ void Application::notify(const QString& account, const Shared::Message& msg) } args << body; - args << QStringList(); + args << QStringList({ + "markAsRead", tr("Mark as Read"), + "openConversation", tr("Open conversation") + }); args << QVariantMap({ {"desktop-entry", qApp->desktopFileName()}, {"category", QString("message")}, @@ -222,11 +245,40 @@ void Application::notify(const QString& account, const Shared::Message& msg) args << -1; notifications.callWithArgumentList(QDBus::AutoDetect, "Notify", args); + storage.insert(std::make_pair(notificationId, std::make_pair(Models::Roster::ElId(account, name), msg.getId()))); + if (squawk != nullptr) { QApplication::alert(squawk); } } +void Application::onNotificationClosed(quint32 id, quint32 reason) +{ + Notifications::const_iterator itr = storage.find(id); + if (itr != storage.end()) { + if (reason == 2) { //dissmissed by user (https://specifications.freedesktop.org/notification-spec/latest/ar01s09.html) + //TODO may ba also mark as read? + } + if (reason != 1) { //just expired, can be activated again from history, so no removing for now + storage.erase(id); + qDebug() << "Notification" << id << "was closed"; + } + } +} + +void Application::onNotificationInvoked(quint32 id, const QString& action) +{ + qDebug() << "Notification" << id << action << "request"; + Notifications::const_iterator itr = storage.find(id); + if (itr != storage.end()) { + if (action == "markAsRead") { + roster.markMessageAsRead(itr->second.first, itr->second.second); + } else if (action == "openConversation") { + focusConversation(itr->second.first, "", itr->second.second); + } + } +} + void Application::unreadMessagesCountChanged(int count) { QDBusMessage signal = QDBusMessage::createSignal("/", "com.canonical.Unity.LauncherEntry", "Update"); @@ -238,6 +290,27 @@ void Application::unreadMessagesCountChanged(int count) QDBusConnection::sessionBus().send(signal); } +void Application::focusConversation(const Models::Roster::ElId& id, const QString& resource, const QString& messageId) +{ + if (squawk != nullptr) { + if (squawk->currentConversationId() != id) { + QModelIndex index = roster.getContactIndex(id.account, id.name, resource); + squawk->select(index); + } + + if (squawk->isMinimized()) { + squawk->showNormal(); + } else { + squawk->show(); + } + squawk->raise(); + squawk->activateWindow(); + } else { + openConversation(id, resource); + } + + //TODO focus messageId; +} void Application::setState(Shared::Availability p_availability) { @@ -343,7 +416,7 @@ void Application::openConversation(const Models::Roster::ElId& id, const QString conv = itr->second; } else { Models::Element* el = roster.getElement(id); - if (el != NULL) { + if (el != nullptr) { if (el->type == Models::Item::room) { created = true; Models::Room* room = static_cast(el); @@ -409,7 +482,7 @@ void Application::onSquawkOpenedConversation() { Models::Roster::ElId id = squawk->currentConversationId(); const Models::Element* el = roster.getElementConst(id); - if (el != NULL && el->isRoom() && !static_cast(el)->getJoined()) { + if (el != nullptr && el->isRoom() && !static_cast(el)->getJoined()) { emit setRoomJoined(id.account, id.name, true); } } diff --git a/main/application.h b/main/application.h index 7d70877..301edc4 100644 --- a/main/application.h +++ b/main/application.h @@ -89,14 +89,19 @@ private slots: void stateChanged(Shared::Availability state); void onSquawkClosing(); void onSquawkDestroyed(); + void onNotificationClosed(quint32 id, quint32 reason); + void onNotificationInvoked(quint32 id, const QString& action); + private: void createMainWindow(); void subscribeConversation(Conversation* conv); void checkForTheLastWindow(); + void focusConversation(const Models::Roster::ElId& id, const QString& resource = "", const QString& messageId = ""); private: typedef std::map Conversations; + typedef std::map> Notifications; Shared::Availability availability; Core::Squawk* core; @@ -107,6 +112,7 @@ private: DialogQueue dialogueQueue; bool nowQuitting; bool destroyingSquawk; + Notifications storage; }; #endif // APPLICATION_H diff --git a/ui/models/contact.cpp b/ui/models/contact.cpp index a0c70ac..d5c7dc4 100644 --- a/ui/models/contact.cpp +++ b/ui/models/contact.cpp @@ -155,6 +155,16 @@ void Models::Contact::removePresence(const QString& name) } } +Models::Presence * Models::Contact::getPresence(const QString& name) +{ + QMap::iterator itr = presences.find(name); + if (itr == presences.end()) { + return nullptr; + } else { + return itr.value(); + } +} + void Models::Contact::refresh() { QDateTime lastActivity; diff --git a/ui/models/contact.h b/ui/models/contact.h index a8b80a3..c4fc131 100644 --- a/ui/models/contact.h +++ b/ui/models/contact.h @@ -51,6 +51,7 @@ public: void addPresence(const QString& name, const QMap& data); void removePresence(const QString& name); + Presence* getPresence(const QString& name); QString getContactName() const; QString getStatus() const; diff --git a/ui/models/element.cpp b/ui/models/element.cpp index 0c709ab..acea46f 100644 --- a/ui/models/element.cpp +++ b/ui/models/element.cpp @@ -134,6 +134,11 @@ unsigned int Models::Element::getMessagesCount() const return feed->unreadMessagesCount(); } +bool Models::Element::markMessageAsRead(const QString& id) const +{ + return feed->markMessageAsRead(id); +} + void Models::Element::addMessage(const Shared::Message& data) { feed->addMessage(data); diff --git a/ui/models/element.h b/ui/models/element.h index dd90ea1..c6d3d6e 100644 --- a/ui/models/element.h +++ b/ui/models/element.h @@ -42,6 +42,7 @@ public: void addMessage(const Shared::Message& data); void changeMessage(const QString& id, const QMap& data); unsigned int getMessagesCount() const; + bool markMessageAsRead(const QString& id) const; void responseArchive(const std::list list, bool last); bool isRoom() const; void fileProgress(const QString& messageId, qreal value, bool up); diff --git a/ui/models/room.cpp b/ui/models/room.cpp index a6a36d0..4aaa07e 100644 --- a/ui/models/room.cpp +++ b/ui/models/room.cpp @@ -264,6 +264,16 @@ void Models::Room::removeParticipant(const QString& p_name) } } +Models::Participant * Models::Room::getParticipant(const QString& p_name) +{ + std::map::const_iterator itr = participants.find(p_name); + if (itr == participants.end()) { + return nullptr; + } else { + return itr->second; + } +} + void Models::Room::handleParticipantUpdate(std::map::const_iterator itr, const QMap& data) { Participant* part = itr->second; diff --git a/ui/models/room.h b/ui/models/room.h index a51a537..707b35b 100644 --- a/ui/models/room.h +++ b/ui/models/room.h @@ -58,6 +58,7 @@ public: void addParticipant(const QString& name, const QMap& data); void changeParticipant(const QString& name, const QMap& data); void removeParticipant(const QString& name); + Participant* getParticipant(const QString& name); void toOfflineState() override; QString getDisplayedName() const override; diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index b2caf6b..fbb7e52 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -549,8 +549,8 @@ void Models::Roster::removeGroup(const QString& account, const QString& name) void Models::Roster::changeContact(const QString& account, const QString& jid, const QMap& data) { - Element* el = getElement({account, jid}); - if (el != NULL) { + Element* el = getElement(ElId(account, jid)); + if (el != nullptr) { for (QMap::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) { el->update(itr.key(), itr.value()); } @@ -559,8 +559,8 @@ void Models::Roster::changeContact(const QString& account, const QString& jid, c void Models::Roster::changeMessage(const QString& account, const QString& jid, const QString& id, const QMap& data) { - Element* el = getElement({account, jid}); - if (el != NULL) { + Element* el = getElement(ElId(account, jid)); + if (el != nullptr) { el->changeMessage(id, data); } else { qDebug() << "A request to change a message of the contact " << jid << " in the account " << account << " but it wasn't found"; @@ -707,8 +707,8 @@ void Models::Roster::removePresence(const QString& account, const QString& jid, void Models::Roster::addMessage(const QString& account, const Shared::Message& data) { - Element* el = getElement({account, data.getPenPalJid()}); - if (el != NULL) { + Element* el = getElement(ElId(account, data.getPenPalJid())); + if (el != nullptr) { el->addMessage(data); } } @@ -948,9 +948,18 @@ const Models::Element * Models::Roster::getElementConst(const Models::Roster::El } } - return NULL; + return nullptr; } +bool Models::Roster::markMessageAsRead(const Models::Roster::ElId& elementId, const QString& messageId) +{ + const Element* el = getElementConst(elementId); + if (el != nullptr) { + return el->markMessageAsRead(messageId); + } else { + return false; + } +} QModelIndex Models::Roster::getAccountIndex(const QString& name) { @@ -968,7 +977,7 @@ QModelIndex Models::Roster::getGroupIndex(const QString& account, const QString& if (itr == accounts.end()) { return QModelIndex(); } else { - std::map::const_iterator gItr = groups.find({account, name}); + std::map::const_iterator gItr = groups.find(ElId(account, name)); if (gItr == groups.end()) { return QModelIndex(); } else { @@ -978,6 +987,48 @@ QModelIndex Models::Roster::getGroupIndex(const QString& account, const QString& } } +QModelIndex Models::Roster::getContactIndex(const QString& account, const QString& jid, const QString& resource) +{ + std::map::const_iterator itr = accounts.find(account); + if (itr == accounts.end()) { + return QModelIndex(); + } else { + Account* acc = itr->second; + QModelIndex accIndex = index(acc->row(), 0, QModelIndex()); + std::map::const_iterator cItr = contacts.find(ElId(account, jid)); + if (cItr != contacts.end()) { + QModelIndex contactIndex = index(acc->getContact(jid), 0, accIndex); + if (resource.size() == 0) { + return contactIndex; + } else { + Presence* pres = cItr->second->getPresence(resource); + if (pres != nullptr) { + return index(pres->row(), 0, contactIndex); + } else { + return contactIndex; + } + } + } else { + std::map::const_iterator rItr = rooms.find(ElId(account, jid)); + if (rItr != rooms.end()) { + QModelIndex roomIndex = index(rItr->second->row(), 0, accIndex); + if (resource.size() == 0) { + return roomIndex; + } else { + Participant* part = rItr->second->getParticipant(resource); + if (part != nullptr) { + return index(part->row(), 0, roomIndex); + } else { + return roomIndex; + } + } + } else { + return QModelIndex(); + } + } + } +} + void Models::Roster::onElementRequestArchive(const QString& before) { Element* el = static_cast(sender()); @@ -988,7 +1039,7 @@ void Models::Roster::responseArchive(const QString& account, const QString& jid, { ElId id(account, jid); Element* el = getElement(id); - if (el != NULL) { + if (el != nullptr) { el->responseArchive(list, last); } } @@ -996,8 +1047,8 @@ void Models::Roster::responseArchive(const QString& account, const QString& jid, void Models::Roster::fileProgress(const std::list& msgs, qreal value, bool up) { for (const Shared::MessageInfo& info : msgs) { - Element* el = getElement({info.account, info.jid}); - if (el != NULL) { + Element* el = getElement(ElId(info.account, info.jid)); + if (el != nullptr) { el->fileProgress(info.messageId, value, up); } } @@ -1006,8 +1057,8 @@ void Models::Roster::fileProgress(const std::list& msgs, qr void Models::Roster::fileComplete(const std::list& msgs, bool up) { for (const Shared::MessageInfo& info : msgs) { - Element* el = getElement({info.account, info.jid}); - if (el != NULL) { + Element* el = getElement(ElId(info.account, info.jid)); + if (el != nullptr) { el->fileComplete(info.messageId, up); } } @@ -1016,8 +1067,8 @@ void Models::Roster::fileComplete(const std::list& msgs, bo void Models::Roster::fileError(const std::list& msgs, const QString& err, bool up) { for (const Shared::MessageInfo& info : msgs) { - Element* el = getElement({info.account, info.jid}); - if (el != NULL) { + Element* el = getElement(ElId(info.account, info.jid)); + if (el != nullptr) { el->fileError(info.messageId, err, up); } } @@ -1031,7 +1082,7 @@ Models::Element * Models::Roster::getElement(const Models::Roster::ElId& id) Models::Item::Type Models::Roster::getContactType(const Models::Roster::ElId& id) const { const Models::Element* el = getElementConst(id); - if (el == NULL) { + if (el == nullptr) { return Item::root; } diff --git a/ui/models/roster.h b/ui/models/roster.h index 249947b..efc50f2 100644 --- a/ui/models/roster.h +++ b/ui/models/roster.h @@ -88,6 +88,8 @@ public: 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 = ""); + bool markMessageAsRead(const ElId& elementId, const QString& messageId); void responseArchive(const QString& account, const QString& jid, const std::list& list, bool last); void fileProgress(const std::list& msgs, qreal value, bool up); @@ -115,7 +117,7 @@ private slots: void onChildMoved(); void onElementRequestArchive(const QString& before); void recalculateUnreadMessages(); - + private: Item* root; std::map accounts; diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 434b442..9b6158c 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -656,3 +656,8 @@ Models::Roster::ElId Squawk::currentConversationId() const } } +void Squawk::select(QModelIndex index) +{ + m_ui->roster->scrollTo(index, QAbstractItemView::EnsureVisible); + m_ui->roster->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); +} diff --git a/ui/squawk.h b/ui/squawk.h index 5ffe090..15a73dd 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -90,6 +90,7 @@ public slots: void writeSettings(); void stateChanged(Shared::Availability state); void responseVCard(const QString& jid, const Shared::VCard& card); + void select(QModelIndex index); private: QScopedPointer m_ui; diff --git a/ui/widgets/messageline/messagefeed.cpp b/ui/widgets/messageline/messagefeed.cpp index 521e981..ad67bb3 100644 --- a/ui/widgets/messageline/messagefeed.cpp +++ b/ui/widgets/messageline/messagefeed.cpp @@ -318,12 +318,7 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const case Bulk: { FeedItem item; item.id = msg->getId(); - - std::set::const_iterator umi = unreadMessages->find(item.id); - if (umi != unreadMessages->end()) { - unreadMessages->erase(umi); - emit unreadMessagesCountChanged(); - } + markMessageAsRead(item.id); item.sentByMe = sentByMe(*msg); item.date = msg->getTime(); @@ -373,6 +368,17 @@ int Models::MessageFeed::rowCount(const QModelIndex& parent) const return storage.size(); } +bool Models::MessageFeed::markMessageAsRead(const QString& id) const +{ + std::set::const_iterator umi = unreadMessages->find(id); + if (umi != unreadMessages->end()) { + unreadMessages->erase(umi); + emit unreadMessagesCountChanged(); + return true; + } + return false; +} + unsigned int Models::MessageFeed::unreadMessagesCount() const { return unreadMessages->size(); diff --git a/ui/widgets/messageline/messagefeed.h b/ui/widgets/messageline/messagefeed.h index c9701ae..f362989 100644 --- a/ui/widgets/messageline/messagefeed.h +++ b/ui/widgets/messageline/messagefeed.h @@ -72,6 +72,7 @@ public: void reportLocalPathInvalid(const QString& messageId); unsigned int unreadMessagesCount() const; + bool markMessageAsRead(const QString& id) const; void fileProgress(const QString& messageId, qreal value, bool up); void fileError(const QString& messageId, const QString& error, bool up); void fileComplete(const QString& messageId, bool up); From 2fcc432aef6e1656ffff967aad7bbcbb9c2e2cfd Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 26 Apr 2022 23:08:25 +0300 Subject: [PATCH 052/137] some polish --- main/application.cpp | 3 ++- shared/utils.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/main/application.cpp b/main/application.cpp index ddf17b3..410cd81 100644 --- a/main/application.cpp +++ b/main/application.cpp @@ -220,7 +220,7 @@ void Application::notify(const QString& account, const Shared::Message& msg) args << QString("mail-message"); //TODO should here better be unknown user icon? } if (msg.getType() == Shared::Message::groupChat) { - args << msg.getFromResource() + " from " + name; + args << msg.getFromResource() + tr(" from ") + name; } else { args << name; } @@ -239,6 +239,7 @@ void Application::notify(const QString& account, const Shared::Message& msg) args << QVariantMap({ {"desktop-entry", qApp->desktopFileName()}, {"category", QString("message")}, + {"urgency", 1}, // {"sound-file", "/path/to/macaw/squawk"}, {"sound-name", QString("message-new-instant")} }); diff --git a/shared/utils.cpp b/shared/utils.cpp index a7a4ecb..fdaf9a4 100644 --- a/shared/utils.cpp +++ b/shared/utils.cpp @@ -40,5 +40,5 @@ QString Shared::processMessageBody(const QString& msg) { QString processed = msg.toHtmlEscaped(); processed.replace(urlReg, "\\1"); - return "

" + processed + "

"; + return processed; } From d86e2c28a02955135759fe3835676c1a16b4396f Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 27 Apr 2022 01:17:53 +0300 Subject: [PATCH 053/137] an attempt to display text in a better way with QTextDocument + QTextBrowser --- shared/utils.cpp | 2 +- ui/utils/CMakeLists.txt | 2 - ui/utils/textmeter.cpp | 233 --------------------- ui/utils/textmeter.h | 68 ------ ui/widgets/messageline/feedview.cpp | 6 +- ui/widgets/messageline/messagedelegate.cpp | 104 ++++++--- ui/widgets/messageline/messagedelegate.h | 11 +- 7 files changed, 88 insertions(+), 338 deletions(-) delete mode 100644 ui/utils/textmeter.cpp delete mode 100644 ui/utils/textmeter.h diff --git a/shared/utils.cpp b/shared/utils.cpp index a7a4ecb..518d288 100644 --- a/shared/utils.cpp +++ b/shared/utils.cpp @@ -40,5 +40,5 @@ QString Shared::processMessageBody(const QString& msg) { QString processed = msg.toHtmlEscaped(); processed.replace(urlReg, "\\1"); - return "

" + processed + "

"; + return "

" + processed + "

"; } diff --git a/ui/utils/CMakeLists.txt b/ui/utils/CMakeLists.txt index 823287d..b46d30d 100644 --- a/ui/utils/CMakeLists.txt +++ b/ui/utils/CMakeLists.txt @@ -15,6 +15,4 @@ target_sources(squawk PRIVATE resizer.h shadowoverlay.cpp shadowoverlay.h - textmeter.cpp - textmeter.h ) diff --git a/ui/utils/textmeter.cpp b/ui/utils/textmeter.cpp deleted file mode 100644 index 51c6d54..0000000 --- a/ui/utils/textmeter.cpp +++ /dev/null @@ -1,233 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 "textmeter.h" -#include -#include - -TextMeter::TextMeter(): - base(), - fonts() -{ -} - -TextMeter::~TextMeter() -{ -} - -void TextMeter::initializeFonts(const QFont& font) -{ - fonts.clear(); - QList supported = base.writingSystems(font.family()); - std::set sup; - std::set added({font.family()}); - for (const QFontDatabase::WritingSystem& system : supported) { - sup.insert(system); - } - fonts.push_back(QFontMetrics(font)); - QString style = base.styleString(font); - - QList systems = base.writingSystems(); - for (const QFontDatabase::WritingSystem& system : systems) { - if (sup.count(system) == 0) { - QStringList families = base.families(system); - if (!families.empty() && added.count(families.first()) == 0) { - QString family(families.first()); - QFont nfont = base.font(family, style, font.pointSize()); - if (added.count(nfont.family()) == 0) { - added.insert(family); - fonts.push_back(QFontMetrics(nfont)); - qDebug() << "Added font" << nfont.family() << "for" << system; - } - } - } - } -} - -QSize TextMeter::boundingSize(const QString& text, const QSize& limits) const -{ -// QString str("ridiculus mus. Suspendisse potenti. Cras pretium venenatis enim, faucibus accumsan ex"); -// bool first = true; -// int width = 0; -// QStringList list = str.split(" "); -// QFontMetrics m = fonts.front(); -// for (const QString& word : list) { -// if (first) { -// first = false; -// } else { -// width += m.horizontalAdvance(QChar::Space); -// } -// width += m.horizontalAdvance(word); -// for (const QChar& ch : word) { -// width += m.horizontalAdvance(ch); -// } -// } -// qDebug() << "together:" << m.horizontalAdvance(str); -// qDebug() << "apart:" << width; -// I cant measure or wrap text this way, this simple example shows that even this gives differen result -// The Qt implementation under it is thousands and thousands lines of code in QTextEngine -// I simply can't get though it - - if (text.size() == 0) { - return QSize (0, 0); - } - Helper current(limits.width()); - for (const QChar& ch : text) { - if (newLine(ch)) { - current.computeNewWord(); - if (current.height == 0) { - current.height = fonts.front().lineSpacing(); - } - current.beginNewLine(); - } else if (visible(ch)) { - bool found = false; - for (const QFontMetrics& metrics : fonts) { - if (metrics.inFont(ch)) { - current.computeChar(ch, metrics); - found = true; - break; - } - } - - if (!found) { - current.computeChar(ch, fonts.front()); - } - } - } - current.computeNewWord(); - current.beginNewLine(); - - int& height = current.size.rheight(); - if (height > 0) { - height -= fonts.front().leading(); - } - - return current.size; -} - -void TextMeter::Helper::computeChar(const QChar& ch, const QFontMetrics& metrics) -{ - int ha = metrics.horizontalAdvance(ch); - if (newWord(ch)) { - if (printOnLineBreak(ch)) { - if (!lineOverflow(metrics, ha, ch)){ - computeNewWord(); - } - } else { - computeNewWord(); - delayedSpaceWidth = ha; - lastSpace = ch; - } - } else { - lineOverflow(metrics, ha, ch); - } -} - -void TextMeter::Helper::computeNewLine(const QFontMetrics& metrics, int horizontalAdvance, const QChar& ch) -{ - if (wordBeganWithTheLine) { - text = word.chopped(1); - width = wordWidth - horizontalAdvance; - height = wordHeight; - } - if (width != metrics.horizontalAdvance(text)) { - qDebug() << "Kerning Error" << width - metrics.horizontalAdvance(text); - } - beginNewLine(); - if (wordBeganWithTheLine) { - word = ch; - wordWidth = horizontalAdvance; - wordHeight = metrics.lineSpacing(); - } - - wordBeganWithTheLine = true; - delayedSpaceWidth = 0; - lastSpace = QChar::Null; -} - -void TextMeter::Helper::beginNewLine() -{ - size.rheight() += height; - size.rwidth() = qMax(size.width(), width); - qDebug() << text; - text = ""; - width = 0; - height = 0; -} - -void TextMeter::Helper::computeNewWord() -{ - width += wordWidth + delayedSpaceWidth; - height = qMax(height, wordHeight); - if (lastSpace != QChar::Null) { - text += lastSpace; - } - text += word; - word = ""; - wordWidth = 0; - wordHeight = 0; - delayedSpaceWidth = 0; - lastSpace = QChar::Null; - wordBeganWithTheLine = false; -} - -bool TextMeter::Helper::lineOverflow(const QFontMetrics& metrics, int horizontalAdvance, const QChar& ch) -{ - wordHeight = qMax(wordHeight, metrics.lineSpacing()); - wordWidth += horizontalAdvance; - word += ch; - if (width + delayedSpaceWidth + wordWidth > maxWidth) { - computeNewLine(metrics, horizontalAdvance, ch); - return true; - } - return false; -} - - -bool TextMeter::newLine(const QChar& ch) -{ - return ch == QChar::LineFeed; -} - -bool TextMeter::newWord(const QChar& ch) -{ - return ch.isSpace() || ch.isPunct(); -} - -bool TextMeter::printOnLineBreak(const QChar& ch) -{ - return ch != QChar::Space; -} - -bool TextMeter::visible(const QChar& ch) -{ - return true; -} - -TextMeter::Helper::Helper(int p_maxWidth): - width(0), - height(0), - wordWidth(0), - wordHeight(0), - delayedSpaceWidth(0), - maxWidth(p_maxWidth), - wordBeganWithTheLine(true), - text(""), - word(""), - lastSpace(QChar::Null), - size(0, 0) -{ -} diff --git a/ui/utils/textmeter.h b/ui/utils/textmeter.h deleted file mode 100644 index 243d989..0000000 --- a/ui/utils/textmeter.h +++ /dev/null @@ -1,68 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 TEXTMETER_H -#define TEXTMETER_H - -#include -#include - -#include -#include -#include -#include - -class TextMeter -{ -public: - TextMeter(); - ~TextMeter(); - void initializeFonts(const QFont& font); - QSize boundingSize(const QString& text, const QSize& limits) const; - -private: - QFontDatabase base; - std::list fonts; - - struct Helper { - Helper(int maxWidth); - int width; - int height; - int wordWidth; - int wordHeight; - int delayedSpaceWidth; - int maxWidth; - bool wordBeganWithTheLine; - QString text; - QString word; - QChar lastSpace; - QSize size; - - void computeNewLine(const QFontMetrics& metrics, int horizontalAdvance, const QChar& ch); - void computeChar(const QChar& ch, const QFontMetrics& metrics); - void computeNewWord(); - void beginNewLine(); - bool lineOverflow(const QFontMetrics& metrics, int horizontalAdvance, const QChar& ch); - }; - - static bool newLine(const QChar& ch); - static bool newWord(const QChar& ch); - static bool visible(const QChar& ch); - static bool printOnLineBreak(const QChar& ch); - -}; - -#endif // TEXTMETER_H diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index de7f56f..e0c1477 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -343,6 +343,7 @@ void FeedView::paintEvent(QPaintEvent* event) QDateTime lastDate; bool first = true; + QRect viewportRect = vp->rect(); for (const QModelIndex& index : toRener) { QDateTime currentDate = index.data(Models::MessageFeed::Date).toDateTime(); option.rect = visualRect(index); @@ -356,7 +357,10 @@ void FeedView::paintEvent(QPaintEvent* event) } first = false; } - bool mouseOver = option.rect.contains(cursor) && vp->rect().contains(cursor); + QRect stripe = option.rect; + stripe.setLeft(0); + stripe.setWidth(viewportRect.width()); + bool mouseOver = stripe.contains(cursor) && viewportRect.contains(cursor); option.state.setFlag(QStyle::State_MouseOver, mouseOver); itemDelegate(index)->paint(&painter, option, index); diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index 4ddecee..1d094fa 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "messagedelegate.h" #include "messagefeed.h" @@ -42,7 +43,7 @@ MessageDelegate::MessageDelegate(QObject* parent): nickFont(), dateFont(), bodyMetrics(bodyFont), - bodyMeter(), + bodyRenderer(new QTextDocument()), nickMetrics(nickFont), dateMetrics(dateFont), buttonHeight(0), @@ -52,11 +53,13 @@ MessageDelegate::MessageDelegate(QObject* parent): bars(new std::map()), statusIcons(new std::map()), pencilIcons(new std::map()), - bodies(new std::map()), + bodies(new std::map()), previews(new std::map()), idsToKeep(new std::set()), clearingWidgets(false) { + bodyRenderer->setDocumentMargin(0); + QPushButton btn(QCoreApplication::translate("MessageLine", "Download")); buttonHeight = btn.sizeHint().height(); buttonWidth = btn.sizeHint().width(); @@ -83,7 +86,7 @@ MessageDelegate::~MessageDelegate() delete pair.second; } - for (const std::pair& pair: *bodies){ + for (const std::pair& pair: *bodies){ delete pair.second; } @@ -98,6 +101,7 @@ MessageDelegate::~MessageDelegate() delete bars; delete bodies; delete previews; + delete bodyRenderer; } void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const @@ -124,8 +128,6 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio opt.displayAlignment = Qt::AlignRight | Qt::AlignTop; } - QSize bodySize = bodyMeter.boundingSize(data.text, opt.rect.size()); - QRect rect; if (ntds) { painter->setFont(nickFont); @@ -168,15 +170,7 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio painter->restore(); QWidget* vp = static_cast(painter->device()); - if (data.text.size() > 0) { - QLabel* body = getBody(data); - body->setParent(vp); - body->setMinimumSize(bodySize); - body->setMaximumSize(bodySize); - body->move(opt.rect.left(), opt.rect.y()); - body->show(); - opt.rect.adjust(0, bodySize.height() + textMargin, 0, 0); - } + paintBody(data, painter, opt); painter->setFont(dateFont); QColor q = painter->pen().color(); QString dateString = data.date.toLocalTime().toString("hh:mm"); @@ -304,7 +298,12 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel Models::FeedItem data = qvariant_cast(vi); QSize messageSize(0, 0); if (data.text.size() > 0) { - messageSize = bodyMeter.boundingSize(data.text, messageRect.size()); + bodyRenderer->setPlainText(data.text); + bodyRenderer->setTextWidth(messageRect.size().width()); + + QSizeF size = bodyRenderer->size(); + size.setWidth(bodyRenderer->idealWidth()); + messageSize = QSize(qCeil(size.width()), qCeil(size.height())); messageSize.rheight() += textMargin; } @@ -393,7 +392,7 @@ void MessageDelegate::initializeFonts(const QFont& font) nickMetrics = QFontMetrics(nickFont); dateMetrics = QFontMetrics(dateFont); - bodyMeter.initializeFonts(bodyFont); + bodyRenderer->setDefaultFont(bodyFont); Preview::initializeFont(bodyFont); } @@ -573,34 +572,36 @@ QLabel * MessageDelegate::getPencilIcon(const Models::FeedItem& data) const return result; } -QLabel * MessageDelegate::getBody(const Models::FeedItem& data) const +QTextBrowser * MessageDelegate::getBody(const Models::FeedItem& data) const { - std::map::const_iterator itr = bodies->find(data.id); - QLabel* result = 0; + std::map::const_iterator itr = bodies->find(data.id); + QTextBrowser* result = 0; if (itr != bodies->end()) { result = itr->second; } else { - result = new QLabel(); + result = new QTextBrowser(); result->setFont(bodyFont); result->setContextMenuPolicy(Qt::NoContextMenu); - result->setWordWrap(true); + result->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + result->setContentsMargins(0, 0, 0, 0); + //result->viewport()->setAutoFillBackground(false); + result->document()->setDocumentMargin(0); + result->setFrameStyle(0); + result->setLineWidth(0); + //result->setAutoFillBackground(false); + //->setWordWrap(true); + result->setOpenExternalLinks(true); + //result->setTextInteractionFlags(result->textInteractionFlags() | Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse); result->setOpenExternalLinks(true); - result->setTextInteractionFlags(result->textInteractionFlags() | Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse); bodies->insert(std::make_pair(data.id, result)); } - result->setText(Shared::processMessageBody(data.text)); + result->setHtml(Shared::processMessageBody(data.text)); return result; } -void MessageDelegate::beginClearWidgets() -{ - idsToKeep->clear(); - clearingWidgets = true; -} - template void removeElements(std::map* elements, std::set* idsToKeep) { std::set toRemove; @@ -615,6 +616,51 @@ void removeElements(std::map* elements, std::set* idsToKee } } +int MessageDelegate::paintBody(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const +{ + if (data.text.size() > 0) { + bodyRenderer->setHtml(Shared::processMessageBody(data.text)); + bodyRenderer->setTextWidth(option.rect.size().width()); + painter->save(); + painter->translate(option.rect.topLeft()); + bodyRenderer->drawContents(painter); + painter->restore(); + QSize bodySize(qCeil(bodyRenderer->idealWidth()), qCeil(bodyRenderer->size().height())); + + + QTextBrowser* editor = nullptr; + if (option.state.testFlag(QStyle::State_MouseOver)) { + std::set ids({data.id}); + removeElements(bodies, &ids); + editor = getBody(data); + editor->setParent(static_cast(painter->device())); + } else { + std::map::const_iterator itr = bodies->find(data.id); + if (itr != bodies->end()) { + editor = itr->second; + } + } + if (editor != nullptr) { + editor->setMinimumSize(bodySize); + editor->setMaximumSize(bodySize); + editor->move(option.rect.left(), option.rect.y()); + editor->show(); + } + + option.rect.adjust(0, bodySize.height() + textMargin, 0, 0); + return bodySize.width(); + } + return 0; +} + +void MessageDelegate::beginClearWidgets() +{ + idsToKeep->clear(); + clearingWidgets = true; +} + + + void MessageDelegate::endClearWidgets() { if (clearingWidgets) { diff --git a/ui/widgets/messageline/messagedelegate.h b/ui/widgets/messageline/messagedelegate.h index 38ec0ee..b49410f 100644 --- a/ui/widgets/messageline/messagedelegate.h +++ b/ui/widgets/messageline/messagedelegate.h @@ -29,12 +29,13 @@ #include #include #include +#include +#include #include "shared/icons.h" #include "shared/global.h" #include "shared/utils.h" #include "shared/pathcheck.h" -#include "ui/utils/textmeter.h" #include "preview.h" @@ -70,13 +71,15 @@ protected: int paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const; int paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const; int paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const; + int paintBody(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const; void paintAvatar(const Models::FeedItem& data, const QModelIndex& index, const QStyleOptionViewItem& option, QPainter* painter) const; void paintBubble(const Models::FeedItem& data, QPainter* painter, const QStyleOptionViewItem& option) const; + QPushButton* getButton(const Models::FeedItem& data) const; QProgressBar* getBar(const Models::FeedItem& data) const; QLabel* getStatusIcon(const Models::FeedItem& data) const; QLabel* getPencilIcon(const Models::FeedItem& data) const; - QLabel* getBody(const Models::FeedItem& data) const; + QTextBrowser* getBody(const Models::FeedItem& data) const; void clearHelperWidget(const Models::FeedItem& data) const; bool needToDrawAvatar(const QModelIndex& index, const Models::FeedItem& data, const QStyleOptionViewItem& option) const; @@ -95,7 +98,7 @@ private: QFont nickFont; QFont dateFont; QFontMetrics bodyMetrics; - TextMeter bodyMeter; + QTextDocument* bodyRenderer; QFontMetrics nickMetrics; QFontMetrics dateMetrics; @@ -107,7 +110,7 @@ private: std::map* bars; std::map* statusIcons; std::map* pencilIcons; - std::map* bodies; + std::map* bodies; std::map* previews; std::set* idsToKeep; bool clearingWidgets; From eac87e713f72f51f1f24ac11d4fc52a57c861f8f Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 28 Apr 2022 00:08:59 +0300 Subject: [PATCH 054/137] seem to have found a text block, to activate with the click later --- ui/widgets/messageline/feedview.cpp | 29 ++++++++- ui/widgets/messageline/feedview.h | 3 + ui/widgets/messageline/messagedelegate.cpp | 74 ++++++++++++++++++++-- ui/widgets/messageline/messagedelegate.h | 1 + 4 files changed, 101 insertions(+), 6 deletions(-) diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index e0c1477..bf1da61 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -50,7 +50,8 @@ FeedView::FeedView(QWidget* parent): modelState(Models::MessageFeed::complete), progress(), dividerFont(), - dividerMetrics(dividerFont) + dividerMetrics(dividerFont), + mousePressed(false) { horizontalScrollBar()->setRange(0, 0); verticalScrollBar()->setSingleStep(approximateSingleMessageHeight); @@ -412,10 +413,36 @@ void FeedView::mouseMoveEvent(QMouseEvent* event) if (!isVisible()) { return; } + + mousePressed = false; + //qDebug() << event; QAbstractItemView::mouseMoveEvent(event); } +void FeedView::mousePressEvent(QMouseEvent* event) +{ + QAbstractItemView::mousePressEvent(event); + mousePressed = event->button() == Qt::LeftButton; +} + +void FeedView::mouseReleaseEvent(QMouseEvent* event) +{ + QAbstractItemView::mouseReleaseEvent(event); + + if (mousePressed && specialDelegate) { + QPoint point = event->localPos().toPoint(); + QModelIndex index = indexAt(point); + if (index.isValid()) { + QRect rect = visualRect(index); + MessageDelegate* del = static_cast(itemDelegate()); + if (rect.contains(point)) { + del->leftClick(point, index, rect); + } + } + } +} + void FeedView::resizeEvent(QResizeEvent* event) { QAbstractItemView::resizeEvent(event); diff --git a/ui/widgets/messageline/feedview.h b/ui/widgets/messageline/feedview.h index 8bcd913..c757986 100644 --- a/ui/widgets/messageline/feedview.h +++ b/ui/widgets/messageline/feedview.h @@ -68,6 +68,8 @@ protected: void paintEvent(QPaintEvent * event) override; void updateGeometries() override; void mouseMoveEvent(QMouseEvent * event) override; + void mousePressEvent(QMouseEvent * event) override; + void mouseReleaseEvent(QMouseEvent * event) override; void resizeEvent(QResizeEvent * event) override; private: @@ -93,6 +95,7 @@ private: Progress progress; QFont dividerFont; QFontMetrics dividerMetrics; + bool mousePressed; static const std::set geometryChangingRoles; diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index 1d094fa..c787cfa 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -22,7 +22,9 @@ #include #include #include -#include +#include +#include +#include #include "messagedelegate.h" #include "messagefeed.h" @@ -303,7 +305,7 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel QSizeF size = bodyRenderer->size(); size.setWidth(bodyRenderer->idealWidth()); - messageSize = QSize(qCeil(size.width()), qCeil(size.height())); + messageSize = QSize(std::ceil(size.width()), std::ceil(size.height())); messageSize.rheight() += textMargin; } @@ -364,6 +366,68 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel return messageSize; } +void MessageDelegate::leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const +{ + QVariant vi = index.data(Models::MessageFeed::Bulk); + Models::FeedItem data = qvariant_cast(vi); + if (data.text.size() > 0) { + QRect localHint = sizeHint.adjusted(bubbleMargin, bubbleMargin + margin, -bubbleMargin, -bubbleMargin / 2); + if (needToDrawSender(index, data)) { + localHint.adjust(0, nickMetrics.lineSpacing() + textMargin, 0, 0); + } + + int attachHeight = 0; + switch (data.attach.state) { + case Models::none: + break; + case Models::uploading: + attachHeight += Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), localHint).height() + textMargin; + [[fallthrough]]; + case Models::downloading: + attachHeight += barHeight + textMargin; + break; + case Models::remote: + attachHeight += buttonHeight + textMargin; + break; + case Models::ready: + case Models::local: { + QSize aSize = Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), localHint); + attachHeight += aSize.height() + textMargin; + } + break; + case Models::errorDownload: { + QSize commentSize = dateMetrics.boundingRect(localHint, Qt::TextWordWrap, data.attach.error).size(); + attachHeight += commentSize.height() + buttonHeight + textMargin * 2; + } + break; + case Models::errorUpload: { + QSize aSize = Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), localHint); + QSize commentSize = dateMetrics.boundingRect(localHint, Qt::TextWordWrap, data.attach.error).size(); + attachHeight += aSize.height() + commentSize.height() + textMargin * 2; + } + break; + } + + int bottomSize = std::max(dateMetrics.lineSpacing(), statusIconSize); + localHint.adjust(0, attachHeight, 0, -(bottomSize + textMargin)); + + if (localHint.contains(point)) { + qDebug() << "MESSAGE CLICKED"; + QPoint translated = point - localHint.topLeft(); + + bodyRenderer->setPlainText(data.text); + bodyRenderer->setTextWidth(localHint.size().width()); + + int pos = bodyRenderer->documentLayout()->hitTest(translated, Qt::FuzzyHit); + QTextBlock block = bodyRenderer->findBlock(pos); + QString text = block.text(); + if (text.size() > 0) { + qDebug() << text; + } + } + } +} + void MessageDelegate::initializeFonts(const QFont& font) { bodyFont = font; @@ -625,9 +689,9 @@ int MessageDelegate::paintBody(const Models::FeedItem& data, QPainter* painter, painter->translate(option.rect.topLeft()); bodyRenderer->drawContents(painter); painter->restore(); - QSize bodySize(qCeil(bodyRenderer->idealWidth()), qCeil(bodyRenderer->size().height())); - + QSize bodySize(std::ceil(bodyRenderer->idealWidth()), std::ceil(bodyRenderer->size().height())); +/* QTextBrowser* editor = nullptr; if (option.state.testFlag(QStyle::State_MouseOver)) { std::set ids({data.id}); @@ -645,7 +709,7 @@ int MessageDelegate::paintBody(const Models::FeedItem& data, QPainter* painter, editor->setMaximumSize(bodySize); editor->move(option.rect.left(), option.rect.y()); editor->show(); - } + }*/ option.rect.adjust(0, bodySize.height() + textMargin, 0, 0); return bodySize.width(); diff --git a/ui/widgets/messageline/messagedelegate.h b/ui/widgets/messageline/messagedelegate.h index b49410f..9333d45 100644 --- a/ui/widgets/messageline/messagedelegate.h +++ b/ui/widgets/messageline/messagedelegate.h @@ -58,6 +58,7 @@ public: bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) override; void endClearWidgets(); void beginClearWidgets(); + void leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const; static int avatarHeight; static int margin; From 7ba94e9deb786393e78880ee279554410662a168 Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 29 Apr 2022 00:29:44 +0300 Subject: [PATCH 055/137] link clicking and hovering in message body now works! --- ui/widgets/messageline/feedview.cpp | 32 +++- ui/widgets/messageline/feedview.h | 4 + ui/widgets/messageline/messagedelegate.cpp | 179 ++++++++------------- ui/widgets/messageline/messagedelegate.h | 10 +- 4 files changed, 105 insertions(+), 120 deletions(-) diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index bf1da61..0758dd9 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -51,7 +51,8 @@ FeedView::FeedView(QWidget* parent): progress(), dividerFont(), dividerMetrics(dividerFont), - mousePressed(false) + mousePressed(false), + anchorHovered(false) { horizontalScrollBar()->setRange(0, 0); verticalScrollBar()->setSingleStep(approximateSingleMessageHeight); @@ -408,6 +409,18 @@ void FeedView::verticalScrollbarValueChanged(int value) QAbstractItemView::verticalScrollbarValueChanged(vo); } +void FeedView::setAnchorHovered(bool hovered) +{ + if (anchorHovered != hovered) { + anchorHovered = hovered; + if (anchorHovered) { + setCursor(Qt::PointingHandCursor); + } else { + setCursor(Qt::ArrowCursor); + } + } +} + void FeedView::mouseMoveEvent(QMouseEvent* event) { if (!isVisible()) { @@ -418,6 +431,22 @@ void FeedView::mouseMoveEvent(QMouseEvent* event) //qDebug() << event; QAbstractItemView::mouseMoveEvent(event); + + if (specialDelegate) { + QPoint point = event->localPos().toPoint(); + QModelIndex index = indexAt(point); + if (index.isValid()) { + QRect rect = visualRect(index); + MessageDelegate* del = static_cast(itemDelegate()); + if (rect.contains(point)) { + setAnchorHovered(del->isAnchorHovered(point, index, rect)); + } else { + setAnchorHovered(false); + } + } else { + setAnchorHovered(false); + } + } } void FeedView::mousePressEvent(QMouseEvent* event) @@ -487,6 +516,7 @@ void FeedView::setItemDelegate(QAbstractItemDelegate* delegate) elementMargin = MessageDelegate::margin; connect(del, &MessageDelegate::buttonPushed, this, &FeedView::onMessageButtonPushed); connect(del, &MessageDelegate::invalidPath, this, &FeedView::onMessageInvalidPath); + connect(del, &MessageDelegate::openLink, &QDesktopServices::openUrl); } else { specialDelegate = false; elementMargin = 0; diff --git a/ui/widgets/messageline/feedview.h b/ui/widgets/messageline/feedview.h index c757986..7a00dd7 100644 --- a/ui/widgets/messageline/feedview.h +++ b/ui/widgets/messageline/feedview.h @@ -20,6 +20,8 @@ #define FEEDVIEW_H #include +#include +#include #include #include @@ -76,6 +78,7 @@ private: bool tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* model, uint32_t totalHeight); void positionProgress(); void drawDateDevider(int top, const QDateTime& date, QPainter& painter); + void setAnchorHovered(bool hovered); private: struct Hint { @@ -96,6 +99,7 @@ private: QFont dividerFont; QFontMetrics dividerMetrics; bool mousePressed; + bool anchorHovered; static const std::set geometryChangingRoles; diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index c787cfa..22a575b 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -44,7 +44,6 @@ MessageDelegate::MessageDelegate(QObject* parent): bodyFont(), nickFont(), dateFont(), - bodyMetrics(bodyFont), bodyRenderer(new QTextDocument()), nickMetrics(nickFont), dateMetrics(dateFont), @@ -55,7 +54,6 @@ MessageDelegate::MessageDelegate(QObject* parent): bars(new std::map()), statusIcons(new std::map()), pencilIcons(new std::map()), - bodies(new std::map()), previews(new std::map()), idsToKeep(new std::set()), clearingWidgets(false) @@ -88,10 +86,6 @@ MessageDelegate::~MessageDelegate() delete pair.second; } - for (const std::pair& pair: *bodies){ - delete pair.second; - } - for (const std::pair& pair: *previews){ delete pair.second; } @@ -101,7 +95,6 @@ MessageDelegate::~MessageDelegate() delete idsToKeep; delete buttons; delete bars; - delete bodies; delete previews; delete bodyRenderer; } @@ -366,66 +359,83 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel return messageSize; } -void MessageDelegate::leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const +QRect MessageDelegate::getHoveredMessageBodyRect(const QModelIndex& index, const Models::FeedItem& data, const QRect& sizeHint) const +{ + QRect localHint = sizeHint.adjusted(bubbleMargin, bubbleMargin + margin, -bubbleMargin, -bubbleMargin / 2); + if (needToDrawSender(index, data)) { + localHint.adjust(0, nickMetrics.lineSpacing() + textMargin, 0, 0); + } + + int attachHeight = 0; + switch (data.attach.state) { + case Models::none: + break; + case Models::uploading: + attachHeight += Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), localHint).height() + textMargin; + [[fallthrough]]; + case Models::downloading: + attachHeight += barHeight + textMargin; + break; + case Models::remote: + attachHeight += buttonHeight + textMargin; + break; + case Models::ready: + case Models::local: { + QSize aSize = Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), localHint); + attachHeight += aSize.height() + textMargin; + } + break; + case Models::errorDownload: { + QSize commentSize = dateMetrics.boundingRect(localHint, Qt::TextWordWrap, data.attach.error).size(); + attachHeight += commentSize.height() + buttonHeight + textMargin * 2; + } + break; + case Models::errorUpload: { + QSize aSize = Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), localHint); + QSize commentSize = dateMetrics.boundingRect(localHint, Qt::TextWordWrap, data.attach.error).size(); + attachHeight += aSize.height() + commentSize.height() + textMargin * 2; + } + break; + } + + int bottomSize = std::max(dateMetrics.lineSpacing(), statusIconSize); + localHint.adjust(0, attachHeight, 0, -(bottomSize + textMargin)); + + return localHint; +} + +QString MessageDelegate::getAnchor(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const { QVariant vi = index.data(Models::MessageFeed::Bulk); Models::FeedItem data = qvariant_cast(vi); if (data.text.size() > 0) { - QRect localHint = sizeHint.adjusted(bubbleMargin, bubbleMargin + margin, -bubbleMargin, -bubbleMargin / 2); - if (needToDrawSender(index, data)) { - localHint.adjust(0, nickMetrics.lineSpacing() + textMargin, 0, 0); - } - - int attachHeight = 0; - switch (data.attach.state) { - case Models::none: - break; - case Models::uploading: - attachHeight += Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), localHint).height() + textMargin; - [[fallthrough]]; - case Models::downloading: - attachHeight += barHeight + textMargin; - break; - case Models::remote: - attachHeight += buttonHeight + textMargin; - break; - case Models::ready: - case Models::local: { - QSize aSize = Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), localHint); - attachHeight += aSize.height() + textMargin; - } - break; - case Models::errorDownload: { - QSize commentSize = dateMetrics.boundingRect(localHint, Qt::TextWordWrap, data.attach.error).size(); - attachHeight += commentSize.height() + buttonHeight + textMargin * 2; - } - break; - case Models::errorUpload: { - QSize aSize = Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), localHint); - QSize commentSize = dateMetrics.boundingRect(localHint, Qt::TextWordWrap, data.attach.error).size(); - attachHeight += aSize.height() + commentSize.height() + textMargin * 2; - } - break; - } - - int bottomSize = std::max(dateMetrics.lineSpacing(), statusIconSize); - localHint.adjust(0, attachHeight, 0, -(bottomSize + textMargin)); + QRect localHint = getHoveredMessageBodyRect(index, data, sizeHint); if (localHint.contains(point)) { - qDebug() << "MESSAGE CLICKED"; QPoint translated = point - localHint.topLeft(); - bodyRenderer->setPlainText(data.text); + bodyRenderer->setHtml(Shared::processMessageBody(data.text)); bodyRenderer->setTextWidth(localHint.size().width()); - int pos = bodyRenderer->documentLayout()->hitTest(translated, Qt::FuzzyHit); - QTextBlock block = bodyRenderer->findBlock(pos); - QString text = block.text(); - if (text.size() > 0) { - qDebug() << text; - } + return bodyRenderer->documentLayout()->anchorAt(translated); } } + + return QString(); +} + +void MessageDelegate::leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const +{ + QString anchor = getAnchor(point, index, sizeHint); + if (anchor.size() > 0) { + emit openLink(anchor); + } +} + +bool MessageDelegate::isAnchorHovered(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const +{ + QString anchor = getAnchor(point, index, sizeHint); + return anchor.size() > 0; } void MessageDelegate::initializeFonts(const QFont& font) @@ -452,7 +462,6 @@ void MessageDelegate::initializeFonts(const QFont& font) } bodyFont.setKerning(false); - bodyMetrics = QFontMetrics(bodyFont); nickMetrics = QFontMetrics(nickFont); dateMetrics = QFontMetrics(dateFont); @@ -636,36 +645,6 @@ QLabel * MessageDelegate::getPencilIcon(const Models::FeedItem& data) const return result; } -QTextBrowser * MessageDelegate::getBody(const Models::FeedItem& data) const -{ - std::map::const_iterator itr = bodies->find(data.id); - QTextBrowser* result = 0; - - if (itr != bodies->end()) { - result = itr->second; - } else { - result = new QTextBrowser(); - result->setFont(bodyFont); - result->setContextMenuPolicy(Qt::NoContextMenu); - result->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - result->setContentsMargins(0, 0, 0, 0); - //result->viewport()->setAutoFillBackground(false); - result->document()->setDocumentMargin(0); - result->setFrameStyle(0); - result->setLineWidth(0); - //result->setAutoFillBackground(false); - //->setWordWrap(true); - result->setOpenExternalLinks(true); - //result->setTextInteractionFlags(result->textInteractionFlags() | Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse); - result->setOpenExternalLinks(true); - bodies->insert(std::make_pair(data.id, result)); - } - - result->setHtml(Shared::processMessageBody(data.text)); - - return result; -} - template void removeElements(std::map* elements, std::set* idsToKeep) { std::set toRemove; @@ -691,26 +670,6 @@ int MessageDelegate::paintBody(const Models::FeedItem& data, QPainter* painter, painter->restore(); QSize bodySize(std::ceil(bodyRenderer->idealWidth()), std::ceil(bodyRenderer->size().height())); -/* - QTextBrowser* editor = nullptr; - if (option.state.testFlag(QStyle::State_MouseOver)) { - std::set ids({data.id}); - removeElements(bodies, &ids); - editor = getBody(data); - editor->setParent(static_cast(painter->device())); - } else { - std::map::const_iterator itr = bodies->find(data.id); - if (itr != bodies->end()) { - editor = itr->second; - } - } - if (editor != nullptr) { - editor->setMinimumSize(bodySize); - editor->setMaximumSize(bodySize); - editor->move(option.rect.left(), option.rect.y()); - editor->show(); - }*/ - option.rect.adjust(0, bodySize.height() + textMargin, 0, 0); return bodySize.width(); } @@ -723,8 +682,6 @@ void MessageDelegate::beginClearWidgets() clearingWidgets = true; } - - void MessageDelegate::endClearWidgets() { if (clearingWidgets) { @@ -732,7 +689,6 @@ void MessageDelegate::endClearWidgets() removeElements(bars, idsToKeep); removeElements(statusIcons, idsToKeep); removeElements(pencilIcons, idsToKeep); - removeElements(bodies, idsToKeep); removeElements(previews, idsToKeep); idsToKeep->clear(); @@ -760,8 +716,3 @@ void MessageDelegate::clearHelperWidget(const Models::FeedItem& data) const } } } - -// void MessageDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const -// { -// -// } diff --git a/ui/widgets/messageline/messagedelegate.h b/ui/widgets/messageline/messagedelegate.h index 9333d45..d871a52 100644 --- a/ui/widgets/messageline/messagedelegate.h +++ b/ui/widgets/messageline/messagedelegate.h @@ -29,8 +29,6 @@ #include #include #include -#include -#include #include "shared/icons.h" #include "shared/global.h" @@ -59,6 +57,7 @@ public: void endClearWidgets(); void beginClearWidgets(); void leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const; + bool isAnchorHovered(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const; static int avatarHeight; static int margin; @@ -66,6 +65,7 @@ public: signals: void buttonPushed(const QString& messageId) const; void invalidPath(const QString& messageId) const; + void openLink(const QString& href) const; protected: int paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const; @@ -80,11 +80,13 @@ protected: QProgressBar* getBar(const Models::FeedItem& data) const; QLabel* getStatusIcon(const Models::FeedItem& data) const; QLabel* getPencilIcon(const Models::FeedItem& data) const; - QTextBrowser* getBody(const Models::FeedItem& data) const; void clearHelperWidget(const Models::FeedItem& data) const; bool needToDrawAvatar(const QModelIndex& index, const Models::FeedItem& data, const QStyleOptionViewItem& option) const; bool needToDrawSender(const QModelIndex& index, const Models::FeedItem& data) const; + + QRect getHoveredMessageBodyRect(const QModelIndex& index, const Models::FeedItem& data, const QRect& sizeHint) const; + QString getAnchor(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const; protected slots: void onButtonPushed() const; @@ -98,7 +100,6 @@ private: QFont bodyFont; QFont nickFont; QFont dateFont; - QFontMetrics bodyMetrics; QTextDocument* bodyRenderer; QFontMetrics nickMetrics; QFontMetrics dateMetrics; @@ -111,7 +112,6 @@ private: std::map* bars; std::map* statusIcons; std::map* pencilIcons; - std::map* bodies; std::map* previews; std::set* idsToKeep; bool clearingWidgets; From 0340db7f2fcff211f99d6ccc92bd98669a406581 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 1 May 2022 23:19:52 +0300 Subject: [PATCH 056/137] first successfull attempt to visualize selection on message body --- ui/widgets/messageline/feedview.cpp | 56 +++++++++++------ ui/widgets/messageline/feedview.h | 3 + ui/widgets/messageline/messagedelegate.cpp | 70 ++++++++++++++-------- ui/widgets/messageline/messagedelegate.h | 5 +- 4 files changed, 92 insertions(+), 42 deletions(-) diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index 0758dd9..f467f43 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -52,7 +52,10 @@ FeedView::FeedView(QWidget* parent): dividerFont(), dividerMetrics(dividerFont), mousePressed(false), - anchorHovered(false) + dragging(false), + anchorHovered(false), + dragStartPoint(), + dragEndPoint() { horizontalScrollBar()->setRange(0, 0); verticalScrollBar()->setSingleStep(approximateSingleMessageHeight); @@ -304,7 +307,7 @@ bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewIt void FeedView::paintEvent(QPaintEvent* event) { - //qDebug() << "paint" << event->rect(); + qDebug() << "paint" << event->rect(); const QAbstractItemModel* m = model(); QWidget* vp = viewport(); QRect zone = event->rect().translated(0, -vo); @@ -427,19 +430,31 @@ void FeedView::mouseMoveEvent(QMouseEvent* event) return; } - mousePressed = false; - //qDebug() << event; + dragEndPoint = event->localPos().toPoint(); + if (mousePressed) { + QPoint distance = dragStartPoint - dragEndPoint; + if (distance.manhattanLength() > 5) { + dragging = true; + } + } QAbstractItemView::mouseMoveEvent(event); if (specialDelegate) { - QPoint point = event->localPos().toPoint(); - QModelIndex index = indexAt(point); + QModelIndex index = indexAt(dragEndPoint); if (index.isValid()) { QRect rect = visualRect(index); - MessageDelegate* del = static_cast(itemDelegate()); - if (rect.contains(point)) { - setAnchorHovered(del->isAnchorHovered(point, index, rect)); + if (rect.contains(dragEndPoint)) { + MessageDelegate* del = static_cast(itemDelegate()); + if (dragging) { + setAnchorHovered(false); + if (del->mouseDrag(dragStartPoint, dragEndPoint, index, rect)) { + qDebug() << "asking to repaint" << rect; + setDirtyRegion(rect); + } + } else { + setAnchorHovered(del->isAnchorHovered(dragEndPoint, index, rect)); + } } else { setAnchorHovered(false); } @@ -453,22 +468,29 @@ void FeedView::mousePressEvent(QMouseEvent* event) { QAbstractItemView::mousePressEvent(event); mousePressed = event->button() == Qt::LeftButton; + if (mousePressed) { + dragStartPoint = event->localPos().toPoint(); + } } void FeedView::mouseReleaseEvent(QMouseEvent* event) { QAbstractItemView::mouseReleaseEvent(event); - if (mousePressed && specialDelegate) { - QPoint point = event->localPos().toPoint(); - QModelIndex index = indexAt(point); - if (index.isValid()) { - QRect rect = visualRect(index); - MessageDelegate* del = static_cast(itemDelegate()); - if (rect.contains(point)) { - del->leftClick(point, index, rect); + if (mousePressed) { + if (!dragging && specialDelegate) { + QPoint point = event->localPos().toPoint(); + QModelIndex index = indexAt(point); + if (index.isValid()) { + QRect rect = visualRect(index); + MessageDelegate* del = static_cast(itemDelegate()); + if (rect.contains(point)) { + del->leftClick(point, index, rect); + } } } + dragging = false; + mousePressed = false; } } diff --git a/ui/widgets/messageline/feedview.h b/ui/widgets/messageline/feedview.h index 7a00dd7..c0d6254 100644 --- a/ui/widgets/messageline/feedview.h +++ b/ui/widgets/messageline/feedview.h @@ -99,7 +99,10 @@ private: QFont dividerFont; QFontMetrics dividerMetrics; bool mousePressed; + bool dragging; bool anchorHovered; + QPoint dragStartPoint; + QPoint dragEndPoint; static const std::set geometryChangingRoles; diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index ca2e0a6..197248a 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -56,7 +56,9 @@ MessageDelegate::MessageDelegate(QObject* parent): pencilIcons(new std::map()), previews(new std::map()), idsToKeep(new std::set()), - clearingWidgets(false) + clearingWidgets(false), + currentId(""), + selection(0, 0) { bodyRenderer->setDocumentMargin(0); @@ -438,6 +440,37 @@ bool MessageDelegate::isAnchorHovered(const QPoint& point, const QModelIndex& in return anchor.size() > 0; } +bool MessageDelegate::mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint) +{ + QVariant vi = index.data(Models::MessageFeed::Bulk); + Models::FeedItem data = qvariant_cast(vi); + if (data.text.size() > 0) { + QRect localHint = getHoveredMessageBodyRect(index, data, sizeHint); + + if (localHint.contains(start)) { + QPoint translated = start - localHint.topLeft(); + + bodyRenderer->setHtml(Shared::processMessageBody(data.text)); + bodyRenderer->setTextWidth(localHint.size().width()); + selection.first = bodyRenderer->documentLayout()->hitTest(translated, Qt::HitTestAccuracy::FuzzyHit); + selection.second = bodyRenderer->documentLayout()->hitTest(end - localHint.topLeft(), Qt::HitTestAccuracy::FuzzyHit); + + currentId = data.id; + + return true; + } + } + return false; +} + +QString MessageDelegate::clearSelection() +{ + QString lastSelectedId = currentId; + currentId = ""; + selection = std::pair(0, 0); + return lastSelectedId; +} + void MessageDelegate::initializeFonts(const QFont& font) { bodyFont = font; @@ -664,32 +697,21 @@ int MessageDelegate::paintBody(const Models::FeedItem& data, QPainter* painter, if (data.text.size() > 0) { bodyRenderer->setHtml(Shared::processMessageBody(data.text)); bodyRenderer->setTextWidth(option.rect.size().width()); - painter->setBackgroundMode(Qt::BGMode::OpaqueMode); painter->save(); -// QTextCursor cursor(bodyRenderer); -// cursor.setPosition(2, QTextCursor::KeepAnchor); painter->translate(option.rect.topLeft()); -// QTextFrameFormat format = bodyRenderer->rootFrame()->frameFormat(); -// format.setBackground(option.palette.brush(QPalette::Active, QPalette::Highlight)); -// bodyRenderer->rootFrame()->setFrameFormat(format); + + if (data.id == currentId) { + QTextCursor cursor(bodyRenderer); + cursor.setPosition(selection.first, QTextCursor::MoveAnchor); + cursor.setPosition(selection.second, QTextCursor::KeepAnchor); + QTextCharFormat format = cursor.charFormat(); + format.setBackground(option.palette.color(QPalette::Active, QPalette::Highlight)); + format.setForeground(option.palette.color(QPalette::Active, QPalette::HighlightedText)); + cursor.setCharFormat(format); + } + bodyRenderer->drawContents(painter); -// QColor c = option.palette.color(QPalette::Active, QPalette::Highlight); -// QTextBlock b = bodyRenderer->begin(); -// QTextBlockFormat format = b.blockFormat(); -// format.setBackground(option.palette.brush(QPalette::Active, QPalette::Highlight)); -// format.setProperty(QTextFormat::BackgroundBrush, option.palette.brush(QPalette::Active, QPalette::Highlight)); -// QTextCursor cursor(bodyRenderer); -// cursor.setBlockFormat(format); -// b = bodyRenderer->begin(); -// while (b.isValid() > 0) { -// QTextLayout* lay = b.layout(); -// QTextLayout::FormatRange range; -// range.format = b.charFormat(); -// range.start = 0; -// range.length = 2; -// lay->draw(painter, option.rect.topLeft(), {range}); -// b = b.next(); -// } + painter->restore(); QSize bodySize(std::ceil(bodyRenderer->idealWidth()), std::ceil(bodyRenderer->size().height())); diff --git a/ui/widgets/messageline/messagedelegate.h b/ui/widgets/messageline/messagedelegate.h index df883f7..2aea240 100644 --- a/ui/widgets/messageline/messagedelegate.h +++ b/ui/widgets/messageline/messagedelegate.h @@ -59,6 +59,8 @@ public: void beginClearWidgets(); void leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const; bool isAnchorHovered(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const; + bool mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint); + QString clearSelection(); static int avatarHeight; static int margin; @@ -116,7 +118,8 @@ private: std::map* previews; std::set* idsToKeep; bool clearingWidgets; - + QString currentId; + std::pair selection; }; #endif // MESSAGEDELEGATE_H From 3c48577eeed23e5f21af70c0ae90695e91893ac7 Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 2 May 2022 22:25:50 +0300 Subject: [PATCH 057/137] selection message body now actually working --- shared/utils.h | 6 ++ ui/widgets/conversation.cpp | 10 +++ ui/widgets/messageline/feedview.cpp | 92 ++++++++++++++++------ ui/widgets/messageline/feedview.h | 8 +- ui/widgets/messageline/messagedelegate.cpp | 54 ++++++++++--- ui/widgets/messageline/messagedelegate.h | 4 +- ui/widgets/messageline/messagefeed.h | 4 +- 7 files changed, 138 insertions(+), 40 deletions(-) diff --git a/shared/utils.h b/shared/utils.h index 564e2e6..0329cee 100644 --- a/shared/utils.h +++ b/shared/utils.h @@ -69,6 +69,12 @@ static const std::vector colorPalette = { QColor(17, 17, 80), QColor(54, 54, 94) }; + +enum class Hover { + nothing, + text, + anchor +}; } #endif // SHARED_UTILS_H diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 70a468c..b2c7a5f 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -499,6 +499,16 @@ void Conversation::onFeedContext(const QPoint& pos) }); } + QString selected = feed->getSelectedText(); + if (selected.size() > 0) { + showMenu = true; + QAction* copy = contextMenu->addAction(Shared::icon("edit-copy"), tr("Copy selected")); + connect(copy, &QAction::triggered, [selected] () { + QClipboard* cb = QApplication::clipboard(); + cb->setText(selected); + }); + } + QString body = item->getBody(); if (body.size() > 0) { showMenu = true; diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index f467f43..353d851 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include "messagedelegate.h" @@ -53,9 +55,10 @@ FeedView::FeedView(QWidget* parent): dividerMetrics(dividerFont), mousePressed(false), dragging(false), - anchorHovered(false), + hovered(Shared::Hover::nothing), dragStartPoint(), - dragEndPoint() + dragEndPoint(), + selectedText() { horizontalScrollBar()->setRange(0, 0); verticalScrollBar()->setSingleStep(approximateSingleMessageHeight); @@ -167,7 +170,7 @@ void FeedView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottom void FeedView::updateGeometries() { - qDebug() << "updateGeometries"; + //qDebug() << "updateGeometries"; QScrollBar* bar = verticalScrollBar(); const QStyle* st = style(); @@ -307,7 +310,7 @@ bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewIt void FeedView::paintEvent(QPaintEvent* event) { - qDebug() << "paint" << event->rect(); + //qDebug() << "paint" << event->rect(); const QAbstractItemModel* m = model(); QWidget* vp = viewport(); QRect zone = event->rect().translated(0, -vo); @@ -412,14 +415,20 @@ void FeedView::verticalScrollbarValueChanged(int value) QAbstractItemView::verticalScrollbarValueChanged(vo); } -void FeedView::setAnchorHovered(bool hovered) +void FeedView::setAnchorHovered(Shared::Hover type) { - if (anchorHovered != hovered) { - anchorHovered = hovered; - if (anchorHovered) { - setCursor(Qt::PointingHandCursor); - } else { - setCursor(Qt::ArrowCursor); + if (hovered != type) { + hovered = type; + switch (hovered) { + case Shared::Hover::nothing: + setCursor(Qt::ArrowCursor); + break; + case Shared::Hover::text: + setCursor(Qt::IBeamCursor); + break; + case Shared::Hover::anchor: + setCursor(Qt::PointingHandCursor); + break; } } } @@ -441,25 +450,31 @@ void FeedView::mouseMoveEvent(QMouseEvent* event) QAbstractItemView::mouseMoveEvent(event); if (specialDelegate) { - QModelIndex index = indexAt(dragEndPoint); - if (index.isValid()) { - QRect rect = visualRect(index); - if (rect.contains(dragEndPoint)) { - MessageDelegate* del = static_cast(itemDelegate()); - if (dragging) { - setAnchorHovered(false); - if (del->mouseDrag(dragStartPoint, dragEndPoint, index, rect)) { - qDebug() << "asking to repaint" << rect; + MessageDelegate* del = static_cast(itemDelegate()); + if (dragging) { + QModelIndex index = indexAt(dragStartPoint); + if (index.isValid()) { + QRect rect = visualRect(index); + if (rect.contains(dragStartPoint)) { + QString selected = del->mouseDrag(dragStartPoint, dragEndPoint, index, rect); + if (selectedText != selected) { + selectedText = selected; setDirtyRegion(rect); } - } else { - setAnchorHovered(del->isAnchorHovered(dragEndPoint, index, rect)); } - } else { - setAnchorHovered(false); } } else { - setAnchorHovered(false); + QModelIndex index = indexAt(dragEndPoint); + if (index.isValid()) { + QRect rect = visualRect(index); + if (rect.contains(dragEndPoint)) { + setAnchorHovered(del->hoverType(dragEndPoint, index, rect)); + } else { + setAnchorHovered(Shared::Hover::nothing); + } + } else { + setAnchorHovered(Shared::Hover::nothing); + } } } } @@ -470,6 +485,17 @@ void FeedView::mousePressEvent(QMouseEvent* event) mousePressed = event->button() == Qt::LeftButton; if (mousePressed) { dragStartPoint = event->localPos().toPoint(); + if (specialDelegate && specialModel) { + MessageDelegate* del = static_cast(itemDelegate()); + QString lastSelectedId = del->clearSelection(); + if (lastSelectedId.size()) { + Models::MessageFeed* feed = static_cast(model()); + QModelIndex index = feed->modelIndexById(lastSelectedId); + if (index.isValid()) { + setDirtyRegion(visualRect(index)); + } + } + } } } @@ -494,6 +520,17 @@ void FeedView::mouseReleaseEvent(QMouseEvent* event) } } +void FeedView::keyPressEvent(QKeyEvent* event) +{ + QKeyEvent *key_event = static_cast(event); + if (key_event->matches(QKeySequence::Copy)) { + if (selectedText.size() > 0) { + QClipboard* cb = QApplication::clipboard(); + cb->setText(selectedText); + } + } +} + void FeedView::resizeEvent(QResizeEvent* event) { QAbstractItemView::resizeEvent(event); @@ -603,3 +640,8 @@ void FeedView::onModelSyncStateChange(Models::MessageFeed::SyncState state) scheduleDelayedItemsLayout(); } } + +QString FeedView::getSelectedText() const +{ + return selectedText; +} diff --git a/ui/widgets/messageline/feedview.h b/ui/widgets/messageline/feedview.h index c0d6254..d0763a5 100644 --- a/ui/widgets/messageline/feedview.h +++ b/ui/widgets/messageline/feedview.h @@ -28,6 +28,7 @@ #include #include +#include /** * @todo write docs @@ -50,6 +51,7 @@ public: void setModel(QAbstractItemModel * model) override; QFont getFont() const; + QString getSelectedText() const; signals: void resized(); @@ -72,13 +74,14 @@ protected: void mouseMoveEvent(QMouseEvent * event) override; void mousePressEvent(QMouseEvent * event) override; void mouseReleaseEvent(QMouseEvent * event) override; + void keyPressEvent(QKeyEvent * event) override; void resizeEvent(QResizeEvent * event) override; private: bool tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* model, uint32_t totalHeight); void positionProgress(); void drawDateDevider(int top, const QDateTime& date, QPainter& painter); - void setAnchorHovered(bool hovered); + void setAnchorHovered(Shared::Hover type); private: struct Hint { @@ -100,9 +103,10 @@ private: QFontMetrics dividerMetrics; bool mousePressed; bool dragging; - bool anchorHovered; + Shared::Hover hovered; QPoint dragStartPoint; QPoint dragEndPoint; + QString selectedText; static const std::set geometryChangingRoles; diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index 197248a..840ef5c 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -434,13 +434,37 @@ void MessageDelegate::leftClick(const QPoint& point, const QModelIndex& index, c } } -bool MessageDelegate::isAnchorHovered(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const +Shared::Hover MessageDelegate::hoverType(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const { - QString anchor = getAnchor(point, index, sizeHint); - return anchor.size() > 0; + QVariant vi = index.data(Models::MessageFeed::Bulk); + Models::FeedItem data = qvariant_cast(vi); + if (data.text.size() > 0) { + QRect localHint = getHoveredMessageBodyRect(index, data, sizeHint); + + if (localHint.contains(point)) { + QPoint translated = point - localHint.topLeft(); + + bodyRenderer->setHtml(Shared::processMessageBody(data.text)); + bodyRenderer->setTextWidth(localHint.size().width()); + + QAbstractTextDocumentLayout* lay = bodyRenderer->documentLayout(); + QString anchor = lay->anchorAt(translated); + + if (anchor.size() > 0) { + return Shared::Hover::anchor; + } else { + int position = lay->hitTest(translated, Qt::HitTestAccuracy::ExactHit); + if (position != -1) { //this is a bad way, it's false positive on the end of the last + return Shared::Hover::text; //line of a multiline block, so it's not better the checking the rect + } + //return Shared::Hover::text; + } + } + } + return Shared::Hover::nothing; } -bool MessageDelegate::mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint) +QString MessageDelegate::mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint) { QVariant vi = index.data(Models::MessageFeed::Bulk); Models::FeedItem data = qvariant_cast(vi); @@ -448,19 +472,31 @@ bool MessageDelegate::mouseDrag(const QPoint& start, const QPoint& end, const QM QRect localHint = getHoveredMessageBodyRect(index, data, sizeHint); if (localHint.contains(start)) { - QPoint translated = start - localHint.topLeft(); + QPoint tl = localHint.topLeft(); + QPoint first = start - tl; + QPoint last = end - tl; + last.setX(std::max(last.x(), 0)); + last.setX(std::min(last.x(), localHint.width() - 1)); + last.setY(std::max(last.y(), 0)); + last.setY(std::min(last.y(), localHint.height())); + bodyRenderer->setHtml(Shared::processMessageBody(data.text)); bodyRenderer->setTextWidth(localHint.size().width()); - selection.first = bodyRenderer->documentLayout()->hitTest(translated, Qt::HitTestAccuracy::FuzzyHit); - selection.second = bodyRenderer->documentLayout()->hitTest(end - localHint.topLeft(), Qt::HitTestAccuracy::FuzzyHit); + selection.first = bodyRenderer->documentLayout()->hitTest(first, Qt::HitTestAccuracy::FuzzyHit); + selection.second = bodyRenderer->documentLayout()->hitTest(last, Qt::HitTestAccuracy::FuzzyHit); currentId = data.id; - return true; + if (selection.first != selection.second) { + QTextCursor cursor(bodyRenderer); + cursor.setPosition(selection.first, QTextCursor::MoveAnchor); + cursor.setPosition(selection.second, QTextCursor::KeepAnchor); + return cursor.selectedText(); + } } } - return false; + return ""; } QString MessageDelegate::clearSelection() diff --git a/ui/widgets/messageline/messagedelegate.h b/ui/widgets/messageline/messagedelegate.h index 2aea240..dc0fb49 100644 --- a/ui/widgets/messageline/messagedelegate.h +++ b/ui/widgets/messageline/messagedelegate.h @@ -58,8 +58,8 @@ public: void endClearWidgets(); void beginClearWidgets(); void leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const; - bool isAnchorHovered(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const; - bool mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint); + Shared::Hover hoverType(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const; + QString mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint); QString clearSelection(); static int avatarHeight; diff --git a/ui/widgets/messageline/messagefeed.h b/ui/widgets/messageline/messagefeed.h index f362989..db174d2 100644 --- a/ui/widgets/messageline/messagefeed.h +++ b/ui/widgets/messageline/messagefeed.h @@ -57,6 +57,8 @@ public: void changeMessage(const QString& id, const QMap& data); void removeMessage(const QString& id); Shared::Message getMessage(const QString& id); + QModelIndex modelIndexById(const QString& id) const; + QModelIndex modelIndexByTime(const QString& id, const QDateTime& time) const; QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; @@ -126,8 +128,6 @@ protected: bool sentByMe(const Shared::Message& msg) const; Attachment fillAttach(const Shared::Message& msg) const; Edition fillCorrection(const Shared::Message& msg) const; - QModelIndex modelIndexById(const QString& id) const; - QModelIndex modelIndexByTime(const QString& id, const QDateTime& time) const; std::set detectChanges(const Shared::Message& msg, const QMap& data) const; private: From 1f065f23e65d3d85c994a9d3d342cac14a9bccc1 Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 3 May 2022 12:17:08 +0300 Subject: [PATCH 058/137] double click word selection handle, sigint sermentation fault fix --- CHANGELOG.md | 2 ++ main/application.cpp | 4 ++- ui/widgets/messageline/feedview.cpp | 31 +++++++++++++++++ ui/widgets/messageline/feedview.h | 1 + ui/widgets/messageline/messagedelegate.cpp | 39 +++++++++++++++++++--- ui/widgets/messageline/messagedelegate.h | 1 + 6 files changed, 73 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 241d61d..01fa4a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ - now when you remove an account it actually gets removed - segfault on unitialized Availability in some rare occesions - fixed crash when you open a dialog with someone that has only error messages in archive +- message height is now calculated correctly on Chinese and Japanese paragraphs +- the app doesn't crash on SIGINT anymore ### Improvements - there is a way to disable an account and it wouldn't connect when you change availability diff --git a/main/application.cpp b/main/application.cpp index 410cd81..216e322 100644 --- a/main/application.cpp +++ b/main/application.cpp @@ -186,7 +186,9 @@ void Application::onSquawkClosing() { dialogueQueue.setParentWidnow(nullptr); - disconnect(core, &Core::Squawk::responseVCard, squawk, &Squawk::responseVCard); + if (!nowQuitting) { + disconnect(core, &Core::Squawk::responseVCard, squawk, &Squawk::responseVCard); + } destroyingSquawk = true; squawk->deleteLater(); diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index 353d851..69b5093 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -482,6 +482,7 @@ void FeedView::mouseMoveEvent(QMouseEvent* event) void FeedView::mousePressEvent(QMouseEvent* event) { QAbstractItemView::mousePressEvent(event); + mousePressed = event->button() == Qt::LeftButton; if (mousePressed) { dragStartPoint = event->localPos().toPoint(); @@ -499,6 +500,36 @@ void FeedView::mousePressEvent(QMouseEvent* event) } } +void FeedView::mouseDoubleClickEvent(QMouseEvent* event) +{ + QAbstractItemView::mouseDoubleClickEvent(event); + mousePressed = event->button() == Qt::LeftButton; + if (mousePressed) { + dragStartPoint = event->localPos().toPoint(); + if (specialDelegate && specialModel) { + MessageDelegate* del = static_cast(itemDelegate()); + QString lastSelectedId = del->clearSelection(); + selectedText = ""; + if (lastSelectedId.size()) { + Models::MessageFeed* feed = static_cast(model()); + QModelIndex index = feed->modelIndexById(lastSelectedId); + if (index.isValid()) { + setDirtyRegion(visualRect(index)); + } + } + + QModelIndex index = indexAt(dragStartPoint); + QRect rect = visualRect(index); + if (rect.contains(dragStartPoint)) { + selectedText = del->leftDoubleClick(dragStartPoint, index, rect); + if (selectedText.size() > 0) { + setDirtyRegion(rect); + } + } + } + } +} + void FeedView::mouseReleaseEvent(QMouseEvent* event) { QAbstractItemView::mouseReleaseEvent(event); diff --git a/ui/widgets/messageline/feedview.h b/ui/widgets/messageline/feedview.h index d0763a5..4194849 100644 --- a/ui/widgets/messageline/feedview.h +++ b/ui/widgets/messageline/feedview.h @@ -74,6 +74,7 @@ protected: void mouseMoveEvent(QMouseEvent * event) override; void mousePressEvent(QMouseEvent * event) override; void mouseReleaseEvent(QMouseEvent * event) override; + void mouseDoubleClickEvent(QMouseEvent * event) override; void keyPressEvent(QKeyEvent * event) override; void resizeEvent(QResizeEvent * event) override; diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index 840ef5c..f935057 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -434,6 +434,39 @@ void MessageDelegate::leftClick(const QPoint& point, const QModelIndex& index, c } } +QString MessageDelegate::leftDoubleClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) +{ + QVariant vi = index.data(Models::MessageFeed::Bulk); + Models::FeedItem data = qvariant_cast(vi); + if (data.text.size() > 0) { + QRect localHint = getHoveredMessageBodyRect(index, data, sizeHint); + + if (localHint.contains(point)) { + QPoint translated = point - localHint.topLeft(); + + bodyRenderer->setHtml(Shared::processMessageBody(data.text)); + bodyRenderer->setTextWidth(localHint.size().width()); + + QAbstractTextDocumentLayout* lay = bodyRenderer->documentLayout(); + + int position = lay->hitTest(translated, Qt::HitTestAccuracy::FuzzyHit); + QTextCursor cursor(bodyRenderer); + cursor.setPosition(position, QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); + + selection.first = cursor.anchor(); + selection.second = cursor.position(); + currentId = data.id; + + if (selection.first != selection.second) { + return cursor.selectedText(); + } + } + } + return ""; +} + Shared::Hover MessageDelegate::hoverType(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const { QVariant vi = index.data(Models::MessageFeed::Bulk); @@ -454,10 +487,9 @@ Shared::Hover MessageDelegate::hoverType(const QPoint& point, const QModelIndex& return Shared::Hover::anchor; } else { int position = lay->hitTest(translated, Qt::HitTestAccuracy::ExactHit); - if (position != -1) { //this is a bad way, it's false positive on the end of the last - return Shared::Hover::text; //line of a multiline block, so it's not better the checking the rect + if (position != -1) { + return Shared::Hover::text; } - //return Shared::Hover::text; } } } @@ -480,7 +512,6 @@ QString MessageDelegate::mouseDrag(const QPoint& start, const QPoint& end, const last.setY(std::max(last.y(), 0)); last.setY(std::min(last.y(), localHint.height())); - bodyRenderer->setHtml(Shared::processMessageBody(data.text)); bodyRenderer->setTextWidth(localHint.size().width()); selection.first = bodyRenderer->documentLayout()->hitTest(first, Qt::HitTestAccuracy::FuzzyHit); diff --git a/ui/widgets/messageline/messagedelegate.h b/ui/widgets/messageline/messagedelegate.h index dc0fb49..322fb76 100644 --- a/ui/widgets/messageline/messagedelegate.h +++ b/ui/widgets/messageline/messagedelegate.h @@ -58,6 +58,7 @@ public: void endClearWidgets(); void beginClearWidgets(); void leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const; + QString leftDoubleClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint); Shared::Hover hoverType(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const; QString mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint); QString clearSelection(); From 80c5e2f2b4214656ea6b62a9c3e981c79e3c122b Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 4 May 2022 19:20:30 +0300 Subject: [PATCH 059/137] added en lolcalization file, actualized localizations --- CHANGELOG.md | 1 + translations/CMakeLists.txt | 1 + translations/squawk.en.ts | 1420 ++++++++++++++++++++++++++++++++++ translations/squawk.pt_BR.ts | 347 ++++++++- translations/squawk.ru.ts | 345 ++++++++- 5 files changed, 2072 insertions(+), 42 deletions(-) create mode 100644 translations/squawk.en.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 01fa4a2..f34bf3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - if you filled password field and chose KWallet as a storage Squawk wouldn't ask you again for the same password - if left the password field empty and chose KWallet as a storage Squawk will try to get that passord from KWallet before asking you to input it - accounts now connect to the server asyncronously - if one is stopped on password prompt another is connecting +- actualized translations, added English localization file ### New features - new "About" window with links, license, gratitudes diff --git a/translations/CMakeLists.txt b/translations/CMakeLists.txt index eee4f98..f70fe2b 100644 --- a/translations/CMakeLists.txt +++ b/translations/CMakeLists.txt @@ -1,6 +1,7 @@ find_package(Qt5LinguistTools) set(TS_FILES + squawk.en.ts squawk.ru.ts squawk.pt_BR.ts ) diff --git a/translations/squawk.en.ts b/translations/squawk.en.ts new file mode 100644 index 0000000..b219af0 --- /dev/null +++ b/translations/squawk.en.ts @@ -0,0 +1,1420 @@ + + + + + About + + + + About Squawk + About window header + About Squawk + + + + + Squawk + Squawk + + + + + About + Tab title + About + + + + + XMPP (jabber) messenger + XMPP (jabber) messenger + + + + + (c) 2019 - 2022, Yury Gubich + (c) 2019 - 2022, Yury Gubich + + + + + <a href="https://git.macaw.me/blue/squawk">Project site</a> + <a href="https://git.macaw.me/blue/squawk">Project site</a> + + + + + <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">License: GNU General Public License version 3</a> + <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">License: GNU General Public License version 3</a> + + + + + Components + Tab header + Components + + + + + + + + + Version + Version + + + + + + + + + 0.0.0 + 0.0.0 + + + + + Report Bugs + Report Bugs + + + + + Please report any bug you find! +To report bugs you can use: + Please report any bug you find! +To report bugs you can use: + + + + + <a href="https://git.macaw.me/blue/squawk/issues">Project bug tracker</> + <a href="https://git.macaw.me/blue/squawk/issues">Project bug tracker</> + + + + + XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) + XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) + + + + + E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) + E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) + + + + + Thanks To + Thanks to + + + + + Vae + Vae + + + + + Major refactoring, bug fixes, constructive criticism + Major refactoring, bug fixes, constructive criticism + + + + + Shunf4 + Shunf4 + + + + + Major refactoring, bug fixes, build adaptations for Windows and MacOS + Major refactoring, bug fixes, build adaptations for Windows and MacOS + + + + + Bruno F. Fontes + Bruno F. Fontes + + + + + Brazilian Portuguese translation + Brazilian Portuguese translation + + + + + (built against %1) + (built against %1) + + + + License + License + + + + Account + + + + Account + Window title + Account + + + + + Your account login + Tooltip + Your account login + + + + + john_smith1987 + Login placeholder + john_smith1987 + + + + + Server + Server + + + + + A server address of your account. Like 404.city or macaw.me + Tooltip + A server address of your account. Like 404.city or macaw.me + + + + + macaw.me + Placeholder + macaw.me + + + + + Login + Login + + + + + Password + Password + + + + + Password of your account + Tooltip + Password of your account + + + + + Name + Name + + + + + Just a name how would you call this account, doesn't affect anything + Just a name how would you call this account, doesn't affect anything (cant be changed) + + + + + John + Placeholder + John + + + + + Resource + Resource + + + + + A resource name like "Home" or "Work" + Tooltip + A resource name like "Home" or "Work" + + + + + QXmpp + Default resource + QXmpp + + + + + Password storage + Password storage + + + + + Active + Active + + + + + enable + enable + + + + Accounts + + + + Accounts + Accounts + + + + + Delete + Delete + + + + + Add + Add + + + + + Edit + Edit + + + + + Change password + Change password + + + + + Connect + Connect + + + + Deactivate + Deactivate + + + + + Activate + Activate + + + + Application + + + from + from + + + + Attached file + Attached file + + + + Mark as Read + Mark as Read + + + + Open conversation + Open conversation + + + + Conversation + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + + + + + Type your message here... + Placeholder + Type your message here... + + + + Paste Image + Paste Image + + + + Drop files here to attach them to your message + Drop files here to attach them to your message + + + + Chose a file to send + Chose a file to send + + + + Try sending again + Try sending again + + + + Copy selected + Copy selected + + + + Copy message + Copy message + + + + Open + Open + + + + Show in folder + Show in folder + + + + Edit + Edit + + + + Editing message... + Editing message... + + + + CredentialsPrompt + + + + Authentication error: %1 + Window title + Authentication error: %1 + + + + + Couldn't authenticate account %1: login or password is icorrect. +Would you like to check them and try again? + Couldn't authenticate account %1: login or password is incorrect. +Would you like to check them and try again? + + + + + Login + Login + + + + + Your account login (without @server.domain) + Tooltip + Your account login (without @server.domain) + + + + + Password + Password + + + + + Your password + Password + + + + DialogQueue + + + Input the password for account %1 + Input the password for account %1 + + + + Password for account %1 + Password for account %1 + + + + Global + + + Online + Availability + Online + + + + Away + Availability + Away + + + + Absent + Availability + Absent + + + + Busy + Availability + Busy + + + + Chatty + Availability + Chatty + + + + Invisible + Availability + Invisible + + + + Offline + Availability + Offline + + + + Disconnected + ConnectionState + Disconnected + + + + Connecting + ConnectionState + Connecting + + + + Connected + ConnectionState + Connected + + + + Error + ConnectionState + Error + + + + None + SubscriptionState + None + + + + From + SubscriptionState + From + + + + To + SubscriptionState + To + + + + Both + SubscriptionState + Both + + + + Unknown + SubscriptionState + Unknown + + + + Unspecified + Affiliation + Unspecified + + + + Outcast + Affiliation + Outcast + + + + Nobody + Affiliation + Nobody + + + + Member + Affiliation + Member + + + + Admin + Affiliation + Admin + + + + Owner + Affiliation + Owner + + + + Unspecified + Role + Unspecified + + + + Nobody + Role + Nobody + + + + Visitor + Role + Visitor + + + + Participant + Role + Participant + + + + Moderator + Role + Moderator + + + + Pending + MessageState + Pending + + + + Sent + MessageState + Sent + + + + Delivered + MessageState + Delivered + + + + Error + MessageState + Error + + + + Plain + AccountPassword + Plain + + + + Jammed + AccountPassword + Jammed + + + + Always Ask + AccountPassword + Always Ask + + + + KWallet + AccountPassword + KWallet + + + + Your password is going to be stored in config file in plain text + AccountPasswordDescription + Your password is going to be stored in config file in plain text + + + + Your password is going to be stored in config file but jammed with constant encryption key you can find in program source code. It might look like encryption but it's not + AccountPasswordDescription + Your password is going to be stored in config file but jammed with constant encryption key you can find in program source code. It might look like encryption but it's not + + + + Squawk is going to query you for the password on every start of the program + AccountPasswordDescription + Squawk is going to query you for the password on every start of the program + + + + Your password is going to be stored in KDE wallet storage (KWallet). You're going to be queried for permissions + AccountPasswordDescription + Your password is going to be stored in KDE wallet storage (KWallet). You're going to be queried for permissions + + + + JoinConference + + + + Join new conference + Join new conference + + + + + JID + JID + + + + + Room JID + Room JID + + + + + identifier@conference.server.org + identifier@conference.server.org + + + + + Account + Account + + + + + Join on login + Join on login + + + + + If checked Squawk will try to join this conference on login + If checked Squawk will try to join this conference on login + + + + + Nick name + Nick name + + + + + Your nick name for that conference. If you leave this field empty your account name will be used as a nick name + Your nick name for that conference. If you leave this field empty your account name will be used as a nick name + + + + + John + John + + + + MessageLine + + + + Download + Download + + + + Models::Room + + + Subscribed + Subscribed + + + + Temporarily unsubscribed + Temporarily unsubscribed + + + + Temporarily subscribed + Temporarily subscribed + + + + Unsubscribed + Unsubscribed + + + + Models::Roster + + + New messages + New messages + + + + + + New messages: + New messages: + + + + + Jabber ID: + Jabber ID: + + + + + + Availability: + Availability: + + + + + + Status: + Status: + + + + + + Subscription: + Subscription: + + + + Affiliation: + Affiliation: + + + + Role: + Role: + + + + Online contacts: + Online contacts: + + + + Total contacts: + Total contacts: + + + + Members: + Members: + + + + NewContact + + + + Add new contact + Window title + Add new contact + + + + + Account + Account + + + + + An account that is going to have new contact + An account that is going to have new contact + + + + + JID + JID + + + + + Jabber id of your new contact + Jabber id of your new contact + + + + + name@server.dmn + Placeholder + name@server.dmn + + + + + Name + Name + + + + + The way this new contact will be labeled in your roster (optional) + The way this new contact will be labeled in your roster (optional) + + + + + John Smith + John Smith + + + + PageAppearance + + + + Theme + Style + + + + + Color scheme + Color scheme + + + + + + + System + System + + + + PageGeneral + + + + Downloads path + Downloads path + + + + + Browse + Browse + + + + Select where downloads folder is going to be + Select where downloads folder is going to be + + + + Settings + + + + Preferences + Window title + Preferences + + + + + + + General + General + + + + + Appearance + Appearance + + + + + Apply + Apply + + + + + Cancel + Cancel + + + + + Ok + Ok + + + + Squawk + + + + squawk + Squawk + + + + + Please select a contact to start chatting + Please select a contact to start chatting + + + + + Settings + Settings + + + + + Squawk + Menu bar entry + Squawk + + + + + Help + Help + + + + + Accounts + Accounts + + + + + Quit + Quit + + + + + Add contact + Add contact + + + + + Add conference + Join conference + + + + + Preferences + Preferences + + + + + About Squawk + About Squawk + + + + Deactivate + Deactivate + + + + Activate + Activate + + + + + VCard + VCard + + + + + + Remove + Remove + + + + Open dialog + Open dialog + + + + + Unsubscribe + Unsubscribe + + + + + Subscribe + Subscribe + + + + Rename + Rename + + + + Input new name for %1 +or leave it empty for the contact +to be displayed as %1 + Input new name for %1 +or leave it empty for the contact +to be displayed as %1 + + + + Renaming %1 + Renaming %1 + + + + Groups + Groups + + + + New group + New group + + + + New group name + New group name + + + + Add %1 to a new group + Add %1 to a new group + + + + Open conversation + Open conversation + + + + %1 account card + %1 account card + + + + %1 contact card + %1 contact card + + + + Downloading vCard + Downloading vCard + + + + VCard + + + + Received 12.07.2007 at 17.35 + Never updated + + + + + + + General + General + + + + + Organization + Organization + + + + + Middle name + Middle name + + + + + First name + First name + + + + + Last name + Last name + + + + + Nick name + Nick name + + + + + Birthday + Birthday + + + + + Organization name + Organization name + + + + + Unit / Department + Unit / Department + + + + + Role / Profession + Role / Profession + + + + + Job title + Job title + + + + + Full name + Full name + + + + + Personal information + Personal information + + + + + + + Contact + Contact + + + + + Addresses + Addresses + + + + + E-Mail addresses + E-Mail addresses + + + + + Jabber ID + Jabber ID + + + + + Web site + Web site + + + + + Phone numbers + Phone numbers + + + + + + + Description + Description + + + + + Set avatar + Set avatar + + + + + Clear avatar + Clear avatar + + + + Account %1 card + Account %1 card + + + + Contact %1 card + Contact %1 card + + + + Received %1 at %2 + Received %1 at %2 + + + + Chose your new avatar + Chose your new avatar + + + + Images (*.png *.jpg *.jpeg) + Images (*.png *.jpg *.jpeg) + + + + Add email address + Add email address + + + + Unset this email as preferred + Unset this email as preferred + + + + Set this email as preferred + Set this email as preferred + + + + Remove selected email addresses + Remove selected email addresses + + + + Copy selected emails to clipboard + Copy selected emails to clipboard + + + + Add phone number + Add phone number + + + + Unset this phone as preferred + Unset this phone as preferred + + + + Set this phone as preferred + Set this phone as preferred + + + + Remove selected phone numbers + Remove selected phone numbers + + + + Copy selected phones to clipboard + Copy selected phones to clipboard + + + diff --git a/translations/squawk.pt_BR.ts b/translations/squawk.pt_BR.ts index 4330979..6041678 100644 --- a/translations/squawk.pt_BR.ts +++ b/translations/squawk.pt_BR.ts @@ -1,6 +1,108 @@ + + About + + About Squawk + Sorbe Squawk + + + Squawk + Squawk + + + About + Tab title + Sobre + + + XMPP (jabber) messenger + XMPP (jabber) mensageiro + + + (c) 2019 - 2022, Yury Gubich + (c) 2019 - 2022, Yury Gubich + + + <a href="https://git.macaw.me/blue/squawk">Project site</a> + <a href="https://git.macaw.me/blue/squawk">Site do projeto</a> + + + <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">License: GNU General Public License version 3</a> + <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">Licença: GNU General Public License versão 3</a> + + + Components + Componentes + + + Version + Versão + + + 0.0.0 + 0.0.0 + + + Report Bugs + Relatório de erros + + + Please report any bug you find! +To report bugs you can use: + Por favor reportar qualquer erro que você encontrar! +Para relatar bugs você pode usar: + + + <a href="https://git.macaw.me/blue/squawk/issues">Project bug tracker</> + <a href="https://git.macaw.me/blue/squawk/issues">Rastreador de bugs do projeto</> + + + XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) + XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) + + + E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) + E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) + + + Thanks To + Graças ao + + + Vae + Vae + + + Major refactoring, bug fixes, constructive criticism + Refatoração importante, correção de erros, críticas construtivas + + + Shunf4 + Shunf4 + + + Major refactoring, bug fixes, build adaptations for Windows and MacOS + Refatoração importante, correção de erros, adaptações de construção para Windows e MacOS + + + Bruno F. Fontes + Bruno F. Fontes + + + Brazilian Portuguese translation + Tradução para o português do Brasil + + + (built against %1) + (Versão durante a compilação %1) + + + License + Licença + + Account @@ -10,10 +112,12 @@ Your account login + Tooltip Suas informações de login john_smith1987 + Login placeholder josé_silva1987 @@ -22,10 +126,12 @@ A server address of your account. Like 404.city or macaw.me + Tooltip O endereço do servidor da sua conta, como o 404.city ou o macaw.me macaw.me + Placeholder macaw.me @@ -38,6 +144,7 @@ Password of your account + Tooltip Senha da sua conta @@ -46,10 +153,11 @@ Just a name how would you call this account, doesn't affect anything - Apenas um nome para identificar esta conta. Não influencia em nada + Apenas um nome para identificar esta conta. Não influencia em nada (não pode ser mudado) John + Placeholder José @@ -58,6 +166,7 @@ A resource name like "Home" or "Work" + Tooltip Um nome de recurso como "Casa" ou "Trabalho" @@ -69,6 +178,14 @@ Password storage Armazenamento de senha + + Active + Ativo + + + enable + habilitar + Accounts @@ -98,30 +215,135 @@ Disconnect - Desconectar + Desconectar + + + Deactivate + Desativar + + + Activate + Ativar + + + + Application + + from + de + + + Attached file + Arquivo anexado + + + Mark as Read + Marcar como lido + + + Open conversation + Abrir conversa Conversation Type your message here... + Placeholder Digite sua mensagem aqui... Chose a file to send Escolha um arquivo para enviar + + Drop files here to attach them to your message + Arraste seus arquivos aqui para anexá-los a sua mensagem + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Liberation Sans'; font-size:10pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - Drop files here to attach them to your message - Arraste seus arquivos aqui para anexá-los a sua mensagem + Paste Image + Colar imagem + + + Try sending again + Tente enviar de novo + + + Copy selected + Copiar selecionado + + + Copy message + Copiar mensagem + + + Open + Abrir + + + Show in folder + Show in explorer + + + Edit + Editar + + + Editing message... + Messae está sendo editado... + + + + CredentialsPrompt + + Authentication error: %1 + Window title + Erro de autenticação: %1 + + + Couldn't authenticate account %1: login or password is icorrect. +Would you like to check them and try again? + Não foi possível autenticar a conta %1: login ou senha incorretos. +Deseja verificá-los e tentar novamente? + + + Login + Usuário + + + Your account login (without @server.domain) + Suas informações de login (sem @server.domain) + + + Password + Senha + + + Your password + Senha da sua conta + + + + DialogQueue + + Input the password for account %1 + Digite a senha para a conta %1 + + + Password for account %1 + Senha para a conta %1 @@ -370,14 +592,14 @@ p, li { white-space: pre-wrap; } Message Open - Abrir + Abrir MessageLine Downloading... - Baixando... + Baixando... Download @@ -386,28 +608,28 @@ p, li { white-space: pre-wrap; } Error uploading file: %1 You can try again - Error fazendo upload do arquivo: + Error fazendo upload do arquivo: %1 Você pode tentar novamente Upload - Upload + Upload Error downloading file: %1 You can try again - Erro baixando arquivo: + Erro baixando arquivo: %1 Você pode tentar novamente Uploading... - Fazendo upload... + Fazendo upload... Push the button to download the file - Pressione o botão para baixar o arquivo + Pressione o botão para baixar o arquivo @@ -481,7 +703,7 @@ Você pode tentar novamente NewContact Add new contact - Заголовок окна + Window title Adicionar novo contato @@ -502,7 +724,7 @@ Você pode tentar novamente name@server.dmn - Placeholder поля ввода JID + Placeholder nome@servidor.com.br @@ -518,6 +740,66 @@ Você pode tentar novamente José Silva + + PageAppearance + + Theme + Estilo + + + Color scheme + Esquema de cores + + + System + Do sistema + + + + PageGeneral + + Downloads path + Pasta de downloads + + + Browse + 6 / 5,000 +Translation results +Navegar + + + Select where downloads folder is going to be + Selecione onde a pasta de downloads ficará + + + + Settings + + Preferences + Window title + Preferências + + + General + Geral + + + Appearance + Aparência + + + Apply + Aplicar + + + Cancel + Cancelar + + + Ok + Feito + + Squawk @@ -530,6 +812,7 @@ Você pode tentar novamente Squawk + Menu bar entry Squawk @@ -550,11 +833,11 @@ Você pode tentar novamente Disconnect - Desconectar + Desconectar Connect - Conectar + Conectar VCard @@ -627,26 +910,46 @@ ser exibido com Attached file - Arquivo anexado + Arquivo anexado Input the password for account %1 - Digite a senha para a conta %1 + Digite a senha para a conta %1 Password for account %1 - Senha para a conta %1 + Senha para a conta %1 Please select a contact to start chatting Por favor selecione um contato para começar a conversar + + Help + Ajuda + + + Preferences + Preferências + + + About Squawk + Sorbe Squawk + + + Deactivate + Desativar + + + Activate + Ativar + VCard Received 12.07.2007 at 17.35 - Recebido 12/07/2007 às 17:35 + Nunca atualizado General @@ -682,7 +985,7 @@ ser exibido com Unit / Department - Unidade/Departamento + Unidade / Departamento Role / Profession diff --git a/translations/squawk.ru.ts b/translations/squawk.ru.ts index e3b4d52..39dbfac 100644 --- a/translations/squawk.ru.ts +++ b/translations/squawk.ru.ts @@ -1,6 +1,108 @@ + + About + + About Squawk + О Программе Squawk + + + Squawk + Squawk + + + About + Tab title + Общее + + + XMPP (jabber) messenger + XMPP (jabber) мессенджер + + + (c) 2019 - 2022, Yury Gubich + (c) 2019 - 2022, Юрий Губич + + + <a href="https://git.macaw.me/blue/squawk">Project site</a> + <a href="https://git.macaw.me/blue/squawk">Сайт проекта</a> + + + <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">License: GNU General Public License version 3</a> + <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">Лицензия: GNU General Public License версия 3</a> + + + Components + Компоненты + + + Version + Версия + + + 0.0.0 + 0.0.0 + + + Report Bugs + Сообщать об ошибках + + + Please report any bug you find! +To report bugs you can use: + Пожалуйста, сообщайте о любых ошибках! +Способы сообщить об ошибках: + + + <a href="https://git.macaw.me/blue/squawk/issues">Project bug tracker</> + <a href="https://git.macaw.me/blue/squawk/issues">Баг-трекер проекта</> + + + XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) + XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) + + + E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) + E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) + + + Thanks To + Благодарности + + + Vae + Vae + + + Major refactoring, bug fixes, constructive criticism + Крупный рефакторинг, исправление ошибок, конструктивная критика + + + Shunf4 + Shunf4 + + + Major refactoring, bug fixes, build adaptations for Windows and MacOS + Крупный рефакторинг, исправление ошибок, адаптация сборки под Windows and MacOS + + + Bruno F. Fontes + Bruno F. Fontes + + + Brazilian Portuguese translation + Перевод на Португальский (Бразилия) + + + (built against %1) + (версия при сборке %1) + + + License + Лицензия + + Account @@ -10,10 +112,12 @@ Your account login + Tooltip Имя пользователя Вашей учетной записи john_smith1987 + Login placeholder ivan_ivanov1987 @@ -22,10 +126,12 @@ A server address of your account. Like 404.city or macaw.me + Tooltip Адресс сервера вашей учетной записи (выглядит как 404.city или macaw.me) macaw.me + Placeholder macaw.me @@ -38,6 +144,7 @@ Password of your account + Tooltip Пароль вашей учетной записи @@ -46,10 +153,11 @@ Just a name how would you call this account, doesn't affect anything - Просто имя, то как Вы называете свою учетную запись, может быть любым + Просто имя, то как Вы называете свою учетную запись, может быть любым (нельзя поменять) John + Placeholder Иван @@ -58,6 +166,7 @@ A resource name like "Home" or "Work" + Tooltip Имя этой программы для ваших контактов, может быть "Home" или "Phone" @@ -69,6 +178,14 @@ Password storage Хранение пароля + + Active + Активен + + + enable + включен + Accounts @@ -98,30 +215,139 @@ Disconnect - Отключить + Отключить + + + Deactivate + Деактивировать + + + Activate + Активировать + + + + Application + + from + от + + + Attached file + Прикрепленный файл + + + Mark as Read + Пометить прочитанным + + + Open conversation + Открыть окно беседы Conversation Type your message here... + Placeholder Введите сообщение... Chose a file to send Выберите файл для отправки + + Drop files here to attach them to your message + Бросьте файлы сюда для того что бы прикрепить их к сообщению + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Liberation Sans'; font-size:10pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> - Drop files here to attach them to your message - Бросьте файлы сюда для того что бы прикрепить их к сообщению + Paste Image + Вставить изображение + + + Try sending again + Отправить снова + + + Copy selected + Скопировать выделенное + + + Copy message + Скопировать сообщение + + + Open + Открыть + + + Show in folder + Показать в проводнике + + + Edit + Редактировать + + + Editing message... + Сообщение редактируется... + + + + CredentialsPrompt + + Authentication error: %1 + Window title + Ошибка аутентификации: %1 + + + Couldn't authenticate account %1: login or password is icorrect. +Would you like to check them and try again? + Не получилось аутентифицировать +учетную запись %1: +имя пользователя или пароль введены неверно. +Желаете ли проверить их и +попробовать аутентифицироваться еще раз? + + + Login + Имя учетной записи + + + Your account login (without @server.domain) + Tooltip + Имя вашей учтетной записи (без @server.domain) + + + Password + Пароль + + + Your password + Ваш пароль + + + + DialogQueue + + Input the password for account %1 + Введите пароль для учетной записи %1 + + + Password for account %1 + Пароль для учетной записи %1 @@ -370,14 +596,14 @@ p, li { white-space: pre-wrap; } Message Open - Открыть + Открыть MessageLine Downloading... - Скачивается... + Скачивается... Download @@ -386,28 +612,28 @@ p, li { white-space: pre-wrap; } Error uploading file: %1 You can try again - Ошибка загрузки файла на сервер: + Ошибка загрузки файла на сервер: %1 Для того, что бы попробовать снова нажмите на кнопку Upload - Загрузить + Загрузить Error downloading file: %1 You can try again - Ошибка скачивания файла: + Ошибка скачивания файла: %1 Вы можете попробовать снова Uploading... - Загружается... + Загружается... Push the button to download the file - Нажмите на кнопку что бы загрузить файл + Нажмите на кнопку что бы загрузить файл @@ -481,7 +707,7 @@ You can try again NewContact Add new contact - Заголовок окна + Window title Добавление нового контакта @@ -502,7 +728,7 @@ You can try again name@server.dmn - Placeholder поля ввода JID + Placeholder name@server.dmn @@ -518,6 +744,64 @@ You can try again Иван Иванов + + PageAppearance + + Theme + Оформление + + + Color scheme + Цветовая схема + + + System + Системная + + + + PageGeneral + + Downloads path + Папка для сохраненных файлов + + + Browse + Выбрать + + + Select where downloads folder is going to be + Выберете папку, в которую будут сохраняться файлы + + + + Settings + + Preferences + Window title + Настройки + + + General + Общее + + + Appearance + Внешний вид + + + Apply + Применить + + + Cancel + Отменить + + + Ok + Готово + + Squawk @@ -530,6 +814,7 @@ You can try again Squawk + Menu bar entry Squawk @@ -550,11 +835,11 @@ You can try again Disconnect - Отключить + Отключить Connect - Подключить + Подключить VCard @@ -627,20 +912,40 @@ to be displayed as %1 Attached file - Прикрепленный файл + Прикрепленный файл Input the password for account %1 - Введите пароль для учетной записи %1 + Введите пароль для учетной записи %1 Password for account %1 - Пароль для учетной записи %1 + Пароль для учетной записи %1 Please select a contact to start chatting Выберите контакт или группу что бы начать переписку + + Help + Помощь + + + Preferences + Настройки + + + About Squawk + О Программе Squawk + + + Deactivate + Деактивировать + + + Activate + Активировать + VCard From 645b92ba51124006d61cb2f20059b186d2213b93 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 5 May 2022 20:46:49 +0300 Subject: [PATCH 060/137] release 0.2.2 preparation --- CHANGELOG.md | 2 +- README.md | 2 +- packaging/Archlinux/PKGBUILD | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f34bf3c..f4fb579 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Squawk 0.2.2 (UNRELEASED) +## Squawk 0.2.2 (May 05, 2022) ### Bug fixes - now when you remove an account it actually gets removed - segfault on unitialized Availability in some rare occesions diff --git a/README.md b/README.md index 5845c46..3e20568 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![AUR version](https://img.shields.io/aur/version/squawk?style=flat-square)](https://aur.archlinux.org/packages/squawk/) [![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me) -![Squawk screenshot](https://macaw.me/images/squawk/0.2.1.png) +![Squawk screenshot](https://macaw.me/images/squawk/0.2.2.png) ### Prerequisites diff --git a/packaging/Archlinux/PKGBUILD b/packaging/Archlinux/PKGBUILD index 899f058..7db43ff 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Yury Gubich pkgname=squawk -pkgver=0.2.1 +pkgver=0.2.2 pkgrel=1 pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)" arch=('i686' 'x86_64') @@ -14,7 +14,7 @@ optdepends=('kwallet: secure password storage (requires rebuild)' 'kio: better show in folder action (requires rebuild)') source=("$pkgname-$pkgver.tar.gz") -sha256sums=('c00dad1e441601acabb5200dc394f53abfc9876f3902a7dd4ad2fee3232ee84d') +sha256sums=('e4fa2174a3ba95159cc3b0bac3f00550c9e0ce971c55334e2662696a4543fc7e') build() { cd "$srcdir/squawk" cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release From 7192286aeb69a20c8ecd4f5337f7ea5a59935c71 Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 3 Jun 2022 09:44:48 +0300 Subject: [PATCH 061/137] fix some bugs about disabled menus --- CHANGELOG.md | 17 +++++++++++++---- CMakeLists.txt | 2 +- main/main.cpp | 2 +- ui/models/accounts.cpp | 26 +++++++++++++++++++++++--- ui/models/accounts.h | 4 +++- ui/squawk.cpp | 8 +++++--- ui/squawk.h | 2 +- ui/widgets/joinconference.cpp | 11 +++++------ ui/widgets/newcontact.cpp | 8 ++++---- 9 files changed, 56 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4fb579..600c795 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,18 @@ # Changelog +## Squawk 0.2.3 (UNRELEASED) +### Bug fixes +- "Add contact" and "Join conference" menu are enabled once again (pavavno)! + +### Improvements +- deactivated accounts now don't appear in combobox of "Add contact" and "Join conference" dialogues + +### New features + ## Squawk 0.2.2 (May 05, 2022) ### Bug fixes - now when you remove an account it actually gets removed -- segfault on unitialized Availability in some rare occesions +- segfault on uninitialized Availability in some rare occasions - fixed crash when you open a dialog with someone that has only error messages in archive - message height is now calculated correctly on Chinese and Japanese paragraphs - the app doesn't crash on SIGINT anymore @@ -12,13 +21,13 @@ - there is a way to disable an account and it wouldn't connect when you change availability - if you cancel password query an account becomes inactive and doesn't annoy you anymore - if you filled password field and chose KWallet as a storage Squawk wouldn't ask you again for the same password -- if left the password field empty and chose KWallet as a storage Squawk will try to get that passord from KWallet before asking you to input it -- accounts now connect to the server asyncronously - if one is stopped on password prompt another is connecting +- if left the password field empty and chose KWallet as a storage Squawk will try to get that password from KWallet before asking you to input it +- accounts now connect to the server asynchronously - if one is stopped on password prompt another is connecting - actualized translations, added English localization file ### New features - new "About" window with links, license, gratitudes -- if the authentication failed Squawk will ask againg for your password and login +- if the authentication failed Squawk will ask again for your password and login - now there is an amount of unread messages showing on top of Squawk launcher icon - notifications now have buttons to open a conversation or to mark that message as read diff --git a/CMakeLists.txt b/CMakeLists.txt index 75011d8..bbae079 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.4) -project(squawk VERSION 0.2.2 LANGUAGES CXX) +project(squawk VERSION 0.2.3 LANGUAGES CXX) cmake_policy(SET CMP0076 NEW) cmake_policy(SET CMP0079 NEW) diff --git a/main/main.cpp b/main/main.cpp index 60b3c83..3368e0a 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -49,7 +49,7 @@ int main(int argc, char *argv[]) QApplication::setApplicationName("squawk"); QApplication::setOrganizationName("macaw.me"); QApplication::setApplicationDisplayName("Squawk"); - QApplication::setApplicationVersion("0.2.2"); + QApplication::setApplicationVersion("0.2.3"); app.setDesktopFileName("squawk"); QTranslator qtTranslator; diff --git a/ui/models/accounts.cpp b/ui/models/accounts.cpp index 463ab40..8a9e5d9 100644 --- a/ui/models/accounts.cpp +++ b/ui/models/accounts.cpp @@ -69,6 +69,23 @@ int Models::Accounts::rowCount ( const QModelIndex& parent ) const return accs.size(); } +unsigned int Models::Accounts::size() const +{ + return rowCount(QModelIndex()); +} + +unsigned int Models::Accounts::activeSize() const +{ + unsigned int size = 0; + for (const Models::Account* acc : accs) { + if (acc->getActive()) { + ++size; + } + } + + return size; +} + QVariant Models::Accounts::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { @@ -97,6 +114,7 @@ void Models::Accounts::addAccount(Account* account) endInsertRows(); emit sizeChanged(accs.size()); + emit changed(); } void Models::Accounts::onAccountChanged(Item* item, int row, int col) @@ -157,12 +175,14 @@ void Models::Accounts::removeAccount(int index) emit sizeChanged(accs.size()); } -std::deque Models::Accounts::getNames() const +std::deque Models::Accounts::getActiveNames() const { std::deque res; - for (std::deque::const_iterator i = accs.begin(), end = accs.end(); i != end; ++i) { - res.push_back((*i)->getName()); + for (const Models::Account* acc : accs) { + if (acc->getActive()) { + res.push_back(acc->getName()); + } } return res; diff --git a/ui/models/accounts.h b/ui/models/accounts.h index e8be07c..c5d59af 100644 --- a/ui/models/accounts.h +++ b/ui/models/accounts.h @@ -39,11 +39,13 @@ public: QVariant data ( const QModelIndex& index, int role ) const override; int columnCount ( const QModelIndex& parent ) const override; int rowCount ( const QModelIndex& parent ) const override; + unsigned int size () const; + unsigned int activeSize () const; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; Account* getAccount(int index); - std::deque getNames() const; + std::deque getActiveNames() const; signals: void changed(); diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 9b6158c..db92e93 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -63,7 +63,7 @@ Squawk::Squawk(Models::Roster& p_rosterModel, QWidget *parent) : connect(m_ui->roster, &QTreeView::collapsed, this, &Squawk::onItemCollepsed); connect(m_ui->roster->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &Squawk::onRosterSelectionChanged); - connect(rosterModel.accountsModel, &Models::Accounts::sizeChanged, this, &Squawk::onAccountsSizeChanged); + connect(rosterModel.accountsModel, &Models::Accounts::changed, this, &Squawk::onAccountsChanged); connect(contextMenu, &QMenu::aboutToHide, this, &Squawk::onContextAboutToHide); connect(m_ui->actionAboutSquawk, &QAction::triggered, this, &Squawk::onAboutSquawkCalled); //m_ui->mainToolBar->addWidget(m_ui->comboBox); @@ -87,6 +87,8 @@ Squawk::Squawk(Models::Roster& p_rosterModel, QWidget *parent) : m_ui->splitter->restoreState(settings.value("splitter").toByteArray()); } settings.endGroup(); + + onAccountsChanged(); } Squawk::~Squawk() { @@ -129,9 +131,9 @@ void Squawk::onPreferences() } } - -void Squawk::onAccountsSizeChanged(unsigned int size) +void Squawk::onAccountsChanged() { + unsigned int size = rosterModel.accountsModel->activeSize(); if (size > 0) { m_ui->actionAddContact->setEnabled(true); m_ui->actionAddConference->setEnabled(true); diff --git a/ui/squawk.h b/ui/squawk.h index 15a73dd..f55a000 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -116,7 +116,7 @@ private slots: void onNewConference(); void onNewContactAccepted(); void onJoinConferenceAccepted(); - void onAccountsSizeChanged(unsigned int size); + void onAccountsChanged(); void onAccountsClosed(); void onPreferencesClosed(); void onVCardClosed(); diff --git a/ui/widgets/joinconference.cpp b/ui/widgets/joinconference.cpp index 648de25..890e4e2 100644 --- a/ui/widgets/joinconference.cpp +++ b/ui/widgets/joinconference.cpp @@ -26,10 +26,10 @@ JoinConference::JoinConference(const Models::Accounts* accounts, QWidget* parent m_ui(new Ui::JoinConference()) { m_ui->setupUi ( this ); - std::deque names = accounts->getNames(); + std::deque names = accounts->getActiveNames(); - for (std::deque::const_iterator i = names.begin(), end = names.end(); i != end; i++) { - m_ui->account->addItem(*i); + for (const QString& name : names) { + m_ui->account->addItem(name); } m_ui->account->setCurrentIndex(0); @@ -40,12 +40,11 @@ JoinConference::JoinConference(const QString& acc, const Models::Accounts* accou m_ui(new Ui::JoinConference()) { m_ui->setupUi ( this ); - std::deque names = accounts->getNames(); + std::deque names = accounts->getActiveNames(); int index = 0; bool found = false; - for (std::deque::const_iterator i = names.begin(), end = names.end(); i != end; i++) { - const QString& name = *i; + for (const QString& name : names) { m_ui->account->addItem(name); if (!found) { if (name == acc) { diff --git a/ui/widgets/newcontact.cpp b/ui/widgets/newcontact.cpp index 920c757..9303963 100644 --- a/ui/widgets/newcontact.cpp +++ b/ui/widgets/newcontact.cpp @@ -25,10 +25,10 @@ NewContact::NewContact(const Models::Accounts* accounts, QWidget* parent): m_ui(new Ui::NewContact()) { m_ui->setupUi ( this ); - std::deque names = accounts->getNames(); + std::deque names = accounts->getActiveNames(); - for (std::deque::const_iterator i = names.begin(), end = names.end(); i != end; i++) { - m_ui->account->addItem(*i); + for (const QString& name : names) { + m_ui->account->addItem(name); } m_ui->account->setCurrentIndex(0); @@ -40,7 +40,7 @@ NewContact::NewContact(const QString& acc, const Models::Accounts* accounts, QWi { m_ui->setupUi ( this ); - std::deque names = accounts->getNames(); + std::deque names = accounts->getActiveNames(); int index = 0; bool found = false; From 7e9eed207557188f375d455d442cedacd032ed8e Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 15 Aug 2022 19:40:07 +0300 Subject: [PATCH 062/137] First tray attempt, seems to be working --- CHANGELOG.md | 1 + main/application.cpp | 96 ++++++++++++++++++++++++++++- main/application.h | 11 +++- translations/squawk.en.ts | 5 ++ translations/squawk.pt_BR.ts | 5 ++ translations/squawk.ru.ts | 5 ++ ui/squawk.cpp | 3 +- ui/squawk.h | 2 + ui/widgets/settings/pagegeneral.cpp | 33 ++++++++++ ui/widgets/settings/pagegeneral.h | 3 + ui/widgets/settings/pagegeneral.ui | 32 +++++++++- ui/widgets/settings/settings.cpp | 11 ++++ ui/widgets/settings/settings.h | 1 + 13 files changed, 201 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 600c795..40a85c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - deactivated accounts now don't appear in combobox of "Add contact" and "Join conference" dialogues ### New features +- Now you can enable tray icon from settings! ## Squawk 0.2.2 (May 05, 2022) ### Bug fixes diff --git a/main/application.cpp b/main/application.cpp index 216e322..a319ee5 100644 --- a/main/application.cpp +++ b/main/application.cpp @@ -27,8 +27,14 @@ Application::Application(Core::Squawk* p_core): dialogueQueue(roster), nowQuitting(false), destroyingSquawk(false), - storage() + storage(), + trayIcon(nullptr), + actionQuit(Shared::icon("application-exit"), tr("Quit")), + actionToggle(tr("Minimize to tray")) { + 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); @@ -130,6 +136,12 @@ void Application::quit() if (squawk != nullptr) { squawk->close(); } + + if (trayIcon != nullptr) { + trayIcon->deleteLater(); + trayIcon = nullptr; + } + if (!destroyingSquawk) { checkForTheLastWindow(); } @@ -155,6 +167,8 @@ void Application::createMainWindow() connect(squawk, &Squawk::openedConversation, this, &Application::onSquawkOpenedConversation); connect(squawk, &Squawk::openConversation, this, &Application::openConversation); connect(squawk, &Squawk::changeState, this, &Application::setState); + connect(squawk, &Squawk::changeTray, this, &Application::onChangeTray); + connect(squawk, &Squawk::quit, this, &Application::quit); connect(squawk, &Squawk::closing, this, &Application::onSquawkClosing); connect(squawk, &Squawk::modifyAccountRequest, core, &Core::Squawk::modifyAccountRequest); @@ -194,8 +208,15 @@ void Application::onSquawkClosing() squawk->deleteLater(); squawk = nullptr; - //for now - quit(); + QSettings settings; + if (!nowQuitting && QSystemTrayIcon::isSystemTrayAvailable() && settings.value("tray", false).toBool()) { + if (settings.value("hideTray", false).toBool()) { + createTrayIcon(); + } + actionToggle.setText(tr("Show Squawk")); + } else { + quit(); + } } void Application::onSquawkDestroyed() { @@ -205,6 +226,71 @@ void Application::onSquawkDestroyed() { } } +void Application::onChangeTray(bool enabled, bool hide) +{ + if (enabled) { + if (trayIcon == nullptr) { + if (!hide || squawk == nullptr) { + createTrayIcon(); + } + } else { + if (hide && squawk != nullptr) { + trayIcon->deleteLater(); + trayIcon = nullptr; + } + } + } else if (trayIcon == nullptr) { + trayIcon->deleteLater(); + trayIcon = nullptr; + } +} + +void Application::createTrayIcon() +{ + trayIcon = new QSystemTrayIcon(); + + QMenu* trayIconMenu = new QMenu(); + trayIconMenu->addAction(&actionToggle); + trayIconMenu->addAction(&actionQuit); + + trayIcon->setContextMenu(trayIconMenu); + trayIcon->setIcon(QApplication::windowIcon().pixmap(32, 32)); + trayIcon->setToolTip(QApplication::applicationDisplayName()); + + connect(trayIcon, &QSystemTrayIcon::activated, this, &Application::trayClicked); + connect(trayIcon, &QSystemTrayIcon::destroyed, trayIconMenu, &QMenu::deleteLater); + + trayIcon->show(); +} + +void Application::trayClicked(QSystemTrayIcon::ActivationReason reason) +{ + switch (reason) { + case QSystemTrayIcon::Trigger: + case QSystemTrayIcon::DoubleClick: + case QSystemTrayIcon::MiddleClick: + toggleSquawk(); + break; + default: + break; + } +} + +void Application::toggleSquawk() +{ + QSettings settings; + if (squawk == nullptr) { + createMainWindow(); + if (settings.value("hideTray", false).toBool()) { + trayIcon->deleteLater(); + trayIcon = nullptr; + } + + actionToggle.setText(tr("Minimize to tray")); + } else { + squawk->close(); + } +} void Application::notify(const QString& account, const Shared::Message& msg) { @@ -345,6 +431,10 @@ void Application::readSettings() setState(Shared::Global::fromInt(avail)); createMainWindow(); + + if (settings.value("tray", false).toBool() && !settings.value("hideTray", false).toBool()) { + createTrayIcon(); + } } void Application::writeSettings() diff --git a/main/application.h b/main/application.h index 301edc4..db60009 100644 --- a/main/application.h +++ b/main/application.h @@ -21,6 +21,9 @@ #include #include +#include +#include +#include #include "dialogqueue.h" #include "core/squawk.h" @@ -91,13 +94,16 @@ private slots: void onSquawkDestroyed(); void onNotificationClosed(quint32 id, quint32 reason); void onNotificationInvoked(quint32 id, const QString& action); - + void onChangeTray(bool enabled, bool hide); + void trayClicked(QSystemTrayIcon::ActivationReason reason); + void toggleSquawk(); private: void createMainWindow(); void subscribeConversation(Conversation* conv); void checkForTheLastWindow(); void focusConversation(const Models::Roster::ElId& id, const QString& resource = "", const QString& messageId = ""); + void createTrayIcon(); private: typedef std::map Conversations; @@ -113,6 +119,9 @@ private: bool nowQuitting; bool destroyingSquawk; Notifications storage; + QSystemTrayIcon* trayIcon; + QAction actionQuit; + QAction actionToggle; }; #endif // APPLICATION_H diff --git a/translations/squawk.en.ts b/translations/squawk.en.ts index b219af0..0aef979 100644 --- a/translations/squawk.en.ts +++ b/translations/squawk.en.ts @@ -972,6 +972,11 @@ Would you like to check them and try again? Downloads path Downloads path + + + Close to tray icon + Close to tray icon + diff --git a/translations/squawk.pt_BR.ts b/translations/squawk.pt_BR.ts index 6041678..f818e63 100644 --- a/translations/squawk.pt_BR.ts +++ b/translations/squawk.pt_BR.ts @@ -761,6 +761,11 @@ Você pode tentar novamente Downloads path Pasta de downloads + + + Close to tray icon + Fechar ao ícone da bandeja + Browse 6 / 5,000 diff --git a/translations/squawk.ru.ts b/translations/squawk.ru.ts index 39dbfac..1774bb9 100644 --- a/translations/squawk.ru.ts +++ b/translations/squawk.ru.ts @@ -765,6 +765,11 @@ You can try again Downloads path Папка для сохраненных файлов + + + Close to tray icon + Закрывать в трей + Browse Выбрать diff --git a/ui/squawk.cpp b/ui/squawk.cpp index db92e93..39a7202 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -56,7 +56,7 @@ Squawk::Squawk(Models::Roster& p_rosterModel, QWidget *parent) : connect(m_ui->actionPreferences, &QAction::triggered, this, &Squawk::onPreferences); connect(m_ui->actionAddContact, &QAction::triggered, this, &Squawk::onNewContact); connect(m_ui->actionAddConference, &QAction::triggered, this, &Squawk::onNewConference); - connect(m_ui->actionQuit, &QAction::triggered, this, &Squawk::close); + connect(m_ui->actionQuit, &QAction::triggered, this, &Squawk::quit); connect(m_ui->comboBox, qOverload(&QComboBox::activated), this, &Squawk::onComboboxActivated); //connect(m_ui->roster, &QTreeView::doubleClicked, this, &Squawk::onRosterItemDoubleClicked); connect(m_ui->roster, &QTreeView::customContextMenuRequested, this, &Squawk::onRosterContextMenu); @@ -122,6 +122,7 @@ void Squawk::onPreferences() preferences->setAttribute(Qt::WA_DeleteOnClose); connect(preferences, &Settings::destroyed, this, &Squawk::onPreferencesClosed); connect(preferences, &Settings::changeDownloadsPath, this, &Squawk::changeDownloadsPath); + connect(preferences, &Settings::changeTray, this, &Squawk::changeTray); preferences->show(); } else { diff --git a/ui/squawk.h b/ui/squawk.h index f55a000..19fc058 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -59,6 +59,7 @@ public: signals: void closing(); + void quit(); void newAccountRequest(const QMap&); void removeAccountRequest(const QString&); void connectAccount(const QString&); @@ -74,6 +75,7 @@ signals: void requestVCard(const QString& account, const QString& jid); void uploadVCard(const QString& account, const Shared::VCard& card); void changeDownloadsPath(const QString& path); + void changeTray(bool enabled, bool hide); void notify(const QString& account, const Shared::Message& msg); void changeSubscription(const Models::Roster::ElId& id, bool subscribe); diff --git a/ui/widgets/settings/pagegeneral.cpp b/ui/widgets/settings/pagegeneral.cpp index 9ed46a2..39e155a 100644 --- a/ui/widgets/settings/pagegeneral.cpp +++ b/ui/widgets/settings/pagegeneral.cpp @@ -28,6 +28,23 @@ PageGeneral::PageGeneral(QWidget* parent): QSettings settings; m_ui->downloadsPathInput->setText(settings.value("downloadsPath").toString()); + if (QSystemTrayIcon::isSystemTrayAvailable()) { + bool tray = settings.value("tray", false).toBool(); + m_ui->trayInput->setChecked(tray); + if (tray) { + m_ui->hideTrayInput->setChecked(settings.value("hideTray", false).toBool()); + } else { + m_ui->hideTrayInput->setEnabled(false); + } + + connect(m_ui->trayInput, &QCheckBox::stateChanged, this, &PageGeneral::onTrayChecked); + connect(m_ui->hideTrayInput, &QCheckBox::stateChanged, this, &PageGeneral::onHideTrayChecked); + } else { + m_ui->trayInput->setEnabled(false); + m_ui->hideTrayInput->setEnabled(false); + m_ui->trayInput->setToolTip(tr("Tray is not available for your system")); //TODO translate + } + connect(m_ui->downloadsPathButton, &QPushButton::clicked, this, &PageGeneral::onBrowseButtonClicked); } @@ -76,3 +93,19 @@ void PageGeneral::onDialogDestroyed() { dialog = nullptr; } + +void PageGeneral::onTrayChecked(int state) +{ + bool enabled = state == Qt::Checked; + emit variableModified("tray", enabled); + m_ui->hideTrayInput->setEnabled(enabled); + if (!enabled) { + m_ui->hideTrayInput->setEnabled(false); + } +} + +void PageGeneral::onHideTrayChecked(int state) +{ + bool enabled = state == Qt::Checked; + emit variableModified("hideTray", enabled); +} diff --git a/ui/widgets/settings/pagegeneral.h b/ui/widgets/settings/pagegeneral.h index 7f58d71..15738be 100644 --- a/ui/widgets/settings/pagegeneral.h +++ b/ui/widgets/settings/pagegeneral.h @@ -24,6 +24,7 @@ #include #include #include +#include namespace Ui { @@ -47,6 +48,8 @@ private slots: void onBrowseButtonClicked(); void onDialogAccepted(); void onDialogDestroyed(); + void onTrayChecked(int state); + void onHideTrayChecked(int state); private: QScopedPointer m_ui; diff --git a/ui/widgets/settings/pagegeneral.ui b/ui/widgets/settings/pagegeneral.ui index e412668..e08b606 100644 --- a/ui/widgets/settings/pagegeneral.ui +++ b/ui/widgets/settings/pagegeneral.ui @@ -6,8 +6,8 @@ 0 0 - 400 - 300 + 477 + 310
@@ -39,6 +39,34 @@
+ + + + Tray icon + + + + + + + Mimimize Squawk to tray when closing main window + + + + + + + Hide tray icon + + + + + + + Hide tray icon when Squawk main window is visible + + + diff --git a/ui/widgets/settings/settings.cpp b/ui/widgets/settings/settings.cpp index cf5e905..62dd80b 100644 --- a/ui/widgets/settings/settings.cpp +++ b/ui/widgets/settings/settings.cpp @@ -57,6 +57,7 @@ void Settings::onVariableModified(const QString& key, const QVariant& value) void Settings::apply() { QSettings settings; + bool trayChanged = false; for (const std::pair& pair: modifiedSettings) { if (pair.first == "style") { Shared::Global::setStyle(pair.second.toString()); @@ -73,8 +74,18 @@ void Settings::apply() emit changeDownloadsPath(path); } } + } else if (pair.first == "tray" || pair.first == "hideTray") { + bool oldValue = settings.value(pair.first, false).toBool(); + bool newValue = pair.second.toBool(); + if (oldValue != newValue) { + trayChanged = true; + settings.setValue(pair.first, pair.second); + } } + } + if (trayChanged) { + emit changeTray(settings.value("tray", false).toBool(), settings.value("hideTray", false).toBool()); } modifiedSettings.clear(); diff --git a/ui/widgets/settings/settings.h b/ui/widgets/settings/settings.h index 689e0ce..59a65cb 100644 --- a/ui/widgets/settings/settings.h +++ b/ui/widgets/settings/settings.h @@ -45,6 +45,7 @@ public: signals: void changeDownloadsPath(const QString& path); + void changeTray(bool enable, bool hide); public slots: void apply(); From d162494ec8d6c26fd48099ca3558acd931705b88 Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 17 Aug 2022 19:25:35 +0300 Subject: [PATCH 063/137] Better way to store expanded elements in roster, several clean ups, translations --- CHANGELOG.md | 3 + main/application.cpp | 96 ++++++--- main/application.h | 7 +- translations/squawk.en.ts | 259 ++++++++---------------- translations/squawk.pt_BR.ts | 299 +++++++++++++++++++++++++++- translations/squawk.ru.ts | 299 +++++++++++++++++++++++++++- ui/models/element.cpp | 5 + ui/models/element.h | 1 + ui/models/item.cpp | 5 + ui/models/item.h | 1 + ui/models/reference.cpp | 5 + ui/models/reference.h | 2 + ui/models/room.h | 1 - ui/models/roster.cpp | 82 +++++++- ui/models/roster.h | 11 +- ui/squawk.cpp | 45 +---- ui/squawk.h | 3 +- ui/widgets/settings/pagegeneral.cpp | 2 +- 18 files changed, 874 insertions(+), 252 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40a85c8..7107e26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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! diff --git a/main/application.cpp b/main/application.cpp index a319ee5..3942e53 100644 --- a/main/application.cpp +++ b/main/application.cpp @@ -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(&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& 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 address = roster.getItemPath(index); + if (address.size() > 0) { + expandedPaths.erase(address); + } +} + +void Application::onItemExpanded(const QModelIndex& index) +{ + std::list address = roster.getItemPath(index); + if (address.size() > 0) { + expandedPaths.insert(address); + } +} + +void Application::onAddedElement(const std::list& 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(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(avail)); @@ -440,7 +494,22 @@ void Application::readSettings() void Application::writeSettings() { QSettings settings; - settings.setValue("availability", static_cast(availability)); + settings.beginGroup("ui"); + settings.setValue("availability", static_cast(availability)); + + settings.remove("roster"); + settings.beginGroup("roster"); + for (const std::list& 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& 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); diff --git a/main/application.h b/main/application.h index db60009..54c2dbc 100644 --- a/main/application.h +++ b/main/application.h @@ -18,6 +18,8 @@ #define APPLICATION_H #include +#include +#include #include #include @@ -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& 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& path); private: void createMainWindow(); @@ -122,6 +126,7 @@ private: QSystemTrayIcon* trayIcon; QAction actionQuit; QAction actionToggle; + std::set> expandedPaths; }; #endif // APPLICATION_H diff --git a/translations/squawk.en.ts b/translations/squawk.en.ts index 0aef979..db178ac 100644 --- a/translations/squawk.en.ts +++ b/translations/squawk.en.ts @@ -5,51 +5,43 @@ About - About Squawk About window header About Squawk - Squawk Squawk - About Tab title About - XMPP (jabber) messenger XMPP (jabber) messenger - (c) 2019 - 2022, Yury Gubich (c) 2019 - 2022, Yury Gubich - <a href="https://git.macaw.me/blue/squawk">Project site</a> <a href="https://git.macaw.me/blue/squawk">Project site</a> - <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">License: GNU General Public License version 3</a> <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">License: GNU General Public License version 3</a> - Components Tab header Components @@ -58,9 +50,6 @@ - - - Version Version @@ -68,21 +57,16 @@ - - - 0.0.0 0.0.0 - Report Bugs Report Bugs - Please report any bug you find! To report bugs you can use: Please report any bug you find! @@ -90,61 +74,51 @@ To report bugs you can use: - <a href="https://git.macaw.me/blue/squawk/issues">Project bug tracker</> <a href="https://git.macaw.me/blue/squawk/issues">Project bug tracker</> - XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) - E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) - Thanks To Thanks to - Vae Vae - Major refactoring, bug fixes, constructive criticism Major refactoring, bug fixes, constructive criticism - Shunf4 Shunf4 - Major refactoring, bug fixes, build adaptations for Windows and MacOS Major refactoring, bug fixes, build adaptations for Windows and MacOS - Bruno F. Fontes Bruno F. Fontes - Brazilian Portuguese translation Brazilian Portuguese translation @@ -164,118 +138,100 @@ To report bugs you can use: Account - Account Window title Account - Your account login Tooltip Your account login - john_smith1987 Login placeholder john_smith1987 - Server Server - A server address of your account. Like 404.city or macaw.me Tooltip A server address of your account. Like 404.city or macaw.me - macaw.me Placeholder macaw.me - Login Login - Password Password - Password of your account Tooltip Password of your account - Name Name - Just a name how would you call this account, doesn't affect anything Just a name how would you call this account, doesn't affect anything (cant be changed) - John Placeholder John - Resource Resource - A resource name like "Home" or "Work" Tooltip A resource name like "Home" or "Work" - QXmpp Default resource QXmpp - Password storage Password storage - Active Active - enable enable @@ -284,37 +240,31 @@ To report bugs you can use: Accounts - Accounts Accounts - Delete Delete - Add Add - Edit Edit - Change password Change password - Connect Connect @@ -333,22 +283,38 @@ To report bugs you can use: Application - + + Quit + Quit + + + + + Minimize to tray + Minimize to tray + + + + Show Squawk + Show Squawk + + + from from - + Attached file Attached file - + Mark as Read Mark as Read - + Open conversation Open conversation @@ -357,7 +323,6 @@ To report bugs you can use: Conversation - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -371,7 +336,6 @@ p, li { white-space: pre-wrap; } - Type your message here... Placeholder Type your message here... @@ -431,14 +395,12 @@ p, li { white-space: pre-wrap; } CredentialsPrompt - Authentication error: %1 Window title Authentication error: %1 - Couldn't authenticate account %1: login or password is icorrect. Would you like to check them and try again? Couldn't authenticate account %1: login or password is incorrect. @@ -446,26 +408,22 @@ Would you like to check them and try again? - Login Login - Your account login (without @server.domain) Tooltip Your account login (without @server.domain) - Password Password - Your password Password @@ -724,61 +682,51 @@ Would you like to check them and try again? JoinConference - Join new conference Join new conference - JID JID - Room JID Room JID - identifier@conference.server.org identifier@conference.server.org - Account Account - Join on login Join on login - If checked Squawk will try to join this conference on login If checked Squawk will try to join this conference on login - Nick name Nick name - Your nick name for that conference. If you leave this field empty your account name will be used as a nick name Your nick name for that conference. If you leave this field empty your account name will be used as a nick name - John John @@ -818,66 +766,66 @@ Would you like to check them and try again? Models::Roster - + New messages New messages - - - + + + New messages: New messages: - - + + Jabber ID: Jabber ID: - - - + + + Availability: Availability: - - - + + + Status: Status: - - + + Subscription: Subscription: - + Affiliation: Affiliation: - + Role: Role: - + Online contacts: Online contacts: - + Total contacts: Total contacts: - + Members: Members: @@ -886,57 +834,48 @@ Would you like to check them and try again? NewContact - Add new contact Window title Add new contact - Account Account - An account that is going to have new contact An account that is going to have new contact - JID JID - Jabber id of your new contact Jabber id of your new contact - name@server.dmn Placeholder name@server.dmn - Name Name - The way this new contact will be labeled in your roster (optional) The way this new contact will be labeled in your roster (optional) - John Smith John Smith @@ -945,13 +884,11 @@ Would you like to check them and try again? PageAppearance - Theme Style - Color scheme Color scheme @@ -968,23 +905,45 @@ Would you like to check them and try again? PageGeneral - Downloads path Downloads path + Tray icon + Tray icon + + + + Mimimize Squawk to tray when closing main window + Mimimize Squawk to tray when closing main window + + + + Hide tray icon + Hide tray icon + + + + Hide tray icon when Squawk main window is visible + Hide tray icon when Squawk main window is visible + + Close to tray icon - Close to tray icon + Close to tray icon - Browse Browse + Tray is not available for your system + Tray is not available for your system + + + Select where downloads folder is going to be Select where downloads folder is going to be @@ -993,7 +952,6 @@ Would you like to check them and try again? Settings - Preferences Window title Preferences @@ -1001,32 +959,26 @@ Would you like to check them and try again? - - General General - Appearance Appearance - Apply Apply - Cancel Cancel - Ok Ok @@ -1035,118 +987,107 @@ Would you like to check them and try again? Squawk - squawk Squawk - Please select a contact to start chatting Please select a contact to start chatting - Settings Settings - Squawk Menu bar entry Squawk - Help Help - Accounts Accounts - Quit Quit - Add contact Add contact - Add conference Join conference - Preferences Preferences - About Squawk About Squawk - + Deactivate Deactivate - + Activate Activate - - + + VCard VCard - - - + + + Remove Remove - + Open dialog Open dialog - - + + Unsubscribe Unsubscribe - - + + Subscribe Subscribe - + Rename Rename - + Input new name for %1 or leave it empty for the contact to be displayed as %1 @@ -1155,47 +1096,47 @@ or leave it empty for the contact to be displayed as %1 - + Renaming %1 Renaming %1 - + Groups Groups - + New group New group - + New group name New group name - + Add %1 to a new group Add %1 to a new group - + Open conversation Open conversation - + %1 account card %1 account card - + %1 contact card %1 contact card - + Downloading vCard Downloading vCard @@ -1204,145 +1145,119 @@ to be displayed as %1 VCard - Received 12.07.2007 at 17.35 Never updated - - General General - Organization Organization - Middle name Middle name - First name First name - Last name Last name - Nick name Nick name - Birthday Birthday - Organization name Organization name - Unit / Department Unit / Department - Role / Profession Role / Profession - Job title Job title - Full name Full name - Personal information Personal information - - Contact Contact - Addresses Addresses - E-Mail addresses E-Mail addresses - Jabber ID Jabber ID - Web site Web site - Phone numbers Phone numbers - - Description Description - Set avatar Set avatar - Clear avatar Clear avatar diff --git a/translations/squawk.pt_BR.ts b/translations/squawk.pt_BR.ts index f818e63..5d76ade 100644 --- a/translations/squawk.pt_BR.ts +++ b/translations/squawk.pt_BR.ts @@ -4,101 +4,130 @@ About + About Squawk Sorbe Squawk + Squawk Squawk + About Tab title Sobre + XMPP (jabber) messenger XMPP (jabber) mensageiro + (c) 2019 - 2022, Yury Gubich (c) 2019 - 2022, Yury Gubich + <a href="https://git.macaw.me/blue/squawk">Project site</a> <a href="https://git.macaw.me/blue/squawk">Site do projeto</a> + <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">License: GNU General Public License version 3</a> <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">Licença: GNU General Public License versão 3</a> + Components Componentes + + + Version Versão + + + 0.0.0 0.0.0 + Report Bugs Relatório de erros + Please report any bug you find! To report bugs you can use: Por favor reportar qualquer erro que você encontrar! Para relatar bugs você pode usar: + <a href="https://git.macaw.me/blue/squawk/issues">Project bug tracker</> <a href="https://git.macaw.me/blue/squawk/issues">Rastreador de bugs do projeto</> + XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) + E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) + Thanks To Graças ao + Vae Vae + Major refactoring, bug fixes, constructive criticism Refatoração importante, correção de erros, críticas construtivas + Shunf4 Shunf4 + Major refactoring, bug fixes, build adaptations for Windows and MacOS Refatoração importante, correção de erros, adaptações de construção para Windows e MacOS + Bruno F. Fontes Bruno F. Fontes + Brazilian Portuguese translation Tradução para o português do Brasil + + (built against %1) (Versão durante a compilação %1) + License Licença @@ -106,83 +135,101 @@ Para relatar bugs você pode usar: Account + Account Window header Conta + Your account login Tooltip Suas informações de login + john_smith1987 Login placeholder josé_silva1987 + Server Servidor + A server address of your account. Like 404.city or macaw.me Tooltip O endereço do servidor da sua conta, como o 404.city ou o macaw.me + macaw.me Placeholder macaw.me + Login Usuário + Password Senha + Password of your account Tooltip Senha da sua conta + Name Nome + Just a name how would you call this account, doesn't affect anything Apenas um nome para identificar esta conta. Não influencia em nada (não pode ser mudado) + John Placeholder José + Resource Recurso + A resource name like "Home" or "Work" Tooltip Um nome de recurso como "Casa" ou "Trabalho" + QXmpp Default resource QXmpp + Password storage Armazenamento de senha + Active Ativo + enable habilitar @@ -190,26 +237,32 @@ Para relatar bugs você pode usar: Accounts + Accounts Contas + Delete Apagar + Add Adicionar + Edit Editar + Change password Alterar senha + Connect Conectar @@ -218,10 +271,13 @@ Para relatar bugs você pode usar: Desconectar + Deactivate Desativar + + Activate Ativar @@ -229,18 +285,38 @@ Para relatar bugs você pode usar: Application + + Quit + Sair + + + + + Minimize to tray + Minimizar para bandeja + + + + Show Squawk + Aparecer Squawk + + + from de + Attached file Arquivo anexado + Mark as Read Marcar como lido + Open conversation Abrir conversa @@ -248,19 +324,23 @@ Para relatar bugs você pode usar: Conversation + Type your message here... Placeholder Digite sua mensagem aqui... + Chose a file to send Escolha um arquivo para enviar + Drop files here to attach them to your message Arraste seus arquivos aqui para anexá-los a sua mensagem + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -273,34 +353,42 @@ p, li { white-space: pre-wrap; } <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + Paste Image Colar imagem + Try sending again Tente enviar de novo + Copy selected Copiar selecionado + Copy message Copiar mensagem + Open Abrir + Show in folder Show in explorer + Edit Editar + Editing message... Messae está sendo editado... @@ -308,29 +396,35 @@ p, li { white-space: pre-wrap; } CredentialsPrompt + Authentication error: %1 Window title Erro de autenticação: %1 + Couldn't authenticate account %1: login or password is icorrect. Would you like to check them and try again? Não foi possível autenticar a conta %1: login ou senha incorretos. Deseja verificá-los e tentar novamente? + Login Usuário + Your account login (without @server.domain) Suas informações de login (sem @server.domain) + Password Senha + Your password Senha da sua conta @@ -338,10 +432,12 @@ Deseja verificá-los e tentar novamente? DialogQueue + Input the password for account %1 Digite a senha para a conta %1 + Password for account %1 Senha para a conta %1 @@ -349,196 +445,235 @@ Deseja verificá-los e tentar novamente? Global + Online Availability Conectado + Away Availability Distante + Absent Availability Ausente + Busy Availability Ocupado + Chatty Availability Tagarela + Invisible Availability Invisível + Offline Availability Desconectado + Disconnected ConnectionState Desconectado + Connecting ConnectionState Connectando + Connected ConnectionState Conectado + Error ConnectionState Erro + None SubscriptionState Nenhum + From SubscriptionState De + To SubscriptionState Para + Both SubscriptionState Ambos + Unknown SubscriptionState Desconhecido + Unspecified Affiliation Não especificado + Outcast Affiliation Rejeitado + Nobody Affiliation Ninguém + Member Affiliation Membro + Admin Affiliation Administrador + Owner Affiliation Dono + Unspecified Role Não especificado + Nobody Role Ninguém + Visitor Role Visitante + Participant Role Participante + Moderator Role Moderador + Pending MessageState Aguardando + Sent MessageState Enviada + Delivered MessageState Entregue + Error MessageState Erro + Plain AccountPassword Texto simples + Jammed AccountPassword Embaralhado + Always Ask AccountPassword Sempre perguntar + KWallet AccountPassword KWallet + Your password is going to be stored in config file but jammed with constant encryption key you can find in program source code. It might look like encryption but it's not AccountPasswordDescription Sua senha será armazenada em um arquivo de configurações, porém embaralhada com uma chave criptográfica constante que você pode encontrar no código fonte do programa. Parece criptografado, mas não é + Squawk is going to query you for the password on every start of the program AccountPasswordDescription O Squark vai requisitar sua senha a cada vez que você abrir o programa + Your password is going to be stored in config file in plain text AccountPasswordDescription Sua senha será armazenada em um arquivo de configurações em texto simples + Your password is going to be stored in KDE wallet storage (KWallet). You're going to be queried for permissions AccountPasswordDescription Sua senha será armazenada no KDE wallet (KWallet). Sua autorização será requerida @@ -547,43 +682,53 @@ Deseja verificá-los e tentar novamente? JoinConference + Join new conference Заголовок окна Entrar em uma nova conferência + JID JID + Room JID Sala JID + identifier@conference.server.org identifier@conference.server.org + Account Conta + Join on login Entrar ao se conectar + If checked Squawk will try to join this conference on login Se marcado, o Squawk tentará entrar nesta conferência automaticamente durante o login + Nick name Apelido + Your nick name for that conference. If you leave this field empty your account name will be used as a nick name Seu apelido para essa conferência. Se você deixar este campo em branco, seu nome de usuário será usado como apelido + John José @@ -602,6 +747,8 @@ Deseja verificá-los e tentar novamente? Baixando... + + Download Baixar @@ -635,18 +782,22 @@ Você pode tentar novamente Models::Room + Subscribed Inscrito + Temporarily unsubscribed Inscrição temporariamente cancelada + Temporarily subscribed Temporariamente inscrito + Unsubscribed Não inscrito @@ -654,47 +805,67 @@ Você pode tentar novamente Models::Roster + New messages Novas mensagens + + + New messages: Novas mensagens: + + Jabber ID: ID Jabber: + + + Availability: Disponibilidade: + + + Status: Status: + + + Subscription: Inscrição: + Affiliation: Я правда не знаю, как это объяснить, не то что перевести Afiliação: + Role: Papel: + Online contacts: Contatos online: + Total contacts: Contatos totais: + Members: Membros: @@ -702,40 +873,49 @@ Você pode tentar novamente NewContact + Add new contact Window title Adicionar novo contato + Account Conta + An account that is going to have new contact A conta que terá um novo contato + JID JID + Jabber id of your new contact ID Jabber do seu novo contato + name@server.dmn Placeholder nome@servidor.com.br + Name Nome + The way this new contact will be labeled in your roster (optional) A forma com que o novo contato será classificado em sua lista (opcional) + John Smith José Silva @@ -743,14 +923,20 @@ Você pode tentar novamente PageAppearance + Theme Estilo + Color scheme Esquema de cores + + + + System Do sistema @@ -758,21 +944,48 @@ Você pode tentar novamente PageGeneral + Downloads path Pasta de downloads - Close to tray icon - Fechar ao ícone da bandeja + Tray icon + Ícone da bandeja + + Mimimize Squawk to tray when closing main window + Minimizar Squawk para bandeja ao fechar a janela principal + + + + Hide tray icon + Esconder o ícone da bandeja + + + + Hide tray icon when Squawk main window is visible + Esconder o ícone da bandeja quando a janela principal não está fechada + + + Close to tray icon + Fechar ao ícone da bandeja + + + Browse 6 / 5,000 Translation results Navegar + + Tray is not available for your system + A bandeja não está disponível para seu sistema + + + Select where downloads folder is going to be Selecione onde a pasta de downloads ficará @@ -780,27 +993,34 @@ Navegar Settings + Preferences Window title Preferências + + General Geral + Appearance Aparência + Apply Aplicar + Cancel Cancelar + Ok Feito @@ -808,31 +1028,38 @@ Navegar Squawk + squawk Squawk + Settings Configurações + Squawk Menu bar entry Squawk + Accounts Contas + Quit Sair + Add contact Adicionar contato + Add conference Adicionar conferência @@ -845,30 +1072,42 @@ Navegar Conectar + + VCard VCard + + + Remove Remover + Open dialog Abrir caixa de diálogo + + Unsubscribe Cancelar inscrição + + Subscribe Inscrever-se + Rename Renomear + Input new name for %1 or leave it empty for the contact to be displayed as %1 @@ -878,38 +1117,47 @@ ser exibido com %1 + Renaming %1 Renomeando %1 + Groups Grupos + New group Novo grupo + New group name Novo nome do grupo + Add %1 to a new group Adicionar %1 a um novo grupo + Open conversation Abrir conversa + %1 account card cartão da conta %1 + %1 contact card cartão de contato %1 + Downloading vCard Baixando vCard @@ -926,26 +1174,32 @@ ser exibido com Senha para a conta %1 + Please select a contact to start chatting Por favor selecione um contato para começar a conversar + Help Ajuda + Preferences Preferências + About Squawk Sorbe Squawk + Deactivate Desativar + Activate Ativar @@ -953,154 +1207,195 @@ ser exibido com VCard + Received 12.07.2007 at 17.35 Nunca atualizado + + General Geral + Organization Empresa + Middle name Nome do meio + First name Primeiro nome + Last name Sobrenome + Nick name Apelido + Birthday Data de aniversário + Organization name Nome da empresa + Unit / Department Unidade / Departamento + Role / Profession Profissão + Job title Cargo + Full name Nome completo + Personal information Informações pessoais + Addresses Endereços + E-Mail addresses Endereços de e-mail + Phone numbers Números de telefone + + Contact Contato + Jabber ID ID Jabber + Web site Site web + + Description Descrição + Set avatar Definir avatar + Clear avatar Apagar avatar + Account %1 card Cartão da conta %1 + Contact %1 card Cartão do contato %1 + Received %1 at %2 Recebido %1 em %2 + Chose your new avatar Escolha um novo avatar + Images (*.png *.jpg *.jpeg) Imagens (*.png *.jpg *.jpeg) + Add email address Adicionar endereço de e-mail + Unset this email as preferred Desmarcar este e-mail como preferido + Set this email as preferred Marcar este e-mail como preferido + Remove selected email addresses Remover endereço de e-mail selecionado + Copy selected emails to clipboard Copiar endereços de e-mails selecionados para a área de transferência + Add phone number Adicionar número de telefone + Unset this phone as preferred Desmarcar este número de telefone como preferido + Set this phone as preferred Marcar este número de telefone como preferido + Remove selected phone numbers Remover os números de telefones selecionados + Copy selected phones to clipboard Copiar os números de telefone selecionados para a área de transferência diff --git a/translations/squawk.ru.ts b/translations/squawk.ru.ts index 1774bb9..421a2d2 100644 --- a/translations/squawk.ru.ts +++ b/translations/squawk.ru.ts @@ -4,101 +4,130 @@ About + About Squawk О Программе Squawk + Squawk Squawk + About Tab title Общее + XMPP (jabber) messenger XMPP (jabber) мессенджер + (c) 2019 - 2022, Yury Gubich (c) 2019 - 2022, Юрий Губич + <a href="https://git.macaw.me/blue/squawk">Project site</a> <a href="https://git.macaw.me/blue/squawk">Сайт проекта</a> + <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">License: GNU General Public License version 3</a> <a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">Лицензия: GNU General Public License версия 3</a> + Components Компоненты + + + Version Версия + + + 0.0.0 0.0.0 + Report Bugs Сообщать об ошибках + Please report any bug you find! To report bugs you can use: Пожалуйста, сообщайте о любых ошибках! Способы сообщить об ошибках: + <a href="https://git.macaw.me/blue/squawk/issues">Project bug tracker</> <a href="https://git.macaw.me/blue/squawk/issues">Баг-трекер проекта</> + XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>) + E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>) + Thanks To Благодарности + Vae Vae + Major refactoring, bug fixes, constructive criticism Крупный рефакторинг, исправление ошибок, конструктивная критика + Shunf4 Shunf4 + Major refactoring, bug fixes, build adaptations for Windows and MacOS Крупный рефакторинг, исправление ошибок, адаптация сборки под Windows and MacOS + Bruno F. Fontes Bruno F. Fontes + Brazilian Portuguese translation Перевод на Португальский (Бразилия) + + (built against %1) (версия при сборке %1) + License Лицензия @@ -106,83 +135,101 @@ To report bugs you can use: Account + Account Заголовок окна Учетная запись + Your account login Tooltip Имя пользователя Вашей учетной записи + john_smith1987 Login placeholder ivan_ivanov1987 + Server Сервер + A server address of your account. Like 404.city or macaw.me Tooltip Адресс сервера вашей учетной записи (выглядит как 404.city или macaw.me) + macaw.me Placeholder macaw.me + Login Имя учетной записи + Password Пароль + Password of your account Tooltip Пароль вашей учетной записи + Name Имя + Just a name how would you call this account, doesn't affect anything Просто имя, то как Вы называете свою учетную запись, может быть любым (нельзя поменять) + John Placeholder Иван + Resource Ресурс + A resource name like "Home" or "Work" Tooltip Имя этой программы для ваших контактов, может быть "Home" или "Phone" + QXmpp Ресурс по умолчанию QXmpp + Password storage Хранение пароля + Active Активен + enable включен @@ -190,26 +237,32 @@ To report bugs you can use: Accounts + Accounts Учетные записи + Delete Удалить + Add Добавить + Edit Редактировать + Change password Изменить пароль + Connect Подключить @@ -218,10 +271,13 @@ To report bugs you can use: Отключить + Deactivate Деактивировать + + Activate Активировать @@ -229,18 +285,38 @@ To report bugs you can use: Application + + Quit + Выйти + + + + + Minimize to tray + Свернуть в трей + + + + Show Squawk + Показать Squawk + + + from от + Attached file Прикрепленный файл + Mark as Read Пометить прочитанным + Open conversation Открыть окно беседы @@ -248,19 +324,23 @@ To report bugs you can use: Conversation + Type your message here... Placeholder Введите сообщение... + Chose a file to send Выберите файл для отправки + Drop files here to attach them to your message Бросьте файлы сюда для того что бы прикрепить их к сообщению + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -273,34 +353,42 @@ p, li { white-space: pre-wrap; } <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> + Paste Image Вставить изображение + Try sending again Отправить снова + Copy selected Скопировать выделенное + Copy message Скопировать сообщение + Open Открыть + Show in folder Показать в проводнике + Edit Редактировать + Editing message... Сообщение редактируется... @@ -308,11 +396,13 @@ p, li { white-space: pre-wrap; } CredentialsPrompt + Authentication error: %1 Window title Ошибка аутентификации: %1 + Couldn't authenticate account %1: login or password is icorrect. Would you like to check them and try again? Не получилось аутентифицировать @@ -322,19 +412,23 @@ Would you like to check them and try again? попробовать аутентифицироваться еще раз? + Login Имя учетной записи + Your account login (without @server.domain) Tooltip Имя вашей учтетной записи (без @server.domain) + Password Пароль + Your password Ваш пароль @@ -342,10 +436,12 @@ Would you like to check them and try again? DialogQueue + Input the password for account %1 Введите пароль для учетной записи %1 + Password for account %1 Пароль для учетной записи %1 @@ -353,196 +449,235 @@ Would you like to check them and try again? Global + Online Availability В сети + Away Availability Отошел + Absent Availability Недоступен + Busy Availability Занят + Chatty Availability Готов поболтать + Invisible Availability Невидимый + Offline Availability Отключен + Disconnected ConnectionState Отключен + Connecting ConnectionState Подключается + Connected ConnectionState Подключен + Error ConnectionState Ошибка + None SubscriptionState Нет + From SubscriptionState Входящая + To SubscriptionState Исходящая + Both SubscriptionState Взаимная + Unknown SubscriptionState Неизвестно + Unspecified Affiliation Не назначено + Outcast Affiliation Изгой + Nobody Affiliation Никто + Member Affiliation Участник + Admin Affiliation Администратор + Owner Affiliation Владелец + Unspecified Role Не назначено + Nobody Role Никто + Visitor Role Гость + Participant Role Участник + Moderator Role Модератор + Pending MessageState В процессе отправки + Sent MessageState Отправлено + Delivered MessageState Доставлено + Error MessageState Ошибка + Plain AccountPassword Открытый текст + Jammed AccountPassword Обфусцированный + Always Ask AccountPassword Всегда спрашивать + KWallet AccountPassword KWallet + Your password is going to be stored in config file but jammed with constant encryption key you can find in program source code. It might look like encryption but it's not AccountPasswordDescription Ваш пароль будет храниться в обфусцированном виде в конфигурационном файле. Обфускация производится с помощью постоянного числа, которое можно найти в исходном коде программы. Это может и выглядит как шифрование но им не является + Squawk is going to query you for the password on every start of the program AccountPasswordDescription Squawk будет спрашивать пароль от этой учетной записи каждый раз при запуске + Your password is going to be stored in config file in plain text AccountPasswordDescription Ваш пароль будет храниться в конфигурационном файле открытым текстром + Your password is going to be stored in KDE wallet storage (KWallet). You're going to be queried for permissions AccountPasswordDescription Ваш пароль будет храниться в бумажнике KDE (KWallet). В первый раз программа попросит разрешения для доступа к бумажнику @@ -551,43 +686,53 @@ Would you like to check them and try again? JoinConference + Join new conference Заголовок окна Присоединиться к новой беседе + JID JID + Room JID Jabber-идентификатор беседы + identifier@conference.server.org identifier@conference.server.org + Account Учетная запись + Join on login Автовход + If checked Squawk will try to join this conference on login Если стоит галочка Squawk автоматически присоединится к этой беседе при подключении + Nick name Псевдоним + Your nick name for that conference. If you leave this field empty your account name will be used as a nick name Ваш псевдоним в этой беседе, если оставите это поле пустым - будет использовано имя Вашей учетной записи + John Ivan @@ -606,6 +751,8 @@ Would you like to check them and try again? Скачивается... + + Download Скачать @@ -639,18 +786,22 @@ You can try again Models::Room + Subscribed Вы состоите в беседе + Temporarily unsubscribed Вы временно не состоите в беседе + Temporarily subscribed Вы временно состоите в беседе + Unsubscribed Вы не состоите в беседе @@ -658,47 +809,67 @@ You can try again Models::Roster + New messages Есть непрочитанные сообщения + + + New messages: Новых сообщений: + + Jabber ID: Идентификатор: + + + Availability: Доступность: + + + Status: Статус: + + + Subscription: Подписка: + Affiliation: Я правда не знаю, как это объяснить, не то что перевести Причастность: + Role: Роль: + Online contacts: Контакстов в сети: + Total contacts: Всего контактов: + Members: Участников: @@ -706,40 +877,49 @@ You can try again NewContact + Add new contact Window title Добавление нового контакта + Account Учетная запись + An account that is going to have new contact Учетная запись для которой будет добавлен контакт + JID JID + Jabber id of your new contact Jabber-идентификатор нового контакта + name@server.dmn Placeholder name@server.dmn + Name Имя + The way this new contact will be labeled in your roster (optional) То, как будет подписан контакт в вашем списке контактов (не обязательно) + John Smith Иван Иванов @@ -747,14 +927,20 @@ You can try again PageAppearance + Theme Оформление + Color scheme Цветовая схема + + + + System Системная @@ -762,19 +948,46 @@ You can try again PageGeneral + Downloads path Папка для сохраненных файлов - Close to tray icon - Закрывать в трей + Tray icon + Иконка трея + + Mimimize Squawk to tray when closing main window + Сворачивать Squawk в трей когда закрывается основное окно + + + + Hide tray icon + Скрывать иконку трея + + + + Hide tray icon when Squawk main window is visible + Прятать иконку трея если основное окно не закрыто + + + Close to tray icon + Закрывать в трей + + + Browse Выбрать + + Tray is not available for your system + На вашей системе недоступен трей + + + Select where downloads folder is going to be Выберете папку, в которую будут сохраняться файлы @@ -782,27 +995,34 @@ You can try again Settings + Preferences Window title Настройки + + General Общее + Appearance Внешний вид + Apply Применить + Cancel Отменить + Ok Готово @@ -810,31 +1030,38 @@ You can try again Squawk + squawk Squawk + Settings Настройки + Squawk Menu bar entry Squawk + Accounts Учетные записи + Quit Выйти + Add contact Добавить контакт + Add conference Присоединиться к беседе @@ -847,30 +1074,42 @@ You can try again Подключить + + VCard Карточка + + + Remove Удалить + Open dialog Открыть диалог + + Unsubscribe Отписаться + + Subscribe Подписаться + Rename Переименовать + Input new name for %1 or leave it empty for the contact to be displayed as %1 @@ -880,38 +1119,47 @@ to be displayed as %1 %1 + Renaming %1 Назначение имени контакту %1 + Groups Группы + New group Создать новую группу + New group name Имя группы + Add %1 to a new group Добавление %1 в новую группу + Open conversation Открыть окно беседы + %1 account card Карточка учетной записи %1 + %1 contact card Карточка контакта %1 + Downloading vCard Получение карточки @@ -928,26 +1176,32 @@ to be displayed as %1 Пароль для учетной записи %1 + Please select a contact to start chatting Выберите контакт или группу что бы начать переписку + Help Помощь + Preferences Настройки + About Squawk О Программе Squawk + Deactivate Деактивировать + Activate Активировать @@ -955,154 +1209,195 @@ to be displayed as %1 VCard + Received 12.07.2007 at 17.35 Не обновлялось + + General Общее + Organization Место работы + Middle name Среднее имя + First name Имя + Last name Фамилия + Nick name Псевдоним + Birthday Дата рождения + Organization name Название организации + Unit / Department Отдел + Role / Profession Профессия + Job title Наименование должности + Full name Полное имя + Personal information Личная информация + Addresses Адреса + E-Mail addresses Адреса электронной почты + Phone numbers Номера телефонов + + Contact Контактная информация + Jabber ID Jabber ID + Web site Веб сайт + + Description Описание + Set avatar Установить иконку + Clear avatar Убрать иконку + Account %1 card Карточка учетной записи %1 + Contact %1 card Карточка контакта %1 + Received %1 at %2 Получено %1 в %2 + Chose your new avatar Выберите новую иконку + Images (*.png *.jpg *.jpeg) Изображения (*.png *.jpg *.jpeg) + Add email address Добавить адрес электронной почты + Unset this email as preferred Убрать отметку "предпочтительный" с этого адреса + Set this email as preferred Отметить этот адрес как "предпочтительный" + Remove selected email addresses Удалить выбранные адреса + Copy selected emails to clipboard Скопировать выбранные адреса в буфер обмена + Add phone number Добавить номер телефона + Unset this phone as preferred Убрать отметку "предпочтительный" с этого номера + Set this phone as preferred Отметить этот номер как "предпочтительный" + Remove selected phone numbers Удалить выбранные телефонные номера + Copy selected phones to clipboard Скопировать выбранные телефонные номера в буфер обмена diff --git a/ui/models/element.cpp b/ui/models/element.cpp index acea46f..e89c5aa 100644 --- a/ui/models/element.cpp +++ b/ui/models/element.cpp @@ -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; diff --git a/ui/models/element.h b/ui/models/element.h index c6d3d6e..2ce5a21 100644 --- a/ui/models/element.h +++ b/ui/models/element.h @@ -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& data); diff --git a/ui/models/item.cpp b/ui/models/item.cpp index 4a88dd2..dda28d6 100644 --- a/ui/models/item.cpp +++ b/ui/models/item.cpp @@ -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) { diff --git a/ui/models/item.h b/ui/models/item.h index 4661479..d3fbb02 100644 --- a/ui/models/item.h +++ b/ui/models/item.h @@ -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); diff --git a/ui/models/reference.cpp b/ui/models/reference.cpp index 1aaea15..fe3bf41 100644 --- a/ui/models/reference.cpp +++ b/ui/models/reference.cpp @@ -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; diff --git a/ui/models/reference.h b/ui/models/reference.h index 8ec5352..bc717bb 100644 --- a/ui/models/reference.h +++ b/ui/models/reference.h @@ -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; diff --git a/ui/models/room.h b/ui/models/room.h index 707b35b..b3e889c 100644 --- a/ui/models/room.h +++ b/ui/models/room.h @@ -76,7 +76,6 @@ private: private: bool autoJoin; bool joined; - QString jid; QString nick; QString subject; std::map participants; diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index fbb7e52..8ce3464 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -52,6 +52,8 @@ void Models::Roster::addAccount(const QMap& 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 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& 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::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::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::const_iterator itr = accounts.find(account); if (itr == accounts.end()) { @@ -1113,3 +1126,66 @@ void Models::Roster::recalculateUnreadMessages() } emit unreadMessagesCountChanged(count); } + +std::list Models::Roster::getItemPath(const QModelIndex& index) const +{ + std::list result; + if (index.isValid() && index.model() == this) { + Item* item = static_cast(index.internalPointer()); + while (item->type != Item::root) { + result.push_front(item->getId()); + item = item->parentItem(); + } + } + + return result; +} + +QModelIndex Models::Roster::getIndexByPath(const std::list& 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(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::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; +} diff --git a/ui/models/roster.h b/ui/models/roster.h index efc50f2..59fef44 100644 --- a/ui/models/roster.h +++ b/ui/models/roster.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #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& path) const; + std::list getItemPath(const QModelIndex& index) const; + bool markMessageAsRead(const ElId& elementId, const QString& messageId); void responseArchive(const QString& account, const QString& jid, const std::list& 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& 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& roles); diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 39a7202..c086e10 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -60,7 +60,8 @@ Squawk::Squawk(Models::Roster& p_rosterModel, QWidget *parent) : connect(m_ui->comboBox, qOverload(&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,49 +492,7 @@ 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 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(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) diff --git a/ui/squawk.h b/ui/squawk.h index 19fc058..84e94a8 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -83,6 +83,8 @@ signals: void openConversation(const Models::Roster::ElId& id, const QString& resource = ""); void modifyAccountRequest(const QString&, const QMap&); + 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(); diff --git a/ui/widgets/settings/pagegeneral.cpp b/ui/widgets/settings/pagegeneral.cpp index 39e155a..9e73574 100644 --- a/ui/widgets/settings/pagegeneral.cpp +++ b/ui/widgets/settings/pagegeneral.cpp @@ -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); } } From 2ae75a4b916b01b91a8e55bb005a6bbf7b23246f Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 20 Aug 2022 00:28:59 +0300 Subject: [PATCH 064/137] New object for cached database, also ClientInfo class --- core/account.cpp | 2 ++ core/squawk.h | 3 ++ core/storage/CMakeLists.txt | 2 ++ core/storage/cache.cpp | 69 +++++++++++++++++++++++++++++++++++++ core/storage/cache.h | 50 +++++++++++++++++++++++++++ core/storage/storage.cpp | 52 +++++++++++++++++++--------- core/storage/storage.h | 7 ++-- shared/CMakeLists.txt | 2 ++ shared/clientinfo.cpp | 69 +++++++++++++++++++++++++++++++++++++ shared/clientinfo.h | 50 +++++++++++++++++++++++++++ 10 files changed, 287 insertions(+), 19 deletions(-) create mode 100644 core/storage/cache.cpp create mode 100644 core/storage/cache.h create mode 100644 shared/clientinfo.cpp create mode 100644 shared/clientinfo.h diff --git a/core/account.cpp b/core/account.cpp index 3b9d7ec..0cb159d 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -93,6 +93,8 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& QObject::connect(reconnectTimer, &QTimer::timeout, this, &Account::onReconnectTimer); if (name == "Test") { + qDebug() << "Presence capabilities: " << presence.capabilityNode(); + QXmppLogger* logger = new QXmppLogger(this); logger->setLoggingType(QXmppLogger::SignalLogging); client.setLogger(logger); diff --git a/core/squawk.h b/core/squawk.h index c82b1c8..3b8073b 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -31,8 +31,10 @@ #include "shared/enums.h" #include "shared/message.h" #include "shared/global.h" +#include "shared/clientinfo.h" #include "networkaccess.h" #include "external/simpleCrypt/simplecrypt.h" +#include #ifdef WITH_KWALLET #include "passwordStorageEngines/kwallet.h" @@ -135,6 +137,7 @@ private: Shared::Availability state; NetworkAccess network; bool isInitialized; + //Cache clientCache; #ifdef WITH_KWALLET PSE::KWallet kwallet; diff --git a/core/storage/CMakeLists.txt b/core/storage/CMakeLists.txt index 4c263d5..5d34b7c 100644 --- a/core/storage/CMakeLists.txt +++ b/core/storage/CMakeLists.txt @@ -5,4 +5,6 @@ target_sources(squawk PRIVATE storage.h urlstorage.cpp urlstorage.h + cache.cpp + cache.h ) diff --git a/core/storage/cache.cpp b/core/storage/cache.cpp new file mode 100644 index 0000000..71fe369 --- /dev/null +++ b/core/storage/cache.cpp @@ -0,0 +1,69 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "cache.h" + +#include + +template class Core::Cache; + +template +Core::Cache::Cache(const QString& name): + storage(name), + cache(new std::map ()) {} + +template +Core::Cache::~Cache() { + close(); + delete cache; +} + +template +void Core::Cache::open() { + storage.open();} + +template +void Core::Cache::close() { + storage.close();} + +template +void Core::Cache::addRecord(const QString& key, const T& value) { + storage.addRecord(key, value); + cache->insert(std::make_pair(key, value)); +} + +template +T Core::Cache::getRecord(const QString& key) const { + typename std::map::const_iterator itr = cache->find(key); + if (itr == cache->end()) { + T value = storage.getRecord(key); + itr = cache->insert(std::make_pair(key, value)).first; + } + + return itr->second; +} + +template +void Core::Cache::changeRecord(const QString& key, const T& value) { + storage.changeRecord(key, value); + cache->at(key) = value; +} + +template +void Core::Cache::removeRecord(const QString& key) { + storage.removeRecord(key); + cache->erase(key); +} diff --git a/core/storage/cache.h b/core/storage/cache.h new file mode 100644 index 0000000..e5b1b88 --- /dev/null +++ b/core/storage/cache.h @@ -0,0 +1,50 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 CORE_CACHE_H +#define CORE_CACHE_H + +#include + +#include + +#include + +namespace Core { + +template +class Cache +{ +public: + Cache(const QString& name); + ~Cache(); + + void open(); + void close(); + + void addRecord(const QString& key, const T& value); + void changeRecord(const QString& key, const T& value); + void removeRecord(const QString& key); + T getRecord(const QString& key) const; + +private: + Core::Storage storage; + std::map* cache; +}; + +} + +#endif // CORE_CACHE_H diff --git a/core/storage/storage.cpp b/core/storage/storage.cpp index 7ff0ef7..99db8af 100644 --- a/core/storage/storage.cpp +++ b/core/storage/storage.cpp @@ -21,7 +21,12 @@ #include "storage.h" -Core::Storage::Storage(const QString& p_name): +#include + +template class Core::Storage; + +template +Core::Storage::Storage(const QString& p_name): name(p_name), opened(false), environment(), @@ -29,12 +34,14 @@ Core::Storage::Storage(const QString& p_name): { } -Core::Storage::~Storage() +template +Core::Storage::~Storage() { close(); } -void Core::Storage::open() +template +void Core::Storage::open() { if (!opened) { mdb_env_create(&environment); @@ -61,7 +68,8 @@ void Core::Storage::open() } } -void Core::Storage::close() +template +void Core::Storage::close() { if (opened) { mdb_dbi_close(environment, base); @@ -70,19 +78,22 @@ void Core::Storage::close() } } -void Core::Storage::addRecord(const QString& key, const QString& value) +template +void Core::Storage::addRecord(const QString& key, const T& value) { if (!opened) { throw Archive::Closed("addRecord", name.toStdString()); } + QByteArray ba; + QDataStream ds(&ba, QIODevice::WriteOnly); const std::string& id = key.toStdString(); - const std::string& val = value.toStdString(); + ds << value; MDB_val lmdbKey, lmdbData; lmdbKey.mv_size = id.size(); lmdbKey.mv_data = (char*)id.c_str(); - lmdbData.mv_size = val.size(); - lmdbData.mv_data = (char*)val.c_str(); + lmdbData.mv_size = ba.size(); + lmdbData.mv_data = (uint8_t*)ba.data(); MDB_txn *txn; mdb_txn_begin(environment, NULL, 0, &txn); int rc; @@ -99,19 +110,23 @@ void Core::Storage::addRecord(const QString& key, const QString& value) } } -void Core::Storage::changeRecord(const QString& key, const QString& value) +template +void Core::Storage::changeRecord(const QString& key, const T& value) { if (!opened) { throw Archive::Closed("changeRecord", name.toStdString()); } + + QByteArray ba; + QDataStream ds(&ba, QIODevice::WriteOnly); const std::string& id = key.toStdString(); - const std::string& val = value.toStdString(); + ds << value; MDB_val lmdbKey, lmdbData; lmdbKey.mv_size = id.size(); lmdbKey.mv_data = (char*)id.c_str(); - lmdbData.mv_size = val.size(); - lmdbData.mv_data = (char*)val.c_str(); + lmdbData.mv_size = ba.size(); + lmdbData.mv_data = (uint8_t*)ba.data(); MDB_txn *txn; mdb_txn_begin(environment, NULL, 0, &txn); int rc; @@ -126,7 +141,8 @@ void Core::Storage::changeRecord(const QString& key, const QString& value) } } -QString Core::Storage::getRecord(const QString& key) const +template +T Core::Storage::getRecord(const QString& key) const { if (!opened) { throw Archive::Closed("addElement", name.toStdString()); @@ -149,14 +165,18 @@ QString Core::Storage::getRecord(const QString& key) const throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); } } else { - std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size); - QString value(sId.c_str()); + QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size); + QDataStream ds(&ba, QIODevice::ReadOnly); + T value; + ds >> value; mdb_txn_abort(txn); + return value; } } -void Core::Storage::removeRecord(const QString& key) +template +void Core::Storage::removeRecord(const QString& key) { if (!opened) { throw Archive::Closed("addElement", name.toStdString()); diff --git a/core/storage/storage.h b/core/storage/storage.h index d2abfde..5942c0f 100644 --- a/core/storage/storage.h +++ b/core/storage/storage.h @@ -29,6 +29,7 @@ namespace Core { /** * @todo write docs */ +template class Storage { public: @@ -38,10 +39,10 @@ public: void open(); void close(); - void addRecord(const QString& key, const QString& value); - void changeRecord(const QString& key, const QString& value); + void addRecord(const QString& key, const T& value); + void changeRecord(const QString& key, const T& value); void removeRecord(const QString& key); - QString getRecord(const QString& key) const; + T getRecord(const QString& key) const; private: diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index 0ab7dbd..1e47618 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -18,4 +18,6 @@ target_sources(squawk PRIVATE vcard.h pathcheck.cpp pathcheck.h + clientinfo.h + clientinfo.cpp ) diff --git a/shared/clientinfo.cpp b/shared/clientinfo.cpp new file mode 100644 index 0000000..745bd54 --- /dev/null +++ b/shared/clientinfo.cpp @@ -0,0 +1,69 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "clientinfo.h" + +Shared::ClientInfo::ClientInfo(): + name(), + category(), + type(), + capabilitiesNode(), + capabilitiesVerification(), + capabilitiesHash(), + capabilitiesExtensions() {} + +QDataStream & Shared::ClientInfo::operator >> (QDataStream& stream) const { + stream << name; + stream << category; + stream << type; + stream << capabilitiesNode; + stream << capabilitiesVerification; + stream << capabilitiesHash; + stream << (quint8)capabilitiesExtensions.size(); + for (const QString& ext : capabilitiesExtensions) { + stream << ext; + } + + return stream; +} + +QDataStream & Shared::ClientInfo::operator << (QDataStream& stream) { + stream >> name; + stream >> category; + stream >> type; + stream >> capabilitiesNode; + stream >> capabilitiesVerification; + stream >> capabilitiesHash; + + quint8 size; + stream >> size; + for (quint8 i = 0; i < size; ++i) { + QString ext; + stream >> ext; + capabilitiesExtensions.insert(ext); + } + + return stream; +} + +QDataStream& operator<< (QDataStream& stream, const Shared::ClientInfo& image) { + image >> stream; + return stream; +} +QDataStream& operator>> (QDataStream& stream, Shared::ClientInfo& image) { + image << stream; + return stream; +} diff --git a/shared/clientinfo.h b/shared/clientinfo.h new file mode 100644 index 0000000..742e49a --- /dev/null +++ b/shared/clientinfo.h @@ -0,0 +1,50 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 SHARED_CLIENTINFO_H +#define SHARED_CLIENTINFO_H + +#include + +#include +#include + +namespace Shared { + +class ClientInfo +{ +public: + ClientInfo(); + + QDataStream& operator << (QDataStream& stream); + QDataStream& operator >> (QDataStream& stream) const; + +public: + QString name; + QString category; + QString type; + QString capabilitiesNode; + QString capabilitiesVerification; + QString capabilitiesHash; + std::set capabilitiesExtensions; +}; + +} + +QDataStream& operator<< (QDataStream& stream, const Shared::ClientInfo& image); +QDataStream& operator>> (QDataStream& stream, Shared::ClientInfo& image); + +#endif // SHARED_CLIENTINFO_H From 037dabbe06bf8ec6e219c15becac83eb8ad3c2c6 Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 22 Aug 2022 23:29:43 +0300 Subject: [PATCH 065/137] some new shared classes, little reorganization, preparation to cache client info --- core/CMakeLists.txt | 3 +- core/account.cpp | 35 +++++++++++--- core/account.h | 9 +++- core/components/CMakeLists.txt | 6 +++ core/components/clientcache.cpp | 55 ++++++++++++++++++++++ core/components/clientcache.h | 56 +++++++++++++++++++++++ core/{ => components}/networkaccess.cpp | 0 core/{ => components}/networkaccess.h | 4 +- core/squawk.cpp | 30 +++++++++++- core/squawk.h | 9 ++-- core/storage/CMakeLists.txt | 4 +- core/storage/cache.h | 5 ++ core/storage/{cache.cpp => cache.hpp} | 49 ++++++++++++++++---- core/storage/storage.h | 3 ++ core/storage/{storage.cpp => storage.hpp} | 12 +++-- main/main.cpp | 4 ++ shared/CMakeLists.txt | 1 + shared/clientinfo.cpp | 6 +++ shared/clientinfo.h | 3 ++ shared/identity.h | 35 ++++++++++++++ 20 files changed, 297 insertions(+), 32 deletions(-) create mode 100644 core/components/CMakeLists.txt create mode 100644 core/components/clientcache.cpp create mode 100644 core/components/clientcache.h rename core/{ => components}/networkaccess.cpp (100%) rename core/{ => components}/networkaccess.h (98%) rename core/storage/{cache.cpp => cache.hpp} (60%) rename core/storage/{storage.cpp => storage.hpp} (97%) create mode 100644 shared/identity.h diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 6c7a3b5..d3327c9 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -12,8 +12,6 @@ target_sources(squawk PRIVATE conference.h contact.cpp contact.h - networkaccess.cpp - networkaccess.h rosteritem.cpp rosteritem.h ${SIGNALCATCHER_SOURCE} @@ -27,3 +25,4 @@ target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS}) add_subdirectory(handlers) add_subdirectory(storage) add_subdirectory(passwordStorageEngines) +add_subdirectory(components) diff --git a/core/account.cpp b/core/account.cpp index 0cb159d..08321d3 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -285,7 +285,10 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) emit addPresence(jid, resource, { {"lastActivity", lastInteraction}, {"availability", p_presence.availableStatusType()}, //TODO check and handle invisible - {"status", p_presence.statusText()} + {"status", p_presence.statusText()}, + {"capabilityNode", p_presence.capabilityNode()}, + {"capabilityVer", p_presence.capabilityVer().toBase64()}, + {"capabilityHash", p_presence.capabilityHash()} }); } break; @@ -594,7 +597,8 @@ void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items) void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) { - if (info.from() == getServer()) { + QString from = info.from(); + if (from == getServer()) { bool enableCC = false; qDebug() << "Server info received for account" << name; QStringList features = info.features(); @@ -613,7 +617,7 @@ void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) qDebug() << "Requesting account" << name << "capabilities"; dm->requestInfo(getBareJid()); - } else if (info.from() == getBareJid()) { + } else if (from == getBareJid()) { qDebug() << "Received capabilities for account" << name << ":"; QList identities = info.identities(); bool pepSupported = false; @@ -626,10 +630,27 @@ void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) } rh->setPepSupport(pepSupported); } else { - qDebug() << "Received info for account" << name << "about" << info.from(); - QList identities = info.identities(); - for (const QXmppDiscoveryIq::Identity& identity : identities) { - qDebug() << " " << identity.name() << identity.category() << identity.type(); + qDebug() << "Received info for account" << name << "about" << from; + QString node = info.queryNode(); + if (!node.isEmpty()) { + QStringList feats = info.features(); + std::list identities; + std::set features(feats.begin(), feats.end()); + QList idents = info.identities(); + for (const QXmppDiscoveryIq::Identity& ident : idents) { + identities.emplace_back(); + Shared::Identity& identity = identities.back(); + identity.category = ident.category(); + identity.language = ident.language(); + identity.name = ident.name(); + identity.type = ident.type(); + + qDebug() << " " << identity.name << identity.category << identity.type; + } + for (const QString& feat : features) { + qDebug() << " " << feat; + } + emit infoDiscovered(from, node, identities, features); } } } diff --git a/core/account.h b/core/account.h index 2c9ec70..5651ad9 100644 --- a/core/account.h +++ b/core/account.h @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -42,10 +43,11 @@ #include #include -#include "shared/shared.h" +#include +#include #include "contact.h" #include "conference.h" -#include "networkaccess.h" +#include #include "handlers/messagehandler.h" #include "handlers/rosterhandler.h" @@ -118,6 +120,8 @@ public: void resendMessage(const QString& jid, const QString& id); void replaceMessage(const QString& originalId, const Shared::Message& data); void invalidatePassword(); + + void discoverInfo(const QString& address, const QString& node); public slots: void connect(); @@ -151,6 +155,7 @@ signals: void uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap headers); void uploadFileError(const QString& jid, const QString& messageId, const QString& error); void needPassword(); + void infoDiscovered(const QString& address, const QString& node, const std::list& identities, const std::set& features); private: QString name; diff --git a/core/components/CMakeLists.txt b/core/components/CMakeLists.txt new file mode 100644 index 0000000..5faf837 --- /dev/null +++ b/core/components/CMakeLists.txt @@ -0,0 +1,6 @@ +target_sources(squawk PRIVATE + networkaccess.cpp + networkaccess.h + clientcache.cpp + clientcache.h +) diff --git a/core/components/clientcache.cpp b/core/components/clientcache.cpp new file mode 100644 index 0000000..0259f9c --- /dev/null +++ b/core/components/clientcache.cpp @@ -0,0 +1,55 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "clientcache.h" + +Core::ClientCache::ClientCache(): + requested(), + cache("clients") +{ + cache.open(); +} + +Core::ClientCache::~ClientCache() { + cache.close(); +} + +void Core::ClientCache::open() { + cache.open();} + +void Core::ClientCache::close() { + cache.close();} + + +bool Core::ClientCache::checkClient(const QString& node, const QString& ver, const QString& hash) { + QString id = node + "/" + ver; + if (requested.count(id) == 0 && !cache.checkRecord(id)) { + Shared::ClientInfo& info = requested.insert(std::make_pair(id, Shared::ClientInfo())).first->second; + info.capabilitiesNode = node; + info.capabilitiesVerification = ver; + info.capabilitiesHash = hash; + emit requestClientInfo(id); + return false; + } + + return true; +} + + +bool Core::ClientCache::registerClientInfo(const QString& sourceFullJid, const QString& id, const Shared::Identity& identity, const std::set& features) { + +} diff --git a/core/components/clientcache.h b/core/components/clientcache.h new file mode 100644 index 0000000..78c7e84 --- /dev/null +++ b/core/components/clientcache.h @@ -0,0 +1,56 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 CORE_CLIENTCACHE_H +#define CORE_CLIENTCACHE_H + +#include +#include + +#include +#include + +#include +#include +#include + +namespace Core { + +class ClientCache : public QObject { + Q_OBJECT +public: + ClientCache(); + ~ClientCache(); + + void open(); + void close(); + +signals: + void requestClientInfo(const QString& id); + +public slots: + bool checkClient(const QString& node, const QString& ver, const QString& hash); + bool registerClientInfo(const QString& sourceFullJid, const QString& id, const Shared::Identity& identity, const std::set& features); + +private: + std::map requested; + Cache cache; +}; + +} + + +#endif // CORE_CLIENTCACHE_H diff --git a/core/networkaccess.cpp b/core/components/networkaccess.cpp similarity index 100% rename from core/networkaccess.cpp rename to core/components/networkaccess.cpp diff --git a/core/networkaccess.h b/core/components/networkaccess.h similarity index 98% rename from core/networkaccess.h rename to core/components/networkaccess.h index 6ddfa99..c94c22a 100644 --- a/core/networkaccess.h +++ b/core/components/networkaccess.h @@ -30,8 +30,8 @@ #include -#include "storage/urlstorage.h" -#include "shared/pathcheck.h" +#include +#include namespace Core { diff --git a/core/squawk.cpp b/core/squawk.cpp index 0f8fe9f..5edcf58 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -28,9 +28,10 @@ Core::Squawk::Squawk(QObject* parent): amap(), state(Shared::Availability::offline), network(), - isInitialized(false) + isInitialized(false), + clientCache(), #ifdef WITH_KWALLET - ,kwallet() + kwallet() #endif { connect(&network, &NetworkAccess::loadFileProgress, this, &Squawk::fileProgress); @@ -67,6 +68,7 @@ void Core::Squawk::stop() { qDebug("Stopping squawk core.."); network.stop(); + clientCache.close(); if (isInitialized) { QSettings settings; @@ -115,6 +117,7 @@ void Core::Squawk::start() readSettings(); isInitialized = true; network.start(); + clientCache.open(); } void Core::Squawk::newAccountRequest(const QMap& map) @@ -180,6 +183,8 @@ void Core::Squawk::addAccount( connect(acc, &Account::receivedVCard, this, &Squawk::responseVCard); connect(acc, &Account::uploadFileError, this, &Squawk::onAccountUploadFileError); + + connect(acc, &Account::infoDiscovered, this, &Squawk::onAccountInfoDiscovered); QMap map = { {"login", login}, @@ -314,6 +319,27 @@ void Core::Squawk::onAccountAddPresence(const QString& jid, const QString& name, { Account* acc = static_cast(sender()); emit addPresence(acc->getName(), jid, name, data); + + QString node = data["capabilityNode"].toString(); + QString ver = data["capabilityVer"].toString(); + QString hash = data["capabilityHash"].toString(); + if (!clientCache.checkClient(node, ver, hash)) { + acc->discoverInfo(jid + "/" + name, node + "/" + ver); + } +} + +void Core::Squawk::onAccountInfoDiscovered( + const QString& address, + const QString& node, + const std::list& identities, + const std::set& features) +{ + Account* acc = static_cast(sender()); + + if (identities.size() != 1 || clientCache.registerClientInfo(address, node, identities.back(), features)) { + qDebug() << "Account" << acc->getName() << "received an ill-formed client discovery response from" << address << "about" << node; + return; + } } void Core::Squawk::onAccountRemovePresence(const QString& jid, const QString& name) diff --git a/core/squawk.h b/core/squawk.h index 3b8073b..ea17cdf 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -32,9 +32,10 @@ #include "shared/message.h" #include "shared/global.h" #include "shared/clientinfo.h" -#include "networkaccess.h" #include "external/simpleCrypt/simplecrypt.h" -#include + +#include +#include #ifdef WITH_KWALLET #include "passwordStorageEngines/kwallet.h" @@ -137,7 +138,7 @@ private: Shared::Availability state; NetworkAccess network; bool isInitialized; - //Cache clientCache; + ClientCache clientCache; #ifdef WITH_KWALLET PSE::KWallet kwallet; @@ -181,6 +182,8 @@ private slots: void onWalletOpened(bool success); void onWalletRejectPassword(const QString& login); + + void onAccountInfoDiscovered(const QString& address, const QString& node, const std::list& identities, const std::set& features); private: void readSettings(); diff --git a/core/storage/CMakeLists.txt b/core/storage/CMakeLists.txt index 5d34b7c..2da3c67 100644 --- a/core/storage/CMakeLists.txt +++ b/core/storage/CMakeLists.txt @@ -1,10 +1,10 @@ target_sources(squawk PRIVATE archive.cpp archive.h - storage.cpp + storage.hpp storage.h urlstorage.cpp urlstorage.h - cache.cpp + cache.hpp cache.h ) diff --git a/core/storage/cache.h b/core/storage/cache.h index e5b1b88..5091561 100644 --- a/core/storage/cache.h +++ b/core/storage/cache.h @@ -18,6 +18,7 @@ #define CORE_CACHE_H #include +#include #include @@ -39,12 +40,16 @@ public: void changeRecord(const QString& key, const T& value); void removeRecord(const QString& key); T getRecord(const QString& key) const; + bool checkRecord(const QString& key) const; private: Core::Storage storage; std::map* cache; + std::set* abscent; }; } +#include "cache.hpp" + #endif // CORE_CACHE_H diff --git a/core/storage/cache.cpp b/core/storage/cache.hpp similarity index 60% rename from core/storage/cache.cpp rename to core/storage/cache.hpp index 71fe369..1604d66 100644 --- a/core/storage/cache.cpp +++ b/core/storage/cache.hpp @@ -14,21 +14,21 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#ifndef CORE_CACHE_HPP +#define CORE_CACHE_HPP #include "cache.h" -#include - -template class Core::Cache; - template Core::Cache::Cache(const QString& name): storage(name), - cache(new std::map ()) {} + cache(new std::map ()), + abscent(new std::set ()) {} template Core::Cache::~Cache() { close(); delete cache; + delete abscent; } template @@ -43,27 +43,60 @@ template void Core::Cache::addRecord(const QString& key, const T& value) { storage.addRecord(key, value); cache->insert(std::make_pair(key, value)); + abscent->erase(key); } template T Core::Cache::getRecord(const QString& key) const { typename std::map::const_iterator itr = cache->find(key); if (itr == cache->end()) { - T value = storage.getRecord(key); - itr = cache->insert(std::make_pair(key, value)).first; + if (abscent->count(key) > 0) { + throw Archive::NotFound(key, storage.getName().toStdString()); + } + + try { + T value = storage.getRecord(key); + itr = cache->insert(std::make_pair(key, value)).first; + } catch (const Archive::NotFound& error) { + abscent->insert(key); + throw error; + } } return itr->second; } +template +bool Core::Cache::checkRecord(const QString& key) const { + typename std::map::const_iterator itr = cache->find(key); + if (itr != cache->end()) + return true; + + if (abscent->count(key) > 0) + return false; + + try { + T value = storage.getRecord(key); + itr = cache->insert(std::make_pair(key, value)).first; + } catch (const Archive::NotFound& error) { + return false; + } + + return true; +} + template void Core::Cache::changeRecord(const QString& key, const T& value) { - storage.changeRecord(key, value); + storage.changeRecord(key, value); //there is a non straightforward behaviour: if there was no element at the sorage it will be added cache->at(key) = value; + abscent->erase(key); //so... this line here is to make it coherent with the storage } template void Core::Cache::removeRecord(const QString& key) { storage.removeRecord(key); cache->erase(key); + abscent->insert(key); } + +#endif //CORE_CACHE_HPP diff --git a/core/storage/storage.h b/core/storage/storage.h index 5942c0f..5d6dabc 100644 --- a/core/storage/storage.h +++ b/core/storage/storage.h @@ -43,6 +43,7 @@ public: void changeRecord(const QString& key, const T& value); void removeRecord(const QString& key); T getRecord(const QString& key) const; + QString getName() const; private: @@ -54,4 +55,6 @@ private: } +#include "storage.hpp" + #endif // CORE_STORAGE_H diff --git a/core/storage/storage.cpp b/core/storage/storage.hpp similarity index 97% rename from core/storage/storage.cpp rename to core/storage/storage.hpp index 99db8af..48e334d 100644 --- a/core/storage/storage.cpp +++ b/core/storage/storage.hpp @@ -15,16 +15,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#ifndef CORE_STORAGE_HPP +#define CORE_STORAGE_HPP #include #include #include "storage.h" -#include - -template class Core::Storage; - template Core::Storage::Storage(const QString& p_name): name(p_name), @@ -202,3 +200,9 @@ void Core::Storage::removeRecord(const QString& key) mdb_txn_commit(txn); } } + +template +QString Core::Storage::getName() const { + return name;} + +#endif //CORE_STORAGE_HPP diff --git a/main/main.cpp b/main/main.cpp index 3368e0a..086dbc0 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -19,6 +19,7 @@ #include "shared/global.h" #include "shared/messageinfo.h" #include "shared/pathcheck.h" +#include "shared/identity.h" #include "main/application.h" #include "core/signalcatcher.h" #include "core/squawk.h" @@ -39,6 +40,9 @@ int main(int argc, char *argv[]) qRegisterMetaType("Shared::VCard"); qRegisterMetaType>("std::list"); qRegisterMetaType>("std::list"); + qRegisterMetaType>("std::list"); + qRegisterMetaType>("std::set"); + qRegisterMetaType>("std::list"); qRegisterMetaType>("QSet"); qRegisterMetaType("Shared::ConnectionState"); qRegisterMetaType("Shared::Availability"); diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index 1e47618..5828bd4 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -20,4 +20,5 @@ target_sources(squawk PRIVATE pathcheck.h clientinfo.h clientinfo.cpp + identity.h ) diff --git a/shared/clientinfo.cpp b/shared/clientinfo.cpp index 745bd54..3a2f441 100644 --- a/shared/clientinfo.cpp +++ b/shared/clientinfo.cpp @@ -23,8 +23,14 @@ Shared::ClientInfo::ClientInfo(): capabilitiesNode(), capabilitiesVerification(), capabilitiesHash(), + specificPresence(), capabilitiesExtensions() {} +QString Shared::ClientInfo::getId() const { + return capabilitiesNode + "/" + capabilitiesVerification; +} + + QDataStream & Shared::ClientInfo::operator >> (QDataStream& stream) const { stream << name; stream << category; diff --git a/shared/clientinfo.h b/shared/clientinfo.h index 742e49a..55dd29f 100644 --- a/shared/clientinfo.h +++ b/shared/clientinfo.h @@ -29,6 +29,8 @@ class ClientInfo public: ClientInfo(); + QString getId() const; + QDataStream& operator << (QDataStream& stream); QDataStream& operator >> (QDataStream& stream) const; @@ -39,6 +41,7 @@ public: QString capabilitiesNode; QString capabilitiesVerification; QString capabilitiesHash; + QString specificPresence; std::set capabilitiesExtensions; }; diff --git a/shared/identity.h b/shared/identity.h new file mode 100644 index 0000000..6041e1a --- /dev/null +++ b/shared/identity.h @@ -0,0 +1,35 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 SHARED_IDENTITY_H +#define SHARED_IDENTITY_H + +#include + +namespace Shared { + +struct Identity { + QString category; + QString language; + QString name; + QString type; +}; + +} + +#endif //SHARED_IDENTITY_H From c50cd1140e211d9864b1295b4115b43bf93d2e2a Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 25 Aug 2022 01:41:06 +0300 Subject: [PATCH 066/137] first ever received and cached client data! --- core/account.cpp | 16 +++- core/account.h | 2 +- core/components/clientcache.cpp | 37 +++++++-- core/components/clientcache.h | 3 +- core/squawk.cpp | 18 ++-- core/squawk.h | 2 +- shared/CMakeLists.txt | 1 + shared/clientinfo.cpp | 92 ++++++++++++++------- shared/clientinfo.h | 23 ++++-- shared/identity.cpp | 142 ++++++++++++++++++++++++++++++++ shared/identity.h | 21 ++++- 11 files changed, 297 insertions(+), 60 deletions(-) create mode 100644 shared/identity.cpp diff --git a/core/account.cpp b/core/account.cpp index 08321d3..48c48f9 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -634,16 +634,16 @@ void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) QString node = info.queryNode(); if (!node.isEmpty()) { QStringList feats = info.features(); - std::list identities; + std::set identities; std::set features(feats.begin(), feats.end()); QList idents = info.identities(); for (const QXmppDiscoveryIq::Identity& ident : idents) { - identities.emplace_back(); - Shared::Identity& identity = identities.back(); + Shared::Identity identity; identity.category = ident.category(); identity.language = ident.language(); identity.name = ident.name(); identity.type = ident.type(); + identities.insert(identity); qDebug() << " " << identity.name << identity.category << identity.type; } @@ -655,6 +655,16 @@ void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) } } +void Core::Account::discoverInfo(const QString& address, const QString& node) { + if (state == Shared::ConnectionState::connected) { + dm->requestInfo(address, node); + } else { + qDebug() << "An attempt to send a discover info by account" << name << + "sending request to" << address << "about node" << node << + "but the account is not in the connected state, skipping"; + } +} + void Core::Account::handleDisconnection() { cm->setCarbonsEnabled(false); diff --git a/core/account.h b/core/account.h index 5651ad9..a409490 100644 --- a/core/account.h +++ b/core/account.h @@ -155,7 +155,7 @@ signals: void uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap headers); void uploadFileError(const QString& jid, const QString& messageId, const QString& error); void needPassword(); - void infoDiscovered(const QString& address, const QString& node, const std::list& identities, const std::set& features); + void infoDiscovered(const QString& address, const QString& node, const std::set& identities, const std::set& features); private: QString name; diff --git a/core/components/clientcache.cpp b/core/components/clientcache.cpp index 0259f9c..a054428 100644 --- a/core/components/clientcache.cpp +++ b/core/components/clientcache.cpp @@ -14,12 +14,14 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . - #include "clientcache.h" +#include + Core::ClientCache::ClientCache(): requested(), - cache("clients") + cache("clients"), + specific() { cache.open(); } @@ -39,9 +41,9 @@ bool Core::ClientCache::checkClient(const QString& node, const QString& ver, con QString id = node + "/" + ver; if (requested.count(id) == 0 && !cache.checkRecord(id)) { Shared::ClientInfo& info = requested.insert(std::make_pair(id, Shared::ClientInfo())).first->second; - info.capabilitiesNode = node; - info.capabilitiesVerification = ver; - info.capabilitiesHash = hash; + info.node = node; + info.verification = ver; + info.hash = hash; emit requestClientInfo(id); return false; } @@ -49,7 +51,28 @@ bool Core::ClientCache::checkClient(const QString& node, const QString& ver, con return true; } +bool Core::ClientCache::registerClientInfo ( + const QString& sourceFullJid, + const QString& id, + const std::set& identities, + const std::set& features) +{ + std::map::iterator itr = requested.find(id); + if (itr != requested.end()) { + Shared::ClientInfo& info = itr->second; + info.identities = identities; + info.extensions = features; -bool Core::ClientCache::registerClientInfo(const QString& sourceFullJid, const QString& id, const Shared::Identity& identity, const std::set& features) { - + bool valid = info.valid(); + if (valid) { + cache.addRecord(id, info); + } else { + info.specificPresence = sourceFullJid; + specific.insert(std::make_pair(sourceFullJid, info)); + } + requested.erase(id); + return valid; + } else { + return false; + } } diff --git a/core/components/clientcache.h b/core/components/clientcache.h index 78c7e84..b2ff70d 100644 --- a/core/components/clientcache.h +++ b/core/components/clientcache.h @@ -43,11 +43,12 @@ signals: public slots: bool checkClient(const QString& node, const QString& ver, const QString& hash); - bool registerClientInfo(const QString& sourceFullJid, const QString& id, const Shared::Identity& identity, const std::set& features); + bool registerClientInfo(const QString& sourceFullJid, const QString& id, const std::set& identities, const std::set& features); private: std::map requested; Cache cache; + std::map specific; }; } diff --git a/core/squawk.cpp b/core/squawk.cpp index 5edcf58..3b976b0 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -320,25 +320,27 @@ void Core::Squawk::onAccountAddPresence(const QString& jid, const QString& name, Account* acc = static_cast(sender()); emit addPresence(acc->getName(), jid, name, data); - QString node = data["capabilityNode"].toString(); - QString ver = data["capabilityVer"].toString(); - QString hash = data["capabilityHash"].toString(); - if (!clientCache.checkClient(node, ver, hash)) { - acc->discoverInfo(jid + "/" + name, node + "/" + ver); + //it's equal if a MUC sends its status with presence of the same jid (ex: muc@srv.im/muc@srv.im), it's not a client, so, no need to request + if (jid != name) { + QString node = data["capabilityNode"].toString(); + QString ver = data["capabilityVer"].toString(); + QString hash = data["capabilityHash"].toString(); + if (!clientCache.checkClient(node, ver, hash)) { + acc->discoverInfo(jid + "/" + name, node + "/" + ver); + } } } void Core::Squawk::onAccountInfoDiscovered( const QString& address, const QString& node, - const std::list& identities, + const std::set& identities, const std::set& features) { Account* acc = static_cast(sender()); - if (identities.size() != 1 || clientCache.registerClientInfo(address, node, identities.back(), features)) { + if (!clientCache.registerClientInfo(address, node, identities, features)) { qDebug() << "Account" << acc->getName() << "received an ill-formed client discovery response from" << address << "about" << node; - return; } } diff --git a/core/squawk.h b/core/squawk.h index ea17cdf..cdaaf6e 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -183,7 +183,7 @@ private slots: void onWalletOpened(bool success); void onWalletRejectPassword(const QString& login); - void onAccountInfoDiscovered(const QString& address, const QString& node, const std::list& identities, const std::set& features); + void onAccountInfoDiscovered(const QString& address, const QString& node, const std::set& identities, const std::set& features); private: void readSettings(); diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index 5828bd4..7495d4e 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -21,4 +21,5 @@ target_sources(squawk PRIVATE clientinfo.h clientinfo.cpp identity.h + identity.cpp ) diff --git a/shared/clientinfo.cpp b/shared/clientinfo.cpp index 3a2f441..86af1c8 100644 --- a/shared/clientinfo.cpp +++ b/shared/clientinfo.cpp @@ -16,30 +16,42 @@ #include "clientinfo.h" +const std::map Shared::ClientInfo::hashes = { + //md2 is missing + {"md5", QCryptographicHash::Md5}, + {"sha-1", QCryptographicHash::Sha1}, + {"sha-224", QCryptographicHash::Sha224}, + {"sha-256", QCryptographicHash::Sha256}, + {"sha-384", QCryptographicHash::Sha384}, + {"sha-512", QCryptographicHash::Sha512}, + //shake128 is missing + //shake256 is missing + +}; + Shared::ClientInfo::ClientInfo(): - name(), - category(), - type(), - capabilitiesNode(), - capabilitiesVerification(), - capabilitiesHash(), - specificPresence(), - capabilitiesExtensions() {} + identities(), + extensions(), + node(), + verification(), + hash(), + specificPresence() {} QString Shared::ClientInfo::getId() const { - return capabilitiesNode + "/" + capabilitiesVerification; + return node + "/" + verification; } QDataStream & Shared::ClientInfo::operator >> (QDataStream& stream) const { - stream << name; - stream << category; - stream << type; - stream << capabilitiesNode; - stream << capabilitiesVerification; - stream << capabilitiesHash; - stream << (quint8)capabilitiesExtensions.size(); - for (const QString& ext : capabilitiesExtensions) { + stream << node; + stream << verification; + stream << hash; + stream << (quint8)identities.size(); + for (const Shared::Identity& identity : identities) { + stream << identity; + } + stream << (quint8)extensions.size(); + for (const QString& ext : extensions) { stream << ext; } @@ -47,29 +59,53 @@ QDataStream & Shared::ClientInfo::operator >> (QDataStream& stream) const { } QDataStream & Shared::ClientInfo::operator << (QDataStream& stream) { - stream >> name; - stream >> category; - stream >> type; - stream >> capabilitiesNode; - stream >> capabilitiesVerification; - stream >> capabilitiesHash; + stream >> node; + stream >> verification; + stream >> hash; quint8 size; + stream >> size; + for (quint8 i = 0; i < size; ++i) { + Shared::Identity identity; + stream >> identity; + identities.insert(identity); + } + stream >> size; for (quint8 i = 0; i < size; ++i) { QString ext; stream >> ext; - capabilitiesExtensions.insert(ext); + extensions.insert(ext); } return stream; } -QDataStream& operator<< (QDataStream& stream, const Shared::ClientInfo& image) { - image >> stream; +bool Shared::ClientInfo::valid() const { + std::map::const_iterator itr = hashes.find(hash); + if (itr == hashes.end()) { + return false; + } + + QCryptographicHash calc(itr->second); + QString validationString = ""; + for (const Identity& identity : identities) { + calc.addData((identity.category + "/" + identity.type + "/" + identity.language + "/" + identity.name + "<").toUtf8()); + } + for (const QString& ext : extensions) { + calc.addData((ext + "<").toUtf8()); + } + + QString result = calc.result().toBase64(); + + return result == verification; +} + +QDataStream& operator << (QDataStream& stream, const Shared::ClientInfo& info) { + info >> stream; return stream; } -QDataStream& operator>> (QDataStream& stream, Shared::ClientInfo& image) { - image << stream; +QDataStream& operator >> (QDataStream& stream, Shared::ClientInfo& info) { + info << stream; return stream; } diff --git a/shared/clientinfo.h b/shared/clientinfo.h index 55dd29f..53c7dd0 100644 --- a/shared/clientinfo.h +++ b/shared/clientinfo.h @@ -21,6 +21,9 @@ #include #include +#include + +#include namespace Shared { @@ -30,24 +33,26 @@ public: ClientInfo(); QString getId() const; + bool valid() const; QDataStream& operator << (QDataStream& stream); QDataStream& operator >> (QDataStream& stream) const; public: - QString name; - QString category; - QString type; - QString capabilitiesNode; - QString capabilitiesVerification; - QString capabilitiesHash; + std::set identities; + std::set extensions; + QString node; + QString verification; + QString hash; QString specificPresence; - std::set capabilitiesExtensions; + +private: + static const std::map hashes; }; } -QDataStream& operator<< (QDataStream& stream, const Shared::ClientInfo& image); -QDataStream& operator>> (QDataStream& stream, Shared::ClientInfo& image); +QDataStream& operator << (QDataStream& stream, const Shared::ClientInfo& info); +QDataStream& operator >> (QDataStream& stream, Shared::ClientInfo& info); #endif // SHARED_CLIENTINFO_H diff --git a/shared/identity.cpp b/shared/identity.cpp new file mode 100644 index 0000000..a0a4f0a --- /dev/null +++ b/shared/identity.cpp @@ -0,0 +1,142 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "identity.h" + +Shared::Identity::Identity(): + category(), + type(), + language(), + name() {} + +bool Shared::Identity::operator==(const Shared::Identity& other) const { + return category == other.category && type == other.type && language == other.language && name == other.name; +} + +bool Shared::Identity::operator!=(const Shared::Identity& other) const { + return category != other.category || type != other.type || language != other.language || name != other.name; +} + +bool Shared::Identity::operator > (const Shared::Identity& other) const { + if (category > other.category) { + return true; + } else if (category < other.category) { + return false; + } else if (type > other.type) { + return true; + } else if (type < other.type) { + return false; + } else if (language > other.language) { + return true; + } else if (language < other.language) { + return false; + } else if (name > other.name) { + return true; + } else { + return false; + } +} + +bool Shared::Identity::operator < (const Shared::Identity& other) const { + if (category < other.category) { + return true; + } else if (category > other.category) { + return false; + } else if (type < other.type) { + return true; + } else if (type > other.type) { + return false; + } else if (language < other.language) { + return true; + } else if (language > other.language) { + return false; + } else if (name < other.name) { + return true; + } else { + return false; + } +} + +bool Shared::Identity::operator >= (const Shared::Identity& other) const { + if (category > other.category) { + return true; + } else if (category < other.category) { + return false; + } else if (type > other.type) { + return true; + } else if (type < other.type) { + return false; + } else if (language > other.language) { + return true; + } else if (language < other.language) { + return false; + } else if (name > other.name) { + return true; + } else if (name < other.name) { + return false; + } else { + return true; + } +} + +bool Shared::Identity::operator <= (const Shared::Identity& other) const { + if (category < other.category) { + return true; + } else if (category > other.category) { + return false; + } else if (type < other.type) { + return true; + } else if (type > other.type) { + return false; + } else if (language < other.language) { + return true; + } else if (language > other.language) { + return false; + } else if (name < other.name) { + return true; + } else if (name > other.name) { + return false; + } else { + return true; + } +} + +QDataStream & Shared::Identity::operator >> (QDataStream& stream) const { + stream << category; + stream << type; + stream << language; + stream << name; +} + +QDataStream & Shared::Identity::operator << (QDataStream& stream) { + stream >> category; + stream >> type; + stream >> language; + stream >> name; +} + +QDataStream & operator >> (QDataStream& stream, Shared::Identity& identity) { + identity << stream; + return stream; +} + +QDataStream & operator << (QDataStream& stream, const Shared::Identity& identity) { + identity >> stream; + return stream; +} + diff --git a/shared/identity.h b/shared/identity.h index 6041e1a..c19102b 100644 --- a/shared/identity.h +++ b/shared/identity.h @@ -19,17 +19,34 @@ #ifndef SHARED_IDENTITY_H #define SHARED_IDENTITY_H +#include #include namespace Shared { -struct Identity { +class Identity { +public: + Identity(); + + QDataStream& operator << (QDataStream& stream); + QDataStream& operator >> (QDataStream& stream) const; + + bool operator < (const Identity& other) const; + bool operator > (const Identity& other) const; + bool operator >= (const Identity& other) const; + bool operator <= (const Identity& other) const; + bool operator == (const Identity& other) const; + bool operator != (const Identity& other) const; +public: QString category; + QString type; QString language; QString name; - QString type; }; } +QDataStream& operator << (QDataStream& stream, const Shared::Identity& identity); +QDataStream& operator >> (QDataStream& stream, Shared::Identity& identity); + #endif //SHARED_IDENTITY_H From 7b2b7ee5d5ff55d8a7dd5fb05995e61337a237e7 Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 26 Aug 2022 01:49:49 +0300 Subject: [PATCH 067/137] first thought about forms, discovering contact pep support --- core/account.cpp | 23 +++++++++++-- core/contact.cpp | 15 +++++++- core/contact.h | 7 ++++ core/handlers/rosterhandler.cpp | 18 ++++++++-- core/handlers/rosterhandler.h | 5 +-- shared/CMakeLists.txt | 4 +++ shared/enums.h | 7 ++++ shared/field.cpp | 28 +++++++++++++++ shared/field.h | 61 +++++++++++++++++++++++++++++++++ shared/form.cpp | 23 +++++++++++++ shared/form.h | 48 ++++++++++++++++++++++++++ 11 files changed, 230 insertions(+), 9 deletions(-) create mode 100644 shared/field.cpp create mode 100644 shared/field.h create mode 100644 shared/form.cpp create mode 100644 shared/form.h diff --git a/core/account.cpp b/core/account.cpp index 48c48f9..c3203b4 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -623,12 +623,13 @@ void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) bool pepSupported = false; for (const QXmppDiscoveryIq::Identity& identity : identities) { QString type = identity.type(); - qDebug() << " " << identity.category() << type; - if (type == "pep") { + QString category = identity.category(); + qDebug() << " " << category << type; + if (type == "pep" && category == "pubsub") { pepSupported = true; } } - rh->setPepSupport(pepSupported); + rh->setPepSupport(pepSupported ? Shared::Support::supported : Shared::Support::unsupported); } else { qDebug() << "Received info for account" << name << "about" << from; QString node = info.queryNode(); @@ -651,6 +652,22 @@ void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) qDebug() << " " << feat; } emit infoDiscovered(from, node, identities, features); + } else { + Contact* cont = rh->getContact(from); + if (cont != nullptr) { + qDebug() << "Received info for account" << name << "about" << from; + QList identities = info.identities(); + bool pepSupported = false; + for (const QXmppDiscoveryIq::Identity& identity : identities) { + QString type = identity.type(); + QString category = identity.category(); + qDebug() << " " << category << type; + if (type == "pep" && category == "pubsub") { + pepSupported = true; + } + } + cont->setPepSupport(pepSupported ? Shared::Support::supported : Shared::Support::unsupported); + } } } } diff --git a/core/contact.cpp b/core/contact.cpp index 0471a5c..3030f4d 100644 --- a/core/contact.cpp +++ b/core/contact.cpp @@ -22,7 +22,8 @@ Core::Contact::Contact(const QString& pJid, const QString& account, QObject* parent): RosterItem(pJid, account, parent), groups(), - subscriptionState(Shared::SubscriptionState::unknown) + subscriptionState(Shared::SubscriptionState::unknown), + pep(Shared::Support::unknown) { } @@ -98,3 +99,15 @@ void Core::Contact::handlePresence(const QXmppPresence& pres) } } } + +void Core::Contact::setPepSupport(Shared::Support support) { + if (pep != support) { + pep = support; + } +} + +Shared::Support Core::Contact::getPepSupport() const { + return pep;} + + + diff --git a/core/contact.h b/core/contact.h index aa81010..01c082f 100644 --- a/core/contact.h +++ b/core/contact.h @@ -21,8 +21,11 @@ #include #include + #include "rosteritem.h" +#include + namespace Core { class Contact : public RosterItem @@ -38,6 +41,9 @@ public: void setSubscriptionState(Shared::SubscriptionState state); Shared::SubscriptionState getSubscriptionState() const; + void setPepSupport(Shared::Support support); + Shared::Support getPepSupport() const; + void handlePresence(const QXmppPresence & pres) override; signals: @@ -48,6 +54,7 @@ signals: private: QSet groups; Shared::SubscriptionState subscriptionState; + Shared::Support pep; }; } diff --git a/core/handlers/rosterhandler.cpp b/core/handlers/rosterhandler.cpp index 6a233d6..748f0f7 100644 --- a/core/handlers/rosterhandler.cpp +++ b/core/handlers/rosterhandler.cpp @@ -27,7 +27,7 @@ Core::RosterHandler::RosterHandler(Core::Account* account): groups(), queuedContacts(), outOfRosterContacts(), - pepSupport(false) + pepSupport(Shared::Support::unknown) { connect(acc->rm, &QXmppRosterManager::rosterReceived, this, &RosterHandler::onRosterReceived); connect(acc->rm, &QXmppRosterManager::itemAdded, this, &RosterHandler::onRosterItemAdded); @@ -111,6 +111,10 @@ void Core::RosterHandler::addedAccount(const QString& jid) if (grCount == 0) { emit acc->addContact(jid, "", cData); } + if (pepSupport == Shared::Support::supported) { + acc->dm->requestInfo(jid); + //acc->dm->requestItems(jid); + } handleNewContact(contact); } } @@ -588,13 +592,21 @@ void Core::RosterHandler::handleOffline() pair.second->clearArchiveRequests(); pair.second->downgradeDatabaseState(); } - setPepSupport(false); + setPepSupport(Shared::Support::unknown); } -void Core::RosterHandler::setPepSupport(bool support) +void Core::RosterHandler::setPepSupport(Shared::Support support) { if (pepSupport != support) { pepSupport = support; + + if (pepSupport == Shared::Support::supported) { + for (const std::pair& pair : contacts) { + if (pair.second->getPepSupport() == Shared::Support::unknown) { + acc->dm->requestInfo(pair.first); + } + } + } } } diff --git a/core/handlers/rosterhandler.h b/core/handlers/rosterhandler.h index 02bbc98..7be38e1 100644 --- a/core/handlers/rosterhandler.h +++ b/core/handlers/rosterhandler.h @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -64,7 +65,7 @@ public: void storeConferences(); void clearConferences(); - void setPepSupport(bool support); + void setPepSupport(Shared::Support support); private slots: void onRosterReceived(); @@ -108,7 +109,7 @@ private: std::map> groups; std::map queuedContacts; std::set outOfRosterContacts; - bool pepSupport; + Shared::Support pepSupport; }; } diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index 7495d4e..51c599f 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -22,4 +22,8 @@ target_sources(squawk PRIVATE clientinfo.cpp identity.h identity.cpp + form.h + form.cpp + field.h + field.cpp ) diff --git a/shared/enums.h b/shared/enums.h index cb41443..273a22d 100644 --- a/shared/enums.h +++ b/shared/enums.h @@ -117,5 +117,12 @@ Q_ENUM_NS(AccountPassword) static const AccountPassword AccountPasswordHighest = AccountPassword::kwallet; static const AccountPassword AccountPasswordLowest = AccountPassword::plain; +enum class Support { + unknown, + supported, + unsupported +}; +Q_ENUM_NS(Support) + } #endif // SHARED_ENUMS_H diff --git a/shared/field.cpp b/shared/field.cpp new file mode 100644 index 0000000..f469646 --- /dev/null +++ b/shared/field.cpp @@ -0,0 +1,28 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "field.h" + +Shared::Field::Field(Shared::Field::Type fieldTtype): + type(fieldTtype), + key(), + label(), + description(), + required(false), + options(), + value() +{ +} diff --git a/shared/field.h b/shared/field.h new file mode 100644 index 0000000..e7c9a02 --- /dev/null +++ b/shared/field.h @@ -0,0 +1,61 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 SHARED_FIELD_H +#define SHARED_FIELD_H + +#include + +#include +#include + +namespace Shared { + +/** + * @todo write docs + */ +class Field +{ +public: + enum class Type { + boolean, + fixed, + hidden, + jidMultiple, + jidSingle, + listMultiple, + listSingle, + textMultiple, + textPrivate, + textSingle + }; + + Field(Type fieldType); + +public: + const Type type; + QString key; + QString label; + QString description; + bool required; + std::list> options; + QVariant value; + +}; + +} + +#endif // SHARED_FIELD_H diff --git a/shared/form.cpp b/shared/form.cpp new file mode 100644 index 0000000..bf309ca --- /dev/null +++ b/shared/form.cpp @@ -0,0 +1,23 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "form.h" + +Shared::Form::Form(Shared::Form::Type formType): + type(formType), + title(), + instructions(), + fields() {} diff --git a/shared/form.h b/shared/form.h new file mode 100644 index 0000000..08c8c95 --- /dev/null +++ b/shared/form.h @@ -0,0 +1,48 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 SHARED_FORM_H +#define SHARED_FORM_H + +#include + +#include + +namespace Shared { + +class Form +{ +public: + enum class Type { + none, + form, + submit, + cancel, + result + }; + + Form(Type formType); + +public: + const Type type; + QString title; + QString instructions; + std::list fields; +}; + +} + +#endif // SHARED_FORM_H From b6ba022bffd435746087814a917afe63b875e7a6 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 27 Aug 2022 14:39:24 +0300 Subject: [PATCH 068/137] removed own VCard request at the start if the presence doesn't show that the avatar changed, little refactoring --- CHANGELOG.md | 1 + core/account.cpp | 121 +++----------------------- core/account.h | 9 +- core/handlers/CMakeLists.txt | 2 + core/handlers/discoveryhandler.cpp | 135 +++++++++++++++++++++++++++++ core/handlers/discoveryhandler.h | 45 ++++++++++ core/handlers/rosterhandler.cpp | 24 ++--- core/handlers/rosterhandler.h | 3 +- core/handlers/vcardhandler.cpp | 2 +- core/handlers/vcardhandler.h | 2 +- 10 files changed, 215 insertions(+), 129 deletions(-) create mode 100644 core/handlers/discoveryhandler.cpp create mode 100644 core/handlers/discoveryhandler.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 7107e26..a36da73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - 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 +- removed unnecessary own vcard request at sturtup (used to do it to discover my own avatar) ### New features - Now you can enable tray icon from settings! diff --git a/core/account.cpp b/core/account.cpp index c3203b4..1c3314f 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -18,6 +18,7 @@ #include "account.h" #include + #include using namespace Core; @@ -44,12 +45,13 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& network(p_net), passwordType(Shared::AccountPassword::plain), lastError(Error::none), - pepSupport(false), + pepSupport(Shared::Support::unknown), active(p_active), notReadyPassword(false), mh(new MessageHandler(this)), rh(new RosterHandler(this)), - vh(new VCardHandler(this)) + vh(new VCardHandler(this)), + dh(new DiscoveryHandler(this)) { config.setUser(p_login); config.setDomain(p_server); @@ -79,9 +81,6 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& QObject::connect(um, &QXmppUploadRequestManager::slotReceived, mh, &MessageHandler::onUploadSlotReceived); QObject::connect(um, &QXmppUploadRequestManager::requestFailed, mh, &MessageHandler::onUploadSlotRequestFailed); - QObject::connect(dm, &QXmppDiscoveryManager::itemsReceived, this, &Account::onDiscoveryItemsReceived); - QObject::connect(dm, &QXmppDiscoveryManager::infoReceived, this, &Account::onDiscoveryInfoReceived); - QObject::connect(network, &NetworkAccess::uploadFileComplete, mh, &MessageHandler::onUploadFileComplete); QObject::connect(network, &NetworkAccess::downloadFileComplete, mh, &MessageHandler::onDownloadFileComplete); QObject::connect(network, &NetworkAccess::loadFileError, mh, &MessageHandler::onLoadFileError); @@ -93,8 +92,6 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& QObject::connect(reconnectTimer, &QTimer::timeout, this, &Account::onReconnectTimer); if (name == "Test") { - qDebug() << "Presence capabilities: " << presence.capabilityNode(); - QXmppLogger* logger = new QXmppLogger(this); logger->setLoggingType(QXmppLogger::SignalLogging); client.setLogger(logger); @@ -263,9 +260,8 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) if (jid == getBareJid()) { if (resource == getResource()) { emit availabilityChanged(static_cast(p_presence.availableStatusType())); - } else { - vh->handleOtherPresenceOfMyAccountChange(p_presence); } + vh->handlePresenceOfMyAccountChange(p_presence); } else { RosterItem* item = rh->getRosterItem(jid); if (item != 0) { @@ -574,104 +570,6 @@ void Core::Account::setRoomJoined(const QString& jid, bool joined) conf->setJoined(joined); } -void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items) -{ - if (items.from() == getServer()) { - std::set needToRequest; - qDebug() << "Server items list received for account " << name << ":"; - for (QXmppDiscoveryIq::Item item : items.items()) { - QString jid = item.jid(); - if (jid != getServer()) { - qDebug() << " Node" << jid; - needToRequest.insert(jid); - } else { - qDebug() << " " << item.node().toStdString().c_str(); - } - } - - for (const QString& jid : needToRequest) { - dm->requestInfo(jid); - } - } -} - -void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) -{ - QString from = info.from(); - if (from == getServer()) { - bool enableCC = false; - qDebug() << "Server info received for account" << name; - QStringList features = info.features(); - qDebug() << "List of supported features of the server " << getServer() << ":"; - for (const QString& feature : features) { - qDebug() << " " << feature.toStdString().c_str(); - if (feature == "urn:xmpp:carbons:2") { - enableCC = true; - } - } - - if (enableCC) { - qDebug() << "Enabling carbon copies for account" << name; - cm->setCarbonsEnabled(true); - } - - qDebug() << "Requesting account" << name << "capabilities"; - dm->requestInfo(getBareJid()); - } else if (from == getBareJid()) { - qDebug() << "Received capabilities for account" << name << ":"; - QList identities = info.identities(); - bool pepSupported = false; - for (const QXmppDiscoveryIq::Identity& identity : identities) { - QString type = identity.type(); - QString category = identity.category(); - qDebug() << " " << category << type; - if (type == "pep" && category == "pubsub") { - pepSupported = true; - } - } - rh->setPepSupport(pepSupported ? Shared::Support::supported : Shared::Support::unsupported); - } else { - qDebug() << "Received info for account" << name << "about" << from; - QString node = info.queryNode(); - if (!node.isEmpty()) { - QStringList feats = info.features(); - std::set identities; - std::set features(feats.begin(), feats.end()); - QList idents = info.identities(); - for (const QXmppDiscoveryIq::Identity& ident : idents) { - Shared::Identity identity; - identity.category = ident.category(); - identity.language = ident.language(); - identity.name = ident.name(); - identity.type = ident.type(); - identities.insert(identity); - - qDebug() << " " << identity.name << identity.category << identity.type; - } - for (const QString& feat : features) { - qDebug() << " " << feat; - } - emit infoDiscovered(from, node, identities, features); - } else { - Contact* cont = rh->getContact(from); - if (cont != nullptr) { - qDebug() << "Received info for account" << name << "about" << from; - QList identities = info.identities(); - bool pepSupported = false; - for (const QXmppDiscoveryIq::Identity& identity : identities) { - QString type = identity.type(); - QString category = identity.category(); - qDebug() << " " << category << type; - if (type == "pep" && category == "pubsub") { - pepSupported = true; - } - } - cont->setPepSupport(pepSupported ? Shared::Support::supported : Shared::Support::unsupported); - } - } - } -} - void Core::Account::discoverInfo(const QString& address, const QString& node) { if (state == Shared::ConnectionState::connected) { dm->requestInfo(address, node); @@ -682,8 +580,17 @@ void Core::Account::discoverInfo(const QString& address, const QString& node) { } } +void Core::Account::setPepSupport(Shared::Support support) +{ + if (support != pepSupport) { + pepSupport = support; + emit pepSupportChanged(pepSupport); + } +} + void Core::Account::handleDisconnection() { + setPepSupport(Shared::Support::unknown); cm->setCarbonsEnabled(false); rh->handleOffline(); vh->handleOffline(); diff --git a/core/account.h b/core/account.h index a409490..0ce39e5 100644 --- a/core/account.h +++ b/core/account.h @@ -52,6 +52,7 @@ #include "handlers/messagehandler.h" #include "handlers/rosterhandler.h" #include "handlers/vcardhandler.h" +#include "handlers/discoveryhandler.h" namespace Core { @@ -62,6 +63,7 @@ class Account : public QObject friend class MessageHandler; friend class RosterHandler; friend class VCardHandler; + friend class DiscoveryHandler; public: enum class Error { authentication, @@ -156,6 +158,7 @@ signals: void uploadFileError(const QString& jid, const QString& messageId, const QString& error); void needPassword(); void infoDiscovered(const QString& address, const QString& node, const std::set& identities, const std::set& features); + void pepSupportChanged(Shared::Support support); private: QString name; @@ -179,13 +182,14 @@ private: NetworkAccess* network; Shared::AccountPassword passwordType; Error lastError; - bool pepSupport; + Shared::Support pepSupport; bool active; bool notReadyPassword; MessageHandler* mh; RosterHandler* rh; VCardHandler* vh; + DiscoveryHandler* dh; private slots: void onClientStateChange(QXmppClient::State state); @@ -199,13 +203,12 @@ private slots: void onMamLog(QXmppLogger::MessageType type, const QString &msg); - void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items); - void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info); void onContactHistoryResponse(const std::list& list, bool last); private: void handleDisconnection(); void onReconnectTimer(); + void setPepSupport(Shared::Support support); }; } diff --git a/core/handlers/CMakeLists.txt b/core/handlers/CMakeLists.txt index fb67953..255d7fa 100644 --- a/core/handlers/CMakeLists.txt +++ b/core/handlers/CMakeLists.txt @@ -5,4 +5,6 @@ target_sources(squawk PRIVATE rosterhandler.h vcardhandler.cpp vcardhandler.h + discoveryhandler.cpp + discoveryhandler.h ) diff --git a/core/handlers/discoveryhandler.cpp b/core/handlers/discoveryhandler.cpp new file mode 100644 index 0000000..e562e68 --- /dev/null +++ b/core/handlers/discoveryhandler.cpp @@ -0,0 +1,135 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "discoveryhandler.h" +#include "core/account.h" + +#include + +Core::DiscoveryHandler::DiscoveryHandler(Core::Account* account): + QObject(), + acc(account) +{ + QObject::connect(acc->dm, &QXmppDiscoveryManager::itemsReceived, this, &DiscoveryHandler::onItemsReceived); + QObject::connect(acc->dm, &QXmppDiscoveryManager::infoReceived, this, &DiscoveryHandler::onInfoReceived); + + acc->dm->setClientType("pc"); + acc->dm->setClientCategory("client"); + acc->dm->setClientName(qApp->applicationDisplayName() + " " + qApp->applicationVersion()); + acc->dm->setClientCapabilitiesNode("https://git.macaw.me/blue/squawk"); +} + +void Core::DiscoveryHandler::onItemsReceived(const QXmppDiscoveryIq& items) +{ + QString server = acc->getServer(); + if (items.from() == server) { + std::set needToRequest; + qDebug() << "Server items list received for account " << acc->getName() << ":"; + for (QXmppDiscoveryIq::Item item : items.items()) { + QString jid = item.jid(); + if (jid != server) { + qDebug() << " Node" << jid; + needToRequest.insert(jid); + } else { + qDebug() << " " << item.node().toStdString().c_str(); + } + } + + for (const QString& jid : needToRequest) { + acc->dm->requestInfo(jid); + } + } +} + +void Core::DiscoveryHandler::onInfoReceived(const QXmppDiscoveryIq& info) +{ + QString from = info.from(); + QString server = acc->getServer(); + QString accName = acc->getName(); + QString bareJid = acc->getBareJid(); + if (from == server) { + bool enableCC = false; + qDebug() << "Server info received for account" << accName; + QStringList features = info.features(); + qDebug() << "List of supported features of the server " << server << ":"; + for (const QString& feature : features) { + qDebug() << " " << feature.toStdString().c_str(); + if (feature == "urn:xmpp:carbons:2") { + enableCC = true; + } + } + + if (enableCC) { + qDebug() << "Enabling carbon copies for account" << accName; + acc->cm->setCarbonsEnabled(true); + } + + qDebug() << "Requesting account" << accName << "capabilities"; + acc->dm->requestInfo(bareJid); + } else if (from == bareJid) { + qDebug() << "Received capabilities for account" << accName << ":"; + QList identities = info.identities(); + bool pepSupported = false; + for (const QXmppDiscoveryIq::Identity& identity : identities) { + QString type = identity.type(); + QString category = identity.category(); + qDebug() << " " << category << type; + if (type == "pep" && category == "pubsub") { + pepSupported = true; + } + } + acc->setPepSupport(pepSupported ? Shared::Support::supported : Shared::Support::unsupported); + } else { + qDebug() << "Received info for account" << accName << "about" << from; + QString node = info.queryNode(); + if (!node.isEmpty()) { + QStringList feats = info.features(); + std::set identities; + std::set features(feats.begin(), feats.end()); + QList idents = info.identities(); + for (const QXmppDiscoveryIq::Identity& ident : idents) { + Shared::Identity identity; + identity.category = ident.category(); + identity.language = ident.language(); + identity.name = ident.name(); + identity.type = ident.type(); + identities.insert(identity); + + qDebug() << " " << identity.name << identity.category << identity.type; + } + for (const QString& feat : features) { + qDebug() << " " << feat; + } + emit acc->infoDiscovered(from, node, identities, features); + } else { + Contact* cont = acc->rh->getContact(from); + if (cont != nullptr) { + qDebug() << "Received info for account" << accName << "about" << from; + QList identities = info.identities(); + bool pepSupported = false; + for (const QXmppDiscoveryIq::Identity& identity : identities) { + QString type = identity.type(); + QString category = identity.category(); + qDebug() << " " << category << type; + if (type == "pep" && category == "pubsub") { + pepSupported = true; + } + } + cont->setPepSupport(pepSupported ? Shared::Support::supported : Shared::Support::unsupported); + } + } + } +} diff --git a/core/handlers/discoveryhandler.h b/core/handlers/discoveryhandler.h new file mode 100644 index 0000000..3129219 --- /dev/null +++ b/core/handlers/discoveryhandler.h @@ -0,0 +1,45 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 CORE_DISCOVERYHANDLER_H +#define CORE_DISCOVERYHANDLER_H + +#include + +#include +#include + +namespace Core { +class Account; + +class DiscoveryHandler : public QObject +{ + Q_OBJECT +public: + DiscoveryHandler(Account* account); + ~DiscoveryHandler(); + +private slots: + void onItemsReceived (const QXmppDiscoveryIq& items); + void onInfoReceived (const QXmppDiscoveryIq& info); + +private: + Account* acc; +}; + +} + +#endif // CORE_DISCOVERYHANDLER_H diff --git a/core/handlers/rosterhandler.cpp b/core/handlers/rosterhandler.cpp index 748f0f7..1a61440 100644 --- a/core/handlers/rosterhandler.cpp +++ b/core/handlers/rosterhandler.cpp @@ -26,8 +26,7 @@ Core::RosterHandler::RosterHandler(Core::Account* account): conferences(), groups(), queuedContacts(), - outOfRosterContacts(), - pepSupport(Shared::Support::unknown) + outOfRosterContacts() { connect(acc->rm, &QXmppRosterManager::rosterReceived, this, &RosterHandler::onRosterReceived); connect(acc->rm, &QXmppRosterManager::itemAdded, this, &RosterHandler::onRosterItemAdded); @@ -37,6 +36,8 @@ Core::RosterHandler::RosterHandler(Core::Account* account): connect(acc->mm, &QXmppMucManager::roomAdded, this, &RosterHandler::onMucRoomAdded); connect(acc->bm, &QXmppBookmarkManager::bookmarksReceived, this, &RosterHandler::bookmarksReceived); + + connect(acc, &Account::pepSupportChanged, this, &RosterHandler::onPepSupportedChanged); } Core::RosterHandler::~RosterHandler() @@ -52,8 +53,6 @@ Core::RosterHandler::~RosterHandler() void Core::RosterHandler::onRosterReceived() { - acc->requestVCard(acc->getBareJid()); //TODO need to make sure server actually supports vCards - QStringList bj = acc->rm->getRosterBareJids(); for (int i = 0; i < bj.size(); ++i) { const QString& jid = bj[i]; @@ -111,7 +110,7 @@ void Core::RosterHandler::addedAccount(const QString& jid) if (grCount == 0) { emit acc->addContact(jid, "", cData); } - if (pepSupport == Shared::Support::supported) { + if (acc->pepSupport == Shared::Support::supported) { acc->dm->requestInfo(jid); //acc->dm->requestItems(jid); } @@ -592,20 +591,15 @@ void Core::RosterHandler::handleOffline() pair.second->clearArchiveRequests(); pair.second->downgradeDatabaseState(); } - setPepSupport(Shared::Support::unknown); } -void Core::RosterHandler::setPepSupport(Shared::Support support) +void Core::RosterHandler::onPepSupportedChanged(Shared::Support support) { - if (pepSupport != support) { - pepSupport = support; - - if (pepSupport == Shared::Support::supported) { - for (const std::pair& pair : contacts) { - if (pair.second->getPepSupport() == Shared::Support::unknown) { - acc->dm->requestInfo(pair.first); - } + if (support == Shared::Support::supported) { + for (const std::pair& pair : contacts) { + if (pair.second->getPepSupport() == Shared::Support::unknown) { + acc->dm->requestInfo(pair.first); } } } diff --git a/core/handlers/rosterhandler.h b/core/handlers/rosterhandler.h index 7be38e1..6a56b15 100644 --- a/core/handlers/rosterhandler.h +++ b/core/handlers/rosterhandler.h @@ -65,7 +65,6 @@ public: void storeConferences(); void clearConferences(); - void setPepSupport(Shared::Support support); private slots: void onRosterReceived(); @@ -89,6 +88,7 @@ private slots: void onContactNameChanged(const QString& name); void onContactSubscriptionStateChanged(Shared::SubscriptionState state); void onContactAvatarChanged(Shared::Avatar, const QString& path); + void onPepSupportedChanged(Shared::Support support); private: void addNewRoom(const QString& jid, const QString& nick, const QString& roomName, bool autoJoin); @@ -109,7 +109,6 @@ private: std::map> groups; std::map queuedContacts; std::set outOfRosterContacts; - Shared::Support pepSupport; }; } diff --git a/core/handlers/vcardhandler.cpp b/core/handlers/vcardhandler.cpp index 2a8d65c..24cb6ee 100644 --- a/core/handlers/vcardhandler.cpp +++ b/core/handlers/vcardhandler.cpp @@ -228,7 +228,7 @@ void Core::VCardHandler::requestVCard(const QString& jid) } } -void Core::VCardHandler::handleOtherPresenceOfMyAccountChange(const QXmppPresence& p_presence) +void Core::VCardHandler::handlePresenceOfMyAccountChange(const QXmppPresence& p_presence) { if (!ownVCardRequestInProgress) { switch (p_presence.vCardUpdateType()) { diff --git a/core/handlers/vcardhandler.h b/core/handlers/vcardhandler.h index 4febb69..ee61d0a 100644 --- a/core/handlers/vcardhandler.h +++ b/core/handlers/vcardhandler.h @@ -44,7 +44,7 @@ public: void handleOffline(); void requestVCard(const QString& jid); - void handleOtherPresenceOfMyAccountChange(const QXmppPresence& p_presence); + void handlePresenceOfMyAccountChange(const QXmppPresence& p_presence); void uploadVCard(const Shared::VCard& card); QString getAvatarPath() const; From 87973b3b67fc87174333f906a119af49cad91e93 Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 29 Aug 2022 21:34:25 +0300 Subject: [PATCH 069/137] first attempts to build against upstream qxmpp --- CMakeLists.txt | 83 ++++++++++++++++++++++-------- core/account.cpp | 11 ++++ core/account.h | 6 +++ core/handlers/discoveryhandler.cpp | 5 ++ 4 files changed, 84 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bbae079..d9d0dab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,11 @@ -cmake_minimum_required(VERSION 3.4) +cmake_minimum_required(VERSION 3.5) project(squawk VERSION 0.2.3 LANGUAGES CXX) cmake_policy(SET CMP0076 NEW) cmake_policy(SET CMP0079 NEW) set(CMAKE_CXX_STANDARD 17) +set(QT_VERSION_MAJOR 5) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) @@ -30,21 +31,44 @@ option(SYSTEM_QXMPP "Use system qxmpp lib" ON) option(WITH_KWALLET "Build KWallet support module" ON) option(WITH_KIO "Build KIO support module" ON) option(WITH_KCONFIG "Build KConfig support module" ON) +option(WITH_OMEMO "Build OMEMO support module" ON) # Dependencies ## Qt -set(QT_VERSION_MAJOR 5) -find_package(Qt5 COMPONENTS Widgets DBus Gui Xml Network Core REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) find_package(Boost COMPONENTS) target_include_directories(squawk PRIVATE ${Boost_INCLUDE_DIRS}) -target_include_directories(squawk PRIVATE ${Qt5_INCLUDE_DIRS}) -target_include_directories(squawk PRIVATE ${Qt5Widgets_INCLUDE_DIRS}) -target_include_directories(squawk PRIVATE ${Qt5DBus_INCLUDE_DIRS}) -target_include_directories(squawk PRIVATE ${Qt5Gui_INCLUDE_DIRS}) -target_include_directories(squawk PRIVATE ${Qt5Xml_INCLUDE_DIRS}) -target_include_directories(squawk PRIVATE ${Qt5Network_INCLUDE_DIRS}) -target_include_directories(squawk PRIVATE ${Qt5Core_INCLUDE_DIRS}) +target_include_directories(squawk PRIVATE ${Qt${QT_VERSION_MAJOR}_INCLUDE_DIRS}) +target_include_directories(squawk PRIVATE ${Qt${QT_VERSION_MAJOR}Widgets_INCLUDE_DIRS}) +target_include_directories(squawk PRIVATE ${Qt${QT_VERSION_MAJOR}DBus_INCLUDE_DIRS}) +target_include_directories(squawk PRIVATE ${Qt${QT_VERSION_MAJOR}Gui_INCLUDE_DIRS}) +target_include_directories(squawk PRIVATE ${Qt${QT_VERSION_MAJOR}Xml_INCLUDE_DIRS}) +target_include_directories(squawk PRIVATE ${Qt${QT_VERSION_MAJOR}Network_INCLUDE_DIRS}) +target_include_directories(squawk PRIVATE ${Qt${QT_VERSION_MAJOR}Core_INCLUDE_DIRS}) + +#OMEMO +if (WITH_OMEMO) + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_check_modules(OMEMO libomemo) + if (OMEMO_FOUND) + pkg_check_modules(SIGNAL libsignal-protocol-c) + if (SIGNAL_FOUND) + message("Building with support of OMEMO") + else () + message("signal-protocol package wasn't found, trying to build without OMEMO support") + set(WITH_OMEMO OFF) + endif() + else () + message("libomemo package wasn't found, trying to build without OMEMO support") + set(WITH_OMEMO OFF) + endif () + else () + message("PKG_CONFIG module wasn't found, can not check libomemo and libsignal-protocol support, trying to build without OMEMO support") + set(WITH_OMEMO OFF) + endif () +endif () ## QXmpp if (SYSTEM_QXMPP) @@ -58,13 +82,6 @@ if (SYSTEM_QXMPP) endif () endif () -if (NOT SYSTEM_QXMPP) - target_link_libraries(squawk PRIVATE qxmpp) - add_subdirectory(external/qxmpp) -else () - target_link_libraries(squawk PRIVATE QXmpp::QXmpp) -endif () - ## KIO if (WITH_KIO) find_package(KF5KIO CONFIG) @@ -109,14 +126,38 @@ if (WITH_KCONFIG) endif() endif() -## Signal (TODO) -# find_package(Signal REQUIRED) +if (NOT SYSTEM_QXMPP) + message("Building with bundled QXmpp") + + if (WITH_OMEMO) + set(BUILD_OMEMO ON) + else () + set(BUILD_OMEMO OFF) + endif () + add_subdirectory(external/qxmpp) + if (WITH_OMEMO) + target_include_directories(QXmppOmemo PRIVATE ${SIGNAL_INCLUDE_DIRS}) + target_include_directories(QXmppOmemo PRIVATE ${OMEMO_INCLUDE_DIRS}) + endif () + + target_link_libraries(squawk PRIVATE qxmpp) +else () + target_link_libraries(squawk PRIVATE QXmpp::QXmpp) +endif () ## LMDB find_package(LMDB REQUIRED) # Linking -target_link_libraries(squawk PRIVATE Qt5::Core Qt5::Widgets Qt5::DBus Qt5::Network Qt5::Gui Qt5::Xml) +target_link_libraries(squawk + PRIVATE + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Widgets + Qt${QT_VERSION_MAJOR}::DBus + Qt${QT_VERSION_MAJOR}::Network + Qt${QT_VERSION_MAJOR}::Gui + Qt${QT_VERSION_MAJOR}::Xml +) target_link_libraries(squawk PRIVATE lmdb) target_link_libraries(squawk PRIVATE simpleCrypt) # Link thread libraries on Linux @@ -166,7 +207,7 @@ install(FILES LICENSE.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk) if (CMAKE_BUILD_TYPE STREQUAL "Release") if (APPLE) add_custom_command(TARGET squawk POST_BUILD COMMENT "Running macdeployqt..." - COMMAND "${Qt5Widgets_DIR}/../../../bin/macdeployqt" "${CMAKE_CURRENT_BINARY_DIR}/squawk.app" + COMMAND "${Qt${QT_VERSION_MAJOR}Widgets_DIR}/../../../bin/macdeployqt" "${CMAKE_CURRENT_BINARY_DIR}/squawk.app" ) endif(APPLE) endif() diff --git a/core/account.cpp b/core/account.cpp index 1c3314f..a523554 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -40,6 +40,9 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& um(new QXmppUploadRequestManager()), dm(client.findExtension()), rcpm(new QXmppMessageReceiptManager()), +#ifdef WITH_OMEMO + om(new QXmppOmemoManager()), +#endif reconnectScheduled(false), reconnectTimer(new QTimer), network(p_net), @@ -87,6 +90,11 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& client.addExtension(rcpm); QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived); + +#ifdef WITH_OMEMO + client.addExtension(om); + qDebug("Added OMEMO manager"); +#endif reconnectTimer->setSingleShot(true); QObject::connect(reconnectTimer, &QTimer::timeout, this, &Account::onReconnectTimer); @@ -118,6 +126,9 @@ Account::~Account() delete rh; delete reconnectTimer; +#ifdef WITH_OMEMO + delete om; +#endif delete rcpm; delete dm; delete um; diff --git a/core/account.h b/core/account.h index 0ce39e5..1c5d14a 100644 --- a/core/account.h +++ b/core/account.h @@ -42,6 +42,9 @@ #include #include #include +#ifdef WITH_OMEMO +#include +#endif #include #include @@ -176,6 +179,9 @@ private: QXmppUploadRequestManager* um; QXmppDiscoveryManager* dm; QXmppMessageReceiptManager* rcpm; +#ifdef WITH_OMEMO + QXmppOmemoManager* om; +#endif bool reconnectScheduled; QTimer* reconnectTimer; diff --git a/core/handlers/discoveryhandler.cpp b/core/handlers/discoveryhandler.cpp index e562e68..e17c072 100644 --- a/core/handlers/discoveryhandler.cpp +++ b/core/handlers/discoveryhandler.cpp @@ -32,6 +32,11 @@ Core::DiscoveryHandler::DiscoveryHandler(Core::Account* account): acc->dm->setClientCapabilitiesNode("https://git.macaw.me/blue/squawk"); } +Core::DiscoveryHandler::~DiscoveryHandler() +{ +} + + void Core::DiscoveryHandler::onItemsReceived(const QXmppDiscoveryIq& items) { QString server = acc->getServer(); From 820dc845eaa77943b5d5d879fe52e4710ad25288 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 3 Sep 2022 14:39:42 +0300 Subject: [PATCH 070/137] BUILD FAILS! some ideas of storage and cache --- CMakeLists.txt | 31 ++++++------ core/account.cpp | 4 ++ core/components/clientcache.h | 2 +- core/handlers/CMakeLists.txt | 2 + core/handlers/omemohandler.cpp | 17 +++++++ core/handlers/omemohandler.h | 57 ++++++++++++++++++++++ core/storage/cache.h | 18 +++---- core/storage/cache.hpp | 50 ++++++++++---------- core/storage/storage.h | 20 ++++++-- core/storage/storage.hpp | 86 ++++++++++++++++++++-------------- 10 files changed, 198 insertions(+), 89 deletions(-) create mode 100644 core/handlers/omemohandler.cpp create mode 100644 core/handlers/omemohandler.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d9d0dab..af1e7f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,12 @@ option(WITH_OMEMO "Build OMEMO support module" ON) # Dependencies ## Qt -find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) +if (NOT DEFINED QT_VERSION_MAJOR) + find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) +else () + find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) +endif() + find_package(Boost COMPONENTS) target_include_directories(squawk PRIVATE ${Boost_INCLUDE_DIRS}) @@ -51,21 +56,15 @@ target_include_directories(squawk PRIVATE ${Qt${QT_VERSION_MAJOR}Core_INCLUDE_DI if (WITH_OMEMO) find_package(PkgConfig) if (PKG_CONFIG_FOUND) - pkg_check_modules(OMEMO libomemo) + pkg_check_modules(OMEMO libomemo-c) if (OMEMO_FOUND) - pkg_check_modules(SIGNAL libsignal-protocol-c) - if (SIGNAL_FOUND) - message("Building with support of OMEMO") - else () - message("signal-protocol package wasn't found, trying to build without OMEMO support") - set(WITH_OMEMO OFF) - endif() + message("Building with support of OMEMO") else () - message("libomemo package wasn't found, trying to build without OMEMO support") + message("libomemo-c package wasn't found, trying to build without OMEMO support") set(WITH_OMEMO OFF) endif () else () - message("PKG_CONFIG module wasn't found, can not check libomemo and libsignal-protocol support, trying to build without OMEMO support") + message("PKG_CONFIG module wasn't found, can not check libomemo-c support, trying to build without OMEMO support") set(WITH_OMEMO OFF) endif () endif () @@ -129,16 +128,18 @@ endif() if (NOT SYSTEM_QXMPP) message("Building with bundled QXmpp") + target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/base) + target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/client) + if (WITH_OMEMO) + target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/omemo) + target_include_directories(squawk PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/qxmpp/src/omemo) set(BUILD_OMEMO ON) + target_compile_definitions(squawk PRIVATE WITH_OMEMO) else () set(BUILD_OMEMO OFF) endif () add_subdirectory(external/qxmpp) - if (WITH_OMEMO) - target_include_directories(QXmppOmemo PRIVATE ${SIGNAL_INCLUDE_DIRS}) - target_include_directories(QXmppOmemo PRIVATE ${OMEMO_INCLUDE_DIRS}) - endif () target_link_libraries(squawk PRIVATE qxmpp) else () diff --git a/core/account.cpp b/core/account.cpp index a523554..87c341b 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -304,12 +304,16 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) break; case QXmppPresence::Subscribe: qDebug("xmpp presence \"subscribe\" received, do not yet know what to do, skipping"); + break; case QXmppPresence::Subscribed: qDebug("xmpp presence \"subscribed\" received, do not yet know what to do, skipping"); + break; case QXmppPresence::Unsubscribe: qDebug("xmpp presence \"unsubscribe\" received, do not yet know what to do, skipping"); + break; case QXmppPresence::Unsubscribed: qDebug("xmpp presence \"unsubscribed\" received, do not yet know what to do, skipping"); + break; case QXmppPresence::Probe: qDebug("xmpp presence \"probe\" received, do not yet know what to do, skipping"); break; diff --git a/core/components/clientcache.h b/core/components/clientcache.h index b2ff70d..15bcb7d 100644 --- a/core/components/clientcache.h +++ b/core/components/clientcache.h @@ -47,7 +47,7 @@ public slots: private: std::map requested; - Cache cache; + Cache cache; std::map specific; }; diff --git a/core/handlers/CMakeLists.txt b/core/handlers/CMakeLists.txt index 255d7fa..1516aa7 100644 --- a/core/handlers/CMakeLists.txt +++ b/core/handlers/CMakeLists.txt @@ -7,4 +7,6 @@ target_sources(squawk PRIVATE vcardhandler.h discoveryhandler.cpp discoveryhandler.h + omemohandler.cpp + omemohandler.h ) diff --git a/core/handlers/omemohandler.cpp b/core/handlers/omemohandler.cpp new file mode 100644 index 0000000..ef2f5ea --- /dev/null +++ b/core/handlers/omemohandler.cpp @@ -0,0 +1,17 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "omemohandler.h" diff --git a/core/handlers/omemohandler.h b/core/handlers/omemohandler.h new file mode 100644 index 0000000..2759a21 --- /dev/null +++ b/core/handlers/omemohandler.h @@ -0,0 +1,57 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 CORE_OMEMOHANDLER_H +#define CORE_OMEMOHANDLER_H + +#include +#include + +namespace Core { + +class OmemoHandler : public QXmppOmemoStorage +{ +public: + OmemoHandler(); + ~OmemoHandler() override; + + QFuture allData() override; + + QFuture setOwnDevice(const std::optional &device) override; + + QFuture addSignedPreKeyPair(uint32_t keyId, const SignedPreKeyPair &keyPair) override; + QFuture removeSignedPreKeyPair(uint32_t keyId) override; + + QFuture addPreKeyPairs(const QHash &keyPairs) override; + QFuture removePreKeyPair(uint32_t keyId) override; + + QFuture addDevice(const QString &jid, uint32_t deviceId, const Device &device) override; + QFuture removeDevice(const QString &jid, uint32_t deviceId) override; + QFuture removeDevices(const QString &jid) override; + + QFuture resetAll() override; + +private: + std::optional ownDevice; + Cache> devices; + Cache preKeyPairs; + Cache signedPreKeyPairs; + + +}; + +} +#endif // CORE_OMEMOHANDLER_H diff --git a/core/storage/cache.h b/core/storage/cache.h index 5091561..23f72f0 100644 --- a/core/storage/cache.h +++ b/core/storage/cache.h @@ -26,7 +26,7 @@ namespace Core { -template +template class Cache { public: @@ -36,16 +36,16 @@ public: void open(); void close(); - void addRecord(const QString& key, const T& value); - void changeRecord(const QString& key, const T& value); - void removeRecord(const QString& key); - T getRecord(const QString& key) const; - bool checkRecord(const QString& key) const; + void addRecord(const K& key, const V& value); + void changeRecord(const K& key, const V& value); + void removeRecord(const K& key); + V getRecord(const K& key) const; + bool checkRecord(const K& key) const; private: - Core::Storage storage; - std::map* cache; - std::set* abscent; + Core::Storage storage; + std::map* cache; + std::set* abscent; }; } diff --git a/core/storage/cache.hpp b/core/storage/cache.hpp index 1604d66..9491de2 100644 --- a/core/storage/cache.hpp +++ b/core/storage/cache.hpp @@ -18,44 +18,44 @@ #define CORE_CACHE_HPP #include "cache.h" -template -Core::Cache::Cache(const QString& name): +template +Core::Cache::Cache(const QString& name): storage(name), - cache(new std::map ()), - abscent(new std::set ()) {} + cache(new std::map ()), + abscent(new std::set ()) {} -template -Core::Cache::~Cache() { +template +Core::Cache::~Cache() { close(); delete cache; delete abscent; } -template -void Core::Cache::open() { +template +void Core::Cache::open() { storage.open();} -template -void Core::Cache::close() { +template +void Core::Cache::close() { storage.close();} -template -void Core::Cache::addRecord(const QString& key, const T& value) { +template +void Core::Cache::addRecord(const K& key, const V& value) { storage.addRecord(key, value); cache->insert(std::make_pair(key, value)); abscent->erase(key); } -template -T Core::Cache::getRecord(const QString& key) const { - typename std::map::const_iterator itr = cache->find(key); +template +V Core::Cache::getRecord(const K& key) const { + typename std::map::const_iterator itr = cache->find(key); if (itr == cache->end()) { if (abscent->count(key) > 0) { - throw Archive::NotFound(key, storage.getName().toStdString()); + throw Archive::NotFound(std::to_string(key), storage.getName().toStdString()); } try { - T value = storage.getRecord(key); + V value = storage.getRecord(key); itr = cache->insert(std::make_pair(key, value)).first; } catch (const Archive::NotFound& error) { abscent->insert(key); @@ -66,9 +66,9 @@ T Core::Cache::getRecord(const QString& key) const { return itr->second; } -template -bool Core::Cache::checkRecord(const QString& key) const { - typename std::map::const_iterator itr = cache->find(key); +template +bool Core::Cache::checkRecord(const K& key) const { + typename std::map::const_iterator itr = cache->find(key); if (itr != cache->end()) return true; @@ -76,7 +76,7 @@ bool Core::Cache::checkRecord(const QString& key) const { return false; try { - T value = storage.getRecord(key); + V value = storage.getRecord(key); itr = cache->insert(std::make_pair(key, value)).first; } catch (const Archive::NotFound& error) { return false; @@ -85,15 +85,15 @@ bool Core::Cache::checkRecord(const QString& key) const { return true; } -template -void Core::Cache::changeRecord(const QString& key, const T& value) { +template +void Core::Cache::changeRecord(const K& key, const V& value) { storage.changeRecord(key, value); //there is a non straightforward behaviour: if there was no element at the sorage it will be added cache->at(key) = value; abscent->erase(key); //so... this line here is to make it coherent with the storage } -template -void Core::Cache::removeRecord(const QString& key) { +template +void Core::Cache::removeRecord(const K& key) { storage.removeRecord(key); cache->erase(key); abscent->insert(key); diff --git a/core/storage/storage.h b/core/storage/storage.h index 5d6dabc..e43ae1a 100644 --- a/core/storage/storage.h +++ b/core/storage/storage.h @@ -29,7 +29,7 @@ namespace Core { /** * @todo write docs */ -template +template class Storage { public: @@ -39,10 +39,10 @@ public: void open(); void close(); - void addRecord(const QString& key, const T& value); - void changeRecord(const QString& key, const T& value); - void removeRecord(const QString& key); - T getRecord(const QString& key) const; + void addRecord(const K& key, const V& value); + void changeRecord(const K& key, const V& value); + void removeRecord(const K& key); + V getRecord(const K& key) const; QString getName() const; @@ -55,6 +55,16 @@ private: } +MDB_val& operator << (MDB_val& data, QString& value); +MDB_val& operator >> (MDB_val& data, QString& value); + +MDB_val& operator << (MDB_val& data, uint32_t& value); +MDB_val& operator >> (MDB_val& data, uint32_t& value); + +namespace std { + std::string to_string(const QString& str); +} + #include "storage.hpp" #endif // CORE_STORAGE_H diff --git a/core/storage/storage.hpp b/core/storage/storage.hpp index 48e334d..33b8b56 100644 --- a/core/storage/storage.hpp +++ b/core/storage/storage.hpp @@ -22,9 +22,10 @@ #include #include "storage.h" +#include -template -Core::Storage::Storage(const QString& p_name): +template +Core::Storage::Storage(const QString& p_name): name(p_name), opened(false), environment(), @@ -32,14 +33,14 @@ Core::Storage::Storage(const QString& p_name): { } -template -Core::Storage::~Storage() +template +Core::Storage::~Storage() { close(); } -template -void Core::Storage::open() +template +void Core::Storage::open() { if (!opened) { mdb_env_create(&environment); @@ -66,8 +67,8 @@ void Core::Storage::open() } } -template -void Core::Storage::close() +template +void Core::Storage::close() { if (opened) { mdb_dbi_close(environment, base); @@ -76,20 +77,19 @@ void Core::Storage::close() } } -template -void Core::Storage::addRecord(const QString& key, const T& value) +template +void Core::Storage::addRecord(const K& key, const V& value) { if (!opened) { throw Archive::Closed("addRecord", name.toStdString()); } QByteArray ba; QDataStream ds(&ba, QIODevice::WriteOnly); - const std::string& id = key.toStdString(); ds << value; MDB_val lmdbKey, lmdbData; - lmdbKey.mv_size = id.size(); - lmdbKey.mv_data = (char*)id.c_str(); + lmdbKey << key; + lmdbData.mv_size = ba.size(); lmdbData.mv_data = (uint8_t*)ba.data(); MDB_txn *txn; @@ -99,7 +99,7 @@ void Core::Storage::addRecord(const QString& key, const T& value) if (rc != 0) { mdb_txn_abort(txn); if (rc == MDB_KEYEXIST) { - throw Archive::Exist(name.toStdString(), id); + throw Archive::Exist(name.toStdString(), std::to_string(key)); } else { throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); } @@ -108,8 +108,8 @@ void Core::Storage::addRecord(const QString& key, const T& value) } } -template -void Core::Storage::changeRecord(const QString& key, const T& value) +template +void Core::Storage::changeRecord(const K& key, const V& value) { if (!opened) { throw Archive::Closed("changeRecord", name.toStdString()); @@ -117,12 +117,10 @@ void Core::Storage::changeRecord(const QString& key, const T& value) QByteArray ba; QDataStream ds(&ba, QIODevice::WriteOnly); - const std::string& id = key.toStdString(); ds << value; MDB_val lmdbKey, lmdbData; - lmdbKey.mv_size = id.size(); - lmdbKey.mv_data = (char*)id.c_str(); + lmdbKey << key; lmdbData.mv_size = ba.size(); lmdbData.mv_data = (uint8_t*)ba.data(); MDB_txn *txn; @@ -139,17 +137,15 @@ void Core::Storage::changeRecord(const QString& key, const T& value) } } -template -T Core::Storage::getRecord(const QString& key) const +template +V Core::Storage::getRecord(const K& key) const { if (!opened) { throw Archive::Closed("addElement", name.toStdString()); } - const std::string& id = key.toStdString(); MDB_val lmdbKey, lmdbData; - lmdbKey.mv_size = id.size(); - lmdbKey.mv_data = (char*)id.c_str(); + lmdbKey << key; MDB_txn *txn; int rc; @@ -158,14 +154,14 @@ T Core::Storage::getRecord(const QString& key) const if (rc) { mdb_txn_abort(txn); if (rc == MDB_NOTFOUND) { - throw Archive::NotFound(id, name.toStdString()); + throw Archive::NotFound(std::to_string(key), name.toStdString()); } else { throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); } } else { QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size); QDataStream ds(&ba, QIODevice::ReadOnly); - T value; + V value; ds >> value; mdb_txn_abort(txn); @@ -173,17 +169,15 @@ T Core::Storage::getRecord(const QString& key) const } } -template -void Core::Storage::removeRecord(const QString& key) +template +void Core::Storage::removeRecord(const K& key) { if (!opened) { throw Archive::Closed("addElement", name.toStdString()); } - const std::string& id = key.toStdString(); MDB_val lmdbKey; - lmdbKey.mv_size = id.size(); - lmdbKey.mv_data = (char*)id.c_str(); + lmdbKey << key; MDB_txn *txn; int rc; @@ -192,7 +186,7 @@ void Core::Storage::removeRecord(const QString& key) if (rc) { mdb_txn_abort(txn); if (rc == MDB_NOTFOUND) { - throw Archive::NotFound(id, name.toStdString()); + throw Archive::NotFound(std::to_string(key), name.toStdString()); } else { throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); } @@ -201,8 +195,32 @@ void Core::Storage::removeRecord(const QString& key) } } -template -QString Core::Storage::getName() const { +template +QString Core::Storage::getName() const { return name;} +MDB_val& operator << (MDB_val& data, const QString& value) { + QByteArray ba = value.toUtf8(); + data.mv_size = ba.size(); + data.mv_data = ba.data(); + return data; +} +MDB_val& operator >> (MDB_val& data, QString& value) { + value = QString::fromUtf8((const char*)data.mv_data, data.mv_size); + return data; +} + +MDB_val& operator << (MDB_val& data, uint32_t& value) { + data.mv_size = 4; + data.mv_data = &value; + return data; +} +MDB_val& operator >> (MDB_val& data, uint32_t& value) { + std::memcpy(&value, data.mv_data, data.mv_size); + return data; +} + +std::string std::to_string(const QString& str) { + return str.toStdString(); +} #endif //CORE_STORAGE_HPP From 0b61b6e928d60c21396ec9279510df945549adf6 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 15 Dec 2022 02:08:08 +0300 Subject: [PATCH 071/137] Some work on omemo handler, NOT DONE, BUILD FAILS! --- .gitmodules | 3 + CMakeLists.txt | 8 +- core/handlers/omemohandler.cpp | 144 +++++++++++++++++++++++++++++++++ core/handlers/omemohandler.h | 28 +++++-- external/qxmpp | 2 +- external/storage | 1 + main/main.cpp | 13 ++- 7 files changed, 187 insertions(+), 12 deletions(-) create mode 160000 external/storage diff --git a/.gitmodules b/.gitmodules index bbe5364..098973a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "external/qxmpp"] path = external/qxmpp url = https://github.com/qxmpp-project/qxmpp.git +[submodule "external/storage"] + path = external/storage + url = https://git.macaw.me/blue/storage diff --git a/CMakeLists.txt b/CMakeLists.txt index af1e7f3..3f5bd96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,7 +147,13 @@ else () endif () ## LMDB -find_package(LMDB REQUIRED) +#find_package(LMDB REQUIRED) + + +#TODO conditioning! +add_subdirectory(external/storage) +target_include_directories(squawk PRIVATE external/storage) +target_link_libraries(squawk PRIVATE storage) # Linking target_link_libraries(squawk diff --git a/core/handlers/omemohandler.cpp b/core/handlers/omemohandler.cpp index ef2f5ea..c1dc7ef 100644 --- a/core/handlers/omemohandler.cpp +++ b/core/handlers/omemohandler.cpp @@ -14,4 +14,148 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include #include "omemohandler.h" +#include "core/account.h" + +Core::OmemoHandler::OmemoHandler(Account* account) : + QXmppOmemoStorage(), + acc(account), + ownDevice(std::nullopt), + db("omemo"), + meta(db.addCache("meta")), + devices(db.addCache>("devices")), + preKeyPairs(db.addCache("preKeyPairs")), + signedPreKeyPairs(db.addCache("signedPreKeyPairs")) +{ + db.open(); + try { + QVariant own = meta->getRecord("ownDevice"); + ownDevice.value() = own.value(); + qDebug() << "Successfully found own device omemo data for account" << acc->getName(); + } catch (const DataBase::NotFound& e) { + qDebug() << "No device omemo data was found for account" << acc->getName(); + } +} + +Core::OmemoHandler::~OmemoHandler() { + db.close(); +} + +QFuture Core::OmemoHandler::emptyVoidFuture() { + QFutureInterface result(QFutureInterfaceBase::Started); + result.reportFinished(); + return result.future(); +} + +QFuture Core::OmemoHandler::allData() { + OmemoData data; + data.ownDevice = ownDevice; + + std::map pkeys = preKeyPairs->readAll(); + for (const std::pair& pair : pkeys) { + data.preKeyPairs.insert(pair.first, pair.second); + } + + std::map spre = signedPreKeyPairs->readAll(); + for (const std::pair& pair : spre) { + data.signedPreKeyPairs.insert(pair.first, pair.second); + } + + std::map> devs = devices->readAll(); + for (const std::pair>& pair : devs) { + data.devices.insert(pair.first, pair.second); + } + + QFutureInterface result(QFutureInterfaceBase::Started); + result.reportResult(std::move(data)); + result.reportFinished(); + return result.future(); +} + +QFuture Core::OmemoHandler::addDevice(const QString& jid, uint32_t deviceId, const QXmppOmemoStorage::Device& device) { + QHash devs; + bool had = true; + try { + devs = devices->getRecord(jid); + } catch (const DataBase::NotFound& error) { + had = false; + } + + devs.insert(deviceId, device); + if (had) { + devices->changeRecord(jid, devs); + } else { + devices->addRecord(jid, devs); + } + + return emptyVoidFuture(); +} + +QFuture Core::OmemoHandler::addPreKeyPairs(const QHash& keyPairs) { + for (QHash::const_iterator itr = keyPairs.begin(), end = keyPairs.end(); itr != end; ++itr) { + preKeyPairs->forceRecord(itr.key(), itr.value()); + } + + return emptyVoidFuture(); +} + +QFuture Core::OmemoHandler::addSignedPreKeyPair(uint32_t keyId, const QXmppOmemoStorage::SignedPreKeyPair& keyPair) { + signedPreKeyPairs->addRecord(keyId, keyPair); + return emptyVoidFuture(); +} + +QFuture Core::OmemoHandler::removeDevice(const QString& jid, uint32_t deviceId) { + QHash devs = devices->getRecord(jid); + devs.remove(deviceId); + if (devs.isEmpty()) { + devices->removeRecord(jid); + } else { + devices->changeRecord(jid, devs); + } + return emptyVoidFuture(); +} + +QFuture Core::OmemoHandler::removeDevices(const QString& jid) { + devices->removeRecord(jid); + return emptyVoidFuture(); +} + +QFuture Core::OmemoHandler::removePreKeyPair(uint32_t keyId) { + preKeyPairs->removeRecord(keyId); + return emptyVoidFuture(); +} + +QFuture Core::OmemoHandler::removeSignedPreKeyPair(uint32_t keyId) { + signedPreKeyPairs->removeRecord(keyId); + return emptyVoidFuture(); +} + +QFuture Core::OmemoHandler::setOwnDevice(const std::optional& device) { + bool had = ownDevice.has_value(); + ownDevice = device; + if (ownDevice.has_value()) { + if (had) { + meta->changeRecord("ownDevice", QVariant::fromValue(ownDevice.value())); + } else { + meta->addRecord("ownDevice", QVariant::fromValue(ownDevice.value())); + } + } else { + if (had) { + meta->removeRecord("ownDevice"); + } + } + return emptyVoidFuture(); +} + +QFuture Core::OmemoHandler::resetAll() { + ownDevice = std::nullopt; + db.drop(); + + return emptyVoidFuture(); +} + + + + + diff --git a/core/handlers/omemohandler.h b/core/handlers/omemohandler.h index 2759a21..8f3ac9c 100644 --- a/core/handlers/omemohandler.h +++ b/core/handlers/omemohandler.h @@ -18,14 +18,17 @@ #define CORE_OMEMOHANDLER_H #include -#include +#include + +Q_DECLARE_METATYPE(QXmppOmemoStorage::OwnDevice); namespace Core { +class Account; class OmemoHandler : public QXmppOmemoStorage { public: - OmemoHandler(); + OmemoHandler(Account* account); ~OmemoHandler() override; QFuture allData() override; @@ -45,13 +48,24 @@ public: QFuture resetAll() override; private: + static QFuture emptyVoidFuture(); + +private: + Account* acc; std::optional ownDevice; - Cache> devices; - Cache preKeyPairs; - Cache signedPreKeyPairs; - - + DataBase db; + DataBase::Cache* meta; + DataBase::Cache>* devices; + DataBase::Cache* preKeyPairs; + DataBase::Cache* signedPreKeyPairs; }; } + +QDataStream& operator << (QDataStream &out, const QXmppOmemoStorage::Device& device); +QDataStream& operator >> (QDataStream &out, QXmppOmemoStorage::Device device); + +QDataStream& operator << (QDataStream &out, const QXmppOmemoStorage::SignedPreKeyPair& device); +QDataStream& operator >> (QDataStream &out, QXmppOmemoStorage::SignedPreKeyPair device); + #endif // CORE_OMEMOHANDLER_H diff --git a/external/qxmpp b/external/qxmpp index fe83e9c..f6e7591 160000 --- a/external/qxmpp +++ b/external/qxmpp @@ -1 +1 @@ -Subproject commit fe83e9c3d42c3becf682e2b5ecfc9d77b24c614f +Subproject commit f6e7591e21b4c55319918ac296b2341b2e9f1988 diff --git a/external/storage b/external/storage new file mode 160000 index 0000000..08daad4 --- /dev/null +++ b/external/storage @@ -0,0 +1 @@ +Subproject commit 08daad48ad36d9f12dc6485a085190e31e4cf49e diff --git a/main/main.cpp b/main/main.cpp index 086dbc0..58063e0 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -33,6 +33,10 @@ #include #include +#ifdef WITH_OMEMO +#include +#endif + int main(int argc, char *argv[]) { qRegisterMetaType("Shared::Message"); @@ -40,12 +44,15 @@ int main(int argc, char *argv[]) qRegisterMetaType("Shared::VCard"); qRegisterMetaType>("std::list"); qRegisterMetaType>("std::list"); - qRegisterMetaType>("std::list"); - qRegisterMetaType>("std::set"); - qRegisterMetaType>("std::list"); + qRegisterMetaType>("std::list"); + qRegisterMetaType>("std::set"); + qRegisterMetaType>("std::list"); qRegisterMetaType>("QSet"); qRegisterMetaType("Shared::ConnectionState"); qRegisterMetaType("Shared::Availability"); +#ifdef WITH_OMEMO + qRegisterMetaType("QXmppOmemoStorage::OwnDevice"); +#endif QApplication app(argc, argv); SignalCatcher sc(&app); From db3bc358a7f6b8079f0651b9f6f9f3e0ca5a537d Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 19 Dec 2022 18:43:24 +0300 Subject: [PATCH 072/137] work progress: trust manager. DOESN'T START! --- CMakeLists.txt | 1 + core/account.cpp | 25 +- core/account.h | 30 ++- core/components/clientcache.cpp | 15 +- core/components/clientcache.h | 6 +- core/handlers/CMakeLists.txt | 2 + core/handlers/discoveryhandler.cpp | 11 +- core/handlers/discoveryhandler.h | 2 + core/handlers/omemohandler.cpp | 55 +++- core/handlers/omemohandler.h | 9 +- core/handlers/rosterhandler.cpp | 9 +- core/handlers/rosterhandler.h | 2 + core/handlers/trusthandler.cpp | 390 +++++++++++++++++++++++++++++ core/handlers/trusthandler.h | 81 ++++++ core/handlers/vcardhandler.cpp | 17 +- core/handlers/vcardhandler.h | 2 + core/storage/CMakeLists.txt | 8 +- external/qxmpp | 2 +- external/storage | 2 +- shared/enums.h | 20 ++ 20 files changed, 634 insertions(+), 55 deletions(-) create mode 100644 core/handlers/trusthandler.cpp create mode 100644 core/handlers/trusthandler.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f5bd96..b40e876 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,6 +142,7 @@ if (NOT SYSTEM_QXMPP) add_subdirectory(external/qxmpp) target_link_libraries(squawk PRIVATE qxmpp) + target_link_libraries(squawk PRIVATE QXmppOmemo) else () target_link_libraries(squawk PRIVATE QXmpp::QXmpp) endif () diff --git a/core/account.cpp b/core/account.cpp index 87c341b..4b25737 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -31,6 +31,17 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& config(), presence(), state(Shared::ConnectionState::disconnected), + + mh(new MessageHandler(this)), + rh(new RosterHandler(this)), + vh(new VCardHandler(this)), + dh(new DiscoveryHandler(this)), +#ifdef WITH_OMEMO + th(new TrustHandler(this)), + oh(new OmemoHandler(this)), + tm(new QXmppTrustManager(th)), + om(new QXmppOmemoManager(oh)), +#endif cm(new QXmppCarbonManager()), am(new QXmppMamManager()), mm(new QXmppMucManager()), @@ -40,9 +51,6 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& um(new QXmppUploadRequestManager()), dm(client.findExtension()), rcpm(new QXmppMessageReceiptManager()), -#ifdef WITH_OMEMO - om(new QXmppOmemoManager()), -#endif reconnectScheduled(false), reconnectTimer(new QTimer), network(p_net), @@ -50,17 +58,17 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& lastError(Error::none), pepSupport(Shared::Support::unknown), active(p_active), - notReadyPassword(false), - mh(new MessageHandler(this)), - rh(new RosterHandler(this)), - vh(new VCardHandler(this)), - dh(new DiscoveryHandler(this)) + notReadyPassword(false) { config.setUser(p_login); config.setDomain(p_server); config.setPassword(p_password); config.setAutoAcceptSubscriptions(true); //config.setAutoReconnectionEnabled(false); + + rh->initialize(); + vh->initialize(); + dh->initialize(); QObject::connect(&client, &QXmppClient::stateChanged, this, &Account::onClientStateChange); QObject::connect(&client, &QXmppClient::presenceReceived, this, &Account::onPresenceReceived); @@ -92,6 +100,7 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived); #ifdef WITH_OMEMO + client.addExtension(tm); client.addExtension(om); qDebug("Added OMEMO manager"); #endif diff --git a/core/account.h b/core/account.h index 1c5d14a..7e5e3b0 100644 --- a/core/account.h +++ b/core/account.h @@ -42,9 +42,6 @@ #include #include #include -#ifdef WITH_OMEMO -#include -#endif #include #include @@ -57,6 +54,13 @@ #include "handlers/vcardhandler.h" #include "handlers/discoveryhandler.h" +#ifdef WITH_OMEMO +#include +#include +#include "handlers/trusthandler.h" +#include "handlers/omemohandler.h" +#endif + namespace Core { @@ -170,6 +174,18 @@ private: QXmppConfiguration config; QXmppPresence presence; Shared::ConnectionState state; + + MessageHandler* mh; + RosterHandler* rh; + VCardHandler* vh; + DiscoveryHandler* dh; +#ifdef WITH_OMEMO + TrustHandler* th; + OmemoHandler* oh; + + QXmppTrustManager* tm; + QXmppOmemoManager* om; +#endif QXmppCarbonManager* cm; QXmppMamManager* am; QXmppMucManager* mm; @@ -179,9 +195,6 @@ private: QXmppUploadRequestManager* um; QXmppDiscoveryManager* dm; QXmppMessageReceiptManager* rcpm; -#ifdef WITH_OMEMO - QXmppOmemoManager* om; -#endif bool reconnectScheduled; QTimer* reconnectTimer; @@ -192,11 +205,6 @@ private: bool active; bool notReadyPassword; - MessageHandler* mh; - RosterHandler* rh; - VCardHandler* vh; - DiscoveryHandler* dh; - private slots: void onClientStateChange(QXmppClient::State state); void onClientError(QXmppClient::Error err); diff --git a/core/components/clientcache.cpp b/core/components/clientcache.cpp index a054428..a312b24 100644 --- a/core/components/clientcache.cpp +++ b/core/components/clientcache.cpp @@ -19,27 +19,28 @@ #include Core::ClientCache::ClientCache(): + db("clients"), + cache(db.addCache("info")), requested(), - cache("clients"), specific() { - cache.open(); + db.open(); } Core::ClientCache::~ClientCache() { - cache.close(); + db.close(); } void Core::ClientCache::open() { - cache.open();} + db.open();} void Core::ClientCache::close() { - cache.close();} + db.close();} bool Core::ClientCache::checkClient(const QString& node, const QString& ver, const QString& hash) { QString id = node + "/" + ver; - if (requested.count(id) == 0 && !cache.checkRecord(id)) { + if (requested.count(id) == 0 && !cache->checkRecord(id)) { Shared::ClientInfo& info = requested.insert(std::make_pair(id, Shared::ClientInfo())).first->second; info.node = node; info.verification = ver; @@ -65,7 +66,7 @@ bool Core::ClientCache::registerClientInfo ( bool valid = info.valid(); if (valid) { - cache.addRecord(id, info); + cache->addRecord(id, info); } else { info.specificPresence = sourceFullJid; specific.insert(std::make_pair(sourceFullJid, info)); diff --git a/core/components/clientcache.h b/core/components/clientcache.h index 15bcb7d..640def3 100644 --- a/core/components/clientcache.h +++ b/core/components/clientcache.h @@ -23,7 +23,8 @@ #include #include -#include +#include + #include #include @@ -46,8 +47,9 @@ public slots: bool registerClientInfo(const QString& sourceFullJid, const QString& id, const std::set& identities, const std::set& features); private: + DataBase db; + DataBase::Cache* cache; std::map requested; - Cache cache; std::map specific; }; diff --git a/core/handlers/CMakeLists.txt b/core/handlers/CMakeLists.txt index 1516aa7..746a36f 100644 --- a/core/handlers/CMakeLists.txt +++ b/core/handlers/CMakeLists.txt @@ -9,4 +9,6 @@ target_sources(squawk PRIVATE discoveryhandler.h omemohandler.cpp omemohandler.h + trusthandler.cpp + trusthandler.h ) diff --git a/core/handlers/discoveryhandler.cpp b/core/handlers/discoveryhandler.cpp index e17c072..2464f07 100644 --- a/core/handlers/discoveryhandler.cpp +++ b/core/handlers/discoveryhandler.cpp @@ -21,7 +21,11 @@ Core::DiscoveryHandler::DiscoveryHandler(Core::Account* account): QObject(), - acc(account) + acc(account) {} + +Core::DiscoveryHandler::~DiscoveryHandler() {} + +void Core::DiscoveryHandler::initialize() { QObject::connect(acc->dm, &QXmppDiscoveryManager::itemsReceived, this, &DiscoveryHandler::onItemsReceived); QObject::connect(acc->dm, &QXmppDiscoveryManager::infoReceived, this, &DiscoveryHandler::onInfoReceived); @@ -32,11 +36,6 @@ Core::DiscoveryHandler::DiscoveryHandler(Core::Account* account): acc->dm->setClientCapabilitiesNode("https://git.macaw.me/blue/squawk"); } -Core::DiscoveryHandler::~DiscoveryHandler() -{ -} - - void Core::DiscoveryHandler::onItemsReceived(const QXmppDiscoveryIq& items) { QString server = acc->getServer(); diff --git a/core/handlers/discoveryhandler.h b/core/handlers/discoveryhandler.h index 3129219..e4ef265 100644 --- a/core/handlers/discoveryhandler.h +++ b/core/handlers/discoveryhandler.h @@ -32,6 +32,8 @@ public: DiscoveryHandler(Account* account); ~DiscoveryHandler(); + void initialize(); + private slots: void onItemsReceived (const QXmppDiscoveryIq& items); void onInfoReceived (const QXmppDiscoveryIq& info); diff --git a/core/handlers/omemohandler.cpp b/core/handlers/omemohandler.cpp index c1dc7ef..25ac752 100644 --- a/core/handlers/omemohandler.cpp +++ b/core/handlers/omemohandler.cpp @@ -22,7 +22,7 @@ Core::OmemoHandler::OmemoHandler(Account* account) : QXmppOmemoStorage(), acc(account), ownDevice(std::nullopt), - db("omemo"), + db(acc->getName() + "/omemo"), meta(db.addCache("meta")), devices(db.addCache>("devices")), preKeyPairs(db.addCache("preKeyPairs")), @@ -155,7 +155,60 @@ QFuture Core::OmemoHandler::resetAll() { return emptyVoidFuture(); } +QDataStream & operator >> (QDataStream& in, QXmppOmemoStorage::Device& device) { + in >> device.label; + in >> device.keyId; + in >> device.session; + in >> device.unrespondedSentStanzasCount; + in >> device.unrespondedReceivedStanzasCount; + in >> device.removalFromDeviceListDate; + return in; +} +QDataStream & operator << (QDataStream& out, const QXmppOmemoStorage::Device& device) { + out << device.label; + out << device.keyId; + out << device.session; + out << device.unrespondedSentStanzasCount; + out << device.unrespondedReceivedStanzasCount; + out << device.removalFromDeviceListDate; + return out; +} +QDataStream & operator >> (QDataStream& in, QXmppOmemoStorage::OwnDevice& device) { + in >> device.id; + in >> device.label; + in >> device.privateIdentityKey; + in >> device.publicIdentityKey; + in >> device.latestSignedPreKeyId; + in >> device.latestPreKeyId; + + return in; +} + +QDataStream & operator << (QDataStream& out, const QXmppOmemoStorage::OwnDevice& device) { + out << device.id; + out << device.label; + out << device.privateIdentityKey; + out << device.publicIdentityKey; + out << device.latestSignedPreKeyId; + out << device.latestPreKeyId; + + return out; +} + +QDataStream & operator >> (QDataStream& in, QXmppOmemoStorage::SignedPreKeyPair& pair) { + in >> pair.creationDate; + in >> pair.data; + + return in; +} + +QDataStream & operator << (QDataStream& out, const QXmppOmemoStorage::SignedPreKeyPair& pair) { + out << pair.creationDate; + out << pair.data; + + return out; +} diff --git a/core/handlers/omemohandler.h b/core/handlers/omemohandler.h index 8f3ac9c..537dfec 100644 --- a/core/handlers/omemohandler.h +++ b/core/handlers/omemohandler.h @@ -21,6 +21,8 @@ #include Q_DECLARE_METATYPE(QXmppOmemoStorage::OwnDevice); +Q_DECLARE_METATYPE(QXmppOmemoStorage::Device); +Q_DECLARE_METATYPE(QXmppOmemoStorage::SignedPreKeyPair); namespace Core { class Account; @@ -63,9 +65,12 @@ private: } QDataStream& operator << (QDataStream &out, const QXmppOmemoStorage::Device& device); -QDataStream& operator >> (QDataStream &out, QXmppOmemoStorage::Device device); +QDataStream& operator >> (QDataStream &in, QXmppOmemoStorage::Device& device); + +QDataStream& operator << (QDataStream &out, const QXmppOmemoStorage::OwnDevice& device); +QDataStream& operator >> (QDataStream &in, QXmppOmemoStorage::OwnDevice& device); QDataStream& operator << (QDataStream &out, const QXmppOmemoStorage::SignedPreKeyPair& device); -QDataStream& operator >> (QDataStream &out, QXmppOmemoStorage::SignedPreKeyPair device); +QDataStream& operator >> (QDataStream &in, QXmppOmemoStorage::SignedPreKeyPair& device); #endif // CORE_OMEMOHANDLER_H diff --git a/core/handlers/rosterhandler.cpp b/core/handlers/rosterhandler.cpp index 1a61440..8c65c63 100644 --- a/core/handlers/rosterhandler.cpp +++ b/core/handlers/rosterhandler.cpp @@ -26,14 +26,15 @@ Core::RosterHandler::RosterHandler(Core::Account* account): conferences(), groups(), queuedContacts(), - outOfRosterContacts() -{ + outOfRosterContacts() {} + +void Core::RosterHandler::initialize() { connect(acc->rm, &QXmppRosterManager::rosterReceived, this, &RosterHandler::onRosterReceived); connect(acc->rm, &QXmppRosterManager::itemAdded, this, &RosterHandler::onRosterItemAdded); connect(acc->rm, &QXmppRosterManager::itemRemoved, this, &RosterHandler::onRosterItemRemoved); connect(acc->rm, &QXmppRosterManager::itemChanged, this, &RosterHandler::onRosterItemChanged); - - + + connect(acc->mm, &QXmppMucManager::roomAdded, this, &RosterHandler::onMucRoomAdded); connect(acc->bm, &QXmppBookmarkManager::bookmarksReceived, this, &RosterHandler::bookmarksReceived); diff --git a/core/handlers/rosterhandler.h b/core/handlers/rosterhandler.h index 6a56b15..11525be 100644 --- a/core/handlers/rosterhandler.h +++ b/core/handlers/rosterhandler.h @@ -65,6 +65,8 @@ public: void storeConferences(); void clearConferences(); + + void initialize(); private slots: void onRosterReceived(); diff --git a/core/handlers/trusthandler.cpp b/core/handlers/trusthandler.cpp new file mode 100644 index 0000000..2c0be29 --- /dev/null +++ b/core/handlers/trusthandler.cpp @@ -0,0 +1,390 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "trusthandler.h" +#include "core/account.h" + +using namespace Core; + +Core::TrustHandler::TrustHandler(Account* account): + acc(account), + db(acc->getName() + "/trust"), + protocols(db.createDirectory() + "/protocols"), + securityPolicies(db.addCache("securityPolicies")), + ownKeys(db.addCache("ownKeys")), + keysByProtocol() +{ + if (!protocols.open(QIODevice::ReadWrite | QIODevice::Text)) { //never supposed to happen since I have just created a directory; + throw DataBase::Directory(protocols.fileName().toStdString()); + } + + QTextStream in(&protocols); + while(!in.atEnd()) { + QString protocol = in.readLine(); + + if (protocol.size() > 1) { //I'm afraid of reading some nonsence like separately standing \n or EF or BOM, so... let it be at least 2 chars long + KeyCache* cache = db.addCache(protocol.toStdString()); + keysByProtocol.insert(std::make_pair(protocol, cache)); + } + } + + protocols.close(); + db.open(); +} + +Core::TrustHandler::~TrustHandler() { + protocols.close(); + db.close(); +} + +Core::TrustHandler::KeyCache * Core::TrustHandler::getCache(const QString& encryption) { + std::map::iterator itr = keysByProtocol.find(encryption); + if (itr == keysByProtocol.end()) { + return createNewCache(encryption); + } else { + return itr->second; + } +} + +Core::TrustHandler::KeyCache * Core::TrustHandler::createNewCache(const QString& encryption) { + db.close(); + KeyCache* cache = db.addCache(encryption.toStdString()); + keysByProtocol.insert(std::make_pair(encryption, cache)); + + if(!protocols.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) { + throw DataBase::Directory(protocols.fileName().toStdString()); + } + QTextStream out(&protocols); + out << encryption + "\n"; + protocols.close(); + + db.open(); + return cache; +} + + +QFuture Core::TrustHandler::emptyVoidFuture() { + QFutureInterface result(QFutureInterfaceBase::Started); + result.reportFinished(); + return result.future(); +} + + +QFuture Core::TrustHandler::resetAll(const QString& encryption) { + securityPolicies->removeRecord(encryption); + ownKeys->removeRecord(encryption); + getCache(encryption)->drop(); + + return emptyVoidFuture(); +} + +QFuture Core::TrustHandler::trustLevel( + const QString& encryption, + const QString& keyOwnerJid, + const QByteArray& keyId) +{ + Keys map = getCache(encryption)->getRecord(keyOwnerJid); + Shared::TrustLevel level = map.at(keyId); + + QFutureInterface result(QFutureInterfaceBase::Started); + result.reportResult(convert(level)); + result.reportFinished(); + return result.future(); +} + +QFuture>> Core::TrustHandler::setTrustLevel( + const QString& encryption, + const QList& keyOwnerJids, + QXmpp::TrustLevel oldTrustLevel, + QXmpp::TrustLevel newTrustLevel) +{ + QHash> modifiedKeys; + Shared::TrustLevel oldLevel = convert(oldTrustLevel); + Shared::TrustLevel newLevel = convert(newTrustLevel); + KeyCache* cache = getCache(encryption); + for (const QString& keyOwnerJid : keyOwnerJids) { + Keys map = cache->getRecord(keyOwnerJid); + uint count = 0; + for (std::pair& pair : map) { + Shared::TrustLevel& current = pair.second; + if (current == oldLevel) { + current = newLevel; + modifiedKeys[encryption].insert(keyOwnerJid, pair.first); + ++count; + } + } + if (count > 0) { + cache->changeRecord(keyOwnerJid, map); + } + } + + QFutureInterface>> result(QFutureInterfaceBase::Started); + result.reportResult(modifiedKeys); + result.reportFinished(); + return result.future(); +} + +QFuture>> Core::TrustHandler::setTrustLevel( + const QString& encryption, + const QMultiHash& keyIds, + QXmpp::TrustLevel trustLevel) +{ + QHash> modifiedKeys; + Shared::TrustLevel level = convert(trustLevel); + KeyCache* cache = getCache(encryption); + + for (MultySB::const_iterator itr = keyIds.begin(), end = keyIds.end(); itr != end; ++itr) { + const QString& keyOwnerJid = itr.key(); + const QByteArray& keyId = itr.value(); + Keys map = cache->getRecord(keyOwnerJid); + std::pair result = map.insert(std::make_pair(keyId, level)); + if (result.second) { + modifiedKeys[encryption].insert(keyOwnerJid, keyId); + cache->changeRecord(keyOwnerJid, map); + } else if (result.first->second != level) { + result.first->second = level; + modifiedKeys[encryption].insert(keyOwnerJid, keyId); + cache->changeRecord(keyOwnerJid, map); + } + } + + QFutureInterface>> result(QFutureInterfaceBase::Started); + result.reportResult(modifiedKeys); + result.reportFinished(); + return result.future(); +} + +QFuture TrustHandler::hasKey(const QString& encryption, + const QString& keyOwnerJid, + QXmpp::TrustLevels trustLevels) +{ + KeyCache* cache = getCache(encryption); + bool found = false; + try { + Keys map = cache->getRecord(keyOwnerJid); + for (const std::pair& pair : map) { + if (trustLevels.testFlag(convert(pair.second))) { + found = true; + break; + } + } + } catch (const DataBase::NotFound& e) {} + + QFutureInterface result(QFutureInterfaceBase::Started); + result.reportResult(found); + result.reportFinished(); + return result.future(); +} + +QFuture>> TrustHandler::keys( + const QString& encryption, + const QList& keyOwnerJids, + QXmpp::TrustLevels trustLevels) +{ + HSHBTL res; + + KeyCache* cache = getCache(encryption); + for (const QString& keyOwnerJid : keyOwnerJids) { + try { + Keys map = cache->getRecord(keyOwnerJid); + QHash& pRes = res[keyOwnerJid]; + for (const std::pair& pair : map) { + QXmpp::TrustLevel level = convert(pair.second); + if (!trustLevels || trustLevels.testFlag(level)) { + pRes.insert(pair.first, level); + } + } + } catch (const DataBase::NotFound& e) {} + } + + QFutureInterface result(QFutureInterfaceBase::Started); + result.reportResult(res); + result.reportFinished(); + return result.future(); +} + +QFuture>> TrustHandler::keys( + const QString& encryption, + QXmpp::TrustLevels trustLevels) +{ + QHash res; + KeyCache* cache = getCache(encryption); + std::map storage = cache->readAll(); + for (const std::pair& value : storage) { + for (const std::pair& pair : value.second) { + QXmpp::TrustLevel level = convert(pair.second); + if (!trustLevels || trustLevels.testFlag(level)) { + res[level].insert(value.first, pair.first); + } + } + } + + QFutureInterface> result(QFutureInterfaceBase::Started); + result.reportResult(res); + result.reportFinished(); + return result.future(); +} + +QFuture TrustHandler::removeKeys(const QString& encryption) { + getCache(encryption)->drop(); + return emptyVoidFuture(); +} + +QFuture TrustHandler::removeKeys(const QString& encryption, const QString& keyOwnerJid) { + getCache(encryption)->removeRecord(keyOwnerJid); + return emptyVoidFuture(); +} + +QFuture TrustHandler::removeKeys(const QString& encryption, const QList& keyIds) { + std::set set; + for (const QByteArray& keyId : keyIds) { + set.insert(keyId); + } + + KeyCache* cache = getCache(encryption); + std::map data = cache->readAll(); + bool changed = false; + for (std::map::iterator cItr = data.begin(), cEnd = data.end(); cItr != cEnd; /*no increment*/) { + Keys& byOwner = cItr->second; + for (Keys::const_iterator itr = byOwner.begin(), end = byOwner.end(); itr != end; /*no increment*/) { + const QByteArray& keyId = itr->first; + if (set.erase(keyId)) { + byOwner.erase(itr++); + changed = true; + } else { + ++itr; + } + } + if (byOwner.size() > 0) { + data.erase(cItr++); + } else { + ++cItr; + } + } + if (changed) { + cache->replaceAll(data); + } + + return emptyVoidFuture(); +} + +QFuture TrustHandler::addKeys( + const QString& encryption, + const QString& keyOwnerJid, + const QList& keyIds, + QXmpp::TrustLevel trustLevel) +{ + KeyCache* cache = getCache(encryption); + Shared::TrustLevel level = convert(trustLevel); + Keys data; + bool had = false; + try { + data = cache->getRecord(keyOwnerJid); + had = true; + } catch (const DataBase::NotFound& e) {} + for (const QByteArray& keyId : keyIds) { + std::pair result = data.insert(std::make_pair(keyId, level)); + if (!result.second) { + result.first->second = level; + } + } + + if (had) { + cache->changeRecord(keyOwnerJid, data); + } else { + cache->addRecord(keyOwnerJid, data); + } + + return emptyVoidFuture(); +} + +QFuture TrustHandler::ownKey(const QString& encryption) { + QByteArray res; + try { + res = ownKeys->getRecord(encryption); + } catch (const DataBase::NotFound& e) {} + + QFutureInterface result(QFutureInterfaceBase::Started); + result.reportResult(res); + result.reportFinished(); + return result.future(); +} + +QFuture TrustHandler::resetOwnKey(const QString& encryption) { + try { + ownKeys->removeRecord(encryption); + } catch (const DataBase::NotFound& e) {} + + return emptyVoidFuture(); +} + +QFuture TrustHandler::setOwnKey(const QString& encryption, const QByteArray& keyId) { + ownKeys->forceRecord(encryption, keyId); + return emptyVoidFuture(); +} + +QFuture TrustHandler::securityPolicy(const QString& encryption) { + QXmpp::TrustSecurityPolicy res; + try { + res = static_cast(securityPolicies->getRecord(encryption)); + } catch (const DataBase::NotFound& e) {} + + QFutureInterface result(QFutureInterfaceBase::Started); + result.reportResult(res); + result.reportFinished(); + return result.future(); +} + +QFuture TrustHandler::resetSecurityPolicy(const QString& encryption) { + try { + securityPolicies->removeRecord(encryption); + } catch (const DataBase::NotFound& e) {} + return emptyVoidFuture(); +} + +QFuture TrustHandler::setSecurityPolicy( + const QString& encryption, + QXmpp::TrustSecurityPolicy securityPolicy) +{ + uint8_t pol = securityPolicy; + securityPolicies->forceRecord(encryption, pol); + + return emptyVoidFuture(); +} + +Shared::TrustLevel Core::TrustHandler::convert(Core::TrustHandler::TL level) +{ + switch (level) { + case QXmpp::TrustLevel::Undecided: return Shared::TrustLevel::Undecided; + case QXmpp::TrustLevel::AutomaticallyDistrusted: return Shared::TrustLevel::AutomaticallyDistrusted; + case QXmpp::TrustLevel::ManuallyDistrusted: return Shared::TrustLevel::ManuallyDistrusted; + case QXmpp::TrustLevel::AutomaticallyTrusted: return Shared::TrustLevel::AutomaticallyTrusted; + case QXmpp::TrustLevel::ManuallyTrusted: return Shared::TrustLevel::ManuallyTrusted; + case QXmpp::TrustLevel::Authenticated: return Shared::TrustLevel::Authenticated; + } +} + +Core::TrustHandler::TL Core::TrustHandler::convert(Shared::TrustLevel level) +{ + switch (level) { + case Shared::TrustLevel::Undecided: return QXmpp::TrustLevel::Undecided; + case Shared::TrustLevel::AutomaticallyDistrusted: return QXmpp::TrustLevel::AutomaticallyDistrusted; + case Shared::TrustLevel::ManuallyDistrusted: return QXmpp::TrustLevel::ManuallyDistrusted; + case Shared::TrustLevel::AutomaticallyTrusted: return QXmpp::TrustLevel::AutomaticallyTrusted; + case Shared::TrustLevel::ManuallyTrusted: return QXmpp::TrustLevel::ManuallyTrusted; + case Shared::TrustLevel::Authenticated: return QXmpp::TrustLevel::Authenticated; + } +} diff --git a/core/handlers/trusthandler.h b/core/handlers/trusthandler.h new file mode 100644 index 0000000..b1fe0b4 --- /dev/null +++ b/core/handlers/trusthandler.h @@ -0,0 +1,81 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 CORE_TRUSTHANDLER_H +#define CORE_TRUSTHANDLER_H + +#include + +#include +#include + +namespace Core { +class Account; + +class TrustHandler : public QXmppTrustStorage { +public: + TrustHandler(Account* account); + ~TrustHandler(); + + typedef QMultiHash MultySB; + typedef QHash HashSM; + typedef const QList& CLSR; + typedef const QList& CLBAR; + typedef const QString& CSR; + typedef QXmpp::TrustLevel TL; + typedef QHash> HSHBTL; + + typedef std::map Keys; + typedef DataBase::Cache KeyCache; + + virtual QFuture resetAll(CSR encryption); + virtual QFuture trustLevel(CSR encryption, CSR keyOwnerJid, const QByteArray& keyId); + virtual QFuture setTrustLevel(CSR encryption, CLSR keyOwnerJids, TL oldTrustLevel, TL newTrustLevel); + virtual QFuture setTrustLevel(CSR encryption, const MultySB& keyIds, TL trustLevel); + virtual QFuture hasKey(CSR encryption, CSR keyOwnerJid, QXmpp::TrustLevels trustLevels); + virtual QFuture keys(CSR encryption, CLSR keyOwnerJids, QXmpp::TrustLevels trustLevels); + virtual QFuture> keys(CSR encryption, QXmpp::TrustLevels trustLevels); + virtual QFuture removeKeys(CSR encryption); + virtual QFuture removeKeys(CSR encryption, CSR keyOwnerJid); + virtual QFuture removeKeys(CSR encryption, CLBAR keyIds); + virtual QFuture addKeys(CSR encryption, CSR keyOwnerJid, CLBAR keyIds, TL trustLevel); + virtual QFuture ownKey(CSR encryption); + virtual QFuture resetOwnKey(CSR encryption); + virtual QFuture setOwnKey(CSR encryption, const QByteArray& keyId); + virtual QFuture securityPolicy(CSR encryption); + virtual QFuture resetSecurityPolicy(CSR encryption); + virtual QFuture setSecurityPolicy(CSR encryption, QXmpp::TrustSecurityPolicy securityPolicy); + + static TL convert(Shared::TrustLevel level); + static Shared::TrustLevel convert(TL level); + +private: + static QFuture emptyVoidFuture(); + KeyCache* createNewCache(const QString& encryption); + KeyCache* getCache(const QString& encryption); + +private: + Account* acc; + DataBase db; + QFile protocols; + DataBase::Cache* securityPolicies; + DataBase::Cache* ownKeys; + std::map keysByProtocol; +}; + +} + +#endif // CORE_TRUSTHANDLER_H diff --git a/core/handlers/vcardhandler.cpp b/core/handlers/vcardhandler.cpp index 24cb6ee..bde0e64 100644 --- a/core/handlers/vcardhandler.cpp +++ b/core/handlers/vcardhandler.cpp @@ -25,10 +25,6 @@ Core::VCardHandler::VCardHandler(Account* account): avatarHash(), avatarType() { - connect(acc->vm, &QXmppVCardManager::vCardReceived, this, &VCardHandler::onVCardReceived); - //for some reason it doesn't work, launching from common handler - //connect(acc->vm, &QXmppVCardManager::clientVCardReceived, this, &VCardHandler::onOwnVCardReceived); - QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); path += "/" + acc->name; QDir dir(path); @@ -67,6 +63,15 @@ Core::VCardHandler::VCardHandler(Account* account): avatarType = type; } } +} + +Core::VCardHandler::~VCardHandler() {} + +void Core::VCardHandler::initialize() { + connect(acc->vm, &QXmppVCardManager::vCardReceived, this, &VCardHandler::onVCardReceived); + //for some reason it doesn't work, launching from common handler + //connect(acc->vm, &QXmppVCardManager::clientVCardReceived, this, &VCardHandler::onOwnVCardReceived); + if (avatarType.size() != 0) { acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto); acc->presence.setPhotoHash(avatarHash.toUtf8()); @@ -75,10 +80,6 @@ Core::VCardHandler::VCardHandler(Account* account): } } -Core::VCardHandler::~VCardHandler() -{ - -} void Core::VCardHandler::onVCardReceived(const QXmppVCardIq& card) { diff --git a/core/handlers/vcardhandler.h b/core/handlers/vcardhandler.h index ee61d0a..f774cd5 100644 --- a/core/handlers/vcardhandler.h +++ b/core/handlers/vcardhandler.h @@ -48,6 +48,8 @@ public: void uploadVCard(const Shared::VCard& card); QString getAvatarPath() const; + void initialize(); + private slots: void onVCardReceived(const QXmppVCardIq& card); void onOwnVCardReceived(const QXmppVCardIq& card); diff --git a/core/storage/CMakeLists.txt b/core/storage/CMakeLists.txt index 2da3c67..238b59a 100644 --- a/core/storage/CMakeLists.txt +++ b/core/storage/CMakeLists.txt @@ -1,10 +1,10 @@ target_sources(squawk PRIVATE archive.cpp archive.h - storage.hpp - storage.h +# storage.hpp +# storage.h urlstorage.cpp urlstorage.h - cache.hpp - cache.h +# cache.hpp +# cache.h ) diff --git a/external/qxmpp b/external/qxmpp index f6e7591..befab2f 160000 --- a/external/qxmpp +++ b/external/qxmpp @@ -1 +1 @@ -Subproject commit f6e7591e21b4c55319918ac296b2341b2e9f1988 +Subproject commit befab2fe2e71330170bba48f173258be724c65b9 diff --git a/external/storage b/external/storage index 08daad4..a79dae8 160000 --- a/external/storage +++ b/external/storage @@ -1 +1 @@ -Subproject commit 08daad48ad36d9f12dc6485a085190e31e4cf49e +Subproject commit a79dae8fd07b36446041f6f85e2ad42cf5ae5264 diff --git a/shared/enums.h b/shared/enums.h index 273a22d..a32b57e 100644 --- a/shared/enums.h +++ b/shared/enums.h @@ -124,5 +124,25 @@ enum class Support { }; Q_ENUM_NS(Support) +enum class TrustLevel { + /// The key's trust is not decided. + Undecided, + /// The key is automatically distrusted (e.g., by the security policy TOAKAFA). + /// \see SecurityPolicy + AutomaticallyDistrusted, + /// The key is manually distrusted (e.g., by clicking a button or \xep{0450, Automatic Trust + /// Management (ATM)}). + ManuallyDistrusted, + /// The key is automatically trusted (e.g., by the client for all keys of a bare JID until one + /// of it is authenticated). + AutomaticallyTrusted, + /// The key is manually trusted (e.g., by clicking a button). + ManuallyTrusted, + /// The key is authenticated (e.g., by QR code scanning or \xep{0450, Automatic Trust + /// Management (ATM)}). + Authenticated +}; +Q_ENUM_NS(TrustLevel) + } #endif // SHARED_ENUMS_H From dfe72ca36c998e39b8bd4a1e9f44adcfe1b28089 Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 27 Dec 2022 01:01:01 +0300 Subject: [PATCH 073/137] support of the new managers in account code, new states, new lambdas, even launches now, even receives some bundles --- core/account.cpp | 74 +++++++++++++++++++++++++++--- core/account.h | 4 ++ core/handlers/discoveryhandler.cpp | 16 ++++++- core/handlers/discoveryhandler.h | 1 + core/handlers/omemohandler.cpp | 8 +++- core/handlers/omemohandler.h | 2 + core/squawk.cpp | 11 +++-- core/squawk.h | 9 ---- main/main.cpp | 3 ++ shared/enums.h | 3 +- shared/global.cpp | 3 +- shared/icons.h | 4 ++ 12 files changed, 113 insertions(+), 25 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index 4b25737..e1bbfb9 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -51,6 +51,7 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& um(new QXmppUploadRequestManager()), dm(client.findExtension()), rcpm(new QXmppMessageReceiptManager()), + psm(new QXmppPubSubManager()), reconnectScheduled(false), reconnectTimer(new QTimer), network(p_net), @@ -58,7 +59,8 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& lastError(Error::none), pepSupport(Shared::Support::unknown), active(p_active), - notReadyPassword(false) + notReadyPassword(false), + loadingOmemo(false) { config.setUser(p_login); config.setDomain(p_server); @@ -99,10 +101,31 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& client.addExtension(rcpm); QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived); + client.addExtension(psm); + #ifdef WITH_OMEMO client.addExtension(tm); client.addExtension(om); - qDebug("Added OMEMO manager"); + + if (oh->hasOwnDevice()) { + QFuture future = om->load(); + loadingOmemo = true; + + QFutureWatcher *watcher = new QFutureWatcher(this); + QObject::connect(watcher, &QFutureWatcherBase::finished, [watcher, this] () { + loadingOmemo = false; + if (state == Shared::ConnectionState::scheduled) { + client.connectToServer(config, presence); + } + if (watcher->result()) { + qDebug() << "successfully loaded OMEMO data for account" << getName(); + } else { + qDebug() << "couldn't load OMEMO data for account" << getName(); + } + watcher->deleteLater(); + }); + watcher->setFuture(future); + } #endif reconnectTimer->setSingleShot(true); @@ -162,7 +185,12 @@ void Core::Account::connect() if (notReadyPassword) { emit needPassword(); } else { - client.connectToServer(config, presence); + if (loadingOmemo) { + state = Shared::ConnectionState::scheduled; + emit connectionStateChanged(state); + } else { + client.connectToServer(config, presence); + } } } else { @@ -186,7 +214,12 @@ void Core::Account::disconnect() } if (state != Shared::ConnectionState::disconnected) { //rh->clearConferences(); - client.disconnectFromServer(); + if (state != Shared::ConnectionState::scheduled) { + client.disconnectFromServer(); + } else { + state = Shared::ConnectionState::disconnected; + emit connectionStateChanged(state); + } } } @@ -199,9 +232,29 @@ void Core::Account::onClientStateChange(QXmppClient::State st) Shared::ConnectionState os = state; state = Shared::ConnectionState::connected; if (os == Shared::ConnectionState::connecting) { - qDebug() << "running service discovery for account" << name; - dm->requestItems(getServer()); - dm->requestInfo(getServer()); +#ifdef WITH_OMEMO + if (!oh->hasOwnDevice()) { + qDebug() << "setting up OMEMO data for account" << getName(); + QFuture future = om->setUp(); + QFutureWatcher *watcher = new QFutureWatcher(this); + QObject::connect(watcher, &QFutureWatcherBase::finished, [watcher, this] () { + if (watcher->result()) { + qDebug() << "successfully set up OMEMO data for account" << getName(); + } else { + qDebug() << "couldn't set up OMEMO data for account" << getName(); + } + watcher->deleteLater(); + if (state == Shared::ConnectionState::connected) { + runDiscoveryService(); + } + }); + watcher->setFuture(future); + } else { + runDiscoveryService(); + } +#else + runDiscoveryService(); +#endif } lastError = Error::none; emit connectionStateChanged(state); @@ -270,6 +323,13 @@ void Core::Account::setAvailability(Shared::Availability avail) } } +void Core::Account::runDiscoveryService() { + qDebug() << "running service discovery for account" << name; + dm->requestItems(getServer()); + dm->requestInfo(getServer()); +} + + void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) { QString id = p_presence.from(); diff --git a/core/account.h b/core/account.h index 7e5e3b0..393b6e6 100644 --- a/core/account.h +++ b/core/account.h @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -195,6 +196,7 @@ private: QXmppUploadRequestManager* um; QXmppDiscoveryManager* dm; QXmppMessageReceiptManager* rcpm; + QXmppPubSubManager* psm; bool reconnectScheduled; QTimer* reconnectTimer; @@ -204,6 +206,7 @@ private: Shared::Support pepSupport; bool active; bool notReadyPassword; + bool loadingOmemo; private slots: void onClientStateChange(QXmppClient::State state); @@ -223,6 +226,7 @@ private: void handleDisconnection(); void onReconnectTimer(); void setPepSupport(Shared::Support support); + void runDiscoveryService(); }; } diff --git a/core/handlers/discoveryhandler.cpp b/core/handlers/discoveryhandler.cpp index 2464f07..20e982c 100644 --- a/core/handlers/discoveryhandler.cpp +++ b/core/handlers/discoveryhandler.cpp @@ -21,7 +21,8 @@ Core::DiscoveryHandler::DiscoveryHandler(Core::Account* account): QObject(), - acc(account) {} + acc(account), + omemoToCarbonsConnected (false) {} Core::DiscoveryHandler::~DiscoveryHandler() {} @@ -79,6 +80,19 @@ void Core::DiscoveryHandler::onInfoReceived(const QXmppDiscoveryIq& info) if (enableCC) { qDebug() << "Enabling carbon copies for account" << accName; acc->cm->setCarbonsEnabled(true); +#ifdef WITH_OMEMO + if (!omemoToCarbonsConnected && acc->oh->hasOwnDevice()) { + // connect(this, &QXmppCarbonManager::messageSent, acc->om, &QXmppOmemoManager::handleMessage); + // connect(this, &QXmppCarbonManager::messageReceived, acc->om, &QXmppOmemoManager::handleMessage); + omemoToCarbonsConnected = true; + } + } else { + if (omemoToCarbonsConnected) { + // disconnect(this, &QXmppCarbonManager::messageSent, acc->om, &QXmppOmemoManager::handleMessage); + // disconnect(this, &QXmppCarbonManager::messageReceived, acc->om, &QXmppOmemoManager::handleMessage); + omemoToCarbonsConnected = false; + } +#endif } qDebug() << "Requesting account" << accName << "capabilities"; diff --git a/core/handlers/discoveryhandler.h b/core/handlers/discoveryhandler.h index e4ef265..4bf5baf 100644 --- a/core/handlers/discoveryhandler.h +++ b/core/handlers/discoveryhandler.h @@ -40,6 +40,7 @@ private slots: private: Account* acc; + bool omemoToCarbonsConnected; }; } diff --git a/core/handlers/omemohandler.cpp b/core/handlers/omemohandler.cpp index 25ac752..8ce5932 100644 --- a/core/handlers/omemohandler.cpp +++ b/core/handlers/omemohandler.cpp @@ -31,7 +31,7 @@ Core::OmemoHandler::OmemoHandler(Account* account) : db.open(); try { QVariant own = meta->getRecord("ownDevice"); - ownDevice.value() = own.value(); + ownDevice = own.value(); qDebug() << "Successfully found own device omemo data for account" << acc->getName(); } catch (const DataBase::NotFound& e) { qDebug() << "No device omemo data was found for account" << acc->getName(); @@ -42,6 +42,10 @@ Core::OmemoHandler::~OmemoHandler() { db.close(); } +bool Core::OmemoHandler::hasOwnDevice() { + return ownDevice.has_value(); +} + QFuture Core::OmemoHandler::emptyVoidFuture() { QFutureInterface result(QFutureInterfaceBase::Started); result.reportFinished(); @@ -101,7 +105,7 @@ QFuture Core::OmemoHandler::addPreKeyPairs(const QHash Core::OmemoHandler::addSignedPreKeyPair(uint32_t keyId, const QXmppOmemoStorage::SignedPreKeyPair& keyPair) { - signedPreKeyPairs->addRecord(keyId, keyPair); + signedPreKeyPairs->forceRecord(keyId, keyPair); return emptyVoidFuture(); } diff --git a/core/handlers/omemohandler.h b/core/handlers/omemohandler.h index 537dfec..bae14cb 100644 --- a/core/handlers/omemohandler.h +++ b/core/handlers/omemohandler.h @@ -49,6 +49,8 @@ public: QFuture resetAll() override; + bool hasOwnDevice(); + private: static QFuture emptyVoidFuture(); diff --git a/core/squawk.cpp b/core/squawk.cpp index 3b976b0..6600fcb 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -265,10 +265,13 @@ void Core::Squawk::disconnectAccount(const QString& account) void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_state) { Account* acc = static_cast(sender()); - emit changeAccount(acc->getName(), { - {"state", QVariant::fromValue(p_state)}, - {"error", ""} - }); + QMap changes = { + {"state", QVariant::fromValue(p_state)} + }; + if (acc->getLastError() == Account::Error::none) { + changes.insert("error", ""); + } + emit changeAccount(acc->getName(), changes); #ifdef WITH_KWALLET if (p_state == Shared::ConnectionState::connected) { diff --git a/core/squawk.h b/core/squawk.h index cdaaf6e..da4aa7e 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -187,15 +187,6 @@ private slots: private: void readSettings(); - void parseAccount( - const QString& login, - const QString& server, - const QString& password, - const QString& name, - const QString& resource, - bool active, - Shared::AccountPassword passwordType - ); static const quint64 passwordHash = 0x08d054225ac4871d; }; diff --git a/main/main.cpp b/main/main.cpp index 58063e0..020d755 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -52,6 +52,9 @@ int main(int argc, char *argv[]) qRegisterMetaType("Shared::Availability"); #ifdef WITH_OMEMO qRegisterMetaType("QXmppOmemoStorage::OwnDevice"); + qRegisterMetaTypeStreamOperators("QXmppOmemoStorage::OwnDevice"); + qRegisterMetaType("QXmppOmemoStorage::Device"); + qRegisterMetaType("QXmppOmemoStorage::SignedPreKeyPair"); #endif QApplication app(argc, argv); diff --git a/shared/enums.h b/shared/enums.h index a32b57e..7a1f092 100644 --- a/shared/enums.h +++ b/shared/enums.h @@ -29,12 +29,13 @@ Q_NAMESPACE enum class ConnectionState { disconnected, + scheduled, connecting, connected, error }; Q_ENUM_NS(ConnectionState) -static const std::deque connectionStateThemeIcons = {"state-offline", "state-sync", "state-ok", "state-error"}; +static const std::deque connectionStateThemeIcons = {"state-offline", "state-sync", "state-sync", "state-ok", "state-error"}; static const ConnectionState ConnectionStateHighest = ConnectionState::error; static const ConnectionState ConnectionStateLowest = ConnectionState::disconnected; diff --git a/shared/global.cpp b/shared/global.cpp index 6519952..a6f17dc 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -49,7 +49,8 @@ Shared::Global::Global(): }), connectionState({ tr("Disconnected", "ConnectionState"), - tr("Connecting", "ConnectionState"), + tr("Scheduled", "ConnectionState"), + tr("Connecting", "ConnectionState"), tr("Connected", "ConnectionState"), tr("Error", "ConnectionState") }), diff --git a/shared/icons.h b/shared/icons.h index 540d3e9..cf31b3b 100644 --- a/shared/icons.h +++ b/shared/icons.h @@ -48,6 +48,7 @@ static const std::deque fallbackSubscriptionStateThemeIconsLightBig = { static const std::deque fallbackConnectionStateThemeIconsLightBig = { ":images/fallback/light/big/state-offline.svg", ":images/fallback/light/big/state-sync.svg", + ":images/fallback/light/big/state-sync.svg", ":images/fallback/light/big/state-ok.svg", ":images/fallback/light/big/state-error.svg" }; @@ -73,6 +74,7 @@ static const std::deque fallbackSubscriptionStateThemeIconsLightSmall = static const std::deque fallbackConnectionStateThemeIconsLightSmall = { ":images/fallback/light/small/state-offline.svg", ":images/fallback/light/small/state-sync.svg", + ":images/fallback/light/small/state-sync.svg", ":images/fallback/light/small/state-ok.svg", ":images/fallback/light/small/state-error.svg" }; @@ -98,6 +100,7 @@ static const std::deque fallbackSubscriptionStateThemeIconsDarkBig = { static const std::deque fallbackConnectionStateThemeIconsDarkBig = { ":images/fallback/dark/big/state-offline.svg", ":images/fallback/dark/big/state-sync.svg", + ":images/fallback/dark/big/state-sync.svg", ":images/fallback/dark/big/state-ok.svg", ":images/fallback/dark/big/state-error.svg" }; @@ -123,6 +126,7 @@ static const std::deque fallbackSubscriptionStateThemeIconsDarkSmall = static const std::deque fallbackConnectionStateThemeIconsDarkSmall = { ":images/fallback/dark/small/state-offline.svg", ":images/fallback/dark/small/state-sync.svg", + ":images/fallback/dark/small/state-sync.svg", ":images/fallback/dark/small/state-ok.svg", ":images/fallback/dark/small/state-error.svg" }; From 758a9d95f3e738f9c352b37e5753ff510e185a68 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 29 Dec 2022 01:41:59 +0300 Subject: [PATCH 074/137] replaced one structure, stored omemo support in Global object --- core/handlers/omemohandler.cpp | 25 ++++++------------------- core/handlers/omemohandler.h | 10 ++++------ main/main.cpp | 1 - shared/global.cpp | 6 ++++++ shared/global.h | 1 + 5 files changed, 17 insertions(+), 26 deletions(-) diff --git a/core/handlers/omemohandler.cpp b/core/handlers/omemohandler.cpp index 8ce5932..57d9749 100644 --- a/core/handlers/omemohandler.cpp +++ b/core/handlers/omemohandler.cpp @@ -26,7 +26,7 @@ Core::OmemoHandler::OmemoHandler(Account* account) : meta(db.addCache("meta")), devices(db.addCache>("devices")), preKeyPairs(db.addCache("preKeyPairs")), - signedPreKeyPairs(db.addCache("signedPreKeyPairs")) + signedPreKeyPairs(db.addCache("signedPreKeyPairs")) { db.open(); try { @@ -61,9 +61,10 @@ QFuture Core::OmemoHandler::allData() { data.preKeyPairs.insert(pair.first, pair.second); } - std::map spre = signedPreKeyPairs->readAll(); - for (const std::pair& pair : spre) { - data.signedPreKeyPairs.insert(pair.first, pair.second); + std::map spre = signedPreKeyPairs->readAll(); + for (const std::pair& pair : spre) { + QXmppOmemoStorage::SignedPreKeyPair qxpair = {pair.second.first, pair.second.second}; + data.signedPreKeyPairs.insert(pair.first, qxpair); } std::map> devs = devices->readAll(); @@ -105,7 +106,7 @@ QFuture Core::OmemoHandler::addPreKeyPairs(const QHash Core::OmemoHandler::addSignedPreKeyPair(uint32_t keyId, const QXmppOmemoStorage::SignedPreKeyPair& keyPair) { - signedPreKeyPairs->forceRecord(keyId, keyPair); + signedPreKeyPairs->forceRecord(keyId, std::make_pair(keyPair.creationDate, keyPair.data)); return emptyVoidFuture(); } @@ -202,17 +203,3 @@ QDataStream & operator << (QDataStream& out, const QXmppOmemoStorage::OwnDevice& return out; } - -QDataStream & operator >> (QDataStream& in, QXmppOmemoStorage::SignedPreKeyPair& pair) { - in >> pair.creationDate; - in >> pair.data; - - return in; -} - -QDataStream & operator << (QDataStream& out, const QXmppOmemoStorage::SignedPreKeyPair& pair) { - out << pair.creationDate; - out << pair.data; - - return out; -} diff --git a/core/handlers/omemohandler.h b/core/handlers/omemohandler.h index bae14cb..cea9603 100644 --- a/core/handlers/omemohandler.h +++ b/core/handlers/omemohandler.h @@ -22,7 +22,6 @@ Q_DECLARE_METATYPE(QXmppOmemoStorage::OwnDevice); Q_DECLARE_METATYPE(QXmppOmemoStorage::Device); -Q_DECLARE_METATYPE(QXmppOmemoStorage::SignedPreKeyPair); namespace Core { class Account; @@ -30,6 +29,8 @@ class Account; class OmemoHandler : public QXmppOmemoStorage { public: + typedef std::pair SignedPreKeyPair; + OmemoHandler(Account* account); ~OmemoHandler() override; @@ -37,7 +38,7 @@ public: QFuture setOwnDevice(const std::optional &device) override; - QFuture addSignedPreKeyPair(uint32_t keyId, const SignedPreKeyPair &keyPair) override; + QFuture addSignedPreKeyPair(uint32_t keyId, const QXmppOmemoStorage::SignedPreKeyPair &keyPair) override; QFuture removeSignedPreKeyPair(uint32_t keyId) override; QFuture addPreKeyPairs(const QHash &keyPairs) override; @@ -61,7 +62,7 @@ private: DataBase::Cache* meta; DataBase::Cache>* devices; DataBase::Cache* preKeyPairs; - DataBase::Cache* signedPreKeyPairs; + DataBase::Cache* signedPreKeyPairs; }; } @@ -72,7 +73,4 @@ QDataStream& operator >> (QDataStream &in, QXmppOmemoStorage::Device& device); QDataStream& operator << (QDataStream &out, const QXmppOmemoStorage::OwnDevice& device); QDataStream& operator >> (QDataStream &in, QXmppOmemoStorage::OwnDevice& device); -QDataStream& operator << (QDataStream &out, const QXmppOmemoStorage::SignedPreKeyPair& device); -QDataStream& operator >> (QDataStream &in, QXmppOmemoStorage::SignedPreKeyPair& device); - #endif // CORE_OMEMOHANDLER_H diff --git a/main/main.cpp b/main/main.cpp index 020d755..2958b66 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -54,7 +54,6 @@ int main(int argc, char *argv[]) qRegisterMetaType("QXmppOmemoStorage::OwnDevice"); qRegisterMetaTypeStreamOperators("QXmppOmemoStorage::OwnDevice"); qRegisterMetaType("QXmppOmemoStorage::Device"); - qRegisterMetaType("QXmppOmemoStorage::SignedPreKeyPair"); #endif QApplication app(argc, argv); diff --git a/shared/global.cpp b/shared/global.cpp index a6f17dc..2e2978a 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -20,6 +20,11 @@ #include "enums.h" #include "ui/models/roster.h" +#ifdef WITH_OMEMO +constexpr bool OMEMO_SUPPORT = true; +#else +constexpr bool OMEMO_SUPPORT = false +#endif Shared::Global* Shared::Global::instance = 0; const std::set Shared::Global::supportedImagesExts = {"png", "jpg", "webp", "jpeg", "gif", "svg"}; @@ -96,6 +101,7 @@ Shared::Global::Global(): }), defaultSystemStyle(QApplication::style()->objectName()), defaultSystemPalette(QApplication::palette()), + omemoSupport(OMEMO_SUPPORT), pluginSupport({ {"KWallet", false}, {"openFileManagerWindowJob", false}, diff --git a/shared/global.h b/shared/global.h index ebed931..d212cd1 100644 --- a/shared/global.h +++ b/shared/global.h @@ -99,6 +99,7 @@ namespace Shared { static QString getColorSchemeName(const QString& path); static void setTheme(const QString& path); static void setStyle(const QString& style); + const bool omemoSupport; template static T fromInt(int src); From b45a73b723039caf61c74143dd16f9ab484ebd14 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 1 Jan 2023 20:25:51 +0300 Subject: [PATCH 075/137] some initial work and thoughts about encryption --- core/handlers/trusthandler.cpp | 24 ++--- main/main.cpp | 1 + shared/CMakeLists.txt | 2 + shared/enums.h | 17 +-- shared/keyinfo.cpp | 46 +++++++++ shared/keyinfo.h | 55 ++++++++++ shared/shared.h | 1 + ui/widgets/vcard/CMakeLists.txt | 4 + ui/widgets/vcard/omemo/CMakeLists.txt | 5 + ui/widgets/vcard/omemo/omemo.cpp | 20 ++++ ui/widgets/vcard/omemo/omemo.h | 43 ++++++++ ui/widgets/vcard/omemo/omemo.ui | 142 ++++++++++++++++++++++++++ 12 files changed, 342 insertions(+), 18 deletions(-) create mode 100644 shared/keyinfo.cpp create mode 100644 shared/keyinfo.h create mode 100644 ui/widgets/vcard/omemo/CMakeLists.txt create mode 100644 ui/widgets/vcard/omemo/omemo.cpp create mode 100644 ui/widgets/vcard/omemo/omemo.h create mode 100644 ui/widgets/vcard/omemo/omemo.ui diff --git a/core/handlers/trusthandler.cpp b/core/handlers/trusthandler.cpp index 2c0be29..caaaf0e 100644 --- a/core/handlers/trusthandler.cpp +++ b/core/handlers/trusthandler.cpp @@ -368,23 +368,23 @@ QFuture TrustHandler::setSecurityPolicy( Shared::TrustLevel Core::TrustHandler::convert(Core::TrustHandler::TL level) { switch (level) { - case QXmpp::TrustLevel::Undecided: return Shared::TrustLevel::Undecided; - case QXmpp::TrustLevel::AutomaticallyDistrusted: return Shared::TrustLevel::AutomaticallyDistrusted; - case QXmpp::TrustLevel::ManuallyDistrusted: return Shared::TrustLevel::ManuallyDistrusted; - case QXmpp::TrustLevel::AutomaticallyTrusted: return Shared::TrustLevel::AutomaticallyTrusted; - case QXmpp::TrustLevel::ManuallyTrusted: return Shared::TrustLevel::ManuallyTrusted; - case QXmpp::TrustLevel::Authenticated: return Shared::TrustLevel::Authenticated; + case QXmpp::TrustLevel::Undecided: return Shared::TrustLevel::undecided; + case QXmpp::TrustLevel::AutomaticallyDistrusted: return Shared::TrustLevel::automaticallyDistrusted; + case QXmpp::TrustLevel::ManuallyDistrusted: return Shared::TrustLevel::manuallyDistrusted; + case QXmpp::TrustLevel::AutomaticallyTrusted: return Shared::TrustLevel::automaticallyTrusted; + case QXmpp::TrustLevel::ManuallyTrusted: return Shared::TrustLevel::manuallyTrusted; + case QXmpp::TrustLevel::Authenticated: return Shared::TrustLevel::authenticated; } } Core::TrustHandler::TL Core::TrustHandler::convert(Shared::TrustLevel level) { switch (level) { - case Shared::TrustLevel::Undecided: return QXmpp::TrustLevel::Undecided; - case Shared::TrustLevel::AutomaticallyDistrusted: return QXmpp::TrustLevel::AutomaticallyDistrusted; - case Shared::TrustLevel::ManuallyDistrusted: return QXmpp::TrustLevel::ManuallyDistrusted; - case Shared::TrustLevel::AutomaticallyTrusted: return QXmpp::TrustLevel::AutomaticallyTrusted; - case Shared::TrustLevel::ManuallyTrusted: return QXmpp::TrustLevel::ManuallyTrusted; - case Shared::TrustLevel::Authenticated: return QXmpp::TrustLevel::Authenticated; + case Shared::TrustLevel::undecided: return QXmpp::TrustLevel::Undecided; + case Shared::TrustLevel::automaticallyDistrusted: return QXmpp::TrustLevel::AutomaticallyDistrusted; + case Shared::TrustLevel::manuallyDistrusted: return QXmpp::TrustLevel::ManuallyDistrusted; + case Shared::TrustLevel::automaticallyTrusted: return QXmpp::TrustLevel::AutomaticallyTrusted; + case Shared::TrustLevel::manuallyTrusted: return QXmpp::TrustLevel::ManuallyTrusted; + case Shared::TrustLevel::authenticated: return QXmpp::TrustLevel::Authenticated; } } diff --git a/main/main.cpp b/main/main.cpp index 2958b66..3e9add3 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -50,6 +50,7 @@ int main(int argc, char *argv[]) qRegisterMetaType>("QSet"); qRegisterMetaType("Shared::ConnectionState"); qRegisterMetaType("Shared::Availability"); + qRegisterMetaType("Shared::KeyInfo"); #ifdef WITH_OMEMO qRegisterMetaType("QXmppOmemoStorage::OwnDevice"); qRegisterMetaTypeStreamOperators("QXmppOmemoStorage::OwnDevice"); diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index 51c599f..a227163 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -26,4 +26,6 @@ target_sources(squawk PRIVATE form.cpp field.h field.cpp + keyinfo.cpp + keyinfo.h ) diff --git a/shared/enums.h b/shared/enums.h index 7a1f092..7273b2e 100644 --- a/shared/enums.h +++ b/shared/enums.h @@ -127,23 +127,28 @@ Q_ENUM_NS(Support) enum class TrustLevel { /// The key's trust is not decided. - Undecided, + undecided, /// The key is automatically distrusted (e.g., by the security policy TOAKAFA). /// \see SecurityPolicy - AutomaticallyDistrusted, + automaticallyDistrusted, /// The key is manually distrusted (e.g., by clicking a button or \xep{0450, Automatic Trust /// Management (ATM)}). - ManuallyDistrusted, + manuallyDistrusted, /// The key is automatically trusted (e.g., by the client for all keys of a bare JID until one /// of it is authenticated). - AutomaticallyTrusted, + automaticallyTrusted, /// The key is manually trusted (e.g., by clicking a button). - ManuallyTrusted, + manuallyTrusted, /// The key is authenticated (e.g., by QR code scanning or \xep{0450, Automatic Trust /// Management (ATM)}). - Authenticated + authenticated }; Q_ENUM_NS(TrustLevel) +enum class EncryptionProtocol { + omemo +}; +Q_ENUM_NS(EncryptionProtocol) + } #endif // SHARED_ENUMS_H diff --git a/shared/keyinfo.cpp b/shared/keyinfo.cpp new file mode 100644 index 0000000..0c09d1f --- /dev/null +++ b/shared/keyinfo.cpp @@ -0,0 +1,46 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "keyinfo.h" + +using namespace Shared; + +Shared::KeyInfo::KeyInfo( + uint32_t p_id, + const QByteArray& p_fingerPrint, + const QString& p_label, + Shared::TrustLevel p_trustLevel, + Shared::EncryptionProtocol p_protocol, + bool p_currentDevice +): + id(p_id), + fingerPrint(p_fingerPrint), + label(p_label), + trustLevel(p_trustLevel), + protocol(p_protocol), + currentDevice(p_currentDevice) +{ +} + +Shared::KeyInfo::KeyInfo(): + id(0), + fingerPrint(), + label(), + trustLevel(TrustLevel::Undecided), + protocol(EncryptionProtocol::omemo), + currentDevice(false) +{ +} diff --git a/shared/keyinfo.h b/shared/keyinfo.h new file mode 100644 index 0000000..b5dc793 --- /dev/null +++ b/shared/keyinfo.h @@ -0,0 +1,55 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 SHARED_KEYINFO_H +#define SHARED_KEYINFO_H + +#include +#include + +#include + +#include "enums.h" + +namespace Shared { + +class KeyInfo +{ +public: + KeyInfo( + uint32_t id, + const QByteArray& + fingerPrint, + const QString& label, + TrustLevel trustLevel, + EncryptionProtocol protocol = EncryptionProtocol::omemo, + bool currentDevice = false + ); + KeyInfo(); + +private: + uint32_t id; + QByteArray fingerPrint; + QString label; + TrustLevel trustLevel; + EncryptionProtocol protocol; + bool currentDevice; + +}; + +} + +#endif // SHARED_KEYINFO_H diff --git a/shared/shared.h b/shared/shared.h index 1e86c5a..68e5c8d 100644 --- a/shared/shared.h +++ b/shared/shared.h @@ -26,5 +26,6 @@ #include "messageinfo.h" #include "utils.h" #include "vcard.h" +#include "keyinfo.h" #endif // SHARED_H diff --git a/ui/widgets/vcard/CMakeLists.txt b/ui/widgets/vcard/CMakeLists.txt index 51cbaab..c37f4c6 100644 --- a/ui/widgets/vcard/CMakeLists.txt +++ b/ui/widgets/vcard/CMakeLists.txt @@ -7,3 +7,7 @@ target_sources(squawk PRIVATE vcard.h vcard.ui ) + +if (WITH_OMEMO) + add_subdirectory(omemo) +endif() diff --git a/ui/widgets/vcard/omemo/CMakeLists.txt b/ui/widgets/vcard/omemo/CMakeLists.txt new file mode 100644 index 0000000..1468841 --- /dev/null +++ b/ui/widgets/vcard/omemo/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(squawk PRIVATE + omemo.cpp + omemo.h + omemo.ui +) diff --git a/ui/widgets/vcard/omemo/omemo.cpp b/ui/widgets/vcard/omemo/omemo.cpp new file mode 100644 index 0000000..7e39ec0 --- /dev/null +++ b/ui/widgets/vcard/omemo/omemo.cpp @@ -0,0 +1,20 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "omemo.h" +#include "ui_omemo.h" + +using namespace Ui; diff --git a/ui/widgets/vcard/omemo/omemo.h b/ui/widgets/vcard/omemo/omemo.h new file mode 100644 index 0000000..3dccbd3 --- /dev/null +++ b/ui/widgets/vcard/omemo/omemo.h @@ -0,0 +1,43 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 UI_OMEMO_H +#define UI_OMEMO_H + +#include +#include + +namespace Ui { + +namespace Ui +{ +class Omemo; +} + +/** + * @todo write docs + */ +class Omemo : public QWidget +{ + Q_OBJECT + +private: + QScopedPointer m_ui; +}; + +} + +#endif // UI_OMEMO_H diff --git a/ui/widgets/vcard/omemo/omemo.ui b/ui/widgets/vcard/omemo/omemo.ui new file mode 100644 index 0000000..095a3e0 --- /dev/null +++ b/ui/widgets/vcard/omemo/omemo.ui @@ -0,0 +1,142 @@ + + + Ui::Omemo + + + + 0 + 0 + 473 + 657 + + + + + 0 + + + 0 + + + 6 + + + 0 + + + 0 + + + + + + 24 + 75 + true + + + + OMEMO + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + true + + + + + 0 + 0 + 473 + 592 + + + + + + + + + + Qt::Horizontal + + + + + + + + 16 + 75 + true + + + + Active keys + + + + + + + + 16 + 75 + true + + + + Unused keys + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + From 5aa0f4bca90a701238260eb28e082b124fcac0cc Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 3 Jan 2023 18:27:03 +0300 Subject: [PATCH 076/137] some initial classes for keys form --- shared/keyinfo.cpp | 23 ++- shared/keyinfo.h | 5 +- ui/widgets/messageline/messagefeed.cpp | 5 +- ui/widgets/vcard/omemo/CMakeLists.txt | 2 + ui/widgets/vcard/omemo/keysmodel.cpp | 71 +++++++ ui/widgets/vcard/omemo/keysmodel.h | 58 ++++++ ui/widgets/vcard/omemo/omemo.cpp | 29 ++- ui/widgets/vcard/omemo/omemo.h | 24 ++- ui/widgets/vcard/omemo/omemo.ui | 4 +- ui/widgets/vcard/vcard.cpp | 276 +++++++++++++------------ ui/widgets/vcard/vcard.h | 23 ++- 11 files changed, 361 insertions(+), 159 deletions(-) create mode 100644 ui/widgets/vcard/omemo/keysmodel.cpp create mode 100644 ui/widgets/vcard/omemo/keysmodel.h diff --git a/shared/keyinfo.cpp b/shared/keyinfo.cpp index 0c09d1f..bff716e 100644 --- a/shared/keyinfo.cpp +++ b/shared/keyinfo.cpp @@ -39,8 +39,29 @@ Shared::KeyInfo::KeyInfo(): id(0), fingerPrint(), label(), - trustLevel(TrustLevel::Undecided), + trustLevel(TrustLevel::undecided), protocol(EncryptionProtocol::omemo), currentDevice(false) { } + +Shared::KeyInfo::KeyInfo(const Shared::KeyInfo& other): + id(other.id), + fingerPrint(other.fingerPrint), + label(other.label), + trustLevel(other.trustLevel), + protocol(other.protocol), + currentDevice(other.currentDevice) +{ +} + +Shared::KeyInfo & Shared::KeyInfo::operator=(const Shared::KeyInfo& other) { + id = other.id; + fingerPrint = other.fingerPrint; + label = other.label; + trustLevel = other.trustLevel; + protocol = other.protocol; + currentDevice = other.currentDevice; + + return *this; +} diff --git a/shared/keyinfo.h b/shared/keyinfo.h index b5dc793..360a8b9 100644 --- a/shared/keyinfo.h +++ b/shared/keyinfo.h @@ -39,8 +39,11 @@ public: bool currentDevice = false ); KeyInfo(); + KeyInfo(const KeyInfo& other); -private: + KeyInfo& operator=(const KeyInfo& other); + +public: uint32_t id; QByteArray fingerPrint; QString label; diff --git a/ui/widgets/messageline/messagefeed.cpp b/ui/widgets/messageline/messagefeed.cpp index ad67bb3..af772fd 100644 --- a/ui/widgets/messageline/messagefeed.cpp +++ b/ui/widgets/messageline/messagefeed.cpp @@ -440,10 +440,7 @@ QModelIndex Models::MessageFeed::index(int row, int column, const QModelIndex& p } } -QHash Models::MessageFeed::roleNames() const -{ - return roles; -} +QHash Models::MessageFeed::roleNames() const {return roles;} bool Models::MessageFeed::sentByMe(const Shared::Message& msg) const { diff --git a/ui/widgets/vcard/omemo/CMakeLists.txt b/ui/widgets/vcard/omemo/CMakeLists.txt index 1468841..19a9fe9 100644 --- a/ui/widgets/vcard/omemo/CMakeLists.txt +++ b/ui/widgets/vcard/omemo/CMakeLists.txt @@ -2,4 +2,6 @@ target_sources(squawk PRIVATE omemo.cpp omemo.h omemo.ui + keysmodel.cpp + keysmodel.h ) diff --git a/ui/widgets/vcard/omemo/keysmodel.cpp b/ui/widgets/vcard/omemo/keysmodel.cpp new file mode 100644 index 0000000..b70fefd --- /dev/null +++ b/ui/widgets/vcard/omemo/keysmodel.cpp @@ -0,0 +1,71 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "keysmodel.h" + +const QHash UI::KeysModel::roles = { + {Label, "label"}, + {FingerPrint, "fingerPrint"}, + {TrustLevel, "trustLevel"} +}; + +UI::KeysModel::KeysModel(QObject* parent): + QAbstractListModel(parent), + keys() +{ +} + +UI::KeysModel::~KeysModel() { + +} + +void UI::KeysModel::addKey(const Shared::KeyInfo& info) { + beginInsertRows(QModelIndex(), keys.size(), keys.size()); + keys.push_back(new Shared::KeyInfo(info)); + endInsertRows(); +} + +QVariant UI::KeysModel::data(const QModelIndex& index, int role) const { + int i = index.row(); + QVariant answer; + + switch (role) { + case Qt::DisplayRole: + answer = keys[i]->label; + break; + } + + return answer; +} + +int UI::KeysModel::rowCount(const QModelIndex& parent) const { + return keys.size(); +} + +QHash UI::KeysModel::roleNames() const {return roles;} + +QModelIndex UI::KeysModel::index(int row, int column, const QModelIndex& parent) const { + if (!hasIndex(row, column, parent)) { + return QModelIndex(); + } + + return createIndex(row, column, keys[row]); +} + + + + + diff --git a/ui/widgets/vcard/omemo/keysmodel.h b/ui/widgets/vcard/omemo/keysmodel.h new file mode 100644 index 0000000..853c5c6 --- /dev/null +++ b/ui/widgets/vcard/omemo/keysmodel.h @@ -0,0 +1,58 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 UI_KEYSMODEL_H +#define UI_KEYSMODEL_H + +#include + +#include + +namespace UI { + +/** + * @todo write docs + */ +class KeysModel : public QAbstractListModel +{ +public: + KeysModel(QObject *parent = nullptr); + ~KeysModel(); + + void addKey(const Shared::KeyInfo& info); + + QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + + QHash roleNames() const override; + QModelIndex index(int row, int column, const QModelIndex & parent) const override; + + enum Roles { + Label = Qt::UserRole + 1, + FingerPrint, + TrustLevel + }; + +private: + std::deque keys; + +private: + static const QHash roles; +}; + +} + +#endif // UI_KEYSMODEL_H diff --git a/ui/widgets/vcard/omemo/omemo.cpp b/ui/widgets/vcard/omemo/omemo.cpp index 7e39ec0..8a1e1cc 100644 --- a/ui/widgets/vcard/omemo/omemo.cpp +++ b/ui/widgets/vcard/omemo/omemo.cpp @@ -17,4 +17,31 @@ #include "omemo.h" #include "ui_omemo.h" -using namespace Ui; +Omemo::Omemo(QWidget* parent): + QWidget(parent), + m_ui(new Ui::Omemo()), + keysModel(), + unusedKeysModel() +{ + m_ui->setupUi(this); + + generateMockData(); + + m_ui->keysView->setModel(&keysModel); + m_ui->unusedKeysView->setModel(&unusedKeysModel); +} + +Omemo::~Omemo() +{ + +} + +void Omemo::generateMockData() +{ + for (int i = 0; i < 5; ++i) { + Shared::KeyInfo info; + info.id = i; + info.label = QString("test_") + std::to_string(i).c_str(); + keysModel.addKey(info); + } +} diff --git a/ui/widgets/vcard/omemo/omemo.h b/ui/widgets/vcard/omemo/omemo.h index 3dccbd3..d01f4a8 100644 --- a/ui/widgets/vcard/omemo/omemo.h +++ b/ui/widgets/vcard/omemo/omemo.h @@ -14,30 +14,32 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#ifndef UI_OMEMO_H -#define UI_OMEMO_H +#ifndef VCARD_OMEMO_H +#define VCARD_OMEMO_H #include #include -namespace Ui { +#include "keysmodel.h" namespace Ui { class Omemo; } -/** - * @todo write docs - */ -class Omemo : public QWidget -{ +class Omemo : public QWidget { Q_OBJECT +public: + Omemo(QWidget* parent = nullptr); + ~Omemo(); + +private: + void generateMockData(); private: QScopedPointer m_ui; + UI::KeysModel keysModel; + UI::KeysModel unusedKeysModel; }; -} - -#endif // UI_OMEMO_H +#endif // VCARD_OMEMO_H diff --git a/ui/widgets/vcard/omemo/omemo.ui b/ui/widgets/vcard/omemo/omemo.ui index 095a3e0..7052486 100644 --- a/ui/widgets/vcard/omemo/omemo.ui +++ b/ui/widgets/vcard/omemo/omemo.ui @@ -1,7 +1,7 @@ - Ui::Omemo - + Omemo + 0 diff --git a/ui/widgets/vcard/vcard.cpp b/ui/widgets/vcard/vcard.cpp index f1cfe3b..a3e877b 100644 --- a/ui/widgets/vcard/vcard.cpp +++ b/ui/widgets/vcard/vcard.cpp @@ -41,105 +41,29 @@ VCard::VCard(const QString& jid, bool edit, QWidget* parent): phones(edit), roleDelegate(new ComboboxDelegate()), phoneTypeDelegate(new ComboboxDelegate()) + +#ifdef WITH_OMEMO + ,omemo(new Omemo()) +#endif { m_ui->setupUi(this); m_ui->jabberID->setText(jid); m_ui->jabberID->setReadOnly(true); - QAction* setAvatar = m_ui->actionSetAvatar; - QAction* clearAvatar = m_ui->actionClearAvatar; - - connect(setAvatar, &QAction::triggered, this, &VCard::onSetAvatar); - connect(clearAvatar, &QAction::triggered, this, &VCard::onClearAvatar); - - setAvatar->setEnabled(true); - clearAvatar->setEnabled(false); - - roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[0].toStdString().c_str())); - roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[1].toStdString().c_str())); - roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[2].toStdString().c_str())); - - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[0].toStdString().c_str())); - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[1].toStdString().c_str())); - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[2].toStdString().c_str())); - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[3].toStdString().c_str())); - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[4].toStdString().c_str())); - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[5].toStdString().c_str())); - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[6].toStdString().c_str())); - - m_ui->emailsView->setContextMenuPolicy(Qt::CustomContextMenu); - m_ui->emailsView->setModel(&emails); - m_ui->emailsView->setItemDelegateForColumn(1, roleDelegate); - m_ui->emailsView->setColumnWidth(2, 25); - m_ui->emailsView->horizontalHeader()->setStretchLastSection(false); - m_ui->emailsView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); - - m_ui->phonesView->setContextMenuPolicy(Qt::CustomContextMenu); - m_ui->phonesView->setModel(&phones); - m_ui->phonesView->setItemDelegateForColumn(1, roleDelegate); - m_ui->phonesView->setItemDelegateForColumn(2, phoneTypeDelegate); - m_ui->phonesView->setColumnWidth(3, 25); - m_ui->phonesView->horizontalHeader()->setStretchLastSection(false); - m_ui->phonesView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); - - connect(m_ui->phonesView, &QWidget::customContextMenuRequested, this, &VCard::onContextMenu); - connect(m_ui->emailsView, &QWidget::customContextMenuRequested, this, &VCard::onContextMenu); - - if (edit) { - avatarMenu = new QMenu(); - m_ui->avatarButton->setMenu(avatarMenu); - avatarMenu->addAction(setAvatar); - avatarMenu->addAction(clearAvatar); - m_ui->title->setText(tr("Account %1 card").arg(jid)); - } else { - m_ui->buttonBox->hide(); - m_ui->fullName->setReadOnly(true); - m_ui->firstName->setReadOnly(true); - m_ui->middleName->setReadOnly(true); - m_ui->lastName->setReadOnly(true); - m_ui->nickName->setReadOnly(true); - m_ui->birthday->setReadOnly(true); - m_ui->organizationName->setReadOnly(true); - m_ui->organizationDepartment->setReadOnly(true); - m_ui->organizationTitle->setReadOnly(true); - m_ui->organizationRole->setReadOnly(true); - m_ui->description->setReadOnly(true); - m_ui->url->setReadOnly(true); - m_ui->title->setText(tr("Contact %1 card").arg(jid)); - } - - connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &VCard::onButtonBoxAccepted); - connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &VCard::close); - - avatarButtonMargins = m_ui->avatarButton->size(); - - int height = m_ui->personalForm->minimumSize().height() - avatarButtonMargins.height(); - m_ui->avatarButton->setIconSize(QSize(height, height)); - - QGridLayout* gr = static_cast(layout()); - gr->addWidget(overlay, 0, 0, 4, 1); - QVBoxLayout* nl = new QVBoxLayout(); - QGraphicsOpacityEffect* opacity = new QGraphicsOpacityEffect(); - opacity->setOpacity(0.8); - overlay->setLayout(nl); - overlay->setBackgroundRole(QPalette::Base); - overlay->setAutoFillBackground(true); - overlay->setGraphicsEffect(opacity); - progressLabel->setAlignment(Qt::AlignCenter); - QFont pf = progressLabel->font(); - pf.setBold(true); - pf.setPointSize(26); - progressLabel->setFont(pf); - progressLabel->setWordWrap(true); - nl->addStretch(); - nl->addWidget(progress); - nl->addWidget(progressLabel); - nl->addStretch(); - overlay->hide(); + initializeActions(); + initializeDelegates(); + initializeViews(); + initializeInteractiveElements(jid); + initializeOverlay(); +#ifdef WITH_OMEMO + initializeOmemo(); +#endif } -VCard::~VCard() -{ +VCard::~VCard() { +#ifdef WITH_OMEMO + delete omemo; +#endif if (editable) { avatarMenu->deleteLater(); } @@ -149,14 +73,12 @@ VCard::~VCard() contextMenu->deleteLater(); } -void VCard::setVCard(const QString& jid, const Shared::VCard& card) -{ +void VCard::setVCard(const QString& jid, const Shared::VCard& card) { m_ui->jabberID->setText(jid); setVCard(card); } -void VCard::setVCard(const Shared::VCard& card) -{ +void VCard::setVCard(const Shared::VCard& card) { m_ui->fullName->setText(card.getFullName()); m_ui->firstName->setText(card.getFirstName()); m_ui->middleName->setText(card.getMiddleName()); @@ -183,13 +105,11 @@ void VCard::setVCard(const Shared::VCard& card) phones.setPhones(phs); } -QString VCard::getJid() const -{ +QString VCard::getJid() const { return m_ui->jabberID->text(); } -void VCard::onButtonBoxAccepted() -{ +void VCard::onButtonBoxAccepted() { Shared::VCard card; card.setFullName(m_ui->fullName->text()); card.setFirstName(m_ui->firstName->text()); @@ -212,16 +132,14 @@ void VCard::onButtonBoxAccepted() emit saveVCard(card); } -void VCard::onClearAvatar() -{ +void VCard::onClearAvatar() { currentAvatarType = Shared::Avatar::empty; currentAvatarPath = ""; updateAvatar(); } -void VCard::onSetAvatar() -{ +void VCard::onSetAvatar() { QFileDialog* d = new QFileDialog(this, tr("Chose your new avatar")); d->setFileMode(QFileDialog::ExistingFile); d->setNameFilter(tr("Images (*.png *.jpg *.jpeg)")); @@ -232,8 +150,7 @@ void VCard::onSetAvatar() d->show(); } -void VCard::updateAvatar() -{ +void VCard::updateAvatar() { int height = m_ui->personalForm->minimumSize().height() - avatarButtonMargins.height(); switch (currentAvatarType) { case Shared::Avatar::empty: @@ -254,8 +171,7 @@ void VCard::updateAvatar() } } -void VCard::onAvatarSelected() -{ +void VCard::onAvatarSelected() { QFileDialog* d = static_cast(sender()); QMimeDatabase db; QString path = d->selectedFiles().front(); @@ -290,21 +206,18 @@ void VCard::onAvatarSelected() } } -void VCard::showProgress(const QString& line) -{ +void VCard::showProgress(const QString& line) { progressLabel->setText(line); overlay->show(); progress->start(); } -void VCard::hideProgress() -{ +void VCard::hideProgress() { overlay->hide(); progress->stop(); } -void VCard::onContextMenu(const QPoint& point) -{ +void VCard::onContextMenu(const QPoint& point) { contextMenu->clear(); bool hasMenu = false; QAbstractItemView* snd = static_cast(sender()); @@ -371,28 +284,20 @@ void VCard::onContextMenu(const QPoint& point) } } -void VCard::onAddEmail() -{ +void VCard::onAddEmail() { QModelIndex index = emails.addNewEmptyLine(); m_ui->emailsView->setCurrentIndex(index); m_ui->emailsView->edit(index); } -void VCard::onAddAddress() -{ - -} -void VCard::onAddPhone() -{ +void VCard::onAddAddress() {} +void VCard::onAddPhone() { QModelIndex index = phones.addNewEmptyLine(); m_ui->phonesView->setCurrentIndex(index); m_ui->phonesView->edit(index); } -void VCard::onRemoveAddress() -{ -} -void VCard::onRemoveEmail() -{ +void VCard::onRemoveAddress() {} +void VCard::onRemoveEmail() { QItemSelection selection(m_ui->emailsView->selectionModel()->selection()); QList rows; @@ -412,8 +317,7 @@ void VCard::onRemoveEmail() } } -void VCard::onRemovePhone() -{ +void VCard::onRemovePhone() { QItemSelection selection(m_ui->phonesView->selectionModel()->selection()); QList rows; @@ -433,8 +337,7 @@ void VCard::onRemovePhone() } } -void VCard::onCopyEmail() -{ +void VCard::onCopyEmail() { QList selection(m_ui->emailsView->selectionModel()->selectedRows()); QList addrs; @@ -448,8 +351,7 @@ void VCard::onCopyEmail() cb->setText(list); } -void VCard::onCopyPhone() -{ +void VCard::onCopyPhone() { QList selection(m_ui->phonesView->selectionModel()->selectedRows()); QList phs; @@ -462,3 +364,111 @@ void VCard::onCopyPhone() QClipboard* cb = QApplication::clipboard(); cb->setText(list); } + +void VCard::initializeDelegates() { + roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[0].toStdString().c_str())); + roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[1].toStdString().c_str())); + roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[2].toStdString().c_str())); + + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[0].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[1].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[2].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[3].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[4].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[5].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[6].toStdString().c_str())); +} + +void VCard::initializeViews() { + m_ui->emailsView->setContextMenuPolicy(Qt::CustomContextMenu); + m_ui->emailsView->setModel(&emails); + m_ui->emailsView->setItemDelegateForColumn(1, roleDelegate); + m_ui->emailsView->setColumnWidth(2, 25); + m_ui->emailsView->horizontalHeader()->setStretchLastSection(false); + m_ui->emailsView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + + m_ui->phonesView->setContextMenuPolicy(Qt::CustomContextMenu); + m_ui->phonesView->setModel(&phones); + m_ui->phonesView->setItemDelegateForColumn(1, roleDelegate); + m_ui->phonesView->setItemDelegateForColumn(2, phoneTypeDelegate); + m_ui->phonesView->setColumnWidth(3, 25); + m_ui->phonesView->horizontalHeader()->setStretchLastSection(false); + m_ui->phonesView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + + connect(m_ui->phonesView, &QWidget::customContextMenuRequested, this, &VCard::onContextMenu); + connect(m_ui->emailsView, &QWidget::customContextMenuRequested, this, &VCard::onContextMenu); +} + +void VCard::initializeActions() { + QAction* setAvatar = m_ui->actionSetAvatar; + QAction* clearAvatar = m_ui->actionClearAvatar; + + connect(setAvatar, &QAction::triggered, this, &VCard::onSetAvatar); + connect(clearAvatar, &QAction::triggered, this, &VCard::onClearAvatar); + + setAvatar->setEnabled(true); + clearAvatar->setEnabled(false); +} + +void VCard::initializeInteractiveElements(const QString& jid) { + if (editable) { + avatarMenu = new QMenu(); + m_ui->avatarButton->setMenu(avatarMenu); + avatarMenu->addAction(m_ui->actionSetAvatar); + avatarMenu->addAction(m_ui->actionClearAvatar); + m_ui->title->setText(tr("Account %1 card").arg(jid)); + } else { + m_ui->buttonBox->hide(); + m_ui->fullName->setReadOnly(true); + m_ui->firstName->setReadOnly(true); + m_ui->middleName->setReadOnly(true); + m_ui->lastName->setReadOnly(true); + m_ui->nickName->setReadOnly(true); + m_ui->birthday->setReadOnly(true); + m_ui->organizationName->setReadOnly(true); + m_ui->organizationDepartment->setReadOnly(true); + m_ui->organizationTitle->setReadOnly(true); + m_ui->organizationRole->setReadOnly(true); + m_ui->description->setReadOnly(true); + m_ui->url->setReadOnly(true); + m_ui->title->setText(tr("Contact %1 card").arg(jid)); + } + + connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &VCard::onButtonBoxAccepted); + connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &VCard::close); + + avatarButtonMargins = m_ui->avatarButton->size(); + + int height = m_ui->personalForm->minimumSize().height() - avatarButtonMargins.height(); + m_ui->avatarButton->setIconSize(QSize(height, height)); +} + +void VCard::initializeOverlay() { + QGridLayout* gr = static_cast(layout()); + gr->addWidget(overlay, 0, 0, 4, 1); + QVBoxLayout* nl = new QVBoxLayout(); + QGraphicsOpacityEffect* opacity = new QGraphicsOpacityEffect(); + opacity->setOpacity(0.8); + overlay->setLayout(nl); + overlay->setBackgroundRole(QPalette::Base); + overlay->setAutoFillBackground(true); + overlay->setGraphicsEffect(opacity); + progressLabel->setAlignment(Qt::AlignCenter); + QFont pf = progressLabel->font(); + pf.setBold(true); + pf.setPointSize(26); + progressLabel->setFont(pf); + progressLabel->setWordWrap(true); + nl->addStretch(); + nl->addWidget(progress); + nl->addWidget(progressLabel); + nl->addStretch(); + overlay->hide(); +} + +#ifdef WITH_OMEMO +void VCard::initializeOmemo() { + m_ui->tabWidget->addTab(omemo, "OMEMO"); +} + +#endif diff --git a/ui/widgets/vcard/vcard.h b/ui/widgets/vcard/vcard.h index 4d579e1..82b7d53 100644 --- a/ui/widgets/vcard/vcard.h +++ b/ui/widgets/vcard/vcard.h @@ -42,16 +42,16 @@ #include "ui/utils/progress.h" #include "ui/utils/comboboxdelegate.h" +#ifdef WITH_OMEMO +#include "omemo/omemo.h" +#endif + namespace Ui { class VCard; } -/** - * @todo write docs - */ -class VCard : public QWidget -{ +class VCard : public QWidget { Q_OBJECT public: VCard(const QString& jid, bool edit = false, QWidget* parent = nullptr); @@ -96,11 +96,22 @@ private: UI::VCard::PhonesModel phones; ComboboxDelegate* roleDelegate; ComboboxDelegate* phoneTypeDelegate; +#ifdef WITH_OMEMO + Omemo* omemo; +#endif static const std::set supportedTypes; - + private: void updateAvatar(); + void initializeDelegates(); + void initializeViews(); + void initializeActions(); + void initializeInteractiveElements(const QString& jid); + void initializeOverlay(); +#ifdef WITH_OMEMO + void initializeOmemo(); +#endif }; #endif // VCARD_H From 78ef3664f76a98ba871d322468877b72499cbe52 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 7 Jan 2023 17:30:22 +0300 Subject: [PATCH 077/137] some initial delegate stuff --- ui/widgets/vcard/omemo/CMakeLists.txt | 2 + ui/widgets/vcard/omemo/keydelegate.cpp | 71 ++++++++++++++++++++++++++ ui/widgets/vcard/omemo/keydelegate.h | 39 ++++++++++++++ ui/widgets/vcard/omemo/keysmodel.cpp | 3 ++ ui/widgets/vcard/omemo/omemo.cpp | 14 +++++ ui/widgets/vcard/omemo/omemo.h | 3 ++ ui/widgets/vcard/omemo/omemo.ui | 23 ++++++++- ui/widgets/vcard/vcard.ui | 8 +-- 8 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 ui/widgets/vcard/omemo/keydelegate.cpp create mode 100644 ui/widgets/vcard/omemo/keydelegate.h diff --git a/ui/widgets/vcard/omemo/CMakeLists.txt b/ui/widgets/vcard/omemo/CMakeLists.txt index 19a9fe9..e2ade51 100644 --- a/ui/widgets/vcard/omemo/CMakeLists.txt +++ b/ui/widgets/vcard/omemo/CMakeLists.txt @@ -4,4 +4,6 @@ target_sources(squawk PRIVATE omemo.ui keysmodel.cpp keysmodel.h + keydelegate.cpp + keydelegate.h ) diff --git a/ui/widgets/vcard/omemo/keydelegate.cpp b/ui/widgets/vcard/omemo/keydelegate.cpp new file mode 100644 index 0000000..1816ecd --- /dev/null +++ b/ui/widgets/vcard/omemo/keydelegate.cpp @@ -0,0 +1,71 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "keydelegate.h" +#include +#include + +#include "keysmodel.h" + +constexpr int minHeight = 50; +constexpr int minWidth = 400; + +UI::KeyDelegate::KeyDelegate(QObject* parent): + QStyledItemDelegate(parent), + fingerPrintFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)) +{ +} + +UI::KeyDelegate::~KeyDelegate() {} + + +void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { + painter->save(); + painter->setRenderHint(QPainter::Antialiasing, true); + + bool hover = option.state & QStyle::State_MouseOver; + if (hover) { + painter->save(); + painter->fillRect(option.rect, option.palette.brush(QPalette::Inactive, QPalette::Highlight)); + painter->restore(); + } + + QVariant fingerPrintV = index.data(UI::KeysModel::FingerPrint); + if (fingerPrintV.isValid()) { + painter->save(); + QByteArray fingerPrint = fingerPrintV.toByteArray(); + + painter->setFont(fingerPrintFont); + painter->drawText(option.rect, option.displayAlignment | Qt::AlignTop, fingerPrint.toHex()); + + painter->restore(); + } + painter->drawText(option.rect, option.displayAlignment, index.data().toString()); + + painter->restore(); +} + +QSize UI::KeyDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { + QSize size = QStyledItemDelegate::sizeHint(option, index); + + if (size.width() < minWidth) + size.setWidth(minWidth); + + if (size.height() < minHeight) + size.setHeight(minHeight); + + return size; +} diff --git a/ui/widgets/vcard/omemo/keydelegate.h b/ui/widgets/vcard/omemo/keydelegate.h new file mode 100644 index 0000000..d1f768b --- /dev/null +++ b/ui/widgets/vcard/omemo/keydelegate.h @@ -0,0 +1,39 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 UI_KEYDELEGATE_H +#define UI_KEYDELEGATE_H + +#include + +namespace UI { + +class KeyDelegate : public QStyledItemDelegate +{ +public: + KeyDelegate(QObject *parent = nullptr); + ~KeyDelegate(); + + QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const override; + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + +private: + QFont fingerPrintFont; +}; + +} + +#endif // UI_KEYDELEGATE_H diff --git a/ui/widgets/vcard/omemo/keysmodel.cpp b/ui/widgets/vcard/omemo/keysmodel.cpp index b70fefd..4184133 100644 --- a/ui/widgets/vcard/omemo/keysmodel.cpp +++ b/ui/widgets/vcard/omemo/keysmodel.cpp @@ -46,6 +46,9 @@ QVariant UI::KeysModel::data(const QModelIndex& index, int role) const { case Qt::DisplayRole: answer = keys[i]->label; break; + case FingerPrint: + answer = keys[i]->fingerPrint; + break; } return answer; diff --git a/ui/widgets/vcard/omemo/omemo.cpp b/ui/widgets/vcard/omemo/omemo.cpp index 8a1e1cc..b2db902 100644 --- a/ui/widgets/vcard/omemo/omemo.cpp +++ b/ui/widgets/vcard/omemo/omemo.cpp @@ -17,9 +17,14 @@ #include "omemo.h" #include "ui_omemo.h" +#include +constexpr uint8_t fingerprintLength = 24; + Omemo::Omemo(QWidget* parent): QWidget(parent), m_ui(new Ui::Omemo()), + keysDelegate(), + unusedKeysDelegate(), keysModel(), unusedKeysModel() { @@ -27,7 +32,9 @@ Omemo::Omemo(QWidget* parent): generateMockData(); + m_ui->keysView->setItemDelegate(&keysDelegate); m_ui->keysView->setModel(&keysModel); + m_ui->unusedKeysView->setItemDelegate(&unusedKeysDelegate); m_ui->unusedKeysView->setModel(&unusedKeysModel); } @@ -38,10 +45,17 @@ Omemo::~Omemo() void Omemo::generateMockData() { + std::random_device rd; + std::uniform_int_distribution dist(CHAR_MIN, CHAR_MAX); for (int i = 0; i < 5; ++i) { + QByteArray fp(fingerprintLength, 0); + for (int i = 0; i < fingerprintLength; ++i) { + fp[i] = dist(rd); + } Shared::KeyInfo info; info.id = i; info.label = QString("test_") + std::to_string(i).c_str(); + info.fingerPrint = fp; keysModel.addKey(info); } } diff --git a/ui/widgets/vcard/omemo/omemo.h b/ui/widgets/vcard/omemo/omemo.h index d01f4a8..2bc0433 100644 --- a/ui/widgets/vcard/omemo/omemo.h +++ b/ui/widgets/vcard/omemo/omemo.h @@ -21,6 +21,7 @@ #include #include "keysmodel.h" +#include "keydelegate.h" namespace Ui { @@ -38,6 +39,8 @@ private: private: QScopedPointer m_ui; + UI::KeyDelegate keysDelegate; + UI::KeyDelegate unusedKeysDelegate; UI::KeysModel keysModel; UI::KeysModel unusedKeysModel; }; diff --git a/ui/widgets/vcard/omemo/omemo.ui b/ui/widgets/vcard/omemo/omemo.ui index 7052486..cf72e0f 100644 --- a/ui/widgets/vcard/omemo/omemo.ui +++ b/ui/widgets/vcard/omemo/omemo.ui @@ -63,9 +63,28 @@ 592 - + - + + + QAbstractScrollArea::AdjustToContents + + + false + + + QAbstractItemView::SelectColumns + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerItem + + + true + + diff --git a/ui/widgets/vcard/vcard.ui b/ui/widgets/vcard/vcard.ui index b71d262..7e09257 100644 --- a/ui/widgets/vcard/vcard.ui +++ b/ui/widgets/vcard/vcard.ui @@ -10,7 +10,7 @@ 0 0 578 - 671 + 748 @@ -84,7 +84,7 @@ QTabWidget::Rounded - 0 + 1 Qt::ElideNone @@ -566,8 +566,8 @@ 0 0 - 566 - 498 + 545 + 544 From 2aed8a120982c7e04b7e6d94c39b4a1e7a10343e Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 8 Jan 2023 18:16:41 +0300 Subject: [PATCH 078/137] a bit better drawing of a key fingerprint --- ui/widgets/vcard/omemo/keydelegate.cpp | 79 +++++++++++++++++++++++--- ui/widgets/vcard/omemo/keydelegate.h | 7 +++ 2 files changed, 77 insertions(+), 9 deletions(-) diff --git a/ui/widgets/vcard/omemo/keydelegate.cpp b/ui/widgets/vcard/omemo/keydelegate.cpp index 1816ecd..2ab969d 100644 --- a/ui/widgets/vcard/omemo/keydelegate.cpp +++ b/ui/widgets/vcard/omemo/keydelegate.cpp @@ -22,10 +22,15 @@ constexpr int minHeight = 50; constexpr int minWidth = 400; +constexpr uint8_t margin = 10; +constexpr uint8_t partSize = 8; +constexpr uint8_t maxSingleLineParts = 3; UI::KeyDelegate::KeyDelegate(QObject* parent): QStyledItemDelegate(parent), - fingerPrintFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)) + fingerPrintFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)), + fingerPrintMetrics(fingerPrintFont), + spaceWidth(fingerPrintMetrics.horizontalAdvance(" ")) { } @@ -46,14 +51,31 @@ void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio QVariant fingerPrintV = index.data(UI::KeysModel::FingerPrint); if (fingerPrintV.isValid()) { painter->save(); - QByteArray fingerPrint = fingerPrintV.toByteArray(); - painter->setFont(fingerPrintFont); - painter->drawText(option.rect, option.displayAlignment | Qt::AlignTop, fingerPrint.toHex()); + QByteArray fingerPrint = fingerPrintV.toByteArray(); + std::vector parts = getParts(fingerPrint); + uint8_t partsLength = parts.size(); + QRect rect = option.rect; + rect.adjust(margin, margin, 0, 0); + QRect r; + uint8_t firstLine; + if (partsLength > maxSingleLineParts) + firstLine = partsLength / 2 + partsLength % 2; + else + firstLine = partsLength; + + for (uint8_t i = 0; i < partsLength; ++i) { + if (i == firstLine) { + rect.setLeft(option.rect.left() + margin); + rect.adjust(0, r.height() + fingerPrintMetrics.leading(), 0, 0); + } + painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, parts[i], &r); + rect.adjust(r.width() + spaceWidth ,0, 0, 0); + } painter->restore(); } - painter->drawText(option.rect, option.displayAlignment, index.data().toString()); + //painter->drawText(option.rect, option.displayAlignment, index.data().toString()); painter->restore(); } @@ -61,11 +83,50 @@ void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio QSize UI::KeyDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { QSize size = QStyledItemDelegate::sizeHint(option, index); - if (size.width() < minWidth) - size.setWidth(minWidth); + int mh = margin * 2; + int mw = margin * 2; + QVariant fingerPrintV = index.data(UI::KeysModel::FingerPrint); + if (fingerPrintV.isValid()) { + QString hex = fingerPrintV.toByteArray().toHex(); + uint8_t parts = hex.size() / partSize; - if (size.height() < minHeight) - size.setHeight(minHeight); + mh += fingerPrintMetrics.height(); + if (parts > maxSingleLineParts) { + mh += fingerPrintMetrics.height() + fingerPrintMetrics.leading(); + } + + uint8_t firstLine; + if (parts > maxSingleLineParts) + firstLine = parts / 2 + parts % 2; + else + firstLine = parts; + + mw += firstLine * partSize * spaceWidth + (firstLine - 1) * spaceWidth; + } + + + if (size.width() < mw) + size.setWidth(mw); + + if (size.height() < mh) + size.setHeight(mh); return size; } + +std::vector UI::KeyDelegate::getParts(const QByteArray& data) { + QString hex = data.toHex(); + uint8_t parts = hex.size() / partSize; + std::vector result(parts); + for (uint8_t i = 0; i < parts; ++i) { + uint16_t index = i * partSize; + uint8_t length = partSize; + if (index + length > hex.size()) + length = hex.size() - index; + QStringRef part(&hex, index, length); + result[i] = part.toString(); + } + + return result; +} + diff --git a/ui/widgets/vcard/omemo/keydelegate.h b/ui/widgets/vcard/omemo/keydelegate.h index d1f768b..0ce3e7f 100644 --- a/ui/widgets/vcard/omemo/keydelegate.h +++ b/ui/widgets/vcard/omemo/keydelegate.h @@ -18,6 +18,8 @@ #define UI_KEYDELEGATE_H #include +#include +#include namespace UI { @@ -32,6 +34,11 @@ public: private: QFont fingerPrintFont; + QFontMetrics fingerPrintMetrics; + int spaceWidth; + +private: + static std::vector getParts(const QByteArray& data); }; } From 15fb4bbd62ae46c435dcd85b3a018665fa24a748 Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 11 Jan 2023 23:45:38 +0300 Subject: [PATCH 079/137] some thoughts about fonts, lastInteraction and label into keyDelegate --- shared/keyinfo.cpp | 5 + shared/keyinfo.h | 6 +- ui/widgets/conversation.cpp | 1 - ui/widgets/messageline/feedview.cpp | 134 ++++++--------------- ui/widgets/messageline/feedview.h | 5 +- ui/widgets/messageline/messagedelegate.cpp | 76 ++++++------ ui/widgets/messageline/messagedelegate.h | 15 ++- ui/widgets/messageline/preview.cpp | 17 ++- ui/widgets/messageline/preview.h | 1 - ui/widgets/vcard/omemo/keydelegate.cpp | 53 +++++++- ui/widgets/vcard/omemo/keydelegate.h | 2 + ui/widgets/vcard/omemo/keysmodel.cpp | 10 ++ ui/widgets/vcard/omemo/keysmodel.h | 4 +- ui/widgets/vcard/omemo/omemo.cpp | 6 +- ui/widgets/vcard/omemo/omemo.ui | 2 +- 15 files changed, 166 insertions(+), 171 deletions(-) diff --git a/shared/keyinfo.cpp b/shared/keyinfo.cpp index bff716e..db3ce2b 100644 --- a/shared/keyinfo.cpp +++ b/shared/keyinfo.cpp @@ -22,6 +22,7 @@ Shared::KeyInfo::KeyInfo( uint32_t p_id, const QByteArray& p_fingerPrint, const QString& p_label, + const QDateTime& p_lastInteraction, Shared::TrustLevel p_trustLevel, Shared::EncryptionProtocol p_protocol, bool p_currentDevice @@ -29,6 +30,7 @@ Shared::KeyInfo::KeyInfo( id(p_id), fingerPrint(p_fingerPrint), label(p_label), + lastInteraction(p_lastInteraction), trustLevel(p_trustLevel), protocol(p_protocol), currentDevice(p_currentDevice) @@ -39,6 +41,7 @@ Shared::KeyInfo::KeyInfo(): id(0), fingerPrint(), label(), + lastInteraction(), trustLevel(TrustLevel::undecided), protocol(EncryptionProtocol::omemo), currentDevice(false) @@ -49,6 +52,7 @@ Shared::KeyInfo::KeyInfo(const Shared::KeyInfo& other): id(other.id), fingerPrint(other.fingerPrint), label(other.label), + lastInteraction(other.lastInteraction), trustLevel(other.trustLevel), protocol(other.protocol), currentDevice(other.currentDevice) @@ -59,6 +63,7 @@ Shared::KeyInfo & Shared::KeyInfo::operator=(const Shared::KeyInfo& other) { id = other.id; fingerPrint = other.fingerPrint; label = other.label; + lastInteraction = other.lastInteraction; trustLevel = other.trustLevel; protocol = other.protocol; currentDevice = other.currentDevice; diff --git a/shared/keyinfo.h b/shared/keyinfo.h index 360a8b9..1befbc9 100644 --- a/shared/keyinfo.h +++ b/shared/keyinfo.h @@ -19,6 +19,7 @@ #include #include +#include #include @@ -31,9 +32,9 @@ class KeyInfo public: KeyInfo( uint32_t id, - const QByteArray& - fingerPrint, + const QByteArray& fingerPrint, const QString& label, + const QDateTime& lastInteraction, TrustLevel trustLevel, EncryptionProtocol protocol = EncryptionProtocol::omemo, bool currentDevice = false @@ -47,6 +48,7 @@ public: uint32_t id; QByteArray fingerPrint; QString label; + QDateTime lastInteraction; TrustLevel trustLevel; EncryptionProtocol protocol; bool currentDevice; diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index b2c7a5f..3e6a6e0 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -68,7 +68,6 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, feed->setItemDelegate(delegate); feed->setFrameShape(QFrame::NoFrame); feed->setContextMenuPolicy(Qt::CustomContextMenu); - delegate->initializeFonts(feed->getFont()); feed->setModel(el->feed); el->feed->incrementObservers(); m_ui->widget->layout()->addWidget(feed); diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index 69b5093..57e8143 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "messagedelegate.h" #include "messagefeed.h" @@ -40,6 +41,8 @@ const std::set FeedView::geometryChangingRoles = { Models::MessageFeed::Error, Models::MessageFeed::Date }; +QFont FeedView::dividerFont = QFontDatabase::systemFont(QFontDatabase::TitleFont); +QFontMetrics FeedView::dividerMetrics = QFontMetrics(dividerFont); FeedView::FeedView(QWidget* parent): QAbstractItemView(parent), @@ -51,8 +54,6 @@ FeedView::FeedView(QWidget* parent): clearWidgetsMode(false), modelState(Models::MessageFeed::complete), progress(), - dividerFont(), - dividerMetrics(dividerFont), mousePressed(false), dragging(false), hovered(Shared::Hover::nothing), @@ -68,23 +69,11 @@ FeedView::FeedView(QWidget* parent): progress.setParent(viewport()); progress.resize(progressSize, progressSize); - - dividerFont = getFont(); - dividerFont.setBold(true); - float ndps = dividerFont.pointSizeF(); - if (ndps != -1) { - dividerFont.setPointSizeF(ndps * 1.2); - } else { - dividerFont.setPointSize(dividerFont.pointSize() + 2); - } } -FeedView::~FeedView() -{ -} +FeedView::~FeedView() {} -QModelIndex FeedView::indexAt(const QPoint& point) const -{ +QModelIndex FeedView::indexAt(const QPoint& point) const { int32_t vh = viewport()->height(); uint32_t y = vh - point.y() + vo; @@ -102,12 +91,8 @@ QModelIndex FeedView::indexAt(const QPoint& point) const return QModelIndex(); } -void FeedView::scrollTo(const QModelIndex& index, QAbstractItemView::ScrollHint hint) -{ -} -QRect FeedView::visualRect(const QModelIndex& index) const -{ +QRect FeedView::visualRect(const QModelIndex& index) const { unsigned int row = index.row(); if (!index.isValid() || row >= hints.size()) { qDebug() << "visualRect for" << row; @@ -119,44 +104,24 @@ QRect FeedView::visualRect(const QModelIndex& index) const } } -int FeedView::horizontalOffset() const -{ - return 0; -} +QString FeedView::getSelectedText() const{return selectedText;} -bool FeedView::isIndexHidden(const QModelIndex& index) const -{ - return false; -} +//TODO!!! +void FeedView::scrollTo(const QModelIndex& index, QAbstractItemView::ScrollHint hint) {} +int FeedView::horizontalOffset() const {return 0;} +bool FeedView::isIndexHidden(const QModelIndex& index) const{return false;} +QModelIndex FeedView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers) {return QModelIndex();} +void FeedView::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command) {} +int FeedView::verticalOffset() const {return vo;} +QRegion FeedView::visualRegionForSelection(const QItemSelection& selection) const {return QRegion();} -QModelIndex FeedView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers) -{ - return QModelIndex(); -} - -void FeedView::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command) -{ -} - -int FeedView::verticalOffset() const -{ - return vo; -} - -QRegion FeedView::visualRegionForSelection(const QItemSelection& selection) const -{ - return QRegion(); -} - -void FeedView::rowsInserted(const QModelIndex& parent, int start, int end) -{ +void FeedView::rowsInserted(const QModelIndex& parent, int start, int end){ QAbstractItemView::rowsInserted(parent, start, end); scheduleDelayedItemsLayout(); } -void FeedView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles) -{ +void FeedView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles) { if (specialDelegate) { for (int role : roles) { if (geometryChangingRoles.count(role) != 0) { @@ -168,8 +133,7 @@ void FeedView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottom QAbstractItemView::dataChanged(topLeft, bottomRight, roles); } -void FeedView::updateGeometries() -{ +void FeedView::updateGeometries() { //qDebug() << "updateGeometries"; QScrollBar* bar = verticalScrollBar(); @@ -260,8 +224,7 @@ void FeedView::updateGeometries() QAbstractItemView::updateGeometries(); } -bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* m, uint32_t totalHeight) -{ +bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* m, uint32_t totalHeight) { uint32_t previousOffset = elementMargin; QDateTime lastDate; for (int i = 0, size = m->rowCount(); i < size; ++i) { @@ -307,9 +270,7 @@ bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewIt return true; } - -void FeedView::paintEvent(QPaintEvent* event) -{ +void FeedView::paintEvent(QPaintEvent* event) { //qDebug() << "paint" << event->rect(); const QAbstractItemModel* m = model(); QWidget* vp = viewport(); @@ -388,8 +349,7 @@ void FeedView::paintEvent(QPaintEvent* event) } } -void FeedView::drawDateDevider(int top, const QDateTime& date, QPainter& painter) -{ +void FeedView::drawDateDevider(int top, const QDateTime& date, QPainter& painter) { int divisionHeight = dateDeviderMargin * 2 + dividerMetrics.height(); QRect r(QPoint(0, top), QSize(viewport()->width(), divisionHeight)); painter.save(); @@ -398,8 +358,7 @@ void FeedView::drawDateDevider(int top, const QDateTime& date, QPainter& painter painter.restore(); } -void FeedView::verticalScrollbarValueChanged(int value) -{ +void FeedView::verticalScrollbarValueChanged(int value) { vo = verticalScrollBar()->maximum() - value; positionProgress(); @@ -415,8 +374,7 @@ void FeedView::verticalScrollbarValueChanged(int value) QAbstractItemView::verticalScrollbarValueChanged(vo); } -void FeedView::setAnchorHovered(Shared::Hover type) -{ +void FeedView::setAnchorHovered(Shared::Hover type) { if (hovered != type) { hovered = type; switch (hovered) { @@ -433,8 +391,7 @@ void FeedView::setAnchorHovered(Shared::Hover type) } } -void FeedView::mouseMoveEvent(QMouseEvent* event) -{ +void FeedView::mouseMoveEvent(QMouseEvent* event) { if (!isVisible()) { return; } @@ -479,8 +436,7 @@ void FeedView::mouseMoveEvent(QMouseEvent* event) } } -void FeedView::mousePressEvent(QMouseEvent* event) -{ +void FeedView::mousePressEvent(QMouseEvent* event) { QAbstractItemView::mousePressEvent(event); mousePressed = event->button() == Qt::LeftButton; @@ -500,8 +456,7 @@ void FeedView::mousePressEvent(QMouseEvent* event) } } -void FeedView::mouseDoubleClickEvent(QMouseEvent* event) -{ +void FeedView::mouseDoubleClickEvent(QMouseEvent* event) { QAbstractItemView::mouseDoubleClickEvent(event); mousePressed = event->button() == Qt::LeftButton; if (mousePressed) { @@ -530,8 +485,7 @@ void FeedView::mouseDoubleClickEvent(QMouseEvent* event) } } -void FeedView::mouseReleaseEvent(QMouseEvent* event) -{ +void FeedView::mouseReleaseEvent(QMouseEvent* event) { QAbstractItemView::mouseReleaseEvent(event); if (mousePressed) { @@ -551,8 +505,7 @@ void FeedView::mouseReleaseEvent(QMouseEvent* event) } } -void FeedView::keyPressEvent(QKeyEvent* event) -{ +void FeedView::keyPressEvent(QKeyEvent* event) { QKeyEvent *key_event = static_cast(event); if (key_event->matches(QKeySequence::Copy)) { if (selectedText.size() > 0) { @@ -562,16 +515,14 @@ void FeedView::keyPressEvent(QKeyEvent* event) } } -void FeedView::resizeEvent(QResizeEvent* event) -{ +void FeedView::resizeEvent(QResizeEvent* event) { QAbstractItemView::resizeEvent(event); positionProgress(); emit resized(); } -void FeedView::positionProgress() -{ +void FeedView::positionProgress() { QSize layoutBounds = maximumViewportSize(); int progressPosition = layoutBounds.height() - progressSize; std::deque::size_type size = hints.size(); @@ -585,13 +536,7 @@ void FeedView::positionProgress() progress.move((width() - progressSize) / 2, progressPosition); } -QFont FeedView::getFont() const -{ - return viewOptions().font; -} - -void FeedView::setItemDelegate(QAbstractItemDelegate* delegate) -{ +void FeedView::setItemDelegate(QAbstractItemDelegate* delegate) { if (specialDelegate) { MessageDelegate* del = static_cast(itemDelegate()); disconnect(del, &MessageDelegate::buttonPushed, this, &FeedView::onMessageButtonPushed); @@ -613,8 +558,7 @@ void FeedView::setItemDelegate(QAbstractItemDelegate* delegate) } } -void FeedView::setModel(QAbstractItemModel* p_model) -{ +void FeedView::setModel(QAbstractItemModel* p_model) { if (specialModel) { Models::MessageFeed* feed = static_cast(model()); disconnect(feed, &Models::MessageFeed::syncStateChange, this, &FeedView::onModelSyncStateChange); @@ -633,24 +577,21 @@ void FeedView::setModel(QAbstractItemModel* p_model) } } -void FeedView::onMessageButtonPushed(const QString& messageId) -{ +void FeedView::onMessageButtonPushed(const QString& messageId) { if (specialModel) { Models::MessageFeed* feed = static_cast(model()); feed->downloadAttachment(messageId); } } -void FeedView::onMessageInvalidPath(const QString& messageId) -{ +void FeedView::onMessageInvalidPath(const QString& messageId) { if (specialModel) { Models::MessageFeed* feed = static_cast(model()); feed->reportLocalPathInvalid(messageId); } } -void FeedView::onModelSyncStateChange(Models::MessageFeed::SyncState state) -{ +void FeedView::onModelSyncStateChange(Models::MessageFeed::SyncState state) { bool needToUpdateGeometry = false; if (modelState != state) { if (state == Models::MessageFeed::complete || modelState == Models::MessageFeed::complete) { @@ -671,8 +612,3 @@ void FeedView::onModelSyncStateChange(Models::MessageFeed::SyncState state) scheduleDelayedItemsLayout(); } } - -QString FeedView::getSelectedText() const -{ - return selectedText; -} diff --git a/ui/widgets/messageline/feedview.h b/ui/widgets/messageline/feedview.h index 4194849..1d0c92a 100644 --- a/ui/widgets/messageline/feedview.h +++ b/ui/widgets/messageline/feedview.h @@ -50,7 +50,6 @@ public: void setItemDelegate(QAbstractItemDelegate* delegate); void setModel(QAbstractItemModel * model) override; - QFont getFont() const; QString getSelectedText() const; signals: @@ -100,8 +99,8 @@ private: bool clearWidgetsMode; Models::MessageFeed::SyncState modelState; Progress progress; - QFont dividerFont; - QFontMetrics dividerMetrics; + static QFont dividerFont; + static QFontMetrics dividerMetrics; bool mousePressed; bool dragging; Shared::Hover hovered; diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index f935057..6247f4f 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -24,29 +24,40 @@ #include #include #include +#include #include #include "messagedelegate.h" #include "messagefeed.h" -int MessageDelegate::avatarHeight(50); -int MessageDelegate::margin(6); +QFont getFont (QFontDatabase::SystemFont type, bool bold, bool italic) { + QFont font = QFontDatabase::systemFont(type); + if (bold) + font.setBold(true); + if (italic) + font.setItalic(true); + + return font; +} constexpr int textMargin = 2; constexpr int statusIconSize = 16; -constexpr float nickFontMultiplier = 1.1; -constexpr float dateFontMultiplier = 0.8; constexpr int bubbleMargin = 6; constexpr int bubbleBorderRadius = 3; +int MessageDelegate::avatarHeight(50); +int MessageDelegate::margin(6); + +bool MessageDelegate::fontsInitialized(false); +QFont MessageDelegate::bodyFont; +QFont MessageDelegate::nickFont; +QFont MessageDelegate::dateFont; +QFontMetrics MessageDelegate::nickMetrics(nickFont); +QFontMetrics MessageDelegate::dateMetrics(dateFont); + MessageDelegate::MessageDelegate(QObject* parent): QStyledItemDelegate(parent), - bodyFont(), - nickFont(), - dateFont(), bodyRenderer(new QTextDocument()), - nickMetrics(nickFont), - dateMetrics(dateFont), buttonHeight(0), buttonWidth(0), barHeight(0), @@ -60,7 +71,13 @@ MessageDelegate::MessageDelegate(QObject* parent): currentId(""), selection(0, 0) { + if (!fontsInitialized) { + fontsInitialized = true; + initializeFonts(); + } + bodyRenderer->setDocumentMargin(0); + bodyRenderer->setDefaultFont(bodyFont); QPushButton btn(QCoreApplication::translate("MessageLine", "Download")); buttonHeight = btn.sizeHint().height(); @@ -285,7 +302,6 @@ bool MessageDelegate::needToDrawSender(const QModelIndex& index, const Models::F } } - QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { QRect messageRect = option.rect.adjusted(bubbleMargin, margin / 2 + bubbleMargin, -(avatarHeight + 3 * margin + bubbleMargin), -(margin + bubbleMargin) / 2); @@ -538,38 +554,6 @@ QString MessageDelegate::clearSelection() return lastSelectedId; } -void MessageDelegate::initializeFonts(const QFont& font) -{ - bodyFont = font; - nickFont = font; - dateFont = font; - - nickFont.setBold(true); - - float ndps = nickFont.pointSizeF(); - if (ndps != -1) { - nickFont.setPointSizeF(ndps * nickFontMultiplier); - } else { - nickFont.setPointSize(nickFont.pointSize() * nickFontMultiplier); - } - - dateFont.setItalic(true); - float dps = dateFont.pointSizeF(); - if (dps != -1) { - dateFont.setPointSizeF(dps * dateFontMultiplier); - } else { - dateFont.setPointSize(dateFont.pointSize() * dateFontMultiplier); - } - - bodyFont.setKerning(false); - nickMetrics = QFontMetrics(nickFont); - dateMetrics = QFontMetrics(dateFont); - - bodyRenderer->setDefaultFont(bodyFont); - - Preview::initializeFont(bodyFont); -} - bool MessageDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) { //qDebug() << event->type(); @@ -828,3 +812,11 @@ void MessageDelegate::clearHelperWidget(const Models::FeedItem& data) const } } } + +void MessageDelegate::initializeFonts () { + bodyFont = getFont(QFontDatabase::GeneralFont, false, false); + nickFont = getFont(QFontDatabase::TitleFont, true, false); + dateFont = getFont(QFontDatabase::SmallestReadableFont, false, true); + nickMetrics = QFontMetrics(nickFont); + dateMetrics = QFontMetrics(dateFont); +} diff --git a/ui/widgets/messageline/messagedelegate.h b/ui/widgets/messageline/messagedelegate.h index 322fb76..7a54e66 100644 --- a/ui/widgets/messageline/messagedelegate.h +++ b/ui/widgets/messageline/messagedelegate.h @@ -53,7 +53,6 @@ public: QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; //void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const override; - void initializeFonts(const QFont& font); bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) override; void endClearWidgets(); void beginClearWidgets(); @@ -94,6 +93,9 @@ protected: protected slots: void onButtonPushed() const; + +private: + static void initializeFonts(); private: class FeedButton : public QPushButton { @@ -101,12 +103,13 @@ private: QString messageId; }; - QFont bodyFont; - QFont nickFont; - QFont dateFont; + static bool fontsInitialized; + static QFont bodyFont; + static QFont nickFont; + static QFont dateFont; QTextDocument* bodyRenderer; - QFontMetrics nickMetrics; - QFontMetrics dateMetrics; + static QFontMetrics nickMetrics; + static QFontMetrics dateMetrics; int buttonHeight; int buttonWidth; diff --git a/ui/widgets/messageline/preview.cpp b/ui/widgets/messageline/preview.cpp index 137293f..8c844ae 100644 --- a/ui/widgets/messageline/preview.cpp +++ b/ui/widgets/messageline/preview.cpp @@ -18,11 +18,18 @@ #include "preview.h" +#include constexpr int margin = 6; constexpr int maxAttachmentHeight = 500; -QFont Preview::font; +QFont getFont () { + QFont font = QFontDatabase::systemFont(QFontDatabase::GeneralFont); + font.setBold(true); + return font; +} + +QFont Preview::font = getFont(); QFontMetrics Preview::metrics(Preview::font); Preview::Preview(const QString& pPath, const QSize& pMaxSize, const QPoint& pos, QWidget* pParent): @@ -38,20 +45,12 @@ Preview::Preview(const QString& pPath, const QSize& pMaxSize, const QPoint& pos, fileReachable(true), actualPreview(false) { - initializeElements(); if (fileReachable) { positionElements(); } } -void Preview::initializeFont(const QFont& newFont) -{ - font = newFont; - font.setBold(true); - metrics = QFontMetrics(font); -} - Preview::~Preview() { clean(); diff --git a/ui/widgets/messageline/preview.h b/ui/widgets/messageline/preview.h index 5165137..11a5d7d 100644 --- a/ui/widgets/messageline/preview.h +++ b/ui/widgets/messageline/preview.h @@ -48,7 +48,6 @@ public: bool isFileReachable() const; QSize size() const; - static void initializeFont(const QFont& newFont); static QSize constrainAttachSize(QSize src, QSize bounds); static QSize calculateAttachSize(const QString& path, const QRect& bounds); static bool canVisualize(const Shared::Global::FileInfo& info); diff --git a/ui/widgets/vcard/omemo/keydelegate.cpp b/ui/widgets/vcard/omemo/keydelegate.cpp index 2ab969d..646dfe9 100644 --- a/ui/widgets/vcard/omemo/keydelegate.cpp +++ b/ui/widgets/vcard/omemo/keydelegate.cpp @@ -29,10 +29,11 @@ constexpr uint8_t maxSingleLineParts = 3; UI::KeyDelegate::KeyDelegate(QObject* parent): QStyledItemDelegate(parent), fingerPrintFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)), + labelFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)), fingerPrintMetrics(fingerPrintFont), + labelFontMetrics(labelFont), spaceWidth(fingerPrintMetrics.horizontalAdvance(" ")) -{ -} +{} UI::KeyDelegate::~KeyDelegate() {} @@ -48,6 +49,24 @@ void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio painter->restore(); } + QColor q = painter->pen().color(); + q.setAlpha(180); + + QRect rect = option.rect; + rect.adjust(margin, margin, 0, 0); + QVariant labelV = index.data(UI::KeysModel::Label); + if (labelV.isValid()) { + QString label = labelV.toString(); + if (label.size() > 0) { + painter->save(); + painter->setFont(labelFont); + painter->setPen(q); + painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, label); + rect.adjust(0, labelFontMetrics.lineSpacing(), 0, 0); + painter->restore(); + } + } + QVariant fingerPrintV = index.data(UI::KeysModel::FingerPrint); if (fingerPrintV.isValid()) { painter->save(); @@ -55,8 +74,6 @@ void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio QByteArray fingerPrint = fingerPrintV.toByteArray(); std::vector parts = getParts(fingerPrint); uint8_t partsLength = parts.size(); - QRect rect = option.rect; - rect.adjust(margin, margin, 0, 0); QRect r; uint8_t firstLine; if (partsLength > maxSingleLineParts) @@ -73,8 +90,25 @@ void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio rect.adjust(r.width() + spaceWidth ,0, 0, 0); } + rect.adjust(0, r.height() + fingerPrintMetrics.leading(), 0, 0); + rect.setLeft(option.rect.left() + margin); + painter->restore(); } + + + QVariant lastV = index.data(UI::KeysModel::LastInteraction); + if (lastV.isValid()) { + QDateTime last = lastV.toDateTime(); + if (last.isValid()) { + painter->save(); + painter->setFont(labelFont); + painter->setPen(q); + painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, last.toString()); + rect.adjust(0, labelFontMetrics.lineSpacing(), 0, 0); + painter->restore(); + } + } //painter->drawText(option.rect, option.displayAlignment, index.data().toString()); painter->restore(); @@ -85,6 +119,12 @@ QSize UI::KeyDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel int mh = margin * 2; int mw = margin * 2; + + QVariant labelV = index.data(UI::KeysModel::Label); + if (labelV.isValid() && labelV.toString().size() > 0) { + mh += labelFontMetrics.lineSpacing(); + } + QVariant fingerPrintV = index.data(UI::KeysModel::FingerPrint); if (fingerPrintV.isValid()) { QString hex = fingerPrintV.toByteArray().toHex(); @@ -103,7 +143,10 @@ QSize UI::KeyDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel mw += firstLine * partSize * spaceWidth + (firstLine - 1) * spaceWidth; } - + QVariant lastV = index.data(UI::KeysModel::LastInteraction); + if (lastV.isValid() && lastV.toDateTime().isValid()) { + mh += labelFontMetrics.lineSpacing(); + } if (size.width() < mw) size.setWidth(mw); diff --git a/ui/widgets/vcard/omemo/keydelegate.h b/ui/widgets/vcard/omemo/keydelegate.h index 0ce3e7f..2b0eb5b 100644 --- a/ui/widgets/vcard/omemo/keydelegate.h +++ b/ui/widgets/vcard/omemo/keydelegate.h @@ -34,7 +34,9 @@ public: private: QFont fingerPrintFont; + QFont labelFont; QFontMetrics fingerPrintMetrics; + QFontMetrics labelFontMetrics; int spaceWidth; private: diff --git a/ui/widgets/vcard/omemo/keysmodel.cpp b/ui/widgets/vcard/omemo/keysmodel.cpp index 4184133..750e42a 100644 --- a/ui/widgets/vcard/omemo/keysmodel.cpp +++ b/ui/widgets/vcard/omemo/keysmodel.cpp @@ -44,11 +44,21 @@ QVariant UI::KeysModel::data(const QModelIndex& index, int role) const { switch (role) { case Qt::DisplayRole: + case Label: answer = keys[i]->label; break; case FingerPrint: answer = keys[i]->fingerPrint; break; + case TrustLevel: + answer = static_cast(keys[i]->trustLevel); + break; + case LastInteraction: + answer = keys[i]->lastInteraction; + break; + case Dirty: + answer = false; + break; } return answer; diff --git a/ui/widgets/vcard/omemo/keysmodel.h b/ui/widgets/vcard/omemo/keysmodel.h index 853c5c6..68f222c 100644 --- a/ui/widgets/vcard/omemo/keysmodel.h +++ b/ui/widgets/vcard/omemo/keysmodel.h @@ -43,7 +43,9 @@ public: enum Roles { Label = Qt::UserRole + 1, FingerPrint, - TrustLevel + TrustLevel, + LastInteraction, + Dirty }; private: diff --git a/ui/widgets/vcard/omemo/omemo.cpp b/ui/widgets/vcard/omemo/omemo.cpp index b2db902..0aea97c 100644 --- a/ui/widgets/vcard/omemo/omemo.cpp +++ b/ui/widgets/vcard/omemo/omemo.cpp @@ -54,8 +54,12 @@ void Omemo::generateMockData() } Shared::KeyInfo info; info.id = i; - info.label = QString("test_") + std::to_string(i).c_str(); + if (i % 3 == 0) + info.label = QString("test_") + std::to_string(i).c_str(); info.fingerPrint = fp; + if (i % 2 == 0) + info.lastInteraction = QDateTime::currentDateTime(); + keysModel.addKey(info); } } diff --git a/ui/widgets/vcard/omemo/omemo.ui b/ui/widgets/vcard/omemo/omemo.ui index cf72e0f..1dc6dc9 100644 --- a/ui/widgets/vcard/omemo/omemo.ui +++ b/ui/widgets/vcard/omemo/omemo.ui @@ -82,7 +82,7 @@ QAbstractItemView::ScrollPerItem - true + false From d4bf7e599ae25ca6d9d40a94c326ffe91eab4ce2 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 12 Jan 2023 20:56:01 +0300 Subject: [PATCH 080/137] better way to solve yesterday font problem, small visual avatar rendering fix --- shared/global.cpp | 28 +++++++++++++++++ shared/global.h | 10 +++++++ ui/widgets/conversation.cpp | 2 +- ui/widgets/messageline/feedview.cpp | 5 ++-- ui/widgets/messageline/feedview.h | 4 +-- ui/widgets/messageline/messagedelegate.cpp | 35 ++++------------------ ui/widgets/messageline/messagedelegate.h | 14 ++++----- ui/widgets/vcard/omemo/keydelegate.cpp | 12 ++++---- ui/widgets/vcard/omemo/keydelegate.h | 8 ++--- 9 files changed, 62 insertions(+), 56 deletions(-) diff --git a/shared/global.cpp b/shared/global.cpp index 2e2978a..dc1cb80 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -17,6 +17,7 @@ */ #include "global.h" +#include #include "enums.h" #include "ui/models/roster.h" @@ -26,6 +27,25 @@ constexpr bool OMEMO_SUPPORT = true; constexpr bool OMEMO_SUPPORT = false #endif +QFont getFont (QFontDatabase::SystemFont type, bool bold = false, bool italic = false, qreal factor = 1.0) { + QFont font = QFontDatabase::systemFont(type); + if (bold) + font.setBold(true); + if (italic) + font.setItalic(true); + + if (factor != 1.0) { + float ps = font.pointSizeF(); + if (ps != -1) { + font.setPointSizeF(ps * factor); + } else { + font.setPointSize(font.pointSize() * factor); + } + } + + return font; +} + Shared::Global* Shared::Global::instance = 0; const std::set Shared::Global::supportedImagesExts = {"png", "jpg", "webp", "jpeg", "gif", "svg"}; @@ -102,6 +122,14 @@ Shared::Global::Global(): defaultSystemStyle(QApplication::style()->objectName()), defaultSystemPalette(QApplication::palette()), omemoSupport(OMEMO_SUPPORT), + defaultFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)), + smallFont(getFont(QFontDatabase::SmallestReadableFont, false, true)), + headerFont(getFont(QFontDatabase::TitleFont, true, false, 1.1)), + titleFont(getFont(QFontDatabase::TitleFont, true, false, 1.3)), + defaultFontMetrics(defaultFont), + smallFontMetrics(smallFont), + headerFontMetrics(headerFont), + titleFontMetrics(titleFont), pluginSupport({ {"KWallet", false}, {"openFileManagerWindowJob", false}, diff --git a/shared/global.h b/shared/global.h index d212cd1..1fc4aca 100644 --- a/shared/global.h +++ b/shared/global.h @@ -42,6 +42,8 @@ #include #include #include +#include +#include namespace Shared { @@ -100,6 +102,14 @@ namespace Shared { static void setTheme(const QString& path); static void setStyle(const QString& style); const bool omemoSupport; + QFont defaultFont; + QFont smallFont; + QFont headerFont; + QFont titleFont; + QFontMetrics defaultFontMetrics; + QFontMetrics smallFontMetrics; + QFontMetrics headerFontMetrics; + QFontMetrics titleFontMetrics; template static T fromInt(int src); diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 3e6a6e0..61d3163 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -371,7 +371,7 @@ void Conversation::setAvatar(const QString& path) if (path.size() == 0) { pixmap = Shared::icon("user", true).pixmap(avatarSize); } else { - pixmap = QPixmap(path).scaled(avatarSize); + pixmap = QPixmap(path).scaled(avatarSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); } diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index 57e8143..9d222ae 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include "messagedelegate.h" #include "messagefeed.h" @@ -41,8 +40,6 @@ const std::set FeedView::geometryChangingRoles = { Models::MessageFeed::Error, Models::MessageFeed::Date }; -QFont FeedView::dividerFont = QFontDatabase::systemFont(QFontDatabase::TitleFont); -QFontMetrics FeedView::dividerMetrics = QFontMetrics(dividerFont); FeedView::FeedView(QWidget* parent): QAbstractItemView(parent), @@ -54,6 +51,8 @@ FeedView::FeedView(QWidget* parent): clearWidgetsMode(false), modelState(Models::MessageFeed::complete), progress(), + dividerFont(Shared::Global::getInstance()->titleFont), + dividerMetrics(Shared::Global::getInstance()->titleFontMetrics), mousePressed(false), dragging(false), hovered(Shared::Hover::nothing), diff --git a/ui/widgets/messageline/feedview.h b/ui/widgets/messageline/feedview.h index 1d0c92a..a39afc8 100644 --- a/ui/widgets/messageline/feedview.h +++ b/ui/widgets/messageline/feedview.h @@ -99,8 +99,8 @@ private: bool clearWidgetsMode; Models::MessageFeed::SyncState modelState; Progress progress; - static QFont dividerFont; - static QFontMetrics dividerMetrics; + const QFont& dividerFont; + const QFontMetrics& dividerMetrics; bool mousePressed; bool dragging; Shared::Hover hovered; diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index 6247f4f..1830d71 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -24,21 +24,11 @@ #include #include #include -#include #include #include "messagedelegate.h" #include "messagefeed.h" -QFont getFont (QFontDatabase::SystemFont type, bool bold, bool italic) { - QFont font = QFontDatabase::systemFont(type); - if (bold) - font.setBold(true); - if (italic) - font.setItalic(true); - - return font; -} constexpr int textMargin = 2; constexpr int statusIconSize = 16; @@ -48,15 +38,13 @@ constexpr int bubbleBorderRadius = 3; int MessageDelegate::avatarHeight(50); int MessageDelegate::margin(6); -bool MessageDelegate::fontsInitialized(false); -QFont MessageDelegate::bodyFont; -QFont MessageDelegate::nickFont; -QFont MessageDelegate::dateFont; -QFontMetrics MessageDelegate::nickMetrics(nickFont); -QFontMetrics MessageDelegate::dateMetrics(dateFont); - MessageDelegate::MessageDelegate(QObject* parent): QStyledItemDelegate(parent), + bodyFont(Shared::Global::getInstance()->defaultFont), + nickFont(Shared::Global::getInstance()->headerFont), + dateFont(Shared::Global::getInstance()->smallFont), + nickMetrics(Shared::Global::getInstance()->headerFontMetrics), + dateMetrics(Shared::Global::getInstance()->smallFontMetrics), bodyRenderer(new QTextDocument()), buttonHeight(0), buttonWidth(0), @@ -71,11 +59,6 @@ MessageDelegate::MessageDelegate(QObject* parent): currentId(""), selection(0, 0) { - if (!fontsInitialized) { - fontsInitialized = true; - initializeFonts(); - } - bodyRenderer->setDocumentMargin(0); bodyRenderer->setDefaultFont(bodyFont); @@ -812,11 +795,3 @@ void MessageDelegate::clearHelperWidget(const Models::FeedItem& data) const } } } - -void MessageDelegate::initializeFonts () { - bodyFont = getFont(QFontDatabase::GeneralFont, false, false); - nickFont = getFont(QFontDatabase::TitleFont, true, false); - dateFont = getFont(QFontDatabase::SmallestReadableFont, false, true); - nickMetrics = QFontMetrics(nickFont); - dateMetrics = QFontMetrics(dateFont); -} diff --git a/ui/widgets/messageline/messagedelegate.h b/ui/widgets/messageline/messagedelegate.h index 7a54e66..a05b4cd 100644 --- a/ui/widgets/messageline/messagedelegate.h +++ b/ui/widgets/messageline/messagedelegate.h @@ -93,9 +93,6 @@ protected: protected slots: void onButtonPushed() const; - -private: - static void initializeFonts(); private: class FeedButton : public QPushButton { @@ -103,13 +100,12 @@ private: QString messageId; }; - static bool fontsInitialized; - static QFont bodyFont; - static QFont nickFont; - static QFont dateFont; + const QFont& bodyFont; + const QFont& nickFont; + const QFont& dateFont; + const QFontMetrics& nickMetrics; + const QFontMetrics& dateMetrics; QTextDocument* bodyRenderer; - static QFontMetrics nickMetrics; - static QFontMetrics dateMetrics; int buttonHeight; int buttonWidth; diff --git a/ui/widgets/vcard/omemo/keydelegate.cpp b/ui/widgets/vcard/omemo/keydelegate.cpp index 646dfe9..e64cb19 100644 --- a/ui/widgets/vcard/omemo/keydelegate.cpp +++ b/ui/widgets/vcard/omemo/keydelegate.cpp @@ -16,22 +16,20 @@ #include "keydelegate.h" #include -#include #include "keysmodel.h" +#include -constexpr int minHeight = 50; -constexpr int minWidth = 400; constexpr uint8_t margin = 10; constexpr uint8_t partSize = 8; constexpr uint8_t maxSingleLineParts = 3; UI::KeyDelegate::KeyDelegate(QObject* parent): QStyledItemDelegate(parent), - fingerPrintFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)), - labelFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)), - fingerPrintMetrics(fingerPrintFont), - labelFontMetrics(labelFont), + fingerPrintFont(Shared::Global::getInstance()->defaultFont), + labelFont(Shared::Global::getInstance()->smallFont), + fingerPrintMetrics(Shared::Global::getInstance()->defaultFontMetrics), + labelFontMetrics(Shared::Global::getInstance()->smallFontMetrics), spaceWidth(fingerPrintMetrics.horizontalAdvance(" ")) {} diff --git a/ui/widgets/vcard/omemo/keydelegate.h b/ui/widgets/vcard/omemo/keydelegate.h index 2b0eb5b..4c914f3 100644 --- a/ui/widgets/vcard/omemo/keydelegate.h +++ b/ui/widgets/vcard/omemo/keydelegate.h @@ -33,10 +33,10 @@ public: void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; private: - QFont fingerPrintFont; - QFont labelFont; - QFontMetrics fingerPrintMetrics; - QFontMetrics labelFontMetrics; + const QFont& fingerPrintFont; + const QFont& labelFont; + const QFontMetrics& fingerPrintMetrics; + const QFontMetrics& labelFontMetrics; int spaceWidth; private: From b72a837754d7111b80bbb00fe717c27204425a1b Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 14 Jan 2023 18:34:14 +0300 Subject: [PATCH 081/137] trust level display in delegate, list size tweaking --- shared/enums.h | 2 + shared/global.cpp | 16 +++++ shared/global.h | 4 ++ ui/utils/CMakeLists.txt | 2 + ui/utils/expandinglist.cpp | 43 ++++++++++++++ ui/utils/expandinglist.h | 31 ++++++++++ ui/widgets/vcard/omemo/keydelegate.cpp | 81 ++++++++++++++++++++------ ui/widgets/vcard/omemo/keydelegate.h | 4 +- ui/widgets/vcard/omemo/omemo.cpp | 2 +- ui/widgets/vcard/omemo/omemo.ui | 45 ++++++++++++-- 10 files changed, 203 insertions(+), 27 deletions(-) create mode 100644 ui/utils/expandinglist.cpp create mode 100644 ui/utils/expandinglist.h diff --git a/shared/enums.h b/shared/enums.h index 7273b2e..9fb4aad 100644 --- a/shared/enums.h +++ b/shared/enums.h @@ -144,6 +144,8 @@ enum class TrustLevel { authenticated }; Q_ENUM_NS(TrustLevel) +static const TrustLevel TrustLevelHighest = TrustLevel::undecided; +static const TrustLevel TrustLevelLowest = TrustLevel::authenticated; enum class EncryptionProtocol { omemo diff --git a/shared/global.cpp b/shared/global.cpp index dc1cb80..7a1b494 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -113,6 +113,14 @@ Shared::Global::Global(): tr("Always Ask", "AccountPassword"), tr("KWallet", "AccountPassword") }), + trustLevel({ + tr("Undecided", "TrustLevel"), + tr("Automatically distrusted", "TrustLevel"), + tr("Manually distrusted", "TrustLevel"), + tr("Automatically trusted", "TrustLevel"), + tr("Manually trusted", "TrustLevel"), + tr("Authenticated", "TrustLevel") + }), accountPasswordDescription({ tr("Your password is going to be stored in config file in plain text", "AccountPasswordDescription"), tr("Your password is going to be stored in config file but jammed with constant encryption key you can find in program source code. It might look like encryption but it's not", "AccountPasswordDescription"), @@ -123,10 +131,12 @@ Shared::Global::Global(): defaultSystemPalette(QApplication::palette()), omemoSupport(OMEMO_SUPPORT), defaultFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont)), + monospaceFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)), smallFont(getFont(QFontDatabase::SmallestReadableFont, false, true)), headerFont(getFont(QFontDatabase::TitleFont, true, false, 1.1)), titleFont(getFont(QFontDatabase::TitleFont, true, false, 1.3)), defaultFontMetrics(defaultFont), + monospaceMetrics(monospaceFont), smallFontMetrics(smallFont), headerFontMetrics(headerFont), titleFontMetrics(titleFont), @@ -256,6 +266,11 @@ QString Shared::Global::getName(Shared::AccountPassword ap) return instance->accountPassword[static_cast(ap)]; } +QString Shared::Global::getName(Shared::TrustLevel tl) +{ + return instance->trustLevel[static_cast(tl)]; +} + void Shared::Global::setSupported(const QString& pluginName, bool support) { std::map::iterator itr = instance->pluginSupport.find(pluginName); @@ -405,3 +420,4 @@ FROM_INT_INPL(Shared::SubscriptionState) FROM_INT_INPL(Shared::AccountPassword) FROM_INT_INPL(Shared::Avatar) FROM_INT_INPL(Shared::Availability) +FROM_INT_INPL(Shared::TrustLevel) diff --git a/shared/global.h b/shared/global.h index 1fc4aca..b311f9f 100644 --- a/shared/global.h +++ b/shared/global.h @@ -74,6 +74,7 @@ namespace Shared { static QString getName(Role rl); static QString getName(Message::State rl); static QString getName(AccountPassword ap); + static QString getName(TrustLevel tl); static QString getDescription(AccountPassword ap); @@ -84,6 +85,7 @@ namespace Shared { const std::deque role; const std::deque messageState; const std::deque accountPassword; + const std::deque trustLevel; const std::deque accountPasswordDescription; @@ -103,10 +105,12 @@ namespace Shared { static void setStyle(const QString& style); const bool omemoSupport; QFont defaultFont; + QFont monospaceFont; QFont smallFont; QFont headerFont; QFont titleFont; QFontMetrics defaultFontMetrics; + QFontMetrics monospaceMetrics; QFontMetrics smallFontMetrics; QFontMetrics headerFontMetrics; QFontMetrics titleFontMetrics; diff --git a/ui/utils/CMakeLists.txt b/ui/utils/CMakeLists.txt index b46d30d..7e68f25 100644 --- a/ui/utils/CMakeLists.txt +++ b/ui/utils/CMakeLists.txt @@ -15,4 +15,6 @@ target_sources(squawk PRIVATE resizer.h shadowoverlay.cpp shadowoverlay.h + expandinglist.cpp + expandinglist.h ) diff --git a/ui/utils/expandinglist.cpp b/ui/utils/expandinglist.cpp new file mode 100644 index 0000000..6d1546d --- /dev/null +++ b/ui/utils/expandinglist.cpp @@ -0,0 +1,43 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "expandinglist.h" + +QSize ExpandingList::viewportSizeHint() const { + if (QAbstractItemView::sizeAdjustPolicy() != QAbstractScrollArea::AdjustToContents) + return QListView::viewportSizeHint(); + + if (model() == nullptr) + return QSize(0, 0); + if (model()->rowCount() == 0) + return QSize(0, 0); + +#if (QT_VERSION < QT_VERSION_CHECK(6, 2, 0)) + const int rowCount = model()->rowCount(); + int height = 0; + for (int i = 0; i < rowCount; i++) { + height += QListView::sizeHintForRow(i); + } + return QSize(QListView::viewportSizeHint().width(), height); +#else + return QListView::viewportSizeHint(); +#endif +} + +QSize ExpandingList::minimumSizeHint() const { + return viewportSizeHint(); +} + diff --git a/ui/utils/expandinglist.h b/ui/utils/expandinglist.h new file mode 100644 index 0000000..0b29e89 --- /dev/null +++ b/ui/utils/expandinglist.h @@ -0,0 +1,31 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 EXPANDINGLIST_H +#define EXPANDINGLIST_H + +#include + +class ExpandingList : public QListView { + Q_OBJECT +public: + using QListView::QListView; //explicit constructor inheriatnce + + QSize viewportSizeHint() const override; + QSize minimumSizeHint() const override; +}; + +#endif // EXPANDINGLIST_H diff --git a/ui/widgets/vcard/omemo/keydelegate.cpp b/ui/widgets/vcard/omemo/keydelegate.cpp index e64cb19..189d8df 100644 --- a/ui/widgets/vcard/omemo/keydelegate.cpp +++ b/ui/widgets/vcard/omemo/keydelegate.cpp @@ -16,6 +16,7 @@ #include "keydelegate.h" #include +#include #include "keysmodel.h" #include @@ -26,10 +27,12 @@ constexpr uint8_t maxSingleLineParts = 3; UI::KeyDelegate::KeyDelegate(QObject* parent): QStyledItemDelegate(parent), - fingerPrintFont(Shared::Global::getInstance()->defaultFont), + defaultFont(Shared::Global::getInstance()->defaultFont), + fingerPrintFont(Shared::Global::getInstance()->monospaceFont), labelFont(Shared::Global::getInstance()->smallFont), - fingerPrintMetrics(Shared::Global::getInstance()->defaultFontMetrics), - labelFontMetrics(Shared::Global::getInstance()->smallFontMetrics), + defaultMetrics(Shared::Global::getInstance()->defaultFontMetrics), + fingerPrintMetrics(Shared::Global::getInstance()->monospaceMetrics), + labelMetrics(Shared::Global::getInstance()->smallFontMetrics), spaceWidth(fingerPrintMetrics.horizontalAdvance(" ")) {} @@ -47,11 +50,14 @@ void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio painter->restore(); } + QRect r; + int maxRight = 0; + int leftOrigin = option.rect.left() + margin; QColor q = painter->pen().color(); q.setAlpha(180); QRect rect = option.rect; - rect.adjust(margin, margin, 0, 0); + rect.adjust(margin, margin, -margin, -margin); QVariant labelV = index.data(UI::KeysModel::Label); if (labelV.isValid()) { QString label = labelV.toString(); @@ -59,8 +65,9 @@ void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio painter->save(); painter->setFont(labelFont); painter->setPen(q); - painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, label); - rect.adjust(0, labelFontMetrics.lineSpacing(), 0, 0); + painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, label, &r); + rect.adjust(0, labelMetrics.lineSpacing(), 0, 0); + maxRight = std::max(r.width(), maxRight); painter->restore(); } } @@ -72,7 +79,6 @@ void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio QByteArray fingerPrint = fingerPrintV.toByteArray(); std::vector parts = getParts(fingerPrint); uint8_t partsLength = parts.size(); - QRect r; uint8_t firstLine; if (partsLength > maxSingleLineParts) firstLine = partsLength / 2 + partsLength % 2; @@ -81,15 +87,17 @@ void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio for (uint8_t i = 0; i < partsLength; ++i) { if (i == firstLine) { - rect.setLeft(option.rect.left() + margin); + maxRight = std::max(rect.left() - leftOrigin - margin, maxRight); + rect.setLeft(leftOrigin); rect.adjust(0, r.height() + fingerPrintMetrics.leading(), 0, 0); } painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, parts[i], &r); rect.adjust(r.width() + spaceWidth ,0, 0, 0); } + maxRight = std::max(rect.left() - leftOrigin - margin, maxRight); rect.adjust(0, r.height() + fingerPrintMetrics.leading(), 0, 0); - rect.setLeft(option.rect.left() + margin); + rect.setLeft(leftOrigin); painter->restore(); } @@ -102,12 +110,27 @@ void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio painter->save(); painter->setFont(labelFont); painter->setPen(q); - painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, last.toString()); - rect.adjust(0, labelFontMetrics.lineSpacing(), 0, 0); + painter->drawText(rect, Qt::AlignLeft | Qt::AlignTop, last.toString(), &r); + rect.adjust(0, labelMetrics.lineSpacing(), 0, 0); + maxRight = std::max(r.width(), maxRight); painter->restore(); } } - //painter->drawText(option.rect, option.displayAlignment, index.data().toString()); + + QVariant levelV = index.data(UI::KeysModel::TrustLevel); + if (levelV.isValid()) { + Shared::TrustLevel level = static_cast(levelV.toUInt()); + QString levelName = Shared::Global::getName(level); + + if (maxRight > 0) + maxRight += margin; + rect.setLeft(leftOrigin + maxRight); + rect.setTop(option.rect.top() + maxRight); + rect.setBottom(option.rect.bottom() - maxRight); + painter->setFont(defaultFont); + painter->drawText(rect, Qt::AlignLeft | Qt::AlignVCenter, levelName, &r); + } + painter->restore(); } @@ -117,10 +140,15 @@ QSize UI::KeyDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel int mh = margin * 2; int mw = margin * 2; + int width = 0; QVariant labelV = index.data(UI::KeysModel::Label); - if (labelV.isValid() && labelV.toString().size() > 0) { - mh += labelFontMetrics.lineSpacing(); + if (labelV.isValid()) { + QString label = labelV.toString(); + if (label.size() > 0) { + mh += labelMetrics.lineSpacing(); + width = labelMetrics.horizontalAdvance(label); + } } QVariant fingerPrintV = index.data(UI::KeysModel::FingerPrint); @@ -129,9 +157,8 @@ QSize UI::KeyDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel uint8_t parts = hex.size() / partSize; mh += fingerPrintMetrics.height(); - if (parts > maxSingleLineParts) { + if (parts > maxSingleLineParts) mh += fingerPrintMetrics.height() + fingerPrintMetrics.leading(); - } uint8_t firstLine; if (parts > maxSingleLineParts) @@ -139,13 +166,29 @@ QSize UI::KeyDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel else firstLine = parts; - mw += firstLine * partSize * spaceWidth + (firstLine - 1) * spaceWidth; + width = std::max(width, firstLine * fingerPrintMetrics.horizontalAdvance(hex, partSize) + (firstLine - 1) * spaceWidth); } QVariant lastV = index.data(UI::KeysModel::LastInteraction); - if (lastV.isValid() && lastV.toDateTime().isValid()) { - mh += labelFontMetrics.lineSpacing(); + if (lastV.isValid()) { + QDateTime last = lastV.toDateTime(); + if (last.isValid()) { + mh += labelMetrics.lineSpacing(); + QString dt = last.toString(); + width = std::max(labelMetrics.horizontalAdvance(dt), width); + } } + QVariant levelV = index.data(UI::KeysModel::TrustLevel); + if (levelV.isValid()) { + Shared::TrustLevel level = static_cast(levelV.toUInt()); + QString levelName = Shared::Global::getName(level); + if (width > 0) + width += margin; + + width += defaultMetrics.horizontalAdvance(levelName); + } + + mw += width; if (size.width() < mw) size.setWidth(mw); diff --git a/ui/widgets/vcard/omemo/keydelegate.h b/ui/widgets/vcard/omemo/keydelegate.h index 4c914f3..01b45c1 100644 --- a/ui/widgets/vcard/omemo/keydelegate.h +++ b/ui/widgets/vcard/omemo/keydelegate.h @@ -33,10 +33,12 @@ public: void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; private: + const QFont& defaultFont; const QFont& fingerPrintFont; const QFont& labelFont; + const QFontMetrics& defaultMetrics; const QFontMetrics& fingerPrintMetrics; - const QFontMetrics& labelFontMetrics; + const QFontMetrics& labelMetrics; int spaceWidth; private: diff --git a/ui/widgets/vcard/omemo/omemo.cpp b/ui/widgets/vcard/omemo/omemo.cpp index 0aea97c..c260ab8 100644 --- a/ui/widgets/vcard/omemo/omemo.cpp +++ b/ui/widgets/vcard/omemo/omemo.cpp @@ -18,7 +18,7 @@ #include "ui_omemo.h" #include -constexpr uint8_t fingerprintLength = 24; +constexpr uint8_t fingerprintLength = 32; Omemo::Omemo(QWidget* parent): QWidget(parent), diff --git a/ui/widgets/vcard/omemo/omemo.ui b/ui/widgets/vcard/omemo/omemo.ui index 1dc6dc9..d9f55b1 100644 --- a/ui/widgets/vcard/omemo/omemo.ui +++ b/ui/widgets/vcard/omemo/omemo.ui @@ -65,21 +65,24 @@ - + + + + 0 + 0 + + QAbstractScrollArea::AdjustToContents false - - QAbstractItemView::SelectColumns - QAbstractItemView::ScrollPerPixel - QAbstractItemView::ScrollPerItem + QAbstractItemView::ScrollPerPixel false @@ -122,7 +125,29 @@ - + + + + 0 + 0 + + + + QAbstractScrollArea::AdjustToContents + + + false + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + false + + @@ -156,6 +181,14 @@ + + + ExpandingList + QListView +
ui/utils/expandinglist.h
+ 1 +
+
From 73d83f55af829135db3f2879cf849e5c6b9ed144 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 15 Jan 2023 21:17:38 +0300 Subject: [PATCH 082/137] context menu to trust or distrust keys --- ui/widgets/vcard/omemo/keydelegate.cpp | 22 +++++--- ui/widgets/vcard/omemo/keysmodel.cpp | 71 +++++++++++++++++++++++--- ui/widgets/vcard/omemo/keysmodel.h | 7 +++ ui/widgets/vcard/omemo/omemo.cpp | 54 ++++++++++++++++++-- ui/widgets/vcard/omemo/omemo.h | 8 ++- 5 files changed, 144 insertions(+), 18 deletions(-) diff --git a/ui/widgets/vcard/omemo/keydelegate.cpp b/ui/widgets/vcard/omemo/keydelegate.cpp index 189d8df..d0023e5 100644 --- a/ui/widgets/vcard/omemo/keydelegate.cpp +++ b/ui/widgets/vcard/omemo/keydelegate.cpp @@ -43,6 +43,12 @@ void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio painter->save(); painter->setRenderHint(QPainter::Antialiasing, true); + QRect r; + int maxRight = 0; + int leftOrigin = option.rect.left() + margin; + QColor q = painter->pen().color(); + q.setAlpha(180); + QRect rect = option.rect; bool hover = option.state & QStyle::State_MouseOver; if (hover) { painter->save(); @@ -50,13 +56,15 @@ void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio painter->restore(); } - QRect r; - int maxRight = 0; - int leftOrigin = option.rect.left() + margin; - QColor q = painter->pen().color(); - q.setAlpha(180); + QVariant dirtyV = index.data(UI::KeysModel::Dirty); + if (dirtyV.isValid() && dirtyV.toBool()) { + painter->save(); + rect.setWidth(margin / 2); + painter->fillRect(rect, option.palette.brush(QPalette::Active, QPalette::Highlight)); + rect.setWidth(option.rect.width()); + painter->restore(); + } - QRect rect = option.rect; rect.adjust(margin, margin, -margin, -margin); QVariant labelV = index.data(UI::KeysModel::Label); if (labelV.isValid()) { @@ -167,6 +175,7 @@ QSize UI::KeyDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel firstLine = parts; width = std::max(width, firstLine * fingerPrintMetrics.horizontalAdvance(hex, partSize) + (firstLine - 1) * spaceWidth); + width += 1; //there is a mistake somewhere, this the cheapest way to compensate it } QVariant lastV = index.data(UI::KeysModel::LastInteraction); if (lastV.isValid()) { @@ -186,6 +195,7 @@ QSize UI::KeyDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel width += margin; width += defaultMetrics.horizontalAdvance(levelName); + width += 1; //there is a mistake somewhere, this the cheapest way to compensate it } mw += width; diff --git a/ui/widgets/vcard/omemo/keysmodel.cpp b/ui/widgets/vcard/omemo/keysmodel.cpp index 750e42a..1488370 100644 --- a/ui/widgets/vcard/omemo/keysmodel.cpp +++ b/ui/widgets/vcard/omemo/keysmodel.cpp @@ -24,12 +24,32 @@ const QHash UI::KeysModel::roles = { UI::KeysModel::KeysModel(QObject* parent): QAbstractListModel(parent), - keys() + keys(), + modified() { + } UI::KeysModel::~KeysModel() { + for (Shared::KeyInfo* key : keys) { + delete key; + } + for (std::pair& pair: modified) { + delete pair.second; + } +} + +std::deque UI::KeysModel::modifiedKeys() const { + std::deque response(modified.size()); + + int i = 0; + for (const std::pair& pair: modified) { + response[i] = *(pair.second); + ++i; + } + + return response; } void UI::KeysModel::addKey(const Shared::KeyInfo& info) { @@ -40,24 +60,34 @@ void UI::KeysModel::addKey(const Shared::KeyInfo& info) { QVariant UI::KeysModel::data(const QModelIndex& index, int role) const { int i = index.row(); + const Shared::KeyInfo* info; + bool dirty; + std::map::const_iterator itr = modified.find(i); + if (itr != modified.end()) { + info = itr->second; + dirty = true; + } else { + dirty = false; + info = keys[i]; + } QVariant answer; switch (role) { case Qt::DisplayRole: case Label: - answer = keys[i]->label; + answer = info->label; break; case FingerPrint: - answer = keys[i]->fingerPrint; + answer = info->fingerPrint; break; case TrustLevel: - answer = static_cast(keys[i]->trustLevel); + answer = static_cast(info->trustLevel); break; case LastInteraction: - answer = keys[i]->lastInteraction; + answer = info->lastInteraction; break; case Dirty: - answer = false; + answer = dirty; break; } @@ -78,7 +108,34 @@ QModelIndex UI::KeysModel::index(int row, int column, const QModelIndex& parent) return createIndex(row, column, keys[row]); } - +void UI::KeysModel::revertKey(int row) { + std::map::const_iterator itr = modified.find(row); + if (itr != modified.end()) { + modified.erase(itr); + QModelIndex index = createIndex(row, 0, keys[row]); + dataChanged(index, index); + } +} + +void UI::KeysModel::setTrustLevel(int row, Shared::TrustLevel level) { + std::map::const_iterator itr = modified.find(row); + Shared::KeyInfo* info; + if (itr == modified.end()) { + if (row < rowCount()) { + info = new Shared::KeyInfo(*(keys[row])); + modified.insert(std::make_pair(row, info)); + } else { + return; + } + } else { + info = itr->second; + } + + info->trustLevel = level; + + QModelIndex index = createIndex(row, 0, info); + dataChanged(index, index, {KeysModel::Dirty}); +} diff --git a/ui/widgets/vcard/omemo/keysmodel.h b/ui/widgets/vcard/omemo/keysmodel.h index 68f222c..e1a7606 100644 --- a/ui/widgets/vcard/omemo/keysmodel.h +++ b/ui/widgets/vcard/omemo/keysmodel.h @@ -40,6 +40,8 @@ public: QHash roleNames() const override; QModelIndex index(int row, int column, const QModelIndex & parent) const override; + std::deque modifiedKeys() const; + enum Roles { Label = Qt::UserRole + 1, FingerPrint, @@ -48,8 +50,13 @@ public: Dirty }; +public slots: + void revertKey(int index); + void setTrustLevel(int index, Shared::TrustLevel level); + private: std::deque keys; + std::map modified; private: static const QHash roles; diff --git a/ui/widgets/vcard/omemo/omemo.cpp b/ui/widgets/vcard/omemo/omemo.cpp index c260ab8..3a5ab73 100644 --- a/ui/widgets/vcard/omemo/omemo.cpp +++ b/ui/widgets/vcard/omemo/omemo.cpp @@ -26,7 +26,8 @@ Omemo::Omemo(QWidget* parent): keysDelegate(), unusedKeysDelegate(), keysModel(), - unusedKeysModel() + unusedKeysModel(), + contextMenu(new QMenu()) { m_ui->setupUi(this); @@ -36,15 +37,17 @@ Omemo::Omemo(QWidget* parent): m_ui->keysView->setModel(&keysModel); m_ui->unusedKeysView->setItemDelegate(&unusedKeysDelegate); m_ui->unusedKeysView->setModel(&unusedKeysModel); + + m_ui->keysView->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_ui->keysView, &QWidget::customContextMenuRequested, this, &Omemo::onActiveKeysContextMenu); } Omemo::~Omemo() { - + contextMenu->deleteLater(); } -void Omemo::generateMockData() -{ +void Omemo::generateMockData() { std::random_device rd; std::uniform_int_distribution dist(CHAR_MIN, CHAR_MAX); for (int i = 0; i < 5; ++i) { @@ -63,3 +66,46 @@ void Omemo::generateMockData() keysModel.addKey(info); } } + +void Omemo::onActiveKeysContextMenu(const QPoint& pos) { + contextMenu->clear(); + QModelIndex index = m_ui->keysView->indexAt(pos); + if (index.isValid()) { + QVariant dirtyV = index.data(UI::KeysModel::Dirty); + if (dirtyV.isValid() && dirtyV.toBool()) { + QAction* rev = contextMenu->addAction(Shared::icon("clean"), tr("Revert changes")); + connect(rev, &QAction::triggered, std::bind(&UI::KeysModel::revertKey, &keysModel, index.row())); + } + + QVariant levelV = index.data(UI::KeysModel::TrustLevel); + if (levelV.isValid()) { + Shared::TrustLevel level = static_cast(levelV.toUInt()); + if (level == Shared::TrustLevel::undecided || + level == Shared::TrustLevel::automaticallyDistrusted || + level == Shared::TrustLevel::manuallyDistrusted + ) { + QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Trust")); + connect(rev, &QAction::triggered, + std::bind(&UI::KeysModel::setTrustLevel, &keysModel, + index.row(), Shared::TrustLevel::manuallyTrusted + ) + ); + } + + if (level == Shared::TrustLevel::undecided || + level == Shared::TrustLevel::automaticallyTrusted || + level == Shared::TrustLevel::manuallyTrusted || + level == Shared::TrustLevel::authenticated + ) { + QAction* rev = contextMenu->addAction(Shared::icon("unfavorite"), tr("Distrust")); + connect(rev, &QAction::triggered, + std::bind(&UI::KeysModel::setTrustLevel, &keysModel, + index.row(), Shared::TrustLevel::manuallyDistrusted + ) + ); + } + } + } + + contextMenu->popup(m_ui->keysView->viewport()->mapToGlobal(pos)); +} diff --git a/ui/widgets/vcard/omemo/omemo.h b/ui/widgets/vcard/omemo/omemo.h index 2bc0433..59e3dac 100644 --- a/ui/widgets/vcard/omemo/omemo.h +++ b/ui/widgets/vcard/omemo/omemo.h @@ -17,11 +17,13 @@ #ifndef VCARD_OMEMO_H #define VCARD_OMEMO_H -#include +#include #include +#include #include "keysmodel.h" #include "keydelegate.h" +#include "shared/icons.h" namespace Ui { @@ -34,6 +36,9 @@ public: Omemo(QWidget* parent = nullptr); ~Omemo(); +private slots: + void onActiveKeysContextMenu(const QPoint& pos); + private: void generateMockData(); @@ -43,6 +48,7 @@ private: UI::KeyDelegate unusedKeysDelegate; UI::KeysModel keysModel; UI::KeysModel unusedKeysModel; + QMenu* contextMenu; }; #endif // VCARD_OMEMO_H From 3c6b611a411e978d4b9e7e36709b342d6e344ba1 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 29 Jan 2023 20:26:54 +0300 Subject: [PATCH 083/137] keeping up with qxmpp --- CMakeLists.txt | 1 + core/account.cpp | 27 +++----- core/adapterfunctions.h | 15 +++++ core/handlers/messagehandler.cpp | 29 ++++++++ core/handlers/omemohandler.cpp | 54 +++++++-------- core/handlers/omemohandler.h | 23 +++---- core/handlers/trusthandler.cpp | 109 ++++++++++--------------------- core/handlers/trusthandler.h | 35 +++++----- external/qxmpp | 2 +- ui/widgets/about.cpp | 4 ++ 10 files changed, 146 insertions(+), 153 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b40e876..01a9bd1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,6 +130,7 @@ if (NOT SYSTEM_QXMPP) target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/base) target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/client) + target_include_directories(squawk PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/qxmpp/src) if (WITH_OMEMO) target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/omemo) diff --git a/core/account.cpp b/core/account.cpp index e1bbfb9..bb3ebea 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -108,23 +108,19 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& client.addExtension(om); if (oh->hasOwnDevice()) { - QFuture future = om->load(); + QXmppTask future = om->load(); loadingOmemo = true; - - QFutureWatcher *watcher = new QFutureWatcher(this); - QObject::connect(watcher, &QFutureWatcherBase::finished, [watcher, this] () { + future.then(this, [this] (bool result) { loadingOmemo = false; if (state == Shared::ConnectionState::scheduled) { client.connectToServer(config, presence); } - if (watcher->result()) { + if (result) { qDebug() << "successfully loaded OMEMO data for account" << getName(); } else { qDebug() << "couldn't load OMEMO data for account" << getName(); } - watcher->deleteLater(); }); - watcher->setFuture(future); } #endif @@ -235,20 +231,17 @@ void Core::Account::onClientStateChange(QXmppClient::State st) #ifdef WITH_OMEMO if (!oh->hasOwnDevice()) { qDebug() << "setting up OMEMO data for account" << getName(); - QFuture future = om->setUp(); - QFutureWatcher *watcher = new QFutureWatcher(this); - QObject::connect(watcher, &QFutureWatcherBase::finished, [watcher, this] () { - if (watcher->result()) { + QXmppTask future = om->setUp(); + future.then(this, [this] (bool result) { + if (result) qDebug() << "successfully set up OMEMO data for account" << getName(); - } else { + else qDebug() << "couldn't set up OMEMO data for account" << getName(); - } - watcher->deleteLater(); - if (state == Shared::ConnectionState::connected) { + + if (state == Shared::ConnectionState::connected) runDiscoveryService(); - } + }); - watcher->setFuture(future); } else { runDiscoveryService(); } diff --git a/core/adapterfunctions.h b/core/adapterfunctions.h index 6e50a75..287816b 100644 --- a/core/adapterfunctions.h +++ b/core/adapterfunctions.h @@ -19,6 +19,8 @@ #define CORE_ADAPTER_FUNCTIONS_H #include +#include +#include #include namespace Core { @@ -26,6 +28,19 @@ namespace Core { void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card); void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard); +template +QXmppTask makeReadyTask(T &&value) { + QXmppPromise promise; + promise.finish(std::move(value)); + return promise.task(); +} + +inline QXmppTask makeReadyTask() { + QXmppPromise promise; + promise.finish(); + return promise.task(); +} + } diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index b6d32b9..b11ea2a 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -29,6 +29,35 @@ Core::MessageHandler::MessageHandler(Core::Account* account): void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) { +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) +#ifdef WITH_OMEMO + switch (msg.encryptionMethod()) { + case QXmpp::NoEncryption: + break; //just do nothing + case QXmpp::UnknownEncryption: + qDebug() << "Account" << acc->getName() << "received a message with unknown encryption type"; + break; //let it go the way it is, there is nothing I can do here + case QXmpp::Otr: + qDebug() << "Account" << acc->getName() << "received an OTR encrypted message, not supported yet"; + break; //let it go the way it is, there is nothing I can do yet + case QXmpp::LegacyOpenPgp: + qDebug() << "Account" << acc->getName() << "received an LegacyOpenPgp encrypted message, not supported yet"; + break; //let it go the way it is, there is nothing I can do yet + case QXmpp::Ox: + qDebug() << "Account" << acc->getName() << "received an Ox encrypted message, not supported yet"; + break; //let it go the way it is, there is nothing I can do yet + case QXmpp::Omemo0: + qDebug() << "Account" << acc->getName() << "received an Omemo0 encrypted message, not supported yet"; + break; //let it go the way it is, there is nothing I can do yet + case QXmpp::Omemo1: + qDebug() << "Account" << acc->getName() << "received an Omemo1 encrypted message, not supported yet"; + break; //let it go the way it is, there is nothing I can do yet + case QXmpp::Omemo2: + qDebug() << "Account" << acc->getName() << "received an Omemo2 encrypted message, not supported yet"; + break; //let it go the way it is, there is nothing I can do yet + } +#endif +#endif bool handled = false; switch (msg.type()) { case QXmppMessage::Normal: diff --git a/core/handlers/omemohandler.cpp b/core/handlers/omemohandler.cpp index 57d9749..2b864e1 100644 --- a/core/handlers/omemohandler.cpp +++ b/core/handlers/omemohandler.cpp @@ -17,6 +17,7 @@ #include #include "omemohandler.h" #include "core/account.h" +#include "core/adapterfunctions.h" Core::OmemoHandler::OmemoHandler(Account* account) : QXmppOmemoStorage(), @@ -46,13 +47,7 @@ bool Core::OmemoHandler::hasOwnDevice() { return ownDevice.has_value(); } -QFuture Core::OmemoHandler::emptyVoidFuture() { - QFutureInterface result(QFutureInterfaceBase::Started); - result.reportFinished(); - return result.future(); -} - -QFuture Core::OmemoHandler::allData() { +QXmppTask Core::OmemoHandler::allData() { OmemoData data; data.ownDevice = ownDevice; @@ -72,13 +67,10 @@ QFuture Core::OmemoHandler::allData() { data.devices.insert(pair.first, pair.second); } - QFutureInterface result(QFutureInterfaceBase::Started); - result.reportResult(std::move(data)); - result.reportFinished(); - return result.future(); + return Core::makeReadyTask(std::move(data)); } -QFuture Core::OmemoHandler::addDevice(const QString& jid, uint32_t deviceId, const QXmppOmemoStorage::Device& device) { +QXmppTask Core::OmemoHandler::addDevice(const QString& jid, uint32_t deviceId, const QXmppOmemoStorage::Device& device) { QHash devs; bool had = true; try { @@ -94,23 +86,23 @@ QFuture Core::OmemoHandler::addDevice(const QString& jid, uint32_t deviceI devices->addRecord(jid, devs); } - return emptyVoidFuture(); + return Core::makeReadyTask(); } -QFuture Core::OmemoHandler::addPreKeyPairs(const QHash& keyPairs) { +QXmppTask Core::OmemoHandler::addPreKeyPairs(const QHash& keyPairs) { for (QHash::const_iterator itr = keyPairs.begin(), end = keyPairs.end(); itr != end; ++itr) { preKeyPairs->forceRecord(itr.key(), itr.value()); } - return emptyVoidFuture(); + return Core::makeReadyTask(); } -QFuture Core::OmemoHandler::addSignedPreKeyPair(uint32_t keyId, const QXmppOmemoStorage::SignedPreKeyPair& keyPair) { +QXmppTask Core::OmemoHandler::addSignedPreKeyPair(uint32_t keyId, const QXmppOmemoStorage::SignedPreKeyPair& keyPair) { signedPreKeyPairs->forceRecord(keyId, std::make_pair(keyPair.creationDate, keyPair.data)); - return emptyVoidFuture(); + return Core::makeReadyTask(); } -QFuture Core::OmemoHandler::removeDevice(const QString& jid, uint32_t deviceId) { +QXmppTask Core::OmemoHandler::removeDevice(const QString& jid, uint32_t deviceId) { QHash devs = devices->getRecord(jid); devs.remove(deviceId); if (devs.isEmpty()) { @@ -118,25 +110,27 @@ QFuture Core::OmemoHandler::removeDevice(const QString& jid, uint32_t devi } else { devices->changeRecord(jid, devs); } - return emptyVoidFuture(); + return Core::makeReadyTask(); } -QFuture Core::OmemoHandler::removeDevices(const QString& jid) { +QXmppTask Core::OmemoHandler::removeDevices(const QString& jid) { devices->removeRecord(jid); - return emptyVoidFuture(); + return Core::makeReadyTask(); } -QFuture Core::OmemoHandler::removePreKeyPair(uint32_t keyId) { +QXmppTask Core::OmemoHandler::removePreKeyPair(uint32_t keyId) { preKeyPairs->removeRecord(keyId); - return emptyVoidFuture(); + return Core::makeReadyTask(); } -QFuture Core::OmemoHandler::removeSignedPreKeyPair(uint32_t keyId) { - signedPreKeyPairs->removeRecord(keyId); - return emptyVoidFuture(); +QXmppTask Core::OmemoHandler::removeSignedPreKeyPair(uint32_t keyId) { + try { + signedPreKeyPairs->removeRecord(keyId); + } catch (const DataBase::NotFound& e) {} + return Core::makeReadyTask(); } -QFuture Core::OmemoHandler::setOwnDevice(const std::optional& device) { +QXmppTask Core::OmemoHandler::setOwnDevice(const std::optional& device) { bool had = ownDevice.has_value(); ownDevice = device; if (ownDevice.has_value()) { @@ -150,14 +144,14 @@ QFuture Core::OmemoHandler::setOwnDevice(const std::optional& d meta->removeRecord("ownDevice"); } } - return emptyVoidFuture(); + return Core::makeReadyTask(); } -QFuture Core::OmemoHandler::resetAll() { +QXmppTask Core::OmemoHandler::resetAll() { ownDevice = std::nullopt; db.drop(); - return emptyVoidFuture(); + return Core::makeReadyTask(); } QDataStream & operator >> (QDataStream& in, QXmppOmemoStorage::Device& device) { diff --git a/core/handlers/omemohandler.h b/core/handlers/omemohandler.h index cea9603..7783c04 100644 --- a/core/handlers/omemohandler.h +++ b/core/handlers/omemohandler.h @@ -34,27 +34,24 @@ public: OmemoHandler(Account* account); ~OmemoHandler() override; - QFuture allData() override; + QXmppTask allData() override; - QFuture setOwnDevice(const std::optional &device) override; + QXmppTask setOwnDevice(const std::optional &device) override; - QFuture addSignedPreKeyPair(uint32_t keyId, const QXmppOmemoStorage::SignedPreKeyPair &keyPair) override; - QFuture removeSignedPreKeyPair(uint32_t keyId) override; + QXmppTask addSignedPreKeyPair(uint32_t keyId, const QXmppOmemoStorage::SignedPreKeyPair &keyPair) override; + QXmppTask removeSignedPreKeyPair(uint32_t keyId) override; - QFuture addPreKeyPairs(const QHash &keyPairs) override; - QFuture removePreKeyPair(uint32_t keyId) override; + QXmppTask addPreKeyPairs(const QHash &keyPairs) override; + QXmppTask removePreKeyPair(uint32_t keyId) override; - QFuture addDevice(const QString &jid, uint32_t deviceId, const Device &device) override; - QFuture removeDevice(const QString &jid, uint32_t deviceId) override; - QFuture removeDevices(const QString &jid) override; + QXmppTask addDevice(const QString &jid, uint32_t deviceId, const Device &device) override; + QXmppTask removeDevice(const QString &jid, uint32_t deviceId) override; + QXmppTask removeDevices(const QString &jid) override; - QFuture resetAll() override; + QXmppTask resetAll() override; bool hasOwnDevice(); -private: - static QFuture emptyVoidFuture(); - private: Account* acc; std::optional ownDevice; diff --git a/core/handlers/trusthandler.cpp b/core/handlers/trusthandler.cpp index caaaf0e..3ea6e47 100644 --- a/core/handlers/trusthandler.cpp +++ b/core/handlers/trusthandler.cpp @@ -16,6 +16,7 @@ #include "trusthandler.h" #include "core/account.h" +#include "core/adapterfunctions.h" using namespace Core; @@ -75,37 +76,25 @@ Core::TrustHandler::KeyCache * Core::TrustHandler::createNewCache(const QString& return cache; } - -QFuture Core::TrustHandler::emptyVoidFuture() { - QFutureInterface result(QFutureInterfaceBase::Started); - result.reportFinished(); - return result.future(); -} - - -QFuture Core::TrustHandler::resetAll(const QString& encryption) { +QXmppTask Core::TrustHandler::resetAll(const QString& encryption) { securityPolicies->removeRecord(encryption); ownKeys->removeRecord(encryption); getCache(encryption)->drop(); - return emptyVoidFuture(); + return Core::makeReadyTask(); } -QFuture Core::TrustHandler::trustLevel( +QXmppTask Core::TrustHandler::trustLevel( const QString& encryption, const QString& keyOwnerJid, const QByteArray& keyId) { Keys map = getCache(encryption)->getRecord(keyOwnerJid); Shared::TrustLevel level = map.at(keyId); - - QFutureInterface result(QFutureInterfaceBase::Started); - result.reportResult(convert(level)); - result.reportFinished(); - return result.future(); + return Core::makeReadyTask(std::move(convert(level))); } -QFuture>> Core::TrustHandler::setTrustLevel( +QXmppTask>> Core::TrustHandler::setTrustLevel( const QString& encryption, const QList& keyOwnerJids, QXmpp::TrustLevel oldTrustLevel, @@ -130,14 +119,10 @@ QFuture>> Core::TrustHandler::set cache->changeRecord(keyOwnerJid, map); } } - - QFutureInterface>> result(QFutureInterfaceBase::Started); - result.reportResult(modifiedKeys); - result.reportFinished(); - return result.future(); + return Core::makeReadyTask(std::move(modifiedKeys)); } -QFuture>> Core::TrustHandler::setTrustLevel( +QXmppTask>> Core::TrustHandler::setTrustLevel( const QString& encryption, const QMultiHash& keyIds, QXmpp::TrustLevel trustLevel) @@ -160,14 +145,10 @@ QFuture>> Core::TrustHandler::set cache->changeRecord(keyOwnerJid, map); } } - - QFutureInterface>> result(QFutureInterfaceBase::Started); - result.reportResult(modifiedKeys); - result.reportFinished(); - return result.future(); + return Core::makeReadyTask(std::move(modifiedKeys)); } -QFuture TrustHandler::hasKey(const QString& encryption, +QXmppTask TrustHandler::hasKey(const QString& encryption, const QString& keyOwnerJid, QXmpp::TrustLevels trustLevels) { @@ -182,14 +163,10 @@ QFuture TrustHandler::hasKey(const QString& encryption, } } } catch (const DataBase::NotFound& e) {} - - QFutureInterface result(QFutureInterfaceBase::Started); - result.reportResult(found); - result.reportFinished(); - return result.future(); + return Core::makeReadyTask(std::move(found)); } -QFuture>> TrustHandler::keys( +QXmppTask>> TrustHandler::keys( const QString& encryption, const QList& keyOwnerJids, QXmpp::TrustLevels trustLevels) @@ -209,14 +186,10 @@ QFuture>> TrustHandler::keys } } catch (const DataBase::NotFound& e) {} } - - QFutureInterface result(QFutureInterfaceBase::Started); - result.reportResult(res); - result.reportFinished(); - return result.future(); + return Core::makeReadyTask(std::move(res)); } -QFuture>> TrustHandler::keys( +QXmppTask>> TrustHandler::keys( const QString& encryption, QXmpp::TrustLevels trustLevels) { @@ -231,24 +204,20 @@ QFuture>> TrustHandler: } } } - - QFutureInterface> result(QFutureInterfaceBase::Started); - result.reportResult(res); - result.reportFinished(); - return result.future(); + return Core::makeReadyTask(std::move(res)); } -QFuture TrustHandler::removeKeys(const QString& encryption) { +QXmppTask TrustHandler::removeKeys(const QString& encryption) { getCache(encryption)->drop(); - return emptyVoidFuture(); + return Core::makeReadyTask(); } -QFuture TrustHandler::removeKeys(const QString& encryption, const QString& keyOwnerJid) { +QXmppTask TrustHandler::removeKeys(const QString& encryption, const QString& keyOwnerJid) { getCache(encryption)->removeRecord(keyOwnerJid); - return emptyVoidFuture(); + return Core::makeReadyTask(); } -QFuture TrustHandler::removeKeys(const QString& encryption, const QList& keyIds) { +QXmppTask TrustHandler::removeKeys(const QString& encryption, const QList& keyIds) { std::set set; for (const QByteArray& keyId : keyIds) { set.insert(keyId); @@ -278,10 +247,10 @@ QFuture TrustHandler::removeKeys(const QString& encryption, const QListreplaceAll(data); } - return emptyVoidFuture(); + return Core::makeReadyTask(); } -QFuture TrustHandler::addKeys( +QXmppTask TrustHandler::addKeys( const QString& encryption, const QString& keyOwnerJid, const QList& keyIds, @@ -308,61 +277,53 @@ QFuture TrustHandler::addKeys( cache->addRecord(keyOwnerJid, data); } - return emptyVoidFuture(); + return Core::makeReadyTask(); } -QFuture TrustHandler::ownKey(const QString& encryption) { +QXmppTask TrustHandler::ownKey(const QString& encryption) { QByteArray res; try { res = ownKeys->getRecord(encryption); } catch (const DataBase::NotFound& e) {} - - QFutureInterface result(QFutureInterfaceBase::Started); - result.reportResult(res); - result.reportFinished(); - return result.future(); + return Core::makeReadyTask(std::move(res)); } -QFuture TrustHandler::resetOwnKey(const QString& encryption) { +QXmppTask TrustHandler::resetOwnKey(const QString& encryption) { try { ownKeys->removeRecord(encryption); } catch (const DataBase::NotFound& e) {} - return emptyVoidFuture(); + return Core::makeReadyTask(); } -QFuture TrustHandler::setOwnKey(const QString& encryption, const QByteArray& keyId) { +QXmppTask TrustHandler::setOwnKey(const QString& encryption, const QByteArray& keyId) { ownKeys->forceRecord(encryption, keyId); - return emptyVoidFuture(); + return Core::makeReadyTask(); } -QFuture TrustHandler::securityPolicy(const QString& encryption) { +QXmppTask TrustHandler::securityPolicy(const QString& encryption) { QXmpp::TrustSecurityPolicy res; try { res = static_cast(securityPolicies->getRecord(encryption)); } catch (const DataBase::NotFound& e) {} - - QFutureInterface result(QFutureInterfaceBase::Started); - result.reportResult(res); - result.reportFinished(); - return result.future(); + return Core::makeReadyTask(std::move(res)); } -QFuture TrustHandler::resetSecurityPolicy(const QString& encryption) { +QXmppTask TrustHandler::resetSecurityPolicy(const QString& encryption) { try { securityPolicies->removeRecord(encryption); } catch (const DataBase::NotFound& e) {} - return emptyVoidFuture(); + return Core::makeReadyTask(); } -QFuture TrustHandler::setSecurityPolicy( +QXmppTask TrustHandler::setSecurityPolicy( const QString& encryption, QXmpp::TrustSecurityPolicy securityPolicy) { uint8_t pol = securityPolicy; securityPolicies->forceRecord(encryption, pol); - return emptyVoidFuture(); + return Core::makeReadyTask(); } Shared::TrustLevel Core::TrustHandler::convert(Core::TrustHandler::TL level) diff --git a/core/handlers/trusthandler.h b/core/handlers/trusthandler.h index b1fe0b4..e46c7b3 100644 --- a/core/handlers/trusthandler.h +++ b/core/handlers/trusthandler.h @@ -41,29 +41,28 @@ public: typedef std::map Keys; typedef DataBase::Cache KeyCache; - virtual QFuture resetAll(CSR encryption); - virtual QFuture trustLevel(CSR encryption, CSR keyOwnerJid, const QByteArray& keyId); - virtual QFuture setTrustLevel(CSR encryption, CLSR keyOwnerJids, TL oldTrustLevel, TL newTrustLevel); - virtual QFuture setTrustLevel(CSR encryption, const MultySB& keyIds, TL trustLevel); - virtual QFuture hasKey(CSR encryption, CSR keyOwnerJid, QXmpp::TrustLevels trustLevels); - virtual QFuture keys(CSR encryption, CLSR keyOwnerJids, QXmpp::TrustLevels trustLevels); - virtual QFuture> keys(CSR encryption, QXmpp::TrustLevels trustLevels); - virtual QFuture removeKeys(CSR encryption); - virtual QFuture removeKeys(CSR encryption, CSR keyOwnerJid); - virtual QFuture removeKeys(CSR encryption, CLBAR keyIds); - virtual QFuture addKeys(CSR encryption, CSR keyOwnerJid, CLBAR keyIds, TL trustLevel); - virtual QFuture ownKey(CSR encryption); - virtual QFuture resetOwnKey(CSR encryption); - virtual QFuture setOwnKey(CSR encryption, const QByteArray& keyId); - virtual QFuture securityPolicy(CSR encryption); - virtual QFuture resetSecurityPolicy(CSR encryption); - virtual QFuture setSecurityPolicy(CSR encryption, QXmpp::TrustSecurityPolicy securityPolicy); + virtual QXmppTask resetAll(CSR encryption); + virtual QXmppTask trustLevel(CSR encryption, CSR keyOwnerJid, const QByteArray& keyId); + virtual QXmppTask setTrustLevel(CSR encryption, CLSR keyOwnerJids, TL oldTrustLevel, TL newTrustLevel); + virtual QXmppTask setTrustLevel(CSR encryption, const MultySB& keyIds, TL trustLevel); + virtual QXmppTask hasKey(CSR encryption, CSR keyOwnerJid, QXmpp::TrustLevels trustLevels); + virtual QXmppTask keys(CSR encryption, CLSR keyOwnerJids, QXmpp::TrustLevels trustLevels); + virtual QXmppTask> keys(CSR encryption, QXmpp::TrustLevels trustLevels); + virtual QXmppTask removeKeys(CSR encryption); + virtual QXmppTask removeKeys(CSR encryption, CSR keyOwnerJid); + virtual QXmppTask removeKeys(CSR encryption, CLBAR keyIds); + virtual QXmppTask addKeys(CSR encryption, CSR keyOwnerJid, CLBAR keyIds, TL trustLevel); + virtual QXmppTask ownKey(CSR encryption); + virtual QXmppTask resetOwnKey(CSR encryption); + virtual QXmppTask setOwnKey(CSR encryption, const QByteArray& keyId); + virtual QXmppTask securityPolicy(CSR encryption); + virtual QXmppTask resetSecurityPolicy(CSR encryption); + virtual QXmppTask setSecurityPolicy(CSR encryption, QXmpp::TrustSecurityPolicy securityPolicy); static TL convert(Shared::TrustLevel level); static Shared::TrustLevel convert(TL level); private: - static QFuture emptyVoidFuture(); KeyCache* createNewCache(const QString& encryption); KeyCache* getCache(const QString& encryption); diff --git a/external/qxmpp b/external/qxmpp index befab2f..d2c2acd 160000 --- a/external/qxmpp +++ b/external/qxmpp @@ -1 +1 @@ -Subproject commit befab2fe2e71330170bba48f173258be724c65b9 +Subproject commit d2c2acd4848f815d0dc3d108f8bc306f9015fc89 diff --git a/ui/widgets/about.cpp b/ui/widgets/about.cpp index 3782a94..f46c661 100644 --- a/ui/widgets/about.cpp +++ b/ui/widgets/about.cpp @@ -19,10 +19,14 @@ #include #include +#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 3, 0) static const std::string QXMPP_VERSION_PATCH(std::to_string(QXMPP_VERSION & 0xff)); static const std::string QXMPP_VERSION_MINOR(std::to_string((QXMPP_VERSION & 0xff00) >> 8)); static const std::string QXMPP_VERSION_MAJOR(std::to_string(QXMPP_VERSION >> 16)); static const QString QXMPP_VERSION_STRING = QString::fromStdString(QXMPP_VERSION_MAJOR + "." + QXMPP_VERSION_MINOR + "." + QXMPP_VERSION_PATCH); +#else +static const QString QXMPP_VERSION_STRING = QXmppVersion(); +#endif About::About(QWidget* parent): QWidget(parent), From bb304ce77453d7a789ead1671642d7192277f9ab Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 30 Jan 2023 20:52:26 +0300 Subject: [PATCH 084/137] just some unfinished thoughts --- main/main.cpp | 2 ++ shared/CMakeLists.txt | 2 ++ shared/info.cpp | 32 ++++++++++++++++++++++++++++++ shared/info.h | 46 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 shared/info.cpp create mode 100644 shared/info.h diff --git a/main/main.cpp b/main/main.cpp index 3e9add3..9147ef0 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -20,6 +20,7 @@ #include "shared/messageinfo.h" #include "shared/pathcheck.h" #include "shared/identity.h" +#include "shared/info.h" #include "main/application.h" #include "core/signalcatcher.h" #include "core/squawk.h" @@ -51,6 +52,7 @@ int main(int argc, char *argv[]) qRegisterMetaType("Shared::ConnectionState"); qRegisterMetaType("Shared::Availability"); qRegisterMetaType("Shared::KeyInfo"); + qRegisterMetaType("Shared::Info"); #ifdef WITH_OMEMO qRegisterMetaType("QXmppOmemoStorage::OwnDevice"); qRegisterMetaTypeStreamOperators("QXmppOmemoStorage::OwnDevice"); diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index a227163..9080fb6 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -28,4 +28,6 @@ target_sources(squawk PRIVATE field.cpp keyinfo.cpp keyinfo.h + info.cpp + info.h ) diff --git a/shared/info.cpp b/shared/info.cpp new file mode 100644 index 0000000..bda2b9f --- /dev/null +++ b/shared/info.cpp @@ -0,0 +1,32 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "info.h" + +Shared::Info::Info(): + vcard(), + activeKeys(), + inactiveKeys() +{} + +Shared::Info::Info(const Shared::Info& other): + vcard(other.vcard), + activeKeys(other.activeKeys), + inactiveKeys(other.inactiveKeys) +{} + +Shared::Info::~Info() +{} diff --git a/shared/info.h b/shared/info.h new file mode 100644 index 0000000..1692991 --- /dev/null +++ b/shared/info.h @@ -0,0 +1,46 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 SHARED_INFO_H +#define SHARED_INFO_H + +#include "vcard.h" +#include "keyinfo.h" + +#include + +namespace Shared { + +/** + * This class should contain all nessesary data to display + * roster element info (contact, or out of roster contact, or MUC, or MIX in the future) + * + * under development yet + */ +class Info { +public: + Info(); + Info(const Info& other); + ~Info(); + + VCard vcard; + std::list activeKeys; + std::list inactiveKeys; +}; + +} + +#endif // SHARED_INFO_H From 4af16b75bf864aaac391276f70dc45898be31a01 Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 1 Feb 2023 18:56:00 +0300 Subject: [PATCH 085/137] started refactoring of the VCard UI --- shared/info.cpp | 12 + shared/info.h | 3 + ui/models/CMakeLists.txt | 2 + ui/models/info/CMakeLists.txt | 10 + .../info/emails.cpp} | 63 +-- .../emailsmodel.h => models/info/emails.h} | 16 +- ui/models/info/omemo/CMakeLists.txt | 4 + .../info/omemo/keys.cpp} | 31 +- .../keysmodel.h => models/info/omemo/keys.h} | 14 +- .../info/phones.cpp} | 64 +-- .../phonesmodel.h => models/info/phones.h} | 20 +- ui/widgets/CMakeLists.txt | 1 + ui/widgets/info/CMakeLists.txt | 27 ++ ui/widgets/info/contactcontacts.cpp | 31 ++ ui/widgets/info/contactcontacts.h | 41 ++ ui/widgets/info/contactcontacts.ui | 282 +++++++++++ ui/widgets/info/contactgeneral.cpp | 28 ++ ui/widgets/info/contactgeneral.h | 41 ++ ui/widgets/info/contactgeneral.ui | 448 ++++++++++++++++++ ui/widgets/info/info.cpp | 30 ++ ui/widgets/info/info.h | 45 ++ ui/widgets/info/info.ui | 137 ++++++ .../{vcard => info}/omemo/CMakeLists.txt | 2 - .../{vcard => info}/omemo/keydelegate.cpp | 20 +- .../{vcard => info}/omemo/keydelegate.h | 0 ui/widgets/{vcard => info}/omemo/omemo.cpp | 10 +- ui/widgets/{vcard => info}/omemo/omemo.h | 6 +- ui/widgets/{vcard => info}/omemo/omemo.ui | 0 ui/widgets/room.h | 2 +- ui/widgets/vcard/CMakeLists.txt | 8 - ui/widgets/vcard/vcard.cpp | 8 +- ui/widgets/vcard/vcard.h | 10 +- 32 files changed, 1250 insertions(+), 166 deletions(-) create mode 100644 ui/models/info/CMakeLists.txt rename ui/{widgets/vcard/emailsmodel.cpp => models/info/emails.cpp} (80%) rename ui/{widgets/vcard/emailsmodel.h => models/info/emails.h} (87%) create mode 100644 ui/models/info/omemo/CMakeLists.txt rename ui/{widgets/vcard/omemo/keysmodel.cpp => models/info/omemo/keys.cpp} (80%) rename ui/{widgets/vcard/omemo/keysmodel.h => models/info/omemo/keys.h} (89%) rename ui/{widgets/vcard/phonesmodel.cpp => models/info/phones.cpp} (83%) rename ui/{widgets/vcard/phonesmodel.h => models/info/phones.h} (86%) create mode 100644 ui/widgets/info/CMakeLists.txt create mode 100644 ui/widgets/info/contactcontacts.cpp create mode 100644 ui/widgets/info/contactcontacts.h create mode 100644 ui/widgets/info/contactcontacts.ui create mode 100644 ui/widgets/info/contactgeneral.cpp create mode 100644 ui/widgets/info/contactgeneral.h create mode 100644 ui/widgets/info/contactgeneral.ui create mode 100644 ui/widgets/info/info.cpp create mode 100644 ui/widgets/info/info.h create mode 100644 ui/widgets/info/info.ui rename ui/widgets/{vcard => info}/omemo/CMakeLists.txt (76%) rename ui/widgets/{vcard => info}/omemo/keydelegate.cpp (92%) rename ui/widgets/{vcard => info}/omemo/keydelegate.h (100%) rename ui/widgets/{vcard => info}/omemo/omemo.cpp (90%) rename ui/widgets/{vcard => info}/omemo/omemo.h (92%) rename ui/widgets/{vcard => info}/omemo/omemo.ui (100%) diff --git a/shared/info.cpp b/shared/info.cpp index bda2b9f..a5cf046 100644 --- a/shared/info.cpp +++ b/shared/info.cpp @@ -16,13 +16,25 @@ #include "info.h" +Shared::Info::Info(const QString& p_jid, bool p_editable): + jid(p_jid), + editable(p_editable), + vcard(), + activeKeys(), + inactiveKeys() +{} + Shared::Info::Info(): + jid(), + editable(false), vcard(), activeKeys(), inactiveKeys() {} Shared::Info::Info(const Shared::Info& other): + jid(other.jid), + editable(other.editable), vcard(other.vcard), activeKeys(other.activeKeys), inactiveKeys(other.inactiveKeys) diff --git a/shared/info.h b/shared/info.h index 1692991..c3f16f8 100644 --- a/shared/info.h +++ b/shared/info.h @@ -33,9 +33,12 @@ namespace Shared { class Info { public: Info(); + Info(const QString& jid, bool editable = false); Info(const Info& other); ~Info(); + QString jid; + bool editable; VCard vcard; std::list activeKeys; std::list inactiveKeys; diff --git a/ui/models/CMakeLists.txt b/ui/models/CMakeLists.txt index 629db32..84aba66 100644 --- a/ui/models/CMakeLists.txt +++ b/ui/models/CMakeLists.txt @@ -24,3 +24,5 @@ target_sources(squawk PRIVATE roster.cpp roster.h ) + +add_subdirectory(info) diff --git a/ui/models/info/CMakeLists.txt b/ui/models/info/CMakeLists.txt new file mode 100644 index 0000000..57a078c --- /dev/null +++ b/ui/models/info/CMakeLists.txt @@ -0,0 +1,10 @@ +target_sources(squawk PRIVATE + emails.cpp + emails.h + phones.cpp + phones.h + ) + +if (WITH_OMEMO) + add_subdirectory(omemo) +endif() diff --git a/ui/widgets/vcard/emailsmodel.cpp b/ui/models/info/emails.cpp similarity index 80% rename from ui/widgets/vcard/emailsmodel.cpp rename to ui/models/info/emails.cpp index 994fcc3..6c902bf 100644 --- a/ui/widgets/vcard/emailsmodel.cpp +++ b/ui/models/info/emails.cpp @@ -16,30 +16,29 @@ * along with this program. If not, see . */ -#include "emailsmodel.h" +#include "emails.h" #include "shared/icons.h" #include -UI::VCard::EMailsModel::EMailsModel(bool p_edit, QObject* parent): +Models::EMails::EMails(bool p_edit, QObject* parent): QAbstractTableModel(parent), edit(p_edit), - deque() -{ -} + deque() {} -int UI::VCard::EMailsModel::columnCount(const QModelIndex& parent) const -{ - return 3; -} +int Models::EMails::columnCount(const QModelIndex& parent) const { + return 3;} -int UI::VCard::EMailsModel::rowCount(const QModelIndex& parent) const -{ - return deque.size(); -} +int Models::EMails::rowCount(const QModelIndex& parent) const { + return deque.size();} -QVariant UI::VCard::EMailsModel::data(const QModelIndex& index, int role) const -{ +void Models::EMails::revertPreferred(quint32 row) { + setData(createIndex(row, 2), !isPreferred(row));} + +QString Models::EMails::getEmail(quint32 row) const { + return deque[row].address;} + +QVariant Models::EMails::data(const QModelIndex& index, int role) const { if (index.isValid()) { int col = index.column(); switch (col) { @@ -82,8 +81,7 @@ QVariant UI::VCard::EMailsModel::data(const QModelIndex& index, int role) const return QVariant(); } -Qt::ItemFlags UI::VCard::EMailsModel::flags(const QModelIndex& index) const -{ +Qt::ItemFlags Models::EMails::flags(const QModelIndex& index) const { Qt::ItemFlags f = QAbstractTableModel::flags(index); if (edit && index.column() != 2) { f = Qt::ItemIsEditable | f; @@ -91,8 +89,7 @@ Qt::ItemFlags UI::VCard::EMailsModel::flags(const QModelIndex& index) const return f; } -bool UI::VCard::EMailsModel::setData(const QModelIndex& index, const QVariant& value, int role) -{ +bool Models::EMails::setData(const QModelIndex& index, const QVariant& value, int role) { if (role == Qt::EditRole && checkIndex(index)) { Shared::VCard::Email& item = deque[index.row()]; switch (index.column()) { @@ -124,8 +121,7 @@ bool UI::VCard::EMailsModel::setData(const QModelIndex& index, const QVariant& v } -bool UI::VCard::EMailsModel::dropPrefered() -{ +bool Models::EMails::dropPrefered() { bool dropped = false; int i = 0; for (Shared::VCard::Email& email : deque) { @@ -140,16 +136,14 @@ bool UI::VCard::EMailsModel::dropPrefered() return dropped; } -QModelIndex UI::VCard::EMailsModel::addNewEmptyLine() -{ +QModelIndex Models::EMails::addNewEmptyLine() { beginInsertRows(QModelIndex(), deque.size(), deque.size()); deque.emplace_back(""); endInsertRows(); return createIndex(deque.size() - 1, 0, &(deque.back())); } -bool UI::VCard::EMailsModel::isPreferred(quint32 row) const -{ +bool Models::EMails::isPreferred(quint32 row) const { if (row < deque.size()) { return deque[row].prefered; } else { @@ -157,8 +151,7 @@ bool UI::VCard::EMailsModel::isPreferred(quint32 row) const } } -void UI::VCard::EMailsModel::removeLines(quint32 index, quint32 count) -{ +void Models::EMails::removeLines(quint32 index, quint32 count) { if (index < deque.size()) { quint32 maxCount = deque.size() - index; if (count > maxCount) { @@ -175,15 +168,13 @@ void UI::VCard::EMailsModel::removeLines(quint32 index, quint32 count) } } -void UI::VCard::EMailsModel::getEmails(std::deque& emails) const -{ +void Models::EMails::getEmails(std::deque& emails) const { for (const Shared::VCard::Email& my : deque) { emails.emplace_back(my); } } -void UI::VCard::EMailsModel::setEmails(const std::deque& emails) -{ +void Models::EMails::setEmails(const std::deque& emails) { if (deque.size() > 0) { removeLines(0, deque.size()); } @@ -196,13 +187,3 @@ void UI::VCard::EMailsModel::setEmails(const std::deque& e endInsertRows(); } } - -void UI::VCard::EMailsModel::revertPreferred(quint32 row) -{ - setData(createIndex(row, 2), !isPreferred(row)); -} - -QString UI::VCard::EMailsModel::getEmail(quint32 row) const -{ - return deque[row].address; -} diff --git a/ui/widgets/vcard/emailsmodel.h b/ui/models/info/emails.h similarity index 87% rename from ui/widgets/vcard/emailsmodel.h rename to ui/models/info/emails.h index bafbe9f..bb05a5c 100644 --- a/ui/widgets/vcard/emailsmodel.h +++ b/ui/models/info/emails.h @@ -16,8 +16,8 @@ * along with this program. If not, see . */ -#ifndef UI_VCARD_EMAILSMODEL_H -#define UI_VCARD_EMAILSMODEL_H +#ifndef MODELS_EMAILS_H +#define MODELS_EMAILS_H #include #include @@ -26,14 +26,12 @@ #include "shared/vcard.h" -namespace UI { -namespace VCard { +namespace Models { -class EMailsModel : public QAbstractTableModel -{ +class EMails : public QAbstractTableModel { Q_OBJECT public: - EMailsModel(bool edit = false, QObject *parent = nullptr); + EMails(bool edit = false, QObject *parent = nullptr); int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; @@ -59,6 +57,6 @@ private: bool dropPrefered(); }; -}} +} -#endif // UI_VCARD_EMAILSMODEL_H +#endif // MODELS_EMAILS_H diff --git a/ui/models/info/omemo/CMakeLists.txt b/ui/models/info/omemo/CMakeLists.txt new file mode 100644 index 0000000..166ca76 --- /dev/null +++ b/ui/models/info/omemo/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(squawk PRIVATE + keys.cpp + keys.h +) diff --git a/ui/widgets/vcard/omemo/keysmodel.cpp b/ui/models/info/omemo/keys.cpp similarity index 80% rename from ui/widgets/vcard/omemo/keysmodel.cpp rename to ui/models/info/omemo/keys.cpp index 1488370..fa753c0 100644 --- a/ui/widgets/vcard/omemo/keysmodel.cpp +++ b/ui/models/info/omemo/keys.cpp @@ -14,23 +14,20 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "keysmodel.h" +#include "keys.h" -const QHash UI::KeysModel::roles = { +const QHash Models::Keys::roles = { {Label, "label"}, {FingerPrint, "fingerPrint"}, {TrustLevel, "trustLevel"} }; -UI::KeysModel::KeysModel(QObject* parent): +Models::Keys::Keys(QObject* parent): QAbstractListModel(parent), keys(), - modified() -{ + modified() {} -} - -UI::KeysModel::~KeysModel() { +Models::Keys::~Keys() { for (Shared::KeyInfo* key : keys) { delete key; } @@ -40,7 +37,7 @@ UI::KeysModel::~KeysModel() { } } -std::deque UI::KeysModel::modifiedKeys() const { +std::deque Models::Keys::modifiedKeys() const { std::deque response(modified.size()); int i = 0; @@ -52,13 +49,13 @@ std::deque UI::KeysModel::modifiedKeys() const { return response; } -void UI::KeysModel::addKey(const Shared::KeyInfo& info) { +void Models::Keys::addKey(const Shared::KeyInfo& info) { beginInsertRows(QModelIndex(), keys.size(), keys.size()); keys.push_back(new Shared::KeyInfo(info)); endInsertRows(); } -QVariant UI::KeysModel::data(const QModelIndex& index, int role) const { +QVariant Models::Keys::data(const QModelIndex& index, int role) const { int i = index.row(); const Shared::KeyInfo* info; bool dirty; @@ -94,13 +91,13 @@ QVariant UI::KeysModel::data(const QModelIndex& index, int role) const { return answer; } -int UI::KeysModel::rowCount(const QModelIndex& parent) const { +int Models::Keys::rowCount(const QModelIndex& parent) const { return keys.size(); } -QHash UI::KeysModel::roleNames() const {return roles;} +QHash Models::Keys::roleNames() const {return roles;} -QModelIndex UI::KeysModel::index(int row, int column, const QModelIndex& parent) const { +QModelIndex Models::Keys::index(int row, int column, const QModelIndex& parent) const { if (!hasIndex(row, column, parent)) { return QModelIndex(); } @@ -108,7 +105,7 @@ QModelIndex UI::KeysModel::index(int row, int column, const QModelIndex& parent) return createIndex(row, column, keys[row]); } -void UI::KeysModel::revertKey(int row) { +void Models::Keys::revertKey(int row) { std::map::const_iterator itr = modified.find(row); if (itr != modified.end()) { modified.erase(itr); @@ -117,7 +114,7 @@ void UI::KeysModel::revertKey(int row) { } } -void UI::KeysModel::setTrustLevel(int row, Shared::TrustLevel level) { +void Models::Keys::setTrustLevel(int row, Shared::TrustLevel level) { std::map::const_iterator itr = modified.find(row); Shared::KeyInfo* info; if (itr == modified.end()) { @@ -134,7 +131,7 @@ void UI::KeysModel::setTrustLevel(int row, Shared::TrustLevel level) { info->trustLevel = level; QModelIndex index = createIndex(row, 0, info); - dataChanged(index, index, {KeysModel::Dirty}); + dataChanged(index, index, {Keys::Dirty}); } diff --git a/ui/widgets/vcard/omemo/keysmodel.h b/ui/models/info/omemo/keys.h similarity index 89% rename from ui/widgets/vcard/omemo/keysmodel.h rename to ui/models/info/omemo/keys.h index e1a7606..2710bab 100644 --- a/ui/widgets/vcard/omemo/keysmodel.h +++ b/ui/models/info/omemo/keys.h @@ -14,23 +14,23 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#ifndef UI_KEYSMODEL_H -#define UI_KEYSMODEL_H +#ifndef MODELS_KEYS_H +#define MODELS_KEYS_H #include #include -namespace UI { +namespace Models { /** * @todo write docs */ -class KeysModel : public QAbstractListModel +class Keys : public QAbstractListModel { public: - KeysModel(QObject *parent = nullptr); - ~KeysModel(); + Keys(QObject *parent = nullptr); + ~Keys(); void addKey(const Shared::KeyInfo& info); @@ -64,4 +64,4 @@ private: } -#endif // UI_KEYSMODEL_H +#endif // MODELS_KEYS_H diff --git a/ui/widgets/vcard/phonesmodel.cpp b/ui/models/info/phones.cpp similarity index 83% rename from ui/widgets/vcard/phonesmodel.cpp rename to ui/models/info/phones.cpp index d3cace8..7c3b04f 100644 --- a/ui/widgets/vcard/phonesmodel.cpp +++ b/ui/models/info/phones.cpp @@ -16,30 +16,30 @@ * along with this program. If not, see . */ -#include "phonesmodel.h" +#include "phones.h" #include "shared/icons.h" #include -UI::VCard::PhonesModel::PhonesModel(bool p_edit, QObject* parent): +Models::Phones::Phones(bool p_edit, QObject* parent): QAbstractTableModel(parent), edit(p_edit), - deque() -{ + deque() {} + +int Models::Phones::columnCount(const QModelIndex& parent) const { + return 4;} + +int Models::Phones::rowCount(const QModelIndex& parent) const { + return deque.size();} + +void Models::Phones::revertPreferred(quint32 row) { + setData(createIndex(row, 3), !isPreferred(row)); } -int UI::VCard::PhonesModel::columnCount(const QModelIndex& parent) const -{ - return 4; -} +QString Models::Phones::getPhone(quint32 row) const { + return deque[row].number;} -int UI::VCard::PhonesModel::rowCount(const QModelIndex& parent) const -{ - return deque.size(); -} - -QVariant UI::VCard::PhonesModel::data(const QModelIndex& index, int role) const -{ +QVariant Models::Phones::data(const QModelIndex& index, int role) const { if (index.isValid()) { int col = index.column(); switch (col) { @@ -92,16 +92,14 @@ QVariant UI::VCard::PhonesModel::data(const QModelIndex& index, int role) const return QVariant(); } -QModelIndex UI::VCard::PhonesModel::addNewEmptyLine() -{ +QModelIndex Models::Phones::addNewEmptyLine() { beginInsertRows(QModelIndex(), deque.size(), deque.size()); deque.emplace_back("", Shared::VCard::Phone::other); endInsertRows(); return createIndex(deque.size() - 1, 0, &(deque.back())); } -Qt::ItemFlags UI::VCard::PhonesModel::flags(const QModelIndex& index) const -{ +Qt::ItemFlags Models::Phones::flags(const QModelIndex& index) const { Qt::ItemFlags f = QAbstractTableModel::flags(index); if (edit && index.column() != 3) { f = Qt::ItemIsEditable | f; @@ -109,8 +107,7 @@ Qt::ItemFlags UI::VCard::PhonesModel::flags(const QModelIndex& index) const return f; } -bool UI::VCard::PhonesModel::dropPrefered() -{ +bool Models::Phones::dropPrefered() { bool dropped = false; int i = 0; for (Shared::VCard::Phone& phone : deque) { @@ -125,15 +122,13 @@ bool UI::VCard::PhonesModel::dropPrefered() return dropped; } -void UI::VCard::PhonesModel::getPhones(std::deque& phones) const -{ +void Models::Phones::getPhones(std::deque& phones) const { for (const Shared::VCard::Phone& my : deque) { phones.emplace_back(my); } } -bool UI::VCard::PhonesModel::isPreferred(quint32 row) const -{ +bool Models::Phones::isPreferred(quint32 row) const { if (row < deque.size()) { return deque[row].prefered; } else { @@ -141,8 +136,7 @@ bool UI::VCard::PhonesModel::isPreferred(quint32 row) const } } -void UI::VCard::PhonesModel::removeLines(quint32 index, quint32 count) -{ +void Models::Phones::removeLines(quint32 index, quint32 count) { if (index < deque.size()) { quint32 maxCount = deque.size() - index; if (count > maxCount) { @@ -159,13 +153,7 @@ void UI::VCard::PhonesModel::removeLines(quint32 index, quint32 count) } } -void UI::VCard::PhonesModel::revertPreferred(quint32 row) -{ - setData(createIndex(row, 3), !isPreferred(row)); -} - -bool UI::VCard::PhonesModel::setData(const QModelIndex& index, const QVariant& value, int role) -{ +bool Models::Phones::setData(const QModelIndex& index, const QVariant& value, int role) { if (role == Qt::EditRole && checkIndex(index)) { Shared::VCard::Phone& item = deque[index.row()]; switch (index.column()) { @@ -204,8 +192,7 @@ bool UI::VCard::PhonesModel::setData(const QModelIndex& index, const QVariant& v return false; } -void UI::VCard::PhonesModel::setPhones(const std::deque& phones) -{ +void Models::Phones::setPhones(const std::deque& phones) { if (deque.size() > 0) { removeLines(0, deque.size()); } @@ -218,8 +205,3 @@ void UI::VCard::PhonesModel::setPhones(const std::deque& p endInsertRows(); } } - -QString UI::VCard::PhonesModel::getPhone(quint32 row) const -{ - return deque[row].number; -} diff --git a/ui/widgets/vcard/phonesmodel.h b/ui/models/info/phones.h similarity index 86% rename from ui/widgets/vcard/phonesmodel.h rename to ui/models/info/phones.h index 32d08b6..dec27d9 100644 --- a/ui/widgets/vcard/phonesmodel.h +++ b/ui/models/info/phones.h @@ -16,25 +16,19 @@ * along with this program. If not, see . */ -#ifndef UI_VCARD_PHONESMODEL_H -#define UI_VCARD_PHONESMODEL_H +#ifndef MODELS_PHONES_H +#define MODELS_PHONES_H #include #include #include "shared/vcard.h" -namespace UI { -namespace VCard { - -/** - * @todo write docs - */ -class PhonesModel : public QAbstractTableModel -{ +namespace Models { +class Phones : public QAbstractTableModel { Q_OBJECT public: - PhonesModel(bool edit = false, QObject *parent = nullptr); + Phones(bool edit = false, QObject *parent = nullptr); QVariant data(const QModelIndex& index, int role) const override; int columnCount(const QModelIndex& parent) const override; @@ -60,6 +54,6 @@ private: bool dropPrefered(); }; -}} +} -#endif // UI_VCARD_PHONESMODEL_H +#endif // MODELS_PHONES_H diff --git a/ui/widgets/CMakeLists.txt b/ui/widgets/CMakeLists.txt index 21d9504..be77db2 100644 --- a/ui/widgets/CMakeLists.txt +++ b/ui/widgets/CMakeLists.txt @@ -18,6 +18,7 @@ target_sources(squawk PRIVATE ) add_subdirectory(vcard) +add_subdirectory(info) add_subdirectory(messageline) add_subdirectory(settings) add_subdirectory(accounts) diff --git a/ui/widgets/info/CMakeLists.txt b/ui/widgets/info/CMakeLists.txt new file mode 100644 index 0000000..0029e84 --- /dev/null +++ b/ui/widgets/info/CMakeLists.txt @@ -0,0 +1,27 @@ +set(SOURCE_FILES + info.cpp + contactgeneral.cpp + contactcontacts.cpp +) + +set(UI_FILES + info.ui + contactgeneral.ui + contactcontacts.ui +) + +set(HEADER_FILES + info.h + contactgeneral.h + contactcontacts.h +) + +target_sources(squawk PRIVATE + ${SOURCE_FILES} + ${UI_FILES} + ${HEADER_FILES} +) + +if (WITH_OMEMO) + add_subdirectory(omemo) +endif() diff --git a/ui/widgets/info/contactcontacts.cpp b/ui/widgets/info/contactcontacts.cpp new file mode 100644 index 0000000..620d235 --- /dev/null +++ b/ui/widgets/info/contactcontacts.cpp @@ -0,0 +1,31 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "contactcontacts.h" +#include "ui_contactcontacts.h" + +UI::ContactContacts::ContactContacts(const QString& jid, QWidget* parent): + QWidget(parent), + m_ui(new Ui::ContactContacts) +{ + m_ui->setupUi(this); + + m_ui->jabberID->setText(jid); + m_ui->jabberID->setReadOnly(true); +} + +UI::ContactContacts::~ContactContacts() { +} diff --git a/ui/widgets/info/contactcontacts.h b/ui/widgets/info/contactcontacts.h new file mode 100644 index 0000000..7b26eed --- /dev/null +++ b/ui/widgets/info/contactcontacts.h @@ -0,0 +1,41 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 UI_WIDGETS_CONTACTCONTACTS_H +#define UI_WIDGETS_CONTACTCONTACTS_H + +#include +#include + +namespace UI { +namespace Ui +{ +class ContactContacts; +} + +class ContactContacts : public QWidget { + Q_OBJECT +public: + ContactContacts(const QString& jid, QWidget* parent = nullptr); + ~ContactContacts(); + +private: + QScopedPointer m_ui; +}; + +} + +#endif // UI_WIDGETS_CONTACTCONTACTS_H diff --git a/ui/widgets/info/contactcontacts.ui b/ui/widgets/info/contactcontacts.ui new file mode 100644 index 0000000..8104d50 --- /dev/null +++ b/ui/widgets/info/contactcontacts.ui @@ -0,0 +1,282 @@ + + + UI::ContactContacts + + + Contacts + + + + 0 + + + 0 + + + 6 + + + 0 + + + 0 + + + + + font: 600 24pt ; + + + Contact + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + true + + + + + 0 + 0 + 545 + 544 + + + + + + + Qt::Horizontal + + + + + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + false + + + false + + + false + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + font: 600 16pt; + + + Addresses + + + Qt::AlignCenter + + + + + + + QAbstractItemView::SelectRows + + + false + + + false + + + false + + + + + + + font: 600 16pt; + + + E-Mail addresses + + + Qt::AlignCenter + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 150 + 0 + + + + + 300 + 16777215 + + + + + + + + Jabber ID + + + jabberID + + + + + + + + 150 + 0 + + + + + 300 + 16777215 + + + + + + + + Web site + + + url + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + font: 600 16pt; + + + Phone numbers + + + Qt::AlignCenter + + + + + + + QAbstractItemView::SelectRows + + + false + + + false + + + false + + + + + + + + + + + + diff --git a/ui/widgets/info/contactgeneral.cpp b/ui/widgets/info/contactgeneral.cpp new file mode 100644 index 0000000..62efe81 --- /dev/null +++ b/ui/widgets/info/contactgeneral.cpp @@ -0,0 +1,28 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "contactgeneral.h" +#include "ui_contactgeneral.h" + +UI::ContactGeneral::ContactGeneral(QWidget* parent): + QWidget(parent), + m_ui(new Ui::ContactGeneral) +{ + m_ui->setupUi(this); +} + +UI::ContactGeneral::~ContactGeneral() +{} diff --git a/ui/widgets/info/contactgeneral.h b/ui/widgets/info/contactgeneral.h new file mode 100644 index 0000000..5e116c6 --- /dev/null +++ b/ui/widgets/info/contactgeneral.h @@ -0,0 +1,41 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 UI_WIDGETS_CONTACTGENERAL_H +#define UI_WIDGETS_CONTACTGENERAL_H + +#include +#include + +namespace UI { +namespace Ui +{ +class ContactGeneral; +} + +class ContactGeneral : public QWidget{ + Q_OBJECT +public: + ContactGeneral(QWidget* parent = nullptr); + ~ContactGeneral(); + +private: + QScopedPointer m_ui; +}; + +} + +#endif // UI_WIDGETS_CONTACTGENERAL_H diff --git a/ui/widgets/info/contactgeneral.ui b/ui/widgets/info/contactgeneral.ui new file mode 100644 index 0000000..bb2b9e7 --- /dev/null +++ b/ui/widgets/info/contactgeneral.ui @@ -0,0 +1,448 @@ + + + UI::ContactGeneral + + + + 0 + 0 + 340 + 625 + + + + + 0 + + + 6 + + + 0 + + + 0 + + + 6 + + + + + + 0 + 0 + + + + font: 600 16pt; + + + Organization + + + Qt::AlignCenter + + + + + + + QLayout::SetDefaultConstraint + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Middle name + + + middleName + + + + + + + First name + + + firstName + + + + + + + Last name + + + lastName + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Nick name + + + nickName + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Birthday + + + birthday + + + + + + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + Organization name + + + organizationName + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Unit / Department + + + organizationDepartment + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Role / Profession + + + organizationRole + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + Job title + + + organizationTitle + + + + + + + + 200 + 0 + + + + + 350 + 16777215 + + + + + + + + + + Qt::Horizontal + + + + + + + + + + + + Full name + + + fullName + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + + + + font: 600 24pt ; + + + General + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + font: 600 16pt; + + + QFrame::NoFrame + + + QFrame::Plain + + + Personal information + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + :/images/fallback/dark/big/user.svg:/images/fallback/dark/big/user.svg + + + + 0 + 0 + + + + QToolButton::InstantPopup + + + Qt::ToolButtonIconOnly + + + Qt::NoArrow + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + fullName + firstName + middleName + lastName + nickName + birthday + avatarButton + organizationName + organizationDepartment + organizationRole + organizationTitle + + + + + + diff --git a/ui/widgets/info/info.cpp b/ui/widgets/info/info.cpp new file mode 100644 index 0000000..eacab0f --- /dev/null +++ b/ui/widgets/info/info.cpp @@ -0,0 +1,30 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "info.h" +#include "ui_info.h" + +UI::Info::Info(const Shared::Info& info, QWidget* parent): + QWidget(parent), + m_ui(new Ui::Info()) +{ + m_ui->setupUi(this); + + +} + +UI::Info::~Info() +{} diff --git a/ui/widgets/info/info.h b/ui/widgets/info/info.h new file mode 100644 index 0000000..6858074 --- /dev/null +++ b/ui/widgets/info/info.h @@ -0,0 +1,45 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 UI_WIDGETS_INFO_H +#define UI_WIDGETS_INFO_H + +#include +#include + +#include + +#include "contactgeneral.h" + +namespace UI { +namespace Ui +{ +class Info; +} + +class Info : public QWidget { + Q_OBJECT +public: + Info(const Shared::Info& info, QWidget* parent = nullptr); + ~Info(); + +private: + QScopedPointer m_ui; +}; + +} + +#endif // UI_WIDGETS_INFO_H diff --git a/ui/widgets/info/info.ui b/ui/widgets/info/info.ui new file mode 100644 index 0000000..f600d8b --- /dev/null +++ b/ui/widgets/info/info.ui @@ -0,0 +1,137 @@ + + + UI::Info + + + + 0 + 0 + 400 + 300 + + + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + font: 16pt + + + Contact john@dow.org card + + + Qt::AlignCenter + + + + + + + font: italic 8pt; + + + Received 12.07.2007 at 17.35 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + Qt::TabFocus + + + QTabWidget::North + + + QTabWidget::Rounded + + + 1 + + + Qt::ElideNone + + + true + + + false + + + + Description + + + + 0 + + + 6 + + + 0 + + + 0 + + + 6 + + + + + font: 600 24pt ; + + + Description + + + + + + + QFrame::StyledPanel + + + Qt::LinksAccessibleByMouse|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + + QDialogButtonBox::Close|QDialogButtonBox::Save + + + false + + + + + + + + diff --git a/ui/widgets/vcard/omemo/CMakeLists.txt b/ui/widgets/info/omemo/CMakeLists.txt similarity index 76% rename from ui/widgets/vcard/omemo/CMakeLists.txt rename to ui/widgets/info/omemo/CMakeLists.txt index e2ade51..f1dc4ed 100644 --- a/ui/widgets/vcard/omemo/CMakeLists.txt +++ b/ui/widgets/info/omemo/CMakeLists.txt @@ -2,8 +2,6 @@ target_sources(squawk PRIVATE omemo.cpp omemo.h omemo.ui - keysmodel.cpp - keysmodel.h keydelegate.cpp keydelegate.h ) diff --git a/ui/widgets/vcard/omemo/keydelegate.cpp b/ui/widgets/info/omemo/keydelegate.cpp similarity index 92% rename from ui/widgets/vcard/omemo/keydelegate.cpp rename to ui/widgets/info/omemo/keydelegate.cpp index d0023e5..fd687ad 100644 --- a/ui/widgets/vcard/omemo/keydelegate.cpp +++ b/ui/widgets/info/omemo/keydelegate.cpp @@ -18,7 +18,7 @@ #include #include -#include "keysmodel.h" +#include "ui/models/info/omemo/keys.h" #include constexpr uint8_t margin = 10; @@ -56,7 +56,7 @@ void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio painter->restore(); } - QVariant dirtyV = index.data(UI::KeysModel::Dirty); + QVariant dirtyV = index.data(Models::Keys::Dirty); if (dirtyV.isValid() && dirtyV.toBool()) { painter->save(); rect.setWidth(margin / 2); @@ -66,7 +66,7 @@ void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio } rect.adjust(margin, margin, -margin, -margin); - QVariant labelV = index.data(UI::KeysModel::Label); + QVariant labelV = index.data(Models::Keys::Label); if (labelV.isValid()) { QString label = labelV.toString(); if (label.size() > 0) { @@ -80,7 +80,7 @@ void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio } } - QVariant fingerPrintV = index.data(UI::KeysModel::FingerPrint); + QVariant fingerPrintV = index.data(Models::Keys::FingerPrint); if (fingerPrintV.isValid()) { painter->save(); painter->setFont(fingerPrintFont); @@ -111,7 +111,7 @@ void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio } - QVariant lastV = index.data(UI::KeysModel::LastInteraction); + QVariant lastV = index.data(Models::Keys::LastInteraction); if (lastV.isValid()) { QDateTime last = lastV.toDateTime(); if (last.isValid()) { @@ -125,7 +125,7 @@ void UI::KeyDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio } } - QVariant levelV = index.data(UI::KeysModel::TrustLevel); + QVariant levelV = index.data(Models::Keys::TrustLevel); if (levelV.isValid()) { Shared::TrustLevel level = static_cast(levelV.toUInt()); QString levelName = Shared::Global::getName(level); @@ -150,7 +150,7 @@ QSize UI::KeyDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel int mw = margin * 2; int width = 0; - QVariant labelV = index.data(UI::KeysModel::Label); + QVariant labelV = index.data(Models::Keys::Label); if (labelV.isValid()) { QString label = labelV.toString(); if (label.size() > 0) { @@ -159,7 +159,7 @@ QSize UI::KeyDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel } } - QVariant fingerPrintV = index.data(UI::KeysModel::FingerPrint); + QVariant fingerPrintV = index.data(Models::Keys::FingerPrint); if (fingerPrintV.isValid()) { QString hex = fingerPrintV.toByteArray().toHex(); uint8_t parts = hex.size() / partSize; @@ -177,7 +177,7 @@ QSize UI::KeyDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel width = std::max(width, firstLine * fingerPrintMetrics.horizontalAdvance(hex, partSize) + (firstLine - 1) * spaceWidth); width += 1; //there is a mistake somewhere, this the cheapest way to compensate it } - QVariant lastV = index.data(UI::KeysModel::LastInteraction); + QVariant lastV = index.data(Models::Keys::LastInteraction); if (lastV.isValid()) { QDateTime last = lastV.toDateTime(); if (last.isValid()) { @@ -187,7 +187,7 @@ QSize UI::KeyDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel } } - QVariant levelV = index.data(UI::KeysModel::TrustLevel); + QVariant levelV = index.data(Models::Keys::TrustLevel); if (levelV.isValid()) { Shared::TrustLevel level = static_cast(levelV.toUInt()); QString levelName = Shared::Global::getName(level); diff --git a/ui/widgets/vcard/omemo/keydelegate.h b/ui/widgets/info/omemo/keydelegate.h similarity index 100% rename from ui/widgets/vcard/omemo/keydelegate.h rename to ui/widgets/info/omemo/keydelegate.h diff --git a/ui/widgets/vcard/omemo/omemo.cpp b/ui/widgets/info/omemo/omemo.cpp similarity index 90% rename from ui/widgets/vcard/omemo/omemo.cpp rename to ui/widgets/info/omemo/omemo.cpp index 3a5ab73..16414ee 100644 --- a/ui/widgets/vcard/omemo/omemo.cpp +++ b/ui/widgets/info/omemo/omemo.cpp @@ -71,13 +71,13 @@ void Omemo::onActiveKeysContextMenu(const QPoint& pos) { contextMenu->clear(); QModelIndex index = m_ui->keysView->indexAt(pos); if (index.isValid()) { - QVariant dirtyV = index.data(UI::KeysModel::Dirty); + QVariant dirtyV = index.data(Models::Keys::Dirty); if (dirtyV.isValid() && dirtyV.toBool()) { QAction* rev = contextMenu->addAction(Shared::icon("clean"), tr("Revert changes")); - connect(rev, &QAction::triggered, std::bind(&UI::KeysModel::revertKey, &keysModel, index.row())); + connect(rev, &QAction::triggered, std::bind(&Models::Keys::revertKey, &keysModel, index.row())); } - QVariant levelV = index.data(UI::KeysModel::TrustLevel); + QVariant levelV = index.data(Models::Keys::TrustLevel); if (levelV.isValid()) { Shared::TrustLevel level = static_cast(levelV.toUInt()); if (level == Shared::TrustLevel::undecided || @@ -86,7 +86,7 @@ void Omemo::onActiveKeysContextMenu(const QPoint& pos) { ) { QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Trust")); connect(rev, &QAction::triggered, - std::bind(&UI::KeysModel::setTrustLevel, &keysModel, + std::bind(&Models::Keys::setTrustLevel, &keysModel, index.row(), Shared::TrustLevel::manuallyTrusted ) ); @@ -99,7 +99,7 @@ void Omemo::onActiveKeysContextMenu(const QPoint& pos) { ) { QAction* rev = contextMenu->addAction(Shared::icon("unfavorite"), tr("Distrust")); connect(rev, &QAction::triggered, - std::bind(&UI::KeysModel::setTrustLevel, &keysModel, + std::bind(&Models::Keys::setTrustLevel, &keysModel, index.row(), Shared::TrustLevel::manuallyDistrusted ) ); diff --git a/ui/widgets/vcard/omemo/omemo.h b/ui/widgets/info/omemo/omemo.h similarity index 92% rename from ui/widgets/vcard/omemo/omemo.h rename to ui/widgets/info/omemo/omemo.h index 59e3dac..7c6b5a1 100644 --- a/ui/widgets/vcard/omemo/omemo.h +++ b/ui/widgets/info/omemo/omemo.h @@ -21,7 +21,7 @@ #include #include -#include "keysmodel.h" +#include "ui/models/info/omemo/keys.h" #include "keydelegate.h" #include "shared/icons.h" @@ -46,8 +46,8 @@ private: QScopedPointer m_ui; UI::KeyDelegate keysDelegate; UI::KeyDelegate unusedKeysDelegate; - UI::KeysModel keysModel; - UI::KeysModel unusedKeysModel; + Models::Keys keysModel; + Models::Keys unusedKeysModel; QMenu* contextMenu; }; diff --git a/ui/widgets/vcard/omemo/omemo.ui b/ui/widgets/info/omemo/omemo.ui similarity index 100% rename from ui/widgets/vcard/omemo/omemo.ui rename to ui/widgets/info/omemo/omemo.ui diff --git a/ui/widgets/room.h b/ui/widgets/room.h index 3f74e4e..3d231b8 100644 --- a/ui/widgets/room.h +++ b/ui/widgets/room.h @@ -20,7 +20,7 @@ #define ROOM_H #include "conversation.h" -#include "../models/room.h" +#include "ui/models/room.h" /** * @todo write docs diff --git a/ui/widgets/vcard/CMakeLists.txt b/ui/widgets/vcard/CMakeLists.txt index c37f4c6..5dca28d 100644 --- a/ui/widgets/vcard/CMakeLists.txt +++ b/ui/widgets/vcard/CMakeLists.txt @@ -1,13 +1,5 @@ target_sources(squawk PRIVATE - emailsmodel.cpp - emailsmodel.h - phonesmodel.cpp - phonesmodel.h vcard.cpp vcard.h vcard.ui ) - -if (WITH_OMEMO) - add_subdirectory(omemo) -endif() diff --git a/ui/widgets/vcard/vcard.cpp b/ui/widgets/vcard/vcard.cpp index a3e877b..d16aa83 100644 --- a/ui/widgets/vcard/vcard.cpp +++ b/ui/widgets/vcard/vcard.cpp @@ -235,10 +235,10 @@ void VCard::onContextMenu(const QPoint& point) { int row = sm->selectedRows().at(0).row(); if (emails.isPreferred(row)) { QAction* rev = contextMenu->addAction(Shared::icon("unfavorite"), tr("Unset this email as preferred")); - connect(rev, &QAction::triggered, std::bind(&UI::VCard::EMailsModel::revertPreferred, &emails, row)); + connect(rev, &QAction::triggered, std::bind(&Models::EMails::revertPreferred, &emails, row)); } else { QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Set this email as preferred")); - connect(rev, &QAction::triggered, std::bind(&UI::VCard::EMailsModel::revertPreferred, &emails, row)); + connect(rev, &QAction::triggered, std::bind(&Models::EMails::revertPreferred, &emails, row)); } } @@ -263,10 +263,10 @@ void VCard::onContextMenu(const QPoint& point) { int row = sm->selectedRows().at(0).row(); if (phones.isPreferred(row)) { QAction* rev = contextMenu->addAction(Shared::icon("view-media-favorite"), tr("Unset this phone as preferred")); - connect(rev, &QAction::triggered, std::bind(&UI::VCard::PhonesModel::revertPreferred, &phones, row)); + connect(rev, &QAction::triggered, std::bind(&Models::Phones::revertPreferred, &phones, row)); } else { QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Set this phone as preferred")); - connect(rev, &QAction::triggered, std::bind(&UI::VCard::PhonesModel::revertPreferred, &phones, row)); + connect(rev, &QAction::triggered, std::bind(&Models::Phones::revertPreferred, &phones, row)); } } diff --git a/ui/widgets/vcard/vcard.h b/ui/widgets/vcard/vcard.h index 82b7d53..7019797 100644 --- a/ui/widgets/vcard/vcard.h +++ b/ui/widgets/vcard/vcard.h @@ -37,13 +37,13 @@ #include #include "shared/vcard.h" -#include "emailsmodel.h" -#include "phonesmodel.h" +#include "ui/models/info/emails.h" +#include "ui/models/info/phones.h" #include "ui/utils/progress.h" #include "ui/utils/comboboxdelegate.h" #ifdef WITH_OMEMO -#include "omemo/omemo.h" +#include "ui/widgets/info/omemo/omemo.h" #endif namespace Ui @@ -92,8 +92,8 @@ private: QLabel* progressLabel; QWidget* overlay; QMenu* contextMenu; - UI::VCard::EMailsModel emails; - UI::VCard::PhonesModel phones; + Models::EMails emails; + Models::Phones phones; ComboboxDelegate* roleDelegate; ComboboxDelegate* phoneTypeDelegate; #ifdef WITH_OMEMO From edf1ee60cdb1ee7fcd3772118852b806106371b3 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 2 Feb 2023 21:39:38 +0300 Subject: [PATCH 086/137] keep going on refactoring vcard --- shared/enums.h | 10 ++ shared/info.cpp | 5 +- shared/info.h | 4 +- ui/widgets/info/CMakeLists.txt | 3 + ui/widgets/info/contactgeneral.cpp | 176 ++++++++++++++++++++++++++++- ui/widgets/info/contactgeneral.h | 36 ++++++ ui/widgets/info/contactgeneral.ui | 18 +++ ui/widgets/info/description.cpp | 27 +++++ ui/widgets/info/description.h | 41 +++++++ ui/widgets/info/description.ui | 48 ++++++++ ui/widgets/info/info.cpp | 81 ++++++++++++- ui/widgets/info/info.h | 25 +++- ui/widgets/info/info.ui | 64 ++++------- 13 files changed, 483 insertions(+), 55 deletions(-) create mode 100644 ui/widgets/info/description.cpp create mode 100644 ui/widgets/info/description.h create mode 100644 ui/widgets/info/description.ui diff --git a/shared/enums.h b/shared/enums.h index 9fb4aad..9eb9e5e 100644 --- a/shared/enums.h +++ b/shared/enums.h @@ -118,6 +118,16 @@ Q_ENUM_NS(AccountPassword) static const AccountPassword AccountPasswordHighest = AccountPassword::kwallet; static const AccountPassword AccountPasswordLowest = AccountPassword::plain; +enum class EntryType { + contact, + conference, + presence, + participant +}; +Q_ENUM_NS(EntryType) +static const EntryType EntryTypeHighest = EntryType::participant; +static const EntryType EntryTypeLowest = EntryType::contact; + enum class Support { unknown, supported, diff --git a/shared/info.cpp b/shared/info.cpp index a5cf046..a0ac299 100644 --- a/shared/info.cpp +++ b/shared/info.cpp @@ -16,7 +16,8 @@ #include "info.h" -Shared::Info::Info(const QString& p_jid, bool p_editable): +Shared::Info::Info(const QString& p_jid, EntryType p_type, bool p_editable): + type(p_type), jid(p_jid), editable(p_editable), vcard(), @@ -25,6 +26,7 @@ Shared::Info::Info(const QString& p_jid, bool p_editable): {} Shared::Info::Info(): + type(EntryType::contact), jid(), editable(false), vcard(), @@ -33,6 +35,7 @@ Shared::Info::Info(): {} Shared::Info::Info(const Shared::Info& other): + type(other.type), jid(other.jid), editable(other.editable), vcard(other.vcard), diff --git a/shared/info.h b/shared/info.h index c3f16f8..90248c3 100644 --- a/shared/info.h +++ b/shared/info.h @@ -19,6 +19,7 @@ #include "vcard.h" #include "keyinfo.h" +#include "enums.h" #include @@ -33,10 +34,11 @@ namespace Shared { class Info { public: Info(); - Info(const QString& jid, bool editable = false); + Info(const QString& jid, EntryType = EntryType::contact, bool editable = false); Info(const Info& other); ~Info(); + EntryType type; QString jid; bool editable; VCard vcard; diff --git a/ui/widgets/info/CMakeLists.txt b/ui/widgets/info/CMakeLists.txt index 0029e84..44edb66 100644 --- a/ui/widgets/info/CMakeLists.txt +++ b/ui/widgets/info/CMakeLists.txt @@ -2,18 +2,21 @@ set(SOURCE_FILES info.cpp contactgeneral.cpp contactcontacts.cpp + description.cpp ) set(UI_FILES info.ui contactgeneral.ui contactcontacts.ui + description.ui ) set(HEADER_FILES info.h contactgeneral.h contactcontacts.h + description.h ) target_sources(squawk PRIVATE diff --git a/ui/widgets/info/contactgeneral.cpp b/ui/widgets/info/contactgeneral.cpp index 62efe81..57ce700 100644 --- a/ui/widgets/info/contactgeneral.cpp +++ b/ui/widgets/info/contactgeneral.cpp @@ -17,12 +17,182 @@ #include "contactgeneral.h" #include "ui_contactgeneral.h" +#include + +const std::set UI::ContactGeneral::supportedTypes = {"image/jpeg", "image/png"}; +constexpr int maxAvatarSize = 160; + UI::ContactGeneral::ContactGeneral(QWidget* parent): QWidget(parent), - m_ui(new Ui::ContactGeneral) + m_ui(new Ui::ContactGeneral), + avatarMenu(nullptr), + avatarButtonMargins(), + currentAvatarType(Shared::Avatar::empty), + currentAvatarPath(""), + currentJid(""), + editable(false), + avatarDiablog(nullptr) { m_ui->setupUi(this); + + initializeActions(); + initializeAvatar(); } -UI::ContactGeneral::~ContactGeneral() -{} +UI::ContactGeneral::~ContactGeneral() { + if (avatarMenu != nullptr) + avatarMenu->deleteLater(); + + if (avatarDiablog != nullptr) + deleteAvatarDialog(); +} + +QString UI::ContactGeneral::title() const { + return m_ui->generalHeading->text();} + +void UI::ContactGeneral::setEditable(bool edit) { + m_ui->fullName->setReadOnly(!edit); + m_ui->firstName->setReadOnly(!edit); + m_ui->middleName->setReadOnly(!edit); + m_ui->lastName->setReadOnly(!edit); + m_ui->nickName->setReadOnly(!edit); + m_ui->birthday->setReadOnly(!edit); + m_ui->organizationName->setReadOnly(!edit); + m_ui->organizationDepartment->setReadOnly(!edit); + m_ui->organizationTitle->setReadOnly(!edit); + m_ui->organizationRole->setReadOnly(!edit); + + if (edit) { + avatarMenu = new QMenu(); + m_ui->avatarButton->setMenu(avatarMenu); + avatarMenu->addAction(m_ui->actionSetAvatar); + avatarMenu->addAction(m_ui->actionClearAvatar); + } else { + if (avatarMenu != nullptr) { + avatarMenu->deleteLater(); + avatarMenu = nullptr; + m_ui->avatarButton->setMenu(nullptr); + } + } +} + +void UI::ContactGeneral::deleteAvatarDialog() { + avatarDiablog->deleteLater(); + + disconnect(avatarDiablog, &QFileDialog::accepted, this, &UI::ContactGeneral::avatarSelected); + disconnect(avatarDiablog, &QFileDialog::rejected, this, &UI::ContactGeneral::deleteAvatarDialog); + + avatarDiablog = nullptr; +} + +void UI::ContactGeneral::initializeActions() { + QAction* setAvatar = m_ui->actionSetAvatar; + QAction* clearAvatar = m_ui->actionClearAvatar; + + connect(setAvatar, &QAction::triggered, this, &UI::ContactGeneral::onSetAvatar); + connect(clearAvatar, &QAction::triggered, this, &UI::ContactGeneral::onClearAvatar); + + setAvatar->setEnabled(editable); + clearAvatar->setEnabled(false); +} + +void UI::ContactGeneral::initializeAvatar() { + QToolButton* avatarButton = m_ui->avatarButton; + avatarButtonMargins = avatarButton->size(); + + int height = m_ui->personalForm->minimumSize().height() - avatarButtonMargins.height(); + avatarButton->setIconSize(QSize(height, height)); +} + +void UI::ContactGeneral::onSetAvatar() { + avatarDiablog = new QFileDialog(this, tr("Chose your new avatar")); + avatarDiablog->setFileMode(QFileDialog::ExistingFile); + avatarDiablog->setNameFilter(tr("Images (*.png *.jpg *.jpeg)")); + + connect(avatarDiablog, &QFileDialog::accepted, this, &UI::ContactGeneral::avatarSelected); + connect(avatarDiablog, &QFileDialog::rejected, this, &UI::ContactGeneral::deleteAvatarDialog); + + avatarDiablog->show(); +} + +void UI::ContactGeneral::onClearAvatar() { + currentAvatarType = Shared::Avatar::empty; + currentAvatarPath = ""; + + updateAvatar(); +} + +void UI::ContactGeneral::updateAvatar() { + int height = m_ui->personalForm->minimumSize().height() - avatarButtonMargins.height(); + switch (currentAvatarType) { + case Shared::Avatar::empty: + m_ui->avatarButton->setIcon(Shared::icon("user", true)); + m_ui->avatarButton->setIconSize(QSize(height, height)); + m_ui->actionClearAvatar->setEnabled(false); + break; + case Shared::Avatar::autocreated: + case Shared::Avatar::valid: + QPixmap pixmap(currentAvatarPath); + qreal h = pixmap.height(); + qreal w = pixmap.width(); + qreal aspectRatio = w / h; + m_ui->avatarButton->setIconSize(QSize(height * aspectRatio, height)); + m_ui->avatarButton->setIcon(QIcon(currentAvatarPath)); + m_ui->actionClearAvatar->setEnabled(editable); + break; + } +} + +void UI::ContactGeneral::avatarSelected() { + QMimeDatabase db; + QString path = avatarDiablog->selectedFiles().front(); + QMimeType type = db.mimeTypeForFile(path); + deleteAvatarDialog(); + + if (supportedTypes.find(type.name()) == supportedTypes.end()) { + qDebug() << "Selected for avatar file is not supported, skipping"; + } else { + QImage src(path); + QImage dst; + if (src.width() > maxAvatarSize || src.height() > maxAvatarSize) { + dst = src.scaled(maxAvatarSize, maxAvatarSize, Qt::KeepAspectRatio); + } + QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + currentJid + ".temp." + type.preferredSuffix(); + QFile oldTemp(path); + if (oldTemp.exists()) { + if (!oldTemp.remove()) { + qDebug() << "Error removing old temp avatar" << path; + return; + } + } + bool success = dst.save(path); + if (success) { + currentAvatarPath = path; + currentAvatarType = Shared::Avatar::valid; + + updateAvatar(); + } else { + qDebug() << "couldn't save temp avatar" << path << ", skipping"; + } + } +} + +void UI::ContactGeneral::setVCard(const QString& jid, const Shared::VCard& card, bool editable) { + currentJid = jid; + setEditable(editable); + m_ui->fullName->setText(card.getFullName()); + m_ui->firstName->setText(card.getFirstName()); + m_ui->middleName->setText(card.getMiddleName()); + m_ui->lastName->setText(card.getLastName()); + m_ui->nickName->setText(card.getNickName()); + m_ui->birthday->setDate(card.getBirthday()); + m_ui->organizationName->setText(card.getOrgName()); + m_ui->organizationDepartment->setText(card.getOrgUnit()); + m_ui->organizationTitle->setText(card.getOrgTitle()); + m_ui->organizationRole->setText(card.getOrgRole()); + + currentAvatarType = card.getAvatarType(); + currentAvatarPath = card.getAvatarPath(); + + updateAvatar(); +} diff --git a/ui/widgets/info/contactgeneral.h b/ui/widgets/info/contactgeneral.h index 5e116c6..6233c83 100644 --- a/ui/widgets/info/contactgeneral.h +++ b/ui/widgets/info/contactgeneral.h @@ -19,6 +19,18 @@ #include #include +#include +#include +#include +#include +#include +#include + +#include + +#include "shared/enums.h" +#include "shared/vcard.h" +#include "shared/icons.h" namespace UI { namespace Ui @@ -32,8 +44,32 @@ public: ContactGeneral(QWidget* parent = nullptr); ~ContactGeneral(); + void setVCard(const QString& jid, const Shared::VCard& card, bool editable = false); + QString title() const; + +private: + void setEditable(bool edit); + void initializeActions(); + void initializeAvatar(); + void updateAvatar(); + +private slots: + void deleteAvatarDialog(); + void avatarSelected(); + void onSetAvatar(); + void onClearAvatar(); + private: QScopedPointer m_ui; + QMenu* avatarMenu; + QSize avatarButtonMargins; + Shared::Avatar currentAvatarType; + QString currentAvatarPath; + QString currentJid; + bool editable; + QFileDialog* avatarDiablog; + + static const std::set supportedTypes; }; } diff --git a/ui/widgets/info/contactgeneral.ui b/ui/widgets/info/contactgeneral.ui index bb2b9e7..5ec123e 100644 --- a/ui/widgets/info/contactgeneral.ui +++ b/ui/widgets/info/contactgeneral.ui @@ -427,6 +427,24 @@ + + + + :/images/fallback/dark/big/edit-rename.svg:/images/fallback/dark/big/edit-rename.svg + + + Set avatar + + + + + + :/images/fallback/dark/big/clean.svg:/images/fallback/dark/big/clean.svg + + + Clear avatar + + fullName diff --git a/ui/widgets/info/description.cpp b/ui/widgets/info/description.cpp new file mode 100644 index 0000000..5132321 --- /dev/null +++ b/ui/widgets/info/description.cpp @@ -0,0 +1,27 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "description.h" +#include "ui_description.h" + +UI::Description::Description(QWidget* parent): + QWidget(parent), + m_ui(new Ui::Description()) +{ + m_ui->setupUi(this); +} + +UI::Description::~Description() {} diff --git a/ui/widgets/info/description.h b/ui/widgets/info/description.h new file mode 100644 index 0000000..42b0900 --- /dev/null +++ b/ui/widgets/info/description.h @@ -0,0 +1,41 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 UI_WIDGETS_DESCRIPTION_H +#define UI_WIDGETS_DESCRIPTION_H + +#include +#include + +namespace UI { +namespace Ui +{ +class Description; +} + +class Description : public QWidget { + Q_OBJECT +public: + Description(QWidget* parent = nullptr); + ~Description(); + +private: + QScopedPointer m_ui; +}; + +} + +#endif // UI_WIDGETS_DESCRIPTION_H diff --git a/ui/widgets/info/description.ui b/ui/widgets/info/description.ui new file mode 100644 index 0000000..e69a8f6 --- /dev/null +++ b/ui/widgets/info/description.ui @@ -0,0 +1,48 @@ + + + UI::Description + + + Description + + + + 0 + + + 6 + + + 0 + + + 0 + + + 6 + + + + + font: 600 24pt ; + + + Description + + + + + + + QFrame::StyledPanel + + + Qt::LinksAccessibleByMouse|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + diff --git a/ui/widgets/info/info.cpp b/ui/widgets/info/info.cpp index eacab0f..cbaffa8 100644 --- a/ui/widgets/info/info.cpp +++ b/ui/widgets/info/info.cpp @@ -17,14 +17,85 @@ #include "info.h" #include "ui_info.h" -UI::Info::Info(const Shared::Info& info, QWidget* parent): +UI::Info::Info(QWidget* parent): QWidget(parent), - m_ui(new Ui::Info()) + m_ui(new Ui::Info()), + contactGeneral(nullptr), + contactContacts(nullptr), + description(nullptr), + overlay(new QWidget()), + progress(new Progress(100)), + progressLabel(new QLabel()) { m_ui->setupUi(this); - + initializeOverlay(); } -UI::Info::~Info() -{} +UI::Info::~Info() { + if (contactGeneral != nullptr) + contactGeneral->deleteLater(); + + if (contactContacts != nullptr) + contactContacts->deleteLater(); + + if (description != nullptr) + description->deleteLater(); + + overlay->deleteLater(); +} + +void UI::Info::setData(const Shared::Info& info) { + switch (info.type) { + case Shared::EntryType::contact: + initializeContactGeneral(info); + initializeContactContacts(info); + initializeDescription(info.editable); + break; + default: + break; + } +} + +void UI::Info::initializeOverlay() { + QGridLayout* gr = static_cast(layout()); + gr->addWidget(overlay, 0, 0, 4, 1); + QVBoxLayout* nl = new QVBoxLayout(); + QGraphicsOpacityEffect* opacity = new QGraphicsOpacityEffect(); + opacity->setOpacity(0.8); + overlay->setLayout(nl); + overlay->setBackgroundRole(QPalette::Base); + overlay->setAutoFillBackground(true); + overlay->setGraphicsEffect(opacity); + progressLabel->setAlignment(Qt::AlignCenter); + QFont pf = progressLabel->font(); + pf.setBold(true); + pf.setPointSize(26); + progressLabel->setFont(pf); + progressLabel->setWordWrap(true); + nl->addStretch(); + nl->addWidget(progress); + nl->addWidget(progressLabel); + nl->addStretch(); + overlay->hide(); +} + + +void UI::Info::showProgress(const QString& line) { + progressLabel->setText(line); + overlay->show(); + progress->start(); +} + +void UI::Info::hideProgress() { + overlay->hide(); + progress->stop(); +} + +void UI::Info::initializeContactGeneral(const Shared::Info& info) { + if (contactGeneral == nullptr) { + contactGeneral = new ContactGeneral; + m_ui->tabWidget->addTab(contactGeneral, contactGeneral->title()); + } + contactGeneral->setVCard(info.jid, info.vcard, info.editable); +} diff --git a/ui/widgets/info/info.h b/ui/widgets/info/info.h index 6858074..126a092 100644 --- a/ui/widgets/info/info.h +++ b/ui/widgets/info/info.h @@ -19,10 +19,16 @@ #include #include +#include +#include #include +#include "ui/utils/progress.h" + #include "contactgeneral.h" +#include "contactcontacts.h" +#include "description.h" namespace UI { namespace Ui @@ -33,11 +39,28 @@ class Info; class Info : public QWidget { Q_OBJECT public: - Info(const Shared::Info& info, QWidget* parent = nullptr); + Info(QWidget* parent = nullptr); ~Info(); + void setData(const Shared::Info& info); + void showProgress(const QString& = ""); + void hideProgress(); + +private: + void initializeContactGeneral(const Shared::Info& info); + void initializeContactContacts(const Shared::Info& info); + void initializeDescription(bool editable); + void initializeOverlay(); + private: QScopedPointer m_ui; + ContactGeneral* contactGeneral; + ContactContacts* contactContacts; + Description* description; + QWidget* overlay; + Progress* progress; + QLabel* progressLabel; + }; } diff --git a/ui/widgets/info/info.ui b/ui/widgets/info/info.ui index f600d8b..ed2d42e 100644 --- a/ui/widgets/info/info.ui +++ b/ui/widgets/info/info.ui @@ -10,7 +10,23 @@ 300 - + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + 6 @@ -76,49 +92,7 @@ false - - - Description - - - - 0 - - - 6 - - - 0 - - - 0 - - - 6 - - - - - font: 600 24pt ; - - - Description - - - - - - - QFrame::StyledPanel - - - Qt::LinksAccessibleByMouse|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - + @@ -131,6 +105,8 @@ + + From bf11d8a74e1d746aa8b908fda7f2ffc4b61e3ef0 Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 3 Feb 2023 21:43:13 +0300 Subject: [PATCH 087/137] keeping with the refactoring --- ui/models/info/emails.cpp | 16 +++ ui/models/info/emails.h | 1 + ui/models/info/phones.cpp | 16 +++ ui/models/info/phones.h | 5 +- ui/widgets/info/contactcontacts.cpp | 213 +++++++++++++++++++++++++++- ui/widgets/info/contactcontacts.h | 37 ++++- ui/widgets/info/contactgeneral.cpp | 15 ++ ui/widgets/info/contactgeneral.h | 1 + 8 files changed, 298 insertions(+), 6 deletions(-) diff --git a/ui/models/info/emails.cpp b/ui/models/info/emails.cpp index 6c902bf..4bb8a17 100644 --- a/ui/models/info/emails.cpp +++ b/ui/models/info/emails.cpp @@ -89,6 +89,22 @@ Qt::ItemFlags Models::EMails::flags(const QModelIndex& index) const { return f; } +bool Models::EMails::setEditable(bool editable) { + if (edit != editable) { + edit = editable; + + if (deque.size() > 0) { + int lastRow = deque.size() - 1; + QModelIndex begin = createIndex(0, 0, &(deque[0])); + QModelIndex end = createIndex(lastRow, columnCount() - 1, &(deque[lastRow])); + emit dataChanged(begin, end); + } + return true; + } + return false; +} + + bool Models::EMails::setData(const QModelIndex& index, const QVariant& value, int role) { if (role == Qt::EditRole && checkIndex(index)) { Shared::VCard::Email& item = deque[index.row()]; diff --git a/ui/models/info/emails.h b/ui/models/info/emails.h index bb05a5c..f07d238 100644 --- a/ui/models/info/emails.h +++ b/ui/models/info/emails.h @@ -33,6 +33,7 @@ class EMails : public QAbstractTableModel { public: EMails(bool edit = false, QObject *parent = nullptr); + bool setEditable(bool editable); int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; diff --git a/ui/models/info/phones.cpp b/ui/models/info/phones.cpp index 7c3b04f..99c52c2 100644 --- a/ui/models/info/phones.cpp +++ b/ui/models/info/phones.cpp @@ -107,6 +107,22 @@ Qt::ItemFlags Models::Phones::flags(const QModelIndex& index) const { return f; } +bool Models::Phones::setEditable(bool editable) { + if (edit != editable) { + edit = editable; + + if (deque.size() > 0) { + int lastRow = deque.size() - 1; + QModelIndex begin = createIndex(0, 0, &(deque[0])); + QModelIndex end = createIndex(lastRow, columnCount() - 1, &(deque[lastRow])); + emit dataChanged(begin, end); + } + return true; + } + return false; +} + + bool Models::Phones::dropPrefered() { bool dropped = false; int i = 0; diff --git a/ui/models/info/phones.h b/ui/models/info/phones.h index dec27d9..aea03ed 100644 --- a/ui/models/info/phones.h +++ b/ui/models/info/phones.h @@ -30,9 +30,10 @@ class Phones : public QAbstractTableModel { public: Phones(bool edit = false, QObject *parent = nullptr); + bool setEditable(bool editable); QVariant data(const QModelIndex& index, int role) const override; - int columnCount(const QModelIndex& parent) const override; - int rowCount(const QModelIndex& parent) const override; + int columnCount(const QModelIndex& parent = QModelIndex()) const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex &index) const override; bool isPreferred(quint32 row) const; diff --git a/ui/widgets/info/contactcontacts.cpp b/ui/widgets/info/contactcontacts.cpp index 620d235..1196aa9 100644 --- a/ui/widgets/info/contactcontacts.cpp +++ b/ui/widgets/info/contactcontacts.cpp @@ -17,15 +17,222 @@ #include "contactcontacts.h" #include "ui_contactcontacts.h" -UI::ContactContacts::ContactContacts(const QString& jid, QWidget* parent): +UI::ContactContacts::ContactContacts(QWidget* parent): QWidget(parent), - m_ui(new Ui::ContactContacts) + m_ui(new Ui::ContactContacts), + contextMenu(new QMenu()), + emails(), + phones(), + roleDelegate(new ComboboxDelegate()), + phoneTypeDelegate(new ComboboxDelegate()), + editable(false) { m_ui->setupUi(this); - m_ui->jabberID->setText(jid); m_ui->jabberID->setReadOnly(true); + + initializeDelegates(); + initializeViews(); } UI::ContactContacts::~ContactContacts() { + contextMenu->deleteLater(); +} + +void UI::ContactContacts::setVCard(const QString& jid, const Shared::VCard& card, bool p_editable) { + editable = p_editable; + m_ui->jabberID->setText(jid); + m_ui->url->setText(card.getUrl()); + m_ui->url->setReadOnly(!p_editable); + + emails.setEditable(editable); + phones.setEditable(editable); + emails.setEmails(card.getEmails()); + phones.setPhones(card.getPhones()); +} + +void UI::ContactContacts::initializeDelegates() { + roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[0].toStdString().c_str())); + roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[1].toStdString().c_str())); + roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[2].toStdString().c_str())); + + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[0].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[1].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[2].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[3].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[4].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[5].toStdString().c_str())); + phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[6].toStdString().c_str())); +} + +void UI::ContactContacts::initializeViews() { + m_ui->emailsView->setContextMenuPolicy(Qt::CustomContextMenu); + m_ui->emailsView->setModel(&emails); + m_ui->emailsView->setItemDelegateForColumn(1, roleDelegate); + m_ui->emailsView->setColumnWidth(2, 25); + m_ui->emailsView->horizontalHeader()->setStretchLastSection(false); + m_ui->emailsView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + + m_ui->phonesView->setContextMenuPolicy(Qt::CustomContextMenu); + m_ui->phonesView->setModel(&phones); + m_ui->phonesView->setItemDelegateForColumn(1, roleDelegate); + m_ui->phonesView->setItemDelegateForColumn(2, phoneTypeDelegate); + m_ui->phonesView->setColumnWidth(3, 25); + m_ui->phonesView->horizontalHeader()->setStretchLastSection(false); + m_ui->phonesView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); + + connect(m_ui->phonesView, &QWidget::customContextMenuRequested, this, &UI::ContactContacts::onContextMenu); + connect(m_ui->emailsView, &QWidget::customContextMenuRequested, this, &UI::ContactContacts::onContextMenu); +} + +void UI::ContactContacts::onContextMenu(const QPoint& point) { + contextMenu->clear(); + bool hasMenu = false; + QAbstractItemView* snd = static_cast(sender()); + if (snd == m_ui->emailsView) { + hasMenu = true; + if (editable) { + QAction* add = contextMenu->addAction(Shared::icon("list-add"), tr("Add email address")); + connect(add, &QAction::triggered, this, &UI::ContactContacts::onAddEmail); + + QItemSelectionModel* sm = m_ui->emailsView->selectionModel(); + int selectionSize = sm->selectedRows().size(); + + if (selectionSize > 0) { + if (selectionSize == 1) { + int row = sm->selectedRows().at(0).row(); + if (emails.isPreferred(row)) { + QAction* rev = contextMenu->addAction(Shared::icon("unfavorite"), tr("Unset this email as preferred")); + connect(rev, &QAction::triggered, std::bind(&Models::EMails::revertPreferred, &emails, row)); + } else { + QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Set this email as preferred")); + connect(rev, &QAction::triggered, std::bind(&Models::EMails::revertPreferred, &emails, row)); + } + } + + QAction* del = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove selected email addresses")); + connect(del, &QAction::triggered, this, &UI::ContactContacts::onRemoveEmail); + } + } + + QAction* cp = contextMenu->addAction(Shared::icon("edit-copy"), tr("Copy selected emails to clipboard")); + connect(cp, &QAction::triggered, this, &UI::ContactContacts::onCopyEmail); + } else if (snd == m_ui->phonesView) { + hasMenu = true; + if (editable) { + QAction* add = contextMenu->addAction(Shared::icon("list-add"), tr("Add phone number")); + connect(add, &QAction::triggered, this, &UI::ContactContacts::onAddPhone); + + QItemSelectionModel* sm = m_ui->phonesView->selectionModel(); + int selectionSize = sm->selectedRows().size(); + + if (selectionSize > 0) { + if (selectionSize == 1) { + int row = sm->selectedRows().at(0).row(); + if (phones.isPreferred(row)) { + QAction* rev = contextMenu->addAction(Shared::icon("view-media-favorite"), tr("Unset this phone as preferred")); + connect(rev, &QAction::triggered, std::bind(&Models::Phones::revertPreferred, &phones, row)); + } else { + QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Set this phone as preferred")); + connect(rev, &QAction::triggered, std::bind(&Models::Phones::revertPreferred, &phones, row)); + } + } + + QAction* del = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove selected phone numbers")); + connect(del, &QAction::triggered, this, &UI::ContactContacts::onRemovePhone); + } + } + + QAction* cp = contextMenu->addAction(Shared::icon("edit-copy"), tr("Copy selected phones to clipboard")); + connect(cp, &QAction::triggered, this, &UI::ContactContacts::onCopyPhone); + } + + if (hasMenu) { + contextMenu->popup(snd->viewport()->mapToGlobal(point)); + } +} + +void UI::ContactContacts::onAddEmail() { + QModelIndex index = emails.addNewEmptyLine(); + m_ui->emailsView->setCurrentIndex(index); + m_ui->emailsView->edit(index); +} + +void UI::ContactContacts::onAddAddress() {} //TODO +void UI::ContactContacts::onAddPhone() { + QModelIndex index = phones.addNewEmptyLine(); + m_ui->phonesView->setCurrentIndex(index); + m_ui->phonesView->edit(index); +} +void UI::ContactContacts::onRemoveAddress() {} //TODO +void UI::ContactContacts::onRemoveEmail() { + QItemSelection selection(m_ui->emailsView->selectionModel()->selection()); + + QList rows; + for (const QModelIndex& index : selection.indexes()) { + rows.append(index.row()); + } + + std::sort(rows.begin(), rows.end()); + + int prev = -1; + for (int i = rows.count() - 1; i >= 0; i -= 1) { + int current = rows[i]; + if (current != prev) { + emails.removeLines(current, 1); + prev = current; + } + } +} + +void UI::ContactContacts::onRemovePhone() { + QItemSelection selection(m_ui->phonesView->selectionModel()->selection()); + + QList rows; + for (const QModelIndex& index : selection.indexes()) { + rows.append(index.row()); + } + + std::sort(rows.begin(), rows.end()); + + int prev = -1; + for (int i = rows.count() - 1; i >= 0; i -= 1) { + int current = rows[i]; + if (current != prev) { + phones.removeLines(current, 1); + prev = current; + } + } +} + +void UI::ContactContacts::onCopyEmail() { + QList selection(m_ui->emailsView->selectionModel()->selectedRows()); + + QList addrs; + for (const QModelIndex& index : selection) { + addrs.push_back(emails.getEmail(index.row())); + } + + QString list = addrs.join("\n"); + + QClipboard* cb = QApplication::clipboard(); + cb->setText(list); +} + +void UI::ContactContacts::onCopyPhone() { + QList selection(m_ui->phonesView->selectionModel()->selectedRows()); + + QList phs; + for (const QModelIndex& index : selection) { + phs.push_back(phones.getPhone(index.row())); + } + + QString list = phs.join("\n"); + + QClipboard* cb = QApplication::clipboard(); + cb->setText(list); +} + +QString UI::ContactContacts::title() const { + return m_ui->contactHeading->text(); } diff --git a/ui/widgets/info/contactcontacts.h b/ui/widgets/info/contactcontacts.h index 7b26eed..d2c7e9f 100644 --- a/ui/widgets/info/contactcontacts.h +++ b/ui/widgets/info/contactcontacts.h @@ -19,6 +19,16 @@ #include #include +#include +#include +#include +#include + +#include "shared/vcard.h" +#include "shared/icons.h" +#include "ui/models/info/emails.h" +#include "ui/models/info/phones.h" +#include "ui/utils/comboboxdelegate.h" namespace UI { namespace Ui @@ -29,11 +39,36 @@ class ContactContacts; class ContactContacts : public QWidget { Q_OBJECT public: - ContactContacts(const QString& jid, QWidget* parent = nullptr); + ContactContacts(QWidget* parent = nullptr); ~ContactContacts(); + void setVCard(const QString& jid, const Shared::VCard& card, bool editable = false); + void fillVCard(Shared::VCard& card) const; + QString title() const; + +private slots: + void onContextMenu(const QPoint& point); + void onAddAddress(); + void onRemoveAddress(); + void onAddEmail(); + void onCopyEmail(); + void onRemoveEmail(); + void onAddPhone(); + void onCopyPhone(); + void onRemovePhone(); + +private: + void initializeDelegates(); + void initializeViews(); + private: QScopedPointer m_ui; + QMenu* contextMenu; + Models::EMails emails; + Models::Phones phones; + ComboboxDelegate* roleDelegate; + ComboboxDelegate* phoneTypeDelegate; + bool editable; }; } diff --git a/ui/widgets/info/contactgeneral.cpp b/ui/widgets/info/contactgeneral.cpp index 57ce700..ccc6996 100644 --- a/ui/widgets/info/contactgeneral.cpp +++ b/ui/widgets/info/contactgeneral.cpp @@ -196,3 +196,18 @@ void UI::ContactGeneral::setVCard(const QString& jid, const Shared::VCard& card, updateAvatar(); } + +void UI::ContactGeneral::fillVCard(Shared::VCard& card) const { + card.setFullName(m_ui->fullName->text()); + card.setFirstName(m_ui->firstName->text()); + card.setMiddleName(m_ui->middleName->text()); + card.setLastName(m_ui->lastName->text()); + card.setNickName(m_ui->nickName->text()); + card.setBirthday(m_ui->birthday->date()); + card.setOrgName(m_ui->organizationName->text()); + card.setOrgUnit(m_ui->organizationDepartment->text()); + card.setOrgRole(m_ui->organizationRole->text()); + card.setOrgTitle(m_ui->organizationTitle->text()); + card.setAvatarPath(currentAvatarPath); + card.setAvatarType(currentAvatarType); +} diff --git a/ui/widgets/info/contactgeneral.h b/ui/widgets/info/contactgeneral.h index 6233c83..2817e91 100644 --- a/ui/widgets/info/contactgeneral.h +++ b/ui/widgets/info/contactgeneral.h @@ -45,6 +45,7 @@ public: ~ContactGeneral(); void setVCard(const QString& jid, const Shared::VCard& card, bool editable = false); + void fillVCard(Shared::VCard& card) const; QString title() const; private: From e4a2728ef8d5671bc2b7091010804e19e29ebbc6 Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 20 Feb 2023 21:12:32 +0300 Subject: [PATCH 088/137] hopefully end of refactoring of vcard to Info widget --- core/account.cpp | 112 ++-- core/account.h | 7 +- core/conference.cpp | 2 +- core/handlers/rosterhandler.cpp | 4 +- core/handlers/vcardhandler.cpp | 53 +- core/squawk.cpp | 14 +- core/squawk.h | 7 +- main/application.cpp | 8 +- ui/squawk.cpp | 170 +++--- ui/squawk.h | 17 +- ui/widgets/CMakeLists.txt | 1 - ui/widgets/info/contactcontacts.cpp | 8 + ui/widgets/info/description.cpp | 17 + ui/widgets/info/description.h | 5 + ui/widgets/info/info.cpp | 58 +- ui/widgets/info/info.h | 14 +- ui/widgets/vcard/CMakeLists.txt | 5 - ui/widgets/vcard/vcard.cpp | 474 --------------- ui/widgets/vcard/vcard.h | 117 ---- ui/widgets/vcard/vcard.ui | 892 ---------------------------- 20 files changed, 268 insertions(+), 1717 deletions(-) delete mode 100644 ui/widgets/vcard/CMakeLists.txt delete mode 100644 ui/widgets/vcard/vcard.cpp delete mode 100644 ui/widgets/vcard/vcard.h delete mode 100644 ui/widgets/vcard/vcard.ui diff --git a/core/account.cpp b/core/account.cpp index bb3ebea..98862a4 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -138,8 +138,7 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& } } -Account::~Account() -{ +Account::~Account() { if (reconnectScheduled) { reconnectScheduled = false; reconnectTimer->stop(); @@ -166,13 +165,10 @@ Account::~Account() delete cm; } -Shared::ConnectionState Core::Account::getState() const -{ - return state; -} +Shared::ConnectionState Core::Account::getState() const { + return state;} -void Core::Account::connect() -{ +void Core::Account::connect() { if (reconnectScheduled) { reconnectScheduled = false; reconnectTimer->stop(); @@ -194,16 +190,14 @@ void Core::Account::connect() } } -void Core::Account::onReconnectTimer() -{ +void Core::Account::onReconnectTimer() { if (reconnectScheduled) { reconnectScheduled = false; connect(); } } -void Core::Account::disconnect() -{ +void Core::Account::disconnect() { if (reconnectScheduled) { reconnectScheduled = false; reconnectTimer->stop(); @@ -219,8 +213,7 @@ void Core::Account::disconnect() } } -void Core::Account::onClientStateChange(QXmppClient::State st) -{ +void Core::Account::onClientStateChange(QXmppClient::State st) { switch (st) { case QXmppClient::ConnectedState: { if (state != Shared::ConnectionState::connected) { @@ -279,8 +272,7 @@ void Core::Account::onClientStateChange(QXmppClient::State st) } } -void Core::Account::reconnect() -{ +void Core::Account::reconnect() { if (!reconnectScheduled) { //TODO define behavior if It was connection or disconnecting if (state == Shared::ConnectionState::connected) { reconnectScheduled = true; @@ -292,8 +284,7 @@ void Core::Account::reconnect() } } -Shared::Availability Core::Account::getAvailability() const -{ +Shared::Availability Core::Account::getAvailability() const { if (state == Shared::ConnectionState::connected) { QXmppPresence::AvailableStatusType pres = presence.availableStatusType(); return static_cast(pres); //they are compatible; @@ -302,8 +293,7 @@ Shared::Availability Core::Account::getAvailability() const } } -void Core::Account::setAvailability(Shared::Availability avail) -{ +void Core::Account::setAvailability(Shared::Availability avail) { if (avail == Shared::Availability::offline) { disconnect(); //TODO not sure how to do here - changing state may cause connection or disconnection } else { @@ -323,23 +313,21 @@ void Core::Account::runDiscoveryService() { } -void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) -{ +void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) { QString id = p_presence.from(); QStringList comps = id.split("/"); QString jid = comps.front().toLower(); QString resource = comps.back(); if (jid == getBareJid()) { - if (resource == getResource()) { + if (resource == getResource()) emit availabilityChanged(static_cast(p_presence.availableStatusType())); - } + vh->handlePresenceOfMyAccountChange(p_presence); } else { RosterItem* item = rh->getRosterItem(jid); - if (item != 0) { + if (item != 0) item->handlePresence(p_presence); - } } switch (p_presence.type()) { @@ -382,8 +370,7 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) } } -void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMessage& msg) -{ +void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMessage& msg) { if (msg.id().size() > 0 && (msg.body().size() > 0 || msg.outOfBandUrl().size() > 0)) { std::map::const_iterator itr = archiveQueries.find(queryId); if (itr != archiveQueries.end()) { @@ -395,17 +382,15 @@ void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMess sMsg.setState(Shared::Message::State::sent); QString oId = msg.replaceId(); - if (oId.size() > 0) { + if (oId.size() > 0) item->correctMessageInArchive(oId, sMsg); - } else { + else item->addMessageToArchive(sMsg); - } } } } -void Core::Account::requestArchive(const QString& jid, int count, const QString& before) -{ +void Core::Account::requestArchive(const QString& jid, int count, const QString& before) { qDebug() << "An archive request for " << jid << ", before " << before; RosterItem* contact = rh->getRosterItem(jid); @@ -423,8 +408,7 @@ void Core::Account::requestArchive(const QString& jid, int count, const QString& contact->requestHistory(count, before); } -void Core::Account::onContactNeedHistory(const QString& before, const QString& after, const QDateTime& at) -{ +void Core::Account::onContactNeedHistory(const QString& before, const QString& after, const QDateTime& at) { RosterItem* contact = static_cast(sender()); QString to; @@ -468,8 +452,7 @@ void Core::Account::onContactNeedHistory(const QString& before, const QString& a archiveQueries.insert(std::make_pair(q, contact->jid)); } -void Core::Account::onMamResultsReceived(const QString& queryId, const QXmppResultSetReply& resultSetReply, bool complete) -{ +void Core::Account::onMamResultsReceived(const QString& queryId, const QXmppResultSetReply& resultSetReply, bool complete) { std::map::const_iterator itr = archiveQueries.find(queryId); if (itr != archiveQueries.end()) { QString jid = itr->second; @@ -484,14 +467,12 @@ void Core::Account::onMamResultsReceived(const QString& queryId, const QXmppResu } } -void Core::Account::onMamLog(QXmppLogger::MessageType type, const QString& msg) -{ +void Core::Account::onMamLog(QXmppLogger::MessageType type, const QString& msg) { qDebug() << "MAM MESSAGE LOG::"; qDebug() << msg; } -void Core::Account::onClientError(QXmppClient::Error err) -{ +void Core::Account::onClientError(QXmppClient::Error err) { qDebug() << "Error"; QString errorText; QString errorType; @@ -601,22 +582,18 @@ void Core::Account::onClientError(QXmppClient::Error err) emit error(errorText); } -void Core::Account::subscribeToContact(const QString& jid, const QString& reason) -{ - if (state == Shared::ConnectionState::connected) { +void Core::Account::subscribeToContact(const QString& jid, const QString& reason) { + if (state == Shared::ConnectionState::connected) rm->subscribe(jid, reason); - } else { + else qDebug() << "An attempt to subscribe account " << name << " to contact " << jid << " but the account is not in the connected state, skipping"; - } } -void Core::Account::unsubscribeFromContact(const QString& jid, const QString& reason) -{ - if (state == Shared::ConnectionState::connected) { +void Core::Account::unsubscribeFromContact(const QString& jid, const QString& reason) { + if (state == Shared::ConnectionState::connected) rm->unsubscribe(jid, reason); - } else { + else qDebug() << "An attempt to unsubscribe account " << name << " from contact " << jid << " but the account is not in the connected state, skipping"; - } } void Core::Account::removeContactRequest(const QString& jid) { @@ -625,8 +602,7 @@ void Core::Account::removeContactRequest(const QString& jid) { void Core::Account::addContactRequest(const QString& jid, const QString& name, const QSet& groups) { rh->addContactRequest(jid, name, groups);} -void Core::Account::setRoomAutoJoin(const QString& jid, bool joined) -{ +void Core::Account::setRoomAutoJoin(const QString& jid, bool joined) { Conference* conf = rh->getConference(jid); if (conf == 0) { qDebug() << "An attempt to set auto join to the non existing room" << jid << "of the account" << getName() << ", skipping"; @@ -636,8 +612,7 @@ void Core::Account::setRoomAutoJoin(const QString& jid, bool joined) conf->setAutoJoin(joined); } -void Core::Account::setRoomJoined(const QString& jid, bool joined) -{ +void Core::Account::setRoomJoined(const QString& jid, bool joined) { Conference* conf = rh->getConference(jid); if (conf == 0) { qDebug() << "An attempt to set joined to the non existing room" << jid << "of the account" << getName() << ", skipping"; @@ -657,16 +632,14 @@ void Core::Account::discoverInfo(const QString& address, const QString& node) { } } -void Core::Account::setPepSupport(Shared::Support support) -{ +void Core::Account::setPepSupport(Shared::Support support) { if (support != pepSupport) { pepSupport = support; emit pepSupportChanged(pepSupport); } } -void Core::Account::handleDisconnection() -{ +void Core::Account::handleDisconnection() { setPepSupport(Shared::Support::unknown); cm->setCarbonsEnabled(false); rh->handleOffline(); @@ -674,14 +647,13 @@ void Core::Account::handleDisconnection() archiveQueries.clear(); } -void Core::Account::onContactHistoryResponse(const std::list& list, bool last) -{ +void Core::Account::onContactHistoryResponse(const std::list& list, bool last) { RosterItem* contact = static_cast(sender()); qDebug() << "Collected history for contact " << contact->jid << list.size() << "elements"; - if (last) { + if (last) qDebug() << "The response contains the first accounted message"; - } + emit responseArchive(contact->jid, list, last); } @@ -754,11 +726,17 @@ void Core::Account::resendMessage(const QString& jid, const QString& id) { void Core::Account::replaceMessage(const QString& originalId, const Shared::Message& data) { mh->sendMessage(data, false, originalId);} -void Core::Account::requestVCard(const QString& jid) { - vh->requestVCard(jid);} +void Core::Account::requestInfo(const QString& jid) { + //TODO switch case of what kind of entity this info request is about + //right now it could be only about myself or some contact + vh->requestVCard(jid); +} -void Core::Account::uploadVCard(const Shared::VCard& card) { - vh->uploadVCard(card);} +void Core::Account::updateInfo(const Shared::Info& info) { + //TODO switch case of what kind of entity this info update is about + //right now it could be only about myself + vh->uploadVCard(info.vcard); +} QString Core::Account::getAvatarPath() const { return vh->getAvatarPath();} diff --git a/core/account.h b/core/account.h index 393b6e6..598a06e 100644 --- a/core/account.h +++ b/core/account.h @@ -46,6 +46,7 @@ #include #include +#include #include "contact.h" #include "conference.h" #include @@ -126,7 +127,7 @@ public: void setRoomAutoJoin(const QString& jid, bool joined); void removeRoomRequest(const QString& jid); void addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin); - void uploadVCard(const Shared::VCard& card); + void updateInfo(const Shared::Info& info); void resendMessage(const QString& jid, const QString& id); void replaceMessage(const QString& originalId, const Shared::Message& data); void invalidatePassword(); @@ -137,7 +138,7 @@ public slots: void connect(); void disconnect(); void reconnect(); - void requestVCard(const QString& jid); + void requestInfo(const QString& jid); signals: void changed(const QMap& data); @@ -161,7 +162,7 @@ signals: void addRoomParticipant(const QString& jid, const QString& nickName, const QMap& data); void changeRoomParticipant(const QString& jid, const QString& nickName, const QMap& data); void removeRoomParticipant(const QString& jid, const QString& nickName); - void receivedVCard(const QString& jid, const Shared::VCard& card); + void infoReady(const Shared::Info& info); void uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap headers); void uploadFileError(const QString& jid, const QString& messageId, const QString& error); void needPassword(); diff --git a/core/conference.cpp b/core/conference.cpp index 55280e2..a984f41 100644 --- a/core/conference.cpp +++ b/core/conference.cpp @@ -170,7 +170,7 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name) } else { cData.insert("avatarState", static_cast(Shared::Avatar::empty)); cData.insert("avatarPath", ""); - requestVCard(p_name); + emit requestVCard(p_name); } emit addParticipant(resource, cData); diff --git a/core/handlers/rosterhandler.cpp b/core/handlers/rosterhandler.cpp index 8c65c63..0027514 100644 --- a/core/handlers/rosterhandler.cpp +++ b/core/handlers/rosterhandler.cpp @@ -156,7 +156,7 @@ void Core::RosterHandler::careAboutAvatar(Core::RosterItem* item, QMaprequestVCard(item->jid); + acc->vh->requestVCard(item->jid); } } @@ -197,7 +197,7 @@ void Core::RosterHandler::handleNewRosterItem(Core::RosterItem* contact) connect(contact, &RosterItem::historyResponse, this->acc, &Account::onContactHistoryResponse); connect(contact, &RosterItem::nameChanged, this, &RosterHandler::onContactNameChanged); connect(contact, &RosterItem::avatarChanged, this, &RosterHandler::onContactAvatarChanged); - connect(contact, &RosterItem::requestVCard, this->acc, &Account::requestVCard); + connect(contact, &RosterItem::requestVCard, this->acc->vh, &VCardHandler::requestVCard); } void Core::RosterHandler::handleNewContact(Core::Contact* contact) diff --git a/core/handlers/vcardhandler.cpp b/core/handlers/vcardhandler.cpp index bde0e64..c9e7170 100644 --- a/core/handlers/vcardhandler.cpp +++ b/core/handlers/vcardhandler.cpp @@ -80,9 +80,7 @@ void Core::VCardHandler::initialize() { } } - -void Core::VCardHandler::onVCardReceived(const QXmppVCardIq& card) -{ +void Core::VCardHandler::onVCardReceived(const QXmppVCardIq& card) { QString id = card.from(); QStringList comps = id.split("/"); QString jid = comps.front().toLower(); @@ -102,13 +100,13 @@ void Core::VCardHandler::onVCardReceived(const QXmppVCardIq& card) return; } - Shared::VCard vCard = item->handleResponseVCard(card, resource); + Shared::Info info(jid, Shared::EntryType::contact); + info.vcard = item->handleResponseVCard(card, resource); - emit acc->receivedVCard(jid, vCard); + emit acc->infoReady(info); } -void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card) -{ +void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card) { QByteArray ava = card.photo(); bool avaChanged = false; QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/"; @@ -189,32 +187,30 @@ void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card) ownVCardRequestInProgress = false; - Shared::VCard vCard; - initializeVCard(vCard, card); + Shared::Info info(acc->getBareJid(), Shared::EntryType::contact, true); + initializeVCard(info.vcard, card); if (avatarType.size() > 0) { - vCard.setAvatarType(Shared::Avatar::valid); - vCard.setAvatarPath(path + "avatar." + avatarType); + info.vcard.setAvatarType(Shared::Avatar::valid); + info.vcard.setAvatarPath(path + "avatar." + avatarType); } else { - vCard.setAvatarType(Shared::Avatar::empty); + info.vcard.setAvatarType(Shared::Avatar::empty); } - emit acc->receivedVCard(acc->getBareJid(), vCard); + emit acc->infoReady(info); } -void Core::VCardHandler::handleOffline() -{ +void Core::VCardHandler::handleOffline() { pendingVCardRequests.clear(); - Shared::VCard vCard; //just to show, that there is now more pending request for (const QString& jid : pendingVCardRequests) { - emit acc->receivedVCard(jid, vCard); //need to show it better in the future, like with an error + Shared::Info info(jid, Shared::EntryType::contact); + emit acc->infoReady(info); //need to show it better in the future, like with an error } pendingVCardRequests.clear(); ownVCardRequestInProgress = false; } -void Core::VCardHandler::requestVCard(const QString& jid) -{ +void Core::VCardHandler::requestVCard(const QString& jid) { if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) { qDebug() << "requesting vCard" << jid; if (jid == acc->getBareJid()) { @@ -229,8 +225,7 @@ void Core::VCardHandler::requestVCard(const QString& jid) } } -void Core::VCardHandler::handlePresenceOfMyAccountChange(const QXmppPresence& p_presence) -{ +void Core::VCardHandler::handlePresenceOfMyAccountChange(const QXmppPresence& p_presence) { if (!ownVCardRequestInProgress) { switch (p_presence.vCardUpdateType()) { case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo @@ -253,8 +248,7 @@ void Core::VCardHandler::handlePresenceOfMyAccountChange(const QXmppPresence& p_ } } -void Core::VCardHandler::uploadVCard(const Shared::VCard& card) -{ +void Core::VCardHandler::uploadVCard(const Shared::VCard& card) { QXmppVCardIq iq; initializeQXmppVCard(iq, card); @@ -283,11 +277,10 @@ void Core::VCardHandler::uploadVCard(const Shared::VCard& card) } else { if (avatarType.size() > 0) { QFile oA(oldPath); - if (!oA.open(QFile::ReadOnly)) { + if (!oA.open(QFile::ReadOnly)) qDebug() << "Couldn't read old avatar of account" << acc->name << ", uploading empty avatar"; - } else { + else data = oA.readAll(); - } } } @@ -303,11 +296,9 @@ void Core::VCardHandler::uploadVCard(const Shared::VCard& card) onOwnVCardReceived(iq); } -QString Core::VCardHandler::getAvatarPath() const -{ - if (avatarType.size() == 0) { +QString Core::VCardHandler::getAvatarPath() const { + if (avatarType.size() == 0) return ""; - } else { + else return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/" + "avatar." + avatarType; - } } diff --git a/core/squawk.cpp b/core/squawk.cpp index 6600fcb..33f2bf5 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -180,7 +180,7 @@ void Core::Squawk::addAccount( connect(acc, &Account::changeRoomParticipant, this, &Squawk::onAccountChangeRoomPresence); connect(acc, &Account::removeRoomParticipant, this, &Squawk::onAccountRemoveRoomPresence); - connect(acc, &Account::receivedVCard, this, &Squawk::responseVCard); + connect(acc, &Account::infoReady, this, &Squawk::responseInfo); connect(acc, &Account::uploadFileError, this, &Squawk::onAccountUploadFileError); @@ -717,24 +717,24 @@ void Core::Squawk::renameContactRequest(const QString& account, const QString& j itr->second->renameContactRequest(jid, newName); } -void Core::Squawk::requestVCard(const QString& account, const QString& jid) +void Core::Squawk::requestInfo(const QString& account, const QString& jid) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { - qDebug() << "An attempt to request" << jid << "vcard of non existing account" << account << ", skipping"; + qDebug() << "An attempt to request info about" << jid << "of non existing account" << account << ", skipping"; return; } - itr->second->requestVCard(jid); + itr->second->requestInfo(jid); } -void Core::Squawk::uploadVCard(const QString& account, const Shared::VCard& card) +void Core::Squawk::updateInfo(const QString& account, const Shared::Info& info) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { - qDebug() << "An attempt to upload vcard to non existing account" << account << ", skipping"; + qDebug() << "An attempt to update info to non existing account" << account << ", skipping"; return; } - itr->second->uploadVCard(card); + itr->second->updateInfo(info); } void Core::Squawk::readSettings() diff --git a/core/squawk.h b/core/squawk.h index da4aa7e..eebe917 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -31,6 +31,7 @@ #include "shared/enums.h" #include "shared/message.h" #include "shared/global.h" +#include "shared/info.h" #include "shared/clientinfo.h" #include "external/simpleCrypt/simplecrypt.h" @@ -87,7 +88,7 @@ signals: void fileDownloadComplete(const std::list msgs, const QString& path); void fileUploadComplete(const std::list msgs, const QString& url, const QString& path); - void responseVCard(const QString& jid, const Shared::VCard& card); + void responseInfo(const Shared::Info& info); void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap& data); void requestPassword(const QString& account, bool authernticationError); @@ -123,8 +124,8 @@ public slots: void fileDownloadRequest(const QString& url); - void requestVCard(const QString& account, const QString& jid); - void uploadVCard(const QString& account, const Shared::VCard& card); + void requestInfo(const QString& account, const QString& jid); + void updateInfo(const QString& account, const Shared::Info& info); void responsePassword(const QString& account, const QString& password); void onLocalPathInvalid(const QString& path); void changeDownloadsPath(const QString& path); diff --git a/main/application.cpp b/main/application.cpp index 3942e53..edf9fd7 100644 --- a/main/application.cpp +++ b/main/application.cpp @@ -188,11 +188,11 @@ void Application::createMainWindow() connect(squawk, &Squawk::addContactToGroupRequest, core, &Core::Squawk::addContactToGroupRequest); connect(squawk, &Squawk::removeContactFromGroupRequest, core, &Core::Squawk::removeContactFromGroupRequest); connect(squawk, &Squawk::renameContactRequest, core, &Core::Squawk::renameContactRequest); - connect(squawk, &Squawk::requestVCard, core, &Core::Squawk::requestVCard); - connect(squawk, &Squawk::uploadVCard, core, &Core::Squawk::uploadVCard); + connect(squawk, &Squawk::requestInfo, core, &Core::Squawk::requestInfo); + connect(squawk, &Squawk::updateInfo, core, &Core::Squawk::updateInfo); connect(squawk, &Squawk::changeDownloadsPath, core, &Core::Squawk::changeDownloadsPath); - connect(core, &Core::Squawk::responseVCard, squawk, &Squawk::responseVCard); + connect(core, &Core::Squawk::responseInfo, squawk, &Squawk::responseInfo); dialogueQueue.setParentWidnow(squawk); squawk->stateChanged(availability); @@ -217,7 +217,7 @@ void Application::onSquawkClosing() dialogueQueue.setParentWidnow(nullptr); if (!nowQuitting) { - disconnect(core, &Core::Squawk::responseVCard, squawk, &Squawk::responseVCard); + disconnect(core, &Core::Squawk::responseInfo, squawk, &Squawk::responseInfo); } destroyingSquawk = true; diff --git a/ui/squawk.cpp b/ui/squawk.cpp index c086e10..9702357 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -29,7 +29,7 @@ Squawk::Squawk(Models::Roster& p_rosterModel, QWidget *parent) : about(nullptr), rosterModel(p_rosterModel), contextMenu(new QMenu()), - vCards(), + infoWidgets(), currentConversation(nullptr), restoreSelection(), needToRestore(false) @@ -96,8 +96,7 @@ Squawk::~Squawk() { delete contextMenu; } -void Squawk::onAccounts() -{ +void Squawk::onAccounts() { if (accounts == nullptr) { accounts = new Accounts(rosterModel.accountsModel); accounts->setAttribute(Qt::WA_DeleteOnClose); @@ -116,8 +115,7 @@ void Squawk::onAccounts() } } -void Squawk::onPreferences() -{ +void Squawk::onPreferences() { if (preferences == nullptr) { preferences = new Settings(); preferences->setAttribute(Qt::WA_DeleteOnClose); @@ -133,8 +131,7 @@ void Squawk::onPreferences() } } -void Squawk::onAccountsChanged() -{ +void Squawk::onAccountsChanged() { unsigned int size = rosterModel.accountsModel->activeSize(); if (size > 0) { m_ui->actionAddContact->setEnabled(true); @@ -145,8 +142,7 @@ void Squawk::onAccountsChanged() } } -void Squawk::onNewContact() -{ +void Squawk::onNewContact() { NewContact* nc = new NewContact(rosterModel.accountsModel, this); connect(nc, &NewContact::accepted, this, &Squawk::onNewContactAccepted); @@ -155,8 +151,7 @@ void Squawk::onNewContact() nc->exec(); } -void Squawk::onNewConference() -{ +void Squawk::onNewConference() { JoinConference* jc = new JoinConference(rosterModel.accountsModel, this); connect(jc, &JoinConference::accepted, this, &Squawk::onJoinConferenceAccepted); @@ -165,8 +160,7 @@ void Squawk::onNewConference() jc->exec(); } -void Squawk::onNewContactAccepted() -{ +void Squawk::onNewContactAccepted() { NewContact* nc = static_cast(sender()); NewContact::Data value = nc->value(); @@ -175,8 +169,7 @@ void Squawk::onNewContactAccepted() nc->deleteLater(); } -void Squawk::onJoinConferenceAccepted() -{ +void Squawk::onJoinConferenceAccepted() { JoinConference* jc = static_cast(sender()); JoinConference::Data value = jc->value(); @@ -185,23 +178,20 @@ void Squawk::onJoinConferenceAccepted() jc->deleteLater(); } -void Squawk::closeEvent(QCloseEvent* event) -{ - if (accounts != nullptr) { +void Squawk::closeEvent(QCloseEvent* event) { + if (accounts != nullptr) accounts->close(); - } - if (preferences != nullptr) { + if (preferences != nullptr) preferences->close(); - } - if (about != nullptr) { + if (about != nullptr) about->close(); - } + - for (std::map::const_iterator itr = vCards.begin(), end = vCards.end(); itr != end; ++itr) { - disconnect(itr->second, &VCard::destroyed, this, &Squawk::onVCardClosed); - itr->second->close(); + for (const std::pair& pair : infoWidgets) { + disconnect(pair.second, &UI::Info::destroyed, this, &Squawk::onInfoClosed); + pair.second->close(); } - vCards.clear(); + infoWidgets.clear(); writeSettings(); emit closing();; @@ -217,8 +207,7 @@ void Squawk::onPreferencesClosed() { void Squawk::onAboutSquawkClosed() { about = nullptr;} -void Squawk::onComboboxActivated(int index) -{ +void Squawk::onComboboxActivated(int index) { Shared::Availability av = Shared::Global::fromInt(index); emit changeState(av); } @@ -229,8 +218,7 @@ void Squawk::expand(const QModelIndex& index) { void Squawk::stateChanged(Shared::Availability state) { m_ui->comboBox->setCurrentIndex(static_cast(state));} -void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) -{ +void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) { if (item.isValid()) { Models::Item* node = static_cast(item.internalPointer()); if (node->type == Models::Item::reference) { @@ -258,8 +246,7 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) } } -void Squawk::closeCurrentConversation() -{ +void Squawk::closeCurrentConversation() { if (currentConversation != nullptr) { currentConversation->deleteLater(); currentConversation = nullptr; @@ -267,8 +254,7 @@ void Squawk::closeCurrentConversation() } } -void Squawk::onRosterContextMenu(const QPoint& point) -{ +void Squawk::onRosterContextMenu(const QPoint& point) { QModelIndex index = m_ui->roster->indexAt(point); if (index.isValid()) { Models::Item* item = static_cast(index.internalPointer()); @@ -292,9 +278,9 @@ void Squawk::onRosterContextMenu(const QPoint& point) connect(con, &QAction::triggered, std::bind(&Squawk::connectAccount, this, name)); } - QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard")); - card->setEnabled(active); - connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, name, acc->getBareJid(), true)); + QAction* info = contextMenu->addAction(Shared::icon("user-properties"), tr("Info")); + info->setEnabled(active); + connect(info, &QAction::triggered, std::bind(&Squawk::onActivateInfo, this, name, acc->getBareJid())); QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove")); connect(remove, &QAction::triggered, std::bind(&Squawk::removeAccountRequest, this, name)); @@ -379,9 +365,9 @@ void Squawk::onRosterContextMenu(const QPoint& point) }); - QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard")); - card->setEnabled(active); - connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, id.account, id.name, false)); + QAction* info = contextMenu->addAction(Shared::icon("user-properties"), tr("Info")); + info->setEnabled(active); + connect(info, &QAction::triggered, std::bind(&Squawk::onActivateInfo, this, id.account, id.name)); QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove")); remove->setEnabled(active); @@ -425,65 +411,61 @@ void Squawk::onRosterContextMenu(const QPoint& point) } } -void Squawk::responseVCard(const QString& jid, const Shared::VCard& card) -{ - std::map::const_iterator itr = vCards.find(jid); - if (itr != vCards.end()) { - itr->second->setVCard(card); +void Squawk::responseInfo(const Shared::Info& info) { + std::map::const_iterator itr = infoWidgets.find(info.jid); + if (itr != infoWidgets.end()) { + itr->second->setData(info); itr->second->hideProgress(); } } -void Squawk::onVCardClosed() -{ - VCard* vCard = static_cast(sender()); +void Squawk::onInfoClosed() { + UI::Info* info = static_cast(sender()); - std::map::const_iterator itr = vCards.find(vCard->getJid()); - if (itr == vCards.end()) { - qDebug() << "VCard has been closed but can not be found among other opened vCards, application is most probably going to crash"; + std::map::const_iterator itr = infoWidgets.find(info->jid); + if (itr == infoWidgets.end()) { + qDebug() << "Info widget has been closed but can not be found among other opened vCards, application is most probably going to crash"; return; } - vCards.erase(itr); + infoWidgets.erase(itr); } -void Squawk::onActivateVCard(const QString& account, const QString& jid, bool edition) -{ - std::map::const_iterator itr = vCards.find(jid); - VCard* card; - if (itr != vCards.end()) { - card = itr->second; +void Squawk::onActivateInfo(const QString& account, const QString& jid) { + std::map::const_iterator itr = infoWidgets.find(jid); + UI::Info* info; + if (itr != infoWidgets.end()) { + info = itr->second; } else { - card = new VCard(jid, edition); - if (edition) { - card->setWindowTitle(tr("%1 account card").arg(account)); - } else { - card->setWindowTitle(tr("%1 contact card").arg(jid)); - } - card->setAttribute(Qt::WA_DeleteOnClose); - vCards.insert(std::make_pair(jid, card)); + info = new UI::Info(jid); + // TODO need to handle it somewhere else + // if (edition) { + // card->setWindowTitle(tr("%1 account card").arg(account)); + // } else { + // card->setWindowTitle(tr("%1 contact card").arg(jid)); + // } + info->setAttribute(Qt::WA_DeleteOnClose); + infoWidgets.insert(std::make_pair(jid, info)); - connect(card, &VCard::destroyed, this, &Squawk::onVCardClosed); - connect(card, &VCard::saveVCard, std::bind( &Squawk::onVCardSave, this, std::placeholders::_1, account)); + connect(info, &UI::Info::destroyed, this, &Squawk::onInfoClosed); + connect(info, &UI::Info::saveInfo, std::bind(&Squawk::onInfoSave, this, std::placeholders::_1, account)); } - card->show(); - card->raise(); - card->activateWindow(); - card->showProgress(tr("Downloading vCard")); + info->show(); + info->raise(); + info->activateWindow(); + info->showProgress(); - emit requestVCard(account, jid); + emit requestInfo(account, jid); } -void Squawk::onVCardSave(const Shared::VCard& card, const QString& account) -{ - VCard* widget = static_cast(sender()); - emit uploadVCard(account, card); +void Squawk::onInfoSave(const Shared::Info& info, const QString& account) { + UI::Info* widget = static_cast(sender()); + emit updateInfo(account, info); widget->deleteLater(); } -void Squawk::writeSettings() -{ +void Squawk::writeSettings() { QSettings settings; settings.beginGroup("ui"); settings.beginGroup("window"); @@ -495,8 +477,7 @@ void Squawk::writeSettings() settings.endGroup(); } -void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous) -{ +void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous) { if (restoreSelection.isValid() && restoreSelection == current) { restoreSelection = QModelIndex(); return; @@ -504,9 +485,9 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn if (current.isValid()) { Models::Item* node = static_cast(current.internalPointer()); - if (node->type == Models::Item::reference) { + if (node->type == Models::Item::reference) node = static_cast(node)->dereference(); - } + Models::Contact* contact = nullptr; Models::Room* room = nullptr; QString res; @@ -550,9 +531,9 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn if (id != nullptr) { if (currentConversation != nullptr) { if (currentConversation->getId() == *id) { - if (contact != nullptr) { + if (contact != nullptr) currentConversation->setPalResource(res); - } + return; } else { currentConversation->deleteLater(); @@ -588,16 +569,14 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn } } -void Squawk::onContextAboutToHide() -{ +void Squawk::onContextAboutToHide() { if (needToRestore) { needToRestore = false; m_ui->roster->selectionModel()->setCurrentIndex(restoreSelection, QItemSelectionModel::ClearAndSelect); } } -void Squawk::onAboutSquawkCalled() -{ +void Squawk::onAboutSquawkCalled() { if (about == nullptr) { about = new About(); about->setAttribute(Qt::WA_DeleteOnClose); @@ -609,17 +588,14 @@ void Squawk::onAboutSquawkCalled() about->show(); } -Models::Roster::ElId Squawk::currentConversationId() const -{ - if (currentConversation == nullptr) { +Models::Roster::ElId Squawk::currentConversationId() const { + if (currentConversation == nullptr) return Models::Roster::ElId(); - } else { + else return Models::Roster::ElId(currentConversation->getAccount(), currentConversation->getJid()); - } } -void Squawk::select(QModelIndex index) -{ +void Squawk::select(QModelIndex index) { m_ui->roster->scrollTo(index, QAbstractItemView::EnsureVisible); m_ui->roster->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); } diff --git a/ui/squawk.h b/ui/squawk.h index 84e94a8..4e1ca4b 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -36,12 +36,13 @@ #include "widgets/newcontact.h" #include "widgets/joinconference.h" #include "models/roster.h" -#include "widgets/vcard/vcard.h" +#include "widgets/info/info.h" #include "widgets/settings/settings.h" #include "widgets/about.h" #include "shared/shared.h" #include "shared/global.h" +#include "shared/info.h" namespace Ui { class Squawk; @@ -72,8 +73,8 @@ signals: void renameContactRequest(const QString& account, const QString& jid, const QString& newName); void addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin); void removeRoomRequest(const QString& account, const QString& jid); - void requestVCard(const QString& account, const QString& jid); - void uploadVCard(const QString& account, const Shared::VCard& card); + void requestInfo(const QString& account, const QString& jid); + void updateInfo(const QString& account, const Shared::Info& info); void changeDownloadsPath(const QString& path); void changeTray(bool enabled, bool hide); @@ -93,7 +94,7 @@ public: public slots: void writeSettings(); void stateChanged(Shared::Availability state); - void responseVCard(const QString& jid, const Shared::VCard& card); + void responseInfo(const Shared::Info& info); void select(QModelIndex index); private: @@ -104,7 +105,7 @@ private: About* about; Models::Roster& rosterModel; QMenu* contextMenu; - std::map vCards; + std::map infoWidgets; Conversation* currentConversation; QModelIndex restoreSelection; bool needToRestore; @@ -123,9 +124,9 @@ private slots: void onAccountsChanged(); void onAccountsClosed(); void onPreferencesClosed(); - void onVCardClosed(); - void onVCardSave(const Shared::VCard& card, const QString& account); - void onActivateVCard(const QString& account, const QString& jid, bool edition = false); + void onInfoClosed(); + void onInfoSave(const Shared::Info& info, const QString& account); + void onActivateInfo(const QString& account, const QString& jid); void onComboboxActivated(int index); void onRosterItemDoubleClicked(const QModelIndex& item); void onRosterContextMenu(const QPoint& point); diff --git a/ui/widgets/CMakeLists.txt b/ui/widgets/CMakeLists.txt index be77db2..2099db1 100644 --- a/ui/widgets/CMakeLists.txt +++ b/ui/widgets/CMakeLists.txt @@ -17,7 +17,6 @@ target_sources(squawk PRIVATE about.ui ) -add_subdirectory(vcard) add_subdirectory(info) add_subdirectory(messageline) add_subdirectory(settings) diff --git a/ui/widgets/info/contactcontacts.cpp b/ui/widgets/info/contactcontacts.cpp index 1196aa9..10615ed 100644 --- a/ui/widgets/info/contactcontacts.cpp +++ b/ui/widgets/info/contactcontacts.cpp @@ -236,3 +236,11 @@ void UI::ContactContacts::onCopyPhone() { QString UI::ContactContacts::title() const { return m_ui->contactHeading->text(); } + +void UI::ContactContacts::fillVCard(Shared::VCard& card) const { + card.setUrl(m_ui->url->text()); + + emails.getEmails(card.getEmails()); + phones.getPhones(card.getPhones()); +} + diff --git a/ui/widgets/info/description.cpp b/ui/widgets/info/description.cpp index 5132321..b2981ca 100644 --- a/ui/widgets/info/description.cpp +++ b/ui/widgets/info/description.cpp @@ -25,3 +25,20 @@ UI::Description::Description(QWidget* parent): } UI::Description::~Description() {} + +QString UI::Description::title() const { + return m_ui->descriptionHeading->text(); +} + +QString UI::Description::description() const { + return m_ui->description->toPlainText(); +} + +void UI::Description::setDescription(const QString& description) { + m_ui->description->setText(description); +} + +void UI::Description::setEditable(bool editable) { + m_ui->description->setReadOnly(!editable); +} + diff --git a/ui/widgets/info/description.h b/ui/widgets/info/description.h index 42b0900..dc823d0 100644 --- a/ui/widgets/info/description.h +++ b/ui/widgets/info/description.h @@ -32,6 +32,11 @@ public: Description(QWidget* parent = nullptr); ~Description(); + QString title() const; + QString description() const; + void setEditable(bool editable); + void setDescription(const QString& description); + private: QScopedPointer m_ui; }; diff --git a/ui/widgets/info/info.cpp b/ui/widgets/info/info.cpp index cbaffa8..f7d7a97 100644 --- a/ui/widgets/info/info.cpp +++ b/ui/widgets/info/info.cpp @@ -17,8 +17,9 @@ #include "info.h" #include "ui_info.h" -UI::Info::Info(QWidget* parent): +UI::Info::Info(const QString& p_jid, QWidget* parent): QWidget(parent), + jid(p_jid), m_ui(new Ui::Info()), contactGeneral(nullptr), contactContacts(nullptr), @@ -28,8 +29,10 @@ UI::Info::Info(QWidget* parent): progressLabel(new QLabel()) { m_ui->setupUi(this); + m_ui->buttonBox->hide(); initializeOverlay(); + initializeButtonBox(); } UI::Info::~Info() { @@ -50,11 +53,17 @@ void UI::Info::setData(const Shared::Info& info) { case Shared::EntryType::contact: initializeContactGeneral(info); initializeContactContacts(info); - initializeDescription(info.editable); + initializeDescription(info); break; default: break; } + + if (!info.editable) + m_ui->buttonBox->hide(); + else + m_ui->buttonBox->show(); + } void UI::Info::initializeOverlay() { @@ -80,9 +89,32 @@ void UI::Info::initializeOverlay() { overlay->hide(); } +void UI::Info::initializeButtonBox() { + connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &UI::Info::onButtonBoxAccepted); + connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &UI::Info::close); + + m_ui->buttonBox->hide(); +} + +void UI::Info::onButtonBoxAccepted() { + //TODO this is not good, since I don't exactly know what am I editing it's bad to assume there even going to be a vcard + Shared::Info info; + if (contactGeneral != nullptr) + contactGeneral->fillVCard(info.vcard); + if (contactContacts != nullptr) + contactContacts->fillVCard(info.vcard); + if (description != nullptr) + info.vcard.setDescription(description->description()); + + emit saveInfo(info); +} void UI::Info::showProgress(const QString& line) { - progressLabel->setText(line); + if (line.size() > 0) + progressLabel->setText(line); + else + progressLabel->setText(tr("Requesting information about %1").arg(jid)); + overlay->show(); progress->start(); } @@ -99,3 +131,23 @@ void UI::Info::initializeContactGeneral(const Shared::Info& info) { } contactGeneral->setVCard(info.jid, info.vcard, info.editable); } + +void UI::Info::initializeContactContacts(const Shared::Info& info) { + if (contactContacts == nullptr) { + contactContacts = new ContactContacts; + m_ui->tabWidget->addTab(contactContacts, contactContacts->title()); + } + contactContacts->setVCard(info.jid, info.vcard, info.editable); +} + +void UI::Info::initializeDescription(const Shared::Info& info) { + if (description == nullptr) { + description = new Description; + m_ui->tabWidget->addTab(description, description->title()); + } + + description->setDescription(info.vcard.getDescription()); + description->setEditable(info.editable); +} + + diff --git a/ui/widgets/info/info.h b/ui/widgets/info/info.h index 126a092..461242f 100644 --- a/ui/widgets/info/info.h +++ b/ui/widgets/info/info.h @@ -39,18 +39,28 @@ class Info; class Info : public QWidget { Q_OBJECT public: - Info(QWidget* parent = nullptr); + Info(const QString& jid, QWidget* parent = nullptr); ~Info(); void setData(const Shared::Info& info); void showProgress(const QString& = ""); void hideProgress(); +public: + QString jid; + +signals: + void saveInfo(const Shared::Info& info); + +private slots: + void onButtonBoxAccepted(); + private: void initializeContactGeneral(const Shared::Info& info); void initializeContactContacts(const Shared::Info& info); - void initializeDescription(bool editable); + void initializeDescription(const Shared::Info& info); void initializeOverlay(); + void initializeButtonBox(); private: QScopedPointer m_ui; diff --git a/ui/widgets/vcard/CMakeLists.txt b/ui/widgets/vcard/CMakeLists.txt deleted file mode 100644 index 5dca28d..0000000 --- a/ui/widgets/vcard/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -target_sources(squawk PRIVATE - vcard.cpp - vcard.h - vcard.ui - ) diff --git a/ui/widgets/vcard/vcard.cpp b/ui/widgets/vcard/vcard.cpp deleted file mode 100644 index d16aa83..0000000 --- a/ui/widgets/vcard/vcard.cpp +++ /dev/null @@ -1,474 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * 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 "vcard.h" -#include "ui_vcard.h" -#include "shared/icons.h" -#include - -#include - -const std::set VCard::supportedTypes = {"image/jpeg", "image/png"}; - -VCard::VCard(const QString& jid, bool edit, QWidget* parent): - QWidget(parent), - m_ui(new Ui::VCard()), - avatarButtonMargins(), - avatarMenu(nullptr), - editable(edit), - currentAvatarType(Shared::Avatar::empty), - currentAvatarPath(""), - progress(new Progress(100)), - progressLabel(new QLabel()), - overlay(new QWidget()), - contextMenu(new QMenu()), - emails(edit), - phones(edit), - roleDelegate(new ComboboxDelegate()), - phoneTypeDelegate(new ComboboxDelegate()) - -#ifdef WITH_OMEMO - ,omemo(new Omemo()) -#endif -{ - m_ui->setupUi(this); - m_ui->jabberID->setText(jid); - m_ui->jabberID->setReadOnly(true); - - initializeActions(); - initializeDelegates(); - initializeViews(); - initializeInteractiveElements(jid); - initializeOverlay(); -#ifdef WITH_OMEMO - initializeOmemo(); -#endif -} - -VCard::~VCard() { -#ifdef WITH_OMEMO - delete omemo; -#endif - if (editable) { - avatarMenu->deleteLater(); - } - - phoneTypeDelegate->deleteLater(); - roleDelegate->deleteLater(); - contextMenu->deleteLater(); -} - -void VCard::setVCard(const QString& jid, const Shared::VCard& card) { - m_ui->jabberID->setText(jid); - setVCard(card); -} - -void VCard::setVCard(const Shared::VCard& card) { - m_ui->fullName->setText(card.getFullName()); - m_ui->firstName->setText(card.getFirstName()); - m_ui->middleName->setText(card.getMiddleName()); - m_ui->lastName->setText(card.getLastName()); - m_ui->nickName->setText(card.getNickName()); - m_ui->birthday->setDate(card.getBirthday()); - m_ui->organizationName->setText(card.getOrgName()); - m_ui->organizationDepartment->setText(card.getOrgUnit()); - m_ui->organizationTitle->setText(card.getOrgTitle()); - m_ui->organizationRole->setText(card.getOrgRole()); - m_ui->description->setText(card.getDescription()); - m_ui->url->setText(card.getUrl()); - - QDateTime receivingTime = card.getReceivingTime(); - m_ui->receivingTimeLabel->setText(tr("Received %1 at %2").arg(receivingTime.date().toString()).arg(receivingTime.time().toString())); - currentAvatarType = card.getAvatarType(); - currentAvatarPath = card.getAvatarPath(); - - updateAvatar(); - - const std::deque& ems = card.getEmails(); - const std::deque& phs = card.getPhones(); - emails.setEmails(ems); - phones.setPhones(phs); -} - -QString VCard::getJid() const { - return m_ui->jabberID->text(); -} - -void VCard::onButtonBoxAccepted() { - Shared::VCard card; - card.setFullName(m_ui->fullName->text()); - card.setFirstName(m_ui->firstName->text()); - card.setMiddleName(m_ui->middleName->text()); - card.setLastName(m_ui->lastName->text()); - card.setNickName(m_ui->nickName->text()); - card.setBirthday(m_ui->birthday->date()); - card.setDescription(m_ui->description->toPlainText()); - card.setUrl(m_ui->url->text()); - card.setOrgName(m_ui->organizationName->text()); - card.setOrgUnit(m_ui->organizationDepartment->text()); - card.setOrgRole(m_ui->organizationRole->text()); - card.setOrgTitle(m_ui->organizationTitle->text()); - card.setAvatarPath(currentAvatarPath); - card.setAvatarType(currentAvatarType); - - emails.getEmails(card.getEmails()); - phones.getPhones(card.getPhones()); - - emit saveVCard(card); -} - -void VCard::onClearAvatar() { - currentAvatarType = Shared::Avatar::empty; - currentAvatarPath = ""; - - updateAvatar(); -} - -void VCard::onSetAvatar() { - QFileDialog* d = new QFileDialog(this, tr("Chose your new avatar")); - d->setFileMode(QFileDialog::ExistingFile); - d->setNameFilter(tr("Images (*.png *.jpg *.jpeg)")); - - connect(d, &QFileDialog::accepted, this, &VCard::onAvatarSelected); - connect(d, &QFileDialog::rejected, d, &QFileDialog::deleteLater); - - d->show(); -} - -void VCard::updateAvatar() { - int height = m_ui->personalForm->minimumSize().height() - avatarButtonMargins.height(); - switch (currentAvatarType) { - case Shared::Avatar::empty: - m_ui->avatarButton->setIcon(Shared::icon("user", true)); - m_ui->avatarButton->setIconSize(QSize(height, height)); - m_ui->actionClearAvatar->setEnabled(false); - break; - case Shared::Avatar::autocreated: - case Shared::Avatar::valid: - QPixmap pixmap(currentAvatarPath); - qreal h = pixmap.height(); - qreal w = pixmap.width(); - qreal aspectRatio = w / h; - m_ui->avatarButton->setIconSize(QSize(height * aspectRatio, height)); - m_ui->avatarButton->setIcon(QIcon(currentAvatarPath)); - m_ui->actionClearAvatar->setEnabled(true); - break; - } -} - -void VCard::onAvatarSelected() { - QFileDialog* d = static_cast(sender()); - QMimeDatabase db; - QString path = d->selectedFiles().front(); - QMimeType type = db.mimeTypeForFile(path); - d->deleteLater(); - - if (supportedTypes.find(type.name()) == supportedTypes.end()) { - qDebug() << "Selected for avatar file is not supported, skipping"; - } else { - QImage src(path); - QImage dst; - if (src.width() > 160 || src.height() > 160) { - dst = src.scaled(160, 160, Qt::KeepAspectRatio); - } - QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + m_ui->jabberID->text() + ".temp." + type.preferredSuffix(); - QFile oldTemp(path); - if (oldTemp.exists()) { - if (!oldTemp.remove()) { - qDebug() << "Error removing old temp avatar" << path; - return; - } - } - bool success = dst.save(path); - if (success) { - currentAvatarPath = path; - currentAvatarType = Shared::Avatar::valid; - - updateAvatar(); - } else { - qDebug() << "couldn't save avatar" << path << ", skipping"; - } - } -} - -void VCard::showProgress(const QString& line) { - progressLabel->setText(line); - overlay->show(); - progress->start(); -} - -void VCard::hideProgress() { - overlay->hide(); - progress->stop(); -} - -void VCard::onContextMenu(const QPoint& point) { - contextMenu->clear(); - bool hasMenu = false; - QAbstractItemView* snd = static_cast(sender()); - if (snd == m_ui->emailsView) { - hasMenu = true; - if (editable) { - QAction* add = contextMenu->addAction(Shared::icon("list-add"), tr("Add email address")); - connect(add, &QAction::triggered, this, &VCard::onAddEmail); - - QItemSelectionModel* sm = m_ui->emailsView->selectionModel(); - int selectionSize = sm->selectedRows().size(); - - if (selectionSize > 0) { - if (selectionSize == 1) { - int row = sm->selectedRows().at(0).row(); - if (emails.isPreferred(row)) { - QAction* rev = contextMenu->addAction(Shared::icon("unfavorite"), tr("Unset this email as preferred")); - connect(rev, &QAction::triggered, std::bind(&Models::EMails::revertPreferred, &emails, row)); - } else { - QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Set this email as preferred")); - connect(rev, &QAction::triggered, std::bind(&Models::EMails::revertPreferred, &emails, row)); - } - } - - QAction* del = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove selected email addresses")); - connect(del, &QAction::triggered, this, &VCard::onRemoveEmail); - } - } - - QAction* cp = contextMenu->addAction(Shared::icon("edit-copy"), tr("Copy selected emails to clipboard")); - connect(cp, &QAction::triggered, this, &VCard::onCopyEmail); - } else if (snd == m_ui->phonesView) { - hasMenu = true; - if (editable) { - QAction* add = contextMenu->addAction(Shared::icon("list-add"), tr("Add phone number")); - connect(add, &QAction::triggered, this, &VCard::onAddPhone); - - QItemSelectionModel* sm = m_ui->phonesView->selectionModel(); - int selectionSize = sm->selectedRows().size(); - - if (selectionSize > 0) { - if (selectionSize == 1) { - int row = sm->selectedRows().at(0).row(); - if (phones.isPreferred(row)) { - QAction* rev = contextMenu->addAction(Shared::icon("view-media-favorite"), tr("Unset this phone as preferred")); - connect(rev, &QAction::triggered, std::bind(&Models::Phones::revertPreferred, &phones, row)); - } else { - QAction* rev = contextMenu->addAction(Shared::icon("favorite"), tr("Set this phone as preferred")); - connect(rev, &QAction::triggered, std::bind(&Models::Phones::revertPreferred, &phones, row)); - } - } - - QAction* del = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove selected phone numbers")); - connect(del, &QAction::triggered, this, &VCard::onRemovePhone); - } - } - - QAction* cp = contextMenu->addAction(Shared::icon("edit-copy"), tr("Copy selected phones to clipboard")); - connect(cp, &QAction::triggered, this, &VCard::onCopyPhone); - } - - if (hasMenu) { - contextMenu->popup(snd->viewport()->mapToGlobal(point)); - } -} - -void VCard::onAddEmail() { - QModelIndex index = emails.addNewEmptyLine(); - m_ui->emailsView->setCurrentIndex(index); - m_ui->emailsView->edit(index); -} - -void VCard::onAddAddress() {} -void VCard::onAddPhone() { - QModelIndex index = phones.addNewEmptyLine(); - m_ui->phonesView->setCurrentIndex(index); - m_ui->phonesView->edit(index); -} -void VCard::onRemoveAddress() {} -void VCard::onRemoveEmail() { - QItemSelection selection(m_ui->emailsView->selectionModel()->selection()); - - QList rows; - for (const QModelIndex& index : selection.indexes()) { - rows.append(index.row()); - } - - std::sort(rows.begin(), rows.end()); - - int prev = -1; - for (int i = rows.count() - 1; i >= 0; i -= 1) { - int current = rows[i]; - if (current != prev) { - emails.removeLines(current, 1); - prev = current; - } - } -} - -void VCard::onRemovePhone() { - QItemSelection selection(m_ui->phonesView->selectionModel()->selection()); - - QList rows; - for (const QModelIndex& index : selection.indexes()) { - rows.append(index.row()); - } - - std::sort(rows.begin(), rows.end()); - - int prev = -1; - for (int i = rows.count() - 1; i >= 0; i -= 1) { - int current = rows[i]; - if (current != prev) { - phones.removeLines(current, 1); - prev = current; - } - } -} - -void VCard::onCopyEmail() { - QList selection(m_ui->emailsView->selectionModel()->selectedRows()); - - QList addrs; - for (const QModelIndex& index : selection) { - addrs.push_back(emails.getEmail(index.row())); - } - - QString list = addrs.join("\n"); - - QClipboard* cb = QApplication::clipboard(); - cb->setText(list); -} - -void VCard::onCopyPhone() { - QList selection(m_ui->phonesView->selectionModel()->selectedRows()); - - QList phs; - for (const QModelIndex& index : selection) { - phs.push_back(phones.getPhone(index.row())); - } - - QString list = phs.join("\n"); - - QClipboard* cb = QApplication::clipboard(); - cb->setText(list); -} - -void VCard::initializeDelegates() { - roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[0].toStdString().c_str())); - roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[1].toStdString().c_str())); - roleDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Email::roleNames[2].toStdString().c_str())); - - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[0].toStdString().c_str())); - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[1].toStdString().c_str())); - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[2].toStdString().c_str())); - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[3].toStdString().c_str())); - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[4].toStdString().c_str())); - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[5].toStdString().c_str())); - phoneTypeDelegate->addEntry(QCoreApplication::translate("Global", Shared::VCard::Phone::typeNames[6].toStdString().c_str())); -} - -void VCard::initializeViews() { - m_ui->emailsView->setContextMenuPolicy(Qt::CustomContextMenu); - m_ui->emailsView->setModel(&emails); - m_ui->emailsView->setItemDelegateForColumn(1, roleDelegate); - m_ui->emailsView->setColumnWidth(2, 25); - m_ui->emailsView->horizontalHeader()->setStretchLastSection(false); - m_ui->emailsView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); - - m_ui->phonesView->setContextMenuPolicy(Qt::CustomContextMenu); - m_ui->phonesView->setModel(&phones); - m_ui->phonesView->setItemDelegateForColumn(1, roleDelegate); - m_ui->phonesView->setItemDelegateForColumn(2, phoneTypeDelegate); - m_ui->phonesView->setColumnWidth(3, 25); - m_ui->phonesView->horizontalHeader()->setStretchLastSection(false); - m_ui->phonesView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); - - connect(m_ui->phonesView, &QWidget::customContextMenuRequested, this, &VCard::onContextMenu); - connect(m_ui->emailsView, &QWidget::customContextMenuRequested, this, &VCard::onContextMenu); -} - -void VCard::initializeActions() { - QAction* setAvatar = m_ui->actionSetAvatar; - QAction* clearAvatar = m_ui->actionClearAvatar; - - connect(setAvatar, &QAction::triggered, this, &VCard::onSetAvatar); - connect(clearAvatar, &QAction::triggered, this, &VCard::onClearAvatar); - - setAvatar->setEnabled(true); - clearAvatar->setEnabled(false); -} - -void VCard::initializeInteractiveElements(const QString& jid) { - if (editable) { - avatarMenu = new QMenu(); - m_ui->avatarButton->setMenu(avatarMenu); - avatarMenu->addAction(m_ui->actionSetAvatar); - avatarMenu->addAction(m_ui->actionClearAvatar); - m_ui->title->setText(tr("Account %1 card").arg(jid)); - } else { - m_ui->buttonBox->hide(); - m_ui->fullName->setReadOnly(true); - m_ui->firstName->setReadOnly(true); - m_ui->middleName->setReadOnly(true); - m_ui->lastName->setReadOnly(true); - m_ui->nickName->setReadOnly(true); - m_ui->birthday->setReadOnly(true); - m_ui->organizationName->setReadOnly(true); - m_ui->organizationDepartment->setReadOnly(true); - m_ui->organizationTitle->setReadOnly(true); - m_ui->organizationRole->setReadOnly(true); - m_ui->description->setReadOnly(true); - m_ui->url->setReadOnly(true); - m_ui->title->setText(tr("Contact %1 card").arg(jid)); - } - - connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &VCard::onButtonBoxAccepted); - connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &VCard::close); - - avatarButtonMargins = m_ui->avatarButton->size(); - - int height = m_ui->personalForm->minimumSize().height() - avatarButtonMargins.height(); - m_ui->avatarButton->setIconSize(QSize(height, height)); -} - -void VCard::initializeOverlay() { - QGridLayout* gr = static_cast(layout()); - gr->addWidget(overlay, 0, 0, 4, 1); - QVBoxLayout* nl = new QVBoxLayout(); - QGraphicsOpacityEffect* opacity = new QGraphicsOpacityEffect(); - opacity->setOpacity(0.8); - overlay->setLayout(nl); - overlay->setBackgroundRole(QPalette::Base); - overlay->setAutoFillBackground(true); - overlay->setGraphicsEffect(opacity); - progressLabel->setAlignment(Qt::AlignCenter); - QFont pf = progressLabel->font(); - pf.setBold(true); - pf.setPointSize(26); - progressLabel->setFont(pf); - progressLabel->setWordWrap(true); - nl->addStretch(); - nl->addWidget(progress); - nl->addWidget(progressLabel); - nl->addStretch(); - overlay->hide(); -} - -#ifdef WITH_OMEMO -void VCard::initializeOmemo() { - m_ui->tabWidget->addTab(omemo, "OMEMO"); -} - -#endif diff --git a/ui/widgets/vcard/vcard.h b/ui/widgets/vcard/vcard.h deleted file mode 100644 index 7019797..0000000 --- a/ui/widgets/vcard/vcard.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * 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 VCARD_H -#define VCARD_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "shared/vcard.h" -#include "ui/models/info/emails.h" -#include "ui/models/info/phones.h" -#include "ui/utils/progress.h" -#include "ui/utils/comboboxdelegate.h" - -#ifdef WITH_OMEMO -#include "ui/widgets/info/omemo/omemo.h" -#endif - -namespace Ui -{ -class VCard; -} - -class VCard : public QWidget { - Q_OBJECT -public: - VCard(const QString& jid, bool edit = false, QWidget* parent = nullptr); - ~VCard(); - - void setVCard(const Shared::VCard& card); - void setVCard(const QString& jid, const Shared::VCard& card); - QString getJid() const; - void showProgress(const QString& = ""); - void hideProgress(); - -signals: - void saveVCard(const Shared::VCard& card); - -private slots: - void onButtonBoxAccepted(); - void onClearAvatar(); - void onSetAvatar(); - void onAvatarSelected(); - void onAddAddress(); - void onRemoveAddress(); - void onAddEmail(); - void onCopyEmail(); - void onRemoveEmail(); - void onAddPhone(); - void onCopyPhone(); - void onRemovePhone(); - void onContextMenu(const QPoint& point); - -private: - QScopedPointer m_ui; - QSize avatarButtonMargins; - QMenu* avatarMenu; - bool editable; - Shared::Avatar currentAvatarType; - QString currentAvatarPath; - Progress* progress; - QLabel* progressLabel; - QWidget* overlay; - QMenu* contextMenu; - Models::EMails emails; - Models::Phones phones; - ComboboxDelegate* roleDelegate; - ComboboxDelegate* phoneTypeDelegate; -#ifdef WITH_OMEMO - Omemo* omemo; -#endif - - static const std::set supportedTypes; - -private: - void updateAvatar(); - void initializeDelegates(); - void initializeViews(); - void initializeActions(); - void initializeInteractiveElements(const QString& jid); - void initializeOverlay(); -#ifdef WITH_OMEMO - void initializeOmemo(); -#endif -}; - -#endif // VCARD_H diff --git a/ui/widgets/vcard/vcard.ui b/ui/widgets/vcard/vcard.ui deleted file mode 100644 index 7e09257..0000000 --- a/ui/widgets/vcard/vcard.ui +++ /dev/null @@ -1,892 +0,0 @@ - - - VCard - - - true - - - - 0 - 0 - 578 - 748 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - font: 16pt - - - Contact john@dow.org card - - - Qt::AlignCenter - - - - - - - font: italic 8pt; - - - Received 12.07.2007 at 17.35 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - true - - - Qt::TabFocus - - - QTabWidget::North - - - QTabWidget::Rounded - - - 1 - - - Qt::ElideNone - - - true - - - false - - - - General - - - - 0 - - - 6 - - - 0 - - - 0 - - - 6 - - - - - - 0 - 0 - - - - font: 600 16pt; - - - Organization - - - Qt::AlignCenter - - - - - - - QLayout::SetDefaultConstraint - - - Qt::AlignHCenter|Qt::AlignTop - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Middle name - - - middleName - - - - - - - First name - - - firstName - - - - - - - Last name - - - lastName - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Nick name - - - nickName - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Birthday - - - birthday - - - - - - - - - - - - Qt::AlignHCenter|Qt::AlignTop - - - - - Organization name - - - organizationName - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Unit / Department - - - organizationDepartment - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Role / Profession - - - organizationRole - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - Job title - - - organizationTitle - - - - - - - - 200 - 0 - - - - - 350 - 16777215 - - - - - - - - - - Qt::Horizontal - - - - - - - - - - - - Full name - - - fullName - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Horizontal - - - - - - - font: 600 24pt ; - - - General - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - font: 600 16pt; - - - QFrame::NoFrame - - - QFrame::Plain - - - Personal information - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - - - - :/images/fallback/dark/big/user.svg:/images/fallback/dark/big/user.svg - - - - 0 - 0 - - - - QToolButton::InstantPopup - - - Qt::ToolButtonIconOnly - - - Qt::NoArrow - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - Contact - - - - 0 - - - 0 - - - 6 - - - 0 - - - 0 - - - - - font: 600 24pt ; - - - Contact - - - - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - true - - - - - 0 - 0 - 545 - 544 - - - - - - - Qt::Horizontal - - - - - - - QAbstractItemView::ExtendedSelection - - - QAbstractItemView::SelectRows - - - false - - - false - - - false - - - - - - - Qt::Horizontal - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Horizontal - - - - - - - Qt::Horizontal - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - font: 600 16pt; - - - Addresses - - - Qt::AlignCenter - - - - - - - QAbstractItemView::SelectRows - - - false - - - false - - - false - - - - - - - font: 600 16pt; - - - E-Mail addresses - - - Qt::AlignCenter - - - - - - - Qt::AlignHCenter|Qt::AlignTop - - - - - - 150 - 0 - - - - - 300 - 16777215 - - - - - - - - Jabber ID - - - jabberID - - - - - - - - 150 - 0 - - - - - 300 - 16777215 - - - - - - - - Web site - - - url - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - font: 600 16pt; - - - Phone numbers - - - Qt::AlignCenter - - - - - - - QAbstractItemView::SelectRows - - - false - - - false - - - false - - - - - - - - - - - - Description - - - - 0 - - - 6 - - - 0 - - - 0 - - - 6 - - - - - font: 600 24pt ; - - - Description - - - - - - - QFrame::StyledPanel - - - Qt::LinksAccessibleByMouse|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - - - - - QDialogButtonBox::Close|QDialogButtonBox::Save - - - false - - - - - - - - - - :/images/fallback/dark/big/edit-rename.svg:/images/fallback/dark/big/edit-rename.svg - - - Set avatar - - - - - - :/images/fallback/dark/big/clean.svg:/images/fallback/dark/big/clean.svg - - - Clear avatar - - - - - fullName - firstName - middleName - lastName - nickName - birthday - avatarButton - organizationName - organizationDepartment - organizationRole - organizationTitle - jabberID - url - description - - - - - - From ec362cef553f0397065e6004495f0f4cd6a3f22f Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 21 Feb 2023 23:27:28 +0300 Subject: [PATCH 089/137] some further thinking of info widget --- core/handlers/vcardhandler.cpp | 4 +- shared/enums.h | 4 +- ui/squawk.cpp | 9 +--- ui/widgets/info/info.cpp | 81 ++++++++++++++++++++-------------- ui/widgets/info/info.h | 14 +++--- 5 files changed, 64 insertions(+), 48 deletions(-) diff --git a/core/handlers/vcardhandler.cpp b/core/handlers/vcardhandler.cpp index c9e7170..ca8b29b 100644 --- a/core/handlers/vcardhandler.cpp +++ b/core/handlers/vcardhandler.cpp @@ -187,7 +187,7 @@ void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card) { ownVCardRequestInProgress = false; - Shared::Info info(acc->getBareJid(), Shared::EntryType::contact, true); + Shared::Info info(acc->getBareJid(), Shared::EntryType::ownAccount, true); initializeVCard(info.vcard, card); if (avatarType.size() > 0) { @@ -203,7 +203,7 @@ void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card) { void Core::VCardHandler::handleOffline() { pendingVCardRequests.clear(); for (const QString& jid : pendingVCardRequests) { - Shared::Info info(jid, Shared::EntryType::contact); + Shared::Info info(jid, Shared::EntryType::none); emit acc->infoReady(info); //need to show it better in the future, like with an error } pendingVCardRequests.clear(); diff --git a/shared/enums.h b/shared/enums.h index 9eb9e5e..46d954a 100644 --- a/shared/enums.h +++ b/shared/enums.h @@ -119,6 +119,8 @@ static const AccountPassword AccountPasswordHighest = AccountPassword::kwallet; static const AccountPassword AccountPasswordLowest = AccountPassword::plain; enum class EntryType { + none, + ownAccount, contact, conference, presence, @@ -126,7 +128,7 @@ enum class EntryType { }; Q_ENUM_NS(EntryType) static const EntryType EntryTypeHighest = EntryType::participant; -static const EntryType EntryTypeLowest = EntryType::contact; +static const EntryType EntryTypeLowest = EntryType::none; enum class Support { unknown, diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 9702357..2c969da 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -422,7 +422,7 @@ void Squawk::responseInfo(const Shared::Info& info) { void Squawk::onInfoClosed() { UI::Info* info = static_cast(sender()); - std::map::const_iterator itr = infoWidgets.find(info->jid); + std::map::const_iterator itr = infoWidgets.find(info->getJid()); if (itr == infoWidgets.end()) { qDebug() << "Info widget has been closed but can not be found among other opened vCards, application is most probably going to crash"; return; @@ -437,12 +437,7 @@ void Squawk::onActivateInfo(const QString& account, const QString& jid) { info = itr->second; } else { info = new UI::Info(jid); - // TODO need to handle it somewhere else - // if (edition) { - // card->setWindowTitle(tr("%1 account card").arg(account)); - // } else { - // card->setWindowTitle(tr("%1 contact card").arg(jid)); - // } + info->setWindowTitle(tr("Information about %1").arg(jid)); info->setAttribute(Qt::WA_DeleteOnClose); infoWidgets.insert(std::make_pair(jid, info)); diff --git a/ui/widgets/info/info.cpp b/ui/widgets/info/info.cpp index f7d7a97..e951e97 100644 --- a/ui/widgets/info/info.cpp +++ b/ui/widgets/info/info.cpp @@ -20,6 +20,7 @@ UI::Info::Info(const QString& p_jid, QWidget* parent): QWidget(parent), jid(p_jid), + type(Shared::EntryType::none), m_ui(new Ui::Info()), contactGeneral(nullptr), contactContacts(nullptr), @@ -30,36 +31,39 @@ UI::Info::Info(const QString& p_jid, QWidget* parent): { m_ui->setupUi(this); m_ui->buttonBox->hide(); + m_ui->title->setText(tr("%1 info").arg(p_jid)); + m_ui->receivingTimeLabel->hide(); initializeOverlay(); initializeButtonBox(); } UI::Info::~Info() { - if (contactGeneral != nullptr) - contactGeneral->deleteLater(); - - if (contactContacts != nullptr) - contactContacts->deleteLater(); - - if (description != nullptr) - description->deleteLater(); - + clear(); overlay->deleteLater(); } void UI::Info::setData(const Shared::Info& info) { + bool editable = false; switch (info.type) { - case Shared::EntryType::contact: - initializeContactGeneral(info); - initializeContactContacts(info); - initializeDescription(info); + case Shared::EntryType::ownAccount: + editable = true; + case Shared::EntryType::contact: { + QDateTime receivingTime = info.vcard.getReceivingTime(); + m_ui->receivingTimeLabel->show(); + m_ui->receivingTimeLabel->setText(tr("Received %1 at %2").arg(receivingTime.date().toString()).arg(receivingTime.time().toString())); + initializeContactGeneral(jid, info.vcard, editable); + initializeContactContacts(jid, info.vcard, editable); + initializeDescription(info.vcard.getDescription(), editable); + type = info.type; + } break; default: + clear(); break; } - if (!info.editable) + if (!editable) m_ui->buttonBox->hide(); else m_ui->buttonBox->show(); @@ -77,10 +81,7 @@ void UI::Info::initializeOverlay() { overlay->setAutoFillBackground(true); overlay->setGraphicsEffect(opacity); progressLabel->setAlignment(Qt::AlignCenter); - QFont pf = progressLabel->font(); - pf.setBold(true); - pf.setPointSize(26); - progressLabel->setFont(pf); + progressLabel->setFont(Shared::Global::getInstance()->titleFont); progressLabel->setWordWrap(true); nl->addStretch(); nl->addWidget(progress); @@ -97,16 +98,13 @@ void UI::Info::initializeButtonBox() { } void UI::Info::onButtonBoxAccepted() { - //TODO this is not good, since I don't exactly know what am I editing it's bad to assume there even going to be a vcard - Shared::Info info; - if (contactGeneral != nullptr) + if (type == Shared::EntryType::ownAccount) { + Shared::Info info; contactGeneral->fillVCard(info.vcard); - if (contactContacts != nullptr) contactContacts->fillVCard(info.vcard); - if (description != nullptr) info.vcard.setDescription(description->description()); - - emit saveInfo(info); + emit saveInfo(info); + } } void UI::Info::showProgress(const QString& line) { @@ -124,30 +122,49 @@ void UI::Info::hideProgress() { progress->stop(); } -void UI::Info::initializeContactGeneral(const Shared::Info& info) { +void UI::Info::initializeContactGeneral(const QString& jid, const Shared::VCard& card, bool editable) { if (contactGeneral == nullptr) { contactGeneral = new ContactGeneral; m_ui->tabWidget->addTab(contactGeneral, contactGeneral->title()); } - contactGeneral->setVCard(info.jid, info.vcard, info.editable); + contactGeneral->setVCard(jid, card, editable); } -void UI::Info::initializeContactContacts(const Shared::Info& info) { +void UI::Info::initializeContactContacts(const QString& jid, const Shared::VCard& card, bool editable) { if (contactContacts == nullptr) { contactContacts = new ContactContacts; m_ui->tabWidget->addTab(contactContacts, contactContacts->title()); } - contactContacts->setVCard(info.jid, info.vcard, info.editable); + contactContacts->setVCard(jid, card, editable); } -void UI::Info::initializeDescription(const Shared::Info& info) { +void UI::Info::initializeDescription(const QString& descr, bool editable) { if (description == nullptr) { description = new Description; m_ui->tabWidget->addTab(description, description->title()); } - description->setDescription(info.vcard.getDescription()); - description->setEditable(info.editable); + description->setDescription(descr); + description->setEditable(editable); } +QString UI::Info::getJid() const { + return jid;} +void UI::Info::clear() { + if (contactGeneral != nullptr) { + contactGeneral->deleteLater(); + contactGeneral = nullptr; + } + + if (contactContacts != nullptr) { + contactContacts->deleteLater(); + contactContacts = nullptr; + } + + if (description != nullptr) { + description->deleteLater(); + description = nullptr; + } + type = Shared::EntryType::none; +} diff --git a/ui/widgets/info/info.h b/ui/widgets/info/info.h index 461242f..14829e7 100644 --- a/ui/widgets/info/info.h +++ b/ui/widgets/info/info.h @@ -23,6 +23,7 @@ #include #include +#include #include "ui/utils/progress.h" @@ -42,13 +43,11 @@ public: Info(const QString& jid, QWidget* parent = nullptr); ~Info(); + QString getJid() const; void setData(const Shared::Info& info); void showProgress(const QString& = ""); void hideProgress(); -public: - QString jid; - signals: void saveInfo(const Shared::Info& info); @@ -56,13 +55,16 @@ private slots: void onButtonBoxAccepted(); private: - void initializeContactGeneral(const Shared::Info& info); - void initializeContactContacts(const Shared::Info& info); - void initializeDescription(const Shared::Info& info); + void initializeContactGeneral(const QString& jid, const Shared::VCard& card, bool editable); + void initializeContactContacts(const QString& jid, const Shared::VCard& card, bool editable); + void initializeDescription(const QString& description, bool editable); void initializeOverlay(); void initializeButtonBox(); + void clear(); private: + QString jid; + Shared::EntryType type; QScopedPointer m_ui; ContactGeneral* contactGeneral; ContactContacts* contactContacts; From 6f32e995938f58d0aa10823f04d3729586a0b3cf Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 1 Mar 2023 22:32:41 +0300 Subject: [PATCH 090/137] an idea how to manage info object better --- core/account.cpp | 2 +- core/conference.cpp | 10 +- core/conference.h | 2 +- core/handlers/vcardhandler.cpp | 13 +- core/rosteritem.cpp | 5 +- core/rosteritem.h | 2 +- shared/info.cpp | 327 +++++++++++++++++++++++++++-- shared/info.h | 57 ++++- ui/squawk.cpp | 2 +- ui/widgets/info/contactgeneral.cpp | 8 +- ui/widgets/info/contactgeneral.h | 1 - ui/widgets/info/info.cpp | 24 ++- 12 files changed, 390 insertions(+), 63 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index 98862a4..0970a6e 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -735,7 +735,7 @@ void Core::Account::requestInfo(const QString& jid) { void Core::Account::updateInfo(const Shared::Info& info) { //TODO switch case of what kind of entity this info update is about //right now it could be only about myself - vh->uploadVCard(info.vcard); + vh->uploadVCard(info.getVCardRef()); } QString Core::Account::getAvatarPath() const { diff --git a/core/conference.cpp b/core/conference.cpp index a984f41..065490c 100644 --- a/core/conference.cpp +++ b/core/conference.cpp @@ -339,18 +339,16 @@ bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& in return result; } -Shared::VCard Core::Conference::handleResponseVCard(const QXmppVCardIq& card, const QString &resource) +void Core::Conference::handleResponseVCard(const QXmppVCardIq& card, const QString &resource, Shared::VCard& out) { - Shared::VCard result = RosterItem::handleResponseVCard(card, resource); + RosterItem::handleResponseVCard(card, resource, out); if (resource.size() > 0) { emit changeParticipant(resource, { - {"avatarState", static_cast(result.getAvatarType())}, - {"avatarPath", result.getAvatarPath()} + {"avatarState", static_cast(out.getAvatarType())}, + {"avatarPath", out.getAvatarPath()} }); } - - return result; } QMap Core::Conference::getAllAvatars() const diff --git a/core/conference.h b/core/conference.h index 4e0e463..41141ad 100644 --- a/core/conference.h +++ b/core/conference.h @@ -52,7 +52,7 @@ public: void setAutoJoin(bool p_autoJoin); void handlePresence(const QXmppPresence & pres) override; bool setAutoGeneratedAvatar(const QString& resource = "") override; - Shared::VCard handleResponseVCard(const QXmppVCardIq & card, const QString &resource) override; + void handleResponseVCard(const QXmppVCardIq & card, const QString &resource, Shared::VCard& out) override; QMap getAllAvatars() const; signals: diff --git a/core/handlers/vcardhandler.cpp b/core/handlers/vcardhandler.cpp index ca8b29b..33c9cdb 100644 --- a/core/handlers/vcardhandler.cpp +++ b/core/handlers/vcardhandler.cpp @@ -101,7 +101,7 @@ void Core::VCardHandler::onVCardReceived(const QXmppVCardIq& card) { } Shared::Info info(jid, Shared::EntryType::contact); - info.vcard = item->handleResponseVCard(card, resource); + item->handleResponseVCard(card, resource, info.getVCardRef()); emit acc->infoReady(info); } @@ -187,14 +187,15 @@ void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card) { ownVCardRequestInProgress = false; - Shared::Info info(acc->getBareJid(), Shared::EntryType::ownAccount, true); - initializeVCard(info.vcard, card); + Shared::Info info(acc->getBareJid(), Shared::EntryType::ownAccount); + Shared::VCard& vCard = info.getVCardRef(); + initializeVCard(vCard, card); if (avatarType.size() > 0) { - info.vcard.setAvatarType(Shared::Avatar::valid); - info.vcard.setAvatarPath(path + "avatar." + avatarType); + vCard.setAvatarType(Shared::Avatar::valid); + vCard.setAvatarPath(path + "avatar." + avatarType); } else { - info.vcard.setAvatarType(Shared::Avatar::empty); + vCard.setAvatarType(Shared::Avatar::empty); } emit acc->infoReady(info); diff --git a/core/rosteritem.cpp b/core/rosteritem.cpp index 1b8d1e6..0bac4a4 100644 --- a/core/rosteritem.cpp +++ b/core/rosteritem.cpp @@ -506,14 +506,13 @@ bool Core::RosterItem::readAvatarInfo(Archive::AvatarInfo& target, const QString return archive->readAvatarInfo(target, resource); } -Shared::VCard Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QString& resource) +void Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QString& resource, Shared::VCard& vCard) { Archive::AvatarInfo info; Archive::AvatarInfo newInfo; bool hasAvatar = readAvatarInfo(info, resource); QByteArray ava = card.photo(); - Shared::VCard vCard; initializeVCard(vCard, card); Shared::Avatar type = Shared::Avatar::empty; QString path = ""; @@ -546,8 +545,6 @@ Shared::VCard Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, co if (resource.size() == 0) { emit avatarChanged(vCard.getAvatarType(), vCard.getAvatarPath()); } - - return vCard; } void Core::RosterItem::clearArchiveRequests() diff --git a/core/rosteritem.h b/core/rosteritem.h index 5f99017..7c82945 100644 --- a/core/rosteritem.h +++ b/core/rosteritem.h @@ -72,7 +72,7 @@ public: QString folderPath() const; bool readAvatarInfo(Archive::AvatarInfo& target, const QString& resource = "") const; virtual bool setAutoGeneratedAvatar(const QString& resource = ""); - virtual Shared::VCard handleResponseVCard(const QXmppVCardIq& card, const QString& resource); + virtual void handleResponseVCard(const QXmppVCardIq& card, const QString& resource, Shared::VCard& out); virtual void handlePresence(const QXmppPresence& pres) = 0; bool changeMessage(const QString& id, const QMap& data); diff --git a/shared/info.cpp b/shared/info.cpp index a0ac299..c0d6f26 100644 --- a/shared/info.cpp +++ b/shared/info.cpp @@ -16,32 +16,315 @@ #include "info.h" -Shared::Info::Info(const QString& p_jid, EntryType p_type, bool p_editable): - type(p_type), - jid(p_jid), - editable(p_editable), - vcard(), - activeKeys(), - inactiveKeys() -{} +Shared::Info::Info(const QString& addr, EntryType tp): + type(tp), + address(addr), + vcard(nullptr), + activeKeys(nullptr), + inactiveKeys(nullptr) +{ + switch (type) { + case EntryType::none: + break; + case EntryType::contact: + case EntryType::ownAccount: + vcard = new VCard(); + activeKeys = new std::list(); + inactiveKeys = new std::list(); + break; + default: + throw 352; + } +} Shared::Info::Info(): - type(EntryType::contact), - jid(), - editable(false), - vcard(), - activeKeys(), - inactiveKeys() + type(EntryType::none), + address(""), + vcard(nullptr), + activeKeys(nullptr), + inactiveKeys(nullptr) {} Shared::Info::Info(const Shared::Info& other): type(other.type), - jid(other.jid), - editable(other.editable), - vcard(other.vcard), - activeKeys(other.activeKeys), - inactiveKeys(other.inactiveKeys) -{} + address(other.address), + vcard(nullptr), + activeKeys(nullptr), + inactiveKeys(nullptr) +{ + switch (type) { + case EntryType::none: + break; + case EntryType::contact: + case EntryType::ownAccount: + vcard = new VCard(other.getVCardRef()); + activeKeys = new std::list(other.getActiveKeysRef()); + inactiveKeys = new std::list(other.getInactiveKeysRef()); + break; + default: + throw 353; + } +} -Shared::Info::~Info() -{} +Shared::Info::~Info() { + turnIntoNone(); +} + +void Shared::Info::turnIntoNone() { + switch (type) { + case EntryType::none: + break; + case EntryType::contact: + case EntryType::ownAccount: + delete vcard; + vcard = nullptr; + delete activeKeys; + activeKeys = nullptr; + delete inactiveKeys; + inactiveKeys = nullptr; + break; + default: + break; + } + type = EntryType::none; +} + +void Shared::Info::turnIntoContact( + const Shared::VCard& crd, + const std::list& aks, + const std::list& iaks +) { + switch (type) { + case EntryType::none: + vcard = new VCard(crd); + activeKeys = new std::list(aks); + inactiveKeys = new std::list(iaks); + break; + case EntryType::contact: + case EntryType::ownAccount: + *vcard = crd; + *activeKeys = aks; + *inactiveKeys = iaks; + break; + default: + break; + } + + type = EntryType::contact; +} + +void Shared::Info::turnIntoContact( + Shared::VCard* crd, + std::list* aks, + std::list* iaks +) { + switch (type) { + case EntryType::contact: + case EntryType::ownAccount: + delete vcard; + delete activeKeys; + delete inactiveKeys; + [[fallthrough]]; + case EntryType::none: + vcard = crd; + activeKeys = aks; + inactiveKeys = iaks; + break; + default: + break; + } + + type = EntryType::contact; +} + +void Shared::Info::turnIntoOwnAccount( + const Shared::VCard& crd, + const std::list& aks, + const std::list& iaks +) { + switch (type) { + case EntryType::none: + vcard = new VCard(crd); + activeKeys = new std::list(aks); + inactiveKeys = new std::list(iaks); + break; + case EntryType::contact: + case EntryType::ownAccount: + *vcard = crd; + *activeKeys = aks; + *inactiveKeys = iaks; + break; + default: + break; + } + + type = EntryType::ownAccount; +} + +void Shared::Info::turnIntoOwnAccount( + Shared::VCard* crd, + std::list* aks, + std::list* iaks +) { + switch (type) { + case EntryType::contact: + case EntryType::ownAccount: + delete vcard; + delete activeKeys; + delete inactiveKeys; + [[fallthrough]]; + case EntryType::none: + vcard = crd; + activeKeys = aks; + inactiveKeys = iaks; + break; + default: + break; + } + + type = EntryType::ownAccount; +} + +void Shared::Info::setAddress(const QString& addr) { + address = addr;} + +QString Shared::Info::getAddress() const { + return address;} + +const QString& Shared::Info::getAddressRef() const { + return address;} + +Shared::EntryType Shared::Info::getType() const { + return type;} + +std::list & Shared::Info::getActiveKeysRef() { + switch (type) { + case EntryType::contact: + case EntryType::ownAccount: + return *activeKeys; + break; + default: + throw 354; + } +} + +const std::list & Shared::Info::getActiveKeysRef() const { + switch (type) { + case EntryType::contact: + case EntryType::ownAccount: + return *activeKeys; + break; + default: + throw 355; + } +} + +std::list* Shared::Info::getActiveKeys() { + switch (type) { + case EntryType::contact: + case EntryType::ownAccount: + return activeKeys; + break; + default: + throw 356; + } +} + +const std::list* Shared::Info::getActiveKeys() const { + switch (type) { + case EntryType::contact: + case EntryType::ownAccount: + return activeKeys; + break; + default: + throw 357; + } +} + +std::list & Shared::Info::getInactiveKeysRef() { + switch (type) { + case EntryType::contact: + case EntryType::ownAccount: + return *inactiveKeys; + break; + default: + throw 358; + } +} + +const std::list & Shared::Info::getInactiveKeysRef() const { + switch (type) { + case EntryType::contact: + case EntryType::ownAccount: + return *inactiveKeys; + break; + default: + throw 359; + } +} + +std::list* Shared::Info::getInactiveKeys() { + switch (type) { + case EntryType::contact: + case EntryType::ownAccount: + return inactiveKeys; + break; + default: + throw 360; + } +} + +const std::list* Shared::Info::getInactiveKeys() const { + switch (type) { + case EntryType::contact: + case EntryType::ownAccount: + return inactiveKeys; + break; + default: + throw 361; + } +} + +const Shared::VCard & Shared::Info::getVCardRef() const { + switch (type) { + case EntryType::contact: + case EntryType::ownAccount: + return *vcard; + break; + default: + throw 362; + } +} + +Shared::VCard & Shared::Info::getVCardRef() { + switch (type) { + case EntryType::contact: + case EntryType::ownAccount: + return *vcard; + break; + default: + throw 363; + } +} + +const Shared::VCard * Shared::Info::getVCard() const { + switch (type) { + case EntryType::contact: + case EntryType::ownAccount: + return vcard; + break; + default: + throw 364; + } +} + +Shared::VCard * Shared::Info::getVCard() { + switch (type) { + case EntryType::contact: + case EntryType::ownAccount: + return vcard; + break; + default: + throw 365; + } +} diff --git a/shared/info.h b/shared/info.h index 90248c3..b0f495e 100644 --- a/shared/info.h +++ b/shared/info.h @@ -34,16 +34,59 @@ namespace Shared { class Info { public: Info(); - Info(const QString& jid, EntryType = EntryType::contact, bool editable = false); + Info(const QString& address, EntryType = EntryType::none); Info(const Info& other); - ~Info(); + virtual ~Info(); + QString getAddress() const; + const QString& getAddressRef() const; + void setAddress(const QString& address); + + EntryType getType() const; + void turnIntoNone(); + void turnIntoContact( + const VCard& card = VCard(), + const std::list& activeKeys = {}, + const std::list& inactiveKeys = {} + ); + void turnIntoContact( + VCard* card = new VCard, + std::list* activeKeys = new std::list, + std::list* inactiveKeys = new std::list + ); + void turnIntoOwnAccount( + const VCard& card = VCard(), + const std::list& activeKeys = {}, + const std::list& inactiveKeys = {} + ); + void turnIntoOwnAccount( + VCard* card = new VCard, + std::list* activeKeys = new std::list, + std::list* inactiveKeys = new std::list + ); + + const VCard& getVCardRef() const; + VCard& getVCardRef(); + const VCard* getVCard() const; + VCard* getVCard(); + + const std::list& getActiveKeysRef() const; + std::list& getActiveKeysRef(); + const std::list* getActiveKeys() const; + std::list* getActiveKeys(); + + const std::list& getInactiveKeysRef() const; + std::list& getInactiveKeysRef(); + const std::list* getInactiveKeys() const; + std::list* getInactiveKeys(); + +private: EntryType type; - QString jid; - bool editable; - VCard vcard; - std::list activeKeys; - std::list inactiveKeys; + QString address; + + VCard* vcard; + std::list* activeKeys; + std::list* inactiveKeys; }; } diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 2c969da..ce759ac 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -412,7 +412,7 @@ void Squawk::onRosterContextMenu(const QPoint& point) { } void Squawk::responseInfo(const Shared::Info& info) { - std::map::const_iterator itr = infoWidgets.find(info.jid); + std::map::const_iterator itr = infoWidgets.find(info.getAddressRef()); if (itr != infoWidgets.end()) { itr->second->setData(info); itr->second->hideProgress(); diff --git a/ui/widgets/info/contactgeneral.cpp b/ui/widgets/info/contactgeneral.cpp index ccc6996..e469fcb 100644 --- a/ui/widgets/info/contactgeneral.cpp +++ b/ui/widgets/info/contactgeneral.cpp @@ -30,7 +30,6 @@ UI::ContactGeneral::ContactGeneral(QWidget* parent): currentAvatarType(Shared::Avatar::empty), currentAvatarPath(""), currentJid(""), - editable(false), avatarDiablog(nullptr) { m_ui->setupUi(this); @@ -74,6 +73,9 @@ void UI::ContactGeneral::setEditable(bool edit) { m_ui->avatarButton->setMenu(nullptr); } } + + m_ui->actionSetAvatar->setEnabled(edit); + m_ui->actionClearAvatar->setEnabled(false); //need to unlock it explicitly after the type of avatar is clear! } void UI::ContactGeneral::deleteAvatarDialog() { @@ -92,7 +94,7 @@ void UI::ContactGeneral::initializeActions() { connect(setAvatar, &QAction::triggered, this, &UI::ContactGeneral::onSetAvatar); connect(clearAvatar, &QAction::triggered, this, &UI::ContactGeneral::onClearAvatar); - setAvatar->setEnabled(editable); + setAvatar->setEnabled(false); clearAvatar->setEnabled(false); } @@ -138,7 +140,7 @@ void UI::ContactGeneral::updateAvatar() { qreal aspectRatio = w / h; m_ui->avatarButton->setIconSize(QSize(height * aspectRatio, height)); m_ui->avatarButton->setIcon(QIcon(currentAvatarPath)); - m_ui->actionClearAvatar->setEnabled(editable); + m_ui->actionClearAvatar->setEnabled(m_ui->actionSetAvatar->isEnabled()); //I assume that if set avatar is enabled then we can also clear break; } } diff --git a/ui/widgets/info/contactgeneral.h b/ui/widgets/info/contactgeneral.h index 2817e91..33b4c28 100644 --- a/ui/widgets/info/contactgeneral.h +++ b/ui/widgets/info/contactgeneral.h @@ -67,7 +67,6 @@ private: Shared::Avatar currentAvatarType; QString currentAvatarPath; QString currentJid; - bool editable; QFileDialog* avatarDiablog; static const std::set supportedTypes; diff --git a/ui/widgets/info/info.cpp b/ui/widgets/info/info.cpp index e951e97..a400f22 100644 --- a/ui/widgets/info/info.cpp +++ b/ui/widgets/info/info.cpp @@ -45,17 +45,19 @@ UI::Info::~Info() { void UI::Info::setData(const Shared::Info& info) { bool editable = false; - switch (info.type) { + switch (info.getType()) { case Shared::EntryType::ownAccount: editable = true; + [[fallthrough]]; case Shared::EntryType::contact: { - QDateTime receivingTime = info.vcard.getReceivingTime(); + const Shared::VCard card = info.getVCardRef(); + QDateTime receivingTime = card.getReceivingTime(); m_ui->receivingTimeLabel->show(); m_ui->receivingTimeLabel->setText(tr("Received %1 at %2").arg(receivingTime.date().toString()).arg(receivingTime.time().toString())); - initializeContactGeneral(jid, info.vcard, editable); - initializeContactContacts(jid, info.vcard, editable); - initializeDescription(info.vcard.getDescription(), editable); - type = info.type; + initializeContactGeneral(jid, card, editable); + initializeContactContacts(jid, card, editable); + initializeDescription(card.getDescription(), editable); + type = info.getType(); } break; default: @@ -99,11 +101,13 @@ void UI::Info::initializeButtonBox() { void UI::Info::onButtonBoxAccepted() { if (type == Shared::EntryType::ownAccount) { - Shared::Info info; - contactGeneral->fillVCard(info.vcard); - contactContacts->fillVCard(info.vcard); - info.vcard.setDescription(description->description()); + Shared::Info info(jid, Shared::EntryType::ownAccount); + Shared::VCard& card = info.getVCardRef(); + contactGeneral->fillVCard(card); + contactContacts->fillVCard(card); + card.setDescription(description->description()); emit saveInfo(info); + emit close(); } } From 77dd28b6006c301a679a208926835e516c3fa4fb Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 2 Mar 2023 21:17:06 +0300 Subject: [PATCH 091/137] some further work on omemo, far from done yet --- core/account.cpp | 2 ++ core/handlers/discoveryhandler.cpp | 6 ++--- core/handlers/omemohandler.cpp | 13 +++++++++++ core/handlers/omemohandler.h | 28 ++++++++++++++--------- core/handlers/trusthandler.cpp | 14 ++++++++++++ core/handlers/trusthandler.h | 36 ++++++++++++++++-------------- core/handlers/vcardhandler.cpp | 16 +++++++++++++ external/qxmpp | 2 +- shared/enums.h | 4 +++- shared/keyinfo.cpp | 2 +- shared/keyinfo.h | 2 +- ui/models/info/omemo/keys.cpp | 6 +++++ ui/models/info/omemo/keys.h | 1 + ui/widgets/info/info.cpp | 25 +++++++++++++++++++++ ui/widgets/info/info.h | 12 ++++++++++ ui/widgets/info/omemo/omemo.cpp | 22 +++++++++++++----- ui/widgets/info/omemo/omemo.h | 9 +++++++- ui/widgets/info/omemo/omemo.ui | 4 ++-- 18 files changed, 161 insertions(+), 43 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index 0970a6e..0a6c59c 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -106,6 +106,8 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& #ifdef WITH_OMEMO client.addExtension(tm); client.addExtension(om); + om->setSecurityPolicy(QXmpp::Toakafa); + om->setNewDeviceAutoSessionBuildingEnabled(true); if (oh->hasOwnDevice()) { QXmppTask future = om->load(); diff --git a/core/handlers/discoveryhandler.cpp b/core/handlers/discoveryhandler.cpp index 20e982c..977dda2 100644 --- a/core/handlers/discoveryhandler.cpp +++ b/core/handlers/discoveryhandler.cpp @@ -110,10 +110,10 @@ void Core::DiscoveryHandler::onInfoReceived(const QXmppDiscoveryIq& info) } } acc->setPepSupport(pepSupported ? Shared::Support::supported : Shared::Support::unsupported); - } else { - qDebug() << "Received info for account" << accName << "about" << from; + } else { QString node = info.queryNode(); if (!node.isEmpty()) { + qDebug() << "Received features and identities for account" << accName << "about" << from; QStringList feats = info.features(); std::set identities; std::set features(feats.begin(), feats.end()); @@ -135,7 +135,7 @@ void Core::DiscoveryHandler::onInfoReceived(const QXmppDiscoveryIq& info) } else { Contact* cont = acc->rh->getContact(from); if (cont != nullptr) { - qDebug() << "Received info for account" << accName << "about" << from; + qDebug() << "Received info for account" << accName << "about contact" << from; QList identities = info.identities(); bool pepSupported = false; for (const QXmppDiscoveryIq::Identity& identity : identities) { diff --git a/core/handlers/omemohandler.cpp b/core/handlers/omemohandler.cpp index 2b864e1..ce82a96 100644 --- a/core/handlers/omemohandler.cpp +++ b/core/handlers/omemohandler.cpp @@ -154,6 +154,19 @@ QXmppTask Core::OmemoHandler::resetAll() { return Core::makeReadyTask(); } +void Core::OmemoHandler::getDevices(const QString& jid, std::list& out) const { + QHash devs; + try { + devs = devices->getRecord(jid); + } catch (const DataBase::NotFound& error) {} + + for (QHash::const_iterator itr = devs.begin(), end = devs.end(); itr != end; ++itr) { + const Device& dev = itr.value(); + out.emplace_back(itr.key(), dev.keyId, dev.label, QDateTime(), Shared::TrustLevel::undecided, Shared::EncryptionProtocol::omemo2, false); + } +} + + QDataStream & operator >> (QDataStream& in, QXmppOmemoStorage::Device& device) { in >> device.label; in >> device.keyId; diff --git a/core/handlers/omemohandler.h b/core/handlers/omemohandler.h index 7783c04..050677c 100644 --- a/core/handlers/omemohandler.h +++ b/core/handlers/omemohandler.h @@ -17,9 +17,15 @@ #ifndef CORE_OMEMOHANDLER_H #define CORE_OMEMOHANDLER_H +#include +#include + #include #include +#include +#include + Q_DECLARE_METATYPE(QXmppOmemoStorage::OwnDevice); Q_DECLARE_METATYPE(QXmppOmemoStorage::Device); @@ -34,24 +40,26 @@ public: OmemoHandler(Account* account); ~OmemoHandler() override; - QXmppTask allData() override; + virtual QXmppTask allData() override; - QXmppTask setOwnDevice(const std::optional &device) override; + virtual QXmppTask setOwnDevice(const std::optional &device) override; - QXmppTask addSignedPreKeyPair(uint32_t keyId, const QXmppOmemoStorage::SignedPreKeyPair &keyPair) override; - QXmppTask removeSignedPreKeyPair(uint32_t keyId) override; + virtual QXmppTask addSignedPreKeyPair(uint32_t keyId, const QXmppOmemoStorage::SignedPreKeyPair &keyPair) override; + virtual QXmppTask removeSignedPreKeyPair(uint32_t keyId) override; - QXmppTask addPreKeyPairs(const QHash &keyPairs) override; - QXmppTask removePreKeyPair(uint32_t keyId) override; + virtual QXmppTask addPreKeyPairs(const QHash &keyPairs) override; + virtual QXmppTask removePreKeyPair(uint32_t keyId) override; - QXmppTask addDevice(const QString &jid, uint32_t deviceId, const Device &device) override; - QXmppTask removeDevice(const QString &jid, uint32_t deviceId) override; - QXmppTask removeDevices(const QString &jid) override; + virtual QXmppTask addDevice(const QString &jid, uint32_t deviceId, const Device &device) override; + virtual QXmppTask removeDevice(const QString &jid, uint32_t deviceId) override; + virtual QXmppTask removeDevices(const QString &jid) override; - QXmppTask resetAll() override; + virtual QXmppTask resetAll() override; bool hasOwnDevice(); + void getDevices(const QString& jid, std::list& out) const; + private: Account* acc; std::optional ownDevice; diff --git a/core/handlers/trusthandler.cpp b/core/handlers/trusthandler.cpp index 3ea6e47..50fbcbf 100644 --- a/core/handlers/trusthandler.cpp +++ b/core/handlers/trusthandler.cpp @@ -326,6 +326,20 @@ QXmppTask TrustHandler::setSecurityPolicy( return Core::makeReadyTask(); } +Core::TrustHandler::Keys Core::TrustHandler::getKeys(const QString& protocol, const QString& jid) const { + std::map::const_iterator itr = keysByProtocol.find(protocol); + if (itr != keysByProtocol.end()) { + try { + Keys map = itr->second->getRecord(jid); + return map; + } catch (const DataBase::NotFound& e) { + return Keys(); + } + } else { + return Keys(); + } +} + Shared::TrustLevel Core::TrustHandler::convert(Core::TrustHandler::TL level) { switch (level) { diff --git a/core/handlers/trusthandler.h b/core/handlers/trusthandler.h index e46c7b3..677a4f7 100644 --- a/core/handlers/trusthandler.h +++ b/core/handlers/trusthandler.h @@ -41,27 +41,29 @@ public: typedef std::map Keys; typedef DataBase::Cache KeyCache; - virtual QXmppTask resetAll(CSR encryption); - virtual QXmppTask trustLevel(CSR encryption, CSR keyOwnerJid, const QByteArray& keyId); - virtual QXmppTask setTrustLevel(CSR encryption, CLSR keyOwnerJids, TL oldTrustLevel, TL newTrustLevel); - virtual QXmppTask setTrustLevel(CSR encryption, const MultySB& keyIds, TL trustLevel); - virtual QXmppTask hasKey(CSR encryption, CSR keyOwnerJid, QXmpp::TrustLevels trustLevels); - virtual QXmppTask keys(CSR encryption, CLSR keyOwnerJids, QXmpp::TrustLevels trustLevels); - virtual QXmppTask> keys(CSR encryption, QXmpp::TrustLevels trustLevels); - virtual QXmppTask removeKeys(CSR encryption); - virtual QXmppTask removeKeys(CSR encryption, CSR keyOwnerJid); - virtual QXmppTask removeKeys(CSR encryption, CLBAR keyIds); - virtual QXmppTask addKeys(CSR encryption, CSR keyOwnerJid, CLBAR keyIds, TL trustLevel); - virtual QXmppTask ownKey(CSR encryption); - virtual QXmppTask resetOwnKey(CSR encryption); - virtual QXmppTask setOwnKey(CSR encryption, const QByteArray& keyId); - virtual QXmppTask securityPolicy(CSR encryption); - virtual QXmppTask resetSecurityPolicy(CSR encryption); - virtual QXmppTask setSecurityPolicy(CSR encryption, QXmpp::TrustSecurityPolicy securityPolicy); + virtual QXmppTask resetAll(CSR encryption) override; + virtual QXmppTask trustLevel(CSR encryption, CSR keyOwnerJid, const QByteArray& keyId) override; + virtual QXmppTask setTrustLevel(CSR encryption, CLSR keyOwnerJids, TL oldTrustLevel, TL newTrustLevel) override; + virtual QXmppTask setTrustLevel(CSR encryption, const MultySB& keyIds, TL trustLevel) override; + virtual QXmppTask hasKey(CSR encryption, CSR keyOwnerJid, QXmpp::TrustLevels trustLevels) override; + virtual QXmppTask keys(CSR encryption, CLSR keyOwnerJids, QXmpp::TrustLevels trustLevels) override; + virtual QXmppTask> keys(CSR encryption, QXmpp::TrustLevels trustLevels) override; + virtual QXmppTask removeKeys(CSR encryption) override; + virtual QXmppTask removeKeys(CSR encryption, CSR keyOwnerJid) override; + virtual QXmppTask removeKeys(CSR encryption, CLBAR keyIds) override; + virtual QXmppTask addKeys(CSR encryption, CSR keyOwnerJid, CLBAR keyIds, TL trustLevel) override; + virtual QXmppTask ownKey(CSR encryption) override; + virtual QXmppTask resetOwnKey(CSR encryption) override; + virtual QXmppTask setOwnKey(CSR encryption, const QByteArray& keyId) override; + virtual QXmppTask securityPolicy(CSR encryption) override; + virtual QXmppTask resetSecurityPolicy(CSR encryption) override; + virtual QXmppTask setSecurityPolicy(CSR encryption, QXmpp::TrustSecurityPolicy securityPolicy) override; static TL convert(Shared::TrustLevel level); static Shared::TrustLevel convert(TL level); + Keys getKeys(const QString& protocol, const QString& jid) const; + private: KeyCache* createNewCache(const QString& encryption); KeyCache* getCache(const QString& encryption); diff --git a/core/handlers/vcardhandler.cpp b/core/handlers/vcardhandler.cpp index 33c9cdb..6c5cb5b 100644 --- a/core/handlers/vcardhandler.cpp +++ b/core/handlers/vcardhandler.cpp @@ -17,6 +17,8 @@ #include "vcardhandler.h" #include "core/account.h" +constexpr const char* ns_omemo_2 = "urn:xmpp:omemo:2"; + Core::VCardHandler::VCardHandler(Account* account): QObject(), acc(account), @@ -102,6 +104,20 @@ void Core::VCardHandler::onVCardReceived(const QXmppVCardIq& card) { Shared::Info info(jid, Shared::EntryType::contact); item->handleResponseVCard(card, resource, info.getVCardRef()); +#ifdef WITH_OMEMO + std::list& aks = info.getActiveKeysRef(); + acc->oh->getDevices(jid, aks); + std::map trustLevels = acc->th->getKeys(ns_omemo_2, jid); + + qDebug() << "OMEMO info for " << jid << " devices:" << aks.size() << ", trustLevels:" << trustLevels.size(); + for (Shared::KeyInfo& key : aks) { + std::map::const_iterator itr = trustLevels.find(key.fingerPrint); + if (itr != trustLevels.end()) { + key.trustLevel = itr->second; + qDebug() << "Found a trust level for a device!"; + } + } +#endif emit acc->infoReady(info); } diff --git a/external/qxmpp b/external/qxmpp index d2c2acd..9d57624 160000 --- a/external/qxmpp +++ b/external/qxmpp @@ -1 +1 @@ -Subproject commit d2c2acd4848f815d0dc3d108f8bc306f9015fc89 +Subproject commit 9d5762499fbddb3dd1ed8eeca16f9db70adc27d0 diff --git a/shared/enums.h b/shared/enums.h index 46d954a..aff22b9 100644 --- a/shared/enums.h +++ b/shared/enums.h @@ -160,7 +160,9 @@ static const TrustLevel TrustLevelHighest = TrustLevel::undecided; static const TrustLevel TrustLevelLowest = TrustLevel::authenticated; enum class EncryptionProtocol { - omemo + omemo, + omemo1, + omemo2 }; Q_ENUM_NS(EncryptionProtocol) diff --git a/shared/keyinfo.cpp b/shared/keyinfo.cpp index db3ce2b..f7d9e90 100644 --- a/shared/keyinfo.cpp +++ b/shared/keyinfo.cpp @@ -43,7 +43,7 @@ Shared::KeyInfo::KeyInfo(): label(), lastInteraction(), trustLevel(TrustLevel::undecided), - protocol(EncryptionProtocol::omemo), + protocol(EncryptionProtocol::omemo2), currentDevice(false) { } diff --git a/shared/keyinfo.h b/shared/keyinfo.h index 1befbc9..8d7db0a 100644 --- a/shared/keyinfo.h +++ b/shared/keyinfo.h @@ -36,7 +36,7 @@ public: const QString& label, const QDateTime& lastInteraction, TrustLevel trustLevel, - EncryptionProtocol protocol = EncryptionProtocol::omemo, + EncryptionProtocol protocol = EncryptionProtocol::omemo2, bool currentDevice = false ); KeyInfo(); diff --git a/ui/models/info/omemo/keys.cpp b/ui/models/info/omemo/keys.cpp index fa753c0..e4dc8d2 100644 --- a/ui/models/info/omemo/keys.cpp +++ b/ui/models/info/omemo/keys.cpp @@ -134,5 +134,11 @@ void Models::Keys::setTrustLevel(int row, Shared::TrustLevel level) { dataChanged(index, index, {Keys::Dirty}); } +void Models::Keys::clear() { + beginResetModel(); + keys.clear(); + modified.clear(); + endResetModel(); +} diff --git a/ui/models/info/omemo/keys.h b/ui/models/info/omemo/keys.h index 2710bab..49948a2 100644 --- a/ui/models/info/omemo/keys.h +++ b/ui/models/info/omemo/keys.h @@ -33,6 +33,7 @@ public: ~Keys(); void addKey(const Shared::KeyInfo& info); + void clear(); QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; diff --git a/ui/widgets/info/info.cpp b/ui/widgets/info/info.cpp index a400f22..6a1105d 100644 --- a/ui/widgets/info/info.cpp +++ b/ui/widgets/info/info.cpp @@ -24,6 +24,9 @@ UI::Info::Info(const QString& p_jid, QWidget* parent): m_ui(new Ui::Info()), contactGeneral(nullptr), contactContacts(nullptr), +#ifdef WITH_OMEMO + omemo(nullptr), +#endif description(nullptr), overlay(new QWidget()), progress(new Progress(100)), @@ -57,6 +60,9 @@ void UI::Info::setData(const Shared::Info& info) { initializeContactGeneral(jid, card, editable); initializeContactContacts(jid, card, editable); initializeDescription(card.getDescription(), editable); +#ifdef WITH_OMEMO + initializeOmemo(info.getActiveKeysRef()); +#endif type = info.getType(); } break; @@ -170,5 +176,24 @@ void UI::Info::clear() { description->deleteLater(); description = nullptr; } + +#ifdef WITH_OMEMO + if (omemo != nullptr) { + omemo->deleteLater(); + omemo = nullptr; + } +#endif + type = Shared::EntryType::none; } + +#ifdef WITH_OMEMO +void UI::Info::initializeOmemo(const std::list& keys) { + if (omemo == nullptr) { + omemo = new Omemo(); + m_ui->tabWidget->addTab(omemo, omemo->title()); + } + omemo->setData(keys); +} + +#endif diff --git a/ui/widgets/info/info.h b/ui/widgets/info/info.h index 14829e7..10bc063 100644 --- a/ui/widgets/info/info.h +++ b/ui/widgets/info/info.h @@ -17,6 +17,8 @@ #ifndef UI_WIDGETS_INFO_H #define UI_WIDGETS_INFO_H +#include + #include #include #include @@ -30,6 +32,10 @@ #include "contactgeneral.h" #include "contactcontacts.h" #include "description.h" +#ifdef WITH_OMEMO +#include "omemo/omemo.h" +#endif + namespace UI { namespace Ui @@ -58,6 +64,9 @@ private: void initializeContactGeneral(const QString& jid, const Shared::VCard& card, bool editable); void initializeContactContacts(const QString& jid, const Shared::VCard& card, bool editable); void initializeDescription(const QString& description, bool editable); +#ifdef WITH_OMEMO + void initializeOmemo(const std::list& keys); +#endif void initializeOverlay(); void initializeButtonBox(); void clear(); @@ -68,6 +77,9 @@ private: QScopedPointer m_ui; ContactGeneral* contactGeneral; ContactContacts* contactContacts; +#ifdef WITH_OMEMO + Omemo* omemo; +#endif Description* description; QWidget* overlay; Progress* progress; diff --git a/ui/widgets/info/omemo/omemo.cpp b/ui/widgets/info/omemo/omemo.cpp index 16414ee..d3722fe 100644 --- a/ui/widgets/info/omemo/omemo.cpp +++ b/ui/widgets/info/omemo/omemo.cpp @@ -20,7 +20,7 @@ #include constexpr uint8_t fingerprintLength = 32; -Omemo::Omemo(QWidget* parent): +UI::Omemo::Omemo(QWidget* parent): QWidget(parent), m_ui(new Ui::Omemo()), keysDelegate(), @@ -31,8 +31,6 @@ Omemo::Omemo(QWidget* parent): { m_ui->setupUi(this); - generateMockData(); - m_ui->keysView->setItemDelegate(&keysDelegate); m_ui->keysView->setModel(&keysModel); m_ui->unusedKeysView->setItemDelegate(&unusedKeysDelegate); @@ -42,12 +40,12 @@ Omemo::Omemo(QWidget* parent): connect(m_ui->keysView, &QWidget::customContextMenuRequested, this, &Omemo::onActiveKeysContextMenu); } -Omemo::~Omemo() +UI::Omemo::~Omemo() { contextMenu->deleteLater(); } -void Omemo::generateMockData() { +void UI::Omemo::generateMockData() { std::random_device rd; std::uniform_int_distribution dist(CHAR_MIN, CHAR_MAX); for (int i = 0; i < 5; ++i) { @@ -67,7 +65,19 @@ void Omemo::generateMockData() { } } -void Omemo::onActiveKeysContextMenu(const QPoint& pos) { +void UI::Omemo::setData(const std::list& keys) { + keysModel.clear(); + unusedKeysModel.clear(); + for (const Shared::KeyInfo& key : keys) { + keysModel.addKey(key); + } +} + +const QString UI::Omemo::title() const { + return m_ui->OMEMOHeading->text();} + + +void UI::Omemo::onActiveKeysContextMenu(const QPoint& pos) { contextMenu->clear(); QModelIndex index = m_ui->keysView->indexAt(pos); if (index.isValid()) { diff --git a/ui/widgets/info/omemo/omemo.h b/ui/widgets/info/omemo/omemo.h index 7c6b5a1..a279492 100644 --- a/ui/widgets/info/omemo/omemo.h +++ b/ui/widgets/info/omemo/omemo.h @@ -17,6 +17,8 @@ #ifndef VCARD_OMEMO_H #define VCARD_OMEMO_H +#include + #include #include #include @@ -24,7 +26,9 @@ #include "ui/models/info/omemo/keys.h" #include "keydelegate.h" #include "shared/icons.h" +#include "shared/keyinfo.h" +namespace UI { namespace Ui { class Omemo; @@ -36,6 +40,9 @@ public: Omemo(QWidget* parent = nullptr); ~Omemo(); + void setData(const std::list& keys); + const QString title() const; + private slots: void onActiveKeysContextMenu(const QPoint& pos); @@ -50,5 +57,5 @@ private: Models::Keys unusedKeysModel; QMenu* contextMenu; }; - +} #endif // VCARD_OMEMO_H diff --git a/ui/widgets/info/omemo/omemo.ui b/ui/widgets/info/omemo/omemo.ui index d9f55b1..745b981 100644 --- a/ui/widgets/info/omemo/omemo.ui +++ b/ui/widgets/info/omemo/omemo.ui @@ -1,7 +1,7 @@ - Omemo - + UI::Omemo + 0 From 2d8f32c257a001d74fcac87c11614858ec664cc4 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 4 Mar 2023 00:27:12 +0300 Subject: [PATCH 092/137] some ideas over delay manager --- core/CMakeLists.txt | 38 +++--- core/components/CMakeLists.txt | 20 ++- core/components/delaymanager.cpp | 136 +++++++++++++++++++++ core/components/delaymanager.h | 71 +++++++++++ core/handlers/CMakeLists.txt | 22 ++-- core/passwordStorageEngines/CMakeLists.txt | 2 +- ui/widgets/info/omemo/CMakeLists.txt | 18 ++- 7 files changed, 276 insertions(+), 31 deletions(-) create mode 100644 core/components/delaymanager.cpp create mode 100644 core/components/delaymanager.h diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index d3327c9..b971a88 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -3,22 +3,30 @@ if(WIN32) set(SIGNALCATCHER_SOURCE signalcatcher_win32.cpp) endif(WIN32) +set(SOURCE_FILES + account.cpp + adapterfunctions.cpp + conference.cpp + contact.cpp + rosteritem.cpp + ${SIGNALCATCHER_SOURCE} + squawk.cpp +) + +set(HEADER_FILES + account.h + adapterfunctions.h + conference.h + contact.h + rosteritem.h + signalcatcher.h + squawk.h +) + target_sources(squawk PRIVATE - account.cpp - account.h - adapterfunctions.cpp - adapterfunctions.h - conference.cpp - conference.h - contact.cpp - contact.h - rosteritem.cpp - rosteritem.h - ${SIGNALCATCHER_SOURCE} - signalcatcher.h - squawk.cpp - squawk.h - ) + ${SOURCE_FILES} + ${HEADER_FILES} +) target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS}) diff --git a/core/components/CMakeLists.txt b/core/components/CMakeLists.txt index 5faf837..0e26037 100644 --- a/core/components/CMakeLists.txt +++ b/core/components/CMakeLists.txt @@ -1,6 +1,16 @@ -target_sources(squawk PRIVATE - networkaccess.cpp - networkaccess.h - clientcache.cpp - clientcache.h +set(SOURCE_FILES + networkaccess.cpp + clientcache.cpp + delaymanager.cpp +) + +set(HEADER_FILES + networkaccess.h + clientcache.h + delaymanager.h +) + +target_sources(squawk PRIVATE + ${SOURCE_FILES} + ${HEADER_FILES} ) diff --git a/core/components/delaymanager.cpp b/core/components/delaymanager.cpp new file mode 100644 index 0000000..627eab8 --- /dev/null +++ b/core/components/delaymanager.cpp @@ -0,0 +1,136 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "delaymanager.h" + +Core::DelayManager::DelayManager(uint16_t mpj, QObject* parent) : + QObject(parent), + maxParallelJobs(mpj), + nextJobId(0), + scheduledJobs(), + runningJobs(), + pendingVCards(), + requestedVCards() +{ +} + +Core::DelayManager::~DelayManager() {} + +void Core::DelayManager::requestInfo(const QString& jid) { + bool needToRequest = false; + std::pair::const_iterator, bool> result = pendingVCards.insert(jid); + if (result.second) { //if there is a clear evidence that we have not alredy been asked to request a VCard - just request it + needToRequest = true; + } else { + std::map::const_iterator itr = requestedVCards.find(jid); + if (itr != requestedVCards.end()) { //first check if the card is already requested, and if it is make sure we reply to user after we receive it + runningJobs[itr->second].first = TaskType::infoForUser; + } else { + needToRequest = true; + for (Job& job : scheduledJobs) { //looks like we need to manually check all the scheduled job and find the one with that jid there + if (job.first == TaskType::cardInternal || job.first == TaskType::infoForUser) { //to make sure we reply to user after we receive it + QString* jobJid = static_cast(job.second); + if (*jobJid == jid) { + needToRequest = false; + job.first = TaskType::infoForUser; + break; + } + } + } + if (needToRequest) { + throw 8573; //something went terribly wrong here, the state is not correct; + } + } + } + + if (needToRequest) + scheduleJob(TaskType::infoForUser, new QString(jid)); +} + +void Core::DelayManager::requestVCard(const QString& jid) { + std::pair::const_iterator, bool> result = pendingVCards.insert(jid); + if (result.second) + scheduleJob(TaskType::cardInternal, new QString(jid)); +} + +void Core::DelayManager::scheduleJob(Core::DelayManager::TaskType type, void* data) { + if (runningJobs.size() < maxParallelJobs) { + uint16_t currentJobId = nextJobId++; + runningJobs.emplace(currentJobId, std::make_pair(type, data)); + switch (type) { + case TaskType::cardInternal: + case TaskType::infoForUser: { + QString* jid = static_cast(data); + requestedVCards.emplace(*jid, currentJobId); + emit requestVCard(*jid); + } + break; + } + } else { + scheduledJobs.emplace_back(type, data); + } +} + +void Core::DelayManager::executeJob(Core::DelayManager::Job job) { + uint16_t currentJobId = nextJobId++; + runningJobs.emplace(currentJobId, job); + switch (job.first) { + case TaskType::cardInternal: + case TaskType::infoForUser: { + QString* jid = static_cast(job.second); + requestedVCards.emplace(*jid, currentJobId); + emit requestVCard(*jid); + } + break; + } +} + + +void Core::DelayManager::jobIsDone(uint16_t jobId) { + std::map::const_iterator itr = runningJobs.find(jobId); + if (itr == runningJobs.end()) { + throw 8574; //not supposed to happen, never + } + runningJobs.erase(itr); + if (scheduledJobs.size() > 0) { + Job job = scheduledJobs.front(); + scheduledJobs.pop_front(); + executeJob(job); + } +} + + +void Core::DelayManager::receivedVCard(const QString& jid, const Shared::VCard& card) { + std::map::const_iterator cardItr = requestedVCards.find(jid); + if (cardItr == requestedVCards.end()) { + throw 8575; //never supposed to happen, the state is not correct; + } else { + uint16_t jobId = cardItr->second; + requestedVCards.erase(cardItr); + pendingVCards.erase(jid); + const Job& job = runningJobs[jobId]; + switch (job.first) { + case TaskType::cardInternal: + jobIsDone(jobId); + break; + case TaskType::infoForUser: + //all that stuff with further requesting + break; + default: + throw 8576; + } + } +} diff --git a/core/components/delaymanager.h b/core/components/delaymanager.h new file mode 100644 index 0000000..f34b00e --- /dev/null +++ b/core/components/delaymanager.h @@ -0,0 +1,71 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 CORE_DELAYMANAGER_H +#define CORE_DELAYMANAGER_H + +#include +#include + +#include +#include + +#include +#include + +namespace Core { + +class DelayManager : public QObject +{ + Q_OBJECT + + enum class TaskType { + cardInternal, + infoForUser + } +public: + DelayManager(uint16_t maxParallelJobs = 5, QObject* parent = nullptr); + ~DelayManager(); + + void requestVCard(const QString& jid); + void requestInfo(const QString& jid); + +signals: + void requestVCard(const QString& jid); + +public slots: + void receivedVCard(const QString& jid, const Shared::VCard& card); + +private: + typedef std::pair Job; + + void scheduleJob(TaskType type, void* data); + void executeJob(Job job); + void jobIsDone(uint16_t jobId); + +private: + uint16_t maxParallelJobs; + uint16_t nextJobId; + std::list scheduledJobs; + std::map runningJobs; + + std::set pendingVCards; + std::map requestedVCards; +}; + +} + +#endif // CORE_DELAYMANAGER_H diff --git a/core/handlers/CMakeLists.txt b/core/handlers/CMakeLists.txt index 746a36f..11a902d 100644 --- a/core/handlers/CMakeLists.txt +++ b/core/handlers/CMakeLists.txt @@ -1,14 +1,22 @@ -target_sources(squawk PRIVATE +set(SOURCE_FILES messagehandler.cpp - messagehandler.h rosterhandler.cpp - rosterhandler.h vcardhandler.cpp - vcardhandler.h discoveryhandler.cpp - discoveryhandler.h omemohandler.cpp - omemohandler.h trusthandler.cpp +) + +set(HEADER_FILES + messagehandler.h + rosterhandler.h + vcardhandler.h + discoveryhandler.h + omemohandler.h trusthandler.h - ) +) + +target_sources(squawk PRIVATE + ${SOURCE_FILES} + ${HEADER_FILES} +) diff --git a/core/passwordStorageEngines/CMakeLists.txt b/core/passwordStorageEngines/CMakeLists.txt index 4da3873..2afda3f 100644 --- a/core/passwordStorageEngines/CMakeLists.txt +++ b/core/passwordStorageEngines/CMakeLists.txt @@ -2,7 +2,7 @@ if (WITH_KWALLET) target_sources(squawk PRIVATE kwallet.cpp kwallet.h - ) + ) add_subdirectory(wrappers) target_include_directories(squawk PRIVATE $) diff --git a/ui/widgets/info/omemo/CMakeLists.txt b/ui/widgets/info/omemo/CMakeLists.txt index f1dc4ed..4c76900 100644 --- a/ui/widgets/info/omemo/CMakeLists.txt +++ b/ui/widgets/info/omemo/CMakeLists.txt @@ -1,7 +1,19 @@ -target_sources(squawk PRIVATE +set(SOURCE_FILES omemo.cpp - omemo.h - omemo.ui keydelegate.cpp +) + +set(UI_FILES + omemo.ui +) + +set(HEADER_FILES + omemo.h keydelegate.h ) + +target_sources(squawk PRIVATE + ${SOURCE_FILES} + ${UI_FILES} + ${HEADER_FILES} +) From 99fd001292b4841564504d487137875e60495b8b Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 5 Mar 2023 01:36:53 +0300 Subject: [PATCH 093/137] some more thinking about delay manager --- core/CMakeLists.txt | 1 + core/components/CMakeLists.txt | 2 - core/components/delaymanager.cpp | 136 -------------- core/components/delaymanager.h | 71 ------- core/delayManager/CMakeLists.txt | 22 +++ core/delayManager/cardinternal.cpp | 32 ++++ core/delayManager/cardinternal.h | 41 +++++ core/delayManager/delaymanager.cpp | 255 ++++++++++++++++++++++++++ core/delayManager/delaymanager.h | 116 ++++++++++++ core/delayManager/infoforuser.cpp | 26 +++ core/delayManager/infoforuser.h | 32 ++++ core/delayManager/job.cpp | 32 ++++ core/delayManager/job.h | 50 +++++ core/delayManager/owncardinternal.cpp | 30 +++ core/delayManager/owncardinternal.h | 35 ++++ core/delayManager/owninfoforuser.cpp | 25 +++ core/delayManager/owninfoforuser.h | 32 ++++ 17 files changed, 729 insertions(+), 209 deletions(-) delete mode 100644 core/components/delaymanager.cpp delete mode 100644 core/components/delaymanager.h create mode 100644 core/delayManager/CMakeLists.txt create mode 100644 core/delayManager/cardinternal.cpp create mode 100644 core/delayManager/cardinternal.h create mode 100644 core/delayManager/delaymanager.cpp create mode 100644 core/delayManager/delaymanager.h create mode 100644 core/delayManager/infoforuser.cpp create mode 100644 core/delayManager/infoforuser.h create mode 100644 core/delayManager/job.cpp create mode 100644 core/delayManager/job.h create mode 100644 core/delayManager/owncardinternal.cpp create mode 100644 core/delayManager/owncardinternal.h create mode 100644 core/delayManager/owninfoforuser.cpp create mode 100644 core/delayManager/owninfoforuser.h diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index b971a88..01c6d8f 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -34,3 +34,4 @@ add_subdirectory(handlers) add_subdirectory(storage) add_subdirectory(passwordStorageEngines) add_subdirectory(components) +add_subdirectory(delayManager) diff --git a/core/components/CMakeLists.txt b/core/components/CMakeLists.txt index 0e26037..b78794a 100644 --- a/core/components/CMakeLists.txt +++ b/core/components/CMakeLists.txt @@ -1,13 +1,11 @@ set(SOURCE_FILES networkaccess.cpp clientcache.cpp - delaymanager.cpp ) set(HEADER_FILES networkaccess.h clientcache.h - delaymanager.h ) target_sources(squawk PRIVATE diff --git a/core/components/delaymanager.cpp b/core/components/delaymanager.cpp deleted file mode 100644 index 627eab8..0000000 --- a/core/components/delaymanager.cpp +++ /dev/null @@ -1,136 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 "delaymanager.h" - -Core::DelayManager::DelayManager(uint16_t mpj, QObject* parent) : - QObject(parent), - maxParallelJobs(mpj), - nextJobId(0), - scheduledJobs(), - runningJobs(), - pendingVCards(), - requestedVCards() -{ -} - -Core::DelayManager::~DelayManager() {} - -void Core::DelayManager::requestInfo(const QString& jid) { - bool needToRequest = false; - std::pair::const_iterator, bool> result = pendingVCards.insert(jid); - if (result.second) { //if there is a clear evidence that we have not alredy been asked to request a VCard - just request it - needToRequest = true; - } else { - std::map::const_iterator itr = requestedVCards.find(jid); - if (itr != requestedVCards.end()) { //first check if the card is already requested, and if it is make sure we reply to user after we receive it - runningJobs[itr->second].first = TaskType::infoForUser; - } else { - needToRequest = true; - for (Job& job : scheduledJobs) { //looks like we need to manually check all the scheduled job and find the one with that jid there - if (job.first == TaskType::cardInternal || job.first == TaskType::infoForUser) { //to make sure we reply to user after we receive it - QString* jobJid = static_cast(job.second); - if (*jobJid == jid) { - needToRequest = false; - job.first = TaskType::infoForUser; - break; - } - } - } - if (needToRequest) { - throw 8573; //something went terribly wrong here, the state is not correct; - } - } - } - - if (needToRequest) - scheduleJob(TaskType::infoForUser, new QString(jid)); -} - -void Core::DelayManager::requestVCard(const QString& jid) { - std::pair::const_iterator, bool> result = pendingVCards.insert(jid); - if (result.second) - scheduleJob(TaskType::cardInternal, new QString(jid)); -} - -void Core::DelayManager::scheduleJob(Core::DelayManager::TaskType type, void* data) { - if (runningJobs.size() < maxParallelJobs) { - uint16_t currentJobId = nextJobId++; - runningJobs.emplace(currentJobId, std::make_pair(type, data)); - switch (type) { - case TaskType::cardInternal: - case TaskType::infoForUser: { - QString* jid = static_cast(data); - requestedVCards.emplace(*jid, currentJobId); - emit requestVCard(*jid); - } - break; - } - } else { - scheduledJobs.emplace_back(type, data); - } -} - -void Core::DelayManager::executeJob(Core::DelayManager::Job job) { - uint16_t currentJobId = nextJobId++; - runningJobs.emplace(currentJobId, job); - switch (job.first) { - case TaskType::cardInternal: - case TaskType::infoForUser: { - QString* jid = static_cast(job.second); - requestedVCards.emplace(*jid, currentJobId); - emit requestVCard(*jid); - } - break; - } -} - - -void Core::DelayManager::jobIsDone(uint16_t jobId) { - std::map::const_iterator itr = runningJobs.find(jobId); - if (itr == runningJobs.end()) { - throw 8574; //not supposed to happen, never - } - runningJobs.erase(itr); - if (scheduledJobs.size() > 0) { - Job job = scheduledJobs.front(); - scheduledJobs.pop_front(); - executeJob(job); - } -} - - -void Core::DelayManager::receivedVCard(const QString& jid, const Shared::VCard& card) { - std::map::const_iterator cardItr = requestedVCards.find(jid); - if (cardItr == requestedVCards.end()) { - throw 8575; //never supposed to happen, the state is not correct; - } else { - uint16_t jobId = cardItr->second; - requestedVCards.erase(cardItr); - pendingVCards.erase(jid); - const Job& job = runningJobs[jobId]; - switch (job.first) { - case TaskType::cardInternal: - jobIsDone(jobId); - break; - case TaskType::infoForUser: - //all that stuff with further requesting - break; - default: - throw 8576; - } - } -} diff --git a/core/components/delaymanager.h b/core/components/delaymanager.h deleted file mode 100644 index f34b00e..0000000 --- a/core/components/delaymanager.h +++ /dev/null @@ -1,71 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 CORE_DELAYMANAGER_H -#define CORE_DELAYMANAGER_H - -#include -#include - -#include -#include - -#include -#include - -namespace Core { - -class DelayManager : public QObject -{ - Q_OBJECT - - enum class TaskType { - cardInternal, - infoForUser - } -public: - DelayManager(uint16_t maxParallelJobs = 5, QObject* parent = nullptr); - ~DelayManager(); - - void requestVCard(const QString& jid); - void requestInfo(const QString& jid); - -signals: - void requestVCard(const QString& jid); - -public slots: - void receivedVCard(const QString& jid, const Shared::VCard& card); - -private: - typedef std::pair Job; - - void scheduleJob(TaskType type, void* data); - void executeJob(Job job); - void jobIsDone(uint16_t jobId); - -private: - uint16_t maxParallelJobs; - uint16_t nextJobId; - std::list scheduledJobs; - std::map runningJobs; - - std::set pendingVCards; - std::map requestedVCards; -}; - -} - -#endif // CORE_DELAYMANAGER_H diff --git a/core/delayManager/CMakeLists.txt b/core/delayManager/CMakeLists.txt new file mode 100644 index 0000000..f9ccb57 --- /dev/null +++ b/core/delayManager/CMakeLists.txt @@ -0,0 +1,22 @@ +set(SOURCE_FILES + delaymanager.cpp + job.cpp + cardinternal.cpp + infoforuser.cpp + owncardinternal.cpp + owninfoforuser.cpp +) + +set(HEADER_FILES + delaymanager.h + job.h + cardinternal.h + infoforuser.h + owncardinternal.h + owninfoforuser.h +) + +target_sources(squawk PRIVATE + ${SOURCE_FILES} + ${HEADER_FILES} +) diff --git a/core/delayManager/cardinternal.cpp b/core/delayManager/cardinternal.cpp new file mode 100644 index 0000000..c7a599a --- /dev/null +++ b/core/delayManager/cardinternal.cpp @@ -0,0 +1,32 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "cardinternal.h" + +Core::CardInternal::CardInternal(Job::Id p_id, const QString& p_jid) : + Job(p_id, Type::cardInternal), + jid(p_id) +{} + +Core::CardInternal::CardInternal(Job::Id p_id, const QString& p_jid, Job::Type p_type) : + Job(p_id, p_type), + jid(p_id) +{} + +Core::CardInternal::CardInternal(const Core::CardInternal& other) : + Job(other), + jid(other.jid) +{} diff --git a/core/delayManager/cardinternal.h b/core/delayManager/cardinternal.h new file mode 100644 index 0000000..a95f0ba --- /dev/null +++ b/core/delayManager/cardinternal.h @@ -0,0 +1,41 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 CORE_CARDINTERNAL_H +#define CORE_CARDINTERNAL_H + + +#include + +#include "job.h" + +namespace Core { + +class CardInternal : public Job { +protected: + CardInternal(Job::Id id, const QString& jid, Job::Type type); + +public: + CardInternal(Job::Id id, const QString& jid); + CardInternal(const CardInternal& other); + + const QString jid; + +}; + +} + +#endif // CORE_CARDINTERNAL_H diff --git a/core/delayManager/delaymanager.cpp b/core/delayManager/delaymanager.cpp new file mode 100644 index 0000000..6ebc85d --- /dev/null +++ b/core/delayManager/delaymanager.cpp @@ -0,0 +1,255 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "delaymanager.h" + +#include "cardinternal.h" +#include "infoforuser.h" +#include "owncardinternal.h" +#include "owninfoforuser.h" + +Core::DelayManager::DelayManager(Job::Id mpj, QObject* parent) : + QObject(parent), + maxParallelJobs(mpj), + nextJobId(1), + scheduledJobs(), + scheduledJobsById(scheduledJobs.get()), + jobSequence(scheduledJobs.get()), + runningJobs(), + ownVCardJobId(0), + ownInfoJobId(0), + scheduledVCards(), + requestedVCards(), + requestedBundles() +{ +} + +Core::DelayManager::~DelayManager() {} + +Core::Job::Id Core::DelayManager::getNextJobId() { + Job::Id id = nextJobId++; + if (id == 0) + id = nextJobId++; + + return id; +} + +void Core::DelayManager::getInfo(const QString& jid) { + Job* job = nullptr; +#ifdef WITH_OMEMO + std::map::const_iterator bitr = requestedBundles.find(jid); + if (bitr != requestedBundles.end()) + job = runningJobs.at(bitr->second); + else +#endif + job = getVCardJob(jid); + + if (job != nullptr) { + if (job->getType() == Job::Type::cardInternal) + replaceJob(new InfoForUser(job->id, jid)); + } else + scheduleJob(new InfoForUser(getNextJobId(), jid)); +} + +void Core::DelayManager::getOwnInfo() { + if (ownInfoJobId == 0) { + if (ownVCardJobId != 0) + replaceJob(new OwnInfoForUser(ownVCardJobId)); + else + scheduleJob(new OwnInfoForUser(getNextJobId())); + } +} + +void Core::DelayManager::getVCard(const QString& jid) { + Job* job = getVCardJob(jid); + if (job == nullptr) + scheduleJob(new CardInternal(getNextJobId(), jid)); +} + +void Core::DelayManager::getOwnVCard() { + if (ownInfoJobId == 0) + scheduleJob(new OwnCardInternal(getNextJobId())); +} + +Core::Job* Core::DelayManager::getVCardJob(const QString& jid) { + Job* job = nullptr; + std::map::const_iterator sitr = scheduledVCards.find(jid); + if (sitr == scheduledVCards.end()) { + std::map::const_iterator ritr = requestedVCards.find(jid); + if (ritr != requestedVCards.end()) + job = runningJobs.at(ritr->second); + } else { + job = *(scheduledJobsById.find(sitr->second)); + } + + return job; +} +void Core::DelayManager::preScheduleJob(Core::Job* job) { + switch (job->getType()) { + case Job::Type::cardInternal: + scheduledVCards.emplace(static_cast(job)->jid, job->id); + break; + case Job::Type::ownCardInternal: + ownVCardJobId = job->id; + break; + case Job::Type::infoForUser: + scheduledVCards.emplace(static_cast(job)->jid, job->id); + break; + case Job::Type::ownInfoForUser: + ownVCardJobId = job->id; + ownInfoJobId = job->id; + break; + } +} + +void Core::DelayManager::scheduleJob(Core::Job* job) { + preScheduleJob(job); + if (runningJobs.size() < maxParallelJobs) { + executeJob(job); + } else { + scheduledJobs.push_back(job); + } +} + +void Core::DelayManager::preExecuteJob(Core::Job* job) { + switch (job->getType()) { + case Job::Type::cardInternal: + case Job::Type::infoForUser: { + CardInternal* cij = static_cast(job); + requestedVCards.emplace(cij->jid, job->id); + scheduledVCards.erase(cij->jid); + } + break; + case Job::Type::ownInfoForUser: + case Job::Type::ownCardInternal: + break; + } +} + +void Core::DelayManager::executeJob(Core::Job* job) { + preExecuteJob(job); + runningJobs.emplace(job->id, job); + switch (job->getType()) { + case Job::Type::cardInternal: + case Job::Type::infoForUser: + emit requestVCard(static_cast(job)->jid); + break; + case Job::Type::ownInfoForUser: + case Job::Type::ownCardInternal: + emit requestOwnVCard(); + break; + } +} + +void Core::DelayManager::jobIsDone(Job::Id jobId) { + std::map::const_iterator itr = runningJobs.find(jobId); + if (itr == runningJobs.end()) { + throw 8573; //not supposed to happen, ever + } + Job* job = itr->second; + delete job; + runningJobs.erase(itr); + if (scheduledJobs.size() > 0) { + Job* job = scheduledJobs.front(); + scheduledJobs.pop_front(); + executeJob(job); + } +} + +void Core::DelayManager::replaceJob(Core::Job* job) { + preScheduleJob(job); + std::map::iterator itr = runningJobs.find(job->id); + if (itr != runningJobs.end()) { + preExecuteJob(job); + delete itr->second; + itr->second = job; + } else { + StorageById::iterator sitr = scheduledJobsById.find(job->id); + if (sitr != scheduledJobsById.end()) { + delete *(sitr); + scheduledJobsById.replace(sitr, job); + } else { + throw 8574; //not supposed to happen, ever + } + } +} + +void Core::DelayManager::receivedVCard(const QString& jid, const Shared::VCard& card) { + std::map::const_iterator cardItr = requestedVCards.find(jid); + if (cardItr == requestedVCards.end()) { + throw 8575; //never supposed to happen, the state is not correct; + } else { + Job::Id jobId = cardItr->second; + requestedVCards.erase(cardItr); + Job* job = runningJobs.at(jobId); + switch (job->getType()) { + case Job::Type::cardInternal: + jobIsDone(jobId); + emit receivedCard(jid, card); + break; + case Job::Type::infoForUser: +#ifdef WITH_OMEMO + requestedBundles.emplace(jid, jobId); + //TODO save card! + emit requestBundles(jid); +#else + { + Shared::Info info(jid); + info.turnIntoContact(card); + emit receivedInfo(info); + } + jobIsDone(jobId); +#endif + emit receivedCard(jid, card); + break; + default: + throw 8576; + } + } +} + +void Core::DelayManager::ownVCardReceived(const Shared::VCard& card) { + Job::Id jobId = ownVCardJobId; + ownVCardJobId = 0; + Job* job = runningJobs.at(jobId); + switch (job->getType()) { + case Job::Type::ownCardInternal: + jobIsDone(jobId); + emit receivedOwnCard(card); + break; + case Job::Type::ownInfoForUser: +#ifdef WITH_OMEMO + //requestedBundles.emplace(jid, jobId); + //TODO save card! + emit requestOwnBundle(); +#else + { + Shared::Info info(""); + info.turnIntoOwnAccount(card); + emit receivedOwnInfo(info); + } + jobIsDone(jobId); +#endif + emit receivedOwnCard(card); + break; + default: + throw 8576; + } +} + +void Core::DelayManager::receivedBundles(const QString& jid) { + +} diff --git a/core/delayManager/delaymanager.h b/core/delayManager/delaymanager.h new file mode 100644 index 0000000..e4f3e2a --- /dev/null +++ b/core/delayManager/delaymanager.h @@ -0,0 +1,116 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 CORE_DELAYMANAGER_H +#define CORE_DELAYMANAGER_H + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "job.h" + +namespace Core { + +class DelayManager : public QObject +{ + Q_OBJECT +public: + DelayManager(Job::Id maxParallelJobs = 5, QObject* parent = nullptr); + ~DelayManager(); + + void getOwnVCard(); + void getOwnInfo(); + void getVCard(const QString& jid); + void getInfo(const QString& jid); + +signals: + void requestVCard(const QString& jid); + void requestOwnVCard(); + void requestBundles(const QString& jid); + void requestOwnBundle(); + void receivedCard(const QString& jid, const Shared::VCard& info); + void receivedOwnCard(const Shared::VCard& info); + void receivedInfo(const Shared::Info& info); + void receivedOwnInfo(const Shared::Info& info); + +public slots: + void ownVCardReceived(const Shared::VCard& card); + void receivedVCard(const QString& jid, const Shared::VCard& card); + void receivedBundles(const QString& jid); + +private: + void preScheduleJob(Job* job); + void scheduleJob(Job* job); + void preExecuteJob(Job* job); + void executeJob(Job* job); + void jobIsDone(Job::Id jobId); + Job::Id getNextJobId(); + void replaceJob(Job* job); + Job* getVCardJob(const QString& jid); + +private: + struct id {}; + struct sequence {}; + + typedef boost::multi_index_container< + Job*, + boost::multi_index::indexed_by< + boost::multi_index::sequenced< + boost::multi_index::tag + >, + boost::multi_index::ordered_unique< + boost::multi_index::tag, + boost::multi_index::member< + Job, + const Job::Id, + &Job::id + > + > + > + > Storage; + + + typedef Storage::index::type StorageById; + typedef Storage::index::type StorageSequence; + Job::Id maxParallelJobs; + Job::Id nextJobId; + + Storage scheduledJobs; + StorageById& scheduledJobsById; + StorageSequence& jobSequence; + std::map runningJobs; + + Job::Id ownVCardJobId; + Job::Id ownInfoJobId; + std::map scheduledVCards; + std::map requestedVCards; + std::map requestedBundles; +}; + +} + +#endif // CORE_DELAYMANAGER_H diff --git a/core/delayManager/infoforuser.cpp b/core/delayManager/infoforuser.cpp new file mode 100644 index 0000000..31d0571 --- /dev/null +++ b/core/delayManager/infoforuser.cpp @@ -0,0 +1,26 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "infoforuser.h" + +Core::InfoForUser::InfoForUser(Job::Id p_id, const QString& p_jid) : + CardInternal(p_id, p_jid, Type::infoForUser) +{} + +Core::InfoForUser::InfoForUser(const Core::InfoForUser& other) : + CardInternal(other) +{} + diff --git a/core/delayManager/infoforuser.h b/core/delayManager/infoforuser.h new file mode 100644 index 0000000..2bc84e4 --- /dev/null +++ b/core/delayManager/infoforuser.h @@ -0,0 +1,32 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 CORE_INFOFORUSER_H +#define CORE_INFOFORUSER_H + +#include "cardinternal.h" + +namespace Core { + +class InfoForUser : public CardInternal { +public: + InfoForUser(Job::Id id, const QString& jid); + InfoForUser(const InfoForUser& other); +}; + +} + +#endif // CORE_INFOFORUSER_H diff --git a/core/delayManager/job.cpp b/core/delayManager/job.cpp new file mode 100644 index 0000000..0d4c868 --- /dev/null +++ b/core/delayManager/job.cpp @@ -0,0 +1,32 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "job.h" + +Core::Job::Job(Core::Job::Id p_id, Core::Job::Type p_type) : + id (p_id), + type (p_type) {} + + +Core::Job::Job(const Core::Job& other) : + id(other.id), + type(other.type) {} + +Core::Job::~Job() {} + +Core::Job::Type Core::Job::getType() const { + return type; +} diff --git a/core/delayManager/job.h b/core/delayManager/job.h new file mode 100644 index 0000000..6314f48 --- /dev/null +++ b/core/delayManager/job.h @@ -0,0 +1,50 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 CORE_DELAYMANAGER_JOB_H +#define CORE_DELAYMANAGER_JOB_H + +#include + +namespace Core { + +class Job { +public: + typedef uint16_t Id; + + enum class Type { + cardInternal, + ownCardInternal, + infoForUser, + ownInfoForUser + }; + + Job(Id id, Type type); + Job(const Job& other); + virtual ~Job(); + + const Id id; + +public: + Type getType() const; + +protected: + Type type; +}; + +} + +#endif // CORE_DELAYMANAGER_JOB_H diff --git a/core/delayManager/owncardinternal.cpp b/core/delayManager/owncardinternal.cpp new file mode 100644 index 0000000..0167516 --- /dev/null +++ b/core/delayManager/owncardinternal.cpp @@ -0,0 +1,30 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "owncardinternal.h" + +Core::OwnCardInternal::OwnCardInternal(Job::Id p_id) : + Job(p_id, Type::ownCardInternal) +{} + +Core::OwnCardInternal::OwnCardInternal(Job::Id p_id, Job::Type p_type) : + Job(p_id, p_type) +{} + +Core::OwnCardInternal::OwnCardInternal(const Core::OwnCardInternal& other) : + Job(other) +{} + diff --git a/core/delayManager/owncardinternal.h b/core/delayManager/owncardinternal.h new file mode 100644 index 0000000..296666f --- /dev/null +++ b/core/delayManager/owncardinternal.h @@ -0,0 +1,35 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 CORE_OWNCARDINTERNAL_H +#define CORE_OWNCARDINTERNAL_H + +#include "job.h" + +namespace Core { + +class OwnCardInternal : public Job { +protected: + OwnCardInternal(Job::Id id, Job::Type type); + +public: + OwnCardInternal(Job::Id id); + OwnCardInternal(const OwnCardInternal& other); +}; + +} + +#endif // CORE_OWNCARDINTERNAL_H diff --git a/core/delayManager/owninfoforuser.cpp b/core/delayManager/owninfoforuser.cpp new file mode 100644 index 0000000..12d1f72 --- /dev/null +++ b/core/delayManager/owninfoforuser.cpp @@ -0,0 +1,25 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "owninfoforuser.h" + +Core::OwnInfoForUser::OwnInfoForUser(Job::Id p_id) : + OwnCardInternal(p_id, Type::ownInfoForUser) +{} + +Core::OwnInfoForUser::OwnInfoForUser(const Core::OwnInfoForUser& other) : + OwnCardInternal(other) +{} diff --git a/core/delayManager/owninfoforuser.h b/core/delayManager/owninfoforuser.h new file mode 100644 index 0000000..a2534e5 --- /dev/null +++ b/core/delayManager/owninfoforuser.h @@ -0,0 +1,32 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 CORE_OWNINFOFORUSER_H +#define CORE_OWNINFOFORUSER_H + +#include "owncardinternal.h" + +namespace Core { + +class OwnInfoForUser : public OwnCardInternal { +public: + OwnInfoForUser(Job::Id id); + OwnInfoForUser(const OwnInfoForUser& other); +}; + +} + +#endif // CORE_OWNINFOFORUSER_H From 9fff40963021b2a0b9d80b9f8cb53f9817ae1678 Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 7 Mar 2023 21:45:01 +0300 Subject: [PATCH 094/137] some more thinking about delay manager --- core/delayManager/CMakeLists.txt | 4 + core/delayManager/cardinternal.cpp | 14 ++- core/delayManager/cardinternal.h | 20 ++--- core/delayManager/contact.cpp | 26 ++++++ core/delayManager/contact.h | 39 +++++++++ core/delayManager/delaymanager.cpp | 120 ++++++++++++++++++++------ core/delayManager/delaymanager.h | 18 ++-- core/delayManager/info.cpp | 34 ++++++++ core/delayManager/info.h | 48 +++++++++++ core/delayManager/infoforuser.cpp | 12 ++- core/delayManager/infoforuser.h | 15 ++-- core/delayManager/job.cpp | 10 +-- core/delayManager/job.h | 13 ++- core/delayManager/owncardinternal.cpp | 6 +- core/delayManager/owncardinternal.h | 12 +-- core/delayManager/owninfoforuser.cpp | 10 ++- core/delayManager/owninfoforuser.h | 15 ++-- 17 files changed, 320 insertions(+), 96 deletions(-) create mode 100644 core/delayManager/contact.cpp create mode 100644 core/delayManager/contact.h create mode 100644 core/delayManager/info.cpp create mode 100644 core/delayManager/info.h diff --git a/core/delayManager/CMakeLists.txt b/core/delayManager/CMakeLists.txt index f9ccb57..96f4cef 100644 --- a/core/delayManager/CMakeLists.txt +++ b/core/delayManager/CMakeLists.txt @@ -5,6 +5,8 @@ set(SOURCE_FILES infoforuser.cpp owncardinternal.cpp owninfoforuser.cpp + contact.cpp + info.cpp ) set(HEADER_FILES @@ -14,6 +16,8 @@ set(HEADER_FILES infoforuser.h owncardinternal.h owninfoforuser.h + contact.h + info.h ) target_sources(squawk PRIVATE diff --git a/core/delayManager/cardinternal.cpp b/core/delayManager/cardinternal.cpp index c7a599a..c9ed203 100644 --- a/core/delayManager/cardinternal.cpp +++ b/core/delayManager/cardinternal.cpp @@ -16,17 +16,13 @@ #include "cardinternal.h" -Core::CardInternal::CardInternal(Job::Id p_id, const QString& p_jid) : - Job(p_id, Type::cardInternal), - jid(p_id) +Core::DelayManager::CardInternal::CardInternal(Id p_id, const QString& p_jid) : + Job(p_id, Type::cardInternal), + Contact(p_id, p_jid, Type::cardInternal) {} -Core::CardInternal::CardInternal(Job::Id p_id, const QString& p_jid, Job::Type p_type) : - Job(p_id, p_type), - jid(p_id) -{} -Core::CardInternal::CardInternal(const Core::CardInternal& other) : +Core::DelayManager::CardInternal::CardInternal(const CardInternal& other) : Job(other), - jid(other.jid) + Contact(other) {} diff --git a/core/delayManager/cardinternal.h b/core/delayManager/cardinternal.h index a95f0ba..17dd1ba 100644 --- a/core/delayManager/cardinternal.h +++ b/core/delayManager/cardinternal.h @@ -14,28 +14,24 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#ifndef CORE_CARDINTERNAL_H -#define CORE_CARDINTERNAL_H +#ifndef CORE_DELAYMANAGER_CARDINTERNAL_H +#define CORE_DELAYMANAGER_CARDINTERNAL_H #include -#include "job.h" +#include "contact.h" namespace Core { +namespace DelayManager { -class CardInternal : public Job { -protected: - CardInternal(Job::Id id, const QString& jid, Job::Type type); - +class CardInternal : public Contact { public: - CardInternal(Job::Id id, const QString& jid); + CardInternal(Id id, const QString& jid); CardInternal(const CardInternal& other); - - const QString jid; - }; +} } -#endif // CORE_CARDINTERNAL_H +#endif // CORE_DELAYMANAGER_CARDINTERNAL_H diff --git a/core/delayManager/contact.cpp b/core/delayManager/contact.cpp new file mode 100644 index 0000000..286c8bd --- /dev/null +++ b/core/delayManager/contact.cpp @@ -0,0 +1,26 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "contact.h" + +Core::DelayManager::Contact::Contact(const Contact& other): + Job(other), + jid(other.jid) {} + + +Core::DelayManager::Contact::Contact(Id p_id, const QString& p_jid, Type p_type): + Job(p_id, p_type), + jid(p_jid) {} diff --git a/core/delayManager/contact.h b/core/delayManager/contact.h new file mode 100644 index 0000000..c136525 --- /dev/null +++ b/core/delayManager/contact.h @@ -0,0 +1,39 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 CORE_DELAYMANAGER_CONTACT_H +#define CORE_DELAYMANAGER_CONTACT_H + +#include + +#include "job.h" + +namespace Core { +namespace DelayManager { + +class Contact : public virtual Job { +protected: + Contact(Id id, const QString& jid, Type type); + Contact(const Contact& other); + +public: + const QString jid; +}; + +} +} + +#endif // CORE_DELAYMANAGER_CONTACT_H diff --git a/core/delayManager/delaymanager.cpp b/core/delayManager/delaymanager.cpp index 6ebc85d..906724f 100644 --- a/core/delayManager/delaymanager.cpp +++ b/core/delayManager/delaymanager.cpp @@ -21,7 +21,7 @@ #include "owncardinternal.h" #include "owninfoforuser.h" -Core::DelayManager::DelayManager(Job::Id mpj, QObject* parent) : +Core::DelayManager::Manager::Manager(Job::Id mpj, QObject* parent) : QObject(parent), maxParallelJobs(mpj), nextJobId(1), @@ -33,13 +33,23 @@ Core::DelayManager::DelayManager(Job::Id mpj, QObject* parent) : ownInfoJobId(0), scheduledVCards(), requestedVCards(), +#ifdef WITH_OMEMO requestedBundles() +#endif { } -Core::DelayManager::~DelayManager() {} +Core::DelayManager::Manager::~Manager() { + for (const std::pair& pair : runningJobs) { + delete pair.second; + } -Core::Job::Id Core::DelayManager::getNextJobId() { + for (Job* job : jobSequence) { + delete job; + } +} + +Core::Job::Id Core::DelayManager::Manager::getNextJobId() { Job::Id id = nextJobId++; if (id == 0) id = nextJobId++; @@ -47,7 +57,7 @@ Core::Job::Id Core::DelayManager::getNextJobId() { return id; } -void Core::DelayManager::getInfo(const QString& jid) { +void Core::DelayManager::Manager::getInfo(const QString& jid) { Job* job = nullptr; #ifdef WITH_OMEMO std::map::const_iterator bitr = requestedBundles.find(jid); @@ -58,13 +68,13 @@ void Core::DelayManager::getInfo(const QString& jid) { job = getVCardJob(jid); if (job != nullptr) { - if (job->getType() == Job::Type::cardInternal) + if (job->type == Job::Type::cardInternal) replaceJob(new InfoForUser(job->id, jid)); } else scheduleJob(new InfoForUser(getNextJobId(), jid)); } -void Core::DelayManager::getOwnInfo() { +void Core::DelayManager::Manager::getOwnInfo() { if (ownInfoJobId == 0) { if (ownVCardJobId != 0) replaceJob(new OwnInfoForUser(ownVCardJobId)); @@ -73,18 +83,18 @@ void Core::DelayManager::getOwnInfo() { } } -void Core::DelayManager::getVCard(const QString& jid) { +void Core::DelayManager::Manager::getVCard(const QString& jid) { Job* job = getVCardJob(jid); if (job == nullptr) scheduleJob(new CardInternal(getNextJobId(), jid)); } -void Core::DelayManager::getOwnVCard() { +void Core::DelayManager::Manager::getOwnVCard() { if (ownInfoJobId == 0) scheduleJob(new OwnCardInternal(getNextJobId())); } -Core::Job* Core::DelayManager::getVCardJob(const QString& jid) { +Core::Job* Core::DelayManager::Manager::getVCardJob(const QString& jid) { Job* job = nullptr; std::map::const_iterator sitr = scheduledVCards.find(jid); if (sitr == scheduledVCards.end()) { @@ -97,16 +107,16 @@ Core::Job* Core::DelayManager::getVCardJob(const QString& jid) { return job; } -void Core::DelayManager::preScheduleJob(Core::Job* job) { - switch (job->getType()) { +void Core::DelayManager::Manager::preScheduleJob(Core::Job* job) { + switch (job->type) { case Job::Type::cardInternal: - scheduledVCards.emplace(static_cast(job)->jid, job->id); + scheduledVCards.emplace(dynamic_cast(job)->jid, job->id); break; case Job::Type::ownCardInternal: ownVCardJobId = job->id; break; case Job::Type::infoForUser: - scheduledVCards.emplace(static_cast(job)->jid, job->id); + scheduledVCards.emplace(dynamic_cast(job)->jid, job->id); break; case Job::Type::ownInfoForUser: ownVCardJobId = job->id; @@ -115,7 +125,7 @@ void Core::DelayManager::preScheduleJob(Core::Job* job) { } } -void Core::DelayManager::scheduleJob(Core::Job* job) { +void Core::DelayManager::Manager::scheduleJob(Core::Job* job) { preScheduleJob(job); if (runningJobs.size() < maxParallelJobs) { executeJob(job); @@ -124,11 +134,11 @@ void Core::DelayManager::scheduleJob(Core::Job* job) { } } -void Core::DelayManager::preExecuteJob(Core::Job* job) { - switch (job->getType()) { +void Core::DelayManager::Manager::preExecuteJob(Core::Job* job) { + switch (job->type) { case Job::Type::cardInternal: case Job::Type::infoForUser: { - CardInternal* cij = static_cast(job); + Contact* cij = dynamic_cast(job); requestedVCards.emplace(cij->jid, job->id); scheduledVCards.erase(cij->jid); } @@ -139,13 +149,13 @@ void Core::DelayManager::preExecuteJob(Core::Job* job) { } } -void Core::DelayManager::executeJob(Core::Job* job) { +void Core::DelayManager::Manager::executeJob(Core::Job* job) { preExecuteJob(job); runningJobs.emplace(job->id, job); - switch (job->getType()) { + switch (job->type) { case Job::Type::cardInternal: case Job::Type::infoForUser: - emit requestVCard(static_cast(job)->jid); + emit requestVCard(dynamic_cast(job)->jid); break; case Job::Type::ownInfoForUser: case Job::Type::ownCardInternal: @@ -154,7 +164,7 @@ void Core::DelayManager::executeJob(Core::Job* job) { } } -void Core::DelayManager::jobIsDone(Job::Id jobId) { +void Core::DelayManager::Manager::jobIsDone(Job::Id jobId) { std::map::const_iterator itr = runningJobs.find(jobId); if (itr == runningJobs.end()) { throw 8573; //not supposed to happen, ever @@ -169,7 +179,7 @@ void Core::DelayManager::jobIsDone(Job::Id jobId) { } } -void Core::DelayManager::replaceJob(Core::Job* job) { +void Core::DelayManager::Manager::replaceJob(Core::Job* job) { preScheduleJob(job); std::map::iterator itr = runningJobs.find(job->id); if (itr != runningJobs.end()) { @@ -187,7 +197,63 @@ void Core::DelayManager::replaceJob(Core::Job* job) { } } -void Core::DelayManager::receivedVCard(const QString& jid, const Shared::VCard& card) { +void Core::DelayManager::Manager::jobIsCanceled(Core::Job* job, bool wasRunning) { + switch (job->type) { + case Job::Type::cardInternal: { + CardInternal* jb = dynamic_cast(job); + if (wasRunning) + requestedVCards.erase(jb->jid); + else + scheduledVCards.erase(jb->jid); + + emit receivedVCard(jb->jid, Shared::VCard()); + } + break; + case Job::Type::infoForUser: { + InfoForUser* jb = dynamic_cast(job); + if (jb->getStage() == InfoForUser::Stage::waitingForVCard) { + if (wasRunning) + requestedVCards.erase(jb->jid); + else + scheduledVCards.erase(jb->jid); + + emit receivedVCard(jb->jid, Shared::VCard()); + } + emit receivedInfo(Shared::Info(jb->jid)); + } + break; + case Job::Type::ownInfoForUser: { + OwnInfoForUser* jb = dynamic_cast(job); + if (jb->getStage() == OwnInfoForUser::Stage::waitingForVCard) { + ownVCardJobId = 0; + emit receivedOwnCard(Shared::VCard()); + } + ownInfoJobId = 0; + emit receivedOwnInfo(Shared::Info ("")); + } + + break; + case Job::Type::ownCardInternal: + ownVCardJobId = 0; + emit receivedOwnCard(Shared::VCard()); + break; + } + + delete job; +} + +void Core::DelayManager::Manager::disconnected() { + for (const std::pair pair : runningJobs) + jobIsCanceled(pair.second, true); + + for (Job* job : scheduledJobs) + jobIsCanceled(job, false); + + runningJobs.clear(); + scheduledJobs.clear(); +} + +void Core::DelayManager::Manager::receivedVCard(const QString& jid, const Shared::VCard& card) { std::map::const_iterator cardItr = requestedVCards.find(jid); if (cardItr == requestedVCards.end()) { throw 8575; //never supposed to happen, the state is not correct; @@ -195,7 +261,7 @@ void Core::DelayManager::receivedVCard(const QString& jid, const Shared::VCard& Job::Id jobId = cardItr->second; requestedVCards.erase(cardItr); Job* job = runningJobs.at(jobId); - switch (job->getType()) { + switch (job->type) { case Job::Type::cardInternal: jobIsDone(jobId); emit receivedCard(jid, card); @@ -221,11 +287,11 @@ void Core::DelayManager::receivedVCard(const QString& jid, const Shared::VCard& } } -void Core::DelayManager::ownVCardReceived(const Shared::VCard& card) { +void Core::DelayManager::Manager::ownVCardReceived(const Shared::VCard& card) { Job::Id jobId = ownVCardJobId; ownVCardJobId = 0; Job* job = runningJobs.at(jobId); - switch (job->getType()) { + switch (job->type) { case Job::Type::ownCardInternal: jobIsDone(jobId); emit receivedOwnCard(card); @@ -250,6 +316,6 @@ void Core::DelayManager::ownVCardReceived(const Shared::VCard& card) { } } -void Core::DelayManager::receivedBundles(const QString& jid) { +void Core::DelayManager::Manager::receivedBundles(const QString& jid) { } diff --git a/core/delayManager/delaymanager.h b/core/delayManager/delaymanager.h index e4f3e2a..c3d1da8 100644 --- a/core/delayManager/delaymanager.h +++ b/core/delayManager/delaymanager.h @@ -14,8 +14,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#ifndef CORE_DELAYMANAGER_H -#define CORE_DELAYMANAGER_H +#ifndef CORE_DELAYMANAGER_MANAGER_H +#define CORE_DELAYMANAGER_MANAGER_H #include #include @@ -34,13 +34,14 @@ #include "job.h" namespace Core { +namespace DelayManager { -class DelayManager : public QObject +class Manager : public QObject { Q_OBJECT public: - DelayManager(Job::Id maxParallelJobs = 5, QObject* parent = nullptr); - ~DelayManager(); + Manager(Job::Id maxParallelJobs = 5, QObject* parent = nullptr); + ~Manager(); void getOwnVCard(); void getOwnInfo(); @@ -58,6 +59,7 @@ signals: void receivedOwnInfo(const Shared::Info& info); public slots: + void disconnected(); void ownVCardReceived(const Shared::VCard& card); void receivedVCard(const QString& jid, const Shared::VCard& card); void receivedBundles(const QString& jid); @@ -67,6 +69,7 @@ private: void scheduleJob(Job* job); void preExecuteJob(Job* job); void executeJob(Job* job); + void jobIsCanceled(Job* job, bool wasRunning); void jobIsDone(Job::Id jobId); Job::Id getNextJobId(); void replaceJob(Job* job); @@ -108,9 +111,12 @@ private: Job::Id ownInfoJobId; std::map scheduledVCards; std::map requestedVCards; +#ifdef WITH_OMEMO std::map requestedBundles; +#endif }; +} } -#endif // CORE_DELAYMANAGER_H +#endif // CORE_DELAYMANAGER_MANAGER_H diff --git a/core/delayManager/info.cpp b/core/delayManager/info.cpp new file mode 100644 index 0000000..3b61f63 --- /dev/null +++ b/core/delayManager/info.cpp @@ -0,0 +1,34 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "info.h" + +Core::DelayManager::Info::Info(Id p_id, Type p_type) : + Job(p_id, p_type), + stage(Stage::waitingForVCard) +{} + +Core::DelayManager::Info::Info(const Info& other) : + Job(other), + stage(other.stage) +{} + +Core::DelayManager::Info::~Info() { +} + +Core::DelayManager::Info::Stage Core::DelayManager::Info::getStage() const { + return stage; +} diff --git a/core/delayManager/info.h b/core/delayManager/info.h new file mode 100644 index 0000000..0c4249a --- /dev/null +++ b/core/delayManager/info.h @@ -0,0 +1,48 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 CORE_DELAYMANAGER_INFO_H +#define CORE_DELAYMANAGER_INFO_H + +#include "job.h" + +namespace Core { +namespace DelayManager { + +class Info : public virtual Job { +public: + enum class Stage { + waitingForVCard, + waitingForBundles + }; + +protected: + Info(Id id, Type type); + Info(const Info& other); + +public: + ~Info(); + + Stage getStage() const; + +private: + Stage stage; +}; + +} +} + +#endif // CORE_DELAYMANAGER_INFO_H diff --git a/core/delayManager/infoforuser.cpp b/core/delayManager/infoforuser.cpp index 31d0571..fc494fb 100644 --- a/core/delayManager/infoforuser.cpp +++ b/core/delayManager/infoforuser.cpp @@ -16,11 +16,15 @@ #include "infoforuser.h" -Core::InfoForUser::InfoForUser(Job::Id p_id, const QString& p_jid) : - CardInternal(p_id, p_jid, Type::infoForUser) +Core::DelayManager::InfoForUser::InfoForUser(Id p_id, const QString& p_jid) : + Job(p_id, Type::infoForUser), + Contact(p_id, p_jid, Type::infoForUser), + Info(p_id, Type::infoForUser) {} -Core::InfoForUser::InfoForUser(const Core::InfoForUser& other) : - CardInternal(other) +Core::DelayManager::InfoForUser::InfoForUser(const InfoForUser& other) : + Job(other), + Contact(other), + Info(other) {} diff --git a/core/delayManager/infoforuser.h b/core/delayManager/infoforuser.h index 2bc84e4..651d741 100644 --- a/core/delayManager/infoforuser.h +++ b/core/delayManager/infoforuser.h @@ -14,19 +14,22 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#ifndef CORE_INFOFORUSER_H -#define CORE_INFOFORUSER_H +#ifndef CORE_DELAYMANAGER_INFOFORUSER_H +#define CORE_DELAYMANAGER_INFOFORUSER_H -#include "cardinternal.h" +#include "contact.h" +#include "info.h" namespace Core { +namespace DelayManager { -class InfoForUser : public CardInternal { +class InfoForUser : public Contact, public Info { public: - InfoForUser(Job::Id id, const QString& jid); + InfoForUser(Id id, const QString& jid); InfoForUser(const InfoForUser& other); }; +} } -#endif // CORE_INFOFORUSER_H +#endif // CORE_DELAYMANAGER_INFOFORUSER_H diff --git a/core/delayManager/job.cpp b/core/delayManager/job.cpp index 0d4c868..b2d74b2 100644 --- a/core/delayManager/job.cpp +++ b/core/delayManager/job.cpp @@ -16,17 +16,13 @@ #include "job.h" -Core::Job::Job(Core::Job::Id p_id, Core::Job::Type p_type) : +Core::DelayManager::Job::Job(Id p_id, Type p_type) : id (p_id), type (p_type) {} -Core::Job::Job(const Core::Job& other) : +Core::DelayManager::Job::Job(const Job& other) : id(other.id), type(other.type) {} -Core::Job::~Job() {} - -Core::Job::Type Core::Job::getType() const { - return type; -} +Core::DelayManager::Job::~Job() {} diff --git a/core/delayManager/job.h b/core/delayManager/job.h index 6314f48..633f2b2 100644 --- a/core/delayManager/job.h +++ b/core/delayManager/job.h @@ -20,6 +20,7 @@ #include namespace Core { +namespace DelayManager { class Job { public: @@ -31,20 +32,18 @@ public: infoForUser, ownInfoForUser }; - +protected: Job(Id id, Type type); Job(const Job& other); + +public: virtual ~Job(); const Id id; - -public: - Type getType() const; - -protected: - Type type; + const Type type; }; +} } #endif // CORE_DELAYMANAGER_JOB_H diff --git a/core/delayManager/owncardinternal.cpp b/core/delayManager/owncardinternal.cpp index 0167516..43ed540 100644 --- a/core/delayManager/owncardinternal.cpp +++ b/core/delayManager/owncardinternal.cpp @@ -16,15 +16,15 @@ #include "owncardinternal.h" -Core::OwnCardInternal::OwnCardInternal(Job::Id p_id) : +Core::DelayManager::OwnCardInternal::OwnCardInternal(Id p_id) : Job(p_id, Type::ownCardInternal) {} -Core::OwnCardInternal::OwnCardInternal(Job::Id p_id, Job::Type p_type) : +Core::DelayManager::OwnCardInternal::OwnCardInternal(Id p_id, Type p_type) : Job(p_id, p_type) {} -Core::OwnCardInternal::OwnCardInternal(const Core::OwnCardInternal& other) : +Core::DelayManager::OwnCardInternal::OwnCardInternal(const OwnCardInternal& other) : Job(other) {} diff --git a/core/delayManager/owncardinternal.h b/core/delayManager/owncardinternal.h index 296666f..7cca0a0 100644 --- a/core/delayManager/owncardinternal.h +++ b/core/delayManager/owncardinternal.h @@ -14,22 +14,24 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#ifndef CORE_OWNCARDINTERNAL_H -#define CORE_OWNCARDINTERNAL_H +#ifndef CORE_DELAYMANAGER_OWNCARDINTERNAL_H +#define CORE_DELAYMANAGER_OWNCARDINTERNAL_H #include "job.h" namespace Core { +namespace DelayManager { class OwnCardInternal : public Job { protected: - OwnCardInternal(Job::Id id, Job::Type type); + OwnCardInternal(Id id, Type type); public: - OwnCardInternal(Job::Id id); + OwnCardInternal(Id id); OwnCardInternal(const OwnCardInternal& other); }; +} } -#endif // CORE_OWNCARDINTERNAL_H +#endif // CORE_DELAYMANAGER_OWNCARDINTERNAL_H diff --git a/core/delayManager/owninfoforuser.cpp b/core/delayManager/owninfoforuser.cpp index 12d1f72..396dc49 100644 --- a/core/delayManager/owninfoforuser.cpp +++ b/core/delayManager/owninfoforuser.cpp @@ -16,10 +16,12 @@ #include "owninfoforuser.h" -Core::OwnInfoForUser::OwnInfoForUser(Job::Id p_id) : - OwnCardInternal(p_id, Type::ownInfoForUser) +Core::DelayManager::OwnInfoForUser::OwnInfoForUser(Id p_id) : + Job(p_id, Type::ownInfoForUser), + Info(p_id, Type::ownInfoForUser) {} -Core::OwnInfoForUser::OwnInfoForUser(const Core::OwnInfoForUser& other) : - OwnCardInternal(other) +Core::DelayManager::OwnInfoForUser::OwnInfoForUser(const OwnInfoForUser& other) : + Job(other), + Info(other) {} diff --git a/core/delayManager/owninfoforuser.h b/core/delayManager/owninfoforuser.h index a2534e5..80a13b6 100644 --- a/core/delayManager/owninfoforuser.h +++ b/core/delayManager/owninfoforuser.h @@ -14,19 +14,22 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#ifndef CORE_OWNINFOFORUSER_H -#define CORE_OWNINFOFORUSER_H +#ifndef CORE_DELAYMANAGER_OWNINFOFORUSER_H +#define CORE_DELAYMANAGER_OWNINFOFORUSER_H -#include "owncardinternal.h" +#include "contact.h" +#include "info.h" namespace Core { +namespace DelayManager { -class OwnInfoForUser : public OwnCardInternal { +class OwnInfoForUser : public Info { public: - OwnInfoForUser(Job::Id id); + OwnInfoForUser(Id id); OwnInfoForUser(const OwnInfoForUser& other); }; +} } -#endif // CORE_OWNINFOFORUSER_H +#endif // CORE_DELAYMANAGER_OWNINFOFORUSER_H From 5ba97ecc2538b4f823ec68d75dc751e3f8038933 Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 8 Mar 2023 23:28:48 +0300 Subject: [PATCH 095/137] some hopefully final preparations for delay manager --- core/account.cpp | 1 - core/delayManager/delaymanager.cpp | 133 ++++++++++++++++++----------- core/delayManager/delaymanager.h | 8 +- core/delayManager/info.cpp | 26 +++++- core/delayManager/info.h | 10 ++- core/delayManager/infoforuser.cpp | 1 - shared/info.cpp | 23 +++++ shared/info.h | 2 + 8 files changed, 148 insertions(+), 56 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index 0a6c59c..d158970 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -107,7 +107,6 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& client.addExtension(tm); client.addExtension(om); om->setSecurityPolicy(QXmpp::Toakafa); - om->setNewDeviceAutoSessionBuildingEnabled(true); if (oh->hasOwnDevice()) { QXmppTask future = om->load(); diff --git a/core/delayManager/delaymanager.cpp b/core/delayManager/delaymanager.cpp index 906724f..0ecc9a4 100644 --- a/core/delayManager/delaymanager.cpp +++ b/core/delayManager/delaymanager.cpp @@ -21,7 +21,7 @@ #include "owncardinternal.h" #include "owninfoforuser.h" -Core::DelayManager::Manager::Manager(Job::Id mpj, QObject* parent) : +Core::DelayManager::Manager::Manager(const QString& poj, Job::Id mpj, QObject* parent) : QObject(parent), maxParallelJobs(mpj), nextJobId(1), @@ -34,8 +34,9 @@ Core::DelayManager::Manager::Manager(Job::Id mpj, QObject* parent) : scheduledVCards(), requestedVCards(), #ifdef WITH_OMEMO - requestedBundles() + requestedBundles(), #endif + ownJid(poj) { } @@ -49,7 +50,7 @@ Core::DelayManager::Manager::~Manager() { } } -Core::Job::Id Core::DelayManager::Manager::getNextJobId() { +Core::DelayManager::Job::Id Core::DelayManager::Manager::getNextJobId() { Job::Id id = nextJobId++; if (id == 0) id = nextJobId++; @@ -94,7 +95,7 @@ void Core::DelayManager::Manager::getOwnVCard() { scheduleJob(new OwnCardInternal(getNextJobId())); } -Core::Job* Core::DelayManager::Manager::getVCardJob(const QString& jid) { +Core::DelayManager::Job* Core::DelayManager::Manager::getVCardJob(const QString& jid) { Job* job = nullptr; std::map::const_iterator sitr = scheduledVCards.find(jid); if (sitr == scheduledVCards.end()) { @@ -107,7 +108,7 @@ Core::Job* Core::DelayManager::Manager::getVCardJob(const QString& jid) { return job; } -void Core::DelayManager::Manager::preScheduleJob(Core::Job* job) { +void Core::DelayManager::Manager::preScheduleJob(Job* job) { switch (job->type) { case Job::Type::cardInternal: scheduledVCards.emplace(dynamic_cast(job)->jid, job->id); @@ -125,7 +126,7 @@ void Core::DelayManager::Manager::preScheduleJob(Core::Job* job) { } } -void Core::DelayManager::Manager::scheduleJob(Core::Job* job) { +void Core::DelayManager::Manager::scheduleJob(Job* job) { preScheduleJob(job); if (runningJobs.size() < maxParallelJobs) { executeJob(job); @@ -134,7 +135,7 @@ void Core::DelayManager::Manager::scheduleJob(Core::Job* job) { } } -void Core::DelayManager::Manager::preExecuteJob(Core::Job* job) { +void Core::DelayManager::Manager::preExecuteJob(Job* job) { switch (job->type) { case Job::Type::cardInternal: case Job::Type::infoForUser: { @@ -149,7 +150,7 @@ void Core::DelayManager::Manager::preExecuteJob(Core::Job* job) { } } -void Core::DelayManager::Manager::executeJob(Core::Job* job) { +void Core::DelayManager::Manager::executeJob(Job* job) { preExecuteJob(job); runningJobs.emplace(job->id, job); switch (job->type) { @@ -179,7 +180,7 @@ void Core::DelayManager::Manager::jobIsDone(Job::Id jobId) { } } -void Core::DelayManager::Manager::replaceJob(Core::Job* job) { +void Core::DelayManager::Manager::replaceJob(Job* job) { preScheduleJob(job); std::map::iterator itr = runningJobs.find(job->id); if (itr != runningJobs.end()) { @@ -197,7 +198,7 @@ void Core::DelayManager::Manager::replaceJob(Core::Job* job) { } } -void Core::DelayManager::Manager::jobIsCanceled(Core::Job* job, bool wasRunning) { +void Core::DelayManager::Manager::jobIsCanceled(Job* job, bool wasRunning) { switch (job->type) { case Job::Type::cardInternal: { CardInternal* jb = dynamic_cast(job); @@ -211,13 +212,20 @@ void Core::DelayManager::Manager::jobIsCanceled(Core::Job* job, bool wasRunning) break; case Job::Type::infoForUser: { InfoForUser* jb = dynamic_cast(job); - if (jb->getStage() == InfoForUser::Stage::waitingForVCard) { - if (wasRunning) - requestedVCards.erase(jb->jid); - else - scheduledVCards.erase(jb->jid); + switch (jb->getStage()) { + case InfoForUser::Stage::waitingForVCard: + if (wasRunning) + requestedVCards.erase(jb->jid); + else + scheduledVCards.erase(jb->jid); - emit receivedVCard(jb->jid, Shared::VCard()); + emit receivedVCard(jb->jid, Shared::VCard()); + break; + case InfoForUser::Stage::waitingForBundles: + requestedBundles.erase(jb->jid); + break; + default: + break; } emit receivedInfo(Shared::Info(jb->jid)); } @@ -229,7 +237,7 @@ void Core::DelayManager::Manager::jobIsCanceled(Core::Job* job, bool wasRunning) emit receivedOwnCard(Shared::VCard()); } ownInfoJobId = 0; - emit receivedOwnInfo(Shared::Info ("")); + emit receivedOwnInfo(Shared::Info (ownJid)); } break; @@ -257,33 +265,32 @@ void Core::DelayManager::Manager::receivedVCard(const QString& jid, const Shared std::map::const_iterator cardItr = requestedVCards.find(jid); if (cardItr == requestedVCards.end()) { throw 8575; //never supposed to happen, the state is not correct; - } else { - Job::Id jobId = cardItr->second; - requestedVCards.erase(cardItr); - Job* job = runningJobs.at(jobId); - switch (job->type) { - case Job::Type::cardInternal: - jobIsDone(jobId); - emit receivedCard(jid, card); - break; - case Job::Type::infoForUser: + } + Job::Id jobId = cardItr->second; + requestedVCards.erase(cardItr); + Job* job = runningJobs.at(jobId); + switch (job->type) { + case Job::Type::cardInternal: + jobIsDone(jobId); + emit receivedCard(jid, card); + break; + case Job::Type::infoForUser: { #ifdef WITH_OMEMO - requestedBundles.emplace(jid, jobId); - //TODO save card! - emit requestBundles(jid); + requestedBundles.emplace(jid, jobId); + InfoForUser* jb = dynamic_cast(job); + jb->receivedVCard(card); + emit requestBundles(jid); #else - { - Shared::Info info(jid); - info.turnIntoContact(card); - emit receivedInfo(info); - } - jobIsDone(jobId); + Shared::Info info(jid); + info.turnIntoContact(card); + emit receivedInfo(info); + jobIsDone(jobId); #endif - emit receivedCard(jid, card); - break; - default: - throw 8576; + emit receivedCard(jid, card); } + break; + default: + throw 8576; } } @@ -296,26 +303,54 @@ void Core::DelayManager::Manager::ownVCardReceived(const Shared::VCard& card) { jobIsDone(jobId); emit receivedOwnCard(card); break; - case Job::Type::ownInfoForUser: + case Job::Type::ownInfoForUser: { #ifdef WITH_OMEMO - //requestedBundles.emplace(jid, jobId); - //TODO save card! + OwnInfoForUser* jb = dynamic_cast(job); + jb->receivedVCard(card); emit requestOwnBundle(); #else - { - Shared::Info info(""); - info.turnIntoOwnAccount(card); - emit receivedOwnInfo(info); - } + Shared::Info info(ownJid); + info.turnIntoOwnAccount(card); + emit receivedOwnInfo(info); jobIsDone(jobId); #endif emit receivedOwnCard(card); + } break; default: throw 8576; } } -void Core::DelayManager::Manager::receivedBundles(const QString& jid) { +void Core::DelayManager::Manager::receivedBundles(const QString& jid, const std::list& keys) { + std::map::const_iterator itr = requestedBundles.find(jid); + if (itr == requestedBundles.end()) { + throw 8577; //never supposed to happen, the state is not correct; + } + Job::Id jobId = itr->second; + requestedBundles.erase(itr); + Job* jb = runningJobs.at(jobId); + InfoForUser* job = dynamic_cast(jb); + + Shared::Info info(jid); + info.turnIntoContact(job->claim(), new std::list(keys)); + emit receivedInfo(info); + jobIsDone(jobId); +} + +void Core::DelayManager::Manager::receivedOwnBundles(const std::list& keys) { + Job::Id jobId = ownInfoJobId; + ownInfoJobId = 0; + Job* jb = runningJobs.at(jobId); + OwnInfoForUser* job = dynamic_cast(jb); + + Shared::Info info(ownJid); + info.turnIntoOwnAccount(job->claim(), new std::list(keys)); + emit receivedOwnInfo(info); + jobIsDone(jobId); +} + +void Core::DelayManager::Manager::setOwnJid(const QString& jid) { + ownJid = jid; } diff --git a/core/delayManager/delaymanager.h b/core/delayManager/delaymanager.h index c3d1da8..d859a18 100644 --- a/core/delayManager/delaymanager.h +++ b/core/delayManager/delaymanager.h @@ -40,19 +40,21 @@ class Manager : public QObject { Q_OBJECT public: - Manager(Job::Id maxParallelJobs = 5, QObject* parent = nullptr); + Manager(const QString& ownJid, Job::Id maxParallelJobs = 5, QObject* parent = nullptr); ~Manager(); void getOwnVCard(); void getOwnInfo(); void getVCard(const QString& jid); void getInfo(const QString& jid); + void setOwnJid(const QString& jid); signals: void requestVCard(const QString& jid); void requestOwnVCard(); void requestBundles(const QString& jid); void requestOwnBundle(); + void receivedCard(const QString& jid, const Shared::VCard& info); void receivedOwnCard(const Shared::VCard& info); void receivedInfo(const Shared::Info& info); @@ -62,7 +64,8 @@ public slots: void disconnected(); void ownVCardReceived(const Shared::VCard& card); void receivedVCard(const QString& jid, const Shared::VCard& card); - void receivedBundles(const QString& jid); + void receivedBundles(const QString& jid, const std::list& keys); + void receivedOwnBundles(const std::list& keys); private: void preScheduleJob(Job* job); @@ -114,6 +117,7 @@ private: #ifdef WITH_OMEMO std::map requestedBundles; #endif + QString ownJid; }; } diff --git a/core/delayManager/info.cpp b/core/delayManager/info.cpp index 3b61f63..b5b619d 100644 --- a/core/delayManager/info.cpp +++ b/core/delayManager/info.cpp @@ -18,17 +18,39 @@ Core::DelayManager::Info::Info(Id p_id, Type p_type) : Job(p_id, p_type), - stage(Stage::waitingForVCard) + stage(Stage::waitingForVCard), + info(nullptr) {} Core::DelayManager::Info::Info(const Info& other) : Job(other), - stage(other.stage) + stage(other.stage), + info(nullptr) {} Core::DelayManager::Info::~Info() { + if (stage == Stage::waitingForBundles) { + delete info; + } } Core::DelayManager::Info::Stage Core::DelayManager::Info::getStage() const { return stage; } + +void Core::DelayManager::Info::receivedVCard(const Shared::VCard& card) { + if (stage != Stage::waitingForVCard) + throw 245; + + info = new Shared::VCard(card); + stage = Stage::waitingForBundles; +} + +Shared::VCard * Core::DelayManager::Info::claim() { + if (stage != Stage::waitingForBundles) + throw 246; + + Shared::VCard* res = info; + info = nullptr; + return res; +} diff --git a/core/delayManager/info.h b/core/delayManager/info.h index 0c4249a..a6601a1 100644 --- a/core/delayManager/info.h +++ b/core/delayManager/info.h @@ -19,6 +19,9 @@ #include "job.h" +#include +#include + namespace Core { namespace DelayManager { @@ -26,7 +29,8 @@ class Info : public virtual Job { public: enum class Stage { waitingForVCard, - waitingForBundles + waitingForBundles, + finished }; protected: @@ -36,10 +40,14 @@ protected: public: ~Info(); + void receivedVCard(const Shared::VCard& card); + Shared::VCard* claim(); Stage getStage() const; + private: Stage stage; + Shared::VCard* info; }; } diff --git a/core/delayManager/infoforuser.cpp b/core/delayManager/infoforuser.cpp index fc494fb..067be80 100644 --- a/core/delayManager/infoforuser.cpp +++ b/core/delayManager/infoforuser.cpp @@ -27,4 +27,3 @@ Core::DelayManager::InfoForUser::InfoForUser(const InfoForUser& other) : Contact(other), Info(other) {} - diff --git a/shared/info.cpp b/shared/info.cpp index c0d6f26..36cd4b5 100644 --- a/shared/info.cpp +++ b/shared/info.cpp @@ -328,3 +328,26 @@ Shared::VCard * Shared::Info::getVCard() { throw 365; } } + +void Shared::Info::setActiveKeys(std::list* keys) { + switch (type) { + case EntryType::contact: + case EntryType::ownAccount: + activeKeys = keys; + break; + default: + throw 366; + } +} + +void Shared::Info::setVCard(Shared::VCard* card) { + switch (type) { + case EntryType::contact: + case EntryType::ownAccount: + vcard = card; + break; + default: + throw 367; + } +} + diff --git a/shared/info.h b/shared/info.h index b0f495e..e42eddb 100644 --- a/shared/info.h +++ b/shared/info.h @@ -69,11 +69,13 @@ public: VCard& getVCardRef(); const VCard* getVCard() const; VCard* getVCard(); + void setVCard(Shared::VCard* card); const std::list& getActiveKeysRef() const; std::list& getActiveKeysRef(); const std::list* getActiveKeys() const; std::list* getActiveKeys(); + void setActiveKeys(std::list* keys); const std::list& getInactiveKeysRef() const; std::list& getInactiveKeysRef(); From 927bdf0dab1e38b8e5a79ee499d20ede4c570d0e Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 10 Mar 2023 21:43:31 +0300 Subject: [PATCH 096/137] DONT TAKE, BROKEN! first application of delay manager in code, reception of bundles --- core/account.cpp | 25 ++++-- core/account.h | 6 ++ core/delayManager/CMakeLists.txt | 4 +- .../{delaymanager.cpp => manager.cpp} | 48 ++++++----- .../{delaymanager.h => manager.h} | 17 ++-- core/handlers/omemohandler.cpp | 48 +++++++++++ core/handlers/omemohandler.h | 10 ++- core/handlers/rosterhandler.cpp | 4 +- core/handlers/rosterhandler.h | 1 + core/handlers/trusthandler.cpp | 51 ++++++------ core/handlers/vcardhandler.cpp | 79 ++++--------------- core/handlers/vcardhandler.h | 4 - main/CMakeLists.txt | 16 +++- main/main.cpp | 16 ++-- main/root.cpp | 43 ++++++++++ main/root.h | 28 +++++++ 16 files changed, 261 insertions(+), 139 deletions(-) rename core/delayManager/{delaymanager.cpp => manager.cpp} (90%) rename core/delayManager/{delaymanager.h => manager.h} (90%) create mode 100644 main/root.cpp create mode 100644 main/root.h diff --git a/core/account.cpp b/core/account.cpp index d158970..c27f2ea 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -55,6 +55,7 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& reconnectScheduled(false), reconnectTimer(new QTimer), network(p_net), + delay(nullptr), passwordType(Shared::AccountPassword::plain), lastError(Error::none), pepSupport(Shared::Support::unknown), @@ -67,6 +68,12 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& config.setPassword(p_password); config.setAutoAcceptSubscriptions(true); //config.setAutoReconnectionEnabled(false); + delay = new DelayManager::Manager(getBareJid()); + QObject::connect(delay, &DelayManager::Manager::gotInfo, this, &Account::infoReady); + QObject::connect(delay, &DelayManager::Manager::gotOwnInfo, this, &Account::infoReady); + + QObject::connect(delay, &DelayManager::Manager::requestOwnVCard, vm, &QXmppVCardManager::requestClientVCard); + QObject::connect(delay, &DelayManager::Manager::requestVCard, vm, &QXmppVCardManager::requestVCard); rh->initialize(); vh->initialize(); @@ -104,6 +111,9 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& client.addExtension(psm); #ifdef WITH_OMEMO + QObject::connect(delay, &DelayManager::Manager::requestBundles, oh, &OmemoHandler::requestBundles); + QObject::connect(delay, &DelayManager::Manager::requestOwnBundles, oh, &OmemoHandler::requestOwnBundles); + client.addExtension(tm); client.addExtension(om); om->setSecurityPolicy(QXmpp::Toakafa); @@ -164,6 +174,7 @@ Account::~Account() { delete mm; delete am; delete cm; + delete delay; } Shared::ConnectionState Core::Account::getState() const { @@ -313,7 +324,6 @@ void Core::Account::runDiscoveryService() { dm->requestInfo(getServer()); } - void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) { QString id = p_presence.from(); QStringList comps = id.split("/"); @@ -642,9 +652,9 @@ void Core::Account::setPepSupport(Shared::Support support) { void Core::Account::handleDisconnection() { setPepSupport(Shared::Support::unknown); + delay->disconnected(); cm->setCarbonsEnabled(false); rh->handleOffline(); - vh->handleOffline(); archiveQueries.clear(); } @@ -702,7 +712,9 @@ void Core::Account::setPasswordType(Shared::AccountPassword pt) { passwordType = pt; } void Core::Account::setLogin(const QString& p_login) { - config.setUser(p_login);} + config.setUser(p_login); + delay->setOwnJid(getBareJid()); +} void Core::Account::setName(const QString& p_name) { name = p_name;} @@ -713,7 +725,9 @@ void Core::Account::setPassword(const QString& p_password) { } void Core::Account::setServer(const QString& p_server) { - config.setDomain(p_server);} + config.setDomain(p_server); + delay->setOwnJid(getBareJid()); +} void Core::Account::sendMessage(const Shared::Message& data) { mh->sendMessage(data);} @@ -730,7 +744,8 @@ void Core::Account::replaceMessage(const QString& originalId, const Shared::Mess void Core::Account::requestInfo(const QString& jid) { //TODO switch case of what kind of entity this info request is about //right now it could be only about myself or some contact - vh->requestVCard(jid); + delay->getInfo(jid); + //vh->requestVCard(jid); } void Core::Account::updateInfo(const Shared::Info& info) { diff --git a/core/account.h b/core/account.h index 598a06e..e18966d 100644 --- a/core/account.h +++ b/core/account.h @@ -50,6 +50,7 @@ #include "contact.h" #include "conference.h" #include +#include #include "handlers/messagehandler.h" #include "handlers/rosterhandler.h" @@ -73,6 +74,10 @@ class Account : public QObject friend class RosterHandler; friend class VCardHandler; friend class DiscoveryHandler; +#ifdef WITH_OMEMO + friend class OmemoHandler; + friend class TrustHandler; +#endif public: enum class Error { authentication, @@ -202,6 +207,7 @@ private: QTimer* reconnectTimer; NetworkAccess* network; + DelayManager::Manager* delay; Shared::AccountPassword passwordType; Error lastError; Shared::Support pepSupport; diff --git a/core/delayManager/CMakeLists.txt b/core/delayManager/CMakeLists.txt index 96f4cef..6d046c5 100644 --- a/core/delayManager/CMakeLists.txt +++ b/core/delayManager/CMakeLists.txt @@ -1,5 +1,5 @@ set(SOURCE_FILES - delaymanager.cpp + manager.cpp job.cpp cardinternal.cpp infoforuser.cpp @@ -10,7 +10,7 @@ set(SOURCE_FILES ) set(HEADER_FILES - delaymanager.h + manager.h job.h cardinternal.h infoforuser.h diff --git a/core/delayManager/delaymanager.cpp b/core/delayManager/manager.cpp similarity index 90% rename from core/delayManager/delaymanager.cpp rename to core/delayManager/manager.cpp index 0ecc9a4..6a15544 100644 --- a/core/delayManager/delaymanager.cpp +++ b/core/delayManager/manager.cpp @@ -14,7 +14,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "delaymanager.h" +#include "manager.h" + +#include #include "cardinternal.h" #include "infoforuser.h" @@ -59,6 +61,9 @@ Core::DelayManager::Job::Id Core::DelayManager::Manager::getNextJobId() { } void Core::DelayManager::Manager::getInfo(const QString& jid) { + if (jid == ownJid) + return getOwnInfo(); + Job* job = nullptr; #ifdef WITH_OMEMO std::map::const_iterator bitr = requestedBundles.find(jid); @@ -91,10 +96,14 @@ void Core::DelayManager::Manager::getVCard(const QString& jid) { } void Core::DelayManager::Manager::getOwnVCard() { - if (ownInfoJobId == 0) + if (ownVCardJobId == 0) scheduleJob(new OwnCardInternal(getNextJobId())); } +bool Core::DelayManager::Manager::isOwnVCardPending() const { + return ownVCardJobId != 0; +} + Core::DelayManager::Job* Core::DelayManager::Manager::getVCardJob(const QString& jid) { Job* job = nullptr; std::map::const_iterator sitr = scheduledVCards.find(jid); @@ -207,7 +216,7 @@ void Core::DelayManager::Manager::jobIsCanceled(Job* job, bool wasRunning) { else scheduledVCards.erase(jb->jid); - emit receivedVCard(jb->jid, Shared::VCard()); + emit gotVCard(jb->jid, Shared::VCard()); } break; case Job::Type::infoForUser: { @@ -219,7 +228,7 @@ void Core::DelayManager::Manager::jobIsCanceled(Job* job, bool wasRunning) { else scheduledVCards.erase(jb->jid); - emit receivedVCard(jb->jid, Shared::VCard()); + emit gotVCard(jb->jid, Shared::VCard()); break; case InfoForUser::Stage::waitingForBundles: requestedBundles.erase(jb->jid); @@ -227,23 +236,23 @@ void Core::DelayManager::Manager::jobIsCanceled(Job* job, bool wasRunning) { default: break; } - emit receivedInfo(Shared::Info(jb->jid)); + emit gotInfo(Shared::Info(jb->jid)); } break; case Job::Type::ownInfoForUser: { OwnInfoForUser* jb = dynamic_cast(job); if (jb->getStage() == OwnInfoForUser::Stage::waitingForVCard) { ownVCardJobId = 0; - emit receivedOwnCard(Shared::VCard()); + emit gotOwnVCard(Shared::VCard()); } ownInfoJobId = 0; - emit receivedOwnInfo(Shared::Info (ownJid)); + emit gotOwnInfo(Shared::Info (ownJid)); } break; case Job::Type::ownCardInternal: ownVCardJobId = 0; - emit receivedOwnCard(Shared::VCard()); + emit gotOwnVCard(Shared::VCard()); break; } @@ -264,7 +273,8 @@ void Core::DelayManager::Manager::disconnected() { void Core::DelayManager::Manager::receivedVCard(const QString& jid, const Shared::VCard& card) { std::map::const_iterator cardItr = requestedVCards.find(jid); if (cardItr == requestedVCards.end()) { - throw 8575; //never supposed to happen, the state is not correct; + qDebug() << "received VCard for" << jid << "but it was never requested through manager, ignoring"; + return; } Job::Id jobId = cardItr->second; requestedVCards.erase(cardItr); @@ -272,7 +282,7 @@ void Core::DelayManager::Manager::receivedVCard(const QString& jid, const Shared switch (job->type) { case Job::Type::cardInternal: jobIsDone(jobId); - emit receivedCard(jid, card); + emit gotVCard(jid, card); break; case Job::Type::infoForUser: { #ifdef WITH_OMEMO @@ -283,10 +293,10 @@ void Core::DelayManager::Manager::receivedVCard(const QString& jid, const Shared #else Shared::Info info(jid); info.turnIntoContact(card); - emit receivedInfo(info); + emit gotInfo(info); jobIsDone(jobId); #endif - emit receivedCard(jid, card); + emit gotVCard(jid, card); } break; default: @@ -294,27 +304,27 @@ void Core::DelayManager::Manager::receivedVCard(const QString& jid, const Shared } } -void Core::DelayManager::Manager::ownVCardReceived(const Shared::VCard& card) { +void Core::DelayManager::Manager::receivedOwnVCard(const Shared::VCard& card) { Job::Id jobId = ownVCardJobId; ownVCardJobId = 0; Job* job = runningJobs.at(jobId); switch (job->type) { case Job::Type::ownCardInternal: jobIsDone(jobId); - emit receivedOwnCard(card); + emit gotOwnVCard(card); break; case Job::Type::ownInfoForUser: { #ifdef WITH_OMEMO OwnInfoForUser* jb = dynamic_cast(job); jb->receivedVCard(card); - emit requestOwnBundle(); + emit requestOwnBundles(); #else Shared::Info info(ownJid); info.turnIntoOwnAccount(card); - emit receivedOwnInfo(info); + emit gotOwnInfo(info); jobIsDone(jobId); #endif - emit receivedOwnCard(card); + emit gotOwnVCard(card); } break; default: @@ -335,7 +345,7 @@ void Core::DelayManager::Manager::receivedBundles(const QString& jid, const std: Shared::Info info(jid); info.turnIntoContact(job->claim(), new std::list(keys)); - emit receivedInfo(info); + emit gotInfo(info); jobIsDone(jobId); } @@ -347,7 +357,7 @@ void Core::DelayManager::Manager::receivedOwnBundles(const std::listclaim(), new std::list(keys)); - emit receivedOwnInfo(info); + emit gotOwnInfo(info); jobIsDone(jobId); } diff --git a/core/delayManager/delaymanager.h b/core/delayManager/manager.h similarity index 90% rename from core/delayManager/delaymanager.h rename to core/delayManager/manager.h index d859a18..61920ec 100644 --- a/core/delayManager/delaymanager.h +++ b/core/delayManager/manager.h @@ -43,26 +43,29 @@ public: Manager(const QString& ownJid, Job::Id maxParallelJobs = 5, QObject* parent = nullptr); ~Manager(); + void setOwnJid(const QString& jid); + bool isOwnVCardPending() const; + +public slots: void getOwnVCard(); void getOwnInfo(); void getVCard(const QString& jid); void getInfo(const QString& jid); - void setOwnJid(const QString& jid); signals: void requestVCard(const QString& jid); void requestOwnVCard(); void requestBundles(const QString& jid); - void requestOwnBundle(); + void requestOwnBundles(); - void receivedCard(const QString& jid, const Shared::VCard& info); - void receivedOwnCard(const Shared::VCard& info); - void receivedInfo(const Shared::Info& info); - void receivedOwnInfo(const Shared::Info& info); + void gotVCard(const QString& jid, const Shared::VCard& info); + void gotOwnVCard(const Shared::VCard& info); + void gotInfo(const Shared::Info& info); + void gotOwnInfo(const Shared::Info& info); public slots: void disconnected(); - void ownVCardReceived(const Shared::VCard& card); + void receivedOwnVCard(const Shared::VCard& card); void receivedVCard(const QString& jid, const Shared::VCard& card); void receivedBundles(const QString& jid, const std::list& keys); void receivedOwnBundles(const std::list& keys); diff --git a/core/handlers/omemohandler.cpp b/core/handlers/omemohandler.cpp index ce82a96..79eded9 100644 --- a/core/handlers/omemohandler.cpp +++ b/core/handlers/omemohandler.cpp @@ -19,7 +19,10 @@ #include "core/account.h" #include "core/adapterfunctions.h" +constexpr const char* ns_omemo_2 = "urn:xmpp:omemo:2"; + Core::OmemoHandler::OmemoHandler(Account* account) : + QObject(), QXmppOmemoStorage(), acc(account), ownDevice(std::nullopt), @@ -166,6 +169,51 @@ void Core::OmemoHandler::getDevices(const QString& jid, std::list task = acc->om->buildMissingSessions({jid}); + task.then(this, std::bind(&OmemoHandler::onBundlesReceived, this, jid)); +} + +void Core::OmemoHandler::requestOwnBundles() { + QXmppTask task = acc->om->buildMissingSessions({acc->getBareJid()}); + task.then(this, std::bind(&OmemoHandler::onOwnBundlesReceived, this)); +} + +void Core::OmemoHandler::onBundlesReceived(const QString& jid) { + std::list keys; + acc->oh->getDevices(jid, keys); + std::map trustLevels = acc->th->getKeys(ns_omemo_2, jid); + + qDebug() << "OMEMO info for " << jid << " devices:" << keys.size() << ", trustLevels:" << trustLevels.size(); + for (Shared::KeyInfo& key : keys) { + std::map::const_iterator itr = trustLevels.find(key.fingerPrint); + if (itr != trustLevels.end()) { + key.trustLevel = itr->second; + qDebug() << "Found a trust level for a device!"; + } + } + + acc->delay->receivedBundles(jid, keys); +} + +void Core::OmemoHandler::onOwnBundlesReceived() { + QString jid = acc->getBareJid(); + std::list keys; + acc->oh->getDevices(jid, keys); + std::map trustLevels = acc->th->getKeys(ns_omemo_2, jid); + + qDebug() << "OMEMO info for " << jid << " devices:" << keys.size() << ", trustLevels:" << trustLevels.size(); + for (Shared::KeyInfo& key : keys) { + std::map::const_iterator itr = trustLevels.find(key.fingerPrint); + if (itr != trustLevels.end()) { + key.trustLevel = itr->second; + qDebug() << "Found a trust level for a device!"; + } + } + + acc->delay->receivedOwnBundles(keys); +} + QDataStream & operator >> (QDataStream& in, QXmppOmemoStorage::Device& device) { in >> device.label; diff --git a/core/handlers/omemohandler.h b/core/handlers/omemohandler.h index 050677c..0d1021b 100644 --- a/core/handlers/omemohandler.h +++ b/core/handlers/omemohandler.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -32,8 +33,9 @@ Q_DECLARE_METATYPE(QXmppOmemoStorage::Device); namespace Core { class Account; -class OmemoHandler : public QXmppOmemoStorage +class OmemoHandler :public QObject, public QXmppOmemoStorage { + Q_OBJECT public: typedef std::pair SignedPreKeyPair; @@ -58,8 +60,14 @@ public: bool hasOwnDevice(); + void requestBundles(const QString& jid); + void requestOwnBundles(); void getDevices(const QString& jid, std::list& out) const; +private slots: + void onBundlesReceived(const QString& jid); + void onOwnBundlesReceived(); + private: Account* acc; std::optional ownDevice; diff --git a/core/handlers/rosterhandler.cpp b/core/handlers/rosterhandler.cpp index 0027514..cccd47d 100644 --- a/core/handlers/rosterhandler.cpp +++ b/core/handlers/rosterhandler.cpp @@ -156,7 +156,7 @@ void Core::RosterHandler::careAboutAvatar(Core::RosterItem* item, QMapvh->requestVCard(item->jid); + acc->delay->requestVCard(item->jid); } } @@ -197,7 +197,7 @@ void Core::RosterHandler::handleNewRosterItem(Core::RosterItem* contact) connect(contact, &RosterItem::historyResponse, this->acc, &Account::onContactHistoryResponse); connect(contact, &RosterItem::nameChanged, this, &RosterHandler::onContactNameChanged); connect(contact, &RosterItem::avatarChanged, this, &RosterHandler::onContactAvatarChanged); - connect(contact, &RosterItem::requestVCard, this->acc->vh, &VCardHandler::requestVCard); + connect(contact, &RosterItem::requestVCard, acc->delay, &DelayManager::Manager::getVCard); } void Core::RosterHandler::handleNewContact(Core::Contact* contact) diff --git a/core/handlers/rosterhandler.h b/core/handlers/rosterhandler.h index 11525be..62a7b8b 100644 --- a/core/handlers/rosterhandler.h +++ b/core/handlers/rosterhandler.h @@ -36,6 +36,7 @@ #include #include #include +#include namespace Core { diff --git a/core/handlers/trusthandler.cpp b/core/handlers/trusthandler.cpp index 50fbcbf..eb75219 100644 --- a/core/handlers/trusthandler.cpp +++ b/core/handlers/trusthandler.cpp @@ -89,8 +89,12 @@ QXmppTask Core::TrustHandler::trustLevel( const QString& keyOwnerJid, const QByteArray& keyId) { - Keys map = getCache(encryption)->getRecord(keyOwnerJid); - Shared::TrustLevel level = map.at(keyId); + KeyCache* cache = getCache(encryption); + Shared::TrustLevel level = Shared::TrustLevel::undecided; + try { + Keys map = cache->getRecord(keyOwnerJid); + level = map.at(keyId); + } catch (const DataBase::NotFound& e) {} return Core::makeReadyTask(std::move(convert(level))); } @@ -134,15 +138,22 @@ QXmppTask>> Core::TrustHandler::s for (MultySB::const_iterator itr = keyIds.begin(), end = keyIds.end(); itr != end; ++itr) { const QString& keyOwnerJid = itr.key(); const QByteArray& keyId = itr.value(); - Keys map = cache->getRecord(keyOwnerJid); - std::pair result = map.insert(std::make_pair(keyId, level)); - if (result.second) { + try { + Keys map = cache->getRecord(keyOwnerJid); + std::pair result = map.insert(std::make_pair(keyId, level)); + bool changed = result.second; + if (!changed && result.first->second != level) { + result.first->second = level; + changed = true; + } + if (changed) { + modifiedKeys[encryption].insert(keyOwnerJid, keyId); + cache->changeRecord(keyOwnerJid, map); + } + } catch (const DataBase::NotFound& e) { + Keys map({{keyId, level}}); modifiedKeys[encryption].insert(keyOwnerJid, keyId); - cache->changeRecord(keyOwnerJid, map); - } else if (result.first->second != level) { - result.first->second = level; - modifiedKeys[encryption].insert(keyOwnerJid, keyId); - cache->changeRecord(keyOwnerJid, map); + cache->addRecord(keyOwnerJid, map); } } return Core::makeReadyTask(std::move(modifiedKeys)); @@ -199,9 +210,8 @@ QXmppTask>> TrustHandle for (const std::pair& value : storage) { for (const std::pair& pair : value.second) { QXmpp::TrustLevel level = convert(pair.second); - if (!trustLevels || trustLevels.testFlag(level)) { + if (!trustLevels || trustLevels.testFlag(level)) res[level].insert(value.first, pair.first); - } } } return Core::makeReadyTask(std::move(res)); @@ -219,9 +229,8 @@ QXmppTask TrustHandler::removeKeys(const QString& encryption, const QStrin QXmppTask TrustHandler::removeKeys(const QString& encryption, const QList& keyIds) { std::set set; - for (const QByteArray& keyId : keyIds) { + for (const QByteArray& keyId : keyIds) set.insert(keyId); - } KeyCache* cache = getCache(encryption); std::map data = cache->readAll(); @@ -233,19 +242,16 @@ QXmppTask TrustHandler::removeKeys(const QString& encryption, const QList< if (set.erase(keyId)) { byOwner.erase(itr++); changed = true; - } else { + } else ++itr; - } } - if (byOwner.size() > 0) { + if (byOwner.size() > 0) data.erase(cItr++); - } else { + else ++cItr; - } } - if (changed) { + if (changed) cache->replaceAll(data); - } return Core::makeReadyTask(); } @@ -266,9 +272,8 @@ QXmppTask TrustHandler::addKeys( } catch (const DataBase::NotFound& e) {} for (const QByteArray& keyId : keyIds) { std::pair result = data.insert(std::make_pair(keyId, level)); - if (!result.second) { + if (!result.second) result.first->second = level; - } } if (had) { diff --git a/core/handlers/vcardhandler.cpp b/core/handlers/vcardhandler.cpp index 6c5cb5b..d4125e8 100644 --- a/core/handlers/vcardhandler.cpp +++ b/core/handlers/vcardhandler.cpp @@ -17,13 +17,9 @@ #include "vcardhandler.h" #include "core/account.h" -constexpr const char* ns_omemo_2 = "urn:xmpp:omemo:2"; - Core::VCardHandler::VCardHandler(Account* account): QObject(), acc(account), - ownVCardRequestInProgress(false), - pendingVCardRequests(), avatarHash(), avatarType() { @@ -90,36 +86,20 @@ void Core::VCardHandler::onVCardReceived(const QXmppVCardIq& card) { if (comps.size() > 1) { resource = comps.back(); } - pendingVCardRequests.erase(id); RosterItem* item = acc->rh->getRosterItem(jid); if (item == 0) { - if (jid == acc->getBareJid()) { + if (jid == acc->getBareJid()) onOwnVCardReceived(card); - } else { + else qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping"; - } + return; } - Shared::Info info(jid, Shared::EntryType::contact); - item->handleResponseVCard(card, resource, info.getVCardRef()); -#ifdef WITH_OMEMO - std::list& aks = info.getActiveKeysRef(); - acc->oh->getDevices(jid, aks); - std::map trustLevels = acc->th->getKeys(ns_omemo_2, jid); - - qDebug() << "OMEMO info for " << jid << " devices:" << aks.size() << ", trustLevels:" << trustLevels.size(); - for (Shared::KeyInfo& key : aks) { - std::map::const_iterator itr = trustLevels.find(key.fingerPrint); - if (itr != trustLevels.end()) { - key.trustLevel = itr->second; - qDebug() << "Found a trust level for a device!"; - } - } -#endif - - emit acc->infoReady(info); + Shared::VCard vCard; + item->handleResponseVCard(card, resource, vCard); + acc->delay->receivedVCard(id, vCard); } void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card) { @@ -201,10 +181,7 @@ void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card) { emit acc->changed(change); } - ownVCardRequestInProgress = false; - - Shared::Info info(acc->getBareJid(), Shared::EntryType::ownAccount); - Shared::VCard& vCard = info.getVCardRef(); + Shared::VCard vCard; initializeVCard(vCard, card); if (avatarType.size() > 0) { @@ -214,52 +191,24 @@ void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card) { vCard.setAvatarType(Shared::Avatar::empty); } - emit acc->infoReady(info); -} - -void Core::VCardHandler::handleOffline() { - pendingVCardRequests.clear(); - for (const QString& jid : pendingVCardRequests) { - Shared::Info info(jid, Shared::EntryType::none); - emit acc->infoReady(info); //need to show it better in the future, like with an error - } - pendingVCardRequests.clear(); - ownVCardRequestInProgress = false; -} - -void Core::VCardHandler::requestVCard(const QString& jid) { - if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) { - qDebug() << "requesting vCard" << jid; - if (jid == acc->getBareJid()) { - if (!ownVCardRequestInProgress) { - acc->vm->requestClientVCard(); - ownVCardRequestInProgress = true; - } - } else { - acc->vm->requestVCard(jid); - pendingVCardRequests.insert(jid); - } - } + if (acc->delay->isOwnVCardPending()) + acc->delay->receivedOwnVCard(vCard); } void Core::VCardHandler::handlePresenceOfMyAccountChange(const QXmppPresence& p_presence) { - if (!ownVCardRequestInProgress) { + if (!acc->delay->isOwnVCardPending()) { switch (p_presence.vCardUpdateType()) { case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo break; case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here break; case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any - if (avatarType.size() > 0) { - acc->vm->requestClientVCard(); - ownVCardRequestInProgress = true; - } + if (avatarType.size() > 0) + acc->delay->getOwnVCard(); break; case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load - if (avatarHash != p_presence.photoHash()) { - acc->vm->requestClientVCard(); - ownVCardRequestInProgress = true; - } + if (avatarHash != p_presence.photoHash()) + acc->delay->getOwnVCard(); break; } } diff --git a/core/handlers/vcardhandler.h b/core/handlers/vcardhandler.h index f774cd5..469398b 100644 --- a/core/handlers/vcardhandler.h +++ b/core/handlers/vcardhandler.h @@ -42,8 +42,6 @@ public: VCardHandler(Account* account); ~VCardHandler(); - void handleOffline(); - void requestVCard(const QString& jid); void handlePresenceOfMyAccountChange(const QXmppPresence& p_presence); void uploadVCard(const Shared::VCard& card); QString getAvatarPath() const; @@ -57,8 +55,6 @@ private slots: private: Account* acc; - bool ownVCardRequestInProgress; - std::set pendingVCardRequests; QString avatarHash; QString avatarType; }; diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 3c23932..b5bc725 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,7 +1,17 @@ -target_sources(squawk PRIVATE +set(SOURCE_FILES main.cpp application.cpp - application.h dialogqueue.cpp - dialogqueue.h + root.cpp +) + +set(HEADER_FILES + application.h + dialogqueue.h + root.h +) + +target_sources(squawk PRIVATE + ${SOURCE_FILES} + ${HEADER_FILES} ) diff --git a/main/main.cpp b/main/main.cpp index 9147ef0..5a7e034 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include "root.h" #include "shared/global.h" #include "shared/messageinfo.h" #include "shared/pathcheck.h" @@ -31,7 +32,6 @@ #include #include #include -#include #include #ifdef WITH_OMEMO @@ -59,13 +59,13 @@ int main(int argc, char *argv[]) qRegisterMetaType("QXmppOmemoStorage::Device"); #endif - QApplication app(argc, argv); + Root app(argc, argv); SignalCatcher sc(&app); - QApplication::setApplicationName("squawk"); - QApplication::setOrganizationName("macaw.me"); - QApplication::setApplicationDisplayName("Squawk"); - QApplication::setApplicationVersion("0.2.3"); + Root::setApplicationName("squawk"); + Root::setOrganizationName("macaw.me"); + Root::setApplicationDisplayName("Squawk"); + Root::setApplicationVersion("0.2.3"); app.setDesktopFileName("squawk"); QTranslator qtTranslator; @@ -97,7 +97,7 @@ int main(int argc, char *argv[]) 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); + Root::setWindowIcon(icon); new Shared::Global(); //translates enums @@ -139,7 +139,7 @@ int main(int argc, char *argv[]) //QObject::connect(&app, &QApplication::aboutToQuit, &w, &QMainWindow::close); QObject::connect(squawk, &Core::Squawk::quit, squawk, &Core::Squawk::deleteLater); QObject::connect(squawk, &Core::Squawk::destroyed, coreThread, &QThread::quit, Qt::QueuedConnection); - QObject::connect(coreThread, &QThread::finished, &app, &QApplication::quit, Qt::QueuedConnection); + QObject::connect(coreThread, &QThread::finished, &app, &Root::quit, Qt::QueuedConnection); coreThread->start(); int result = app.exec(); diff --git a/main/root.cpp b/main/root.cpp new file mode 100644 index 0000000..96b37b9 --- /dev/null +++ b/main/root.cpp @@ -0,0 +1,43 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "root.h" +#include +#include + +Root::Root(int& argc, char *argv[]) : + QApplication(argc, argv) + {} + +bool Root::notify(QObject* receiver, QEvent* e) { + try { + return QApplication::notify(receiver, e); + } catch(const std::runtime_error& e) { + qDebug() << "std::runtime_error in thread : " << QThread::currentThreadId(); + qDebug() << e.what(); + } catch(const std::exception& e) { + qDebug() << "std::exception in thread : " << QThread::currentThreadId(); + qDebug() << e.what(); + } catch(const int& e) { + qDebug() << "int exception in thread : " << QThread::currentThreadId(); + qDebug() << e; + } catch(...) { + qDebug() << "exception thread : " << QThread::currentThreadId(); + } + + qDebug() << "catch in notify "; + return false; +} diff --git a/main/root.h b/main/root.h new file mode 100644 index 0000000..02a82bc --- /dev/null +++ b/main/root.h @@ -0,0 +1,28 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 ROOT_H +#define ROOT_H + +#include + +class Root : public QApplication { +public: + Root(int& argc, char* argv[]); + bool notify(QObject* receiver, QEvent* e) override; +}; + +#endif // ROOT_H From 4b68da458f6058d979b60d0a87438da123cf25b1 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 11 Mar 2023 19:46:23 +0300 Subject: [PATCH 097/137] debugged a crash, keys are now fetching, refactored main, added some exceptions instead of ints, debugged termination process --- core/delayManager/job.h | 6 ++ core/delayManager/manager.cpp | 103 ++++++++++++++++---- core/delayManager/manager.h | 21 +++++ core/handlers/trusthandler.cpp | 4 +- external/qxmpp | 2 +- external/storage | 2 +- main/main.cpp | 104 +------------------- main/root.cpp | 167 +++++++++++++++++++++++++++++++-- main/root.h | 42 +++++++++ 9 files changed, 321 insertions(+), 130 deletions(-) diff --git a/core/delayManager/job.h b/core/delayManager/job.h index 633f2b2..6bfdc1d 100644 --- a/core/delayManager/job.h +++ b/core/delayManager/job.h @@ -32,6 +32,12 @@ public: infoForUser, ownInfoForUser }; + inline static constexpr const char * const TypeString[] = { + "cardInternal", + "ownCardInternal", + "infoForUser", + "ownInfoForUser" + }; protected: Job(Id id, Type type); Job(const Job& other); diff --git a/core/delayManager/manager.cpp b/core/delayManager/manager.cpp index 6a15544..3281bc6 100644 --- a/core/delayManager/manager.cpp +++ b/core/delayManager/manager.cpp @@ -67,8 +67,13 @@ void Core::DelayManager::Manager::getInfo(const QString& jid) { Job* job = nullptr; #ifdef WITH_OMEMO std::map::const_iterator bitr = requestedBundles.find(jid); - if (bitr != requestedBundles.end()) - job = runningJobs.at(bitr->second); + if (bitr != requestedBundles.end()) { + std::map::const_iterator itr = runningJobs.find(bitr->second); + if (itr == runningJobs.end()) + throw JobNotFound(bitr->second); + + job = itr->second; + } else #endif job = getVCardJob(jid); @@ -109,10 +114,19 @@ Core::DelayManager::Job* Core::DelayManager::Manager::getVCardJob(const QString& std::map::const_iterator sitr = scheduledVCards.find(jid); if (sitr == scheduledVCards.end()) { std::map::const_iterator ritr = requestedVCards.find(jid); - if (ritr != requestedVCards.end()) - job = runningJobs.at(ritr->second); + if (ritr != requestedVCards.end()) { + std::map::const_iterator itr = runningJobs.find(ritr->second); + if (itr == runningJobs.end()) + throw JobNotFound(ritr->second, "getVCardJob:1"); + + job = itr->second; + } } else { - job = *(scheduledJobsById.find(sitr->second)); + StorageById::const_iterator itr = scheduledJobsById.find(sitr->second); + if (itr == scheduledJobsById.end()) + throw JobNotFound(sitr->second, "getVCardJob:2"); + + job = *itr; } return job; @@ -177,7 +191,7 @@ void Core::DelayManager::Manager::executeJob(Job* job) { void Core::DelayManager::Manager::jobIsDone(Job::Id jobId) { std::map::const_iterator itr = runningJobs.find(jobId); if (itr == runningJobs.end()) { - throw 8573; //not supposed to happen, ever + throw JobNotFound(jobId, "jobIsDone"); } Job* job = itr->second; delete job; @@ -201,9 +215,8 @@ void Core::DelayManager::Manager::replaceJob(Job* job) { if (sitr != scheduledJobsById.end()) { delete *(sitr); scheduledJobsById.replace(sitr, job); - } else { - throw 8574; //not supposed to happen, ever - } + } else + throw JobNotFound(job->id, "replaceJob"); } } @@ -278,7 +291,12 @@ void Core::DelayManager::Manager::receivedVCard(const QString& jid, const Shared } Job::Id jobId = cardItr->second; requestedVCards.erase(cardItr); - Job* job = runningJobs.at(jobId); + std::map::const_iterator itr = runningJobs.find(jobId); + if (itr == runningJobs.end()) { + throw JobNotFound(jobId, "receivedVCard"); + } + Job* job = itr->second; + switch (job->type) { case Job::Type::cardInternal: jobIsDone(jobId); @@ -300,14 +318,22 @@ void Core::DelayManager::Manager::receivedVCard(const QString& jid, const Shared } break; default: - throw 8576; + throw UnexpectedJobType(job->type, "receivedVCard"); } } void Core::DelayManager::Manager::receivedOwnVCard(const Shared::VCard& card) { + if (ownVCardJobId == 0) { + qDebug() << "received own VCard for" << ownJid << "but it was never requested through manager, ignoring"; + return; + } Job::Id jobId = ownVCardJobId; ownVCardJobId = 0; - Job* job = runningJobs.at(jobId); + std::map::const_iterator itr = runningJobs.find(jobId); + if (itr == runningJobs.end()) { + throw JobNotFound(jobId, "receivedOwnVCard"); + } + Job* job = itr->second; switch (job->type) { case Job::Type::ownCardInternal: jobIsDone(jobId); @@ -328,19 +354,24 @@ void Core::DelayManager::Manager::receivedOwnVCard(const Shared::VCard& card) { } break; default: - throw 8576; + throw UnexpectedJobType(job->type, "receivedVCard"); } } void Core::DelayManager::Manager::receivedBundles(const QString& jid, const std::list& keys) { std::map::const_iterator itr = requestedBundles.find(jid); if (itr == requestedBundles.end()) { - throw 8577; //never supposed to happen, the state is not correct; + qDebug() << "received bundles for" << jid << "but they were never requested through manager, ignoring"; + return; } Job::Id jobId = itr->second; requestedBundles.erase(itr); - Job* jb = runningJobs.at(jobId); + std::map::const_iterator jitr = runningJobs.find(jobId); + if (jitr == runningJobs.end()) { + throw JobNotFound(jobId, "receivedBundles"); + } + Job* jb = jitr->second; InfoForUser* job = dynamic_cast(jb); Shared::Info info(jid); @@ -350,9 +381,17 @@ void Core::DelayManager::Manager::receivedBundles(const QString& jid, const std: } void Core::DelayManager::Manager::receivedOwnBundles(const std::list& keys) { + if (ownInfoJobId == 0) { + qDebug() << "received own bundles for" << ownJid << "but they were never requested through manager, ignoring"; + return; + } Job::Id jobId = ownInfoJobId; ownInfoJobId = 0; - Job* jb = runningJobs.at(jobId); + std::map::const_iterator jitr = runningJobs.find(jobId); + if (jitr == runningJobs.end()) { + throw JobNotFound(jobId, "receivedOwnBundles"); + } + Job* jb = jitr->second; OwnInfoForUser* job = dynamic_cast(jb); Shared::Info info(ownJid); @@ -364,3 +403,35 @@ void Core::DelayManager::Manager::receivedOwnBundles(const std::list(type)]; + if (method.size() > 0) { + msg += " in method " + method; + } + return msg; +} + +Core::DelayManager::Manager::JobNotFound::JobNotFound(Job::Id p_id, const std::string& p_method) : + Exception(), + id(p_id), + method(p_method) +{} + +std::string Core::DelayManager::Manager::JobNotFound::getMessage() const { + std::string msg("Job with id "); + msg += std::to_string(id); + msg += " was not found"; + if (method.size() > 0) { + msg += " in method " + method; + } + return msg; +} diff --git a/core/delayManager/manager.h b/core/delayManager/manager.h index 61920ec..127d25a 100644 --- a/core/delayManager/manager.h +++ b/core/delayManager/manager.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -30,6 +31,7 @@ #include #include +#include #include "job.h" @@ -121,6 +123,25 @@ private: std::map requestedBundles; #endif QString ownJid; + +public: + class UnexpectedJobType: public Utils::Exception { + public: + UnexpectedJobType(Job::Type p_type, const std::string& p_method = ""); + std::string getMessage() const override; + private: + Job::Type type; + std::string method; + }; + + class JobNotFound: public Utils::Exception { + public: + JobNotFound(Job::Id p_id, const std::string& p_method = ""); + std::string getMessage() const override; + private: + Job::Id id; + std::string method; + }; }; } diff --git a/core/handlers/trusthandler.cpp b/core/handlers/trusthandler.cpp index eb75219..647d568 100644 --- a/core/handlers/trusthandler.cpp +++ b/core/handlers/trusthandler.cpp @@ -93,7 +93,9 @@ QXmppTask Core::TrustHandler::trustLevel( Shared::TrustLevel level = Shared::TrustLevel::undecided; try { Keys map = cache->getRecord(keyOwnerJid); - level = map.at(keyId); + Keys::const_iterator itr = map.find(keyId); + if (itr != map.end()) + level = itr->second; } catch (const DataBase::NotFound& e) {} return Core::makeReadyTask(std::move(convert(level))); } diff --git a/external/qxmpp b/external/qxmpp index 9d57624..d679ad1 160000 --- a/external/qxmpp +++ b/external/qxmpp @@ -1 +1 @@ -Subproject commit 9d5762499fbddb3dd1ed8eeca16f9db70adc27d0 +Subproject commit d679ad1c49eeb28be2ac3a75bd7fd1a9be24d483 diff --git a/external/storage b/external/storage index a79dae8..6a8f67a 160000 --- a/external/storage +++ b/external/storage @@ -1 +1 @@ -Subproject commit a79dae8fd07b36446041f6f85e2ad42cf5ae5264 +Subproject commit 6a8f67ac34de286588cd89e3218f55b54da47f42 diff --git a/main/main.cpp b/main/main.cpp index 5a7e034..afe1cf2 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -17,22 +17,11 @@ */ #include "root.h" -#include "shared/global.h" #include "shared/messageinfo.h" -#include "shared/pathcheck.h" #include "shared/identity.h" #include "shared/info.h" -#include "main/application.h" -#include "core/signalcatcher.h" -#include "core/squawk.h" -#include -#include -#include -#include -#include -#include -#include +#include #ifdef WITH_OMEMO #include @@ -60,97 +49,10 @@ int main(int argc, char *argv[]) #endif Root app(argc, argv); - SignalCatcher sc(&app); - - Root::setApplicationName("squawk"); - Root::setOrganizationName("macaw.me"); - Root::setApplicationDisplayName("Squawk"); - Root::setApplicationVersion("0.2.3"); - app.setDesktopFileName("squawk"); - - QTranslator qtTranslator; - qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); - app.installTranslator(&qtTranslator); - - QTranslator myappTranslator; - QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); - bool found = false; - for (QString share : shares) { - found = myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n"); - if (found) { - break; - } - } - if (!found) { - myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath()); - } - - app.installTranslator(&myappTranslator); - - 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)); - Root::setWindowIcon(icon); - - new Shared::Global(); //translates enums - - QSettings settings; - QVariant vs = settings.value("style"); - if (vs.isValid()) { - QString style = vs.toString().toLower(); - if (style != "system") { - Shared::Global::setStyle(style); - } - } - if (Shared::Global::supported("colorSchemeTools")) { - QVariant vt = settings.value("theme"); - if (vt.isValid()) { - QString theme = vt.toString(); - if (theme.toLower() != "system") { - Shared::Global::setTheme(theme); - } - } - } - QString path = Shared::downloadsPathCheck(); - if (path.size() > 0) { - settings.setValue("downloadsPath", path); - } else { - qDebug() << "couldn't initialize directory for downloads, quitting"; + if (!app.initializeSettings()) return -1; - } - - Core::Squawk* squawk = new Core::Squawk(); - QThread* coreThread = new QThread(); - squawk->moveToThread(coreThread); - - Application application(squawk); - QObject::connect(&sc, &SignalCatcher::interrupt, &application, &Application::quit); - - QObject::connect(coreThread, &QThread::started, squawk, &Core::Squawk::start); - QObject::connect(&application, &Application::quitting, squawk, &Core::Squawk::stop); - //QObject::connect(&app, &QApplication::aboutToQuit, &w, &QMainWindow::close); - QObject::connect(squawk, &Core::Squawk::quit, squawk, &Core::Squawk::deleteLater); - QObject::connect(squawk, &Core::Squawk::destroyed, coreThread, &QThread::quit, Qt::QueuedConnection); - QObject::connect(coreThread, &QThread::finished, &app, &Root::quit, Qt::QueuedConnection); - coreThread->start(); - int result = app.exec(); - - if (coreThread->isRunning()) { - //coreThread->wait(); - //todo if I uncomment that, the app will not quit if it has reconnected at least once - //it feels like a symptom of something badly desinged in the core thread - //need to investigate; - } - - return result; + return app.run(); } diff --git a/main/root.cpp b/main/root.cpp index 96b37b9..d43ae2f 100644 --- a/main/root.cpp +++ b/main/root.cpp @@ -17,27 +17,174 @@ #include "root.h" #include #include +#include +#include + +#include + +#include + +const std::vector Root::appIconSizes({ + 16, 24, 32, 48, 64, 96, 128, 256, 512 +}); Root::Root(int& argc, char *argv[]) : - QApplication(argc, argv) - {} + QApplication(argc, argv), + signalCatcher(this), + defaultTranslator(), + currentTranslator(), + appIcon(), + settings(), + componentsInitialized(false), + global(nullptr), + coreThread(nullptr), + core(nullptr), + gui(nullptr) +{ + setApplicationName("squawk"); + setOrganizationName("macaw.me"); + setApplicationDisplayName("Squawk"); + setApplicationVersion("0.2.3"); + setDesktopFileName("squawk"); + + initializeTranslation(); + initializeAppIcon(); + + global = new Shared::Global(); //important to instantiate after initialization of translations; +} + +Root::~Root() { + if (componentsInitialized) { + delete gui; + if (core != nullptr) + delete core; + delete coreThread; + } + delete global; +} + + +void Root::initializeTranslation() { + defaultTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); + installTranslator(&defaultTranslator); + + QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); + bool found = false; + for (QString share : shares) { + found = currentTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n"); + if (found) { + break; + } + } + if (!found) { + currentTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath()); + } + + installTranslator(¤tTranslator); +} + +void Root::initializeAppIcon() { + for (std::vector::size_type i = 0; i < appIconSizes.size(); ++i) + appIcon.addFile(":images/logo.svg", QSize(appIconSizes[i], appIconSizes[i])); + + Root::setWindowIcon(appIcon); +} + +bool Root::initializeSettings() { + QVariant vs = settings.value("style"); + if (vs.isValid()) { + QString style = vs.toString().toLower(); + if (style != "system") { + Shared::Global::setStyle(style); + } + } + + if (Shared::Global::supported("colorSchemeTools")) { + QVariant vt = settings.value("theme"); + if (vt.isValid()) { + QString theme = vt.toString(); + if (theme.toLower() != "system") { + Shared::Global::setTheme(theme); + } + } + } + + QString path = Shared::downloadsPathCheck(); + if (path.size() > 0) { + settings.setValue("downloadsPath", path); + } else { + qDebug() << "couldn't initialize directory for downloads, quitting"; + return false; + } + + return true; +} + +int Root::run() { + if (!componentsInitialized) + initializeComponents(); + + coreThread->start(); + int result = exec(); + qDebug("Event loop stopped"); + + if (result == 0) { + processEvents(); //I dont like all of this mess + if (coreThread->isRunning()) { //but it's the best solution for now + if (core != nullptr) { //Ideally, following line should never appear in the log + qDebug() << "Core is still seems to be running, killing manually"; + core->deleteLater(); + coreThread->quit(); + processEvents(); + core = nullptr; + } + coreThread->wait(); + } + } + + return result; +} + +void Root::initializeComponents() { + core = new Core::Squawk(); + coreThread = new QThread(); + core->moveToThread(coreThread); + gui = new Application(core); + + QObject::connect(&signalCatcher, &SignalCatcher::interrupt, gui, &Application::quit); + + QObject::connect(coreThread, &QThread::started, core, &Core::Squawk::start); + QObject::connect(gui, &Application::quitting, core, &Core::Squawk::stop); + + QObject::connect(core, &Core::Squawk::quit, core, &Core::Squawk::deleteLater); + QObject::connect(core, &Core::Squawk::destroyed, this, &Root::onCoreDestroyed); + QObject::connect(core, &Core::Squawk::destroyed, coreThread, &QThread::quit, Qt::QueuedConnection); + QObject::connect(coreThread, &QThread::finished, this, &Root::quit, Qt::QueuedConnection); + + componentsInitialized = true; +} bool Root::notify(QObject* receiver, QEvent* e) { try { return QApplication::notify(receiver, e); } catch(const std::runtime_error& e) { - qDebug() << "std::runtime_error in thread : " << QThread::currentThreadId(); - qDebug() << e.what(); + qDebug() << "std::runtime_error in thread:" << QThread::currentThreadId(); + qDebug() << "error message:" << e.what(); } catch(const std::exception& e) { - qDebug() << "std::exception in thread : " << QThread::currentThreadId(); - qDebug() << e.what(); + qDebug() << "std::exception in thread:" << QThread::currentThreadId(); + qDebug() << "error message:" << e.what(); } catch(const int& e) { - qDebug() << "int exception in thread : " << QThread::currentThreadId(); - qDebug() << e; + qDebug() << "integer exception in thread:" << QThread::currentThreadId(); + qDebug() << "thrown integer:" << std::to_string(e).c_str(); } catch(...) { - qDebug() << "exception thread : " << QThread::currentThreadId(); + qDebug() << "unhandled exception thread:" << QThread::currentThreadId(); } - qDebug() << "catch in notify "; + qDebug() << "Squawk is crashing..."; + exit(1); return false; } + +void Root::onCoreDestroyed() { + core = nullptr; +} diff --git a/main/root.h b/main/root.h index 02a82bc..003d9cb 100644 --- a/main/root.h +++ b/main/root.h @@ -18,11 +18,53 @@ #define ROOT_H #include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include "application.h" class Root : public QApplication { + Q_OBJECT public: Root(int& argc, char* argv[]); + ~Root(); bool notify(QObject* receiver, QEvent* e) override; + int run(); + + bool initializeSettings(); + +private slots: + void onCoreDestroyed(); + +private: + void initializeTranslation(); + void initializeAppIcon(); + void initializeComponents(); + +private: + static const std::vector appIconSizes; + + SignalCatcher signalCatcher; + QTranslator defaultTranslator; + QTranslator currentTranslator; + QIcon appIcon; + QSettings settings; + bool componentsInitialized; + + Shared::Global* global; + QThread* coreThread; + Core::Squawk* core; + Application* gui; + }; #endif // ROOT_H From 8ec0af3205d8297b8d92ef7140bb67d628a5f646 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 12 Mar 2023 01:38:54 +0300 Subject: [PATCH 098/137] transition to QXMppCarbonManagerV2 if QXmpp version is heigher than 1.5.0 --- core/account.cpp | 8 ++++++++ core/account.h | 8 ++++++++ core/handlers/discoveryhandler.cpp | 2 ++ core/handlers/messagehandler.cpp | 10 ++++++++-- core/handlers/messagehandler.h | 2 ++ 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index c27f2ea..a0b7311 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -42,7 +42,11 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& tm(new QXmppTrustManager(th)), om(new QXmppOmemoManager(oh)), #endif +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) + cm(new QXmppCarbonManagerV2()), +#else cm(new QXmppCarbonManager()), +#endif am(new QXmppMamManager()), mm(new QXmppMucManager()), bm(new QXmppBookmarkManager()), @@ -86,8 +90,10 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& client.addExtension(cm); +#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 5, 0) QObject::connect(cm, &QXmppCarbonManager::messageReceived, mh, &MessageHandler::onCarbonMessageReceived); QObject::connect(cm, &QXmppCarbonManager::messageSent, mh, &MessageHandler::onCarbonMessageSent); +#endif client.addExtension(am); @@ -653,7 +659,9 @@ void Core::Account::setPepSupport(Shared::Support support) { void Core::Account::handleDisconnection() { setPepSupport(Shared::Support::unknown); delay->disconnected(); +#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 5, 0) cm->setCarbonsEnabled(false); +#endif rh->handleOffline(); archiveQueries.clear(); } diff --git a/core/account.h b/core/account.h index e18966d..58ff9b5 100644 --- a/core/account.h +++ b/core/account.h @@ -32,7 +32,11 @@ #include #include +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) +#include +#else #include +#endif #include #include #include @@ -193,7 +197,11 @@ private: QXmppTrustManager* tm; QXmppOmemoManager* om; #endif +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) + QXmppCarbonManagerV2* cm; +#else QXmppCarbonManager* cm; +#endif QXmppMamManager* am; QXmppMucManager* mm; QXmppBookmarkManager* bm; diff --git a/core/handlers/discoveryhandler.cpp b/core/handlers/discoveryhandler.cpp index 977dda2..6ed06c6 100644 --- a/core/handlers/discoveryhandler.cpp +++ b/core/handlers/discoveryhandler.cpp @@ -79,7 +79,9 @@ void Core::DiscoveryHandler::onInfoReceived(const QXmppDiscoveryIq& info) if (enableCC) { qDebug() << "Enabling carbon copies for account" << accName; +#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 5, 0) acc->cm->setCarbonsEnabled(true); +#endif #ifdef WITH_OMEMO if (!omemoToCarbonsConnected && acc->oh->hasOwnDevice()) { // connect(this, &QXmppCarbonManager::messageSent, acc->om, &QXmppOmemoManager::handleMessage); diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index b11ea2a..2c5b16d 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -64,7 +64,11 @@ void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) qDebug() << "received a message with type \"Normal\", not sure what to do with it now, skipping"; break; case QXmppMessage::Chat: +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) + handled = handleChatMessage(msg, false, msg.isCarbonForwarded(), true); +#else handled = handleChatMessage(msg); +#endif break; case QXmppMessage::GroupChat: handled = handleGroupMessage(msg); @@ -109,8 +113,8 @@ bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgo cnt = acc->rh->addOutOfRosterContact(jid); qDebug() << "appending message" << sMsg.getId() << "to an out of roster contact"; } - if (outgoing) { - if (forwarded) { + if (sMsg.getOutgoing()) { + if (sMsg.getForwarded()) { sMsg.setState(Shared::Message::State::sent); } } else { @@ -242,6 +246,7 @@ void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& re qDebug() << "=============================="; } +#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 5, 0) void Core::MessageHandler::onCarbonMessageReceived(const QXmppMessage& msg) { handleChatMessage(msg, false, true); @@ -251,6 +256,7 @@ void Core::MessageHandler::onCarbonMessageSent(const QXmppMessage& msg) { handleChatMessage(msg, true, true); } +#endif std::tuple Core::MessageHandler::getOriginalPendingMessageId(const QString& id) { diff --git a/core/handlers/messagehandler.h b/core/handlers/messagehandler.h index 1ab2d0d..a08fee8 100644 --- a/core/handlers/messagehandler.h +++ b/core/handlers/messagehandler.h @@ -52,8 +52,10 @@ public: public slots: void onMessageReceived(const QXmppMessage& message); +#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 5, 0) void onCarbonMessageReceived(const QXmppMessage& message); void onCarbonMessageSent(const QXmppMessage& message); +#endif void onReceiptReceived(const QString& jid, const QString& id); void onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot); void onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request); From 76a9c5da0cf734e854e2dc49af47ad2e8a4394d4 Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 13 Mar 2023 22:07:10 +0300 Subject: [PATCH 099/137] extracted clientId from clientInfo to use it in the presence information later --- core/components/clientcache.cpp | 6 ++-- shared/CMakeLists.txt | 46 +++++++++++++++++------------ shared/clientid.cpp | 52 +++++++++++++++++++++++++++++++++ shared/clientid.h | 45 ++++++++++++++++++++++++++++ shared/clientinfo.cpp | 18 ++++-------- shared/clientinfo.h | 8 ++--- 6 files changed, 137 insertions(+), 38 deletions(-) create mode 100644 shared/clientid.cpp create mode 100644 shared/clientid.h diff --git a/core/components/clientcache.cpp b/core/components/clientcache.cpp index a312b24..dfd9680 100644 --- a/core/components/clientcache.cpp +++ b/core/components/clientcache.cpp @@ -42,9 +42,9 @@ bool Core::ClientCache::checkClient(const QString& node, const QString& ver, con QString id = node + "/" + ver; if (requested.count(id) == 0 && !cache->checkRecord(id)) { Shared::ClientInfo& info = requested.insert(std::make_pair(id, Shared::ClientInfo())).first->second; - info.node = node; - info.verification = ver; - info.hash = hash; + info.id.node = node; + info.id.verification = ver; + info.id.hash = hash; emit requestClientInfo(id); return false; } diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index 9080fb6..5db96f0 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -1,33 +1,43 @@ -target_sources(squawk PRIVATE - enums.h +set(SOURCE_FILES global.cpp - global.h exception.cpp - exception.h icons.cpp - icons.h message.cpp - message.h messageinfo.cpp - messageinfo.h - order.h - shared.h utils.cpp - utils.h vcard.cpp - vcard.h pathcheck.cpp - pathcheck.h - clientinfo.h clientinfo.cpp - identity.h identity.cpp - form.h form.cpp - field.h field.cpp keyinfo.cpp - keyinfo.h info.cpp + clientid.cpp +) + +set(HEADER_FILES + order.h + shared.h + enums.h + global.h + exception.h + icons.h + message.h + messageinfo.h + utils.h + vcard.h + pathcheck.h + clientinfo.h + identity.h + form.h + field.h + keyinfo.h info.h - ) + clientid.h +) + +target_sources(squawk PRIVATE + ${SOURCE_FILES} + ${HEADER_FILES} +) diff --git a/shared/clientid.cpp b/shared/clientid.cpp new file mode 100644 index 0000000..f16736c --- /dev/null +++ b/shared/clientid.cpp @@ -0,0 +1,52 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "clientid.h" + +Shared::ClientId::ClientId(): + node(), + verification(), + hash() +{} + +QString Shared::ClientId::getId() const { + return node + "/" + verification; +} + +QDataStream & Shared::ClientId::operator<<(QDataStream& stream) { + stream >> node; + stream >> verification; + stream >> hash; + return stream; +} + +QDataStream & Shared::ClientId::operator>>(QDataStream& stream) const { + stream << node; + stream << verification; + stream << hash; + return stream; +} + +QDataStream & operator<<(QDataStream& stream, const Shared::ClientId& info) { + info >> stream; + return stream; +} + +QDataStream & operator>>(QDataStream& stream, Shared::ClientId& info) { + info << stream; + return stream; +} + diff --git a/shared/clientid.h b/shared/clientid.h new file mode 100644 index 0000000..defe909 --- /dev/null +++ b/shared/clientid.h @@ -0,0 +1,45 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 SHARED_CLIENTID_H +#define SHARED_CLIENTID_H + +#include +#include + +namespace Shared { + +class ClientId { +public: + ClientId(); + + QString getId() const; + + QDataStream& operator << (QDataStream& stream); + QDataStream& operator >> (QDataStream& stream) const; + +public: + QString node; + QString verification; + QString hash; +}; + +} + +QDataStream& operator << (QDataStream& stream, const Shared::ClientId& info); +QDataStream& operator >> (QDataStream& stream, Shared::ClientId& info); + +#endif // SHARED_CLIENTID_H diff --git a/shared/clientinfo.cpp b/shared/clientinfo.cpp index 86af1c8..9a3fdac 100644 --- a/shared/clientinfo.cpp +++ b/shared/clientinfo.cpp @@ -32,20 +32,16 @@ const std::map Shared::ClientInfo::hashe Shared::ClientInfo::ClientInfo(): identities(), extensions(), - node(), - verification(), - hash(), + id(), specificPresence() {} QString Shared::ClientInfo::getId() const { - return node + "/" + verification; + return id.getId(); } QDataStream & Shared::ClientInfo::operator >> (QDataStream& stream) const { - stream << node; - stream << verification; - stream << hash; + stream << id; stream << (quint8)identities.size(); for (const Shared::Identity& identity : identities) { stream << identity; @@ -59,9 +55,7 @@ QDataStream & Shared::ClientInfo::operator >> (QDataStream& stream) const { } QDataStream & Shared::ClientInfo::operator << (QDataStream& stream) { - stream >> node; - stream >> verification; - stream >> hash; + stream >> id; quint8 size; stream >> size; @@ -82,7 +76,7 @@ QDataStream & Shared::ClientInfo::operator << (QDataStream& stream) { } bool Shared::ClientInfo::valid() const { - std::map::const_iterator itr = hashes.find(hash); + std::map::const_iterator itr = hashes.find(id.hash); if (itr == hashes.end()) { return false; } @@ -98,7 +92,7 @@ bool Shared::ClientInfo::valid() const { QString result = calc.result().toBase64(); - return result == verification; + return result == id.verification; } QDataStream& operator << (QDataStream& stream, const Shared::ClientInfo& info) { diff --git a/shared/clientinfo.h b/shared/clientinfo.h index 53c7dd0..8e95180 100644 --- a/shared/clientinfo.h +++ b/shared/clientinfo.h @@ -24,11 +24,11 @@ #include #include +#include namespace Shared { -class ClientInfo -{ +class ClientInfo { public: ClientInfo(); @@ -41,9 +41,7 @@ public: public: std::set identities; std::set extensions; - QString node; - QString verification; - QString hash; + ClientId id; QString specificPresence; private: From 21b40a9ccbf6a89bc41ed143780359103b996a85 Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 14 Mar 2023 22:49:58 +0300 Subject: [PATCH 100/137] Client node now displays in all participants and presences, some additional checkups before querying empty clients, refactoring --- core/account.cpp | 10 +- core/account.h | 1 + core/components/clientcache.cpp | 10 +- core/components/clientcache.h | 3 +- core/conference.cpp | 18 ++- core/conference.h | 3 +- core/handlers/trusthandler.cpp | 8 +- core/squawk.cpp | 232 +++++++++++------------------- shared/clientid.cpp | 115 +++++++++++++++ shared/clientid.h | 13 ++ shared/clientinfo.cpp | 13 +- shared/clientinfo.h | 2 + ui/models/CMakeLists.txt | 58 ++++---- ui/models/abstractparticipant.cpp | 83 ++++++----- ui/models/abstractparticipant.h | 12 +- ui/models/participant.cpp | 127 ++++++++-------- ui/models/presence.cpp | 10 +- ui/models/presence.h | 2 - ui/models/roster.cpp | 4 +- 19 files changed, 407 insertions(+), 317 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index a0b7311..9740029 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -360,9 +360,13 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) { {"lastActivity", lastInteraction}, {"availability", p_presence.availableStatusType()}, //TODO check and handle invisible {"status", p_presence.statusText()}, - {"capabilityNode", p_presence.capabilityNode()}, - {"capabilityVer", p_presence.capabilityVer().toBase64()}, - {"capabilityHash", p_presence.capabilityHash()} + {"client", QVariant::fromValue( + Shared::ClientId( + p_presence.capabilityNode(), + p_presence.capabilityVer().toBase64(), + p_presence.capabilityHash()) + ) + } }); } break; diff --git a/core/account.h b/core/account.h index 58ff9b5..b257d2b 100644 --- a/core/account.h +++ b/core/account.h @@ -51,6 +51,7 @@ #include #include #include +#include #include "contact.h" #include "conference.h" #include diff --git a/core/components/clientcache.cpp b/core/components/clientcache.cpp index dfd9680..fe1fa64 100644 --- a/core/components/clientcache.cpp +++ b/core/components/clientcache.cpp @@ -37,14 +37,10 @@ void Core::ClientCache::open() { void Core::ClientCache::close() { db.close();} - -bool Core::ClientCache::checkClient(const QString& node, const QString& ver, const QString& hash) { - QString id = node + "/" + ver; +bool Core::ClientCache::checkClient(const Shared::ClientId& p_id) { + QString id = p_id.getId(); if (requested.count(id) == 0 && !cache->checkRecord(id)) { - Shared::ClientInfo& info = requested.insert(std::make_pair(id, Shared::ClientInfo())).first->second; - info.id.node = node; - info.id.verification = ver; - info.id.hash = hash; + requested.emplace(id, p_id); emit requestClientInfo(id); return false; } diff --git a/core/components/clientcache.h b/core/components/clientcache.h index 640def3..6202af5 100644 --- a/core/components/clientcache.h +++ b/core/components/clientcache.h @@ -25,6 +25,7 @@ #include +#include #include #include @@ -43,7 +44,7 @@ signals: void requestClientInfo(const QString& id); public slots: - bool checkClient(const QString& node, const QString& ver, const QString& hash); + bool checkClient(const Shared::ClientId& id); bool registerClientInfo(const QString& sourceFullJid, const QString& id, const std::set& identities, const std::set& features); private: diff --git a/core/conference.cpp b/core/conference.cpp index 065490c..2e49625 100644 --- a/core/conference.cpp +++ b/core/conference.cpp @@ -157,7 +157,14 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name) {"availability", pres.availableStatusType()}, {"status", pres.statusText()}, {"affiliation", mi.affiliation()}, - {"role", mi.role()} + {"role", mi.role()}, + {"client", QVariant::fromValue( + Shared::ClientId( + pres.capabilityNode(), + pres.capabilityVer().toBase64(), + pres.capabilityHash()) + ) + } }; if (hasAvatar) { @@ -218,7 +225,14 @@ void Core::Conference::onRoomParticipantChanged(const QString& p_name) {"availability", pres.availableStatusType()}, {"status", pres.statusText()}, {"affiliation", mi.affiliation()}, - {"role", mi.role()} + {"role", mi.role()}, + {"client", QVariant::fromValue( + Shared::ClientId( + pres.capabilityNode(), + pres.capabilityVer().toBase64(), + pres.capabilityHash()) + ) + } }); } } diff --git a/core/conference.h b/core/conference.h index 41141ad..00ade47 100644 --- a/core/conference.h +++ b/core/conference.h @@ -26,7 +26,8 @@ #include #include "rosteritem.h" -#include "shared/global.h" +#include +#include namespace Core { diff --git a/core/handlers/trusthandler.cpp b/core/handlers/trusthandler.cpp index 647d568..cc97e57 100644 --- a/core/handlers/trusthandler.cpp +++ b/core/handlers/trusthandler.cpp @@ -347,8 +347,7 @@ Core::TrustHandler::Keys Core::TrustHandler::getKeys(const QString& protocol, co } } -Shared::TrustLevel Core::TrustHandler::convert(Core::TrustHandler::TL level) -{ +Shared::TrustLevel Core::TrustHandler::convert(Core::TrustHandler::TL level) { switch (level) { case QXmpp::TrustLevel::Undecided: return Shared::TrustLevel::undecided; case QXmpp::TrustLevel::AutomaticallyDistrusted: return Shared::TrustLevel::automaticallyDistrusted; @@ -356,11 +355,11 @@ Shared::TrustLevel Core::TrustHandler::convert(Core::TrustHandler::TL level) case QXmpp::TrustLevel::AutomaticallyTrusted: return Shared::TrustLevel::automaticallyTrusted; case QXmpp::TrustLevel::ManuallyTrusted: return Shared::TrustLevel::manuallyTrusted; case QXmpp::TrustLevel::Authenticated: return Shared::TrustLevel::authenticated; + default: throw 2413; //never supposed to get here, switch case if complete, this line is just to suppress a warning } } -Core::TrustHandler::TL Core::TrustHandler::convert(Shared::TrustLevel level) -{ +Core::TrustHandler::TL Core::TrustHandler::convert(Shared::TrustLevel level) { switch (level) { case Shared::TrustLevel::undecided: return QXmpp::TrustLevel::Undecided; case Shared::TrustLevel::automaticallyDistrusted: return QXmpp::TrustLevel::AutomaticallyDistrusted; @@ -368,5 +367,6 @@ Core::TrustHandler::TL Core::TrustHandler::convert(Shared::TrustLevel level) case Shared::TrustLevel::automaticallyTrusted: return QXmpp::TrustLevel::AutomaticallyTrusted; case Shared::TrustLevel::manuallyTrusted: return QXmpp::TrustLevel::ManuallyTrusted; case Shared::TrustLevel::authenticated: return QXmpp::TrustLevel::Authenticated; + default: throw 2413; //never supposed to get here, switch case if complete, this line is just to suppress a warning } } diff --git a/core/squawk.cpp b/core/squawk.cpp index 33f2bf5..b7a0aad 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -50,8 +50,7 @@ Core::Squawk::Squawk(QObject* parent): #endif } -Core::Squawk::~Squawk() -{ +Core::Squawk::~Squawk() { Accounts::const_iterator itr = accounts.begin(); Accounts::const_iterator end = accounts.end(); for (; itr != end; ++itr) { @@ -59,13 +58,11 @@ Core::Squawk::~Squawk() } } -void Core::Squawk::onWalletOpened(bool success) -{ +void Core::Squawk::onWalletOpened(bool success) { qDebug() << "KWallet opened: " << success; } -void Core::Squawk::stop() -{ +void Core::Squawk::stop() { qDebug("Stopping squawk core.."); network.stop(); clientCache.close(); @@ -110,8 +107,7 @@ void Core::Squawk::stop() emit quit(); } -void Core::Squawk::start() -{ +void Core::Squawk::start() { qDebug("Starting squawk core.."); readSettings(); @@ -120,8 +116,7 @@ void Core::Squawk::start() clientCache.open(); } -void Core::Squawk::newAccountRequest(const QMap& map) -{ +void Core::Squawk::newAccountRequest(const QMap& map) { QString name = map.value("name").toString(); QString login = map.value("login").toString(); QString server = map.value("server").toString(); @@ -205,31 +200,28 @@ void Core::Squawk::addAccount( switch (passwordType) { case Shared::AccountPassword::alwaysAsk: case Shared::AccountPassword::kwallet: - if (password == "") { + if (password == "") acc->invalidatePassword(); - break; - } + + break; default: break; } if (state != Shared::Availability::offline) { acc->setAvailability(state); - if (acc->getActive()) { + if (acc->getActive()) acc->connect(); - } } } -void Core::Squawk::changeState(Shared::Availability p_state) -{ +void Core::Squawk::changeState(Shared::Availability p_state) { if (state != p_state) { for (std::deque::iterator itr = accounts.begin(), end = accounts.end(); itr != end; ++itr) { Account* acc = *itr; acc->setAvailability(p_state); - if (state == Shared::Availability::offline && acc->getActive()) { + if (state == Shared::Availability::offline && acc->getActive()) acc->connect(); - } } state = p_state; @@ -237,21 +229,18 @@ void Core::Squawk::changeState(Shared::Availability p_state) } } -void Core::Squawk::connectAccount(const QString& account) -{ +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->setActive(true); - if (state != Shared::Availability::offline) { + if (state != Shared::Availability::offline) itr->second->connect(); - } } -void Core::Squawk::disconnectAccount(const QString& account) -{ +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"); @@ -262,15 +251,14 @@ void Core::Squawk::disconnectAccount(const QString& account) itr->second->disconnect(); } -void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_state) -{ +void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_state) { Account* acc = static_cast(sender()); QMap changes = { {"state", QVariant::fromValue(p_state)} }; - if (acc->getLastError() == Account::Error::none) { + if (acc->getLastError() == Account::Error::none) changes.insert("error", ""); - } + emit changeAccount(acc->getName(), changes); #ifdef WITH_KWALLET @@ -282,55 +270,48 @@ void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_sta #endif } -void Core::Squawk::onAccountAddContact(const QString& jid, const QString& group, const QMap& data) -{ +void Core::Squawk::onAccountAddContact(const QString& jid, const QString& group, const QMap& data) { Account* acc = static_cast(sender()); emit addContact(acc->getName(), jid, group, data); } -void Core::Squawk::onAccountAddGroup(const QString& name) -{ +void Core::Squawk::onAccountAddGroup(const QString& name) { Account* acc = static_cast(sender()); emit addGroup(acc->getName(), name); } -void Core::Squawk::onAccountRemoveGroup(const QString& name) -{ +void Core::Squawk::onAccountRemoveGroup(const QString& name) { Account* acc = static_cast(sender()); emit removeGroup(acc->getName(), name); } -void Core::Squawk::onAccountChangeContact(const QString& jid, const QMap& data) -{ +void Core::Squawk::onAccountChangeContact(const QString& jid, const QMap& data) { Account* acc = static_cast(sender()); emit changeContact(acc->getName(), jid, data); } -void Core::Squawk::onAccountRemoveContact(const QString& jid) -{ +void Core::Squawk::onAccountRemoveContact(const QString& jid) { Account* acc = static_cast(sender()); emit removeContact(acc->getName(), jid); } -void Core::Squawk::onAccountRemoveContact(const QString& jid, const QString& group) -{ +void Core::Squawk::onAccountRemoveContact(const QString& jid, const QString& group) { Account* acc = static_cast(sender()); emit removeContact(acc->getName(), jid, group); } -void Core::Squawk::onAccountAddPresence(const QString& jid, const QString& name, const QMap& data) -{ +void Core::Squawk::onAccountAddPresence(const QString& jid, const QString& name, const QMap& data) { Account* acc = static_cast(sender()); emit addPresence(acc->getName(), jid, name, data); //it's equal if a MUC sends its status with presence of the same jid (ex: muc@srv.im/muc@srv.im), it's not a client, so, no need to request if (jid != name) { - QString node = data["capabilityNode"].toString(); - QString ver = data["capabilityVer"].toString(); - QString hash = data["capabilityHash"].toString(); - if (!clientCache.checkClient(node, ver, hash)) { - acc->discoverInfo(jid + "/" + name, node + "/" + ver); - } + const Shared::ClientId& id = data["client"].value(); + if (!id.valid()) + return; + + if (!clientCache.checkClient(id)) + acc->discoverInfo(jid + "/" + name, id.getId()); } } @@ -347,32 +328,27 @@ void Core::Squawk::onAccountInfoDiscovered( } } -void Core::Squawk::onAccountRemovePresence(const QString& jid, const QString& name) -{ +void Core::Squawk::onAccountRemovePresence(const QString& jid, const QString& name) { Account* acc = static_cast(sender()); emit removePresence(acc->getName(), jid, name); } -void Core::Squawk::onAccountAvailabilityChanged(Shared::Availability state) -{ +void Core::Squawk::onAccountAvailabilityChanged(Shared::Availability state) { Account* acc = static_cast(sender()); emit changeAccount(acc->getName(), {{"availability", QVariant::fromValue(state)}}); } -void Core::Squawk::onAccountChanged(const QMap& data) -{ +void Core::Squawk::onAccountChanged(const QMap& data) { Account* acc = static_cast(sender()); emit changeAccount(acc->getName(), data); } -void Core::Squawk::onAccountMessage(const Shared::Message& data) -{ +void Core::Squawk::onAccountMessage(const Shared::Message& data) { Account* acc = static_cast(sender()); emit accountMessage(acc->getName(), data); } -void Core::Squawk::sendMessage(const QString& account, const Shared::Message& data) -{ +void Core::Squawk::sendMessage(const QString& account, const Shared::Message& data) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to send a message with non existing account" << account << ", skipping"; @@ -382,8 +358,7 @@ void Core::Squawk::sendMessage(const QString& account, const Shared::Message& da itr->second->sendMessage(data); } -void Core::Squawk::replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data) -{ +void Core::Squawk::replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to replace a message with non existing account" << account << ", skipping"; @@ -393,8 +368,7 @@ void Core::Squawk::replaceMessage(const QString& account, const QString& origina itr->second->replaceMessage(originalId, data); } -void Core::Squawk::resendMessage(const QString& account, const QString& jid, const QString& id) -{ +void Core::Squawk::resendMessage(const QString& account, const QString& jid, const QString& id) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to resend a message with non existing account" << account << ", skipping"; @@ -404,8 +378,7 @@ void Core::Squawk::resendMessage(const QString& account, const QString& jid, con itr->second->resendMessage(jid, id); } -void Core::Squawk::requestArchive(const QString& account, const QString& jid, int count, const QString& before) -{ +void Core::Squawk::requestArchive(const QString& account, const QString& jid, int count, const QString& before) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug("An attempt to request an archive of non existing account, skipping"); @@ -414,14 +387,12 @@ void Core::Squawk::requestArchive(const QString& account, const QString& jid, in itr->second->requestArchive(jid, count, before); } -void Core::Squawk::onAccountResponseArchive(const QString& jid, const std::list& list, bool last) -{ +void Core::Squawk::onAccountResponseArchive(const QString& jid, const std::list& list, bool last) { Account* acc = static_cast(sender()); emit responseArchive(acc->getName(), jid, list, last); } -void Core::Squawk::modifyAccountRequest(const QString& name, const QMap& map) -{ +void Core::Squawk::modifyAccountRequest(const QString& name, const QMap& map) { AccountsMap::const_iterator itr = amap.find(name); if (itr == amap.end()) { qDebug("An attempt to modify non existing account, skipping"); @@ -471,29 +442,24 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMapsetLogin(mItr->toString()); - } mItr = map.find("password"); - if (mItr != map.end()) { + if (mItr != map.end()) acc->setPassword(mItr->toString()); - } mItr = map.find("resource"); - if (mItr != map.end()) { + if (mItr != map.end()) acc->setResource(mItr->toString()); - } mItr = map.find("server"); - if (mItr != map.end()) { + if (mItr != map.end()) acc->setServer(mItr->toString()); - } mItr = map.find("passwordType"); - if (mItr != map.end()) { + if (mItr != map.end()) acc->setPasswordType(Shared::Global::fromInt(mItr->toInt())); - } #ifdef WITH_KWALLET if (acc->getPasswordType() == Shared::AccountPassword::kwallet @@ -505,28 +471,24 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMapgetActive()) { + if (activeChanged && acc->getActive()) acc->connect(); - } else if (!wentReconnecting && acc->getActive() && acc->getLastError() == Account::Error::authentication) { + else if (!wentReconnecting && acc->getActive() && acc->getLastError() == Account::Error::authentication) acc->connect(); - } } emit changeAccount(name, map); } -void Core::Squawk::onAccountError(const QString& text) -{ +void Core::Squawk::onAccountError(const QString& text) { Account* acc = static_cast(sender()); emit changeAccount(acc->getName(), {{"error", text}}); - if (acc->getLastError() == Account::Error::authentication) { + if (acc->getLastError() == Account::Error::authentication) emit requestPassword(acc->getName(), true); - } } -void Core::Squawk::removeAccountRequest(const QString& name) -{ +void Core::Squawk::removeAccountRequest(const QString& name) { AccountsMap::const_iterator itr = amap.find(name); if (itr == amap.end()) { qDebug() << "An attempt to remove non existing account " << name << " from core, skipping"; @@ -534,9 +496,8 @@ void Core::Squawk::removeAccountRequest(const QString& name) } Account* acc = itr->second; - if (acc->getState() != Shared::ConnectionState::disconnected) { + if (acc->getState() != Shared::ConnectionState::disconnected) acc->disconnect(); - } for (Accounts::const_iterator aItr = accounts.begin(); aItr != accounts.end(); ++aItr) { if (*aItr == acc) { @@ -556,8 +517,7 @@ void Core::Squawk::removeAccountRequest(const QString& name) acc->deleteLater(); } -void Core::Squawk::subscribeContact(const QString& account, const QString& jid, const QString& reason) -{ +void Core::Squawk::subscribeContact(const QString& account, const QString& jid, const QString& reason) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug("An attempt to subscribe to the contact with non existing account, skipping"); @@ -567,8 +527,7 @@ void Core::Squawk::subscribeContact(const QString& account, const QString& jid, itr->second->subscribeToContact(jid, reason); } -void Core::Squawk::unsubscribeContact(const QString& account, const QString& jid, const QString& reason) -{ +void Core::Squawk::unsubscribeContact(const QString& account, const QString& jid, const QString& reason) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug("An attempt to subscribe to the contact with non existing account, skipping"); @@ -578,8 +537,7 @@ void Core::Squawk::unsubscribeContact(const QString& account, const QString& jid itr->second->unsubscribeFromContact(jid, reason); } -void Core::Squawk::removeContactRequest(const QString& account, const QString& jid) -{ +void Core::Squawk::removeContactRequest(const QString& account, const QString& jid) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug("An attempt to remove contact from non existing account, skipping"); @@ -589,8 +547,7 @@ void Core::Squawk::removeContactRequest(const QString& account, const QString& j itr->second->removeContactRequest(jid); } -void Core::Squawk::addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet& groups) -{ +void Core::Squawk::addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet& groups) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug("An attempt to add contact to a non existing account, skipping"); @@ -600,26 +557,22 @@ void Core::Squawk::addContactRequest(const QString& account, const QString& jid, itr->second->addContactRequest(jid, name, groups); } -void Core::Squawk::onAccountAddRoom(const QString jid, const QMap& data) -{ +void Core::Squawk::onAccountAddRoom(const QString jid, const QMap& data) { Account* acc = static_cast(sender()); emit addRoom(acc->getName(), jid, data); } -void Core::Squawk::onAccountChangeRoom(const QString jid, const QMap& data) -{ +void Core::Squawk::onAccountChangeRoom(const QString jid, const QMap& data) { Account* acc = static_cast(sender()); emit changeRoom(acc->getName(), jid, data); } -void Core::Squawk::onAccountRemoveRoom(const QString jid) -{ +void Core::Squawk::onAccountRemoveRoom(const QString jid) { Account* acc = static_cast(sender()); emit removeRoom(acc->getName(), jid); } -void Core::Squawk::setRoomJoined(const QString& account, const QString& jid, bool joined) -{ +void Core::Squawk::setRoomJoined(const QString& account, const QString& jid, bool joined) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to set jouned to the room" << jid << "of non existing account" << account << ", skipping"; @@ -628,8 +581,7 @@ void Core::Squawk::setRoomJoined(const QString& account, const QString& jid, boo itr->second->setRoomJoined(jid, joined); } -void Core::Squawk::setRoomAutoJoin(const QString& account, const QString& jid, bool joined) -{ +void Core::Squawk::setRoomAutoJoin(const QString& account, const QString& jid, bool joined) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to set autoJoin to the room" << jid << "of non existing account" << account << ", skipping"; @@ -638,32 +590,27 @@ void Core::Squawk::setRoomAutoJoin(const QString& account, const QString& jid, b itr->second->setRoomAutoJoin(jid, joined); } -void Core::Squawk::onAccountAddRoomPresence(const QString& jid, const QString& nick, const QMap& data) -{ +void Core::Squawk::onAccountAddRoomPresence(const QString& jid, const QString& nick, const QMap& data) { Account* acc = static_cast(sender()); emit addRoomParticipant(acc->getName(), jid, nick, data); } -void Core::Squawk::onAccountChangeRoomPresence(const QString& jid, const QString& nick, const QMap& data) -{ +void Core::Squawk::onAccountChangeRoomPresence(const QString& jid, const QString& nick, const QMap& data) { Account* acc = static_cast(sender()); emit changeRoomParticipant(acc->getName(), jid, nick, data); } -void Core::Squawk::onAccountRemoveRoomPresence(const QString& jid, const QString& nick) -{ +void Core::Squawk::onAccountRemoveRoomPresence(const QString& jid, const QString& nick) { Account* acc = static_cast(sender()); emit removeRoomParticipant(acc->getName(), jid, nick); } -void Core::Squawk::onAccountChangeMessage(const QString& jid, const QString& id, const QMap& data) -{ +void Core::Squawk::onAccountChangeMessage(const QString& jid, const QString& id, const QMap& data) { Account* acc = static_cast(sender()); emit changeMessage(acc->getName(), jid, id, data); } -void Core::Squawk::removeRoomRequest(const QString& account, const QString& jid) -{ +void Core::Squawk::removeRoomRequest(const QString& account, const QString& jid) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to remove the room" << jid << "of non existing account" << account << ", skipping"; @@ -672,8 +619,7 @@ void Core::Squawk::removeRoomRequest(const QString& account, const QString& jid) itr->second->removeRoomRequest(jid); } -void Core::Squawk::addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin) -{ +void Core::Squawk::addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to add the room" << jid << "to non existing account" << account << ", skipping"; @@ -682,13 +628,11 @@ void Core::Squawk::addRoomRequest(const QString& account, const QString& jid, co itr->second->addRoomRequest(jid, nick, password, autoJoin); } -void Core::Squawk::fileDownloadRequest(const QString& url) -{ +void Core::Squawk::fileDownloadRequest(const QString& url) { network.downladFile(url); } -void Core::Squawk::addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName) -{ +void Core::Squawk::addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to add contact" << jid << "of non existing account" << account << "to the group" << groupName << ", skipping"; @@ -697,8 +641,7 @@ void Core::Squawk::addContactToGroupRequest(const QString& account, const QStrin itr->second->addContactToGroupRequest(jid, groupName); } -void Core::Squawk::removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName) -{ +void Core::Squawk::removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to add contact" << jid << "of non existing account" << account << "to the group" << groupName << ", skipping"; @@ -707,8 +650,7 @@ void Core::Squawk::removeContactFromGroupRequest(const QString& account, const Q itr->second->removeContactFromGroupRequest(jid, groupName); } -void Core::Squawk::renameContactRequest(const QString& account, const QString& jid, const QString& newName) -{ +void Core::Squawk::renameContactRequest(const QString& account, const QString& jid, const QString& newName) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to rename contact" << jid << "of non existing account" << account << ", skipping"; @@ -717,8 +659,7 @@ void Core::Squawk::renameContactRequest(const QString& account, const QString& j itr->second->renameContactRequest(jid, newName); } -void Core::Squawk::requestInfo(const QString& account, const QString& jid) -{ +void Core::Squawk::requestInfo(const QString& account, const QString& jid) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to request info about" << jid << "of non existing account" << account << ", skipping"; @@ -727,8 +668,7 @@ void Core::Squawk::requestInfo(const QString& account, const QString& jid) itr->second->requestInfo(jid); } -void Core::Squawk::updateInfo(const QString& account, const Shared::Info& info) -{ +void Core::Squawk::updateInfo(const QString& account, const Shared::Info& info) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to update info to non existing account" << account << ", skipping"; @@ -737,8 +677,7 @@ void Core::Squawk::updateInfo(const QString& account, const Shared::Info& info) itr->second->updateInfo(info); } -void Core::Squawk::readSettings() -{ +void Core::Squawk::readSettings() { QSettings settings; settings.beginGroup("core"); int size = settings.beginReadArray("accounts"); @@ -772,8 +711,7 @@ void Core::Squawk::readSettings() emit ready(); } -void Core::Squawk::onAccountNeedPassword() -{ +void Core::Squawk::onAccountNeedPassword() { Account* acc = static_cast(sender()); switch (acc->getPasswordType()) { case Shared::AccountPassword::alwaysAsk: @@ -796,13 +734,11 @@ void Core::Squawk::onAccountNeedPassword() } } -void Core::Squawk::onWalletRejectPassword(const QString& login) -{ +void Core::Squawk::onWalletRejectPassword(const QString& login) { emit requestPassword(login, false); } -void Core::Squawk::responsePassword(const QString& account, const QString& password) -{ +void Core::Squawk::responsePassword(const QString& account, const QString& password) { AccountsMap::const_iterator itr = amap.find(account); if (itr == amap.end()) { qDebug() << "An attempt to set password to non existing account" << account << ", skipping"; @@ -811,24 +747,19 @@ void Core::Squawk::responsePassword(const QString& account, const QString& passw Account* acc = itr->second; acc->setPassword(password); emit changeAccount(account, {{"password", password}}); - if (state != Shared::Availability::offline && acc->getActive()) { + if (state != Shared::Availability::offline && acc->getActive()) acc->connect(); - } } -void Core::Squawk::onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText) -{ +void Core::Squawk::onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText) { Account* acc = static_cast(sender()); emit fileError({{acc->getName(), jid, id}}, errorText, true); } -void Core::Squawk::onLocalPathInvalid(const QString& path) -{ +void Core::Squawk::onLocalPathInvalid(const QString& path) { std::list list = network.reportPathInvalid(path); - QMap data({ - {"attachPath", ""} - }); + QMap data({{"attachPath", ""}}); for (const Shared::MessageInfo& info : list) { AccountsMap::const_iterator itr = amap.find(info.account); if (itr != amap.end()) { @@ -839,8 +770,7 @@ void Core::Squawk::onLocalPathInvalid(const QString& path) } } -void Core::Squawk::changeDownloadsPath(const QString& path) -{ +void Core::Squawk::changeDownloadsPath(const QString& path) { network.moveFilesDirectory(path); } diff --git a/shared/clientid.cpp b/shared/clientid.cpp index f16736c..b050df6 100644 --- a/shared/clientid.cpp +++ b/shared/clientid.cpp @@ -22,10 +22,125 @@ Shared::ClientId::ClientId(): hash() {} +Shared::ClientId::ClientId(const QString& p_node, const QString& p_ver, const QString& p_hash): + node(p_node), + verification(p_ver), + hash(p_hash) +{} + + +Shared::ClientId::ClientId(const Shared::ClientId& other): + node(other.node), + verification(other.verification), + hash(other.hash) +{} + +Shared::ClientId & Shared::ClientId::operator=(const Shared::ClientId& other) { + node = other.node; + verification = other.verification; + hash = other.hash; + + return *this; +} + +bool Shared::ClientId::operator==(const Shared::ClientId& other) const { + return hash == other.hash && verification == other.verification && node == other.node; +} + +bool Shared::ClientId::operator!=(const Shared::ClientId& other) const { + return hash != other.hash && verification != other.verification && node != other.node; +} + +bool Shared::ClientId::operator<(const Shared::ClientId& other) const { + if (hash < other.hash) + return true; + + if (hash > other.hash) + return false; + + if (verification < other.verification) + return true; + + if (verification > other.verification) + return false; + + if (node < other.node) + return true; + + return false; +} + +bool Shared::ClientId::operator>(const Shared::ClientId& other) const { + if (hash > other.hash) + return true; + + if (hash < other.hash) + return false; + + if (verification > other.verification) + return true; + + if (verification < other.verification) + return false; + + if (node > other.node) + return true; + + return false; +} + +bool Shared::ClientId::operator<=(const Shared::ClientId& other) const { + if (hash < other.hash) + return true; + + if (hash > other.hash) + return false; + + if (verification < other.verification) + return true; + + if (verification > other.verification) + return false; + + if (node < other.node) + return true; + + if (node > other.node) + return false; + + return true; +} + +bool Shared::ClientId::operator>=(const Shared::ClientId& other) const { + if (hash > other.hash) + return true; + + if (hash < other.hash) + return false; + + if (verification > other.verification) + return true; + + if (verification < other.verification) + return false; + + if (node > other.node) + return true; + + if (node < other.node) + return false; + + return true; +} + QString Shared::ClientId::getId() const { return node + "/" + verification; } +bool Shared::ClientId::valid() const { + return node.size() > 0 && verification.size() > 0 && hash.size() > 0; +} + QDataStream & Shared::ClientId::operator<<(QDataStream& stream) { stream >> node; stream >> verification; diff --git a/shared/clientid.h b/shared/clientid.h index defe909..5188b1c 100644 --- a/shared/clientid.h +++ b/shared/clientid.h @@ -25,7 +25,18 @@ namespace Shared { class ClientId { public: ClientId(); + ClientId(const QString& node, const QString& verification, const QString& hash); + ClientId(const ClientId& other); + ClientId& operator = (const ClientId& other); + bool operator == (const ClientId& other) const; + bool operator != (const ClientId& other) const; + bool operator < (const ClientId& other) const; + bool operator > (const ClientId& other) const; + bool operator <= (const ClientId& other) const; + bool operator >= (const ClientId& other) const; + + bool valid() const; QString getId() const; QDataStream& operator << (QDataStream& stream); @@ -39,6 +50,8 @@ public: } +Q_DECLARE_METATYPE(Shared::ClientId) + QDataStream& operator << (QDataStream& stream, const Shared::ClientId& info); QDataStream& operator >> (QDataStream& stream, Shared::ClientId& info); diff --git a/shared/clientinfo.cpp b/shared/clientinfo.cpp index 9a3fdac..9b84755 100644 --- a/shared/clientinfo.cpp +++ b/shared/clientinfo.cpp @@ -35,11 +35,22 @@ Shared::ClientInfo::ClientInfo(): id(), specificPresence() {} +Shared::ClientInfo::ClientInfo(const QString& p_node, const QString& p_ver, const QString& p_hash) : + identities(), + extensions(), + id(p_node, p_ver, p_hash), + specificPresence() {} + +Shared::ClientInfo::ClientInfo(const Shared::ClientId& p_id) : + identities(), + extensions(), + id(p_id), + specificPresence() {} + QString Shared::ClientInfo::getId() const { return id.getId(); } - QDataStream & Shared::ClientInfo::operator >> (QDataStream& stream) const { stream << id; stream << (quint8)identities.size(); diff --git a/shared/clientinfo.h b/shared/clientinfo.h index 8e95180..288e9fa 100644 --- a/shared/clientinfo.h +++ b/shared/clientinfo.h @@ -31,6 +31,8 @@ namespace Shared { class ClientInfo { public: ClientInfo(); + ClientInfo(const ClientId& id); + ClientInfo(const QString& node, const QString& verification, const QString& hash); QString getId() const; bool valid() const; diff --git a/ui/models/CMakeLists.txt b/ui/models/CMakeLists.txt index 84aba66..0e6d8ab 100644 --- a/ui/models/CMakeLists.txt +++ b/ui/models/CMakeLists.txt @@ -1,28 +1,36 @@ +set(SOURCE_FILES + abstractparticipant.cpp + account.cpp + accounts.cpp + contact.cpp + element.cpp + group.cpp + item.cpp + participant.cpp + presence.cpp + reference.cpp + room.cpp + roster.cpp +) + +set(HEADER_FILES + abstractparticipant.h + account.h + accounts.h + contact.h + element.h + group.h + item.h + participant.h + presence.h + reference.h + room.h + roster.h +) + target_sources(squawk PRIVATE - abstractparticipant.cpp - abstractparticipant.h - account.cpp - account.h - accounts.cpp - accounts.h - contact.cpp - contact.h - element.cpp - element.h - group.cpp - group.h - item.cpp - item.h - participant.cpp - participant.h - presence.cpp - presence.h - reference.cpp - reference.h - room.cpp - room.h - roster.cpp - roster.h - ) + ${SOURCE_FILES} + ${HEADER_FILES} +) add_subdirectory(info) diff --git a/ui/models/abstractparticipant.cpp b/ui/models/abstractparticipant.cpp index 029527d..240e5ba 100644 --- a/ui/models/abstractparticipant.cpp +++ b/ui/models/abstractparticipant.cpp @@ -18,40 +18,38 @@ #include "abstractparticipant.h" -using namespace Models; - Models::AbstractParticipant::AbstractParticipant(Models::Item::Type p_type, const QMap& data, Models::Item* parentItem): Item(p_type, data, parentItem), availability(Shared::Availability::offline), lastActivity(data.value("lastActivity").toDateTime()), - status(data.value("status").toString()) + status(data.value("status").toString()), + client() { QMap::const_iterator itr = data.find("availability"); - if (itr != data.end()) { + if (itr != data.end()) setAvailability(itr.value().toUInt()); - } + + itr = data.find("client"); + if (itr != data.end()) + setClient(itr.value().value()); } Models::AbstractParticipant::AbstractParticipant(const Models::AbstractParticipant& other): Item(other), availability(other.availability), lastActivity(other.lastActivity), - status(other.status) -{ + status(other.status), + client(other.client) +{} + + +Models::AbstractParticipant::~AbstractParticipant() {} + +int Models::AbstractParticipant::columnCount() const { + return 5; } - -Models::AbstractParticipant::~AbstractParticipant() -{ -} - -int Models::AbstractParticipant::columnCount() const -{ - return 4; -} - -QVariant Models::AbstractParticipant::data(int column) const -{ +QVariant Models::AbstractParticipant::data(int column) const { switch (column) { case 0: return Item::data(column); @@ -61,62 +59,71 @@ QVariant Models::AbstractParticipant::data(int column) const return QVariant::fromValue(availability); case 3: return status; + case 4: + return QVariant::fromValue(client); default: return QVariant(); } } -Shared::Availability Models::AbstractParticipant::getAvailability() const -{ +Shared::Availability Models::AbstractParticipant::getAvailability() const { return availability; } -QDateTime Models::AbstractParticipant::getLastActivity() const -{ +QDateTime Models::AbstractParticipant::getLastActivity() const { return lastActivity; } -QString Models::AbstractParticipant::getStatus() const -{ +QString Models::AbstractParticipant::getStatus() const { return status; } -void Models::AbstractParticipant::setAvailability(Shared::Availability p_avail) -{ +void Models::AbstractParticipant::setAvailability(Shared::Availability p_avail) { if (availability != p_avail) { availability = p_avail; changed(2); } } -void Models::AbstractParticipant::setAvailability(unsigned int avail) -{ +void Models::AbstractParticipant::setAvailability(unsigned int avail) { setAvailability(Shared::Global::fromInt(avail)); } -void Models::AbstractParticipant::setLastActivity(const QDateTime& p_time) -{ +void Models::AbstractParticipant::setLastActivity(const QDateTime& p_time) { if (lastActivity != p_time) { lastActivity = p_time; changed(1); } } -void Models::AbstractParticipant::setStatus(const QString& p_state) -{ +void Models::AbstractParticipant::setStatus(const QString& p_state) { if (status != p_state) { status = p_state; changed(3); } } -QIcon Models::AbstractParticipant::getStatusIcon(bool big) const -{ +Shared::ClientId Models::AbstractParticipant::getClient() const { + return client; +} + +QString Models::AbstractParticipant::getClientNode() const { + return client.node; +} + +void Models::AbstractParticipant::setClient(const Shared::ClientId& id) { + if (client != id) { + client = id; + changed(4); + } +} + + +QIcon Models::AbstractParticipant::getStatusIcon(bool big) const { return Shared::availabilityIcon(availability, big); } -void Models::AbstractParticipant::update(const QString& key, const QVariant& value) -{ +void Models::AbstractParticipant::update(const QString& key, const QVariant& value) { if (key == "name") { setName(value.toString()); } else if (key == "status") { @@ -125,5 +132,7 @@ void Models::AbstractParticipant::update(const QString& key, const QVariant& val setAvailability(value.toUInt()); } else if (key == "lastActivity") { setLastActivity(value.toDateTime()); + } else if (key == "client") { + setClient(value.value()); } } diff --git a/ui/models/abstractparticipant.h b/ui/models/abstractparticipant.h index cb20788..fe2f0b5 100644 --- a/ui/models/abstractparticipant.h +++ b/ui/models/abstractparticipant.h @@ -21,9 +21,10 @@ #include "item.h" -#include "shared/enums.h" -#include "shared/icons.h" -#include "shared/global.h" +#include +#include +#include +#include #include #include @@ -51,6 +52,10 @@ public: QString getStatus() const; void setStatus(const QString& p_state); virtual QIcon getStatusIcon(bool big = false) const; + + Shared::ClientId getClient() const; + void setClient(const Shared::ClientId& id); + QString getClientNode() const; virtual void update(const QString& key, const QVariant& value); @@ -58,6 +63,7 @@ protected: Shared::Availability availability; QDateTime lastActivity; QString status; + Shared::ClientId client; }; } diff --git a/ui/models/participant.cpp b/ui/models/participant.cpp index dc42c07..7201acb 100644 --- a/ui/models/participant.cpp +++ b/ui/models/participant.cpp @@ -26,52 +26,46 @@ Models::Participant::Participant(const QMap& data, Models::It role(Shared::Role::unspecified) { QMap::const_iterator itr = data.find("affiliation"); - if (itr != data.end()) { + if (itr != data.end()) setAffiliation(itr.value().toUInt()); - } - + itr = data.find("role"); - if (itr != data.end()) { + if (itr != data.end()) setRole(itr.value().toUInt()); - } - + itr = data.find("avatarState"); - if (itr != data.end()) { + if (itr != data.end()) setAvatarState(itr.value().toUInt()); - } + itr = data.find("avatarPath"); - if (itr != data.end()) { + if (itr != data.end()) setAvatarPath(itr.value().toString()); - } + } Models::Participant::~Participant() -{ +{} + +int Models::Participant::columnCount() const { + return 9; } -int Models::Participant::columnCount() const -{ - return 8; -} - -QVariant Models::Participant::data(int column) const -{ +QVariant Models::Participant::data(int column) const { switch (column) { - case 4: - return QVariant::fromValue(affiliation); case 5: - return QVariant::fromValue(role); + return QVariant::fromValue(affiliation); case 6: - return QVariant::fromValue(getAvatarState()); + return QVariant::fromValue(role); case 7: + return QVariant::fromValue(getAvatarState()); + case 8: return getAvatarPath(); default: return AbstractParticipant::data(column); } } -void Models::Participant::update(const QString& key, const QVariant& value) -{ +void Models::Participant::update(const QString& key, const QVariant& value) { if (key == "affiliation") { setAffiliation(value.toUInt()); } else if (key == "role") { @@ -85,67 +79,58 @@ void Models::Participant::update(const QString& key, const QVariant& value) } } -Shared::Affiliation Models::Participant::getAffiliation() const -{ +Shared::Affiliation Models::Participant::getAffiliation() const { return affiliation; } -void Models::Participant::setAffiliation(Shared::Affiliation p_aff) -{ +void Models::Participant::setAffiliation(Shared::Affiliation p_aff) { if (p_aff != affiliation) { affiliation = p_aff; - changed(4); - } -} - -void Models::Participant::setAffiliation(unsigned int aff) -{ - setAffiliation(Shared::Global::fromInt(aff)); -} - -Shared::Role Models::Participant::getRole() const -{ - return role; -} - -void Models::Participant::setRole(Shared::Role p_role) -{ - if (p_role != role) { - role = p_role; changed(5); } } -void Models::Participant::setRole(unsigned int p_role) -{ - setRole(Shared::Global::fromInt(p_role)); +void Models::Participant::setAffiliation(unsigned int aff) { + setAffiliation(Shared::Global::fromInt(aff)); } -QString Models::Participant::getAvatarPath() const -{ - return avatarPath; +Shared::Role Models::Participant::getRole() const { + return role; } -Shared::Avatar Models::Participant::getAvatarState() const -{ - return avatarState; -} - -void Models::Participant::setAvatarPath(const QString& path) -{ - if (avatarPath != path) { - avatarPath = path; - changed(7); - } -} - -void Models::Participant::setAvatarState(Shared::Avatar p_state) -{ - if (avatarState != p_state) { - avatarState = p_state; +void Models::Participant::setRole(Shared::Role p_role) { + if (p_role != role) { + role = p_role; changed(6); } } -void Models::Participant::setAvatarState(unsigned int p_state) -{setAvatarState(Shared::Global::fromInt(p_state));} +void Models::Participant::setRole(unsigned int p_role) { + setRole(Shared::Global::fromInt(p_role)); +} + +QString Models::Participant::getAvatarPath() const { + return avatarPath; +} + +Shared::Avatar Models::Participant::getAvatarState() const { + return avatarState; +} + +void Models::Participant::setAvatarPath(const QString& path) { + if (avatarPath != path) { + avatarPath = path; + changed(8); + } +} + +void Models::Participant::setAvatarState(Shared::Avatar p_state) { + if (avatarState != p_state) { + avatarState = p_state; + changed(7); + } +} + +void Models::Participant::setAvatarState(unsigned int p_state) { + setAvatarState(Shared::Global::fromInt(p_state)); +} diff --git a/ui/models/presence.cpp b/ui/models/presence.cpp index 8ba7c47..a867243 100644 --- a/ui/models/presence.cpp +++ b/ui/models/presence.cpp @@ -21,14 +21,8 @@ Models::Presence::Presence(const QMap& data, Item* parentItem): AbstractParticipant(Item::presence, data, parentItem) -{ -} +{} Models::Presence::~Presence() -{ -} +{} -int Models::Presence::columnCount() const -{ - return 4; -} diff --git a/ui/models/presence.h b/ui/models/presence.h index fb1a31c..4df662a 100644 --- a/ui/models/presence.h +++ b/ui/models/presence.h @@ -34,8 +34,6 @@ class Presence : public Models::AbstractParticipant public: explicit Presence(const QMap &data, Item *parentItem = 0); ~Presence(); - - int columnCount() const override; }; } diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index 8ce3464..43717f8 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -225,6 +225,7 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const if (s.size() > 0) { str += "\n" + tr("Status: ") + s; } + str += "\n" + tr("Client: ") + contact->getClientNode(); result = str; } @@ -240,7 +241,8 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const } str += tr("Affiliation: ") + Shared::Global::getName(p->getAffiliation()) + "\n"; - str += tr("Role: ") + Shared::Global::getName(p->getRole()); + str += tr("Role: ") + Shared::Global::getName(p->getRole()) + "\n"; + str += tr("Client: ") + p->getClientNode(); result = str; } From 283e9ebc4d72e02242f7dc0af46a648771dc63e9 Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 15 Mar 2023 21:17:44 +0300 Subject: [PATCH 101/137] some thoughts on detecting condition for enablining or showing the button for encryption in chat window --- CHANGELOG.md | 5 +- core/account.cpp | 2 + core/handlers/omemohandler.cpp | 4 + core/handlers/omemohandler.h | 3 + shared/CMakeLists.txt | 2 + shared/trustsummary.cpp | 140 ++++++++++++++++++++++++++ shared/trustsummary.h | 51 ++++++++++ ui/widgets/CMakeLists.txt | 34 +++++-- ui/widgets/conversation.cpp | 2 + ui/widgets/conversation.ui | 25 +++++ ui/widgets/messageline/CMakeLists.txt | 18 +++- 11 files changed, 269 insertions(+), 17 deletions(-) create mode 100644 shared/trustsummary.cpp create mode 100644 shared/trustsummary.h diff --git a/CHANGELOG.md b/CHANGELOG.md index a36da73..f554982 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,12 @@ - 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 - removed unnecessary own vcard request at sturtup (used to do it to discover my own avatar) +- vcard window now is Info system and it can display more information ### New features -- Now you can enable tray icon from settings! +- now you can enable tray icon from settings! +- there is a job queue now, this allowes to spread a bit the spam on the server at connection time +- squawk now querries clients of it's peers, you can see what programs other people use ## Squawk 0.2.2 (May 05, 2022) ### Bug fixes diff --git a/core/account.cpp b/core/account.cpp index 9740029..165dfd8 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -120,6 +120,8 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& QObject::connect(delay, &DelayManager::Manager::requestBundles, oh, &OmemoHandler::requestBundles); QObject::connect(delay, &DelayManager::Manager::requestOwnBundles, oh, &OmemoHandler::requestOwnBundles); + QObject::connect(om, &QXmppOmemoManager::deviceAdded, oh, &OmemoHandler::onOmemoDeviceAdded); + client.addExtension(tm); client.addExtension(om); om->setSecurityPolicy(QXmpp::Toakafa); diff --git a/core/handlers/omemohandler.cpp b/core/handlers/omemohandler.cpp index 79eded9..4bccc4b 100644 --- a/core/handlers/omemohandler.cpp +++ b/core/handlers/omemohandler.cpp @@ -214,6 +214,10 @@ void Core::OmemoHandler::onOwnBundlesReceived() { acc->delay->receivedOwnBundles(keys); } +void Core::OmemoHandler::onOmemoDeviceAdded(const QString& jid, uint32_t id) { + qDebug() << "OMEMO device added for" << jid; +} + QDataStream & operator >> (QDataStream& in, QXmppOmemoStorage::Device& device) { in >> device.label; diff --git a/core/handlers/omemohandler.h b/core/handlers/omemohandler.h index 0d1021b..b0db613 100644 --- a/core/handlers/omemohandler.h +++ b/core/handlers/omemohandler.h @@ -64,6 +64,9 @@ public: void requestOwnBundles(); void getDevices(const QString& jid, std::list& out) const; +public slots: + void onOmemoDeviceAdded(const QString& jid, uint32_t id); + private slots: void onBundlesReceived(const QString& jid); void onOwnBundlesReceived(); diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index 5db96f0..4bcc12f 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -14,6 +14,7 @@ set(SOURCE_FILES keyinfo.cpp info.cpp clientid.cpp + trustsummary.cpp ) set(HEADER_FILES @@ -35,6 +36,7 @@ set(HEADER_FILES keyinfo.h info.h clientid.h + trustsummary.h ) target_sources(squawk PRIVATE diff --git a/shared/trustsummary.cpp b/shared/trustsummary.cpp new file mode 100644 index 0000000..658538c --- /dev/null +++ b/shared/trustsummary.cpp @@ -0,0 +1,140 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 "trustsummary.h" + +const std::set Shared::TrustSummary::trustedLevels({ + Shared::TrustLevel::authenticated, + Shared::TrustLevel::automaticallyTrusted, + Shared::TrustLevel::manuallyTrusted +}); +const std::set Shared::TrustSummary::untrustedLevels({ + Shared::TrustLevel::undecided, + Shared::TrustLevel::automaticallyDistrusted, + Shared::TrustLevel::manuallyDistrusted +}); + +Shared::TrustSummary::TrustSummary(): + data() +{} + +void Shared::TrustSummary::set(Shared::EncryptionProtocol protocol, Shared::TrustLevel level, uint8_t amount) { + Data::iterator itr = data.find(protocol); + if (itr == data.end()) { + if (amount == 0) + return; + + itr = data.insert(std::make_pair(protocol, Amounts())).first; + } + + Amounts& am = itr->second; + Amounts::iterator aitr = am.find(level); + if (aitr == am.end()) { + if (amount == 0) + return; + + am.emplace(level, amount); + return; + } + if (amount == 0) { + if (am.size() == 1) + data.erase(itr); + else + am.erase(aitr); + + return; + } + aitr->second = amount; +} + +uint8_t Shared::TrustSummary::amount(Shared::EncryptionProtocol protocol, Shared::TrustLevel level) const { + Data::const_iterator itr = data.find(protocol); + if (itr == data.end()) + return 0; + + const Amounts& am = itr->second; + Amounts::const_iterator aitr = am.find(level); + if (aitr == am.end()) + return 0; + + return aitr->second; +} + +uint8_t Shared::TrustSummary::increment(Shared::EncryptionProtocol protocol, Shared::TrustLevel level) { + Data::iterator itr = data.find(protocol); + if (itr == data.end()) + itr = data.insert(std::make_pair(protocol, Amounts())).first; + + Amounts& am = itr->second; + Amounts::iterator aitr = am.find(level); + if (aitr == am.end()) { + am.emplace(level, 1); + return 1; + } + uint8_t& value = aitr->second; + return ++value; +} + +uint8_t Shared::TrustSummary::decrement(Shared::EncryptionProtocol protocol, Shared::TrustLevel level) { + Data::iterator itr = data.find(protocol); + if (itr == data.end()) + return 0; //should never happen, shall I better throw an exception? + + Amounts& am = itr->second; + Amounts::iterator aitr = am.find(level); + if (aitr == am.end()) + return 0; //should never happen, shall I better throw an exception? + + uint8_t& value = aitr->second; + uint8_t result = --value; + if (value == 0) { + if (am.size() == 1) + data.erase(itr); + else + am.erase(aitr); + } + return result; +} + +bool Shared::TrustSummary::hasKeys(Shared::EncryptionProtocol protocol) const { + return data.count(protocol) > 0; +} + +bool Shared::TrustSummary::hasTrustedKeys(Shared::EncryptionProtocol protocol) const { + Data::const_iterator itr = data.find(protocol); + if (itr == data.end()) + return false; + + for (const std::pair& pair : itr->second) { + if (trustedLevels.count(pair.first) > 0) + return true; + } + + return false; +} + +bool Shared::TrustSummary::hasUntrustedKeys(Shared::EncryptionProtocol protocol) const { + Data::const_iterator itr = data.find(protocol); + if (itr == data.end()) + return false; + + for (const std::pair& pair : itr->second) { + if (untrustedLevels.count(pair.first) > 0) + return true; + } + + return false; +} diff --git a/shared/trustsummary.h b/shared/trustsummary.h new file mode 100644 index 0000000..3283c10 --- /dev/null +++ b/shared/trustsummary.h @@ -0,0 +1,51 @@ +// Squawk messenger. +// Copyright (C) 2019 Yury Gubich +// +// 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 SHARED_TRUSTSUMMARY_H +#define SHARED_TRUSTSUMMARY_H + +#include +#include + +#include "enums.h" + +namespace Shared { + +class TrustSummary { +public: + TrustSummary(); + + void set(EncryptionProtocol protocol, TrustLevel level, uint8_t amount); + uint8_t increment(EncryptionProtocol protocol, TrustLevel level); + uint8_t decrement(EncryptionProtocol protocol, TrustLevel level); + + uint8_t amount(EncryptionProtocol protocol, TrustLevel level) const; + bool hasKeys(EncryptionProtocol protocol) const; + bool hasTrustedKeys(EncryptionProtocol protocol) const; + bool hasUntrustedKeys(EncryptionProtocol protocol) const; + +private: + typedef std::map Amounts; + typedef std::map Data; + + Data data; + static const std::set trustedLevels; + static const std::set untrustedLevels; +}; + +} + +#endif // SHARED_TRUSTSUMMARY_H diff --git a/ui/widgets/CMakeLists.txt b/ui/widgets/CMakeLists.txt index 2099db1..e8d846b 100644 --- a/ui/widgets/CMakeLists.txt +++ b/ui/widgets/CMakeLists.txt @@ -1,21 +1,33 @@ -target_sources(squawk PRIVATE +set(SOURCE_FILES chat.cpp - chat.h conversation.cpp - conversation.h - conversation.ui joinconference.cpp - joinconference.h - joinconference.ui newcontact.cpp - newcontact.h - newcontact.ui room.cpp - room.h about.cpp - about.h +) + +set(UI_FILES + conversation.ui + joinconference.ui + newcontact.ui about.ui - ) +) + +set(HEADER_FILES + chat.h + conversation.h + joinconference.h + newcontact.h + room.h + about.h +) + +target_sources(squawk PRIVATE + ${SOURCE_FILES} + ${UI_FILES} + ${HEADER_FILES} +) add_subdirectory(info) add_subdirectory(messageline) diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 61d3163..c11449f 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -100,6 +100,8 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, connect(m_ui->currentActionBadge, &Badge::close, this, &Conversation::clear); m_ui->currentActionBadge->setVisible(false); + m_ui->encryptionButton->setVisible(false); + //line->setAutoFillBackground(false); //if (testAttribute(Qt::WA_TranslucentBackground)) { //m_ui->scrollArea->setAutoFillBackground(false); diff --git a/ui/widgets/conversation.ui b/ui/widgets/conversation.ui index 1f8b483..c73de45 100644 --- a/ui/widgets/conversation.ui +++ b/ui/widgets/conversation.ui @@ -315,6 +315,31 @@ + + + + false + + + + + + + + + true + + + false + + + false + + + true + + + diff --git a/ui/widgets/messageline/CMakeLists.txt b/ui/widgets/messageline/CMakeLists.txt index 7a850da..7ded76b 100644 --- a/ui/widgets/messageline/CMakeLists.txt +++ b/ui/widgets/messageline/CMakeLists.txt @@ -1,10 +1,18 @@ -target_sources(squawk PRIVATE +set(SOURCE_FILES messagedelegate.cpp - messagedelegate.h preview.cpp - preview.h messagefeed.cpp - messagefeed.h feedview.cpp +) + +set(HEADER_FILES + messagedelegate.h + preview.h + messagefeed.h feedview.h - ) +) + +target_sources(squawk PRIVATE + ${SOURCE_FILES} + ${HEADER_FILES} +) From fffef9876a87a949142688b0bfc6f1bee7783dd2 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 16 Mar 2023 22:38:05 +0300 Subject: [PATCH 102/137] Refactoring, account destruction fix, some thoughts about where to store contact settings (omemo enable status for instance) --- core/account.cpp | 38 +++-- core/account.h | 2 +- core/conference.cpp | 144 +++++++----------- core/conference.h | 3 +- core/contact.cpp | 49 +++---- core/contact.h | 1 + core/handlers/messagehandler.cpp | 16 +- core/handlers/rosterhandler.cpp | 242 +++++++++++-------------------- core/handlers/rosterhandler.h | 3 +- core/handlers/vcardhandler.cpp | 2 +- core/rosteritem.cpp | 162 +++++++++++---------- core/rosteritem.h | 23 ++- core/storage/archive.cpp | 38 ++++- core/storage/archive.h | 5 +- translations/CMakeLists.txt | 4 +- 15 files changed, 352 insertions(+), 380 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index 165dfd8..3e713c4 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -44,6 +44,7 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& #endif #if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) cm(new QXmppCarbonManagerV2()), + psm(new QXmppPubSubManager()), #else cm(new QXmppCarbonManager()), #endif @@ -55,7 +56,6 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& um(new QXmppUploadRequestManager()), dm(client.findExtension()), rcpm(new QXmppMessageReceiptManager()), - psm(new QXmppPubSubManager()), reconnectScheduled(false), reconnectTimer(new QTimer), network(p_net), @@ -167,22 +167,34 @@ Account::~Account() { QObject::disconnect(network, &NetworkAccess::downloadFileComplete, mh, &MessageHandler::onDownloadFileComplete); QObject::disconnect(network, &NetworkAccess::loadFileError, mh, &MessageHandler::onLoadFileError); - delete vh; - delete mh; - delete rh; - + rh->clear(); //conferenses inside of roster handler hold QXmppMuc objects. + //If we destroy QXmppMucManager, then when we will be destroying RosterHandler + //it will try to destory Core::Conference objects + //and inside of those QXmppMuc objects will already be destroyed. + //So, clear will start the destruction from Core::Conference and this way it's not gonna crash + + delete delay; delete reconnectTimer; -#ifdef WITH_OMEMO - delete om; -#endif delete rcpm; - delete dm; delete um; delete bm; delete mm; delete am; delete cm; - delete delay; +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) + delete psm; +#endif +#ifdef WITH_OMEMO + delete om; + delete tm; + delete oh; + delete th; +#endif + + delete dh; + delete vh; + delete rh; + delete mh; } Shared::ConnectionState Core::Account::getState() const { @@ -345,7 +357,7 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) { vh->handlePresenceOfMyAccountChange(p_presence); } else { RosterItem* item = rh->getRosterItem(jid); - if (item != 0) + if (item != nullptr) item->handlePresence(p_presence); } @@ -417,7 +429,7 @@ void Core::Account::requestArchive(const QString& jid, int count, const QString& qDebug() << "An archive request for " << jid << ", before " << before; RosterItem* contact = rh->getRosterItem(jid); - if (contact == 0) { + if (contact == nullptr) { qDebug() << "An attempt to request archive for" << jid << "in account" << name << ", but the contact with such id wasn't found, skipping"; emit responseArchive(jid, std::list(), true); return; @@ -483,7 +495,7 @@ void Core::Account::onMamResultsReceived(const QString& queryId, const QXmppResu RosterItem* ri = rh->getRosterItem(jid); - if (ri != 0) { + if (ri != nullptr) { qDebug() << "Flushing messages for" << jid << ", complete:" << complete; ri->flushMessagesToArchive(complete, resultSetReply.first(), resultSetReply.last()); } diff --git a/core/account.h b/core/account.h index b257d2b..26365c1 100644 --- a/core/account.h +++ b/core/account.h @@ -200,6 +200,7 @@ private: #endif #if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) QXmppCarbonManagerV2* cm; + QXmppPubSubManager* psm; #else QXmppCarbonManager* cm; #endif @@ -211,7 +212,6 @@ private: QXmppUploadRequestManager* um; QXmppDiscoveryManager* dm; QXmppMessageReceiptManager* rcpm; - QXmppPubSubManager* psm; bool reconnectScheduled; QTimer* reconnectTimer; diff --git a/core/conference.cpp b/core/conference.cpp index 2e49625..3904675 100644 --- a/core/conference.cpp +++ b/core/conference.cpp @@ -42,57 +42,48 @@ Core::Conference::Conference(const QString& p_jid, const QString& p_account, boo connect(room, &QXmppMucRoom::error, this, &Conference::onRoomError); room->setNickName(nick); - if (autoJoin) { + if (autoJoin) room->join(); - } archive->readAllResourcesAvatars(exParticipants); } -Core::Conference::~Conference() -{ - if (joined) { +Core::Conference::~Conference(){ + if (joined) room->leave(); - } + room->deleteLater(); } -QString Core::Conference::getNick() const -{ +QString Core::Conference::getNick() const { return nick; } -bool Core::Conference::getAutoJoin() -{ +bool Core::Conference::getAutoJoin() const { return autoJoin; } -bool Core::Conference::getJoined() const -{ +bool Core::Conference::getJoined() const { return joined; } -void Core::Conference::setJoined(bool p_joined) -{ +void Core::Conference::setJoined(bool p_joined) { if (joined != p_joined) { - if (p_joined) { + if (p_joined) room->join(); - } else { + else room->leave(); - } } } -void Core::Conference::setAutoJoin(bool p_autoJoin) -{ +void Core::Conference::setAutoJoin(bool p_autoJoin) { if (autoJoin != p_autoJoin) { autoJoin = p_autoJoin; emit autoJoinChanged(autoJoin); } } -void Core::Conference::setNick(const QString& p_nick) -{ +void Core::Conference::setNick(const QString& p_nick) { if (nick != p_nick) { if (joined) { room->setNickName(p_nick); @@ -103,45 +94,38 @@ void Core::Conference::setNick(const QString& p_nick) } } -void Core::Conference::onRoomJoined() -{ +void Core::Conference::onRoomJoined() { joined = true; emit joinedChanged(joined); } -void Core::Conference::onRoomLeft() -{ +void Core::Conference::onRoomLeft() { joined = false; emit joinedChanged(joined); } -void Core::Conference::onRoomNameChanged(const QString& p_name) -{ +void Core::Conference::onRoomNameChanged(const QString& p_name) { setName(p_name); } -void Core::Conference::onRoomNickNameChanged(const QString& p_nick) -{ +void Core::Conference::onRoomNickNameChanged(const QString& p_nick) { if (p_nick != nick) { nick = p_nick; emit nickChanged(nick); } } -void Core::Conference::onRoomError(const QXmppStanza::Error& err) -{ +void Core::Conference::onRoomError(const QXmppStanza::Error& err) { qDebug() << "MUC" << jid << "error:" << err.text(); } -void Core::Conference::onRoomParticipantAdded(const QString& p_name) -{ +void Core::Conference::onRoomParticipantAdded(const QString& p_name) { QStringList comps = p_name.split("/"); QString resource = comps.back(); QXmppPresence pres = room->participantPresence(p_name); QXmppMucItem mi = pres.mucItem(); - if (resource == jid) { + if (resource == jid) resource = ""; - } std::map::const_iterator itr = exParticipants.find(resource); bool hasAvatar = itr != exParticipants.end(); @@ -166,19 +150,7 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name) ) } }; - - if (hasAvatar) { - if (itr->second.autogenerated) { - cData.insert("avatarState", static_cast(Shared::Avatar::valid)); - } else { - cData.insert("avatarState", static_cast(Shared::Avatar::autocreated)); - } - cData.insert("avatarPath", avatarPath(resource) + "." + itr->second.type); - } else { - cData.insert("avatarState", static_cast(Shared::Avatar::empty)); - cData.insert("avatarPath", ""); - emit requestVCard(p_name); - } + careAboutAvatar(hasAvatar, itr->second, cData, resource, p_name); emit addParticipant(resource, cData); } @@ -196,9 +168,9 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name) break; case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load if (hasAvatar) { - if (itr->second.autogenerated || itr->second.hash != pres.photoHash()) { + if (itr->second.autogenerated || itr->second.hash != pres.photoHash()) emit requestVCard(p_name); - } + } else { emit requestVCard(p_name); } @@ -207,8 +179,7 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name) } } -void Core::Conference::onRoomParticipantChanged(const QString& p_name) -{ +void Core::Conference::onRoomParticipantChanged(const QString& p_name) { QStringList comps = p_name.split("/"); QString resource = comps.back(); QXmppPresence pres = room->participantPresence(p_name); @@ -216,9 +187,8 @@ void Core::Conference::onRoomParticipantChanged(const QString& p_name) handlePresence(pres); if (resource != jid) { QDateTime lastInteraction = pres.lastUserInteraction(); - if (!lastInteraction.isValid()) { + if (!lastInteraction.isValid()) lastInteraction = QDateTime::currentDateTimeUtc(); - } emit changeParticipant(resource, { {"lastActivity", lastInteraction}, @@ -237,8 +207,7 @@ void Core::Conference::onRoomParticipantChanged(const QString& p_name) } } -void Core::Conference::onRoomParticipantRemoved(const QString& p_name) -{ +void Core::Conference::onRoomParticipantRemoved(const QString& p_name) { QStringList comps = p_name.split("/"); QString resource = comps.back(); if (resource == jid) { @@ -248,29 +217,24 @@ void Core::Conference::onRoomParticipantRemoved(const QString& p_name) } } -QString Core::Conference::getSubject() const -{ - if (joined) { +QString Core::Conference::getSubject() const { + if (joined) return room->subject(); - } else { + else return ""; - } } -void Core::Conference::onRoomSubjectChanged(const QString& p_name) -{ +void Core::Conference::onRoomSubjectChanged(const QString& p_name) { emit subjectChanged(p_name); } -void Core::Conference::handlePresence(const QXmppPresence& pres) -{ +void Core::Conference::handlePresence(const QXmppPresence& pres) { QString id = pres.from(); QStringList comps = id.split("/"); QString jid = comps.front(); QString resource(""); - if (comps.size() > 1) { + if (comps.size() > 1) resource = comps.back(); - } switch (pres.vCardUpdateType()) { case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo @@ -284,14 +248,13 @@ void Core::Conference::handlePresence(const QXmppPresence& pres) setAutoGeneratedAvatar(resource); } } - break; + break; case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load Archive::AvatarInfo info; bool hasAvatar = readAvatarInfo(info, resource); if (hasAvatar) { - if (info.autogenerated || info.hash != pres.photoHash()) { + if (info.autogenerated || info.hash != pres.photoHash()) emit requestVCard(id); - } } else { emit requestVCard(id); } @@ -300,17 +263,15 @@ void Core::Conference::handlePresence(const QXmppPresence& pres) } } -bool Core::Conference::setAutoGeneratedAvatar(const QString& resource) -{ +bool Core::Conference::setAutoGeneratedAvatar(const QString& resource) { Archive::AvatarInfo newInfo; bool result = RosterItem::setAutoGeneratedAvatar(newInfo, resource); if (result && resource.size() != 0) { std::map::iterator itr = exParticipants.find(resource); - if (itr == exParticipants.end()) { + if (itr == exParticipants.end()) exParticipants.insert(std::make_pair(resource, newInfo)); - } else { + else itr->second = newInfo; - } emit changeParticipant(resource, { {"avatarState", static_cast(Shared::Avatar::autocreated)}, {"avatarPath", avatarPath(resource) + "." + newInfo.type} @@ -320,17 +281,15 @@ bool Core::Conference::setAutoGeneratedAvatar(const QString& resource) return result; } -bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource) -{ +bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource) { bool result = RosterItem::setAvatar(data, info, resource); if (result && resource.size() != 0) { if (data.size() > 0) { std::map::iterator itr = exParticipants.find(resource); - if (itr == exParticipants.end()) { + if (itr == exParticipants.end()) exParticipants.insert(std::make_pair(resource, info)); - } else { + else itr->second = info; - } emit changeParticipant(resource, { {"avatarState", static_cast(Shared::Avatar::autocreated)}, @@ -338,9 +297,8 @@ bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& in }); } else { std::map::iterator itr = exParticipants.find(resource); - if (itr != exParticipants.end()) { + if (itr != exParticipants.end()) exParticipants.erase(itr); - } emit changeParticipant(resource, { {"avatarState", static_cast(Shared::Avatar::empty)}, @@ -353,10 +311,8 @@ bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& in return result; } -void Core::Conference::handleResponseVCard(const QXmppVCardIq& card, const QString &resource, Shared::VCard& out) -{ +void Core::Conference::handleResponseVCard(const QXmppVCardIq& card, const QString &resource, Shared::VCard& out) { RosterItem::handleResponseVCard(card, resource, out); - if (resource.size() > 0) { emit changeParticipant(resource, { {"avatarState", static_cast(out.getAvatarType())}, @@ -365,11 +321,21 @@ void Core::Conference::handleResponseVCard(const QXmppVCardIq& card, const QStri } } -QMap Core::Conference::getAllAvatars() const -{ +QMap Core::Conference::getAllAvatars() const { QMap result; - for (const std::pair& pair : exParticipants) { + for (const std::pair& pair : exParticipants) result.insert(pair.first, avatarPath(pair.first) + "." + pair.second.type); - } return result; } + +QMap Core::Conference::getInfo() const { + QMap data = RosterItem::getInfo(); + + data.insert("autoJoin", getAutoJoin()); + data.insert("joined", getJoined()); + data.insert("nick", getNick()); + data.insert("avatars", getAllAvatars()); + + return data; +} + diff --git a/core/conference.h b/core/conference.h index 00ade47..3c077e3 100644 --- a/core/conference.h +++ b/core/conference.h @@ -49,12 +49,13 @@ public: bool getJoined() const; void setJoined(bool p_joined); - bool getAutoJoin(); + bool getAutoJoin() const; void setAutoJoin(bool p_autoJoin); void handlePresence(const QXmppPresence & pres) override; bool setAutoGeneratedAvatar(const QString& resource = "") override; void handleResponseVCard(const QXmppVCardIq & card, const QString &resource, Shared::VCard& out) override; QMap getAllAvatars() const; + QMap getInfo() const override; signals: void nickChanged(const QString& nick); diff --git a/core/contact.cpp b/core/contact.cpp index 3030f4d..93139ef 100644 --- a/core/contact.cpp +++ b/core/contact.cpp @@ -24,54 +24,43 @@ Core::Contact::Contact(const QString& pJid, const QString& account, QObject* par groups(), subscriptionState(Shared::SubscriptionState::unknown), pep(Shared::Support::unknown) -{ -} +{} -Core::Contact::~Contact() -{ -} +Core::Contact::~Contact() {} -QSet Core::Contact::getGroups() const -{ +QSet Core::Contact::getGroups() const { return groups; } -unsigned int Core::Contact::groupsCount() const -{ +unsigned int Core::Contact::groupsCount() const { return groups.size(); } -void Core::Contact::setGroups(const QSet& set) -{ +void Core::Contact::setGroups(const QSet& set) { QSet toRemove = groups - set; QSet toAdd = set - groups; groups = set; - for (QSet::iterator itr = toRemove.begin(), end = toRemove.end(); itr != end; ++itr) { - emit groupRemoved(*itr); - } + for (const QString& group : toRemove) + emit groupRemoved(group); - for (QSet::iterator itr = toAdd.begin(), end = toAdd.end(); itr != end; ++itr) { - emit groupAdded(*itr); - } + for (const QString& group : toAdd) + emit groupAdded(group); } -Shared::SubscriptionState Core::Contact::getSubscriptionState() const -{ +Shared::SubscriptionState Core::Contact::getSubscriptionState() const { return subscriptionState; } -void Core::Contact::setSubscriptionState(Shared::SubscriptionState state) -{ +void Core::Contact::setSubscriptionState(Shared::SubscriptionState state) { if (subscriptionState != state) { subscriptionState = state; emit subscriptionStateChanged(subscriptionState); } } -void Core::Contact::handlePresence(const QXmppPresence& pres) -{ +void Core::Contact::handlePresence(const QXmppPresence& pres) { switch (pres.vCardUpdateType()) { case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo break; @@ -101,13 +90,21 @@ void Core::Contact::handlePresence(const QXmppPresence& pres) } void Core::Contact::setPepSupport(Shared::Support support) { - if (pep != support) { + if (pep != support) pep = support; - } } Shared::Support Core::Contact::getPepSupport() const { - return pep;} + return pep; +} + +QMap Core::Contact::getInfo() const { + QMap data = RosterItem::getInfo(); + + data.insert("state", QVariant::fromValue(subscriptionState)); + + return data; +} diff --git a/core/contact.h b/core/contact.h index 01c082f..bde95f2 100644 --- a/core/contact.h +++ b/core/contact.h @@ -45,6 +45,7 @@ public: Shared::Support getPepSupport() const; void handlePresence(const QXmppPresence & pres) override; + QMap getInfo() const override; signals: void groupAdded(const QString& name); diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index 2c5b16d..020ab6f 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -83,7 +83,7 @@ void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) {"state", static_cast(Shared::Message::State::error)}, {"errorText", msg.error().text()} }; - if (cnt != 0) { + if (cnt != nullptr) { cnt->changeMessage(id, cData); } emit acc->changeMessage(jid, id, cData); @@ -291,7 +291,7 @@ void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& QMap cData = {{"state", static_cast(Shared::Message::State::delivered)}}; RosterItem* ri = acc->rh->getRosterItem(std::get<2>(ids)); - if (ri != 0) { + if (ri != nullptr) { ri->changeMessage(std::get<1>(ids), cData); } emit acc->changeMessage(std::get<2>(ids), std::get<1>(ids), cData); @@ -346,7 +346,7 @@ void Core::MessageHandler::performSending(Shared::Message data, const QString& o } else { realId = id; } - if (ri != 0) { + if (ri != nullptr) { if (newMessage) { ri->appendMessageToArchive(data); } else { @@ -429,7 +429,7 @@ void Core::MessageHandler::prepareUpload(const Shared::Message& data, bool newMe QString jid = data.getPenPalJid(); QString id = data.getId(); RosterItem* ri = acc->rh->getRosterItem(jid); - if (!ri) { + if (ri == nullptr) { qDebug() << "An attempt to initialize upload in" << acc->name << "for pal" << jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send"; return; } @@ -517,7 +517,7 @@ void Core::MessageHandler::onDownloadFileComplete(const std::listgetName()) { RosterItem* cnt = acc->rh->getRosterItem(info.jid); - if (cnt != 0) { + if (cnt != nullptr) { if (cnt->changeMessage(info.messageId, cData)) { emit acc->changeMessage(info.jid, info.messageId, cData); } @@ -553,7 +553,7 @@ void Core::MessageHandler::onUploadFileComplete(const std::listgetName()) { RosterItem* ri = acc->rh->getRosterItem(info.jid); - if (ri != 0) { + if (ri != nullptr) { Shared::Message msg = ri->getMessage(info.messageId); msg.setAttachPath(path); sendMessageWithLocalUploadedFile(msg, url, false); @@ -584,7 +584,7 @@ static const std::set allowedToChangeKeys({ void Core::MessageHandler::requestChangeMessage(const QString& jid, const QString& messageId, const QMap& data) { RosterItem* cnt = acc->rh->getRosterItem(jid); - if (cnt != 0) { + if (cnt != nullptr) { bool allSupported = true; QString unsupportedString; for (QMap::const_iterator itr = data.begin(); itr != data.end(); ++itr) { //I need all this madness @@ -607,7 +607,7 @@ void Core::MessageHandler::requestChangeMessage(const QString& jid, const QStrin void Core::MessageHandler::resendMessage(const QString& jid, const QString& id) { RosterItem* cnt = acc->rh->getRosterItem(jid); - if (cnt != 0) { + if (cnt != nullptr) { try { Shared::Message msg = cnt->getMessage(id); if (msg.getState() == Shared::Message::State::error) { diff --git a/core/handlers/rosterhandler.cpp b/core/handlers/rosterhandler.cpp index cccd47d..3738d2c 100644 --- a/core/handlers/rosterhandler.cpp +++ b/core/handlers/rosterhandler.cpp @@ -41,19 +41,23 @@ void Core::RosterHandler::initialize() { connect(acc, &Account::pepSupportChanged, this, &RosterHandler::onPepSupportedChanged); } -Core::RosterHandler::~RosterHandler() -{ - for (std::map::const_iterator itr = contacts.begin(), end = contacts.end(); itr != end; ++itr) { - delete itr->second; - } - - for (std::map::const_iterator itr = conferences.begin(), end = conferences.end(); itr != end; ++itr) { - delete itr->second; - } +Core::RosterHandler::~RosterHandler() { + clear(); } -void Core::RosterHandler::onRosterReceived() -{ +void Core::RosterHandler::clear() { + for (const std::pair& pair : contacts) + delete pair.second; + + for (const std::pair& pair : conferences) + delete pair.second; + + contacts.clear(); + conferences.clear(); +} + + +void Core::RosterHandler::onRosterReceived() { QStringList bj = acc->rm->getRosterBareJids(); for (int i = 0; i < bj.size(); ++i) { const QString& jid = bj[i]; @@ -61,8 +65,7 @@ void Core::RosterHandler::onRosterReceived() } } -void Core::RosterHandler::onRosterItemAdded(const QString& bareJid) -{ +void Core::RosterHandler::onRosterItemAdded(const QString& bareJid) { QString lcJid = bareJid.toLower(); addedAccount(lcJid); std::map::const_iterator itr = queuedContacts.find(lcJid); @@ -72,8 +75,7 @@ void Core::RosterHandler::onRosterItemAdded(const QString& bareJid) } } -void Core::RosterHandler::addedAccount(const QString& jid) -{ +void Core::RosterHandler::addedAccount(const QString& jid) { std::map::const_iterator itr = contacts.find(jid); QXmppRosterIq::Item re = acc->rm->getRosterEntry(jid); Contact* contact; @@ -82,7 +84,6 @@ void Core::RosterHandler::addedAccount(const QString& jid) newContact = true; contact = new Contact(jid, acc->name); contacts.insert(std::make_pair(jid, contact)); - } else { contact = itr->second; } @@ -94,12 +95,7 @@ void Core::RosterHandler::addedAccount(const QString& jid) contact->setName(re.name()); if (newContact) { - QMap cData({ - {"name", re.name()}, - {"state", QVariant::fromValue(state)} - }); - - careAboutAvatar(contact, cData); + QMap cData = contact->getInfo(); int grCount = 0; for (QSet::const_iterator itr = gr.begin(), end = gr.end(); itr != end; ++itr) { const QString& groupName = *itr; @@ -108,9 +104,9 @@ void Core::RosterHandler::addedAccount(const QString& jid) grCount++; } - if (grCount == 0) { + if (grCount == 0) emit acc->addContact(jid, "", cData); - } + if (acc->pepSupport == Shared::Support::supported) { acc->dm->requestInfo(jid); //acc->dm->requestItems(jid); @@ -119,49 +115,22 @@ void Core::RosterHandler::addedAccount(const QString& jid) } } -void Core::RosterHandler::addNewRoom(const QString& jid, const QString& nick, const QString& roomName, bool autoJoin) -{ +void Core::RosterHandler::addNewRoom(const QString& jid, const QString& nick, const QString& roomName, bool autoJoin) { QXmppMucRoom* room = acc->mm->addRoom(jid); QString lNick = nick; - if (lNick.size() == 0) { + if (lNick.size() == 0) lNick = acc->getName(); - } + Conference* conf = new Conference(jid, acc->getName(), autoJoin, roomName, lNick, room); conferences.insert(std::make_pair(jid, conf)); handleNewConference(conf); - QMap cData = { - {"autoJoin", conf->getAutoJoin()}, - {"joined", conf->getJoined()}, - {"nick", conf->getNick()}, - {"name", conf->getName()}, - {"avatars", conf->getAllAvatars()} - }; - careAboutAvatar(conf, cData); + QMap cData = conf->getInfo(); emit acc->addRoom(jid, cData); } -void Core::RosterHandler::careAboutAvatar(Core::RosterItem* item, QMap& data) -{ - Archive::AvatarInfo info; - bool hasAvatar = item->readAvatarInfo(info); - if (hasAvatar) { - if (info.autogenerated) { - data.insert("avatarState", QVariant::fromValue(Shared::Avatar::autocreated)); - } else { - data.insert("avatarState", QVariant::fromValue(Shared::Avatar::valid)); - } - data.insert("avatarPath", item->avatarPath() + "." + info.type); - } else { - data.insert("avatarState", QVariant::fromValue(Shared::Avatar::empty)); - data.insert("avatarPath", ""); - acc->delay->requestVCard(item->jid); - } -} - -void Core::RosterHandler::addContactRequest(const QString& jid, const QString& name, const QSet& groups) -{ +void Core::RosterHandler::addContactRequest(const QString& jid, const QString& name, const QSet& groups) { if (acc->state == Shared::ConnectionState::connected) { std::map::const_iterator itr = queuedContacts.find(jid); if (itr != queuedContacts.end()) { @@ -175,8 +144,7 @@ void Core::RosterHandler::addContactRequest(const QString& jid, const QString& n } } -void Core::RosterHandler::removeContactRequest(const QString& jid) -{ +void Core::RosterHandler::removeContactRequest(const QString& jid) { QString lcJid = jid.toLower(); if (acc->state == Shared::ConnectionState::connected) { std::set::const_iterator itr = outOfRosterContacts.find(lcJid); @@ -191,25 +159,23 @@ void Core::RosterHandler::removeContactRequest(const QString& jid) } } -void Core::RosterHandler::handleNewRosterItem(Core::RosterItem* contact) -{ +void Core::RosterHandler::handleNewRosterItem(Core::RosterItem* contact) { connect(contact, &RosterItem::needHistory, this->acc, &Account::onContactNeedHistory); connect(contact, &RosterItem::historyResponse, this->acc, &Account::onContactHistoryResponse); connect(contact, &RosterItem::nameChanged, this, &RosterHandler::onContactNameChanged); connect(contact, &RosterItem::avatarChanged, this, &RosterHandler::onContactAvatarChanged); + connect(contact, &RosterItem::encryptionChanged, this, &RosterHandler::onContactEncryptionChanged); connect(contact, &RosterItem::requestVCard, acc->delay, &DelayManager::Manager::getVCard); } -void Core::RosterHandler::handleNewContact(Core::Contact* contact) -{ +void Core::RosterHandler::handleNewContact(Core::Contact* contact) { handleNewRosterItem(contact); connect(contact, &Contact::groupAdded, this, &RosterHandler::onContactGroupAdded); connect(contact, &Contact::groupRemoved, this, &RosterHandler::onContactGroupRemoved); connect(contact, &Contact::subscriptionStateChanged, this, &RosterHandler::onContactSubscriptionStateChanged); } -void Core::RosterHandler::handleNewConference(Core::Conference* contact) -{ +void Core::RosterHandler::handleNewConference(Core::Conference* contact) { handleNewRosterItem(contact); connect(contact, &Conference::nickChanged, this, &RosterHandler::onMucNickNameChanged); connect(contact, &Conference::subjectChanged, this, &RosterHandler::onMucSubjectChanged); @@ -220,34 +186,27 @@ void Core::RosterHandler::handleNewConference(Core::Conference* contact) connect(contact, &Conference::removeParticipant, this, &RosterHandler::onMucRemoveParticipant); } -void Core::RosterHandler::onMucAddParticipant(const QString& nickName, const QMap& data) -{ +void Core::RosterHandler::onMucAddParticipant(const QString& nickName, const QMap& data) { Conference* room = static_cast(sender()); emit acc->addRoomParticipant(room->jid, nickName, data); } -void Core::RosterHandler::onMucChangeParticipant(const QString& nickName, const QMap& data) -{ +void Core::RosterHandler::onMucChangeParticipant(const QString& nickName, const QMap& data) { Conference* room = static_cast(sender()); emit acc->changeRoomParticipant(room->jid, nickName, data); } -void Core::RosterHandler::onMucRemoveParticipant(const QString& nickName) -{ +void Core::RosterHandler::onMucRemoveParticipant(const QString& nickName) { Conference* room = static_cast(sender()); emit acc->removeRoomParticipant(room->jid, nickName); } -void Core::RosterHandler::onMucSubjectChanged(const QString& subject) -{ +void Core::RosterHandler::onMucSubjectChanged(const QString& subject) { Conference* room = static_cast(sender()); - emit acc->changeRoom(room->jid, { - {"subject", subject} - }); + emit acc->changeRoom(room->jid, {{"subject", subject}}); } -void Core::RosterHandler::onContactGroupAdded(const QString& group) -{ +void Core::RosterHandler::onContactGroupAdded(const QString& group) { Contact* contact = static_cast(sender()); if (contact->groupsCount() == 1) { // not sure i need to handle it here, the situation with grouped and ungrouped contacts handled on the client anyway @@ -255,14 +214,14 @@ void Core::RosterHandler::onContactGroupAdded(const QString& group) QMap cData({ {"name", contact->getName()}, - {"state", QVariant::fromValue(contact->getSubscriptionState())} + {"state", QVariant::fromValue(contact->getSubscriptionState())}, + {"encryption", contact->isEncryptionEnabled()} }); addToGroup(contact->jid, group); emit acc->addContact(contact->jid, group, cData); } -void Core::RosterHandler::onContactGroupRemoved(const QString& group) -{ +void Core::RosterHandler::onContactGroupRemoved(const QString& group) { Contact* contact = static_cast(sender()); if (contact->groupsCount() == 0) { // not sure i need to handle it here, the situation with grouped and ungrouped contacts handled on the client anyway @@ -272,17 +231,17 @@ void Core::RosterHandler::onContactGroupRemoved(const QString& group) removeFromGroup(contact->jid, group); } -void Core::RosterHandler::onContactNameChanged(const QString& cname) -{ - Contact* contact = static_cast(sender()); - QMap cData({ - {"name", cname}, - }); - emit acc->changeContact(contact->jid, cData); +void Core::RosterHandler::onContactNameChanged(const QString& cname) { + RosterItem* contact = static_cast(sender()); + emit acc->changeContact(contact->jid, {{"name", cname}}); } -void Core::RosterHandler::onContactSubscriptionStateChanged(Shared::SubscriptionState cstate) -{ +void Core::RosterHandler::onContactEncryptionChanged(bool value) { + RosterItem* contact = static_cast(sender()); + emit acc->changeContact(contact->jid, {{"encryption", value}}); +} + +void Core::RosterHandler::onContactSubscriptionStateChanged(Shared::SubscriptionState cstate) { Contact* contact = static_cast(sender()); QMap cData({ {"state", QVariant::fromValue(cstate)}, @@ -290,8 +249,7 @@ void Core::RosterHandler::onContactSubscriptionStateChanged(Shared::Subscription emit acc->changeContact(contact->jid, cData); } -void Core::RosterHandler::addToGroup(const QString& jid, const QString& group) -{ +void Core::RosterHandler::addToGroup(const QString& jid, const QString& group) { std::map>::iterator gItr = groups.find(group); if (gItr == groups.end()) { gItr = groups.insert(std::make_pair(group, std::set())).first; @@ -300,8 +258,7 @@ void Core::RosterHandler::addToGroup(const QString& jid, const QString& group) gItr->second.insert(jid.toLower()); } -void Core::RosterHandler::removeFromGroup(const QString& jid, const QString& group) -{ +void Core::RosterHandler::removeFromGroup(const QString& jid, const QString& group) { QSet toRemove; std::map>::iterator itr = groups.find(group); if (itr == groups.end()) { @@ -319,24 +276,21 @@ void Core::RosterHandler::removeFromGroup(const QString& jid, const QString& gro } } -Core::RosterItem * Core::RosterHandler::getRosterItem(const QString& jid) -{ - RosterItem* item = 0; +Core::RosterItem * Core::RosterHandler::getRosterItem(const QString& jid) { + RosterItem* item = nullptr; QString lcJid = jid.toLower(); std::map::const_iterator citr = contacts.find(lcJid); if (citr != contacts.end()) { item = citr->second; } else { std::map::const_iterator coitr = conferences.find(lcJid); - if (coitr != conferences.end()) { + if (coitr != conferences.end()) item = coitr->second; - } } return item; } -Core::Conference * Core::RosterHandler::getConference(const QString& jid) -{ +Core::Conference * Core::RosterHandler::getConference(const QString& jid) { Conference* item = 0; std::map::const_iterator coitr = conferences.find(jid.toLower()); if (coitr != conferences.end()) { @@ -345,8 +299,7 @@ Core::Conference * Core::RosterHandler::getConference(const QString& jid) return item; } -Core::Contact * Core::RosterHandler::getContact(const QString& jid) -{ +Core::Contact * Core::RosterHandler::getContact(const QString& jid) { Contact* item = 0; std::map::const_iterator citr = contacts.find(jid.toLower()); if (citr != contacts.end()) { @@ -355,22 +308,21 @@ Core::Contact * Core::RosterHandler::getContact(const QString& jid) return item; } -Core::Contact * Core::RosterHandler::addOutOfRosterContact(const QString& jid) -{ +Core::Contact * Core::RosterHandler::addOutOfRosterContact(const QString& jid) { QString lcJid = jid.toLower(); Contact* cnt = new Contact(lcJid, acc->name); contacts.insert(std::make_pair(lcJid, cnt)); outOfRosterContacts.insert(lcJid); cnt->setSubscriptionState(Shared::SubscriptionState::unknown); emit acc->addContact(lcJid, "", QMap({ - {"state", QVariant::fromValue(Shared::SubscriptionState::unknown)} + {"state", QVariant::fromValue(Shared::SubscriptionState::unknown)}, + {"encryption", false} })); handleNewContact(cnt); return cnt; } -void Core::RosterHandler::onRosterItemChanged(const QString& bareJid) -{ +void Core::RosterHandler::onRosterItemChanged(const QString& bareJid) { QString lcJid = bareJid.toLower(); std::map::const_iterator itr = contacts.find(lcJid); if (itr == contacts.end()) { @@ -387,8 +339,7 @@ void Core::RosterHandler::onRosterItemChanged(const QString& bareJid) contact->setName(re.name()); } -void Core::RosterHandler::onRosterItemRemoved(const QString& bareJid) -{ +void Core::RosterHandler::onRosterItemRemoved(const QString& bareJid) { QString lcJid = bareJid.toLower(); std::map::const_iterator itr = contacts.find(lcJid); if (itr == contacts.end()) { @@ -406,14 +357,12 @@ void Core::RosterHandler::onRosterItemRemoved(const QString& bareJid) contact->deleteLater(); } -void Core::RosterHandler::onMucRoomAdded(QXmppMucRoom* room) -{ +void Core::RosterHandler::onMucRoomAdded(QXmppMucRoom* room) { qDebug() << "room" << room->jid() << "added with name" << room->name() << ", account" << acc->getName() << "joined:" << room->isJoined(); } -void Core::RosterHandler::bookmarksReceived(const QXmppBookmarkSet& bookmarks) -{ +void Core::RosterHandler::bookmarksReceived(const QXmppBookmarkSet& bookmarks) { QList confs = bookmarks.conferences(); for (QList::const_iterator itr = confs.begin(), end = confs.end(); itr != end; ++itr) { const QXmppBookmarkConference& c = *itr; @@ -423,54 +372,42 @@ void Core::RosterHandler::bookmarksReceived(const QXmppBookmarkSet& bookmarks) if (cItr == conferences.end()) { addNewRoom(jid, c.nickName(), c.name(), c.autoJoin()); } else { - if (c.autoJoin()) { + if (c.autoJoin()) cItr->second->setJoined(true); - } else { + else cItr->second->setAutoJoin(false); - } } } } -void Core::RosterHandler::onMucJoinedChanged(bool joined) -{ +void Core::RosterHandler::onMucJoinedChanged(bool joined){ Conference* room = static_cast(sender()); - emit acc->changeRoom(room->jid, { - {"joined", joined} - }); + emit acc->changeRoom(room->jid, {{"joined", joined}}); } -void Core::RosterHandler::onMucAutoJoinChanged(bool autoJoin) -{ +void Core::RosterHandler::onMucAutoJoinChanged(bool autoJoin) { storeConferences(); Conference* room = static_cast(sender()); - emit acc->changeRoom(room->jid, { - {"autoJoin", autoJoin} - }); + emit acc->changeRoom(room->jid, {{"autoJoin", autoJoin}}); } -void Core::RosterHandler::onMucNickNameChanged(const QString& nickName) -{ +void Core::RosterHandler::onMucNickNameChanged(const QString& nickName){ storeConferences(); Conference* room = static_cast(sender()); - emit acc->changeRoom(room->jid, { - {"nick", nickName} - }); + emit acc->changeRoom(room->jid, {{"nick", nickName}}); } -Shared::SubscriptionState Core::RosterHandler::castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs) -{ +Shared::SubscriptionState Core::RosterHandler::castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs){ Shared::SubscriptionState state; - if (qs == QXmppRosterIq::Item::NotSet) { + if (qs == QXmppRosterIq::Item::NotSet) state = Shared::SubscriptionState::unknown; - } else { + else state = static_cast(qs); - } + return state; } -void Core::RosterHandler::storeConferences() -{ +void Core::RosterHandler::storeConferences() { QXmppBookmarkSet bms = acc->bm->bookmarks(); QList confs; for (std::map::const_iterator itr = conferences.begin(), end = conferences.end(); itr != end; ++itr) { @@ -486,8 +423,7 @@ void Core::RosterHandler::storeConferences() acc->bm->setBookmarks(bms); } -void Core::RosterHandler::clearConferences() -{ +void Core::RosterHandler::clearConferences() { for (std::map::const_iterator itr = conferences.begin(), end = conferences.end(); itr != end; itr++) { itr->second->deleteLater(); emit acc->removeRoom(itr->first); @@ -495,8 +431,7 @@ void Core::RosterHandler::clearConferences() conferences.clear(); } -void Core::RosterHandler::removeRoomRequest(const QString& jid) -{ +void Core::RosterHandler::removeRoomRequest(const QString& jid) { QString lcJid = jid.toLower(); std::map::const_iterator itr = conferences.find(lcJid); if (itr == conferences.end()) { @@ -508,8 +443,7 @@ void Core::RosterHandler::removeRoomRequest(const QString& jid) storeConferences(); } -void Core::RosterHandler::addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin) -{ +void Core::RosterHandler::addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin) { QString lcJid = jid.toLower(); std::map::const_iterator cItr = conferences.find(lcJid); if (cItr == conferences.end()) { @@ -520,8 +454,7 @@ void Core::RosterHandler::addRoomRequest(const QString& jid, const QString& nick } } -void Core::RosterHandler::addContactToGroupRequest(const QString& jid, const QString& groupName) -{ +void Core::RosterHandler::addContactToGroupRequest(const QString& jid, const QString& groupName) { QString lcJid = jid.toLower(); std::map::const_iterator itr = contacts.find(lcJid); if (itr == contacts.end()) { @@ -545,8 +478,7 @@ void Core::RosterHandler::addContactToGroupRequest(const QString& jid, const QSt } } -void Core::RosterHandler::removeContactFromGroupRequest(const QString& jid, const QString& groupName) -{ +void Core::RosterHandler::removeContactFromGroupRequest(const QString& jid, const QString& groupName) { QString lcJid = jid.toLower(); std::map::const_iterator itr = contacts.find(lcJid); if (itr == contacts.end()) { @@ -571,19 +503,17 @@ void Core::RosterHandler::removeContactFromGroupRequest(const QString& jid, cons } } -void Core::RosterHandler::onContactAvatarChanged(Shared::Avatar type, const QString& path) -{ +void Core::RosterHandler::onContactAvatarChanged(Shared::Avatar type, const QString& path) { RosterItem* item = static_cast(sender()); QMap cData({ - {"avatarState", static_cast(type)}, + {"avatarState", QVariant::fromValue(type)}, {"avatarPath", path} }); emit acc->changeContact(item->jid, cData); } -void Core::RosterHandler::handleOffline() -{ +void Core::RosterHandler::handleOffline() { for (const std::pair& pair : conferences) { pair.second->clearArchiveRequests(); pair.second->downgradeDatabaseState(); @@ -595,13 +525,11 @@ void Core::RosterHandler::handleOffline() } -void Core::RosterHandler::onPepSupportedChanged(Shared::Support support) -{ +void Core::RosterHandler::onPepSupportedChanged(Shared::Support support) { if (support == Shared::Support::supported) { for (const std::pair& pair : contacts) { - if (pair.second->getPepSupport() == Shared::Support::unknown) { + if (pair.second->getPepSupport() == Shared::Support::unknown) acc->dm->requestInfo(pair.first); - } } } } diff --git a/core/handlers/rosterhandler.h b/core/handlers/rosterhandler.h index 62a7b8b..63f291c 100644 --- a/core/handlers/rosterhandler.h +++ b/core/handlers/rosterhandler.h @@ -68,6 +68,7 @@ public: void clearConferences(); void initialize(); + void clear(); private slots: void onRosterReceived(); @@ -91,6 +92,7 @@ private slots: void onContactNameChanged(const QString& name); void onContactSubscriptionStateChanged(Shared::SubscriptionState state); void onContactAvatarChanged(Shared::Avatar, const QString& path); + void onContactEncryptionChanged(bool value); void onPepSupportedChanged(Shared::Support support); private: @@ -101,7 +103,6 @@ private: void handleNewRosterItem(Core::RosterItem* contact); void handleNewContact(Core::Contact* contact); void handleNewConference(Core::Conference* contact); - void careAboutAvatar(Core::RosterItem* item, QMap& data); static Shared::SubscriptionState castSubscriptionState(QXmppRosterIq::Item::SubscriptionType qs); diff --git a/core/handlers/vcardhandler.cpp b/core/handlers/vcardhandler.cpp index d4125e8..33b9c31 100644 --- a/core/handlers/vcardhandler.cpp +++ b/core/handlers/vcardhandler.cpp @@ -88,7 +88,7 @@ void Core::VCardHandler::onVCardReceived(const QXmppVCardIq& card) { } RosterItem* item = acc->rh->getRosterItem(jid); - if (item == 0) { + if (item == nullptr) { if (jid == acc->getBareJid()) onOwnVCardReceived(card); else diff --git a/core/rosteritem.cpp b/core/rosteritem.cpp index 0bac4a4..545e47f 100644 --- a/core/rosteritem.cpp +++ b/core/rosteritem.cpp @@ -41,39 +41,33 @@ Core::RosterItem::RosterItem(const QString& pJid, const QString& pAccount, QObje archive->open(account); if (archive->size() != 0) { - if (archive->isFromTheBeginning()) { + if (archive->isFromTheBeginning()) archiveState = beginning; - } else { + else archiveState = chunk; - } } } -Core::RosterItem::~RosterItem() -{ +Core::RosterItem::~RosterItem() { delete archive; } -Core::RosterItem::ArchiveState Core::RosterItem::getArchiveState() const -{ +Core::RosterItem::ArchiveState Core::RosterItem::getArchiveState() const { return archiveState; } -QString Core::RosterItem::getName() const -{ +QString Core::RosterItem::getName() const { return name; } -void Core::RosterItem::setName(const QString& n) -{ +void Core::RosterItem::setName(const QString& n) { if (name != n) { name = n; emit nameChanged(name); } } -void Core::RosterItem::addMessageToArchive(const Shared::Message& msg) -{ +void Core::RosterItem::addMessageToArchive(const Shared::Message& msg) { if (msg.storable()) { hisoryCache.push_back(msg); std::map::iterator itr = toCorrect.find(msg.getId()); @@ -87,8 +81,7 @@ void Core::RosterItem::addMessageToArchive(const Shared::Message& msg) } } -void Core::RosterItem::correctMessageInArchive(const QString& originalId, const Shared::Message& msg) -{ +void Core::RosterItem::correctMessageInArchive(const QString& originalId, const Shared::Message& msg) { if (msg.storable()) { QDateTime thisTime = msg.getTime(); std::map::iterator itr = toCorrect.find(originalId); @@ -109,8 +102,7 @@ void Core::RosterItem::correctMessageInArchive(const QString& originalId, const } } -void Core::RosterItem::requestHistory(int count, const QString& before) -{ +void Core::RosterItem::requestHistory(int count, const QString& before) { if (syncronizing) { requestCache.emplace_back(count, before); } else { @@ -118,8 +110,7 @@ void Core::RosterItem::requestHistory(int count, const QString& before) } } -void Core::RosterItem::nextRequest() -{ +void Core::RosterItem::nextRequest() { if (syncronizing) { if (requestedCount != -1) { bool last = false; @@ -157,8 +148,7 @@ void Core::RosterItem::nextRequest() } } -void Core::RosterItem::performRequest(int count, const QString& before) -{ +void Core::RosterItem::performRequest(int count, const QString& before) { syncronizing = true; requestedCount = count; requestedBefore = before; @@ -246,8 +236,7 @@ void Core::RosterItem::performRequest(int count, const QString& before) } } -QString Core::RosterItem::getId(const Shared::Message& msg) -{ +QString Core::RosterItem::getId(const Shared::Message& msg) { QString id; if (muc) { id = msg.getStanzaId(); @@ -257,8 +246,7 @@ QString Core::RosterItem::getId(const Shared::Message& msg) return id; } -void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg) -{ +void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg) { if (msg.getId().size() > 0) { if (msg.storable()) { switch (archiveState) { @@ -299,8 +287,7 @@ void Core::RosterItem::appendMessageToArchive(const Shared::Message& msg) } } -bool Core::RosterItem::changeMessage(const QString& id, const QMap& data) -{ +bool Core::RosterItem::changeMessage(const QString& id, const QMap& data) { bool found = false; for (Shared::Message& msg : appendCache) { if (msg.getId() == id) { @@ -341,8 +328,7 @@ bool Core::RosterItem::changeMessage(const QString& id, const QMap 0) { added = archive->addElements(hisoryCache); @@ -429,51 +415,43 @@ void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firs } } -QString Core::RosterItem::getServer() const -{ +QString Core::RosterItem::getServer() const { QStringList lst = jid.split("@"); return lst.back(); } -bool Core::RosterItem::isMuc() const -{ +bool Core::RosterItem::isMuc() const { return muc; } -QString Core::RosterItem::avatarPath(const QString& resource) const -{ +QString Core::RosterItem::avatarPath(const QString& resource) const { QString path = folderPath() + "/" + (resource.size() == 0 ? jid : resource); return path; } -QString Core::RosterItem::folderPath() const -{ +QString Core::RosterItem::folderPath() const { QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); path += "/" + account + "/" + jid; return path; } -bool Core::RosterItem::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource) -{ +bool Core::RosterItem::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource) { bool result = archive->setAvatar(data, info, false, resource); if (resource.size() == 0 && result) { - if (data.size() == 0) { + if (data.size() == 0) emit avatarChanged(Shared::Avatar::empty, ""); - } else { + else emit avatarChanged(Shared::Avatar::valid, avatarPath(resource) + "." + info.type); - } } return result; } -bool Core::RosterItem::setAutoGeneratedAvatar(const QString& resource) -{ +bool Core::RosterItem::setAutoGeneratedAvatar(const QString& resource) { Archive::AvatarInfo info; return setAutoGeneratedAvatar(info, resource); } -bool Core::RosterItem::setAutoGeneratedAvatar(Archive::AvatarInfo& info, const QString& resource) -{ +bool Core::RosterItem::setAutoGeneratedAvatar(Archive::AvatarInfo& info, const QString& resource) { QImage image(96, 96, QImage::Format_ARGB32_Premultiplied); QPainter painter(&image); quint8 colorIndex = rand() % Shared::colorPalette.size(); @@ -483,11 +461,11 @@ bool Core::RosterItem::setAutoGeneratedAvatar(Archive::AvatarInfo& info, const Q f.setBold(true); f.setPixelSize(72); painter.setFont(f); - if (bg.lightnessF() > 0.5) { + if (bg.lightnessF() > 0.5) painter.setPen(Qt::black); - } else { + else painter.setPen(Qt::white); - } + painter.drawText(image.rect(), Qt::AlignCenter | Qt::AlignVCenter, resource.size() == 0 ? jid.at(0).toUpper() : resource.at(0).toUpper()); QByteArray arr; QBuffer stream(&arr); @@ -495,19 +473,17 @@ bool Core::RosterItem::setAutoGeneratedAvatar(Archive::AvatarInfo& info, const Q image.save(&stream, "PNG"); stream.close(); bool result = archive->setAvatar(arr, info, true, resource); - if (resource.size() == 0 && result) { + if (resource.size() == 0 && result) emit avatarChanged(Shared::Avatar::autocreated, avatarPath(resource) + ".png"); - } + return result; } -bool Core::RosterItem::readAvatarInfo(Archive::AvatarInfo& target, const QString& resource) const -{ +bool Core::RosterItem::readAvatarInfo(Archive::AvatarInfo& target, const QString& resource) const { return archive->readAvatarInfo(target, resource); } -void Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QString& resource, Shared::VCard& vCard) -{ +void Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QString& resource, Shared::VCard& vCard) { Archive::AvatarInfo info; Archive::AvatarInfo newInfo; bool hasAvatar = readAvatarInfo(info, resource); @@ -532,9 +508,9 @@ void Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QStri } } } else { - if (!hasAvatar || !info.autogenerated) { + if (!hasAvatar || !info.autogenerated) setAutoGeneratedAvatar(resource); - } + type = Shared::Avatar::autocreated; path = avatarPath(resource) + ".png"; } @@ -542,13 +518,11 @@ void Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QStri vCard.setAvatarType(type); vCard.setAvatarPath(path); - if (resource.size() == 0) { + if (resource.size() == 0) emit avatarChanged(vCard.getAvatarType(), vCard.getAvatarPath()); - } } -void Core::RosterItem::clearArchiveRequests() -{ +void Core::RosterItem::clearArchiveRequests() { syncronizing = false; requestedCount = 0; requestedBefore = ""; @@ -564,30 +538,72 @@ void Core::RosterItem::clearArchiveRequests() requestCache.clear(); } -void Core::RosterItem::downgradeDatabaseState() -{ - if (archiveState == ArchiveState::complete) { +void Core::RosterItem::downgradeDatabaseState() { + if (archiveState == ArchiveState::complete) archiveState = ArchiveState::beginning; - } + - if (archiveState == ArchiveState::end) { + if (archiveState == ArchiveState::end) archiveState = ArchiveState::chunk; - } } -Shared::Message Core::RosterItem::getMessage(const QString& id) -{ +Shared::Message Core::RosterItem::getMessage(const QString& id) { for (const Shared::Message& msg : appendCache) { - if (msg.getId() == id) { + if (msg.getId() == id) return msg; - } } for (Shared::Message& msg : hisoryCache) { - if (msg.getId() == id) { + if (msg.getId() == id) return msg; - } } return archive->getElement(id); } + +bool Core::RosterItem::isEncryptionEnabled() const { + return archive->isEncryptionEnabled(); +} + +void Core::RosterItem::enableEncryption(bool value) { + bool changed = archive->setEncryptionEnabled(value); + if (changed) + emit encryptionChanged(value); +} + +QMap Core::RosterItem::getInfo() const { + QMap result({ + {"name", name}, + {"encryption", isEncryptionEnabled()}, + }); + Archive::AvatarInfo info; + bool hasAvatar = readAvatarInfo(info); + careAboutAvatar(hasAvatar, info, result); + + return result; +} + + +void Core::RosterItem::careAboutAvatar ( + bool hasAvatar, + const Archive::AvatarInfo& info, + QMap& output, + const QString& resource, + const QString& subject +) const { + if (hasAvatar) { + if (info.autogenerated) + output.insert("avatarState", QVariant::fromValue(Shared::Avatar::autocreated)); + else + output.insert("avatarState", QVariant::fromValue(Shared::Avatar::valid)); + + output.insert("avatarPath", avatarPath(resource) + "." + info.type); + } else { + output.insert("avatarState", QVariant::fromValue(Shared::Avatar::empty)); + output.insert("avatarPath", ""); + if (subject.size() == 0) + emit requestVCard(jid); + else + emit requestVCard(subject); + } +} diff --git a/core/rosteritem.h b/core/rosteritem.h index 7c82945..fa154c0 100644 --- a/core/rosteritem.h +++ b/core/rosteritem.h @@ -62,6 +62,8 @@ public: void setName(const QString& n); QString getServer() const; bool isMuc() const; + bool isEncryptionEnabled() const; + void enableEncryption(bool value = true); void addMessageToArchive(const Shared::Message& msg); void correctMessageInArchive(const QString& originalId, const Shared::Message& msg); @@ -78,16 +80,18 @@ public: bool changeMessage(const QString& id, const QMap& data); void clearArchiveRequests(); void downgradeDatabaseState(); + virtual QMap getInfo() const; Shared::Message getMessage(const QString& id); signals: - void nameChanged(const QString& name); - void subscriptionStateChanged(Shared::SubscriptionState state); - void historyResponse(const std::list& messages, bool last); - void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime()); - void avatarChanged(Shared::Avatar, const QString& path); - void requestVCard(const QString& jid); + void nameChanged(const QString& name) const; + void subscriptionStateChanged(Shared::SubscriptionState state) const; + void historyResponse(const std::list& messages, bool last) const; + void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime()) const; + void avatarChanged(Shared::Avatar, const QString& path) const; + void requestVCard(const QString& jid) const; + void encryptionChanged(bool value) const; public: const QString jid; @@ -96,6 +100,13 @@ public: protected: virtual bool setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource = ""); virtual bool setAutoGeneratedAvatar(Archive::AvatarInfo& info, const QString& resource = ""); + void careAboutAvatar( + bool hasAvatar, + const Archive::AvatarInfo& info, + QMap& output, + const QString& resource = "", + const QString& subject = "" + ) const; protected: QString name; diff --git a/core/storage/archive.cpp b/core/storage/archive.cpp index cb65a53..8330cff 100644 --- a/core/storage/archive.cpp +++ b/core/storage/archive.cpp @@ -29,6 +29,7 @@ Core::Archive::Archive(const QString& p_jid, QObject* parent): jid(p_jid), opened(false), fromTheBeginning(false), + encryptionEnabled(false), environment(), main(), order(), @@ -84,6 +85,12 @@ void Core::Archive::open(const QString& account) } catch (const NotFound& e) { fromTheBeginning = false; } + + try { + encryptionEnabled = getStatBoolValue("encryptionEnabled", txn); + } catch (const NotFound& e) { + encryptionEnabled = false; + } std::string sJid = jid.toStdString(); AvatarInfo info; @@ -603,7 +610,7 @@ std::list Core::Archive::getBefore(int count, const QString& id return res; } -bool Core::Archive::isFromTheBeginning() +bool Core::Archive::isFromTheBeginning() const { if (!opened) { throw Closed("isFromTheBeginning", jid.toStdString()); @@ -630,6 +637,35 @@ void Core::Archive::setFromTheBeginning(bool is) } } +bool Core::Archive::isEncryptionEnabled() const +{ + if (!opened) { + throw Closed("isEncryptionEnabled", jid.toStdString()); + } + return encryptionEnabled; +} + +bool Core::Archive::setEncryptionEnabled(bool is) +{ + if (!opened) { + throw Closed("setEncryptionEnabled", jid.toStdString()); + } + if (encryptionEnabled != is) { + encryptionEnabled = is; + + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + bool success = setStatValue("encryptionEnabled", is, txn); + if (success) { + mdb_txn_commit(txn); + return true; + } else { + mdb_txn_abort(txn); + } + } + return false; +} + QString Core::Archive::idByStanzaId(const QString& stanzaId) const { if (!opened) { diff --git a/core/storage/archive.h b/core/storage/archive.h index 47c62dc..ef10555 100644 --- a/core/storage/archive.h +++ b/core/storage/archive.h @@ -55,8 +55,10 @@ public: void clear(); long unsigned int size() const; std::list getBefore(int count, const QString& id); - bool isFromTheBeginning(); + bool isFromTheBeginning() const; void setFromTheBeginning(bool is); + bool isEncryptionEnabled() const; + bool setEncryptionEnabled(bool is); //returns true if changed, false otherwise bool setAvatar(const QByteArray& data, AvatarInfo& info, bool generated = false, const QString& resource = ""); AvatarInfo getAvatarInfo(const QString& resource = "") const; bool readAvatarInfo(AvatarInfo& target, const QString& resource = "") const; @@ -171,6 +173,7 @@ public: private: bool opened; bool fromTheBeginning; + bool encryptionEnabled; MDB_env* environment; MDB_dbi main; //id to message MDB_dbi order; //time to id diff --git a/translations/CMakeLists.txt b/translations/CMakeLists.txt index f70fe2b..6bdb6c9 100644 --- a/translations/CMakeLists.txt +++ b/translations/CMakeLists.txt @@ -1,11 +1,11 @@ -find_package(Qt5LinguistTools) +find_package(Qt${QT_VERSION_MAJOR}LinguistTools) set(TS_FILES squawk.en.ts squawk.ru.ts squawk.pt_BR.ts ) -qt5_add_translation(QM_FILES ${TS_FILES}) +qt_add_translation(QM_FILES ${TS_FILES}) add_custom_target(translations ALL DEPENDS ${QM_FILES}) install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk/l10n) From 4f295fee3ca6695178b8f8a1095a48c2bbfaae16 Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 17 Mar 2023 23:59:51 +0300 Subject: [PATCH 103/137] trust summary gui delivery --- core/account.cpp | 32 ++++++---- core/account.h | 6 +- core/handlers/omemohandler.cpp | 6 +- core/handlers/omemohandler.h | 3 +- core/handlers/rosterhandler.cpp | 3 + core/handlers/trusthandler.cpp | 50 ++++++++++----- core/handlers/trusthandler.h | 9 ++- shared/global.cpp | 50 +++++---------- shared/global.h | 4 +- shared/trustsummary.cpp | 11 ++++ shared/trustsummary.h | 10 +++ ui/models/contact.cpp | 109 +++++++++++++++----------------- ui/models/contact.h | 19 ++++-- 13 files changed, 169 insertions(+), 143 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index 3e713c4..736d5ae 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -21,9 +21,15 @@ #include -using namespace Core; - -Account::Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, bool p_active, NetworkAccess* p_net, QObject* parent): +Core::Account::Account( + const QString& p_login, + const QString& p_server, + const QString& p_password, + const QString& p_name, + bool p_active, + NetworkAccess* p_net, + QObject* parent +): QObject(parent), name(p_name), archiveQueries(), @@ -36,13 +42,15 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& rh(new RosterHandler(this)), vh(new VCardHandler(this)), dh(new DiscoveryHandler(this)), -#ifdef WITH_OMEMO +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) th(new TrustHandler(this)), +#endif +#ifdef WITH_OMEMO oh(new OmemoHandler(this)), - tm(new QXmppTrustManager(th)), om(new QXmppOmemoManager(oh)), #endif #if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) + tm(new QXmppTrustManager(th)), cm(new QXmppCarbonManagerV2()), psm(new QXmppPubSubManager()), #else @@ -157,7 +165,7 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& } } -Account::~Account() { +Core::Account::~Account() { if (reconnectScheduled) { reconnectScheduled = false; reconnectTimer->stop(); @@ -701,9 +709,7 @@ void Core::Account::setActive(bool p_active) { if (active != p_active) { active = p_active; - emit changed({ - {"active", active} - }); + emit changed({{"active", active}}); } } @@ -795,14 +801,12 @@ void Core::Account::addContactToGroupRequest(const QString& jid, const QString& void Core::Account::removeContactFromGroupRequest(const QString& jid, const QString& groupName) { rh->removeContactFromGroupRequest(jid, groupName);} -void Core::Account::renameContactRequest(const QString& jid, const QString& newName) -{ +void Core::Account::renameContactRequest(const QString& jid, const QString& newName) { Contact* cnt = rh->getContact(jid); - if (cnt == 0) { + if (cnt == 0) qDebug() << "An attempt to rename non existing contact" << jid << "of account" << name << ", skipping"; - } else { + else rm->renameItem(jid, newName); - } } void Core::Account::invalidatePassword() { diff --git a/core/account.h b/core/account.h index 26365c1..fe3988c 100644 --- a/core/account.h +++ b/core/account.h @@ -191,14 +191,16 @@ private: RosterHandler* rh; VCardHandler* vh; DiscoveryHandler* dh; -#ifdef WITH_OMEMO +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) TrustHandler* th; +#endif +#ifdef WITH_OMEMO OmemoHandler* oh; - QXmppTrustManager* tm; QXmppOmemoManager* om; #endif #if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) + QXmppTrustManager* tm; QXmppCarbonManagerV2* cm; QXmppPubSubManager* psm; #else diff --git a/core/handlers/omemohandler.cpp b/core/handlers/omemohandler.cpp index 4bccc4b..c8bcb17 100644 --- a/core/handlers/omemohandler.cpp +++ b/core/handlers/omemohandler.cpp @@ -19,8 +19,6 @@ #include "core/account.h" #include "core/adapterfunctions.h" -constexpr const char* ns_omemo_2 = "urn:xmpp:omemo:2"; - Core::OmemoHandler::OmemoHandler(Account* account) : QObject(), QXmppOmemoStorage(), @@ -182,7 +180,7 @@ void Core::OmemoHandler::requestOwnBundles() { void Core::OmemoHandler::onBundlesReceived(const QString& jid) { std::list keys; acc->oh->getDevices(jid, keys); - std::map trustLevels = acc->th->getKeys(ns_omemo_2, jid); + std::map trustLevels = acc->th->getKeys(Shared::EncryptionProtocol::omemo2, jid); qDebug() << "OMEMO info for " << jid << " devices:" << keys.size() << ", trustLevels:" << trustLevels.size(); for (Shared::KeyInfo& key : keys) { @@ -200,7 +198,7 @@ void Core::OmemoHandler::onOwnBundlesReceived() { QString jid = acc->getBareJid(); std::list keys; acc->oh->getDevices(jid, keys); - std::map trustLevels = acc->th->getKeys(ns_omemo_2, jid); + std::map trustLevels = acc->th->getKeys(Shared::EncryptionProtocol::omemo2, jid); qDebug() << "OMEMO info for " << jid << " devices:" << keys.size() << ", trustLevels:" << trustLevels.size(); for (Shared::KeyInfo& key : keys) { diff --git a/core/handlers/omemohandler.h b/core/handlers/omemohandler.h index b0db613..7053450 100644 --- a/core/handlers/omemohandler.h +++ b/core/handlers/omemohandler.h @@ -33,8 +33,7 @@ Q_DECLARE_METATYPE(QXmppOmemoStorage::Device); namespace Core { class Account; -class OmemoHandler :public QObject, public QXmppOmemoStorage -{ +class OmemoHandler : public QObject, public QXmppOmemoStorage { Q_OBJECT public: typedef std::pair SignedPreKeyPair; diff --git a/core/handlers/rosterhandler.cpp b/core/handlers/rosterhandler.cpp index 3738d2c..ceaab2d 100644 --- a/core/handlers/rosterhandler.cpp +++ b/core/handlers/rosterhandler.cpp @@ -96,6 +96,9 @@ void Core::RosterHandler::addedAccount(const QString& jid) { if (newContact) { QMap cData = contact->getInfo(); +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) + cData.insert("trust", QVariant::fromValue(acc->th->getSummary(jid))); +#endif int grCount = 0; for (QSet::const_iterator itr = gr.begin(), end = gr.end(); itr != end; ++itr) { const QString& groupName = *itr; diff --git a/core/handlers/trusthandler.cpp b/core/handlers/trusthandler.cpp index cc97e57..35e1bc6 100644 --- a/core/handlers/trusthandler.cpp +++ b/core/handlers/trusthandler.cpp @@ -18,9 +18,9 @@ #include "core/account.h" #include "core/adapterfunctions.h" -using namespace Core; - Core::TrustHandler::TrustHandler(Account* account): + QObject(), + QXmppTrustStorage(), acc(account), db(acc->getName() + "/trust"), protocols(db.createDirectory() + "/protocols"), @@ -161,7 +161,7 @@ QXmppTask>> Core::TrustHandler::s return Core::makeReadyTask(std::move(modifiedKeys)); } -QXmppTask TrustHandler::hasKey(const QString& encryption, +QXmppTask Core::TrustHandler::hasKey(const QString& encryption, const QString& keyOwnerJid, QXmpp::TrustLevels trustLevels) { @@ -179,7 +179,7 @@ QXmppTask TrustHandler::hasKey(const QString& encryption, return Core::makeReadyTask(std::move(found)); } -QXmppTask>> TrustHandler::keys( +QXmppTask>> Core::TrustHandler::keys( const QString& encryption, const QList& keyOwnerJids, QXmpp::TrustLevels trustLevels) @@ -202,7 +202,7 @@ QXmppTask>> TrustHandler::ke return Core::makeReadyTask(std::move(res)); } -QXmppTask>> TrustHandler::keys( +QXmppTask>> Core::TrustHandler::keys( const QString& encryption, QXmpp::TrustLevels trustLevels) { @@ -219,17 +219,17 @@ QXmppTask>> TrustHandle return Core::makeReadyTask(std::move(res)); } -QXmppTask TrustHandler::removeKeys(const QString& encryption) { +QXmppTask Core::TrustHandler::removeKeys(const QString& encryption) { getCache(encryption)->drop(); return Core::makeReadyTask(); } -QXmppTask TrustHandler::removeKeys(const QString& encryption, const QString& keyOwnerJid) { +QXmppTask Core::TrustHandler::removeKeys(const QString& encryption, const QString& keyOwnerJid) { getCache(encryption)->removeRecord(keyOwnerJid); return Core::makeReadyTask(); } -QXmppTask TrustHandler::removeKeys(const QString& encryption, const QList& keyIds) { +QXmppTask Core::TrustHandler::removeKeys(const QString& encryption, const QList& keyIds) { std::set set; for (const QByteArray& keyId : keyIds) set.insert(keyId); @@ -258,7 +258,7 @@ QXmppTask TrustHandler::removeKeys(const QString& encryption, const QList< return Core::makeReadyTask(); } -QXmppTask TrustHandler::addKeys( +QXmppTask Core::TrustHandler::addKeys( const QString& encryption, const QString& keyOwnerJid, const QList& keyIds, @@ -287,7 +287,7 @@ QXmppTask TrustHandler::addKeys( return Core::makeReadyTask(); } -QXmppTask TrustHandler::ownKey(const QString& encryption) { +QXmppTask Core::TrustHandler::ownKey(const QString& encryption) { QByteArray res; try { res = ownKeys->getRecord(encryption); @@ -295,7 +295,7 @@ QXmppTask TrustHandler::ownKey(const QString& encryption) { return Core::makeReadyTask(std::move(res)); } -QXmppTask TrustHandler::resetOwnKey(const QString& encryption) { +QXmppTask Core::TrustHandler::resetOwnKey(const QString& encryption) { try { ownKeys->removeRecord(encryption); } catch (const DataBase::NotFound& e) {} @@ -303,12 +303,12 @@ QXmppTask TrustHandler::resetOwnKey(const QString& encryption) { return Core::makeReadyTask(); } -QXmppTask TrustHandler::setOwnKey(const QString& encryption, const QByteArray& keyId) { +QXmppTask Core::TrustHandler::setOwnKey(const QString& encryption, const QByteArray& keyId) { ownKeys->forceRecord(encryption, keyId); return Core::makeReadyTask(); } -QXmppTask TrustHandler::securityPolicy(const QString& encryption) { +QXmppTask Core::TrustHandler::securityPolicy(const QString& encryption) { QXmpp::TrustSecurityPolicy res; try { res = static_cast(securityPolicies->getRecord(encryption)); @@ -316,14 +316,14 @@ QXmppTask TrustHandler::securityPolicy(const QString return Core::makeReadyTask(std::move(res)); } -QXmppTask TrustHandler::resetSecurityPolicy(const QString& encryption) { +QXmppTask Core::TrustHandler::resetSecurityPolicy(const QString& encryption) { try { securityPolicies->removeRecord(encryption); } catch (const DataBase::NotFound& e) {} return Core::makeReadyTask(); } -QXmppTask TrustHandler::setSecurityPolicy( +QXmppTask Core::TrustHandler::setSecurityPolicy( const QString& encryption, QXmpp::TrustSecurityPolicy securityPolicy) { @@ -333,8 +333,9 @@ QXmppTask TrustHandler::setSecurityPolicy( return Core::makeReadyTask(); } -Core::TrustHandler::Keys Core::TrustHandler::getKeys(const QString& protocol, const QString& jid) const { - std::map::const_iterator itr = keysByProtocol.find(protocol); +Core::TrustHandler::Keys Core::TrustHandler::getKeys(Shared::EncryptionProtocol protocol, const QString& jid) const { + const QString& prt = Shared::TrustSummary::protocolKeys.at(protocol); + std::map::const_iterator itr = keysByProtocol.find(prt); if (itr != keysByProtocol.end()) { try { Keys map = itr->second->getRecord(jid); @@ -347,6 +348,21 @@ Core::TrustHandler::Keys Core::TrustHandler::getKeys(const QString& protocol, co } } +Shared::TrustSummary Core::TrustHandler::getSummary(const QString& jid) const { + Shared::TrustSummary result; + for (const std::pair& pair : keysByProtocol) { + try { + Keys keys = pair.second->getRecord(jid); + Shared::EncryptionProtocol protocol = Shared::TrustSummary::protocolValues.at(pair.first); + for (const std::pair& trust : keys) { + result.increment(protocol, trust.second); + } + } catch (const DataBase::NotFound& e) {} + } + + return result; +} + Shared::TrustLevel Core::TrustHandler::convert(Core::TrustHandler::TL level) { switch (level) { case QXmpp::TrustLevel::Undecided: return Shared::TrustLevel::undecided; diff --git a/core/handlers/trusthandler.h b/core/handlers/trusthandler.h index 677a4f7..d21e0c3 100644 --- a/core/handlers/trusthandler.h +++ b/core/handlers/trusthandler.h @@ -18,6 +18,7 @@ #define CORE_TRUSTHANDLER_H #include +#include #include #include @@ -25,7 +26,8 @@ namespace Core { class Account; -class TrustHandler : public QXmppTrustStorage { +class TrustHandler : public QObject, public QXmppTrustStorage { + Q_OBJECT public: TrustHandler(Account* account); ~TrustHandler(); @@ -57,12 +59,13 @@ public: virtual QXmppTask setOwnKey(CSR encryption, const QByteArray& keyId) override; virtual QXmppTask securityPolicy(CSR encryption) override; virtual QXmppTask resetSecurityPolicy(CSR encryption) override; - virtual QXmppTask setSecurityPolicy(CSR encryption, QXmpp::TrustSecurityPolicy securityPolicy) override; + virtual QXmppTask setSecurityPolicy(const QString& encryption, QXmpp::TrustSecurityPolicy securityPolicy) override; static TL convert(Shared::TrustLevel level); static Shared::TrustLevel convert(TL level); - Keys getKeys(const QString& protocol, const QString& jid) const; + Keys getKeys(Shared::EncryptionProtocol protocol, const QString& jid) const; + Shared::TrustSummary getSummary(const QString& jid) const; private: KeyCache* createNewCache(const QString& encryption); diff --git a/shared/global.cpp b/shared/global.cpp index 7a1b494..1b5763b 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -226,61 +226,50 @@ Shared::Global::FileInfo Shared::Global::getFileInfo(const QString& path) } -Shared::Global * Shared::Global::getInstance() -{ +Shared::Global * Shared::Global::getInstance() { return instance; } -QString Shared::Global::getName(Message::State rl) -{ +QString Shared::Global::getName(Message::State rl) { return instance->messageState[static_cast(rl)]; } -QString Shared::Global::getName(Shared::Affiliation af) -{ +QString Shared::Global::getName(Shared::Affiliation af) { return instance->affiliation[static_cast(af)]; } -QString Shared::Global::getName(Shared::Availability av) -{ +QString Shared::Global::getName(Shared::Availability av) { return instance->availability[static_cast(av)]; } -QString Shared::Global::getName(Shared::ConnectionState cs) -{ +QString Shared::Global::getName(Shared::ConnectionState cs) { return instance->connectionState[static_cast(cs)]; } -QString Shared::Global::getName(Shared::Role rl) -{ +QString Shared::Global::getName(Shared::Role rl) { return instance->role[static_cast(rl)]; } -QString Shared::Global::getName(Shared::SubscriptionState ss) -{ +QString Shared::Global::getName(Shared::SubscriptionState ss) { return instance->subscriptionState[static_cast(ss)]; } -QString Shared::Global::getName(Shared::AccountPassword ap) -{ +QString Shared::Global::getName(Shared::AccountPassword ap) { return instance->accountPassword[static_cast(ap)]; } -QString Shared::Global::getName(Shared::TrustLevel tl) -{ +QString Shared::Global::getName(Shared::TrustLevel tl) { return instance->trustLevel[static_cast(tl)]; } -void Shared::Global::setSupported(const QString& pluginName, bool support) -{ +void Shared::Global::setSupported(const QString& pluginName, bool support) { std::map::iterator itr = instance->pluginSupport.find(pluginName); if (itr != instance->pluginSupport.end()) { itr->second = support; } } -bool Shared::Global::supported(const QString& pluginName) -{ +bool Shared::Global::supported(const QString& pluginName) { std::map::iterator itr = instance->pluginSupport.find(pluginName); if (itr != instance->pluginSupport.end()) { return itr->second; @@ -288,8 +277,7 @@ bool Shared::Global::supported(const QString& pluginName) return false; } -QString Shared::Global::getDescription(Shared::AccountPassword ap) -{ +QString Shared::Global::getDescription(Shared::AccountPassword ap) { return instance->accountPasswordDescription[static_cast(ap)]; } @@ -355,8 +343,7 @@ void Shared::Global::highlightInFileManager(const QString& path) } } -QIcon Shared::Global::createThemePreview(const QString& path) -{ +QIcon Shared::Global::createThemePreview(const QString& path) { if (supported("colorSchemeTools")) { QIcon* icon = createPreview(path); QIcon localIcon = *icon; @@ -367,8 +354,7 @@ QIcon Shared::Global::createThemePreview(const QString& path) } } -QString Shared::Global::getColorSchemeName(const QString& path) -{ +QString Shared::Global::getColorSchemeName(const QString& path) { if (supported("colorSchemeTools")) { QString res; colorSchemeName(path, res); @@ -378,8 +364,7 @@ QString Shared::Global::getColorSchemeName(const QString& path) } } -void Shared::Global::setTheme(const QString& path) -{ +void Shared::Global::setTheme(const QString& path) { if (supported("colorSchemeTools")) { if (path.toLower() == "system") { QApplication::setPalette(getInstance()->defaultSystemPalette); @@ -391,8 +376,7 @@ void Shared::Global::setTheme(const QString& path) } } -void Shared::Global::setStyle(const QString& style) -{ +void Shared::Global::setStyle(const QString& style) { if (style.toLower() == "system") { QApplication::setStyle(getInstance()->defaultSystemStyle); } else { @@ -404,7 +388,7 @@ void Shared::Global::setStyle(const QString& style) template<> \ Enum Shared::Global::fromInt(int src) \ { \ - if (src < static_cast(Enum##Lowest) && src > static_cast(Enum##Highest)) { \ + if (src < static_cast(Enum##Lowest) || src > static_cast(Enum##Highest)) { \ throw EnumOutOfRange(#Enum); \ } \ return static_cast(src); \ diff --git a/shared/global.h b/shared/global.h index b311f9f..578fc42 100644 --- a/shared/global.h +++ b/shared/global.h @@ -121,9 +121,7 @@ namespace Shared { template static T fromInt(unsigned int src); - class EnumOutOfRange: - public Utils::Exception - { + class EnumOutOfRange: public Utils::Exception { public: EnumOutOfRange(const std::string& p_name):Exception(), name(p_name) {} diff --git a/shared/trustsummary.cpp b/shared/trustsummary.cpp index 658538c..33a3873 100644 --- a/shared/trustsummary.cpp +++ b/shared/trustsummary.cpp @@ -27,6 +27,17 @@ const std::set Shared::TrustSummary::untrustedLevels({ Shared::TrustLevel::manuallyDistrusted }); +const std::map Shared::TrustSummary::protocolKeys({ + {Shared::EncryptionProtocol::omemo, "eu.siacs.conversations.axolotl"}, + {Shared::EncryptionProtocol::omemo1, "urn:xmpp:omemo:1"}, + {Shared::EncryptionProtocol::omemo2, "urn:xmpp:omemo:2"} +}); +const std::map Shared::TrustSummary::protocolValues({ + {"eu.siacs.conversations.axolotl", Shared::EncryptionProtocol::omemo}, + {"urn:xmpp:omemo:1", Shared::EncryptionProtocol::omemo1}, + {"urn:xmpp:omemo:2", Shared::EncryptionProtocol::omemo2} +}); + Shared::TrustSummary::TrustSummary(): data() {} diff --git a/shared/trustsummary.h b/shared/trustsummary.h index 3283c10..7c79978 100644 --- a/shared/trustsummary.h +++ b/shared/trustsummary.h @@ -17,6 +17,8 @@ #ifndef SHARED_TRUSTSUMMARY_H #define SHARED_TRUSTSUMMARY_H +#include + #include #include @@ -42,10 +44,18 @@ private: typedef std::map Data; Data data; + +public: + static const std::map protocolKeys; + static const std::map protocolValues; + +private: static const std::set trustedLevels; static const std::set untrustedLevels; }; } +Q_DECLARE_METATYPE(Shared::TrustSummary) + #endif // SHARED_TRUSTSUMMARY_H diff --git a/ui/models/contact.cpp b/ui/models/contact.cpp index d5c7dc4..b963421 100644 --- a/ui/models/contact.cpp +++ b/ui/models/contact.cpp @@ -24,62 +24,56 @@ Models::Contact::Contact(const Account* acc, const QString& p_jid ,const QMap::const_iterator itr = data.find("state"); - if (itr != data.end()) { + if (itr != data.end()) setState(itr.value().toUInt()); - } + + itr = data.find("trust"); + if (itr != data.end()) + setTrust(itr.value().value()); } -Models::Contact::~Contact() -{ -} +Models::Contact::~Contact() {} -void Models::Contact::setAvailability(unsigned int p_state) -{ +void Models::Contact::setAvailability(unsigned int p_state) { setAvailability(Shared::Global::fromInt(p_state)); } -void Models::Contact::setState(unsigned int p_state) -{ +void Models::Contact::setState(unsigned int p_state) { setState(Shared::Global::fromInt(p_state)); } -Shared::Availability Models::Contact::getAvailability() const -{ +Shared::Availability Models::Contact::getAvailability() const { return availability; } -void Models::Contact::setAvailability(Shared::Availability p_state) -{ +void Models::Contact::setAvailability(Shared::Availability p_state) { if (availability != p_state) { availability = p_state; changed(3); } } -QString Models::Contact::getStatus() const -{ +QString Models::Contact::getStatus() const { return status; } -void Models::Contact::setStatus(const QString& p_state) -{ +void Models::Contact::setStatus(const QString& p_state) { if (status != p_state) { status = p_state; changed(5); } } -int Models::Contact::columnCount() const -{ - return 8; +int Models::Contact::columnCount() const { + return 9; } -QVariant Models::Contact::data(int column) const -{ +QVariant Models::Contact::data(int column) const { switch (column) { case 0: return getContactName(); @@ -97,35 +91,35 @@ QVariant Models::Contact::data(int column) const return QVariant::fromValue(getAvatarState()); case 7: return getAvatarPath(); + case 8: + return QVariant::fromValue(getTrust()); default: return QVariant(); } } -QString Models::Contact::getContactName() const -{ - if (name == "") { +QString Models::Contact::getContactName() const { + if (name == "") return jid; - } else { + else return name; - } } -void Models::Contact::update(const QString& field, const QVariant& value) -{ +void Models::Contact::update(const QString& field, const QVariant& value) { if (field == "name") { setName(value.toString()); } else if (field == "availability") { setAvailability(value.toUInt()); } else if (field == "state") { setState(value.toUInt()); + } else if (field == "trust") { + setTrust(value.value()); } else { Element::update(field, value); } } -void Models::Contact::addPresence(const QString& p_name, const QMap& data) -{ +void Models::Contact::addPresence(const QString& p_name, const QMap& data) { QMap::iterator itr = presences.find(p_name); if (itr == presences.end()) { @@ -135,14 +129,12 @@ void Models::Contact::addPresence(const QString& p_name, const QMap::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) { + for (QMap::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) pr->update(itr.key(), itr.value()); - } } } -void Models::Contact::removePresence(const QString& name) -{ +void Models::Contact::removePresence(const QString& name) { QMap::iterator itr = presences.find(name); if (itr == presences.end()) { @@ -155,18 +147,15 @@ void Models::Contact::removePresence(const QString& name) } } -Models::Presence * Models::Contact::getPresence(const QString& name) -{ +Models::Presence * Models::Contact::getPresence(const QString& name) { QMap::iterator itr = presences.find(name); - if (itr == presences.end()) { + if (itr == presences.end()) return nullptr; - } else { + else return itr.value(); - } } -void Models::Contact::refresh() -{ +void Models::Contact::refresh() { QDateTime lastActivity; Presence* presence = 0; for (QMap::iterator itr = presences.begin(), end = presences.end(); itr != end; ++itr) { @@ -188,36 +177,43 @@ void Models::Contact::refresh() } } -void Models::Contact::_removeChild(int index) -{ +void Models::Contact::_removeChild(int index) { Item* child = childItems[index]; disconnect(child, &Item::childChanged, this, &Contact::refresh); Item::_removeChild(index); refresh(); } -void Models::Contact::_appendChild(Models::Item* child) -{ +void Models::Contact::_appendChild(Models::Item* child) { Item::_appendChild(child); connect(child, &Item::childChanged, this, &Contact::refresh); refresh(); } -Shared::SubscriptionState Models::Contact::getState() const -{ +Shared::SubscriptionState Models::Contact::getState() const { return state; } -void Models::Contact::setState(Shared::SubscriptionState p_state) -{ +void Models::Contact::setState(Shared::SubscriptionState p_state) { if (state != p_state) { state = p_state; changed(2); } } -QIcon Models::Contact::getStatusIcon(bool big) const -{ +Shared::TrustSummary Models::Contact::getTrust() const { + return trust; +} + +void Models::Contact::setTrust(const Shared::TrustSummary& p_trust) { + //if (trust != p_trust) { + trust = p_trust; + changed(8); + //} +} + + +QIcon Models::Contact::getStatusIcon(bool big) const { if (getMessagesCount() > 0) { return Shared::icon("mail-message", big); } else if (state == Shared::SubscriptionState::both || state == Shared::SubscriptionState::to) { @@ -227,8 +223,7 @@ QIcon Models::Contact::getStatusIcon(bool big) const } } -void Models::Contact::toOfflineState() -{ +void Models::Contact::toOfflineState() { std::deque::size_type size = childItems.size(); if (size > 0) { emit childIsAboutToBeRemoved(this, 0, size - 1); @@ -245,13 +240,11 @@ void Models::Contact::toOfflineState() } } -QString Models::Contact::getDisplayedName() const -{ +QString Models::Contact::getDisplayedName() const { return getContactName(); } -void Models::Contact::handleRecconnect() -{ +void Models::Contact::handleRecconnect() { if (getMessagesCount() > 0) { feed->requestLatestMessages(); } diff --git a/ui/models/contact.h b/ui/models/contact.h index c4fc131..36802cb 100644 --- a/ui/models/contact.h +++ b/ui/models/contact.h @@ -19,17 +19,19 @@ #ifndef MODELS_CONTACT_H #define MODELS_CONTACT_H -#include "element.h" -#include "presence.h" -#include "shared/enums.h" -#include "shared/message.h" -#include "shared/icons.h" -#include "shared/global.h" - #include #include + #include +#include "element.h" +#include "presence.h" +#include +#include +#include +#include +#include + namespace Models { class Contact : public Element @@ -56,6 +58,7 @@ public: QString getContactName() const; QString getStatus() const; QString getDisplayedName() const override; + Shared::TrustSummary getTrust() const; void handleRecconnect(); //this is a special method Models::Roster calls when reconnect happens @@ -73,10 +76,12 @@ protected: void setState(Shared::SubscriptionState p_state); void setState(unsigned int p_state); void setStatus(const QString& p_state); + void setTrust(const Shared::TrustSummary& p_trust); private: Shared::Availability availability; Shared::SubscriptionState state; + Shared::TrustSummary trust; QMap presences; QString status; }; From 69d797fe51d57a373ee56f67124fe0a239790716 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 18 Mar 2023 02:50:04 +0300 Subject: [PATCH 104/137] showing the button for encryption if there is at least one omemo key, trust summary update calculations --- core/handlers/rosterhandler.cpp | 30 +++++++----- core/handlers/rosterhandler.h | 3 ++ core/handlers/trusthandler.cpp | 86 ++++++++++++++++++++++++++++----- core/handlers/trusthandler.h | 4 ++ shared/trustsummary.cpp | 10 +++- shared/trustsummary.h | 3 ++ ui/models/contact.cpp | 12 +++-- ui/models/contact.h | 1 + ui/widgets/chat.cpp | 24 ++++----- 9 files changed, 134 insertions(+), 39 deletions(-) diff --git a/core/handlers/rosterhandler.cpp b/core/handlers/rosterhandler.cpp index ceaab2d..2e59971 100644 --- a/core/handlers/rosterhandler.cpp +++ b/core/handlers/rosterhandler.cpp @@ -39,6 +39,10 @@ void Core::RosterHandler::initialize() { connect(acc->bm, &QXmppBookmarkManager::bookmarksReceived, this, &RosterHandler::bookmarksReceived); connect(acc, &Account::pepSupportChanged, this, &RosterHandler::onPepSupportedChanged); + +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) + connect(acc->th, &TrustHandler::trustLevelsChanged, this, &RosterHandler::onTrustChanged); +#endif } Core::RosterHandler::~RosterHandler() { @@ -214,10 +218,13 @@ void Core::RosterHandler::onContactGroupAdded(const QString& group) { if (contact->groupsCount() == 1) { // not sure i need to handle it here, the situation with grouped and ungrouped contacts handled on the client anyway } - + QMap cData({ {"name", contact->getName()}, {"state", QVariant::fromValue(contact->getSubscriptionState())}, +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) + {"trust", QVariant::fromValue(acc->th->getSummary(contact->jid))}, +#endif {"encryption", contact->isEncryptionEnabled()} }); addToGroup(contact->jid, group); @@ -229,7 +236,7 @@ void Core::RosterHandler::onContactGroupRemoved(const QString& group) { if (contact->groupsCount() == 0) { // not sure i need to handle it here, the situation with grouped and ungrouped contacts handled on the client anyway } - + emit acc->removeContact(contact->jid, group); removeFromGroup(contact->jid, group); } @@ -246,12 +253,14 @@ void Core::RosterHandler::onContactEncryptionChanged(bool value) { void Core::RosterHandler::onContactSubscriptionStateChanged(Shared::SubscriptionState cstate) { Contact* contact = static_cast(sender()); - QMap cData({ - {"state", QVariant::fromValue(cstate)}, - }); - emit acc->changeContact(contact->jid, cData); + emit acc->changeContact(contact->jid, {{"state", QVariant::fromValue(cstate)}}); } +void Core::RosterHandler::onTrustChanged(const QString& jid, const Shared::TrustSummary& trust) { + emit acc->changeContact(jid, {{"trust", QVariant::fromValue(trust)}}); +} + + void Core::RosterHandler::addToGroup(const QString& jid, const QString& group) { std::map>::iterator gItr = groups.find(group); if (gItr == groups.end()) { @@ -296,18 +305,18 @@ Core::RosterItem * Core::RosterHandler::getRosterItem(const QString& jid) { Core::Conference * Core::RosterHandler::getConference(const QString& jid) { Conference* item = 0; std::map::const_iterator coitr = conferences.find(jid.toLower()); - if (coitr != conferences.end()) { + if (coitr != conferences.end()) item = coitr->second; - } + return item; } Core::Contact * Core::RosterHandler::getContact(const QString& jid) { Contact* item = 0; std::map::const_iterator citr = contacts.find(jid.toLower()); - if (citr != contacts.end()) { + if (citr != contacts.end()) item = citr->second; - } + return item; } @@ -527,7 +536,6 @@ void Core::RosterHandler::handleOffline() { } } - void Core::RosterHandler::onPepSupportedChanged(Shared::Support support) { if (support == Shared::Support::supported) { for (const std::pair& pair : contacts) { diff --git a/core/handlers/rosterhandler.h b/core/handlers/rosterhandler.h index 63f291c..61f3d7a 100644 --- a/core/handlers/rosterhandler.h +++ b/core/handlers/rosterhandler.h @@ -34,6 +34,8 @@ #include #include +#include + #include #include #include @@ -75,6 +77,7 @@ private slots: void onRosterItemAdded(const QString& bareJid); void onRosterItemChanged(const QString& bareJid); void onRosterItemRemoved(const QString& bareJid); + void onTrustChanged(const QString& jid, const Shared::TrustSummary& trust); void onMucRoomAdded(QXmppMucRoom* room); void onMucJoinedChanged(bool joined); diff --git a/core/handlers/trusthandler.cpp b/core/handlers/trusthandler.cpp index 35e1bc6..c866d6f 100644 --- a/core/handlers/trusthandler.cpp +++ b/core/handlers/trusthandler.cpp @@ -79,7 +79,25 @@ Core::TrustHandler::KeyCache * Core::TrustHandler::createNewCache(const QString& QXmppTask Core::TrustHandler::resetAll(const QString& encryption) { securityPolicies->removeRecord(encryption); ownKeys->removeRecord(encryption); - getCache(encryption)->drop(); + std::map::const_iterator itr = keysByProtocol.find(encryption); + if (itr == keysByProtocol.end()) + return Core::makeReadyTask(); + + KeyCache* cache = itr->second; + std::map keys = cache->readAll(); + cache->drop(); + + for (const std::pair& pair : keys) { + bool empty = true; + for (const std::pair& trust : pair.second) { + if (trust.second != Shared::TrustLevel::undecided) { + empty = false; + break; + } + } + if (!empty) + emit trustLevelsChanged(pair.first, getSummary(pair.first)); + } return Core::makeReadyTask(); } @@ -109,6 +127,7 @@ QXmppTask>> Core::TrustHandler::s QHash> modifiedKeys; Shared::TrustLevel oldLevel = convert(oldTrustLevel); Shared::TrustLevel newLevel = convert(newTrustLevel); + std::set modifiedJids; KeyCache* cache = getCache(encryption); for (const QString& keyOwnerJid : keyOwnerJids) { Keys map = cache->getRecord(keyOwnerJid); @@ -118,13 +137,16 @@ QXmppTask>> Core::TrustHandler::s if (current == oldLevel) { current = newLevel; modifiedKeys[encryption].insert(keyOwnerJid, pair.first); + modifiedJids.insert(keyOwnerJid); ++count; } } - if (count > 0) { + if (count > 0) cache->changeRecord(keyOwnerJid, map); - } } + for (const QString& jid : modifiedJids) + emit trustLevelsChanged(jid, getSummary(jid)); + return Core::makeReadyTask(std::move(modifiedKeys)); } @@ -135,6 +157,7 @@ QXmppTask>> Core::TrustHandler::s { QHash> modifiedKeys; Shared::TrustLevel level = convert(trustLevel); + std::set modifiedJids; KeyCache* cache = getCache(encryption); for (MultySB::const_iterator itr = keyIds.begin(), end = keyIds.end(); itr != end; ++itr) { @@ -150,14 +173,20 @@ QXmppTask>> Core::TrustHandler::s } if (changed) { modifiedKeys[encryption].insert(keyOwnerJid, keyId); + modifiedJids.insert(keyOwnerJid); cache->changeRecord(keyOwnerJid, map); } } catch (const DataBase::NotFound& e) { Keys map({{keyId, level}}); modifiedKeys[encryption].insert(keyOwnerJid, keyId); + modifiedJids.insert(keyOwnerJid); cache->addRecord(keyOwnerJid, map); } } + + for (const QString& jid : modifiedJids) + emit trustLevelsChanged(jid, getSummary(jid)); + return Core::makeReadyTask(std::move(modifiedKeys)); } @@ -220,12 +249,41 @@ QXmppTask>> Core::Trust } QXmppTask Core::TrustHandler::removeKeys(const QString& encryption) { - getCache(encryption)->drop(); + std::map::const_iterator itr = keysByProtocol.find(encryption); + if (itr == keysByProtocol.end()) + return Core::makeReadyTask(); + + KeyCache* cache = itr->second; + std::map keys = cache->readAll(); + cache->drop(); + + for (const std::pair& pair : keys) { + bool empty = true; + for (const std::pair& trust : pair.second) { + if (trust.second != Shared::TrustLevel::undecided) { + empty = false; + break; + } + } + if (!empty) + emit trustLevelsChanged(pair.first, getSummary(pair.first)); + } + return Core::makeReadyTask(); } QXmppTask Core::TrustHandler::removeKeys(const QString& encryption, const QString& keyOwnerJid) { - getCache(encryption)->removeRecord(keyOwnerJid); + std::map::const_iterator itr = keysByProtocol.find(encryption); + if (itr == keysByProtocol.end()) + return Core::makeReadyTask(); + + KeyCache* cache = itr->second; + try { + cache->removeRecord(keyOwnerJid); + emit trustLevelsChanged(keyOwnerJid, getSummary(keyOwnerJid)); //TODO there is a probability of notification without the actial change + } catch (const DataBase::NotFound& e) {} //if the movin entry was empty or if it consisted of Undecided keys + + return Core::makeReadyTask(); } @@ -236,14 +294,15 @@ QXmppTask Core::TrustHandler::removeKeys(const QString& encryption, const KeyCache* cache = getCache(encryption); std::map data = cache->readAll(); - bool changed = false; + std::set modifiedJids; + for (std::map::iterator cItr = data.begin(), cEnd = data.end(); cItr != cEnd; /*no increment*/) { Keys& byOwner = cItr->second; for (Keys::const_iterator itr = byOwner.begin(), end = byOwner.end(); itr != end; /*no increment*/) { const QByteArray& keyId = itr->first; if (set.erase(keyId)) { byOwner.erase(itr++); - changed = true; + modifiedJids.insert(cItr->first); } else ++itr; } @@ -252,8 +311,12 @@ QXmppTask Core::TrustHandler::removeKeys(const QString& encryption, const else ++cItr; } - if (changed) + if (modifiedJids.size() > 0) { cache->replaceAll(data); + } + + for (const QString& jid : modifiedJids) + emit trustLevelsChanged(jid, getSummary(jid)); return Core::makeReadyTask(); } @@ -278,11 +341,12 @@ QXmppTask Core::TrustHandler::addKeys( result.first->second = level; } - if (had) { + if (had) cache->changeRecord(keyOwnerJid, data); - } else { + else cache->addRecord(keyOwnerJid, data); - } + + emit trustLevelsChanged(keyOwnerJid, getSummary(keyOwnerJid)); return Core::makeReadyTask(); } diff --git a/core/handlers/trusthandler.h b/core/handlers/trusthandler.h index d21e0c3..98d7f1e 100644 --- a/core/handlers/trusthandler.h +++ b/core/handlers/trusthandler.h @@ -32,6 +32,10 @@ public: TrustHandler(Account* account); ~TrustHandler(); +signals: + void trustLevelsChanged(const QString& jid, const Shared::TrustSummary& summary) const; + +public: typedef QMultiHash MultySB; typedef QHash HashSM; typedef const QList& CLSR; diff --git a/shared/trustsummary.cpp b/shared/trustsummary.cpp index 33a3873..eae98bf 100644 --- a/shared/trustsummary.cpp +++ b/shared/trustsummary.cpp @@ -138,7 +138,7 @@ bool Shared::TrustSummary::hasTrustedKeys(Shared::EncryptionProtocol protocol) c } bool Shared::TrustSummary::hasUntrustedKeys(Shared::EncryptionProtocol protocol) const { - Data::const_iterator itr = data.find(protocol); + Data::const_iterator itr = data.find(protocol); if (itr == data.end()) return false; @@ -149,3 +149,11 @@ bool Shared::TrustSummary::hasUntrustedKeys(Shared::EncryptionProtocol protocol) return false; } + +bool Shared::TrustSummary::operator==(const Shared::TrustSummary& other) { + return data == other.data; +} + +bool Shared::TrustSummary::operator!=(const Shared::TrustSummary& other) { + return data != other.data; +} diff --git a/shared/trustsummary.h b/shared/trustsummary.h index 7c79978..e663a9d 100644 --- a/shared/trustsummary.h +++ b/shared/trustsummary.h @@ -30,6 +30,9 @@ class TrustSummary { public: TrustSummary(); + bool operator == (const TrustSummary& other); + bool operator != (const TrustSummary& other); + void set(EncryptionProtocol protocol, TrustLevel level, uint8_t amount); uint8_t increment(EncryptionProtocol protocol, TrustLevel level); uint8_t decrement(EncryptionProtocol protocol, TrustLevel level); diff --git a/ui/models/contact.cpp b/ui/models/contact.cpp index b963421..8d6def9 100644 --- a/ui/models/contact.cpp +++ b/ui/models/contact.cpp @@ -206,10 +206,10 @@ Shared::TrustSummary Models::Contact::getTrust() const { } void Models::Contact::setTrust(const Shared::TrustSummary& p_trust) { - //if (trust != p_trust) { + if (trust != p_trust) { trust = p_trust; changed(8); - //} + } } @@ -245,7 +245,11 @@ QString Models::Contact::getDisplayedName() const { } void Models::Contact::handleRecconnect() { - if (getMessagesCount() > 0) { + if (getMessagesCount() > 0) feed->requestLatestMessages(); - } } + +bool Models::Contact::hasKeys(Shared::EncryptionProtocol protocol) const { + return trust.hasKeys(protocol); +} + diff --git a/ui/models/contact.h b/ui/models/contact.h index 36802cb..e7d767d 100644 --- a/ui/models/contact.h +++ b/ui/models/contact.h @@ -59,6 +59,7 @@ public: QString getStatus() const; QString getDisplayedName() const override; Shared::TrustSummary getTrust() const; + bool hasKeys(Shared::EncryptionProtocol protocol) const; void handleRecconnect(); //this is a special method Models::Roster calls when reconnect happens diff --git a/ui/widgets/chat.cpp b/ui/widgets/chat.cpp index 052d83d..607fab4 100644 --- a/ui/widgets/chat.cpp +++ b/ui/widgets/chat.cpp @@ -17,6 +17,7 @@ */ #include "chat.h" +#include "ui_conversation.h" Chat::Chat(Models::Account* acc, Models::Contact* p_contact, QWidget* parent): Conversation(false, acc, p_contact, p_contact->getJid(), "", parent), @@ -28,14 +29,14 @@ Chat::Chat(Models::Account* acc, Models::Contact* p_contact, QWidget* parent): setAvatar(p_contact->getAvatarPath()); connect(contact, &Models::Contact::childChanged, this, &Chat::onContactChanged); + if (p_contact->hasKeys(Shared::EncryptionProtocol::omemo2)) + m_ui->encryptionButton->setVisible(true); } Chat::~Chat() -{ -} +{} -void Chat::onContactChanged(Models::Item* item, int row, int col) -{ +void Chat::onContactChanged(Models::Item* item, int row, int col) { if (item == contact) { switch (col) { case 0: @@ -50,19 +51,20 @@ void Chat::onContactChanged(Models::Item* item, int row, int col) case 7: setAvatar(contact->getAvatarPath()); break; + case 8: + m_ui->encryptionButton->setVisible(contact->hasKeys(Shared::EncryptionProtocol::omemo2)); + break; } } } -void Chat::updateState() -{ +void Chat::updateState() { Shared::Availability av = contact->getAvailability(); statusIcon->setPixmap(Shared::availabilityIcon(av, true).pixmap(40)); statusIcon->setToolTip(Shared::Global::getName(av)); } -Shared::Message Chat::createMessage() const -{ +Shared::Message Chat::createMessage() const { Shared::Message msg = Conversation::createMessage(); msg.setType(Shared::Message::chat); msg.setFrom(account->getFullJid()); @@ -71,14 +73,12 @@ Shared::Message Chat::createMessage() const return msg; } -void Chat::onMessage(const Shared::Message& data) -{ +void Chat::onMessage(const Shared::Message& data){ Conversation::onMessage(data); if (!data.getOutgoing()) { const QString& res = data.getPenPalResource(); - if (res.size() > 0) { + if (res.size() > 0) setPalResource(res); - } } } From 81cf0f8d342776e9bb53ea67a475add4f15684ca Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 27 Mar 2023 21:45:29 +0300 Subject: [PATCH 105/137] transition to LMDBAL --- .gitmodules | 3 +++ CMakeLists.txt | 24 ++++++++++++++++++------ README.md | 4 +++- core/components/clientcache.h | 11 ++++++++--- core/handlers/omemohandler.cpp | 8 ++++---- core/handlers/omemohandler.h | 10 +++++----- core/handlers/trusthandler.cpp | 28 ++++++++++++++-------------- core/handlers/trusthandler.h | 8 ++++---- external/lmdbal | 1 + external/storage | 1 - main/main.cpp | 1 + packaging/Archlinux/PKGBUILD | 6 +++--- shared/enums.h | 3 +++ shared/global.cpp | 1 + shared/trustsummary.cpp | 2 ++ ui/models/contact.cpp | 1 + ui/models/contact.h | 2 ++ ui/widgets/chat.cpp | 4 +++- 18 files changed, 76 insertions(+), 42 deletions(-) create mode 160000 external/lmdbal delete mode 160000 external/storage diff --git a/.gitmodules b/.gitmodules index 098973a..448dae5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "external/storage"] path = external/storage url = https://git.macaw.me/blue/storage +[submodule "external/lmdbal"] + path = external/lmdbal + url = gitea@git.macaw.me:blue/lmdbal.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 01a9bd1..6cdb712 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ add_executable(squawk ${WIN32_FLAG} ${MACOSX_BUNDLE_FLAG}) target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}) option(SYSTEM_QXMPP "Use system qxmpp lib" ON) +option(SYSTEM_LMDBAL "Use system lmdbal lib" ON) option(WITH_KWALLET "Build KWallet support module" ON) option(WITH_KIO "Build KIO support module" ON) option(WITH_KCONFIG "Build KConfig support module" ON) @@ -148,14 +149,25 @@ else () target_link_libraries(squawk PRIVATE QXmpp::QXmpp) endif () -## LMDB -#find_package(LMDB REQUIRED) +## LMDBAL +if (SYSTEM_LMDBAL) + find_package(lmdbal CONFIG) + if (NOT LMDBAL_FOUND) + set(SYSTEM_LMDBAL OFF) + message("LMDBAL package wasn't found, trying to build with bundled LMDBAL") + else () + message("Building with system LMDBAL") + endif () +endif() +if (NOT SYSTEM_LMDBAL) + message("Building with bundled LMDBAL") + set(BUILD_STATIC ON) + add_subdirectory(external/lmdbal) + add_library(LMDBAL::LMDBAL ALIAS LMDBAL) +endif() -#TODO conditioning! -add_subdirectory(external/storage) -target_include_directories(squawk PRIVATE external/storage) -target_link_libraries(squawk PRIVATE storage) +target_link_libraries(squawk PRIVATE LMDBAL::LMDBAL) # Linking target_link_libraries(squawk diff --git a/README.md b/README.md index 3e20568..121b640 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ ### Prerequisites - QT 5.12 *(lower versions might work but it wasn't tested)* -- lmdb - CMake 3.4 or higher - qxmpp 1.1.0 or higher +- LMDBAL (my own [library](https://git.macaw.me/blue/lmdbal) around lmdb) - KDE Frameworks: kwallet (optional) - KDE Frameworks: KIO (optional) - KDE Frameworks: KConfig (optional) @@ -92,9 +92,11 @@ You can always refer to `appveyor.yml` to see how AppVeyor build squawk. Here is the list of keys you can pass to configuration phase of `cmake ..`. - `CMAKE_BUILD_TYPE` - `Debug` just builds showing all warnings, `Release` builds with no warnings and applies optimizations (default is `Debug`) - `SYSTEM_QXMPP` - `True` tries to link against `qxmpp` installed in the system, `False` builds bundled `qxmpp` library (default is `True`) +- `SYSTEM_LMDBAL` - `True` tries to link against `LMDABL` installed in the system, `False` builds bundled `LMDBAL` library (default is `True`) - `WITH_KWALLET` - `True` builds the `KWallet` capability module if `KWallet` is installed and if not goes to `False`. `False` disables `KWallet` support (default is `True`) - `WITH_KIO` - `True` builds the `KIO` capability module if `KIO` is installed and if not goes to `False`. `False` disables `KIO` support (default is `True`) - `WITH_KCONFIG` - `True` builds the `KConfig` and `KConfigWidgets` capability module if such packages are installed and if not goes to `False`. `False` disables `KConfig` and `KConfigWidgets` support (default is `True`) +- `WITH_OMEMO` - `True` builds the OMEMO encryption, requires `qxmpp` of version >= 1.5.0 built with OMEMO support. `False` disables OMEMO support (default is `True`) ## License diff --git a/core/components/clientcache.h b/core/components/clientcache.h index 6202af5..b9dba73 100644 --- a/core/components/clientcache.h +++ b/core/components/clientcache.h @@ -45,11 +45,16 @@ signals: public slots: bool checkClient(const Shared::ClientId& id); - bool registerClientInfo(const QString& sourceFullJid, const QString& id, const std::set& identities, const std::set& features); + bool registerClientInfo( + const QString& sourceFullJid, + const QString& id, + const std::set& identities, + const std::set& features + ); private: - DataBase db; - DataBase::Cache* cache; + LMDBAL::Base db; + LMDBAL::Cache* cache; std::map requested; std::map specific; }; diff --git a/core/handlers/omemohandler.cpp b/core/handlers/omemohandler.cpp index c8bcb17..35bca9e 100644 --- a/core/handlers/omemohandler.cpp +++ b/core/handlers/omemohandler.cpp @@ -35,7 +35,7 @@ Core::OmemoHandler::OmemoHandler(Account* account) : QVariant own = meta->getRecord("ownDevice"); ownDevice = own.value(); qDebug() << "Successfully found own device omemo data for account" << acc->getName(); - } catch (const DataBase::NotFound& e) { + } catch (const LMDBAL::NotFound& e) { qDebug() << "No device omemo data was found for account" << acc->getName(); } } @@ -76,7 +76,7 @@ QXmppTask Core::OmemoHandler::addDevice(const QString& jid, uint32_t devic bool had = true; try { devs = devices->getRecord(jid); - } catch (const DataBase::NotFound& error) { + } catch (const LMDBAL::NotFound& error) { had = false; } @@ -127,7 +127,7 @@ QXmppTask Core::OmemoHandler::removePreKeyPair(uint32_t keyId) { QXmppTask Core::OmemoHandler::removeSignedPreKeyPair(uint32_t keyId) { try { signedPreKeyPairs->removeRecord(keyId); - } catch (const DataBase::NotFound& e) {} + } catch (const LMDBAL::NotFound& e) {} return Core::makeReadyTask(); } @@ -159,7 +159,7 @@ void Core::OmemoHandler::getDevices(const QString& jid, std::list devs; try { devs = devices->getRecord(jid); - } catch (const DataBase::NotFound& error) {} + } catch (const LMDBAL::NotFound& error) {} for (QHash::const_iterator itr = devs.begin(), end = devs.end(); itr != end; ++itr) { const Device& dev = itr.value(); diff --git a/core/handlers/omemohandler.h b/core/handlers/omemohandler.h index 7053450..1ea1f26 100644 --- a/core/handlers/omemohandler.h +++ b/core/handlers/omemohandler.h @@ -73,11 +73,11 @@ private slots: private: Account* acc; std::optional ownDevice; - DataBase db; - DataBase::Cache* meta; - DataBase::Cache>* devices; - DataBase::Cache* preKeyPairs; - DataBase::Cache* signedPreKeyPairs; + LMDBAL::Base db; + LMDBAL::Cache* meta; + LMDBAL::Cache>* devices; + LMDBAL::Cache* preKeyPairs; + LMDBAL::Cache* signedPreKeyPairs; }; } diff --git a/core/handlers/trusthandler.cpp b/core/handlers/trusthandler.cpp index c866d6f..21b3491 100644 --- a/core/handlers/trusthandler.cpp +++ b/core/handlers/trusthandler.cpp @@ -29,7 +29,7 @@ Core::TrustHandler::TrustHandler(Account* account): keysByProtocol() { if (!protocols.open(QIODevice::ReadWrite | QIODevice::Text)) { //never supposed to happen since I have just created a directory; - throw DataBase::Directory(protocols.fileName().toStdString()); + throw LMDBAL::Directory(protocols.fileName().toStdString()); } QTextStream in(&protocols); @@ -66,7 +66,7 @@ Core::TrustHandler::KeyCache * Core::TrustHandler::createNewCache(const QString& keysByProtocol.insert(std::make_pair(encryption, cache)); if(!protocols.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) { - throw DataBase::Directory(protocols.fileName().toStdString()); + throw LMDBAL::Directory(protocols.fileName().toStdString()); } QTextStream out(&protocols); out << encryption + "\n"; @@ -114,7 +114,7 @@ QXmppTask Core::TrustHandler::trustLevel( Keys::const_iterator itr = map.find(keyId); if (itr != map.end()) level = itr->second; - } catch (const DataBase::NotFound& e) {} + } catch (const LMDBAL::NotFound& e) {} return Core::makeReadyTask(std::move(convert(level))); } @@ -176,7 +176,7 @@ QXmppTask>> Core::TrustHandler::s modifiedJids.insert(keyOwnerJid); cache->changeRecord(keyOwnerJid, map); } - } catch (const DataBase::NotFound& e) { + } catch (const LMDBAL::NotFound& e) { Keys map({{keyId, level}}); modifiedKeys[encryption].insert(keyOwnerJid, keyId); modifiedJids.insert(keyOwnerJid); @@ -204,7 +204,7 @@ QXmppTask Core::TrustHandler::hasKey(const QString& encryption, break; } } - } catch (const DataBase::NotFound& e) {} + } catch (const LMDBAL::NotFound& e) {} return Core::makeReadyTask(std::move(found)); } @@ -226,7 +226,7 @@ QXmppTask>> Core::TrustHandl pRes.insert(pair.first, level); } } - } catch (const DataBase::NotFound& e) {} + } catch (const LMDBAL::NotFound& e) {} } return Core::makeReadyTask(std::move(res)); } @@ -281,7 +281,7 @@ QXmppTask Core::TrustHandler::removeKeys(const QString& encryption, const try { cache->removeRecord(keyOwnerJid); emit trustLevelsChanged(keyOwnerJid, getSummary(keyOwnerJid)); //TODO there is a probability of notification without the actial change - } catch (const DataBase::NotFound& e) {} //if the movin entry was empty or if it consisted of Undecided keys + } catch (const LMDBAL::NotFound& e) {} //if the movin entry was empty or if it consisted of Undecided keys return Core::makeReadyTask(); @@ -334,7 +334,7 @@ QXmppTask Core::TrustHandler::addKeys( try { data = cache->getRecord(keyOwnerJid); had = true; - } catch (const DataBase::NotFound& e) {} + } catch (const LMDBAL::NotFound& e) {} for (const QByteArray& keyId : keyIds) { std::pair result = data.insert(std::make_pair(keyId, level)); if (!result.second) @@ -355,14 +355,14 @@ QXmppTask Core::TrustHandler::ownKey(const QString& encryption) { QByteArray res; try { res = ownKeys->getRecord(encryption); - } catch (const DataBase::NotFound& e) {} + } catch (const LMDBAL::NotFound& e) {} return Core::makeReadyTask(std::move(res)); } QXmppTask Core::TrustHandler::resetOwnKey(const QString& encryption) { try { ownKeys->removeRecord(encryption); - } catch (const DataBase::NotFound& e) {} + } catch (const LMDBAL::NotFound& e) {} return Core::makeReadyTask(); } @@ -376,14 +376,14 @@ QXmppTask Core::TrustHandler::securityPolicy(const Q QXmpp::TrustSecurityPolicy res; try { res = static_cast(securityPolicies->getRecord(encryption)); - } catch (const DataBase::NotFound& e) {} + } catch (const LMDBAL::NotFound& e) {} return Core::makeReadyTask(std::move(res)); } QXmppTask Core::TrustHandler::resetSecurityPolicy(const QString& encryption) { try { securityPolicies->removeRecord(encryption); - } catch (const DataBase::NotFound& e) {} + } catch (const LMDBAL::NotFound& e) {} return Core::makeReadyTask(); } @@ -404,7 +404,7 @@ Core::TrustHandler::Keys Core::TrustHandler::getKeys(Shared::EncryptionProtocol try { Keys map = itr->second->getRecord(jid); return map; - } catch (const DataBase::NotFound& e) { + } catch (const LMDBAL::NotFound& e) { return Keys(); } } else { @@ -421,7 +421,7 @@ Shared::TrustSummary Core::TrustHandler::getSummary(const QString& jid) const { for (const std::pair& trust : keys) { result.increment(protocol, trust.second); } - } catch (const DataBase::NotFound& e) {} + } catch (const LMDBAL::NotFound& e) {} } return result; diff --git a/core/handlers/trusthandler.h b/core/handlers/trusthandler.h index 98d7f1e..68a4aa1 100644 --- a/core/handlers/trusthandler.h +++ b/core/handlers/trusthandler.h @@ -45,7 +45,7 @@ public: typedef QHash> HSHBTL; typedef std::map Keys; - typedef DataBase::Cache KeyCache; + typedef LMDBAL::Cache KeyCache; virtual QXmppTask resetAll(CSR encryption) override; virtual QXmppTask trustLevel(CSR encryption, CSR keyOwnerJid, const QByteArray& keyId) override; @@ -77,10 +77,10 @@ private: private: Account* acc; - DataBase db; + LMDBAL::Base db; QFile protocols; - DataBase::Cache* securityPolicies; - DataBase::Cache* ownKeys; + LMDBAL::Cache* securityPolicies; + LMDBAL::Cache* ownKeys; std::map keysByProtocol; }; diff --git a/external/lmdbal b/external/lmdbal new file mode 160000 index 0000000..c83369f --- /dev/null +++ b/external/lmdbal @@ -0,0 +1 @@ +Subproject commit c83369f34761e7a053d62312bd07fe5b3db3a519 diff --git a/external/storage b/external/storage deleted file mode 160000 index 6a8f67a..0000000 --- a/external/storage +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6a8f67ac34de286588cd89e3218f55b54da47f42 diff --git a/main/main.cpp b/main/main.cpp index afe1cf2..ce614b3 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -40,6 +40,7 @@ int main(int argc, char *argv[]) qRegisterMetaType>("QSet"); qRegisterMetaType("Shared::ConnectionState"); qRegisterMetaType("Shared::Availability"); + qRegisterMetaType("Shared::EncryptionProtocol"); qRegisterMetaType("Shared::KeyInfo"); qRegisterMetaType("Shared::Info"); #ifdef WITH_OMEMO diff --git a/packaging/Archlinux/PKGBUILD b/packaging/Archlinux/PKGBUILD index 7db43ff..6e63901 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -6,7 +6,7 @@ pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)" arch=('i686' 'x86_64') url="https://git.macaw.me/blue/squawk" license=('GPL3') -depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdb' 'qxmpp>=1.1.0') +depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdbal' 'qxmpp>=1.1.0') makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools' 'boost') optdepends=('kwallet: secure password storage (requires rebuild)' 'kconfig: system themes support (requires rebuild)' @@ -18,9 +18,9 @@ sha256sums=('e4fa2174a3ba95159cc3b0bac3f00550c9e0ce971c55334e2662696a4543fc7e') build() { cd "$srcdir/squawk" cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release - cmake --build . -j $nproc + cmake --build . } package() { cd "$srcdir/squawk" - DESTDIR="$pkgdir/" cmake --build . --target install + DESTDIR="$pkgdir/" cmake --install . } diff --git a/shared/enums.h b/shared/enums.h index aff22b9..c959883 100644 --- a/shared/enums.h +++ b/shared/enums.h @@ -160,11 +160,14 @@ static const TrustLevel TrustLevelHighest = TrustLevel::undecided; static const TrustLevel TrustLevelLowest = TrustLevel::authenticated; enum class EncryptionProtocol { + none, omemo, omemo1, omemo2 }; Q_ENUM_NS(EncryptionProtocol) +static const EncryptionProtocol EncryptionProtocolHighest = EncryptionProtocol::none; +static const EncryptionProtocol EncryptionProtocolLowest = EncryptionProtocol::omemo2; } #endif // SHARED_ENUMS_H diff --git a/shared/global.cpp b/shared/global.cpp index 1b5763b..95168b2 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -405,3 +405,4 @@ FROM_INT_INPL(Shared::AccountPassword) FROM_INT_INPL(Shared::Avatar) FROM_INT_INPL(Shared::Availability) FROM_INT_INPL(Shared::TrustLevel) +FROM_INT_INPL(Shared::EncryptionProtocol) diff --git a/shared/trustsummary.cpp b/shared/trustsummary.cpp index eae98bf..47abb16 100644 --- a/shared/trustsummary.cpp +++ b/shared/trustsummary.cpp @@ -28,11 +28,13 @@ const std::set Shared::TrustSummary::untrustedLevels({ }); const std::map Shared::TrustSummary::protocolKeys({ + {Shared::EncryptionProtocol::none, "none"}, {Shared::EncryptionProtocol::omemo, "eu.siacs.conversations.axolotl"}, {Shared::EncryptionProtocol::omemo1, "urn:xmpp:omemo:1"}, {Shared::EncryptionProtocol::omemo2, "urn:xmpp:omemo:2"} }); const std::map Shared::TrustSummary::protocolValues({ + {"none", Shared::EncryptionProtocol::none}, {"eu.siacs.conversations.axolotl", Shared::EncryptionProtocol::omemo}, {"urn:xmpp:omemo:1", Shared::EncryptionProtocol::omemo1}, {"urn:xmpp:omemo:2", Shared::EncryptionProtocol::omemo2} diff --git a/ui/models/contact.cpp b/ui/models/contact.cpp index 8d6def9..c27965b 100644 --- a/ui/models/contact.cpp +++ b/ui/models/contact.cpp @@ -24,6 +24,7 @@ Models::Contact::Contact(const Account* acc, const QString& p_jid ,const QMap presences; QString status; + }; } diff --git a/ui/widgets/chat.cpp b/ui/widgets/chat.cpp index 607fab4..301553b 100644 --- a/ui/widgets/chat.cpp +++ b/ui/widgets/chat.cpp @@ -29,8 +29,10 @@ Chat::Chat(Models::Account* acc, Models::Contact* p_contact, QWidget* parent): setAvatar(p_contact->getAvatarPath()); connect(contact, &Models::Contact::childChanged, this, &Chat::onContactChanged); - if (p_contact->hasKeys(Shared::EncryptionProtocol::omemo2)) + if (p_contact->hasKeys(Shared::EncryptionProtocol::omemo2)) { m_ui->encryptionButton->setVisible(true); + //if () + } } Chat::~Chat() From 5fbb03fc4604279c6836c7baa8ca837b8a306db0 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 15 Apr 2023 15:07:27 -0300 Subject: [PATCH 106/137] transitioned urlstorage to LMDBAL, made it possible to build against latest qxmpp --- CMakeLists.txt | 10 +- core/components/CMakeLists.txt | 2 + core/components/networkaccess.cpp | 126 +++--- core/components/networkaccess.h | 6 +- core/components/urlstorage.cpp | 289 +++++++++++++ core/{storage => components}/urlstorage.h | 28 +- core/storage/CMakeLists.txt | 6 - core/storage/urlstorage.cpp | 491 ---------------------- external/qxmpp | 2 +- main/main.cpp | 2 +- 10 files changed, 363 insertions(+), 599 deletions(-) create mode 100644 core/components/urlstorage.cpp rename core/{storage => components}/urlstorage.h (90%) delete mode 100644 core/storage/urlstorage.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6cdb712..06c4344 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,16 +143,18 @@ if (NOT SYSTEM_QXMPP) endif () add_subdirectory(external/qxmpp) - target_link_libraries(squawk PRIVATE qxmpp) - target_link_libraries(squawk PRIVATE QXmppOmemo) + target_link_libraries(squawk PRIVATE QXmppQt${QT_VERSION_MAJOR}) + if (WITH_OMEMO) + target_link_libraries(squawk PRIVATE QXmppOmemoQt${QT_VERSION_MAJOR}) + endif () else () target_link_libraries(squawk PRIVATE QXmpp::QXmpp) endif () ## LMDBAL if (SYSTEM_LMDBAL) - find_package(lmdbal CONFIG) - if (NOT LMDBAL_FOUND) + find_package(lmdbal) + if (NOT lmdbal_FOUND) set(SYSTEM_LMDBAL OFF) message("LMDBAL package wasn't found, trying to build with bundled LMDBAL") else () diff --git a/core/components/CMakeLists.txt b/core/components/CMakeLists.txt index b78794a..86d9fb8 100644 --- a/core/components/CMakeLists.txt +++ b/core/components/CMakeLists.txt @@ -1,11 +1,13 @@ set(SOURCE_FILES networkaccess.cpp clientcache.cpp + urlstorage.cpp ) set(HEADER_FILES networkaccess.h clientcache.h + urlstorage.h ) target_sources(squawk PRIVATE diff --git a/core/components/networkaccess.cpp b/core/components/networkaccess.cpp index 22bb7a2..862c664 100644 --- a/core/components/networkaccess.cpp +++ b/core/components/networkaccess.cpp @@ -35,13 +35,11 @@ Core::NetworkAccess::NetworkAccess(QObject* parent): currentPath = settings.value("downloadsPath").toString(); } -Core::NetworkAccess::~NetworkAccess() -{ +Core::NetworkAccess::~NetworkAccess() { stop(); } -void Core::NetworkAccess::downladFile(const QString& url) -{ +void Core::NetworkAccess::downladFile(const QString& url) { std::map::iterator itr = downloads.find(url); if (itr != downloads.end()) { qDebug() << "NetworkAccess received a request to download a file" << url << ", but the file is currently downloading, skipping"; @@ -50,27 +48,25 @@ void Core::NetworkAccess::downladFile(const QString& url) std::pair> p = storage.getPath(url); if (p.first.size() > 0) { QFileInfo info(p.first); - if (info.exists() && info.isFile()) { + if (info.exists() && info.isFile()) emit downloadFileComplete(p.second, p.first); - } else { + else startDownload(p.second, url); - } } else { startDownload(p.second, url); } - } catch (const Archive::NotFound& e) { + } catch (const LMDBAL::NotFound& e) { qDebug() << "NetworkAccess received a request to download a file" << url << ", but there is now record of which message uses that file, downloading anyway"; storage.addFile(url); startDownload(std::list(), url); - } catch (const Archive::Unknown& e) { + } catch (const LMDBAL::Unknown& e) { qDebug() << "Error requesting file path:" << e.what(); emit loadFileError(std::list(), QString("Database error: ") + e.what(), false); } } } -void Core::NetworkAccess::start() -{ +void Core::NetworkAccess::start() { if (!running) { manager = new QNetworkAccessManager(); #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) @@ -81,8 +77,7 @@ void Core::NetworkAccess::start() } } -void Core::NetworkAccess::stop() -{ +void Core::NetworkAccess::stop() { if (running) { storage.close(); manager->deleteLater(); @@ -96,8 +91,7 @@ void Core::NetworkAccess::stop() } } -void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) -{ +void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { QNetworkReply* rpl = static_cast(sender()); QString url = rpl->url().toString(); std::map::const_iterator itr = downloads.find(url); @@ -115,8 +109,7 @@ void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesT } } -void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code) -{ +void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code) { qDebug() << "DEBUG: DOWNLOAD ERROR"; QNetworkReply* rpl = static_cast(sender()); qDebug() << rpl->errorString(); @@ -134,8 +127,7 @@ void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code) } } -void Core::NetworkAccess::onDownloadSSLError(const QList& errors) -{ +void Core::NetworkAccess::onDownloadSSLError(const QList& errors) { qDebug() << "DEBUG: DOWNLOAD SSL ERRORS"; for (const QSslError& err : errors) { qDebug() << err.errorString(); @@ -154,9 +146,7 @@ void Core::NetworkAccess::onDownloadSSLError(const QList& errors) } } - -QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code) -{ +QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code) { QString errorText(""); switch (code) { case QNetworkReply::NoError: @@ -280,8 +270,7 @@ QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code) } -void Core::NetworkAccess::onDownloadFinished() -{ +void Core::NetworkAccess::onDownloadFinished() { qDebug() << "DEBUG: DOWNLOAD FINISHED"; QNetworkReply* rpl = static_cast(sender()); QString url = rpl->url().toString(); @@ -296,11 +285,11 @@ void Core::NetworkAccess::onDownloadFinished() QStringList hops = url.split("/"); QString fileName = hops.back(); QString jid; - if (dwn->messages.size() > 0) { + if (dwn->messages.size() > 0) jid = dwn->messages.front().jid; - } else { + else qDebug() << "An attempt to save the file but it doesn't seem to belong to any message, download is definately going to be broken"; - } + QString path = prepareDirectory(jid); if (path.size() > 0) { path = checkFileName(fileName, path); @@ -319,11 +308,10 @@ void Core::NetworkAccess::onDownloadFinished() err = "Couldn't prepare a directory for file"; } - if (path.size() > 0) { + if (path.size() > 0) emit downloadFileComplete(dwn->messages, path); - } else { + else emit loadFileError(dwn->messages, "Error saving file " + url + "; " + err, false); - } } dwn->reply->deleteLater(); @@ -332,8 +320,7 @@ void Core::NetworkAccess::onDownloadFinished() } } -void Core::NetworkAccess::startDownload(const std::list& msgs, const QString& url) -{ +void Core::NetworkAccess::startDownload(const std::list& msgs, const QString& url) { Transfer* dwn = new Transfer({msgs, 0, 0, true, "", url, 0}); QNetworkRequest req(url); dwn->reply = manager->get(req); @@ -349,8 +336,7 @@ void Core::NetworkAccess::startDownload(const std::list& ms emit loadFileProgress(dwn->messages, 0, false); } -void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code) -{ +void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code) { QNetworkReply* rpl = static_cast(sender()); QString url = rpl->url().toString(); std::map::const_iterator itr = uploads.find(url); @@ -368,8 +354,7 @@ void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code) } } -void Core::NetworkAccess::onUploadFinished() -{ +void Core::NetworkAccess::onUploadFinished() { QNetworkReply* rpl = static_cast(sender()); QString url = rpl->url().toString(); std::map::const_iterator itr = uploads.find(url); @@ -389,20 +374,16 @@ void Core::NetworkAccess::onUploadFinished() // Copy {TEMPDIR}/squawk_img_attach_XXXXXX.png to Download folder bool copyResult = QFile::copy(upl->path, Shared::resolvePath(newPath)); - - if (copyResult) { - // Change storage - upl->path = newPath; - } else { + if (copyResult) + upl->path = newPath; // Change storage + else err = "copying to " + newPath + " failed"; - } } else { err = "Couldn't prepare a directory for file"; } - if (err.size() != 0) { + if (err.size() != 0) qDebug() << "failed to copy temporary upload file " << upl->path << " to download folder:" << err; - } } storage.addFile(upl->messages, upl->url, upl->path); @@ -417,8 +398,7 @@ void Core::NetworkAccess::onUploadFinished() } } -void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTotal) -{ +void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTotal) { QNetworkReply* rpl = static_cast(sender()); QString url = rpl->url().toString(); std::map::const_iterator itr = uploads.find(url); @@ -436,13 +416,12 @@ void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTot } } -QString Core::NetworkAccess::getFileRemoteUrl(const QString& path) -{ +QString Core::NetworkAccess::getFileRemoteUrl(const QString& path) { QString p = Shared::squawkifyPath(path); try { p = storage.getUrl(p); - } catch (const Archive::NotFound& err) { + } catch (const LMDBAL::NotFound& err) { p = ""; } catch (...) { throw; @@ -451,8 +430,13 @@ QString Core::NetworkAccess::getFileRemoteUrl(const QString& path) return p; } -void Core::NetworkAccess::uploadFile(const Shared::MessageInfo& info, const QString& path, const QUrl& put, const QUrl& get, const QMap headers) -{ +void Core::NetworkAccess::uploadFile( + const Shared::MessageInfo& info, + const QString& path, + const QUrl& put, + const QUrl& get, + const QMap headers +) { QFile* file = new QFile(path); Transfer* upl = new Transfer({{info}, 0, 0, true, path, get.toString(), file}); QNetworkRequest req(put); @@ -479,22 +463,18 @@ void Core::NetworkAccess::uploadFile(const Shared::MessageInfo& info, const QStr } } -void Core::NetworkAccess::registerFile(const QString& url, const QString& account, const QString& jid, const QString& id) -{ +void Core::NetworkAccess::registerFile(const QString& url, const QString& account, const QString& jid, const QString& id) { storage.addFile(url, account, jid, id); std::map::iterator itr = downloads.find(url); - if (itr != downloads.end()) { + if (itr != downloads.end()) itr->second->messages.emplace_back(account, jid, id); //TODO notification is going to happen the next tick, is that okay? - } } -void Core::NetworkAccess::registerFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id) -{ +void Core::NetworkAccess::registerFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id) { storage.addFile(url, path, account, jid, id); } -bool Core::NetworkAccess::checkAndAddToUploading(const QString& acc, const QString& jid, const QString id, const QString path) -{ +bool Core::NetworkAccess::checkAndAddToUploading(const QString& acc, const QString& jid, const QString id, const QString path) { for (const std::pair& pair : uploads) { Transfer* info = pair.second; if (pair.second->path == path) { @@ -516,8 +496,7 @@ bool Core::NetworkAccess::checkAndAddToUploading(const QString& acc, const QStri return false; } -QString Core::NetworkAccess::prepareDirectory(const QString& jid) -{ +QString Core::NetworkAccess::prepareDirectory(const QString& jid) { QString path = currentPath; QString addition; if (jid.size() > 0) { @@ -529,25 +508,23 @@ QString Core::NetworkAccess::prepareDirectory(const QString& jid) if (!location.exists()) { bool res = location.mkpath(path); - if (!res) { + if (!res) return ""; - } else { + else return "squawk://" + addition; - } } return "squawk://" + addition; } -QString Core::NetworkAccess::checkFileName(const QString& name, const QString& path) -{ +QString Core::NetworkAccess::checkFileName(const QString& name, const QString& path) { QStringList parts = name.split("."); QString suffix(""); QStringList::const_iterator sItr = parts.begin(); QString realName = *sItr; ++sItr; - for (QStringList::const_iterator sEnd = parts.end(); sItr != sEnd; ++sItr) { + for (QStringList::const_iterator sEnd = parts.end(); sItr != sEnd; ++sItr) suffix += "." + (*sItr); - } + QString postfix(""); QString resolvedPath = Shared::resolvePath(path); QString count(""); @@ -562,18 +539,15 @@ QString Core::NetworkAccess::checkFileName(const QString& name, const QString& p return path + QDir::separator() + realName + count + suffix; } -QString Core::NetworkAccess::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id) -{ +QString Core::NetworkAccess::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id) { return storage.addMessageAndCheckForPath(url, account, jid, id); } -std::list Core::NetworkAccess::reportPathInvalid(const QString& path) -{ +std::list Core::NetworkAccess::reportPathInvalid(const QString& path) { return storage.deletedFile(path); } -void Core::NetworkAccess::moveFilesDirectory(const QString& newPath) -{ +void Core::NetworkAccess::moveFilesDirectory(const QString& newPath) { QDir dir(currentPath); bool success = true; qDebug() << "moving" << currentPath << "to" << newPath; @@ -582,8 +556,8 @@ void Core::NetworkAccess::moveFilesDirectory(const QString& newPath) success = dir.rename(fileName, newPath + QDir::separator() + fileName) && success; } - if (!success) { + if (!success) qDebug() << "couldn't move downloads directory, most probably downloads will be broken"; - } + currentPath = newPath; } diff --git a/core/components/networkaccess.h b/core/components/networkaccess.h index c94c22a..99a15ed 100644 --- a/core/components/networkaccess.h +++ b/core/components/networkaccess.h @@ -30,15 +30,11 @@ #include -#include #include +#include "urlstorage.h" namespace Core { -/** - * @todo write docs - */ - //TODO Need to describe how to get rid of records when file is no longer reachable; class NetworkAccess : public QObject { diff --git a/core/components/urlstorage.cpp b/core/components/urlstorage.cpp new file mode 100644 index 0000000..c745236 --- /dev/null +++ b/core/components/urlstorage.cpp @@ -0,0 +1,289 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 +#include +#include + +#include "urlstorage.h" + +Core::UrlStorage::UrlStorage(const QString& p_name): + base(p_name), + urlToInfo(base.addStorage("urlToInfo")), + pathToUrl(base.addStorage("pathToUrl")) +{} + +Core::UrlStorage::~UrlStorage() { + close(); +} + +void Core::UrlStorage::open() { + base.open(); +} + +void Core::UrlStorage::close() { + base.close(); +} + +void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, bool overwrite) { + LMDBAL::TransactionID txn = base.beginTransaction(); + + try { + writeInfo(key, info, txn, overwrite); + base.commitTransaction(txn); + } catch (...) { + base.abortTransaction(txn); + throw; + } +} + +void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, MDB_txn* txn, bool overwrite) { + if (overwrite) + urlToInfo->forceRecord(key, info, txn); + else + urlToInfo->addRecord(key, info, txn); + + if (info.hasPath()) + pathToUrl->forceRecord(info.getPath(), key, txn); +} + +void Core::UrlStorage::addFile(const QString& url) { + addToInfo(url, "", "", ""); +} + +void Core::UrlStorage::addFile(const QString& url, const QString& path) { + addToInfo(url, "", "", "", path); +} + +void Core::UrlStorage::addFile(const QString& url, const QString& account, const QString& jid, const QString& id) { + addToInfo(url, account, jid, id); +} + +void Core::UrlStorage::addFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id) { + addToInfo(url, account, jid, id, path); +} + +void Core::UrlStorage::addFile(const std::list& msgs, const QString& url, const QString& path) { + UrlInfo info (path, msgs); + writeInfo(url, info, true); +} + +QString Core::UrlStorage::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id){ + return addToInfo(url, account, jid, id).getPath(); +} + +Core::UrlStorage::UrlInfo Core::UrlStorage::addToInfo( + const QString& url, + const QString& account, + const QString& jid, + const QString& id, + const QString& path +) { + UrlInfo info; + LMDBAL::TransactionID txn = base.beginTransaction(); + + try { + urlToInfo->getRecord(url, info, txn); + } catch (const LMDBAL::NotFound& e) { + } catch (...) { + base.abortTransaction(txn); + throw; + } + + bool pathChange = false; + bool listChange = false; + if (path != "-s") { + if (info.getPath() != path) { + info.setPath(path); + pathChange = true; + } + } + + if (account.size() > 0 && jid.size() > 0 && id.size() > 0) + listChange = info.addMessage(account, jid, id); + + if (pathChange || listChange) { + try { + writeInfo(url, info, txn, true); + base.commitTransaction(txn); + } catch (...) { + base.abortTransaction(txn); + throw; + } + } else { + base.abortTransaction(txn); + } + + return info; +} + +std::list Core::UrlStorage::setPath(const QString& url, const QString& path) { + std::list list; + LMDBAL::TransactionID txn = base.beginTransaction(); + UrlInfo info; + + try { + urlToInfo->getRecord(url, info, txn); + info.getMessages(list); + } catch (const LMDBAL::NotFound& e) { + } catch (...) { + base.abortTransaction(txn); + throw; + } + + info.setPath(path); + try { + writeInfo(url, info, txn, true); + base.commitTransaction(txn); + } catch (...) { + base.abortTransaction(txn); + throw; + } + + return list; +} + +std::list Core::UrlStorage::removeFile(const QString& url) { + std::list list; + LMDBAL::TransactionID txn = base.beginTransaction(); + UrlInfo info; + + try { + urlToInfo->getRecord(url, info, txn); + urlToInfo->removeRecord(url); + info.getMessages(list); + + if (info.hasPath()) + pathToUrl->removeRecord(info.getPath()); + + base.commitTransaction(txn); + } catch (...) { + base.abortTransaction(txn); + throw; + } + + return list; +} + +std::list Core::UrlStorage::deletedFile(const QString& path) { + std::list list; + LMDBAL::TransactionID txn = base.beginTransaction(); + + try { + QString url = pathToUrl->getRecord(path, txn); + pathToUrl->removeRecord(path); + + UrlInfo info = urlToInfo->getRecord(url, txn); + info.getMessages(list); + info.setPath(QString()); + urlToInfo->changeRecord(url, info, txn); + + base.commitTransaction(txn); + } catch (...) { + base.abortTransaction(txn); + throw; + } + + return list; +} + +QString Core::UrlStorage::getUrl(const QString& path) { + return pathToUrl->getRecord(path); +} + +std::pair> Core::UrlStorage::getPath(const QString& url) { + UrlInfo info = urlToInfo->getRecord(url); + std::list container; + info.getMessages(container); + return std::make_pair(info.getPath(), container); +} + +Core::UrlStorage::UrlInfo::UrlInfo(): + localPath(), + messages() {} + +Core::UrlStorage::UrlInfo::UrlInfo(const QString& path): + localPath(path), + messages() {} + +Core::UrlStorage::UrlInfo::UrlInfo(const QString& path, const std::list& msgs): + localPath(path), + messages(msgs) {} + +Core::UrlStorage::UrlInfo::~UrlInfo() {} + +bool Core::UrlStorage::UrlInfo::addMessage(const QString& acc, const QString& jid, const QString& id) { + for (const Shared::MessageInfo& info : messages) { + if (info.account == acc && info.jid == jid && info.messageId == id) { + return false; + } + } + messages.emplace_back(acc, jid, id); + return true; +} + +void Core::UrlStorage::UrlInfo::serialize(QDataStream& data) const { + data << localPath; + std::list::size_type size = messages.size(); + data << quint32(size); + for (const Shared::MessageInfo& info : messages) { + data << info.account; + data << info.jid; + data << info.messageId; + } +} + +QDataStream & operator << (QDataStream& in, const Core::UrlStorage::UrlInfo& info) { + info.serialize(in); + return in; +} + +QDataStream & operator >> (QDataStream& out, Core::UrlStorage::UrlInfo& info) { + info.deserialize(out); + return out; +} + +void Core::UrlStorage::UrlInfo::deserialize(QDataStream& data) { + data >> localPath; + quint32 size; + data >> size; + for (quint32 i = 0; i < size; ++i) { + messages.emplace_back(); + Shared::MessageInfo& info = messages.back(); + data >> info.account; + data >> info.jid; + data >> info.messageId; + } +} + +void Core::UrlStorage::UrlInfo::getMessages(std::list& container) const { + for (const Shared::MessageInfo& info : messages) + container.emplace_back(info); +} + +QString Core::UrlStorage::UrlInfo::getPath() const { + return localPath; +} + +bool Core::UrlStorage::UrlInfo::hasPath() const { + return localPath.size() > 0; +} + +void Core::UrlStorage::UrlInfo::setPath(const QString& path) { + localPath = path; +} diff --git a/core/storage/urlstorage.h b/core/components/urlstorage.h similarity index 90% rename from core/storage/urlstorage.h rename to core/components/urlstorage.h index 3dc5c21..ee8e30d 100644 --- a/core/storage/urlstorage.h +++ b/core/components/urlstorage.h @@ -21,20 +21,19 @@ #include #include -#include + #include -#include "archive.h" +#include + #include namespace Core { -/** - * @todo write docs - */ -class UrlStorage -{ +class UrlStorage { +public: class UrlInfo; + public: UrlStorage(const QString& name); ~UrlStorage(); @@ -55,20 +54,16 @@ public: std::pair> getPath(const QString& url); private: - QString name; - bool opened; - MDB_env* environment; - MDB_dbi base; - MDB_dbi map; + LMDBAL::Base base; + LMDBAL::Storage* urlToInfo; + LMDBAL::Storage* pathToUrl; private: void writeInfo(const QString& key, const UrlInfo& info, bool overwrite = false); void writeInfo(const QString& key, const UrlInfo& info, MDB_txn* txn, bool overwrite = false); - void readInfo(const QString& key, UrlInfo& info); - void readInfo(const QString& key, UrlInfo& info, MDB_txn* txn); UrlInfo addToInfo(const QString& url, const QString& account, const QString& jid, const QString& id, const QString& path = "-s"); -private: +public: class UrlInfo { public: UrlInfo(const QString& path); @@ -96,4 +91,7 @@ private: } +QDataStream& operator >> (QDataStream &in, Core::UrlStorage::UrlInfo& info); +QDataStream& operator << (QDataStream &out, const Core::UrlStorage::UrlInfo& info); + #endif // CORE_URLSTORAGE_H diff --git a/core/storage/CMakeLists.txt b/core/storage/CMakeLists.txt index 238b59a..e2de42c 100644 --- a/core/storage/CMakeLists.txt +++ b/core/storage/CMakeLists.txt @@ -1,10 +1,4 @@ target_sources(squawk PRIVATE archive.cpp archive.h -# storage.hpp -# storage.h - urlstorage.cpp - urlstorage.h -# cache.hpp -# cache.h ) diff --git a/core/storage/urlstorage.cpp b/core/storage/urlstorage.cpp deleted file mode 100644 index f59ff62..0000000 --- a/core/storage/urlstorage.cpp +++ /dev/null @@ -1,491 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * 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 -#include -#include - -#include "urlstorage.h" - -Core::UrlStorage::UrlStorage(const QString& p_name): - name(p_name), - opened(false), - environment(), - base(), - map() -{ -} - -Core::UrlStorage::~UrlStorage() -{ - close(); -} - -void Core::UrlStorage::open() -{ - if (!opened) { - mdb_env_create(&environment); - QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); - path += "/" + name; - QDir cache(path); - - if (!cache.exists()) { - bool res = cache.mkpath(path); - if (!res) { - throw Archive::Directory(path.toStdString()); - } - } - - mdb_env_set_maxdbs(environment, 2); - mdb_env_set_mapsize(environment, 10UL * 1024UL * 1024UL); - mdb_env_open(environment, path.toStdString().c_str(), 0, 0664); - - MDB_txn *txn; - mdb_txn_begin(environment, NULL, 0, &txn); - mdb_dbi_open(txn, "base", MDB_CREATE, &base); - mdb_dbi_open(txn, "map", MDB_CREATE, &map); - mdb_txn_commit(txn); - opened = true; - } -} - -void Core::UrlStorage::close() -{ - if (opened) { - mdb_dbi_close(environment, map); - mdb_dbi_close(environment, base); - mdb_env_close(environment); - opened = false; - } -} - -void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, bool overwrite) -{ - MDB_txn *txn; - mdb_txn_begin(environment, NULL, 0, &txn); - - try { - writeInfo(key, info, txn, overwrite); - mdb_txn_commit(txn); - } catch (...) { - mdb_txn_abort(txn); - throw; - } -} - -void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, MDB_txn* txn, bool overwrite) -{ - QByteArray ba; - QDataStream ds(&ba, QIODevice::WriteOnly); - info.serialize(ds); - - const std::string& id = key.toStdString(); - MDB_val lmdbKey, lmdbData; - lmdbKey.mv_size = id.size(); - lmdbKey.mv_data = (char*)id.c_str(); - lmdbData.mv_size = ba.size(); - lmdbData.mv_data = (uint8_t*)ba.data(); - - int rc; - rc = mdb_put(txn, base, &lmdbKey, &lmdbData, overwrite ? 0 : MDB_NOOVERWRITE); - - if (rc != 0) { - if (rc == MDB_KEYEXIST) { - if (!overwrite) { - throw Archive::Exist(name.toStdString(), id); - } - } else { - throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); - } - } - - if (info.hasPath()) { - std::string sp = info.getPath().toStdString(); - lmdbData.mv_size = sp.size(); - lmdbData.mv_data = (char*)sp.c_str(); - rc = mdb_put(txn, map, &lmdbData, &lmdbKey, 0); - if (rc != 0) { - throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); - } - } -} - -void Core::UrlStorage::readInfo(const QString& key, Core::UrlStorage::UrlInfo& info, MDB_txn* txn) -{ - const std::string& id = key.toStdString(); - MDB_val lmdbKey, lmdbData; - lmdbKey.mv_size = id.size(); - lmdbKey.mv_data = (char*)id.c_str(); - int rc = mdb_get(txn, base, &lmdbKey, &lmdbData); - - if (rc == 0) { - QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size); - QDataStream ds(&ba, QIODevice::ReadOnly); - - info.deserialize(ds); - } else if (rc == MDB_NOTFOUND) { - throw Archive::NotFound(id, name.toStdString()); - } else { - throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); - } -} - -void Core::UrlStorage::readInfo(const QString& key, Core::UrlStorage::UrlInfo& info) -{ - MDB_txn *txn; - mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); - - try { - readInfo(key, info, txn); - mdb_txn_commit(txn); - } catch (...) { - mdb_txn_abort(txn); - throw; - } -} - -void Core::UrlStorage::addFile(const QString& url) -{ - if (!opened) { - throw Archive::Closed("addFile(no message, no path)", name.toStdString()); - } - - addToInfo(url, "", "", ""); -} - -void Core::UrlStorage::addFile(const QString& url, const QString& path) -{ - if (!opened) { - throw Archive::Closed("addFile(no message, with path)", name.toStdString()); - } - - addToInfo(url, "", "", "", path); -} - -void Core::UrlStorage::addFile(const QString& url, const QString& account, const QString& jid, const QString& id) -{ - if (!opened) { - throw Archive::Closed("addFile(with message, no path)", name.toStdString()); - } - - addToInfo(url, account, jid, id); -} - -void Core::UrlStorage::addFile(const QString& url, const QString& path, const QString& account, const QString& jid, const QString& id) -{ - if (!opened) { - throw Archive::Closed("addFile(with message, with path)", name.toStdString()); - } - - addToInfo(url, account, jid, id, path); -} - -void Core::UrlStorage::addFile(const std::list& msgs, const QString& url, const QString& path) -{ - if (!opened) { - throw Archive::Closed("addFile(with list)", name.toStdString()); - } - - UrlInfo info (path, msgs); - writeInfo(url, info, true);; -} - -QString Core::UrlStorage::addMessageAndCheckForPath(const QString& url, const QString& account, const QString& jid, const QString& id) -{ - if (!opened) { - throw Archive::Closed("addMessageAndCheckForPath", name.toStdString()); - } - - return addToInfo(url, account, jid, id).getPath(); -} - -Core::UrlStorage::UrlInfo Core::UrlStorage::addToInfo(const QString& url, const QString& account, const QString& jid, const QString& id, const QString& path) -{ - UrlInfo info; - MDB_txn *txn; - mdb_txn_begin(environment, NULL, 0, &txn); - - try { - readInfo(url, info, txn); - } catch (const Archive::NotFound& e) { - - } catch (...) { - mdb_txn_abort(txn); - throw; - } - - bool pathChange = false; - bool listChange = false; - if (path != "-s") { - if (info.getPath() != path) { - info.setPath(path); - pathChange = true; - } - } - - if (account.size() > 0 && jid.size() > 0 && id.size() > 0) { - listChange = info.addMessage(account, jid, id); - } - - if (pathChange || listChange) { - try { - writeInfo(url, info, txn, true); - mdb_txn_commit(txn); - } catch (...) { - mdb_txn_abort(txn); - throw; - } - } else { - mdb_txn_abort(txn); - } - - return info; -} - -std::list Core::UrlStorage::setPath(const QString& url, const QString& path) -{ - std::list list; - - MDB_txn *txn; - mdb_txn_begin(environment, NULL, 0, &txn); - UrlInfo info; - - try { - readInfo(url, info, txn); - info.getMessages(list); - } catch (const Archive::NotFound& e) { - } catch (...) { - mdb_txn_abort(txn); - throw; - } - - info.setPath(path); - try { - writeInfo(url, info, txn, true); - mdb_txn_commit(txn); - } catch (...) { - mdb_txn_abort(txn); - throw; - } - - return list; -} - -std::list Core::UrlStorage::removeFile(const QString& url) -{ - std::list list; - - MDB_txn *txn; - mdb_txn_begin(environment, NULL, 0, &txn); - UrlInfo info; - - try { - std::string id = url.toStdString(); - readInfo(url, info, txn); - info.getMessages(list); - - MDB_val lmdbKey; - lmdbKey.mv_size = id.size(); - lmdbKey.mv_data = (char*)id.c_str(); - int rc = mdb_del(txn, base, &lmdbKey, NULL); - if (rc != 0) { - throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); - } - - if (info.hasPath()) { - std::string path = info.getPath().toStdString(); - lmdbKey.mv_size = path.size(); - lmdbKey.mv_data = (char*)path.c_str(); - - int rc = mdb_del(txn, map, &lmdbKey, NULL); - if (rc != 0) { - throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); - } - } - mdb_txn_commit(txn); - } catch (...) { - mdb_txn_abort(txn); - throw; - } - - return list; -} - -std::list Core::UrlStorage::deletedFile(const QString& path) -{ - std::list list; - - MDB_txn *txn; - mdb_txn_begin(environment, NULL, 0, &txn); - - try { - std::string spath = path.toStdString(); - - MDB_val lmdbKey, lmdbData; - lmdbKey.mv_size = spath.size(); - lmdbKey.mv_data = (char*)spath.c_str(); - - QString url; - int rc = mdb_get(txn, map, &lmdbKey, &lmdbData); - - if (rc == 0) { - std::string surl((char*)lmdbData.mv_data, lmdbData.mv_size); - url = QString(surl.c_str()); - } else if (rc == MDB_NOTFOUND) { - qDebug() << "Have been asked to remove file" << path << ", which isn't in the database, skipping"; - mdb_txn_abort(txn); - return list; - } else { - throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); - } - - UrlInfo info; - std::string id = url.toStdString(); - readInfo(url, info, txn); - info.getMessages(list); - info.setPath(QString()); - writeInfo(url, info, txn, true); - - rc = mdb_del(txn, map, &lmdbKey, NULL); - if (rc != 0) { - throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); - } - - mdb_txn_commit(txn); - } catch (...) { - mdb_txn_abort(txn); - throw; - } - - return list; -} - - -QString Core::UrlStorage::getUrl(const QString& path) -{ - std::list list; - - MDB_txn *txn; - mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); - - std::string spath = path.toStdString(); - - MDB_val lmdbKey, lmdbData; - lmdbKey.mv_size = spath.size(); - lmdbKey.mv_data = (char*)spath.c_str(); - - QString url; - int rc = mdb_get(txn, map, &lmdbKey, &lmdbData); - - if (rc == 0) { - std::string surl((char*)lmdbData.mv_data, lmdbData.mv_size); - url = QString(surl.c_str()); - - mdb_txn_abort(txn); - return url; - } else if (rc == MDB_NOTFOUND) { - mdb_txn_abort(txn); - throw Archive::NotFound(spath, name.toStdString()); - } else { - mdb_txn_abort(txn); - throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); - } -} - -std::pair> Core::UrlStorage::getPath(const QString& url) -{ - UrlInfo info; - readInfo(url, info); - std::list container; - info.getMessages(container); - return std::make_pair(info.getPath(), container); -} - -Core::UrlStorage::UrlInfo::UrlInfo(): - localPath(), - messages() {} - -Core::UrlStorage::UrlInfo::UrlInfo(const QString& path): - localPath(path), - messages() {} - -Core::UrlStorage::UrlInfo::UrlInfo(const QString& path, const std::list& msgs): - localPath(path), - messages(msgs) {} - -Core::UrlStorage::UrlInfo::~UrlInfo() {} - -bool Core::UrlStorage::UrlInfo::addMessage(const QString& acc, const QString& jid, const QString& id) -{ - for (const Shared::MessageInfo& info : messages) { - if (info.account == acc && info.jid == jid && info.messageId == id) { - return false; - } - } - messages.emplace_back(acc, jid, id); - return true; -} - -void Core::UrlStorage::UrlInfo::serialize(QDataStream& data) const -{ - data << localPath; - std::list::size_type size = messages.size(); - data << quint32(size); - for (const Shared::MessageInfo& info : messages) { - data << info.account; - data << info.jid; - data << info.messageId; - } -} - -void Core::UrlStorage::UrlInfo::deserialize(QDataStream& data) -{ - data >> localPath; - quint32 size; - data >> size; - for (quint32 i = 0; i < size; ++i) { - messages.emplace_back(); - Shared::MessageInfo& info = messages.back(); - data >> info.account; - data >> info.jid; - data >> info.messageId; - } -} - -void Core::UrlStorage::UrlInfo::getMessages(std::list& container) const -{ - for (const Shared::MessageInfo& info : messages) { - container.emplace_back(info); - } -} - -QString Core::UrlStorage::UrlInfo::getPath() const -{ - return localPath; -} - -bool Core::UrlStorage::UrlInfo::hasPath() const -{ - return localPath.size() > 0; -} - - -void Core::UrlStorage::UrlInfo::setPath(const QString& path) -{ - localPath = path; -} diff --git a/external/qxmpp b/external/qxmpp index d679ad1..ab4bdf2 160000 --- a/external/qxmpp +++ b/external/qxmpp @@ -1 +1 @@ -Subproject commit d679ad1c49eeb28be2ac3a75bd7fd1a9be24d483 +Subproject commit ab4bdf2da41a26f462fe3a333a34e32c999e2a6d diff --git a/main/main.cpp b/main/main.cpp index ce614b3..d9ad4cc 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -43,6 +43,7 @@ int main(int argc, char *argv[]) qRegisterMetaType("Shared::EncryptionProtocol"); qRegisterMetaType("Shared::KeyInfo"); qRegisterMetaType("Shared::Info"); + qRegisterMetaType("Shared::TrustLevel"); #ifdef WITH_OMEMO qRegisterMetaType("QXmppOmemoStorage::OwnDevice"); qRegisterMetaTypeStreamOperators("QXmppOmemoStorage::OwnDevice"); @@ -53,7 +54,6 @@ int main(int argc, char *argv[]) if (!app.initializeSettings()) return -1; - return app.run(); } From 23ec80ccba5604aebcd73e9975bf5b743b685a74 Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 15 Aug 2023 12:28:25 -0300 Subject: [PATCH 107/137] cleanup some warnings suppression --- CMakeLists.txt | 1 + core/account.cpp | 6 +- core/handlers/messagehandler.cpp | 152 ++++---- core/handlers/omemohandler.cpp | 1 + core/handlers/rosterhandler.cpp | 1 + core/signalcatcher.cpp | 18 +- main/application.cpp | 196 ++++------- shared/CMakeLists.txt | 1 + shared/defines.h | 24 ++ shared/identity.cpp | 4 + shared/shared.h | 1 + ui/models/accounts.cpp | 85 ++--- ui/models/info/emails.cpp | 36 +- ui/models/info/omemo/keys.cpp | 12 +- ui/models/info/phones.cpp | 44 +-- ui/models/roster.cpp | 390 ++++++++------------- ui/utils/comboboxdelegate.cpp | 30 +- ui/utils/resizer.cpp | 13 +- ui/widgets/chat.cpp | 3 + ui/widgets/conversation.cpp | 135 +++---- ui/widgets/conversation.h | 3 +- ui/widgets/messageline/feedview.cpp | 91 ++--- ui/widgets/messageline/messagedelegate.cpp | 152 +++----- ui/widgets/messageline/messagedelegate.h | 2 + ui/widgets/messageline/messagefeed.cpp | 131 +++---- ui/widgets/room.cpp | 22 +- 26 files changed, 630 insertions(+), 924 deletions(-) create mode 100644 shared/defines.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 06c4344..d29bb4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.5) project(squawk VERSION 0.2.3 LANGUAGES CXX) cmake_policy(SET CMP0076 NEW) +cmake_policy(SET CMP0077 NEW) cmake_policy(SET CMP0079 NEW) set(CMAKE_CXX_STANDARD 17) diff --git a/core/account.cpp b/core/account.cpp index 736d5ae..b06f51b 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -21,6 +21,8 @@ #include +#include "shared/defines.h" + Core::Account::Account( const QString& p_login, const QString& p_server, @@ -159,7 +161,8 @@ Core::Account::Account( logger->setLoggingType(QXmppLogger::SignalLogging); client.setLogger(logger); - QObject::connect(logger, &QXmppLogger::message, this, [](QXmppLogger::MessageType type, const QString& text){ + QObject::connect(logger, &QXmppLogger::message, this, [](QXmppLogger::MessageType type, const QString& text) { + SHARED_UNUSED(type); qDebug() << text; }); } @@ -511,6 +514,7 @@ void Core::Account::onMamResultsReceived(const QString& queryId, const QXmppResu } void Core::Account::onMamLog(QXmppLogger::MessageType type, const QString& msg) { + SHARED_UNUSED(type); qDebug() << "MAM MESSAGE LOG::"; qDebug() << msg; } diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index 020ab6f..3ed8dec 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -24,11 +24,9 @@ Core::MessageHandler::MessageHandler(Core::Account* account): acc(account), pendingStateMessages(), uploadingSlotsQueue() -{ -} +{} -void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) -{ +void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) { #if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) #ifdef WITH_OMEMO switch (msg.encryptionMethod()) { @@ -83,9 +81,9 @@ void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) {"state", static_cast(Shared::Message::State::error)}, {"errorText", msg.error().text()} }; - if (cnt != nullptr) { + if (cnt != nullptr) cnt->changeMessage(id, cData); - } + emit acc->changeMessage(jid, id, cData); handled = true; } else { @@ -97,13 +95,11 @@ void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) qDebug() << "received a message with type \"Headline\", not sure what to do with it now, skipping"; break; } - if (!handled) { + if (!handled) logMessage(msg); - } } -bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) -{ +bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) { if (msg.body().size() != 0 || msg.outOfBandUrl().size() > 0) { Shared::Message sMsg(Shared::Message::chat); initializeMessage(sMsg, msg, outgoing, forwarded, guessing); @@ -138,8 +134,7 @@ bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgo return false; } -bool Core::MessageHandler::handleGroupMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) -{ +bool Core::MessageHandler::handleGroupMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) { const QString& body(msg.body()); if (body.size() != 0) { @@ -182,8 +177,7 @@ bool Core::MessageHandler::handleGroupMessage(const QXmppMessage& msg, bool outg } -void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing, bool forwarded, bool guessing) const -{ +void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing, bool forwarded, bool guessing) const { const QDateTime& time(source.stamp()); QString id; #if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) @@ -229,8 +223,7 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp target.setOutOfBandUrl(oob); } -void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& reason) -{ +void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& reason) { qDebug() << reason; qDebug() << "- from: " << msg.from(); qDebug() << "- to: " << msg.to(); @@ -247,19 +240,16 @@ void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& re } #if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 5, 0) -void Core::MessageHandler::onCarbonMessageReceived(const QXmppMessage& msg) -{ +void Core::MessageHandler::onCarbonMessageReceived(const QXmppMessage& msg) { handleChatMessage(msg, false, true); } -void Core::MessageHandler::onCarbonMessageSent(const QXmppMessage& msg) -{ +void Core::MessageHandler::onCarbonMessageSent(const QXmppMessage& msg) { handleChatMessage(msg, true, true); } #endif -std::tuple Core::MessageHandler::getOriginalPendingMessageId(const QString& id) -{ +std::tuple Core::MessageHandler::getOriginalPendingMessageId(const QString& id) { std::tuple result({false, "", ""}); std::map::const_iterator itr = pendingStateMessages.find(id); if (itr != pendingStateMessages.end()) { @@ -268,11 +258,11 @@ std::tuple Core::MessageHandler::getOriginalPendingMessa std::map::const_iterator itrC = pendingCorrectionMessages.find(id); if (itrC != pendingCorrectionMessages.end()) { - if (itrC->second.size() > 0) { + if (itrC->second.size() > 0) std::get<1>(result) = itrC->second; - } else { + else std::get<1>(result) = itr->first; - } + pendingCorrectionMessages.erase(itrC); } else { std::get<1>(result) = itr->first; @@ -284,8 +274,8 @@ std::tuple Core::MessageHandler::getOriginalPendingMessa return result; } -void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& id) -{ +void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& id) { + SHARED_UNUSED(jid); std::tuple ids = getOriginalPendingMessageId(id); if (std::get<0>(ids)) { QMap cData = {{"state", static_cast(Shared::Message::State::delivered)}}; @@ -298,8 +288,7 @@ void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& } } -void Core::MessageHandler::sendMessage(const Shared::Message& data, bool newMessage, QString originalId) -{ +void Core::MessageHandler::sendMessage(const Shared::Message& data, bool newMessage, QString originalId) { if (data.getOutOfBandUrl().size() == 0 && data.getAttachPath().size() > 0) { pendingCorrectionMessages.insert(std::make_pair(data.getId(), originalId)); prepareUpload(data, newMessage); @@ -308,19 +297,18 @@ void Core::MessageHandler::sendMessage(const Shared::Message& data, bool newMess } } -void Core::MessageHandler::performSending(Shared::Message data, const QString& originalId, bool newMessage) -{ +void Core::MessageHandler::performSending(Shared::Message data, const QString& originalId, bool newMessage) { QString jid = data.getPenPalJid(); QString id = data.getId(); qDebug() << "Sending message with id:" << id; - if (originalId.size() > 0) { + if (originalId.size() > 0) qDebug() << "To replace one with id:" << originalId; - } + RosterItem* ri = acc->rh->getRosterItem(jid); bool sent = false; - if (newMessage && originalId.size() > 0) { + if (newMessage && originalId.size() > 0) newMessage = false; - } + QDateTime sendTime = QDateTime::currentDateTimeUtc(); if (acc->state == Shared::ConnectionState::connected) { QXmppMessage msg(createPacket(data, sendTime, originalId)); @@ -341,22 +329,21 @@ void Core::MessageHandler::performSending(Shared::Message data, const QString& o QMap changes(getChanges(data, sendTime, newMessage, originalId)); QString realId; - if (originalId.size() > 0) { + if (originalId.size() > 0) realId = originalId; - } else { + else realId = id; - } + if (ri != nullptr) { - if (newMessage) { + if (newMessage) ri->appendMessageToArchive(data); - } else { + else ri->changeMessage(realId, changes); - } + if (sent) { pendingStateMessages.insert(std::make_pair(id, jid)); - if (originalId.size() > 0) { + if (originalId.size() > 0) pendingCorrectionMessages.insert(std::make_pair(id, originalId)); - } } else { pendingStateMessages.erase(id); pendingCorrectionMessages.erase(id); @@ -366,25 +353,24 @@ void Core::MessageHandler::performSending(Shared::Message data, const QString& o emit acc->changeMessage(jid, realId, changes); } -QMap Core::MessageHandler::getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const -{ +QMap Core::MessageHandler::getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const { QMap changes; QString oob = data.getOutOfBandUrl(); Shared::Message::State mstate = data.getState(); changes.insert("state", static_cast(mstate)); - if (mstate == Shared::Message::State::error) { + if (mstate == Shared::Message::State::error) changes.insert("errorText", data.getErrorText()); - } - if (oob.size() > 0) { + + if (oob.size() > 0) changes.insert("outOfBandUrl", oob); - } - if (newMessage) { + + if (newMessage) data.setTime(time); - } - if (originalId.size() > 0) { + + if (originalId.size() > 0) changes.insert("body", data.getBody()); - } + changes.insert("stamp", time); //sometimes (when the image is pasted with ctrl+v) @@ -394,22 +380,20 @@ QMap Core::MessageHandler::getChanges(Shared::Message& data, if (attachPath.size() > 0) { QString squawkified = Shared::squawkifyPath(attachPath); changes.insert("attachPath", squawkified); - if (attachPath != squawkified) { + if (attachPath != squawkified) data.setAttachPath(squawkified); - } } return changes; } -QXmppMessage Core::MessageHandler::createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const -{ +QXmppMessage Core::MessageHandler::createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const { QXmppMessage msg(acc->getFullJid(), data.getTo(), data.getBody(), data.getThread()); QString id(data.getId()); - if (originalId.size() > 0) { + if (originalId.size() > 0) msg.setReplaceId(originalId); - } + #if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) msg.setOriginId(id); @@ -423,8 +407,7 @@ QXmppMessage Core::MessageHandler::createPacket(const Shared::Message& data, con return msg; } -void Core::MessageHandler::prepareUpload(const Shared::Message& data, bool newMessage) -{ +void Core::MessageHandler::prepareUpload(const Shared::Message& data, bool newMessage) { if (acc->state == Shared::ConnectionState::connected) { QString jid = data.getPenPalJid(); QString id = data.getId(); @@ -455,9 +438,8 @@ void Core::MessageHandler::prepareUpload(const Shared::Message& data, bool newMe if (file.exists() && file.isReadable()) { pendingStateMessages.insert(std::make_pair(id, jid)); uploadingSlotsQueue.emplace_back(path, id); - if (uploadingSlotsQueue.size() == 1) { + if (uploadingSlotsQueue.size() == 1) acc->um->requestUploadSlot(file); - } } else { handleUploadError(jid, id, "Uploading file no longer exists or your system user has no permission to read it"); qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but the file doesn't exist or is not readable"; @@ -474,8 +456,7 @@ void Core::MessageHandler::prepareUpload(const Shared::Message& data, bool newMe } } -void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot) -{ +void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot) { if (uploadingSlotsQueue.size() == 0) { qDebug() << "HTTP Upload manager of account" << acc->name << "reports about success requesting upload slot, but none was requested"; } else { @@ -485,14 +466,12 @@ void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slo acc->network->uploadFile({acc->name, palJid, mId}, pair.first, slot.putUrl(), slot.getUrl(), slot.putHeaders()); uploadingSlotsQueue.pop_front(); - if (uploadingSlotsQueue.size() > 0) { + if (uploadingSlotsQueue.size() > 0) acc->um->requestUploadSlot(uploadingSlotsQueue.front().first); - } } } -void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request) -{ +void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request) { QString err(request.error().text()); if (uploadingSlotsQueue.size() == 0) { qDebug() << "HTTP Upload manager of account" << acc->name << "reports about an error requesting upload slot, but none was requested"; @@ -503,14 +482,12 @@ void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadReques handleUploadError(pendingStateMessages.at(pair.second), pair.second, err); uploadingSlotsQueue.pop_front(); - if (uploadingSlotsQueue.size() > 0) { + if (uploadingSlotsQueue.size() > 0) acc->um->requestUploadSlot(uploadingSlotsQueue.front().first); - } } } -void Core::MessageHandler::onDownloadFileComplete(const std::list& msgs, const QString& path) -{ +void Core::MessageHandler::onDownloadFileComplete(const std::list& msgs, const QString& path) { QMap cData = { {"attachPath", path} }; @@ -518,27 +495,24 @@ void Core::MessageHandler::onDownloadFileComplete(const std::listgetName()) { RosterItem* cnt = acc->rh->getRosterItem(info.jid); if (cnt != nullptr) { - if (cnt->changeMessage(info.messageId, cData)) { + bool changed = cnt->changeMessage(info.messageId, cData); + if (changed) emit acc->changeMessage(info.jid, info.messageId, cData); - } } } } } -void Core::MessageHandler::onLoadFileError(const std::list& msgs, const QString& text, bool up) -{ +void Core::MessageHandler::onLoadFileError(const std::list& msgs, const QString& text, bool up) { if (up) { for (const Shared::MessageInfo& info : msgs) { - if (info.account == acc->getName()) { + if (info.account == acc->getName()) handleUploadError(info.jid, info.messageId, text); - } } } } -void Core::MessageHandler::handleUploadError(const QString& jid, const QString& messageId, const QString& errorText) -{ +void Core::MessageHandler::handleUploadError(const QString& jid, const QString& messageId, const QString& errorText) { emit acc->uploadFileError(jid, messageId, "Error requesting slot to upload file: " + errorText); pendingStateMessages.erase(messageId); pendingCorrectionMessages.erase(messageId); @@ -548,8 +522,7 @@ void Core::MessageHandler::handleUploadError(const QString& jid, const QString& }); } -void Core::MessageHandler::onUploadFileComplete(const std::list& msgs, const QString& url, const QString& path) -{ +void Core::MessageHandler::onUploadFileComplete(const std::list& msgs, const QString& url, const QString& path) { for (const Shared::MessageInfo& info : msgs) { if (info.account == acc->getName()) { RosterItem* ri = acc->rh->getRosterItem(info.jid); @@ -564,12 +537,11 @@ void Core::MessageHandler::onUploadFileComplete(const std::list allowedToChangeKeys({ "errorText" }); -void Core::MessageHandler::requestChangeMessage(const QString& jid, const QString& messageId, const QMap& data) -{ +void Core::MessageHandler::requestChangeMessage(const QString& jid, const QString& messageId, const QMap& data) { RosterItem* cnt = acc->rh->getRosterItem(jid); if (cnt != nullptr) { bool allSupported = true; @@ -604,8 +575,7 @@ void Core::MessageHandler::requestChangeMessage(const QString& jid, const QStrin } } -void Core::MessageHandler::resendMessage(const QString& jid, const QString& id) -{ +void Core::MessageHandler::resendMessage(const QString& jid, const QString& id) { RosterItem* cnt = acc->rh->getRosterItem(jid); if (cnt != nullptr) { try { diff --git a/core/handlers/omemohandler.cpp b/core/handlers/omemohandler.cpp index 35bca9e..2f9f70e 100644 --- a/core/handlers/omemohandler.cpp +++ b/core/handlers/omemohandler.cpp @@ -213,6 +213,7 @@ void Core::OmemoHandler::onOwnBundlesReceived() { } void Core::OmemoHandler::onOmemoDeviceAdded(const QString& jid, uint32_t id) { + SHARED_UNUSED(id); qDebug() << "OMEMO device added for" << jid; } diff --git a/core/handlers/rosterhandler.cpp b/core/handlers/rosterhandler.cpp index 2e59971..4ce8939 100644 --- a/core/handlers/rosterhandler.cpp +++ b/core/handlers/rosterhandler.cpp @@ -456,6 +456,7 @@ void Core::RosterHandler::removeRoomRequest(const QString& jid) { } void Core::RosterHandler::addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin) { + SHARED_UNUSED(password); QString lcJid = jid.toLower(); std::map::const_iterator cItr = conferences.find(lcJid); if (cItr == conferences.end()) { diff --git a/core/signalcatcher.cpp b/core/signalcatcher.cpp index 046c67e..ef3e14c 100644 --- a/core/signalcatcher.cpp +++ b/core/signalcatcher.cpp @@ -21,6 +21,8 @@ #include #include +#include "shared/defines.h" + int SignalCatcher::sigintFd[2] = {0,0}; SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent): @@ -28,14 +30,10 @@ SignalCatcher::SignalCatcher(QCoreApplication *p_app, 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, &QSocketNotifier::activated, this, &SignalCatcher::handleSigInt); @@ -44,25 +42,25 @@ SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent): SignalCatcher::~SignalCatcher() {} -void SignalCatcher::handleSigInt() -{ +void SignalCatcher::handleSigInt() { snInt->setEnabled(false); char tmp; ssize_t s = ::read(sigintFd[1], &tmp, sizeof(tmp)); + SHARED_UNUSED(s); emit interrupt(); snInt->setEnabled(true); } -void SignalCatcher::intSignalHandler(int unused) -{ +void SignalCatcher::intSignalHandler(int unused) { char a = 1; ssize_t s = ::write(sigintFd[0], &a, sizeof(a)); + SHARED_UNUSED(s); + SHARED_UNUSED(unused); } -int SignalCatcher::setup_unix_signal_handlers() -{ +int SignalCatcher::setup_unix_signal_handlers() { struct sigaction s_int; s_int.sa_handler = SignalCatcher::intSignalHandler; diff --git a/main/application.cpp b/main/application.cpp index edf9fd7..0701a0c 100644 --- a/main/application.cpp +++ b/main/application.cpp @@ -120,8 +120,7 @@ Application::Application(Core::Squawk* p_core): Application::~Application() {} -void Application::quit() -{ +void Application::quit() { if (!nowQuitting) { nowQuitting = true; emit quitting(); @@ -135,32 +134,27 @@ void Application::quit() conversations.clear(); dialogueQueue.quit(); - if (squawk != nullptr) { + if (squawk != nullptr) squawk->close(); - } if (trayIcon != nullptr) { trayIcon->deleteLater(); trayIcon = nullptr; } - if (!destroyingSquawk) { + if (!destroyingSquawk) checkForTheLastWindow(); - } } } -void Application::checkForTheLastWindow() -{ - if (QApplication::topLevelWidgets().size() > 0) { +void Application::checkForTheLastWindow() { + if (QApplication::topLevelWidgets().size() > 0) emit readyToQuit(); - } else { + else connect(qApp, &QApplication::lastWindowClosed, this, &Application::readyToQuit); - } } -void Application::createMainWindow() -{ +void Application::createMainWindow() { if (squawk == nullptr) { squawk = new Squawk(roster); @@ -202,9 +196,8 @@ void Application::createMainWindow() for (const std::list& entry : expandedPaths) { QModelIndex ind = roster.getIndexByPath(entry); - if (ind.isValid()) { + if (ind.isValid()) squawk->expand(ind); - } } connect(squawk, &Squawk::itemExpanded, this, &Application::onItemExpanded); @@ -212,8 +205,7 @@ void Application::createMainWindow() } } -void Application::onSquawkClosing() -{ +void Application::onSquawkClosing() { dialogueQueue.setParentWidnow(nullptr); if (!nowQuitting) { @@ -237,18 +229,15 @@ void Application::onSquawkClosing() void Application::onSquawkDestroyed() { destroyingSquawk = false; - if (nowQuitting) { + if (nowQuitting) checkForTheLastWindow(); - } } -void Application::onChangeTray(bool enabled, bool hide) -{ +void Application::onChangeTray(bool enabled, bool hide) { if (enabled) { if (trayIcon == nullptr) { - if (!hide || squawk == nullptr) { + if (!hide || squawk == nullptr) createTrayIcon(); - } } else { if (hide && squawk != nullptr) { trayIcon->deleteLater(); @@ -261,8 +250,7 @@ void Application::onChangeTray(bool enabled, bool hide) } } -void Application::createTrayIcon() -{ +void Application::createTrayIcon() { trayIcon = new QSystemTrayIcon(); QMenu* trayIconMenu = new QMenu(); @@ -279,8 +267,7 @@ void Application::createTrayIcon() trayIcon->show(); } -void Application::trayClicked(QSystemTrayIcon::ActivationReason reason) -{ +void Application::trayClicked(QSystemTrayIcon::ActivationReason reason) { switch (reason) { case QSystemTrayIcon::Trigger: case QSystemTrayIcon::DoubleClick: @@ -292,8 +279,7 @@ void Application::trayClicked(QSystemTrayIcon::ActivationReason reason) } } -void Application::toggleSquawk() -{ +void Application::toggleSquawk() { QSettings settings; if (squawk == nullptr) { createMainWindow(); @@ -308,34 +294,27 @@ void Application::toggleSquawk() } } -void Application::onItemCollapsed(const QModelIndex& index) -{ +void Application::onItemCollapsed(const QModelIndex& index) { std::list address = roster.getItemPath(index); - if (address.size() > 0) { + if (address.size() > 0) expandedPaths.erase(address); - } } -void Application::onItemExpanded(const QModelIndex& index) -{ +void Application::onItemExpanded(const QModelIndex& index) { std::list address = roster.getItemPath(index); - if (address.size() > 0) { + if (address.size() > 0) expandedPaths.insert(address); - } } -void Application::onAddedElement(const std::list& path) -{ +void Application::onAddedElement(const std::list& path) { if (squawk != nullptr && expandedPaths.count(path) > 0) { QModelIndex index = roster.getIndexByPath(path); - if (index.isValid()) { + if (index.isValid()) squawk->expand(index); - } } } -void Application::notify(const QString& account, const Shared::Message& msg) -{ +void Application::notify(const QString& account, const Shared::Message& msg) { QString jid = msg.getPenPalJid(); QString name = QString(roster.getContactName(account, jid)); QString path = QString(roster.getContactIconPath(account, jid, msg.getPenPalResource())); @@ -344,16 +323,15 @@ void Application::notify(const QString& account, const Shared::Message& msg) uint32_t notificationId = qHash(msg.getId()); args << notificationId; - if (path.size() > 0) { + if (path.size() > 0) args << path; - } else { + else args << QString("mail-message"); //TODO should here better be unknown user icon? - } - if (msg.getType() == Shared::Message::groupChat) { + + if (msg.getType() == Shared::Message::groupChat) args << msg.getFromResource() + tr(" from ") + name; - } else { + else args << name; - } QString body(msg.getBody()); QString oob(msg.getOutOfBandUrl()); @@ -378,13 +356,11 @@ void Application::notify(const QString& account, const Shared::Message& msg) storage.insert(std::make_pair(notificationId, std::make_pair(Models::Roster::ElId(account, name), msg.getId()))); - if (squawk != nullptr) { + if (squawk != nullptr) QApplication::alert(squawk); - } } -void Application::onNotificationClosed(quint32 id, quint32 reason) -{ +void Application::onNotificationClosed(quint32 id, quint32 reason) { Notifications::const_iterator itr = storage.find(id); if (itr != storage.end()) { if (reason == 2) { //dissmissed by user (https://specifications.freedesktop.org/notification-spec/latest/ar01s09.html) @@ -397,21 +373,18 @@ void Application::onNotificationClosed(quint32 id, quint32 reason) } } -void Application::onNotificationInvoked(quint32 id, const QString& action) -{ +void Application::onNotificationInvoked(quint32 id, const QString& action) { qDebug() << "Notification" << id << action << "request"; Notifications::const_iterator itr = storage.find(id); if (itr != storage.end()) { - if (action == "markAsRead") { + if (action == "markAsRead") roster.markMessageAsRead(itr->second.first, itr->second.second); - } else if (action == "openConversation") { + else if (action == "openConversation") focusConversation(itr->second.first, "", itr->second.second); - } } } -void Application::unreadMessagesCountChanged(int count) -{ +void Application::unreadMessagesCountChanged(int count) { QDBusMessage signal = QDBusMessage::createSignal("/", "com.canonical.Unity.LauncherEntry", "Update"); signal << qApp->desktopFileName() + QLatin1String(".desktop"); signal << QVariantMap ({ @@ -421,54 +394,49 @@ void Application::unreadMessagesCountChanged(int count) QDBusConnection::sessionBus().send(signal); } -void Application::focusConversation(const Models::Roster::ElId& id, const QString& resource, const QString& messageId) -{ +void Application::focusConversation(const Models::Roster::ElId& id, const QString& resource, const QString& messageId) { if (squawk != nullptr) { if (squawk->currentConversationId() != id) { QModelIndex index = roster.getContactIndex(id.account, id.name, resource); squawk->select(index); } - if (squawk->isMinimized()) { + if (squawk->isMinimized()) squawk->showNormal(); - } else { + else squawk->show(); - } + squawk->raise(); squawk->activateWindow(); } else { openConversation(id, resource); } + SHARED_UNUSED(messageId); //TODO focus messageId; } -void Application::setState(Shared::Availability p_availability) -{ +void Application::setState(Shared::Availability p_availability) { if (availability != p_availability) { availability = p_availability; emit changeState(availability); } } -void Application::stateChanged(Shared::Availability state) -{ +void Application::stateChanged(Shared::Availability state) { availability = state; - if (squawk != nullptr) { + if (squawk != nullptr) squawk->stateChanged(state); - } } -void Application::readSettings() -{ +void Application::readSettings() { QSettings settings; settings.beginGroup("ui"); int avail; - if (settings.contains("availability")) { + if (settings.contains("availability")) avail = settings.value("availability").toInt(); - } else { + else avail = static_cast(Shared::Availability::online); - } settings.beginGroup("roster"); QStringList entries = settings.allKeys(); @@ -486,13 +454,11 @@ void Application::readSettings() setState(Shared::Global::fromInt(avail)); createMainWindow(); - if (settings.value("tray", false).toBool() && !settings.value("hideTray", false).toBool()) { + if (settings.value("tray", false).toBool() && !settings.value("hideTray", false).toBool()) createTrayIcon(); - } } -void Application::writeSettings() -{ +void Application::writeSettings() { QSettings settings; settings.beginGroup("ui"); settings.setValue("availability", static_cast(availability)); @@ -501,9 +467,9 @@ void Application::writeSettings() settings.beginGroup("roster"); for (const std::list& address : expandedPaths) { QString path = ""; - for (const QString& hop : address) { + for (const QString& hop : address) path += hop + "/"; - } + path += "expanded"; settings.setValue(path, true); } @@ -513,54 +479,49 @@ void Application::writeSettings() } void Application::requestPassword(const QString& account, bool authenticationError) { - if (authenticationError) { + if (authenticationError) dialogueQueue.addAction(account, DialogQueue::askCredentials); - } else { + else dialogueQueue.addAction(account, DialogQueue::askPassword); - } - } -void Application::onConversationClosed() -{ + +void Application::onConversationClosed() { Conversation* conv = static_cast(sender()); Models::Roster::ElId id(conv->getAccount(), conv->getJid()); Conversations::const_iterator itr = conversations.find(id); - if (itr != conversations.end()) { + if (itr != conversations.end()) conversations.erase(itr); - } + if (conv->isMuc) { Room* room = static_cast(conv); - if (!room->autoJoined()) { + if (!room->autoJoined()) emit setRoomJoined(id.account, id.name, false); - } } } -void Application::changeSubscription(const Models::Roster::ElId& id, bool subscribe) -{ +void Application::changeSubscription(const Models::Roster::ElId& id, bool subscribe) { Models::Item::Type type = roster.getContactType(id); switch (type) { case Models::Item::contact: - if (subscribe) { + if (subscribe) emit subscribeContact(id.account, id.name, ""); - } else { + else emit unsubscribeContact(id.account, id.name, ""); - } + break; case Models::Item::room: setRoomAutoJoin(id.account, id.name, subscribe); - if (!isConverstationOpened(id)) { + if (!isConverstationOpened(id)) emit setRoomJoined(id.account, id.name, subscribe); - } + break; default: break; } } -void Application::subscribeConversation(Conversation* conv) -{ +void Application::subscribeConversation(Conversation* conv) { connect(conv, &Conversation::destroyed, this, &Application::onConversationClosed); connect(conv, &Conversation::sendMessage, this, &Application::onConversationMessage); connect(conv, &Conversation::replaceMessage, this, &Application::onConversationReplaceMessage); @@ -568,8 +529,7 @@ void Application::subscribeConversation(Conversation* conv) connect(conv, &Conversation::notifyableMessage, this, &Application::notify); } -void Application::openConversation(const Models::Roster::ElId& id, const QString& resource) -{ +void Application::openConversation(const Models::Roster::ElId& id, const QString& resource) { Conversations::const_iterator itr = conversations.find(id); Models::Account* acc = roster.getAccount(id.account); Conversation* conv = nullptr; @@ -583,9 +543,8 @@ void Application::openConversation(const Models::Roster::ElId& id, const QString created = true; Models::Room* room = static_cast(el); conv = new Room(acc, room); - if (!room->getJoined()) { + if (!room->getJoined()) emit setRoomJoined(id.account, id.name, true); - } } else if (el->type == Models::Item::contact) { created = true; conv = new Chat(acc, static_cast(el)); @@ -604,14 +563,12 @@ void Application::openConversation(const Models::Roster::ElId& id, const QString conv->raise(); conv->activateWindow(); - if (resource.size() > 0) { + if (resource.size() > 0) conv->setPalResource(resource); - } } } -void Application::onConversationMessage(const Shared::Message& msg) -{ +void Application::onConversationMessage(const Shared::Message& msg) { Conversation* conv = static_cast(sender()); QString acc = conv->getAccount(); @@ -619,8 +576,7 @@ void Application::onConversationMessage(const Shared::Message& msg) emit sendMessage(acc, msg); } -void Application::onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg) -{ +void Application::onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg) { Conversation* conv = static_cast(sender()); QString acc = conv->getAccount(); @@ -630,8 +586,7 @@ void Application::onConversationReplaceMessage(const QString& originalId, const emit replaceMessage(acc, originalId, msg); } -void Application::onConversationResend(const QString& id) -{ +void Application::onConversationResend(const QString& id) { Conversation* conv = static_cast(sender()); QString acc = conv->getAccount(); QString jid = conv->getJid(); @@ -649,8 +604,7 @@ void Application::onSquawkOpenedConversation() { } } -void Application::removeAccount(const QString& account) -{ +void Application::removeAccount(const QString& account) { Conversations::const_iterator itr = conversations.begin(); while (itr != conversations.end()) { if (itr->first.account == account) { @@ -665,23 +619,20 @@ void Application::removeAccount(const QString& account) } } - if (squawk != nullptr && squawk->currentConversationId().account == account) { + if (squawk != nullptr && squawk->currentConversationId().account == account) squawk->closeCurrentConversation(); - } roster.removeAccount(account); } -void Application::changeAccount(const QString& account, const QMap& data) -{ +void Application::changeAccount(const QString& account, const QMap& data) { for (QMap::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) { QString attr = itr.key(); roster.updateAccount(account, attr, *itr); } } -void Application::addGroup(const QString& account, const QString& name) -{ +void Application::addGroup(const QString& account, const QString& name) { roster.addGroup(account, name); if (squawk != nullptr) { @@ -692,9 +643,8 @@ void Application::addGroup(const QString& account, const QString& name) if (settings.value("expanded", false).toBool()) { QModelIndex ind = roster.getAccountIndex(account); squawk->expand(ind); - if (settings.value(name + "/expanded", false).toBool()) { + if (settings.value(name + "/expanded", false).toBool()) squawk->expand(roster.getGroupIndex(account, name)); - } } settings.endGroup(); settings.endGroup(); diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index 4bcc12f..4ba24ea 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -37,6 +37,7 @@ set(HEADER_FILES info.h clientid.h trustsummary.h + defines.h ) target_sources(squawk PRIVATE diff --git a/shared/defines.h b/shared/defines.h new file mode 100644 index 0000000..227a714 --- /dev/null +++ b/shared/defines.h @@ -0,0 +1,24 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 SHARED_DEFINES_H +#define SHARED_DEFINES_H + +#define SHARED_UNUSED(x) (void)(x) + +#endif diff --git a/shared/identity.cpp b/shared/identity.cpp index a0a4f0a..ef616d8 100644 --- a/shared/identity.cpp +++ b/shared/identity.cpp @@ -121,6 +121,8 @@ QDataStream & Shared::Identity::operator >> (QDataStream& stream) const { stream << type; stream << language; stream << name; + + return stream; } QDataStream & Shared::Identity::operator << (QDataStream& stream) { @@ -128,6 +130,8 @@ QDataStream & Shared::Identity::operator << (QDataStream& stream) { stream >> type; stream >> language; stream >> name; + + return stream; } QDataStream & operator >> (QDataStream& stream, Shared::Identity& identity) { diff --git a/shared/shared.h b/shared/shared.h index 68e5c8d..ba9a2ed 100644 --- a/shared/shared.h +++ b/shared/shared.h @@ -19,6 +19,7 @@ #ifndef SHARED_H #define SHARED_H +#include "defines.h" #include "enums.h" #include "global.h" #include "icons.h" diff --git a/ui/models/accounts.cpp b/ui/models/accounts.cpp index 8a9e5d9..e401b3a 100644 --- a/ui/models/accounts.cpp +++ b/ui/models/accounts.cpp @@ -18,6 +18,7 @@ #include "accounts.h" #include "shared/icons.h" +#include "shared/defines.h" #include #include @@ -26,32 +27,24 @@ std::deque Models::Accounts::columns = {"Name", "Server", "State", "Err Models::Accounts::Accounts(QObject* parent): QAbstractTableModel(parent), - accs() -{ + accs() {} -} +Models::Accounts::~Accounts() {} -Models::Accounts::~Accounts() -{ - -} - -QVariant Models::Accounts::data (const QModelIndex& index, int role) const -{ +QVariant Models::Accounts::data (const QModelIndex& index, int role) const { QVariant answer; switch (role) { case Qt::DisplayRole: answer = accs[index.row()]->data(index.column()); break; case Qt::DecorationRole: - if (index.column() == 2) { + if (index.column() == 2) answer = Shared::connectionStateIcon(accs[index.row()]->getState()); - } break; case Qt::ForegroundRole: - if (!accs[index.row()]->getActive()) { + if (!accs[index.row()]->getActive()) answer = qApp->palette().brush(QPalette::Disabled, QPalette::Text); - } + break; default: break; } @@ -59,53 +52,46 @@ QVariant Models::Accounts::data (const QModelIndex& index, int role) const return answer; } -int Models::Accounts::columnCount ( const QModelIndex& parent ) const -{ +int Models::Accounts::columnCount (const QModelIndex& parent) const { + SHARED_UNUSED(parent); return columns.size(); } -int Models::Accounts::rowCount ( const QModelIndex& parent ) const -{ +int Models::Accounts::rowCount (const QModelIndex& parent) const { + SHARED_UNUSED(parent); return accs.size(); } -unsigned int Models::Accounts::size() const -{ +unsigned int Models::Accounts::size() const { return rowCount(QModelIndex()); } -unsigned int Models::Accounts::activeSize() const -{ +unsigned int Models::Accounts::activeSize() const { unsigned int size = 0; for (const Models::Account* acc : accs) { - if (acc->getActive()) { + if (acc->getActive()) ++size; - } } return size; } -QVariant Models::Accounts::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { +QVariant Models::Accounts::headerData(int section, Qt::Orientation orientation, int role) const { + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) return tr(columns[section].toLatin1()); - } + return QVariant(); } -void Models::Accounts::addAccount(Account* account) -{ +void Models::Accounts::addAccount(Account* account) { beginInsertRows(QModelIndex(), accs.size(), accs.size()); - int index = 0; std::deque::const_iterator before = accs.begin(); while (before != accs.end()) { Account* bfr = *before; - if (bfr->getDisplayedName() > account->getDisplayedName()) { + if (bfr->getDisplayedName() > account->getDisplayedName()) break; - } - index++; + before++; } @@ -117,25 +103,23 @@ void Models::Accounts::addAccount(Account* account) emit changed(); } -void Models::Accounts::onAccountChanged(Item* item, int row, int col) -{ - if (row < 0) { +void Models::Accounts::onAccountChanged(Item* item, int row, int col) { + if (row < 0) return; - } + if (static_cast::size_type>(row) < accs.size()) { Account* acc = getAccount(row); - if (item != acc) { + if (item != acc) return; //it means the signal is emitted by one of accounts' children, not exactly him, this model has no interest in that - } if (col == 0) { int newRow = 0; std::deque::const_iterator before = accs.begin(); while (before != accs.end()) { Item* bfr = *before; - if (bfr->getDisplayedName() > item->getDisplayedName()) { + if (bfr->getDisplayedName() > item->getDisplayedName()) break; - } + newRow++; before++; } @@ -152,20 +136,18 @@ void Models::Accounts::onAccountChanged(Item* item, int row, int col) } } - if (col < columnCount(QModelIndex())) { + if (col < columnCount(QModelIndex())) emit dataChanged(createIndex(row, col), createIndex(row, col)); - } + emit changed(); } } -Models::Account * Models::Accounts::getAccount(int index) -{ +Models::Account * Models::Accounts::getAccount(int index) { return accs[index]; } -void Models::Accounts::removeAccount(int index) -{ +void Models::Accounts::removeAccount(int index) { Account* account = accs[index]; beginRemoveRows(QModelIndex(), index, index); disconnect(account, &Account::childChanged, this, &Accounts::onAccountChanged); @@ -175,14 +157,13 @@ void Models::Accounts::removeAccount(int index) emit sizeChanged(accs.size()); } -std::deque Models::Accounts::getActiveNames() const -{ +std::deque Models::Accounts::getActiveNames() const { std::deque res; for (const Models::Account* acc : accs) { - if (acc->getActive()) { + if (acc->getActive()) res.push_back(acc->getName()); - } + } return res; diff --git a/ui/models/info/emails.cpp b/ui/models/info/emails.cpp index 4bb8a17..694c0c9 100644 --- a/ui/models/info/emails.cpp +++ b/ui/models/info/emails.cpp @@ -19,6 +19,7 @@ #include "emails.h" #include "shared/icons.h" +#include "shared/defines.h" #include Models::EMails::EMails(bool p_edit, QObject* parent): @@ -27,10 +28,15 @@ Models::EMails::EMails(bool p_edit, QObject* parent): deque() {} int Models::EMails::columnCount(const QModelIndex& parent) const { - return 3;} + SHARED_UNUSED(parent); + return 3; +} int Models::EMails::rowCount(const QModelIndex& parent) const { - return deque.size();} + SHARED_UNUSED(parent); + return deque.size(); + +} void Models::EMails::revertPreferred(quint32 row) { setData(createIndex(row, 2), !isPreferred(row));} @@ -83,9 +89,9 @@ QVariant Models::EMails::data(const QModelIndex& index, int role) const { Qt::ItemFlags Models::EMails::flags(const QModelIndex& index) const { Qt::ItemFlags f = QAbstractTableModel::flags(index); - if (edit && index.column() != 2) { + if (edit && index.column() != 2) f = Qt::ItemIsEditable | f; - } + return f; } @@ -114,9 +120,9 @@ bool Models::EMails::setData(const QModelIndex& index, const QVariant& value, in return true; case 1: { quint8 newRole = value.toUInt(); - if (newRole > Shared::VCard::Email::work) { + if (newRole > Shared::VCard::Email::work) return false; - } + item.role = static_cast(newRole); return true; } @@ -160,19 +166,17 @@ QModelIndex Models::EMails::addNewEmptyLine() { } bool Models::EMails::isPreferred(quint32 row) const { - if (row < deque.size()) { + if (row < deque.size()) return deque[row].prefered; - } else { + else return false; - } } void Models::EMails::removeLines(quint32 index, quint32 count) { if (index < deque.size()) { quint32 maxCount = deque.size() - index; - if (count > maxCount) { + if (count > maxCount) count = maxCount; - } if (count > 0) { beginRemoveRows(QModelIndex(), index, index + count - 1); @@ -185,21 +189,19 @@ void Models::EMails::removeLines(quint32 index, quint32 count) { } void Models::EMails::getEmails(std::deque& emails) const { - for (const Shared::VCard::Email& my : deque) { + for (const Shared::VCard::Email& my : deque) emails.emplace_back(my); - } } void Models::EMails::setEmails(const std::deque& emails) { - if (deque.size() > 0) { + if (deque.size() > 0) removeLines(0, deque.size()); - } if (emails.size() > 0) { beginInsertRows(QModelIndex(), 0, emails.size() - 1); - for (const Shared::VCard::Email& comming : emails) { + for (const Shared::VCard::Email& comming : emails) deque.emplace_back(comming); - } + endInsertRows(); } } diff --git a/ui/models/info/omemo/keys.cpp b/ui/models/info/omemo/keys.cpp index e4dc8d2..5d957b1 100644 --- a/ui/models/info/omemo/keys.cpp +++ b/ui/models/info/omemo/keys.cpp @@ -16,6 +16,8 @@ #include "keys.h" +#include "shared/defines.h" + const QHash Models::Keys::roles = { {Label, "label"}, {FingerPrint, "fingerPrint"}, @@ -28,13 +30,11 @@ Models::Keys::Keys(QObject* parent): modified() {} Models::Keys::~Keys() { - for (Shared::KeyInfo* key : keys) { + for (Shared::KeyInfo* key : keys) delete key; - } - for (std::pair& pair: modified) { + for (std::pair& pair: modified) delete pair.second; - } } std::deque Models::Keys::modifiedKeys() const { @@ -92,15 +92,15 @@ QVariant Models::Keys::data(const QModelIndex& index, int role) const { } int Models::Keys::rowCount(const QModelIndex& parent) const { + SHARED_UNUSED(parent); return keys.size(); } QHash Models::Keys::roleNames() const {return roles;} QModelIndex Models::Keys::index(int row, int column, const QModelIndex& parent) const { - if (!hasIndex(row, column, parent)) { + if (!hasIndex(row, column, parent)) return QModelIndex(); - } return createIndex(row, column, keys[row]); } diff --git a/ui/models/info/phones.cpp b/ui/models/info/phones.cpp index 99c52c2..2b6523a 100644 --- a/ui/models/info/phones.cpp +++ b/ui/models/info/phones.cpp @@ -19,6 +19,7 @@ #include "phones.h" #include "shared/icons.h" +#include "shared/defines.h" #include Models::Phones::Phones(bool p_edit, QObject* parent): @@ -27,10 +28,14 @@ Models::Phones::Phones(bool p_edit, QObject* parent): deque() {} int Models::Phones::columnCount(const QModelIndex& parent) const { - return 4;} + SHARED_UNUSED(parent); + return 4; +} int Models::Phones::rowCount(const QModelIndex& parent) const { - return deque.size();} + SHARED_UNUSED(parent); + return deque.size(); +} void Models::Phones::revertPreferred(quint32 row) { setData(createIndex(row, 3), !isPreferred(row)); @@ -77,9 +82,9 @@ QVariant Models::Phones::data(const QModelIndex& index, int role) const { case Qt::DisplayRole: return QVariant(); case Qt::DecorationRole: - if (deque[index.row()].prefered) { + if (deque[index.row()].prefered) return Shared::icon("favorite", false); - } + return QVariant(); default: return QVariant(); @@ -101,9 +106,9 @@ QModelIndex Models::Phones::addNewEmptyLine() { Qt::ItemFlags Models::Phones::flags(const QModelIndex& index) const { Qt::ItemFlags f = QAbstractTableModel::flags(index); - if (edit && index.column() != 3) { + if (edit && index.column() != 3) f = Qt::ItemIsEditable | f; - } + return f; } @@ -139,25 +144,22 @@ bool Models::Phones::dropPrefered() { } void Models::Phones::getPhones(std::deque& phones) const { - for (const Shared::VCard::Phone& my : deque) { + for (const Shared::VCard::Phone& my : deque) phones.emplace_back(my); - } } bool Models::Phones::isPreferred(quint32 row) const { - if (row < deque.size()) { + if (row < deque.size()) return deque[row].prefered; - } else { + else return false; - } } void Models::Phones::removeLines(quint32 index, quint32 count) { if (index < deque.size()) { quint32 maxCount = deque.size() - index; - if (count > maxCount) { + if (count > maxCount) count = maxCount; - } if (count > 0) { beginRemoveRows(QModelIndex(), index, index + count - 1); @@ -178,17 +180,17 @@ bool Models::Phones::setData(const QModelIndex& index, const QVariant& value, in return true; case 1: { quint8 newRole = value.toUInt(); - if (newRole > Shared::VCard::Phone::work) { + if (newRole > Shared::VCard::Phone::work) return false; - } + item.role = static_cast(newRole); return true; } case 2: { quint8 newType = value.toUInt(); - if (newType > Shared::VCard::Phone::other) { + if (newType > Shared::VCard::Phone::other) return false; - } + item.type = static_cast(newType); return true; } @@ -209,15 +211,15 @@ bool Models::Phones::setData(const QModelIndex& index, const QVariant& value, in } void Models::Phones::setPhones(const std::deque& phones) { - if (deque.size() > 0) { + if (deque.size() > 0) removeLines(0, deque.size()); - } + if (phones.size() > 0) { beginInsertRows(QModelIndex(), 0, phones.size() - 1); - for (const Shared::VCard::Phone& comming : phones) { + for (const Shared::VCard::Phone& comming : phones) deque.emplace_back(comming); - } + endInsertRows(); } } diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index 43717f8..0f35baa 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -21,6 +21,8 @@ #include #include +#include "shared/defines.h" + Models::Roster::Roster(QObject* parent): QAbstractItemModel(parent), accountsModel(new Accounts()), @@ -39,14 +41,12 @@ Models::Roster::Roster(QObject* parent): connect(root, &Item::childMoved, this, &Roster::onChildMoved); } -Models::Roster::~Roster() -{ +Models::Roster::~Roster() { delete accountsModel; delete root; } -void Models::Roster::addAccount(const QMap& data) -{ +void Models::Roster::addAccount(const QMap& data) { Account* acc = new Account(data); connect(acc, &Account::reconnected, this, &Roster::onAccountReconnected); root->appendChild(acc); @@ -56,24 +56,21 @@ void Models::Roster::addAccount(const QMap& data) emit addedElement({acc->getId()}); } -QVariant Models::Roster::data (const QModelIndex& index, int role) const -{ - if (!index.isValid()) { +QVariant Models::Roster::data (const QModelIndex& index, int role) const { + if (!index.isValid()) return QVariant(); - } QVariant result; Item *item = static_cast(index.internalPointer()); - if (item->type == Item::reference) { + if (item->type == Item::reference) item = static_cast(item)->dereference(); - } + switch (role) { - case Qt::DisplayRole: - { - if (index.column() != 0) { + case Qt::DisplayRole: { + if (index.column() != 0) break; - } + switch (item->type) { case Item::group: { Group* gr = static_cast(item); @@ -81,9 +78,9 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const str += gr->getName(); unsigned int amount = gr->getUnreadMessages(); - if (amount > 0) { + if (amount > 0) str += QString(" (") + tr("New messages") + ")"; - } + result = str; } @@ -104,9 +101,8 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const } else if (col == 1) { QString path = acc->getAvatarPath(); - if (path.size() > 0) { + if (path.size() > 0) result = QIcon(path); - } } } break; @@ -116,16 +112,15 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const if (col == 0) { result = contact->getStatusIcon(false); } else if (col == 1) { - if (contact->getAvatarState() != Shared::Avatar::empty) { + if (contact->getAvatarState() != Shared::Avatar::empty) result = QIcon(contact->getAvatarPath()); - } } } break; case Item::presence: { - if (index.column() != 0) { + if (index.column() != 0) break; - } + Presence* presence = static_cast(item); result = presence->getStatusIcon(false); } @@ -138,9 +133,8 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const } else if (col == 1) { QString path = room->getAvatarPath(); - if (path.size() > 0) { + if (path.size() > 0) result = QIcon(path); - } } } break; @@ -151,14 +145,11 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const result = p->getStatusIcon(false); } else if (col == 1) { QString path = p->getAvatarPath(); - if (path.size() > 0) { + if (path.size() > 0) result = QIcon(path); - } } - if (index.column() != 0) { + if (index.column() != 0) break; - } - } break; default: @@ -194,9 +185,9 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const Contact* contact = static_cast(item); QString str(""); int mc = contact->getMessagesCount(); - if (mc > 0) { + if (mc > 0) str += QString(tr("New messages: ")) + std::to_string(mc).c_str() + "\n"; - } + str += tr("Jabber ID: ") + contact->getJid() + "\n"; Shared::SubscriptionState ss = contact->getState(); if (ss == Shared::SubscriptionState::both || ss == Shared::SubscriptionState::to) { @@ -204,9 +195,8 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const str += tr("Availability: ") + Shared::Global::getName(av); if (av != Shared::Availability::offline) { QString s = contact->getStatus(); - if (s.size() > 0) { + if (s.size() > 0) str += "\n" + tr("Status: ") + s; - } } str += "\n" + tr("Subscription: ") + Shared::Global::getName(ss); } else { @@ -222,9 +212,9 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const Shared::Availability av = contact->getAvailability(); str += tr("Availability: ") + Shared::Global::getName(av); QString s = contact->getStatus(); - if (s.size() > 0) { + if (s.size() > 0) str += "\n" + tr("Status: ") + s; - } + str += "\n" + tr("Client: ") + contact->getClientNode(); result = str; @@ -236,9 +226,8 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const Shared::Availability av = p->getAvailability(); str += tr("Availability: ") + Shared::Global::getName(av) + "\n"; QString s = p->getStatus(); - if (s.size() > 0) { + if (s.size() > 0) str += tr("Status: ") + s + "\n"; - } str += tr("Affiliation: ") + Shared::Global::getName(p->getAffiliation()) + "\n"; str += tr("Role: ") + Shared::Global::getName(p->getRole()) + "\n"; @@ -251,9 +240,9 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const Group* gr = static_cast(item); unsigned int count = gr->getUnreadMessages(); QString str(""); - if (count > 0) { + if (count > 0) str += tr("New messages: ") + std::to_string(count).c_str() + "\n"; - } + str += tr("Online contacts: ") + std::to_string(gr->getOnlineContacts()).c_str() + "\n"; str += tr("Total contacts: ") + std::to_string(gr->childCount()).c_str(); result = str; @@ -263,15 +252,14 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const Room* rm = static_cast(item); unsigned int count = rm->getMessagesCount(); QString str(""); - if (count > 0) { + if (count > 0) str += tr("New messages: ") + std::to_string(count).c_str() + "\n"; - } str += tr("Jabber ID: ") + rm->getJid() + "\n"; str += tr("Subscription: ") + rm->getStatusText(); - if (rm->getJoined()) { + if (rm->getJoined()) str += QString("\n") + tr("Members: ") + std::to_string(rm->childCount()).c_str(); - } + result = str; } break; @@ -284,9 +272,8 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const switch (item->type) { case Item::account: { Account* acc = static_cast(item); - if (!acc->getActive()) { + if (!acc->getActive()) result = qApp->palette().brush(QPalette::Disabled, QPalette::Text); - } } break; default: @@ -299,17 +286,14 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const return result; } -int Models::Roster::columnCount (const QModelIndex& parent) const -{ - if (parent.isValid()) { +int Models::Roster::columnCount (const QModelIndex& parent) const { + if (parent.isValid()) return static_cast(parent.internalPointer())->columnCount(); - } else { + else return root->columnCount(); - } } -void Models::Roster::updateAccount(const QString& account, const QString& field, const QVariant& value) -{ +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; @@ -317,75 +301,63 @@ void Models::Roster::updateAccount(const QString& account, const QString& field, } } - -Qt::ItemFlags Models::Roster::flags(const QModelIndex& index) const -{ - if (!index.isValid()) { +Qt::ItemFlags Models::Roster::flags(const QModelIndex& index) const { + if (!index.isValid()) return Qt::ItemFlags(); - } return QAbstractItemModel::flags(index); } - -int Models::Roster::rowCount (const QModelIndex& parent) const -{ +int Models::Roster::rowCount (const QModelIndex& parent) const{ Item *parentItem; - if (!parent.isValid()) { + if (!parent.isValid()) parentItem = root; - } else { + else parentItem = static_cast(parent.internalPointer()); - } return parentItem->childCount(); } -QVariant Models::Roster::headerData(int section, Qt::Orientation orientation, int role) const -{ +QVariant Models::Roster::headerData(int section, Qt::Orientation orientation, int role) const { + SHARED_UNUSED(section); + SHARED_UNUSED(orientation); + SHARED_UNUSED(role); + return QVariant(); } -QModelIndex Models::Roster::parent (const QModelIndex& child) const -{ - if (!child.isValid()) { +QModelIndex Models::Roster::parent (const QModelIndex& child) const { + if (!child.isValid()) return QModelIndex(); - } Item *childItem = static_cast(child.internalPointer()); - if (childItem == root) { + if (childItem == root) return QModelIndex(); - } Item *parentItem = childItem->parentItem(); - - if (parentItem == root) { + if (parentItem == root) return createIndex(0, 0, parentItem); - } return createIndex(parentItem->row(), 0, parentItem); } -QModelIndex Models::Roster::index (int row, int column, const QModelIndex& parent) const -{ - if (!hasIndex(row, column, parent)) { +QModelIndex Models::Roster::index (int row, int column, const QModelIndex& parent) const { + if (!hasIndex(row, column, parent)) return QModelIndex(); - } Item *parentItem; - if (!parent.isValid()) { + if (!parent.isValid()) parentItem = root; - } else { + else parentItem = static_cast(parent.internalPointer()); - } Item *childItem = parentItem->child(row); - if (childItem) { + if (childItem) return createIndex(row, column, childItem); - } else { + else return QModelIndex(); - } } Models::Roster::ElId::ElId(const QString& p_account, const QString& p_name): @@ -393,27 +365,22 @@ Models::Roster::ElId::ElId(const QString& p_account, const QString& p_name): name(p_name) {} -bool Models::Roster::ElId::operator <(const Models::Roster::ElId& other) const -{ - if (account == other.account) { +bool Models::Roster::ElId::operator <(const Models::Roster::ElId& other) const { + if (account == other.account) return name < other.name; - } else { + else return account < other.account; - } } -bool Models::Roster::ElId::operator!=(const Models::Roster::ElId& other) const -{ +bool Models::Roster::ElId::operator!=(const Models::Roster::ElId& other) const { return !(operator == (other)); } -bool Models::Roster::ElId::operator==(const Models::Roster::ElId& other) const -{ +bool Models::Roster::ElId::operator==(const Models::Roster::ElId& other) const { return (account == other.account) && (name == other.name); } -void Models::Roster::onAccountDataChanged(const QModelIndex& tl, const QModelIndex& br, const QVector& roles) -{ +void Models::Roster::onAccountDataChanged(const QModelIndex& tl, const QModelIndex& br, const QVector& roles) { if (tl.column() == 0) { emit dataChanged(tl, br, roles); } else if (tl.column() == 2) { @@ -423,8 +390,7 @@ void Models::Roster::onAccountDataChanged(const QModelIndex& tl, const QModelInd } } -void Models::Roster::addGroup(const QString& account, const QString& name) -{ +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()) { @@ -438,15 +404,13 @@ void Models::Roster::addGroup(const QString& account, const QString& 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"; } } -void Models::Roster::addContact(const QString& account, const QString& jid, const QString& group, const QMap& data) -{ +void Models::Roster::addContact(const QString& account, const QString& jid, const QString& group, const QMap& data) { Item* parent; Account* acc; Contact* contact; @@ -513,16 +477,15 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons } path.push_back(contact->getId()); - if (ref == 0) { + if (ref == 0) ref = new Reference(contact); - } + parent->appendChild(ref); emit addedElement(path); } -void Models::Roster::removeGroup(const QString& account, const QString& name) -{ +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()) { @@ -541,11 +504,10 @@ void Models::Roster::removeGroup(const QString& account, const QString& name) item->removeChild(0); Contact* cont = static_cast(ref->dereference()); - if (cont->referencesCount() == 1) { + if (cont->referencesCount() == 1) toInsert.push_back(ref); - } else { + else delete ref; - } } if (toInsert.size() > 0) { @@ -559,8 +521,7 @@ void Models::Roster::removeGroup(const QString& account, const QString& name) groups.erase(gItr); } -void Models::Roster::changeContact(const QString& account, const QString& jid, const QMap& data) -{ +void Models::Roster::changeContact(const QString& account, const QString& jid, const QMap& data) { Element* el = getElement(ElId(account, jid)); if (el != nullptr) { for (QMap::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) { @@ -569,18 +530,15 @@ void Models::Roster::changeContact(const QString& account, const QString& jid, c } } -void Models::Roster::changeMessage(const QString& account, const QString& jid, const QString& id, const QMap& data) -{ +void Models::Roster::changeMessage(const QString& account, const QString& jid, const QString& id, const QMap& data) { Element* el = getElement(ElId(account, jid)); - if (el != nullptr) { + if (el != nullptr) el->changeMessage(id, data); - } else { + else qDebug() << "A request to change a message of the contact " << jid << " in the account " << account << " but it wasn't found"; - } } -void Models::Roster::removeContact(const QString& account, const QString& jid) -{ +void Models::Roster::removeContact(const QString& account, const QString& jid) { ElId id(account, jid); std::map::iterator itr = contacts.find(id); @@ -592,21 +550,18 @@ void Models::Roster::removeContact(const QString& account, const QString& jid) std::set toRemove; for (std::pair pair : groups) { - if (pair.second->childCount() == 0) { + if (pair.second->childCount() == 0) toRemove.insert(pair.first); - } } - for (const ElId& elId : toRemove) { + for (const ElId& elId : toRemove) removeGroup(elId.account, elId.name); - } } else { qDebug() << "An attempt to remove contact " << jid << " from account " << account <<" which doesn't exist there, skipping"; } } -void Models::Roster::removeContact(const QString& account, const QString& jid, const QString& group) -{ +void Models::Roster::removeContact(const QString& account, const QString& jid, const QString& group) { ElId contactId(account, jid); ElId groupId(account, group); @@ -639,20 +594,18 @@ void Models::Roster::removeContact(const QString& account, const QString& jid, c } else { delete ref; } - if (gr->childCount() == 0) { + if (gr->childCount() == 0) removeGroup(account, group); - } } -void Models::Roster::onChildChanged(Models::Item* item, int row, int col) -{ +void Models::Roster::onChildChanged(Models::Item* item, int row, int col) { + SHARED_UNUSED(col); QModelIndex index = createIndex(row, 0, item); QModelIndex index2 = createIndex(row, 1, item); emit dataChanged(index, index2); } -void Models::Roster::onChildIsAboutToBeInserted(Models::Item* parent, int first, int last) -{ +void Models::Roster::onChildIsAboutToBeInserted(Models::Item* parent, int first, int last) { int row = 0; if (parent != root) { row = parent->row(); @@ -662,45 +615,39 @@ void Models::Roster::onChildIsAboutToBeInserted(Models::Item* parent, int first, } } -void Models::Roster::onChildIsAboutToBeMoved(Models::Item* source, int first, int last, Models::Item* destination, int newIndex) -{ +void Models::Roster::onChildIsAboutToBeMoved(Models::Item* source, int first, int last, Models::Item* destination, int newIndex) { int oldRow = 0; - if (source != root) { + if (source != root) oldRow = source->row(); - } + int newRow = 0; - if (destination != root) { + if (destination != root) newRow = destination->row(); - } + beginMoveRows(createIndex(oldRow, 0, source), first, last, createIndex(newRow, 0, destination), newIndex); } -void Models::Roster::onChildIsAboutToBeRemoved(Models::Item* parent, int first, int last) -{ +void Models::Roster::onChildIsAboutToBeRemoved(Models::Item* parent, int first, int last) { int row = 0; - if (parent != root) { + if (parent != root) row = parent->row(); - } + beginRemoveRows(createIndex(row, 0, parent), first, last); } -void Models::Roster::onChildInserted() -{ +void Models::Roster::onChildInserted() { endInsertRows(); } -void Models::Roster::onChildMoved() -{ +void Models::Roster::onChildMoved() { endMoveRows(); } -void Models::Roster::onChildRemoved() -{ +void Models::Roster::onChildRemoved() { endRemoveRows(); } -void Models::Roster::addPresence(const QString& account, const QString& jid, const QString& name, const QMap& data) -{ +void Models::Roster::addPresence(const QString& account, const QString& jid, const QString& name, const QMap& data) { ElId contactId(account, jid); std::map::iterator itr = contacts.find(contactId); if (itr != contacts.end()) { @@ -709,8 +656,7 @@ void Models::Roster::addPresence(const QString& account, const QString& jid, con } -void Models::Roster::removePresence(const QString& account, const QString& jid, const QString& name) -{ +void Models::Roster::removePresence(const QString& account, const QString& jid, const QString& name) { ElId contactId(account, jid); std::map::iterator itr = contacts.find(contactId); if (itr != contacts.end()) { @@ -718,16 +664,13 @@ void Models::Roster::removePresence(const QString& account, const QString& jid, } } -void Models::Roster::addMessage(const QString& account, const Shared::Message& data) -{ +void Models::Roster::addMessage(const QString& account, const Shared::Message& data) { Element* el = getElement(ElId(account, data.getPenPalJid())); - if (el != nullptr) { + if (el != nullptr) el->addMessage(data); - } } -void Models::Roster::removeAccount(const QString& account) -{ +void Models::Roster::removeAccount(const QString& account) { std::map::const_iterator itr = accounts.find(account); if (itr == accounts.end()) { qDebug() << "An attempt to remove non existing account " << account << ", skipping"; @@ -777,26 +720,23 @@ void Models::Roster::removeAccount(const QString& account) acc->deleteLater(); } -QString Models::Roster::getContactName(const QString& account, const QString& jid) const -{ +QString Models::Roster::getContactName(const QString& account, const QString& jid) const { ElId id(account, jid); std::map::const_iterator cItr = contacts.find(id); QString name = ""; if (cItr == contacts.end()) { std::map::const_iterator rItr = rooms.find(id); - if (rItr == rooms.end()) { + if (rItr == rooms.end()) qDebug() << "An attempt to get a name of non existing contact/room " << account << ":" << jid << ", skipping"; - } else { + else name = rItr->second->getRoomName(); - } } else { name = cItr->second->getContactName(); } return name; } -void Models::Roster::addRoom(const QString& account, const QString jid, const QMap& data) -{ +void Models::Roster::addRoom(const QString& account, const QString jid, const QMap& data) { Account* acc; { std::map::iterator itr = accounts.find(account); @@ -826,8 +766,7 @@ void Models::Roster::addRoom(const QString& account, const QString jid, const QM emit addedElement({acc->getId(), room->getId()}); } -void Models::Roster::changeRoom(const QString& account, const QString jid, const QMap& data) -{ +void Models::Roster::changeRoom(const QString& account, const QString jid, const QMap& data) { ElId id = {account, jid}; std::map::const_iterator itr = rooms.find(id); if (itr == rooms.end()) { @@ -840,8 +779,7 @@ void Models::Roster::changeRoom(const QString& account, const QString jid, const } } -void Models::Roster::removeRoom(const QString& account, const QString jid) -{ +void Models::Roster::removeRoom(const QString& account, const QString jid) { Account* acc; { std::map::iterator itr = accounts.find(account); @@ -865,8 +803,7 @@ void Models::Roster::removeRoom(const QString& account, const QString jid) rooms.erase(itr); } -void Models::Roster::addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data) -{ +void Models::Roster::addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data) { ElId id = {account, jid}; std::map::const_iterator itr = rooms.find(id); if (itr == rooms.end()) { @@ -877,8 +814,7 @@ void Models::Roster::addRoomParticipant(const QString& account, const QString& j } } -void Models::Roster::changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data) -{ +void Models::Roster::changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data) { ElId id = {account, jid}; std::map::const_iterator itr = rooms.find(id); if (itr == rooms.end()) { @@ -889,8 +825,7 @@ void Models::Roster::changeRoomParticipant(const QString& account, const QString } } -void Models::Roster::removeRoomParticipant(const QString& account, const QString& jid, const QString& name) -{ +void Models::Roster::removeRoomParticipant(const QString& account, const QString& jid, const QString& name) { ElId id = {account, jid}; std::map::const_iterator itr = rooms.find(id); if (itr == rooms.end()) { @@ -901,20 +836,17 @@ void Models::Roster::removeRoomParticipant(const QString& account, const QString } } -std::deque Models::Roster::groupList(const QString& account) const -{ +std::deque Models::Roster::groupList(const QString& account) const { std::deque answer; for (std::pair pair : groups) { - if (pair.first.account == account) { + if (pair.first.account == account) answer.push_back(pair.first.name); - } } return answer; } -bool Models::Roster::groupHasContact(const QString& account, const QString& group, const QString& contact) const -{ +bool Models::Roster::groupHasContact(const QString& account, const QString& group, const QString& contact) const { ElId grId({account, group}); std::map::const_iterator gItr = groups.find(grId); if (gItr == groups.end()) { @@ -924,22 +856,19 @@ bool Models::Roster::groupHasContact(const QString& account, const QString& grou } } -QString Models::Roster::getContactIconPath(const QString& account, const QString& jid, const QString& resource) const -{ +QString Models::Roster::getContactIconPath(const QString& account, const QString& jid, const QString& resource) const { ElId id(account, jid); std::map::const_iterator cItr = contacts.find(id); QString path = ""; if (cItr == contacts.end()) { std::map::const_iterator rItr = rooms.find(id); - if (rItr == rooms.end()) { + if (rItr == rooms.end()) qDebug() << "An attempt to get an icon path of non existing contact" << account << ":" << jid << ", returning empty value"; - } else { + else path = rItr->second->getParticipantIconPath(resource); - } } else { - if (cItr->second->getAvatarState() != Shared::Avatar::empty) { + if (cItr->second->getAvatarState() != Shared::Avatar::empty) path = cItr->second->getAvatarPath(); - } } return path; } @@ -950,34 +879,29 @@ Models::Account * Models::Roster::getAccount(const QString& name) { const Models::Account * Models::Roster::getAccountConst(const QString& name) const { return accounts.at(name);} -const Models::Element * Models::Roster::getElementConst(const Models::Roster::ElId& id) const -{ +const Models::Element * Models::Roster::getElementConst(const Models::Roster::ElId& id) const { std::map::const_iterator cItr = contacts.find(id); if (cItr != contacts.end()) { return cItr->second; } else { std::map::const_iterator rItr = rooms.find(id); - if (rItr != rooms.end()) { + if (rItr != rooms.end()) return rItr->second; - } } return nullptr; } -bool Models::Roster::markMessageAsRead(const Models::Roster::ElId& elementId, const QString& messageId) -{ +bool Models::Roster::markMessageAsRead(const Models::Roster::ElId& elementId, const QString& messageId) { const Element* el = getElementConst(elementId); - if (el != nullptr) { + if (el != nullptr) return el->markMessageAsRead(messageId); - } else { + else return false; - } } -QModelIndex Models::Roster::getAccountIndex(const QString& name) const -{ +QModelIndex Models::Roster::getAccountIndex(const QString& name) const { std::map::const_iterator itr = accounts.find(name); if (itr == accounts.end()) { return QModelIndex(); @@ -986,8 +910,7 @@ QModelIndex Models::Roster::getAccountIndex(const QString& name) const } } -QModelIndex Models::Roster::getGroupIndex(const QString& account, const QString& name) const -{ +QModelIndex Models::Roster::getGroupIndex(const QString& account, const QString& name) const { std::map::const_iterator itr = accounts.find(account); if (itr == accounts.end()) { return QModelIndex(); @@ -1002,8 +925,7 @@ QModelIndex Models::Roster::getGroupIndex(const QString& account, const QString& } } -QModelIndex Models::Roster::getContactIndex(const QString& account, const QString& jid, const QString& resource) const -{ +QModelIndex Models::Roster::getContactIndex(const QString& account, const QString& jid, const QString& resource) const { std::map::const_iterator itr = accounts.find(account); if (itr == accounts.end()) { return QModelIndex(); @@ -1017,11 +939,10 @@ QModelIndex Models::Roster::getContactIndex(const QString& account, const QStrin return contactIndex; } else { Presence* pres = cItr->second->getPresence(resource); - if (pres != nullptr) { + if (pres != nullptr) return index(pres->row(), 0, contactIndex); - } else { + else return contactIndex; - } } } else { std::map::const_iterator rItr = rooms.find(ElId(account, jid)); @@ -1031,11 +952,10 @@ QModelIndex Models::Roster::getContactIndex(const QString& account, const QStrin return roomIndex; } else { Participant* part = rItr->second->getParticipant(resource); - if (part != nullptr) { + if (part != nullptr) return index(part->row(), 0, roomIndex); - } else { + else return roomIndex; - } } } else { return QModelIndex(); @@ -1044,93 +964,78 @@ QModelIndex Models::Roster::getContactIndex(const QString& account, const QStrin } } -void Models::Roster::onElementRequestArchive(const QString& before) -{ +void Models::Roster::onElementRequestArchive(const QString& before) { Element* el = static_cast(sender()); emit requestArchive(el->getAccountName(), el->getJid(), before); } -void Models::Roster::responseArchive(const QString& account, const QString& jid, const std::list& list, bool last) -{ +void Models::Roster::responseArchive(const QString& account, const QString& jid, const std::list& list, bool last) { ElId id(account, jid); Element* el = getElement(id); - if (el != nullptr) { + if (el != nullptr) el->responseArchive(list, last); - } } -void Models::Roster::fileProgress(const std::list& msgs, qreal value, bool up) -{ +void Models::Roster::fileProgress(const std::list& msgs, qreal value, bool up) { for (const Shared::MessageInfo& info : msgs) { Element* el = getElement(ElId(info.account, info.jid)); - if (el != nullptr) { + if (el != nullptr) el->fileProgress(info.messageId, value, up); - } } } -void Models::Roster::fileComplete(const std::list& msgs, bool up) -{ +void Models::Roster::fileComplete(const std::list& msgs, bool up) { for (const Shared::MessageInfo& info : msgs) { Element* el = getElement(ElId(info.account, info.jid)); - if (el != nullptr) { + if (el != nullptr) el->fileComplete(info.messageId, up); - } } } -void Models::Roster::fileError(const std::list& msgs, const QString& err, bool up) -{ +void Models::Roster::fileError(const std::list& msgs, const QString& err, bool up) { for (const Shared::MessageInfo& info : msgs) { Element* el = getElement(ElId(info.account, info.jid)); - if (el != nullptr) { + if (el != nullptr) el->fileError(info.messageId, err, up); - } } } -Models::Element * Models::Roster::getElement(const Models::Roster::ElId& id) -{ +Models::Element * Models::Roster::getElement(const Models::Roster::ElId& id) { return const_cast(getElementConst(id)); } -Models::Item::Type Models::Roster::getContactType(const Models::Roster::ElId& id) const -{ +Models::Item::Type Models::Roster::getContactType(const Models::Roster::ElId& id) const { const Models::Element* el = getElementConst(id); - if (el == nullptr) { + if (el == nullptr) return Item::root; - } + return el->type; } -void Models::Roster::onAccountReconnected() -{ +void Models::Roster::onAccountReconnected() { Account* acc = static_cast(sender()); QString accName = acc->getName(); for (const std::pair& pair : contacts) { - if (pair.first.account == accName) { + if (pair.first.account == accName) pair.second->handleRecconnect(); - } } } -void Models::Roster::recalculateUnreadMessages() -{ +void Models::Roster::recalculateUnreadMessages() { int count(0); - for (const std::pair& pair : contacts) { + for (const std::pair& pair : contacts) count += pair.second->getMessagesCount(); - } - for (const std::pair& pair : rooms) { + + for (const std::pair& pair : rooms) count += pair.second->getMessagesCount(); - } + emit unreadMessagesCountChanged(count); } -std::list Models::Roster::getItemPath(const QModelIndex& index) const -{ +std::list Models::Roster::getItemPath(const QModelIndex& index) const { std::list result; if (index.isValid() && index.model() == this) { Item* item = static_cast(index.internalPointer()); @@ -1143,8 +1048,7 @@ std::list Models::Roster::getItemPath(const QModelIndex& index) const return result; } -QModelIndex Models::Roster::getIndexByPath(const std::list& path) const -{ +QModelIndex Models::Roster::getIndexByPath(const std::list& path) const { if (path.empty()) return QModelIndex(); diff --git a/ui/utils/comboboxdelegate.cpp b/ui/utils/comboboxdelegate.cpp index 4c96c79..cbaa1e3 100644 --- a/ui/utils/comboboxdelegate.cpp +++ b/ui/utils/comboboxdelegate.cpp @@ -18,35 +18,30 @@ #include "QTimer" #include "comboboxdelegate.h" +#include "shared/defines.h" ComboboxDelegate::ComboboxDelegate(QObject *parent): QStyledItemDelegate(parent), entries(), ff(new FocusFilter()) -{ -} +{} - -ComboboxDelegate::~ComboboxDelegate() -{ +ComboboxDelegate::~ComboboxDelegate() { delete ff; } - -QWidget* ComboboxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ +QWidget* ComboboxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { + SHARED_UNUSED(option); + SHARED_UNUSED(index); QComboBox *cb = new QComboBox(parent); - - for (const std::pair& pair : entries) { + for (const std::pair& pair : entries) cb->addItem(pair.second, pair.first); - } return cb; } -void ComboboxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const -{ +void ComboboxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QComboBox *cb = static_cast(editor); int currentIndex = index.data(Qt::EditRole).toInt(); if (currentIndex >= 0) { @@ -56,19 +51,16 @@ void ComboboxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) } -void ComboboxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const -{ +void ComboboxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QComboBox *cb = static_cast(editor); model->setData(index, cb->currentIndex(), Qt::EditRole); } -void ComboboxDelegate::addEntry(const QString& title, const QIcon& icon) -{ +void ComboboxDelegate::addEntry(const QString& title, const QIcon& icon) { entries.emplace_back(title, icon); } -bool ComboboxDelegate::FocusFilter::eventFilter(QObject* src, QEvent* evt) -{ +bool ComboboxDelegate::FocusFilter::eventFilter(QObject* src, QEvent* evt) { if (evt->type() == QEvent::FocusIn) { QComboBox* cb = static_cast(src); cb->removeEventFilter(this); diff --git a/ui/utils/resizer.cpp b/ui/utils/resizer.cpp index 8691400..ad62e6d 100644 --- a/ui/utils/resizer.cpp +++ b/ui/utils/resizer.cpp @@ -18,14 +18,13 @@ #include "resizer.h" -Resizer::Resizer(QWidget* parent): -QObject(parent) -{ - -} +#include "shared/defines.h" -bool Resizer::eventFilter(QObject* obj, QEvent* event) -{ +Resizer::Resizer(QWidget* parent): +QObject(parent) {} + +bool Resizer::eventFilter(QObject* obj, QEvent* event) { + SHARED_UNUSED(obj); if (event->type() == QEvent::Resize) { QResizeEvent* ev = static_cast(event); emit resized(ev->oldSize(), ev->size()); diff --git a/ui/widgets/chat.cpp b/ui/widgets/chat.cpp index 301553b..3b9f090 100644 --- a/ui/widgets/chat.cpp +++ b/ui/widgets/chat.cpp @@ -19,6 +19,8 @@ #include "chat.h" #include "ui_conversation.h" +#include "shared/defines.h" + Chat::Chat(Models::Account* acc, Models::Contact* p_contact, QWidget* parent): Conversation(false, acc, p_contact, p_contact->getJid(), "", parent), contact(p_contact) @@ -39,6 +41,7 @@ Chat::~Chat() {} void Chat::onContactChanged(Models::Item* item, int row, int col) { + SHARED_UNUSED(row); if (item == contact) { switch (col) { case 0: diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index c11449f..e6316c7 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -115,15 +115,14 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, initializeOverlay(); } -Conversation::~Conversation() -{ +Conversation::~Conversation() { delete contextMenu; element->feed->decrementObservers(); } -void Conversation::onAccountChanged(Models::Item* item, int row, int col) -{ +void Conversation::onAccountChanged(Models::Item* item, int row, int col) { + SHARED_UNUSED(row); if (item == account) { if (col == 2 && account->getState() == Shared::ConnectionState::connected) { //to request the history when we're back online after reconnect //if (!requestingHistory) { @@ -136,8 +135,7 @@ void Conversation::onAccountChanged(Models::Item* item, int row, int col) } } -void Conversation::initializeOverlay() -{ +void Conversation::initializeOverlay() { QGridLayout* gr = static_cast(layout()); QLabel* progressLabel = new QLabel(tr("Drop files here to attach them to your message")); gr->addWidget(overlay, 0, 0, 2, 1); @@ -160,26 +158,22 @@ void Conversation::initializeOverlay() overlay->hide(); } -void Conversation::setName(const QString& name) -{ +void Conversation::setName(const QString& name) { m_ui->nameLabel->setText(name); setWindowTitle(name); } -QString Conversation::getAccount() const -{ +QString Conversation::getAccount() const { return account->getName(); } -QString Conversation::getJid() const -{ +QString Conversation::getJid() const { return palJid; } KeyEnterReceiver::KeyEnterReceiver(QObject* parent): QObject(parent), ownEvent(false) {} -bool KeyEnterReceiver::eventFilter(QObject* obj, QEvent* event) -{ +bool KeyEnterReceiver::eventFilter(QObject* obj, QEvent* event) { QEvent::Type type = event->type(); if (type == QEvent::KeyPress) { QKeyEvent* key = static_cast(event); @@ -215,18 +209,15 @@ bool Conversation::checkClipboardImage() { return !QApplication::clipboard()->image().isNull(); } -QString Conversation::getPalResource() const -{ +QString Conversation::getPalResource() const { return activePalResource; } -void Conversation::setPalResource(const QString& res) -{ +void Conversation::setPalResource(const QString& res) { activePalResource = res; } -void Conversation::initiateMessageSending() -{ +void Conversation::initiateMessageSending() { QString body(m_ui->messageEditor->toPlainText()); if (body.size() > 0) { @@ -245,8 +236,7 @@ void Conversation::initiateMessageSending() clear(); } -void Conversation::initiateMessageSending(const Shared::Message& msg) -{ +void Conversation::initiateMessageSending(const Shared::Message& msg) { if (currentAction == CurrentAction::edit) { emit replaceMessage(currentMessageId, msg); currentAction = CurrentAction::none; @@ -255,12 +245,11 @@ void Conversation::initiateMessageSending(const Shared::Message& msg) } } -void Conversation::onImagePasted() -{ +void Conversation::onImagePasted() { QImage image = QApplication::clipboard()->image(); - if (image.isNull()) { + if (image.isNull()) return; - } + QTemporaryFile *tempFile = new QTemporaryFile(QDir::tempPath() + QStringLiteral("/squawk_img_attach_XXXXXX.png"), QApplication::instance()); tempFile->open(); image.save(tempFile, "PNG"); @@ -273,8 +262,7 @@ void Conversation::onImagePasted() // See Core::NetworkAccess::onUploadFinished. } -void Conversation::onAttach() -{ +void Conversation::onAttach() { QFileDialog* d = new QFileDialog(this, tr("Chose a file to send")); d->setFileMode(QFileDialog::ExistingFile); @@ -284,37 +272,31 @@ void Conversation::onAttach() d->show(); } -void Conversation::onFileSelected() -{ +void Conversation::onFileSelected() { QFileDialog* d = static_cast(sender()); - - for (const QString& path : d->selectedFiles()) { + for (const QString& path : d->selectedFiles()) addAttachedFile(path); - } d->deleteLater(); } -void Conversation::setStatus(const QString& status) -{ +void Conversation::setStatus(const QString& status) { statusLabel->setText(Shared::processMessageBody(status)); } -Models::Roster::ElId Conversation::getId() const -{ +Models::Roster::ElId Conversation::getId() const { return {getAccount(), getJid()}; } -void Conversation::addAttachedFile(const QString& path) -{ +void Conversation::addAttachedFile(const QString& path) { QMimeDatabase db; QMimeType type = db.mimeTypeForFile(path); QFileInfo info(path); QIcon fileIcon = QIcon::fromTheme(type.iconName()); - if (fileIcon.isNull()) { + if (fileIcon.isNull()) fileIcon.addFile(QString::fromUtf8(":/images/fallback/dark/big/mail-attachment.svg"), QSize(), QIcon::Normal, QIcon::Off); - } + Badge* badge = new Badge(path, info.fileName(), fileIcon); connect(badge, &Badge::close, this, &Conversation::onBadgeClose); @@ -331,35 +313,31 @@ void Conversation::addAttachedFile(const QString& path) } } -void Conversation::removeAttachedFile(Badge* badge) -{ +void Conversation::removeAttachedFile(Badge* badge) { W::Order::const_iterator itr = filesToAttach.find(badge); if (itr != filesToAttach.end()) { filesToAttach.erase(badge); - if (filesLayout->count() == 1) { + if (filesLayout->count() == 1) filesLayout->setContentsMargins(0, 0, 0, 0); - } + badge->deleteLater(); } } -void Conversation::onBadgeClose() -{ +void Conversation::onBadgeClose() { Badge* badge = static_cast(sender()); removeAttachedFile(badge); } -void Conversation::clearAttachedFiles() -{ - for (Badge* badge : filesToAttach) { +void Conversation::clearAttachedFiles() { + for (Badge* badge : filesToAttach) badge->deleteLater(); - } + filesToAttach.clear(); filesLayout->setContentsMargins(0, 0, 0, 0); } -void Conversation::clear() -{ +void Conversation::clear() { currentMessageId.clear(); currentAction = CurrentAction::none; m_ui->currentActionBadge->setVisible(false); @@ -367,8 +345,7 @@ void Conversation::clear() m_ui->messageEditor->clear(); } -void Conversation::setAvatar(const QString& path) -{ +void Conversation::setAvatar(const QString& path) { QPixmap pixmap; if (path.size() == 0) { pixmap = Shared::icon("user", true).pixmap(avatarSize); @@ -390,13 +367,11 @@ void Conversation::setAvatar(const QString& path) m_ui->avatar->setPixmap(result); } -void Conversation::onTextEditDocSizeChanged(const QSizeF& size) -{ +void Conversation::onTextEditDocSizeChanged(const QSizeF& size) { m_ui->messageEditor->setMaximumHeight(int(size.height())); } -void Conversation::setFeedFrames(bool top, bool right, bool bottom, bool left) -{ +void Conversation::setFeedFrames(bool top, bool right, bool bottom, bool left) { shadow.setFrames(top, right, bottom, left); } @@ -421,13 +396,12 @@ void Conversation::dragEnterEvent(QDragEnterEvent* event) } } -void Conversation::dragLeaveEvent(QDragLeaveEvent* event) -{ +void Conversation::dragLeaveEvent(QDragLeaveEvent* event) { + SHARED_UNUSED(event); overlay->hide(); } -void Conversation::dropEvent(QDropEvent* event) -{ +void Conversation::dropEvent(QDropEvent* event) { bool accept = false; if (event->mimeData()->hasUrls()) { QList list = event->mimeData()->urls(); @@ -441,14 +415,13 @@ void Conversation::dropEvent(QDropEvent* event) } } } - if (accept) { + if (accept) event->acceptProposedAction(); - } + overlay->hide(); } -Shared::Message Conversation::createMessage() const -{ +Shared::Message Conversation::createMessage() const { Shared::Message msg; msg.setOutgoing(true); msg.generateRandomId(); @@ -457,23 +430,20 @@ Shared::Message Conversation::createMessage() const return msg; } -void Conversation::onFeedMessage(const Shared::Message& msg) -{ +void Conversation::onFeedMessage(const Shared::Message& msg) { this->onMessage(msg); } -void Conversation::onMessage(const Shared::Message& msg) -{ +void Conversation::onMessage(const Shared::Message& msg) { if (!msg.getForwarded()) { QApplication::alert(this); - if (window()->windowState().testFlag(Qt::WindowMinimized)) { + if (window()->windowState().testFlag(Qt::WindowMinimized)) emit notifyableMessage(getAccount(), msg); - } + } } -void Conversation::positionShadow() -{ +void Conversation::positionShadow() { int w = width(); int h = feed->height(); @@ -482,8 +452,7 @@ void Conversation::positionShadow() shadow.raise(); } -void Conversation::onFeedContext(const QPoint& pos) -{ +void Conversation::onFeedContext(const QPoint& pos) { QModelIndex index = feed->indexAt(pos); if (index.isValid()) { Shared::Message* item = static_cast(index.internalPointer()); @@ -542,14 +511,12 @@ void Conversation::onFeedContext(const QPoint& pos) connect(edit, &QAction::triggered, this, std::bind(&Conversation::onMessageEditRequested, this, id)); } - if (showMenu) { + if (showMenu) contextMenu->popup(feed->viewport()->mapToGlobal(pos)); - } } } -void Conversation::onMessageEditorContext(const QPoint& pos) -{ +void Conversation::onMessageEditorContext(const QPoint& pos) { pasteImageAction->setEnabled(Conversation::checkClipboardImage()); QMenu *editorMenu = m_ui->messageEditor->createStandardContextMenu(); @@ -559,8 +526,7 @@ void Conversation::onMessageEditorContext(const QPoint& pos) editorMenu->exec(this->m_ui->messageEditor->mapToGlobal(pos)); } -void Conversation::onMessageEditRequested(const QString& id) -{ +void Conversation::onMessageEditRequested(const QString& id) { clear(); try { @@ -582,8 +548,7 @@ void Conversation::onMessageEditRequested(const QString& id) } } -void Conversation::showEvent(QShowEvent* event) -{ +void Conversation::showEvent(QShowEvent* event) { QWidget::showEvent(event); emit shown(); diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index 0c44bd9..80c8c2f 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -34,6 +34,7 @@ #include "shared/icons.h" #include "shared/utils.h" #include "shared/pathcheck.h" +#include "shared/defines.h" #include "ui/models/account.h" #include "ui/models/roster.h" @@ -159,8 +160,6 @@ private: static QPainterPath* avatarMask; static QPixmap* avatarPixmap; static QPainter* avatarPainter; - - }; #endif // CONVERSATION_H diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index 9d222ae..43d0218 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -79,11 +79,10 @@ QModelIndex FeedView::indexAt(const QPoint& point) const { for (std::deque::size_type i = 0; i < hints.size(); ++i) { const Hint& hint = hints[i]; if (y <= hint.offset + hint.height) { - if (y > hint.offset) { + if (y > hint.offset) return model()->index(i, 0, rootIndex()); - } else { + else break; - } } } @@ -156,13 +155,11 @@ void FeedView::updateGeometries() { vo = 0; } else { int verticalMargin = 0; - if (st->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) { + if (st->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) frameAroundContents = st->pixelMetric(QStyle::PM_DefaultFrameWidth) * 2; - } - if (verticalScrollBarPolicy() == Qt::ScrollBarAsNeeded) { + if (verticalScrollBarPolicy() == Qt::ScrollBarAsNeeded) verticalMargin = verticalScrollBarExtent + frameAroundContents; - } layoutBounds.rwidth() -= verticalMargin; @@ -176,21 +173,19 @@ void FeedView::updateGeometries() { QModelIndex index = m->index(i, 0, rootIndex()); QDateTime currentDate = index.data(Models::MessageFeed::Date).toDateTime(); if (i > 0) { - if (currentDate.daysTo(lastDate) > 0) { + if (currentDate.daysTo(lastDate) > 0) previousOffset += dividerMetrics.height() + dateDeviderMargin * 2; - } else { + else previousOffset += elementMargin; - } } lastDate = currentDate; QSize messageSize = itemDelegate(index)->sizeHint(option, index); uint32_t offsetX(0); if (specialDelegate) { - if (index.data(Models::MessageFeed::SentByMe).toBool()) { + if (index.data(Models::MessageFeed::SentByMe).toBool()) offsetX = layoutBounds.width() - messageSize.width() - MessageDelegate::avatarHeight - MessageDelegate::margin * 2; - } else { + else offsetX = MessageDelegate::avatarHeight + MessageDelegate::margin * 2; - } } hints.emplace_back(Hint({ @@ -204,9 +199,9 @@ void FeedView::updateGeometries() { } int totalHeight = previousOffset - layoutBounds.height() + dividerMetrics.height() + dateDeviderMargin * 2; - if (modelState != Models::MessageFeed::complete) { + if (modelState != Models::MessageFeed::complete) totalHeight += progressSize; - } + vo = qMax(qMin(vo, totalHeight), 0); bar->setRange(0, totalHeight); bar->setPageStep(layoutBounds.height()); @@ -219,7 +214,6 @@ void FeedView::updateGeometries() { clearWidgetsMode = true; } - QAbstractItemView::updateGeometries(); } @@ -230,26 +224,23 @@ bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewIt QModelIndex index = m->index(i, 0, rootIndex()); QDateTime currentDate = index.data(Models::MessageFeed::Date).toDateTime(); if (i > 0) { - if (currentDate.daysTo(lastDate) > 0) { + if (currentDate.daysTo(lastDate) > 0) previousOffset += dateDeviderMargin * 2 + dividerMetrics.height(); - } else { + else previousOffset += elementMargin; - } } lastDate = currentDate; QSize messageSize = itemDelegate(index)->sizeHint(option, index); - if (previousOffset + messageSize.height() + elementMargin > totalHeight) { + if (previousOffset + messageSize.height() + elementMargin > totalHeight) return false; - } uint32_t offsetX(0); if (specialDelegate) { - if (index.data(Models::MessageFeed::SentByMe).toBool()) { + if (index.data(Models::MessageFeed::SentByMe).toBool()) offsetX = option.rect.width() - messageSize.width() - MessageDelegate::avatarHeight - MessageDelegate::margin * 2; - } else { + else offsetX = MessageDelegate::avatarHeight + MessageDelegate::margin * 2; - } } hints.emplace_back(Hint({ false, @@ -262,9 +253,8 @@ bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewIt } previousOffset += dateDeviderMargin * 2 + dividerMetrics.height(); - if (previousOffset > totalHeight) { + if (previousOffset > totalHeight) return false; - } return true; } @@ -284,13 +274,12 @@ void FeedView::paintEvent(QPaintEvent* event) { const Hint& hint = hints[i]; int32_t relativeY1 = vph - hint.offset - hint.height; if (!inZone) { - if (y2 > relativeY1) { + if (y2 > relativeY1) inZone = true; - } } - if (inZone) { + if (inZone) toRener.emplace_back(m->index(i, 0, rootIndex())); - } + if (y1 > relativeY1) { inZone = false; break; @@ -304,9 +293,8 @@ void FeedView::paintEvent(QPaintEvent* event) { if (specialDelegate) { MessageDelegate* del = static_cast(itemDelegate()); - if (clearWidgetsMode) { + if (clearWidgetsMode) del->beginClearWidgets(); - } } QDateTime lastDate; @@ -319,9 +307,8 @@ void FeedView::paintEvent(QPaintEvent* event) { int ind = index.row() - 1; if (ind > 0) { QDateTime underDate = m->index(ind, 0, rootIndex()).data(Models::MessageFeed::Date).toDateTime(); - if (currentDate.daysTo(underDate) > 0) { + if (currentDate.daysTo(underDate) > 0) drawDateDevider(option.rect.bottom(), underDate, painter); - } } first = false; } @@ -332,14 +319,13 @@ void FeedView::paintEvent(QPaintEvent* event) { option.state.setFlag(QStyle::State_MouseOver, mouseOver); itemDelegate(index)->paint(&painter, option, index); - if (!lastDate.isNull() && currentDate.daysTo(lastDate) > 0) { + if (!lastDate.isNull() && currentDate.daysTo(lastDate) > 0) drawDateDevider(option.rect.bottom(), lastDate, painter); - } + lastDate = currentDate; } - if (!lastDate.isNull() && inZone) { //if after drawing all messages there is still space + if (!lastDate.isNull() && inZone) //if after drawing all messages there is still space drawDateDevider(option.rect.top() - dateDeviderMargin * 2 - dividerMetrics.height(), lastDate, painter); - } if (clearWidgetsMode && specialDelegate) { MessageDelegate* del = static_cast(itemDelegate()); @@ -362,13 +348,11 @@ void FeedView::verticalScrollbarValueChanged(int value) { positionProgress(); - if (specialDelegate) { + if (specialDelegate) clearWidgetsMode = true; - } - if (modelState == Models::MessageFeed::incomplete && value < progressSize) { + if (modelState == Models::MessageFeed::incomplete && value < progressSize) model()->fetchMore(rootIndex()); - } QAbstractItemView::verticalScrollbarValueChanged(vo); } @@ -391,16 +375,14 @@ void FeedView::setAnchorHovered(Shared::Hover type) { } void FeedView::mouseMoveEvent(QMouseEvent* event) { - if (!isVisible()) { + if (!isVisible()) return; - } dragEndPoint = event->localPos().toPoint(); if (mousePressed) { QPoint distance = dragStartPoint - dragEndPoint; - if (distance.manhattanLength() > 5) { + if (distance.manhattanLength() > 5) dragging = true; - } } QAbstractItemView::mouseMoveEvent(event); @@ -423,11 +405,10 @@ void FeedView::mouseMoveEvent(QMouseEvent* event) { QModelIndex index = indexAt(dragEndPoint); if (index.isValid()) { QRect rect = visualRect(index); - if (rect.contains(dragEndPoint)) { + if (rect.contains(dragEndPoint)) setAnchorHovered(del->hoverType(dragEndPoint, index, rect)); - } else { + else setAnchorHovered(Shared::Hover::nothing); - } } else { setAnchorHovered(Shared::Hover::nothing); } @@ -447,9 +428,8 @@ void FeedView::mousePressEvent(QMouseEvent* event) { if (lastSelectedId.size()) { Models::MessageFeed* feed = static_cast(model()); QModelIndex index = feed->modelIndexById(lastSelectedId); - if (index.isValid()) { + if (index.isValid()) setDirtyRegion(visualRect(index)); - } } } } @@ -467,18 +447,16 @@ void FeedView::mouseDoubleClickEvent(QMouseEvent* event) { if (lastSelectedId.size()) { Models::MessageFeed* feed = static_cast(model()); QModelIndex index = feed->modelIndexById(lastSelectedId); - if (index.isValid()) { + if (index.isValid()) setDirtyRegion(visualRect(index)); - } } QModelIndex index = indexAt(dragStartPoint); QRect rect = visualRect(index); if (rect.contains(dragStartPoint)) { selectedText = del->leftDoubleClick(dragStartPoint, index, rect); - if (selectedText.size() > 0) { + if (selectedText.size() > 0) setDirtyRegion(rect); - } } } } @@ -494,9 +472,8 @@ void FeedView::mouseReleaseEvent(QMouseEvent* event) { if (index.isValid()) { QRect rect = visualRect(index); MessageDelegate* del = static_cast(itemDelegate()); - if (rect.contains(point)) { + if (rect.contains(point)) del->leftClick(point, index, rect); - } } } dragging = false; diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index 1830d71..35a73c2 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -29,6 +29,8 @@ #include "messagedelegate.h" #include "messagefeed.h" +#include "shared/defines.h" + constexpr int textMargin = 2; constexpr int statusIconSize = 16; @@ -70,27 +72,21 @@ MessageDelegate::MessageDelegate(QObject* parent): barHeight = bar.sizeHint().height(); } -MessageDelegate::~MessageDelegate() -{ - for (const std::pair& pair: *buttons){ +MessageDelegate::~MessageDelegate() { + for (const std::pair& pair: *buttons) delete pair.second; - } - for (const std::pair& pair: *bars){ + for (const std::pair& pair: *bars) delete pair.second; - } - for (const std::pair& pair: *statusIcons){ + for (const std::pair& pair: *statusIcons) delete pair.second; - } - for (const std::pair& pair: *pencilIcons){ + for (const std::pair& pair: *pencilIcons) delete pair.second; - } - for (const std::pair& pair: *previews){ + for (const std::pair& pair: *previews) delete pair.second; - } delete statusIcons; delete pencilIcons; @@ -101,29 +97,26 @@ MessageDelegate::~MessageDelegate() delete bodyRenderer; } -void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const -{ +void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { QVariant vi = index.data(Models::MessageFeed::Bulk); - if (!vi.isValid()) { + if (!vi.isValid()) return; - } + Models::FeedItem data = qvariant_cast(vi); painter->save(); painter->setRenderHint(QPainter::Antialiasing, true); paintBubble(data, painter, option); bool ntds = needToDrawSender(index, data); - if (ntds || option.rect.y() < 1) { + if (ntds || option.rect.y() < 1) paintAvatar(data, index, option, painter); - } QStyleOptionViewItem opt = option; opt.rect = option.rect.adjusted(bubbleMargin, bubbleMargin, -bubbleMargin, -bubbleMargin / 2); - if (!data.sentByMe) { + if (!data.sentByMe) opt.displayAlignment = Qt::AlignLeft | Qt::AlignTop; - } else { + else opt.displayAlignment = Qt::AlignRight | Qt::AlignTop; - } QRect rect; if (ntds) { @@ -189,11 +182,11 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio QLabel* pencilIcon = getPencilIcon(data); pencilIcon->setParent(vp); - if (data.sentByMe) { + if (data.sentByMe) pencilIcon->move(opt.rect.left() + statusIconSize + margin, currentY); - } else { + else pencilIcon->move(opt.rect.right() - statusIconSize - margin, currentY); - } + pencilIcon->show(); } else { std::map::const_iterator itr = pencilIcons->find(data.id); @@ -205,27 +198,24 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio painter->restore(); - if (clearingWidgets) { + if (clearingWidgets) idsToKeep->insert(data.id); - } } -void MessageDelegate::paintBubble(const Models::FeedItem& data, QPainter* painter, const QStyleOptionViewItem& option) const -{ +void MessageDelegate::paintBubble(const Models::FeedItem& data, QPainter* painter, const QStyleOptionViewItem& option) const { painter->save(); - if (data.sentByMe) { + if (data.sentByMe) painter->setBrush(option.palette.brush(QPalette::Inactive, QPalette::Highlight)); - } else { + else painter->setBrush(option.palette.brush(QPalette::Window)); - } + painter->setPen(Qt::NoPen); painter->drawRoundedRect(option.rect, bubbleBorderRadius, bubbleBorderRadius); painter->restore(); } -void MessageDelegate::paintAvatar(const Models::FeedItem& data, const QModelIndex& index, const QStyleOptionViewItem& option, QPainter* painter) const -{ +void MessageDelegate::paintAvatar(const Models::FeedItem& data, const QModelIndex& index, const QStyleOptionViewItem& option, QPainter* painter) const { int currentRow = index.row(); int y = option.rect.y(); bool firstAttempt = true; @@ -255,11 +245,10 @@ void MessageDelegate::paintAvatar(const Models::FeedItem& data, const QModelInde QPainterPath path; int ax; - if (data.sentByMe) { + if (data.sentByMe) ax = option.rect.x() + option.rect.width() + margin; - } else { + else ax = margin; - } path.addEllipse(ax, y + margin / 2, avatarHeight, avatarHeight); painter->save(); @@ -268,8 +257,7 @@ void MessageDelegate::paintAvatar(const Models::FeedItem& data, const QModelInde painter->restore(); } -bool MessageDelegate::needToDrawAvatar(const QModelIndex& index, const Models::FeedItem& data, const QStyleOptionViewItem& option) const -{ +bool MessageDelegate::needToDrawAvatar(const QModelIndex& index, const Models::FeedItem& data, const QStyleOptionViewItem& option) const { return (option.rect.y() < 1) || needToDrawSender(index, data); } @@ -285,8 +273,7 @@ bool MessageDelegate::needToDrawSender(const QModelIndex& index, const Models::F } } -QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const -{ +QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { QRect messageRect = option.rect.adjusted(bubbleMargin, margin / 2 + bubbleMargin, -(avatarHeight + 3 * margin + bubbleMargin), -(margin + bubbleMargin) / 2); QStyleOptionViewItem opt = option; opt.rect = messageRect; @@ -360,12 +347,10 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel return messageSize; } -QRect MessageDelegate::getHoveredMessageBodyRect(const QModelIndex& index, const Models::FeedItem& data, const QRect& sizeHint) const -{ +QRect MessageDelegate::getHoveredMessageBodyRect(const QModelIndex& index, const Models::FeedItem& data, const QRect& sizeHint) const { QRect localHint = sizeHint.adjusted(bubbleMargin, bubbleMargin + margin, -bubbleMargin, -bubbleMargin / 2); - if (needToDrawSender(index, data)) { + if (needToDrawSender(index, data)) localHint.adjust(0, nickMetrics.lineSpacing() + textMargin, 0, 0); - } int attachHeight = 0; switch (data.attach.state) { @@ -405,8 +390,7 @@ QRect MessageDelegate::getHoveredMessageBodyRect(const QModelIndex& index, const return localHint; } -QString MessageDelegate::getAnchor(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const -{ +QString MessageDelegate::getAnchor(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const { QVariant vi = index.data(Models::MessageFeed::Bulk); Models::FeedItem data = qvariant_cast(vi); if (data.text.size() > 0) { @@ -425,16 +409,13 @@ QString MessageDelegate::getAnchor(const QPoint& point, const QModelIndex& index return QString(); } -void MessageDelegate::leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const -{ +void MessageDelegate::leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const { QString anchor = getAnchor(point, index, sizeHint); - if (anchor.size() > 0) { + if (anchor.size() > 0) emit openLink(anchor); - } } -QString MessageDelegate::leftDoubleClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) -{ +QString MessageDelegate::leftDoubleClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) { QVariant vi = index.data(Models::MessageFeed::Bulk); Models::FeedItem data = qvariant_cast(vi); if (data.text.size() > 0) { @@ -466,8 +447,7 @@ QString MessageDelegate::leftDoubleClick(const QPoint& point, const QModelIndex& return ""; } -Shared::Hover MessageDelegate::hoverType(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const -{ +Shared::Hover MessageDelegate::hoverType(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const { QVariant vi = index.data(Models::MessageFeed::Bulk); Models::FeedItem data = qvariant_cast(vi); if (data.text.size() > 0) { @@ -486,17 +466,15 @@ Shared::Hover MessageDelegate::hoverType(const QPoint& point, const QModelIndex& return Shared::Hover::anchor; } else { int position = lay->hitTest(translated, Qt::HitTestAccuracy::ExactHit); - if (position != -1) { + if (position != -1) return Shared::Hover::text; - } } } } return Shared::Hover::nothing; } -QString MessageDelegate::mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint) -{ +QString MessageDelegate::mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint) { QVariant vi = index.data(Models::MessageFeed::Bulk); Models::FeedItem data = qvariant_cast(vi); if (data.text.size() > 0) { @@ -529,30 +507,24 @@ QString MessageDelegate::mouseDrag(const QPoint& start, const QPoint& end, const return ""; } -QString MessageDelegate::clearSelection() -{ +QString MessageDelegate::clearSelection() { QString lastSelectedId = currentId; currentId = ""; selection = std::pair(0, 0); return lastSelectedId; } -bool MessageDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) -{ +bool MessageDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) { //qDebug() << event->type(); - - return QStyledItemDelegate::editorEvent(event, model, option, index); } -int MessageDelegate::paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const -{ +int MessageDelegate::paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const { QPoint start; - if (sentByMe) { + if (sentByMe) start = {option.rect.x() + option.rect.width() - btn->width(), option.rect.top()}; - } else { + else start = option.rect.topLeft(); - } QWidget* vp = static_cast(painter->device()); btn->setParent(vp); @@ -563,8 +535,7 @@ int MessageDelegate::paintButton(QPushButton* btn, QPainter* painter, bool sentB return btn->width(); } -int MessageDelegate::paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const -{ +int MessageDelegate::paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const { painter->setFont(dateFont); QColor q = painter->pen().color(); q.setAlpha(180); @@ -576,8 +547,8 @@ int MessageDelegate::paintComment(const Models::FeedItem& data, QPainter* painte return rect.width(); } -int MessageDelegate::paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const -{ +int MessageDelegate::paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const { + SHARED_UNUSED(sentByMe); QPoint start = option.rect.topLeft(); bar->resize(option.rect.width(), barHeight); @@ -589,8 +560,7 @@ int MessageDelegate::paintBar(QProgressBar* bar, QPainter* painter, bool sentByM return option.rect.width(); } -int MessageDelegate::paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const -{ +int MessageDelegate::paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const { Preview* preview = 0; std::map::iterator itr = previews->find(data.id); @@ -605,18 +575,16 @@ int MessageDelegate::paintPreview(const Models::FeedItem& data, QPainter* painte previews->insert(std::make_pair(data.id, preview)); } - if (!preview->isFileReachable()) { //this is the situation when the file preview couldn't be painted because the file was moved + if (!preview->isFileReachable()) //this is the situation when the file preview couldn't be painted because the file was moved emit invalidPath(data.id); //or deleted. This signal notifies the model, and the model notifies the core, preview can - } //handle being invalid for as long as I need and can be even become valid again with a new path - + //handle being invalid for as long as I need and can be even become valid again with a new path QSize pSize(preview->size()); option.rect.adjust(0, pSize.height() + textMargin, 0, 0); return pSize.width(); } -QPushButton * MessageDelegate::getButton(const Models::FeedItem& data) const -{ +QPushButton * MessageDelegate::getButton(const Models::FeedItem& data) const { std::map::const_iterator itr = buttons->find(data.id); FeedButton* result = 0; if (itr != buttons->end()) { @@ -640,8 +608,7 @@ QPushButton * MessageDelegate::getButton(const Models::FeedItem& data) const return result; } -QProgressBar * MessageDelegate::getBar(const Models::FeedItem& data) const -{ +QProgressBar * MessageDelegate::getBar(const Models::FeedItem& data) const { std::map::const_iterator barItr = bars->find(data.id); QProgressBar* result = 0; if (barItr != bars->end()) { @@ -665,8 +632,7 @@ QProgressBar * MessageDelegate::getBar(const Models::FeedItem& data) const return result; } -QLabel * MessageDelegate::getStatusIcon(const Models::FeedItem& data) const -{ +QLabel * MessageDelegate::getStatusIcon(const Models::FeedItem& data) const { std::map::const_iterator itr = statusIcons->find(data.id); QLabel* result = 0; @@ -680,9 +646,8 @@ QLabel * MessageDelegate::getStatusIcon(const Models::FeedItem& data) const QIcon q(Shared::icon(Shared::messageStateThemeIcons[static_cast(data.state)])); QString tt = Shared::Global::getName(data.state); if (data.state == Shared::Message::State::error) { - if (data.error > 0) { + if (data.error > 0) tt += ": " + data.error; - } } if (result->toolTip() != tt) { //If i just assign pixmap every time unconditionally result->setPixmap(q.pixmap(statusIconSize)); //it invokes an infinite cycle of repaint @@ -692,8 +657,7 @@ QLabel * MessageDelegate::getStatusIcon(const Models::FeedItem& data) const return result; } -QLabel * MessageDelegate::getPencilIcon(const Models::FeedItem& data) const -{ +QLabel * MessageDelegate::getPencilIcon(const Models::FeedItem& data) const { std::map::const_iterator itr = pencilIcons->find(data.id); QLabel* result = 0; @@ -755,14 +719,12 @@ int MessageDelegate::paintBody(const Models::FeedItem& data, QPainter* painter, return 0; } -void MessageDelegate::beginClearWidgets() -{ +void MessageDelegate::beginClearWidgets() { idsToKeep->clear(); clearingWidgets = true; } -void MessageDelegate::endClearWidgets() -{ +void MessageDelegate::endClearWidgets() { if (clearingWidgets) { removeElements(buttons, idsToKeep); removeElements(bars, idsToKeep); @@ -775,14 +737,12 @@ void MessageDelegate::endClearWidgets() } } -void MessageDelegate::onButtonPushed() const -{ +void MessageDelegate::onButtonPushed() const { FeedButton* btn = static_cast(sender()); emit buttonPushed(btn->messageId); } -void MessageDelegate::clearHelperWidget(const Models::FeedItem& data) const -{ +void MessageDelegate::clearHelperWidget(const Models::FeedItem& data) const { std::map::const_iterator itr = buttons->find(data.id); if (itr != buttons->end()) { delete itr->second; diff --git a/ui/widgets/messageline/messagedelegate.h b/ui/widgets/messageline/messagedelegate.h index a05b4cd..16e39ff 100644 --- a/ui/widgets/messageline/messagedelegate.h +++ b/ui/widgets/messageline/messagedelegate.h @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include "shared/icons.h" #include "shared/global.h" diff --git a/ui/widgets/messageline/messagefeed.cpp b/ui/widgets/messageline/messagefeed.cpp index af772fd..1eba1fa 100644 --- a/ui/widgets/messageline/messagefeed.cpp +++ b/ui/widgets/messageline/messagefeed.cpp @@ -20,6 +20,7 @@ #include #include +#include #include @@ -228,24 +229,20 @@ std::set Models::MessageFeed::detectChanges(c return roles; } -void Models::MessageFeed::removeMessage(const QString& id) -{ +void Models::MessageFeed::removeMessage(const QString& id) { //todo; } -Shared::Message Models::MessageFeed::getMessage(const QString& id) -{ +Shared::Message Models::MessageFeed::getMessage(const QString& id) { StorageById::iterator itr = indexById.find(id); - if (itr == indexById.end()) { + if (itr == indexById.end()) throw NotFound(id.toStdString(), rosterItem->getJid().toStdString(), rosterItem->getAccountName().toStdString()); - } return **itr; } -QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const -{ +QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const { int i = index.row(); QVariant answer; @@ -266,11 +263,10 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const if (sentByMe(*msg)) { answer = rosterItem->getAccountName(); } else { - if (rosterItem->isRoom()) { + if (rosterItem->isRoom()) answer = msg->getFromResource(); - } else { + else answer = rosterItem->getDisplayedName(); - } } break; case Date: @@ -290,19 +286,17 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const if (sentByMe(*msg)) { path = rosterItem->getAccountAvatarPath(); } else if (!rosterItem->isRoom()) { - if (rosterItem->getAvatarState() != Shared::Avatar::empty) { + if (rosterItem->getAvatarState() != Shared::Avatar::empty) path = rosterItem->getAvatarPath(); - } } else { const Room* room = static_cast(rosterItem); path = room->getParticipantIconPath(msg->getFromResource()); } - if (path.size() == 0) { + if (path.size() == 0) answer = Shared::iconPath("user", true); - } else { + else answer = path; - } } break; case Attach: @@ -342,15 +336,14 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const item.avatar = room->getParticipantIconPath(msg->getFromResource()); } else { item.sender = rosterItem->getDisplayedName(); - if (rosterItem->getAvatarState() != Shared::Avatar::empty) { + if (rosterItem->getAvatarState() != Shared::Avatar::empty) item.avatar = rosterItem->getAvatarPath(); - } } } - if (item.avatar.size() == 0) { + if (item.avatar.size() == 0) item.avatar = Shared::iconPath("user", true); - } + item.attach = fillAttach(*msg); answer.setValue(item); } @@ -363,13 +356,12 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const return answer; } -int Models::MessageFeed::rowCount(const QModelIndex& parent) const -{ +int Models::MessageFeed::rowCount(const QModelIndex& parent) const { + SHARED_UNUSED(parent); return storage.size(); } -bool Models::MessageFeed::markMessageAsRead(const QString& id) const -{ +bool Models::MessageFeed::markMessageAsRead(const QString& id) const { std::set::const_iterator umi = unreadMessages->find(id); if (umi != unreadMessages->end()) { unreadMessages->erase(umi); @@ -379,32 +371,29 @@ bool Models::MessageFeed::markMessageAsRead(const QString& id) const return false; } -unsigned int Models::MessageFeed::unreadMessagesCount() const -{ +unsigned int Models::MessageFeed::unreadMessagesCount() const { return unreadMessages->size(); } -bool Models::MessageFeed::canFetchMore(const QModelIndex& parent) const -{ +bool Models::MessageFeed::canFetchMore(const QModelIndex& parent) const { + SHARED_UNUSED(parent); return syncState == incomplete; } -void Models::MessageFeed::fetchMore(const QModelIndex& parent) -{ +void Models::MessageFeed::fetchMore(const QModelIndex& parent) { + SHARED_UNUSED(parent); if (syncState == incomplete) { syncState = syncing; emit syncStateChange(syncState); - if (storage.size() == 0) { + if (storage.size() == 0) emit requestArchive(""); - } else { + else emit requestArchive((*indexByTime.rbegin())->getId()); - } } } -void Models::MessageFeed::responseArchive(const std::list list, bool last) -{ +void Models::MessageFeed::responseArchive(const std::list list, bool last) { Storage::size_type size = storage.size(); beginInsertRows(QModelIndex(), size, size + list.size() - 1); @@ -415,21 +404,19 @@ void Models::MessageFeed::responseArchive(const std::list list, endInsertRows(); if (syncState == syncing) { - if (last) { + if (last) syncState = complete; - } else { + else syncState = incomplete; - } + emit syncStateChange(syncState); } } -QModelIndex Models::MessageFeed::index(int row, int column, const QModelIndex& parent) const -{ - if (!hasIndex(row, column, parent)) { +QModelIndex Models::MessageFeed::index(int row, int column, const QModelIndex& parent) const{ + if (!hasIndex(row, column, parent)) return QModelIndex(); - } - + StorageByTime::iterator itr = indexByTime.nth(row); if (itr != indexByTime.end()) { Shared::Message* msg = *itr; @@ -442,8 +429,7 @@ QModelIndex Models::MessageFeed::index(int row, int column, const QModelIndex& p QHash Models::MessageFeed::roleNames() const {return roles;} -bool Models::MessageFeed::sentByMe(const Shared::Message& msg) const -{ +bool Models::MessageFeed::sentByMe(const Shared::Message& msg) const { if (rosterItem->isRoom()) { const Room* room = static_cast(rosterItem); return room->getNick().toLower() == msg.getFromResource().toLower(); @@ -452,8 +438,7 @@ bool Models::MessageFeed::sentByMe(const Shared::Message& msg) const } } -Models::Attachment Models::MessageFeed::fillAttach(const Shared::Message& msg) const -{ +Models::Attachment Models::MessageFeed::fillAttach(const Shared::Message& msg) const { ::Models::Attachment att; QString id = msg.getId(); @@ -501,16 +486,14 @@ Models::Attachment Models::MessageFeed::fillAttach(const Shared::Message& msg) c return att; } -Models::Edition Models::MessageFeed::fillCorrection(const Shared::Message& msg) const -{ +Models::Edition Models::MessageFeed::fillCorrection(const Shared::Message& msg) const { ::Models::Edition ed({msg.getEdited(), msg.getOriginalBody(), msg.getLastModified()}); return ed; } -void Models::MessageFeed::downloadAttachment(const QString& messageId) -{ +void Models::MessageFeed::downloadAttachment(const QString& messageId) { bool notify = false; Err::const_iterator eitr = failedDownloads.find(messageId); if (eitr != failedDownloads.end()) { @@ -532,13 +515,11 @@ void Models::MessageFeed::downloadAttachment(const QString& messageId) qDebug() << "An attempt to download an attachment for the message that doesn't exist. ID:" << messageId; } - if (notify) { + if (notify) emit dataChanged(ind, ind, {MessageRoles::Attach}); - } } -bool Models::MessageFeed::registerUpload(const QString& messageId) -{ +bool Models::MessageFeed::registerUpload(const QString& messageId) { bool success = uploads.insert(std::make_pair(messageId, 0)).second; QVector roles({}); @@ -556,8 +537,7 @@ bool Models::MessageFeed::registerUpload(const QString& messageId) return success; } -void Models::MessageFeed::fileProgress(const QString& messageId, qreal value, bool up) -{ +void Models::MessageFeed::fileProgress(const QString& messageId, qreal value, bool up) { Progress* pr = 0; Err* err = 0; if (up) { @@ -583,13 +563,11 @@ void Models::MessageFeed::fileProgress(const QString& messageId, qreal value, bo } } -void Models::MessageFeed::fileComplete(const QString& messageId, bool up) -{ +void Models::MessageFeed::fileComplete(const QString& messageId, bool up) { fileProgress(messageId, 1, up); } -void Models::MessageFeed::fileError(const QString& messageId, const QString& error, bool up) -{ +void Models::MessageFeed::fileError(const QString& messageId, const QString& error, bool up) { Err* failed; Progress* loads; if (up) { @@ -601,33 +579,28 @@ void Models::MessageFeed::fileError(const QString& messageId, const QString& err } Progress::iterator pitr = loads->find(messageId); - if (pitr != loads->end()) { + if (pitr != loads->end()) loads->erase(pitr); - } std::pair pair = failed->insert(std::make_pair(messageId, error)); - if (!pair.second) { + if (!pair.second) pair.first->second = error; - } + QModelIndex ind = modelIndexById(messageId); - if (ind.isValid()) { + if (ind.isValid()) emit dataChanged(ind, ind, {MessageRoles::Attach}); - } } -void Models::MessageFeed::incrementObservers() -{ +void Models::MessageFeed::incrementObservers() { ++observersAmount; } -void Models::MessageFeed::decrementObservers() -{ +void Models::MessageFeed::decrementObservers() { --observersAmount; } -QModelIndex Models::MessageFeed::modelIndexById(const QString& id) const -{ +QModelIndex Models::MessageFeed::modelIndexById(const QString& id) const { StorageById::const_iterator itr = indexById.find(id); if (itr != indexById.end()) { Shared::Message* msg = *itr; @@ -637,8 +610,7 @@ QModelIndex Models::MessageFeed::modelIndexById(const QString& id) const return QModelIndex(); } -QModelIndex Models::MessageFeed::modelIndexByTime(const QString& id, const QDateTime& time) const -{ +QModelIndex Models::MessageFeed::modelIndexByTime(const QString& id, const QDateTime& time) const { if (indexByTime.size() > 0) { StorageByTime::const_iterator tItr = indexByTime.lower_bound(time); StorageByTime::const_iterator tEnd = indexByTime.upper_bound(time); @@ -660,8 +632,7 @@ QModelIndex Models::MessageFeed::modelIndexByTime(const QString& id, const QDate return QModelIndex(); } -void Models::MessageFeed::reportLocalPathInvalid(const QString& messageId) -{ +void Models::MessageFeed::reportLocalPathInvalid(const QString& messageId) { StorageById::iterator itr = indexById.find(messageId); if (itr == indexById.end()) { qDebug() << "received a command to change a message, but the message couldn't be found, skipping"; @@ -679,13 +650,11 @@ void Models::MessageFeed::reportLocalPathInvalid(const QString& messageId) emit dataChanged(index, index, {MessageRoles::Attach}); } -Models::MessageFeed::SyncState Models::MessageFeed::getSyncState() const -{ +Models::MessageFeed::SyncState Models::MessageFeed::getSyncState() const { return syncState; } -void Models::MessageFeed::requestLatestMessages() -{ +void Models::MessageFeed::requestLatestMessages() { if (syncState != syncing) { syncState = syncing; emit syncStateChange(syncState); diff --git a/ui/widgets/room.cpp b/ui/widgets/room.cpp index 91d7255..3409a86 100644 --- a/ui/widgets/room.cpp +++ b/ui/widgets/room.cpp @@ -31,12 +31,10 @@ Room::Room(Models::Account* acc, Models::Room* p_room, QWidget* parent): connect(room, &Models::Room::participantLeft, this, &Room::onParticipantLeft); } -Room::~Room() -{ +Room::~Room() { } -Shared::Message Room::createMessage() const -{ +Shared::Message Room::createMessage() const { Shared::Message msg = Conversation::createMessage(); msg.setType(Shared::Message::groupChat); msg.setFromJid(room->getJid()); @@ -45,13 +43,12 @@ Shared::Message Room::createMessage() const return msg; } -bool Room::autoJoined() const -{ +bool Room::autoJoined() const { return room->getAutoJoin(); } -void Room::onRoomChanged(Models::Item* item, int row, int col) -{ +void Room::onRoomChanged(Models::Item* item, int row, int col) { + SHARED_UNUSED(row); if (item == room) { switch (col) { case 0: @@ -67,11 +64,10 @@ void Room::onRoomChanged(Models::Item* item, int row, int col) } } -void Room::onParticipantJoined(const Models::Participant& participant) -{ - +void Room::onParticipantJoined(const Models::Participant& participant) { + SHARED_UNUSED(participant); } -void Room::onParticipantLeft(const QString& name) -{ +void Room::onParticipantLeft(const QString& name) { + SHARED_UNUSED(name); } From 9d688e85964c64c20d59fa40d87ce69ea66bd0e1 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 2 Nov 2023 19:55:11 -0300 Subject: [PATCH 108/137] full transition to lmdbal, DOESNT WORK, DONT TAKE! --- CMakeLists.txt | 11 +- core/account.h | 2 +- core/components/urlstorage.cpp | 96 +-- core/components/urlstorage.h | 2 +- core/handlers/messagehandler.cpp | 2 +- core/handlers/omemohandler.h | 2 +- core/rosteritem.cpp | 34 +- core/storage/archive.cpp | 1181 +++++++-------------------- core/storage/archive.h | 143 +--- core/storage/cache.h | 55 -- core/storage/cache.hpp | 102 --- core/storage/storage.h | 70 -- core/storage/storage.hpp | 226 ----- external/qxmpp | 2 +- external/simpleCrypt/CMakeLists.txt | 2 +- main/main.cpp | 2 +- shared/message.cpp | 306 +++---- shared/message.h | 11 +- 18 files changed, 497 insertions(+), 1752 deletions(-) delete mode 100644 core/storage/cache.h delete mode 100644 core/storage/cache.hpp delete mode 100644 core/storage/storage.h delete mode 100644 core/storage/storage.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d29bb4f..5950c36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,7 @@ if (WITH_OMEMO) if (PKG_CONFIG_FOUND) pkg_check_modules(OMEMO libomemo-c) if (OMEMO_FOUND) + target_compile_definitions(squawk PRIVATE WITH_OMEMO) message("Building with support of OMEMO") else () message("libomemo-c package wasn't found, trying to build without OMEMO support") @@ -73,7 +74,11 @@ endif () ## QXmpp if (SYSTEM_QXMPP) - find_package(QXmpp CONFIG) + if (WITH_OMEMO) + find_package(QXmpp CONFIG COMPONENTS Omemo) + else () + find_package(QXmpp CONFIG) + endif () if (NOT QXmpp_FOUND) set(SYSTEM_QXMPP OFF) @@ -138,7 +143,6 @@ if (NOT SYSTEM_QXMPP) target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/omemo) target_include_directories(squawk PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/qxmpp/src/omemo) set(BUILD_OMEMO ON) - target_compile_definitions(squawk PRIVATE WITH_OMEMO) else () set(BUILD_OMEMO OFF) endif () @@ -150,6 +154,9 @@ if (NOT SYSTEM_QXMPP) endif () else () target_link_libraries(squawk PRIVATE QXmpp::QXmpp) + if (WITH_OMEMO) + target_link_libraries(squawk PRIVATE QXmpp::Omemo) + endif () endif () ## LMDBAL diff --git a/core/account.h b/core/account.h index fe3988c..a9425e7 100644 --- a/core/account.h +++ b/core/account.h @@ -63,7 +63,7 @@ #include "handlers/discoveryhandler.h" #ifdef WITH_OMEMO -#include +#include #include #include "handlers/trusthandler.h" #include "handlers/omemohandler.h" diff --git a/core/components/urlstorage.cpp b/core/components/urlstorage.cpp index c745236..31f36ad 100644 --- a/core/components/urlstorage.cpp +++ b/core/components/urlstorage.cpp @@ -41,18 +41,12 @@ void Core::UrlStorage::close() { } void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, bool overwrite) { - LMDBAL::TransactionID txn = base.beginTransaction(); - - try { - writeInfo(key, info, txn, overwrite); - base.commitTransaction(txn); - } catch (...) { - base.abortTransaction(txn); - throw; - } + LMDBAL::WriteTransaction txn = base.beginTransaction(); + writeInfo(key, info, txn, overwrite); + txn.commit(); } -void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, MDB_txn* txn, bool overwrite) { +void Core::UrlStorage::writeInfo(const QString& key, const Core::UrlStorage::UrlInfo& info, const LMDBAL::WriteTransaction& txn, bool overwrite) { if (overwrite) urlToInfo->forceRecord(key, info, txn); else @@ -95,15 +89,11 @@ Core::UrlStorage::UrlInfo Core::UrlStorage::addToInfo( const QString& path ) { UrlInfo info; - LMDBAL::TransactionID txn = base.beginTransaction(); + LMDBAL::WriteTransaction txn = base.beginTransaction(); try { urlToInfo->getRecord(url, info, txn); - } catch (const LMDBAL::NotFound& e) { - } catch (...) { - base.abortTransaction(txn); - throw; - } + } catch (const LMDBAL::NotFound& e) {} bool pathChange = false; bool listChange = false; @@ -118,15 +108,8 @@ Core::UrlStorage::UrlInfo Core::UrlStorage::addToInfo( listChange = info.addMessage(account, jid, id); if (pathChange || listChange) { - try { - writeInfo(url, info, txn, true); - base.commitTransaction(txn); - } catch (...) { - base.abortTransaction(txn); - throw; - } - } else { - base.abortTransaction(txn); + writeInfo(url, info, txn, true); + txn.commit(); } return info; @@ -134,70 +117,51 @@ Core::UrlStorage::UrlInfo Core::UrlStorage::addToInfo( std::list Core::UrlStorage::setPath(const QString& url, const QString& path) { std::list list; - LMDBAL::TransactionID txn = base.beginTransaction(); + LMDBAL::WriteTransaction txn = base.beginTransaction(); UrlInfo info; try { urlToInfo->getRecord(url, info, txn); info.getMessages(list); - } catch (const LMDBAL::NotFound& e) { - } catch (...) { - base.abortTransaction(txn); - throw; - } + } catch (const LMDBAL::NotFound& e) {} info.setPath(path); - try { - writeInfo(url, info, txn, true); - base.commitTransaction(txn); - } catch (...) { - base.abortTransaction(txn); - throw; - } + writeInfo(url, info, txn, true); + txn.commit(); return list; } std::list Core::UrlStorage::removeFile(const QString& url) { std::list list; - LMDBAL::TransactionID txn = base.beginTransaction(); + LMDBAL::WriteTransaction txn = base.beginTransaction(); UrlInfo info; - try { - urlToInfo->getRecord(url, info, txn); - urlToInfo->removeRecord(url); - info.getMessages(list); - - if (info.hasPath()) - pathToUrl->removeRecord(info.getPath()); + urlToInfo->getRecord(url, info, txn); + urlToInfo->removeRecord(url, txn); + info.getMessages(list); - base.commitTransaction(txn); - } catch (...) { - base.abortTransaction(txn); - throw; - } + if (info.hasPath()) + pathToUrl->removeRecord(info.getPath(), txn); + + txn.commit(); return list; } std::list Core::UrlStorage::deletedFile(const QString& path) { std::list list; - LMDBAL::TransactionID txn = base.beginTransaction(); + LMDBAL::WriteTransaction txn = base.beginTransaction(); - try { - QString url = pathToUrl->getRecord(path, txn); - pathToUrl->removeRecord(path); - - UrlInfo info = urlToInfo->getRecord(url, txn); - info.getMessages(list); - info.setPath(QString()); - urlToInfo->changeRecord(url, info, txn); - - base.commitTransaction(txn); - } catch (...) { - base.abortTransaction(txn); - throw; - } + QString url = pathToUrl->getRecord(path, txn); + pathToUrl->removeRecord(path, txn); + + UrlInfo info = urlToInfo->getRecord(url, txn); + info.getMessages(list); + info.setPath(QString()); + urlToInfo->changeRecord(url, info, txn); + + txn.commit(); return list; } diff --git a/core/components/urlstorage.h b/core/components/urlstorage.h index ee8e30d..fc9d71d 100644 --- a/core/components/urlstorage.h +++ b/core/components/urlstorage.h @@ -60,7 +60,7 @@ private: private: void writeInfo(const QString& key, const UrlInfo& info, bool overwrite = false); - void writeInfo(const QString& key, const UrlInfo& info, MDB_txn* txn, bool overwrite = false); + void writeInfo(const QString& key, const UrlInfo& info, const LMDBAL::WriteTransaction& txn, bool overwrite = false); UrlInfo addToInfo(const QString& url, const QString& account, const QString& jid, const QString& id, const QString& path = "-s"); public: diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index 3ed8dec..2632848 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -591,7 +591,7 @@ void Core::MessageHandler::resendMessage(const QString& jid, const QString& id) } else { qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message seems to have been normally sent, this method was made to retry sending failed to be sent messages, skipping"; } - } catch (const Archive::NotFound& err) { + } catch (const LMDBAL::NotFound& err) { qDebug() << "An attempt to resend a message to" << jid << "by account" << acc->getName() << ", but this message wasn't found in history, skipping"; } } else { diff --git a/core/handlers/omemohandler.h b/core/handlers/omemohandler.h index 1ea1f26..e626b4c 100644 --- a/core/handlers/omemohandler.h +++ b/core/handlers/omemohandler.h @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include diff --git a/core/rosteritem.cpp b/core/rosteritem.cpp index 545e47f..afbf836 100644 --- a/core/rosteritem.cpp +++ b/core/rosteritem.cpp @@ -27,7 +27,7 @@ Core::RosterItem::RosterItem(const QString& pJid, const QString& pAccount, QObje account(pAccount), name(), archiveState(empty), - archive(new Archive(jid)), + archive(new Archive(account, jid)), syncronizing(false), requestedCount(0), requestedBefore(), @@ -38,7 +38,7 @@ Core::RosterItem::RosterItem(const QString& pJid, const QString& pAccount, QObje toCorrect(), muc(false) { - archive->open(account); + archive->open(); if (archive->size() != 0) { if (archive->isFromTheBeginning()) @@ -126,7 +126,8 @@ void Core::RosterItem::nextRequest() { last = true; } } - } catch (const Archive::Empty& e) { + //} catch (const Archive::Empty& e) { + } catch (const LMDBAL::NotFound& e) { last = true; } } else if (archiveState == empty && responseCache.size() == 0) { @@ -168,7 +169,8 @@ void Core::RosterItem::performRequest(int count, const QString& before) { try { Shared::Message msg = archive->newest(); emit needHistory("", getId(msg), msg.getTime()); - } catch (const Archive::Empty& e) { //this can happen when the only message in archive is not server stored (error, for example) + //} catch (const Archive::Empty& e) { + } catch (const LMDBAL::NotFound& e) { //this can happen when the only message in archive is not server stored (error, for example) emit needHistory(before, ""); } } @@ -186,14 +188,14 @@ void Core::RosterItem::performRequest(int count, const QString& before) { std::list arc = archive->getBefore(requestedCount - responseCache.size(), lBefore); responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); found = true; - } catch (const Archive::NotFound& e) { - requestCache.emplace_back(requestedCount, before); - requestedCount = -1; - emit needHistory(getId(archive->oldest()), ""); - } catch (const Archive::Empty& e) { + } catch (const LMDBAL::NotFound& e) { requestCache.emplace_back(requestedCount, before); requestedCount = -1; emit needHistory(getId(archive->oldest()), ""); + // } catch (const Archive::Empty& e) { + // requestCache.emplace_back(requestedCount, before); + // requestedCount = -1; + // emit needHistory(getId(archive->oldest()), ""); } if (found) { @@ -226,10 +228,10 @@ void Core::RosterItem::performRequest(int count, const QString& before) { try { std::list arc = archive->getBefore(requestedCount - responseCache.size(), before); responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); - } catch (const Archive::NotFound& e) { - qDebug("requesting id hasn't been found in archive, skipping"); - } catch (const Archive::Empty& e) { + } catch (const LMDBAL::NotFound& e) { qDebug("requesting id hasn't been found in archive, skipping"); + // } catch (const Archive::Empty& e) { + // qDebug("requesting id hasn't been found in archive, skipping"); } nextRequest(); break; @@ -311,7 +313,7 @@ bool Core::RosterItem::changeMessage(const QString& id, const QMapchangeMessage(id, data); found = true; - } catch (const Archive::NotFound& e) { + } catch (const LMDBAL::NotFound& e) { qDebug() << "An attempt to change state to the message" << id << "but it couldn't be found"; } } @@ -387,10 +389,8 @@ void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firs std::list arc = archive->getBefore(requestedCount - responseCache.size(), before); responseCache.insert(responseCache.begin(), arc.begin(), arc.end()); found = true; - } catch (const Archive::NotFound& e) { - - } catch (const Archive::Empty& e) { - + } catch (const LMDBAL::NotFound& e) { + // } catch (const Archive::Empty& e) { } if (!found || requestedCount > int(responseCache.size())) { if (archiveState == complete) { diff --git a/core/storage/archive.cpp b/core/storage/archive.cpp index 8330cff..6b00f37 100644 --- a/core/storage/archive.cpp +++ b/core/storage/archive.cpp @@ -24,858 +24,310 @@ #include #include -Core::Archive::Archive(const QString& p_jid, QObject* parent): +Core::Archive::Archive(const QString& account, const QString& p_jid, QObject* parent): QObject(parent), jid(p_jid), + account(account), opened(false), - fromTheBeginning(false), - encryptionEnabled(false), - environment(), - main(), - order(), - stats(), - avatars() -{ -} + db(account + "/" + jid), + messages(db.addStorage("messages")), + order(db.addStorage("order")), + stats(db.addStorage("stats")), + avatars(db.addStorage("avatars")), + stanzaIdToId(db.addStorage("stanzaIdToId")), + cursor(order->createCursor()) +{} -Core::Archive::~Archive() -{ +Core::Archive::~Archive() { close(); } -void Core::Archive::open(const QString& account) -{ - if (!opened) { - mdb_env_create(&environment); - QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); - path += "/" + account + "/" + jid; - QDir cache(path); - - if (!cache.exists()) { - bool res = cache.mkpath(path); - if (!res) { - throw Directory(path.toStdString()); - } - } - - mdb_env_set_maxdbs(environment, 5); - mdb_env_set_mapsize(environment, -#ifdef Q_OS_WIN - // On Windows, the file is immediately allocated. - // So we have to limit the size. - 80UL * 1024UL * 1024UL -#else - 512UL * 1024UL * 1024UL -#endif - ); - mdb_env_open(environment, path.toStdString().c_str(), 0, 0664); - - MDB_txn *txn; - mdb_txn_begin(environment, NULL, 0, &txn); - mdb_dbi_open(txn, "main", MDB_CREATE, &main); - mdb_dbi_open(txn, "order", MDB_CREATE | MDB_INTEGERKEY | MDB_INTEGERDUP | MDB_DUPSORT, &order); - mdb_dbi_open(txn, "stats", MDB_CREATE, &stats); - mdb_dbi_open(txn, "avatars", MDB_CREATE, &avatars); - mdb_dbi_open(txn, "sid", MDB_CREATE, &sid); - mdb_txn_commit(txn); - - mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); - try { - fromTheBeginning = getStatBoolValue("beginning", txn); - } catch (const NotFound& e) { - fromTheBeginning = false; - } +void Core::Archive::open() { + db.open(); + LMDBAL::WriteTransaction txn = db.beginTransaction(); - try { - encryptionEnabled = getStatBoolValue("encryptionEnabled", txn); - } catch (const NotFound& e) { - encryptionEnabled = false; - } - - std::string sJid = jid.toStdString(); - AvatarInfo info; - bool hasAvatar = readAvatarInfo(info, sJid, txn); - mdb_txn_abort(txn); - - if (hasAvatar) { - QFile ava(path + "/" + sJid.c_str() + "." + info.type); - if (!ava.exists()) { - bool success = dropAvatar(sJid); - if (!success) { - qDebug() << "error opening archive" << jid << "for account" << account - << ". There is supposed to be avatar but the file doesn't exist, couldn't even drop it, it surely will lead to an error"; - } - } - } - - opened = true; - } -} - -void Core::Archive::close() -{ - if (opened) { - mdb_dbi_close(environment, sid); - mdb_dbi_close(environment, avatars); - mdb_dbi_close(environment, stats); - mdb_dbi_close(environment, order); - mdb_dbi_close(environment, main); - mdb_env_close(environment); - opened = false; - } -} - -bool Core::Archive::addElement(const Shared::Message& message) -{ - if (!opened) { - throw Closed("addElement", jid.toStdString()); - } - qDebug() << "Adding message with id " << message.getId(); - QByteArray ba; - QDataStream ds(&ba, QIODevice::WriteOnly); - message.serialize(ds); - quint64 stamp = message.getTime().toMSecsSinceEpoch(); - const std::string& id = message.getId().toStdString(); - - MDB_val lmdbKey, lmdbData; - lmdbKey.mv_size = id.size(); - lmdbKey.mv_data = (char*)id.c_str(); - lmdbData.mv_size = ba.size(); - lmdbData.mv_data = (uint8_t*)ba.data(); - MDB_txn *txn; - mdb_txn_begin(environment, NULL, 0, &txn); - int rc; - rc = mdb_put(txn, main, &lmdbKey, &lmdbData, MDB_NOOVERWRITE); - if (rc == 0) { - MDB_val orderKey; - orderKey.mv_size = 8; - orderKey.mv_data = (uint8_t*) &stamp; - - rc = mdb_put(txn, order, &orderKey, &lmdbKey, 0); - if (rc) { - qDebug() << "An element couldn't be inserted into the index" << mdb_strerror(rc); - mdb_txn_abort(txn); - return false; - } else { - if (message.getStanzaId().size() > 0) { - const std::string& szid = message.getStanzaId().toStdString(); - - lmdbKey.mv_size = szid.size(); - lmdbKey.mv_data = (char*)szid.c_str(); - lmdbData.mv_size = id.size(); - lmdbData.mv_data = (uint8_t*)id.data(); - rc = mdb_put(txn, sid, &lmdbKey, &lmdbData, MDB_NOOVERWRITE); - - if (rc) { - qDebug() << "An element stanzaId to id pair couldn't be inserted into the archive" << mdb_strerror(rc); - mdb_txn_abort(txn); - return false; - } else { - rc = mdb_txn_commit(txn); - if (rc) { - qDebug() << "A transaction error: " << mdb_strerror(rc); - return false; - } - return true; - } - - } else { - rc = mdb_txn_commit(txn); - if (rc) { - qDebug() << "A transaction error: " << mdb_strerror(rc); - return false; - } - return true; - } - } - } else { - qDebug() << "An element couldn't been added to the archive, skipping" << mdb_strerror(rc); - mdb_txn_abort(txn); - return false; - } -} - -void Core::Archive::clear() -{ - if (!opened) { - throw Closed("clear", jid.toStdString()); - } - - MDB_txn *txn; - mdb_txn_begin(environment, NULL, 0, &txn); - mdb_drop(txn, main, 0); - mdb_drop(txn, order, 0); - mdb_drop(txn, stats, 0); - mdb_drop(txn, avatars, 0); - mdb_drop(txn, sid, 0); - mdb_txn_commit(txn); -} - -Shared::Message Core::Archive::getElement(const QString& id) const -{ - if (!opened) { - throw Closed("getElement", jid.toStdString()); - } - - MDB_txn *txn; - mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); - + AvatarInfo info; + bool hasAvatar = false; try { - Shared::Message msg = getMessage(id.toStdString(), txn); - mdb_txn_abort(txn); - return msg; + avatars->getRecord(jid, info, txn); + hasAvatar = true; + } catch (const LMDBAL::NotFound& e) {} + + if (!hasAvatar) + return; + + QFile ava(db.getPath() + "/" + jid + "." + info.type); + if (ava.exists()) + return; + + try { + avatars->removeRecord(jid, txn); + txn.commit(); + } catch (const std::exception& e) { + qDebug() << e.what(); + qDebug() << "error opening archive" << jid << "for account" << account + << ". There is supposed to be avatar but the file doesn't exist, couldn't even drop it, it surely will lead to an error"; + } +} + +void Core::Archive::close() { + db.close(); +} + +bool Core::Archive::addElement(const Shared::Message& message) { + QString id = message.getId(); + qDebug() << "Adding message with id " << id; + + try { + LMDBAL::WriteTransaction txn = db.beginTransaction(); + messages->addRecord(id, message, txn); + order->addRecord(message.getTime().toMSecsSinceEpoch(), id, txn); + QString stanzaId = message.getStanzaId(); + if (!stanzaId.isEmpty()) + stanzaIdToId->addRecord(stanzaId, id); + + txn.commit(); + return true; + } catch (const std::exception& e) { + qDebug() << "Could not add message with id " + id; + qDebug() << e.what(); + } + + return false; +} + +void Core::Archive::clear() { + db.drop(); +} + +Shared::Message Core::Archive::getElement(const QString& id) const { + return messages->getRecord(id); +} + +bool Core::Archive::hasElement(const QString& id) const { + return messages->checkRecord(id); +} + +void Core::Archive::changeMessage(const QString& id, const QMap& data) { + LMDBAL::WriteTransaction txn = db.beginTransaction(); + Shared::Message msg = messages->getRecord(id, txn); + + bool hadStanzaId = !msg.getStanzaId().isEmpty(); + QDateTime oTime = msg.getTime(); + bool idChange = msg.change(data); + QString newId = msg.getId(); + QDateTime nTime = msg.getTime(); + + bool orderChange = oTime != nTime; + if (idChange || orderChange) { + if (idChange) + messages->removeRecord(id, txn); + + if (orderChange) + order->removeRecord(oTime.toMSecsSinceEpoch(), txn); + + order->forceRecord(nTime.toMSecsSinceEpoch(), newId, txn); + } + + QString sid = msg.getStanzaId(); + if (!sid.isEmpty() && (idChange || !hadStanzaId)) + stanzaIdToId->forceRecord(sid, newId, txn); + + messages->forceRecord(newId, msg, txn); + txn.commit(); +} + +Shared::Message Core::Archive::newest() const { + LMDBAL::Transaction txn = db.beginReadOnlyTransaction(); + + try { + cursor.open(txn); + while (true) { + std::pair pair = cursor.prev(); + Shared::Message msg = messages->getRecord(pair.second, txn); + if (msg.serverStored()) { + cursor.close(); + return msg; + } + } } catch (...) { - mdb_txn_abort(txn); + cursor.close(); throw; } } -bool Core::Archive::hasElement(const QString& id) const -{ - if (!opened) { - throw Closed("hasElement", jid.toStdString()); - } - - MDB_txn *txn; - mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); - - bool has; - MDB_val lmdbKey, lmdbData; - lmdbKey.mv_size = id.size(); - lmdbKey.mv_data = (char*)id.toStdString().c_str(); - int rc = mdb_get(txn, main, &lmdbKey, &lmdbData); - has = rc == 0; - mdb_txn_abort(txn); - - return has; -} - -Shared::Message Core::Archive::getMessage(const std::string& id, MDB_txn* txn) const -{ - MDB_val lmdbKey, lmdbData; - lmdbKey.mv_size = id.size(); - lmdbKey.mv_data = (char*)id.c_str(); - int rc = mdb_get(txn, main, &lmdbKey, &lmdbData); - - if (rc == 0) { - QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size); - QDataStream ds(&ba, QIODevice::ReadOnly); - - Shared::Message msg; - msg.deserialize(ds); - - return msg; - } else if (rc == MDB_NOTFOUND) { - throw NotFound(id, jid.toStdString()); - } else { - throw Unknown(jid.toStdString(), mdb_strerror(rc)); - } -} - -void Core::Archive::changeMessage(const QString& id, const QMap& data) -{ - if (!opened) { - throw Closed("setMessageState", jid.toStdString()); - } - - MDB_txn *txn; - mdb_txn_begin(environment, NULL, 0, &txn); - - std::string strId(id.toStdString()); - try { - Shared::Message msg = getMessage(strId, txn); - bool hadStanzaId = msg.getStanzaId().size() > 0; - QDateTime oTime = msg.getTime(); - bool idChange = msg.change(data); - QDateTime nTime = msg.getTime(); - bool orderChange = oTime != nTime; - - MDB_val lmdbKey, lmdbData; - QByteArray ba; - QDataStream ds(&ba, QIODevice::WriteOnly); - msg.serialize(ds); - - lmdbKey.mv_size = strId.size(); - lmdbKey.mv_data = (char*)strId.c_str(); - int rc; - if (idChange || orderChange) { - if (idChange) { - rc = mdb_del(txn, main, &lmdbKey, &lmdbData); - } else { - quint64 ostamp = oTime.toMSecsSinceEpoch(); - lmdbData.mv_data = (quint8*)&ostamp; - lmdbData.mv_size = 8; - rc = mdb_del(txn, order, &lmdbData, &lmdbKey); - } - if (rc == 0) { - strId = msg.getId().toStdString(); - lmdbKey.mv_size = strId.size(); - lmdbKey.mv_data = (char*)strId.c_str(); - - quint64 stamp = nTime.toMSecsSinceEpoch(); - lmdbData.mv_data = (quint8*)&stamp; - lmdbData.mv_size = 8; - rc = mdb_put(txn, order, &lmdbData, &lmdbKey, 0); - if (rc != 0) { - throw Unknown(jid.toStdString(), mdb_strerror(rc)); - } - } else { - throw Unknown(jid.toStdString(), mdb_strerror(rc)); - } - } - - QString qsid = msg.getStanzaId(); - if (qsid.size() > 0 && (idChange || !hadStanzaId)) { - std::string szid = qsid.toStdString(); - - lmdbData.mv_size = szid.size(); - lmdbData.mv_data = (char*)szid.c_str(); - rc = mdb_put(txn, sid, &lmdbData, &lmdbKey, 0); - - if (rc != 0) { - throw Unknown(jid.toStdString(), mdb_strerror(rc)); - } - }; - - lmdbData.mv_size = ba.size(); - lmdbData.mv_data = (uint8_t*)ba.data(); - rc = mdb_put(txn, main, &lmdbKey, &lmdbData, 0); - if (rc == 0) { - rc = mdb_txn_commit(txn); - } else { - throw Unknown(jid.toStdString(), mdb_strerror(rc)); - } - - } catch (...) { - mdb_txn_abort(txn); - throw; - } -} - -Shared::Message Core::Archive::newest() -{ - return edge(true); -} - -QString Core::Archive::newestId() -{ - if (!opened) { - throw Closed("newestId", jid.toStdString()); - } +QString Core::Archive::newestId() const { Shared::Message msg = newest(); return msg.getId(); } -QString Core::Archive::oldestId() -{ - if (!opened) { - throw Closed("oldestId", jid.toStdString()); - } +QString Core::Archive::oldestId() const { Shared::Message msg = oldest(); return msg.getId(); } -Shared::Message Core::Archive::oldest() -{ - return edge(false); -} +Shared::Message Core::Archive::oldest() const { + LMDBAL::Transaction txn = db.beginReadOnlyTransaction(); -Shared::Message Core::Archive::edge(bool end) -{ - QString name; - MDB_cursor_op begin; - MDB_cursor_op iteration; - if (end) { - name = "newest"; - begin = MDB_LAST; - iteration = MDB_PREV; - } else { - name = "oldest"; - begin = MDB_FIRST; - iteration = MDB_NEXT; - } - - - if (!opened) { - throw Closed(name.toStdString(), jid.toStdString()); - } - - MDB_txn *txn; - MDB_cursor* cursor; - MDB_val lmdbKey, lmdbData; - int rc; - rc = mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); - rc = mdb_cursor_open(txn, order, &cursor); - rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, begin); - - Shared::Message msg = getStoredMessage(txn, cursor, iteration, &lmdbKey, &lmdbData, rc); - - mdb_cursor_close(cursor); - mdb_txn_abort(txn); - - if (rc) { - qDebug() << "Error geting" << name << "message" << mdb_strerror(rc); - throw Empty(jid.toStdString()); - } else { - return msg; - } -} - -Shared::Message Core::Archive::getStoredMessage(MDB_txn *txn, MDB_cursor* cursor, MDB_cursor_op op, MDB_val* key, MDB_val* value, int& rc) -{ - Shared::Message msg; - std::string sId; - while (true) { - if (rc) { - break; - } - sId = std::string((char*)value->mv_data, value->mv_size); - - try { - msg = getMessage(sId, txn); + try { + cursor.open(txn); + while (true) { + std::pair pair = cursor.next(); + Shared::Message msg = messages->getRecord(pair.second, txn); if (msg.serverStored()) { - break; - } else { - rc = mdb_cursor_get(cursor, key, value, op); + cursor.close(); + return msg; } - } catch (...) { - break; } + } catch (...) { + cursor.close(); + throw; } - - return msg; } -unsigned int Core::Archive::addElements(const std::list& messages) -{ - if (!opened) { - throw Closed("addElements", jid.toStdString()); - } - - int success = 0; - int rc = 0; - MDB_val lmdbKey, lmdbData; - MDB_txn *txn; - mdb_txn_begin(environment, NULL, 0, &txn); - std::list::const_iterator itr = messages.begin(); - while (rc == 0 && itr != messages.end()) { - const Shared::Message& message = *itr; - - QByteArray ba; - QDataStream ds(&ba, QIODevice::WriteOnly); - message.serialize(ds); - quint64 stamp = message.getTime().toMSecsSinceEpoch(); - const std::string& id = message.getId().toStdString(); - - lmdbKey.mv_size = id.size(); - lmdbKey.mv_data = (char*)id.c_str(); - lmdbData.mv_size = ba.size(); - lmdbData.mv_data = (uint8_t*)ba.data(); - - rc = mdb_put(txn, main, &lmdbKey, &lmdbData, MDB_NOOVERWRITE); - if (rc == 0) { - MDB_val orderKey; - orderKey.mv_size = 8; - orderKey.mv_data = (uint8_t*) &stamp; - - rc = mdb_put(txn, order, &orderKey, &lmdbKey, 0); - if (rc) { - qDebug() << "An element couldn't be inserted into the index, aborting the transaction" << mdb_strerror(rc); - } else { - if (message.getStanzaId().size() > 0) { - const std::string& szid = message.getStanzaId().toStdString(); - - lmdbKey.mv_size = szid.size(); - lmdbKey.mv_data = (char*)szid.c_str(); - lmdbData.mv_size = id.size(); - lmdbData.mv_data = (uint8_t*)id.data(); - rc = mdb_put(txn, sid, &lmdbKey, &lmdbData, MDB_NOOVERWRITE); - - if (rc) { - qDebug() << "During bulk add an element stanzaId to id pair couldn't be inserted into the archive, continuing without stanzaId" << mdb_strerror(rc); - } - - } - success++; - } - } else { - if (rc == MDB_KEYEXIST) { - rc = 0; - } else { - qDebug() << "An element couldn't been added to the archive, aborting the transaction" << mdb_strerror(rc); - } - } - itr++; - } - - if (rc != 0) { - mdb_txn_abort(txn); - success = 0; - } else { - mdb_txn_commit(txn); +unsigned int Core::Archive::addElements(const std::list& messages) { + unsigned int success = 0; + LMDBAL::WriteTransaction txn = db.beginTransaction(); + for (const Shared::Message& message : messages) { + QString id = message.getId(); + bool added = false; + try { + Core::Archive::messages->addRecord(id, message, txn); + added = true; + } catch (const LMDBAL::Exist& e) {} + + if (!added) + continue; + + order->addRecord(message.getTime().toMSecsSinceEpoch(), id); + + QString sid = message.getStanzaId(); + if (!sid.isEmpty()) + stanzaIdToId->addRecord(sid, id); + + ++success; } return success; } -long unsigned int Core::Archive::size() const -{ - if (!opened) { - throw Closed("size", jid.toStdString()); - } - MDB_txn *txn; - mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); - MDB_stat stat; - mdb_stat(txn, order, &stat); - size_t amount = stat.ms_entries; - mdb_txn_abort(txn); - return amount; +long unsigned int Core::Archive::size() const { + return order->count(); } -std::list Core::Archive::getBefore(int count, const QString& id) -{ - if (!opened) { - throw Closed("getBefore", jid.toStdString()); - } - std::list res; - MDB_cursor* cursor; - MDB_txn *txn; - MDB_val lmdbKey, lmdbData; - int rc; - rc = mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); - rc = mdb_cursor_open(txn, order, &cursor); - if (id == "") { - rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_LAST); - if (rc) { - qDebug() << "Error getting before" << mdb_strerror(rc) << ", id:" << id; - mdb_cursor_close(cursor); - mdb_txn_abort(txn); - - throw Empty(jid.toStdString()); - } - } else { - std::string stdId(id.toStdString()); - try { - Shared::Message msg = getMessage(stdId, txn); - quint64 stamp = msg.getTime().toMSecsSinceEpoch(); - lmdbKey.mv_data = (quint8*)&stamp; - lmdbKey.mv_size = 8; - - rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_SET); - - if (rc) { - qDebug() << "Error getting before: couldn't set " << mdb_strerror(rc); - throw NotFound(stdId, jid.toStdString()); - } else { - rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_PREV); - if (rc) { - qDebug() << "Error getting before, couldn't prev " << mdb_strerror(rc); - throw NotFound(stdId, jid.toStdString()); - } - } - - } catch (...) { - mdb_cursor_close(cursor); - mdb_txn_abort(txn); - throw; - } - } - - do { - MDB_val dKey, dData; - dKey.mv_size = lmdbData.mv_size; - dKey.mv_data = lmdbData.mv_data; - rc = mdb_get(txn, main, &dKey, &dData); - if (rc) { - qDebug() <<"Get error: " << mdb_strerror(rc); - std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size); - mdb_cursor_close(cursor); - mdb_txn_abort(txn); - throw NotFound(sId, jid.toStdString()); +std::list Core::Archive::getBefore(unsigned int count, const QString& id) { + LMDBAL::Transaction txn = db.beginReadOnlyTransaction(); + try { + cursor.open(txn); + if (id.isEmpty()) { + cursor.last(); } else { - QByteArray ba((char*)dData.mv_data, dData.mv_size); - QDataStream ds(&ba, QIODevice::ReadOnly); - + Shared::Message reference = messages->getRecord(id, txn); + uint64_t stamp = reference.getTime().toMSecsSinceEpoch(); + cursor.set(stamp); + cursor.prev(); + } + + std::list res; + for (unsigned int i = 0; i < count; ++i) { + std::pair pair; + if (i == 0) + cursor.current(pair.first, pair.second); + else + cursor.prev(pair.first, pair.second); + res.emplace_back(); Shared::Message& msg = res.back(); - msg.deserialize(ds); + messages->getRecord(pair.second, msg, txn); } - - --count; - - } while (count > 0 && mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_PREV) == 0); - - mdb_cursor_close(cursor); - mdb_txn_abort(txn); - return res; -} + cursor.close(); -bool Core::Archive::isFromTheBeginning() const -{ - if (!opened) { - throw Closed("isFromTheBeginning", jid.toStdString()); - } - return fromTheBeginning; -} - -void Core::Archive::setFromTheBeginning(bool is) -{ - if (!opened) { - throw Closed("setFromTheBeginning", jid.toStdString()); - } - if (fromTheBeginning != is) { - fromTheBeginning = is; - - MDB_txn *txn; - mdb_txn_begin(environment, NULL, 0, &txn); - bool success = setStatValue("beginning", is, txn); - if (success) { - mdb_txn_commit(txn); - } else { - mdb_txn_abort(txn); - } - } -} - -bool Core::Archive::isEncryptionEnabled() const -{ - if (!opened) { - throw Closed("isEncryptionEnabled", jid.toStdString()); - } - return encryptionEnabled; -} - -bool Core::Archive::setEncryptionEnabled(bool is) -{ - if (!opened) { - throw Closed("setEncryptionEnabled", jid.toStdString()); - } - if (encryptionEnabled != is) { - encryptionEnabled = is; - - MDB_txn *txn; - mdb_txn_begin(environment, NULL, 0, &txn); - bool success = setStatValue("encryptionEnabled", is, txn); - if (success) { - mdb_txn_commit(txn); - return true; - } else { - mdb_txn_abort(txn); - } - } - return false; -} - -QString Core::Archive::idByStanzaId(const QString& stanzaId) const -{ - if (!opened) { - throw Closed("idByStanzaId", jid.toStdString()); - } - QString id; - std::string ssid = stanzaId.toStdString(); - - MDB_txn *txn; - MDB_val lmdbKey, lmdbData; - lmdbKey.mv_size = ssid.size(); - lmdbKey.mv_data = (char*)ssid.c_str(); - mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); - int rc = mdb_get(txn, sid, &lmdbKey, &lmdbData); - if (rc == 0) { - id = QString::fromStdString(std::string((char*)lmdbData.mv_data, lmdbData.mv_size)); - } - mdb_txn_abort(txn); - - return id; -} - -QString Core::Archive::stanzaIdById(const QString& id) const -{ - if (!opened) { - throw Closed("stanzaIdById", jid.toStdString()); - } - - try { - Shared::Message msg = getElement(id); - return msg.getStanzaId(); - } catch (const NotFound& e) { - return QString(); - } catch (const Empty& e) { - return QString(); + return res; } catch (...) { + cursor.close(); throw; } } -void Core::Archive::printOrder() -{ - qDebug() << "Printing order"; - MDB_txn *txn; - mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); - MDB_cursor* cursor; - mdb_cursor_open(txn, order, &cursor); - MDB_val lmdbKey, lmdbData; - - mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST); - - do { - std::string sId((char*)lmdbData.mv_data, lmdbData.mv_size); - qDebug() << QString(sId.c_str()); - } while (mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_NEXT) == 0); - - mdb_cursor_close(cursor); - mdb_txn_abort(txn); -} - -void Core::Archive::printKeys() -{ - MDB_txn *txn; - mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); - MDB_cursor* cursor; - mdb_cursor_open(txn, main, &cursor); - MDB_val lmdbKey, lmdbData; - - mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST); - - do { - std::string sId((char*)lmdbKey.mv_data, lmdbKey.mv_size); - qDebug() << QString(sId.c_str()); - } while (mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_NEXT) == 0); - - mdb_cursor_close(cursor); - mdb_txn_abort(txn); -} - -bool Core::Archive::getStatBoolValue(const std::string& id, MDB_txn* txn) -{ - MDB_val lmdbKey, lmdbData; - lmdbKey.mv_size = id.size(); - lmdbKey.mv_data = (char*)id.c_str(); - - int rc; - rc = mdb_get(txn, stats, &lmdbKey, &lmdbData); - if (rc == MDB_NOTFOUND) { - throw NotFound(id, jid.toStdString()); - } else if (rc) { - std::string err(mdb_strerror(rc)); - qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << err.c_str(); - throw Unknown(jid.toStdString(), err); - } else { - uint8_t value = *(uint8_t*)(lmdbData.mv_data); - bool is; - if (value == 144) { - is = false; - } else if (value == 72) { - is = true; - } else { - qDebug() << "error retrieving boolean stat" << id.c_str() << ": stored value doesn't match any magic number, the answer is most probably wrong"; - throw NotFound(id, jid.toStdString()); - } - return is; - } -} - -std::string Core::Archive::getStatStringValue(const std::string& id, MDB_txn* txn) -{ - MDB_val lmdbKey, lmdbData; - lmdbKey.mv_size = id.size(); - lmdbKey.mv_data = (char*)id.c_str(); - - int rc; - rc = mdb_get(txn, stats, &lmdbKey, &lmdbData); - if (rc == MDB_NOTFOUND) { - throw NotFound(id, jid.toStdString()); - } else if (rc) { - std::string err(mdb_strerror(rc)); - qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << err.c_str(); - throw Unknown(jid.toStdString(), err); - } else { - std::string value((char*)lmdbData.mv_data, lmdbData.mv_size); - return value; - } -} - -bool Core::Archive::setStatValue(const std::string& id, bool value, MDB_txn* txn) -{ - uint8_t binvalue = 144; - if (value) { - binvalue = 72; - } - MDB_val lmdbKey, lmdbData; - lmdbKey.mv_size = id.size(); - lmdbKey.mv_data = (char*)id.c_str(); - lmdbData.mv_size = sizeof binvalue; - lmdbData.mv_data = &binvalue; - int rc = mdb_put(txn, stats, &lmdbKey, &lmdbData, 0); - if (rc != 0) { - qDebug() << "Couldn't store" << id.c_str() << "key into stat database:" << mdb_strerror(rc); +bool Core::Archive::isFromTheBeginning() const { + try { + return stats->getRecord("fromTheBeginning").toBool(); + } catch (const LMDBAL::NotFound& e) { return false; } - return true; } -bool Core::Archive::setStatValue(const std::string& id, const std::string& value, MDB_txn* txn) -{ - MDB_val lmdbKey, lmdbData; - lmdbKey.mv_size = id.size(); - lmdbKey.mv_data = (char*)id.c_str(); - lmdbData.mv_size = value.size(); - lmdbData.mv_data = (char*)value.c_str(); - int rc = mdb_put(txn, stats, &lmdbKey, &lmdbData, 0); - if (rc != 0) { - qDebug() << "Couldn't store" << id.c_str() << "key into stat database:" << mdb_strerror(rc); +void Core::Archive::setFromTheBeginning(bool is) { + stats->forceRecord("fromTheBeginning", is); +} + +bool Core::Archive::isEncryptionEnabled() const { + try { + return stats->getRecord("isEncryptionEnabled").toBool(); + } catch (const LMDBAL::NotFound& e) { return false; } - return true; } -bool Core::Archive::dropAvatar(const std::string& resource) -{ - MDB_txn *txn; - MDB_val lmdbKey; - mdb_txn_begin(environment, NULL, 0, &txn); - lmdbKey.mv_size = resource.size(); - lmdbKey.mv_data = (char*)resource.c_str(); - int rc = mdb_del(txn, avatars, &lmdbKey, NULL); - if (rc != 0) { - mdb_txn_abort(txn); - return false; - } else { - mdb_txn_commit(txn); +bool Core::Archive::setEncryptionEnabled(bool is) { + LMDBAL::WriteTransaction txn = db.beginTransaction(); + bool current = false; + try { + current = stats->getRecord("isEncryptionEnabled").toBool(); + } catch (const LMDBAL::NotFound& e) {} + + if (is != current) { + stats->forceRecord("isEncryptionEnabled", is, txn); + txn.commit(); return true; } + + return false; } -bool Core::Archive::setAvatar(const QByteArray& data, AvatarInfo& newInfo, bool generated, const QString& resource) -{ - if (!opened) { - throw Closed("setAvatar", jid.toStdString()); +QString Core::Archive::idByStanzaId(const QString& stanzaId) const { + return stanzaIdToId->getRecord(stanzaId); +} + +QString Core::Archive::stanzaIdById(const QString& id) const { + try { + Shared::Message msg = getElement(id); + return msg.getStanzaId(); + } catch (const LMDBAL::NotFound& e) { + return QString(); } - +} + +bool Core::Archive::setAvatar(const QByteArray& data, AvatarInfo& newInfo, bool generated, const QString& resource) { + LMDBAL::WriteTransaction txn = db.beginTransaction(); AvatarInfo oldInfo; - bool hasAvatar = readAvatarInfo(oldInfo, resource); - std::string res = resource.size() == 0 ? jid.toStdString() : resource.toStdString(); + bool haveAvatar = false; + QString res = resource.isEmpty() ? jid : resource; + try { + avatars->getRecord(res, oldInfo, txn); + haveAvatar = true; + } catch (const LMDBAL::NotFound& e) {} if (data.size() == 0) { - if (!hasAvatar) { + if (!haveAvatar) return false; - } else { - return dropAvatar(res); - } + + avatars->removeRecord(res, txn); + txn.commit(); + return true; } else { - const char* cep; - mdb_env_get_path(environment, &cep); - QString currentPath(cep); + QString currentPath = db.getPath(); bool needToRemoveOld = false; QCryptographicHash hash(QCryptographicHash::Sha1); hash.addData(data); QByteArray newHash(hash.result()); - if (hasAvatar) { - if (!generated && !oldInfo.autogenerated && oldInfo.hash == newHash) { + if (haveAvatar) { + if (!generated && !oldInfo.autogenerated && oldInfo.hash == newHash) return false; - } - QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type); + + QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type); if (oldAvatar.exists()) { - if (oldAvatar.rename(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak")) { + if (oldAvatar.rename(currentPath + "/" + res + "." + oldInfo.type + ".bak")) { needToRemoveOld = true; } else { qDebug() << "Can't change avatar: couldn't get rid of the old avatar" << oldAvatar.fileName(); @@ -883,161 +335,86 @@ bool Core::Archive::setAvatar(const QByteArray& data, AvatarInfo& newInfo, bool } } } - QMimeDatabase db; - QMimeType type = db.mimeTypeForData(data); + QMimeDatabase mimedb; + QMimeType type = mimedb.mimeTypeForData(data); QString ext = type.preferredSuffix(); - QFile newAvatar(currentPath + "/" + res.c_str() + "." + ext); + QFile newAvatar(currentPath + "/" + res + "." + ext); if (newAvatar.open(QFile::WriteOnly)) { newAvatar.write(data); newAvatar.close(); - MDB_txn *txn; - mdb_txn_begin(environment, NULL, 0, &txn); - - MDB_val lmdbKey, lmdbData; - QByteArray value; newInfo.type = ext; newInfo.hash = newHash; newInfo.autogenerated = generated; - newInfo.serialize(&value); - lmdbKey.mv_size = res.size(); - lmdbKey.mv_data = (char*)res.c_str(); - lmdbData.mv_size = value.size(); - lmdbData.mv_data = value.data(); - int rc = mdb_put(txn, avatars, &lmdbKey, &lmdbData, 0); - - if (rc != 0) { + try { + avatars->forceRecord(res, newInfo, txn); + txn.commit(); + } catch (...) { qDebug() << "Can't change avatar: couldn't store changes to database for" << newAvatar.fileName() << "rolling back to the previous state"; if (needToRemoveOld) { - QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak"); - oldAvatar.rename(currentPath + "/" + res.c_str() + "." + oldInfo.type); + QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type + ".bak"); + oldAvatar.rename(currentPath + "/" + res + "." + oldInfo.type); } - mdb_txn_abort(txn); return false; - } else { - mdb_txn_commit(txn); - if (needToRemoveOld) { - QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak"); - oldAvatar.remove(); - } - return true; } + + if (needToRemoveOld) { + QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type + ".bak"); + oldAvatar.remove(); + } + return true; } else { qDebug() << "Can't change avatar: cant open file to write" << newAvatar.fileName() << "rolling back to the previous state"; if (needToRemoveOld) { - QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak"); - oldAvatar.rename(currentPath + "/" + res.c_str() + "." + oldInfo.type); + QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type + ".bak"); + oldAvatar.rename(currentPath + "/" + res + "." + oldInfo.type); } return false; } } } -bool Core::Archive::readAvatarInfo(Core::Archive::AvatarInfo& target, const QString& resource) const -{ - if (!opened) { - throw Closed("readAvatarInfo", jid.toStdString()); - } - std::string res = resource.size() == 0 ? jid.toStdString() : resource.toStdString(); - - MDB_txn *txn; - mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); - +bool Core::Archive::readAvatarInfo(Core::Archive::AvatarInfo& target, const QString& resource) const { try { - bool success = readAvatarInfo(target, res, txn); - mdb_txn_abort(txn); - return success; - } catch (...) { - mdb_txn_abort(txn); - throw; - } - -} - -bool Core::Archive::readAvatarInfo(Core::Archive::AvatarInfo& target, const std::string& res, MDB_txn* txn) const -{ - MDB_val lmdbKey, lmdbData; - lmdbKey.mv_size = res.size(); - lmdbKey.mv_data = (char*)res.c_str(); - - int rc; - rc = mdb_get(txn, avatars, &lmdbKey, &lmdbData); - if (rc == MDB_NOTFOUND) { - return false; - } else if (rc) { - std::string err(mdb_strerror(rc)); - qDebug() << "error reading avatar info for" << res.c_str() << "resource of" << jid << err.c_str(); - throw Unknown(jid.toStdString(), err); - } else { - target.deserialize((char*)lmdbData.mv_data, lmdbData.mv_size); + avatars->getRecord(resource.isEmpty() ? jid : resource, target); return true; + } catch (const LMDBAL::NotFound& e) { + return false; } } -void Core::Archive::readAllResourcesAvatars(std::map& data) const -{ - if (!opened) { - throw Closed("readAllResourcesAvatars", jid.toStdString()); - } - - int rc; - MDB_val lmdbKey, lmdbData; - MDB_txn *txn; - MDB_cursor* cursor; - mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); - mdb_cursor_open(txn, avatars, &cursor); - rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST); - if (rc == 0) { //the db might be empty yet - do { - std::string sId((char*)lmdbKey.mv_data, lmdbKey.mv_size); - QString res(sId.c_str()); - if (res != jid) { - data.emplace(res, AvatarInfo()); - data[res].deserialize((char*)lmdbData.mv_data, lmdbData.mv_size); - } - } while (mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_NEXT) == 0); - } - - mdb_cursor_close(cursor); - mdb_txn_abort(txn); +void Core::Archive::readAllResourcesAvatars(std::map& data) const { + avatars->readAll(data); } -Core::Archive::AvatarInfo Core::Archive::getAvatarInfo(const QString& resource) const -{ - if (!opened) { - throw Closed("readAvatarInfo", jid.toStdString()); - } - - AvatarInfo info; - bool success = readAvatarInfo(info, resource); - if (success) { - return info; - } else { - throw NoAvatar(jid.toStdString(), resource.toStdString()); - } +Core::Archive::AvatarInfo Core::Archive::getAvatarInfo(const QString& resource) const { + return avatars->getRecord(resource); } Core::Archive::AvatarInfo::AvatarInfo(): -type(), -hash(), -autogenerated(false) {} + type(), + hash(), + autogenerated(false) +{} Core::Archive::AvatarInfo::AvatarInfo(const QString& p_type, const QByteArray& p_hash, bool p_autogenerated): -type(p_type), -hash(p_hash), -autogenerated(p_autogenerated) {} + type(p_type), + hash(p_hash), + autogenerated(p_autogenerated) +{} -void Core::Archive::AvatarInfo::deserialize(char* pointer, uint32_t size) -{ - QByteArray data = QByteArray::fromRawData(pointer, size); - QDataStream in(&data, QIODevice::ReadOnly); - - in >> type >> hash >> autogenerated; +QDataStream & operator<<(QDataStream& out, const Core::Archive::AvatarInfo& info) { + out << info.type; + out << info.hash; + out << info.autogenerated; + + return out; } -void Core::Archive::AvatarInfo::serialize(QByteArray* ba) const -{ - QDataStream out(ba, QIODevice::WriteOnly); - - out << type << hash << autogenerated; +QDataStream & operator>>(QDataStream& in, Core::Archive::AvatarInfo& info) { + in >> info.type; + in >> info.hash; + in >> info.autogenerated; + + return in; } diff --git a/core/storage/archive.h b/core/storage/archive.h index ef10555..6f3c9eb 100644 --- a/core/storage/archive.h +++ b/core/storage/archive.h @@ -29,18 +29,21 @@ #include #include +#include +#include +#include + namespace Core { -class Archive : public QObject -{ +class Archive : public QObject { Q_OBJECT public: class AvatarInfo; - Archive(const QString& jid, QObject* parent = 0); + Archive(const QString& account, const QString& jid, QObject* parent = 0); ~Archive(); - void open(const QString& account); + void open(); void close(); bool addElement(const Shared::Message& message); @@ -48,13 +51,13 @@ public: Shared::Message getElement(const QString& id) const; bool hasElement(const QString& id) const; void changeMessage(const QString& id, const QMap& data); - Shared::Message oldest(); - QString oldestId(); - Shared::Message newest(); - QString newestId(); + Shared::Message oldest() const; + QString oldestId() const; + Shared::Message newest() const; + QString newestId() const; void clear(); long unsigned int size() const; - std::list getBefore(int count, const QString& id); + std::list getBefore(unsigned int count, const QString& id); bool isFromTheBeginning() const; void setFromTheBeginning(bool is); bool isEncryptionEnabled() const; @@ -68,103 +71,14 @@ public: public: const QString jid; + const QString account; public: - class Directory: - public Utils::Exception - { - public: - Directory(const std::string& p_path):Exception(), path(p_path){} - - std::string getMessage() const{return "Can't create directory for database at " + path;} - private: - std::string path; - }; - - class Closed: - public Utils::Exception - { - public: - Closed(const std::string& op, const std::string& acc):Exception(), operation(op), account(acc){} - - std::string getMessage() const{return "An attempt to perform operation " + operation + " on closed archive for " + account;} - private: - std::string operation; - std::string account; - }; - - class NotFound: - public Utils::Exception - { - public: - NotFound(const std::string& k, const std::string& acc):Exception(), key(k), account(acc){} - - std::string getMessage() const{return "Element for id " + key + " wasn't found in database " + account;} - private: - std::string key; - std::string account; - }; - - class Empty: - public Utils::Exception - { - public: - Empty(const std::string& acc):Exception(), account(acc){} - - std::string getMessage() const{return "An attempt to read ordered elements from database " + account + " but it's empty";} - private: - std::string account; - }; - - class Exist: - public Utils::Exception - { - public: - Exist(const std::string& acc, const std::string& p_key):Exception(), account(acc), key(p_key){} - - std::string getMessage() const{return "An attempt to insert element " + key + " to database " + account + " but it already has an element with given id";} - private: - std::string account; - std::string key; - }; - - class NoAvatar: - public Utils::Exception - { - public: - NoAvatar(const std::string& el, const std::string& res):Exception(), element(el), resource(res){ - if (resource.size() == 0) { - resource = "for himself"; - } - } - - std::string getMessage() const{return "Element " + element + " has no avatar for " + resource ;} - private: - std::string element; - std::string resource; - }; - - class Unknown: - public Utils::Exception - { - public: - Unknown(const std::string& acc, const std::string& message):Exception(), account(acc), msg(message){} - - std::string getMessage() const{return "Unknown error on database " + account + ": " + msg;} - private: - std::string account; - std::string msg; - }; - - class AvatarInfo { public: AvatarInfo(); AvatarInfo(const QString& type, const QByteArray& hash, bool autogenerated); - void deserialize(char* pointer, uint32_t size); - void serialize(QByteArray* ba) const; - QString type; QByteArray hash; bool autogenerated; @@ -172,29 +86,18 @@ public: private: bool opened; - bool fromTheBeginning; - bool encryptionEnabled; - MDB_env* environment; - MDB_dbi main; //id to message - MDB_dbi order; //time to id - MDB_dbi stats; - MDB_dbi avatars; - MDB_dbi sid; //stanzaId to id - - bool getStatBoolValue(const std::string& id, MDB_txn* txn); - std::string getStatStringValue(const std::string& id, MDB_txn* txn); - - bool setStatValue(const std::string& id, bool value, MDB_txn* txn); - bool setStatValue(const std::string& id, const std::string& value, MDB_txn* txn); - bool readAvatarInfo(AvatarInfo& target, const std::string& res, MDB_txn* txn) const; - void printOrder(); - void printKeys(); - bool dropAvatar(const std::string& resource); - Shared::Message getMessage(const std::string& id, MDB_txn* txn) const; - Shared::Message getStoredMessage(MDB_txn *txn, MDB_cursor* cursor, MDB_cursor_op op, MDB_val* key, MDB_val* value, int& rc); - Shared::Message edge(bool end); + LMDBAL::Base db; + LMDBAL::Storage* messages; + LMDBAL::Storage* order; + LMDBAL::Storage* stats; + LMDBAL::Storage* avatars; + LMDBAL::Storage* stanzaIdToId; + mutable LMDBAL::Cursor cursor; }; } +QDataStream& operator << (QDataStream &out, const Core::Archive::AvatarInfo& info); +QDataStream& operator >> (QDataStream &in, Core::Archive::AvatarInfo& info); + #endif // CORE_ARCHIVE_H diff --git a/core/storage/cache.h b/core/storage/cache.h deleted file mode 100644 index 23f72f0..0000000 --- a/core/storage/cache.h +++ /dev/null @@ -1,55 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 CORE_CACHE_H -#define CORE_CACHE_H - -#include -#include - -#include - -#include - -namespace Core { - -template -class Cache -{ -public: - Cache(const QString& name); - ~Cache(); - - void open(); - void close(); - - void addRecord(const K& key, const V& value); - void changeRecord(const K& key, const V& value); - void removeRecord(const K& key); - V getRecord(const K& key) const; - bool checkRecord(const K& key) const; - -private: - Core::Storage storage; - std::map* cache; - std::set* abscent; -}; - -} - -#include "cache.hpp" - -#endif // CORE_CACHE_H diff --git a/core/storage/cache.hpp b/core/storage/cache.hpp deleted file mode 100644 index 9491de2..0000000 --- a/core/storage/cache.hpp +++ /dev/null @@ -1,102 +0,0 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 CORE_CACHE_HPP -#define CORE_CACHE_HPP -#include "cache.h" - -template -Core::Cache::Cache(const QString& name): - storage(name), - cache(new std::map ()), - abscent(new std::set ()) {} - -template -Core::Cache::~Cache() { - close(); - delete cache; - delete abscent; -} - -template -void Core::Cache::open() { - storage.open();} - -template -void Core::Cache::close() { - storage.close();} - -template -void Core::Cache::addRecord(const K& key, const V& value) { - storage.addRecord(key, value); - cache->insert(std::make_pair(key, value)); - abscent->erase(key); -} - -template -V Core::Cache::getRecord(const K& key) const { - typename std::map::const_iterator itr = cache->find(key); - if (itr == cache->end()) { - if (abscent->count(key) > 0) { - throw Archive::NotFound(std::to_string(key), storage.getName().toStdString()); - } - - try { - V value = storage.getRecord(key); - itr = cache->insert(std::make_pair(key, value)).first; - } catch (const Archive::NotFound& error) { - abscent->insert(key); - throw error; - } - } - - return itr->second; -} - -template -bool Core::Cache::checkRecord(const K& key) const { - typename std::map::const_iterator itr = cache->find(key); - if (itr != cache->end()) - return true; - - if (abscent->count(key) > 0) - return false; - - try { - V value = storage.getRecord(key); - itr = cache->insert(std::make_pair(key, value)).first; - } catch (const Archive::NotFound& error) { - return false; - } - - return true; -} - -template -void Core::Cache::changeRecord(const K& key, const V& value) { - storage.changeRecord(key, value); //there is a non straightforward behaviour: if there was no element at the sorage it will be added - cache->at(key) = value; - abscent->erase(key); //so... this line here is to make it coherent with the storage -} - -template -void Core::Cache::removeRecord(const K& key) { - storage.removeRecord(key); - cache->erase(key); - abscent->insert(key); -} - -#endif //CORE_CACHE_HPP diff --git a/core/storage/storage.h b/core/storage/storage.h deleted file mode 100644 index e43ae1a..0000000 --- a/core/storage/storage.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * 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 CORE_STORAGE_H -#define CORE_STORAGE_H - -#include -#include - -#include "archive.h" - -namespace Core { - -/** - * @todo write docs - */ -template -class Storage -{ -public: - Storage(const QString& name); - ~Storage(); - - void open(); - void close(); - - void addRecord(const K& key, const V& value); - void changeRecord(const K& key, const V& value); - void removeRecord(const K& key); - V getRecord(const K& key) const; - QString getName() const; - - -private: - QString name; - bool opened; - MDB_env* environment; - MDB_dbi base; -}; - -} - -MDB_val& operator << (MDB_val& data, QString& value); -MDB_val& operator >> (MDB_val& data, QString& value); - -MDB_val& operator << (MDB_val& data, uint32_t& value); -MDB_val& operator >> (MDB_val& data, uint32_t& value); - -namespace std { - std::string to_string(const QString& str); -} - -#include "storage.hpp" - -#endif // CORE_STORAGE_H diff --git a/core/storage/storage.hpp b/core/storage/storage.hpp deleted file mode 100644 index 33b8b56..0000000 --- a/core/storage/storage.hpp +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * 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 CORE_STORAGE_HPP -#define CORE_STORAGE_HPP - -#include -#include - -#include "storage.h" -#include - -template -Core::Storage::Storage(const QString& p_name): - name(p_name), - opened(false), - environment(), - base() -{ -} - -template -Core::Storage::~Storage() -{ - close(); -} - -template -void Core::Storage::open() -{ - if (!opened) { - mdb_env_create(&environment); - QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); - path += "/" + name; - QDir cache(path); - - if (!cache.exists()) { - bool res = cache.mkpath(path); - if (!res) { - throw Archive::Directory(path.toStdString()); - } - } - - mdb_env_set_maxdbs(environment, 1); - mdb_env_set_mapsize(environment, 10UL * 1024UL * 1024UL); - mdb_env_open(environment, path.toStdString().c_str(), 0, 0664); - - MDB_txn *txn; - mdb_txn_begin(environment, NULL, 0, &txn); - mdb_dbi_open(txn, "base", MDB_CREATE, &base); - mdb_txn_commit(txn); - opened = true; - } -} - -template -void Core::Storage::close() -{ - if (opened) { - mdb_dbi_close(environment, base); - mdb_env_close(environment); - opened = false; - } -} - -template -void Core::Storage::addRecord(const K& key, const V& value) -{ - if (!opened) { - throw Archive::Closed("addRecord", name.toStdString()); - } - QByteArray ba; - QDataStream ds(&ba, QIODevice::WriteOnly); - ds << value; - - MDB_val lmdbKey, lmdbData; - lmdbKey << key; - - lmdbData.mv_size = ba.size(); - lmdbData.mv_data = (uint8_t*)ba.data(); - MDB_txn *txn; - mdb_txn_begin(environment, NULL, 0, &txn); - int rc; - rc = mdb_put(txn, base, &lmdbKey, &lmdbData, MDB_NOOVERWRITE); - if (rc != 0) { - mdb_txn_abort(txn); - if (rc == MDB_KEYEXIST) { - throw Archive::Exist(name.toStdString(), std::to_string(key)); - } else { - throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); - } - } else { - mdb_txn_commit(txn); - } -} - -template -void Core::Storage::changeRecord(const K& key, const V& value) -{ - if (!opened) { - throw Archive::Closed("changeRecord", name.toStdString()); - } - - QByteArray ba; - QDataStream ds(&ba, QIODevice::WriteOnly); - ds << value; - - MDB_val lmdbKey, lmdbData; - lmdbKey << key; - lmdbData.mv_size = ba.size(); - lmdbData.mv_data = (uint8_t*)ba.data(); - MDB_txn *txn; - mdb_txn_begin(environment, NULL, 0, &txn); - int rc; - rc = mdb_put(txn, base, &lmdbKey, &lmdbData, 0); - if (rc != 0) { - mdb_txn_abort(txn); - if (rc) { - throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); - } - } else { - mdb_txn_commit(txn); - } -} - -template -V Core::Storage::getRecord(const K& key) const -{ - if (!opened) { - throw Archive::Closed("addElement", name.toStdString()); - } - - MDB_val lmdbKey, lmdbData; - lmdbKey << key; - - MDB_txn *txn; - int rc; - mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn); - rc = mdb_get(txn, base, &lmdbKey, &lmdbData); - if (rc) { - mdb_txn_abort(txn); - if (rc == MDB_NOTFOUND) { - throw Archive::NotFound(std::to_string(key), name.toStdString()); - } else { - throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); - } - } else { - QByteArray ba((char*)lmdbData.mv_data, lmdbData.mv_size); - QDataStream ds(&ba, QIODevice::ReadOnly); - V value; - ds >> value; - mdb_txn_abort(txn); - - return value; - } -} - -template -void Core::Storage::removeRecord(const K& key) -{ - if (!opened) { - throw Archive::Closed("addElement", name.toStdString()); - } - - MDB_val lmdbKey; - lmdbKey << key; - - MDB_txn *txn; - int rc; - mdb_txn_begin(environment, NULL, 0, &txn); - rc = mdb_del(txn, base, &lmdbKey, NULL); - if (rc) { - mdb_txn_abort(txn); - if (rc == MDB_NOTFOUND) { - throw Archive::NotFound(std::to_string(key), name.toStdString()); - } else { - throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); - } - } else { - mdb_txn_commit(txn); - } -} - -template -QString Core::Storage::getName() const { - return name;} - -MDB_val& operator << (MDB_val& data, const QString& value) { - QByteArray ba = value.toUtf8(); - data.mv_size = ba.size(); - data.mv_data = ba.data(); - return data; -} -MDB_val& operator >> (MDB_val& data, QString& value) { - value = QString::fromUtf8((const char*)data.mv_data, data.mv_size); - return data; -} - -MDB_val& operator << (MDB_val& data, uint32_t& value) { - data.mv_size = 4; - data.mv_data = &value; - return data; -} -MDB_val& operator >> (MDB_val& data, uint32_t& value) { - std::memcpy(&value, data.mv_data, data.mv_size); - return data; -} - -std::string std::to_string(const QString& str) { - return str.toStdString(); -} -#endif //CORE_STORAGE_HPP diff --git a/external/qxmpp b/external/qxmpp index ab4bdf2..9e9c22b 160000 --- a/external/qxmpp +++ b/external/qxmpp @@ -1 +1 @@ -Subproject commit ab4bdf2da41a26f462fe3a333a34e32c999e2a6d +Subproject commit 9e9c22b16a39c7370fed31c6deea56d8abf72440 diff --git a/external/simpleCrypt/CMakeLists.txt b/external/simpleCrypt/CMakeLists.txt index 274d304..5f274ba 100644 --- a/external/simpleCrypt/CMakeLists.txt +++ b/external/simpleCrypt/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5) project(simplecrypt LANGUAGES CXX) set(CMAKE_AUTOMOC ON) diff --git a/main/main.cpp b/main/main.cpp index d9ad4cc..c8e962f 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -24,7 +24,7 @@ #include #ifdef WITH_OMEMO -#include +#include #endif int main(int argc, char *argv[]) diff --git a/shared/message.cpp b/shared/message.cpp index 0e1b3c5..dab03a3 100644 --- a/shared/message.cpp +++ b/shared/message.cpp @@ -61,50 +61,42 @@ Shared::Message::Message(): attachPath() {} -QString Shared::Message::getBody() const -{ +QString Shared::Message::getBody() const { return body; } -QString Shared::Message::getFrom() const -{ +QString Shared::Message::getFrom() const { QString from = jFrom; - if (rFrom.size() > 0) { + if (rFrom.size() > 0) from += "/" + rFrom; - } + return from; } -QString Shared::Message::getTo() const -{ +QString Shared::Message::getTo() const { QString to = jTo; - if (rTo.size() > 0) { + if (rTo.size() > 0) to += "/" + rTo; - } + return to; } -QString Shared::Message::getId() const -{ - if (id.size() > 0) { +QString Shared::Message::getId() const { + if (id.size() > 0) return id; - } else { + else return stanzaId; - } } -QDateTime Shared::Message::getTime() const -{ +QDateTime Shared::Message::getTime() const { return time; } -void Shared::Message::setBody(const QString& p_body) -{ +void Shared::Message::setBody(const QString& p_body) { body = p_body; } -void Shared::Message::setFrom(const QString& from) -{ +void Shared::Message::setFrom(const QString& from) { QStringList list = from.split("/"); if (list.size() == 1) { jFrom = from.toLower(); @@ -114,8 +106,7 @@ void Shared::Message::setFrom(const QString& from) } } -void Shared::Message::setTo(const QString& to) -{ +void Shared::Message::setTo(const QString& to) { QStringList list = to.split("/"); if (list.size() == 1) { jTo = to.toLower(); @@ -125,153 +116,122 @@ void Shared::Message::setTo(const QString& to) } } -void Shared::Message::setId(const QString& p_id) -{ +void Shared::Message::setId(const QString& p_id) { id = p_id; } -void Shared::Message::setTime(const QDateTime& p_time) -{ +void Shared::Message::setTime(const QDateTime& p_time) { time = p_time; } -QString Shared::Message::getFromJid() const -{ +QString Shared::Message::getFromJid() const { return jFrom; } -QString Shared::Message::getFromResource() const -{ +QString Shared::Message::getFromResource() const { return rFrom; } -QString Shared::Message::getToJid() const -{ +QString Shared::Message::getToJid() const { return jTo; } -QString Shared::Message::getToResource() const -{ +QString Shared::Message::getToResource() const { return rTo; } -QString Shared::Message::getErrorText() const -{ +QString Shared::Message::getErrorText() const { return errorText; } -QString Shared::Message::getPenPalJid() const -{ - if (outgoing) { +QString Shared::Message::getPenPalJid() const { + if (outgoing) return jTo; - } else { + else return jFrom; - } } -QString Shared::Message::getPenPalResource() const -{ - if (outgoing) { +QString Shared::Message::getPenPalResource() const { + if (outgoing) return rTo; - } else { + else return rFrom; - } } -Shared::Message::State Shared::Message::getState() const -{ +Shared::Message::State Shared::Message::getState() const { return state; } -bool Shared::Message::getEdited() const -{ +bool Shared::Message::getEdited() const { return edited; } -void Shared::Message::setFromJid(const QString& from) -{ +void Shared::Message::setFromJid(const QString& from) { jFrom = from.toLower(); } -void Shared::Message::setFromResource(const QString& from) -{ +void Shared::Message::setFromResource(const QString& from) { rFrom = from; } -void Shared::Message::setToJid(const QString& to) -{ +void Shared::Message::setToJid(const QString& to) { jTo = to.toLower(); } -void Shared::Message::setToResource(const QString& to) -{ +void Shared::Message::setToResource(const QString& to) { rTo = to; } -void Shared::Message::setErrorText(const QString& err) -{ - if (state == State::error) { +void Shared::Message::setErrorText(const QString& err) { + if (state == State::error) errorText = err; - } } -bool Shared::Message::getOutgoing() const -{ +bool Shared::Message::getOutgoing() const { return outgoing; } -void Shared::Message::setOutgoing(bool og) -{ +void Shared::Message::setOutgoing(bool og) { outgoing = og; } -bool Shared::Message::getForwarded() const -{ +bool Shared::Message::getForwarded() const { return forwarded; } -void Shared::Message::generateRandomId() -{ +void Shared::Message::generateRandomId() { id = generateUUID(); } -QString Shared::Message::getThread() const -{ +QString Shared::Message::getThread() const { return thread; } -void Shared::Message::setForwarded(bool fwd) -{ +void Shared::Message::setForwarded(bool fwd) { forwarded = fwd; } -void Shared::Message::setThread(const QString& p_body) -{ +void Shared::Message::setThread(const QString& p_body) { thread = p_body; } -QDateTime Shared::Message::getLastModified() const -{ +QDateTime Shared::Message::getLastModified() const { return lastModified; } -QString Shared::Message::getOriginalBody() const -{ +QString Shared::Message::getOriginalBody() const { return originalMessage; } -Shared::Message::Type Shared::Message::getType() const -{ +Shared::Message::Type Shared::Message::getType() const { return type; } -void Shared::Message::setType(Shared::Message::Type t) -{ +void Shared::Message::setType(Shared::Message::Type t) { type = t; } -void Shared::Message::setState(Shared::Message::State p_state) -{ +void Shared::Message::setState(Shared::Message::State p_state) { state = p_state; if (state != State::error) { @@ -279,96 +239,92 @@ void Shared::Message::setState(Shared::Message::State p_state) } } -bool Shared::Message::serverStored() const -{ +bool Shared::Message::serverStored() const { return state == State::delivered || state == State::sent; } -void Shared::Message::setEdited(bool p_edited) -{ +void Shared::Message::setEdited(bool p_edited) { edited = p_edited; } -void Shared::Message::serialize(QDataStream& data) const -{ - data << jFrom; - data << rFrom; - data << jTo; - data << rTo; - data << id; - data << body; - data << time; - data << thread; - data << (quint8)type; - data << outgoing; - data << forwarded; - data << oob; - data << (quint8)state; - data << edited; - if (state == State::error) { - data << errorText; +QDataStream& operator<<(QDataStream& out, const Shared::Message& info) { + out << info.jFrom; + out << info.rFrom; + out << info.jTo; + out << info.rTo; + out << info.id; + out << info.body; + out << info.time; + out << info.thread; + out << (quint8)info.type; + out << info.outgoing; + out << info.forwarded; + out << info.oob; + out << (quint8)info.state; + out << info.edited; + if (info.state == Shared::Message::State::error) + out << info.errorText; + + if (info.edited) { + out << info.originalMessage; + out << info.lastModified; } - if (edited) { - data << originalMessage; - data << lastModified; - } - data << stanzaId; - data << attachPath; + out << info.stanzaId; + out << info.attachPath; + + return out; } -void Shared::Message::deserialize(QDataStream& data) -{ - data >> jFrom; - data >> rFrom; - data >> jTo; - data >> rTo; - data >> id; - data >> body; - data >> time; - data >> thread; +QDataStream & operator>>(QDataStream& in, Shared::Message& info) { + in >> info.jFrom; + in >> info.rFrom; + in >> info.jTo; + in >> info.rTo; + in >> info.id; + in >> info.body; + in >> info.time; + in >> info.thread; quint8 t; - data >> t; - type = static_cast(t); - data >> outgoing; - data >> forwarded; - data >> oob; + in >> t; + info.type = static_cast(t); + in >> info.outgoing; + in >> info.forwarded; + in >> info.oob; quint8 s; - data >> s; - state = static_cast(s); - data >> edited; - if (state == State::error) { - data >> errorText; + in >> s; + info.state = static_cast(s); + in >> info.edited; + if (info.state == Shared::Message::State::error) + in >> info.errorText; + + if (info.edited) { + in >> info.originalMessage; + in >> info.lastModified; } - if (edited) { - data >> originalMessage; - data >> lastModified; - } - data >> stanzaId; - data >> attachPath; + in >> info.stanzaId; + in >> info.attachPath; + + return in; } bool Shared::Message::change(const QMap& data) { QMap::const_iterator itr = data.find("state"); - if (itr != data.end()) { + if (itr != data.end()) setState(static_cast(itr.value().toUInt())); - } itr = data.find("outOfBandUrl"); - if (itr != data.end()) { + if (itr != data.end()) setOutOfBandUrl(itr.value().toString()); - } itr = data.find("attachPath"); - if (itr != data.end()) { + if (itr != data.end()) setAttachPath(itr.value().toString()); - } if (state == State::error) { itr = data.find("errorText"); - if (itr != data.end()) { + if (itr != data.end()) setErrorText(itr.value().toString()); - } } bool idChanged = false; @@ -386,9 +342,8 @@ bool Shared::Message::change(const QMap& data) QString newId = itr.value().toString(); if (stanzaId != newId) { setStanzaId(newId); - if (id.size() == 0) { + if (id.size() == 0) idChanged = true; - } } } @@ -398,15 +353,15 @@ bool Shared::Message::change(const QMap& data) if (body != b) { QMap::const_iterator dItr = data.find("stamp"); QDateTime correctionDate; - if (dItr != data.end()) { + if (dItr != data.end()) correctionDate = dItr.value().toDateTime(); - } else { + else correctionDate = QDateTime::currentDateTimeUtc(); //in case there is no information about time of this correction it's applied - } + if (!edited || lastModified < correctionDate) { - if (!edited) { + if (!edited) originalMessage = body; - } + lastModified = correctionDate; setBody(b); setEdited(true); @@ -416,57 +371,47 @@ bool Shared::Message::change(const QMap& data) QMap::const_iterator dItr = data.find("stamp"); if (dItr != data.end()) { QDateTime ntime = dItr.value().toDateTime(); - if (time != ntime) { + if (time != ntime) setTime(ntime); - } } } return idChanged; } -void Shared::Message::setCurrentTime() -{ +void Shared::Message::setCurrentTime() { time = QDateTime::currentDateTimeUtc(); } -QString Shared::Message::getOutOfBandUrl() const -{ +QString Shared::Message::getOutOfBandUrl() const { return oob; } -bool Shared::Message::hasOutOfBandUrl() const -{ +bool Shared::Message::hasOutOfBandUrl() const { return oob.size() > 0; } -void Shared::Message::setOutOfBandUrl(const QString& url) -{ +void Shared::Message::setOutOfBandUrl(const QString& url) { oob = url; } -bool Shared::Message::storable() const -{ +bool Shared::Message::storable() const { return id.size() > 0 && (body.size() > 0 || oob.size() > 0 || attachPath.size() > 0); } -void Shared::Message::setStanzaId(const QString& sid) -{ +void Shared::Message::setStanzaId(const QString& sid) { stanzaId = sid; } -QString Shared::Message::getStanzaId() const -{ +QString Shared::Message::getStanzaId() const { return stanzaId; } -QString Shared::Message::getAttachPath() const -{ +QString Shared::Message::getAttachPath() const { return attachPath; } -void Shared::Message::setAttachPath(const QString& path) -{ +void Shared::Message::setAttachPath(const QString& path) { attachPath = path; } @@ -474,18 +419,15 @@ Shared::Message::Change::Change(const QMap& _data): data(_data), idModified(false) {} -void Shared::Message::Change::operator()(Shared::Message& msg) -{ +void Shared::Message::Change::operator()(Shared::Message& msg) { idModified = msg.change(data); } -void Shared::Message::Change::operator()(Shared::Message* msg) -{ +void Shared::Message::Change::operator()(Shared::Message* msg) { idModified = msg->change(data); } -bool Shared::Message::Change::hasIdBeenModified() const -{ +bool Shared::Message::Change::hasIdBeenModified() const { return idModified; } diff --git a/shared/message.h b/shared/message.h index aa91af6..557b1b3 100644 --- a/shared/message.h +++ b/shared/message.h @@ -25,12 +25,20 @@ #include #include +namespace Shared { + class Message; +} + +QDataStream& operator << (QDataStream& out, const Shared::Message& info); +QDataStream& operator >> (QDataStream& in, Shared::Message& info); namespace Shared { /** * @todo write docs */ class Message { + friend QDataStream& ::operator << (QDataStream& out, const Shared::Message& info); + friend QDataStream& ::operator >> (QDataStream& in, Shared::Message& info); public: enum Type { error, @@ -116,9 +124,6 @@ public: QString getStanzaId() const; QString getAttachPath() const; - void serialize(QDataStream& data) const; - void deserialize(QDataStream& data); - private: QString jFrom; QString rFrom; From 297e08ba412bc656881d3c7bde82922a2db6d3da Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 3 Nov 2023 20:13:45 -0300 Subject: [PATCH 109/137] somewhat working... --- core/storage/archive.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/core/storage/archive.cpp b/core/storage/archive.cpp index 6b00f37..0a93850 100644 --- a/core/storage/archive.cpp +++ b/core/storage/archive.cpp @@ -84,7 +84,7 @@ bool Core::Archive::addElement(const Shared::Message& message) { order->addRecord(message.getTime().toMSecsSinceEpoch(), id, txn); QString stanzaId = message.getStanzaId(); if (!stanzaId.isEmpty()) - stanzaIdToId->addRecord(stanzaId, id); + stanzaIdToId->addRecord(stanzaId, id, txn); txn.commit(); return true; @@ -199,14 +199,15 @@ unsigned int Core::Archive::addElements(const std::list& messag if (!added) continue; - order->addRecord(message.getTime().toMSecsSinceEpoch(), id); + order->addRecord(message.getTime().toMSecsSinceEpoch(), id, txn); QString sid = message.getStanzaId(); if (!sid.isEmpty()) - stanzaIdToId->addRecord(sid, id); + stanzaIdToId->addRecord(sid, id, txn); ++success; } + txn.commit(); return success; } @@ -217,24 +218,18 @@ long unsigned int Core::Archive::size() const { std::list Core::Archive::getBefore(unsigned int count, const QString& id) { LMDBAL::Transaction txn = db.beginReadOnlyTransaction(); + std::list res; try { cursor.open(txn); - if (id.isEmpty()) { - cursor.last(); - } else { + if (!id.isEmpty()) { Shared::Message reference = messages->getRecord(id, txn); uint64_t stamp = reference.getTime().toMSecsSinceEpoch(); cursor.set(stamp); - cursor.prev(); } - std::list res; for (unsigned int i = 0; i < count; ++i) { std::pair pair; - if (i == 0) - cursor.current(pair.first, pair.second); - else - cursor.prev(pair.first, pair.second); + cursor.prev(pair.first, pair.second); res.emplace_back(); Shared::Message& msg = res.back(); @@ -243,6 +238,12 @@ std::list Core::Archive::getBefore(unsigned int count, const QS cursor.close(); return res; + } catch (const LMDBAL::NotFound& e) { + cursor.close(); + if (res.empty()) + throw e; + else + return res; } catch (...) { cursor.close(); throw; @@ -273,7 +274,7 @@ bool Core::Archive::setEncryptionEnabled(bool is) { LMDBAL::WriteTransaction txn = db.beginTransaction(); bool current = false; try { - current = stats->getRecord("isEncryptionEnabled").toBool(); + current = stats->getRecord("isEncryptionEnabled", txn).toBool(); } catch (const LMDBAL::NotFound& e) {} if (is != current) { From a7d1a28f2932f78d75fb44e88f169617b705b9f5 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 4 Nov 2023 22:12:15 -0300 Subject: [PATCH 110/137] some work towards encryption --- CMakeLists.txt | 7 --- core/CMakeLists.txt | 1 - core/account.cpp | 11 +++++ core/account.h | 1 + core/components/CMakeLists.txt | 2 + core/{storage => components}/archive.cpp | 19 ++++---- core/{storage => components}/archive.h | 6 ++- core/handlers/rosterhandler.cpp | 14 +++--- core/handlers/rosterhandler.h | 2 +- core/rosteritem.cpp | 10 ++-- core/rosteritem.h | 8 ++-- core/squawk.cpp | 9 ++++ core/squawk.h | 1 + core/storage/CMakeLists.txt | 4 -- main/application.cpp | 10 ++++ main/application.h | 2 + ui/models/contact.cpp | 25 +++++++++- ui/models/contact.h | 3 ++ ui/squawk.cpp | 61 ++++++++++++------------ ui/squawk.h | 4 +- ui/widgets/conversation.h | 10 ++-- 21 files changed, 129 insertions(+), 81 deletions(-) rename core/{storage => components}/archive.cpp (96%) rename core/{storage => components}/archive.h (94%) delete mode 100644 core/storage/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 5950c36..378aa51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,13 +46,6 @@ endif() find_package(Boost COMPONENTS) target_include_directories(squawk PRIVATE ${Boost_INCLUDE_DIRS}) -target_include_directories(squawk PRIVATE ${Qt${QT_VERSION_MAJOR}_INCLUDE_DIRS}) -target_include_directories(squawk PRIVATE ${Qt${QT_VERSION_MAJOR}Widgets_INCLUDE_DIRS}) -target_include_directories(squawk PRIVATE ${Qt${QT_VERSION_MAJOR}DBus_INCLUDE_DIRS}) -target_include_directories(squawk PRIVATE ${Qt${QT_VERSION_MAJOR}Gui_INCLUDE_DIRS}) -target_include_directories(squawk PRIVATE ${Qt${QT_VERSION_MAJOR}Xml_INCLUDE_DIRS}) -target_include_directories(squawk PRIVATE ${Qt${QT_VERSION_MAJOR}Network_INCLUDE_DIRS}) -target_include_directories(squawk PRIVATE ${Qt${QT_VERSION_MAJOR}Core_INCLUDE_DIRS}) #OMEMO if (WITH_OMEMO) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 01c6d8f..a02bfe6 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -31,7 +31,6 @@ target_sources(squawk PRIVATE target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS}) add_subdirectory(handlers) -add_subdirectory(storage) add_subdirectory(passwordStorageEngines) add_subdirectory(components) add_subdirectory(delayManager) diff --git a/core/account.cpp b/core/account.cpp index b06f51b..96ca4e1 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -669,6 +669,17 @@ void Core::Account::setRoomJoined(const QString& jid, bool joined) { conf->setJoined(joined); } +void Core::Account::setContactEncryption(const QString& jid, Shared::EncryptionProtocol value) { + Contact* cnt = rh->getContact(jid); + if (cnt == nullptr) { + qDebug() << "An attempt to set encryption to the non-existing contact" << jid << "of the account" << getName() << ", skipping"; + return; + } + + cnt->setEncryption(value); +} + + void Core::Account::discoverInfo(const QString& address, const QString& node) { if (state == Shared::ConnectionState::connected) { dm->requestInfo(address, node); diff --git a/core/account.h b/core/account.h index a9425e7..6442abf 100644 --- a/core/account.h +++ b/core/account.h @@ -132,6 +132,7 @@ public: void removeContactFromGroupRequest(const QString& jid, const QString& groupName); void renameContactRequest(const QString& jid, const QString& newName); void requestChangeMessage(const QString& jid, const QString& messageId, const QMap& data); + void setContactEncryption(const QString& jid, Shared::EncryptionProtocol value); void setRoomJoined(const QString& jid, bool joined); void setRoomAutoJoin(const QString& jid, bool joined); diff --git a/core/components/CMakeLists.txt b/core/components/CMakeLists.txt index 86d9fb8..77d290b 100644 --- a/core/components/CMakeLists.txt +++ b/core/components/CMakeLists.txt @@ -2,12 +2,14 @@ set(SOURCE_FILES networkaccess.cpp clientcache.cpp urlstorage.cpp + archive.cpp ) set(HEADER_FILES networkaccess.h clientcache.h urlstorage.h + archive.h ) target_sources(squawk PRIVATE diff --git a/core/storage/archive.cpp b/core/components/archive.cpp similarity index 96% rename from core/storage/archive.cpp rename to core/components/archive.cpp index 0a93850..003cce6 100644 --- a/core/storage/archive.cpp +++ b/core/components/archive.cpp @@ -17,12 +17,11 @@ */ #include "archive.h" + #include #include -#include + #include -#include -#include Core::Archive::Archive(const QString& account, const QString& p_jid, QObject* parent): QObject(parent), @@ -262,23 +261,23 @@ void Core::Archive::setFromTheBeginning(bool is) { stats->forceRecord("fromTheBeginning", is); } -bool Core::Archive::isEncryptionEnabled() const { +Shared::EncryptionProtocol Core::Archive::encryption() const { try { - return stats->getRecord("isEncryptionEnabled").toBool(); + return stats->getRecord("encryption").value(); } catch (const LMDBAL::NotFound& e) { - return false; + return Shared::EncryptionProtocol::none; } } -bool Core::Archive::setEncryptionEnabled(bool is) { +bool Core::Archive::setEncryption(Shared::EncryptionProtocol is) { LMDBAL::WriteTransaction txn = db.beginTransaction(); - bool current = false; + Shared::EncryptionProtocol current = Shared::EncryptionProtocol::none; try { - current = stats->getRecord("isEncryptionEnabled", txn).toBool(); + current = stats->getRecord("encryption", txn).value(); } catch (const LMDBAL::NotFound& e) {} if (is != current) { - stats->forceRecord("isEncryptionEnabled", is, txn); + stats->forceRecord("encryption", static_cast(is), txn); txn.commit(); return true; } diff --git a/core/storage/archive.h b/core/components/archive.h similarity index 94% rename from core/storage/archive.h rename to core/components/archive.h index 6f3c9eb..08f508a 100644 --- a/core/storage/archive.h +++ b/core/components/archive.h @@ -23,7 +23,9 @@ #include #include #include +#include +#include "shared/enums.h" #include "shared/message.h" #include "shared/exception.h" #include @@ -60,8 +62,8 @@ public: std::list getBefore(unsigned int count, const QString& id); bool isFromTheBeginning() const; void setFromTheBeginning(bool is); - bool isEncryptionEnabled() const; - bool setEncryptionEnabled(bool is); //returns true if changed, false otherwise + Shared::EncryptionProtocol encryption() const; + bool setEncryption(Shared::EncryptionProtocol value); //returns true if changed, false otherwise bool setAvatar(const QByteArray& data, AvatarInfo& info, bool generated = false, const QString& resource = ""); AvatarInfo getAvatarInfo(const QString& resource = "") const; bool readAvatarInfo(AvatarInfo& target, const QString& resource = "") const; diff --git a/core/handlers/rosterhandler.cpp b/core/handlers/rosterhandler.cpp index 4ce8939..38425ba 100644 --- a/core/handlers/rosterhandler.cpp +++ b/core/handlers/rosterhandler.cpp @@ -225,7 +225,7 @@ void Core::RosterHandler::onContactGroupAdded(const QString& group) { #if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) {"trust", QVariant::fromValue(acc->th->getSummary(contact->jid))}, #endif - {"encryption", contact->isEncryptionEnabled()} + {"encryption", QVariant::fromValue(contact->encryption())} }); addToGroup(contact->jid, group); emit acc->addContact(contact->jid, group, cData); @@ -246,9 +246,9 @@ void Core::RosterHandler::onContactNameChanged(const QString& cname) { emit acc->changeContact(contact->jid, {{"name", cname}}); } -void Core::RosterHandler::onContactEncryptionChanged(bool value) { +void Core::RosterHandler::onContactEncryptionChanged(Shared::EncryptionProtocol value) { RosterItem* contact = static_cast(sender()); - emit acc->changeContact(contact->jid, {{"encryption", value}}); + emit acc->changeContact(contact->jid, {{"encryption", QVariant::fromValue(value)}}); } void Core::RosterHandler::onContactSubscriptionStateChanged(Shared::SubscriptionState cstate) { @@ -328,7 +328,7 @@ Core::Contact * Core::RosterHandler::addOutOfRosterContact(const QString& jid) { cnt->setSubscriptionState(Shared::SubscriptionState::unknown); emit acc->addContact(lcJid, "", QMap({ {"state", QVariant::fromValue(Shared::SubscriptionState::unknown)}, - {"encryption", false} + {"encryption", QVariant::fromValue(Shared::EncryptionProtocol::none)} })); handleNewContact(cnt); return cnt; @@ -361,9 +361,9 @@ void Core::RosterHandler::onRosterItemRemoved(const QString& bareJid) { Contact* contact = itr->second; contacts.erase(itr); QSet cGroups = contact->getGroups(); - for (QSet::const_iterator itr = cGroups.begin(), end = cGroups.end(); itr != end; ++itr) { - removeFromGroup(lcJid, *itr); - } + for (const QString& group : cGroups) + removeFromGroup(lcJid, group); + emit acc->removeContact(lcJid); contact->deleteLater(); diff --git a/core/handlers/rosterhandler.h b/core/handlers/rosterhandler.h index 61f3d7a..1f8e480 100644 --- a/core/handlers/rosterhandler.h +++ b/core/handlers/rosterhandler.h @@ -95,7 +95,7 @@ private slots: void onContactNameChanged(const QString& name); void onContactSubscriptionStateChanged(Shared::SubscriptionState state); void onContactAvatarChanged(Shared::Avatar, const QString& path); - void onContactEncryptionChanged(bool value); + void onContactEncryptionChanged(Shared::EncryptionProtocol value); void onPepSupportedChanged(Shared::Support support); private: diff --git a/core/rosteritem.cpp b/core/rosteritem.cpp index afbf836..a5763e2 100644 --- a/core/rosteritem.cpp +++ b/core/rosteritem.cpp @@ -561,12 +561,12 @@ Shared::Message Core::RosterItem::getMessage(const QString& id) { return archive->getElement(id); } -bool Core::RosterItem::isEncryptionEnabled() const { - return archive->isEncryptionEnabled(); +Shared::EncryptionProtocol Core::RosterItem::encryption() const { + return archive->encryption(); } -void Core::RosterItem::enableEncryption(bool value) { - bool changed = archive->setEncryptionEnabled(value); +void Core::RosterItem::setEncryption(Shared::EncryptionProtocol value) { + bool changed = archive->setEncryption(value); if (changed) emit encryptionChanged(value); } @@ -574,7 +574,7 @@ void Core::RosterItem::enableEncryption(bool value) { QMap Core::RosterItem::getInfo() const { QMap result({ {"name", name}, - {"encryption", isEncryptionEnabled()}, + {"encryption", QVariant::fromValue(encryption())}, }); Archive::AvatarInfo info; bool hasAvatar = readAvatarInfo(info); diff --git a/core/rosteritem.h b/core/rosteritem.h index fa154c0..203ed88 100644 --- a/core/rosteritem.h +++ b/core/rosteritem.h @@ -34,7 +34,7 @@ #include "shared/enums.h" #include "shared/message.h" #include "shared/vcard.h" -#include "storage/archive.h" +#include "components/archive.h" #include "adapterfunctions.h" namespace Core { @@ -62,8 +62,8 @@ public: void setName(const QString& n); QString getServer() const; bool isMuc() const; - bool isEncryptionEnabled() const; - void enableEncryption(bool value = true); + Shared::EncryptionProtocol encryption() const; + void setEncryption(Shared::EncryptionProtocol value); void addMessageToArchive(const Shared::Message& msg); void correctMessageInArchive(const QString& originalId, const Shared::Message& msg); @@ -91,7 +91,7 @@ signals: void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime()) const; void avatarChanged(Shared::Avatar, const QString& path) const; void requestVCard(const QString& jid) const; - void encryptionChanged(bool value) const; + void encryptionChanged(Shared::EncryptionProtocol value) const; public: const QString jid; diff --git a/core/squawk.cpp b/core/squawk.cpp index b7a0aad..1888487 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -590,6 +590,15 @@ void Core::Squawk::setRoomAutoJoin(const QString& account, const QString& jid, b itr->second->setRoomAutoJoin(jid, joined); } +void Core::Squawk::setContactEncryption(const QString& account, const QString& jid, Shared::EncryptionProtocol value) { + AccountsMap::const_iterator itr = amap.find(account); + if (itr == amap.end()) { + qDebug() << "An attempt to set encryption to the contact" << jid << "of non existing account" << account << ", skipping"; + return; + } + itr->second->setContactEncryption(jid, value); +} + void Core::Squawk::onAccountAddRoomPresence(const QString& jid, const QString& nick, const QMap& data) { Account* acc = static_cast(sender()); emit addRoomParticipant(acc->getName(), jid, nick, data); diff --git a/core/squawk.h b/core/squawk.h index eebe917..2ee122e 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -116,6 +116,7 @@ public slots: void removeContactRequest(const QString& account, const QString& jid); void renameContactRequest(const QString& account, const QString& jid, const QString& newName); void addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet& groups); + void setContactEncryption(const QString& account, const QString& jid, Shared::EncryptionProtocol value); void setRoomJoined(const QString& account, const QString& jid, bool joined); void setRoomAutoJoin(const QString& account, const QString& jid, bool joined); diff --git a/core/storage/CMakeLists.txt b/core/storage/CMakeLists.txt deleted file mode 100644 index e2de42c..0000000 --- a/core/storage/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -target_sources(squawk PRIVATE - archive.cpp - archive.h -) diff --git a/main/application.cpp b/main/application.cpp index 0701a0c..d184e6b 100644 --- a/main/application.cpp +++ b/main/application.cpp @@ -50,6 +50,7 @@ Application::Application(Core::Squawk* p_core): connect(this, &Application::replaceMessage, core, &Core::Squawk::replaceMessage); connect(this, &Application::sendMessage, core, &Core::Squawk::sendMessage); connect(this, &Application::resendMessage, core, &Core::Squawk::resendMessage); + connect(this, &Application::setEncryption, core, &Core::Squawk::setContactEncryption); connect(&roster, &Models::Roster::requestArchive, std::bind(&Core::Squawk::requestArchive, core, std::placeholders::_1, std::placeholders::_2, 20, std::placeholders::_3)); @@ -526,6 +527,7 @@ void Application::subscribeConversation(Conversation* conv) { connect(conv, &Conversation::sendMessage, this, &Application::onConversationMessage); connect(conv, &Conversation::replaceMessage, this, &Application::onConversationReplaceMessage); connect(conv, &Conversation::resendMessage, this, &Application::onConversationResend); + connect(conv, &Conversation::setEncryption, this, &Application::onConversationSetEncryption); connect(conv, &Conversation::notifyableMessage, this, &Application::notify); } @@ -586,6 +588,14 @@ void Application::onConversationReplaceMessage(const QString& originalId, const emit replaceMessage(acc, originalId, msg); } +void Application::onConversationSetEncryption(Shared::EncryptionProtocol value) { + Conversation* conv = static_cast(sender()); + QString acc = conv->getAccount(); + QString jid = conv->getJid(); + + emit setEncryption(acc, jid, value); +} + void Application::onConversationResend(const QString& id) { Conversation* conv = static_cast(sender()); QString acc = conv->getAccount(); diff --git a/main/application.h b/main/application.h index 54c2dbc..408572b 100644 --- a/main/application.h +++ b/main/application.h @@ -60,6 +60,7 @@ signals: void setRoomAutoJoin(const QString& account, const QString& jid, bool joined); void subscribeContact(const QString& account, const QString& jid, const QString& reason); void unsubscribeContact(const QString& account, const QString& jid, const QString& reason); + void setEncryption(const QString& account, const QString& jid, Shared::EncryptionProtocol value); void quitting(); void readyToQuit(); @@ -101,6 +102,7 @@ private slots: void onItemExpanded(const QModelIndex& index); void onItemCollapsed(const QModelIndex& index); void onAddedElement(const std::list& path); + void onConversationSetEncryption(Shared::EncryptionProtocol value); private: void createMainWindow(); diff --git a/ui/models/contact.cpp b/ui/models/contact.cpp index c27965b..c89724e 100644 --- a/ui/models/contact.cpp +++ b/ui/models/contact.cpp @@ -36,6 +36,10 @@ Models::Contact::Contact(const Account* acc, const QString& p_jid ,const QMap()); + + itr = data.find("encryption"); + if (itr != data.end()) + setEncryption(itr.value().value()); } Models::Contact::~Contact() {} @@ -71,7 +75,7 @@ void Models::Contact::setStatus(const QString& p_state) { } int Models::Contact::columnCount() const { - return 9; + return 10; } QVariant Models::Contact::data(int column) const { @@ -94,6 +98,8 @@ QVariant Models::Contact::data(int column) const { return getAvatarPath(); case 8: return QVariant::fromValue(getTrust()); + case 9: + return QVariant::fromValue(getEncryption()); default: return QVariant(); } @@ -115,6 +121,8 @@ void Models::Contact::update(const QString& field, const QVariant& value) { setState(value.toUInt()); } else if (field == "trust") { setTrust(value.value()); + } else if (field == "encryption") { + setEncryption(value.value()); } else { Element::update(field, value); } @@ -254,3 +262,18 @@ bool Models::Contact::hasKeys(Shared::EncryptionProtocol protocol) const { return trust.hasKeys(protocol); } +void Models::Contact::setEncryption(Shared::EncryptionProtocol p_enc) { + if (encryption != p_enc) { + encryption = p_enc; + changed(9); + } +} + +void Models::Contact::setEncryption(unsigned int p_enc) { + setEncryption(Shared::Global::fromInt(p_enc)); +} + +Shared::EncryptionProtocol Models::Contact::getEncryption() const { + return encryption; +} + diff --git a/ui/models/contact.h b/ui/models/contact.h index dbf33d4..d03e936 100644 --- a/ui/models/contact.h +++ b/ui/models/contact.h @@ -43,6 +43,7 @@ public: Shared::Availability getAvailability() const; Shared::SubscriptionState getState() const; + Shared::EncryptionProtocol getEncryption() const; QIcon getStatusIcon(bool big = false) const; @@ -78,6 +79,8 @@ protected: void setState(unsigned int p_state); void setStatus(const QString& p_state); void setTrust(const Shared::TrustSummary& p_trust); + void setEncryption(Shared::EncryptionProtocol p_enc); + void setEncryption(unsigned int p_enc); private: Shared::Availability availability; diff --git a/ui/squawk.cpp b/ui/squawk.cpp index ce759ac..64ea636 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -37,11 +37,11 @@ Squawk::Squawk(Models::Roster& p_rosterModel, QWidget *parent) : m_ui->setupUi(this); m_ui->roster->setModel(&rosterModel); m_ui->roster->setContextMenuPolicy(Qt::CustomContextMenu); - if (QApplication::style()->styleHint(QStyle::SH_ScrollBar_Transient) == 1) { + if (QApplication::style()->styleHint(QStyle::SH_ScrollBar_Transient) == 1) m_ui->roster->setColumnWidth(1, 52); - } else { + else m_ui->roster->setColumnWidth(1, 26); - } + m_ui->roster->setIconSize(QSize(20, 20)); m_ui->roster->header()->setStretchLastSection(false); m_ui->roster->header()->setSectionResizeMode(0, QHeaderView::Stretch); @@ -69,24 +69,24 @@ Squawk::Squawk(Models::Roster& p_rosterModel, QWidget *parent) : connect(m_ui->actionAboutSquawk, &QAction::triggered, this, &Squawk::onAboutSquawkCalled); //m_ui->mainToolBar->addWidget(m_ui->comboBox); - if (testAttribute(Qt::WA_TranslucentBackground)) { + if (testAttribute(Qt::WA_TranslucentBackground)) m_ui->roster->viewport()->setAutoFillBackground(false); - } + QSettings settings; settings.beginGroup("ui"); settings.beginGroup("window"); - if (settings.contains("geometry")) { + if (settings.contains("geometry")) restoreGeometry(settings.value("geometry").toByteArray()); - } - if (settings.contains("state")) { + + if (settings.contains("state")) restoreState(settings.value("state").toByteArray()); - } + settings.endGroup(); - if (settings.contains("splitter")) { + if (settings.contains("splitter")) m_ui->splitter->restoreState(settings.value("splitter").toByteArray()); - } + settings.endGroup(); onAccountsChanged(); @@ -181,8 +181,10 @@ void Squawk::onJoinConferenceAccepted() { void Squawk::closeEvent(QCloseEvent* event) { if (accounts != nullptr) accounts->close(); + if (preferences != nullptr) preferences->close(); + if (about != nullptr) about->close(); @@ -221,9 +223,9 @@ void Squawk::stateChanged(Shared::Availability state) { void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) { if (item.isValid()) { Models::Item* node = static_cast(item.internalPointer()); - if (node->type == Models::Item::reference) { + if (node->type == Models::Item::reference) node = static_cast(node)->dereference(); - } + Models::Contact* contact = nullptr; Models::Room* room = nullptr; switch (node->type) { @@ -258,9 +260,9 @@ void Squawk::onRosterContextMenu(const QPoint& point) { QModelIndex index = m_ui->roster->indexAt(point); if (index.isValid()) { Models::Item* item = static_cast(index.internalPointer()); - if (item->type == Models::Item::reference) { + if (item->type == Models::Item::reference) item = static_cast(item)->dereference(); - } + contextMenu->clear(); bool hasMenu = false; bool active = item->getAccountConnectionState() == Shared::ConnectionState::connected; @@ -320,9 +322,9 @@ void Squawk::onRosterContextMenu(const QPoint& point) { QInputDialog* dialog = new QInputDialog(this); connect(dialog, &QDialog::accepted, [this, dialog, cntName, id]() { QString newName = dialog->textValue(); - if (newName != cntName) { + if (newName != cntName) emit renameContactRequest(id.account, id.name, newName); - } + dialog->deleteLater(); }); connect(dialog, &QDialog::rejected, dialog, &QObject::deleteLater); @@ -342,11 +344,10 @@ void Squawk::onRosterContextMenu(const QPoint& point) { gr->setChecked(rosterModel.groupHasContact(id.account, groupName, id.name)); gr->setEnabled(active); connect(gr, &QAction::toggled, [this, groupName, id](bool checked) { - if (checked) { + if (checked) emit addContactToGroupRequest(id.account, id.name, groupName); - } else { + else emit removeContactFromGroupRequest(id.account, id.name, groupName); - } }); } QAction* newGroup = groupsMenu->addAction(Shared::icon("group-new"), tr("New group")); @@ -405,9 +406,8 @@ void Squawk::onRosterContextMenu(const QPoint& point) { default: break; } - if (hasMenu) { + if (hasMenu) contextMenu->popup(m_ui->roster->viewport()->mapToGlobal(point)); - } } } @@ -515,9 +515,9 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn } if (hasContext && QGuiApplication::mouseButtons() & Qt::RightButton) { - if (id != nullptr) { + if (id != nullptr) delete id; - } + needToRestore = true; restoreSelection = previous; return; @@ -538,20 +538,19 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn } Models::Account* acc = rosterModel.getAccount(id->account); - if (contact != nullptr) { + if (contact != nullptr) currentConversation = new Chat(acc, contact); - } else if (room != nullptr) { + else if (room != nullptr) currentConversation = new Room(acc, room); - } - if (!testAttribute(Qt::WA_TranslucentBackground)) { + + if (!testAttribute(Qt::WA_TranslucentBackground)) currentConversation->setFeedFrames(true, false, true, true); - } + emit openedConversation(); - if (res.size() > 0) { + if (res.size() > 0) currentConversation->setPalResource(res); - } m_ui->splitter->insertWidget(1, currentConversation); diff --git a/ui/squawk.h b/ui/squawk.h index 4e1ca4b..70eae8e 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -50,8 +51,7 @@ class Squawk; class Application; -class Squawk : public QMainWindow -{ +class Squawk : public QMainWindow { Q_OBJECT friend class Application; public: diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index 80c8c2f..8af1745 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -46,13 +46,11 @@ #include "ui/widgets/messageline/feedview.h" #include "ui/widgets/messageline/messagedelegate.h" -namespace Ui -{ +namespace Ui { class Conversation; } -class KeyEnterReceiver : public QObject -{ +class KeyEnterReceiver : public QObject { Q_OBJECT public: KeyEnterReceiver(QObject* parent = 0); @@ -65,8 +63,7 @@ signals: void imagePasted(); }; -class Conversation : public QWidget -{ +class Conversation : public QWidget { Q_OBJECT public: Conversation(bool muc, Models::Account* acc, Models::Element* el, const QString pJid, const QString pRes, QWidget* parent = 0); @@ -91,6 +88,7 @@ signals: void requestLocalFile(const QString& messageId, const QString& url); void downloadFile(const QString& messageId, const QString& url); void notifyableMessage(const QString& account, const Shared::Message& msg); + void setEncryption(Shared::EncryptionProtocol value); protected: virtual void setName(const QString& name); From 637eb702a895fd163ddead3559cc6db75d425a8b Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 5 Nov 2023 16:29:44 -0300 Subject: [PATCH 111/137] cant believe it, first ever encrypted messages! --- CMakeLists.txt | 2 +- core/handlers/messagehandler.cpp | 188 ++++++++++++++++--------- core/handlers/messagehandler.h | 8 +- shared/message.cpp | 17 +++ shared/message.h | 7 + ui/widgets/chat.cpp | 30 +++- ui/widgets/chat.h | 2 + ui/widgets/conversation.cpp | 3 + ui/widgets/conversation.h | 1 + ui/widgets/messageline/messagefeed.cpp | 1 + 10 files changed, 190 insertions(+), 69 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 378aa51..1151cc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,7 @@ option(WITH_OMEMO "Build OMEMO support module" ON) # Dependencies ## Qt if (NOT DEFINED QT_VERSION_MAJOR) - find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) + find_package(QT NAMES Qt5 REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) else () find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) endif() diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index 2632848..f939b9e 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -71,26 +71,12 @@ void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) { case QXmppMessage::GroupChat: handled = handleGroupMessage(msg); break; - case QXmppMessage::Error: { - std::tuple ids = getOriginalPendingMessageId(msg.id()); - if (std::get<0>(ids)) { - QString id = std::get<1>(ids); - QString jid = std::get<2>(ids); - RosterItem* cnt = acc->rh->getRosterItem(jid); - QMap cData = { - {"state", static_cast(Shared::Message::State::error)}, - {"errorText", msg.error().text()} - }; - if (cnt != nullptr) - cnt->changeMessage(id, cData); - - emit acc->changeMessage(jid, id, cData); - handled = true; - } else { + case QXmppMessage::Error: + handled = handlePendingMessageError(msg.id(), msg.error().text()); + if (!handled) qDebug() << "received a message with type \"Error\", not sure what to do with it now, skipping"; - } - } - break; + + break; case QXmppMessage::Headline: qDebug() << "received a message with type \"Headline\", not sure what to do with it now, skipping"; break; @@ -99,6 +85,27 @@ void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) { logMessage(msg); } +bool Core::MessageHandler::handlePendingMessageError(const QString& id, const QString& errorText) { + std::tuple ids = getOriginalPendingMessageId(id); + if (std::get<0>(ids)) { + QString id = std::get<1>(ids); + QString jid = std::get<2>(ids); + RosterItem* ri = acc->rh->getRosterItem(jid); + QMap cData = { + {"state", static_cast(Shared::Message::State::error)}, + {"errorText", errorText} + }; + if (ri != nullptr) + ri->changeMessage(id, cData); + + emit acc->changeMessage(jid, id, cData); + return true; + } + + return false; +} + + bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) { if (msg.body().size() != 0 || msg.outOfBandUrl().size() > 0) { Shared::Message sMsg(Shared::Message::chat); @@ -182,9 +189,9 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp QString id; #if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) id = source.originId(); - if (id.size() == 0) { + if (id.size() == 0) id = source.id(); - } + target.setStanzaId(source.stanzaId()); qDebug() << "initializing message with originId:" << source.originId() << ", id:" << source.id() << ", stansaId:" << source.stanzaId(); #else @@ -202,24 +209,19 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp target.setBody(source.body()); target.setForwarded(forwarded); - if (guessing) { - if (target.getFromJid() == acc->getBareJid()) { - outgoing = true; - } else { - outgoing = false; - } - } + if (guessing) + outgoing = target.getFromJid() == acc->getBareJid(); + target.setOutgoing(outgoing); - if (time.isValid()) { + if (time.isValid()) target.setTime(time); - } else { + else target.setCurrentTime(); - } - + QString oob = source.outOfBandUrl(); - if (oob.size() > 0) { + if (oob.size() > 0) target.setAttachPath(acc->network->addMessageAndCheckForPath(oob, acc->getName(), target.getPenPalJid(), messageId)); - } + target.setOutOfBandUrl(oob); } @@ -249,7 +251,7 @@ void Core::MessageHandler::onCarbonMessageSent(const QXmppMessage& msg) { } #endif -std::tuple Core::MessageHandler::getOriginalPendingMessageId(const QString& id) { +std::tuple Core::MessageHandler::getOriginalPendingMessageId(const QString& id, bool clear) { std::tuple result({false, "", ""}); std::map::const_iterator itr = pendingStateMessages.find(id); if (itr != pendingStateMessages.end()) { @@ -263,12 +265,14 @@ std::tuple Core::MessageHandler::getOriginalPendingMessa else std::get<1>(result) = itr->first; - pendingCorrectionMessages.erase(itrC); + if (clear) + pendingCorrectionMessages.erase(itrC); } else { std::get<1>(result) = itr->first; } - pendingStateMessages.erase(itr); + if (clear) + pendingStateMessages.erase(itr); } return result; @@ -281,9 +285,9 @@ void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& QMap cData = {{"state", static_cast(Shared::Message::State::delivered)}}; RosterItem* ri = acc->rh->getRosterItem(std::get<2>(ids)); - if (ri != nullptr) { + if (ri != nullptr) ri->changeMessage(std::get<1>(ids), cData); - } + emit acc->changeMessage(std::get<2>(ids), std::get<1>(ids), cData); } } @@ -302,45 +306,25 @@ void Core::MessageHandler::performSending(Shared::Message data, const QString& o QString id = data.getId(); qDebug() << "Sending message with id:" << id; if (originalId.size() > 0) - qDebug() << "To replace one with id:" << originalId; + qDebug() << "To replace the one with id:" << originalId; RosterItem* ri = acc->rh->getRosterItem(jid); - bool sent = false; if (newMessage && originalId.size() > 0) newMessage = false; QDateTime sendTime = QDateTime::currentDateTimeUtc(); - if (acc->state == Shared::ConnectionState::connected) { - QXmppMessage msg(createPacket(data, sendTime, originalId)); - - sent = acc->client.sendPacket(msg); - if (sent) { - data.setState(Shared::Message::State::sent); - } else { - data.setState(Shared::Message::State::error); - data.setErrorText("Couldn't send message: internal QXMPP library error, probably need to check out the logs"); - } - - } else { - data.setState(Shared::Message::State::error); - data.setErrorText("You are is offline or reconnecting"); - } + std::pair result = scheduleSending(data, sendTime, originalId); + data.setState(result.first); + data.setErrorText(result.second); QMap changes(getChanges(data, sendTime, newMessage, originalId)); - - QString realId; - if (originalId.size() > 0) - realId = originalId; - else - realId = id; - if (ri != nullptr) { if (newMessage) ri->appendMessageToArchive(data); else - ri->changeMessage(realId, changes); + ri->changeMessage(originalId.isEmpty() ? id : originalId, changes); - if (sent) { + if (data.getState() != Shared::Message::State::error) { pendingStateMessages.insert(std::make_pair(id, jid)); if (originalId.size() > 0) pendingCorrectionMessages.insert(std::make_pair(id, originalId)); @@ -350,9 +334,81 @@ void Core::MessageHandler::performSending(Shared::Message data, const QString& o } } - emit acc->changeMessage(jid, realId, changes); + emit acc->changeMessage(jid, originalId.isEmpty() ? id : originalId, changes); } +std::pair Core::MessageHandler::scheduleSending( + const Shared::Message& message, + const QDateTime& sendTime, + const QString& originalId +) { + if (acc->state != Shared::ConnectionState::connected) + return {Shared::Message::State::error, "You are is offline or reconnecting"}; + + QXmppMessage msg = createPacket(message, sendTime, originalId); + QString id = msg.id(); +#ifdef WITH_OMEMO + if (message.getEncryption() == Shared::EncryptionProtocol::omemo2) { + QXmppTask task = acc->om->encryptMessage(std::move(msg), std::nullopt); + if (task.isFinished()) { + const QXmppE2eeExtension::MessageEncryptResult& res = task.result(); + if (std::holds_alternative>(res)) { + const std::unique_ptr& encrypted = std::get>(res); + bool success = acc->client.sendPacket(*encrypted.get()); + if (success) + return {Shared::Message::State::sent, ""}; + else + return {Shared::Message::State::error, "Error sending successfully encrypted message"}; + } else if (std::holds_alternative(res)) { + const QXmppError& err = std::get(res); + return {Shared::Message::State::error, err.description}; + } else { + return {Shared::Message::State::error, "Unexpected error ecryptng the message"}; + } + } else { + task.then(this, [this, id] (QXmppE2eeExtension::MessageEncryptResult&& result) { + if (std::holds_alternative>(result)) { + const std::unique_ptr& encrypted = std::get>(result); + encrypted->setBody("This message is encrypted with OMEMO 2 but could not be decrypted"); + bool success = acc->client.sendPacket(*encrypted.get()); + if (success) { + std::tuple ids = getOriginalPendingMessageId(id, false); + if (std::get<0>(ids)) { + QString id = std::get<1>(ids); + QString jid = std::get<2>(ids); + RosterItem* ri = acc->rh->getRosterItem(jid); + QMap cData = {{"state", static_cast(Shared::Message::State::sent)}}; + if (ri != nullptr) + ri->changeMessage(id, cData); + + emit acc->changeMessage(jid, id, cData); + } else { + qDebug() << "Encrypted message has been successfully sent, but it couldn't be found to update the sate"; + } + } else { + handlePendingMessageError(id, "Error sending successfully encrypted message"); + } + } else if (std::holds_alternative(result)) { + const QXmppError& err = std::get(result); + handlePendingMessageError(id, err.description); + } else { + handlePendingMessageError(id, "Unexpected error ecryptng the message"); + } + }); + return {Shared::Message::State::pending, ""}; + } + } else +#endif + { + bool success = acc->client.sendPacket(msg); + if (success) + return {Shared::Message::State::sent, ""}; + else + return {Shared::Message::State::error, "Error sending message, internal QXMPP error"}; + } +} + + QMap Core::MessageHandler::getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const { QMap changes; diff --git a/core/handlers/messagehandler.h b/core/handlers/messagehandler.h index a08fee8..99a77d3 100644 --- a/core/handlers/messagehandler.h +++ b/core/handlers/messagehandler.h @@ -23,9 +23,13 @@ #include #include +#include #include #include +#ifdef WITH_OMEMO + #include +#endif #include #include @@ -74,7 +78,9 @@ private: void handleUploadError(const QString& jid, const QString& messageId, const QString& errorText); QXmppMessage createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const; QMap getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const; - std::tuple getOriginalPendingMessageId(const QString& id); + std::tuple getOriginalPendingMessageId(const QString& id, bool clear = true); + bool handlePendingMessageError(const QString& id, const QString& errorText); + std::pair scheduleSending(const Shared::Message& message, const QDateTime& sendTime, const QString& originalId); private: Account* acc; diff --git a/shared/message.cpp b/shared/message.cpp index dab03a3..5b535c1 100644 --- a/shared/message.cpp +++ b/shared/message.cpp @@ -247,6 +247,19 @@ void Shared::Message::setEdited(bool p_edited) { edited = p_edited; } +Shared::EncryptionProtocol Shared::Message::getEncryption() const { + return encryption; +} + +void Shared::Message::setEncryption(EncryptionProtocol encryption) { + Shared::Message::encryption = encryption; +} + +void Shared::Message::setError(const QString& text) { + state = State::error; + errorText = text; +} + QDataStream& operator<<(QDataStream& out, const Shared::Message& info) { out << info.jFrom; out << info.rFrom; @@ -271,6 +284,7 @@ QDataStream& operator<<(QDataStream& out, const Shared::Message& info) { } out << info.stanzaId; out << info.attachPath; + out << (quint8)info.encryption; return out; } @@ -303,6 +317,9 @@ QDataStream & operator>>(QDataStream& in, Shared::Message& info) { } in >> info.stanzaId; in >> info.attachPath; + quint8 e; + in >> e; + info.encryption = static_cast(e); return in; } diff --git a/shared/message.h b/shared/message.h index 557b1b3..d7df9f3 100644 --- a/shared/message.h +++ b/shared/message.h @@ -25,6 +25,8 @@ #include #include +#include "enums.h" + namespace Shared { class Message; } @@ -94,6 +96,9 @@ public: bool change(const QMap& data); void setStanzaId(const QString& sid); void setAttachPath(const QString& path); + void setEncryption(EncryptionProtocol encryption); + + void setError(const QString& text); QString getFrom() const; QString getFromJid() const; @@ -123,6 +128,7 @@ public: QString getOriginalBody() const; QString getStanzaId() const; QString getAttachPath() const; + EncryptionProtocol getEncryption() const; private: QString jFrom; @@ -144,6 +150,7 @@ private: QDateTime lastModified; QString stanzaId; QString attachPath; + EncryptionProtocol encryption; }; } diff --git a/ui/widgets/chat.cpp b/ui/widgets/chat.cpp index 3b9f090..628f30f 100644 --- a/ui/widgets/chat.cpp +++ b/ui/widgets/chat.cpp @@ -31,10 +31,12 @@ Chat::Chat(Models::Account* acc, Models::Contact* p_contact, QWidget* parent): setAvatar(p_contact->getAvatarPath()); connect(contact, &Models::Contact::childChanged, this, &Chat::onContactChanged); +#ifdef WITH_OMEMO if (p_contact->hasKeys(Shared::EncryptionProtocol::omemo2)) { m_ui->encryptionButton->setVisible(true); - //if () + updateEncryptionState(); } +#endif } Chat::~Chat() @@ -56,9 +58,14 @@ void Chat::onContactChanged(Models::Item* item, int row, int col) { case 7: setAvatar(contact->getAvatarPath()); break; +#ifdef WITH_OMEMO case 8: m_ui->encryptionButton->setVisible(contact->hasKeys(Shared::EncryptionProtocol::omemo2)); break; + case 9: + updateEncryptionState(); + break; +#endif } } } @@ -69,12 +76,25 @@ void Chat::updateState() { statusIcon->setToolTip(Shared::Global::getName(av)); } +void Chat::updateEncryptionState() { + m_ui->encryptionButton->setEnabled(true); + if (contact->getEncryption() == Shared::EncryptionProtocol::omemo2) + m_ui->encryptionButton->setIcon(Shared::icon("lock")); + else + m_ui->encryptionButton->setIcon(Shared::icon("unlock")); +} + + Shared::Message Chat::createMessage() const { Shared::Message msg = Conversation::createMessage(); msg.setType(Shared::Message::chat); msg.setFrom(account->getFullJid()); msg.setToJid(palJid); msg.setToResource(activePalResource); +#ifdef WITH_OMEMO + if (contact->getEncryption() == Shared::EncryptionProtocol::omemo2) + msg.setEncryption(Shared::EncryptionProtocol::omemo2); +#endif return msg; } @@ -87,3 +107,11 @@ void Chat::onMessage(const Shared::Message& data){ setPalResource(res); } } + +void Chat::onEncryptionButtonClicked() { + m_ui->encryptionButton->setEnabled(false); + if (contact->getEncryption() == Shared::EncryptionProtocol::omemo2) + emit setEncryption(Shared::EncryptionProtocol::none); + else + emit setEncryption(Shared::EncryptionProtocol::omemo2); +} diff --git a/ui/widgets/chat.h b/ui/widgets/chat.h index 78e6bec..cf7ecd3 100644 --- a/ui/widgets/chat.h +++ b/ui/widgets/chat.h @@ -37,6 +37,7 @@ public: protected slots: void onContactChanged(Models::Item* item, int row, int col); + void onEncryptionButtonClicked() override; protected: Shared::Message createMessage() const override; @@ -44,6 +45,7 @@ protected: private: void updateState(); + void updateEncryptionState(); private: Models::Contact* contact; diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index e6316c7..8336d5e 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -91,6 +91,7 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, connect(m_ui->clearButton, &QPushButton::clicked, this, &Conversation::clear); connect(m_ui->messageEditor->document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged, this, &Conversation::onTextEditDocSizeChanged); + connect(m_ui->encryptionButton, &QPushButton::clicked, this, &Conversation::onEncryptionButtonClicked); m_ui->messageEditor->installEventFilter(&ker); @@ -345,6 +346,8 @@ void Conversation::clear() { m_ui->messageEditor->clear(); } +void Conversation::onEncryptionButtonClicked() {} + void Conversation::setAvatar(const QString& path) { QPixmap pixmap; if (path.size() == 0) { diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index 8af1745..4db5029 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -119,6 +119,7 @@ protected slots: void onFeedContext(const QPoint &pos); void onMessageEditorContext(const QPoint &pos); void onMessageEditRequested(const QString& id); + virtual void onEncryptionButtonClicked(); public: const bool isMuc; diff --git a/ui/widgets/messageline/messagefeed.cpp b/ui/widgets/messageline/messagefeed.cpp index 1eba1fa..29f51d2 100644 --- a/ui/widgets/messageline/messagefeed.cpp +++ b/ui/widgets/messageline/messagefeed.cpp @@ -230,6 +230,7 @@ std::set Models::MessageFeed::detectChanges(c } void Models::MessageFeed::removeMessage(const QString& id) { + SHARED_UNUSED(id); //todo; } From 0a530bfa935ebda733a9c9d4cd965f44e365e26c Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 6 Nov 2023 20:57:08 -0300 Subject: [PATCH 112/137] encrypted messages now are displayed in the feed --- CMakeLists.txt | 4 +- core/handlers/messagehandler.cpp | 9 +- main/main.cpp | 3 +- resources/images/fallback/dark/big/lock.svg | 13 ++ resources/images/fallback/dark/big/shield.svg | 13 ++ resources/images/fallback/dark/big/unlock.svg | 14 ++ resources/images/fallback/dark/small/lock.svg | 13 ++ .../images/fallback/dark/small/shield.svg | 13 ++ .../images/fallback/dark/small/unlock.svg | 13 ++ resources/images/fallback/light/big/lock.svg | 13 ++ .../images/fallback/light/big/shield.svg | 13 ++ .../images/fallback/light/big/unlock.svg | 14 ++ .../images/fallback/light/small/lock.svg | 13 ++ .../images/fallback/light/small/shield.svg | 13 ++ .../images/fallback/light/small/unlock.svg | 13 ++ resources/resources.qrc | 12 + shared/enums.h | 4 +- shared/exception.h | 5 +- shared/global.cpp | 104 +++++---- shared/global.h | 10 +- shared/icons.h | 10 +- shared/message.cpp | 9 +- shared/message.h | 5 +- shared/pathcheck.h | 5 +- shared/utils.h | 5 +- ui/widgets/chat.h | 7 +- ui/widgets/conversation.cpp | 1 + ui/widgets/conversation.h | 3 +- ui/widgets/messageline/CMakeLists.txt | 1 - ui/widgets/messageline/messagedelegate.cpp | 212 ++++++++++-------- ui/widgets/messageline/messagedelegate.h | 50 ++--- ui/widgets/messageline/messagefeed.cpp | 54 +++-- ui/widgets/messageline/messagefeed.h | 8 +- ui/widgets/messageline/preview.h | 5 +- 34 files changed, 439 insertions(+), 245 deletions(-) create mode 100644 resources/images/fallback/dark/big/lock.svg create mode 100644 resources/images/fallback/dark/big/shield.svg create mode 100644 resources/images/fallback/dark/big/unlock.svg create mode 100644 resources/images/fallback/dark/small/lock.svg create mode 100644 resources/images/fallback/dark/small/shield.svg create mode 100644 resources/images/fallback/dark/small/unlock.svg create mode 100644 resources/images/fallback/light/big/lock.svg create mode 100644 resources/images/fallback/light/big/shield.svg create mode 100644 resources/images/fallback/light/big/unlock.svg create mode 100644 resources/images/fallback/light/small/lock.svg create mode 100644 resources/images/fallback/light/small/shield.svg create mode 100644 resources/images/fallback/light/small/unlock.svg diff --git a/CMakeLists.txt b/CMakeLists.txt index 1151cc1..552cba9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,9 +38,9 @@ option(WITH_OMEMO "Build OMEMO support module" ON) # Dependencies ## Qt if (NOT DEFINED QT_VERSION_MAJOR) - find_package(QT NAMES Qt5 REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) + find_package(QT NAMES Qt6 Qt5 CONFIG REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) else () - find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) + find_package(Qt${QT_VERSION_MAJOR} CONFIG REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) endif() find_package(Boost COMPONENTS) diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index f939b9e..2a546e9 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -117,9 +117,8 @@ bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgo qDebug() << "appending message" << sMsg.getId() << "to an out of roster contact"; } if (sMsg.getOutgoing()) { - if (sMsg.getForwarded()) { + if (sMsg.getForwarded()) sMsg.setState(Shared::Message::State::sent); - } } else { sMsg.setState(Shared::Message::State::delivered); } @@ -208,6 +207,12 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp target.setTo(source.to()); target.setBody(source.body()); target.setForwarded(forwarded); +#ifdef WITH_OMEMO + #if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) + if (source.encryptionMethod() == QXmpp::EncryptionMethod::Omemo2) + target.setEncryption(Shared::EncryptionProtocol::omemo2); + #endif +#endif if (guessing) outgoing = target.getFromJid() == acc->getBareJid(); diff --git a/main/main.cpp b/main/main.cpp index c8e962f..06c2c95 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -27,8 +27,7 @@ #include #endif -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { qRegisterMetaType("Shared::Message"); qRegisterMetaType("Shared::MessageInfo"); qRegisterMetaType("Shared::VCard"); diff --git a/resources/images/fallback/dark/big/lock.svg b/resources/images/fallback/dark/big/lock.svg new file mode 100644 index 0000000..d7835b3 --- /dev/null +++ b/resources/images/fallback/dark/big/lock.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/dark/big/shield.svg b/resources/images/fallback/dark/big/shield.svg new file mode 100644 index 0000000..c63b275 --- /dev/null +++ b/resources/images/fallback/dark/big/shield.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/dark/big/unlock.svg b/resources/images/fallback/dark/big/unlock.svg new file mode 100644 index 0000000..a5e4a4c --- /dev/null +++ b/resources/images/fallback/dark/big/unlock.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/dark/small/lock.svg b/resources/images/fallback/dark/small/lock.svg new file mode 100644 index 0000000..6d36522 --- /dev/null +++ b/resources/images/fallback/dark/small/lock.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/dark/small/shield.svg b/resources/images/fallback/dark/small/shield.svg new file mode 100644 index 0000000..c63b275 --- /dev/null +++ b/resources/images/fallback/dark/small/shield.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/dark/small/unlock.svg b/resources/images/fallback/dark/small/unlock.svg new file mode 100644 index 0000000..9f2f121 --- /dev/null +++ b/resources/images/fallback/dark/small/unlock.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/light/big/lock.svg b/resources/images/fallback/light/big/lock.svg new file mode 100644 index 0000000..5c7f4e1 --- /dev/null +++ b/resources/images/fallback/light/big/lock.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/light/big/shield.svg b/resources/images/fallback/light/big/shield.svg new file mode 100644 index 0000000..52f9c34 --- /dev/null +++ b/resources/images/fallback/light/big/shield.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/light/big/unlock.svg b/resources/images/fallback/light/big/unlock.svg new file mode 100644 index 0000000..0548efc --- /dev/null +++ b/resources/images/fallback/light/big/unlock.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/images/fallback/light/small/lock.svg b/resources/images/fallback/light/small/lock.svg new file mode 100644 index 0000000..a566da0 --- /dev/null +++ b/resources/images/fallback/light/small/lock.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/light/small/shield.svg b/resources/images/fallback/light/small/shield.svg new file mode 100644 index 0000000..52f9c34 --- /dev/null +++ b/resources/images/fallback/light/small/shield.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/images/fallback/light/small/unlock.svg b/resources/images/fallback/light/small/unlock.svg new file mode 100644 index 0000000..ff91b8e --- /dev/null +++ b/resources/images/fallback/light/small/unlock.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/resources/resources.qrc b/resources/resources.qrc index 58565fc..993255b 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -42,6 +42,9 @@ images/fallback/dark/big/add.svg images/fallback/dark/big/folder.svg images/fallback/dark/big/document-preview.svg + images/fallback/dark/big/shield.svg + images/fallback/dark/big/lock.svg + images/fallback/dark/big/unlock.svg images/fallback/dark/small/absent.svg @@ -84,6 +87,9 @@ images/fallback/dark/small/add.svg images/fallback/dark/small/folder.svg images/fallback/dark/small/document-preview.svg + images/fallback/dark/small/shield.svg + images/fallback/dark/small/lock.svg + images/fallback/dark/small/unlock.svg images/fallback/light/big/absent.svg @@ -126,6 +132,9 @@ images/fallback/light/big/add.svg images/fallback/light/big/folder.svg images/fallback/light/big/document-preview.svg + images/fallback/light/big/shield.svg + images/fallback/light/big/lock.svg + images/fallback/light/big/unlock.svg images/fallback/light/small/absent.svg @@ -168,5 +177,8 @@ images/fallback/light/small/add.svg images/fallback/light/small/folder.svg images/fallback/light/small/document-preview.svg + images/fallback/light/small/shield.svg + images/fallback/light/small/lock.svg + images/fallback/light/small/unlock.svg diff --git a/shared/enums.h b/shared/enums.h index c959883..6b668d6 100644 --- a/shared/enums.h +++ b/shared/enums.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef SHARED_ENUMS_H -#define SHARED_ENUMS_H +#pragma once #include @@ -170,4 +169,3 @@ static const EncryptionProtocol EncryptionProtocolHighest = EncryptionProtocol:: static const EncryptionProtocol EncryptionProtocolLowest = EncryptionProtocol::omemo2; } -#endif // SHARED_ENUMS_H diff --git a/shared/exception.h b/shared/exception.h index 4c66c2d..48d7eda 100644 --- a/shared/exception.h +++ b/shared/exception.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef EXCEPTION_H -#define EXCEPTION_H +#pragma once #include #include @@ -36,5 +35,3 @@ namespace Utils const char* what() const noexcept( true ); }; } - -#endif // EXCEPTION_H diff --git a/shared/global.cpp b/shared/global.cpp index 95168b2..97f3263 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -62,70 +62,78 @@ Shared::Global::ColorSchemeName Shared::Global::colorSchemeName = 0; Shared::Global::CreatePalette Shared::Global::createPalette = 0; #endif + + Shared::Global::Global(): availability({ - tr("Online", "Availability"), - tr("Away", "Availability"), - tr("Absent", "Availability"), - tr("Busy", "Availability"), - tr("Chatty", "Availability"), - tr("Invisible", "Availability"), - tr("Offline", "Availability") + QCoreApplication::translate("Global", "Online", "Availability"), + QCoreApplication::translate("Global", "Away", "Availability"), + QCoreApplication::translate("Global", "Absent", "Availability"), + QCoreApplication::translate("Global", "Busy", "Availability"), + QCoreApplication::translate("Global", "Chatty", "Availability"), + QCoreApplication::translate("Global", "Invisible", "Availability"), + QCoreApplication::translate("Global", "Offline", "Availability") }), connectionState({ - tr("Disconnected", "ConnectionState"), - tr("Scheduled", "ConnectionState"), - tr("Connecting", "ConnectionState"), - tr("Connected", "ConnectionState"), - tr("Error", "ConnectionState") + QCoreApplication::translate("Global", "Disconnected", "ConnectionState"), + QCoreApplication::translate("Global", "Scheduled", "ConnectionState"), + QCoreApplication::translate("Global", "Connecting", "ConnectionState"), + QCoreApplication::translate("Global", "Connected", "ConnectionState"), + QCoreApplication::translate("Global", "Error", "ConnectionState") }), subscriptionState({ - tr("None", "SubscriptionState"), - tr("From", "SubscriptionState"), - tr("To", "SubscriptionState"), - tr("Both", "SubscriptionState"), - tr("Unknown", "SubscriptionState") + QCoreApplication::translate("Global", "None", "SubscriptionState"), + QCoreApplication::translate("Global", "From", "SubscriptionState"), + QCoreApplication::translate("Global", "To", "SubscriptionState"), + QCoreApplication::translate("Global", "Both", "SubscriptionState"), + QCoreApplication::translate("Global", "Unknown", "SubscriptionState") }), affiliation({ - tr("Unspecified", "Affiliation"), - tr("Outcast", "Affiliation"), - tr("Nobody", "Affiliation"), - tr("Member", "Affiliation"), - tr("Admin", "Affiliation"), - tr("Owner", "Affiliation") + QCoreApplication::translate("Global", "Unspecified", "Affiliation"), + QCoreApplication::translate("Global", "Outcast", "Affiliation"), + QCoreApplication::translate("Global", "Nobody", "Affiliation"), + QCoreApplication::translate("Global", "Member", "Affiliation"), + QCoreApplication::translate("Global", "Admin", "Affiliation"), + QCoreApplication::translate("Global", "Owner", "Affiliation") }), role({ - tr("Unspecified", "Role"), - tr("Nobody", "Role"), - tr("Visitor", "Role"), - tr("Participant", "Role"), - tr("Moderator", "Role") + QCoreApplication::translate("Global", "Unspecified", "Role"), + QCoreApplication::translate("Global", "Nobody", "Role"), + QCoreApplication::translate("Global", "Visitor", "Role"), + QCoreApplication::translate("Global", "Participant", "Role"), + QCoreApplication::translate("Global", "Moderator", "Role") }), messageState({ - tr("Pending", "MessageState"), - tr("Sent", "MessageState"), - tr("Delivered", "MessageState"), - tr("Error", "MessageState") + QCoreApplication::translate("Global", "Pending", "MessageState"), + QCoreApplication::translate("Global", "Sent", "MessageState"), + QCoreApplication::translate("Global", "Delivered", "MessageState"), + QCoreApplication::translate("Global", "Error", "MessageState") }), accountPassword({ - tr("Plain", "AccountPassword"), - tr("Jammed", "AccountPassword"), - tr("Always Ask", "AccountPassword"), - tr("KWallet", "AccountPassword") + QCoreApplication::translate("Global", "Plain", "AccountPassword"), + QCoreApplication::translate("Global", "Jammed", "AccountPassword"), + QCoreApplication::translate("Global", "Always Ask", "AccountPassword"), + QCoreApplication::translate("Global", "KWallet", "AccountPassword") }), trustLevel({ - tr("Undecided", "TrustLevel"), - tr("Automatically distrusted", "TrustLevel"), - tr("Manually distrusted", "TrustLevel"), - tr("Automatically trusted", "TrustLevel"), - tr("Manually trusted", "TrustLevel"), - tr("Authenticated", "TrustLevel") + QCoreApplication::translate("Global", "Undecided", "TrustLevel"), + QCoreApplication::translate("Global", "Automatically distrusted", "TrustLevel"), + QCoreApplication::translate("Global", "Manually distrusted", "TrustLevel"), + QCoreApplication::translate("Global", "Automatically trusted", "TrustLevel"), + QCoreApplication::translate("Global", "Manually trusted", "TrustLevel"), + QCoreApplication::translate("Global", "Authenticated", "TrustLevel") + }), + encryptionProtocols({ + QCoreApplication::translate("Global", "None", "EncryptionProtocol"), + QCoreApplication::translate("Global", "OMEMO 0", "EncryptionProtocol"), + QCoreApplication::translate("Global", "OMEMO 1", "EncryptionProtocol"), + QCoreApplication::translate("Global", "OMEMO 2", "EncryptionProtocol") }), accountPasswordDescription({ - tr("Your password is going to be stored in config file in plain text", "AccountPasswordDescription"), - tr("Your password is going to be stored in config file but jammed with constant encryption key you can find in program source code. It might look like encryption but it's not", "AccountPasswordDescription"), - tr("Squawk is going to query you for the password on every start of the program", "AccountPasswordDescription"), - tr("Your password is going to be stored in KDE wallet storage (KWallet). You're going to be queried for permissions", "AccountPasswordDescription") + QCoreApplication::translate("Global", "Your password is going to be stored in config file in plain text", "AccountPasswordDescription"), + QCoreApplication::translate("Global", "Your password is going to be stored in config file but jammed with constant encryption key you can find in program source code. It might look like encryption but it's not", "AccountPasswordDescription"), + QCoreApplication::translate("Global", "Squawk is going to query you for the password on every start of the program", "AccountPasswordDescription"), + QCoreApplication::translate("Global", "Your password is going to be stored in KDE wallet storage (KWallet). You're going to be queried for permissions", "AccountPasswordDescription") }), defaultSystemStyle(QApplication::style()->objectName()), defaultSystemPalette(QApplication::palette()), @@ -262,6 +270,10 @@ QString Shared::Global::getName(Shared::TrustLevel tl) { return instance->trustLevel[static_cast(tl)]; } +QString Shared::Global::getName(EncryptionProtocol ep) { + return instance->encryptionProtocols[static_cast(ep)]; +} + void Shared::Global::setSupported(const QString& pluginName, bool support) { std::map::iterator itr = instance->pluginSupport.find(pluginName); if (itr != instance->pluginSupport.end()) { diff --git a/shared/global.h b/shared/global.h index 578fc42..6d23c2f 100644 --- a/shared/global.h +++ b/shared/global.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef SHARED_GLOBAL_H -#define SHARED_GLOBAL_H +#pragma once #include "enums.h" #include "message.h" @@ -48,7 +47,6 @@ namespace Shared { class Global { - Q_DECLARE_TR_FUNCTIONS(Global) public: struct FileInfo { enum class Preview { @@ -75,6 +73,7 @@ namespace Shared { static QString getName(Message::State rl); static QString getName(AccountPassword ap); static QString getName(TrustLevel tl); + static QString getName(EncryptionProtocol ep); static QString getDescription(AccountPassword ap); @@ -86,6 +85,7 @@ namespace Shared { const std::deque messageState; const std::deque accountPassword; const std::deque trustLevel; + const std::deque encryptionProtocols; const std::deque accountPasswordDescription; @@ -124,7 +124,7 @@ namespace Shared { class EnumOutOfRange: public Utils::Exception { public: EnumOutOfRange(const std::string& p_name):Exception(), name(p_name) {} - + std::string getMessage() const{ return "An attempt to get enum " + name + " from integer out of range of that enum"; } @@ -161,5 +161,3 @@ namespace Shared { #endif }; } - -#endif // SHARED_GLOBAL_H diff --git a/shared/icons.h b/shared/icons.h index cf31b3b..dadb87e 100644 --- a/shared/icons.h +++ b/shared/icons.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef SHARED_ICONS_H -#define SHARED_ICONS_H +#pragma once #include @@ -175,8 +174,9 @@ static const std::map> icons = { {"unfavorite", {"draw-star", "unfavorite"}}, {"list-add", {"list-add", "add"}}, {"folder", {"folder", "folder"}}, - {"document-preview", {"document-preview", "document-preview"}} + {"document-preview", {"document-preview", "document-preview"}}, + {"secure", {"security-high", "shield"}}, + {"lock", {"lock", "lock"}}, + {"unlock", {"unlock", "unlock"}} }; } - -#endif // SHARED_ICONS_H diff --git a/shared/message.cpp b/shared/message.cpp index 5b535c1..caf8a97 100644 --- a/shared/message.cpp +++ b/shared/message.cpp @@ -37,7 +37,8 @@ Shared::Message::Message(Shared::Message::Type p_type): originalMessage(), lastModified(), stanzaId(), - attachPath() + attachPath(), + encryption(EncryptionProtocol::none) {} Shared::Message::Message(): @@ -58,7 +59,8 @@ Shared::Message::Message(): originalMessage(), lastModified(), stanzaId(), - attachPath() + attachPath(), + encryption(EncryptionProtocol::none) {} QString Shared::Message::getBody() const { @@ -234,9 +236,8 @@ void Shared::Message::setType(Shared::Message::Type t) { void Shared::Message::setState(Shared::Message::State p_state) { state = p_state; - if (state != State::error) { + if (state != State::error) errorText = ""; - } } bool Shared::Message::serverStored() const { diff --git a/shared/message.h b/shared/message.h index d7df9f3..b81881b 100644 --- a/shared/message.h +++ b/shared/message.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef SHAPER_MESSAGE_H -#define SHAPER_MESSAGE_H +#pragma once #include #include @@ -154,5 +153,3 @@ private: }; } - -#endif // SHAPER_MESSAGE_H diff --git a/shared/pathcheck.h b/shared/pathcheck.h index 3ca612b..1608241 100644 --- a/shared/pathcheck.h +++ b/shared/pathcheck.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef PATHCHECK_H -#define PATHCHECK_H +#pragma once #include #include @@ -40,5 +39,3 @@ QString squawkifyPath(QString path); bool isSubdirectoryOfSettings(const QString& path); } - -#endif // PATHCHECK_H diff --git a/shared/utils.h b/shared/utils.h index 0329cee..019f611 100644 --- a/shared/utils.h +++ b/shared/utils.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef SHARED_UTILS_H -#define SHARED_UTILS_H +#pragma once #include #include @@ -76,5 +75,3 @@ enum class Hover { anchor }; } - -#endif // SHARED_UTILS_H diff --git a/ui/widgets/chat.h b/ui/widgets/chat.h index cf7ecd3..912299e 100644 --- a/ui/widgets/chat.h +++ b/ui/widgets/chat.h @@ -24,12 +24,11 @@ #include "shared/icons.h" #include "shared/global.h" -namespace Ui -{ +namespace Ui { class Chat; } -class Chat : public Conversation -{ + +class Chat : public Conversation { Q_OBJECT public: Chat(Models::Account* acc, Models::Contact* p_contact, QWidget* parent = 0); diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 8336d5e..32c38a8 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -120,6 +120,7 @@ Conversation::~Conversation() { delete contextMenu; element->feed->decrementObservers(); + delete m_ui; } void Conversation::onAccountChanged(Models::Item* item, int row, int col) { diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index 4db5029..73e6536 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -20,6 +20,7 @@ #define CONVERSATION_H #include +#include #include #include #include @@ -134,7 +135,7 @@ protected: Models::Element* element; QString palJid; QString activePalResource; - QScopedPointer m_ui; + Ui::Conversation* m_ui; KeyEnterReceiver ker; QString thread; QLabel* statusIcon; diff --git a/ui/widgets/messageline/CMakeLists.txt b/ui/widgets/messageline/CMakeLists.txt index 7ded76b..1524509 100644 --- a/ui/widgets/messageline/CMakeLists.txt +++ b/ui/widgets/messageline/CMakeLists.txt @@ -14,5 +14,4 @@ set(HEADER_FILES target_sources(squawk PRIVATE ${SOURCE_FILES} - ${HEADER_FILES} ) diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index 35a73c2..b89b438 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -47,22 +47,23 @@ MessageDelegate::MessageDelegate(QObject* parent): dateFont(Shared::Global::getInstance()->smallFont), nickMetrics(Shared::Global::getInstance()->headerFontMetrics), dateMetrics(Shared::Global::getInstance()->smallFontMetrics), - bodyRenderer(new QTextDocument()), + bodyRenderer(), buttonHeight(0), buttonWidth(0), barHeight(0), - buttons(new std::map()), - bars(new std::map()), - statusIcons(new std::map()), - pencilIcons(new std::map()), - previews(new std::map()), - idsToKeep(new std::set()), + buttons(), + bars(), + statusIcons(), + pencilIcons(), + encryptionIcons(), + previews(), + idsToKeep(), clearingWidgets(false), currentId(""), selection(0, 0) { - bodyRenderer->setDocumentMargin(0); - bodyRenderer->setDefaultFont(bodyFont); + bodyRenderer.setDocumentMargin(0); + bodyRenderer.setDefaultFont(bodyFont); QPushButton btn(QCoreApplication::translate("MessageLine", "Download")); buttonHeight = btn.sizeHint().height(); @@ -73,28 +74,23 @@ MessageDelegate::MessageDelegate(QObject* parent): } MessageDelegate::~MessageDelegate() { - for (const std::pair& pair: *buttons) + for (const std::pair& pair: buttons) delete pair.second; - for (const std::pair& pair: *bars) + for (const std::pair& pair: bars) delete pair.second; - for (const std::pair& pair: *statusIcons) + for (const std::pair& pair: statusIcons) delete pair.second; - for (const std::pair& pair: *pencilIcons) + for (const std::pair& pair: pencilIcons) + delete pair.second; + + for (const std::pair& pair: encryptionIcons) delete pair.second; - for (const std::pair& pair: *previews) + for (const std::pair& pair: previews) delete pair.second; - - delete statusIcons; - delete pencilIcons; - delete idsToKeep; - delete buttons; - delete bars; - delete previews; - delete bodyRenderer; } void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { @@ -168,6 +164,7 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio painter->setPen(q); painter->drawText(opt.rect, opt.displayAlignment, dateString, &rect); int currentY = opt.rect.y(); + int statusOffset = statusIconSize; if (data.sentByMe) { QLabel* statusIcon = getStatusIcon(data); @@ -176,30 +173,43 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio statusIcon->show(); opt.rect.adjust(0, statusIconSize + textMargin, 0, 0); + statusOffset = statusIconSize + margin; } if (data.correction.corrected) { QLabel* pencilIcon = getPencilIcon(data); - pencilIcon->setParent(vp); if (data.sentByMe) - pencilIcon->move(opt.rect.left() + statusIconSize + margin, currentY); + pencilIcon->move(opt.rect.left() + statusOffset, currentY); else - pencilIcon->move(opt.rect.right() - statusIconSize - margin, currentY); + pencilIcon->move(opt.rect.right() - statusOffset, currentY); pencilIcon->show(); + statusOffset += statusIconSize + margin; } else { - std::map::const_iterator itr = pencilIcons->find(data.id); - if (itr != pencilIcons->end()) { + std::map::const_iterator itr = pencilIcons.find(data.id); + if (itr != pencilIcons.end()) { delete itr->second; - pencilIcons->erase(itr); + pencilIcons.erase(itr); } } + + if (data.encryption != Shared::EncryptionProtocol::none) { + QLabel* shieldIcon = getEncryptionIcon(data); + shieldIcon->setParent(vp); + if (data.sentByMe) + shieldIcon->move(opt.rect.left() + statusOffset, currentY); + else + shieldIcon->move(opt.rect.right() - statusOffset, currentY); + + shieldIcon->show(); + statusOffset += statusIconSize + margin; + } painter->restore(); if (clearingWidgets) - idsToKeep->insert(data.id); + idsToKeep.insert(data.id); } void MessageDelegate::paintBubble(const Models::FeedItem& data, QPainter* painter, const QStyleOptionViewItem& option) const { @@ -281,11 +291,11 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel Models::FeedItem data = qvariant_cast(vi); QSize messageSize(0, 0); if (data.text.size() > 0) { - bodyRenderer->setPlainText(data.text); - bodyRenderer->setTextWidth(messageRect.size().width()); + bodyRenderer.setPlainText(data.text); + bodyRenderer.setTextWidth(messageRect.size().width()); - QSizeF size = bodyRenderer->size(); - size.setWidth(bodyRenderer->idealWidth()); + QSizeF size = bodyRenderer.size(); + size.setWidth(bodyRenderer.idealWidth()); messageSize = QSize(std::ceil(size.width()), std::ceil(size.height())); messageSize.rheight() += textMargin; } @@ -338,9 +348,12 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel messageSize.rheight() += dateSize.height() > statusIconSize ? dateSize.height() : statusIconSize; int statusWidth = dateSize.width() + statusIconSize + margin; - if (data.correction.corrected) { + if (data.correction.corrected) statusWidth += statusIconSize + margin; - } + + if (data.encryption != Shared::EncryptionProtocol::none) + statusWidth += statusIconSize + margin; + messageSize.setWidth(std::max(statusWidth, messageSize.width())); messageSize.rwidth() += 2 * bubbleMargin; @@ -399,10 +412,10 @@ QString MessageDelegate::getAnchor(const QPoint& point, const QModelIndex& index if (localHint.contains(point)) { QPoint translated = point - localHint.topLeft(); - bodyRenderer->setHtml(Shared::processMessageBody(data.text)); - bodyRenderer->setTextWidth(localHint.size().width()); + bodyRenderer.setHtml(Shared::processMessageBody(data.text)); + bodyRenderer.setTextWidth(localHint.size().width()); - return bodyRenderer->documentLayout()->anchorAt(translated); + return bodyRenderer.documentLayout()->anchorAt(translated); } } @@ -424,13 +437,13 @@ QString MessageDelegate::leftDoubleClick(const QPoint& point, const QModelIndex& if (localHint.contains(point)) { QPoint translated = point - localHint.topLeft(); - bodyRenderer->setHtml(Shared::processMessageBody(data.text)); - bodyRenderer->setTextWidth(localHint.size().width()); + bodyRenderer.setHtml(Shared::processMessageBody(data.text)); + bodyRenderer.setTextWidth(localHint.size().width()); - QAbstractTextDocumentLayout* lay = bodyRenderer->documentLayout(); + QAbstractTextDocumentLayout* lay = bodyRenderer.documentLayout(); int position = lay->hitTest(translated, Qt::HitTestAccuracy::FuzzyHit); - QTextCursor cursor(bodyRenderer); + QTextCursor cursor(&bodyRenderer); cursor.setPosition(position, QTextCursor::MoveAnchor); cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor); cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); @@ -456,10 +469,10 @@ Shared::Hover MessageDelegate::hoverType(const QPoint& point, const QModelIndex& if (localHint.contains(point)) { QPoint translated = point - localHint.topLeft(); - bodyRenderer->setHtml(Shared::processMessageBody(data.text)); - bodyRenderer->setTextWidth(localHint.size().width()); + bodyRenderer.setHtml(Shared::processMessageBody(data.text)); + bodyRenderer.setTextWidth(localHint.size().width()); - QAbstractTextDocumentLayout* lay = bodyRenderer->documentLayout(); + QAbstractTextDocumentLayout* lay = bodyRenderer.documentLayout(); QString anchor = lay->anchorAt(translated); if (anchor.size() > 0) { @@ -489,15 +502,15 @@ QString MessageDelegate::mouseDrag(const QPoint& start, const QPoint& end, const last.setY(std::max(last.y(), 0)); last.setY(std::min(last.y(), localHint.height())); - bodyRenderer->setHtml(Shared::processMessageBody(data.text)); - bodyRenderer->setTextWidth(localHint.size().width()); - selection.first = bodyRenderer->documentLayout()->hitTest(first, Qt::HitTestAccuracy::FuzzyHit); - selection.second = bodyRenderer->documentLayout()->hitTest(last, Qt::HitTestAccuracy::FuzzyHit); + bodyRenderer.setHtml(Shared::processMessageBody(data.text)); + bodyRenderer.setTextWidth(localHint.size().width()); + selection.first = bodyRenderer.documentLayout()->hitTest(first, Qt::HitTestAccuracy::FuzzyHit); + selection.second = bodyRenderer.documentLayout()->hitTest(last, Qt::HitTestAccuracy::FuzzyHit); currentId = data.id; if (selection.first != selection.second) { - QTextCursor cursor(bodyRenderer); + QTextCursor cursor(&bodyRenderer); cursor.setPosition(selection.first, QTextCursor::MoveAnchor); cursor.setPosition(selection.second, QTextCursor::KeepAnchor); return cursor.selectedText(); @@ -562,17 +575,17 @@ int MessageDelegate::paintBar(QProgressBar* bar, QPainter* painter, bool sentByM int MessageDelegate::paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const { Preview* preview = 0; - std::map::iterator itr = previews->find(data.id); + std::map::iterator itr = previews.find(data.id); QSize size = option.rect.size(); QString path = Shared::resolvePath(data.attach.localPath); - if (itr != previews->end()) { + if (itr != previews.end()) { preview = itr->second; preview->actualize(path, size, option.rect.topLeft()); } else { QWidget* vp = static_cast(painter->device()); preview = new Preview(path, size, option.rect.topLeft(), vp); - previews->insert(std::make_pair(data.id, preview)); + previews.insert(std::make_pair(data.id, preview)); } if (!preview->isFileReachable()) //this is the situation when the file preview couldn't be painted because the file was moved @@ -585,15 +598,15 @@ int MessageDelegate::paintPreview(const Models::FeedItem& data, QPainter* painte } QPushButton * MessageDelegate::getButton(const Models::FeedItem& data) const { - std::map::const_iterator itr = buttons->find(data.id); + std::map::const_iterator itr = buttons.find(data.id); FeedButton* result = 0; - if (itr != buttons->end()) { + if (itr != buttons.end()) { result = itr->second; } else { - std::map::const_iterator barItr = bars->find(data.id); - if (barItr != bars->end()) { + std::map::const_iterator barItr = bars.find(data.id); + if (barItr != bars.end()) { delete barItr->second; - bars->erase(barItr); + bars.erase(barItr); } } @@ -601,7 +614,7 @@ QPushButton * MessageDelegate::getButton(const Models::FeedItem& data) const { result = new FeedButton(); result->messageId = data.id; result->setText(QCoreApplication::translate("MessageLine", "Download")); - buttons->insert(std::make_pair(data.id, result)); + buttons.insert(std::make_pair(data.id, result)); connect(result, &QPushButton::clicked, this, &MessageDelegate::onButtonPushed); } @@ -609,22 +622,22 @@ QPushButton * MessageDelegate::getButton(const Models::FeedItem& data) const { } QProgressBar * MessageDelegate::getBar(const Models::FeedItem& data) const { - std::map::const_iterator barItr = bars->find(data.id); + std::map::const_iterator barItr = bars.find(data.id); QProgressBar* result = 0; - if (barItr != bars->end()) { + if (barItr != bars.end()) { result = barItr->second; } else { - std::map::const_iterator itr = buttons->find(data.id); - if (itr != buttons->end()) { + std::map::const_iterator itr = buttons.find(data.id); + if (itr != buttons.end()) { delete itr->second; - buttons->erase(itr); + buttons.erase(itr); } } if (result == 0) { result = new QProgressBar(); result->setRange(0, 100); - bars->insert(std::make_pair(data.id, result)); + bars.insert(std::make_pair(data.id, result)); } result->setValue(data.attach.progress * 100); @@ -633,14 +646,14 @@ QProgressBar * MessageDelegate::getBar(const Models::FeedItem& data) const { } QLabel * MessageDelegate::getStatusIcon(const Models::FeedItem& data) const { - std::map::const_iterator itr = statusIcons->find(data.id); + std::map::const_iterator itr = statusIcons.find(data.id); QLabel* result = 0; - if (itr != statusIcons->end()) { + if (itr != statusIcons.end()) { result = itr->second; } else { result = new QLabel(); - statusIcons->insert(std::make_pair(data.id, result)); + statusIcons.insert(std::make_pair(data.id, result)); } QIcon q(Shared::icon(Shared::messageStateThemeIcons[static_cast(data.state)])); @@ -657,17 +670,17 @@ QLabel * MessageDelegate::getStatusIcon(const Models::FeedItem& data) const { return result; } -QLabel * MessageDelegate::getPencilIcon(const Models::FeedItem& data) const { - std::map::const_iterator itr = pencilIcons->find(data.id); +QLabel* MessageDelegate::getPencilIcon(const Models::FeedItem& data) const { + std::map::const_iterator itr = pencilIcons.find(data.id); QLabel* result = 0; - if (itr != pencilIcons->end()) { + if (itr != pencilIcons.end()) { result = itr->second; } else { result = new QLabel(); QIcon icon = Shared::icon("edit-rename"); result->setPixmap(icon.pixmap(statusIconSize)); - pencilIcons->insert(std::make_pair(data.id, result)); + pencilIcons.insert(std::make_pair(data.id, result)); } result->setToolTip("Last time edited: " + data.correction.lastCorrection.toLocalTime().toString() @@ -676,30 +689,46 @@ QLabel * MessageDelegate::getPencilIcon(const Models::FeedItem& data) const { return result; } +QLabel* MessageDelegate::getEncryptionIcon(const Models::FeedItem& data) const { + std::map::const_iterator itr = encryptionIcons.find(data.id); + QLabel* result = 0; + + if (itr != encryptionIcons.end()) { + result = itr->second; + } else { + result = new QLabel(); + QIcon icon = Shared::icon("secure"); + result->setPixmap(icon.pixmap(statusIconSize)); + encryptionIcons.insert(std::make_pair(data.id, result)); + result->setToolTip("Encrypted: " + Shared::Global::getName(data.encryption)); + } + + return result; +} + template -void removeElements(std::map* elements, std::set* idsToKeep) { +void removeElements(std::map& elements, std::set& idsToKeep) { std::set toRemove; - for (const std::pair& pair: *elements) { - if (idsToKeep->find(pair.first) == idsToKeep->end()) { + for (const std::pair& pair: elements) { + if (idsToKeep.find(pair.first) == idsToKeep.end()) { delete pair.second; toRemove.insert(pair.first); } } - for (const QString& key : toRemove) { - elements->erase(key); - } + for (const QString& key : toRemove) + elements.erase(key); } int MessageDelegate::paintBody(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const { if (data.text.size() > 0) { - bodyRenderer->setHtml(Shared::processMessageBody(data.text)); - bodyRenderer->setTextWidth(option.rect.size().width()); + bodyRenderer.setHtml(Shared::processMessageBody(data.text)); + bodyRenderer.setTextWidth(option.rect.size().width()); painter->save(); painter->translate(option.rect.topLeft()); if (data.id == currentId) { - QTextCursor cursor(bodyRenderer); + QTextCursor cursor(&bodyRenderer); cursor.setPosition(selection.first, QTextCursor::MoveAnchor); cursor.setPosition(selection.second, QTextCursor::KeepAnchor); QTextCharFormat format = cursor.charFormat(); @@ -708,10 +737,10 @@ int MessageDelegate::paintBody(const Models::FeedItem& data, QPainter* painter, cursor.setCharFormat(format); } - bodyRenderer->drawContents(painter); + bodyRenderer.drawContents(painter); painter->restore(); - QSize bodySize(std::ceil(bodyRenderer->idealWidth()), std::ceil(bodyRenderer->size().height())); + QSize bodySize(std::ceil(bodyRenderer.idealWidth()), std::ceil(bodyRenderer.size().height())); option.rect.adjust(0, bodySize.height() + textMargin, 0, 0); return bodySize.width(); @@ -720,7 +749,7 @@ int MessageDelegate::paintBody(const Models::FeedItem& data, QPainter* painter, } void MessageDelegate::beginClearWidgets() { - idsToKeep->clear(); + idsToKeep.clear(); clearingWidgets = true; } @@ -730,9 +759,10 @@ void MessageDelegate::endClearWidgets() { removeElements(bars, idsToKeep); removeElements(statusIcons, idsToKeep); removeElements(pencilIcons, idsToKeep); + removeElements(encryptionIcons, idsToKeep); removeElements(previews, idsToKeep); - idsToKeep->clear(); + idsToKeep.clear(); clearingWidgets = false; } } @@ -743,15 +773,15 @@ void MessageDelegate::onButtonPushed() const { } void MessageDelegate::clearHelperWidget(const Models::FeedItem& data) const { - std::map::const_iterator itr = buttons->find(data.id); - if (itr != buttons->end()) { + std::map::const_iterator itr = buttons.find(data.id); + if (itr != buttons.end()) { delete itr->second; - buttons->erase(itr); + buttons.erase(itr); } else { - std::map::const_iterator barItr = bars->find(data.id); - if (barItr != bars->end()) { + std::map::const_iterator barItr = bars.find(data.id); + if (barItr != bars.end()) { delete barItr->second; - bars->erase(barItr); + bars.erase(barItr); } } } diff --git a/ui/widgets/messageline/messagedelegate.h b/ui/widgets/messageline/messagedelegate.h index 16e39ff..90ef819 100644 --- a/ui/widgets/messageline/messagedelegate.h +++ b/ui/widgets/messageline/messagedelegate.h @@ -1,23 +1,22 @@ /* - * Squawk messenger. + * Squawk messenger. * Copyright (C) 2019 Yury Gubich - * + * * 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 MESSAGEDELEGATE_H -#define MESSAGEDELEGATE_H +#pragma once #include #include @@ -44,17 +43,16 @@ namespace Models { struct FeedItem; }; -class MessageDelegate : public QStyledItemDelegate -{ +class MessageDelegate : public QStyledItemDelegate { Q_OBJECT public: MessageDelegate(QObject *parent = nullptr); ~MessageDelegate(); - + void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; //void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const override; - + bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) override; void endClearWidgets(); void beginClearWidgets(); @@ -66,12 +64,12 @@ public: static int avatarHeight; static int margin; - + signals: void buttonPushed(const QString& messageId) const; void invalidPath(const QString& messageId) const; void openLink(const QString& href) const; - + protected: int paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const; int paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const; @@ -85,6 +83,7 @@ protected: QProgressBar* getBar(const Models::FeedItem& data) const; QLabel* getStatusIcon(const Models::FeedItem& data) const; QLabel* getPencilIcon(const Models::FeedItem& data) const; + QLabel* getEncryptionIcon(const Models::FeedItem& data) const; void clearHelperWidget(const Models::FeedItem& data) const; bool needToDrawAvatar(const QModelIndex& index, const Models::FeedItem& data, const QStyleOptionViewItem& option) const; @@ -92,36 +91,35 @@ protected: QRect getHoveredMessageBodyRect(const QModelIndex& index, const Models::FeedItem& data, const QRect& sizeHint) const; QString getAnchor(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const; - + protected slots: void onButtonPushed() const; - + private: class FeedButton : public QPushButton { public: QString messageId; }; - + const QFont& bodyFont; const QFont& nickFont; const QFont& dateFont; const QFontMetrics& nickMetrics; const QFontMetrics& dateMetrics; - QTextDocument* bodyRenderer; - + mutable QTextDocument bodyRenderer; + int buttonHeight; int buttonWidth; int barHeight; - - std::map* buttons; - std::map* bars; - std::map* statusIcons; - std::map* pencilIcons; - std::map* previews; - std::set* idsToKeep; + + mutable std::map buttons; + mutable std::map bars; + mutable std::map statusIcons; + mutable std::map pencilIcons; + mutable std::map encryptionIcons; + mutable std::map previews; + mutable std::set idsToKeep; bool clearingWidgets; QString currentId; std::pair selection; }; - -#endif // MESSAGEDELEGATE_H diff --git a/ui/widgets/messageline/messagefeed.cpp b/ui/widgets/messageline/messagefeed.cpp index 29f51d2..370af36 100644 --- a/ui/widgets/messageline/messagefeed.cpp +++ b/ui/widgets/messageline/messagefeed.cpp @@ -29,6 +29,7 @@ const QHash Models::MessageFeed::roles = { {Sender, "sender"}, {Date, "date"}, {DeliveryState, "deliveryState"}, + {Encryption, "encryption"}, {Correction, "correction"}, {SentByMe,"sentByMe"}, {Avatar, "avatar"}, @@ -51,20 +52,16 @@ Models::MessageFeed::MessageFeed(const Element* ri, QObject* parent): failedUploads(), unreadMessages(new std::set()), observersAmount(0) -{ -} +{} -Models::MessageFeed::~MessageFeed() -{ +Models::MessageFeed::~MessageFeed() { delete unreadMessages; - for (Shared::Message* message : storage) { + for (Shared::Message* message : storage) delete message; - } } -void Models::MessageFeed::addMessage(const Shared::Message& msg) -{ +void Models::MessageFeed::addMessage(const Shared::Message& msg) { QString id = msg.getId(); StorageById::const_iterator itr = indexById.find(id); if (itr != indexById.end()) { @@ -75,11 +72,11 @@ void Models::MessageFeed::addMessage(const Shared::Message& msg) Shared::Message* copy = new Shared::Message(msg); StorageByTime::const_iterator tItr = indexByTime.upper_bound(msg.getTime()); int position; - if (tItr == indexByTime.end()) { + if (tItr == indexByTime.end()) position = storage.size(); - } else { + else position = indexByTime.rank(tItr); - } + beginInsertRows(QModelIndex(), position, position); storage.insert(copy); endInsertRows(); @@ -93,8 +90,7 @@ void Models::MessageFeed::addMessage(const Shared::Message& msg) } } -void Models::MessageFeed::changeMessage(const QString& id, const QMap& data) -{ +void Models::MessageFeed::changeMessage(const QString& id, const QMap& data) { StorageById::iterator itr = indexById.find(id); if (itr == indexById.end()) { qDebug() << "received a command to change a message, but the message couldn't be found, skipping"; @@ -159,9 +155,8 @@ void Models::MessageFeed::changeMessage(const QString& id, const QMap cr; - for (MessageRoles role : changeRoles) { + for (MessageRoles role : changeRoles) cr.push_back(role); - } emit dataChanged(index, index, cr); @@ -173,14 +168,15 @@ void Models::MessageFeed::changeMessage(const QString& id, const QMap Models::MessageFeed::detectChanges(const Shared::Message& msg, const QMap& data) const -{ +std::set Models::MessageFeed::detectChanges( + const Shared::Message& msg, + const QMap& data +) const { std::set roles; Shared::Message::State state = msg.getState(); QMap::const_iterator itr = data.find("state"); - if (itr != data.end() && static_cast(itr.value().toUInt()) != state) { + if (itr != data.end() && static_cast(itr.value().toUInt()) != state) roles.insert(MessageRoles::DeliveryState); - } itr = data.find("outOfBandUrl"); bool att = false; @@ -255,9 +251,8 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const { case Qt::DisplayRole: case Text: { QString body = msg->getBody(); - if (body != msg->getOutOfBandUrl()) { + if (body != msg->getOutOfBandUrl()) answer = body; - } } break; case Sender: @@ -276,6 +271,9 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const { case DeliveryState: answer = static_cast(msg->getState()); break; + case Encryption: + answer = QVariant::fromValue(msg->getEncryption()); + break; case Correction: answer.setValue(fillCorrection(*msg));; break; @@ -318,13 +316,13 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const { item.sentByMe = sentByMe(*msg); item.date = msg->getTime(); item.state = msg->getState(); + item.encryption = msg->getEncryption(); item.error = msg->getErrorText(); item.correction = fillCorrection(*msg); QString body = msg->getBody(); - if (body != msg->getOutOfBandUrl()) { + if (body != msg->getOutOfBandUrl()) item.text = body; - } item.avatar.clear(); if (item.sentByMe) { @@ -419,13 +417,11 @@ QModelIndex Models::MessageFeed::index(int row, int column, const QModelIndex& p return QModelIndex(); StorageByTime::iterator itr = indexByTime.nth(row); - if (itr != indexByTime.end()) { - Shared::Message* msg = *itr; - - return createIndex(row, column, msg); - } else { + if (itr == indexByTime.end()) return QModelIndex(); - } + + Shared::Message* msg = *itr; + return createIndex(row, column, msg); } QHash Models::MessageFeed::roleNames() const {return roles;} diff --git a/ui/widgets/messageline/messagefeed.h b/ui/widgets/messageline/messagefeed.h index db174d2..5cceae9 100644 --- a/ui/widgets/messageline/messagefeed.h +++ b/ui/widgets/messageline/messagefeed.h @@ -16,12 +16,12 @@ * along with this program. If not, see . */ -#ifndef MESSAGEFEED_H -#define MESSAGEFEED_H +#pragma once #include #include #include +#include #include @@ -100,6 +100,7 @@ public: Sender, Date, DeliveryState, + Encryption, Correction, SentByMe, Avatar, @@ -216,6 +217,7 @@ struct FeedItem { Edition correction; QDateTime date; Shared::Message::State state; + Shared::EncryptionProtocol encryption; Attachment attach; }; }; @@ -224,4 +226,4 @@ Q_DECLARE_METATYPE(Models::Attachment); Q_DECLARE_METATYPE(Models::Edition); Q_DECLARE_METATYPE(Models::FeedItem); -#endif // MESSAGEFEED_H +#pragma once diff --git a/ui/widgets/messageline/preview.h b/ui/widgets/messageline/preview.h index 11a5d7d..96a6530 100644 --- a/ui/widgets/messageline/preview.h +++ b/ui/widgets/messageline/preview.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef PREVIEW_H -#define PREVIEW_H +#pragma once #include #include @@ -75,5 +74,3 @@ private: bool fileReachable; bool actualPreview; }; - -#endif // PREVIEW_H From be466fbad12a10de3e14ce6807f3ad9121a3eae5 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 9 Nov 2023 19:36:30 -0300 Subject: [PATCH 113/137] removed Order, resolved a crash on several files being uploaded simultaniuosly --- core/account.cpp | 9 +- core/components/archive.cpp | 2 +- core/handlers/messagehandler.cpp | 6 +- main/CMakeLists.txt | 5 +- shared/CMakeLists.txt | 1 - shared/order.h | 154 ------------------------------ ui/utils/badge.cpp | 38 +++----- ui/utils/badge.h | 22 +---- ui/utils/flowlayout.h | 8 +- ui/widgets/conversation.cpp | 159 ++++++++++++++++--------------- ui/widgets/conversation.h | 20 ++-- ui/widgets/room.cpp | 2 + 12 files changed, 122 insertions(+), 304 deletions(-) delete mode 100644 shared/order.h diff --git a/core/account.cpp b/core/account.cpp index 96ca4e1..a71bf4a 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -141,14 +141,13 @@ Core::Account::Account( loadingOmemo = true; future.then(this, [this] (bool result) { loadingOmemo = false; - if (state == Shared::ConnectionState::scheduled) { + if (state == Shared::ConnectionState::scheduled) client.connectToServer(config, presence); - } - if (result) { + + if (result) qDebug() << "successfully loaded OMEMO data for account" << getName(); - } else { + else qDebug() << "couldn't load OMEMO data for account" << getName(); - } }); } #endif diff --git a/core/components/archive.cpp b/core/components/archive.cpp index 003cce6..67f6693 100644 --- a/core/components/archive.cpp +++ b/core/components/archive.cpp @@ -30,7 +30,7 @@ Core::Archive::Archive(const QString& account, const QString& p_jid, QObject* pa opened(false), db(account + "/" + jid), messages(db.addStorage("messages")), - order(db.addStorage("order")), + order(db.addStorage("order", true)), stats(db.addStorage("stats")), avatars(db.addStorage("avatars")), stanzaIdToId(db.addStorage("stanzaIdToId")), diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index 2a546e9..0d0c446 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -51,8 +51,7 @@ void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) { qDebug() << "Account" << acc->getName() << "received an Omemo1 encrypted message, not supported yet"; break; //let it go the way it is, there is nothing I can do yet case QXmpp::Omemo2: - qDebug() << "Account" << acc->getName() << "received an Omemo2 encrypted message, not supported yet"; - break; //let it go the way it is, there is nothing I can do yet + break; } #endif #endif @@ -359,6 +358,8 @@ std::pair Core::MessageHandler::scheduleSending const QXmppE2eeExtension::MessageEncryptResult& res = task.result(); if (std::holds_alternative>(res)) { const std::unique_ptr& encrypted = std::get>(res); + encrypted->setBody("This message is encrypted with OMEMO 2 but could not be decrypted"); + encrypted->setOutOfBandUrl(""); bool success = acc->client.sendPacket(*encrypted.get()); if (success) return {Shared::Message::State::sent, ""}; @@ -375,6 +376,7 @@ std::pair Core::MessageHandler::scheduleSending if (std::holds_alternative>(result)) { const std::unique_ptr& encrypted = std::get>(result); encrypted->setBody("This message is encrypted with OMEMO 2 but could not be decrypted"); + encrypted->setOutOfBandUrl(""); bool success = acc->client.sendPacket(*encrypted.get()); if (success) { std::tuple ids = getOriginalPendingMessageId(id, false); diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index b5bc725..e92710f 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -11,7 +11,4 @@ set(HEADER_FILES root.h ) -target_sources(squawk PRIVATE - ${SOURCE_FILES} - ${HEADER_FILES} -) +target_sources(squawk PRIVATE ${SOURCE_FILES}) diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index 4ba24ea..2ef3970 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -18,7 +18,6 @@ set(SOURCE_FILES ) set(HEADER_FILES - order.h shared.h enums.h global.h diff --git a/shared/order.h b/shared/order.h deleted file mode 100644 index fa9379b..0000000 --- a/shared/order.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Squawk messenger. - * Copyright (C) 2019 Yury Gubich - * - * 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 ORDER_H -#define ORDER_H - -#include -#include - -#include "exception.h" - -namespace W -{ - template > - class Order - { - public: - class Duplicates: - public Utils::Exception - { - public: - Duplicates():Exception(){} - - std::string getMessage() const{return "Inserting element duplicates existing";} - }; - - class NotFound: - public Utils::Exception - { - public: - NotFound():Exception(){} - - std::string getMessage() const{return "Erasing element haven't been found";} - }; - - protected: - typedef std::list List; - - public: - typedef typename List::size_type size_type; - typedef typename List::const_iterator const_iterator; - typedef typename List::iterator iterator; - - protected: - typedef std::map Map; - typedef typename Map::const_iterator m_const_itr; - typedef typename Map::iterator m_itr; - - public: - Order(): - order(), - r_map() - {} - ~Order() {}; - - size_type size() const { - return order.size(); - } - - void push_back(data_type element) { - m_const_itr m_itr = r_map.find(element); - if (m_itr != r_map.end()) { - throw Duplicates(); - } - - const_iterator itr = order.insert(order.end(), element); - r_map.insert(std::make_pair(element, itr)); - } - - void erase(data_type element) { - m_const_itr itr = r_map.find(element); - if (itr == r_map.end()) { - throw NotFound(); - } - order.erase(itr->second); - r_map.erase(itr); - - } - - void clear() { - order.clear(); - r_map.clear(); - } - - void insert(const_iterator pos, data_type element) { - m_const_itr m_itr = r_map.find(element); - if (m_itr != r_map.end()) { - throw Duplicates(); - } - - const_iterator itr = order.insert(pos, element); - r_map.insert(std::make_pair(element, itr)); - } - - void insert(iterator pos, data_type element) { - m_const_itr m_itr = r_map.find(element); - if (m_itr != r_map.end()) { - throw Duplicates(); - } - - const_iterator itr = order.insert(pos, element); - r_map.insert(std::make_pair(element, itr)); - } - - const_iterator find(data_type element) const { - m_const_itr itr = r_map.find(element); - - if (itr == r_map.end()) { - return end(); - } else { - return itr->second; - } - } - - const_iterator begin() const { - return order.begin(); - } - - const_iterator end() const { - return order.end(); - } - - iterator begin() { - return order.begin(); - } - - iterator end() { - return order.end(); - } - - private: - List order; - Map r_map; - }; -} - - - -#endif // ORDER_H diff --git a/ui/utils/badge.cpp b/ui/utils/badge.cpp index b3b321a..d65b957 100644 --- a/ui/utils/badge.cpp +++ b/ui/utils/badge.cpp @@ -18,6 +18,8 @@ #include "badge.h" +#include "shared/utils.h" + Badge::Badge(const QString& p_id, const QString& p_text, const QIcon& icon, QWidget* parent): QFrame(parent), id(p_id), @@ -48,8 +50,7 @@ Badge::Badge(QWidget* parent): layout->addWidget(closeButton); } -void Badge::setIcon(const QIcon& icon) -{ +void Badge::setIcon(const QIcon& icon) { if (image == nullptr) { image = new QLabel(); image->setPixmap(icon.pixmap(25, 25)); @@ -59,8 +60,7 @@ void Badge::setIcon(const QIcon& icon) } } -void Badge::setText(const QString& p_text) -{ +void Badge::setText(const QString& p_text) { if (text == nullptr) { text = new QLabel(p_text); int index = 0; @@ -73,42 +73,30 @@ void Badge::setText(const QString& p_text) } } -Badge::~Badge() -{ - if (image != nullptr) { +Badge::~Badge() { + if (image != nullptr) delete image; - } - if (text != nullptr) { + + if (text != nullptr) delete text; - } + delete closeButton; } -bool Badge::Comparator::operator()(const Badge* a, const Badge* b) const -{ - return a->id < b->id; -} - -bool Badge::Comparator::operator()(const Badge& a, const Badge& b) const -{ - return a.id < b.id; -} - -void Badge::createMandatoryComponents() -{ +void Badge::createMandatoryComponents() { setBackgroundRole(QPalette::Base); //setAutoFillBackground(true); setFrameStyle(QFrame::StyledPanel); setFrameShadow(QFrame::Raised); QIcon tabCloseIcon = QIcon::fromTheme("tab-close"); - if (tabCloseIcon.isNull()) { + if (tabCloseIcon.isNull()) tabCloseIcon.addFile(QString::fromUtf8(":/images/fallback/dark/big/edit-none.svg"), QSize(), QIcon::Normal, QIcon::Off); - } + closeButton->setIcon(tabCloseIcon); closeButton->setMaximumHeight(25); closeButton->setMaximumWidth(25); layout->setContentsMargins(2, 2, 2, 2); - connect(closeButton, &QPushButton::clicked, this, &Badge::close); + connect(closeButton, &QPushButton::clicked, this, &Badge::closeClicked); } diff --git a/ui/utils/badge.h b/ui/utils/badge.h index 52f4747..074eda0 100644 --- a/ui/utils/badge.h +++ b/ui/utils/badge.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef BADGE_H -#define BADGE_H +#pragma once #include #include @@ -25,13 +24,7 @@ #include #include -#include "shared/utils.h" - -/** - * @todo write docs - */ -class Badge : public QFrame -{ +class Badge : public QFrame { Q_OBJECT public: Badge(const QString& id, const QString& text, const QIcon& icon, QWidget* parent = nullptr); @@ -45,7 +38,7 @@ public: void setIcon(const QIcon& icon); signals: - void close(); + void closeClicked(); private: QLabel* image; @@ -55,12 +48,5 @@ private: private: void createMandatoryComponents(); - -public: - struct Comparator { - bool operator()(const Badge& a, const Badge& b) const; - bool operator()(const Badge* a, const Badge* b) const; - }; -}; -#endif // BADGE_H +}; diff --git a/ui/utils/flowlayout.h b/ui/utils/flowlayout.h index 0e52c87..24830e7 100644 --- a/ui/utils/flowlayout.h +++ b/ui/utils/flowlayout.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef FLOWLAYOUT_H -#define FLOWLAYOUT_H +#pragma once #include #include @@ -26,8 +25,7 @@ /** * @todo write docs */ -class FlowLayout : public QLayout -{ +class FlowLayout : public QLayout { Q_OBJECT public: explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1); @@ -55,5 +53,3 @@ private: int m_hSpace; int m_vSpace; }; - -#endif // FLOWLAYOUT_H diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 32c38a8..7214a40 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -33,6 +33,12 @@ #include #include +#include + +#include "shared/icons.h" +#include "shared/utils.h" +#include "shared/pathcheck.h" +#include "shared/defines.h" constexpr QSize avatarSize(50, 50); @@ -61,58 +67,9 @@ Conversation::Conversation(bool muc, Models::Account* acc, Models::Element* el, currentAction(CurrentAction::none), currentMessageId() { - m_ui->setupUi(this); - - shadow.setFrames(true, false, true, false); - - feed->setItemDelegate(delegate); - feed->setFrameShape(QFrame::NoFrame); - feed->setContextMenuPolicy(Qt::CustomContextMenu); - feed->setModel(el->feed); - el->feed->incrementObservers(); - m_ui->widget->layout()->addWidget(feed); - - connect(el->feed, &Models::MessageFeed::newMessage, this, &Conversation::onFeedMessage); - connect(feed, &FeedView::resized, this, &Conversation::positionShadow); - connect(feed, &FeedView::customContextMenuRequested, this, &Conversation::onFeedContext); - - connect(acc, &Models::Account::childChanged, this, &Conversation::onAccountChanged); - - filesLayout = new FlowLayout(m_ui->filesPanel, 0); - m_ui->filesPanel->setLayout(filesLayout); - - statusIcon = m_ui->statusIcon; - statusLabel = m_ui->statusLabel; - - connect(&ker, &KeyEnterReceiver::enterPressed, this, qOverload<>(&Conversation::initiateMessageSending)); - connect(&ker, &KeyEnterReceiver::imagePasted, this, &Conversation::onImagePasted); - connect(m_ui->sendButton, &QPushButton::clicked, this, qOverload<>(&Conversation::initiateMessageSending)); - connect(m_ui->attachButton, &QPushButton::clicked, this, &Conversation::onAttach); - connect(m_ui->clearButton, &QPushButton::clicked, this, &Conversation::clear); - connect(m_ui->messageEditor->document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged, - this, &Conversation::onTextEditDocSizeChanged); - connect(m_ui->encryptionButton, &QPushButton::clicked, this, &Conversation::onEncryptionButtonClicked); - - m_ui->messageEditor->installEventFilter(&ker); - - connect(m_ui->messageEditor, &QTextEdit::customContextMenuRequested, this, &Conversation::onMessageEditorContext); - connect(pasteImageAction, &QAction::triggered, this, &Conversation::onImagePasted); - - connect(m_ui->currentActionBadge, &Badge::close, this, &Conversation::clear); - m_ui->currentActionBadge->setVisible(false); - - m_ui->encryptionButton->setVisible(false); - - //line->setAutoFillBackground(false); - //if (testAttribute(Qt::WA_TranslucentBackground)) { - //m_ui->scrollArea->setAutoFillBackground(false); - //} else { - //m_ui->scrollArea->setBackgroundRole(QPalette::Base); - //} - - //line->setMyAvatarPath(acc->getAvatarPath()); - //line->setMyName(acc->getName()); - + createUI(); + createFeed(); + subscribeEvents(); initializeOverlay(); } @@ -123,6 +80,52 @@ Conversation::~Conversation() { delete m_ui; } +void Conversation::createFeed() { + feed->setItemDelegate(delegate); + feed->setFrameShape(QFrame::NoFrame); + feed->setContextMenuPolicy(Qt::CustomContextMenu); + feed->setModel(element->feed); + element->feed->incrementObservers(); + m_ui->widget->layout()->addWidget(feed); + + connect(element->feed, &Models::MessageFeed::newMessage, this, &Conversation::onFeedMessage); + connect(feed, &FeedView::resized, this, &Conversation::positionShadow); + connect(feed, &FeedView::customContextMenuRequested, this, &Conversation::onFeedContext); +} + +void Conversation::createUI() { + m_ui->setupUi(this); + statusIcon = m_ui->statusIcon; + statusLabel = m_ui->statusLabel; + + filesLayout = new FlowLayout(m_ui->filesPanel, 0); + m_ui->filesPanel->setLayout(filesLayout); + + m_ui->currentActionBadge->setVisible(false); + m_ui->encryptionButton->setVisible(false); + + shadow.setFrames(true, false, true, false); +} + +void Conversation::subscribeEvents() { + connect(account, &Models::Account::childChanged, this, &Conversation::onAccountChanged); + connect(&ker, &KeyEnterReceiver::enterPressed, this, qOverload<>(&Conversation::initiateMessageSending)); + connect(&ker, &KeyEnterReceiver::imagePasted, this, &Conversation::onImagePasted); + connect(m_ui->sendButton, &QPushButton::clicked, this, qOverload<>(&Conversation::initiateMessageSending)); + connect(m_ui->attachButton, &QPushButton::clicked, this, &Conversation::onAttach); + connect(m_ui->clearButton, &QPushButton::clicked, this, &Conversation::clear); + connect(m_ui->messageEditor->document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged, + this, &Conversation::onTextEditDocSizeChanged); + connect(m_ui->encryptionButton, &QPushButton::clicked, this, &Conversation::onEncryptionButtonClicked); + + m_ui->messageEditor->installEventFilter(&ker); + + connect(m_ui->messageEditor, &QTextEdit::customContextMenuRequested, this, &Conversation::onMessageEditorContext); + connect(pasteImageAction, &QAction::triggered, this, &Conversation::onImagePasted); + + connect(m_ui->currentActionBadge, &Badge::closeClicked, this, &Conversation::clear); +} + void Conversation::onAccountChanged(Models::Item* item, int row, int col) { SHARED_UNUSED(row); if (item == account) { @@ -228,7 +231,7 @@ void Conversation::initiateMessageSending() { initiateMessageSending(msg); } if (filesToAttach.size() > 0) { - for (Badge* badge : filesToAttach) { + for (const Badge* badge : filesToAttach) { Shared::Message msg = createMessage(); msg.setAttachPath(badge->id); element->feed->registerUpload(msg.getId()); @@ -291,6 +294,14 @@ Models::Roster::ElId Conversation::getId() const { } void Conversation::addAttachedFile(const QString& path) { + std::vector::const_iterator itr = std::find_if( + filesToAttach.begin(), + filesToAttach.end(), + [&path] (const Badge* badge) {return badge->id == path;} + ); + if (itr != filesToAttach.end()) + return; + QMimeDatabase db; QMimeType type = db.mimeTypeForFile(path); QFileInfo info(path); @@ -300,25 +311,23 @@ void Conversation::addAttachedFile(const QString& path) { fileIcon.addFile(QString::fromUtf8(":/images/fallback/dark/big/mail-attachment.svg"), QSize(), QIcon::Normal, QIcon::Off); Badge* badge = new Badge(path, info.fileName(), fileIcon); + filesToAttach.push_back(badge); - connect(badge, &Badge::close, this, &Conversation::onBadgeClose); - try { - filesToAttach.push_back(badge); - filesLayout->addWidget(badge); - if (filesLayout->count() == 1) { - filesLayout->setContentsMargins(3, 3, 3, 3); - } - } catch (const W::Order::Duplicates& e) { - delete badge; - } catch (...) { - throw; - } + connect(badge, &Badge::closeClicked, this, &Conversation::onBadgeClose); + filesLayout->addWidget(badge); + if (filesLayout->count() == 1) + filesLayout->setContentsMargins(3, 3, 3, 3); } -void Conversation::removeAttachedFile(Badge* badge) { - W::Order::const_iterator itr = filesToAttach.find(badge); +void Conversation::removeAttachedFile(const QString& id) { + std::vector::const_iterator itr = std::find_if( + filesToAttach.begin(), + filesToAttach.end(), + [&id] (const Badge* badge) {return badge->id == id;} + ); if (itr != filesToAttach.end()) { - filesToAttach.erase(badge); + Badge* badge = *itr; + filesToAttach.erase(itr); if (filesLayout->count() == 1) filesLayout->setContentsMargins(0, 0, 0, 0); @@ -328,7 +337,7 @@ void Conversation::removeAttachedFile(Badge* badge) { void Conversation::onBadgeClose() { Badge* badge = static_cast(sender()); - removeAttachedFile(badge); + removeAttachedFile(badge->id); } void Conversation::clearAttachedFiles() { @@ -351,12 +360,10 @@ void Conversation::onEncryptionButtonClicked() {} void Conversation::setAvatar(const QString& path) { QPixmap pixmap; - if (path.size() == 0) { + if (path.size() == 0) pixmap = Shared::icon("user", true).pixmap(avatarSize); - } else { + else pixmap = QPixmap(path).scaled(avatarSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); - } - QPixmap result(avatarSize); result.fill(Qt::transparent); @@ -379,8 +386,7 @@ void Conversation::setFeedFrames(bool top, bool right, bool bottom, bool left) { shadow.setFrames(top, right, bottom, left); } -void Conversation::dragEnterEvent(QDragEnterEvent* event) -{ +void Conversation::dragEnterEvent(QDragEnterEvent* event) { bool accept = false; if (event->mimeData()->hasUrls()) { QList list = event->mimeData()->urls(); @@ -542,9 +548,8 @@ void Conversation::onMessageEditRequested(const QString& id) { currentAction = CurrentAction::edit; m_ui->messageEditor->setText(msg.getBody()); QString path = msg.getAttachPath(); - if (path.size() > 0) { + if (path.size() > 0) addAttachedFile(path); - } } catch (const Models::MessageFeed::NotFound& e) { qDebug() << "The message requested to be edited was not found" << e.getMessage().c_str(); diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index 73e6536..bece7a8 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef CONVERSATION_H -#define CONVERSATION_H +#pragma once #include #include @@ -30,12 +29,9 @@ #include #include +#include + #include "shared/message.h" -#include "shared/order.h" -#include "shared/icons.h" -#include "shared/utils.h" -#include "shared/pathcheck.h" -#include "shared/defines.h" #include "ui/models/account.h" #include "ui/models/roster.h" @@ -96,7 +92,7 @@ protected: virtual Shared::Message createMessage() const; void setStatus(const QString& status); void addAttachedFile(const QString& path); - void removeAttachedFile(Badge* badge); + void removeAttachedFile(const QString& id); void clearAttachedFiles(); void dragEnterEvent(QDragEnterEvent* event) override; void dragLeaveEvent(QDragLeaveEvent* event) override; @@ -104,6 +100,10 @@ protected: void initializeOverlay(); virtual void onMessage(const Shared::Message& msg); virtual void showEvent(QShowEvent * event) override; + + void createFeed(); + void createUI(); + void subscribeEvents(); protected slots: void initiateMessageSending(); @@ -142,7 +142,7 @@ protected: QLabel* statusLabel; FlowLayout* filesLayout; QWidget* overlay; - W::Order filesToAttach; + std::vector filesToAttach; FeedView* feed; MessageDelegate* delegate; bool manualSliderChange; @@ -161,5 +161,3 @@ private: static QPixmap* avatarPixmap; static QPainter* avatarPainter; }; - -#endif // CONVERSATION_H diff --git a/ui/widgets/room.cpp b/ui/widgets/room.cpp index 3409a86..56ce5a0 100644 --- a/ui/widgets/room.cpp +++ b/ui/widgets/room.cpp @@ -18,6 +18,8 @@ #include "room.h" +#include "shared/defines.h" + Room::Room(Models::Account* acc, Models::Room* p_room, QWidget* parent): Conversation(true, acc, p_room, p_room->getJid(), "", parent), room(p_room) From e31ef78e7187d216365d27ad0c52ea2b1c3978f8 Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 10 Nov 2023 19:26:16 -0300 Subject: [PATCH 114/137] some refactoring, some improvements --- core/account.cpp | 20 ++++--- core/account.h | 7 +-- core/contact.cpp | 4 ++ core/contact.h | 13 ++--- core/delayManager/manager.h | 41 +++++++------- core/handlers/messagehandler.cpp | 10 ++-- core/handlers/omemohandler.cpp | 65 +++++++++++++--------- core/handlers/omemohandler.h | 38 +++++++------ core/handlers/trusthandler.cpp | 93 ++++++++++++++++++-------------- external/qxmpp | 2 +- shared/enums.h | 8 +++ shared/message.cpp | 10 +++- ui/widgets/chat.cpp | 24 ++++----- 13 files changed, 188 insertions(+), 147 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index a71bf4a..efb4a1a 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -266,6 +266,7 @@ void Core::Account::onClientStateChange(QXmppClient::State st) { #ifdef WITH_OMEMO if (!oh->hasOwnDevice()) { qDebug() << "setting up OMEMO data for account" << getName(); + om->changeDeviceLabel(QGuiApplication::applicationDisplayName() + " - " + QSysInfo::productType()); QXmppTask future = om->setUp(); future.then(this, [this] (bool result) { if (result) @@ -342,9 +343,8 @@ void Core::Account::setAvailability(Shared::Availability avail) { QXmppPresence::AvailableStatusType pres = static_cast(avail); presence.setAvailableStatusType(pres); - if (state != Shared::ConnectionState::disconnected) { + if (state != Shared::ConnectionState::disconnected) client.setClientPresence(presence); - } } } @@ -437,9 +437,9 @@ void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMess void Core::Account::requestArchive(const QString& jid, int count, const QString& before) { qDebug() << "An archive request for " << jid << ", before " << before; - RosterItem* contact = rh->getRosterItem(jid); + RosterItem* item = rh->getRosterItem(jid); - if (contact == nullptr) { + if (item == nullptr) { qDebug() << "An attempt to request archive for" << jid << "in account" << name << ", but the contact with such id wasn't found, skipping"; emit responseArchive(jid, std::list(), true); return; @@ -447,10 +447,18 @@ void Core::Account::requestArchive(const QString& jid, int count, const QString& if (state != Shared::ConnectionState::connected) { qDebug() << "An attempt to request archive for" << jid << "in account" << name << ", but the account is not online, skipping"; - emit responseArchive(contact->jid, std::list(), false); + emit responseArchive(jid, std::list(), false); + return; } - contact->requestHistory(count, before); +#ifdef WITH_OMEMO + if (!item->isMuc()) { + Contact* contact = static_cast(item); + if (contact->omemoBundles == Shared::Possible::unknown) + oh->requestBundles(jid); + } +#endif + item->requestHistory(count, before); } void Core::Account::onContactNeedHistory(const QString& before, const QString& after, const QDateTime& at) { diff --git a/core/account.h b/core/account.h index 6442abf..b156059 100644 --- a/core/account.h +++ b/core/account.h @@ -15,9 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - -#ifndef CORE_ACCOUNT_H -#define CORE_ACCOUNT_H +#pragma once #include #include @@ -248,6 +246,3 @@ private: void runDiscoveryService(); }; } - - -#endif // CORE_ACCOUNT_H diff --git a/core/contact.cpp b/core/contact.cpp index 93139ef..f18dae3 100644 --- a/core/contact.cpp +++ b/core/contact.cpp @@ -24,6 +24,10 @@ Core::Contact::Contact(const QString& pJid, const QString& account, QObject* par groups(), subscriptionState(Shared::SubscriptionState::unknown), pep(Shared::Support::unknown) + +#ifdef WITH_OMEMO + ,omemoBundles(Shared::Possible::unknown) +#endif {} Core::Contact::~Contact() {} diff --git a/core/contact.h b/core/contact.h index bde95f2..c23d0dc 100644 --- a/core/contact.h +++ b/core/contact.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef CORE_CONTACT_H -#define CORE_CONTACT_H +#pragma once #include #include @@ -28,8 +27,7 @@ namespace Core { -class Contact : public RosterItem -{ +class Contact : public RosterItem { Q_OBJECT public: Contact(const QString& pJid, const QString& account, QObject* parent = 0); @@ -56,7 +54,10 @@ private: QSet groups; Shared::SubscriptionState subscriptionState; Shared::Support pep; + +#ifdef WITH_OMEMO +public: + Shared::Possible omemoBundles; +#endif }; } - -#endif // CORE_CONTACT_H diff --git a/core/delayManager/manager.h b/core/delayManager/manager.h index 127d25a..5d04a8e 100644 --- a/core/delayManager/manager.h +++ b/core/delayManager/manager.h @@ -1,21 +1,21 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 CORE_DELAYMANAGER_MANAGER_H -#define CORE_DELAYMANAGER_MANAGER_H +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ +#pragma once #include #include @@ -38,8 +38,7 @@ namespace Core { namespace DelayManager { -class Manager : public QObject -{ +class Manager : public QObject { Q_OBJECT public: Manager(const QString& ownJid, Job::Id maxParallelJobs = 5, QObject* parent = nullptr); @@ -146,5 +145,3 @@ public: } } - -#endif // CORE_DELAYMANAGER_MANAGER_H diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index 0d0c446..2c8526a 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -358,8 +358,8 @@ std::pair Core::MessageHandler::scheduleSending const QXmppE2eeExtension::MessageEncryptResult& res = task.result(); if (std::holds_alternative>(res)) { const std::unique_ptr& encrypted = std::get>(res); - encrypted->setBody("This message is encrypted with OMEMO 2 but could not be decrypted"); - encrypted->setOutOfBandUrl(""); + encrypted->setBody(QString()); + encrypted->setOutOfBandUrl(QString()); bool success = acc->client.sendPacket(*encrypted.get()); if (success) return {Shared::Message::State::sent, ""}; @@ -375,8 +375,8 @@ std::pair Core::MessageHandler::scheduleSending task.then(this, [this, id] (QXmppE2eeExtension::MessageEncryptResult&& result) { if (std::holds_alternative>(result)) { const std::unique_ptr& encrypted = std::get>(result); - encrypted->setBody("This message is encrypted with OMEMO 2 but could not be decrypted"); - encrypted->setOutOfBandUrl(""); + encrypted->setBody(QString()); + encrypted->setOutOfBandUrl(QString()); bool success = acc->client.sendPacket(*encrypted.get()); if (success) { std::tuple ids = getOriginalPendingMessageId(id, false); @@ -602,7 +602,7 @@ void Core::MessageHandler::onUploadFileComplete(const std::list Core::OmemoHandler::allData() { OmemoData data; data.ownDevice = ownDevice; + // LMDBAL::Transaction txn = db.beginReadOnlyTransaction(); TODO need to enable transaction after fixing LMDBAL std::map pkeys = preKeyPairs->readAll(); for (const std::pair& pair : pkeys) { data.preKeyPairs.insert(pair.first, pair.second); @@ -73,28 +74,30 @@ QXmppTask Core::OmemoHandler::allData() { QXmppTask Core::OmemoHandler::addDevice(const QString& jid, uint32_t deviceId, const QXmppOmemoStorage::Device& device) { QHash devs; + LMDBAL::WriteTransaction txn = db.beginTransaction(); bool had = true; try { - devs = devices->getRecord(jid); + devices->getRecord(jid, devs, txn); } catch (const LMDBAL::NotFound& error) { had = false; } - devs.insert(deviceId, device); - if (had) { - devices->changeRecord(jid, devs); - } else { - devices->addRecord(jid, devs); - } + devs.insert(deviceId, device); //overwrites + if (had) + devices->changeRecord(jid, devs, txn); + else + devices->addRecord(jid, devs, txn); + txn.commit(); return Core::makeReadyTask(); } QXmppTask Core::OmemoHandler::addPreKeyPairs(const QHash& keyPairs) { - for (QHash::const_iterator itr = keyPairs.begin(), end = keyPairs.end(); itr != end; ++itr) { - preKeyPairs->forceRecord(itr.key(), itr.value()); - } + LMDBAL::WriteTransaction txn = db.beginTransaction(); + for (QHash::const_iterator itr = keyPairs.begin(), end = keyPairs.end(); itr != end; ++itr) + preKeyPairs->forceRecord(itr.key(), itr.value(), txn); + txn.commit(); return Core::makeReadyTask(); } @@ -104,13 +107,15 @@ QXmppTask Core::OmemoHandler::addSignedPreKeyPair(uint32_t keyId, const QX } QXmppTask Core::OmemoHandler::removeDevice(const QString& jid, uint32_t deviceId) { - QHash devs = devices->getRecord(jid); + LMDBAL::WriteTransaction txn = db.beginTransaction(); + QHash devs = devices->getRecord(jid, txn); devs.remove(deviceId); - if (devs.isEmpty()) { - devices->removeRecord(jid); - } else { - devices->changeRecord(jid, devs); - } + if (devs.isEmpty()) + devices->removeRecord(jid, txn); + else + devices->changeRecord(jid, devs, txn); + + txn.commit(); return Core::makeReadyTask(); } @@ -120,7 +125,11 @@ QXmppTask Core::OmemoHandler::removeDevices(const QString& jid) { } QXmppTask Core::OmemoHandler::removePreKeyPair(uint32_t keyId) { - preKeyPairs->removeRecord(keyId); + try { + preKeyPairs->removeRecord(keyId); + } catch (const LMDBAL::NotFound& e) { + qDebug() << "Couldn't remove preKeyPair " << e.what(); + } return Core::makeReadyTask(); } @@ -135,15 +144,12 @@ QXmppTask Core::OmemoHandler::setOwnDevice(const std::optional& bool had = ownDevice.has_value(); ownDevice = device; if (ownDevice.has_value()) { - if (had) { + if (had) meta->changeRecord("ownDevice", QVariant::fromValue(ownDevice.value())); - } else { + else meta->addRecord("ownDevice", QVariant::fromValue(ownDevice.value())); - } - } else { - if (had) { - meta->removeRecord("ownDevice"); - } + } else if (had) { + meta->removeRecord("ownDevice"); } return Core::makeReadyTask(); } @@ -158,7 +164,7 @@ QXmppTask Core::OmemoHandler::resetAll() { void Core::OmemoHandler::getDevices(const QString& jid, std::list& out) const { QHash devs; try { - devs = devices->getRecord(jid); + devices->getRecord(jid, devs); } catch (const LMDBAL::NotFound& error) {} for (QHash::const_iterator itr = devs.begin(), end = devs.end(); itr != end; ++itr) { @@ -169,6 +175,10 @@ void Core::OmemoHandler::getDevices(const QString& jid, std::list task = acc->om->buildMissingSessions({jid}); + Contact* cnt = acc->rh->getContact(jid); + if (cnt) + cnt->omemoBundles = Shared::Possible::discovering; + task.then(this, std::bind(&OmemoHandler::onBundlesReceived, this, jid)); } @@ -191,6 +201,10 @@ void Core::OmemoHandler::onBundlesReceived(const QString& jid) { } } + Contact* cnt = acc->rh->getContact(jid); + if (cnt) + cnt->omemoBundles = Shared::Possible::present; + acc->delay->receivedBundles(jid, keys); } @@ -217,7 +231,6 @@ void Core::OmemoHandler::onOmemoDeviceAdded(const QString& jid, uint32_t id) { qDebug() << "OMEMO device added for" << jid; } - QDataStream & operator >> (QDataStream& in, QXmppOmemoStorage::Device& device) { in >> device.label; in >> device.keyId; diff --git a/core/handlers/omemohandler.h b/core/handlers/omemohandler.h index e626b4c..60fd8d1 100644 --- a/core/handlers/omemohandler.h +++ b/core/handlers/omemohandler.h @@ -1,21 +1,21 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 CORE_OMEMOHANDLER_H -#define CORE_OMEMOHANDLER_H +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ +#pragma once #include #include @@ -87,5 +87,3 @@ QDataStream& operator >> (QDataStream &in, QXmppOmemoStorage::Device& device); QDataStream& operator << (QDataStream &out, const QXmppOmemoStorage::OwnDevice& device); QDataStream& operator >> (QDataStream &in, QXmppOmemoStorage::OwnDevice& device); - -#endif // CORE_OMEMOHANDLER_H diff --git a/core/handlers/trusthandler.cpp b/core/handlers/trusthandler.cpp index 21b3491..566265f 100644 --- a/core/handlers/trusthandler.cpp +++ b/core/handlers/trusthandler.cpp @@ -28,9 +28,8 @@ Core::TrustHandler::TrustHandler(Account* account): ownKeys(db.addCache("ownKeys")), keysByProtocol() { - if (!protocols.open(QIODevice::ReadWrite | QIODevice::Text)) { //never supposed to happen since I have just created a directory; + if (!protocols.open(QIODevice::ReadWrite | QIODevice::Text)) //never supposed to happen since I have just created a directory; throw LMDBAL::Directory(protocols.fileName().toStdString()); - } QTextStream in(&protocols); while(!in.atEnd()) { @@ -53,11 +52,10 @@ Core::TrustHandler::~TrustHandler() { Core::TrustHandler::KeyCache * Core::TrustHandler::getCache(const QString& encryption) { std::map::iterator itr = keysByProtocol.find(encryption); - if (itr == keysByProtocol.end()) { + if (itr == keysByProtocol.end()) return createNewCache(encryption); - } else { + else return itr->second; - } } Core::TrustHandler::KeyCache * Core::TrustHandler::createNewCache(const QString& encryption) { @@ -65,9 +63,9 @@ Core::TrustHandler::KeyCache * Core::TrustHandler::createNewCache(const QString& KeyCache* cache = db.addCache(encryption.toStdString()); keysByProtocol.insert(std::make_pair(encryption, cache)); - if(!protocols.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) { + if (!protocols.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) throw LMDBAL::Directory(protocols.fileName().toStdString()); - } + QTextStream out(&protocols); out << encryption + "\n"; protocols.close(); @@ -83,10 +81,12 @@ QXmppTask Core::TrustHandler::resetAll(const QString& encryption) { if (itr == keysByProtocol.end()) return Core::makeReadyTask(); + LMDBAL::WriteTransaction txn = db.beginTransaction(); KeyCache* cache = itr->second; - std::map keys = cache->readAll(); - cache->drop(); + std::map keys = cache->readAll(txn); + cache->drop(txn); + txn.commit(); for (const std::pair& pair : keys) { bool empty = true; for (const std::pair& trust : pair.second) { @@ -105,8 +105,8 @@ QXmppTask Core::TrustHandler::resetAll(const QString& encryption) { QXmppTask Core::TrustHandler::trustLevel( const QString& encryption, const QString& keyOwnerJid, - const QByteArray& keyId) -{ + const QByteArray& keyId +) { KeyCache* cache = getCache(encryption); Shared::TrustLevel level = Shared::TrustLevel::undecided; try { @@ -122,15 +122,17 @@ QXmppTask>> Core::TrustHandler::s const QString& encryption, const QList& keyOwnerJids, QXmpp::TrustLevel oldTrustLevel, - QXmpp::TrustLevel newTrustLevel) -{ + QXmpp::TrustLevel newTrustLevel +) { QHash> modifiedKeys; Shared::TrustLevel oldLevel = convert(oldTrustLevel); Shared::TrustLevel newLevel = convert(newTrustLevel); std::set modifiedJids; KeyCache* cache = getCache(encryption); + + LMDBAL::WriteTransaction txn = db.beginTransaction(); for (const QString& keyOwnerJid : keyOwnerJids) { - Keys map = cache->getRecord(keyOwnerJid); + Keys map = cache->getRecord(keyOwnerJid, txn); uint count = 0; for (std::pair& pair : map) { Shared::TrustLevel& current = pair.second; @@ -142,8 +144,9 @@ QXmppTask>> Core::TrustHandler::s } } if (count > 0) - cache->changeRecord(keyOwnerJid, map); + cache->changeRecord(keyOwnerJid, map, txn); } + txn.commit(); for (const QString& jid : modifiedJids) emit trustLevelsChanged(jid, getSummary(jid)); @@ -153,18 +156,19 @@ QXmppTask>> Core::TrustHandler::s QXmppTask>> Core::TrustHandler::setTrustLevel( const QString& encryption, const QMultiHash& keyIds, - QXmpp::TrustLevel trustLevel) -{ + QXmpp::TrustLevel trustLevel +) { QHash> modifiedKeys; Shared::TrustLevel level = convert(trustLevel); std::set modifiedJids; KeyCache* cache = getCache(encryption); + LMDBAL::WriteTransaction txn = db.beginTransaction(); for (MultySB::const_iterator itr = keyIds.begin(), end = keyIds.end(); itr != end; ++itr) { const QString& keyOwnerJid = itr.key(); const QByteArray& keyId = itr.value(); try { - Keys map = cache->getRecord(keyOwnerJid); + Keys map = cache->getRecord(keyOwnerJid, txn); std::pair result = map.insert(std::make_pair(keyId, level)); bool changed = result.second; if (!changed && result.first->second != level) { @@ -174,15 +178,16 @@ QXmppTask>> Core::TrustHandler::s if (changed) { modifiedKeys[encryption].insert(keyOwnerJid, keyId); modifiedJids.insert(keyOwnerJid); - cache->changeRecord(keyOwnerJid, map); + cache->changeRecord(keyOwnerJid, map, txn); } } catch (const LMDBAL::NotFound& e) { Keys map({{keyId, level}}); modifiedKeys[encryption].insert(keyOwnerJid, keyId); modifiedJids.insert(keyOwnerJid); - cache->addRecord(keyOwnerJid, map); + cache->addRecord(keyOwnerJid, map, txn); } } + txn.commit(); for (const QString& jid : modifiedJids) emit trustLevelsChanged(jid, getSummary(jid)); @@ -190,10 +195,11 @@ QXmppTask>> Core::TrustHandler::s return Core::makeReadyTask(std::move(modifiedKeys)); } -QXmppTask Core::TrustHandler::hasKey(const QString& encryption, - const QString& keyOwnerJid, - QXmpp::TrustLevels trustLevels) -{ +QXmppTask Core::TrustHandler::hasKey( + const QString& encryption, + const QString& keyOwnerJid, + QXmpp::TrustLevels trustLevels +) { KeyCache* cache = getCache(encryption); bool found = false; try { @@ -211,8 +217,8 @@ QXmppTask Core::TrustHandler::hasKey(const QString& encryption, QXmppTask>> Core::TrustHandler::keys( const QString& encryption, const QList& keyOwnerJids, - QXmpp::TrustLevels trustLevels) -{ + QXmpp::TrustLevels trustLevels +) { HSHBTL res; KeyCache* cache = getCache(encryption); @@ -233,8 +239,8 @@ QXmppTask>> Core::TrustHandl QXmppTask>> Core::TrustHandler::keys( const QString& encryption, - QXmpp::TrustLevels trustLevels) -{ + QXmpp::TrustLevels trustLevels +) { QHash res; KeyCache* cache = getCache(encryption); std::map storage = cache->readAll(); @@ -253,9 +259,11 @@ QXmppTask Core::TrustHandler::removeKeys(const QString& encryption) { if (itr == keysByProtocol.end()) return Core::makeReadyTask(); + LMDBAL::WriteTransaction txn = db.beginTransaction(); KeyCache* cache = itr->second; - std::map keys = cache->readAll(); - cache->drop(); + std::map keys = cache->readAll(txn); + cache->drop(txn); + txn.commit(); for (const std::pair& pair : keys) { bool empty = true; @@ -292,8 +300,9 @@ QXmppTask Core::TrustHandler::removeKeys(const QString& encryption, const for (const QByteArray& keyId : keyIds) set.insert(keyId); + LMDBAL::WriteTransaction txn = db.beginTransaction(); KeyCache* cache = getCache(encryption); - std::map data = cache->readAll(); + std::map data = cache->readAll(txn); std::set modifiedJids; for (std::map::iterator cItr = data.begin(), cEnd = data.end(); cItr != cEnd; /*no increment*/) { @@ -311,10 +320,10 @@ QXmppTask Core::TrustHandler::removeKeys(const QString& encryption, const else ++cItr; } - if (modifiedJids.size() > 0) { - cache->replaceAll(data); - } + if (modifiedJids.size() > 0) + cache->replaceAll(data, txn); + txn.commit(); for (const QString& jid : modifiedJids) emit trustLevelsChanged(jid, getSummary(jid)); @@ -325,14 +334,15 @@ QXmppTask Core::TrustHandler::addKeys( const QString& encryption, const QString& keyOwnerJid, const QList& keyIds, - QXmpp::TrustLevel trustLevel) -{ + QXmpp::TrustLevel trustLevel +) { KeyCache* cache = getCache(encryption); Shared::TrustLevel level = convert(trustLevel); Keys data; bool had = false; + LMDBAL::WriteTransaction txn = db.beginTransaction(); try { - data = cache->getRecord(keyOwnerJid); + data = cache->getRecord(keyOwnerJid, txn); had = true; } catch (const LMDBAL::NotFound& e) {} for (const QByteArray& keyId : keyIds) { @@ -342,10 +352,11 @@ QXmppTask Core::TrustHandler::addKeys( } if (had) - cache->changeRecord(keyOwnerJid, data); + cache->changeRecord(keyOwnerJid, data, txn); else - cache->addRecord(keyOwnerJid, data); + cache->addRecord(keyOwnerJid, data, txn); + txn.commit(); emit trustLevelsChanged(keyOwnerJid, getSummary(keyOwnerJid)); return Core::makeReadyTask(); @@ -418,9 +429,9 @@ Shared::TrustSummary Core::TrustHandler::getSummary(const QString& jid) const { try { Keys keys = pair.second->getRecord(jid); Shared::EncryptionProtocol protocol = Shared::TrustSummary::protocolValues.at(pair.first); - for (const std::pair& trust : keys) { + for (const std::pair& trust : keys) result.increment(protocol, trust.second); - } + } catch (const LMDBAL::NotFound& e) {} } diff --git a/external/qxmpp b/external/qxmpp index 9e9c22b..8dda3c9 160000 --- a/external/qxmpp +++ b/external/qxmpp @@ -1 +1 @@ -Subproject commit 9e9c22b16a39c7370fed31c6deea56d8abf72440 +Subproject commit 8dda3c9921c34b9e33883b0d140e8de12edc0736 diff --git a/shared/enums.h b/shared/enums.h index 6b668d6..43d1583 100644 --- a/shared/enums.h +++ b/shared/enums.h @@ -136,6 +136,14 @@ enum class Support { }; Q_ENUM_NS(Support) +enum class Possible { + unknown, + discovering, + present, + abscent +}; +Q_ENUM_NS(Possible) + enum class TrustLevel { /// The key's trust is not decided. undecided, diff --git a/shared/message.cpp b/shared/message.cpp index caf8a97..70fae63 100644 --- a/shared/message.cpp +++ b/shared/message.cpp @@ -268,7 +268,9 @@ QDataStream& operator<<(QDataStream& out, const Shared::Message& info) { out << info.rTo; out << info.id; out << info.body; - out << info.time; + quint64 msecs = info.time.toMSecsSinceEpoch(); + out << msecs; + out << info.thread; out << (quint8)info.type; out << info.outgoing; @@ -297,7 +299,11 @@ QDataStream & operator>>(QDataStream& in, Shared::Message& info) { in >> info.rTo; in >> info.id; in >> info.body; - in >> info.time; + + quint64 msecs; + in >> msecs; + info.time = QDateTime::fromMSecsSinceEpoch(msecs); + in >> info.thread; quint8 t; in >> t; diff --git a/ui/widgets/chat.cpp b/ui/widgets/chat.cpp index 628f30f..b624a5d 100644 --- a/ui/widgets/chat.cpp +++ b/ui/widgets/chat.cpp @@ -32,10 +32,7 @@ Chat::Chat(Models::Account* acc, Models::Contact* p_contact, QWidget* parent): connect(contact, &Models::Contact::childChanged, this, &Chat::onContactChanged); #ifdef WITH_OMEMO - if (p_contact->hasKeys(Shared::EncryptionProtocol::omemo2)) { - m_ui->encryptionButton->setVisible(true); - updateEncryptionState(); - } + updateEncryptionState(); #endif } @@ -59,9 +56,7 @@ void Chat::onContactChanged(Models::Item* item, int row, int col) { setAvatar(contact->getAvatarPath()); break; #ifdef WITH_OMEMO - case 8: - m_ui->encryptionButton->setVisible(contact->hasKeys(Shared::EncryptionProtocol::omemo2)); - break; + case 8: //[fallthrough] case 9: updateEncryptionState(); break; @@ -77,11 +72,16 @@ void Chat::updateState() { } void Chat::updateEncryptionState() { - m_ui->encryptionButton->setEnabled(true); - if (contact->getEncryption() == Shared::EncryptionProtocol::omemo2) - m_ui->encryptionButton->setIcon(Shared::icon("lock")); - else - m_ui->encryptionButton->setIcon(Shared::icon("unlock")); + if (contact->hasKeys(Shared::EncryptionProtocol::omemo2)) { + m_ui->encryptionButton->setVisible(true); + m_ui->encryptionButton->setEnabled(true); + if (contact->getEncryption() == Shared::EncryptionProtocol::omemo2) + m_ui->encryptionButton->setIcon(Shared::icon("lock")); + else + m_ui->encryptionButton->setIcon(Shared::icon("unlock")); + } else { + m_ui->encryptionButton->setVisible(false); + } } From 19835af3cf1eaa1e4e60915fd60ebcb271d01900 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 12 Nov 2023 19:55:32 -0300 Subject: [PATCH 115/137] some debug, fix a crash removing a currently selected contact --- CMakeLists.txt | 5 +++++ core/account.h | 2 +- core/delayManager/manager.cpp | 4 ++-- core/handlers/messagehandler.cpp | 15 +++++++++++++-- core/handlers/omemohandler.cpp | 10 +++++++++- core/handlers/omemohandler.h | 2 +- main/main.cpp | 2 +- packaging/Archlinux/PKGBUILD | 2 +- ui/widgets/messageline/feedview.cpp | 12 +++++++----- 9 files changed, 40 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 552cba9..280fb22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,11 +136,16 @@ if (NOT SYSTEM_QXMPP) target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/omemo) target_include_directories(squawk PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/qxmpp/src/omemo) set(BUILD_OMEMO ON) + set(BUILD_TESTS OFF) else () set(BUILD_OMEMO OFF) endif () add_subdirectory(external/qxmpp) + if (WITH_OMEMO) + target_include_directories(QXmppOmemoQt${QT_VERSION_MAJOR} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/qxmpp/src) + endif() + target_link_libraries(squawk PRIVATE QXmppQt${QT_VERSION_MAJOR}) if (WITH_OMEMO) target_link_libraries(squawk PRIVATE QXmppOmemoQt${QT_VERSION_MAJOR}) diff --git a/core/account.h b/core/account.h index b156059..47c5c7e 100644 --- a/core/account.h +++ b/core/account.h @@ -61,7 +61,7 @@ #include "handlers/discoveryhandler.h" #ifdef WITH_OMEMO -#include +#include #include #include "handlers/trusthandler.h" #include "handlers/omemohandler.h" diff --git a/core/delayManager/manager.cpp b/core/delayManager/manager.cpp index 3281bc6..07d6431 100644 --- a/core/delayManager/manager.cpp +++ b/core/delayManager/manager.cpp @@ -368,9 +368,9 @@ void Core::DelayManager::Manager::receivedBundles(const QString& jid, const std: Job::Id jobId = itr->second; requestedBundles.erase(itr); std::map::const_iterator jitr = runningJobs.find(jobId); - if (jitr == runningJobs.end()) { + if (jitr == runningJobs.end()) throw JobNotFound(jobId, "receivedBundles"); - } + Job* jb = jitr->second; InfoForUser* job = dynamic_cast(jb); diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index 2c8526a..f9b331f 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -357,28 +357,36 @@ std::pair Core::MessageHandler::scheduleSending if (task.isFinished()) { const QXmppE2eeExtension::MessageEncryptResult& res = task.result(); if (std::holds_alternative>(res)) { + qDebug() << "Successfully encrypted a message"; const std::unique_ptr& encrypted = std::get>(res); encrypted->setBody(QString()); encrypted->setOutOfBandUrl(QString()); bool success = acc->client.sendPacket(*encrypted.get()); - if (success) + if (success) { + qDebug() << "Successfully sent an encrypted message"; return {Shared::Message::State::sent, ""}; - else + } else { + qDebug() << "Couldn't sent an encrypted message"; return {Shared::Message::State::error, "Error sending successfully encrypted message"}; + } } else if (std::holds_alternative(res)) { + qDebug() << "Couldn't encrypt a message"; const QXmppError& err = std::get(res); return {Shared::Message::State::error, err.description}; } else { + qDebug() << "Couldn't encrypt a message"; return {Shared::Message::State::error, "Unexpected error ecryptng the message"}; } } else { task.then(this, [this, id] (QXmppE2eeExtension::MessageEncryptResult&& result) { if (std::holds_alternative>(result)) { + qDebug() << "Successfully encrypted a message"; const std::unique_ptr& encrypted = std::get>(result); encrypted->setBody(QString()); encrypted->setOutOfBandUrl(QString()); bool success = acc->client.sendPacket(*encrypted.get()); if (success) { + qDebug() << "Successfully sent an encrypted message"; std::tuple ids = getOriginalPendingMessageId(id, false); if (std::get<0>(ids)) { QString id = std::get<1>(ids); @@ -393,12 +401,15 @@ std::pair Core::MessageHandler::scheduleSending qDebug() << "Encrypted message has been successfully sent, but it couldn't be found to update the sate"; } } else { + qDebug() << "Couldn't sent an encrypted message"; handlePendingMessageError(id, "Error sending successfully encrypted message"); } } else if (std::holds_alternative(result)) { + qDebug() << "Couldn't encrypt a message"; const QXmppError& err = std::get(result); handlePendingMessageError(id, err.description); } else { + qDebug() << "Couldn't encrypt a message"; handlePendingMessageError(id, "Unexpected error ecryptng the message"); } }); diff --git a/core/handlers/omemohandler.cpp b/core/handlers/omemohandler.cpp index 41cee76..7f7c53b 100644 --- a/core/handlers/omemohandler.cpp +++ b/core/handlers/omemohandler.cpp @@ -169,7 +169,15 @@ void Core::OmemoHandler::getDevices(const QString& jid, std::list::const_iterator itr = devs.begin(), end = devs.end(); itr != end; ++itr) { const Device& dev = itr.value(); - out.emplace_back(itr.key(), dev.keyId, dev.label, QDateTime(), Shared::TrustLevel::undecided, Shared::EncryptionProtocol::omemo2, false); + out.emplace_back( + itr.key(), + dev.keyId, + dev.label, + dev.removalFromDeviceListDate, + Shared::TrustLevel::undecided, + Shared::EncryptionProtocol::omemo2, + false + ); } } diff --git a/core/handlers/omemohandler.h b/core/handlers/omemohandler.h index 60fd8d1..2d46546 100644 --- a/core/handlers/omemohandler.h +++ b/core/handlers/omemohandler.h @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include diff --git a/main/main.cpp b/main/main.cpp index 06c2c95..f810dbb 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -24,7 +24,7 @@ #include #ifdef WITH_OMEMO -#include +#include #endif int main(int argc, char *argv[]) { diff --git a/packaging/Archlinux/PKGBUILD b/packaging/Archlinux/PKGBUILD index 6e63901..bd9c981 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Yury Gubich pkgname=squawk -pkgver=0.2.2 +pkgver=0.2.3 pkgrel=1 pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)" arch=('i686' 'x86_64') diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index 43d0218..41e4484 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -133,10 +133,13 @@ void FeedView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottom void FeedView::updateGeometries() { //qDebug() << "updateGeometries"; - QScrollBar* bar = verticalScrollBar(); - - const QStyle* st = style(); const QAbstractItemModel* m = model(); + if (m == nullptr) + return QAbstractItemView::updateGeometries(); + + QScrollBar* bar = verticalScrollBar(); + const QStyle* st = style(); + QSize layoutBounds = maximumViewportSize(); QStyleOptionViewItem option = viewOptions(); option.rect.setHeight(maxMessageHeight); @@ -210,9 +213,8 @@ void FeedView::updateGeometries() { positionProgress(); - if (specialDelegate) { + if (specialDelegate) clearWidgetsMode = true; - } QAbstractItemView::updateGeometries(); } From 00af58228724e2cfb39467f887ad9220de6cfedd Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 13 Nov 2023 19:05:26 -0300 Subject: [PATCH 116/137] Own omemo key display, a bit of CMake clean up --- CMakeLists.txt | 73 +++++++-------- core/delayManager/manager.cpp | 70 +++++++------- core/handlers/messagehandler.cpp | 5 +- core/handlers/messagehandler.h | 13 +-- core/handlers/omemohandler.cpp | 83 +++++++++-------- core/handlers/omemohandler.h | 1 + shared/keyinfo.h | 40 ++++---- ui/utils/expandinglist.cpp | 81 +++++++++------- ui/utils/expandinglist.h | 48 +++++----- ui/widgets/info/info.cpp | 32 ++++--- ui/widgets/info/info.h | 37 ++++---- ui/widgets/info/omemo/keydelegate.cpp | 32 ++++--- ui/widgets/info/omemo/keydelegate.h | 46 ++++----- ui/widgets/info/omemo/omemo.cpp | 71 +++++++++----- ui/widgets/info/omemo/omemo.h | 43 +++++---- ui/widgets/info/omemo/omemo.ui | 128 +++++++++++++++++--------- 16 files changed, 443 insertions(+), 360 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 280fb22..edf9297 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,7 +47,7 @@ find_package(Boost COMPONENTS) target_include_directories(squawk PRIVATE ${Boost_INCLUDE_DIRS}) -#OMEMO +## OMEMO if (WITH_OMEMO) find_package(PkgConfig) if (PKG_CONFIG_FOUND) @@ -65,22 +65,6 @@ if (WITH_OMEMO) endif () endif () -## QXmpp -if (SYSTEM_QXMPP) - if (WITH_OMEMO) - find_package(QXmpp CONFIG COMPONENTS Omemo) - else () - find_package(QXmpp CONFIG) - endif () - - if (NOT QXmpp_FOUND) - set(SYSTEM_QXMPP OFF) - message("QXmpp package wasn't found, trying to build with bundled QXmpp") - else () - message("Building with system QXmpp") - endif () -endif () - ## KIO if (WITH_KIO) find_package(KF5KIO CONFIG) @@ -107,6 +91,7 @@ if (WITH_KWALLET) endif () endif () +## KConfig if (WITH_KCONFIG) find_package(KF5Config CONFIG) if (NOT KF5Config_FOUND) @@ -125,7 +110,22 @@ if (WITH_KCONFIG) endif() endif() -if (NOT SYSTEM_QXMPP) +## QXmpp +if (SYSTEM_QXMPP) + if (WITH_OMEMO) + find_package(QXmpp CONFIG COMPONENTS Omemo) + else () + find_package(QXmpp CONFIG) + endif () + + if (NOT QXmpp_FOUND) + set(SYSTEM_QXMPP OFF) + message("QXmpp package wasn't found, trying to build with bundled QXmpp") + else () + message("Building with system QXmpp") + endif () +endif () #it's endif() + if() and not else() because I want it to have a fallback behaviour +if (NOT SYSTEM_QXMPP) #we can fail finding system QXmpp and this way we'll check bundled before failing completely message("Building with bundled QXmpp") target_include_directories(squawk PRIVATE ${CMAKE_SOURCE_DIR}/external/qxmpp/src/base) @@ -141,19 +141,10 @@ if (NOT SYSTEM_QXMPP) set(BUILD_OMEMO OFF) endif () add_subdirectory(external/qxmpp) - + add_library(QXmpp::QXmpp ALIAS QXmppQt${QT_VERSION_MAJOR}) if (WITH_OMEMO) target_include_directories(QXmppOmemoQt${QT_VERSION_MAJOR} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/external/qxmpp/src) - endif() - - target_link_libraries(squawk PRIVATE QXmppQt${QT_VERSION_MAJOR}) - if (WITH_OMEMO) - target_link_libraries(squawk PRIVATE QXmppOmemoQt${QT_VERSION_MAJOR}) - endif () -else () - target_link_libraries(squawk PRIVATE QXmpp::QXmpp) - if (WITH_OMEMO) - target_link_libraries(squawk PRIVATE QXmpp::Omemo) + add_library(QXmpp::Omemo ALIAS QXmppOmemoQt${QT_VERSION_MAJOR}) endif () endif () @@ -166,18 +157,14 @@ if (SYSTEM_LMDBAL) else () message("Building with system LMDBAL") endif () -endif() - -if (NOT SYSTEM_LMDBAL) +else() message("Building with bundled LMDBAL") set(BUILD_STATIC ON) add_subdirectory(external/lmdbal) add_library(LMDBAL::LMDBAL ALIAS LMDBAL) endif() -target_link_libraries(squawk PRIVATE LMDBAL::LMDBAL) - -# Linking +## Linking target_link_libraries(squawk PRIVATE Qt${QT_VERSION_MAJOR}::Core @@ -186,17 +173,23 @@ target_link_libraries(squawk Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Xml + LMDBAL::LMDBAL + QXmpp::QXmpp + simpleCrypt ) -target_link_libraries(squawk PRIVATE lmdb) -target_link_libraries(squawk PRIVATE simpleCrypt) -# Link thread libraries on Linux + +if (WITH_OMEMO) + target_link_libraries(squawk PRIVATE QXmpp::Omemo) +endif () + +## Link thread libraries on Linux if(UNIX AND NOT APPLE) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) target_link_libraries(squawk PRIVATE Threads::Threads) endif() -# Build type +## Build type if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug) endif () @@ -228,7 +221,7 @@ add_subdirectory(shared) add_subdirectory(translations) add_subdirectory(ui) -# Install the executable +## Install the executable install(TARGETS squawk DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES README.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk) install(FILES LICENSE.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk) diff --git a/core/delayManager/manager.cpp b/core/delayManager/manager.cpp index 07d6431..de37d47 100644 --- a/core/delayManager/manager.cpp +++ b/core/delayManager/manager.cpp @@ -1,18 +1,20 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "manager.h" @@ -39,17 +41,14 @@ Core::DelayManager::Manager::Manager(const QString& poj, Job::Id mpj, QObject* p requestedBundles(), #endif ownJid(poj) -{ -} +{} Core::DelayManager::Manager::~Manager() { - for (const std::pair& pair : runningJobs) { + for (const std::pair& pair : runningJobs) delete pair.second; - } - for (Job* job : jobSequence) { + for (Job* job : jobSequence) delete job; - } } Core::DelayManager::Job::Id Core::DelayManager::Manager::getNextJobId() { @@ -151,11 +150,10 @@ void Core::DelayManager::Manager::preScheduleJob(Job* job) { void Core::DelayManager::Manager::scheduleJob(Job* job) { preScheduleJob(job); - if (runningJobs.size() < maxParallelJobs) { + if (runningJobs.size() < maxParallelJobs) executeJob(job); - } else { + else scheduledJobs.push_back(job); - } } void Core::DelayManager::Manager::preExecuteJob(Job* job) { @@ -190,9 +188,9 @@ void Core::DelayManager::Manager::executeJob(Job* job) { void Core::DelayManager::Manager::jobIsDone(Job::Id jobId) { std::map::const_iterator itr = runningJobs.find(jobId); - if (itr == runningJobs.end()) { + if (itr == runningJobs.end()) throw JobNotFound(jobId, "jobIsDone"); - } + Job* job = itr->second; delete job; runningJobs.erase(itr); @@ -292,9 +290,9 @@ void Core::DelayManager::Manager::receivedVCard(const QString& jid, const Shared Job::Id jobId = cardItr->second; requestedVCards.erase(cardItr); std::map::const_iterator itr = runningJobs.find(jobId); - if (itr == runningJobs.end()) { + if (itr == runningJobs.end()) throw JobNotFound(jobId, "receivedVCard"); - } + Job* job = itr->second; switch (job->type) { @@ -330,9 +328,9 @@ void Core::DelayManager::Manager::receivedOwnVCard(const Shared::VCard& card) { Job::Id jobId = ownVCardJobId; ownVCardJobId = 0; std::map::const_iterator itr = runningJobs.find(jobId); - if (itr == runningJobs.end()) { + if (itr == runningJobs.end()) throw JobNotFound(jobId, "receivedOwnVCard"); - } + Job* job = itr->second; switch (job->type) { case Job::Type::ownCardInternal: @@ -388,9 +386,9 @@ void Core::DelayManager::Manager::receivedOwnBundles(const std::list::const_iterator jitr = runningJobs.find(jobId); - if (jitr == runningJobs.end()) { + if (jitr == runningJobs.end()) throw JobNotFound(jobId, "receivedOwnBundles"); - } + Job* jb = jitr->second; OwnInfoForUser* job = dynamic_cast(jb); @@ -414,9 +412,9 @@ Core::DelayManager::Manager::UnexpectedJobType::UnexpectedJobType(Job::Type p_ty std::string Core::DelayManager::Manager::UnexpectedJobType::getMessage() const{ std::string msg("Unexpected job type: "); msg += Job::TypeString[static_cast(type)]; - if (method.size() > 0) { + if (method.size() > 0) msg += " in method " + method; - } + return msg; } @@ -430,8 +428,8 @@ std::string Core::DelayManager::Manager::JobNotFound::getMessage() const { std::string msg("Job with id "); msg += std::to_string(id); msg += " was not found"; - if (method.size() > 0) { + if (method.size() > 0) msg += " in method " + method; - } + return msg; } diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index f9b331f..8489dba 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -147,10 +147,9 @@ bool Core::MessageHandler::handleGroupMessage(const QXmppMessage& msg, bool outg initializeMessage(sMsg, msg, outgoing, forwarded, guessing); QString jid = sMsg.getPenPalJid(); Conference* cnt = acc->rh->getConference(jid); - if (cnt == 0) { + if (cnt == 0) return false; - } - + std::tuple ids = getOriginalPendingMessageId(msg.id()); if (std::get<0>(ids)) { QMap cData = {{"state", static_cast(Shared::Message::State::delivered)}}; diff --git a/core/handlers/messagehandler.h b/core/handlers/messagehandler.h index 99a77d3..e3199dc 100644 --- a/core/handlers/messagehandler.h +++ b/core/handlers/messagehandler.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef CORE_MESSAGEHANDLER_H -#define CORE_MESSAGEHANDLER_H +#pragma once #include @@ -36,15 +35,9 @@ #include namespace Core { - -/** - * @todo write docs - */ - class Account; -class MessageHandler : public QObject -{ +class MessageHandler : public QObject { Q_OBJECT public: MessageHandler(Account* account); @@ -90,5 +83,3 @@ private: }; } - -#endif // CORE_MESSAGEHANDLER_H diff --git a/core/handlers/omemohandler.cpp b/core/handlers/omemohandler.cpp index 7f7c53b..eaa674f 100644 --- a/core/handlers/omemohandler.cpp +++ b/core/handlers/omemohandler.cpp @@ -1,18 +1,20 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 #include "omemohandler.h" @@ -196,18 +198,7 @@ void Core::OmemoHandler::requestOwnBundles() { } void Core::OmemoHandler::onBundlesReceived(const QString& jid) { - std::list keys; - acc->oh->getDevices(jid, keys); - std::map trustLevels = acc->th->getKeys(Shared::EncryptionProtocol::omemo2, jid); - - qDebug() << "OMEMO info for " << jid << " devices:" << keys.size() << ", trustLevels:" << trustLevels.size(); - for (Shared::KeyInfo& key : keys) { - std::map::const_iterator itr = trustLevels.find(key.fingerPrint); - if (itr != trustLevels.end()) { - key.trustLevel = itr->second; - qDebug() << "Found a trust level for a device!"; - } - } + std::list keys = readKeys(jid); Contact* cnt = acc->rh->getContact(jid); if (cnt) @@ -217,23 +208,35 @@ void Core::OmemoHandler::onBundlesReceived(const QString& jid) { } void Core::OmemoHandler::onOwnBundlesReceived() { - QString jid = acc->getBareJid(); - std::list keys; - acc->oh->getDevices(jid, keys); - std::map trustLevels = acc->th->getKeys(Shared::EncryptionProtocol::omemo2, jid); - - qDebug() << "OMEMO info for " << jid << " devices:" << keys.size() << ", trustLevels:" << trustLevels.size(); - for (Shared::KeyInfo& key : keys) { - std::map::const_iterator itr = trustLevels.find(key.fingerPrint); - if (itr != trustLevels.end()) { - key.trustLevel = itr->second; - qDebug() << "Found a trust level for a device!"; - } - } + std::list keys = readKeys(acc->getBareJid()); + if (ownDevice) + keys.emplace_front( + ownDevice->id, + ownDevice->publicIdentityKey, + ownDevice->label, + QDateTime::currentDateTime(), + Shared::TrustLevel::authenticated, + Shared::EncryptionProtocol::omemo2, + true + ); acc->delay->receivedOwnBundles(keys); } +std::list Core::OmemoHandler::readKeys(const QString& jid) { + std::list keys; + getDevices(jid, keys); + std::map trustLevels = acc->th->getKeys(Shared::EncryptionProtocol::omemo2, jid); + + for (Shared::KeyInfo& key : keys) { + std::map::const_iterator itr = trustLevels.find(key.fingerPrint); + if (itr != trustLevels.end()) + key.trustLevel = itr->second; + } + + return keys; +} + void Core::OmemoHandler::onOmemoDeviceAdded(const QString& jid, uint32_t id) { SHARED_UNUSED(id); qDebug() << "OMEMO device added for" << jid; diff --git a/core/handlers/omemohandler.h b/core/handlers/omemohandler.h index 2d46546..99bfe38 100644 --- a/core/handlers/omemohandler.h +++ b/core/handlers/omemohandler.h @@ -69,6 +69,7 @@ public slots: private slots: void onBundlesReceived(const QString& jid); void onOwnBundlesReceived(); + std::list readKeys(const QString& jid); private: Account* acc; diff --git a/shared/keyinfo.h b/shared/keyinfo.h index 8d7db0a..500a2c1 100644 --- a/shared/keyinfo.h +++ b/shared/keyinfo.h @@ -1,21 +1,22 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 SHARED_KEYINFO_H -#define SHARED_KEYINFO_H +#pragma once #include #include @@ -27,8 +28,7 @@ namespace Shared { -class KeyInfo -{ +class KeyInfo { public: KeyInfo( uint32_t id, @@ -56,5 +56,3 @@ public: }; } - -#endif // SHARED_KEYINFO_H diff --git a/ui/utils/expandinglist.cpp b/ui/utils/expandinglist.cpp index 6d1546d..5cc9bad 100644 --- a/ui/utils/expandinglist.cpp +++ b/ui/utils/expandinglist.cpp @@ -1,43 +1,60 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "expandinglist.h" -QSize ExpandingList::viewportSizeHint() const { - if (QAbstractItemView::sizeAdjustPolicy() != QAbstractScrollArea::AdjustToContents) - return QListView::viewportSizeHint(); +#include - if (model() == nullptr) - return QSize(0, 0); - if (model()->rowCount() == 0) - return QSize(0, 0); +ExpandingList::ExpandingList(QWidget* parent) : + QListView(parent), + scrollBarExtent(qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent)) +{ + setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); +} +bool ExpandingList::hasHeightForWidth() const { #if (QT_VERSION < QT_VERSION_CHECK(6, 2, 0)) - const int rowCount = model()->rowCount(); - int height = 0; - for (int i = 0; i < rowCount; i++) { - height += QListView::sizeHintForRow(i); - } - return QSize(QListView::viewportSizeHint().width(), height); + return + QAbstractItemView::sizeAdjustPolicy() == QAbstractScrollArea::AdjustToContents && + scrollBarExtent > 0; #else - return QListView::viewportSizeHint(); + return false; #endif } -QSize ExpandingList::minimumSizeHint() const { - return viewportSizeHint(); -} +int ExpandingList::heightForWidth(int width) const { + QAbstractItemModel* md = model(); + if (md == nullptr) + return 0; + if (md->rowCount() == 0) + return 0; + + const int rowCount = md->rowCount(); + int height = 0; + for (int i = 0; i < rowCount; i++) + height += QListView::sizeHintForRow(i); + + height += frameWidth(); + int minWidth = sizeHintForColumn(0) + frameWidth() + 1; + + if (width <= minWidth) + height += scrollBarExtent + 1; + + return height; +} diff --git a/ui/utils/expandinglist.h b/ui/utils/expandinglist.h index 0b29e89..9084757 100644 --- a/ui/utils/expandinglist.h +++ b/ui/utils/expandinglist.h @@ -1,31 +1,35 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 EXPANDINGLIST_H -#define EXPANDINGLIST_H +#pragma once #include class ExpandingList : public QListView { Q_OBJECT public: - using QListView::QListView; //explicit constructor inheriatnce + ExpandingList(QWidget* parent = nullptr); - QSize viewportSizeHint() const override; - QSize minimumSizeHint() const override; + bool hasHeightForWidth() const override; + int heightForWidth(int width) const override; + // QSize viewportSizeHint() const override; + // QSize minimumSizeHint() const override; + +private: + int scrollBarExtent; }; - -#endif // EXPANDINGLIST_H diff --git a/ui/widgets/info/info.cpp b/ui/widgets/info/info.cpp index 6a1105d..948ee27 100644 --- a/ui/widgets/info/info.cpp +++ b/ui/widgets/info/info.cpp @@ -1,18 +1,20 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "info.h" #include "ui_info.h" diff --git a/ui/widgets/info/info.h b/ui/widgets/info/info.h index 10bc063..7d52693 100644 --- a/ui/widgets/info/info.h +++ b/ui/widgets/info/info.h @@ -1,21 +1,22 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 UI_WIDGETS_INFO_H -#define UI_WIDGETS_INFO_H +#pragma once #include @@ -88,5 +89,3 @@ private: }; } - -#endif // UI_WIDGETS_INFO_H diff --git a/ui/widgets/info/omemo/keydelegate.cpp b/ui/widgets/info/omemo/keydelegate.cpp index fd687ad..074ef1c 100644 --- a/ui/widgets/info/omemo/keydelegate.cpp +++ b/ui/widgets/info/omemo/keydelegate.cpp @@ -1,18 +1,20 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "keydelegate.h" #include diff --git a/ui/widgets/info/omemo/keydelegate.h b/ui/widgets/info/omemo/keydelegate.h index 01b45c1..6d7f067 100644 --- a/ui/widgets/info/omemo/keydelegate.h +++ b/ui/widgets/info/omemo/keydelegate.h @@ -1,35 +1,37 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 UI_KEYDELEGATE_H -#define UI_KEYDELEGATE_H +#pragma once #include #include + #include namespace UI { -class KeyDelegate : public QStyledItemDelegate -{ +class KeyDelegate : public QStyledItemDelegate { + Q_OBJECT public: - KeyDelegate(QObject *parent = nullptr); + KeyDelegate(QObject* parent = nullptr); ~KeyDelegate(); - QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const override; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; private: @@ -46,5 +48,3 @@ private: }; } - -#endif // UI_KEYDELEGATE_H diff --git a/ui/widgets/info/omemo/omemo.cpp b/ui/widgets/info/omemo/omemo.cpp index d3722fe..294fbe2 100644 --- a/ui/widgets/info/omemo/omemo.cpp +++ b/ui/widgets/info/omemo/omemo.cpp @@ -1,18 +1,20 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "omemo.h" #include "ui_omemo.h" @@ -23,25 +25,31 @@ constexpr uint8_t fingerprintLength = 32; UI::Omemo::Omemo(QWidget* parent): QWidget(parent), m_ui(new Ui::Omemo()), + deviceKeyDelegate(), keysDelegate(), unusedKeysDelegate(), + deviceKeyModel(), keysModel(), unusedKeysModel(), contextMenu(new QMenu()) { m_ui->setupUi(this); + m_ui->deviceKeyView->setItemDelegate(&deviceKeyDelegate); + m_ui->deviceKeyView->setModel(&deviceKeyModel); m_ui->keysView->setItemDelegate(&keysDelegate); m_ui->keysView->setModel(&keysModel); m_ui->unusedKeysView->setItemDelegate(&unusedKeysDelegate); m_ui->unusedKeysView->setModel(&unusedKeysModel); + unusedVisibility(false); + deviceKeyVisibility(false); + m_ui->keysView->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_ui->keysView, &QWidget::customContextMenuRequested, this, &Omemo::onActiveKeysContextMenu); } -UI::Omemo::~Omemo() -{ +UI::Omemo::~Omemo() { contextMenu->deleteLater(); } @@ -50,9 +58,9 @@ void UI::Omemo::generateMockData() { std::uniform_int_distribution dist(CHAR_MIN, CHAR_MAX); for (int i = 0; i < 5; ++i) { QByteArray fp(fingerprintLength, 0); - for (int i = 0; i < fingerprintLength; ++i) { + for (int i = 0; i < fingerprintLength; ++i) fp[i] = dist(rd); - } + Shared::KeyInfo info; info.id = i; if (i % 3 == 0) @@ -68,14 +76,21 @@ void UI::Omemo::generateMockData() { void UI::Omemo::setData(const std::list& keys) { keysModel.clear(); unusedKeysModel.clear(); + deviceKeyModel.clear(); for (const Shared::KeyInfo& key : keys) { - keysModel.addKey(key); + if (key.currentDevice) + deviceKeyModel.addKey(key); + else + keysModel.addKey(key); } + + unusedVisibility(unusedKeysModel.rowCount() > 0); + deviceKeyVisibility(deviceKeyModel.rowCount() > 0); } const QString UI::Omemo::title() const { - return m_ui->OMEMOHeading->text();} - + return m_ui->OMEMOHeading->text(); +} void UI::Omemo::onActiveKeysContextMenu(const QPoint& pos) { contextMenu->clear(); @@ -119,3 +134,15 @@ void UI::Omemo::onActiveKeysContextMenu(const QPoint& pos) { contextMenu->popup(m_ui->keysView->viewport()->mapToGlobal(pos)); } + +void UI::Omemo::deviceKeyVisibility(bool visible) { + m_ui->deviceKeyHeading->setVisible(visible); + m_ui->deviceKeyline->setVisible(visible); + m_ui->deviceKeyView->setVisible(visible); +} + +void UI::Omemo::unusedVisibility(bool visible) { + m_ui->unusedKeysView->setVisible(visible); + m_ui->unusedKeysHeading->setVisible(visible); + m_ui->line->setVisible(visible); +} diff --git a/ui/widgets/info/omemo/omemo.h b/ui/widgets/info/omemo/omemo.h index a279492..75b1df2 100644 --- a/ui/widgets/info/omemo/omemo.h +++ b/ui/widgets/info/omemo/omemo.h @@ -1,21 +1,22 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 VCARD_OMEMO_H -#define VCARD_OMEMO_H +#pragma once #include @@ -29,8 +30,7 @@ #include "shared/keyinfo.h" namespace UI { -namespace Ui -{ +namespace Ui { class Omemo; } @@ -48,14 +48,17 @@ private slots: private: void generateMockData(); + void unusedVisibility(bool visible); + void deviceKeyVisibility(bool visible); private: QScopedPointer m_ui; + UI::KeyDelegate deviceKeyDelegate; UI::KeyDelegate keysDelegate; UI::KeyDelegate unusedKeysDelegate; + Models::Keys deviceKeyModel; Models::Keys keysModel; Models::Keys unusedKeysModel; QMenu* contextMenu; }; } -#endif // VCARD_OMEMO_H diff --git a/ui/widgets/info/omemo/omemo.ui b/ui/widgets/info/omemo/omemo.ui index 745b981..64361f9 100644 --- a/ui/widgets/info/omemo/omemo.ui +++ b/ui/widgets/info/omemo/omemo.ui @@ -64,7 +64,28 @@ - + + + + + 16 + 75 + true + + + + Unused keys + + + + + + + Qt::Horizontal + + + + @@ -89,42 +110,7 @@ - - - - Qt::Horizontal - - - - - - - - 16 - 75 - true - - - - Active keys - - - - - - - - 16 - 75 - true - - - - Unused keys - - - - + @@ -149,8 +135,68 @@ - - + + + + + 16 + 75 + true + + + + Device key + + + + + + + + 16 + 75 + true + + + + Active keys + + + + + + + + 0 + 0 + + + + QAbstractScrollArea::AdjustToContents + + + false + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + false + + + + + + + Qt::Horizontal + + + + + Qt::Horizontal @@ -162,8 +208,8 @@ - - + + Qt::Horizontal From 75554c74511e1bb8b74f56cb2f1159a7f177527d Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 14 Nov 2023 20:23:39 -0300 Subject: [PATCH 117/137] refactorng --- cmake/FindLMDB.cmake | 52 ---- cmake/FindSignal.cmake | 15 -- core/components/archive.cpp | 105 ++++---- core/components/archive.h | 5 +- core/components/clientcache.cpp | 32 +-- core/components/clientcache.h | 38 ++- core/components/networkaccess.cpp | 5 +- core/components/networkaccess.h | 8 +- core/components/urlstorage.cpp | 3 +- core/components/urlstorage.h | 5 +- core/delayManager/cardinternal.cpp | 32 +-- core/delayManager/cardinternal.h | 38 ++- core/delayManager/contact.cpp | 32 +-- core/delayManager/contact.h | 37 ++- core/delayManager/info.cpp | 32 +-- core/delayManager/info.h | 37 ++- core/delayManager/infoforuser.cpp | 32 +-- core/delayManager/infoforuser.h | 37 ++- core/delayManager/job.cpp | 32 +-- core/delayManager/job.h | 37 ++- core/delayManager/owncardinternal.cpp | 32 +-- core/delayManager/owncardinternal.h | 37 ++- core/delayManager/owninfoforuser.cpp | 32 +-- core/delayManager/owninfoforuser.h | 37 ++- core/handlers/messagehandler.cpp | 340 ++++++++++++-------------- core/handlers/messagehandler.h | 4 +- core/handlers/omemohandler.cpp | 14 +- core/handlers/rosterhandler.cpp | 14 +- core/handlers/rosterhandler.h | 9 +- shared/messageinfo.h | 9 +- 30 files changed, 515 insertions(+), 627 deletions(-) delete mode 100644 cmake/FindLMDB.cmake delete mode 100644 cmake/FindSignal.cmake diff --git a/cmake/FindLMDB.cmake b/cmake/FindLMDB.cmake deleted file mode 100644 index d6f2cd3..0000000 --- a/cmake/FindLMDB.cmake +++ /dev/null @@ -1,52 +0,0 @@ -#This file is taken from here https://gitlab.ralph.or.at/causal-rt/causal-cpp/, it was GPLv3 license -#Thank you so much, mr. Ralph Alexander Bariz, I hope you don't mind me using your code - -# Try to find LMDB headers and library. -# -# Usage of this module as follows: -# -# find_package(LMDB) -# -# Variables used by this module, they can change the default behaviour and need -# to be set before calling find_package: -# -# LMDB_ROOT_DIR Set this variable to the root installation of -# LMDB if the module has problems finding the -# proper installation path. -# -# Variables defined by this module: -# -# LMDB_FOUND System has LMDB library/headers. -# LMDB_LIBRARIES The LMDB library. -# LMDB_INCLUDE_DIRS The location of LMDB headers. - -find_path(LMDB_ROOT_DIR - NAMES include/lmdb.h - ) - -find_library(LMDB_LIBRARIES - NAMES liblmdb.a liblmdb.so liblmdb.so.a liblmdb.dll.a # We want lmdb to be static, if possible - HINTS ${LMDB_ROOT_DIR}/lib - ) - -add_library(lmdb UNKNOWN IMPORTED) -set_target_properties(lmdb PROPERTIES - IMPORTED_LOCATION ${LMDB_LIBRARIES} - ) - -find_path(LMDB_INCLUDE_DIRS - NAMES lmdb.h - HINTS ${LMDB_ROOT_DIR}/include - ) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(LMDB DEFAULT_MSG - LMDB_LIBRARIES - LMDB_INCLUDE_DIRS - ) - -mark_as_advanced( - LMDB_ROOT_DIR - LMDB_LIBRARIES - LMDB_INCLUDE_DIRS -) diff --git a/cmake/FindSignal.cmake b/cmake/FindSignal.cmake deleted file mode 100644 index 752fed7..0000000 --- a/cmake/FindSignal.cmake +++ /dev/null @@ -1,15 +0,0 @@ -find_path(Signal_INCLUDE_DIR NAMES signal/signal_protocol.h) -find_library(Signal_LIBRARY signal-protocol-c) -mark_as_advanced(Signal_INCLUDE_DIR Signal_LIBRARY) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Signal REQUIRED_VARS Signal_LIBRARY Signal_INCLUDE_DIR) - -if (Signal_FOUND AND NOT TARGET Signal::Signal) - add_library(Signal::Signal UNKNOWN IMPORTED) - set_target_properties(Signal::Signal PROPERTIES - IMPORTED_LINK_INTERFACE_LANGUAGES "C" - IMPORTED_LOCATION "${Signal_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${Signal_INCLUDE_DIR}" - ) -endif () diff --git a/core/components/archive.cpp b/core/components/archive.cpp index 67f6693..2f7139a 100644 --- a/core/components/archive.cpp +++ b/core/components/archive.cpp @@ -207,7 +207,6 @@ unsigned int Core::Archive::addElements(const std::list& messag ++success; } txn.commit(); - return success; } @@ -315,63 +314,63 @@ bool Core::Archive::setAvatar(const QByteArray& data, AvatarInfo& newInfo, bool avatars->removeRecord(res, txn); txn.commit(); return true; - } else { - QString currentPath = db.getPath(); - bool needToRemoveOld = false; - QCryptographicHash hash(QCryptographicHash::Sha1); - hash.addData(data); - QByteArray newHash(hash.result()); - if (haveAvatar) { - if (!generated && !oldInfo.autogenerated && oldInfo.hash == newHash) - return false; + } - QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type); - if (oldAvatar.exists()) { - if (oldAvatar.rename(currentPath + "/" + res + "." + oldInfo.type + ".bak")) { - needToRemoveOld = true; - } else { - qDebug() << "Can't change avatar: couldn't get rid of the old avatar" << oldAvatar.fileName(); - return false; - } - } - } - QMimeDatabase mimedb; - QMimeType type = mimedb.mimeTypeForData(data); - QString ext = type.preferredSuffix(); - QFile newAvatar(currentPath + "/" + res + "." + ext); - if (newAvatar.open(QFile::WriteOnly)) { - newAvatar.write(data); - newAvatar.close(); - - newInfo.type = ext; - newInfo.hash = newHash; - newInfo.autogenerated = generated; - try { - avatars->forceRecord(res, newInfo, txn); - txn.commit(); - } catch (...) { - qDebug() << "Can't change avatar: couldn't store changes to database for" << newAvatar.fileName() << "rolling back to the previous state"; - if (needToRemoveOld) { - QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type + ".bak"); - oldAvatar.rename(currentPath + "/" + res + "." + oldInfo.type); - } + QString currentPath = db.getPath(); + bool needToRemoveOld = false; + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(data); + QByteArray newHash(hash.result()); + if (haveAvatar) { + if (!generated && !oldInfo.autogenerated && oldInfo.hash == newHash) + return false; + + QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type); + if (oldAvatar.exists()) { + if (oldAvatar.rename(currentPath + "/" + res + "." + oldInfo.type + ".bak")) { + needToRemoveOld = true; + } else { + qDebug() << "Can't change avatar: couldn't get rid of the old avatar" << oldAvatar.fileName(); return false; } - - if (needToRemoveOld) { - QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type + ".bak"); - oldAvatar.remove(); - } - return true; - } else { - qDebug() << "Can't change avatar: cant open file to write" << newAvatar.fileName() << "rolling back to the previous state"; - if (needToRemoveOld) { - QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type + ".bak"); - oldAvatar.rename(currentPath + "/" + res + "." + oldInfo.type); - } - return false; } } + QMimeDatabase mimedb; + QMimeType type = mimedb.mimeTypeForData(data); + QString ext = type.preferredSuffix(); + QFile newAvatar(currentPath + "/" + res + "." + ext); + if (!newAvatar.open(QFile::WriteOnly)) { + qDebug() << "Can't change avatar: cant open file to write" << newAvatar.fileName() << "rolling back to the previous state"; + if (needToRemoveOld) { + QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type + ".bak"); + oldAvatar.rename(currentPath + "/" + res + "." + oldInfo.type); + } + return false; + } + + newAvatar.write(data); + newAvatar.close(); + + newInfo.type = ext; + newInfo.hash = newHash; + newInfo.autogenerated = generated; + try { + avatars->forceRecord(res, newInfo, txn); + txn.commit(); + } catch (...) { + qDebug() << "Can't change avatar: couldn't store changes to database for" << newAvatar.fileName() << "rolling back to the previous state"; + if (needToRemoveOld) { + QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type + ".bak"); + oldAvatar.rename(currentPath + "/" + res + "." + oldInfo.type); + } + return false; + } + + if (needToRemoveOld) { + QFile oldAvatar(currentPath + "/" + res + "." + oldInfo.type + ".bak"); + oldAvatar.remove(); + } + return true; } bool Core::Archive::readAvatarInfo(Core::Archive::AvatarInfo& target, const QString& resource) const { diff --git a/core/components/archive.h b/core/components/archive.h index 08f508a..6a2be73 100644 --- a/core/components/archive.h +++ b/core/components/archive.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef CORE_ARCHIVE_H -#define CORE_ARCHIVE_H +#pragma once #include #include @@ -101,5 +100,3 @@ private: QDataStream& operator << (QDataStream &out, const Core::Archive::AvatarInfo& info); QDataStream& operator >> (QDataStream &in, Core::Archive::AvatarInfo& info); - -#endif // CORE_ARCHIVE_H diff --git a/core/components/clientcache.cpp b/core/components/clientcache.cpp index fe1fa64..f1eb886 100644 --- a/core/components/clientcache.cpp +++ b/core/components/clientcache.cpp @@ -1,18 +1,20 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "clientcache.h" diff --git a/core/components/clientcache.h b/core/components/clientcache.h index b9dba73..c68dbfb 100644 --- a/core/components/clientcache.h +++ b/core/components/clientcache.h @@ -1,21 +1,22 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 CORE_CLIENTCACHE_H -#define CORE_CLIENTCACHE_H +#pragma once #include #include @@ -60,6 +61,3 @@ private: }; } - - -#endif // CORE_CLIENTCACHE_H diff --git a/core/components/networkaccess.cpp b/core/components/networkaccess.cpp index 862c664..0771dfa 100644 --- a/core/components/networkaccess.cpp +++ b/core/components/networkaccess.cpp @@ -16,7 +16,6 @@ * along with this program. If not, see . */ - #include #include @@ -129,9 +128,9 @@ void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code) { void Core::NetworkAccess::onDownloadSSLError(const QList& errors) { qDebug() << "DEBUG: DOWNLOAD SSL ERRORS"; - for (const QSslError& err : errors) { + for (const QSslError& err : errors) qDebug() << err.errorString(); - } + QNetworkReply* rpl = static_cast(sender()); QString url = rpl->url().toString(); std::map::const_iterator itr = downloads.find(url); diff --git a/core/components/networkaccess.h b/core/components/networkaccess.h index 99a15ed..ddb5ba8 100644 --- a/core/components/networkaccess.h +++ b/core/components/networkaccess.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef CORE_NETWORKACCESS_H -#define CORE_NETWORKACCESS_H +#pragma once #include #include @@ -36,8 +35,7 @@ namespace Core { //TODO Need to describe how to get rid of records when file is no longer reachable; -class NetworkAccess : public QObject -{ +class NetworkAccess : public QObject { Q_OBJECT struct Transfer; public: @@ -100,5 +98,3 @@ private: }; } - -#endif // CORE_NETWORKACCESS_H diff --git a/core/components/urlstorage.cpp b/core/components/urlstorage.cpp index 31f36ad..5e34792 100644 --- a/core/components/urlstorage.cpp +++ b/core/components/urlstorage.cpp @@ -193,9 +193,8 @@ Core::UrlStorage::UrlInfo::~UrlInfo() {} bool Core::UrlStorage::UrlInfo::addMessage(const QString& acc, const QString& jid, const QString& id) { for (const Shared::MessageInfo& info : messages) { - if (info.account == acc && info.jid == jid && info.messageId == id) { + if (info.account == acc && info.jid == jid && info.messageId == id) return false; - } } messages.emplace_back(acc, jid, id); return true; diff --git a/core/components/urlstorage.h b/core/components/urlstorage.h index fc9d71d..6fb536a 100644 --- a/core/components/urlstorage.h +++ b/core/components/urlstorage.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef CORE_URLSTORAGE_H -#define CORE_URLSTORAGE_H +#pragma once #include #include @@ -93,5 +92,3 @@ public: QDataStream& operator >> (QDataStream &in, Core::UrlStorage::UrlInfo& info); QDataStream& operator << (QDataStream &out, const Core::UrlStorage::UrlInfo& info); - -#endif // CORE_URLSTORAGE_H diff --git a/core/delayManager/cardinternal.cpp b/core/delayManager/cardinternal.cpp index c9ed203..b5be472 100644 --- a/core/delayManager/cardinternal.cpp +++ b/core/delayManager/cardinternal.cpp @@ -1,18 +1,20 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "cardinternal.h" diff --git a/core/delayManager/cardinternal.h b/core/delayManager/cardinternal.h index 17dd1ba..6db7734 100644 --- a/core/delayManager/cardinternal.h +++ b/core/delayManager/cardinternal.h @@ -1,22 +1,22 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 CORE_DELAYMANAGER_CARDINTERNAL_H -#define CORE_DELAYMANAGER_CARDINTERNAL_H +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ +#pragma once #include @@ -33,5 +33,3 @@ public: } } - -#endif // CORE_DELAYMANAGER_CARDINTERNAL_H diff --git a/core/delayManager/contact.cpp b/core/delayManager/contact.cpp index 286c8bd..3af1e3f 100644 --- a/core/delayManager/contact.cpp +++ b/core/delayManager/contact.cpp @@ -1,18 +1,20 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "contact.h" diff --git a/core/delayManager/contact.h b/core/delayManager/contact.h index c136525..3f1ab98 100644 --- a/core/delayManager/contact.h +++ b/core/delayManager/contact.h @@ -1,21 +1,22 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 CORE_DELAYMANAGER_CONTACT_H -#define CORE_DELAYMANAGER_CONTACT_H +#pragma once #include @@ -35,5 +36,3 @@ public: } } - -#endif // CORE_DELAYMANAGER_CONTACT_H diff --git a/core/delayManager/info.cpp b/core/delayManager/info.cpp index b5b619d..9953fd4 100644 --- a/core/delayManager/info.cpp +++ b/core/delayManager/info.cpp @@ -1,18 +1,20 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "info.h" diff --git a/core/delayManager/info.h b/core/delayManager/info.h index a6601a1..f03e0c7 100644 --- a/core/delayManager/info.h +++ b/core/delayManager/info.h @@ -1,21 +1,22 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 CORE_DELAYMANAGER_INFO_H -#define CORE_DELAYMANAGER_INFO_H +#pragma once #include "job.h" @@ -52,5 +53,3 @@ private: } } - -#endif // CORE_DELAYMANAGER_INFO_H diff --git a/core/delayManager/infoforuser.cpp b/core/delayManager/infoforuser.cpp index 067be80..7d0aed9 100644 --- a/core/delayManager/infoforuser.cpp +++ b/core/delayManager/infoforuser.cpp @@ -1,18 +1,20 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "infoforuser.h" diff --git a/core/delayManager/infoforuser.h b/core/delayManager/infoforuser.h index 651d741..c2f4692 100644 --- a/core/delayManager/infoforuser.h +++ b/core/delayManager/infoforuser.h @@ -1,21 +1,22 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 CORE_DELAYMANAGER_INFOFORUSER_H -#define CORE_DELAYMANAGER_INFOFORUSER_H +#pragma once #include "contact.h" #include "info.h" @@ -31,5 +32,3 @@ public: } } - -#endif // CORE_DELAYMANAGER_INFOFORUSER_H diff --git a/core/delayManager/job.cpp b/core/delayManager/job.cpp index b2d74b2..37acc02 100644 --- a/core/delayManager/job.cpp +++ b/core/delayManager/job.cpp @@ -1,18 +1,20 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "job.h" diff --git a/core/delayManager/job.h b/core/delayManager/job.h index 6bfdc1d..26156c0 100644 --- a/core/delayManager/job.h +++ b/core/delayManager/job.h @@ -1,21 +1,22 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 CORE_DELAYMANAGER_JOB_H -#define CORE_DELAYMANAGER_JOB_H +#pragma once #include @@ -51,5 +52,3 @@ public: } } - -#endif // CORE_DELAYMANAGER_JOB_H diff --git a/core/delayManager/owncardinternal.cpp b/core/delayManager/owncardinternal.cpp index 43ed540..d9729ba 100644 --- a/core/delayManager/owncardinternal.cpp +++ b/core/delayManager/owncardinternal.cpp @@ -1,18 +1,20 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "owncardinternal.h" diff --git a/core/delayManager/owncardinternal.h b/core/delayManager/owncardinternal.h index 7cca0a0..30d7c56 100644 --- a/core/delayManager/owncardinternal.h +++ b/core/delayManager/owncardinternal.h @@ -1,21 +1,22 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 CORE_DELAYMANAGER_OWNCARDINTERNAL_H -#define CORE_DELAYMANAGER_OWNCARDINTERNAL_H +#pragma once #include "job.h" @@ -33,5 +34,3 @@ public: } } - -#endif // CORE_DELAYMANAGER_OWNCARDINTERNAL_H diff --git a/core/delayManager/owninfoforuser.cpp b/core/delayManager/owninfoforuser.cpp index 396dc49..98cbdfa 100644 --- a/core/delayManager/owninfoforuser.cpp +++ b/core/delayManager/owninfoforuser.cpp @@ -1,18 +1,20 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "owninfoforuser.h" diff --git a/core/delayManager/owninfoforuser.h b/core/delayManager/owninfoforuser.h index 80a13b6..4348b04 100644 --- a/core/delayManager/owninfoforuser.h +++ b/core/delayManager/owninfoforuser.h @@ -1,21 +1,22 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 CORE_DELAYMANAGER_OWNINFOFORUSER_H -#define CORE_DELAYMANAGER_OWNINFOFORUSER_H +#pragma once #include "contact.h" #include "info.h" @@ -31,5 +32,3 @@ public: } } - -#endif // CORE_DELAYMANAGER_OWNINFOFORUSER_H diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index 8489dba..094f671 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -19,6 +19,10 @@ #include "messagehandler.h" #include "core/account.h" +static const QMap statePending({{"state", static_cast(Shared::Message::State::pending)}}); +static const QMap stateDelivered({{"state", static_cast(Shared::Message::State::delivered)}}); +static const QMap stateSent({{"state", static_cast(Shared::Message::State::sent)}}); + Core::MessageHandler::MessageHandler(Core::Account* account): QObject(), acc(account), @@ -85,102 +89,80 @@ void Core::MessageHandler::onMessageReceived(const QXmppMessage& msg) { } bool Core::MessageHandler::handlePendingMessageError(const QString& id, const QString& errorText) { - std::tuple ids = getOriginalPendingMessageId(id); - if (std::get<0>(ids)) { - QString id = std::get<1>(ids); - QString jid = std::get<2>(ids); - RosterItem* ri = acc->rh->getRosterItem(jid); - QMap cData = { - {"state", static_cast(Shared::Message::State::error)}, + return adjustPendingMessage(id, { + {"state", static_cast(Shared::Message::State::error)}, {"errorText", errorText} - }; - if (ri != nullptr) - ri->changeMessage(id, cData); - - emit acc->changeMessage(jid, id, cData); - return true; - } - - return false; + }, true); } - bool Core::MessageHandler::handleChatMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) { - if (msg.body().size() != 0 || msg.outOfBandUrl().size() > 0) { - Shared::Message sMsg(Shared::Message::chat); - initializeMessage(sMsg, msg, outgoing, forwarded, guessing); - QString jid = sMsg.getPenPalJid(); - Contact* cnt = acc->rh->getContact(jid); - if (cnt == 0) { - cnt = acc->rh->addOutOfRosterContact(jid); - qDebug() << "appending message" << sMsg.getId() << "to an out of roster contact"; - } - if (sMsg.getOutgoing()) { - if (sMsg.getForwarded()) - sMsg.setState(Shared::Message::State::sent); - } else { - sMsg.setState(Shared::Message::State::delivered); - } - QString oId = msg.replaceId(); - if (oId.size() > 0) { - QMap cData = { - {"body", sMsg.getBody()}, - {"stamp", sMsg.getTime()} - }; - cnt->correctMessageInArchive(oId, sMsg); - emit acc->changeMessage(jid, oId, cData); - } else { - cnt->appendMessageToArchive(sMsg); - emit acc->message(sMsg); - } - - return true; + if (msg.body().isEmpty() && msg.outOfBandUrl().isEmpty()) + return false; + + Shared::Message sMsg(Shared::Message::chat); + initializeMessage(sMsg, msg, outgoing, forwarded, guessing); + QString jid = sMsg.getPenPalJid(); + Contact* cnt = acc->rh->getContact(jid); + if (cnt == 0) { + cnt = acc->rh->addOutOfRosterContact(jid); + qDebug() << "appending message" << sMsg.getId() << "to an out of roster contact"; } - return false; + if (sMsg.getOutgoing()) { + if (sMsg.getForwarded()) + sMsg.setState(Shared::Message::State::sent); + } else { + sMsg.setState(Shared::Message::State::delivered); + } + QString oId = msg.replaceId(); + if (oId.size() > 0) { + QMap cData = { + {"body", sMsg.getBody()}, + {"stamp", sMsg.getTime()} + }; + cnt->correctMessageInArchive(oId, sMsg); + emit acc->changeMessage(jid, oId, cData); + } else { + cnt->appendMessageToArchive(sMsg); + emit acc->message(sMsg); + } + + return true; } bool Core::MessageHandler::handleGroupMessage(const QXmppMessage& msg, bool outgoing, bool forwarded, bool guessing) { const QString& body(msg.body()); - if (body.size() != 0) { - - Shared::Message sMsg(Shared::Message::groupChat); - initializeMessage(sMsg, msg, outgoing, forwarded, guessing); - QString jid = sMsg.getPenPalJid(); - Conference* cnt = acc->rh->getConference(jid); - if (cnt == 0) - return false; + if (body.isEmpty()) + return false; - std::tuple ids = getOriginalPendingMessageId(msg.id()); - if (std::get<0>(ids)) { - QMap cData = {{"state", static_cast(Shared::Message::State::delivered)}}; - cnt->changeMessage(std::get<1>(ids), cData); - emit acc->changeMessage(std::get<2>(ids), std::get<1>(ids), cData); - } else { - QString oId = msg.replaceId(); - if (oId.size() > 0) { - QMap cData = { - {"body", sMsg.getBody()}, - {"stamp", sMsg.getTime()} - }; - cnt->correctMessageInArchive(oId, sMsg); - emit acc->changeMessage(jid, oId, cData); - } else { - cnt->appendMessageToArchive(sMsg); - QDateTime minAgo = QDateTime::currentDateTimeUtc().addSecs(-60); - if (sMsg.getTime() > minAgo) { //otherwise it's considered a delayed delivery, most probably MUC history receipt - emit acc->message(sMsg); - } else { - //qDebug() << "Delayed delivery: "; - } - } - } - - return true; - } - return false; + Shared::Message sMsg(Shared::Message::groupChat); + initializeMessage(sMsg, msg, outgoing, forwarded, guessing); + QString jid = sMsg.getPenPalJid(); + Conference* cnt = acc->rh->getConference(jid); + if (cnt == 0) + return false; + + bool result = adjustPendingMessage(msg.id(), stateDelivered, true); + if (result) //then it was an echo of my own sent message, nothing else needs to be done + return result; + + QString oId = msg.replaceId(); + if (oId.size() > 0) { + QMap cData = { + {"body", sMsg.getBody()}, + {"stamp", sMsg.getTime()} + }; + cnt->correctMessageInArchive(oId, sMsg); + emit acc->changeMessage(jid, oId, cData); + } else { + cnt->appendMessageToArchive(sMsg); + QDateTime minAgo = QDateTime::currentDateTimeUtc().addSecs(-60); + if (sMsg.getTime() > minAgo) //otherwise it's considered a delayed delivery, most probably MUC history initial fetch + emit acc->message(sMsg); + } + + return true; } - void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing, bool forwarded, bool guessing) const { const QDateTime& time(source.stamp()); QString id; @@ -254,45 +236,31 @@ void Core::MessageHandler::onCarbonMessageSent(const QXmppMessage& msg) { } #endif -std::tuple Core::MessageHandler::getOriginalPendingMessageId(const QString& id, bool clear) { - std::tuple result({false, "", ""}); +std::optional Core::MessageHandler::getOriginalPendingMessageId(const QString& id, bool clear) { std::map::const_iterator itr = pendingStateMessages.find(id); if (itr != pendingStateMessages.end()) { - std::get<0>(result) = true; - std::get<2>(result) = itr->second; - + Shared::MessageInfo info(acc->name, itr->second, itr->first); std::map::const_iterator itrC = pendingCorrectionMessages.find(id); if (itrC != pendingCorrectionMessages.end()) { if (itrC->second.size() > 0) - std::get<1>(result) = itrC->second; - else - std::get<1>(result) = itr->first; + info.jid = itrC->second; if (clear) pendingCorrectionMessages.erase(itrC); - } else { - std::get<1>(result) = itr->first; } if (clear) pendingStateMessages.erase(itr); + + return info; } - return result; + return std::nullopt; } void Core::MessageHandler::onReceiptReceived(const QString& jid, const QString& id) { SHARED_UNUSED(jid); - std::tuple ids = getOriginalPendingMessageId(id); - if (std::get<0>(ids)) { - QMap cData = {{"state", static_cast(Shared::Message::State::delivered)}}; - RosterItem* ri = acc->rh->getRosterItem(std::get<2>(ids)); - - if (ri != nullptr) - ri->changeMessage(std::get<1>(ids), cData); - - emit acc->changeMessage(std::get<2>(ids), std::get<1>(ids), cData); - } + adjustPendingMessage(id, {{"state", static_cast(Shared::Message::State::delivered)}}, true); } void Core::MessageHandler::sendMessage(const Shared::Message& data, bool newMessage, QString originalId) { @@ -386,19 +354,8 @@ std::pair Core::MessageHandler::scheduleSending bool success = acc->client.sendPacket(*encrypted.get()); if (success) { qDebug() << "Successfully sent an encrypted message"; - std::tuple ids = getOriginalPendingMessageId(id, false); - if (std::get<0>(ids)) { - QString id = std::get<1>(ids); - QString jid = std::get<2>(ids); - RosterItem* ri = acc->rh->getRosterItem(jid); - QMap cData = {{"state", static_cast(Shared::Message::State::sent)}}; - if (ri != nullptr) - ri->changeMessage(id, cData); - - emit acc->changeMessage(jid, id, cData); - } else { + if (!adjustPendingMessage(id, stateSent, false)) qDebug() << "Encrypted message has been successfully sent, but it couldn't be found to update the sate"; - } } else { qDebug() << "Couldn't sent an encrypted message"; handlePendingMessageError(id, "Error sending successfully encrypted message"); @@ -425,6 +382,19 @@ std::pair Core::MessageHandler::scheduleSending } } +bool Core::MessageHandler::adjustPendingMessage(const QString& messageId, const QMap& data, bool final) { + std::optional info = getOriginalPendingMessageId(messageId, final); + if (info) { + RosterItem* ri = acc->rh->getRosterItem(info->jid); + if (ri != nullptr) + ri->changeMessage(info->messageId, data); + + emit acc->changeMessage(info->jid, info->messageId, data); + return true; + } + + return false; +} QMap Core::MessageHandler::getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const { QMap changes; @@ -481,51 +451,53 @@ QXmppMessage Core::MessageHandler::createPacket(const Shared::Message& data, con } void Core::MessageHandler::prepareUpload(const Shared::Message& data, bool newMessage) { - if (acc->state == Shared::ConnectionState::connected) { - QString jid = data.getPenPalJid(); - QString id = data.getId(); - RosterItem* ri = acc->rh->getRosterItem(jid); - if (ri == nullptr) { - qDebug() << "An attempt to initialize upload in" << acc->name << "for pal" << jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send"; - return; - } - QString path = data.getAttachPath(); - QString url = acc->network->getFileRemoteUrl(path); - if (url.size() != 0) { - sendMessageWithLocalUploadedFile(data, url, newMessage); - } else { - pendingStateMessages.insert(std::make_pair(id, jid)); - if (newMessage) { - ri->appendMessageToArchive(data); - } else { - QMap changes({ - {"state", (uint)Shared::Message::State::pending} - }); - ri->changeMessage(id, changes); - emit acc->changeMessage(jid, id, changes); - } - //this checks if the file is already uploading, and if so it subscribes to it's success, so, i need to do stuff only if the network knows nothing of this file - if (!acc->network->checkAndAddToUploading(acc->getName(), jid, id, path)) { - if (acc->um->serviceFound()) { - QFileInfo file(path); - if (file.exists() && file.isReadable()) { - pendingStateMessages.insert(std::make_pair(id, jid)); - uploadingSlotsQueue.emplace_back(path, id); - if (uploadingSlotsQueue.size() == 1) - acc->um->requestUploadSlot(file); - } else { - handleUploadError(jid, id, "Uploading file no longer exists or your system user has no permission to read it"); - qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but the file doesn't exist or is not readable"; - } - } else { - handleUploadError(jid, id, "Your server doesn't support file upload service, or it's prohibited for your account"); - qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but upload manager didn't discover any upload services"; - } - } - } - } else { + if (acc->state != Shared::ConnectionState::connected) { handleUploadError(data.getPenPalJid(), data.getId(), "Account is offline or reconnecting"); qDebug() << "An attempt to send message with not connected account " << acc->name << ", skipping"; + return; + } + + QString jid = data.getPenPalJid(); + QString id = data.getId(); + RosterItem* ri = acc->rh->getRosterItem(jid); + if (ri == nullptr) { + qDebug() << "An attempt to initialize upload in" << acc->name << "for pal" << jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send"; + return; + } + + QString path = data.getAttachPath(); + QString url = acc->network->getFileRemoteUrl(path); + if (url.size() != 0) + return sendMessageWithLocalUploadedFile(data, url, newMessage); + + pendingStateMessages.insert(std::make_pair(id, jid)); + if (newMessage) { + ri->appendMessageToArchive(data); + } else { + ri->changeMessage(id, statePending); + emit acc->changeMessage(jid, id, statePending); + } + + //this checks if the file is already uploading, and if so it subscribes to it's success, + //So, I need to do stuff only if the network knows nothing of this file + if (acc->network->checkAndAddToUploading(acc->getName(), jid, id, path)) + return; + + if (!acc->um->serviceFound()) { + handleUploadError(jid, id, "Your server doesn't support file upload service, or it's prohibited for your account"); + qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but upload manager didn't discover any upload services"; + return; + } + + QFileInfo file(path); + if (file.exists() && file.isReadable()) { + pendingStateMessages.insert(std::make_pair(id, jid)); + uploadingSlotsQueue.emplace_back(path, id); + if (uploadingSlotsQueue.size() == 1) + acc->um->requestUploadSlot(file); + } else { + handleUploadError(jid, id, "Uploading file no longer exists or your system user has no permission to read it"); + qDebug() << "Requested upload slot in account" << acc->name << "for file" << path << "but the file doesn't exist or is not readable"; } } @@ -565,24 +537,25 @@ void Core::MessageHandler::onDownloadFileComplete(const std::listgetName()) { - RosterItem* cnt = acc->rh->getRosterItem(info.jid); - if (cnt != nullptr) { - bool changed = cnt->changeMessage(info.messageId, cData); - if (changed) - emit acc->changeMessage(info.jid, info.messageId, cData); - } + if (info.account != acc->getName()) + continue; + + RosterItem* cnt = acc->rh->getRosterItem(info.jid); + if (cnt != nullptr) { + bool changed = cnt->changeMessage(info.messageId, cData); + if (changed) + emit acc->changeMessage(info.jid, info.messageId, cData); } } } void Core::MessageHandler::onLoadFileError(const std::list& msgs, const QString& text, bool up) { - if (up) { - for (const Shared::MessageInfo& info : msgs) { - if (info.account == acc->getName()) - handleUploadError(info.jid, info.messageId, text); - } - } + if (!up) + return; + + for (const Shared::MessageInfo& info : msgs) + if (info.account == acc->getName()) + handleUploadError(info.jid, info.messageId, text); } void Core::MessageHandler::handleUploadError(const QString& jid, const QString& messageId, const QString& errorText) { @@ -597,15 +570,16 @@ void Core::MessageHandler::handleUploadError(const QString& jid, const QString& void Core::MessageHandler::onUploadFileComplete(const std::list& msgs, const QString& url, const QString& path) { for (const Shared::MessageInfo& info : msgs) { - if (info.account == acc->getName()) { - RosterItem* ri = acc->rh->getRosterItem(info.jid); - if (ri != nullptr) { - Shared::Message msg = ri->getMessage(info.messageId); - msg.setAttachPath(path); - sendMessageWithLocalUploadedFile(msg, url, false); - } else { - qDebug() << "A signal received about complete upload to" << acc->name << "for pal" << info.jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send"; - } + if (info.account != acc->getName()) + continue; + + RosterItem* ri = acc->rh->getRosterItem(info.jid); + if (ri != nullptr) { + Shared::Message msg = ri->getMessage(info.messageId); + msg.setAttachPath(path); + sendMessageWithLocalUploadedFile(msg, url, false); + } else { + qDebug() << "A signal received about complete upload to" << acc->name << "for pal" << info.jid << "but the object for this pal wasn't found, something went terrebly wrong, skipping send"; } } } @@ -654,7 +628,7 @@ void Core::MessageHandler::resendMessage(const QString& jid, const QString& id) try { Shared::Message msg = cnt->getMessage(id); if (msg.getState() == Shared::Message::State::error) { - if (msg.getEdited()){ + if (msg.getEdited()) { QString originalId = msg.getId(); msg.generateRandomId(); sendMessage(msg, false, originalId); diff --git a/core/handlers/messagehandler.h b/core/handlers/messagehandler.h index e3199dc..15f99bf 100644 --- a/core/handlers/messagehandler.h +++ b/core/handlers/messagehandler.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -71,9 +72,10 @@ private: void handleUploadError(const QString& jid, const QString& messageId, const QString& errorText); QXmppMessage createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const; QMap getChanges(Shared::Message& data, const QDateTime& time, bool newMessage, const QString& originalId) const; - std::tuple getOriginalPendingMessageId(const QString& id, bool clear = true); + std::optional getOriginalPendingMessageId(const QString& id, bool clear = true); bool handlePendingMessageError(const QString& id, const QString& errorText); std::pair scheduleSending(const Shared::Message& message, const QDateTime& sendTime, const QString& originalId); + bool adjustPendingMessage(const QString& messageId, const QMap& data, bool final); private: Account* acc; diff --git a/core/handlers/omemohandler.cpp b/core/handlers/omemohandler.cpp index eaa674f..b50bed8 100644 --- a/core/handlers/omemohandler.cpp +++ b/core/handlers/omemohandler.cpp @@ -54,22 +54,20 @@ QXmppTask Core::OmemoHandler::allData() { OmemoData data; data.ownDevice = ownDevice; - // LMDBAL::Transaction txn = db.beginReadOnlyTransaction(); TODO need to enable transaction after fixing LMDBAL - std::map pkeys = preKeyPairs->readAll(); - for (const std::pair& pair : pkeys) { + LMDBAL::Transaction txn = db.beginReadOnlyTransaction(); + std::map pkeys = preKeyPairs->readAll(txn); + for (const std::pair& pair : pkeys) data.preKeyPairs.insert(pair.first, pair.second); - } - std::map spre = signedPreKeyPairs->readAll(); + std::map spre = signedPreKeyPairs->readAll(txn); for (const std::pair& pair : spre) { QXmppOmemoStorage::SignedPreKeyPair qxpair = {pair.second.first, pair.second.second}; data.signedPreKeyPairs.insert(pair.first, qxpair); } - std::map> devs = devices->readAll(); - for (const std::pair>& pair : devs) { + std::map> devs = devices->readAll(txn); + for (const std::pair>& pair : devs) data.devices.insert(pair.first, pair.second); - } return Core::makeReadyTask(std::move(data)); } diff --git a/core/handlers/rosterhandler.cpp b/core/handlers/rosterhandler.cpp index 38425ba..940ddbf 100644 --- a/core/handlers/rosterhandler.cpp +++ b/core/handlers/rosterhandler.cpp @@ -60,7 +60,6 @@ void Core::RosterHandler::clear() { conferences.clear(); } - void Core::RosterHandler::onRosterReceived() { QStringList bj = acc->rm->getRosterBareJids(); for (int i = 0; i < bj.size(); ++i) { @@ -260,7 +259,6 @@ void Core::RosterHandler::onTrustChanged(const QString& jid, const Shared::Trust emit acc->changeContact(jid, {{"trust", QVariant::fromValue(trust)}}); } - void Core::RosterHandler::addToGroup(const QString& jid, const QString& group) { std::map>::iterator gItr = groups.find(group); if (gItr == groups.end()) { @@ -288,7 +286,7 @@ void Core::RosterHandler::removeFromGroup(const QString& jid, const QString& gro } } -Core::RosterItem * Core::RosterHandler::getRosterItem(const QString& jid) { +Core::RosterItem* Core::RosterHandler::getRosterItem(const QString& jid) { RosterItem* item = nullptr; QString lcJid = jid.toLower(); std::map::const_iterator citr = contacts.find(lcJid); @@ -302,7 +300,7 @@ Core::RosterItem * Core::RosterHandler::getRosterItem(const QString& jid) { return item; } -Core::Conference * Core::RosterHandler::getConference(const QString& jid) { +Core::Conference* Core::RosterHandler::getConference(const QString& jid) { Conference* item = 0; std::map::const_iterator coitr = conferences.find(jid.toLower()); if (coitr != conferences.end()) @@ -311,7 +309,7 @@ Core::Conference * Core::RosterHandler::getConference(const QString& jid) { return item; } -Core::Contact * Core::RosterHandler::getContact(const QString& jid) { +Core::Contact* Core::RosterHandler::getContact(const QString& jid) { Contact* item = 0; std::map::const_iterator citr = contacts.find(jid.toLower()); if (citr != contacts.end()) @@ -320,7 +318,7 @@ Core::Contact * Core::RosterHandler::getContact(const QString& jid) { return item; } -Core::Contact * Core::RosterHandler::addOutOfRosterContact(const QString& jid) { +Core::Contact* Core::RosterHandler::addOutOfRosterContact(const QString& jid) { QString lcJid = jid.toLower(); Contact* cnt = new Contact(lcJid, acc->name); contacts.insert(std::make_pair(lcJid, cnt)); @@ -446,9 +444,9 @@ void Core::RosterHandler::clearConferences() { void Core::RosterHandler::removeRoomRequest(const QString& jid) { QString lcJid = jid.toLower(); std::map::const_iterator itr = conferences.find(lcJid); - if (itr == conferences.end()) { + if (itr == conferences.end()) qDebug() << "An attempt to remove non existing room" << lcJid << "from account" << acc->name << ", skipping"; - } + itr->second->deleteLater(); conferences.erase(itr); emit acc->removeRoom(lcJid); diff --git a/core/handlers/rosterhandler.h b/core/handlers/rosterhandler.h index 1f8e480..5abc416 100644 --- a/core/handlers/rosterhandler.h +++ b/core/handlers/rosterhandler.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef CORE_ROSTERHANDLER_H -#define CORE_ROSTERHANDLER_H +#pragma once #include #include @@ -27,6 +26,7 @@ #include #include #include +#include #include #include @@ -45,8 +45,7 @@ namespace Core { class Account; -class RosterHandler : public QObject -{ +class RosterHandler : public QObject { Q_OBJECT public: RosterHandler(Account* account); @@ -119,5 +118,3 @@ private: }; } - -#endif // CORE_ROSTERHANDLER_H diff --git a/shared/messageinfo.h b/shared/messageinfo.h index 942d88c..3cf75bc 100644 --- a/shared/messageinfo.h +++ b/shared/messageinfo.h @@ -16,16 +16,11 @@ * along with this program. If not, see . */ -#ifndef SHARED_MESSAGEINFO_H -#define SHARED_MESSAGEINFO_H +#pragma once #include namespace Shared { - -/** - * @todo write docs - */ struct MessageInfo { MessageInfo(); MessageInfo(const QString& acc, const QString& j, const QString& id); @@ -39,5 +34,3 @@ struct MessageInfo { }; } - -#endif // SHARED_MESSAGEINFO_H From 8f5325b291bffb63a738bc6e79d07af4e407ee10 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 16 Nov 2023 21:08:40 -0300 Subject: [PATCH 118/137] beginning of keys setting --- core/account.cpp | 8 + core/account.h | 6 +- core/handlers/rosterhandler.h | 2 - shared/info.cpp | 525 +++++++++++++++++--------------- shared/info.h | 41 +-- ui/models/info/omemo/keys.cpp | 62 ++-- ui/models/info/omemo/keys.h | 48 ++- ui/widgets/info/info.cpp | 6 +- ui/widgets/info/info.h | 3 +- ui/widgets/info/omemo/omemo.cpp | 6 + ui/widgets/info/omemo/omemo.h | 1 + 11 files changed, 385 insertions(+), 323 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index efb4a1a..858c177 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -806,6 +806,14 @@ void Core::Account::updateInfo(const Shared::Info& info) { //TODO switch case of what kind of entity this info update is about //right now it could be only about myself vh->uploadVCard(info.getVCardRef()); + const std::list& keys = info.getActiveKeysRef(); + for (const Shared::KeyInfo& info : keys) { + qDebug() << "An attempt to save key: "; + qDebug() << "id:" << info.id; + qDebug() << "label:" << info.label; + qDebug() << "current device:" << info.currentDevice; + qDebug() << "... but it's not implemented yet, ignoring"; + } } QString Core::Account::getAvatarPath() const { diff --git a/core/account.h b/core/account.h index 47c5c7e..ea1a13d 100644 --- a/core/account.h +++ b/core/account.h @@ -67,11 +67,9 @@ #include "handlers/omemohandler.h" #endif -namespace Core -{ +namespace Core { -class Account : public QObject -{ +class Account : public QObject { Q_OBJECT friend class MessageHandler; friend class RosterHandler; diff --git a/core/handlers/rosterhandler.h b/core/handlers/rosterhandler.h index 5abc416..afbd372 100644 --- a/core/handlers/rosterhandler.h +++ b/core/handlers/rosterhandler.h @@ -41,8 +41,6 @@ #include namespace Core { - - class Account; class RosterHandler : public QObject { diff --git a/shared/info.cpp b/shared/info.cpp index 36cd4b5..35f6427 100644 --- a/shared/info.cpp +++ b/shared/info.cpp @@ -1,353 +1,384 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "info.h" -Shared::Info::Info(const QString& addr, EntryType tp): - type(tp), - address(addr), - vcard(nullptr), - activeKeys(nullptr), - inactiveKeys(nullptr) -{ +Shared::Info::Info(const QString &addr, EntryType tp): +type(tp), +address(addr), +vcard(nullptr), +activeKeys(nullptr), +inactiveKeys(nullptr) { switch (type) { - case EntryType::none: - break; - case EntryType::contact: - case EntryType::ownAccount: - vcard = new VCard(); - activeKeys = new std::list(); - inactiveKeys = new std::list(); - break; - default: - throw 352; + case EntryType::none: + break; + case EntryType::contact: + case EntryType::ownAccount: + vcard = new VCard(); + activeKeys = new std::list(); + inactiveKeys = new std::list(); + break; + default: + throw 352; } } Shared::Info::Info(): - type(EntryType::none), - address(""), - vcard(nullptr), - activeKeys(nullptr), - inactiveKeys(nullptr) -{} +type(EntryType::none), +address(""), +vcard(nullptr), +activeKeys(nullptr), +inactiveKeys(nullptr) {} -Shared::Info::Info(const Shared::Info& other): - type(other.type), - address(other.address), - vcard(nullptr), - activeKeys(nullptr), - inactiveKeys(nullptr) -{ +Shared::Info::Info(const Shared::Info &other): +type(other.type), +address(other.address), +vcard(nullptr), +activeKeys(nullptr), +inactiveKeys(nullptr) { switch (type) { - case EntryType::none: - break; - case EntryType::contact: - case EntryType::ownAccount: - vcard = new VCard(other.getVCardRef()); - activeKeys = new std::list(other.getActiveKeysRef()); - inactiveKeys = new std::list(other.getInactiveKeysRef()); - break; - default: - throw 353; + case EntryType::none: + break; + case EntryType::contact: + case EntryType::ownAccount: + vcard = new VCard(other.getVCardRef()); + activeKeys = new std::list(other.getActiveKeysRef()); + inactiveKeys = new std::list(other.getInactiveKeysRef()); + break; + default: + throw 353; } } +Shared::Info::Info(Info &&other): +type(other.type), +address(other.address), +vcard(other.vcard), +activeKeys(other.activeKeys), +inactiveKeys(other.inactiveKeys) { + other.type = EntryType::none; +} + +Shared::Info &Shared::Info::operator=(Info &&other) { + type = other.type; + address = other.address; + vcard = other.vcard; + activeKeys = other.activeKeys; + inactiveKeys = other.inactiveKeys; + + other.type = EntryType::none; + + return *this; +} + +Shared::Info &Shared::Info::operator=(const Info &other) { + type = other.type; + address = other.address; + + switch (type) { + case EntryType::none: + break; + case EntryType::contact: + case EntryType::ownAccount: + vcard = new VCard(other.getVCardRef()); + activeKeys = new std::list(other.getActiveKeysRef()); + inactiveKeys = new std::list(other.getInactiveKeysRef()); + break; + default: + throw 351; + } + + return *this; +} + Shared::Info::~Info() { turnIntoNone(); } void Shared::Info::turnIntoNone() { switch (type) { - case EntryType::none: - break; - case EntryType::contact: - case EntryType::ownAccount: - delete vcard; - vcard = nullptr; - delete activeKeys; - activeKeys = nullptr; - delete inactiveKeys; - inactiveKeys = nullptr; - break; - default: - break; + case EntryType::none: + break; + case EntryType::contact: + case EntryType::ownAccount: + delete vcard; + vcard = nullptr; + delete activeKeys; + activeKeys = nullptr; + delete inactiveKeys; + inactiveKeys = nullptr; + break; + default: + break; } type = EntryType::none; } void Shared::Info::turnIntoContact( - const Shared::VCard& crd, - const std::list& aks, - const std::list& iaks + const Shared::VCard &crd, const std::list &aks, const std::list &iaks ) { switch (type) { - case EntryType::none: - vcard = new VCard(crd); - activeKeys = new std::list(aks); - inactiveKeys = new std::list(iaks); - break; - case EntryType::contact: - case EntryType::ownAccount: - *vcard = crd; - *activeKeys = aks; - *inactiveKeys = iaks; - break; - default: - break; + case EntryType::none: + vcard = new VCard(crd); + activeKeys = new std::list(aks); + inactiveKeys = new std::list(iaks); + break; + case EntryType::contact: + case EntryType::ownAccount: + *vcard = crd; + *activeKeys = aks; + *inactiveKeys = iaks; + break; + default: + break; } type = EntryType::contact; } -void Shared::Info::turnIntoContact( - Shared::VCard* crd, - std::list* aks, - std::list* iaks -) { +void Shared::Info::turnIntoContact(Shared::VCard *crd, std::list *aks, std::list *iaks) { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - delete vcard; - delete activeKeys; - delete inactiveKeys; - [[fallthrough]]; - case EntryType::none: - vcard = crd; - activeKeys = aks; - inactiveKeys = iaks; - break; - default: - break; + case EntryType::contact: + case EntryType::ownAccount: + delete vcard; + delete activeKeys; + delete inactiveKeys; + [[fallthrough]]; + case EntryType::none: + vcard = crd; + activeKeys = aks; + inactiveKeys = iaks; + break; + default: + break; } type = EntryType::contact; } void Shared::Info::turnIntoOwnAccount( - const Shared::VCard& crd, - const std::list& aks, - const std::list& iaks -) { - switch (type) { - case EntryType::none: - vcard = new VCard(crd); - activeKeys = new std::list(aks); - inactiveKeys = new std::list(iaks); - break; - case EntryType::contact: - case EntryType::ownAccount: - *vcard = crd; - *activeKeys = aks; - *inactiveKeys = iaks; - break; - default: - break; - } - - type = EntryType::ownAccount; -} - -void Shared::Info::turnIntoOwnAccount( - Shared::VCard* crd, - std::list* aks, - std::list* iaks + const Shared::VCard &crd, const std::list &aks, const std::list &iaks ) { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - delete vcard; - delete activeKeys; - delete inactiveKeys; - [[fallthrough]]; - case EntryType::none: - vcard = crd; - activeKeys = aks; - inactiveKeys = iaks; - break; - default: - break; + case EntryType::none: + vcard = new VCard(crd); + activeKeys = new std::list(aks); + inactiveKeys = new std::list(iaks); + break; + case EntryType::contact: + case EntryType::ownAccount: + *vcard = crd; + *activeKeys = aks; + *inactiveKeys = iaks; + break; + default: + break; } type = EntryType::ownAccount; } -void Shared::Info::setAddress(const QString& addr) { - address = addr;} +void Shared::Info::turnIntoOwnAccount(Shared::VCard *crd, std::list *aks, std::list *iaks) { + switch (type) { + case EntryType::contact: + case EntryType::ownAccount: + delete vcard; + delete activeKeys; + delete inactiveKeys; + [[fallthrough]]; + case EntryType::none: + vcard = crd; + activeKeys = aks; + inactiveKeys = iaks; + break; + default: + break; + } + + type = EntryType::ownAccount; +} + +void Shared::Info::setAddress(const QString &addr) { + address = addr; +} QString Shared::Info::getAddress() const { - return address;} + return address; +} -const QString& Shared::Info::getAddressRef() const { - return address;} +const QString &Shared::Info::getAddressRef() const { + return address; +} Shared::EntryType Shared::Info::getType() const { - return type;} + return type; +} -std::list & Shared::Info::getActiveKeysRef() { +std::list &Shared::Info::getActiveKeysRef() { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return *activeKeys; - break; - default: - throw 354; + case EntryType::contact: + case EntryType::ownAccount: + return *activeKeys; + break; + default: + throw 354; } } -const std::list & Shared::Info::getActiveKeysRef() const { +const std::list &Shared::Info::getActiveKeysRef() const { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return *activeKeys; - break; - default: - throw 355; + case EntryType::contact: + case EntryType::ownAccount: + return *activeKeys; + break; + default: + throw 355; } } -std::list* Shared::Info::getActiveKeys() { +std::list *Shared::Info::getActiveKeys() { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return activeKeys; - break; - default: - throw 356; + case EntryType::contact: + case EntryType::ownAccount: + return activeKeys; + break; + default: + throw 356; } } -const std::list* Shared::Info::getActiveKeys() const { +const std::list *Shared::Info::getActiveKeys() const { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return activeKeys; - break; - default: - throw 357; + case EntryType::contact: + case EntryType::ownAccount: + return activeKeys; + break; + default: + throw 357; } } -std::list & Shared::Info::getInactiveKeysRef() { +std::list &Shared::Info::getInactiveKeysRef() { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return *inactiveKeys; - break; - default: - throw 358; + case EntryType::contact: + case EntryType::ownAccount: + return *inactiveKeys; + break; + default: + throw 358; } } -const std::list & Shared::Info::getInactiveKeysRef() const { +const std::list &Shared::Info::getInactiveKeysRef() const { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return *inactiveKeys; - break; - default: - throw 359; + case EntryType::contact: + case EntryType::ownAccount: + return *inactiveKeys; + break; + default: + throw 359; } } -std::list* Shared::Info::getInactiveKeys() { +std::list *Shared::Info::getInactiveKeys() { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return inactiveKeys; - break; - default: - throw 360; + case EntryType::contact: + case EntryType::ownAccount: + return inactiveKeys; + break; + default: + throw 360; } } -const std::list* Shared::Info::getInactiveKeys() const { +const std::list *Shared::Info::getInactiveKeys() const { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return inactiveKeys; - break; - default: - throw 361; + case EntryType::contact: + case EntryType::ownAccount: + return inactiveKeys; + break; + default: + throw 361; } } -const Shared::VCard & Shared::Info::getVCardRef() const { +const Shared::VCard &Shared::Info::getVCardRef() const { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return *vcard; - break; - default: - throw 362; + case EntryType::contact: + case EntryType::ownAccount: + return *vcard; + break; + default: + throw 362; } } -Shared::VCard & Shared::Info::getVCardRef() { +Shared::VCard &Shared::Info::getVCardRef() { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return *vcard; - break; - default: - throw 363; + case EntryType::contact: + case EntryType::ownAccount: + return *vcard; + break; + default: + throw 363; } } -const Shared::VCard * Shared::Info::getVCard() const { +const Shared::VCard *Shared::Info::getVCard() const { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return vcard; - break; - default: - throw 364; + case EntryType::contact: + case EntryType::ownAccount: + return vcard; + break; + default: + throw 364; } } -Shared::VCard * Shared::Info::getVCard() { +Shared::VCard *Shared::Info::getVCard() { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return vcard; - break; - default: - throw 365; + case EntryType::contact: + case EntryType::ownAccount: + return vcard; + break; + default: + throw 365; } } -void Shared::Info::setActiveKeys(std::list* keys) { +void Shared::Info::setActiveKeys(std::list *keys) { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - activeKeys = keys; - break; - default: - throw 366; + case EntryType::contact: + case EntryType::ownAccount: + activeKeys = keys; + break; + default: + throw 366; } } -void Shared::Info::setVCard(Shared::VCard* card) { +void Shared::Info::setVCard(Shared::VCard *card) { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - vcard = card; - break; - default: - throw 367; + case EntryType::contact: + case EntryType::ownAccount: + vcard = card; + break; + default: + throw 367; } } - diff --git a/shared/info.h b/shared/info.h index e42eddb..d5a48b1 100644 --- a/shared/info.h +++ b/shared/info.h @@ -1,21 +1,22 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 SHARED_INFO_H -#define SHARED_INFO_H +#pragma once #include "vcard.h" #include "keyinfo.h" @@ -36,7 +37,10 @@ public: Info(); Info(const QString& address, EntryType = EntryType::none); Info(const Info& other); + Info(Info&& other); virtual ~Info(); + Info& operator = (const Info& other); + Info& operator = (Info&& other); QString getAddress() const; const QString& getAddressRef() const; @@ -90,7 +94,4 @@ private: std::list* activeKeys; std::list* inactiveKeys; }; - } - -#endif // SHARED_INFO_H diff --git a/ui/models/info/omemo/keys.cpp b/ui/models/info/omemo/keys.cpp index 5d957b1..9ef8c73 100644 --- a/ui/models/info/omemo/keys.cpp +++ b/ui/models/info/omemo/keys.cpp @@ -1,18 +1,20 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "keys.h" @@ -37,18 +39,34 @@ Models::Keys::~Keys() { delete pair.second; } -std::deque Models::Keys::modifiedKeys() const { - std::deque response(modified.size()); +std::list Models::Keys::modifiedKeys() const { + std::list response; + for (const std::pair& pair: modified) + response.push_back(*(pair.second)); - int i = 0; - for (const std::pair& pair: modified) { - response[i] = *(pair.second); - ++i; - } return response; } +std::list Models::Keys::finalKeys() const { + std::list result; + finalKeys(result); + return result; +} + +void Models::Keys::finalKeys(std::list& out) const { + for (int i = 0; i < rowCount(); ++i) + out.push_back(key(i)); +} + +const Shared::KeyInfo & Models::Keys::key(unsigned int i) const { + std::map::const_iterator itr = modified.find(i); + if (itr != modified.end()) + return*(itr->second); + else + return *(keys[i]); +} + void Models::Keys::addKey(const Shared::KeyInfo& info) { beginInsertRows(QModelIndex(), keys.size(), keys.size()); keys.push_back(new Shared::KeyInfo(info)); diff --git a/ui/models/info/omemo/keys.h b/ui/models/info/omemo/keys.h index 49948a2..1bc79de 100644 --- a/ui/models/info/omemo/keys.h +++ b/ui/models/info/omemo/keys.h @@ -1,21 +1,22 @@ -// Squawk messenger. -// Copyright (C) 2019 Yury Gubich -// -// 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 . +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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_KEYS_H -#define MODELS_KEYS_H +#pragma once #include @@ -23,11 +24,7 @@ namespace Models { -/** - * @todo write docs - */ -class Keys : public QAbstractListModel -{ +class Keys : public QAbstractListModel { public: Keys(QObject *parent = nullptr); ~Keys(); @@ -37,11 +34,14 @@ public: QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; + const Shared::KeyInfo& key(unsigned int i) const; QHash roleNames() const override; QModelIndex index(int row, int column, const QModelIndex & parent) const override; - std::deque modifiedKeys() const; + std::list modifiedKeys() const; + std::list finalKeys() const; + void finalKeys(std::list& out) const; enum Roles { Label = Qt::UserRole + 1, @@ -64,5 +64,3 @@ private: }; } - -#endif // MODELS_KEYS_H diff --git a/ui/widgets/info/info.cpp b/ui/widgets/info/info.cpp index 948ee27..26f63f1 100644 --- a/ui/widgets/info/info.cpp +++ b/ui/widgets/info/info.cpp @@ -114,6 +114,9 @@ void UI::Info::onButtonBoxAccepted() { contactGeneral->fillVCard(card); contactContacts->fillVCard(card); card.setDescription(description->description()); +#ifdef WITH_OMEMO + omemo->fillData(info.getActiveKeysRef()); +#endif emit saveInfo(info); emit close(); } @@ -161,7 +164,8 @@ void UI::Info::initializeDescription(const QString& descr, bool editable) { } QString UI::Info::getJid() const { - return jid;} + return jid; +} void UI::Info::clear() { if (contactGeneral != nullptr) { diff --git a/ui/widgets/info/info.h b/ui/widgets/info/info.h index 7d52693..10b7209 100644 --- a/ui/widgets/info/info.h +++ b/ui/widgets/info/info.h @@ -39,8 +39,7 @@ namespace UI { -namespace Ui -{ +namespace Ui { class Info; } diff --git a/ui/widgets/info/omemo/omemo.cpp b/ui/widgets/info/omemo/omemo.cpp index 294fbe2..baa7aa0 100644 --- a/ui/widgets/info/omemo/omemo.cpp +++ b/ui/widgets/info/omemo/omemo.cpp @@ -88,6 +88,12 @@ void UI::Omemo::setData(const std::list& keys) { deviceKeyVisibility(deviceKeyModel.rowCount() > 0); } +void UI::Omemo::fillData(std::list& out) { + deviceKeyModel.finalKeys(out); + keysModel.finalKeys(out); + unusedKeysModel.finalKeys(out); +} + const QString UI::Omemo::title() const { return m_ui->OMEMOHeading->text(); } diff --git a/ui/widgets/info/omemo/omemo.h b/ui/widgets/info/omemo/omemo.h index 75b1df2..64c2486 100644 --- a/ui/widgets/info/omemo/omemo.h +++ b/ui/widgets/info/omemo/omemo.h @@ -41,6 +41,7 @@ public: ~Omemo(); void setData(const std::list& keys); + void fillData(std::list& out); const QString title() const; private slots: From 93c5be412ea6130971d8ede1e9bb7e6ba65929cc Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 17 Nov 2023 21:52:33 -0300 Subject: [PATCH 119/137] trying linter settings --- .uncrustify.cfg | 3640 +++++++++++++++++++++++++++++++++++++++++++++++ shared/info.cpp | 475 +++---- shared/info.h | 82 +- 3 files changed, 3903 insertions(+), 294 deletions(-) create mode 100644 .uncrustify.cfg diff --git a/.uncrustify.cfg b/.uncrustify.cfg new file mode 100644 index 0000000..c333a42 --- /dev/null +++ b/.uncrustify.cfg @@ -0,0 +1,3640 @@ +# Uncrustify_d-0.77.1_f + +# +# General options +# + +# The type of line endings. +# +# Default: auto +newlines = lf # lf/crlf/cr/auto + +# The original size of tabs in the input. +# +# Default: 8 +input_tab_size = 8 # unsigned number + +# The size of tabs in the output (only used if align_with_tabs=true). +# +# Default: 8 +output_tab_size = 8 # unsigned number + +# The ASCII value of the string escape char, usually 92 (\) or (Pawn) 94 (^). +# +# Default: 92 +string_escape_char = 92 # unsigned number + +# Alternate string escape char (usually only used for Pawn). +# Only works right before the quote char. +string_escape_char2 = 0 # unsigned number + +# Replace tab characters found in string literals with the escape sequence \t +# instead. +string_replace_tab_chars = false # true/false + +# Allow interpreting '>=' and '>>=' as part of a template in code like +# 'void f(list>=val);'. If true, 'assert(x<0 && y>=3)' will be broken. +# Improvements to template detection may make this option obsolete. +tok_split_gte = false # true/false + +# Disable formatting of NL_CONT ('\\n') ended lines (e.g. multi-line macros). +disable_processing_nl_cont = false # true/false + +# Specify the marker used in comments to disable processing of part of the +# file. +# +# Default: *INDENT-OFF* +disable_processing_cmt = " *INDENT-OFF*" # string + +# Specify the marker used in comments to (re)enable processing in a file. +# +# Default: *INDENT-ON* +enable_processing_cmt = " *INDENT-ON*" # string + +# Enable parsing of digraphs. +enable_digraphs = false # true/false + +# Option to allow both disable_processing_cmt and enable_processing_cmt +# strings, if specified, to be interpreted as ECMAScript regular expressions. +# If true, a regex search will be performed within comments according to the +# specified patterns in order to disable/enable processing. +processing_cmt_as_regex = false # true/false + +# Add or remove the UTF-8 BOM (recommend 'remove'). +utf8_bom = ignore # ignore/add/remove/force/not_defined + +# If the file contains bytes with values between 128 and 255, but is not +# UTF-8, then output as UTF-8. +utf8_byte = false # true/false + +# Force the output encoding to UTF-8. +utf8_force = false # true/false + +# +# Spacing options +# + +# Add or remove space around non-assignment symbolic operators ('+', '/', '%', +# '<<', and so forth). +sp_arith = force # ignore/add/remove/force/not_defined + +# Add or remove space around arithmetic operators '+' and '-'. +# +# Overrides sp_arith. +sp_arith_additive = force # ignore/add/remove/force/not_defined + +# Add or remove space around assignment operator '=', '+=', etc. +sp_assign = force # ignore/add/remove/force/not_defined + +# Add or remove space around '=' in C++11 lambda capture specifications. +# +# Overrides sp_assign. +sp_cpp_lambda_assign = remove # ignore/add/remove/force/not_defined + +# Add or remove space after the capture specification of a C++11 lambda when +# an argument list is present, as in '[] (int x){ ... }'. +sp_cpp_lambda_square_paren = force # ignore/add/remove/force/not_defined + +# Add or remove space after the capture specification of a C++11 lambda with +# no argument list is present, as in '[] { ... }'. +sp_cpp_lambda_square_brace = force # ignore/add/remove/force/not_defined + +# Add or remove space after the opening parenthesis and before the closing +# parenthesis of a argument list of a C++11 lambda, as in +# '[]( int x ){ ... }'. +sp_cpp_lambda_argument_list = remove # ignore/add/remove/force/not_defined + +# Add or remove space after the argument list of a C++11 lambda, as in +# '[](int x) { ... }'. +sp_cpp_lambda_paren_brace = force # ignore/add/remove/force/not_defined + +# Add or remove space between a lambda body and its call operator of an +# immediately invoked lambda, as in '[]( ... ){ ... } ( ... )'. +sp_cpp_lambda_fparen = remove # ignore/add/remove/force/not_defined + +# Add or remove space around assignment operator '=' in a prototype. +# +# If set to ignore, use sp_assign. +sp_assign_default = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before assignment operator '=', '+=', etc. +# +# Overrides sp_assign. +sp_before_assign = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after assignment operator '=', '+=', etc. +# +# Overrides sp_assign. +sp_after_assign = ignore # ignore/add/remove/force/not_defined + +# Add or remove space in 'enum {'. +# +# Default: add +sp_enum_brace = force # ignore/add/remove/force/not_defined + +# Add or remove space in 'NS_ENUM ('. +sp_enum_paren = force # ignore/add/remove/force/not_defined + +# Add or remove space around assignment '=' in enum. +sp_enum_assign = force # ignore/add/remove/force/not_defined + +# Add or remove space before assignment '=' in enum. +# +# Overrides sp_enum_assign. +sp_enum_before_assign = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after assignment '=' in enum. +# +# Overrides sp_enum_assign. +sp_enum_after_assign = ignore # ignore/add/remove/force/not_defined + +# Add or remove space around assignment ':' in enum. +sp_enum_colon = add # ignore/add/remove/force/not_defined + +# Add or remove space around preprocessor '##' concatenation operator. +# +# Default: add +sp_pp_concat = add # ignore/add/remove/force/not_defined + +# Add or remove space after preprocessor '#' stringify operator. +# Also affects the '#@' charizing operator. +sp_pp_stringify = remove # ignore/add/remove/force/not_defined + +# Add or remove space before preprocessor '#' stringify operator +# as in '#define x(y) L#y'. +sp_before_pp_stringify = ignore # ignore/add/remove/force/not_defined + +# Add or remove space around boolean operators '&&' and '||'. +sp_bool = force # ignore/add/remove/force/not_defined + +# Add or remove space around compare operator '<', '>', '==', etc. +sp_compare = force # ignore/add/remove/force/not_defined + +# Add or remove space inside '(' and ')'. +sp_inside_paren = remove # ignore/add/remove/force/not_defined + +# Add or remove space between nested parentheses, i.e. '((' vs. ') )'. +sp_paren_paren = remove # ignore/add/remove/force/not_defined + +# Add or remove space between back-to-back parentheses, i.e. ')(' vs. ') ('. +sp_cparen_oparen = remove # ignore/add/remove/force/not_defined + +# Whether to balance spaces inside nested parentheses. +sp_balance_nested_parens = false # true/false + +# Add or remove space between ')' and '{'. +sp_paren_brace = force # ignore/add/remove/force/not_defined + +# Add or remove space between nested braces, i.e. '{{' vs. '{ {'. +sp_brace_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove space before pointer star '*'. +sp_before_ptr_star = remove # ignore/add/remove/force/not_defined + +# Add or remove space before pointer star '*' that isn't followed by a +# variable name. If set to ignore, sp_before_ptr_star is used instead. +sp_before_unnamed_ptr_star = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a qualifier and a pointer star '*' that isn't +# followed by a variable name, as in '(char const *)'. If set to ignore, +# sp_before_ptr_star is used instead. +sp_qualifier_unnamed_ptr_star = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between pointer stars '*', as in 'int ***a;'. +sp_between_ptr_star = remove # ignore/add/remove/force/not_defined + +# Add or remove space after pointer star '*', if followed by a word. +# +# Overrides sp_type_func. +sp_after_ptr_star = force # ignore/add/remove/force/not_defined + +# Add or remove space after pointer caret '^', if followed by a word. +sp_after_ptr_block_caret = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after pointer star '*', if followed by a qualifier. +sp_after_ptr_star_qualifier = force # ignore/add/remove/force/not_defined + +# Add or remove space after a pointer star '*', if followed by a function +# prototype or function definition. +# +# Overrides sp_after_ptr_star and sp_type_func. +sp_after_ptr_star_func = force # ignore/add/remove/force/not_defined + +# Add or remove space after a pointer star '*' in the trailing return of a +# function prototype or function definition. +sp_after_ptr_star_trailing = force # ignore/add/remove/force/not_defined + +# Add or remove space between the pointer star '*' and the name of the variable +# in a function pointer definition. +sp_ptr_star_func_var = force # ignore/add/remove/force/not_defined + +# Add or remove space between the pointer star '*' and the name of the type +# in a function pointer type definition. +sp_ptr_star_func_type = force # ignore/add/remove/force/not_defined + +# Add or remove space after a pointer star '*', if followed by an open +# parenthesis, as in 'void* (*)()'. +sp_ptr_star_paren = force # ignore/add/remove/force/not_defined + +# Add or remove space before a pointer star '*', if followed by a function +# prototype or function definition. If set to ignore, sp_before_ptr_star is +# used instead. +sp_before_ptr_star_func = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a qualifier and a pointer star '*' followed by +# the name of the function in a function prototype or definition, as in +# 'char const *foo()`. If set to ignore, sp_before_ptr_star is used instead. +sp_qualifier_ptr_star_func = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before a pointer star '*' in the trailing return of a +# function prototype or function definition. +sp_before_ptr_star_trailing = remove # ignore/add/remove/force/not_defined + +# Add or remove space between a qualifier and a pointer star '*' in the +# trailing return of a function prototype or function definition, as in +# 'auto foo() -> char const *'. +sp_qualifier_ptr_star_trailing = remove # ignore/add/remove/force/not_defined + +# Add or remove space before a reference sign '&'. +sp_before_byref = remove # ignore/add/remove/force/not_defined + +# Add or remove space before a reference sign '&' that isn't followed by a +# variable name. If set to ignore, sp_before_byref is used instead. +sp_before_unnamed_byref = remove # ignore/add/remove/force/not_defined + +# Add or remove space after reference sign '&', if followed by a word. +# +# Overrides sp_type_func. +sp_after_byref = force # ignore/add/remove/force/not_defined + +# Add or remove space after a reference sign '&', if followed by a function +# prototype or function definition. +# +# Overrides sp_after_byref and sp_type_func. +sp_after_byref_func = force # ignore/add/remove/force/not_defined + +# Add or remove space before a reference sign '&', if followed by a function +# prototype or function definition. +sp_before_byref_func = remove # ignore/add/remove/force/not_defined + +# Add or remove space after a reference sign '&', if followed by an open +# parenthesis, as in 'char& (*)()'. +sp_byref_paren = force # ignore/add/remove/force/not_defined + +# Add or remove space between type and word. In cases where total removal of +# whitespace would be a syntax error, a value of 'remove' is treated the same +# as 'force'. +# +# This also affects some other instances of space following a type that are +# not covered by other options; for example, between the return type and +# parenthesis of a function type template argument, between the type and +# parenthesis of an array parameter, or between 'decltype(...)' and the +# following word. +# +# Default: force +sp_after_type = force # ignore/add/remove/force/not_defined + +# Add or remove space between 'decltype(...)' and word, +# brace or function call. +sp_after_decltype = ignore # ignore/add/remove/force/not_defined + +# (D) Add or remove space before the parenthesis in the D constructs +# 'template Foo(' and 'class Foo('. +sp_before_template_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'template' and '<'. +# If set to ignore, sp_before_angle is used. +sp_template_angle = force # ignore/add/remove/force/not_defined + +# Add or remove space before '<'. +sp_before_angle = remove # ignore/add/remove/force/not_defined + +# Add or remove space inside '<' and '>'. +sp_inside_angle = remove # ignore/add/remove/force/not_defined + +# Add or remove space inside '<>'. +sp_inside_angle_empty = remove # ignore/add/remove/force/not_defined + +# Add or remove space between '>' and ':'. +sp_angle_colon = remove # ignore/add/remove/force/not_defined + +# Add or remove space after '>'. +sp_after_angle = force # ignore/add/remove/force/not_defined + +# Add or remove space between '>' and '(' as found in 'new List(foo);'. +sp_angle_paren = remove # ignore/add/remove/force/not_defined + +# Add or remove space between '>' and '()' as found in 'new List();'. +sp_angle_paren_empty = remove # ignore/add/remove/force/not_defined + +# Add or remove space between '>' and a word as in 'List m;' or +# 'template static ...'. +sp_angle_word = force # ignore/add/remove/force/not_defined + +# Add or remove space between '>' and '>' in '>>' (template stuff). +# +# Default: add +sp_angle_shift = remove # ignore/add/remove/force/not_defined + +# (C++11) Permit removal of the space between '>>' in 'foo >'. Note +# that sp_angle_shift cannot remove the space without this option. +sp_permit_cpp11_shift = true # true/false + +# Add or remove space before '(' of control statements ('if', 'for', 'switch', +# 'while', etc.). +sp_before_sparen = force # ignore/add/remove/force/not_defined + +# Add or remove space inside '(' and ')' of control statements other than +# 'for'. +sp_inside_sparen = remove # ignore/add/remove/force/not_defined + +# Add or remove space after '(' of control statements other than 'for'. +# +# Overrides sp_inside_sparen. +sp_inside_sparen_open = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before ')' of control statements other than 'for'. +# +# Overrides sp_inside_sparen. +sp_inside_sparen_close = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside '(' and ')' of 'for' statements. +sp_inside_for = remove # ignore/add/remove/force/not_defined + +# Add or remove space after '(' of 'for' statements. +# +# Overrides sp_inside_for. +sp_inside_for_open = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before ')' of 'for' statements. +# +# Overrides sp_inside_for. +sp_inside_for_close = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between '((' or '))' of control statements. +sp_sparen_paren = remove # ignore/add/remove/force/not_defined + +# Add or remove space after ')' of control statements. +sp_after_sparen = force # ignore/add/remove/force/not_defined + +# Add or remove space between ')' and '{' of control statements. +sp_sparen_brace = force # ignore/add/remove/force/not_defined + +# Add or remove space between 'do' and '{'. +sp_do_brace_open = force # ignore/add/remove/force/not_defined + +# Add or remove space between '}' and 'while'. +sp_brace_close_while = force # ignore/add/remove/force/not_defined + +# Add or remove space between 'while' and '('. Overrides sp_before_sparen. +sp_while_paren_open = force # ignore/add/remove/force/not_defined + +# (D) Add or remove space between 'invariant' and '('. +sp_invariant_paren = force # ignore/add/remove/force/not_defined + +# (D) Add or remove space after the ')' in 'invariant (C) c'. +sp_after_invariant_paren = force # ignore/add/remove/force/not_defined + +# Add or remove space before empty statement ';' on 'if', 'for' and 'while'. +sp_special_semi = remove # ignore/add/remove/force/not_defined + +# Add or remove space before ';'. +# +# Default: remove +sp_before_semi = remove # ignore/add/remove/force/not_defined + +# Add or remove space before ';' in non-empty 'for' statements. +sp_before_semi_for = remove # ignore/add/remove/force/not_defined + +# Add or remove space before a semicolon of an empty left part of a for +# statement, as in 'for ( ; ; )'. +sp_before_semi_for_empty = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between the semicolons of an empty middle part of a for +# statement, as in 'for ( ; ; )'. +sp_between_semi_for_empty = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after ';', except when followed by a comment. +# +# Default: add +sp_after_semi = add # ignore/add/remove/force/not_defined + +# Add or remove space after ';' in non-empty 'for' statements. +# +# Default: force +sp_after_semi_for = force # ignore/add/remove/force/not_defined + +# Add or remove space after the final semicolon of an empty part of a for +# statement, as in 'for ( ; ; )'. +sp_after_semi_for_empty = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before '[' (except '[]'). +sp_before_square = remove # ignore/add/remove/force/not_defined + +# Add or remove space before '[' for a variable definition. +# +# Default: remove +sp_before_vardef_square = remove # ignore/add/remove/force/not_defined + +# Add or remove space before '[' for asm block. +sp_before_square_asm_block = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before '[]'. +sp_before_squares = remove # ignore/add/remove/force/not_defined + +# Add or remove space before C++17 structured bindings. +sp_cpp_before_struct_binding = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside a non-empty '[' and ']'. +sp_inside_square = remove # ignore/add/remove/force/not_defined + +# Add or remove space inside '[]'. +sp_inside_square_empty = remove # ignore/add/remove/force/not_defined + +# (OC) Add or remove space inside a non-empty Objective-C boxed array '@[' and +# ']'. If set to ignore, sp_inside_square is used. +sp_inside_square_oc_array = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after ',', i.e. 'a,b' vs. 'a, b'. +sp_after_comma = force # ignore/add/remove/force/not_defined + +# Add or remove space before ',', i.e. 'a,b' vs. 'a ,b'. +# +# Default: remove +sp_before_comma = remove # ignore/add/remove/force/not_defined + +# (C#, Vala) Add or remove space between ',' and ']' in multidimensional array type +# like 'int[,,]'. +sp_after_mdatype_commas = ignore # ignore/add/remove/force/not_defined + +# (C#, Vala) Add or remove space between '[' and ',' in multidimensional array type +# like 'int[,,]'. +sp_before_mdatype_commas = ignore # ignore/add/remove/force/not_defined + +# (C#, Vala) Add or remove space between ',' in multidimensional array type +# like 'int[,,]'. +sp_between_mdatype_commas = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between an open parenthesis and comma, +# i.e. '(,' vs. '( ,'. +# +# Default: force +sp_paren_comma = force # ignore/add/remove/force/not_defined + +# Add or remove space between a type and ':'. +sp_type_colon = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after the variadic '...' when preceded by a +# non-punctuator. +# The value REMOVE will be overridden with FORCE +sp_after_ellipsis = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before the variadic '...' when preceded by a +# non-punctuator. +# The value REMOVE will be overridden with FORCE +sp_before_ellipsis = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a type and '...'. +sp_type_ellipsis = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a '*' and '...'. +sp_ptr_type_ellipsis = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between ')' and '...'. +sp_paren_ellipsis = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between '&&' and '...'. +sp_byref_ellipsis = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between ')' and a qualifier such as 'const'. +sp_paren_qualifier = force # ignore/add/remove/force/not_defined + +# Add or remove space between ')' and 'noexcept'. +sp_paren_noexcept = force # ignore/add/remove/force/not_defined + +# Add or remove space after class ':'. +sp_after_class_colon = force # ignore/add/remove/force/not_defined + +# Add or remove space before class ':'. +sp_before_class_colon = force # ignore/add/remove/force/not_defined + +# Add or remove space after class constructor ':'. +# +# Default: add +sp_after_constr_colon = add # ignore/add/remove/force/not_defined + +# Add or remove space before class constructor ':'. +# +# Default: add +sp_before_constr_colon = remove # ignore/add/remove/force/not_defined + +# Add or remove space before case ':'. +# +# Default: remove +sp_before_case_colon = remove # ignore/add/remove/force/not_defined + +# Add or remove space between 'operator' and operator sign. +sp_after_operator = force # ignore/add/remove/force/not_defined + +# Add or remove space between the operator symbol and the open parenthesis, as +# in 'operator ++('. +sp_after_operator_sym = force # ignore/add/remove/force/not_defined + +# Overrides sp_after_operator_sym when the operator has no arguments, as in +# 'operator *()'. +sp_after_operator_sym_empty = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after C/D cast, i.e. 'cast(int)a' vs. 'cast(int) a' or +# '(int)a' vs. '(int) a'. +sp_after_cast = remove # ignore/add/remove/force/not_defined + +# Add or remove spaces inside cast parentheses. +sp_inside_paren_cast = remove # ignore/add/remove/force/not_defined + +# Add or remove space between the type and open parenthesis in a C++ cast, +# i.e. 'int(exp)' vs. 'int (exp)'. +sp_cpp_cast_paren = remove # ignore/add/remove/force/not_defined + +# Add or remove space between 'sizeof' and '('. +sp_sizeof_paren = remove # ignore/add/remove/force/not_defined + +# Add or remove space between 'sizeof' and '...'. +sp_sizeof_ellipsis = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'sizeof...' and '('. +sp_sizeof_ellipsis_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between '...' and a parameter pack. +sp_ellipsis_parameter_pack = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a parameter pack and '...'. +sp_parameter_pack_ellipsis = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'decltype' and '('. +sp_decltype_paren = ignore # ignore/add/remove/force/not_defined + +# (Pawn) Add or remove space after the tag keyword. +sp_after_tag = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside enum '{' and '}'. +sp_inside_braces_enum = remove # ignore/add/remove/force/not_defined + +# Add or remove space inside struct/union '{' and '}'. +sp_inside_braces_struct = remove # ignore/add/remove/force/not_defined + +# (OC) Add or remove space inside Objective-C boxed dictionary '{' and '}' +sp_inside_braces_oc_dict = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after open brace in an unnamed temporary +# direct-list-initialization +# if statement is a brace_init_lst +# works only if sp_brace_brace is set to ignore. +sp_after_type_brace_init_lst_open = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before close brace in an unnamed temporary +# direct-list-initialization +# if statement is a brace_init_lst +# works only if sp_brace_brace is set to ignore. +sp_before_type_brace_init_lst_close = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside an unnamed temporary direct-list-initialization +# if statement is a brace_init_lst +# works only if sp_brace_brace is set to ignore +# works only if sp_before_type_brace_init_lst_close is set to ignore. +sp_inside_type_brace_init_lst = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside '{' and '}'. +sp_inside_braces = remove # ignore/add/remove/force/not_defined + +# Add or remove space inside '{}'. +sp_inside_braces_empty = remove # ignore/add/remove/force/not_defined + +# Add or remove space around trailing return operator '->'. +sp_trailing_return = return # ignore/add/remove/force/not_defined + +# Add or remove space between return type and function name. A minimum of 1 +# is forced except for pointer return types. +sp_type_func = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between type and open brace of an unnamed temporary +# direct-list-initialization. +sp_type_brace_init_lst = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between function name and '(' on function declaration. +sp_func_proto_paren = force # ignore/add/remove/force/not_defined + +# Add or remove space between function name and '()' on function declaration +# without parameters. +sp_func_proto_paren_empty = force # ignore/add/remove/force/not_defined + +# Add or remove space between function name and '(' with a typedef specifier. +sp_func_type_paren = force # ignore/add/remove/force/not_defined + +# Add or remove space between alias name and '(' of a non-pointer function type typedef. +sp_func_def_paren = force # ignore/add/remove/force/not_defined + +# Add or remove space between function name and '()' on function definition +# without parameters. +sp_func_def_paren_empty = force # ignore/add/remove/force/not_defined + +# Add or remove space inside empty function '()'. +# Overrides sp_after_angle unless use_sp_after_angle_always is set to true. +sp_inside_fparens = remove # ignore/add/remove/force/not_defined + +# Add or remove space inside function '(' and ')'. +sp_inside_fparen = remove # ignore/add/remove/force/not_defined + +# Add or remove space inside user functor '(' and ')'. +sp_func_call_user_inside_rparen = remove # ignore/add/remove/force/not_defined + +# Add or remove space inside empty functor '()'. +# Overrides sp_after_angle unless use_sp_after_angle_always is set to true. +sp_inside_rparens = remove # ignore/add/remove/force/not_defined + +# Add or remove space inside functor '(' and ')'. +sp_inside_rparen = remove # ignore/add/remove/force/not_defined + +# Add or remove space inside the first parentheses in a function type, as in +# 'void (*x)(...)'. +sp_inside_tparen = remove # ignore/add/remove/force/not_defined + +# Add or remove space between the ')' and '(' in a function type, as in +# 'void (*x)(...)'. +sp_after_tparen_close = force # ignore/add/remove/force/not_defined + +# Add or remove space between ']' and '(' when part of a function call. +sp_square_fparen = remove # ignore/add/remove/force/not_defined + +# Add or remove space between ')' and '{' of function. +sp_fparen_brace = force # ignore/add/remove/force/not_defined + +# Add or remove space between ')' and '{' of a function call in object +# initialization. +# +# Overrides sp_fparen_brace. +sp_fparen_brace_initializer = force # ignore/add/remove/force/not_defined + +# (Java) Add or remove space between ')' and '{{' of double brace initializer. +sp_fparen_dbrace = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between function name and '(' on function calls. +sp_func_call_paren = remove # ignore/add/remove/force/not_defined + +# Add or remove space between function name and '()' on function calls without +# parameters. If set to ignore (the default), sp_func_call_paren is used. +sp_func_call_paren_empty = remove # ignore/add/remove/force/not_defined + +# Add or remove space between the user function name and '(' on function +# calls. You need to set a keyword to be a user function in the config file, +# like: +# set func_call_user tr _ i18n +sp_func_call_user_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside user function '(' and ')'. +sp_func_call_user_inside_fparen = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between nested parentheses with user functions, +# i.e. '((' vs. '( ('. +sp_func_call_user_paren_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a constructor/destructor and the open +# parenthesis. +sp_func_class_paren = force # ignore/add/remove/force/not_defined + +# Add or remove space between a constructor without parameters or destructor +# and '()'. +sp_func_class_paren_empty = force # ignore/add/remove/force/not_defined + +# Add or remove space after 'return'. +# +# Default: force +sp_return = force # ignore/add/remove/force/not_defined + +# Add or remove space between 'return' and '('. +sp_return_paren = force # ignore/add/remove/force/not_defined + +# Add or remove space between 'return' and '{'. +sp_return_brace = force # ignore/add/remove/force/not_defined + +# Add or remove space between '__attribute__' and '('. +sp_attribute_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'defined' and '(' in '#if defined (FOO)'. +sp_defined_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'throw' and '(' in 'throw (something)'. +sp_throw_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'throw' and anything other than '(' as in +# '@throw [...];'. +sp_after_throw = force # ignore/add/remove/force/not_defined + +# Add or remove space between 'catch' and '(' in 'catch (something) { }'. +# If set to ignore, sp_before_sparen is used. +sp_catch_paren = force # ignore/add/remove/force/not_defined + +# (OC) Add or remove space between '@catch' and '(' +# in '@catch (something) { }'. If set to ignore, sp_catch_paren is used. +sp_oc_catch_paren = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space before Objective-C protocol list +# as in '@protocol Protocol' or '@interface MyClass : NSObject'. +sp_before_oc_proto_list = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space between class name and '(' +# in '@interface className(categoryName):BaseClass' +sp_oc_classname_paren = ignore # ignore/add/remove/force/not_defined + +# (D) Add or remove space between 'version' and '(' +# in 'version (something) { }'. If set to ignore, sp_before_sparen is used. +sp_version_paren = ignore # ignore/add/remove/force/not_defined + +# (D) Add or remove space between 'scope' and '(' +# in 'scope (something) { }'. If set to ignore, sp_before_sparen is used. +sp_scope_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'super' and '(' in 'super (something)'. +# +# Default: remove +sp_super_paren = remove # ignore/add/remove/force/not_defined + +# Add or remove space between 'this' and '(' in 'this (something)'. +# +# Default: remove +sp_this_paren = remove # ignore/add/remove/force/not_defined + +# Add or remove space between a macro name and its definition. +sp_macro = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a macro function ')' and its definition. +sp_macro_func = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between 'else' and '{' if on the same line. +sp_else_brace = force # ignore/add/remove/force/not_defined + +# Add or remove space between '}' and 'else' if on the same line. +sp_brace_else = force # ignore/add/remove/force/not_defined + +# Add or remove space between '}' and the name of a typedef on the same line. +sp_brace_typedef = force # ignore/add/remove/force/not_defined + +# Add or remove space before the '{' of a 'catch' statement, if the '{' and +# 'catch' are on the same line, as in 'catch (decl) {'. +sp_catch_brace = force # ignore/add/remove/force/not_defined + +# (OC) Add or remove space before the '{' of a '@catch' statement, if the '{' +# and '@catch' are on the same line, as in '@catch (decl) {'. +# If set to ignore, sp_catch_brace is used. +sp_oc_catch_brace = force # ignore/add/remove/force/not_defined + +# Add or remove space between '}' and 'catch' if on the same line. +sp_brace_catch = force # ignore/add/remove/force/not_defined + +# (OC) Add or remove space between '}' and '@catch' if on the same line. +# If set to ignore, sp_brace_catch is used. +sp_oc_brace_catch = force # ignore/add/remove/force/not_defined + +# Add or remove space between 'finally' and '{' if on the same line. +sp_finally_brace = force # ignore/add/remove/force/not_defined + +# Add or remove space between '}' and 'finally' if on the same line. +sp_brace_finally = force # ignore/add/remove/force/not_defined + +# Add or remove space between 'try' and '{' if on the same line. +sp_try_brace = force # ignore/add/remove/force/not_defined + +# Add or remove space between get/set and '{' if on the same line. +sp_getset_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a variable and '{' for C++ uniform +# initialization. +sp_word_brace_init_lst = remove # ignore/add/remove/force/not_defined + +# Add or remove space between a variable and '{' for a namespace. +# +# Default: add +sp_word_brace_ns = add # ignore/add/remove/force/not_defined + +# Add or remove space before the '::' operator. +sp_before_dc = remove # ignore/add/remove/force/not_defined + +# Add or remove space after the '::' operator. +sp_after_dc = remove # ignore/add/remove/force/not_defined + +# (D) Add or remove around the D named array initializer ':' operator. +sp_d_array_colon = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after the '!' (not) unary operator. +# +# Default: remove +sp_not = remove # ignore/add/remove/force/not_defined + +# Add or remove space between two '!' (not) unary operators. +# If set to ignore, sp_not will be used. +sp_not_not = remove # ignore/add/remove/force/not_defined + +# Add or remove space after the '~' (invert) unary operator. +# +# Default: remove +sp_inv = remove # ignore/add/remove/force/not_defined + +# Add or remove space after the '&' (address-of) unary operator. This does not +# affect the spacing after a '&' that is part of a type. +# +# Default: remove +sp_addr = remove # ignore/add/remove/force/not_defined + +# Add or remove space around the '.' or '->' operators. +# +# Default: remove +sp_member = remove # ignore/add/remove/force/not_defined + +# Add or remove space after the '*' (dereference) unary operator. This does +# not affect the spacing after a '*' that is part of a type. +# +# Default: remove +sp_deref = remove # ignore/add/remove/force/not_defined + +# Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. +# +# Default: remove +sp_sign = remove # ignore/add/remove/force/not_defined + +# Add or remove space between '++' and '--' the word to which it is being +# applied, as in '(--x)' or 'y++;'. +# +# Default: remove +sp_incdec = remove # ignore/add/remove/force/not_defined + +# Add or remove space before a backslash-newline at the end of a line. +# +# Default: add +sp_before_nl_cont = add # ignore/add/remove/force/not_defined + +# (OC) Add or remove space after the scope '+' or '-', as in '-(void) foo;' +# or '+(int) bar;'. +sp_after_oc_scope = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space after the colon in message specs, +# i.e. '-(int) f:(int) x;' vs. '-(int) f: (int) x;'. +sp_after_oc_colon = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space before the colon in message specs, +# i.e. '-(int) f: (int) x;' vs. '-(int) f : (int) x;'. +sp_before_oc_colon = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space after the colon in immutable dictionary expression +# 'NSDictionary *test = @{@"foo" :@"bar"};'. +sp_after_oc_dict_colon = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space before the colon in immutable dictionary expression +# 'NSDictionary *test = @{@"foo" :@"bar"};'. +sp_before_oc_dict_colon = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space after the colon in message specs, +# i.e. '[object setValue:1];' vs. '[object setValue: 1];'. +sp_after_send_oc_colon = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space before the colon in message specs, +# i.e. '[object setValue:1];' vs. '[object setValue :1];'. +sp_before_send_oc_colon = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space after the (type) in message specs, +# i.e. '-(int)f: (int) x;' vs. '-(int)f: (int)x;'. +sp_after_oc_type = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space after the first (type) in message specs, +# i.e. '-(int) f:(int)x;' vs. '-(int)f:(int)x;'. +sp_after_oc_return_type = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space between '@selector' and '(', +# i.e. '@selector(msgName)' vs. '@selector (msgName)'. +# Also applies to '@protocol()' constructs. +sp_after_oc_at_sel = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space between '@selector(x)' and the following word, +# i.e. '@selector(foo) a:' vs. '@selector(foo)a:'. +sp_after_oc_at_sel_parens = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space inside '@selector' parentheses, +# i.e. '@selector(foo)' vs. '@selector( foo )'. +# Also applies to '@protocol()' constructs. +sp_inside_oc_at_sel_parens = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space before a block pointer caret, +# i.e. '^int (int arg){...}' vs. ' ^int (int arg){...}'. +sp_before_oc_block_caret = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space after a block pointer caret, +# i.e. '^int (int arg){...}' vs. '^ int (int arg){...}'. +sp_after_oc_block_caret = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space between the receiver and selector in a message, +# as in '[receiver selector ...]'. +sp_after_oc_msg_receiver = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space after '@property'. +sp_after_oc_property = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove space between '@synchronized' and the open parenthesis, +# i.e. '@synchronized(foo)' vs. '@synchronized (foo)'. +sp_after_oc_synchronized = ignore # ignore/add/remove/force/not_defined + +# Add or remove space around the ':' in 'b ? t : f'. +sp_cond_colon = force # ignore/add/remove/force/not_defined + +# Add or remove space before the ':' in 'b ? t : f'. +# +# Overrides sp_cond_colon. +sp_cond_colon_before = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after the ':' in 'b ? t : f'. +# +# Overrides sp_cond_colon. +sp_cond_colon_after = ignore # ignore/add/remove/force/not_defined + +# Add or remove space around the '?' in 'b ? t : f'. +sp_cond_question = force # ignore/add/remove/force/not_defined + +# Add or remove space before the '?' in 'b ? t : f'. +# +# Overrides sp_cond_question. +sp_cond_question_before = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after the '?' in 'b ? t : f'. +# +# Overrides sp_cond_question. +sp_cond_question_after = ignore # ignore/add/remove/force/not_defined + +# In the abbreviated ternary form '(a ?: b)', add or remove space between '?' +# and ':'. +# +# Overrides all other sp_cond_* options. +sp_cond_ternary_short = ignore # ignore/add/remove/force/not_defined + +# Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make +# sense here. +sp_case_label = force # ignore/add/remove/force/not_defined + +# (D) Add or remove space around the D '..' operator. +sp_range = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after ':' in a Java/C++11 range-based 'for', +# as in 'for (Type var : expr)'. +sp_after_for_colon = force # ignore/add/remove/force/not_defined + +# Add or remove space before ':' in a Java/C++11 range-based 'for', +# as in 'for (Type var : expr)'. +sp_before_for_colon = force # ignore/add/remove/force/not_defined + +# (D) Add or remove space between 'extern' and '(' as in 'extern (C)'. +sp_extern_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after the opening of a C++ comment, as in '// A'. +sp_cmt_cpp_start = remove # ignore/add/remove/force/not_defined + +# remove space after the '//' and the pvs command '-V1234', +# only works with sp_cmt_cpp_start set to add or force. +sp_cmt_cpp_pvs = false # true/false + +# remove space after the '//' and the command 'lint', +# only works with sp_cmt_cpp_start set to add or force. +sp_cmt_cpp_lint = false # true/false + +# Add or remove space in a C++ region marker comment, as in '// BEGIN'. +# A region marker is defined as a comment which is not preceded by other text +# (i.e. the comment is the first non-whitespace on the line), and which starts +# with either 'BEGIN' or 'END'. +# +# Overrides sp_cmt_cpp_start. +sp_cmt_cpp_region = ignore # ignore/add/remove/force/not_defined + +# If true, space added with sp_cmt_cpp_start will be added after Doxygen +# sequences like '///', '///<', '//!' and '//!<'. +sp_cmt_cpp_doxygen = false # true/false + +# If true, space added with sp_cmt_cpp_start will be added after Qt translator +# or meta-data comments like '//:', '//=', and '//~'. +sp_cmt_cpp_qttr = false # true/false + +# Add or remove space between #else or #endif and a trailing comment. +sp_endif_cmt = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after 'new', 'delete' and 'delete[]'. +sp_after_new = force # ignore/add/remove/force/not_defined + +# Add or remove space between 'new' and '(' in 'new()'. +sp_between_new_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between ')' and type in 'new(foo) BAR'. +sp_after_newop_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space inside parentheses of the new operator +# as in 'new(foo) BAR'. +sp_inside_newop_paren = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after the open parenthesis of the new operator, +# as in 'new(foo) BAR'. +# +# Overrides sp_inside_newop_paren. +sp_inside_newop_paren_open = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before the close parenthesis of the new operator, +# as in 'new(foo) BAR'. +# +# Overrides sp_inside_newop_paren. +sp_inside_newop_paren_close = ignore # ignore/add/remove/force/not_defined + +# Add or remove space before a trailing comment. +sp_before_tr_cmt = ignore # ignore/add/remove/force/not_defined + +# Number of spaces before a trailing comment. +sp_num_before_tr_cmt = 0 # unsigned number + +# Add or remove space before an embedded comment. +# +# Default: force +sp_before_emb_cmt = force # ignore/add/remove/force/not_defined + +# Number of spaces before an embedded comment. +# +# Default: 1 +sp_num_before_emb_cmt = 1 # unsigned number + +# Add or remove space after an embedded comment. +# +# Default: force +sp_after_emb_cmt = force # ignore/add/remove/force/not_defined + +# Number of spaces after an embedded comment. +# +# Default: 1 +sp_num_after_emb_cmt = 1 # unsigned number + +# (Java) Add or remove space between an annotation and the open parenthesis. +sp_annotation_paren = ignore # ignore/add/remove/force/not_defined + +# If true, vbrace tokens are dropped to the previous token and skipped. +sp_skip_vbrace_tokens = false # true/false + +# Add or remove space after 'noexcept'. +sp_after_noexcept = ignore # ignore/add/remove/force/not_defined + +# Add or remove space after '_'. +sp_vala_after_translation = ignore # ignore/add/remove/force/not_defined + +# If true, a is inserted after #define. +force_tab_after_define = false # true/false + +# +# Indenting options +# + +# The number of columns to indent per level. Usually 2, 3, 4, or 8. +# +# Default: 8 +indent_columns = 4 # unsigned number + +# Whether to ignore indent for the first continuation line. Subsequent +# continuation lines will still be indented to match the first. +indent_ignore_first_continue = false # true/false + +# The continuation indent. If non-zero, this overrides the indent of '(', '[' +# and '=' continuation indents. Negative values are OK; negative value is +# absolute and not increased for each '(' or '[' level. +# +# For FreeBSD, this is set to 4. +# Requires indent_ignore_first_continue=false. +indent_continue = 0 # number + +# The continuation indent, only for class header line(s). If non-zero, this +# overrides the indent of 'class' continuation indents. +# Requires indent_ignore_first_continue=false. +indent_continue_class_head = 0 # unsigned number + +# Whether to indent empty lines (i.e. lines which contain only spaces before +# the newline character). +indent_single_newlines = false # true/false + +# The continuation indent for func_*_param if they are true. If non-zero, this +# overrides the indent. +indent_param = 0 # unsigned number + +# How to use tabs when indenting code. +# +# 0: Spaces only +# 1: Indent with tabs to brace level, align with spaces (default) +# 2: Indent and align with tabs, using spaces when not on a tabstop +# +# Default: 1 +indent_with_tabs = 0 # unsigned number + +# Whether to indent comments that are not at a brace level with tabs on a +# tabstop. Requires indent_with_tabs=2. If false, will use spaces. +indent_cmt_with_tabs = false # true/false + +# Whether to indent strings broken by '\' so that they line up. +indent_align_string = true # true/false + +# The number of spaces to indent multi-line XML strings. +# Requires indent_align_string=true. +indent_xml_string = 2 # unsigned number + +# Spaces to indent '{' from level. +indent_brace = 0 # unsigned number + +# Whether braces are indented to the body level. +indent_braces = false # true/false + +# Whether to disable indenting function braces if indent_braces=true. +indent_braces_no_func = false # true/false + +# Whether to disable indenting class braces if indent_braces=true. +indent_braces_no_class = false # true/false + +# Whether to disable indenting struct braces if indent_braces=true. +indent_braces_no_struct = false # true/false + +# Whether to indent based on the size of the brace parent, +# i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc. +indent_brace_parent = false # true/false + +# Whether to indent based on the open parenthesis instead of the open brace +# in '({\n'. +indent_paren_open_brace = false # true/false + +# (C#) Whether to indent the brace of a C# delegate by another level. +indent_cs_delegate_brace = false # true/false + +# (C#) Whether to indent a C# delegate (to handle delegates with no brace) by +# another level. +indent_cs_delegate_body = false # true/false + +# Whether to indent the body of a 'namespace'. +indent_namespace = false # true/false + +# Whether to indent only the first namespace, and not any nested namespaces. +# Requires indent_namespace=true. +indent_namespace_single_indent = false # true/false + +# The number of spaces to indent a namespace block. +# If set to zero, use the value indent_columns +indent_namespace_level = 0 # unsigned number + +# If the body of the namespace is longer than this number, it won't be +# indented. Requires indent_namespace=true. 0 means no limit. +indent_namespace_limit = 0 # unsigned number + +# Whether to indent only in inner namespaces (nested in other namespaces). +# Requires indent_namespace=true. +indent_namespace_inner_only = false # true/false + +# Whether the 'extern "C"' body is indented. +indent_extern = false # true/false + +# Whether the 'class' body is indented. +indent_class = true # true/false + +# Whether to ignore indent for the leading base class colon. +indent_ignore_before_class_colon = false # true/false + +# Additional indent before the leading base class colon. +# Negative values decrease indent down to the first column. +# Requires indent_ignore_before_class_colon=false and a newline break before +# the colon (see pos_class_colon and nl_class_colon) +indent_before_class_colon = 0 # number + +# Whether to indent the stuff after a leading base class colon. +indent_class_colon = false # true/false + +# Whether to indent based on a class colon instead of the stuff after the +# colon. Requires indent_class_colon=true. +indent_class_on_colon = false # true/false + +# Whether to ignore indent for a leading class initializer colon. +indent_ignore_before_constr_colon = false # true/false + +# Whether to indent the stuff after a leading class initializer colon. +indent_constr_colon = true # true/false + +# Virtual indent from the ':' for leading member initializers. +# +# Default: 2 +indent_ctor_init_leading = 2 # unsigned number + +# Virtual indent from the ':' for following member initializers. +# +# Default: 2 +indent_ctor_init_following = 2 # unsigned number + +# Additional indent for constructor initializer list. +# Negative values decrease indent down to the first column. +indent_ctor_init = 0 # number + +# Whether to indent 'if' following 'else' as a new block under the 'else'. +# If false, 'else\nif' is treated as 'else if' for indenting purposes. +indent_else_if = false # true/false + +# Amount to indent variable declarations after a open brace. +# +# <0: Relative +# >=0: Absolute +indent_var_def_blk = 0 # number + +# Whether to indent continued variable declarations instead of aligning. +indent_var_def_cont = true # true/false + +# How to indent continued shift expressions ('<<' and '>>'). +# Set align_left_shift=false when using this. +# 0: Align shift operators instead of indenting them (default) +# 1: Indent by one level +# -1: Preserve original indentation +indent_shift = 0 # number + +# Whether to force indentation of function definitions to start in column 1. +indent_func_def_force_col1 = false # true/false + +# Whether to indent continued function call parameters one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_call_param = true # true/false + +# Whether to indent continued function definition parameters one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_def_param = false # true/false + +# for function definitions, only if indent_func_def_param is false +# Allows to align params when appropriate and indent them when not +# behave as if it was true if paren position is more than this value +# if paren position is more than the option value +indent_func_def_param_paren_pos_threshold = 0 # unsigned number + +# Whether to indent continued function call prototype one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_proto_param = true # true/false + +# Whether to indent continued function call declaration one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_class_param = true # true/false + +# Whether to indent continued class variable constructors one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_ctor_var_param = true # true/false + +# Whether to indent continued template parameter list one indent level, +# rather than aligning parameters under the open parenthesis. +indent_template_param = true # true/false + +# Double the indent for indent_func_xxx_param options. +# Use both values of the options indent_columns and indent_param. +indent_func_param_double = false # true/false + +# Indentation column for standalone 'const' qualifier on a function +# prototype. +indent_func_const = 0 # unsigned number + +# Indentation column for standalone 'throw' qualifier on a function +# prototype. +indent_func_throw = 0 # unsigned number + +# How to indent within a macro followed by a brace on the same line +# This allows reducing the indent in macros that have (for example) +# `do { ... } while (0)` blocks bracketing them. +# +# true: add an indent for the brace on the same line as the macro +# false: do not add an indent for the brace on the same line as the macro +# +# Default: true +indent_macro_brace = true # true/false + +# The number of spaces to indent a continued '->' or '.'. +# Usually set to 0, 1, or indent_columns. +indent_member = 0 # unsigned number + +# Whether lines broken at '.' or '->' should be indented by a single indent. +# The indent_member option will not be effective if this is set to true. +indent_member_single = true # true/false + +# Spaces to indent single line ('//') comments on lines before code. +indent_single_line_comments_before = 0 # unsigned number + +# Spaces to indent single line ('//') comments on lines after code. +indent_single_line_comments_after = 0 # unsigned number + +# When opening a paren for a control statement (if, for, while, etc), increase +# the indent level by this value. Negative values decrease the indent level. +indent_sparen_extra = 0 # number + +# Whether to indent trailing single line ('//') comments relative to the code +# instead of trying to keep the same absolute column. +indent_relative_single_line_comments = false # true/false + +# Spaces to indent 'case' from 'switch'. Usually 0 or indent_columns. +# It might be wise to choose the same value for the option indent_case_brace. +indent_switch_case = indent_columns # unsigned number + +# Spaces to indent the body of a 'switch' before any 'case'. +# Usually the same as indent_columns or indent_switch_case. +indent_switch_body = indent_columns # unsigned number + +# Whether to ignore indent for '{' following 'case'. +indent_ignore_case_brace = false # true/false + +# Spaces to indent '{' from 'case'. By default, the brace will appear under +# the 'c' in case. Usually set to 0 or indent_columns. Negative values are OK. +# It might be wise to choose the same value for the option indent_switch_case. +indent_case_brace = indent_columns # number + +# indent 'break' with 'case' from 'switch'. +indent_switch_break_with_case = false # true/false + +# Whether to indent preprocessor statements inside of switch statements. +# +# Default: true +indent_switch_pp = false # true/false + +# Spaces to shift the 'case' line, without affecting any other lines. +# Usually 0. +indent_case_shift = 0 # unsigned number + +# Whether to align comments before 'case' with the 'case'. +# +# Default: true +indent_case_comment = true # true/false + +# Whether to indent comments not found in first column. +# +# Default: true +indent_comment = true # true/false + +# Whether to indent comments found in first column. +indent_col1_comment = false # true/false + +# Whether to indent multi string literal in first column. +indent_col1_multi_string_literal = true # true/false + +# Align comments on adjacent lines that are this many columns apart or less. +# +# Default: 3 +indent_comment_align_thresh = 3 # unsigned number + +# Whether to ignore indent for goto labels. +indent_ignore_label = false # true/false + +# How to indent goto labels. Requires indent_ignore_label=false. +# +# >0: Absolute column where 1 is the leftmost column +# <=0: Subtract from brace indent +# +# Default: 1 +indent_label = 1 # number + +# How to indent access specifiers that are followed by a +# colon. +# +# >0: Absolute column where 1 is the leftmost column +# <=0: Subtract from brace indent +# +# Default: 1 +indent_access_spec = 1 # number + +# Whether to indent the code after an access specifier by one level. +# If true, this option forces 'indent_access_spec=0'. +indent_access_spec_body = false # true/false + +# If an open parenthesis is followed by a newline, whether to indent the next +# line so that it lines up after the open parenthesis (not recommended). +indent_paren_nl = false # true/false + +# How to indent a close parenthesis after a newline. +# +# 0: Indent to body level (default) +# 1: Align under the open parenthesis +# 2: Indent to the brace level +# -1: Preserve original indentation +indent_paren_close = 2 # number + +# Whether to indent the open parenthesis of a function definition, +# if the parenthesis is on its own line. +indent_paren_after_func_def = false # true/false + +# Whether to indent the open parenthesis of a function declaration, +# if the parenthesis is on its own line. +indent_paren_after_func_decl = false # true/false + +# Whether to indent the open parenthesis of a function call, +# if the parenthesis is on its own line. +indent_paren_after_func_call = false # true/false + +# How to indent a comma when inside braces. +# 0: Indent by one level (default) +# 1: Align under the open brace +# -1: Preserve original indentation +indent_comma_brace = 0 # number + +# How to indent a comma when inside parentheses. +# 0: Indent by one level (default) +# 1: Align under the open parenthesis +# -1: Preserve original indentation +indent_comma_paren = 0 # number + +# How to indent a Boolean operator when inside parentheses. +# 0: Indent by one level (default) +# 1: Align under the open parenthesis +# -1: Preserve original indentation +indent_bool_paren = 0 # number + +# Whether to ignore the indentation of a Boolean operator when outside +# parentheses. +indent_ignore_bool = false # true/false + +# Whether to ignore the indentation of an arithmetic operator. +indent_ignore_arith = false # true/false + +# Whether to indent a semicolon when inside a for parenthesis. +# If true, aligns under the open for parenthesis. +indent_semicolon_for_paren = false # true/false + +# Whether to ignore the indentation of a semicolon outside of a 'for' +# statement. +indent_ignore_semicolon = false # true/false + +# Whether to align the first expression to following ones +# if indent_bool_paren=1. +indent_first_bool_expr = false # true/false + +# Whether to align the first expression to following ones +# if indent_semicolon_for_paren=true. +indent_first_for_expr = false # true/false + +# If an open square is followed by a newline, whether to indent the next line +# so that it lines up after the open square (not recommended). +indent_square_nl = false # true/false + +# (ESQL/C) Whether to preserve the relative indent of 'EXEC SQL' bodies. +indent_preserve_sql = false # true/false + +# Whether to ignore the indentation of an assignment operator. +indent_ignore_assign = false # true/false + +# Whether to align continued statements at the '='. If false or if the '=' is +# followed by a newline, the next line is indent one tab. +# +# Default: true +indent_align_assign = true # true/false + +# If true, the indentation of the chunks after a '=' sequence will be set at +# LHS token indentation column before '='. +indent_off_after_assign = false # true/false + +# Whether to align continued statements at the '('. If false or the '(' is +# followed by a newline, the next line indent is one tab. +# +# Default: true +indent_align_paren = true # true/false + +# (OC) Whether to indent Objective-C code inside message selectors. +indent_oc_inside_msg_sel = false # true/false + +# (OC) Whether to indent Objective-C blocks at brace level instead of usual +# rules. +indent_oc_block = false # true/false + +# (OC) Indent for Objective-C blocks in a message relative to the parameter +# name. +# +# =0: Use indent_oc_block rules +# >0: Use specified number of spaces to indent +indent_oc_block_msg = 0 # unsigned number + +# (OC) Minimum indent for subsequent parameters +indent_oc_msg_colon = 0 # unsigned number + +# (OC) Whether to prioritize aligning with initial colon (and stripping spaces +# from lines, if necessary). +# +# Default: true +indent_oc_msg_prioritize_first_colon = true # true/false + +# (OC) Whether to indent blocks the way that Xcode does by default +# (from the keyword if the parameter is on its own line; otherwise, from the +# previous indentation level). Requires indent_oc_block_msg=true. +indent_oc_block_msg_xcode_style = false # true/false + +# (OC) Whether to indent blocks from where the brace is, relative to a +# message keyword. Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_keyword = false # true/false + +# (OC) Whether to indent blocks from where the brace is, relative to a message +# colon. Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_colon = false # true/false + +# (OC) Whether to indent blocks from where the block caret is. +# Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_caret = false # true/false + +# (OC) Whether to indent blocks from where the brace caret is. +# Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_brace = false # true/false + +# When indenting after virtual brace open and newline add further spaces to +# reach this minimum indent. +indent_min_vbrace_open = 0 # unsigned number + +# Whether to add further spaces after regular indent to reach next tabstop +# when indenting after virtual brace open and newline. +indent_vbrace_open_on_tabstop = false # true/false + +# How to indent after a brace followed by another token (not a newline). +# true: indent all contained lines to match the token +# false: indent all contained lines to match the brace +# +# Default: true +indent_token_after_brace = true # true/false + +# Whether to indent the body of a C++11 lambda. +indent_cpp_lambda_body = true # true/false + +# How to indent compound literals that are being returned. +# true: add both the indent from return & the compound literal open brace +# (i.e. 2 indent levels) +# false: only indent 1 level, don't add the indent for the open brace, only +# add the indent for the return. +# +# Default: true +indent_compound_literal_return = true # true/false + +# (C#) Whether to indent a 'using' block if no braces are used. +# +# Default: true +indent_using_block = true # true/false + +# How to indent the continuation of ternary operator. +# +# 0: Off (default) +# 1: When the `if_false` is a continuation, indent it under the `if_true` branch +# 2: When the `:` is a continuation, indent it under `?` +indent_ternary_operator = 1 # unsigned number + +# Whether to indent the statements inside ternary operator. +indent_inside_ternary_operator = false # true/false + +# If true, the indentation of the chunks after a `return` sequence will be set at return indentation column. +indent_off_after_return = false # true/false + +# If true, the indentation of the chunks after a `return new` sequence will be set at return indentation column. +indent_off_after_return_new = false # true/false + +# If true, the tokens after return are indented with regular single indentation. By default (false) the indentation is after the return token. +indent_single_after_return = false # true/false + +# Whether to ignore indent and alignment for 'asm' blocks (i.e. assume they +# have their own indentation). +indent_ignore_asm_block = false # true/false + +# Don't indent the close parenthesis of a function definition, +# if the parenthesis is on its own line. +donot_indent_func_def_close_paren = false # true/false + +# +# Newline adding and removing options +# + +# Whether to collapse empty blocks between '{' and '}' except for functions. +# Use nl_collapse_empty_body_functions to specify how empty function braces +# should be formatted. +nl_collapse_empty_body = true # true/false + +# Whether to collapse empty blocks between '{' and '}' for functions only. +# If true, overrides nl_inside_empty_func. +nl_collapse_empty_body_functions = true # true/false + +# Don't split one-line braced assignments, as in 'foo_t f = { 1, 2 };'. +nl_assign_leave_one_liners = true # true/false + +# Don't split one-line braced statements inside a 'class xx { }' body. +nl_class_leave_one_liners = false # true/false + +# Don't split one-line enums, as in 'enum foo { BAR = 15 };' +nl_enum_leave_one_liners = false # true/false + +# Don't split one-line get or set functions. +nl_getset_leave_one_liners = false # true/false + +# (C#) Don't split one-line property get or set functions. +nl_cs_property_leave_one_liners = false # true/false + +# Don't split one-line function definitions, as in 'int foo() { return 0; }'. +# might modify nl_func_type_name +nl_func_leave_one_liners = true # true/false + +# Don't split one-line C++11 lambdas, as in '[]() { return 0; }'. +nl_cpp_lambda_leave_one_liners = true # true/false + +# Don't split one-line if/else statements, as in 'if(...) b++;'. +nl_if_leave_one_liners = false # true/false + +# Don't split one-line while statements, as in 'while(...) b++;'. +nl_while_leave_one_liners = false # true/false + +# Don't split one-line do statements, as in 'do { b++; } while(...);'. +nl_do_leave_one_liners = false # true/false + +# Don't split one-line for statements, as in 'for(...) b++;'. +nl_for_leave_one_liners = false # true/false + +# (OC) Don't split one-line Objective-C messages. +nl_oc_msg_leave_one_liner = false # true/false + +# (OC) Add or remove newline between method declaration and '{'. +nl_oc_mdef_brace = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove newline between Objective-C block signature and '{'. +nl_oc_block_brace = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove blank line before '@interface' statement. +nl_oc_before_interface = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove blank line before '@implementation' statement. +nl_oc_before_implementation = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove blank line before '@end' statement. +nl_oc_before_end = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove newline between '@interface' and '{'. +nl_oc_interface_brace = ignore # ignore/add/remove/force/not_defined + +# (OC) Add or remove newline between '@implementation' and '{'. +nl_oc_implementation_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove newlines at the start of the file. +nl_start_of_file = remove # ignore/add/remove/force/not_defined + +# The minimum number of newlines at the start of the file (only used if +# nl_start_of_file is 'add' or 'force'). +nl_start_of_file_min = 0 # unsigned number + +# Add or remove newline at the end of the file. +nl_end_of_file = remove # ignore/add/remove/force/not_defined + +# The minimum number of newlines at the end of the file (only used if +# nl_end_of_file is 'add' or 'force'). +nl_end_of_file_min = 0 # unsigned number + +# Add or remove newline between '=' and '{'. +nl_assign_brace = remove # ignore/add/remove/force/not_defined + +# (D) Add or remove newline between '=' and '['. +nl_assign_square = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between '[]' and '{'. +nl_tsquare_brace = remove # ignore/add/remove/force/not_defined + +# (D) Add or remove newline after '= ['. Will also affect the newline before +# the ']'. +nl_after_square_assign = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between a function call's ')' and '{', as in +# 'list_for_each(item, &list) { }'. +nl_fcall_brace = force # ignore/add/remove/force/not_defined + +# Add or remove newline between 'enum' and '{'. +nl_enum_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'enum' and 'class'. +nl_enum_class = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'enum class' and the identifier. +nl_enum_class_identifier = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'enum class' type and ':'. +nl_enum_identifier_colon = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'enum class identifier :' and type. +nl_enum_colon_type = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'struct and '{'. +nl_struct_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'union' and '{'. +nl_union_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'if' and '{'. +nl_if_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between '}' and 'else'. +nl_brace_else = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'else if' and '{'. If set to ignore, +# nl_if_brace is used instead. +nl_elseif_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between 'else' and '{'. +nl_else_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'else' and 'if'. +nl_else_if = remove # ignore/add/remove/force/not_defined + +# Add or remove newline before 'if'/'else if' closing parenthesis. +nl_before_if_closing_paren = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between '}' and 'finally'. +nl_brace_finally = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'finally' and '{'. +nl_finally_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'try' and '{'. +nl_try_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between get/set and '{'. +nl_getset_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between 'for' and '{'. +nl_for_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline before the '{' of a 'catch' statement, as in +# 'catch (decl) {'. +nl_catch_brace = remove # ignore/add/remove/force/not_defined + +# (OC) Add or remove newline before the '{' of a '@catch' statement, as in +# '@catch (decl) {'. If set to ignore, nl_catch_brace is used. +nl_oc_catch_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between '}' and 'catch'. +nl_brace_catch = remove # ignore/add/remove/force/not_defined + +# (OC) Add or remove newline between '}' and '@catch'. If set to ignore, +# nl_brace_catch is used. +nl_oc_brace_catch = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between '}' and ']'. +nl_brace_square = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between '}' and ')' in a function invocation. +nl_brace_fparen = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between 'while' and '{'. +nl_while_brace = remove # ignore/add/remove/force/not_defined + +# (D) Add or remove newline between 'scope (x)' and '{'. +nl_scope_brace = ignore # ignore/add/remove/force/not_defined + +# (D) Add or remove newline between 'unittest' and '{'. +nl_unittest_brace = ignore # ignore/add/remove/force/not_defined + +# (D) Add or remove newline between 'version (x)' and '{'. +nl_version_brace = ignore # ignore/add/remove/force/not_defined + +# (C#) Add or remove newline between 'using' and '{'. +nl_using_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between two open or close braces. Due to general +# newline/brace handling, REMOVE may not work. +nl_brace_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between 'do' and '{'. +nl_do_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between '}' and 'while' of 'do' statement. +nl_brace_while = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'switch' and '{'. +nl_switch_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'synchronized' and '{'. +nl_synchronized_brace = remove # ignore/add/remove/force/not_defined + +# Add a newline between ')' and '{' if the ')' is on a different line than the +# if/for/etc. +# +# Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch and +# nl_catch_brace. +nl_multi_line_cond = false # true/false + +# Add a newline after '(' if an if/for/while/switch condition spans multiple +# lines +nl_multi_line_sparen_open = force # ignore/add/remove/force/not_defined + +# Add a newline before ')' if an if/for/while/switch condition spans multiple +# lines. Overrides nl_before_if_closing_paren if both are specified. +nl_multi_line_sparen_close = force # ignore/add/remove/force/not_defined + +# Force a newline in a define after the macro name for multi-line defines. +nl_multi_line_define = false # true/false + +# Whether to add a newline before 'case', and a blank line before a 'case' +# statement that follows a ';' or '}'. +nl_before_case = false # true/false + +# Whether to add a newline after a 'case' statement. +nl_after_case = true # true/false + +# Add or remove newline between a case ':' and '{'. +# +# Overrides nl_after_case. +nl_case_colon_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between ')' and 'throw'. +nl_before_throw = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between 'namespace' and '{'. +nl_namespace_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<...>' of a template class. +nl_template_class = force # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<...>' of a template class declaration. +# +# Overrides nl_template_class. +nl_template_class_decl = force # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<>' of a specialized class declaration. +# +# Overrides nl_template_class_decl. +nl_template_class_decl_special = force # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<...>' of a template class definition. +# +# Overrides nl_template_class. +nl_template_class_def = force # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<>' of a specialized class definition. +# +# Overrides nl_template_class_def. +nl_template_class_def_special = force # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<...>' of a template function. +nl_template_func = force # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<...>' of a template function +# declaration. +# +# Overrides nl_template_func. +nl_template_func_decl = force # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<>' of a specialized function +# declaration. +# +# Overrides nl_template_func_decl. +nl_template_func_decl_special = force # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<...>' of a template function +# definition. +# +# Overrides nl_template_func. +nl_template_func_def = force # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<>' of a specialized function +# definition. +# +# Overrides nl_template_func_def. +nl_template_func_def_special = force # ignore/add/remove/force/not_defined + +# Add or remove newline after 'template<...>' of a template variable. +nl_template_var = force # ignore/add/remove/force/not_defined + +# Add or remove newline between 'template<...>' and 'using' of a templated +# type alias. +nl_template_using = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between 'class' and '{'. +nl_class_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline before or after (depending on pos_class_comma, +# may not be IGNORE) each',' in the base class list. +nl_class_init_args = force # ignore/add/remove/force/not_defined + +# Add or remove newline after each ',' in the constructor member +# initialization. Related to nl_constr_colon, pos_constr_colon and +# pos_constr_comma. +nl_constr_init_args = force # ignore/add/remove/force/not_defined + +# Add or remove newline before first element, after comma, and after last +# element, in 'enum'. +nl_enum_own_lines = add # ignore/add/remove/force/not_defined + +# Add or remove newline between return type and function name in a function +# definition. +# might be modified by nl_func_leave_one_liners +nl_func_type_name = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between return type and function name inside a class +# definition. If set to ignore, nl_func_type_name or nl_func_proto_type_name +# is used instead. +nl_func_type_name_class = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between class specification and '::' +# in 'void A::f() { }'. Only appears in separate member implementation (does +# not appear with in-line implementation). +nl_func_class_scope = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between function scope and name, as in +# 'void A :: f() { }'. +nl_func_scope_name = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between return type and function name in a prototype. +nl_func_proto_type_name = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between a function name and the opening '(' in the +# declaration. +nl_func_paren = remove # ignore/add/remove/force/not_defined + +# Overrides nl_func_paren for functions with no parameters. +nl_func_paren_empty = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between a function name and the opening '(' in the +# definition. +nl_func_def_paren = remove # ignore/add/remove/force/not_defined + +# Overrides nl_func_def_paren for functions with no parameters. +nl_func_def_paren_empty = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between a function name and the opening '(' in the +# call. +nl_func_call_paren = remove # ignore/add/remove/force/not_defined + +# Overrides nl_func_call_paren for functions with no parameters. +nl_func_call_paren_empty = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline after '(' in a function declaration. +nl_func_decl_start = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline after '(' in a function definition. +nl_func_def_start = ignore # ignore/add/remove/force/not_defined + +# Overrides nl_func_decl_start when there is only one parameter. +nl_func_decl_start_single = ignore # ignore/add/remove/force/not_defined + +# Overrides nl_func_def_start when there is only one parameter. +nl_func_def_start_single = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline after '(' in a function declaration if '(' and ')' +# are in different lines. If false, nl_func_decl_start is used instead. +nl_func_decl_start_multi_line = true # true/false + +# Whether to add a newline after '(' in a function definition if '(' and ')' +# are in different lines. If false, nl_func_def_start is used instead. +nl_func_def_start_multi_line = true # true/false + +# Add or remove newline after each ',' in a function declaration. +nl_func_decl_args = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline after each ',' in a function definition. +nl_func_def_args = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline after each ',' in a function call. +nl_func_call_args = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline after each ',' in a function declaration if '(' +# and ')' are in different lines. If false, nl_func_decl_args is used instead. +nl_func_decl_args_multi_line = true # true/false + +# Whether to add a newline after each ',' in a function definition if '(' +# and ')' are in different lines. If false, nl_func_def_args is used instead. +nl_func_def_args_multi_line = true # true/false + +# Add or remove newline before the ')' in a function declaration. +nl_func_decl_end = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline before the ')' in a function definition. +nl_func_def_end = ignore # ignore/add/remove/force/not_defined + +# Overrides nl_func_decl_end when there is only one parameter. +nl_func_decl_end_single = ignore # ignore/add/remove/force/not_defined + +# Overrides nl_func_def_end when there is only one parameter. +nl_func_def_end_single = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline before ')' in a function declaration if '(' and ')' +# are in different lines. If false, nl_func_decl_end is used instead. +nl_func_decl_end_multi_line = true # true/false + +# Whether to add a newline before ')' in a function definition if '(' and ')' +# are in different lines. If false, nl_func_def_end is used instead. +nl_func_def_end_multi_line = true # true/false + +# Add or remove newline between '()' in a function declaration. +nl_func_decl_empty = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between '()' in a function definition. +nl_func_def_empty = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between '()' in a function call. +nl_func_call_empty = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline after '(' in a function call, +# has preference over nl_func_call_start_multi_line. +nl_func_call_start = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline before ')' in a function call. +nl_func_call_end = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline after '(' in a function call if '(' and ')' are in +# different lines. +nl_func_call_start_multi_line = true # true/false + +# Whether to add a newline after each ',' in a function call if '(' and ')' +# are in different lines. +nl_func_call_args_multi_line = true # true/false + +# Whether to add a newline before ')' in a function call if '(' and ')' are in +# different lines. +nl_func_call_end_multi_line = true # true/false + +# Whether to respect nl_func_call_XXX option in case of closure args. +nl_func_call_args_multi_line_ignore_closures = false # true/false + +# Whether to add a newline after '<' of a template parameter list. +nl_template_start = false # true/false + +# Whether to add a newline after each ',' in a template parameter list. +nl_template_args = false # true/false + +# Whether to add a newline before '>' of a template parameter list. +nl_template_end = false # true/false + +# (OC) Whether to put each Objective-C message parameter on a separate line. +# See nl_oc_msg_leave_one_liner. +nl_oc_msg_args = false # true/false + +# (OC) Minimum number of Objective-C message parameters before applying nl_oc_msg_args. +nl_oc_msg_args_min_params = 0 # unsigned number + +# (OC) Max code width of Objective-C message before applying nl_oc_msg_args. +nl_oc_msg_args_max_code_width = 0 # unsigned number + +# Add or remove newline between function signature and '{'. +nl_fdef_brace = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline between function signature and '{', +# if signature ends with ')'. Overrides nl_fdef_brace. +nl_fdef_brace_cond = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline before '{' opening brace +nl_before_opening_brace_func_class_def = force # ignore/add/remove/force/not_defined + +# Add or remove newline between C++11 lambda signature and '{'. +nl_cpp_ldef_brace = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'return' and the return expression. +nl_return_expr = remove # ignore/add/remove/force/not_defined + +# Add or remove newline between 'throw' and the throw expression. +nl_throw_expr = remove # ignore/add/remove/force/not_defined + +# Whether to add a newline after semicolons, except in 'for' statements. +nl_after_semicolon = true # true/false + +# (Java) Add or remove newline between the ')' and '{{' of the double brace +# initializer. +nl_paren_dbrace_open = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline after the type in an unnamed temporary +# direct-list-initialization, better: +# before a direct-list-initialization. +nl_type_brace_init_lst = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline after the open brace in an unnamed temporary +# direct-list-initialization. +nl_type_brace_init_lst_open = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline before the close brace in an unnamed temporary +# direct-list-initialization. +nl_type_brace_init_lst_close = ignore # ignore/add/remove/force/not_defined + +# Whether to add a newline before '{'. +nl_before_brace_open = false # true/false + +# Whether to add a newline after '{'. +nl_after_brace_open = false # true/false + +# Whether to add a newline between the open brace and a trailing single-line +# comment. Requires nl_after_brace_open=true. +nl_after_brace_open_cmt = false # true/false + +# Whether to add a newline after a virtual brace open with a non-empty body. +# These occur in un-braced if/while/do/for statement bodies. +nl_after_vbrace_open = false # true/false + +# Whether to add a newline after a virtual brace open with an empty body. +# These occur in un-braced if/while/do/for statement bodies. +nl_after_vbrace_open_empty = false # true/false + +# Whether to add a newline after '}'. Does not apply if followed by a +# necessary ';'. +nl_after_brace_close = true # true/false + +# Whether to add a newline after a virtual brace close, +# as in 'if (foo) a++; return;'. +nl_after_vbrace_close = true # true/false + +# Add or remove newline between the close brace and identifier, +# as in 'struct { int a; } b;'. Affects enumerations, unions and +# structures. If set to ignore, uses nl_after_brace_close. +nl_brace_struct_var = ignore # ignore/add/remove/force/not_defined + +# Whether to alter newlines in '#define' macros. +nl_define_macro = false # true/false + +# Whether to alter newlines between consecutive parenthesis closes. The number +# of closing parentheses in a line will depend on respective open parenthesis +# lines. +nl_squeeze_paren_close = false # true/false + +# Whether to remove blanks after '#ifxx' and '#elxx', or before '#elxx' and +# '#endif'. Does not affect top-level #ifdefs. +nl_squeeze_ifdef = false # true/false + +# Makes the nl_squeeze_ifdef option affect the top-level #ifdefs as well. +nl_squeeze_ifdef_top_level = false # true/false + +# Add or remove blank line before 'if'. +nl_before_if = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line after 'if' statement. Add/Force work only if the +# next token is not a closing brace. +nl_after_if = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line before 'for'. +nl_before_for = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line after 'for' statement. +nl_after_for = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line before 'while'. +nl_before_while = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line after 'while' statement. +nl_after_while = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line before 'switch'. +nl_before_switch = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line after 'switch' statement. +nl_after_switch = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line before 'synchronized'. +nl_before_synchronized = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line after 'synchronized' statement. +nl_after_synchronized = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line before 'do'. +nl_before_do = ignore # ignore/add/remove/force/not_defined + +# Add or remove blank line after 'do/while' statement. +nl_after_do = ignore # ignore/add/remove/force/not_defined + +# Ignore nl_before_{if,for,switch,do,synchronized} if the control +# statement is immediately after a case statement. +# if nl_before_{if,for,switch,do} is set to remove, this option +# does nothing. +nl_before_ignore_after_case = false # true/false + +# Whether to put a blank line before 'return' statements, unless after an open +# brace. +nl_before_return = false # true/false + +# Whether to put a blank line after 'return' statements, unless followed by a +# close brace. +nl_after_return = false # true/false + +# Whether to put a blank line before a member '.' or '->' operators. +nl_before_member = ignore # ignore/add/remove/force/not_defined + +# (Java) Whether to put a blank line after a member '.' or '->' operators. +nl_after_member = ignore # ignore/add/remove/force/not_defined + +# Whether to double-space commented-entries in 'struct'/'union'/'enum'. +nl_ds_struct_enum_cmt = false # true/false + +# Whether to force a newline before '}' of a 'struct'/'union'/'enum'. +# (Lower priority than eat_blanks_before_close_brace.) +nl_ds_struct_enum_close_brace = false # true/false + +# Add or remove newline before or after (depending on pos_class_colon) a class +# colon, as in 'class Foo : public Bar'. +nl_class_colon = ignore # ignore/add/remove/force/not_defined + +# Add or remove newline around a class constructor colon. The exact position +# depends on nl_constr_init_args, pos_constr_colon and pos_constr_comma. +nl_constr_colon = force # ignore/add/remove/force/not_defined + +# Whether to collapse a two-line namespace, like 'namespace foo\n{ decl; }' +# into a single line. If true, prevents other brace newline rules from turning +# such code into four lines. If true, it also preserves one-liner namespaces. +nl_namespace_two_to_one_liner = false # true/false + +# Whether to remove a newline in simple unbraced if statements, turning them +# into one-liners, as in 'if(b)\n i++;' => 'if(b) i++;'. +nl_create_if_one_liner = false # true/false + +# Whether to remove a newline in simple unbraced for statements, turning them +# into one-liners, as in 'for (...)\n stmt;' => 'for (...) stmt;'. +nl_create_for_one_liner = false # true/false + +# Whether to remove a newline in simple unbraced while statements, turning +# them into one-liners, as in 'while (expr)\n stmt;' => 'while (expr) stmt;'. +nl_create_while_one_liner = false # true/false + +# Whether to collapse a function definition whose body (not counting braces) +# is only one line so that the entire definition (prototype, braces, body) is +# a single line. +nl_create_func_def_one_liner = false # true/false + +# Whether to split one-line simple list definitions into three lines by +# adding newlines, as in 'int a[12] = { 0 };'. +nl_create_list_one_liner = false # true/false + +# Whether to split one-line simple unbraced if statements into two lines by +# adding a newline, as in 'if(b) i++;'. +nl_split_if_one_liner = false # true/false + +# Whether to split one-line simple unbraced for statements into two lines by +# adding a newline, as in 'for (...) stmt;'. +nl_split_for_one_liner = false # true/false + +# Whether to split one-line simple unbraced while statements into two lines by +# adding a newline, as in 'while (expr) stmt;'. +nl_split_while_one_liner = false # true/false + +# Don't add a newline before a cpp-comment in a parameter list of a function +# call. +donot_add_nl_before_cpp_comment = false # true/false + +# +# Blank line options +# + +# The maximum number of consecutive newlines (3 = 2 blank lines). +nl_max = 2 # unsigned number + +# The maximum number of consecutive newlines in a function. +nl_max_blank_in_func = 2 # unsigned number + +# The number of newlines inside an empty function body. +# This option overrides eat_blanks_after_open_brace and +# eat_blanks_before_close_brace, but is ignored when +# nl_collapse_empty_body_functions=true +nl_inside_empty_func = 0 # unsigned number + +# The number of newlines before a function prototype. +nl_before_func_body_proto = 0 # unsigned number + +# The number of newlines before a multi-line function definition. Where +# applicable, this option is overridden with eat_blanks_after_open_brace=true +nl_before_func_body_def = 0 # unsigned number + +# The number of newlines before a class constructor/destructor prototype. +nl_before_func_class_proto = 0 # unsigned number + +# The number of newlines before a class constructor/destructor definition. +nl_before_func_class_def = 0 # unsigned number + +# The number of newlines after a function prototype. +nl_after_func_proto = 0 # unsigned number + +# The number of newlines after a function prototype, if not followed by +# another function prototype. +nl_after_func_proto_group = 2 # unsigned number + +# The number of newlines after a class constructor/destructor prototype. +nl_after_func_class_proto = 0 # unsigned number + +# The number of newlines after a class constructor/destructor prototype, +# if not followed by another constructor/destructor prototype. +nl_after_func_class_proto_group = 2 # unsigned number + +# Whether one-line method definitions inside a class body should be treated +# as if they were prototypes for the purposes of adding newlines. +# +# Requires nl_class_leave_one_liners=true. Overrides nl_before_func_body_def +# and nl_before_func_class_def for one-liners. +nl_class_leave_one_liner_groups = false # true/false + +# The number of newlines after '}' of a multi-line function body. +nl_after_func_body = 2 # unsigned number + +# The number of newlines after '}' of a multi-line function body in a class +# declaration. Also affects class constructors/destructors. +# +# Overrides nl_after_func_body. +nl_after_func_body_class = 2 # unsigned number + +# The number of newlines after '}' of a single line function body. Also +# affects class constructors/destructors. +# +# Overrides nl_after_func_body and nl_after_func_body_class. +nl_after_func_body_one_liner = 2 # unsigned number + +# The number of newlines before a block of typedefs. If nl_after_access_spec +# is non-zero, that option takes precedence. +# +# 0: No change (default). +nl_typedef_blk_start = 0 # unsigned number + +# The number of newlines after a block of typedefs. +# +# 0: No change (default). +nl_typedef_blk_end = 0 # unsigned number + +# The maximum number of consecutive newlines within a block of typedefs. +# +# 0: No change (default). +nl_typedef_blk_in = 0 # unsigned number + +# The minimum number of blank lines after a block of variable definitions +# at the top of a function body. If any preprocessor directives appear +# between the opening brace of the function and the variable block, then +# it is considered as not at the top of the function.Newlines are added +# before trailing preprocessor directives, if any exist. +# +# 0: No change (default). +nl_var_def_blk_end_func_top = 0 # unsigned number + +# The minimum number of empty newlines before a block of variable definitions +# not at the top of a function body. If nl_after_access_spec is non-zero, +# that option takes precedence. Newlines are not added at the top of the +# file or just after an opening brace. Newlines are added above any +# preprocessor directives before the block. +# +# 0: No change (default). +nl_var_def_blk_start = 0 # unsigned number + +# The minimum number of empty newlines after a block of variable definitions +# not at the top of a function body. Newlines are not added if the block +# is at the bottom of the file or just before a preprocessor directive. +# +# 0: No change (default). +nl_var_def_blk_end = 0 # unsigned number + +# The maximum number of consecutive newlines within a block of variable +# definitions. +# +# 0: No change (default). +nl_var_def_blk_in = 0 # unsigned number + +# The minimum number of newlines before a multi-line comment. +# Doesn't apply if after a brace open or another multi-line comment. +nl_before_block_comment = 0 # unsigned number + +# The minimum number of newlines before a single-line C comment. +# Doesn't apply if after a brace open or other single-line C comments. +nl_before_c_comment = 0 # unsigned number + +# The minimum number of newlines before a CPP comment. +# Doesn't apply if after a brace open or other CPP comments. +nl_before_cpp_comment = 0 # unsigned number + +# Whether to force a newline after a multi-line comment. +nl_after_multiline_comment = false # true/false + +# Whether to force a newline after a label's colon. +nl_after_label_colon = false # true/false + +# The number of newlines before a struct definition. +nl_before_struct = 0 # unsigned number + +# The number of newlines after '}' or ';' of a struct/enum/union definition. +nl_after_struct = 0 # unsigned number + +# The number of newlines before a class definition. +nl_before_class = 0 # unsigned number + +# The number of newlines after '}' or ';' of a class definition. +nl_after_class = 0 # unsigned number + +# The number of newlines before a namespace. +nl_before_namespace = 0 # unsigned number + +# The number of newlines after '{' of a namespace. This also adds newlines +# before the matching '}'. +# +# 0: Apply eat_blanks_after_open_brace or eat_blanks_before_close_brace if +# applicable, otherwise no change. +# +# Overrides eat_blanks_after_open_brace and eat_blanks_before_close_brace. +nl_inside_namespace = 0 # unsigned number + +# The number of newlines after '}' of a namespace. +nl_after_namespace = 0 # unsigned number + +# The number of newlines before an access specifier label. This also includes +# the Qt-specific 'signals:' and 'slots:'. Will not change the newline count +# if after a brace open. +# +# 0: No change (default). +nl_before_access_spec = 2 # unsigned number + +# The number of newlines after an access specifier label. This also includes +# the Qt-specific 'signals:' and 'slots:'. Will not change the newline count +# if after a brace open. +# +# 0: No change (default). +# +# Overrides nl_typedef_blk_start and nl_var_def_blk_start. +nl_after_access_spec = 1 # unsigned number + +# The number of newlines between a function definition and the function +# comment, as in '// comment\n void foo() {...}'. +# +# 0: No change (default). +nl_comment_func_def = 0 # unsigned number + +# The number of newlines after a try-catch-finally block that isn't followed +# by a brace close. +# +# 0: No change (default). +nl_after_try_catch_finally = 0 # unsigned number + +# (C#) The number of newlines before and after a property, indexer or event +# declaration. +# +# 0: No change (default). +nl_around_cs_property = 0 # unsigned number + +# (C#) The number of newlines between the get/set/add/remove handlers. +# +# 0: No change (default). +nl_between_get_set = 0 # unsigned number + +# (C#) Add or remove newline between property and the '{'. +nl_property_brace = ignore # ignore/add/remove/force/not_defined + +# Whether to remove blank lines after '{'. +eat_blanks_after_open_brace = true # true/false + +# Whether to remove blank lines before '}'. +eat_blanks_before_close_brace = true # true/false + +# How aggressively to remove extra newlines not in preprocessor. +# +# 0: No change (default) +# 1: Remove most newlines not handled by other config +# 2: Remove all newlines and reformat completely by config +nl_remove_extra_newlines = 2 # unsigned number + +# (Java) Add or remove newline after an annotation statement. Only affects +# annotations that are after a newline. +nl_after_annotation = ignore # ignore/add/remove/force/not_defined + +# (Java) Add or remove newline between two annotations. +nl_between_annotation = ignore # ignore/add/remove/force/not_defined + +# The number of newlines before a whole-file #ifdef. +# +# 0: No change (default). +nl_before_whole_file_ifdef = 0 # unsigned number + +# The number of newlines after a whole-file #ifdef. +# +# 0: No change (default). +nl_after_whole_file_ifdef = 0 # unsigned number + +# The number of newlines before a whole-file #endif. +# +# 0: No change (default). +nl_before_whole_file_endif = 0 # unsigned number + +# The number of newlines after a whole-file #endif. +# +# 0: No change (default). +nl_after_whole_file_endif = 0 # unsigned number + +# +# Positioning options +# + +# The position of arithmetic operators in wrapped expressions. +pos_arith = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of assignment in wrapped expressions. Do not affect '=' +# followed by '{'. +pos_assign = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of Boolean operators in wrapped expressions. +pos_bool = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of comparison operators in wrapped expressions. +pos_compare = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of conditional operators, as in the '?' and ':' of +# 'expr ? stmt : stmt', in wrapped expressions. +pos_conditional = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of the comma in wrapped expressions. +pos_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of the comma in enum entries. +pos_enum_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of the comma in the base class list if there is more than one +# line. Affects nl_class_init_args. +pos_class_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of the comma in the constructor initialization list. +# Related to nl_constr_colon, nl_constr_init_args and pos_constr_colon. +pos_constr_comma = trail_force # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of trailing/leading class colon, between class and base class +# list. Affects nl_class_colon. +pos_class_colon = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of colons between constructor and member initialization. +# Related to nl_constr_colon, nl_constr_init_args and pos_constr_comma. +pos_constr_colon = trail_force # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of shift operators in wrapped expressions. +pos_shift = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# +# Line splitting options +# + +# Try to limit code width to N columns. +code_width = 0 # unsigned number + +# Whether to fully split long 'for' statements at semi-colons. +ls_for_split_full = false # true/false + +# Whether to fully split long function prototypes/calls at commas. +# The option ls_code_width has priority over the option ls_func_split_full. +ls_func_split_full = false # true/false + +# Whether to split lines as close to code_width as possible and ignore some +# groupings. +# The option ls_code_width has priority over the option ls_func_split_full. +ls_code_width = false # true/false + +# +# Code alignment options (not left column spaces/tabs) +# + +# Whether to keep non-indenting tabs. +align_keep_tabs = false # true/false + +# Whether to use tabs for aligning. +align_with_tabs = false # true/false + +# Whether to bump out to the next tab when aligning. +align_on_tabstop = false # true/false + +# Whether to right-align numbers. +align_number_right = false # true/false + +# Whether to keep whitespace not required for alignment. +align_keep_extra_space = false # true/false + +# Whether to align variable definitions in prototypes and functions. +align_func_params = false # true/false + +# The span for aligning parameter definitions in function on parameter name. +# +# 0: Don't align (default). +align_func_params_span = 0 # unsigned number + +# The threshold for aligning function parameter definitions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_func_params_thresh = 0 # number + +# The gap for aligning function parameter definitions. +align_func_params_gap = 0 # unsigned number + +# The span for aligning constructor value. +# +# 0: Don't align (default). +align_constr_value_span = 0 # unsigned number + +# The threshold for aligning constructor value. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_constr_value_thresh = 0 # number + +# The gap for aligning constructor value. +align_constr_value_gap = 0 # unsigned number + +# Whether to align parameters in single-line functions that have the same +# name. The function names must already be aligned with each other. +align_same_func_call_params = false # true/false + +# The span for aligning function-call parameters for single line functions. +# +# 0: Don't align (default). +align_same_func_call_params_span = 0 # unsigned number + +# The threshold for aligning function-call parameters for single line +# functions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_same_func_call_params_thresh = 0 # number + +# The span for aligning variable definitions. +# +# 0: Don't align (default). +align_var_def_span = 0 # unsigned number + +# How to consider (or treat) the '*' in the alignment of variable definitions. +# +# 0: Part of the type 'void * foo;' (default) +# 1: Part of the variable 'void *foo;' +# 2: Dangling 'void *foo;' +# Dangling: the '*' will not be taken into account when aligning. +align_var_def_star_style = 0 # unsigned number + +# How to consider (or treat) the '&' in the alignment of variable definitions. +# +# 0: Part of the type 'long & foo;' (default) +# 1: Part of the variable 'long &foo;' +# 2: Dangling 'long &foo;' +# Dangling: the '&' will not be taken into account when aligning. +align_var_def_amp_style = 0 # unsigned number + +# The threshold for aligning variable definitions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_var_def_thresh = 0 # number + +# The gap for aligning variable definitions. +align_var_def_gap = 0 # unsigned number + +# Whether to align the colon in struct bit fields. +align_var_def_colon = false # true/false + +# The gap for aligning the colon in struct bit fields. +align_var_def_colon_gap = 0 # unsigned number + +# Whether to align any attribute after the variable name. +align_var_def_attribute = false # true/false + +# Whether to align inline struct/enum/union variable definitions. +align_var_def_inline = false # true/false + +# The span for aligning on '=' in assignments. +# +# 0: Don't align (default). +align_assign_span = 0 # unsigned number + +# The span for aligning on '=' in function prototype modifier. +# +# 0: Don't align (default). +align_assign_func_proto_span = 0 # unsigned number + +# The threshold for aligning on '=' in assignments. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_assign_thresh = 0 # number + +# Whether to align on the left most assignment when multiple +# definitions are found on the same line. +# Depends on 'align_assign_span' and 'align_assign_thresh' settings. +align_assign_on_multi_var_defs = false # true/false + +# The span for aligning on '{' in braced init list. +# +# 0: Don't align (default). +align_braced_init_list_span = 0 # unsigned number + +# The threshold for aligning on '{' in braced init list. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_braced_init_list_thresh = 0 # number + +# How to apply align_assign_span to function declaration "assignments", i.e. +# 'virtual void foo() = 0' or '~foo() = {default|delete}'. +# +# 0: Align with other assignments (default) +# 1: Align with each other, ignoring regular assignments +# 2: Don't align +align_assign_decl_func = 0 # unsigned number + +# The span for aligning on '=' in enums. +# +# 0: Don't align (default). +align_enum_equ_span = 0 # unsigned number + +# The threshold for aligning on '=' in enums. +# Use a negative number for absolute thresholds. +# +# 0: no limit (default). +align_enum_equ_thresh = 0 # number + +# The span for aligning class member definitions. +# +# 0: Don't align (default). +align_var_class_span = 0 # unsigned number + +# The threshold for aligning class member definitions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_var_class_thresh = 0 # number + +# The gap for aligning class member definitions. +align_var_class_gap = 0 # unsigned number + +# The span for aligning struct/union member definitions. +# +# 0: Don't align (default). +align_var_struct_span = 0 # unsigned number + +# The threshold for aligning struct/union member definitions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_var_struct_thresh = 0 # number + +# The gap for aligning struct/union member definitions. +align_var_struct_gap = 0 # unsigned number + +# The span for aligning struct initializer values. +# +# 0: Don't align (default). +align_struct_init_span = 0 # unsigned number + +# The span for aligning single-line typedefs. +# +# 0: Don't align (default). +align_typedef_span = 0 # unsigned number + +# The minimum space between the type and the synonym of a typedef. +align_typedef_gap = 0 # unsigned number + +# How to align typedef'd functions with other typedefs. +# +# 0: Don't mix them at all (default) +# 1: Align the open parenthesis with the types +# 2: Align the function type name with the other type names +align_typedef_func = 0 # unsigned number + +# How to consider (or treat) the '*' in the alignment of typedefs. +# +# 0: Part of the typedef type, 'typedef int * pint;' (default) +# 1: Part of type name: 'typedef int *pint;' +# 2: Dangling: 'typedef int *pint;' +# Dangling: the '*' will not be taken into account when aligning. +align_typedef_star_style = 0 # unsigned number + +# How to consider (or treat) the '&' in the alignment of typedefs. +# +# 0: Part of the typedef type, 'typedef int & intref;' (default) +# 1: Part of type name: 'typedef int &intref;' +# 2: Dangling: 'typedef int &intref;' +# Dangling: the '&' will not be taken into account when aligning. +align_typedef_amp_style = 0 # unsigned number + +# The span for aligning comments that end lines. +# +# 0: Don't align (default). +align_right_cmt_span = 0 # unsigned number + +# Minimum number of columns between preceding text and a trailing comment in +# order for the comment to qualify for being aligned. Must be non-zero to have +# an effect. +align_right_cmt_gap = 0 # unsigned number + +# If aligning comments, whether to mix with comments after '}' and #endif with +# less than three spaces before the comment. +align_right_cmt_mix = false # true/false + +# Whether to only align trailing comments that are at the same brace level. +align_right_cmt_same_level = false # true/false + +# Minimum column at which to align trailing comments. Comments which are +# aligned beyond this column, but which can be aligned in a lesser column, +# may be "pulled in". +# +# 0: Ignore (default). +align_right_cmt_at_col = 0 # unsigned number + +# The span for aligning function prototypes. +# +# 0: Don't align (default). +align_func_proto_span = 0 # unsigned number + +# How to consider (or treat) the '*' in the alignment of function prototypes. +# +# 0: Part of the type 'void * foo();' (default) +# 1: Part of the function 'void *foo();' +# 2: Dangling 'void *foo();' +# Dangling: the '*' will not be taken into account when aligning. +align_func_proto_star_style = 0 # unsigned number + +# How to consider (or treat) the '&' in the alignment of function prototypes. +# +# 0: Part of the type 'long & foo();' (default) +# 1: Part of the function 'long &foo();' +# 2: Dangling 'long &foo();' +# Dangling: the '&' will not be taken into account when aligning. +align_func_proto_amp_style = 0 # unsigned number + +# The threshold for aligning function prototypes. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_func_proto_thresh = 0 # number + +# Minimum gap between the return type and the function name. +align_func_proto_gap = 0 # unsigned number + +# Whether to align function prototypes on the 'operator' keyword instead of +# what follows. +align_on_operator = false # true/false + +# Whether to mix aligning prototype and variable declarations. If true, +# align_var_def_XXX options are used instead of align_func_proto_XXX options. +align_mix_var_proto = false # true/false + +# Whether to align single-line functions with function prototypes. +# Uses align_func_proto_span. +align_single_line_func = false # true/false + +# Whether to align the open brace of single-line functions. +# Requires align_single_line_func=true. Uses align_func_proto_span. +align_single_line_brace = false # true/false + +# Gap for align_single_line_brace. +align_single_line_brace_gap = 0 # unsigned number + +# (OC) The span for aligning Objective-C message specifications. +# +# 0: Don't align (default). +align_oc_msg_spec_span = 0 # unsigned number + +# Whether and how to align backslashes that split a macro onto multiple lines. +# This will not work right if the macro contains a multi-line comment. +# +# 0: Do nothing (default) +# 1: Align the backslashes in the column at the end of the longest line +# 2: Align with the backslash that is farthest to the left, or, if that +# backslash is farther left than the end of the longest line, at the end of +# the longest line +# 3: Align with the backslash that is farthest to the right +align_nl_cont = 0 # unsigned number + +# Whether to align macro functions and variables together. +align_pp_define_together = false # true/false + +# The span for aligning on '#define' bodies. +# +# =0: Don't align (default) +# >0: Number of lines (including comments) between blocks +align_pp_define_span = 0 # unsigned number + +# The minimum space between label and value of a preprocessor define. +align_pp_define_gap = 0 # unsigned number + +# Whether to align lines that start with '<<' with previous '<<'. +# +# Default: true +align_left_shift = true # true/false + +# Whether to align comma-separated statements following '<<' (as used to +# initialize Eigen matrices). +align_eigen_comma_init = false # true/false + +# Whether to align text after 'asm volatile ()' colons. +align_asm_colon = false # true/false + +# (OC) Span for aligning parameters in an Objective-C message call +# on the ':'. +# +# 0: Don't align. +align_oc_msg_colon_span = 0 # unsigned number + +# (OC) Whether to always align with the first parameter, even if it is too +# short. +align_oc_msg_colon_first = false # true/false + +# (OC) Whether to align parameters in an Objective-C '+' or '-' declaration +# on the ':'. +align_oc_decl_colon = false # true/false + +# (OC) Whether to not align parameters in an Objectve-C message call if first +# colon is not on next line of the message call (the same way Xcode does +# alignment) +align_oc_msg_colon_xcode_like = false # true/false + +# +# Comment modification options +# + +# Try to wrap comments at N columns. +cmt_width = 0 # unsigned number + +# How to reflow comments. +# +# 0: No reflowing (apart from the line wrapping due to cmt_width) (default) +# 1: No touching at all +# 2: Full reflow (enable cmt_indent_multi for indent with line wrapping due to cmt_width) +cmt_reflow_mode = 0 # unsigned number + +# Path to a file that contains regular expressions describing patterns for +# which the end of one line and the beginning of the next will be folded into +# the same sentence or paragraph during full comment reflow. The regular +# expressions are described using ECMAScript syntax. The syntax for this +# specification is as follows, where "..." indicates the custom regular +# expression and "n" indicates the nth end_of_prev_line_regex and +# beg_of_next_line_regex regular expression pair: +# +# end_of_prev_line_regex[1] = "...$" +# beg_of_next_line_regex[1] = "^..." +# end_of_prev_line_regex[2] = "...$" +# beg_of_next_line_regex[2] = "^..." +# . +# . +# . +# end_of_prev_line_regex[n] = "...$" +# beg_of_next_line_regex[n] = "^..." +# +# Note that use of this option overrides the default reflow fold regular +# expressions, which are internally defined as follows: +# +# end_of_prev_line_regex[1] = "[\w,\]\)]$" +# beg_of_next_line_regex[1] = "^[\w,\[\(]" +# end_of_prev_line_regex[2] = "\.$" +# beg_of_next_line_regex[2] = "^[A-Z]" +cmt_reflow_fold_regex_file = "" # string + +# Whether to indent wrapped lines to the start of the encompassing paragraph +# during full comment reflow (cmt_reflow_mode = 2). Overrides the value +# specified by cmt_sp_after_star_cont. +# +# Note that cmt_align_doxygen_javadoc_tags overrides this option for +# paragraphs associated with javadoc tags +cmt_reflow_indent_to_paragraph_start = false # true/false + +# Whether to convert all tabs to spaces in comments. If false, tabs in +# comments are left alone, unless used for indenting. +cmt_convert_tab_to_spaces = false # true/false + +# Whether to apply changes to multi-line comments, including cmt_width, +# keyword substitution and leading chars. +# +# Default: true +cmt_indent_multi = true # true/false + +# Whether to align doxygen javadoc-style tags ('@param', '@return', etc.) +# and corresponding fields such that groups of consecutive block tags, +# parameter names, and descriptions align with one another. Overrides that +# which is specified by the cmt_sp_after_star_cont. If cmt_width > 0, it may +# be necessary to enable cmt_indent_multi and set cmt_reflow_mode = 2 +# in order to achieve the desired alignment for line-wrapping. +cmt_align_doxygen_javadoc_tags = false # true/false + +# The number of spaces to insert after the star and before doxygen +# javadoc-style tags (@param, @return, etc). Requires enabling +# cmt_align_doxygen_javadoc_tags. Overrides that which is specified by the +# cmt_sp_after_star_cont. +# +# Default: 1 +cmt_sp_before_doxygen_javadoc_tags = 1 # unsigned number + +# Whether to change trailing, single-line c-comments into cpp-comments. +cmt_trailing_single_line_c_to_cpp = false # true/false + +# Whether to group c-comments that look like they are in a block. +cmt_c_group = false # true/false + +# Whether to put an empty '/*' on the first line of the combined c-comment. +cmt_c_nl_start = false # true/false + +# Whether to add a newline before the closing '*/' of the combined c-comment. +cmt_c_nl_end = false # true/false + +# Whether to change cpp-comments into c-comments. +cmt_cpp_to_c = false # true/false + +# Whether to group cpp-comments that look like they are in a block. Only +# meaningful if cmt_cpp_to_c=true. +cmt_cpp_group = false # true/false + +# Whether to put an empty '/*' on the first line of the combined cpp-comment +# when converting to a c-comment. +# +# Requires cmt_cpp_to_c=true and cmt_cpp_group=true. +cmt_cpp_nl_start = false # true/false + +# Whether to add a newline before the closing '*/' of the combined cpp-comment +# when converting to a c-comment. +# +# Requires cmt_cpp_to_c=true and cmt_cpp_group=true. +cmt_cpp_nl_end = false # true/false + +# Whether to put a star on subsequent comment lines. +cmt_star_cont = false # true/false + +# The number of spaces to insert at the start of subsequent comment lines. +cmt_sp_before_star_cont = 0 # unsigned number + +# The number of spaces to insert after the star on subsequent comment lines. +cmt_sp_after_star_cont = 0 # unsigned number + +# For multi-line comments with a '*' lead, remove leading spaces if the first +# and last lines of the comment are the same length. +# +# Default: true +cmt_multi_check_last = true # true/false + +# For multi-line comments with a '*' lead, remove leading spaces if the first +# and last lines of the comment are the same length AND if the length is +# bigger as the first_len minimum. +# +# Default: 4 +cmt_multi_first_len_minimum = 4 # unsigned number + +# Path to a file that contains text to insert at the beginning of a file if +# the file doesn't start with a C/C++ comment. If the inserted text contains +# '$(filename)', that will be replaced with the current file's name. +cmt_insert_file_header = "" # string + +# Path to a file that contains text to insert at the end of a file if the +# file doesn't end with a C/C++ comment. If the inserted text contains +# '$(filename)', that will be replaced with the current file's name. +cmt_insert_file_footer = "" # string + +# Path to a file that contains text to insert before a function definition if +# the function isn't preceded by a C/C++ comment. If the inserted text +# contains '$(function)', '$(javaparam)' or '$(fclass)', these will be +# replaced with, respectively, the name of the function, the javadoc '@param' +# and '@return' stuff, or the name of the class to which the member function +# belongs. +cmt_insert_func_header = "" # string + +# Path to a file that contains text to insert before a class if the class +# isn't preceded by a C/C++ comment. If the inserted text contains '$(class)', +# that will be replaced with the class name. +cmt_insert_class_header = "" # string + +# Path to a file that contains text to insert before an Objective-C message +# specification, if the method isn't preceded by a C/C++ comment. If the +# inserted text contains '$(message)' or '$(javaparam)', these will be +# replaced with, respectively, the name of the function, or the javadoc +# '@param' and '@return' stuff. +cmt_insert_oc_msg_header = "" # string + +# Whether a comment should be inserted if a preprocessor is encountered when +# stepping backwards from a function name. +# +# Applies to cmt_insert_oc_msg_header, cmt_insert_func_header and +# cmt_insert_class_header. +cmt_insert_before_preproc = false # true/false + +# Whether a comment should be inserted if a function is declared inline to a +# class definition. +# +# Applies to cmt_insert_func_header. +# +# Default: true +cmt_insert_before_inlines = true # true/false + +# Whether a comment should be inserted if the function is a class constructor +# or destructor. +# +# Applies to cmt_insert_func_header. +cmt_insert_before_ctor_dtor = false # true/false + +# +# Code modifying options (non-whitespace) +# + +# Add or remove braces on a single-line 'do' statement. +mod_full_brace_do = ignore # ignore/add/remove/force/not_defined + +# Add or remove braces on a single-line 'for' statement. +mod_full_brace_for = ignore # ignore/add/remove/force/not_defined + +# (Pawn) Add or remove braces on a single-line function definition. +mod_full_brace_function = ignore # ignore/add/remove/force/not_defined + +# Add or remove braces on a single-line 'if' statement. Braces will not be +# removed if the braced statement contains an 'else'. +mod_full_brace_if = ignore # ignore/add/remove/force/not_defined + +# Whether to enforce that all blocks of an 'if'/'else if'/'else' chain either +# have, or do not have, braces. Overrides mod_full_brace_if. +# +# 0: Don't override mod_full_brace_if +# 1: Add braces to all blocks if any block needs braces and remove braces if +# they can be removed from all blocks +# 2: Add braces to all blocks if any block already has braces, regardless of +# whether it needs them +# 3: Add braces to all blocks if any block needs braces and remove braces if +# they can be removed from all blocks, except if all blocks have braces +# despite none needing them +mod_full_brace_if_chain = 0 # unsigned number + +# Whether to add braces to all blocks of an 'if'/'else if'/'else' chain. +# If true, mod_full_brace_if_chain will only remove braces from an 'if' that +# does not have an 'else if' or 'else'. +mod_full_brace_if_chain_only = false # true/false + +# Add or remove braces on single-line 'while' statement. +mod_full_brace_while = ignore # ignore/add/remove/force/not_defined + +# Add or remove braces on single-line 'using ()' statement. +mod_full_brace_using = ignore # ignore/add/remove/force/not_defined + +# Don't remove braces around statements that span N newlines +mod_full_brace_nl = 0 # unsigned number + +# Whether to prevent removal of braces from 'if'/'for'/'while'/etc. blocks +# which span multiple lines. +# +# Affects: +# mod_full_brace_for +# mod_full_brace_if +# mod_full_brace_if_chain +# mod_full_brace_if_chain_only +# mod_full_brace_while +# mod_full_brace_using +# +# Does not affect: +# mod_full_brace_do +# mod_full_brace_function +mod_full_brace_nl_block_rem_mlcond = false # true/false + +# Add or remove unnecessary parentheses on 'return' statement. +mod_paren_on_return = ignore # ignore/add/remove/force/not_defined + +# Add or remove unnecessary parentheses on 'throw' statement. +mod_paren_on_throw = ignore # ignore/add/remove/force/not_defined + +# (Pawn) Whether to change optional semicolons to real semicolons. +mod_pawn_semicolon = false # true/false + +# Whether to fully parenthesize Boolean expressions in 'while' and 'if' +# statement, as in 'if (a && b > c)' => 'if (a && (b > c))'. +mod_full_paren_if_bool = false # true/false + +# Whether to fully parenthesize Boolean expressions after '=' +# statement, as in 'x = a && b > c;' => 'x = (a && (b > c));'. +mod_full_paren_assign_bool = false # true/false + +# Whether to fully parenthesize Boolean expressions after '=' +# statement, as in 'return a && b > c;' => 'return (a && (b > c));'. +mod_full_paren_return_bool = false # true/false + +# Whether to remove superfluous semicolons. +mod_remove_extra_semicolon = false # true/false + +# Whether to remove duplicate include. +mod_remove_duplicate_include = false # true/false + +# the following options (mod_XX_closebrace_comment) use different comment, +# depending of the setting of the next option. +# false: Use the c comment (default) +# true : Use the cpp comment +mod_add_force_c_closebrace_comment = false # true/false + +# If a function body exceeds the specified number of newlines and doesn't have +# a comment after the close brace, a comment will be added. +mod_add_long_function_closebrace_comment = 0 # unsigned number + +# If a namespace body exceeds the specified number of newlines and doesn't +# have a comment after the close brace, a comment will be added. +mod_add_long_namespace_closebrace_comment = 0 # unsigned number + +# If a class body exceeds the specified number of newlines and doesn't have a +# comment after the close brace, a comment will be added. +mod_add_long_class_closebrace_comment = 0 # unsigned number + +# If a switch body exceeds the specified number of newlines and doesn't have a +# comment after the close brace, a comment will be added. +mod_add_long_switch_closebrace_comment = 0 # unsigned number + +# If an #ifdef body exceeds the specified number of newlines and doesn't have +# a comment after the #endif, a comment will be added. +mod_add_long_ifdef_endif_comment = 0 # unsigned number + +# If an #ifdef or #else body exceeds the specified number of newlines and +# doesn't have a comment after the #else, a comment will be added. +mod_add_long_ifdef_else_comment = 0 # unsigned number + +# Whether to take care of the case by the mod_sort_xx options. +mod_sort_case_sensitive = false # true/false + +# Whether to sort consecutive single-line 'import' statements. +mod_sort_import = false # true/false + +# (C#) Whether to sort consecutive single-line 'using' statements. +mod_sort_using = false # true/false + +# Whether to sort consecutive single-line '#include' statements (C/C++) and +# '#import' statements (Objective-C). Be aware that this has the potential to +# break your code if your includes/imports have ordering dependencies. +mod_sort_include = false # true/false + +# Whether to prioritize '#include' and '#import' statements that contain +# filename without extension when sorting is enabled. +mod_sort_incl_import_prioritize_filename = false # true/false + +# Whether to prioritize '#include' and '#import' statements that does not +# contain extensions when sorting is enabled. +mod_sort_incl_import_prioritize_extensionless = false # true/false + +# Whether to prioritize '#include' and '#import' statements that contain +# angle over quotes when sorting is enabled. +mod_sort_incl_import_prioritize_angle_over_quotes = false # true/false + +# Whether to ignore file extension in '#include' and '#import' statements +# for sorting comparison. +mod_sort_incl_import_ignore_extension = false # true/false + +# Whether to group '#include' and '#import' statements when sorting is enabled. +mod_sort_incl_import_grouping_enabled = false # true/false + +# Whether to move a 'break' that appears after a fully braced 'case' before +# the close brace, as in 'case X: { ... } break;' => 'case X: { ... break; }'. +mod_move_case_break = false # true/false + +# Whether to move a 'return' that appears after a fully braced 'case' before +# the close brace, as in 'case X: { ... } return;' => 'case X: { ... return; }'. +mod_move_case_return = false # true/false + +# Add or remove braces around a fully braced case statement. Will only remove +# braces if there are no variable declarations in the block. +mod_case_brace = ignore # ignore/add/remove/force/not_defined + +# Whether to remove a void 'return;' that appears as the last statement in a +# function. +mod_remove_empty_return = false # true/false + +# Add or remove the comma after the last value of an enumeration. +mod_enum_last_comma = ignore # ignore/add/remove/force/not_defined + +# Syntax to use for infinite loops. +# +# 0: Leave syntax alone (default) +# 1: Rewrite as `for(;;)` +# 2: Rewrite as `while(true)` +# 3: Rewrite as `do`...`while(true);` +# 4: Rewrite as `while(1)` +# 5: Rewrite as `do`...`while(1);` +# +# Infinite loops that do not already match one of these syntaxes are ignored. +# Other options that affect loop formatting will be applied after transforming +# the syntax. +mod_infinite_loop = 0 # unsigned number + +# Add or remove the 'int' keyword in 'int short'. +mod_int_short = ignore # ignore/add/remove/force/not_defined + +# Add or remove the 'int' keyword in 'short int'. +mod_short_int = ignore # ignore/add/remove/force/not_defined + +# Add or remove the 'int' keyword in 'int long'. +mod_int_long = ignore # ignore/add/remove/force/not_defined + +# Add or remove the 'int' keyword in 'long int'. +mod_long_int = ignore # ignore/add/remove/force/not_defined + +# Add or remove the 'int' keyword in 'int signed'. +mod_int_signed = ignore # ignore/add/remove/force/not_defined + +# Add or remove the 'int' keyword in 'signed int'. +mod_signed_int = ignore # ignore/add/remove/force/not_defined + +# Add or remove the 'int' keyword in 'int unsigned'. +mod_int_unsigned = ignore # ignore/add/remove/force/not_defined + +# Add or remove the 'int' keyword in 'unsigned int'. +mod_unsigned_int = ignore # ignore/add/remove/force/not_defined + +# If there is a situation where mod_int_* and mod_*_int would result in +# multiple int keywords, whether to keep the rightmost int (the default) or the +# leftmost int. +mod_int_prefer_int_on_left = false # true/false + +# (OC) Whether to organize the properties. If true, properties will be +# rearranged according to the mod_sort_oc_property_*_weight factors. +mod_sort_oc_properties = false # true/false + +# (OC) Weight of a class property modifier. +mod_sort_oc_property_class_weight = 0 # number + +# (OC) Weight of 'atomic' and 'nonatomic'. +mod_sort_oc_property_thread_safe_weight = 0 # number + +# (OC) Weight of 'readwrite' when organizing properties. +mod_sort_oc_property_readwrite_weight = 0 # number + +# (OC) Weight of a reference type specifier ('retain', 'copy', 'assign', +# 'weak', 'strong') when organizing properties. +mod_sort_oc_property_reference_weight = 0 # number + +# (OC) Weight of getter type ('getter=') when organizing properties. +mod_sort_oc_property_getter_weight = 0 # number + +# (OC) Weight of setter type ('setter=') when organizing properties. +mod_sort_oc_property_setter_weight = 0 # number + +# (OC) Weight of nullability type ('nullable', 'nonnull', 'null_unspecified', +# 'null_resettable') when organizing properties. +mod_sort_oc_property_nullability_weight = 0 # number + +# +# Preprocessor options +# + +# How to use tabs when indenting preprocessor code. +# +# -1: Use 'indent_with_tabs' setting (default) +# 0: Spaces only +# 1: Indent with tabs to brace level, align with spaces +# 2: Indent and align with tabs, using spaces when not on a tabstop +# +# Default: -1 +pp_indent_with_tabs = -1 # number + +# Add or remove indentation of preprocessor directives inside #if blocks +# at brace level 0 (file-level). +pp_indent = ignore # ignore/add/remove/force/not_defined + +# Whether to indent #if/#else/#endif at the brace level. If false, these are +# indented from column 1. +pp_indent_at_level = false # true/false + +# Whether to indent #if/#else/#endif at the parenthesis level if the brace +# level is 0. If false, these are indented from column 1. +pp_indent_at_level0 = false # true/false + +# Specifies the number of columns to indent preprocessors per level +# at brace level 0 (file-level). If pp_indent_at_level=false, also specifies +# the number of columns to indent preprocessors per level +# at brace level > 0 (function-level). +# +# Default: 1 +pp_indent_count = 1 # unsigned number + +# Add or remove space after # based on pp level of #if blocks. +pp_space_after = ignore # ignore/add/remove/force/not_defined + +# Sets the number of spaces per level added with pp_space_after. +pp_space_count = 0 # unsigned number + +# The indent for '#region' and '#endregion' in C# and '#pragma region' in +# C/C++. Negative values decrease indent down to the first column. +pp_indent_region = 0 # number + +# Whether to indent the code between #region and #endregion. +pp_region_indent_code = false # true/false + +# If pp_indent_at_level=true, sets the indent for #if, #else and #endif when +# not at file-level. Negative values decrease indent down to the first column. +# +# =0: Indent preprocessors using output_tab_size +# >0: Column at which all preprocessors will be indented +pp_indent_if = 0 # number + +# Whether to indent the code between #if, #else and #endif. +pp_if_indent_code = false # true/false + +# Whether to indent the body of an #if that encompasses all the code in the file. +pp_indent_in_guard = false # true/false + +# Whether to indent '#define' at the brace level. If false, these are +# indented from column 1. +pp_define_at_level = false # true/false + +# Whether to indent '#include' at the brace level. +pp_include_at_level = false # true/false + +# Whether to ignore the '#define' body while formatting. +pp_ignore_define_body = false # true/false + +# An offset value that controls the indentation of the body of a multiline #define. +# 'body' refers to all the lines of a multiline #define except the first line. +# Requires 'pp_ignore_define_body = false'. +# +# <0: Absolute column: the body indentation starts off at the specified column +# (ex. -3 ==> the body is indented starting from column 3) +# >=0: Relative to the column of the '#' of '#define' +# (ex. 3 ==> the body is indented starting 3 columns at the right of '#') +# +# Default: 8 +pp_multiline_define_body_indent = 8 # number + +# Whether to indent case statements between #if, #else, and #endif. +# Only applies to the indent of the preprocessor that the case statements +# directly inside of. +# +# Default: true +pp_indent_case = true # true/false + +# Whether to indent whole function definitions between #if, #else, and #endif. +# Only applies to the indent of the preprocessor that the function definition +# is directly inside of. +# +# Default: true +pp_indent_func_def = true # true/false + +# Whether to indent extern C blocks between #if, #else, and #endif. +# Only applies to the indent of the preprocessor that the extern block is +# directly inside of. +# +# Default: true +pp_indent_extern = true # true/false + +# How to indent braces directly inside #if, #else, and #endif. +# Requires pp_if_indent_code=true and only applies to the indent of the +# preprocessor that the braces are directly inside of. +# 0: No extra indent +# 1: Indent by one level +# -1: Preserve original indentation +# +# Default: 1 +pp_indent_brace = 1 # number + +# Whether to print warning messages for unbalanced #if and #else blocks. +# This will print a message in the following cases: +# - if an #ifdef block ends on a different indent level than +# where it started from. Example: +# +# #ifdef TEST +# int i; +# { +# int j; +# #endif +# +# - an #elif/#else block ends on a different indent level than +# the corresponding #ifdef block. Example: +# +# #ifdef TEST +# int i; +# #else +# } +# int j; +# #endif +pp_warn_unbalanced_if = false # true/false + +# +# Sort includes options +# + +# The regex for include category with priority 0. +include_category_0 = "" # string + +# The regex for include category with priority 1. +include_category_1 = "" # string + +# The regex for include category with priority 2. +include_category_2 = "" # string + +# +# Use or Do not Use options +# + +# true: indent_func_call_param will be used (default) +# false: indent_func_call_param will NOT be used +# +# Default: true +use_indent_func_call_param = true # true/false + +# The value of the indentation for a continuation line is calculated +# differently if the statement is: +# - a declaration: your case with QString fileName ... +# - an assignment: your case with pSettings = new QSettings( ... +# +# At the second case the indentation value might be used twice: +# - at the assignment +# - at the function call (if present) +# +# To prevent the double use of the indentation value, use this option with the +# value 'true'. +# +# true: indent_continue will be used only once +# false: indent_continue will be used every time (default) +# +# Requires indent_ignore_first_continue=false. +use_indent_continue_only_once = false # true/false + +# The indentation can be: +# - after the assignment, at the '[' character +# - at the beginning of the lambda body +# +# true: indentation will be at the beginning of the lambda body +# false: indentation will be after the assignment (default) +indent_cpp_lambda_only_once = false # true/false + +# Whether sp_after_angle takes precedence over sp_inside_fparen. This was the +# historic behavior, but is probably not the desired behavior, so this is off +# by default. +use_sp_after_angle_always = false # true/false + +# Whether to apply special formatting for Qt SIGNAL/SLOT macros. Essentially, +# this tries to format these so that they match Qt's normalized form (i.e. the +# result of QMetaObject::normalizedSignature), which can slightly improve the +# performance of the QObject::connect call, rather than how they would +# otherwise be formatted. +# +# See options_for_QT.cpp for details. +# +# Default: true +use_options_overriding_for_qt_macros = true # true/false + +# If true: the form feed character is removed from the list of whitespace +# characters. See https://en.cppreference.com/w/cpp/string/byte/isspace. +use_form_feed_no_more_as_whitespace_character = false # true/false + +# +# Warn levels - 1: error, 2: warning (default), 3: note +# + +# (C#) Warning is given if doing tab-to-\t replacement and we have found one +# in a C# verbatim string literal. +# +# Default: 2 +warn_level_tabs_found_in_verbatim_string_literals = 2 # unsigned number + +# Limit the number of loops. +# Used by uncrustify.cpp to exit from infinite loop. +# 0: no limit. +debug_max_number_of_loops = 0 # number + +# Set the number of the line to protocol; +# Used in the function prot_the_line if the 2. parameter is zero. +# 0: nothing protocol. +debug_line_number_to_protocol = 0 # number + +# Set the number of second(s) before terminating formatting the current file, +# 0: no timeout. +# only for linux +debug_timeout = 0 # number + +# Set the number of characters to be printed if the text is too long, +# 0: do not truncate. +debug_truncate = 0 # unsigned number + +# sort (or not) the tracking info. +# +# Default: true +debug_sort_the_tracks = true # true/false + +# decode (or not) the flags as a new line. +# only if the -p option is set. +debug_decode_the_flags = false # true/false + +# insert the number of the line at the beginning of each line +set_numbering_for_html_output = false # true/false + +# Meaning of the settings: +# Ignore - do not do any changes +# Add - makes sure there is 1 or more space/brace/newline/etc +# Force - makes sure there is exactly 1 space/brace/newline/etc, +# behaves like Add in some contexts +# Remove - removes space/brace/newline/etc +# +# +# - Token(s) can be treated as specific type(s) with the 'set' option: +# `set tokenType tokenString [tokenString...]` +# +# Example: +# `set BOOL __AND__ __OR__` +# +# tokenTypes are defined in src/token_enum.h, use them without the +# 'CT_' prefix: 'CT_BOOL' => 'BOOL' +# +# +# - Token(s) can be treated as type(s) with the 'type' option. +# `type tokenString [tokenString...]` +# +# Example: +# `type int c_uint_8 Rectangle` +# +# This can also be achieved with `set TYPE int c_uint_8 Rectangle` +# +# +# To embed whitespace in tokenStrings use the '\' escape character, or quote +# the tokenStrings. These quotes are supported: "'` +# +# +# - Support for the auto detection of languages through the file ending can be +# added using the 'file_ext' command. +# `file_ext langType langString [langString..]` +# +# Example: +# `file_ext CPP .ch .cxx .cpp.in` +# +# langTypes are defined in uncrusify_types.h in the lang_flag_e enum, use +# them without the 'LANG_' prefix: 'LANG_CPP' => 'CPP' +# +# +# - Custom macro-based indentation can be set up using 'macro-open', +# 'macro-else' and 'macro-close'. +# `(macro-open | macro-else | macro-close) tokenString` +# +# Example: +# `macro-open BEGIN_TEMPLATE_MESSAGE_MAP` +# `macro-open BEGIN_MESSAGE_MAP` +# `macro-close END_MESSAGE_MAP` +# +# +# option(s) with 'not default' value: 0 +# diff --git a/shared/info.cpp b/shared/info.cpp index 35f6427..2dd9181 100644 --- a/shared/info.cpp +++ b/shared/info.cpp @@ -17,368 +17,359 @@ */ #include "info.h" - -Shared::Info::Info(const QString &addr, EntryType tp): -type(tp), -address(addr), -vcard(nullptr), -activeKeys(nullptr), -inactiveKeys(nullptr) { +Shared::Info::Info (const QString& addr, EntryType tp): + type(tp), + address(addr), + vcard(nullptr), + activeKeys(nullptr), + inactiveKeys(nullptr) +{ switch (type) { - case EntryType::none: - break; - case EntryType::contact: - case EntryType::ownAccount: - vcard = new VCard(); - activeKeys = new std::list(); - inactiveKeys = new std::list(); - break; - default: - throw 352; + case EntryType::none: + break; + case EntryType::contact: + case EntryType::ownAccount: + vcard = new VCard(); + activeKeys = new std::list(); + inactiveKeys = new std::list(); + break; + default: + throw 352; } } -Shared::Info::Info(): -type(EntryType::none), -address(""), -vcard(nullptr), -activeKeys(nullptr), -inactiveKeys(nullptr) {} +Shared::Info::Info (): + type(EntryType::none), + address(""), + vcard(nullptr), + activeKeys(nullptr), + inactiveKeys(nullptr) {} -Shared::Info::Info(const Shared::Info &other): -type(other.type), -address(other.address), -vcard(nullptr), -activeKeys(nullptr), -inactiveKeys(nullptr) { +Shared::Info::Info (const Shared::Info& other): + type(other.type), + address(other.address), + vcard(nullptr), + activeKeys(nullptr), + inactiveKeys(nullptr) +{ switch (type) { - case EntryType::none: - break; - case EntryType::contact: - case EntryType::ownAccount: - vcard = new VCard(other.getVCardRef()); - activeKeys = new std::list(other.getActiveKeysRef()); - inactiveKeys = new std::list(other.getInactiveKeysRef()); - break; - default: - throw 353; + case EntryType::none: + break; + case EntryType::contact: + case EntryType::ownAccount: + vcard = new VCard(other.getVCardRef()); + activeKeys = new std::list(other.getActiveKeysRef()); + inactiveKeys = new std::list(other.getInactiveKeysRef()); + break; + default: + throw 353; } } -Shared::Info::Info(Info &&other): -type(other.type), -address(other.address), -vcard(other.vcard), -activeKeys(other.activeKeys), -inactiveKeys(other.inactiveKeys) { +Shared::Info::Info (Info&& other): + type(other.type), + address(other.address), + vcard(other.vcard), + activeKeys(other.activeKeys), + inactiveKeys(other.inactiveKeys) +{ other.type = EntryType::none; } -Shared::Info &Shared::Info::operator=(Info &&other) { +Shared::Info& Shared::Info::operator = (Info&& other) { type = other.type; address = other.address; vcard = other.vcard; activeKeys = other.activeKeys; inactiveKeys = other.inactiveKeys; - other.type = EntryType::none; - return *this; } -Shared::Info &Shared::Info::operator=(const Info &other) { +Shared::Info& Shared::Info::operator = (const Info& other) { type = other.type; address = other.address; - switch (type) { - case EntryType::none: - break; - case EntryType::contact: - case EntryType::ownAccount: - vcard = new VCard(other.getVCardRef()); - activeKeys = new std::list(other.getActiveKeysRef()); - inactiveKeys = new std::list(other.getInactiveKeysRef()); - break; - default: - throw 351; + case EntryType::none: + break; + case EntryType::contact: + case EntryType::ownAccount: + vcard = new VCard(other.getVCardRef()); + activeKeys = new std::list(other.getActiveKeysRef()); + inactiveKeys = new std::list(other.getInactiveKeysRef()); + break; + default: + throw 351; } - return *this; } -Shared::Info::~Info() { +Shared::Info::~Info () +{ turnIntoNone(); } -void Shared::Info::turnIntoNone() { +void Shared::Info::turnIntoNone () { switch (type) { - case EntryType::none: - break; - case EntryType::contact: - case EntryType::ownAccount: - delete vcard; - vcard = nullptr; - delete activeKeys; - activeKeys = nullptr; - delete inactiveKeys; - inactiveKeys = nullptr; - break; - default: - break; + case EntryType::none: + break; + case EntryType::contact: + case EntryType::ownAccount: + delete vcard; + vcard = nullptr; + delete activeKeys; + activeKeys = nullptr; + delete inactiveKeys; + inactiveKeys = nullptr; + break; + default: + break; } type = EntryType::none; } -void Shared::Info::turnIntoContact( - const Shared::VCard &crd, const std::list &aks, const std::list &iaks -) { +void Shared::Info::turnIntoContact (const Shared::VCard& crd, const std::list& aks, const std::list& iaks) { switch (type) { - case EntryType::none: - vcard = new VCard(crd); - activeKeys = new std::list(aks); - inactiveKeys = new std::list(iaks); - break; - case EntryType::contact: - case EntryType::ownAccount: - *vcard = crd; - *activeKeys = aks; - *inactiveKeys = iaks; - break; - default: - break; + case EntryType::none: + vcard = new VCard(crd); + activeKeys = new std::list(aks); + inactiveKeys = new std::list(iaks); + break; + case EntryType::contact: + case EntryType::ownAccount: + *vcard = crd; + *activeKeys = aks; + *inactiveKeys = iaks; + break; + default: + break; } - type = EntryType::contact; } -void Shared::Info::turnIntoContact(Shared::VCard *crd, std::list *aks, std::list *iaks) { +void Shared::Info::turnIntoContact (Shared::VCard* crd, std::list* aks, std::list* iaks) { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - delete vcard; - delete activeKeys; - delete inactiveKeys; - [[fallthrough]]; - case EntryType::none: - vcard = crd; - activeKeys = aks; - inactiveKeys = iaks; - break; - default: - break; + case EntryType::contact: + case EntryType::ownAccount: + delete vcard; + delete activeKeys; + delete inactiveKeys; + [[fallthrough]]; + case EntryType::none: + vcard = crd; + activeKeys = aks; + inactiveKeys = iaks; + break; + default: + break; } - type = EntryType::contact; } -void Shared::Info::turnIntoOwnAccount( - const Shared::VCard &crd, const std::list &aks, const std::list &iaks -) { +void Shared::Info::turnIntoOwnAccount (const Shared::VCard& crd, const std::list& aks, const std::list& iaks) { switch (type) { - case EntryType::none: - vcard = new VCard(crd); - activeKeys = new std::list(aks); - inactiveKeys = new std::list(iaks); - break; - case EntryType::contact: - case EntryType::ownAccount: - *vcard = crd; - *activeKeys = aks; - *inactiveKeys = iaks; - break; - default: - break; + case EntryType::none: + vcard = new VCard(crd); + activeKeys = new std::list(aks); + inactiveKeys = new std::list(iaks); + break; + case EntryType::contact: + case EntryType::ownAccount: + *vcard = crd; + *activeKeys = aks; + *inactiveKeys = iaks; + break; + default: + break; } - type = EntryType::ownAccount; } -void Shared::Info::turnIntoOwnAccount(Shared::VCard *crd, std::list *aks, std::list *iaks) { +void Shared::Info::turnIntoOwnAccount (Shared::VCard* crd, std::list* aks, std::list* iaks) { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - delete vcard; - delete activeKeys; - delete inactiveKeys; - [[fallthrough]]; - case EntryType::none: - vcard = crd; - activeKeys = aks; - inactiveKeys = iaks; - break; - default: - break; + case EntryType::contact: + case EntryType::ownAccount: + delete vcard; + delete activeKeys; + delete inactiveKeys; + [[fallthrough]]; + case EntryType::none: + vcard = crd; + activeKeys = aks; + inactiveKeys = iaks; + break; + default: + break; } - type = EntryType::ownAccount; } -void Shared::Info::setAddress(const QString &addr) { +void Shared::Info::setAddress (const QString& addr) { address = addr; } -QString Shared::Info::getAddress() const { +QString Shared::Info::getAddress () const { return address; } -const QString &Shared::Info::getAddressRef() const { +const QString& Shared::Info::getAddressRef () const { return address; } -Shared::EntryType Shared::Info::getType() const { +Shared::EntryType Shared::Info::getType () const { return type; } -std::list &Shared::Info::getActiveKeysRef() { +std::list& Shared::Info::getActiveKeysRef () { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return *activeKeys; - break; - default: - throw 354; + case EntryType::contact: + case EntryType::ownAccount: + return *activeKeys; + break; + default: + throw 354; } } -const std::list &Shared::Info::getActiveKeysRef() const { +const std::list& Shared::Info::getActiveKeysRef () const { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return *activeKeys; - break; - default: - throw 355; + case EntryType::contact: + case EntryType::ownAccount: + return *activeKeys; + break; + default: + throw 355; } } -std::list *Shared::Info::getActiveKeys() { +std::list* Shared::Info::getActiveKeys () { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return activeKeys; - break; - default: - throw 356; + case EntryType::contact: + case EntryType::ownAccount: + return activeKeys; + break; + default: + throw 356; } } -const std::list *Shared::Info::getActiveKeys() const { +const std::list* Shared::Info::getActiveKeys () const { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return activeKeys; - break; - default: - throw 357; + case EntryType::contact: + case EntryType::ownAccount: + return activeKeys; + break; + default: + throw 357; } } -std::list &Shared::Info::getInactiveKeysRef() { +std::list& Shared::Info::getInactiveKeysRef () { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return *inactiveKeys; - break; - default: - throw 358; + case EntryType::contact: + case EntryType::ownAccount: + return *inactiveKeys; + break; + default: + throw 358; } } -const std::list &Shared::Info::getInactiveKeysRef() const { +const std::list& Shared::Info::getInactiveKeysRef () const { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return *inactiveKeys; - break; - default: - throw 359; + case EntryType::contact: + case EntryType::ownAccount: + return *inactiveKeys; + break; + default: + throw 359; } } -std::list *Shared::Info::getInactiveKeys() { +std::list* Shared::Info::getInactiveKeys () { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return inactiveKeys; - break; - default: - throw 360; + case EntryType::contact: + case EntryType::ownAccount: + return inactiveKeys; + break; + default: + throw 360; } } -const std::list *Shared::Info::getInactiveKeys() const { +const std::list* Shared::Info::getInactiveKeys () const { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return inactiveKeys; - break; - default: - throw 361; + case EntryType::contact: + case EntryType::ownAccount: + return inactiveKeys; + break; + default: + throw 361; } } -const Shared::VCard &Shared::Info::getVCardRef() const { +const Shared::VCard& Shared::Info::getVCardRef () const { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return *vcard; - break; - default: - throw 362; + case EntryType::contact: + case EntryType::ownAccount: + return *vcard; + break; + default: + throw 362; } } -Shared::VCard &Shared::Info::getVCardRef() { +Shared::VCard& Shared::Info::getVCardRef () { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return *vcard; - break; - default: - throw 363; + case EntryType::contact: + case EntryType::ownAccount: + return *vcard; + break; + default: + throw 363; } } -const Shared::VCard *Shared::Info::getVCard() const { +const Shared::VCard* Shared::Info::getVCard () const { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return vcard; - break; - default: - throw 364; + case EntryType::contact: + case EntryType::ownAccount: + return vcard; + break; + default: + throw 364; } } -Shared::VCard *Shared::Info::getVCard() { +Shared::VCard* Shared::Info::getVCard () { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - return vcard; - break; - default: - throw 365; + case EntryType::contact: + case EntryType::ownAccount: + return vcard; + break; + default: + throw 365; } } -void Shared::Info::setActiveKeys(std::list *keys) { +void Shared::Info::setActiveKeys (std::list* keys) { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - activeKeys = keys; - break; - default: - throw 366; + case EntryType::contact: + case EntryType::ownAccount: + activeKeys = keys; + break; + default: + throw 366; } } -void Shared::Info::setVCard(Shared::VCard *card) { +void Shared::Info::setVCard (Shared::VCard* card) { switch (type) { - case EntryType::contact: - case EntryType::ownAccount: - vcard = card; - break; - default: - throw 367; + case EntryType::contact: + case EntryType::ownAccount: + vcard = card; + break; + default: + throw 367; } } diff --git a/shared/info.h b/shared/info.h index d5a48b1..90feadb 100644 --- a/shared/info.h +++ b/shared/info.h @@ -25,71 +25,49 @@ #include namespace Shared { - /** * This class should contain all nessesary data to display * roster element info (contact, or out of roster contact, or MUC, or MIX in the future) * * under development yet */ -class Info { +class Info : public QObject, public VCard { public: - Info(); - Info(const QString& address, EntryType = EntryType::none); - Info(const Info& other); - Info(Info&& other); - virtual ~Info(); + Info (); + Info (const QString& address, EntryType = EntryType::none); + Info (const Info& other); + Info (Info&& other); + virtual ~Info (); + Info& operator = (const Info& other); Info& operator = (Info&& other); - - QString getAddress() const; - const QString& getAddressRef() const; - void setAddress(const QString& address); - - EntryType getType() const; - void turnIntoNone(); - void turnIntoContact( - const VCard& card = VCard(), - const std::list& activeKeys = {}, - const std::list& inactiveKeys = {} - ); - void turnIntoContact( - VCard* card = new VCard, - std::list* activeKeys = new std::list, - std::list* inactiveKeys = new std::list - ); - void turnIntoOwnAccount( - const VCard& card = VCard(), - const std::list& activeKeys = {}, - const std::list& inactiveKeys = {} - ); - void turnIntoOwnAccount( - VCard* card = new VCard, - std::list* activeKeys = new std::list, - std::list* inactiveKeys = new std::list - ); - - const VCard& getVCardRef() const; - VCard& getVCardRef(); - const VCard* getVCard() const; - VCard* getVCard(); - void setVCard(Shared::VCard* card); - - const std::list& getActiveKeysRef() const; - std::list& getActiveKeysRef(); - const std::list* getActiveKeys() const; - std::list* getActiveKeys(); - void setActiveKeys(std::list* keys); - - const std::list& getInactiveKeysRef() const; - std::list& getInactiveKeysRef(); - const std::list* getInactiveKeys() const; - std::list* getInactiveKeys(); + QString getAddress () const; + const QString& getAddressRef () const; + void setAddress (const QString& address); + EntryType getType () const; + void turnIntoNone (); + void turnIntoContact (const VCard& card = VCard(), const std::list& activeKeys = {}, const std::list& inactiveKeys = {}); + void turnIntoContact (VCard* card = new VCard, std::list* activeKeys = new std::list, std::list* inactiveKeys = new std::list); + void turnIntoOwnAccount (const VCard& card = VCard(), const std::list& activeKeys = {}, const std::list& inactiveKeys = {}); + void turnIntoOwnAccount (VCard* card = new VCard, std::list* activeKeys = new std::list, std::list* inactiveKeys = new std::list); + const VCard& getVCardRef () const; + VCard& getVCardRef (); + const VCard* getVCard () const; + VCard* getVCard (); + void setVCard (Shared::VCard* card); + const std::list& getActiveKeysRef () const; + std::list& getActiveKeysRef (); + const std::list* getActiveKeys () const; + std::list* getActiveKeys (); + void setActiveKeys (std::list* keys); + const std::list& getInactiveKeysRef () const; + std::list& getInactiveKeysRef (); + const std::list* getInactiveKeys () const; + std::list* getInactiveKeys (); private: EntryType type; QString address; - VCard* vcard; std::list* activeKeys; std::list* inactiveKeys; From 0be264884958228af5294a27ab09680880ba294a Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 31 Jan 2024 20:22:49 -0300 Subject: [PATCH 120/137] Now avatars are properly autogenerated, reduced vCard spam --- core/account.cpp | 17 ++++---- core/conference.cpp | 70 +++++++++++++-------------------- core/conference.h | 23 ++++++----- core/contact.cpp | 7 ++-- core/handlers/rosterhandler.cpp | 36 ++++++++--------- core/rosteritem.h | 5 +-- external/lmdbal | 2 +- external/qxmpp | 2 +- ui/models/roster.cpp | 6 +-- 9 files changed, 76 insertions(+), 92 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index 858c177..8082aeb 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -367,19 +367,23 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) { vh->handlePresenceOfMyAccountChange(p_presence); } else { RosterItem* item = rh->getRosterItem(jid); - if (item != nullptr) - item->handlePresence(p_presence); + if (item != nullptr) { + if (item->isMuc()) //MUC presence is handled by inner muc events + return; + else + item->handlePresence(p_presence); + } } switch (p_presence.type()) { case QXmppPresence::Error: qDebug() << "An error reported by presence from" << id << p_presence.error().text(); break; - case QXmppPresence::Available:{ + case QXmppPresence::Available: { QDateTime lastInteraction = p_presence.lastUserInteraction(); - if (!lastInteraction.isValid()) { + if (!lastInteraction.isValid()) lastInteraction = QDateTime::currentDateTimeUtc(); - } + emit addPresence(jid, resource, { {"lastActivity", lastInteraction}, {"availability", p_presence.availableStatusType()}, //TODO check and handle invisible @@ -392,8 +396,7 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence) { ) } }); - } - break; + } break; case QXmppPresence::Unavailable: emit removePresence(jid, resource); break; diff --git a/core/conference.cpp b/core/conference.cpp index 3904675..48331e9 100644 --- a/core/conference.cpp +++ b/core/conference.cpp @@ -132,9 +132,8 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name) { if (resource.size() > 0) { QDateTime lastInteraction = pres.lastUserInteraction(); - if (!lastInteraction.isValid()) { + if (!lastInteraction.isValid()) lastInteraction = QDateTime::currentDateTimeUtc(); - } QMap cData = { {"lastActivity", lastInteraction}, @@ -153,29 +152,37 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name) { careAboutAvatar(hasAvatar, itr->second, cData, resource, p_name); emit addParticipant(resource, cData); + + if (!hasAvatar) // because this way vCard is already requested, no need to handle possible avatar update + return; } - + handlePossibleAvatarUpdate(pres, resource, hasAvatar, itr->second); +} + +void Core::Conference::handlePossibleAvatarUpdate ( + const QXmppPresence& pres, + const QString& resource, + bool hasAvatar, + const Archive::AvatarInfo& info +) { switch (pres.vCardUpdateType()) { case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo break; case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here break; - case QXmppPresence::VCardUpdateNoPhoto: { //there is no photo, need to drop if any - if (!hasAvatar || !itr->second.autogenerated) { + case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any + if (!hasAvatar || !info.autogenerated) setAutoGeneratedAvatar(resource); - } - } - break; - case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load - if (hasAvatar) { - if (itr->second.autogenerated || itr->second.hash != pres.photoHash()) - emit requestVCard(p_name); + break; + case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load + if (hasAvatar) { + if (info.autogenerated || info.hash != pres.photoHash()) + emit requestVCard(pres.from()); } else { - emit requestVCard(p_name); + emit requestVCard(pres.from()); } break; - } } } @@ -235,32 +242,10 @@ void Core::Conference::handlePresence(const QXmppPresence& pres) { QString resource(""); if (comps.size() > 1) resource = comps.back(); - - switch (pres.vCardUpdateType()) { - case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo - break; - case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here - break; - case QXmppPresence::VCardUpdateNoPhoto: { //there is no photo, need to drop if any - Archive::AvatarInfo info; - bool hasAvatar = readAvatarInfo(info, resource); - if (!hasAvatar || !info.autogenerated) { - setAutoGeneratedAvatar(resource); - } - } - break; - case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load - Archive::AvatarInfo info; - bool hasAvatar = readAvatarInfo(info, resource); - if (hasAvatar) { - if (info.autogenerated || info.hash != pres.photoHash()) - emit requestVCard(id); - } else { - emit requestVCard(id); - } - break; - } - } + + Archive::AvatarInfo info; + bool hasAvatar = readAvatarInfo(info, resource); + handlePossibleAvatarUpdate(pres, resource, hasAvatar, info); } bool Core::Conference::setAutoGeneratedAvatar(const QString& resource) { @@ -272,6 +257,7 @@ bool Core::Conference::setAutoGeneratedAvatar(const QString& resource) { exParticipants.insert(std::make_pair(resource, newInfo)); else itr->second = newInfo; + emit changeParticipant(resource, { {"avatarState", static_cast(Shared::Avatar::autocreated)}, {"avatarPath", avatarPath(resource) + "." + newInfo.type} @@ -313,18 +299,18 @@ bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& in void Core::Conference::handleResponseVCard(const QXmppVCardIq& card, const QString &resource, Shared::VCard& out) { RosterItem::handleResponseVCard(card, resource, out); - if (resource.size() > 0) { + if (resource.size() > 0) emit changeParticipant(resource, { {"avatarState", static_cast(out.getAvatarType())}, {"avatarPath", out.getAvatarPath()} }); - } } QMap Core::Conference::getAllAvatars() const { QMap result; for (const std::pair& pair : exParticipants) result.insert(pair.first, avatarPath(pair.first) + "." + pair.second.type); + return result; } diff --git a/core/conference.h b/core/conference.h index 3c077e3..501738b 100644 --- a/core/conference.h +++ b/core/conference.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef CORE_CONFERENCE_H -#define CORE_CONFERENCE_H +#pragma once #include @@ -29,14 +28,8 @@ #include #include -namespace Core -{ - -/** - * @todo write docs - */ -class Conference : public RosterItem -{ +namespace Core { +class Conference : public RosterItem { Q_OBJECT public: Conference(const QString& p_jid, const QString& p_account, bool p_autoJoin, const QString& p_name, const QString& p_nick, QXmppMucRoom* p_room); @@ -69,6 +62,14 @@ signals: protected: bool setAvatar(const QByteArray &data, Archive::AvatarInfo& info, const QString &resource = "") override; +private: + void handlePossibleAvatarUpdate( + const QXmppPresence& pres, + const QString& resource, + bool hasAvatar, + const Archive::AvatarInfo& info + ); + private: QString nick; QXmppMucRoom* room; @@ -91,5 +92,3 @@ private slots: }; } - -#endif // CORE_CONFERENCE_H diff --git a/core/contact.cpp b/core/contact.cpp index f18dae3..aef637b 100644 --- a/core/contact.cpp +++ b/core/contact.cpp @@ -73,18 +73,17 @@ void Core::Contact::handlePresence(const QXmppPresence& pres) { case QXmppPresence::VCardUpdateNoPhoto: { //there is no photo, need to drop if any Archive::AvatarInfo info; bool hasAvatar = readAvatarInfo(info); - if (!hasAvatar || !info.autogenerated) { + if (!hasAvatar || !info.autogenerated) setAutoGeneratedAvatar(); - } + } break; case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load Archive::AvatarInfo info; bool hasAvatar = readAvatarInfo(info); if (hasAvatar) { - if (info.autogenerated || info.hash != pres.photoHash()) { + if (info.autogenerated || info.hash != pres.photoHash()) emit requestVCard(jid); - } } else { emit requestVCard(jid); } diff --git a/core/handlers/rosterhandler.cpp b/core/handlers/rosterhandler.cpp index 940ddbf..e3020c8 100644 --- a/core/handlers/rosterhandler.cpp +++ b/core/handlers/rosterhandler.cpp @@ -98,13 +98,13 @@ void Core::RosterHandler::addedAccount(const QString& jid) { contact->setName(re.name()); if (newContact) { + handleNewContact(contact); QMap cData = contact->getInfo(); #if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) cData.insert("trust", QVariant::fromValue(acc->th->getSummary(jid))); #endif int grCount = 0; - for (QSet::const_iterator itr = gr.begin(), end = gr.end(); itr != end; ++itr) { - const QString& groupName = *itr; + for (const QString& groupName : gr) { addToGroup(jid, groupName); emit acc->addContact(jid, groupName, cData); grCount++; @@ -117,7 +117,6 @@ void Core::RosterHandler::addedAccount(const QString& jid) { acc->dm->requestInfo(jid); //acc->dm->requestItems(jid); } - handleNewContact(contact); } } @@ -495,22 +494,23 @@ void Core::RosterHandler::removeContactFromGroupRequest(const QString& jid, cons if (itr == contacts.end()) { qDebug() << "An attempt to remove non existing contact" << lcJid << "of account" << acc->name << "from the group" << groupName << ", skipping"; + return; + } + + QXmppRosterIq::Item item = acc->rm->getRosterEntry(lcJid); + QSet groups = item.groups(); + QSet::const_iterator gItr = groups.find(groupName); + if (gItr != groups.end()) { + groups.erase(gItr); + item.setGroups(groups); + + QXmppRosterIq iq; + iq.setType(QXmppIq::Set); + iq.addItem(item); + acc->client.sendPacket(iq); } else { - QXmppRosterIq::Item item = acc->rm->getRosterEntry(lcJid); - QSet groups = item.groups(); - QSet::const_iterator gItr = groups.find(groupName); - if (gItr != groups.end()) { - groups.erase(gItr); - item.setGroups(groups); - - QXmppRosterIq iq; - iq.setType(QXmppIq::Set); - iq.addItem(item); - acc->client.sendPacket(iq); - } else { - qDebug() << "An attempt to remove contact" << lcJid << "of account" - << acc->name << "from the group" << groupName << "but it's not in that group, skipping"; - } + qDebug() << "An attempt to remove contact" << lcJid << "of account" + << acc->name << "from the group" << groupName << "but it's not in that group, skipping"; } } diff --git a/core/rosteritem.h b/core/rosteritem.h index 203ed88..33a08f0 100644 --- a/core/rosteritem.h +++ b/core/rosteritem.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef CORE_ROSTERITEM_H -#define CORE_ROSTERITEM_H +#pragma once #include #include @@ -130,5 +129,3 @@ private: }; } - -#endif // CORE_ROSTERITEM_H diff --git a/external/lmdbal b/external/lmdbal index c83369f..79240aa 160000 --- a/external/lmdbal +++ b/external/lmdbal @@ -1 +1 @@ -Subproject commit c83369f34761e7a053d62312bd07fe5b3db3a519 +Subproject commit 79240aa5354e9fa9d09d3b12ea1077a81bb90809 diff --git a/external/qxmpp b/external/qxmpp index 8dda3c9..0cd7379 160000 --- a/external/qxmpp +++ b/external/qxmpp @@ -1 +1 @@ -Subproject commit 8dda3c9921c34b9e33883b0d140e8de12edc0736 +Subproject commit 0cd7379bd78aa01af7e84f2fad6269ef0c0ba49c diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index 0f35baa..fc6382a 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -650,10 +650,10 @@ void Models::Roster::onChildRemoved() { void Models::Roster::addPresence(const QString& account, const QString& jid, const QString& name, const QMap& data) { ElId contactId(account, jid); std::map::iterator itr = contacts.find(contactId); - if (itr != contacts.end()) { + if (itr != contacts.end()) itr->second->addPresence(name, data); - } - + else + qDebug() << "Received a presence" << jid + "/" + name << "don't know what to do with it"; } void Models::Roster::removePresence(const QString& account, const QString& jid, const QString& name) { From 829777935fb31351486c79f72574a41d0be91323 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 1 Feb 2024 13:10:52 -0300 Subject: [PATCH 121/137] fixes for bundled LMDBAL build --- core/components/archive.h | 7 +++---- external/lmdbal | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/core/components/archive.h b/core/components/archive.h index 6a2be73..67208b4 100644 --- a/core/components/archive.h +++ b/core/components/archive.h @@ -27,12 +27,11 @@ #include "shared/enums.h" #include "shared/message.h" #include "shared/exception.h" -#include #include -#include -#include -#include +#include +#include +#include namespace Core { diff --git a/external/lmdbal b/external/lmdbal index 79240aa..d62eddc 160000 --- a/external/lmdbal +++ b/external/lmdbal @@ -1 +1 @@ -Subproject commit 79240aa5354e9fa9d09d3b12ea1077a81bb90809 +Subproject commit d62eddc47edbec9f8c071459e045578f61ab58df From acd60eaba2e7a8156635360a8ca81ffd3ba90929 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 4 Feb 2024 09:44:19 -0300 Subject: [PATCH 122/137] Fixing build without omemo, release preparation, unnecessary inheritance removed, info widget fix --- .gitea/workflows/release.yml | 45 +++++++++++++++++++++++++++++++++++ CHANGELOG.md | 4 +++- CMakeLists.txt | 2 +- core/account.h | 12 ++++++---- core/delayManager/info.cpp | 3 +++ core/delayManager/manager.cpp | 4 ++++ core/delayManager/manager.h | 6 +++++ core/handlers/CMakeLists.txt | 12 +++++----- packaging/Archlinux/PKGBUILD | 6 ++--- shared/global.cpp | 2 +- shared/info.h | 2 +- ui/widgets/about.cpp | 20 +++++++--------- ui/widgets/about.h | 14 +++-------- 13 files changed, 92 insertions(+), 40 deletions(-) create mode 100644 .gitea/workflows/release.yml diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..f8bbef7 --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,45 @@ +name: Squawk Release workflow +run-name: ${{ gitea.actor }} is running Squawk Release workflow on release ${{ gitea.event.release.tag_name }} +on: + release: + types: [published] + +jobs: + Archlinux: + runs-on: archlinux + steps: + - name: Download the release tarball + run: curl -sL ${{ gitea.server_url }}/${{ gitea.repository }}/archive/${{ gitea.event.release.tag_name }}.tar.gz --output tarball.tar.gz + + - name: Calculate SHA256 for the tarball + run: echo "tbSum=$(sha256sum tarball.tar.gz | cut -d ' ' -f 1)" >> $GITHUB_ENV + + - name: Unarchive tarball + run: tar -xvzf tarball.tar.gz + + - name: Clone the AUR repository + run: | + echo "${{ secrets.DEPLOY_TO_AUR_PRIVATE_KEY }}" > key + chmod 600 key + GIT_SSH_COMMAND="ssh -i key -o 'IdentitiesOnly yes' -o 'StrictHostKeyChecking no'" git clone ssh://aur@aur.archlinux.org/squawk.git aur + chmod 777 -R aur + cd aur + git config user.name ${{ secrets.DEPLOY_TO_AUR_USER_NAME }} + git config user.email ${{ secrets.DEPLOY_TO_AUR_EMAIL }} + + + - name: Copy PKGBUILD to the directory + run: cp squawk/packaging/Archlinux/PKGBUILD aur/ + + - name: Put SHA256 sum to PKGBUILD file, and generate .SRCINFO + working-directory: aur + run: | + sed -i "/sha256sums=/c\sha256sums=('${{ env.tbSum }}')" PKGBUILD + sudo -u build makepkg --printsrcinfo > .SRCINFO + + - name: Commit package to aur + working-directory: aur + run: | + git add PKGBUILD .SRCINFO + git commit -m "${{ gitea.event.release.body }}" + GIT_SSH_COMMAND="ssh -i ../key -o 'IdentitiesOnly yes' -o 'StrictHostKeyChecking no'" git push diff --git a/CHANGELOG.md b/CHANGELOG.md index f554982..0c11404 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ # Changelog -## Squawk 0.2.3 (UNRELEASED) +## Squawk 0.2.3 (February 04, 2024) ### 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 +- automatic avatars (if a contact doesn't have one) get generated once again ### Improvements - deactivated accounts now don't appear in combobox of "Add contact" and "Join conference" dialogues @@ -11,6 +12,7 @@ - settings file on the disk is not rewritten every roster element expansion or collapse - removed unnecessary own vcard request at sturtup (used to do it to discover my own avatar) - vcard window now is Info system and it can display more information +- reduced vcard request spam in MUCs ### New features - now you can enable tray icon from settings! diff --git a/CMakeLists.txt b/CMakeLists.txt index edf9297..e2465cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,7 @@ option(SYSTEM_LMDBAL "Use system lmdbal lib" ON) option(WITH_KWALLET "Build KWallet support module" ON) option(WITH_KIO "Build KIO support module" ON) option(WITH_KCONFIG "Build KConfig support module" ON) -option(WITH_OMEMO "Build OMEMO support module" ON) +option(WITH_OMEMO "Build OMEMO support module" OFF) #it should be off by default untill I sourt the problems out # Dependencies ## Qt diff --git a/core/account.h b/core/account.h index ea1a13d..4cd3316 100644 --- a/core/account.h +++ b/core/account.h @@ -60,11 +60,13 @@ #include "handlers/vcardhandler.h" #include "handlers/discoveryhandler.h" -#ifdef WITH_OMEMO -#include -#include -#include "handlers/trusthandler.h" -#include "handlers/omemohandler.h" +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) + #include + #ifdef WITH_OMEMO + #include + #include "handlers/omemohandler.h" + #endif + #include "handlers/trusthandler.h" #endif namespace Core { diff --git a/core/delayManager/info.cpp b/core/delayManager/info.cpp index 9953fd4..1b8da8d 100644 --- a/core/delayManager/info.cpp +++ b/core/delayManager/info.cpp @@ -45,7 +45,10 @@ void Core::DelayManager::Info::receivedVCard(const Shared::VCard& card) { throw 245; info = new Shared::VCard(card); + +#ifdef WITH_OMEMO stage = Stage::waitingForBundles; +#endif } Shared::VCard * Core::DelayManager::Info::claim() { diff --git a/core/delayManager/manager.cpp b/core/delayManager/manager.cpp index de37d47..efafaf5 100644 --- a/core/delayManager/manager.cpp +++ b/core/delayManager/manager.cpp @@ -242,7 +242,9 @@ void Core::DelayManager::Manager::jobIsCanceled(Job* job, bool wasRunning) { emit gotVCard(jb->jid, Shared::VCard()); break; case InfoForUser::Stage::waitingForBundles: +#ifdef WITH_OMEMO requestedBundles.erase(jb->jid); +#endif break; default: break; @@ -356,6 +358,7 @@ void Core::DelayManager::Manager::receivedOwnVCard(const Shared::VCard& card) { } } +#ifdef WITH_OMEMO void Core::DelayManager::Manager::receivedBundles(const QString& jid, const std::list& keys) { std::map::const_iterator itr = requestedBundles.find(jid); if (itr == requestedBundles.end()) { @@ -397,6 +400,7 @@ void Core::DelayManager::Manager::receivedOwnBundles(const std::list& keys); void receivedOwnBundles(const std::list& keys); +#endif private: void preScheduleJob(Job* job); diff --git a/core/handlers/CMakeLists.txt b/core/handlers/CMakeLists.txt index 11a902d..ed359bd 100644 --- a/core/handlers/CMakeLists.txt +++ b/core/handlers/CMakeLists.txt @@ -3,7 +3,6 @@ set(SOURCE_FILES rosterhandler.cpp vcardhandler.cpp discoveryhandler.cpp - omemohandler.cpp trusthandler.cpp ) @@ -12,11 +11,12 @@ set(HEADER_FILES rosterhandler.h vcardhandler.h discoveryhandler.h - omemohandler.h trusthandler.h ) -target_sources(squawk PRIVATE - ${SOURCE_FILES} - ${HEADER_FILES} -) +if(WITH_OMEMO) + list(APPEND SOURCE_FILES omemohandler.cpp) + list(APPEND HEADER_FILES omemohandler.h) +endif() + +target_sources(squawk PRIVATE ${SOURCE_FILES}) diff --git a/packaging/Archlinux/PKGBUILD b/packaging/Archlinux/PKGBUILD index bd9c981..53347dc 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -6,15 +6,15 @@ pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)" arch=('i686' 'x86_64') url="https://git.macaw.me/blue/squawk" license=('GPL3') -depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdbal' 'qxmpp>=1.1.0') +depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdbal' 'qxmpp-qt5') makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools' 'boost') optdepends=('kwallet: secure password storage (requires rebuild)' 'kconfig: system themes support (requires rebuild)' 'kconfigwidgets: system themes support (requires rebuild)' 'kio: better show in folder action (requires rebuild)') -source=("$pkgname-$pkgver.tar.gz") -sha256sums=('e4fa2174a3ba95159cc3b0bac3f00550c9e0ce971c55334e2662696a4543fc7e') +source=("$pkgname-$pkgver.tar.gz::https://git.macaw.me/blue/$pkgname/archive/$pkgver.tar.gz") +sha256sums=('SKIP') build() { cd "$srcdir/squawk" cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release diff --git a/shared/global.cpp b/shared/global.cpp index 97f3263..6618426 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -24,7 +24,7 @@ #ifdef WITH_OMEMO constexpr bool OMEMO_SUPPORT = true; #else -constexpr bool OMEMO_SUPPORT = false +constexpr bool OMEMO_SUPPORT = false; #endif QFont getFont (QFontDatabase::SystemFont type, bool bold = false, bool italic = false, qreal factor = 1.0) { diff --git a/shared/info.h b/shared/info.h index 90feadb..ae34ef2 100644 --- a/shared/info.h +++ b/shared/info.h @@ -31,7 +31,7 @@ namespace Shared { * * under development yet */ -class Info : public QObject, public VCard { +class Info { public: Info (); Info (const QString& address, EntryType = EntryType::none); diff --git a/ui/widgets/about.cpp b/ui/widgets/about.cpp index f46c661..ca1bfa2 100644 --- a/ui/widgets/about.cpp +++ b/ui/widgets/about.cpp @@ -19,13 +19,13 @@ #include #include -#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 3, 0) -static const std::string QXMPP_VERSION_PATCH(std::to_string(QXMPP_VERSION & 0xff)); -static const std::string QXMPP_VERSION_MINOR(std::to_string((QXMPP_VERSION & 0xff00) >> 8)); -static const std::string QXMPP_VERSION_MAJOR(std::to_string(QXMPP_VERSION >> 16)); -static const QString QXMPP_VERSION_STRING = QString::fromStdString(QXMPP_VERSION_MAJOR + "." + QXMPP_VERSION_MINOR + "." + QXMPP_VERSION_PATCH); +#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 2, 0) +static const std::string _QXMPP_PATCH_(std::to_string(QXMPP_VERSION & 0xff)); +static const std::string _QXMPP_MINOR_(std::to_string((QXMPP_VERSION & 0xff00) >> 8)); +static const std::string _QXMPP_MAJOR_(std::to_string(QXMPP_VERSION >> 16)); +static const QString SQUAWK_INNER_QXMPP_VERSION_STRING = QString::fromStdString(_QXMPP_MAJOR_ + "." + _QXMPP_MINOR_ + "." + _QXMPP_PATCH_); #else -static const QString QXMPP_VERSION_STRING = QXmppVersion(); +static const QString SQUAWK_INNER_QXMPP_VERSION_STRING = QXmppVersion(); #endif About::About(QWidget* parent): @@ -39,7 +39,7 @@ About::About(QWidget* parent): m_ui->qtBuiltAgainstVersion->setText(tr("(built against %1)").arg(QT_VERSION_STR)); m_ui->qxmppVersionValue->setText(QXmppVersion()); - m_ui->qxmppBuiltAgainstVersion->setText(tr("(built against %1)").arg(QXMPP_VERSION_STRING)); + m_ui->qxmppBuiltAgainstVersion->setText(tr("(built against %1)").arg(SQUAWK_INNER_QXMPP_VERSION_STRING)); setWindowFlag(Qt::Tool); @@ -52,8 +52,7 @@ About::~About() { } }; -void About::onLicenseActivated() -{ +void About::onLicenseActivated() { if (license == nullptr) { QFile file; bool found = false; @@ -106,7 +105,6 @@ void About::onLicenseActivated() license->show(); } -void About::onLicenseClosed() -{ +void About::onLicenseClosed() { license = nullptr; } diff --git a/ui/widgets/about.h b/ui/widgets/about.h index 1506b7f..35df9f4 100644 --- a/ui/widgets/about.h +++ b/ui/widgets/about.h @@ -14,8 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#ifndef ABOUT_H -#define ABOUT_H +#pragma once #include #include @@ -24,16 +23,11 @@ #include #include -namespace Ui -{ +namespace Ui{ class About; } -/** - * @todo write docs - */ -class About : public QWidget -{ +class About : public QWidget { Q_OBJECT public: About(QWidget* parent = nullptr); @@ -47,5 +41,3 @@ private: QScopedPointer m_ui; QWidget* license; }; - -#endif // ABOUT_H From 8d82d340a4fa5394a97bab107e0797d5095970d2 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 4 Feb 2024 13:36:51 -0300 Subject: [PATCH 123/137] 0.2.3 preparation, typo fix, readme changes --- CHANGELOG.md | 2 +- README.md | 73 +++++++++++++++++++++--------------- main/application.cpp | 2 +- packaging/Archlinux/PKGBUILD | 8 ++-- 4 files changed, 48 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c11404..4ca1542 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ ### New features - now you can enable tray icon from settings! - there is a job queue now, this allowes to spread a bit the spam on the server at connection time -- squawk now querries clients of it's peers, you can see what programs other people use +- squawk now queries clients of it's peers, you can see what programs other people use ## Squawk 0.2.2 (May 05, 2022) ### Bug fixes diff --git a/README.md b/README.md index 121b640..9c835b6 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ - QT 5.12 *(lower versions might work but it wasn't tested)* - CMake 3.4 or higher - qxmpp 1.1.0 or higher -- LMDBAL (my own [library](https://git.macaw.me/blue/lmdbal) around lmdb) +- LMDBAL (my own [library](https://git.macaw.me/blue/lmdbal) for lmdb) - KDE Frameworks: kwallet (optional) - KDE Frameworks: KIO (optional) - KDE Frameworks: KConfig (optional) @@ -33,14 +33,49 @@ $ pacaur -S squawk ### Building -You can also clone the repo and build it from source +You can also the repo and build it from source Squawk requires Qt with SSL enabled. It uses CMake as build system. Please check the prerequisites and install them before installation. +--- + +There are several ways to build Squawk. The one you need depends on whether you have `qxmpp` and `lmdbal` installed in your system. + +#### Building with system dependencies + +This is the easiest way but it requires you to have `qxmpp` and `lmdbal` installed as system packages. Here is what you do: + +``` +$ git clone https://git.macaw.me/blue/squawk +$ cd squawk +$ mkdir build +$ cd build +$ cmake .. +$ cmake --build . +``` + +#### Building with bundled qxmpp + +If you don't have any of `qxmpp` or `lmdbal` (or both) installed the process is abit mor complicated. +On the configuration stage you need to enable one or both entries in the square brackets, depending on what package your system lacks. + +Here is what you do + +``` +$ git clone --recurse-submodules https://git.macaw.me/blue/squawk +$ cd squawk +$ mkdir build +$ cd build +$ cmake .. [-D SYSTEM_QXMPP=False] [-D SYSTEM_LMDBAL=False] +$ cmake --build . +``` + #### For Windows (Mingw-w64) build +**Building for windows is not mainteined, but was possible in the past, you can try, but it probably won't work** + You need Qt for mingw64 (MinGW 64-bit) platform when installing Qt. The best way to acquire library `lmdb` and `boost` is through Msys2. @@ -49,54 +84,30 @@ First install Msys2, and then install `mingw-w64-x86_64-lmdb` and `mingw-w64-x86 Then you need to provide the cmake cache entry when calling cmake for configuration: -``` -cmake .. -D LMDB_ROOT_DIR:PATH= -D BOOST_ROOT:PATH= -``` - ``: e.g. `C:/msys64/mingw64`. ---- - -There are two ways to build, it depends whether you have qxmpp installed in your system - -#### Building with system qxmpp - -Here is what you do - -``` -$ git clone https://git.macaw.me/blue/squawk -$ cd squawk -$ mkdir build -$ cd build -$ cmake .. [-D LMDB_ROOT_DIR:PATH=...] [-D BOOST_ROOT:PATH=...] -$ cmake --build . -``` - -#### Building with bundled qxmpp - -Here is what you do - ``` $ git clone --recurse-submodules https://git.macaw.me/blue/squawk $ cd squawk $ mkdir build $ cd build -$ cmake .. -D SYSTEM_QXMPP=False [-D LMDB_ROOT_DIR:PATH=...] [-D BOOST_ROOT:PATH=...] +$ cmake .. -D SYSTEM_QXMPP=False -D SYSTEM_LMDBAL=False -D LMDB_ROOT_DIR:PATH= -D BOOST_ROOT:PATH= $ cmake --build . ``` -You can always refer to `appveyor.yml` to see how AppVeyor build squawk. +You can always refer to `appveyor.yml` to see how AppVeyor build squawk for windows. ### List of keys -Here is the list of keys you can pass to configuration phase of `cmake ..`. +Here is the list of keys you can pass to configuration phase of `cmake ..`: + - `CMAKE_BUILD_TYPE` - `Debug` just builds showing all warnings, `Release` builds with no warnings and applies optimizations (default is `Debug`) - `SYSTEM_QXMPP` - `True` tries to link against `qxmpp` installed in the system, `False` builds bundled `qxmpp` library (default is `True`) - `SYSTEM_LMDBAL` - `True` tries to link against `LMDABL` installed in the system, `False` builds bundled `LMDBAL` library (default is `True`) - `WITH_KWALLET` - `True` builds the `KWallet` capability module if `KWallet` is installed and if not goes to `False`. `False` disables `KWallet` support (default is `True`) - `WITH_KIO` - `True` builds the `KIO` capability module if `KIO` is installed and if not goes to `False`. `False` disables `KIO` support (default is `True`) - `WITH_KCONFIG` - `True` builds the `KConfig` and `KConfigWidgets` capability module if such packages are installed and if not goes to `False`. `False` disables `KConfig` and `KConfigWidgets` support (default is `True`) -- `WITH_OMEMO` - `True` builds the OMEMO encryption, requires `qxmpp` of version >= 1.5.0 built with OMEMO support. `False` disables OMEMO support (default is `True`) +- `WITH_OMEMO` - `True` builds the OMEMO encryption, requires `qxmpp` of version >= 1.5.0 built with OMEMO support. `False` disables OMEMO support (default is `False`) ## License diff --git a/main/application.cpp b/main/application.cpp index d184e6b..15e9208 100644 --- a/main/application.cpp +++ b/main/application.cpp @@ -245,7 +245,7 @@ void Application::onChangeTray(bool enabled, bool hide) { trayIcon = nullptr; } } - } else if (trayIcon == nullptr) { + } else if (trayIcon != nullptr) { trayIcon->deleteLater(); trayIcon = nullptr; } diff --git a/packaging/Archlinux/PKGBUILD b/packaging/Archlinux/PKGBUILD index 53347dc..29ed800 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -8,10 +8,10 @@ url="https://git.macaw.me/blue/squawk" license=('GPL3') depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdbal' 'qxmpp-qt5') makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools' 'boost') -optdepends=('kwallet: secure password storage (requires rebuild)' - 'kconfig: system themes support (requires rebuild)' - 'kconfigwidgets: system themes support (requires rebuild)' - 'kio: better show in folder action (requires rebuild)') +optdepends=('kwallet5: secure password storage (requires rebuild)' + 'kconfig5: system themes support (requires rebuild)' + 'kconfigwidgets5: system themes support (requires rebuild)' + 'kio5: better show in folder action (requires rebuild)') source=("$pkgname-$pkgver.tar.gz::https://git.macaw.me/blue/$pkgname/archive/$pkgver.tar.gz") sha256sums=('SKIP') From fb843a13463e6600f86d9dde5aa728cdcfa57259 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 4 Feb 2024 13:51:16 -0300 Subject: [PATCH 124/137] ci --- .gitea/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index f8bbef7..5d4fe58 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -41,5 +41,5 @@ jobs: working-directory: aur run: | git add PKGBUILD .SRCINFO - git commit -m "${{ gitea.event.release.body }}" + git commit -m "${{ gitea.event.release.body//\"/\\\" }}" GIT_SSH_COMMAND="ssh -i ../key -o 'IdentitiesOnly yes' -o 'StrictHostKeyChecking no'" git push From 2c61b82924747a0b7677a1f5605c3cb064f884ea Mon Sep 17 00:00:00 2001 From: Benson Muite Date: Sun, 6 Oct 2024 19:26:44 +0300 Subject: [PATCH 125/137] Add appdata file --- packaging/CMakeLists.txt | 6 ++++- packaging/macaw.me.squawk.appdata.xml | 33 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 packaging/macaw.me.squawk.appdata.xml diff --git a/packaging/CMakeLists.txt b/packaging/CMakeLists.txt index 4965b37..4bb9c87 100644 --- a/packaging/CMakeLists.txt +++ b/packaging/CMakeLists.txt @@ -1,3 +1,7 @@ configure_file(squawk.desktop squawk.desktop COPYONLY) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) \ No newline at end of file +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) + +configure_file(macaw.me.squawk.appdata.xml macaw.me.squawk.appdata.xml COPYONLY) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/macaw.me.squawk.appdata.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo) diff --git a/packaging/macaw.me.squawk.appdata.xml b/packaging/macaw.me.squawk.appdata.xml new file mode 100644 index 0000000..c188496 --- /dev/null +++ b/packaging/macaw.me.squawk.appdata.xml @@ -0,0 +1,33 @@ + + + macaw.me.squawk + CC0-1.0 + GPL-3.0+ + Squawk + Desktop Qt based XMPP messenger + +

+ Squawk is a lightweight XMPP desktop messenger. + The primary objective of this project is to offer + you a fast and user-friendly messaging experience + that closely aligns with your system’s style, while + also minimizing resource consumption. +

+

+ Squawk is still at a very early stage and might not suit + everyone but you are welcome to try it out. +

+
+ macaw.me.squawk.desktop + + + https://macaw.me/projects/squawk/0.2.2.png + View XMPP contacts and conversations + + + https://macaw.me/projects/squawk/ + + squawk + + blue@macaw.me +
From 030c374139e0c6fcab978bd4d4a125ddbb4dff19 Mon Sep 17 00:00:00 2001 From: Benson Muite Date: Sun, 6 Oct 2024 19:29:38 +0300 Subject: [PATCH 126/137] Update image link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9c835b6..655304f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![AUR version](https://img.shields.io/aur/version/squawk?style=flat-square)](https://aur.archlinux.org/packages/squawk/) [![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me) -![Squawk screenshot](https://macaw.me/images/squawk/0.2.2.png) +![Squawk screenshot](https://macaw.me/projects/squawk/0.2.2.png) ### Prerequisites From 80838595413188936f95cfc8a2352824db595d8d Mon Sep 17 00:00:00 2001 From: Benson Muite Date: Mon, 7 Oct 2024 13:45:15 +0300 Subject: [PATCH 127/137] Fix license text error --- external/simpleCrypt/simplecrypt.cpp | 2 +- external/simpleCrypt/simplecrypt.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/external/simpleCrypt/simplecrypt.cpp b/external/simpleCrypt/simplecrypt.cpp index 19a96be..093403e 100644 --- a/external/simpleCrypt/simplecrypt.cpp +++ b/external/simpleCrypt/simplecrypt.cpp @@ -19,7 +19,7 @@ DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR #######; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/external/simpleCrypt/simplecrypt.h b/external/simpleCrypt/simplecrypt.h index d75bdc2..0052618 100644 --- a/external/simpleCrypt/simplecrypt.h +++ b/external/simpleCrypt/simplecrypt.h @@ -19,7 +19,7 @@ DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR #######; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From ff9a591d6de95d75bfbe7ee0d99fad32887d2673 Mon Sep 17 00:00:00 2001 From: Benson Muite Date: Sun, 6 Oct 2024 20:54:18 +0300 Subject: [PATCH 128/137] Private libraries directory --- core/passwordStorageEngines/wrappers/CMakeLists.txt | 2 +- plugins/CMakeLists.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/passwordStorageEngines/wrappers/CMakeLists.txt b/core/passwordStorageEngines/wrappers/CMakeLists.txt index e8420da..432079f 100644 --- a/core/passwordStorageEngines/wrappers/CMakeLists.txt +++ b/core/passwordStorageEngines/wrappers/CMakeLists.txt @@ -1,4 +1,4 @@ add_library(kwalletWrapper SHARED kwallet.cpp) target_link_libraries(kwalletWrapper PRIVATE KF5::Wallet) -install(TARGETS kwalletWrapper LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) +install(TARGETS kwalletWrapper LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/squawk) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 388c258..565651e 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -2,7 +2,7 @@ if (WITH_KIO) add_library(openFileManagerWindowJob SHARED openfilemanagerwindowjob.cpp) target_link_libraries(openFileManagerWindowJob PRIVATE KF5::KIOWidgets) - install(TARGETS openFileManagerWindowJob LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(TARGETS openFileManagerWindowJob LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/squawk) endif () if (WITH_KCONFIG) @@ -10,5 +10,5 @@ if (WITH_KCONFIG) target_link_libraries(colorSchemeTools PRIVATE KF5::ConfigCore) target_link_libraries(colorSchemeTools PRIVATE KF5::ConfigWidgets) - install(TARGETS colorSchemeTools LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(TARGETS colorSchemeTools LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/squawk) endif() From 3cc7db8eff99f8d414a8798f3272fd41a6ad66c8 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 27 Oct 2024 19:31:47 +0200 Subject: [PATCH 129/137] A workaround to store plugins in a subdirectory --- CMakeLists.txt | 7 ++++++- core/passwordStorageEngines/kwallet.cpp | 3 ++- core/squawk.h | 6 ++---- shared/global.cpp | 4 ++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e2465cf..aa028e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.16) project(squawk VERSION 0.2.3 LANGUAGES CXX) cmake_policy(SET CMP0076 NEW) @@ -211,6 +211,11 @@ if(CMAKE_COMPILER_IS_GNUCXX) target_compile_options(squawk PRIVATE ${COMPILE_OPTIONS}) endif(CMAKE_COMPILER_IS_GNUCXX) +# I am not really sure about this solution +# This should enable plugins to be found in path like /usr/lib/squawk instead of just /usr/lib +set(PLUGIN_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/squawk") +add_compile_definitions(PLUGIN_PATH="${PLUGIN_PATH}") + add_subdirectory(main) add_subdirectory(core) add_subdirectory(external/simpleCrypt) diff --git a/core/passwordStorageEngines/kwallet.cpp b/core/passwordStorageEngines/kwallet.cpp index 0dfe071..c92085b 100644 --- a/core/passwordStorageEngines/kwallet.cpp +++ b/core/passwordStorageEngines/kwallet.cpp @@ -28,7 +28,8 @@ Core::PSE::KWallet::CreateFolder Core::PSE::KWallet::createFolder = 0; Core::PSE::KWallet::SetFolder Core::PSE::KWallet::setFolder = 0; Core::PSE::KWallet::SupportState Core::PSE::KWallet::sState = Core::PSE::KWallet::initial; -QLibrary Core::PSE::KWallet::lib("kwalletWrapper"); + +QLibrary Core::PSE::KWallet::lib(QString("%1/kwalletWrapper").arg(PLUGIN_PATH)); Core::PSE::KWallet::KWallet(): QObject(), diff --git a/core/squawk.h b/core/squawk.h index 2ee122e..f00500e 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -42,10 +42,8 @@ #include "passwordStorageEngines/kwallet.h" #endif -namespace Core -{ -class Squawk : public QObject -{ +namespace Core { +class Squawk : public QObject { Q_OBJECT public: diff --git a/shared/global.cpp b/shared/global.cpp index 6618426..8277ff5 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -50,12 +50,12 @@ Shared::Global* Shared::Global::instance = 0; const std::set Shared::Global::supportedImagesExts = {"png", "jpg", "webp", "jpeg", "gif", "svg"}; #ifdef WITH_KIO -QLibrary Shared::Global::openFileManagerWindowJob("openFileManagerWindowJob"); +QLibrary Shared::Global::openFileManagerWindowJob(QString("%1/openFileManagerWindowJob").arg(PLUGIN_PATH)); Shared::Global::HighlightInFileManager Shared::Global::hfm = 0; #endif #ifdef WITH_KCONFIG -QLibrary Shared::Global::colorSchemeTools("colorSchemeTools"); +QLibrary Shared::Global::colorSchemeTools(QString("%1/colorSchemeTools").arg(PLUGIN_PATH)); Shared::Global::CreatePreview Shared::Global::createPreview = 0; Shared::Global::DeletePreview Shared::Global::deletePreview = 0; Shared::Global::ColorSchemeName Shared::Global::colorSchemeName = 0; From 85ff6c25ba598695ebc0c40e8e889e7118c530f9 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 27 Oct 2024 20:02:34 +0200 Subject: [PATCH 130/137] find boos cmake new policy magick instead of convert rendering images --- CMakeLists.txt | 3 ++- main/root.cpp | 15 +++++++-------- resources/CMakeLists.txt | 33 +++++++++++++++------------------ 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aa028e8..7b00bfc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ project(squawk VERSION 0.2.3 LANGUAGES CXX) cmake_policy(SET CMP0076 NEW) cmake_policy(SET CMP0077 NEW) cmake_policy(SET CMP0079 NEW) +cmake_policy(SET CMP0167 NEW) set(CMAKE_CXX_STANDARD 17) set(QT_VERSION_MAJOR 5) @@ -33,7 +34,7 @@ option(SYSTEM_LMDBAL "Use system lmdbal lib" ON) option(WITH_KWALLET "Build KWallet support module" ON) option(WITH_KIO "Build KIO support module" ON) option(WITH_KCONFIG "Build KConfig support module" ON) -option(WITH_OMEMO "Build OMEMO support module" OFF) #it should be off by default untill I sourt the problems out +option(WITH_OMEMO "Build OMEMO support module" OFF) #it should be off by default untill I sort the problems out # Dependencies ## Qt diff --git a/main/root.cpp b/main/root.cpp index d43ae2f..87d97bb 100644 --- a/main/root.cpp +++ b/main/root.cpp @@ -58,6 +58,7 @@ Root::~Root() { delete gui; if (core != nullptr) delete core; + delete coreThread; } delete global; @@ -72,13 +73,13 @@ void Root::initializeTranslation() { bool found = false; for (QString share : shares) { found = currentTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n"); - if (found) { + if (found) break; - } } - if (!found) { + + if (!found) currentTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath()); - } + installTranslator(¤tTranslator); } @@ -94,18 +95,16 @@ bool Root::initializeSettings() { QVariant vs = settings.value("style"); if (vs.isValid()) { QString style = vs.toString().toLower(); - if (style != "system") { + if (style != "system") Shared::Global::setStyle(style); - } } if (Shared::Global::supported("colorSchemeTools")) { QVariant vt = settings.value("theme"); if (vt.isValid()) { QString theme = vt.toString(); - if (theme.toLower() != "system") { + if (theme.toLower() != "system") Shared::Global::setTheme(theme); - } } } diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt index 9288650..717abf2 100644 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -3,11 +3,8 @@ target_sources(squawk PRIVATE resources.qrc) configure_file(images/logo.svg squawk.svg COPYONLY) configure_file(squawk.rc squawk.rc COPYONLY) -if(WIN32) - set(CONVERT_BIN magick convert) -else(WIN32) - set(CONVERT_BIN convert) -endif(WIN32) +set(CONVERT_BIN magick) + execute_process(COMMAND ${CONVERT_BIN} -background none -size 48x48 squawk.svg squawk48.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) execute_process(COMMAND ${CONVERT_BIN} -background none -size 64x64 squawk.svg squawk64.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) execute_process(COMMAND ${CONVERT_BIN} -background none -size 128x128 squawk.svg squawk128.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) @@ -18,20 +15,20 @@ if (WIN32) set(SQUAWK_WIN_RC "${CMAKE_CURRENT_BINARY_DIR}/squawk.rc") set(SQUAWK_WIN_RC "${SQUAWK_WIN_RC}" PARENT_SCOPE) target_sources(squawk PRIVATE ${SQUAWK_WIN_RC}) -endif(WIN32) +endif (WIN32) if (APPLE) file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/icns.iconset") - execute_process(COMMAND convert -background none -size 16x16 squawk.svg icns.iconset/icon_16x16.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND convert -background none -resize !32x32 squawk.svg "icns.iconset/icon_16x16@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND convert -background none -resize !32x32 squawk.svg "icns.iconset/icon_32x32.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND convert -background none -resize !64x64 squawk.svg "icns.iconset/icon_32x32@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND convert -background none -resize !128x128 squawk.svg "icns.iconset/icon_128x128.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND convert -background none -resize !256x256 squawk.svg "icns.iconset/icon_128x128@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND convert -background none -resize !256x256 squawk.svg "icns.iconset/icon_256x256.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND convert -background none -resize !512x512 squawk.svg "icns.iconset/icon_256x256@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND convert -background none -resize !512x512 squawk.svg "icns.iconset/icon_512x512.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - execute_process(COMMAND convert -background none -resize !1024x1024 squawk.svg "icns.iconset/icon_512x512@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -size 16x16 squawk.svg icns.iconset/icon_16x16.png WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -resize !32x32 squawk.svg "icns.iconset/icon_16x16@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -resize !32x32 squawk.svg "icns.iconset/icon_32x32.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -resize !64x64 squawk.svg "icns.iconset/icon_32x32@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -resize !128x128 squawk.svg "icns.iconset/icon_128x128.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -resize !256x256 squawk.svg "icns.iconset/icon_128x128@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -resize !256x256 squawk.svg "icns.iconset/icon_256x256.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -resize !512x512 squawk.svg "icns.iconset/icon_256x256@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -resize !512x512 squawk.svg "icns.iconset/icon_512x512.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + execute_process(COMMAND ${CONVERT_BIN} -background none -resize !1024x1024 squawk.svg "icns.iconset/icon_512x512@2x.png" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) execute_process(COMMAND iconutil -c icns "icns.iconset" -o "squawk.icns" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) set(MACOSX_BUNDLE_ICON_FILE squawk.icns) set(MACOSX_BUNDLE_ICON_FILE ${MACOSX_BUNDLE_ICON_FILE} PARENT_SCOPE) @@ -47,8 +44,8 @@ if (APPLE) MACOSX_BUNDLE_ICON_FILE "${MACOSX_BUNDLE_ICON_FILE}" # TODO MACOSX_BUNDLE_BUNDLE_NAME "Squawk" MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/cmake/MacOSXBundleInfo.plist.in) - endif(APPLE) - endif() + endif (APPLE) + endif () endif (APPLE) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/squawk.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps) From 9a44ae1fa59d09ae83a05149b4f16b4633ab28af Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 17 Nov 2024 20:25:33 +0200 Subject: [PATCH 131/137] SimpleCrypt password jamming is now optional --- CMakeLists.txt | 9 +++++-- core/squawk.cpp | 22 ++++++++++++++--- core/squawk.h | 1 - shared/global.cpp | 44 ++++++++++++++++++--------------- shared/global.h | 2 +- ui/widgets/accounts/account.cpp | 23 +++++++++-------- 6 files changed, 62 insertions(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b00bfc..b537fe0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ option(WITH_KWALLET "Build KWallet support module" ON) option(WITH_KIO "Build KIO support module" ON) option(WITH_KCONFIG "Build KConfig support module" ON) option(WITH_OMEMO "Build OMEMO support module" OFF) #it should be off by default untill I sort the problems out +option(WITH_SIMPLE_CRYPT "Builds with SimpleCrypt to obfuscate password" ON) # Dependencies ## Qt @@ -176,13 +177,18 @@ target_link_libraries(squawk Qt${QT_VERSION_MAJOR}::Xml LMDBAL::LMDBAL QXmpp::QXmpp - simpleCrypt ) if (WITH_OMEMO) target_link_libraries(squawk PRIVATE QXmpp::Omemo) endif () +if (WITH_SIMPLE_CRYPT) + target_compile_definitions(squawk PRIVATE WITH_SIMPLE_CRYPT) + add_subdirectory(external/simpleCrypt) + target_link_libraries(squawk PRIVATE simpleCrypt) +endif () + ## Link thread libraries on Linux if(UNIX AND NOT APPLE) set(THREADS_PREFER_PTHREAD_FLAG ON) @@ -219,7 +225,6 @@ add_compile_definitions(PLUGIN_PATH="${PLUGIN_PATH}") add_subdirectory(main) add_subdirectory(core) -add_subdirectory(external/simpleCrypt) add_subdirectory(packaging) add_subdirectory(plugins) add_subdirectory(resources) diff --git a/core/squawk.cpp b/core/squawk.cpp index 1888487..5978651 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -22,6 +22,10 @@ #include #include +#ifdef WITH_SIMPLE_CRYPT +#include "external/simpleCrypt/simplecrypt.h" +#endif + Core::Squawk::Squawk(QObject* parent): QObject(parent), accounts(), @@ -71,7 +75,6 @@ void Core::Squawk::stop() { QSettings settings; settings.beginGroup("core"); settings.beginWriteArray("accounts"); - SimpleCrypt crypto(passwordHash); for (std::deque::size_type i = 0; i < accounts.size(); ++i) { settings.setArrayIndex(i); Account* acc = accounts[i]; @@ -84,7 +87,13 @@ void Core::Squawk::stop() { password = acc->getPassword(); break; case Shared::AccountPassword::jammed: - password = crypto.encryptToString(acc->getPassword()); +#ifdef WITH_SIMPLE_CRYPT2 + password = SimpleCrypt(passwordHash).encryptToString(acc->getPassword()); +#else + qDebug() << "The password for account" << acc->getName() << "is set to be jammed, but Squawk was compiled without SimpleCrypt support"; + qDebug("Can not encode password, setting this account to always ask password mode"); + ap = Shared::AccountPassword::alwaysAsk; +#endif break; default: break; @@ -697,17 +706,24 @@ void Core::Squawk::readSettings() { settings.value("passwordType", static_cast(Shared::AccountPassword::plain)).toInt() ); + QString name = settings.value("name").toString(); QString password = settings.value("password", "").toString(); if (passwordType == Shared::AccountPassword::jammed) { +#ifdef WITH_SIMPLE_CRYPT SimpleCrypt crypto(passwordHash); password = crypto.decryptToString(password); +#else + qDebug() << "The password for account" << name << "is jammed, but Squawk was compiled without SimpleCrypt support"; + qDebug("Can not decode password, setting this account to always ask password mode"); + passwordType = Shared::AccountPassword::alwaysAsk; +#endif } addAccount( settings.value("login").toString(), settings.value("server").toString(), password, - settings.value("name").toString(), + name, settings.value("resource").toString(), settings.value("active").toBool(), passwordType diff --git a/core/squawk.h b/core/squawk.h index f00500e..8586498 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -33,7 +33,6 @@ #include "shared/global.h" #include "shared/info.h" #include "shared/clientinfo.h" -#include "external/simpleCrypt/simplecrypt.h" #include #include diff --git a/shared/global.cpp b/shared/global.cpp index 8277ff5..efa85cf 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -21,6 +21,13 @@ #include "enums.h" #include "ui/models/roster.h" + +#ifdef WITH_SIMPLE_CRYPT +#define SIMPLE_CRYPT_ENABLED true +#else +#define SIMPLE_CRYPT_ENABLED false +#endif + #ifdef WITH_OMEMO constexpr bool OMEMO_SUPPORT = true; #else @@ -36,11 +43,10 @@ QFont getFont (QFontDatabase::SystemFont type, bool bold = false, bool italic = if (factor != 1.0) { float ps = font.pointSizeF(); - if (ps != -1) { + if (ps != -1) font.setPointSizeF(ps * factor); - } else { + else font.setPointSize(font.pointSize() * factor); - } } return font; @@ -148,10 +154,11 @@ Shared::Global::Global(): smallFontMetrics(smallFont), headerFontMetrics(headerFont), titleFontMetrics(titleFont), - pluginSupport({ + optionalFeatures({ {"KWallet", false}, {"openFileManagerWindowJob", false}, - {"colorSchemeTools", false} + {"colorSchemeTools", false}, + {"simpleCryptJammedPassword", SIMPLE_CRYPT_ENABLED} }), fileCache() { @@ -197,8 +204,7 @@ Shared::Global::Global(): static const QSize defaultIconFileInfoHeight(50, 50); -Shared::Global::FileInfo Shared::Global::getFileInfo(const QString& path) -{ +Shared::Global::FileInfo Shared::Global::getFileInfo(const QString& path) { std::map::const_iterator itr = instance->fileCache.find(path); if (itr == instance->fileCache.end()) { QMimeDatabase db; @@ -275,17 +281,17 @@ QString Shared::Global::getName(EncryptionProtocol ep) { } void Shared::Global::setSupported(const QString& pluginName, bool support) { - std::map::iterator itr = instance->pluginSupport.find(pluginName); - if (itr != instance->pluginSupport.end()) { + std::map::iterator itr = instance->optionalFeatures.find(pluginName); + if (itr != instance->optionalFeatures.end()) { itr->second = support; } } bool Shared::Global::supported(const QString& pluginName) { - std::map::iterator itr = instance->pluginSupport.find(pluginName); - if (itr != instance->pluginSupport.end()) { + std::map::iterator itr = instance->optionalFeatures.find(pluginName); + if (itr != instance->optionalFeatures.end()) return itr->second; - } + return false; } @@ -325,11 +331,10 @@ void Shared::Global::highlightInFileManager(const QString& path) QString output = proc.readLine().simplified(); QString folder; - if (info.isDir()) { + if (info.isDir()) folder = info.canonicalFilePath(); - } else { + else folder = info.canonicalPath(); - } if (output.contains(dolphinReg)) { //there is a bug on current (21.04.0) dolphin, it works correct only if you already have dolphin launched @@ -389,20 +394,19 @@ void Shared::Global::setTheme(const QString& path) { } void Shared::Global::setStyle(const QString& style) { - if (style.toLower() == "system") { + if (style.toLower() == "system") QApplication::setStyle(getInstance()->defaultSystemStyle); - } else { + else QApplication::setStyle(style); - } } #define FROM_INT_INPL(Enum) \ template<> \ Enum Shared::Global::fromInt(int src) \ { \ - if (src < static_cast(Enum##Lowest) || src > static_cast(Enum##Highest)) { \ + if (src < static_cast(Enum##Lowest) || src > static_cast(Enum##Highest)) \ throw EnumOutOfRange(#Enum); \ - } \ + \ return static_cast(src); \ } \ template<> \ diff --git a/shared/global.h b/shared/global.h index 6d23c2f..627903f 100644 --- a/shared/global.h +++ b/shared/global.h @@ -135,7 +135,7 @@ namespace Shared { private: static Global* instance; - std::map pluginSupport; + std::map optionalFeatures; std::map fileCache; #ifdef WITH_KIO diff --git a/ui/widgets/accounts/account.cpp b/ui/widgets/accounts/account.cpp index 164af6c..2d70603 100644 --- a/ui/widgets/accounts/account.cpp +++ b/ui/widgets/accounts/account.cpp @@ -26,6 +26,7 @@ Account::Account(): m_ui->setupUi(this); connect(m_ui->passwordType, qOverload(&QComboBox::currentIndexChanged), this, &Account::onComboboxChange); + QStandardItemModel *model = static_cast(m_ui->passwordType->model()); for (int i = static_cast(Shared::AccountPasswordLowest); i < static_cast(Shared::AccountPasswordHighest) + 1; ++i) { Shared::AccountPassword ap = static_cast(i); @@ -34,18 +35,19 @@ Account::Account(): m_ui->passwordType->setCurrentIndex(static_cast(Shared::AccountPassword::plain)); if (!Shared::Global::supported("KWallet")) { - QStandardItemModel *model = static_cast(m_ui->passwordType->model()); QStandardItem *item = model->item(static_cast(Shared::AccountPassword::kwallet)); item->setFlags(item->flags() & ~Qt::ItemIsEnabled); } + + if (!Shared::Global::supported("simpleCryptJammedPassword")) { + QStandardItem *item = model->item(static_cast(Shared::AccountPassword::jammed)); + item->setFlags(item->flags() & ~Qt::ItemIsEnabled); + } } -Account::~Account() -{ -} +Account::~Account() {} -QMap Account::value() const -{ +QMap Account::value() const { QMap map; map["login"] = m_ui->login->text(); map["password"] = m_ui->password->text(); @@ -58,13 +60,11 @@ QMap Account::value() const return map; } -void Account::lockId() -{ +void Account::lockId() { m_ui->name->setReadOnly(true);; } -void Account::setData(const QMap& data) -{ +void Account::setData(const QMap& data) { m_ui->login->setText(data.value("login").toString()); m_ui->password->setText(data.value("password").toString()); m_ui->server->setText(data.value("server").toString()); @@ -73,8 +73,7 @@ void Account::setData(const QMap& data) m_ui->passwordType->setCurrentIndex(data.value("passwordType").toInt()); } -void Account::onComboboxChange(int index) -{ +void Account::onComboboxChange(int index) { QString description = Shared::Global::getDescription(Shared::Global::fromInt(index)); m_ui->comment->setText(description); } From 3cce057545dd654be5e41c71ba091aac512e8c20 Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 18 Nov 2024 22:43:46 +0200 Subject: [PATCH 132/137] fix: omitting from attribute not to wreck the stream fix: build without kwallet --- core/handlers/messagehandler.cpp | 2 +- core/squawk.cpp | 4 ++-- core/squawk.h | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index 094f671..ae6c695 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -431,7 +431,7 @@ QMap Core::MessageHandler::getChanges(Shared::Message& data, } QXmppMessage Core::MessageHandler::createPacket(const Shared::Message& data, const QDateTime& time, const QString& originalId) const { - QXmppMessage msg(acc->getFullJid(), data.getTo(), data.getBody(), data.getThread()); + QXmppMessage msg(QString(), data.getTo(), data.getBody(), data.getThread()); QString id(data.getId()); if (originalId.size() > 0) diff --git a/core/squawk.cpp b/core/squawk.cpp index 5978651..1d1abe5 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -33,10 +33,10 @@ Core::Squawk::Squawk(QObject* parent): state(Shared::Availability::offline), network(), isInitialized(false), - clientCache(), #ifdef WITH_KWALLET - kwallet() + kwallet(), #endif + clientCache() { connect(&network, &NetworkAccess::loadFileProgress, this, &Squawk::fileProgress); connect(&network, &NetworkAccess::loadFileError, this, &Squawk::fileError); diff --git a/core/squawk.h b/core/squawk.h index 8586498..983f5ab 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -137,11 +137,12 @@ private: Shared::Availability state; NetworkAccess network; bool isInitialized; - ClientCache clientCache; #ifdef WITH_KWALLET PSE::KWallet kwallet; #endif + + ClientCache clientCache; private slots: void addAccount( From a04693e39dec21c95afd147af7022788c0b537ec Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 19 Nov 2024 18:45:11 +0200 Subject: [PATCH 133/137] fix: build without KConfig is possible once more --- shared/defines.h | 5 +---- shared/global.cpp | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/shared/defines.h b/shared/defines.h index 227a714..8ead3f6 100644 --- a/shared/defines.h +++ b/shared/defines.h @@ -16,9 +16,6 @@ * along with this program. If not, see . */ -#ifndef SHARED_DEFINES_H -#define SHARED_DEFINES_H +#pragma once #define SHARED_UNUSED(x) (void)(x) - -#endif diff --git a/shared/global.cpp b/shared/global.cpp index efa85cf..362bf81 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -19,6 +19,7 @@ #include "global.h" #include +#include "defines.h" #include "enums.h" #include "ui/models/roster.h" @@ -361,27 +362,32 @@ void Shared::Global::highlightInFileManager(const QString& path) } QIcon Shared::Global::createThemePreview(const QString& path) { +#ifdef WITH_KCONFIG if (supported("colorSchemeTools")) { QIcon* icon = createPreview(path); QIcon localIcon = *icon; deletePreview(icon); return localIcon; - } else { - return QIcon(); } +#endif + + return QIcon(); } QString Shared::Global::getColorSchemeName(const QString& path) { +#ifdef WITH_KCONFIG if (supported("colorSchemeTools")) { QString res; colorSchemeName(path, res); return res; - } else { - return ""; } +#endif + + return ""; } void Shared::Global::setTheme(const QString& path) { +#ifdef WITH_KCONFIG if (supported("colorSchemeTools")) { if (path.toLower() == "system") { QApplication::setPalette(getInstance()->defaultSystemPalette); @@ -391,6 +397,10 @@ void Shared::Global::setTheme(const QString& path) { QApplication::setPalette(pallete); } } +#else + SHARED_UNUSED(path); + qDebug("setTheme() was called, but this version of squawk was compiled without KConfig support, ignoring"); +#endif } void Shared::Global::setStyle(const QString& style) { From d4cec645b5a6f7d6ef490b262c5a43e901013df6 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 14 Dec 2024 01:53:04 +0200 Subject: [PATCH 134/137] qt6 build --- CMakeLists.txt | 41 ++- core/CMakeLists.txt | 6 +- core/account.cpp | 1 + core/components/CMakeLists.txt | 5 +- core/components/networkaccess.cpp | 4 +- core/handlers/messagehandler.cpp | 8 +- core/handlers/messagehandler.h | 3 +- core/passwordStorageEngines/CMakeLists.txt | 7 +- core/passwordStorageEngines/kwallet.h | 2 +- .../wrappers/CMakeLists.txt | 3 +- .../wrappers/kwallet.cpp | 2 +- core/squawk.cpp | 26 +- core/utils/CMakeLists.txt | 9 + core/utils/jammer.cpp | 94 +++++++ core/utils/jammer.h | 44 ++++ external/simpleCrypt/CMakeLists.txt | 10 - external/simpleCrypt/simplecrypt.cpp | 248 ------------------ external/simpleCrypt/simplecrypt.h | 226 ---------------- plugins/CMakeLists.txt | 6 +- plugins/colorschemetools.cpp | 6 +- shared/CMakeLists.txt | 5 +- shared/clientid.h | 6 +- shared/clientinfo.h | 6 +- shared/enums.h | 4 +- shared/global.cpp | 11 +- shared/messageinfo.cpp | 16 ++ shared/messageinfo.h | 4 + ui/utils/progress.cpp | 2 +- ui/widgets/accounts/account.cpp | 5 - ui/widgets/conversation.cpp | 4 +- ui/widgets/messageline/feedview.cpp | 32 ++- ui/widgets/messageline/feedview.h | 11 +- ui/widgets/messageline/messagedelegate.cpp | 2 +- ui/widgets/settings/settingslist.cpp | 35 +-- ui/widgets/settings/settingslist.h | 17 +- 35 files changed, 279 insertions(+), 632 deletions(-) create mode 100644 core/utils/CMakeLists.txt create mode 100644 core/utils/jammer.cpp create mode 100644 core/utils/jammer.h delete mode 100644 external/simpleCrypt/CMakeLists.txt delete mode 100644 external/simpleCrypt/simplecrypt.cpp delete mode 100644 external/simpleCrypt/simplecrypt.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b537fe0..a62c800 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,6 @@ cmake_policy(SET CMP0079 NEW) cmake_policy(SET CMP0167 NEW) set(CMAKE_CXX_STANDARD 17) -set(QT_VERSION_MAJOR 5) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) @@ -35,7 +34,6 @@ option(WITH_KWALLET "Build KWallet support module" ON) option(WITH_KIO "Build KIO support module" ON) option(WITH_KCONFIG "Build KConfig support module" ON) option(WITH_OMEMO "Build OMEMO support module" OFF) #it should be off by default untill I sort the problems out -option(WITH_SIMPLE_CRYPT "Builds with SimpleCrypt to obfuscate password" ON) # Dependencies ## Qt @@ -69,9 +67,9 @@ endif () ## KIO if (WITH_KIO) - find_package(KF5KIO CONFIG) + find_package(KF${QT_VERSION_MAJOR}KIO CONFIG) - if (NOT KF5KIO_FOUND) + if (NOT KF${QT_VERSION_MAJOR}KIO_FOUND) set(WITH_KIO OFF) message("KIO package wasn't found, KIO support modules wouldn't be built") else () @@ -82,9 +80,9 @@ endif () ## KWallet if (WITH_KWALLET) - find_package(KF5Wallet CONFIG) + find_package(KF${QT_VERSION_MAJOR}Wallet CONFIG) - if (NOT KF5Wallet_FOUND) + if (NOT KF${QT_VERSION_MAJOR}Wallet_FOUND) set(WITH_KWALLET OFF) message("KWallet package wasn't found, KWallet support module wouldn't be built") else () @@ -95,13 +93,13 @@ endif () ## KConfig if (WITH_KCONFIG) - find_package(KF5Config CONFIG) - if (NOT KF5Config_FOUND) + find_package(KF${QT_VERSION_MAJOR}Config CONFIG) + if (NOT KF${QT_VERSION_MAJOR}Config_FOUND) set(WITH_KCONFIG OFF) message("KConfig package wasn't found, KConfig support modules wouldn't be built") else() - find_package(KF5ConfigWidgets CONFIG) - if (NOT KF5ConfigWidgets_FOUND) + find_package(KF${QT_VERSION_MAJOR}ConfigWidgets CONFIG) + if (NOT KF${QT_VERSION_MAJOR}ConfigWidgets_FOUND) set(WITH_KCONFIG OFF) message("KConfigWidgets package wasn't found, KConfigWidgets support modules wouldn't be built") else() @@ -115,12 +113,12 @@ endif() ## QXmpp if (SYSTEM_QXMPP) if (WITH_OMEMO) - find_package(QXmpp CONFIG COMPONENTS Omemo) + find_package(QXmppQt${QT_VERSION_MAJOR} CONFIG COMPONENTS Omemo) else () - find_package(QXmpp CONFIG) + find_package(QXmppQt${QT_VERSION_MAJOR} CONFIG) endif () - if (NOT QXmpp_FOUND) + if (NOT QXmppQt${QT_VERSION_MAJOR}_FOUND) set(SYSTEM_QXMPP OFF) message("QXmpp package wasn't found, trying to build with bundled QXmpp") else () @@ -152,8 +150,8 @@ endif () ## LMDBAL if (SYSTEM_LMDBAL) - find_package(lmdbal) - if (NOT lmdbal_FOUND) + find_package(lmdbalqt${QT_VERSION_MAJOR}) + if (NOT lmdbalqt${QT_VERSION_MAJOR}_FOUND) set(SYSTEM_LMDBAL OFF) message("LMDBAL package wasn't found, trying to build with bundled LMDBAL") else () @@ -163,9 +161,11 @@ else() message("Building with bundled LMDBAL") set(BUILD_STATIC ON) add_subdirectory(external/lmdbal) - add_library(LMDBAL::LMDBAL ALIAS LMDBAL) + add_library(LMDBALQT${QT_VERSION_MAJOR}::LMDBALQT${QT_VERSION_MAJOR} ALIAS LMDBAL) endif() +find_package(OpenSSL REQUIRED) + ## Linking target_link_libraries(squawk PRIVATE @@ -175,7 +175,8 @@ target_link_libraries(squawk Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Xml - LMDBAL::LMDBAL + LMDBALQT${QT_VERSION_MAJOR}::LMDBALQT${QT_VERSION_MAJOR} + OpenSSL::Crypto QXmpp::QXmpp ) @@ -183,12 +184,6 @@ if (WITH_OMEMO) target_link_libraries(squawk PRIVATE QXmpp::Omemo) endif () -if (WITH_SIMPLE_CRYPT) - target_compile_definitions(squawk PRIVATE WITH_SIMPLE_CRYPT) - add_subdirectory(external/simpleCrypt) - target_link_libraries(squawk PRIVATE simpleCrypt) -endif () - ## Link thread libraries on Linux if(UNIX AND NOT APPLE) set(THREADS_PREFER_PTHREAD_FLAG ON) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index a02bfe6..8baa5ad 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -23,10 +23,7 @@ set(HEADER_FILES squawk.h ) -target_sources(squawk PRIVATE - ${SOURCE_FILES} - ${HEADER_FILES} -) +target_sources(squawk PRIVATE ${SOURCE_FILES}) target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS}) @@ -34,3 +31,4 @@ add_subdirectory(handlers) add_subdirectory(passwordStorageEngines) add_subdirectory(components) add_subdirectory(delayManager) +add_subdirectory(utils) diff --git a/core/account.cpp b/core/account.cpp index 8082aeb..6143fa6 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -81,6 +81,7 @@ Core::Account::Account( config.setDomain(p_server); config.setPassword(p_password); config.setAutoAcceptSubscriptions(true); + config.setIgnoreSslErrors(true); //config.setAutoReconnectionEnabled(false); delay = new DelayManager::Manager(getBareJid()); QObject::connect(delay, &DelayManager::Manager::gotInfo, this, &Account::infoReady); diff --git a/core/components/CMakeLists.txt b/core/components/CMakeLists.txt index 77d290b..751a01f 100644 --- a/core/components/CMakeLists.txt +++ b/core/components/CMakeLists.txt @@ -12,7 +12,4 @@ set(HEADER_FILES archive.h ) -target_sources(squawk PRIVATE - ${SOURCE_FILES} - ${HEADER_FILES} -) +target_sources(squawk PRIVATE ${SOURCE_FILES}) diff --git a/core/components/networkaccess.cpp b/core/components/networkaccess.cpp index 0771dfa..59e2448 100644 --- a/core/components/networkaccess.cpp +++ b/core/components/networkaccess.cpp @@ -550,10 +550,8 @@ void Core::NetworkAccess::moveFilesDirectory(const QString& newPath) { QDir dir(currentPath); bool success = true; qDebug() << "moving" << currentPath << "to" << newPath; - for (QFileInfo fileInfo : dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System)) { - QString fileName = fileInfo.fileName(); + for (QString fileName : dir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System)) success = dir.rename(fileName, newPath + QDir::separator() + fileName) && success; - } if (!success) qDebug() << "couldn't move downloads directory, most probably downloads will be broken"; diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index ae6c695..b9ae3b6 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -492,7 +492,7 @@ void Core::MessageHandler::prepareUpload(const Shared::Message& data, bool newMe QFileInfo file(path); if (file.exists() && file.isReadable()) { pendingStateMessages.insert(std::make_pair(id, jid)); - uploadingSlotsQueue.emplace_back(path, id); + uploadingSlotsQueue.emplace_back(file, id); if (uploadingSlotsQueue.size() == 1) acc->um->requestUploadSlot(file); } else { @@ -505,10 +505,10 @@ void Core::MessageHandler::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slo if (uploadingSlotsQueue.size() == 0) { qDebug() << "HTTP Upload manager of account" << acc->name << "reports about success requesting upload slot, but none was requested"; } else { - const std::pair& pair = uploadingSlotsQueue.front(); + const std::pair& pair = uploadingSlotsQueue.front(); const QString& mId = pair.second; QString palJid = pendingStateMessages.at(mId); - acc->network->uploadFile({acc->name, palJid, mId}, pair.first, slot.putUrl(), slot.getUrl(), slot.putHeaders()); + acc->network->uploadFile({acc->name, palJid, mId}, pair.first.path(), slot.putUrl(), slot.getUrl(), slot.putHeaders()); uploadingSlotsQueue.pop_front(); if (uploadingSlotsQueue.size() > 0) @@ -522,7 +522,7 @@ void Core::MessageHandler::onUploadSlotRequestFailed(const QXmppHttpUploadReques qDebug() << "HTTP Upload manager of account" << acc->name << "reports about an error requesting upload slot, but none was requested"; qDebug() << err; } else { - const std::pair& pair = uploadingSlotsQueue.front(); + const std::pair& pair = uploadingSlotsQueue.front(); qDebug() << "Error requesting upload slot for file" << pair.first << "in account" << acc->name << ":" << err; handleUploadError(pendingStateMessages.at(pair.second), pair.second, err); diff --git a/core/handlers/messagehandler.h b/core/handlers/messagehandler.h index 15f99bf..917b98d 100644 --- a/core/handlers/messagehandler.h +++ b/core/handlers/messagehandler.h @@ -19,6 +19,7 @@ #pragma once #include +#include #include #include @@ -81,7 +82,7 @@ private: Account* acc; std::map pendingStateMessages; //key is message id, value is JID std::map pendingCorrectionMessages; //key is new mesage, value is originalOne - std::deque> uploadingSlotsQueue; + std::deque> uploadingSlotsQueue; }; } diff --git a/core/passwordStorageEngines/CMakeLists.txt b/core/passwordStorageEngines/CMakeLists.txt index 2afda3f..2a38931 100644 --- a/core/passwordStorageEngines/CMakeLists.txt +++ b/core/passwordStorageEngines/CMakeLists.txt @@ -1,9 +1,6 @@ if (WITH_KWALLET) - target_sources(squawk PRIVATE - kwallet.cpp - kwallet.h - ) + target_sources(squawk PRIVATE kwallet.cpp) add_subdirectory(wrappers) - target_include_directories(squawk PRIVATE $) + target_include_directories(squawk PRIVATE $) endif () diff --git a/core/passwordStorageEngines/kwallet.h b/core/passwordStorageEngines/kwallet.h index 28475d2..1f047e6 100644 --- a/core/passwordStorageEngines/kwallet.h +++ b/core/passwordStorageEngines/kwallet.h @@ -27,7 +27,7 @@ #include #include -#include +#include namespace Core { namespace PSE { diff --git a/core/passwordStorageEngines/wrappers/CMakeLists.txt b/core/passwordStorageEngines/wrappers/CMakeLists.txt index 432079f..6280cc0 100644 --- a/core/passwordStorageEngines/wrappers/CMakeLists.txt +++ b/core/passwordStorageEngines/wrappers/CMakeLists.txt @@ -1,4 +1,5 @@ add_library(kwalletWrapper SHARED kwallet.cpp) -target_link_libraries(kwalletWrapper PRIVATE KF5::Wallet) + +target_link_libraries(kwalletWrapper PRIVATE KF${QT_VERSION_MAJOR}::Wallet) install(TARGETS kwalletWrapper LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/squawk) diff --git a/core/passwordStorageEngines/wrappers/kwallet.cpp b/core/passwordStorageEngines/wrappers/kwallet.cpp index d899985..5c7bc99 100644 --- a/core/passwordStorageEngines/wrappers/kwallet.cpp +++ b/core/passwordStorageEngines/wrappers/kwallet.cpp @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -#include +#include extern "C" KWallet::Wallet* openWallet(const QString &name, WId w, KWallet::Wallet::OpenType ot = KWallet::Wallet::Synchronous) { return KWallet::Wallet::openWallet(name, w, ot); diff --git a/core/squawk.cpp b/core/squawk.cpp index 1d1abe5..1851eb3 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -22,9 +22,7 @@ #include #include -#ifdef WITH_SIMPLE_CRYPT -#include "external/simpleCrypt/simplecrypt.h" -#endif +#include "utils/jammer.h" Core::Squawk::Squawk(QObject* parent): QObject(parent), @@ -87,18 +85,14 @@ void Core::Squawk::stop() { password = acc->getPassword(); break; case Shared::AccountPassword::jammed: -#ifdef WITH_SIMPLE_CRYPT2 - password = SimpleCrypt(passwordHash).encryptToString(acc->getPassword()); -#else - qDebug() << "The password for account" << acc->getName() << "is set to be jammed, but Squawk was compiled without SimpleCrypt support"; - qDebug("Can not encode password, setting this account to always ask password mode"); - ap = Shared::AccountPassword::alwaysAsk; -#endif + password = Jammer::encrypt(acc->getPassword(), passwordHash); break; default: break; } + qDebug() << "Saving password for" << acc->getName() << password; + settings.setValue("name", acc->getName()); settings.setValue("server", acc->getServer()); settings.setValue("login", acc->getLogin()); @@ -708,16 +702,8 @@ void Core::Squawk::readSettings() { QString name = settings.value("name").toString(); QString password = settings.value("password", "").toString(); - if (passwordType == Shared::AccountPassword::jammed) { -#ifdef WITH_SIMPLE_CRYPT - SimpleCrypt crypto(passwordHash); - password = crypto.decryptToString(password); -#else - qDebug() << "The password for account" << name << "is jammed, but Squawk was compiled without SimpleCrypt support"; - qDebug("Can not decode password, setting this account to always ask password mode"); - passwordType = Shared::AccountPassword::alwaysAsk; -#endif - } + if (passwordType == Shared::AccountPassword::jammed) + password = Jammer::decrypt(password, passwordHash); addAccount( settings.value("login").toString(), diff --git a/core/utils/CMakeLists.txt b/core/utils/CMakeLists.txt new file mode 100644 index 0000000..e030130 --- /dev/null +++ b/core/utils/CMakeLists.txt @@ -0,0 +1,9 @@ +set(SOURCE_FILES + jammer.cpp +) + +set(HEADER_FILES + jammer.h +) + +target_sources(squawk PRIVATE ${SOURCE_FILES}) diff --git a/core/utils/jammer.cpp b/core/utils/jammer.cpp new file mode 100644 index 0000000..1714c6b --- /dev/null +++ b/core/utils/jammer.cpp @@ -0,0 +1,94 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 "jammer.h" + +#include + +#include +#include + +struct CipherCtxDeleter { + void operator()(EVP_CIPHER_CTX* ctx) const { + EVP_CIPHER_CTX_free(ctx); + } +}; +typedef std::unique_ptr CipherCtx; + +QString Core::Jammer::encrypt(const QString& plaintext, qint64 key) { + QByteArray encryptedData = process(plaintext.toUtf8(), intToKey(key), true); + + return QString::fromUtf8(encryptedData.toHex()); +} + +QString Core::Jammer::decrypt(const QString& ciphertext, qint64 key) { + QByteArray encryptedData = QByteArray::fromHex(ciphertext.toUtf8()); + QByteArray decryptedData = process(encryptedData, intToKey(key), false); + + return QString::fromUtf8(decryptedData); +} + +std::string Core::Jammer::getOpenSSLErrorString() { + unsigned long errCode = ERR_get_error(); + if (errCode == 0) { + return "No OpenSSL error"; + } + const char *errMsg = ERR_reason_error_string(errCode); + return errMsg ? std::string(errMsg) : "Unknown OpenSSL error"; +} + +QByteArray Core::Jammer::process(const QByteArray& input, const QByteArray& key, bool encrypt) { + CipherCtx ctx(EVP_CIPHER_CTX_new()); + if (!ctx) + throw std::runtime_error("Failed to create password jammer context"); + + QByteArray output(input.size() + 16, 0); + int outputLength = 0; + int finalLength = 0; + + if (!ctx) + throw std::runtime_error("Failed to create EVP_CIPHER_CTX: " + getOpenSSLErrorString()); + + if (EVP_CipherInit_ex(ctx.get(), EVP_chacha20(), nullptr, toUChar(key), nullptr, encrypt) != 1) + throw std::runtime_error("EVP_CipherInit_ex failed. " + getOpenSSLErrorString()); + + if (EVP_CipherUpdate(ctx.get(), toUChar(output), &outputLength, toUChar(input), input.size()) != 1) + throw std::runtime_error("EVP_CipherUpdate failed. " + getOpenSSLErrorString()); + + if (EVP_CipherFinal_ex(ctx.get(), toUChar(output) + outputLength, &finalLength) != 1) + throw std::runtime_error("EVP_CipherFinal_ex failed. " + getOpenSSLErrorString()); + + output.resize(outputLength + finalLength); + + return output; +} + +QByteArray Core::Jammer::intToKey(qint64 key, int keySize) { + QByteArray keyBytes(reinterpret_cast(&key), sizeof(key)); + while (keyBytes.size() < keySize) + keyBytes.append(keyBytes); + + keyBytes.truncate(keySize); + return keyBytes; +} + +unsigned char* Core::Jammer::toUChar(QByteArray& data) { + return reinterpret_cast(data.data());} + +const unsigned char* Core::Jammer::toUChar(const QByteArray& data) { + return reinterpret_cast(data.constData());} diff --git a/core/utils/jammer.h b/core/utils/jammer.h new file mode 100644 index 0000000..456c14b --- /dev/null +++ b/core/utils/jammer.h @@ -0,0 +1,44 @@ +/* + * Squawk messenger. + * Copyright (C) 2019 Yury Gubich + * + * 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 . + */ + +#pragma once + +#include + +#include +#include + +namespace Core { + +class Jammer { +public: + static QString encrypt(const QString& plaintext, qint64 key); + static QString decrypt(const QString& ciphertext, qint64 key); + +private: + Jammer() = delete; + + static QByteArray process(const QByteArray& input, const QByteArray& key, bool encrypt); + static QByteArray intToKey(qint64 key, int keySize = 32); + + static unsigned char* toUChar(QByteArray& data); + static const unsigned char* toUChar(const QByteArray& data); + static std::string getOpenSSLErrorString(); +}; + +} diff --git a/external/simpleCrypt/CMakeLists.txt b/external/simpleCrypt/CMakeLists.txt deleted file mode 100644 index 5f274ba..0000000 --- a/external/simpleCrypt/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -cmake_minimum_required(VERSION 3.5) -project(simplecrypt LANGUAGES CXX) - -set(CMAKE_AUTOMOC ON) - -find_package(Qt5 COMPONENTS Core REQUIRED) - -add_library(simpleCrypt STATIC simplecrypt.cpp simplecrypt.h) - -target_link_libraries(simpleCrypt Qt5::Core) diff --git a/external/simpleCrypt/simplecrypt.cpp b/external/simpleCrypt/simplecrypt.cpp deleted file mode 100644 index 093403e..0000000 --- a/external/simpleCrypt/simplecrypt.cpp +++ /dev/null @@ -1,248 +0,0 @@ -/* - Copyright (c) 2011, Andre Somers - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Rathenau Instituut, Andre Somers nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "simplecrypt.h" -#include -#include -#include -#include -#include -#include - -SimpleCrypt::SimpleCrypt(): -m_key(0), -m_compressionMode(CompressionAuto), -m_protectionMode(ProtectionChecksum), -m_lastError(ErrorNoError) {} - -SimpleCrypt::SimpleCrypt(quint64 key): -m_key(key), -m_compressionMode(CompressionAuto), -m_protectionMode(ProtectionChecksum), -m_lastError(ErrorNoError) -{ - splitKey(); -} - -void SimpleCrypt::setKey(quint64 key) -{ - m_key = key; - splitKey(); -} - -void SimpleCrypt::splitKey() -{ - m_keyParts.clear(); - m_keyParts.resize(8); - for (int i=0;i<8;i++) { - quint64 part = m_key; - for (int j=i; j>0; j--) - part = part >> 8; - part = part & 0xff; - m_keyParts[i] = static_cast(part); - } -} - -QByteArray SimpleCrypt::encryptToByteArray(const QString& plaintext) -{ - QByteArray plaintextArray = plaintext.toUtf8(); - return encryptToByteArray(plaintextArray); -} - -QByteArray SimpleCrypt::encryptToByteArray(QByteArray plaintext) -{ - if (m_keyParts.isEmpty()) { - qWarning() << "No key set."; - m_lastError = ErrorNoKeySet; - return QByteArray(); - } - - - QByteArray ba = plaintext; - - CryptoFlags flags = CryptoFlagNone; - if (m_compressionMode == CompressionAlways) { - ba = qCompress(ba, 9); //maximum compression - flags |= CryptoFlagCompression; - } else if (m_compressionMode == CompressionAuto) { - QByteArray compressed = qCompress(ba, 9); - if (compressed.count() < ba.count()) { - ba = compressed; - flags |= CryptoFlagCompression; - } - } - - QByteArray integrityProtection; - if (m_protectionMode == ProtectionChecksum) { - flags |= CryptoFlagChecksum; - QDataStream s(&integrityProtection, QIODevice::WriteOnly); - s << qChecksum(ba.constData(), ba.length()); - } else if (m_protectionMode == ProtectionHash) { - flags |= CryptoFlagHash; - QCryptographicHash hash(QCryptographicHash::Sha1); - hash.addData(ba); - - integrityProtection += hash.result(); - } - - //prepend a random char to the string - char randomChar = char(QRandomGenerator::global()->generate() & 0xFF); - ba = randomChar + integrityProtection + ba; - - int pos(0); - char lastChar(0); - - int cnt = ba.count(); - - while (pos < cnt) { - ba[pos] = ba.at(pos) ^ m_keyParts.at(pos % 8) ^ lastChar; - lastChar = ba.at(pos); - ++pos; - } - - QByteArray resultArray; - resultArray.append(char(0x03)); //version for future updates to algorithm - resultArray.append(char(flags)); //encryption flags - resultArray.append(ba); - - m_lastError = ErrorNoError; - return resultArray; -} - -QString SimpleCrypt::encryptToString(const QString& plaintext) -{ - QByteArray plaintextArray = plaintext.toUtf8(); - QByteArray cypher = encryptToByteArray(plaintextArray); - QString cypherString = QString::fromLatin1(cypher.toBase64()); - return cypherString; -} - -QString SimpleCrypt::encryptToString(QByteArray plaintext) -{ - QByteArray cypher = encryptToByteArray(plaintext); - QString cypherString = QString::fromLatin1(cypher.toBase64()); - return cypherString; -} - -QString SimpleCrypt::decryptToString(const QString &cyphertext) -{ - QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); - QByteArray plaintextArray = decryptToByteArray(cyphertextArray); - QString plaintext = QString::fromUtf8(plaintextArray, plaintextArray.size()); - - return plaintext; -} - -QString SimpleCrypt::decryptToString(QByteArray cypher) -{ - QByteArray ba = decryptToByteArray(cypher); - QString plaintext = QString::fromUtf8(ba, ba.size()); - - return plaintext; -} - -QByteArray SimpleCrypt::decryptToByteArray(const QString& cyphertext) -{ - QByteArray cyphertextArray = QByteArray::fromBase64(cyphertext.toLatin1()); - QByteArray ba = decryptToByteArray(cyphertextArray); - - return ba; -} - -QByteArray SimpleCrypt::decryptToByteArray(QByteArray cypher) -{ - if (m_keyParts.isEmpty()) { - qWarning() << "No key set."; - m_lastError = ErrorNoKeySet; - return QByteArray(); - } - - QByteArray ba = cypher; - - if( cypher.count() < 3 ) - return QByteArray(); - - char version = ba.at(0); - - if (version !=3) { //we only work with version 3 - m_lastError = ErrorUnknownVersion; - qWarning() << "Invalid version or not a cyphertext."; - return QByteArray(); - } - - CryptoFlags flags = CryptoFlags(ba.at(1)); - - ba = ba.mid(2); - int pos(0); - int cnt(ba.count()); - char lastChar = 0; - - while (pos < cnt) { - char currentChar = ba[pos]; - ba[pos] = ba.at(pos) ^ lastChar ^ m_keyParts.at(pos % 8); - lastChar = currentChar; - ++pos; - } - - ba = ba.mid(1); //chop off the random number at the start - - bool integrityOk(true); - if (flags.testFlag(CryptoFlagChecksum)) { - if (ba.length() < 2) { - m_lastError = ErrorIntegrityFailed; - return QByteArray(); - } - quint16 storedChecksum; - { - QDataStream s(&ba, QIODevice::ReadOnly); - s >> storedChecksum; - } - ba = ba.mid(2); - quint16 checksum = qChecksum(ba.constData(), ba.length()); - integrityOk = (checksum == storedChecksum); - } else if (flags.testFlag(CryptoFlagHash)) { - if (ba.length() < 20) { - m_lastError = ErrorIntegrityFailed; - return QByteArray(); - } - QByteArray storedHash = ba.left(20); - ba = ba.mid(20); - QCryptographicHash hash(QCryptographicHash::Sha1); - hash.addData(ba); - integrityOk = (hash.result() == storedHash); - } - - if (!integrityOk) { - m_lastError = ErrorIntegrityFailed; - return QByteArray(); - } - - if (flags.testFlag(CryptoFlagCompression)) - ba = qUncompress(ba); - - m_lastError = ErrorNoError; - return ba; -} diff --git a/external/simpleCrypt/simplecrypt.h b/external/simpleCrypt/simplecrypt.h deleted file mode 100644 index 0052618..0000000 --- a/external/simpleCrypt/simplecrypt.h +++ /dev/null @@ -1,226 +0,0 @@ -/* - Copyright (c) 2011, Andre Somers - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the Rathenau Instituut, Andre Somers nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL ANDRE SOMERS BE LIABLE FOR ANY - DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef SIMPLECRYPT_H -#define SIMPLECRYPT_H -#include -#include -#include -#include - -/** - @ short Simple encrypt*ion and decryption of strings and byte arrays - - This class provides a simple implementation of encryption and decryption - of strings and byte arrays. - - @warning The encryption provided by this class is NOT strong encryption. It may - help to shield things from curious eyes, but it will NOT stand up to someone - determined to break the encryption. Don't say you were not warned. - - The class uses a 64 bit key. Simply create an instance of the class, set the key, - and use the encryptToString() method to calculate an encrypted version of the input string. - To decrypt that string again, use an instance of SimpleCrypt initialized with - the same key, and call the decryptToString() method with the encrypted string. If the key - matches, the decrypted version of the string will be returned again. - - If you do not provide a key, or if something else is wrong, the encryption and - decryption function will return an empty string or will return a string containing nonsense. - lastError() will return a value indicating if the method was succesful, and if not, why not. - - SimpleCrypt is prepared for the case that the encryption and decryption - algorithm is changed in a later version, by prepending a version identifier to the cypertext. - */ -class SimpleCrypt -{ -public: - /** - CompressionMode describes if compression will be applied to the data to be - encrypted. - */ - enum CompressionMode { - CompressionAuto, /*!< Only apply compression if that results in a shorter plaintext. */ - CompressionAlways, /*!< Always apply compression. Note that for short inputs, a compression may result in longer data */ - CompressionNever /*!< Never apply compression. */ - }; - /** - IntegrityProtectionMode describes measures taken to make it possible to detect problems with the data - or wrong decryption keys. - - Measures involve adding a checksum or a cryptograhpic hash to the data to be encrypted. This - increases the length of the resulting cypertext, but makes it possible to check if the plaintext - appears to be valid after decryption. - */ - enum IntegrityProtectionMode { - ProtectionNone, /*!< The integerity of the encrypted data is not protected. It is not really possible to detect a wrong key, for instance. */ - ProtectionChecksum,/*!< A simple checksum is used to verify that the data is in order. If not, an empty string is returned. */ - ProtectionHash /*!< A cryptographic hash is used to verify the integrity of the data. This method produces a much stronger, but longer check */ - }; - /** - Error describes t*he type of error that occured. - */ - enum Error { - ErrorNoError, /*!< No error occurred. */ - ErrorNoKeySet, /*!< No key was set. You can not encrypt or decrypt without a valid key. */ - ErrorUnknownVersion, /*!< The version of this data is unknown, or the data is otherwise not valid. */ - ErrorIntegrityFailed, /*!< The integrity check of the data failed. Perhaps the wrong key was used. */ - }; - - /** - Constructor. * - - Constructs a SimpleCrypt instance without a valid key set on it. - */ - SimpleCrypt(); - /** - Constructor. * - - Constructs a SimpleCrypt instance and initializes it with the given @arg key. - */ - explicit SimpleCrypt(quint64 key); - - /** - ( Re-) initializes* the key with the given @arg key. - */ - void setKey(quint64 key); - /** - Returns true if SimpleCrypt has been initialized with a key. - */ - bool hasKey() const {return !m_keyParts.isEmpty();} - - /** - Sets the compress*ion mode to use when encrypting data. The default mode is Auto. - - Note that decryption is not influenced by this mode, as the decryption recognizes - what mode was used when encrypting. - */ - void setCompressionMode(CompressionMode mode) {m_compressionMode = mode;} - /** - Returns the CompressionMode that is currently in use. - */ - CompressionMode compressionMode() const {return m_compressionMode;} - - /** - Sets the integrity mode to use when encrypting data. The default mode is Checksum. - - Note that decryption is not influenced by this mode, as the decryption recognizes - what mode was used when encrypting. - */ - void setIntegrityProtectionMode(IntegrityProtectionMode mode) {m_protectionMode = mode;} - /** - Returns the IntegrityProtectionMode that is currently in use. - */ - IntegrityProtectionMode integrityProtectionMode() const {return m_protectionMode;} - - /** - Returns the last *error that occurred. - */ - Error lastError() const {return m_lastError;} - - /** - Encrypts the @arg* plaintext string with the key the class was initialized with, and returns - a cyphertext the result. The result is a base64 encoded version of the binary array that is the - actual result of the string, so it can be stored easily in a text format. - */ - QString encryptToString(const QString& plaintext) ; - /** - Encrypts the @arg* plaintext QByteArray with the key the class was initialized with, and returns - a cyphertext the result. The result is a base64 encoded version of the binary array that is the - actual result of the encryption, so it can be stored easily in a text format. - */ - QString encryptToString(QByteArray plaintext) ; - /** - Encrypts the @arg* plaintext string with the key the class was initialized with, and returns - a binary cyphertext in a QByteArray the result. - - This method returns a byte array, that is useable for storing a binary format. If you need - a string you can store in a text file, use encryptToString() instead. - */ - QByteArray encryptToByteArray(const QString& plaintext) ; - /** - Encrypts the @arg* plaintext QByteArray with the key the class was initialized with, and returns - a binary cyphertext in a QByteArray the result. - - This method returns a byte array, that is useable for storing a binary format. If you need - a string you can store in a text file, use encryptToString() instead. - */ - QByteArray encryptToByteArray(QByteArray plaintext) ; - - /** - Decrypts a cypher*text string encrypted with this class with the set key back to the - plain text version. - - If an error occured, such as non-matching keys between encryption and decryption, - an empty string or a string containing nonsense may be returned. - */ - QString decryptToString(const QString& cyphertext) ; - /** - Decrypts a cypher*text string encrypted with this class with the set key back to the - plain text version. - - If an error occured, such as non-matching keys between encryption and decryption, - an empty string or a string containing nonsense may be returned. - */ - QByteArray decryptToByteArray(const QString& cyphertext) ; - /** - Decrypts a cypher*text binary encrypted with this class with the set key back to the - plain text version. - - If an error occured, such as non-matching keys between encryption and decryption, - an empty string or a string containing nonsense may be returned. - */ - QString decryptToString(QByteArray cypher) ; - /** - Decrypts a cypher*text binary encrypted with this class with the set key back to the - plain text version. - - If an error occured, such as non-matching keys between encryption and decryption, - an empty string or a string containing nonsense may be returned. - */ - QByteArray decryptToByteArray(QByteArray cypher) ; - - //enum to describe options that have been used for the encryption. Currently only one, but - //that only leaves room for future extensions like adding a cryptographic hash... - enum CryptoFlag{CryptoFlagNone = 0, - CryptoFlagCompression = 0x01, - CryptoFlagChecksum = 0x02, - CryptoFlagHash = 0x04 - }; - Q_DECLARE_FLAGS(CryptoFlags, CryptoFlag); -private: - - void splitKey(); - - quint64 m_key; - QVector m_keyParts; - CompressionMode m_compressionMode; - IntegrityProtectionMode m_protectionMode; - Error m_lastError; -}; -Q_DECLARE_OPERATORS_FOR_FLAGS(SimpleCrypt::CryptoFlags) - -#endif // SimpleCrypt_H diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 565651e..9eb9070 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -1,14 +1,14 @@ if (WITH_KIO) add_library(openFileManagerWindowJob SHARED openfilemanagerwindowjob.cpp) - target_link_libraries(openFileManagerWindowJob PRIVATE KF5::KIOWidgets) + target_link_libraries(openFileManagerWindowJob PRIVATE KF${QT_VERSION_MAJOR}::KIOWidgets) install(TARGETS openFileManagerWindowJob LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/squawk) endif () if (WITH_KCONFIG) add_library(colorSchemeTools SHARED colorschemetools.cpp) - target_link_libraries(colorSchemeTools PRIVATE KF5::ConfigCore) - target_link_libraries(colorSchemeTools PRIVATE KF5::ConfigWidgets) + target_link_libraries(colorSchemeTools PRIVATE KF${QT_VERSION_MAJOR}::ConfigCore) + target_link_libraries(colorSchemeTools PRIVATE KF${QT_VERSION_MAJOR}::ConfigWidgets) install(TARGETS colorSchemeTools LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/squawk) endif() diff --git a/plugins/colorschemetools.cpp b/plugins/colorschemetools.cpp index ea2c23e..76eba9b 100644 --- a/plugins/colorschemetools.cpp +++ b/plugins/colorschemetools.cpp @@ -20,9 +20,9 @@ #include #include -#include -#include -#include +#include +#include +#include QPixmap createPixmap(int size, const QBrush& window, const QBrush& button, const QBrush& view, const QBrush& selection); diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt index 2ef3970..45fcfd0 100644 --- a/shared/CMakeLists.txt +++ b/shared/CMakeLists.txt @@ -39,7 +39,4 @@ set(HEADER_FILES defines.h ) -target_sources(squawk PRIVATE - ${SOURCE_FILES} - ${HEADER_FILES} -) +target_sources(squawk PRIVATE ${SOURCE_FILES} ${HEADER_FILES}) diff --git a/shared/clientid.h b/shared/clientid.h index 5188b1c..05bed45 100644 --- a/shared/clientid.h +++ b/shared/clientid.h @@ -14,11 +14,11 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#ifndef SHARED_CLIENTID_H -#define SHARED_CLIENTID_H +#pragma once #include #include +#include namespace Shared { @@ -54,5 +54,3 @@ Q_DECLARE_METATYPE(Shared::ClientId) QDataStream& operator << (QDataStream& stream, const Shared::ClientId& info); QDataStream& operator >> (QDataStream& stream, Shared::ClientId& info); - -#endif // SHARED_CLIENTID_H diff --git a/shared/clientinfo.h b/shared/clientinfo.h index 288e9fa..ca66c6d 100644 --- a/shared/clientinfo.h +++ b/shared/clientinfo.h @@ -14,10 +14,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#ifndef SHARED_CLIENTINFO_H -#define SHARED_CLIENTINFO_H +#pragma once #include +#include #include #include @@ -54,5 +54,3 @@ private: QDataStream& operator << (QDataStream& stream, const Shared::ClientInfo& info); QDataStream& operator >> (QDataStream& stream, Shared::ClientInfo& info); - -#endif // SHARED_CLIENTINFO_H diff --git a/shared/enums.h b/shared/enums.h index 43d1583..6f5e9db 100644 --- a/shared/enums.h +++ b/shared/enums.h @@ -101,8 +101,8 @@ enum class Avatar { valid }; Q_ENUM_NS(Avatar) -static const Avatar AvatarHighest = Avatar::valid; -static const Avatar AvatarLowest = Avatar::empty; +static const Avatar AvatarHighest = Avatar::valid; +static const Avatar AvatarLowest = Avatar::empty; static const std::deque messageStateThemeIcons = {"state-offline", "state-sync", "state-ok", "state-error"}; diff --git a/shared/global.cpp b/shared/global.cpp index 362bf81..f0e3230 100644 --- a/shared/global.cpp +++ b/shared/global.cpp @@ -23,12 +23,6 @@ #include "enums.h" #include "ui/models/roster.h" -#ifdef WITH_SIMPLE_CRYPT -#define SIMPLE_CRYPT_ENABLED true -#else -#define SIMPLE_CRYPT_ENABLED false -#endif - #ifdef WITH_OMEMO constexpr bool OMEMO_SUPPORT = true; #else @@ -158,8 +152,7 @@ Shared::Global::Global(): optionalFeatures({ {"KWallet", false}, {"openFileManagerWindowJob", false}, - {"colorSchemeTools", false}, - {"simpleCryptJammedPassword", SIMPLE_CRYPT_ENABLED} + {"colorSchemeTools", false} }), fileCache() { @@ -324,7 +317,7 @@ void Shared::Global::highlightInFileManager(const QString& path) qDebug() << "requested to highlight in file manager url" << path << "but it's not supported: squawk wasn't compiled to support it, trying fallback"; #endif - QFileInfo info = path; + QFileInfo info(path); if (info.exists()) { QProcess proc; proc.start("xdg-mime", query); diff --git a/shared/messageinfo.cpp b/shared/messageinfo.cpp index 7502a6e..a26f23f 100644 --- a/shared/messageinfo.cpp +++ b/shared/messageinfo.cpp @@ -43,3 +43,19 @@ Shared::MessageInfo & Shared::MessageInfo::operator=(const Shared::MessageInfo& return *this; } + +QDataStream& operator >> (QDataStream& in, Shared::MessageInfo& info) { + in >> info.account; + in >> info.jid; + in >> info.messageId; + + return in; +} + +QDataStream& operator <<( QDataStream& out, const Shared::MessageInfo& info) { + out << info.account; + out << info.jid; + out << info.messageId; + + return out; +} diff --git a/shared/messageinfo.h b/shared/messageinfo.h index 3cf75bc..f06371b 100644 --- a/shared/messageinfo.h +++ b/shared/messageinfo.h @@ -19,6 +19,7 @@ #pragma once #include +#include namespace Shared { struct MessageInfo { @@ -34,3 +35,6 @@ struct MessageInfo { }; } + +QDataStream& operator << (QDataStream& out, const Shared::MessageInfo& info); +QDataStream& operator >> (QDataStream& in, Shared::MessageInfo& info); diff --git a/ui/utils/progress.cpp b/ui/utils/progress.cpp index 73e4d02..676376b 100644 --- a/ui/utils/progress.cpp +++ b/ui/utils/progress.cpp @@ -49,7 +49,7 @@ Progress::Progress(quint16 p_size, QWidget* parent): QGridLayout* layout = new QGridLayout(); setLayout(layout); - layout->setMargin(0); + layout->setContentsMargins(0, 0, 0, 0); layout->setVerticalSpacing(0); layout->setHorizontalSpacing(0); diff --git a/ui/widgets/accounts/account.cpp b/ui/widgets/accounts/account.cpp index 2d70603..1e2c4c7 100644 --- a/ui/widgets/accounts/account.cpp +++ b/ui/widgets/accounts/account.cpp @@ -38,11 +38,6 @@ Account::Account(): QStandardItem *item = model->item(static_cast(Shared::AccountPassword::kwallet)); item->setFlags(item->flags() & ~Qt::ItemIsEnabled); } - - if (!Shared::Global::supported("simpleCryptJammedPassword")) { - QStandardItem *item = model->item(static_cast(Shared::AccountPassword::jammed)); - item->setFlags(item->flags() & ~Qt::ItemIsEnabled); - } } Account::~Account() {} diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 7214a40..cedcf21 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -500,7 +500,7 @@ void Conversation::onFeedContext(const QPoint& pos) { } QString path = Shared::resolvePath(item->getAttachPath()); - if (path.size() > 0) { + if (!path.isEmpty()) { showMenu = true; QAction* open = contextMenu->addAction(Shared::icon("document-preview"), tr("Open")); connect(open, &QAction::triggered, [path]() { @@ -513,7 +513,7 @@ void Conversation::onFeedContext(const QPoint& pos) { }); } - bool hasAttach = item->getAttachPath() > 0 || item->getOutOfBandUrl() > 0; + bool hasAttach = !item->getAttachPath().isEmpty() || !item->getOutOfBandUrl().isEmpty(); //the only mandatory condition - is for the message to be outgoing, the rest is just a good intention on the server if (item->getOutgoing() && !hasAttach && index.row() < 100 && item->getTime().daysTo(QDateTime::currentDateTimeUtc()) < 20) { showMenu = true; diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index 41e4484..3c56d84 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -141,7 +141,15 @@ void FeedView::updateGeometries() { const QStyle* st = style(); QSize layoutBounds = maximumViewportSize(); - QStyleOptionViewItem option = viewOptions(); + + + QStyleOptionViewItem option; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + initViewItemOption(&option); +#else + option = viewOptions(); +#endif + option.rect.setHeight(maxMessageHeight); option.rect.setWidth(layoutBounds.width()); int frameAroundContents = 0; @@ -182,7 +190,7 @@ void FeedView::updateGeometries() { previousOffset += elementMargin; } lastDate = currentDate; - QSize messageSize = itemDelegate(index)->sizeHint(option, index); + QSize messageSize = itemDelegateForIndex(index)->sizeHint(option, index); uint32_t offsetX(0); if (specialDelegate) { if (index.data(Models::MessageFeed::SentByMe).toBool()) @@ -232,7 +240,7 @@ bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewIt previousOffset += elementMargin; } lastDate = currentDate; - QSize messageSize = itemDelegate(index)->sizeHint(option, index); + QSize messageSize = itemDelegateForIndex(index)->sizeHint(option, index); if (previousOffset + messageSize.height() + elementMargin > totalHeight) return false; @@ -288,8 +296,14 @@ void FeedView::paintEvent(QPaintEvent* event) { } } + QStyleOptionViewItem option; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + initViewItemOption(&option); +#else + option = viewOptions(); +#endif + QPainter painter(vp); - QStyleOptionViewItem option = viewOptions(); option.features = QStyleOptionViewItem::WrapText; QPoint cursor = vp->mapFromGlobal(QCursor::pos()); @@ -319,7 +333,7 @@ void FeedView::paintEvent(QPaintEvent* event) { stripe.setWidth(viewportRect.width()); bool mouseOver = stripe.contains(cursor) && viewportRect.contains(cursor); option.state.setFlag(QStyle::State_MouseOver, mouseOver); - itemDelegate(index)->paint(&painter, option, index); + itemDelegateForIndex(index)->paint(&painter, option, index); if (!lastDate.isNull() && currentDate.daysTo(lastDate) > 0) drawDateDevider(option.rect.bottom(), lastDate, painter); @@ -380,7 +394,7 @@ void FeedView::mouseMoveEvent(QMouseEvent* event) { if (!isVisible()) return; - dragEndPoint = event->localPos().toPoint(); + dragEndPoint = event->position().toPoint(); if (mousePressed) { QPoint distance = dragStartPoint - dragEndPoint; if (distance.manhattanLength() > 5) @@ -423,7 +437,7 @@ void FeedView::mousePressEvent(QMouseEvent* event) { mousePressed = event->button() == Qt::LeftButton; if (mousePressed) { - dragStartPoint = event->localPos().toPoint(); + dragStartPoint = event->position().toPoint(); if (specialDelegate && specialModel) { MessageDelegate* del = static_cast(itemDelegate()); QString lastSelectedId = del->clearSelection(); @@ -441,7 +455,7 @@ void FeedView::mouseDoubleClickEvent(QMouseEvent* event) { QAbstractItemView::mouseDoubleClickEvent(event); mousePressed = event->button() == Qt::LeftButton; if (mousePressed) { - dragStartPoint = event->localPos().toPoint(); + dragStartPoint = event->position().toPoint(); if (specialDelegate && specialModel) { MessageDelegate* del = static_cast(itemDelegate()); QString lastSelectedId = del->clearSelection(); @@ -469,7 +483,7 @@ void FeedView::mouseReleaseEvent(QMouseEvent* event) { if (mousePressed) { if (!dragging && specialDelegate) { - QPoint point = event->localPos().toPoint(); + QPoint point = event->position().toPoint(); QModelIndex index = indexAt(point); if (index.isValid()) { QRect rect = visualRect(index); diff --git a/ui/widgets/messageline/feedview.h b/ui/widgets/messageline/feedview.h index a39afc8..19645c0 100644 --- a/ui/widgets/messageline/feedview.h +++ b/ui/widgets/messageline/feedview.h @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -#ifndef FEEDVIEW_H -#define FEEDVIEW_H +#pragma once #include #include @@ -30,11 +29,7 @@ #include #include -/** - * @todo write docs - */ -class FeedView : public QAbstractItemView -{ +class FeedView : public QAbstractItemView { Q_OBJECT public: FeedView(QWidget* parent = nullptr); @@ -111,5 +106,3 @@ private: static const std::set geometryChangingRoles; }; - -#endif //FEEDVIEW_H diff --git a/ui/widgets/messageline/messagedelegate.cpp b/ui/widgets/messageline/messagedelegate.cpp index b89b438..b82a992 100644 --- a/ui/widgets/messageline/messagedelegate.cpp +++ b/ui/widgets/messageline/messagedelegate.cpp @@ -659,7 +659,7 @@ QLabel * MessageDelegate::getStatusIcon(const Models::FeedItem& data) const { QIcon q(Shared::icon(Shared::messageStateThemeIcons[static_cast(data.state)])); QString tt = Shared::Global::getName(data.state); if (data.state == Shared::Message::State::error) { - if (data.error > 0) + if (data.error.size() > 0) tt += ": " + data.error; } if (result->toolTip() != tt) { //If i just assign pixmap every time unconditionally diff --git a/ui/widgets/settings/settingslist.cpp b/ui/widgets/settings/settingslist.cpp index ee2e3ed..ae071ff 100644 --- a/ui/widgets/settings/settingslist.cpp +++ b/ui/widgets/settings/settingslist.cpp @@ -21,36 +21,39 @@ SettingsList::SettingsList(QWidget* parent): QListWidget(parent), lastWidth(0) -{ +{} +SettingsList::~SettingsList() {} + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +void SettingsList::initViewItemOption(QStyleOptionViewItem* option) const { + QListWidget::initViewItemOption(option); + if (!iconSize().isValid()) + option->decorationSize.setWidth(lastWidth); + + option->rect.setWidth(lastWidth); } - -SettingsList::~SettingsList() -{ -} - -QStyleOptionViewItem SettingsList::viewOptions() const -{ +#else +QStyleOptionViewItem SettingsList::viewOptions() const { QStyleOptionViewItem option = QListWidget::viewOptions(); - if (!iconSize().isValid()) { + if (!iconSize().isValid()) option.decorationSize.setWidth(lastWidth); - } + option.rect.setWidth(lastWidth); return option; } +#endif -void SettingsList::resizeEvent(QResizeEvent* event) -{ +void SettingsList::resizeEvent(QResizeEvent* event) { lastWidth = event->size().width(); QListWidget::resizeEvent(event); } -QRect SettingsList::visualRect(const QModelIndex& index) const -{ +QRect SettingsList::visualRect(const QModelIndex& index) const { QRect res = QListWidget::visualRect(index); - if (index.isValid()) { + if (index.isValid()) res.setWidth(lastWidth); - } + return res; } diff --git a/ui/widgets/settings/settingslist.h b/ui/widgets/settings/settingslist.h index 64c9d57..14ebf55 100644 --- a/ui/widgets/settings/settingslist.h +++ b/ui/widgets/settings/settingslist.h @@ -16,29 +16,28 @@ * along with this program. If not, see . */ -#ifndef UI_SETTINGSLIST_H -#define UI_SETTINGSLIST_H +#pragma once #include #include +#include -/** - * @todo write docs - */ -class SettingsList : public QListWidget -{ +class SettingsList : public QListWidget { Q_OBJECT public: SettingsList(QWidget* parent = nullptr); ~SettingsList(); protected: +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + void initViewItemOption(QStyleOptionViewItem* option) const override; +#else QStyleOptionViewItem viewOptions() const override; +#endif + void resizeEvent(QResizeEvent * event) override; QRect visualRect(const QModelIndex & index) const override; private: int lastWidth; }; - -#endif // UI_SETTINGSLIST_H From 321f0b03c8200fa166371f5df78d6641b10e7654 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 14 Dec 2024 18:08:50 +0200 Subject: [PATCH 135/137] Fix build for qt 5, removed some debug messages --- CHANGELOG.md | 8 ++++++ CMakeLists.txt | 2 +- README.md | 5 ++-- core/account.cpp | 4 +-- core/squawk.cpp | 2 -- packaging/Archlinux/PKGBUILD | 16 ++++++------ packaging/squawk.desktop | 2 +- ui/widgets/messageline/feedview.cpp | 39 +++++++++++++++++++++++++---- 8 files changed, 57 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ca1542..6599bb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Squawk 0.2.4 (UNRELEASED) +### Bug fixes +- messages to the mucs get sent once again + +### Improvements +- it's possible to build against Qt 6 now +- got rid of deprecated SimpleCrypt library + ## Squawk 0.2.3 (February 04, 2024) ### Bug fixes - "Add contact" and "Join conference" menu are enabled once again (pavavno)! diff --git a/CMakeLists.txt b/CMakeLists.txt index a62c800..4eb833a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.16) -project(squawk VERSION 0.2.3 LANGUAGES CXX) +project(squawk VERSION 0.2.4 LANGUAGES CXX) cmake_policy(SET CMP0076 NEW) cmake_policy(SET CMP0077 NEW) diff --git a/README.md b/README.md index 655304f..08bf735 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,8 @@ ### Prerequisites -- QT 5.12 *(lower versions might work but it wasn't tested)* -- CMake 3.4 or higher +- QT 5 or 6 +- CMake 3.10 or higher - qxmpp 1.1.0 or higher - LMDBAL (my own [library](https://git.macaw.me/blue/lmdbal) for lmdb) - KDE Frameworks: kwallet (optional) @@ -108,6 +108,7 @@ Here is the list of keys you can pass to configuration phase of `cmake ..`: - `WITH_KIO` - `True` builds the `KIO` capability module if `KIO` is installed and if not goes to `False`. `False` disables `KIO` support (default is `True`) - `WITH_KCONFIG` - `True` builds the `KConfig` and `KConfigWidgets` capability module if such packages are installed and if not goes to `False`. `False` disables `KConfig` and `KConfigWidgets` support (default is `True`) - `WITH_OMEMO` - `True` builds the OMEMO encryption, requires `qxmpp` of version >= 1.5.0 built with OMEMO support. `False` disables OMEMO support (default is `False`) +- `QT_VERSION_MAJOR` - `6` builds against Qt 6, `5` builds against Qt 6, corresponding version of lmdbal and qxmpp should be installed. By default it picks your system default Qt ## License diff --git a/core/account.cpp b/core/account.cpp index 6143fa6..e3d14e1 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -81,8 +81,8 @@ Core::Account::Account( config.setDomain(p_server); config.setPassword(p_password); config.setAutoAcceptSubscriptions(true); - config.setIgnoreSslErrors(true); - //config.setAutoReconnectionEnabled(false); + // config.setIgnoreSslErrors(true); + // config.setAutoReconnectionEnabled(false); delay = new DelayManager::Manager(getBareJid()); QObject::connect(delay, &DelayManager::Manager::gotInfo, this, &Account::infoReady); QObject::connect(delay, &DelayManager::Manager::gotOwnInfo, this, &Account::infoReady); diff --git a/core/squawk.cpp b/core/squawk.cpp index 1851eb3..7f04d9a 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -91,8 +91,6 @@ void Core::Squawk::stop() { break; } - qDebug() << "Saving password for" << acc->getName() << password; - settings.setValue("name", acc->getName()); settings.setValue("server", acc->getServer()); settings.setValue("login", acc->getLogin()); diff --git a/packaging/Archlinux/PKGBUILD b/packaging/Archlinux/PKGBUILD index 29ed800..86f2abf 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -1,19 +1,19 @@ # Maintainer: Yury Gubich pkgname=squawk -pkgver=0.2.3 +pkgver=0.2.4 pkgrel=1 pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)" arch=('i686' 'x86_64') url="https://git.macaw.me/blue/squawk" license=('GPL3') -depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdbal' 'qxmpp-qt5') -makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools' 'boost') -optdepends=('kwallet5: secure password storage (requires rebuild)' - 'kconfig5: system themes support (requires rebuild)' - 'kconfigwidgets5: system themes support (requires rebuild)' - 'kio5: better show in folder action (requires rebuild)') +depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdbal-qt6' 'qxmpp-qt6') +makedepends=('cmake>=3.3' 'imagemagick' 'qt6-tools' 'boost') +optdepends=('kwallet6: secure password storage (requires rebuild)' + 'kconfig6: system themes support (requires rebuild)' + 'kconfigwidgets6: system themes support (requires rebuild)' + 'kio6: better show in folder action (requires rebuild)') -source=("$pkgname-$pkgver.tar.gz::https://git.macaw.me/blue/$pkgname/archive/$pkgver.tar.gz") +source=("$pkgname-$pkgver-$pkgrel.tar.gz::https://git.macaw.me/blue/$pkgname/archive/$pkgver.tar.gz") sha256sums=('SKIP') build() { cd "$srcdir/squawk" diff --git a/packaging/squawk.desktop b/packaging/squawk.desktop index ba0f13c..c64f9ab 100644 --- a/packaging/squawk.desktop +++ b/packaging/squawk.desktop @@ -1,7 +1,7 @@ [Desktop Entry] Type=Application -Version=1.0 +Version=0.2.4 Name=Squawk GenericName=Instant Messenger GenericName[ru]=Мгновенные сообщения diff --git a/ui/widgets/messageline/feedview.cpp b/ui/widgets/messageline/feedview.cpp index 3c56d84..29ec9c6 100644 --- a/ui/widgets/messageline/feedview.cpp +++ b/ui/widgets/messageline/feedview.cpp @@ -190,7 +190,11 @@ void FeedView::updateGeometries() { previousOffset += elementMargin; } lastDate = currentDate; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) QSize messageSize = itemDelegateForIndex(index)->sizeHint(option, index); +#else + QSize messageSize = itemDelegate(index)->sizeHint(option, index); +#endif uint32_t offsetX(0); if (specialDelegate) { if (index.data(Models::MessageFeed::SentByMe).toBool()) @@ -240,7 +244,11 @@ bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewIt previousOffset += elementMargin; } lastDate = currentDate; - QSize messageSize = itemDelegateForIndex(index)->sizeHint(option, index); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QSize messageSize = itemDelegateForIndex(index)->sizeHint(option, index); +#else + QSize messageSize = itemDelegate(index)->sizeHint(option, index); +#endif if (previousOffset + messageSize.height() + elementMargin > totalHeight) return false; @@ -333,7 +341,12 @@ void FeedView::paintEvent(QPaintEvent* event) { stripe.setWidth(viewportRect.width()); bool mouseOver = stripe.contains(cursor) && viewportRect.contains(cursor); option.state.setFlag(QStyle::State_MouseOver, mouseOver); - itemDelegateForIndex(index)->paint(&painter, option, index); + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + itemDelegateForIndex(index)->paint(&painter, option, index); +#else + itemDelegate(index)->paint(&painter, option, index); +#endif if (!lastDate.isNull() && currentDate.daysTo(lastDate) > 0) drawDateDevider(option.rect.bottom(), lastDate, painter); @@ -393,8 +406,12 @@ void FeedView::setAnchorHovered(Shared::Hover type) { void FeedView::mouseMoveEvent(QMouseEvent* event) { if (!isVisible()) return; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + dragEndPoint = event->position().toPoint(); +#else + dragEndPoint = event->localPos().toPoint(); +#endif - dragEndPoint = event->position().toPoint(); if (mousePressed) { QPoint distance = dragStartPoint - dragEndPoint; if (distance.manhattanLength() > 5) @@ -437,7 +454,11 @@ void FeedView::mousePressEvent(QMouseEvent* event) { mousePressed = event->button() == Qt::LeftButton; if (mousePressed) { - dragStartPoint = event->position().toPoint(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + dragStartPoint = event->position().toPoint(); +#else + dragStartPoint = event->localPos().toPoint(); +#endif if (specialDelegate && specialModel) { MessageDelegate* del = static_cast(itemDelegate()); QString lastSelectedId = del->clearSelection(); @@ -455,7 +476,11 @@ void FeedView::mouseDoubleClickEvent(QMouseEvent* event) { QAbstractItemView::mouseDoubleClickEvent(event); mousePressed = event->button() == Qt::LeftButton; if (mousePressed) { - dragStartPoint = event->position().toPoint(); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + dragStartPoint = event->position().toPoint(); +#else + dragStartPoint = event->localPos().toPoint(); +#endif if (specialDelegate && specialModel) { MessageDelegate* del = static_cast(itemDelegate()); QString lastSelectedId = del->clearSelection(); @@ -483,7 +508,11 @@ void FeedView::mouseReleaseEvent(QMouseEvent* event) { if (mousePressed) { if (!dragging && specialDelegate) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) QPoint point = event->position().toPoint(); +#else + QPoint point = event->localPos().toPoint(); +#endif QModelIndex index = indexAt(point); if (index.isValid()) { QRect rect = visualRect(index); From a8060b393ca606e0683f4762c9f0f163bb966430 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 9 Feb 2025 19:51:30 +0200 Subject: [PATCH 136/137] Updated LMDBAL --- external/lmdbal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/lmdbal b/external/lmdbal index d62eddc..3ae1fd1 160000 --- a/external/lmdbal +++ b/external/lmdbal @@ -1 +1 @@ -Subproject commit d62eddc47edbec9f8c071459e045578f61ab58df +Subproject commit 3ae1fd15c0f4f753227d6fd5bafa4968c7310b92 From 066ab487fcc7cb07b65548bc1785412dccd24708 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 20 Feb 2025 21:37:38 +0200 Subject: [PATCH 137/137] Defaulted to qt6, fix some deprecations --- CHANGELOG.md | 3 +- CMakeLists.txt | 15 +++--- README.md | 2 +- core/handlers/messagehandler.cpp | 49 +++++++++++++------- core/handlers/messagehandler.h | 1 + packaging/Archlinux/PKGBUILD | 2 +- ui/utils/flowlayout.cpp | 78 ++++++++++++-------------------- 7 files changed, 76 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6599bb5..44c00ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,9 @@ - messages to the mucs get sent once again ### Improvements -- it's possible to build against Qt 6 now +- it's possible to build against Qt 6 now, Qt6 is the default - got rid of deprecated SimpleCrypt library +- look up for proper stanzaID ## Squawk 0.2.3 (February 04, 2024) ### Bug fixes diff --git a/CMakeLists.txt b/CMakeLists.txt index 4eb833a..7d764ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,15 +36,18 @@ option(WITH_KCONFIG "Build KConfig support module" ON) option(WITH_OMEMO "Build OMEMO support module" OFF) #it should be off by default untill I sort the problems out # Dependencies -## Qt -if (NOT DEFINED QT_VERSION_MAJOR) - find_package(QT NAMES Qt6 Qt5 CONFIG REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) -else () - find_package(Qt${QT_VERSION_MAJOR} CONFIG REQUIRED COMPONENTS Widgets DBus Gui Xml Network Core) +## Qt, detect and prefer Qt6 if available +find_package(Qt6 QUIET CONFIG COMPONENTS Widgets DBus Gui Xml Network Core) +if (Qt6_FOUND) + set(QT_VERSION_MAJOR 6) +else() + find_package(Qt5 REQUIRED CONFIG COMPONENTS Widgets DBus Gui Xml Network Core) + set(QT_VERSION_MAJOR 5) endif() -find_package(Boost COMPONENTS) +message(STATUS "Building against Qt${QT_VERSION_MAJOR}") +find_package(Boost COMPONENTS) target_include_directories(squawk PRIVATE ${Boost_INCLUDE_DIRS}) ## OMEMO diff --git a/README.md b/README.md index 08bf735..b65e5e9 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ Here is the list of keys you can pass to configuration phase of `cmake ..`: - `WITH_KIO` - `True` builds the `KIO` capability module if `KIO` is installed and if not goes to `False`. `False` disables `KIO` support (default is `True`) - `WITH_KCONFIG` - `True` builds the `KConfig` and `KConfigWidgets` capability module if such packages are installed and if not goes to `False`. `False` disables `KConfig` and `KConfigWidgets` support (default is `True`) - `WITH_OMEMO` - `True` builds the OMEMO encryption, requires `qxmpp` of version >= 1.5.0 built with OMEMO support. `False` disables OMEMO support (default is `False`) -- `QT_VERSION_MAJOR` - `6` builds against Qt 6, `5` builds against Qt 6, corresponding version of lmdbal and qxmpp should be installed. By default it picks your system default Qt +- `QT_VERSION_MAJOR` - `6` builds against Qt 6, `5` builds against Qt 6, corresponding version of lmdbal and qxmpp should be installed (default is `6`) ## License diff --git a/core/handlers/messagehandler.cpp b/core/handlers/messagehandler.cpp index b9ae3b6..34f7fd1 100644 --- a/core/handlers/messagehandler.cpp +++ b/core/handlers/messagehandler.cpp @@ -164,19 +164,7 @@ bool Core::MessageHandler::handleGroupMessage(const QXmppMessage& msg, bool outg } void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmppMessage& source, bool outgoing, bool forwarded, bool guessing) const { - const QDateTime& time(source.stamp()); - QString id; -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) - id = source.originId(); - if (id.size() == 0) - id = source.id(); - - target.setStanzaId(source.stanzaId()); - qDebug() << "initializing message with originId:" << source.originId() << ", id:" << source.id() << ", stansaId:" << source.stanzaId(); -#else - id = source.id(); -#endif - target.setId(id); + initializeIDs(target, source); QString messageId = target.getId(); if (messageId.size() == 0) { target.generateRandomId(); //TODO out of desperation, I need at least a random ID @@ -197,6 +185,7 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp if (guessing) outgoing = target.getFromJid() == acc->getBareJid(); + const QDateTime& time(source.stamp()); target.setOutgoing(outgoing); if (time.isValid()) target.setTime(time); @@ -210,6 +199,37 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp target.setOutOfBandUrl(oob); } +void Core::MessageHandler::initializeIDs(Shared::Message& target, const QXmppMessage& source) const { + QString id; +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) + id = source.originId(); + if (id.size() == 0) + id = source.id(); + + QString stanzaID; +#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 5, 0) + // here I'm looking preferably for id generated by myself, but if there isn't - any is better than nothing + QVector sIDs = source.stanzaIds(); + for (const QXmppStanzaId& sID : sIDs) { + bool match = sID.by == acc->getBareJid(); + if (stanzaID.isEmpty() || match) + stanzaID = sID.id; + + if (match) + break; + } +#else + stanzaID = source.stanzaId(); +#endif + target.setStanzaId(stanzaID); + qDebug() << "initializing message with originId:" << source.originId() << ", id:" << source.id() << ", stanzaId:" << stanzaID; +#else + id = source.id(); +#endif + target.setId(id); +} + + void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& reason) { qDebug() << reason; qDebug() << "- from: " << msg.from(); @@ -219,9 +239,6 @@ void Core::MessageHandler::logMessage(const QXmppMessage& msg, const QString& re qDebug() << "- state: " << msg.state(); qDebug() << "- stamp: " << msg.stamp(); qDebug() << "- id: " << msg.id(); -#if (QXMPP_VERSION) >= QT_VERSION_CHECK(1, 3, 0) - qDebug() << "- stanzaId: " << msg.stanzaId(); -#endif qDebug() << "- outOfBandUrl: " << msg.outOfBandUrl(); qDebug() << "=============================="; } diff --git a/core/handlers/messagehandler.h b/core/handlers/messagehandler.h index 917b98d..3555548 100644 --- a/core/handlers/messagehandler.h +++ b/core/handlers/messagehandler.h @@ -77,6 +77,7 @@ private: bool handlePendingMessageError(const QString& id, const QString& errorText); std::pair scheduleSending(const Shared::Message& message, const QDateTime& sendTime, const QString& originalId); bool adjustPendingMessage(const QString& messageId, const QMap& data, bool final); + void initializeIDs(Shared::Message& target, const QXmppMessage& source) const; private: Account* acc; diff --git a/packaging/Archlinux/PKGBUILD b/packaging/Archlinux/PKGBUILD index 86f2abf..0defdc3 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -6,7 +6,7 @@ pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)" arch=('i686' 'x86_64') url="https://git.macaw.me/blue/squawk" license=('GPL3') -depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdbal-qt6' 'qxmpp-qt6') +depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdbal-qt6' 'qxmpp') makedepends=('cmake>=3.3' 'imagemagick' 'qt6-tools' 'boost') optdepends=('kwallet6: secure password storage (requires rebuild)' 'kconfig6: system themes support (requires rebuild)' diff --git a/ui/utils/flowlayout.cpp b/ui/utils/flowlayout.cpp index ad7715e..34f978a 100644 --- a/ui/utils/flowlayout.cpp +++ b/ui/utils/flowlayout.cpp @@ -33,96 +33,78 @@ FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing): setContentsMargins(margin, margin, margin, margin); } -FlowLayout::~FlowLayout() -{ +FlowLayout::~FlowLayout() { QLayoutItem *item; - while ((item = takeAt(0))) { + while ((item = takeAt(0))) delete item; - } } -void FlowLayout::addItem(QLayoutItem *item) -{ +void FlowLayout::addItem(QLayoutItem *item) { itemList.append(item); } -int FlowLayout::horizontalSpacing() const -{ - if (m_hSpace >= 0) { +int FlowLayout::horizontalSpacing() const { + if (m_hSpace >= 0) return m_hSpace; - } else { + else return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); - } } -int FlowLayout::verticalSpacing() const -{ - if (m_vSpace >= 0) { +int FlowLayout::verticalSpacing() const { + if (m_vSpace >= 0) return m_vSpace; - } else { + else return smartSpacing(QStyle::PM_LayoutVerticalSpacing); - } } -int FlowLayout::count() const -{ +int FlowLayout::count() const { return itemList.size(); } -QLayoutItem *FlowLayout::itemAt(int index) const -{ +QLayoutItem *FlowLayout::itemAt(int index) const { return itemList.value(index); } -QLayoutItem *FlowLayout::takeAt(int index) -{ - if (index >= 0 && index < itemList.size()) { +QLayoutItem *FlowLayout::takeAt(int index) { + if (index >= 0 && index < itemList.size()) return itemList.takeAt(index); - } + return nullptr; } -Qt::Orientations FlowLayout::expandingDirections() const -{ +Qt::Orientations FlowLayout::expandingDirections() const { return Qt::Orientations(); } -bool FlowLayout::hasHeightForWidth() const -{ +bool FlowLayout::hasHeightForWidth() const { return true; } -int FlowLayout::heightForWidth(int width) const -{ +int FlowLayout::heightForWidth(int width) const { int height = doLayout(QRect(0, 0, width, 0), true); return height; } -void FlowLayout::setGeometry(const QRect &rect) -{ +void FlowLayout::setGeometry(const QRect &rect) { QLayout::setGeometry(rect); doLayout(rect, false); } -QSize FlowLayout::sizeHint() const -{ +QSize FlowLayout::sizeHint() const { return minimumSize(); } -QSize FlowLayout::minimumSize() const -{ +QSize FlowLayout::minimumSize() const { QSize size; - for (const QLayoutItem *item : qAsConst(itemList)) { + for (const QLayoutItem *item : std::as_const(itemList)) size = size.expandedTo(item->minimumSize()); - } const QMargins margins = contentsMargins(); size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom()); return size; } -int FlowLayout::doLayout(const QRect &rect, bool testOnly) const -{ +int FlowLayout::doLayout(const QRect &rect, bool testOnly) const { int left, top, right, bottom; getContentsMargins(&left, &top, &right, &bottom); QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); @@ -130,16 +112,16 @@ int FlowLayout::doLayout(const QRect &rect, bool testOnly) const int y = effectiveRect.y(); int lineHeight = 0; - for (QLayoutItem *item : qAsConst(itemList)) { + for (QLayoutItem *item : std::as_const(itemList)) { const QWidget *wid = item->widget(); int spaceX = horizontalSpacing(); - if (spaceX == -1) { + if (spaceX == -1) spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); - } + int spaceY = verticalSpacing(); - if (spaceY == -1) { + if (spaceY == -1) spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); - } + int nextX = x + item->sizeHint().width() + spaceX; if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) { x = effectiveRect.x(); @@ -148,9 +130,8 @@ int FlowLayout::doLayout(const QRect &rect, bool testOnly) const lineHeight = 0; } - if (!testOnly) { + if (!testOnly) item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); - } x = nextX; lineHeight = qMax(lineHeight, item->sizeHint().height()); @@ -158,8 +139,7 @@ int FlowLayout::doLayout(const QRect &rect, bool testOnly) const return y + lineHeight - rect.y() + bottom; } -int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const -{ +int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const { QObject *parent = this->parent(); if (!parent) { return -1;