diff --git a/mozilla/editor/base/SplitElementTxn.cpp b/mozilla/editor/base/SplitElementTxn.cpp index 513f8847f0a..41f1cdc7d2c 100644 --- a/mozilla/editor/base/SplitElementTxn.cpp +++ b/mozilla/editor/base/SplitElementTxn.cpp @@ -69,6 +69,8 @@ NS_IMETHODIMP SplitElementTxn::Do(void) NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewLeftNode)), "could not create element."); if (NS_FAILED(result)) return result; if (!mNewLeftNode) return NS_ERROR_NULL_POINTER; + mEditor->MarkNodeDirty(mExistingRightNode); + if (gNoisy) { printf(" created left node = %p\n", mNewLeftNode.get()); } // get the parent node @@ -80,9 +82,6 @@ NS_IMETHODIMP SplitElementTxn::Do(void) result = mEditor->SplitNodeImpl(mExistingRightNode, mOffset, mNewLeftNode, mParent); if (NS_SUCCEEDED(result) && mNewLeftNode) { - // Insert formatting whitespace for the new node: - mEditor->InsertFormattingForNode(mExistingRightNode); - nsCOMPtrselection; mEditor->GetSelection(getter_AddRefs(selection)); if (NS_FAILED(result)) return result; diff --git a/mozilla/editor/base/nsEditRules.h b/mozilla/editor/base/nsEditRules.h index b1dcfd5bc2c..55af338ef2c 100644 --- a/mozilla/editor/base/nsEditRules.h +++ b/mozilla/editor/base/nsEditRules.h @@ -23,6 +23,11 @@ #ifndef nsEditRules_h__ #define nsEditRules_h__ +#define NS_IEDITRULES_IID \ +{ /* a6cf911b-15b3-11d2-932e-00805f8add32 */ \ +0xa6cf911b, 0x15b3, 0x11d2, \ +{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32} } + class nsHTMLEditor; class nsIDOMSelection; @@ -40,20 +45,21 @@ class nsRulesInfo int action; }; -MOZ_DECL_CTOR_COUNTER(nsEditRules); - /*************************************************************************** * Interface of editing rules. * */ -class nsEditRules +class nsIEditRules : public nsISupports { public: - nsEditRules() { MOZ_COUNT_CTOR(nsEditRules); } - virtual ~nsEditRules() { MOZ_COUNT_DTOR(nsEditRules); } + static const nsIID& GetIID() { static nsIID iid = NS_IEDITRULES_IID; return iid; } + +//Interfaces for addref and release and queryinterface +//NOTE: Use NS_DECL_ISUPPORTS_INHERITED in any class inherited from nsIEditRules + NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags)=0; NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection)=0; - NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection, PRBool aSetSelection)=0; + NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)=0; NS_IMETHOD WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled)=0; NS_IMETHOD DidDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, nsresult aResult)=0; NS_IMETHOD GetFlags(PRUint32 *aFlags)=0; diff --git a/mozilla/editor/base/nsEditor.cpp b/mozilla/editor/base/nsEditor.cpp index f2da926e434..fd08d563bb5 100644 --- a/mozilla/editor/base/nsEditor.cpp +++ b/mozilla/editor/base/nsEditor.cpp @@ -144,13 +144,13 @@ PRInt32 nsEditor::gInstanceCount = 0; * { {startnode, startoffset} , {endnode, endoffset} } tuples. Cant store * ranges since dom gravity will possibly change the ranges. */ -nsSelectionState::nsSelectionState() {} +nsSelectionState::nsSelectionState() : mArray(), mLock(PR_FALSE) {} nsSelectionState::~nsSelectionState() { // free any items in the array SelRangeStore *item; - while (item = (SelRangeStore*)mArray.ElementAt(0)) + while ((item = (SelRangeStore*)mArray.ElementAt(0))) { delete item; mArray.RemoveElementAt(0); @@ -180,7 +180,7 @@ nsSelectionState::SaveSelection(nsIDOMSelection *aSel) // else if we have too many, delete them else if (rangeCount>arrayCount) { - while (item = (SelRangeStore*)mArray.ElementAt(rangeCount)) + while ((item = (SelRangeStore*)mArray.ElementAt(rangeCount))) { delete item; mArray.RemoveElementAt(rangeCount); @@ -208,7 +208,7 @@ nsSelectionState::RestoreSelection(nsIDOMSelection *aSel) PRInt32 i, arrayCount = mArray.Count(); SelRangeStore *item; - // clear ot cur selection + // clear out selection aSel->ClearSelection(); // set the selection ranges anew @@ -273,7 +273,334 @@ nsSelectionState::IsEqual(nsSelectionState *aSelState) return PR_TRUE; } +// notification routines used to update the saved selection state in response to +// document editing. +nsresult +nsSelectionState::SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition) +{ + if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... + if (!aParent) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + SelRangeStore *item; + + for (i=0; istartNode.get() == aParent) && (item->startOffset > aPosition)) + item->startOffset++; + if ((item->endNode.get() == aParent) && (item->endOffset > aPosition)) + item->endOffset++; + } + return NS_OK; +} + +nsresult +nsSelectionState::SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition) +{ + return SelAdjCreateNode(aParent, aPosition); +} + + +nsresult +nsSelectionState::SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset) +{ + if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... + if (!aNode) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + SelRangeStore *item; + + for (i=0; istartNode.get() == aParent) && (item->startOffset > aOffset)) + item->startOffset--; + if ((item->endNode.get() == aParent) && (item->endOffset > aOffset)) + item->endOffset--; + } + // MOOSE: also check inside of aNode, expensive. But in theory, we shouldn't + // actually hit this case in the usage i forsee for this. + return NS_OK; +} + + +nsresult +nsSelectionState::SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode) +{ + if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... + if (!aOldRightNode || !aNewLeftNode) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + nsCOMPtr parent; + PRInt32 offset; + nsresult result = nsEditor::GetNodeLocation(aOldRightNode, &parent, &offset); + if (NS_FAILED(result)) return result; + + // first part is same as inserting aNewLeftnode + result = SelAdjInsertNode(parent,offset); + if (NS_FAILED(result)) return result; + + // next step is to check for range enpoints inside aOldRightNode + SelRangeStore *item; + + for (i=0; istartNode.get() == aOldRightNode) + { + if (item->startOffset > aOffset) + { + item->startOffset -= aOffset; + } + else + { + item->startNode = aNewLeftNode; + } + } + if (item->endNode.get() == aOldRightNode) + { + if (item->endOffset > aOffset) + { + item->endOffset -= aOffset; + } + else + { + item->endNode = aNewLeftNode; + } + } + } + return NS_OK; +} + + +nsresult +nsSelectionState::SelAdjJoinNodes(nsIDOMNode *aLeftNode, + nsIDOMNode *aRightNode, + nsIDOMNode *aParent, + PRInt32 aOffset, + PRInt32 aOldLeftNodeLength) +{ + if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... + if (!aLeftNode || !aRightNode || !aParent) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + SelRangeStore *item; + + for (i=0; istartNode.get() == aParent) + { + if (item->startOffset > aOffset) + { + item->startOffset--; + } + else if (item->startOffset == aOffset) + { + // join keeps right hand node + item->startNode = aRightNode; + item->startOffset = aOldLeftNodeLength; + } + } + if (item->endNode.get() == aParent) + { + if (item->endOffset > aOffset) + { + item->endOffset--; + } + else if (item->endOffset == aOffset) + { + // join keeps right hand node + item->endNode = aRightNode; + item->endOffset = aOldLeftNodeLength; + } + } + // adjust endpoints in aRightNode + if (item->startNode.get() == aRightNode) + item->startOffset += aOldLeftNodeLength; + if (item->endNode.get() == aRightNode) + item->endOffset += aOldLeftNodeLength; + } + + return NS_OK; +} + + +nsresult +nsSelectionState::SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString) +{ + if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... + return NS_OK; +} + + +nsresult +nsSelectionState::SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength) +{ + if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... + return NS_OK; +} + + +nsresult +nsSelectionState::WillReplaceContainer() +{ + if (mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_TRUE; + return NS_OK; +} + + +nsresult +nsSelectionState::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) +{ + if (!mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_FALSE; + + if (!aOriginalNode || !aNewNode) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + SelRangeStore *item; + + for (i=0; istartNode.get() == aOriginalNode) + item->startNode = aNewNode; + if (item->endNode.get() == aOriginalNode) + item->endNode = aNewNode; + } + return NS_OK; +} + + +nsresult +nsSelectionState::WillRemoveContainer() +{ + if (mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_TRUE; + return NS_OK; +} + + +nsresult +nsSelectionState::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen) +{ + if (!mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_FALSE; + + if (!aNode || !aParent) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + SelRangeStore *item; + + for (i=0; istartNode.get() == aNode) + { + item->startNode = aParent; + item->startOffset += aOffset; + } + if (item->endNode.get() == aNode) + { + item->endNode = aParent; + item->endOffset += aOffset; + } + if ((item->startNode.get() == aParent) && (item->startOffset > aOffset)) + item->startOffset += (PRInt32)aNodeOrigLen-1; + if ((item->endNode.get() == aParent) && (item->endOffset > aOffset)) + item->endOffset += (PRInt32)aNodeOrigLen-1; + } + return NS_OK; +} + + +nsresult +nsSelectionState::WillInsertContainer() +{ + if (mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_TRUE; + return NS_OK; +} + + +nsresult +nsSelectionState::DidInsertContainer() +{ + if (!mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_FALSE; + return NS_OK; +} + + +nsresult +nsSelectionState::WillMoveNode() +{ + if (mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_TRUE; + return NS_OK; +} + + +nsresult +nsSelectionState::DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset) +{ + if (!mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_FALSE; + + if (!aOldParent || !aNewParent) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + SelRangeStore *item; + + for (i=0; istartNode.get() == aOldParent) && (item->startOffset > aOldOffset)) + item->startOffset--; + if ((item->endNode.get() == aOldParent) && (item->endOffset > aOldOffset)) + item->endOffset--; + + // and like an insert in aNewParent + if ((item->startNode.get() == aNewParent) && (item->startOffset > aNewOffset)) + item->startOffset++; + if ((item->endNode.get() == aNewParent) && (item->endOffset > aNewOffset)) + item->endOffset++; + } + return NS_OK; +} + + + +/*************************************************************************** + * helper class for nsSelectionState. SelRangeStore stores range endpoints. + */ nsresult SelRangeStore::StoreRange(nsIDOMRange *aRange) { @@ -301,8 +628,135 @@ nsresult SelRangeStore::GetRange(nsCOMPtr *outRange) return res; } +/*************************************************************************** + * another helper class for nsSelectionState. stack based class for doing + * Will/DidReplaceContainer() + */ + +class nsAutoReplaceContainerSelNotify +{ + private: + nsSelectionState *mSel; + nsIDOMNode *mOriginalNode; + nsIDOMNode *mNewNode; + + public: + nsAutoReplaceContainerSelNotify(nsSelectionState *aSelState, nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) : + mSel(aSelState) + ,mOriginalNode(aOriginalNode) + ,mNewNode(aNewNode) + { + if (mSel) mSel->WillReplaceContainer(); + } + + ~nsAutoReplaceContainerSelNotify() + { + if (mSel) mSel->DidReplaceContainer(mOriginalNode, mNewNode); + } +}; + + +/*************************************************************************** + * another helper class for nsSelectionState. stack based class for doing + * Will/DidRemoveContainer() + */ + +class nsAutoRemoveContainerSelNotify +{ + private: + nsSelectionState *mSel; + nsIDOMNode *mNode; + nsIDOMNode *mParent; + PRInt32 mOffset; + PRUint32 mNodeOrigLen; + + public: + nsAutoRemoveContainerSelNotify(nsSelectionState *aSelState, + nsIDOMNode *aNode, + nsIDOMNode *aParent, + PRInt32 aOffset, + PRUint32 aNodeOrigLen) : + mSel(aSelState) + ,mNode(aNode) + ,mParent(aParent) + ,mOffset(aOffset) + ,mNodeOrigLen(aNodeOrigLen) + { + if (mSel) mSel->WillRemoveContainer(); + } + + ~nsAutoRemoveContainerSelNotify() + { + if (mSel) mSel->DidRemoveContainer(mNode, mParent, mOffset, mNodeOrigLen); + } +}; + +/*************************************************************************** + * another helper class for nsSelectionState. stack based class for doing + * Will/DidInsertContainer() + */ + +class nsAutoInsertContainerSelNotify +{ + private: + nsSelectionState *mSel; + + public: + nsAutoInsertContainerSelNotify(nsSelectionState *aSelState) : + mSel(aSelState) + { + if (mSel) mSel->WillInsertContainer(); + } + + ~nsAutoInsertContainerSelNotify() + { + if (mSel) mSel->DidInsertContainer(); + } +}; + + +/*************************************************************************** + * another helper class for nsSelectionState. stack based class for doing + * Will/DidMoveNode() + */ + +class nsAutoMoveNodeSelNotify +{ + private: + nsSelectionState *mSel; + nsIDOMNode *mOldParent; + nsIDOMNode *mNewParent; + PRInt32 mOldOffset; + PRInt32 mNewOffset; + + public: + nsAutoMoveNodeSelNotify(nsSelectionState *aSelState, + nsIDOMNode *aOldParent, + PRInt32 aOldOffset, + nsIDOMNode *aNewParent, + PRInt32 aNewOffset) : + mSel(aSelState) + ,mOldParent(aOldParent) + ,mOldOffset(aOldOffset) + ,mNewParent(aNewParent) + ,mNewOffset(aNewOffset) + { + if (mSel) mSel->WillMoveNode(); + } + + ~nsAutoMoveNodeSelNotify() + { + if (mSel) mSel->DidMoveNode(mOldParent, mOldOffset, mNewParent, mNewOffset); + } +}; + + +//--------------------------------------------------------------------------- +// +// nsEditor: base editor class implementation +// +//--------------------------------------------------------------------------- -//class implementations are in order they are declared in nsEditor.h nsEditor::nsEditor() : mPresShellWeak(nsnull) @@ -312,6 +766,7 @@ nsEditor::nsEditor() , mPlaceHolderName(nsnull) , mPlaceHolderBatch(0) , mSelState(nsnull) +, mSavedSel(nsnull) , mShouldTxnSetSelection(PR_TRUE) , mBodyElement(nsnull) , mInIMEMode(PR_FALSE) @@ -361,39 +816,11 @@ nsEditor::~nsEditor() NS_IMPL_ADDREF(nsEditor) NS_IMPL_RELEASE(nsEditor) - - -NS_IMETHODIMP -nsEditor::QueryInterface(REFNSIID aIID, void** aInstancePtr) -{ - if (!aInstancePtr) - return NS_ERROR_NULL_POINTER; - - *aInstancePtr = nsnull; - if (aIID.Equals(NS_GET_IID(nsISupports))) { - nsIEditor *tmp = this; - nsISupports *tmp2 = tmp; - *aInstancePtr = (void*)tmp2; - NS_ADDREF_THIS(); - return NS_OK; - } - if (aIID.Equals(NS_GET_IID(nsIEditor))) { - *aInstancePtr = (void*)(nsIEditor*)this; - NS_ADDREF_THIS(); - return NS_OK; - } - if (aIID.Equals(NS_GET_IID(nsIEditorIMESupport))) { - *aInstancePtr = (void*)(nsIEditorIMESupport*)this; - NS_ADDREF_THIS(); - return NS_OK; - } - - return NS_NOINTERFACE; -} +NS_IMPL_QUERY_INTERFACE2(nsEditor, nsIEditor, nsIEditorIMESupport) #ifdef XP_MAC #pragma mark - -#pragma mark --- nsIEditorMethods --- +#pragma mark nsIEditorMethods #pragma mark - #endif @@ -1111,6 +1538,64 @@ nsEditor::RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute) } +// +// Insert a noneditable text node, e.g. formatting whitespace +// +nsresult +nsEditor::InsertNoneditableTextNode(nsIDOMNode* parent, PRInt32 offset, + nsString& aStr) +{ + nsAutoString textNodeTag; + nsresult res = GetTextNodeTag(textNodeTag); + if (NS_FAILED(res)) + return res; + + // Can't call CreateNode, because that will call us recursively. + // So duplicate what it does: + CreateElementTxn *txn; + res = CreateTxnForCreateElement(textNodeTag, parent, offset, &txn); + if (NS_FAILED(res)) + return res; + + res = Do(txn); + if (NS_FAILED(res)) + return res; + + // Now get the pointer to the node we just created ... + nsCOMPtr newNode; + res = txn->GetNewNode(getter_AddRefs(newNode)); + + // The transaction system (if any) has taken ownwership of txn + NS_IF_RELEASE(txn); + + if (NS_FAILED(res)) + return res; + nsCOMPtr newTextNode; + newTextNode = do_QueryInterface(newNode); + if (!newTextNode) + return NS_ERROR_UNEXPECTED; + + // ... and set its text. + return newTextNode->SetData(aStr); +} + +NS_IMETHODIMP +nsEditor::MarkNodeDirty(nsIDOMNode* aNode) +{ + // mark the node dirty. + nsCOMPtr element (do_QueryInterface(aNode)); + if (element) + element->SetAttribute("_moz_dirty", ""); + return NS_OK; +} + + +#ifdef XP_MAC +#pragma mark - +#pragma mark main node manipulation routines +#pragma mark - +#endif + NS_IMETHODIMP nsEditor::CreateNode(const nsString& aTag, nsIDOMNode * aParent, PRInt32 aPosition, @@ -1145,6 +1630,8 @@ NS_IMETHODIMP nsEditor::CreateNode(const nsString& aTag, // The transaction system (if any) has taken ownwership of txn NS_IF_RELEASE(txn); + if (mSavedSel) mSavedSel->SelAdjCreateNode(aParent, aPosition); + if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -1185,6 +1672,8 @@ NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode, // The transaction system (if any) has taken ownwership of txn NS_IF_RELEASE(txn); + if (mSavedSel) mSavedSel->SelAdjInsertNode(aParent, aPosition); + if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -1232,6 +1721,8 @@ nsEditor::SplitNode(nsIDOMNode * aNode, // The transaction system (if any) has taken ownwership of txn NS_IF_RELEASE(txn); + if (mSavedSel) mSavedSel->SelAdjSplitNode(aNode, aOffset, *aNewLeftNode); + if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -1249,113 +1740,25 @@ nsEditor::SplitNode(nsIDOMNode * aNode, } -// -// Insert a noneditable text node, e.g. formatting whitespace -// -nsresult -nsEditor::InsertNoneditableTextNode(nsIDOMNode* parent, PRInt32 offset, - nsString& aStr) -{ - nsAutoString textNodeTag; - nsresult res = GetTextNodeTag(textNodeTag); - if (NS_FAILED(res)) - return res; - - // Can't call CreateNode, because that will call us recursively. - // So duplicate what it does: - CreateElementTxn *txn; - res = CreateTxnForCreateElement(textNodeTag, parent, offset, &txn); - if (NS_FAILED(res)) - return res; - - res = Do(txn); - if (NS_FAILED(res)) - return res; - - // Now get the pointer to the node we just created ... - nsCOMPtr newNode; - res = txn->GetNewNode(getter_AddRefs(newNode)); - - // The transaction system (if any) has taken ownwership of txn - NS_IF_RELEASE(txn); - - if (NS_FAILED(res)) - return res; - nsCOMPtr newTextNode; - newTextNode = do_QueryInterface(newNode); - if (!newTextNode) - return NS_ERROR_UNEXPECTED; - - // ... and set its text. - return newTextNode->SetData(aStr); -} - -// -// Figure out what formatting needs to go with this node, and insert it. -// -NS_IMETHODIMP -nsEditor::InsertFormattingForNode(nsIDOMNode* aNode) -{ - nsresult res; - - // Don't insert any formatting unless it's an element node - PRUint16 nodeType; - res = aNode->GetNodeType(&nodeType); - if (NS_FAILED(res)) - return res; - if (nodeType != nsIDOMNode::ELEMENT_NODE) - return NS_OK; - - // Insert formatting only for block nodes - // (would it be better to insert for any non-inline node?) - PRBool block; - res = IsNodeBlock(aNode, block); - if (NS_FAILED(res)) - return res; - if (!block) - return NS_OK; - - nsCOMPtr parent; - res = aNode->GetParentNode(getter_AddRefs(parent)); - if (NS_FAILED(res)) - return res; - PRInt32 offset = GetIndexOf(parent, aNode); - -#ifdef DEBUG_akkana - //DumpContentTree(); - nsString namestr; - aNode->GetNodeName(namestr); - char* nodename = namestr.ToNewCString(); - printf("Inserting formatting for node <%s> at offset %d\n", - nodename, offset); - Recycle(nodename); -#endif /* DEBUG_akkana */ - - // - // XXX We don't yet have a real formatter. As a cheap stopgap, - // XXX just insert a newline before and after each newly inserted tag. - // - - nsAutoString str ("\n"); - - // After the close tag - //res = InsertNoneditableTextNode(parent, offset+1, str); - - // Before the open tag - res = InsertNoneditableTextNode(parent, offset, str); - - return res; -} NS_IMETHODIMP nsEditor::JoinNodes(nsIDOMNode * aLeftNode, nsIDOMNode * aRightNode, nsIDOMNode * aParent) { - PRInt32 i; + PRInt32 i, offset; + PRUint32 oldLeftNodeLen; nsIEditActionListener *listener; nsAutoRules beginRulesSniffing(this, kOpJoinNode, nsIEditor::ePrevious); + // remember some values; later used for saved selection updating. + // find the offset between the nodes to be joined. + nsresult result = GetChildOffset(aRightNode, aParent, offset); + if (NS_FAILED(result)) return result; + // find the number of children of the lefthand node + result = GetLengthOfDOMNode(aLeftNode, oldLeftNodeLen); + if (NS_FAILED(result)) return result; + if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -1367,7 +1770,7 @@ nsEditor::JoinNodes(nsIDOMNode * aLeftNode, } JoinElementTxn *txn; - nsresult result = CreateTxnForJoinNode(aLeftNode, aRightNode, &txn); + result = CreateTxnForJoinNode(aLeftNode, aRightNode, &txn); if (NS_SUCCEEDED(result)) { result = Do(txn); } @@ -1375,6 +1778,8 @@ nsEditor::JoinNodes(nsIDOMNode * aLeftNode, // The transaction system (if any) has taken ownwership of txn NS_IF_RELEASE(txn); + if (mSavedSel) mSavedSel->SelAdjJoinNodes(aLeftNode, aRightNode, aParent, offset, (PRInt32)oldLeftNodeLen); + if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -1391,10 +1796,15 @@ nsEditor::JoinNodes(nsIDOMNode * aLeftNode, NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement) { - PRInt32 i; + PRInt32 i, offset; + nsCOMPtr parent; nsIEditActionListener *listener; nsAutoRules beginRulesSniffing(this, kOpCreateNode, nsIEditor::ePrevious); + // save node location for selection updating code. + nsresult result = GetNodeLocation(aElement, &parent, &offset); + if (NS_FAILED(result)) return result; + if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -1406,7 +1816,7 @@ NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement) } DeleteElementTxn *txn; - nsresult result = CreateTxnForDeleteElement(aElement, &txn); + result = CreateTxnForDeleteElement(aElement, &txn); if (NS_SUCCEEDED(result)) { result = Do(txn); } @@ -1414,6 +1824,8 @@ NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement) // The transaction system (if any) has taken ownwership of txn NS_IF_RELEASE(txn); + if (mSavedSel) mSavedSel->SelAdjDeleteNode(aElement, parent, offset); + if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -1427,24 +1839,218 @@ NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement) return result; } - -NS_IMETHODIMP nsEditor::OutputToString(nsString& aOutputString, - const nsString& aFormatType, - PRUint32 aFlags) +/////////////////////////////////////////////////////////////////////////// +// ReplaceContainer: replace inNode with a new node (outNode) which is contructed +// to be of type aNodeType. Put inNodes children into outNode. +// Callers responsibility to make sure inNode's children can +// go in outNode. +nsresult +nsEditor::ReplaceContainer(nsIDOMNode *inNode, + nsCOMPtr *outNode, + const nsString &aNodeType, + const nsString *aAttribute, + const nsString *aValue) { - // these should be implemented by derived classes. - return NS_ERROR_NOT_IMPLEMENTED; + if (!inNode || !outNode) + return NS_ERROR_NULL_POINTER; + nsresult res; + nsCOMPtr parent; + PRInt32 offset; + res = GetNodeLocation(inNode, &parent, &offset); + if (NS_FAILED(res)) return res; + + // get our doc + nsCOMPtrdoc; + res = GetDocument(getter_AddRefs(doc)); + if (NS_FAILED(res)) return res; + if (!doc) return NS_ERROR_NULL_POINTER; + + // create new container + nsCOMPtr elem; + res = doc->CreateElement(aNodeType, getter_AddRefs(elem)); + if (NS_FAILED(res)) return res; + *outNode = do_QueryInterface(elem); + + // set attribute if needed + if (aAttribute && aValue && !aAttribute->IsEmpty()) + { + res = elem->SetAttribute(*aAttribute, *aValue); + if (NS_FAILED(res)) return res; + } + + // notify our internal selection state listener + nsAutoReplaceContainerSelNotify selStateNotify(mSavedSel, inNode, *outNode); + + // move children into new container + PRBool bHasMoreChildren; + inNode->HasChildNodes(&bHasMoreChildren); + nsCOMPtr child; + offset = 0; + while (bHasMoreChildren) + { + inNode->GetLastChild(getter_AddRefs(child)); + res = DeleteNode(child); + if (NS_FAILED(res)) return res; + res = InsertNode(child, *outNode, 0); + if (NS_FAILED(res)) return res; + inNode->HasChildNodes(&bHasMoreChildren); + } + + // delete old container + res = DeleteNode(inNode); + if (NS_FAILED(res)) return res; + + // insert new container into tree + res = InsertNode( *outNode, parent, offset); + return res; } -NS_IMETHODIMP nsEditor::OutputToStream(nsIOutputStream* aOutputStream, - const nsString& aFormatType, - const nsString* aCharsetOverride, - PRUint32 aFlags) +/////////////////////////////////////////////////////////////////////////// +// RemoveContainer: remove inNode, reparenting it's children into their +// the parent of inNode +// +nsresult +nsEditor::RemoveContainer(nsIDOMNode *inNode) { - // these should be implemented by derived classes. - return NS_ERROR_NOT_IMPLEMENTED; + if (!inNode) + return NS_ERROR_NULL_POINTER; + nsresult res; + nsCOMPtr parent; + PRInt32 offset; + PRUint32 nodeOrigLen; + + res = GetNodeLocation(inNode, &parent, &offset); + if (NS_FAILED(res)) return res; + + // loop through the child nodes of inNode and promote them + // into inNode's parent. + PRBool bHasMoreChildren; + inNode->HasChildNodes(&bHasMoreChildren); + nsCOMPtr nodeList; + res = inNode->GetChildNodes(getter_AddRefs(nodeList)); + if (NS_FAILED(res)) return res; + if (!nodeList) return NS_ERROR_NULL_POINTER; + nodeList->GetLength(&nodeOrigLen); + + // notify our internal selection state listener + nsAutoRemoveContainerSelNotify selNotify(mSavedSel, inNode, parent, offset, nodeOrigLen); + + nsCOMPtr child; + while (bHasMoreChildren) + { + inNode->GetLastChild(getter_AddRefs(child)); + res = DeleteNode(child); + if (NS_FAILED(res)) return res; + res = InsertNode(child, parent, offset); + if (NS_FAILED(res)) return res; + inNode->HasChildNodes(&bHasMoreChildren); + } + res = DeleteNode(inNode); + return res; } + +/////////////////////////////////////////////////////////////////////////// +// InsertContainerAbove: insert a new parent for inNode, returned in outNode, +// which is contructed to be of type aNodeType. outNode becomes +// a child of inNode's earlier parent. +// Callers responsibility to make sure inNode's can be child +// of outNode, and outNode can be child of old parent. +nsresult +nsEditor::InsertContainerAbove( nsIDOMNode *inNode, + nsCOMPtr *outNode, + const nsString &aNodeType, + const nsString *aAttribute, + const nsString *aValue) +{ + if (!inNode || !outNode) + return NS_ERROR_NULL_POINTER; + nsresult res; + nsCOMPtr parent; + PRInt32 offset; + res = GetNodeLocation(inNode, &parent, &offset); + if (NS_FAILED(res)) return res; + + // get our doc + nsCOMPtrdoc; + res = GetDocument(getter_AddRefs(doc)); + if (NS_FAILED(res)) return res; + if (!doc) return NS_ERROR_NULL_POINTER; + + // create new container + nsCOMPtr elem; + res = doc->CreateElement(aNodeType, getter_AddRefs(elem)); + if (NS_FAILED(res)) return res; + *outNode = do_QueryInterface(elem); + + // set attribute if needed + if (aAttribute && aValue && !aAttribute->IsEmpty()) + { + res = elem->SetAttribute(*aAttribute, *aValue); + if (NS_FAILED(res)) return res; + } + + // notify our internal selection state listener + nsAutoInsertContainerSelNotify selNotify(mSavedSel); + + // put inNode in new parent, outNode + res = DeleteNode(inNode); + if (NS_FAILED(res)) return res; + res = InsertNode(inNode, *outNode, 0); + if (NS_FAILED(res)) return res; + + // put new parent in doc + res = InsertNode(*outNode, parent, offset); + return res; +} + +/////////////////////////////////////////////////////////////////////////// +// MoveNode: move aNode to {aParent,aOffset} +nsresult +nsEditor::MoveNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset) +{ + if (!aNode || !aParent) + return NS_ERROR_NULL_POINTER; + nsresult res; + + nsCOMPtr oldParent; + PRInt32 oldOffset; + res = GetNodeLocation(aNode, &oldParent, &oldOffset); + + if (aOffset == -1) + { + PRUint32 unsignedOffset; + // magic value meaning "move to end of aParent" + res = GetLengthOfDOMNode(aParent, unsignedOffset); + if (NS_FAILED(res)) return res; + aOffset = (PRInt32)unsignedOffset; + } + + // dont do anything if it's already in right place + if ((aParent == oldParent.get()) && (oldOffset == aOffset)) return NS_OK; + + // notify our internal selection state listener + nsAutoMoveNodeSelNotify selNotify(mSavedSel, oldParent, oldOffset, aParent, aOffset); + + // need to adjust aOffset if we are moving aNode further along in it's current parent + if ((aParent == oldParent.get()) && (oldOffset < aOffset)) + { + aOffset--; // this is because when we delete aNode, it will make the offsets after it off by one + } + + // put aNode in new parent + res = DeleteNode(aNode); + if (NS_FAILED(res)) return res; + res = InsertNode(aNode, aParent, aOffset); + return res; +} + +#ifdef XP_MAC +#pragma mark - +#pragma mark action listener maintainance +#pragma mark - +#endif + NS_IMETHODIMP nsEditor::AddEditActionListener(nsIEditActionListener *aListener) { @@ -1529,6 +2135,29 @@ nsEditor::RemoveDocumentStateListener(nsIDocumentStateListener *aListener) } +#ifdef XP_MAC +#pragma mark - +#pragma mark misc +#pragma mark - +#endif + +NS_IMETHODIMP nsEditor::OutputToString(nsString& aOutputString, + const nsString& aFormatType, + PRUint32 aFlags) +{ + // these should be implemented by derived classes. + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsEditor::OutputToStream(nsIOutputStream* aOutputStream, + const nsString& aFormatType, + const nsString* aCharsetOverride, + PRUint32 aFlags) +{ + // these should be implemented by derived classes. + return NS_ERROR_NOT_IMPLEMENTED; +} + NS_IMETHODIMP nsEditor::DumpContentTree() { @@ -1584,7 +2213,7 @@ nsEditor::DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed) #ifdef XP_MAC #pragma mark - -#pragma mark --- nsIEditorIMESupport --- +#pragma mark nsIEditorIMESupport #pragma mark - #endif @@ -1731,7 +2360,7 @@ nsEditor::ForceCompositionEnd() } #ifdef XP_MAC #pragma mark - -#pragma mark --- public nsEditor methods --- +#pragma mark public nsEditor methods #pragma mark - #endif /* Non-interface, public methods */ @@ -1812,7 +2441,7 @@ nsEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection) /** All editor operations which alter the doc should be followed * with a call to EndOperation, naming the action and direction */ NS_IMETHODIMP -nsEditor::EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection, PRBool aSetSelection) +nsEditor::EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection) { return NS_OK; } @@ -1894,7 +2523,7 @@ nsEditor::CloneAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) #ifdef XP_MAC #pragma mark - -#pragma mark --- Protected and static methods --- +#pragma mark Protected and static methods #pragma mark - #endif @@ -1920,6 +2549,89 @@ nsresult nsEditor::GetTextNodeTag(nsString& aOutString) } +NS_IMETHODIMP nsEditor::JoeInsertTextImpl(const nsString& aStringToInsert, + nsCOMPtr *aInOutNode, + PRInt32 *aInOutOffset, + nsIDOMDocument *aDoc) +{ + // NOTE: caller *must* have already used nsAutoTxnsConserveSelection stack-based + // class to turn off txn selection updating. Caller also turned on rules sniffing + // if desired. + + if (!aInOutNode || !*aInOutNode || !aInOutOffset || !aDoc) return NS_ERROR_NULL_POINTER; + if (aStringToInsert.IsEmpty()) return NS_OK; + nsCOMPtr nodeAsText = do_QueryInterface(*aInOutNode); + PRInt32 offset = *aInOutOffset; + nsresult res; + if (nodeAsText) + { + // we are inserting text into an existing text node. + res = JoeInsertTextIntoTextNodeImpl(aStringToInsert, nodeAsText, offset); + if (NS_FAILED(res)) return res; + *aInOutOffset += aStringToInsert.Length(); + } + else + { + nsCOMPtr newNode; + // we are inserting text into a non-text node + // first we have to create a textnode (this also populates it with the text) + res = aDoc->CreateTextNode(aStringToInsert, getter_AddRefs(nodeAsText)); + if (NS_FAILED(res)) return res; + if (!nodeAsText) return NS_ERROR_NULL_POINTER; + newNode = do_QueryInterface(nodeAsText); + // then we insert it into the dom tree + res = InsertNode(newNode, *aInOutNode, offset); + if (NS_FAILED(res)) return res; + *aInOutNode = newNode; + *aInOutOffset = aStringToInsert.Length(); + } + return res; +} + + +NS_IMETHODIMP nsEditor::JoeInsertTextIntoTextNodeImpl(const nsString& aStringToInsert, + nsIDOMCharacterData *aTextNode, + PRInt32 aOffset) +{ + EditTxn *txn; + nsresult result = CreateTxnForInsertText(aStringToInsert, aTextNode, aOffset, (InsertTextTxn**)&txn); + if (NS_FAILED(result)) return result; + + // let listeners know whats up + PRInt32 i; + nsIEditActionListener *listener; + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->WillInsertText(aTextNode, aOffset, aStringToInsert); + } + } + + BeginUpdateViewBatch(); + result = Do(txn); + // The transaction system (if any) has taken ownwership of txns. + // aggTxn released at end of routine. + NS_IF_RELEASE(txn); + EndUpdateViewBatch(); + + // let listeners know what happened + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->DidInsertText(aTextNode, aOffset, aStringToInsert, result); + } + } + + return result; +} + + NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert) { // First delete the selection if needed @@ -2014,10 +2726,7 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert) newTextNode = do_QueryInterface(newNode); if (newTextNode) { - nsAutoString placeholderText(" "); - newTextNode->SetData(placeholderText); selection->Collapse(newNode, 0); - selection->Extend(newNode, 1); result = InsertTextImpl(aStringToInsert); // this really recurses, right? } } @@ -2327,7 +3036,7 @@ NS_IMETHODIMP nsEditor::CreateTxnForJoinNode(nsIDOMNode *aLeftNode, #ifdef XP_MAC #pragma mark - -#pragma mark --- nsEditor public static helper methods --- +#pragma mark nsEditor public static helper methods #pragma mark - #endif @@ -2361,9 +3070,9 @@ nsEditor::SplitNodeImpl(nsIDOMNode * aExistingRightNode, nsCOMPtr selStartNode, selEndNode; PRInt32 selStartOffset, selEndOffset; result = GetStartNodeAndOffset(selection, &selStartNode, &selStartOffset); - if (NS_FAILED(result)) return result; + if (NS_FAILED(result)) selStartNode = nsnull; // if selection is cleared, remember that result = GetEndNodeAndOffset(selection, &selEndNode, &selEndOffset); - if (NS_FAILED(result)) return result; + if (NS_FAILED(result)) selStartNode = nsnull; // if selection is cleared, remember that nsCOMPtr resultNode; result = aParent->InsertBefore(aNewLeftNode, aExistingRightNode, getter_AddRefs(resultNode)); @@ -2421,9 +3130,9 @@ nsEditor::SplitNodeImpl(nsIDOMNode * aExistingRightNode, // editor wants us to set selection at split point selection->Collapse(aNewLeftNode, aOffset); } - else + else if (selStartNode) { - // and adjust the selection if needed + // else adjust the selection if needed. if selStartNode is null, then there was no selection. // HACK: this is overly simplified - multi-range selections need more work than this if (selStartNode.get() == aExistingRightNode) { @@ -2479,9 +3188,11 @@ nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep, nsCOMPtr selStartNode, selEndNode, parent; PRInt32 selStartOffset, selEndOffset, joinOffset, keepOffset; result = GetStartNodeAndOffset(selection, &selStartNode, &selStartOffset); - if (NS_FAILED(result)) return result; + if (NS_FAILED(result)) selStartNode = nsnull; result = GetEndNodeAndOffset(selection, &selEndNode, &selEndOffset); - if (NS_FAILED(result)) return result; + if (NS_FAILED(result)) selStartNode = nsnull; + + PRUint32 firstNodeLength; nsCOMPtr leftNode = aNodeToJoin; if (aNodeToKeepIsFirst) leftNode = aNodeToKeep; @@ -2495,45 +3206,47 @@ nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep, // if selection endpoint is between the nodes, remember it as being // in the one that is going away instead. This simplifies later selection // adjustment logic at end of this method. - if (selStartNode == parent) + if (selStartNode) { - if (aNodeToKeepIsFirst) + if (selStartNode == parent) { - if ((selStartOffset > keepOffset) && (selStartOffset <= joinOffset)) + if (aNodeToKeepIsFirst) { - selStartNode = aNodeToJoin; - selStartOffset = 0; + if ((selStartOffset > keepOffset) && (selStartOffset <= joinOffset)) + { + selStartNode = aNodeToJoin; + selStartOffset = 0; + } + } + else + { + if ((selStartOffset > joinOffset) && (selStartOffset <= keepOffset)) + { + selStartNode = aNodeToJoin; + selStartOffset = firstNodeLength; + } } } - else + if (selEndNode == parent) { - if ((selStartOffset > joinOffset) && (selStartOffset <= keepOffset)) + if (aNodeToKeepIsFirst) { - selStartNode = aNodeToJoin; - selStartOffset = firstNodeLength; + if ((selEndOffset > keepOffset) && (selEndOffset <= joinOffset)) + { + selEndNode = aNodeToJoin; + selEndOffset = 0; + } + } + else + { + if ((selEndOffset > joinOffset) && (selEndOffset <= keepOffset)) + { + selEndNode = aNodeToJoin; + selEndOffset = firstNodeLength; + } } } } - if (selEndNode == parent) - { - if (aNodeToKeepIsFirst) - { - if ((selEndOffset > keepOffset) && (selEndOffset <= joinOffset)) - { - selEndNode = aNodeToJoin; - selEndOffset = 0; - } - } - else - { - if ((selEndOffset > joinOffset) && (selEndOffset <= keepOffset)) - { - selEndNode = aNodeToJoin; - selEndOffset = firstNodeLength; - } - } - } - // ok, ready to do join now. // if it's a text node, just shuffle around some text nsCOMPtr keepNodeAsText( do_QueryInterface(aNodeToKeep) ); @@ -2608,7 +3321,7 @@ nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep, // editor wants us to set selection at join point selection->Collapse(aNodeToKeep, firstNodeLength); } - else + else if (selStartNode) { // and adjust the selection if needed // HACK: this is overly simplified - multi-range selections need more work than this @@ -2652,34 +3365,12 @@ nsresult nsEditor::GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset) { NS_ASSERTION((aChild && aParent), "bad args"); - nsresult result = NS_ERROR_NULL_POINTER; - if (aChild && aParent) - { - nsCOMPtr childNodes; - result = aParent->GetChildNodes(getter_AddRefs(childNodes)); - if ((NS_SUCCEEDED(result)) && (childNodes)) - { - PRInt32 i=0; - for ( ; NS_SUCCEEDED(result); i++) - { - nsCOMPtr childNode; - result = childNodes->Item(i, getter_AddRefs(childNode)); - if ((NS_SUCCEEDED(result)) && (childNode)) - { - if (childNode.get()==aChild) - { - aOffset = i; - break; - } - } - else if (!childNode) - result = NS_ERROR_NULL_POINTER; - } - } - else if (!childNodes) - result = NS_ERROR_NULL_POINTER; - } - return result; + if (!aChild || !aParent) return NS_ERROR_NULL_POINTER; + nsCOMPtr content = do_QueryInterface(aParent); + nsCOMPtr cChild = do_QueryInterface(aChild); + if (!cChild || !content) return NS_ERROR_NULL_POINTER; + nsresult res = content->IndexOf(cChild, aOffset); + return res; } nsresult @@ -3221,11 +3912,11 @@ nsEditor::GetNextNode(nsIDOMNode *aCurrentNode, nsresult result; *aResultNode = nsnull; // if aCurrentNode has a right sibling, return that sibling's leftmost child (or itself if it has no children) - nsCOMPtr prevSibling; - result = aCurrentNode->GetNextSibling(getter_AddRefs(prevSibling)); - if ((NS_SUCCEEDED(result)) && prevSibling) + nsCOMPtr nextSibling; + result = aCurrentNode->GetNextSibling(getter_AddRefs(nextSibling)); + if ((NS_SUCCEEDED(result)) && nextSibling) { - result = GetLeftmostChild(prevSibling, aResultNode); + result = GetLeftmostChild(nextSibling, aResultNode); if (NS_FAILED(result)) { return result; } if (PR_FALSE==aEditableNode) { return result; @@ -3236,6 +3927,7 @@ nsEditor::GetNextNode(nsIDOMNode *aCurrentNode, else { // restart the search from the non-editable node we just found nsCOMPtr notEditableNode = do_QueryInterface(*aResultNode); + NS_IF_RELEASE(*aResultNode); return GetNextNode(notEditableNode, aEditableNode, aResultNode); } } @@ -3351,10 +4043,16 @@ nsEditor::TagCanContain(const nsString &aParentTag, nsIDOMNode* aChild) { nsAutoString childStringTag; - nsCOMPtr childElement = do_QueryInterface(aChild); - if (!childElement) return PR_FALSE; - - childElement->GetTagName(childStringTag); + if (IsTextNode(aChild)) + { + childStringTag = "__moz_text"; + } + else + { + nsCOMPtr childElement = do_QueryInterface(aChild); + if (!childElement) return PR_FALSE; + childElement->GetTagName(childStringTag); + } return TagCanContainTag(aParentTag, childStringTag); } @@ -3780,6 +4478,12 @@ nsEditor::GetBlockNodeParent(nsIDOMNode *aNode) nsCOMPtr tmp; nsCOMPtr p; + if (!aNode) + { + NS_NOTREACHED("null node passed to GetBlockNodeParent()"); + return PR_FALSE; + } + if (NS_FAILED(aNode->GetParentNode(getter_AddRefs(p)))) // no parent, ran off top of tree return tmp; @@ -4441,7 +5145,7 @@ nsEditor::SetShouldTxnSetSelection(PRBool aShould) #ifdef XP_MAC #pragma mark - -#pragma mark --- protected nsEditor methods --- +#pragma mark protected nsEditor methods #pragma mark - #endif @@ -4455,13 +5159,10 @@ nsEditor::DeleteSelectionImpl(EDirection aAction) PRInt32 i; nsIEditActionListener *listener; nsCOMPtrselection; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; res = CreateTxnForDeleteSelection(aAction, &txn); if (NS_FAILED(res)) return res; - res = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(res)) { - NS_IF_RELEASE(txn); - return res; - } nsAutoRules beginRulesSniffing(this, kOpDeleteSelection, aAction); if (NS_SUCCEEDED(res)) @@ -5024,6 +5725,8 @@ nsEditor::CreateRange(nsIDOMNode *aStartParent, PRInt32 aStartOffset, nsresult nsEditor::GetFirstNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aNode) { + // Note: this might return a node that is outside of the range. + // Use caqrefully. if (!aRange || !aNode) return NS_ERROR_NULL_POINTER; *aNode = nsnull; diff --git a/mozilla/editor/base/nsEditor.h b/mozilla/editor/base/nsEditor.h index 0ba74ad0e24..6868b7e09f4 100644 --- a/mozilla/editor/base/nsEditor.h +++ b/mozilla/editor/base/nsEditor.h @@ -100,11 +100,41 @@ class nsSelectionState nsresult RestoreSelection(nsIDOMSelection *aSel); PRBool IsCollapsed(); PRBool IsEqual(nsSelectionState *aSelState); - + void MakeEmpty(); + PRBool IsEmpty(); + + // editor selection gravity routines. Note that we can't always depend on + // DOM Range gravity to do what we want to the "real" selection. For instance, + // if you move a node, that corresponds to deleting it and reinserting it. + // DOM Range gravity will promote the selection out of the node on deletion, + // which is not what you want if you know you are reinserting it. + nsresult SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition); + nsresult SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition); + nsresult SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset); + nsresult SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode); + nsresult SelAdjJoinNodes(nsIDOMNode *aLeftNode, + nsIDOMNode *aRightNode, + nsIDOMNode *aParent, + PRInt32 aOffset, + PRInt32 aOldLeftNodeLength); + nsresult SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString); + nsresult SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength); + // the following gravity routines need will/did sandwiches, because the other gravity + // routines will be called inside of these sandwiches, but should be ignored. + nsresult WillReplaceContainer(); + nsresult DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode); + nsresult WillRemoveContainer(); + nsresult DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen); + nsresult WillInsertContainer(); + nsresult DidInsertContainer(); + nsresult WillMoveNode(); + nsresult DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset); + nsVoidArray mArray; + PRBool mLock; }; -/** implementation of an editor object. it will be the controler/focal point +/** implementation of an editor object. it will be the controller/focal point * for the main editor services. i.e. the GUIManager, publishing, transaction * manager, event interfaces. the idea for the event interfaces is to have them * delegate the actual commands to the editor independent of the XPFE implementation. @@ -237,7 +267,7 @@ public: NS_IMETHOD InsertNoneditableTextNode(nsIDOMNode* aParent, PRInt32 aOffset, nsString& aStr); - NS_IMETHOD InsertFormattingForNode(nsIDOMNode* aNode); + NS_IMETHOD MarkNodeDirty(nsIDOMNode* aNode); /* output */ @@ -274,8 +304,28 @@ public: NS_IMETHOD InsertTextImpl(const nsString& aStringToInsert); + NS_IMETHOD JoeInsertTextImpl(const nsString& aStringToInsert, + nsCOMPtr *aInOutNode, + PRInt32 *aInOutOffset, + nsIDOMDocument *aDoc); + NS_IMETHOD JoeInsertTextIntoTextNodeImpl(const nsString& aStringToInsert, + nsIDOMCharacterData *aTextNode, + PRInt32 aOffset); NS_IMETHOD DeleteSelectionImpl(EDirection aAction); + /* helper routines for node/parent manipulations */ + nsresult ReplaceContainer(nsIDOMNode *inNode, + nsCOMPtr *outNode, + const nsString &aNodeType, + const nsString *aAttribute = nsnull, + const nsString *aValue = nsnull); + nsresult RemoveContainer(nsIDOMNode *inNode); + nsresult InsertContainerAbove(nsIDOMNode *inNode, + nsCOMPtr *outNode, + const nsString &aNodeType, + const nsString *aAttribute = nsnull, + const nsString *aValue = nsnull); + nsresult MoveNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset); protected: @@ -423,7 +473,7 @@ public: /** All editor operations which alter the doc should be followed * with a call to EndOperation, naming the action and direction */ - NS_IMETHOD EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection, PRBool aSetSelection); + NS_IMETHOD EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection); /** return the string that represents text nodes in the content tree */ static nsresult GetTextNodeTag(nsString& aOutString); @@ -702,7 +752,8 @@ protected: nsWeakPtr mPlaceHolderTxn; // weak reference to placeholder for begin/end batch purposes nsIAtom *mPlaceHolderName; // name of placeholder transaction PRInt32 mPlaceHolderBatch; // nesting count for batching - nsSelectionState *mSelState; // saved selection state for placeholder txn batching + nsSelectionState *mSelState; // saved selection state for placeholder txn batching + nsSelectionState *mSavedSel; // cached selection for nsAutoSelectionReset PRBool mShouldTxnSetSelection; // turn off for conservative selection adjustment by txns nsCOMPtr mBodyElement; // cached body node // @@ -718,7 +769,6 @@ protected: nsCOMPtr mDocStateListeners; PRInt8 mDocDirtyState; // -1 = not initialized - nsWeakPtr mDocWeak; // weak reference to the nsIDOMDocument nsCOMPtr mDTD; @@ -726,6 +776,7 @@ protected: friend PRBool NSCanUnload(nsISupports* serviceMgr); friend class nsAutoTxnsConserveSelection; + friend class nsAutoSelectionReset; }; diff --git a/mozilla/editor/base/nsEditorUtils.cpp b/mozilla/editor/base/nsEditorUtils.cpp index 39f051b50b8..982337fb017 100644 --- a/mozilla/editor/base/nsEditorUtils.cpp +++ b/mozilla/editor/base/nsEditorUtils.cpp @@ -29,39 +29,28 @@ * nsAutoSelectionReset *****************************************************************************/ -nsAutoSelectionReset::nsAutoSelectionReset(nsIDOMSelection *aSel) +nsAutoSelectionReset::nsAutoSelectionReset(nsIDOMSelection *aSel, nsEditor *aEd) : +mSel(nsnull) +,mEd(nsnull) { - mInitialized = PR_FALSE; + if (!aSel || !aEd) return; // not much we can do, bail. + if (aEd->mSavedSel) return; // we already have initted mSavedSel, so this must be nested call. mSel = do_QueryInterface(aSel); + mEd = aEd; if (mSel) { - mSel->GetAnchorNode(getter_AddRefs(mStartNode)); - mSel->GetAnchorOffset(&mStartOffset); - mSel->GetFocusNode(getter_AddRefs(mEndNode)); - mSel->GetFocusOffset(&mEndOffset); - if (mStartNode && mEndNode) - mInitialized = PR_TRUE; + mEd->mSavedSel = new nsSelectionState(); + mEd->mSavedSel->SaveSelection(mSel); } } nsAutoSelectionReset::~nsAutoSelectionReset() { - if (mSel && mInitialized) + if (mSel && mEd->mSavedSel) // mSel will be null if this was nested call { - // make sure that mStartNode and mEndNode are still in a document - nsCOMPtr docStart; - nsresult res = mStartNode->GetOwnerDocument(getter_AddRefs(docStart)); - if (NS_SUCCEEDED(res) && docStart) - { - nsCOMPtr docEnd; - res = mEndNode->GetOwnerDocument(getter_AddRefs(docEnd)); - if (NS_SUCCEEDED(res) && (docStart == docEnd)) - { - // restore original selection - mSel->Collapse(mStartNode, mStartOffset); - mSel->Extend(mEndNode, mEndOffset); - } - } + mEd->mSavedSel->RestoreSelection(mSel); + delete mEd->mSavedSel; + mEd->mSavedSel = nsnull; } } diff --git a/mozilla/editor/base/nsEditorUtils.h b/mozilla/editor/base/nsEditorUtils.h index d288c4ed447..1e1c41e4189 100644 --- a/mozilla/editor/base/nsEditorUtils.h +++ b/mozilla/editor/base/nsEditorUtils.h @@ -69,18 +69,11 @@ class nsAutoSelectionReset private: /** ref-counted reference to the selection that we are supposed to restore */ nsCOMPtr mSel; - - /** PR_TRUE if this instance initialized itself correctly */ - PRBool mInitialized; - - nsCOMPtr mStartNode; - nsCOMPtr mEndNode; - PRInt32 mStartOffset; - PRInt32 mEndOffset; + nsEditor *mEd; // non-owning ref to nsEditor public: /** constructor responsible for remembering all state needed to restore aSel */ - nsAutoSelectionReset(nsIDOMSelection *aSel); + nsAutoSelectionReset(nsIDOMSelection *aSel, nsEditor *aEd); /** destructor restores mSel to its former state */ ~nsAutoSelectionReset(); @@ -93,16 +86,15 @@ class nsAutoRules { public: - nsAutoRules(nsEditor *ed, PRInt32 action, nsIEditor::EDirection aDirection, PRBool setSelection=PR_FALSE) : - mEd(ed), mAction(action), mDirection(aDirection), mSetSelection(setSelection) + nsAutoRules(nsEditor *ed, PRInt32 action, nsIEditor::EDirection aDirection) : + mEd(ed), mAction(action), mDirection(aDirection) {if (mEd) mEd->StartOperation(mAction, mDirection);} - ~nsAutoRules() {if (mEd) mEd->EndOperation(mAction, mDirection, mSetSelection);} + ~nsAutoRules() {if (mEd) mEd->EndOperation(mAction, mDirection);} protected: nsEditor *mEd; PRInt32 mAction; nsIEditor::EDirection mDirection; - PRBool mSetSelection; }; diff --git a/mozilla/editor/base/nsHTMLEditRules.cpp b/mozilla/editor/base/nsHTMLEditRules.cpp index ce278cb157c..a7e506eac97 100644 --- a/mozilla/editor/base/nsHTMLEditRules.cpp +++ b/mozilla/editor/base/nsHTMLEditRules.cpp @@ -62,21 +62,46 @@ enum kBothSibs = 3 }; +nsresult +NS_NewHTMLEditRules(nsIEditRules** aInstancePtrResult) +{ + nsHTMLEditRules * rules = new nsHTMLEditRules(); + if (rules) + return rules->QueryInterface(NS_GET_IID(nsIEditRules), (void**) aInstancePtrResult); + return NS_ERROR_OUT_OF_MEMORY; +} + /******************************************************** * Constructor/Destructor ********************************************************/ -nsHTMLEditRules::nsHTMLEditRules() -: nsTextEditRules() +nsHTMLEditRules::nsHTMLEditRules() : +mDocChangeRange(nsnull) +,mListenerEnabled(PR_TRUE) +,mUtilRange(nsnull) +,mBody(nsnull) +,mJoinOffset(0) { } nsHTMLEditRules::~nsHTMLEditRules() { // remove ourselves as a listener to edit actions - mEditor->RemoveEditActionListener(mListener); + // In the normal case, we have already been removed by + // ~nsHTMLEditor, in which case we will get an error here + // which we ignore. But this allows us to add the ability to + // switch rule sets on the fly if we want. + mEditor->RemoveEditActionListener(this); } +/******************************************************** + * XPCOM Cruft + ********************************************************/ + +NS_IMPL_ADDREF_INHERITED(nsHTMLEditRules, nsTextEditRules) +NS_IMPL_RELEASE_INHERITED(nsHTMLEditRules, nsTextEditRules) +NS_IMPL_QUERY_INTERFACE2(nsHTMLEditRules, nsIEditRules, nsIEditActionListener) + /******************************************************** * Public methods @@ -89,9 +114,15 @@ nsHTMLEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags) nsresult res = nsTextEditRules::Init(aEditor, aFlags); if (NS_FAILED(res)) return res; + // make a utility range for use by the listenter + res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange), + getter_AddRefs(mUtilRange)); + if (NS_FAILED(res)) return res; + // pass over document and add any needed mozBRs // first turn off undo mEditor->EnableUndo(PR_FALSE); + // set up mDocChangeRange to be whole doc nsCOMPtr bodyElem; nsCOMPtr bodyNode; @@ -111,15 +142,12 @@ nsHTMLEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags) res = AdjustSpecialBreaks(); if (NS_FAILED(res)) return res; } + // turn on undo mEditor->EnableUndo(PR_TRUE); - // make a listener - res = NS_NewEditListener(getter_AddRefs(mListener), mEditor, this); - if (NS_FAILED(res)) return res; - - // add it as a listener to edit actions - res = mEditor->AddEditActionListener(mListener); + // add ourselves as a listener to edit actions + res = mEditor->AddEditActionListener(this); return res; } @@ -150,7 +178,7 @@ nsHTMLEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection) NS_IMETHODIMP -nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection, PRBool aSetSelection) +nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) { if (mLockRulesSniffing) return NS_OK; @@ -174,16 +202,8 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection, PRB nsAutoTxnsConserveSelection dontSpazMySelection(mEditor); // expand the "changed doc range" as needed - // MOOSE: I don't do this if the aSetSelection param is true. This is because - // if aSetSelection is true, it means we are to set the selection to the changed - // document region when we are done. So I need to preserve mDocChangeRange as is. - // I should probably set up a parellel range for this purpose. - - if (!aSetSelection) - { - res = PromoteRange(mDocChangeRange, action); - if (NS_FAILED(res)) return res; - } + res = PromoteRange(mDocChangeRange, action); + if (NS_FAILED(res)) return res; // add in any needed
s, and remove any unneeded ones. res = AdjustSpecialBreaks(); @@ -241,12 +261,6 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection, PRB nsCOMPtr pres; mEditor->GetPresShell(getter_AddRefs(pres)); if (pres) pres->SetCaretEnabled(PR_TRUE); - - if (aSetSelection) - { - selection->ClearSelection(); - selection->AddRange(mDocChangeRange); - } } return res; @@ -379,23 +393,14 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction, if (!mEditor->IsTextNode(selNode) && !mEditor->CanContainTag(selNode, textTag)) return NS_ERROR_FAILURE; - // split any mailcites in the way - if ( (mTheAction != nsHTMLEditor::kOpInsertQuotation) && (mFlags & nsIHTMLEditor::eEditorMailMask) ) - { - nsCOMPtr citeNode; - PRInt32 newOffset; - res = GetTopEnclosingMailCite(selNode, &citeNode); + // take care of typeinstate issues + if (typeInState.IsAnySet()) + { // for every property that is set, insert a new inline style node + res = CreateStyleForInsertText(aSelection, typeInState); + if (NS_FAILED(res)) return res; + // refresh the (collapsed) selection location + res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset); if (NS_FAILED(res)) return res; - - if (citeNode) - { - res = mEditor->SplitNodeDeep(citeNode, selNode, selOffset, &newOffset); - if (NS_FAILED(res)) return res; - res = citeNode->GetParentNode(getter_AddRefs(selNode)); - if (NS_FAILED(res)) return res; - res = aSelection->Collapse(selNode, newOffset); - if (NS_FAILED(res)) return res; - } } // identify the block @@ -420,34 +425,104 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction, } else // aAction == kInsertText { - while (theString.Length()) + // we need to get the doc + nsCOMPtrdoc; + res = mEditor->GetDocument(getter_AddRefs(doc)); + if (NS_FAILED(res)) return res; + if (!doc) return NS_ERROR_NULL_POINTER; + + // find where we are + nsCOMPtr curNode = selNode; + PRInt32 curOffset = selOffset; + + // is our text going to be PREformatted? + // We remember this so that we know how to handle tabs. + PRBool isPRE; + res = mEditor->IsPreformatted(selNode, &isPRE); + if (NS_FAILED(res)) return res; + + // turn off the edit listener: we know how to + // build the "doc changed range" ourselves, and it's + // must faster to do it once here than to track all + // the changes one at a time. + nsAutoLockListener lockit(&mListenerEnabled); + + // dont spaz my selection in subtransactions + nsAutoTxnsConserveSelection dontSpazMySelection(mEditor); + nsAutoString partialString; + nsCOMPtr unused; + PRInt32 pos; + + // for efficiency, break out the pre case seperately. This is because + // its a lot cheaper to search the input string for only newlines than + // it is to search for both tabs and newlines. + if (isPRE) { - nsString partialString; - PRInt32 pos = theString.FindCharInSet(specialChars); - // if first char is special, then use just it - if (pos == 0) pos = 1; - if (pos == -1) pos = theString.Length(); - theString.Left(partialString, pos); - theString.Cut(0, pos); - // is it a tab? - if (partialString == "\t" ) + char newlineChar = '\n'; + while (theString.Length()) { - res = InsertTab(aSelection,outString); + pos = theString.FindChar(newlineChar); + // if first char is newline, then use just it + if (pos == 0) pos = 1; + if (pos == -1) pos = theString.Length(); + theString.Left(partialString, pos); + theString.Cut(0, pos); + // is it a return? + if (partialString == "\n") + { + res = mEditor->JoeCreateBR(&curNode, &curOffset, &unused, nsIEditor::eNone); + } + else + { + res = mEditor->JoeInsertTextImpl(partialString, &curNode, &curOffset, doc); + } if (NS_FAILED(res)) return res; - res = DoTextInsertion(aSelection, &bCancel, outString, typeInState); } - // is it a return? - else if (partialString == "\n") - { - res = mEditor->InsertBreak(); - } - else - { - res = DoTextInsertion(aSelection, &bCancel, &partialString, typeInState); - } - if (NS_FAILED(res)) return res; - pos = theString.FindCharInSet(specialChars); } + else + { + char specialChars[] = {'\t','\n',0}; + nsAutoString tabString = " "; + while (theString.Length()) + { + pos = theString.FindCharInSet(specialChars); + // if first char is special, then use just it + if (pos == 0) pos = 1; + if (pos == -1) pos = theString.Length(); + theString.Left(partialString, pos); + theString.Cut(0, pos); + // is it a tab? + if (partialString == "\t") + { + partialString = " "; + res = mEditor->JoeInsertTextImpl(tabString, &curNode, &curOffset, doc); + } + // is it a return? + else if (partialString == "\n") + { + res = mEditor->JoeCreateBR(&curNode, &curOffset, &unused, nsIEditor::eNone); + } + else + { + res = mEditor->JoeInsertTextImpl(partialString, &curNode, &curOffset, doc); + } + if (NS_FAILED(res)) return res; + } + } + if (curNode) aSelection->Collapse(curNode, curOffset); + // manually update the doc changed range so that AfterEdit will clean up + // the correct portion of the document. + if (!mDocChangeRange) + { + res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange), + getter_AddRefs(mDocChangeRange)); + if (NS_FAILED(res)) return res; + if (!mDocChangeRange) return NS_ERROR_NULL_POINTER; + } + res = mDocChangeRange->SetStart(selNode, selOffset); + if (NS_FAILED(res)) return res; + res = mDocChangeRange->SetEnd(curNode, curOffset); + if (NS_FAILED(res)) return res; } return res; } @@ -993,9 +1068,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, nsAutoString blockType("ul"); if (aOrdered) blockType = "ol"; - - nsAutoSelectionReset selectionResetter(aSelection); - + PRBool outMakeEmpty; res = ShouldMakeEmptyBlock(aSelection, &blockType, &outMakeEmpty); if (NS_FAILED(res)) return res; @@ -1040,6 +1113,8 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, *aHandled = PR_TRUE; + nsAutoSelectionReset selectionResetter(aSelection, mEditor); + nsCOMPtr arrayOfRanges; res = GetPromotedRanges(aSelection, &arrayOfRanges, kMakeList); if (NS_FAILED(res)) return res; @@ -1226,9 +1301,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRUint32 listItemLen; res = mEditor->GetLengthOfDOMNode(prevListItem, listItemLen); if (NS_FAILED(res)) return res; - res = mEditor->DeleteNode(curNode); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(curNode, prevListItem, listItemLen); + res = mEditor->MoveNode(curNode, prevListItem, listItemLen); if (NS_FAILED(res)) return res; } else @@ -1258,9 +1331,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRUint32 listLen; res = mEditor->GetLengthOfDOMNode(curList, listLen); if (NS_FAILED(res)) return res; - res = mEditor->DeleteNode(listItem); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(listItem, curList, listLen); + res = mEditor->MoveNode(listItem, curList, listLen); if (NS_FAILED(res)) return res; } } @@ -1283,7 +1354,7 @@ nsHTMLEditRules::WillRemoveList(nsIDOMSelection *aSelection, nsAutoString blockType("ul"); if (aOrdered) blockType = "ol"; - nsAutoSelectionReset selectionResetter(aSelection); + nsAutoSelectionReset selectionResetter(aSelection, mEditor); nsCOMPtr arrayOfRanges; nsresult res = GetPromotedRanges(aSelection, &arrayOfRanges, kMakeList); @@ -1386,7 +1457,7 @@ nsHTMLEditRules::WillMakeBasicBlock(nsIDOMSelection *aSelection, if (makeEmpty) return res; // just insert a new empty block // else it's not that easy... - nsAutoSelectionReset selectionResetter(aSelection); + nsAutoSelectionReset selectionResetter(aSelection, mEditor); *aHandled = PR_TRUE; nsCOMPtr arrayOfRanges; @@ -1418,7 +1489,7 @@ nsHTMLEditRules::WillIndent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aCancel = PR_FALSE; *aHandled = PR_TRUE; - nsAutoSelectionReset selectionResetter(aSelection); + nsAutoSelectionReset selectionResetter(aSelection, mEditor); // convert the selection ranges into "promoted" selection ranges: // this basically just expands the range to include the immediate @@ -1475,9 +1546,7 @@ nsHTMLEditRules::WillIndent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool PRUint32 listLen; res = mEditor->GetLengthOfDOMNode(curList, listLen); if (NS_FAILED(res)) return res; - res = mEditor->DeleteNode(curNode); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(curNode, curList, listLen); + res = mEditor->MoveNode(curNode, curList, listLen); if (NS_FAILED(res)) return res; } @@ -1509,9 +1578,7 @@ nsHTMLEditRules::WillIndent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool PRUint32 quoteLen; res = mEditor->GetLengthOfDOMNode(curQuote, quoteLen); if (NS_FAILED(res)) return res; - res = mEditor->DeleteNode(curNode); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(curNode, curQuote, quoteLen); + res = mEditor->MoveNode(curNode, curQuote, quoteLen); if (NS_FAILED(res)) return res; } } @@ -1527,7 +1594,7 @@ nsHTMLEditRules::WillOutdent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBoo *aCancel = PR_FALSE; *aHandled = PR_TRUE; - nsAutoSelectionReset selectionResetter(aSelection); + nsAutoSelectionReset selectionResetter(aSelection, mEditor); nsresult res = NS_OK; // convert the selection ranges into "promoted" selection ranges: @@ -1796,7 +1863,7 @@ nsHTMLEditRules::WillAlign(nsIDOMSelection *aSelection, return res; } - nsAutoSelectionReset selectionResetter(aSelection); + nsAutoSelectionReset selectionResetter(aSelection, mEditor); // convert the selection ranges into "promoted" selection ranges: // this basically just expands the range to include the immediate @@ -1879,9 +1946,7 @@ nsHTMLEditRules::WillAlign(nsIDOMSelection *aSelection, PRUint32 listLen; res = mEditor->GetLengthOfDOMNode(curDiv, listLen); if (NS_FAILED(res)) return res; - res = mEditor->DeleteNode(curNode); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(curNode, curDiv, listLen); + res = mEditor->MoveNode(curNode, curDiv, listLen); if (NS_FAILED(res)) return res; } @@ -1989,9 +2054,7 @@ nsHTMLEditRules::AlignTableCellContents(nsIDOMNode *aNode, const nsString *align // tuck the children into the end of the active div while (lastChild && (lastChild != divNode)) { - res = mEditor->DeleteNode(lastChild); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(lastChild, divNode, 0); + res = mEditor->MoveNode(lastChild, divNode, 0); if (NS_FAILED(res)) return res; res = GetLastEditableChild(aNode, &lastChild); if (NS_FAILED(res)) return res; @@ -2250,7 +2313,7 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt } // else not kInsertText. In this case we want to see if we should - // grab any adjacent inlline nodes and/or parents and other ancestors + // grab any adjacent inline nodes and/or parents and other ancestors if (nsHTMLEditUtils::IsBody(aNode)) { // we cant go any higher @@ -2428,7 +2491,7 @@ nsHTMLEditRules::GetPromotedRanges(nsIDOMSelection *inSelection, /////////////////////////////////////////////////////////////////////////// -// PromoteRange: expand a range to include any parentsfor which all +// PromoteRange: expand a range to include any parents for which all // editable children are already in range. // nsresult @@ -2449,7 +2512,7 @@ nsHTMLEditRules::PromoteRange(nsIDOMRange *inRange, res = inRange->GetEndOffset(&endOffset); if (NS_FAILED(res)) return res; - // make a new adjusted range to represent the appropriate block content + // make a new adjusted range to represent the appropriate block content. // this is tricky. the basic idea is to push out the range endpoints // to truly enclose the blocks that we will affect @@ -2878,9 +2941,7 @@ nsHTMLEditRules::ReturnInListItem(nsIDOMSelection *aSelection, if (nsHTMLEditUtils::IsList(listparent)) //in a sublist { // if so, move this list item out of this list and into the grandparent list - res = mEditor->DeleteNode(aListItem); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(aListItem,listparent,offset+1); + res = mEditor->MoveNode(aListItem,listparent,offset+1); if (NS_FAILED(res)) return res; res = aSelection->Collapse(aListItem,0); } @@ -3185,9 +3246,7 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString PRUint32 blockLen; res = mEditor->GetLengthOfDOMNode(curBlock, blockLen); if (NS_FAILED(res)) return res; - res = mEditor->DeleteNode(curNode); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(curNode, curBlock, blockLen); + res = mEditor->MoveNode(curNode, curBlock, blockLen); if (NS_FAILED(res)) return res; } } @@ -3227,9 +3286,7 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft, // to after the 'left' one if (parent != rightParent) { - res = mEditor->DeleteNode(aNodeRight); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(aNodeRight, parent, parOffset); + res = mEditor->MoveNode(aNodeRight, parent, parOffset); if (NS_FAILED(res)) return res; } @@ -3380,7 +3437,7 @@ nsHTMLEditRules::AdjustWhitespace(nsIDOMSelection *aSelection) PRUint32 nodeCount,j; nsresult res; - nsAutoSelectionReset selectionResetter(aSelection); + nsAutoSelectionReset selectionResetter(aSelection, mEditor); // special case for mDocChangeRange entirely in one text node. // This is an efficiency hack for normal typing in the editor. @@ -3492,27 +3549,30 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirect return NS_OK; // we LIKE it when we are in a text node. that RULZ // do we need to insert a special mozBR? We do if we are: - // 1) in a collapsed selection AND - // 2) after a normal (non-moz) br AND - // 3) that br is the last editable node in it's block AND - // 4) that block is same block where selection is + // 1) that block is same block where selection is AND + // 2) in a collapsed selection AND + // 3) after a normal (non-moz) br AND + // 4) that br is the last editable node in it's block + nsCOMPtr nearNode; res = GetPriorHTMLNode(selNode, selOffset, &nearNode); if (NS_FAILED(res)) return res; - if (nearNode && nsHTMLEditUtils::IsBreak(nearNode) - && !nsHTMLEditUtils::IsMozBR(nearNode)) + if (!nearNode) return res; + + // is nearNode also a descendant of same block? + nsCOMPtr block, nearBlock; + if (mEditor->IsBlockNode(selNode)) block = selNode; + else block = mEditor->GetBlockNodeParent(selNode); + nearBlock = mEditor->GetBlockNodeParent(nearNode); + if (block == nearBlock) { - PRBool bIsLast; - res = IsLastEditableChild(nearNode, &bIsLast); - if (NS_FAILED(res)) return res; - if (bIsLast) + if (nearNode && nsHTMLEditUtils::IsBreak(nearNode) + && !nsHTMLEditUtils::IsMozBR(nearNode)) { - // is nearNode also a descendant of same block? - nsCOMPtr block, nearBlock; - if (mEditor->IsBlockNode(selNode)) block = selNode; - else block = mEditor->GetBlockNodeParent(selNode); - nearBlock = mEditor->GetBlockNodeParent(nearNode); - if (block == nearBlock) + PRBool bIsLast; + res = IsLastEditableChild(nearNode, &bIsLast); + if (NS_FAILED(res)) return res; + if (bIsLast) { // need to insert special moz BR. Why? Because if we don't // the user will see no new line for the break. Also, things @@ -3527,31 +3587,31 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirect res = aSelection->Collapse(selNode,selOffset); if (NS_FAILED(res)) return res; } - } - else - { - // ok, the br inst the last child. - // the br might be right in front of a new block (ie,: - // text
  1. list item
) - // in this case we also need moz-br. - nsCOMPtr nextNode; - res = GetNextHTMLNode(nearNode, &nextNode); - if (NS_FAILED(res)) return res; - res = GetNextHTMLSibling(nearNode, &nextNode); - if (NS_FAILED(res)) return res; - if (nextNode && mEditor->IsBlockNode(nextNode)) + else { - // need to insert special moz BR. Why? Because if we don't - // the user will see no new line for the break. - nsCOMPtr brNode; - res = CreateMozBR(selNode, selOffset, &brNode); + // ok, the br inst the last child. + // the br might be right in front of a new block (ie,: + // text
  1. list item
) + // in this case we also need moz-br. + nsCOMPtr nextNode; + res = GetNextHTMLNode(nearNode, &nextNode); if (NS_FAILED(res)) return res; - res = nsEditor::GetNodeLocation(brNode, &selNode, &selOffset); - if (NS_FAILED(res)) return res; - // selection stays *before* moz-br, sticking to it - aSelection->SetHint(PR_TRUE); - res = aSelection->Collapse(selNode,selOffset); + res = GetNextHTMLSibling(nearNode, &nextNode); if (NS_FAILED(res)) return res; + if (nextNode && mEditor->IsBlockNode(nextNode)) + { + // need to insert special moz BR. Why? Because if we don't + // the user will see no new line for the break. + nsCOMPtr brNode; + res = CreateMozBR(selNode, selOffset, &brNode); + if (NS_FAILED(res)) return res; + res = nsEditor::GetNodeLocation(brNode, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + // selection stays *before* moz-br, sticking to it + aSelection->SetHint(PR_TRUE); + res = aSelection->Collapse(selNode,selOffset); + if (NS_FAILED(res)) return res; + } } } } @@ -3841,6 +3901,7 @@ nsHTMLEditRules::DoTextNodeWhitespace(nsIDOMCharacterData *aTextNode, PRInt32 aS // runStart to runEnd is a run of whitespace nsAutoString runStr, newStr; tempString.Mid(runStr, runStart, runEnd-runStart); + res = ConvertWhitespace(runStr, newStr); if (NS_FAILED(res)) return res; @@ -3885,7 +3946,10 @@ nsHTMLEditRules::ConvertWhitespace(const nsString & inString, nsString & outStri outString = ""; return NS_OK; case 1: - outString = " "; + if (inString == "\n") // a bit of a hack: don't convert single newlines that + outString = "\n"; // dont have whitespace adjacent. This is to preserve + else // html source formatting to some degree. + outString = " "; return NS_OK; case 2: outString = (PRUnichar)nbsp; @@ -3964,9 +4028,7 @@ nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList) if (!bIsFirstListItem) parOffset++; - res = mEditor->DeleteNode(curNode); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(curNode, curParPar, parOffset); + res = mEditor->MoveNode(curNode, curParPar, parOffset); if (NS_FAILED(res)) return res; // unwrap list item contents if they are no longer in a list @@ -4108,318 +4170,9 @@ nsHTMLEditRules::UpdateDocChangeRange(nsIDOMRange *aRange) } -#ifdef XP_MAC -#pragma mark - -#pragma mark --- nsIEditActionListener methods --- -#pragma mark - -#endif - -/* Factory for edit listener object */ -nsresult NS_NewEditListener(nsIEditActionListener **aResult, - nsHTMLEditor *htmlEditor, - nsHTMLEditRules *htmlRules) -{ - if (!aResult) return NS_ERROR_NULL_POINTER; - - *aResult = new nsHTMLEditListener(htmlEditor, htmlRules); - if (!*aResult) return NS_ERROR_OUT_OF_MEMORY; - - NS_ADDREF(*aResult); - return NS_OK; -} - -NS_IMPL_ADDREF(nsHTMLEditListener) -NS_IMPL_RELEASE(nsHTMLEditListener) -NS_IMPL_QUERY_INTERFACE1(nsHTMLEditListener, nsIEditActionListener) - -nsHTMLEditListener::nsHTMLEditListener(nsHTMLEditor *htmlEditor, - nsHTMLEditRules *htmlRules) : - mEditor(htmlEditor), - mRules(htmlRules) -{ - NS_INIT_REFCNT(); - - NS_PRECONDITION(mEditor, "null editor!"); - NS_PRECONDITION(mRules, "null edit rules!"); - - nsCOMPtr bodyElement; - nsresult res = mEditor->GetBodyElement(getter_AddRefs(bodyElement)); - NS_POSTCONDITION(NS_SUCCEEDED(res), "no body element found for edit listener"); - NS_POSTCONDITION(bodyElement, "no body element found for edit listener"); - mBody = do_QueryInterface(bodyElement); -} - -nsHTMLEditListener::~nsHTMLEditListener() -{} - -NS_IMETHODIMP -nsHTMLEditListener::WillCreateNode(const nsString& aTag, nsIDOMNode *aParent, PRInt32 aPosition) -{ - return NS_OK; -} - -NS_IMETHODIMP -nsHTMLEditListener::DidCreateNode(const nsString& aTag, - nsIDOMNode *aNode, - nsIDOMNode *aParent, - PRInt32 aPosition, - nsresult aResult) -{ - nsCOMPtr range; - // assumption that Join keeps the righthand node - nsresult res = MakeRangeFromNode(aNode, &range); - if (NS_FAILED(res)) return res; - if (range) - { - res = mRules->UpdateDocChangeRange(range); - } - return res; -} - - -NS_IMETHODIMP -nsHTMLEditListener::WillInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition) -{ - return NS_OK; -} - - -NS_IMETHODIMP -nsHTMLEditListener::DidInsertNode(nsIDOMNode *aNode, - nsIDOMNode *aParent, - PRInt32 aPosition, - nsresult aResult) -{ - nsCOMPtr range; - // assumption that Join keeps the righthand node - nsresult res = MakeRangeFromNode(aNode, &range); - if (NS_FAILED(res)) return res; - if (range) - { - res = mRules->UpdateDocChangeRange(range); - } - return res; -} - - -NS_IMETHODIMP -nsHTMLEditListener::WillDeleteNode(nsIDOMNode *aChild) -{ - nsCOMPtr range; - nsresult res = MakeRangeFromNode(aChild, &range); - if (NS_FAILED(res)) return res; - if (range) - { - res = mRules->UpdateDocChangeRange(range); - } - return res; -} - - -NS_IMETHODIMP -nsHTMLEditListener::DidDeleteNode(nsIDOMNode *aChild, nsresult aResult) -{ - return NS_OK; -} - - -NS_IMETHODIMP -nsHTMLEditListener::WillSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset) -{ - return NS_OK; -} - - -NS_IMETHODIMP -nsHTMLEditListener::DidSplitNode(nsIDOMNode *aExistingRightNode, - PRInt32 aOffset, - nsIDOMNode *aNewLeftNode, - nsresult aResult) -{ - nsCOMPtr range; - nsresult res = MakeCollapsedRange(aExistingRightNode, 0, &range); - if (NS_FAILED(res)) return res; - res = mRules->UpdateDocChangeRange(range); - return res; -} - - -NS_IMETHODIMP -nsHTMLEditListener::WillJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent) -{ - // remember split point - nsresult res = nsEditor::GetLengthOfDOMNode(aLeftNode, mJoinOffset); - return res; -} - - -NS_IMETHODIMP -nsHTMLEditListener::DidJoinNodes(nsIDOMNode *aLeftNode, - nsIDOMNode *aRightNode, - nsIDOMNode *aParent, - nsresult aResult) -{ - // assumption that Join keeps the righthand node - nsCOMPtr range; - nsresult res = MakeCollapsedRange(aRightNode, mJoinOffset, &range); - if (NS_FAILED(res)) return res; - res = mRules->UpdateDocChangeRange(range); - return res; -} - - -NS_IMETHODIMP -nsHTMLEditListener::WillInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString) -{ - return NS_OK; -} - - -NS_IMETHODIMP -nsHTMLEditListener::DidInsertText(nsIDOMCharacterData *aTextNode, - PRInt32 aOffset, - const nsString &aString, - nsresult aResult) -{ - nsCOMPtr range; - PRInt32 length = aString.Length(); - nsresult res = MakeRangeFromTextOffsets(aTextNode, aOffset, aOffset+length, &range); - if (NS_FAILED(res)) return res; - if (range) - { - res = mRules->UpdateDocChangeRange(range); - } - return res; -} - - -NS_IMETHODIMP -nsHTMLEditListener::WillDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength) -{ - return NS_OK; -} - - -NS_IMETHODIMP -nsHTMLEditListener::DidDeleteText(nsIDOMCharacterData *aTextNode, - PRInt32 aOffset, - PRInt32 aLength, - nsresult aResult) -{ - nsCOMPtr range; - nsresult res = MakeRangeFromTextOffsets(aTextNode, aOffset, aOffset, &range); - if (NS_FAILED(res)) return res; - if (range) - { - res = mRules->UpdateDocChangeRange(range); - } - return res; -} - -NS_IMETHODIMP -nsHTMLEditListener::WillDeleteSelection(nsIDOMSelection *aSelection) -{ - nsCOMPtr range; - // get the (collapsed) selection location - nsCOMPtr selNode; - PRInt32 selOffset; - // construct a range to represent start and end of inNode - nsresult res = nsComponentManager::CreateInstance(kRangeCID, - nsnull, - NS_GET_IID(nsIDOMRange), - getter_AddRefs(range)); - if (NS_FAILED(res)) return res; - res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset); - if (NS_FAILED(res)) return res; - res = range->SetStart(selNode, selOffset); - if (NS_FAILED(res)) return res; - res = mEditor->GetEndNodeAndOffset(aSelection, &selNode, &selOffset); - if (NS_FAILED(res)) return res; - res = range->SetEnd(selNode, selOffset); - if (NS_FAILED(res)) return res; - if (range) - { - res = mRules->UpdateDocChangeRange(range); - } - return res; -} - -NS_IMETHODIMP -nsHTMLEditListener::DidDeleteSelection(nsIDOMSelection *aSelection) -{ - return NS_OK; -} - -nsresult -nsHTMLEditListener::MakeRangeFromNode(nsIDOMNode *inNode, nsCOMPtr *outRange) -{ - if (!inNode || !outRange) return NS_ERROR_NULL_POINTER; - *outRange = nsnull; - - // first check that inNode is still a descendant of the body - if (!IsDescendantOfBody(inNode)) return NS_OK; - - // construct a range to represent start and end of inNode - nsresult res = nsComponentManager::CreateInstance(kRangeCID, - nsnull, - NS_GET_IID(nsIDOMRange), - getter_AddRefs(*outRange)); - if (NS_FAILED(res)) return res; - res = (*outRange)->SelectNode(inNode); - return res; -} - - -nsresult -nsHTMLEditListener::MakeCollapsedRange(nsIDOMNode *inNode, PRInt32 inOffset, nsCOMPtr *outRange) -{ - if (!inNode || !outRange) return NS_ERROR_NULL_POINTER; - *outRange = nsnull; - - // first check that inNode is still a descendant of the body - if (!IsDescendantOfBody(inNode)) return NS_OK; - - // construct a range to represent start and end of inNode - nsresult res = nsComponentManager::CreateInstance(kRangeCID, - nsnull, - NS_GET_IID(nsIDOMRange), - getter_AddRefs(*outRange)); - if (NS_FAILED(res)) return res; - res = (*outRange)->SetStart(inNode, inOffset); - if (NS_FAILED(res)) return res; - res = (*outRange)->SetEnd(inNode, inOffset); - return res; -} - - -nsresult -nsHTMLEditListener::MakeRangeFromTextOffsets(nsIDOMCharacterData *inNode, - PRInt32 inStart, - PRInt32 inEnd, - nsCOMPtr *outRange) -{ - if (!inNode || !outRange) return NS_ERROR_NULL_POINTER; - *outRange = nsnull; - nsCOMPtr theNode = do_QueryInterface(inNode); - - // first check that inNode is still a descendant of the body - if (!IsDescendantOfBody(theNode)) return NS_OK; - - // construct a range to represent start and end of text run - nsresult res = nsComponentManager::CreateInstance(kRangeCID, - nsnull, - NS_GET_IID(nsIDOMRange), - getter_AddRefs(*outRange)); - if (NS_FAILED(res)) return res; - res = (*outRange)->SetStart(theNode, inStart); - if (NS_FAILED(res)) return res; - res = (*outRange)->SetEnd(theNode, inEnd); - return res; -} - PRBool -nsHTMLEditListener::IsDescendantOfBody(nsIDOMNode *inNode) +nsHTMLEditRules::IsDescendantOfBody(nsIDOMNode *inNode) { if (!inNode) return PR_FALSE; if (inNode == mBody.get()) return PR_TRUE; @@ -4440,6 +4193,198 @@ nsHTMLEditListener::IsDescendantOfBody(nsIDOMNode *inNode) +#ifdef XP_MAC +#pragma mark - +#pragma mark nsIEditActionListener methods +#pragma mark - +#endif + +NS_IMETHODIMP +nsHTMLEditRules::WillCreateNode(const nsString& aTag, nsIDOMNode *aParent, PRInt32 aPosition) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsHTMLEditRules::DidCreateNode(const nsString& aTag, + nsIDOMNode *aNode, + nsIDOMNode *aParent, + PRInt32 aPosition, + nsresult aResult) +{ + if (!mListenerEnabled) return NS_OK; + // assumption that Join keeps the righthand node + nsresult res = mUtilRange->SelectNode(aNode); + if (NS_FAILED(res)) return res; + res = UpdateDocChangeRange(mUtilRange); + return res; +} + + +NS_IMETHODIMP +nsHTMLEditRules::WillInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsHTMLEditRules::DidInsertNode(nsIDOMNode *aNode, + nsIDOMNode *aParent, + PRInt32 aPosition, + nsresult aResult) +{ + if (!mListenerEnabled) return NS_OK; + nsresult res = mUtilRange->SelectNode(aNode); + if (NS_FAILED(res)) return res; + res = UpdateDocChangeRange(mUtilRange); + return res; +} + + +NS_IMETHODIMP +nsHTMLEditRules::WillDeleteNode(nsIDOMNode *aChild) +{ + if (!mListenerEnabled) return NS_OK; + nsresult res = mUtilRange->SelectNode(aChild); + if (NS_FAILED(res)) return res; + res = UpdateDocChangeRange(mUtilRange); + return res; +} + + +NS_IMETHODIMP +nsHTMLEditRules::DidDeleteNode(nsIDOMNode *aChild, nsresult aResult) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsHTMLEditRules::WillSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsHTMLEditRules::DidSplitNode(nsIDOMNode *aExistingRightNode, + PRInt32 aOffset, + nsIDOMNode *aNewLeftNode, + nsresult aResult) +{ + if (!mListenerEnabled) return NS_OK; + nsresult res = mUtilRange->SetStart(aExistingRightNode, 0); + if (NS_FAILED(res)) return res; + res = mUtilRange->SetEnd(aExistingRightNode, 0); + if (NS_FAILED(res)) return res; + res = UpdateDocChangeRange(mUtilRange); + return res; +} + + +NS_IMETHODIMP +nsHTMLEditRules::WillJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent) +{ + if (!mListenerEnabled) return NS_OK; + // remember split point + nsresult res = nsEditor::GetLengthOfDOMNode(aLeftNode, mJoinOffset); + return res; +} + + +NS_IMETHODIMP +nsHTMLEditRules::DidJoinNodes(nsIDOMNode *aLeftNode, + nsIDOMNode *aRightNode, + nsIDOMNode *aParent, + nsresult aResult) +{ + if (!mListenerEnabled) return NS_OK; + // assumption that Join keeps the righthand node + nsresult res = mUtilRange->SetStart(aRightNode, mJoinOffset); + if (NS_FAILED(res)) return res; + res = mUtilRange->SetEnd(aRightNode, mJoinOffset); + if (NS_FAILED(res)) return res; + res = UpdateDocChangeRange(mUtilRange); + return res; +} + + +NS_IMETHODIMP +nsHTMLEditRules::WillInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsHTMLEditRules::DidInsertText(nsIDOMCharacterData *aTextNode, + PRInt32 aOffset, + const nsString &aString, + nsresult aResult) +{ + if (!mListenerEnabled) return NS_OK; + PRInt32 length = aString.Length(); + nsCOMPtr theNode = do_QueryInterface(aTextNode); + nsresult res = mUtilRange->SetStart(theNode, aOffset); + if (NS_FAILED(res)) return res; + res = mUtilRange->SetEnd(theNode, aOffset+length); + if (NS_FAILED(res)) return res; + res = UpdateDocChangeRange(mUtilRange); + return res; +} + + +NS_IMETHODIMP +nsHTMLEditRules::WillDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsHTMLEditRules::DidDeleteText(nsIDOMCharacterData *aTextNode, + PRInt32 aOffset, + PRInt32 aLength, + nsresult aResult) +{ + if (!mListenerEnabled) return NS_OK; + nsCOMPtr theNode = do_QueryInterface(aTextNode); + nsresult res = mUtilRange->SetStart(theNode, aOffset); + if (NS_FAILED(res)) return res; + res = mUtilRange->SetEnd(theNode, aOffset); + if (NS_FAILED(res)) return res; + res = UpdateDocChangeRange(mUtilRange); + return res; +} + +NS_IMETHODIMP +nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection) +{ + if (!mListenerEnabled) return NS_OK; + // get the (collapsed) selection location + nsCOMPtr selNode; + PRInt32 selOffset; + + nsresult res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + res = mUtilRange->SetStart(selNode, selOffset); + if (NS_FAILED(res)) return res; + res = mEditor->GetEndNodeAndOffset(aSelection, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + res = mUtilRange->SetEnd(selNode, selOffset); + if (NS_FAILED(res)) return res; + res = UpdateDocChangeRange(mUtilRange); + return res; +} + +NS_IMETHODIMP +nsHTMLEditRules::DidDeleteSelection(nsIDOMSelection *aSelection) +{ + return NS_OK; +} + + diff --git a/mozilla/editor/base/nsHTMLEditRules.h b/mozilla/editor/base/nsHTMLEditRules.h index 9825d4f7d66..51e9d7dabe2 100644 --- a/mozilla/editor/base/nsHTMLEditRules.h +++ b/mozilla/editor/base/nsHTMLEditRules.h @@ -33,20 +33,42 @@ class nsISupportsArray; class nsVoidArray; class nsIDOMElement; -class nsHTMLEditRules : public nsTextEditRules +class nsHTMLEditRules : public nsTextEditRules, nsIEditActionListener { public: + NS_DECL_ISUPPORTS_INHERITED + nsHTMLEditRules(); virtual ~nsHTMLEditRules(); - // nsEditRules methods - NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection); - NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection, PRBool aSetSelection); + + // nsIEditRules methods NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags); + NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection); + NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection); NS_IMETHOD WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled); NS_IMETHOD DidDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, nsresult aResult); + // nsIEditActionListener methods + + NS_IMETHOD WillCreateNode(const nsString& aTag, nsIDOMNode *aParent, PRInt32 aPosition); + NS_IMETHOD DidCreateNode(const nsString& aTag, nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition, nsresult aResult); + NS_IMETHOD WillInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition); + NS_IMETHOD DidInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition, nsresult aResult); + NS_IMETHOD WillDeleteNode(nsIDOMNode *aChild); + NS_IMETHOD DidDeleteNode(nsIDOMNode *aChild, nsresult aResult); + NS_IMETHOD WillSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset); + NS_IMETHOD DidSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode, nsresult aResult); + NS_IMETHOD WillJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent); + NS_IMETHOD DidJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent, nsresult aResult); + NS_IMETHOD WillInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString); + NS_IMETHOD DidInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString, nsresult aResult); + NS_IMETHOD WillDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength); + NS_IMETHOD DidDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength, nsresult aResult); + NS_IMETHOD WillDeleteSelection(nsIDOMSelection *aSelection); + NS_IMETHOD DidDeleteSelection(nsIDOMSelection *aSelection); + protected: enum RulesEndpoint @@ -138,67 +160,19 @@ protected: nsresult ConvertWhitespace(const nsString & inString, nsString & outString); nsresult ConfirmSelectionInBody(); -// removed from use: -#if 0 - nsresult AddTrailerBR(nsIDOMNode *aNode); - nsresult CreateMozDiv(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outDiv); -#endif - -// data members -protected: - nsCOMPtr mListener; - nsCOMPtr mDocChangeRange; - -// friends - friend class nsHTMLEditListener; - friend nsresult NS_NewEditListener(nsIEditActionListener **aResult, - nsHTMLEditor *htmlEditor, - nsHTMLEditRules *htmlRules); - -}; - -class nsHTMLEditListener : public nsIEditActionListener -{ -public: - nsHTMLEditListener(nsHTMLEditor *htmlEditor, nsHTMLEditRules *htmlRules); - virtual ~nsHTMLEditListener(); - NS_DECL_ISUPPORTS - - // nsIEditActionListener methods - - NS_IMETHOD WillCreateNode(const nsString& aTag, nsIDOMNode *aParent, PRInt32 aPosition); - NS_IMETHOD DidCreateNode(const nsString& aTag, nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition, nsresult aResult); - NS_IMETHOD WillInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition); - NS_IMETHOD DidInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition, nsresult aResult); - NS_IMETHOD WillDeleteNode(nsIDOMNode *aChild); - NS_IMETHOD DidDeleteNode(nsIDOMNode *aChild, nsresult aResult); - NS_IMETHOD WillSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset); - NS_IMETHOD DidSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode, nsresult aResult); - NS_IMETHOD WillJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent); - NS_IMETHOD DidJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent, nsresult aResult); - NS_IMETHOD WillInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString); - NS_IMETHOD DidInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString, nsresult aResult); - NS_IMETHOD WillDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength); - NS_IMETHOD DidDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength, nsresult aResult); - NS_IMETHOD WillDeleteSelection(nsIDOMSelection *aSelection); - NS_IMETHOD DidDeleteSelection(nsIDOMSelection *aSelection); - -protected: - - nsresult MakeRangeFromNode(nsIDOMNode *inNode, nsCOMPtr *outRange); - nsresult MakeRangeFromTextOffsets(nsIDOMCharacterData *inNode, - PRInt32 inStart, - PRInt32 inEnd, - nsCOMPtr *outRange); - nsresult MakeCollapsedRange(nsIDOMNode *inNode, PRInt32 inOffset, nsCOMPtr *outRange); PRBool IsDescendantOfBody(nsIDOMNode *inNode) ; - + // data members - nsHTMLEditor *mEditor; - nsHTMLEditRules *mRules; - nsCOMPtr mBody; - PRUint32 mJoinOffset; // need to remember an int across willJoin/didJoin... +protected: + nsCOMPtr mDocChangeRange; + PRBool mListenerEnabled; + nsCOMPtr mUtilRange; + nsCOMPtr mBody; + PRUint32 mJoinOffset; // need to remember an int across willJoin/didJoin... + }; +nsresult NS_NewHTMLEditRules(nsIEditRules** aInstancePtrResult); + #endif //nsHTMLEditRules_h__ diff --git a/mozilla/editor/base/nsHTMLEditor.cpp b/mozilla/editor/base/nsHTMLEditor.cpp index 359a053b5fb..2f16ab35a8d 100644 --- a/mozilla/editor/base/nsHTMLEditor.cpp +++ b/mozilla/editor/base/nsHTMLEditor.cpp @@ -32,6 +32,7 @@ #include "nsIDOMText.h" #include "nsIDOMNodeList.h" #include "nsIDOMDocument.h" +#include "nsIDOMAttr.h" #include "nsIDocument.h" #include "nsIDOMEventReceiver.h" #include "nsIDOMKeyEvent.h" @@ -125,6 +126,9 @@ nsIAtom *nsHTMLEditor::gTypingTxnName; nsIAtom *nsHTMLEditor::gIMETxnName; nsIAtom *nsHTMLEditor::gDeleteTxnName; +// some prototypes for rules creation shortcuts +nsresult NS_NewTextEditRules(nsIEditRules** aInstancePtrResult); +nsresult NS_NewHTMLEditRules(nsIEditRules** aInstancePtrResult); #define IsLink(s) (s.EqualsIgnoreCase(hrefText)) #define IsNamedAnchor(s) (s.EqualsIgnoreCase(anchorTxt) || s.EqualsIgnoreCase(namedanchorText)) @@ -276,7 +280,12 @@ nsHTMLEditor::~nsHTMLEditor() gDeleteTxnName = nsnull; } } - + + // remove the rules as an action listener. Else we get a bad ownership loop later on. + // it's ok if the rules aren't a listener; we ignore the error. + nsCOMPtr mListener = do_QueryInterface(mRules); + RemoveEditActionListener(mListener); + //the autopointers will clear themselves up. //but we need to also remove the listeners or we have a leak nsCOMPtrselection; @@ -325,9 +334,6 @@ nsHTMLEditor::~nsHTMLEditor() } } - // deleting a null pointer is safe - delete mRules; - NS_IF_RELEASE(mTypeInState); } @@ -412,10 +418,11 @@ NS_IMETHODIMP nsHTMLEditor::Init(nsIDOMDocument *aDoc, result = nsComponentManager::CreateInstance(kCNavDTDCID, nsnull, NS_GET_IID(nsIDTD), getter_AddRefs(mDTD)); if (!mDTD) result = NS_ERROR_FAILURE; - + if (NS_FAILED(result)) return result; + // Init the rules system - // XXX: ERROR CHECKING should InitRules return an error, and then we could check it here? - InitRules(); + result = InitRules(); + if (NS_FAILED(result)) return result; EnableUndo(PR_TRUE); @@ -636,16 +643,21 @@ nsHTMLEditor::SetFlags(PRUint32 aFlags) } -void nsHTMLEditor::InitRules() +NS_IMETHODIMP nsHTMLEditor::InitRules() { // instantiate the rules for this text editor // XXX: we should be told which set of rules to instantiate + nsresult res = NS_ERROR_FAILURE; if (mFlags & eEditorPlaintextMask) - mRules = new nsTextEditRules(); + res = NS_NewTextEditRules(getter_AddRefs(mRules)); else - mRules = new nsHTMLEditRules(); + res = NS_NewHTMLEditRules(getter_AddRefs(mRules)); - mRules->Init(this, mFlags); + if (NS_FAILED(res)) return res; + if (!mRules) return NS_ERROR_UNEXPECTED; + res = mRules->Init(this, mFlags); + + return res; } @@ -661,7 +673,7 @@ PRBool nsHTMLEditor::IsModifiable() #ifdef XP_MAC #pragma mark - -#pragma mark --- nsIHTMLEditor methods --- +#pragma mark nsIHTMLEditor methods #pragma mark - #endif @@ -812,14 +824,16 @@ NS_IMETHODIMP nsHTMLEditor::TabInTable(PRBool inIsShift, PRBool *outHandled) return NS_OK; } -NS_IMETHODIMP nsHTMLEditor::CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPtr *outBRNode, EDirection aSelect) +NS_IMETHODIMP nsHTMLEditor::JoeCreateBR(nsCOMPtr *aInOutParent, PRInt32 *aInOutOffset, nsCOMPtr *outBRNode, EDirection aSelect) { - if (!aNode || !outBRNode) return NS_ERROR_NULL_POINTER; + if (!aInOutParent || !*aInOutParent || !aInOutOffset || !outBRNode) return NS_ERROR_NULL_POINTER; *outBRNode = nsnull; nsresult res; // we need to insert a br. unfortunately, we may have to split a text node to do it. - nsCOMPtr nodeAsText = do_QueryInterface(aNode); + nsCOMPtr node = *aInOutParent; + PRInt32 theOffset = *aInOutOffset; + nsCOMPtr nodeAsText = do_QueryInterface(node); nsAutoString brType("br"); nsCOMPtr brNode; if (nodeAsText) @@ -828,13 +842,13 @@ NS_IMETHODIMP nsHTMLEditor::CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPt PRInt32 offset; PRUint32 len; nodeAsText->GetLength(&len); - GetNodeLocation(aNode, &tmp, &offset); + GetNodeLocation(node, &tmp, &offset); if (!tmp) return NS_ERROR_FAILURE; - if (!aOffset) + if (!theOffset) { // we are already set to go } - else if (aOffset == (PRInt32)len) + else if (theOffset == (PRInt32)len) { // update offset to point AFTER the text node offset++; @@ -842,19 +856,22 @@ NS_IMETHODIMP nsHTMLEditor::CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPt else { // split the text node - res = SplitNode(aNode, aOffset, getter_AddRefs(tmp)); + res = SplitNode(node, theOffset, getter_AddRefs(tmp)); if (NS_FAILED(res)) return res; - res = GetNodeLocation(aNode, &tmp, &offset); + res = GetNodeLocation(node, &tmp, &offset); if (NS_FAILED(res)) return res; } // create br res = CreateNode(brType, tmp, offset, getter_AddRefs(brNode)); if (NS_FAILED(res)) return res; + *aInOutParent = tmp; + *aInOutOffset = offset+1; } else { - res = CreateNode(brType, aNode, aOffset, getter_AddRefs(brNode)); + res = CreateNode(brType, node, theOffset, getter_AddRefs(brNode)); if (NS_FAILED(res)) return res; + (*aInOutOffset)++; } *outBRNode = brNode; @@ -883,6 +900,14 @@ NS_IMETHODIMP nsHTMLEditor::CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPt return NS_OK; } + +NS_IMETHODIMP nsHTMLEditor::CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPtr *outBRNode, EDirection aSelect) +{ + nsCOMPtr parent = aNode; + PRInt32 offset = aOffset; + return JoeCreateBR(&parent, &offset, outBRNode, aSelect); +} + NS_IMETHODIMP nsHTMLEditor::InsertBR(nsCOMPtr *outBRNode) { PRBool bCollapsed; @@ -922,137 +947,662 @@ NS_IMETHODIMP nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty, if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } ForceCompositionEnd(); + nsresult res; + nsCOMPtrselection; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_NULL_POINTER; + + PRBool isCollapsed; + selection->GetIsCollapsed(&isCollapsed); + if (isCollapsed) + { + // manipulating text attributes on a collapsed selection only sets state for the next text insertion + return SetTypeInStateForProperty(*mTypeInState, aProperty, aAttribute, aValue); + } + nsAutoEditBatch batchIt(this); nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext); + nsAutoSelectionReset selectionResetter(selection, this); - if (gNoisy) - { - nsAutoString propString; - aProperty->ToString(propString); - char *propCString = propString.ToNewCString(); - if (gNoisy) { printf("---------- start nsTextEditor::SetTextProperty %s ----------\n", propCString); } - nsCRT::free(propCString); - } - - nsresult result=NS_ERROR_NOT_INITIALIZED; - nsCOMPtrselection; - result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; PRBool cancel, handled; nsTextRulesInfo ruleInfo(nsTextEditRules::kSetTextProperty); - result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); - if (NS_FAILED(result)) return result; + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (NS_FAILED(res)) return res; if (!cancel && !handled) { - PRBool isCollapsed; - selection->GetIsCollapsed(&isCollapsed); - if (PR_TRUE==isCollapsed) - { - // manipulating text attributes on a collapsed selection only sets state for the next text insertion - SetTypeInStateForProperty(*mTypeInState, aProperty, aAttribute, aValue); - } - else - { - // set the text property for all selected ranges - nsCOMPtr enumerator; - result = selection->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_FAILED(result)) return result; - if (!enumerator) return NS_ERROR_NULL_POINTER; - - enumerator->First(); - nsCOMPtr currentItem; - result = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if (NS_FAILED(result)) return result; - if (!currentItem) return NS_ERROR_NULL_POINTER; + // get selection range enumerator + nsCOMPtr enumerator; + res = selection->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_FAILED(res)) return res; + if (!enumerator) return NS_ERROR_FAILURE; + // loop thru the ranges in the selection + enumerator->First(); + nsCOMPtr currentItem; + while ((NS_ENUMERATOR_FALSE == enumerator->IsDone())) + { + res = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if (NS_FAILED(res)) return res; + if (!currentItem) return NS_ERROR_FAILURE; + nsCOMPtr range( do_QueryInterface(currentItem) ); - nsCOMPtrcommonParent; - result = range->GetCommonParent(getter_AddRefs(commonParent)); - if (NS_FAILED(result)) return result; - if (!commonParent) return NS_ERROR_NULL_POINTER; - PRInt32 startOffset, endOffset; - range->GetStartOffset(&startOffset); - range->GetEndOffset(&endOffset); - nsCOMPtr startParent; nsCOMPtr endParent; - range->GetStartParent(getter_AddRefs(startParent)); - range->GetEndParent(getter_AddRefs(endParent)); - PRBool startIsText = IsTextNode(startParent); - PRBool endIsText = IsTextNode(endParent); - if ((PR_TRUE==startIsText) && (PR_TRUE==endIsText) && - (startParent.get()==endParent.get())) - { // the range is entirely contained within a single text node - // commonParent==aStartParent, so get the "real" parent of the selection - startParent->GetParentNode(getter_AddRefs(commonParent)); - result = SetTextPropertiesForNode(startParent, commonParent, - startOffset, endOffset, - aProperty, aAttribute, aValue); + // adjust range to include any ancestors who's children are entirely selected + res = PromoteInlineRange(range); + if (NS_FAILED(res)) return res; + + // check for easy case: both range endpoints in same text node + nsCOMPtr startNode, endNode; + res = range->GetStartParent(getter_AddRefs(startNode)); + if (NS_FAILED(res)) return res; + res = range->GetEndParent(getter_AddRefs(endNode)); + if (NS_FAILED(res)) return res; + if ((startNode == endNode) && IsTextNode(startNode)) + { + // MOOSE: workaround for selection bug: + selection->ClearSelection(); + + PRInt32 startOffset, endOffset; + range->GetStartOffset(&startOffset); + range->GetEndOffset(&endOffset); + nsCOMPtr nodeAsText = do_QueryInterface(startNode); + res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, endOffset, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; } else { - nsCOMPtr startGrandParent; - result = startParent->GetParentNode(getter_AddRefs(startGrandParent)); - if (NS_FAILED(result)) return result; - if (!startGrandParent) return NS_ERROR_NULL_POINTER; - nsCOMPtr endGrandParent; - result = endParent->GetParentNode(getter_AddRefs(endGrandParent)); - if (NS_FAILED(result)) return result; - if (!endGrandParent) return NS_ERROR_NULL_POINTER; + // not the easy case. range not contained in single text node. + // there are up to three phases here. There are all the nodes + // reported by the subtree iterator to be processed. And there + // are potentially a starting textnode and an ending textnode + // which are only partially contained by the range. + + // lets handle the nodes reported by the iterator. These nodes + // are entirely contained in the selection range. We build up + // a list of them (since doing operations on the document during + // iteration would perturb the iterator). - PRBool canCollapseStyleNode = PR_FALSE; - if ((PR_TRUE==startIsText) && (PR_TRUE==endIsText) && - endGrandParent.get()==startGrandParent.get()) + nsCOMPtr iter; + res = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull, + NS_GET_IID(nsIContentIterator), + getter_AddRefs(iter)); + if (NS_FAILED(res)) return res; + if (!iter) return NS_ERROR_FAILURE; + + nsCOMPtr arrayOfNodes; + nsCOMPtr content; + nsCOMPtr node; + nsCOMPtr isupports; + + // make a array + res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); + if (NS_FAILED(res)) return res; + + // iterate range and build up array + iter->Init(range); + while (NS_ENUMERATOR_FALSE == iter->IsDone()) { - result = IntermediateNodesAreInline(range, startParent, startOffset, - endParent, endOffset, - canCollapseStyleNode); + res = iter->CurrentNode(getter_AddRefs(content)); + if (NS_FAILED(res)) return res; + node = do_QueryInterface(content); + if (!node) return NS_ERROR_FAILURE; + if (IsEditable(node)) + { + isupports = do_QueryInterface(node); + arrayOfNodes->AppendElement(isupports); + } + res = iter->Next(); + if (NS_FAILED(res)) return res; } - if (NS_SUCCEEDED(result)) + + // MOOSE: workaround for selection bug: + selection->ClearSelection(); + + // first check the start parent of the range to see if it needs to + // be seperately handled (it does if it's a text node, due to how the + // subtree iterator works - it will not have reported it). + if (IsTextNode(startNode) && IsEditable(startNode)) { - if (PR_TRUE==canCollapseStyleNode) - { // the range is between 2 nodes that have a common (immediate) grandparent, - // and any intermediate nodes are just inline style nodes - result = SetTextPropertiesForNodesWithSameParent(startParent,startOffset, - endParent, endOffset, - commonParent, - aProperty, aAttribute, aValue); - } - else - { // the range is between 2 nodes that have no simple relationship - result = SetTextPropertiesForNodeWithDifferentParents(range, - startParent,startOffset, - endParent, endOffset, - commonParent, - aProperty, aAttribute, aValue); - } + nsCOMPtr nodeAsText = do_QueryInterface(startNode); + PRInt32 startOffset; + PRUint32 textLen; + range->GetStartOffset(&startOffset); + nodeAsText->GetLength(&textLen); + res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, textLen, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; + } + + // then loop through the list, set the property on each node + PRUint32 listCount; + PRUint32 j; + arrayOfNodes->Count(&listCount); + for (j = 0; j < listCount; j++) + { + isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); + node = do_QueryInterface(isupports); + res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; + arrayOfNodes->RemoveElementAt(0); + } + + // last check the end parent of the range to see if it needs to + // be seperately handled (it does if it's a text node, due to how the + // subtree iterator works - it will not have reported it). + if (IsTextNode(endNode) && IsEditable(endNode)) + { + nsCOMPtr nodeAsText = do_QueryInterface(endNode); + PRInt32 endOffset; + range->GetEndOffset(&endOffset); + res = SetInlinePropertyOnTextNode(nodeAsText, 0, endOffset, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; } } - if (NS_SUCCEEDED(result)) - { // compute a range for the selection - // don't want to actually do anything with selection, because - // we are still iterating through it. Just want to create and remember - // an nsIDOMRange, and later add the range to the selection after clearing it. - // XXX: I'm blocked here because nsIDOMSelection doesn't provide a mechanism - // for setting a compound selection yet. - } + enumerator->Next(); } } if (!cancel) { // post-process - result = mRules->DidDoAction(selection, &ruleInfo, result); + res = mRules->DidDoAction(selection, &ruleInfo, res); } - if (gNoisy) {DebugDumpContent(); } // DEBUG - if (gNoisy) - { - nsAutoString propString; - aProperty->ToString(propString); - char *propCString = propString.ToNewCString(); - if (gNoisy) { printf("---------- end nsTextEditor::SetTextProperty %s ----------\n", propCString); } - nsCRT::free(propCString); + return res; +} + + + +nsresult +nsHTMLEditor::SetInlinePropertyOnTextNode( nsIDOMCharacterData *aTextNode, + PRInt32 aStartOffset, + PRInt32 aEndOffset, + nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue) +{ + if (!aTextNode) return NS_ERROR_NULL_POINTER; + + // dont need to do anything if no characters actually selected + if (aStartOffset == aEndOffset) return NS_OK; + + nsresult res = NS_OK; + nsCOMPtr tmp, node = do_QueryInterface(aTextNode); + + // dont need to do anything if property already set on node + PRBool bHasProp; + nsCOMPtr styleNode; + IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, bHasProp, getter_AddRefs(styleNode)); + if (bHasProp) return NS_OK; + + // do we need to split the text node? + PRUint32 textLen; + aTextNode->GetLength(&textLen); + + if ( (PRUint32)aEndOffset != textLen ) + { + // we need to split off back of text node + res = SplitNode(node, aEndOffset, getter_AddRefs(tmp)); + if (NS_FAILED(res)) return res; + node = tmp; // remember left node + } + if ( aStartOffset ) + { + // we need to split off front of text node + res = SplitNode(node, aStartOffset, getter_AddRefs(tmp)); + if (NS_FAILED(res)) return res; + } + + // reparent the node inside inline node with appropriate {attribute,value} + res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue); + return res; +} + + +nsresult +nsHTMLEditor::SetInlinePropertyOnNode( nsIDOMNode *aNode, + nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue) +{ + if (!aNode || !aProperty) return NS_ERROR_NULL_POINTER; + + nsresult res = NS_OK; + nsCOMPtr tmp; + nsAutoString tag; + aProperty->ToString(tag); + tag.ToLowerCase(); + + // dont need to do anything if property already set on node + PRBool bHasProp; + nsCOMPtr styleNode; + IsTextPropertySetByContent(aNode, aProperty, aAttribute, aValue, bHasProp, getter_AddRefs(styleNode)); + if (bHasProp) return NS_OK; + + // is it already the right kind of node, but with wrong attribute? + if (NodeIsType(aNode, aProperty)) + { + // just set the attribute on it. + // but first remove any contrary style in it's children. + res = RemoveStyleInside(aNode, aProperty, aAttribute, PR_TRUE); + if (NS_FAILED(res)) return res; + nsCOMPtr elem = do_QueryInterface(aNode); + return SetAttribute(elem, *aAttribute, *aValue); + } + + // can it be put inside inline node? + if (TagCanContain(tag, aNode)) + { + nsCOMPtr priorNode, nextNode; + // is either of it's neighbors the right kind of node? + GetPriorHTMLSibling(aNode, &priorNode); + GetNextHTMLSibling(aNode, &nextNode); + if (priorNode && NodeIsType(priorNode, aProperty) && + HasAttrVal(priorNode, aAttribute, aValue) && + IsOnlyAttribute(priorNode, aAttribute) ) + { + // previous sib is already right kind of inline node; slide this over into it + res = MoveNode(aNode, priorNode, -1); + } + else if (nextNode && NodeIsType(nextNode, aProperty) && + HasAttrVal(nextNode, aAttribute, aValue) && + IsOnlyAttribute(priorNode, aAttribute) ) + { + // following sib is already right kind of inline node; slide this over into it + res = MoveNode(aNode, nextNode, 0); + } + else + { + // ok, chuck it in it's very own container + res = InsertContainerAbove(aNode, &tmp, tag, aAttribute, aValue); + } + if (NS_FAILED(res)) return res; + return RemoveStyleInside(aNode, aProperty, aAttribute); + } + // none of the above? then cycle through the children. + nsCOMPtr childNodes; + res = aNode->GetChildNodes(getter_AddRefs(childNodes)); + if (NS_FAILED(res)) return res; + if (childNodes) + { + PRInt32 j; + PRUint32 childCount; + childNodes->GetLength(&childCount); + if (childCount) + { + nsCOMPtr arrayOfNodes; + nsCOMPtr node; + nsCOMPtr isupports; + + // make a array + res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); + if (NS_FAILED(res)) return res; + + // populate the list + for (j=0 ; j < (PRInt32)childCount; j++) + { + nsCOMPtr childNode; + res = childNodes->Item(j, getter_AddRefs(childNode)); + if ((NS_SUCCEEDED(res)) && (childNode) && IsEditable(childNode)) + { + isupports = do_QueryInterface(childNode); + arrayOfNodes->AppendElement(isupports); + } + } + + // then loop through the list, set the property on each node + PRUint32 listCount; + arrayOfNodes->Count(&listCount); + for (j = 0; j < listCount; j++) + { + isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); + node = do_QueryInterface(isupports); + res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; + arrayOfNodes->RemoveElementAt(0); + } + } + } + return res; +} + + +nsresult nsHTMLEditor::SplitStyleAboveRange(nsIDOMRange *inRange, + nsIAtom *aProperty, + const nsString *aAttribute) +{ + if (!inRange || !aProperty) return NS_ERROR_NULL_POINTER; + nsresult res; + nsCOMPtr startNode, endNode, origStartNode; + PRInt32 startOffset, endOffset, origStartOffset; + + res = inRange->GetStartParent(getter_AddRefs(startNode)); + if (NS_FAILED(res)) return res; + res = inRange->GetStartOffset(&startOffset); + if (NS_FAILED(res)) return res; + res = inRange->GetEndParent(getter_AddRefs(endNode)); + if (NS_FAILED(res)) return res; + res = inRange->GetEndOffset(&endOffset); + if (NS_FAILED(res)) return res; + + origStartNode = startNode; + origStartOffset = startOffset; + PRBool sameNode = (startNode==endNode); + + // split any matching style nodes above the start of range + res = SplitStyleAbovePoint(&startNode, &startOffset, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + + if (sameNode && (startNode != origStartNode)) + { + // our startNode got split. This changes the offset of the end of our range. + endOffset -= origStartOffset; + } + + // second verse, same as the first... + res = SplitStyleAbovePoint(&endNode, &endOffset, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + + // reset the range + res = inRange->SetStart(startNode, startOffset); + if (NS_FAILED(res)) return res; + res = inRange->SetEnd(endNode, endOffset); + return res; +} + +nsresult nsHTMLEditor::SplitStyleAbovePoint(nsCOMPtr *aNode, + PRInt32 *aOffset, + nsIAtom *aProperty, + const nsString *aAttribute) +{ + if (!aNode || !*aNode || !aOffset) return NS_ERROR_NULL_POINTER; + // split any matching style nodes above the node/offset + nsCOMPtr parent, tmp = *aNode; + PRInt32 offset; + while (tmp && !nsHTMLEditUtils::IsBody(tmp)) + { + if (NodeIsType(tmp, aProperty)) + { + // found a style node we need to split + SplitNodeDeep(tmp, *aNode, *aOffset, &offset); + // reset startNode/startOffset + tmp->GetParentNode(getter_AddRefs(*aNode)); + *aOffset = offset; + } + tmp->GetParentNode(getter_AddRefs(parent)); + tmp = parent; + } + return NS_OK; +} + +nsresult nsHTMLEditor::RemoveStyleInside(nsIDOMNode *aNode, + nsIAtom *aProperty, + const nsString *aAttribute, + PRBool aChildrenOnly) +{ + if (!aNode || !aProperty) return NS_ERROR_NULL_POINTER; + if (IsTextNode(aNode)) return NS_OK; + nsresult res = NS_OK; + + // first process the children + nsCOMPtr child, tmp; + aNode->GetFirstChild(getter_AddRefs(child)); + while (child) + { + // cache next sibling since we might remove child + child->GetNextSibling(getter_AddRefs(tmp)); + res = RemoveStyleInside(child, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + child = tmp; + } + + // then process the node itself + if (!aChildrenOnly && NodeIsType(aNode, aProperty)) + { + // if we weren't passed an attribute, then we want to + // remove any matching inlinestyles entirely + if (!aAttribute || aAttribute->IsEmpty()) + { + res = RemoveContainer(aNode); + } + // otherwise we just want to eliminate the attribute + else + { + if (HasAttr(aNode, aAttribute)) + { + // if this matching attribute is the ONLY one on the node, + // then remove the whole node. Otherwise just nix the attribute. + if (IsOnlyAttribute(aNode, aAttribute)) + { + res = RemoveContainer(aNode); + } + else + { + nsCOMPtr elem = do_QueryInterface(aNode); + if (!elem) return NS_ERROR_NULL_POINTER; + res = RemoveAttribute(elem, *aAttribute); + } + } + } + } + return res; +} + +PRBool nsHTMLEditor::IsOnlyAttribute(nsIDOMNode *aNode, + const nsString *aAttribute) +{ + if (!aNode || !aAttribute) return PR_FALSE; // ooops + nsCOMPtr content = do_QueryInterface(aNode); + if (!content) return PR_FALSE; // ooops + + PRInt32 attrCount, i, nameSpaceID; + nsIAtom* attrName; + content->GetAttributeCount(attrCount); + + for (i=0; iGetAttributeNameAt(i, nameSpaceID, attrName); + nsAutoString attrString, tmp; + if (!attrName) continue; // ooops + attrName->ToString(attrString); + // if it's the attribute we know about, keep looking + if (attrString.EqualsIgnoreCase(*aAttribute)) continue; + // if it's a special _moz... attribute, keep looking + attrString.Left(tmp,4); + if (tmp=="_moz") continue; + // otherwise, it's another attribute, so return false + return PR_FALSE; + } + // if we made it through all of them without finding a real attribute + // other than aAttribute, then return PR_TRUE + return PR_TRUE; +} + +PRBool +nsHTMLEditor::HasMatchingAttributes(nsIDOMNode *aNode1, + nsIDOMNode *aNode2) +{ + if (!aNode1 || !aNode2) return PR_FALSE; // ooops + nsCOMPtr content1 = do_QueryInterface(aNode1); + if (!content1) return PR_FALSE; // ooops + nsCOMPtr content2 = do_QueryInterface(aNode2); + if (!content2) return PR_FALSE; // ooops + + PRInt32 attrCount, i, nameSpaceID, realCount1=0, realCount2=0; + nsIAtom* attrName; + nsresult res, res2; + content1->GetAttributeCount(attrCount); + nsAutoString attrString, tmp, attrVal1, attrVal2; + + for (i=0; iGetAttributeNameAt(i, nameSpaceID, attrName); + if (!attrName) continue; // ooops + attrName->ToString(attrString); + // if it's a special _moz... attribute, keep going + attrString.Left(tmp,4); + if (tmp=="_moz") continue; + // otherwise, it's another attribute, so count it + realCount1++; + // and compare it to element2's attributes + res = content1->GetAttribute(nameSpaceID, attrName, attrVal1); + res2 = content2->GetAttribute(nameSpaceID, attrName, attrVal2); + if (res != res2) return PR_FALSE; + if (!attrVal1.EqualsIgnoreCase(attrVal2)) return PR_FALSE; + } + + content2->GetAttributeCount(attrCount); + for (i=0; iGetAttributeNameAt(i, nameSpaceID, attrName); + if (!attrName) continue; // ooops + attrName->ToString(attrString); + // if it's a special _moz... attribute, keep going + attrString.Left(tmp,4); + if (tmp=="_moz") continue; + // otherwise, it's another attribute, so count it + realCount2++; + } + + if (realCount1 != realCount2) return PR_FALSE; + // otherwise, attribute counts match, and we already compared them + // when going through the first list, so we're done. + return PR_TRUE; +} + +PRBool nsHTMLEditor::HasAttr(nsIDOMNode *aNode, + const nsString *aAttribute) +{ + if (!aNode) return PR_FALSE; + if (!aAttribute || aAttribute->IsEmpty()) return PR_TRUE; // everybody has the 'null' attribute + + // get element + nsCOMPtr elem = do_QueryInterface(aNode); + if (!elem) return PR_FALSE; + + // get attribute node + nsCOMPtr attNode; + nsresult res = elem->GetAttributeNode(*aAttribute, getter_AddRefs(attNode)); + if ((NS_FAILED(res)) || !attNode) return PR_FALSE; + return PR_TRUE; +} + + +PRBool nsHTMLEditor::HasAttrVal(nsIDOMNode *aNode, + const nsString *aAttribute, + const nsString *aValue) +{ + if (!aNode) return PR_FALSE; + if (!aAttribute || aAttribute->IsEmpty()) return PR_TRUE; // everybody has the 'null' attribute + + // get element + nsCOMPtr elem = do_QueryInterface(aNode); + if (!elem) return PR_FALSE; + + // get attribute node + nsCOMPtr attNode; + nsresult res = elem->GetAttributeNode(*aAttribute, getter_AddRefs(attNode)); + if ((NS_FAILED(res)) || !attNode) return PR_FALSE; + + // check if attribute has a value + PRBool isSet; + attNode->GetSpecified(&isSet); + // if no value, and that's what we wanted, then return true + if (!isSet && (!aValue || aValue->IsEmpty())) return PR_TRUE; + + // get attribute value + nsAutoString attrVal; + attNode->GetValue(attrVal); + + // do values match? + if (attrVal.EqualsIgnoreCase(*aValue)) return PR_TRUE; + return PR_FALSE; +} + + +nsresult nsHTMLEditor::PromoteInlineRange(nsIDOMRange *inRange) +{ + if (!inRange) return NS_ERROR_NULL_POINTER; + nsresult res; + nsCOMPtr startNode, endNode, parent; + PRInt32 startOffset, endOffset; + + res = inRange->GetStartParent(getter_AddRefs(startNode)); + if (NS_FAILED(res)) return res; + res = inRange->GetStartOffset(&startOffset); + if (NS_FAILED(res)) return res; + res = inRange->GetEndParent(getter_AddRefs(endNode)); + if (NS_FAILED(res)) return res; + res = inRange->GetEndOffset(&endOffset); + if (NS_FAILED(res)) return res; + + while ( startNode && + !nsHTMLEditUtils::IsBody(startNode) && + IsAtFrontOfNode(startNode, startOffset) ) + { + res = GetNodeLocation(startNode, &parent, &startOffset); + if (NS_FAILED(res)) return res; + startNode = parent; + } + if (!startNode) return NS_ERROR_NULL_POINTER; + + while ( endNode && + !nsHTMLEditUtils::IsBody(endNode) && + IsAtEndOfNode(endNode, endOffset) ) + { + res = GetNodeLocation(endNode, &parent, &endOffset); + if (NS_FAILED(res)) return res; + endNode = parent; + endOffset++; // we are AFTER this node + } + if (!endNode) return NS_ERROR_NULL_POINTER; + + res = inRange->SetStart(startNode, startOffset); + if (NS_FAILED(res)) return res; + res = inRange->SetEnd(endNode, endOffset); + return res; +} + +PRBool nsHTMLEditor::IsAtFrontOfNode(nsIDOMNode *aNode, PRInt32 aOffset) +{ + if (!aNode) return PR_FALSE; // oops + if (!aOffset) return PR_TRUE; + + if (IsTextNode(aNode)) + { + return PR_FALSE; + } + else + { + nsCOMPtr firstNode; + GetFirstEditableNode(aNode, &firstNode); + if (!firstNode) return PR_TRUE; + PRInt32 offset; + nsEditor::GetChildOffset(firstNode, aNode, offset); + if (offset < aOffset) return PR_FALSE; + return PR_TRUE; + } +} + +PRBool nsHTMLEditor::IsAtEndOfNode(nsIDOMNode *aNode, PRInt32 aOffset) +{ + if (!aNode) return PR_FALSE; // oops + PRUint32 len; + GetLengthOfDOMNode(aNode, len); + if (aOffset == len) return PR_TRUE; + + if (IsTextNode(aNode)) + { + return PR_FALSE; + } + else + { + nsCOMPtr lastNode; + GetLastEditableNode(aNode, &lastNode); + if (!lastNode) return PR_TRUE; + PRInt32 offset; + nsEditor::GetChildOffset(lastNode, aNode, offset); + if (offset < aOffset) return PR_TRUE; + return PR_FALSE; } - return result; } NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, @@ -1176,7 +1726,7 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, PRBool skipNode = PR_FALSE; if (text) { - if (PR_FALSE==isCollapsed && PR_TRUE==first && PR_TRUE==firstNodeInRange) + if (!isCollapsed && first && firstNodeInRange) { firstNodeInRange = PR_FALSE; PRInt32 startOffset; @@ -1194,7 +1744,7 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, { // handle non-text leaf nodes here PRBool canContainChildren; content->CanContainChildren(canContainChildren); - if (PR_TRUE==canContainChildren) + if (canContainChildren) { //if (gNoisy) { printf(" skipping non-leaf node %p\n", content.get()); } skipNode = PR_TRUE; @@ -1203,7 +1753,7 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, //if (gNoisy) { printf(" testing non-text leaf node %p\n", content.get()); } } } - if (PR_FALSE==skipNode) + if (!skipNode) { nsCOMPtrnode; node = do_QueryInterface(content); @@ -1212,12 +1762,12 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, PRBool isSet; nsCOMPtrresultNode; IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode)); - if (PR_TRUE==first) + if (first) { aFirst = isSet; first = PR_FALSE; } - if (PR_TRUE==isSet) { + if (isSet) { aAny = PR_TRUE; } else { @@ -1229,7 +1779,7 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, iter->CurrentNode(getter_AddRefs(content)); } } - if (PR_FALSE==aAny) + if (!aAny) { // make sure that if none of the selection is set, we don't report all is set aAll = PR_FALSE; } @@ -1239,37 +1789,29 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttribute) { - if (!aProperty) { return NS_ERROR_NULL_POINTER; } - if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + if (!aProperty) return NS_ERROR_NULL_POINTER; + if (!mRules) return NS_ERROR_NOT_INITIALIZED; ForceCompositionEnd(); + nsresult res; + nsCOMPtrselection; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_NULL_POINTER; + nsAutoEditBatch batchIt(this); nsAutoRules beginRulesSniffing(this, kOpRemoveTextProperty, nsIEditor::eNext); + nsAutoSelectionReset selectionResetter(selection, this); - if (gNoisy) - { - nsAutoString propString; - aProperty->ToString(propString); - char *propCString = propString.ToNewCString(); - if (gNoisy) { printf("---------- start nsTextEditor::RemoveInlineProperty %s ----------\n", propCString); } - nsCRT::free(propCString); - } - - nsresult result; - nsCOMPtrselection; - result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - PRBool cancel, handled; nsTextRulesInfo ruleInfo(nsTextEditRules::kRemoveTextProperty); - result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); - if (NS_FAILED(result)) return result; + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (NS_FAILED(res)) return res; if (!cancel && !handled) { PRBool isCollapsed; selection->GetIsCollapsed(&isCollapsed); - if (PR_TRUE==isCollapsed) + if (isCollapsed) { // manipulating text attributes on a collapsed selection only sets state for the next text insertion // But only if it's a property for which we have TypeInState! @@ -1286,16 +1828,16 @@ NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsStr // collapsed insertion point. nsCOMPtr selNode, tmpNode, parent; PRInt32 selOffset, outOffset; - result = GetStartNodeAndOffset(selection, &selNode, &selOffset); - if (NS_FAILED(result)) return result; + res = GetStartNodeAndOffset(selection, &selNode, &selOffset); + if (NS_FAILED(res)) return res; tmpNode = selNode; while (tmpNode) { if (nsHTMLEditUtils::IsBody(tmpNode)) break; if (IsLinkNode(tmpNode)) { - result = SplitNodeDeep(tmpNode, selNode, selOffset, &outOffset); - if (NS_FAILED(result)) return result; + res = SplitNodeDeep(tmpNode, selNode, selOffset, &outOffset); + if (NS_FAILED(res)) return res; tmpNode->GetParentNode(getter_AddRefs(parent)); selection->Collapse(parent, outOffset); break; @@ -1307,92 +1849,101 @@ NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsStr } else { - // removing text properties can really shuffle text nodes around - // so we need to keep some extra state to restore a reasonable selection - // after we're done - nsCOMPtr parentForSelection; // selection's block parent - PRInt32 rangeStartOffset, rangeEndOffset; - GetTextSelectionOffsetsForRange(selection, getter_AddRefs(parentForSelection), - rangeStartOffset, rangeEndOffset); - nsCOMPtr startParent, endParent; - PRInt32 startOffset, endOffset; + // get selection range enumerator nsCOMPtr enumerator; - result = selection->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_FAILED(result)) return result; - if (!enumerator) return NS_ERROR_NULL_POINTER; + res = selection->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_FAILED(res)) return res; + if (!enumerator) return NS_ERROR_FAILURE; + // loop thru the ranges in the selection enumerator->First(); - nsCOMPtrcurrentItem; - result = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if (NS_FAILED(result)) return result; - //XXX: should be a while loop to get all ranged in selection - if (currentItem) + nsCOMPtr currentItem; + while ((NS_ENUMERATOR_FALSE == enumerator->IsDone())) { + res = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if (NS_FAILED(res)) return res; + if (!currentItem) return NS_ERROR_FAILURE; + nsCOMPtr range( do_QueryInterface(currentItem) ); - nsCOMPtrcommonParent; - result = range->GetCommonParent(getter_AddRefs(commonParent)); - if (NS_FAILED(result)) return result; - if (!commonParent) return NS_ERROR_NULL_POINTER; - range->GetStartOffset(&startOffset); - range->GetEndOffset(&endOffset); - result = range->GetStartParent(getter_AddRefs(startParent)); - if (NS_FAILED(result)) return result; - if (!startParent) return NS_ERROR_NULL_POINTER; - result = range->GetEndParent(getter_AddRefs(endParent)); - if (NS_FAILED(result)) return result; - if (!endParent) return NS_ERROR_NULL_POINTER; - - if (startParent.get()==endParent.get()) - { // the range is entirely contained within a single text node - // commonParent==aStartParent, so get the "real" parent of the selection - startParent->GetParentNode(getter_AddRefs(commonParent)); - result = RemoveTextPropertiesForNode(startParent, commonParent, - startOffset, endOffset, - aProperty, nsnull); + // adjust range to include any ancestors who's children are entirely selected + res = PromoteInlineRange(range); + if (NS_FAILED(res)) return res; + + // remove this style from ancestors of our range empoints, + // splitting them as appropriate + res = SplitStyleAboveRange(range, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + + // check for easy case: both range endpoints in same text node + nsCOMPtr startNode, endNode; + res = range->GetStartParent(getter_AddRefs(startNode)); + if (NS_FAILED(res)) return res; + res = range->GetEndParent(getter_AddRefs(endNode)); + if (NS_FAILED(res)) return res; + if ((startNode == endNode) && IsTextNode(startNode)) + { + // we're done with this range! } else { - result = RemoveTextPropertiesForNodeWithDifferentParents(startParent,startOffset, - endParent, endOffset, - commonParent, - aProperty, nsnull); - } - if (NS_FAILED(result)) return result; + // not the easy case. range not contained in single text node. + nsCOMPtr iter; + res = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull, + NS_GET_IID(nsIContentIterator), + getter_AddRefs(iter)); + if (NS_FAILED(res)) return res; + if (!iter) return NS_ERROR_FAILURE; - { // compute a range for the selection - // don't want to actually do anything with selection, because - // we are still iterating through it. Just want to create and remember - // an nsIDOMRange, and later add the range to the selection after clearing it. - // XXX: I'm blocked here because nsIDOMSelection doesn't provide a mechanism - // for setting a compound selection yet. + nsCOMPtr arrayOfNodes; + nsCOMPtr content; + nsCOMPtr node; + nsCOMPtr isupports; + + // make a array + res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); + if (NS_FAILED(res)) return res; + + // iterate range and build up array + iter->Init(range); + while (NS_ENUMERATOR_FALSE == iter->IsDone()) + { + res = iter->CurrentNode(getter_AddRefs(content)); + if (NS_FAILED(res)) return res; + node = do_QueryInterface(content); + if (!node) return NS_ERROR_FAILURE; + if (IsEditable(node)) + { + isupports = do_QueryInterface(node); + arrayOfNodes->AppendElement(isupports); + } + res = iter->Next(); + if (NS_FAILED(res)) return res; + } + + // loop through the list, remove the property on each node + PRUint32 listCount; + PRUint32 j; + arrayOfNodes->Count(&listCount); + for (j = 0; j < listCount; j++) + { + isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); + node = do_QueryInterface(isupports); + res = RemoveStyleInside(node, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + arrayOfNodes->RemoveElementAt(0); + } } - } - if (NS_SUCCEEDED(result)) - { - result = CollapseAdjacentTextNodes(selection); // we may have created wasteful consecutive text nodes. collapse them. - } - if (NS_SUCCEEDED(result)) - { - // XXX: ERROR_HANDLING should get a result and validate it - ResetTextSelectionForRange(parentForSelection, rangeStartOffset, rangeEndOffset, selection); + enumerator->Next(); } } } if (!cancel) { // post-process - result = mRules->DidDoAction(selection, &ruleInfo, result); + res = mRules->DidDoAction(selection, &ruleInfo, res); } - if (gNoisy) - { - nsAutoString propString; - aProperty->ToString(propString); - char *propCString = propString.ToNewCString(); - if (gNoisy) { printf("---------- end nsTextEditor::RemoveInlineProperty %s ----------\n", propCString); } - nsCRT::free(propCString); - } - return result; + return res; } NS_IMETHODIMP nsHTMLEditor::IncreaseFontSize() @@ -2094,7 +2645,7 @@ nsHTMLEditor::GetParentBlockTags(nsStringArray *aTagList, PRBool aGetLists) blockParent->GetTagName(blockParentTag); PRBool isRoot; IsRootTag(blockParentTag, isRoot); - if ((PR_FALSE==isRoot) && (-1==aTagList->IndexOf(blockParentTag))) { + if ((!isRoot) && (-1==aTagList->IndexOf(blockParentTag))) { aTagList->AppendString(blockParentTag); } } @@ -2181,7 +2732,7 @@ nsHTMLEditor::ReplaceBlockParent(nsString& aParentTag) if (gNoisy) { char *tag = aParentTag.ToNewCString(); - printf("---------- nsHTMLEditor::ReplaceBlockParent %s ----------\n", tag); + printf("- nsHTMLEditor::ReplaceBlockParent %s -\n", tag); nsCRT::free(tag); } @@ -2191,7 +2742,7 @@ nsHTMLEditor::ReplaceBlockParent(nsString& aParentTag) if (NS_FAILED(res)) return res; if (!selection) return NS_ERROR_NULL_POINTER; - nsAutoSelectionReset selectionResetter(selection); + nsAutoSelectionReset selectionResetter(selection, this); // set the block parent for all selected ranges nsAutoEditBatch beginBatching(this); nsCOMPtr enumerator; @@ -2219,7 +2770,7 @@ NS_IMETHODIMP nsHTMLEditor::RemoveParagraphStyle() { if (gNoisy) { - printf("---------- nsHTMLEditor::RemoveParagraphStyle ----------\n"); + printf("- nsHTMLEditor::RemoveParagraphStyle -\n"); } nsresult res=NS_ERROR_NOT_INITIALIZED; @@ -2228,7 +2779,7 @@ nsHTMLEditor::RemoveParagraphStyle() if (NS_FAILED(res)) return res; if (!selection) return NS_ERROR_NULL_POINTER; - nsAutoSelectionReset selectionResetter(selection); + nsAutoSelectionReset selectionResetter(selection, this); nsAutoEditBatch beginBatching(this); nsCOMPtr enumerator; @@ -2252,7 +2803,7 @@ NS_IMETHODIMP nsHTMLEditor::RemoveParent(const nsString &aParentTag) { if (gNoisy) { - printf("---------- nsHTMLEditor::RemoveParent ----------\n"); + printf("- nsHTMLEditor::RemoveParent -\n"); } nsresult res; @@ -2261,7 +2812,7 @@ nsHTMLEditor::RemoveParent(const nsString &aParentTag) if (NS_FAILED(res)) return res; if (!selection) return NS_ERROR_NULL_POINTER; - nsAutoSelectionReset selectionResetter(selection); + nsAutoSelectionReset selectionResetter(selection, this); nsAutoEditBatch beginBatching(this); nsCOMPtr enumerator; res = selection->GetEnumerator(getter_AddRefs(enumerator)); @@ -3232,7 +3783,7 @@ NS_IMETHODIMP nsHTMLEditor::GetMaxTextLength(PRInt32& aMaxTextLength) #ifdef XP_MAC #pragma mark - -#pragma mark --- nsIEditorStyleSheets methods --- +#pragma mark nsIEditorStyleSheets methods #pragma mark - #endif @@ -3379,7 +3930,7 @@ nsHTMLEditor::ApplyDocumentOrOverrideStyleSheet(const nsString& aURL, PRBool aOv #ifdef XP_MAC #pragma mark - -#pragma mark --- nsIEditorMailSupport methods --- +#pragma mark nsIEditorMailSupport methods #pragma mark - #endif @@ -3605,130 +4156,10 @@ nsHTMLEditor::GetEmbeddedObjects(nsISupportsArray** aNodeList) #ifdef XP_MAC #pragma mark - -#pragma mark --- nsIEditor overrides --- +#pragma mark nsIEditor overrides #pragma mark - #endif -// -// Figure out what formatting needs to go with this node, and insert it. -// -NS_IMETHODIMP -nsHTMLEditor::InsertFormattingForNode(nsIDOMNode* aNode) -{ - // New formatting attempt: just mark the node dirty. - nsCOMPtr element (do_QueryInterface(aNode)); - if (element) - element->SetAttribute("_moz_dirty", ""); - return NS_OK; - -#ifdef FORMATTING_NODES_IN_DOM - nsresult res; - - // Don't insert any formatting unless it's an element node - PRUint16 nodeType; - res = aNode->GetNodeType(&nodeType); - if (NS_FAILED(res)) - return res; - if (nodeType != nsIDOMNode::ELEMENT_NODE) - return NS_OK; - - // Don't insert formatting if we're a plaintext editor. - // The newlines get considered to be part of the text. - // This, of course, makes the html look lousy, but we're expecting - // that plaintext editors will only output plaintext, not html. - if (mFlags & nsHTMLEditor::eEditorPlaintextMask) - return NS_OK; - - nsCOMPtr parent; - res = aNode->GetParentNode(getter_AddRefs(parent)); - if (NS_FAILED(res)) - return res; - -#ifdef DEBUG_formatting - nsString namestr; - aNode->GetNodeName(namestr); - //DumpContentTree(); - char* nodename = namestr.ToNewCString(); - printf("Inserting formatting for node <%s> at offset %d\n", - nodename, GetIndexOf(parent, aNode)); -#endif /* DEBUG_formatting */ - - // If it has children, first iterate over the children: - nsCOMPtr child; - res = aNode->GetFirstChild(getter_AddRefs(child)); - if (NS_SUCCEEDED(res) && child) - { -#ifdef DEBUG_formatting - printf("%s: Iterating over children\n", nodename); -#endif /* DEBUG_formatting */ - - while (child) - { -#ifdef DEBUG_formatting - printf("%s child\n", nodename); -#endif /* DEBUG_formatting */ - InsertFormattingForNode(child); - nsCOMPtr nextSib; - child->GetNextSibling(getter_AddRefs(nextSib)); - child = nextSib; - } - } - - nsAutoString newline ("\n"); - - // - // XXX Would be nice, ultimately, to format according to user prefs. - // - res = NS_OK; - - PRInt32 offset = GetIndexOf(parent, aNode); - - if (nsHTMLEditUtils::IsBreak(aNode) && !nsHTMLEditUtils::IsMozBR(aNode)) - { - // After the close tag - res = InsertNoneditableTextNode(parent, offset+1, newline); - } - - else if (nsEditor::IsBlockNode(aNode)) - { -#ifdef DEBUG_formatting - printf("Block node %s at offset %d\n-----------\n", nodename, offset); - DumpContentTree(); -#endif /* DEBUG_formatting */ - if (!nsHTMLEditUtils::IsListItem(aNode)) - { - // After the close tag - InsertNoneditableTextNode(parent, offset+1, newline); -#ifdef DEBUG_formatting - printf("Now %s has offset %d\n-----------\n", - nodename, GetIndexOf(parent, aNode)); - DumpContentTree(); - printf("----------------\n"); -#endif /* DEBUG_formatting */ - } - - // Before the open tag - res = InsertNoneditableTextNode(parent, offset, newline); -#ifdef DEBUG_formatting - printf("And NOW, %s has offset %d\n-----------\n", nodename, GetIndexOf(parent, aNode)); - DumpContentTree(); - printf("----------------\n"); -#endif /* DEBUG_formatting */ - } - - // Some inline tags for which we might want formatting: - else if (nsHTMLEditUtils::IsImage(aNode)) - { - res = InsertNoneditableTextNode(parent, offset, newline); - } - -#ifdef DEBUG_formatting - Recycle(nodename); -#endif /* DEBUG_formatting */ - - return res; -#endif // FORMATTING_NODES_IN_DOM -} NS_IMETHODIMP nsHTMLEditor::Undo(PRUint32 aCount) @@ -4503,7 +4934,7 @@ nsHTMLEditor::DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed) #ifdef XP_MAC #pragma mark - -#pragma mark --- nsIEditorIMESupport overrides --- +#pragma mark nsIEditorIMESupport overrides #pragma mark - #endif @@ -4552,7 +4983,7 @@ nsHTMLEditor::SetCompositionString(const nsString& aCompositionString, nsIPrivat #ifdef XP_MAC #pragma mark - -#pragma mark --- StyleSheet utils --- +#pragma mark StyleSheet utils #pragma mark - #endif @@ -4598,7 +5029,7 @@ void nsHTMLEditor::ApplyStyleSheetToPresShellDocument(nsICSSStyleSheet* aSheet, #ifdef XP_MAC #pragma mark - -#pragma mark --- nsEditor overrides --- +#pragma mark nsEditor overrides #pragma mark - #endif @@ -4618,11 +5049,11 @@ nsHTMLEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection) /** All editor operations which alter the doc should be followed * with a call to EndOperation, naming the action and direction */ NS_IMETHODIMP -nsHTMLEditor::EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection, PRBool aSetSelection) +nsHTMLEditor::EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection) { if (! ((opID==kOpInsertText) || (opID==kOpInsertIMEText)) ) ClearInlineStylesCache(); - if (mRules) return mRules->AfterEdit(opID, aDirection, aSetSelection); + if (mRules) return mRules->AfterEdit(opID, aDirection); return NS_OK; } @@ -4673,7 +5104,7 @@ nsHTMLEditor::SelectEntireDocument(nsIDOMSelection *aSelection) #ifdef XP_MAC #pragma mark - -#pragma mark --- Random methods --- +#pragma mark Random methods #pragma mark - #endif @@ -4716,7 +5147,7 @@ nsHTMLEditor::SetTypeInStateForProperty(TypeInState &aTypeInState, aTypeInState.GetEnumForName(aPropName, propEnum); if (nsIEditProperty::b==aPropName || nsIEditProperty::i==aPropName || nsIEditProperty::u==aPropName) { - if (PR_TRUE==aTypeInState.IsSet(propEnum)) + if (aTypeInState.IsSet(propEnum)) { // toggle currently set boldness aTypeInState.UnSet(propEnum); } @@ -4738,7 +5169,7 @@ nsHTMLEditor::SetTypeInStateForProperty(TypeInState &aTypeInState, aTypeInState.GetEnumForName(attribute, attrEnum); if (nsIEditProperty::color==attribute || nsIEditProperty::face==attribute || nsIEditProperty::size==attribute) { - if (PR_TRUE==aTypeInState.IsSet(attrEnum)) + if (aTypeInState.IsSet(attrEnum)) { if (nsnull==aValue) { aTypeInState.UnSet(attrEnum); @@ -4754,7 +5185,7 @@ nsHTMLEditor::SetTypeInStateForProperty(TypeInState &aTypeInState, PRBool all = PR_FALSE; PRBool first = PR_FALSE; GetInlineProperty(aPropName, aAttribute, aValue, first, any, all); // operates on current selection - if (PR_FALSE==all) { + if (!all) { aTypeInState.SetPropValue(attrEnum, *aValue); } } @@ -4813,7 +5244,7 @@ void nsHTMLEditor::IsTextPropertySetByContent(nsIDOMNode *aNode, else { found = PR_TRUE; } - if (PR_TRUE==found) + if (found) { aIsSet = PR_TRUE; break; @@ -4933,7 +5364,7 @@ nsresult nsHTMLEditor::GetAbsoluteOffsetsForPoints(nsIDOMNode *aInStartNode, { nsCOMPtrcurrentNode = do_QueryInterface(textNode); if (!currentNode) {return NS_ERROR_NO_INTERFACE;} - if (PR_TRUE==IsEditable(currentNode)) + if (IsEditable(currentNode)) { if (currentNode.get() == aInStartNode) { @@ -5069,7 +5500,7 @@ void nsHTMLEditor::ResetTextSelectionForRange(nsIDOMNode *aParent, { PRUint32 length; textNode->GetLength(&length); - if ((PR_FALSE==setStart) && aOutStartOffset<=(PRInt32)(totalLength+length)) + if ((!setStart) && aOutStartOffset<=(PRInt32)(totalLength+length)) { setStart = PR_TRUE; startNode = do_QueryInterface(textNode); @@ -5119,7 +5550,7 @@ nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, PRBool nodeIsInline; PRBool nodeIsBlock=PR_FALSE; IsNodeInline(aNode, nodeIsInline); - if (PR_FALSE==nodeIsInline) + if (!nodeIsInline) { nsresult QIResult; nsCOMPtrnodeAsText; @@ -5129,7 +5560,7 @@ nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, } } // if aNode is the block parent, then the node to reparent is one of its children - if (PR_TRUE==nodeIsBlock) + if (nodeIsBlock) { res = aNode->QueryInterface(NS_GET_IID(nsIDOMNode), getter_AddRefs(blockParentElement)); if (NS_SUCCEEDED(res) && blockParentElement) { @@ -5151,7 +5582,7 @@ nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, blockParentElement->GetTagName(parentTag); PRBool isRoot; IsRootTag(parentTag, isRoot); - if (PR_TRUE==isRoot) + if (isRoot) { // if nodeToReParent is a text node, we have Text. // re-parent Text into a new at the offset of Text in @@ -5180,7 +5611,7 @@ nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, else { // the block parent is not a ROOT, // for the selected block content, transform blockParentNode - if (((eReplaceParent==aTransformation) && (PR_FALSE==parentTag.EqualsIgnoreCase(aParentTag))) || + if (((eReplaceParent==aTransformation) && (!parentTag.EqualsIgnoreCase(aParentTag))) || (eInsertParent==aTransformation)) { if (gNoisy) { DebugDumpContent(); } // DEBUG @@ -5190,7 +5621,7 @@ nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, { PRBool hasChildren; blockParentNode->HasChildNodes(&hasChildren); - if (PR_FALSE==hasChildren) + if (!hasChildren) { res = nsEditor::DeleteNode(blockParentNode); if (gNoisy) @@ -5260,7 +5691,7 @@ nsHTMLEditor::ReParentBlockContent(nsIDOMNode *aNode, IsRootTag(blockParentTag, isRootBlock); } - if (PR_TRUE==isRootBlock) + if (isRootBlock) { // we're creating a block element where a block element did not previously exist removeBreakBefore = PR_TRUE; removeBreakAfter = PR_TRUE; @@ -5268,7 +5699,7 @@ nsHTMLEditor::ReParentBlockContent(nsIDOMNode *aNode, // apply the transformation PRInt32 offsetInParent=0; - if (eInsertParent==aTransformation || PR_TRUE==isRootBlock) + if (eInsertParent==aTransformation || isRootBlock) { res = GetChildOffset(leftNode, blockParentNode, offsetInParent); NS_ASSERTION((NS_SUCCEEDED(res)), "bad res from GetChildOffset"); @@ -5328,16 +5759,10 @@ nsHTMLEditor::ReParentBlockContent(nsIDOMNode *aNode, while (NS_SUCCEEDED(res) && childNode) { childNode->GetPreviousSibling(getter_AddRefs(previousSiblingNode)); - // explicitly delete of childNode from it's current parent - // can't just rely on DOM semantics of InsertNode doing the delete implicitly, doesn't undo! - res = nsEditor::DeleteNode(childNode); - if (NS_SUCCEEDED(res)) + res = nsEditor::MoveNode(childNode, *aNewParentNode, 0); + if (gNoisy) { - res = nsEditor::InsertNode(childNode, *aNewParentNode, 0); - if (gNoisy) - { - printf("re-parented sibling node %p\n", childNode.get()); - } + printf("re-parented sibling node %p\n", childNode.get()); } if (childNode==leftNode || rightNode==leftNode) { break; @@ -5382,7 +5807,7 @@ nsHTMLEditor::ReParentBlockContent(nsIDOMNode *aNode, } } } - if ((NS_SUCCEEDED(res)) && (PR_TRUE==removeBlockParent)) + if ((NS_SUCCEEDED(res)) && (removeBlockParent)) { // we determined we need to remove the previous block parent. Do it! // go through list backwards so deletes don't interfere with the iteration nsCOMPtr childNodes; @@ -5402,10 +5827,7 @@ nsHTMLEditor::ReParentBlockContent(nsIDOMNode *aNode, res = childNodes->Item(i, getter_AddRefs(childNode)); if ((NS_SUCCEEDED(res)) && (childNode)) { - res = DeleteNode(childNode); - if (NS_SUCCEEDED(res)) { - res = InsertNode(childNode, grandParent, offsetInParent); - } + res = MoveNode(childNode, grandParent, offsetInParent); } } if (gNoisy) { printf("removing the old block parent %p\n", blockParentNode.get()); } @@ -5505,7 +5927,7 @@ nsHTMLEditor::RemoveParagraphStyleFromBlockContent(nsIDOMRange *aRange) blockParentElement->GetTagName(blockParentTag); PRBool isSubordinateBlock; IsSubordinateBlock(blockParentTag, isSubordinateBlock); - if (PR_FALSE==isSubordinateBlock) { + if (!isSubordinateBlock) { break; } else @@ -5531,12 +5953,8 @@ nsHTMLEditor::RemoveParagraphStyleFromBlockContent(nsIDOMRange *aRange) res = childNodes->Item(i, getter_AddRefs(childNode)); if ((NS_SUCCEEDED(res)) && (childNode)) { - res = DeleteNode(childNode); - if (NS_SUCCEEDED(res)) - { - res = InsertNode(childNode, grandParent, offsetInParent); - if (NS_FAILED(res)) return res; - } + res = MoveNode(childNode, grandParent, offsetInParent); + if (NS_FAILED(res)) return res; } } if (NS_SUCCEEDED(res)) @@ -5629,9 +6047,7 @@ nsHTMLEditor::RemoveParentFromBlockContent(const nsString &aParentTag, nsIDOMRan res = childNodes->Item(i, getter_AddRefs(childNode)); if (NS_FAILED(res)) return res; if (!childNode) return NS_ERROR_NULL_POINTER; - res = DeleteNode(childNode); - if (NS_FAILED(res)) return res; - res = InsertNode(childNode, grandParent, offsetInParent); + res = MoveNode(childNode, grandParent, offsetInParent); if (NS_FAILED(res)) return res; } res = DeleteNode(parentElement); @@ -5639,7 +6055,7 @@ nsHTMLEditor::RemoveParentFromBlockContent(const nsString &aParentTag, nsIDOMRan break; } - else if (PR_TRUE==isRoot) { // hit a local root node, terminate loop + else if (isRoot) { // hit a local root node, terminate loop break; } res = parentElement->GetParentNode(getter_AddRefs(parentNode)); @@ -5765,10 +6181,10 @@ nsHTMLEditor::IsRootTag(nsString &aTag, PRBool &aIsTag) static char tdTag[] = "td"; static char thTag[] = "th"; static char captionTag[] = "caption"; - if (PR_TRUE==aTag.EqualsIgnoreCase(bodyTag) || - PR_TRUE==aTag.EqualsIgnoreCase(tdTag) || - PR_TRUE==aTag.EqualsIgnoreCase(thTag) || - PR_TRUE==aTag.EqualsIgnoreCase(captionTag) ) + if (aTag.EqualsIgnoreCase(bodyTag) || + aTag.EqualsIgnoreCase(tdTag) || + aTag.EqualsIgnoreCase(thTag) || + aTag.EqualsIgnoreCase(captionTag) ) { aIsTag = PR_TRUE; } @@ -5793,18 +6209,18 @@ nsHTMLEditor::IsSubordinateBlock(nsString &aTag, PRBool &aIsTag) static char li[] = "li"; static char dt[] = "dt"; static char dd[] = "dd"; - if (PR_TRUE==aTag.EqualsIgnoreCase(p) || - PR_TRUE==aTag.EqualsIgnoreCase(h1) || - PR_TRUE==aTag.EqualsIgnoreCase(h2) || - PR_TRUE==aTag.EqualsIgnoreCase(h3) || - PR_TRUE==aTag.EqualsIgnoreCase(h4) || - PR_TRUE==aTag.EqualsIgnoreCase(h5) || - PR_TRUE==aTag.EqualsIgnoreCase(h6) || - PR_TRUE==aTag.EqualsIgnoreCase(address) || - PR_TRUE==aTag.EqualsIgnoreCase(pre) || - PR_TRUE==aTag.EqualsIgnoreCase(li) || - PR_TRUE==aTag.EqualsIgnoreCase(dt) || - PR_TRUE==aTag.EqualsIgnoreCase(dd) ) + if (aTag.EqualsIgnoreCase(p) || + aTag.EqualsIgnoreCase(h1) || + aTag.EqualsIgnoreCase(h2) || + aTag.EqualsIgnoreCase(h3) || + aTag.EqualsIgnoreCase(h4) || + aTag.EqualsIgnoreCase(h5) || + aTag.EqualsIgnoreCase(h6) || + aTag.EqualsIgnoreCase(address) || + aTag.EqualsIgnoreCase(pre) || + aTag.EqualsIgnoreCase(li) || + aTag.EqualsIgnoreCase(dt) || + aTag.EqualsIgnoreCase(dd) ) { aIsTag = PR_TRUE; } @@ -5920,7 +6336,7 @@ nsHTMLEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parent PRBool testCollapsed; debugResult = selection->GetIsCollapsed(&testCollapsed); NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion"); - NS_ASSERTION(PR_TRUE==testCollapsed, "selection not reset after deletion"); + NS_ASSERTION(testCollapsed, "selection not reset after deletion"); } #endif } @@ -6131,7 +6547,7 @@ nsHTMLEditor::SetTextPropertiesForNode(nsIDOMNode *aNode, PRBool textPropertySet; nsCOMPtrresultNode; IsTextPropertySetByContent(aNode, aPropName, aAttribute, aValue, textPropertySet, getter_AddRefs(resultNode)); - if (PR_FALSE==textPropertySet) + if (!textPropertySet) { if (aValue && 0!=aValue->Length()) { @@ -6161,15 +6577,11 @@ nsHTMLEditor::SetTextPropertiesForNode(nsIDOMNode *aNode, // XXX: need to loop for aStartOffset!=aEndOffset-1? PRInt32 offsetInParent = aStartOffset; // remember where aNode was in aParent if (NS_SUCCEEDED(result) && child) - { // remove child from parent - result = nsEditor::DeleteNode(child); + { // move child + result = nsEditor::MoveNode(child, newStyleNode, 0); if (NS_SUCCEEDED(result)) - { // put child into the newStyleNode - result = nsEditor::InsertNode(child, newStyleNode, 0); - if (NS_SUCCEEDED(result)) - { // put newStyleNode in parent where child was - result = nsEditor::InsertNode(newStyleNode, parent, offsetInParent); - } + { // put newStyleNode in parent where child was + result = nsEditor::InsertNode(newStyleNode, parent, offsetInParent); } } } @@ -6242,7 +6654,7 @@ NS_IMETHODIMP nsHTMLEditor::MoveContentOfNodeIntoNewParent(nsIDOMNode *aNode, // ??? can we really compute this? } */ - if (PR_FALSE==done) + if (!done) { // if we've ended up with an empty text node, just delete it and we're done nsCOMPtrnewChildNodeAsChar; @@ -6259,7 +6671,7 @@ NS_IMETHODIMP nsHTMLEditor::MoveContentOfNodeIntoNewParent(nsIDOMNode *aNode, } } // move the new child node into the new parent - if (PR_FALSE==done) + if (!done) { // first, move the new parent into the correct location PRInt32 offsetInParent; @@ -6277,22 +6689,7 @@ NS_IMETHODIMP nsHTMLEditor::MoveContentOfNodeIntoNewParent(nsIDOMNode *aNode, if (NS_SUCCEEDED(result)) { // then move the new child into the new parent node - result = nsEditor::DeleteNode(newChildNode); - if (NS_SUCCEEDED(result)) - { - result = nsEditor::InsertNode(newChildNode, aNewParentNode, 0); - if (NS_SUCCEEDED(result)) - { - // set the selection - nsCOMPtrselection; - result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - selection->Collapse(newChildNode, 0); - PRInt32 endOffset = aEndOffset-aStartOffset; - selection->Extend(newChildNode, endOffset); - } - } + result = nsEditor::MoveNode(newChildNode, aNewParentNode, 0); } } } @@ -6316,12 +6713,12 @@ nsHTMLEditor::SetTextPropertiesForNodesWithSameParent(nsIDOMNode *aStartNode, const nsString *aAttribute, const nsString *aValue) { - if (gNoisy) { printf("---------- start nsTextEditor::SetTextPropertiesForNodesWithSameParent ----------\n"); } + if (gNoisy) { printf("- start nsTextEditor::SetTextPropertiesForNodesWithSameParent -\n"); } nsresult result=NS_OK; PRBool textPropertySet; nsCOMPtrresultNode; IsTextPropertySetByContent(aStartNode, aPropName, aAttribute, aValue, textPropertySet, getter_AddRefs(resultNode)); - if (PR_FALSE==textPropertySet) + if (!textPropertySet) { result = RemoveTextPropertiesForNodeWithDifferentParents(aStartNode, aStartOffset, aEndNode, aEndOffset, @@ -6446,70 +6843,43 @@ nsHTMLEditor::MoveContiguousContentIntoNewParent(nsIDOMNode *aStartNode, result = startNode->GetNextSibling(getter_AddRefs(intermediateNode)); if (NS_SUCCEEDED(result)) { - result = nsEditor::DeleteNode(startNode); - if (gNoisy) printf("just after DeleteNode 1\n"); + PRInt32 childIndex=0; + result = nsEditor::MoveNode(startNode, aNewParentNode, childIndex); + if (gNoisy) printf("just after MoveNode 2\n"); if (gNoisy) {DebugDumpContent(); } // DEBUG - if (NS_SUCCEEDED(result)) - { - PRInt32 childIndex=0; - result = nsEditor::InsertNode(startNode, aNewParentNode, childIndex); - if (gNoisy) printf("just after InsertNode 2\n"); - if (gNoisy) {DebugDumpContent(); } // DEBUG - childIndex++; - if (NS_SUCCEEDED(result)) - { // move all the intermediate nodes into the new parent node - nsCOMPtrnextSibling; - while (intermediateNode.get() != endNode.get()) + childIndex++; + if (NS_SUCCEEDED(result)) + { // move all the intermediate nodes into the new parent node + nsCOMPtrnextSibling; + while (intermediateNode.get() != endNode.get()) + { + if (!intermediateNode) + result = NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) { - if (!intermediateNode) - result = NS_ERROR_NULL_POINTER; - if (NS_FAILED(result)) { - break; - } - // get the next sibling before moving the current child!!! - intermediateNode->GetNextSibling(getter_AddRefs(nextSibling)); - result = nsEditor::DeleteNode(intermediateNode); - if (gNoisy) printf("just after DeleteNode 3\n"); - if (gNoisy) {DebugDumpContent(); } // DEBUG - if (NS_SUCCEEDED(result)) - { - result = nsEditor::InsertNode(intermediateNode, aNewParentNode, childIndex); - if (gNoisy) printf("just after InsertNode 4\n"); - if (gNoisy) {DebugDumpContent(); } // DEBUG - childIndex++; - } - intermediateNode = do_QueryInterface(nextSibling); - } - if (NS_SUCCEEDED(result)) - { // move the left half of the end node into the new parent node - result = nsEditor::DeleteNode(newRightNode); - if (gNoisy) printf("just after DeleteNode 5\n"); - if (gNoisy) {DebugDumpContent(); } // DEBUG - if (NS_SUCCEEDED(result)) - { - result = nsEditor::InsertNode(newRightNode, aNewParentNode, childIndex); - if (gNoisy) printf("just after InsertNode 5\n"); - if (gNoisy) {DebugDumpContent(); } // DEBUG - // now set the selection - if (NS_SUCCEEDED(result)) - { - nsCOMPtrselection; - result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - selection->Collapse(startNode, startOffset); - selection->Extend(newRightNode, endOffset); - } - } + break; } + // get the next sibling before moving the current child!!! + intermediateNode->GetNextSibling(getter_AddRefs(nextSibling)); + result = nsEditor::MoveNode(intermediateNode, aNewParentNode, childIndex); + if (gNoisy) printf("just after MoveNode 4\n"); + if (gNoisy) {DebugDumpContent(); } // DEBUG + childIndex++; } + intermediateNode = do_QueryInterface(nextSibling); + } + if (NS_SUCCEEDED(result)) + { // move the left half of the end node into the new parent node + result = nsEditor::MoveNode(newRightNode, aNewParentNode, childIndex); + if (gNoisy) printf("just after MoveNode 5\n"); + if (gNoisy) {DebugDumpContent(); } // DEBUG } } } } } } - if (gNoisy) { printf("--- end nsTextEditor::MoveContiguousContentIntoNewParent ---\n"); } + if (gNoisy) { printf(" end nsTextEditor::MoveContiguousContentIntoNewParent \n"); } if (gNoisy) {DebugDumpContent(); } // DEBUG return result; } @@ -6519,8 +6889,8 @@ NS_IMETHODIMP nsHTMLEditor::IsLeafThatTakesInlineStyle(const nsString *aTag, { if (!aTag) { return NS_ERROR_NULL_POINTER; } - aResult = (PRBool)((PR_FALSE==aTag->EqualsIgnoreCase("br")) && - (PR_FALSE==aTag->EqualsIgnoreCase("hr")) ); + aResult = (PRBool)((!aTag->EqualsIgnoreCase("br")) && + (!aTag->EqualsIgnoreCase("hr")) ); return NS_OK; } @@ -6575,7 +6945,7 @@ nsHTMLEditor::SetTextPropertiesForNodeWithDifferentParents(nsIDOMRange *aRange, // find our starting point PRBool startIsText = IsTextNode(aStartNode); nsCOMPtrstartContent; - if (PR_TRUE==startIsText) { + if (startIsText) { startContent = do_QueryInterface(aStartNode); } else { @@ -6586,7 +6956,7 @@ nsHTMLEditor::SetTextPropertiesForNodeWithDifferentParents(nsIDOMRange *aRange, // find our ending point PRBool endIsText = IsTextNode(aEndNode); nsCOMPtrendContent; - if (PR_TRUE==endIsText) { + if (endIsText) { endContent = do_QueryInterface(aEndNode); } else @@ -6622,7 +6992,7 @@ nsHTMLEditor::SetTextPropertiesForNodeWithDifferentParents(nsIDOMRange *aRange, { PRBool canContainChildren; content->CanContainChildren(canContainChildren); - if (PR_FALSE==canContainChildren) + if (!canContainChildren) { nsEditor::GetTagString(node,tag); PRBool processLeaf; @@ -6634,7 +7004,7 @@ nsHTMLEditor::SetTextPropertiesForNodeWithDifferentParents(nsIDOMRange *aRange, PRBool textPropertySet; nsCOMPtrresultNode; IsTextPropertySetByContent(node, aPropName, aAttribute, aValue, textPropertySet, getter_AddRefs(resultNode)); - if (PR_FALSE==textPropertySet) + if (!textPropertySet) { if (gNoisy) { printf("property not set\n"); } node->GetParentNode(getter_AddRefs(parent)); @@ -6642,7 +7012,7 @@ nsHTMLEditor::SetTextPropertiesForNodeWithDifferentParents(nsIDOMRange *aRange, nsCOMPtrparentContent; parentContent = do_QueryInterface(parent); nsCOMPtrparentNode = do_QueryInterface(parent); - if (PR_TRUE==IsTextNode(node)) + if (IsTextNode(node)) { startOffset = 0; result = GetLengthOfDOMNode(node, (PRUint32&)endOffset); @@ -6763,7 +7133,7 @@ nsHTMLEditor::RemoveTextPropertiesForNode(nsIDOMNode *aNode, PRBool textPropertySet; nsCOMPtrresultNode; IsTextPropertySetByContent(aNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode)); - if (PR_TRUE==textPropertySet) + if (textPropertySet) { nsCOMPtrparent; // initially set to first interior parent node to process nsCOMPtrnewMiddleNode; // this will be the middle node after any required splits @@ -6823,7 +7193,7 @@ nsHTMLEditor::RemoveTextPropertiesForNode(nsIDOMNode *aNode, { const PRUnichar *unicodeString; aPropName->GetUnicode(&unicodeString); - if (PR_FALSE==tag.EqualsIgnoreCase(unicodeString)) + if (!tag.EqualsIgnoreCase(unicodeString)) { PRInt32 offsetInParent; result = GetChildOffset(newMiddleNode, parent, offsetInParent); @@ -6918,35 +7288,29 @@ nsHTMLEditor::RemoveTextPropertiesForNode(nsIDOMNode *aNode, result = parent->GetParentNode(getter_AddRefs(grandParent)); if (NS_SUCCEEDED(result) && grandParent) { - if (gNoisy) { printf("* deleting middle node %p\n", newMiddleNode.get());} - result = nsEditor::DeleteNode(newMiddleNode); - if (gNoisy) {DebugDumpContent(); } // DEBUG - if (NS_SUCCEEDED(result)) + PRInt32 position; + result = GetChildOffset(parent, grandParent, position); + if (NS_SUCCEEDED(result)) { - PRInt32 position; - result = GetChildOffset(parent, grandParent, position); + if (insertAfter) + { + if (gNoisy) {printf("insertAfter=PR_TRUE, incr. position\n"); } + position++; + } + if (gNoisy) { + printf("* inserting node %p in grandparent %p at offset %d\n", + newMiddleNode.get(), grandParent.get(), position); + } + result = nsEditor::MoveNode(newMiddleNode, grandParent, position); + if (gNoisy) {DebugDumpContent(); } // DEBUG if (NS_SUCCEEDED(result)) { - if (PR_TRUE==insertAfter) - { - if (gNoisy) {printf("insertAfter=PR_TRUE, incr. position\n"); } - position++; - } - if (gNoisy) { - printf("* inserting node %p in grandparent %p at offset %d\n", - newMiddleNode.get(), grandParent.get(), position); - } - result = nsEditor::InsertNode(newMiddleNode, grandParent, position); - if (gNoisy) {DebugDumpContent(); } // DEBUG - if (NS_SUCCEEDED(result)) - { - PRBool hasChildren=PR_TRUE; - parent->HasChildNodes(&hasChildren); - if (PR_FALSE==hasChildren) { - if (gNoisy) { printf("* deleting empty style node %p\n", parent.get());} - result = nsEditor::DeleteNode(parent); - if (gNoisy) {DebugDumpContent(); } // DEBUG - } + PRBool hasChildren=PR_TRUE; + parent->HasChildNodes(&hasChildren); + if (!hasChildren) { + if (gNoisy) { printf("* deleting empty style node %p\n", parent.get());} + result = nsEditor::DeleteNode(parent); + if (gNoisy) {DebugDumpContent(); } // DEBUG } } } @@ -6987,7 +7351,7 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar nsIAtom *aPropName, const nsString *aAttribute) { - if (gNoisy) { printf("----- start nsTextEditor::RemoveTextPropertiesForNodeWithDifferentParents-----\n"); } + if (gNoisy) { printf("-- start nsTextEditor::RemoveTextPropertiesForNodeWithDifferentParents--\n"); } nsresult result=NS_OK; if (!aStartNode || !aEndNode || !aParent || !aPropName) return NS_ERROR_NULL_POINTER; @@ -7016,7 +7380,7 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar if ((PRUint32)aStartOffset!=count) { // only do this if at least one child is selected IsTextPropertySetByContent(aStartNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode)); - if (PR_TRUE==textPropertySet) + if (textPropertySet) { result = RemoveTextPropertiesForNode(aStartNode, parent, aStartOffset, count, aPropName, aAttribute); if (0!=aStartOffset) { @@ -7041,7 +7405,7 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar parent = do_QueryInterface(aStartNode); if (!startNode || !parent) {return NS_ERROR_NULL_POINTER;} IsTextPropertySetByContent(startNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode)); - if (PR_TRUE==textPropertySet) + if (textPropertySet) { result = RemoveTextPropertiesForNode(startNode, parent, aStartOffset, aStartOffset+1, aPropName, aAttribute); } @@ -7065,7 +7429,7 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar if (aEndOffset!=0) { // only do this if at least one child is selected IsTextPropertySetByContent(endNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode)); - if (PR_TRUE==textPropertySet) + if (textPropertySet) { result = RemoveTextPropertiesForNode(endNode, parent, 0, aEndOffset, aPropName, aAttribute); if (0!=aEndOffset && (((PRInt32)count)!=aEndOffset)) { @@ -7093,7 +7457,7 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar parent = do_QueryInterface(aEndNode); if (!endNode || !parent) {return NS_ERROR_NULL_POINTER;} IsTextPropertySetByContent(endNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode)); - if (PR_TRUE==textPropertySet) + if (textPropertySet) { result = RemoveTextPropertiesForNode(endNode, parent, aEndOffset-1, aEndOffset, aPropName, aAttribute); } @@ -7117,7 +7481,7 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar if (NS_FAILED(result)) { return result; } if (!range) { return NS_ERROR_NULL_POINTER; } // compute the start node - if (PR_TRUE==skippedStartNode) + if (skippedStartNode) { nsCOMPtrtempNode = do_QueryInterface(startNode); nsEditor::GetNextNode(startNode, PR_TRUE, getter_AddRefs(tempNode)); @@ -7199,16 +7563,10 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar while (NS_SUCCEEDED(result) && childNode) { childNode->GetPreviousSibling(getter_AddRefs(previousSiblingNode)); - // explicitly delete of childNode from styleNode - // can't just rely on DOM semantics of InsertNode doing the delete implicitly, doesn't undo! - result = nsEditor::DeleteNode(childNode); - if (NS_SUCCEEDED(result)) + result = nsEditor::MoveNode(childNode, parentNode, position); + if (gNoisy) { - result = nsEditor::InsertNode(childNode, parentNode, position); - if (gNoisy) - { - printf("deleted next sibling node %p\n", childNode.get()); - } + printf("deleted next sibling node %p\n", childNode.get()); } childNode = do_QueryInterface(previousSiblingNode); } // end while loop @@ -7232,13 +7590,13 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar if (!selection) return NS_ERROR_NULL_POINTER; // set the sel. start point. if we skipped the start node, just use it - if (PR_TRUE==skippedStartNode) + if (skippedStartNode) startNode = do_QueryInterface(aStartNode); result = selection->Collapse(startNode, rangeStartOffset); if (NS_FAILED(result)) return result; // set the sel. end point. if we skipped the end node, just use it - if (PR_TRUE==skippedEndNode) + if (skippedEndNode) endNode = do_QueryInterface(aEndNode); result = selection->Extend(endNode, rangeEndOffset); if (NS_FAILED(result)) return result; @@ -7280,7 +7638,7 @@ nsHTMLEditor::CollapseAdjacentTextNodes(nsIDOMSelection *aInSelection) PRBool isCollapsed; aInSelection->GetIsCollapsed(&isCollapsed); - if (PR_TRUE==isCollapsed) { return NS_OK; } // no need to scan collapsed selection + if (isCollapsed) { return NS_OK; } // no need to scan collapsed selection // store info about selection endpoints so we can re-establish selection after collapsing text nodes // XXX: won't work for multiple selections (this will create a single selection from anchor to focus) @@ -7485,9 +7843,9 @@ nsHTMLEditor::GetNextElementByTagName(nsIDOMElement *aCurrentElement, NS_IMETHODIMP nsHTMLEditor::SetSelectionAtDocumentStart(nsIDOMSelection *aSelection) { - nsCOMPtr bodyElement; - nsresult res = GetBodyElement(getter_AddRefs(bodyElement)); - if (NS_SUCCEEDED(res)) + nsCOMPtr bodyElement; + nsresult res = GetBodyElement(getter_AddRefs(bodyElement)); + if (NS_SUCCEEDED(res)) { if (!bodyElement) return NS_ERROR_NULL_POINTER; res = aSelection->Collapse(bodyElement,0); @@ -7508,10 +7866,6 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) ForceCompositionEnd(); - // wrap with txn batching, rules sniffing, and selection preservation code - nsAutoEditBatch batchIt(this); - nsAutoRules beginRulesSniffing(this, kOpSetTextProperty, nsIEditor::eNext, PR_TRUE); - // Get the selection nsCOMPtrselection; nsresult res = GetSelection(getter_AddRefs(selection)); @@ -7531,6 +7885,11 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) return NS_OK; } + // wrap with txn batching, rules sniffing, and selection preservation code + nsAutoEditBatch batchIt(this); + nsAutoRules beginRulesSniffing(this, kOpSetTextProperty, nsIEditor::eNext); + nsAutoSelectionReset selectionResetter(selection, this); + // get selection range enumerator nsCOMPtr enumerator; res = selection->GetEnumerator(getter_AddRefs(enumerator)); @@ -7548,6 +7907,10 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) nsCOMPtr range( do_QueryInterface(currentItem) ); + // adjust range to include any ancestors who's children are entirely selected + res = PromoteInlineRange(range); + if (NS_FAILED(res)) return res; + // check for easy case: both range endpoints in same text node nsCOMPtr startNode, endNode; res = range->GetStartParent(getter_AddRefs(startNode)); @@ -7556,6 +7919,9 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) if (NS_FAILED(res)) return res; if ((startNode == endNode) && IsTextNode(startNode)) { + // MOOSE: workaround for selection bug: + selection->ClearSelection(); + PRInt32 startOffset, endOffset; range->GetStartOffset(&startOffset); range->GetEndOffset(&endOffset); @@ -7600,12 +7966,18 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) if (NS_FAILED(res)) return res; node = do_QueryInterface(content); if (!node) return NS_ERROR_FAILURE; - isupports = do_QueryInterface(node); - arrayOfNodes->AppendElement(isupports); + if (IsEditable(node)) + { + isupports = do_QueryInterface(node); + arrayOfNodes->AppendElement(isupports); + } res = iter->Next(); if (NS_FAILED(res)) return res; } + // MOOSE: workaround for selection bug: + selection->ClearSelection(); + // now that we have the list, do the font size change on each node PRUint32 listCount; PRUint32 j; @@ -7622,7 +7994,7 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) // now check the start and end parents of the range to see if they need to // be seperately handled (they do if they are text nodes, due to how the // subtree iterator works - it will not have reported them). - if (IsTextNode(startNode)) + if (IsTextNode(startNode) && IsEditable(startNode)) { nsCOMPtr nodeAsText = do_QueryInterface(startNode); PRInt32 startOffset; @@ -7632,7 +8004,7 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, textLen); if (NS_FAILED(res)) return res; } - if (IsTextNode(endNode)) + if (IsTextNode(endNode) && IsEditable(endNode)) { nsCOMPtr nodeAsText = do_QueryInterface(endNode); PRInt32 endOffset; @@ -7753,108 +8125,284 @@ nsHTMLEditor::RelativeFontChangeOnNode( PRInt32 aSizeChange, return res; } +/////////////////////////////////////////////////////////////////////////// +// GetPriorHTMLSibling: returns the previous editable sibling, if there is +// one within the parent +// +nsresult +nsHTMLEditor::GetPriorHTMLSibling(nsIDOMNode *inNode, nsCOMPtr *outNode) +{ + if (!outNode || !inNode) return NS_ERROR_NULL_POINTER; + nsresult res = NS_OK; + *outNode = nsnull; + nsCOMPtr temp, node = do_QueryInterface(inNode); + + while (1) + { + res = node->GetPreviousSibling(getter_AddRefs(temp)); + if (NS_FAILED(res)) return res; + if (!temp) return NS_OK; // return null sibling + // if it's editable, we're done + if (IsEditable(temp)) break; + // otherwise try again + node = temp; + } + *outNode = temp; + return res; +} + + /////////////////////////////////////////////////////////////////////////// -// ReplaceContainer: replace inNode with a new node (outNode) which is contructed -// to be of type aNodeType. Put inNodes children into outNode. -// Callers responsibility to make sure inNode's children can -// go in outNode. +// GetPriorHTMLSibling: returns the previous editable sibling, if there is +// one within the parent. just like above routine but +// takes a parent/offset instead of a node. +// nsresult -nsHTMLEditor::ReplaceContainer(nsIDOMNode *inNode, - nsCOMPtr *outNode, - const nsString &aNodeType) +nsHTMLEditor::GetPriorHTMLSibling(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outNode) { - if (!inNode || !outNode) - return NS_ERROR_NULL_POINTER; - nsresult res; - nsCOMPtr parent; - PRInt32 offset; - res = GetNodeLocation(inNode, &parent, &offset); - if (NS_FAILED(res)) return res; - res = CreateNode(aNodeType, parent, offset, getter_AddRefs(*outNode)); - if (NS_FAILED(res)) return res; - PRBool bHasMoreChildren; - inNode->HasChildNodes(&bHasMoreChildren); - nsCOMPtr child; - offset = 0; - while (bHasMoreChildren) + if (!outNode || !inParent) return NS_ERROR_NULL_POINTER; + nsresult res = NS_OK; + *outNode = nsnull; + if (!inOffset) return NS_OK; // return null sibling if at offset zero + nsCOMPtr node = nsEditor::GetChildAt(inParent,inOffset-1); + if (IsEditable(node)) { - inNode->GetLastChild(getter_AddRefs(child)); - res = DeleteNode(child); - if (NS_FAILED(res)) return res; - res = InsertNode(child, *outNode, 0); - if (NS_FAILED(res)) return res; - inNode->HasChildNodes(&bHasMoreChildren); + *outNode = node; + return res; + } + // else + return GetPriorHTMLSibling(node, outNode); +} + + + +/////////////////////////////////////////////////////////////////////////// +// GetNextHTMLSibling: returns the next editable sibling, if there is +// one within the parent +// +nsresult +nsHTMLEditor::GetNextHTMLSibling(nsIDOMNode *inNode, nsCOMPtr *outNode) +{ + if (!outNode) return NS_ERROR_NULL_POINTER; + nsresult res = NS_OK; + *outNode = nsnull; + nsCOMPtr temp, node = do_QueryInterface(inNode); + + while (1) + { + res = node->GetNextSibling(getter_AddRefs(temp)); + if (NS_FAILED(res)) return res; + if (!temp) return NS_ERROR_FAILURE; + // if it's editable, we're done + if (IsEditable(temp)) break; + // otherwise try again + node = temp; + } + *outNode = temp; + return res; +} + + + +/////////////////////////////////////////////////////////////////////////// +// GetNextHTMLSibling: returns the next editable sibling, if there is +// one within the parent. just like above routine but +// takes a parent/offset instead of a node. +// +nsresult +nsHTMLEditor::GetNextHTMLSibling(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outNode) +{ + if (!outNode || !inParent) return NS_ERROR_NULL_POINTER; + nsresult res = NS_OK; + *outNode = nsnull; + nsCOMPtr node = nsEditor::GetChildAt(inParent,inOffset); + if (!node) return NS_OK; // return null sibling if no sibling + if (IsEditable(node)) + { + *outNode = node; + return res; + } + // else + return GetPriorHTMLSibling(node, outNode); +} + + + +/////////////////////////////////////////////////////////////////////////// +// GetPriorHTMLNode: returns the previous editable leaf node, if there is +// one within the +// +nsresult +nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode *inNode, nsCOMPtr *outNode) +{ + if (!outNode) return NS_ERROR_NULL_POINTER; + nsresult res = GetPriorNode(inNode, PR_TRUE, getter_AddRefs(*outNode)); + if (NS_FAILED(res)) return res; + + // if it's not in the body, then zero it out + if (*outNode && !nsHTMLEditUtils::InBody(*outNode)) + { + *outNode = nsnull; } - res = DeleteNode(inNode); return res; } /////////////////////////////////////////////////////////////////////////// -// RemoveContainer: remove inNode, reparenting it's children into their -// the parent of inNode -// +// GetPriorHTMLNode: same as above but takes {parent,offset} instead of node +// nsresult -nsHTMLEditor::RemoveContainer(nsIDOMNode *inNode) +nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outNode) { - if (!inNode) - return NS_ERROR_NULL_POINTER; - if (nsHTMLEditUtils::IsBody(inNode)) - return NS_ERROR_UNEXPECTED; - nsresult res; - nsCOMPtr parent; - PRInt32 offset; - res = GetNodeLocation(inNode, &parent, &offset); + if (!outNode) return NS_ERROR_NULL_POINTER; + nsresult res = GetPriorNode(inParent, inOffset, PR_TRUE, getter_AddRefs(*outNode)); if (NS_FAILED(res)) return res; - // loop through the child nodes of inNode and promote them - // into inNode's parent. - PRBool bHasMoreChildren; - inNode->HasChildNodes(&bHasMoreChildren); - nsCOMPtr child; - while (bHasMoreChildren) + // if it's not in the body, then zero it out + if (*outNode && !nsHTMLEditUtils::InBody(*outNode)) { - inNode->GetLastChild(getter_AddRefs(child)); - res = DeleteNode(child); - if (NS_FAILED(res)) return res; - res = InsertNode(child, parent, offset); - if (NS_FAILED(res)) return res; - inNode->HasChildNodes(&bHasMoreChildren); + *outNode = nsnull; } - res = DeleteNode(inNode); return res; } /////////////////////////////////////////////////////////////////////////// -// InsertContainerAbove: insert a new parent for inNode, returned in outNode, -// which is contructed to be of type aNodeType. outNode becomes -// a child of inNode's earlier parent. -// Callers responsibility to make sure inNode's can be child -// of outNode, and outNode can be child of old parent. +// GetNextHTMLNode: returns the previous editable leaf node, if there is +// one within the +// nsresult -nsHTMLEditor::InsertContainerAbove(nsIDOMNode *inNode, - nsCOMPtr *outNode, - const nsString &aNodeType) +nsHTMLEditor::GetNextHTMLNode(nsIDOMNode *inNode, nsCOMPtr *outNode) { - if (!inNode || !outNode) - return NS_ERROR_NULL_POINTER; - nsresult res; - nsCOMPtr parent; - PRInt32 offset; - res = GetNodeLocation(inNode, &parent, &offset); + if (!outNode) return NS_ERROR_NULL_POINTER; + nsresult res = GetNextNode(inNode, PR_TRUE, getter_AddRefs(*outNode)); if (NS_FAILED(res)) return res; - // make new parent, outNode - res = CreateNode(aNodeType, parent, offset, getter_AddRefs(*outNode)); - if (NS_FAILED(res)) return res; - - // put inNode in new parent, outNode - res = DeleteNode(inNode); - if (NS_FAILED(res)) return res; - res = InsertNode(inNode, *outNode, 0); - if (NS_FAILED(res)) return res; - - return NS_OK; + // if it's not in the body, then zero it out + if (*outNode && !nsHTMLEditUtils::InBody(*outNode)) + { + *outNode = nsnull; + } + return res; } + + +/////////////////////////////////////////////////////////////////////////// +// GetNHTMLextNode: same as above but takes {parent,offset} instead of node +// +nsresult +nsHTMLEditor::GetNextHTMLNode(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outNode) +{ + if (!outNode) return NS_ERROR_NULL_POINTER; + nsresult res = GetNextNode(inParent, inOffset, PR_TRUE, getter_AddRefs(*outNode)); + if (NS_FAILED(res)) return res; + + // if it's not in the body, then zero it out + if (*outNode && !nsHTMLEditUtils::InBody(*outNode)) + { + *outNode = nsnull; + } + return res; +} + + +nsresult +nsHTMLEditor::IsFirstEditableChild( nsIDOMNode *aNode, PRBool *aOutIsFirst) +{ + // check parms + if (!aOutIsFirst || !aNode) return NS_ERROR_NULL_POINTER; + + // init out parms + *aOutIsFirst = PR_FALSE; + + // find first editable child and compare it to aNode + nsCOMPtr parent, firstChild; + nsresult res = aNode->GetParentNode(getter_AddRefs(parent)); + if (NS_FAILED(res)) return res; + if (!parent) return NS_ERROR_FAILURE; + res = GetFirstEditableChild(parent, &firstChild); + if (NS_FAILED(res)) return res; + + *aOutIsFirst = (firstChild.get() == aNode); + return res; +} + + +nsresult +nsHTMLEditor::IsLastEditableChild( nsIDOMNode *aNode, PRBool *aOutIsLast) +{ + // check parms + if (!aOutIsLast || !aNode) return NS_ERROR_NULL_POINTER; + + // init out parms + *aOutIsLast = PR_FALSE; + + // find last editable child and compare it to aNode + nsCOMPtr parent, lastChild; + nsresult res = aNode->GetParentNode(getter_AddRefs(parent)); + if (NS_FAILED(res)) return res; + if (!parent) return NS_ERROR_FAILURE; + res = GetLastEditableChild(parent, &lastChild); + if (NS_FAILED(res)) return res; + + *aOutIsLast = (lastChild.get() == aNode); + return res; +} + + +nsresult +nsHTMLEditor::GetFirstEditableChild( nsIDOMNode *aNode, nsCOMPtr *aOutFirstChild) +{ + // check parms + if (!aOutFirstChild || !aNode) return NS_ERROR_NULL_POINTER; + + // init out parms + *aOutFirstChild = nsnull; + + // find first editable child + nsCOMPtr child; + nsresult res = aNode->GetFirstChild(getter_AddRefs(child)); + if (NS_FAILED(res)) return res; + + while (child && !IsEditable(child)) + { + nsCOMPtr tmp; + res = child->GetNextSibling(getter_AddRefs(tmp)); + if (NS_FAILED(res)) return res; + if (!tmp) return NS_ERROR_FAILURE; + child = tmp; + } + + *aOutFirstChild = child; + return res; +} + + +nsresult +nsHTMLEditor::GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr *aOutLastChild) +{ + // check parms + if (!aOutLastChild || !aNode) return NS_ERROR_NULL_POINTER; + + // init out parms + *aOutLastChild = nsnull; + + // find last editable child + nsCOMPtr child; + nsresult res = aNode->GetLastChild(getter_AddRefs(child)); + if (NS_FAILED(res)) return res; + + while (child && !IsEditable(child)) + { + nsCOMPtr tmp; + res = child->GetPreviousSibling(getter_AddRefs(tmp)); + if (NS_FAILED(res)) return res; + if (!tmp) return NS_ERROR_FAILURE; + child = tmp; + } + + *aOutLastChild = child; + return res; +} + diff --git a/mozilla/editor/base/nsHTMLEditor.h b/mozilla/editor/base/nsHTMLEditor.h index 75a3d30895e..a894e63796f 100644 --- a/mozilla/editor/base/nsHTMLEditor.h +++ b/mozilla/editor/base/nsHTMLEditor.h @@ -246,9 +246,6 @@ public: NS_IMETHOD GetFlags(PRUint32 *aFlags); NS_IMETHOD SetFlags(PRUint32 aFlags); - /** Inherited from nsEditor with special cases for some html nodes **/ - NS_IMETHOD InsertFormattingForNode(nsIDOMNode* aNode); - NS_IMETHOD Undo(PRUint32 aCount); NS_IMETHOD Redo(PRUint32 aCount); @@ -276,7 +273,7 @@ public: /** All editor operations which alter the doc should be followed * with a call to EndOperation, naming the action and direction */ - NS_IMETHOD EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection, PRBool aSetSelection); + NS_IMETHOD EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection); /** returns PR_TRUE if aParent can contain a child of type aTag */ PRBool CanContainTag(nsIDOMNode* aParent, const nsString &aTag); @@ -292,7 +289,7 @@ public: protected: - virtual void InitRules(); + NS_IMETHOD InitRules(); /** install the event listeners for the editor * used to be part of Init, but now broken out into a separate method @@ -336,6 +333,10 @@ protected: NS_IMETHOD TabInTable(PRBool inIsShift, PRBool *outHandled); NS_IMETHOD CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPtr *outBRNode, EDirection aSelect = eNone); + NS_IMETHOD JoeCreateBR(nsCOMPtr *aInOutParent, + PRInt32 *aInOutOffset, + nsCOMPtr *outBRNode, + EDirection aSelect); NS_IMETHOD InsertBR(nsCOMPtr *outBRNode); // Table Editing (implemented in nsTableEditor.cpp) @@ -582,17 +583,59 @@ protected: nsresult RelativeFontChangeOnNode( PRInt32 aSizeChange, nsIDOMNode *aNode); - /* helper routines for node/parent manipulations */ - nsresult ReplaceContainer(nsIDOMNode *inNode, nsCOMPtr *outNode, const nsString &aNodeType); - nsresult RemoveContainer(nsIDOMNode *inNode); - nsresult InsertContainerAbove(nsIDOMNode *inNode, nsCOMPtr *outNode, const nsString &aNodeType); + /* helper routines for inline style */ + nsresult SetInlinePropertyOnTextNode( nsIDOMCharacterData *aTextNode, + PRInt32 aStartOffset, + PRInt32 aEndOffset, + nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue); + nsresult SetInlinePropertyOnNode( nsIDOMNode *aNode, + nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue); + + nsresult PromoteInlineRange(nsIDOMRange *inRange); + nsresult SplitStyleAboveRange(nsIDOMRange *aRange, + nsIAtom *aProperty, + const nsString *aAttribute); + nsresult SplitStyleAbovePoint(nsCOMPtr *aNode, + PRInt32 *aOffset, + nsIAtom *aProperty, + const nsString *aAttribute); + nsresult RemoveStyleInside(nsIDOMNode *aNode, + nsIAtom *aProperty, + const nsString *aAttribute, + PRBool aChildrenOnly = PR_FALSE); + + PRBool HasAttr(nsIDOMNode *aNode, const nsString *aAttribute); + PRBool HasAttrVal(nsIDOMNode *aNode, const nsString *aAttribute, const nsString *aValue); + PRBool IsAtFrontOfNode(nsIDOMNode *aNode, PRInt32 aOffset); + PRBool IsAtEndOfNode(nsIDOMNode *aNode, PRInt32 aOffset); + PRBool IsOnlyAttribute(nsIDOMNode *aElement, const nsString *aAttribute); + PRBool HasMatchingAttributes(nsIDOMNode *aNode1, + nsIDOMNode *aNode2); + + nsresult GetPriorHTMLSibling(nsIDOMNode *inNode, nsCOMPtr *outNode); + nsresult GetPriorHTMLSibling(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outNode); + nsresult GetNextHTMLSibling(nsIDOMNode *inNode, nsCOMPtr *outNode); + nsresult GetNextHTMLSibling(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outNode); + nsresult GetPriorHTMLNode(nsIDOMNode *inNode, nsCOMPtr *outNode); + nsresult GetPriorHTMLNode(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outNode); + nsresult GetNextHTMLNode(nsIDOMNode *inNode, nsCOMPtr *outNode); + nsresult GetNextHTMLNode(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outNode); + + nsresult IsFirstEditableChild( nsIDOMNode *aNode, PRBool *aOutIsFirst); + nsresult IsLastEditableChild( nsIDOMNode *aNode, PRBool *aOutIsLast); + nsresult GetFirstEditableChild( nsIDOMNode *aNode, nsCOMPtr *aOutFirstChild); + nsresult GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr *aOutLastChild); // Data members protected: - TypeInState* mTypeInState; - nsEditRules* mRules; + TypeInState* mTypeInState; + nsCOMPtr mRules; nsCOMPtr mKeyListenerP; nsCOMPtr mMouseListenerP; nsCOMPtr mTextListenerP; diff --git a/mozilla/editor/base/nsTextEditRules.cpp b/mozilla/editor/base/nsTextEditRules.cpp index e3076cbd634..803d0cf1de9 100644 --- a/mozilla/editor/base/nsTextEditRules.cpp +++ b/mozilla/editor/base/nsTextEditRules.cpp @@ -51,6 +51,17 @@ static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID); return NS_OK; \ }; + +nsresult +NS_NewTextEditRules(nsIEditRules** aInstancePtrResult) +{ + nsTextEditRules * rules = new nsTextEditRules(); + if (rules) + return rules->QueryInterface(NS_GET_IID(nsIEditRules), (void**) aInstancePtrResult); + return NS_ERROR_OUT_OF_MEMORY; +} + + /******************************************************** * Constructor/Destructor ********************************************************/ @@ -62,6 +73,7 @@ nsTextEditRules::nsTextEditRules() , mLockRulesSniffing(PR_FALSE) , mTheAction(0) { + NS_INIT_REFCNT(); } nsTextEditRules::~nsTextEditRules() @@ -69,6 +81,14 @@ nsTextEditRules::~nsTextEditRules() // do NOT delete mEditor here. We do not hold a ref count to mEditor. mEditor owns our lifespan. } +/******************************************************** + * XPCOM Cruft + ********************************************************/ + +NS_IMPL_ADDREF(nsTextEditRules) +NS_IMPL_RELEASE(nsTextEditRules) +NS_IMPL_QUERY_INTERFACE1(nsTextEditRules, nsIEditRules) + /******************************************************** * Public methods @@ -156,7 +176,7 @@ nsTextEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection) NS_IMETHODIMP -nsTextEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection, PRBool aSetSelection) +nsTextEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) { if (mLockRulesSniffing) return NS_OK; diff --git a/mozilla/editor/base/nsTextEditRules.h b/mozilla/editor/base/nsTextEditRules.h index 1eae320847c..eac378ad4d5 100644 --- a/mozilla/editor/base/nsTextEditRules.h +++ b/mozilla/editor/base/nsTextEditRules.h @@ -42,17 +42,18 @@ * 2. Selection must not be explicitly set by the rule method. * Any manipulation of Selection must be done by the editor. */ -class nsTextEditRules : public nsEditRules +class nsTextEditRules : public nsIEditRules { public: - + NS_DECL_ISUPPORTS + nsTextEditRules(); virtual ~nsTextEditRules(); - // nsEditRules methods + // nsIEditRules methods NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags); NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection); - NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection, PRBool aSetSelection); + NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection); NS_IMETHOD WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled); NS_IMETHOD DidDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, nsresult aResult); NS_IMETHOD GetFlags(PRUint32 *aFlags); @@ -302,5 +303,25 @@ class nsAutoLockRulesSniffing +/*************************************************************************** + * stack based helper class for turning on/off the edit listener. + */ +class nsAutoLockListener +{ + public: + + nsAutoLockListener(PRBool *enabled) : mEnabled(enabled) + {if (mEnabled) { mOldState=*mEnabled; *mEnabled = PR_FALSE;}} + ~nsAutoLockListener() + {if (mEnabled) *mEnabled = mOldState;} + + protected: + PRBool *mEnabled; + PRBool mOldState; +}; + + +nsresult NS_NewTextEditRules(nsIEditRules** aInstancePtrResult); + #endif //nsTextEditRules_h__ diff --git a/mozilla/editor/libeditor/base/SplitElementTxn.cpp b/mozilla/editor/libeditor/base/SplitElementTxn.cpp index 513f8847f0a..41f1cdc7d2c 100644 --- a/mozilla/editor/libeditor/base/SplitElementTxn.cpp +++ b/mozilla/editor/libeditor/base/SplitElementTxn.cpp @@ -69,6 +69,8 @@ NS_IMETHODIMP SplitElementTxn::Do(void) NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewLeftNode)), "could not create element."); if (NS_FAILED(result)) return result; if (!mNewLeftNode) return NS_ERROR_NULL_POINTER; + mEditor->MarkNodeDirty(mExistingRightNode); + if (gNoisy) { printf(" created left node = %p\n", mNewLeftNode.get()); } // get the parent node @@ -80,9 +82,6 @@ NS_IMETHODIMP SplitElementTxn::Do(void) result = mEditor->SplitNodeImpl(mExistingRightNode, mOffset, mNewLeftNode, mParent); if (NS_SUCCEEDED(result) && mNewLeftNode) { - // Insert formatting whitespace for the new node: - mEditor->InsertFormattingForNode(mExistingRightNode); - nsCOMPtrselection; mEditor->GetSelection(getter_AddRefs(selection)); if (NS_FAILED(result)) return result; diff --git a/mozilla/editor/libeditor/base/nsEditRules.h b/mozilla/editor/libeditor/base/nsEditRules.h index b1dcfd5bc2c..55af338ef2c 100644 --- a/mozilla/editor/libeditor/base/nsEditRules.h +++ b/mozilla/editor/libeditor/base/nsEditRules.h @@ -23,6 +23,11 @@ #ifndef nsEditRules_h__ #define nsEditRules_h__ +#define NS_IEDITRULES_IID \ +{ /* a6cf911b-15b3-11d2-932e-00805f8add32 */ \ +0xa6cf911b, 0x15b3, 0x11d2, \ +{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32} } + class nsHTMLEditor; class nsIDOMSelection; @@ -40,20 +45,21 @@ class nsRulesInfo int action; }; -MOZ_DECL_CTOR_COUNTER(nsEditRules); - /*************************************************************************** * Interface of editing rules. * */ -class nsEditRules +class nsIEditRules : public nsISupports { public: - nsEditRules() { MOZ_COUNT_CTOR(nsEditRules); } - virtual ~nsEditRules() { MOZ_COUNT_DTOR(nsEditRules); } + static const nsIID& GetIID() { static nsIID iid = NS_IEDITRULES_IID; return iid; } + +//Interfaces for addref and release and queryinterface +//NOTE: Use NS_DECL_ISUPPORTS_INHERITED in any class inherited from nsIEditRules + NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags)=0; NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection)=0; - NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection, PRBool aSetSelection)=0; + NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)=0; NS_IMETHOD WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled)=0; NS_IMETHOD DidDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, nsresult aResult)=0; NS_IMETHOD GetFlags(PRUint32 *aFlags)=0; diff --git a/mozilla/editor/libeditor/base/nsEditor.cpp b/mozilla/editor/libeditor/base/nsEditor.cpp index f2da926e434..fd08d563bb5 100644 --- a/mozilla/editor/libeditor/base/nsEditor.cpp +++ b/mozilla/editor/libeditor/base/nsEditor.cpp @@ -144,13 +144,13 @@ PRInt32 nsEditor::gInstanceCount = 0; * { {startnode, startoffset} , {endnode, endoffset} } tuples. Cant store * ranges since dom gravity will possibly change the ranges. */ -nsSelectionState::nsSelectionState() {} +nsSelectionState::nsSelectionState() : mArray(), mLock(PR_FALSE) {} nsSelectionState::~nsSelectionState() { // free any items in the array SelRangeStore *item; - while (item = (SelRangeStore*)mArray.ElementAt(0)) + while ((item = (SelRangeStore*)mArray.ElementAt(0))) { delete item; mArray.RemoveElementAt(0); @@ -180,7 +180,7 @@ nsSelectionState::SaveSelection(nsIDOMSelection *aSel) // else if we have too many, delete them else if (rangeCount>arrayCount) { - while (item = (SelRangeStore*)mArray.ElementAt(rangeCount)) + while ((item = (SelRangeStore*)mArray.ElementAt(rangeCount))) { delete item; mArray.RemoveElementAt(rangeCount); @@ -208,7 +208,7 @@ nsSelectionState::RestoreSelection(nsIDOMSelection *aSel) PRInt32 i, arrayCount = mArray.Count(); SelRangeStore *item; - // clear ot cur selection + // clear out selection aSel->ClearSelection(); // set the selection ranges anew @@ -273,7 +273,334 @@ nsSelectionState::IsEqual(nsSelectionState *aSelState) return PR_TRUE; } +// notification routines used to update the saved selection state in response to +// document editing. +nsresult +nsSelectionState::SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition) +{ + if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... + if (!aParent) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + SelRangeStore *item; + + for (i=0; istartNode.get() == aParent) && (item->startOffset > aPosition)) + item->startOffset++; + if ((item->endNode.get() == aParent) && (item->endOffset > aPosition)) + item->endOffset++; + } + return NS_OK; +} + +nsresult +nsSelectionState::SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition) +{ + return SelAdjCreateNode(aParent, aPosition); +} + + +nsresult +nsSelectionState::SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset) +{ + if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... + if (!aNode) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + SelRangeStore *item; + + for (i=0; istartNode.get() == aParent) && (item->startOffset > aOffset)) + item->startOffset--; + if ((item->endNode.get() == aParent) && (item->endOffset > aOffset)) + item->endOffset--; + } + // MOOSE: also check inside of aNode, expensive. But in theory, we shouldn't + // actually hit this case in the usage i forsee for this. + return NS_OK; +} + + +nsresult +nsSelectionState::SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode) +{ + if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... + if (!aOldRightNode || !aNewLeftNode) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + nsCOMPtr parent; + PRInt32 offset; + nsresult result = nsEditor::GetNodeLocation(aOldRightNode, &parent, &offset); + if (NS_FAILED(result)) return result; + + // first part is same as inserting aNewLeftnode + result = SelAdjInsertNode(parent,offset); + if (NS_FAILED(result)) return result; + + // next step is to check for range enpoints inside aOldRightNode + SelRangeStore *item; + + for (i=0; istartNode.get() == aOldRightNode) + { + if (item->startOffset > aOffset) + { + item->startOffset -= aOffset; + } + else + { + item->startNode = aNewLeftNode; + } + } + if (item->endNode.get() == aOldRightNode) + { + if (item->endOffset > aOffset) + { + item->endOffset -= aOffset; + } + else + { + item->endNode = aNewLeftNode; + } + } + } + return NS_OK; +} + + +nsresult +nsSelectionState::SelAdjJoinNodes(nsIDOMNode *aLeftNode, + nsIDOMNode *aRightNode, + nsIDOMNode *aParent, + PRInt32 aOffset, + PRInt32 aOldLeftNodeLength) +{ + if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... + if (!aLeftNode || !aRightNode || !aParent) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + SelRangeStore *item; + + for (i=0; istartNode.get() == aParent) + { + if (item->startOffset > aOffset) + { + item->startOffset--; + } + else if (item->startOffset == aOffset) + { + // join keeps right hand node + item->startNode = aRightNode; + item->startOffset = aOldLeftNodeLength; + } + } + if (item->endNode.get() == aParent) + { + if (item->endOffset > aOffset) + { + item->endOffset--; + } + else if (item->endOffset == aOffset) + { + // join keeps right hand node + item->endNode = aRightNode; + item->endOffset = aOldLeftNodeLength; + } + } + // adjust endpoints in aRightNode + if (item->startNode.get() == aRightNode) + item->startOffset += aOldLeftNodeLength; + if (item->endNode.get() == aRightNode) + item->endOffset += aOldLeftNodeLength; + } + + return NS_OK; +} + + +nsresult +nsSelectionState::SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString) +{ + if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... + return NS_OK; +} + + +nsresult +nsSelectionState::SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength) +{ + if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc... + return NS_OK; +} + + +nsresult +nsSelectionState::WillReplaceContainer() +{ + if (mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_TRUE; + return NS_OK; +} + + +nsresult +nsSelectionState::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) +{ + if (!mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_FALSE; + + if (!aOriginalNode || !aNewNode) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + SelRangeStore *item; + + for (i=0; istartNode.get() == aOriginalNode) + item->startNode = aNewNode; + if (item->endNode.get() == aOriginalNode) + item->endNode = aNewNode; + } + return NS_OK; +} + + +nsresult +nsSelectionState::WillRemoveContainer() +{ + if (mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_TRUE; + return NS_OK; +} + + +nsresult +nsSelectionState::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen) +{ + if (!mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_FALSE; + + if (!aNode || !aParent) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + SelRangeStore *item; + + for (i=0; istartNode.get() == aNode) + { + item->startNode = aParent; + item->startOffset += aOffset; + } + if (item->endNode.get() == aNode) + { + item->endNode = aParent; + item->endOffset += aOffset; + } + if ((item->startNode.get() == aParent) && (item->startOffset > aOffset)) + item->startOffset += (PRInt32)aNodeOrigLen-1; + if ((item->endNode.get() == aParent) && (item->endOffset > aOffset)) + item->endOffset += (PRInt32)aNodeOrigLen-1; + } + return NS_OK; +} + + +nsresult +nsSelectionState::WillInsertContainer() +{ + if (mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_TRUE; + return NS_OK; +} + + +nsresult +nsSelectionState::DidInsertContainer() +{ + if (!mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_FALSE; + return NS_OK; +} + + +nsresult +nsSelectionState::WillMoveNode() +{ + if (mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_TRUE; + return NS_OK; +} + + +nsresult +nsSelectionState::DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset) +{ + if (!mLock) return NS_ERROR_UNEXPECTED; + mLock = PR_FALSE; + + if (!aOldParent || !aNewParent) return NS_ERROR_NULL_POINTER; + PRInt32 i, count = mArray.Count(); + if (!count) return NS_OK; + + SelRangeStore *item; + + for (i=0; istartNode.get() == aOldParent) && (item->startOffset > aOldOffset)) + item->startOffset--; + if ((item->endNode.get() == aOldParent) && (item->endOffset > aOldOffset)) + item->endOffset--; + + // and like an insert in aNewParent + if ((item->startNode.get() == aNewParent) && (item->startOffset > aNewOffset)) + item->startOffset++; + if ((item->endNode.get() == aNewParent) && (item->endOffset > aNewOffset)) + item->endOffset++; + } + return NS_OK; +} + + + +/*************************************************************************** + * helper class for nsSelectionState. SelRangeStore stores range endpoints. + */ nsresult SelRangeStore::StoreRange(nsIDOMRange *aRange) { @@ -301,8 +628,135 @@ nsresult SelRangeStore::GetRange(nsCOMPtr *outRange) return res; } +/*************************************************************************** + * another helper class for nsSelectionState. stack based class for doing + * Will/DidReplaceContainer() + */ + +class nsAutoReplaceContainerSelNotify +{ + private: + nsSelectionState *mSel; + nsIDOMNode *mOriginalNode; + nsIDOMNode *mNewNode; + + public: + nsAutoReplaceContainerSelNotify(nsSelectionState *aSelState, nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) : + mSel(aSelState) + ,mOriginalNode(aOriginalNode) + ,mNewNode(aNewNode) + { + if (mSel) mSel->WillReplaceContainer(); + } + + ~nsAutoReplaceContainerSelNotify() + { + if (mSel) mSel->DidReplaceContainer(mOriginalNode, mNewNode); + } +}; + + +/*************************************************************************** + * another helper class for nsSelectionState. stack based class for doing + * Will/DidRemoveContainer() + */ + +class nsAutoRemoveContainerSelNotify +{ + private: + nsSelectionState *mSel; + nsIDOMNode *mNode; + nsIDOMNode *mParent; + PRInt32 mOffset; + PRUint32 mNodeOrigLen; + + public: + nsAutoRemoveContainerSelNotify(nsSelectionState *aSelState, + nsIDOMNode *aNode, + nsIDOMNode *aParent, + PRInt32 aOffset, + PRUint32 aNodeOrigLen) : + mSel(aSelState) + ,mNode(aNode) + ,mParent(aParent) + ,mOffset(aOffset) + ,mNodeOrigLen(aNodeOrigLen) + { + if (mSel) mSel->WillRemoveContainer(); + } + + ~nsAutoRemoveContainerSelNotify() + { + if (mSel) mSel->DidRemoveContainer(mNode, mParent, mOffset, mNodeOrigLen); + } +}; + +/*************************************************************************** + * another helper class for nsSelectionState. stack based class for doing + * Will/DidInsertContainer() + */ + +class nsAutoInsertContainerSelNotify +{ + private: + nsSelectionState *mSel; + + public: + nsAutoInsertContainerSelNotify(nsSelectionState *aSelState) : + mSel(aSelState) + { + if (mSel) mSel->WillInsertContainer(); + } + + ~nsAutoInsertContainerSelNotify() + { + if (mSel) mSel->DidInsertContainer(); + } +}; + + +/*************************************************************************** + * another helper class for nsSelectionState. stack based class for doing + * Will/DidMoveNode() + */ + +class nsAutoMoveNodeSelNotify +{ + private: + nsSelectionState *mSel; + nsIDOMNode *mOldParent; + nsIDOMNode *mNewParent; + PRInt32 mOldOffset; + PRInt32 mNewOffset; + + public: + nsAutoMoveNodeSelNotify(nsSelectionState *aSelState, + nsIDOMNode *aOldParent, + PRInt32 aOldOffset, + nsIDOMNode *aNewParent, + PRInt32 aNewOffset) : + mSel(aSelState) + ,mOldParent(aOldParent) + ,mOldOffset(aOldOffset) + ,mNewParent(aNewParent) + ,mNewOffset(aNewOffset) + { + if (mSel) mSel->WillMoveNode(); + } + + ~nsAutoMoveNodeSelNotify() + { + if (mSel) mSel->DidMoveNode(mOldParent, mOldOffset, mNewParent, mNewOffset); + } +}; + + +//--------------------------------------------------------------------------- +// +// nsEditor: base editor class implementation +// +//--------------------------------------------------------------------------- -//class implementations are in order they are declared in nsEditor.h nsEditor::nsEditor() : mPresShellWeak(nsnull) @@ -312,6 +766,7 @@ nsEditor::nsEditor() , mPlaceHolderName(nsnull) , mPlaceHolderBatch(0) , mSelState(nsnull) +, mSavedSel(nsnull) , mShouldTxnSetSelection(PR_TRUE) , mBodyElement(nsnull) , mInIMEMode(PR_FALSE) @@ -361,39 +816,11 @@ nsEditor::~nsEditor() NS_IMPL_ADDREF(nsEditor) NS_IMPL_RELEASE(nsEditor) - - -NS_IMETHODIMP -nsEditor::QueryInterface(REFNSIID aIID, void** aInstancePtr) -{ - if (!aInstancePtr) - return NS_ERROR_NULL_POINTER; - - *aInstancePtr = nsnull; - if (aIID.Equals(NS_GET_IID(nsISupports))) { - nsIEditor *tmp = this; - nsISupports *tmp2 = tmp; - *aInstancePtr = (void*)tmp2; - NS_ADDREF_THIS(); - return NS_OK; - } - if (aIID.Equals(NS_GET_IID(nsIEditor))) { - *aInstancePtr = (void*)(nsIEditor*)this; - NS_ADDREF_THIS(); - return NS_OK; - } - if (aIID.Equals(NS_GET_IID(nsIEditorIMESupport))) { - *aInstancePtr = (void*)(nsIEditorIMESupport*)this; - NS_ADDREF_THIS(); - return NS_OK; - } - - return NS_NOINTERFACE; -} +NS_IMPL_QUERY_INTERFACE2(nsEditor, nsIEditor, nsIEditorIMESupport) #ifdef XP_MAC #pragma mark - -#pragma mark --- nsIEditorMethods --- +#pragma mark nsIEditorMethods #pragma mark - #endif @@ -1111,6 +1538,64 @@ nsEditor::RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute) } +// +// Insert a noneditable text node, e.g. formatting whitespace +// +nsresult +nsEditor::InsertNoneditableTextNode(nsIDOMNode* parent, PRInt32 offset, + nsString& aStr) +{ + nsAutoString textNodeTag; + nsresult res = GetTextNodeTag(textNodeTag); + if (NS_FAILED(res)) + return res; + + // Can't call CreateNode, because that will call us recursively. + // So duplicate what it does: + CreateElementTxn *txn; + res = CreateTxnForCreateElement(textNodeTag, parent, offset, &txn); + if (NS_FAILED(res)) + return res; + + res = Do(txn); + if (NS_FAILED(res)) + return res; + + // Now get the pointer to the node we just created ... + nsCOMPtr newNode; + res = txn->GetNewNode(getter_AddRefs(newNode)); + + // The transaction system (if any) has taken ownwership of txn + NS_IF_RELEASE(txn); + + if (NS_FAILED(res)) + return res; + nsCOMPtr newTextNode; + newTextNode = do_QueryInterface(newNode); + if (!newTextNode) + return NS_ERROR_UNEXPECTED; + + // ... and set its text. + return newTextNode->SetData(aStr); +} + +NS_IMETHODIMP +nsEditor::MarkNodeDirty(nsIDOMNode* aNode) +{ + // mark the node dirty. + nsCOMPtr element (do_QueryInterface(aNode)); + if (element) + element->SetAttribute("_moz_dirty", ""); + return NS_OK; +} + + +#ifdef XP_MAC +#pragma mark - +#pragma mark main node manipulation routines +#pragma mark - +#endif + NS_IMETHODIMP nsEditor::CreateNode(const nsString& aTag, nsIDOMNode * aParent, PRInt32 aPosition, @@ -1145,6 +1630,8 @@ NS_IMETHODIMP nsEditor::CreateNode(const nsString& aTag, // The transaction system (if any) has taken ownwership of txn NS_IF_RELEASE(txn); + if (mSavedSel) mSavedSel->SelAdjCreateNode(aParent, aPosition); + if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -1185,6 +1672,8 @@ NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode, // The transaction system (if any) has taken ownwership of txn NS_IF_RELEASE(txn); + if (mSavedSel) mSavedSel->SelAdjInsertNode(aParent, aPosition); + if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -1232,6 +1721,8 @@ nsEditor::SplitNode(nsIDOMNode * aNode, // The transaction system (if any) has taken ownwership of txn NS_IF_RELEASE(txn); + if (mSavedSel) mSavedSel->SelAdjSplitNode(aNode, aOffset, *aNewLeftNode); + if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -1249,113 +1740,25 @@ nsEditor::SplitNode(nsIDOMNode * aNode, } -// -// Insert a noneditable text node, e.g. formatting whitespace -// -nsresult -nsEditor::InsertNoneditableTextNode(nsIDOMNode* parent, PRInt32 offset, - nsString& aStr) -{ - nsAutoString textNodeTag; - nsresult res = GetTextNodeTag(textNodeTag); - if (NS_FAILED(res)) - return res; - - // Can't call CreateNode, because that will call us recursively. - // So duplicate what it does: - CreateElementTxn *txn; - res = CreateTxnForCreateElement(textNodeTag, parent, offset, &txn); - if (NS_FAILED(res)) - return res; - - res = Do(txn); - if (NS_FAILED(res)) - return res; - - // Now get the pointer to the node we just created ... - nsCOMPtr newNode; - res = txn->GetNewNode(getter_AddRefs(newNode)); - - // The transaction system (if any) has taken ownwership of txn - NS_IF_RELEASE(txn); - - if (NS_FAILED(res)) - return res; - nsCOMPtr newTextNode; - newTextNode = do_QueryInterface(newNode); - if (!newTextNode) - return NS_ERROR_UNEXPECTED; - - // ... and set its text. - return newTextNode->SetData(aStr); -} - -// -// Figure out what formatting needs to go with this node, and insert it. -// -NS_IMETHODIMP -nsEditor::InsertFormattingForNode(nsIDOMNode* aNode) -{ - nsresult res; - - // Don't insert any formatting unless it's an element node - PRUint16 nodeType; - res = aNode->GetNodeType(&nodeType); - if (NS_FAILED(res)) - return res; - if (nodeType != nsIDOMNode::ELEMENT_NODE) - return NS_OK; - - // Insert formatting only for block nodes - // (would it be better to insert for any non-inline node?) - PRBool block; - res = IsNodeBlock(aNode, block); - if (NS_FAILED(res)) - return res; - if (!block) - return NS_OK; - - nsCOMPtr parent; - res = aNode->GetParentNode(getter_AddRefs(parent)); - if (NS_FAILED(res)) - return res; - PRInt32 offset = GetIndexOf(parent, aNode); - -#ifdef DEBUG_akkana - //DumpContentTree(); - nsString namestr; - aNode->GetNodeName(namestr); - char* nodename = namestr.ToNewCString(); - printf("Inserting formatting for node <%s> at offset %d\n", - nodename, offset); - Recycle(nodename); -#endif /* DEBUG_akkana */ - - // - // XXX We don't yet have a real formatter. As a cheap stopgap, - // XXX just insert a newline before and after each newly inserted tag. - // - - nsAutoString str ("\n"); - - // After the close tag - //res = InsertNoneditableTextNode(parent, offset+1, str); - - // Before the open tag - res = InsertNoneditableTextNode(parent, offset, str); - - return res; -} NS_IMETHODIMP nsEditor::JoinNodes(nsIDOMNode * aLeftNode, nsIDOMNode * aRightNode, nsIDOMNode * aParent) { - PRInt32 i; + PRInt32 i, offset; + PRUint32 oldLeftNodeLen; nsIEditActionListener *listener; nsAutoRules beginRulesSniffing(this, kOpJoinNode, nsIEditor::ePrevious); + // remember some values; later used for saved selection updating. + // find the offset between the nodes to be joined. + nsresult result = GetChildOffset(aRightNode, aParent, offset); + if (NS_FAILED(result)) return result; + // find the number of children of the lefthand node + result = GetLengthOfDOMNode(aLeftNode, oldLeftNodeLen); + if (NS_FAILED(result)) return result; + if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -1367,7 +1770,7 @@ nsEditor::JoinNodes(nsIDOMNode * aLeftNode, } JoinElementTxn *txn; - nsresult result = CreateTxnForJoinNode(aLeftNode, aRightNode, &txn); + result = CreateTxnForJoinNode(aLeftNode, aRightNode, &txn); if (NS_SUCCEEDED(result)) { result = Do(txn); } @@ -1375,6 +1778,8 @@ nsEditor::JoinNodes(nsIDOMNode * aLeftNode, // The transaction system (if any) has taken ownwership of txn NS_IF_RELEASE(txn); + if (mSavedSel) mSavedSel->SelAdjJoinNodes(aLeftNode, aRightNode, aParent, offset, (PRInt32)oldLeftNodeLen); + if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -1391,10 +1796,15 @@ nsEditor::JoinNodes(nsIDOMNode * aLeftNode, NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement) { - PRInt32 i; + PRInt32 i, offset; + nsCOMPtr parent; nsIEditActionListener *listener; nsAutoRules beginRulesSniffing(this, kOpCreateNode, nsIEditor::ePrevious); + // save node location for selection updating code. + nsresult result = GetNodeLocation(aElement, &parent, &offset); + if (NS_FAILED(result)) return result; + if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -1406,7 +1816,7 @@ NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement) } DeleteElementTxn *txn; - nsresult result = CreateTxnForDeleteElement(aElement, &txn); + result = CreateTxnForDeleteElement(aElement, &txn); if (NS_SUCCEEDED(result)) { result = Do(txn); } @@ -1414,6 +1824,8 @@ NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement) // The transaction system (if any) has taken ownwership of txn NS_IF_RELEASE(txn); + if (mSavedSel) mSavedSel->SelAdjDeleteNode(aElement, parent, offset); + if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -1427,24 +1839,218 @@ NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement) return result; } - -NS_IMETHODIMP nsEditor::OutputToString(nsString& aOutputString, - const nsString& aFormatType, - PRUint32 aFlags) +/////////////////////////////////////////////////////////////////////////// +// ReplaceContainer: replace inNode with a new node (outNode) which is contructed +// to be of type aNodeType. Put inNodes children into outNode. +// Callers responsibility to make sure inNode's children can +// go in outNode. +nsresult +nsEditor::ReplaceContainer(nsIDOMNode *inNode, + nsCOMPtr *outNode, + const nsString &aNodeType, + const nsString *aAttribute, + const nsString *aValue) { - // these should be implemented by derived classes. - return NS_ERROR_NOT_IMPLEMENTED; + if (!inNode || !outNode) + return NS_ERROR_NULL_POINTER; + nsresult res; + nsCOMPtr parent; + PRInt32 offset; + res = GetNodeLocation(inNode, &parent, &offset); + if (NS_FAILED(res)) return res; + + // get our doc + nsCOMPtrdoc; + res = GetDocument(getter_AddRefs(doc)); + if (NS_FAILED(res)) return res; + if (!doc) return NS_ERROR_NULL_POINTER; + + // create new container + nsCOMPtr elem; + res = doc->CreateElement(aNodeType, getter_AddRefs(elem)); + if (NS_FAILED(res)) return res; + *outNode = do_QueryInterface(elem); + + // set attribute if needed + if (aAttribute && aValue && !aAttribute->IsEmpty()) + { + res = elem->SetAttribute(*aAttribute, *aValue); + if (NS_FAILED(res)) return res; + } + + // notify our internal selection state listener + nsAutoReplaceContainerSelNotify selStateNotify(mSavedSel, inNode, *outNode); + + // move children into new container + PRBool bHasMoreChildren; + inNode->HasChildNodes(&bHasMoreChildren); + nsCOMPtr child; + offset = 0; + while (bHasMoreChildren) + { + inNode->GetLastChild(getter_AddRefs(child)); + res = DeleteNode(child); + if (NS_FAILED(res)) return res; + res = InsertNode(child, *outNode, 0); + if (NS_FAILED(res)) return res; + inNode->HasChildNodes(&bHasMoreChildren); + } + + // delete old container + res = DeleteNode(inNode); + if (NS_FAILED(res)) return res; + + // insert new container into tree + res = InsertNode( *outNode, parent, offset); + return res; } -NS_IMETHODIMP nsEditor::OutputToStream(nsIOutputStream* aOutputStream, - const nsString& aFormatType, - const nsString* aCharsetOverride, - PRUint32 aFlags) +/////////////////////////////////////////////////////////////////////////// +// RemoveContainer: remove inNode, reparenting it's children into their +// the parent of inNode +// +nsresult +nsEditor::RemoveContainer(nsIDOMNode *inNode) { - // these should be implemented by derived classes. - return NS_ERROR_NOT_IMPLEMENTED; + if (!inNode) + return NS_ERROR_NULL_POINTER; + nsresult res; + nsCOMPtr parent; + PRInt32 offset; + PRUint32 nodeOrigLen; + + res = GetNodeLocation(inNode, &parent, &offset); + if (NS_FAILED(res)) return res; + + // loop through the child nodes of inNode and promote them + // into inNode's parent. + PRBool bHasMoreChildren; + inNode->HasChildNodes(&bHasMoreChildren); + nsCOMPtr nodeList; + res = inNode->GetChildNodes(getter_AddRefs(nodeList)); + if (NS_FAILED(res)) return res; + if (!nodeList) return NS_ERROR_NULL_POINTER; + nodeList->GetLength(&nodeOrigLen); + + // notify our internal selection state listener + nsAutoRemoveContainerSelNotify selNotify(mSavedSel, inNode, parent, offset, nodeOrigLen); + + nsCOMPtr child; + while (bHasMoreChildren) + { + inNode->GetLastChild(getter_AddRefs(child)); + res = DeleteNode(child); + if (NS_FAILED(res)) return res; + res = InsertNode(child, parent, offset); + if (NS_FAILED(res)) return res; + inNode->HasChildNodes(&bHasMoreChildren); + } + res = DeleteNode(inNode); + return res; } + +/////////////////////////////////////////////////////////////////////////// +// InsertContainerAbove: insert a new parent for inNode, returned in outNode, +// which is contructed to be of type aNodeType. outNode becomes +// a child of inNode's earlier parent. +// Callers responsibility to make sure inNode's can be child +// of outNode, and outNode can be child of old parent. +nsresult +nsEditor::InsertContainerAbove( nsIDOMNode *inNode, + nsCOMPtr *outNode, + const nsString &aNodeType, + const nsString *aAttribute, + const nsString *aValue) +{ + if (!inNode || !outNode) + return NS_ERROR_NULL_POINTER; + nsresult res; + nsCOMPtr parent; + PRInt32 offset; + res = GetNodeLocation(inNode, &parent, &offset); + if (NS_FAILED(res)) return res; + + // get our doc + nsCOMPtrdoc; + res = GetDocument(getter_AddRefs(doc)); + if (NS_FAILED(res)) return res; + if (!doc) return NS_ERROR_NULL_POINTER; + + // create new container + nsCOMPtr elem; + res = doc->CreateElement(aNodeType, getter_AddRefs(elem)); + if (NS_FAILED(res)) return res; + *outNode = do_QueryInterface(elem); + + // set attribute if needed + if (aAttribute && aValue && !aAttribute->IsEmpty()) + { + res = elem->SetAttribute(*aAttribute, *aValue); + if (NS_FAILED(res)) return res; + } + + // notify our internal selection state listener + nsAutoInsertContainerSelNotify selNotify(mSavedSel); + + // put inNode in new parent, outNode + res = DeleteNode(inNode); + if (NS_FAILED(res)) return res; + res = InsertNode(inNode, *outNode, 0); + if (NS_FAILED(res)) return res; + + // put new parent in doc + res = InsertNode(*outNode, parent, offset); + return res; +} + +/////////////////////////////////////////////////////////////////////////// +// MoveNode: move aNode to {aParent,aOffset} +nsresult +nsEditor::MoveNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset) +{ + if (!aNode || !aParent) + return NS_ERROR_NULL_POINTER; + nsresult res; + + nsCOMPtr oldParent; + PRInt32 oldOffset; + res = GetNodeLocation(aNode, &oldParent, &oldOffset); + + if (aOffset == -1) + { + PRUint32 unsignedOffset; + // magic value meaning "move to end of aParent" + res = GetLengthOfDOMNode(aParent, unsignedOffset); + if (NS_FAILED(res)) return res; + aOffset = (PRInt32)unsignedOffset; + } + + // dont do anything if it's already in right place + if ((aParent == oldParent.get()) && (oldOffset == aOffset)) return NS_OK; + + // notify our internal selection state listener + nsAutoMoveNodeSelNotify selNotify(mSavedSel, oldParent, oldOffset, aParent, aOffset); + + // need to adjust aOffset if we are moving aNode further along in it's current parent + if ((aParent == oldParent.get()) && (oldOffset < aOffset)) + { + aOffset--; // this is because when we delete aNode, it will make the offsets after it off by one + } + + // put aNode in new parent + res = DeleteNode(aNode); + if (NS_FAILED(res)) return res; + res = InsertNode(aNode, aParent, aOffset); + return res; +} + +#ifdef XP_MAC +#pragma mark - +#pragma mark action listener maintainance +#pragma mark - +#endif + NS_IMETHODIMP nsEditor::AddEditActionListener(nsIEditActionListener *aListener) { @@ -1529,6 +2135,29 @@ nsEditor::RemoveDocumentStateListener(nsIDocumentStateListener *aListener) } +#ifdef XP_MAC +#pragma mark - +#pragma mark misc +#pragma mark - +#endif + +NS_IMETHODIMP nsEditor::OutputToString(nsString& aOutputString, + const nsString& aFormatType, + PRUint32 aFlags) +{ + // these should be implemented by derived classes. + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsEditor::OutputToStream(nsIOutputStream* aOutputStream, + const nsString& aFormatType, + const nsString* aCharsetOverride, + PRUint32 aFlags) +{ + // these should be implemented by derived classes. + return NS_ERROR_NOT_IMPLEMENTED; +} + NS_IMETHODIMP nsEditor::DumpContentTree() { @@ -1584,7 +2213,7 @@ nsEditor::DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed) #ifdef XP_MAC #pragma mark - -#pragma mark --- nsIEditorIMESupport --- +#pragma mark nsIEditorIMESupport #pragma mark - #endif @@ -1731,7 +2360,7 @@ nsEditor::ForceCompositionEnd() } #ifdef XP_MAC #pragma mark - -#pragma mark --- public nsEditor methods --- +#pragma mark public nsEditor methods #pragma mark - #endif /* Non-interface, public methods */ @@ -1812,7 +2441,7 @@ nsEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection) /** All editor operations which alter the doc should be followed * with a call to EndOperation, naming the action and direction */ NS_IMETHODIMP -nsEditor::EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection, PRBool aSetSelection) +nsEditor::EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection) { return NS_OK; } @@ -1894,7 +2523,7 @@ nsEditor::CloneAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) #ifdef XP_MAC #pragma mark - -#pragma mark --- Protected and static methods --- +#pragma mark Protected and static methods #pragma mark - #endif @@ -1920,6 +2549,89 @@ nsresult nsEditor::GetTextNodeTag(nsString& aOutString) } +NS_IMETHODIMP nsEditor::JoeInsertTextImpl(const nsString& aStringToInsert, + nsCOMPtr *aInOutNode, + PRInt32 *aInOutOffset, + nsIDOMDocument *aDoc) +{ + // NOTE: caller *must* have already used nsAutoTxnsConserveSelection stack-based + // class to turn off txn selection updating. Caller also turned on rules sniffing + // if desired. + + if (!aInOutNode || !*aInOutNode || !aInOutOffset || !aDoc) return NS_ERROR_NULL_POINTER; + if (aStringToInsert.IsEmpty()) return NS_OK; + nsCOMPtr nodeAsText = do_QueryInterface(*aInOutNode); + PRInt32 offset = *aInOutOffset; + nsresult res; + if (nodeAsText) + { + // we are inserting text into an existing text node. + res = JoeInsertTextIntoTextNodeImpl(aStringToInsert, nodeAsText, offset); + if (NS_FAILED(res)) return res; + *aInOutOffset += aStringToInsert.Length(); + } + else + { + nsCOMPtr newNode; + // we are inserting text into a non-text node + // first we have to create a textnode (this also populates it with the text) + res = aDoc->CreateTextNode(aStringToInsert, getter_AddRefs(nodeAsText)); + if (NS_FAILED(res)) return res; + if (!nodeAsText) return NS_ERROR_NULL_POINTER; + newNode = do_QueryInterface(nodeAsText); + // then we insert it into the dom tree + res = InsertNode(newNode, *aInOutNode, offset); + if (NS_FAILED(res)) return res; + *aInOutNode = newNode; + *aInOutOffset = aStringToInsert.Length(); + } + return res; +} + + +NS_IMETHODIMP nsEditor::JoeInsertTextIntoTextNodeImpl(const nsString& aStringToInsert, + nsIDOMCharacterData *aTextNode, + PRInt32 aOffset) +{ + EditTxn *txn; + nsresult result = CreateTxnForInsertText(aStringToInsert, aTextNode, aOffset, (InsertTextTxn**)&txn); + if (NS_FAILED(result)) return result; + + // let listeners know whats up + PRInt32 i; + nsIEditActionListener *listener; + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->WillInsertText(aTextNode, aOffset, aStringToInsert); + } + } + + BeginUpdateViewBatch(); + result = Do(txn); + // The transaction system (if any) has taken ownwership of txns. + // aggTxn released at end of routine. + NS_IF_RELEASE(txn); + EndUpdateViewBatch(); + + // let listeners know what happened + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->DidInsertText(aTextNode, aOffset, aStringToInsert, result); + } + } + + return result; +} + + NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert) { // First delete the selection if needed @@ -2014,10 +2726,7 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert) newTextNode = do_QueryInterface(newNode); if (newTextNode) { - nsAutoString placeholderText(" "); - newTextNode->SetData(placeholderText); selection->Collapse(newNode, 0); - selection->Extend(newNode, 1); result = InsertTextImpl(aStringToInsert); // this really recurses, right? } } @@ -2327,7 +3036,7 @@ NS_IMETHODIMP nsEditor::CreateTxnForJoinNode(nsIDOMNode *aLeftNode, #ifdef XP_MAC #pragma mark - -#pragma mark --- nsEditor public static helper methods --- +#pragma mark nsEditor public static helper methods #pragma mark - #endif @@ -2361,9 +3070,9 @@ nsEditor::SplitNodeImpl(nsIDOMNode * aExistingRightNode, nsCOMPtr selStartNode, selEndNode; PRInt32 selStartOffset, selEndOffset; result = GetStartNodeAndOffset(selection, &selStartNode, &selStartOffset); - if (NS_FAILED(result)) return result; + if (NS_FAILED(result)) selStartNode = nsnull; // if selection is cleared, remember that result = GetEndNodeAndOffset(selection, &selEndNode, &selEndOffset); - if (NS_FAILED(result)) return result; + if (NS_FAILED(result)) selStartNode = nsnull; // if selection is cleared, remember that nsCOMPtr resultNode; result = aParent->InsertBefore(aNewLeftNode, aExistingRightNode, getter_AddRefs(resultNode)); @@ -2421,9 +3130,9 @@ nsEditor::SplitNodeImpl(nsIDOMNode * aExistingRightNode, // editor wants us to set selection at split point selection->Collapse(aNewLeftNode, aOffset); } - else + else if (selStartNode) { - // and adjust the selection if needed + // else adjust the selection if needed. if selStartNode is null, then there was no selection. // HACK: this is overly simplified - multi-range selections need more work than this if (selStartNode.get() == aExistingRightNode) { @@ -2479,9 +3188,11 @@ nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep, nsCOMPtr selStartNode, selEndNode, parent; PRInt32 selStartOffset, selEndOffset, joinOffset, keepOffset; result = GetStartNodeAndOffset(selection, &selStartNode, &selStartOffset); - if (NS_FAILED(result)) return result; + if (NS_FAILED(result)) selStartNode = nsnull; result = GetEndNodeAndOffset(selection, &selEndNode, &selEndOffset); - if (NS_FAILED(result)) return result; + if (NS_FAILED(result)) selStartNode = nsnull; + + PRUint32 firstNodeLength; nsCOMPtr leftNode = aNodeToJoin; if (aNodeToKeepIsFirst) leftNode = aNodeToKeep; @@ -2495,45 +3206,47 @@ nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep, // if selection endpoint is between the nodes, remember it as being // in the one that is going away instead. This simplifies later selection // adjustment logic at end of this method. - if (selStartNode == parent) + if (selStartNode) { - if (aNodeToKeepIsFirst) + if (selStartNode == parent) { - if ((selStartOffset > keepOffset) && (selStartOffset <= joinOffset)) + if (aNodeToKeepIsFirst) { - selStartNode = aNodeToJoin; - selStartOffset = 0; + if ((selStartOffset > keepOffset) && (selStartOffset <= joinOffset)) + { + selStartNode = aNodeToJoin; + selStartOffset = 0; + } + } + else + { + if ((selStartOffset > joinOffset) && (selStartOffset <= keepOffset)) + { + selStartNode = aNodeToJoin; + selStartOffset = firstNodeLength; + } } } - else + if (selEndNode == parent) { - if ((selStartOffset > joinOffset) && (selStartOffset <= keepOffset)) + if (aNodeToKeepIsFirst) { - selStartNode = aNodeToJoin; - selStartOffset = firstNodeLength; + if ((selEndOffset > keepOffset) && (selEndOffset <= joinOffset)) + { + selEndNode = aNodeToJoin; + selEndOffset = 0; + } + } + else + { + if ((selEndOffset > joinOffset) && (selEndOffset <= keepOffset)) + { + selEndNode = aNodeToJoin; + selEndOffset = firstNodeLength; + } } } } - if (selEndNode == parent) - { - if (aNodeToKeepIsFirst) - { - if ((selEndOffset > keepOffset) && (selEndOffset <= joinOffset)) - { - selEndNode = aNodeToJoin; - selEndOffset = 0; - } - } - else - { - if ((selEndOffset > joinOffset) && (selEndOffset <= keepOffset)) - { - selEndNode = aNodeToJoin; - selEndOffset = firstNodeLength; - } - } - } - // ok, ready to do join now. // if it's a text node, just shuffle around some text nsCOMPtr keepNodeAsText( do_QueryInterface(aNodeToKeep) ); @@ -2608,7 +3321,7 @@ nsEditor::JoinNodesImpl(nsIDOMNode * aNodeToKeep, // editor wants us to set selection at join point selection->Collapse(aNodeToKeep, firstNodeLength); } - else + else if (selStartNode) { // and adjust the selection if needed // HACK: this is overly simplified - multi-range selections need more work than this @@ -2652,34 +3365,12 @@ nsresult nsEditor::GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset) { NS_ASSERTION((aChild && aParent), "bad args"); - nsresult result = NS_ERROR_NULL_POINTER; - if (aChild && aParent) - { - nsCOMPtr childNodes; - result = aParent->GetChildNodes(getter_AddRefs(childNodes)); - if ((NS_SUCCEEDED(result)) && (childNodes)) - { - PRInt32 i=0; - for ( ; NS_SUCCEEDED(result); i++) - { - nsCOMPtr childNode; - result = childNodes->Item(i, getter_AddRefs(childNode)); - if ((NS_SUCCEEDED(result)) && (childNode)) - { - if (childNode.get()==aChild) - { - aOffset = i; - break; - } - } - else if (!childNode) - result = NS_ERROR_NULL_POINTER; - } - } - else if (!childNodes) - result = NS_ERROR_NULL_POINTER; - } - return result; + if (!aChild || !aParent) return NS_ERROR_NULL_POINTER; + nsCOMPtr content = do_QueryInterface(aParent); + nsCOMPtr cChild = do_QueryInterface(aChild); + if (!cChild || !content) return NS_ERROR_NULL_POINTER; + nsresult res = content->IndexOf(cChild, aOffset); + return res; } nsresult @@ -3221,11 +3912,11 @@ nsEditor::GetNextNode(nsIDOMNode *aCurrentNode, nsresult result; *aResultNode = nsnull; // if aCurrentNode has a right sibling, return that sibling's leftmost child (or itself if it has no children) - nsCOMPtr prevSibling; - result = aCurrentNode->GetNextSibling(getter_AddRefs(prevSibling)); - if ((NS_SUCCEEDED(result)) && prevSibling) + nsCOMPtr nextSibling; + result = aCurrentNode->GetNextSibling(getter_AddRefs(nextSibling)); + if ((NS_SUCCEEDED(result)) && nextSibling) { - result = GetLeftmostChild(prevSibling, aResultNode); + result = GetLeftmostChild(nextSibling, aResultNode); if (NS_FAILED(result)) { return result; } if (PR_FALSE==aEditableNode) { return result; @@ -3236,6 +3927,7 @@ nsEditor::GetNextNode(nsIDOMNode *aCurrentNode, else { // restart the search from the non-editable node we just found nsCOMPtr notEditableNode = do_QueryInterface(*aResultNode); + NS_IF_RELEASE(*aResultNode); return GetNextNode(notEditableNode, aEditableNode, aResultNode); } } @@ -3351,10 +4043,16 @@ nsEditor::TagCanContain(const nsString &aParentTag, nsIDOMNode* aChild) { nsAutoString childStringTag; - nsCOMPtr childElement = do_QueryInterface(aChild); - if (!childElement) return PR_FALSE; - - childElement->GetTagName(childStringTag); + if (IsTextNode(aChild)) + { + childStringTag = "__moz_text"; + } + else + { + nsCOMPtr childElement = do_QueryInterface(aChild); + if (!childElement) return PR_FALSE; + childElement->GetTagName(childStringTag); + } return TagCanContainTag(aParentTag, childStringTag); } @@ -3780,6 +4478,12 @@ nsEditor::GetBlockNodeParent(nsIDOMNode *aNode) nsCOMPtr tmp; nsCOMPtr p; + if (!aNode) + { + NS_NOTREACHED("null node passed to GetBlockNodeParent()"); + return PR_FALSE; + } + if (NS_FAILED(aNode->GetParentNode(getter_AddRefs(p)))) // no parent, ran off top of tree return tmp; @@ -4441,7 +5145,7 @@ nsEditor::SetShouldTxnSetSelection(PRBool aShould) #ifdef XP_MAC #pragma mark - -#pragma mark --- protected nsEditor methods --- +#pragma mark protected nsEditor methods #pragma mark - #endif @@ -4455,13 +5159,10 @@ nsEditor::DeleteSelectionImpl(EDirection aAction) PRInt32 i; nsIEditActionListener *listener; nsCOMPtrselection; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; res = CreateTxnForDeleteSelection(aAction, &txn); if (NS_FAILED(res)) return res; - res = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(res)) { - NS_IF_RELEASE(txn); - return res; - } nsAutoRules beginRulesSniffing(this, kOpDeleteSelection, aAction); if (NS_SUCCEEDED(res)) @@ -5024,6 +5725,8 @@ nsEditor::CreateRange(nsIDOMNode *aStartParent, PRInt32 aStartOffset, nsresult nsEditor::GetFirstNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aNode) { + // Note: this might return a node that is outside of the range. + // Use caqrefully. if (!aRange || !aNode) return NS_ERROR_NULL_POINTER; *aNode = nsnull; diff --git a/mozilla/editor/libeditor/base/nsEditor.h b/mozilla/editor/libeditor/base/nsEditor.h index 0ba74ad0e24..6868b7e09f4 100644 --- a/mozilla/editor/libeditor/base/nsEditor.h +++ b/mozilla/editor/libeditor/base/nsEditor.h @@ -100,11 +100,41 @@ class nsSelectionState nsresult RestoreSelection(nsIDOMSelection *aSel); PRBool IsCollapsed(); PRBool IsEqual(nsSelectionState *aSelState); - + void MakeEmpty(); + PRBool IsEmpty(); + + // editor selection gravity routines. Note that we can't always depend on + // DOM Range gravity to do what we want to the "real" selection. For instance, + // if you move a node, that corresponds to deleting it and reinserting it. + // DOM Range gravity will promote the selection out of the node on deletion, + // which is not what you want if you know you are reinserting it. + nsresult SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition); + nsresult SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition); + nsresult SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset); + nsresult SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode); + nsresult SelAdjJoinNodes(nsIDOMNode *aLeftNode, + nsIDOMNode *aRightNode, + nsIDOMNode *aParent, + PRInt32 aOffset, + PRInt32 aOldLeftNodeLength); + nsresult SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString); + nsresult SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength); + // the following gravity routines need will/did sandwiches, because the other gravity + // routines will be called inside of these sandwiches, but should be ignored. + nsresult WillReplaceContainer(); + nsresult DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode); + nsresult WillRemoveContainer(); + nsresult DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen); + nsresult WillInsertContainer(); + nsresult DidInsertContainer(); + nsresult WillMoveNode(); + nsresult DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset); + nsVoidArray mArray; + PRBool mLock; }; -/** implementation of an editor object. it will be the controler/focal point +/** implementation of an editor object. it will be the controller/focal point * for the main editor services. i.e. the GUIManager, publishing, transaction * manager, event interfaces. the idea for the event interfaces is to have them * delegate the actual commands to the editor independent of the XPFE implementation. @@ -237,7 +267,7 @@ public: NS_IMETHOD InsertNoneditableTextNode(nsIDOMNode* aParent, PRInt32 aOffset, nsString& aStr); - NS_IMETHOD InsertFormattingForNode(nsIDOMNode* aNode); + NS_IMETHOD MarkNodeDirty(nsIDOMNode* aNode); /* output */ @@ -274,8 +304,28 @@ public: NS_IMETHOD InsertTextImpl(const nsString& aStringToInsert); + NS_IMETHOD JoeInsertTextImpl(const nsString& aStringToInsert, + nsCOMPtr *aInOutNode, + PRInt32 *aInOutOffset, + nsIDOMDocument *aDoc); + NS_IMETHOD JoeInsertTextIntoTextNodeImpl(const nsString& aStringToInsert, + nsIDOMCharacterData *aTextNode, + PRInt32 aOffset); NS_IMETHOD DeleteSelectionImpl(EDirection aAction); + /* helper routines for node/parent manipulations */ + nsresult ReplaceContainer(nsIDOMNode *inNode, + nsCOMPtr *outNode, + const nsString &aNodeType, + const nsString *aAttribute = nsnull, + const nsString *aValue = nsnull); + nsresult RemoveContainer(nsIDOMNode *inNode); + nsresult InsertContainerAbove(nsIDOMNode *inNode, + nsCOMPtr *outNode, + const nsString &aNodeType, + const nsString *aAttribute = nsnull, + const nsString *aValue = nsnull); + nsresult MoveNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset); protected: @@ -423,7 +473,7 @@ public: /** All editor operations which alter the doc should be followed * with a call to EndOperation, naming the action and direction */ - NS_IMETHOD EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection, PRBool aSetSelection); + NS_IMETHOD EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection); /** return the string that represents text nodes in the content tree */ static nsresult GetTextNodeTag(nsString& aOutString); @@ -702,7 +752,8 @@ protected: nsWeakPtr mPlaceHolderTxn; // weak reference to placeholder for begin/end batch purposes nsIAtom *mPlaceHolderName; // name of placeholder transaction PRInt32 mPlaceHolderBatch; // nesting count for batching - nsSelectionState *mSelState; // saved selection state for placeholder txn batching + nsSelectionState *mSelState; // saved selection state for placeholder txn batching + nsSelectionState *mSavedSel; // cached selection for nsAutoSelectionReset PRBool mShouldTxnSetSelection; // turn off for conservative selection adjustment by txns nsCOMPtr mBodyElement; // cached body node // @@ -718,7 +769,6 @@ protected: nsCOMPtr mDocStateListeners; PRInt8 mDocDirtyState; // -1 = not initialized - nsWeakPtr mDocWeak; // weak reference to the nsIDOMDocument nsCOMPtr mDTD; @@ -726,6 +776,7 @@ protected: friend PRBool NSCanUnload(nsISupports* serviceMgr); friend class nsAutoTxnsConserveSelection; + friend class nsAutoSelectionReset; }; diff --git a/mozilla/editor/libeditor/base/nsEditorUtils.cpp b/mozilla/editor/libeditor/base/nsEditorUtils.cpp index 39f051b50b8..982337fb017 100644 --- a/mozilla/editor/libeditor/base/nsEditorUtils.cpp +++ b/mozilla/editor/libeditor/base/nsEditorUtils.cpp @@ -29,39 +29,28 @@ * nsAutoSelectionReset *****************************************************************************/ -nsAutoSelectionReset::nsAutoSelectionReset(nsIDOMSelection *aSel) +nsAutoSelectionReset::nsAutoSelectionReset(nsIDOMSelection *aSel, nsEditor *aEd) : +mSel(nsnull) +,mEd(nsnull) { - mInitialized = PR_FALSE; + if (!aSel || !aEd) return; // not much we can do, bail. + if (aEd->mSavedSel) return; // we already have initted mSavedSel, so this must be nested call. mSel = do_QueryInterface(aSel); + mEd = aEd; if (mSel) { - mSel->GetAnchorNode(getter_AddRefs(mStartNode)); - mSel->GetAnchorOffset(&mStartOffset); - mSel->GetFocusNode(getter_AddRefs(mEndNode)); - mSel->GetFocusOffset(&mEndOffset); - if (mStartNode && mEndNode) - mInitialized = PR_TRUE; + mEd->mSavedSel = new nsSelectionState(); + mEd->mSavedSel->SaveSelection(mSel); } } nsAutoSelectionReset::~nsAutoSelectionReset() { - if (mSel && mInitialized) + if (mSel && mEd->mSavedSel) // mSel will be null if this was nested call { - // make sure that mStartNode and mEndNode are still in a document - nsCOMPtr docStart; - nsresult res = mStartNode->GetOwnerDocument(getter_AddRefs(docStart)); - if (NS_SUCCEEDED(res) && docStart) - { - nsCOMPtr docEnd; - res = mEndNode->GetOwnerDocument(getter_AddRefs(docEnd)); - if (NS_SUCCEEDED(res) && (docStart == docEnd)) - { - // restore original selection - mSel->Collapse(mStartNode, mStartOffset); - mSel->Extend(mEndNode, mEndOffset); - } - } + mEd->mSavedSel->RestoreSelection(mSel); + delete mEd->mSavedSel; + mEd->mSavedSel = nsnull; } } diff --git a/mozilla/editor/libeditor/base/nsEditorUtils.h b/mozilla/editor/libeditor/base/nsEditorUtils.h index d288c4ed447..1e1c41e4189 100644 --- a/mozilla/editor/libeditor/base/nsEditorUtils.h +++ b/mozilla/editor/libeditor/base/nsEditorUtils.h @@ -69,18 +69,11 @@ class nsAutoSelectionReset private: /** ref-counted reference to the selection that we are supposed to restore */ nsCOMPtr mSel; - - /** PR_TRUE if this instance initialized itself correctly */ - PRBool mInitialized; - - nsCOMPtr mStartNode; - nsCOMPtr mEndNode; - PRInt32 mStartOffset; - PRInt32 mEndOffset; + nsEditor *mEd; // non-owning ref to nsEditor public: /** constructor responsible for remembering all state needed to restore aSel */ - nsAutoSelectionReset(nsIDOMSelection *aSel); + nsAutoSelectionReset(nsIDOMSelection *aSel, nsEditor *aEd); /** destructor restores mSel to its former state */ ~nsAutoSelectionReset(); @@ -93,16 +86,15 @@ class nsAutoRules { public: - nsAutoRules(nsEditor *ed, PRInt32 action, nsIEditor::EDirection aDirection, PRBool setSelection=PR_FALSE) : - mEd(ed), mAction(action), mDirection(aDirection), mSetSelection(setSelection) + nsAutoRules(nsEditor *ed, PRInt32 action, nsIEditor::EDirection aDirection) : + mEd(ed), mAction(action), mDirection(aDirection) {if (mEd) mEd->StartOperation(mAction, mDirection);} - ~nsAutoRules() {if (mEd) mEd->EndOperation(mAction, mDirection, mSetSelection);} + ~nsAutoRules() {if (mEd) mEd->EndOperation(mAction, mDirection);} protected: nsEditor *mEd; PRInt32 mAction; nsIEditor::EDirection mDirection; - PRBool mSetSelection; }; diff --git a/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp b/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp index ce278cb157c..a7e506eac97 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp @@ -62,21 +62,46 @@ enum kBothSibs = 3 }; +nsresult +NS_NewHTMLEditRules(nsIEditRules** aInstancePtrResult) +{ + nsHTMLEditRules * rules = new nsHTMLEditRules(); + if (rules) + return rules->QueryInterface(NS_GET_IID(nsIEditRules), (void**) aInstancePtrResult); + return NS_ERROR_OUT_OF_MEMORY; +} + /******************************************************** * Constructor/Destructor ********************************************************/ -nsHTMLEditRules::nsHTMLEditRules() -: nsTextEditRules() +nsHTMLEditRules::nsHTMLEditRules() : +mDocChangeRange(nsnull) +,mListenerEnabled(PR_TRUE) +,mUtilRange(nsnull) +,mBody(nsnull) +,mJoinOffset(0) { } nsHTMLEditRules::~nsHTMLEditRules() { // remove ourselves as a listener to edit actions - mEditor->RemoveEditActionListener(mListener); + // In the normal case, we have already been removed by + // ~nsHTMLEditor, in which case we will get an error here + // which we ignore. But this allows us to add the ability to + // switch rule sets on the fly if we want. + mEditor->RemoveEditActionListener(this); } +/******************************************************** + * XPCOM Cruft + ********************************************************/ + +NS_IMPL_ADDREF_INHERITED(nsHTMLEditRules, nsTextEditRules) +NS_IMPL_RELEASE_INHERITED(nsHTMLEditRules, nsTextEditRules) +NS_IMPL_QUERY_INTERFACE2(nsHTMLEditRules, nsIEditRules, nsIEditActionListener) + /******************************************************** * Public methods @@ -89,9 +114,15 @@ nsHTMLEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags) nsresult res = nsTextEditRules::Init(aEditor, aFlags); if (NS_FAILED(res)) return res; + // make a utility range for use by the listenter + res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange), + getter_AddRefs(mUtilRange)); + if (NS_FAILED(res)) return res; + // pass over document and add any needed mozBRs // first turn off undo mEditor->EnableUndo(PR_FALSE); + // set up mDocChangeRange to be whole doc nsCOMPtr bodyElem; nsCOMPtr bodyNode; @@ -111,15 +142,12 @@ nsHTMLEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags) res = AdjustSpecialBreaks(); if (NS_FAILED(res)) return res; } + // turn on undo mEditor->EnableUndo(PR_TRUE); - // make a listener - res = NS_NewEditListener(getter_AddRefs(mListener), mEditor, this); - if (NS_FAILED(res)) return res; - - // add it as a listener to edit actions - res = mEditor->AddEditActionListener(mListener); + // add ourselves as a listener to edit actions + res = mEditor->AddEditActionListener(this); return res; } @@ -150,7 +178,7 @@ nsHTMLEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection) NS_IMETHODIMP -nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection, PRBool aSetSelection) +nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) { if (mLockRulesSniffing) return NS_OK; @@ -174,16 +202,8 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection, PRB nsAutoTxnsConserveSelection dontSpazMySelection(mEditor); // expand the "changed doc range" as needed - // MOOSE: I don't do this if the aSetSelection param is true. This is because - // if aSetSelection is true, it means we are to set the selection to the changed - // document region when we are done. So I need to preserve mDocChangeRange as is. - // I should probably set up a parellel range for this purpose. - - if (!aSetSelection) - { - res = PromoteRange(mDocChangeRange, action); - if (NS_FAILED(res)) return res; - } + res = PromoteRange(mDocChangeRange, action); + if (NS_FAILED(res)) return res; // add in any needed
s, and remove any unneeded ones. res = AdjustSpecialBreaks(); @@ -241,12 +261,6 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection, PRB nsCOMPtr pres; mEditor->GetPresShell(getter_AddRefs(pres)); if (pres) pres->SetCaretEnabled(PR_TRUE); - - if (aSetSelection) - { - selection->ClearSelection(); - selection->AddRange(mDocChangeRange); - } } return res; @@ -379,23 +393,14 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction, if (!mEditor->IsTextNode(selNode) && !mEditor->CanContainTag(selNode, textTag)) return NS_ERROR_FAILURE; - // split any mailcites in the way - if ( (mTheAction != nsHTMLEditor::kOpInsertQuotation) && (mFlags & nsIHTMLEditor::eEditorMailMask) ) - { - nsCOMPtr citeNode; - PRInt32 newOffset; - res = GetTopEnclosingMailCite(selNode, &citeNode); + // take care of typeinstate issues + if (typeInState.IsAnySet()) + { // for every property that is set, insert a new inline style node + res = CreateStyleForInsertText(aSelection, typeInState); + if (NS_FAILED(res)) return res; + // refresh the (collapsed) selection location + res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset); if (NS_FAILED(res)) return res; - - if (citeNode) - { - res = mEditor->SplitNodeDeep(citeNode, selNode, selOffset, &newOffset); - if (NS_FAILED(res)) return res; - res = citeNode->GetParentNode(getter_AddRefs(selNode)); - if (NS_FAILED(res)) return res; - res = aSelection->Collapse(selNode, newOffset); - if (NS_FAILED(res)) return res; - } } // identify the block @@ -420,34 +425,104 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction, } else // aAction == kInsertText { - while (theString.Length()) + // we need to get the doc + nsCOMPtrdoc; + res = mEditor->GetDocument(getter_AddRefs(doc)); + if (NS_FAILED(res)) return res; + if (!doc) return NS_ERROR_NULL_POINTER; + + // find where we are + nsCOMPtr curNode = selNode; + PRInt32 curOffset = selOffset; + + // is our text going to be PREformatted? + // We remember this so that we know how to handle tabs. + PRBool isPRE; + res = mEditor->IsPreformatted(selNode, &isPRE); + if (NS_FAILED(res)) return res; + + // turn off the edit listener: we know how to + // build the "doc changed range" ourselves, and it's + // must faster to do it once here than to track all + // the changes one at a time. + nsAutoLockListener lockit(&mListenerEnabled); + + // dont spaz my selection in subtransactions + nsAutoTxnsConserveSelection dontSpazMySelection(mEditor); + nsAutoString partialString; + nsCOMPtr unused; + PRInt32 pos; + + // for efficiency, break out the pre case seperately. This is because + // its a lot cheaper to search the input string for only newlines than + // it is to search for both tabs and newlines. + if (isPRE) { - nsString partialString; - PRInt32 pos = theString.FindCharInSet(specialChars); - // if first char is special, then use just it - if (pos == 0) pos = 1; - if (pos == -1) pos = theString.Length(); - theString.Left(partialString, pos); - theString.Cut(0, pos); - // is it a tab? - if (partialString == "\t" ) + char newlineChar = '\n'; + while (theString.Length()) { - res = InsertTab(aSelection,outString); + pos = theString.FindChar(newlineChar); + // if first char is newline, then use just it + if (pos == 0) pos = 1; + if (pos == -1) pos = theString.Length(); + theString.Left(partialString, pos); + theString.Cut(0, pos); + // is it a return? + if (partialString == "\n") + { + res = mEditor->JoeCreateBR(&curNode, &curOffset, &unused, nsIEditor::eNone); + } + else + { + res = mEditor->JoeInsertTextImpl(partialString, &curNode, &curOffset, doc); + } if (NS_FAILED(res)) return res; - res = DoTextInsertion(aSelection, &bCancel, outString, typeInState); } - // is it a return? - else if (partialString == "\n") - { - res = mEditor->InsertBreak(); - } - else - { - res = DoTextInsertion(aSelection, &bCancel, &partialString, typeInState); - } - if (NS_FAILED(res)) return res; - pos = theString.FindCharInSet(specialChars); } + else + { + char specialChars[] = {'\t','\n',0}; + nsAutoString tabString = " "; + while (theString.Length()) + { + pos = theString.FindCharInSet(specialChars); + // if first char is special, then use just it + if (pos == 0) pos = 1; + if (pos == -1) pos = theString.Length(); + theString.Left(partialString, pos); + theString.Cut(0, pos); + // is it a tab? + if (partialString == "\t") + { + partialString = " "; + res = mEditor->JoeInsertTextImpl(tabString, &curNode, &curOffset, doc); + } + // is it a return? + else if (partialString == "\n") + { + res = mEditor->JoeCreateBR(&curNode, &curOffset, &unused, nsIEditor::eNone); + } + else + { + res = mEditor->JoeInsertTextImpl(partialString, &curNode, &curOffset, doc); + } + if (NS_FAILED(res)) return res; + } + } + if (curNode) aSelection->Collapse(curNode, curOffset); + // manually update the doc changed range so that AfterEdit will clean up + // the correct portion of the document. + if (!mDocChangeRange) + { + res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange), + getter_AddRefs(mDocChangeRange)); + if (NS_FAILED(res)) return res; + if (!mDocChangeRange) return NS_ERROR_NULL_POINTER; + } + res = mDocChangeRange->SetStart(selNode, selOffset); + if (NS_FAILED(res)) return res; + res = mDocChangeRange->SetEnd(curNode, curOffset); + if (NS_FAILED(res)) return res; } return res; } @@ -993,9 +1068,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, nsAutoString blockType("ul"); if (aOrdered) blockType = "ol"; - - nsAutoSelectionReset selectionResetter(aSelection); - + PRBool outMakeEmpty; res = ShouldMakeEmptyBlock(aSelection, &blockType, &outMakeEmpty); if (NS_FAILED(res)) return res; @@ -1040,6 +1113,8 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, *aHandled = PR_TRUE; + nsAutoSelectionReset selectionResetter(aSelection, mEditor); + nsCOMPtr arrayOfRanges; res = GetPromotedRanges(aSelection, &arrayOfRanges, kMakeList); if (NS_FAILED(res)) return res; @@ -1226,9 +1301,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRUint32 listItemLen; res = mEditor->GetLengthOfDOMNode(prevListItem, listItemLen); if (NS_FAILED(res)) return res; - res = mEditor->DeleteNode(curNode); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(curNode, prevListItem, listItemLen); + res = mEditor->MoveNode(curNode, prevListItem, listItemLen); if (NS_FAILED(res)) return res; } else @@ -1258,9 +1331,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRUint32 listLen; res = mEditor->GetLengthOfDOMNode(curList, listLen); if (NS_FAILED(res)) return res; - res = mEditor->DeleteNode(listItem); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(listItem, curList, listLen); + res = mEditor->MoveNode(listItem, curList, listLen); if (NS_FAILED(res)) return res; } } @@ -1283,7 +1354,7 @@ nsHTMLEditRules::WillRemoveList(nsIDOMSelection *aSelection, nsAutoString blockType("ul"); if (aOrdered) blockType = "ol"; - nsAutoSelectionReset selectionResetter(aSelection); + nsAutoSelectionReset selectionResetter(aSelection, mEditor); nsCOMPtr arrayOfRanges; nsresult res = GetPromotedRanges(aSelection, &arrayOfRanges, kMakeList); @@ -1386,7 +1457,7 @@ nsHTMLEditRules::WillMakeBasicBlock(nsIDOMSelection *aSelection, if (makeEmpty) return res; // just insert a new empty block // else it's not that easy... - nsAutoSelectionReset selectionResetter(aSelection); + nsAutoSelectionReset selectionResetter(aSelection, mEditor); *aHandled = PR_TRUE; nsCOMPtr arrayOfRanges; @@ -1418,7 +1489,7 @@ nsHTMLEditRules::WillIndent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aCancel = PR_FALSE; *aHandled = PR_TRUE; - nsAutoSelectionReset selectionResetter(aSelection); + nsAutoSelectionReset selectionResetter(aSelection, mEditor); // convert the selection ranges into "promoted" selection ranges: // this basically just expands the range to include the immediate @@ -1475,9 +1546,7 @@ nsHTMLEditRules::WillIndent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool PRUint32 listLen; res = mEditor->GetLengthOfDOMNode(curList, listLen); if (NS_FAILED(res)) return res; - res = mEditor->DeleteNode(curNode); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(curNode, curList, listLen); + res = mEditor->MoveNode(curNode, curList, listLen); if (NS_FAILED(res)) return res; } @@ -1509,9 +1578,7 @@ nsHTMLEditRules::WillIndent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool PRUint32 quoteLen; res = mEditor->GetLengthOfDOMNode(curQuote, quoteLen); if (NS_FAILED(res)) return res; - res = mEditor->DeleteNode(curNode); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(curNode, curQuote, quoteLen); + res = mEditor->MoveNode(curNode, curQuote, quoteLen); if (NS_FAILED(res)) return res; } } @@ -1527,7 +1594,7 @@ nsHTMLEditRules::WillOutdent(nsIDOMSelection *aSelection, PRBool *aCancel, PRBoo *aCancel = PR_FALSE; *aHandled = PR_TRUE; - nsAutoSelectionReset selectionResetter(aSelection); + nsAutoSelectionReset selectionResetter(aSelection, mEditor); nsresult res = NS_OK; // convert the selection ranges into "promoted" selection ranges: @@ -1796,7 +1863,7 @@ nsHTMLEditRules::WillAlign(nsIDOMSelection *aSelection, return res; } - nsAutoSelectionReset selectionResetter(aSelection); + nsAutoSelectionReset selectionResetter(aSelection, mEditor); // convert the selection ranges into "promoted" selection ranges: // this basically just expands the range to include the immediate @@ -1879,9 +1946,7 @@ nsHTMLEditRules::WillAlign(nsIDOMSelection *aSelection, PRUint32 listLen; res = mEditor->GetLengthOfDOMNode(curDiv, listLen); if (NS_FAILED(res)) return res; - res = mEditor->DeleteNode(curNode); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(curNode, curDiv, listLen); + res = mEditor->MoveNode(curNode, curDiv, listLen); if (NS_FAILED(res)) return res; } @@ -1989,9 +2054,7 @@ nsHTMLEditRules::AlignTableCellContents(nsIDOMNode *aNode, const nsString *align // tuck the children into the end of the active div while (lastChild && (lastChild != divNode)) { - res = mEditor->DeleteNode(lastChild); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(lastChild, divNode, 0); + res = mEditor->MoveNode(lastChild, divNode, 0); if (NS_FAILED(res)) return res; res = GetLastEditableChild(aNode, &lastChild); if (NS_FAILED(res)) return res; @@ -2250,7 +2313,7 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt } // else not kInsertText. In this case we want to see if we should - // grab any adjacent inlline nodes and/or parents and other ancestors + // grab any adjacent inline nodes and/or parents and other ancestors if (nsHTMLEditUtils::IsBody(aNode)) { // we cant go any higher @@ -2428,7 +2491,7 @@ nsHTMLEditRules::GetPromotedRanges(nsIDOMSelection *inSelection, /////////////////////////////////////////////////////////////////////////// -// PromoteRange: expand a range to include any parentsfor which all +// PromoteRange: expand a range to include any parents for which all // editable children are already in range. // nsresult @@ -2449,7 +2512,7 @@ nsHTMLEditRules::PromoteRange(nsIDOMRange *inRange, res = inRange->GetEndOffset(&endOffset); if (NS_FAILED(res)) return res; - // make a new adjusted range to represent the appropriate block content + // make a new adjusted range to represent the appropriate block content. // this is tricky. the basic idea is to push out the range endpoints // to truly enclose the blocks that we will affect @@ -2878,9 +2941,7 @@ nsHTMLEditRules::ReturnInListItem(nsIDOMSelection *aSelection, if (nsHTMLEditUtils::IsList(listparent)) //in a sublist { // if so, move this list item out of this list and into the grandparent list - res = mEditor->DeleteNode(aListItem); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(aListItem,listparent,offset+1); + res = mEditor->MoveNode(aListItem,listparent,offset+1); if (NS_FAILED(res)) return res; res = aSelection->Collapse(aListItem,0); } @@ -3185,9 +3246,7 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString PRUint32 blockLen; res = mEditor->GetLengthOfDOMNode(curBlock, blockLen); if (NS_FAILED(res)) return res; - res = mEditor->DeleteNode(curNode); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(curNode, curBlock, blockLen); + res = mEditor->MoveNode(curNode, curBlock, blockLen); if (NS_FAILED(res)) return res; } } @@ -3227,9 +3286,7 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft, // to after the 'left' one if (parent != rightParent) { - res = mEditor->DeleteNode(aNodeRight); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(aNodeRight, parent, parOffset); + res = mEditor->MoveNode(aNodeRight, parent, parOffset); if (NS_FAILED(res)) return res; } @@ -3380,7 +3437,7 @@ nsHTMLEditRules::AdjustWhitespace(nsIDOMSelection *aSelection) PRUint32 nodeCount,j; nsresult res; - nsAutoSelectionReset selectionResetter(aSelection); + nsAutoSelectionReset selectionResetter(aSelection, mEditor); // special case for mDocChangeRange entirely in one text node. // This is an efficiency hack for normal typing in the editor. @@ -3492,27 +3549,30 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirect return NS_OK; // we LIKE it when we are in a text node. that RULZ // do we need to insert a special mozBR? We do if we are: - // 1) in a collapsed selection AND - // 2) after a normal (non-moz) br AND - // 3) that br is the last editable node in it's block AND - // 4) that block is same block where selection is + // 1) that block is same block where selection is AND + // 2) in a collapsed selection AND + // 3) after a normal (non-moz) br AND + // 4) that br is the last editable node in it's block + nsCOMPtr nearNode; res = GetPriorHTMLNode(selNode, selOffset, &nearNode); if (NS_FAILED(res)) return res; - if (nearNode && nsHTMLEditUtils::IsBreak(nearNode) - && !nsHTMLEditUtils::IsMozBR(nearNode)) + if (!nearNode) return res; + + // is nearNode also a descendant of same block? + nsCOMPtr block, nearBlock; + if (mEditor->IsBlockNode(selNode)) block = selNode; + else block = mEditor->GetBlockNodeParent(selNode); + nearBlock = mEditor->GetBlockNodeParent(nearNode); + if (block == nearBlock) { - PRBool bIsLast; - res = IsLastEditableChild(nearNode, &bIsLast); - if (NS_FAILED(res)) return res; - if (bIsLast) + if (nearNode && nsHTMLEditUtils::IsBreak(nearNode) + && !nsHTMLEditUtils::IsMozBR(nearNode)) { - // is nearNode also a descendant of same block? - nsCOMPtr block, nearBlock; - if (mEditor->IsBlockNode(selNode)) block = selNode; - else block = mEditor->GetBlockNodeParent(selNode); - nearBlock = mEditor->GetBlockNodeParent(nearNode); - if (block == nearBlock) + PRBool bIsLast; + res = IsLastEditableChild(nearNode, &bIsLast); + if (NS_FAILED(res)) return res; + if (bIsLast) { // need to insert special moz BR. Why? Because if we don't // the user will see no new line for the break. Also, things @@ -3527,31 +3587,31 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirect res = aSelection->Collapse(selNode,selOffset); if (NS_FAILED(res)) return res; } - } - else - { - // ok, the br inst the last child. - // the br might be right in front of a new block (ie,: - // text
  1. list item
) - // in this case we also need moz-br. - nsCOMPtr nextNode; - res = GetNextHTMLNode(nearNode, &nextNode); - if (NS_FAILED(res)) return res; - res = GetNextHTMLSibling(nearNode, &nextNode); - if (NS_FAILED(res)) return res; - if (nextNode && mEditor->IsBlockNode(nextNode)) + else { - // need to insert special moz BR. Why? Because if we don't - // the user will see no new line for the break. - nsCOMPtr brNode; - res = CreateMozBR(selNode, selOffset, &brNode); + // ok, the br inst the last child. + // the br might be right in front of a new block (ie,: + // text
  1. list item
) + // in this case we also need moz-br. + nsCOMPtr nextNode; + res = GetNextHTMLNode(nearNode, &nextNode); if (NS_FAILED(res)) return res; - res = nsEditor::GetNodeLocation(brNode, &selNode, &selOffset); - if (NS_FAILED(res)) return res; - // selection stays *before* moz-br, sticking to it - aSelection->SetHint(PR_TRUE); - res = aSelection->Collapse(selNode,selOffset); + res = GetNextHTMLSibling(nearNode, &nextNode); if (NS_FAILED(res)) return res; + if (nextNode && mEditor->IsBlockNode(nextNode)) + { + // need to insert special moz BR. Why? Because if we don't + // the user will see no new line for the break. + nsCOMPtr brNode; + res = CreateMozBR(selNode, selOffset, &brNode); + if (NS_FAILED(res)) return res; + res = nsEditor::GetNodeLocation(brNode, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + // selection stays *before* moz-br, sticking to it + aSelection->SetHint(PR_TRUE); + res = aSelection->Collapse(selNode,selOffset); + if (NS_FAILED(res)) return res; + } } } } @@ -3841,6 +3901,7 @@ nsHTMLEditRules::DoTextNodeWhitespace(nsIDOMCharacterData *aTextNode, PRInt32 aS // runStart to runEnd is a run of whitespace nsAutoString runStr, newStr; tempString.Mid(runStr, runStart, runEnd-runStart); + res = ConvertWhitespace(runStr, newStr); if (NS_FAILED(res)) return res; @@ -3885,7 +3946,10 @@ nsHTMLEditRules::ConvertWhitespace(const nsString & inString, nsString & outStri outString = ""; return NS_OK; case 1: - outString = " "; + if (inString == "\n") // a bit of a hack: don't convert single newlines that + outString = "\n"; // dont have whitespace adjacent. This is to preserve + else // html source formatting to some degree. + outString = " "; return NS_OK; case 2: outString = (PRUnichar)nbsp; @@ -3964,9 +4028,7 @@ nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList) if (!bIsFirstListItem) parOffset++; - res = mEditor->DeleteNode(curNode); - if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(curNode, curParPar, parOffset); + res = mEditor->MoveNode(curNode, curParPar, parOffset); if (NS_FAILED(res)) return res; // unwrap list item contents if they are no longer in a list @@ -4108,318 +4170,9 @@ nsHTMLEditRules::UpdateDocChangeRange(nsIDOMRange *aRange) } -#ifdef XP_MAC -#pragma mark - -#pragma mark --- nsIEditActionListener methods --- -#pragma mark - -#endif - -/* Factory for edit listener object */ -nsresult NS_NewEditListener(nsIEditActionListener **aResult, - nsHTMLEditor *htmlEditor, - nsHTMLEditRules *htmlRules) -{ - if (!aResult) return NS_ERROR_NULL_POINTER; - - *aResult = new nsHTMLEditListener(htmlEditor, htmlRules); - if (!*aResult) return NS_ERROR_OUT_OF_MEMORY; - - NS_ADDREF(*aResult); - return NS_OK; -} - -NS_IMPL_ADDREF(nsHTMLEditListener) -NS_IMPL_RELEASE(nsHTMLEditListener) -NS_IMPL_QUERY_INTERFACE1(nsHTMLEditListener, nsIEditActionListener) - -nsHTMLEditListener::nsHTMLEditListener(nsHTMLEditor *htmlEditor, - nsHTMLEditRules *htmlRules) : - mEditor(htmlEditor), - mRules(htmlRules) -{ - NS_INIT_REFCNT(); - - NS_PRECONDITION(mEditor, "null editor!"); - NS_PRECONDITION(mRules, "null edit rules!"); - - nsCOMPtr bodyElement; - nsresult res = mEditor->GetBodyElement(getter_AddRefs(bodyElement)); - NS_POSTCONDITION(NS_SUCCEEDED(res), "no body element found for edit listener"); - NS_POSTCONDITION(bodyElement, "no body element found for edit listener"); - mBody = do_QueryInterface(bodyElement); -} - -nsHTMLEditListener::~nsHTMLEditListener() -{} - -NS_IMETHODIMP -nsHTMLEditListener::WillCreateNode(const nsString& aTag, nsIDOMNode *aParent, PRInt32 aPosition) -{ - return NS_OK; -} - -NS_IMETHODIMP -nsHTMLEditListener::DidCreateNode(const nsString& aTag, - nsIDOMNode *aNode, - nsIDOMNode *aParent, - PRInt32 aPosition, - nsresult aResult) -{ - nsCOMPtr range; - // assumption that Join keeps the righthand node - nsresult res = MakeRangeFromNode(aNode, &range); - if (NS_FAILED(res)) return res; - if (range) - { - res = mRules->UpdateDocChangeRange(range); - } - return res; -} - - -NS_IMETHODIMP -nsHTMLEditListener::WillInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition) -{ - return NS_OK; -} - - -NS_IMETHODIMP -nsHTMLEditListener::DidInsertNode(nsIDOMNode *aNode, - nsIDOMNode *aParent, - PRInt32 aPosition, - nsresult aResult) -{ - nsCOMPtr range; - // assumption that Join keeps the righthand node - nsresult res = MakeRangeFromNode(aNode, &range); - if (NS_FAILED(res)) return res; - if (range) - { - res = mRules->UpdateDocChangeRange(range); - } - return res; -} - - -NS_IMETHODIMP -nsHTMLEditListener::WillDeleteNode(nsIDOMNode *aChild) -{ - nsCOMPtr range; - nsresult res = MakeRangeFromNode(aChild, &range); - if (NS_FAILED(res)) return res; - if (range) - { - res = mRules->UpdateDocChangeRange(range); - } - return res; -} - - -NS_IMETHODIMP -nsHTMLEditListener::DidDeleteNode(nsIDOMNode *aChild, nsresult aResult) -{ - return NS_OK; -} - - -NS_IMETHODIMP -nsHTMLEditListener::WillSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset) -{ - return NS_OK; -} - - -NS_IMETHODIMP -nsHTMLEditListener::DidSplitNode(nsIDOMNode *aExistingRightNode, - PRInt32 aOffset, - nsIDOMNode *aNewLeftNode, - nsresult aResult) -{ - nsCOMPtr range; - nsresult res = MakeCollapsedRange(aExistingRightNode, 0, &range); - if (NS_FAILED(res)) return res; - res = mRules->UpdateDocChangeRange(range); - return res; -} - - -NS_IMETHODIMP -nsHTMLEditListener::WillJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent) -{ - // remember split point - nsresult res = nsEditor::GetLengthOfDOMNode(aLeftNode, mJoinOffset); - return res; -} - - -NS_IMETHODIMP -nsHTMLEditListener::DidJoinNodes(nsIDOMNode *aLeftNode, - nsIDOMNode *aRightNode, - nsIDOMNode *aParent, - nsresult aResult) -{ - // assumption that Join keeps the righthand node - nsCOMPtr range; - nsresult res = MakeCollapsedRange(aRightNode, mJoinOffset, &range); - if (NS_FAILED(res)) return res; - res = mRules->UpdateDocChangeRange(range); - return res; -} - - -NS_IMETHODIMP -nsHTMLEditListener::WillInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString) -{ - return NS_OK; -} - - -NS_IMETHODIMP -nsHTMLEditListener::DidInsertText(nsIDOMCharacterData *aTextNode, - PRInt32 aOffset, - const nsString &aString, - nsresult aResult) -{ - nsCOMPtr range; - PRInt32 length = aString.Length(); - nsresult res = MakeRangeFromTextOffsets(aTextNode, aOffset, aOffset+length, &range); - if (NS_FAILED(res)) return res; - if (range) - { - res = mRules->UpdateDocChangeRange(range); - } - return res; -} - - -NS_IMETHODIMP -nsHTMLEditListener::WillDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength) -{ - return NS_OK; -} - - -NS_IMETHODIMP -nsHTMLEditListener::DidDeleteText(nsIDOMCharacterData *aTextNode, - PRInt32 aOffset, - PRInt32 aLength, - nsresult aResult) -{ - nsCOMPtr range; - nsresult res = MakeRangeFromTextOffsets(aTextNode, aOffset, aOffset, &range); - if (NS_FAILED(res)) return res; - if (range) - { - res = mRules->UpdateDocChangeRange(range); - } - return res; -} - -NS_IMETHODIMP -nsHTMLEditListener::WillDeleteSelection(nsIDOMSelection *aSelection) -{ - nsCOMPtr range; - // get the (collapsed) selection location - nsCOMPtr selNode; - PRInt32 selOffset; - // construct a range to represent start and end of inNode - nsresult res = nsComponentManager::CreateInstance(kRangeCID, - nsnull, - NS_GET_IID(nsIDOMRange), - getter_AddRefs(range)); - if (NS_FAILED(res)) return res; - res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset); - if (NS_FAILED(res)) return res; - res = range->SetStart(selNode, selOffset); - if (NS_FAILED(res)) return res; - res = mEditor->GetEndNodeAndOffset(aSelection, &selNode, &selOffset); - if (NS_FAILED(res)) return res; - res = range->SetEnd(selNode, selOffset); - if (NS_FAILED(res)) return res; - if (range) - { - res = mRules->UpdateDocChangeRange(range); - } - return res; -} - -NS_IMETHODIMP -nsHTMLEditListener::DidDeleteSelection(nsIDOMSelection *aSelection) -{ - return NS_OK; -} - -nsresult -nsHTMLEditListener::MakeRangeFromNode(nsIDOMNode *inNode, nsCOMPtr *outRange) -{ - if (!inNode || !outRange) return NS_ERROR_NULL_POINTER; - *outRange = nsnull; - - // first check that inNode is still a descendant of the body - if (!IsDescendantOfBody(inNode)) return NS_OK; - - // construct a range to represent start and end of inNode - nsresult res = nsComponentManager::CreateInstance(kRangeCID, - nsnull, - NS_GET_IID(nsIDOMRange), - getter_AddRefs(*outRange)); - if (NS_FAILED(res)) return res; - res = (*outRange)->SelectNode(inNode); - return res; -} - - -nsresult -nsHTMLEditListener::MakeCollapsedRange(nsIDOMNode *inNode, PRInt32 inOffset, nsCOMPtr *outRange) -{ - if (!inNode || !outRange) return NS_ERROR_NULL_POINTER; - *outRange = nsnull; - - // first check that inNode is still a descendant of the body - if (!IsDescendantOfBody(inNode)) return NS_OK; - - // construct a range to represent start and end of inNode - nsresult res = nsComponentManager::CreateInstance(kRangeCID, - nsnull, - NS_GET_IID(nsIDOMRange), - getter_AddRefs(*outRange)); - if (NS_FAILED(res)) return res; - res = (*outRange)->SetStart(inNode, inOffset); - if (NS_FAILED(res)) return res; - res = (*outRange)->SetEnd(inNode, inOffset); - return res; -} - - -nsresult -nsHTMLEditListener::MakeRangeFromTextOffsets(nsIDOMCharacterData *inNode, - PRInt32 inStart, - PRInt32 inEnd, - nsCOMPtr *outRange) -{ - if (!inNode || !outRange) return NS_ERROR_NULL_POINTER; - *outRange = nsnull; - nsCOMPtr theNode = do_QueryInterface(inNode); - - // first check that inNode is still a descendant of the body - if (!IsDescendantOfBody(theNode)) return NS_OK; - - // construct a range to represent start and end of text run - nsresult res = nsComponentManager::CreateInstance(kRangeCID, - nsnull, - NS_GET_IID(nsIDOMRange), - getter_AddRefs(*outRange)); - if (NS_FAILED(res)) return res; - res = (*outRange)->SetStart(theNode, inStart); - if (NS_FAILED(res)) return res; - res = (*outRange)->SetEnd(theNode, inEnd); - return res; -} - PRBool -nsHTMLEditListener::IsDescendantOfBody(nsIDOMNode *inNode) +nsHTMLEditRules::IsDescendantOfBody(nsIDOMNode *inNode) { if (!inNode) return PR_FALSE; if (inNode == mBody.get()) return PR_TRUE; @@ -4440,6 +4193,198 @@ nsHTMLEditListener::IsDescendantOfBody(nsIDOMNode *inNode) +#ifdef XP_MAC +#pragma mark - +#pragma mark nsIEditActionListener methods +#pragma mark - +#endif + +NS_IMETHODIMP +nsHTMLEditRules::WillCreateNode(const nsString& aTag, nsIDOMNode *aParent, PRInt32 aPosition) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsHTMLEditRules::DidCreateNode(const nsString& aTag, + nsIDOMNode *aNode, + nsIDOMNode *aParent, + PRInt32 aPosition, + nsresult aResult) +{ + if (!mListenerEnabled) return NS_OK; + // assumption that Join keeps the righthand node + nsresult res = mUtilRange->SelectNode(aNode); + if (NS_FAILED(res)) return res; + res = UpdateDocChangeRange(mUtilRange); + return res; +} + + +NS_IMETHODIMP +nsHTMLEditRules::WillInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsHTMLEditRules::DidInsertNode(nsIDOMNode *aNode, + nsIDOMNode *aParent, + PRInt32 aPosition, + nsresult aResult) +{ + if (!mListenerEnabled) return NS_OK; + nsresult res = mUtilRange->SelectNode(aNode); + if (NS_FAILED(res)) return res; + res = UpdateDocChangeRange(mUtilRange); + return res; +} + + +NS_IMETHODIMP +nsHTMLEditRules::WillDeleteNode(nsIDOMNode *aChild) +{ + if (!mListenerEnabled) return NS_OK; + nsresult res = mUtilRange->SelectNode(aChild); + if (NS_FAILED(res)) return res; + res = UpdateDocChangeRange(mUtilRange); + return res; +} + + +NS_IMETHODIMP +nsHTMLEditRules::DidDeleteNode(nsIDOMNode *aChild, nsresult aResult) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsHTMLEditRules::WillSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsHTMLEditRules::DidSplitNode(nsIDOMNode *aExistingRightNode, + PRInt32 aOffset, + nsIDOMNode *aNewLeftNode, + nsresult aResult) +{ + if (!mListenerEnabled) return NS_OK; + nsresult res = mUtilRange->SetStart(aExistingRightNode, 0); + if (NS_FAILED(res)) return res; + res = mUtilRange->SetEnd(aExistingRightNode, 0); + if (NS_FAILED(res)) return res; + res = UpdateDocChangeRange(mUtilRange); + return res; +} + + +NS_IMETHODIMP +nsHTMLEditRules::WillJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent) +{ + if (!mListenerEnabled) return NS_OK; + // remember split point + nsresult res = nsEditor::GetLengthOfDOMNode(aLeftNode, mJoinOffset); + return res; +} + + +NS_IMETHODIMP +nsHTMLEditRules::DidJoinNodes(nsIDOMNode *aLeftNode, + nsIDOMNode *aRightNode, + nsIDOMNode *aParent, + nsresult aResult) +{ + if (!mListenerEnabled) return NS_OK; + // assumption that Join keeps the righthand node + nsresult res = mUtilRange->SetStart(aRightNode, mJoinOffset); + if (NS_FAILED(res)) return res; + res = mUtilRange->SetEnd(aRightNode, mJoinOffset); + if (NS_FAILED(res)) return res; + res = UpdateDocChangeRange(mUtilRange); + return res; +} + + +NS_IMETHODIMP +nsHTMLEditRules::WillInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsHTMLEditRules::DidInsertText(nsIDOMCharacterData *aTextNode, + PRInt32 aOffset, + const nsString &aString, + nsresult aResult) +{ + if (!mListenerEnabled) return NS_OK; + PRInt32 length = aString.Length(); + nsCOMPtr theNode = do_QueryInterface(aTextNode); + nsresult res = mUtilRange->SetStart(theNode, aOffset); + if (NS_FAILED(res)) return res; + res = mUtilRange->SetEnd(theNode, aOffset+length); + if (NS_FAILED(res)) return res; + res = UpdateDocChangeRange(mUtilRange); + return res; +} + + +NS_IMETHODIMP +nsHTMLEditRules::WillDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsHTMLEditRules::DidDeleteText(nsIDOMCharacterData *aTextNode, + PRInt32 aOffset, + PRInt32 aLength, + nsresult aResult) +{ + if (!mListenerEnabled) return NS_OK; + nsCOMPtr theNode = do_QueryInterface(aTextNode); + nsresult res = mUtilRange->SetStart(theNode, aOffset); + if (NS_FAILED(res)) return res; + res = mUtilRange->SetEnd(theNode, aOffset); + if (NS_FAILED(res)) return res; + res = UpdateDocChangeRange(mUtilRange); + return res; +} + +NS_IMETHODIMP +nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection) +{ + if (!mListenerEnabled) return NS_OK; + // get the (collapsed) selection location + nsCOMPtr selNode; + PRInt32 selOffset; + + nsresult res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + res = mUtilRange->SetStart(selNode, selOffset); + if (NS_FAILED(res)) return res; + res = mEditor->GetEndNodeAndOffset(aSelection, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + res = mUtilRange->SetEnd(selNode, selOffset); + if (NS_FAILED(res)) return res; + res = UpdateDocChangeRange(mUtilRange); + return res; +} + +NS_IMETHODIMP +nsHTMLEditRules::DidDeleteSelection(nsIDOMSelection *aSelection) +{ + return NS_OK; +} + + diff --git a/mozilla/editor/libeditor/html/nsHTMLEditRules.h b/mozilla/editor/libeditor/html/nsHTMLEditRules.h index 9825d4f7d66..51e9d7dabe2 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditRules.h +++ b/mozilla/editor/libeditor/html/nsHTMLEditRules.h @@ -33,20 +33,42 @@ class nsISupportsArray; class nsVoidArray; class nsIDOMElement; -class nsHTMLEditRules : public nsTextEditRules +class nsHTMLEditRules : public nsTextEditRules, nsIEditActionListener { public: + NS_DECL_ISUPPORTS_INHERITED + nsHTMLEditRules(); virtual ~nsHTMLEditRules(); - // nsEditRules methods - NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection); - NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection, PRBool aSetSelection); + + // nsIEditRules methods NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags); + NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection); + NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection); NS_IMETHOD WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled); NS_IMETHOD DidDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, nsresult aResult); + // nsIEditActionListener methods + + NS_IMETHOD WillCreateNode(const nsString& aTag, nsIDOMNode *aParent, PRInt32 aPosition); + NS_IMETHOD DidCreateNode(const nsString& aTag, nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition, nsresult aResult); + NS_IMETHOD WillInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition); + NS_IMETHOD DidInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition, nsresult aResult); + NS_IMETHOD WillDeleteNode(nsIDOMNode *aChild); + NS_IMETHOD DidDeleteNode(nsIDOMNode *aChild, nsresult aResult); + NS_IMETHOD WillSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset); + NS_IMETHOD DidSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode, nsresult aResult); + NS_IMETHOD WillJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent); + NS_IMETHOD DidJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent, nsresult aResult); + NS_IMETHOD WillInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString); + NS_IMETHOD DidInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString, nsresult aResult); + NS_IMETHOD WillDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength); + NS_IMETHOD DidDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength, nsresult aResult); + NS_IMETHOD WillDeleteSelection(nsIDOMSelection *aSelection); + NS_IMETHOD DidDeleteSelection(nsIDOMSelection *aSelection); + protected: enum RulesEndpoint @@ -138,67 +160,19 @@ protected: nsresult ConvertWhitespace(const nsString & inString, nsString & outString); nsresult ConfirmSelectionInBody(); -// removed from use: -#if 0 - nsresult AddTrailerBR(nsIDOMNode *aNode); - nsresult CreateMozDiv(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outDiv); -#endif - -// data members -protected: - nsCOMPtr mListener; - nsCOMPtr mDocChangeRange; - -// friends - friend class nsHTMLEditListener; - friend nsresult NS_NewEditListener(nsIEditActionListener **aResult, - nsHTMLEditor *htmlEditor, - nsHTMLEditRules *htmlRules); - -}; - -class nsHTMLEditListener : public nsIEditActionListener -{ -public: - nsHTMLEditListener(nsHTMLEditor *htmlEditor, nsHTMLEditRules *htmlRules); - virtual ~nsHTMLEditListener(); - NS_DECL_ISUPPORTS - - // nsIEditActionListener methods - - NS_IMETHOD WillCreateNode(const nsString& aTag, nsIDOMNode *aParent, PRInt32 aPosition); - NS_IMETHOD DidCreateNode(const nsString& aTag, nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition, nsresult aResult); - NS_IMETHOD WillInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition); - NS_IMETHOD DidInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aPosition, nsresult aResult); - NS_IMETHOD WillDeleteNode(nsIDOMNode *aChild); - NS_IMETHOD DidDeleteNode(nsIDOMNode *aChild, nsresult aResult); - NS_IMETHOD WillSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset); - NS_IMETHOD DidSplitNode(nsIDOMNode *aExistingRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode, nsresult aResult); - NS_IMETHOD WillJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent); - NS_IMETHOD DidJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent, nsresult aResult); - NS_IMETHOD WillInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString); - NS_IMETHOD DidInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString, nsresult aResult); - NS_IMETHOD WillDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength); - NS_IMETHOD DidDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength, nsresult aResult); - NS_IMETHOD WillDeleteSelection(nsIDOMSelection *aSelection); - NS_IMETHOD DidDeleteSelection(nsIDOMSelection *aSelection); - -protected: - - nsresult MakeRangeFromNode(nsIDOMNode *inNode, nsCOMPtr *outRange); - nsresult MakeRangeFromTextOffsets(nsIDOMCharacterData *inNode, - PRInt32 inStart, - PRInt32 inEnd, - nsCOMPtr *outRange); - nsresult MakeCollapsedRange(nsIDOMNode *inNode, PRInt32 inOffset, nsCOMPtr *outRange); PRBool IsDescendantOfBody(nsIDOMNode *inNode) ; - + // data members - nsHTMLEditor *mEditor; - nsHTMLEditRules *mRules; - nsCOMPtr mBody; - PRUint32 mJoinOffset; // need to remember an int across willJoin/didJoin... +protected: + nsCOMPtr mDocChangeRange; + PRBool mListenerEnabled; + nsCOMPtr mUtilRange; + nsCOMPtr mBody; + PRUint32 mJoinOffset; // need to remember an int across willJoin/didJoin... + }; +nsresult NS_NewHTMLEditRules(nsIEditRules** aInstancePtrResult); + #endif //nsHTMLEditRules_h__ diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp index 359a053b5fb..2f16ab35a8d 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp @@ -32,6 +32,7 @@ #include "nsIDOMText.h" #include "nsIDOMNodeList.h" #include "nsIDOMDocument.h" +#include "nsIDOMAttr.h" #include "nsIDocument.h" #include "nsIDOMEventReceiver.h" #include "nsIDOMKeyEvent.h" @@ -125,6 +126,9 @@ nsIAtom *nsHTMLEditor::gTypingTxnName; nsIAtom *nsHTMLEditor::gIMETxnName; nsIAtom *nsHTMLEditor::gDeleteTxnName; +// some prototypes for rules creation shortcuts +nsresult NS_NewTextEditRules(nsIEditRules** aInstancePtrResult); +nsresult NS_NewHTMLEditRules(nsIEditRules** aInstancePtrResult); #define IsLink(s) (s.EqualsIgnoreCase(hrefText)) #define IsNamedAnchor(s) (s.EqualsIgnoreCase(anchorTxt) || s.EqualsIgnoreCase(namedanchorText)) @@ -276,7 +280,12 @@ nsHTMLEditor::~nsHTMLEditor() gDeleteTxnName = nsnull; } } - + + // remove the rules as an action listener. Else we get a bad ownership loop later on. + // it's ok if the rules aren't a listener; we ignore the error. + nsCOMPtr mListener = do_QueryInterface(mRules); + RemoveEditActionListener(mListener); + //the autopointers will clear themselves up. //but we need to also remove the listeners or we have a leak nsCOMPtrselection; @@ -325,9 +334,6 @@ nsHTMLEditor::~nsHTMLEditor() } } - // deleting a null pointer is safe - delete mRules; - NS_IF_RELEASE(mTypeInState); } @@ -412,10 +418,11 @@ NS_IMETHODIMP nsHTMLEditor::Init(nsIDOMDocument *aDoc, result = nsComponentManager::CreateInstance(kCNavDTDCID, nsnull, NS_GET_IID(nsIDTD), getter_AddRefs(mDTD)); if (!mDTD) result = NS_ERROR_FAILURE; - + if (NS_FAILED(result)) return result; + // Init the rules system - // XXX: ERROR CHECKING should InitRules return an error, and then we could check it here? - InitRules(); + result = InitRules(); + if (NS_FAILED(result)) return result; EnableUndo(PR_TRUE); @@ -636,16 +643,21 @@ nsHTMLEditor::SetFlags(PRUint32 aFlags) } -void nsHTMLEditor::InitRules() +NS_IMETHODIMP nsHTMLEditor::InitRules() { // instantiate the rules for this text editor // XXX: we should be told which set of rules to instantiate + nsresult res = NS_ERROR_FAILURE; if (mFlags & eEditorPlaintextMask) - mRules = new nsTextEditRules(); + res = NS_NewTextEditRules(getter_AddRefs(mRules)); else - mRules = new nsHTMLEditRules(); + res = NS_NewHTMLEditRules(getter_AddRefs(mRules)); - mRules->Init(this, mFlags); + if (NS_FAILED(res)) return res; + if (!mRules) return NS_ERROR_UNEXPECTED; + res = mRules->Init(this, mFlags); + + return res; } @@ -661,7 +673,7 @@ PRBool nsHTMLEditor::IsModifiable() #ifdef XP_MAC #pragma mark - -#pragma mark --- nsIHTMLEditor methods --- +#pragma mark nsIHTMLEditor methods #pragma mark - #endif @@ -812,14 +824,16 @@ NS_IMETHODIMP nsHTMLEditor::TabInTable(PRBool inIsShift, PRBool *outHandled) return NS_OK; } -NS_IMETHODIMP nsHTMLEditor::CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPtr *outBRNode, EDirection aSelect) +NS_IMETHODIMP nsHTMLEditor::JoeCreateBR(nsCOMPtr *aInOutParent, PRInt32 *aInOutOffset, nsCOMPtr *outBRNode, EDirection aSelect) { - if (!aNode || !outBRNode) return NS_ERROR_NULL_POINTER; + if (!aInOutParent || !*aInOutParent || !aInOutOffset || !outBRNode) return NS_ERROR_NULL_POINTER; *outBRNode = nsnull; nsresult res; // we need to insert a br. unfortunately, we may have to split a text node to do it. - nsCOMPtr nodeAsText = do_QueryInterface(aNode); + nsCOMPtr node = *aInOutParent; + PRInt32 theOffset = *aInOutOffset; + nsCOMPtr nodeAsText = do_QueryInterface(node); nsAutoString brType("br"); nsCOMPtr brNode; if (nodeAsText) @@ -828,13 +842,13 @@ NS_IMETHODIMP nsHTMLEditor::CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPt PRInt32 offset; PRUint32 len; nodeAsText->GetLength(&len); - GetNodeLocation(aNode, &tmp, &offset); + GetNodeLocation(node, &tmp, &offset); if (!tmp) return NS_ERROR_FAILURE; - if (!aOffset) + if (!theOffset) { // we are already set to go } - else if (aOffset == (PRInt32)len) + else if (theOffset == (PRInt32)len) { // update offset to point AFTER the text node offset++; @@ -842,19 +856,22 @@ NS_IMETHODIMP nsHTMLEditor::CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPt else { // split the text node - res = SplitNode(aNode, aOffset, getter_AddRefs(tmp)); + res = SplitNode(node, theOffset, getter_AddRefs(tmp)); if (NS_FAILED(res)) return res; - res = GetNodeLocation(aNode, &tmp, &offset); + res = GetNodeLocation(node, &tmp, &offset); if (NS_FAILED(res)) return res; } // create br res = CreateNode(brType, tmp, offset, getter_AddRefs(brNode)); if (NS_FAILED(res)) return res; + *aInOutParent = tmp; + *aInOutOffset = offset+1; } else { - res = CreateNode(brType, aNode, aOffset, getter_AddRefs(brNode)); + res = CreateNode(brType, node, theOffset, getter_AddRefs(brNode)); if (NS_FAILED(res)) return res; + (*aInOutOffset)++; } *outBRNode = brNode; @@ -883,6 +900,14 @@ NS_IMETHODIMP nsHTMLEditor::CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPt return NS_OK; } + +NS_IMETHODIMP nsHTMLEditor::CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPtr *outBRNode, EDirection aSelect) +{ + nsCOMPtr parent = aNode; + PRInt32 offset = aOffset; + return JoeCreateBR(&parent, &offset, outBRNode, aSelect); +} + NS_IMETHODIMP nsHTMLEditor::InsertBR(nsCOMPtr *outBRNode) { PRBool bCollapsed; @@ -922,137 +947,662 @@ NS_IMETHODIMP nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty, if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } ForceCompositionEnd(); + nsresult res; + nsCOMPtrselection; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_NULL_POINTER; + + PRBool isCollapsed; + selection->GetIsCollapsed(&isCollapsed); + if (isCollapsed) + { + // manipulating text attributes on a collapsed selection only sets state for the next text insertion + return SetTypeInStateForProperty(*mTypeInState, aProperty, aAttribute, aValue); + } + nsAutoEditBatch batchIt(this); nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext); + nsAutoSelectionReset selectionResetter(selection, this); - if (gNoisy) - { - nsAutoString propString; - aProperty->ToString(propString); - char *propCString = propString.ToNewCString(); - if (gNoisy) { printf("---------- start nsTextEditor::SetTextProperty %s ----------\n", propCString); } - nsCRT::free(propCString); - } - - nsresult result=NS_ERROR_NOT_INITIALIZED; - nsCOMPtrselection; - result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; PRBool cancel, handled; nsTextRulesInfo ruleInfo(nsTextEditRules::kSetTextProperty); - result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); - if (NS_FAILED(result)) return result; + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (NS_FAILED(res)) return res; if (!cancel && !handled) { - PRBool isCollapsed; - selection->GetIsCollapsed(&isCollapsed); - if (PR_TRUE==isCollapsed) - { - // manipulating text attributes on a collapsed selection only sets state for the next text insertion - SetTypeInStateForProperty(*mTypeInState, aProperty, aAttribute, aValue); - } - else - { - // set the text property for all selected ranges - nsCOMPtr enumerator; - result = selection->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_FAILED(result)) return result; - if (!enumerator) return NS_ERROR_NULL_POINTER; - - enumerator->First(); - nsCOMPtr currentItem; - result = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if (NS_FAILED(result)) return result; - if (!currentItem) return NS_ERROR_NULL_POINTER; + // get selection range enumerator + nsCOMPtr enumerator; + res = selection->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_FAILED(res)) return res; + if (!enumerator) return NS_ERROR_FAILURE; + // loop thru the ranges in the selection + enumerator->First(); + nsCOMPtr currentItem; + while ((NS_ENUMERATOR_FALSE == enumerator->IsDone())) + { + res = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if (NS_FAILED(res)) return res; + if (!currentItem) return NS_ERROR_FAILURE; + nsCOMPtr range( do_QueryInterface(currentItem) ); - nsCOMPtrcommonParent; - result = range->GetCommonParent(getter_AddRefs(commonParent)); - if (NS_FAILED(result)) return result; - if (!commonParent) return NS_ERROR_NULL_POINTER; - PRInt32 startOffset, endOffset; - range->GetStartOffset(&startOffset); - range->GetEndOffset(&endOffset); - nsCOMPtr startParent; nsCOMPtr endParent; - range->GetStartParent(getter_AddRefs(startParent)); - range->GetEndParent(getter_AddRefs(endParent)); - PRBool startIsText = IsTextNode(startParent); - PRBool endIsText = IsTextNode(endParent); - if ((PR_TRUE==startIsText) && (PR_TRUE==endIsText) && - (startParent.get()==endParent.get())) - { // the range is entirely contained within a single text node - // commonParent==aStartParent, so get the "real" parent of the selection - startParent->GetParentNode(getter_AddRefs(commonParent)); - result = SetTextPropertiesForNode(startParent, commonParent, - startOffset, endOffset, - aProperty, aAttribute, aValue); + // adjust range to include any ancestors who's children are entirely selected + res = PromoteInlineRange(range); + if (NS_FAILED(res)) return res; + + // check for easy case: both range endpoints in same text node + nsCOMPtr startNode, endNode; + res = range->GetStartParent(getter_AddRefs(startNode)); + if (NS_FAILED(res)) return res; + res = range->GetEndParent(getter_AddRefs(endNode)); + if (NS_FAILED(res)) return res; + if ((startNode == endNode) && IsTextNode(startNode)) + { + // MOOSE: workaround for selection bug: + selection->ClearSelection(); + + PRInt32 startOffset, endOffset; + range->GetStartOffset(&startOffset); + range->GetEndOffset(&endOffset); + nsCOMPtr nodeAsText = do_QueryInterface(startNode); + res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, endOffset, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; } else { - nsCOMPtr startGrandParent; - result = startParent->GetParentNode(getter_AddRefs(startGrandParent)); - if (NS_FAILED(result)) return result; - if (!startGrandParent) return NS_ERROR_NULL_POINTER; - nsCOMPtr endGrandParent; - result = endParent->GetParentNode(getter_AddRefs(endGrandParent)); - if (NS_FAILED(result)) return result; - if (!endGrandParent) return NS_ERROR_NULL_POINTER; + // not the easy case. range not contained in single text node. + // there are up to three phases here. There are all the nodes + // reported by the subtree iterator to be processed. And there + // are potentially a starting textnode and an ending textnode + // which are only partially contained by the range. + + // lets handle the nodes reported by the iterator. These nodes + // are entirely contained in the selection range. We build up + // a list of them (since doing operations on the document during + // iteration would perturb the iterator). - PRBool canCollapseStyleNode = PR_FALSE; - if ((PR_TRUE==startIsText) && (PR_TRUE==endIsText) && - endGrandParent.get()==startGrandParent.get()) + nsCOMPtr iter; + res = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull, + NS_GET_IID(nsIContentIterator), + getter_AddRefs(iter)); + if (NS_FAILED(res)) return res; + if (!iter) return NS_ERROR_FAILURE; + + nsCOMPtr arrayOfNodes; + nsCOMPtr content; + nsCOMPtr node; + nsCOMPtr isupports; + + // make a array + res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); + if (NS_FAILED(res)) return res; + + // iterate range and build up array + iter->Init(range); + while (NS_ENUMERATOR_FALSE == iter->IsDone()) { - result = IntermediateNodesAreInline(range, startParent, startOffset, - endParent, endOffset, - canCollapseStyleNode); + res = iter->CurrentNode(getter_AddRefs(content)); + if (NS_FAILED(res)) return res; + node = do_QueryInterface(content); + if (!node) return NS_ERROR_FAILURE; + if (IsEditable(node)) + { + isupports = do_QueryInterface(node); + arrayOfNodes->AppendElement(isupports); + } + res = iter->Next(); + if (NS_FAILED(res)) return res; } - if (NS_SUCCEEDED(result)) + + // MOOSE: workaround for selection bug: + selection->ClearSelection(); + + // first check the start parent of the range to see if it needs to + // be seperately handled (it does if it's a text node, due to how the + // subtree iterator works - it will not have reported it). + if (IsTextNode(startNode) && IsEditable(startNode)) { - if (PR_TRUE==canCollapseStyleNode) - { // the range is between 2 nodes that have a common (immediate) grandparent, - // and any intermediate nodes are just inline style nodes - result = SetTextPropertiesForNodesWithSameParent(startParent,startOffset, - endParent, endOffset, - commonParent, - aProperty, aAttribute, aValue); - } - else - { // the range is between 2 nodes that have no simple relationship - result = SetTextPropertiesForNodeWithDifferentParents(range, - startParent,startOffset, - endParent, endOffset, - commonParent, - aProperty, aAttribute, aValue); - } + nsCOMPtr nodeAsText = do_QueryInterface(startNode); + PRInt32 startOffset; + PRUint32 textLen; + range->GetStartOffset(&startOffset); + nodeAsText->GetLength(&textLen); + res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, textLen, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; + } + + // then loop through the list, set the property on each node + PRUint32 listCount; + PRUint32 j; + arrayOfNodes->Count(&listCount); + for (j = 0; j < listCount; j++) + { + isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); + node = do_QueryInterface(isupports); + res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; + arrayOfNodes->RemoveElementAt(0); + } + + // last check the end parent of the range to see if it needs to + // be seperately handled (it does if it's a text node, due to how the + // subtree iterator works - it will not have reported it). + if (IsTextNode(endNode) && IsEditable(endNode)) + { + nsCOMPtr nodeAsText = do_QueryInterface(endNode); + PRInt32 endOffset; + range->GetEndOffset(&endOffset); + res = SetInlinePropertyOnTextNode(nodeAsText, 0, endOffset, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; } } - if (NS_SUCCEEDED(result)) - { // compute a range for the selection - // don't want to actually do anything with selection, because - // we are still iterating through it. Just want to create and remember - // an nsIDOMRange, and later add the range to the selection after clearing it. - // XXX: I'm blocked here because nsIDOMSelection doesn't provide a mechanism - // for setting a compound selection yet. - } + enumerator->Next(); } } if (!cancel) { // post-process - result = mRules->DidDoAction(selection, &ruleInfo, result); + res = mRules->DidDoAction(selection, &ruleInfo, res); } - if (gNoisy) {DebugDumpContent(); } // DEBUG - if (gNoisy) - { - nsAutoString propString; - aProperty->ToString(propString); - char *propCString = propString.ToNewCString(); - if (gNoisy) { printf("---------- end nsTextEditor::SetTextProperty %s ----------\n", propCString); } - nsCRT::free(propCString); + return res; +} + + + +nsresult +nsHTMLEditor::SetInlinePropertyOnTextNode( nsIDOMCharacterData *aTextNode, + PRInt32 aStartOffset, + PRInt32 aEndOffset, + nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue) +{ + if (!aTextNode) return NS_ERROR_NULL_POINTER; + + // dont need to do anything if no characters actually selected + if (aStartOffset == aEndOffset) return NS_OK; + + nsresult res = NS_OK; + nsCOMPtr tmp, node = do_QueryInterface(aTextNode); + + // dont need to do anything if property already set on node + PRBool bHasProp; + nsCOMPtr styleNode; + IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, bHasProp, getter_AddRefs(styleNode)); + if (bHasProp) return NS_OK; + + // do we need to split the text node? + PRUint32 textLen; + aTextNode->GetLength(&textLen); + + if ( (PRUint32)aEndOffset != textLen ) + { + // we need to split off back of text node + res = SplitNode(node, aEndOffset, getter_AddRefs(tmp)); + if (NS_FAILED(res)) return res; + node = tmp; // remember left node + } + if ( aStartOffset ) + { + // we need to split off front of text node + res = SplitNode(node, aStartOffset, getter_AddRefs(tmp)); + if (NS_FAILED(res)) return res; + } + + // reparent the node inside inline node with appropriate {attribute,value} + res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue); + return res; +} + + +nsresult +nsHTMLEditor::SetInlinePropertyOnNode( nsIDOMNode *aNode, + nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue) +{ + if (!aNode || !aProperty) return NS_ERROR_NULL_POINTER; + + nsresult res = NS_OK; + nsCOMPtr tmp; + nsAutoString tag; + aProperty->ToString(tag); + tag.ToLowerCase(); + + // dont need to do anything if property already set on node + PRBool bHasProp; + nsCOMPtr styleNode; + IsTextPropertySetByContent(aNode, aProperty, aAttribute, aValue, bHasProp, getter_AddRefs(styleNode)); + if (bHasProp) return NS_OK; + + // is it already the right kind of node, but with wrong attribute? + if (NodeIsType(aNode, aProperty)) + { + // just set the attribute on it. + // but first remove any contrary style in it's children. + res = RemoveStyleInside(aNode, aProperty, aAttribute, PR_TRUE); + if (NS_FAILED(res)) return res; + nsCOMPtr elem = do_QueryInterface(aNode); + return SetAttribute(elem, *aAttribute, *aValue); + } + + // can it be put inside inline node? + if (TagCanContain(tag, aNode)) + { + nsCOMPtr priorNode, nextNode; + // is either of it's neighbors the right kind of node? + GetPriorHTMLSibling(aNode, &priorNode); + GetNextHTMLSibling(aNode, &nextNode); + if (priorNode && NodeIsType(priorNode, aProperty) && + HasAttrVal(priorNode, aAttribute, aValue) && + IsOnlyAttribute(priorNode, aAttribute) ) + { + // previous sib is already right kind of inline node; slide this over into it + res = MoveNode(aNode, priorNode, -1); + } + else if (nextNode && NodeIsType(nextNode, aProperty) && + HasAttrVal(nextNode, aAttribute, aValue) && + IsOnlyAttribute(priorNode, aAttribute) ) + { + // following sib is already right kind of inline node; slide this over into it + res = MoveNode(aNode, nextNode, 0); + } + else + { + // ok, chuck it in it's very own container + res = InsertContainerAbove(aNode, &tmp, tag, aAttribute, aValue); + } + if (NS_FAILED(res)) return res; + return RemoveStyleInside(aNode, aProperty, aAttribute); + } + // none of the above? then cycle through the children. + nsCOMPtr childNodes; + res = aNode->GetChildNodes(getter_AddRefs(childNodes)); + if (NS_FAILED(res)) return res; + if (childNodes) + { + PRInt32 j; + PRUint32 childCount; + childNodes->GetLength(&childCount); + if (childCount) + { + nsCOMPtr arrayOfNodes; + nsCOMPtr node; + nsCOMPtr isupports; + + // make a array + res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); + if (NS_FAILED(res)) return res; + + // populate the list + for (j=0 ; j < (PRInt32)childCount; j++) + { + nsCOMPtr childNode; + res = childNodes->Item(j, getter_AddRefs(childNode)); + if ((NS_SUCCEEDED(res)) && (childNode) && IsEditable(childNode)) + { + isupports = do_QueryInterface(childNode); + arrayOfNodes->AppendElement(isupports); + } + } + + // then loop through the list, set the property on each node + PRUint32 listCount; + arrayOfNodes->Count(&listCount); + for (j = 0; j < listCount; j++) + { + isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); + node = do_QueryInterface(isupports); + res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue); + if (NS_FAILED(res)) return res; + arrayOfNodes->RemoveElementAt(0); + } + } + } + return res; +} + + +nsresult nsHTMLEditor::SplitStyleAboveRange(nsIDOMRange *inRange, + nsIAtom *aProperty, + const nsString *aAttribute) +{ + if (!inRange || !aProperty) return NS_ERROR_NULL_POINTER; + nsresult res; + nsCOMPtr startNode, endNode, origStartNode; + PRInt32 startOffset, endOffset, origStartOffset; + + res = inRange->GetStartParent(getter_AddRefs(startNode)); + if (NS_FAILED(res)) return res; + res = inRange->GetStartOffset(&startOffset); + if (NS_FAILED(res)) return res; + res = inRange->GetEndParent(getter_AddRefs(endNode)); + if (NS_FAILED(res)) return res; + res = inRange->GetEndOffset(&endOffset); + if (NS_FAILED(res)) return res; + + origStartNode = startNode; + origStartOffset = startOffset; + PRBool sameNode = (startNode==endNode); + + // split any matching style nodes above the start of range + res = SplitStyleAbovePoint(&startNode, &startOffset, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + + if (sameNode && (startNode != origStartNode)) + { + // our startNode got split. This changes the offset of the end of our range. + endOffset -= origStartOffset; + } + + // second verse, same as the first... + res = SplitStyleAbovePoint(&endNode, &endOffset, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + + // reset the range + res = inRange->SetStart(startNode, startOffset); + if (NS_FAILED(res)) return res; + res = inRange->SetEnd(endNode, endOffset); + return res; +} + +nsresult nsHTMLEditor::SplitStyleAbovePoint(nsCOMPtr *aNode, + PRInt32 *aOffset, + nsIAtom *aProperty, + const nsString *aAttribute) +{ + if (!aNode || !*aNode || !aOffset) return NS_ERROR_NULL_POINTER; + // split any matching style nodes above the node/offset + nsCOMPtr parent, tmp = *aNode; + PRInt32 offset; + while (tmp && !nsHTMLEditUtils::IsBody(tmp)) + { + if (NodeIsType(tmp, aProperty)) + { + // found a style node we need to split + SplitNodeDeep(tmp, *aNode, *aOffset, &offset); + // reset startNode/startOffset + tmp->GetParentNode(getter_AddRefs(*aNode)); + *aOffset = offset; + } + tmp->GetParentNode(getter_AddRefs(parent)); + tmp = parent; + } + return NS_OK; +} + +nsresult nsHTMLEditor::RemoveStyleInside(nsIDOMNode *aNode, + nsIAtom *aProperty, + const nsString *aAttribute, + PRBool aChildrenOnly) +{ + if (!aNode || !aProperty) return NS_ERROR_NULL_POINTER; + if (IsTextNode(aNode)) return NS_OK; + nsresult res = NS_OK; + + // first process the children + nsCOMPtr child, tmp; + aNode->GetFirstChild(getter_AddRefs(child)); + while (child) + { + // cache next sibling since we might remove child + child->GetNextSibling(getter_AddRefs(tmp)); + res = RemoveStyleInside(child, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + child = tmp; + } + + // then process the node itself + if (!aChildrenOnly && NodeIsType(aNode, aProperty)) + { + // if we weren't passed an attribute, then we want to + // remove any matching inlinestyles entirely + if (!aAttribute || aAttribute->IsEmpty()) + { + res = RemoveContainer(aNode); + } + // otherwise we just want to eliminate the attribute + else + { + if (HasAttr(aNode, aAttribute)) + { + // if this matching attribute is the ONLY one on the node, + // then remove the whole node. Otherwise just nix the attribute. + if (IsOnlyAttribute(aNode, aAttribute)) + { + res = RemoveContainer(aNode); + } + else + { + nsCOMPtr elem = do_QueryInterface(aNode); + if (!elem) return NS_ERROR_NULL_POINTER; + res = RemoveAttribute(elem, *aAttribute); + } + } + } + } + return res; +} + +PRBool nsHTMLEditor::IsOnlyAttribute(nsIDOMNode *aNode, + const nsString *aAttribute) +{ + if (!aNode || !aAttribute) return PR_FALSE; // ooops + nsCOMPtr content = do_QueryInterface(aNode); + if (!content) return PR_FALSE; // ooops + + PRInt32 attrCount, i, nameSpaceID; + nsIAtom* attrName; + content->GetAttributeCount(attrCount); + + for (i=0; iGetAttributeNameAt(i, nameSpaceID, attrName); + nsAutoString attrString, tmp; + if (!attrName) continue; // ooops + attrName->ToString(attrString); + // if it's the attribute we know about, keep looking + if (attrString.EqualsIgnoreCase(*aAttribute)) continue; + // if it's a special _moz... attribute, keep looking + attrString.Left(tmp,4); + if (tmp=="_moz") continue; + // otherwise, it's another attribute, so return false + return PR_FALSE; + } + // if we made it through all of them without finding a real attribute + // other than aAttribute, then return PR_TRUE + return PR_TRUE; +} + +PRBool +nsHTMLEditor::HasMatchingAttributes(nsIDOMNode *aNode1, + nsIDOMNode *aNode2) +{ + if (!aNode1 || !aNode2) return PR_FALSE; // ooops + nsCOMPtr content1 = do_QueryInterface(aNode1); + if (!content1) return PR_FALSE; // ooops + nsCOMPtr content2 = do_QueryInterface(aNode2); + if (!content2) return PR_FALSE; // ooops + + PRInt32 attrCount, i, nameSpaceID, realCount1=0, realCount2=0; + nsIAtom* attrName; + nsresult res, res2; + content1->GetAttributeCount(attrCount); + nsAutoString attrString, tmp, attrVal1, attrVal2; + + for (i=0; iGetAttributeNameAt(i, nameSpaceID, attrName); + if (!attrName) continue; // ooops + attrName->ToString(attrString); + // if it's a special _moz... attribute, keep going + attrString.Left(tmp,4); + if (tmp=="_moz") continue; + // otherwise, it's another attribute, so count it + realCount1++; + // and compare it to element2's attributes + res = content1->GetAttribute(nameSpaceID, attrName, attrVal1); + res2 = content2->GetAttribute(nameSpaceID, attrName, attrVal2); + if (res != res2) return PR_FALSE; + if (!attrVal1.EqualsIgnoreCase(attrVal2)) return PR_FALSE; + } + + content2->GetAttributeCount(attrCount); + for (i=0; iGetAttributeNameAt(i, nameSpaceID, attrName); + if (!attrName) continue; // ooops + attrName->ToString(attrString); + // if it's a special _moz... attribute, keep going + attrString.Left(tmp,4); + if (tmp=="_moz") continue; + // otherwise, it's another attribute, so count it + realCount2++; + } + + if (realCount1 != realCount2) return PR_FALSE; + // otherwise, attribute counts match, and we already compared them + // when going through the first list, so we're done. + return PR_TRUE; +} + +PRBool nsHTMLEditor::HasAttr(nsIDOMNode *aNode, + const nsString *aAttribute) +{ + if (!aNode) return PR_FALSE; + if (!aAttribute || aAttribute->IsEmpty()) return PR_TRUE; // everybody has the 'null' attribute + + // get element + nsCOMPtr elem = do_QueryInterface(aNode); + if (!elem) return PR_FALSE; + + // get attribute node + nsCOMPtr attNode; + nsresult res = elem->GetAttributeNode(*aAttribute, getter_AddRefs(attNode)); + if ((NS_FAILED(res)) || !attNode) return PR_FALSE; + return PR_TRUE; +} + + +PRBool nsHTMLEditor::HasAttrVal(nsIDOMNode *aNode, + const nsString *aAttribute, + const nsString *aValue) +{ + if (!aNode) return PR_FALSE; + if (!aAttribute || aAttribute->IsEmpty()) return PR_TRUE; // everybody has the 'null' attribute + + // get element + nsCOMPtr elem = do_QueryInterface(aNode); + if (!elem) return PR_FALSE; + + // get attribute node + nsCOMPtr attNode; + nsresult res = elem->GetAttributeNode(*aAttribute, getter_AddRefs(attNode)); + if ((NS_FAILED(res)) || !attNode) return PR_FALSE; + + // check if attribute has a value + PRBool isSet; + attNode->GetSpecified(&isSet); + // if no value, and that's what we wanted, then return true + if (!isSet && (!aValue || aValue->IsEmpty())) return PR_TRUE; + + // get attribute value + nsAutoString attrVal; + attNode->GetValue(attrVal); + + // do values match? + if (attrVal.EqualsIgnoreCase(*aValue)) return PR_TRUE; + return PR_FALSE; +} + + +nsresult nsHTMLEditor::PromoteInlineRange(nsIDOMRange *inRange) +{ + if (!inRange) return NS_ERROR_NULL_POINTER; + nsresult res; + nsCOMPtr startNode, endNode, parent; + PRInt32 startOffset, endOffset; + + res = inRange->GetStartParent(getter_AddRefs(startNode)); + if (NS_FAILED(res)) return res; + res = inRange->GetStartOffset(&startOffset); + if (NS_FAILED(res)) return res; + res = inRange->GetEndParent(getter_AddRefs(endNode)); + if (NS_FAILED(res)) return res; + res = inRange->GetEndOffset(&endOffset); + if (NS_FAILED(res)) return res; + + while ( startNode && + !nsHTMLEditUtils::IsBody(startNode) && + IsAtFrontOfNode(startNode, startOffset) ) + { + res = GetNodeLocation(startNode, &parent, &startOffset); + if (NS_FAILED(res)) return res; + startNode = parent; + } + if (!startNode) return NS_ERROR_NULL_POINTER; + + while ( endNode && + !nsHTMLEditUtils::IsBody(endNode) && + IsAtEndOfNode(endNode, endOffset) ) + { + res = GetNodeLocation(endNode, &parent, &endOffset); + if (NS_FAILED(res)) return res; + endNode = parent; + endOffset++; // we are AFTER this node + } + if (!endNode) return NS_ERROR_NULL_POINTER; + + res = inRange->SetStart(startNode, startOffset); + if (NS_FAILED(res)) return res; + res = inRange->SetEnd(endNode, endOffset); + return res; +} + +PRBool nsHTMLEditor::IsAtFrontOfNode(nsIDOMNode *aNode, PRInt32 aOffset) +{ + if (!aNode) return PR_FALSE; // oops + if (!aOffset) return PR_TRUE; + + if (IsTextNode(aNode)) + { + return PR_FALSE; + } + else + { + nsCOMPtr firstNode; + GetFirstEditableNode(aNode, &firstNode); + if (!firstNode) return PR_TRUE; + PRInt32 offset; + nsEditor::GetChildOffset(firstNode, aNode, offset); + if (offset < aOffset) return PR_FALSE; + return PR_TRUE; + } +} + +PRBool nsHTMLEditor::IsAtEndOfNode(nsIDOMNode *aNode, PRInt32 aOffset) +{ + if (!aNode) return PR_FALSE; // oops + PRUint32 len; + GetLengthOfDOMNode(aNode, len); + if (aOffset == len) return PR_TRUE; + + if (IsTextNode(aNode)) + { + return PR_FALSE; + } + else + { + nsCOMPtr lastNode; + GetLastEditableNode(aNode, &lastNode); + if (!lastNode) return PR_TRUE; + PRInt32 offset; + nsEditor::GetChildOffset(lastNode, aNode, offset); + if (offset < aOffset) return PR_TRUE; + return PR_FALSE; } - return result; } NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, @@ -1176,7 +1726,7 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, PRBool skipNode = PR_FALSE; if (text) { - if (PR_FALSE==isCollapsed && PR_TRUE==first && PR_TRUE==firstNodeInRange) + if (!isCollapsed && first && firstNodeInRange) { firstNodeInRange = PR_FALSE; PRInt32 startOffset; @@ -1194,7 +1744,7 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, { // handle non-text leaf nodes here PRBool canContainChildren; content->CanContainChildren(canContainChildren); - if (PR_TRUE==canContainChildren) + if (canContainChildren) { //if (gNoisy) { printf(" skipping non-leaf node %p\n", content.get()); } skipNode = PR_TRUE; @@ -1203,7 +1753,7 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, //if (gNoisy) { printf(" testing non-text leaf node %p\n", content.get()); } } } - if (PR_FALSE==skipNode) + if (!skipNode) { nsCOMPtrnode; node = do_QueryInterface(content); @@ -1212,12 +1762,12 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, PRBool isSet; nsCOMPtrresultNode; IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet, getter_AddRefs(resultNode)); - if (PR_TRUE==first) + if (first) { aFirst = isSet; first = PR_FALSE; } - if (PR_TRUE==isSet) { + if (isSet) { aAny = PR_TRUE; } else { @@ -1229,7 +1779,7 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, iter->CurrentNode(getter_AddRefs(content)); } } - if (PR_FALSE==aAny) + if (!aAny) { // make sure that if none of the selection is set, we don't report all is set aAll = PR_FALSE; } @@ -1239,37 +1789,29 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttribute) { - if (!aProperty) { return NS_ERROR_NULL_POINTER; } - if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } + if (!aProperty) return NS_ERROR_NULL_POINTER; + if (!mRules) return NS_ERROR_NOT_INITIALIZED; ForceCompositionEnd(); + nsresult res; + nsCOMPtrselection; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + if (!selection) return NS_ERROR_NULL_POINTER; + nsAutoEditBatch batchIt(this); nsAutoRules beginRulesSniffing(this, kOpRemoveTextProperty, nsIEditor::eNext); + nsAutoSelectionReset selectionResetter(selection, this); - if (gNoisy) - { - nsAutoString propString; - aProperty->ToString(propString); - char *propCString = propString.ToNewCString(); - if (gNoisy) { printf("---------- start nsTextEditor::RemoveInlineProperty %s ----------\n", propCString); } - nsCRT::free(propCString); - } - - nsresult result; - nsCOMPtrselection; - result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - PRBool cancel, handled; nsTextRulesInfo ruleInfo(nsTextEditRules::kRemoveTextProperty); - result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); - if (NS_FAILED(result)) return result; + res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); + if (NS_FAILED(res)) return res; if (!cancel && !handled) { PRBool isCollapsed; selection->GetIsCollapsed(&isCollapsed); - if (PR_TRUE==isCollapsed) + if (isCollapsed) { // manipulating text attributes on a collapsed selection only sets state for the next text insertion // But only if it's a property for which we have TypeInState! @@ -1286,16 +1828,16 @@ NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsStr // collapsed insertion point. nsCOMPtr selNode, tmpNode, parent; PRInt32 selOffset, outOffset; - result = GetStartNodeAndOffset(selection, &selNode, &selOffset); - if (NS_FAILED(result)) return result; + res = GetStartNodeAndOffset(selection, &selNode, &selOffset); + if (NS_FAILED(res)) return res; tmpNode = selNode; while (tmpNode) { if (nsHTMLEditUtils::IsBody(tmpNode)) break; if (IsLinkNode(tmpNode)) { - result = SplitNodeDeep(tmpNode, selNode, selOffset, &outOffset); - if (NS_FAILED(result)) return result; + res = SplitNodeDeep(tmpNode, selNode, selOffset, &outOffset); + if (NS_FAILED(res)) return res; tmpNode->GetParentNode(getter_AddRefs(parent)); selection->Collapse(parent, outOffset); break; @@ -1307,92 +1849,101 @@ NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsStr } else { - // removing text properties can really shuffle text nodes around - // so we need to keep some extra state to restore a reasonable selection - // after we're done - nsCOMPtr parentForSelection; // selection's block parent - PRInt32 rangeStartOffset, rangeEndOffset; - GetTextSelectionOffsetsForRange(selection, getter_AddRefs(parentForSelection), - rangeStartOffset, rangeEndOffset); - nsCOMPtr startParent, endParent; - PRInt32 startOffset, endOffset; + // get selection range enumerator nsCOMPtr enumerator; - result = selection->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_FAILED(result)) return result; - if (!enumerator) return NS_ERROR_NULL_POINTER; + res = selection->GetEnumerator(getter_AddRefs(enumerator)); + if (NS_FAILED(res)) return res; + if (!enumerator) return NS_ERROR_FAILURE; + // loop thru the ranges in the selection enumerator->First(); - nsCOMPtrcurrentItem; - result = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if (NS_FAILED(result)) return result; - //XXX: should be a while loop to get all ranged in selection - if (currentItem) + nsCOMPtr currentItem; + while ((NS_ENUMERATOR_FALSE == enumerator->IsDone())) { + res = enumerator->CurrentItem(getter_AddRefs(currentItem)); + if (NS_FAILED(res)) return res; + if (!currentItem) return NS_ERROR_FAILURE; + nsCOMPtr range( do_QueryInterface(currentItem) ); - nsCOMPtrcommonParent; - result = range->GetCommonParent(getter_AddRefs(commonParent)); - if (NS_FAILED(result)) return result; - if (!commonParent) return NS_ERROR_NULL_POINTER; - range->GetStartOffset(&startOffset); - range->GetEndOffset(&endOffset); - result = range->GetStartParent(getter_AddRefs(startParent)); - if (NS_FAILED(result)) return result; - if (!startParent) return NS_ERROR_NULL_POINTER; - result = range->GetEndParent(getter_AddRefs(endParent)); - if (NS_FAILED(result)) return result; - if (!endParent) return NS_ERROR_NULL_POINTER; - - if (startParent.get()==endParent.get()) - { // the range is entirely contained within a single text node - // commonParent==aStartParent, so get the "real" parent of the selection - startParent->GetParentNode(getter_AddRefs(commonParent)); - result = RemoveTextPropertiesForNode(startParent, commonParent, - startOffset, endOffset, - aProperty, nsnull); + // adjust range to include any ancestors who's children are entirely selected + res = PromoteInlineRange(range); + if (NS_FAILED(res)) return res; + + // remove this style from ancestors of our range empoints, + // splitting them as appropriate + res = SplitStyleAboveRange(range, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + + // check for easy case: both range endpoints in same text node + nsCOMPtr startNode, endNode; + res = range->GetStartParent(getter_AddRefs(startNode)); + if (NS_FAILED(res)) return res; + res = range->GetEndParent(getter_AddRefs(endNode)); + if (NS_FAILED(res)) return res; + if ((startNode == endNode) && IsTextNode(startNode)) + { + // we're done with this range! } else { - result = RemoveTextPropertiesForNodeWithDifferentParents(startParent,startOffset, - endParent, endOffset, - commonParent, - aProperty, nsnull); - } - if (NS_FAILED(result)) return result; + // not the easy case. range not contained in single text node. + nsCOMPtr iter; + res = nsComponentManager::CreateInstance(kSubtreeIteratorCID, nsnull, + NS_GET_IID(nsIContentIterator), + getter_AddRefs(iter)); + if (NS_FAILED(res)) return res; + if (!iter) return NS_ERROR_FAILURE; - { // compute a range for the selection - // don't want to actually do anything with selection, because - // we are still iterating through it. Just want to create and remember - // an nsIDOMRange, and later add the range to the selection after clearing it. - // XXX: I'm blocked here because nsIDOMSelection doesn't provide a mechanism - // for setting a compound selection yet. + nsCOMPtr arrayOfNodes; + nsCOMPtr content; + nsCOMPtr node; + nsCOMPtr isupports; + + // make a array + res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes)); + if (NS_FAILED(res)) return res; + + // iterate range and build up array + iter->Init(range); + while (NS_ENUMERATOR_FALSE == iter->IsDone()) + { + res = iter->CurrentNode(getter_AddRefs(content)); + if (NS_FAILED(res)) return res; + node = do_QueryInterface(content); + if (!node) return NS_ERROR_FAILURE; + if (IsEditable(node)) + { + isupports = do_QueryInterface(node); + arrayOfNodes->AppendElement(isupports); + } + res = iter->Next(); + if (NS_FAILED(res)) return res; + } + + // loop through the list, remove the property on each node + PRUint32 listCount; + PRUint32 j; + arrayOfNodes->Count(&listCount); + for (j = 0; j < listCount; j++) + { + isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0)); + node = do_QueryInterface(isupports); + res = RemoveStyleInside(node, aProperty, aAttribute); + if (NS_FAILED(res)) return res; + arrayOfNodes->RemoveElementAt(0); + } } - } - if (NS_SUCCEEDED(result)) - { - result = CollapseAdjacentTextNodes(selection); // we may have created wasteful consecutive text nodes. collapse them. - } - if (NS_SUCCEEDED(result)) - { - // XXX: ERROR_HANDLING should get a result and validate it - ResetTextSelectionForRange(parentForSelection, rangeStartOffset, rangeEndOffset, selection); + enumerator->Next(); } } } if (!cancel) { // post-process - result = mRules->DidDoAction(selection, &ruleInfo, result); + res = mRules->DidDoAction(selection, &ruleInfo, res); } - if (gNoisy) - { - nsAutoString propString; - aProperty->ToString(propString); - char *propCString = propString.ToNewCString(); - if (gNoisy) { printf("---------- end nsTextEditor::RemoveInlineProperty %s ----------\n", propCString); } - nsCRT::free(propCString); - } - return result; + return res; } NS_IMETHODIMP nsHTMLEditor::IncreaseFontSize() @@ -2094,7 +2645,7 @@ nsHTMLEditor::GetParentBlockTags(nsStringArray *aTagList, PRBool aGetLists) blockParent->GetTagName(blockParentTag); PRBool isRoot; IsRootTag(blockParentTag, isRoot); - if ((PR_FALSE==isRoot) && (-1==aTagList->IndexOf(blockParentTag))) { + if ((!isRoot) && (-1==aTagList->IndexOf(blockParentTag))) { aTagList->AppendString(blockParentTag); } } @@ -2181,7 +2732,7 @@ nsHTMLEditor::ReplaceBlockParent(nsString& aParentTag) if (gNoisy) { char *tag = aParentTag.ToNewCString(); - printf("---------- nsHTMLEditor::ReplaceBlockParent %s ----------\n", tag); + printf("- nsHTMLEditor::ReplaceBlockParent %s -\n", tag); nsCRT::free(tag); } @@ -2191,7 +2742,7 @@ nsHTMLEditor::ReplaceBlockParent(nsString& aParentTag) if (NS_FAILED(res)) return res; if (!selection) return NS_ERROR_NULL_POINTER; - nsAutoSelectionReset selectionResetter(selection); + nsAutoSelectionReset selectionResetter(selection, this); // set the block parent for all selected ranges nsAutoEditBatch beginBatching(this); nsCOMPtr enumerator; @@ -2219,7 +2770,7 @@ NS_IMETHODIMP nsHTMLEditor::RemoveParagraphStyle() { if (gNoisy) { - printf("---------- nsHTMLEditor::RemoveParagraphStyle ----------\n"); + printf("- nsHTMLEditor::RemoveParagraphStyle -\n"); } nsresult res=NS_ERROR_NOT_INITIALIZED; @@ -2228,7 +2779,7 @@ nsHTMLEditor::RemoveParagraphStyle() if (NS_FAILED(res)) return res; if (!selection) return NS_ERROR_NULL_POINTER; - nsAutoSelectionReset selectionResetter(selection); + nsAutoSelectionReset selectionResetter(selection, this); nsAutoEditBatch beginBatching(this); nsCOMPtr enumerator; @@ -2252,7 +2803,7 @@ NS_IMETHODIMP nsHTMLEditor::RemoveParent(const nsString &aParentTag) { if (gNoisy) { - printf("---------- nsHTMLEditor::RemoveParent ----------\n"); + printf("- nsHTMLEditor::RemoveParent -\n"); } nsresult res; @@ -2261,7 +2812,7 @@ nsHTMLEditor::RemoveParent(const nsString &aParentTag) if (NS_FAILED(res)) return res; if (!selection) return NS_ERROR_NULL_POINTER; - nsAutoSelectionReset selectionResetter(selection); + nsAutoSelectionReset selectionResetter(selection, this); nsAutoEditBatch beginBatching(this); nsCOMPtr enumerator; res = selection->GetEnumerator(getter_AddRefs(enumerator)); @@ -3232,7 +3783,7 @@ NS_IMETHODIMP nsHTMLEditor::GetMaxTextLength(PRInt32& aMaxTextLength) #ifdef XP_MAC #pragma mark - -#pragma mark --- nsIEditorStyleSheets methods --- +#pragma mark nsIEditorStyleSheets methods #pragma mark - #endif @@ -3379,7 +3930,7 @@ nsHTMLEditor::ApplyDocumentOrOverrideStyleSheet(const nsString& aURL, PRBool aOv #ifdef XP_MAC #pragma mark - -#pragma mark --- nsIEditorMailSupport methods --- +#pragma mark nsIEditorMailSupport methods #pragma mark - #endif @@ -3605,130 +4156,10 @@ nsHTMLEditor::GetEmbeddedObjects(nsISupportsArray** aNodeList) #ifdef XP_MAC #pragma mark - -#pragma mark --- nsIEditor overrides --- +#pragma mark nsIEditor overrides #pragma mark - #endif -// -// Figure out what formatting needs to go with this node, and insert it. -// -NS_IMETHODIMP -nsHTMLEditor::InsertFormattingForNode(nsIDOMNode* aNode) -{ - // New formatting attempt: just mark the node dirty. - nsCOMPtr element (do_QueryInterface(aNode)); - if (element) - element->SetAttribute("_moz_dirty", ""); - return NS_OK; - -#ifdef FORMATTING_NODES_IN_DOM - nsresult res; - - // Don't insert any formatting unless it's an element node - PRUint16 nodeType; - res = aNode->GetNodeType(&nodeType); - if (NS_FAILED(res)) - return res; - if (nodeType != nsIDOMNode::ELEMENT_NODE) - return NS_OK; - - // Don't insert formatting if we're a plaintext editor. - // The newlines get considered to be part of the text. - // This, of course, makes the html look lousy, but we're expecting - // that plaintext editors will only output plaintext, not html. - if (mFlags & nsHTMLEditor::eEditorPlaintextMask) - return NS_OK; - - nsCOMPtr parent; - res = aNode->GetParentNode(getter_AddRefs(parent)); - if (NS_FAILED(res)) - return res; - -#ifdef DEBUG_formatting - nsString namestr; - aNode->GetNodeName(namestr); - //DumpContentTree(); - char* nodename = namestr.ToNewCString(); - printf("Inserting formatting for node <%s> at offset %d\n", - nodename, GetIndexOf(parent, aNode)); -#endif /* DEBUG_formatting */ - - // If it has children, first iterate over the children: - nsCOMPtr child; - res = aNode->GetFirstChild(getter_AddRefs(child)); - if (NS_SUCCEEDED(res) && child) - { -#ifdef DEBUG_formatting - printf("%s: Iterating over children\n", nodename); -#endif /* DEBUG_formatting */ - - while (child) - { -#ifdef DEBUG_formatting - printf("%s child\n", nodename); -#endif /* DEBUG_formatting */ - InsertFormattingForNode(child); - nsCOMPtr nextSib; - child->GetNextSibling(getter_AddRefs(nextSib)); - child = nextSib; - } - } - - nsAutoString newline ("\n"); - - // - // XXX Would be nice, ultimately, to format according to user prefs. - // - res = NS_OK; - - PRInt32 offset = GetIndexOf(parent, aNode); - - if (nsHTMLEditUtils::IsBreak(aNode) && !nsHTMLEditUtils::IsMozBR(aNode)) - { - // After the close tag - res = InsertNoneditableTextNode(parent, offset+1, newline); - } - - else if (nsEditor::IsBlockNode(aNode)) - { -#ifdef DEBUG_formatting - printf("Block node %s at offset %d\n-----------\n", nodename, offset); - DumpContentTree(); -#endif /* DEBUG_formatting */ - if (!nsHTMLEditUtils::IsListItem(aNode)) - { - // After the close tag - InsertNoneditableTextNode(parent, offset+1, newline); -#ifdef DEBUG_formatting - printf("Now %s has offset %d\n-----------\n", - nodename, GetIndexOf(parent, aNode)); - DumpContentTree(); - printf("----------------\n"); -#endif /* DEBUG_formatting */ - } - - // Before the open tag - res = InsertNoneditableTextNode(parent, offset, newline); -#ifdef DEBUG_formatting - printf("And NOW, %s has offset %d\n-----------\n", nodename, GetIndexOf(parent, aNode)); - DumpContentTree(); - printf("----------------\n"); -#endif /* DEBUG_formatting */ - } - - // Some inline tags for which we might want formatting: - else if (nsHTMLEditUtils::IsImage(aNode)) - { - res = InsertNoneditableTextNode(parent, offset, newline); - } - -#ifdef DEBUG_formatting - Recycle(nodename); -#endif /* DEBUG_formatting */ - - return res; -#endif // FORMATTING_NODES_IN_DOM -} NS_IMETHODIMP nsHTMLEditor::Undo(PRUint32 aCount) @@ -4503,7 +4934,7 @@ nsHTMLEditor::DebugUnitTests(PRInt32 *outNumTests, PRInt32 *outNumTestsFailed) #ifdef XP_MAC #pragma mark - -#pragma mark --- nsIEditorIMESupport overrides --- +#pragma mark nsIEditorIMESupport overrides #pragma mark - #endif @@ -4552,7 +4983,7 @@ nsHTMLEditor::SetCompositionString(const nsString& aCompositionString, nsIPrivat #ifdef XP_MAC #pragma mark - -#pragma mark --- StyleSheet utils --- +#pragma mark StyleSheet utils #pragma mark - #endif @@ -4598,7 +5029,7 @@ void nsHTMLEditor::ApplyStyleSheetToPresShellDocument(nsICSSStyleSheet* aSheet, #ifdef XP_MAC #pragma mark - -#pragma mark --- nsEditor overrides --- +#pragma mark nsEditor overrides #pragma mark - #endif @@ -4618,11 +5049,11 @@ nsHTMLEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection) /** All editor operations which alter the doc should be followed * with a call to EndOperation, naming the action and direction */ NS_IMETHODIMP -nsHTMLEditor::EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection, PRBool aSetSelection) +nsHTMLEditor::EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection) { if (! ((opID==kOpInsertText) || (opID==kOpInsertIMEText)) ) ClearInlineStylesCache(); - if (mRules) return mRules->AfterEdit(opID, aDirection, aSetSelection); + if (mRules) return mRules->AfterEdit(opID, aDirection); return NS_OK; } @@ -4673,7 +5104,7 @@ nsHTMLEditor::SelectEntireDocument(nsIDOMSelection *aSelection) #ifdef XP_MAC #pragma mark - -#pragma mark --- Random methods --- +#pragma mark Random methods #pragma mark - #endif @@ -4716,7 +5147,7 @@ nsHTMLEditor::SetTypeInStateForProperty(TypeInState &aTypeInState, aTypeInState.GetEnumForName(aPropName, propEnum); if (nsIEditProperty::b==aPropName || nsIEditProperty::i==aPropName || nsIEditProperty::u==aPropName) { - if (PR_TRUE==aTypeInState.IsSet(propEnum)) + if (aTypeInState.IsSet(propEnum)) { // toggle currently set boldness aTypeInState.UnSet(propEnum); } @@ -4738,7 +5169,7 @@ nsHTMLEditor::SetTypeInStateForProperty(TypeInState &aTypeInState, aTypeInState.GetEnumForName(attribute, attrEnum); if (nsIEditProperty::color==attribute || nsIEditProperty::face==attribute || nsIEditProperty::size==attribute) { - if (PR_TRUE==aTypeInState.IsSet(attrEnum)) + if (aTypeInState.IsSet(attrEnum)) { if (nsnull==aValue) { aTypeInState.UnSet(attrEnum); @@ -4754,7 +5185,7 @@ nsHTMLEditor::SetTypeInStateForProperty(TypeInState &aTypeInState, PRBool all = PR_FALSE; PRBool first = PR_FALSE; GetInlineProperty(aPropName, aAttribute, aValue, first, any, all); // operates on current selection - if (PR_FALSE==all) { + if (!all) { aTypeInState.SetPropValue(attrEnum, *aValue); } } @@ -4813,7 +5244,7 @@ void nsHTMLEditor::IsTextPropertySetByContent(nsIDOMNode *aNode, else { found = PR_TRUE; } - if (PR_TRUE==found) + if (found) { aIsSet = PR_TRUE; break; @@ -4933,7 +5364,7 @@ nsresult nsHTMLEditor::GetAbsoluteOffsetsForPoints(nsIDOMNode *aInStartNode, { nsCOMPtrcurrentNode = do_QueryInterface(textNode); if (!currentNode) {return NS_ERROR_NO_INTERFACE;} - if (PR_TRUE==IsEditable(currentNode)) + if (IsEditable(currentNode)) { if (currentNode.get() == aInStartNode) { @@ -5069,7 +5500,7 @@ void nsHTMLEditor::ResetTextSelectionForRange(nsIDOMNode *aParent, { PRUint32 length; textNode->GetLength(&length); - if ((PR_FALSE==setStart) && aOutStartOffset<=(PRInt32)(totalLength+length)) + if ((!setStart) && aOutStartOffset<=(PRInt32)(totalLength+length)) { setStart = PR_TRUE; startNode = do_QueryInterface(textNode); @@ -5119,7 +5550,7 @@ nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, PRBool nodeIsInline; PRBool nodeIsBlock=PR_FALSE; IsNodeInline(aNode, nodeIsInline); - if (PR_FALSE==nodeIsInline) + if (!nodeIsInline) { nsresult QIResult; nsCOMPtrnodeAsText; @@ -5129,7 +5560,7 @@ nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, } } // if aNode is the block parent, then the node to reparent is one of its children - if (PR_TRUE==nodeIsBlock) + if (nodeIsBlock) { res = aNode->QueryInterface(NS_GET_IID(nsIDOMNode), getter_AddRefs(blockParentElement)); if (NS_SUCCEEDED(res) && blockParentElement) { @@ -5151,7 +5582,7 @@ nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, blockParentElement->GetTagName(parentTag); PRBool isRoot; IsRootTag(parentTag, isRoot); - if (PR_TRUE==isRoot) + if (isRoot) { // if nodeToReParent is a text node, we have Text. // re-parent Text into a new at the offset of Text in @@ -5180,7 +5611,7 @@ nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, else { // the block parent is not a ROOT, // for the selected block content, transform blockParentNode - if (((eReplaceParent==aTransformation) && (PR_FALSE==parentTag.EqualsIgnoreCase(aParentTag))) || + if (((eReplaceParent==aTransformation) && (!parentTag.EqualsIgnoreCase(aParentTag))) || (eInsertParent==aTransformation)) { if (gNoisy) { DebugDumpContent(); } // DEBUG @@ -5190,7 +5621,7 @@ nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, { PRBool hasChildren; blockParentNode->HasChildNodes(&hasChildren); - if (PR_FALSE==hasChildren) + if (!hasChildren) { res = nsEditor::DeleteNode(blockParentNode); if (gNoisy) @@ -5260,7 +5691,7 @@ nsHTMLEditor::ReParentBlockContent(nsIDOMNode *aNode, IsRootTag(blockParentTag, isRootBlock); } - if (PR_TRUE==isRootBlock) + if (isRootBlock) { // we're creating a block element where a block element did not previously exist removeBreakBefore = PR_TRUE; removeBreakAfter = PR_TRUE; @@ -5268,7 +5699,7 @@ nsHTMLEditor::ReParentBlockContent(nsIDOMNode *aNode, // apply the transformation PRInt32 offsetInParent=0; - if (eInsertParent==aTransformation || PR_TRUE==isRootBlock) + if (eInsertParent==aTransformation || isRootBlock) { res = GetChildOffset(leftNode, blockParentNode, offsetInParent); NS_ASSERTION((NS_SUCCEEDED(res)), "bad res from GetChildOffset"); @@ -5328,16 +5759,10 @@ nsHTMLEditor::ReParentBlockContent(nsIDOMNode *aNode, while (NS_SUCCEEDED(res) && childNode) { childNode->GetPreviousSibling(getter_AddRefs(previousSiblingNode)); - // explicitly delete of childNode from it's current parent - // can't just rely on DOM semantics of InsertNode doing the delete implicitly, doesn't undo! - res = nsEditor::DeleteNode(childNode); - if (NS_SUCCEEDED(res)) + res = nsEditor::MoveNode(childNode, *aNewParentNode, 0); + if (gNoisy) { - res = nsEditor::InsertNode(childNode, *aNewParentNode, 0); - if (gNoisy) - { - printf("re-parented sibling node %p\n", childNode.get()); - } + printf("re-parented sibling node %p\n", childNode.get()); } if (childNode==leftNode || rightNode==leftNode) { break; @@ -5382,7 +5807,7 @@ nsHTMLEditor::ReParentBlockContent(nsIDOMNode *aNode, } } } - if ((NS_SUCCEEDED(res)) && (PR_TRUE==removeBlockParent)) + if ((NS_SUCCEEDED(res)) && (removeBlockParent)) { // we determined we need to remove the previous block parent. Do it! // go through list backwards so deletes don't interfere with the iteration nsCOMPtr childNodes; @@ -5402,10 +5827,7 @@ nsHTMLEditor::ReParentBlockContent(nsIDOMNode *aNode, res = childNodes->Item(i, getter_AddRefs(childNode)); if ((NS_SUCCEEDED(res)) && (childNode)) { - res = DeleteNode(childNode); - if (NS_SUCCEEDED(res)) { - res = InsertNode(childNode, grandParent, offsetInParent); - } + res = MoveNode(childNode, grandParent, offsetInParent); } } if (gNoisy) { printf("removing the old block parent %p\n", blockParentNode.get()); } @@ -5505,7 +5927,7 @@ nsHTMLEditor::RemoveParagraphStyleFromBlockContent(nsIDOMRange *aRange) blockParentElement->GetTagName(blockParentTag); PRBool isSubordinateBlock; IsSubordinateBlock(blockParentTag, isSubordinateBlock); - if (PR_FALSE==isSubordinateBlock) { + if (!isSubordinateBlock) { break; } else @@ -5531,12 +5953,8 @@ nsHTMLEditor::RemoveParagraphStyleFromBlockContent(nsIDOMRange *aRange) res = childNodes->Item(i, getter_AddRefs(childNode)); if ((NS_SUCCEEDED(res)) && (childNode)) { - res = DeleteNode(childNode); - if (NS_SUCCEEDED(res)) - { - res = InsertNode(childNode, grandParent, offsetInParent); - if (NS_FAILED(res)) return res; - } + res = MoveNode(childNode, grandParent, offsetInParent); + if (NS_FAILED(res)) return res; } } if (NS_SUCCEEDED(res)) @@ -5629,9 +6047,7 @@ nsHTMLEditor::RemoveParentFromBlockContent(const nsString &aParentTag, nsIDOMRan res = childNodes->Item(i, getter_AddRefs(childNode)); if (NS_FAILED(res)) return res; if (!childNode) return NS_ERROR_NULL_POINTER; - res = DeleteNode(childNode); - if (NS_FAILED(res)) return res; - res = InsertNode(childNode, grandParent, offsetInParent); + res = MoveNode(childNode, grandParent, offsetInParent); if (NS_FAILED(res)) return res; } res = DeleteNode(parentElement); @@ -5639,7 +6055,7 @@ nsHTMLEditor::RemoveParentFromBlockContent(const nsString &aParentTag, nsIDOMRan break; } - else if (PR_TRUE==isRoot) { // hit a local root node, terminate loop + else if (isRoot) { // hit a local root node, terminate loop break; } res = parentElement->GetParentNode(getter_AddRefs(parentNode)); @@ -5765,10 +6181,10 @@ nsHTMLEditor::IsRootTag(nsString &aTag, PRBool &aIsTag) static char tdTag[] = "td"; static char thTag[] = "th"; static char captionTag[] = "caption"; - if (PR_TRUE==aTag.EqualsIgnoreCase(bodyTag) || - PR_TRUE==aTag.EqualsIgnoreCase(tdTag) || - PR_TRUE==aTag.EqualsIgnoreCase(thTag) || - PR_TRUE==aTag.EqualsIgnoreCase(captionTag) ) + if (aTag.EqualsIgnoreCase(bodyTag) || + aTag.EqualsIgnoreCase(tdTag) || + aTag.EqualsIgnoreCase(thTag) || + aTag.EqualsIgnoreCase(captionTag) ) { aIsTag = PR_TRUE; } @@ -5793,18 +6209,18 @@ nsHTMLEditor::IsSubordinateBlock(nsString &aTag, PRBool &aIsTag) static char li[] = "li"; static char dt[] = "dt"; static char dd[] = "dd"; - if (PR_TRUE==aTag.EqualsIgnoreCase(p) || - PR_TRUE==aTag.EqualsIgnoreCase(h1) || - PR_TRUE==aTag.EqualsIgnoreCase(h2) || - PR_TRUE==aTag.EqualsIgnoreCase(h3) || - PR_TRUE==aTag.EqualsIgnoreCase(h4) || - PR_TRUE==aTag.EqualsIgnoreCase(h5) || - PR_TRUE==aTag.EqualsIgnoreCase(h6) || - PR_TRUE==aTag.EqualsIgnoreCase(address) || - PR_TRUE==aTag.EqualsIgnoreCase(pre) || - PR_TRUE==aTag.EqualsIgnoreCase(li) || - PR_TRUE==aTag.EqualsIgnoreCase(dt) || - PR_TRUE==aTag.EqualsIgnoreCase(dd) ) + if (aTag.EqualsIgnoreCase(p) || + aTag.EqualsIgnoreCase(h1) || + aTag.EqualsIgnoreCase(h2) || + aTag.EqualsIgnoreCase(h3) || + aTag.EqualsIgnoreCase(h4) || + aTag.EqualsIgnoreCase(h5) || + aTag.EqualsIgnoreCase(h6) || + aTag.EqualsIgnoreCase(address) || + aTag.EqualsIgnoreCase(pre) || + aTag.EqualsIgnoreCase(li) || + aTag.EqualsIgnoreCase(dt) || + aTag.EqualsIgnoreCase(dd) ) { aIsTag = PR_TRUE; } @@ -5920,7 +6336,7 @@ nsHTMLEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parent PRBool testCollapsed; debugResult = selection->GetIsCollapsed(&testCollapsed); NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion"); - NS_ASSERTION(PR_TRUE==testCollapsed, "selection not reset after deletion"); + NS_ASSERTION(testCollapsed, "selection not reset after deletion"); } #endif } @@ -6131,7 +6547,7 @@ nsHTMLEditor::SetTextPropertiesForNode(nsIDOMNode *aNode, PRBool textPropertySet; nsCOMPtrresultNode; IsTextPropertySetByContent(aNode, aPropName, aAttribute, aValue, textPropertySet, getter_AddRefs(resultNode)); - if (PR_FALSE==textPropertySet) + if (!textPropertySet) { if (aValue && 0!=aValue->Length()) { @@ -6161,15 +6577,11 @@ nsHTMLEditor::SetTextPropertiesForNode(nsIDOMNode *aNode, // XXX: need to loop for aStartOffset!=aEndOffset-1? PRInt32 offsetInParent = aStartOffset; // remember where aNode was in aParent if (NS_SUCCEEDED(result) && child) - { // remove child from parent - result = nsEditor::DeleteNode(child); + { // move child + result = nsEditor::MoveNode(child, newStyleNode, 0); if (NS_SUCCEEDED(result)) - { // put child into the newStyleNode - result = nsEditor::InsertNode(child, newStyleNode, 0); - if (NS_SUCCEEDED(result)) - { // put newStyleNode in parent where child was - result = nsEditor::InsertNode(newStyleNode, parent, offsetInParent); - } + { // put newStyleNode in parent where child was + result = nsEditor::InsertNode(newStyleNode, parent, offsetInParent); } } } @@ -6242,7 +6654,7 @@ NS_IMETHODIMP nsHTMLEditor::MoveContentOfNodeIntoNewParent(nsIDOMNode *aNode, // ??? can we really compute this? } */ - if (PR_FALSE==done) + if (!done) { // if we've ended up with an empty text node, just delete it and we're done nsCOMPtrnewChildNodeAsChar; @@ -6259,7 +6671,7 @@ NS_IMETHODIMP nsHTMLEditor::MoveContentOfNodeIntoNewParent(nsIDOMNode *aNode, } } // move the new child node into the new parent - if (PR_FALSE==done) + if (!done) { // first, move the new parent into the correct location PRInt32 offsetInParent; @@ -6277,22 +6689,7 @@ NS_IMETHODIMP nsHTMLEditor::MoveContentOfNodeIntoNewParent(nsIDOMNode *aNode, if (NS_SUCCEEDED(result)) { // then move the new child into the new parent node - result = nsEditor::DeleteNode(newChildNode); - if (NS_SUCCEEDED(result)) - { - result = nsEditor::InsertNode(newChildNode, aNewParentNode, 0); - if (NS_SUCCEEDED(result)) - { - // set the selection - nsCOMPtrselection; - result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - selection->Collapse(newChildNode, 0); - PRInt32 endOffset = aEndOffset-aStartOffset; - selection->Extend(newChildNode, endOffset); - } - } + result = nsEditor::MoveNode(newChildNode, aNewParentNode, 0); } } } @@ -6316,12 +6713,12 @@ nsHTMLEditor::SetTextPropertiesForNodesWithSameParent(nsIDOMNode *aStartNode, const nsString *aAttribute, const nsString *aValue) { - if (gNoisy) { printf("---------- start nsTextEditor::SetTextPropertiesForNodesWithSameParent ----------\n"); } + if (gNoisy) { printf("- start nsTextEditor::SetTextPropertiesForNodesWithSameParent -\n"); } nsresult result=NS_OK; PRBool textPropertySet; nsCOMPtrresultNode; IsTextPropertySetByContent(aStartNode, aPropName, aAttribute, aValue, textPropertySet, getter_AddRefs(resultNode)); - if (PR_FALSE==textPropertySet) + if (!textPropertySet) { result = RemoveTextPropertiesForNodeWithDifferentParents(aStartNode, aStartOffset, aEndNode, aEndOffset, @@ -6446,70 +6843,43 @@ nsHTMLEditor::MoveContiguousContentIntoNewParent(nsIDOMNode *aStartNode, result = startNode->GetNextSibling(getter_AddRefs(intermediateNode)); if (NS_SUCCEEDED(result)) { - result = nsEditor::DeleteNode(startNode); - if (gNoisy) printf("just after DeleteNode 1\n"); + PRInt32 childIndex=0; + result = nsEditor::MoveNode(startNode, aNewParentNode, childIndex); + if (gNoisy) printf("just after MoveNode 2\n"); if (gNoisy) {DebugDumpContent(); } // DEBUG - if (NS_SUCCEEDED(result)) - { - PRInt32 childIndex=0; - result = nsEditor::InsertNode(startNode, aNewParentNode, childIndex); - if (gNoisy) printf("just after InsertNode 2\n"); - if (gNoisy) {DebugDumpContent(); } // DEBUG - childIndex++; - if (NS_SUCCEEDED(result)) - { // move all the intermediate nodes into the new parent node - nsCOMPtrnextSibling; - while (intermediateNode.get() != endNode.get()) + childIndex++; + if (NS_SUCCEEDED(result)) + { // move all the intermediate nodes into the new parent node + nsCOMPtrnextSibling; + while (intermediateNode.get() != endNode.get()) + { + if (!intermediateNode) + result = NS_ERROR_NULL_POINTER; + if (NS_FAILED(result)) { - if (!intermediateNode) - result = NS_ERROR_NULL_POINTER; - if (NS_FAILED(result)) { - break; - } - // get the next sibling before moving the current child!!! - intermediateNode->GetNextSibling(getter_AddRefs(nextSibling)); - result = nsEditor::DeleteNode(intermediateNode); - if (gNoisy) printf("just after DeleteNode 3\n"); - if (gNoisy) {DebugDumpContent(); } // DEBUG - if (NS_SUCCEEDED(result)) - { - result = nsEditor::InsertNode(intermediateNode, aNewParentNode, childIndex); - if (gNoisy) printf("just after InsertNode 4\n"); - if (gNoisy) {DebugDumpContent(); } // DEBUG - childIndex++; - } - intermediateNode = do_QueryInterface(nextSibling); - } - if (NS_SUCCEEDED(result)) - { // move the left half of the end node into the new parent node - result = nsEditor::DeleteNode(newRightNode); - if (gNoisy) printf("just after DeleteNode 5\n"); - if (gNoisy) {DebugDumpContent(); } // DEBUG - if (NS_SUCCEEDED(result)) - { - result = nsEditor::InsertNode(newRightNode, aNewParentNode, childIndex); - if (gNoisy) printf("just after InsertNode 5\n"); - if (gNoisy) {DebugDumpContent(); } // DEBUG - // now set the selection - if (NS_SUCCEEDED(result)) - { - nsCOMPtrselection; - result = GetSelection(getter_AddRefs(selection)); - if (NS_FAILED(result)) return result; - if (!selection) return NS_ERROR_NULL_POINTER; - selection->Collapse(startNode, startOffset); - selection->Extend(newRightNode, endOffset); - } - } + break; } + // get the next sibling before moving the current child!!! + intermediateNode->GetNextSibling(getter_AddRefs(nextSibling)); + result = nsEditor::MoveNode(intermediateNode, aNewParentNode, childIndex); + if (gNoisy) printf("just after MoveNode 4\n"); + if (gNoisy) {DebugDumpContent(); } // DEBUG + childIndex++; } + intermediateNode = do_QueryInterface(nextSibling); + } + if (NS_SUCCEEDED(result)) + { // move the left half of the end node into the new parent node + result = nsEditor::MoveNode(newRightNode, aNewParentNode, childIndex); + if (gNoisy) printf("just after MoveNode 5\n"); + if (gNoisy) {DebugDumpContent(); } // DEBUG } } } } } } - if (gNoisy) { printf("--- end nsTextEditor::MoveContiguousContentIntoNewParent ---\n"); } + if (gNoisy) { printf(" end nsTextEditor::MoveContiguousContentIntoNewParent \n"); } if (gNoisy) {DebugDumpContent(); } // DEBUG return result; } @@ -6519,8 +6889,8 @@ NS_IMETHODIMP nsHTMLEditor::IsLeafThatTakesInlineStyle(const nsString *aTag, { if (!aTag) { return NS_ERROR_NULL_POINTER; } - aResult = (PRBool)((PR_FALSE==aTag->EqualsIgnoreCase("br")) && - (PR_FALSE==aTag->EqualsIgnoreCase("hr")) ); + aResult = (PRBool)((!aTag->EqualsIgnoreCase("br")) && + (!aTag->EqualsIgnoreCase("hr")) ); return NS_OK; } @@ -6575,7 +6945,7 @@ nsHTMLEditor::SetTextPropertiesForNodeWithDifferentParents(nsIDOMRange *aRange, // find our starting point PRBool startIsText = IsTextNode(aStartNode); nsCOMPtrstartContent; - if (PR_TRUE==startIsText) { + if (startIsText) { startContent = do_QueryInterface(aStartNode); } else { @@ -6586,7 +6956,7 @@ nsHTMLEditor::SetTextPropertiesForNodeWithDifferentParents(nsIDOMRange *aRange, // find our ending point PRBool endIsText = IsTextNode(aEndNode); nsCOMPtrendContent; - if (PR_TRUE==endIsText) { + if (endIsText) { endContent = do_QueryInterface(aEndNode); } else @@ -6622,7 +6992,7 @@ nsHTMLEditor::SetTextPropertiesForNodeWithDifferentParents(nsIDOMRange *aRange, { PRBool canContainChildren; content->CanContainChildren(canContainChildren); - if (PR_FALSE==canContainChildren) + if (!canContainChildren) { nsEditor::GetTagString(node,tag); PRBool processLeaf; @@ -6634,7 +7004,7 @@ nsHTMLEditor::SetTextPropertiesForNodeWithDifferentParents(nsIDOMRange *aRange, PRBool textPropertySet; nsCOMPtrresultNode; IsTextPropertySetByContent(node, aPropName, aAttribute, aValue, textPropertySet, getter_AddRefs(resultNode)); - if (PR_FALSE==textPropertySet) + if (!textPropertySet) { if (gNoisy) { printf("property not set\n"); } node->GetParentNode(getter_AddRefs(parent)); @@ -6642,7 +7012,7 @@ nsHTMLEditor::SetTextPropertiesForNodeWithDifferentParents(nsIDOMRange *aRange, nsCOMPtrparentContent; parentContent = do_QueryInterface(parent); nsCOMPtrparentNode = do_QueryInterface(parent); - if (PR_TRUE==IsTextNode(node)) + if (IsTextNode(node)) { startOffset = 0; result = GetLengthOfDOMNode(node, (PRUint32&)endOffset); @@ -6763,7 +7133,7 @@ nsHTMLEditor::RemoveTextPropertiesForNode(nsIDOMNode *aNode, PRBool textPropertySet; nsCOMPtrresultNode; IsTextPropertySetByContent(aNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode)); - if (PR_TRUE==textPropertySet) + if (textPropertySet) { nsCOMPtrparent; // initially set to first interior parent node to process nsCOMPtrnewMiddleNode; // this will be the middle node after any required splits @@ -6823,7 +7193,7 @@ nsHTMLEditor::RemoveTextPropertiesForNode(nsIDOMNode *aNode, { const PRUnichar *unicodeString; aPropName->GetUnicode(&unicodeString); - if (PR_FALSE==tag.EqualsIgnoreCase(unicodeString)) + if (!tag.EqualsIgnoreCase(unicodeString)) { PRInt32 offsetInParent; result = GetChildOffset(newMiddleNode, parent, offsetInParent); @@ -6918,35 +7288,29 @@ nsHTMLEditor::RemoveTextPropertiesForNode(nsIDOMNode *aNode, result = parent->GetParentNode(getter_AddRefs(grandParent)); if (NS_SUCCEEDED(result) && grandParent) { - if (gNoisy) { printf("* deleting middle node %p\n", newMiddleNode.get());} - result = nsEditor::DeleteNode(newMiddleNode); - if (gNoisy) {DebugDumpContent(); } // DEBUG - if (NS_SUCCEEDED(result)) + PRInt32 position; + result = GetChildOffset(parent, grandParent, position); + if (NS_SUCCEEDED(result)) { - PRInt32 position; - result = GetChildOffset(parent, grandParent, position); + if (insertAfter) + { + if (gNoisy) {printf("insertAfter=PR_TRUE, incr. position\n"); } + position++; + } + if (gNoisy) { + printf("* inserting node %p in grandparent %p at offset %d\n", + newMiddleNode.get(), grandParent.get(), position); + } + result = nsEditor::MoveNode(newMiddleNode, grandParent, position); + if (gNoisy) {DebugDumpContent(); } // DEBUG if (NS_SUCCEEDED(result)) { - if (PR_TRUE==insertAfter) - { - if (gNoisy) {printf("insertAfter=PR_TRUE, incr. position\n"); } - position++; - } - if (gNoisy) { - printf("* inserting node %p in grandparent %p at offset %d\n", - newMiddleNode.get(), grandParent.get(), position); - } - result = nsEditor::InsertNode(newMiddleNode, grandParent, position); - if (gNoisy) {DebugDumpContent(); } // DEBUG - if (NS_SUCCEEDED(result)) - { - PRBool hasChildren=PR_TRUE; - parent->HasChildNodes(&hasChildren); - if (PR_FALSE==hasChildren) { - if (gNoisy) { printf("* deleting empty style node %p\n", parent.get());} - result = nsEditor::DeleteNode(parent); - if (gNoisy) {DebugDumpContent(); } // DEBUG - } + PRBool hasChildren=PR_TRUE; + parent->HasChildNodes(&hasChildren); + if (!hasChildren) { + if (gNoisy) { printf("* deleting empty style node %p\n", parent.get());} + result = nsEditor::DeleteNode(parent); + if (gNoisy) {DebugDumpContent(); } // DEBUG } } } @@ -6987,7 +7351,7 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar nsIAtom *aPropName, const nsString *aAttribute) { - if (gNoisy) { printf("----- start nsTextEditor::RemoveTextPropertiesForNodeWithDifferentParents-----\n"); } + if (gNoisy) { printf("-- start nsTextEditor::RemoveTextPropertiesForNodeWithDifferentParents--\n"); } nsresult result=NS_OK; if (!aStartNode || !aEndNode || !aParent || !aPropName) return NS_ERROR_NULL_POINTER; @@ -7016,7 +7380,7 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar if ((PRUint32)aStartOffset!=count) { // only do this if at least one child is selected IsTextPropertySetByContent(aStartNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode)); - if (PR_TRUE==textPropertySet) + if (textPropertySet) { result = RemoveTextPropertiesForNode(aStartNode, parent, aStartOffset, count, aPropName, aAttribute); if (0!=aStartOffset) { @@ -7041,7 +7405,7 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar parent = do_QueryInterface(aStartNode); if (!startNode || !parent) {return NS_ERROR_NULL_POINTER;} IsTextPropertySetByContent(startNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode)); - if (PR_TRUE==textPropertySet) + if (textPropertySet) { result = RemoveTextPropertiesForNode(startNode, parent, aStartOffset, aStartOffset+1, aPropName, aAttribute); } @@ -7065,7 +7429,7 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar if (aEndOffset!=0) { // only do this if at least one child is selected IsTextPropertySetByContent(endNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode)); - if (PR_TRUE==textPropertySet) + if (textPropertySet) { result = RemoveTextPropertiesForNode(endNode, parent, 0, aEndOffset, aPropName, aAttribute); if (0!=aEndOffset && (((PRInt32)count)!=aEndOffset)) { @@ -7093,7 +7457,7 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar parent = do_QueryInterface(aEndNode); if (!endNode || !parent) {return NS_ERROR_NULL_POINTER;} IsTextPropertySetByContent(endNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode)); - if (PR_TRUE==textPropertySet) + if (textPropertySet) { result = RemoveTextPropertiesForNode(endNode, parent, aEndOffset-1, aEndOffset, aPropName, aAttribute); } @@ -7117,7 +7481,7 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar if (NS_FAILED(result)) { return result; } if (!range) { return NS_ERROR_NULL_POINTER; } // compute the start node - if (PR_TRUE==skippedStartNode) + if (skippedStartNode) { nsCOMPtrtempNode = do_QueryInterface(startNode); nsEditor::GetNextNode(startNode, PR_TRUE, getter_AddRefs(tempNode)); @@ -7199,16 +7563,10 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar while (NS_SUCCEEDED(result) && childNode) { childNode->GetPreviousSibling(getter_AddRefs(previousSiblingNode)); - // explicitly delete of childNode from styleNode - // can't just rely on DOM semantics of InsertNode doing the delete implicitly, doesn't undo! - result = nsEditor::DeleteNode(childNode); - if (NS_SUCCEEDED(result)) + result = nsEditor::MoveNode(childNode, parentNode, position); + if (gNoisy) { - result = nsEditor::InsertNode(childNode, parentNode, position); - if (gNoisy) - { - printf("deleted next sibling node %p\n", childNode.get()); - } + printf("deleted next sibling node %p\n", childNode.get()); } childNode = do_QueryInterface(previousSiblingNode); } // end while loop @@ -7232,13 +7590,13 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar if (!selection) return NS_ERROR_NULL_POINTER; // set the sel. start point. if we skipped the start node, just use it - if (PR_TRUE==skippedStartNode) + if (skippedStartNode) startNode = do_QueryInterface(aStartNode); result = selection->Collapse(startNode, rangeStartOffset); if (NS_FAILED(result)) return result; // set the sel. end point. if we skipped the end node, just use it - if (PR_TRUE==skippedEndNode) + if (skippedEndNode) endNode = do_QueryInterface(aEndNode); result = selection->Extend(endNode, rangeEndOffset); if (NS_FAILED(result)) return result; @@ -7280,7 +7638,7 @@ nsHTMLEditor::CollapseAdjacentTextNodes(nsIDOMSelection *aInSelection) PRBool isCollapsed; aInSelection->GetIsCollapsed(&isCollapsed); - if (PR_TRUE==isCollapsed) { return NS_OK; } // no need to scan collapsed selection + if (isCollapsed) { return NS_OK; } // no need to scan collapsed selection // store info about selection endpoints so we can re-establish selection after collapsing text nodes // XXX: won't work for multiple selections (this will create a single selection from anchor to focus) @@ -7485,9 +7843,9 @@ nsHTMLEditor::GetNextElementByTagName(nsIDOMElement *aCurrentElement, NS_IMETHODIMP nsHTMLEditor::SetSelectionAtDocumentStart(nsIDOMSelection *aSelection) { - nsCOMPtr bodyElement; - nsresult res = GetBodyElement(getter_AddRefs(bodyElement)); - if (NS_SUCCEEDED(res)) + nsCOMPtr bodyElement; + nsresult res = GetBodyElement(getter_AddRefs(bodyElement)); + if (NS_SUCCEEDED(res)) { if (!bodyElement) return NS_ERROR_NULL_POINTER; res = aSelection->Collapse(bodyElement,0); @@ -7508,10 +7866,6 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) ForceCompositionEnd(); - // wrap with txn batching, rules sniffing, and selection preservation code - nsAutoEditBatch batchIt(this); - nsAutoRules beginRulesSniffing(this, kOpSetTextProperty, nsIEditor::eNext, PR_TRUE); - // Get the selection nsCOMPtrselection; nsresult res = GetSelection(getter_AddRefs(selection)); @@ -7531,6 +7885,11 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) return NS_OK; } + // wrap with txn batching, rules sniffing, and selection preservation code + nsAutoEditBatch batchIt(this); + nsAutoRules beginRulesSniffing(this, kOpSetTextProperty, nsIEditor::eNext); + nsAutoSelectionReset selectionResetter(selection, this); + // get selection range enumerator nsCOMPtr enumerator; res = selection->GetEnumerator(getter_AddRefs(enumerator)); @@ -7548,6 +7907,10 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) nsCOMPtr range( do_QueryInterface(currentItem) ); + // adjust range to include any ancestors who's children are entirely selected + res = PromoteInlineRange(range); + if (NS_FAILED(res)) return res; + // check for easy case: both range endpoints in same text node nsCOMPtr startNode, endNode; res = range->GetStartParent(getter_AddRefs(startNode)); @@ -7556,6 +7919,9 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) if (NS_FAILED(res)) return res; if ((startNode == endNode) && IsTextNode(startNode)) { + // MOOSE: workaround for selection bug: + selection->ClearSelection(); + PRInt32 startOffset, endOffset; range->GetStartOffset(&startOffset); range->GetEndOffset(&endOffset); @@ -7600,12 +7966,18 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) if (NS_FAILED(res)) return res; node = do_QueryInterface(content); if (!node) return NS_ERROR_FAILURE; - isupports = do_QueryInterface(node); - arrayOfNodes->AppendElement(isupports); + if (IsEditable(node)) + { + isupports = do_QueryInterface(node); + arrayOfNodes->AppendElement(isupports); + } res = iter->Next(); if (NS_FAILED(res)) return res; } + // MOOSE: workaround for selection bug: + selection->ClearSelection(); + // now that we have the list, do the font size change on each node PRUint32 listCount; PRUint32 j; @@ -7622,7 +7994,7 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) // now check the start and end parents of the range to see if they need to // be seperately handled (they do if they are text nodes, due to how the // subtree iterator works - it will not have reported them). - if (IsTextNode(startNode)) + if (IsTextNode(startNode) && IsEditable(startNode)) { nsCOMPtr nodeAsText = do_QueryInterface(startNode); PRInt32 startOffset; @@ -7632,7 +8004,7 @@ nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange) res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, textLen); if (NS_FAILED(res)) return res; } - if (IsTextNode(endNode)) + if (IsTextNode(endNode) && IsEditable(endNode)) { nsCOMPtr nodeAsText = do_QueryInterface(endNode); PRInt32 endOffset; @@ -7753,108 +8125,284 @@ nsHTMLEditor::RelativeFontChangeOnNode( PRInt32 aSizeChange, return res; } +/////////////////////////////////////////////////////////////////////////// +// GetPriorHTMLSibling: returns the previous editable sibling, if there is +// one within the parent +// +nsresult +nsHTMLEditor::GetPriorHTMLSibling(nsIDOMNode *inNode, nsCOMPtr *outNode) +{ + if (!outNode || !inNode) return NS_ERROR_NULL_POINTER; + nsresult res = NS_OK; + *outNode = nsnull; + nsCOMPtr temp, node = do_QueryInterface(inNode); + + while (1) + { + res = node->GetPreviousSibling(getter_AddRefs(temp)); + if (NS_FAILED(res)) return res; + if (!temp) return NS_OK; // return null sibling + // if it's editable, we're done + if (IsEditable(temp)) break; + // otherwise try again + node = temp; + } + *outNode = temp; + return res; +} + + /////////////////////////////////////////////////////////////////////////// -// ReplaceContainer: replace inNode with a new node (outNode) which is contructed -// to be of type aNodeType. Put inNodes children into outNode. -// Callers responsibility to make sure inNode's children can -// go in outNode. +// GetPriorHTMLSibling: returns the previous editable sibling, if there is +// one within the parent. just like above routine but +// takes a parent/offset instead of a node. +// nsresult -nsHTMLEditor::ReplaceContainer(nsIDOMNode *inNode, - nsCOMPtr *outNode, - const nsString &aNodeType) +nsHTMLEditor::GetPriorHTMLSibling(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outNode) { - if (!inNode || !outNode) - return NS_ERROR_NULL_POINTER; - nsresult res; - nsCOMPtr parent; - PRInt32 offset; - res = GetNodeLocation(inNode, &parent, &offset); - if (NS_FAILED(res)) return res; - res = CreateNode(aNodeType, parent, offset, getter_AddRefs(*outNode)); - if (NS_FAILED(res)) return res; - PRBool bHasMoreChildren; - inNode->HasChildNodes(&bHasMoreChildren); - nsCOMPtr child; - offset = 0; - while (bHasMoreChildren) + if (!outNode || !inParent) return NS_ERROR_NULL_POINTER; + nsresult res = NS_OK; + *outNode = nsnull; + if (!inOffset) return NS_OK; // return null sibling if at offset zero + nsCOMPtr node = nsEditor::GetChildAt(inParent,inOffset-1); + if (IsEditable(node)) { - inNode->GetLastChild(getter_AddRefs(child)); - res = DeleteNode(child); - if (NS_FAILED(res)) return res; - res = InsertNode(child, *outNode, 0); - if (NS_FAILED(res)) return res; - inNode->HasChildNodes(&bHasMoreChildren); + *outNode = node; + return res; + } + // else + return GetPriorHTMLSibling(node, outNode); +} + + + +/////////////////////////////////////////////////////////////////////////// +// GetNextHTMLSibling: returns the next editable sibling, if there is +// one within the parent +// +nsresult +nsHTMLEditor::GetNextHTMLSibling(nsIDOMNode *inNode, nsCOMPtr *outNode) +{ + if (!outNode) return NS_ERROR_NULL_POINTER; + nsresult res = NS_OK; + *outNode = nsnull; + nsCOMPtr temp, node = do_QueryInterface(inNode); + + while (1) + { + res = node->GetNextSibling(getter_AddRefs(temp)); + if (NS_FAILED(res)) return res; + if (!temp) return NS_ERROR_FAILURE; + // if it's editable, we're done + if (IsEditable(temp)) break; + // otherwise try again + node = temp; + } + *outNode = temp; + return res; +} + + + +/////////////////////////////////////////////////////////////////////////// +// GetNextHTMLSibling: returns the next editable sibling, if there is +// one within the parent. just like above routine but +// takes a parent/offset instead of a node. +// +nsresult +nsHTMLEditor::GetNextHTMLSibling(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outNode) +{ + if (!outNode || !inParent) return NS_ERROR_NULL_POINTER; + nsresult res = NS_OK; + *outNode = nsnull; + nsCOMPtr node = nsEditor::GetChildAt(inParent,inOffset); + if (!node) return NS_OK; // return null sibling if no sibling + if (IsEditable(node)) + { + *outNode = node; + return res; + } + // else + return GetPriorHTMLSibling(node, outNode); +} + + + +/////////////////////////////////////////////////////////////////////////// +// GetPriorHTMLNode: returns the previous editable leaf node, if there is +// one within the +// +nsresult +nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode *inNode, nsCOMPtr *outNode) +{ + if (!outNode) return NS_ERROR_NULL_POINTER; + nsresult res = GetPriorNode(inNode, PR_TRUE, getter_AddRefs(*outNode)); + if (NS_FAILED(res)) return res; + + // if it's not in the body, then zero it out + if (*outNode && !nsHTMLEditUtils::InBody(*outNode)) + { + *outNode = nsnull; } - res = DeleteNode(inNode); return res; } /////////////////////////////////////////////////////////////////////////// -// RemoveContainer: remove inNode, reparenting it's children into their -// the parent of inNode -// +// GetPriorHTMLNode: same as above but takes {parent,offset} instead of node +// nsresult -nsHTMLEditor::RemoveContainer(nsIDOMNode *inNode) +nsHTMLEditor::GetPriorHTMLNode(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outNode) { - if (!inNode) - return NS_ERROR_NULL_POINTER; - if (nsHTMLEditUtils::IsBody(inNode)) - return NS_ERROR_UNEXPECTED; - nsresult res; - nsCOMPtr parent; - PRInt32 offset; - res = GetNodeLocation(inNode, &parent, &offset); + if (!outNode) return NS_ERROR_NULL_POINTER; + nsresult res = GetPriorNode(inParent, inOffset, PR_TRUE, getter_AddRefs(*outNode)); if (NS_FAILED(res)) return res; - // loop through the child nodes of inNode and promote them - // into inNode's parent. - PRBool bHasMoreChildren; - inNode->HasChildNodes(&bHasMoreChildren); - nsCOMPtr child; - while (bHasMoreChildren) + // if it's not in the body, then zero it out + if (*outNode && !nsHTMLEditUtils::InBody(*outNode)) { - inNode->GetLastChild(getter_AddRefs(child)); - res = DeleteNode(child); - if (NS_FAILED(res)) return res; - res = InsertNode(child, parent, offset); - if (NS_FAILED(res)) return res; - inNode->HasChildNodes(&bHasMoreChildren); + *outNode = nsnull; } - res = DeleteNode(inNode); return res; } /////////////////////////////////////////////////////////////////////////// -// InsertContainerAbove: insert a new parent for inNode, returned in outNode, -// which is contructed to be of type aNodeType. outNode becomes -// a child of inNode's earlier parent. -// Callers responsibility to make sure inNode's can be child -// of outNode, and outNode can be child of old parent. +// GetNextHTMLNode: returns the previous editable leaf node, if there is +// one within the +// nsresult -nsHTMLEditor::InsertContainerAbove(nsIDOMNode *inNode, - nsCOMPtr *outNode, - const nsString &aNodeType) +nsHTMLEditor::GetNextHTMLNode(nsIDOMNode *inNode, nsCOMPtr *outNode) { - if (!inNode || !outNode) - return NS_ERROR_NULL_POINTER; - nsresult res; - nsCOMPtr parent; - PRInt32 offset; - res = GetNodeLocation(inNode, &parent, &offset); + if (!outNode) return NS_ERROR_NULL_POINTER; + nsresult res = GetNextNode(inNode, PR_TRUE, getter_AddRefs(*outNode)); if (NS_FAILED(res)) return res; - // make new parent, outNode - res = CreateNode(aNodeType, parent, offset, getter_AddRefs(*outNode)); - if (NS_FAILED(res)) return res; - - // put inNode in new parent, outNode - res = DeleteNode(inNode); - if (NS_FAILED(res)) return res; - res = InsertNode(inNode, *outNode, 0); - if (NS_FAILED(res)) return res; - - return NS_OK; + // if it's not in the body, then zero it out + if (*outNode && !nsHTMLEditUtils::InBody(*outNode)) + { + *outNode = nsnull; + } + return res; } + + +/////////////////////////////////////////////////////////////////////////// +// GetNHTMLextNode: same as above but takes {parent,offset} instead of node +// +nsresult +nsHTMLEditor::GetNextHTMLNode(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outNode) +{ + if (!outNode) return NS_ERROR_NULL_POINTER; + nsresult res = GetNextNode(inParent, inOffset, PR_TRUE, getter_AddRefs(*outNode)); + if (NS_FAILED(res)) return res; + + // if it's not in the body, then zero it out + if (*outNode && !nsHTMLEditUtils::InBody(*outNode)) + { + *outNode = nsnull; + } + return res; +} + + +nsresult +nsHTMLEditor::IsFirstEditableChild( nsIDOMNode *aNode, PRBool *aOutIsFirst) +{ + // check parms + if (!aOutIsFirst || !aNode) return NS_ERROR_NULL_POINTER; + + // init out parms + *aOutIsFirst = PR_FALSE; + + // find first editable child and compare it to aNode + nsCOMPtr parent, firstChild; + nsresult res = aNode->GetParentNode(getter_AddRefs(parent)); + if (NS_FAILED(res)) return res; + if (!parent) return NS_ERROR_FAILURE; + res = GetFirstEditableChild(parent, &firstChild); + if (NS_FAILED(res)) return res; + + *aOutIsFirst = (firstChild.get() == aNode); + return res; +} + + +nsresult +nsHTMLEditor::IsLastEditableChild( nsIDOMNode *aNode, PRBool *aOutIsLast) +{ + // check parms + if (!aOutIsLast || !aNode) return NS_ERROR_NULL_POINTER; + + // init out parms + *aOutIsLast = PR_FALSE; + + // find last editable child and compare it to aNode + nsCOMPtr parent, lastChild; + nsresult res = aNode->GetParentNode(getter_AddRefs(parent)); + if (NS_FAILED(res)) return res; + if (!parent) return NS_ERROR_FAILURE; + res = GetLastEditableChild(parent, &lastChild); + if (NS_FAILED(res)) return res; + + *aOutIsLast = (lastChild.get() == aNode); + return res; +} + + +nsresult +nsHTMLEditor::GetFirstEditableChild( nsIDOMNode *aNode, nsCOMPtr *aOutFirstChild) +{ + // check parms + if (!aOutFirstChild || !aNode) return NS_ERROR_NULL_POINTER; + + // init out parms + *aOutFirstChild = nsnull; + + // find first editable child + nsCOMPtr child; + nsresult res = aNode->GetFirstChild(getter_AddRefs(child)); + if (NS_FAILED(res)) return res; + + while (child && !IsEditable(child)) + { + nsCOMPtr tmp; + res = child->GetNextSibling(getter_AddRefs(tmp)); + if (NS_FAILED(res)) return res; + if (!tmp) return NS_ERROR_FAILURE; + child = tmp; + } + + *aOutFirstChild = child; + return res; +} + + +nsresult +nsHTMLEditor::GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr *aOutLastChild) +{ + // check parms + if (!aOutLastChild || !aNode) return NS_ERROR_NULL_POINTER; + + // init out parms + *aOutLastChild = nsnull; + + // find last editable child + nsCOMPtr child; + nsresult res = aNode->GetLastChild(getter_AddRefs(child)); + if (NS_FAILED(res)) return res; + + while (child && !IsEditable(child)) + { + nsCOMPtr tmp; + res = child->GetPreviousSibling(getter_AddRefs(tmp)); + if (NS_FAILED(res)) return res; + if (!tmp) return NS_ERROR_FAILURE; + child = tmp; + } + + *aOutLastChild = child; + return res; +} + diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.h b/mozilla/editor/libeditor/html/nsHTMLEditor.h index 75a3d30895e..a894e63796f 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditor.h +++ b/mozilla/editor/libeditor/html/nsHTMLEditor.h @@ -246,9 +246,6 @@ public: NS_IMETHOD GetFlags(PRUint32 *aFlags); NS_IMETHOD SetFlags(PRUint32 aFlags); - /** Inherited from nsEditor with special cases for some html nodes **/ - NS_IMETHOD InsertFormattingForNode(nsIDOMNode* aNode); - NS_IMETHOD Undo(PRUint32 aCount); NS_IMETHOD Redo(PRUint32 aCount); @@ -276,7 +273,7 @@ public: /** All editor operations which alter the doc should be followed * with a call to EndOperation, naming the action and direction */ - NS_IMETHOD EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection, PRBool aSetSelection); + NS_IMETHOD EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection); /** returns PR_TRUE if aParent can contain a child of type aTag */ PRBool CanContainTag(nsIDOMNode* aParent, const nsString &aTag); @@ -292,7 +289,7 @@ public: protected: - virtual void InitRules(); + NS_IMETHOD InitRules(); /** install the event listeners for the editor * used to be part of Init, but now broken out into a separate method @@ -336,6 +333,10 @@ protected: NS_IMETHOD TabInTable(PRBool inIsShift, PRBool *outHandled); NS_IMETHOD CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPtr *outBRNode, EDirection aSelect = eNone); + NS_IMETHOD JoeCreateBR(nsCOMPtr *aInOutParent, + PRInt32 *aInOutOffset, + nsCOMPtr *outBRNode, + EDirection aSelect); NS_IMETHOD InsertBR(nsCOMPtr *outBRNode); // Table Editing (implemented in nsTableEditor.cpp) @@ -582,17 +583,59 @@ protected: nsresult RelativeFontChangeOnNode( PRInt32 aSizeChange, nsIDOMNode *aNode); - /* helper routines for node/parent manipulations */ - nsresult ReplaceContainer(nsIDOMNode *inNode, nsCOMPtr *outNode, const nsString &aNodeType); - nsresult RemoveContainer(nsIDOMNode *inNode); - nsresult InsertContainerAbove(nsIDOMNode *inNode, nsCOMPtr *outNode, const nsString &aNodeType); + /* helper routines for inline style */ + nsresult SetInlinePropertyOnTextNode( nsIDOMCharacterData *aTextNode, + PRInt32 aStartOffset, + PRInt32 aEndOffset, + nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue); + nsresult SetInlinePropertyOnNode( nsIDOMNode *aNode, + nsIAtom *aProperty, + const nsString *aAttribute, + const nsString *aValue); + + nsresult PromoteInlineRange(nsIDOMRange *inRange); + nsresult SplitStyleAboveRange(nsIDOMRange *aRange, + nsIAtom *aProperty, + const nsString *aAttribute); + nsresult SplitStyleAbovePoint(nsCOMPtr *aNode, + PRInt32 *aOffset, + nsIAtom *aProperty, + const nsString *aAttribute); + nsresult RemoveStyleInside(nsIDOMNode *aNode, + nsIAtom *aProperty, + const nsString *aAttribute, + PRBool aChildrenOnly = PR_FALSE); + + PRBool HasAttr(nsIDOMNode *aNode, const nsString *aAttribute); + PRBool HasAttrVal(nsIDOMNode *aNode, const nsString *aAttribute, const nsString *aValue); + PRBool IsAtFrontOfNode(nsIDOMNode *aNode, PRInt32 aOffset); + PRBool IsAtEndOfNode(nsIDOMNode *aNode, PRInt32 aOffset); + PRBool IsOnlyAttribute(nsIDOMNode *aElement, const nsString *aAttribute); + PRBool HasMatchingAttributes(nsIDOMNode *aNode1, + nsIDOMNode *aNode2); + + nsresult GetPriorHTMLSibling(nsIDOMNode *inNode, nsCOMPtr *outNode); + nsresult GetPriorHTMLSibling(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outNode); + nsresult GetNextHTMLSibling(nsIDOMNode *inNode, nsCOMPtr *outNode); + nsresult GetNextHTMLSibling(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outNode); + nsresult GetPriorHTMLNode(nsIDOMNode *inNode, nsCOMPtr *outNode); + nsresult GetPriorHTMLNode(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outNode); + nsresult GetNextHTMLNode(nsIDOMNode *inNode, nsCOMPtr *outNode); + nsresult GetNextHTMLNode(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outNode); + + nsresult IsFirstEditableChild( nsIDOMNode *aNode, PRBool *aOutIsFirst); + nsresult IsLastEditableChild( nsIDOMNode *aNode, PRBool *aOutIsLast); + nsresult GetFirstEditableChild( nsIDOMNode *aNode, nsCOMPtr *aOutFirstChild); + nsresult GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr *aOutLastChild); // Data members protected: - TypeInState* mTypeInState; - nsEditRules* mRules; + TypeInState* mTypeInState; + nsCOMPtr mRules; nsCOMPtr mKeyListenerP; nsCOMPtr mMouseListenerP; nsCOMPtr mTextListenerP; diff --git a/mozilla/editor/libeditor/text/nsTextEditRules.cpp b/mozilla/editor/libeditor/text/nsTextEditRules.cpp index e3076cbd634..803d0cf1de9 100644 --- a/mozilla/editor/libeditor/text/nsTextEditRules.cpp +++ b/mozilla/editor/libeditor/text/nsTextEditRules.cpp @@ -51,6 +51,17 @@ static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID); return NS_OK; \ }; + +nsresult +NS_NewTextEditRules(nsIEditRules** aInstancePtrResult) +{ + nsTextEditRules * rules = new nsTextEditRules(); + if (rules) + return rules->QueryInterface(NS_GET_IID(nsIEditRules), (void**) aInstancePtrResult); + return NS_ERROR_OUT_OF_MEMORY; +} + + /******************************************************** * Constructor/Destructor ********************************************************/ @@ -62,6 +73,7 @@ nsTextEditRules::nsTextEditRules() , mLockRulesSniffing(PR_FALSE) , mTheAction(0) { + NS_INIT_REFCNT(); } nsTextEditRules::~nsTextEditRules() @@ -69,6 +81,14 @@ nsTextEditRules::~nsTextEditRules() // do NOT delete mEditor here. We do not hold a ref count to mEditor. mEditor owns our lifespan. } +/******************************************************** + * XPCOM Cruft + ********************************************************/ + +NS_IMPL_ADDREF(nsTextEditRules) +NS_IMPL_RELEASE(nsTextEditRules) +NS_IMPL_QUERY_INTERFACE1(nsTextEditRules, nsIEditRules) + /******************************************************** * Public methods @@ -156,7 +176,7 @@ nsTextEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection) NS_IMETHODIMP -nsTextEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection, PRBool aSetSelection) +nsTextEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) { if (mLockRulesSniffing) return NS_OK; diff --git a/mozilla/editor/libeditor/text/nsTextEditRules.h b/mozilla/editor/libeditor/text/nsTextEditRules.h index 1eae320847c..eac378ad4d5 100644 --- a/mozilla/editor/libeditor/text/nsTextEditRules.h +++ b/mozilla/editor/libeditor/text/nsTextEditRules.h @@ -42,17 +42,18 @@ * 2. Selection must not be explicitly set by the rule method. * Any manipulation of Selection must be done by the editor. */ -class nsTextEditRules : public nsEditRules +class nsTextEditRules : public nsIEditRules { public: - + NS_DECL_ISUPPORTS + nsTextEditRules(); virtual ~nsTextEditRules(); - // nsEditRules methods + // nsIEditRules methods NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags); NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection); - NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection, PRBool aSetSelection); + NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection); NS_IMETHOD WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled); NS_IMETHOD DidDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, nsresult aResult); NS_IMETHOD GetFlags(PRUint32 *aFlags); @@ -302,5 +303,25 @@ class nsAutoLockRulesSniffing +/*************************************************************************** + * stack based helper class for turning on/off the edit listener. + */ +class nsAutoLockListener +{ + public: + + nsAutoLockListener(PRBool *enabled) : mEnabled(enabled) + {if (mEnabled) { mOldState=*mEnabled; *mEnabled = PR_FALSE;}} + ~nsAutoLockListener() + {if (mEnabled) *mEnabled = mOldState;} + + protected: + PRBool *mEnabled; + PRBool mOldState; +}; + + +nsresult NS_NewTextEditRules(nsIEditRules** aInstancePtrResult); + #endif //nsTextEditRules_h__