source: trunk/src/gui/inputmethod/qcoefepinputcontext_s60.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

  • Property svn:eol-style set to native
File size: 33.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#ifndef QT_NO_IM
43
44#include "qcoefepinputcontext_p.h"
45#include <qapplication.h>
46#include <qtextformat.h>
47#include <qgraphicsview.h>
48#include <qgraphicsscene.h>
49#include <qgraphicswidget.h>
50#include <qsymbianevent.h>
51#include <private/qcore_symbian_p.h>
52
53#include <fepitfr.h>
54#include <hal.h>
55
56#include <limits.h>
57// You only find these enumerations on SDK 5 onwards, so we need to provide our own
58// to remain compatible with older releases. They won't be called by pre-5.0 SDKs.
59
60// MAknEdStateObserver::EAknCursorPositionChanged
61#define QT_EAknCursorPositionChanged MAknEdStateObserver::EAknEdwinStateEvent(6)
62// MAknEdStateObserver::EAknActivatePenInputRequest
63#define QT_EAknActivatePenInputRequest MAknEdStateObserver::EAknEdwinStateEvent(7)
64
65// EAknEditorFlagSelectionVisible is only valid from 3.2 onwards.
66// Sym^3 AVKON FEP manager expects that this flag is used for FEP-aware editors
67// that support text selection.
68#define QT_EAknEditorFlagSelectionVisible 0x100000
69
70QT_BEGIN_NAMESPACE
71
72QCoeFepInputContext::QCoeFepInputContext(QObject *parent)
73 : QInputContext(parent),
74 m_fepState(q_check_ptr(new CAknEdwinState)), // CBase derived object needs check on new
75 m_lastImHints(Qt::ImhNone),
76 m_textCapabilities(TCoeInputCapabilities::EAllText),
77 m_inDestruction(false),
78 m_pendingInputCapabilitiesChanged(false),
79 m_cursorVisibility(1),
80 m_inlinePosition(0),
81 m_formatRetriever(0),
82 m_pointerHandler(0),
83 m_hasTempPreeditString(false)
84{
85 m_fepState->SetObjectProvider(this);
86 if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0)
87 m_fepState->SetFlags(EAknEditorFlagDefault | QT_EAknEditorFlagSelectionVisible);
88 else
89 m_fepState->SetFlags(EAknEditorFlagDefault);
90 m_fepState->SetDefaultInputMode( EAknEditorTextInputMode );
91 m_fepState->SetPermittedInputModes( EAknEditorAllInputModes );
92 m_fepState->SetDefaultCase( EAknEditorTextCase );
93 m_fepState->SetPermittedCases( EAknEditorAllCaseModes );
94 m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_SPECIAL_CHARACTER_TABLE_DIALOG);
95 m_fepState->SetNumericKeymap( EAknEditorStandardNumberModeKeymap );
96}
97
98QCoeFepInputContext::~QCoeFepInputContext()
99{
100 m_inDestruction = true;
101
102 // This is to make sure that the FEP manager "forgets" about us,
103 // otherwise we may get callbacks even after we're destroyed.
104 // The call below is essentially equivalent to InputCapabilitiesChanged(),
105 // but is synchronous, rather than asynchronous.
106 CCoeEnv::Static()->SyncNotifyFocusObserversOfChangeInFocus();
107
108 if (m_fepState)
109 delete m_fepState;
110}
111
112void QCoeFepInputContext::reset()
113{
114 commitCurrentString(true);
115}
116
117void QCoeFepInputContext::ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateEvent aEventType)
118{
119 QT_TRAP_THROWING(m_fepState->ReportAknEdStateEventL(aEventType));
120}
121
122void QCoeFepInputContext::update()
123{
124 updateHints(false);
125
126 // For pre-5.0 SDKs, we don't do text updates on S60 side.
127 if (QSysInfo::s60Version() < QSysInfo::SV_S60_5_0) {
128 return;
129 }
130
131 // Don't be fooled (as I was) by the name of this enumeration.
132 // What it really does is tell the virtual keyboard UI that the text has been
133 // updated and it should be reflected in the internal display of the VK.
134 ReportAknEdStateEvent(QT_EAknCursorPositionChanged);
135}
136
137void QCoeFepInputContext::setFocusWidget(QWidget *w)
138{
139 commitCurrentString(true);
140
141 QInputContext::setFocusWidget(w);
142
143 updateHints(true);
144}
145
146void QCoeFepInputContext::widgetDestroyed(QWidget *w)
147{
148 // Make sure that the input capabilities of whatever new widget got focused are queried.
149 CCoeControl *ctrl = w->effectiveWinId();
150 if (ctrl->IsFocused()) {
151 queueInputCapabilitiesChanged();
152 }
153}
154
155QString QCoeFepInputContext::language()
156{
157 TLanguage lang = m_fepState->LocalLanguage();
158 const QByteArray localeName = qt_symbianLocaleName(lang);
159 if (!localeName.isEmpty()) {
160 return QString::fromLatin1(localeName);
161 } else {
162 return QString::fromLatin1("C");
163 }
164}
165
166bool QCoeFepInputContext::needsInputPanel()
167{
168 switch (QSysInfo::s60Version()) {
169 case QSysInfo::SV_S60_3_1:
170 case QSysInfo::SV_S60_3_2:
171 // There are no touch phones for pre-5.0 SDKs.
172 return false;
173#ifdef Q_CC_NOKIAX86
174 default:
175 // For emulator we assume that we need an input panel, since we can't
176 // separate between phone types.
177 return true;
178#else
179 case QSysInfo::SV_S60_5_0: {
180 // For SDK == 5.0, we need phone specific detection, since the HAL API
181 // is no good on most phones. However, all phones at the time of writing use the
182 // input panel, except N97 in landscape mode, but in this mode it refuses to bring
183 // up the panel anyway, so we don't have to care.
184 return true;
185 }
186 default:
187 // For unknown/newer types, we try to use the HAL API.
188 int keyboardEnabled;
189 int keyboardType;
190 int err[2];
191 err[0] = HAL::Get(HAL::EKeyboard, keyboardType);
192 err[1] = HAL::Get(HAL::EKeyboardState, keyboardEnabled);
193 if (err[0] == KErrNone && err[1] == KErrNone
194 && keyboardType != 0 && keyboardEnabled)
195 // Means that we have some sort of keyboard.
196 return false;
197
198 // Fall back to using the input panel.
199 return true;
200#endif // !Q_CC_NOKIAX86
201 }
202}
203
204bool QCoeFepInputContext::filterEvent(const QEvent *event)
205{
206 // The CloseSoftwareInputPanel event is not handled here, because the VK will automatically
207 // close when it discovers that the underlying widget does not have input capabilities.
208
209 if (!focusWidget())
210 return false;
211
212 switch (event->type()) {
213 case QEvent::KeyPress:
214 commitTemporaryPreeditString();
215 // fall through intended
216 case QEvent::KeyRelease:
217 const QKeyEvent *keyEvent = static_cast<const QKeyEvent *>(event);
218 switch (keyEvent->key()) {
219 case Qt::Key_F20:
220 Q_ASSERT(m_lastImHints == focusWidget()->inputMethodHints());
221 if (m_lastImHints & Qt::ImhHiddenText) {
222 // Special case in Symbian. On editors with secret text, F20 is for some reason
223 // considered to be a backspace.
224 QKeyEvent modifiedEvent(keyEvent->type(), Qt::Key_Backspace, keyEvent->modifiers(),
225 keyEvent->text(), keyEvent->isAutoRepeat(), keyEvent->count());
226 QApplication::sendEvent(focusWidget(), &modifiedEvent);
227 return true;
228 }
229 break;
230 case Qt::Key_Select:
231 if (!m_preeditString.isEmpty()) {
232 commitCurrentString(true);
233 return true;
234 }
235 break;
236 default:
237 break;
238 }
239
240 QString widgetText = focusWidget()->inputMethodQuery(Qt::ImSurroundingText).toString();
241 bool validLength;
242 int maxLength = focusWidget()->inputMethodQuery(Qt::ImMaximumTextLength).toInt(&validLength);
243 if (!keyEvent->text().isEmpty() && validLength
244 && widgetText.size() + m_preeditString.size() >= maxLength) {
245 // Don't send key events with string content if the widget is "full".
246 return true;
247 }
248
249 if (keyEvent->type() == QEvent::KeyPress
250 && focusWidget()->inputMethodHints() & Qt::ImhHiddenText
251 && !keyEvent->text().isEmpty()) {
252 // Send some temporary preedit text in order to make text visible for a moment.
253 m_preeditString = keyEvent->text();
254 QList<QInputMethodEvent::Attribute> attributes;
255 QInputMethodEvent imEvent(m_preeditString, attributes);
256 sendEvent(imEvent);
257 m_tempPreeditStringTimeout.start(1000, this);
258 m_hasTempPreeditString = true;
259 update();
260 return true;
261 }
262 break;
263 }
264
265 if (!needsInputPanel())
266 return false;
267
268 if (event->type() == QEvent::RequestSoftwareInputPanel) {
269 // Notify S60 that we want the virtual keyboard to show up.
270 QSymbianControl *sControl;
271 sControl = focusWidget()->effectiveWinId()->MopGetObject(sControl);
272 Q_ASSERT(sControl);
273
274 // The FEP UI temporarily steals focus when it shows up the first time, causing
275 // all sorts of weird effects on the focused widgets. Since it will immediately give
276 // back focus to us, we temporarily disable focus handling until the job's done.
277 if (sControl) {
278 sControl->setIgnoreFocusChanged(true);
279 }
280
281 ensureInputCapabilitiesChanged();
282 m_fepState->ReportAknEdStateEventL(MAknEdStateObserver::QT_EAknActivatePenInputRequest);
283
284 if (sControl) {
285 sControl->setIgnoreFocusChanged(false);
286 }
287 return true;
288 }
289
290 return false;
291}
292
293bool QCoeFepInputContext::symbianFilterEvent(QWidget *keyWidget, const QSymbianEvent *event)
294{
295 Q_UNUSED(keyWidget);
296 if (event->type() == QSymbianEvent::CommandEvent)
297 // A command basically means the same as a button being pushed. With Qt buttons
298 // that would normally result in a reset of the input method due to the focus change.
299 // This should also happen for commands.
300 reset();
301
302 return false;
303}
304
305void QCoeFepInputContext::timerEvent(QTimerEvent *timerEvent)
306{
307 if (timerEvent->timerId() == m_tempPreeditStringTimeout.timerId())
308 commitTemporaryPreeditString();
309}
310
311void QCoeFepInputContext::commitTemporaryPreeditString()
312{
313 if (m_tempPreeditStringTimeout.isActive())
314 m_tempPreeditStringTimeout.stop();
315
316 if (!m_hasTempPreeditString)
317 return;
318
319 commitCurrentString(false);
320}
321
322void QCoeFepInputContext::mouseHandler( int x, QMouseEvent *event)
323{
324 Q_ASSERT(focusWidget());
325
326 if (event->type() == QEvent::MouseButtonPress && event->button() == Qt::LeftButton) {
327 commitCurrentString(true);
328 int pos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt();
329
330 QList<QInputMethodEvent::Attribute> attributes;
331 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, pos + x, 0, QVariant());
332 QInputMethodEvent event(QLatin1String(""), attributes);
333 sendEvent(event);
334 }
335}
336
337TCoeInputCapabilities QCoeFepInputContext::inputCapabilities()
338{
339 if (m_inDestruction || !focusWidget()) {
340 return TCoeInputCapabilities(TCoeInputCapabilities::ENone, 0, 0);
341 }
342
343 return TCoeInputCapabilities(m_textCapabilities, this, 0);
344}
345
346static QTextCharFormat qt_TCharFormat2QTextCharFormat(const TCharFormat &cFormat, bool validStyleColor)
347{
348 QTextCharFormat qFormat;
349
350 if (validStyleColor) {
351 QBrush foreground(QColor(cFormat.iFontPresentation.iTextColor.Internal()));
352 qFormat.setForeground(foreground);
353 }
354
355 qFormat.setFontStrikeOut(cFormat.iFontPresentation.iStrikethrough == EStrikethroughOn);
356 qFormat.setFontUnderline(cFormat.iFontPresentation.iUnderline == EUnderlineOn);
357
358 return qFormat;
359}
360
361void QCoeFepInputContext::updateHints(bool mustUpdateInputCapabilities)
362{
363 QWidget *w = focusWidget();
364 if (w) {
365 Qt::InputMethodHints hints = w->inputMethodHints();
366 if (hints != m_lastImHints) {
367 m_lastImHints = hints;
368 applyHints(hints);
369 } else if (!mustUpdateInputCapabilities) {
370 // Optimization. Return immediately if there was no change.
371 return;
372 }
373 }
374 queueInputCapabilitiesChanged();
375}
376
377void QCoeFepInputContext::applyHints(Qt::InputMethodHints hints)
378{
379 using namespace Qt;
380
381 commitTemporaryPreeditString();
382
383 const bool anynumbermodes = hints & (ImhDigitsOnly | ImhFormattedNumbersOnly | ImhDialableCharactersOnly);
384 const bool anytextmodes = hints & (ImhUppercaseOnly | ImhLowercaseOnly | ImhEmailCharactersOnly | ImhUrlCharactersOnly);
385 const bool numbersOnly = anynumbermodes && !anytextmodes;
386 const bool noOnlys = !(hints & ImhExclusiveInputMask);
387 // if alphanumeric input, or if multiple incompatible number modes are selected;
388 // then make all symbols available in numeric mode too.
389 const bool needsCharMap= !numbersOnly || ((hints & ImhFormattedNumbersOnly) && (hints & ImhDialableCharactersOnly));
390 TInt flags;
391 Qt::InputMethodHints oldHints = hints;
392
393 // Some sanity checking. Make sure that only one preference is set.
394 InputMethodHints prefs = ImhPreferNumbers | ImhPreferUppercase | ImhPreferLowercase;
395 prefs &= hints;
396 if (prefs != ImhPreferNumbers && prefs != ImhPreferUppercase && prefs != ImhPreferLowercase) {
397 hints &= ~prefs;
398 }
399 if (!noOnlys) {
400 // Make sure that the preference is within the permitted set.
401 if (hints & ImhPreferNumbers && !anynumbermodes) {
402 hints &= ~ImhPreferNumbers;
403 } else if (hints & ImhPreferUppercase && !(hints & ImhUppercaseOnly)) {
404 hints &= ~ImhPreferUppercase;
405 } else if (hints & ImhPreferLowercase && !(hints & ImhLowercaseOnly)) {
406 hints &= ~ImhPreferLowercase;
407 }
408 // If there is no preference, set it to something within the permitted set.
409 if (!(hints & ImhPreferNumbers || hints & ImhPreferUppercase || hints & ImhPreferLowercase)) {
410 if (hints & ImhLowercaseOnly) {
411 hints |= ImhPreferLowercase;
412 } else if (hints & ImhUppercaseOnly) {
413 hints |= ImhPreferUppercase;
414 } else if (numbersOnly) {
415 hints |= ImhPreferNumbers;
416 }
417 }
418 }
419
420 if (hints & ImhPreferNumbers) {
421 m_fepState->SetDefaultInputMode(EAknEditorNumericInputMode);
422 m_fepState->SetCurrentInputMode(EAknEditorNumericInputMode);
423 } else {
424 m_fepState->SetDefaultInputMode(EAknEditorTextInputMode);
425 m_fepState->SetCurrentInputMode(EAknEditorTextInputMode);
426 }
427 flags = 0;
428 if (noOnlys || (anynumbermodes && anytextmodes)) {
429 flags = EAknEditorAllInputModes;
430 }
431 else if (anynumbermodes) {
432 flags |= EAknEditorNumericInputMode;
433 if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0
434 && ((hints & ImhFormattedNumbersOnly) || (hints & ImhDialableCharactersOnly))) {
435 //workaround - the * key does not launch the symbols menu, making it impossible to use these modes unless text mode is enabled.
436 flags |= EAknEditorTextInputMode;
437 }
438 }
439 else if (anytextmodes) {
440 flags |= EAknEditorTextInputMode;
441 }
442 else {
443 flags = EAknEditorAllInputModes;
444 }
445 m_fepState->SetPermittedInputModes(flags);
446 ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateInputModeUpdate);
447
448 if (hints & ImhPreferLowercase) {
449 m_fepState->SetDefaultCase(EAknEditorLowerCase);
450 m_fepState->SetCurrentCase(EAknEditorLowerCase);
451 } else if (hints & ImhPreferUppercase) {
452 m_fepState->SetDefaultCase(EAknEditorUpperCase);
453 m_fepState->SetCurrentCase(EAknEditorUpperCase);
454 } else if (hints & ImhNoAutoUppercase) {
455 m_fepState->SetDefaultCase(EAknEditorLowerCase);
456 m_fepState->SetCurrentCase(EAknEditorLowerCase);
457 } else {
458 m_fepState->SetDefaultCase(EAknEditorTextCase);
459 m_fepState->SetCurrentCase(EAknEditorTextCase);
460 }
461 flags = 0;
462 if (hints & ImhUppercaseOnly) {
463 flags |= EAknEditorUpperCase;
464 }
465 if (hints & ImhLowercaseOnly) {
466 flags |= EAknEditorLowerCase;
467 }
468 if (flags == 0) {
469 flags = EAknEditorAllCaseModes;
470 if (hints & ImhNoAutoUppercase) {
471 flags &= ~EAknEditorTextCase;
472 }
473 }
474 m_fepState->SetPermittedCases(flags);
475 ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateCaseModeUpdate);
476
477 if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0)
478 flags = QT_EAknEditorFlagSelectionVisible;
479 else
480 flags = 0;
481 if (hints & ImhUppercaseOnly && !(hints & ImhLowercaseOnly)
482 || hints & ImhLowercaseOnly && !(hints & ImhUppercaseOnly)) {
483 flags |= EAknEditorFlagFixedCase;
484 }
485 // Using T9 and hidden text together may actually crash the FEP, so check for hidden text too.
486 if (hints & ImhNoPredictiveText || hints & ImhHiddenText) {
487 flags |= EAknEditorFlagNoT9;
488 }
489 if (needsCharMap)
490 flags |= EAknEditorFlagUseSCTNumericCharmap;
491 m_fepState->SetFlags(flags);
492 ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateFlagsUpdate);
493
494 if (hints & ImhDialableCharactersOnly) {
495 // This is first, because if (ImhDialableCharactersOnly | ImhFormattedNumbersOnly)
496 // is specified, this one is more natural (# key enters a #)
497 flags = EAknEditorStandardNumberModeKeymap;
498 } else if (hints & ImhFormattedNumbersOnly) {
499 // # key enters decimal point
500 flags = EAknEditorCalculatorNumberModeKeymap;
501 } else if (hints & ImhDigitsOnly) {
502 // This is last, because it is most restrictive (# key is inactive)
503 flags = EAknEditorPlainNumberModeKeymap;
504 } else {
505 flags = EAknEditorStandardNumberModeKeymap;
506 }
507 m_fepState->SetNumericKeymap(static_cast<TAknEditorNumericKeymap>(flags));
508
509 if (hints & ImhUrlCharactersOnly) {
510 // URL characters is everything except space, so a superset of the other restrictions
511 m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_URL_SPECIAL_CHARACTER_TABLE_DIALOG);
512 } else if (hints & ImhEmailCharactersOnly) {
513 m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_EMAIL_ADDR_SPECIAL_CHARACTER_TABLE_DIALOG);
514 } else if (needsCharMap) {
515 m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_SPECIAL_CHARACTER_TABLE_DIALOG);
516 } else {
517 m_fepState->SetSpecialCharacterTableResourceId(0);
518 }
519
520 if (hints & ImhHiddenText) {
521 m_textCapabilities = TCoeInputCapabilities::EAllText | TCoeInputCapabilities::ESecretText;
522 } else {
523 m_textCapabilities = TCoeInputCapabilities::EAllText;
524 }
525}
526
527void QCoeFepInputContext::applyFormat(QList<QInputMethodEvent::Attribute> *attributes)
528{
529 TCharFormat cFormat;
530 QColor styleTextColor;
531 if (QWidget *focused = focusWidget()) {
532 QGraphicsView *gv = qobject_cast<QGraphicsView*>(focused);
533 if (!gv) // could be either the QGV or its viewport that has focus
534 gv = qobject_cast<QGraphicsView*>(focused->parentWidget());
535 if (gv) {
536 if (QGraphicsScene *scene = gv->scene()) {
537 if (QGraphicsItem *focusItem = scene->focusItem()) {
538 if (focusItem->isWidget()) {
539 styleTextColor = static_cast<QGraphicsWidget*>(focusItem)->palette().text().color();
540 }
541 }
542 }
543 } else {
544 styleTextColor = focused->palette().text().color();
545 }
546 } else {
547 styleTextColor = QApplication::palette("QLineEdit").text().color();
548 }
549
550 if (styleTextColor.isValid()) {
551 const TLogicalRgb fontColor(TRgb(styleTextColor.red(), styleTextColor.green(), styleTextColor.blue(), styleTextColor.alpha()));
552 cFormat.iFontPresentation.iTextColor = fontColor;
553 }
554
555 TInt numChars = 0;
556 TInt charPos = 0;
557 int oldSize = attributes->size();
558 while (m_formatRetriever) {
559 m_formatRetriever->GetFormatOfFepInlineText(cFormat, numChars, charPos);
560 if (numChars <= 0) {
561 // This shouldn't happen according to S60 docs, but apparently does sometimes.
562 break;
563 }
564 attributes->append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
565 charPos,
566 numChars,
567 QVariant(qt_TCharFormat2QTextCharFormat(cFormat, styleTextColor.isValid()))));
568 charPos += numChars;
569 if (charPos >= m_preeditString.size()) {
570 break;
571 }
572 }
573
574 if (attributes->size() == oldSize) {
575 // S60 didn't provide any format, so let's give our own instead.
576 attributes->append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
577 0,
578 m_preeditString.size(),
579 standardFormat(PreeditFormat)));
580 }
581}
582
583void QCoeFepInputContext::queueInputCapabilitiesChanged()
584{
585 if (m_pendingInputCapabilitiesChanged)
586 return;
587
588 // Call ensureInputCapabilitiesChanged asynchronously. This is done to improve performance
589 // by not updating input capabilities too often. The reason we don't call the Symbian
590 // asynchronous version of InputCapabilitiesChanged is because we need to ensure that it
591 // is synchronous in some specific cases. Those will call ensureInputCapabilitesChanged.
592 QMetaObject::invokeMethod(this, "ensureInputCapabilitiesChanged", Qt::QueuedConnection);
593 m_pendingInputCapabilitiesChanged = true;
594}
595
596void QCoeFepInputContext::ensureInputCapabilitiesChanged()
597{
598 if (!m_pendingInputCapabilitiesChanged)
599 return;
600
601 // The call below is essentially equivalent to InputCapabilitiesChanged(),
602 // but is synchronous, rather than asynchronous.
603 CCoeEnv::Static()->SyncNotifyFocusObserversOfChangeInFocus();
604 m_pendingInputCapabilitiesChanged = false;
605}
606
607void QCoeFepInputContext::StartFepInlineEditL(const TDesC& aInitialInlineText,
608 TInt aPositionOfInsertionPointInInlineText, TBool aCursorVisibility, const MFormCustomDraw* /*aCustomDraw*/,
609 MFepInlineTextFormatRetriever& aInlineTextFormatRetriever,
610 MFepPointerEventHandlerDuringInlineEdit& aPointerEventHandlerDuringInlineEdit)
611{
612 QWidget *w = focusWidget();
613 if (!w)
614 return;
615
616 commitTemporaryPreeditString();
617
618 QList<QInputMethodEvent::Attribute> attributes;
619
620 m_cursorVisibility = aCursorVisibility ? 1 : 0;
621 m_inlinePosition = aPositionOfInsertionPointInInlineText;
622 m_preeditString = qt_TDesC2QString(aInitialInlineText);
623
624 m_formatRetriever = &aInlineTextFormatRetriever;
625 m_pointerHandler = &aPointerEventHandlerDuringInlineEdit;
626
627 // With T9 aInitialInlineText is typically empty when StartFepInlineEditL is called,
628 // but FEP requires that selected text is always removed at StartFepInlineEditL.
629 // Let's remove the selected text if aInitialInlineText is empty and there is selected text
630 if (m_preeditString.isEmpty()) {
631 int anchor = w->inputMethodQuery(Qt::ImAnchorPosition).toInt();
632 int cursorPos = w->inputMethodQuery(Qt::ImCursorPosition).toInt();
633 int replacementLength = qAbs(cursorPos-anchor);
634 if (replacementLength > 0) {
635 int replacementStart = cursorPos < anchor ? 0 : -replacementLength;
636 QList<QInputMethodEvent::Attribute> clearSelectionAttributes;
637 QInputMethodEvent clearSelectionEvent(QLatin1String(""), clearSelectionAttributes);
638 clearSelectionEvent.setCommitString(QLatin1String(""), replacementStart, replacementLength);
639 sendEvent(clearSelectionEvent);
640 }
641 }
642
643 applyFormat(&attributes);
644
645 attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
646 m_inlinePosition,
647 m_cursorVisibility,
648 QVariant()));
649 QInputMethodEvent event(m_preeditString, attributes);
650 sendEvent(event);
651}
652
653void QCoeFepInputContext::UpdateFepInlineTextL(const TDesC& aNewInlineText,
654 TInt aPositionOfInsertionPointInInlineText)
655{
656 QWidget *w = focusWidget();
657 if (!w)
658 return;
659
660 commitTemporaryPreeditString();
661
662 m_inlinePosition = aPositionOfInsertionPointInInlineText;
663
664 QList<QInputMethodEvent::Attribute> attributes;
665 applyFormat(&attributes);
666 attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
667 m_inlinePosition,
668 m_cursorVisibility,
669 QVariant()));
670 QString newPreeditString = qt_TDesC2QString(aNewInlineText);
671 QInputMethodEvent event(newPreeditString, attributes);
672 if (newPreeditString.isEmpty() && m_preeditString.isEmpty()) {
673 // In Symbian world this means "erase last character".
674 event.setCommitString(QLatin1String(""), -1, 1);
675 }
676 m_preeditString = newPreeditString;
677 sendEvent(event);
678}
679
680void QCoeFepInputContext::SetInlineEditingCursorVisibilityL(TBool aCursorVisibility)
681{
682 QWidget *w = focusWidget();
683 if (!w)
684 return;
685
686 m_cursorVisibility = aCursorVisibility ? 1 : 0;
687
688 QList<QInputMethodEvent::Attribute> attributes;
689 attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
690 m_inlinePosition,
691 m_cursorVisibility,
692 QVariant()));
693 QInputMethodEvent event(m_preeditString, attributes);
694 sendEvent(event);
695}
696
697void QCoeFepInputContext::CancelFepInlineEdit()
698{
699 // We are not supposed to ever have a tempPreeditString and a real preedit string
700 // from S60 at the same time, so it should be safe to rely on this test to determine
701 // whether we should honor S60's request to clear the text or not.
702 if (m_hasTempPreeditString)
703 return;
704
705 QList<QInputMethodEvent::Attribute> attributes;
706 QInputMethodEvent event(QLatin1String(""), attributes);
707 event.setCommitString(QLatin1String(""), 0, 0);
708 m_preeditString.clear();
709 m_inlinePosition = 0;
710 sendEvent(event);
711}
712
713TInt QCoeFepInputContext::DocumentLengthForFep() const
714{
715 QWidget *w = focusWidget();
716 if (!w)
717 return 0;
718
719 QVariant variant = w->inputMethodQuery(Qt::ImSurroundingText);
720 return variant.value<QString>().size() + m_preeditString.size();
721}
722
723TInt QCoeFepInputContext::DocumentMaximumLengthForFep() const
724{
725 QWidget *w = focusWidget();
726 if (!w)
727 return 0;
728
729 QVariant variant = w->inputMethodQuery(Qt::ImMaximumTextLength);
730 int size;
731 if (variant.isValid()) {
732 size = variant.toInt();
733 } else {
734 size = INT_MAX; // Sensible default for S60.
735 }
736 return size;
737}
738
739void QCoeFepInputContext::SetCursorSelectionForFepL(const TCursorSelection& aCursorSelection)
740{
741 QWidget *w = focusWidget();
742 if (!w)
743 return;
744
745 commitTemporaryPreeditString();
746
747 int pos = aCursorSelection.iAnchorPos;
748 int length = aCursorSelection.iCursorPos - pos;
749
750 QList<QInputMethodEvent::Attribute> attributes;
751 attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, pos, length, QVariant());
752 QInputMethodEvent event(m_preeditString, attributes);
753 sendEvent(event);
754}
755
756void QCoeFepInputContext::GetCursorSelectionForFep(TCursorSelection& aCursorSelection) const
757{
758 QWidget *w = focusWidget();
759 if (!w) {
760 aCursorSelection.SetSelection(0,0);
761 return;
762 }
763
764 int cursor = w->inputMethodQuery(Qt::ImCursorPosition).toInt() + m_preeditString.size();
765 int anchor = w->inputMethodQuery(Qt::ImAnchorPosition).toInt() + m_preeditString.size();
766 QString text = w->inputMethodQuery(Qt::ImSurroundingText).value<QString>();
767 int combinedSize = text.size() + m_preeditString.size();
768 if (combinedSize < anchor || combinedSize < cursor) {
769 // ### TODO! FIXME! QTBUG-5050
770 // This is a hack to prevent crashing in 4.6 with QLineEdits that use input masks.
771 // The root problem is that cursor position is relative to displayed text instead of the
772 // actual text we get.
773 //
774 // To properly fix this we would need to know the displayText of QLineEdits instead
775 // of just the text, which on itself should be a trivial change. The difficulties start
776 // when we need to commit the changes back to the QLineEdit, which would have to be somehow
777 // able to handle displayText, too.
778 //
779 // Until properly fixed, the cursor and anchor positions will not reflect correct positions
780 // for masked QLineEdits, unless all the masked positions are filled in order so that
781 // cursor position relative to the displayed text matches position relative to actual text.
782 aCursorSelection.iAnchorPos = combinedSize;
783 aCursorSelection.iCursorPos = combinedSize;
784 } else {
785 aCursorSelection.iAnchorPos = anchor;
786 aCursorSelection.iCursorPos = cursor;
787 }
788}
789
790void QCoeFepInputContext::GetEditorContentForFep(TDes& aEditorContent, TInt aDocumentPosition,
791 TInt aLengthToRetrieve) const
792{
793 QWidget *w = focusWidget();
794 if (!w) {
795 aEditorContent.FillZ(aLengthToRetrieve);
796 return;
797 }
798
799 QString text = w->inputMethodQuery(Qt::ImSurroundingText).value<QString>();
800 // FEP expects the preedit string to be part of the editor content, so let's mix it in.
801 int cursor = w->inputMethodQuery(Qt::ImCursorPosition).toInt();
802 text.insert(cursor, m_preeditString);
803 aEditorContent.Copy(qt_QString2TPtrC(text.mid(aDocumentPosition, aLengthToRetrieve)));
804}
805
806void QCoeFepInputContext::GetFormatForFep(TCharFormat& aFormat, TInt /* aDocumentPosition */) const
807{
808 QWidget *w = focusWidget();
809 if (!w) {
810 aFormat = TCharFormat();
811 return;
812 }
813
814 QFont font = w->inputMethodQuery(Qt::ImFont).value<QFont>();
815 QFontMetrics metrics(font);
816 //QString name = font.rawName();
817 QString name = font.defaultFamily(); // TODO! FIXME! Should be the above.
818 QHBufC hBufC(name);
819 aFormat = TCharFormat(hBufC->Des(), metrics.height());
820}
821
822void QCoeFepInputContext::GetScreenCoordinatesForFepL(TPoint& aLeftSideOfBaseLine, TInt& aHeight,
823 TInt& aAscent, TInt /* aDocumentPosition */) const
824{
825 QWidget *w = focusWidget();
826 if (!w) {
827 aLeftSideOfBaseLine = TPoint(0,0);
828 aHeight = 0;
829 aAscent = 0;
830 return;
831 }
832
833 QRect rect = w->inputMethodQuery(Qt::ImMicroFocus).value<QRect>();
834 aLeftSideOfBaseLine.iX = rect.left();
835 aLeftSideOfBaseLine.iY = rect.bottom();
836
837 QFont font = w->inputMethodQuery(Qt::ImFont).value<QFont>();
838 QFontMetrics metrics(font);
839 aHeight = metrics.height();
840 aAscent = metrics.ascent();
841}
842
843void QCoeFepInputContext::DoCommitFepInlineEditL()
844{
845 commitCurrentString(false);
846 if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0)
847 ReportAknEdStateEvent(QT_EAknCursorPositionChanged);
848
849}
850
851void QCoeFepInputContext::commitCurrentString(bool cancelFepTransaction)
852{
853 QList<QInputMethodEvent::Attribute> attributes;
854 QInputMethodEvent event(QLatin1String(""), attributes);
855 event.setCommitString(m_preeditString, 0, 0);
856 m_preeditString.clear();
857 m_inlinePosition = 0;
858 sendEvent(event);
859
860 m_hasTempPreeditString = false;
861
862 if (cancelFepTransaction) {
863 CCoeFep* fep = CCoeEnv::Static()->Fep();
864 if (fep)
865 fep->CancelTransaction();
866 }
867}
868
869MCoeFepAwareTextEditor_Extension1* QCoeFepInputContext::Extension1(TBool& aSetToTrue)
870{
871 aSetToTrue = ETrue;
872 return this;
873}
874
875void QCoeFepInputContext::SetStateTransferingOwnershipL(MCoeFepAwareTextEditor_Extension1::CState* aState,
876 TUid /*aTypeSafetyUid*/)
877{
878 // Note: The S60 docs are wrong! See the State() function.
879 if (m_fepState)
880 delete m_fepState;
881 m_fepState = static_cast<CAknEdwinState *>(aState);
882}
883
884MCoeFepAwareTextEditor_Extension1::CState* QCoeFepInputContext::State(TUid /*aTypeSafetyUid*/)
885{
886 // Note: The S60 docs are horribly wrong when describing the
887 // SetStateTransferingOwnershipL function and this function. They say that the former
888 // sets a CState object identified by the TUid, and the latter retrieves it.
889 // In reality, the CState is expected to always be a CAknEdwinState (even if it was not
890 // previously set), and the TUid is ignored. All in all, there is a single CAknEdwinState
891 // per QCoeFepInputContext, which should be deleted if the SetStateTransferingOwnershipL
892 // function is used to set a new one.
893 return m_fepState;
894}
895
896TTypeUid::Ptr QCoeFepInputContext::MopSupplyObject(TTypeUid /*id*/)
897{
898 return TTypeUid::Null();
899}
900
901MObjectProvider *QCoeFepInputContext::MopNext()
902{
903 QWidget *w = focusWidget();
904 if (w)
905 return w->effectiveWinId();
906 return 0;
907}
908
909QT_END_NAMESPACE
910
911#endif // QT_NO_IM
Note: See TracBrowser for help on using the repository browser.