michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef NSTEXTSTORE_H_ michael@0: #define NSTEXTSTORE_H_ michael@0: michael@0: #include "nsAutoPtr.h" michael@0: #include "nsString.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIWidget.h" michael@0: #include "nsWindowBase.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/TextRange.h" michael@0: #include "mozilla/WindowsVersion.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: // GUID_PROP_INPUTSCOPE is declared in inputscope.h using INIT_GUID. michael@0: // With initguid.h, we get its instance instead of extern declaration. michael@0: #ifdef INPUTSCOPE_INIT_GUID michael@0: #include michael@0: #endif michael@0: #include michael@0: michael@0: // TSF InputScope, for earlier SDK 8 michael@0: #define IS_SEARCH static_cast(50) michael@0: michael@0: struct ITfThreadMgr; michael@0: struct ITfDocumentMgr; michael@0: struct ITfDisplayAttributeMgr; michael@0: struct ITfCategoryMgr; michael@0: class nsWindow; michael@0: #ifdef MOZ_METRO michael@0: class MetroWidget; michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: namespace widget { michael@0: struct MSGResult; michael@0: } // namespace widget michael@0: } // namespace mozilla michael@0: michael@0: /* michael@0: * Text Services Framework text store michael@0: */ michael@0: michael@0: class nsTextStore MOZ_FINAL : public ITextStoreACP, michael@0: public ITfContextOwnerCompositionSink, michael@0: public ITfActiveLanguageProfileNotifySink, michael@0: public ITfInputProcessorProfileActivationSink michael@0: { michael@0: public: /*IUnknown*/ michael@0: STDMETHODIMP_(ULONG) AddRef(void); michael@0: STDMETHODIMP QueryInterface(REFIID, void**); michael@0: STDMETHODIMP_(ULONG) Release(void); michael@0: michael@0: public: /*ITextStoreACP*/ michael@0: STDMETHODIMP AdviseSink(REFIID, IUnknown*, DWORD); michael@0: STDMETHODIMP UnadviseSink(IUnknown*); michael@0: STDMETHODIMP RequestLock(DWORD, HRESULT*); michael@0: STDMETHODIMP GetStatus(TS_STATUS*); michael@0: STDMETHODIMP QueryInsert(LONG, LONG, ULONG, LONG*, LONG*); michael@0: STDMETHODIMP GetSelection(ULONG, ULONG, TS_SELECTION_ACP*, ULONG*); michael@0: STDMETHODIMP SetSelection(ULONG, const TS_SELECTION_ACP*); michael@0: STDMETHODIMP GetText(LONG, LONG, WCHAR*, ULONG, ULONG*, TS_RUNINFO*, ULONG, michael@0: ULONG*, LONG*); michael@0: STDMETHODIMP SetText(DWORD, LONG, LONG, const WCHAR*, ULONG, TS_TEXTCHANGE*); michael@0: STDMETHODIMP GetFormattedText(LONG, LONG, IDataObject**); michael@0: STDMETHODIMP GetEmbedded(LONG, REFGUID, REFIID, IUnknown**); michael@0: STDMETHODIMP QueryInsertEmbedded(const GUID*, const FORMATETC*, BOOL*); michael@0: STDMETHODIMP InsertEmbedded(DWORD, LONG, LONG, IDataObject*, TS_TEXTCHANGE*); michael@0: STDMETHODIMP RequestSupportedAttrs(DWORD, ULONG, const TS_ATTRID*); michael@0: STDMETHODIMP RequestAttrsAtPosition(LONG, ULONG, const TS_ATTRID*, DWORD); michael@0: STDMETHODIMP RequestAttrsTransitioningAtPosition(LONG, ULONG, michael@0: const TS_ATTRID*, DWORD); michael@0: STDMETHODIMP FindNextAttrTransition(LONG, LONG, ULONG, const TS_ATTRID*, michael@0: DWORD, LONG*, BOOL*, LONG*); michael@0: STDMETHODIMP RetrieveRequestedAttrs(ULONG, TS_ATTRVAL*, ULONG*); michael@0: STDMETHODIMP GetEndACP(LONG*); michael@0: STDMETHODIMP GetActiveView(TsViewCookie*); michael@0: STDMETHODIMP GetACPFromPoint(TsViewCookie, const POINT*, DWORD, LONG*); michael@0: STDMETHODIMP GetTextExt(TsViewCookie, LONG, LONG, RECT*, BOOL*); michael@0: STDMETHODIMP GetScreenExt(TsViewCookie, RECT*); michael@0: STDMETHODIMP GetWnd(TsViewCookie, HWND*); michael@0: STDMETHODIMP InsertTextAtSelection(DWORD, const WCHAR*, ULONG, LONG*, LONG*, michael@0: TS_TEXTCHANGE*); michael@0: STDMETHODIMP InsertEmbeddedAtSelection(DWORD, IDataObject*, LONG*, LONG*, michael@0: TS_TEXTCHANGE*); michael@0: michael@0: public: /*ITfContextOwnerCompositionSink*/ michael@0: STDMETHODIMP OnStartComposition(ITfCompositionView*, BOOL*); michael@0: STDMETHODIMP OnUpdateComposition(ITfCompositionView*, ITfRange*); michael@0: STDMETHODIMP OnEndComposition(ITfCompositionView*); michael@0: michael@0: public: /*ITfActiveLanguageProfileNotifySink*/ michael@0: STDMETHODIMP OnActivated(REFCLSID clsid, REFGUID guidProfile, michael@0: BOOL fActivated); michael@0: michael@0: public: /*ITfInputProcessorProfileActivationSink*/ michael@0: STDMETHODIMP OnActivated(DWORD, LANGID, REFCLSID, REFGUID, REFGUID, michael@0: HKL, DWORD); michael@0: michael@0: protected: michael@0: typedef mozilla::widget::IMENotification IMENotification; michael@0: typedef mozilla::widget::IMEState IMEState; michael@0: typedef mozilla::widget::InputContext InputContext; michael@0: typedef mozilla::widget::InputContextAction InputContextAction; michael@0: michael@0: public: michael@0: static void Initialize(void); michael@0: static void Terminate(void); michael@0: michael@0: static bool ProcessRawKeyMessage(const MSG& aMsg); michael@0: static void ProcessMessage(nsWindowBase* aWindow, UINT aMessage, michael@0: WPARAM& aWParam, LPARAM& aLParam, michael@0: mozilla::widget::MSGResult& aResult); michael@0: michael@0: michael@0: static void SetIMEOpenState(bool); michael@0: static bool GetIMEOpenState(void); michael@0: michael@0: static void CommitComposition(bool aDiscard) michael@0: { michael@0: NS_ENSURE_TRUE_VOID(sTsfTextStore); michael@0: sTsfTextStore->CommitCompositionInternal(aDiscard); michael@0: } michael@0: michael@0: static void SetInputContext(nsWindowBase* aWidget, michael@0: const InputContext& aContext, michael@0: const InputContextAction& aAction); michael@0: michael@0: static nsresult OnFocusChange(bool aGotFocus, michael@0: nsWindowBase* aFocusedWidget, michael@0: IMEState::Enabled aIMEEnabled); michael@0: static nsresult OnTextChange(const IMENotification& aIMENotification) michael@0: { michael@0: NS_ENSURE_TRUE(sTsfTextStore, NS_ERROR_NOT_AVAILABLE); michael@0: return sTsfTextStore->OnTextChangeInternal(aIMENotification); michael@0: } michael@0: michael@0: static nsresult OnSelectionChange(void) michael@0: { michael@0: NS_ENSURE_TRUE(sTsfTextStore, NS_ERROR_NOT_AVAILABLE); michael@0: return sTsfTextStore->OnSelectionChangeInternal(); michael@0: } michael@0: michael@0: static nsresult OnLayoutChange() michael@0: { michael@0: NS_ENSURE_TRUE(sTsfTextStore, NS_ERROR_NOT_AVAILABLE); michael@0: return sTsfTextStore->OnLayoutChangeInternal(); michael@0: } michael@0: michael@0: static nsIMEUpdatePreference GetIMEUpdatePreference(); michael@0: michael@0: // Returns the address of the pointer so that the TSF automatic test can michael@0: // replace the system object with a custom implementation for testing. michael@0: static void* GetNativeData(uint32_t aDataType) michael@0: { michael@0: switch (aDataType) { michael@0: case NS_NATIVE_TSF_THREAD_MGR: michael@0: Initialize(); // Apply any previous changes michael@0: return static_cast(&sTsfThreadMgr); michael@0: case NS_NATIVE_TSF_CATEGORY_MGR: michael@0: return static_cast(&sCategoryMgr); michael@0: case NS_NATIVE_TSF_DISPLAY_ATTR_MGR: michael@0: return static_cast(&sDisplayAttrMgr); michael@0: default: michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: static ITfMessagePump* GetMessagePump() michael@0: { michael@0: return sMessagePump; michael@0: } michael@0: michael@0: static void* GetTextStore() michael@0: { michael@0: return static_cast(sTsfTextStore); michael@0: } michael@0: michael@0: static bool ThinksHavingFocus() michael@0: { michael@0: return (sTsfTextStore && sTsfTextStore->mContext); michael@0: } michael@0: michael@0: static bool IsInTSFMode() michael@0: { michael@0: return sTsfThreadMgr != nullptr; michael@0: } michael@0: michael@0: static bool IsComposing() michael@0: { michael@0: return (sTsfTextStore && sTsfTextStore->mComposition.IsComposing()); michael@0: } michael@0: michael@0: static bool IsComposingOn(nsWindowBase* aWidget) michael@0: { michael@0: return (IsComposing() && sTsfTextStore->mWidget == aWidget); michael@0: } michael@0: michael@0: static bool IsIMM_IME() michael@0: { michael@0: if (!sTsfTextStore || !sTsfTextStore->EnsureInitActiveTIPKeyboard()) { michael@0: return IsIMM_IME(::GetKeyboardLayout(0)); michael@0: } michael@0: return sTsfTextStore->mIsIMM_IME; michael@0: } michael@0: michael@0: static bool IsIMM_IME(HKL aHKL) michael@0: { michael@0: return (::ImmGetIMEFileNameW(aHKL, nullptr, 0) > 0); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: // Returns true when keyboard layout has IME (TIP). michael@0: static bool CurrentKeyboardLayoutHasIME(); michael@0: #endif // #ifdef DEBUG michael@0: michael@0: protected: michael@0: nsTextStore(); michael@0: ~nsTextStore(); michael@0: michael@0: bool Init(ITfThreadMgr* aThreadMgr); michael@0: michael@0: static void MarkContextAsKeyboardDisabled(ITfContext* aContext); michael@0: static void MarkContextAsEmpty(ITfContext* aContext); michael@0: michael@0: static bool IsTIPCategoryKeyboard(REFCLSID aTextService, LANGID aLangID, michael@0: REFGUID aProfile); michael@0: static void GetTIPDescription(REFCLSID aTextService, LANGID aLangID, michael@0: REFGUID aProfile, nsAString& aDescription); michael@0: michael@0: bool Create(nsWindowBase* aWidget); michael@0: bool Destroy(void); michael@0: michael@0: bool IsReadLock(DWORD aLock) const michael@0: { michael@0: return (TS_LF_READ == (aLock & TS_LF_READ)); michael@0: } michael@0: bool IsReadWriteLock(DWORD aLock) const michael@0: { michael@0: return (TS_LF_READWRITE == (aLock & TS_LF_READWRITE)); michael@0: } michael@0: bool IsReadLocked() const { return IsReadLock(mLock); } michael@0: bool IsReadWriteLocked() const { return IsReadWriteLock(mLock); } michael@0: michael@0: // This is called immediately after a call of OnLockGranted() of mSink. michael@0: // Note that mLock isn't cleared yet when this is called. michael@0: void DidLockGranted(); michael@0: michael@0: bool GetScreenExtInternal(RECT &aScreenExt); michael@0: // If aDispatchTextEvent is true, this method will dispatch text event if michael@0: // this is called during IME composing. aDispatchTextEvent should be true michael@0: // only when this is called from SetSelection. Because otherwise, the text michael@0: // event should not be sent from here. michael@0: HRESULT SetSelectionInternal(const TS_SELECTION_ACP*, michael@0: bool aDispatchTextEvent = false); michael@0: bool InsertTextAtSelectionInternal(const nsAString &aInsertStr, michael@0: TS_TEXTCHANGE* aTextChange); michael@0: void CommitCompositionInternal(bool); michael@0: nsresult OnTextChangeInternal(const IMENotification& aIMENotification); michael@0: nsresult OnSelectionChangeInternal(void); michael@0: HRESULT GetDisplayAttribute(ITfProperty* aProperty, michael@0: ITfRange* aRange, michael@0: TF_DISPLAYATTRIBUTE* aResult); michael@0: HRESULT RestartCompositionIfNecessary(ITfRange* pRangeNew = nullptr); michael@0: michael@0: // Following methods record composing action(s) to mPendingActions. michael@0: // They will be flushed FlushPendingActions(). michael@0: HRESULT RecordCompositionStartAction(ITfCompositionView* aCompositionView, michael@0: ITfRange* aRange, michael@0: bool aPreserveSelection); michael@0: HRESULT RecordCompositionUpdateAction(); michael@0: HRESULT RecordCompositionEndAction(); michael@0: michael@0: // FlushPendingActions() performs pending actions recorded in mPendingActions michael@0: // and clear it. michael@0: void FlushPendingActions(); michael@0: michael@0: nsresult OnLayoutChangeInternal(); michael@0: HRESULT ProcessScopeRequest(DWORD dwFlags, michael@0: ULONG cFilterAttrs, michael@0: const TS_ATTRID *paFilterAttrs); michael@0: void SetInputScope(const nsString& aHTMLInputType); michael@0: michael@0: // Creates native caret over our caret. This method only works on desktop michael@0: // application. Otherwise, this does nothing. michael@0: void CreateNativeCaret(); michael@0: michael@0: bool EnsureInitActiveTIPKeyboard(); michael@0: michael@0: // Holds the pointer to our current win32 or metro widget michael@0: nsRefPtr mWidget; michael@0: // Document manager for the currently focused editor michael@0: nsRefPtr mDocumentMgr; michael@0: // Edit cookie associated with the current editing context michael@0: DWORD mEditCookie; michael@0: // Cookie of installing ITfInputProcessorProfileActivationSink michael@0: DWORD mIPProfileCookie; michael@0: // Cookie of installing ITfActiveLanguageProfileNotifySink michael@0: DWORD mLangProfileCookie; michael@0: // Editing context at the bottom of mDocumentMgr's context stack michael@0: nsRefPtr mContext; michael@0: // Currently installed notification sink michael@0: nsRefPtr mSink; michael@0: // TS_AS_* mask of what events to notify michael@0: DWORD mSinkMask; michael@0: // 0 if not locked, otherwise TS_LF_* indicating the current lock michael@0: DWORD mLock; michael@0: // 0 if no lock is queued, otherwise TS_LF_* indicating the queue lock michael@0: DWORD mLockQueued; michael@0: // Active TIP keyboard's description. If active language profile isn't TIP, michael@0: // i.e., IMM-IME or just a keyboard layout, this is empty. michael@0: nsString mActiveTIPKeyboardDescription; michael@0: michael@0: class Composition MOZ_FINAL michael@0: { michael@0: public: michael@0: // nullptr if no composition is active, otherwise the current composition michael@0: nsRefPtr mView; michael@0: michael@0: // Current copy of the active composition string. Only mString is michael@0: // changed during a InsertTextAtSelection call if we have a composition. michael@0: // mString acts as a buffer until OnUpdateComposition is called michael@0: // and mString is flushed to editor through NS_TEXT_TEXT. This michael@0: // way all changes are updated in batches to avoid michael@0: // inconsistencies/artifacts. michael@0: nsString mString; michael@0: michael@0: // The latest composition string which was dispatched by composition update michael@0: // event. michael@0: nsString mLastData; michael@0: michael@0: // The start of the current active composition, in ACP offsets michael@0: LONG mStart; michael@0: michael@0: bool IsComposing() const michael@0: { michael@0: return (mView != nullptr); michael@0: } michael@0: michael@0: LONG EndOffset() const michael@0: { michael@0: return mStart + static_cast(mString.Length()); michael@0: } michael@0: michael@0: // Start() and End() updates the members for emulating the latest state. michael@0: // Unless flush the pending actions, this data never matches the actual michael@0: // content. michael@0: void Start(ITfCompositionView* aCompositionView, michael@0: LONG aCompositionStartOffset, michael@0: const nsAString& aCompositionString); michael@0: void End(); michael@0: }; michael@0: // While the document is locked, we cannot dispatch any events which cause michael@0: // DOM events since the DOM events' handlers may modify the locked document. michael@0: // However, even while the document is locked, TSF may queries us. michael@0: // For that, nsTextStore modifies mComposition even while the document is michael@0: // locked. With mComposition, query methods can returns the text content michael@0: // information. michael@0: Composition mComposition; michael@0: michael@0: class Selection michael@0: { michael@0: public: michael@0: Selection() : mDirty(true) {} michael@0: michael@0: bool IsDirty() const { return mDirty; }; michael@0: void MarkDirty() { mDirty = true; } michael@0: michael@0: TS_SELECTION_ACP& ACP() michael@0: { michael@0: MOZ_ASSERT(!mDirty); michael@0: return mACP; michael@0: } michael@0: michael@0: void SetSelection(const TS_SELECTION_ACP& aSelection) michael@0: { michael@0: mDirty = false; michael@0: mACP = aSelection; michael@0: // Selection end must be active in our editor. michael@0: if (mACP.style.ase != TS_AE_START) { michael@0: mACP.style.ase = TS_AE_END; michael@0: } michael@0: // We're not support interim char selection for now. michael@0: // XXX Probably, this is necessary for supporting South Asian languages. michael@0: mACP.style.fInterimChar = FALSE; michael@0: } michael@0: michael@0: void SetSelection(uint32_t aStart, uint32_t aLength, bool aReversed) michael@0: { michael@0: mDirty = false; michael@0: mACP.acpStart = static_cast(aStart); michael@0: mACP.acpEnd = static_cast(aStart + aLength); michael@0: mACP.style.ase = aReversed ? TS_AE_START : TS_AE_END; michael@0: mACP.style.fInterimChar = FALSE; michael@0: } michael@0: michael@0: bool IsCollapsed() const michael@0: { michael@0: MOZ_ASSERT(!mDirty); michael@0: return (mACP.acpStart == mACP.acpEnd); michael@0: } michael@0: michael@0: void CollapseAt(uint32_t aOffset) michael@0: { michael@0: mDirty = false; michael@0: mACP.acpStart = mACP.acpEnd = static_cast(aOffset); michael@0: mACP.style.ase = TS_AE_END; michael@0: mACP.style.fInterimChar = FALSE; michael@0: } michael@0: michael@0: LONG MinOffset() const michael@0: { michael@0: MOZ_ASSERT(!mDirty); michael@0: LONG min = std::min(mACP.acpStart, mACP.acpEnd); michael@0: MOZ_ASSERT(min >= 0); michael@0: return min; michael@0: } michael@0: michael@0: LONG MaxOffset() const michael@0: { michael@0: MOZ_ASSERT(!mDirty); michael@0: LONG max = std::max(mACP.acpStart, mACP.acpEnd); michael@0: MOZ_ASSERT(max >= 0); michael@0: return max; michael@0: } michael@0: michael@0: LONG StartOffset() const michael@0: { michael@0: MOZ_ASSERT(!mDirty); michael@0: MOZ_ASSERT(mACP.acpStart >= 0); michael@0: return mACP.acpStart; michael@0: } michael@0: michael@0: LONG EndOffset() const michael@0: { michael@0: MOZ_ASSERT(!mDirty); michael@0: MOZ_ASSERT(mACP.acpEnd >= 0); michael@0: return mACP.acpEnd; michael@0: } michael@0: michael@0: LONG Length() const michael@0: { michael@0: MOZ_ASSERT(!mDirty); michael@0: MOZ_ASSERT(mACP.acpEnd >= mACP.acpStart); michael@0: return std::abs(mACP.acpEnd - mACP.acpStart); michael@0: } michael@0: michael@0: bool IsReversed() const michael@0: { michael@0: MOZ_ASSERT(!mDirty); michael@0: return (mACP.style.ase == TS_AE_START); michael@0: } michael@0: michael@0: TsActiveSelEnd ActiveSelEnd() const michael@0: { michael@0: MOZ_ASSERT(!mDirty); michael@0: return mACP.style.ase; michael@0: } michael@0: michael@0: bool IsInterimChar() const michael@0: { michael@0: MOZ_ASSERT(!mDirty); michael@0: return (mACP.style.fInterimChar != FALSE); michael@0: } michael@0: michael@0: private: michael@0: TS_SELECTION_ACP mACP; michael@0: bool mDirty; michael@0: }; michael@0: // Don't access mSelection directly except at calling MarkDirty(). michael@0: // Use CurrentSelection() instead. This is marked as dirty when the michael@0: // selection or content is changed without document lock. michael@0: Selection mSelection; michael@0: michael@0: // Get "current selection" while the document is locked. The selection is michael@0: // NOT modified immediately during document lock. The pending changes will michael@0: // be flushed at unlocking the document. The "current selection" is the michael@0: // modified selection during document lock. This is also called michael@0: // CurrentContent() too. michael@0: Selection& CurrentSelection(); michael@0: michael@0: struct PendingAction MOZ_FINAL michael@0: { michael@0: enum ActionType MOZ_ENUM_TYPE(uint8_t) michael@0: { michael@0: COMPOSITION_START, michael@0: COMPOSITION_UPDATE, michael@0: COMPOSITION_END, michael@0: SELECTION_SET michael@0: }; michael@0: ActionType mType; michael@0: // For compositionstart and selectionset michael@0: LONG mSelectionStart; michael@0: LONG mSelectionLength; michael@0: // For compositionupdate and compositionend michael@0: nsString mData; michael@0: // For compositionupdate michael@0: nsRefPtr mRanges; michael@0: // For selectionset michael@0: bool mSelectionReversed; michael@0: }; michael@0: // Items of mPendingActions are appended when TSF tells us to need to dispatch michael@0: // DOM composition events. However, we cannot dispatch while the document is michael@0: // locked because it can cause modifying the locked document. So, the pending michael@0: // actions should be performed when document lock is unlocked. michael@0: nsTArray mPendingActions; michael@0: michael@0: PendingAction* GetPendingCompositionUpdate() michael@0: { michael@0: if (!mPendingActions.IsEmpty()) { michael@0: PendingAction& lastAction = mPendingActions.LastElement(); michael@0: if (lastAction.mType == PendingAction::COMPOSITION_UPDATE) { michael@0: return &lastAction; michael@0: } michael@0: } michael@0: PendingAction* newAction = mPendingActions.AppendElement(); michael@0: newAction->mType = PendingAction::COMPOSITION_UPDATE; michael@0: newAction->mRanges = new mozilla::TextRangeArray(); michael@0: return newAction; michael@0: } michael@0: michael@0: // When On*Composition() is called without document lock, we need to flush michael@0: // the recorded actions at quitting the method. michael@0: // AutoPendingActionAndContentFlusher class is usedful for it. michael@0: class MOZ_STACK_CLASS AutoPendingActionAndContentFlusher MOZ_FINAL michael@0: { michael@0: public: michael@0: AutoPendingActionAndContentFlusher(nsTextStore* aTextStore) michael@0: : mTextStore(aTextStore) michael@0: { michael@0: MOZ_ASSERT(!mTextStore->mIsRecordingActionsWithoutLock); michael@0: if (!mTextStore->IsReadWriteLocked()) { michael@0: mTextStore->mIsRecordingActionsWithoutLock = true; michael@0: } michael@0: } michael@0: michael@0: ~AutoPendingActionAndContentFlusher() michael@0: { michael@0: if (!mTextStore->mIsRecordingActionsWithoutLock) { michael@0: return; michael@0: } michael@0: mTextStore->FlushPendingActions(); michael@0: mTextStore->mIsRecordingActionsWithoutLock = false; michael@0: } michael@0: michael@0: private: michael@0: AutoPendingActionAndContentFlusher() {} michael@0: michael@0: nsRefPtr mTextStore; michael@0: }; michael@0: michael@0: class Content MOZ_FINAL michael@0: { michael@0: public: michael@0: Content(nsTextStore::Composition& aComposition, michael@0: nsTextStore::Selection& aSelection) : michael@0: mComposition(aComposition), mSelection(aSelection) michael@0: { michael@0: Clear(); michael@0: } michael@0: michael@0: void Clear() michael@0: { michael@0: mText.Truncate(); michael@0: mInitialized = false; michael@0: } michael@0: michael@0: bool IsInitialized() const { return mInitialized; } michael@0: michael@0: void Init(const nsAString& aText) michael@0: { michael@0: mText = aText; michael@0: mMinTextModifiedOffset = NOT_MODIFIED; michael@0: mInitialized = true; michael@0: } michael@0: michael@0: const nsDependentSubstring GetSelectedText() const; michael@0: const nsDependentSubstring GetSubstring(uint32_t aStart, michael@0: uint32_t aLength) const; michael@0: void ReplaceSelectedTextWith(const nsAString& aString); michael@0: void ReplaceTextWith(LONG aStart, LONG aLength, const nsAString& aString); michael@0: michael@0: void StartComposition(ITfCompositionView* aCompositionView, michael@0: const PendingAction& aCompStart, michael@0: bool aPreserveSelection); michael@0: void EndComposition(const PendingAction& aCompEnd); michael@0: michael@0: const nsString& Text() const michael@0: { michael@0: MOZ_ASSERT(mInitialized); michael@0: return mText; michael@0: } michael@0: michael@0: // Returns true if layout of the character at the aOffset has not been michael@0: // calculated. michael@0: bool IsLayoutChangedAfter(uint32_t aOffset) const michael@0: { michael@0: return mInitialized && (mMinTextModifiedOffset < aOffset); michael@0: } michael@0: // Returns true if layout of the content has been changed, i.e., the new michael@0: // layout has not been calculated. michael@0: bool IsLayoutChanged() const michael@0: { michael@0: return mInitialized && (mMinTextModifiedOffset != NOT_MODIFIED); michael@0: } michael@0: michael@0: nsTextStore::Composition& Composition() { return mComposition; } michael@0: nsTextStore::Selection& Selection() { return mSelection; } michael@0: michael@0: private: michael@0: nsString mText; michael@0: nsTextStore::Composition& mComposition; michael@0: nsTextStore::Selection& mSelection; michael@0: michael@0: // The minimum offset of modified part of the text. michael@0: enum MOZ_ENUM_TYPE(uint32_t) michael@0: { michael@0: NOT_MODIFIED = UINT32_MAX michael@0: }; michael@0: uint32_t mMinTextModifiedOffset; michael@0: michael@0: bool mInitialized; michael@0: }; michael@0: // mContent caches "current content" of the document ONLY while the document michael@0: // is locked. I.e., the content is cleared at unlocking the document since michael@0: // we need to reduce the memory usage. This is initialized by michael@0: // CurrentContent() automatically, so, don't access this member directly michael@0: // except at calling Clear(), IsInitialized(), IsLayoutChangedAfter() or michael@0: // IsLayoutChanged(). michael@0: Content mContent; michael@0: michael@0: Content& CurrentContent(); michael@0: michael@0: // The input scopes for this context, defaults to IS_DEFAULT. michael@0: nsTArray mInputScopes; michael@0: bool mInputScopeDetected; michael@0: bool mInputScopeRequested; michael@0: // If edit actions are being recorded without document lock, this is true. michael@0: // Otherwise, false. michael@0: bool mIsRecordingActionsWithoutLock; michael@0: // During recording actions, we shouldn't call mSink->OnSelectionChange() michael@0: // because it may cause TSF request new lock. This is a problem if the michael@0: // selection change is caused by a call of On*Composition() without document michael@0: // lock since RequestLock() tries to flush the pending actions again (which michael@0: // are flushing). Therefore, OnSelectionChangeInternal() sets this true michael@0: // during recoding actions and then, RequestLock() will call michael@0: // mSink->OnSelectionChange() after mLock becomes 0. michael@0: bool mPendingOnSelectionChange; michael@0: // If GetTextExt() or GetACPFromPoint() is called and the layout hasn't been michael@0: // calculated yet, these methods return TS_E_NOLAYOUT. Then, RequestLock() michael@0: // will call mSink->OnLayoutChange() and michael@0: // ITfContextOwnerServices::OnLayoutChange() after the layout is fixed and michael@0: // the document is unlocked. michael@0: bool mPendingOnLayoutChange; michael@0: // While there is native caret, this is true. Otherwise, false. michael@0: bool mNativeCaretIsCreated; michael@0: michael@0: // True if current IME is implemented with IMM. michael@0: bool mIsIMM_IME; michael@0: // True if OnActivated() is already called michael@0: bool mOnActivatedCalled; michael@0: michael@0: // TSF thread manager object for the current application michael@0: static ITfThreadMgr* sTsfThreadMgr; michael@0: // sMessagePump is QI'ed from sTsfThreadMgr michael@0: static ITfMessagePump* sMessagePump; michael@0: // sKeystrokeMgr is QI'ed from sTsfThreadMgr michael@0: static ITfKeystrokeMgr* sKeystrokeMgr; michael@0: // TSF display attribute manager michael@0: static ITfDisplayAttributeMgr* sDisplayAttrMgr; michael@0: // TSF category manager michael@0: static ITfCategoryMgr* sCategoryMgr; michael@0: michael@0: // TSF client ID for the current application michael@0: static DWORD sTsfClientId; michael@0: // Current text store. Currently only ONE nsTextStore instance is ever used, michael@0: // although Create is called when an editor is focused and Destroy called michael@0: // when the focused editor is blurred. michael@0: static nsTextStore* sTsfTextStore; michael@0: michael@0: // For IME (keyboard) disabled state: michael@0: static ITfDocumentMgr* sTsfDisabledDocumentMgr; michael@0: static ITfContext* sTsfDisabledContext; michael@0: michael@0: static ITfInputProcessorProfiles* sInputProcessorProfiles; michael@0: michael@0: // Enables/Disables hack for specific TIP. michael@0: static bool sCreateNativeCaretForATOK; michael@0: michael@0: // Message the Tablet Input Panel uses to flush text during blurring. michael@0: // See comments in Destroy michael@0: static UINT sFlushTIPInputMessage; michael@0: michael@0: private: michael@0: ULONG mRefCnt; michael@0: }; michael@0: michael@0: #endif /*NSTEXTSTORE_H_*/