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
20namespace gfx {
21class Rect;
22}
23
24#if !defined(TOOLKIT_VIEWS)
25class MenuGtk;
26#endif
27class RenderWidgetHostViewGtk;
28struct NativeWebKeyboardEvent;
29typedef struct _GtkIMContext GtkIMContext;
30typedef 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.
43class 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