widget/windows/nsIMM32Handler.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #ifndef nsIMM32Handler_h__
michael@0 7 #define nsIMM32Handler_h__
michael@0 8
michael@0 9 #include "nscore.h"
michael@0 10 #include <windows.h>
michael@0 11 #include "nsCOMPtr.h"
michael@0 12 #include "nsString.h"
michael@0 13 #include "nsTArray.h"
michael@0 14 #include "nsIWidget.h"
michael@0 15 #include "mozilla/EventForwards.h"
michael@0 16
michael@0 17 class nsWindow;
michael@0 18 struct nsIntRect;
michael@0 19
michael@0 20 namespace mozilla {
michael@0 21 namespace widget {
michael@0 22
michael@0 23 struct MSGResult;
michael@0 24
michael@0 25 } // namespace widget
michael@0 26 } // namespace mozilla
michael@0 27
michael@0 28 class nsIMEContext
michael@0 29 {
michael@0 30 public:
michael@0 31 nsIMEContext(HWND aWnd) : mWnd(aWnd)
michael@0 32 {
michael@0 33 mIMC = ::ImmGetContext(mWnd);
michael@0 34 }
michael@0 35
michael@0 36 ~nsIMEContext()
michael@0 37 {
michael@0 38 if (mIMC) {
michael@0 39 ::ImmReleaseContext(mWnd, mIMC);
michael@0 40 mIMC = nullptr;
michael@0 41 }
michael@0 42 }
michael@0 43
michael@0 44 HIMC get() const
michael@0 45 {
michael@0 46 return mIMC;
michael@0 47 }
michael@0 48
michael@0 49 bool IsValid() const
michael@0 50 {
michael@0 51 return !!mIMC;
michael@0 52 }
michael@0 53
michael@0 54 void SetOpenState(bool aOpen) const
michael@0 55 {
michael@0 56 if (!mIMC) {
michael@0 57 return;
michael@0 58 }
michael@0 59 ::ImmSetOpenStatus(mIMC, aOpen);
michael@0 60 }
michael@0 61
michael@0 62 bool GetOpenState() const
michael@0 63 {
michael@0 64 if (!mIMC) {
michael@0 65 return false;
michael@0 66 }
michael@0 67 return (::ImmGetOpenStatus(mIMC) != FALSE);
michael@0 68 }
michael@0 69
michael@0 70 bool AssociateDefaultContext()
michael@0 71 {
michael@0 72 // We assume that there is only default IMC, no new IMC has been created.
michael@0 73 if (mIMC) {
michael@0 74 return false;
michael@0 75 }
michael@0 76 if (!::ImmAssociateContextEx(mWnd, nullptr, IACE_DEFAULT)) {
michael@0 77 return false;
michael@0 78 }
michael@0 79 mIMC = ::ImmGetContext(mWnd);
michael@0 80 return (mIMC != nullptr);
michael@0 81 }
michael@0 82
michael@0 83 bool Disassociate()
michael@0 84 {
michael@0 85 if (!mIMC) {
michael@0 86 return false;
michael@0 87 }
michael@0 88 if (!::ImmAssociateContextEx(mWnd, nullptr, 0)) {
michael@0 89 return false;
michael@0 90 }
michael@0 91 ::ImmReleaseContext(mWnd, mIMC);
michael@0 92 mIMC = nullptr;
michael@0 93 return true;
michael@0 94 }
michael@0 95
michael@0 96 protected:
michael@0 97 nsIMEContext()
michael@0 98 {
michael@0 99 NS_ERROR("Don't create nsIMEContext without window handle");
michael@0 100 }
michael@0 101
michael@0 102 nsIMEContext(const nsIMEContext &aSrc) : mWnd(nullptr), mIMC(nullptr)
michael@0 103 {
michael@0 104 NS_ERROR("Don't copy nsIMEContext");
michael@0 105 }
michael@0 106
michael@0 107 HWND mWnd;
michael@0 108 HIMC mIMC;
michael@0 109 };
michael@0 110
michael@0 111 class nsIMM32Handler
michael@0 112 {
michael@0 113 typedef mozilla::widget::MSGResult MSGResult;
michael@0 114 public:
michael@0 115 static void Initialize();
michael@0 116 static void Terminate();
michael@0 117
michael@0 118 // If Process*() returns true, the caller shouldn't do anything anymore.
michael@0 119 static bool ProcessMessage(nsWindow* aWindow, UINT msg,
michael@0 120 WPARAM& wParam, LPARAM& lParam,
michael@0 121 MSGResult& aResult);
michael@0 122 static bool IsComposing()
michael@0 123 {
michael@0 124 return IsComposingOnOurEditor() || IsComposingOnPlugin();
michael@0 125 }
michael@0 126 static bool IsComposingOn(nsWindow* aWindow)
michael@0 127 {
michael@0 128 return IsComposing() && IsComposingWindow(aWindow);
michael@0 129 }
michael@0 130
michael@0 131 #ifdef DEBUG
michael@0 132 /**
michael@0 133 * IsIMEAvailable() returns TRUE when current keyboard layout has IME.
michael@0 134 * Otherwise, FALSE.
michael@0 135 */
michael@0 136 static bool IsIMEAvailable() { return !!::ImmIsIME(::GetKeyboardLayout(0)); }
michael@0 137 #endif
michael@0 138
michael@0 139 // If aForce is TRUE, these methods doesn't check whether we have composition
michael@0 140 // or not. If you don't set it to TRUE, these method doesn't commit/cancel
michael@0 141 // the composition on uexpected window.
michael@0 142 static void CommitComposition(nsWindow* aWindow, bool aForce = false);
michael@0 143 static void CancelComposition(nsWindow* aWindow, bool aForce = false);
michael@0 144 static void OnUpdateComposition(nsWindow* aWindow);
michael@0 145
michael@0 146 static nsIMEUpdatePreference GetIMEUpdatePreference();
michael@0 147
michael@0 148 protected:
michael@0 149 static void EnsureHandlerInstance();
michael@0 150
michael@0 151 static bool IsComposingOnOurEditor();
michael@0 152 static bool IsComposingOnPlugin();
michael@0 153 static bool IsComposingWindow(nsWindow* aWindow);
michael@0 154
michael@0 155 static bool ShouldDrawCompositionStringOurselves();
michael@0 156 static void InitKeyboardLayout(HKL aKeyboardLayout);
michael@0 157 static UINT GetKeyboardCodePage();
michael@0 158
michael@0 159 /**
michael@0 160 * Checks whether the window is top level window of the composing window.
michael@0 161 * In this method, the top level window means in all windows, not only in all
michael@0 162 * OUR windows. I.e., if the aWindow is embedded, this always returns FALSE.
michael@0 163 */
michael@0 164 static bool IsTopLevelWindowOfComposition(nsWindow* aWindow);
michael@0 165
michael@0 166 static bool ProcessInputLangChangeMessage(nsWindow* aWindow,
michael@0 167 WPARAM wParam,
michael@0 168 LPARAM lParam,
michael@0 169 MSGResult& aResult);
michael@0 170 static bool ProcessMessageForPlugin(nsWindow* aWindow, UINT msg,
michael@0 171 WPARAM &wParam, LPARAM &lParam,
michael@0 172 MSGResult& aResult);
michael@0 173
michael@0 174 nsIMM32Handler();
michael@0 175 ~nsIMM32Handler();
michael@0 176
michael@0 177 // On*() methods return true if the caller of message handler shouldn't do
michael@0 178 // anything anymore. Otherwise, false.
michael@0 179 bool OnMouseEvent(nsWindow* aWindow, LPARAM lParam, int aAction,
michael@0 180 MSGResult& aResult);
michael@0 181 static bool OnKeyDownEvent(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
michael@0 182 MSGResult& aResult);
michael@0 183
michael@0 184 bool OnIMEStartComposition(nsWindow* aWindow, MSGResult& aResult);
michael@0 185 bool OnIMEStartCompositionOnPlugin(nsWindow* aWindow,
michael@0 186 WPARAM wParam, LPARAM lParam,
michael@0 187 MSGResult& aResult);
michael@0 188 bool OnIMEComposition(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
michael@0 189 MSGResult& aResult);
michael@0 190 bool OnIMECompositionOnPlugin(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
michael@0 191 MSGResult& aResult);
michael@0 192 bool OnIMEEndComposition(nsWindow* aWindow, MSGResult& aResult);
michael@0 193 bool OnIMEEndCompositionOnPlugin(nsWindow* aWindow, WPARAM wParam,
michael@0 194 LPARAM lParam, MSGResult& aResult);
michael@0 195 bool OnIMERequest(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
michael@0 196 MSGResult& aResult);
michael@0 197 bool OnIMECharOnPlugin(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
michael@0 198 MSGResult& aResult);
michael@0 199 bool OnChar(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
michael@0 200 MSGResult& aResult);
michael@0 201 bool OnCharOnPlugin(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
michael@0 202 MSGResult& aResult);
michael@0 203 void OnInputLangChange(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
michael@0 204 MSGResult& aResult);
michael@0 205
michael@0 206 // These message handlers don't use instance members, we should not create
michael@0 207 // the instance by the messages. So, they should be static.
michael@0 208 static bool OnIMEChar(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
michael@0 209 MSGResult& aResult);
michael@0 210 static bool OnIMESetContext(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
michael@0 211 MSGResult& aResult);
michael@0 212 static bool OnIMESetContextOnPlugin(nsWindow* aWindow,
michael@0 213 WPARAM wParam, LPARAM lParam,
michael@0 214 MSGResult& aResult);
michael@0 215 static bool OnIMECompositionFull(nsWindow* aWindow, MSGResult& aResult);
michael@0 216 static bool OnIMENotify(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
michael@0 217 MSGResult& aResult);
michael@0 218 static bool OnIMESelect(nsWindow* aWindow, WPARAM wParam, LPARAM lParam,
michael@0 219 MSGResult& aResult);
michael@0 220
michael@0 221 // The result of Handle* method mean "Processed" when it's TRUE.
michael@0 222 void HandleStartComposition(nsWindow* aWindow,
michael@0 223 const nsIMEContext &aIMEContext);
michael@0 224 bool HandleComposition(nsWindow* aWindow, const nsIMEContext &aIMEContext,
michael@0 225 LPARAM lParam);
michael@0 226 void HandleEndComposition(nsWindow* aWindow);
michael@0 227 bool HandleReconvert(nsWindow* aWindow, LPARAM lParam, LRESULT *oResult);
michael@0 228 bool HandleQueryCharPosition(nsWindow* aWindow, LPARAM lParam,
michael@0 229 LRESULT *oResult);
michael@0 230 bool HandleDocumentFeed(nsWindow* aWindow, LPARAM lParam, LRESULT *oResult);
michael@0 231
michael@0 232 /**
michael@0 233 * When a window's IME context is activating but we have composition on
michael@0 234 * another window, we should commit our composition because IME context is
michael@0 235 * shared by all our windows (including plug-ins).
michael@0 236 * @param aWindow is a new activated window.
michael@0 237 * If aWindow is our composing window, this method does nothing.
michael@0 238 * Otherwise, this commits the composition on the previous window.
michael@0 239 * If this method did commit a composition, this returns TRUE.
michael@0 240 */
michael@0 241 bool CommitCompositionOnPreviousWindow(nsWindow* aWindow);
michael@0 242
michael@0 243 /**
michael@0 244 * ResolveIMECaretPos
michael@0 245 * Convert the caret rect of a composition event to another widget's
michael@0 246 * coordinate system.
michael@0 247 *
michael@0 248 * @param aReferenceWidget The origin widget of aCursorRect.
michael@0 249 * Typically, this is mReferenceWidget of the
michael@0 250 * composing events. If the aCursorRect is in screen
michael@0 251 * coordinates, set nullptr.
michael@0 252 * @param aCursorRect The cursor rect.
michael@0 253 * @param aNewOriginWidget aOutRect will be in this widget's coordinates. If
michael@0 254 * this is nullptr, aOutRect will be in screen
michael@0 255 * coordinates.
michael@0 256 * @param aOutRect The converted cursor rect.
michael@0 257 */
michael@0 258 void ResolveIMECaretPos(nsIWidget* aReferenceWidget,
michael@0 259 nsIntRect& aCursorRect,
michael@0 260 nsIWidget* aNewOriginWidget,
michael@0 261 nsIntRect& aOutRect);
michael@0 262
michael@0 263 bool ConvertToANSIString(const nsAFlatString& aStr,
michael@0 264 UINT aCodePage,
michael@0 265 nsACString& aANSIStr);
michael@0 266
michael@0 267 bool SetIMERelatedWindowsPos(nsWindow* aWindow,
michael@0 268 const nsIMEContext& aIMEContext);
michael@0 269 void SetIMERelatedWindowsPosOnPlugin(nsWindow* aWindow,
michael@0 270 const nsIMEContext& aIMEContext);
michael@0 271 bool GetCharacterRectOfSelectedTextAt(nsWindow* aWindow,
michael@0 272 uint32_t aOffset,
michael@0 273 nsIntRect &aCharRect);
michael@0 274 bool GetCaretRect(nsWindow* aWindow, nsIntRect &aCaretRect);
michael@0 275 void GetCompositionString(const nsIMEContext &aIMEContext, DWORD aIndex);
michael@0 276 /**
michael@0 277 * Get the current target clause of composition string.
michael@0 278 * If there are one or more characters whose attribute is ATTR_TARGET_*,
michael@0 279 * this returns the first character's offset and its length.
michael@0 280 * Otherwise, e.g., the all characters are ATTR_INPUT, this returns
michael@0 281 * the composition string range because the all is the current target.
michael@0 282 *
michael@0 283 * aLength can be null (default), but aOffset must not be null.
michael@0 284 *
michael@0 285 * The aOffset value is offset in the contents. So, when you need offset
michael@0 286 * in the composition string, you need to subtract mCompositionStart from it.
michael@0 287 */
michael@0 288 bool GetTargetClauseRange(uint32_t *aOffset, uint32_t *aLength = nullptr);
michael@0 289 void DispatchTextEvent(nsWindow* aWindow, const nsIMEContext &aIMEContext,
michael@0 290 bool aCheckAttr = true);
michael@0 291 already_AddRefed<mozilla::TextRangeArray> CreateTextRangeArray();
michael@0 292
michael@0 293 nsresult EnsureClauseArray(int32_t aCount);
michael@0 294 nsresult EnsureAttributeArray(int32_t aCount);
michael@0 295
michael@0 296 /**
michael@0 297 * When WM_IME_CHAR is received and passed to DefWindowProc, we need to
michael@0 298 * record the messages. In other words, we should record the messages
michael@0 299 * when we receive WM_IME_CHAR on windowless plug-in (if we have focus,
michael@0 300 * we always eat them). When focus is moved from a windowless plug-in to
michael@0 301 * our window during composition, WM_IME_CHAR messages were received when
michael@0 302 * the plug-in has focus. However, WM_CHAR messages are received after the
michael@0 303 * plug-in lost focus. So, we need to ignore the WM_CHAR messages because
michael@0 304 * they make unexpected text input events on us.
michael@0 305 */
michael@0 306 nsTArray<MSG> mPassedIMEChar;
michael@0 307
michael@0 308 bool IsIMECharRecordsEmpty()
michael@0 309 {
michael@0 310 return mPassedIMEChar.IsEmpty();
michael@0 311 }
michael@0 312 void ResetIMECharRecords()
michael@0 313 {
michael@0 314 mPassedIMEChar.Clear();
michael@0 315 }
michael@0 316 void DequeueIMECharRecords(WPARAM &wParam, LPARAM &lParam)
michael@0 317 {
michael@0 318 MSG msg = mPassedIMEChar.ElementAt(0);
michael@0 319 wParam = msg.wParam;
michael@0 320 lParam = msg.lParam;
michael@0 321 mPassedIMEChar.RemoveElementAt(0);
michael@0 322 }
michael@0 323 void EnqueueIMECharRecords(WPARAM wParam, LPARAM lParam)
michael@0 324 {
michael@0 325 MSG msg;
michael@0 326 msg.wParam = wParam;
michael@0 327 msg.lParam = lParam;
michael@0 328 mPassedIMEChar.AppendElement(msg);
michael@0 329 }
michael@0 330
michael@0 331 nsWindow* mComposingWindow;
michael@0 332 nsString mCompositionString;
michael@0 333 nsString mLastDispatchedCompositionString;
michael@0 334 InfallibleTArray<uint32_t> mClauseArray;
michael@0 335 InfallibleTArray<uint8_t> mAttributeArray;
michael@0 336
michael@0 337 int32_t mCursorPosition;
michael@0 338 uint32_t mCompositionStart;
michael@0 339
michael@0 340 bool mIsComposing;
michael@0 341 bool mIsComposingOnPlugin;
michael@0 342 bool mNativeCaretIsCreated;
michael@0 343
michael@0 344 static UINT sCodePage;
michael@0 345 static DWORD sIMEProperty;
michael@0 346 };
michael@0 347
michael@0 348 #endif // nsIMM32Handler_h__

mercurial