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