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