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