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