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