| | 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 |