1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:expandtab:shiftwidth=4:tabstop=4: 3 */ 4 /* This Source Code Form is subject to the terms of the Mozilla Public 5 * License, v. 2.0. If a copy of the MPL was not distributed with this 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 7 8 #ifndef IMContextWrapper_h_ 9 #define IMContextWrapper_h_ 10 11 #include <gdk/gdk.h> 12 #include <gtk/gtk.h> 13 14 #include "nsString.h" 15 #include "nsCOMPtr.h" 16 #include "nsTArray.h" 17 #include "nsIWidget.h" 18 #include "mozilla/CheckedInt.h" 19 #include "mozilla/EventForwards.h" 20 #include "mozilla/TextEventDispatcherListener.h" 21 #include "WritingModes.h" 22 23 class nsWindow; 24 25 namespace mozilla { 26 namespace widget { 27 28 /** 29 * KeyHandlingState is result of IMContextWrapper::OnKeyEvent(). 30 */ 31 enum class KeyHandlingState { 32 // The native key event has not been handled by IMContextWrapper. 33 eNotHandled, 34 // The native key event was handled by IMContextWrapper. 35 eHandled, 36 // The native key event has not been handled by IMContextWrapper, 37 // but eKeyDown or eKeyUp event has been dispatched. 38 eNotHandledButEventDispatched, 39 // The native key event has not been handled by IMContextWrapper, 40 // but eKeyDown or eKeyUp event has been dispatched and consumed. 41 eNotHandledButEventConsumed, 42 }; 43 44 class IMContextWrapper final : public TextEventDispatcherListener { 45 public: 46 // TextEventDispatcherListener implementation 47 NS_DECL_ISUPPORTS 48 49 NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher, 50 const IMENotification& aNotification) override; 51 NS_IMETHOD_(IMENotificationRequests) GetIMENotificationRequests() override; 52 NS_IMETHOD_(void) 53 OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) override; 54 NS_IMETHOD_(void) 55 WillDispatchKeyboardEvent(TextEventDispatcher* aTextEventDispatcher, 56 WidgetKeyboardEvent& aKeyboardEvent, 57 uint32_t aIndexOfKeypress, void* aData) override; 58 59 public: 60 // aOwnerWindow is a pointer of the owner window. When aOwnerWindow is 61 // destroyed, the related IME contexts are released (i.e., IME cannot be 62 // used with the instance after that). 63 explicit IMContextWrapper(nsWindow* aOwnerWindow); 64 65 // Called when the process is being shut down. 66 static void Shutdown(); 67 68 // "Enabled" means the users can use all IMEs. 69 // I.e., the focus is in the normal editors. 70 bool IsEnabled() const; 71 72 // OnFocusWindow is a notification that aWindow is going to be focused. 73 void OnFocusWindow(nsWindow* aWindow); 74 // OnBlurWindow is a notification that aWindow is going to be unfocused. 75 void OnBlurWindow(nsWindow* aWindow); 76 // OnDestroyWindow is a notification that aWindow is going to be destroyed. 77 void OnDestroyWindow(nsWindow* aWindow); 78 // OnFocusChangeInGecko is a notification that an editor gets focus. 79 void OnFocusChangeInGecko(bool aFocus); 80 // OnSelectionChange is a notification that selection (caret) is changed 81 // in the focused editor. 82 void OnSelectionChange(nsWindow* aCaller, 83 const IMENotification& aIMENotification); 84 // OnThemeChanged is called when desktop theme is changed. 85 static void OnThemeChanged(); 86 87 /** 88 * OnKeyEvent() is called when aWindow gets a native key press event or a 89 * native key release event. If this returns true, the key event was 90 * filtered by IME. Otherwise, this returns false. 91 * NOTE: When the native key press event starts composition, this returns 92 * true but dispatches an eKeyDown event or eKeyUp event before 93 * dispatching composition events or content command event. 94 * 95 * @param aWindow A window on which user operate the 96 * key. 97 * @param aEvent A native key press or release 98 * event. 99 * @param aKeyboardEventWasDispatched true if eKeyDown or eKeyUp event 100 * for aEvent has already been 101 * dispatched. In this case, 102 * this class doesn't dispatch 103 * keyboard event anymore. 104 */ 105 KeyHandlingState OnKeyEvent(nsWindow* aWindow, GdkEventKey* aEvent, 106 bool aKeyboardEventWasDispatched = false); 107 108 // IME related nsIWidget methods. 109 nsresult EndIMEComposition(nsWindow* aCaller); 110 void SetInputContext(nsWindow* aCaller, const InputContext* aContext, 111 const InputContextAction* aAction); 112 InputContext GetInputContext(); 113 void OnUpdateComposition(); 114 void OnLayoutChange(); 115 116 TextEventDispatcher* GetTextEventDispatcher(); 117 118 // TODO: Typically, new IM comes every several years. And now, our code 119 // becomes really IM behavior dependent. So, perhaps, we need prefs 120 // to control related flags for IM developers. 121 enum class IMContextID : uint8_t { 122 Fcitx, // 4.x or earlier 123 Fcitx5, 124 IBus, 125 IIIMF, 126 Scim, 127 Uim, 128 Wayland, 129 Unknown, 130 }; 131 132 friend std::ostream& operator<<(std::ostream& aStream, 133 const IMContextID& aIMContextID) { 134 switch (aIMContextID) { 135 case IMContextID::Fcitx: 136 return aStream << "Fcitx"; 137 case IMContextID::Fcitx5: 138 return aStream << "Fcitx5"; 139 case IMContextID::IBus: 140 return aStream << "IBus"; 141 case IMContextID::IIIMF: 142 return aStream << "IIIMF"; 143 case IMContextID::Scim: 144 return aStream << "Scim"; 145 case IMContextID::Uim: 146 return aStream << "Uim"; 147 case IMContextID::Wayland: 148 return aStream << "Wayland"; 149 case IMContextID::Unknown: 150 return aStream << "Unknown"; 151 } 152 MOZ_ASSERT_UNREACHABLE("Add new case for the new IM support"); 153 return aStream << "Unknown"; 154 } 155 156 /** 157 * GetIMName() returns IM name associated with mContext. If the context is 158 * xim, this look for actual engine from XMODIFIERS environment variable. 159 */ 160 nsDependentCSubstring GetIMName() const; 161 162 /** 163 * GetWaitingSynthesizedKeyPressHardwareKeyCode() returns hardware_keycode 164 * value of last handled GDK_KEY_PRESS event which is probable handled by 165 * IME asynchronously and we have not received synthesized GDK_KEY_PRESS 166 * event yet. 167 */ GetWaitingSynthesizedKeyPressHardwareKeyCode()168 static guint16 GetWaitingSynthesizedKeyPressHardwareKeyCode() { 169 return sWaitingSynthesizedKeyPressHardwareKeyCode; 170 } 171 172 protected: 173 ~IMContextWrapper(); 174 175 // Owner of an instance of this class. This should be top level window. 176 // The owner window must release the contexts when it's destroyed because 177 // the IME contexts need the native window. If OnDestroyWindow() is called 178 // with the owner window, it'll release IME contexts. Otherwise, it'll 179 // just clean up any existing composition if it's related to the destroying 180 // child window. 181 nsWindow* mOwnerWindow; 182 183 // A last focused window in this class's context. 184 nsWindow* mLastFocusedWindow; 185 186 // Actual context. This is used for handling the user's input. 187 GtkIMContext* mContext; 188 189 // mSimpleContext is used for the password field and 190 // the |ime-mode: disabled;| editors if sUseSimpleContext is true. 191 // These editors disable IME. But dead keys should work. Fortunately, 192 // the simple IM context of GTK2 support only them. 193 GtkIMContext* mSimpleContext; 194 195 // mDummyContext is a dummy context and will be used in Focus() 196 // when the state of mEnabled means disabled. This context's IME state is 197 // always "closed", so it closes IME forcedly. 198 GtkIMContext* mDummyContext; 199 200 // mComposingContext is not nullptr while one of mContext, mSimpleContext 201 // and mDummyContext has composition. 202 // XXX: We don't assume that two or more context have composition same time. 203 GtkIMContext* mComposingContext; 204 205 // IME enabled state and other things defined in InputContext. 206 // Use following helper methods if you don't need the detail of the status. 207 InputContext mInputContext; 208 209 // mCompositionStart is the start offset of the composition string in the 210 // current content. When <textarea> or <input> have focus, it means offset 211 // from the first character of them. When a HTML editor has focus, it 212 // means offset from the first character of the root element of the editor. 213 uint32_t mCompositionStart; 214 215 // mDispatchedCompositionString is the latest composition string which 216 // was dispatched by compositionupdate event. 217 nsString mDispatchedCompositionString; 218 219 // mSelectedStringRemovedByComposition is the selected string which was 220 // removed by first compositionchange event. 221 nsString mSelectedStringRemovedByComposition; 222 223 // OnKeyEvent() temporarily sets mProcessingKeyEvent to the given native 224 // event. 225 GdkEventKey* mProcessingKeyEvent; 226 227 /** 228 * GdkEventKeyQueue stores *copy* of GdkEventKey instances. However, this 229 * must be safe to our usecase since it has |time| and the value should not 230 * be same as older event. 231 */ 232 class GdkEventKeyQueue final { 233 public: ~GdkEventKeyQueue()234 ~GdkEventKeyQueue() { Clear(); } 235 Clear()236 void Clear() { 237 if (!mEvents.IsEmpty()) { 238 RemoveEventsAt(0, mEvents.Length()); 239 } 240 } 241 242 /** 243 * PutEvent() puts new event into the queue. 244 */ PutEvent(const GdkEventKey * aEvent)245 void PutEvent(const GdkEventKey* aEvent) { 246 GdkEventKey* newEvent = reinterpret_cast<GdkEventKey*>( 247 gdk_event_copy(reinterpret_cast<const GdkEvent*>(aEvent))); 248 newEvent->state &= GDK_MODIFIER_MASK; 249 mEvents.AppendElement(newEvent); 250 } 251 252 /** 253 * RemoveEvent() removes oldest same event and its preceding events 254 * from the queue. 255 */ RemoveEvent(const GdkEventKey * aEvent)256 void RemoveEvent(const GdkEventKey* aEvent) { 257 size_t index = IndexOf(aEvent); 258 if (NS_WARN_IF(index == GdkEventKeyQueue::NoIndex())) { 259 return; 260 } 261 RemoveEventsAt(0, index + 1); 262 } 263 264 /** 265 * FirstEvent() returns oldest event in the queue. 266 */ GetFirstEvent()267 GdkEventKey* GetFirstEvent() const { 268 if (mEvents.IsEmpty()) { 269 return nullptr; 270 } 271 return mEvents[0]; 272 } 273 IsEmpty()274 bool IsEmpty() const { return mEvents.IsEmpty(); } 275 NoIndex()276 static size_t NoIndex() { return nsTArray<GdkEventKey*>::NoIndex; } Length()277 size_t Length() const { return mEvents.Length(); } IndexOf(const GdkEventKey * aEvent)278 size_t IndexOf(const GdkEventKey* aEvent) const { 279 static_assert(!(GDK_MODIFIER_MASK & (1 << 24)), 280 "We assumes 25th bit is used by some IM, but used by GDK"); 281 static_assert(!(GDK_MODIFIER_MASK & (1 << 25)), 282 "We assumes 26th bit is used by some IM, but used by GDK"); 283 for (size_t i = 0; i < mEvents.Length(); i++) { 284 GdkEventKey* event = mEvents[i]; 285 // It must be enough to compare only type, time, keyval and 286 // part of state. Note that we cannot compaire two events 287 // simply since IME may have changed unused bits of state. 288 if (event->time == aEvent->time) { 289 if (NS_WARN_IF(event->type != aEvent->type) || 290 NS_WARN_IF(event->keyval != aEvent->keyval) || 291 NS_WARN_IF(event->state != (aEvent->state & GDK_MODIFIER_MASK))) { 292 continue; 293 } 294 } 295 return i; 296 } 297 return GdkEventKeyQueue::NoIndex(); 298 } 299 300 private: 301 nsTArray<GdkEventKey*> mEvents; 302 RemoveEventsAt(size_t aStart,size_t aCount)303 void RemoveEventsAt(size_t aStart, size_t aCount) { 304 for (size_t i = aStart; i < aStart + aCount; i++) { 305 gdk_event_free(reinterpret_cast<GdkEvent*>(mEvents[i])); 306 } 307 mEvents.RemoveElementsAt(aStart, aCount); 308 } 309 }; 310 // OnKeyEvent() append mPostingKeyEvents when it believes that a key event 311 // is posted to other IME process. 312 GdkEventKeyQueue mPostingKeyEvents; 313 314 static guint16 sWaitingSynthesizedKeyPressHardwareKeyCode; 315 316 struct Range { 317 uint32_t mOffset; 318 uint32_t mLength; 319 RangeRange320 Range() : mOffset(UINT32_MAX), mLength(UINT32_MAX) {} 321 IsValidRange322 bool IsValid() const { return mOffset != UINT32_MAX; } ClearRange323 void Clear() { 324 mOffset = UINT32_MAX; 325 mLength = UINT32_MAX; 326 } 327 }; 328 329 // current target offset and length of IME composition 330 Range mCompositionTargetRange; 331 332 // mCompositionState indicates current status of composition. 333 enum eCompositionState : uint8_t { 334 eCompositionState_NotComposing, 335 eCompositionState_CompositionStartDispatched, 336 eCompositionState_CompositionChangeEventDispatched 337 }; 338 eCompositionState mCompositionState; 339 IsComposing()340 bool IsComposing() const { 341 return (mCompositionState != eCompositionState_NotComposing); 342 } 343 IsComposingOn(GtkIMContext * aContext)344 bool IsComposingOn(GtkIMContext* aContext) const { 345 return IsComposing() && mComposingContext == aContext; 346 } 347 IsComposingOnCurrentContext()348 bool IsComposingOnCurrentContext() const { 349 return IsComposingOn(GetCurrentContext()); 350 } 351 EditorHasCompositionString()352 bool EditorHasCompositionString() { 353 return (mCompositionState == 354 eCompositionState_CompositionChangeEventDispatched); 355 } 356 357 /** 358 * Checks if aContext is valid context for handling composition. 359 * 360 * @param aContext An IM context which is specified by native 361 * composition events. 362 * @return true if the context is valid context for 363 * handling composition. Otherwise, false. 364 */ 365 bool IsValidContext(GtkIMContext* aContext) const; 366 GetCompositionStateName()367 const char* GetCompositionStateName() { 368 switch (mCompositionState) { 369 case eCompositionState_NotComposing: 370 return "NotComposing"; 371 case eCompositionState_CompositionStartDispatched: 372 return "CompositionStartDispatched"; 373 case eCompositionState_CompositionChangeEventDispatched: 374 return "CompositionChangeEventDispatched"; 375 default: 376 return "InvaildState"; 377 } 378 } 379 380 // mIMContextID indicates the ID of mContext. This is actually indicates 381 // IM which user selected. 382 IMContextID mIMContextID; 383 384 struct Selection final { 385 nsString mString; 386 uint32_t mOffset; 387 WritingMode mWritingMode; 388 Selectionfinal389 Selection() : mOffset(UINT32_MAX) {} 390 Clearfinal391 void Clear() { 392 mString.Truncate(); 393 mOffset = UINT32_MAX; 394 mWritingMode = WritingMode(); 395 } CollapseTofinal396 void CollapseTo(uint32_t aOffset, const WritingMode& aWritingMode) { 397 mWritingMode = aWritingMode; 398 mOffset = aOffset; 399 mString.Truncate(); 400 } 401 402 void Assign(const IMENotification& aIMENotification); 403 void Assign(const WidgetQueryContentEvent& aSelectedTextEvent); 404 IsValidfinal405 bool IsValid() const { return mOffset != UINT32_MAX; } Collapsedfinal406 bool Collapsed() const { return mString.IsEmpty(); } Lengthfinal407 uint32_t Length() const { return mString.Length(); } EndOffsetfinal408 uint32_t EndOffset() const { 409 if (NS_WARN_IF(!IsValid())) { 410 return UINT32_MAX; 411 } 412 CheckedInt<uint32_t> endOffset = 413 CheckedInt<uint32_t>(mOffset) + mString.Length(); 414 if (NS_WARN_IF(!endOffset.isValid())) { 415 return UINT32_MAX; 416 } 417 return endOffset.value(); 418 } 419 } mSelection; 420 bool EnsureToCacheSelection(nsAString* aSelectedString = nullptr); 421 422 // mIsIMFocused is set to TRUE when we call gtk_im_context_focus_in(). And 423 // it's set to FALSE when we call gtk_im_context_focus_out(). 424 bool mIsIMFocused; 425 // mFallbackToKeyEvent is set to false when this class starts to handle 426 // a native key event (at that time, mProcessingKeyEvent is set to the 427 // native event). If active IME just commits composition with a character 428 // which is produced by the key with current keyboard layout, this is set 429 // to true. 430 bool mFallbackToKeyEvent; 431 // mKeyboardEventWasDispatched is used by OnKeyEvent() and 432 // MaybeDispatchKeyEventAsProcessedByIME(). 433 // MaybeDispatchKeyEventAsProcessedByIME() dispatches an eKeyDown or 434 // eKeyUp event event if the composition is caused by a native 435 // key press event. If this is true, a keyboard event has been dispatched 436 // for the native event. If so, MaybeDispatchKeyEventAsProcessedByIME() 437 // won't dispatch keyboard event anymore. 438 bool mKeyboardEventWasDispatched; 439 // Whether the keyboard event which as dispatched at setting 440 // mKeyboardEventWasDispatched to true was consumed or not. 441 bool mKeyboardEventWasConsumed; 442 // mIsDeletingSurrounding is true while OnDeleteSurroundingNative() is 443 // trying to delete the surrounding text. 444 bool mIsDeletingSurrounding; 445 // mLayoutChanged is true after OnLayoutChange() is called. This is reset 446 // when eCompositionChange is being dispatched. 447 bool mLayoutChanged; 448 // mSetCursorPositionOnKeyEvent true when caret rect or position is updated 449 // with no composition. If true, we update candidate window position 450 // before key down 451 bool mSetCursorPositionOnKeyEvent; 452 // mPendingResettingIMContext becomes true if selection change notification 453 // is received during composition but the selection change occurred before 454 // starting the composition. In such case, we cannot notify IME of 455 // selection change during composition because we don't want to commit 456 // the composition in such case. However, we should notify IME of the 457 // selection change after the composition is committed. 458 bool mPendingResettingIMContext; 459 // mRetrieveSurroundingSignalReceived is true after "retrieve_surrounding" 460 // signal is received until selection is changed in Gecko. 461 bool mRetrieveSurroundingSignalReceived; 462 // mMaybeInDeadKeySequence is set to true when we detect a dead key press 463 // and set to false when we're sure dead key sequence has been finished. 464 // Note that we cannot detect which key event causes ending a dead key 465 // sequence. For example, when you press dead key grave with ibus Spanish 466 // keyboard layout, it just consumes the key event when we call 467 // gtk_im_context_filter_keypress(). Then, pressing "Escape" key cancels 468 // the dead key sequence but we don't receive any signal and it's consumed 469 // by gtk_im_context_filter_keypress() normally. On the other hand, when 470 // pressing "Shift" key causes exactly same behavior but dead key sequence 471 // isn't finished yet. 472 bool mMaybeInDeadKeySequence; 473 // mIsIMInAsyncKeyHandlingMode is set to true if we know that IM handles 474 // key events asynchronously. I.e., filtered key event may come again 475 // later. 476 bool mIsIMInAsyncKeyHandlingMode; 477 // mIsKeySnooped is set to true if IM uses key snooper to listen key events. 478 // In such case, we won't receive key events if IME consumes the event. 479 bool mIsKeySnooped; 480 481 // sLastFocusedContext is a pointer to the last focused instance of this 482 // class. When a instance is destroyed and sLastFocusedContext refers it, 483 // this is cleared. So, this refers valid pointer always. 484 static IMContextWrapper* sLastFocusedContext; 485 486 // sUseSimpleContext indeicates if password editors and editors with 487 // |ime-mode: disabled;| should use GtkIMContextSimple. 488 // If true, they use GtkIMContextSimple. Otherwise, not. 489 static bool sUseSimpleContext; 490 491 // Callback methods for native IME events. These methods should call 492 // the related instance methods simply. 493 static gboolean OnRetrieveSurroundingCallback(GtkIMContext* aContext, 494 IMContextWrapper* aModule); 495 static gboolean OnDeleteSurroundingCallback(GtkIMContext* aContext, 496 gint aOffset, gint aNChars, 497 IMContextWrapper* aModule); 498 static void OnCommitCompositionCallback(GtkIMContext* aContext, 499 const gchar* aString, 500 IMContextWrapper* aModule); 501 static void OnChangeCompositionCallback(GtkIMContext* aContext, 502 IMContextWrapper* aModule); 503 static void OnStartCompositionCallback(GtkIMContext* aContext, 504 IMContextWrapper* aModule); 505 static void OnEndCompositionCallback(GtkIMContext* aContext, 506 IMContextWrapper* aModule); 507 508 // The instance methods for the native IME events. 509 gboolean OnRetrieveSurroundingNative(GtkIMContext* aContext); 510 gboolean OnDeleteSurroundingNative(GtkIMContext* aContext, gint aOffset, 511 gint aNChars); 512 void OnCommitCompositionNative(GtkIMContext* aContext, const gchar* aString); 513 void OnChangeCompositionNative(GtkIMContext* aContext); 514 void OnStartCompositionNative(GtkIMContext* aContext); 515 void OnEndCompositionNative(GtkIMContext* aContext); 516 517 /** 518 * GetCurrentContext() returns current IM context which is chosen with the 519 * enabled state. 520 * WARNING: 521 * When this class receives some signals for a composition after focus 522 * is moved in Gecko, the result of this may be different from given 523 * context by the signals. 524 */ 525 GtkIMContext* GetCurrentContext() const; 526 527 /** 528 * GetActiveContext() returns a composing context or current context. 529 */ GetActiveContext()530 GtkIMContext* GetActiveContext() const { 531 return mComposingContext ? mComposingContext : GetCurrentContext(); 532 } 533 534 // If the owner window and IM context have been destroyed, returns TRUE. IsDestroyed()535 bool IsDestroyed() { return !mOwnerWindow; } 536 537 // Sets focus to the instance of this class. 538 void Focus(); 539 540 // Steals focus from the instance of this class. 541 void Blur(); 542 543 // Initializes the instance. 544 void Init(); 545 546 /** 547 * Reset the active context, i.e., if there is mComposingContext, reset it. 548 * Otherwise, reset current context. Note that all native composition 549 * events during calling this will be ignored. 550 */ 551 void ResetIME(); 552 553 // Gets the current composition string by the native APIs. 554 void GetCompositionString(GtkIMContext* aContext, 555 nsAString& aCompositionString); 556 557 /** 558 * Generates our text range array from current composition string. 559 * 560 * @param aContext A GtkIMContext which is being handled. 561 * @param aCompositionString The data to be dispatched with 562 * compositionchange event. 563 */ 564 already_AddRefed<TextRangeArray> CreateTextRangeArray( 565 GtkIMContext* aContext, const nsAString& aCompositionString); 566 567 /** 568 * SetTextRange() initializes aTextRange with aPangoAttrIter. 569 * 570 * @param aPangoAttrIter An iter which represents a clause of the 571 * composition string. 572 * @param aUTF8CompositionString The whole composition string (UTF-8). 573 * @param aUTF16CaretOffset The caret offset in the composition 574 * string encoded as UTF-16. 575 * @param aTextRange The result. 576 * @return true if this initializes aTextRange. 577 * Otherwise, false. 578 */ 579 bool SetTextRange(PangoAttrIterator* aPangoAttrIter, 580 const gchar* aUTF8CompositionString, 581 uint32_t aUTF16CaretOffset, TextRange& aTextRange) const; 582 583 /** 584 * ToNscolor() converts the PangoColor in aPangoAttrColor to nscolor. 585 */ 586 static nscolor ToNscolor(PangoAttrColor* aPangoAttrColor); 587 588 /** 589 * Move the candidate window with "fake" cursor position. 590 * 591 * @param aContext A GtkIMContext which is being handled. 592 */ 593 void SetCursorPosition(GtkIMContext* aContext); 594 595 // Queries the current selection offset of the window. 596 uint32_t GetSelectionOffset(nsWindow* aWindow); 597 598 // Get current paragraph text content and cursor position 599 nsresult GetCurrentParagraph(nsAString& aText, uint32_t& aCursorPos); 600 601 /** 602 * Delete text portion 603 * 604 * @param aContext A GtkIMContext which is being handled. 605 * @param aOffset Start offset of the range to delete. 606 * @param aNChars Count of characters to delete. It depends 607 * on |g_utf8_strlen()| what is one character. 608 */ 609 nsresult DeleteText(GtkIMContext* aContext, int32_t aOffset, 610 uint32_t aNChars); 611 612 // Initializes the GUI event. 613 void InitEvent(WidgetGUIEvent& aEvent); 614 615 // Called before destroying the context to work around some platform bugs. 616 void PrepareToDestroyContext(GtkIMContext* aContext); 617 618 /** 619 * WARNING: 620 * Following methods dispatch gecko events. Then, the focused widget 621 * can be destroyed, and also it can be stolen focus. If they returns 622 * FALSE, callers cannot continue the composition. 623 * - MaybeDispatchKeyEventAsProcessedByIME 624 * - DispatchCompositionStart 625 * - DispatchCompositionChangeEvent 626 * - DispatchCompositionCommitEvent 627 */ 628 629 /** 630 * Dispatch an eKeyDown or eKeyUp event whose mKeyCode value is 631 * NS_VK_PROCESSKEY and mKeyNameIndex is KEY_NAME_INDEX_Process if 632 * we're not in a dead key sequence, mProcessingKeyEvent is nullptr 633 * but mPostingKeyEvents is not empty or mProcessingKeyEvent is not 634 * nullptr and mKeyboardEventWasDispatched is still false. If this 635 * dispatches a keyboard event, this sets mKeyboardEventWasDispatched 636 * to true. 637 * 638 * @param aFollowingEvent The following event message. 639 * @return If the caller can continue to handle 640 * composition, returns true. Otherwise, 641 * false. For example, if focus is moved 642 * by dispatched keyboard event, returns 643 * false. 644 */ 645 bool MaybeDispatchKeyEventAsProcessedByIME(EventMessage aFollowingEvent); 646 647 /** 648 * Dispatches a composition start event. 649 * 650 * @param aContext A GtkIMContext which is being handled. 651 * @return true if the focused widget is neither 652 * destroyed nor changed. Otherwise, false. 653 */ 654 bool DispatchCompositionStart(GtkIMContext* aContext); 655 656 /** 657 * Dispatches a compositionchange event. 658 * 659 * @param aContext A GtkIMContext which is being handled. 660 * @param aCompositionString New composition string. 661 * @return true if the focused widget is neither 662 * destroyed nor changed. Otherwise, false. 663 */ 664 bool DispatchCompositionChangeEvent(GtkIMContext* aContext, 665 const nsAString& aCompositionString); 666 667 /** 668 * Dispatches a compositioncommit event or compositioncommitasis event. 669 * 670 * @param aContext A GtkIMContext which is being handled. 671 * @param aCommitString If this is nullptr, the composition will 672 * be committed with last dispatched data. 673 * Otherwise, the composition will be 674 * committed with this value. 675 * @return true if the focused widget is neither 676 * destroyed nor changed. Otherwise, false. 677 */ 678 bool DispatchCompositionCommitEvent(GtkIMContext* aContext, 679 const nsAString* aCommitString = nullptr); 680 }; 681 682 } // namespace widget 683 } // namespace mozilla 684 685 #endif // #ifndef IMContextWrapper_h_ 686