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