45 #include <qapplication.h> |
45 #include <qapplication.h> |
46 #include <qtextformat.h> |
46 #include <qtextformat.h> |
47 #include <qgraphicsview.h> |
47 #include <qgraphicsview.h> |
48 #include <qgraphicsscene.h> |
48 #include <qgraphicsscene.h> |
49 #include <qgraphicswidget.h> |
49 #include <qgraphicswidget.h> |
|
50 #include <qsymbianevent.h> |
50 #include <private/qcore_symbian_p.h> |
51 #include <private/qcore_symbian_p.h> |
51 |
52 |
52 #include <fepitfr.h> |
53 #include <fepitfr.h> |
53 #include <hal.h> |
54 #include <hal.h> |
54 |
55 |
77 m_pendingInputCapabilitiesChanged(false), |
78 m_pendingInputCapabilitiesChanged(false), |
78 m_cursorVisibility(1), |
79 m_cursorVisibility(1), |
79 m_inlinePosition(0), |
80 m_inlinePosition(0), |
80 m_formatRetriever(0), |
81 m_formatRetriever(0), |
81 m_pointerHandler(0), |
82 m_pointerHandler(0), |
82 m_cursorPos(0), |
|
83 m_hasTempPreeditString(false) |
83 m_hasTempPreeditString(false) |
84 { |
84 { |
85 m_fepState->SetObjectProvider(this); |
85 m_fepState->SetObjectProvider(this); |
86 if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0) |
86 if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0) |
87 m_fepState->SetFlags(EAknEditorFlagDefault | QT_EAknEditorFlagSelectionVisible); |
87 m_fepState->SetFlags(EAknEditorFlagDefault | QT_EAknEditorFlagSelectionVisible); |
235 break; |
235 break; |
236 default: |
236 default: |
237 break; |
237 break; |
238 } |
238 } |
239 |
239 |
|
240 QString widgetText = focusWidget()->inputMethodQuery(Qt::ImSurroundingText).toString(); |
|
241 int maxLength = focusWidget()->inputMethodQuery(Qt::ImMaximumTextLength).toInt(); |
|
242 if (!keyEvent->text().isEmpty() && widgetText.size() + m_preeditString.size() >= maxLength) { |
|
243 // Don't send key events with string content if the widget is "full". |
|
244 return true; |
|
245 } |
|
246 |
240 if (keyEvent->type() == QEvent::KeyPress |
247 if (keyEvent->type() == QEvent::KeyPress |
241 && focusWidget()->inputMethodHints() & Qt::ImhHiddenText |
248 && focusWidget()->inputMethodHints() & Qt::ImhHiddenText |
242 && !keyEvent->text().isEmpty()) { |
249 && !keyEvent->text().isEmpty()) { |
243 // Send some temporary preedit text in order to make text visible for a moment. |
250 // Send some temporary preedit text in order to make text visible for a moment. |
244 m_cursorPos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt(); |
|
245 m_preeditString = keyEvent->text(); |
251 m_preeditString = keyEvent->text(); |
246 QList<QInputMethodEvent::Attribute> attributes; |
252 QList<QInputMethodEvent::Attribute> attributes; |
247 QInputMethodEvent imEvent(m_preeditString, attributes); |
253 QInputMethodEvent imEvent(m_preeditString, attributes); |
248 sendEvent(imEvent); |
254 sendEvent(imEvent); |
249 m_tempPreeditStringTimeout.start(1000, this); |
255 m_tempPreeditStringTimeout.start(1000, this); |
280 } |
286 } |
281 |
287 |
282 return false; |
288 return false; |
283 } |
289 } |
284 |
290 |
|
291 bool QCoeFepInputContext::symbianFilterEvent(QWidget *keyWidget, const QSymbianEvent *event) |
|
292 { |
|
293 Q_UNUSED(keyWidget); |
|
294 if (event->type() == QSymbianEvent::CommandEvent) |
|
295 // A command basically means the same as a button being pushed. With Qt buttons |
|
296 // that would normally result in a reset of the input method due to the focus change. |
|
297 // This should also happen for commands. |
|
298 reset(); |
|
299 |
|
300 return false; |
|
301 } |
|
302 |
285 void QCoeFepInputContext::timerEvent(QTimerEvent *timerEvent) |
303 void QCoeFepInputContext::timerEvent(QTimerEvent *timerEvent) |
286 { |
304 { |
287 if (timerEvent->timerId() == m_tempPreeditStringTimeout.timerId()) |
305 if (timerEvent->timerId() == m_tempPreeditStringTimeout.timerId()) |
288 commitTemporaryPreeditString(); |
306 commitTemporaryPreeditString(); |
289 } |
307 } |
295 |
313 |
296 if (!m_hasTempPreeditString) |
314 if (!m_hasTempPreeditString) |
297 return; |
315 return; |
298 |
316 |
299 commitCurrentString(false); |
317 commitCurrentString(false); |
300 |
|
301 //update cursor position, now this pre-edit text has been committed. |
|
302 //this prevents next keypress overwriting it (QTBUG-11673) |
|
303 m_cursorPos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt(); |
|
304 } |
318 } |
305 |
319 |
306 void QCoeFepInputContext::mouseHandler( int x, QMouseEvent *event) |
320 void QCoeFepInputContext::mouseHandler( int x, QMouseEvent *event) |
307 { |
321 { |
308 Q_ASSERT(focusWidget()); |
322 Q_ASSERT(focusWidget()); |
362 { |
376 { |
363 using namespace Qt; |
377 using namespace Qt; |
364 |
378 |
365 commitTemporaryPreeditString(); |
379 commitTemporaryPreeditString(); |
366 |
380 |
367 bool numbersOnly = hints & ImhDigitsOnly || hints & ImhFormattedNumbersOnly |
381 const bool anynumbermodes = hints & (ImhDigitsOnly | ImhFormattedNumbersOnly | ImhDialableCharactersOnly); |
368 || hints & ImhDialableCharactersOnly; |
382 const bool anytextmodes = hints & (ImhUppercaseOnly | ImhLowercaseOnly | ImhEmailCharactersOnly | ImhUrlCharactersOnly); |
369 bool noOnlys = !(numbersOnly || hints & ImhUppercaseOnly |
383 const bool numbersOnly = anynumbermodes && !anytextmodes; |
370 || hints & ImhLowercaseOnly); |
384 const bool noOnlys = !(hints & ImhExclusiveInputMask); |
371 TInt flags; |
385 TInt flags; |
372 Qt::InputMethodHints oldHints = hints; |
386 Qt::InputMethodHints oldHints = hints; |
373 |
387 |
374 // Some sanity checking. Make sure that only one preference is set. |
388 // Some sanity checking. Make sure that only one preference is set. |
375 InputMethodHints prefs = ImhPreferNumbers | ImhPreferUppercase | ImhPreferLowercase; |
389 InputMethodHints prefs = ImhPreferNumbers | ImhPreferUppercase | ImhPreferLowercase; |
377 if (prefs != ImhPreferNumbers && prefs != ImhPreferUppercase && prefs != ImhPreferLowercase) { |
391 if (prefs != ImhPreferNumbers && prefs != ImhPreferUppercase && prefs != ImhPreferLowercase) { |
378 hints &= ~prefs; |
392 hints &= ~prefs; |
379 } |
393 } |
380 if (!noOnlys) { |
394 if (!noOnlys) { |
381 // Make sure that the preference is within the permitted set. |
395 // Make sure that the preference is within the permitted set. |
382 if (hints & ImhPreferNumbers && !(hints & ImhDigitsOnly || hints & ImhFormattedNumbersOnly |
396 if (hints & ImhPreferNumbers && !anynumbermodes) { |
383 || hints & ImhDialableCharactersOnly)) { |
|
384 hints &= ~ImhPreferNumbers; |
397 hints &= ~ImhPreferNumbers; |
385 } else if (hints & ImhPreferUppercase && !(hints & ImhUppercaseOnly)) { |
398 } else if (hints & ImhPreferUppercase && !(hints & ImhUppercaseOnly)) { |
386 hints &= ~ImhPreferUppercase; |
399 hints &= ~ImhPreferUppercase; |
387 } else if (hints & ImhPreferLowercase && !(hints & ImhLowercaseOnly)) { |
400 } else if (hints & ImhPreferLowercase && !(hints & ImhLowercaseOnly)) { |
388 hints &= ~ImhPreferLowercase; |
401 hints &= ~ImhPreferLowercase; |
391 if (!(hints & ImhPreferNumbers || hints & ImhPreferUppercase || hints & ImhPreferLowercase)) { |
404 if (!(hints & ImhPreferNumbers || hints & ImhPreferUppercase || hints & ImhPreferLowercase)) { |
392 if (hints & ImhLowercaseOnly) { |
405 if (hints & ImhLowercaseOnly) { |
393 hints |= ImhPreferLowercase; |
406 hints |= ImhPreferLowercase; |
394 } else if (hints & ImhUppercaseOnly) { |
407 } else if (hints & ImhUppercaseOnly) { |
395 hints |= ImhPreferUppercase; |
408 hints |= ImhPreferUppercase; |
396 } else if (hints & ImhDigitsOnly || hints & ImhFormattedNumbersOnly |
409 } else if (numbersOnly) { |
397 || hints & ImhDialableCharactersOnly) { |
|
398 hints |= ImhPreferNumbers; |
410 hints |= ImhPreferNumbers; |
399 } |
411 } |
400 } |
412 } |
401 } |
413 } |
402 |
414 |
406 } else { |
418 } else { |
407 m_fepState->SetDefaultInputMode(EAknEditorTextInputMode); |
419 m_fepState->SetDefaultInputMode(EAknEditorTextInputMode); |
408 m_fepState->SetCurrentInputMode(EAknEditorTextInputMode); |
420 m_fepState->SetCurrentInputMode(EAknEditorTextInputMode); |
409 } |
421 } |
410 flags = 0; |
422 flags = 0; |
411 if (numbersOnly) { |
423 if (noOnlys || (anynumbermodes && anytextmodes)) { |
|
424 flags = EAknEditorAllInputModes; |
|
425 } |
|
426 else if (anynumbermodes) { |
412 flags |= EAknEditorNumericInputMode; |
427 flags |= EAknEditorNumericInputMode; |
413 } |
428 if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0 |
414 if (hints & ImhUppercaseOnly || hints & ImhLowercaseOnly) { |
429 && ((hints & ImhFormattedNumbersOnly) || (hints & ImhDialableCharactersOnly))) { |
|
430 //workaround - the * key does not launch the symbols menu, making it impossible to use these modes unless text mode is enabled. |
|
431 flags |= EAknEditorTextInputMode; |
|
432 } |
|
433 } |
|
434 else if (anytextmodes) { |
415 flags |= EAknEditorTextInputMode; |
435 flags |= EAknEditorTextInputMode; |
416 } |
436 } |
417 if (flags == 0) { |
437 else { |
418 flags = EAknEditorAllInputModes; |
438 flags = EAknEditorAllInputModes; |
419 } |
439 } |
420 m_fepState->SetPermittedInputModes(flags); |
440 m_fepState->SetPermittedInputModes(flags); |
421 ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateInputModeUpdate); |
441 ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateInputModeUpdate); |
422 |
442 |
459 } |
479 } |
460 // Using T9 and hidden text together may actually crash the FEP, so check for hidden text too. |
480 // Using T9 and hidden text together may actually crash the FEP, so check for hidden text too. |
461 if (hints & ImhNoPredictiveText || hints & ImhHiddenText) { |
481 if (hints & ImhNoPredictiveText || hints & ImhHiddenText) { |
462 flags |= EAknEditorFlagNoT9; |
482 flags |= EAknEditorFlagNoT9; |
463 } |
483 } |
|
484 // if alphanumeric input, or if multiple incompatible number modes are selected; |
|
485 // then make all symbols available in numeric mode too. |
|
486 if (!numbersOnly || ((hints & ImhFormattedNumbersOnly) && (hints & ImhDialableCharactersOnly))) |
|
487 flags |= EAknEditorFlagUseSCTNumericCharmap; |
464 m_fepState->SetFlags(flags); |
488 m_fepState->SetFlags(flags); |
465 ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateFlagsUpdate); |
489 ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateFlagsUpdate); |
466 |
490 |
467 if (hints & ImhFormattedNumbersOnly) { |
491 if (hints & ImhDialableCharactersOnly) { |
|
492 // This is first, because if (ImhDialableCharactersOnly | ImhFormattedNumbersOnly) |
|
493 // is specified, this one is more natural (# key enters a #) |
|
494 flags = EAknEditorStandardNumberModeKeymap; |
|
495 } else if (hints & ImhFormattedNumbersOnly) { |
|
496 // # key enters decimal point |
468 flags = EAknEditorCalculatorNumberModeKeymap; |
497 flags = EAknEditorCalculatorNumberModeKeymap; |
469 } else if (hints & ImhDigitsOnly) { |
498 } else if (hints & ImhDigitsOnly) { |
|
499 // This is last, because it is most restrictive (# key is inactive) |
470 flags = EAknEditorPlainNumberModeKeymap; |
500 flags = EAknEditorPlainNumberModeKeymap; |
471 } else { |
501 } else { |
472 // ImhDialableCharactersOnly is the fallback as well, so we don't need to check for |
|
473 // that flag. |
|
474 flags = EAknEditorStandardNumberModeKeymap; |
502 flags = EAknEditorStandardNumberModeKeymap; |
475 } |
503 } |
476 m_fepState->SetNumericKeymap(static_cast<TAknEditorNumericKeymap>(flags)); |
504 m_fepState->SetNumericKeymap(static_cast<TAknEditorNumericKeymap>(flags)); |
477 |
505 |
478 if (hints & ImhEmailCharactersOnly) { |
506 if (hints & ImhUrlCharactersOnly) { |
|
507 // URL characters is everything except space, so a superset of the other restrictions |
|
508 m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_URL_SPECIAL_CHARACTER_TABLE_DIALOG); |
|
509 } else if (hints & ImhEmailCharactersOnly) { |
479 m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_EMAIL_ADDR_SPECIAL_CHARACTER_TABLE_DIALOG); |
510 m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_EMAIL_ADDR_SPECIAL_CHARACTER_TABLE_DIALOG); |
480 } else if (hints & ImhUrlCharactersOnly) { |
|
481 m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_URL_SPECIAL_CHARACTER_TABLE_DIALOG); |
|
482 } else { |
511 } else { |
483 m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_SPECIAL_CHARACTER_TABLE_DIALOG); |
512 m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_SPECIAL_CHARACTER_TABLE_DIALOG); |
484 } |
513 } |
485 |
514 |
486 if (hints & ImhHiddenText) { |
515 if (hints & ImhHiddenText) { |
579 if (!w) |
608 if (!w) |
580 return; |
609 return; |
581 |
610 |
582 commitTemporaryPreeditString(); |
611 commitTemporaryPreeditString(); |
583 |
612 |
584 m_cursorPos = w->inputMethodQuery(Qt::ImCursorPosition).toInt(); |
|
585 |
|
586 QList<QInputMethodEvent::Attribute> attributes; |
613 QList<QInputMethodEvent::Attribute> attributes; |
587 |
614 |
588 m_cursorVisibility = aCursorVisibility ? 1 : 0; |
615 m_cursorVisibility = aCursorVisibility ? 1 : 0; |
589 m_inlinePosition = aPositionOfInsertionPointInInlineText; |
616 m_inlinePosition = aPositionOfInsertionPointInInlineText; |
590 m_preeditString = qt_TDesC2QString(aInitialInlineText); |
617 m_preeditString = qt_TDesC2QString(aInitialInlineText); |
595 // With T9 aInitialInlineText is typically empty when StartFepInlineEditL is called, |
622 // With T9 aInitialInlineText is typically empty when StartFepInlineEditL is called, |
596 // but FEP requires that selected text is always removed at StartFepInlineEditL. |
623 // but FEP requires that selected text is always removed at StartFepInlineEditL. |
597 // Let's remove the selected text if aInitialInlineText is empty and there is selected text |
624 // Let's remove the selected text if aInitialInlineText is empty and there is selected text |
598 if (m_preeditString.isEmpty()) { |
625 if (m_preeditString.isEmpty()) { |
599 int anchor = w->inputMethodQuery(Qt::ImAnchorPosition).toInt(); |
626 int anchor = w->inputMethodQuery(Qt::ImAnchorPosition).toInt(); |
600 int replacementLength = qAbs(m_cursorPos-anchor); |
627 int cursorPos = w->inputMethodQuery(Qt::ImCursorPosition).toInt(); |
|
628 int replacementLength = qAbs(cursorPos-anchor); |
601 if (replacementLength > 0) { |
629 if (replacementLength > 0) { |
602 int replacementStart = m_cursorPos < anchor ? 0 : -replacementLength; |
630 int replacementStart = cursorPos < anchor ? 0 : -replacementLength; |
603 QList<QInputMethodEvent::Attribute> clearSelectionAttributes; |
631 QList<QInputMethodEvent::Attribute> clearSelectionAttributes; |
604 QInputMethodEvent clearSelectionEvent(QLatin1String(""), clearSelectionAttributes); |
632 QInputMethodEvent clearSelectionEvent(QLatin1String(""), clearSelectionAttributes); |
605 clearSelectionEvent.setCommitString(QLatin1String(""), replacementStart, replacementLength); |
633 clearSelectionEvent.setCommitString(QLatin1String(""), replacementStart, replacementLength); |
606 sendEvent(clearSelectionEvent); |
634 sendEvent(clearSelectionEvent); |
607 } |
635 } |
630 applyFormat(&attributes); |
658 applyFormat(&attributes); |
631 attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, |
659 attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, |
632 m_inlinePosition, |
660 m_inlinePosition, |
633 m_cursorVisibility, |
661 m_cursorVisibility, |
634 QVariant())); |
662 QVariant())); |
635 m_preeditString = qt_TDesC2QString(aNewInlineText); |
663 QString newPreeditString = qt_TDesC2QString(aNewInlineText); |
636 QInputMethodEvent event(m_preeditString, attributes); |
664 QInputMethodEvent event(newPreeditString, attributes); |
|
665 if (newPreeditString.isEmpty() && m_preeditString.isEmpty()) { |
|
666 // In Symbian world this means "erase last character". |
|
667 event.setCommitString(QLatin1String(""), -1, 1); |
|
668 } |
|
669 m_preeditString = newPreeditString; |
637 sendEvent(event); |
670 sendEvent(event); |
638 } |
671 } |
639 |
672 |
640 void QCoeFepInputContext::SetInlineEditingCursorVisibilityL(TBool aCursorVisibility) |
673 void QCoeFepInputContext::SetInlineEditingCursorVisibilityL(TBool aCursorVisibility) |
641 { |
674 { |
801 |
834 |
802 } |
835 } |
803 |
836 |
804 void QCoeFepInputContext::commitCurrentString(bool cancelFepTransaction) |
837 void QCoeFepInputContext::commitCurrentString(bool cancelFepTransaction) |
805 { |
838 { |
806 int longPress = 0; |
|
807 |
|
808 if (m_preeditString.size() == 0) { |
|
809 QWidget *w = focusWidget(); |
|
810 if (!cancelFepTransaction && w) { |
|
811 // We must replace the last character only if the input box has already accepted one |
|
812 if (w->inputMethodQuery(Qt::ImCursorPosition).toInt() != m_cursorPos) |
|
813 longPress = 1; |
|
814 } |
|
815 } |
|
816 |
|
817 QList<QInputMethodEvent::Attribute> attributes; |
839 QList<QInputMethodEvent::Attribute> attributes; |
818 QInputMethodEvent event(QLatin1String(""), attributes); |
840 QInputMethodEvent event(QLatin1String(""), attributes); |
819 event.setCommitString(m_preeditString, 0-longPress, longPress); |
841 event.setCommitString(m_preeditString, 0, 0); |
820 m_preeditString.clear(); |
842 m_preeditString.clear(); |
821 sendEvent(event); |
843 sendEvent(event); |
822 |
844 |
823 m_hasTempPreeditString = false; |
845 m_hasTempPreeditString = false; |
824 longPress = 0; |
|
825 |
846 |
826 if (cancelFepTransaction) { |
847 if (cancelFepTransaction) { |
827 CCoeFep* fep = CCoeEnv::Static()->Fep(); |
848 CCoeFep* fep = CCoeEnv::Static()->Fep(); |
828 if (fep) |
849 if (fep) |
829 fep->CancelTransaction(); |
850 fep->CancelTransaction(); |