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 ®ion)
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