|
1 /* |
|
2 * Hedgewars, a free turn based strategy game |
|
3 * Copyright (c) 2006-2007 Igor Ulyanov <iulyanov@gmail.com> |
|
4 * Copyright (c) 2007-2011 Andrey Korotaev <unC0Rr@gmail.com> |
|
5 * |
|
6 * This program is free software; you can redistribute it and/or modify |
|
7 * it under the terms of the GNU General Public License as published by |
|
8 * the Free Software Foundation; version 2 of the License |
|
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 Free Software |
|
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA |
|
18 */ |
|
19 |
|
20 #include <QStringList> |
|
21 |
|
22 #include "SmartLineEdit.h" |
|
23 |
|
24 SmartLineEdit::SmartLineEdit(QWidget * parent) |
|
25 : QLineEdit(parent) |
|
26 { |
|
27 m_whitespace = QRegExp("\\s"); |
|
28 |
|
29 m_cmds = new QStringList(); |
|
30 m_nicks = new QStringList(); |
|
31 |
|
32 reset(); |
|
33 |
|
34 // reset when cursor is moved or content is changed |
|
35 connect(this, SIGNAL(cursorPositionChanged(int, int)), this, SLOT(reset())); |
|
36 connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(reset())); |
|
37 } |
|
38 |
|
39 |
|
40 void SmartLineEdit::addCommands(const QStringList & commands) |
|
41 { |
|
42 m_mutex.lock(); |
|
43 |
|
44 m_cmds->append(commands); |
|
45 |
|
46 m_mutex.unlock(); |
|
47 } |
|
48 |
|
49 |
|
50 void SmartLineEdit::removeCommands(const QStringList & commands) |
|
51 { |
|
52 m_mutex.lock(); |
|
53 |
|
54 foreach (const QString & cmd, commands) |
|
55 { |
|
56 m_cmds->removeAll(cmd); |
|
57 } |
|
58 |
|
59 m_mutex.unlock(); |
|
60 } |
|
61 |
|
62 |
|
63 void SmartLineEdit::addNickname(const QString & name) |
|
64 { |
|
65 m_mutex.lock(); |
|
66 |
|
67 m_nicks->append(name); |
|
68 |
|
69 m_mutex.unlock(); |
|
70 } |
|
71 |
|
72 |
|
73 void SmartLineEdit::removeNickname(const QString & name) |
|
74 { |
|
75 m_mutex.lock(); |
|
76 |
|
77 m_nicks->removeAll(name); |
|
78 |
|
79 m_mutex.unlock(); |
|
80 } |
|
81 |
|
82 bool SmartLineEdit::event(QEvent * event) |
|
83 { |
|
84 // we only want special treatment for key press events |
|
85 if (event->type() == QEvent::KeyPress) |
|
86 { |
|
87 QKeyEvent * keyEvent = static_cast<QKeyEvent*>(event); |
|
88 |
|
89 // TAB key pressed and any useful chars in the matchMe -> let's process those |
|
90 if ((keyEvent->key() == Qt::Key_Tab) && (!text().trimmed().isEmpty())) |
|
91 { |
|
92 keyPressEvent(keyEvent); |
|
93 if (event->isAccepted()) |
|
94 return true; |
|
95 } |
|
96 } |
|
97 return QLineEdit::event(event); |
|
98 } |
|
99 |
|
100 void SmartLineEdit::keyPressEvent(QKeyEvent * event) |
|
101 { |
|
102 int key = event->key(); // retrieve pressed key |
|
103 |
|
104 // auto-complete on pressed TAB (except for whitespace-only contents) |
|
105 if ((key == Qt::Key_Tab) && (!text().trimmed().isEmpty())) |
|
106 { |
|
107 autoComplete(); |
|
108 event->accept(); |
|
109 } |
|
110 // clear contents on pressed ESC |
|
111 else if ((event->key() == Qt::Key_Escape) && |
|
112 (event->modifiers() == Qt::NoModifier) |
|
113 ) |
|
114 clear(); |
|
115 // otherwise forward keys to parent |
|
116 else |
|
117 QLineEdit::keyPressEvent(event); |
|
118 } |
|
119 |
|
120 |
|
121 void SmartLineEdit::autoComplete() |
|
122 { |
|
123 QString match = ""; |
|
124 bool isNick = false; |
|
125 QString matchMe = text(); |
|
126 QString prefix = ""; |
|
127 QString postfix = ""; |
|
128 bool isFirstWord; |
|
129 |
|
130 // we are trying to rematch, so use the data from earlier |
|
131 if (m_hasJustMatched) |
|
132 { |
|
133 // restore values from earlier auto-completion |
|
134 matchMe = m_beforeMatch; |
|
135 prefix = m_prefix; |
|
136 postfix = m_postfix; |
|
137 isFirstWord = prefix.isEmpty(); |
|
138 } |
|
139 else |
|
140 { |
|
141 m_mutex.lock(); |
|
142 m_cmds->sort(); |
|
143 m_nicks->sort(); |
|
144 m_mutex.unlock(); |
|
145 |
|
146 int cp = cursorPosition(); |
|
147 |
|
148 // cursor is not in or at end/beginning of word |
|
149 if ((cp = matchMe.length()) || (QString(matchMe.at(cp)).contains(m_whitespace))) |
|
150 if ((cp < 1) || (QString(matchMe.at(cp-1)).contains(m_whitespace))) |
|
151 return; |
|
152 |
|
153 // crop matchMe at cursor position |
|
154 prefix = matchMe.left (cp); |
|
155 postfix = matchMe.right(matchMe.length()-cp); |
|
156 |
|
157 matchMe = ""; |
|
158 |
|
159 |
|
160 // use the whole word the curser is on for matching |
|
161 int prefixLen = prefix.lastIndexOf(m_whitespace) + 1; |
|
162 int preWordLen = prefix.length() - prefixLen; |
|
163 int postWordLen = postfix.indexOf(m_whitespace); |
|
164 int postfixLen = 0; |
|
165 if (postWordLen < 0) |
|
166 postWordLen = postfix.length(); |
|
167 else |
|
168 postfixLen = postfix.length() - (postWordLen + 1); |
|
169 |
|
170 matchMe = prefix.right(preWordLen) + postfix.left(postWordLen); |
|
171 prefix = prefix.left(prefixLen); |
|
172 postfix = postfix.right(postfixLen); |
|
173 |
|
174 |
|
175 isFirstWord = prefix.isEmpty(); // true if first word |
|
176 } |
|
177 |
|
178 |
|
179 m_mutex.lock(); |
|
180 |
|
181 if (isFirstWord) |
|
182 { |
|
183 // find matching commands |
|
184 foreach (const QString & cmd, *m_cmds) |
|
185 { |
|
186 if (cmd.startsWith(matchMe, Qt::CaseInsensitive)) |
|
187 { |
|
188 match = cmd; |
|
189 |
|
190 // move match to end so next time new matches will be prefered |
|
191 if (m_cmds->removeAll(cmd) > 0); |
|
192 m_cmds->append(cmd); |
|
193 |
|
194 break; |
|
195 } |
|
196 } |
|
197 } |
|
198 |
|
199 if (match.isEmpty()) |
|
200 { |
|
201 // find matching nicks |
|
202 foreach (const QString & nick, *m_nicks) |
|
203 { |
|
204 if (nick.startsWith(matchMe, Qt::CaseInsensitive)) |
|
205 { |
|
206 match = nick; |
|
207 isNick = true; |
|
208 |
|
209 // move match to end so next time new matches will be prefered |
|
210 if (m_nicks->removeAll(nick) > 0); |
|
211 m_nicks->append(nick); |
|
212 |
|
213 break; |
|
214 } |
|
215 } |
|
216 } |
|
217 |
|
218 m_mutex.unlock(); |
|
219 |
|
220 // we found a single match? |
|
221 if (!match.isEmpty()) |
|
222 { |
|
223 // replace last word with match |
|
224 // and append ':' if a name at the beginning of the matchMe got completed |
|
225 // also add a space at the very end to ease further typing |
|
226 QString addAfter = ((isNick && isFirstWord)?": ":" "); |
|
227 match = prefix + match + addAfter; |
|
228 setText(match + postfix); |
|
229 |
|
230 setCursorPosition(match.length()); |
|
231 |
|
232 // save values for for the case a rematch is requested |
|
233 m_beforeMatch = matchMe; |
|
234 m_hasJustMatched = true; |
|
235 m_prefix = prefix; |
|
236 m_postfix = postfix; |
|
237 } |
|
238 } |
|
239 |
|
240 void SmartLineEdit::reset() |
|
241 { |
|
242 m_beforeMatch = ""; |
|
243 m_hasJustMatched = false; |
|
244 m_prefix = ""; |
|
245 m_postfix = ""; |
|
246 } |