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