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