1 /* * This file is part of Maliit framework *
2  *
3  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4  * All rights reserved.
5  *
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License version 2.1 as published by the Free Software Foundation
10  * and appearing in the file LICENSE.LGPL included in the packaging
11  * of this file.
12  */
13 
14 #include "minputcontextconnection.h"
15 
16 #include <QKeyEvent>
17 
18 namespace {
19     // attribute names for updateWidgetInformation() map
20     const char * const FocusStateAttribute = "focusState";
21     const char * const ContentTypeAttribute = "contentType";
22     const char * const CorrectionAttribute = "correctionEnabled";
23     const char * const PredictionAttribute = "predictionEnabled";
24     const char * const AutoCapitalizationAttribute = "autocapitalizationEnabled";
25     const char * const SurroundingTextAttribute = "surroundingText";
26     const char * const AnchorPositionAttribute = "anchorPosition";
27     const char * const CursorPositionAttribute = "cursorPosition";
28     const char * const HasSelectionAttribute = "hasSelection";
29     const char * const InputMethodModeAttribute = "inputMethodMode";
30     const char * const WinId = "winId";
31     const char * const CursorRectAttribute = "cursorRectangle";
32     const char * const HiddenTextAttribute = "hiddenText";
33     const char * const PreeditClickPosAttribute = "preeditClickPos";
34 }
35 
36 class MInputContextConnectionPrivate
37 {
38 public:
39     MInputContextConnectionPrivate();
40     ~MInputContextConnectionPrivate();
41 };
42 
43 
MInputContextConnectionPrivate()44 MInputContextConnectionPrivate::MInputContextConnectionPrivate()
45 {
46     // nothing
47 }
48 
49 
~MInputContextConnectionPrivate()50 MInputContextConnectionPrivate::~MInputContextConnectionPrivate()
51 {
52     // nothing
53 }
54 
55 
56 ////////////////////////
57 // actual class
58 
MInputContextConnection(QObject * parent)59 MInputContextConnection::MInputContextConnection(QObject *parent)
60     : activeConnection(0)
61     , d(new MInputContextConnectionPrivate)
62     , lastOrientation(0)
63     , mGlobalCorrectionEnabled(false)
64     , mRedirectionEnabled(false)
65     , mDetectableAutoRepeat(false)
66 {
67     Q_UNUSED(parent);
68 }
69 
70 
~MInputContextConnection()71 MInputContextConnection::~MInputContextConnection()
72 {
73     delete d;
74 }
75 
76 /* Accessors to widgetState */
focusState(bool & valid)77 bool MInputContextConnection::focusState(bool &valid)
78 {
79     QVariant focusStateVariant = mWidgetState[FocusStateAttribute];
80     valid = focusStateVariant.isValid();
81     return focusStateVariant.toBool();
82 }
83 
contentType(bool & valid)84 int MInputContextConnection::contentType(bool &valid)
85 {
86     QVariant contentTypeVariant = mWidgetState[ContentTypeAttribute];
87     return contentTypeVariant.toInt(&valid);
88 }
89 
correctionEnabled(bool & valid)90 bool MInputContextConnection::correctionEnabled(bool &valid)
91 {
92     QVariant correctionVariant = mWidgetState[CorrectionAttribute];
93     valid = correctionVariant.isValid();
94     return correctionVariant.toBool();
95 }
96 
97 
predictionEnabled(bool & valid)98 bool MInputContextConnection::predictionEnabled(bool &valid)
99 {
100     QVariant predictionVariant = mWidgetState[PredictionAttribute];
101     valid = predictionVariant.isValid();
102     return predictionVariant.toBool();
103 }
104 
autoCapitalizationEnabled(bool & valid)105 bool MInputContextConnection::autoCapitalizationEnabled(bool &valid)
106 {
107     QVariant capitalizationVariant = mWidgetState[AutoCapitalizationAttribute];
108     valid = capitalizationVariant.isValid();
109     return capitalizationVariant.toBool();
110 }
111 
cursorRectangle(bool & valid)112 QRect MInputContextConnection::cursorRectangle(bool &valid)
113 {
114     QVariant cursorRectVariant = mWidgetState[CursorRectAttribute];
115     valid = cursorRectVariant.isValid();
116     return cursorRectVariant.toRect();
117 }
118 
hiddenText(bool & valid)119 bool MInputContextConnection::hiddenText(bool &valid)
120 {
121     QVariant hiddenTextVariant = mWidgetState[HiddenTextAttribute];
122     valid = hiddenTextVariant.isValid();
123     return hiddenTextVariant.toBool();
124 }
125 
surroundingText(QString & text,int & cursorPosition)126 bool MInputContextConnection::surroundingText(QString &text, int &cursorPosition)
127 {
128     QVariant textVariant = mWidgetState[SurroundingTextAttribute];
129     QVariant posVariant = mWidgetState[CursorPositionAttribute];
130 
131     if (textVariant.isValid() && posVariant.isValid()) {
132         text = textVariant.toString();
133         cursorPosition = posVariant.toInt();
134         return true;
135     }
136 
137     return false;
138 }
139 
hasSelection(bool & valid)140 bool MInputContextConnection::hasSelection(bool &valid)
141 {
142     QVariant selectionVariant = mWidgetState[HasSelectionAttribute];
143     valid = selectionVariant.isValid();
144     return selectionVariant.toBool();
145 }
146 
inputMethodMode(bool & valid)147 int MInputContextConnection::inputMethodMode(bool &valid)
148 {
149     QVariant modeVariant = mWidgetState[InputMethodModeAttribute];
150     return modeVariant.toInt(&valid);
151 }
152 
preeditRectangle(bool & valid)153 QRect MInputContextConnection::preeditRectangle(bool &valid)
154 {
155     valid = false;
156     return QRect();
157 }
158 
winId()159 WId MInputContextConnection::winId()
160 {
161 #ifdef Q_WS_WIN
162     WId result = 0;
163     return result;
164 #else
165     QVariant winIdVariant = mWidgetState[WinId];
166     // after transfer by dbus type can change
167     switch (winIdVariant.type()) {
168     case QVariant::UInt:
169         if (sizeof(uint) >= sizeof(WId))
170             return winIdVariant.toUInt();
171         break;
172     case QVariant::ULongLong:
173         if (sizeof(qulonglong) >= sizeof(WId))
174             return winIdVariant.toULongLong();
175         break;
176     default:
177         if (winIdVariant.canConvert<WId>())
178             return winIdVariant.value<WId>();
179     }
180     return 0;
181 #endif
182 }
183 
184 
anchorPosition(bool & valid)185 int MInputContextConnection::anchorPosition(bool &valid)
186 {
187     QVariant posVariant = mWidgetState[AnchorPositionAttribute];
188     valid = posVariant.isValid();
189     return posVariant.toInt();
190 }
191 
preeditClickPos(bool & valid) const192 int MInputContextConnection::preeditClickPos(bool &valid) const
193 {
194     QVariant selectionVariant = mWidgetState[PreeditClickPosAttribute];
195     valid = selectionVariant.isValid();
196     return selectionVariant.toInt();
197 }
198 
199 /* End accessors to widget state */
200 
201 /* Handlers for inbound communication */
showInputMethod(unsigned int connectionId)202 void MInputContextConnection::showInputMethod(unsigned int connectionId)
203 {
204     if (activeConnection != connectionId)
205         return;
206 
207     Q_EMIT showInputMethodRequest();
208 }
209 
210 
hideInputMethod(unsigned int connectionId)211 void MInputContextConnection::hideInputMethod(unsigned int connectionId)
212 {
213     // Only allow this call for current active connection.
214     if (activeConnection != connectionId)
215         return;
216 
217     Q_EMIT hideInputMethodRequest();
218 }
219 
220 
mouseClickedOnPreedit(unsigned int connectionId,const QPoint & pos,const QRect & preeditRect)221 void MInputContextConnection::mouseClickedOnPreedit(unsigned int connectionId,
222                                                             const QPoint &pos, const QRect &preeditRect)
223 {
224     if (activeConnection != connectionId)
225         return;
226 
227     Q_EMIT mouseClickedOnPreedit(pos, preeditRect);
228 }
229 
230 
setPreedit(unsigned int connectionId,const QString & text,int cursorPos)231 void MInputContextConnection::setPreedit(unsigned int connectionId,
232                                                  const QString &text, int cursorPos)
233 {
234     if (activeConnection != connectionId)
235         return;
236 
237     preedit = text;
238 
239     Q_EMIT preeditChanged(text, cursorPos);
240 }
241 
242 
reset(unsigned int connectionId)243 void MInputContextConnection::reset(unsigned int connectionId)
244 {
245     if (activeConnection != connectionId)
246         return;
247 
248     preedit.clear();
249 
250     Q_EMIT resetInputMethodRequest();
251 
252     if (!preedit.isEmpty()) {
253         qWarning("Preedit set from InputMethod::reset()!");
254         preedit.clear();
255     }
256 }
257 
258 void
updateWidgetInformation(unsigned int connectionId,const QMap<QString,QVariant> & stateInfo,bool handleFocusChange)259 MInputContextConnection::updateWidgetInformation(
260     unsigned int connectionId, const QMap<QString, QVariant> &stateInfo,
261     bool handleFocusChange)
262 {
263     if (activeConnection != connectionId)
264         return;
265 
266     QMap<QString, QVariant> oldState = mWidgetState;
267 
268     mWidgetState = stateInfo;
269 
270 #ifndef Q_WS_WIN
271     if (handleFocusChange) {
272         Q_EMIT focusChanged(winId());
273     }
274 #endif
275 
276     Q_EMIT widgetStateChanged(connectionId, mWidgetState, oldState, handleFocusChange);
277 }
278 
279 void
receivedAppOrientationAboutToChange(unsigned int connectionId,int angle)280 MInputContextConnection::receivedAppOrientationAboutToChange(unsigned int connectionId,
281                                                                      int angle)
282 {
283     if (activeConnection != connectionId)
284         return;
285 
286     // Needs to be passed to the MImRotationAnimation listening
287     // to this signal first before the plugins. This ensures
288     // that the rotation animation can be painted sufficiently early.
289     Q_EMIT contentOrientationAboutToChange(angle);
290 
291     Q_EMIT contentOrientationAboutToChangeCompleted(angle);
292 }
293 
294 
receivedAppOrientationChanged(unsigned int connectionId,int angle)295 void MInputContextConnection::receivedAppOrientationChanged(unsigned int connectionId,
296                                                                     int angle)
297 {
298     if (activeConnection != connectionId)
299         return;
300 
301     // Handle orientation changes through MImRotationAnimation with priority.
302     // That's needed for getting the correct rotated pixmap buffers.
303     Q_EMIT contentOrientationChanged(angle);
304 
305     Q_EMIT contentOrientationChangeCompleted(angle);
306 }
307 
308 
setCopyPasteState(unsigned int connectionId,bool copyAvailable,bool pasteAvailable)309 void MInputContextConnection::setCopyPasteState(unsigned int connectionId,
310                                                         bool copyAvailable, bool pasteAvailable)
311 {
312     if (activeConnection != connectionId)
313         return;
314 
315     Q_EMIT copyPasteStateChanged(copyAvailable, pasteAvailable);
316 }
317 
318 
processKeyEvent(unsigned int connectionId,QEvent::Type keyType,Qt::Key keyCode,Qt::KeyboardModifiers modifiers,const QString & text,bool autoRepeat,int count,quint32 nativeScanCode,quint32 nativeModifiers,unsigned long time)319 void MInputContextConnection::processKeyEvent(
320     unsigned int connectionId, QEvent::Type keyType, Qt::Key keyCode,
321     Qt::KeyboardModifiers modifiers, const QString &text, bool autoRepeat, int count,
322     quint32 nativeScanCode, quint32 nativeModifiers, unsigned long time)
323 {
324     if (activeConnection != connectionId)
325         return;
326 
327     Q_EMIT receivedKeyEvent(keyType, keyCode,
328                             modifiers, text, autoRepeat, count,
329                             nativeScanCode, nativeModifiers, time);
330 }
331 
registerAttributeExtension(unsigned int connectionId,int id,const QString & attributeExtension)332 void MInputContextConnection::registerAttributeExtension(unsigned int connectionId, int id,
333                                                          const QString &attributeExtension)
334 {
335     Q_EMIT attributeExtensionRegistered(connectionId, id, attributeExtension);
336 }
337 
unregisterAttributeExtension(unsigned int connectionId,int id)338 void MInputContextConnection::unregisterAttributeExtension(unsigned int connectionId, int id)
339 {
340     Q_EMIT attributeExtensionUnregistered(connectionId, id);
341 }
342 
setExtendedAttribute(unsigned int connectionId,int id,const QString & target,const QString & targetName,const QString & attribute,const QVariant & value)343 void MInputContextConnection::setExtendedAttribute(
344     unsigned int connectionId, int id, const QString &target, const QString &targetName,
345     const QString &attribute, const QVariant &value)
346 {
347     Q_EMIT extendedAttributeChanged(connectionId, id, target, targetName, attribute, value);
348 }
349 
loadPluginSettings(int connectionId,const QString & descriptionLanguage)350 void MInputContextConnection::loadPluginSettings(int connectionId, const QString &descriptionLanguage)
351 {
352     Q_EMIT pluginSettingsRequested(connectionId, descriptionLanguage);
353 }
354 /* End handlers for inbound communication */
355 
detectableAutoRepeat()356 bool MInputContextConnection::detectableAutoRepeat()
357 {
358     return mDetectableAutoRepeat;
359 }
360 
setDetectableAutoRepeat(bool enabled)361 void MInputContextConnection::setDetectableAutoRepeat(bool enabled)
362 {
363     mDetectableAutoRepeat = enabled;
364 }
365 
setGlobalCorrectionEnabled(bool enabled)366 void MInputContextConnection::setGlobalCorrectionEnabled(bool enabled)
367 {
368     mGlobalCorrectionEnabled = enabled;
369 }
370 
globalCorrectionEnabled()371 bool MInputContextConnection::globalCorrectionEnabled()
372 {
373     return mGlobalCorrectionEnabled;
374 }
375 
setRedirectKeys(bool enabled)376 void MInputContextConnection::setRedirectKeys(bool enabled)
377 {
378     mRedirectionEnabled = enabled;
379 }
380 
redirectKeysEnabled()381 bool MInputContextConnection::redirectKeysEnabled()
382 {
383     return mRedirectionEnabled;
384 }
385 
386 /* */
sendCommitString(const QString & string,int replaceStart,int replaceLength,int cursorPos)387 void MInputContextConnection::sendCommitString(const QString &string, int replaceStart,
388                                           int replaceLength, int cursorPos) {
389 
390     const int cursorPosition(mWidgetState[CursorPositionAttribute].toInt());
391     bool validAnchor(false);
392 
393     preedit.clear();
394 
395     if (replaceLength == 0  // we don't support replacement
396         // we don't support selections
397         && anchorPosition(validAnchor) == cursorPosition
398         && validAnchor) {
399         const int insertPosition(cursorPosition + replaceStart);
400         if (insertPosition >= 0) {
401             mWidgetState[SurroundingTextAttribute]
402                 = mWidgetState[SurroundingTextAttribute].toString().insert(insertPosition, string);
403             mWidgetState[CursorPositionAttribute] = cursorPos < 0 ? (insertPosition + string.length()) : cursorPos;
404             mWidgetState[AnchorPositionAttribute] = mWidgetState[CursorPositionAttribute];
405         }
406     }
407 }
408 
sendKeyEvent(const QKeyEvent & keyEvent,Maliit::EventRequestType requestType)409 void MInputContextConnection::sendKeyEvent(const QKeyEvent &keyEvent,
410                                            Maliit::EventRequestType requestType)
411 {
412     if (requestType != Maliit::EventRequestSignalOnly
413         && preedit.isEmpty()
414         && keyEvent.key() == Qt::Key_Backspace
415         && keyEvent.type() == QEvent::KeyPress) {
416         QString surrString(mWidgetState[SurroundingTextAttribute].toString());
417         const int cursorPosition(mWidgetState[CursorPositionAttribute].toInt());
418         bool validAnchor(false);
419 
420         if (!surrString.isEmpty()
421             && cursorPosition > 0
422             // we don't support selections
423             && anchorPosition(validAnchor) == cursorPosition
424             && validAnchor) {
425             mWidgetState[SurroundingTextAttribute] = surrString.remove(cursorPosition - 1, 1);
426             mWidgetState[CursorPositionAttribute] = cursorPosition - 1;
427             mWidgetState[AnchorPositionAttribute] = cursorPosition - 1;
428         }
429     }
430 }
431 /* */
432 
433 /* */
handleDisconnection(unsigned int connectionId)434 void MInputContextConnection::handleDisconnection(unsigned int connectionId)
435 {
436     Q_EMIT clientDisconnected(connectionId);
437 
438     if (activeConnection != connectionId) {
439         return;
440     }
441 
442     activeConnection = 0;
443 
444     Q_EMIT activeClientDisconnected();
445 }
446 
activateContext(unsigned int connectionId)447 void MInputContextConnection::activateContext(unsigned int connectionId)
448 {
449     if (connectionId == activeConnection) {
450         return;
451     }
452 
453     /* Notify current/previously active context that it is no longer active */
454     sendActivationLostEvent();
455 
456     activeConnection = connectionId;
457 
458     /* Notify new input context about state/settings stored in the IM server */
459     if (activeConnection) {
460         /* Hack: Circumvent if(newValue == oldValue) return; guards */
461         mGlobalCorrectionEnabled = !mGlobalCorrectionEnabled;
462         setGlobalCorrectionEnabled(!mGlobalCorrectionEnabled);
463 
464         mRedirectionEnabled = !mRedirectionEnabled;
465         setRedirectKeys(!mRedirectionEnabled);
466 
467         mDetectableAutoRepeat = !mDetectableAutoRepeat;
468         setDetectableAutoRepeat(!mDetectableAutoRepeat);
469     }
470 
471     Q_EMIT clientActivated(connectionId);
472 
473 }
474 /* */
475 
sendPreeditString(const QString & string,const QList<Maliit::PreeditTextFormat> & preeditFormats,int replaceStart,int replaceLength,int cursorPos)476 void MInputContextConnection::sendPreeditString(const QString &string,
477                                                 const QList<Maliit::PreeditTextFormat> &preeditFormats,
478                                                 int replaceStart, int replaceLength,
479                                                 int cursorPos)
480 {
481     Q_UNUSED(preeditFormats);
482     Q_UNUSED(replaceStart);
483     Q_UNUSED(replaceLength);
484     Q_UNUSED(cursorPos);
485     if (activeConnection) {
486         preedit = string;
487     }
488 }
489 
490 /* */
setSelection(int start,int length)491 void MInputContextConnection::setSelection(int start, int length)
492 {
493     Q_UNUSED(start);
494     Q_UNUSED(length);
495 }
496 
497 
notifyImInitiatedHiding()498 void MInputContextConnection::notifyImInitiatedHiding()
499 {}
500 
501 
invokeAction(const QString & action,const QKeySequence & sequence)502 void MInputContextConnection::invokeAction(const QString &action, const QKeySequence &sequence)
503 {
504     Q_UNUSED(action);
505     Q_UNUSED(sequence);
506 }
507 
508 
selection(bool & valid)509 QString MInputContextConnection::selection(bool &valid)
510 {
511     valid = false;
512     return QString();
513 }
514 
setLanguage(const QString & language)515 void MInputContextConnection::setLanguage(const QString &language)
516 {
517     Q_UNUSED(language);
518 }
519 
sendActivationLostEvent()520 void MInputContextConnection::sendActivationLostEvent()
521 {}
522 
updateInputMethodArea(const QRegion & region)523 void MInputContextConnection::updateInputMethodArea(const QRegion &region)
524 {
525     Q_UNUSED(region);
526 }
527 
notifyExtendedAttributeChanged(int,const QString &,const QString &,const QString &,const QVariant &)528 void MInputContextConnection::notifyExtendedAttributeChanged(int ,
529                                                              const QString &,
530                                                              const QString &,
531                                                              const QString &,
532                                                              const QVariant &)
533 {
534     // empty default implementation
535 }
536 
notifyExtendedAttributeChanged(const QList<int> &,int,const QString &,const QString &,const QString &,const QVariant &)537 void MInputContextConnection::notifyExtendedAttributeChanged(const QList<int> &,
538                                                              int ,
539                                                              const QString &,
540                                                              const QString &,
541                                                              const QString &,
542                                                              const QVariant &)
543 {
544     // empty default implementation
545 }
546 
pluginSettingsLoaded(int clientId,const QList<MImPluginSettingsInfo> & info)547 void MInputContextConnection::pluginSettingsLoaded(int clientId, const QList<MImPluginSettingsInfo> &info)
548 {
549     Q_UNUSED(clientId);
550     Q_UNUSED(info);
551 
552     // empty default implementation
553 }
554 
555 
widgetState() const556 QVariantMap MInputContextConnection::widgetState() const
557 {
558     return mWidgetState;
559 }
560 
inputMethodQuery(Qt::InputMethodQuery query,const QVariant & argument) const561 QVariant MInputContextConnection::inputMethodQuery(Qt::InputMethodQuery query, const QVariant &argument) const
562 {
563     switch (query) {
564         case Qt::ImEnabled:
565             return mWidgetState.value(QStringLiteral("focusState"));
566         case Qt::ImCursorRectangle:
567             return mWidgetState.value(QStringLiteral("cursorRectangle"));
568 //        case Qt::ImFont:
569 //            return QVariant();
570         case Qt::ImCursorPosition:
571             return mWidgetState.value(QStringLiteral("cursorPosition"));
572         case Qt::ImSurroundingText:
573             return mWidgetState.value(QStringLiteral("surroundingText"));
574         case Qt::ImCurrentSelection:
575             return QVariant(); // TODO implement
576 //        case Qt::ImMaximumTextLength:
577         case Qt::ImAnchorPosition:
578             return mWidgetState.value(QStringLiteral("anchorPosition"));
579         case Qt::ImHints:
580             return mWidgetState.value(QStringLiteral("maliit-inputmethod-hints"));
581 //        case Qt::ImPreferredLanguage:
582 //        case Qt::ImAbsolutePosition:
583 //        case Qt::ImTextBeforeCursor:
584 //        case Qt::ImTextAfterCursor:
585         case Qt::ImEnterKeyType:
586             return mWidgetState.value(QStringLiteral("enterKeyType"));
587 //        case Qt::ImAnchorRectangle:
588 //        case Qt::ImInputItemClipRectangle:
589 //            return QVariant();
590     }
591     return QVariant();
592 
593 }
594