1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/windows/nsIMM32Handler.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,348 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifndef nsIMM32Handler_h__ 1.10 +#define nsIMM32Handler_h__ 1.11 + 1.12 +#include "nscore.h" 1.13 +#include <windows.h> 1.14 +#include "nsCOMPtr.h" 1.15 +#include "nsString.h" 1.16 +#include "nsTArray.h" 1.17 +#include "nsIWidget.h" 1.18 +#include "mozilla/EventForwards.h" 1.19 + 1.20 +class nsWindow; 1.21 +struct nsIntRect; 1.22 + 1.23 +namespace mozilla { 1.24 +namespace widget { 1.25 + 1.26 +struct MSGResult; 1.27 + 1.28 +} // namespace widget 1.29 +} // namespace mozilla 1.30 + 1.31 +class nsIMEContext 1.32 +{ 1.33 +public: 1.34 + nsIMEContext(HWND aWnd) : mWnd(aWnd) 1.35 + { 1.36 + mIMC = ::ImmGetContext(mWnd); 1.37 + } 1.38 + 1.39 + ~nsIMEContext() 1.40 + { 1.41 + if (mIMC) { 1.42 + ::ImmReleaseContext(mWnd, mIMC); 1.43 + mIMC = nullptr; 1.44 + } 1.45 + } 1.46 + 1.47 + HIMC get() const 1.48 + { 1.49 + return mIMC; 1.50 + } 1.51 + 1.52 + bool IsValid() const 1.53 + { 1.54 + return !!mIMC; 1.55 + } 1.56 + 1.57 + void SetOpenState(bool aOpen) const 1.58 + { 1.59 + if (!mIMC) { 1.60 + return; 1.61 + } 1.62 + ::ImmSetOpenStatus(mIMC, aOpen); 1.63 + } 1.64 + 1.65 + bool GetOpenState() const 1.66 + { 1.67 + if (!mIMC) { 1.68 + return false; 1.69 + } 1.70 + return (::ImmGetOpenStatus(mIMC) != FALSE); 1.71 + } 1.72 + 1.73 + bool AssociateDefaultContext() 1.74 + { 1.75 + // We assume that there is only default IMC, no new IMC has been created. 1.76 + if (mIMC) { 1.77 + return false; 1.78 + } 1.79 + if (!::ImmAssociateContextEx(mWnd, nullptr, IACE_DEFAULT)) { 1.80 + return false; 1.81 + } 1.82 + mIMC = ::ImmGetContext(mWnd); 1.83 + return (mIMC != nullptr); 1.84 + } 1.85 + 1.86 + bool Disassociate() 1.87 + { 1.88 + if (!mIMC) { 1.89 + return false; 1.90 + } 1.91 + if (!::ImmAssociateContextEx(mWnd, nullptr, 0)) { 1.92 + return false; 1.93 + } 1.94 + ::ImmReleaseContext(mWnd, mIMC); 1.95 + mIMC = nullptr; 1.96 + return true; 1.97 + } 1.98 + 1.99 +protected: 1.100 + nsIMEContext() 1.101 + { 1.102 + NS_ERROR("Don't create nsIMEContext without window handle"); 1.103 + } 1.104 + 1.105 + nsIMEContext(const nsIMEContext &aSrc) : mWnd(nullptr), mIMC(nullptr) 1.106 + { 1.107 + NS_ERROR("Don't copy nsIMEContext"); 1.108 + } 1.109 + 1.110 + HWND mWnd; 1.111 + HIMC mIMC; 1.112 +}; 1.113 + 1.114 +class nsIMM32Handler 1.115 +{ 1.116 + typedef mozilla::widget::MSGResult MSGResult; 1.117 +public: 1.118 + static void Initialize(); 1.119 + static void Terminate(); 1.120 + 1.121 + // If Process*() returns true, the caller shouldn't do anything anymore. 1.122 + static bool ProcessMessage(nsWindow* aWindow, UINT msg, 1.123 + WPARAM& wParam, LPARAM& lParam, 1.124 + MSGResult& aResult); 1.125 + static bool IsComposing() 1.126 + { 1.127 + return IsComposingOnOurEditor() || IsComposingOnPlugin(); 1.128 + } 1.129 + static bool IsComposingOn(nsWindow* aWindow) 1.130 + { 1.131 + return IsComposing() && IsComposingWindow(aWindow); 1.132 + } 1.133 + 1.134 +#ifdef DEBUG 1.135 + /** 1.136 + * IsIMEAvailable() returns TRUE when current keyboard layout has IME. 1.137 + * Otherwise, FALSE. 1.138 + */ 1.139 + static bool IsIMEAvailable() { return !!::ImmIsIME(::GetKeyboardLayout(0)); } 1.140 +#endif 1.141 + 1.142 + // If aForce is TRUE, these methods doesn't check whether we have composition 1.143 + // or not. If you don't set it to TRUE, these method doesn't commit/cancel 1.144 + // the composition on uexpected window. 1.145 + static void CommitComposition(nsWindow* aWindow, bool aForce = false); 1.146 + static void CancelComposition(nsWindow* aWindow, bool aForce = false); 1.147 + static void OnUpdateComposition(nsWindow* aWindow); 1.148 + 1.149 + static nsIMEUpdatePreference GetIMEUpdatePreference(); 1.150 + 1.151 +protected: 1.152 + static void EnsureHandlerInstance(); 1.153 + 1.154 + static bool IsComposingOnOurEditor(); 1.155 + static bool IsComposingOnPlugin(); 1.156 + static bool IsComposingWindow(nsWindow* aWindow); 1.157 + 1.158 + static bool ShouldDrawCompositionStringOurselves(); 1.159 + static void InitKeyboardLayout(HKL aKeyboardLayout); 1.160 + static UINT GetKeyboardCodePage(); 1.161 + 1.162 + /** 1.163 + * Checks whether the window is top level window of the composing window. 1.164 + * In this method, the top level window means in all windows, not only in all 1.165 + * OUR windows. I.e., if the aWindow is embedded, this always returns FALSE. 1.166 + */ 1.167 + static bool IsTopLevelWindowOfComposition(nsWindow* aWindow); 1.168 + 1.169 + static bool ProcessInputLangChangeMessage(nsWindow* aWindow, 1.170 + WPARAM wParam, 1.171 + LPARAM lParam, 1.172 + MSGResult& aResult); 1.173 + static bool ProcessMessageForPlugin(nsWindow* aWindow, UINT msg, 1.174 + WPARAM &wParam, LPARAM &lParam, 1.175 + MSGResult& aResult); 1.176 + 1.177 + nsIMM32Handler(); 1.178 + ~nsIMM32Handler(); 1.179 + 1.180 + // On*() methods return true if the caller of message handler shouldn't do 1.181 + // anything anymore. Otherwise, false. 1.182 + bool OnMouseEvent(nsWindow* aWindow, LPARAM lParam, int aAction, 1.183 + MSGResult& aResult); 1.184 + static bool OnKeyDownEvent(nsWindow* aWindow, WPARAM wParam, LPARAM lParam, 1.185 + MSGResult& aResult); 1.186 + 1.187 + bool OnIMEStartComposition(nsWindow* aWindow, MSGResult& aResult); 1.188 + bool OnIMEStartCompositionOnPlugin(nsWindow* aWindow, 1.189 + WPARAM wParam, LPARAM lParam, 1.190 + MSGResult& aResult); 1.191 + bool OnIMEComposition(nsWindow* aWindow, WPARAM wParam, LPARAM lParam, 1.192 + MSGResult& aResult); 1.193 + bool OnIMECompositionOnPlugin(nsWindow* aWindow, WPARAM wParam, LPARAM lParam, 1.194 + MSGResult& aResult); 1.195 + bool OnIMEEndComposition(nsWindow* aWindow, MSGResult& aResult); 1.196 + bool OnIMEEndCompositionOnPlugin(nsWindow* aWindow, WPARAM wParam, 1.197 + LPARAM lParam, MSGResult& aResult); 1.198 + bool OnIMERequest(nsWindow* aWindow, WPARAM wParam, LPARAM lParam, 1.199 + MSGResult& aResult); 1.200 + bool OnIMECharOnPlugin(nsWindow* aWindow, WPARAM wParam, LPARAM lParam, 1.201 + MSGResult& aResult); 1.202 + bool OnChar(nsWindow* aWindow, WPARAM wParam, LPARAM lParam, 1.203 + MSGResult& aResult); 1.204 + bool OnCharOnPlugin(nsWindow* aWindow, WPARAM wParam, LPARAM lParam, 1.205 + MSGResult& aResult); 1.206 + void OnInputLangChange(nsWindow* aWindow, WPARAM wParam, LPARAM lParam, 1.207 + MSGResult& aResult); 1.208 + 1.209 + // These message handlers don't use instance members, we should not create 1.210 + // the instance by the messages. So, they should be static. 1.211 + static bool OnIMEChar(nsWindow* aWindow, WPARAM wParam, LPARAM lParam, 1.212 + MSGResult& aResult); 1.213 + static bool OnIMESetContext(nsWindow* aWindow, WPARAM wParam, LPARAM lParam, 1.214 + MSGResult& aResult); 1.215 + static bool OnIMESetContextOnPlugin(nsWindow* aWindow, 1.216 + WPARAM wParam, LPARAM lParam, 1.217 + MSGResult& aResult); 1.218 + static bool OnIMECompositionFull(nsWindow* aWindow, MSGResult& aResult); 1.219 + static bool OnIMENotify(nsWindow* aWindow, WPARAM wParam, LPARAM lParam, 1.220 + MSGResult& aResult); 1.221 + static bool OnIMESelect(nsWindow* aWindow, WPARAM wParam, LPARAM lParam, 1.222 + MSGResult& aResult); 1.223 + 1.224 + // The result of Handle* method mean "Processed" when it's TRUE. 1.225 + void HandleStartComposition(nsWindow* aWindow, 1.226 + const nsIMEContext &aIMEContext); 1.227 + bool HandleComposition(nsWindow* aWindow, const nsIMEContext &aIMEContext, 1.228 + LPARAM lParam); 1.229 + void HandleEndComposition(nsWindow* aWindow); 1.230 + bool HandleReconvert(nsWindow* aWindow, LPARAM lParam, LRESULT *oResult); 1.231 + bool HandleQueryCharPosition(nsWindow* aWindow, LPARAM lParam, 1.232 + LRESULT *oResult); 1.233 + bool HandleDocumentFeed(nsWindow* aWindow, LPARAM lParam, LRESULT *oResult); 1.234 + 1.235 + /** 1.236 + * When a window's IME context is activating but we have composition on 1.237 + * another window, we should commit our composition because IME context is 1.238 + * shared by all our windows (including plug-ins). 1.239 + * @param aWindow is a new activated window. 1.240 + * If aWindow is our composing window, this method does nothing. 1.241 + * Otherwise, this commits the composition on the previous window. 1.242 + * If this method did commit a composition, this returns TRUE. 1.243 + */ 1.244 + bool CommitCompositionOnPreviousWindow(nsWindow* aWindow); 1.245 + 1.246 + /** 1.247 + * ResolveIMECaretPos 1.248 + * Convert the caret rect of a composition event to another widget's 1.249 + * coordinate system. 1.250 + * 1.251 + * @param aReferenceWidget The origin widget of aCursorRect. 1.252 + * Typically, this is mReferenceWidget of the 1.253 + * composing events. If the aCursorRect is in screen 1.254 + * coordinates, set nullptr. 1.255 + * @param aCursorRect The cursor rect. 1.256 + * @param aNewOriginWidget aOutRect will be in this widget's coordinates. If 1.257 + * this is nullptr, aOutRect will be in screen 1.258 + * coordinates. 1.259 + * @param aOutRect The converted cursor rect. 1.260 + */ 1.261 + void ResolveIMECaretPos(nsIWidget* aReferenceWidget, 1.262 + nsIntRect& aCursorRect, 1.263 + nsIWidget* aNewOriginWidget, 1.264 + nsIntRect& aOutRect); 1.265 + 1.266 + bool ConvertToANSIString(const nsAFlatString& aStr, 1.267 + UINT aCodePage, 1.268 + nsACString& aANSIStr); 1.269 + 1.270 + bool SetIMERelatedWindowsPos(nsWindow* aWindow, 1.271 + const nsIMEContext& aIMEContext); 1.272 + void SetIMERelatedWindowsPosOnPlugin(nsWindow* aWindow, 1.273 + const nsIMEContext& aIMEContext); 1.274 + bool GetCharacterRectOfSelectedTextAt(nsWindow* aWindow, 1.275 + uint32_t aOffset, 1.276 + nsIntRect &aCharRect); 1.277 + bool GetCaretRect(nsWindow* aWindow, nsIntRect &aCaretRect); 1.278 + void GetCompositionString(const nsIMEContext &aIMEContext, DWORD aIndex); 1.279 + /** 1.280 + * Get the current target clause of composition string. 1.281 + * If there are one or more characters whose attribute is ATTR_TARGET_*, 1.282 + * this returns the first character's offset and its length. 1.283 + * Otherwise, e.g., the all characters are ATTR_INPUT, this returns 1.284 + * the composition string range because the all is the current target. 1.285 + * 1.286 + * aLength can be null (default), but aOffset must not be null. 1.287 + * 1.288 + * The aOffset value is offset in the contents. So, when you need offset 1.289 + * in the composition string, you need to subtract mCompositionStart from it. 1.290 + */ 1.291 + bool GetTargetClauseRange(uint32_t *aOffset, uint32_t *aLength = nullptr); 1.292 + void DispatchTextEvent(nsWindow* aWindow, const nsIMEContext &aIMEContext, 1.293 + bool aCheckAttr = true); 1.294 + already_AddRefed<mozilla::TextRangeArray> CreateTextRangeArray(); 1.295 + 1.296 + nsresult EnsureClauseArray(int32_t aCount); 1.297 + nsresult EnsureAttributeArray(int32_t aCount); 1.298 + 1.299 + /** 1.300 + * When WM_IME_CHAR is received and passed to DefWindowProc, we need to 1.301 + * record the messages. In other words, we should record the messages 1.302 + * when we receive WM_IME_CHAR on windowless plug-in (if we have focus, 1.303 + * we always eat them). When focus is moved from a windowless plug-in to 1.304 + * our window during composition, WM_IME_CHAR messages were received when 1.305 + * the plug-in has focus. However, WM_CHAR messages are received after the 1.306 + * plug-in lost focus. So, we need to ignore the WM_CHAR messages because 1.307 + * they make unexpected text input events on us. 1.308 + */ 1.309 + nsTArray<MSG> mPassedIMEChar; 1.310 + 1.311 + bool IsIMECharRecordsEmpty() 1.312 + { 1.313 + return mPassedIMEChar.IsEmpty(); 1.314 + } 1.315 + void ResetIMECharRecords() 1.316 + { 1.317 + mPassedIMEChar.Clear(); 1.318 + } 1.319 + void DequeueIMECharRecords(WPARAM &wParam, LPARAM &lParam) 1.320 + { 1.321 + MSG msg = mPassedIMEChar.ElementAt(0); 1.322 + wParam = msg.wParam; 1.323 + lParam = msg.lParam; 1.324 + mPassedIMEChar.RemoveElementAt(0); 1.325 + } 1.326 + void EnqueueIMECharRecords(WPARAM wParam, LPARAM lParam) 1.327 + { 1.328 + MSG msg; 1.329 + msg.wParam = wParam; 1.330 + msg.lParam = lParam; 1.331 + mPassedIMEChar.AppendElement(msg); 1.332 + } 1.333 + 1.334 + nsWindow* mComposingWindow; 1.335 + nsString mCompositionString; 1.336 + nsString mLastDispatchedCompositionString; 1.337 + InfallibleTArray<uint32_t> mClauseArray; 1.338 + InfallibleTArray<uint8_t> mAttributeArray; 1.339 + 1.340 + int32_t mCursorPosition; 1.341 + uint32_t mCompositionStart; 1.342 + 1.343 + bool mIsComposing; 1.344 + bool mIsComposingOnPlugin; 1.345 + bool mNativeCaretIsCreated; 1.346 + 1.347 + static UINT sCodePage; 1.348 + static DWORD sIMEProperty; 1.349 +}; 1.350 + 1.351 +#endif // nsIMM32Handler_h__