From a77dfd191ab5dcf47169b3a6eee33e83afad0f38 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 11 Apr 2020 23:00:15 +0300 Subject: [PATCH] single window mode --- CHANGELOG.md | 1 + README.md | 3 - core/passwordStorageEngines/CMakeLists.txt | 5 - ui/models/roster.cpp | 10 + ui/models/roster.h | 2 + ui/squawk.cpp | 212 ++++++++++++++++++--- ui/squawk.h | 3 + ui/squawk.ui | 148 ++++++++++---- ui/utils/messageline.cpp | 8 +- ui/utils/messageline.h | 1 + ui/widgets/conversation.cpp | 14 ++ ui/widgets/conversation.h | 4 + ui/widgets/conversation.ui | 8 +- 13 files changed, 345 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11177e8..2fb98c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Squawk 0.1.4 (UNRELEASED) ### New features +- message line now is in the same window with roster (new window dialog is still able to opened on double click) - several ways to manage your account password: - store it in plain text with the config (like it always was) - store it in config jammed (local hashing with the constant seed, not secure at all but might look like it is) diff --git a/README.md b/README.md index c820ccd..7d55eb5 100644 --- a/README.md +++ b/README.md @@ -67,9 +67,6 @@ 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`) - -Each key is supposed to be passed like that - ## License This project is licensed under the GPLv3 License - see the [LICENSE.md](LICENSE.md) file for details diff --git a/core/passwordStorageEngines/CMakeLists.txt b/core/passwordStorageEngines/CMakeLists.txt index 36f67b1..e824f77 100644 --- a/core/passwordStorageEngines/CMakeLists.txt +++ b/core/passwordStorageEngines/CMakeLists.txt @@ -1,7 +1,6 @@ cmake_minimum_required(VERSION 3.0) project(pse) - if (WITH_KWALLET) set(CMAKE_AUTOMOC ON) @@ -36,7 +35,3 @@ if (WITH_KWALLET) install(TARGETS kwalletWrapper DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() - - - - diff --git a/ui/models/roster.cpp b/ui/models/roster.cpp index 609715f..42ea9f8 100644 --- a/ui/models/roster.cpp +++ b/ui/models/roster.cpp @@ -386,6 +386,16 @@ 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 +{ + return (account == other.account) && (name == other.name); +} + void Models::Roster::onAccountDataChanged(const QModelIndex& tl, const QModelIndex& br, const QVector& roles) { if (tl.column() == 0) { diff --git a/ui/models/roster.h b/ui/models/roster.h index 50c6532..34c343c 100644 --- a/ui/models/roster.h +++ b/ui/models/roster.h @@ -109,6 +109,8 @@ public: const QString name; bool operator < (const ElId& other) const; + bool operator == (const ElId& other) const; + bool operator != (const ElId& other) const; }; }; diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 3a11c1a..120123a 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -32,7 +32,8 @@ Squawk::Squawk(QWidget *parent) : requestedFiles(), vCards(), requestedAccountsForPasswords(), - prompt(0) + prompt(0), + currentConversation(0) { m_ui->setupUi(this); m_ui->roster->setModel(&rosterModel); @@ -55,6 +56,7 @@ Squawk::Squawk(QWidget *parent) : 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->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &Squawk::onRosterSelectionChanged); connect(rosterModel.accountsModel, &Models::Accounts::sizeChanged, this, &Squawk::onAccountsSizeChanged); //m_ui->mainToolBar->addWidget(m_ui->comboBox); @@ -74,6 +76,10 @@ Squawk::Squawk(QWidget *parent) : restoreState(settings.value("state").toByteArray()); } settings.endGroup(); + + if (settings.contains("splitter")) { + m_ui->splitter->restoreState(settings.value("splitter").toByteArray()); + } settings.endGroup(); } @@ -344,16 +350,7 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) if (conv != 0) { if (created) { conv->setAttribute(Qt::WA_DeleteOnClose); - - connect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed); - connect(conv, qOverload(&Conversation::sendMessage), this, qOverload(&Squawk::onConversationMessage)); - connect(conv, qOverload(&Conversation::sendMessage), - this, qOverload(&Squawk::onConversationMessage)); - connect(conv, &Conversation::requestArchive, this, &Squawk::onConversationRequestArchive); - connect(conv, &Conversation::requestLocalFile, this, &Squawk::onConversationRequestLocalFile); - connect(conv, &Conversation::downloadFile, this, &Squawk::onConversationDownloadFile); - connect(conv, &Conversation::shown, this, &Squawk::onConversationShown); - + subscribeConversation(conv); conversations.insert(std::make_pair(*id, conv)); if (created) { @@ -372,6 +369,7 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) } } + delete id; } } } @@ -387,9 +385,8 @@ void Squawk::onConversationClosed(QObject* parent) Conversation* conv = static_cast(sender()); Models::Roster::ElId id(conv->getAccount(), conv->getJid()); Conversations::const_iterator itr = conversations.find(id); - if (itr == conversations.end()) { - qDebug() << "Conversation has been closed but can not be found among other opened conversations, application is most probably going to crash"; - return; + if (itr != conversations.end()) { + conversations.erase(itr); } if (conv->isMuc) { Room* room = static_cast(conv); @@ -397,7 +394,6 @@ void Squawk::onConversationClosed(QObject* parent) emit setRoomJoined(id.account, id.name, false); } } - conversations.erase(itr); } void Squawk::onConversationDownloadFile(const QString& messageId, const QString& url) @@ -429,6 +425,9 @@ void Squawk::fileProgress(const QString& messageId, qreal value) if (c != conversations.end()) { c->second->responseFileProgress(messageId, value); } + if (currentConversation != 0 && currentConversation->getId() == id) { + currentConversation->responseFileProgress(messageId, value); + } } } } @@ -447,6 +446,9 @@ void Squawk::fileError(const QString& messageId, const QString& error) if (c != conversations.end()) { c->second->fileError(messageId, error); } + if (currentConversation != 0 && currentConversation->getId() == id) { + currentConversation->fileError(messageId, error); + } } requestedFiles.erase(itr); } @@ -466,6 +468,9 @@ void Squawk::fileLocalPathResponse(const QString& messageId, const QString& path if (c != conversations.end()) { c->second->responseLocalFile(messageId, path); } + if (currentConversation != 0 && currentConversation->getId() == id) { + currentConversation->responseLocalFile(messageId, path); + } } requestedFiles.erase(itr); @@ -490,18 +495,33 @@ void Squawk::onConversationRequestLocalFile(const QString& messageId, const QStr void Squawk::accountMessage(const QString& account, const Shared::Message& data) { const QString& from = data.getPenPalJid(); - Conversations::iterator itr = conversations.find({account, from}); + Models::Roster::ElId id({account, from}); + Conversations::iterator itr = conversations.find(id); + bool found = false; + + if (currentConversation != 0 && currentConversation->getId() == id) { + currentConversation->addMessage(data); + QApplication::alert(this); + if (!isVisible() && !data.getForwarded()) { + notify(account, data); + } + found = true; + } + if (itr != conversations.end()) { Conversation* conv = itr->second; conv->addMessage(data); QApplication::alert(conv); - if (conv->isMinimized()) { + if (!found && conv->isMinimized()) { rosterModel.addMessage(account, data); } if (!conv->isVisible() && !data.getForwarded()) { notify(account, data); } - } else { + found = true; + } + + if (!found) { rosterModel.addMessage(account, data); if (!data.getForwarded()) { QApplication::alert(this); @@ -512,14 +532,26 @@ void Squawk::accountMessage(const QString& account, const Shared::Message& data) void Squawk::changeMessage(const QString& account, const QString& jid, const QString& id, const QMap& data) { - Conversations::iterator itr = conversations.find({account, jid}); + Models::Roster::ElId eid({account, jid}); + bool found = false; + + if (currentConversation != 0 && currentConversation->getId() == eid) { + currentConversation->changeMessage(id, data); + QApplication::alert(this); + found = true; + } + + Conversations::iterator itr = conversations.find(eid); if (itr != conversations.end()) { Conversation* conv = itr->second; conv->changeMessage(id, data); - if (conv->isMinimized()) { + if (!found && conv->isMinimized()) { rosterModel.changeMessage(account, jid, id, data); } - } else { + found = true; + } + + if (!found) { rosterModel.changeMessage(account, jid, id, data); } } @@ -559,13 +591,37 @@ void Squawk::onConversationMessage(const Shared::Message& msg) { Conversation* conv = static_cast(sender()); emit sendMessage(conv->getAccount(), msg); + Models::Roster::ElId id = conv->getId(); + + if (currentConversation != 0 && currentConversation->getId() == id) { + if (conv == currentConversation) { + Conversations::iterator itr = conversations.find(id); + if (itr != conversations.end()) { + itr->second->addMessage(msg); + } + } else { + currentConversation->addMessage(msg); + } + } } void Squawk::onConversationMessage(const Shared::Message& msg, const QString& path) { Conversation* conv = static_cast(sender()); + Models::Roster::ElId id = conv->getId(); std::map>::iterator itr = requestedFiles.insert(std::make_pair(msg.getId(), std::set())).first; - itr->second.insert(Models::Roster::ElId(conv->getAccount(), conv->getJid())); + itr->second.insert(id); + + if (currentConversation != 0 && currentConversation->getId() == id) { + if (conv == currentConversation) { + Conversations::iterator itr = conversations.find(id); + if (itr != conversations.end()) { + itr->second->appendMessageWithUpload(msg, path); + } + } else { + currentConversation->appendMessageWithUpload(msg, path); + } + } emit sendMessage(conv->getAccount(), msg, path); } @@ -580,6 +636,10 @@ void Squawk::responseArchive(const QString& account, const QString& jid, const s { Models::Roster::ElId id(account, jid); + if (currentConversation != 0 && currentConversation->getId() == id) { + currentConversation->responseArchive(list); + } + Conversations::const_iterator itr = conversations.find(id); if (itr != conversations.end()) { itr->second->responseArchive(list); @@ -603,6 +663,13 @@ void Squawk::removeAccount(const QString& account) ++itr; } } + + if (currentConversation != 0 && currentConversation->getAccount() == account) { + currentConversation->deleteLater(); + currentConversation = 0; + m_ui->filler->show(); + } + rosterModel.removeAccount(account); } @@ -761,7 +828,9 @@ void Squawk::onRosterContextMenu(const QPoint& point) unsub->setEnabled(active); connect(unsub, &QAction::triggered, [this, id]() { emit setRoomAutoJoin(id.account, id.name, false); - if (conversations.find(id) == conversations.end()) { //to leave the room if it's not opened in a conversation window + if (conversations.find(id) == conversations.end() + && (currentConversation == 0 || currentConversation->getId() != id) + ) { //to leave the room if it's not opened in a conversation window emit setRoomJoined(id.account, id.name, false); } }); @@ -770,7 +839,9 @@ void Squawk::onRosterContextMenu(const QPoint& point) unsub->setEnabled(active); connect(unsub, &QAction::triggered, [this, id]() { emit setRoomAutoJoin(id.account, id.name, true); - if (conversations.find(id) == conversations.end()) { //to join the room if it's not already joined + if (conversations.find(id) == conversations.end() + && (currentConversation == 0 || currentConversation->getId() != id) + ) { //to join the room if it's not already joined emit setRoomJoined(id.account, id.name, true); } }); @@ -897,7 +968,6 @@ void Squawk::readSettings() } // need to fix that settings.endArray(); } - settings.endGroup(); } @@ -910,6 +980,8 @@ void Squawk::writeSettings() settings.setValue("state", saveState()); settings.endGroup(); + settings.setValue("splitter", m_ui->splitter->saveState()); + settings.setValue("availability", m_ui->comboBox->currentIndex()); settings.beginWriteArray("connectedAccounts"); int size = rosterModel.accountsModel->rowCount(QModelIndex()); @@ -1008,3 +1080,93 @@ void Squawk::onPasswordPromptRejected() emit responsePassword(requestedAccountsForPasswords.front(), prompt->textValue()); onPasswordPromptDone(); } + +void Squawk::subscribeConversation(Conversation* conv) +{ + connect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed); + connect(conv, qOverload(&Conversation::sendMessage), this, qOverload(&Squawk::onConversationMessage)); + connect(conv, qOverload(&Conversation::sendMessage), + this, qOverload(&Squawk::onConversationMessage)); + connect(conv, &Conversation::requestArchive, this, &Squawk::onConversationRequestArchive); + connect(conv, &Conversation::requestLocalFile, this, &Squawk::onConversationRequestLocalFile); + connect(conv, &Conversation::downloadFile, this, &Squawk::onConversationDownloadFile); + connect(conv, &Conversation::shown, this, &Squawk::onConversationShown); +} + +void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous) +{ + if (current.isValid()) { + Models::Item* node = static_cast(current.internalPointer()); + Models::Contact* contact = 0; + Models::Room* room = 0; + QString res; + Models::Roster::ElId* id = 0; + switch (node->type) { + case Models::Item::contact: + contact = static_cast(node); + id = new 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(); + break; + case Models::Item::room: + room = static_cast(node); + id = new Models::Roster::ElId(room->getAccountName(), room->getJid()); + break; + default: + break; + } + + if (id != 0) { + if (currentConversation != 0) { + if (currentConversation->getJid() == id->name) { + if (contact != 0) { + currentConversation->setPalResource(res); + } + } else { + currentConversation->deleteLater(); + } + } else { + m_ui->filler->hide(); + } + + Models::Account* acc = rosterModel.getAccount(id->account); + Models::Contact::Messages deque; + if (contact != 0) { + currentConversation = new Chat(acc, contact); + contact->getMessages(deque); + } else if (room != 0) { + currentConversation = new Room(acc, room); + room->getMessages(deque); + + if (!room->getJoined()) { + emit setRoomJoined(id->account, id->name, true); + } + } + if (!testAttribute(Qt::WA_TranslucentBackground)) { + currentConversation->setFeedFrames(true, false, true, true); + } + + subscribeConversation(currentConversation); + for (Models::Contact::Messages::const_iterator itr = deque.begin(), end = deque.end(); itr != end; ++itr) { + currentConversation->addMessage(*itr); + } + + if (res.size() > 0) { + currentConversation->setPalResource(res); + } + + m_ui->splitter->insertWidget(1, currentConversation); + + delete id; + } else { + if (currentConversation != 0) { + currentConversation->deleteLater(); + currentConversation = 0; + m_ui->filler->show(); + } + } + } +} diff --git a/ui/squawk.h b/ui/squawk.h index d5bde9c..5b3d7cd 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -124,6 +124,7 @@ private: std::map vCards; std::deque requestedAccountsForPasswords; QInputDialog* prompt; + Conversation* currentConversation; protected: void closeEvent(QCloseEvent * event) override; @@ -153,10 +154,12 @@ private slots: void onItemCollepsed(const QModelIndex& index); void onPasswordPromptAccepted(); void onPasswordPromptRejected(); + void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous); private: void checkNextAccountForPassword(); void onPasswordPromptDone(); + void subscribeConversation(Conversation* conv); }; #endif // SQUAWK_H diff --git a/ui/squawk.ui b/ui/squawk.ui index e09cd19..6c50024 100644 --- a/ui/squawk.ui +++ b/ui/squawk.ui @@ -6,8 +6,8 @@ 0 0 - 385 - 508 + 718 + 720 @@ -17,7 +17,7 @@ Qt::ToolButtonFollowStyle - + 0 @@ -30,42 +30,112 @@ 0 - - - + + + + Qt::Horizontal + + + 1 + + false - - - - - -1 - - - - - - - QFrame::NoFrame - - - QFrame::Sunken - - - true - - - true - - - true - - - false - - - false - + + + + 0 + 0 + + + + + 200 + 0 + + + + + 400 + 16777215 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Sunken + + + true + + + true + + + true + + + false + + + false + + + + + + + false + + + + + + -1 + + + + + + + + + 2 + 0 + + + + + + + <html><head/><body><p><span style=" font-size:26pt;">Please select a contact to start chatting</span></p></body></html> + + + Qt::AlignCenter + + + true + + + + + @@ -75,8 +145,8 @@ 0 0 - 385 - 22 + 718 + 27 diff --git a/ui/utils/messageline.cpp b/ui/utils/messageline.cpp index deb9a45..ecd10a1 100644 --- a/ui/utils/messageline.cpp +++ b/ui/utils/messageline.cpp @@ -427,6 +427,12 @@ void MessageLine::fileError(const QString& messageId, const QString& error) } 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(); @@ -436,9 +442,9 @@ void MessageLine::appendMessageWithUpload(const Shared::Message& msg, const QStr ui->showComment(tr("Uploading...")); uploading.insert(std::make_pair(id, ui)); uploadPaths.insert(std::make_pair(id, path)); - emit uploadFile(msg, path); } + void MessageLine::onUpload() { //TODO retry diff --git a/ui/utils/messageline.h b/ui/utils/messageline.h index 277d429..104dc72 100644 --- a/ui/utils/messageline.h +++ b/ui/utils/messageline.h @@ -53,6 +53,7 @@ public: 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); diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 13cb881..d413506 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -210,6 +210,11 @@ void Conversation::onEnterPressed() } } +void Conversation::appendMessageWithUpload(const Shared::Message& data, const QString& path) +{ + line->appendMessageWithUploadNoSiganl(data, path); +} + void Conversation::onMessagesResize(int amount) { manualSliderChange = true; @@ -334,6 +339,11 @@ void Conversation::responseLocalFile(const QString& messageId, const QString& pa line->responseLocalFile(messageId, path); } +Models::Roster::ElId Conversation::getId() const +{ + return {getAccount(), getJid()}; +} + void Conversation::addAttachedFile(const QString& path) { QMimeDatabase db; @@ -397,6 +407,10 @@ void Conversation::onTextEditDocSizeChanged(const QSizeF& size) m_ui->messageEditor->setMaximumHeight(int(size.height())); } +void Conversation::setFeedFrames(bool top, bool right, bool bottom, bool left) +{ + static_cast(m_ui->scrollArea->graphicsEffect())->setFrame(top, right, bottom, left); +} bool VisibilityCatcher::eventFilter(QObject* obj, QEvent* event) { diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index 6ce8cad..075bc31 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -26,6 +26,7 @@ #include "shared/message.h" #include "order.h" #include "ui/models/account.h" +#include "ui/models/roster.h" #include "ui/utils/messageline.h" #include "ui/utils/resizer.h" #include "ui/utils/flowlayout.h" @@ -72,6 +73,7 @@ public: QString getJid() const; QString getAccount() const; QString getPalResource() const; + Models::Roster::ElId getId() const; virtual void addMessage(const Shared::Message& data); void setPalResource(const QString& res); @@ -82,6 +84,8 @@ public: void responseFileProgress(const QString& messageId, qreal progress); virtual void setAvatar(const QString& path); void changeMessage(const QString& id, const QMap& data); + void setFeedFrames(bool top, bool right, bool bottom, bool left); + virtual void appendMessageWithUpload(const Shared::Message& data, const QString& path); signals: void sendMessage(const Shared::Message& message); diff --git a/ui/widgets/conversation.ui b/ui/widgets/conversation.ui index 4ff8b34..9b1321e 100644 --- a/ui/widgets/conversation.ui +++ b/ui/widgets/conversation.ui @@ -10,6 +10,12 @@ 658 + + + 2 + 0 + + 0 @@ -206,7 +212,7 @@ 0 0 520 - 389 + 392