1 /***************************************************************************
2 * Copyright (C) 2005-09 by the Quassel Project *
3 * devel@quassel-irc.org *
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. *
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. *
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 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include <QInputDialog>
23 #include <QMessageBox>
25 #include "networkmodelactionprovider.h"
27 #include "buffermodel.h"
28 #include "buffersettings.h"
29 #include "iconloader.h"
30 #include "clientidentity.h"
34 NetworkModelActionProvider::NetworkModelActionProvider(QObject *parent)
35 : AbstractActionProvider(parent),
36 _actionCollection(new ActionCollection(this)),
40 registerAction(NetworkConnect, SmallIcon("network-connect"), tr("Connect"));
41 registerAction(NetworkDisconnect, SmallIcon("network-disconnect"), tr("Disconnect"));
43 registerAction(BufferJoin, tr("Join"));
44 registerAction(BufferPart, tr("Part"));
45 registerAction(BufferRemove, tr("Delete Buffer(s)..."));
46 registerAction(BufferSwitchTo, tr("Show Buffer"));
48 registerAction(HideJoin, tr("Joins"), true);
49 registerAction(HidePart, tr("Parts"), true);
50 registerAction(HideQuit, tr("Quits"), true);
51 registerAction(HideNick, tr("Nick Changes"), true);
52 registerAction(HideMode, tr("Mode Changes"), true);
53 registerAction(HideDayChange, tr("Day Changes"), true);
54 registerAction(HideApplyToAll, tr("Set as Default..."));
55 registerAction(HideUseDefaults, tr("Use Defaults..."));
57 registerAction(JoinChannel, tr("Join Channel..."));
59 registerAction(NickQuery, tr("Start Query"));
60 registerAction(NickSwitchTo, tr("Show Query"));
61 registerAction(NickWhois, tr("Whois"));
63 registerAction(NickCtcpVersion, tr("Version"));
64 registerAction(NickCtcpTime, tr("Time"));
65 registerAction(NickCtcpPing, tr("Ping"));
66 registerAction(NickCtcpFinger, tr("Finger"));
68 registerAction(NickOp, tr("Give Operator Status"));
69 registerAction(NickDeop, tr("Take Operator Status"));
70 registerAction(NickVoice, tr("Give Voice"));
71 registerAction(NickDevoice, tr("Take Voice"));
72 registerAction(NickKick, tr("Kick From Channel"));
73 registerAction(NickBan, tr("Ban From Channel"));
74 registerAction(NickKickBan, tr("Kick && Ban"));
76 registerAction(HideBufferTemporarily, tr("Hide Buffer(s) Temporarily"));
77 registerAction(HideBufferPermanently, tr("Hide Buffer(s) Permanently"));
78 registerAction(ShowChannelList, SmallIcon("format-list-unordered"), tr("Show Channel List"));
79 registerAction(ShowIgnoreList, tr("Show Ignore List"));
81 connect(_actionCollection, SIGNAL(actionTriggered(QAction *)), SLOT(actionTriggered(QAction *)));
83 QMenu *hideEventsMenu = new QMenu();
84 hideEventsMenu->addAction(action(HideJoin));
85 hideEventsMenu->addAction(action(HidePart));
86 hideEventsMenu->addAction(action(HideQuit));
87 hideEventsMenu->addAction(action(HideNick));
88 hideEventsMenu->addAction(action(HideMode));
89 hideEventsMenu->addAction(action(HideDayChange));
90 hideEventsMenu->addSeparator();
91 hideEventsMenu->addAction(action(HideApplyToAll));
92 hideEventsMenu->addAction(action(HideUseDefaults));
93 _hideEventsMenuAction = new Action(tr("Hide Events"), 0);
94 _hideEventsMenuAction->setMenu(hideEventsMenu);
96 QMenu *nickCtcpMenu = new QMenu();
97 nickCtcpMenu->addAction(action(NickCtcpPing));
98 nickCtcpMenu->addAction(action(NickCtcpVersion));
99 nickCtcpMenu->addAction(action(NickCtcpTime));
100 nickCtcpMenu->addAction(action(NickCtcpFinger));
101 _nickCtcpMenuAction = new Action(tr("CTCP"), 0);
102 _nickCtcpMenuAction->setMenu(nickCtcpMenu);
104 QMenu *nickModeMenu = new QMenu();
105 nickModeMenu->addAction(action(NickOp));
106 nickModeMenu->addAction(action(NickDeop));
107 nickModeMenu->addAction(action(NickVoice));
108 nickModeMenu->addAction(action(NickDevoice));
109 nickModeMenu->addSeparator();
110 nickModeMenu->addAction(action(NickKick));
111 nickModeMenu->addAction(action(NickBan));
112 nickModeMenu->addAction(action(NickKickBan));
113 _nickModeMenuAction = new Action(tr("Actions"), 0);
114 _nickModeMenuAction->setMenu(nickModeMenu);
117 NetworkModelActionProvider::~NetworkModelActionProvider() {
118 _hideEventsMenuAction->menu()->deleteLater();
119 _hideEventsMenuAction->deleteLater();
120 _nickCtcpMenuAction->menu()->deleteLater();
121 _nickCtcpMenuAction->deleteLater();
122 _nickModeMenuAction->menu()->deleteLater();
123 _nickModeMenuAction->deleteLater();
126 void NetworkModelActionProvider::registerAction(ActionType type, const QString &text, bool checkable) {
127 registerAction(type, QPixmap(), text, checkable);
130 void NetworkModelActionProvider::registerAction(ActionType type, const QPixmap &icon, const QString &text, bool checkable) {
133 act = new Action(text, this);
135 act = new Action(icon, text, this);
137 act->setCheckable(checkable);
140 _actionCollection->addAction(QString::number(type, 16), act);
141 _actionByType[type] = act;
144 void NetworkModelActionProvider::addActions(QMenu *menu, BufferId bufId, QObject *receiver, const char *method) {
147 addActions(menu, Client::networkModel()->bufferIndex(bufId), receiver, method);
150 void NetworkModelActionProvider::addActions(QMenu *menu, const QModelIndex &index, QObject *receiver, const char *method, bool isCustomBufferView) {
153 addActions(menu, QList<QModelIndex>() << index, 0, QString(), receiver, method, isCustomBufferView);
156 void NetworkModelActionProvider::addActions(QMenu *menu, MessageFilter *filter, BufferId msgBuffer, QObject *receiver, const char *slot) {
157 addActions(menu, filter, msgBuffer, QString(), receiver, slot);
160 void NetworkModelActionProvider::addActions(QMenu *menu, MessageFilter *filter, BufferId msgBuffer, const QString &chanOrNick, QObject *receiver, const char *method) {
163 addActions(menu, QList<QModelIndex>() << Client::networkModel()->bufferIndex(msgBuffer), filter, chanOrNick, receiver, method, false);
166 void NetworkModelActionProvider::addActions(QMenu *menu, const QList<QModelIndex> &indexList, QObject *receiver, const char *method, bool isCustomBufferView) {
167 addActions(menu, indexList, 0, QString(), receiver, method, isCustomBufferView);
170 // add a list of actions sensible for the current item(s)
171 void NetworkModelActionProvider::addActions(QMenu *menu,
172 const QList<QModelIndex> &indexList,
173 MessageFilter *filter,
174 const QString &contextItem,
177 bool isCustomBufferView)
179 if(!indexList.count())
182 _indexList = indexList;
183 _messageFilter = filter;
184 _contextItem = contextItem;
185 _receiver = receiver;
188 if(!_messageFilter) {
189 // this means we are in a BufferView (or NickView) rather than a ChatView
191 // first index in list determines the menu type (just in case we have both buffers and networks selected, for example)
192 QModelIndex index = _indexList.at(0);
193 NetworkModel::ItemType itemType = static_cast<NetworkModel::ItemType>(index.data(NetworkModel::ItemTypeRole).toInt());
196 case NetworkModel::NetworkItemType:
197 addNetworkItemActions(menu, index);
199 case NetworkModel::BufferItemType:
200 addBufferItemActions(menu, index, isCustomBufferView);
202 case NetworkModel::IrcUserItemType:
203 addIrcUserActions(menu, index);
211 if(_contextItem.isEmpty()) {
212 // a) query buffer: handle like ircuser
213 // b) general chatview: handle like channel iff it displays a single buffer
214 // NOTE stuff breaks probably with merged buffers, need to rework a lot around here then
215 if(_messageFilter->containedBuffers().count() == 1) {
216 // we can handle this like a single bufferItem
217 QModelIndex index = Client::networkModel()->bufferIndex(_messageFilter->containedBuffers().values().at(0));
218 _indexList = QList<QModelIndex>() << index;
219 addBufferItemActions(menu, index);
222 // TODO: actions for merged buffers... _indexList contains the index of the message we clicked on
226 // context item = chan or nick, _indexList = buf where the msg clicked on originated
227 if(isChannelName(_contextItem)) {
228 QModelIndex msgIdx = _indexList.at(0);
229 if(!msgIdx.isValid())
231 NetworkId networkId = msgIdx.data(NetworkModel::NetworkIdRole).value<NetworkId>();
232 BufferId bufId = Client::networkModel()->bufferId(networkId, _contextItem);
233 if(bufId.isValid()) {
234 QModelIndex targetIdx = Client::networkModel()->bufferIndex(bufId);
235 _indexList = QList<QModelIndex>() << targetIdx;
236 addAction(BufferJoin, menu, targetIdx, InactiveState);
237 addAction(BufferSwitchTo, menu, targetIdx, ActiveState);
239 addAction(JoinChannel, menu);
241 // TODO: actions for a nick
247 void NetworkModelActionProvider::addNetworkItemActions(QMenu *menu, const QModelIndex &index) {
248 NetworkId networkId = index.data(NetworkModel::NetworkIdRole).value<NetworkId>();
249 if(!networkId.isValid())
251 const Network *network = Client::network(networkId);
252 Q_CHECK_PTR(network);
256 addAction(NetworkConnect, menu, network->connectionState() == Network::Disconnected);
257 addAction(NetworkDisconnect, menu, network->connectionState() != Network::Disconnected);
258 menu->addSeparator();
259 addAction(ShowChannelList, menu, index, ActiveState);
260 addAction(JoinChannel, menu, index, ActiveState);
264 void NetworkModelActionProvider::addBufferItemActions(QMenu *menu, const QModelIndex &index, bool isCustomBufferView) {
265 BufferInfo bufferInfo = index.data(NetworkModel::BufferInfoRole).value<BufferInfo>();
267 menu->addSeparator();
268 switch(bufferInfo.type()) {
269 case BufferInfo::ChannelBuffer:
270 addAction(BufferJoin, menu, index, InactiveState);
271 addAction(BufferPart, menu, index, ActiveState);
272 menu->addSeparator();
273 addHideEventsMenu(menu, bufferInfo.bufferId());
274 menu->addSeparator();
275 addAction(HideBufferTemporarily, menu, isCustomBufferView);
276 addAction(HideBufferPermanently, menu, isCustomBufferView);
277 addAction(BufferRemove, menu, index, InactiveState);
280 case BufferInfo::QueryBuffer:
282 //IrcUser *ircUser = qobject_cast<IrcUser *>(index.data(NetworkModel::IrcUserRole).value<QObject *>());
284 addIrcUserActions(menu, index);
285 menu->addSeparator();
287 addHideEventsMenu(menu, bufferInfo.bufferId());
288 menu->addSeparator();
289 addAction(HideBufferTemporarily, menu, isCustomBufferView);
290 addAction(HideBufferPermanently, menu, isCustomBufferView);
291 addAction(BufferRemove, menu, index);
296 addAction(HideBufferTemporarily, menu, isCustomBufferView);
297 addAction(HideBufferPermanently, menu, isCustomBufferView);
301 void NetworkModelActionProvider::addIrcUserActions(QMenu *menu, const QModelIndex &index) {
302 // this can be called: a) as a nicklist context menu (index has IrcUserItemType)
303 // b) as a query buffer context menu (index has BufferItemType and is a QueryBufferItem)
304 // c) right-click in a query chatview (same as b), index will be the corresponding QueryBufferItem)
305 // d) right-click on some nickname (_contextItem will be non-null, _filter -> chatview, index -> message buffer)
307 if(_contextItem.isNull()) {
309 bool haveQuery = _indexList.count() == 1 && findQueryBuffer(index).isValid();
310 NetworkModel::ItemType itemType = static_cast<NetworkModel::ItemType>(index.data(NetworkModel::ItemTypeRole).toInt());
311 addAction(_nickModeMenuAction, menu, itemType == NetworkModel::IrcUserItemType);
312 addAction(_nickCtcpMenuAction, menu);
313 menu->addSeparator();
314 addAction(NickQuery, menu, itemType == NetworkModel::IrcUserItemType && !haveQuery && _indexList.count() == 1);
315 addAction(NickSwitchTo, menu, itemType == NetworkModel::IrcUserItemType && haveQuery);
316 menu->addSeparator();
317 addAction(NickWhois, menu, true);
319 } else if(!_contextItem.isEmpty() && _messageFilter) {
326 /******** Helper Functions ***********************************************************************/
328 bool NetworkModelActionProvider::checkRequirements(const QModelIndex &index, ItemActiveStates requiredActiveState) {
332 ItemActiveStates isActive = index.data(NetworkModel::ItemActiveRole).toBool()
336 if(!(isActive & requiredActiveState))
342 Action * NetworkModelActionProvider::addAction(ActionType type , QMenu *menu, const QModelIndex &index, ItemActiveStates requiredActiveState) {
343 return addAction(action(type), menu, checkRequirements(index, requiredActiveState));
346 Action * NetworkModelActionProvider::addAction(Action *action , QMenu *menu, const QModelIndex &index, ItemActiveStates requiredActiveState) {
347 return addAction(action, menu, checkRequirements(index, requiredActiveState));
350 Action * NetworkModelActionProvider::addAction(ActionType type , QMenu *menu, bool condition) {
351 return addAction(action(type), menu, condition);
354 Action * NetworkModelActionProvider::addAction(Action *action , QMenu *menu, bool condition) {
356 menu->addAction(action);
357 action->setVisible(true);
359 action->setVisible(false);
364 void NetworkModelActionProvider::addHideEventsMenu(QMenu *menu, BufferId bufferId) {
365 if(BufferSettings(bufferId).hasFilter())
366 addHideEventsMenu(menu, BufferSettings(bufferId).messageFilter());
368 addHideEventsMenu(menu);
371 void NetworkModelActionProvider::addHideEventsMenu(QMenu *menu, MessageFilter *msgFilter) {
372 if(BufferSettings(msgFilter->idString()).hasFilter())
373 addHideEventsMenu(menu, BufferSettings(msgFilter->idString()).messageFilter());
375 addHideEventsMenu(menu);
378 void NetworkModelActionProvider::addHideEventsMenu(QMenu *menu, int filter) {
379 action(HideApplyToAll)->setEnabled(filter != -1);
380 action(HideUseDefaults)->setEnabled(filter != -1);
382 filter = BufferSettings().messageFilter();
384 action(HideJoin)->setChecked(filter & Message::Join);
385 action(HidePart)->setChecked(filter & Message::Part);
386 action(HideQuit)->setChecked(filter & Message::Quit);
387 action(HideNick)->setChecked(filter & Message::Nick);
388 action(HideMode)->setChecked(filter & Message::Mode);
389 action(HideDayChange)->setChecked(filter & Message::DayChange);
391 menu->addAction(_hideEventsMenuAction);
394 QString NetworkModelActionProvider::nickName(const QModelIndex &index) const {
395 IrcUser *ircUser = qobject_cast<IrcUser *>(index.data(NetworkModel::IrcUserRole).value<QObject *>());
397 return ircUser->nick();
399 BufferInfo bufferInfo = index.data(NetworkModel::BufferInfoRole).value<BufferInfo>();
400 if(!bufferInfo.isValid())
402 if(!bufferInfo.type() == BufferInfo::QueryBuffer)
405 return bufferInfo.bufferName(); // FIXME this might break with merged queries maybe
408 BufferId NetworkModelActionProvider::findQueryBuffer(const QModelIndex &index, const QString &predefinedNick) const {
409 NetworkId networkId = index.data(NetworkModel::NetworkIdRole).value<NetworkId>();
410 if(!networkId.isValid())
413 QString nick = predefinedNick.isEmpty() ? nickName(index) : predefinedNick;
417 return findQueryBuffer(networkId, nick);
420 BufferId NetworkModelActionProvider::findQueryBuffer(NetworkId networkId, const QString &nick) const {
421 return Client::networkModel()->bufferId(networkId, nick);
424 void NetworkModelActionProvider::handleExternalAction(ActionType type, QAction *action) {
426 if(_receiver && _method) {
427 if(!QMetaObject::invokeMethod(_receiver, _method, Q_ARG(QAction *, action)))
428 qWarning() << "NetworkModelActionProvider::handleExternalAction(): Could not invoke slot" << _receiver << _method;
432 /******** Handle Actions *************************************************************************/
434 void NetworkModelActionProvider::actionTriggered(QAction *action) {
435 ActionType type = (ActionType)action->data().toInt();
437 if(type & NetworkMask)
438 handleNetworkAction(type, action);
439 else if(type & BufferMask)
440 handleBufferAction(type, action);
441 else if(type & HideMask)
442 handleHideAction(type, action);
443 else if(type & GeneralMask)
444 handleGeneralAction(type, action);
445 else if(type & NickMask)
446 handleNickAction(type, action);
447 else if(type & ExternalMask)
448 handleExternalAction(type, action);
450 qWarning() << "NetworkModelActionProvider::actionTriggered(): Unhandled action!";
457 void NetworkModelActionProvider::handleNetworkAction(ActionType type, QAction *) {
458 if(!_indexList.count())
460 const Network *network = Client::network(_indexList.at(0).data(NetworkModel::NetworkIdRole).value<NetworkId>());
461 Q_CHECK_PTR(network);
467 network->requestConnect();
469 case NetworkDisconnect:
470 network->requestDisconnect();
477 void NetworkModelActionProvider::handleBufferAction(ActionType type, QAction *) {
478 if(type == BufferRemove) {
479 removeBuffers(_indexList);
482 foreach(QModelIndex index, _indexList) {
483 BufferInfo bufferInfo = index.data(NetworkModel::BufferInfoRole).value<BufferInfo>();
484 if(!bufferInfo.isValid())
489 Client::userInput(bufferInfo, QString("/JOIN %1").arg(bufferInfo.bufferName()));
493 QString reason = Client::identity(Client::network(bufferInfo.networkId())->identity())->partReason();
494 Client::userInput(bufferInfo, QString("/PART %1").arg(reason));
498 Client::bufferModel()->switchToBuffer(bufferInfo.bufferId());
507 void NetworkModelActionProvider::removeBuffers(const QModelIndexList &indexList) {
508 QList<BufferInfo> inactive;
509 foreach(QModelIndex index, indexList) {
510 BufferInfo info = index.data(NetworkModel::BufferInfoRole).value<BufferInfo>();
512 if(info.type() == BufferInfo::QueryBuffer
513 || (info.type() == BufferInfo::ChannelBuffer && !index.data(NetworkModel::ItemActiveRole).toBool()))
518 if(inactive.count()) {
519 msg = tr("Do you want to delete the following buffer(s) permanently?", 0, inactive.count());
521 foreach(BufferInfo info, inactive)
522 msg += QString("<li>%1</li>").arg(info.bufferName());
524 msg += tr("<b>Note:</b> This will delete all related data, including all backlog data, from the core's database and cannot be undone.");
525 if(inactive.count() != indexList.count())
526 msg += tr("<br>Active channel buffers cannot be deleted, please part the channel first.");
528 if(QMessageBox::question(0, tr("Remove buffers permanently?"), msg, QMessageBox::Yes|QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
529 foreach(BufferInfo info, inactive)
530 Client::removeBuffer(info.bufferId());
535 void NetworkModelActionProvider::handleHideAction(ActionType type, QAction *action) {
539 if(NetworkModelActionProvider::action(HideJoin)->isChecked())
540 filter |= Message::Join;
541 if(NetworkModelActionProvider::action(HidePart)->isChecked())
542 filter |= Message::Part;
543 if(NetworkModelActionProvider::action(HideQuit)->isChecked())
544 filter |= Message::Quit;
545 if(NetworkModelActionProvider::action(HideNick)->isChecked())
546 filter |= Message::Nick;
547 if(NetworkModelActionProvider::action(HideMode)->isChecked())
548 filter |= Message::Mode;
549 if(NetworkModelActionProvider::action(HideDayChange)->isChecked())
550 filter |= Message::DayChange;
560 BufferSettings(_messageFilter->idString()).setMessageFilter(filter);
562 foreach(QModelIndex index, _indexList) {
563 BufferId bufferId = index.data(NetworkModel::BufferIdRole).value<BufferId>();
564 if(!bufferId.isValid())
566 BufferSettings(bufferId).setMessageFilter(filter);
571 BufferSettings().setMessageFilter(filter);
572 case HideUseDefaults:
574 BufferSettings(_messageFilter->idString()).removeFilter();
576 foreach(QModelIndex index, _indexList) {
577 BufferId bufferId = index.data(NetworkModel::BufferIdRole).value<BufferId>();
578 if(!bufferId.isValid())
580 BufferSettings(bufferId).removeFilter();
589 void NetworkModelActionProvider::handleGeneralAction(ActionType type, QAction *action) {
592 if(!_indexList.count())
594 NetworkId networkId = _indexList.at(0).data(NetworkModel::NetworkIdRole).value<NetworkId>();
595 if(!networkId.isValid())
600 QString channelName = _contextItem;
601 if(channelName.isEmpty()) {
603 channelName = QInputDialog::getText(0, tr("Join Channel"), tr("Input channel name:"), QLineEdit::Normal, QString(), &ok);
607 if(!channelName.isEmpty()) {
608 Client::instance()->userInput(BufferInfo::fakeStatusBuffer(networkId), QString("/JOIN %1").arg(channelName));
612 case ShowChannelList:
613 emit showChannelList(networkId);
616 emit showIgnoreList(networkId);
623 void NetworkModelActionProvider::handleNickAction(ActionType type, QAction *) {
624 foreach(QModelIndex index, _indexList) {
625 NetworkId networkId = index.data(NetworkModel::NetworkIdRole).value<NetworkId>();
626 if(!networkId.isValid())
628 QString nick = nickName(index);
631 BufferInfo bufferInfo = index.data(NetworkModel::BufferInfoRole).value<BufferInfo>();
632 if(!bufferInfo.isValid())
637 Client::userInput(bufferInfo, QString("/WHOIS %1 %1").arg(nick));
639 case NickCtcpVersion:
640 Client::userInput(bufferInfo, QString("/CTCP %1 VERSION").arg(nick));
643 Client::userInput(bufferInfo, QString("/CTCP %1 PING").arg(nick));
646 Client::userInput(bufferInfo, QString("/CTCP %1 TIME").arg(nick));
649 Client::userInput(bufferInfo, QString("/CTCP %1 FINGER").arg(nick));
652 Client::userInput(bufferInfo, QString("/OP %1").arg(nick));
655 Client::userInput(bufferInfo, QString("/DEOP %1").arg(nick));
658 Client::userInput(bufferInfo, QString("/VOICE %1").arg(nick));
661 Client::userInput(bufferInfo, QString("/DEVOICE %1").arg(nick));
664 Client::userInput(bufferInfo, QString("/KICK %1").arg(nick));
667 Client::userInput(bufferInfo, QString("/BAN %1").arg(nick));
670 Client::userInput(bufferInfo, QString("/BAN %1").arg(nick));
671 Client::userInput(bufferInfo, QString("/KICK %1").arg(nick));
674 Client::bufferModel()->switchToBuffer(findQueryBuffer(networkId, nick));
677 Client::userInput(bufferInfo, QString("/QUERY %1").arg(nick));
680 qWarning() << "Unhandled nick action";