Simplify code, fix potential crash
[quassel.git] / src / uisupport / contextmenuactionprovider.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-09 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  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20
21 #include <QInputDialog>
22 #include <QMenu>
23 #include <QMessageBox>
24
25 #include "contextmenuactionprovider.h"
26
27 #include "buffermodel.h"
28 #include "buffersettings.h"
29 #include "iconloader.h"
30 #include "clientidentity.h"
31 #include "network.h"
32 #include "util.h"
33
34 ContextMenuActionProvider::ContextMenuActionProvider(QObject *parent) : NetworkModelController(parent) {
35   registerAction(NetworkConnect, SmallIcon("network-connect"), tr("Connect"));
36   registerAction(NetworkDisconnect, SmallIcon("network-disconnect"), tr("Disconnect"));
37
38   registerAction(BufferJoin, SmallIcon("irc-join-channel"), tr("Join"));
39   registerAction(BufferPart, SmallIcon("irc-close-channel"), tr("Part"));
40   registerAction(BufferRemove, tr("Delete Chat(s)..."));
41   registerAction(BufferSwitchTo, tr("Go to Chat"));
42
43   registerAction(HideJoin, tr("Joins"), true);
44   registerAction(HidePart, tr("Parts"), true);
45   registerAction(HideQuit, tr("Quits"), true);
46   registerAction(HideNick, tr("Nick Changes"), true);
47   registerAction(HideMode, tr("Mode Changes"), true);
48   registerAction(HideDayChange, tr("Day Changes"), true);
49   registerAction(HideTopic, tr("Topic Changes"), true);
50   registerAction(HideApplyToAll, tr("Set as Default..."));
51   registerAction(HideUseDefaults, tr("Use Defaults..."));
52
53   registerAction(JoinChannel, SmallIcon("irc-join-channel"), tr("Join Channel..."));
54
55   registerAction(NickQuery, tr("Start Query"));
56   registerAction(NickSwitchTo, tr("Show Query"));
57   registerAction(NickWhois, tr("Whois"));
58
59   registerAction(NickCtcpVersion, tr("Version"));
60   registerAction(NickCtcpTime, tr("Time"));
61   registerAction(NickCtcpPing, tr("Ping"));
62   registerAction(NickCtcpFinger, tr("Finger"));
63
64   registerAction(NickOp, SmallIcon("irc-operator"), tr("Give Operator Status"));
65   registerAction(NickDeop, SmallIcon("irc-remove-operator"), tr("Take Operator Status"));
66   registerAction(NickVoice, SmallIcon("irc-voice"), tr("Give Voice"));
67   registerAction(NickDevoice, SmallIcon("irc-unvoice"), tr("Take Voice"));
68   registerAction(NickKick, SmallIcon("im-kick-user"), tr("Kick From Channel"));
69   registerAction(NickBan, SmallIcon("im-ban-user"), tr("Ban From Channel"));
70   registerAction(NickKickBan, SmallIcon("im-ban-kick-user"), tr("Kick && Ban"));
71
72   registerAction(HideBufferTemporarily, tr("Hide Chat(s) Temporarily"));
73   registerAction(HideBufferPermanently, tr("Hide Chat(s) Permanently"));
74   registerAction(ShowChannelList, tr("Show Channel List"));
75   registerAction(ShowIgnoreList, tr("Show Ignore List"));
76
77   QMenu *hideEventsMenu = new QMenu();
78   hideEventsMenu->addAction(action(HideJoin));
79   hideEventsMenu->addAction(action(HidePart));
80   hideEventsMenu->addAction(action(HideQuit));
81   hideEventsMenu->addAction(action(HideNick));
82   hideEventsMenu->addAction(action(HideMode));
83   hideEventsMenu->addAction(action(HideTopic));
84   hideEventsMenu->addAction(action(HideDayChange));
85   hideEventsMenu->addSeparator();
86   hideEventsMenu->addAction(action(HideApplyToAll));
87   hideEventsMenu->addAction(action(HideUseDefaults));
88   _hideEventsMenuAction = new Action(tr("Hide Events"), 0);
89   _hideEventsMenuAction->setMenu(hideEventsMenu);
90
91   QMenu *nickCtcpMenu = new QMenu();
92   nickCtcpMenu->addAction(action(NickCtcpPing));
93   nickCtcpMenu->addAction(action(NickCtcpVersion));
94   nickCtcpMenu->addAction(action(NickCtcpTime));
95   nickCtcpMenu->addAction(action(NickCtcpFinger));
96   _nickCtcpMenuAction = new Action(tr("CTCP"), 0);
97   _nickCtcpMenuAction->setMenu(nickCtcpMenu);
98
99   QMenu *nickModeMenu = new QMenu();
100   nickModeMenu->addAction(action(NickOp));
101   nickModeMenu->addAction(action(NickDeop));
102   nickModeMenu->addAction(action(NickVoice));
103   nickModeMenu->addAction(action(NickDevoice));
104   nickModeMenu->addSeparator();
105   nickModeMenu->addAction(action(NickKick));
106   nickModeMenu->addAction(action(NickBan));
107   nickModeMenu->addAction(action(NickKickBan));
108   _nickModeMenuAction = new Action(tr("Actions"), 0);
109   _nickModeMenuAction->setMenu(nickModeMenu);
110 }
111
112 ContextMenuActionProvider::~ContextMenuActionProvider() {
113   _hideEventsMenuAction->menu()->deleteLater();
114   _hideEventsMenuAction->deleteLater();
115   _nickCtcpMenuAction->menu()->deleteLater();
116   _nickCtcpMenuAction->deleteLater();
117   _nickModeMenuAction->menu()->deleteLater();
118   _nickModeMenuAction->deleteLater();
119 }
120
121 void ContextMenuActionProvider::addActions(QMenu *menu, BufferId bufId, QObject *receiver, const char *method) {
122   if(!bufId.isValid())
123     return;
124   addActions(menu, Client::networkModel()->bufferIndex(bufId), receiver, method);
125 }
126
127 void ContextMenuActionProvider::addActions(QMenu *menu, const QModelIndex &index, QObject *receiver, const char *method, bool isCustomBufferView) {
128   if(!index.isValid())
129     return;
130   addActions(menu, QList<QModelIndex>() << index, 0, QString(), receiver, method, isCustomBufferView);
131 }
132
133 void ContextMenuActionProvider::addActions(QMenu *menu, MessageFilter *filter, BufferId msgBuffer, QObject *receiver, const char *slot) {
134   addActions(menu, filter, msgBuffer, QString(), receiver, slot);
135 }
136
137 void ContextMenuActionProvider::addActions(QMenu *menu, MessageFilter *filter, BufferId msgBuffer, const QString &chanOrNick, QObject *receiver, const char *method) {
138   if(!filter)
139     return;
140   addActions(menu, QList<QModelIndex>() << Client::networkModel()->bufferIndex(msgBuffer), filter, chanOrNick, receiver, method, false);
141 }
142
143 void ContextMenuActionProvider::addActions(QMenu *menu, const QList<QModelIndex> &indexList, QObject *receiver,  const char *method, bool isCustomBufferView) {
144   addActions(menu, indexList, 0, QString(), receiver, method, isCustomBufferView);
145 }
146
147 // add a list of actions sensible for the current item(s)
148 void ContextMenuActionProvider::addActions(QMenu *menu,
149                                             const QList<QModelIndex> &indexList_,
150                                             MessageFilter *filter_,
151                                             const QString &contextItem_,
152                                             QObject *receiver_,
153                                             const char *method_,
154                                             bool isCustomBufferView)
155 {
156   if(!indexList_.count())
157     return;
158
159   setIndexList(indexList_);
160   setMessageFilter(filter_);
161   setContextItem(contextItem_);
162   setSlot(receiver_, method_);
163
164   if(!messageFilter()) {
165     // this means we are in a BufferView (or NickView) rather than a ChatView
166
167     // first index in list determines the menu type (just in case we have both buffers and networks selected, for example)
168     QModelIndex index = indexList().at(0);
169     NetworkModel::ItemType itemType = static_cast<NetworkModel::ItemType>(index.data(NetworkModel::ItemTypeRole).toInt());
170
171     switch(itemType) {
172       case NetworkModel::NetworkItemType:
173         addNetworkItemActions(menu, index);
174         break;
175       case NetworkModel::BufferItemType:
176         addBufferItemActions(menu, index, isCustomBufferView);
177         break;
178       case NetworkModel::IrcUserItemType:
179         addIrcUserActions(menu, index);
180         break;
181       default:
182         return;
183
184     }
185   } else {
186     // ChatView actions
187     if(contextItem().isEmpty()) {
188       // a) query buffer: handle like ircuser
189       // b) general chatview: handle like channel iff it displays a single buffer
190       // NOTE stuff breaks probably with merged buffers, need to rework a lot around here then
191       if(messageFilter()->containedBuffers().count() == 1) {
192         // we can handle this like a single bufferItem
193         QModelIndex index = Client::networkModel()->bufferIndex(messageFilter()->containedBuffers().values().at(0));
194         setIndexList(index);
195         addBufferItemActions(menu, index);
196         return;
197       } else {
198         // TODO: actions for merged buffers... _indexList contains the index of the message we clicked on
199
200       }
201     } else {
202       // context item = chan or nick, _indexList = buf where the msg clicked on originated
203       if(isChannelName(contextItem())) {
204         QModelIndex msgIdx = indexList().at(0);
205         if(!msgIdx.isValid())
206           return;
207         NetworkId networkId = msgIdx.data(NetworkModel::NetworkIdRole).value<NetworkId>();
208         BufferId bufId = Client::networkModel()->bufferId(networkId, contextItem());
209         if(bufId.isValid()) {
210           QModelIndex targetIdx = Client::networkModel()->bufferIndex(bufId);
211           setIndexList(targetIdx);
212           addAction(BufferJoin, menu, targetIdx, InactiveState);
213           addAction(BufferSwitchTo, menu, targetIdx, ActiveState);
214         } else
215           addAction(JoinChannel, menu);
216       } else {
217         // TODO: actions for a nick
218       }
219     }
220   }
221 }
222
223 void ContextMenuActionProvider::addNetworkItemActions(QMenu *menu, const QModelIndex &index) {
224   NetworkId networkId = index.data(NetworkModel::NetworkIdRole).value<NetworkId>();
225   if(!networkId.isValid())
226     return;
227   const Network *network = Client::network(networkId);
228   Q_CHECK_PTR(network);
229   if(!network)
230     return;
231
232   addAction(NetworkConnect, menu, network->connectionState() == Network::Disconnected);
233   addAction(NetworkDisconnect, menu, network->connectionState() != Network::Disconnected);
234   menu->addSeparator();
235   addAction(ShowChannelList, menu, index, ActiveState);
236   addAction(JoinChannel, menu, index, ActiveState);
237
238 }
239
240 void ContextMenuActionProvider::addBufferItemActions(QMenu *menu, const QModelIndex &index, bool isCustomBufferView) {
241   BufferInfo bufferInfo = index.data(NetworkModel::BufferInfoRole).value<BufferInfo>();
242
243   menu->addSeparator();
244   switch(bufferInfo.type()) {
245     case BufferInfo::ChannelBuffer:
246       addAction(BufferJoin, menu, index, InactiveState);
247       addAction(BufferPart, menu, index, ActiveState);
248       menu->addSeparator();
249       addHideEventsMenu(menu, bufferInfo.bufferId());
250       menu->addSeparator();
251       addAction(HideBufferTemporarily, menu, isCustomBufferView);
252       addAction(HideBufferPermanently, menu, isCustomBufferView);
253       addAction(BufferRemove, menu, index, InactiveState);
254       break;
255
256     case BufferInfo::QueryBuffer:
257     {
258       //IrcUser *ircUser = qobject_cast<IrcUser *>(index.data(NetworkModel::IrcUserRole).value<QObject *>());
259       //if(ircUser) {
260         addIrcUserActions(menu, index);
261         menu->addSeparator();
262       //}
263       addHideEventsMenu(menu, bufferInfo.bufferId());
264       menu->addSeparator();
265       addAction(HideBufferTemporarily, menu, isCustomBufferView);
266       addAction(HideBufferPermanently, menu, isCustomBufferView);
267       addAction(BufferRemove, menu, index);
268       break;
269     }
270
271     default:
272       addAction(HideBufferTemporarily, menu, isCustomBufferView);
273       addAction(HideBufferPermanently, menu, isCustomBufferView);
274   }
275 }
276
277 void ContextMenuActionProvider::addIrcUserActions(QMenu *menu, const QModelIndex &index) {
278   // this can be called: a) as a nicklist context menu (index has IrcUserItemType)
279   //                     b) as a query buffer context menu (index has BufferItemType and is a QueryBufferItem)
280   //                     c) right-click in a query chatview (same as b), index will be the corresponding QueryBufferItem)
281   //                     d) right-click on some nickname (_contextItem will be non-null, _filter -> chatview, index -> message buffer)
282
283   if(contextItem().isNull()) {
284     // cases a, b, c
285     bool haveQuery = indexList().count() == 1 && findQueryBuffer(index).isValid();
286     NetworkModel::ItemType itemType = static_cast<NetworkModel::ItemType>(index.data(NetworkModel::ItemTypeRole).toInt());
287     addAction(_nickModeMenuAction, menu, itemType == NetworkModel::IrcUserItemType);
288     addAction(_nickCtcpMenuAction, menu);
289     menu->addSeparator();
290     addAction(NickQuery, menu, itemType == NetworkModel::IrcUserItemType && !haveQuery && indexList().count() == 1);
291     addAction(NickSwitchTo, menu, itemType == NetworkModel::IrcUserItemType && haveQuery);
292     menu->addSeparator();
293     addAction(NickWhois, menu, true);
294
295   } else if(!contextItem().isEmpty() && messageFilter()) {
296     // case d
297     // TODO
298
299   }
300 }
301
302 Action * ContextMenuActionProvider::addAction(ActionType type , QMenu *menu, const QModelIndex &index, ItemActiveStates requiredActiveState) {
303   return addAction(action(type), menu, checkRequirements(index, requiredActiveState));
304 }
305
306 Action * ContextMenuActionProvider::addAction(Action *action , QMenu *menu, const QModelIndex &index, ItemActiveStates requiredActiveState) {
307   return addAction(action, menu, checkRequirements(index, requiredActiveState));
308 }
309
310 Action * ContextMenuActionProvider::addAction(ActionType type , QMenu *menu, bool condition) {
311   return addAction(action(type), menu, condition);
312 }
313
314 Action * ContextMenuActionProvider::addAction(Action *action , QMenu *menu, bool condition) {
315   if(condition) {
316     menu->addAction(action);
317     action->setVisible(true);
318   } else {
319     action->setVisible(false);
320   }
321   return action;
322 }
323
324 void ContextMenuActionProvider::addHideEventsMenu(QMenu *menu, BufferId bufferId) {
325   if(BufferSettings(bufferId).hasFilter())
326     addHideEventsMenu(menu, BufferSettings(bufferId).messageFilter());
327   else
328     addHideEventsMenu(menu);
329 }
330
331 void ContextMenuActionProvider::addHideEventsMenu(QMenu *menu, MessageFilter *msgFilter) {
332   if(BufferSettings(msgFilter->idString()).hasFilter())
333     addHideEventsMenu(menu, BufferSettings(msgFilter->idString()).messageFilter());
334   else
335     addHideEventsMenu(menu);
336 }
337
338 void ContextMenuActionProvider::addHideEventsMenu(QMenu *menu, int filter) {
339   action(HideApplyToAll)->setEnabled(filter != -1);
340   action(HideUseDefaults)->setEnabled(filter != -1);
341   if(filter == -1)
342     filter = BufferSettings().messageFilter();
343
344   action(HideJoin)->setChecked(filter & Message::Join);
345   action(HidePart)->setChecked(filter & Message::Part);
346   action(HideQuit)->setChecked(filter & Message::Quit);
347   action(HideNick)->setChecked(filter & Message::Nick);
348   action(HideMode)->setChecked(filter & Message::Mode);
349   action(HideDayChange)->setChecked(filter & Message::DayChange);
350   action(HideTopic)->setChecked(filter & Message::Topic);
351
352   menu->addAction(_hideEventsMenuAction);
353 }