modernize: Remove old-style slot usage in NetworkModelController
[quassel.git] / src / uisupport / networkmodelcontroller.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #include "networkmodelcontroller.h"
22
23 #include <QComboBox>
24 #include <QDialogButtonBox>
25 #include <QGridLayout>
26 #include <QIcon>
27 #include <QLabel>
28 #include <QLineEdit>
29 #include <QInputDialog>
30 #include <QMessageBox>
31 #include <QPushButton>
32
33 #include "buffermodel.h"
34 #include "buffersettings.h"
35 #include "client.h"
36 #include "clientidentity.h"
37 #include "clientignorelistmanager.h"
38 #include "icon.h"
39 #include "network.h"
40 #include "util.h"
41
42 NetworkModelController::NetworkModelController(QObject *parent)
43     : QObject(parent),
44     _actionCollection(new ActionCollection(this))
45 {
46     connect(_actionCollection, &ActionCollection::actionTriggered, this, &NetworkModelController::actionTriggered);
47 }
48
49
50 Action *NetworkModelController::registerAction(ActionType type, const QString &text, bool checkable)
51 {
52     return registerAction(type, QPixmap(), text, checkable);
53 }
54
55
56 Action *NetworkModelController::registerAction(ActionType type, const QIcon &icon, const QString &text, bool checkable)
57 {
58     Action *act;
59     if (icon.isNull())
60         act = new Action(text, this);
61     else
62         act = new Action(icon, text, this);
63
64     act->setCheckable(checkable);
65     act->setData(type);
66
67     _actionCollection->addAction(QString::number(type, 16), act);
68     _actionByType[type] = act;
69     return act;
70 }
71
72
73 /******** Helper Functions ***********************************************************************/
74
75 void NetworkModelController::setIndexList(const QModelIndex &index)
76 {
77     _indexList = QList<QModelIndex>() << index;
78 }
79
80
81 void NetworkModelController::setIndexList(const QList<QModelIndex> &list)
82 {
83     _indexList = list;
84 }
85
86
87 void NetworkModelController::setMessageFilter(MessageFilter *filter)
88 {
89     _messageFilter = filter;
90 }
91
92
93 void NetworkModelController::setContextItem(const QString &contextItem)
94 {
95     _contextItem = contextItem;
96 }
97
98
99 void NetworkModelController::setSlot(ActionSlot slot)
100 {
101     _actionSlot = std::move(slot);
102 }
103
104
105 bool NetworkModelController::checkRequirements(const QModelIndex &index, ItemActiveStates requiredActiveState)
106 {
107     if (!index.isValid())
108         return false;
109
110     ItemActiveStates isActive = index.data(NetworkModel::ItemActiveRole).toBool()
111                                 ? ActiveState
112                                 : InactiveState;
113
114     if (!(isActive & requiredActiveState))
115         return false;
116
117     return true;
118 }
119
120
121 QString NetworkModelController::nickName(const QModelIndex &index) const
122 {
123     auto *ircUser = qobject_cast<IrcUser *>(index.data(NetworkModel::IrcUserRole).value<QObject *>());
124     if (ircUser)
125         return ircUser->nick();
126
127     BufferInfo bufferInfo = index.data(NetworkModel::BufferInfoRole).value<BufferInfo>();
128     if (!bufferInfo.isValid())
129         return QString();
130     if (bufferInfo.type() != BufferInfo::QueryBuffer)
131         return QString();
132
133     return bufferInfo.bufferName(); // FIXME this might break with merged queries maybe
134 }
135
136
137 BufferId NetworkModelController::findQueryBuffer(const QModelIndex &index, const QString &predefinedNick) const
138 {
139     NetworkId networkId = index.data(NetworkModel::NetworkIdRole).value<NetworkId>();
140     if (!networkId.isValid())
141         return {};
142
143     QString nick = predefinedNick.isEmpty() ? nickName(index) : predefinedNick;
144     if (nick.isEmpty())
145         return {};
146
147     return findQueryBuffer(networkId, nick);
148 }
149
150
151 BufferId NetworkModelController::findQueryBuffer(NetworkId networkId, const QString &nick) const
152 {
153     return Client::networkModel()->bufferId(networkId, nick);
154 }
155
156
157 void NetworkModelController::removeBuffers(const QModelIndexList &indexList)
158 {
159     QList<BufferInfo> inactive;
160     foreach(QModelIndex index, indexList) {
161         BufferInfo info = index.data(NetworkModel::BufferInfoRole).value<BufferInfo>();
162         if (info.isValid()) {
163             if (info.type() == BufferInfo::QueryBuffer
164                 || (info.type() == BufferInfo::ChannelBuffer && !index.data(NetworkModel::ItemActiveRole).toBool()))
165                 inactive << info;
166         }
167     }
168     QString msg;
169     if (inactive.count()) {
170         msg = tr("Do you want to delete the following buffer(s) permanently?", "", inactive.count());
171         msg += "<ul>";
172         int count = 0;
173         foreach(BufferInfo info, inactive) {
174             if (count < 10) {
175                 msg += QString("<li>%1</li>").arg(info.bufferName());
176                 count++;
177             }
178             else
179                 break;
180         }
181         msg += "</ul>";
182         if (count > 9 && inactive.size() - count != 0)
183             msg += tr("...and <b>%1</b> more<br><br>").arg(inactive.size() - count);
184         msg += tr("<b>Note:</b> This will delete all related data, including all backlog data, from the core's database and cannot be undone.");
185         if (inactive.count() != indexList.count())
186             msg += tr("<br>Active channel buffers cannot be deleted, please part the channel first.");
187
188         if (QMessageBox::question(nullptr, tr("Remove buffers permanently?"), msg, QMessageBox::Yes|QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
189             foreach(BufferInfo info, inactive)
190             Client::removeBuffer(info.bufferId());
191         }
192     }
193 }
194
195
196 void NetworkModelController::handleExternalAction(ActionType type, QAction *action)
197 {
198     Q_UNUSED(type);
199     if (_actionSlot) {
200         _actionSlot(action);
201     }
202 }
203
204
205 /******** Handle Actions *************************************************************************/
206
207 void NetworkModelController::actionTriggered(QAction *action)
208 {
209     ActionType type = (ActionType)action->data().toInt();
210     if (type > 0) {
211         if (type & NetworkMask)
212             handleNetworkAction(type, action);
213         else if (type & BufferMask)
214             handleBufferAction(type, action);
215         else if (type & HideMask)
216             handleHideAction(type, action);
217         else if (type & GeneralMask)
218             handleGeneralAction(type, action);
219         else if (type & NickMask)
220             handleNickAction(type, action);
221         else if (type & ExternalMask)
222             handleExternalAction(type, action);
223         else
224             qWarning() << "NetworkModelController::actionTriggered(): Unhandled action!";
225     }
226 }
227
228
229 void NetworkModelController::handleNetworkAction(ActionType type, QAction *)
230 {
231     if (type == NetworkConnectAllWithDropdown || type == NetworkDisconnectAllWithDropdown || type == NetworkConnectAll || type == NetworkDisconnectAll) {
232         if (type == NetworkConnectAllWithDropdown && QMessageBox::question(nullptr, tr("Question"), tr("Really Connect to all IRC Networks?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::No)
233             return;
234         if (type == NetworkDisconnectAllWithDropdown && QMessageBox::question(nullptr, tr("Question"), tr("Really disconnect from all IRC Networks?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No)
235             return;
236         foreach(NetworkId id, Client::networkIds()) {
237             const Network *net = Client::network(id);
238             if ((type == NetworkConnectAllWithDropdown || type == NetworkConnectAll) && net->connectionState() == Network::Disconnected)
239                 net->requestConnect();
240             if ((type == NetworkDisconnectAllWithDropdown || type == NetworkDisconnectAll) && net->connectionState() != Network::Disconnected)
241                 net->requestDisconnect();
242         }
243         return;
244     }
245
246     if (!indexList().count())
247         return;
248
249     const Network *network = Client::network(indexList().at(0).data(NetworkModel::NetworkIdRole).value<NetworkId>());
250     Q_CHECK_PTR(network);
251     if (!network)
252         return;
253
254     switch (type) {
255     case NetworkConnect:
256         network->requestConnect();
257         break;
258     case NetworkDisconnect:
259         network->requestDisconnect();
260         break;
261     default:
262         break;
263     }
264 }
265
266
267 void NetworkModelController::handleBufferAction(ActionType type, QAction *)
268 {
269     if (type == BufferRemove) {
270         removeBuffers(indexList());
271     }
272     else {
273         QList<BufferInfo> bufferList; // create temp list because model indexes might change
274         foreach(QModelIndex index, indexList()) {
275             BufferInfo bufferInfo = index.data(NetworkModel::BufferInfoRole).value<BufferInfo>();
276             if (bufferInfo.isValid())
277                 bufferList << bufferInfo;
278         }
279
280         foreach(BufferInfo bufferInfo, bufferList) {
281             switch (type) {
282             case BufferJoin:
283                 Client::userInput(bufferInfo, QString("/JOIN %1").arg(bufferInfo.bufferName()));
284                 break;
285             case BufferPart:
286             {
287                 QString reason = Client::identity(Client::network(bufferInfo.networkId())->identity())->partReason();
288                 Client::userInput(bufferInfo, QString("/PART %1").arg(reason));
289                 break;
290             }
291             case BufferSwitchTo:
292                 Client::bufferModel()->switchToBuffer(bufferInfo.bufferId());
293                 break;
294             default:
295                 break;
296             }
297         }
298     }
299 }
300
301
302 void NetworkModelController::handleHideAction(ActionType type, QAction *action)
303 {
304     Q_UNUSED(action)
305
306     if (type == HideJoinPartQuit) {
307         bool anyChecked = NetworkModelController::action(HideJoin)->isChecked();
308         anyChecked |= NetworkModelController::action(HidePart)->isChecked();
309         anyChecked |= NetworkModelController::action(HideQuit)->isChecked();
310
311         // If any are checked, uncheck them all.
312         // If none are checked, check them all.
313         bool newCheckedState = !anyChecked;
314         NetworkModelController::action(HideJoin)->setChecked(newCheckedState);
315         NetworkModelController::action(HidePart)->setChecked(newCheckedState);
316         NetworkModelController::action(HideQuit)->setChecked(newCheckedState);
317     }
318
319     int filter = 0;
320     if (NetworkModelController::action(HideJoin)->isChecked())
321         filter |= Message::Join | Message::NetsplitJoin;
322     if (NetworkModelController::action(HidePart)->isChecked())
323         filter |= Message::Part;
324     if (NetworkModelController::action(HideQuit)->isChecked())
325         filter |= Message::Quit | Message::NetsplitQuit;
326     if (NetworkModelController::action(HideNick)->isChecked())
327         filter |= Message::Nick;
328     if (NetworkModelController::action(HideMode)->isChecked())
329         filter |= Message::Mode;
330     if (NetworkModelController::action(HideDayChange)->isChecked())
331         filter |= Message::DayChange;
332     if (NetworkModelController::action(HideTopic)->isChecked())
333         filter |= Message::Topic;
334
335     switch (type) {
336     case HideJoinPartQuit:
337     case HideJoin:
338     case HidePart:
339     case HideQuit:
340     case HideNick:
341     case HideMode:
342     case HideDayChange:
343     case HideTopic:
344         if (_messageFilter)
345             BufferSettings(_messageFilter->idString()).setMessageFilter(filter);
346         else {
347             foreach(QModelIndex index, _indexList) {
348                 BufferId bufferId = index.data(NetworkModel::BufferIdRole).value<BufferId>();
349                 if (!bufferId.isValid())
350                     continue;
351                 BufferSettings(bufferId).setMessageFilter(filter);
352             }
353         }
354         return;
355     case HideApplyToAll:
356         BufferSettings().setMessageFilter(filter);
357         // fallthrough
358     case HideUseDefaults:
359         if (_messageFilter)
360             BufferSettings(_messageFilter->idString()).removeFilter();
361         else {
362             foreach(QModelIndex index, _indexList) {
363                 BufferId bufferId = index.data(NetworkModel::BufferIdRole).value<BufferId>();
364                 if (!bufferId.isValid())
365                     continue;
366                 BufferSettings(bufferId).removeFilter();
367             }
368         }
369         return;
370     default:
371         return;
372     };
373 }
374
375
376 void NetworkModelController::handleGeneralAction(ActionType type, QAction *action)
377 {
378     Q_UNUSED(action)
379
380     if (!indexList().count())
381         return;
382     NetworkId networkId = indexList().at(0).data(NetworkModel::NetworkIdRole).value<NetworkId>();
383
384     switch (type) {
385     case JoinChannel:
386     {
387         QString channelName = contextItem();
388         QString channelPassword;
389         if (channelName.isEmpty()) {
390             JoinDlg dlg(indexList().first());
391             if (dlg.exec() == QDialog::Accepted) {
392                 channelName = dlg.channelName();
393                 networkId = dlg.networkId();
394                 channelPassword = dlg.channelPassword();
395             }
396         }
397         if (!channelName.isEmpty()) {
398             if (!channelPassword.isEmpty())
399                 Client::instance()->userInput(BufferInfo::fakeStatusBuffer(networkId), QString("/JOIN %1 %2").arg(channelName).arg(channelPassword));
400             else
401                 Client::instance()->userInput(BufferInfo::fakeStatusBuffer(networkId), QString("/JOIN %1").arg(channelName));
402         }
403         break;
404     }
405     case ShowChannelList:
406         if (networkId.isValid()) {
407             // Don't immediately list channels, allowing customization of filter first
408             emit showChannelList(networkId, {}, false);
409         }
410         break;
411     case ShowNetworkConfig:
412         if (networkId.isValid())
413             emit showNetworkConfig(networkId);
414         break;
415     case ShowIgnoreList:
416         if (networkId.isValid())
417             emit showIgnoreList(QString());
418         break;
419     default:
420         break;
421     }
422 }
423
424
425 void NetworkModelController::handleNickAction(ActionType type, QAction *action)
426 {
427     foreach(QModelIndex index, indexList()) {
428         NetworkId networkId = index.data(NetworkModel::NetworkIdRole).value<NetworkId>();
429         if (!networkId.isValid())
430             continue;
431         QString nick = nickName(index);
432         if (nick.isEmpty())
433             continue;
434         BufferInfo bufferInfo = index.data(NetworkModel::BufferInfoRole).value<BufferInfo>();
435         if (!bufferInfo.isValid())
436             continue;
437
438         switch (type) {
439         case NickWhois:
440             Client::userInput(bufferInfo, QString("/WHOIS %1 %1").arg(nick));
441             break;
442         case NickCtcpVersion:
443             Client::userInput(bufferInfo, QString("/CTCP %1 VERSION").arg(nick));
444             break;
445         case NickCtcpPing:
446             Client::userInput(bufferInfo, QString("/CTCP %1 PING").arg(nick));
447             break;
448         case NickCtcpTime:
449             Client::userInput(bufferInfo, QString("/CTCP %1 TIME").arg(nick));
450             break;
451         case NickCtcpClientinfo:
452             Client::userInput(bufferInfo, QString("/CTCP %1 CLIENTINFO").arg(nick));
453             break;
454         case NickOp:
455             Client::userInput(bufferInfo, QString("/OP %1").arg(nick));
456             break;
457         case NickDeop:
458             Client::userInput(bufferInfo, QString("/DEOP %1").arg(nick));
459             break;
460         case NickHalfop:
461             Client::userInput(bufferInfo, QString("/HALFOP %1").arg(nick));
462             break;
463         case NickDehalfop:
464             Client::userInput(bufferInfo, QString("/DEHALFOP %1").arg(nick));
465             break;
466         case NickVoice:
467             Client::userInput(bufferInfo, QString("/VOICE %1").arg(nick));
468             break;
469         case NickDevoice:
470             Client::userInput(bufferInfo, QString("/DEVOICE %1").arg(nick));
471             break;
472         case NickKick:
473             Client::userInput(bufferInfo, QString("/KICK %1").arg(nick));
474             break;
475         case NickBan:
476             Client::userInput(bufferInfo, QString("/BAN %1").arg(nick));
477             break;
478         case NickKickBan:
479             Client::userInput(bufferInfo, QString("/BAN %1").arg(nick));
480             Client::userInput(bufferInfo, QString("/KICK %1").arg(nick));
481             break;
482         case NickSwitchTo:
483         case NickQuery:
484             Client::bufferModel()->switchToOrStartQuery(networkId, nick);
485             break;
486         case NickIgnoreUser:
487         {
488             auto *ircUser = qobject_cast<IrcUser *>(index.data(NetworkModel::IrcUserRole).value<QObject *>());
489             if (!ircUser)
490                 break;
491             Client::ignoreListManager()->requestAddIgnoreListItem(IgnoreListManager::SenderIgnore,
492                 action->property("ignoreRule").toString(),
493                 false, IgnoreListManager::SoftStrictness,
494                 IgnoreListManager::NetworkScope,
495                 ircUser->network()->networkName(), true);
496             break;
497         }
498         case NickIgnoreHost:
499         {
500             auto *ircUser = qobject_cast<IrcUser *>(index.data(NetworkModel::IrcUserRole).value<QObject *>());
501             if (!ircUser)
502                 break;
503             Client::ignoreListManager()->requestAddIgnoreListItem(IgnoreListManager::SenderIgnore,
504                 action->property("ignoreRule").toString(),
505                 false, IgnoreListManager::SoftStrictness,
506                 IgnoreListManager::NetworkScope,
507                 ircUser->network()->networkName(), true);
508             break;
509         }
510         case NickIgnoreDomain:
511         {
512             auto *ircUser = qobject_cast<IrcUser *>(index.data(NetworkModel::IrcUserRole).value<QObject *>());
513             if (!ircUser)
514                 break;
515             Client::ignoreListManager()->requestAddIgnoreListItem(IgnoreListManager::SenderIgnore,
516                 action->property("ignoreRule").toString(),
517                 false, IgnoreListManager::SoftStrictness,
518                 IgnoreListManager::NetworkScope,
519                 ircUser->network()->networkName(), true);
520             break;
521         }
522         case NickIgnoreCustom:
523             // forward that to mainwin since we can access the settingspage only from there
524             emit showIgnoreList(action->property("ignoreRule").toString());
525             break;
526         case NickIgnoreToggleEnabled0:
527         case NickIgnoreToggleEnabled1:
528         case NickIgnoreToggleEnabled2:
529         case NickIgnoreToggleEnabled3:
530         case NickIgnoreToggleEnabled4:
531             Client::ignoreListManager()->requestToggleIgnoreRule(action->property("ignoreRule").toString());
532             break;
533         default:
534             qWarning() << "Unhandled nick action";
535         }
536     }
537 }
538
539
540 /***************************************************************************************************************
541  * JoinDlg
542  ***************************************************************************************************************/
543
544 NetworkModelController::JoinDlg::JoinDlg(const QModelIndex &index, QWidget *parent) : QDialog(parent)
545 {
546     setWindowIcon(icon::get("irc-join-channel"));
547     setWindowTitle(tr("Join Channel"));
548
549     auto *layout = new QGridLayout(this);
550     layout->addWidget(new QLabel(tr("Network:")), 0, 0);
551     layout->addWidget(networks = new QComboBox, 0, 1);
552     layout->addWidget(new QLabel(tr("Channel:")), 1, 0);
553     layout->addWidget(channel = new QLineEdit, 1, 1);
554     layout->addWidget(new QLabel(tr("Password:")), 2, 0);
555     layout->addWidget(password = new QLineEdit, 2, 1);
556     layout->addWidget(buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel), 3, 0, 1, 2);
557     setLayout(layout);
558
559     channel->setFocus();
560     buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
561     networks->setInsertPolicy(QComboBox::InsertAlphabetically);
562     password->setEchoMode(QLineEdit::Password);
563
564     connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
565     connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
566     connect(channel, &QLineEdit::textChanged, this, &JoinDlg::on_channel_textChanged);
567
568     foreach(NetworkId id, Client::networkIds()) {
569         const Network *net = Client::network(id);
570         if (net->isConnected()) {
571             networks->addItem(net->networkName(), QVariant::fromValue<NetworkId>(id));
572         }
573     }
574
575     if (index.isValid()) {
576         NetworkId networkId = index.data(NetworkModel::NetworkIdRole).value<NetworkId>();
577         if (networkId.isValid()) {
578             networks->setCurrentIndex(networks->findText(Client::network(networkId)->networkName()));
579             if (index.data(NetworkModel::BufferTypeRole) == BufferInfo::ChannelBuffer
580                 && !index.data(NetworkModel::ItemActiveRole).toBool())
581                 channel->setText(index.data(Qt::DisplayRole).toString());
582         }
583     }
584 }
585
586
587 NetworkId NetworkModelController::JoinDlg::networkId() const
588 {
589     return networks->itemData(networks->currentIndex()).value<NetworkId>();
590 }
591
592
593 QString NetworkModelController::JoinDlg::channelName() const
594 {
595     return channel->text();
596 }
597
598
599 QString NetworkModelController::JoinDlg::channelPassword() const
600 {
601     return password->text();
602 }
603
604
605 void NetworkModelController::JoinDlg::on_channel_textChanged(const QString &text)
606 {
607     buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.isEmpty());
608 }