1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef CONTENT_BROWSER_RENDERER_HOST_GTK_IM_CONTEXT_WRAPPER_H_ 6 #define CONTENT_BROWSER_RENDERER_HOST_GTK_IM_CONTEXT_WRAPPER_H_ 7 8 #include <gdk/gdk.h> 9 #include <pango/pango-attributes.h> 10 #include <vector> 11 12 #include "base/basictypes.h" 13 #include "base/gtest_prod_util.h" 14 #include "base/strings/string16.h" 15 #include "third_party/WebKit/public/web/WebInputEvent.h" 16 #include "ui/base/ime/composition_text.h" 17 #include "ui/base/ime/text_input_type.h" 18 19 typedef struct _GtkIMContext GtkIMContext; 20 typedef struct _GtkWidget GtkWidget; 21 22 namespace gfx { 23 class Rect; 24 } 25 26 namespace content { 27 class RenderWidgetHostViewGtk; 28 struct NativeWebKeyboardEvent; 29 30 // This class is a convenience wrapper for GtkIMContext. 31 // It creates and manages two GtkIMContext instances, one is GtkIMMulticontext, 32 // for plain text input box, another is GtkIMContextSimple, for password input 33 // box. 34 // 35 // This class is in charge of dispatching key events to these two GtkIMContext 36 // instances and handling signals emitted by them. Key events then will be 37 // forwarded to renderer along with input method results via corresponding host 38 // view. 39 // 40 // This class is used solely by RenderWidgetHostViewGtk. 41 class GtkIMContextWrapper { 42 public: 43 explicit GtkIMContextWrapper(RenderWidgetHostViewGtk* host_view); 44 ~GtkIMContextWrapper(); 45 46 // Processes a gdk key event received by |host_view|. 47 void ProcessKeyEvent(GdkEventKey* event); 48 49 void UpdateInputMethodState(ui::TextInputType type, 50 bool can_compose_inline); 51 void UpdateCaretBounds(const gfx::Rect& caret_bounds); 52 void OnFocusIn(); 53 void OnFocusOut(); 54 bool is_focused() const { return is_focused_; } 55 56 GtkWidget* BuildInputMethodsGtkMenu(); 57 58 void CancelComposition(); 59 60 void ConfirmComposition(); 61 62 private: 63 // Check if a text needs commit by forwarding a char event instead of 64 // by confirming as a composition text. 65 bool NeedCommitByForwardingCharEvent() const; 66 67 // Check if the input method returned any result, eg. preedit and commit text. 68 bool HasInputMethodResult() const; 69 70 void ProcessFilteredKeyPressEvent(NativeWebKeyboardEvent* wke); 71 void ProcessUnfilteredKeyPressEvent(NativeWebKeyboardEvent* wke); 72 73 // Processes result returned from input method after filtering a key event. 74 // |filtered| indicates if the key event was filtered by the input method. 75 void ProcessInputMethodResult(const GdkEventKey* event, bool filtered); 76 77 // Real code of "commit" signal handler. 78 void HandleCommit(const string16& text); 79 80 // Real code of "preedit-start" signal handler. 81 void HandlePreeditStart(); 82 83 // Real code of "preedit-changed" signal handler. 84 void HandlePreeditChanged(const gchar* text, 85 PangoAttrList* attrs, 86 int cursor_position); 87 88 // Real code of "preedit-end" signal handler. 89 void HandlePreeditEnd(); 90 91 // Real code of "retrieve-surrounding" signal handler. 92 gboolean HandleRetrieveSurrounding(GtkIMContext* context); 93 94 // Real code of "realize" signal handler, used for setting im context's client 95 // window. 96 void HandleHostViewRealize(GtkWidget* widget); 97 98 // Real code of "unrealize" signal handler, used for unsetting im context's 99 // client window. 100 void HandleHostViewUnrealize(); 101 102 // Sends a fake composition key event with specified event type. A composition 103 // key event is a key event with special key code 229. 104 void SendFakeCompositionKeyEvent(WebKit::WebInputEvent::Type type); 105 106 // Signal handlers of GtkIMContext object. 107 static void HandleCommitThunk(GtkIMContext* context, gchar* text, 108 GtkIMContextWrapper* self); 109 static void HandlePreeditStartThunk(GtkIMContext* context, 110 GtkIMContextWrapper* self); 111 static void HandlePreeditChangedThunk(GtkIMContext* context, 112 GtkIMContextWrapper* self); 113 static void HandlePreeditEndThunk(GtkIMContext* context, 114 GtkIMContextWrapper* self); 115 static gboolean HandleRetrieveSurroundingThunk(GtkIMContext* context, 116 GtkIMContextWrapper* self); 117 118 // Signal handlers connecting to |host_view_|'s native view widget. 119 static void HandleHostViewRealizeThunk(GtkWidget* widget, 120 GtkIMContextWrapper* self); 121 static void HandleHostViewUnrealizeThunk(GtkWidget* widget, 122 GtkIMContextWrapper* self); 123 124 // The parent object. 125 RenderWidgetHostViewGtk* host_view_; 126 127 // The GtkIMContext object. 128 // In terms of the DOM event specification Appendix A 129 // <http://www.w3.org/TR/DOM-Level-3-Events/keyset.html>, 130 // GTK uses a GtkIMContext object for the following two purposes: 131 // 1. Composing Latin characters (A.1.2), and; 132 // 2. Composing CJK characters with an IME (A.1.3). 133 // Many JavaScript pages assume composed Latin characters are dispatched to 134 // their onkeypress() handlers but not dispatched CJK characters composed 135 // with an IME. To emulate this behavior, we should monitor the status of 136 // this GtkIMContext object and prevent sending Char events when a 137 // GtkIMContext object sends a "commit" signal with the CJK characters 138 // composed by an IME. 139 GtkIMContext* context_; 140 141 // A GtkIMContextSimple object, for supporting dead/compose keys when input 142 // method is disabled, eg. in password input box. 143 GtkIMContext* context_simple_; 144 145 // Whether or not this widget is focused. 146 bool is_focused_; 147 148 // Whether or not the above GtkIMContext is composing a text with an IME. 149 // This flag is used in "commit" signal handler of the GtkIMContext object, 150 // which determines how to submit the result text to WebKit according to this 151 // flag. 152 // If this flag is true or there are more than one characters in the result, 153 // then the result text will be committed to WebKit as a confirmed 154 // composition. Otherwise, it'll be forwarded as a key event. 155 // 156 // The GtkIMContext object sends a "preedit_start" before it starts composing 157 // a text and a "preedit_end" signal after it finishes composing it. 158 // "preedit_start" signal is monitored to turn it on. 159 // We don't monitor "preedit_end" signal to turn it off, because an input 160 // method may fire "preedit_end" signal before "commit" signal. 161 // A buggy input method may not fire "preedit_start" and/or "preedit_end" 162 // at all, so this flag will also be set to true when "preedit_changed" signal 163 // is fired with non-empty preedit text. 164 bool is_composing_text_; 165 166 // Whether or not the IME is enabled. 167 bool is_enabled_; 168 169 // Whether or not it's currently running inside key event handler. 170 // If it's true, then preedit-changed and commit handler will backup the 171 // preedit or commit text instead of sending them down to webkit. 172 // key event handler will send them later. 173 bool is_in_key_event_handler_; 174 175 // The most recent composition text information retrieved from context_; 176 ui::CompositionText composition_; 177 178 // Whether or not the composition has been changed since last key event. 179 bool is_composition_changed_; 180 181 // Stores a copy of the most recent commit text received by commit signal 182 // handler. 183 string16 commit_text_; 184 185 // If it's true then the next "commit" signal will be suppressed. 186 // It's only used to workaround http://crbug.com/50485. 187 // TODO(suzhe): Remove it after input methods get fixed. 188 bool suppress_next_commit_; 189 190 // Information of the last key event, for working around 191 // http://crosbug.com/6582 192 int last_key_code_; 193 bool last_key_was_up_; 194 bool last_key_filtered_no_result_; 195 196 DISALLOW_COPY_AND_ASSIGN(GtkIMContextWrapper); 197 }; 198 199 } // namespace content 200 201 #endif // CONTENT_BROWSER_RENDERER_HOST_GTK_IM_CONTEXT_WRAPPER_H_ 202