1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef nsIMM32Handler_h__
7 #define nsIMM32Handler_h__
14 #include "nsIWidget.h"
15 #include "mozilla/EventForwards.h"
26 } // namespace mozilla
31 nsIMEContext(HWND aWnd
) : mWnd(aWnd
)
33 mIMC
= ::ImmGetContext(mWnd
);
39 ::ImmReleaseContext(mWnd
, mIMC
);
54 void SetOpenState(bool aOpen
) const
59 ::ImmSetOpenStatus(mIMC
, aOpen
);
62 bool GetOpenState() const
67 return (::ImmGetOpenStatus(mIMC
) != FALSE
);
70 bool AssociateDefaultContext()
72 // We assume that there is only default IMC, no new IMC has been created.
76 if (!::ImmAssociateContextEx(mWnd
, nullptr, IACE_DEFAULT
)) {
79 mIMC
= ::ImmGetContext(mWnd
);
80 return (mIMC
!= nullptr);
88 if (!::ImmAssociateContextEx(mWnd
, nullptr, 0)) {
91 ::ImmReleaseContext(mWnd
, mIMC
);
99 NS_ERROR("Don't create nsIMEContext without window handle");
102 nsIMEContext(const nsIMEContext
&aSrc
) : mWnd(nullptr), mIMC(nullptr)
104 NS_ERROR("Don't copy nsIMEContext");
113 typedef mozilla::widget::MSGResult MSGResult
;
115 static void Initialize();
116 static void Terminate();
118 // If Process*() returns true, the caller shouldn't do anything anymore.
119 static bool ProcessMessage(nsWindow
* aWindow
, UINT msg
,
120 WPARAM
& wParam
, LPARAM
& lParam
,
122 static bool IsComposing()
124 return IsComposingOnOurEditor() || IsComposingOnPlugin();
126 static bool IsComposingOn(nsWindow
* aWindow
)
128 return IsComposing() && IsComposingWindow(aWindow
);
133 * IsIMEAvailable() returns TRUE when current keyboard layout has IME.
136 static bool IsIMEAvailable() { return !!::ImmIsIME(::GetKeyboardLayout(0)); }
139 // If aForce is TRUE, these methods doesn't check whether we have composition
140 // or not. If you don't set it to TRUE, these method doesn't commit/cancel
141 // the composition on uexpected window.
142 static void CommitComposition(nsWindow
* aWindow
, bool aForce
= false);
143 static void CancelComposition(nsWindow
* aWindow
, bool aForce
= false);
144 static void OnUpdateComposition(nsWindow
* aWindow
);
146 static nsIMEUpdatePreference
GetIMEUpdatePreference();
149 static void EnsureHandlerInstance();
151 static bool IsComposingOnOurEditor();
152 static bool IsComposingOnPlugin();
153 static bool IsComposingWindow(nsWindow
* aWindow
);
155 static bool ShouldDrawCompositionStringOurselves();
156 static void InitKeyboardLayout(HKL aKeyboardLayout
);
157 static UINT
GetKeyboardCodePage();
160 * Checks whether the window is top level window of the composing window.
161 * In this method, the top level window means in all windows, not only in all
162 * OUR windows. I.e., if the aWindow is embedded, this always returns FALSE.
164 static bool IsTopLevelWindowOfComposition(nsWindow
* aWindow
);
166 static bool ProcessInputLangChangeMessage(nsWindow
* aWindow
,
170 static bool ProcessMessageForPlugin(nsWindow
* aWindow
, UINT msg
,
171 WPARAM
&wParam
, LPARAM
&lParam
,
177 // On*() methods return true if the caller of message handler shouldn't do
178 // anything anymore. Otherwise, false.
179 bool OnMouseEvent(nsWindow
* aWindow
, LPARAM lParam
, int aAction
,
181 static bool OnKeyDownEvent(nsWindow
* aWindow
, WPARAM wParam
, LPARAM lParam
,
184 bool OnIMEStartComposition(nsWindow
* aWindow
, MSGResult
& aResult
);
185 bool OnIMEStartCompositionOnPlugin(nsWindow
* aWindow
,
186 WPARAM wParam
, LPARAM lParam
,
188 bool OnIMEComposition(nsWindow
* aWindow
, WPARAM wParam
, LPARAM lParam
,
190 bool OnIMECompositionOnPlugin(nsWindow
* aWindow
, WPARAM wParam
, LPARAM lParam
,
192 bool OnIMEEndComposition(nsWindow
* aWindow
, MSGResult
& aResult
);
193 bool OnIMEEndCompositionOnPlugin(nsWindow
* aWindow
, WPARAM wParam
,
194 LPARAM lParam
, MSGResult
& aResult
);
195 bool OnIMERequest(nsWindow
* aWindow
, WPARAM wParam
, LPARAM lParam
,
197 bool OnIMECharOnPlugin(nsWindow
* aWindow
, WPARAM wParam
, LPARAM lParam
,
199 bool OnChar(nsWindow
* aWindow
, WPARAM wParam
, LPARAM lParam
,
201 bool OnCharOnPlugin(nsWindow
* aWindow
, WPARAM wParam
, LPARAM lParam
,
203 void OnInputLangChange(nsWindow
* aWindow
, WPARAM wParam
, LPARAM lParam
,
206 // These message handlers don't use instance members, we should not create
207 // the instance by the messages. So, they should be static.
208 static bool OnIMEChar(nsWindow
* aWindow
, WPARAM wParam
, LPARAM lParam
,
210 static bool OnIMESetContext(nsWindow
* aWindow
, WPARAM wParam
, LPARAM lParam
,
212 static bool OnIMESetContextOnPlugin(nsWindow
* aWindow
,
213 WPARAM wParam
, LPARAM lParam
,
215 static bool OnIMECompositionFull(nsWindow
* aWindow
, MSGResult
& aResult
);
216 static bool OnIMENotify(nsWindow
* aWindow
, WPARAM wParam
, LPARAM lParam
,
218 static bool OnIMESelect(nsWindow
* aWindow
, WPARAM wParam
, LPARAM lParam
,
221 // The result of Handle* method mean "Processed" when it's TRUE.
222 void HandleStartComposition(nsWindow
* aWindow
,
223 const nsIMEContext
&aIMEContext
);
224 bool HandleComposition(nsWindow
* aWindow
, const nsIMEContext
&aIMEContext
,
226 void HandleEndComposition(nsWindow
* aWindow
);
227 bool HandleReconvert(nsWindow
* aWindow
, LPARAM lParam
, LRESULT
*oResult
);
228 bool HandleQueryCharPosition(nsWindow
* aWindow
, LPARAM lParam
,
230 bool HandleDocumentFeed(nsWindow
* aWindow
, LPARAM lParam
, LRESULT
*oResult
);
233 * When a window's IME context is activating but we have composition on
234 * another window, we should commit our composition because IME context is
235 * shared by all our windows (including plug-ins).
236 * @param aWindow is a new activated window.
237 * If aWindow is our composing window, this method does nothing.
238 * Otherwise, this commits the composition on the previous window.
239 * If this method did commit a composition, this returns TRUE.
241 bool CommitCompositionOnPreviousWindow(nsWindow
* aWindow
);
245 * Convert the caret rect of a composition event to another widget's
248 * @param aReferenceWidget The origin widget of aCursorRect.
249 * Typically, this is mReferenceWidget of the
250 * composing events. If the aCursorRect is in screen
251 * coordinates, set nullptr.
252 * @param aCursorRect The cursor rect.
253 * @param aNewOriginWidget aOutRect will be in this widget's coordinates. If
254 * this is nullptr, aOutRect will be in screen
256 * @param aOutRect The converted cursor rect.
258 void ResolveIMECaretPos(nsIWidget
* aReferenceWidget
,
259 nsIntRect
& aCursorRect
,
260 nsIWidget
* aNewOriginWidget
,
261 nsIntRect
& aOutRect
);
263 bool ConvertToANSIString(const nsAFlatString
& aStr
,
265 nsACString
& aANSIStr
);
267 bool SetIMERelatedWindowsPos(nsWindow
* aWindow
,
268 const nsIMEContext
& aIMEContext
);
269 void SetIMERelatedWindowsPosOnPlugin(nsWindow
* aWindow
,
270 const nsIMEContext
& aIMEContext
);
271 bool GetCharacterRectOfSelectedTextAt(nsWindow
* aWindow
,
273 nsIntRect
&aCharRect
);
274 bool GetCaretRect(nsWindow
* aWindow
, nsIntRect
&aCaretRect
);
275 void GetCompositionString(const nsIMEContext
&aIMEContext
, DWORD aIndex
);
277 * Get the current target clause of composition string.
278 * If there are one or more characters whose attribute is ATTR_TARGET_*,
279 * this returns the first character's offset and its length.
280 * Otherwise, e.g., the all characters are ATTR_INPUT, this returns
281 * the composition string range because the all is the current target.
283 * aLength can be null (default), but aOffset must not be null.
285 * The aOffset value is offset in the contents. So, when you need offset
286 * in the composition string, you need to subtract mCompositionStart from it.
288 bool GetTargetClauseRange(uint32_t *aOffset
, uint32_t *aLength
= nullptr);
289 void DispatchTextEvent(nsWindow
* aWindow
, const nsIMEContext
&aIMEContext
,
290 bool aCheckAttr
= true);
291 already_AddRefed
<mozilla::TextRangeArray
> CreateTextRangeArray();
293 nsresult
EnsureClauseArray(int32_t aCount
);
294 nsresult
EnsureAttributeArray(int32_t aCount
);
297 * When WM_IME_CHAR is received and passed to DefWindowProc, we need to
298 * record the messages. In other words, we should record the messages
299 * when we receive WM_IME_CHAR on windowless plug-in (if we have focus,
300 * we always eat them). When focus is moved from a windowless plug-in to
301 * our window during composition, WM_IME_CHAR messages were received when
302 * the plug-in has focus. However, WM_CHAR messages are received after the
303 * plug-in lost focus. So, we need to ignore the WM_CHAR messages because
304 * they make unexpected text input events on us.
306 nsTArray
<MSG
> mPassedIMEChar
;
308 bool IsIMECharRecordsEmpty()
310 return mPassedIMEChar
.IsEmpty();
312 void ResetIMECharRecords()
314 mPassedIMEChar
.Clear();
316 void DequeueIMECharRecords(WPARAM
&wParam
, LPARAM
&lParam
)
318 MSG msg
= mPassedIMEChar
.ElementAt(0);
321 mPassedIMEChar
.RemoveElementAt(0);
323 void EnqueueIMECharRecords(WPARAM wParam
, LPARAM lParam
)
328 mPassedIMEChar
.AppendElement(msg
);
331 nsWindow
* mComposingWindow
;
332 nsString mCompositionString
;
333 nsString mLastDispatchedCompositionString
;
334 InfallibleTArray
<uint32_t> mClauseArray
;
335 InfallibleTArray
<uint8_t> mAttributeArray
;
337 int32_t mCursorPosition
;
338 uint32_t mCompositionStart
;
341 bool mIsComposingOnPlugin
;
342 bool mNativeCaretIsCreated
;
344 static UINT sCodePage
;
345 static DWORD sIMEProperty
;
348 #endif // nsIMM32Handler_h__