|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (developer.feedback@nokia.com) |
|
6 ** |
|
7 ** This file is part of the HbPlugins module of the UI Extensions for Mobile. |
|
8 ** |
|
9 ** GNU Lesser General Public License Usage |
|
10 ** This file may be used under the terms of the GNU Lesser General Public |
|
11 ** License version 2.1 as published by the Free Software Foundation and |
|
12 ** appearing in the file LICENSE.LGPL included in the packaging of this file. |
|
13 ** Please review the following information to ensure the GNU Lesser General |
|
14 ** Public License version 2.1 requirements will be met: |
|
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
16 ** |
|
17 ** In addition, as a special exception, Nokia gives you certain additional |
|
18 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
20 ** |
|
21 ** If you have questions regarding the use of this file, please contact |
|
22 ** Nokia at developer.feedback@nokia.com. |
|
23 ** |
|
24 ****************************************************************************/ |
|
25 |
|
26 #include <QTimer> |
|
27 #include <hbinputpredictionengine.h> |
|
28 #include <hbinputmethod.h> |
|
29 #include <hbinputkeymap.h> |
|
30 #include <hbinputkeymapfactory.h> |
|
31 |
|
32 #include "hbinputabstractbase.h" |
|
33 #include "hbinputbasicqwertyhandler.h" |
|
34 #include "hbinputbasichandler_p.h" |
|
35 |
|
36 class HbInputBasicQwertyHandlerPrivate: public HbInputBasicHandlerPrivate |
|
37 { |
|
38 Q_DECLARE_PUBLIC(HbInputBasicQwertyHandler) |
|
39 public: |
|
40 HbInputBasicQwertyHandlerPrivate(); |
|
41 ~HbInputBasicQwertyHandlerPrivate(); |
|
42 |
|
43 void init(); |
|
44 |
|
45 // button related operations. |
|
46 bool buttonPressed(const QKeyEvent *event); |
|
47 bool buttonReleased(const QKeyEvent *event); |
|
48 void _q_timeout(); |
|
49 |
|
50 public: |
|
51 HbFnState mFnState; |
|
52 int mButton; |
|
53 bool mPreviewAvailable; |
|
54 QChar mPrevDeadKey; |
|
55 HbInputFocusObject *mCurrentlyFocused; |
|
56 }; |
|
57 |
|
58 HbInputBasicQwertyHandlerPrivate::HbInputBasicQwertyHandlerPrivate() |
|
59 :mFnState(HbFnOff), |
|
60 mButton(0), |
|
61 mPreviewAvailable(false), |
|
62 mPrevDeadKey(0), |
|
63 mCurrentlyFocused(0) |
|
64 { |
|
65 } |
|
66 |
|
67 HbInputBasicQwertyHandlerPrivate::~HbInputBasicQwertyHandlerPrivate() |
|
68 { |
|
69 } |
|
70 |
|
71 void HbInputBasicQwertyHandlerPrivate::init() |
|
72 { |
|
73 } |
|
74 |
|
75 bool HbInputBasicQwertyHandlerPrivate::buttonPressed(const QKeyEvent * event) |
|
76 { |
|
77 if (!mKeymap->isDeadKey(event->key())) { |
|
78 mButton = event->key(); |
|
79 mTimer->start(HbLongPressTimerTimeout); |
|
80 mPreviewAvailable = false; |
|
81 } |
|
82 return false; |
|
83 } |
|
84 |
|
85 bool HbInputBasicQwertyHandlerPrivate::buttonReleased(const QKeyEvent *event) |
|
86 { |
|
87 Q_Q(HbInputBasicQwertyHandler); |
|
88 int buttonId = event->key(); |
|
89 QChar firstChar = 0; |
|
90 QChar secondChar = 0; |
|
91 |
|
92 // If the timer is not active and it is alpha mode, it is a long press |
|
93 // and handled in another function. So just return. |
|
94 if (mTimer->isActive()) { |
|
95 mTimer->stop(); |
|
96 } else if (buttonId == Qt::Key_Control) { |
|
97 return false; |
|
98 } else if (!(buttonId & 0xffff0000) && mPreviewAvailable) { |
|
99 return false; |
|
100 } |
|
101 |
|
102 HbInputFocusObject *focusObject = 0; |
|
103 focusObject = mInputMethod->focusObject(); |
|
104 if (!focusObject) { |
|
105 qDebug("HbInputBasicQwertyHandler::virtualButtonClicked : no focused editor widget!"); |
|
106 return false; |
|
107 } |
|
108 int currentTextCase = focusObject->editorInterface().textCase(); |
|
109 const HbMappedKey *mappedKey = mKeymap->keyForKeycode(HbKeyboardVirtualQwerty, event->key()); |
|
110 QChar newChar; |
|
111 if (mFnState == HbFnNext) { |
|
112 newChar = (mappedKey->characters(HbModifierFnPressed)).at(0); |
|
113 mFnState = HbFnOff; |
|
114 } else { |
|
115 if (currentTextCase == HbTextCaseLower) { |
|
116 if (mappedKey && mappedKey->characters(HbModifierNone).size() > 0) { |
|
117 newChar = mappedKey->characters(HbModifierNone).at(0); |
|
118 } else { |
|
119 newChar = buttonId; |
|
120 } |
|
121 } else { |
|
122 if (mappedKey && mappedKey->characters(HbModifierShiftPressed).size() > 0) { |
|
123 newChar = mappedKey->characters(HbModifierShiftPressed).at(0); |
|
124 } else { |
|
125 newChar = buttonId; |
|
126 } |
|
127 } |
|
128 } |
|
129 |
|
130 if (!mPrevDeadKey.isNull()) { |
|
131 if (event->key() != Qt::Key_Shift && event->key() != Qt::Key_Alt) { |
|
132 mKeymap->combineCharacter(mPrevDeadKey, newChar, firstChar, secondChar ); |
|
133 mPrevDeadKey = 0; |
|
134 if (firstChar.isNull() && secondChar.isNull()) { |
|
135 return true; |
|
136 } |
|
137 } |
|
138 } else { |
|
139 if (mKeymap->isDeadKey(newChar.unicode())) { |
|
140 mPrevDeadKey = newChar.unicode(); |
|
141 return true; |
|
142 } else { |
|
143 firstChar = newChar; |
|
144 } |
|
145 } |
|
146 |
|
147 bool ret = false; |
|
148 switch(buttonId) { |
|
149 case Qt::Key_Alt: |
|
150 if (mFnState == HbFnOff) { |
|
151 mFnState = HbFnNext; |
|
152 } else if (mFnState == HbFnNext) { |
|
153 mFnState = HbFnOn; |
|
154 } else { |
|
155 mFnState = HbFnOff; |
|
156 } |
|
157 ret = true; |
|
158 break; |
|
159 case Qt::Key_Shift: { |
|
160 HbTextCase currentTextCase = focusObject->editorInterface().textCase(); |
|
161 HbInputLanguage language = mInputMethod->inputState().language(); |
|
162 |
|
163 // Update the Case Information in HbInputState, it internally updates in HbEditorInterface as well |
|
164 switch(currentTextCase) { |
|
165 case HbTextCaseLower: |
|
166 // For Case-insensitive languages, Shift Key is used to switch between character sets (i.e lower case characters and shifted characters) |
|
167 if(!language.isCaseSensitiveLanguage()){ |
|
168 currentTextCase = HbTextCaseUpper; |
|
169 } |
|
170 else { |
|
171 currentTextCase = HbTextCaseAutomatic; |
|
172 } |
|
173 break; |
|
174 case HbTextCaseUpper: |
|
175 currentTextCase = HbTextCaseLower; |
|
176 break; |
|
177 case HbTextCaseAutomatic: |
|
178 currentTextCase = HbTextCaseUpper; |
|
179 break; |
|
180 default: |
|
181 break; |
|
182 } |
|
183 HbInputState state = mInputMethod->inputState(); |
|
184 state.setTextCase(currentTextCase); |
|
185 mInputMethod->activateState(state); |
|
186 ret = true; |
|
187 } |
|
188 break; |
|
189 case Qt::Key_Control: // Ctrl/Chr |
|
190 mInputMethod->switchSpecialCharacterTable(); |
|
191 ret = true; |
|
192 break; |
|
193 default: |
|
194 |
|
195 // let's pass non deadkey event to the base class. |
|
196 if (!mKeymap->isDeadKey(firstChar.unicode()) && secondChar.isNull() && q->HbInputBasicHandler::filterEvent(event)) { |
|
197 return true; |
|
198 } |
|
199 |
|
200 if (event->key() == Qt::Key_Delete || event->key() == Qt::Key_Backspace) { |
|
201 return false; |
|
202 } |
|
203 |
|
204 QList<QInputMethodEvent::Attribute> list; |
|
205 QString newText; |
|
206 // If function key is pressed, get the functionized |
|
207 // character and commit. |
|
208 |
|
209 if (mFnState == HbFnNext) { |
|
210 if (!mappedKey->characters(HbModifierFnPressed).isEmpty() && focusObject && focusObject->characterAllowedInEditor(mappedKey->characters(HbModifierFnPressed).at(0))) { |
|
211 QInputMethodEvent event(QString(), list); |
|
212 event.setCommitString(QString(mappedKey->characters(HbModifierFnPressed).at(0))); |
|
213 focusObject->sendEvent(event); |
|
214 } |
|
215 mFnState = HbFnOff; |
|
216 return true; |
|
217 } |
|
218 |
|
219 if (!firstChar.isNull()){ |
|
220 q->commitAndUpdate(firstChar); |
|
221 if (!secondChar.isNull() ) { |
|
222 if (!q->HbInputBasicHandler::filterEvent(event)) { |
|
223 q->commitAndUpdate(secondChar); |
|
224 } |
|
225 } |
|
226 |
|
227 // Need to refresh the autocompletion list |
|
228 refreshAutoCompleter(); |
|
229 ret = true; |
|
230 } |
|
231 |
|
232 break; |
|
233 } |
|
234 return ret; |
|
235 } |
|
236 |
|
237 void HbInputBasicQwertyHandlerPrivate::_q_timeout() |
|
238 { |
|
239 mTimer->stop(); |
|
240 |
|
241 //If long key press of shift key is received, just return |
|
242 if (mButton == Qt::Key_Shift) { |
|
243 return; |
|
244 } else if (mButton == Qt::Key_Control) { |
|
245 mInputMethod->selectSpecialCharacterTableMode(); |
|
246 } |
|
247 QStringList spellList; |
|
248 //If long key press of shift key, space key and enter key is received, don't |
|
249 if (mButton) { |
|
250 mInputMethod->launchCharacterPreviewPane(mButton); |
|
251 } |
|
252 |
|
253 return; |
|
254 } |
|
255 |
|
256 HbInputBasicQwertyHandler::HbInputBasicQwertyHandler(HbInputAbstractMethod* inputMethod) |
|
257 :HbInputBasicHandler(* new HbInputBasicQwertyHandlerPrivate, inputMethod) |
|
258 { |
|
259 Q_D(HbInputBasicQwertyHandler); |
|
260 d->q_ptr = this; |
|
261 d->init(); |
|
262 } |
|
263 |
|
264 |
|
265 /*! |
|
266 This function lists different input modes. |
|
267 */ |
|
268 void HbInputBasicQwertyHandler::listInputModes(QVector<HbInputModeProperties>& modes) const |
|
269 { |
|
270 HbInputModeProperties binding; |
|
271 binding.iMode = HbInputModeDefault; |
|
272 binding.iKeyboard = HbKeyboardVirtualQwerty; |
|
273 |
|
274 QList<HbInputLanguage> languages = HbKeymapFactory::availableLanguages(); |
|
275 foreach (HbInputLanguage language, languages) { |
|
276 binding.iLanguage = language; |
|
277 modes.push_back(binding); |
|
278 } |
|
279 } |
|
280 |
|
281 HbInputBasicQwertyHandler::~HbInputBasicQwertyHandler() |
|
282 { |
|
283 } |
|
284 |
|
285 /*! |
|
286 filterEvent function key handling. |
|
287 */ |
|
288 bool HbInputBasicQwertyHandler::filterEvent(const QKeyEvent* event) |
|
289 { |
|
290 Q_D(HbInputBasicQwertyHandler); |
|
291 |
|
292 if (event->type() == QEvent::KeyRelease) { |
|
293 return d->buttonReleased(event); |
|
294 } else { |
|
295 return d->buttonPressed(event); |
|
296 } |
|
297 } |
|
298 |
|
299 /*! |
|
300 returns true if in inline edit. |
|
301 */ |
|
302 bool HbInputBasicQwertyHandler::isComposing() const |
|
303 { |
|
304 Q_D(const HbInputBasicQwertyHandler); |
|
305 return d->mTimer->isActive(); |
|
306 } |
|
307 |
|
308 /*! |
|
309 Action handler |
|
310 */ |
|
311 bool HbInputBasicQwertyHandler::actionHandler(HbInputModeAction action) |
|
312 { |
|
313 Q_D(HbInputBasicQwertyHandler); |
|
314 HbInputFocusObject *focusObject = 0; |
|
315 focusObject = d->mInputMethod->focusObject(); |
|
316 bool ret = true; |
|
317 switch (action) { |
|
318 case HbInputModeActionCancelButtonPress: |
|
319 case HbInputModeActionReset: |
|
320 if (d->mTimer->isActive()) { |
|
321 d->mTimer->stop(); |
|
322 } |
|
323 break; |
|
324 case HbInputModeActionFocusRecieved: |
|
325 if (d->mTimer->isActive()) { |
|
326 d->mTimer->stop(); |
|
327 } |
|
328 // set up auto completer |
|
329 setUpAutoCompleter(); |
|
330 break; |
|
331 case HbInputModeActionFocusLost: |
|
332 // We should add the commit autocompleting text when focus lost happens |
|
333 if (d->mTimer->isActive()) { |
|
334 d->mTimer->stop(); |
|
335 } |
|
336 if (d->mCurrentlyFocused != focusObject) { |
|
337 d->mCurrentlyFocused = focusObject; |
|
338 addWordInAutoCompleter(); |
|
339 } |
|
340 break; |
|
341 default: { |
|
342 ret = HbInputBasicHandler::actionHandler(action); |
|
343 } |
|
344 } |
|
345 return ret; |
|
346 } |
|
347 |
|
348 /*! |
|
349 this SLOT is called by input plugin when there is a character selected from character preview pane. |
|
350 */ |
|
351 void HbInputBasicQwertyHandler::charFromPreviewSelected(QString characters) |
|
352 { |
|
353 Q_D(HbInputBasicQwertyHandler); |
|
354 if(characters.size() > 0) { |
|
355 sctCharacterSelected(characters.at(0)); |
|
356 d->mInputMethod->updateState(); |
|
357 } |
|
358 } |
|
359 |
|
360 void HbInputBasicQwertyHandler::sctCharacterSelected(QString character) |
|
361 { |
|
362 HbInputModeHandler::sctCharacterSelected(character); |
|
363 } |
|
364 |
|
365 void HbInputBasicQwertyHandler::characterPreviewAvailable(bool available) |
|
366 { |
|
367 Q_D(HbInputBasicQwertyHandler); |
|
368 d->mPreviewAvailable = available; |
|
369 } |