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 __selectionstate_h__ michael@0: #define __selectionstate_h__ michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIDOMNode.h" michael@0: #include "nsINode.h" michael@0: #include "nsTArray.h" michael@0: #include "nscore.h" michael@0: michael@0: class nsCycleCollectionTraversalCallback; michael@0: class nsIDOMCharacterData; michael@0: class nsIDOMRange; michael@0: class nsISelection; michael@0: class nsRange; michael@0: namespace mozilla { michael@0: namespace dom { michael@0: class Selection; michael@0: } michael@0: } michael@0: michael@0: /*************************************************************************** michael@0: * class for recording selection info. stores selection as collection of michael@0: * { {startnode, startoffset} , {endnode, endoffset} } tuples. Can't store michael@0: * ranges since dom gravity will possibly change the ranges. michael@0: */ michael@0: michael@0: // first a helper struct for saving/setting ranges michael@0: struct nsRangeStore MOZ_FINAL michael@0: { michael@0: nsRangeStore(); michael@0: michael@0: private: michael@0: // Private destructor, to discourage deletion outside of Release(): michael@0: ~nsRangeStore(); michael@0: michael@0: public: michael@0: nsresult StoreRange(nsIDOMRange *aRange); michael@0: nsresult GetRange(nsRange** outRange); michael@0: michael@0: NS_INLINE_DECL_REFCOUNTING(nsRangeStore) michael@0: michael@0: nsCOMPtr startNode; michael@0: int32_t startOffset; michael@0: nsCOMPtr endNode; michael@0: int32_t endOffset; michael@0: // DEBUG: static int32_t n; michael@0: }; michael@0: michael@0: class nsSelectionState michael@0: { michael@0: public: michael@0: michael@0: nsSelectionState(); michael@0: ~nsSelectionState(); michael@0: michael@0: void DoTraverse(nsCycleCollectionTraversalCallback &cb); michael@0: void DoUnlink() { MakeEmpty(); } michael@0: michael@0: void SaveSelection(mozilla::dom::Selection *aSel); michael@0: nsresult RestoreSelection(nsISelection *aSel); michael@0: bool IsCollapsed(); michael@0: bool IsEqual(nsSelectionState *aSelState); michael@0: void MakeEmpty(); michael@0: bool IsEmpty(); michael@0: protected: michael@0: nsTArray > mArray; michael@0: michael@0: friend class nsRangeUpdater; michael@0: }; michael@0: michael@0: class nsRangeUpdater michael@0: { michael@0: public: michael@0: michael@0: nsRangeUpdater(); michael@0: ~nsRangeUpdater(); michael@0: michael@0: void RegisterRangeItem(nsRangeStore *aRangeItem); michael@0: void DropRangeItem(nsRangeStore *aRangeItem); michael@0: nsresult RegisterSelectionState(nsSelectionState &aSelState); michael@0: nsresult DropSelectionState(nsSelectionState &aSelState); michael@0: michael@0: // editor selection gravity routines. Note that we can't always depend on michael@0: // DOM Range gravity to do what we want to the "real" selection. For instance, michael@0: // if you move a node, that corresponds to deleting it and reinserting it. michael@0: // DOM Range gravity will promote the selection out of the node on deletion, michael@0: // which is not what you want if you know you are reinserting it. michael@0: nsresult SelAdjCreateNode(nsIDOMNode *aParent, int32_t aPosition); michael@0: nsresult SelAdjInsertNode(nsIDOMNode *aParent, int32_t aPosition); michael@0: void SelAdjDeleteNode(nsIDOMNode *aNode); michael@0: nsresult SelAdjSplitNode(nsIDOMNode *aOldRightNode, int32_t aOffset, nsIDOMNode *aNewLeftNode); michael@0: nsresult SelAdjJoinNodes(nsIDOMNode *aLeftNode, michael@0: nsIDOMNode *aRightNode, michael@0: nsIDOMNode *aParent, michael@0: int32_t aOffset, michael@0: int32_t aOldLeftNodeLength); michael@0: nsresult SelAdjInsertText(nsIDOMCharacterData *aTextNode, int32_t aOffset, const nsAString &aString); michael@0: nsresult SelAdjDeleteText(nsIDOMCharacterData *aTextNode, int32_t aOffset, int32_t aLength); michael@0: // the following gravity routines need will/did sandwiches, because the other gravity michael@0: // routines will be called inside of these sandwiches, but should be ignored. michael@0: nsresult WillReplaceContainer(); michael@0: nsresult DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode); michael@0: nsresult WillRemoveContainer(); michael@0: nsresult DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, int32_t aOffset, uint32_t aNodeOrigLen); michael@0: nsresult WillInsertContainer(); michael@0: nsresult DidInsertContainer(); michael@0: void WillMoveNode(); michael@0: void DidMoveNode(nsINode* aOldParent, int32_t aOldOffset, michael@0: nsINode* aNewParent, int32_t aNewOffset); michael@0: protected: michael@0: nsTArray > mArray; michael@0: bool mLock; michael@0: }; michael@0: michael@0: michael@0: /*************************************************************************** michael@0: * helper class for using nsSelectionState. stack based class for doing michael@0: * preservation of dom points across editor actions michael@0: */ michael@0: michael@0: class MOZ_STACK_CLASS nsAutoTrackDOMPoint michael@0: { michael@0: private: michael@0: nsRangeUpdater &mRU; michael@0: nsCOMPtr *mNode; michael@0: int32_t *mOffset; michael@0: nsRefPtr mRangeItem; michael@0: public: michael@0: nsAutoTrackDOMPoint(nsRangeUpdater &aRangeUpdater, nsCOMPtr *aNode, int32_t *aOffset) : michael@0: mRU(aRangeUpdater) michael@0: ,mNode(aNode) michael@0: ,mOffset(aOffset) michael@0: { michael@0: mRangeItem = new nsRangeStore(); michael@0: mRangeItem->startNode = *mNode; michael@0: mRangeItem->endNode = *mNode; michael@0: mRangeItem->startOffset = *mOffset; michael@0: mRangeItem->endOffset = *mOffset; michael@0: mRU.RegisterRangeItem(mRangeItem); michael@0: } michael@0: michael@0: ~nsAutoTrackDOMPoint() michael@0: { michael@0: mRU.DropRangeItem(mRangeItem); michael@0: *mNode = mRangeItem->startNode; michael@0: *mOffset = mRangeItem->startOffset; michael@0: } michael@0: }; michael@0: michael@0: michael@0: michael@0: /*************************************************************************** michael@0: * another helper class for nsSelectionState. stack based class for doing michael@0: * Will/DidReplaceContainer() michael@0: */ michael@0: michael@0: class MOZ_STACK_CLASS nsAutoReplaceContainerSelNotify michael@0: { michael@0: private: michael@0: nsRangeUpdater &mRU; michael@0: nsIDOMNode *mOriginalNode; michael@0: nsIDOMNode *mNewNode; michael@0: michael@0: public: michael@0: nsAutoReplaceContainerSelNotify(nsRangeUpdater &aRangeUpdater, nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) : michael@0: mRU(aRangeUpdater) michael@0: ,mOriginalNode(aOriginalNode) michael@0: ,mNewNode(aNewNode) michael@0: { michael@0: mRU.WillReplaceContainer(); michael@0: } michael@0: michael@0: ~nsAutoReplaceContainerSelNotify() michael@0: { michael@0: mRU.DidReplaceContainer(mOriginalNode, mNewNode); michael@0: } michael@0: }; michael@0: michael@0: michael@0: /*************************************************************************** michael@0: * another helper class for nsSelectionState. stack based class for doing michael@0: * Will/DidRemoveContainer() michael@0: */ michael@0: michael@0: class MOZ_STACK_CLASS nsAutoRemoveContainerSelNotify michael@0: { michael@0: private: michael@0: nsRangeUpdater &mRU; michael@0: nsIDOMNode *mNode; michael@0: nsIDOMNode *mParent; michael@0: int32_t mOffset; michael@0: uint32_t mNodeOrigLen; michael@0: michael@0: public: michael@0: nsAutoRemoveContainerSelNotify(nsRangeUpdater& aRangeUpdater, michael@0: nsINode* aNode, michael@0: nsINode* aParent, michael@0: int32_t aOffset, michael@0: uint32_t aNodeOrigLen) michael@0: : mRU(aRangeUpdater) michael@0: , mNode(aNode->AsDOMNode()) michael@0: , mParent(aParent->AsDOMNode()) michael@0: , mOffset(aOffset) michael@0: , mNodeOrigLen(aNodeOrigLen) michael@0: { michael@0: mRU.WillRemoveContainer(); michael@0: } michael@0: michael@0: ~nsAutoRemoveContainerSelNotify() michael@0: { michael@0: mRU.DidRemoveContainer(mNode, mParent, mOffset, mNodeOrigLen); michael@0: } michael@0: }; michael@0: michael@0: /*************************************************************************** michael@0: * another helper class for nsSelectionState. stack based class for doing michael@0: * Will/DidInsertContainer() michael@0: */ michael@0: michael@0: class MOZ_STACK_CLASS nsAutoInsertContainerSelNotify michael@0: { michael@0: private: michael@0: nsRangeUpdater &mRU; michael@0: michael@0: public: michael@0: nsAutoInsertContainerSelNotify(nsRangeUpdater &aRangeUpdater) : michael@0: mRU(aRangeUpdater) michael@0: { michael@0: mRU.WillInsertContainer(); michael@0: } michael@0: michael@0: ~nsAutoInsertContainerSelNotify() michael@0: { michael@0: mRU.DidInsertContainer(); michael@0: } michael@0: }; michael@0: michael@0: michael@0: /*************************************************************************** michael@0: * another helper class for nsSelectionState. stack based class for doing michael@0: * Will/DidMoveNode() michael@0: */ michael@0: michael@0: class MOZ_STACK_CLASS nsAutoMoveNodeSelNotify michael@0: { michael@0: private: michael@0: nsRangeUpdater &mRU; michael@0: nsINode* mOldParent; michael@0: nsINode* mNewParent; michael@0: int32_t mOldOffset; michael@0: int32_t mNewOffset; michael@0: michael@0: public: michael@0: nsAutoMoveNodeSelNotify(nsRangeUpdater &aRangeUpdater, michael@0: nsINode* aOldParent, michael@0: int32_t aOldOffset, michael@0: nsINode* aNewParent, michael@0: int32_t aNewOffset) michael@0: : mRU(aRangeUpdater) michael@0: , mOldParent(aOldParent) michael@0: , mNewParent(aNewParent) michael@0: , mOldOffset(aOldOffset) michael@0: , mNewOffset(aNewOffset) michael@0: { michael@0: MOZ_ASSERT(aOldParent); michael@0: MOZ_ASSERT(aNewParent); michael@0: mRU.WillMoveNode(); michael@0: } michael@0: michael@0: ~nsAutoMoveNodeSelNotify() michael@0: { michael@0: mRU.DidMoveNode(mOldParent, mOldOffset, mNewParent, mNewOffset); michael@0: } michael@0: }; michael@0: michael@0: #endif michael@0: michael@0: