From cf23838099abcc3b5133ec3dc5dda021a4d2df69 Mon Sep 17 00:00:00 2001 From: fourtf Date: Sun, 8 Sep 2019 18:06:43 +0200 Subject: [PATCH 01/41] added irc support --- chatterino.pro | 9 +- src/Application.cpp | 2 + src/providers/irc/Irc2.cpp | 73 +++++++++ src/providers/irc/Irc2.hpp | 38 +++++ src/util/LayoutCreator.hpp | 22 +++ src/widgets/dialogs/IrcConnectionEditor.cpp | 16 ++ src/widgets/dialogs/IrcConnectionEditor.hpp | 19 +++ src/widgets/dialogs/IrcConnectionEditor.ui | 159 ++++++++++++++++++++ src/widgets/dialogs/IrcConnectionPopup.cpp | 36 +++++ src/widgets/dialogs/IrcConnectionPopup.hpp | 13 ++ src/widgets/dialogs/SelectChannelDialog.cpp | 81 ++++++++-- 11 files changed, 455 insertions(+), 13 deletions(-) create mode 100644 src/providers/irc/Irc2.cpp create mode 100644 src/providers/irc/Irc2.hpp create mode 100644 src/widgets/dialogs/IrcConnectionEditor.cpp create mode 100644 src/widgets/dialogs/IrcConnectionEditor.hpp create mode 100644 src/widgets/dialogs/IrcConnectionEditor.ui create mode 100644 src/widgets/dialogs/IrcConnectionPopup.cpp create mode 100644 src/widgets/dialogs/IrcConnectionPopup.hpp diff --git a/chatterino.pro b/chatterino.pro index d2db3dbc9..a1ef3933a 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -129,6 +129,7 @@ SOURCES += \ src/providers/ffz/FfzEmotes.cpp \ src/providers/ffz/FfzModBadge.cpp \ src/providers/irc/AbstractIrcServer.cpp \ + src/providers/irc/Irc2.cpp \ src/providers/irc/IrcAccount.cpp \ src/providers/irc/IrcChannel2.cpp \ src/providers/irc/IrcConnection2.cpp \ @@ -177,6 +178,8 @@ SOURCES += \ src/widgets/AccountSwitchWidget.cpp \ src/widgets/AttachedWindow.cpp \ src/widgets/dialogs/EmotePopup.cpp \ + src/widgets/dialogs/IrcConnectionEditor.cpp \ + src/widgets/dialogs/IrcConnectionPopup.cpp \ src/widgets/dialogs/LastRunCrashDialog.cpp \ src/widgets/dialogs/LoginDialog.cpp \ src/widgets/dialogs/LogsPopup.cpp \ @@ -294,6 +297,7 @@ HEADERS += \ src/providers/ffz/FfzEmotes.hpp \ src/providers/ffz/FfzModBadge.hpp \ src/providers/irc/AbstractIrcServer.hpp \ + src/providers/irc/Irc2.hpp \ src/providers/irc/IrcAccount.hpp \ src/providers/irc/IrcChannel2.hpp \ src/providers/irc/IrcConnection2.hpp \ @@ -354,6 +358,8 @@ HEADERS += \ src/widgets/AccountSwitchWidget.hpp \ src/widgets/AttachedWindow.hpp \ src/widgets/dialogs/EmotePopup.hpp \ + src/widgets/dialogs/IrcConnectionEditor.hpp \ + src/widgets/dialogs/IrcConnectionPopup.hpp \ src/widgets/dialogs/LastRunCrashDialog.hpp \ src/widgets/dialogs/LoginDialog.hpp \ src/widgets/dialogs/LogsPopup.hpp \ @@ -405,7 +411,8 @@ RESOURCES += \ DISTFILES += -FORMS += +FORMS += \ + src/widgets/dialogs/IrcConnectionEditor.ui # do not use windows min/max macros #win32 { diff --git a/src/Application.cpp b/src/Application.cpp index 42b9bd0bf..37b26394b 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -116,6 +116,8 @@ void Application::save() void Application::initNm(Paths &paths) { + (void)paths; + #ifdef Q_OS_WIN # if defined QT_NO_DEBUG || defined C_DEBUG_NM registerNmHost(paths); diff --git a/src/providers/irc/Irc2.cpp b/src/providers/irc/Irc2.cpp new file mode 100644 index 000000000..44f88f855 --- /dev/null +++ b/src/providers/irc/Irc2.cpp @@ -0,0 +1,73 @@ +#include "Irc2.hpp" + +#include "common/SignalVectorModel.hpp" +#include "util/StandardItemHelper.hpp" + +namespace chatterino { + +namespace { + class Model : public SignalVectorModel + { + public: + Model(QObject *parent) + : SignalVectorModel(6, parent) + { + } + + // turn a vector item into a model row + IrcConnection_ getItemFromRow(std::vector &row, + const IrcConnection_ &original) + { + return IrcConnection_{ + row[0]->data(Qt::EditRole).toString(), // host + row[1]->data(Qt::EditRole).toInt(), // port + row[2]->data(Qt::Checked).toBool(), // ssl + row[3]->data(Qt::EditRole).toString(), // user + row[4]->data(Qt::EditRole).toString(), // nick + row[5]->data(Qt::EditRole).toString(), // password + original.id, // id + }; + } + + // turns a row in the model into a vector item + void getRowFromItem(const IrcConnection_ &item, + std::vector &row) + { + setStringItem(row[0], item.host); + setStringItem(row[1], QString::number(item.port)); + setBoolItem(row[2], item.ssl); + setStringItem(row[3], item.user); + setStringItem(row[4], item.nick); + setStringItem(row[5], item.password); + } + }; +} // namespace + +static std::atomic_int currentId; + +Irc::Irc() +{ +} + +IrcConnection_ IrcConnection_::unique() +{ + IrcConnection_ c; + c.id = currentId++; + c.port = 6697; + return c; +} + +QAbstractTableModel *Irc::newConnectionModel(QObject *parent) +{ + auto model = new Model(parent); + model->init(&this->connections); + return model; +} + +Irc &Irc::getInstance() +{ + static Irc irc; + return irc; +} + +} // namespace chatterino diff --git a/src/providers/irc/Irc2.hpp b/src/providers/irc/Irc2.hpp new file mode 100644 index 000000000..ef3d7a922 --- /dev/null +++ b/src/providers/irc/Irc2.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + +class QAbstractTableModel; + +namespace chatterino { + +struct IrcConnection_ { + QString host; + int port; + bool ssl; + + QString user; + QString nick; + QString password; + + int id; + + // makes an IrcConnection with a unique id + static IrcConnection_ unique(); +}; + +class Irc +{ +public: + Irc(); + + static Irc &getInstance(); + + UnsortedSignalVector connections; + QAbstractTableModel *newConnectionModel(QObject *parent); + +signals: + void connectionUpdated(int id); +}; + +} // namespace chatterino diff --git a/src/util/LayoutCreator.hpp b/src/util/LayoutCreator.hpp index b31fcef8b..f29d0b528 100644 --- a/src/util/LayoutCreator.hpp +++ b/src/util/LayoutCreator.hpp @@ -126,6 +126,20 @@ public: return LayoutCreator(item); } + template + LayoutCreator connect(Slot slot, QObject *receiver, Func func) + { + QObject::connect(this->getElement(), slot, receiver, func); + return *this; + } + + template + LayoutCreator onClick(QObject *receiver, Func func) + { + QObject::connect(this->getElement(), &T::clicked, receiver, func); + return *this; + } + private: T *item_; @@ -169,4 +183,12 @@ private: } }; +template +LayoutCreator makeDialog(Args &&... args) +{ + T *t = new T(std::forward(args)...); + t->setAttribute(Qt::WA_DeleteOnClose); + return LayoutCreator(t); +} + } // namespace chatterino diff --git a/src/widgets/dialogs/IrcConnectionEditor.cpp b/src/widgets/dialogs/IrcConnectionEditor.cpp new file mode 100644 index 000000000..14ba38151 --- /dev/null +++ b/src/widgets/dialogs/IrcConnectionEditor.cpp @@ -0,0 +1,16 @@ +#include "IrcConnectionEditor.hpp" +#include "ui_IrcConnectionEditor.h" + +IrcConnectionEditor::IrcConnectionEditor(bool isAdd, QWidget *parent) + : QDialog(parent) + , ui(new Ui::IrcConnectionEditor) +{ + ui->setupUi(this); + + this->setWindowTitle(QString(isAdd ? "Add " : "Edit ") + "Irc Connection"); +} + +IrcConnectionEditor::~IrcConnectionEditor() +{ + delete ui; +} diff --git a/src/widgets/dialogs/IrcConnectionEditor.hpp b/src/widgets/dialogs/IrcConnectionEditor.hpp new file mode 100644 index 000000000..fc700ecfc --- /dev/null +++ b/src/widgets/dialogs/IrcConnectionEditor.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace Ui { +class IrcConnectionEditor; +} + +class IrcConnectionEditor : public QDialog +{ + Q_OBJECT + +public: + explicit IrcConnectionEditor(bool isAdd = false, QWidget *parent = nullptr); + ~IrcConnectionEditor(); + +private: + Ui::IrcConnectionEditor *ui; +}; diff --git a/src/widgets/dialogs/IrcConnectionEditor.ui b/src/widgets/dialogs/IrcConnectionEditor.ui new file mode 100644 index 000000000..a7fcc3ef5 --- /dev/null +++ b/src/widgets/dialogs/IrcConnectionEditor.ui @@ -0,0 +1,159 @@ + + + IrcConnectionEditor + + + + 0 + 0 + 264 + 258 + + + + Dialog + + + + + + + + Server: + + + + + + + + + + Port: + + + + + + + 65636 + + + 6697 + + + + + + + SSL: + + + + + + + true + + + true + + + + + + + Qt::Vertical + + + + 20 + 20 + + + + + + + + User Name: + + + + + + + + + + Nick Name: + + + + + + + + + + Password: + + + + + + + QLineEdit::Password + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + IrcConnectionEditor + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + IrcConnectionEditor + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/widgets/dialogs/IrcConnectionPopup.cpp b/src/widgets/dialogs/IrcConnectionPopup.cpp new file mode 100644 index 000000000..c06827299 --- /dev/null +++ b/src/widgets/dialogs/IrcConnectionPopup.cpp @@ -0,0 +1,36 @@ +#include "IrcConnectionPopup.hpp" + +#include "providers/irc/Irc2.hpp" +#include "util/LayoutHelper.hpp" +#include "widgets/helper/EditableModelView.hpp" + +#include +#include + +namespace chatterino { + +IrcConnectionPopup::IrcConnectionPopup(QWidget *parent) + : BaseWindow(parent, BaseWindow::Flags::EnableCustomFrame) +{ + this->setWindowTitle("Edit Irc Connections"); + + // view + auto view = + new EditableModelView(Irc::getInstance().newConnectionModel(this)); + + view->setTitles({"host", "port", "ssl", "user", "nick", "password"}); + view->getTableView()->horizontalHeader()->resizeSection(0, 140); + view->getTableView()->horizontalHeader()->resizeSection(1, 30); + view->getTableView()->horizontalHeader()->resizeSection(2, 30); + + this->setScaleIndependantSize(800, 500); + + view->addButtonPressed.connect([] { + Irc::getInstance().connections.appendItem(IrcConnection_::unique()); + }); + + // init layout + this->getLayoutContainer()->setLayout(makeLayout({view})); +} + +} // namespace chatterino diff --git a/src/widgets/dialogs/IrcConnectionPopup.hpp b/src/widgets/dialogs/IrcConnectionPopup.hpp new file mode 100644 index 000000000..8d0ca333e --- /dev/null +++ b/src/widgets/dialogs/IrcConnectionPopup.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include "widgets/BaseWindow.hpp" + +namespace chatterino { + +class IrcConnectionPopup : public BaseWindow +{ +public: + IrcConnectionPopup(QWidget *parent); +}; + +} // namespace chatterino diff --git a/src/widgets/dialogs/SelectChannelDialog.cpp b/src/widgets/dialogs/SelectChannelDialog.cpp index f965ccb5c..b5af90fe5 100644 --- a/src/widgets/dialogs/SelectChannelDialog.cpp +++ b/src/widgets/dialogs/SelectChannelDialog.cpp @@ -5,6 +5,8 @@ #include "singletons/Theme.hpp" #include "util/LayoutCreator.hpp" #include "widgets/Notebook.hpp" +#include "widgets/dialogs/IrcConnectionEditor.hpp" +#include "widgets/dialogs/IrcConnectionPopup.hpp" #include "widgets/helper/NotebookTab.hpp" #include @@ -14,6 +16,10 @@ #include #include +#include +#include "providers/irc/Irc2.hpp" +#include "widgets/helper/EditableModelView.hpp" + #define TAB_TWITCH 0 namespace chatterino { @@ -122,21 +128,72 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) } // irc - /* + { + LayoutCreator obj(new QWidget()); + auto outerBox = obj.setLayoutType(); + // outerBox.emplace("Connection:"); + { - LayoutCreator obj(new QWidget()); - auto vbox = obj.setLayoutType(); - auto form = vbox.emplace(); + auto view = new EditableModelView( + Irc::getInstance().newConnectionModel(this)); - form->addRow(new QLabel("User name:"), new QLineEdit()); - form->addRow(new QLabel("First nick choice:"), new QLineEdit()); - form->addRow(new QLabel("Second nick choice:"), new QLineEdit()); - form->addRow(new QLabel("Third nick choice:"), new QLineEdit()); + view->setTitles( + {"host", "port", "ssl", "user", "nick", "password"}); + view->getTableView()->horizontalHeader()->resizeSection(0, 140); + view->getTableView()->horizontalHeader()->resizeSection(1, 30); + view->getTableView()->horizontalHeader()->resizeSection(2, 30); - auto tab = notebook->addPage(obj.getElement()); - tab->setCustomTitle("Irc"); + view->addButtonPressed.connect([] { + Irc::getInstance().connections.appendItem( + IrcConnection_::unique()); + }); + + outerBox->addWidget(view); + + // auto box = outerBox.emplace().withoutMargin(); + + // auto conns = box.emplace(); + // conns->addActions({new QAction("hackint")}); + + // auto buttons = box.emplace().withoutMargin(); + + // buttons.emplace("Add").onClick(this, [this]() { + // (new IrcConnectionPopup(this)) + // ->show(); // XXX: don't show multiple + // }); + + // buttons.emplace("Edit"); + // buttons.emplace("Remove"); + // buttons->addStretch(1); } - */ + + { + auto box = outerBox.emplace().withoutMargin(); + box.emplace("Channel:"); + box.emplace(); + } + + // auto vbox = obj.setLayoutType(); + // auto form = vbox.emplace(); + + // auto servers = new QComboBox; + // auto accounts = new QComboBox; + //servers->setEditable(true); + //servers->addItems( + // {"irc://irc.hackint.org:6697", "irc://irc.somethingelse.com:6667"}); + + // form->addRow("Server:", servers); + // form->addRow("Account:", accounts); + // form->addRow("Channel:", new QLineEdit()); + + // form->addRow("User name:", new QLineEdit()); + // form->addRow("First nick choice:", new QLineEdit()); + // form->addRow("Second nick choice:", new QLineEdit()); + // form->addRow("Third nick choice:", new QLineEdit()); + + auto tab = notebook->addPage(obj.getElement()); + tab->setCustomTitle("Irc"); + } layout->setStretchFactor(notebook.getElement(), 1); @@ -151,7 +208,7 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) [=](bool) { this->close(); }); } - this->setScaleIndependantSize(300, 310); + this->setMinimumSize(300, 310); this->ui_.notebook->selectIndex(TAB_TWITCH); this->ui_.twitch.channel->setFocus(); From 8241ce70974c0b06022441fb8ab3349487621fc8 Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 9 Sep 2019 22:18:56 +0200 Subject: [PATCH 02/41] added qtkeychain dependency --- .gitmodules | 3 +++ chatterino.pro | 3 +++ lib/qtkeychain.pri | 1 + resources/resources_autogenerated.qrc | 1 + src/widgets/settingspages/AboutPage.cpp | 3 +++ 5 files changed, 11 insertions(+) create mode 100644 lib/qtkeychain.pri diff --git a/.gitmodules b/.gitmodules index 28a1b9d6a..30dbba83e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -26,3 +26,6 @@ [submodule "lib/rapidjson"] path = lib/rapidjson url = https://github.com/Tencent/rapidjson +[submodule "lib/qtkeychain"] + path = lib/qtkeychain + url = https://github.com/frankosterfeld/qtkeychain diff --git a/chatterino.pro b/chatterino.pro index a1ef3933a..d2dbc1182 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -54,6 +54,7 @@ include(lib/settings.pri) include(lib/serialize.pri) include(lib/winsdk.pri) include(lib/rapidjson.pri) +include(lib/qtkeychain.pri) exists( $$OUT_PWD/conanbuildinfo.pri ) { message("Using conan packages") @@ -79,6 +80,7 @@ SOURCES += \ src/BrowserExtension.cpp \ src/common/Channel.cpp \ src/common/CompletionModel.cpp \ + src/common/Credentials.cpp \ src/common/DownloadManager.cpp \ src/common/Env.cpp \ src/common/LinkParser.cpp \ @@ -233,6 +235,7 @@ HEADERS += \ src/common/Common.hpp \ src/common/CompletionModel.hpp \ src/common/ConcurrentMap.hpp \ + src/common/Credentials.hpp \ src/common/DownloadManager.hpp \ src/common/Env.hpp \ src/common/LinkParser.hpp \ diff --git a/lib/qtkeychain.pri b/lib/qtkeychain.pri new file mode 100644 index 000000000..2ed6914fd --- /dev/null +++ b/lib/qtkeychain.pri @@ -0,0 +1 @@ +include(qtkeychain/qt5keychain.pri) diff --git a/resources/resources_autogenerated.qrc b/resources/resources_autogenerated.qrc index 9f0569e3d..04deaf238 100644 --- a/resources/resources_autogenerated.qrc +++ b/resources/resources_autogenerated.qrc @@ -46,6 +46,7 @@ licenses/pajlada_settings.txt licenses/pajlada_signals.txt licenses/qt_lgpl-3.0.txt + licenses/qtkeychain.txt licenses/rapidjson.txt licenses/websocketpp.txt pajaDank.png diff --git a/src/widgets/settingspages/AboutPage.cpp b/src/widgets/settingspages/AboutPage.cpp index a69da6ba1..c59ffb6ed 100644 --- a/src/widgets/settingspages/AboutPage.cpp +++ b/src/widgets/settingspages/AboutPage.cpp @@ -98,6 +98,9 @@ AboutPage::AboutPage() addLicense(form.getElement(), "Websocketpp", "https://www.zaphoyd.com/websocketpp/", ":/licenses/websocketpp.txt"); + addLicense(form.getElement(), "QtKeychain", + "https://github.com/frankosterfeld/qtkeychain", + ":/licenses/qtkeychain.txt"); } auto attributions = layout.emplace("Attributions..."); From 99b92bf8207a86ce5b031087e93765ebe968d352 Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 9 Sep 2019 22:19:20 +0200 Subject: [PATCH 03/41] moved communi namespace define into libcommuni.pri --- chatterino.pro | 2 -- lib/libcommuni.pri | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chatterino.pro b/chatterino.pro index d2dbc1182..d59bbb0fd 100644 --- a/chatterino.pro +++ b/chatterino.pro @@ -40,8 +40,6 @@ macx { } # Submodules -DEFINES += IRC_NAMESPACE=Communi - include(lib/warnings.pri) include(lib/appbase.pri) include(lib/fmt.pri) diff --git a/lib/libcommuni.pri b/lib/libcommuni.pri index 43b902fbc..8402eb747 100644 --- a/lib/libcommuni.pri +++ b/lib/libcommuni.pri @@ -1,3 +1,5 @@ +DEFINES += IRC_NAMESPACE=Communi + include(../lib/libcommuni/src/core/core.pri) include(../lib/libcommuni/src/model/model.pri) include(../lib/libcommuni/src/util/util.pri) From 9f1a5b900e793ebc46bc4dda84f0a10638022358 Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 9 Sep 2019 22:22:47 +0200 Subject: [PATCH 04/41] fixed single connection code and deleting abstractircserver --- src/common/Channel.hpp | 1 + src/providers/irc/AbstractIrcServer.cpp | 72 ++++++++++++++++--------- src/providers/irc/AbstractIrcServer.hpp | 19 ++++--- 3 files changed, 59 insertions(+), 33 deletions(-) diff --git a/src/common/Channel.hpp b/src/common/Channel.hpp index 596560bd8..32ca3e6e5 100644 --- a/src/common/Channel.hpp +++ b/src/common/Channel.hpp @@ -37,6 +37,7 @@ public: TwitchWatching, TwitchMentions, TwitchEnd, + Irc, Misc }; diff --git a/src/providers/irc/AbstractIrcServer.cpp b/src/providers/irc/AbstractIrcServer.cpp index 3e9c0b6b3..8aab88df8 100644 --- a/src/providers/irc/AbstractIrcServer.cpp +++ b/src/providers/irc/AbstractIrcServer.cpp @@ -18,13 +18,17 @@ const int MAX_FALLOFF_COUNTER = 60; AbstractIrcServer::AbstractIrcServer() { // Initialize the connections + // XXX: don't create write connection if there is not separate write connection. this->writeConnection_.reset(new IrcConnection); this->writeConnection_->moveToThread( QCoreApplication::instance()->thread()); QObject::connect( this->writeConnection_.get(), &Communi::IrcConnection::messageReceived, - [this](auto msg) { this->writeConnectionMessageReceived(msg); }); + this, [this](auto msg) { this->writeConnectionMessageReceived(msg); }); + QObject::connect( + this->writeConnection_.get(), &Communi::IrcConnection::connected, this, + [this] { this->onWriteConnected(this->writeConnection_.get()); }); // Listen to read connection message signals this->readConnection_.reset(new IrcConnection); @@ -32,21 +36,18 @@ AbstractIrcServer::AbstractIrcServer() QObject::connect( this->readConnection_.get(), &Communi::IrcConnection::messageReceived, - [this](auto msg) { this->readConnectionMessageReceived(msg); }); + this, [this](auto msg) { this->readConnectionMessageReceived(msg); }); QObject::connect(this->readConnection_.get(), - &Communi::IrcConnection::privateMessageReceived, + &Communi::IrcConnection::privateMessageReceived, this, [this](auto msg) { this->privateMessageReceived(msg); }); QObject::connect( - this->readConnection_.get(), &Communi::IrcConnection::connected, + this->readConnection_.get(), &Communi::IrcConnection::connected, this, [this] { this->onReadConnected(this->readConnection_.get()); }); - QObject::connect( - this->writeConnection_.get(), &Communi::IrcConnection::connected, - [this] { this->onWriteConnected(this->writeConnection_.get()); }); QObject::connect(this->readConnection_.get(), - &Communi::IrcConnection::disconnected, + &Communi::IrcConnection::disconnected, this, [this] { this->onDisconnected(); }); QObject::connect(this->readConnection_.get(), - &Communi::IrcConnection::socketError, + &Communi::IrcConnection::socketError, this, [this] { this->onSocketError(); }); // listen to reconnect request @@ -75,9 +76,7 @@ void AbstractIrcServer::connect() { this->disconnect(); - bool separateWriteConnection = this->hasSeparateWriteConnection(); - - if (separateWriteConnection) + if (this->hasSeparateWriteConnection()) { this->initializeConnection(this->writeConnection_.get(), false, true); this->initializeConnection(this->readConnection_.get(), true, false); @@ -94,18 +93,18 @@ void AbstractIrcServer::connect() for (std::weak_ptr &weak : this->channels.values()) { - if (auto channel = std::shared_ptr(weak.lock())) + if (auto channel = weak.lock()) { this->readConnection_->sendRaw("JOIN #" + channel->getName()); } } - this->writeConnection_->open(); + if (this->hasSeparateWriteConnection()) + { + this->writeConnection_->open(); + } this->readConnection_->open(); } - - // this->onConnected(); - // possbile event: started to connect } void AbstractIrcServer::disconnect() @@ -113,7 +112,10 @@ void AbstractIrcServer::disconnect() std::lock_guard locker(this->connectionMutex_); this->readConnection_->close(); - this->writeConnection_->close(); + if (this->hasSeparateWriteConnection()) + { + this->writeConnection_->close(); + } } void AbstractIrcServer::sendMessage(const QString &channelName, @@ -139,10 +141,10 @@ void AbstractIrcServer::sendRawMessage(const QString &rawMessage) void AbstractIrcServer::writeConnectionMessageReceived( Communi::IrcMessage *message) { + (void)message; } -std::shared_ptr AbstractIrcServer::getOrAddChannel( - const QString &dirtyChannelName) +ChannelPtr AbstractIrcServer::getOrAddChannel(const QString &dirtyChannelName) { auto channelName = this->cleanChannelName(dirtyChannelName); @@ -177,7 +179,7 @@ std::shared_ptr AbstractIrcServer::getOrAddChannel( this->readConnection_->sendRaw("PART #" + clojuresInCppAreShit); } - if (this->writeConnection_) + if (this->writeConnection_ && this->hasSeparateWriteConnection()) { this->writeConnection_->sendRaw("PART #" + clojuresInCppAreShit); } @@ -192,7 +194,7 @@ std::shared_ptr AbstractIrcServer::getOrAddChannel( this->readConnection_->sendRaw("JOIN #" + channelName); } - if (this->writeConnection_) + if (this->writeConnection_ && this->hasSeparateWriteConnection()) { this->writeConnection_->sendRaw("JOIN #" + channelName); } @@ -201,8 +203,7 @@ std::shared_ptr AbstractIrcServer::getOrAddChannel( return chan; } -std::shared_ptr AbstractIrcServer::getChannelOrEmpty( - const QString &dirtyChannelName) +ChannelPtr AbstractIrcServer::getChannelOrEmpty(const QString &dirtyChannelName) { auto channelName = this->cleanChannelName(dirtyChannelName); @@ -230,9 +231,24 @@ std::shared_ptr AbstractIrcServer::getChannelOrEmpty( return Channel::getEmpty(); } +std::vector> AbstractIrcServer::getChannels() +{ + std::lock_guard lock(this->channelMutex); + std::vector> channels; + + for (auto &&weak : this->channels.values()) + { + channels.push_back(weak); + } + + return channels; +} + void AbstractIrcServer::onReadConnected(IrcConnection *connection) { - std::lock_guard lock(this->channelMutex); + (void)connection; + + std::lock_guard lock(this->channelMutex); auto connectedMsg = makeSystemMessage("connected"); connectedMsg->flags.set(MessageFlag::ConnectedMessage); @@ -267,6 +283,7 @@ void AbstractIrcServer::onReadConnected(IrcConnection *connection) void AbstractIrcServer::onWriteConnected(IrcConnection *connection) { + (void)connection; } void AbstractIrcServer::onDisconnected() @@ -297,6 +314,7 @@ void AbstractIrcServer::onSocketError() std::shared_ptr AbstractIrcServer::getCustomChannel( const QString &channelName) { + (void)channelName; return nullptr; } @@ -324,11 +342,13 @@ void AbstractIrcServer::addFakeMessage(const QString &data) void AbstractIrcServer::privateMessageReceived( Communi::IrcPrivateMessage *message) { + (void)message; } void AbstractIrcServer::readConnectionMessageReceived( Communi::IrcMessage *message) { + (void)message; } void AbstractIrcServer::forEachChannel(std::function func) @@ -337,7 +357,7 @@ void AbstractIrcServer::forEachChannel(std::function func) for (std::weak_ptr &weak : this->channels.values()) { - std::shared_ptr chan = weak.lock(); + ChannelPtr chan = weak.lock(); if (!chan) { continue; diff --git a/src/providers/irc/AbstractIrcServer.hpp b/src/providers/irc/AbstractIrcServer.hpp index 98abc970c..5127b8a68 100644 --- a/src/providers/irc/AbstractIrcServer.hpp +++ b/src/providers/irc/AbstractIrcServer.hpp @@ -13,7 +13,7 @@ namespace chatterino { class Channel; using ChannelPtr = std::shared_ptr; -class AbstractIrcServer +class AbstractIrcServer : public QObject { public: virtual ~AbstractIrcServer() = default; @@ -26,14 +26,12 @@ public: void sendRawMessage(const QString &rawMessage); // channels - std::shared_ptr getOrAddChannel(const QString &dirtyChannelName); - std::shared_ptr getChannelOrEmpty(const QString &dirtyChannelName); + ChannelPtr getOrAddChannel(const QString &dirtyChannelName); + ChannelPtr getChannelOrEmpty(const QString &dirtyChannelName); // signals pajlada::Signals::NoArgSignal connected; pajlada::Signals::NoArgSignal disconnected; - // pajlada::Signals::Signal - // onPrivateMessage; void addFakeMessage(const QString &data); @@ -69,8 +67,15 @@ protected: private: void initConnection(); - std::unique_ptr writeConnection_ = nullptr; - std::unique_ptr readConnection_ = nullptr; + struct Deleter { + void operator()(IrcConnection *conn) + { + conn->deleteLater(); + } + }; + + std::unique_ptr writeConnection_ = nullptr; + std::unique_ptr readConnection_ = nullptr; QTimer reconnectTimer_; int falloffCounter_ = 1; From fd0c11964e805a568c4fefe5c7a5927cc6d81939 Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 9 Sep 2019 22:26:14 +0200 Subject: [PATCH 05/41] added new function and classes for irc support --- src/providers/irc/AbstractIrcServer.hpp | 1 + src/providers/irc/Irc2.cpp | 79 +++++++++++++++++++++++++ src/providers/irc/Irc2.hpp | 16 +++++ src/providers/irc/IrcChannel2.cpp | 9 +-- src/providers/irc/IrcChannel2.hpp | 14 +++-- src/providers/irc/IrcServer.cpp | 62 +++++++++++++++++-- src/providers/irc/IrcServer.hpp | 34 +++++++---- 7 files changed, 187 insertions(+), 28 deletions(-) diff --git a/src/providers/irc/AbstractIrcServer.hpp b/src/providers/irc/AbstractIrcServer.hpp index 5127b8a68..c02b8bd4e 100644 --- a/src/providers/irc/AbstractIrcServer.hpp +++ b/src/providers/irc/AbstractIrcServer.hpp @@ -28,6 +28,7 @@ public: // channels ChannelPtr getOrAddChannel(const QString &dirtyChannelName); ChannelPtr getChannelOrEmpty(const QString &dirtyChannelName); + std::vector> getChannels(); // signals pajlada::Signals::NoArgSignal connected; diff --git a/src/providers/irc/Irc2.cpp b/src/providers/irc/Irc2.cpp index 44f88f855..551f9ef13 100644 --- a/src/providers/irc/Irc2.cpp +++ b/src/providers/irc/Irc2.cpp @@ -1,6 +1,9 @@ #include "Irc2.hpp" +#include +#include "common/Credentials.hpp" #include "common/SignalVectorModel.hpp" +#include "util/RapidjsonHelpers.hpp" #include "util/StandardItemHelper.hpp" namespace chatterino { @@ -45,15 +48,68 @@ namespace { static std::atomic_int currentId; +//inline QString escape(QString str) +//{ +// return str.replace(":", "::"); +//} + +//inline QString getCredentialName(const IrcConnection_ &conn) +//{ +// //return escape(conn.host) + escape(conn. +//} + +//QString IrcConnection_::getPassword() +//{ +// return +//} + +//void IrcConnection_::setPassword(const QString &str) +//{ +// //Credentials::set("irc", +//} + Irc::Irc() { + this->connections.itemInserted.connect([this](auto &&args) { + // make sure only one id can only exist for one server + assert(this->servers_.find(args.item.id) == this->servers_.end()); + + if (auto ab = this->abandonedChannels_.find(args.item.id); + ab != this->abandonedChannels_.end()) + { + // add new server with abandoned channels + this->servers_.emplace(args.item.id, std::make_unique( + args.item, ab->second)); + this->abandonedChannels_.erase(ab); + } + else + { + // add new server + this->servers_.emplace(args.item.id, + std::make_unique(args.item)); + } + }); + + this->connections.itemRemoved.connect([this](auto &&args) { + // restore + if (auto server = this->servers_.find(args.item.id); + server != this->servers_.end()) + { + this->abandonedChannels_[args.item.id] = + server->second->getChannels(); + this->servers_.erase(server); + } + }); } IrcConnection_ IrcConnection_::unique() { IrcConnection_ c; + c.host = "localhost"; c.id = currentId++; c.port = 6697; + c.user = "xD"; + c.nick = "xD"; return c; } @@ -64,6 +120,29 @@ QAbstractTableModel *Irc::newConnectionModel(QObject *parent) return model; } +IrcServer *Irc::getServerOfChannel(Channel *channel) +{ + for (auto &&server : this->servers_) + for (auto weak : server.second->getChannels()) + if (auto shared = weak.lock()) + if (shared.get() == channel) + return server.second.get(); + + return nullptr; +} + +ChannelPtr Irc::getOrAddChannel(int id, QString name) +{ + if (auto server = this->servers_.find(id); server != this->servers_.end()) + { + return server->second->getOrAddChannel(name); + } + else + { + return Channel::getEmpty(); + } +} + Irc &Irc::getInstance() { static Irc irc; diff --git a/src/providers/irc/Irc2.hpp b/src/providers/irc/Irc2.hpp index ef3d7a922..f50c6b73f 100644 --- a/src/providers/irc/Irc2.hpp +++ b/src/providers/irc/Irc2.hpp @@ -1,7 +1,11 @@ #pragma once +#include #include +#include "providers/irc/IrcChannel2.hpp" +#include "providers/irc/IrcServer.hpp" + class QAbstractTableModel; namespace chatterino { @@ -31,8 +35,20 @@ public: UnsortedSignalVector connections; QAbstractTableModel *newConnectionModel(QObject *parent); + IrcServer *getServerOfChannel(Channel *channel); + ChannelPtr getOrAddChannel(int serverId, QString name); + signals: void connectionUpdated(int id); + +private: + // Servers have a unique id. + // When a server gets changed it gets removed and then added again. + // So we store the channels of that server in abandonedChannels_ temporarily. + // Or if the server got removed permanently then it's still stored there. + std::unordered_map> servers_; + std::unordered_map>> + abandonedChannels_; }; } // namespace chatterino diff --git a/src/providers/irc/IrcChannel2.cpp b/src/providers/irc/IrcChannel2.cpp index bbdee2086..b938841d9 100644 --- a/src/providers/irc/IrcChannel2.cpp +++ b/src/providers/irc/IrcChannel2.cpp @@ -2,8 +2,9 @@ namespace chatterino { -// IrcChannel::IrcChannel() -//{ -//} -// +IrcChannel::IrcChannel(const QString &name) + : Channel(name, Channel::Type::Irc) +{ +} + } // namespace chatterino diff --git a/src/providers/irc/IrcChannel2.hpp b/src/providers/irc/IrcChannel2.hpp index ca6c9e24a..53da3e23a 100644 --- a/src/providers/irc/IrcChannel2.hpp +++ b/src/providers/irc/IrcChannel2.hpp @@ -1,11 +1,13 @@ #pragma once +#include "common/Channel.hpp" + namespace chatterino { -// class IrcChannel -//{ -// public: -// IrcChannel(); -//}; -// +class IrcChannel : public Channel +{ +public: + explicit IrcChannel(const QString &name); +}; + } // namespace chatterino diff --git a/src/providers/irc/IrcServer.cpp b/src/providers/irc/IrcServer.cpp index be5623610..ff256a2b7 100644 --- a/src/providers/irc/IrcServer.cpp +++ b/src/providers/irc/IrcServer.cpp @@ -2,11 +2,63 @@ #include +#include "providers/irc/Irc2.hpp" +#include "providers/irc/IrcChannel2.hpp" + namespace chatterino { -// IrcServer::IrcServer(const QString &hostname, int port) -//{ -// this->initConnection(); -//} -// +IrcServer::IrcServer(const IrcConnection_ &data) + : data_(new IrcConnection_(data)) +{ + this->connect(); +} + +IrcServer::IrcServer(const IrcConnection_ &data, + const std::vector> &restoreChannels) + : IrcServer(data) +{ + for (auto &&weak : restoreChannels) + { + if (auto shared = weak.lock()) + { + this->channels[shared->getName()] = weak; + } + } +} + +IrcServer::~IrcServer() +{ + delete this->data_; +} + +int IrcServer::getId() +{ + return this->data_->id; +} + +void IrcServer::initializeConnection(IrcConnection *connection, bool isRead, + bool isWrite) +{ + assert(isRead && isWrite); + + connection->setSecure(this->data_->ssl); + connection->setHost(this->data_->host); + connection->setPort(this->data_->port); + + connection->setUserName(this->data_->user); + connection->setNickName(this->data_->nick); + connection->setRealName(this->data_->nick); + connection->setPassword(this->data_->password); +} + +std::shared_ptr IrcServer::createChannel(const QString &channelName) +{ + return std::make_shared(channelName); +} + +bool IrcServer::hasSeparateWriteConnection() const +{ + return false; +} + } // namespace chatterino diff --git a/src/providers/irc/IrcServer.hpp b/src/providers/irc/IrcServer.hpp index c8faf434c..bb9218312 100644 --- a/src/providers/irc/IrcServer.hpp +++ b/src/providers/irc/IrcServer.hpp @@ -5,20 +5,28 @@ namespace chatterino { -// class IrcServer -//{ -// public: -// IrcServer(const QString &hostname, int port); +struct IrcConnection_; -// void setAccount(std::shared_ptr newAccount); -// std::shared_ptr getAccount() const; +class IrcServer : public AbstractIrcServer +{ +public: + explicit IrcServer(const IrcConnection_ &data); + IrcServer(const IrcConnection_ &data, + const std::vector> &restoreChannels); + ~IrcServer() override; -// protected: -// virtual void initializeConnection(Communi::IrcConnection *connection, bool -// isReadConnection); + int getId(); + + // AbstractIrcServer interface +protected: + void initializeConnection(IrcConnection *connection, bool isRead, + bool isWrite) override; + std::shared_ptr createChannel(const QString &channelName) override; + bool hasSeparateWriteConnection() const override; + +private: + // pointer so we don't have to circle include Irc2.hpp + IrcConnection_ *data_; +}; -// virtual void privateMessageReceived(Communi::IrcPrivateMessage *message); -// virtual void messageReceived(Communi::IrcMessage *message); -//}; -// } // namespace chatterino From 5974438edfd13de6ff4e27575a19c918f4e50b0b Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 9 Sep 2019 22:26:56 +0200 Subject: [PATCH 06/41] added irc tab in SelectChannelView --- src/widgets/dialogs/SelectChannelDialog.cpp | 50 ++++++++++++++++++++- src/widgets/dialogs/SelectChannelDialog.hpp | 5 +++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/widgets/dialogs/SelectChannelDialog.cpp b/src/widgets/dialogs/SelectChannelDialog.cpp index b5af90fe5..be0779190 100644 --- a/src/widgets/dialogs/SelectChannelDialog.cpp +++ b/src/widgets/dialogs/SelectChannelDialog.cpp @@ -21,6 +21,7 @@ #include "widgets/helper/EditableModelView.hpp" #define TAB_TWITCH 0 +#define TAB_IRC 1 namespace chatterino { @@ -134,7 +135,7 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) // outerBox.emplace("Connection:"); { - auto view = new EditableModelView( + auto view = this->ui_.irc.servers = new EditableModelView( Irc::getInstance().newConnectionModel(this)); view->setTitles( @@ -170,7 +171,7 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) { auto box = outerBox.emplace().withoutMargin(); box.emplace("Channel:"); - box.emplace(); + this->ui_.irc.channel = box.emplace().getElement(); } // auto vbox = obj.setLayoutType(); @@ -261,6 +262,30 @@ void SelectChannelDialog::setSelectedChannel(IndirectChannel _channel) this->ui_.twitch.whispers->setFocus(); } break; + case Channel::Type::Irc: + { + this->ui_.notebook->selectIndex(TAB_IRC); + this->ui_.irc.channel->setText(_channel.get()->getName()); + + if (auto ircChannel = + dynamic_cast(_channel.get().get())) + { + if (auto server = + Irc::getInstance().getServerOfChannel(ircChannel)) + { + int i = 0; + for (auto &&conn : Irc::getInstance().connections) + { + if (conn.id == server->getId()) + { + this->ui_.irc.servers->getTableView()->selectRow(i); + break; + } + } + } + } + } + break; default: { this->ui_.notebook->selectIndex(TAB_TWITCH); @@ -302,6 +327,27 @@ IndirectChannel SelectChannelDialog::getSelectedChannel() const return app->twitch.server->whispersChannel; } } + break; + case TAB_IRC: + { + int row = this->ui_.irc.servers->getTableView() + ->selectionModel() + ->currentIndex() + .row(); + + auto &&vector = Irc::getInstance().connections.getVector(); + + if (row >= 0 && row < int(vector.size())) + { + return Irc::getInstance().getOrAddChannel( + vector[row].id, this->ui_.irc.channel->text()); + } + else + { + return Channel::getEmpty(); + } + } + break; } return this->selectedChannel_; diff --git a/src/widgets/dialogs/SelectChannelDialog.hpp b/src/widgets/dialogs/SelectChannelDialog.hpp index 5d5890d9a..51944ce7b 100644 --- a/src/widgets/dialogs/SelectChannelDialog.hpp +++ b/src/widgets/dialogs/SelectChannelDialog.hpp @@ -12,6 +12,7 @@ namespace chatterino { class Notebook; +class EditableModelView; class SelectChannelDialog final : public BaseWindow { @@ -47,6 +48,10 @@ private: QRadioButton *mentions; QRadioButton *watching; } twitch; + struct { + QLineEdit *channel; + EditableModelView *servers; + } irc; } ui_; EventFilter tabFilter_; From 25f75f758034d8336821317aa438f60f5aff0578 Mon Sep 17 00:00:00 2001 From: fourtf Date: Mon, 9 Sep 2019 22:27:46 +0200 Subject: [PATCH 07/41] added Credential class --- resources/licenses/qtkeychain.txt | 0 src/common/Credentials.cpp | 88 +++++++++++++++++++++++++++++++ src/common/Credentials.hpp | 23 ++++++++ 3 files changed, 111 insertions(+) create mode 100644 resources/licenses/qtkeychain.txt create mode 100644 src/common/Credentials.cpp create mode 100644 src/common/Credentials.hpp diff --git a/resources/licenses/qtkeychain.txt b/resources/licenses/qtkeychain.txt new file mode 100644 index 000000000..e69de29bb diff --git a/src/common/Credentials.cpp b/src/common/Credentials.cpp new file mode 100644 index 000000000..ad0e9c423 --- /dev/null +++ b/src/common/Credentials.cpp @@ -0,0 +1,88 @@ +#include "Credentials.hpp" + +#include "keychain.h" +#include "singletons/Paths.hpp" + +#define FORMAT_NAME \ + ([&] { \ + assert(!provider.contains(":")); \ + return QString("chatterino:%1:%2").arg(provider).arg(name_); \ + })() + +namespace chatterino { + +Credentials &Credentials::getInstance() +{ + static Credentials creds; + return creds; +} + +Credentials::Credentials() +{ +} + +QString Credentials::get(const QString &provider, const QString &name_, + std::function &&onLoaded) +{ + auto name = FORMAT_NAME; + + if (getPaths()->isPortable()) + { + assert(false); + + return {}; + } + else + { + auto job = new QKeychain::ReadPasswordJob("chatterino"); + job->setAutoDelete(true); + job->setKey(name); + QObject::connect(job, &QKeychain::Job::finished, qApp, + [job, onLoaded = std::move(onLoaded)](auto) mutable { + onLoaded(job->textData()); + }); + job->start(); + + return job->textData(); + } +} + +void Credentials::set(const QString &provider, const QString &name_, + const QString &credential) +{ + auto name = FORMAT_NAME; + + if (getPaths()->isPortable()) + { + assert(false); + } + else + { + auto job = new QKeychain::WritePasswordJob("chatterino"); + job->setAutoDelete(true); + job->setKey(name); + job->setTextData(credential); + QObject::connect(job, &QKeychain::Job::finished, qApp, [](auto) {}); + job->start(); + } +} + +void Credentials::erase(const QString &provider, const QString &name_) +{ + auto name = FORMAT_NAME; + + if (getPaths()->isPortable()) + { + assert(false); + } + else + { + auto job = new QKeychain::DeletePasswordJob("chatterino"); + job->setAutoDelete(true); + job->setKey(name); + QObject::connect(job, &QKeychain::Job::finished, qApp, [](auto) {}); + job->start(); + } +} + +} // namespace chatterino diff --git a/src/common/Credentials.hpp b/src/common/Credentials.hpp new file mode 100644 index 000000000..95ae27afb --- /dev/null +++ b/src/common/Credentials.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +namespace chatterino { + +class Credentials +{ +public: + static Credentials &getInstance(); + + QString get(const QString &provider, const QString &name, + std::function &&onLoaded); + void set(const QString &provider, const QString &name, + const QString &credential); + void erase(const QString &provider, const QString &name); + +private: + Credentials(); +}; + +} // namespace chatterino From 82f63d2f7eaf21192d5d5efe23001ec45c4b1e5c Mon Sep 17 00:00:00 2001 From: fourtf Date: Tue, 10 Sep 2019 14:46:43 +0200 Subject: [PATCH 08/41] added send and receive for irc messages --- src/providers/irc/Irc2.cpp | 61 ++++++++++++--------- src/providers/irc/Irc2.hpp | 3 +- src/providers/irc/IrcChannel2.cpp | 35 +++++++++++- src/providers/irc/IrcChannel2.hpp | 16 +++++- src/providers/irc/IrcServer.cpp | 40 +++++++++++++- src/providers/irc/IrcServer.hpp | 6 +- src/util/StandardItemHelper.hpp | 10 ++-- src/widgets/dialogs/SelectChannelDialog.cpp | 7 +-- 8 files changed, 137 insertions(+), 41 deletions(-) diff --git a/src/providers/irc/Irc2.cpp b/src/providers/irc/Irc2.cpp index 551f9ef13..5f92ad590 100644 --- a/src/providers/irc/Irc2.cpp +++ b/src/providers/irc/Irc2.cpp @@ -13,7 +13,7 @@ namespace { { public: Model(QObject *parent) - : SignalVectorModel(6, parent) + : SignalVectorModel(7, parent) { } @@ -21,14 +21,17 @@ namespace { IrcConnection_ getItemFromRow(std::vector &row, const IrcConnection_ &original) { + qDebug() << row[2]->data(Qt::CheckStateRole).toBool(); + return IrcConnection_{ - row[0]->data(Qt::EditRole).toString(), // host - row[1]->data(Qt::EditRole).toInt(), // port - row[2]->data(Qt::Checked).toBool(), // ssl - row[3]->data(Qt::EditRole).toString(), // user - row[4]->data(Qt::EditRole).toString(), // nick - row[5]->data(Qt::EditRole).toString(), // password - original.id, // id + row[0]->data(Qt::EditRole).toString(), // host + row[1]->data(Qt::EditRole).toInt(), // port + row[2]->data(Qt::CheckStateRole).toBool(), // ssl + row[3]->data(Qt::EditRole).toString(), // user + row[4]->data(Qt::EditRole).toString(), // nick + row[5]->data(Qt::EditRole).toString(), // real + row[6]->data(Qt::EditRole).toString(), // password + original.id, // id }; } @@ -41,7 +44,8 @@ namespace { setBoolItem(row[2], item.ssl); setStringItem(row[3], item.user); setStringItem(row[4], item.nick); - setStringItem(row[5], item.password); + setStringItem(row[5], item.real); + setStringItem(row[6], item.password); } }; } // namespace @@ -77,9 +81,17 @@ Irc::Irc() if (auto ab = this->abandonedChannels_.find(args.item.id); ab != this->abandonedChannels_.end()) { + auto server = std::make_unique(args.item, ab->second); + + // set server of abandoned channels + for (auto weak : ab->second) + if (auto shared = weak.lock()) + if (auto ircChannel = + dynamic_cast(shared.get())) + ircChannel->setServer(server.get()); + // add new server with abandoned channels - this->servers_.emplace(args.item.id, std::make_unique( - args.item, ab->second)); + this->servers_.emplace(args.item.id, std::move(server)); this->abandonedChannels_.erase(ab); } else @@ -95,8 +107,16 @@ Irc::Irc() if (auto server = this->servers_.find(args.item.id); server != this->servers_.end()) { - this->abandonedChannels_[args.item.id] = - server->second->getChannels(); + auto abandoned = server->second->getChannels(); + + // set server of abandoned servers to nullptr + for (auto weak : abandoned) + if (auto shared = weak.lock()) + if (auto ircChannel = + dynamic_cast(shared.get())) + ircChannel->setServer(nullptr); + + this->abandonedChannels_[args.item.id] = abandoned; this->servers_.erase(server); } }); @@ -107,9 +127,11 @@ IrcConnection_ IrcConnection_::unique() IrcConnection_ c; c.host = "localhost"; c.id = currentId++; - c.port = 6697; + c.port = 6667; + c.ssl = false; c.user = "xD"; c.nick = "xD"; + c.real = "xD"; return c; } @@ -120,17 +142,6 @@ QAbstractTableModel *Irc::newConnectionModel(QObject *parent) return model; } -IrcServer *Irc::getServerOfChannel(Channel *channel) -{ - for (auto &&server : this->servers_) - for (auto weak : server.second->getChannels()) - if (auto shared = weak.lock()) - if (shared.get() == channel) - return server.second.get(); - - return nullptr; -} - ChannelPtr Irc::getOrAddChannel(int id, QString name) { if (auto server = this->servers_.find(id); server != this->servers_.end()) diff --git a/src/providers/irc/Irc2.hpp b/src/providers/irc/Irc2.hpp index f50c6b73f..ec99c8511 100644 --- a/src/providers/irc/Irc2.hpp +++ b/src/providers/irc/Irc2.hpp @@ -17,6 +17,8 @@ struct IrcConnection_ { QString user; QString nick; + QString real; + QString password; int id; @@ -35,7 +37,6 @@ public: UnsortedSignalVector connections; QAbstractTableModel *newConnectionModel(QObject *parent); - IrcServer *getServerOfChannel(Channel *channel); ChannelPtr getOrAddChannel(int serverId, QString name); signals: diff --git a/src/providers/irc/IrcChannel2.cpp b/src/providers/irc/IrcChannel2.cpp index b938841d9..6ccf681e1 100644 --- a/src/providers/irc/IrcChannel2.cpp +++ b/src/providers/irc/IrcChannel2.cpp @@ -1,10 +1,43 @@ #include "IrcChannel2.hpp" +#include "debug/AssertInGuiThread.hpp" +#include "messages/MessageBuilder.hpp" +#include "providers/irc/IrcServer.hpp" + namespace chatterino { -IrcChannel::IrcChannel(const QString &name) +IrcChannel::IrcChannel(const QString &name, IrcServer *server) : Channel(name, Channel::Type::Irc) + , server_(server) { } +void IrcChannel::sendMessage(const QString &message) +{ + assertInGuiThread(); + + if (this->server()) + this->server()->sendMessage(this->getName(), message); + + MessageBuilder builder; + builder.emplace(this->server()->nick() + ":", + MessageElementFlag::Username); + builder.emplace(message, MessageElementFlag::Text); + this->addMessage(builder.release()); +} + +IrcServer *IrcChannel::server() +{ + assertInGuiThread(); + + return this->server_; +} + +void IrcChannel::setServer(IrcServer *server) +{ + assertInGuiThread(); + + this->server_ = server; +} + } // namespace chatterino diff --git a/src/providers/irc/IrcChannel2.hpp b/src/providers/irc/IrcChannel2.hpp index 53da3e23a..4b07be155 100644 --- a/src/providers/irc/IrcChannel2.hpp +++ b/src/providers/irc/IrcChannel2.hpp @@ -4,10 +4,24 @@ namespace chatterino { +class Irc; +class IrcServer; + class IrcChannel : public Channel { public: - explicit IrcChannel(const QString &name); + explicit IrcChannel(const QString &name, IrcServer *server); + + void sendMessage(const QString &message) override; + + IrcServer *server(); + +private: + void setServer(IrcServer *server); + + IrcServer *server_; + + friend class Irc; }; } // namespace chatterino diff --git a/src/providers/irc/IrcServer.cpp b/src/providers/irc/IrcServer.cpp index ff256a2b7..832c67f50 100644 --- a/src/providers/irc/IrcServer.cpp +++ b/src/providers/irc/IrcServer.cpp @@ -2,6 +2,7 @@ #include +#include "messages/MessageBuilder.hpp" #include "providers/irc/Irc2.hpp" #include "providers/irc/IrcChannel2.hpp" @@ -31,11 +32,21 @@ IrcServer::~IrcServer() delete this->data_; } -int IrcServer::getId() +int IrcServer::id() { return this->data_->id; } +const QString &IrcServer::user() +{ + return this->data_->user; +} + +const QString &IrcServer::nick() +{ + return this->data_->nick; +} + void IrcServer::initializeConnection(IrcConnection *connection, bool isRead, bool isWrite) { @@ -47,13 +58,13 @@ void IrcServer::initializeConnection(IrcConnection *connection, bool isRead, connection->setUserName(this->data_->user); connection->setNickName(this->data_->nick); - connection->setRealName(this->data_->nick); + connection->setRealName(this->data_->real); connection->setPassword(this->data_->password); } std::shared_ptr IrcServer::createChannel(const QString &channelName) { - return std::make_shared(channelName); + return std::make_shared(channelName, this); } bool IrcServer::hasSeparateWriteConnection() const @@ -61,4 +72,27 @@ bool IrcServer::hasSeparateWriteConnection() const return false; } +void IrcServer::privateMessageReceived(Communi::IrcPrivateMessage *message) +{ + auto target = message->target(); + target = target.startsWith('#') ? target.mid(1) : target; + + if (auto channel = this->getChannelOrEmpty(target); !channel->isEmpty()) + { + MessageBuilder builder; + + builder.emplace(message->nick() + ":", + MessageElementFlag::Username); + builder.emplace(message->content(), + MessageElementFlag::Text); + + channel->addMessage(builder.release()); + } +} + +void IrcServer::readConnectionMessageReceived(Communi::IrcMessage *message) +{ + qDebug() << QString(message->toData()); +} + } // namespace chatterino diff --git a/src/providers/irc/IrcServer.hpp b/src/providers/irc/IrcServer.hpp index bb9218312..9a7310a57 100644 --- a/src/providers/irc/IrcServer.hpp +++ b/src/providers/irc/IrcServer.hpp @@ -15,7 +15,9 @@ public: const std::vector> &restoreChannels); ~IrcServer() override; - int getId(); + int id(); + const QString &user(); + const QString &nick(); // AbstractIrcServer interface protected: @@ -23,6 +25,8 @@ protected: bool isWrite) override; std::shared_ptr createChannel(const QString &channelName) override; bool hasSeparateWriteConnection() const override; + void privateMessageReceived(Communi::IrcPrivateMessage *message) override; + void readConnectionMessageReceived(Communi::IrcMessage *message) override; private: // pointer so we don't have to circle include Irc2.hpp diff --git a/src/util/StandardItemHelper.hpp b/src/util/StandardItemHelper.hpp index 4638e7277..fb5eda108 100644 --- a/src/util/StandardItemHelper.hpp +++ b/src/util/StandardItemHelper.hpp @@ -7,7 +7,7 @@ namespace chatterino { static void setBoolItem(QStandardItem *item, bool value, bool userCheckable = true, bool selectable = true) { - item->setFlags((Qt::ItemFlags)( + item->setFlags(Qt::ItemFlags( Qt::ItemIsEnabled | (selectable ? Qt::ItemIsSelectable : 0) | (userCheckable ? Qt::ItemIsUserCheckable : 0))); item->setCheckState(value ? Qt::Checked : Qt::Unchecked); @@ -17,15 +17,15 @@ static void setStringItem(QStandardItem *item, const QString &value, bool editable = true, bool selectable = true) { item->setData(value, Qt::EditRole); - item->setFlags((Qt::ItemFlags)(Qt::ItemIsEnabled | - (selectable ? Qt::ItemIsSelectable : 0) | - (editable ? (Qt::ItemIsEditable) : 0))); + item->setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | + (selectable ? Qt::ItemIsSelectable : 0) | + (editable ? (Qt::ItemIsEditable) : 0))); } static QStandardItem *emptyItem() { auto *item = new QStandardItem(); - item->setFlags((Qt::ItemFlags)0); + item->setFlags(Qt::ItemFlags(0)); return item; } diff --git a/src/widgets/dialogs/SelectChannelDialog.cpp b/src/widgets/dialogs/SelectChannelDialog.cpp index be0779190..6a8f027f4 100644 --- a/src/widgets/dialogs/SelectChannelDialog.cpp +++ b/src/widgets/dialogs/SelectChannelDialog.cpp @@ -139,7 +139,7 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) Irc::getInstance().newConnectionModel(this)); view->setTitles( - {"host", "port", "ssl", "user", "nick", "password"}); + {"real", "port", "ssl", "user", "nick", "real", "password"}); view->getTableView()->horizontalHeader()->resizeSection(0, 140); view->getTableView()->horizontalHeader()->resizeSection(1, 30); view->getTableView()->horizontalHeader()->resizeSection(2, 30); @@ -270,13 +270,12 @@ void SelectChannelDialog::setSelectedChannel(IndirectChannel _channel) if (auto ircChannel = dynamic_cast(_channel.get().get())) { - if (auto server = - Irc::getInstance().getServerOfChannel(ircChannel)) + if (auto server = ircChannel->server()) { int i = 0; for (auto &&conn : Irc::getInstance().connections) { - if (conn.id == server->getId()) + if (conn.id == server->id()) { this->ui_.irc.servers->getTableView()->selectRow(i); break; From d0e10a937e2c0bd081fda6f3531d45303238c7f8 Mon Sep 17 00:00:00 2001 From: fourtf Date: Tue, 10 Sep 2019 15:53:45 +0200 Subject: [PATCH 09/41] fixed qtkeychain dependency --- lib/qtkeychain | 1 + 1 file changed, 1 insertion(+) create mode 160000 lib/qtkeychain diff --git a/lib/qtkeychain b/lib/qtkeychain new file mode 160000 index 000000000..e7118623a --- /dev/null +++ b/lib/qtkeychain @@ -0,0 +1 @@ +Subproject commit e7118623a44485c173cc43e7386725c8cfec0842 From cfaf59b6a5d3f55c836d992a93ade36bd08107f5 Mon Sep 17 00:00:00 2001 From: fourtf Date: Tue, 10 Sep 2019 16:42:11 +0200 Subject: [PATCH 10/41] removed qtkeychain --- .gitmodules | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index 30dbba83e..28a1b9d6a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -26,6 +26,3 @@ [submodule "lib/rapidjson"] path = lib/rapidjson url = https://github.com/Tencent/rapidjson -[submodule "lib/qtkeychain"] - path = lib/qtkeychain - url = https://github.com/frankosterfeld/qtkeychain From de956d2b9567b87cfa3d092d55d070b59d4f14d6 Mon Sep 17 00:00:00 2001 From: fourtf Date: Tue, 10 Sep 2019 16:46:02 +0200 Subject: [PATCH 11/41] deleted qtkeychain submodule --- lib/qtkeychain | 1 - 1 file changed, 1 deletion(-) delete mode 160000 lib/qtkeychain diff --git a/lib/qtkeychain b/lib/qtkeychain deleted file mode 160000 index e7118623a..000000000 --- a/lib/qtkeychain +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e7118623a44485c173cc43e7386725c8cfec0842 From fe67bda1361a474a52b778c9cde29b3853def1ca Mon Sep 17 00:00:00 2001 From: fourtf Date: Tue, 10 Sep 2019 17:00:23 +0200 Subject: [PATCH 12/41] moved .clang-format into src directory so submodules don't get formatted --- lib/appbase/.clang-format | 34 ++++++++++++++++++++++++++++++++++ src/.clang-format | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 lib/appbase/.clang-format create mode 100644 src/.clang-format diff --git a/lib/appbase/.clang-format b/lib/appbase/.clang-format new file mode 100644 index 000000000..fd28e7c25 --- /dev/null +++ b/lib/appbase/.clang-format @@ -0,0 +1,34 @@ +Language: Cpp + +AccessModifierOffset: -1 +AccessModifierOffset: -4 +AlignEscapedNewlinesLeft: true +AllowShortFunctionsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: false +AlwaysBreakBeforeMultilineStrings: false +BasedOnStyle: Google +BraceWrapping: { + AfterNamespace: 'false' + AfterClass: 'true' + BeforeElse: 'true' + AfterControlStatement: 'true' + AfterFunction: 'true' + BeforeCatch: 'true' +} +BreakBeforeBraces: Custom +BreakConstructorInitializersBeforeComma: true +ColumnLimit: 80 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +DerivePointerBinding: false +FixNamespaceComments: true +IndentCaseLabels: true +IndentWidth: 4 +IndentWrappedFunctionNames: true +IndentPPDirectives: AfterHash +NamespaceIndentation: Inner +PointerBindsToType: false +SpacesBeforeTrailingComments: 2 +Standard: Auto +ReflowComments: false diff --git a/src/.clang-format b/src/.clang-format new file mode 100644 index 000000000..fd28e7c25 --- /dev/null +++ b/src/.clang-format @@ -0,0 +1,34 @@ +Language: Cpp + +AccessModifierOffset: -1 +AccessModifierOffset: -4 +AlignEscapedNewlinesLeft: true +AllowShortFunctionsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: false +AlwaysBreakBeforeMultilineStrings: false +BasedOnStyle: Google +BraceWrapping: { + AfterNamespace: 'false' + AfterClass: 'true' + BeforeElse: 'true' + AfterControlStatement: 'true' + AfterFunction: 'true' + BeforeCatch: 'true' +} +BreakBeforeBraces: Custom +BreakConstructorInitializersBeforeComma: true +ColumnLimit: 80 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +DerivePointerBinding: false +FixNamespaceComments: true +IndentCaseLabels: true +IndentWidth: 4 +IndentWrappedFunctionNames: true +IndentPPDirectives: AfterHash +NamespaceIndentation: Inner +PointerBindsToType: false +SpacesBeforeTrailingComments: 2 +Standard: Auto +ReflowComments: false From 0219cb0c3650714a6841fbcb3d03b4fe32d77ad2 Mon Sep 17 00:00:00 2001 From: fourtf Date: Tue, 10 Sep 2019 17:06:58 +0200 Subject: [PATCH 13/41] added Chatterino/qtkeychain to fix compilation --- .clang-format | 34 ---------------------------------- .gitmodules | 3 +++ lib/qtkeychain | 1 + 3 files changed, 4 insertions(+), 34 deletions(-) delete mode 100644 .clang-format create mode 160000 lib/qtkeychain diff --git a/.clang-format b/.clang-format deleted file mode 100644 index fd28e7c25..000000000 --- a/.clang-format +++ /dev/null @@ -1,34 +0,0 @@ -Language: Cpp - -AccessModifierOffset: -1 -AccessModifierOffset: -4 -AlignEscapedNewlinesLeft: true -AllowShortFunctionsOnASingleLine: false -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterDefinitionReturnType: false -AlwaysBreakBeforeMultilineStrings: false -BasedOnStyle: Google -BraceWrapping: { - AfterNamespace: 'false' - AfterClass: 'true' - BeforeElse: 'true' - AfterControlStatement: 'true' - AfterFunction: 'true' - BeforeCatch: 'true' -} -BreakBeforeBraces: Custom -BreakConstructorInitializersBeforeComma: true -ColumnLimit: 80 -ConstructorInitializerAllOnOneLineOrOnePerLine: false -DerivePointerBinding: false -FixNamespaceComments: true -IndentCaseLabels: true -IndentWidth: 4 -IndentWrappedFunctionNames: true -IndentPPDirectives: AfterHash -NamespaceIndentation: Inner -PointerBindsToType: false -SpacesBeforeTrailingComments: 2 -Standard: Auto -ReflowComments: false diff --git a/.gitmodules b/.gitmodules index 28a1b9d6a..2036c3ab9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -26,3 +26,6 @@ [submodule "lib/rapidjson"] path = lib/rapidjson url = https://github.com/Tencent/rapidjson +[submodule "lib/qtkeychain"] + path = lib/qtkeychain + url = https://github.com/Chatterino/qtkeychain diff --git a/lib/qtkeychain b/lib/qtkeychain new file mode 160000 index 000000000..832f550da --- /dev/null +++ b/lib/qtkeychain @@ -0,0 +1 @@ +Subproject commit 832f550da3f6655168a737d2e1b7df37272e936d From c4d0efacffdf910b1c2ff448120ed080611ebeaf Mon Sep 17 00:00:00 2001 From: fourtf Date: Tue, 10 Sep 2019 23:55:43 +0200 Subject: [PATCH 14/41] added loading and saving of irc servers --- src/Application.cpp | 3 + src/common/SignalVectorModel.hpp | 5 + src/providers/irc/Irc2.cpp | 108 +++++++++++++++++--- src/providers/irc/Irc2.hpp | 18 +++- src/singletons/Paths.cpp | 2 +- src/widgets/dialogs/IrcConnectionEditor.cpp | 46 +++++++-- src/widgets/dialogs/IrcConnectionEditor.hpp | 19 +++- src/widgets/dialogs/IrcConnectionEditor.ui | 89 ++++++++++++++-- src/widgets/dialogs/IrcConnectionPopup.cpp | 4 +- src/widgets/dialogs/SelectChannelDialog.cpp | 78 +++++++------- 10 files changed, 292 insertions(+), 80 deletions(-) diff --git a/src/Application.cpp b/src/Application.cpp index 37b26394b..332272246 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -13,6 +13,7 @@ #include "providers/bttv/BttvEmotes.hpp" #include "providers/chatterino/ChatterinoBadges.hpp" #include "providers/ffz/FfzEmotes.hpp" +#include "providers/irc/Irc2.hpp" #include "providers/twitch/PubsubClient.hpp" #include "providers/twitch/TwitchServer.hpp" #include "singletons/Emotes.hpp" @@ -69,6 +70,8 @@ Application::Application(Settings &_settings, Paths &_paths) this->fonts->fontChanged.connect( [this]() { this->windows->layoutChannelViews(); }); + Irc::getInstance().load(); + this->twitch.server = this->twitch2; this->twitch.pubsub = this->twitch2->pubsub; } diff --git a/src/common/SignalVectorModel.hpp b/src/common/SignalVectorModel.hpp index d79263830..73e64d099 100644 --- a/src/common/SignalVectorModel.hpp +++ b/src/common/SignalVectorModel.hpp @@ -185,6 +185,11 @@ public: Qt::ItemFlags flags(const QModelIndex &index) const override { int row = index.row(), column = index.column(); + + if (row < 0 || column < 0) { + return Qt::NoItemFlags; + } + assert(row >= 0 && row < this->rows_.size() && column >= 0 && column < this->columnCount_); diff --git a/src/providers/irc/Irc2.cpp b/src/providers/irc/Irc2.cpp index 5f92ad590..d2857b433 100644 --- a/src/providers/irc/Irc2.cpp +++ b/src/providers/irc/Irc2.cpp @@ -3,17 +3,26 @@ #include #include "common/Credentials.hpp" #include "common/SignalVectorModel.hpp" +#include "singletons/Paths.hpp" +#include "util/CombinePath.hpp" #include "util/RapidjsonHelpers.hpp" #include "util/StandardItemHelper.hpp" +#include + namespace chatterino { namespace { + QString configPath() + { + return combinePath(getPaths()->settingsDirectory, "irc.json"); + } + class Model : public SignalVectorModel { public: Model(QObject *parent) - : SignalVectorModel(7, parent) + : SignalVectorModel(8, parent) { } @@ -39,7 +48,7 @@ namespace { void getRowFromItem(const IrcConnection_ &item, std::vector &row) { - setStringItem(row[0], item.host); + setStringItem(row[0], item.host, false); setStringItem(row[1], QString::number(item.port)); setBoolItem(row[2], item.ssl); setStringItem(row[3], item.user); @@ -50,8 +59,6 @@ namespace { }; } // namespace -static std::atomic_int currentId; - //inline QString escape(QString str) //{ // return str.replace(":", "::"); @@ -120,19 +127,8 @@ Irc::Irc() this->servers_.erase(server); } }); -} -IrcConnection_ IrcConnection_::unique() -{ - IrcConnection_ c; - c.host = "localhost"; - c.id = currentId++; - c.port = 6667; - c.ssl = false; - c.user = "xD"; - c.nick = "xD"; - c.real = "xD"; - return c; + this->connections.delayedItemsChanged.connect([this] { this->save(); }); } QAbstractTableModel *Irc::newConnectionModel(QObject *parent) @@ -160,4 +156,84 @@ Irc &Irc::getInstance() return irc; } +int Irc::uniqueId() +{ + /// XXX: also check for channels + int i = this->currentId_ + 1; + auto it = this->servers_.find(i); + + while (it != this->servers_.end()) + { + i++; + it = this->servers_.find(i); + } + + return (this->currentId_ = i); +} + +void Irc::save() +{ + QJsonDocument doc; + QJsonObject root; + QJsonArray servers; + + for (auto &&conn : this->connections) + { + QJsonObject obj; + obj.insert("host", conn.host); + obj.insert("port", conn.port); + obj.insert("ssl", conn.ssl); + obj.insert("username", conn.user); + obj.insert("nickname", conn.nick); + obj.insert("realname", conn.real); + //obj.insert("password", conn.password); + obj.insert("id", conn.id); + servers.append(obj); + } + + root.insert("servers", servers); + doc.setObject(root); + + QSaveFile file(configPath()); + file.open(QIODevice::WriteOnly); + file.write(doc.toJson()); + file.commit(); +} + +void Irc::load() +{ + if (this->loaded_) + return; + this->loaded_ = true; + + QString config = configPath(); + QFile file(configPath()); + file.open(QIODevice::ReadOnly); + auto doc = QJsonDocument::fromJson(file.readAll()); + + auto object = doc.object(); + std::unordered_set ids; + + for (auto server : doc.object().value("servers").toArray()) + { + auto obj = server.toObject(); + IrcConnection_ conn; + conn.host = obj.value("host").toString(conn.host); + conn.port = obj.value("port").toInt(conn.port); + conn.ssl = obj.value("ssl").toBool(conn.ssl); + conn.user = obj.value("username").toString(conn.user); + conn.nick = obj.value("nickname").toString(conn.nick); + conn.real = obj.value("realname").toString(conn.real); + // conn.password = obj.value("password").toString(conn.password); + conn.id = obj.value("id").toInt(conn.id); + + // duplicate id's are not allowed :( + if (ids.find(conn.id) == ids.end()) + { + this->connections.appendItem(conn); + ids.insert(conn.id); + } + } +} + } // namespace chatterino diff --git a/src/providers/irc/Irc2.hpp b/src/providers/irc/Irc2.hpp index ec99c8511..ff23e4f68 100644 --- a/src/providers/irc/Irc2.hpp +++ b/src/providers/irc/Irc2.hpp @@ -10,21 +10,21 @@ class QAbstractTableModel; namespace chatterino { +//enum IrcAuthType { Anonymous, /*Sals,*/ Pass, MsgNickServ, NickServ }; + struct IrcConnection_ { QString host; - int port; - bool ssl; + int port = 6667; + bool ssl = false; QString user; QString nick; QString real; + // IrcAuthType authType = Anonymous; QString password; int id; - - // makes an IrcConnection with a unique id - static IrcConnection_ unique(); }; class Irc @@ -39,10 +39,18 @@ public: ChannelPtr getOrAddChannel(int serverId, QString name); + void save(); + void load(); + + int uniqueId(); + signals: void connectionUpdated(int id); private: + int currentId_{}; + bool loaded_{}; + // Servers have a unique id. // When a server gets changed it gets removed and then added again. // So we store the channels of that server in abandonedChannels_ temporarily. diff --git a/src/singletons/Paths.cpp b/src/singletons/Paths.cpp index 0ba547135..eea60a6b2 100644 --- a/src/singletons/Paths.cpp +++ b/src/singletons/Paths.cpp @@ -135,7 +135,7 @@ void Paths::initSubDirectories() this->messageLogDirectory = makePath("Logs"); this->miscDirectory = makePath("Misc"); this->twitchProfileAvatars = makePath("ProfileAvatars"); - QDir().mkdir(this->twitchProfileAvatars + "/twitch"); + //QDir().mkdir(this->twitchProfileAvatars + "/twitch"); } Paths *getPaths() diff --git a/src/widgets/dialogs/IrcConnectionEditor.cpp b/src/widgets/dialogs/IrcConnectionEditor.cpp index 14ba38151..8a2134423 100644 --- a/src/widgets/dialogs/IrcConnectionEditor.cpp +++ b/src/widgets/dialogs/IrcConnectionEditor.cpp @@ -1,16 +1,50 @@ #include "IrcConnectionEditor.hpp" #include "ui_IrcConnectionEditor.h" -IrcConnectionEditor::IrcConnectionEditor(bool isAdd, QWidget *parent) - : QDialog(parent) - , ui(new Ui::IrcConnectionEditor) +namespace chatterino { + +IrcConnectionEditor::IrcConnectionEditor(const IrcConnection_ &data, bool isAdd, + QWidget *parent) + + : QDialog(parent, Qt::WindowStaysOnTopHint) + , ui_(new Ui::IrcConnectionEditor) + , data_(data) { - ui->setupUi(this); + this->ui_->setupUi(this); this->setWindowTitle(QString(isAdd ? "Add " : "Edit ") + "Irc Connection"); -} + + QObject::connect(this->ui_->userNameLineEdit, &QLineEdit::textChanged, this, + [this](const QString &text) { + this->ui_->nickNameLineEdit->setPlaceholderText(text); + this->ui_->realNameLineEdit->setPlaceholderText(text); + }); + + this->ui_->serverLineEdit->setText(data.host); + this->ui_->portSpinBox->setValue(data.port); + this->ui_->securityCheckBox->setChecked(data.ssl); + this->ui_->userNameLineEdit->setText(data.user); + this->ui_->nickNameLineEdit->setText(data.nick); + this->ui_->realNameLineEdit->setText(data.real); + this->ui_->passwordLineEdit->setText(data.password); +} // namespace chatterino IrcConnectionEditor::~IrcConnectionEditor() { - delete ui; + delete ui_; } + +IrcConnection_ IrcConnectionEditor::data() +{ + auto data = this->data_; + data.host = this->ui_->serverLineEdit->text(); + data.port = this->ui_->portSpinBox->value(); + data.ssl = this->ui_->securityCheckBox->isChecked(); + data.user = this->ui_->userNameLineEdit->text(); + data.nick = this->ui_->nickNameLineEdit->text(); + data.real = this->ui_->realNameLineEdit->text(); + data.password = this->ui_->passwordLineEdit->text(); + return data; +} + +} // namespace chatterino diff --git a/src/widgets/dialogs/IrcConnectionEditor.hpp b/src/widgets/dialogs/IrcConnectionEditor.hpp index fc700ecfc..8ec7574da 100644 --- a/src/widgets/dialogs/IrcConnectionEditor.hpp +++ b/src/widgets/dialogs/IrcConnectionEditor.hpp @@ -1,19 +1,32 @@ #pragma once -#include +#include + +#include "providers/irc/Irc2.hpp" +#include "widgets/BaseWindow.hpp" namespace Ui { class IrcConnectionEditor; } +namespace chatterino { + +struct IrcConnection_; + class IrcConnectionEditor : public QDialog { Q_OBJECT public: - explicit IrcConnectionEditor(bool isAdd = false, QWidget *parent = nullptr); + explicit IrcConnectionEditor(const IrcConnection_ &data, bool isAdd = false, + QWidget *parent = nullptr); ~IrcConnectionEditor(); + IrcConnection_ data(); + private: - Ui::IrcConnectionEditor *ui; + Ui::IrcConnectionEditor *ui_; + IrcConnection_ data_; }; + +} // namespace chatterino diff --git a/src/widgets/dialogs/IrcConnectionEditor.ui b/src/widgets/dialogs/IrcConnectionEditor.ui index a7fcc3ef5..53e8faab0 100644 --- a/src/widgets/dialogs/IrcConnectionEditor.ui +++ b/src/widgets/dialogs/IrcConnectionEditor.ui @@ -1,13 +1,13 @@ IrcConnectionEditor - + 0 0 264 - 258 + 350 @@ -19,12 +19,16 @@ - Server: + Host: - + + + irc.example.com + + @@ -94,21 +98,78 @@ + + + Real Name: + + + + + + + + + + Login method: + + + + Password: - + QLineEdit::Password + + + + Qt::Vertical + + + + 20 + 20 + + + + + + + + + Anonymous + + + + + PASS + + + + + + + + Qt::Vertical + + + + 20 + 20 + + + + @@ -121,6 +182,16 @@ + + serverLineEdit + portSpinBox + securityCheckBox + userNameLineEdit + nickNameLineEdit + realNameLineEdit + comboBox + passwordLineEdit + @@ -130,8 +201,8 @@ accept() - 248 - 254 + 254 + 248 157 @@ -146,8 +217,8 @@ reject() - 316 - 260 + 254 + 248 286 diff --git a/src/widgets/dialogs/IrcConnectionPopup.cpp b/src/widgets/dialogs/IrcConnectionPopup.cpp index c06827299..0386c74df 100644 --- a/src/widgets/dialogs/IrcConnectionPopup.cpp +++ b/src/widgets/dialogs/IrcConnectionPopup.cpp @@ -26,7 +26,9 @@ IrcConnectionPopup::IrcConnectionPopup(QWidget *parent) this->setScaleIndependantSize(800, 500); view->addButtonPressed.connect([] { - Irc::getInstance().connections.appendItem(IrcConnection_::unique()); + auto unique = IrcConnection_{}; + unique.id = Irc::getInstance().uniqueId(); + Irc::getInstance().connections.appendItem(unique); }); // init layout diff --git a/src/widgets/dialogs/SelectChannelDialog.cpp b/src/widgets/dialogs/SelectChannelDialog.cpp index 6a8f027f4..5126355a1 100644 --- a/src/widgets/dialogs/SelectChannelDialog.cpp +++ b/src/widgets/dialogs/SelectChannelDialog.cpp @@ -138,34 +138,52 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) auto view = this->ui_.irc.servers = new EditableModelView( Irc::getInstance().newConnectionModel(this)); - view->setTitles( - {"real", "port", "ssl", "user", "nick", "real", "password"}); + view->setTitles({"host", "port", "ssl", "user", "nick", "real", + "password", "login command"}); view->getTableView()->horizontalHeader()->resizeSection(0, 140); - view->getTableView()->horizontalHeader()->resizeSection(1, 30); + view->getTableView()->horizontalHeader()->resizeSection(1, 40); view->getTableView()->horizontalHeader()->resizeSection(2, 30); + //view->getTableView()->horizontalHeader()->setVisible(false); + + view->getTableView()->horizontalHeader()->setSectionHidden(1, true); + view->getTableView()->horizontalHeader()->setSectionHidden(2, true); + view->getTableView()->horizontalHeader()->setSectionHidden(6, true); + view->getTableView()->horizontalHeader()->setSectionHidden(7, true); view->addButtonPressed.connect([] { - Irc::getInstance().connections.appendItem( - IrcConnection_::unique()); + auto unique = IrcConnection_{}; + unique.id = Irc::getInstance().uniqueId(); + Irc::getInstance().connections.appendItem(unique); }); + QObject::connect( + view->getTableView(), &QTableView::clicked, + [](const QModelIndex &index) { + auto data = + Irc::getInstance() + .connections.getVector()[size_t(index.row())]; + + auto editor = new IrcConnectionEditor(data); + if (editor->exec() == QDialog::Accepted) + { + auto data = editor->data(); + auto &&conns = + Irc::getInstance().connections.getVector(); + int i = 0; + for (auto &&conn : conns) + { + if (conn.id == data.id) + { + Irc::getInstance().connections.removeItem(i); + Irc::getInstance().connections.insertItem(data, + i); + } + i++; + } + } + }); + outerBox->addWidget(view); - - // auto box = outerBox.emplace().withoutMargin(); - - // auto conns = box.emplace(); - // conns->addActions({new QAction("hackint")}); - - // auto buttons = box.emplace().withoutMargin(); - - // buttons.emplace("Add").onClick(this, [this]() { - // (new IrcConnectionPopup(this)) - // ->show(); // XXX: don't show multiple - // }); - - // buttons.emplace("Edit"); - // buttons.emplace("Remove"); - // buttons->addStretch(1); } { @@ -174,24 +192,6 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) this->ui_.irc.channel = box.emplace().getElement(); } - // auto vbox = obj.setLayoutType(); - // auto form = vbox.emplace(); - - // auto servers = new QComboBox; - // auto accounts = new QComboBox; - //servers->setEditable(true); - //servers->addItems( - // {"irc://irc.hackint.org:6697", "irc://irc.somethingelse.com:6667"}); - - // form->addRow("Server:", servers); - // form->addRow("Account:", accounts); - // form->addRow("Channel:", new QLineEdit()); - - // form->addRow("User name:", new QLineEdit()); - // form->addRow("First nick choice:", new QLineEdit()); - // form->addRow("Second nick choice:", new QLineEdit()); - // form->addRow("Third nick choice:", new QLineEdit()); - auto tab = notebook->addPage(obj.getElement()); tab->setCustomTitle("Irc"); } From 2a8c5e654f985d5966d221be42f29bc4e4a436bf Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 11 Sep 2019 00:10:49 +0200 Subject: [PATCH 15/41] saving irc splits now --- src/providers/irc/Irc2.cpp | 5 +++-- src/singletons/WindowManager.cpp | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/providers/irc/Irc2.cpp b/src/providers/irc/Irc2.cpp index d2857b433..a0f9378ab 100644 --- a/src/providers/irc/Irc2.cpp +++ b/src/providers/irc/Irc2.cpp @@ -158,14 +158,15 @@ Irc &Irc::getInstance() int Irc::uniqueId() { - /// XXX: also check for channels int i = this->currentId_ + 1; auto it = this->servers_.find(i); + auto it2 = this->abandonedChannels_.find(i); - while (it != this->servers_.end()) + while (it != this->servers_.end() || it2 != this->abandonedChannels_.end()) { i++; it = this->servers_.find(i); + it2 = this->abandonedChannels_.find(i); } return (this->currentId_ = i); diff --git a/src/singletons/WindowManager.cpp b/src/singletons/WindowManager.cpp index bef99f77b..4adb08e74 100644 --- a/src/singletons/WindowManager.cpp +++ b/src/singletons/WindowManager.cpp @@ -4,6 +4,9 @@ #include "debug/AssertInGuiThread.hpp" #include "debug/Log.hpp" #include "messages/MessageElement.hpp" +#include "providers/irc/Irc2.hpp" +#include "providers/irc/IrcChannel2.hpp" +#include "providers/irc/IrcServer.hpp" #include "providers/twitch/TwitchServer.hpp" #include "singletons/Fonts.hpp" #include "singletons/Paths.hpp" @@ -612,6 +615,17 @@ void WindowManager::encodeChannel(IndirectChannel channel, QJsonObject &obj) obj.insert("type", "whispers"); } break; + case Channel::Type::Irc: + { + if (auto ircChannel = + dynamic_cast(channel.get().get())) + { + obj.insert("type", "irc"); + obj.insert("server", ircChannel->server()->id()); + obj.insert("channel", ircChannel->getName()); + } + } + break; } } @@ -639,6 +653,11 @@ IndirectChannel WindowManager::decodeChannel(const QJsonObject &obj) { return app->twitch.server->whispersChannel; } + else if (type == "irc") + { + return Irc::getInstance().getOrAddChannel( + obj.value("server").toInt(-1), obj.value("channel").toString()); + } return Channel::getEmpty(); } From 2f39f4246c9d56bc52e772fee4142e4cc51792a1 Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 11 Sep 2019 13:12:08 +0200 Subject: [PATCH 16/41] fixed warning + added more checks to SignalVectorModel --- src/common/SignalVectorModel.hpp | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/common/SignalVectorModel.hpp b/src/common/SignalVectorModel.hpp index 73e64d099..8fa3fec8c 100644 --- a/src/common/SignalVectorModel.hpp +++ b/src/common/SignalVectorModel.hpp @@ -102,19 +102,26 @@ public: int rowCount(const QModelIndex &parent) const override { + (void)parent; + return this->rows_.size(); } int columnCount(const QModelIndex &parent) const override { + (void)parent; + return this->columnCount_; } QVariant data(const QModelIndex &index, int role) const override { int row = index.row(), column = index.column(); - assert(row >= 0 && row < this->rows_.size() && column >= 0 && - column < this->columnCount_); + if (row < 0 || column < 0 || row >= this->rows_.size() || + column >= this->columnCount_) + { + return QVariant(); + } return rows_[row].items[column]->data(role); } @@ -123,8 +130,11 @@ public: int role) override { int row = index.row(), column = index.column(); - assert(row >= 0 && row < this->rows_.size() && column >= 0 && - column < this->columnCount_); + if (row < 0 || column < 0 || row >= this->rows_.size() || + column >= this->columnCount_) + { + return false; + } Row &rowItem = this->rows_[row]; @@ -186,7 +196,9 @@ public: { int row = index.row(), column = index.column(); - if (row < 0 || column < 0) { + if (row < 0 || column < 0 || row >= this->rows_.size() || + column >= this->columnCount_) + { return Qt::NoItemFlags; } @@ -212,6 +224,8 @@ public: bool removeRows(int row, int count, const QModelIndex &parent) override { + (void)parent; + if (count != 1) { return false; @@ -242,18 +256,22 @@ protected: std::vector &row, int proposedIndex) { + (void)item, (void)row; + return proposedIndex; } virtual void afterRemoved(const TVectorItem &item, std::vector &row, int index) { + (void)item, (void)row, (void)index; } virtual void customRowSetData(const std::vector &row, int column, const QVariant &value, int role, int rowIndex) { + (void)row, (void)column, (void)value, (void)role, (void)rowIndex; } void insertCustomRow(std::vector row, int index) From b20fdc0da6a1a5be5284f693eb11b18a42aa1aec Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 11 Sep 2019 13:17:36 +0200 Subject: [PATCH 17/41] smol irc fixes --- src/providers/irc/AbstractIrcServer.cpp | 26 ++++++++------- src/providers/irc/AbstractIrcServer.hpp | 2 ++ src/providers/irc/IrcServer.cpp | 22 +++++++++++-- src/providers/irc/IrcServer.hpp | 2 ++ src/singletons/Settings.hpp | 4 +++ src/widgets/dialogs/IrcConnectionEditor.ui | 2 +- src/widgets/dialogs/SelectChannelDialog.cpp | 36 ++++++++++++++++----- 7 files changed, 72 insertions(+), 22 deletions(-) diff --git a/src/providers/irc/AbstractIrcServer.cpp b/src/providers/irc/AbstractIrcServer.cpp index 8aab88df8..fa8de283b 100644 --- a/src/providers/irc/AbstractIrcServer.cpp +++ b/src/providers/irc/AbstractIrcServer.cpp @@ -164,26 +164,24 @@ ChannelPtr AbstractIrcServer::getOrAddChannel(const QString &dirtyChannelName) return Channel::getEmpty(); } - QString clojuresInCppAreShit = channelName; - this->channels.insert(channelName, chan); - chan->destroyed.connect([this, clojuresInCppAreShit] { + this->connections_.emplace_back(chan->destroyed.connect([this, + channelName] { // fourtf: issues when the server itself is destroyed - log("[AbstractIrcServer::addChannel] {} was destroyed", - clojuresInCppAreShit); - this->channels.remove(clojuresInCppAreShit); + log("[AbstractIrcServer::addChannel] {} was destroyed", channelName); + this->channels.remove(channelName); if (this->readConnection_) { - this->readConnection_->sendRaw("PART #" + clojuresInCppAreShit); + this->readConnection_->sendRaw("PART #" + channelName); } if (this->writeConnection_ && this->hasSeparateWriteConnection()) { - this->writeConnection_->sendRaw("PART #" + clojuresInCppAreShit); + this->writeConnection_->sendRaw("PART #" + channelName); } - }); + })); // join irc channel { @@ -191,12 +189,18 @@ ChannelPtr AbstractIrcServer::getOrAddChannel(const QString &dirtyChannelName) if (this->readConnection_) { - this->readConnection_->sendRaw("JOIN #" + channelName); + if (this->readConnection_->isConnected()) + { + this->readConnection_->sendRaw("JOIN #" + channelName); + } } if (this->writeConnection_ && this->hasSeparateWriteConnection()) { - this->writeConnection_->sendRaw("JOIN #" + channelName); + if (this->readConnection_->isConnected()) + { + this->writeConnection_->sendRaw("JOIN #" + channelName); + } } } diff --git a/src/providers/irc/AbstractIrcServer.hpp b/src/providers/irc/AbstractIrcServer.hpp index c02b8bd4e..23f22a407 100644 --- a/src/providers/irc/AbstractIrcServer.hpp +++ b/src/providers/irc/AbstractIrcServer.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -84,6 +85,7 @@ private: std::mutex connectionMutex_; // bool autoReconnect_ = false; + pajlada::Signals::SignalHolder connections_; }; } // namespace chatterino diff --git a/src/providers/irc/IrcServer.cpp b/src/providers/irc/IrcServer.cpp index 832c67f50..077437a4b 100644 --- a/src/providers/irc/IrcServer.cpp +++ b/src/providers/irc/IrcServer.cpp @@ -57,8 +57,10 @@ void IrcServer::initializeConnection(IrcConnection *connection, bool isRead, connection->setPort(this->data_->port); connection->setUserName(this->data_->user); - connection->setNickName(this->data_->nick); - connection->setRealName(this->data_->real); + connection->setNickName(this->data_->nick.isEmpty() ? this->data_->user + : this->data_->nick); + connection->setRealName(this->data_->real.isEmpty() ? this->data_->user + : this->data_->nick); connection->setPassword(this->data_->password); } @@ -72,6 +74,21 @@ bool IrcServer::hasSeparateWriteConnection() const return false; } +void IrcServer::onReadConnected(IrcConnection *connection) +{ + AbstractIrcServer::onReadConnected(connection); + + std::lock_guard lock(this->channelMutex); + + for (auto &&weak : this->channels) + { + if (auto channel = weak.lock()) + { + connection->sendRaw("JOIN #" + channel->getName()); + } + } +} + void IrcServer::privateMessageReceived(Communi::IrcPrivateMessage *message) { auto target = message->target(); @@ -81,6 +98,7 @@ void IrcServer::privateMessageReceived(Communi::IrcPrivateMessage *message) { MessageBuilder builder; + builder.emplace(); builder.emplace(message->nick() + ":", MessageElementFlag::Username); builder.emplace(message->content(), diff --git a/src/providers/irc/IrcServer.hpp b/src/providers/irc/IrcServer.hpp index 9a7310a57..8cac05f3f 100644 --- a/src/providers/irc/IrcServer.hpp +++ b/src/providers/irc/IrcServer.hpp @@ -25,6 +25,8 @@ protected: bool isWrite) override; std::shared_ptr createChannel(const QString &channelName) override; bool hasSeparateWriteConnection() const override; + + void onReadConnected(IrcConnection *connection) override; void privateMessageReceived(Communi::IrcPrivateMessage *message) override; void readConnectionMessageReceived(Communi::IrcMessage *message) override; diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index 2ab20d06d..1d0d87be1 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -207,6 +207,10 @@ public: QStringSetting cachePath = {"/cache/path", ""}; + /// UI + IntSetting lastSelectChannelTab = {"/ui/lastSelectChannelTab", 0}; + IntSetting lastSelectIrcConn = {"/ui/lastSelectIrcConn", 0}; + private: void updateModerationActions(); }; diff --git a/src/widgets/dialogs/IrcConnectionEditor.ui b/src/widgets/dialogs/IrcConnectionEditor.ui index 53e8faab0..7fc356943 100644 --- a/src/widgets/dialogs/IrcConnectionEditor.ui +++ b/src/widgets/dialogs/IrcConnectionEditor.ui @@ -1,7 +1,7 @@ IrcConnectionEditor - + 0 diff --git a/src/widgets/dialogs/SelectChannelDialog.cpp b/src/widgets/dialogs/SelectChannelDialog.cpp index 5126355a1..59fc9c77c 100644 --- a/src/widgets/dialogs/SelectChannelDialog.cpp +++ b/src/widgets/dialogs/SelectChannelDialog.cpp @@ -147,23 +147,29 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) view->getTableView()->horizontalHeader()->setSectionHidden(1, true); view->getTableView()->horizontalHeader()->setSectionHidden(2, true); + view->getTableView()->horizontalHeader()->setSectionHidden(4, true); + view->getTableView()->horizontalHeader()->setSectionHidden(5, true); view->getTableView()->horizontalHeader()->setSectionHidden(6, true); view->getTableView()->horizontalHeader()->setSectionHidden(7, true); view->addButtonPressed.connect([] { auto unique = IrcConnection_{}; unique.id = Irc::getInstance().uniqueId(); - Irc::getInstance().connections.appendItem(unique); + + auto editor = new IrcConnectionEditor(unique); + if (editor->exec() == QDialog::Accepted) + { + Irc::getInstance().connections.appendItem(unique); + } }); QObject::connect( - view->getTableView(), &QTableView::clicked, + view->getTableView(), &QTableView::doubleClicked, [](const QModelIndex &index) { - auto data = + auto editor = new IrcConnectionEditor( Irc::getInstance() - .connections.getVector()[size_t(index.row())]; + .connections.getVector()[size_t(index.row())]); - auto editor = new IrcConnectionEditor(data); if (editor->exec() == QDialog::Accepted) { auto data = editor->data(); @@ -219,10 +225,24 @@ SelectChannelDialog::SelectChannelDialog(QWidget *parent) auto *shortcut_cancel = new QShortcut(QKeySequence("Esc"), this); QObject::connect(shortcut_cancel, &QShortcut::activated, [=] { this->close(); }); + + // restore ui state + this->ui_.notebook->selectIndex(getSettings()->lastSelectChannelTab); + this->ui_.irc.servers->getTableView()->selectRow( + getSettings()->lastSelectIrcConn); } void SelectChannelDialog::ok() { + // save ui state + getSettings()->lastSelectChannelTab = + this->ui_.notebook->getSelectedIndex(); + getSettings()->lastSelectIrcConn = this->ui_.irc.servers->getTableView() + ->selectionModel() + ->currentIndex() + .row(); + + // accept and close this->hasSelectedChannel_ = true; this->close(); } @@ -339,14 +359,14 @@ IndirectChannel SelectChannelDialog::getSelectedChannel() const if (row >= 0 && row < int(vector.size())) { return Irc::getInstance().getOrAddChannel( - vector[row].id, this->ui_.irc.channel->text()); + vector[size_t(row)].id, this->ui_.irc.channel->text()); } else { return Channel::getEmpty(); } } - break; + //break; } return this->selectedChannel_; @@ -360,7 +380,7 @@ bool SelectChannelDialog::hasSeletedChannel() const bool SelectChannelDialog::EventFilter::eventFilter(QObject *watched, QEvent *event) { - auto *widget = (QWidget *)watched; + auto *widget = static_cast(watched); if (event->type() == QEvent::FocusIn) { From 158564d0c2d655a676e3d668a4429bcc3bb837f5 Mon Sep 17 00:00:00 2001 From: fourtf Date: Wed, 11 Sep 2019 13:34:59 +0200 Subject: [PATCH 18/41] disabled twitch dropdown items for irc chats --- src/widgets/splits/SplitHeader.cpp | 69 ++++++++++++++++++------------ 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/src/widgets/splits/SplitHeader.cpp b/src/widgets/splits/SplitHeader.cpp index 5d3a642f4..c451f2ead 100644 --- a/src/widgets/splits/SplitHeader.cpp +++ b/src/widgets/splits/SplitHeader.cpp @@ -211,9 +211,12 @@ void SplitHeader::initializeLayout() }), // dropdown this->dropdownButton_ = makeWidget