dc8b6a6a6b1ada1fb4c88cda00e9158f41b7fa6f
[quassel.git] / src / qtui / inputwidget.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 "inputwidget.h"
22
23 #include "action.h"
24 #include "actioncollection.h"
25 #include "bufferview.h"
26 #include "client.h"
27 #include "iconloader.h"
28 #include "ircuser.h"
29 #include "jumpkeyhandler.h"
30 #include "networkmodel.h"
31 #include "qtui.h"
32 #include "qtuisettings.h"
33 #include "tabcompleter.h"
34
35 InputWidget::InputWidget(QWidget *parent)
36   : AbstractItemView(parent),
37     _networkId(0)
38 {
39   ui.setupUi(this);
40   connect(ui.inputEdit, SIGNAL(textEntered(QString)), this, SLOT(sendText(QString)));
41   connect(ui.ownNick, SIGNAL(activated(QString)), this, SLOT(changeNick(QString)));
42
43   layout()->setAlignment(ui.ownNick, Qt::AlignBottom);
44   layout()->setAlignment(ui.inputEdit, Qt::AlignBottom);
45
46   setFocusProxy(ui.inputEdit);
47   ui.ownNick->setFocusProxy(ui.inputEdit);
48
49   ui.ownNick->setSizeAdjustPolicy(QComboBox::AdjustToContents);
50   ui.ownNick->installEventFilter(new MouseWheelFilter(this));
51   ui.inputEdit->installEventFilter(new JumpKeyHandler(this));
52
53   ui.inputEdit->setMinHeight(1);
54   ui.inputEdit->setMaxHeight(5);
55   ui.inputEdit->setMode(MultiLineEdit::MultiLine);
56
57   new TabCompleter(ui.inputEdit);
58
59   QtUiStyleSettings s("Fonts");
60   s.notify("InputLine", this, SLOT(setCustomFont(QVariant)));
61   setCustomFont(s.value("InputLine", QFont()));
62
63   ActionCollection *coll = QtUi::actionCollection();
64
65   Action *activateInputline = coll->add<Action>("FocusInputLine");
66   connect(activateInputline, SIGNAL(triggered()), SLOT(setFocus()));
67   activateInputline->setText(tr("Focus Input Line"));
68   activateInputline->setShortcut(tr("Ctrl+L"));
69 }
70
71 InputWidget::~InputWidget() {
72 }
73
74 void InputWidget::setCustomFont(const QVariant &v) {
75   QFont font = v.value<QFont>();
76   if(font.family().isEmpty())
77     font = QApplication::font();
78   ui.inputEdit->setCustomFont(font);
79 }
80
81 bool InputWidget::eventFilter(QObject *watched, QEvent *event) {
82   if(event->type() != QEvent::KeyPress)
83     return false;
84
85   // keys from BufferView should be sent to (and focus) the input line
86   BufferView *view = qobject_cast<BufferView *>(watched);
87   if(view) {
88     QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
89     if(keyEvent->text().length() == 1 && !(keyEvent->modifiers() & (Qt::ControlModifier ^ Qt::AltModifier)) ) { // normal key press
90       QChar c = keyEvent->text().at(0);
91       if(c.isLetterOrNumber() || c.isSpace() || c.isPunct() || c.isSymbol()) {
92         setFocus();
93         QCoreApplication::sendEvent(inputLine(), keyEvent);
94         return true;
95       } else
96         return false;
97     }
98   }
99   return false;
100 }
101
102 void InputWidget::keyPressEvent(QKeyEvent * event) {
103   if(event->matches(QKeySequence::Find)) {
104     QAction *act = GraphicalUi::actionCollection()->action("ToggleSearchBar");
105     if(act) {
106       act->toggle();
107       return;
108     }
109   }
110   AbstractItemView::keyPressEvent(event);
111 }
112
113 void InputWidget::currentChanged(const QModelIndex &current, const QModelIndex &previous) {
114   Q_UNUSED(previous)
115   NetworkId networkId = current.data(NetworkModel::NetworkIdRole).value<NetworkId>();
116   if(networkId == _networkId)
117     return;
118
119   setNetwork(networkId);
120   updateNickSelector();
121   updateEnabledState();
122 }
123
124 void InputWidget::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) {
125   QItemSelectionRange changedArea(topLeft, bottomRight);
126   if(changedArea.contains(selectionModel()->currentIndex())) {
127     updateEnabledState();
128   }
129 };
130
131 void InputWidget::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) {
132   NetworkId networkId;
133   QModelIndex child;
134   for(int row = start; row <= end; row++) {
135     child = model()->index(row, 0, parent);
136     if(NetworkModel::NetworkItemType != child.data(NetworkModel::ItemTypeRole).toInt())
137       continue;
138     networkId = child.data(NetworkModel::NetworkIdRole).value<NetworkId>();
139     if(networkId == _networkId) {
140       setNetwork(0);
141       updateNickSelector();
142       return;
143     }
144   }
145 }
146
147 void InputWidget::updateEnabledState() {
148   QModelIndex currentIndex = selectionModel()->currentIndex();
149
150   const Network *net = Client::networkModel()->networkByIndex(currentIndex);
151   bool enabled = false;
152   if(net) {
153     // disable inputline if it's a channelbuffer we parted from or...
154     enabled = (currentIndex.data(NetworkModel::ItemActiveRole).value<bool>() || (currentIndex.data(NetworkModel::BufferTypeRole).toInt() != BufferInfo::ChannelBuffer));
155     // ... if we're not connected to the network at all
156     enabled &= net->isConnected();
157   }
158   ui.inputEdit->setEnabled(enabled);
159 }
160
161 const Network *InputWidget::currentNetwork() const {
162   return Client::network(_networkId);
163 }
164
165 BufferInfo InputWidget::currentBufferInfo() const {
166   return selectionModel()->currentIndex().data(NetworkModel::BufferInfoRole).value<BufferInfo>();
167 };
168
169 void InputWidget::setNetwork(NetworkId networkId) {
170   if(_networkId == networkId)
171     return;
172
173   const Network *previousNet = Client::network(_networkId);
174   if(previousNet) {
175     disconnect(previousNet, 0, this, 0);
176     if(previousNet->me())
177       disconnect(previousNet->me(), 0, this, 0);
178   }
179
180   _networkId = networkId;
181
182   const Network *network = Client::network(networkId);
183   if(network) {
184     connect(network, SIGNAL(identitySet(IdentityId)), this, SLOT(setIdentity(IdentityId)));
185     connectMyIrcUser();
186     setIdentity(network->identity());
187   } else {
188     setIdentity(0);
189     _networkId = 0;
190   }
191 }
192
193 void InputWidget::connectMyIrcUser() {
194   const Network *network = currentNetwork();
195   if(network->me()) {
196     connect(network->me(), SIGNAL(nickSet(const QString &)), this, SLOT(updateNickSelector()));
197     connect(network->me(), SIGNAL(userModesSet(QString)), this, SLOT(updateNickSelector()));
198     connect(network->me(), SIGNAL(userModesAdded(QString)), this, SLOT(updateNickSelector()));
199     connect(network->me(), SIGNAL(userModesRemoved(QString)), this, SLOT(updateNickSelector()));
200     connect(network->me(), SIGNAL(awaySet(bool)), this, SLOT(updateNickSelector()));
201     disconnect(network, SIGNAL(myNickSet(const QString &)), this, SLOT(connectMyIrcUser()));
202     updateNickSelector();
203   } else {
204     connect(network, SIGNAL(myNickSet(const QString &)), this, SLOT(connectMyIrcUser()));
205   }
206 }
207
208 void InputWidget::setIdentity(IdentityId identityId) {
209   if(_identityId == identityId)
210     return;
211
212   const Identity *previousIdentity = Client::identity(_identityId);
213   if(previousIdentity)
214     disconnect(previousIdentity, 0, this, 0);
215
216   _identityId = identityId;
217
218   const Identity *identity = Client::identity(identityId);
219   if(identity) {
220     connect(identity, SIGNAL(nicksSet(QStringList)), this, SLOT(updateNickSelector()));
221   } else {
222     _identityId = 0;
223   }
224   updateNickSelector();
225 }
226
227 void InputWidget::updateNickSelector() const {
228   ui.ownNick->clear();
229
230   const Network *net = currentNetwork();
231   if(!net)
232     return;
233
234   const Identity *identity = Client::identity(net->identity());
235   if(!identity) {
236     qWarning() << "InputWidget::updateNickSelector(): can't find Identity for Network" << net->networkId() << "IdentityId:" << net->identity();
237     return;
238   }
239
240   int nickIdx;
241   QStringList nicks = identity->nicks();
242   if((nickIdx = nicks.indexOf(net->myNick())) == -1) {
243     nicks.prepend(net->myNick());
244     nickIdx = 0;
245   }
246
247   if(nicks.isEmpty())
248     return;
249
250   IrcUser *me = net->me();
251   if(me) {
252     nicks[nickIdx] = net->myNick();
253     if(!me->userModes().isEmpty())
254       nicks[nickIdx] += QString(" (+%1)").arg(me->userModes());
255   }
256       
257   ui.ownNick->addItems(nicks);
258
259   if(me && me->isAway())
260     ui.ownNick->setItemData(nickIdx, SmallIcon("user-away"), Qt::DecorationRole);
261
262   ui.ownNick->setCurrentIndex(nickIdx);
263 }
264
265 void InputWidget::changeNick(const QString &newNick) const {
266   const Network *net = currentNetwork();
267   if(!net || net->isMyNick(newNick))
268     return;
269
270   // we reset the nick selecter as we have no confirmation yet, that this will succeed.
271   // if the action succeeds it will be properly updated anyways.
272   updateNickSelector();
273   Client::userInput(BufferInfo::fakeStatusBuffer(net->networkId()), QString("/NICK %1").arg(newNick));
274 }
275
276 void InputWidget::sendText(const QString &text) const {
277   Client::userInput(currentBufferInfo(), text);
278 }
279
280
281 // MOUSE WHEEL FILTER
282 MouseWheelFilter::MouseWheelFilter(QObject *parent)
283   : QObject(parent)
284 {
285 }
286
287 bool MouseWheelFilter::eventFilter(QObject *obj, QEvent *event) {
288   if(event->type() != QEvent::Wheel)
289     return QObject::eventFilter(obj, event);
290   else
291     return true;
292 }