| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). |
| 4 | ** All rights reserved. |
| 5 | ** Contact: Nokia Corporation (qt-info@nokia.com) |
| 6 | ** |
| 7 | ** This file is part of the QtGui module of the Qt Toolkit. |
| 8 | ** |
| 9 | ** $QT_BEGIN_LICENSE:LGPL$ |
| 10 | ** Commercial Usage |
| 11 | ** Licensees holding valid Qt Commercial licenses may use this file in |
| 12 | ** accordance with the Qt Commercial License Agreement provided with the |
| 13 | ** Software or, alternatively, in accordance with the terms contained in |
| 14 | ** a written agreement between you and Nokia. |
| 15 | ** |
| 16 | ** GNU Lesser General Public License Usage |
| 17 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
| 18 | ** General Public License version 2.1 as published by the Free Software |
| 19 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
| 20 | ** packaging of this file. Please review the following information to |
| 21 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
| 22 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| 23 | ** |
| 24 | ** In addition, as a special exception, Nokia gives you certain additional |
| 25 | ** rights. These rights are described in the Nokia Qt LGPL Exception |
| 26 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| 27 | ** |
| 28 | ** GNU General Public License Usage |
| 29 | ** Alternatively, this file may be used under the terms of the GNU |
| 30 | ** General Public License version 3.0 as published by the Free Software |
| 31 | ** Foundation and appearing in the file LICENSE.GPL included in the |
| 32 | ** packaging of this file. Please review the following information to |
| 33 | ** ensure the GNU General Public License version 3.0 requirements will be |
| 34 | ** met: http://www.gnu.org/copyleft/gpl.html. |
| 35 | ** |
| 36 | ** If you have questions regarding the use of this file, please contact |
| 37 | ** Nokia at qt-info@nokia.com. |
| 38 | ** $QT_END_LICENSE$ |
| 39 | ** |
| 40 | ****************************************************************************/ |
| 41 | |
| 42 | #include "qos2inputcontext_p.h" |
| 43 | #include "qinputcontext_p.h" |
| 44 | |
| 45 | #include "qfont.h" |
| 46 | #include "qwidget.h" |
| 47 | #include "qapplication.h" |
| 48 | #include "qevent.h" |
| 49 | #include "qtextformat.h" |
| 50 | #include "qtextboundaryfinder.h" |
| 51 | |
| 52 | //#define Q_IME_DEBUG |
| 53 | |
| 54 | #ifdef Q_IME_DEBUG |
| 55 | #include "qdebug.h" |
| 56 | #endif |
| 57 | |
| 58 | #include <os2im.h> |
| 59 | |
| 60 | QT_BEGIN_NAMESPACE |
| 61 | |
| 62 | bool qt_sendSpontaneousEvent(QObject*, QEvent*); |
| 63 | |
| 64 | static QString *imeComposition = 0; |
| 65 | static int imePosition = -1; |
| 66 | static bool haveCursor = false; |
| 67 | |
| 68 | static HMODULE im32Mod = NULLHANDLE; |
| 69 | static APIRET (APIENTRY *im32AssociateInstance)(HWND, HIMI, PHIMI); |
| 70 | static APIRET (APIENTRY *im32GetInstance)(HWND, PHIMI); |
| 71 | static APIRET (APIENTRY *im32ReleaseInstance)(HWND, HIMI); |
| 72 | static APIRET (APIENTRY *im32GetConversionString)(HIMI, ULONG, PVOID, PULONG); |
| 73 | static APIRET (APIENTRY *im32GetResultString)(HIMI, ULONG, PVOID, PULONG); |
| 74 | static APIRET (APIENTRY *im32RequestIme)(HIMI, ULONG, ULONG, ULONG); |
| 75 | |
| 76 | static void loadIme() |
| 77 | { |
| 78 | if (im32Mod) |
| 79 | return; |
| 80 | |
| 81 | CHAR szName[CCHMAXPATH]; |
| 82 | ULONG rc; |
| 83 | |
| 84 | rc = DosLoadModule(szName, sizeof(szName), "os2im", &im32Mod); |
| 85 | |
| 86 | if (!rc) |
| 87 | rc = DosQueryProcAddr(im32Mod, 101, NULL, |
| 88 | (PFN *)&im32AssociateInstance); |
| 89 | |
| 90 | if (!rc) |
| 91 | rc = DosQueryProcAddr(im32Mod, 104, NULL, (PFN *)&im32GetInstance); |
| 92 | |
| 93 | if (!rc) |
| 94 | rc = DosQueryProcAddr(im32Mod, 106, NULL, (PFN *)&im32ReleaseInstance); |
| 95 | |
| 96 | if (!rc) |
| 97 | rc = DosQueryProcAddr(im32Mod, 118, NULL, |
| 98 | (PFN *)&im32GetConversionString); |
| 99 | |
| 100 | if (!rc) |
| 101 | rc = DosQueryProcAddr(im32Mod, 122, NULL, (PFN *)&im32GetResultString); |
| 102 | |
| 103 | if (!rc) |
| 104 | rc = DosQueryProcAddr(im32Mod, 131, NULL, (PFN *)&im32RequestIme); |
| 105 | |
| 106 | if (rc) { |
| 107 | DosFreeModule(im32Mod); |
| 108 | |
| 109 | im32Mod = NULLHANDLE; |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | static int getCursorPosition(HIMI himi) |
| 114 | { |
| 115 | ULONG len = 256; |
| 116 | CHAR buffer[len]; |
| 117 | |
| 118 | if (im32GetConversionString(himi, IMR_CONV_CONVERSIONSTRING, buffer, &len)) |
| 119 | return 0; |
| 120 | |
| 121 | ULONG cursorPos; |
| 122 | ULONG cursorPosLen = sizeof(cursorPos); |
| 123 | |
| 124 | if (im32GetConversionString(himi, IMR_CONV_CURSORPOS, &cursorPos, |
| 125 | &cursorPosLen)) |
| 126 | return 0; |
| 127 | |
| 128 | return QString::fromLocal8Bit(buffer, cursorPos).length(); |
| 129 | } |
| 130 | |
| 131 | static QString getString(HIMI himi, ULONG ulIndex, int *selStart = 0, |
| 132 | int *selLength = 0) |
| 133 | { |
| 134 | const int bufferSize = 256; |
| 135 | ULONG len = bufferSize; |
| 136 | CHAR buffer[len]; |
| 137 | |
| 138 | if (ulIndex == IMR_CONV_CONVERSIONSTRING) { |
| 139 | if (im32GetConversionString(himi, ulIndex, buffer, &len)) |
| 140 | len = 0; |
| 141 | } else if (ulIndex == IMR_RESULT_RESULTSTRING) { |
| 142 | if (im32GetResultString(himi, ulIndex, buffer, &len)) |
| 143 | len = 0; |
| 144 | } else |
| 145 | len = 0; |
| 146 | |
| 147 | if (ulIndex == IMR_CONV_CONVERSIONSTRING && selStart) { |
| 148 | ULONG attrLen = bufferSize; |
| 149 | CHAR attrBuffer[attrLen]; |
| 150 | if (im32GetConversionString(himi, IMR_CONV_CONVERSIONATTR, attrBuffer, |
| 151 | &attrLen)) |
| 152 | attrLen = 0; |
| 153 | |
| 154 | *selStart = QString::fromLocal8Bit(buffer, attrLen).length() + 1; |
| 155 | *selLength = -1; |
| 156 | for (int i = 0; i < (LONG)attrLen; i++) { |
| 157 | if (attrBuffer[i] & CP_ATTR_TARGET_CONVERTED) { |
| 158 | int index = QString::fromLocal8Bit(buffer, i).length(); |
| 159 | |
| 160 | *selStart = qMin(*selStart, index); |
| 161 | *selLength = qMax(*selLength, index); |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | *selLength = qMax(0, *selLength - *selStart + 1); |
| 166 | } |
| 167 | |
| 168 | if (len <= 0) |
| 169 | return QString(); |
| 170 | |
| 171 | return QString::fromLocal8Bit(buffer, len); |
| 172 | } |
| 173 | |
| 174 | QOS2InputContext::QOS2InputContext(QObject *parent) |
| 175 | : QInputContext(parent), recursionGuard(false) |
| 176 | { |
| 177 | loadIme(); |
| 178 | } |
| 179 | |
| 180 | QOS2InputContext::~QOS2InputContext() |
| 181 | { |
| 182 | delete imeComposition; |
| 183 | imeComposition = 0; |
| 184 | } |
| 185 | |
| 186 | void QOS2InputContext::update() |
| 187 | { |
| 188 | if (!im32Mod) |
| 189 | return; |
| 190 | |
| 191 | QWidget *w = focusWidget(); |
| 192 | if(!w) |
| 193 | return; |
| 194 | |
| 195 | Q_ASSERT(w->testAttribute(Qt::WA_WState_Created)); |
| 196 | |
| 197 | HIMI himi; |
| 198 | |
| 199 | if (im32GetInstance(w->effectiveWinId(), &himi)) |
| 200 | return; |
| 201 | |
| 202 | QRect r = w->inputMethodQuery(Qt::ImMicroFocus).toRect(); |
| 203 | |
| 204 | // The ime window positions are based on the WinId with active focus. |
| 205 | QWidget *imeWnd = QWidget::find(::WinQueryFocus(HWND_DESKTOP)); |
| 206 | if (imeWnd) { |
| 207 | QPoint pt (r.bottomLeft()); |
| 208 | pt = w->mapToGlobal(pt); |
| 209 | pt = imeWnd->mapFromGlobal(pt); |
| 210 | |
| 211 | // invert Y |
| 212 | // bottom is exclusive on Qt |
| 213 | pt.setY(imeWnd->height() - pt.y()); |
| 214 | |
| 215 | if(haveCursor) |
| 216 | WinCreateCursor(imeWnd->effectiveWinId(), pt.x(), pt.y(), 0, 0, |
| 217 | CURSOR_SETPOS, NULL); |
| 218 | } |
| 219 | |
| 220 | // todo : set composition font, composition window |
| 221 | // BTW is this really needed ? |
| 222 | |
| 223 | im32ReleaseInstance(w->effectiveWinId(), himi); |
| 224 | } |
| 225 | |
| 226 | |
| 227 | bool QOS2InputContext::endComposition() |
| 228 | { |
| 229 | QWidget *fw = focusWidget(); |
| 230 | #ifdef Q_IME_DEBUG |
| 231 | qDebug("endComposition! fw = %s", |
| 232 | fw ? qPrintable(fw->objectName()) : "(null)"); |
| 233 | #endif |
| 234 | |
| 235 | if (!im32Mod) |
| 236 | return false; |
| 237 | |
| 238 | bool result = true; |
| 239 | if(imePosition == -1 || recursionGuard) |
| 240 | return result; |
| 241 | |
| 242 | // Googles Pinyin Input Method likes to call endComposition again |
| 243 | // when we call notifyIME with CPS_CANCEL, so protect ourselves |
| 244 | // against that. |
| 245 | recursionGuard = true; |
| 246 | |
| 247 | if (fw) { |
| 248 | Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created)); |
| 249 | HIMI himi; |
| 250 | im32GetInstance(fw->effectiveWinId(), &himi); |
| 251 | im32RequestIme(himi, REQ_CONVERSIONSTRING, CNV_CANCEL, 0); |
| 252 | im32ReleaseInstance(fw->effectiveWinId(), himi); |
| 253 | if(haveCursor) { |
| 254 | WinDestroyCursor(fw->effectiveWinId()); |
| 255 | haveCursor = false; |
| 256 | } |
| 257 | } |
| 258 | |
| 259 | if (!fw) |
| 260 | fw = QApplication::focusWidget(); |
| 261 | |
| 262 | if (fw) { |
| 263 | QInputMethodEvent e; |
| 264 | result = qt_sendSpontaneousEvent(fw, &e); |
| 265 | } |
| 266 | |
| 267 | if (imeComposition) |
| 268 | imeComposition->clear(); |
| 269 | imePosition = -1; |
| 270 | |
| 271 | recursionGuard = false; |
| 272 | |
| 273 | return result; |
| 274 | } |
| 275 | |
| 276 | void QOS2InputContext::reset() |
| 277 | { |
| 278 | QWidget *fw = focusWidget(); |
| 279 | |
| 280 | #ifdef Q_IME_DEBUG |
| 281 | qDebug("sending accept to focus widget %s", |
| 282 | fw ? qPrintable(fw->objectName()) : "(null)"); |
| 283 | #endif |
| 284 | |
| 285 | if (!im32Mod) |
| 286 | return; |
| 287 | |
| 288 | if (fw && imePosition != -1) { |
| 289 | QInputMethodEvent e; |
| 290 | if (imeComposition) |
| 291 | e.setCommitString(*imeComposition); |
| 292 | qt_sendSpontaneousEvent(fw, &e); |
| 293 | } |
| 294 | |
| 295 | if (imeComposition) |
| 296 | imeComposition->clear(); |
| 297 | imePosition = -1; |
| 298 | |
| 299 | if (fw) { |
| 300 | Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created)); |
| 301 | HIMI himi; |
| 302 | im32GetInstance(fw->effectiveWinId(), &himi); |
| 303 | im32RequestIme(himi, REQ_CONVERSIONSTRING, CNV_CANCEL, 0); |
| 304 | im32ReleaseInstance(fw->effectiveWinId(), himi); |
| 305 | } |
| 306 | } |
| 307 | |
| 308 | |
| 309 | bool QOS2InputContext::startComposition() |
| 310 | { |
| 311 | #ifdef Q_IME_DEBUG |
| 312 | qDebug("startComposition"); |
| 313 | #endif |
| 314 | |
| 315 | if (!im32Mod) |
| 316 | return false; |
| 317 | |
| 318 | if (!imeComposition) |
| 319 | imeComposition = new QString(); |
| 320 | |
| 321 | QWidget *fw = focusWidget(); |
| 322 | if (fw) { |
| 323 | Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created)); |
| 324 | imePosition = 0; |
| 325 | haveCursor = WinCreateCursor(fw->effectiveWinId(), 0, 0, 1, 1, |
| 326 | CURSOR_SOLID, NULL); |
| 327 | WinShowCursor(fw->effectiveWinId(), FALSE); |
| 328 | update(); |
| 329 | } |
| 330 | return fw != 0; |
| 331 | } |
| 332 | |
| 333 | enum StandardFormat { |
| 334 | PreeditFormat, |
| 335 | SelectionFormat |
| 336 | }; |
| 337 | |
| 338 | bool QOS2InputContext::composition(ULONG mp2) |
| 339 | { |
| 340 | #ifdef Q_IME_DEBUG |
| 341 | QString str; |
| 342 | if (mp2 & IMR_RESULT_RESULTSTRING) |
| 343 | str += QLatin1String("RESULTSTRING "); |
| 344 | if (mp2 & IMR_CONV_CONVERSIONSTRING) |
| 345 | str += QLatin1String("CONVERSIONSTRING "); |
| 346 | if (mp2 & IMR_CONV_CONVERSIONATTR) |
| 347 | str += QLatin1String("CONVERSIONATTR "); |
| 348 | if (mp2 & IMR_CONV_CURSORPOS) |
| 349 | str += QLatin1String("CURSORPOS "); |
| 350 | if (mp2 & IMR_CONV_CONVERSIONCLAUSE) |
| 351 | str += QLatin1String("CONVERSIONCLAUSE "); |
| 352 | if (mp2 & IMR_CONV_INSERTCHAR) |
| 353 | str += QLatin1String("INSERTCHAR "); |
| 354 | if (mp2 & IMR_CONV_NOMOVECARET) |
| 355 | str += QLatin1String("NOMOVECARET "); |
| 356 | if (mp2 & IMR_CONV_CONVERSIONPOS) |
| 357 | str += QLatin1String("CONVERSIONPOS "); |
| 358 | qDebug("composition, mp2=(%lx) %s imePosition=%d", |
| 359 | mp2, qPrintable(str), imePosition); |
| 360 | #endif |
| 361 | |
| 362 | if (!im32Mod) |
| 363 | return false; |
| 364 | |
| 365 | bool result = true; |
| 366 | |
| 367 | QWidget *fw = QApplication::focusWidget(); |
| 368 | if (fw) { |
| 369 | Q_ASSERT(fw->testAttribute(Qt::WA_WState_Created)); |
| 370 | HIMI himi; |
| 371 | im32GetInstance(fw->effectiveWinId(), &himi); |
| 372 | QInputMethodEvent e; |
| 373 | if (mp2 & (IMR_CONV_CONVERSIONSTRING | |
| 374 | IMR_CONV_CONVERSIONATTR | |
| 375 | IMR_CONV_CURSORPOS)) { |
| 376 | if (imePosition == -1) |
| 377 | // need to send a start event |
| 378 | startComposition(); |
| 379 | |
| 380 | // some intermediate composition result |
| 381 | int selStart, selLength; |
| 382 | *imeComposition = getString(himi, IMR_CONV_CONVERSIONSTRING, |
| 383 | &selStart, &selLength); |
| 384 | imePosition = getCursorPosition(himi); |
| 385 | if ((mp2 & IMR_CONV_INSERTCHAR) && (mp2 & IMR_CONV_NOMOVECARET)) { |
| 386 | // make korean work correctly. Hope this is correct for all IMEs |
| 387 | selStart = 0; |
| 388 | selLength = imeComposition->length(); |
| 389 | } |
| 390 | if(selLength == 0) |
| 391 | selStart = 0; |
| 392 | |
| 393 | QList<QInputMethodEvent::Attribute> attrs; |
| 394 | if (selStart > 0) |
| 395 | attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, selStart, |
| 396 | standardFormat(PreeditFormat)); |
| 397 | if (selLength) |
| 398 | attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, selStart, selLength, |
| 399 | standardFormat(SelectionFormat)); |
| 400 | if (selStart + selLength < imeComposition->length()) |
| 401 | attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, selStart + selLength, |
| 402 | imeComposition->length() - selStart - selLength, |
| 403 | standardFormat(PreeditFormat)); |
| 404 | if(imePosition >= 0) |
| 405 | attrs << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, imePosition, selLength ? 0 : 1, QVariant()); |
| 406 | |
| 407 | e = QInputMethodEvent(*imeComposition, attrs); |
| 408 | } |
| 409 | |
| 410 | if (mp2 & IMR_RESULT_RESULTSTRING) { |
| 411 | if(imePosition == -1) |
| 412 | startComposition(); |
| 413 | // a fixed result, return the converted string |
| 414 | *imeComposition = getString(himi, IMR_RESULT_RESULTSTRING); |
| 415 | imePosition = -1; |
| 416 | e.setCommitString(*imeComposition); |
| 417 | imeComposition->clear(); |
| 418 | } |
| 419 | |
| 420 | if (!e.preeditString().isEmpty() || !e.commitString().isEmpty()) |
| 421 | result = qt_sendSpontaneousEvent(fw, &e); |
| 422 | |
| 423 | update(); |
| 424 | im32ReleaseInstance(fw->effectiveWinId(), himi); |
| 425 | } |
| 426 | #ifdef Q_IME_DEBUG |
| 427 | qDebug("imecomposition: cursor pos at %d, result=%d", imePosition, result); |
| 428 | #endif |
| 429 | return result; |
| 430 | } |
| 431 | |
| 432 | static HIMI defaultInstance = 0; |
| 433 | |
| 434 | // checks whether widget is a popup |
| 435 | inline bool isPopup(QWidget *w) |
| 436 | { |
| 437 | if (w && (w->windowFlags() & Qt::Popup) == Qt::Popup) |
| 438 | return true; |
| 439 | else |
| 440 | return false; |
| 441 | } |
| 442 | // checks whether widget is in a popup |
| 443 | inline bool isInPopup(QWidget *w) |
| 444 | { |
| 445 | if (w && (isPopup(w) || isPopup(w->window()))) |
| 446 | return true; |
| 447 | else |
| 448 | return false; |
| 449 | } |
| 450 | |
| 451 | // find the parent widget, which is a non popup toplevel |
| 452 | // this is valid only if the widget is/in a popup |
| 453 | inline QWidget *findParentforPopup(QWidget *w) |
| 454 | { |
| 455 | QWidget *e = QWidget::find(w->effectiveWinId()); |
| 456 | // check if this or its parent is a popup |
| 457 | while (isInPopup(e)) { |
| 458 | e = e->window()->parentWidget(); |
| 459 | if (!e) |
| 460 | break; |
| 461 | e = QWidget::find(e->effectiveWinId()); |
| 462 | } |
| 463 | if (e) |
| 464 | return e->window(); |
| 465 | else |
| 466 | return 0; |
| 467 | } |
| 468 | |
| 469 | // enables or disables the ime |
| 470 | inline void enableIme(QWidget *w, bool value) |
| 471 | { |
| 472 | if (!im32Mod) |
| 473 | return; |
| 474 | |
| 475 | HIMI oldInstance; |
| 476 | if (value) { |
| 477 | // enable ime |
| 478 | if (defaultInstance) |
| 479 | im32AssociateInstance(w->effectiveWinId(), defaultInstance, |
| 480 | &oldInstance); |
| 481 | } else { |
| 482 | // disable ime |
| 483 | im32AssociateInstance(w->effectiveWinId(), NULL, &oldInstance); |
| 484 | if (!defaultInstance) |
| 485 | defaultInstance = oldInstance; |
| 486 | } |
| 487 | } |
| 488 | |
| 489 | |
| 490 | void QOS2InputContext::updateImeStatus(QWidget *w, bool hasFocus) |
| 491 | { |
| 492 | if (!w) |
| 493 | return; |
| 494 | // It's always the proxy that carries the hints. |
| 495 | QWidget *focusProxyWidget = w->focusProxy(); |
| 496 | if (!focusProxyWidget) |
| 497 | focusProxyWidget = w; |
| 498 | bool e = w->testAttribute(Qt::WA_InputMethodEnabled) && w->isEnabled() |
| 499 | && !(focusProxyWidget->inputMethodHints() & (Qt::ImhExclusiveInputMask | Qt::ImhHiddenText)); |
| 500 | bool hasIme = e && hasFocus; |
| 501 | #ifdef Q_IME_DEBUG |
| 502 | qDebug("%s HasFocus = %d hasIme = %d e = %d ", |
| 503 | qPrintable(w->objectName()), hasFocus, hasIme, e); |
| 504 | #endif |
| 505 | if (hasFocus || e) { |
| 506 | if (isInPopup(w)) |
| 507 | QOS2InputContext::enablePopupChild(w, hasIme); |
| 508 | else |
| 509 | QOS2InputContext::enable(w, hasIme); |
| 510 | } |
| 511 | } |
| 512 | |
| 513 | void QOS2InputContext::enablePopupChild(QWidget *w, bool e) |
| 514 | { |
| 515 | if (!w || !isInPopup(w)) |
| 516 | return; |
| 517 | #ifdef Q_IME_DEBUG |
| 518 | qDebug("enablePopupChild: w=%s, enable = %s", |
| 519 | w ? qPrintable(w->objectName()) : "(null)" , e ? "true" : "false"); |
| 520 | #endif |
| 521 | QWidget *parent = findParentforPopup(w); |
| 522 | if (parent) { |
| 523 | // update ime status of the normal toplevel parent of the popup |
| 524 | enableIme(parent, e); |
| 525 | } |
| 526 | QWidget *toplevel = w->window(); |
| 527 | if (toplevel) { |
| 528 | // update ime status of the toplevel popup |
| 529 | enableIme(toplevel, e); |
| 530 | } |
| 531 | } |
| 532 | |
| 533 | void QOS2InputContext::enable(QWidget *w, bool e) |
| 534 | { |
| 535 | if(w) { |
| 536 | #ifdef Q_IME_DEBUG |
| 537 | qDebug("enable: w=%s, enable = %s", |
| 538 | w ? qPrintable(w->objectName()) : "(null)", |
| 539 | e ? "true" : "false"); |
| 540 | #endif |
| 541 | if (!w->testAttribute(Qt::WA_WState_Created)) |
| 542 | return; |
| 543 | |
| 544 | // update ime status on the widget |
| 545 | QWidget *p = QWidget::find(w->effectiveWinId()); |
| 546 | if (p) |
| 547 | enableIme(p, e); |
| 548 | } |
| 549 | } |
| 550 | |
| 551 | void QOS2InputContext::setFocusWidget(QWidget *w) |
| 552 | { |
| 553 | QWidget *oldFocus = focusWidget(); |
| 554 | if (oldFocus == w) |
| 555 | return; |
| 556 | if (w) { |
| 557 | QOS2InputContext::updateImeStatus(w, true); |
| 558 | } else { |
| 559 | if (oldFocus) |
| 560 | QOS2InputContext::updateImeStatus(oldFocus , false); |
| 561 | } |
| 562 | QInputContext::setFocusWidget(w); |
| 563 | update(); |
| 564 | } |
| 565 | |
| 566 | bool QOS2InputContext::isComposing() const |
| 567 | { |
| 568 | return imeComposition && !imeComposition->isEmpty(); |
| 569 | } |
| 570 | |
| 571 | void QOS2InputContext::mouseHandler(int pos, QMouseEvent *e) |
| 572 | { |
| 573 | if(e->type() != QEvent::MouseButtonPress) |
| 574 | return; |
| 575 | |
| 576 | #ifdef Q_IME_DEBUG |
| 577 | qDebug("mouseHandler: pos=%d, length = %d", pos, imeComposition->length()); |
| 578 | #endif |
| 579 | |
| 580 | if (1 || pos < 0 || pos > imeComposition->length()) |
| 581 | reset(); |
| 582 | } |
| 583 | |
| 584 | QString QOS2InputContext::language() |
| 585 | { |
| 586 | return QString(); |
| 587 | } |
| 588 | |
| 589 | QT_END_NAMESPACE |