Use KTextEdit instead of QLineEdit. On most KDE systems this means Spell Check support!
[quassel.git] / src / uisupport / inputline.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005/06 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 "bufferview.h"
22
23 #include "inputline.h"
24 #include "tabcompleter.h"
25
26 InputLine::InputLine(QWidget *parent)
27   :
28 #ifdef HAVE_KDE
29     KTextEdit(parent),
30 #else
31     QLineEdit(parent),
32 #endif
33     idx(0),
34     tabCompleter(new TabCompleter(this))
35 {
36 #ifdef HAVE_KDE
37 //This is done to make the KTextEdit look like a lineedit
38   setMaximumHeight(document()->size().toSize().height());
39   setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
40   setAcceptRichText(false);
41   connect(this, SIGNAL(textChanged()), this, SLOT(on_textChanged()));
42 #endif
43
44   connect(this, SIGNAL(returnPressed()), this, SLOT(on_returnPressed()));
45   connect(this, SIGNAL(textChanged(QString)), this, SLOT(on_textChanged(QString)));
46 }
47
48 InputLine::~InputLine() {
49 }
50
51 bool InputLine::eventFilter(QObject *watched, QEvent *event) {
52   if(event->type() != QEvent::KeyPress)
53     return false;
54
55   // keys from BufferView should be sent to (and focus) the input line
56   BufferView *view = qobject_cast<BufferView *>(watched);
57   if(view) {
58     QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
59     if(keyEvent->text().length() == 1 && !(keyEvent->modifiers() & (Qt::ControlModifier ^ Qt::AltModifier)) ) { // normal key press
60       QChar c = keyEvent->text().at(0);
61       if(c.isLetterOrNumber() || c.isSpace() || c.isPunct() || c.isSymbol()) {
62         setFocus();
63         keyPressEvent(keyEvent);
64         return true;
65       } else
66         return false;
67     }
68   }
69   return false;
70 }
71
72 void InputLine::keyPressEvent(QKeyEvent * event) {
73   switch(event->key()) {
74   case Qt::Key_Up:
75     event->accept();
76
77     addToHistory(text(), true);
78
79     if(idx > 0) {
80       idx--;
81       showHistoryEntry();
82     }
83
84     break;
85
86   case Qt::Key_Down:
87     event->accept();
88
89     addToHistory(text(), true);
90
91     if(idx < history.count()) {
92       idx++;
93       if(idx < history.count() || tempHistory.contains(idx)) // tempHistory might have an entry for idx == history.count() + 1
94         showHistoryEntry();
95       else
96         resetLine();              // equals clear() in this case
97     } else {
98       addToHistory(text());
99       resetLine();
100     }
101
102     break;
103
104   case Qt::Key_Select:          // for Qtopia
105     emit returnPressed();
106
107 #ifdef HAVE_KDE
108 //Since this is a ktextedit, we don't have this signal "natively"
109   case Qt::Key_Return:
110     emit returnPressed();
111 #endif
112
113   default:
114 #ifdef HAVE_KDE
115     KTextEdit::keyPressEvent(event);
116 #else
117     QLineEdit::keyPressEvent(event);
118 #endif
119   }
120 }
121
122 bool InputLine::addToHistory(const QString &text, bool temporary) {
123   if(text.isEmpty())
124     return false;
125
126   Q_ASSERT(0 <= idx && idx <= history.count());
127
128   if(history.isEmpty() || text != history[idx - (int)(idx == history.count())]) {
129     // if an entry of the history is changed, we remember it and show it again at this
130     // position until a line was actually sent
131     // sent lines get appended to the history
132     if(temporary) {
133       tempHistory[idx] = text;
134     } else {
135       history << text;
136       tempHistory.clear();
137     }
138     return true;
139   } else {
140     return false;
141   }
142 }
143
144 void InputLine::on_returnPressed() {
145   addToHistory(text());
146   emit sendText(text());
147   resetLine();
148 }
149
150 void InputLine::on_textChanged(QString newText) {
151   QStringList lineSeparators;
152   lineSeparators << QString("\r\n")
153                  << QString('\n')
154                  << QString('\r');
155
156   QString lineSep;
157   foreach(QString separator, lineSeparators) {
158     if(newText.contains(separator)) {
159       lineSep = separator;
160       break;
161     }
162   }
163
164   if(lineSep.isEmpty())
165     return;
166
167   QStringList lines = newText.split(lineSep);
168   clear();
169
170   if(lines.count() >= 4) {
171     QString msg = tr("Do you really want to paste %1 lines?").arg(lines.count());
172     msg += "<p>";
173     for(int i = 0; i < 3; i++) {
174       msg += lines[i].left(40);
175       if(lines[i].count() > 40)
176         msg += "...";
177       msg += "<br />";
178     }
179     msg += "...</p>";
180     QMessageBox question(QMessageBox::NoIcon, tr("Paste Protection"), msg, QMessageBox::Yes|QMessageBox::No);
181     question.setDefaultButton(QMessageBox::No);
182 #ifdef Q_WS_MAC
183     question.setWindowFlags(question.windowFlags() | Qt::Sheet);
184 #endif
185     if(question.exec() == QMessageBox::No)
186       return;
187   }
188
189   foreach(QString line, lines) {
190     clear();
191     insert(line);
192     emit returnPressed();
193   }
194 //   if(newText.contains(lineSep)) {
195 //     clear();
196 //     QString line = newText.section(lineSep, 0, 0);
197 //     QString remainder = newText.section(lineSep, 1);
198 //     insert(line);
199 //     emit returnPressed();
200 //     insert(remainder);
201 //   }
202 }
203
204 void InputLine::resetLine() {
205   // every time the InputLine is cleared we also reset history index
206   idx = history.count();
207   clear();
208 }
209
210 void InputLine::showHistoryEntry() {
211   // if the user changed the history, display the changed line
212   tempHistory.contains(idx) ? setText(tempHistory[idx]) : setText(history[idx]);
213 }