|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
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 __nsGtkIMModule_h__ |
|
9 #define __nsGtkIMModule_h__ |
|
10 |
|
11 #include <gdk/gdk.h> |
|
12 #include <gtk/gtk.h> |
|
13 |
|
14 #include "nsString.h" |
|
15 #include "nsAutoPtr.h" |
|
16 #include "nsCOMPtr.h" |
|
17 #include "nsTArray.h" |
|
18 #include "nsIWidget.h" |
|
19 #include "mozilla/EventForwards.h" |
|
20 |
|
21 class nsWindow; |
|
22 |
|
23 class nsGtkIMModule |
|
24 { |
|
25 protected: |
|
26 typedef mozilla::widget::InputContext InputContext; |
|
27 typedef mozilla::widget::InputContextAction InputContextAction; |
|
28 |
|
29 public: |
|
30 nsrefcnt AddRef() |
|
31 { |
|
32 NS_PRECONDITION(int32_t(mRefCnt) >= 0, "mRefCnt is negative"); |
|
33 ++mRefCnt; |
|
34 NS_LOG_ADDREF(this, mRefCnt, "nsGtkIMModule", sizeof(*this)); |
|
35 return mRefCnt; |
|
36 } |
|
37 nsrefcnt Release() |
|
38 { |
|
39 NS_PRECONDITION(mRefCnt != 0, "mRefCnt is alrady zero"); |
|
40 --mRefCnt; |
|
41 NS_LOG_RELEASE(this, mRefCnt, "nsGtkIMModule"); |
|
42 if (mRefCnt == 0) { |
|
43 mRefCnt = 1; /* stabilize */ |
|
44 delete this; |
|
45 return 0; |
|
46 } |
|
47 return mRefCnt; |
|
48 } |
|
49 |
|
50 protected: |
|
51 nsAutoRefCnt mRefCnt; |
|
52 |
|
53 public: |
|
54 // aOwnerWindow is a pointer of the owner window. When aOwnerWindow is |
|
55 // destroyed, the related IME contexts are released (i.e., IME cannot be |
|
56 // used with the instance after that). |
|
57 nsGtkIMModule(nsWindow* aOwnerWindow); |
|
58 ~nsGtkIMModule(); |
|
59 |
|
60 // "Enabled" means the users can use all IMEs. |
|
61 // I.e., the focus is in the normal editors. |
|
62 bool IsEnabled(); |
|
63 |
|
64 // OnFocusWindow is a notification that aWindow is going to be focused. |
|
65 void OnFocusWindow(nsWindow* aWindow); |
|
66 // OnBlurWindow is a notification that aWindow is going to be unfocused. |
|
67 void OnBlurWindow(nsWindow* aWindow); |
|
68 // OnDestroyWindow is a notification that aWindow is going to be destroyed. |
|
69 void OnDestroyWindow(nsWindow* aWindow); |
|
70 // OnFocusChangeInGecko is a notification that an editor gets focus. |
|
71 void OnFocusChangeInGecko(bool aFocus); |
|
72 |
|
73 // OnKeyEvent is called when aWindow gets a native key press event or a |
|
74 // native key release event. If this returns TRUE, the key event was |
|
75 // filtered by IME. Otherwise, this returns FALSE. |
|
76 // NOTE: When the keypress event starts composition, this returns TRUE but |
|
77 // this dispatches keydown event before compositionstart event. |
|
78 bool OnKeyEvent(nsWindow* aWindow, GdkEventKey* aEvent, |
|
79 bool aKeyDownEventWasSent = false); |
|
80 |
|
81 // IME related nsIWidget methods. |
|
82 nsresult CommitIMEComposition(nsWindow* aCaller); |
|
83 void SetInputContext(nsWindow* aCaller, |
|
84 const InputContext* aContext, |
|
85 const InputContextAction* aAction); |
|
86 InputContext GetInputContext(); |
|
87 nsresult CancelIMEComposition(nsWindow* aCaller); |
|
88 void OnUpdateComposition(); |
|
89 |
|
90 // If a software keyboard has been opened, this returns TRUE. |
|
91 // Otherwise, FALSE. |
|
92 static bool IsVirtualKeyboardOpened(); |
|
93 |
|
94 protected: |
|
95 // Owner of an instance of this class. This should be top level window. |
|
96 // The owner window must release the contexts when it's destroyed because |
|
97 // the IME contexts need the native window. If OnDestroyWindow() is called |
|
98 // with the owner window, it'll release IME contexts. Otherwise, it'll |
|
99 // just clean up any existing composition if it's related to the destroying |
|
100 // child window. |
|
101 nsWindow* mOwnerWindow; |
|
102 |
|
103 // A last focused window in this class's context. |
|
104 nsWindow* mLastFocusedWindow; |
|
105 |
|
106 // Actual context. This is used for handling the user's input. |
|
107 GtkIMContext *mContext; |
|
108 |
|
109 // mSimpleContext is used for the password field and |
|
110 // the |ime-mode: disabled;| editors if sUseSimpleContext is true. |
|
111 // These editors disable IME. But dead keys should work. Fortunately, |
|
112 // the simple IM context of GTK2 support only them. |
|
113 GtkIMContext *mSimpleContext; |
|
114 |
|
115 // mDummyContext is a dummy context and will be used in Focus() |
|
116 // when the state of mEnabled means disabled. This context's IME state is |
|
117 // always "closed", so it closes IME forcedly. |
|
118 GtkIMContext *mDummyContext; |
|
119 |
|
120 // IME enabled state and other things defined in InputContext. |
|
121 // Use following helper methods if you don't need the detail of the status. |
|
122 InputContext mInputContext; |
|
123 |
|
124 // mCompositionStart is the start offset of the composition string in the |
|
125 // current content. When <textarea> or <input> have focus, it means offset |
|
126 // from the first character of them. When a HTML editor has focus, it |
|
127 // means offset from the first character of the root element of the editor. |
|
128 uint32_t mCompositionStart; |
|
129 |
|
130 // mDispatchedCompositionString is the latest composition string which |
|
131 // was dispatched by compositionupdate event. |
|
132 nsString mDispatchedCompositionString; |
|
133 |
|
134 // mSelectedString is the selected string which was removed by first |
|
135 // text event. |
|
136 nsString mSelectedString; |
|
137 |
|
138 // OnKeyEvent() temporarily sets mProcessingKeyEvent to the given native |
|
139 // event. |
|
140 GdkEventKey* mProcessingKeyEvent; |
|
141 |
|
142 // current target offset of IME composition |
|
143 uint32_t mCompositionTargetOffset; |
|
144 |
|
145 // mCompositionState indicates current status of composition. |
|
146 enum eCompositionState { |
|
147 eCompositionState_NotComposing, |
|
148 eCompositionState_CompositionStartDispatched, |
|
149 eCompositionState_TextEventDispatched, |
|
150 eCompositionState_CommitTextEventDispatched |
|
151 }; |
|
152 eCompositionState mCompositionState; |
|
153 |
|
154 bool IsComposing() |
|
155 { |
|
156 return (mCompositionState != eCompositionState_NotComposing); |
|
157 } |
|
158 |
|
159 bool EditorHasCompositionString() |
|
160 { |
|
161 return (mCompositionState == eCompositionState_TextEventDispatched); |
|
162 } |
|
163 |
|
164 #ifdef PR_LOGGING |
|
165 const char* GetCompositionStateName() |
|
166 { |
|
167 switch (mCompositionState) { |
|
168 case eCompositionState_NotComposing: |
|
169 return "NotComposing"; |
|
170 case eCompositionState_CompositionStartDispatched: |
|
171 return "CompositionStartDispatched"; |
|
172 case eCompositionState_TextEventDispatched: |
|
173 return "TextEventDispatched"; |
|
174 case eCompositionState_CommitTextEventDispatched: |
|
175 return "CommitTextEventDispatched"; |
|
176 default: |
|
177 return "InvaildState"; |
|
178 } |
|
179 } |
|
180 #endif // PR_LOGGING |
|
181 |
|
182 |
|
183 // mIsIMFocused is set to TRUE when we call gtk_im_context_focus_in(). And |
|
184 // it's set to FALSE when we call gtk_im_context_focus_out(). |
|
185 bool mIsIMFocused; |
|
186 // mFilterKeyEvent is used by OnKeyEvent(). If the commit event should |
|
187 // be processed as simple key event, this is set to TRUE by the commit |
|
188 // handler. |
|
189 bool mFilterKeyEvent; |
|
190 // When mIgnoreNativeCompositionEvent is TRUE, all native composition |
|
191 // should be ignored except that the compositon should be restarted in |
|
192 // another content (nsIContent). Don't refer this value directly, use |
|
193 // ShouldIgnoreNativeCompositionEvent(). |
|
194 bool mIgnoreNativeCompositionEvent; |
|
195 // mKeyDownEventWasSent is used by OnKeyEvent() and |
|
196 // DispatchCompositionStart(). DispatchCompositionStart() dispatches |
|
197 // a keydown event if the composition start is caused by a native |
|
198 // keypress event. If this is true, the keydown event has been dispatched. |
|
199 // Then, DispatchCompositionStart() doesn't dispatch keydown event. |
|
200 bool mKeyDownEventWasSent; |
|
201 |
|
202 // sLastFocusedModule is a pointer to the last focused instance of this |
|
203 // class. When a instance is destroyed and sLastFocusedModule refers it, |
|
204 // this is cleared. So, this refers valid pointer always. |
|
205 static nsGtkIMModule* sLastFocusedModule; |
|
206 |
|
207 // sUseSimpleContext indeicates if password editors and editors with |
|
208 // |ime-mode: disabled;| should use GtkIMContextSimple. |
|
209 // If true, they use GtkIMContextSimple. Otherwise, not. |
|
210 static bool sUseSimpleContext; |
|
211 |
|
212 // Callback methods for native IME events. These methods should call |
|
213 // the related instance methods simply. |
|
214 static gboolean OnRetrieveSurroundingCallback(GtkIMContext *aContext, |
|
215 nsGtkIMModule *aModule); |
|
216 static gboolean OnDeleteSurroundingCallback(GtkIMContext *aContext, |
|
217 gint aOffset, |
|
218 gint aNChars, |
|
219 nsGtkIMModule *aModule); |
|
220 static void OnCommitCompositionCallback(GtkIMContext *aContext, |
|
221 const gchar *aString, |
|
222 nsGtkIMModule* aModule); |
|
223 static void OnChangeCompositionCallback(GtkIMContext *aContext, |
|
224 nsGtkIMModule* aModule); |
|
225 static void OnStartCompositionCallback(GtkIMContext *aContext, |
|
226 nsGtkIMModule* aModule); |
|
227 static void OnEndCompositionCallback(GtkIMContext *aContext, |
|
228 nsGtkIMModule* aModule); |
|
229 |
|
230 // The instance methods for the native IME events. |
|
231 gboolean OnRetrieveSurroundingNative(GtkIMContext *aContext); |
|
232 gboolean OnDeleteSurroundingNative(GtkIMContext *aContext, |
|
233 gint aOffset, |
|
234 gint aNChars); |
|
235 void OnCommitCompositionNative(GtkIMContext *aContext, |
|
236 const gchar *aString); |
|
237 void OnChangeCompositionNative(GtkIMContext *aContext); |
|
238 void OnStartCompositionNative(GtkIMContext *aContext); |
|
239 void OnEndCompositionNative(GtkIMContext *aContext); |
|
240 |
|
241 |
|
242 // GetContext() returns current IM context which is chosen by the enabled |
|
243 // state. So, this means *current* IM context. |
|
244 GtkIMContext* GetContext(); |
|
245 |
|
246 // "Editable" means the users can input characters. They may be not able to |
|
247 // use IMEs but they can use dead keys. |
|
248 // I.e., the focus is in the normal editors or the password editors or |
|
249 // the |ime-mode: disabled;| editors. |
|
250 bool IsEditable(); |
|
251 |
|
252 // If the owner window and IM context have been destroyed, returns TRUE. |
|
253 bool IsDestroyed() { return !mOwnerWindow; } |
|
254 |
|
255 // Sets focus to the instance of this class. |
|
256 void Focus(); |
|
257 |
|
258 // Steals focus from the instance of this class. |
|
259 void Blur(); |
|
260 |
|
261 // Initializes the instance. |
|
262 void Init(); |
|
263 |
|
264 // Reset the current composition of IME. All native composition events |
|
265 // during this processing are ignored. |
|
266 void ResetIME(); |
|
267 |
|
268 // Gets the current composition string by the native APIs. |
|
269 void GetCompositionString(nsAString &aCompositionString); |
|
270 |
|
271 // Generates our text range array from current composition string. |
|
272 already_AddRefed<mozilla::TextRangeArray> CreateTextRangeArray(); |
|
273 |
|
274 // Sets the offset's cursor position to IME. |
|
275 void SetCursorPosition(uint32_t aTargetOffset); |
|
276 |
|
277 // Queries the current selection offset of the window. |
|
278 uint32_t GetSelectionOffset(nsWindow* aWindow); |
|
279 |
|
280 // Get current paragraph text content and cursor position |
|
281 nsresult GetCurrentParagraph(nsAString& aText, uint32_t& aCursorPos); |
|
282 |
|
283 // Delete text portion |
|
284 nsresult DeleteText(const int32_t aOffset, const uint32_t aNChars); |
|
285 |
|
286 // Initializes the GUI event. |
|
287 void InitEvent(mozilla::WidgetGUIEvent& aEvent); |
|
288 |
|
289 // Called before destroying the context to work around some platform bugs. |
|
290 void PrepareToDestroyContext(GtkIMContext *aContext); |
|
291 |
|
292 bool ShouldIgnoreNativeCompositionEvent(); |
|
293 |
|
294 /** |
|
295 * WARNING: |
|
296 * Following methods dispatch gecko events. Then, the focused widget |
|
297 * can be destroyed, and also it can be stolen focus. If they returns |
|
298 * FALSE, callers cannot continue the composition. |
|
299 * - CommitCompositionBy |
|
300 * - DispatchCompositionStart |
|
301 * - DispatchCompositionEnd |
|
302 * - DispatchTextEvent |
|
303 */ |
|
304 |
|
305 // Commits the current composition by the aString. |
|
306 bool CommitCompositionBy(const nsAString& aString); |
|
307 |
|
308 // Dispatches a composition start event or a composition end event. |
|
309 bool DispatchCompositionStart(); |
|
310 bool DispatchCompositionEnd(); |
|
311 |
|
312 // Dispatches a text event. If aIsCommit is TRUE, dispatches a committed |
|
313 // text event. Otherwise, dispatches a composing text event. |
|
314 bool DispatchTextEvent(const nsAString& aCompositionString, |
|
315 bool aIsCommit); |
|
316 |
|
317 }; |
|
318 |
|
319 #endif // __nsGtkIMModule_h__ |