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