30 #include <hbinputpredictionengine.h> |
30 #include <hbinputpredictionengine.h> |
31 #include <hbinputsettingproxy.h> |
31 #include <hbinputsettingproxy.h> |
32 #include <hbinputvkbhost.h> |
32 #include <hbinputvkbhost.h> |
33 #include <hbinputdialog.h> |
33 #include <hbinputdialog.h> |
34 #include <hbaction.h> |
34 #include <hbaction.h> |
|
35 #include <hbmainwindow.h> |
|
36 #include <hbinstance.h> |
|
37 #include <hbeffect.h> |
|
38 |
35 #include "virtual12key.h" |
39 #include "virtual12key.h" |
36 |
40 |
37 #include "hbinputprediction12keyhandler.h" |
41 #include "hbinputprediction12keyhandler.h" |
38 #include "hbinputpredictionhandler_p.h" |
42 #include "hbinputpredictionhandler_p.h" |
39 #include "hbinputabstractbase.h" |
43 #include "hbinputabstractbase.h" |
|
44 #include "hbinputprediction12keyhandler_p.h" |
40 |
45 |
41 #define HbDeltaHeight 3.0 |
46 #define HbDeltaHeight 3.0 |
42 #define MAXUDBWORDSIZE 64 |
47 #define MAXUDBWORDSIZE 64 |
43 |
48 |
44 class HbInputPrediction12KeyHandlerPrivate: public HbInputPredictionHandlerPrivate |
49 HbInputSpellQuery::HbInputSpellQuery(HbInputPrediction12KeyHandlerPrivate *owner) : mOwner(owner) |
45 { |
50 { |
46 Q_DECLARE_PUBLIC(HbInputPrediction12KeyHandler) |
51 } |
47 |
52 |
48 public: |
53 void HbInputSpellQuery::launch(QString editorText) |
49 HbInputPrediction12KeyHandlerPrivate(); |
54 { |
50 ~HbInputPrediction12KeyHandlerPrivate(); |
55 HbInputFocusObject *focusObject = mOwner->mInputMethod->focusObject(); |
51 |
56 if (!focusObject) { |
52 bool buttonReleased(const QKeyEvent *keyEvent); |
57 return; |
53 bool buttonPressed(const QKeyEvent *keyEvent); |
58 } |
54 void _q_timeout(); |
59 mSavedState = mOwner->mInputMethod->inputState(); |
55 void launchSpellDialog(QString customWord); |
60 mOwner->mEngine->clear(); |
56 void getSpellDialogPositionAndSize(QPointF & pos,QSizeF & size,QRectF & geom); |
61 mOwner->mCanContinuePrediction = true; |
57 void cancelButtonPress(); |
62 // close the keypad before showing the spell dialog |
58 public: |
63 HbVkbHost *vkbHost = focusObject->editorInterface().vkbHost(); |
59 int mLastKey; |
64 if (vkbHost && vkbHost->keypadStatus() != HbVkbHost::HbVkbStatusClosed) { |
60 bool mButtonDown; |
65 vkbHost->closeKeypad(); |
61 QChar mCurrentChar; |
66 } |
62 bool mLongPressHappened; |
67 setInputMode(HbInputDialog::TextInput); |
63 bool mShiftKeyDoubleTap; |
68 setPromptText(tr("Spell:")); |
64 }; |
69 setValue(QVariant(editorText)); |
|
70 |
|
71 //set the spell dialog position |
|
72 QSizeF newSize; |
|
73 QPointF newPos; |
|
74 QRectF newGeometry; |
|
75 getPositionAndSize(newPos, newSize, newGeometry); |
|
76 newGeometry.setHeight(newSize.height()); |
|
77 newGeometry.setWidth(newSize.width()); |
|
78 setGeometry(newGeometry); |
|
79 setPos(newPos); |
|
80 |
|
81 // change the focus to spell dialog editor |
|
82 HbLineEdit *spellEdit = lineEdit(); |
|
83 if (spellEdit) { |
|
84 spellEdit->setMaxLength(MAXUDBWORDSIZE); |
|
85 spellEdit->setSmileysEnabled(false); |
|
86 HbEditorInterface eInt(spellEdit); |
|
87 // we don't want prediction and automatic textcase in spell query dialog |
|
88 spellEdit->setInputMethodHints(spellEdit->inputMethodHints() | Qt::ImhNoPredictiveText | Qt::ImhNoAutoUppercase); |
|
89 eInt.setLastFocusedState(mSavedState); |
|
90 spellEdit->setFocus(); |
|
91 } |
|
92 |
|
93 // execute the spell dialog |
|
94 mSavedFocusObject = focusObject->object(); |
|
95 mSavedEditorText = editorText; |
|
96 //setAttribute(Qt::WA_DeleteOnClose); |
|
97 mDidHandleFinish = false; |
|
98 open(this,SLOT(dialogClosed(HbAction*))); |
|
99 } |
|
100 |
|
101 void HbInputSpellQuery::dialogClosed(HbAction* action) |
|
102 { |
|
103 //There are multiple dialog closed event received. This will make sure we handle finish |
|
104 //only once |
|
105 if(mDidHandleFinish) { |
|
106 return; |
|
107 } else { |
|
108 mDidHandleFinish = true; |
|
109 } |
|
110 |
|
111 bool isOk = false; |
|
112 bool isCancel = false; |
|
113 bool isExternalClose = false; |
|
114 // action is null when input query is closed externally , for example by calling |
|
115 // HbDialog::close() function. |
|
116 if (action) { |
|
117 isOk = (action->text() == primaryAction()->text())? true : false; |
|
118 isCancel = (action->text() == secondaryAction()->text())? true:false; |
|
119 } else { |
|
120 isExternalClose = true; |
|
121 } |
|
122 |
|
123 //Need to disable effects as asynchronous hide will commit the word otherwise. |
|
124 HbEffect::disable(this); |
|
125 hide(); |
|
126 HbEffect::enable(this); |
|
127 |
|
128 HbInputFocusObject *newFocusObject = new HbInputFocusObject(mSavedFocusObject); |
|
129 newFocusObject->releaseFocus(); |
|
130 newFocusObject->setFocus(); |
|
131 |
|
132 HbAbstractEdit *abstractEdit = qobject_cast<HbAbstractEdit*>(mSavedFocusObject); |
|
133 |
|
134 if(abstractEdit) { |
|
135 abstractEdit->setCursorPosition(abstractEdit->cursorPosition()); |
|
136 } |
|
137 |
|
138 mOwner->mInputMethod->setFocusObject(newFocusObject); |
|
139 mOwner->mInputMethod->focusObject()->editorInterface().setTextCase(mSavedState.textCase()); |
|
140 |
|
141 if (isOk) { |
|
142 mOwner->commit(value().toString(), true, true); |
|
143 } else if (isCancel) { |
|
144 //update the editor with pre-edit text |
|
145 mOwner->mEngine->setWord(mSavedEditorText); |
|
146 bool used = false; |
|
147 mOwner->mEngine->updateCandidates(mOwner->mBestGuessLocation, used); |
|
148 mOwner->mShowTail = false; |
|
149 mOwner->updateEditor(); |
|
150 } else if (isExternalClose) { |
|
151 mOwner->commit(mSavedEditorText, true, true); |
|
152 } |
|
153 |
|
154 mSavedEditorText.clear(); |
|
155 } |
|
156 |
|
157 void HbInputSpellQuery::getPositionAndSize(QPointF &pos,QSizeF &size, QRectF &geom) |
|
158 { |
|
159 pos = HbInputDialog::pos(); |
|
160 size = HbInputDialog::size(); |
|
161 geom = HbInputDialog::geometry(); |
|
162 |
|
163 QRectF cursorRect = mOwner->mInputMethod->focusObject()->microFocus(); // from the top of the screen |
|
164 pos = QPointF(cursorRect.bottomLeft().x(),cursorRect.bottomLeft().y()); |
|
165 qreal heightOfTitlebar = 80.0; // Using magic number for now... |
|
166 qreal screenHeight = (qreal)HbDeviceProfile::current().logicalSize().height(); |
|
167 |
|
168 if( ((screenHeight - cursorRect.bottomLeft().y()) > (cursorRect.y() - heightOfTitlebar)) |
|
169 || ((screenHeight - cursorRect.bottomLeft().y() + HbDeltaHeight ) > geom.height()) ) { |
|
170 // this means there is amore space below inline text than at the top or we can fit spell Dialog |
|
171 // below inline text |
|
172 pos.setY(cursorRect.bottomLeft().y() + HbDeltaHeight); |
|
173 size.setHeight(screenHeight - pos.y()); |
|
174 } else { |
|
175 // this means there is amore space above inline text than below it |
|
176 pos.setY(cursorRect.y() - geom.height() - HbDeltaHeight); |
|
177 if (pos.y() < heightOfTitlebar) { |
|
178 // this means that spell dialog can not be fit in from top of inline text, we need to trim it |
|
179 pos.setY(heightOfTitlebar); |
|
180 } |
|
181 size.setHeight(cursorRect.y() - heightOfTitlebar - HbDeltaHeight); |
|
182 } |
|
183 if ( size.height() > geom.height()) { |
|
184 size.setHeight(geom.height()); |
|
185 } |
|
186 if ((pos.x() + size.width()) > (qreal)HbDeviceProfile::current().logicalSize().width()) { |
|
187 // can not fit spell dialog to the right side of inline edit text. |
|
188 pos.setX((qreal)HbDeviceProfile::current().logicalSize().width()- size.width()); |
|
189 } |
|
190 } |
65 |
191 |
66 HbInputPrediction12KeyHandlerPrivate::HbInputPrediction12KeyHandlerPrivate() |
192 HbInputPrediction12KeyHandlerPrivate::HbInputPrediction12KeyHandlerPrivate() |
67 :mLastKey(0), |
193 :mLastKey(0), |
68 mButtonDown(false), |
194 mButtonDown(false), |
69 mCurrentChar(0), |
195 mCurrentChar(0), |
70 mLongPressHappened(false), |
196 mLongPressHappened(false), |
71 mShiftKeyDoubleTap(false) |
197 mShiftKeyDoubleTap(false), |
|
198 mInputSpellQuery(NULL) |
72 { |
199 { |
73 } |
200 } |
74 |
201 |
75 HbInputPrediction12KeyHandlerPrivate::~HbInputPrediction12KeyHandlerPrivate() |
202 HbInputPrediction12KeyHandlerPrivate::~HbInputPrediction12KeyHandlerPrivate() |
76 { |
203 { |
|
204 delete mInputSpellQuery; |
|
205 mInputSpellQuery = 0; |
|
206 } |
|
207 |
|
208 void HbInputPrediction12KeyHandlerPrivate::chopQMarkAndUpdateEditor() |
|
209 { |
|
210 if(!mCanContinuePrediction && (*mCandidates)[mBestGuessLocation].endsWith('?')) { |
|
211 (*mCandidates)[mBestGuessLocation].chop(1); |
|
212 updateEditor(); |
|
213 mCanContinuePrediction = true; |
|
214 } |
77 } |
215 } |
78 |
216 |
79 void HbInputPrediction12KeyHandlerPrivate::_q_timeout() |
217 void HbInputPrediction12KeyHandlerPrivate::_q_timeout() |
80 { |
218 { |
81 qDebug("HbInputPrediction12KeyHandlerPrivate::_q_timeout()"); |
219 qDebug("HbInputPrediction12KeyHandlerPrivate::_q_timeout()"); |
86 |
224 |
87 //Long key press number key is applicable to all keys |
225 //Long key press number key is applicable to all keys |
88 if (mButtonDown) { |
226 if (mButtonDown) { |
89 if (mLastKey == Qt::Key_Asterisk) { |
227 if (mLastKey == Qt::Key_Asterisk) { |
90 //Remove the "?" mark if present |
228 //Remove the "?" mark if present |
91 if(!mCanContinuePrediction && (*mCandidates)[mBestGuessLocation].endsWith('?')) { |
229 chopQMarkAndUpdateEditor(); |
92 (*mCandidates)[mBestGuessLocation].chop(1); |
|
93 updateEditor(); |
|
94 mCanContinuePrediction = true; |
|
95 } |
|
96 mInputMethod->switchMode(mLastKey); |
230 mInputMethod->switchMode(mLastKey); |
97 } else if (mLastKey == Qt::Key_Shift) { |
231 } else if (mLastKey == Qt::Key_Shift) { |
98 mInputMethod->switchMode(Qt::Key_Shift); |
232 mInputMethod->switchMode(Qt::Key_Shift); |
99 mLongPressHappened = true; |
233 mLongPressHappened = true; |
100 } else if (mLastKey == Qt::Key_Control) { |
234 } else if (mLastKey == Qt::Key_Control) { |
|
235 //Remove the "?" mark if present |
|
236 chopQMarkAndUpdateEditor(); |
101 mInputMethod->selectSpecialCharacterTableMode(); |
237 mInputMethod->selectSpecialCharacterTableMode(); |
102 } else { |
238 } else { |
103 //With a long key press of a key, numbers are supposed to be entered. |
239 //With a long key press of a key, numbers are supposed to be entered. |
104 //When the existing input (along with the short key press input of the |
240 //When the existing input (along with the short key press input of the |
105 //existing key) results in tail (i.e. autocompletion), we need to accept |
241 //existing key) results in tail (i.e. autocompletion), we need to accept |
247 return true; |
383 return true; |
248 } |
384 } |
249 return false; |
385 return false; |
250 } |
386 } |
251 |
387 |
252 void HbInputPrediction12KeyHandlerPrivate::launchSpellDialog(QString editorText) |
|
253 { |
|
254 HbInputFocusObject *focusObject = mInputMethod->focusObject(); |
|
255 if (!focusObject) { |
|
256 return; |
|
257 } |
|
258 QPointer<QObject> focusedQObject = focusObject->object(); |
|
259 // store the current focused editor |
|
260 |
|
261 HbTextCase currentTextCase = focusObject->editorInterface().textCase(); |
|
262 mEngine->clear(); |
|
263 mCanContinuePrediction = true; |
|
264 // close the keypad before showing the spell dialog |
|
265 HbVkbHost *vkbHost = focusObject->editorInterface().vkbHost(); |
|
266 if (vkbHost && vkbHost->keypadStatus() != HbVkbHost::HbVkbStatusClosed) { |
|
267 vkbHost->closeKeypad(true); |
|
268 } |
|
269 // create the spell dialog |
|
270 HbInputDialog *spellDialog = new HbInputDialog(); |
|
271 spellDialog->setInputMode(HbInputDialog::TextInput); |
|
272 spellDialog->setPromptText(""); |
|
273 spellDialog->setValue(QVariant(editorText)); |
|
274 QSizeF dialogSize = spellDialog->size(); |
|
275 QPointF dialogPos = spellDialog->pos(); |
|
276 QRectF geom = spellDialog->geometry(); |
|
277 |
|
278 //set the spell dialog position |
|
279 getSpellDialogPositionAndSize(dialogPos,dialogSize, geom); |
|
280 geom.setHeight(dialogSize.height()); |
|
281 geom.setWidth(dialogSize.width()); |
|
282 spellDialog->setGeometry(geom); |
|
283 spellDialog->setPos(dialogPos); |
|
284 |
|
285 // change the focus to spell dialog editor |
|
286 HbLineEdit *spellEdit = spellDialog->lineEdit(); |
|
287 |
|
288 if (spellEdit) { |
|
289 spellEdit->setFocus(); |
|
290 spellEdit->clearFocus(); |
|
291 spellEdit->setFocus(); |
|
292 spellEdit->setMaxLength(MAXUDBWORDSIZE); |
|
293 spellEdit->setSmileysEnabled(false); |
|
294 HbEditorInterface eInt(spellEdit); |
|
295 spellEdit->setInputMethodHints(spellEdit->inputMethodHints() | Qt::ImhNoPredictiveText); |
|
296 eInt.setTextCase(currentTextCase); |
|
297 } |
|
298 // execute the spell dialog |
|
299 HbAction *act = spellDialog->exec(); |
|
300 |
|
301 //create new focus object and set the focus back to main editor |
|
302 HbInputFocusObject *newFocusObject = new HbInputFocusObject(focusedQObject); |
|
303 |
|
304 |
|
305 newFocusObject->releaseFocus(); |
|
306 newFocusObject->setFocus(); |
|
307 |
|
308 HbAbstractEdit *abstractEdit = qobject_cast<HbAbstractEdit*>(focusedQObject); |
|
309 if(abstractEdit) { |
|
310 abstractEdit->setCursorPosition(abstractEdit->cursorPosition()); |
|
311 } |
|
312 |
|
313 mInputMethod->setFocusObject(newFocusObject); |
|
314 mInputMethod->focusObject()->editorInterface().setTextCase(currentTextCase); |
|
315 |
|
316 if (act->text() == spellDialog->primaryAction()->text()) { |
|
317 commit(spellDialog->value().toString() , true, true); |
|
318 } else if (act->text() == spellDialog->secondaryAction()->text()) { |
|
319 //update the editor with pre-edit text |
|
320 mEngine->setWord(editorText); |
|
321 bool used = false; |
|
322 mEngine->updateCandidates(mBestGuessLocation, used); |
|
323 mShowTail = false; |
|
324 updateEditor(); |
|
325 } |
|
326 delete spellDialog; |
|
327 } |
|
328 |
|
329 void HbInputPrediction12KeyHandlerPrivate::getSpellDialogPositionAndSize(QPointF & pos,QSizeF & size, QRectF &geom) |
|
330 { |
|
331 QRectF cursorRect = mInputMethod->focusObject()->microFocus(); // from the top of the screen |
|
332 pos = QPointF(cursorRect.bottomLeft().x(),cursorRect.bottomLeft().y()); |
|
333 qreal heightOfTitlebar = 80.0; // Using magic number for now... |
|
334 qreal screenHeight = (qreal)HbDeviceProfile::current().logicalSize().height(); |
|
335 if( ((screenHeight - cursorRect.bottomLeft().y()) > (cursorRect.y() - heightOfTitlebar)) |
|
336 || ((screenHeight - cursorRect.bottomLeft().y() + HbDeltaHeight ) > geom.height()) ) { |
|
337 // this means there is amore space below inline text than at the top or we can fit spell Dialog |
|
338 // below inline text |
|
339 pos.setY(cursorRect.bottomLeft().y() + HbDeltaHeight); |
|
340 size.setHeight(screenHeight - pos.y()); |
|
341 } else { |
|
342 // this means there is amore space above inline text than below it |
|
343 pos.setY(cursorRect.y() - geom.height() - HbDeltaHeight); |
|
344 if (pos.y() < heightOfTitlebar) { |
|
345 // this means that spell dialog can not be fit in from top of inline text, we need to trim it |
|
346 pos.setY(heightOfTitlebar); |
|
347 } |
|
348 size.setHeight(cursorRect.y() - heightOfTitlebar - HbDeltaHeight); |
|
349 } |
|
350 if ( size.height() > geom.height()) { |
|
351 size.setHeight(geom.height()); |
|
352 } |
|
353 if ((pos.x() + size.width()) > (qreal)HbDeviceProfile::current().logicalSize().width()) { |
|
354 // can not fit spell dialog to the right side of inline edit text. |
|
355 pos.setX((qreal)HbDeviceProfile::current().logicalSize().width()- size.width()); |
|
356 } |
|
357 } |
|
358 |
388 |
359 void HbInputPrediction12KeyHandlerPrivate::cancelButtonPress() |
389 void HbInputPrediction12KeyHandlerPrivate::cancelButtonPress() |
360 { |
390 { |
361 mTimer->stop(); |
391 mTimer->stop(); |
362 mButtonDown = false; |
392 mButtonDown = false; |