Thu, 22 Jan 2015 13:21:57 +0100
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 | #include <olectl.h> |
michael@0 | 7 | #include <algorithm> |
michael@0 | 8 | |
michael@0 | 9 | #ifdef MOZ_LOGGING |
michael@0 | 10 | #define FORCE_PR_LOG /* Allow logging in the release build */ |
michael@0 | 11 | #endif // MOZ_LOGGING |
michael@0 | 12 | #include "prlog.h" |
michael@0 | 13 | |
michael@0 | 14 | #include "nscore.h" |
michael@0 | 15 | #include "nsWindow.h" |
michael@0 | 16 | #ifdef MOZ_METRO |
michael@0 | 17 | #include "winrt/MetroWidget.h" |
michael@0 | 18 | #endif |
michael@0 | 19 | #include "nsPrintfCString.h" |
michael@0 | 20 | #include "WinUtils.h" |
michael@0 | 21 | #include "mozilla/Preferences.h" |
michael@0 | 22 | #include "mozilla/TextEvents.h" |
michael@0 | 23 | #include "mozilla/WindowsVersion.h" |
michael@0 | 24 | |
michael@0 | 25 | #define INPUTSCOPE_INIT_GUID |
michael@0 | 26 | #include "nsTextStore.h" |
michael@0 | 27 | |
michael@0 | 28 | using namespace mozilla; |
michael@0 | 29 | using namespace mozilla::widget; |
michael@0 | 30 | |
michael@0 | 31 | static const char* kPrefNameTSFEnabled = "intl.tsf.enable"; |
michael@0 | 32 | |
michael@0 | 33 | static const char* kLegacyPrefNameTSFEnabled = "intl.enable_tsf_support"; |
michael@0 | 34 | |
michael@0 | 35 | #ifdef PR_LOGGING |
michael@0 | 36 | /** |
michael@0 | 37 | * TSF related code should log its behavior even on release build especially |
michael@0 | 38 | * in the interface methods. |
michael@0 | 39 | * |
michael@0 | 40 | * In interface methods, use PR_LOG_ALWAYS. |
michael@0 | 41 | * In internal methods, use PR_LOG_DEBUG for logging normal behavior. |
michael@0 | 42 | * For logging error, use PR_LOG_ERROR. |
michael@0 | 43 | * |
michael@0 | 44 | * When an instance method is called, start with following text: |
michael@0 | 45 | * "TSF: 0x%p nsFoo::Bar(", the 0x%p should be the "this" of the nsFoo. |
michael@0 | 46 | * after that, start with: |
michael@0 | 47 | * "TSF: 0x%p nsFoo::Bar(" |
michael@0 | 48 | * In an internal method, start with following text: |
michael@0 | 49 | * "TSF: 0x%p nsFoo::Bar(" |
michael@0 | 50 | * When a static method is called, start with following text: |
michael@0 | 51 | * "TSF: nsFoo::Bar(" |
michael@0 | 52 | */ |
michael@0 | 53 | |
michael@0 | 54 | PRLogModuleInfo* sTextStoreLog = nullptr; |
michael@0 | 55 | #endif // #ifdef PR_LOGGING |
michael@0 | 56 | |
michael@0 | 57 | /******************************************************************/ |
michael@0 | 58 | /* InputScopeImpl */ |
michael@0 | 59 | /******************************************************************/ |
michael@0 | 60 | |
michael@0 | 61 | class InputScopeImpl MOZ_FINAL : public ITfInputScope |
michael@0 | 62 | { |
michael@0 | 63 | public: |
michael@0 | 64 | InputScopeImpl(const nsTArray<InputScope>& aList) : |
michael@0 | 65 | mRefCnt(1), |
michael@0 | 66 | mInputScopes(aList) |
michael@0 | 67 | { |
michael@0 | 68 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 69 | ("TSF: 0x%p InputScopeImpl()", this)); |
michael@0 | 70 | } |
michael@0 | 71 | |
michael@0 | 72 | STDMETHODIMP_(ULONG) AddRef(void) { return ++mRefCnt; } |
michael@0 | 73 | |
michael@0 | 74 | STDMETHODIMP_(ULONG) Release(void) |
michael@0 | 75 | { |
michael@0 | 76 | --mRefCnt; |
michael@0 | 77 | if (mRefCnt) { |
michael@0 | 78 | return mRefCnt; |
michael@0 | 79 | } |
michael@0 | 80 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 81 | ("TSF: 0x%p InputScopeImpl::Release() final", this)); |
michael@0 | 82 | delete this; |
michael@0 | 83 | return 0; |
michael@0 | 84 | } |
michael@0 | 85 | |
michael@0 | 86 | STDMETHODIMP QueryInterface(REFIID riid, void** ppv) |
michael@0 | 87 | { |
michael@0 | 88 | *ppv=nullptr; |
michael@0 | 89 | if ( (IID_IUnknown == riid) || (IID_ITfInputScope == riid) ) { |
michael@0 | 90 | *ppv = static_cast<ITfInputScope*>(this); |
michael@0 | 91 | } |
michael@0 | 92 | if (*ppv) { |
michael@0 | 93 | AddRef(); |
michael@0 | 94 | return S_OK; |
michael@0 | 95 | } |
michael@0 | 96 | return E_NOINTERFACE; |
michael@0 | 97 | } |
michael@0 | 98 | |
michael@0 | 99 | STDMETHODIMP GetInputScopes(InputScope** pprgInputScopes, UINT* pcCount) |
michael@0 | 100 | { |
michael@0 | 101 | uint32_t count = (mInputScopes.IsEmpty() ? 1 : mInputScopes.Length()); |
michael@0 | 102 | |
michael@0 | 103 | InputScope* pScope = (InputScope*) CoTaskMemAlloc(sizeof(InputScope) * count); |
michael@0 | 104 | NS_ENSURE_TRUE(pScope, E_OUTOFMEMORY); |
michael@0 | 105 | |
michael@0 | 106 | if (mInputScopes.IsEmpty()) { |
michael@0 | 107 | *pScope = IS_DEFAULT; |
michael@0 | 108 | *pcCount = 1; |
michael@0 | 109 | *pprgInputScopes = pScope; |
michael@0 | 110 | return S_OK; |
michael@0 | 111 | } |
michael@0 | 112 | |
michael@0 | 113 | *pcCount = 0; |
michael@0 | 114 | |
michael@0 | 115 | for (uint32_t idx = 0; idx < count; idx++) { |
michael@0 | 116 | *(pScope + idx) = mInputScopes[idx]; |
michael@0 | 117 | (*pcCount)++; |
michael@0 | 118 | } |
michael@0 | 119 | |
michael@0 | 120 | *pprgInputScopes = pScope; |
michael@0 | 121 | return S_OK; |
michael@0 | 122 | } |
michael@0 | 123 | |
michael@0 | 124 | STDMETHODIMP GetPhrase(BSTR **ppbstrPhrases, UINT *pcCount) { return E_NOTIMPL; } |
michael@0 | 125 | STDMETHODIMP GetRegularExpression(BSTR *pbstrRegExp) { return E_NOTIMPL; } |
michael@0 | 126 | STDMETHODIMP GetSRGS(BSTR *pbstrSRGS) { return E_NOTIMPL; } |
michael@0 | 127 | STDMETHODIMP GetXML(BSTR *pbstrXML) { return E_NOTIMPL; } |
michael@0 | 128 | |
michael@0 | 129 | private: |
michael@0 | 130 | DWORD mRefCnt; |
michael@0 | 131 | nsTArray<InputScope> mInputScopes; |
michael@0 | 132 | }; |
michael@0 | 133 | |
michael@0 | 134 | /******************************************************************/ |
michael@0 | 135 | /* nsTextStore */ |
michael@0 | 136 | /******************************************************************/ |
michael@0 | 137 | |
michael@0 | 138 | ITfThreadMgr* nsTextStore::sTsfThreadMgr = nullptr; |
michael@0 | 139 | ITfMessagePump* nsTextStore::sMessagePump = nullptr; |
michael@0 | 140 | ITfKeystrokeMgr* nsTextStore::sKeystrokeMgr = nullptr; |
michael@0 | 141 | ITfDisplayAttributeMgr* nsTextStore::sDisplayAttrMgr = nullptr; |
michael@0 | 142 | ITfCategoryMgr* nsTextStore::sCategoryMgr = nullptr; |
michael@0 | 143 | ITfDocumentMgr* nsTextStore::sTsfDisabledDocumentMgr = nullptr; |
michael@0 | 144 | ITfContext* nsTextStore::sTsfDisabledContext = nullptr; |
michael@0 | 145 | ITfInputProcessorProfiles* nsTextStore::sInputProcessorProfiles = nullptr; |
michael@0 | 146 | DWORD nsTextStore::sTsfClientId = 0; |
michael@0 | 147 | nsTextStore* nsTextStore::sTsfTextStore = nullptr; |
michael@0 | 148 | |
michael@0 | 149 | bool nsTextStore::sCreateNativeCaretForATOK = false; |
michael@0 | 150 | |
michael@0 | 151 | UINT nsTextStore::sFlushTIPInputMessage = 0; |
michael@0 | 152 | |
michael@0 | 153 | #define TEXTSTORE_DEFAULT_VIEW (1) |
michael@0 | 154 | |
michael@0 | 155 | #ifdef PR_LOGGING |
michael@0 | 156 | |
michael@0 | 157 | static const char* |
michael@0 | 158 | GetBoolName(bool aBool) |
michael@0 | 159 | { |
michael@0 | 160 | return aBool ? "true" : "false"; |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | static void |
michael@0 | 164 | HandleSeparator(nsCString& aDesc) |
michael@0 | 165 | { |
michael@0 | 166 | if (!aDesc.IsEmpty()) { |
michael@0 | 167 | aDesc.AppendLiteral(" | "); |
michael@0 | 168 | } |
michael@0 | 169 | } |
michael@0 | 170 | |
michael@0 | 171 | static const nsCString |
michael@0 | 172 | GetFindFlagName(DWORD aFindFlag) |
michael@0 | 173 | { |
michael@0 | 174 | nsAutoCString description; |
michael@0 | 175 | if (!aFindFlag) { |
michael@0 | 176 | description.AppendLiteral("no flags (0)"); |
michael@0 | 177 | return description; |
michael@0 | 178 | } |
michael@0 | 179 | if (aFindFlag & TS_ATTR_FIND_BACKWARDS) { |
michael@0 | 180 | description.AppendLiteral("TS_ATTR_FIND_BACKWARDS"); |
michael@0 | 181 | } |
michael@0 | 182 | if (aFindFlag & TS_ATTR_FIND_WANT_OFFSET) { |
michael@0 | 183 | HandleSeparator(description); |
michael@0 | 184 | description.AppendLiteral("TS_ATTR_FIND_WANT_OFFSET"); |
michael@0 | 185 | } |
michael@0 | 186 | if (aFindFlag & TS_ATTR_FIND_UPDATESTART) { |
michael@0 | 187 | HandleSeparator(description); |
michael@0 | 188 | description.AppendLiteral("TS_ATTR_FIND_UPDATESTART"); |
michael@0 | 189 | } |
michael@0 | 190 | if (aFindFlag & TS_ATTR_FIND_WANT_VALUE) { |
michael@0 | 191 | HandleSeparator(description); |
michael@0 | 192 | description.AppendLiteral("TS_ATTR_FIND_WANT_VALUE"); |
michael@0 | 193 | } |
michael@0 | 194 | if (aFindFlag & TS_ATTR_FIND_WANT_END) { |
michael@0 | 195 | HandleSeparator(description); |
michael@0 | 196 | description.AppendLiteral("TS_ATTR_FIND_WANT_END"); |
michael@0 | 197 | } |
michael@0 | 198 | if (aFindFlag & TS_ATTR_FIND_HIDDEN) { |
michael@0 | 199 | HandleSeparator(description); |
michael@0 | 200 | description.AppendLiteral("TS_ATTR_FIND_HIDDEN"); |
michael@0 | 201 | } |
michael@0 | 202 | if (description.IsEmpty()) { |
michael@0 | 203 | description.AppendLiteral("Unknown ("); |
michael@0 | 204 | description.AppendInt(static_cast<uint32_t>(aFindFlag)); |
michael@0 | 205 | description.AppendLiteral(")"); |
michael@0 | 206 | } |
michael@0 | 207 | return description; |
michael@0 | 208 | } |
michael@0 | 209 | |
michael@0 | 210 | static const char* |
michael@0 | 211 | GetIMEEnabledName(IMEState::Enabled aIMEEnabled) |
michael@0 | 212 | { |
michael@0 | 213 | switch (aIMEEnabled) { |
michael@0 | 214 | case IMEState::DISABLED: |
michael@0 | 215 | return "DISABLED"; |
michael@0 | 216 | case IMEState::ENABLED: |
michael@0 | 217 | return "ENABLED"; |
michael@0 | 218 | case IMEState::PASSWORD: |
michael@0 | 219 | return "PASSWORD"; |
michael@0 | 220 | case IMEState::PLUGIN: |
michael@0 | 221 | return "PLUGIN"; |
michael@0 | 222 | default: |
michael@0 | 223 | return "Invalid"; |
michael@0 | 224 | } |
michael@0 | 225 | } |
michael@0 | 226 | |
michael@0 | 227 | static const char* |
michael@0 | 228 | GetFocusChangeName(InputContextAction::FocusChange aFocusChange) |
michael@0 | 229 | { |
michael@0 | 230 | switch (aFocusChange) { |
michael@0 | 231 | case InputContextAction::FOCUS_NOT_CHANGED: |
michael@0 | 232 | return "FOCUS_NOT_CHANGED"; |
michael@0 | 233 | case InputContextAction::GOT_FOCUS: |
michael@0 | 234 | return "GOT_FOCUS"; |
michael@0 | 235 | case InputContextAction::LOST_FOCUS: |
michael@0 | 236 | return "LOST_FOCUS"; |
michael@0 | 237 | case InputContextAction::MENU_GOT_PSEUDO_FOCUS: |
michael@0 | 238 | return "MENU_GOT_PSEUDO_FOCUS"; |
michael@0 | 239 | case InputContextAction::MENU_LOST_PSEUDO_FOCUS: |
michael@0 | 240 | return "MENU_LOST_PSEUDO_FOCUS"; |
michael@0 | 241 | default: |
michael@0 | 242 | return "Unknown"; |
michael@0 | 243 | } |
michael@0 | 244 | } |
michael@0 | 245 | |
michael@0 | 246 | static nsCString |
michael@0 | 247 | GetCLSIDNameStr(REFCLSID aCLSID) |
michael@0 | 248 | { |
michael@0 | 249 | LPOLESTR str = nullptr; |
michael@0 | 250 | HRESULT hr = ::StringFromCLSID(aCLSID, &str); |
michael@0 | 251 | if (FAILED(hr) || !str || !str[0]) { |
michael@0 | 252 | return EmptyCString(); |
michael@0 | 253 | } |
michael@0 | 254 | |
michael@0 | 255 | nsAutoCString result; |
michael@0 | 256 | result = NS_ConvertUTF16toUTF8(str); |
michael@0 | 257 | ::CoTaskMemFree(str); |
michael@0 | 258 | return result; |
michael@0 | 259 | } |
michael@0 | 260 | |
michael@0 | 261 | static nsCString |
michael@0 | 262 | GetGUIDNameStr(REFGUID aGUID) |
michael@0 | 263 | { |
michael@0 | 264 | OLECHAR str[40]; |
michael@0 | 265 | int len = ::StringFromGUID2(aGUID, str, ArrayLength(str)); |
michael@0 | 266 | if (!len || !str[0]) { |
michael@0 | 267 | return EmptyCString(); |
michael@0 | 268 | } |
michael@0 | 269 | |
michael@0 | 270 | return NS_ConvertUTF16toUTF8(str); |
michael@0 | 271 | } |
michael@0 | 272 | |
michael@0 | 273 | static nsCString |
michael@0 | 274 | GetRIIDNameStr(REFIID aRIID) |
michael@0 | 275 | { |
michael@0 | 276 | LPOLESTR str = nullptr; |
michael@0 | 277 | HRESULT hr = ::StringFromIID(aRIID, &str); |
michael@0 | 278 | if (FAILED(hr) || !str || !str[0]) { |
michael@0 | 279 | return EmptyCString(); |
michael@0 | 280 | } |
michael@0 | 281 | |
michael@0 | 282 | nsAutoString key(L"Interface\\"); |
michael@0 | 283 | key += str; |
michael@0 | 284 | |
michael@0 | 285 | nsAutoCString result; |
michael@0 | 286 | wchar_t buf[256]; |
michael@0 | 287 | if (WinUtils::GetRegistryKey(HKEY_CLASSES_ROOT, key.get(), nullptr, |
michael@0 | 288 | buf, sizeof(buf))) { |
michael@0 | 289 | result = NS_ConvertUTF16toUTF8(buf); |
michael@0 | 290 | } else { |
michael@0 | 291 | result = NS_ConvertUTF16toUTF8(str); |
michael@0 | 292 | } |
michael@0 | 293 | |
michael@0 | 294 | ::CoTaskMemFree(str); |
michael@0 | 295 | return result; |
michael@0 | 296 | } |
michael@0 | 297 | |
michael@0 | 298 | static const char* |
michael@0 | 299 | GetCommonReturnValueName(HRESULT aResult) |
michael@0 | 300 | { |
michael@0 | 301 | switch (aResult) { |
michael@0 | 302 | case S_OK: |
michael@0 | 303 | return "S_OK"; |
michael@0 | 304 | case E_ABORT: |
michael@0 | 305 | return "E_ABORT"; |
michael@0 | 306 | case E_ACCESSDENIED: |
michael@0 | 307 | return "E_ACCESSDENIED"; |
michael@0 | 308 | case E_FAIL: |
michael@0 | 309 | return "E_FAIL"; |
michael@0 | 310 | case E_HANDLE: |
michael@0 | 311 | return "E_HANDLE"; |
michael@0 | 312 | case E_INVALIDARG: |
michael@0 | 313 | return "E_INVALIDARG"; |
michael@0 | 314 | case E_NOINTERFACE: |
michael@0 | 315 | return "E_NOINTERFACE"; |
michael@0 | 316 | case E_NOTIMPL: |
michael@0 | 317 | return "E_NOTIMPL"; |
michael@0 | 318 | case E_OUTOFMEMORY: |
michael@0 | 319 | return "E_OUTOFMEMORY"; |
michael@0 | 320 | case E_POINTER: |
michael@0 | 321 | return "E_POINTER"; |
michael@0 | 322 | case E_UNEXPECTED: |
michael@0 | 323 | return "E_UNEXPECTED"; |
michael@0 | 324 | default: |
michael@0 | 325 | return SUCCEEDED(aResult) ? "Succeeded" : "Failed"; |
michael@0 | 326 | } |
michael@0 | 327 | } |
michael@0 | 328 | |
michael@0 | 329 | static const char* |
michael@0 | 330 | GetTextStoreReturnValueName(HRESULT aResult) |
michael@0 | 331 | { |
michael@0 | 332 | switch (aResult) { |
michael@0 | 333 | case TS_E_FORMAT: |
michael@0 | 334 | return "TS_E_FORMAT"; |
michael@0 | 335 | case TS_E_INVALIDPOINT: |
michael@0 | 336 | return "TS_E_INVALIDPOINT"; |
michael@0 | 337 | case TS_E_INVALIDPOS: |
michael@0 | 338 | return "TS_E_INVALIDPOS"; |
michael@0 | 339 | case TS_E_NOINTERFACE: |
michael@0 | 340 | return "TS_E_NOINTERFACE"; |
michael@0 | 341 | case TS_E_NOLAYOUT: |
michael@0 | 342 | return "TS_E_NOLAYOUT"; |
michael@0 | 343 | case TS_E_NOLOCK: |
michael@0 | 344 | return "TS_E_NOLOCK"; |
michael@0 | 345 | case TS_E_NOOBJECT: |
michael@0 | 346 | return "TS_E_NOOBJECT"; |
michael@0 | 347 | case TS_E_NOSELECTION: |
michael@0 | 348 | return "TS_E_NOSELECTION"; |
michael@0 | 349 | case TS_E_NOSERVICE: |
michael@0 | 350 | return "TS_E_NOSERVICE"; |
michael@0 | 351 | case TS_E_READONLY: |
michael@0 | 352 | return "TS_E_READONLY"; |
michael@0 | 353 | case TS_E_SYNCHRONOUS: |
michael@0 | 354 | return "TS_E_SYNCHRONOUS"; |
michael@0 | 355 | case TS_S_ASYNC: |
michael@0 | 356 | return "TS_S_ASYNC"; |
michael@0 | 357 | default: |
michael@0 | 358 | return GetCommonReturnValueName(aResult); |
michael@0 | 359 | } |
michael@0 | 360 | } |
michael@0 | 361 | |
michael@0 | 362 | static const nsCString |
michael@0 | 363 | GetSinkMaskNameStr(DWORD aSinkMask) |
michael@0 | 364 | { |
michael@0 | 365 | nsAutoCString description; |
michael@0 | 366 | if (aSinkMask & TS_AS_TEXT_CHANGE) { |
michael@0 | 367 | description.AppendLiteral("TS_AS_TEXT_CHANGE"); |
michael@0 | 368 | } |
michael@0 | 369 | if (aSinkMask & TS_AS_SEL_CHANGE) { |
michael@0 | 370 | HandleSeparator(description); |
michael@0 | 371 | description.AppendLiteral("TS_AS_SEL_CHANGE"); |
michael@0 | 372 | } |
michael@0 | 373 | if (aSinkMask & TS_AS_LAYOUT_CHANGE) { |
michael@0 | 374 | HandleSeparator(description); |
michael@0 | 375 | description.AppendLiteral("TS_AS_LAYOUT_CHANGE"); |
michael@0 | 376 | } |
michael@0 | 377 | if (aSinkMask & TS_AS_ATTR_CHANGE) { |
michael@0 | 378 | HandleSeparator(description); |
michael@0 | 379 | description.AppendLiteral("TS_AS_ATTR_CHANGE"); |
michael@0 | 380 | } |
michael@0 | 381 | if (aSinkMask & TS_AS_STATUS_CHANGE) { |
michael@0 | 382 | HandleSeparator(description); |
michael@0 | 383 | description.AppendLiteral("TS_AS_STATUS_CHANGE"); |
michael@0 | 384 | } |
michael@0 | 385 | if (description.IsEmpty()) { |
michael@0 | 386 | description.AppendLiteral("not-specified"); |
michael@0 | 387 | } |
michael@0 | 388 | return description; |
michael@0 | 389 | } |
michael@0 | 390 | |
michael@0 | 391 | static const char* |
michael@0 | 392 | GetActiveSelEndName(TsActiveSelEnd aSelEnd) |
michael@0 | 393 | { |
michael@0 | 394 | return aSelEnd == TS_AE_NONE ? "TS_AE_NONE" : |
michael@0 | 395 | aSelEnd == TS_AE_START ? "TS_AE_START" : |
michael@0 | 396 | aSelEnd == TS_AE_END ? "TS_AE_END" : "Unknown"; |
michael@0 | 397 | } |
michael@0 | 398 | |
michael@0 | 399 | static const nsCString |
michael@0 | 400 | GetLockFlagNameStr(DWORD aLockFlags) |
michael@0 | 401 | { |
michael@0 | 402 | nsAutoCString description; |
michael@0 | 403 | if ((aLockFlags & TS_LF_READWRITE) == TS_LF_READWRITE) { |
michael@0 | 404 | description.AppendLiteral("TS_LF_READWRITE"); |
michael@0 | 405 | } else if (aLockFlags & TS_LF_READ) { |
michael@0 | 406 | description.AppendLiteral("TS_LF_READ"); |
michael@0 | 407 | } |
michael@0 | 408 | if (aLockFlags & TS_LF_SYNC) { |
michael@0 | 409 | if (!description.IsEmpty()) { |
michael@0 | 410 | description.AppendLiteral(" | "); |
michael@0 | 411 | } |
michael@0 | 412 | description.AppendLiteral("TS_LF_SYNC"); |
michael@0 | 413 | } |
michael@0 | 414 | if (description.IsEmpty()) { |
michael@0 | 415 | description.AppendLiteral("not-specified"); |
michael@0 | 416 | } |
michael@0 | 417 | return description; |
michael@0 | 418 | } |
michael@0 | 419 | |
michael@0 | 420 | static const char* |
michael@0 | 421 | GetTextRunTypeName(TsRunType aRunType) |
michael@0 | 422 | { |
michael@0 | 423 | switch (aRunType) { |
michael@0 | 424 | case TS_RT_PLAIN: |
michael@0 | 425 | return "TS_RT_PLAIN"; |
michael@0 | 426 | case TS_RT_HIDDEN: |
michael@0 | 427 | return "TS_RT_HIDDEN"; |
michael@0 | 428 | case TS_RT_OPAQUE: |
michael@0 | 429 | return "TS_RT_OPAQUE"; |
michael@0 | 430 | default: |
michael@0 | 431 | return "Unknown"; |
michael@0 | 432 | } |
michael@0 | 433 | } |
michael@0 | 434 | |
michael@0 | 435 | static nsCString |
michael@0 | 436 | GetColorName(const TF_DA_COLOR &aColor) |
michael@0 | 437 | { |
michael@0 | 438 | switch (aColor.type) { |
michael@0 | 439 | case TF_CT_NONE: |
michael@0 | 440 | return NS_LITERAL_CSTRING("TF_CT_NONE"); |
michael@0 | 441 | case TF_CT_SYSCOLOR: |
michael@0 | 442 | return nsPrintfCString("TF_CT_SYSCOLOR, nIndex:0x%08X", |
michael@0 | 443 | static_cast<int32_t>(aColor.nIndex)); |
michael@0 | 444 | case TF_CT_COLORREF: |
michael@0 | 445 | return nsPrintfCString("TF_CT_COLORREF, cr:0x%08X", |
michael@0 | 446 | static_cast<int32_t>(aColor.cr)); |
michael@0 | 447 | break; |
michael@0 | 448 | default: |
michael@0 | 449 | return nsPrintfCString("Unknown(%08X)", |
michael@0 | 450 | static_cast<int32_t>(aColor.type)); |
michael@0 | 451 | } |
michael@0 | 452 | } |
michael@0 | 453 | |
michael@0 | 454 | static nsCString |
michael@0 | 455 | GetLineStyleName(TF_DA_LINESTYLE aLineStyle) |
michael@0 | 456 | { |
michael@0 | 457 | switch (aLineStyle) { |
michael@0 | 458 | case TF_LS_NONE: |
michael@0 | 459 | return NS_LITERAL_CSTRING("TF_LS_NONE"); |
michael@0 | 460 | case TF_LS_SOLID: |
michael@0 | 461 | return NS_LITERAL_CSTRING("TF_LS_SOLID"); |
michael@0 | 462 | case TF_LS_DOT: |
michael@0 | 463 | return NS_LITERAL_CSTRING("TF_LS_DOT"); |
michael@0 | 464 | case TF_LS_DASH: |
michael@0 | 465 | return NS_LITERAL_CSTRING("TF_LS_DASH"); |
michael@0 | 466 | case TF_LS_SQUIGGLE: |
michael@0 | 467 | return NS_LITERAL_CSTRING("TF_LS_SQUIGGLE"); |
michael@0 | 468 | default: { |
michael@0 | 469 | return nsPrintfCString("Unknown(%08X)", static_cast<int32_t>(aLineStyle)); |
michael@0 | 470 | } |
michael@0 | 471 | } |
michael@0 | 472 | } |
michael@0 | 473 | |
michael@0 | 474 | static nsCString |
michael@0 | 475 | GetClauseAttrName(TF_DA_ATTR_INFO aAttr) |
michael@0 | 476 | { |
michael@0 | 477 | switch (aAttr) { |
michael@0 | 478 | case TF_ATTR_INPUT: |
michael@0 | 479 | return NS_LITERAL_CSTRING("TF_ATTR_INPUT"); |
michael@0 | 480 | case TF_ATTR_TARGET_CONVERTED: |
michael@0 | 481 | return NS_LITERAL_CSTRING("TF_ATTR_TARGET_CONVERTED"); |
michael@0 | 482 | case TF_ATTR_CONVERTED: |
michael@0 | 483 | return NS_LITERAL_CSTRING("TF_ATTR_CONVERTED"); |
michael@0 | 484 | case TF_ATTR_TARGET_NOTCONVERTED: |
michael@0 | 485 | return NS_LITERAL_CSTRING("TF_ATTR_TARGET_NOTCONVERTED"); |
michael@0 | 486 | case TF_ATTR_INPUT_ERROR: |
michael@0 | 487 | return NS_LITERAL_CSTRING("TF_ATTR_INPUT_ERROR"); |
michael@0 | 488 | case TF_ATTR_FIXEDCONVERTED: |
michael@0 | 489 | return NS_LITERAL_CSTRING("TF_ATTR_FIXEDCONVERTED"); |
michael@0 | 490 | case TF_ATTR_OTHER: |
michael@0 | 491 | return NS_LITERAL_CSTRING("TF_ATTR_OTHER"); |
michael@0 | 492 | default: { |
michael@0 | 493 | return nsPrintfCString("Unknown(%08X)", static_cast<int32_t>(aAttr)); |
michael@0 | 494 | } |
michael@0 | 495 | } |
michael@0 | 496 | } |
michael@0 | 497 | |
michael@0 | 498 | static nsCString |
michael@0 | 499 | GetDisplayAttrStr(const TF_DISPLAYATTRIBUTE &aDispAttr) |
michael@0 | 500 | { |
michael@0 | 501 | nsAutoCString str; |
michael@0 | 502 | str = "crText:{ "; |
michael@0 | 503 | str += GetColorName(aDispAttr.crText); |
michael@0 | 504 | str += " }, crBk:{ "; |
michael@0 | 505 | str += GetColorName(aDispAttr.crBk); |
michael@0 | 506 | str += " }, lsStyle: "; |
michael@0 | 507 | str += GetLineStyleName(aDispAttr.lsStyle); |
michael@0 | 508 | str += ", fBoldLine: "; |
michael@0 | 509 | str += GetBoolName(aDispAttr.fBoldLine); |
michael@0 | 510 | str += ", crLine:{ "; |
michael@0 | 511 | str += GetColorName(aDispAttr.crLine); |
michael@0 | 512 | str += " }, bAttr: "; |
michael@0 | 513 | str += GetClauseAttrName(aDispAttr.bAttr); |
michael@0 | 514 | return str; |
michael@0 | 515 | } |
michael@0 | 516 | |
michael@0 | 517 | #endif // #ifdef PR_LOGGING |
michael@0 | 518 | |
michael@0 | 519 | nsTextStore::nsTextStore() |
michael@0 | 520 | : mContent(mComposition, mSelection) |
michael@0 | 521 | { |
michael@0 | 522 | mRefCnt = 1; |
michael@0 | 523 | mEditCookie = 0; |
michael@0 | 524 | mIPProfileCookie = TF_INVALID_COOKIE; |
michael@0 | 525 | mLangProfileCookie = TF_INVALID_COOKIE; |
michael@0 | 526 | mSinkMask = 0; |
michael@0 | 527 | mLock = 0; |
michael@0 | 528 | mLockQueued = 0; |
michael@0 | 529 | mInputScopeDetected = false; |
michael@0 | 530 | mInputScopeRequested = false; |
michael@0 | 531 | mIsRecordingActionsWithoutLock = false; |
michael@0 | 532 | mPendingOnSelectionChange = false; |
michael@0 | 533 | mPendingOnLayoutChange = false; |
michael@0 | 534 | mNativeCaretIsCreated = false; |
michael@0 | 535 | mIsIMM_IME = false; |
michael@0 | 536 | mOnActivatedCalled = false; |
michael@0 | 537 | // We hope that 5 or more actions don't occur at once. |
michael@0 | 538 | mPendingActions.SetCapacity(5); |
michael@0 | 539 | |
michael@0 | 540 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 541 | ("TSF: 0x%p nsTextStore::nsTestStore() SUCCEEDED", this)); |
michael@0 | 542 | } |
michael@0 | 543 | |
michael@0 | 544 | bool |
michael@0 | 545 | nsTextStore::Init(ITfThreadMgr* aThreadMgr) |
michael@0 | 546 | { |
michael@0 | 547 | nsRefPtr<ITfSource> source; |
michael@0 | 548 | HRESULT hr = |
michael@0 | 549 | aThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source)); |
michael@0 | 550 | if (FAILED(hr)) { |
michael@0 | 551 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 552 | ("TSF: 0x%p nsTextStore::Init() FAILED to get ITfSource instance " |
michael@0 | 553 | "(0x%08X)", this, hr)); |
michael@0 | 554 | return false; |
michael@0 | 555 | } |
michael@0 | 556 | |
michael@0 | 557 | // On Vista or later, Windows let us know activate IME changed only with |
michael@0 | 558 | // ITfInputProcessorProfileActivationSink. However, it's not available on XP. |
michael@0 | 559 | // On XP, ITfActiveLanguageProfileNotifySink is available for it. |
michael@0 | 560 | // NOTE: Each OnActivated() should be called when TSF becomes available. |
michael@0 | 561 | if (IsVistaOrLater()) { |
michael@0 | 562 | hr = source->AdviseSink(IID_ITfInputProcessorProfileActivationSink, |
michael@0 | 563 | static_cast<ITfInputProcessorProfileActivationSink*>(this), |
michael@0 | 564 | &mIPProfileCookie); |
michael@0 | 565 | if (FAILED(hr) || mIPProfileCookie == TF_INVALID_COOKIE) { |
michael@0 | 566 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 567 | ("TSF: 0x%p nsTextStore::Init() FAILED to install " |
michael@0 | 568 | "ITfInputProcessorProfileActivationSink (0x%08X)", this, hr)); |
michael@0 | 569 | return false; |
michael@0 | 570 | } |
michael@0 | 571 | } else { |
michael@0 | 572 | hr = source->AdviseSink(IID_ITfActiveLanguageProfileNotifySink, |
michael@0 | 573 | static_cast<ITfActiveLanguageProfileNotifySink*>(this), |
michael@0 | 574 | &mLangProfileCookie); |
michael@0 | 575 | if (FAILED(hr) || mLangProfileCookie == TF_INVALID_COOKIE) { |
michael@0 | 576 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 577 | ("TSF: 0x%p nsTextStore::Init() FAILED to install " |
michael@0 | 578 | "ITfActiveLanguageProfileNotifySink (0x%08X)", this, hr)); |
michael@0 | 579 | return false; |
michael@0 | 580 | } |
michael@0 | 581 | } |
michael@0 | 582 | |
michael@0 | 583 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 584 | ("TSF: 0x%p nsTextStore::Init(), " |
michael@0 | 585 | "mIPProfileCookie=0x%08X, mLangProfileCookie=0x%08X", |
michael@0 | 586 | this, mIPProfileCookie, mLangProfileCookie)); |
michael@0 | 587 | |
michael@0 | 588 | return true; |
michael@0 | 589 | } |
michael@0 | 590 | |
michael@0 | 591 | nsTextStore::~nsTextStore() |
michael@0 | 592 | { |
michael@0 | 593 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 594 | ("TSF: 0x%p nsTextStore instance is destroyed, " |
michael@0 | 595 | "mWidget=0x%p, mDocumentMgr=0x%p, mContext=0x%p, mIPProfileCookie=0x%08X, " |
michael@0 | 596 | "mLangProfileCookie=0x%08X", |
michael@0 | 597 | this, mWidget.get(), mDocumentMgr.get(), mContext.get(), |
michael@0 | 598 | mIPProfileCookie, mLangProfileCookie)); |
michael@0 | 599 | |
michael@0 | 600 | if (mIPProfileCookie != TF_INVALID_COOKIE) { |
michael@0 | 601 | nsRefPtr<ITfSource> source; |
michael@0 | 602 | HRESULT hr = |
michael@0 | 603 | sTsfThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source)); |
michael@0 | 604 | if (FAILED(hr)) { |
michael@0 | 605 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 606 | ("TSF: 0x%p ~nsTextStore FAILED to get ITfSource instance " |
michael@0 | 607 | "(0x%08X)", this, hr)); |
michael@0 | 608 | } else { |
michael@0 | 609 | hr = source->UnadviseSink(mIPProfileCookie); |
michael@0 | 610 | if (FAILED(hr)) { |
michael@0 | 611 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 612 | ("TSF: 0x%p ~nsTextStore FAILED to uninstall " |
michael@0 | 613 | "ITfInputProcessorProfileActivationSink (0x%08X)", |
michael@0 | 614 | this, hr)); |
michael@0 | 615 | } |
michael@0 | 616 | } |
michael@0 | 617 | } |
michael@0 | 618 | |
michael@0 | 619 | if (mLangProfileCookie != TF_INVALID_COOKIE) { |
michael@0 | 620 | nsRefPtr<ITfSource> source; |
michael@0 | 621 | HRESULT hr = |
michael@0 | 622 | sTsfThreadMgr->QueryInterface(IID_ITfSource, getter_AddRefs(source)); |
michael@0 | 623 | if (FAILED(hr)) { |
michael@0 | 624 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 625 | ("TSF: 0x%p ~nsTextStore FAILED to get ITfSource instance " |
michael@0 | 626 | "(0x%08X)", this, hr)); |
michael@0 | 627 | } else { |
michael@0 | 628 | hr = source->UnadviseSink(mLangProfileCookie); |
michael@0 | 629 | if (FAILED(hr)) { |
michael@0 | 630 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 631 | ("TSF: 0x%p ~nsTextStore FAILED to uninstall " |
michael@0 | 632 | "ITfActiveLanguageProfileNotifySink (0x%08X)", |
michael@0 | 633 | this, hr)); |
michael@0 | 634 | } |
michael@0 | 635 | } |
michael@0 | 636 | } |
michael@0 | 637 | } |
michael@0 | 638 | |
michael@0 | 639 | bool |
michael@0 | 640 | nsTextStore::Create(nsWindowBase* aWidget) |
michael@0 | 641 | { |
michael@0 | 642 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 643 | ("TSF: 0x%p nsTextStore::Create(aWidget=0x%p)", |
michael@0 | 644 | this, aWidget)); |
michael@0 | 645 | |
michael@0 | 646 | EnsureInitActiveTIPKeyboard(); |
michael@0 | 647 | |
michael@0 | 648 | if (mDocumentMgr) { |
michael@0 | 649 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 650 | ("TSF: 0x%p nsTextStore::Create() FAILED due to already initialized", |
michael@0 | 651 | this)); |
michael@0 | 652 | return false; |
michael@0 | 653 | } |
michael@0 | 654 | |
michael@0 | 655 | // Create document manager |
michael@0 | 656 | HRESULT hr = sTsfThreadMgr->CreateDocumentMgr( |
michael@0 | 657 | getter_AddRefs(mDocumentMgr)); |
michael@0 | 658 | if (FAILED(hr)) { |
michael@0 | 659 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 660 | ("TSF: 0x%p nsTextStore::Create() FAILED to create DocumentMgr " |
michael@0 | 661 | "(0x%08X)", this, hr)); |
michael@0 | 662 | return false; |
michael@0 | 663 | } |
michael@0 | 664 | mWidget = aWidget; |
michael@0 | 665 | |
michael@0 | 666 | // Create context and add it to document manager |
michael@0 | 667 | hr = mDocumentMgr->CreateContext(sTsfClientId, 0, |
michael@0 | 668 | static_cast<ITextStoreACP*>(this), |
michael@0 | 669 | getter_AddRefs(mContext), &mEditCookie); |
michael@0 | 670 | if (FAILED(hr)) { |
michael@0 | 671 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 672 | ("TSF: 0x%p nsTextStore::Create() FAILED to create the context " |
michael@0 | 673 | "(0x%08X)", this, hr)); |
michael@0 | 674 | mDocumentMgr = nullptr; |
michael@0 | 675 | return false; |
michael@0 | 676 | } |
michael@0 | 677 | |
michael@0 | 678 | hr = mDocumentMgr->Push(mContext); |
michael@0 | 679 | if (FAILED(hr)) { |
michael@0 | 680 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 681 | ("TSF: 0x%p nsTextStore::Create() FAILED to push the context (0x%08X)", |
michael@0 | 682 | this, hr)); |
michael@0 | 683 | // XXX Why don't we use NS_IF_RELEASE() here?? |
michael@0 | 684 | mContext = nullptr; |
michael@0 | 685 | mDocumentMgr = nullptr; |
michael@0 | 686 | return false; |
michael@0 | 687 | } |
michael@0 | 688 | |
michael@0 | 689 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 690 | ("TSF: 0x%p nsTextStore::Create() succeeded: " |
michael@0 | 691 | "mDocumentMgr=0x%p, mContext=0x%p, mEditCookie=0x%08X", |
michael@0 | 692 | this, mDocumentMgr.get(), mContext.get(), mEditCookie)); |
michael@0 | 693 | |
michael@0 | 694 | return true; |
michael@0 | 695 | } |
michael@0 | 696 | |
michael@0 | 697 | bool |
michael@0 | 698 | nsTextStore::Destroy(void) |
michael@0 | 699 | { |
michael@0 | 700 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 701 | ("TSF: 0x%p nsTextStore::Destroy(), mComposition.IsComposing()=%s", |
michael@0 | 702 | this, GetBoolName(mComposition.IsComposing()))); |
michael@0 | 703 | |
michael@0 | 704 | // If there is composition, TSF keeps the composition even after the text |
michael@0 | 705 | // store destroyed. So, we should clear the composition here. |
michael@0 | 706 | if (mComposition.IsComposing()) { |
michael@0 | 707 | NS_WARNING("Composition is still alive at destroying the text store"); |
michael@0 | 708 | CommitCompositionInternal(false); |
michael@0 | 709 | } |
michael@0 | 710 | |
michael@0 | 711 | mContent.Clear(); |
michael@0 | 712 | mSelection.MarkDirty(); |
michael@0 | 713 | |
michael@0 | 714 | if (mWidget) { |
michael@0 | 715 | // When blurred, Tablet Input Panel posts "blur" messages |
michael@0 | 716 | // and try to insert text when the message is retrieved later. |
michael@0 | 717 | // But by that time the text store is already destroyed, |
michael@0 | 718 | // so try to get the message early |
michael@0 | 719 | MSG msg; |
michael@0 | 720 | if (WinUtils::PeekMessage(&msg, mWidget->GetWindowHandle(), |
michael@0 | 721 | sFlushTIPInputMessage, sFlushTIPInputMessage, |
michael@0 | 722 | PM_REMOVE)) { |
michael@0 | 723 | ::DispatchMessageW(&msg); |
michael@0 | 724 | } |
michael@0 | 725 | } |
michael@0 | 726 | mContext = nullptr; |
michael@0 | 727 | if (mDocumentMgr) { |
michael@0 | 728 | mDocumentMgr->Pop(TF_POPF_ALL); |
michael@0 | 729 | mDocumentMgr = nullptr; |
michael@0 | 730 | } |
michael@0 | 731 | mSink = nullptr; |
michael@0 | 732 | mWidget = nullptr; |
michael@0 | 733 | |
michael@0 | 734 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 735 | ("TSF: 0x%p nsTextStore::Destroy() succeeded", this)); |
michael@0 | 736 | return true; |
michael@0 | 737 | } |
michael@0 | 738 | |
michael@0 | 739 | STDMETHODIMP |
michael@0 | 740 | nsTextStore::QueryInterface(REFIID riid, |
michael@0 | 741 | void** ppv) |
michael@0 | 742 | { |
michael@0 | 743 | *ppv=nullptr; |
michael@0 | 744 | if ( (IID_IUnknown == riid) || (IID_ITextStoreACP == riid) ) { |
michael@0 | 745 | *ppv = static_cast<ITextStoreACP*>(this); |
michael@0 | 746 | } else if (IID_ITfContextOwnerCompositionSink == riid) { |
michael@0 | 747 | *ppv = static_cast<ITfContextOwnerCompositionSink*>(this); |
michael@0 | 748 | } else if (IID_ITfActiveLanguageProfileNotifySink == riid) { |
michael@0 | 749 | *ppv = static_cast<ITfActiveLanguageProfileNotifySink*>(this); |
michael@0 | 750 | } else if (IID_ITfInputProcessorProfileActivationSink == riid) { |
michael@0 | 751 | *ppv = static_cast<ITfInputProcessorProfileActivationSink*>(this); |
michael@0 | 752 | } |
michael@0 | 753 | if (*ppv) { |
michael@0 | 754 | AddRef(); |
michael@0 | 755 | return S_OK; |
michael@0 | 756 | } |
michael@0 | 757 | |
michael@0 | 758 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 759 | ("TSF: 0x%p nsTextStore::QueryInterface() FAILED, riid=%s", |
michael@0 | 760 | this, GetRIIDNameStr(riid).get())); |
michael@0 | 761 | return E_NOINTERFACE; |
michael@0 | 762 | } |
michael@0 | 763 | |
michael@0 | 764 | STDMETHODIMP_(ULONG) nsTextStore::AddRef() |
michael@0 | 765 | { |
michael@0 | 766 | return ++mRefCnt; |
michael@0 | 767 | } |
michael@0 | 768 | |
michael@0 | 769 | STDMETHODIMP_(ULONG) nsTextStore::Release() |
michael@0 | 770 | { |
michael@0 | 771 | --mRefCnt; |
michael@0 | 772 | if (0 != mRefCnt) |
michael@0 | 773 | return mRefCnt; |
michael@0 | 774 | delete this; |
michael@0 | 775 | return 0; |
michael@0 | 776 | } |
michael@0 | 777 | |
michael@0 | 778 | STDMETHODIMP |
michael@0 | 779 | nsTextStore::AdviseSink(REFIID riid, |
michael@0 | 780 | IUnknown *punk, |
michael@0 | 781 | DWORD dwMask) |
michael@0 | 782 | { |
michael@0 | 783 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 784 | ("TSF: 0x%p nsTextStore::AdviseSink(riid=%s, punk=0x%p, dwMask=%s), " |
michael@0 | 785 | "mSink=0x%p, mSinkMask=%s", |
michael@0 | 786 | this, GetRIIDNameStr(riid).get(), punk, GetSinkMaskNameStr(dwMask).get(), |
michael@0 | 787 | mSink.get(), GetSinkMaskNameStr(mSinkMask).get())); |
michael@0 | 788 | |
michael@0 | 789 | if (!punk) { |
michael@0 | 790 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 791 | ("TSF: 0x%p nsTextStore::AdviseSink() FAILED due to the null punk", |
michael@0 | 792 | this)); |
michael@0 | 793 | return E_UNEXPECTED; |
michael@0 | 794 | } |
michael@0 | 795 | |
michael@0 | 796 | if (IID_ITextStoreACPSink != riid) { |
michael@0 | 797 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 798 | ("TSF: 0x%p nsTextStore::AdviseSink() FAILED due to " |
michael@0 | 799 | "unsupported interface", this)); |
michael@0 | 800 | return E_INVALIDARG; // means unsupported interface. |
michael@0 | 801 | } |
michael@0 | 802 | |
michael@0 | 803 | if (!mSink) { |
michael@0 | 804 | // Install sink |
michael@0 | 805 | punk->QueryInterface(IID_ITextStoreACPSink, getter_AddRefs(mSink)); |
michael@0 | 806 | if (!mSink) { |
michael@0 | 807 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 808 | ("TSF: 0x%p nsTextStore::AdviseSink() FAILED due to " |
michael@0 | 809 | "punk not having the interface", this)); |
michael@0 | 810 | return E_UNEXPECTED; |
michael@0 | 811 | } |
michael@0 | 812 | } else { |
michael@0 | 813 | // If sink is already installed we check to see if they are the same |
michael@0 | 814 | // Get IUnknown from both sides for comparison |
michael@0 | 815 | nsRefPtr<IUnknown> comparison1, comparison2; |
michael@0 | 816 | punk->QueryInterface(IID_IUnknown, getter_AddRefs(comparison1)); |
michael@0 | 817 | mSink->QueryInterface(IID_IUnknown, getter_AddRefs(comparison2)); |
michael@0 | 818 | if (comparison1 != comparison2) { |
michael@0 | 819 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 820 | ("TSF: 0x%p nsTextStore::AdviseSink() FAILED due to " |
michael@0 | 821 | "the sink being different from the stored sink", this)); |
michael@0 | 822 | return CONNECT_E_ADVISELIMIT; |
michael@0 | 823 | } |
michael@0 | 824 | } |
michael@0 | 825 | // Update mask either for a new sink or an existing sink |
michael@0 | 826 | mSinkMask = dwMask; |
michael@0 | 827 | return S_OK; |
michael@0 | 828 | } |
michael@0 | 829 | |
michael@0 | 830 | STDMETHODIMP |
michael@0 | 831 | nsTextStore::UnadviseSink(IUnknown *punk) |
michael@0 | 832 | { |
michael@0 | 833 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 834 | ("TSF: 0x%p nsTextStore::UnadviseSink(punk=0x%p), mSink=0x%p", |
michael@0 | 835 | this, punk, mSink.get())); |
michael@0 | 836 | |
michael@0 | 837 | if (!punk) { |
michael@0 | 838 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 839 | ("TSF: 0x%p nsTextStore::UnadviseSink() FAILED due to the null punk", |
michael@0 | 840 | this)); |
michael@0 | 841 | return E_INVALIDARG; |
michael@0 | 842 | } |
michael@0 | 843 | if (!mSink) { |
michael@0 | 844 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 845 | ("TSF: 0x%p nsTextStore::UnadviseSink() FAILED due to " |
michael@0 | 846 | "any sink not stored", this)); |
michael@0 | 847 | return CONNECT_E_NOCONNECTION; |
michael@0 | 848 | } |
michael@0 | 849 | // Get IUnknown from both sides for comparison |
michael@0 | 850 | nsRefPtr<IUnknown> comparison1, comparison2; |
michael@0 | 851 | punk->QueryInterface(IID_IUnknown, getter_AddRefs(comparison1)); |
michael@0 | 852 | mSink->QueryInterface(IID_IUnknown, getter_AddRefs(comparison2)); |
michael@0 | 853 | // Unadvise only if sinks are the same |
michael@0 | 854 | if (comparison1 != comparison2) { |
michael@0 | 855 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 856 | ("TSF: 0x%p nsTextStore::UnadviseSink() FAILED due to " |
michael@0 | 857 | "the sink being different from the stored sink", this)); |
michael@0 | 858 | return CONNECT_E_NOCONNECTION; |
michael@0 | 859 | } |
michael@0 | 860 | mSink = nullptr; |
michael@0 | 861 | mSinkMask = 0; |
michael@0 | 862 | return S_OK; |
michael@0 | 863 | } |
michael@0 | 864 | |
michael@0 | 865 | STDMETHODIMP |
michael@0 | 866 | nsTextStore::RequestLock(DWORD dwLockFlags, |
michael@0 | 867 | HRESULT *phrSession) |
michael@0 | 868 | { |
michael@0 | 869 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 870 | ("TSF: 0x%p nsTextStore::RequestLock(dwLockFlags=%s, phrSession=0x%p), " |
michael@0 | 871 | "mLock=%s", this, GetLockFlagNameStr(dwLockFlags).get(), phrSession, |
michael@0 | 872 | GetLockFlagNameStr(mLock).get())); |
michael@0 | 873 | |
michael@0 | 874 | if (!mSink) { |
michael@0 | 875 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 876 | ("TSF: 0x%p nsTextStore::RequestLock() FAILED due to " |
michael@0 | 877 | "any sink not stored", this)); |
michael@0 | 878 | return E_FAIL; |
michael@0 | 879 | } |
michael@0 | 880 | if (!phrSession) { |
michael@0 | 881 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 882 | ("TSF: 0x%p nsTextStore::RequestLock() FAILED due to " |
michael@0 | 883 | "null phrSession", this)); |
michael@0 | 884 | return E_INVALIDARG; |
michael@0 | 885 | } |
michael@0 | 886 | |
michael@0 | 887 | if (!mLock) { |
michael@0 | 888 | // put on lock |
michael@0 | 889 | mLock = dwLockFlags & (~TS_LF_SYNC); |
michael@0 | 890 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 891 | ("TSF: 0x%p Locking (%s) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" |
michael@0 | 892 | ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", |
michael@0 | 893 | this, GetLockFlagNameStr(mLock).get())); |
michael@0 | 894 | *phrSession = mSink->OnLockGranted(mLock); |
michael@0 | 895 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 896 | ("TSF: 0x%p Unlocked (%s) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" |
michael@0 | 897 | "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", |
michael@0 | 898 | this, GetLockFlagNameStr(mLock).get())); |
michael@0 | 899 | DidLockGranted(); |
michael@0 | 900 | while (mLockQueued) { |
michael@0 | 901 | mLock = mLockQueued; |
michael@0 | 902 | mLockQueued = 0; |
michael@0 | 903 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 904 | ("TSF: 0x%p Locking for the request in the queue (%s) >>>>>>>>>>>>>>" |
michael@0 | 905 | ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", |
michael@0 | 906 | this, GetLockFlagNameStr(mLock).get())); |
michael@0 | 907 | mSink->OnLockGranted(mLock); |
michael@0 | 908 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 909 | ("TSF: 0x%p Unlocked (%s) <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" |
michael@0 | 910 | "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", |
michael@0 | 911 | this, GetLockFlagNameStr(mLock).get())); |
michael@0 | 912 | DidLockGranted(); |
michael@0 | 913 | } |
michael@0 | 914 | |
michael@0 | 915 | // The document is now completely unlocked. |
michael@0 | 916 | mLock = 0; |
michael@0 | 917 | |
michael@0 | 918 | if (mPendingOnLayoutChange) { |
michael@0 | 919 | mPendingOnLayoutChange = false; |
michael@0 | 920 | if (mSink) { |
michael@0 | 921 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 922 | ("TSF: 0x%p nsTextStore::RequestLock(), " |
michael@0 | 923 | "calling ITextStoreACPSink::OnLayoutChange()...", this)); |
michael@0 | 924 | mSink->OnLayoutChange(TS_LC_CHANGE, TEXTSTORE_DEFAULT_VIEW); |
michael@0 | 925 | } |
michael@0 | 926 | // The layout change caused by composition string change should cause |
michael@0 | 927 | // calling ITfContextOwnerServices::OnLayoutChange() too. |
michael@0 | 928 | if (mContext) { |
michael@0 | 929 | nsRefPtr<ITfContextOwnerServices> service; |
michael@0 | 930 | mContext->QueryInterface(IID_ITfContextOwnerServices, |
michael@0 | 931 | getter_AddRefs(service)); |
michael@0 | 932 | if (service) { |
michael@0 | 933 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 934 | ("TSF: 0x%p nsTextStore::RequestLock(), " |
michael@0 | 935 | "calling ITfContextOwnerServices::OnLayoutChange()...", |
michael@0 | 936 | this)); |
michael@0 | 937 | service->OnLayoutChange(); |
michael@0 | 938 | } |
michael@0 | 939 | } |
michael@0 | 940 | } |
michael@0 | 941 | |
michael@0 | 942 | if (mPendingOnSelectionChange) { |
michael@0 | 943 | mPendingOnSelectionChange = false; |
michael@0 | 944 | if (mSink) { |
michael@0 | 945 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 946 | ("TSF: 0x%p nsTextStore::RequestLock(), " |
michael@0 | 947 | "calling ITextStoreACPSink::OnSelectionChange()...", this)); |
michael@0 | 948 | mSink->OnSelectionChange(); |
michael@0 | 949 | } |
michael@0 | 950 | } |
michael@0 | 951 | |
michael@0 | 952 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 953 | ("TSF: 0x%p nsTextStore::RequestLock() succeeded: *phrSession=%s", |
michael@0 | 954 | this, GetTextStoreReturnValueName(*phrSession))); |
michael@0 | 955 | return S_OK; |
michael@0 | 956 | } |
michael@0 | 957 | |
michael@0 | 958 | // only time when reentrant lock is allowed is when caller holds a |
michael@0 | 959 | // read-only lock and is requesting an async write lock |
michael@0 | 960 | if (IsReadLocked() && !IsReadWriteLocked() && IsReadWriteLock(dwLockFlags) && |
michael@0 | 961 | !(dwLockFlags & TS_LF_SYNC)) { |
michael@0 | 962 | *phrSession = TS_S_ASYNC; |
michael@0 | 963 | mLockQueued = dwLockFlags & (~TS_LF_SYNC); |
michael@0 | 964 | |
michael@0 | 965 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 966 | ("TSF: 0x%p nsTextStore::RequestLock() stores the request in the " |
michael@0 | 967 | "queue, *phrSession=TS_S_ASYNC", this)); |
michael@0 | 968 | return S_OK; |
michael@0 | 969 | } |
michael@0 | 970 | |
michael@0 | 971 | // no more locks allowed |
michael@0 | 972 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 973 | ("TSF: 0x%p nsTextStore::RequestLock() didn't allow to lock, " |
michael@0 | 974 | "*phrSession=TS_E_SYNCHRONOUS", this)); |
michael@0 | 975 | *phrSession = TS_E_SYNCHRONOUS; |
michael@0 | 976 | return E_FAIL; |
michael@0 | 977 | } |
michael@0 | 978 | |
michael@0 | 979 | void |
michael@0 | 980 | nsTextStore::DidLockGranted() |
michael@0 | 981 | { |
michael@0 | 982 | if (mNativeCaretIsCreated) { |
michael@0 | 983 | ::DestroyCaret(); |
michael@0 | 984 | mNativeCaretIsCreated = false; |
michael@0 | 985 | } |
michael@0 | 986 | if (IsReadWriteLocked()) { |
michael@0 | 987 | FlushPendingActions(); |
michael@0 | 988 | } |
michael@0 | 989 | |
michael@0 | 990 | // If the widget has gone, we don't need to notify anything. |
michael@0 | 991 | if (!mWidget || mWidget->Destroyed()) { |
michael@0 | 992 | mPendingOnSelectionChange = false; |
michael@0 | 993 | mPendingOnLayoutChange = false; |
michael@0 | 994 | } |
michael@0 | 995 | } |
michael@0 | 996 | |
michael@0 | 997 | void |
michael@0 | 998 | nsTextStore::FlushPendingActions() |
michael@0 | 999 | { |
michael@0 | 1000 | if (!mWidget || mWidget->Destroyed()) { |
michael@0 | 1001 | mPendingActions.Clear(); |
michael@0 | 1002 | mContent.Clear(); |
michael@0 | 1003 | mPendingOnSelectionChange = false; |
michael@0 | 1004 | mPendingOnLayoutChange = false; |
michael@0 | 1005 | return; |
michael@0 | 1006 | } |
michael@0 | 1007 | |
michael@0 | 1008 | mContent.Clear(); |
michael@0 | 1009 | |
michael@0 | 1010 | nsRefPtr<nsWindowBase> kungFuDeathGrip(mWidget); |
michael@0 | 1011 | for (uint32_t i = 0; i < mPendingActions.Length(); i++) { |
michael@0 | 1012 | PendingAction& action = mPendingActions[i]; |
michael@0 | 1013 | switch (action.mType) { |
michael@0 | 1014 | case PendingAction::COMPOSITION_START: { |
michael@0 | 1015 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 1016 | ("TSF: 0x%p nsTextStore::FlushPendingActions() " |
michael@0 | 1017 | "flushing COMPOSITION_START={ mSelectionStart=%d, " |
michael@0 | 1018 | "mSelectionLength=%d }", |
michael@0 | 1019 | this, action.mSelectionStart, action.mSelectionLength)); |
michael@0 | 1020 | |
michael@0 | 1021 | MOZ_ASSERT(mComposition.mLastData.IsEmpty()); |
michael@0 | 1022 | |
michael@0 | 1023 | // Select composition range so the new composition replaces the range |
michael@0 | 1024 | WidgetSelectionEvent selectionSet(true, NS_SELECTION_SET, mWidget); |
michael@0 | 1025 | mWidget->InitEvent(selectionSet); |
michael@0 | 1026 | selectionSet.mOffset = static_cast<uint32_t>(action.mSelectionStart); |
michael@0 | 1027 | selectionSet.mLength = static_cast<uint32_t>(action.mSelectionLength); |
michael@0 | 1028 | selectionSet.mReversed = false; |
michael@0 | 1029 | mWidget->DispatchWindowEvent(&selectionSet); |
michael@0 | 1030 | if (!selectionSet.mSucceeded) { |
michael@0 | 1031 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1032 | ("TSF: 0x%p nsTextStore::FlushPendingActions() " |
michael@0 | 1033 | "FAILED due to NS_SELECTION_SET failure", this)); |
michael@0 | 1034 | break; |
michael@0 | 1035 | } |
michael@0 | 1036 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 1037 | ("TSF: 0x%p nsTextStore::FlushPendingActions() " |
michael@0 | 1038 | "dispatching compositionstart event...", this)); |
michael@0 | 1039 | WidgetCompositionEvent compositionStart(true, NS_COMPOSITION_START, |
michael@0 | 1040 | mWidget); |
michael@0 | 1041 | mWidget->InitEvent(compositionStart); |
michael@0 | 1042 | mWidget->DispatchWindowEvent(&compositionStart); |
michael@0 | 1043 | if (!mWidget || mWidget->Destroyed()) { |
michael@0 | 1044 | break; |
michael@0 | 1045 | } |
michael@0 | 1046 | break; |
michael@0 | 1047 | } |
michael@0 | 1048 | case PendingAction::COMPOSITION_UPDATE: { |
michael@0 | 1049 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 1050 | ("TSF: 0x%p nsTextStore::FlushPendingActions() " |
michael@0 | 1051 | "flushing COMPOSITION_UPDATE={ mData=\"%s\", " |
michael@0 | 1052 | "mRanges=0x%p, mRanges->Length()=%d }", |
michael@0 | 1053 | this, NS_ConvertUTF16toUTF8(action.mData).get(), action.mRanges.get(), |
michael@0 | 1054 | action.mRanges ? action.mRanges->Length() : 0)); |
michael@0 | 1055 | |
michael@0 | 1056 | if (!action.mRanges) { |
michael@0 | 1057 | NS_WARNING("How does this case occur?"); |
michael@0 | 1058 | action.mRanges = new TextRangeArray(); |
michael@0 | 1059 | } |
michael@0 | 1060 | |
michael@0 | 1061 | // Adjust offsets in the ranges for XP linefeed character (only \n). |
michael@0 | 1062 | // XXX Following code is the safest approach. However, it wastes |
michael@0 | 1063 | // a little performance. For ensuring the clauses do not |
michael@0 | 1064 | // overlap each other, we should redesign TextRange later. |
michael@0 | 1065 | for (uint32_t i = 0; i < action.mRanges->Length(); ++i) { |
michael@0 | 1066 | TextRange& range = action.mRanges->ElementAt(i); |
michael@0 | 1067 | TextRange nativeRange = range; |
michael@0 | 1068 | if (nativeRange.mStartOffset > 0) { |
michael@0 | 1069 | nsAutoString preText( |
michael@0 | 1070 | Substring(action.mData, 0, nativeRange.mStartOffset)); |
michael@0 | 1071 | preText.ReplaceSubstring(NS_LITERAL_STRING("\r\n"), |
michael@0 | 1072 | NS_LITERAL_STRING("\n")); |
michael@0 | 1073 | range.mStartOffset = preText.Length(); |
michael@0 | 1074 | } |
michael@0 | 1075 | if (nativeRange.Length() == 0) { |
michael@0 | 1076 | range.mEndOffset = range.mStartOffset; |
michael@0 | 1077 | } else { |
michael@0 | 1078 | nsAutoString clause( |
michael@0 | 1079 | Substring(action.mData, |
michael@0 | 1080 | nativeRange.mStartOffset, nativeRange.Length())); |
michael@0 | 1081 | clause.ReplaceSubstring(NS_LITERAL_STRING("\r\n"), |
michael@0 | 1082 | NS_LITERAL_STRING("\n")); |
michael@0 | 1083 | range.mEndOffset = range.mStartOffset + clause.Length(); |
michael@0 | 1084 | } |
michael@0 | 1085 | } |
michael@0 | 1086 | |
michael@0 | 1087 | action.mData.ReplaceSubstring(NS_LITERAL_STRING("\r\n"), |
michael@0 | 1088 | NS_LITERAL_STRING("\n")); |
michael@0 | 1089 | |
michael@0 | 1090 | if (action.mData != mComposition.mLastData) { |
michael@0 | 1091 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 1092 | ("TSF: 0x%p nsTextStore::FlushPendingActions(), " |
michael@0 | 1093 | "dispatching compositionupdate event...", this)); |
michael@0 | 1094 | WidgetCompositionEvent compositionUpdate(true, NS_COMPOSITION_UPDATE, |
michael@0 | 1095 | mWidget); |
michael@0 | 1096 | mWidget->InitEvent(compositionUpdate); |
michael@0 | 1097 | compositionUpdate.data = action.mData; |
michael@0 | 1098 | mComposition.mLastData = compositionUpdate.data; |
michael@0 | 1099 | mWidget->DispatchWindowEvent(&compositionUpdate); |
michael@0 | 1100 | if (!mWidget || mWidget->Destroyed()) { |
michael@0 | 1101 | break; |
michael@0 | 1102 | } |
michael@0 | 1103 | } |
michael@0 | 1104 | |
michael@0 | 1105 | MOZ_ASSERT(action.mData == mComposition.mLastData); |
michael@0 | 1106 | |
michael@0 | 1107 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 1108 | ("TSF: 0x%p nsTextStore::FlushPendingActions(), " |
michael@0 | 1109 | "dispatching text event...", this)); |
michael@0 | 1110 | WidgetTextEvent textEvent(true, NS_TEXT_TEXT, mWidget); |
michael@0 | 1111 | mWidget->InitEvent(textEvent); |
michael@0 | 1112 | textEvent.theText = mComposition.mLastData; |
michael@0 | 1113 | if (action.mRanges->IsEmpty()) { |
michael@0 | 1114 | TextRange wholeRange; |
michael@0 | 1115 | wholeRange.mStartOffset = 0; |
michael@0 | 1116 | wholeRange.mEndOffset = textEvent.theText.Length(); |
michael@0 | 1117 | wholeRange.mRangeType = NS_TEXTRANGE_RAWINPUT; |
michael@0 | 1118 | action.mRanges->AppendElement(wholeRange); |
michael@0 | 1119 | } |
michael@0 | 1120 | textEvent.mRanges = action.mRanges; |
michael@0 | 1121 | mWidget->DispatchWindowEvent(&textEvent); |
michael@0 | 1122 | // Be aware, the mWidget might already have been destroyed. |
michael@0 | 1123 | break; |
michael@0 | 1124 | } |
michael@0 | 1125 | case PendingAction::COMPOSITION_END: { |
michael@0 | 1126 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 1127 | ("TSF: 0x%p nsTextStore::FlushPendingActions() " |
michael@0 | 1128 | "flushing COMPOSITION_END={ mData=\"%s\" }", |
michael@0 | 1129 | this, NS_ConvertUTF16toUTF8(action.mData).get())); |
michael@0 | 1130 | |
michael@0 | 1131 | action.mData.ReplaceSubstring(NS_LITERAL_STRING("\r\n"), |
michael@0 | 1132 | NS_LITERAL_STRING("\n")); |
michael@0 | 1133 | if (action.mData != mComposition.mLastData) { |
michael@0 | 1134 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 1135 | ("TSF: 0x%p nsTextStore::FlushPendingActions(), " |
michael@0 | 1136 | "dispatching compositionupdate event...", this)); |
michael@0 | 1137 | WidgetCompositionEvent compositionUpdate(true, NS_COMPOSITION_UPDATE, |
michael@0 | 1138 | mWidget); |
michael@0 | 1139 | mWidget->InitEvent(compositionUpdate); |
michael@0 | 1140 | compositionUpdate.data = action.mData; |
michael@0 | 1141 | mComposition.mLastData = compositionUpdate.data; |
michael@0 | 1142 | mWidget->DispatchWindowEvent(&compositionUpdate); |
michael@0 | 1143 | if (!mWidget || mWidget->Destroyed()) { |
michael@0 | 1144 | break; |
michael@0 | 1145 | } |
michael@0 | 1146 | } |
michael@0 | 1147 | |
michael@0 | 1148 | MOZ_ASSERT(action.mData == mComposition.mLastData); |
michael@0 | 1149 | |
michael@0 | 1150 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 1151 | ("TSF: 0x%p nsTextStore::FlushPendingActions(), " |
michael@0 | 1152 | "dispatching text event...", this)); |
michael@0 | 1153 | WidgetTextEvent textEvent(true, NS_TEXT_TEXT, mWidget); |
michael@0 | 1154 | mWidget->InitEvent(textEvent); |
michael@0 | 1155 | textEvent.theText = mComposition.mLastData; |
michael@0 | 1156 | mWidget->DispatchWindowEvent(&textEvent); |
michael@0 | 1157 | if (!mWidget || mWidget->Destroyed()) { |
michael@0 | 1158 | break; |
michael@0 | 1159 | } |
michael@0 | 1160 | |
michael@0 | 1161 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 1162 | ("TSF: 0x%p nsTextStore::FlushPendingActions(), " |
michael@0 | 1163 | "dispatching compositionend event...", this)); |
michael@0 | 1164 | WidgetCompositionEvent compositionEnd(true, NS_COMPOSITION_END, |
michael@0 | 1165 | mWidget); |
michael@0 | 1166 | compositionEnd.data = mComposition.mLastData; |
michael@0 | 1167 | mWidget->InitEvent(compositionEnd); |
michael@0 | 1168 | mWidget->DispatchWindowEvent(&compositionEnd); |
michael@0 | 1169 | if (!mWidget || mWidget->Destroyed()) { |
michael@0 | 1170 | break; |
michael@0 | 1171 | } |
michael@0 | 1172 | mComposition.mLastData.Truncate(); |
michael@0 | 1173 | break; |
michael@0 | 1174 | } |
michael@0 | 1175 | case PendingAction::SELECTION_SET: { |
michael@0 | 1176 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 1177 | ("TSF: 0x%p nsTextStore::FlushPendingActions() " |
michael@0 | 1178 | "flushing SELECTION_SET={ mSelectionStart=%d, " |
michael@0 | 1179 | "mSelectionLength=%d, mSelectionReversed=%s }", |
michael@0 | 1180 | this, action.mSelectionStart, action.mSelectionLength, |
michael@0 | 1181 | GetBoolName(action.mSelectionReversed))); |
michael@0 | 1182 | |
michael@0 | 1183 | WidgetSelectionEvent selectionSet(true, NS_SELECTION_SET, mWidget); |
michael@0 | 1184 | selectionSet.mOffset = |
michael@0 | 1185 | static_cast<uint32_t>(action.mSelectionStart); |
michael@0 | 1186 | selectionSet.mLength = |
michael@0 | 1187 | static_cast<uint32_t>(action.mSelectionLength); |
michael@0 | 1188 | selectionSet.mReversed = action.mSelectionReversed; |
michael@0 | 1189 | break; |
michael@0 | 1190 | } |
michael@0 | 1191 | default: |
michael@0 | 1192 | MOZ_CRASH("unexpected action type"); |
michael@0 | 1193 | } |
michael@0 | 1194 | |
michael@0 | 1195 | if (mWidget && !mWidget->Destroyed()) { |
michael@0 | 1196 | continue; |
michael@0 | 1197 | } |
michael@0 | 1198 | |
michael@0 | 1199 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 1200 | ("TSF: 0x%p nsTextStore::FlushPendingActions(), " |
michael@0 | 1201 | "qutting since the mWidget has gone", this)); |
michael@0 | 1202 | break; |
michael@0 | 1203 | } |
michael@0 | 1204 | mPendingActions.Clear(); |
michael@0 | 1205 | } |
michael@0 | 1206 | |
michael@0 | 1207 | STDMETHODIMP |
michael@0 | 1208 | nsTextStore::GetStatus(TS_STATUS *pdcs) |
michael@0 | 1209 | { |
michael@0 | 1210 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 1211 | ("TSF: 0x%p nsTextStore::GetStatus(pdcs=0x%p)", this, pdcs)); |
michael@0 | 1212 | |
michael@0 | 1213 | if (!pdcs) { |
michael@0 | 1214 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1215 | ("TSF: 0x%p nsTextStore::GetStatus() FAILED due to null pdcs", this)); |
michael@0 | 1216 | return E_INVALIDARG; |
michael@0 | 1217 | } |
michael@0 | 1218 | pdcs->dwDynamicFlags = 0; |
michael@0 | 1219 | // we use a "flat" text model for TSF support so no hidden text |
michael@0 | 1220 | pdcs->dwStaticFlags = TS_SS_NOHIDDENTEXT; |
michael@0 | 1221 | return S_OK; |
michael@0 | 1222 | } |
michael@0 | 1223 | |
michael@0 | 1224 | STDMETHODIMP |
michael@0 | 1225 | nsTextStore::QueryInsert(LONG acpTestStart, |
michael@0 | 1226 | LONG acpTestEnd, |
michael@0 | 1227 | ULONG cch, |
michael@0 | 1228 | LONG *pacpResultStart, |
michael@0 | 1229 | LONG *pacpResultEnd) |
michael@0 | 1230 | { |
michael@0 | 1231 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 1232 | ("TSF: 0x%p nsTextStore::QueryInsert(acpTestStart=%ld, " |
michael@0 | 1233 | "acpTestEnd=%ld, cch=%lu, pacpResultStart=0x%p, pacpResultEnd=0x%p)", |
michael@0 | 1234 | this, acpTestStart, acpTestEnd, cch, acpTestStart, acpTestEnd)); |
michael@0 | 1235 | |
michael@0 | 1236 | if (!pacpResultStart || !pacpResultEnd) { |
michael@0 | 1237 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1238 | ("TSF: 0x%p nsTextStore::QueryInsert() FAILED due to " |
michael@0 | 1239 | "the null argument", this)); |
michael@0 | 1240 | return E_INVALIDARG; |
michael@0 | 1241 | } |
michael@0 | 1242 | |
michael@0 | 1243 | if (acpTestStart < 0 || acpTestStart > acpTestEnd) { |
michael@0 | 1244 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1245 | ("TSF: 0x%p nsTextStore::QueryInsert() FAILED due to " |
michael@0 | 1246 | "wrong argument", this)); |
michael@0 | 1247 | return E_INVALIDARG; |
michael@0 | 1248 | } |
michael@0 | 1249 | |
michael@0 | 1250 | // XXX need to adjust to cluster boundary |
michael@0 | 1251 | // Assume we are given good offsets for now |
michael@0 | 1252 | *pacpResultStart = acpTestStart; |
michael@0 | 1253 | *pacpResultEnd = acpTestStart + cch; |
michael@0 | 1254 | |
michael@0 | 1255 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 1256 | ("TSF: 0x%p nsTextStore::QueryInsert() succeeded: " |
michael@0 | 1257 | "*pacpResultStart=%ld, *pacpResultEnd=%ld)", |
michael@0 | 1258 | this, *pacpResultStart, *pacpResultEnd)); |
michael@0 | 1259 | return S_OK; |
michael@0 | 1260 | } |
michael@0 | 1261 | |
michael@0 | 1262 | STDMETHODIMP |
michael@0 | 1263 | nsTextStore::GetSelection(ULONG ulIndex, |
michael@0 | 1264 | ULONG ulCount, |
michael@0 | 1265 | TS_SELECTION_ACP *pSelection, |
michael@0 | 1266 | ULONG *pcFetched) |
michael@0 | 1267 | { |
michael@0 | 1268 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 1269 | ("TSF: 0x%p nsTextStore::GetSelection(ulIndex=%lu, ulCount=%lu, " |
michael@0 | 1270 | "pSelection=0x%p, pcFetched=0x%p)", |
michael@0 | 1271 | this, ulIndex, ulCount, pSelection, pcFetched)); |
michael@0 | 1272 | |
michael@0 | 1273 | if (!IsReadLocked()) { |
michael@0 | 1274 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1275 | ("TSF: 0x%p nsTextStore::GetSelection() FAILED due to not locked", |
michael@0 | 1276 | this)); |
michael@0 | 1277 | return TS_E_NOLOCK; |
michael@0 | 1278 | } |
michael@0 | 1279 | if (!ulCount || !pSelection || !pcFetched) { |
michael@0 | 1280 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1281 | ("TSF: 0x%p nsTextStore::GetSelection() FAILED due to " |
michael@0 | 1282 | "null argument", this)); |
michael@0 | 1283 | return E_INVALIDARG; |
michael@0 | 1284 | } |
michael@0 | 1285 | |
michael@0 | 1286 | *pcFetched = 0; |
michael@0 | 1287 | |
michael@0 | 1288 | if (ulIndex != static_cast<ULONG>(TS_DEFAULT_SELECTION) && |
michael@0 | 1289 | ulIndex != 0) { |
michael@0 | 1290 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1291 | ("TSF: 0x%p nsTextStore::GetSelection() FAILED due to " |
michael@0 | 1292 | "unsupported selection", this)); |
michael@0 | 1293 | return TS_E_NOSELECTION; |
michael@0 | 1294 | } |
michael@0 | 1295 | |
michael@0 | 1296 | Selection& currentSel = CurrentSelection(); |
michael@0 | 1297 | if (currentSel.IsDirty()) { |
michael@0 | 1298 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1299 | ("TSF: 0x%p nsTextStore::GetSelection() FAILED due to " |
michael@0 | 1300 | "CurrentSelection() failure", this)); |
michael@0 | 1301 | return E_FAIL; |
michael@0 | 1302 | } |
michael@0 | 1303 | *pSelection = currentSel.ACP(); |
michael@0 | 1304 | *pcFetched = 1; |
michael@0 | 1305 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 1306 | ("TSF: 0x%p nsTextStore::GetSelection() succeeded", this)); |
michael@0 | 1307 | return S_OK; |
michael@0 | 1308 | } |
michael@0 | 1309 | |
michael@0 | 1310 | nsTextStore::Content& |
michael@0 | 1311 | nsTextStore::CurrentContent() |
michael@0 | 1312 | { |
michael@0 | 1313 | Selection& currentSel = CurrentSelection(); |
michael@0 | 1314 | if (currentSel.IsDirty()) { |
michael@0 | 1315 | mContent.Clear(); |
michael@0 | 1316 | return mContent; |
michael@0 | 1317 | } |
michael@0 | 1318 | |
michael@0 | 1319 | if (!mContent.IsInitialized()) { |
michael@0 | 1320 | MOZ_ASSERT(mWidget && !mWidget->Destroyed()); |
michael@0 | 1321 | |
michael@0 | 1322 | WidgetQueryContentEvent queryText(true, NS_QUERY_TEXT_CONTENT, mWidget); |
michael@0 | 1323 | queryText.InitForQueryTextContent(0, UINT32_MAX); |
michael@0 | 1324 | mWidget->InitEvent(queryText); |
michael@0 | 1325 | mWidget->DispatchWindowEvent(&queryText); |
michael@0 | 1326 | NS_ENSURE_TRUE(queryText.mSucceeded, mContent); |
michael@0 | 1327 | |
michael@0 | 1328 | mContent.Init(queryText.mReply.mString); |
michael@0 | 1329 | } |
michael@0 | 1330 | |
michael@0 | 1331 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 1332 | ("TSF: 0x%p nsTextStore::CurrentContent(): " |
michael@0 | 1333 | "mContent={ mText.Length()=%d }", |
michael@0 | 1334 | this, mContent.Text().Length())); |
michael@0 | 1335 | |
michael@0 | 1336 | return mContent; |
michael@0 | 1337 | } |
michael@0 | 1338 | |
michael@0 | 1339 | nsTextStore::Selection& |
michael@0 | 1340 | nsTextStore::CurrentSelection() |
michael@0 | 1341 | { |
michael@0 | 1342 | if (mSelection.IsDirty()) { |
michael@0 | 1343 | // If the window has never been available, we should crash since working |
michael@0 | 1344 | // with broken values may make TIP confused. |
michael@0 | 1345 | if (!mWidget || mWidget->Destroyed()) { |
michael@0 | 1346 | MOZ_CRASH(); |
michael@0 | 1347 | } |
michael@0 | 1348 | |
michael@0 | 1349 | WidgetQueryContentEvent querySelection(true, NS_QUERY_SELECTED_TEXT, |
michael@0 | 1350 | mWidget); |
michael@0 | 1351 | mWidget->InitEvent(querySelection); |
michael@0 | 1352 | mWidget->DispatchWindowEvent(&querySelection); |
michael@0 | 1353 | NS_ENSURE_TRUE(querySelection.mSucceeded, mSelection); |
michael@0 | 1354 | |
michael@0 | 1355 | mSelection.SetSelection(querySelection.mReply.mOffset, |
michael@0 | 1356 | querySelection.mReply.mString.Length(), |
michael@0 | 1357 | querySelection.mReply.mReversed); |
michael@0 | 1358 | } |
michael@0 | 1359 | |
michael@0 | 1360 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 1361 | ("TSF: 0x%p nsTextStore::CurrentSelection(): " |
michael@0 | 1362 | "acpStart=%d, acpEnd=%d (length=%d), reverted=%s", |
michael@0 | 1363 | this, mSelection.StartOffset(), mSelection.EndOffset(), |
michael@0 | 1364 | mSelection.Length(), |
michael@0 | 1365 | GetBoolName(mSelection.IsReversed()))); |
michael@0 | 1366 | |
michael@0 | 1367 | return mSelection; |
michael@0 | 1368 | } |
michael@0 | 1369 | |
michael@0 | 1370 | static HRESULT |
michael@0 | 1371 | GetRangeExtent(ITfRange* aRange, LONG* aStart, LONG* aLength) |
michael@0 | 1372 | { |
michael@0 | 1373 | nsRefPtr<ITfRangeACP> rangeACP; |
michael@0 | 1374 | aRange->QueryInterface(IID_ITfRangeACP, getter_AddRefs(rangeACP)); |
michael@0 | 1375 | NS_ENSURE_TRUE(rangeACP, E_FAIL); |
michael@0 | 1376 | return rangeACP->GetExtent(aStart, aLength); |
michael@0 | 1377 | } |
michael@0 | 1378 | |
michael@0 | 1379 | static uint32_t |
michael@0 | 1380 | GetGeckoSelectionValue(TF_DISPLAYATTRIBUTE &aDisplayAttr) |
michael@0 | 1381 | { |
michael@0 | 1382 | uint32_t result; |
michael@0 | 1383 | switch (aDisplayAttr.bAttr) { |
michael@0 | 1384 | case TF_ATTR_TARGET_CONVERTED: |
michael@0 | 1385 | result = NS_TEXTRANGE_SELECTEDCONVERTEDTEXT; |
michael@0 | 1386 | break; |
michael@0 | 1387 | case TF_ATTR_CONVERTED: |
michael@0 | 1388 | result = NS_TEXTRANGE_CONVERTEDTEXT; |
michael@0 | 1389 | break; |
michael@0 | 1390 | case TF_ATTR_TARGET_NOTCONVERTED: |
michael@0 | 1391 | result = NS_TEXTRANGE_SELECTEDRAWTEXT; |
michael@0 | 1392 | break; |
michael@0 | 1393 | default: |
michael@0 | 1394 | result = NS_TEXTRANGE_RAWINPUT; |
michael@0 | 1395 | break; |
michael@0 | 1396 | } |
michael@0 | 1397 | return result; |
michael@0 | 1398 | } |
michael@0 | 1399 | |
michael@0 | 1400 | HRESULT |
michael@0 | 1401 | nsTextStore::GetDisplayAttribute(ITfProperty* aAttrProperty, |
michael@0 | 1402 | ITfRange* aRange, |
michael@0 | 1403 | TF_DISPLAYATTRIBUTE* aResult) |
michael@0 | 1404 | { |
michael@0 | 1405 | NS_ENSURE_TRUE(aAttrProperty, E_FAIL); |
michael@0 | 1406 | NS_ENSURE_TRUE(aRange, E_FAIL); |
michael@0 | 1407 | NS_ENSURE_TRUE(aResult, E_FAIL); |
michael@0 | 1408 | |
michael@0 | 1409 | HRESULT hr; |
michael@0 | 1410 | |
michael@0 | 1411 | #ifdef PR_LOGGING |
michael@0 | 1412 | if (PR_LOG_TEST(sTextStoreLog, PR_LOG_DEBUG)) { |
michael@0 | 1413 | LONG start = 0, length = 0; |
michael@0 | 1414 | hr = GetRangeExtent(aRange, &start, &length); |
michael@0 | 1415 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 1416 | ("TSF: 0x%p nsTextStore::GetDisplayAttribute(): " |
michael@0 | 1417 | "GetDisplayAttribute range=%ld-%ld (hr=%s)", |
michael@0 | 1418 | this, start - mComposition.mStart, |
michael@0 | 1419 | start - mComposition.mStart + length, |
michael@0 | 1420 | GetCommonReturnValueName(hr))); |
michael@0 | 1421 | } |
michael@0 | 1422 | #endif |
michael@0 | 1423 | |
michael@0 | 1424 | VARIANT propValue; |
michael@0 | 1425 | ::VariantInit(&propValue); |
michael@0 | 1426 | hr = aAttrProperty->GetValue(TfEditCookie(mEditCookie), aRange, &propValue); |
michael@0 | 1427 | if (FAILED(hr)) { |
michael@0 | 1428 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1429 | ("TSF: 0x%p nsTextStore::GetDisplayAttribute() FAILED due to " |
michael@0 | 1430 | "ITfProperty::GetValue() failed", this)); |
michael@0 | 1431 | return hr; |
michael@0 | 1432 | } |
michael@0 | 1433 | if (VT_I4 != propValue.vt) { |
michael@0 | 1434 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1435 | ("TSF: 0x%p nsTextStore::GetDisplayAttribute() FAILED due to " |
michael@0 | 1436 | "ITfProperty::GetValue() returns non-VT_I4 value", this)); |
michael@0 | 1437 | ::VariantClear(&propValue); |
michael@0 | 1438 | return E_FAIL; |
michael@0 | 1439 | } |
michael@0 | 1440 | |
michael@0 | 1441 | NS_ENSURE_TRUE(sCategoryMgr, E_FAIL); |
michael@0 | 1442 | GUID guid; |
michael@0 | 1443 | hr = sCategoryMgr->GetGUID(DWORD(propValue.lVal), &guid); |
michael@0 | 1444 | ::VariantClear(&propValue); |
michael@0 | 1445 | if (FAILED(hr)) { |
michael@0 | 1446 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1447 | ("TSF: 0x%p nsTextStore::GetDisplayAttribute() FAILED due to " |
michael@0 | 1448 | "ITfCategoryMgr::GetGUID() failed", this)); |
michael@0 | 1449 | return hr; |
michael@0 | 1450 | } |
michael@0 | 1451 | |
michael@0 | 1452 | NS_ENSURE_TRUE(sDisplayAttrMgr, E_FAIL); |
michael@0 | 1453 | nsRefPtr<ITfDisplayAttributeInfo> info; |
michael@0 | 1454 | hr = sDisplayAttrMgr->GetDisplayAttributeInfo(guid, getter_AddRefs(info), |
michael@0 | 1455 | nullptr); |
michael@0 | 1456 | if (FAILED(hr) || !info) { |
michael@0 | 1457 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1458 | ("TSF: 0x%p nsTextStore::GetDisplayAttribute() FAILED due to " |
michael@0 | 1459 | "ITfDisplayAttributeMgr::GetDisplayAttributeInfo() failed", this)); |
michael@0 | 1460 | return hr; |
michael@0 | 1461 | } |
michael@0 | 1462 | |
michael@0 | 1463 | hr = info->GetAttributeInfo(aResult); |
michael@0 | 1464 | if (FAILED(hr)) { |
michael@0 | 1465 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1466 | ("TSF: 0x%p nsTextStore::GetDisplayAttribute() FAILED due to " |
michael@0 | 1467 | "ITfDisplayAttributeInfo::GetAttributeInfo() failed", this)); |
michael@0 | 1468 | return hr; |
michael@0 | 1469 | } |
michael@0 | 1470 | |
michael@0 | 1471 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 1472 | ("TSF: 0x%p nsTextStore::GetDisplayAttribute() succeeded: " |
michael@0 | 1473 | "Result={ %s }", this, GetDisplayAttrStr(*aResult).get())); |
michael@0 | 1474 | return S_OK; |
michael@0 | 1475 | } |
michael@0 | 1476 | |
michael@0 | 1477 | HRESULT |
michael@0 | 1478 | nsTextStore::RestartCompositionIfNecessary(ITfRange* aRangeNew) |
michael@0 | 1479 | { |
michael@0 | 1480 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 1481 | ("TSF: 0x%p nsTextStore::RestartCompositionIfNecessary(" |
michael@0 | 1482 | "aRangeNew=0x%p), mComposition.mView=0x%p", |
michael@0 | 1483 | this, aRangeNew, mComposition.mView.get())); |
michael@0 | 1484 | |
michael@0 | 1485 | if (!mComposition.IsComposing()) { |
michael@0 | 1486 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1487 | ("TSF: 0x%p nsTextStore::RestartCompositionIfNecessary() FAILED " |
michael@0 | 1488 | "due to no composition view", this)); |
michael@0 | 1489 | return E_FAIL; |
michael@0 | 1490 | } |
michael@0 | 1491 | |
michael@0 | 1492 | HRESULT hr; |
michael@0 | 1493 | nsRefPtr<ITfCompositionView> pComposition(mComposition.mView); |
michael@0 | 1494 | nsRefPtr<ITfRange> composingRange(aRangeNew); |
michael@0 | 1495 | if (!composingRange) { |
michael@0 | 1496 | hr = pComposition->GetRange(getter_AddRefs(composingRange)); |
michael@0 | 1497 | if (FAILED(hr)) { |
michael@0 | 1498 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1499 | ("TSF: 0x%p nsTextStore::RestartCompositionIfNecessary() FAILED " |
michael@0 | 1500 | "due to pComposition->GetRange() failure", this)); |
michael@0 | 1501 | return hr; |
michael@0 | 1502 | } |
michael@0 | 1503 | } |
michael@0 | 1504 | |
michael@0 | 1505 | // Get starting offset of the composition |
michael@0 | 1506 | LONG compStart = 0, compLength = 0; |
michael@0 | 1507 | hr = GetRangeExtent(composingRange, &compStart, &compLength); |
michael@0 | 1508 | if (FAILED(hr)) { |
michael@0 | 1509 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1510 | ("TSF: 0x%p nsTextStore::RestartCompositionIfNecessary() FAILED " |
michael@0 | 1511 | "due to GetRangeExtent() failure", this)); |
michael@0 | 1512 | return hr; |
michael@0 | 1513 | } |
michael@0 | 1514 | |
michael@0 | 1515 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 1516 | ("TSF: 0x%p nsTextStore::RestartCompositionIfNecessary(), " |
michael@0 | 1517 | "range=%ld-%ld, mComposition={ mStart=%ld, mString.Length()=%lu }", |
michael@0 | 1518 | this, compStart, compStart + compLength, mComposition.mStart, |
michael@0 | 1519 | mComposition.mString.Length())); |
michael@0 | 1520 | |
michael@0 | 1521 | if (mComposition.mStart != compStart || |
michael@0 | 1522 | mComposition.mString.Length() != (ULONG)compLength) { |
michael@0 | 1523 | // If the queried composition length is different from the length |
michael@0 | 1524 | // of our composition string, OnUpdateComposition is being called |
michael@0 | 1525 | // because a part of the original composition was committed. |
michael@0 | 1526 | // Reflect that by committing existing composition and starting |
michael@0 | 1527 | // a new one. RecordCompositionEndAction() followed by |
michael@0 | 1528 | // RecordCompositionStartAction() will accomplish this automagically. |
michael@0 | 1529 | RecordCompositionEndAction(); |
michael@0 | 1530 | RecordCompositionStartAction(pComposition, composingRange, true); |
michael@0 | 1531 | } |
michael@0 | 1532 | |
michael@0 | 1533 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 1534 | ("TSF: 0x%p nsTextStore::RestartCompositionIfNecessary() succeeded", |
michael@0 | 1535 | this)); |
michael@0 | 1536 | return S_OK; |
michael@0 | 1537 | } |
michael@0 | 1538 | |
michael@0 | 1539 | static bool |
michael@0 | 1540 | GetColor(const TF_DA_COLOR &aTSFColor, nscolor &aResult) |
michael@0 | 1541 | { |
michael@0 | 1542 | switch (aTSFColor.type) { |
michael@0 | 1543 | case TF_CT_SYSCOLOR: { |
michael@0 | 1544 | DWORD sysColor = ::GetSysColor(aTSFColor.nIndex); |
michael@0 | 1545 | aResult = NS_RGB(GetRValue(sysColor), GetGValue(sysColor), |
michael@0 | 1546 | GetBValue(sysColor)); |
michael@0 | 1547 | return true; |
michael@0 | 1548 | } |
michael@0 | 1549 | case TF_CT_COLORREF: |
michael@0 | 1550 | aResult = NS_RGB(GetRValue(aTSFColor.cr), GetGValue(aTSFColor.cr), |
michael@0 | 1551 | GetBValue(aTSFColor.cr)); |
michael@0 | 1552 | return true; |
michael@0 | 1553 | case TF_CT_NONE: |
michael@0 | 1554 | default: |
michael@0 | 1555 | return false; |
michael@0 | 1556 | } |
michael@0 | 1557 | } |
michael@0 | 1558 | |
michael@0 | 1559 | static bool |
michael@0 | 1560 | GetLineStyle(TF_DA_LINESTYLE aTSFLineStyle, uint8_t &aTextRangeLineStyle) |
michael@0 | 1561 | { |
michael@0 | 1562 | switch (aTSFLineStyle) { |
michael@0 | 1563 | case TF_LS_NONE: |
michael@0 | 1564 | aTextRangeLineStyle = TextRangeStyle::LINESTYLE_NONE; |
michael@0 | 1565 | return true; |
michael@0 | 1566 | case TF_LS_SOLID: |
michael@0 | 1567 | aTextRangeLineStyle = TextRangeStyle::LINESTYLE_SOLID; |
michael@0 | 1568 | return true; |
michael@0 | 1569 | case TF_LS_DOT: |
michael@0 | 1570 | aTextRangeLineStyle = TextRangeStyle::LINESTYLE_DOTTED; |
michael@0 | 1571 | return true; |
michael@0 | 1572 | case TF_LS_DASH: |
michael@0 | 1573 | aTextRangeLineStyle = TextRangeStyle::LINESTYLE_DASHED; |
michael@0 | 1574 | return true; |
michael@0 | 1575 | case TF_LS_SQUIGGLE: |
michael@0 | 1576 | aTextRangeLineStyle = TextRangeStyle::LINESTYLE_WAVY; |
michael@0 | 1577 | return true; |
michael@0 | 1578 | default: |
michael@0 | 1579 | return false; |
michael@0 | 1580 | } |
michael@0 | 1581 | } |
michael@0 | 1582 | |
michael@0 | 1583 | HRESULT |
michael@0 | 1584 | nsTextStore::RecordCompositionUpdateAction() |
michael@0 | 1585 | { |
michael@0 | 1586 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 1587 | ("TSF: 0x%p nsTextStore::RecordCompositionUpdateAction(), " |
michael@0 | 1588 | "mComposition={ mView=0x%p, mString=\"%s\" }", |
michael@0 | 1589 | this, mComposition.mView.get(), |
michael@0 | 1590 | NS_ConvertUTF16toUTF8(mComposition.mString).get())); |
michael@0 | 1591 | |
michael@0 | 1592 | if (!mComposition.IsComposing()) { |
michael@0 | 1593 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1594 | ("TSF: 0x%p nsTextStore::RecordCompositionUpdateAction() FAILED " |
michael@0 | 1595 | "due to no composition view", this)); |
michael@0 | 1596 | return E_FAIL; |
michael@0 | 1597 | } |
michael@0 | 1598 | |
michael@0 | 1599 | // Getting display attributes is *really* complicated! |
michael@0 | 1600 | // We first get the context and the property objects to query for |
michael@0 | 1601 | // attributes, but since a big range can have a variety of values for |
michael@0 | 1602 | // the attribute, we have to find out all the ranges that have distinct |
michael@0 | 1603 | // attribute values. Then we query for what the value represents through |
michael@0 | 1604 | // the display attribute manager and translate that to TextRange to be |
michael@0 | 1605 | // sent in NS_TEXT_TEXT |
michael@0 | 1606 | |
michael@0 | 1607 | nsRefPtr<ITfProperty> attrPropetry; |
michael@0 | 1608 | HRESULT hr = mContext->GetProperty(GUID_PROP_ATTRIBUTE, |
michael@0 | 1609 | getter_AddRefs(attrPropetry)); |
michael@0 | 1610 | if (FAILED(hr) || !attrPropetry) { |
michael@0 | 1611 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1612 | ("TSF: 0x%p nsTextStore::RecordCompositionUpdateAction() FAILED " |
michael@0 | 1613 | "due to mContext->GetProperty() failure", this)); |
michael@0 | 1614 | return FAILED(hr) ? hr : E_FAIL; |
michael@0 | 1615 | } |
michael@0 | 1616 | |
michael@0 | 1617 | nsRefPtr<ITfRange> composingRange; |
michael@0 | 1618 | hr = mComposition.mView->GetRange(getter_AddRefs(composingRange)); |
michael@0 | 1619 | if (FAILED(hr)) { |
michael@0 | 1620 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1621 | ("TSF: 0x%p nsTextStore::RecordCompositionUpdateAction() " |
michael@0 | 1622 | "FAILED due to mComposition.mView->GetRange() failure", this)); |
michael@0 | 1623 | return hr; |
michael@0 | 1624 | } |
michael@0 | 1625 | |
michael@0 | 1626 | nsRefPtr<IEnumTfRanges> enumRanges; |
michael@0 | 1627 | hr = attrPropetry->EnumRanges(TfEditCookie(mEditCookie), |
michael@0 | 1628 | getter_AddRefs(enumRanges), composingRange); |
michael@0 | 1629 | if (FAILED(hr) || !enumRanges) { |
michael@0 | 1630 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1631 | ("TSF: 0x%p nsTextStore::RecordCompositionUpdateAction() FAILED " |
michael@0 | 1632 | "due to attrPropetry->EnumRanges() failure", this)); |
michael@0 | 1633 | return FAILED(hr) ? hr : E_FAIL; |
michael@0 | 1634 | } |
michael@0 | 1635 | |
michael@0 | 1636 | // First, put the log of content and selection here. |
michael@0 | 1637 | Selection& currentSel = CurrentSelection(); |
michael@0 | 1638 | if (currentSel.IsDirty()) { |
michael@0 | 1639 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1640 | ("TSF: 0x%p nsTextStore::RecordCompositionUpdateAction() FAILED " |
michael@0 | 1641 | "due to CurrentSelection() failure", this)); |
michael@0 | 1642 | return E_FAIL; |
michael@0 | 1643 | } |
michael@0 | 1644 | |
michael@0 | 1645 | PendingAction* action = GetPendingCompositionUpdate(); |
michael@0 | 1646 | action->mData = mComposition.mString; |
michael@0 | 1647 | // The ranges might already have been initialized, however, if this is |
michael@0 | 1648 | // called again, that means we need to overwrite the ranges with current |
michael@0 | 1649 | // information. |
michael@0 | 1650 | action->mRanges->Clear(); |
michael@0 | 1651 | |
michael@0 | 1652 | TextRange newRange; |
michael@0 | 1653 | // No matter if we have display attribute info or not, |
michael@0 | 1654 | // we always pass in at least one range to NS_TEXT_TEXT |
michael@0 | 1655 | newRange.mStartOffset = 0; |
michael@0 | 1656 | newRange.mEndOffset = action->mData.Length(); |
michael@0 | 1657 | newRange.mRangeType = NS_TEXTRANGE_RAWINPUT; |
michael@0 | 1658 | action->mRanges->AppendElement(newRange); |
michael@0 | 1659 | |
michael@0 | 1660 | nsRefPtr<ITfRange> range; |
michael@0 | 1661 | while (S_OK == enumRanges->Next(1, getter_AddRefs(range), nullptr) && range) { |
michael@0 | 1662 | |
michael@0 | 1663 | LONG start = 0, length = 0; |
michael@0 | 1664 | if (FAILED(GetRangeExtent(range, &start, &length))) |
michael@0 | 1665 | continue; |
michael@0 | 1666 | |
michael@0 | 1667 | TextRange newRange; |
michael@0 | 1668 | newRange.mStartOffset = uint32_t(start - mComposition.mStart); |
michael@0 | 1669 | // The end of the last range in the array is |
michael@0 | 1670 | // always kept at the end of composition |
michael@0 | 1671 | newRange.mEndOffset = mComposition.mString.Length(); |
michael@0 | 1672 | |
michael@0 | 1673 | TF_DISPLAYATTRIBUTE attr; |
michael@0 | 1674 | hr = GetDisplayAttribute(attrPropetry, range, &attr); |
michael@0 | 1675 | if (FAILED(hr)) { |
michael@0 | 1676 | newRange.mRangeType = NS_TEXTRANGE_RAWINPUT; |
michael@0 | 1677 | } else { |
michael@0 | 1678 | newRange.mRangeType = GetGeckoSelectionValue(attr); |
michael@0 | 1679 | if (GetColor(attr.crText, newRange.mRangeStyle.mForegroundColor)) { |
michael@0 | 1680 | newRange.mRangeStyle.mDefinedStyles |= |
michael@0 | 1681 | TextRangeStyle::DEFINED_FOREGROUND_COLOR; |
michael@0 | 1682 | } |
michael@0 | 1683 | if (GetColor(attr.crBk, newRange.mRangeStyle.mBackgroundColor)) { |
michael@0 | 1684 | newRange.mRangeStyle.mDefinedStyles |= |
michael@0 | 1685 | TextRangeStyle::DEFINED_BACKGROUND_COLOR; |
michael@0 | 1686 | } |
michael@0 | 1687 | if (GetColor(attr.crLine, newRange.mRangeStyle.mUnderlineColor)) { |
michael@0 | 1688 | newRange.mRangeStyle.mDefinedStyles |= |
michael@0 | 1689 | TextRangeStyle::DEFINED_UNDERLINE_COLOR; |
michael@0 | 1690 | } |
michael@0 | 1691 | if (GetLineStyle(attr.lsStyle, newRange.mRangeStyle.mLineStyle)) { |
michael@0 | 1692 | newRange.mRangeStyle.mDefinedStyles |= |
michael@0 | 1693 | TextRangeStyle::DEFINED_LINESTYLE; |
michael@0 | 1694 | newRange.mRangeStyle.mIsBoldLine = attr.fBoldLine != 0; |
michael@0 | 1695 | } |
michael@0 | 1696 | } |
michael@0 | 1697 | |
michael@0 | 1698 | TextRange& lastRange = action->mRanges->LastElement(); |
michael@0 | 1699 | if (lastRange.mStartOffset == newRange.mStartOffset) { |
michael@0 | 1700 | // Replace range if last range is the same as this one |
michael@0 | 1701 | // So that ranges don't overlap and confuse the editor |
michael@0 | 1702 | lastRange = newRange; |
michael@0 | 1703 | } else { |
michael@0 | 1704 | lastRange.mEndOffset = newRange.mStartOffset; |
michael@0 | 1705 | action->mRanges->AppendElement(newRange); |
michael@0 | 1706 | } |
michael@0 | 1707 | } |
michael@0 | 1708 | |
michael@0 | 1709 | // We need to hack for Korean Input System which is Korean standard TIP. |
michael@0 | 1710 | // It sets no change style to IME selection (the selection is always only |
michael@0 | 1711 | // one). So, the composition string looks like normal (or committed) string. |
michael@0 | 1712 | // At this time, current selection range is same as the composition string |
michael@0 | 1713 | // range. Other applications set a wide caret which covers the composition |
michael@0 | 1714 | // string, however, Gecko doesn't support the wide caret drawing now (Gecko |
michael@0 | 1715 | // doesn't support XOR drawing), unfortunately. For now, we should change |
michael@0 | 1716 | // the range style to undefined. |
michael@0 | 1717 | if (!currentSel.IsCollapsed() && action->mRanges->Length() == 1) { |
michael@0 | 1718 | TextRange& range = action->mRanges->ElementAt(0); |
michael@0 | 1719 | LONG start = currentSel.MinOffset(); |
michael@0 | 1720 | LONG end = currentSel.MaxOffset(); |
michael@0 | 1721 | if ((LONG)range.mStartOffset == start - mComposition.mStart && |
michael@0 | 1722 | (LONG)range.mEndOffset == end - mComposition.mStart && |
michael@0 | 1723 | range.mRangeStyle.IsNoChangeStyle()) { |
michael@0 | 1724 | range.mRangeStyle.Clear(); |
michael@0 | 1725 | // The looks of selected type is better than others. |
michael@0 | 1726 | range.mRangeType = NS_TEXTRANGE_SELECTEDRAWTEXT; |
michael@0 | 1727 | } |
michael@0 | 1728 | } |
michael@0 | 1729 | |
michael@0 | 1730 | // The caret position has to be collapsed. |
michael@0 | 1731 | LONG caretPosition = currentSel.MaxOffset(); |
michael@0 | 1732 | caretPosition -= mComposition.mStart; |
michael@0 | 1733 | TextRange caretRange; |
michael@0 | 1734 | caretRange.mStartOffset = caretRange.mEndOffset = uint32_t(caretPosition); |
michael@0 | 1735 | caretRange.mRangeType = NS_TEXTRANGE_CARETPOSITION; |
michael@0 | 1736 | action->mRanges->AppendElement(caretRange); |
michael@0 | 1737 | |
michael@0 | 1738 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 1739 | ("TSF: 0x%p nsTextStore::RecordCompositionUpdateAction() " |
michael@0 | 1740 | "succeeded", this)); |
michael@0 | 1741 | |
michael@0 | 1742 | return S_OK; |
michael@0 | 1743 | } |
michael@0 | 1744 | |
michael@0 | 1745 | HRESULT |
michael@0 | 1746 | nsTextStore::SetSelectionInternal(const TS_SELECTION_ACP* pSelection, |
michael@0 | 1747 | bool aDispatchTextEvent) |
michael@0 | 1748 | { |
michael@0 | 1749 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 1750 | ("TSF: 0x%p nsTextStore::SetSelectionInternal(pSelection={ " |
michael@0 | 1751 | "acpStart=%ld, acpEnd=%ld, style={ ase=%s, fInterimChar=%s} }, " |
michael@0 | 1752 | "aDispatchTextEvent=%s), mComposition.IsComposing()=%s", |
michael@0 | 1753 | this, pSelection->acpStart, pSelection->acpEnd, |
michael@0 | 1754 | GetActiveSelEndName(pSelection->style.ase), |
michael@0 | 1755 | GetBoolName(pSelection->style.fInterimChar), |
michael@0 | 1756 | GetBoolName(aDispatchTextEvent), |
michael@0 | 1757 | GetBoolName(mComposition.IsComposing()))); |
michael@0 | 1758 | |
michael@0 | 1759 | MOZ_ASSERT(IsReadWriteLocked()); |
michael@0 | 1760 | |
michael@0 | 1761 | Selection& currentSel = CurrentSelection(); |
michael@0 | 1762 | if (currentSel.IsDirty()) { |
michael@0 | 1763 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1764 | ("TSF: 0x%p nsTextStore::SetSelectionInternal() FAILED due to " |
michael@0 | 1765 | "CurrentSelection() failure", this)); |
michael@0 | 1766 | return E_FAIL; |
michael@0 | 1767 | } |
michael@0 | 1768 | |
michael@0 | 1769 | if (mComposition.IsComposing()) { |
michael@0 | 1770 | if (aDispatchTextEvent) { |
michael@0 | 1771 | HRESULT hr = RestartCompositionIfNecessary(); |
michael@0 | 1772 | if (FAILED(hr)) { |
michael@0 | 1773 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1774 | ("TSF: 0x%p nsTextStore::SetSelectionInternal() FAILED due to " |
michael@0 | 1775 | "RestartCompositionIfNecessary() failure", this)); |
michael@0 | 1776 | return hr; |
michael@0 | 1777 | } |
michael@0 | 1778 | } |
michael@0 | 1779 | if (pSelection->acpStart < mComposition.mStart || |
michael@0 | 1780 | pSelection->acpEnd > mComposition.EndOffset()) { |
michael@0 | 1781 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1782 | ("TSF: 0x%p nsTextStore::SetSelectionInternal() FAILED due to " |
michael@0 | 1783 | "the selection being out of the composition string", this)); |
michael@0 | 1784 | return TS_E_INVALIDPOS; |
michael@0 | 1785 | } |
michael@0 | 1786 | // Emulate selection during compositions |
michael@0 | 1787 | currentSel.SetSelection(*pSelection); |
michael@0 | 1788 | if (aDispatchTextEvent) { |
michael@0 | 1789 | HRESULT hr = RecordCompositionUpdateAction(); |
michael@0 | 1790 | if (FAILED(hr)) { |
michael@0 | 1791 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1792 | ("TSF: 0x%p nsTextStore::SetSelectionInternal() FAILED due to " |
michael@0 | 1793 | "RecordCompositionUpdateAction() failure", this)); |
michael@0 | 1794 | return hr; |
michael@0 | 1795 | } |
michael@0 | 1796 | } |
michael@0 | 1797 | return S_OK; |
michael@0 | 1798 | } |
michael@0 | 1799 | |
michael@0 | 1800 | PendingAction* action = mPendingActions.AppendElement(); |
michael@0 | 1801 | action->mType = PendingAction::SELECTION_SET; |
michael@0 | 1802 | action->mSelectionStart = pSelection->acpStart; |
michael@0 | 1803 | action->mSelectionLength = pSelection->acpEnd - pSelection->acpStart; |
michael@0 | 1804 | action->mSelectionReversed = (pSelection->style.ase == TS_AE_START); |
michael@0 | 1805 | |
michael@0 | 1806 | currentSel.SetSelection(*pSelection); |
michael@0 | 1807 | |
michael@0 | 1808 | return S_OK; |
michael@0 | 1809 | } |
michael@0 | 1810 | |
michael@0 | 1811 | STDMETHODIMP |
michael@0 | 1812 | nsTextStore::SetSelection(ULONG ulCount, |
michael@0 | 1813 | const TS_SELECTION_ACP *pSelection) |
michael@0 | 1814 | { |
michael@0 | 1815 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 1816 | ("TSF: 0x%p nsTextStore::SetSelection(ulCount=%lu, pSelection=%p { " |
michael@0 | 1817 | "acpStart=%ld, acpEnd=%ld, style={ ase=%s, fInterimChar=%s } }), " |
michael@0 | 1818 | "mComposition.IsComposing()=%s", |
michael@0 | 1819 | this, ulCount, pSelection, |
michael@0 | 1820 | pSelection ? pSelection->acpStart : 0, |
michael@0 | 1821 | pSelection ? pSelection->acpEnd : 0, |
michael@0 | 1822 | pSelection ? GetActiveSelEndName(pSelection->style.ase) : "", |
michael@0 | 1823 | pSelection ? GetBoolName(pSelection->style.fInterimChar) : "", |
michael@0 | 1824 | GetBoolName(mComposition.IsComposing()))); |
michael@0 | 1825 | |
michael@0 | 1826 | if (!IsReadWriteLocked()) { |
michael@0 | 1827 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1828 | ("TSF: 0x%p nsTextStore::SetSelection() FAILED due to " |
michael@0 | 1829 | "not locked (read-write)", this)); |
michael@0 | 1830 | return TS_E_NOLOCK; |
michael@0 | 1831 | } |
michael@0 | 1832 | if (ulCount != 1) { |
michael@0 | 1833 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1834 | ("TSF: 0x%p nsTextStore::SetSelection() FAILED due to " |
michael@0 | 1835 | "trying setting multiple selection", this)); |
michael@0 | 1836 | return E_INVALIDARG; |
michael@0 | 1837 | } |
michael@0 | 1838 | if (!pSelection) { |
michael@0 | 1839 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1840 | ("TSF: 0x%p nsTextStore::SetSelection() FAILED due to " |
michael@0 | 1841 | "null argument", this)); |
michael@0 | 1842 | return E_INVALIDARG; |
michael@0 | 1843 | } |
michael@0 | 1844 | |
michael@0 | 1845 | HRESULT hr = SetSelectionInternal(pSelection, true); |
michael@0 | 1846 | if (FAILED(hr)) { |
michael@0 | 1847 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1848 | ("TSF: 0x%p nsTextStore::SetSelection() FAILED due to " |
michael@0 | 1849 | "SetSelectionInternal() failure", this)); |
michael@0 | 1850 | } else { |
michael@0 | 1851 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 1852 | ("TSF: 0x%p nsTextStore::SetSelection() succeeded", this)); |
michael@0 | 1853 | } |
michael@0 | 1854 | return hr; |
michael@0 | 1855 | } |
michael@0 | 1856 | |
michael@0 | 1857 | STDMETHODIMP |
michael@0 | 1858 | nsTextStore::GetText(LONG acpStart, |
michael@0 | 1859 | LONG acpEnd, |
michael@0 | 1860 | WCHAR *pchPlain, |
michael@0 | 1861 | ULONG cchPlainReq, |
michael@0 | 1862 | ULONG *pcchPlainOut, |
michael@0 | 1863 | TS_RUNINFO *prgRunInfo, |
michael@0 | 1864 | ULONG ulRunInfoReq, |
michael@0 | 1865 | ULONG *pulRunInfoOut, |
michael@0 | 1866 | LONG *pacpNext) |
michael@0 | 1867 | { |
michael@0 | 1868 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 1869 | ("TSF: 0x%p nsTextStore::GetText(acpStart=%ld, acpEnd=%ld, pchPlain=0x%p, " |
michael@0 | 1870 | "cchPlainReq=%lu, pcchPlainOut=0x%p, prgRunInfo=0x%p, ulRunInfoReq=%lu, " |
michael@0 | 1871 | "pulRunInfoOut=0x%p, pacpNext=0x%p), mComposition={ mStart=%ld, " |
michael@0 | 1872 | "mString.Length()=%lu, IsComposing()=%s }", |
michael@0 | 1873 | this, acpStart, acpEnd, pchPlain, cchPlainReq, pcchPlainOut, |
michael@0 | 1874 | prgRunInfo, ulRunInfoReq, pulRunInfoOut, pacpNext, |
michael@0 | 1875 | mComposition.mStart, mComposition.mString.Length(), |
michael@0 | 1876 | GetBoolName(mComposition.IsComposing()))); |
michael@0 | 1877 | |
michael@0 | 1878 | if (!IsReadLocked()) { |
michael@0 | 1879 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1880 | ("TSF: 0x%p nsTextStore::GetText() FAILED due to " |
michael@0 | 1881 | "not locked (read)", this)); |
michael@0 | 1882 | return TS_E_NOLOCK; |
michael@0 | 1883 | } |
michael@0 | 1884 | |
michael@0 | 1885 | if (!pcchPlainOut || (!pchPlain && !prgRunInfo) || |
michael@0 | 1886 | !cchPlainReq != !pchPlain || !ulRunInfoReq != !prgRunInfo) { |
michael@0 | 1887 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1888 | ("TSF: 0x%p nsTextStore::GetText() FAILED due to " |
michael@0 | 1889 | "invalid argument", this)); |
michael@0 | 1890 | return E_INVALIDARG; |
michael@0 | 1891 | } |
michael@0 | 1892 | |
michael@0 | 1893 | if (acpStart < 0 || acpEnd < -1 || (acpEnd != -1 && acpStart > acpEnd)) { |
michael@0 | 1894 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1895 | ("TSF: 0x%p nsTextStore::GetText() FAILED due to " |
michael@0 | 1896 | "invalid position", this)); |
michael@0 | 1897 | return TS_E_INVALIDPOS; |
michael@0 | 1898 | } |
michael@0 | 1899 | |
michael@0 | 1900 | // Making sure to null-terminate string just to be on the safe side |
michael@0 | 1901 | *pcchPlainOut = 0; |
michael@0 | 1902 | if (pchPlain && cchPlainReq) *pchPlain = 0; |
michael@0 | 1903 | if (pulRunInfoOut) *pulRunInfoOut = 0; |
michael@0 | 1904 | if (pacpNext) *pacpNext = acpStart; |
michael@0 | 1905 | if (prgRunInfo && ulRunInfoReq) { |
michael@0 | 1906 | prgRunInfo->uCount = 0; |
michael@0 | 1907 | prgRunInfo->type = TS_RT_PLAIN; |
michael@0 | 1908 | } |
michael@0 | 1909 | |
michael@0 | 1910 | Content& currentContent = CurrentContent(); |
michael@0 | 1911 | if (!currentContent.IsInitialized()) { |
michael@0 | 1912 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1913 | ("TSF: 0x%p nsTextStore::GetText() FAILED due to " |
michael@0 | 1914 | "CurrentContent() failure", this)); |
michael@0 | 1915 | return E_FAIL; |
michael@0 | 1916 | } |
michael@0 | 1917 | if (currentContent.Text().Length() < static_cast<uint32_t>(acpStart)) { |
michael@0 | 1918 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1919 | ("TSF: 0x%p nsTextStore::GetText() FAILED due to " |
michael@0 | 1920 | "acpStart is larger offset than the actual text length", this)); |
michael@0 | 1921 | return TS_E_INVALIDPOS; |
michael@0 | 1922 | } |
michael@0 | 1923 | if (acpEnd != -1 && |
michael@0 | 1924 | currentContent.Text().Length() < static_cast<uint32_t>(acpEnd)) { |
michael@0 | 1925 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1926 | ("TSF: 0x%p nsTextStore::GetText() FAILED due to " |
michael@0 | 1927 | "acpEnd is larger offset than the actual text length", this)); |
michael@0 | 1928 | return TS_E_INVALIDPOS; |
michael@0 | 1929 | } |
michael@0 | 1930 | uint32_t length = (acpEnd == -1) ? |
michael@0 | 1931 | currentContent.Text().Length() - static_cast<uint32_t>(acpStart) : |
michael@0 | 1932 | static_cast<uint32_t>(acpEnd - acpStart); |
michael@0 | 1933 | if (cchPlainReq && cchPlainReq - 1 < length) { |
michael@0 | 1934 | length = cchPlainReq - 1; |
michael@0 | 1935 | } |
michael@0 | 1936 | if (length) { |
michael@0 | 1937 | if (pchPlain && cchPlainReq) { |
michael@0 | 1938 | const char16_t* startChar = |
michael@0 | 1939 | currentContent.Text().BeginReading() + acpStart; |
michael@0 | 1940 | memcpy(pchPlain, startChar, length * sizeof(*pchPlain)); |
michael@0 | 1941 | pchPlain[length] = 0; |
michael@0 | 1942 | *pcchPlainOut = length; |
michael@0 | 1943 | } |
michael@0 | 1944 | if (prgRunInfo && ulRunInfoReq) { |
michael@0 | 1945 | prgRunInfo->uCount = length; |
michael@0 | 1946 | prgRunInfo->type = TS_RT_PLAIN; |
michael@0 | 1947 | if (pulRunInfoOut) *pulRunInfoOut = 1; |
michael@0 | 1948 | } |
michael@0 | 1949 | if (pacpNext) *pacpNext = acpStart + length; |
michael@0 | 1950 | } |
michael@0 | 1951 | |
michael@0 | 1952 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 1953 | ("TSF: 0x%p nsTextStore::GetText() succeeded: pcchPlainOut=0x%p, " |
michael@0 | 1954 | "*prgRunInfo={ uCount=%lu, type=%s }, *pulRunInfoOut=%lu, " |
michael@0 | 1955 | "*pacpNext=%ld)", |
michael@0 | 1956 | this, pcchPlainOut, prgRunInfo ? prgRunInfo->uCount : 0, |
michael@0 | 1957 | prgRunInfo ? GetTextRunTypeName(prgRunInfo->type) : "N/A", |
michael@0 | 1958 | pulRunInfoOut ? pulRunInfoOut : 0, pacpNext ? pacpNext : 0)); |
michael@0 | 1959 | return S_OK; |
michael@0 | 1960 | } |
michael@0 | 1961 | |
michael@0 | 1962 | STDMETHODIMP |
michael@0 | 1963 | nsTextStore::SetText(DWORD dwFlags, |
michael@0 | 1964 | LONG acpStart, |
michael@0 | 1965 | LONG acpEnd, |
michael@0 | 1966 | const WCHAR *pchText, |
michael@0 | 1967 | ULONG cch, |
michael@0 | 1968 | TS_TEXTCHANGE *pChange) |
michael@0 | 1969 | { |
michael@0 | 1970 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 1971 | ("TSF: 0x%p nsTextStore::SetText(dwFlags=%s, acpStart=%ld, " |
michael@0 | 1972 | "acpEnd=%ld, pchText=0x%p \"%s\", cch=%lu, pChange=0x%p), " |
michael@0 | 1973 | "mComposition.IsComposing()=%s", |
michael@0 | 1974 | this, dwFlags == TS_ST_CORRECTION ? "TS_ST_CORRECTION" : |
michael@0 | 1975 | "not-specified", |
michael@0 | 1976 | acpStart, acpEnd, pchText, |
michael@0 | 1977 | pchText && cch ? |
michael@0 | 1978 | NS_ConvertUTF16toUTF8(pchText, cch).get() : "", |
michael@0 | 1979 | cch, pChange, GetBoolName(mComposition.IsComposing()))); |
michael@0 | 1980 | |
michael@0 | 1981 | // Per SDK documentation, and since we don't have better |
michael@0 | 1982 | // ways to do this, this method acts as a helper to |
michael@0 | 1983 | // call SetSelection followed by InsertTextAtSelection |
michael@0 | 1984 | if (!IsReadWriteLocked()) { |
michael@0 | 1985 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 1986 | ("TSF: 0x%p nsTextStore::SetText() FAILED due to " |
michael@0 | 1987 | "not locked (read)", this)); |
michael@0 | 1988 | return TS_E_NOLOCK; |
michael@0 | 1989 | } |
michael@0 | 1990 | |
michael@0 | 1991 | TS_SELECTION_ACP selection; |
michael@0 | 1992 | selection.acpStart = acpStart; |
michael@0 | 1993 | selection.acpEnd = acpEnd; |
michael@0 | 1994 | selection.style.ase = TS_AE_END; |
michael@0 | 1995 | selection.style.fInterimChar = 0; |
michael@0 | 1996 | // Set selection to desired range |
michael@0 | 1997 | HRESULT hr = SetSelectionInternal(&selection); |
michael@0 | 1998 | if (FAILED(hr)) { |
michael@0 | 1999 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2000 | ("TSF: 0x%p nsTextStore::SetText() FAILED due to " |
michael@0 | 2001 | "SetSelectionInternal() failure", this)); |
michael@0 | 2002 | return hr; |
michael@0 | 2003 | } |
michael@0 | 2004 | // Replace just selected text |
michael@0 | 2005 | if (!InsertTextAtSelectionInternal(nsDependentSubstring(pchText, cch), |
michael@0 | 2006 | pChange)) { |
michael@0 | 2007 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2008 | ("TSF: 0x%p nsTextStore::SetText() FAILED due to " |
michael@0 | 2009 | "InsertTextAtSelectionInternal() failure", this)); |
michael@0 | 2010 | return E_FAIL; |
michael@0 | 2011 | } |
michael@0 | 2012 | |
michael@0 | 2013 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2014 | ("TSF: 0x%p nsTextStore::SetText() succeeded: pChange={ " |
michael@0 | 2015 | "acpStart=%ld, acpOldEnd=%ld, acpNewEnd=%ld }", |
michael@0 | 2016 | this, pChange ? pChange->acpStart : 0, |
michael@0 | 2017 | pChange ? pChange->acpOldEnd : 0, pChange ? pChange->acpNewEnd : 0)); |
michael@0 | 2018 | return S_OK; |
michael@0 | 2019 | } |
michael@0 | 2020 | |
michael@0 | 2021 | STDMETHODIMP |
michael@0 | 2022 | nsTextStore::GetFormattedText(LONG acpStart, |
michael@0 | 2023 | LONG acpEnd, |
michael@0 | 2024 | IDataObject **ppDataObject) |
michael@0 | 2025 | { |
michael@0 | 2026 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2027 | ("TSF: 0x%p nsTextStore::GetFormattedText() called " |
michael@0 | 2028 | "but not supported (E_NOTIMPL)", this)); |
michael@0 | 2029 | |
michael@0 | 2030 | // no support for formatted text |
michael@0 | 2031 | return E_NOTIMPL; |
michael@0 | 2032 | } |
michael@0 | 2033 | |
michael@0 | 2034 | STDMETHODIMP |
michael@0 | 2035 | nsTextStore::GetEmbedded(LONG acpPos, |
michael@0 | 2036 | REFGUID rguidService, |
michael@0 | 2037 | REFIID riid, |
michael@0 | 2038 | IUnknown **ppunk) |
michael@0 | 2039 | { |
michael@0 | 2040 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2041 | ("TSF: 0x%p nsTextStore::GetEmbedded() called " |
michael@0 | 2042 | "but not supported (E_NOTIMPL)", this)); |
michael@0 | 2043 | |
michael@0 | 2044 | // embedded objects are not supported |
michael@0 | 2045 | return E_NOTIMPL; |
michael@0 | 2046 | } |
michael@0 | 2047 | |
michael@0 | 2048 | STDMETHODIMP |
michael@0 | 2049 | nsTextStore::QueryInsertEmbedded(const GUID *pguidService, |
michael@0 | 2050 | const FORMATETC *pFormatEtc, |
michael@0 | 2051 | BOOL *pfInsertable) |
michael@0 | 2052 | { |
michael@0 | 2053 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2054 | ("TSF: 0x%p nsTextStore::QueryInsertEmbedded() called " |
michael@0 | 2055 | "but not supported, *pfInsertable=FALSE (S_OK)", this)); |
michael@0 | 2056 | |
michael@0 | 2057 | // embedded objects are not supported |
michael@0 | 2058 | *pfInsertable = FALSE; |
michael@0 | 2059 | return S_OK; |
michael@0 | 2060 | } |
michael@0 | 2061 | |
michael@0 | 2062 | STDMETHODIMP |
michael@0 | 2063 | nsTextStore::InsertEmbedded(DWORD dwFlags, |
michael@0 | 2064 | LONG acpStart, |
michael@0 | 2065 | LONG acpEnd, |
michael@0 | 2066 | IDataObject *pDataObject, |
michael@0 | 2067 | TS_TEXTCHANGE *pChange) |
michael@0 | 2068 | { |
michael@0 | 2069 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2070 | ("TSF: 0x%p nsTextStore::InsertEmbedded() called " |
michael@0 | 2071 | "but not supported (E_NOTIMPL)", this)); |
michael@0 | 2072 | |
michael@0 | 2073 | // embedded objects are not supported |
michael@0 | 2074 | return E_NOTIMPL; |
michael@0 | 2075 | } |
michael@0 | 2076 | |
michael@0 | 2077 | void |
michael@0 | 2078 | nsTextStore::SetInputScope(const nsString& aHTMLInputType) |
michael@0 | 2079 | { |
michael@0 | 2080 | mInputScopes.Clear(); |
michael@0 | 2081 | if (aHTMLInputType.IsEmpty() || aHTMLInputType.EqualsLiteral("text")) { |
michael@0 | 2082 | return; |
michael@0 | 2083 | } |
michael@0 | 2084 | |
michael@0 | 2085 | // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html |
michael@0 | 2086 | if (aHTMLInputType.EqualsLiteral("url")) { |
michael@0 | 2087 | mInputScopes.AppendElement(IS_URL); |
michael@0 | 2088 | } else if (aHTMLInputType.EqualsLiteral("search")) { |
michael@0 | 2089 | mInputScopes.AppendElement(IS_SEARCH); |
michael@0 | 2090 | } else if (aHTMLInputType.EqualsLiteral("email")) { |
michael@0 | 2091 | mInputScopes.AppendElement(IS_EMAIL_SMTPEMAILADDRESS); |
michael@0 | 2092 | } else if (aHTMLInputType.EqualsLiteral("password")) { |
michael@0 | 2093 | mInputScopes.AppendElement(IS_PASSWORD); |
michael@0 | 2094 | } else if (aHTMLInputType.EqualsLiteral("datetime") || |
michael@0 | 2095 | aHTMLInputType.EqualsLiteral("datetime-local")) { |
michael@0 | 2096 | mInputScopes.AppendElement(IS_DATE_FULLDATE); |
michael@0 | 2097 | mInputScopes.AppendElement(IS_TIME_FULLTIME); |
michael@0 | 2098 | } else if (aHTMLInputType.EqualsLiteral("date") || |
michael@0 | 2099 | aHTMLInputType.EqualsLiteral("month") || |
michael@0 | 2100 | aHTMLInputType.EqualsLiteral("week")) { |
michael@0 | 2101 | mInputScopes.AppendElement(IS_DATE_FULLDATE); |
michael@0 | 2102 | } else if (aHTMLInputType.EqualsLiteral("time")) { |
michael@0 | 2103 | mInputScopes.AppendElement(IS_TIME_FULLTIME); |
michael@0 | 2104 | } else if (aHTMLInputType.EqualsLiteral("tel")) { |
michael@0 | 2105 | mInputScopes.AppendElement(IS_TELEPHONE_FULLTELEPHONENUMBER); |
michael@0 | 2106 | mInputScopes.AppendElement(IS_TELEPHONE_LOCALNUMBER); |
michael@0 | 2107 | } else if (aHTMLInputType.EqualsLiteral("number")) { |
michael@0 | 2108 | mInputScopes.AppendElement(IS_NUMBER); |
michael@0 | 2109 | } |
michael@0 | 2110 | } |
michael@0 | 2111 | |
michael@0 | 2112 | HRESULT |
michael@0 | 2113 | nsTextStore::ProcessScopeRequest(DWORD dwFlags, |
michael@0 | 2114 | ULONG cFilterAttrs, |
michael@0 | 2115 | const TS_ATTRID *paFilterAttrs) |
michael@0 | 2116 | { |
michael@0 | 2117 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2118 | ("TSF: 0x%p nsTextStore::ProcessScopeRequest(dwFlags=%s, " |
michael@0 | 2119 | "cFilterAttrs=%ld", |
michael@0 | 2120 | this, GetFindFlagName(dwFlags).get(), cFilterAttrs)); |
michael@0 | 2121 | |
michael@0 | 2122 | // This is a little weird! RequestSupportedAttrs gives us advanced notice |
michael@0 | 2123 | // of a support query via RetrieveRequestedAttrs for a specific attribute. |
michael@0 | 2124 | // RetrieveRequestedAttrs needs to return valid data for all attributes we |
michael@0 | 2125 | // support, but the text service will only want the input scope object |
michael@0 | 2126 | // returned in RetrieveRequestedAttrs if the dwFlags passed in here contains |
michael@0 | 2127 | // TS_ATTR_FIND_WANT_VALUE. |
michael@0 | 2128 | mInputScopeDetected = mInputScopeRequested = false; |
michael@0 | 2129 | |
michael@0 | 2130 | // Currently we only support GUID_PROP_INPUTSCOPE |
michael@0 | 2131 | for (uint32_t idx = 0; idx < cFilterAttrs; ++idx) { |
michael@0 | 2132 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2133 | ("TSF: 0x%p nsTextStore::ProcessScopeRequest() " |
michael@0 | 2134 | "requested attr=%s", |
michael@0 | 2135 | this, GetCLSIDNameStr(paFilterAttrs[idx]).get())); |
michael@0 | 2136 | if (IsEqualGUID(paFilterAttrs[idx], GUID_PROP_INPUTSCOPE)) { |
michael@0 | 2137 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2138 | ("TSF: 0x%p nsTextStore::ProcessScopeRequest() " |
michael@0 | 2139 | "GUID_PROP_INPUTSCOPE queried", this)); |
michael@0 | 2140 | mInputScopeDetected = true; |
michael@0 | 2141 | if (dwFlags & TS_ATTR_FIND_WANT_VALUE) { |
michael@0 | 2142 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2143 | ("TSF: 0x%p nsTextStore::ProcessScopeRequest() " |
michael@0 | 2144 | "TS_ATTR_FIND_WANT_VALUE specified", this)); |
michael@0 | 2145 | mInputScopeRequested = true; |
michael@0 | 2146 | } |
michael@0 | 2147 | break; |
michael@0 | 2148 | } |
michael@0 | 2149 | } |
michael@0 | 2150 | return S_OK; |
michael@0 | 2151 | } |
michael@0 | 2152 | |
michael@0 | 2153 | STDMETHODIMP |
michael@0 | 2154 | nsTextStore::RequestSupportedAttrs(DWORD dwFlags, |
michael@0 | 2155 | ULONG cFilterAttrs, |
michael@0 | 2156 | const TS_ATTRID *paFilterAttrs) |
michael@0 | 2157 | { |
michael@0 | 2158 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2159 | ("TSF: 0x%p nsTextStore::RequestSupportedAttrs(dwFlags=%s, " |
michael@0 | 2160 | "cFilterAttrs=%lu)", |
michael@0 | 2161 | this, GetFindFlagName(dwFlags).get(), cFilterAttrs)); |
michael@0 | 2162 | |
michael@0 | 2163 | return ProcessScopeRequest(dwFlags, cFilterAttrs, paFilterAttrs); |
michael@0 | 2164 | } |
michael@0 | 2165 | |
michael@0 | 2166 | STDMETHODIMP |
michael@0 | 2167 | nsTextStore::RequestAttrsAtPosition(LONG acpPos, |
michael@0 | 2168 | ULONG cFilterAttrs, |
michael@0 | 2169 | const TS_ATTRID *paFilterAttrs, |
michael@0 | 2170 | DWORD dwFlags) |
michael@0 | 2171 | { |
michael@0 | 2172 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2173 | ("TSF: 0x%p nsTextStore::RequestAttrsAtPosition(acpPos=%ld, " |
michael@0 | 2174 | "cFilterAttrs=%lu, dwFlags=%s)", |
michael@0 | 2175 | this, acpPos, cFilterAttrs, GetFindFlagName(dwFlags).get())); |
michael@0 | 2176 | |
michael@0 | 2177 | return ProcessScopeRequest(dwFlags | TS_ATTR_FIND_WANT_VALUE, |
michael@0 | 2178 | cFilterAttrs, paFilterAttrs); |
michael@0 | 2179 | } |
michael@0 | 2180 | |
michael@0 | 2181 | STDMETHODIMP |
michael@0 | 2182 | nsTextStore::RequestAttrsTransitioningAtPosition(LONG acpPos, |
michael@0 | 2183 | ULONG cFilterAttrs, |
michael@0 | 2184 | const TS_ATTRID *paFilterAttr, |
michael@0 | 2185 | DWORD dwFlags) |
michael@0 | 2186 | { |
michael@0 | 2187 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2188 | ("TSF: 0x%p nsTextStore::RequestAttrsTransitioningAtPosition(" |
michael@0 | 2189 | "acpPos=%ld, cFilterAttrs=%lu, dwFlags=%s) called but not supported " |
michael@0 | 2190 | "(S_OK)", |
michael@0 | 2191 | this, acpPos, cFilterAttrs, GetFindFlagName(dwFlags).get())); |
michael@0 | 2192 | |
michael@0 | 2193 | // no per character attributes defined |
michael@0 | 2194 | return S_OK; |
michael@0 | 2195 | } |
michael@0 | 2196 | |
michael@0 | 2197 | STDMETHODIMP |
michael@0 | 2198 | nsTextStore::FindNextAttrTransition(LONG acpStart, |
michael@0 | 2199 | LONG acpHalt, |
michael@0 | 2200 | ULONG cFilterAttrs, |
michael@0 | 2201 | const TS_ATTRID *paFilterAttrs, |
michael@0 | 2202 | DWORD dwFlags, |
michael@0 | 2203 | LONG *pacpNext, |
michael@0 | 2204 | BOOL *pfFound, |
michael@0 | 2205 | LONG *plFoundOffset) |
michael@0 | 2206 | { |
michael@0 | 2207 | if (!pacpNext || !pfFound || !plFoundOffset) { |
michael@0 | 2208 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2209 | ("TSF: 0x%p nsTextStore::FindNextAttrTransition() FAILED due to " |
michael@0 | 2210 | "null argument", this)); |
michael@0 | 2211 | return E_INVALIDARG; |
michael@0 | 2212 | } |
michael@0 | 2213 | |
michael@0 | 2214 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2215 | ("TSF: 0x%p nsTextStore::FindNextAttrTransition() called " |
michael@0 | 2216 | "but not supported (S_OK)", this)); |
michael@0 | 2217 | |
michael@0 | 2218 | // no per character attributes defined |
michael@0 | 2219 | *pacpNext = *plFoundOffset = acpHalt; |
michael@0 | 2220 | *pfFound = FALSE; |
michael@0 | 2221 | return S_OK; |
michael@0 | 2222 | } |
michael@0 | 2223 | |
michael@0 | 2224 | STDMETHODIMP |
michael@0 | 2225 | nsTextStore::RetrieveRequestedAttrs(ULONG ulCount, |
michael@0 | 2226 | TS_ATTRVAL *paAttrVals, |
michael@0 | 2227 | ULONG *pcFetched) |
michael@0 | 2228 | { |
michael@0 | 2229 | if (!pcFetched || !ulCount || !paAttrVals) { |
michael@0 | 2230 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2231 | ("TSF: 0x%p nsTextStore::RetrieveRequestedAttrs() FAILED due to " |
michael@0 | 2232 | "null argument", this)); |
michael@0 | 2233 | return E_INVALIDARG; |
michael@0 | 2234 | } |
michael@0 | 2235 | |
michael@0 | 2236 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2237 | ("TSF: 0x%p nsTextStore::RetrieveRequestedAttrs() called " |
michael@0 | 2238 | "ulCount=%d", this, ulCount)); |
michael@0 | 2239 | |
michael@0 | 2240 | if (mInputScopeDetected || mInputScopeRequested) { |
michael@0 | 2241 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2242 | ("TSF: 0x%p nsTextStore::RetrieveRequestedAttrs() for " |
michael@0 | 2243 | "GUID_PROP_INPUTSCOPE: " |
michael@0 | 2244 | "mInputScopeDetected=%s mInputScopeRequested=%s", |
michael@0 | 2245 | this, GetBoolName(mInputScopeDetected), |
michael@0 | 2246 | GetBoolName(mInputScopeRequested))); |
michael@0 | 2247 | |
michael@0 | 2248 | paAttrVals->idAttr = GUID_PROP_INPUTSCOPE; |
michael@0 | 2249 | paAttrVals->dwOverlapId = 0; |
michael@0 | 2250 | paAttrVals->varValue.vt = VT_EMPTY; |
michael@0 | 2251 | *pcFetched = 1; |
michael@0 | 2252 | |
michael@0 | 2253 | if (mInputScopeRequested) { |
michael@0 | 2254 | paAttrVals->varValue.vt = VT_UNKNOWN; |
michael@0 | 2255 | paAttrVals->varValue.punkVal = (IUnknown*) new InputScopeImpl(mInputScopes); |
michael@0 | 2256 | } |
michael@0 | 2257 | |
michael@0 | 2258 | mInputScopeDetected = mInputScopeRequested = false; |
michael@0 | 2259 | return S_OK; |
michael@0 | 2260 | } |
michael@0 | 2261 | |
michael@0 | 2262 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2263 | ("TSF: 0x%p nsTextStore::RetrieveRequestedAttrs() called " |
michael@0 | 2264 | "for unknown TS_ATTRVAL, *pcFetched=0 (S_OK)", this)); |
michael@0 | 2265 | |
michael@0 | 2266 | paAttrVals->dwOverlapId = 0; |
michael@0 | 2267 | paAttrVals->varValue.vt = VT_EMPTY; |
michael@0 | 2268 | *pcFetched = 0; |
michael@0 | 2269 | return S_OK; |
michael@0 | 2270 | } |
michael@0 | 2271 | |
michael@0 | 2272 | STDMETHODIMP |
michael@0 | 2273 | nsTextStore::GetEndACP(LONG *pacp) |
michael@0 | 2274 | { |
michael@0 | 2275 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2276 | ("TSF: 0x%p nsTextStore::GetEndACP(pacp=0x%p)", this, pacp)); |
michael@0 | 2277 | |
michael@0 | 2278 | if (!IsReadLocked()) { |
michael@0 | 2279 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2280 | ("TSF: 0x%p nsTextStore::GetEndACP() FAILED due to " |
michael@0 | 2281 | "not locked (read)", this)); |
michael@0 | 2282 | return TS_E_NOLOCK; |
michael@0 | 2283 | } |
michael@0 | 2284 | |
michael@0 | 2285 | if (!pacp) { |
michael@0 | 2286 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2287 | ("TSF: 0x%p nsTextStore::GetEndACP() FAILED due to " |
michael@0 | 2288 | "null argument", this)); |
michael@0 | 2289 | return E_INVALIDARG; |
michael@0 | 2290 | } |
michael@0 | 2291 | |
michael@0 | 2292 | Content& currentContent = CurrentContent(); |
michael@0 | 2293 | if (!currentContent.IsInitialized()) { |
michael@0 | 2294 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2295 | ("TSF: 0x%p nsTextStore::GetEndACP() FAILED due to " |
michael@0 | 2296 | "CurrentContent() failure", this)); |
michael@0 | 2297 | return E_FAIL; |
michael@0 | 2298 | } |
michael@0 | 2299 | *pacp = static_cast<LONG>(currentContent.Text().Length()); |
michael@0 | 2300 | return S_OK; |
michael@0 | 2301 | } |
michael@0 | 2302 | |
michael@0 | 2303 | STDMETHODIMP |
michael@0 | 2304 | nsTextStore::GetActiveView(TsViewCookie *pvcView) |
michael@0 | 2305 | { |
michael@0 | 2306 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2307 | ("TSF: 0x%p nsTextStore::GetActiveView(pvcView=0x%p)", this, pvcView)); |
michael@0 | 2308 | |
michael@0 | 2309 | if (!pvcView) { |
michael@0 | 2310 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2311 | ("TSF: 0x%p nsTextStore::GetActiveView() FAILED due to " |
michael@0 | 2312 | "null argument", this)); |
michael@0 | 2313 | return E_INVALIDARG; |
michael@0 | 2314 | } |
michael@0 | 2315 | |
michael@0 | 2316 | *pvcView = TEXTSTORE_DEFAULT_VIEW; |
michael@0 | 2317 | |
michael@0 | 2318 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2319 | ("TSF: 0x%p nsTextStore::GetActiveView() succeeded: *pvcView=%ld", |
michael@0 | 2320 | this, *pvcView)); |
michael@0 | 2321 | return S_OK; |
michael@0 | 2322 | } |
michael@0 | 2323 | |
michael@0 | 2324 | STDMETHODIMP |
michael@0 | 2325 | nsTextStore::GetACPFromPoint(TsViewCookie vcView, |
michael@0 | 2326 | const POINT *pt, |
michael@0 | 2327 | DWORD dwFlags, |
michael@0 | 2328 | LONG *pacp) |
michael@0 | 2329 | { |
michael@0 | 2330 | if (!IsReadLocked()) { |
michael@0 | 2331 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2332 | ("TSF: 0x%p nsTextStore::GetACPFromPoint() FAILED due to " |
michael@0 | 2333 | "not locked (read)", this)); |
michael@0 | 2334 | return TS_E_NOLOCK; |
michael@0 | 2335 | } |
michael@0 | 2336 | |
michael@0 | 2337 | if (vcView != TEXTSTORE_DEFAULT_VIEW) { |
michael@0 | 2338 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2339 | ("TSF: 0x%p nsTextStore::GetACPFromPoint() FAILED due to " |
michael@0 | 2340 | "called with invalid view", this)); |
michael@0 | 2341 | return E_INVALIDARG; |
michael@0 | 2342 | } |
michael@0 | 2343 | |
michael@0 | 2344 | if (mContent.IsLayoutChanged()) { |
michael@0 | 2345 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2346 | ("TSF: 0x%p nsTextStore::GetACPFromPoint() FAILED due to " |
michael@0 | 2347 | "layout not recomputed", this)); |
michael@0 | 2348 | mPendingOnLayoutChange = true; |
michael@0 | 2349 | return TS_E_NOLAYOUT; |
michael@0 | 2350 | } |
michael@0 | 2351 | |
michael@0 | 2352 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2353 | ("TSF: 0x%p nsTextStore::GetACPFromPoint(vcView=%ld, " |
michael@0 | 2354 | "pt(0x%p)={ x=%ld, y=%ld }, dwFlags=%s, pacp=0x%p) called " |
michael@0 | 2355 | "but not supported (E_NOTIMPL)", this)); |
michael@0 | 2356 | |
michael@0 | 2357 | // not supported for now |
michael@0 | 2358 | return E_NOTIMPL; |
michael@0 | 2359 | } |
michael@0 | 2360 | |
michael@0 | 2361 | STDMETHODIMP |
michael@0 | 2362 | nsTextStore::GetTextExt(TsViewCookie vcView, |
michael@0 | 2363 | LONG acpStart, |
michael@0 | 2364 | LONG acpEnd, |
michael@0 | 2365 | RECT *prc, |
michael@0 | 2366 | BOOL *pfClipped) |
michael@0 | 2367 | { |
michael@0 | 2368 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2369 | ("TSF: 0x%p nsTextStore::GetTextExt(vcView=%ld, " |
michael@0 | 2370 | "acpStart=%ld, acpEnd=%ld, prc=0x%p, pfClipped=0x%p)", |
michael@0 | 2371 | this, vcView, acpStart, acpEnd, prc, pfClipped)); |
michael@0 | 2372 | |
michael@0 | 2373 | if (!IsReadLocked()) { |
michael@0 | 2374 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2375 | ("TSF: 0x%p nsTextStore::GetTextExt() FAILED due to " |
michael@0 | 2376 | "not locked (read)", this)); |
michael@0 | 2377 | return TS_E_NOLOCK; |
michael@0 | 2378 | } |
michael@0 | 2379 | |
michael@0 | 2380 | if (vcView != TEXTSTORE_DEFAULT_VIEW) { |
michael@0 | 2381 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2382 | ("TSF: 0x%p nsTextStore::GetTextExt() FAILED due to " |
michael@0 | 2383 | "called with invalid view", this)); |
michael@0 | 2384 | return E_INVALIDARG; |
michael@0 | 2385 | } |
michael@0 | 2386 | |
michael@0 | 2387 | if (!prc || !pfClipped) { |
michael@0 | 2388 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2389 | ("TSF: 0x%p nsTextStore::GetTextExt() FAILED due to " |
michael@0 | 2390 | "null argument", this)); |
michael@0 | 2391 | return E_INVALIDARG; |
michael@0 | 2392 | } |
michael@0 | 2393 | |
michael@0 | 2394 | if (acpStart < 0 || acpEnd < acpStart) { |
michael@0 | 2395 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2396 | ("TSF: 0x%p nsTextStore::GetTextExt() FAILED due to " |
michael@0 | 2397 | "invalid position", this)); |
michael@0 | 2398 | return TS_E_INVALIDPOS; |
michael@0 | 2399 | } |
michael@0 | 2400 | |
michael@0 | 2401 | if (mContent.IsLayoutChangedAfter(acpEnd)) { |
michael@0 | 2402 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2403 | ("TSF: 0x%p nsTextStore::GetTextExt() FAILED due to " |
michael@0 | 2404 | "layout not recomputed at %d", this, acpEnd)); |
michael@0 | 2405 | mPendingOnLayoutChange = true; |
michael@0 | 2406 | return TS_E_NOLAYOUT; |
michael@0 | 2407 | } |
michael@0 | 2408 | |
michael@0 | 2409 | // use NS_QUERY_TEXT_RECT to get rect in system, screen coordinates |
michael@0 | 2410 | WidgetQueryContentEvent event(true, NS_QUERY_TEXT_RECT, mWidget); |
michael@0 | 2411 | mWidget->InitEvent(event); |
michael@0 | 2412 | event.InitForQueryTextRect(acpStart, acpEnd - acpStart); |
michael@0 | 2413 | mWidget->DispatchWindowEvent(&event); |
michael@0 | 2414 | if (!event.mSucceeded) { |
michael@0 | 2415 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2416 | ("TSF: 0x%p nsTextStore::GetTextExt() FAILED due to " |
michael@0 | 2417 | "NS_QUERY_TEXT_RECT failure", this)); |
michael@0 | 2418 | return TS_E_INVALIDPOS; // but unexpected failure, maybe. |
michael@0 | 2419 | } |
michael@0 | 2420 | // IMEs don't like empty rects, fix here |
michael@0 | 2421 | if (event.mReply.mRect.width <= 0) |
michael@0 | 2422 | event.mReply.mRect.width = 1; |
michael@0 | 2423 | if (event.mReply.mRect.height <= 0) |
michael@0 | 2424 | event.mReply.mRect.height = 1; |
michael@0 | 2425 | |
michael@0 | 2426 | if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) { |
michael@0 | 2427 | // convert to unclipped screen rect |
michael@0 | 2428 | nsWindow* refWindow = static_cast<nsWindow*>( |
michael@0 | 2429 | event.mReply.mFocusedWidget ? event.mReply.mFocusedWidget : mWidget); |
michael@0 | 2430 | // Result rect is in top level widget coordinates |
michael@0 | 2431 | refWindow = refWindow->GetTopLevelWindow(false); |
michael@0 | 2432 | if (!refWindow) { |
michael@0 | 2433 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2434 | ("TSF: 0x%p nsTextStore::GetTextExt() FAILED due to " |
michael@0 | 2435 | "no top level window", this)); |
michael@0 | 2436 | return E_FAIL; |
michael@0 | 2437 | } |
michael@0 | 2438 | |
michael@0 | 2439 | event.mReply.mRect.MoveBy(refWindow->WidgetToScreenOffset()); |
michael@0 | 2440 | } |
michael@0 | 2441 | |
michael@0 | 2442 | // get bounding screen rect to test for clipping |
michael@0 | 2443 | if (!GetScreenExtInternal(*prc)) { |
michael@0 | 2444 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2445 | ("TSF: 0x%p nsTextStore::GetTextExt() FAILED due to " |
michael@0 | 2446 | "GetScreenExtInternal() failure", this)); |
michael@0 | 2447 | return E_FAIL; |
michael@0 | 2448 | } |
michael@0 | 2449 | |
michael@0 | 2450 | // clip text rect to bounding rect |
michael@0 | 2451 | RECT textRect; |
michael@0 | 2452 | ::SetRect(&textRect, event.mReply.mRect.x, event.mReply.mRect.y, |
michael@0 | 2453 | event.mReply.mRect.XMost(), event.mReply.mRect.YMost()); |
michael@0 | 2454 | if (!::IntersectRect(prc, prc, &textRect)) |
michael@0 | 2455 | // Text is not visible |
michael@0 | 2456 | ::SetRectEmpty(prc); |
michael@0 | 2457 | |
michael@0 | 2458 | // not equal if text rect was clipped |
michael@0 | 2459 | *pfClipped = !::EqualRect(prc, &textRect); |
michael@0 | 2460 | |
michael@0 | 2461 | // ATOK refers native caret position and size on Desktop applications for |
michael@0 | 2462 | // deciding candidate window. Therefore, we need to create native caret |
michael@0 | 2463 | // for hacking the bug. |
michael@0 | 2464 | if (sCreateNativeCaretForATOK && |
michael@0 | 2465 | StringBeginsWith( |
michael@0 | 2466 | mActiveTIPKeyboardDescription, NS_LITERAL_STRING("ATOK ")) && |
michael@0 | 2467 | mComposition.IsComposing() && |
michael@0 | 2468 | mComposition.mStart <= acpStart && mComposition.EndOffset() >= acpStart && |
michael@0 | 2469 | mComposition.mStart <= acpEnd && mComposition.EndOffset() >= acpEnd) { |
michael@0 | 2470 | if (mNativeCaretIsCreated) { |
michael@0 | 2471 | ::DestroyCaret(); |
michael@0 | 2472 | mNativeCaretIsCreated = false; |
michael@0 | 2473 | } |
michael@0 | 2474 | CreateNativeCaret(); |
michael@0 | 2475 | } |
michael@0 | 2476 | |
michael@0 | 2477 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2478 | ("TSF: 0x%p nsTextStore::GetTextExt() succeeded: " |
michael@0 | 2479 | "*prc={ left=%ld, top=%ld, right=%ld, bottom=%ld }, *pfClipped=%s", |
michael@0 | 2480 | this, prc->left, prc->top, prc->right, prc->bottom, |
michael@0 | 2481 | GetBoolName(*pfClipped))); |
michael@0 | 2482 | |
michael@0 | 2483 | return S_OK; |
michael@0 | 2484 | } |
michael@0 | 2485 | |
michael@0 | 2486 | STDMETHODIMP |
michael@0 | 2487 | nsTextStore::GetScreenExt(TsViewCookie vcView, |
michael@0 | 2488 | RECT *prc) |
michael@0 | 2489 | { |
michael@0 | 2490 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2491 | ("TSF: 0x%p nsTextStore::GetScreenExt(vcView=%ld, prc=0x%p)", |
michael@0 | 2492 | this, vcView, prc)); |
michael@0 | 2493 | |
michael@0 | 2494 | if (vcView != TEXTSTORE_DEFAULT_VIEW) { |
michael@0 | 2495 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2496 | ("TSF: 0x%p nsTextStore::GetScreenExt() FAILED due to " |
michael@0 | 2497 | "called with invalid view", this)); |
michael@0 | 2498 | return E_INVALIDARG; |
michael@0 | 2499 | } |
michael@0 | 2500 | |
michael@0 | 2501 | if (!prc) { |
michael@0 | 2502 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2503 | ("TSF: 0x%p nsTextStore::GetScreenExt() FAILED due to " |
michael@0 | 2504 | "null argument", this)); |
michael@0 | 2505 | return E_INVALIDARG; |
michael@0 | 2506 | } |
michael@0 | 2507 | |
michael@0 | 2508 | if (!GetScreenExtInternal(*prc)) { |
michael@0 | 2509 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2510 | ("TSF: 0x%p nsTextStore::GetScreenExt() FAILED due to " |
michael@0 | 2511 | "GetScreenExtInternal() failure", this)); |
michael@0 | 2512 | return E_FAIL; |
michael@0 | 2513 | } |
michael@0 | 2514 | |
michael@0 | 2515 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2516 | ("TSF: 0x%p nsTextStore::GetScreenExt() succeeded: " |
michael@0 | 2517 | "*prc={ left=%ld, top=%ld, right=%ld, bottom=%ld }", |
michael@0 | 2518 | this, prc->left, prc->top, prc->right, prc->bottom)); |
michael@0 | 2519 | return S_OK; |
michael@0 | 2520 | } |
michael@0 | 2521 | |
michael@0 | 2522 | bool |
michael@0 | 2523 | nsTextStore::GetScreenExtInternal(RECT &aScreenExt) |
michael@0 | 2524 | { |
michael@0 | 2525 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 2526 | ("TSF: 0x%p nsTextStore::GetScreenExtInternal()", this)); |
michael@0 | 2527 | |
michael@0 | 2528 | // use NS_QUERY_EDITOR_RECT to get rect in system, screen coordinates |
michael@0 | 2529 | WidgetQueryContentEvent event(true, NS_QUERY_EDITOR_RECT, mWidget); |
michael@0 | 2530 | mWidget->InitEvent(event); |
michael@0 | 2531 | mWidget->DispatchWindowEvent(&event); |
michael@0 | 2532 | if (!event.mSucceeded) { |
michael@0 | 2533 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2534 | ("TSF: 0x%p nsTextStore::GetScreenExtInternal() FAILED due to " |
michael@0 | 2535 | "NS_QUERY_EDITOR_RECT failure", this)); |
michael@0 | 2536 | return false; |
michael@0 | 2537 | } |
michael@0 | 2538 | |
michael@0 | 2539 | if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) { |
michael@0 | 2540 | nsIntRect boundRect; |
michael@0 | 2541 | if (NS_FAILED(mWidget->GetClientBounds(boundRect))) { |
michael@0 | 2542 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2543 | ("TSF: 0x%p nsTextStore::GetScreenExtInternal() FAILED due to " |
michael@0 | 2544 | "failed to get the client bounds", this)); |
michael@0 | 2545 | return false; |
michael@0 | 2546 | } |
michael@0 | 2547 | ::SetRect(&aScreenExt, boundRect.x, boundRect.y, |
michael@0 | 2548 | boundRect.XMost(), boundRect.YMost()); |
michael@0 | 2549 | } else { |
michael@0 | 2550 | NS_ASSERTION(XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop, |
michael@0 | 2551 | "environment isn't WindowsEnvironmentType_Desktop!"); |
michael@0 | 2552 | nsWindow* refWindow = static_cast<nsWindow*>( |
michael@0 | 2553 | event.mReply.mFocusedWidget ? |
michael@0 | 2554 | event.mReply.mFocusedWidget : mWidget); |
michael@0 | 2555 | // Result rect is in top level widget coordinates |
michael@0 | 2556 | refWindow = refWindow->GetTopLevelWindow(false); |
michael@0 | 2557 | if (!refWindow) { |
michael@0 | 2558 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2559 | ("TSF: 0x%p nsTextStore::GetScreenExtInternal() FAILED due to " |
michael@0 | 2560 | "no top level window", this)); |
michael@0 | 2561 | return false; |
michael@0 | 2562 | } |
michael@0 | 2563 | |
michael@0 | 2564 | nsIntRect boundRect; |
michael@0 | 2565 | if (NS_FAILED(refWindow->GetClientBounds(boundRect))) { |
michael@0 | 2566 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2567 | ("TSF: 0x%p nsTextStore::GetScreenExtInternal() FAILED due to " |
michael@0 | 2568 | "failed to get the client bounds", this)); |
michael@0 | 2569 | return false; |
michael@0 | 2570 | } |
michael@0 | 2571 | |
michael@0 | 2572 | boundRect.MoveTo(0, 0); |
michael@0 | 2573 | |
michael@0 | 2574 | // Clip frame rect to window rect |
michael@0 | 2575 | boundRect.IntersectRect(event.mReply.mRect, boundRect); |
michael@0 | 2576 | if (!boundRect.IsEmpty()) { |
michael@0 | 2577 | boundRect.MoveBy(refWindow->WidgetToScreenOffset()); |
michael@0 | 2578 | ::SetRect(&aScreenExt, boundRect.x, boundRect.y, |
michael@0 | 2579 | boundRect.XMost(), boundRect.YMost()); |
michael@0 | 2580 | } else { |
michael@0 | 2581 | ::SetRectEmpty(&aScreenExt); |
michael@0 | 2582 | } |
michael@0 | 2583 | } |
michael@0 | 2584 | |
michael@0 | 2585 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 2586 | ("TSF: 0x%p nsTextStore::GetScreenExtInternal() succeeded: " |
michael@0 | 2587 | "aScreenExt={ left=%ld, top=%ld, right=%ld, bottom=%ld }", |
michael@0 | 2588 | this, aScreenExt.left, aScreenExt.top, |
michael@0 | 2589 | aScreenExt.right, aScreenExt.bottom)); |
michael@0 | 2590 | return true; |
michael@0 | 2591 | } |
michael@0 | 2592 | |
michael@0 | 2593 | STDMETHODIMP |
michael@0 | 2594 | nsTextStore::GetWnd(TsViewCookie vcView, |
michael@0 | 2595 | HWND *phwnd) |
michael@0 | 2596 | { |
michael@0 | 2597 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2598 | ("TSF: 0x%p nsTextStore::GetWnd(vcView=%ld, phwnd=0x%p), " |
michael@0 | 2599 | "mWidget=0x%p", |
michael@0 | 2600 | this, vcView, phwnd, mWidget.get())); |
michael@0 | 2601 | |
michael@0 | 2602 | if (vcView != TEXTSTORE_DEFAULT_VIEW) { |
michael@0 | 2603 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2604 | ("TSF: 0x%p nsTextStore::GetWnd() FAILED due to " |
michael@0 | 2605 | "called with invalid view", this)); |
michael@0 | 2606 | return E_INVALIDARG; |
michael@0 | 2607 | } |
michael@0 | 2608 | |
michael@0 | 2609 | if (!phwnd) { |
michael@0 | 2610 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2611 | ("TSF: 0x%p nsTextStore::GetScreenExt() FAILED due to " |
michael@0 | 2612 | "null argument", this)); |
michael@0 | 2613 | return E_INVALIDARG; |
michael@0 | 2614 | } |
michael@0 | 2615 | |
michael@0 | 2616 | *phwnd = mWidget->GetWindowHandle(); |
michael@0 | 2617 | |
michael@0 | 2618 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2619 | ("TSF: 0x%p nsTextStore::GetWnd() succeeded: *phwnd=0x%p", |
michael@0 | 2620 | this, static_cast<void*>(*phwnd))); |
michael@0 | 2621 | return S_OK; |
michael@0 | 2622 | } |
michael@0 | 2623 | |
michael@0 | 2624 | STDMETHODIMP |
michael@0 | 2625 | nsTextStore::InsertTextAtSelection(DWORD dwFlags, |
michael@0 | 2626 | const WCHAR *pchText, |
michael@0 | 2627 | ULONG cch, |
michael@0 | 2628 | LONG *pacpStart, |
michael@0 | 2629 | LONG *pacpEnd, |
michael@0 | 2630 | TS_TEXTCHANGE *pChange) |
michael@0 | 2631 | { |
michael@0 | 2632 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2633 | ("TSF: 0x%p nsTextStore::InsertTextAtSelection(dwFlags=%s, " |
michael@0 | 2634 | "pchText=0x%p \"%s\", cch=%lu, pacpStart=0x%p, pacpEnd=0x%p, " |
michael@0 | 2635 | "pChange=0x%p), IsComposing()=%s", |
michael@0 | 2636 | this, dwFlags == 0 ? "0" : |
michael@0 | 2637 | dwFlags == TF_IAS_NOQUERY ? "TF_IAS_NOQUERY" : |
michael@0 | 2638 | dwFlags == TF_IAS_QUERYONLY ? "TF_IAS_QUERYONLY" : "Unknown", |
michael@0 | 2639 | pchText, |
michael@0 | 2640 | pchText && cch ? NS_ConvertUTF16toUTF8(pchText, cch).get() : "", |
michael@0 | 2641 | cch, pacpStart, pacpEnd, pChange, |
michael@0 | 2642 | GetBoolName(mComposition.IsComposing()))); |
michael@0 | 2643 | |
michael@0 | 2644 | if (cch && !pchText) { |
michael@0 | 2645 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2646 | ("TSF: 0x%p nsTextStore::InsertTextAtSelection() FAILED due to " |
michael@0 | 2647 | "null pchText", this)); |
michael@0 | 2648 | return E_INVALIDARG; |
michael@0 | 2649 | } |
michael@0 | 2650 | |
michael@0 | 2651 | if (TS_IAS_QUERYONLY == dwFlags) { |
michael@0 | 2652 | if (!IsReadLocked()) { |
michael@0 | 2653 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2654 | ("TSF: 0x%p nsTextStore::InsertTextAtSelection() FAILED due to " |
michael@0 | 2655 | "not locked (read)", this)); |
michael@0 | 2656 | return TS_E_NOLOCK; |
michael@0 | 2657 | } |
michael@0 | 2658 | |
michael@0 | 2659 | if (!pacpStart || !pacpEnd) { |
michael@0 | 2660 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2661 | ("TSF: 0x%p nsTextStore::InsertTextAtSelection() FAILED due to " |
michael@0 | 2662 | "null argument", this)); |
michael@0 | 2663 | return E_INVALIDARG; |
michael@0 | 2664 | } |
michael@0 | 2665 | |
michael@0 | 2666 | // Get selection first |
michael@0 | 2667 | Selection& currentSel = CurrentSelection(); |
michael@0 | 2668 | if (currentSel.IsDirty()) { |
michael@0 | 2669 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2670 | ("TSF: 0x%p nsTextStore::InsertTextAtSelection() FAILED due to " |
michael@0 | 2671 | "CurrentSelection() failure", this)); |
michael@0 | 2672 | return E_FAIL; |
michael@0 | 2673 | } |
michael@0 | 2674 | |
michael@0 | 2675 | // Simulate text insertion |
michael@0 | 2676 | *pacpStart = currentSel.StartOffset(); |
michael@0 | 2677 | *pacpEnd = currentSel.EndOffset(); |
michael@0 | 2678 | if (pChange) { |
michael@0 | 2679 | pChange->acpStart = currentSel.StartOffset(); |
michael@0 | 2680 | pChange->acpOldEnd = currentSel.EndOffset(); |
michael@0 | 2681 | pChange->acpNewEnd = currentSel.StartOffset() + static_cast<LONG>(cch); |
michael@0 | 2682 | } |
michael@0 | 2683 | } else { |
michael@0 | 2684 | if (!IsReadWriteLocked()) { |
michael@0 | 2685 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2686 | ("TSF: 0x%p nsTextStore::InsertTextAtSelection() FAILED due to " |
michael@0 | 2687 | "not locked (read-write)", this)); |
michael@0 | 2688 | return TS_E_NOLOCK; |
michael@0 | 2689 | } |
michael@0 | 2690 | |
michael@0 | 2691 | if (!pChange) { |
michael@0 | 2692 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2693 | ("TSF: 0x%p nsTextStore::InsertTextAtSelection() FAILED due to " |
michael@0 | 2694 | "null pChange", this)); |
michael@0 | 2695 | return E_INVALIDARG; |
michael@0 | 2696 | } |
michael@0 | 2697 | |
michael@0 | 2698 | if (TS_IAS_NOQUERY != dwFlags && (!pacpStart || !pacpEnd)) { |
michael@0 | 2699 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2700 | ("TSF: 0x%p nsTextStore::InsertTextAtSelection() FAILED due to " |
michael@0 | 2701 | "null argument", this)); |
michael@0 | 2702 | return E_INVALIDARG; |
michael@0 | 2703 | } |
michael@0 | 2704 | |
michael@0 | 2705 | if (!InsertTextAtSelectionInternal(nsDependentSubstring(pchText, cch), |
michael@0 | 2706 | pChange)) { |
michael@0 | 2707 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2708 | ("TSF: 0x%p nsTextStore::InsertTextAtSelection() FAILED due to " |
michael@0 | 2709 | "InsertTextAtSelectionInternal() failure", this)); |
michael@0 | 2710 | return E_FAIL; |
michael@0 | 2711 | } |
michael@0 | 2712 | |
michael@0 | 2713 | if (TS_IAS_NOQUERY != dwFlags) { |
michael@0 | 2714 | *pacpStart = pChange->acpStart; |
michael@0 | 2715 | *pacpEnd = pChange->acpNewEnd; |
michael@0 | 2716 | } |
michael@0 | 2717 | } |
michael@0 | 2718 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2719 | ("TSF: 0x%p nsTextStore::InsertTextAtSelection() succeeded: " |
michael@0 | 2720 | "*pacpStart=%ld, *pacpEnd=%ld, " |
michael@0 | 2721 | "*pChange={ acpStart=%ld, acpOldEnd=%ld, acpNewEnd=%ld })", |
michael@0 | 2722 | this, pacpStart ? *pacpStart : 0, pacpEnd ? *pacpEnd : 0, |
michael@0 | 2723 | pChange ? pChange->acpStart: 0, pChange ? pChange->acpOldEnd : 0, |
michael@0 | 2724 | pChange ? pChange->acpNewEnd : 0)); |
michael@0 | 2725 | return S_OK; |
michael@0 | 2726 | } |
michael@0 | 2727 | |
michael@0 | 2728 | bool |
michael@0 | 2729 | nsTextStore::InsertTextAtSelectionInternal(const nsAString &aInsertStr, |
michael@0 | 2730 | TS_TEXTCHANGE* aTextChange) |
michael@0 | 2731 | { |
michael@0 | 2732 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 2733 | ("TSF: 0x%p nsTextStore::InsertTextAtSelectionInternal(" |
michael@0 | 2734 | "aInsertStr=\"%s\", aTextChange=0x%p), IsComposing=%s", |
michael@0 | 2735 | this, NS_ConvertUTF16toUTF8(aInsertStr).get(), aTextChange, |
michael@0 | 2736 | GetBoolName(mComposition.IsComposing()))); |
michael@0 | 2737 | |
michael@0 | 2738 | Content& currentContent = CurrentContent(); |
michael@0 | 2739 | if (!currentContent.IsInitialized()) { |
michael@0 | 2740 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2741 | ("TSF: 0x%p nsTextStore::InsertTextAtSelectionInternal() failed " |
michael@0 | 2742 | "due to CurrentContent() failure()", this)); |
michael@0 | 2743 | return false; |
michael@0 | 2744 | } |
michael@0 | 2745 | |
michael@0 | 2746 | TS_SELECTION_ACP oldSelection = currentContent.Selection().ACP(); |
michael@0 | 2747 | if (!mComposition.IsComposing()) { |
michael@0 | 2748 | // Use a temporary composition to contain the text |
michael@0 | 2749 | PendingAction* compositionStart = mPendingActions.AppendElement(); |
michael@0 | 2750 | compositionStart->mType = PendingAction::COMPOSITION_START; |
michael@0 | 2751 | compositionStart->mSelectionStart = oldSelection.acpStart; |
michael@0 | 2752 | compositionStart->mSelectionLength = |
michael@0 | 2753 | oldSelection.acpEnd - oldSelection.acpStart; |
michael@0 | 2754 | |
michael@0 | 2755 | PendingAction* compositionEnd = mPendingActions.AppendElement(); |
michael@0 | 2756 | compositionEnd->mType = PendingAction::COMPOSITION_END; |
michael@0 | 2757 | compositionEnd->mData = aInsertStr; |
michael@0 | 2758 | } |
michael@0 | 2759 | |
michael@0 | 2760 | currentContent.ReplaceSelectedTextWith(aInsertStr); |
michael@0 | 2761 | |
michael@0 | 2762 | if (aTextChange) { |
michael@0 | 2763 | aTextChange->acpStart = oldSelection.acpStart; |
michael@0 | 2764 | aTextChange->acpOldEnd = oldSelection.acpEnd; |
michael@0 | 2765 | aTextChange->acpNewEnd = currentContent.Selection().EndOffset(); |
michael@0 | 2766 | } |
michael@0 | 2767 | |
michael@0 | 2768 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 2769 | ("TSF: 0x%p nsTextStore::InsertTextAtSelectionInternal() succeeded: " |
michael@0 | 2770 | "mWidget=0x%p, mWidget->Destroyed()=%s, aTextChange={ acpStart=%ld, " |
michael@0 | 2771 | "acpOldEnd=%ld, acpNewEnd=%ld }", |
michael@0 | 2772 | this, mWidget.get(), |
michael@0 | 2773 | GetBoolName(mWidget ? mWidget->Destroyed() : true), |
michael@0 | 2774 | aTextChange ? aTextChange->acpStart : 0, |
michael@0 | 2775 | aTextChange ? aTextChange->acpOldEnd : 0, |
michael@0 | 2776 | aTextChange ? aTextChange->acpNewEnd : 0)); |
michael@0 | 2777 | return true; |
michael@0 | 2778 | } |
michael@0 | 2779 | |
michael@0 | 2780 | STDMETHODIMP |
michael@0 | 2781 | nsTextStore::InsertEmbeddedAtSelection(DWORD dwFlags, |
michael@0 | 2782 | IDataObject *pDataObject, |
michael@0 | 2783 | LONG *pacpStart, |
michael@0 | 2784 | LONG *pacpEnd, |
michael@0 | 2785 | TS_TEXTCHANGE *pChange) |
michael@0 | 2786 | { |
michael@0 | 2787 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2788 | ("TSF: 0x%p nsTextStore::InsertEmbeddedAtSelection() called " |
michael@0 | 2789 | "but not supported (E_NOTIMPL)", this)); |
michael@0 | 2790 | |
michael@0 | 2791 | // embedded objects are not supported |
michael@0 | 2792 | return E_NOTIMPL; |
michael@0 | 2793 | } |
michael@0 | 2794 | |
michael@0 | 2795 | HRESULT |
michael@0 | 2796 | nsTextStore::RecordCompositionStartAction(ITfCompositionView* pComposition, |
michael@0 | 2797 | ITfRange* aRange, |
michael@0 | 2798 | bool aPreserveSelection) |
michael@0 | 2799 | { |
michael@0 | 2800 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 2801 | ("TSF: 0x%p nsTextStore::RecordCompositionStartAction(" |
michael@0 | 2802 | "pComposition=0x%p, aRange=0x%p, aPreserveSelection=%s), " |
michael@0 | 2803 | "mComposition.mView=0x%p", |
michael@0 | 2804 | this, pComposition, aRange, GetBoolName(aPreserveSelection), |
michael@0 | 2805 | mComposition.mView.get())); |
michael@0 | 2806 | |
michael@0 | 2807 | LONG start = 0, length = 0; |
michael@0 | 2808 | HRESULT hr = GetRangeExtent(aRange, &start, &length); |
michael@0 | 2809 | if (FAILED(hr)) { |
michael@0 | 2810 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2811 | ("TSF: 0x%p nsTextStore::RecordCompositionStartAction() FAILED " |
michael@0 | 2812 | "due to GetRangeExtent() failure", this)); |
michael@0 | 2813 | return hr; |
michael@0 | 2814 | } |
michael@0 | 2815 | |
michael@0 | 2816 | Content& currentContent = CurrentContent(); |
michael@0 | 2817 | if (!currentContent.IsInitialized()) { |
michael@0 | 2818 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2819 | ("TSF: 0x%p nsTextStore::RecordCompositionStartAction() FAILED " |
michael@0 | 2820 | "due to CurrentContent() failure", this)); |
michael@0 | 2821 | return E_FAIL; |
michael@0 | 2822 | } |
michael@0 | 2823 | |
michael@0 | 2824 | PendingAction* action = mPendingActions.AppendElement(); |
michael@0 | 2825 | action->mType = PendingAction::COMPOSITION_START; |
michael@0 | 2826 | action->mSelectionStart = start; |
michael@0 | 2827 | action->mSelectionLength = length; |
michael@0 | 2828 | |
michael@0 | 2829 | currentContent.StartComposition(pComposition, *action, aPreserveSelection); |
michael@0 | 2830 | |
michael@0 | 2831 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2832 | ("TSF: 0x%p nsTextStore::RecordCompositionStartAction() succeeded: " |
michael@0 | 2833 | "mComposition={ mStart=%ld, mString.Length()=%ld, " |
michael@0 | 2834 | "mSelection={ acpStart=%ld, acpEnd=%ld, style.ase=%s, " |
michael@0 | 2835 | "style.fInterimChar=%s } }", |
michael@0 | 2836 | this, mComposition.mStart, mComposition.mString.Length(), |
michael@0 | 2837 | mSelection.StartOffset(), mSelection.EndOffset(), |
michael@0 | 2838 | GetActiveSelEndName(mSelection.ActiveSelEnd()), |
michael@0 | 2839 | GetBoolName(mSelection.IsInterimChar()))); |
michael@0 | 2840 | return S_OK; |
michael@0 | 2841 | } |
michael@0 | 2842 | |
michael@0 | 2843 | HRESULT |
michael@0 | 2844 | nsTextStore::RecordCompositionEndAction() |
michael@0 | 2845 | { |
michael@0 | 2846 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 2847 | ("TSF: 0x%p nsTextStore::RecordCompositionEndAction(), " |
michael@0 | 2848 | "mComposition={ mView=0x%p, mString=\"%s\" }", |
michael@0 | 2849 | this, mComposition.mView.get(), |
michael@0 | 2850 | NS_ConvertUTF16toUTF8(mComposition.mString).get())); |
michael@0 | 2851 | |
michael@0 | 2852 | MOZ_ASSERT(mComposition.IsComposing()); |
michael@0 | 2853 | |
michael@0 | 2854 | PendingAction* action = mPendingActions.AppendElement(); |
michael@0 | 2855 | action->mType = PendingAction::COMPOSITION_END; |
michael@0 | 2856 | action->mData = mComposition.mString; |
michael@0 | 2857 | |
michael@0 | 2858 | Content& currentContent = CurrentContent(); |
michael@0 | 2859 | if (!currentContent.IsInitialized()) { |
michael@0 | 2860 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2861 | ("TSF: 0x%p nsTextStore::RecordCompositionEndAction() FAILED due " |
michael@0 | 2862 | "to CurrentContent() failure", this)); |
michael@0 | 2863 | return E_FAIL; |
michael@0 | 2864 | } |
michael@0 | 2865 | currentContent.EndComposition(*action); |
michael@0 | 2866 | |
michael@0 | 2867 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2868 | ("TSF: 0x%p nsTextStore::RecordCompositionEndAction(), succeeded", |
michael@0 | 2869 | this)); |
michael@0 | 2870 | return S_OK; |
michael@0 | 2871 | } |
michael@0 | 2872 | |
michael@0 | 2873 | STDMETHODIMP |
michael@0 | 2874 | nsTextStore::OnStartComposition(ITfCompositionView* pComposition, |
michael@0 | 2875 | BOOL* pfOk) |
michael@0 | 2876 | { |
michael@0 | 2877 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2878 | ("TSF: 0x%p nsTextStore::OnStartComposition(pComposition=0x%p, " |
michael@0 | 2879 | "pfOk=0x%p), mComposition.mView=0x%p", |
michael@0 | 2880 | this, pComposition, pfOk, mComposition.mView.get())); |
michael@0 | 2881 | |
michael@0 | 2882 | AutoPendingActionAndContentFlusher flusher(this); |
michael@0 | 2883 | |
michael@0 | 2884 | *pfOk = FALSE; |
michael@0 | 2885 | |
michael@0 | 2886 | // Only one composition at a time |
michael@0 | 2887 | if (mComposition.IsComposing()) { |
michael@0 | 2888 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2889 | ("TSF: 0x%p nsTextStore::OnStartComposition() FAILED due to " |
michael@0 | 2890 | "there is another composition already (but returns S_OK)", this)); |
michael@0 | 2891 | return S_OK; |
michael@0 | 2892 | } |
michael@0 | 2893 | |
michael@0 | 2894 | nsRefPtr<ITfRange> range; |
michael@0 | 2895 | HRESULT hr = pComposition->GetRange(getter_AddRefs(range)); |
michael@0 | 2896 | if (FAILED(hr)) { |
michael@0 | 2897 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2898 | ("TSF: 0x%p nsTextStore::OnStartComposition() FAILED due to " |
michael@0 | 2899 | "pComposition->GetRange() failure", this)); |
michael@0 | 2900 | return hr; |
michael@0 | 2901 | } |
michael@0 | 2902 | hr = RecordCompositionStartAction(pComposition, range, false); |
michael@0 | 2903 | if (FAILED(hr)) { |
michael@0 | 2904 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2905 | ("TSF: 0x%p nsTextStore::OnStartComposition() FAILED due to " |
michael@0 | 2906 | "RecordCompositionStartAction() failure", this)); |
michael@0 | 2907 | return hr; |
michael@0 | 2908 | } |
michael@0 | 2909 | |
michael@0 | 2910 | *pfOk = TRUE; |
michael@0 | 2911 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2912 | ("TSF: 0x%p nsTextStore::OnStartComposition() succeeded", this)); |
michael@0 | 2913 | return S_OK; |
michael@0 | 2914 | } |
michael@0 | 2915 | |
michael@0 | 2916 | STDMETHODIMP |
michael@0 | 2917 | nsTextStore::OnUpdateComposition(ITfCompositionView* pComposition, |
michael@0 | 2918 | ITfRange* pRangeNew) |
michael@0 | 2919 | { |
michael@0 | 2920 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2921 | ("TSF: 0x%p nsTextStore::OnUpdateComposition(pComposition=0x%p, " |
michael@0 | 2922 | "pRangeNew=0x%p), mComposition.mView=0x%p", |
michael@0 | 2923 | this, pComposition, pRangeNew, mComposition.mView.get())); |
michael@0 | 2924 | |
michael@0 | 2925 | AutoPendingActionAndContentFlusher flusher(this); |
michael@0 | 2926 | |
michael@0 | 2927 | if (!mDocumentMgr || !mContext) { |
michael@0 | 2928 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2929 | ("TSF: 0x%p nsTextStore::OnUpdateComposition() FAILED due to " |
michael@0 | 2930 | "not ready for the composition", this)); |
michael@0 | 2931 | return E_UNEXPECTED; |
michael@0 | 2932 | } |
michael@0 | 2933 | if (!mComposition.IsComposing()) { |
michael@0 | 2934 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2935 | ("TSF: 0x%p nsTextStore::OnUpdateComposition() FAILED due to " |
michael@0 | 2936 | "no active composition", this)); |
michael@0 | 2937 | return E_UNEXPECTED; |
michael@0 | 2938 | } |
michael@0 | 2939 | if (mComposition.mView != pComposition) { |
michael@0 | 2940 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2941 | ("TSF: 0x%p nsTextStore::OnUpdateComposition() FAILED due to " |
michael@0 | 2942 | "different composition view specified", this)); |
michael@0 | 2943 | return E_UNEXPECTED; |
michael@0 | 2944 | } |
michael@0 | 2945 | |
michael@0 | 2946 | // pRangeNew is null when the update is not complete |
michael@0 | 2947 | if (!pRangeNew) { |
michael@0 | 2948 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2949 | ("TSF: 0x%p nsTextStore::OnUpdateComposition() succeeded but " |
michael@0 | 2950 | "not complete", this)); |
michael@0 | 2951 | return S_OK; |
michael@0 | 2952 | } |
michael@0 | 2953 | |
michael@0 | 2954 | HRESULT hr = RestartCompositionIfNecessary(pRangeNew); |
michael@0 | 2955 | if (FAILED(hr)) { |
michael@0 | 2956 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2957 | ("TSF: 0x%p nsTextStore::OnUpdateComposition() FAILED due to " |
michael@0 | 2958 | "RestartCompositionIfNecessary() failure", this)); |
michael@0 | 2959 | return hr; |
michael@0 | 2960 | } |
michael@0 | 2961 | |
michael@0 | 2962 | hr = RecordCompositionUpdateAction(); |
michael@0 | 2963 | if (FAILED(hr)) { |
michael@0 | 2964 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2965 | ("TSF: 0x%p nsTextStore::OnUpdateComposition() FAILED due to " |
michael@0 | 2966 | "RecordCompositionUpdateAction() failure", this)); |
michael@0 | 2967 | return hr; |
michael@0 | 2968 | } |
michael@0 | 2969 | |
michael@0 | 2970 | #ifdef PR_LOGGING |
michael@0 | 2971 | if (PR_LOG_TEST(sTextStoreLog, PR_LOG_ALWAYS)) { |
michael@0 | 2972 | Selection& currentSel = CurrentSelection(); |
michael@0 | 2973 | if (currentSel.IsDirty()) { |
michael@0 | 2974 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 2975 | ("TSF: 0x%p nsTextStore::OnUpdateComposition() FAILED due to " |
michael@0 | 2976 | "CurrentSelection() failure", this)); |
michael@0 | 2977 | return E_FAIL; |
michael@0 | 2978 | } |
michael@0 | 2979 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2980 | ("TSF: 0x%p nsTextStore::OnUpdateComposition() succeeded: " |
michael@0 | 2981 | "mComposition={ mStart=%ld, mString=\"%s\" }, " |
michael@0 | 2982 | "CurrentSelection()={ acpStart=%ld, acpEnd=%ld, style.ase=%s }", |
michael@0 | 2983 | this, mComposition.mStart, |
michael@0 | 2984 | NS_ConvertUTF16toUTF8(mComposition.mString).get(), |
michael@0 | 2985 | currentSel.StartOffset(), currentSel.EndOffset(), |
michael@0 | 2986 | GetActiveSelEndName(currentSel.ActiveSelEnd()))); |
michael@0 | 2987 | } |
michael@0 | 2988 | #endif // #ifdef PR_LOGGING |
michael@0 | 2989 | return S_OK; |
michael@0 | 2990 | } |
michael@0 | 2991 | |
michael@0 | 2992 | STDMETHODIMP |
michael@0 | 2993 | nsTextStore::OnEndComposition(ITfCompositionView* pComposition) |
michael@0 | 2994 | { |
michael@0 | 2995 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 2996 | ("TSF: 0x%p nsTextStore::OnEndComposition(pComposition=0x%p), " |
michael@0 | 2997 | "mComposition={ mView=0x%p, mString=\"%s\" }", |
michael@0 | 2998 | this, pComposition, mComposition.mView.get(), |
michael@0 | 2999 | NS_ConvertUTF16toUTF8(mComposition.mString).get())); |
michael@0 | 3000 | |
michael@0 | 3001 | AutoPendingActionAndContentFlusher flusher(this); |
michael@0 | 3002 | |
michael@0 | 3003 | if (!mComposition.IsComposing()) { |
michael@0 | 3004 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3005 | ("TSF: 0x%p nsTextStore::OnEndComposition() FAILED due to " |
michael@0 | 3006 | "no active composition", this)); |
michael@0 | 3007 | return E_UNEXPECTED; |
michael@0 | 3008 | } |
michael@0 | 3009 | |
michael@0 | 3010 | if (mComposition.mView != pComposition) { |
michael@0 | 3011 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3012 | ("TSF: 0x%p nsTextStore::OnEndComposition() FAILED due to " |
michael@0 | 3013 | "different composition view specified", this)); |
michael@0 | 3014 | return E_UNEXPECTED; |
michael@0 | 3015 | } |
michael@0 | 3016 | |
michael@0 | 3017 | HRESULT hr = RecordCompositionEndAction(); |
michael@0 | 3018 | if (FAILED(hr)) { |
michael@0 | 3019 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3020 | ("TSF: 0x%p nsTextStore::OnEndComposition() FAILED due to " |
michael@0 | 3021 | "RecordCompositionEndAction() failure", this)); |
michael@0 | 3022 | return hr; |
michael@0 | 3023 | } |
michael@0 | 3024 | |
michael@0 | 3025 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 3026 | ("TSF: 0x%p nsTextStore::OnEndComposition(), succeeded", this)); |
michael@0 | 3027 | return S_OK; |
michael@0 | 3028 | } |
michael@0 | 3029 | |
michael@0 | 3030 | STDMETHODIMP |
michael@0 | 3031 | nsTextStore::OnActivated(REFCLSID clsid, REFGUID guidProfile, |
michael@0 | 3032 | BOOL fActivated) |
michael@0 | 3033 | { |
michael@0 | 3034 | // NOTE: This is installed only on XP or Server 2003. |
michael@0 | 3035 | if (fActivated) { |
michael@0 | 3036 | // TODO: We should check if the profile's category is keyboard or not. |
michael@0 | 3037 | mOnActivatedCalled = true; |
michael@0 | 3038 | mIsIMM_IME = IsIMM_IME(::GetKeyboardLayout(0)); |
michael@0 | 3039 | |
michael@0 | 3040 | LANGID langID; |
michael@0 | 3041 | HRESULT hr = sInputProcessorProfiles->GetCurrentLanguage(&langID); |
michael@0 | 3042 | if (FAILED(hr)) { |
michael@0 | 3043 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3044 | ("TSF: nsTextStore::OnActivated() FAILED due to " |
michael@0 | 3045 | "GetCurrentLanguage() failure, hr=0x%08X", hr)); |
michael@0 | 3046 | } else if (IsTIPCategoryKeyboard(clsid, langID, guidProfile)) { |
michael@0 | 3047 | GetTIPDescription(clsid, langID, guidProfile, |
michael@0 | 3048 | mActiveTIPKeyboardDescription); |
michael@0 | 3049 | } else if (clsid == CLSID_NULL || guidProfile == GUID_NULL) { |
michael@0 | 3050 | // Perhaps, this case is that keyboard layout without TIP is activated. |
michael@0 | 3051 | mActiveTIPKeyboardDescription.Truncate(); |
michael@0 | 3052 | } |
michael@0 | 3053 | } |
michael@0 | 3054 | |
michael@0 | 3055 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 3056 | ("TSF: 0x%p nsTextStore::OnActivated(rclsid=%s, guidProfile=%s, " |
michael@0 | 3057 | "fActivated=%s), mIsIMM_IME=%s, mActiveTIPDescription=\"%s\"", |
michael@0 | 3058 | this, GetCLSIDNameStr(clsid).get(), |
michael@0 | 3059 | GetGUIDNameStr(guidProfile).get(), GetBoolName(fActivated), |
michael@0 | 3060 | GetBoolName(mIsIMM_IME), |
michael@0 | 3061 | NS_ConvertUTF16toUTF8(mActiveTIPKeyboardDescription).get())); |
michael@0 | 3062 | return S_OK; |
michael@0 | 3063 | } |
michael@0 | 3064 | |
michael@0 | 3065 | STDMETHODIMP |
michael@0 | 3066 | nsTextStore::OnActivated(DWORD dwProfileType, |
michael@0 | 3067 | LANGID langid, |
michael@0 | 3068 | REFCLSID rclsid, |
michael@0 | 3069 | REFGUID catid, |
michael@0 | 3070 | REFGUID guidProfile, |
michael@0 | 3071 | HKL hkl, |
michael@0 | 3072 | DWORD dwFlags) |
michael@0 | 3073 | { |
michael@0 | 3074 | // NOTE: This is installed only on Vista or later. However, this may be |
michael@0 | 3075 | // called by EnsureInitActiveLanguageProfile() even on XP or Server |
michael@0 | 3076 | // 2003. |
michael@0 | 3077 | if ((dwFlags & TF_IPSINK_FLAG_ACTIVE) && |
michael@0 | 3078 | (dwProfileType == TF_PROFILETYPE_KEYBOARDLAYOUT || |
michael@0 | 3079 | catid == GUID_TFCAT_TIP_KEYBOARD)) { |
michael@0 | 3080 | mOnActivatedCalled = true; |
michael@0 | 3081 | mIsIMM_IME = IsIMM_IME(hkl); |
michael@0 | 3082 | GetTIPDescription(rclsid, langid, guidProfile, |
michael@0 | 3083 | mActiveTIPKeyboardDescription); |
michael@0 | 3084 | } |
michael@0 | 3085 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 3086 | ("TSF: 0x%p nsTextStore::OnActivated(dwProfileType=%s (0x%08X), " |
michael@0 | 3087 | "langid=0x%08X, rclsid=%s, catid=%s, guidProfile=%s, hkl=0x%08X, " |
michael@0 | 3088 | "dwFlags=0x%08X (TF_IPSINK_FLAG_ACTIVE: %s)), mIsIMM_IME=%s, " |
michael@0 | 3089 | "mActiveTIPDescription=\"%s\"", |
michael@0 | 3090 | this, dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR ? |
michael@0 | 3091 | "TF_PROFILETYPE_INPUTPROCESSOR" : |
michael@0 | 3092 | dwProfileType == TF_PROFILETYPE_KEYBOARDLAYOUT ? |
michael@0 | 3093 | "TF_PROFILETYPE_KEYBOARDLAYOUT" : "Unknown", dwProfileType, |
michael@0 | 3094 | langid, GetCLSIDNameStr(rclsid).get(), GetGUIDNameStr(catid).get(), |
michael@0 | 3095 | GetGUIDNameStr(guidProfile).get(), hkl, dwFlags, |
michael@0 | 3096 | GetBoolName(dwFlags & TF_IPSINK_FLAG_ACTIVE), |
michael@0 | 3097 | GetBoolName(mIsIMM_IME), |
michael@0 | 3098 | NS_ConvertUTF16toUTF8(mActiveTIPKeyboardDescription).get())); |
michael@0 | 3099 | return S_OK; |
michael@0 | 3100 | } |
michael@0 | 3101 | |
michael@0 | 3102 | // static |
michael@0 | 3103 | nsresult |
michael@0 | 3104 | nsTextStore::OnFocusChange(bool aGotFocus, |
michael@0 | 3105 | nsWindowBase* aFocusedWidget, |
michael@0 | 3106 | IMEState::Enabled aIMEEnabled) |
michael@0 | 3107 | { |
michael@0 | 3108 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 3109 | ("TSF: nsTextStore::OnFocusChange(aGotFocus=%s, " |
michael@0 | 3110 | "aFocusedWidget=0x%p, aIMEEnabled=%s), sTsfThreadMgr=0x%p, " |
michael@0 | 3111 | "sTsfTextStore=0x%p", |
michael@0 | 3112 | GetBoolName(aGotFocus), aFocusedWidget, |
michael@0 | 3113 | GetIMEEnabledName(aIMEEnabled), sTsfThreadMgr, sTsfTextStore)); |
michael@0 | 3114 | |
michael@0 | 3115 | // no change notifications if TSF is disabled |
michael@0 | 3116 | NS_ENSURE_TRUE(sTsfThreadMgr && sTsfTextStore, NS_ERROR_NOT_AVAILABLE); |
michael@0 | 3117 | |
michael@0 | 3118 | nsRefPtr<ITfDocumentMgr> prevFocusedDocumentMgr; |
michael@0 | 3119 | if (aGotFocus && (aIMEEnabled == IMEState::ENABLED || |
michael@0 | 3120 | aIMEEnabled == IMEState::PASSWORD)) { |
michael@0 | 3121 | bool bRet = sTsfTextStore->Create(aFocusedWidget); |
michael@0 | 3122 | NS_ENSURE_TRUE(bRet, NS_ERROR_FAILURE); |
michael@0 | 3123 | NS_ENSURE_TRUE(sTsfTextStore->mDocumentMgr, NS_ERROR_FAILURE); |
michael@0 | 3124 | if (aIMEEnabled == IMEState::PASSWORD) { |
michael@0 | 3125 | MarkContextAsKeyboardDisabled(sTsfTextStore->mContext); |
michael@0 | 3126 | nsRefPtr<ITfContext> topContext; |
michael@0 | 3127 | sTsfTextStore->mDocumentMgr->GetTop(getter_AddRefs(topContext)); |
michael@0 | 3128 | if (topContext && topContext != sTsfTextStore->mContext) { |
michael@0 | 3129 | MarkContextAsKeyboardDisabled(topContext); |
michael@0 | 3130 | } |
michael@0 | 3131 | } |
michael@0 | 3132 | HRESULT hr = sTsfThreadMgr->SetFocus(sTsfTextStore->mDocumentMgr); |
michael@0 | 3133 | NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); |
michael@0 | 3134 | // Use AssociateFocus() for ensuring that any native focus event |
michael@0 | 3135 | // never steal focus from our documentMgr. |
michael@0 | 3136 | hr = sTsfThreadMgr->AssociateFocus(aFocusedWidget->GetWindowHandle(), |
michael@0 | 3137 | sTsfTextStore->mDocumentMgr, |
michael@0 | 3138 | getter_AddRefs(prevFocusedDocumentMgr)); |
michael@0 | 3139 | NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); |
michael@0 | 3140 | } else { |
michael@0 | 3141 | if (ThinksHavingFocus()) { |
michael@0 | 3142 | DebugOnly<HRESULT> hr = sTsfThreadMgr->AssociateFocus( |
michael@0 | 3143 | sTsfTextStore->mWidget->GetWindowHandle(), |
michael@0 | 3144 | nullptr, getter_AddRefs(prevFocusedDocumentMgr)); |
michael@0 | 3145 | NS_ASSERTION(SUCCEEDED(hr), "Disassociating focus failed"); |
michael@0 | 3146 | NS_ASSERTION(prevFocusedDocumentMgr == sTsfTextStore->mDocumentMgr, |
michael@0 | 3147 | "different documentMgr has been associated with the window"); |
michael@0 | 3148 | sTsfTextStore->Destroy(); |
michael@0 | 3149 | } |
michael@0 | 3150 | HRESULT hr = sTsfThreadMgr->SetFocus(sTsfDisabledDocumentMgr); |
michael@0 | 3151 | NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); |
michael@0 | 3152 | } |
michael@0 | 3153 | return NS_OK; |
michael@0 | 3154 | } |
michael@0 | 3155 | |
michael@0 | 3156 | // static |
michael@0 | 3157 | nsIMEUpdatePreference |
michael@0 | 3158 | nsTextStore::GetIMEUpdatePreference() |
michael@0 | 3159 | { |
michael@0 | 3160 | if (sTsfThreadMgr && sTsfTextStore && sTsfTextStore->mDocumentMgr) { |
michael@0 | 3161 | nsRefPtr<ITfDocumentMgr> docMgr; |
michael@0 | 3162 | sTsfThreadMgr->GetFocus(getter_AddRefs(docMgr)); |
michael@0 | 3163 | if (docMgr == sTsfTextStore->mDocumentMgr) { |
michael@0 | 3164 | nsIMEUpdatePreference updatePreference( |
michael@0 | 3165 | nsIMEUpdatePreference::NOTIFY_SELECTION_CHANGE | |
michael@0 | 3166 | nsIMEUpdatePreference::NOTIFY_TEXT_CHANGE | |
michael@0 | 3167 | nsIMEUpdatePreference::NOTIFY_POSITION_CHANGE | |
michael@0 | 3168 | nsIMEUpdatePreference::NOTIFY_DURING_DEACTIVE); |
michael@0 | 3169 | // nsTextStore shouldn't notify TSF of selection change and text change |
michael@0 | 3170 | // which are caused by composition. |
michael@0 | 3171 | updatePreference.DontNotifyChangesCausedByComposition(); |
michael@0 | 3172 | return updatePreference; |
michael@0 | 3173 | } |
michael@0 | 3174 | } |
michael@0 | 3175 | return nsIMEUpdatePreference(); |
michael@0 | 3176 | } |
michael@0 | 3177 | |
michael@0 | 3178 | nsresult |
michael@0 | 3179 | nsTextStore::OnTextChangeInternal(const IMENotification& aIMENotification) |
michael@0 | 3180 | { |
michael@0 | 3181 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 3182 | ("TSF: 0x%p nsTextStore::OnTextChangeInternal(aIMENotification={ " |
michael@0 | 3183 | "mMessage=0x%08X, mTextChangeData={ mStartOffset=%lu, " |
michael@0 | 3184 | "mOldEndOffset=%lu, mNewEndOffset=%lu}), mSink=0x%p, mSinkMask=%s, " |
michael@0 | 3185 | "mComposition.IsComposing()=%s", |
michael@0 | 3186 | this, aIMENotification.mMessage, |
michael@0 | 3187 | aIMENotification.mTextChangeData.mStartOffset, |
michael@0 | 3188 | aIMENotification.mTextChangeData.mOldEndOffset, |
michael@0 | 3189 | aIMENotification.mTextChangeData.mNewEndOffset, mSink.get(), |
michael@0 | 3190 | GetSinkMaskNameStr(mSinkMask).get(), |
michael@0 | 3191 | GetBoolName(mComposition.IsComposing()))); |
michael@0 | 3192 | |
michael@0 | 3193 | if (IsReadLocked()) { |
michael@0 | 3194 | return NS_OK; |
michael@0 | 3195 | } |
michael@0 | 3196 | |
michael@0 | 3197 | mSelection.MarkDirty(); |
michael@0 | 3198 | |
michael@0 | 3199 | if (!mSink || !(mSinkMask & TS_AS_TEXT_CHANGE)) { |
michael@0 | 3200 | return NS_OK; |
michael@0 | 3201 | } |
michael@0 | 3202 | |
michael@0 | 3203 | if (!aIMENotification.mTextChangeData.IsInInt32Range()) { |
michael@0 | 3204 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3205 | ("TSF: 0x%p nsTextStore::OnTextChangeInternal() FAILED due to " |
michael@0 | 3206 | "offset is too big for calling mSink->OnTextChange()...", |
michael@0 | 3207 | this)); |
michael@0 | 3208 | return NS_OK; |
michael@0 | 3209 | } |
michael@0 | 3210 | |
michael@0 | 3211 | // Some TIPs are confused by text change notification during composition. |
michael@0 | 3212 | // Especially, some of them stop working for composition in our process. |
michael@0 | 3213 | // For preventing it, let's commit the composition. |
michael@0 | 3214 | if (mComposition.IsComposing()) { |
michael@0 | 3215 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 3216 | ("TSF: 0x%p nsTextStore::OnTextChangeInternal(), " |
michael@0 | 3217 | "committing the composition for avoiding making TIP confused...", |
michael@0 | 3218 | this)); |
michael@0 | 3219 | CommitCompositionInternal(false); |
michael@0 | 3220 | return NS_OK; |
michael@0 | 3221 | } |
michael@0 | 3222 | |
michael@0 | 3223 | TS_TEXTCHANGE textChange; |
michael@0 | 3224 | textChange.acpStart = |
michael@0 | 3225 | static_cast<LONG>(aIMENotification.mTextChangeData.mStartOffset); |
michael@0 | 3226 | textChange.acpOldEnd = |
michael@0 | 3227 | static_cast<LONG>(aIMENotification.mTextChangeData.mOldEndOffset); |
michael@0 | 3228 | textChange.acpNewEnd = |
michael@0 | 3229 | static_cast<LONG>(aIMENotification.mTextChangeData.mNewEndOffset); |
michael@0 | 3230 | |
michael@0 | 3231 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 3232 | ("TSF: 0x%p nsTextStore::OnTextChangeInternal(), calling " |
michael@0 | 3233 | "mSink->OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, " |
michael@0 | 3234 | "acpNewEnd=%ld })...", this, textChange.acpStart, |
michael@0 | 3235 | textChange.acpOldEnd, textChange.acpNewEnd)); |
michael@0 | 3236 | mSink->OnTextChange(0, &textChange); |
michael@0 | 3237 | |
michael@0 | 3238 | return NS_OK; |
michael@0 | 3239 | } |
michael@0 | 3240 | |
michael@0 | 3241 | nsresult |
michael@0 | 3242 | nsTextStore::OnSelectionChangeInternal(void) |
michael@0 | 3243 | { |
michael@0 | 3244 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 3245 | ("TSF: 0x%p nsTextStore::OnSelectionChangeInternal(), " |
michael@0 | 3246 | "mSink=0x%p, mSinkMask=%s, mIsRecordingActionsWithoutLock=%s, " |
michael@0 | 3247 | "mComposition.IsComposing()=%s", |
michael@0 | 3248 | this, mSink.get(), GetSinkMaskNameStr(mSinkMask).get(), |
michael@0 | 3249 | GetBoolName(mIsRecordingActionsWithoutLock), |
michael@0 | 3250 | GetBoolName(mComposition.IsComposing()))); |
michael@0 | 3251 | |
michael@0 | 3252 | if (IsReadLocked()) { |
michael@0 | 3253 | return NS_OK; |
michael@0 | 3254 | } |
michael@0 | 3255 | |
michael@0 | 3256 | mSelection.MarkDirty(); |
michael@0 | 3257 | |
michael@0 | 3258 | if (!mSink || !(mSinkMask & TS_AS_SEL_CHANGE)) { |
michael@0 | 3259 | return NS_OK; |
michael@0 | 3260 | } |
michael@0 | 3261 | |
michael@0 | 3262 | // Some TIPs are confused by selection change notification during composition. |
michael@0 | 3263 | // Especially, some of them stop working for composition in our process. |
michael@0 | 3264 | // For preventing it, let's commit the composition. |
michael@0 | 3265 | if (mComposition.IsComposing()) { |
michael@0 | 3266 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 3267 | ("TSF: 0x%p nsTextStore::OnSelectionChangeInternal(), " |
michael@0 | 3268 | "committing the composition for avoiding making TIP confused...", |
michael@0 | 3269 | this)); |
michael@0 | 3270 | CommitCompositionInternal(false); |
michael@0 | 3271 | return NS_OK; |
michael@0 | 3272 | } |
michael@0 | 3273 | |
michael@0 | 3274 | if (!mIsRecordingActionsWithoutLock) { |
michael@0 | 3275 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 3276 | ("TSF: 0x%p nsTextStore::OnSelectionChangeInternal(), calling " |
michael@0 | 3277 | "mSink->OnSelectionChange()...", this)); |
michael@0 | 3278 | mSink->OnSelectionChange(); |
michael@0 | 3279 | } else { |
michael@0 | 3280 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 3281 | ("TSF: 0x%p nsTextStore::OnSelectionChangeInternal(), pending " |
michael@0 | 3282 | "a call of mSink->OnSelectionChange()...", this)); |
michael@0 | 3283 | mPendingOnSelectionChange = true; |
michael@0 | 3284 | } |
michael@0 | 3285 | return NS_OK; |
michael@0 | 3286 | } |
michael@0 | 3287 | |
michael@0 | 3288 | nsresult |
michael@0 | 3289 | nsTextStore::OnLayoutChangeInternal() |
michael@0 | 3290 | { |
michael@0 | 3291 | NS_ENSURE_TRUE(mContext, NS_ERROR_FAILURE); |
michael@0 | 3292 | NS_ENSURE_TRUE(mSink, NS_ERROR_FAILURE); |
michael@0 | 3293 | |
michael@0 | 3294 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 3295 | ("TSF: 0x%p nsTextStore::OnLayoutChangeInternal(), calling " |
michael@0 | 3296 | "mSink->OnLayoutChange()...", this)); |
michael@0 | 3297 | HRESULT hr = mSink->OnLayoutChange(TS_LC_CHANGE, TEXTSTORE_DEFAULT_VIEW); |
michael@0 | 3298 | NS_ENSURE_TRUE(SUCCEEDED(hr), NS_ERROR_FAILURE); |
michael@0 | 3299 | |
michael@0 | 3300 | return NS_OK; |
michael@0 | 3301 | } |
michael@0 | 3302 | |
michael@0 | 3303 | void |
michael@0 | 3304 | nsTextStore::CreateNativeCaret() |
michael@0 | 3305 | { |
michael@0 | 3306 | // This method must work only on desktop application. |
michael@0 | 3307 | if (XRE_GetWindowsEnvironment() != WindowsEnvironmentType_Desktop) { |
michael@0 | 3308 | return; |
michael@0 | 3309 | } |
michael@0 | 3310 | |
michael@0 | 3311 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 3312 | ("TSF: 0x%p nsTextStore::CreateNativeCaret(), " |
michael@0 | 3313 | "mComposition.IsComposing()=%s", |
michael@0 | 3314 | this, GetBoolName(mComposition.IsComposing()))); |
michael@0 | 3315 | |
michael@0 | 3316 | Selection& currentSel = CurrentSelection(); |
michael@0 | 3317 | if (currentSel.IsDirty()) { |
michael@0 | 3318 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3319 | ("TSF: 0x%p nsTextStore::CreateNativeCaret() FAILED due to " |
michael@0 | 3320 | "CurrentSelection() failure", this)); |
michael@0 | 3321 | return; |
michael@0 | 3322 | } |
michael@0 | 3323 | |
michael@0 | 3324 | // XXX If this is called without composition and the selection isn't |
michael@0 | 3325 | // collapsed, is it OK? |
michael@0 | 3326 | uint32_t caretOffset = currentSel.MaxOffset(); |
michael@0 | 3327 | |
michael@0 | 3328 | WidgetQueryContentEvent queryCaretRect(true, NS_QUERY_CARET_RECT, mWidget); |
michael@0 | 3329 | queryCaretRect.InitForQueryCaretRect(caretOffset); |
michael@0 | 3330 | mWidget->InitEvent(queryCaretRect); |
michael@0 | 3331 | mWidget->DispatchWindowEvent(&queryCaretRect); |
michael@0 | 3332 | if (!queryCaretRect.mSucceeded) { |
michael@0 | 3333 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3334 | ("TSF: 0x%p nsTextStore::CreateNativeCaret() FAILED due to " |
michael@0 | 3335 | "NS_QUERY_CARET_RECT failure (offset=%d)", this, caretOffset)); |
michael@0 | 3336 | return; |
michael@0 | 3337 | } |
michael@0 | 3338 | |
michael@0 | 3339 | nsIntRect& caretRect = queryCaretRect.mReply.mRect; |
michael@0 | 3340 | mNativeCaretIsCreated = ::CreateCaret(mWidget->GetWindowHandle(), nullptr, |
michael@0 | 3341 | caretRect.width, caretRect.height); |
michael@0 | 3342 | if (!mNativeCaretIsCreated) { |
michael@0 | 3343 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3344 | ("TSF: 0x%p nsTextStore::CreateNativeCaret() FAILED due to " |
michael@0 | 3345 | "CreateCaret() failure", this)); |
michael@0 | 3346 | return; |
michael@0 | 3347 | } |
michael@0 | 3348 | |
michael@0 | 3349 | nsWindow* window = static_cast<nsWindow*>(mWidget.get()); |
michael@0 | 3350 | nsWindow* toplevelWindow = window->GetTopLevelWindow(false); |
michael@0 | 3351 | if (!toplevelWindow) { |
michael@0 | 3352 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3353 | ("TSF: 0x%p nsTextStore::CreateNativeCaret() FAILED due to " |
michael@0 | 3354 | "no top level window", this)); |
michael@0 | 3355 | return; |
michael@0 | 3356 | } |
michael@0 | 3357 | |
michael@0 | 3358 | if (toplevelWindow != window) { |
michael@0 | 3359 | caretRect.MoveBy(toplevelWindow->WidgetToScreenOffset()); |
michael@0 | 3360 | caretRect.MoveBy(-window->WidgetToScreenOffset()); |
michael@0 | 3361 | } |
michael@0 | 3362 | |
michael@0 | 3363 | ::SetCaretPos(caretRect.x, caretRect.y); |
michael@0 | 3364 | } |
michael@0 | 3365 | |
michael@0 | 3366 | bool |
michael@0 | 3367 | nsTextStore::EnsureInitActiveTIPKeyboard() |
michael@0 | 3368 | { |
michael@0 | 3369 | if (mOnActivatedCalled) { |
michael@0 | 3370 | return true; |
michael@0 | 3371 | } |
michael@0 | 3372 | |
michael@0 | 3373 | if (IsVistaOrLater()) { |
michael@0 | 3374 | nsRefPtr<ITfInputProcessorProfileMgr> profileMgr; |
michael@0 | 3375 | HRESULT hr = |
michael@0 | 3376 | sInputProcessorProfiles->QueryInterface(IID_ITfInputProcessorProfileMgr, |
michael@0 | 3377 | getter_AddRefs(profileMgr)); |
michael@0 | 3378 | if (FAILED(hr) || !profileMgr) { |
michael@0 | 3379 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3380 | ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), FAILED " |
michael@0 | 3381 | "to get input processor profile manager, hr=0x%08X", this, hr)); |
michael@0 | 3382 | return false; |
michael@0 | 3383 | } |
michael@0 | 3384 | |
michael@0 | 3385 | TF_INPUTPROCESSORPROFILE profile; |
michael@0 | 3386 | hr = profileMgr->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, &profile); |
michael@0 | 3387 | if (hr == S_FALSE) { |
michael@0 | 3388 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 3389 | ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), FAILED " |
michael@0 | 3390 | "to get active keyboard layout profile due to no active profile, " |
michael@0 | 3391 | "hr=0x%08X", this, hr)); |
michael@0 | 3392 | // XXX Should we call OnActivated() with arguments like non-TIP in this |
michael@0 | 3393 | // case? |
michael@0 | 3394 | return false; |
michael@0 | 3395 | } |
michael@0 | 3396 | if (FAILED(hr)) { |
michael@0 | 3397 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3398 | ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), FAILED " |
michael@0 | 3399 | "to get active TIP keyboard, hr=0x%08X", this, hr)); |
michael@0 | 3400 | return false; |
michael@0 | 3401 | } |
michael@0 | 3402 | |
michael@0 | 3403 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 3404 | ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), " |
michael@0 | 3405 | "calling OnActivated() manually...", this)); |
michael@0 | 3406 | OnActivated(profile.dwProfileType, profile.langid, profile.clsid, |
michael@0 | 3407 | profile.catid, profile.guidProfile, ::GetKeyboardLayout(0), |
michael@0 | 3408 | TF_IPSINK_FLAG_ACTIVE); |
michael@0 | 3409 | return true; |
michael@0 | 3410 | } |
michael@0 | 3411 | |
michael@0 | 3412 | LANGID langID; |
michael@0 | 3413 | HRESULT hr = sInputProcessorProfiles->GetCurrentLanguage(&langID); |
michael@0 | 3414 | if (FAILED(hr)) { |
michael@0 | 3415 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3416 | ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), FAILED " |
michael@0 | 3417 | "to get current language ID, hr=0x%08X", this, hr)); |
michael@0 | 3418 | return false; |
michael@0 | 3419 | } |
michael@0 | 3420 | |
michael@0 | 3421 | nsRefPtr<IEnumTfLanguageProfiles> enumLangProfiles; |
michael@0 | 3422 | hr = sInputProcessorProfiles->EnumLanguageProfiles(langID, |
michael@0 | 3423 | getter_AddRefs(enumLangProfiles)); |
michael@0 | 3424 | if (FAILED(hr) || !enumLangProfiles) { |
michael@0 | 3425 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3426 | ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), FAILED " |
michael@0 | 3427 | "to get language profiles enumerator, hr=0x%08X", this, hr)); |
michael@0 | 3428 | return false; |
michael@0 | 3429 | } |
michael@0 | 3430 | |
michael@0 | 3431 | TF_LANGUAGEPROFILE profile; |
michael@0 | 3432 | ULONG fetch = 0; |
michael@0 | 3433 | while (SUCCEEDED(enumLangProfiles->Next(1, &profile, &fetch)) && fetch) { |
michael@0 | 3434 | if (!profile.fActive || profile.catid != GUID_TFCAT_TIP_KEYBOARD) { |
michael@0 | 3435 | continue; |
michael@0 | 3436 | } |
michael@0 | 3437 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 3438 | ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), " |
michael@0 | 3439 | "calling OnActivated() manually...", this)); |
michael@0 | 3440 | bool isTIP = profile.guidProfile != GUID_NULL; |
michael@0 | 3441 | OnActivated(isTIP ? TF_PROFILETYPE_INPUTPROCESSOR : |
michael@0 | 3442 | TF_PROFILETYPE_KEYBOARDLAYOUT, |
michael@0 | 3443 | profile.langid, profile.clsid, profile.catid, |
michael@0 | 3444 | profile.guidProfile, ::GetKeyboardLayout(0), |
michael@0 | 3445 | TF_IPSINK_FLAG_ACTIVE); |
michael@0 | 3446 | return true; |
michael@0 | 3447 | } |
michael@0 | 3448 | |
michael@0 | 3449 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 3450 | ("TSF: 0x%p nsTextStore::EnsureInitActiveLanguageProfile(), " |
michael@0 | 3451 | "calling OnActivated() without active TIP manually...", this)); |
michael@0 | 3452 | OnActivated(TF_PROFILETYPE_KEYBOARDLAYOUT, |
michael@0 | 3453 | langID, CLSID_NULL, GUID_TFCAT_TIP_KEYBOARD, |
michael@0 | 3454 | GUID_NULL, ::GetKeyboardLayout(0), |
michael@0 | 3455 | TF_IPSINK_FLAG_ACTIVE); |
michael@0 | 3456 | return true; |
michael@0 | 3457 | } |
michael@0 | 3458 | |
michael@0 | 3459 | void |
michael@0 | 3460 | nsTextStore::CommitCompositionInternal(bool aDiscard) |
michael@0 | 3461 | { |
michael@0 | 3462 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 3463 | ("TSF: 0x%p nsTextStore::CommitCompositionInternal(aDiscard=%s), " |
michael@0 | 3464 | "mSink=0x%p, mContext=0x%p, mComposition.mView=0x%p, " |
michael@0 | 3465 | "mComposition.mString=\"%s\"", |
michael@0 | 3466 | this, GetBoolName(aDiscard), mSink.get(), mContext.get(), |
michael@0 | 3467 | mComposition.mView.get(), |
michael@0 | 3468 | NS_ConvertUTF16toUTF8(mComposition.mString).get())); |
michael@0 | 3469 | |
michael@0 | 3470 | if (mComposition.IsComposing() && aDiscard) { |
michael@0 | 3471 | LONG endOffset = mComposition.EndOffset(); |
michael@0 | 3472 | mComposition.mString.Truncate(0); |
michael@0 | 3473 | if (mSink && !mLock) { |
michael@0 | 3474 | TS_TEXTCHANGE textChange; |
michael@0 | 3475 | textChange.acpStart = mComposition.mStart; |
michael@0 | 3476 | textChange.acpOldEnd = endOffset; |
michael@0 | 3477 | textChange.acpNewEnd = mComposition.mStart; |
michael@0 | 3478 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 3479 | ("TSF: 0x%p nsTextStore::CommitCompositionInternal(), calling" |
michael@0 | 3480 | "mSink->OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, " |
michael@0 | 3481 | "acpNewEnd=%ld })...", this, textChange.acpStart, |
michael@0 | 3482 | textChange.acpOldEnd, textChange.acpNewEnd)); |
michael@0 | 3483 | mSink->OnTextChange(0, &textChange); |
michael@0 | 3484 | } |
michael@0 | 3485 | } |
michael@0 | 3486 | // Terminate two contexts, the base context (mContext) and the top |
michael@0 | 3487 | // if the top context is not the same as the base context |
michael@0 | 3488 | nsRefPtr<ITfContext> context = mContext; |
michael@0 | 3489 | do { |
michael@0 | 3490 | if (context) { |
michael@0 | 3491 | nsRefPtr<ITfContextOwnerCompositionServices> services; |
michael@0 | 3492 | context->QueryInterface(IID_ITfContextOwnerCompositionServices, |
michael@0 | 3493 | getter_AddRefs(services)); |
michael@0 | 3494 | if (services) { |
michael@0 | 3495 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 3496 | ("TSF: 0x%p nsTextStore::CommitCompositionInternal(), " |
michael@0 | 3497 | "requesting TerminateComposition() for the context 0x%p...", |
michael@0 | 3498 | this, context.get())); |
michael@0 | 3499 | services->TerminateComposition(nullptr); |
michael@0 | 3500 | } |
michael@0 | 3501 | } |
michael@0 | 3502 | if (context != mContext) |
michael@0 | 3503 | break; |
michael@0 | 3504 | if (mDocumentMgr) |
michael@0 | 3505 | mDocumentMgr->GetTop(getter_AddRefs(context)); |
michael@0 | 3506 | } while (context != mContext); |
michael@0 | 3507 | } |
michael@0 | 3508 | |
michael@0 | 3509 | static |
michael@0 | 3510 | bool |
michael@0 | 3511 | GetCompartment(IUnknown* pUnk, |
michael@0 | 3512 | const GUID& aID, |
michael@0 | 3513 | ITfCompartment** aCompartment) |
michael@0 | 3514 | { |
michael@0 | 3515 | if (!pUnk) return false; |
michael@0 | 3516 | |
michael@0 | 3517 | nsRefPtr<ITfCompartmentMgr> compMgr; |
michael@0 | 3518 | pUnk->QueryInterface(IID_ITfCompartmentMgr, getter_AddRefs(compMgr)); |
michael@0 | 3519 | if (!compMgr) return false; |
michael@0 | 3520 | |
michael@0 | 3521 | return SUCCEEDED(compMgr->GetCompartment(aID, aCompartment)) && |
michael@0 | 3522 | (*aCompartment) != nullptr; |
michael@0 | 3523 | } |
michael@0 | 3524 | |
michael@0 | 3525 | // static |
michael@0 | 3526 | void |
michael@0 | 3527 | nsTextStore::SetIMEOpenState(bool aState) |
michael@0 | 3528 | { |
michael@0 | 3529 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 3530 | ("TSF: nsTextStore::SetIMEOpenState(aState=%s)", GetBoolName(aState))); |
michael@0 | 3531 | |
michael@0 | 3532 | nsRefPtr<ITfCompartment> comp; |
michael@0 | 3533 | if (!GetCompartment(sTsfThreadMgr, |
michael@0 | 3534 | GUID_COMPARTMENT_KEYBOARD_OPENCLOSE, |
michael@0 | 3535 | getter_AddRefs(comp))) { |
michael@0 | 3536 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 3537 | ("TSF: nsTextStore::SetIMEOpenState() FAILED due to" |
michael@0 | 3538 | "no compartment available")); |
michael@0 | 3539 | return; |
michael@0 | 3540 | } |
michael@0 | 3541 | |
michael@0 | 3542 | VARIANT variant; |
michael@0 | 3543 | variant.vt = VT_I4; |
michael@0 | 3544 | variant.lVal = aState; |
michael@0 | 3545 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 3546 | ("TSF: nsTextStore::SetIMEOpenState(), setting " |
michael@0 | 3547 | "0x%04X to GUID_COMPARTMENT_KEYBOARD_OPENCLOSE...", |
michael@0 | 3548 | variant.lVal)); |
michael@0 | 3549 | comp->SetValue(sTsfClientId, &variant); |
michael@0 | 3550 | } |
michael@0 | 3551 | |
michael@0 | 3552 | // static |
michael@0 | 3553 | bool |
michael@0 | 3554 | nsTextStore::GetIMEOpenState(void) |
michael@0 | 3555 | { |
michael@0 | 3556 | nsRefPtr<ITfCompartment> comp; |
michael@0 | 3557 | if (!GetCompartment(sTsfThreadMgr, |
michael@0 | 3558 | GUID_COMPARTMENT_KEYBOARD_OPENCLOSE, |
michael@0 | 3559 | getter_AddRefs(comp))) |
michael@0 | 3560 | return false; |
michael@0 | 3561 | |
michael@0 | 3562 | VARIANT variant; |
michael@0 | 3563 | ::VariantInit(&variant); |
michael@0 | 3564 | if (SUCCEEDED(comp->GetValue(&variant)) && variant.vt == VT_I4) |
michael@0 | 3565 | return variant.lVal != 0; |
michael@0 | 3566 | |
michael@0 | 3567 | ::VariantClear(&variant); // clear up in case variant.vt != VT_I4 |
michael@0 | 3568 | return false; |
michael@0 | 3569 | } |
michael@0 | 3570 | |
michael@0 | 3571 | // static |
michael@0 | 3572 | void |
michael@0 | 3573 | nsTextStore::SetInputContext(nsWindowBase* aWidget, |
michael@0 | 3574 | const InputContext& aContext, |
michael@0 | 3575 | const InputContextAction& aAction) |
michael@0 | 3576 | { |
michael@0 | 3577 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 3578 | ("TSF: nsTextStore::SetInputContext(aWidget=%p, " |
michael@0 | 3579 | "aContext.mIMEState.mEnabled=%s, aAction.mFocusChange=%s), " |
michael@0 | 3580 | "ThinksHavingFocus()=%s", |
michael@0 | 3581 | aWidget, GetIMEEnabledName(aContext.mIMEState.mEnabled), |
michael@0 | 3582 | GetFocusChangeName(aAction.mFocusChange), |
michael@0 | 3583 | GetBoolName(ThinksHavingFocus()))); |
michael@0 | 3584 | |
michael@0 | 3585 | NS_ENSURE_TRUE_VOID(sTsfTextStore); |
michael@0 | 3586 | sTsfTextStore->SetInputScope(aContext.mHTMLInputType); |
michael@0 | 3587 | |
michael@0 | 3588 | if (aAction.mFocusChange != InputContextAction::FOCUS_NOT_CHANGED) { |
michael@0 | 3589 | return; |
michael@0 | 3590 | } |
michael@0 | 3591 | |
michael@0 | 3592 | // If focus isn't actually changed but the enabled state is changed, |
michael@0 | 3593 | // emulate the focus move. |
michael@0 | 3594 | if (!ThinksHavingFocus() && |
michael@0 | 3595 | aContext.mIMEState.mEnabled == IMEState::ENABLED) { |
michael@0 | 3596 | OnFocusChange(true, aWidget, aContext.mIMEState.mEnabled); |
michael@0 | 3597 | } else if (ThinksHavingFocus() && |
michael@0 | 3598 | aContext.mIMEState.mEnabled != IMEState::ENABLED) { |
michael@0 | 3599 | OnFocusChange(false, aWidget, aContext.mIMEState.mEnabled); |
michael@0 | 3600 | } |
michael@0 | 3601 | } |
michael@0 | 3602 | |
michael@0 | 3603 | // static |
michael@0 | 3604 | void |
michael@0 | 3605 | nsTextStore::MarkContextAsKeyboardDisabled(ITfContext* aContext) |
michael@0 | 3606 | { |
michael@0 | 3607 | VARIANT variant_int4_value1; |
michael@0 | 3608 | variant_int4_value1.vt = VT_I4; |
michael@0 | 3609 | variant_int4_value1.lVal = 1; |
michael@0 | 3610 | |
michael@0 | 3611 | nsRefPtr<ITfCompartment> comp; |
michael@0 | 3612 | if (!GetCompartment(aContext, |
michael@0 | 3613 | GUID_COMPARTMENT_KEYBOARD_DISABLED, |
michael@0 | 3614 | getter_AddRefs(comp))) { |
michael@0 | 3615 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3616 | ("TSF: nsTextStore::MarkContextAsKeyboardDisabled() failed" |
michael@0 | 3617 | "aContext=0x%p...", aContext)); |
michael@0 | 3618 | return; |
michael@0 | 3619 | } |
michael@0 | 3620 | |
michael@0 | 3621 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 3622 | ("TSF: nsTextStore::MarkContextAsKeyboardDisabled(), setting " |
michael@0 | 3623 | "to disable context 0x%p...", |
michael@0 | 3624 | aContext)); |
michael@0 | 3625 | comp->SetValue(sTsfClientId, &variant_int4_value1); |
michael@0 | 3626 | } |
michael@0 | 3627 | |
michael@0 | 3628 | // static |
michael@0 | 3629 | void |
michael@0 | 3630 | nsTextStore::MarkContextAsEmpty(ITfContext* aContext) |
michael@0 | 3631 | { |
michael@0 | 3632 | VARIANT variant_int4_value1; |
michael@0 | 3633 | variant_int4_value1.vt = VT_I4; |
michael@0 | 3634 | variant_int4_value1.lVal = 1; |
michael@0 | 3635 | |
michael@0 | 3636 | nsRefPtr<ITfCompartment> comp; |
michael@0 | 3637 | if (!GetCompartment(aContext, |
michael@0 | 3638 | GUID_COMPARTMENT_EMPTYCONTEXT, |
michael@0 | 3639 | getter_AddRefs(comp))) { |
michael@0 | 3640 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3641 | ("TSF: nsTextStore::MarkContextAsEmpty() failed" |
michael@0 | 3642 | "aContext=0x%p...", aContext)); |
michael@0 | 3643 | return; |
michael@0 | 3644 | } |
michael@0 | 3645 | |
michael@0 | 3646 | PR_LOG(sTextStoreLog, PR_LOG_DEBUG, |
michael@0 | 3647 | ("TSF: nsTextStore::MarkContextAsEmpty(), setting " |
michael@0 | 3648 | "to mark empty context 0x%p...", aContext)); |
michael@0 | 3649 | comp->SetValue(sTsfClientId, &variant_int4_value1); |
michael@0 | 3650 | } |
michael@0 | 3651 | |
michael@0 | 3652 | // static |
michael@0 | 3653 | bool |
michael@0 | 3654 | nsTextStore::IsTIPCategoryKeyboard(REFCLSID aTextService, LANGID aLangID, |
michael@0 | 3655 | REFGUID aProfile) |
michael@0 | 3656 | { |
michael@0 | 3657 | if (aTextService == CLSID_NULL || aProfile == GUID_NULL) { |
michael@0 | 3658 | return false; |
michael@0 | 3659 | } |
michael@0 | 3660 | |
michael@0 | 3661 | nsRefPtr<IEnumTfLanguageProfiles> enumLangProfiles; |
michael@0 | 3662 | HRESULT hr = |
michael@0 | 3663 | sInputProcessorProfiles->EnumLanguageProfiles(aLangID, |
michael@0 | 3664 | getter_AddRefs(enumLangProfiles)); |
michael@0 | 3665 | if (FAILED(hr) || !enumLangProfiles) { |
michael@0 | 3666 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3667 | ("TSF: nsTextStore::IsTIPCategoryKeyboard(), FAILED " |
michael@0 | 3668 | "to get language profiles enumerator, hr=0x%08X", hr)); |
michael@0 | 3669 | return false; |
michael@0 | 3670 | } |
michael@0 | 3671 | |
michael@0 | 3672 | TF_LANGUAGEPROFILE profile; |
michael@0 | 3673 | ULONG fetch = 0; |
michael@0 | 3674 | while (SUCCEEDED(enumLangProfiles->Next(1, &profile, &fetch)) && fetch) { |
michael@0 | 3675 | // XXX We're not sure a profile is registered with two or more categories. |
michael@0 | 3676 | if (profile.clsid == aTextService && |
michael@0 | 3677 | profile.guidProfile == aProfile && |
michael@0 | 3678 | profile.catid == GUID_TFCAT_TIP_KEYBOARD) { |
michael@0 | 3679 | return true; |
michael@0 | 3680 | } |
michael@0 | 3681 | } |
michael@0 | 3682 | return false; |
michael@0 | 3683 | } |
michael@0 | 3684 | |
michael@0 | 3685 | // static |
michael@0 | 3686 | void |
michael@0 | 3687 | nsTextStore::GetTIPDescription(REFCLSID aTextService, LANGID aLangID, |
michael@0 | 3688 | REFGUID aProfile, nsAString& aDescription) |
michael@0 | 3689 | { |
michael@0 | 3690 | aDescription.Truncate(); |
michael@0 | 3691 | |
michael@0 | 3692 | if (aTextService == CLSID_NULL || aProfile == GUID_NULL) { |
michael@0 | 3693 | return; |
michael@0 | 3694 | } |
michael@0 | 3695 | |
michael@0 | 3696 | BSTR description = nullptr; |
michael@0 | 3697 | HRESULT hr = |
michael@0 | 3698 | sInputProcessorProfiles->GetLanguageProfileDescription(aTextService, |
michael@0 | 3699 | aLangID, |
michael@0 | 3700 | aProfile, |
michael@0 | 3701 | &description); |
michael@0 | 3702 | if (FAILED(hr)) { |
michael@0 | 3703 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3704 | ("TSF: nsTextStore::InitActiveTIPDescription() FAILED due to " |
michael@0 | 3705 | "GetLanguageProfileDescription() failure, hr=0x%08X", hr)); |
michael@0 | 3706 | return; |
michael@0 | 3707 | } |
michael@0 | 3708 | |
michael@0 | 3709 | if (description && description[0]) { |
michael@0 | 3710 | aDescription.Assign(description); |
michael@0 | 3711 | } |
michael@0 | 3712 | ::SysFreeString(description); |
michael@0 | 3713 | } |
michael@0 | 3714 | |
michael@0 | 3715 | // static |
michael@0 | 3716 | void |
michael@0 | 3717 | nsTextStore::Initialize() |
michael@0 | 3718 | { |
michael@0 | 3719 | #ifdef PR_LOGGING |
michael@0 | 3720 | if (!sTextStoreLog) { |
michael@0 | 3721 | sTextStoreLog = PR_NewLogModule("nsTextStoreWidgets"); |
michael@0 | 3722 | } |
michael@0 | 3723 | #endif |
michael@0 | 3724 | |
michael@0 | 3725 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 3726 | ("TSF: nsTextStore::Initialize() is called...")); |
michael@0 | 3727 | |
michael@0 | 3728 | if (sTsfThreadMgr) { |
michael@0 | 3729 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3730 | ("TSF: nsTextStore::Initialize() FAILED due to already initialized")); |
michael@0 | 3731 | return; |
michael@0 | 3732 | } |
michael@0 | 3733 | |
michael@0 | 3734 | bool enableTsf = Preferences::GetBool(kPrefNameTSFEnabled, false); |
michael@0 | 3735 | // Migrate legacy TSF pref to new pref. This should be removed in next |
michael@0 | 3736 | // release cycle or later. |
michael@0 | 3737 | if (!enableTsf && Preferences::GetBool(kLegacyPrefNameTSFEnabled, false)) { |
michael@0 | 3738 | enableTsf = true; |
michael@0 | 3739 | Preferences::SetBool(kPrefNameTSFEnabled, true); |
michael@0 | 3740 | Preferences::ClearUser(kLegacyPrefNameTSFEnabled); |
michael@0 | 3741 | } |
michael@0 | 3742 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 3743 | ("TSF: nsTextStore::Initialize(), TSF is %s", |
michael@0 | 3744 | enableTsf ? "enabled" : "disabled")); |
michael@0 | 3745 | if (!enableTsf) { |
michael@0 | 3746 | return; |
michael@0 | 3747 | } |
michael@0 | 3748 | |
michael@0 | 3749 | // XXX MSDN documents that ITfInputProcessorProfiles is available only on |
michael@0 | 3750 | // desktop apps. However, there is no known way to obtain |
michael@0 | 3751 | // ITfInputProcessorProfileMgr instance without ITfInputProcessorProfiles |
michael@0 | 3752 | // instance. |
michael@0 | 3753 | nsRefPtr<ITfInputProcessorProfiles> inputProcessorProfiles; |
michael@0 | 3754 | HRESULT hr = |
michael@0 | 3755 | ::CoCreateInstance(CLSID_TF_InputProcessorProfiles, nullptr, |
michael@0 | 3756 | CLSCTX_INPROC_SERVER, |
michael@0 | 3757 | IID_ITfInputProcessorProfiles, |
michael@0 | 3758 | getter_AddRefs(inputProcessorProfiles)); |
michael@0 | 3759 | if (FAILED(hr) || !inputProcessorProfiles) { |
michael@0 | 3760 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3761 | ("TSF: nsTextStore::Initialize() FAILED to create input processor " |
michael@0 | 3762 | "profiles, hr=0x%08X", hr)); |
michael@0 | 3763 | return; |
michael@0 | 3764 | } |
michael@0 | 3765 | |
michael@0 | 3766 | nsRefPtr<ITfThreadMgr> threadMgr; |
michael@0 | 3767 | hr = ::CoCreateInstance(CLSID_TF_ThreadMgr, nullptr, |
michael@0 | 3768 | CLSCTX_INPROC_SERVER, IID_ITfThreadMgr, |
michael@0 | 3769 | getter_AddRefs(threadMgr)); |
michael@0 | 3770 | if (FAILED(hr) || !threadMgr) { |
michael@0 | 3771 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3772 | ("TSF: nsTextStore::Initialize() FAILED to " |
michael@0 | 3773 | "create the thread manager, hr=0x%08X", hr)); |
michael@0 | 3774 | return; |
michael@0 | 3775 | } |
michael@0 | 3776 | |
michael@0 | 3777 | nsRefPtr<ITfMessagePump> messagePump; |
michael@0 | 3778 | hr = threadMgr->QueryInterface(IID_ITfMessagePump, |
michael@0 | 3779 | getter_AddRefs(messagePump)); |
michael@0 | 3780 | if (FAILED(hr) || !messagePump) { |
michael@0 | 3781 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3782 | ("TSF: nsTextStore::Initialize() FAILED to " |
michael@0 | 3783 | "QI message pump from the thread manager, hr=0x%08X", hr)); |
michael@0 | 3784 | return; |
michael@0 | 3785 | } |
michael@0 | 3786 | |
michael@0 | 3787 | nsRefPtr<ITfKeystrokeMgr> keystrokeMgr; |
michael@0 | 3788 | hr = threadMgr->QueryInterface(IID_ITfKeystrokeMgr, |
michael@0 | 3789 | getter_AddRefs(keystrokeMgr)); |
michael@0 | 3790 | if (FAILED(hr) || !keystrokeMgr) { |
michael@0 | 3791 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3792 | ("TSF: nsTextStore::Initialize() FAILED to " |
michael@0 | 3793 | "QI keystroke manager from the thread manager, hr=0x%08X", hr)); |
michael@0 | 3794 | return; |
michael@0 | 3795 | } |
michael@0 | 3796 | |
michael@0 | 3797 | hr = threadMgr->Activate(&sTsfClientId); |
michael@0 | 3798 | if (FAILED(hr)) { |
michael@0 | 3799 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3800 | ("TSF: nsTextStore::Initialize() FAILED to activate, hr=0x%08X", hr)); |
michael@0 | 3801 | return; |
michael@0 | 3802 | } |
michael@0 | 3803 | |
michael@0 | 3804 | nsRefPtr<ITfDisplayAttributeMgr> displayAttributeMgr; |
michael@0 | 3805 | hr = ::CoCreateInstance(CLSID_TF_DisplayAttributeMgr, nullptr, |
michael@0 | 3806 | CLSCTX_INPROC_SERVER, IID_ITfDisplayAttributeMgr, |
michael@0 | 3807 | getter_AddRefs(displayAttributeMgr)); |
michael@0 | 3808 | if (FAILED(hr) || !displayAttributeMgr) { |
michael@0 | 3809 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3810 | ("TSF: nsTextStore::Initialize() FAILED to create " |
michael@0 | 3811 | "a display attribute manager instance, hr=0x%08X", hr)); |
michael@0 | 3812 | return; |
michael@0 | 3813 | } |
michael@0 | 3814 | |
michael@0 | 3815 | nsRefPtr<ITfCategoryMgr> categoryMgr; |
michael@0 | 3816 | hr = ::CoCreateInstance(CLSID_TF_CategoryMgr, nullptr, |
michael@0 | 3817 | CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, |
michael@0 | 3818 | getter_AddRefs(categoryMgr)); |
michael@0 | 3819 | if (FAILED(hr) || !categoryMgr) { |
michael@0 | 3820 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3821 | ("TSF: nsTextStore::Initialize() FAILED to create " |
michael@0 | 3822 | "a category manager instance, hr=0x%08X", hr)); |
michael@0 | 3823 | return; |
michael@0 | 3824 | } |
michael@0 | 3825 | |
michael@0 | 3826 | nsRefPtr<ITfDocumentMgr> disabledDocumentMgr; |
michael@0 | 3827 | hr = threadMgr->CreateDocumentMgr(getter_AddRefs(disabledDocumentMgr)); |
michael@0 | 3828 | if (FAILED(hr) || !disabledDocumentMgr) { |
michael@0 | 3829 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3830 | ("TSF: nsTextStore::Initialize() FAILED to create " |
michael@0 | 3831 | "a document manager for disabled mode, hr=0x%08X", hr)); |
michael@0 | 3832 | return; |
michael@0 | 3833 | } |
michael@0 | 3834 | |
michael@0 | 3835 | nsRefPtr<ITfContext> disabledContext; |
michael@0 | 3836 | DWORD editCookie = 0; |
michael@0 | 3837 | hr = disabledDocumentMgr->CreateContext(sTsfClientId, 0, nullptr, |
michael@0 | 3838 | getter_AddRefs(disabledContext), |
michael@0 | 3839 | &editCookie); |
michael@0 | 3840 | if (FAILED(hr) || !disabledContext) { |
michael@0 | 3841 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3842 | ("TSF: nsTextStore::Initialize() FAILED to create " |
michael@0 | 3843 | "a context for disabled mode, hr=0x%08X", hr)); |
michael@0 | 3844 | return; |
michael@0 | 3845 | } |
michael@0 | 3846 | |
michael@0 | 3847 | MarkContextAsKeyboardDisabled(disabledContext); |
michael@0 | 3848 | MarkContextAsEmpty(disabledContext); |
michael@0 | 3849 | |
michael@0 | 3850 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 3851 | ("TSF: nsTextStore::Initialize() is creating " |
michael@0 | 3852 | "an nsTextStore instance...")); |
michael@0 | 3853 | nsRefPtr<nsTextStore> textStore = new nsTextStore(); |
michael@0 | 3854 | if (!textStore->Init(threadMgr)) { |
michael@0 | 3855 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 3856 | ("TSF: nsTextStore::Initialize() FAILED to initialize nsTextStore " |
michael@0 | 3857 | "instance")); |
michael@0 | 3858 | return; |
michael@0 | 3859 | } |
michael@0 | 3860 | |
michael@0 | 3861 | inputProcessorProfiles.swap(sInputProcessorProfiles); |
michael@0 | 3862 | threadMgr.swap(sTsfThreadMgr); |
michael@0 | 3863 | messagePump.swap(sMessagePump); |
michael@0 | 3864 | keystrokeMgr.swap(sKeystrokeMgr); |
michael@0 | 3865 | displayAttributeMgr.swap(sDisplayAttrMgr); |
michael@0 | 3866 | categoryMgr.swap(sCategoryMgr); |
michael@0 | 3867 | disabledDocumentMgr.swap(sTsfDisabledDocumentMgr); |
michael@0 | 3868 | disabledContext.swap(sTsfDisabledContext); |
michael@0 | 3869 | textStore.swap(sTsfTextStore); |
michael@0 | 3870 | |
michael@0 | 3871 | sCreateNativeCaretForATOK = |
michael@0 | 3872 | Preferences::GetBool("intl.tsf.hack.atok.create_native_caret", true); |
michael@0 | 3873 | |
michael@0 | 3874 | MOZ_ASSERT(!sFlushTIPInputMessage); |
michael@0 | 3875 | sFlushTIPInputMessage = ::RegisterWindowMessageW(L"Flush TIP Input Message"); |
michael@0 | 3876 | |
michael@0 | 3877 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, |
michael@0 | 3878 | ("TSF: nsTextStore::Initialize(), sTsfThreadMgr=0x%p, " |
michael@0 | 3879 | "sTsfClientId=0x%08X, sTsfTextStore=0x%p, sDisplayAttrMgr=0x%p, " |
michael@0 | 3880 | "sCategoryMgr=0x%p, sTsfDisabledDocumentMgr=0x%p, sTsfDisabledContext=%p, " |
michael@0 | 3881 | "sCreateNativeCaretForATOK=%s", |
michael@0 | 3882 | sTsfThreadMgr, sTsfClientId, sTsfTextStore, sDisplayAttrMgr, sCategoryMgr, |
michael@0 | 3883 | sTsfDisabledDocumentMgr, sTsfDisabledContext, |
michael@0 | 3884 | GetBoolName(sCreateNativeCaretForATOK))); |
michael@0 | 3885 | } |
michael@0 | 3886 | |
michael@0 | 3887 | // static |
michael@0 | 3888 | void |
michael@0 | 3889 | nsTextStore::Terminate(void) |
michael@0 | 3890 | { |
michael@0 | 3891 | PR_LOG(sTextStoreLog, PR_LOG_ALWAYS, ("TSF: nsTextStore::Terminate()")); |
michael@0 | 3892 | |
michael@0 | 3893 | NS_IF_RELEASE(sDisplayAttrMgr); |
michael@0 | 3894 | NS_IF_RELEASE(sCategoryMgr); |
michael@0 | 3895 | NS_IF_RELEASE(sTsfTextStore); |
michael@0 | 3896 | NS_IF_RELEASE(sTsfDisabledDocumentMgr); |
michael@0 | 3897 | NS_IF_RELEASE(sTsfDisabledContext); |
michael@0 | 3898 | NS_IF_RELEASE(sInputProcessorProfiles); |
michael@0 | 3899 | sTsfClientId = 0; |
michael@0 | 3900 | if (sTsfThreadMgr) { |
michael@0 | 3901 | sTsfThreadMgr->Deactivate(); |
michael@0 | 3902 | NS_RELEASE(sTsfThreadMgr); |
michael@0 | 3903 | NS_RELEASE(sMessagePump); |
michael@0 | 3904 | NS_RELEASE(sKeystrokeMgr); |
michael@0 | 3905 | } |
michael@0 | 3906 | } |
michael@0 | 3907 | |
michael@0 | 3908 | // static |
michael@0 | 3909 | bool |
michael@0 | 3910 | nsTextStore::ProcessRawKeyMessage(const MSG& aMsg) |
michael@0 | 3911 | { |
michael@0 | 3912 | if (!sKeystrokeMgr) { |
michael@0 | 3913 | return false; // not in TSF mode |
michael@0 | 3914 | } |
michael@0 | 3915 | |
michael@0 | 3916 | if (aMsg.message == WM_KEYDOWN) { |
michael@0 | 3917 | BOOL eaten; |
michael@0 | 3918 | HRESULT hr = sKeystrokeMgr->TestKeyDown(aMsg.wParam, aMsg.lParam, &eaten); |
michael@0 | 3919 | if (FAILED(hr) || !eaten) { |
michael@0 | 3920 | return false; |
michael@0 | 3921 | } |
michael@0 | 3922 | hr = sKeystrokeMgr->KeyDown(aMsg.wParam, aMsg.lParam, &eaten); |
michael@0 | 3923 | return SUCCEEDED(hr) && eaten; |
michael@0 | 3924 | } |
michael@0 | 3925 | if (aMsg.message == WM_KEYUP) { |
michael@0 | 3926 | BOOL eaten; |
michael@0 | 3927 | HRESULT hr = sKeystrokeMgr->TestKeyUp(aMsg.wParam, aMsg.lParam, &eaten); |
michael@0 | 3928 | if (FAILED(hr) || !eaten) { |
michael@0 | 3929 | return false; |
michael@0 | 3930 | } |
michael@0 | 3931 | hr = sKeystrokeMgr->KeyUp(aMsg.wParam, aMsg.lParam, &eaten); |
michael@0 | 3932 | return SUCCEEDED(hr) && eaten; |
michael@0 | 3933 | } |
michael@0 | 3934 | return false; |
michael@0 | 3935 | } |
michael@0 | 3936 | |
michael@0 | 3937 | // static |
michael@0 | 3938 | void |
michael@0 | 3939 | nsTextStore::ProcessMessage(nsWindowBase* aWindow, UINT aMessage, |
michael@0 | 3940 | WPARAM& aWParam, LPARAM& aLParam, |
michael@0 | 3941 | MSGResult& aResult) |
michael@0 | 3942 | { |
michael@0 | 3943 | switch (aMessage) { |
michael@0 | 3944 | case WM_IME_SETCONTEXT: |
michael@0 | 3945 | // If a windowless plugin had focus and IME was handled on it, composition |
michael@0 | 3946 | // window was set the position. After that, even in TSF mode, WinXP keeps |
michael@0 | 3947 | // to use composition window at the position if the active IME is not |
michael@0 | 3948 | // aware TSF. For avoiding this issue, we need to hide the composition |
michael@0 | 3949 | // window here. |
michael@0 | 3950 | if (aWParam) { |
michael@0 | 3951 | aLParam &= ~ISC_SHOWUICOMPOSITIONWINDOW; |
michael@0 | 3952 | } |
michael@0 | 3953 | break; |
michael@0 | 3954 | } |
michael@0 | 3955 | } |
michael@0 | 3956 | |
michael@0 | 3957 | /******************************************************************/ |
michael@0 | 3958 | /* nsTextStore::Composition */ |
michael@0 | 3959 | /******************************************************************/ |
michael@0 | 3960 | |
michael@0 | 3961 | void |
michael@0 | 3962 | nsTextStore::Composition::Start(ITfCompositionView* aCompositionView, |
michael@0 | 3963 | LONG aCompositionStartOffset, |
michael@0 | 3964 | const nsAString& aCompositionString) |
michael@0 | 3965 | { |
michael@0 | 3966 | mView = aCompositionView; |
michael@0 | 3967 | mString = aCompositionString; |
michael@0 | 3968 | mStart = aCompositionStartOffset; |
michael@0 | 3969 | } |
michael@0 | 3970 | |
michael@0 | 3971 | void |
michael@0 | 3972 | nsTextStore::Composition::End() |
michael@0 | 3973 | { |
michael@0 | 3974 | mView = nullptr; |
michael@0 | 3975 | mString.Truncate(); |
michael@0 | 3976 | } |
michael@0 | 3977 | |
michael@0 | 3978 | /****************************************************************************** |
michael@0 | 3979 | * nsTextStore::Content |
michael@0 | 3980 | *****************************************************************************/ |
michael@0 | 3981 | |
michael@0 | 3982 | const nsDependentSubstring |
michael@0 | 3983 | nsTextStore::Content::GetSelectedText() const |
michael@0 | 3984 | { |
michael@0 | 3985 | MOZ_ASSERT(mInitialized); |
michael@0 | 3986 | return GetSubstring(static_cast<uint32_t>(mSelection.StartOffset()), |
michael@0 | 3987 | static_cast<uint32_t>(mSelection.Length())); |
michael@0 | 3988 | } |
michael@0 | 3989 | |
michael@0 | 3990 | const nsDependentSubstring |
michael@0 | 3991 | nsTextStore::Content::GetSubstring(uint32_t aStart, uint32_t aLength) const |
michael@0 | 3992 | { |
michael@0 | 3993 | MOZ_ASSERT(mInitialized); |
michael@0 | 3994 | return nsDependentSubstring(mText, aStart, aLength); |
michael@0 | 3995 | } |
michael@0 | 3996 | |
michael@0 | 3997 | void |
michael@0 | 3998 | nsTextStore::Content::ReplaceSelectedTextWith(const nsAString& aString) |
michael@0 | 3999 | { |
michael@0 | 4000 | MOZ_ASSERT(mInitialized); |
michael@0 | 4001 | ReplaceTextWith(mSelection.StartOffset(), mSelection.Length(), aString); |
michael@0 | 4002 | } |
michael@0 | 4003 | |
michael@0 | 4004 | inline uint32_t |
michael@0 | 4005 | FirstDifferentCharOffset(const nsAString& aStr1, const nsAString& aStr2) |
michael@0 | 4006 | { |
michael@0 | 4007 | MOZ_ASSERT(aStr1 != aStr2); |
michael@0 | 4008 | uint32_t i = 0; |
michael@0 | 4009 | uint32_t minLength = std::min(aStr1.Length(), aStr2.Length()); |
michael@0 | 4010 | for (; i < minLength && aStr1[i] == aStr2[i]; i++) { |
michael@0 | 4011 | /* nothing to do */ |
michael@0 | 4012 | } |
michael@0 | 4013 | return i; |
michael@0 | 4014 | } |
michael@0 | 4015 | |
michael@0 | 4016 | void |
michael@0 | 4017 | nsTextStore::Content::ReplaceTextWith(LONG aStart, LONG aLength, |
michael@0 | 4018 | const nsAString& aReplaceString) |
michael@0 | 4019 | { |
michael@0 | 4020 | MOZ_ASSERT(mInitialized); |
michael@0 | 4021 | const nsDependentSubstring replacedString = |
michael@0 | 4022 | GetSubstring(static_cast<uint32_t>(aStart), |
michael@0 | 4023 | static_cast<uint32_t>(aLength)); |
michael@0 | 4024 | if (aReplaceString != replacedString) { |
michael@0 | 4025 | uint32_t firstDifferentOffset = |
michael@0 | 4026 | static_cast<uint32_t>(aStart) + FirstDifferentCharOffset(aReplaceString, |
michael@0 | 4027 | replacedString); |
michael@0 | 4028 | mMinTextModifiedOffset = |
michael@0 | 4029 | std::min(mMinTextModifiedOffset, firstDifferentOffset); |
michael@0 | 4030 | if (mComposition.IsComposing()) { |
michael@0 | 4031 | // Emulate text insertion during compositions, because during a |
michael@0 | 4032 | // composition, editor expects the whole composition string to |
michael@0 | 4033 | // be sent in NS_TEXT_TEXT, not just the inserted part. |
michael@0 | 4034 | // The actual NS_TEXT_TEXT will be sent in SetSelection or |
michael@0 | 4035 | // OnUpdateComposition. |
michael@0 | 4036 | MOZ_ASSERT(aStart >= mComposition.mStart); |
michael@0 | 4037 | MOZ_ASSERT(aStart + aLength <= mComposition.EndOffset()); |
michael@0 | 4038 | mComposition.mString.Replace( |
michael@0 | 4039 | static_cast<uint32_t>(aStart - mComposition.mStart), |
michael@0 | 4040 | static_cast<uint32_t>(aLength), aReplaceString); |
michael@0 | 4041 | } |
michael@0 | 4042 | mText.Replace(static_cast<uint32_t>(aStart), |
michael@0 | 4043 | static_cast<uint32_t>(aLength), aReplaceString); |
michael@0 | 4044 | } |
michael@0 | 4045 | // Selection should be collapsed at the end of the inserted string. |
michael@0 | 4046 | mSelection.CollapseAt( |
michael@0 | 4047 | static_cast<uint32_t>(aStart) + aReplaceString.Length()); |
michael@0 | 4048 | } |
michael@0 | 4049 | |
michael@0 | 4050 | void |
michael@0 | 4051 | nsTextStore::Content::StartComposition(ITfCompositionView* aCompositionView, |
michael@0 | 4052 | const PendingAction& aCompStart, |
michael@0 | 4053 | bool aPreserveSelection) |
michael@0 | 4054 | { |
michael@0 | 4055 | MOZ_ASSERT(mInitialized); |
michael@0 | 4056 | MOZ_ASSERT(aCompositionView); |
michael@0 | 4057 | MOZ_ASSERT(!mComposition.mView); |
michael@0 | 4058 | MOZ_ASSERT(aCompStart.mType == PendingAction::COMPOSITION_START); |
michael@0 | 4059 | |
michael@0 | 4060 | mComposition.Start(aCompositionView, aCompStart.mSelectionStart, |
michael@0 | 4061 | GetSubstring(static_cast<uint32_t>(aCompStart.mSelectionStart), |
michael@0 | 4062 | static_cast<uint32_t>(aCompStart.mSelectionLength))); |
michael@0 | 4063 | if (!aPreserveSelection) { |
michael@0 | 4064 | mSelection.SetSelection(mComposition.mStart, mComposition.mString.Length(), |
michael@0 | 4065 | false); |
michael@0 | 4066 | } |
michael@0 | 4067 | } |
michael@0 | 4068 | |
michael@0 | 4069 | void |
michael@0 | 4070 | nsTextStore::Content::EndComposition(const PendingAction& aCompEnd) |
michael@0 | 4071 | { |
michael@0 | 4072 | MOZ_ASSERT(mInitialized); |
michael@0 | 4073 | MOZ_ASSERT(mComposition.mView); |
michael@0 | 4074 | MOZ_ASSERT(aCompEnd.mType == PendingAction::COMPOSITION_END); |
michael@0 | 4075 | |
michael@0 | 4076 | mSelection.CollapseAt(mComposition.mStart + aCompEnd.mData.Length()); |
michael@0 | 4077 | mComposition.End(); |
michael@0 | 4078 | } |
michael@0 | 4079 | |
michael@0 | 4080 | #ifdef DEBUG |
michael@0 | 4081 | // static |
michael@0 | 4082 | bool |
michael@0 | 4083 | nsTextStore::CurrentKeyboardLayoutHasIME() |
michael@0 | 4084 | { |
michael@0 | 4085 | if (!sInputProcessorProfiles) { |
michael@0 | 4086 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 4087 | ("TSF: nsTextStore::CurrentKeyboardLayoutHasIME() FAILED due to there is " |
michael@0 | 4088 | "no input processor profiles instance")); |
michael@0 | 4089 | return false; |
michael@0 | 4090 | } |
michael@0 | 4091 | nsRefPtr<ITfInputProcessorProfileMgr> profileMgr; |
michael@0 | 4092 | HRESULT hr = |
michael@0 | 4093 | sInputProcessorProfiles->QueryInterface(IID_ITfInputProcessorProfileMgr, |
michael@0 | 4094 | getter_AddRefs(profileMgr)); |
michael@0 | 4095 | if (FAILED(hr) || !profileMgr) { |
michael@0 | 4096 | // On Windows Vista or later, ImmIsIME() API always returns true. |
michael@0 | 4097 | // If we failed to obtain the profile manager, we cannot know if current |
michael@0 | 4098 | // keyboard layout has IME. |
michael@0 | 4099 | if (IsVistaOrLater()) { |
michael@0 | 4100 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 4101 | ("TSF: nsTextStore::CurrentKeyboardLayoutHasIME() FAILED to query " |
michael@0 | 4102 | "ITfInputProcessorProfileMgr")); |
michael@0 | 4103 | return false; |
michael@0 | 4104 | } |
michael@0 | 4105 | // If the profiles instance doesn't have ITfInputProcessorProfileMgr |
michael@0 | 4106 | // interface, that means probably we're running on WinXP or WinServer2003 |
michael@0 | 4107 | // (except WinServer2003 R2). Then, we should use ImmIsIME(). |
michael@0 | 4108 | return ::ImmIsIME(::GetKeyboardLayout(0)); |
michael@0 | 4109 | } |
michael@0 | 4110 | |
michael@0 | 4111 | TF_INPUTPROCESSORPROFILE profile; |
michael@0 | 4112 | hr = profileMgr->GetActiveProfile(GUID_TFCAT_TIP_KEYBOARD, &profile); |
michael@0 | 4113 | if (hr == S_FALSE) { |
michael@0 | 4114 | return false; // not found or not active |
michael@0 | 4115 | } |
michael@0 | 4116 | if (FAILED(hr)) { |
michael@0 | 4117 | PR_LOG(sTextStoreLog, PR_LOG_ERROR, |
michael@0 | 4118 | ("TSF: nsTextStore::CurrentKeyboardLayoutHasIME() FAILED to retreive " |
michael@0 | 4119 | "active profile")); |
michael@0 | 4120 | return false; |
michael@0 | 4121 | } |
michael@0 | 4122 | return (profile.dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR); |
michael@0 | 4123 | } |
michael@0 | 4124 | #endif // #ifdef DEBUG |