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 __mozinlinespellchecker_h__ michael@0: #define __mozinlinespellchecker_h__ michael@0: michael@0: #include "nsAutoPtr.h" michael@0: #include "nsRange.h" michael@0: #include "nsIEditorSpellCheck.h" michael@0: #include "nsIEditActionListener.h" michael@0: #include "nsIInlineSpellChecker.h" michael@0: #include "nsIDOMTreeWalker.h" michael@0: #include "nsWeakReference.h" michael@0: #include "nsEditor.h" michael@0: #include "nsIDOMEventListener.h" michael@0: #include "nsWeakReference.h" michael@0: #include "mozISpellI18NUtil.h" michael@0: #include "nsCycleCollectionParticipant.h" michael@0: michael@0: // X.h defines KeyPress michael@0: #ifdef KeyPress michael@0: #undef KeyPress michael@0: #endif michael@0: michael@0: class nsIDOMMouseEventListener; michael@0: class mozInlineSpellWordUtil; michael@0: class mozInlineSpellChecker; michael@0: class mozInlineSpellResume; michael@0: class InitEditorSpellCheckCallback; michael@0: class UpdateCurrentDictionaryCallback; michael@0: class mozInlineSpellResume; michael@0: michael@0: class mozInlineSpellStatus michael@0: { michael@0: public: michael@0: mozInlineSpellStatus(mozInlineSpellChecker* aSpellChecker); michael@0: michael@0: nsresult InitForEditorChange(EditAction aAction, michael@0: nsIDOMNode* aAnchorNode, int32_t aAnchorOffset, michael@0: nsIDOMNode* aPreviousNode, int32_t aPreviousOffset, michael@0: nsIDOMNode* aStartNode, int32_t aStartOffset, michael@0: nsIDOMNode* aEndNode, int32_t aEndOffset); michael@0: nsresult InitForNavigation(bool aForceCheck, int32_t aNewPositionOffset, michael@0: nsIDOMNode* aOldAnchorNode, int32_t aOldAnchorOffset, michael@0: nsIDOMNode* aNewAnchorNode, int32_t aNewAnchorOffset, michael@0: bool* aContinue); michael@0: nsresult InitForSelection(); michael@0: nsresult InitForRange(nsRange* aRange); michael@0: michael@0: nsresult FinishInitOnEvent(mozInlineSpellWordUtil& aWordUtil); michael@0: michael@0: // Return true if we plan to spell-check everything michael@0: bool IsFullSpellCheck() const { michael@0: return mOp == eOpChange && !mRange; michael@0: } michael@0: michael@0: nsRefPtr mSpellChecker; michael@0: michael@0: // The total number of words checked in this sequence, using this tally tells michael@0: // us when to stop. This count is preserved as we continue checking in new michael@0: // messages. michael@0: int32_t mWordCount; michael@0: michael@0: // what happened? michael@0: enum Operation { eOpChange, // for SpellCheckAfterChange except deleteSelection michael@0: eOpChangeDelete, // for SpellCheckAfterChange deleteSelection michael@0: eOpNavigation, // for HandleNavigationEvent michael@0: eOpSelection, // re-check all misspelled words michael@0: eOpResume }; // for resuming a previously started check michael@0: Operation mOp; michael@0: michael@0: // Used for events where we have already computed the range to use. It can michael@0: // also be nullptr in these cases where we need to check the entire range. michael@0: nsRefPtr mRange; michael@0: michael@0: // If we happen to know something was inserted, this is that range. michael@0: // Can be nullptr (this only allows an optimization, so not setting doesn't hurt) michael@0: nsCOMPtr mCreatedRange; michael@0: michael@0: // Contains the range computed for the current word. Can be nullptr. michael@0: nsRefPtr mNoCheckRange; michael@0: michael@0: // Indicates the position of the cursor for the event (so we can compute michael@0: // mNoCheckRange). It can be nullptr if we don't care about the cursor position michael@0: // (such as for the intial check of everything). michael@0: // michael@0: // For mOp == eOpNavigation, this is the NEW position of the cursor michael@0: nsCOMPtr mAnchorRange; michael@0: michael@0: // ----- michael@0: // The following members are only for navigation events and are only michael@0: // stored for FinishNavigationEvent to initialize the other members. michael@0: // ----- michael@0: michael@0: // this is the OLD position of the cursor michael@0: nsCOMPtr mOldNavigationAnchorRange; michael@0: michael@0: // Set when we should force checking the current word. See michael@0: // mozInlineSpellChecker::HandleNavigationEvent for a description of why we michael@0: // have this. michael@0: bool mForceNavigationWordCheck; michael@0: michael@0: // Contains the offset passed in to HandleNavigationEvent michael@0: int32_t mNewNavigationPositionOffset; michael@0: michael@0: protected: michael@0: nsresult FinishNavigationEvent(mozInlineSpellWordUtil& aWordUtil); michael@0: michael@0: nsresult FillNoCheckRangeFromAnchor(mozInlineSpellWordUtil& aWordUtil); michael@0: michael@0: nsresult GetDocument(nsIDOMDocument** aDocument); michael@0: nsresult PositionToCollapsedRange(nsIDOMDocument* aDocument, michael@0: nsIDOMNode* aNode, int32_t aOffset, michael@0: nsIDOMRange** aRange); michael@0: }; michael@0: michael@0: class mozInlineSpellChecker : public nsIInlineSpellChecker, michael@0: public nsIEditActionListener, michael@0: public nsIDOMEventListener, michael@0: public nsSupportsWeakReference michael@0: { michael@0: private: michael@0: friend class mozInlineSpellStatus; michael@0: friend class InitEditorSpellCheckCallback; michael@0: friend class UpdateCurrentDictionaryCallback; michael@0: friend class AutoChangeNumPendingSpellChecks; michael@0: friend class mozInlineSpellResume; michael@0: michael@0: // Access with CanEnableInlineSpellChecking michael@0: enum SpellCheckingState { SpellCheck_Uninitialized = -1, michael@0: SpellCheck_NotAvailable = 0, michael@0: SpellCheck_Available = 1}; michael@0: static SpellCheckingState gCanEnableSpellChecking; michael@0: michael@0: nsWeakPtr mEditor; michael@0: nsCOMPtr mSpellCheck; michael@0: nsCOMPtr mPendingSpellCheck; michael@0: nsCOMPtr mTreeWalker; michael@0: nsCOMPtr mConverter; michael@0: michael@0: int32_t mNumWordsInSpellSelection; michael@0: int32_t mMaxNumWordsInSpellSelection; michael@0: michael@0: // How many misspellings we can add at once. This is often less than the max michael@0: // total number of misspellings. When you have a large textarea prepopulated michael@0: // with text with many misspellings, we can hit this limit. By making it michael@0: // lower than the total number of misspelled words, new text typed by the michael@0: // user can also have spellchecking in it. michael@0: int32_t mMaxMisspellingsPerCheck; michael@0: michael@0: // we need to keep track of the current text position in the document michael@0: // so we can spell check the old word when the user clicks around the document. michael@0: nsCOMPtr mCurrentSelectionAnchorNode; michael@0: int32_t mCurrentSelectionOffset; michael@0: michael@0: // Tracks the number of pending spell checks *and* async operations that may michael@0: // lead to spell checks, like updating the current dictionary. This is michael@0: // necessary so that observers can know when to wait for spell check to michael@0: // complete. michael@0: int32_t mNumPendingSpellChecks; michael@0: michael@0: // The number of calls to UpdateCurrentDictionary that haven't finished yet. michael@0: int32_t mNumPendingUpdateCurrentDictionary; michael@0: michael@0: // This number is incremented each time the spell checker is disabled so that michael@0: // pending scheduled spell checks and UpdateCurrentDictionary calls can be michael@0: // ignored when they finish. michael@0: uint32_t mDisabledAsyncToken; michael@0: michael@0: // When mPendingSpellCheck is non-null, this is the callback passed when michael@0: // it was initialized. michael@0: nsRefPtr mPendingInitEditorSpellCheckCallback; michael@0: michael@0: // Set when we have spellchecked after the last edit operation. See the michael@0: // commment at the top of the .cpp file for more info. michael@0: bool mNeedsCheckAfterNavigation; michael@0: michael@0: // Set when we have a pending mozInlineSpellResume which will check michael@0: // the whole document. michael@0: bool mFullSpellCheckScheduled; michael@0: michael@0: // Maintains state during the asynchronous UpdateCurrentDictionary call. michael@0: nsString mPreviousDictionary; michael@0: michael@0: public: michael@0: michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_NSIEDITACTIONLISTENER michael@0: NS_DECL_NSIINLINESPELLCHECKER michael@0: NS_DECL_NSIDOMEVENTLISTENER michael@0: NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(mozInlineSpellChecker, nsIDOMEventListener) michael@0: michael@0: // returns true if there are any spell checking dictionaries available michael@0: static bool CanEnableInlineSpellChecking(); michael@0: // update the cached value whenever the list of available dictionaries changes michael@0: static void UpdateCanEnableInlineSpellChecking(); michael@0: michael@0: nsresult Blur(nsIDOMEvent* aEvent); michael@0: nsresult MouseClick(nsIDOMEvent* aMouseEvent); michael@0: nsresult KeyPress(nsIDOMEvent* aKeyEvent); michael@0: michael@0: mozInlineSpellChecker(); michael@0: virtual ~mozInlineSpellChecker(); michael@0: michael@0: // spell checks all of the words between two nodes michael@0: nsresult SpellCheckBetweenNodes(nsIDOMNode *aStartNode, michael@0: int32_t aStartOffset, michael@0: nsIDOMNode *aEndNode, michael@0: int32_t aEndOffset); michael@0: michael@0: // examines the dom node in question and returns true if the inline spell michael@0: // checker should skip the node (i.e. the text is inside of a block quote michael@0: // or an e-mail signature...) michael@0: nsresult SkipSpellCheckForNode(nsIEditor* aEditor, michael@0: nsIDOMNode *aNode, bool * aCheckSpelling); michael@0: michael@0: nsresult SpellCheckAfterChange(nsIDOMNode* aCursorNode, int32_t aCursorOffset, michael@0: nsIDOMNode* aPreviousNode, int32_t aPreviousOffset, michael@0: nsISelection* aSpellCheckSelection); michael@0: michael@0: // spell check the text contained within aRange, potentially scheduling michael@0: // another check in the future if the time threshold is reached michael@0: nsresult ScheduleSpellCheck(const mozInlineSpellStatus& aStatus); michael@0: michael@0: nsresult DoSpellCheckSelection(mozInlineSpellWordUtil& aWordUtil, michael@0: nsISelection* aSpellCheckSelection, michael@0: mozInlineSpellStatus* aStatus); michael@0: nsresult DoSpellCheck(mozInlineSpellWordUtil& aWordUtil, michael@0: nsISelection *aSpellCheckSelection, michael@0: mozInlineSpellStatus* aStatus, michael@0: bool* aDoneChecking); michael@0: michael@0: // helper routine to determine if a point is inside of the passed in selection. michael@0: nsresult IsPointInSelection(nsISelection *aSelection, michael@0: nsIDOMNode *aNode, michael@0: int32_t aOffset, michael@0: nsIDOMRange **aRange); michael@0: michael@0: nsresult CleanupRangesInSelection(nsISelection *aSelection); michael@0: michael@0: nsresult RemoveRange(nsISelection *aSpellCheckSelection, nsIDOMRange * aRange); michael@0: nsresult AddRange(nsISelection *aSpellCheckSelection, nsIDOMRange * aRange); michael@0: bool SpellCheckSelectionIsFull() { return mNumWordsInSpellSelection >= mMaxNumWordsInSpellSelection; } michael@0: michael@0: nsresult MakeSpellCheckRange(nsIDOMNode* aStartNode, int32_t aStartOffset, michael@0: nsIDOMNode* aEndNode, int32_t aEndOffset, michael@0: nsRange** aRange); michael@0: michael@0: // DOM and editor event registration helper routines michael@0: nsresult RegisterEventListeners(); michael@0: nsresult UnregisterEventListeners(); michael@0: nsresult HandleNavigationEvent(bool aForceWordSpellCheck, int32_t aNewPositionOffset = 0); michael@0: michael@0: nsresult GetSpellCheckSelection(nsISelection ** aSpellCheckSelection); michael@0: nsresult SaveCurrentSelectionPosition(); michael@0: michael@0: nsresult ResumeCheck(mozInlineSpellStatus* aStatus); michael@0: michael@0: protected: michael@0: michael@0: // called when async nsIEditorSpellCheck methods complete michael@0: nsresult EditorSpellCheckInited(); michael@0: nsresult CurrentDictionaryUpdated(); michael@0: michael@0: // track the number of pending spell checks and async operations that may lead michael@0: // to spell checks, notifying observers accordingly michael@0: void ChangeNumPendingSpellChecks(int32_t aDelta, michael@0: nsIEditor* aEditor = nullptr); michael@0: void NotifyObservers(const char* aTopic, nsIEditor* aEditor); michael@0: }; michael@0: michael@0: #endif /* __mozinlinespellchecker_h__ */