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 | |
41 | NetworkModelController::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 | |
51 | NetworkModelController::~NetworkModelController() |
52 | { |
53 | } |
54 | |
55 | |
56 | Action *NetworkModelController::registerAction(ActionType type, const QString &text, bool checkable) |
57 | { |
58 | return registerAction(type, QPixmap(), text, checkable); |
59 | } |
60 | |
61 | |
62 | Action *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 | |
81 | void NetworkModelController::setIndexList(const QModelIndex &index) |
82 | { |
83 | _indexList = QList<QModelIndex>() << index; |
84 | } |
85 | |
86 | |
87 | void NetworkModelController::setIndexList(const QList<QModelIndex> &list) |
88 | { |
89 | _indexList = list; |
90 | } |
91 | |
92 | |
93 | void NetworkModelController::setMessageFilter(MessageFilter *filter) |
94 | { |
95 | _messageFilter = filter; |
96 | } |
97 | |
98 | |
99 | void NetworkModelController::setContextItem(const QString &contextItem) |
100 | { |
101 | _contextItem = contextItem; |
102 | } |
103 | |
104 | |
105 | void NetworkModelController::setSlot(QObject *receiver, const char *method) |
106 | { |
107 | _receiver = receiver; |
108 | _method = method; |
109 | } |
110 | |
111 | |
112 | bool 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 | |
128 | QString 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 | |
144 | BufferId 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 | |
158 | BufferId NetworkModelController::findQueryBuffer(NetworkId networkId, const QString &nick) const |
159 | { |
160 | return Client::networkModel()->bufferId(networkId, nick); |
161 | } |
162 | |
163 | |
164 | void 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 | |
203 | void 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 | |
215 | void 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 | |
237 | void 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 | |
271 | void 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 | |
306 | void 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 | |
365 | void 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 | |
408 | void 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 | |
527 | NetworkModelController::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 | |
570 | NetworkId NetworkModelController::JoinDlg::networkId() const |
571 | { |
572 | return networks->itemData(networks->currentIndex()).value<NetworkId>(); |
573 | } |
574 | |
575 | |
576 | QString NetworkModelController::JoinDlg::channelName() const |
577 | { |
578 | return channel->text(); |
579 | } |
580 | |
581 | |
582 | QString NetworkModelController::JoinDlg::channelPassword() const |
583 | { |
584 | return password->text(); |
585 | } |
586 | |
587 | |
588 | void NetworkModelController::JoinDlg::on_channel_textChanged(const QString &text) |
589 | { |
590 | buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.isEmpty()); |
591 | } |
592 | |