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