| 1 |
/**************************************************************************** |
| 2 |
** |
| 3 |
** Copyright (C) 2010 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 |
** No Commercial Usage |
| 11 |
** This file contains pre-release code and may not be distributed. |
| 12 |
** You may use this file in accordance with the terms and conditions |
| 13 |
** contained in the Technology Preview License Agreement accompanying |
| 14 |
** this package. |
| 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 |
** If you have questions regarding the use of this file, please contact |
| 29 |
** Nokia at qt-info@nokia.com. |
| 30 |
** |
| 31 |
** |
| 32 |
** |
| 33 |
** |
| 34 |
** |
| 35 |
** |
| 36 |
** |
| 37 |
** |
| 38 |
** $QT_END_LICENSE$ |
| 39 |
** |
| 40 |
****************************************************************************/ |
| 41 |
#include "qdebug.h" |
| 42 |
#include "qhildoninputcontext_p.h" |
| 43 |
#include "qpointer.h" |
| 44 |
#include "qapplication.h" |
| 45 |
#include "qclipboard.h" |
| 46 |
#include "qplaintextedit.h" |
| 47 |
#include "qlineedit.h" |
| 48 |
#include "qtextedit.h" |
| 49 |
#include "qtextbrowser.h" |
| 50 |
#include "kernel/qevent_p.h" //QKeyEventEx |
| 51 |
#include "kernel/qapplication_p.h" //QApplicationPrivate::areXInputEventsUsed() |
| 52 |
#include "qinputcontext.h" |
| 53 |
#include "qtextcodec.h" |
| 54 |
#include "qtextboundaryfinder.h" |
| 55 |
#include <private/qkeymapper_p.h> |
| 56 |
|
| 57 |
#include "qgraphicsview.h" |
| 58 |
#ifndef QT_NO_GRAPHICSVIEW |
| 59 |
#include "qgraphicsitem.h" |
| 60 |
#include "qgraphicsproxywidget.h" |
| 61 |
#include "qgraphicsscene.h" |
| 62 |
#include "qgraphicswidget.h" |
| 63 |
#endif |
| 64 |
|
| 65 |
#ifdef Q_WS_MAEMO_5 |
| 66 |
|
| 67 |
#define GDK_ISO_ENTER 0xfe34 |
| 68 |
#define COMPOSE_KEY Qt::Key_Multi_key // "Ch" key |
| 69 |
#define LEVEL_KEY Qt::Key_AltGr //"Fn" key |
| 70 |
|
| 71 |
#define STATE_LEVEL_MASK 1 << 7 |
| 72 |
#define STATE_CONTROL_MASK 1 << 2 |
| 73 |
#define STATE_SHIFT_MASK 1 << 0 |
| 74 |
|
| 75 |
//Keyboard layout levels |
| 76 |
#define NUMERIC_LEVEL 2 |
| 77 |
#define LOCKABLE_LEVEL 4 |
| 78 |
|
| 79 |
|
| 80 |
extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); //qapplication_x11.cpp |
| 81 |
|
| 82 |
#define HIM_DEBUG |
| 83 |
|
| 84 |
static inline bool qHimDebugEnabled() |
| 85 |
{ |
| 86 |
static const bool debug = !qgetenv("QT_HIM_DEBUG").isEmpty(); |
| 87 |
return debug; |
| 88 |
} |
| 89 |
|
| 90 |
#ifdef HIM_DEBUG |
| 91 |
# define qHimDebug if (!qHimDebugEnabled()) {} else qDebug |
| 92 |
#else |
| 93 |
# define qHimDebug while (false) qDebug |
| 94 |
#endif |
| 95 |
|
| 96 |
static const char *debugNameForCommunicationId(HildonIMCommunication id) |
| 97 |
{ |
| 98 |
#ifdef HIM_DEBUG |
| 99 |
static const char * const mapping[] = { |
| 100 |
"HILDON_IM_CONTEXT_HANDLE_ENTER", |
| 101 |
"HILDON_IM_CONTEXT_HANDLE_TAB", |
| 102 |
"HILDON_IM_CONTEXT_HANDLE_BACKSPACE", |
| 103 |
"HILDON_IM_CONTEXT_HANDLE_SPACE", |
| 104 |
"HILDON_IM_CONTEXT_CONFIRM_SENTENCE_START", |
| 105 |
"HILDON_IM_CONTEXT_FLUSH_PREEDIT", |
| 106 |
"HILDON_IM_CONTEXT_CANCEL_PREEDIT", |
| 107 |
"HILDON_IM_CONTEXT_BUFFERED_MODE", |
| 108 |
"HILDON_IM_CONTEXT_DIRECT_MODE", |
| 109 |
"HILDON_IM_CONTEXT_REDIRECT_MODE", |
| 110 |
"HILDON_IM_CONTEXT_SURROUNDING_MODE", |
| 111 |
"HILDON_IM_CONTEXT_PREEDIT_MODE", |
| 112 |
"HILDON_IM_CONTEXT_CLIPBOARD_COPY", |
| 113 |
"HILDON_IM_CONTEXT_CLIPBOARD_CUT", |
| 114 |
"HILDON_IM_CONTEXT_CLIPBOARD_PASTE", |
| 115 |
"HILDON_IM_CONTEXT_CLIPBOARD_SELECTION_QUERY", |
| 116 |
"HILDON_IM_CONTEXT_REQUEST_SURROUNDING", |
| 117 |
"HILDON_IM_CONTEXT_REQUEST_SURROUNDING_FULL", |
| 118 |
"HILDON_IM_CONTEXT_WIDGET_CHANGED", |
| 119 |
"HILDON_IM_CONTEXT_OPTION_CHANGED", |
| 120 |
"HILDON_IM_CONTEXT_CLEAR_STICKY", |
| 121 |
"HILDON_IM_CONTEXT_ENTER_ON_FOCUS", |
| 122 |
"HILDON_IM_CONTEXT_SPACE_AFTER_COMMIT", |
| 123 |
"HILDON_IM_CONTEXT_NO_SPACE_AFTER_COMMIT" |
| 124 |
}; |
| 125 |
if (unsigned(id) < (sizeof(mapping) / sizeof(mapping[0]))) { |
| 126 |
return mapping[id]; |
| 127 |
} else { |
| 128 |
static char name[] = "ID 00"; |
| 129 |
name[3] = '0' + (id / 10); |
| 130 |
name[4] = '0' + (id % 10); |
| 131 |
return name; |
| 132 |
} |
| 133 |
#endif |
| 134 |
return 0; |
| 135 |
} |
| 136 |
|
| 137 |
|
| 138 |
#define LOGMESSAGE1(x) qHimDebug() << x; |
| 139 |
#define LOGMESSAGE2(x, y) qHimDebug() << x << "(" << y << ")"; |
| 140 |
#define LOGMESSAGE3(x, y, z) qHimDebug() << x << "(" << y << " " << z << ")"; |
| 141 |
|
| 142 |
|
| 143 |
QMap<QWidget *, QHIMProxyWidget *> QHIMProxyWidget::proxies; |
| 144 |
|
| 145 |
QHIMProxyWidget::QHIMProxyWidget(QWidget *widget) |
| 146 |
: QWidget(0), w(widget) |
| 147 |
{ |
| 148 |
setAttribute(Qt::WA_InputMethodEnabled); |
| 149 |
setAttribute(Qt::WA_NativeWindow); |
| 150 |
createWinId(); |
| 151 |
connect(w, SIGNAL(destroyed()), this, SLOT(widgetWasDestroyed())); |
| 152 |
} |
| 153 |
|
| 154 |
QHIMProxyWidget::~QHIMProxyWidget() |
| 155 |
{ |
| 156 |
} |
| 157 |
|
| 158 |
QWidget *QHIMProxyWidget::widget() const |
| 159 |
{ |
| 160 |
return w; |
| 161 |
} |
| 162 |
|
| 163 |
QHIMProxyWidget *QHIMProxyWidget::proxyFor(QWidget *w) |
| 164 |
{ |
| 165 |
QHIMProxyWidget *proxy = qobject_cast<QHIMProxyWidget *>(w); |
| 166 |
|
| 167 |
if (!proxy) |
| 168 |
proxy = proxies.value(w); |
| 169 |
|
| 170 |
if (!proxy) { |
| 171 |
proxy = new QHIMProxyWidget(w); |
| 172 |
proxies.insert(w, proxy); |
| 173 |
} |
| 174 |
//qWarning() << "Using HIM Proxy widget" << proxy << "for widget" << w << "isnative: " << proxy->testAttribute(Qt::WA_NativeWindow) << " / " << w->testAttribute(Qt::WA_NativeWindow); |
| 175 |
|
| 176 |
return proxy; |
| 177 |
} |
| 178 |
|
| 179 |
void QHIMProxyWidget::widgetWasDestroyed() |
| 180 |
{ |
| 181 |
proxies.remove(w); |
| 182 |
delete this; |
| 183 |
} |
| 184 |
|
| 185 |
|
| 186 |
/*! XkbLookupKeySym ( X11->display, event->nativeScanCode(), HILDON_IM_SHIFT_STICKY_MASK, &mods_rtrn, sym_rtrn) |
| 187 |
*/ |
| 188 |
static QString translateKeycodeAndState(KeyCode key, uint state, quint32 &keysym){ |
| 189 |
uint mods; |
| 190 |
KeySym *ks = reinterpret_cast<KeySym*>(&keysym); |
| 191 |
if ( XkbLookupKeySym ( X11->display, key, state, &mods, ks) ) |
| 192 |
return QKeyMapperPrivate::maemo5TranslateKeySym(*ks); |
| 193 |
else |
| 194 |
return QString(); |
| 195 |
} |
| 196 |
|
| 197 |
static Window findHildonIm() |
| 198 |
{ |
| 199 |
union |
| 200 |
{ |
| 201 |
Window *win; |
| 202 |
unsigned char *val; |
| 203 |
} value; |
| 204 |
|
| 205 |
Window result = 0; |
| 206 |
ulong n = 0; |
| 207 |
ulong extra = 0; |
| 208 |
int format = 0; |
| 209 |
Atom realType; |
| 210 |
|
| 211 |
int status = XGetWindowProperty(X11->display, QX11Info::appRootWindow(), |
| 212 |
ATOM(_HILDON_IM_WINDOW), 0L, 1L, 0, |
| 213 |
XA_WINDOW, &realType, &format, |
| 214 |
&n, &extra, (unsigned char **) &value.val); |
| 215 |
|
| 216 |
if (status == Success && realType == XA_WINDOW |
| 217 |
&& format == HILDON_IM_WINDOW_ID_FORMAT && n == 1 && value.win != 0) { |
| 218 |
result = value.win[0]; |
| 219 |
XFree(value.val); |
| 220 |
} else { |
| 221 |
qWarning("QHildonInputContext: Unable to get the Hildon IM window id"); |
| 222 |
} |
| 223 |
|
| 224 |
return result; |
| 225 |
} |
| 226 |
|
| 227 |
|
| 228 |
|
| 229 |
/*! Send a key event to the IM, which makes it available to the plugins |
| 230 |
*/ |
| 231 |
static void sendKeyEvent(QWidget *widget, QEvent::Type type, uint state, uint keyval, quint16 keycode) |
| 232 |
{ |
| 233 |
int gdkEventType; |
| 234 |
Window w = findHildonIm(); |
| 235 |
|
| 236 |
if (!w) |
| 237 |
return; |
| 238 |
|
| 239 |
//Translate QEvent::Type in GDK_Event |
| 240 |
switch (type){ |
| 241 |
case QEvent::KeyPress: |
| 242 |
gdkEventType = 8; |
| 243 |
break; |
| 244 |
case QEvent::KeyRelease: |
| 245 |
gdkEventType = 9; |
| 246 |
break; |
| 247 |
default: |
| 248 |
qWarning("QHildonInputContext: Event type not allowed"); |
| 249 |
return; |
| 250 |
} |
| 251 |
|
| 252 |
XEvent ev; |
| 253 |
memset(&ev, 0, sizeof(XEvent)); |
| 254 |
|
| 255 |
ev.xclient.type = ClientMessage; |
| 256 |
ev.xclient.window = w; |
| 257 |
ev.xclient.message_type = ATOM(_HILDON_IM_KEY_EVENT); |
| 258 |
ev.xclient.format = HILDON_IM_KEY_EVENT_FORMAT; |
| 259 |
|
| 260 |
HildonIMKeyEventMessage *msg = reinterpret_cast<HildonIMKeyEventMessage *>(&ev.xclient.data); |
| 261 |
msg->input_window = QHIMProxyWidget::proxyFor(widget)->winId(); |
| 262 |
|
| 263 |
msg->type = gdkEventType; |
| 264 |
msg->state = state; |
| 265 |
msg->keyval = keyval; |
| 266 |
msg->hardware_keycode = keycode; |
| 267 |
|
| 268 |
XSendEvent(X11->display, w, false, 0, &ev); |
| 269 |
XSync( X11->display, false ); |
| 270 |
} |
| 271 |
|
| 272 |
static quint32 dead_key_to_unicode_combining_character(int /*qtkeycode*/) |
| 273 |
{ |
| 274 |
quint32 combining = 0; //Unicode Hex value |
| 275 |
|
| 276 |
#if 0 |
| 277 |
//TODO Diablo - Not required in Fremantle |
| 278 |
switch (qtkeycode) |
| 279 |
{ |
| 280 |
case Qt::Key_Dead_Grave: combining = 0x0300; break; |
| 281 |
case Qt::Key_Dead_Acute: combining = 0x0301; break; |
| 282 |
case Qt::Key_Dead_Circumflex: combining = 0x0302; break; |
| 283 |
case Qt::Key_Dead_Tilde: combining = 0x0303; break; |
| 284 |
case Qt::Key_Dead_Macron: combining = 0x0304; break; |
| 285 |
case Qt::Key_Dead_Breve: combining = 0x032e; break; |
| 286 |
case Qt::Key_Dead_Abovedot: combining = 0x0307; break; |
| 287 |
case Qt::Key_Dead_Diaeresis: combining = 0x0308; break; |
| 288 |
case Qt::Key_Dead_Abovering: combining = 0x030a; break; |
| 289 |
case Qt::Key_Dead_Doubleacute: combining = 0x030b; break; |
| 290 |
case Qt::Key_Dead_Caron: combining = 0x030c; break; |
| 291 |
case Qt::Key_Dead_Cedilla: combining = 0x0327; break; |
| 292 |
case Qt::Key_Dead_Ogonek: combining = 0x0328; break; |
| 293 |
case Qt::Key_Dead_Iota: combining = 0; break; /* Cannot be combined */ |
| 294 |
case Qt::Key_Dead_Voiced_Sound: combining = 0; break; /* Cannot be combined */ |
| 295 |
case Qt::Key_Dead_Semivoiced_Sound: combining = 0; break; /* Cannot be combined */ |
| 296 |
case Qt::Key_Dead_Belowdot: combining = 0x0323; break; |
| 297 |
case Qt::Key_Dead_Hook: combining = 0x0309; break; |
| 298 |
case Qt::Key_Dead_Horn: combining = 0x031b; break; |
| 299 |
default: combining = 0; break; /* Unknown dead key */ |
| 300 |
} |
| 301 |
#endif |
| 302 |
|
| 303 |
return combining; |
| 304 |
} |
| 305 |
|
| 306 |
/*! Sends the key as a spontaneous event. |
| 307 |
*/ |
| 308 |
static void sendKey(QWidget *keywidget, int qtCode) |
| 309 |
{ |
| 310 |
QPointer<QWidget> guard = keywidget; |
| 311 |
|
| 312 |
KeySym keysym = NoSymbol; |
| 313 |
int keycode; |
| 314 |
|
| 315 |
switch (qtCode){ |
| 316 |
case Qt::Key_Enter: |
| 317 |
keycode = 36; |
| 318 |
break; |
| 319 |
case Qt::Key_Tab: |
| 320 |
keycode = 66; |
| 321 |
break; |
| 322 |
case Qt::Key_Backspace: |
| 323 |
keycode = 22; |
| 324 |
break; |
| 325 |
default: |
| 326 |
qWarning("keycode not allowed"); |
| 327 |
return; |
| 328 |
} |
| 329 |
|
| 330 |
keysym = XKeycodeToKeysym(X11->display, keycode, 0); |
| 331 |
|
| 332 |
QKeyEventEx click(QEvent::KeyPress, qtCode, Qt::NoModifier , QString(), false, 1, keycode, keysym, 0); |
| 333 |
qt_sendSpontaneousEvent(keywidget, &click); |
| 334 |
|
| 335 |
// in case the widget was destroyed when the key went down |
| 336 |
if (guard.isNull()){ |
| 337 |
return; |
| 338 |
} |
| 339 |
|
| 340 |
QKeyEventEx release(QEvent::KeyRelease, qtCode, Qt::NoModifier , QString(), false, 1, keycode, keysym, 0); |
| 341 |
qt_sendSpontaneousEvent(keywidget, &release); |
| 342 |
} |
| 343 |
|
| 344 |
/*! |
| 345 |
*/ |
| 346 |
static void answerClipboardSelectionQuery(QWidget *widget) |
| 347 |
{ |
| 348 |
bool hasSelection = !widget->inputMethodQuery(Qt::ImCurrentSelection).toString().isEmpty(); |
| 349 |
|
| 350 |
XEvent xev; |
| 351 |
Window w = findHildonIm(); |
| 352 |
|
| 353 |
memset(&xev, 0, sizeof(xev)); |
| 354 |
xev.xclient.type = ClientMessage; |
| 355 |
xev.xclient.window = w; |
| 356 |
xev.xclient.message_type = ATOM(_HILDON_IM_CLIPBOARD_SELECTION_REPLY); |
| 357 |
xev.xclient.format = HILDON_IM_CLIPBOARD_SELECTION_REPLY_FORMAT; |
| 358 |
xev.xclient.data.l[0] = hasSelection; |
| 359 |
|
| 360 |
XSendEvent(X11->display, w, false, 0, &xev); |
| 361 |
} |
| 362 |
|
| 363 |
|
| 364 |
KeySym getKeySymForLevel(int keycode, int level ){ |
| 365 |
XkbDescPtr xkbDesc = XkbGetMap(X11->display, XkbAllClientInfoMask, XkbUseCoreKbd); |
| 366 |
if (!xkbDesc) |
| 367 |
return NoSymbol; |
| 368 |
|
| 369 |
KeySym keySym = XkbKeySymEntry(xkbDesc, keycode, level, 0); |
| 370 |
|
| 371 |
//Check for a not repated keysym |
| 372 |
KeySym keySymTest = XkbKeySymEntry(xkbDesc, keycode, 0, 1); |
| 373 |
if (keySym == keySymTest) |
| 374 |
return NoSymbol; |
| 375 |
|
| 376 |
return keySym; |
| 377 |
} |
| 378 |
|
| 379 |
QHildonInputContext::QHildonInputContext(QObject* parent) |
| 380 |
: QInputContext(parent), |
| 381 |
timerId(-1), mask(0), options(0), |
| 382 |
triggerMode(HILDON_IM_TRIGGER_NONE), |
| 383 |
commitMode(HILDON_IM_COMMIT_REDIRECT), |
| 384 |
lastCommitMode(HILDON_IM_COMMIT_REDIRECT), |
| 385 |
inputMode(HILDON_GTK_INPUT_MODE_FULL), |
| 386 |
textCursorPosOnPress(0), autoUpper(false), |
| 387 |
lastInternalChange(false), spaceAfterCommit(false) |
| 388 |
{ |
| 389 |
} |
| 390 |
|
| 391 |
QHildonInputContext::~QHildonInputContext() |
| 392 |
{ |
| 393 |
sendHildonCommand(HILDON_IM_HIDE); |
| 394 |
} |
| 395 |
|
| 396 |
QString QHildonInputContext::identifierName() |
| 397 |
{ |
| 398 |
return QLatin1String("hildon"); |
| 399 |
} |
| 400 |
|
| 401 |
QString QHildonInputContext::language() |
| 402 |
{ |
| 403 |
//TODO GConf /apps/osso/inputmethod/hildon-im-languages |
| 404 |
return QString(); |
| 405 |
} |
| 406 |
|
| 407 |
/*! \internal |
| 408 |
* Resolves the focus for a widget inside a QGraphicsView. |
| 409 |
* Returns the Widget really holding the focus in this case. |
| 410 |
*/ |
| 411 |
QWidget *resolveFocusWidget(QWidget *w) |
| 412 |
{ |
| 413 |
#ifndef QT_NO_GRAPHICSVIEW |
| 414 |
while (QGraphicsView *view = qobject_cast<QGraphicsView *>(w)) { |
| 415 |
|
| 416 |
QGraphicsScene *scene = view->scene(); |
| 417 |
if (scene) { |
| 418 |
QGraphicsItem *item = scene->focusItem(); |
| 419 |
QGraphicsProxyWidget *proxy = qgraphicsitem_cast<QGraphicsProxyWidget *>(item); |
| 420 |
if (proxy && proxy->widget() && proxy->widget()->focusWidget() ) { |
| 421 |
w = proxy->widget()->focusWidget(); |
| 422 |
} else { |
| 423 |
break; |
| 424 |
} |
| 425 |
} else { |
| 426 |
break; |
| 427 |
} |
| 428 |
} |
| 429 |
#endif |
| 430 |
|
| 431 |
return w; |
| 432 |
} |
| 433 |
|
| 434 |
QWidget *QHildonInputContext::focusWidget() const |
| 435 |
{ |
| 436 |
return realFocus ? realFocus : lastFocus; |
| 437 |
} |
| 438 |
|
| 439 |
/*!\internal |
| 440 |
reset the UI state |
| 441 |
*/ |
| 442 |
void QHildonInputContext::reset() |
| 443 |
{ |
| 444 |
qHimDebug() << "HIM: reset()"; |
| 445 |
|
| 446 |
if (realFocus) |
| 447 |
sendHildonCommand(HILDON_IM_CLEAR, realFocus); |
| 448 |
|
| 449 |
cancelPreedit(); |
| 450 |
|
| 451 |
//Reset internals |
| 452 |
mask = 0; |
| 453 |
lastInternalChange = false; |
| 454 |
} |
| 455 |
|
| 456 |
bool QHildonInputContext::isComposing() const |
| 457 |
{ |
| 458 |
return false; |
| 459 |
} |
| 460 |
|
| 461 |
void QHildonInputContext::setFocusWidget(QWidget *w) |
| 462 |
{ |
| 463 |
// As soon as the virtual keyboard is mapped by the X11 server, |
| 464 |
// it is also activated, which essentially steals our focus. |
| 465 |
// The same happens for the symbol picker. |
| 466 |
// This is a bug in the HIM, that we try to work around here. |
| 467 |
lastFocus = realFocus; |
| 468 |
|
| 469 |
// Another work around for the GraphicsView. |
| 470 |
// In case of a Widget inside a GraphicsViewProxyWidget we need to remember |
| 471 |
// that it had the focus |
| 472 |
realFocus = resolveFocusWidget(w); |
| 473 |
|
| 474 |
if (realFocus) { |
| 475 |
Qt::InputMethodHints hints = realFocus->inputMethodHints(); |
| 476 |
|
| 477 |
// restrictions |
| 478 |
if ((hints & Qt::ImhExclusiveInputMask) == Qt::ImhDialableCharactersOnly) { |
| 479 |
inputMode = HILDON_GTK_INPUT_MODE_TELE; |
| 480 |
} else if (((hints & Qt::ImhExclusiveInputMask) == (Qt::ImhDigitsOnly | Qt::ImhUppercaseOnly)) || |
| 481 |
((hints & Qt::ImhExclusiveInputMask) == (Qt::ImhDigitsOnly | Qt::ImhLowercaseOnly))) { |
| 482 |
inputMode = HILDON_GTK_INPUT_MODE_HEXA; |
| 483 |
} else if ((hints & Qt::ImhExclusiveInputMask) == Qt::ImhDigitsOnly) { |
| 484 |
inputMode = HILDON_GTK_INPUT_MODE_NUMERIC; |
| 485 |
} else if ((hints & Qt::ImhExclusiveInputMask) == Qt::ImhFormattedNumbersOnly) { |
| 486 |
inputMode = HILDON_GTK_INPUT_MODE_NUMERIC | HILDON_GTK_INPUT_MODE_SPECIAL; |
| 487 |
} else { |
| 488 |
inputMode = HILDON_GTK_INPUT_MODE_FULL; |
| 489 |
} |
| 490 |
|
| 491 |
// behavior flags |
| 492 |
if (hints & Qt::ImhHiddenText) { |
| 493 |
inputMode |= HILDON_GTK_INPUT_MODE_INVISIBLE; |
| 494 |
} else { |
| 495 |
// no auto upper case or predictive text for passwords |
| 496 |
if (!(hints & Qt::ImhNoAutoUppercase)) |
| 497 |
inputMode |= HILDON_GTK_INPUT_MODE_AUTOCAP; |
| 498 |
if (!(hints & Qt::ImhNoPredictiveText)) |
| 499 |
inputMode |= HILDON_GTK_INPUT_MODE_DICTIONARY; |
| 500 |
} |
| 501 |
|
| 502 |
// multi-line support |
| 503 |
// TODO: this really needs to fixed in Qt |
| 504 |
if (qobject_cast<QTextEdit *>(realFocus) || qobject_cast<QPlainTextEdit *>(realFocus)) |
| 505 |
inputMode |= HILDON_GTK_INPUT_MODE_MULTILINE; |
| 506 |
|
| 507 |
qHimDebug("Mapped hint: 0x%x to mode: 0x%x", int(hints), int(inputMode)); |
| 508 |
} else { |
| 509 |
inputMode = 0; |
| 510 |
} |
| 511 |
|
| 512 |
QInputContext::setFocusWidget(w); |
| 513 |
|
| 514 |
qHimDebug() << "HIM: setFocusWidget: " << w << " (real: " << realFocus << " / last: " << lastFocus << ")"; |
| 515 |
} |
| 516 |
|
| 517 |
bool QHildonInputContext::filterEvent(const QEvent *event) |
| 518 |
{ |
| 519 |
QWidget *w = realFocus; |
| 520 |
if (!w) |
| 521 |
return false; |
| 522 |
|
| 523 |
switch (event->type()){ |
| 524 |
case QEvent::RequestSoftwareInputPanel:{ |
| 525 |
//On the device, these events are sent at the same time of the TabletRelease ones |
| 526 |
triggerMode = HILDON_IM_TRIGGER_FINGER; |
| 527 |
|
| 528 |
// workaround for a very weird interaction between QLineEdit (which |
| 529 |
// changes its internal editing:yes/no state on focus out) and the |
| 530 |
// Hildon fullscreen keyboard, which steals the focus from the |
| 531 |
// application (NOT when it shows up, but as only soon as the user |
| 532 |
// clicks on any button within the keyboard) |
| 533 |
if (QLineEdit *le = qobject_cast<QLineEdit *>(w)) { |
| 534 |
if (le->echoMode() == QLineEdit::PasswordEchoOnEdit) |
| 535 |
le->clear(); |
| 536 |
} |
| 537 |
|
| 538 |
sendHildonCommand(HILDON_IM_SETNSHOW, realFocus); |
| 539 |
return true; |
| 540 |
} |
| 541 |
case QEvent::KeyPress: |
| 542 |
case QEvent::KeyRelease: |
| 543 |
triggerMode = HILDON_IM_TRIGGER_KEYBOARD; |
| 544 |
return filterKeyPress(w, static_cast<const QKeyEvent *>(event)); |
| 545 |
|
| 546 |
default: |
| 547 |
break; |
| 548 |
} |
| 549 |
return QInputContext::filterEvent(event); |
| 550 |
} |
| 551 |
|
| 552 |
//TODO |
| 553 |
void QHildonInputContext::update() |
| 554 |
{ |
| 555 |
qHimDebug() << "HIM: update(): lastInternalChange =" << lastInternalChange; |
| 556 |
|
| 557 |
if (lastInternalChange) { |
| 558 |
//Autocase update |
| 559 |
checkSentenceStart(); |
| 560 |
lastInternalChange = false; |
| 561 |
} |
| 562 |
} |
| 563 |
|
| 564 |
/*! \internal |
| 565 |
Filters spontaneous keyevents then elaborates them and updates the Hildon Main UI |
| 566 |
* via XMessages. In some cases it creates and posts a new keyevent |
| 567 |
* as no spontaneous event. |
| 568 |
*/ |
| 569 |
bool QHildonInputContext::filterKeyPress(QWidget *keywidget, const QKeyEvent *event){ |
| 570 |
|
| 571 |
//Avoid to filter events generated by this function. |
| 572 |
// Also ignore non-extended events, since we can't handle those below. |
| 573 |
if (!event->spontaneous() || !event->hasExtendedInfo()) |
| 574 |
return false; |
| 575 |
|
| 576 |
const quint32 state = event->nativeModifiers(); |
| 577 |
const quint32 keycode = event->nativeScanCode(); |
| 578 |
quint32 keysym= event->nativeVirtualKey(); |
| 579 |
const int qtkeycode = event->key(); |
| 580 |
|
| 581 |
//qHimDebug("HIM: filterKeyPress Mask: %x state: %x options: %x keycode: %d keysym: %x QtKey: %x", |
| 582 |
// mask, state, options, keycode, keysym, qtkeycode); |
| 583 |
|
| 584 |
//Drop auto repeated keys for COMPOSE_KEY |
| 585 |
if (qtkeycode == COMPOSE_KEY && event->isAutoRepeat()){ |
| 586 |
return true; |
| 587 |
} |
| 588 |
|
| 589 |
//TODO MOVE |
| 590 |
static QWidget* lastKeywidget = 0; |
| 591 |
static int lastQtkeycode = 0; |
| 592 |
static qint32 combiningChar = 0; //Unicode rappresentation of the dead key. |
| 593 |
|
| 594 |
QString commitString; //String to commit to the Key Widget |
| 595 |
|
| 596 |
//Reset static vars when the widget change. |
| 597 |
if (keywidget != lastKeywidget){ |
| 598 |
mask = 0; |
| 599 |
lastKeywidget = keywidget; |
| 600 |
lastQtkeycode = 0; |
| 601 |
combiningChar = 0; |
| 602 |
} |
| 603 |
|
| 604 |
if (!qtkeycode) |
| 605 |
return true; |
| 606 |
|
| 607 |
//1. A dead key will not be immediately commited, but combined with the next key |
| 608 |
if (qtkeycode >= Qt::Key_Dead_Grave && qtkeycode <= Qt::Key_Dead_Horn) |
| 609 |
mask |= HILDON_IM_DEAD_KEY_MASK; |
| 610 |
else |
| 611 |
mask &= ~HILDON_IM_DEAD_KEY_MASK; |
| 612 |
|
| 613 |
if (mask & HILDON_IM_DEAD_KEY_MASK && combiningChar == 0) |
| 614 |
{ |
| 615 |
combiningChar = dead_key_to_unicode_combining_character(qtkeycode);//### WORKS? IMPROVE? |
| 616 |
return true; |
| 617 |
} |
| 618 |
|
| 619 |
/*2. Pressing any key while the compose key is pressed will keep that |
| 620 |
* character from being directly submitted to the application. This |
| 621 |
* allows the IM process to override the interpretation of the key |
| 622 |
*/ |
| 623 |
if (qtkeycode == COMPOSE_KEY) |
| 624 |
{ |
| 625 |
if (event->type() == QEvent::KeyPress) |
| 626 |
mask |= HILDON_IM_COMPOSE_MASK; |
| 627 |
else |
| 628 |
mask &= ~HILDON_IM_COMPOSE_MASK; |
| 629 |
} |
| 630 |
|
| 631 |
// 3 Sticky and locking keys initialization |
| 632 |
if (event->type() == QEvent::KeyRelease) |
| 633 |
{ |
| 634 |
if (qtkeycode == Qt::Key_Shift ) |
| 635 |
{ |
| 636 |
setMaskState(&mask, |
| 637 |
HILDON_IM_SHIFT_LOCK_MASK, |
| 638 |
HILDON_IM_SHIFT_STICKY_MASK, |
| 639 |
lastQtkeycode == Qt::Key_Shift); |
| 640 |
}else if (qtkeycode == LEVEL_KEY){ |
| 641 |
setMaskState(&mask, |
| 642 |
HILDON_IM_LEVEL_LOCK_MASK, |
| 643 |
HILDON_IM_LEVEL_STICKY_MASK, |
| 644 |
lastQtkeycode == LEVEL_KEY); |
| 645 |
} |
| 646 |
} |
| 647 |
|
| 648 |
//Update lastQtkeycode. |
| 649 |
lastQtkeycode=qtkeycode; |
| 650 |
|
| 651 |
if (qtkeycode == Qt::Key_Tab){ |
| 652 |
commitString = QLatin1String("\t"); |
| 653 |
} |
| 654 |
|
| 655 |
/* 5. When the level key is in sticky or locked state, translate the |
| 656 |
* keyboard state as if that level key was being held down. |
| 657 |
*/ |
| 658 |
if ((mask & (HILDON_IM_LEVEL_STICKY_MASK | HILDON_IM_LEVEL_LOCK_MASK)) || |
| 659 |
(state & STATE_LEVEL_MASK)) { |
| 660 |
commitString = translateKeycodeAndState(keycode, STATE_LEVEL_MASK, keysym); |
| 661 |
} |
| 662 |
|
| 663 |
/* If the input mode is strictly numeric and the digits are level |
| 664 |
* shifted on the layout, it's not necessary for the level key to |
| 665 |
* be pressed at all. |
| 666 |
*/ |
| 667 |
else if ((options & HILDON_IM_AUTOLEVEL_NUMERIC) && |
| 668 |
((inputMode & HILDON_GTK_INPUT_MODE_FULL) == HILDON_GTK_INPUT_MODE_NUMERIC)) { |
| 669 |
KeySym ks = getKeySymForLevel(keycode, NUMERIC_LEVEL); |
| 670 |
QString string = QKeyMapperPrivate::maemo5TranslateKeySym(ks); |
| 671 |
|
| 672 |
if (!string.isEmpty()) { |
| 673 |
keysym = ks; |
| 674 |
commitString = string; |
| 675 |
} |
| 676 |
} |
| 677 |
/* The input is forced to a predetermined level |
| 678 |
*/ |
| 679 |
else if (options & HILDON_IM_LOCK_LEVEL) |
| 680 |
{ |
| 681 |
KeySym ks = getKeySymForLevel(keycode, LOCKABLE_LEVEL); |
| 682 |
QString string = QKeyMapperPrivate::maemo5TranslateKeySym(ks); |
| 683 |
|
| 684 |
if (!string.isEmpty()){ |
| 685 |
keysym = ks; |
| 686 |
commitString = string; |
| 687 |
} |
| 688 |
} |
| 689 |
/* Hardware keyboard autocapitalization */ |
| 690 |
if (autoUpper && inputMode & HILDON_GTK_INPUT_MODE_AUTOCAP) |
| 691 |
{ |
| 692 |
qHimDebug() << "AutoCAP"; |
| 693 |
QChar currentChar; |
| 694 |
KeySym lower = NoSymbol; |
| 695 |
KeySym upper = NoSymbol; |
| 696 |
|
| 697 |
if (commitString.isEmpty()){ |
| 698 |
QString ks = QKeyMapperPrivate::maemo5TranslateKeySym(keysym); |
| 699 |
if (!ks.isEmpty()) |
| 700 |
currentChar = ks.at(0); |
| 701 |
}else{ |
| 702 |
currentChar = commitString.at(0); |
| 703 |
} |
| 704 |
|
| 705 |
XConvertCase(keysym, &lower, &upper); |
| 706 |
|
| 707 |
if (currentChar.isPrint()){ |
| 708 |
if (state & STATE_SHIFT_MASK){ |
| 709 |
currentChar = currentChar.toLower(); |
| 710 |
keysym = lower; |
| 711 |
} else { |
| 712 |
currentChar = currentChar.toUpper(); |
| 713 |
keysym = upper; |
| 714 |
} |
| 715 |
commitString = QString(currentChar); //sent to the widget |
| 716 |
} |
| 717 |
} |
| 718 |
|
| 719 |
//6. Shift lock or holding the shift down forces uppercase, ignoring autocap |
| 720 |
if (mask & HILDON_IM_SHIFT_LOCK_MASK || state & STATE_SHIFT_MASK) |
| 721 |
{ |
| 722 |
KeySym lower = NoSymbol; |
| 723 |
KeySym upper = NoSymbol; |
| 724 |
XConvertCase(keysym, &lower, &upper); |
| 725 |
QString tempStr = QKeyMapperPrivate::maemo5TranslateKeySym(upper); |
| 726 |
if (!tempStr.isEmpty()) |
| 727 |
commitString = tempStr.at(0); |
| 728 |
}else if (mask & HILDON_IM_SHIFT_STICKY_MASK){ |
| 729 |
KeySym lower = NoSymbol; |
| 730 |
KeySym upper = NoSymbol; |
| 731 |
QString tempStr = QKeyMapperPrivate::maemo5TranslateKeySym(keysym); |
| 732 |
QChar currentChar; |
| 733 |
if (!tempStr.isEmpty()){ |
| 734 |
currentChar = tempStr.at(0); |
| 735 |
|
| 736 |
/* Simulate shift key being held down in sticky state for non-printables */ |
| 737 |
if ( currentChar.isPrint() ){ |
| 738 |
/* For printable characters sticky shift negates the case, |
| 739 |
* including any autocapitalization changes |
| 740 |
*/ |
| 741 |
if ( currentChar.isUpper() ){ |
| 742 |
currentChar = currentChar.toLower(); |
| 743 |
lower = lower; |
| 744 |
}else{ |
| 745 |
currentChar = currentChar.toUpper(); |
| 746 |
upper = upper; |
| 747 |
} |
| 748 |
commitString = QString(currentChar); //sent to the widget |
| 749 |
} |
| 750 |
} |
| 751 |
} |
| 752 |
|
| 753 |
//F. word completion manipulation (for fremantle) |
| 754 |
if (event->type() == QEvent::KeyPress && |
| 755 |
lastCommitMode == HILDON_IM_COMMIT_PREEDIT && |
| 756 |
!preEditBuffer.isNull()) |
| 757 |
{ |
| 758 |
switch (qtkeycode){ |
| 759 |
case Qt::Key_Right:{ |
| 760 |
commitPreeditBuffer(); |
| 761 |
return true; |
| 762 |
} |
| 763 |
case Qt::Key_Backspace: |
| 764 |
case Qt::Key_Up: |
| 765 |
case Qt::Key_Down: |
| 766 |
case Qt::Key_Left:{ |
| 767 |
cancelPreedit(); |
| 768 |
return true; |
| 769 |
} |
| 770 |
|
| 771 |
case Qt::Key_Return: |
| 772 |
case Qt::Key_Enter: { |
| 773 |
cancelPreedit(); |
| 774 |
break; |
| 775 |
} |
| 776 |
default: { |
| 777 |
if (keysym == GDK_ISO_ENTER) |
| 778 |
cancelPreedit(); |
| 779 |
break; |
| 780 |
} |
| 781 |
} |
| 782 |
} |
| 783 |
|
| 784 |
//7. Sticky and lock state reset |
| 785 |
if (event->type() == QEvent::KeyRelease) |
| 786 |
{ |
| 787 |
if (qtkeycode != Qt::Key_Shift ) |
| 788 |
{ |
| 789 |
/* If not locked, pressing any character resets shift state */ |
| 790 |
if ((mask & HILDON_IM_SHIFT_LOCK_MASK) == 0) |
| 791 |
{ |
| 792 |
mask &= ~HILDON_IM_SHIFT_STICKY_MASK; |
| 793 |
} |
| 794 |
} |
| 795 |
if (qtkeycode != LEVEL_KEY) |
| 796 |
{ |
| 797 |
/* If not locked, pressing any character resets level state */ |
| 798 |
if ((mask & HILDON_IM_LEVEL_LOCK_MASK) == 0) |
| 799 |
{ |
| 800 |
mask &= ~HILDON_IM_LEVEL_STICKY_MASK; |
| 801 |
} |
| 802 |
} |
| 803 |
} |
| 804 |
|
| 805 |
if (event->type() == QEvent::KeyRelease || state & STATE_CONTROL_MASK) |
| 806 |
{ |
| 807 |
//QString debug = QLatin1String("Sending state=0x%1 keysym=0x%2 keycode=%3"); |
| 808 |
//LOGMESSAGE2(" - ", debug.arg(state,0,16).arg(keysym,0,16).arg(keycode)); |
| 809 |
|
| 810 |
sendKeyEvent(keywidget, event->type(), state, keysym, keycode); |
| 811 |
return false; |
| 812 |
} |
| 813 |
|
| 814 |
|
| 815 |
/* 8. Pressing a dead key twice, or if followed by a space, inputs |
| 816 |
* the dead key's character representation |
| 817 |
*/ |
| 818 |
if ((mask & HILDON_IM_DEAD_KEY_MASK || qtkeycode == Qt::Key_Space) && combiningChar) |
| 819 |
{ |
| 820 |
qint32 last; |
| 821 |
last = dead_key_to_unicode_combining_character (qtkeycode); |
| 822 |
if ((last == combiningChar) || qtkeycode == Qt::Key_Space) |
| 823 |
{ |
| 824 |
commitString = QString(combiningChar); |
| 825 |
}else{ |
| 826 |
commitString = QString::fromUtf8(XKeysymToString(keysym)); |
| 827 |
} |
| 828 |
combiningChar = 0; |
| 829 |
}else{ |
| 830 |
/* Regular keypress */ |
| 831 |
if (mask & HILDON_IM_COMPOSE_MASK) |
| 832 |
{ |
| 833 |
sendKeyEvent(keywidget, event->type(),state, keysym, keycode); |
| 834 |
return true; |
| 835 |
}else{ |
| 836 |
if ( commitString.isEmpty() && qtkeycode != Qt::Key_Backspace){ |
| 837 |
//LOGMESSAGE3(" - ", "text sent to IM", event->text()) |
| 838 |
commitString = QString(event->text()); |
| 839 |
} |
| 840 |
} |
| 841 |
} |
| 842 |
|
| 843 |
/* Control keys should not produce commitString, if they do it's a bug |
| 844 |
on keymap side and we have to workaround this here */ |
| 845 |
if (qtkeycode == Qt::Key_Return || qtkeycode == Qt::Key_Enter || |
| 846 |
keysym == GDK_ISO_ENTER || qtkeycode == Qt::Key_Backspace) { |
| 847 |
commitString = QString(); |
| 848 |
lastInternalChange = true; |
| 849 |
} else if (qtkeycode == Qt::Key_Shift || qtkeycode == Qt::Key_AltGr || |
| 850 |
qtkeycode == Qt::Key_Control) { |
| 851 |
commitString = QString(); |
| 852 |
} |
| 853 |
|
| 854 |
if (!commitString.isEmpty()){ |
| 855 |
//entering a new character cleans the preedit buffer |
| 856 |
cancelPreedit(); |
| 857 |
|
| 858 |
/* Pressing a dead key followed by a regular key combines to form |
| 859 |
* an accented character |
| 860 |
*/ |
| 861 |
if (combiningChar){ //FIXME |
| 862 |
commitString.append(combiningChar);//This will be sent to the widget |
| 863 |
const char *charStr = qPrintable(commitString); |
| 864 |
keysym = XStringToKeysym(charStr); //This will be sent to the IM |
| 865 |
} |
| 866 |
|
| 867 |
//Create the new event with the elaborate information, |
| 868 |
//then it adds the event to the events queue |
| 869 |
{ |
| 870 |
QEvent::Type type = event->type(); |
| 871 |
Qt::KeyboardModifiers modifiers= event->modifiers(); |
| 872 |
//WARNING the qt keycode has not been updated!! |
| 873 |
QKeyEventEx *ke= new QKeyEventEx(type, keycode, modifiers, commitString, false, commitString.size(), keycode, keysym, state); |
| 874 |
QCoreApplication::postEvent(keywidget,ke); |
| 875 |
} |
| 876 |
|
| 877 |
//Send the new keysym |
| 878 |
sendKeyEvent(keywidget, event->type(), state, keysym, keycode); |
| 879 |
#if 0 |
| 880 |
/* Non-printable characters invalidate any previous dead keys */ |
| 881 |
if (qtkeycode != Qt::Key_Shift) |
| 882 |
combiningChar=0; |
| 883 |
#endif |
| 884 |
lastInternalChange = true; |
| 885 |
return true; |
| 886 |
} else { |
| 887 |
//Send the new keysym |
| 888 |
sendKeyEvent(keywidget, event->type(), state, keysym, keycode); |
| 889 |
return false; |
| 890 |
} |
| 891 |
} |
| 892 |
|
| 893 |
void QHildonInputContext::setCommitMode(HildonIMCommitMode mode, bool clearPreEdit) |
| 894 |
{ |
| 895 |
if (commitMode != mode) { |
| 896 |
if (clearPreEdit) |
| 897 |
preEditBuffer.clear(); |
| 898 |
lastCommitMode = commitMode; |
| 899 |
} |
| 900 |
commitMode = mode; |
| 901 |
} |
| 902 |
|
| 903 |
|
| 904 |
|
| 905 |
/*! \internal |
| 906 |
Filters the XClientMessages sent by QApplication_x11 |
| 907 |
*/ |
| 908 |
bool QHildonInputContext::x11FilterEvent(QWidget *keywidget, XEvent *event) |
| 909 |
{ |
| 910 |
if (QHIMProxyWidget *proxy = qobject_cast<QHIMProxyWidget *>(keywidget)) |
| 911 |
keywidget = proxy->widget(); |
| 912 |
|
| 913 |
if (event->xclient.message_type == ATOM(_HILDON_IM_INSERT_UTF8) && |
| 914 |
event->xclient.format == HILDON_IM_INSERT_UTF8_FORMAT) { |
| 915 |
qHimDebug() << "HIM: x11FilterEvent( HILDON_IM_INSERT_UTF8_FORMAT )"; |
| 916 |
|
| 917 |
HildonIMInsertUtf8Message *msg = reinterpret_cast<HildonIMInsertUtf8Message *>(&event->xclient.data); |
| 918 |
insertUtf8(msg->msg_flag, QString::fromUtf8(msg->utf8_str)); |
| 919 |
return true; |
| 920 |
} else if (event->xclient.message_type == ATOM(_HILDON_IM_COM)) { |
| 921 |
HildonIMComMessage *msg = (HildonIMComMessage *)&event->xclient.data; |
| 922 |
options = msg->options; |
| 923 |
|
| 924 |
qHimDebug() << "HIM: x11FilterEvent( _HILDON_IM_COM /" << debugNameForCommunicationId(msg->type) << ")"; |
| 925 |
|
| 926 |
switch (msg->type) { |
| 927 |
//Handle Keys msgs |
| 928 |
case HILDON_IM_CONTEXT_HANDLE_ENTER: |
| 929 |
sendKey(keywidget, Qt::Key_Enter); |
| 930 |
return true; |
| 931 |
case HILDON_IM_CONTEXT_HANDLE_TAB: |
| 932 |
sendKey(keywidget, Qt::Key_Tab); |
| 933 |
return true; |
| 934 |
case HILDON_IM_CONTEXT_HANDLE_BACKSPACE: |
| 935 |
sendKey(keywidget, Qt::Key_Backspace); |
| 936 |
return true; |
| 937 |
case HILDON_IM_CONTEXT_HANDLE_SPACE: |
| 938 |
insertUtf8(HILDON_IM_MSG_CONTINUE, QChar(Qt::Key_Space)); |
| 939 |
commitPreeditBuffer(); |
| 940 |
return true; |
| 941 |
|
| 942 |
//Handle Clipboard msgs |
| 943 |
case HILDON_IM_CONTEXT_CLIPBOARD_SELECTION_QUERY: |
| 944 |
answerClipboardSelectionQuery(keywidget); |
| 945 |
return true; |
| 946 |
case HILDON_IM_CONTEXT_CLIPBOARD_PASTE: |
| 947 |
if (QClipboard *clipboard = QApplication::clipboard()) { |
| 948 |
QInputMethodEvent e; |
| 949 |
e.setCommitString(clipboard->text()); |
| 950 |
QApplication::sendEvent(keywidget, &e); |
| 951 |
} |
| 952 |
return true; |
| 953 |
case HILDON_IM_CONTEXT_CLIPBOARD_COPY: |
| 954 |
if (QClipboard *clipboard = QApplication::clipboard()) |
| 955 |
clipboard->setText(keywidget->inputMethodQuery(Qt::ImCurrentSelection).toString()); |
| 956 |
return true; |
| 957 |
case HILDON_IM_CONTEXT_CLIPBOARD_CUT: |
| 958 |
if (QClipboard *clipboard = QApplication::clipboard()) { |
| 959 |
clipboard->setText(keywidget->inputMethodQuery(Qt::ImCurrentSelection).toString()); |
| 960 |
QInputMethodEvent ev; |
| 961 |
QApplication::sendEvent(keywidget, &ev); |
| 962 |
} |
| 963 |
return true; |
| 964 |
|
| 965 |
//Handle commit mode msgs |
| 966 |
case HILDON_IM_CONTEXT_DIRECT_MODE: |
| 967 |
setCommitMode(HILDON_IM_COMMIT_DIRECT); |
| 968 |
return true; |
| 969 |
case HILDON_IM_CONTEXT_BUFFERED_MODE: |
| 970 |
setCommitMode(HILDON_IM_COMMIT_BUFFERED); |
| 971 |
return true; |
| 972 |
case HILDON_IM_CONTEXT_REDIRECT_MODE: |
| 973 |
setCommitMode(HILDON_IM_COMMIT_REDIRECT); |
| 974 |
clearSelection(); |
| 975 |
return true; |
| 976 |
case HILDON_IM_CONTEXT_SURROUNDING_MODE: |
| 977 |
setCommitMode(HILDON_IM_COMMIT_SURROUNDING); |
| 978 |
return true; |
| 979 |
case HILDON_IM_CONTEXT_PREEDIT_MODE: |
| 980 |
setCommitMode(HILDON_IM_COMMIT_PREEDIT); |
| 981 |
return true; |
| 982 |
|
| 983 |
//Handle context |
| 984 |
case HILDON_IM_CONTEXT_CONFIRM_SENTENCE_START: |
| 985 |
checkSentenceStart(); |
| 986 |
return true; |
| 987 |
case HILDON_IM_CONTEXT_FLUSH_PREEDIT: |
| 988 |
commitPreeditBuffer(); |
| 989 |
return true; |
| 990 |
case HILDON_IM_CONTEXT_REQUEST_SURROUNDING: |
| 991 |
sendSurrounding(false); |
| 992 |
return true; |
| 993 |
case HILDON_IM_CONTEXT_CLEAR_STICKY: |
| 994 |
mask &= ~(HILDON_IM_SHIFT_STICKY_MASK | |
| 995 |
HILDON_IM_SHIFT_LOCK_MASK | |
| 996 |
HILDON_IM_LEVEL_STICKY_MASK | |
| 997 |
HILDON_IM_LEVEL_LOCK_MASK); |
| 998 |
return true; |
| 999 |
case HILDON_IM_CONTEXT_CANCEL_PREEDIT: |
| 1000 |
cancelPreedit(); |
| 1001 |
return true; |
| 1002 |
case HILDON_IM_CONTEXT_REQUEST_SURROUNDING_FULL: |
| 1003 |
sendSurrounding(true); |
| 1004 |
return true; |
| 1005 |
case HILDON_IM_CONTEXT_SPACE_AFTER_COMMIT: |
| 1006 |
case HILDON_IM_CONTEXT_NO_SPACE_AFTER_COMMIT: |
| 1007 |
spaceAfterCommit = (msg->type == HILDON_IM_CONTEXT_SPACE_AFTER_COMMIT); |
| 1008 |
return true; |
| 1009 |
|
| 1010 |
case HILDON_IM_CONTEXT_WIDGET_CHANGED: |
| 1011 |
case HILDON_IM_CONTEXT_ENTER_ON_FOCUS: |
| 1012 |
// ignore |
| 1013 |
return true; |
| 1014 |
|
| 1015 |
default: |
| 1016 |
qWarning() << "HIM: x11FilterEvent( _HILDON_IM_COM /" << debugNameForCommunicationId(msg->type) << ") was not handled."; |
| 1017 |
break; |
| 1018 |
} |
| 1019 |
} else if (event->xclient.message_type == ATOM(_HILDON_IM_SURROUNDING_CONTENT) && |
| 1020 |
event->xclient.format == HILDON_IM_SURROUNDING_CONTENT_FORMAT) { |
| 1021 |
qWarning() << "HIM: x11FilterEvent( _HILDON_IM_SURROUNDING_CONTENT ) is not supported"; |
| 1022 |
} else if (event->xclient.message_type == ATOM(_HILDON_IM_SURROUNDING) && |
| 1023 |
event->xclient.format == HILDON_IM_SURROUNDING_FORMAT) { |
| 1024 |
qHimDebug() << "HIM: x11FilterEvent( _HILDON_IM_SURROUNDING )"; |
| 1025 |
|
| 1026 |
HildonIMSurroundingMessage *msg = reinterpret_cast<HildonIMSurroundingMessage*>(&event->xclient.data); |
| 1027 |
setClientCursorLocation(msg->offset_is_relative, msg->cursor_offset ); |
| 1028 |
return true; |
| 1029 |
} |
| 1030 |
return false; |
| 1031 |
} |
| 1032 |
|
| 1033 |
/*! \internal |
| 1034 |
Ask the client widget to insert the specified text at the cursor |
| 1035 |
* position, by triggering the commit signal on the context |
| 1036 |
*/ |
| 1037 |
void QHildonInputContext::insertUtf8(int flag, const QString& text) |
| 1038 |
{ |
| 1039 |
qHimDebug() << "HIM: insertUtf8(" << flag << ", " << text << ")"; |
| 1040 |
|
| 1041 |
QWidget *w = focusWidget(); |
| 1042 |
if (!w) |
| 1043 |
return; |
| 1044 |
|
| 1045 |
QString cleanText = text; |
| 1046 |
if (mask & HILDON_IM_SHIFT_LOCK_MASK) |
| 1047 |
cleanText = cleanText.toUpper(); |
| 1048 |
|
| 1049 |
lastInternalChange = true; |
| 1050 |
|
| 1051 |
//TODO HILDON_IM_AUTOCORRECT is used by the hadwriting plugin |
| 1052 |
//Writing CiAo in the plugin add Ciao in the widget. |
| 1053 |
if (options & HILDON_IM_AUTOCORRECT){ |
| 1054 |
qWarning() << "HILDON_IM_AUTOCORRECT Not Implemented Yet"; |
| 1055 |
} |
| 1056 |
|
| 1057 |
//Delete suroundings when we are using the preeditbuffer. |
| 1058 |
// Eg: For the HandWriting plugin |
| 1059 |
if (!preEditBuffer.isNull()) { |
| 1060 |
//Updates preEditBuffer |
| 1061 |
if (flag != HILDON_IM_MSG_START) { |
| 1062 |
preEditBuffer.append(cleanText); |
| 1063 |
cleanText = preEditBuffer; |
| 1064 |
} |
| 1065 |
} |
| 1066 |
|
| 1067 |
if (commitMode == HILDON_IM_COMMIT_PREEDIT) { |
| 1068 |
if (preEditBuffer.isNull()) |
| 1069 |
preEditBuffer = cleanText; |
| 1070 |
|
| 1071 |
//Creating attribute list |
| 1072 |
QList<QInputMethodEvent::Attribute> attributes; |
| 1073 |
QTextCharFormat textCharFormat; |
| 1074 |
textCharFormat.setFontUnderline(true); |
| 1075 |
textCharFormat.setBackground(w->palette().highlight()); |
| 1076 |
textCharFormat.setForeground(w->palette().base()); |
| 1077 |
attributes << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, cleanText.length(), textCharFormat); |
| 1078 |
attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()); |
| 1079 |
|
| 1080 |
QInputMethodEvent e(cleanText, attributes); |
| 1081 |
QApplication::sendEvent(w, &e); |
| 1082 |
|
| 1083 |
//Reset commit mode |
| 1084 |
if (flag == HILDON_IM_MSG_END) |
| 1085 |
setCommitMode(lastCommitMode, false); |
| 1086 |
} else { // commitMode != HILDON_IM_COMMIT_PREEDIT |
| 1087 |
QInputMethodEvent e; |
| 1088 |
e.setCommitString(cleanText); |
| 1089 |
QApplication::sendEvent(w, &e); |
| 1090 |
} |
| 1091 |
} |
| 1092 |
|
| 1093 |
void QHildonInputContext::clearSelection() |
| 1094 |
{ |
| 1095 |
qHimDebug() << "HIM: clearSelection()"; |
| 1096 |
|
| 1097 |
QWidget *w = focusWidget(); |
| 1098 |
if (!w) |
| 1099 |
return; |
| 1100 |
|
| 1101 |
int textCursorPos = w->inputMethodQuery(Qt::ImCursorPosition).toInt(); |
| 1102 |
QString selection = w->inputMethodQuery(Qt::ImCurrentSelection).toString(); |
| 1103 |
|
| 1104 |
if (selection.isEmpty()) |
| 1105 |
return; |
| 1106 |
|
| 1107 |
//Remove the selection |
| 1108 |
QInputMethodEvent e; |
| 1109 |
e.setCommitString(selection); |
| 1110 |
QApplication::sendEvent(w, &e); |
| 1111 |
|
| 1112 |
//Move the cursor backward if the text has been selected from right to left |
| 1113 |
if (textCursorPos < textCursorPosOnPress){ |
| 1114 |
QInputMethodEvent e; |
| 1115 |
e.setCommitString(QString(), -selection.length(),0); |
| 1116 |
QApplication::sendEvent(w, &e); |
| 1117 |
} |
| 1118 |
} |
| 1119 |
|
| 1120 |
void QHildonInputContext::cancelPreedit() |
| 1121 |
{ |
| 1122 |
qHimDebug() << "HIM: cancelPreedit()"; |
| 1123 |
|
| 1124 |
QWidget *w = focusWidget(); |
| 1125 |
if (!w) |
| 1126 |
return; |
| 1127 |
|
| 1128 |
if (preEditBuffer.isEmpty()) |
| 1129 |
return; |
| 1130 |
preEditBuffer.clear(); |
| 1131 |
|
| 1132 |
QInputMethodEvent e; |
| 1133 |
QApplication::sendEvent(w, &e); |
| 1134 |
} |
| 1135 |
|
| 1136 |
void QHildonInputContext::sendHildonCommand(HildonIMCommand cmd, QWidget *widget) |
| 1137 |
{ |
| 1138 |
qHimDebug() << "HIM: sendHildonCommand(" << cmd << "," << widget << ")"; |
| 1139 |
|
| 1140 |
Window w = findHildonIm(); |
| 1141 |
if (!w) |
| 1142 |
return; |
| 1143 |
|
| 1144 |
XEvent ev; |
| 1145 |
memset(&ev, 0, sizeof(XEvent)); |
| 1146 |
|
| 1147 |
ev.xclient.type = ClientMessage; |
| 1148 |
ev.xclient.window = w; |
| 1149 |
ev.xclient.message_type = ATOM(_HILDON_IM_ACTIVATE); |
| 1150 |
ev.xclient.format = HILDON_IM_ACTIVATE_FORMAT; |
| 1151 |
|
| 1152 |
HildonIMActivateMessage *msg = reinterpret_cast<HildonIMActivateMessage *>(&ev.xclient.data); |
| 1153 |
|
| 1154 |
if (widget) { |
| 1155 |
msg->input_window = QHIMProxyWidget::proxyFor(widget)->winId(); |
| 1156 |
msg->app_window = widget->window()->winId(); |
| 1157 |
} else if (cmd != HILDON_IM_HIDE) { |
| 1158 |
qWarning() << "Invalid Hildon Command:" << cmd; |
| 1159 |
return; |
| 1160 |
} |
| 1161 |
|
| 1162 |
if (cmd == HILDON_IM_HIDE && timerId != -1) |
| 1163 |
killTimer(timerId); |
| 1164 |
|
| 1165 |
if (cmd == HILDON_IM_SETCLIENT || cmd == HILDON_IM_SETNSHOW) |
| 1166 |
sendInputMode(); |
| 1167 |
|
| 1168 |
msg->cmd = cmd; |
| 1169 |
msg->trigger = triggerMode; |
| 1170 |
|
| 1171 |
XSendEvent(X11->display, w, false, 0, &ev); |
| 1172 |
XSync(X11->display, False); |
| 1173 |
} |
| 1174 |
|
| 1175 |
|
| 1176 |
/*! |
| 1177 |
\internal |
| 1178 |
*/ |
| 1179 |
void QHildonInputContext::sendX11Event(XEvent *event) |
| 1180 |
{ |
| 1181 |
if (Window w = findHildonIm()) { |
| 1182 |
event->xclient.type = ClientMessage; |
| 1183 |
event->xclient.window = w; |
| 1184 |
|
| 1185 |
XSendEvent(X11->display, w, false, 0, event); |
| 1186 |
XSync(X11->display, False); |
| 1187 |
} |
| 1188 |
} |
| 1189 |
|
| 1190 |
//CONTEXT |
| 1191 |
/*! \internal |
| 1192 |
Updates the IM with the autocap state at the active cursor position |
| 1193 |
*/ |
| 1194 |
void QHildonInputContext::checkSentenceStart() |
| 1195 |
{ |
| 1196 |
qHimDebug() << "HIM: checkSentenceStart()"; |
| 1197 |
|
| 1198 |
QWidget *w = focusWidget(); |
| 1199 |
if (!w) |
| 1200 |
return; |
| 1201 |
|
| 1202 |
if ((inputMode & (HILDON_GTK_INPUT_MODE_ALPHA | HILDON_GTK_INPUT_MODE_AUTOCAP)) != |
| 1203 |
(HILDON_GTK_INPUT_MODE_ALPHA | HILDON_GTK_INPUT_MODE_AUTOCAP)) { |
| 1204 |
// If autocap is off, but the mode contains alpha, send autocap message. |
| 1205 |
// The important part is that when entering a numerical entry the autocap |
| 1206 |
// is not defined, and the plugin sets the mode appropriate for the language */ |
| 1207 |
if (inputMode & HILDON_GTK_INPUT_MODE_ALPHA) { |
| 1208 |
autoUpper = false; |
| 1209 |
sendHildonCommand(HILDON_IM_LOW, w); |
| 1210 |
} |
| 1211 |
return; |
| 1212 |
} else if (inputMode & HILDON_GTK_INPUT_MODE_INVISIBLE) { |
| 1213 |
// no autocap for passwords |
| 1214 |
autoUpper = false; |
| 1215 |
sendHildonCommand(HILDON_IM_LOW, w); |
| 1216 |
} |
| 1217 |
|
| 1218 |
int cpos = w->inputMethodQuery(Qt::ImCursorPosition).toInt(); |
| 1219 |
QString analyze; |
| 1220 |
const int analyzeCount = 10; |
| 1221 |
|
| 1222 |
// Improve performance: only analyze 10 chars before the cursor |
| 1223 |
if (cpos) { |
| 1224 |
analyze = w->inputMethodQuery(Qt::ImSurroundingText).toString() |
| 1225 |
.mid(qMax(cpos - analyzeCount, 0), qMin(cpos, analyzeCount)); |
| 1226 |
} |
| 1227 |
|
| 1228 |
int spaces = 0; |
| 1229 |
|
| 1230 |
while (spaces < analyze.length()) { |
| 1231 |
if (analyze.at(analyze.length() - spaces - 1).isSpace()) |
| 1232 |
spaces++; |
| 1233 |
else |
| 1234 |
break; |
| 1235 |
} |
| 1236 |
|
| 1237 |
// not very nice, but QTextBoundaryFinder doesn't really work here |
| 1238 |
static const QString punctuation = QLatin1String(".!?\xa1\xbf"); // spanish inverted ! and ? |
| 1239 |
|
| 1240 |
if (!cpos || analyze.length() == spaces) { |
| 1241 |
autoUpper = true; |
| 1242 |
sendHildonCommand(HILDON_IM_UPP, w); |
| 1243 |
} else if (spaces && punctuation.contains(analyze.at(analyze.length() - spaces - 1))) { |
| 1244 |
autoUpper = options & HILDON_IM_AUTOCASE; |
| 1245 |
sendHildonCommand(HILDON_IM_UPP, w); |
| 1246 |
} else { |
| 1247 |
autoUpper = false; |
| 1248 |
sendHildonCommand(HILDON_IM_LOW, w); |
| 1249 |
} |
| 1250 |
} |
| 1251 |
|
| 1252 |
void QHildonInputContext::commitPreeditBuffer() |
| 1253 |
{ |
| 1254 |
qHimDebug() << "HIM: commitPreeditBuffer()"; |
| 1255 |
|
| 1256 |
QWidget *w = focusWidget(); |
| 1257 |
if (!w) |
| 1258 |
return; |
| 1259 |
|
| 1260 |
QInputMethodEvent e; |
| 1261 |
|
| 1262 |
if (spaceAfterCommit) |
| 1263 |
e.setCommitString(preEditBuffer + QLatin1Char(' ')); |
| 1264 |
else |
| 1265 |
e.setCommitString(preEditBuffer); |
| 1266 |
|
| 1267 |
QApplication::sendEvent(w, &e); |
| 1268 |
preEditBuffer.clear(); |
| 1269 |
} |
| 1270 |
|
| 1271 |
void QHildonInputContext::sendSurrounding(bool sendAllContents) |
| 1272 |
{ |
| 1273 |
QWidget *w = focusWidget(); |
| 1274 |
if (!w) |
| 1275 |
return; |
| 1276 |
|
| 1277 |
QString surrounding; |
| 1278 |
int cpos; |
| 1279 |
if (sendAllContents) { |
| 1280 |
// Qt::ImSurrounding only returns the current block |
| 1281 |
|
| 1282 |
if (QTextEdit *te = qobject_cast<QTextEdit*>(w)) { |
| 1283 |
surrounding = te->toPlainText(); |
| 1284 |
cpos = te->textCursor().position(); |
| 1285 |
} else if (QPlainTextEdit *pte = qobject_cast<QPlainTextEdit*>(w)) { |
| 1286 |
surrounding = pte->toPlainText(); |
| 1287 |
cpos = pte->textCursor().position(); |
| 1288 |
} else { |
| 1289 |
surrounding = w->inputMethodQuery(Qt::ImSurroundingText).toString(); |
| 1290 |
cpos = w->inputMethodQuery(Qt::ImCursorPosition).toInt(); |
| 1291 |
} |
| 1292 |
} else { |
| 1293 |
surrounding = w->inputMethodQuery(Qt::ImSurroundingText).toString(); |
| 1294 |
cpos = w->inputMethodQuery(Qt::ImCursorPosition).toInt(); |
| 1295 |
} |
| 1296 |
|
| 1297 |
if (surrounding.isEmpty()) |
| 1298 |
cpos = 0; |
| 1299 |
|
| 1300 |
XEvent xev; |
| 1301 |
HildonIMSurroundingContentMessage *surroundingContentMsg = reinterpret_cast<HildonIMSurroundingContentMessage*>(&xev.xclient.data); |
| 1302 |
|
| 1303 |
// Split surrounding context into parts that are small enough to send in a X11 message |
| 1304 |
QByteArray ba = surrounding.toUtf8(); |
| 1305 |
bool firstPart = true; |
| 1306 |
int offset = 0; |
| 1307 |
|
| 1308 |
while (firstPart || (offset < ba.size())) { |
| 1309 |
//this call will take care of adding the trailing '\0' for surrounding string |
| 1310 |
memset(&xev, 0, sizeof(XEvent)); |
| 1311 |
xev.xclient.message_type = ATOM(_HILDON_IM_SURROUNDING_CONTENT); |
| 1312 |
xev.xclient.format = HILDON_IM_SURROUNDING_CONTENT_FORMAT; |
| 1313 |
|
| 1314 |
int len = qMin(ba.size() - offset, int(HILDON_IM_CLIENT_MESSAGE_BUFFER_SIZE) - 1); |
| 1315 |
::memcpy(surroundingContentMsg->surrounding, ba.constData() + offset, len); |
| 1316 |
offset += len; |
| 1317 |
|
| 1318 |
if (firstPart) |
| 1319 |
surroundingContentMsg->msg_flag = HILDON_IM_MSG_START; |
| 1320 |
else if (offset == ba.size()) |
| 1321 |
surroundingContentMsg->msg_flag = HILDON_IM_MSG_END; |
| 1322 |
else |
| 1323 |
surroundingContentMsg->msg_flag = HILDON_IM_MSG_CONTINUE; |
| 1324 |
|
| 1325 |
sendX11Event(&xev); |
| 1326 |
firstPart = false; |
| 1327 |
} |
| 1328 |
|
| 1329 |
// Send the cursor offset in the surrounding |
| 1330 |
memset(&xev, 0, sizeof(XEvent)); |
| 1331 |
xev.xclient.message_type = ATOM(_HILDON_IM_SURROUNDING); |
| 1332 |
xev.xclient.format = HILDON_IM_SURROUNDING_FORMAT; |
| 1333 |
|
| 1334 |
HildonIMSurroundingMessage *surroundingMsg = reinterpret_cast<HildonIMSurroundingMessage *>(&xev.xclient.data); |
| 1335 |
surroundingMsg->commit_mode = commitMode; |
| 1336 |
surroundingMsg->cursor_offset = cpos; |
| 1337 |
sendX11Event(&xev); |
| 1338 |
} |
| 1339 |
|
| 1340 |
|
| 1341 |
/*! \internal |
| 1342 |
Notify IM of any input mode changes |
| 1343 |
*/ |
| 1344 |
void QHildonInputContext::inputModeChanged() |
| 1345 |
{ |
| 1346 |
qHimDebug() << "HIM: inputModeChanged()"; |
| 1347 |
|
| 1348 |
#if 0 |
| 1349 |
//TODO |
| 1350 |
if ((input_mode & HILDON_GTK_INPUT_MODE_ALPHA) == 0 && |
| 1351 |
(input_mode & HILDON_GTK_INPUT_MODE_HEXA) == 0 && |
| 1352 |
( (input_mode & HILDON_GTK_INPUT_MODE_NUMERIC) != 0 || |
| 1353 |
(input_mode & HILDON_GTK_INPUT_MODE_TELE) != 0)) |
| 1354 |
{ |
| 1355 |
self->mask = HILDON_IM_LEVEL_LOCK_MASK | HILDON_IM_LEVEL_STICKY_MASK; |
| 1356 |
} |
| 1357 |
else |
| 1358 |
{ |
| 1359 |
self->mask &= ~HILDON_IM_LEVEL_LOCK_MASK; |
| 1360 |
self->mask &= ~HILDON_IM_LEVEL_STICKY_MASK; |
| 1361 |
} |
| 1362 |
#endif |
| 1363 |
/* Notify IM of any input mode changes in cases where the UI is |
| 1364 |
already visible. */ |
| 1365 |
sendInputMode(); |
| 1366 |
} |
| 1367 |
|
| 1368 |
void QHildonInputContext::sendInputMode() |
| 1369 |
{ |
| 1370 |
qHimDebug() << "HIM: sendInputMode"; |
| 1371 |
|
| 1372 |
Window w = findHildonIm(); |
| 1373 |
if (!w) |
| 1374 |
return; |
| 1375 |
|
| 1376 |
XEvent ev; |
| 1377 |
memset(&ev, 0, sizeof(XEvent)); |
| 1378 |
|
| 1379 |
ev.xclient.type = ClientMessage; |
| 1380 |
ev.xclient.window = w; |
| 1381 |
ev.xclient.message_type = ATOM(_HILDON_IM_INPUT_MODE); |
| 1382 |
ev.xclient.format = HILDON_IM_INPUT_MODE_FORMAT; |
| 1383 |
|
| 1384 |
HildonIMInputModeMessage *msg = reinterpret_cast<HildonIMInputModeMessage *>(&ev.xclient.data); |
| 1385 |
msg->input_mode = static_cast<HildonGtkInputMode>(inputMode); |
| 1386 |
msg->default_input_mode = static_cast<HildonGtkInputMode>(HILDON_GTK_INPUT_MODE_FULL); |
| 1387 |
|
| 1388 |
XSendEvent(X11->display, w, false, 0, &ev); |
| 1389 |
XSync(X11->display, False); |
| 1390 |
} |
| 1391 |
|
| 1392 |
/*! \internal |
| 1393 |
In redirect mode we use a proxy widget (fullscreen vkb). When the cursor position |
| 1394 |
* changes there, the HIM update the cursor position in the client (Qt application) |
| 1395 |
*/ |
| 1396 |
void QHildonInputContext::setClientCursorLocation(bool offsetIsRelative, int cursorOffset) |
| 1397 |
{ |
| 1398 |
qHimDebug() << "HIM: setClientCursorLocation(" << offsetIsRelative << ", " << cursorOffset<< ")"; |
| 1399 |
|
| 1400 |
QWidget *w = focusWidget(); |
| 1401 |
if (!w) |
| 1402 |
return; |
| 1403 |
|
| 1404 |
if (offsetIsRelative) |
| 1405 |
cursorOffset += w->inputMethodQuery(Qt::ImCursorPosition).toInt(); |
| 1406 |
|
| 1407 |
|
| 1408 |
QList<QInputMethodEvent::Attribute> attributes; |
| 1409 |
|
| 1410 |
attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, |
| 1411 |
cursorOffset, 0, QVariant()); |
| 1412 |
|
| 1413 |
QInputMethodEvent e(QString(), attributes); |
| 1414 |
QApplication::sendEvent(w, &e); |
| 1415 |
} |
| 1416 |
|
| 1417 |
void QHildonInputContext::setMaskState(int *mask, |
| 1418 |
HildonIMInternalModifierMask lock_mask, |
| 1419 |
HildonIMInternalModifierMask sticky_mask, |
| 1420 |
bool was_press_and_release) |
| 1421 |
{ |
| 1422 |
//LOGMESSAGE3("setMaskState", lock_mask, sticky_mask) |
| 1423 |
//LOGMESSAGE3(" - ", "mask=", *mask) |
| 1424 |
|
| 1425 |
/* Locking Fn is disabled in TELE and NUMERIC */ |
| 1426 |
if (!(inputMode & HILDON_GTK_INPUT_MODE_ALPHA) && |
| 1427 |
!(inputMode & HILDON_GTK_INPUT_MODE_HEXA) && |
| 1428 |
((inputMode & HILDON_GTK_INPUT_MODE_TELE) || |
| 1429 |
(inputMode & HILDON_GTK_INPUT_MODE_NUMERIC)) |
| 1430 |
) { |
| 1431 |
if (*mask & lock_mask){ |
| 1432 |
/* already locked, remove lock and set it to sticky */ |
| 1433 |
*mask &= ~(lock_mask | sticky_mask); |
| 1434 |
*mask |= sticky_mask; |
| 1435 |
}else if (*mask & sticky_mask){ |
| 1436 |
/* the key is already sticky, it's fine */ |
| 1437 |
}else if (was_press_and_release){ |
| 1438 |
/* Pressing the key for the first time stickies the key for one character, |
| 1439 |
* but only if no characters were entered while holding the key down */ |
| 1440 |
*mask |= sticky_mask; |
| 1441 |
} |
| 1442 |
return; |
| 1443 |
} |
| 1444 |
|
| 1445 |
if (*mask & lock_mask) |
| 1446 |
{ |
| 1447 |
/* Pressing the key while already locked clears the state */ |
| 1448 |
if (lock_mask & HILDON_IM_SHIFT_LOCK_MASK) |
| 1449 |
sendHildonCommand(HILDON_IM_SHIFT_UNLOCKED, realFocus); |
| 1450 |
else if (lock_mask & HILDON_IM_LEVEL_LOCK_MASK) |
| 1451 |
sendHildonCommand(HILDON_IM_MOD_UNLOCKED, realFocus); |
| 1452 |
|
| 1453 |
*mask &= ~(lock_mask | sticky_mask); |
| 1454 |
} else if (*mask & sticky_mask) { |
| 1455 |
/* When the key is already sticky, a second press locks the key */ |
| 1456 |
*mask |= lock_mask; |
| 1457 |
|
| 1458 |
if (lock_mask & HILDON_IM_SHIFT_LOCK_MASK) |
| 1459 |
sendHildonCommand(HILDON_IM_SHIFT_LOCKED, realFocus); |
| 1460 |
else if (lock_mask & HILDON_IM_LEVEL_LOCK_MASK) |
| 1461 |
sendHildonCommand(HILDON_IM_MOD_LOCKED, realFocus); |
| 1462 |
}else if (was_press_and_release){ |
| 1463 |
/* Pressing the key for the first time stickies the key for one character, |
| 1464 |
* but only if no characters were entered while holding the key down */ |
| 1465 |
*mask |= sticky_mask; |
| 1466 |
} |
| 1467 |
|
| 1468 |
} |
| 1469 |
|
| 1470 |
#endif |