diff --git a/mozilla/editor/base/DeleteRangeTxn.cpp b/mozilla/editor/base/DeleteRangeTxn.cpp index 7b315af46bb..14d63c38f41 100644 --- a/mozilla/editor/base/DeleteRangeTxn.cpp +++ b/mozilla/editor/base/DeleteRangeTxn.cpp @@ -127,7 +127,7 @@ NS_IMETHODIMP DeleteRangeTxn::Do(void) else { // the selection ends in a different node from where it started // delete the relevant content in the start node - result = CreateTxnsToDeleteContent(mStartParent, mStartOffset, nsIEditor::eDeleteNext); + result = CreateTxnsToDeleteContent(mStartParent, mStartOffset, nsIEditor::eNext); if (NS_SUCCEEDED(result)) { // delete the intervening nodes @@ -135,7 +135,7 @@ NS_IMETHODIMP DeleteRangeTxn::Do(void) if (NS_SUCCEEDED(result)) { // delete the relevant content in the end node - result = CreateTxnsToDeleteContent(mEndParent, mEndOffset, nsIEditor::eDeletePrevious); + result = CreateTxnsToDeleteContent(mEndParent, mEndOffset, nsIEditor::ePrevious); } } } @@ -286,7 +286,7 @@ DeleteRangeTxn::CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent, NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent, PRUint32 aOffset, - nsIEditor::ESelectionCollapseDirection aAction) + nsIEditor::EDirection aAction) { nsresult result; // see what kind of node we have @@ -295,7 +295,7 @@ NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent, if (textNode) { // if the node is a text node, then delete text content PRUint32 start, numToDelete; - if (nsIEditor::eDeleteNext == aAction) + if (nsIEditor::eNext == aAction) { start=aOffset; textNode->GetLength(&numToDelete); diff --git a/mozilla/editor/base/DeleteRangeTxn.h b/mozilla/editor/base/DeleteRangeTxn.h index ef9e2505324..638b2b6f546 100644 --- a/mozilla/editor/base/DeleteRangeTxn.h +++ b/mozilla/editor/base/DeleteRangeTxn.h @@ -87,7 +87,7 @@ protected: NS_IMETHOD CreateTxnsToDeleteContent(nsIDOMNode *aParent, PRUint32 aOffset, - nsIEditor::ESelectionCollapseDirection aAction); + nsIEditor::EDirection aAction); protected: diff --git a/mozilla/editor/base/TextEditorTest.cpp b/mozilla/editor/base/TextEditorTest.cpp index a77a6bda959..5483adce627 100644 --- a/mozilla/editor/base/TextEditorTest.cpp +++ b/mozilla/editor/base/TextEditorTest.cpp @@ -104,7 +104,7 @@ nsresult TextEditorTest::InitDoc() { nsresult result = mEditor->SelectAll(); TEST_RESULT(result); - result = mEditor->DeleteSelection(nsIEditor::eDeleteNext); + result = mEditor->DeleteSelection(nsIEditor::eNext); TEST_RESULT(result); return result; } diff --git a/mozilla/editor/base/nsEditRules.h b/mozilla/editor/base/nsEditRules.h index 2722a5a9318..e4c54ea1852 100644 --- a/mozilla/editor/base/nsEditRules.h +++ b/mozilla/editor/base/nsEditRules.h @@ -48,6 +48,8 @@ class nsEditRules { public: 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)=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; @@ -55,5 +57,6 @@ public: NS_IMETHOD DocumentIsEmpty(PRBool *aDocumentIsEmpty)=0; }; + #endif //nsEditRules_h__ diff --git a/mozilla/editor/base/nsEditor.cpp b/mozilla/editor/base/nsEditor.cpp index acaaba67c77..848f37a1c78 100644 --- a/mozilla/editor/base/nsEditor.cpp +++ b/mozilla/editor/base/nsEditor.cpp @@ -444,6 +444,8 @@ nsEditor::Undo(PRUint32 aCount) if (gNoisy) { printf("Editor::Undo ----------\n"); } nsresult result = NS_OK; + nsAutoRules beginRulesSniffing(this, kOpUndo, nsIEditor::eNone); + BeginUpdateViewBatch(); if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) @@ -489,6 +491,7 @@ nsEditor::Redo(PRUint32 aCount) if (gNoisy) { printf("Editor::Redo ----------\n"); } nsresult result = NS_OK; + nsAutoRules beginRulesSniffing(this, kOpRedo, nsIEditor::eNone); BeginUpdateViewBatch(); if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) @@ -916,6 +919,8 @@ NS_IMETHODIMP nsEditor::CreateNode(const nsString& aTag, PRInt32 i; nsIEditActionListener *listener; + nsAutoRules beginRulesSniffing(this, kOpCreateNode, nsIEditor::eNext); + if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -960,6 +965,7 @@ NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode, { PRInt32 i; nsIEditActionListener *listener; + nsAutoRules beginRulesSniffing(this, kOpInsertNode, nsIEditor::eNext); if (mActionListeners) { @@ -1000,6 +1006,7 @@ nsEditor::SplitNode(nsIDOMNode * aNode, { PRInt32 i; nsIEditActionListener *listener; + nsAutoRules beginRulesSniffing(this, kOpSplitNode, nsIEditor::eNext); if (mActionListeners) { @@ -1147,6 +1154,7 @@ nsEditor::JoinNodes(nsIDOMNode * aLeftNode, { PRInt32 i; nsIEditActionListener *listener; + nsAutoRules beginRulesSniffing(this, kOpJoinNode, nsIEditor::ePrevious); if (mActionListeners) { @@ -1185,6 +1193,7 @@ NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement) { PRInt32 i; nsIEditActionListener *listener; + nsAutoRules beginRulesSniffing(this, kOpCreateNode, nsIEditor::ePrevious); if (mActionListeners) { @@ -1509,6 +1518,24 @@ nsEditor::GetBodyElement(nsIDOMElement **aBodyElement) } +/** All editor operations which alter the doc should be prefaced + * with a call to StartOperation, naming the action and direction */ +NS_IMETHODIMP +nsEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection) +{ + return NS_OK; +} + + +/** 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) +{ + return NS_OK; +} + + // Objects must be DOM elements NS_IMETHODIMP nsEditor::CloneAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) @@ -1614,12 +1641,23 @@ nsresult nsEditor::GetTextNodeTag(nsString& aOutString) NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert) { - EditAggregateTxn *aggTxn = nsnull; - // Create the "delete current selection" txn - nsresult result = CreateAggregateTxnForDeleteSelection(InsertTextTxn::gInsertTextTxnName, &aggTxn); - if ((NS_FAILED(result)) || (nsnull==aggTxn)) { - return NS_ERROR_OUT_OF_MEMORY; + // First delete the selection if needed + nsCOMPtr selection; + nsresult result = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; + + PRBool bIsCollapsed; + selection->GetIsCollapsed(&bIsCollapsed); + if (!bIsCollapsed) + { + result = DeleteSelectionImpl(nsIEditor::eNone); + if (NS_FAILED(result)) return result; } + + PRInt32 action = kOpInsertText; + if (mInIMEMode) action = kOpInsertIMEText; + nsAutoRules beginRulesSniffing(this, action, nsIEditor::eNext); nsCOMPtr nodeAsText; PRInt32 offset; @@ -1635,11 +1673,6 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert) mIMETextNode = nodeAsText; mIMETextOffset = offset; } -// else if (mIMETextNode != nodeAsText) || (mIMETextOffset+mIMEBufferLength != offset)) -// { -// // IME and we do not agree about where we are. Force a commit of -// // earlier ime txn. We don't have a way to do that yet. -// } result = CreateTxnForIMEText(aStringToInsert,(IMETextTxn**)&txn); } else @@ -1663,7 +1696,6 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert) } BeginUpdateViewBatch(); - result = Do(aggTxn); result = Do(txn); // The transaction system (if any) has taken ownwership of txns. // aggTxn released at end of routine. @@ -1682,63 +1714,34 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert) } } - else if (NS_ERROR_EDITOR_NO_SELECTION==result) - { - result = DoInitialInsert(aStringToInsert); - } else if (NS_ERROR_EDITOR_NO_TEXTNODE==result) { - - // only execute the aggTxn if we actually populated it with at least one sub-txn - PRUint32 count=0; - aggTxn->GetCount(&count); - if (count) - { - result = Do(aggTxn); - } - else - { - result = NS_OK; - } - // The transaction system (if any) has taken ownwership of txns - NS_IF_RELEASE(aggTxn); - // create the text node - if (NS_SUCCEEDED(result)) + nsCOMPtr selectedNode; + result = selection->GetAnchorNode(getter_AddRefs(selectedNode)); + if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offset)) && selectedNode) { - nsCOMPtr selection; - result = GetSelection(getter_AddRefs(selection)); - if ((NS_SUCCEEDED(result)) && selection) + nsCOMPtr newNode; + nsAutoString textNodeTag; + result = GetTextNodeTag(textNodeTag); + if (NS_FAILED(result)) { return result; } + result = CreateNode(textNodeTag, selectedNode, offset, + getter_AddRefs(newNode)); + if (NS_SUCCEEDED(result) && newNode) { - nsCOMPtr selectedNode; - result = selection->GetAnchorNode(getter_AddRefs(selectedNode)); - if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offset)) && selectedNode) + nsCOMPtrnewTextNode; + newTextNode = do_QueryInterface(newNode); + if (newTextNode) { - nsCOMPtr newNode; - nsAutoString textNodeTag; - result = GetTextNodeTag(textNodeTag); - if (NS_FAILED(result)) { return result; } - result = CreateNode(textNodeTag, selectedNode, offset, - getter_AddRefs(newNode)); - if (NS_SUCCEEDED(result) && newNode) - { - nsCOMPtrnewTextNode; - 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? - } - } + nsAutoString placeholderText(" "); + newTextNode->SetData(placeholderText); + selection->Collapse(newNode, 0); + selection->Extend(newNode, 1); + result = InsertTextImpl(aStringToInsert); // this really recurses, right? } - } - } + } + } } - // The transaction system (if any) has taken ownwership of txns - NS_IF_RELEASE(aggTxn); return result; } @@ -1749,48 +1752,34 @@ NS_IMETHODIMP nsEditor::PrepareToInsertText(nsCOMPtr *aOutT nsCOMPtr nodeAsText; nsCOMPtr selection; - if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED; - nsCOMPtr ps = do_QueryReferent(mPresShellWeak); - if (!ps) return NS_ERROR_NOT_INITIALIZED; - result = ps->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); - if ((NS_SUCCEEDED(result)) && selection) + result = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; + nsCOMPtr selNode; + result = GetStartNodeAndOffset(selection, &selNode, aOutOffset); + if (NS_FAILED(result)) return result; + if (!selNode) { - result = NS_ERROR_UNEXPECTED; - nsCOMPtr enumerator; - result = selection->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_SUCCEEDED(result) && enumerator) - { - enumerator->First(); - nsCOMPtr currentItem; - result = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if ((NS_SUCCEEDED(result)) && (currentItem)) - { - result = NS_ERROR_UNEXPECTED; - nsCOMPtr range( do_QueryInterface(currentItem) ); - if (range) - { - nsCOMPtr node; - result = range->GetStartParent(getter_AddRefs(node)); - if ((NS_SUCCEEDED(result)) && (node)) - { - nodeAsText = do_QueryInterface(node); - range->GetStartOffset(aOutOffset); - if (nodeAsText) - { - *aOutTextNode = nodeAsText; - } - else - { - result = NS_ERROR_EDITOR_NO_TEXTNODE; - } - } - } - } - else - { - result = NS_ERROR_EDITOR_NO_SELECTION; - } - } + // should nsEditor really be concerned with the body node? + // That's an html concept. + nsCOMPtrbodyElement; + result = GetBodyElement(getter_AddRefs(bodyElement)); + if (NS_FAILED(result)) return result; + if (!bodyElement) return NS_ERROR_NULL_POINTER; + nsCOMPtrbodyNode = do_QueryInterface(bodyElement); + selection->Collapse(bodyNode,0); + result = GetStartNodeAndOffset(selection, &selNode, aOutOffset); + if (NS_FAILED(result)) return result; + } + + nodeAsText = do_QueryInterface(selNode); + if (nodeAsText) + { + *aOutTextNode = nodeAsText; + } + else + { + result = NS_ERROR_EDITOR_NO_TEXTNODE; } return result; } @@ -1961,130 +1950,13 @@ NS_IMETHODIMP nsEditor::CreateTxnForInsertText(const nsString & aStringToInsert, return result; } -// we're in the special situation where there is no selection. Insert the text -// at the beginning of the document. -// XXX: this is all logic that must be moved to the rule system -// for HTML, we create a text node on the body. That's what is done below -// for XML, we would create a text node on the root element. -// The rule system should be telling us which of these (or any other variant) to do. -/* this method should look something like - BeginTransaction() - mRule->GetNodeForInitialInsert(parentNode) - mRule->CreateInitialDocumentFragment(childNode) - InsertElement(childNode, parentNode) - find the first text node in childNode - insert the text there -*/ - -NS_IMETHODIMP nsEditor::DoInitialInsert(const nsString & aStringToInsert) -{ - if (!mDocWeak) { - return NS_ERROR_NOT_INITIALIZED; - } - - nsCOMPtrnodeList; - nsAutoString bodyTag = "body"; - nsCOMPtr doc = do_QueryReferent(mDocWeak); - if (!doc) return NS_ERROR_NOT_INITIALIZED; - nsresult result = doc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); - if ((NS_SUCCEEDED(result)) && nodeList) - { - PRUint32 count; - nodeList->GetLength(&count); - NS_ASSERTION(1==count, "there is not exactly 1 body in the document!"); - nsCOMPtrnode; - result = nodeList->Item(0, getter_AddRefs(node)); - if ((NS_SUCCEEDED(result)) && node) - { // now we've got the body tag. - // create transaction to insert the text node, - // and create a transaction to insert the text - CreateElementTxn *txn; - nsAutoString textNodeTag; - result = GetTextNodeTag(textNodeTag); - if (NS_FAILED(result)) { return result; } - result = CreateTxnForCreateElement(textNodeTag, node, 0, &txn); - if ((NS_SUCCEEDED(result)) && txn) - { - result = Do(txn); - if (NS_SUCCEEDED(result)) - { - nsCOMPtrnewNode; - txn->GetNewNode(getter_AddRefs(newNode)); - if ((NS_SUCCEEDED(result)) && newNode) - { - nsCOMPtrnewTextNode; - newTextNode = do_QueryInterface(newNode); - if (newTextNode) - { - EditTxn *insertTxn; - if (mInIMEMode) - { - nsCOMPtr selection; - result = GetSelection(getter_AddRefs(selection)); - if ((NS_SUCCEEDED(result)) && selection) - { - selection->Collapse(newTextNode, 0); - mIMETextNode = newTextNode; - mIMETextOffset = 0; - mIMEBufferLength = 0; - result = CreateTxnForIMEText(aStringToInsert,(IMETextTxn**)&txn); - } - } - else - { - result = CreateTxnForInsertText(aStringToInsert, newTextNode, 0, (InsertTextTxn**)&txn); - } - if (NS_SUCCEEDED(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(newTextNode, 0, aStringToInsert); - } - } - - result = Do(insertTxn); - - // let listeners know what happened - if (mActionListeners) - { - for (i = 0; i < mActionListeners->Count(); i++) - { - listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); - if (listener) - listener->DidInsertText(newTextNode, 0, aStringToInsert, result); - } - } - - } - // The transaction system (if any) has taken ownwership of txn - NS_IF_RELEASE(insertTxn); - } - else { - result = NS_ERROR_UNEXPECTED; - } - } - } - } - // The transaction system (if any) has taken ownwership of txn - NS_IF_RELEASE(txn); - } - } - return result; -} - NS_IMETHODIMP nsEditor::DeleteText(nsIDOMCharacterData *aElement, PRUint32 aOffset, PRUint32 aLength) { DeleteTextTxn *txn; nsresult result = CreateTxnForDeleteText(aElement, aOffset, aLength, &txn); + nsAutoRules beginRulesSniffing(this, kOpDeleteText, nsIEditor::ePrevious); if (NS_SUCCEEDED(result)) { // let listeners know whats up @@ -4087,20 +3959,49 @@ nsresult nsEditor::EndUpdateViewBatch() NS_IMETHODIMP -nsEditor::DeleteSelectionImpl(ESelectionCollapseDirection aAction) +nsEditor::DeleteSelectionImpl(EDirection aAction) { - nsresult result; + nsresult res; EditAggregateTxn *txn; - result = CreateTxnForDeleteSelection(aAction, &txn); - if (NS_SUCCEEDED(result)) { - result = Do(txn); + PRInt32 i; + nsIEditActionListener *listener; + nsCOMPtrselection; + res = CreateTxnForDeleteSelection(aAction, &txn); + if (NS_FAILED(res)) return res; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + nsAutoRules beginRulesSniffing(this, kOpDeleteSelection, aAction); + + if (NS_SUCCEEDED(res)) + { + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->WillDeleteSelection(selection); + } + } + + res = Do(txn); + + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->DidDeleteSelection(selection); + } + } } // The transaction system (if any) has taken ownwership of txn NS_IF_RELEASE(txn); - return result; + return res; } @@ -4246,7 +4147,7 @@ NS_IMETHODIMP nsEditor::CreateTxnForDeleteElement(nsIDOMNode * aElement, return result; } -NS_IMETHODIMP nsEditor::CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, EditAggregateTxn **aAggTxn) +/*NS_IMETHODIMP nsEditor::CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, EditAggregateTxn **aAggTxn) { nsresult result = NS_ERROR_NULL_POINTER; if (aAggTxn) @@ -4274,7 +4175,7 @@ NS_IMETHODIMP nsEditor::CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, result = selection->GetIsCollapsed(&collapsed); if (NS_SUCCEEDED(result) && !collapsed) { EditAggregateTxn *delSelTxn; - result = CreateTxnForDeleteSelection(eDoNothing, &delSelTxn); + result = CreateTxnForDeleteSelection(eNone, &delSelTxn); if (NS_SUCCEEDED(result) && delSelTxn) { (*aAggTxn)->AppendChild(delSelTxn); } @@ -4283,7 +4184,7 @@ NS_IMETHODIMP nsEditor::CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, } return result; } - +*/ NS_IMETHODIMP nsEditor::CreateTxnForIMEText(const nsString & aStringToInsert, @@ -4335,7 +4236,7 @@ nsEditor::CreateTxnForRemoveStyleSheet(nsICSSStyleSheet* aSheet, RemoveStyleShee NS_IMETHODIMP -nsEditor::CreateTxnForDeleteSelection(nsIEditor::ESelectionCollapseDirection aAction, +nsEditor::CreateTxnForDeleteSelection(nsIEditor::EDirection aAction, EditAggregateTxn ** aTxn) { if (!aTxn) @@ -4352,8 +4253,8 @@ nsEditor::CreateTxnForDeleteSelection(nsIEditor::ESelectionCollapseDirection aAc { // If it's one of these modes, // we have to extend the selection first: - if (aAction == eDeleteNextWord || aAction == eDeletePreviousWord - || aAction == eDeleteToEndOfLine) + if (aAction == eNextWord || aAction == ePreviousWord + || aAction == eToEndOfLine) { nsCOMPtr selCont (do_QueryInterface(ps)); if (!selCont) @@ -4361,13 +4262,13 @@ nsEditor::CreateTxnForDeleteSelection(nsIEditor::ESelectionCollapseDirection aAc switch (aAction) { - case eDeleteNextWord: + case eNextWord: result = selCont->WordMove(PR_TRUE, PR_TRUE); break; - case eDeletePreviousWord: + case ePreviousWord: result = selCont->WordMove(PR_FALSE, PR_TRUE); break; - case eDeleteToEndOfLine: + case eToEndOfLine: result = selCont->IntraLineMove(PR_TRUE, PR_TRUE); break; default: break; // avoid compiler warnings @@ -4382,7 +4283,7 @@ nsEditor::CreateTxnForDeleteSelection(nsIEditor::ESelectionCollapseDirection aAc // Check whether the selection is collapsed and we should do nothing: PRBool isCollapsed; result = (selection->GetIsCollapsed(&isCollapsed)); - if (NS_SUCCEEDED(result) && isCollapsed && aAction == eDoNothing) + if (NS_SUCCEEDED(result) && isCollapsed && aAction == eNone) return NS_OK; // allocate the out-param transaction @@ -4437,7 +4338,7 @@ nsEditor::CreateTxnForDeleteSelection(nsIEditor::ESelectionCollapseDirection aAc //XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior are not implemented NS_IMETHODIMP nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, - nsIEditor::ESelectionCollapseDirection + nsIEditor::EDirection aAction, EditAggregateTxn *aTxn) { @@ -4483,7 +4384,7 @@ nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, // build a transaction for deleting the appropriate data // XXX: this has to come from rule section - if ((eDeletePrevious==aAction) && (PR_TRUE==isFirst)) + if ((ePrevious==aAction) && (PR_TRUE==isFirst)) { // we're backspacing from the beginning of the node. Delete the first thing to our left nsCOMPtr priorNode; result = GetPriorNode(node, PR_TRUE, getter_AddRefs(priorNode)); @@ -4520,7 +4421,7 @@ nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, } } } - else if ((nsIEditor::eDeleteNext==aAction) && (PR_TRUE==isLast)) + else if ((nsIEditor::eNext==aAction) && (PR_TRUE==isLast)) { // we're deleting from the end of the node. Delete the first thing to our right nsCOMPtr nextNode; result = GetNextNode(node, PR_TRUE, getter_AddRefs(nextNode)); @@ -4561,7 +4462,7 @@ nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, { if (nodeAsText) { // we have text, so delete a char at the proper offset - if (nsIEditor::eDeletePrevious==aAction) { + if (nsIEditor::ePrevious==aAction) { offset --; } DeleteTextTxn *txn; @@ -4573,11 +4474,11 @@ nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, else { // we're either deleting a node or some text, need to dig into the next/prev node to find out nsCOMPtr selectedNode; - if (eDeletePrevious==aAction) + if (ePrevious==aAction) { result = GetPriorNode(node, offset, PR_TRUE, getter_AddRefs(selectedNode)); } - else if (eDeleteNext==aAction) + else if (eNext==aAction) { result = GetNextNode(node, offset, PR_TRUE, getter_AddRefs(selectedNode)); } @@ -4589,7 +4490,7 @@ nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, if (selectedNodeAsText) { // we are deleting from a text node, so do a text deletion PRInt32 begin = 0; // default for forward delete - if (eDeletePrevious==aAction) + if (ePrevious==aAction) { PRUint32 length=0; selectedNodeAsText->GetLength(&length); diff --git a/mozilla/editor/base/nsEditor.h b/mozilla/editor/base/nsEditor.h index 6ff9c5894a8..f3e8b989045 100644 --- a/mozilla/editor/base/nsEditor.h +++ b/mozilla/editor/base/nsEditor.h @@ -86,6 +86,25 @@ public: kIterBackward }; + enum OperationID + { + kOpIgnore = -1, + kOpNone = 0, + kOpUndo, + kOpRedo, + kOpSetTextProperty, + kOpRemoveTextProperty, + kOpInsertNode, + kOpCreateNode, + kOpDeleteNode, + kOpSplitNode, + kOpJoinNode, + kOpDeleteText, + kOpInsertText, + kOpInsertIMEText, + kOpDeleteSelection + }; + static const char* kMOZEditorBogusNodeAttr; static const char* kMOZEditorBogusNodeValue; @@ -110,7 +129,6 @@ public: NS_IMETHOD GetDocument(nsIDOMDocument **aDoc); NS_IMETHOD GetPresShell(nsIPresShell **aPS); NS_IMETHOD GetSelection(nsIDOMSelection **aSelection); - NS_IMETHOD GetBodyElement(nsIDOMElement **aElement); NS_IMETHOD EnableUndo(PRBool aEnable); NS_IMETHOD Do(nsITransaction *aTxn); @@ -216,7 +234,7 @@ public: NS_IMETHOD InsertTextImpl(const nsString& aStringToInsert); - NS_IMETHOD DeleteSelectionImpl(ESelectionCollapseDirection aAction); + NS_IMETHOD DeleteSelectionImpl(EDirection aAction); protected: @@ -264,22 +282,11 @@ protected: DeleteElementTxn ** aTxn); - /** Create an aggregate transaction for deleting current selection - * Used by all methods that need to delete current selection, - * then insert something new to replace it - * @param nsString& aTxnName is the name of the aggregated transaction - * @param EditTxn **aAggTxn is the return location of the aggregate TXN, - * with the DeleteSelectionTxn as the first child ONLY - * if there was a selection to delete. - */ - NS_IMETHOD CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, EditAggregateTxn **aAggTxn); - - - NS_IMETHOD CreateTxnForDeleteSelection(ESelectionCollapseDirection aAction, + NS_IMETHOD CreateTxnForDeleteSelection(EDirection aAction, EditAggregateTxn ** aTxn); NS_IMETHOD CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, - ESelectionCollapseDirection aAction, + EDirection aAction, EditAggregateTxn *aTxn); @@ -302,10 +309,6 @@ protected: */ NS_IMETHOD CreateTxnForRemoveStyleSheet(nsICSSStyleSheet* aSheet, RemoveStyleSheetTxn* *aTxn); - /** insert aStringToInsert as the first text in the document - */ - NS_IMETHOD DoInitialInsert(const nsString & aStringToInsert); - NS_IMETHOD PrepareToInsertText(nsCOMPtr *aOutTextNode, PRInt32 *aOutOffset); NS_IMETHOD DeleteText(nsIDOMCharacterData *aElement, @@ -370,6 +373,18 @@ protected: public: + /** return the body element */ + /** It would make more sense for this to be in nsHTMLEditor, no? */ + NS_IMETHOD GetBodyElement(nsIDOMElement **aElement); + + /** All editor operations which alter the doc should be prefaced + * with a call to StartOperation, naming the action and direction */ + NS_IMETHOD 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_IMETHOD EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection); + /** return the string that represents text nodes in the content tree */ static nsresult GetTextNodeTag(nsString& aOutString); diff --git a/mozilla/editor/base/nsEditorEventListeners.cpp b/mozilla/editor/base/nsEditorEventListeners.cpp index 407eeaa8723..da89018bba1 100644 --- a/mozilla/editor/base/nsEditorEventListeners.cpp +++ b/mozilla/editor/base/nsEditorEventListeners.cpp @@ -171,13 +171,13 @@ nsTextEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent) return NS_ERROR_BASE; // consumed if (nsIDOMKeyEvent::DOM_VK_BACK_SPACE==keyCode) { - mEditor->DeleteSelection(nsIEditor::eDeletePrevious); + mEditor->DeleteSelection(nsIEditor::ePrevious); ScrollSelectionIntoView(); return NS_ERROR_BASE; // consumed } if (nsIDOMKeyEvent::DOM_VK_DELETE==keyCode) { - mEditor->DeleteSelection(nsIEditor::eDeleteNext); + mEditor->DeleteSelection(nsIEditor::eNext); ScrollSelectionIntoView(); return NS_ERROR_BASE; // consumed } diff --git a/mozilla/editor/base/nsEditorShell.cpp b/mozilla/editor/base/nsEditorShell.cpp index 7a643830471..45261cede2c 100644 --- a/mozilla/editor/base/nsEditorShell.cpp +++ b/mozilla/editor/base/nsEditorShell.cpp @@ -625,7 +625,7 @@ nsEditorShell::DeleteCharForward() { nsCOMPtr editor = do_QueryInterface(mEditor); if (!editor) return NS_ERROR_NOT_INITIALIZED; - nsresult rv = editor->DeleteSelection(nsIEditor::eDeleteNext); + nsresult rv = editor->DeleteSelection(nsIEditor::eNext); ScrollSelectionIntoView(); return rv; } @@ -635,7 +635,7 @@ nsEditorShell::DeleteCharBackward() { nsCOMPtr editor = do_QueryInterface(mEditor); if (!editor) return NS_ERROR_NOT_INITIALIZED; - nsresult rv = editor->DeleteSelection(nsIEditor::eDeletePrevious); + nsresult rv = editor->DeleteSelection(nsIEditor::ePrevious); ScrollSelectionIntoView(); return rv; } @@ -645,7 +645,7 @@ nsEditorShell::DeleteWordForward() { nsCOMPtr editor = do_QueryInterface(mEditor); if (!editor) return NS_ERROR_NOT_INITIALIZED; - nsresult rv = editor->DeleteSelection(nsIEditor::eDeleteNextWord); + nsresult rv = editor->DeleteSelection(nsIEditor::eNextWord); ScrollSelectionIntoView(); return rv; } @@ -655,7 +655,7 @@ nsEditorShell::DeleteWordBackward() { nsCOMPtr editor = do_QueryInterface(mEditor); if (!editor) return NS_ERROR_NOT_INITIALIZED; - nsresult rv = editor->DeleteSelection(nsIEditor::eDeletePreviousWord); + nsresult rv = editor->DeleteSelection(nsIEditor::ePreviousWord); ScrollSelectionIntoView(); return rv; } @@ -665,7 +665,7 @@ nsEditorShell::DeleteToEndOfLine() { nsCOMPtr editor = do_QueryInterface(mEditor); if (!editor) return NS_ERROR_NOT_INITIALIZED; - nsresult rv = editor->DeleteSelection(nsIEditor::eDeleteToEndOfLine); + nsresult rv = editor->DeleteSelection(nsIEditor::eToEndOfLine); ScrollSelectionIntoView(); return rv; } @@ -2038,18 +2038,18 @@ NS_IMETHODIMP nsEditorShell::DeleteSelection(PRInt32 action) { nsresult err = NS_NOINTERFACE; - nsIEditor::ESelectionCollapseDirection selectionAction; + nsIEditor::EDirection selectionAction; switch(action) { case 1: - selectionAction = nsIEditor::eDeleteNext; + selectionAction = nsIEditor::eNext; break; case 2: - selectionAction = nsIEditor::eDeletePrevious; + selectionAction = nsIEditor::ePrevious; break; default: - selectionAction = nsIEditor::eDoNothing; + selectionAction = nsIEditor::eNone; break; } diff --git a/mozilla/editor/base/nsEditorUtils.h b/mozilla/editor/base/nsEditorUtils.h index b2859f9efa3..e70472714a8 100644 --- a/mozilla/editor/base/nsEditorUtils.h +++ b/mozilla/editor/base/nsEditorUtils.h @@ -31,6 +31,8 @@ #include "nsIEditor.h" #include "nsIAtom.h" +#include "nsEditor.h" + class nsAutoEditBatch { private: @@ -86,5 +88,24 @@ class nsAutoSelectionReset ~nsAutoSelectionReset(); }; +/*************************************************************************** + * stack based helper class for StartOperation()/EndOperation() sandwich + */ +class nsAutoRules +{ + public: + + 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);} + + protected: + nsEditor *mEd; + PRInt32 mAction; + nsIEditor::EDirection mDirection; +}; + + #endif // nsEditorUtils_h__ diff --git a/mozilla/editor/base/nsHTMLEditRules.cpp b/mozilla/editor/base/nsHTMLEditRules.cpp index dde9e7e1ba8..e01f1a5a353 100644 --- a/mozilla/editor/base/nsHTMLEditRules.cpp +++ b/mozilla/editor/base/nsHTMLEditRules.cpp @@ -52,7 +52,6 @@ static NS_DEFINE_IID(kSubtreeIteratorCID, NS_SUBTREEITERATOR_CID); static NS_DEFINE_IID(kContentIteratorCID, NS_CONTENTITERATOR_CID); static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID); - enum { kLonely = 0, @@ -118,6 +117,70 @@ nsHTMLEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags) return res; } + +NS_IMETHODIMP +nsHTMLEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection) +{ + if (mLockRulesSniffing) return NS_OK; + + nsAutoLockRulesSniffing lockIt((nsTextEditRules*)this); + + if (!mActionNesting) + { + mDocChangeRange = nsnull; // clear out our accounting of what changed + } + mActionNesting++; + return NS_OK; +} + + +NS_IMETHODIMP +nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) +{ + if (mLockRulesSniffing) return NS_OK; + + nsAutoLockRulesSniffing lockIt(this); + + NS_PRECONDITION(mActionNesting>0, "bad action nesting!"); + nsresult res = NS_OK; + if (!--mActionNesting) + { + if (action == nsEditor::kOpIgnore) return NS_OK; + + nsCOMPtrselection; + nsresult res = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + if (mDocChangeRange && !((action == nsEditor::kOpUndo) || (action == nsEditor::kOpRedo))) + { + // expand the "changed doc range" as needed + res = PromoteRange(mDocChangeRange, action); + if (NS_FAILED(res)) return res; + + // add in any needed
s, and remove any unneeded ones. + res = AdjustSpecialBreaks(); + if (NS_FAILED(res)) return res; + // adjust whitespace for insert text and delete actions + if ((action == nsEditor::kOpInsertText) || (action == nsEditor::kOpDeleteSelection)) + { + res = AdjustWhitespace(selection); + if (NS_FAILED(res)) return res; + } + // clean up any empty nodes in the selection + res = RemoveEmptyNodes(); + if (NS_FAILED(res)) return res; + } + + // adjust selection + res = AdjustSelection(selection, aDirection); + if (NS_FAILED(res)) return res; + // detect empty doc + res = CreateBogusNodeIfNeeded(selection); + } + return res; +} + + NS_IMETHODIMP nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, @@ -132,8 +195,6 @@ nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection, *aCancel = PR_FALSE; *aHandled = PR_FALSE; - - mDocChangeRange = nsnull; // clear out our accounting of what changed // my kingdom for dynamic cast nsTextRulesInfo *info = NS_STATIC_CAST(nsTextRulesInfo*, aInfo); @@ -181,53 +242,8 @@ nsHTMLEditRules::DidDoAction(nsIDOMSelection *aSelection, nsTextRulesInfo *info = NS_STATIC_CAST(nsTextRulesInfo*, aInfo); nsresult res; - switch (info->action) - { - case kUndo: - mDocChangeRange = nsnull; // clear out our accounting of what changed - res = nsTextEditRules::DidDoAction(aSelection, aInfo, aResult); - if (NS_FAILED(res)) return res; - return AdjustSelection(aSelection,info->collapsedAction); - case kRedo: - mDocChangeRange = nsnull; // clear out our accounting of what changed - res = nsTextEditRules::DidDoAction(aSelection, aInfo, aResult); - if (NS_FAILED(res)) return res; - return AdjustSelection(aSelection,info->collapsedAction); - } - - // for other than undo and redo: - - if (mDocChangeRange) - { - // expand the "changed doc range" as needed - res = PromoteRange(mDocChangeRange, info->action); - if (NS_FAILED(res)) return res; - - // add in any needed
s, and remove any unneeded ones. - res = AdjustSpecialBreaks(); - if (NS_FAILED(res)) return res; - // adjust whitespace for insert text and delete actions - if ((aInfo->action == kInsertText) || (aInfo->action == kDeleteSelection)) - { - res = AdjustWhitespace(aSelection); - if (NS_FAILED(res)) return res; - } - // clean up any empty nodes in the selection - res = RemoveEmptyNodes(); - if (NS_FAILED(res)) return res; - } - - // do default: - if (aInfo->action != kInsertBreak) // nsTextEditRules::DidInsertBreak() not usable by html rules - { - res = nsTextEditRules::DidDoAction(aSelection, aInfo, aResult); - if (NS_FAILED(res)) return res; - } - // adjust selection - res = AdjustSelection(aSelection,info->collapsedAction); - if (NS_FAILED(res)) return res; - // detect empty doc - res = CreateBogusNodeIfNeeded(aSelection); + // pass thru to nsTextEditRules: + res = nsTextEditRules::DidDoAction(aSelection, aInfo, aResult); return res; } @@ -276,7 +292,7 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction, if (NS_FAILED(res)) return res; if (!bCollapsed) { - res = mEditor->DeleteSelection(nsIEditor::eDoNothing); + res = mEditor->DeleteSelection(nsIEditor::eNone); if (NS_FAILED(res)) return res; } @@ -450,7 +466,7 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, P if (NS_FAILED(res)) return res; if (!bCollapsed) { - res = mEditor->DeleteSelection(nsIEditor::eDoNothing); + res = mEditor->DeleteSelection(nsIEditor::eNone); if (NS_FAILED(res)) return res; } @@ -589,7 +605,7 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, P nsresult nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, - nsIEditor::ESelectionCollapseDirection aAction, + nsIEditor::EDirection aAction, PRBool *aCancel, PRBool *aHandled) { @@ -629,7 +645,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, if (NS_FAILED(res)) return res; // at beginning of text node and backspaced? - if (!offset && (aAction == nsIEditor::eDeletePrevious)) + if (!offset && (aAction == nsIEditor::ePrevious)) { nsCOMPtr priorNode; res = GetPriorHTMLNode(node, &priorNode); @@ -727,7 +743,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, // at end of text node and deleted? if ((offset == (PRInt32)strLength) - && (aAction == nsIEditor::eDeleteNext)) + && (aAction == nsIEditor::eNext)) { nsCOMPtr nextNode; res = GetNextHTMLNode(node, &nextNode); @@ -828,9 +844,9 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, { nsCOMPtr nodeToDelete; - if (aAction == nsIEditor::eDeletePrevious) + if (aAction == nsIEditor::ePrevious) res = GetPriorHTMLNode(node, offset, &nodeToDelete); - else if (aAction == nsIEditor::eDeleteNext) + else if (aAction == nsIEditor::eNext) res = GetNextHTMLNode(node, offset, &nodeToDelete); else return NS_OK; @@ -844,7 +860,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, PRUint32 selPoint = 0; nsCOMPtrnodeAsText; nodeAsText = do_QueryInterface(nodeToDelete); - if (aAction == nsIEditor::eDeletePrevious) + if (aAction == nsIEditor::ePrevious) nodeAsText->GetLength(&selPoint); res = aSelection->Collapse(nodeToDelete,selPoint); return res; @@ -3053,7 +3069,7 @@ nsHTMLEditRules::ReturnInListItem(nsIDOMSelection *aSelection, // now attempt to adjust selection to a text node. // if we fail, then that means we need toinsert a break - res = AdjustSelection(aSelection, nsIEditor::eDeleteNext); + res = AdjustSelection(aSelection, nsIEditor::eNext); if (NS_FAILED(res)) return res; // get the selection location nsCOMPtr selNode; @@ -3410,6 +3426,10 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft, // now we need to insert a br. nsCOMPtr brNode; res = mEditor->CreateBR(*aOutMergeParent, *aOutMergeOffset, &brNode); + if (NS_FAILED(res)) return res; + res = nsEditor::GetNodeLocation(brNode, aOutMergeParent, aOutMergeOffset); + if (NS_FAILED(res)) return res; + (*aOutMergeOffset)++; return res; } else if (IsList(aNodeLeft) || mEditor->IsTextNode(aNodeLeft)) @@ -3587,7 +3607,7 @@ nsHTMLEditRules::AdjustWhitespace(nsIDOMSelection *aSelection) node = do_QueryInterface(content); if (!node) return NS_ERROR_FAILURE; - if (nsEditor::IsTextNode(node)) + if (nsEditor::IsTextNode(node) && mEditor->IsEditable(node)) { isupports = do_QueryInterface(node); arrayOfNodes->AppendElement(isupports); @@ -3614,7 +3634,7 @@ nsHTMLEditRules::AdjustWhitespace(nsIDOMSelection *aSelection) nsresult -nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::ESelectionCollapseDirection aAction) +nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirection aAction) { if (!aSelection) return NS_ERROR_NULL_POINTER; @@ -3689,11 +3709,51 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::ESelect } } - // we aren't in a textnode: look for a nearby text node, in the right direction. - if (aAction == nsIEditor::eDeletePrevious) - res = GetPriorHTMLNode(selNode, selOffset, &nearNode); + // we aren't in a textnode: look for a nearby text node. + // prefer the correct direction. + res = FindNearTextNode(selNode, selOffset, aAction, &nearNode); + if (NS_FAILED(res)) return res; + // did we suceed in getting back a node? + nsIEditor::EDirection dir; + if (!nearNode) + { + // if not try again in other direction + dir = nsIEditor::eNext; + if (aAction == nsIEditor::eNext) dir = nsIEditor::ePrevious; + res = FindNearTextNode(selNode, selOffset, dir, &nearNode); + if (NS_FAILED(res)) return res; + } else - res = GetNextHTMLNode(selNode, selOffset, &nearNode); + { + dir = aAction; + } + // is the nearnode a text node? + textNode = do_QueryInterface(nearNode); + if (!textNode) return NS_ERROR_UNEXPECTED; + PRInt32 offset = 0; + // put selection in right place: + if (dir == nsIEditor::ePrevious) + textNode->GetLength((PRUint32*)&offset); + res = aSelection->Collapse(nearNode,offset); + if (NS_FAILED(res)) return res; + return res; +} + +nsresult +nsHTMLEditRules::FindNearTextNode(nsIDOMNode *aSelNode, + PRInt32 aSelOffset, + nsIEditor::EDirection aDirection, + nsCOMPtr *outTextNode) +{ + if (!aSelNode || !outTextNode) return NS_ERROR_NULL_POINTER; + *outTextNode = nsnull; + nsresult res = NS_OK; + + nsCOMPtr nearNode; + if (aDirection == nsIEditor::ePrevious) + res = GetPriorHTMLNode(aSelNode, aSelOffset, &nearNode); + else + res = GetNextHTMLNode(aSelNode, aSelOffset, &nearNode); if (NS_FAILED(res)) return res; // if there is no node then punt @@ -3701,24 +3761,16 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::ESelect // is nearNode also a descendant of same block? nsCOMPtr block, nearBlock; - if (mEditor->IsBlockNode(selNode)) block = selNode; - else block = mEditor->GetBlockNodeParent(selNode); + if (mEditor->IsBlockNode(aSelNode)) block = aSelNode; + else block = mEditor->GetBlockNodeParent(aSelNode); if (mEditor->IsBlockNode(nearNode)) nearBlock = nearNode; else nearBlock = mEditor->GetBlockNodeParent(nearNode); if (block != nearBlock) return NS_OK; // punt - we dont want to jump across a block // is the nearnode a text node? - textNode = do_QueryInterface(nearNode); - if (textNode) - { - PRInt32 offset = 0; - // put selection in right place: - if (aAction == nsIEditor::eDeletePrevious) - textNode->GetLength((PRUint32*)&offset); - res = aSelection->Collapse(nearNode,offset); - if (NS_FAILED(res)) return res; - } + nsCOMPtr textNode = do_QueryInterface(nearNode); + if (textNode) *outTextNode = nearNode; return res; } @@ -4281,6 +4333,39 @@ nsHTMLEditListener::DidDeleteText(nsIDOMCharacterData *aTextNode, 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, + nsIDOMRange::GetIID(), + 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) diff --git a/mozilla/editor/base/nsHTMLEditRules.h b/mozilla/editor/base/nsHTMLEditRules.h index 0f7ec0767e3..da8b675f810 100644 --- a/mozilla/editor/base/nsHTMLEditRules.h +++ b/mozilla/editor/base/nsHTMLEditRules.h @@ -41,6 +41,8 @@ public: virtual ~nsHTMLEditRules(); // nsEditRules methods + NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection); + NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection); NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags); NS_IMETHOD WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled); NS_IMETHOD DidDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, nsresult aResult); @@ -64,7 +66,7 @@ protected: TypeInState typeInState, PRInt32 aMaxLength); nsresult WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aHandled); - nsresult WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESelectionCollapseDirection aAction, + nsresult WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::EDirection aAction, PRBool *aCancel, PRBool *aHandled); nsresult WillMakeList(nsIDOMSelection *aSelection, PRBool aOrderd, PRBool *aCancel, PRBool *aHandled); nsresult WillRemoveList(nsIDOMSelection *aSelection, PRBool aOrderd, PRBool *aCancel, PRBool *aHandled); @@ -144,7 +146,11 @@ protected: nsresult AdjustSpecialBreaks(); nsresult AdjustWhitespace(nsIDOMSelection *aSelection); - nsresult AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::ESelectionCollapseDirection aAction); + nsresult AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirection aAction); + nsresult FindNearTextNode(nsIDOMNode *aSelNode, + PRInt32 aSelOffset, + nsIEditor::EDirection aDirection, + nsCOMPtr *outTextNode); nsresult RemoveEmptyNodes(); nsresult DoTextNodeWhitespace(nsIDOMCharacterData *aTextNode, PRInt32 aStart, PRInt32 aEnd); nsresult UpdateDocChangeRange(nsIDOMRange *aRange); @@ -193,6 +199,8 @@ public: 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: diff --git a/mozilla/editor/base/nsHTMLEditor.cpp b/mozilla/editor/base/nsHTMLEditor.cpp index 7e3c3cceaf3..2e4e8a40bf6 100644 --- a/mozilla/editor/base/nsHTMLEditor.cpp +++ b/mozilla/editor/base/nsHTMLEditor.cpp @@ -826,7 +826,7 @@ NS_IMETHODIMP nsHTMLEditor::InsertBR(nsCOMPtr *outBRNode) if (NS_FAILED(res)) return res; if (!bCollapsed) { - res = DeleteSelection(nsIEditor::eDoNothing); + res = DeleteSelection(nsIEditor::eNone); if (NS_FAILED(res)) return res; } nsCOMPtr selNode; @@ -1109,6 +1109,7 @@ NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsStr if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } nsAutoEditBatch batchIt(this); + nsAutoRules beginRulesSniffing(this, kOpRemoveTextProperty, nsIEditor::eNext); if (gNoisy) { @@ -1277,7 +1278,7 @@ NS_IMETHODIMP nsHTMLEditor::GetTypingStateValue(nsIAtom *aProperty, nsString &aV return NS_OK; } -NS_IMETHODIMP nsHTMLEditor::DeleteSelection(nsIEditor::ESelectionCollapseDirection aAction) +NS_IMETHODIMP nsHTMLEditor::DeleteSelection(nsIEditor::EDirection aAction) { if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } @@ -1286,6 +1287,7 @@ NS_IMETHODIMP nsHTMLEditor::DeleteSelection(nsIEditor::ESelectionCollapseDirecti // delete placeholder txns merge. nsAutoPlaceHolderBatch batch(this, gDeleteTxnName); + nsAutoRules beginRulesSniffing(this, kOpDeleteSelection, aAction); // pre-process nsresult result = GetSelection(getter_AddRefs(selection)); @@ -1315,17 +1317,22 @@ NS_IMETHODIMP nsHTMLEditor::InsertText(const nsString& aStringToInsert) nsCOMPtr selection; PRBool cancel, handled; - + PRInt32 theAction = nsTextEditRules::kInsertText; + PRInt32 opID = kOpInsertText; + if (mInIMEMode) + { + theAction = nsTextEditRules::kInsertTextIME; + opID = kOpInsertIMEText; + } nsAutoPlaceHolderBatch batch(this, nsnull); + nsAutoRules beginRulesSniffing(this, opID, nsIEditor::eNext); // pre-process nsresult result = GetSelection(getter_AddRefs(selection)); if (NS_FAILED(result)) return result; if (!selection) return NS_ERROR_NULL_POINTER; nsAutoString resultString; - nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertText); - // set a different action flag if we are an IME event - if (mInIMEMode) ruleInfo.action = nsTextEditRules::kInsertTextIME; + nsTextRulesInfo ruleInfo(theAction); ruleInfo.inString = &aStringToInsert; ruleInfo.outString = &resultString; ruleInfo.typeInState = *mTypeInState; @@ -1349,7 +1356,8 @@ NS_IMETHODIMP nsHTMLEditor::InsertText(const nsString& aStringToInsert) NS_IMETHODIMP nsHTMLEditor::InsertHTML(const nsString& aInputString) { nsAutoEditBatch beginBatching(this); - + nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext); + nsresult res; nsCOMPtrselection; @@ -1538,6 +1546,7 @@ nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSe return NS_ERROR_NULL_POINTER; nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext); nsCOMPtrselection; res = GetSelection(getter_AddRefs(selection)); @@ -1869,6 +1878,7 @@ nsHTMLEditor::GetListTags(nsStringArray *aTagList) return GetParentBlockTags(aTagList, PR_TRUE); } +/* // use this when block parents are to be added regardless of current state NS_IMETHODIMP nsHTMLEditor::AddBlockParent(nsString& aParentTag) @@ -1955,7 +1965,6 @@ nsHTMLEditor::ReplaceBlockParent(nsString& aParentTag) return res; } - NS_IMETHODIMP nsHTMLEditor::RemoveParagraphStyle() { @@ -1971,6 +1980,7 @@ nsHTMLEditor::RemoveParagraphStyle() nsAutoSelectionReset selectionResetter(selection); nsAutoEditBatch beginBatching(this); + nsCOMPtr enumerator; res = selection->GetEnumerator(getter_AddRefs(enumerator)); if (NS_FAILED(res)) return res; @@ -2019,6 +2029,7 @@ nsHTMLEditor::RemoveParent(const nsString &aParentTag) } return res; } +*/ NS_IMETHODIMP nsHTMLEditor::MakeOrChangeList(const nsString& aListType) @@ -2030,6 +2041,7 @@ nsHTMLEditor::MakeOrChangeList(const nsString& aListType) PRBool cancel, handled; nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, kOpMakeList, nsIEditor::eNext); // pre-process res = GetSelection(getter_AddRefs(selection)); @@ -2119,6 +2131,7 @@ nsHTMLEditor::RemoveList(const nsString& aListType) PRBool cancel, handled; nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, kOpRemoveList, nsIEditor::eNext); // pre-process res = GetSelection(getter_AddRefs(selection)); @@ -2148,6 +2161,7 @@ nsHTMLEditor::InsertBasicBlock(const nsString& aBlockType) PRBool cancel, handled; nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, kOpMakeBasicBlock, nsIEditor::eNext); // pre-process res = GetSelection(getter_AddRefs(selection)); @@ -2221,7 +2235,6 @@ nsHTMLEditor::InsertBasicBlock(const nsString& aBlockType) return res; } -// TODO: Implement "outdent" NS_IMETHODIMP nsHTMLEditor::Indent(const nsString& aIndent) { @@ -2229,8 +2242,15 @@ nsHTMLEditor::Indent(const nsString& aIndent) if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } PRBool cancel, handled; - + PRInt32 theAction = nsHTMLEditRules::kIndent; + PRInt32 opID = kOpIndent; + if (aIndent == "outdent") + { + theAction = nsHTMLEditRules::kOutdent; + opID = kOpOutdent; + } nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, opID, nsIEditor::eNext); // pre-process nsCOMPtr selection; @@ -2238,9 +2258,7 @@ nsHTMLEditor::Indent(const nsString& aIndent) if (NS_FAILED(res)) return res; if (!selection) return NS_ERROR_NULL_POINTER; - nsTextRulesInfo ruleInfo(nsHTMLEditRules::kIndent); - if (aIndent == "outdent") - ruleInfo.action = nsHTMLEditRules::kOutdent; + nsTextRulesInfo ruleInfo(theAction); res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); if (cancel || (NS_FAILED(res))) return res; @@ -2310,6 +2328,8 @@ NS_IMETHODIMP nsHTMLEditor::Align(const nsString& aAlignType) { nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, kOpAlign, nsIEditor::eNext); + nsCOMPtr node; PRBool cancel, handled; @@ -3435,7 +3455,7 @@ NS_IMETHODIMP nsHTMLEditor::Cut() res = Copy(); if (NS_SUCCEEDED(res)) - res = DeleteSelection(eDoNothing); + res = DeleteSelection(eNone); return res; } @@ -4118,6 +4138,34 @@ void nsHTMLEditor::ApplyStyleSheetToPresShellDocument(nsICSSStyleSheet* aSheet, } +#ifdef XP_MAC +#pragma mark - +#pragma mark --- nsEditor overrides --- +#pragma mark - +#endif + + +/** All editor operations which alter the doc should be prefaced + * with a call to StartOperation, naming the action and direction */ +NS_IMETHODIMP +nsHTMLEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection) +{ + if (mRules) return mRules->BeforeEdit(opID, aDirection); + return NS_OK; +} + + +/** 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) +{ + if (mRules) return mRules->AfterEdit(opID, aDirection); + return NS_OK; +} + + + #ifdef XP_MAC #pragma mark - #pragma mark --- Random methods --- @@ -4862,6 +4910,7 @@ nsHTMLEditor::ReParentBlockContent(nsIDOMNode *aNode, return res; } +/* NS_IMETHODIMP nsHTMLEditor::ReParentContentOfRange(nsIDOMRange *aRange, nsString &aParentTag, @@ -4901,7 +4950,7 @@ nsHTMLEditor::ReParentContentOfRange(nsIDOMRange *aRange, return res; } - +*/ NS_IMETHODIMP nsHTMLEditor::RemoveParagraphStyleFromRange(nsIDOMRange *aRange) @@ -5339,7 +5388,7 @@ nsHTMLEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parent result = selection->GetIsCollapsed(&collapsed); if (NS_SUCCEEDED(result) && !collapsed) { - result = DeleteSelection(nsIEditor::eDoNothing); + result = DeleteSelection(nsIEditor::eNone); if (NS_FAILED(result)) { return result; } diff --git a/mozilla/editor/base/nsHTMLEditor.h b/mozilla/editor/base/nsHTMLEditor.h index 8840fce5f17..49e5fb039cb 100644 --- a/mozilla/editor/base/nsHTMLEditor.h +++ b/mozilla/editor/base/nsHTMLEditor.h @@ -57,6 +57,20 @@ class nsHTMLEditor : public nsEditor, typedef enum {eNoOp, eReplaceParent=1, eInsertParent=2} BlockTransformationType; public: + + enum OperationID + { + kOpInsertBreak = 3000, + kOpMakeList = 3001, + kOpIndent = 3002, + kOpOutdent = 3003, + kOpAlign = 3004, + kOpMakeBasicBlock = 3005, + kOpRemoveList = 3006, + kOpInsertElement = 3008 + }; + + // see nsIHTMLEditor for documentation //Interfaces for addref and release and queryinterface @@ -97,7 +111,7 @@ public: NS_IMETHOD InsertHTML(const nsString &aInputString); NS_IMETHOD InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSelection); - NS_IMETHOD DeleteSelection(ESelectionCollapseDirection aAction); + NS_IMETHOD DeleteSelection(EDirection aAction); NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag, nsIDOMNode ** aNewNode); NS_IMETHOD SelectElement(nsIDOMElement* aElement); NS_IMETHOD SetCaretAfterElement(nsIDOMElement* aElement); @@ -107,11 +121,11 @@ public: NS_IMETHOD GetParentBlockTags(nsStringArray *aTagList, PRBool aGetLists); NS_IMETHOD GetParagraphTags(nsStringArray *aTagList); NS_IMETHOD GetListTags(nsStringArray *aTagList); - NS_IMETHOD RemoveParagraphStyle(); +// NS_IMETHOD RemoveParagraphStyle(); - NS_IMETHOD AddBlockParent(nsString& aParentTag); - NS_IMETHOD ReplaceBlockParent(nsString& aParentTag); - NS_IMETHOD RemoveParent(const nsString &aParentTag); +// NS_IMETHOD AddBlockParent(nsString& aParentTag); +// NS_IMETHOD ReplaceBlockParent(nsString& aParentTag); +// NS_IMETHOD RemoveParent(const nsString &aParentTag); NS_IMETHOD MakeOrChangeList(const nsString& aListType); NS_IMETHOD RemoveList(const nsString& aListType); @@ -225,6 +239,14 @@ public: /* ------------ nsICSSLoaderObserver -------------- */ NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aNotify); + /** All editor operations which alter the doc should be prefaced + * with a call to StartOperation, naming the action and direction */ + NS_IMETHOD 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_IMETHOD EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection); + /* ------------ Utility Routines, not part of public API -------------- */ NS_IMETHOD GetBodyStyleContext(nsIStyleContext** aStyleContext); @@ -307,10 +329,10 @@ protected: BlockTransformationType aTranformation, nsIDOMNode **aNewParentNode); - NS_IMETHOD ReParentContentOfRange(nsIDOMRange *aRange, +/* NS_IMETHOD ReParentContentOfRange(nsIDOMRange *aRange, nsString &aParentTag, BlockTransformationType aTranformation); - +*/ NS_IMETHOD RemoveParagraphStyleFromRange(nsIDOMRange *aRange); NS_IMETHOD RemoveParagraphStyleFromBlockContent(nsIDOMRange *aRange); diff --git a/mozilla/editor/base/nsHTMLEditorLog.cpp b/mozilla/editor/base/nsHTMLEditorLog.cpp index fd98ad8eaee..77b42245c8d 100644 --- a/mozilla/editor/base/nsHTMLEditorLog.cpp +++ b/mozilla/editor/base/nsHTMLEditorLog.cpp @@ -142,7 +142,7 @@ nsHTMLEditorLog::RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttri } NS_IMETHODIMP -nsHTMLEditorLog::DeleteSelection(nsIEditor::ESelectionCollapseDirection aAction) +nsHTMLEditorLog::DeleteSelection(nsIEditor::EDirection aAction) { nsAutoHTMLEditorLogLock logLock(this); diff --git a/mozilla/editor/base/nsHTMLEditorLog.h b/mozilla/editor/base/nsHTMLEditorLog.h index 100836149af..4c5ca3ab5de 100644 --- a/mozilla/editor/base/nsHTMLEditorLog.h +++ b/mozilla/editor/base/nsHTMLEditorLog.h @@ -57,7 +57,7 @@ public: const nsString *aValue); NS_IMETHOD SetParagraphFormat(const nsString& aParagraphFormat); NS_IMETHOD RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttribute); - NS_IMETHOD DeleteSelection(nsIEditor::ESelectionCollapseDirection aAction); + NS_IMETHOD DeleteSelection(nsIEditor::EDirection aAction); NS_IMETHOD TypedText(const nsString& aString, PRInt32 aAction); NS_IMETHOD InsertText(const nsString& aStringToInsert); NS_IMETHOD InsertBreak(); diff --git a/mozilla/editor/base/nsTextEditRules.cpp b/mozilla/editor/base/nsTextEditRules.cpp index 16eafd3e849..41b51c307bf 100644 --- a/mozilla/editor/base/nsTextEditRules.cpp +++ b/mozilla/editor/base/nsTextEditRules.cpp @@ -36,9 +36,11 @@ #include "nsIEnumerator.h" #include "nsLayoutCID.h" #include "nsIEditProperty.h" +#include "nsEditorUtils.h" static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); + #define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \ if ((mFlags & nsIHTMLEditor::eEditorReadonlyMask) || (mFlags & nsIHTMLEditor::eEditorDisabledMask)) \ { \ @@ -53,6 +55,8 @@ static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); nsTextEditRules::nsTextEditRules() : mEditor(nsnull) , mFlags(0) // initialized to 0 ("no flags set"). Real initial value is given in Init() +, mActionNesting(0) +, mLockRulesSniffing(PR_FALSE) { } @@ -109,20 +113,54 @@ nsTextEditRules::SetFlags(PRUint32 aFlags) { // we are converting TO a plaintext editor // put a "white-space: pre" style on the body - nsCOMPtr bodyElement; - nsresult res = mEditor->GetBodyElement(getter_AddRefs(bodyElement)); - if (NS_FAILED(res)) return res; - if (!bodyElement) return NS_ERROR_NULL_POINTER; + nsCOMPtr bodyElement; + nsresult res = mEditor->GetBodyElement(getter_AddRefs(bodyElement)); + if (NS_FAILED(res)) return res; + if (!bodyElement) return NS_ERROR_NULL_POINTER; // not going through the editor to do this. // XXX This is not the right way to do this; we need an editor style // system so that we can add & replace style attrs. - bodyElement->SetAttribute("style", PlaintextInitalStyle); + bodyElement->SetAttribute("style", PlaintextInitalStyle); } } mFlags = aFlags; return NS_OK; } +NS_IMETHODIMP +nsTextEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection) +{ + if (mLockRulesSniffing) return NS_OK; + + nsAutoLockRulesSniffing lockIt(this); + + mActionNesting++; + return NS_OK; +} + + +NS_IMETHODIMP +nsTextEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) +{ + if (mLockRulesSniffing) return NS_OK; + + nsAutoLockRulesSniffing lockIt(this); + + NS_PRECONDITION(mActionNesting>0, "bad action nesting!"); + nsresult res = NS_OK; + if (!--mActionNesting) + { + nsCOMPtrselection; + res = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + // detect empty doc + res = CreateBogusNodeIfNeeded(selection); + } + return res; +} + + NS_IMETHODIMP nsTextEditRules::WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, @@ -336,7 +374,7 @@ nsTextEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, P if (NS_FAILED(res)) return res; if (!bCollapsed) { - res = mEditor->DeleteSelection(nsIEditor::eDoNothing); + res = mEditor->DeleteSelection(nsIEditor::eNone); if (NS_FAILED(res)) return res; } @@ -378,6 +416,11 @@ nsTextEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, P nsresult nsTextEditRules::DidInsertBreak(nsIDOMSelection *aSelection, nsresult aResult) { + // we only need to execute the stuff below if we are a plaintext editor. + // html editors have a different mechanism for putting in mozBR's + // (because there are a bunch more placesyou have to worry about it in html) + if (!nsIHTMLEditor::eEditorPlaintextMask & mFlags) return NS_OK; + // if we are at the end of the document, we need to insert // a special mozBR following the normal br, and then set the // selection to after the mozBR. @@ -412,16 +455,16 @@ nsTextEditRules::DidInsertBreak(nsIDOMSelection *aSelection, nsresult aResult) // with a mozBR already exiting after it. In this case we have to // move the selection to after the mozBR so it will show up on the // empty line. - nsCOMPtr nextNode; - res = GetNextHTMLNode(nearNode, &nextNode); - if (NS_FAILED(res)) return res; - if (IsMozBR(nextNode)) - { - res = nsEditor::GetNodeLocation(nextNode, &selNode, &selOffset); - if (NS_FAILED(res)) return res; - res = aSelection->Collapse(selNode,selOffset+1); - if (NS_FAILED(res)) return res; - } + nsCOMPtr nextNode; + res = GetNextHTMLNode(nearNode, &nextNode); + if (NS_FAILED(res)) return res; + if (IsMozBR(nextNode)) + { + res = nsEditor::GetNodeLocation(nextNode, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + res = aSelection->Collapse(selNode,selOffset+1); + if (NS_FAILED(res)) return res; + } } } return res; @@ -465,7 +508,7 @@ nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection, if (NS_FAILED(res)) return res; if (!bCollapsed) { - res = mEditor->DeleteSelection(nsIEditor::eDoNothing); + res = mEditor->DeleteSelection(nsIEditor::eNone); if (NS_FAILED(res)) return res; } @@ -533,9 +576,9 @@ nsTextEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, TypeInSta // splitting anchor twice sets newTextNode as an empty text node between // two halves of the original text node res = mEditor->SplitNode(anchorAsText, offset, getter_AddRefs(newTextNode)); - if (NS_SUCCEEDED(res)) { - res = mEditor->SplitNode(anchorAsText, 0, getter_AddRefs(newTextNode)); - } + if (NS_SUCCEEDED(res)) { + res = mEditor->SplitNode(anchorAsText, 0, getter_AddRefs(newTextNode)); + } } } // now we have the new text node we are going to insert into. @@ -552,8 +595,8 @@ nsTextEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, TypeInSta { nsCOMPtrparent; res = newTextNode->GetParentNode(getter_AddRefs(parent)); - if (NS_FAILED(res)) return res; - if (!parent) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(res)) return res; + if (!parent) return NS_ERROR_NULL_POINTER; res = mEditor->RemoveTextPropertiesForNode (newTextNode, parent, 0, 0, nsIEditProperty::b, nsnull); } } @@ -566,8 +609,8 @@ nsTextEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, TypeInSta { nsCOMPtrparent; res = newTextNode->GetParentNode(getter_AddRefs(parent)); - if (NS_FAILED(res)) return res; - if (!parent) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(res)) return res; + if (!parent) return NS_ERROR_NULL_POINTER; res = mEditor->RemoveTextPropertiesForNode (newTextNode, parent, 0, 0, nsIEditProperty::i, nsnull); } } @@ -580,8 +623,8 @@ nsTextEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, TypeInSta { nsCOMPtrparent; res = newTextNode->GetParentNode(getter_AddRefs(parent)); - if (NS_FAILED(res)) return res; - if (!parent) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(res)) return res; + if (!parent) return NS_ERROR_NULL_POINTER; res = mEditor->RemoveTextPropertiesForNode (newTextNode, parent, 0, 0, nsIEditProperty::u, nsnull); } } @@ -633,7 +676,7 @@ nsTextEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, TypeInSta if (!bodyElement) return NS_ERROR_NULL_POINTER; parent = do_QueryInterface(bodyElement); // offset already set to 0 - } + } if (!parent) { return NS_ERROR_NULL_POINTER; } nsAutoString attr, value; @@ -719,8 +762,8 @@ nsTextEditRules::CreateFontStyleForInsertText(nsIDOMNode *aNewTextNode, { nsCOMPtrparent; res = aNewTextNode->GetParentNode(getter_AddRefs(parent)); - if (NS_FAILED(res)) return res; - if (!parent) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(res)) return res; + if (!parent) return NS_ERROR_NULL_POINTER; res = mEditor->RemoveTextPropertiesForNode (aNewTextNode, parent, 0, 0, nsIEditProperty::font, &aAttr); } return res; @@ -738,8 +781,8 @@ nsTextEditRules::InsertStyleNode(nsIDOMNode *aNode, nsresult res; nsCOMPtrparent; res = aNode->GetParentNode(getter_AddRefs(parent)); - if (NS_FAILED(res)) return res; - if (!parent) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(res)) return res; + if (!parent) return NS_ERROR_NULL_POINTER; nsAutoString tag; aTag->ToString(tag); @@ -754,8 +797,8 @@ nsTextEditRules::InsertStyleNode(nsIDOMNode *aNode, if (NS_FAILED(res)) return res; res = mEditor->CreateNode(tag, parent, offsetInParent, aNewNode); - if (NS_FAILED(res)) return res; - if (!aNewNode) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(res)) return res; + if (!aNewNode) return NS_ERROR_NULL_POINTER; res = mEditor->DeleteNode(aNode); if (NS_SUCCEEDED(res)) @@ -849,8 +892,8 @@ nsTextEditRules::InsertStyleAndNewTextNode(nsIDOMNode *aParentNode, if (NS_FAILED(res)) { return res; } res = mEditor->CreateNode(textNodeTag, newStyleNode, 0, getter_AddRefs(newTextNode)); - if (NS_FAILED(res)) return res; - if (!newTextNode) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(res)) return res; + if (!newTextNode) return NS_ERROR_NULL_POINTER; // if we have a selection collapse the selection to the beginning of the new text node if (aInOutSelection) { @@ -901,7 +944,7 @@ nsTextEditRules::DidRemoveTextProperty(nsIDOMSelection *aSelection, nsresult aRe nsresult nsTextEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, - nsIEditor::ESelectionCollapseDirection aCollapsedAction, + nsIEditor::EDirection aCollapsedAction, PRBool *aCancel, PRBool *aHandled) { @@ -924,10 +967,10 @@ nsTextEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, mEditor->GetTextSelectionOffsets(aSelection, start, end); if (end==start) { // collapsed selection - if (nsIEditor::eDeletePrevious==aCollapsedAction && 0anchor; PRInt32 offset; - res = aSelection->GetAnchorNode(getter_AddRefs(anchor)); + res = aSelection->GetAnchorNode(getter_AddRefs(anchor)); if (NS_FAILED(res)) return res; if (!anchor) return NS_ERROR_NULL_POINTER; res = aSelection->GetAnchorOffset(&offset); @@ -1005,8 +1047,8 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection, siblingNodeAsText->GetLength(&siblingLength); nsCOMPtr parentNode; res = selectedNode->GetParentNode(getter_AddRefs(parentNode)); - if (NS_FAILED(res)) return res; - if (!parentNode) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(res)) return res; + if (!parentNode) return NS_ERROR_NULL_POINTER; res = mEditor->JoinNodes(siblingNode, selectedNode, parentNode); // selectedNode will remain after the join, siblingNode is removed } @@ -1022,11 +1064,11 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection, selectedNodeAsText->GetLength(&selectedNodeLength); nsCOMPtr parentNode; res = selectedNode->GetParentNode(getter_AddRefs(parentNode)); - if (NS_FAILED(res)) return res; - if (!parentNode) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(res)) return res; + if (!parentNode) return NS_ERROR_NULL_POINTER; res = mEditor->JoinNodes(selectedNode, siblingNode, parentNode); - if (NS_FAILED(res)) return res; + if (NS_FAILED(res)) return res; // selectedNode will remain after the join, siblingNode is removed // set selection res = aSelection->Collapse(siblingNode, selectedNodeLength); @@ -1206,12 +1248,16 @@ nsTextEditRules::CreateBogusNodeIfNeeded(nsIDOMSelection *aSelection) if (!aSelection) { return NS_ERROR_NULL_POINTER; } if (!mEditor) { return NS_ERROR_NULL_POINTER; } if (mBogusNode) return NS_OK; // let's not create more than one, ok? + + // tell rules system to not do any post-processing + nsAutoRules beginRulesSniffing(mEditor, nsEditor::kOpIgnore, nsIEditor::eNone); - nsCOMPtr bodyElement; - nsresult res = mEditor->GetBodyElement(getter_AddRefs(bodyElement)); - if (NS_FAILED(res)) return res; - if (!bodyElement) return NS_ERROR_NULL_POINTER; - nsCOMPtrbodyNode = do_QueryInterface(bodyElement); + nsCOMPtr bodyElement; + + nsresult res = mEditor->GetBodyElement(getter_AddRefs(bodyElement)); + if (NS_FAILED(res)) return res; + if (!bodyElement) return NS_ERROR_NULL_POINTER; + nsCOMPtrbodyNode = do_QueryInterface(bodyElement); // now we've got the body tag. // iterate the body tag, looking for editable content @@ -1270,14 +1316,14 @@ nsTextEditRules::TruncateInsertionIfNeeded(nsIDOMSelection *aSelection, { // Get the current text length. // Get the length of inString. - // Get the length of the selection. - // If selection is collapsed, it is length 0. - // Subtract the length of the selection from the len(doc) - // since we'll delete the selection on insert. - // This is resultingDocLength. + // Get the length of the selection. + // If selection is collapsed, it is length 0. + // Subtract the length of the selection from the len(doc) + // since we'll delete the selection on insert. + // This is resultingDocLength. // If (resultingDocLength) is at or over max, cancel the insert // If (resultingDocLength) + (length of input) > max, - // set aOutString to subset of inString so length = max + // set aOutString to subset of inString so length = max PRInt32 docLength; res = mEditor->GetDocumentLength(&docLength); if (NS_FAILED(res)) { return res; } diff --git a/mozilla/editor/base/nsTextEditRules.h b/mozilla/editor/base/nsTextEditRules.h index 8b76f37afce..db89294a7e2 100644 --- a/mozilla/editor/base/nsTextEditRules.h +++ b/mozilla/editor/base/nsTextEditRules.h @@ -51,6 +51,8 @@ public: // nsEditRules 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); NS_IMETHOD GetFlags(PRUint32 *aFlags); @@ -103,11 +105,11 @@ protected: nsresult DidInsert(nsIDOMSelection *aSelection, nsresult aResult); nsresult WillDeleteSelection(nsIDOMSelection *aSelection, - nsIEditor::ESelectionCollapseDirection aCollapsedAction, + nsIEditor::EDirection aCollapsedAction, PRBool *aCancel, PRBool *aHandled); nsresult DidDeleteSelection(nsIDOMSelection *aSelection, - nsIEditor::ESelectionCollapseDirection aCollapsedAction, + nsIEditor::EDirection aCollapsedAction, nsresult aResult); nsresult WillSetTextProperty(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aHandled); @@ -222,11 +224,17 @@ protected: nsresult CreateMozBR(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outBRNode); - // data + // data members nsHTMLEditor *mEditor; // note that we do not refcount the editor nsString mPasswordText; // a buffer we use to store the real value of password editors nsCOMPtr mBogusNode; // magic node acts as placeholder in empty doc PRUint32 mFlags; + PRUint32 mActionNesting; + PRBool mLockRulesSniffing; + + // friends + friend class nsAutoLockRulesSniffing; + }; @@ -242,7 +250,7 @@ class nsTextRulesInfo : public nsRulesInfo outputFormat(0), typeInState(), maxLength(-1), - collapsedAction(nsIEditor::eDeleteNext), + collapsedAction(nsIEditor::eNext), bOrdered(PR_FALSE), alignType(0), blockType(0), @@ -259,7 +267,7 @@ class nsTextRulesInfo : public nsRulesInfo PRInt32 maxLength; // kDeleteSelection - nsIEditor::ESelectionCollapseDirection collapsedAction; + nsIEditor::EDirection collapsedAction; // kMakeList PRBool bOrdered; @@ -274,5 +282,26 @@ class nsTextRulesInfo : public nsRulesInfo const nsIDOMElement* insertElement; }; + +/*************************************************************************** + * stack based helper class for StartOperation()/EndOperation() sandwich. + * this class sets a bool letting us know to ignore any rules sniffing + * that tries to occur reentrantly. + */ +class nsAutoLockRulesSniffing +{ + public: + + nsAutoLockRulesSniffing(nsTextEditRules *rules) : mRules(rules) + {if (mRules) mRules->mLockRulesSniffing = PR_TRUE;} + ~nsAutoLockRulesSniffing() + {if (mRules) mRules->mLockRulesSniffing = PR_FALSE;} + + protected: + nsTextEditRules *mRules; +}; + + + #endif //nsTextEditRules_h__ diff --git a/mozilla/editor/composer/src/nsEditorShell.cpp b/mozilla/editor/composer/src/nsEditorShell.cpp index 7a643830471..45261cede2c 100644 --- a/mozilla/editor/composer/src/nsEditorShell.cpp +++ b/mozilla/editor/composer/src/nsEditorShell.cpp @@ -625,7 +625,7 @@ nsEditorShell::DeleteCharForward() { nsCOMPtr editor = do_QueryInterface(mEditor); if (!editor) return NS_ERROR_NOT_INITIALIZED; - nsresult rv = editor->DeleteSelection(nsIEditor::eDeleteNext); + nsresult rv = editor->DeleteSelection(nsIEditor::eNext); ScrollSelectionIntoView(); return rv; } @@ -635,7 +635,7 @@ nsEditorShell::DeleteCharBackward() { nsCOMPtr editor = do_QueryInterface(mEditor); if (!editor) return NS_ERROR_NOT_INITIALIZED; - nsresult rv = editor->DeleteSelection(nsIEditor::eDeletePrevious); + nsresult rv = editor->DeleteSelection(nsIEditor::ePrevious); ScrollSelectionIntoView(); return rv; } @@ -645,7 +645,7 @@ nsEditorShell::DeleteWordForward() { nsCOMPtr editor = do_QueryInterface(mEditor); if (!editor) return NS_ERROR_NOT_INITIALIZED; - nsresult rv = editor->DeleteSelection(nsIEditor::eDeleteNextWord); + nsresult rv = editor->DeleteSelection(nsIEditor::eNextWord); ScrollSelectionIntoView(); return rv; } @@ -655,7 +655,7 @@ nsEditorShell::DeleteWordBackward() { nsCOMPtr editor = do_QueryInterface(mEditor); if (!editor) return NS_ERROR_NOT_INITIALIZED; - nsresult rv = editor->DeleteSelection(nsIEditor::eDeletePreviousWord); + nsresult rv = editor->DeleteSelection(nsIEditor::ePreviousWord); ScrollSelectionIntoView(); return rv; } @@ -665,7 +665,7 @@ nsEditorShell::DeleteToEndOfLine() { nsCOMPtr editor = do_QueryInterface(mEditor); if (!editor) return NS_ERROR_NOT_INITIALIZED; - nsresult rv = editor->DeleteSelection(nsIEditor::eDeleteToEndOfLine); + nsresult rv = editor->DeleteSelection(nsIEditor::eToEndOfLine); ScrollSelectionIntoView(); return rv; } @@ -2038,18 +2038,18 @@ NS_IMETHODIMP nsEditorShell::DeleteSelection(PRInt32 action) { nsresult err = NS_NOINTERFACE; - nsIEditor::ESelectionCollapseDirection selectionAction; + nsIEditor::EDirection selectionAction; switch(action) { case 1: - selectionAction = nsIEditor::eDeleteNext; + selectionAction = nsIEditor::eNext; break; case 2: - selectionAction = nsIEditor::eDeletePrevious; + selectionAction = nsIEditor::ePrevious; break; default: - selectionAction = nsIEditor::eDoNothing; + selectionAction = nsIEditor::eNone; break; } diff --git a/mozilla/editor/libeditor/base/DeleteRangeTxn.cpp b/mozilla/editor/libeditor/base/DeleteRangeTxn.cpp index 7b315af46bb..14d63c38f41 100644 --- a/mozilla/editor/libeditor/base/DeleteRangeTxn.cpp +++ b/mozilla/editor/libeditor/base/DeleteRangeTxn.cpp @@ -127,7 +127,7 @@ NS_IMETHODIMP DeleteRangeTxn::Do(void) else { // the selection ends in a different node from where it started // delete the relevant content in the start node - result = CreateTxnsToDeleteContent(mStartParent, mStartOffset, nsIEditor::eDeleteNext); + result = CreateTxnsToDeleteContent(mStartParent, mStartOffset, nsIEditor::eNext); if (NS_SUCCEEDED(result)) { // delete the intervening nodes @@ -135,7 +135,7 @@ NS_IMETHODIMP DeleteRangeTxn::Do(void) if (NS_SUCCEEDED(result)) { // delete the relevant content in the end node - result = CreateTxnsToDeleteContent(mEndParent, mEndOffset, nsIEditor::eDeletePrevious); + result = CreateTxnsToDeleteContent(mEndParent, mEndOffset, nsIEditor::ePrevious); } } } @@ -286,7 +286,7 @@ DeleteRangeTxn::CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent, NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent, PRUint32 aOffset, - nsIEditor::ESelectionCollapseDirection aAction) + nsIEditor::EDirection aAction) { nsresult result; // see what kind of node we have @@ -295,7 +295,7 @@ NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent, if (textNode) { // if the node is a text node, then delete text content PRUint32 start, numToDelete; - if (nsIEditor::eDeleteNext == aAction) + if (nsIEditor::eNext == aAction) { start=aOffset; textNode->GetLength(&numToDelete); diff --git a/mozilla/editor/libeditor/base/DeleteRangeTxn.h b/mozilla/editor/libeditor/base/DeleteRangeTxn.h index ef9e2505324..638b2b6f546 100644 --- a/mozilla/editor/libeditor/base/DeleteRangeTxn.h +++ b/mozilla/editor/libeditor/base/DeleteRangeTxn.h @@ -87,7 +87,7 @@ protected: NS_IMETHOD CreateTxnsToDeleteContent(nsIDOMNode *aParent, PRUint32 aOffset, - nsIEditor::ESelectionCollapseDirection aAction); + nsIEditor::EDirection aAction); protected: diff --git a/mozilla/editor/libeditor/base/nsEditRules.h b/mozilla/editor/libeditor/base/nsEditRules.h index 2722a5a9318..e4c54ea1852 100644 --- a/mozilla/editor/libeditor/base/nsEditRules.h +++ b/mozilla/editor/libeditor/base/nsEditRules.h @@ -48,6 +48,8 @@ class nsEditRules { public: 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)=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; @@ -55,5 +57,6 @@ public: NS_IMETHOD DocumentIsEmpty(PRBool *aDocumentIsEmpty)=0; }; + #endif //nsEditRules_h__ diff --git a/mozilla/editor/libeditor/base/nsEditor.cpp b/mozilla/editor/libeditor/base/nsEditor.cpp index acaaba67c77..848f37a1c78 100644 --- a/mozilla/editor/libeditor/base/nsEditor.cpp +++ b/mozilla/editor/libeditor/base/nsEditor.cpp @@ -444,6 +444,8 @@ nsEditor::Undo(PRUint32 aCount) if (gNoisy) { printf("Editor::Undo ----------\n"); } nsresult result = NS_OK; + nsAutoRules beginRulesSniffing(this, kOpUndo, nsIEditor::eNone); + BeginUpdateViewBatch(); if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) @@ -489,6 +491,7 @@ nsEditor::Redo(PRUint32 aCount) if (gNoisy) { printf("Editor::Redo ----------\n"); } nsresult result = NS_OK; + nsAutoRules beginRulesSniffing(this, kOpRedo, nsIEditor::eNone); BeginUpdateViewBatch(); if ((nsITransactionManager *)nsnull!=mTxnMgr.get()) @@ -916,6 +919,8 @@ NS_IMETHODIMP nsEditor::CreateNode(const nsString& aTag, PRInt32 i; nsIEditActionListener *listener; + nsAutoRules beginRulesSniffing(this, kOpCreateNode, nsIEditor::eNext); + if (mActionListeners) { for (i = 0; i < mActionListeners->Count(); i++) @@ -960,6 +965,7 @@ NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode, { PRInt32 i; nsIEditActionListener *listener; + nsAutoRules beginRulesSniffing(this, kOpInsertNode, nsIEditor::eNext); if (mActionListeners) { @@ -1000,6 +1006,7 @@ nsEditor::SplitNode(nsIDOMNode * aNode, { PRInt32 i; nsIEditActionListener *listener; + nsAutoRules beginRulesSniffing(this, kOpSplitNode, nsIEditor::eNext); if (mActionListeners) { @@ -1147,6 +1154,7 @@ nsEditor::JoinNodes(nsIDOMNode * aLeftNode, { PRInt32 i; nsIEditActionListener *listener; + nsAutoRules beginRulesSniffing(this, kOpJoinNode, nsIEditor::ePrevious); if (mActionListeners) { @@ -1185,6 +1193,7 @@ NS_IMETHODIMP nsEditor::DeleteNode(nsIDOMNode * aElement) { PRInt32 i; nsIEditActionListener *listener; + nsAutoRules beginRulesSniffing(this, kOpCreateNode, nsIEditor::ePrevious); if (mActionListeners) { @@ -1509,6 +1518,24 @@ nsEditor::GetBodyElement(nsIDOMElement **aBodyElement) } +/** All editor operations which alter the doc should be prefaced + * with a call to StartOperation, naming the action and direction */ +NS_IMETHODIMP +nsEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection) +{ + return NS_OK; +} + + +/** 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) +{ + return NS_OK; +} + + // Objects must be DOM elements NS_IMETHODIMP nsEditor::CloneAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode) @@ -1614,12 +1641,23 @@ nsresult nsEditor::GetTextNodeTag(nsString& aOutString) NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert) { - EditAggregateTxn *aggTxn = nsnull; - // Create the "delete current selection" txn - nsresult result = CreateAggregateTxnForDeleteSelection(InsertTextTxn::gInsertTextTxnName, &aggTxn); - if ((NS_FAILED(result)) || (nsnull==aggTxn)) { - return NS_ERROR_OUT_OF_MEMORY; + // First delete the selection if needed + nsCOMPtr selection; + nsresult result = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; + + PRBool bIsCollapsed; + selection->GetIsCollapsed(&bIsCollapsed); + if (!bIsCollapsed) + { + result = DeleteSelectionImpl(nsIEditor::eNone); + if (NS_FAILED(result)) return result; } + + PRInt32 action = kOpInsertText; + if (mInIMEMode) action = kOpInsertIMEText; + nsAutoRules beginRulesSniffing(this, action, nsIEditor::eNext); nsCOMPtr nodeAsText; PRInt32 offset; @@ -1635,11 +1673,6 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert) mIMETextNode = nodeAsText; mIMETextOffset = offset; } -// else if (mIMETextNode != nodeAsText) || (mIMETextOffset+mIMEBufferLength != offset)) -// { -// // IME and we do not agree about where we are. Force a commit of -// // earlier ime txn. We don't have a way to do that yet. -// } result = CreateTxnForIMEText(aStringToInsert,(IMETextTxn**)&txn); } else @@ -1663,7 +1696,6 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert) } BeginUpdateViewBatch(); - result = Do(aggTxn); result = Do(txn); // The transaction system (if any) has taken ownwership of txns. // aggTxn released at end of routine. @@ -1682,63 +1714,34 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert) } } - else if (NS_ERROR_EDITOR_NO_SELECTION==result) - { - result = DoInitialInsert(aStringToInsert); - } else if (NS_ERROR_EDITOR_NO_TEXTNODE==result) { - - // only execute the aggTxn if we actually populated it with at least one sub-txn - PRUint32 count=0; - aggTxn->GetCount(&count); - if (count) - { - result = Do(aggTxn); - } - else - { - result = NS_OK; - } - // The transaction system (if any) has taken ownwership of txns - NS_IF_RELEASE(aggTxn); - // create the text node - if (NS_SUCCEEDED(result)) + nsCOMPtr selectedNode; + result = selection->GetAnchorNode(getter_AddRefs(selectedNode)); + if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offset)) && selectedNode) { - nsCOMPtr selection; - result = GetSelection(getter_AddRefs(selection)); - if ((NS_SUCCEEDED(result)) && selection) + nsCOMPtr newNode; + nsAutoString textNodeTag; + result = GetTextNodeTag(textNodeTag); + if (NS_FAILED(result)) { return result; } + result = CreateNode(textNodeTag, selectedNode, offset, + getter_AddRefs(newNode)); + if (NS_SUCCEEDED(result) && newNode) { - nsCOMPtr selectedNode; - result = selection->GetAnchorNode(getter_AddRefs(selectedNode)); - if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offset)) && selectedNode) + nsCOMPtrnewTextNode; + newTextNode = do_QueryInterface(newNode); + if (newTextNode) { - nsCOMPtr newNode; - nsAutoString textNodeTag; - result = GetTextNodeTag(textNodeTag); - if (NS_FAILED(result)) { return result; } - result = CreateNode(textNodeTag, selectedNode, offset, - getter_AddRefs(newNode)); - if (NS_SUCCEEDED(result) && newNode) - { - nsCOMPtrnewTextNode; - 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? - } - } + nsAutoString placeholderText(" "); + newTextNode->SetData(placeholderText); + selection->Collapse(newNode, 0); + selection->Extend(newNode, 1); + result = InsertTextImpl(aStringToInsert); // this really recurses, right? } - } - } + } + } } - // The transaction system (if any) has taken ownwership of txns - NS_IF_RELEASE(aggTxn); return result; } @@ -1749,48 +1752,34 @@ NS_IMETHODIMP nsEditor::PrepareToInsertText(nsCOMPtr *aOutT nsCOMPtr nodeAsText; nsCOMPtr selection; - if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED; - nsCOMPtr ps = do_QueryReferent(mPresShellWeak); - if (!ps) return NS_ERROR_NOT_INITIALIZED; - result = ps->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection)); - if ((NS_SUCCEEDED(result)) && selection) + result = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(result)) return result; + if (!selection) return NS_ERROR_NULL_POINTER; + nsCOMPtr selNode; + result = GetStartNodeAndOffset(selection, &selNode, aOutOffset); + if (NS_FAILED(result)) return result; + if (!selNode) { - result = NS_ERROR_UNEXPECTED; - nsCOMPtr enumerator; - result = selection->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_SUCCEEDED(result) && enumerator) - { - enumerator->First(); - nsCOMPtr currentItem; - result = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if ((NS_SUCCEEDED(result)) && (currentItem)) - { - result = NS_ERROR_UNEXPECTED; - nsCOMPtr range( do_QueryInterface(currentItem) ); - if (range) - { - nsCOMPtr node; - result = range->GetStartParent(getter_AddRefs(node)); - if ((NS_SUCCEEDED(result)) && (node)) - { - nodeAsText = do_QueryInterface(node); - range->GetStartOffset(aOutOffset); - if (nodeAsText) - { - *aOutTextNode = nodeAsText; - } - else - { - result = NS_ERROR_EDITOR_NO_TEXTNODE; - } - } - } - } - else - { - result = NS_ERROR_EDITOR_NO_SELECTION; - } - } + // should nsEditor really be concerned with the body node? + // That's an html concept. + nsCOMPtrbodyElement; + result = GetBodyElement(getter_AddRefs(bodyElement)); + if (NS_FAILED(result)) return result; + if (!bodyElement) return NS_ERROR_NULL_POINTER; + nsCOMPtrbodyNode = do_QueryInterface(bodyElement); + selection->Collapse(bodyNode,0); + result = GetStartNodeAndOffset(selection, &selNode, aOutOffset); + if (NS_FAILED(result)) return result; + } + + nodeAsText = do_QueryInterface(selNode); + if (nodeAsText) + { + *aOutTextNode = nodeAsText; + } + else + { + result = NS_ERROR_EDITOR_NO_TEXTNODE; } return result; } @@ -1961,130 +1950,13 @@ NS_IMETHODIMP nsEditor::CreateTxnForInsertText(const nsString & aStringToInsert, return result; } -// we're in the special situation where there is no selection. Insert the text -// at the beginning of the document. -// XXX: this is all logic that must be moved to the rule system -// for HTML, we create a text node on the body. That's what is done below -// for XML, we would create a text node on the root element. -// The rule system should be telling us which of these (or any other variant) to do. -/* this method should look something like - BeginTransaction() - mRule->GetNodeForInitialInsert(parentNode) - mRule->CreateInitialDocumentFragment(childNode) - InsertElement(childNode, parentNode) - find the first text node in childNode - insert the text there -*/ - -NS_IMETHODIMP nsEditor::DoInitialInsert(const nsString & aStringToInsert) -{ - if (!mDocWeak) { - return NS_ERROR_NOT_INITIALIZED; - } - - nsCOMPtrnodeList; - nsAutoString bodyTag = "body"; - nsCOMPtr doc = do_QueryReferent(mDocWeak); - if (!doc) return NS_ERROR_NOT_INITIALIZED; - nsresult result = doc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList)); - if ((NS_SUCCEEDED(result)) && nodeList) - { - PRUint32 count; - nodeList->GetLength(&count); - NS_ASSERTION(1==count, "there is not exactly 1 body in the document!"); - nsCOMPtrnode; - result = nodeList->Item(0, getter_AddRefs(node)); - if ((NS_SUCCEEDED(result)) && node) - { // now we've got the body tag. - // create transaction to insert the text node, - // and create a transaction to insert the text - CreateElementTxn *txn; - nsAutoString textNodeTag; - result = GetTextNodeTag(textNodeTag); - if (NS_FAILED(result)) { return result; } - result = CreateTxnForCreateElement(textNodeTag, node, 0, &txn); - if ((NS_SUCCEEDED(result)) && txn) - { - result = Do(txn); - if (NS_SUCCEEDED(result)) - { - nsCOMPtrnewNode; - txn->GetNewNode(getter_AddRefs(newNode)); - if ((NS_SUCCEEDED(result)) && newNode) - { - nsCOMPtrnewTextNode; - newTextNode = do_QueryInterface(newNode); - if (newTextNode) - { - EditTxn *insertTxn; - if (mInIMEMode) - { - nsCOMPtr selection; - result = GetSelection(getter_AddRefs(selection)); - if ((NS_SUCCEEDED(result)) && selection) - { - selection->Collapse(newTextNode, 0); - mIMETextNode = newTextNode; - mIMETextOffset = 0; - mIMEBufferLength = 0; - result = CreateTxnForIMEText(aStringToInsert,(IMETextTxn**)&txn); - } - } - else - { - result = CreateTxnForInsertText(aStringToInsert, newTextNode, 0, (InsertTextTxn**)&txn); - } - if (NS_SUCCEEDED(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(newTextNode, 0, aStringToInsert); - } - } - - result = Do(insertTxn); - - // let listeners know what happened - if (mActionListeners) - { - for (i = 0; i < mActionListeners->Count(); i++) - { - listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); - if (listener) - listener->DidInsertText(newTextNode, 0, aStringToInsert, result); - } - } - - } - // The transaction system (if any) has taken ownwership of txn - NS_IF_RELEASE(insertTxn); - } - else { - result = NS_ERROR_UNEXPECTED; - } - } - } - } - // The transaction system (if any) has taken ownwership of txn - NS_IF_RELEASE(txn); - } - } - return result; -} - NS_IMETHODIMP nsEditor::DeleteText(nsIDOMCharacterData *aElement, PRUint32 aOffset, PRUint32 aLength) { DeleteTextTxn *txn; nsresult result = CreateTxnForDeleteText(aElement, aOffset, aLength, &txn); + nsAutoRules beginRulesSniffing(this, kOpDeleteText, nsIEditor::ePrevious); if (NS_SUCCEEDED(result)) { // let listeners know whats up @@ -4087,20 +3959,49 @@ nsresult nsEditor::EndUpdateViewBatch() NS_IMETHODIMP -nsEditor::DeleteSelectionImpl(ESelectionCollapseDirection aAction) +nsEditor::DeleteSelectionImpl(EDirection aAction) { - nsresult result; + nsresult res; EditAggregateTxn *txn; - result = CreateTxnForDeleteSelection(aAction, &txn); - if (NS_SUCCEEDED(result)) { - result = Do(txn); + PRInt32 i; + nsIEditActionListener *listener; + nsCOMPtrselection; + res = CreateTxnForDeleteSelection(aAction, &txn); + if (NS_FAILED(res)) return res; + res = GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + nsAutoRules beginRulesSniffing(this, kOpDeleteSelection, aAction); + + if (NS_SUCCEEDED(res)) + { + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->WillDeleteSelection(selection); + } + } + + res = Do(txn); + + if (mActionListeners) + { + for (i = 0; i < mActionListeners->Count(); i++) + { + listener = (nsIEditActionListener *)mActionListeners->ElementAt(i); + if (listener) + listener->DidDeleteSelection(selection); + } + } } // The transaction system (if any) has taken ownwership of txn NS_IF_RELEASE(txn); - return result; + return res; } @@ -4246,7 +4147,7 @@ NS_IMETHODIMP nsEditor::CreateTxnForDeleteElement(nsIDOMNode * aElement, return result; } -NS_IMETHODIMP nsEditor::CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, EditAggregateTxn **aAggTxn) +/*NS_IMETHODIMP nsEditor::CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, EditAggregateTxn **aAggTxn) { nsresult result = NS_ERROR_NULL_POINTER; if (aAggTxn) @@ -4274,7 +4175,7 @@ NS_IMETHODIMP nsEditor::CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, result = selection->GetIsCollapsed(&collapsed); if (NS_SUCCEEDED(result) && !collapsed) { EditAggregateTxn *delSelTxn; - result = CreateTxnForDeleteSelection(eDoNothing, &delSelTxn); + result = CreateTxnForDeleteSelection(eNone, &delSelTxn); if (NS_SUCCEEDED(result) && delSelTxn) { (*aAggTxn)->AppendChild(delSelTxn); } @@ -4283,7 +4184,7 @@ NS_IMETHODIMP nsEditor::CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, } return result; } - +*/ NS_IMETHODIMP nsEditor::CreateTxnForIMEText(const nsString & aStringToInsert, @@ -4335,7 +4236,7 @@ nsEditor::CreateTxnForRemoveStyleSheet(nsICSSStyleSheet* aSheet, RemoveStyleShee NS_IMETHODIMP -nsEditor::CreateTxnForDeleteSelection(nsIEditor::ESelectionCollapseDirection aAction, +nsEditor::CreateTxnForDeleteSelection(nsIEditor::EDirection aAction, EditAggregateTxn ** aTxn) { if (!aTxn) @@ -4352,8 +4253,8 @@ nsEditor::CreateTxnForDeleteSelection(nsIEditor::ESelectionCollapseDirection aAc { // If it's one of these modes, // we have to extend the selection first: - if (aAction == eDeleteNextWord || aAction == eDeletePreviousWord - || aAction == eDeleteToEndOfLine) + if (aAction == eNextWord || aAction == ePreviousWord + || aAction == eToEndOfLine) { nsCOMPtr selCont (do_QueryInterface(ps)); if (!selCont) @@ -4361,13 +4262,13 @@ nsEditor::CreateTxnForDeleteSelection(nsIEditor::ESelectionCollapseDirection aAc switch (aAction) { - case eDeleteNextWord: + case eNextWord: result = selCont->WordMove(PR_TRUE, PR_TRUE); break; - case eDeletePreviousWord: + case ePreviousWord: result = selCont->WordMove(PR_FALSE, PR_TRUE); break; - case eDeleteToEndOfLine: + case eToEndOfLine: result = selCont->IntraLineMove(PR_TRUE, PR_TRUE); break; default: break; // avoid compiler warnings @@ -4382,7 +4283,7 @@ nsEditor::CreateTxnForDeleteSelection(nsIEditor::ESelectionCollapseDirection aAc // Check whether the selection is collapsed and we should do nothing: PRBool isCollapsed; result = (selection->GetIsCollapsed(&isCollapsed)); - if (NS_SUCCEEDED(result) && isCollapsed && aAction == eDoNothing) + if (NS_SUCCEEDED(result) && isCollapsed && aAction == eNone) return NS_OK; // allocate the out-param transaction @@ -4437,7 +4338,7 @@ nsEditor::CreateTxnForDeleteSelection(nsIEditor::ESelectionCollapseDirection aAc //XXX: currently, this doesn't handle edge conditions because GetNext/GetPrior are not implemented NS_IMETHODIMP nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, - nsIEditor::ESelectionCollapseDirection + nsIEditor::EDirection aAction, EditAggregateTxn *aTxn) { @@ -4483,7 +4384,7 @@ nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, // build a transaction for deleting the appropriate data // XXX: this has to come from rule section - if ((eDeletePrevious==aAction) && (PR_TRUE==isFirst)) + if ((ePrevious==aAction) && (PR_TRUE==isFirst)) { // we're backspacing from the beginning of the node. Delete the first thing to our left nsCOMPtr priorNode; result = GetPriorNode(node, PR_TRUE, getter_AddRefs(priorNode)); @@ -4520,7 +4421,7 @@ nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, } } } - else if ((nsIEditor::eDeleteNext==aAction) && (PR_TRUE==isLast)) + else if ((nsIEditor::eNext==aAction) && (PR_TRUE==isLast)) { // we're deleting from the end of the node. Delete the first thing to our right nsCOMPtr nextNode; result = GetNextNode(node, PR_TRUE, getter_AddRefs(nextNode)); @@ -4561,7 +4462,7 @@ nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, { if (nodeAsText) { // we have text, so delete a char at the proper offset - if (nsIEditor::eDeletePrevious==aAction) { + if (nsIEditor::ePrevious==aAction) { offset --; } DeleteTextTxn *txn; @@ -4573,11 +4474,11 @@ nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, else { // we're either deleting a node or some text, need to dig into the next/prev node to find out nsCOMPtr selectedNode; - if (eDeletePrevious==aAction) + if (ePrevious==aAction) { result = GetPriorNode(node, offset, PR_TRUE, getter_AddRefs(selectedNode)); } - else if (eDeleteNext==aAction) + else if (eNext==aAction) { result = GetNextNode(node, offset, PR_TRUE, getter_AddRefs(selectedNode)); } @@ -4589,7 +4490,7 @@ nsEditor::CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, if (selectedNodeAsText) { // we are deleting from a text node, so do a text deletion PRInt32 begin = 0; // default for forward delete - if (eDeletePrevious==aAction) + if (ePrevious==aAction) { PRUint32 length=0; selectedNodeAsText->GetLength(&length); diff --git a/mozilla/editor/libeditor/base/nsEditor.h b/mozilla/editor/libeditor/base/nsEditor.h index 6ff9c5894a8..f3e8b989045 100644 --- a/mozilla/editor/libeditor/base/nsEditor.h +++ b/mozilla/editor/libeditor/base/nsEditor.h @@ -86,6 +86,25 @@ public: kIterBackward }; + enum OperationID + { + kOpIgnore = -1, + kOpNone = 0, + kOpUndo, + kOpRedo, + kOpSetTextProperty, + kOpRemoveTextProperty, + kOpInsertNode, + kOpCreateNode, + kOpDeleteNode, + kOpSplitNode, + kOpJoinNode, + kOpDeleteText, + kOpInsertText, + kOpInsertIMEText, + kOpDeleteSelection + }; + static const char* kMOZEditorBogusNodeAttr; static const char* kMOZEditorBogusNodeValue; @@ -110,7 +129,6 @@ public: NS_IMETHOD GetDocument(nsIDOMDocument **aDoc); NS_IMETHOD GetPresShell(nsIPresShell **aPS); NS_IMETHOD GetSelection(nsIDOMSelection **aSelection); - NS_IMETHOD GetBodyElement(nsIDOMElement **aElement); NS_IMETHOD EnableUndo(PRBool aEnable); NS_IMETHOD Do(nsITransaction *aTxn); @@ -216,7 +234,7 @@ public: NS_IMETHOD InsertTextImpl(const nsString& aStringToInsert); - NS_IMETHOD DeleteSelectionImpl(ESelectionCollapseDirection aAction); + NS_IMETHOD DeleteSelectionImpl(EDirection aAction); protected: @@ -264,22 +282,11 @@ protected: DeleteElementTxn ** aTxn); - /** Create an aggregate transaction for deleting current selection - * Used by all methods that need to delete current selection, - * then insert something new to replace it - * @param nsString& aTxnName is the name of the aggregated transaction - * @param EditTxn **aAggTxn is the return location of the aggregate TXN, - * with the DeleteSelectionTxn as the first child ONLY - * if there was a selection to delete. - */ - NS_IMETHOD CreateAggregateTxnForDeleteSelection(nsIAtom *aTxnName, EditAggregateTxn **aAggTxn); - - - NS_IMETHOD CreateTxnForDeleteSelection(ESelectionCollapseDirection aAction, + NS_IMETHOD CreateTxnForDeleteSelection(EDirection aAction, EditAggregateTxn ** aTxn); NS_IMETHOD CreateTxnForDeleteInsertionPoint(nsIDOMRange *aRange, - ESelectionCollapseDirection aAction, + EDirection aAction, EditAggregateTxn *aTxn); @@ -302,10 +309,6 @@ protected: */ NS_IMETHOD CreateTxnForRemoveStyleSheet(nsICSSStyleSheet* aSheet, RemoveStyleSheetTxn* *aTxn); - /** insert aStringToInsert as the first text in the document - */ - NS_IMETHOD DoInitialInsert(const nsString & aStringToInsert); - NS_IMETHOD PrepareToInsertText(nsCOMPtr *aOutTextNode, PRInt32 *aOutOffset); NS_IMETHOD DeleteText(nsIDOMCharacterData *aElement, @@ -370,6 +373,18 @@ protected: public: + /** return the body element */ + /** It would make more sense for this to be in nsHTMLEditor, no? */ + NS_IMETHOD GetBodyElement(nsIDOMElement **aElement); + + /** All editor operations which alter the doc should be prefaced + * with a call to StartOperation, naming the action and direction */ + NS_IMETHOD 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_IMETHOD EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection); + /** return the string that represents text nodes in the content tree */ static nsresult GetTextNodeTag(nsString& aOutString); diff --git a/mozilla/editor/libeditor/base/nsEditorUtils.h b/mozilla/editor/libeditor/base/nsEditorUtils.h index b2859f9efa3..e70472714a8 100644 --- a/mozilla/editor/libeditor/base/nsEditorUtils.h +++ b/mozilla/editor/libeditor/base/nsEditorUtils.h @@ -31,6 +31,8 @@ #include "nsIEditor.h" #include "nsIAtom.h" +#include "nsEditor.h" + class nsAutoEditBatch { private: @@ -86,5 +88,24 @@ class nsAutoSelectionReset ~nsAutoSelectionReset(); }; +/*************************************************************************** + * stack based helper class for StartOperation()/EndOperation() sandwich + */ +class nsAutoRules +{ + public: + + 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);} + + protected: + nsEditor *mEd; + PRInt32 mAction; + nsIEditor::EDirection mDirection; +}; + + #endif // nsEditorUtils_h__ diff --git a/mozilla/editor/libeditor/html/TextEditorTest.cpp b/mozilla/editor/libeditor/html/TextEditorTest.cpp index a77a6bda959..5483adce627 100644 --- a/mozilla/editor/libeditor/html/TextEditorTest.cpp +++ b/mozilla/editor/libeditor/html/TextEditorTest.cpp @@ -104,7 +104,7 @@ nsresult TextEditorTest::InitDoc() { nsresult result = mEditor->SelectAll(); TEST_RESULT(result); - result = mEditor->DeleteSelection(nsIEditor::eDeleteNext); + result = mEditor->DeleteSelection(nsIEditor::eNext); TEST_RESULT(result); return result; } diff --git a/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp b/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp index dde9e7e1ba8..e01f1a5a353 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp @@ -52,7 +52,6 @@ static NS_DEFINE_IID(kSubtreeIteratorCID, NS_SUBTREEITERATOR_CID); static NS_DEFINE_IID(kContentIteratorCID, NS_CONTENTITERATOR_CID); static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID); - enum { kLonely = 0, @@ -118,6 +117,70 @@ nsHTMLEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags) return res; } + +NS_IMETHODIMP +nsHTMLEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection) +{ + if (mLockRulesSniffing) return NS_OK; + + nsAutoLockRulesSniffing lockIt((nsTextEditRules*)this); + + if (!mActionNesting) + { + mDocChangeRange = nsnull; // clear out our accounting of what changed + } + mActionNesting++; + return NS_OK; +} + + +NS_IMETHODIMP +nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) +{ + if (mLockRulesSniffing) return NS_OK; + + nsAutoLockRulesSniffing lockIt(this); + + NS_PRECONDITION(mActionNesting>0, "bad action nesting!"); + nsresult res = NS_OK; + if (!--mActionNesting) + { + if (action == nsEditor::kOpIgnore) return NS_OK; + + nsCOMPtrselection; + nsresult res = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + if (mDocChangeRange && !((action == nsEditor::kOpUndo) || (action == nsEditor::kOpRedo))) + { + // expand the "changed doc range" as needed + res = PromoteRange(mDocChangeRange, action); + if (NS_FAILED(res)) return res; + + // add in any needed
s, and remove any unneeded ones. + res = AdjustSpecialBreaks(); + if (NS_FAILED(res)) return res; + // adjust whitespace for insert text and delete actions + if ((action == nsEditor::kOpInsertText) || (action == nsEditor::kOpDeleteSelection)) + { + res = AdjustWhitespace(selection); + if (NS_FAILED(res)) return res; + } + // clean up any empty nodes in the selection + res = RemoveEmptyNodes(); + if (NS_FAILED(res)) return res; + } + + // adjust selection + res = AdjustSelection(selection, aDirection); + if (NS_FAILED(res)) return res; + // detect empty doc + res = CreateBogusNodeIfNeeded(selection); + } + return res; +} + + NS_IMETHODIMP nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, @@ -132,8 +195,6 @@ nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection, *aCancel = PR_FALSE; *aHandled = PR_FALSE; - - mDocChangeRange = nsnull; // clear out our accounting of what changed // my kingdom for dynamic cast nsTextRulesInfo *info = NS_STATIC_CAST(nsTextRulesInfo*, aInfo); @@ -181,53 +242,8 @@ nsHTMLEditRules::DidDoAction(nsIDOMSelection *aSelection, nsTextRulesInfo *info = NS_STATIC_CAST(nsTextRulesInfo*, aInfo); nsresult res; - switch (info->action) - { - case kUndo: - mDocChangeRange = nsnull; // clear out our accounting of what changed - res = nsTextEditRules::DidDoAction(aSelection, aInfo, aResult); - if (NS_FAILED(res)) return res; - return AdjustSelection(aSelection,info->collapsedAction); - case kRedo: - mDocChangeRange = nsnull; // clear out our accounting of what changed - res = nsTextEditRules::DidDoAction(aSelection, aInfo, aResult); - if (NS_FAILED(res)) return res; - return AdjustSelection(aSelection,info->collapsedAction); - } - - // for other than undo and redo: - - if (mDocChangeRange) - { - // expand the "changed doc range" as needed - res = PromoteRange(mDocChangeRange, info->action); - if (NS_FAILED(res)) return res; - - // add in any needed
s, and remove any unneeded ones. - res = AdjustSpecialBreaks(); - if (NS_FAILED(res)) return res; - // adjust whitespace for insert text and delete actions - if ((aInfo->action == kInsertText) || (aInfo->action == kDeleteSelection)) - { - res = AdjustWhitespace(aSelection); - if (NS_FAILED(res)) return res; - } - // clean up any empty nodes in the selection - res = RemoveEmptyNodes(); - if (NS_FAILED(res)) return res; - } - - // do default: - if (aInfo->action != kInsertBreak) // nsTextEditRules::DidInsertBreak() not usable by html rules - { - res = nsTextEditRules::DidDoAction(aSelection, aInfo, aResult); - if (NS_FAILED(res)) return res; - } - // adjust selection - res = AdjustSelection(aSelection,info->collapsedAction); - if (NS_FAILED(res)) return res; - // detect empty doc - res = CreateBogusNodeIfNeeded(aSelection); + // pass thru to nsTextEditRules: + res = nsTextEditRules::DidDoAction(aSelection, aInfo, aResult); return res; } @@ -276,7 +292,7 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction, if (NS_FAILED(res)) return res; if (!bCollapsed) { - res = mEditor->DeleteSelection(nsIEditor::eDoNothing); + res = mEditor->DeleteSelection(nsIEditor::eNone); if (NS_FAILED(res)) return res; } @@ -450,7 +466,7 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, P if (NS_FAILED(res)) return res; if (!bCollapsed) { - res = mEditor->DeleteSelection(nsIEditor::eDoNothing); + res = mEditor->DeleteSelection(nsIEditor::eNone); if (NS_FAILED(res)) return res; } @@ -589,7 +605,7 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, P nsresult nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, - nsIEditor::ESelectionCollapseDirection aAction, + nsIEditor::EDirection aAction, PRBool *aCancel, PRBool *aHandled) { @@ -629,7 +645,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, if (NS_FAILED(res)) return res; // at beginning of text node and backspaced? - if (!offset && (aAction == nsIEditor::eDeletePrevious)) + if (!offset && (aAction == nsIEditor::ePrevious)) { nsCOMPtr priorNode; res = GetPriorHTMLNode(node, &priorNode); @@ -727,7 +743,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, // at end of text node and deleted? if ((offset == (PRInt32)strLength) - && (aAction == nsIEditor::eDeleteNext)) + && (aAction == nsIEditor::eNext)) { nsCOMPtr nextNode; res = GetNextHTMLNode(node, &nextNode); @@ -828,9 +844,9 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, { nsCOMPtr nodeToDelete; - if (aAction == nsIEditor::eDeletePrevious) + if (aAction == nsIEditor::ePrevious) res = GetPriorHTMLNode(node, offset, &nodeToDelete); - else if (aAction == nsIEditor::eDeleteNext) + else if (aAction == nsIEditor::eNext) res = GetNextHTMLNode(node, offset, &nodeToDelete); else return NS_OK; @@ -844,7 +860,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, PRUint32 selPoint = 0; nsCOMPtrnodeAsText; nodeAsText = do_QueryInterface(nodeToDelete); - if (aAction == nsIEditor::eDeletePrevious) + if (aAction == nsIEditor::ePrevious) nodeAsText->GetLength(&selPoint); res = aSelection->Collapse(nodeToDelete,selPoint); return res; @@ -3053,7 +3069,7 @@ nsHTMLEditRules::ReturnInListItem(nsIDOMSelection *aSelection, // now attempt to adjust selection to a text node. // if we fail, then that means we need toinsert a break - res = AdjustSelection(aSelection, nsIEditor::eDeleteNext); + res = AdjustSelection(aSelection, nsIEditor::eNext); if (NS_FAILED(res)) return res; // get the selection location nsCOMPtr selNode; @@ -3410,6 +3426,10 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft, // now we need to insert a br. nsCOMPtr brNode; res = mEditor->CreateBR(*aOutMergeParent, *aOutMergeOffset, &brNode); + if (NS_FAILED(res)) return res; + res = nsEditor::GetNodeLocation(brNode, aOutMergeParent, aOutMergeOffset); + if (NS_FAILED(res)) return res; + (*aOutMergeOffset)++; return res; } else if (IsList(aNodeLeft) || mEditor->IsTextNode(aNodeLeft)) @@ -3587,7 +3607,7 @@ nsHTMLEditRules::AdjustWhitespace(nsIDOMSelection *aSelection) node = do_QueryInterface(content); if (!node) return NS_ERROR_FAILURE; - if (nsEditor::IsTextNode(node)) + if (nsEditor::IsTextNode(node) && mEditor->IsEditable(node)) { isupports = do_QueryInterface(node); arrayOfNodes->AppendElement(isupports); @@ -3614,7 +3634,7 @@ nsHTMLEditRules::AdjustWhitespace(nsIDOMSelection *aSelection) nsresult -nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::ESelectionCollapseDirection aAction) +nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirection aAction) { if (!aSelection) return NS_ERROR_NULL_POINTER; @@ -3689,11 +3709,51 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::ESelect } } - // we aren't in a textnode: look for a nearby text node, in the right direction. - if (aAction == nsIEditor::eDeletePrevious) - res = GetPriorHTMLNode(selNode, selOffset, &nearNode); + // we aren't in a textnode: look for a nearby text node. + // prefer the correct direction. + res = FindNearTextNode(selNode, selOffset, aAction, &nearNode); + if (NS_FAILED(res)) return res; + // did we suceed in getting back a node? + nsIEditor::EDirection dir; + if (!nearNode) + { + // if not try again in other direction + dir = nsIEditor::eNext; + if (aAction == nsIEditor::eNext) dir = nsIEditor::ePrevious; + res = FindNearTextNode(selNode, selOffset, dir, &nearNode); + if (NS_FAILED(res)) return res; + } else - res = GetNextHTMLNode(selNode, selOffset, &nearNode); + { + dir = aAction; + } + // is the nearnode a text node? + textNode = do_QueryInterface(nearNode); + if (!textNode) return NS_ERROR_UNEXPECTED; + PRInt32 offset = 0; + // put selection in right place: + if (dir == nsIEditor::ePrevious) + textNode->GetLength((PRUint32*)&offset); + res = aSelection->Collapse(nearNode,offset); + if (NS_FAILED(res)) return res; + return res; +} + +nsresult +nsHTMLEditRules::FindNearTextNode(nsIDOMNode *aSelNode, + PRInt32 aSelOffset, + nsIEditor::EDirection aDirection, + nsCOMPtr *outTextNode) +{ + if (!aSelNode || !outTextNode) return NS_ERROR_NULL_POINTER; + *outTextNode = nsnull; + nsresult res = NS_OK; + + nsCOMPtr nearNode; + if (aDirection == nsIEditor::ePrevious) + res = GetPriorHTMLNode(aSelNode, aSelOffset, &nearNode); + else + res = GetNextHTMLNode(aSelNode, aSelOffset, &nearNode); if (NS_FAILED(res)) return res; // if there is no node then punt @@ -3701,24 +3761,16 @@ nsHTMLEditRules::AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::ESelect // is nearNode also a descendant of same block? nsCOMPtr block, nearBlock; - if (mEditor->IsBlockNode(selNode)) block = selNode; - else block = mEditor->GetBlockNodeParent(selNode); + if (mEditor->IsBlockNode(aSelNode)) block = aSelNode; + else block = mEditor->GetBlockNodeParent(aSelNode); if (mEditor->IsBlockNode(nearNode)) nearBlock = nearNode; else nearBlock = mEditor->GetBlockNodeParent(nearNode); if (block != nearBlock) return NS_OK; // punt - we dont want to jump across a block // is the nearnode a text node? - textNode = do_QueryInterface(nearNode); - if (textNode) - { - PRInt32 offset = 0; - // put selection in right place: - if (aAction == nsIEditor::eDeletePrevious) - textNode->GetLength((PRUint32*)&offset); - res = aSelection->Collapse(nearNode,offset); - if (NS_FAILED(res)) return res; - } + nsCOMPtr textNode = do_QueryInterface(nearNode); + if (textNode) *outTextNode = nearNode; return res; } @@ -4281,6 +4333,39 @@ nsHTMLEditListener::DidDeleteText(nsIDOMCharacterData *aTextNode, 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, + nsIDOMRange::GetIID(), + 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) diff --git a/mozilla/editor/libeditor/html/nsHTMLEditRules.h b/mozilla/editor/libeditor/html/nsHTMLEditRules.h index 0f7ec0767e3..da8b675f810 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditRules.h +++ b/mozilla/editor/libeditor/html/nsHTMLEditRules.h @@ -41,6 +41,8 @@ public: virtual ~nsHTMLEditRules(); // nsEditRules methods + NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection); + NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection); NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags); NS_IMETHOD WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled); NS_IMETHOD DidDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, nsresult aResult); @@ -64,7 +66,7 @@ protected: TypeInState typeInState, PRInt32 aMaxLength); nsresult WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aHandled); - nsresult WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESelectionCollapseDirection aAction, + nsresult WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::EDirection aAction, PRBool *aCancel, PRBool *aHandled); nsresult WillMakeList(nsIDOMSelection *aSelection, PRBool aOrderd, PRBool *aCancel, PRBool *aHandled); nsresult WillRemoveList(nsIDOMSelection *aSelection, PRBool aOrderd, PRBool *aCancel, PRBool *aHandled); @@ -144,7 +146,11 @@ protected: nsresult AdjustSpecialBreaks(); nsresult AdjustWhitespace(nsIDOMSelection *aSelection); - nsresult AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::ESelectionCollapseDirection aAction); + nsresult AdjustSelection(nsIDOMSelection *aSelection, nsIEditor::EDirection aAction); + nsresult FindNearTextNode(nsIDOMNode *aSelNode, + PRInt32 aSelOffset, + nsIEditor::EDirection aDirection, + nsCOMPtr *outTextNode); nsresult RemoveEmptyNodes(); nsresult DoTextNodeWhitespace(nsIDOMCharacterData *aTextNode, PRInt32 aStart, PRInt32 aEnd); nsresult UpdateDocChangeRange(nsIDOMRange *aRange); @@ -193,6 +199,8 @@ public: 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: diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp index 7e3c3cceaf3..2e4e8a40bf6 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditor.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditor.cpp @@ -826,7 +826,7 @@ NS_IMETHODIMP nsHTMLEditor::InsertBR(nsCOMPtr *outBRNode) if (NS_FAILED(res)) return res; if (!bCollapsed) { - res = DeleteSelection(nsIEditor::eDoNothing); + res = DeleteSelection(nsIEditor::eNone); if (NS_FAILED(res)) return res; } nsCOMPtr selNode; @@ -1109,6 +1109,7 @@ NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsStr if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } nsAutoEditBatch batchIt(this); + nsAutoRules beginRulesSniffing(this, kOpRemoveTextProperty, nsIEditor::eNext); if (gNoisy) { @@ -1277,7 +1278,7 @@ NS_IMETHODIMP nsHTMLEditor::GetTypingStateValue(nsIAtom *aProperty, nsString &aV return NS_OK; } -NS_IMETHODIMP nsHTMLEditor::DeleteSelection(nsIEditor::ESelectionCollapseDirection aAction) +NS_IMETHODIMP nsHTMLEditor::DeleteSelection(nsIEditor::EDirection aAction) { if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } @@ -1286,6 +1287,7 @@ NS_IMETHODIMP nsHTMLEditor::DeleteSelection(nsIEditor::ESelectionCollapseDirecti // delete placeholder txns merge. nsAutoPlaceHolderBatch batch(this, gDeleteTxnName); + nsAutoRules beginRulesSniffing(this, kOpDeleteSelection, aAction); // pre-process nsresult result = GetSelection(getter_AddRefs(selection)); @@ -1315,17 +1317,22 @@ NS_IMETHODIMP nsHTMLEditor::InsertText(const nsString& aStringToInsert) nsCOMPtr selection; PRBool cancel, handled; - + PRInt32 theAction = nsTextEditRules::kInsertText; + PRInt32 opID = kOpInsertText; + if (mInIMEMode) + { + theAction = nsTextEditRules::kInsertTextIME; + opID = kOpInsertIMEText; + } nsAutoPlaceHolderBatch batch(this, nsnull); + nsAutoRules beginRulesSniffing(this, opID, nsIEditor::eNext); // pre-process nsresult result = GetSelection(getter_AddRefs(selection)); if (NS_FAILED(result)) return result; if (!selection) return NS_ERROR_NULL_POINTER; nsAutoString resultString; - nsTextRulesInfo ruleInfo(nsTextEditRules::kInsertText); - // set a different action flag if we are an IME event - if (mInIMEMode) ruleInfo.action = nsTextEditRules::kInsertTextIME; + nsTextRulesInfo ruleInfo(theAction); ruleInfo.inString = &aStringToInsert; ruleInfo.outString = &resultString; ruleInfo.typeInState = *mTypeInState; @@ -1349,7 +1356,8 @@ NS_IMETHODIMP nsHTMLEditor::InsertText(const nsString& aStringToInsert) NS_IMETHODIMP nsHTMLEditor::InsertHTML(const nsString& aInputString) { nsAutoEditBatch beginBatching(this); - + nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext); + nsresult res; nsCOMPtrselection; @@ -1538,6 +1546,7 @@ nsHTMLEditor::InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSe return NS_ERROR_NULL_POINTER; nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext); nsCOMPtrselection; res = GetSelection(getter_AddRefs(selection)); @@ -1869,6 +1878,7 @@ nsHTMLEditor::GetListTags(nsStringArray *aTagList) return GetParentBlockTags(aTagList, PR_TRUE); } +/* // use this when block parents are to be added regardless of current state NS_IMETHODIMP nsHTMLEditor::AddBlockParent(nsString& aParentTag) @@ -1955,7 +1965,6 @@ nsHTMLEditor::ReplaceBlockParent(nsString& aParentTag) return res; } - NS_IMETHODIMP nsHTMLEditor::RemoveParagraphStyle() { @@ -1971,6 +1980,7 @@ nsHTMLEditor::RemoveParagraphStyle() nsAutoSelectionReset selectionResetter(selection); nsAutoEditBatch beginBatching(this); + nsCOMPtr enumerator; res = selection->GetEnumerator(getter_AddRefs(enumerator)); if (NS_FAILED(res)) return res; @@ -2019,6 +2029,7 @@ nsHTMLEditor::RemoveParent(const nsString &aParentTag) } return res; } +*/ NS_IMETHODIMP nsHTMLEditor::MakeOrChangeList(const nsString& aListType) @@ -2030,6 +2041,7 @@ nsHTMLEditor::MakeOrChangeList(const nsString& aListType) PRBool cancel, handled; nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, kOpMakeList, nsIEditor::eNext); // pre-process res = GetSelection(getter_AddRefs(selection)); @@ -2119,6 +2131,7 @@ nsHTMLEditor::RemoveList(const nsString& aListType) PRBool cancel, handled; nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, kOpRemoveList, nsIEditor::eNext); // pre-process res = GetSelection(getter_AddRefs(selection)); @@ -2148,6 +2161,7 @@ nsHTMLEditor::InsertBasicBlock(const nsString& aBlockType) PRBool cancel, handled; nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, kOpMakeBasicBlock, nsIEditor::eNext); // pre-process res = GetSelection(getter_AddRefs(selection)); @@ -2221,7 +2235,6 @@ nsHTMLEditor::InsertBasicBlock(const nsString& aBlockType) return res; } -// TODO: Implement "outdent" NS_IMETHODIMP nsHTMLEditor::Indent(const nsString& aIndent) { @@ -2229,8 +2242,15 @@ nsHTMLEditor::Indent(const nsString& aIndent) if (!mRules) { return NS_ERROR_NOT_INITIALIZED; } PRBool cancel, handled; - + PRInt32 theAction = nsHTMLEditRules::kIndent; + PRInt32 opID = kOpIndent; + if (aIndent == "outdent") + { + theAction = nsHTMLEditRules::kOutdent; + opID = kOpOutdent; + } nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, opID, nsIEditor::eNext); // pre-process nsCOMPtr selection; @@ -2238,9 +2258,7 @@ nsHTMLEditor::Indent(const nsString& aIndent) if (NS_FAILED(res)) return res; if (!selection) return NS_ERROR_NULL_POINTER; - nsTextRulesInfo ruleInfo(nsHTMLEditRules::kIndent); - if (aIndent == "outdent") - ruleInfo.action = nsHTMLEditRules::kOutdent; + nsTextRulesInfo ruleInfo(theAction); res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled); if (cancel || (NS_FAILED(res))) return res; @@ -2310,6 +2328,8 @@ NS_IMETHODIMP nsHTMLEditor::Align(const nsString& aAlignType) { nsAutoEditBatch beginBatching(this); + nsAutoRules beginRulesSniffing(this, kOpAlign, nsIEditor::eNext); + nsCOMPtr node; PRBool cancel, handled; @@ -3435,7 +3455,7 @@ NS_IMETHODIMP nsHTMLEditor::Cut() res = Copy(); if (NS_SUCCEEDED(res)) - res = DeleteSelection(eDoNothing); + res = DeleteSelection(eNone); return res; } @@ -4118,6 +4138,34 @@ void nsHTMLEditor::ApplyStyleSheetToPresShellDocument(nsICSSStyleSheet* aSheet, } +#ifdef XP_MAC +#pragma mark - +#pragma mark --- nsEditor overrides --- +#pragma mark - +#endif + + +/** All editor operations which alter the doc should be prefaced + * with a call to StartOperation, naming the action and direction */ +NS_IMETHODIMP +nsHTMLEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection) +{ + if (mRules) return mRules->BeforeEdit(opID, aDirection); + return NS_OK; +} + + +/** 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) +{ + if (mRules) return mRules->AfterEdit(opID, aDirection); + return NS_OK; +} + + + #ifdef XP_MAC #pragma mark - #pragma mark --- Random methods --- @@ -4862,6 +4910,7 @@ nsHTMLEditor::ReParentBlockContent(nsIDOMNode *aNode, return res; } +/* NS_IMETHODIMP nsHTMLEditor::ReParentContentOfRange(nsIDOMRange *aRange, nsString &aParentTag, @@ -4901,7 +4950,7 @@ nsHTMLEditor::ReParentContentOfRange(nsIDOMRange *aRange, return res; } - +*/ NS_IMETHODIMP nsHTMLEditor::RemoveParagraphStyleFromRange(nsIDOMRange *aRange) @@ -5339,7 +5388,7 @@ nsHTMLEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr &parent result = selection->GetIsCollapsed(&collapsed); if (NS_SUCCEEDED(result) && !collapsed) { - result = DeleteSelection(nsIEditor::eDoNothing); + result = DeleteSelection(nsIEditor::eNone); if (NS_FAILED(result)) { return result; } diff --git a/mozilla/editor/libeditor/html/nsHTMLEditor.h b/mozilla/editor/libeditor/html/nsHTMLEditor.h index 8840fce5f17..49e5fb039cb 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditor.h +++ b/mozilla/editor/libeditor/html/nsHTMLEditor.h @@ -57,6 +57,20 @@ class nsHTMLEditor : public nsEditor, typedef enum {eNoOp, eReplaceParent=1, eInsertParent=2} BlockTransformationType; public: + + enum OperationID + { + kOpInsertBreak = 3000, + kOpMakeList = 3001, + kOpIndent = 3002, + kOpOutdent = 3003, + kOpAlign = 3004, + kOpMakeBasicBlock = 3005, + kOpRemoveList = 3006, + kOpInsertElement = 3008 + }; + + // see nsIHTMLEditor for documentation //Interfaces for addref and release and queryinterface @@ -97,7 +111,7 @@ public: NS_IMETHOD InsertHTML(const nsString &aInputString); NS_IMETHOD InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSelection); - NS_IMETHOD DeleteSelection(ESelectionCollapseDirection aAction); + NS_IMETHOD DeleteSelection(EDirection aAction); NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag, nsIDOMNode ** aNewNode); NS_IMETHOD SelectElement(nsIDOMElement* aElement); NS_IMETHOD SetCaretAfterElement(nsIDOMElement* aElement); @@ -107,11 +121,11 @@ public: NS_IMETHOD GetParentBlockTags(nsStringArray *aTagList, PRBool aGetLists); NS_IMETHOD GetParagraphTags(nsStringArray *aTagList); NS_IMETHOD GetListTags(nsStringArray *aTagList); - NS_IMETHOD RemoveParagraphStyle(); +// NS_IMETHOD RemoveParagraphStyle(); - NS_IMETHOD AddBlockParent(nsString& aParentTag); - NS_IMETHOD ReplaceBlockParent(nsString& aParentTag); - NS_IMETHOD RemoveParent(const nsString &aParentTag); +// NS_IMETHOD AddBlockParent(nsString& aParentTag); +// NS_IMETHOD ReplaceBlockParent(nsString& aParentTag); +// NS_IMETHOD RemoveParent(const nsString &aParentTag); NS_IMETHOD MakeOrChangeList(const nsString& aListType); NS_IMETHOD RemoveList(const nsString& aListType); @@ -225,6 +239,14 @@ public: /* ------------ nsICSSLoaderObserver -------------- */ NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aNotify); + /** All editor operations which alter the doc should be prefaced + * with a call to StartOperation, naming the action and direction */ + NS_IMETHOD 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_IMETHOD EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection); + /* ------------ Utility Routines, not part of public API -------------- */ NS_IMETHOD GetBodyStyleContext(nsIStyleContext** aStyleContext); @@ -307,10 +329,10 @@ protected: BlockTransformationType aTranformation, nsIDOMNode **aNewParentNode); - NS_IMETHOD ReParentContentOfRange(nsIDOMRange *aRange, +/* NS_IMETHOD ReParentContentOfRange(nsIDOMRange *aRange, nsString &aParentTag, BlockTransformationType aTranformation); - +*/ NS_IMETHOD RemoveParagraphStyleFromRange(nsIDOMRange *aRange); NS_IMETHOD RemoveParagraphStyleFromBlockContent(nsIDOMRange *aRange); diff --git a/mozilla/editor/libeditor/html/nsHTMLEditorLog.cpp b/mozilla/editor/libeditor/html/nsHTMLEditorLog.cpp index fd98ad8eaee..77b42245c8d 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditorLog.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditorLog.cpp @@ -142,7 +142,7 @@ nsHTMLEditorLog::RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttri } NS_IMETHODIMP -nsHTMLEditorLog::DeleteSelection(nsIEditor::ESelectionCollapseDirection aAction) +nsHTMLEditorLog::DeleteSelection(nsIEditor::EDirection aAction) { nsAutoHTMLEditorLogLock logLock(this); diff --git a/mozilla/editor/libeditor/html/nsHTMLEditorLog.h b/mozilla/editor/libeditor/html/nsHTMLEditorLog.h index 100836149af..4c5ca3ab5de 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditorLog.h +++ b/mozilla/editor/libeditor/html/nsHTMLEditorLog.h @@ -57,7 +57,7 @@ public: const nsString *aValue); NS_IMETHOD SetParagraphFormat(const nsString& aParagraphFormat); NS_IMETHOD RemoveInlineProperty(nsIAtom *aProperty, const nsString *aAttribute); - NS_IMETHOD DeleteSelection(nsIEditor::ESelectionCollapseDirection aAction); + NS_IMETHOD DeleteSelection(nsIEditor::EDirection aAction); NS_IMETHOD TypedText(const nsString& aString, PRInt32 aAction); NS_IMETHOD InsertText(const nsString& aStringToInsert); NS_IMETHOD InsertBreak(); diff --git a/mozilla/editor/libeditor/text/nsEditorEventListeners.cpp b/mozilla/editor/libeditor/text/nsEditorEventListeners.cpp index 407eeaa8723..da89018bba1 100644 --- a/mozilla/editor/libeditor/text/nsEditorEventListeners.cpp +++ b/mozilla/editor/libeditor/text/nsEditorEventListeners.cpp @@ -171,13 +171,13 @@ nsTextEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent) return NS_ERROR_BASE; // consumed if (nsIDOMKeyEvent::DOM_VK_BACK_SPACE==keyCode) { - mEditor->DeleteSelection(nsIEditor::eDeletePrevious); + mEditor->DeleteSelection(nsIEditor::ePrevious); ScrollSelectionIntoView(); return NS_ERROR_BASE; // consumed } if (nsIDOMKeyEvent::DOM_VK_DELETE==keyCode) { - mEditor->DeleteSelection(nsIEditor::eDeleteNext); + mEditor->DeleteSelection(nsIEditor::eNext); ScrollSelectionIntoView(); return NS_ERROR_BASE; // consumed } diff --git a/mozilla/editor/libeditor/text/nsTextEditRules.cpp b/mozilla/editor/libeditor/text/nsTextEditRules.cpp index 16eafd3e849..41b51c307bf 100644 --- a/mozilla/editor/libeditor/text/nsTextEditRules.cpp +++ b/mozilla/editor/libeditor/text/nsTextEditRules.cpp @@ -36,9 +36,11 @@ #include "nsIEnumerator.h" #include "nsLayoutCID.h" #include "nsIEditProperty.h" +#include "nsEditorUtils.h" static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); + #define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \ if ((mFlags & nsIHTMLEditor::eEditorReadonlyMask) || (mFlags & nsIHTMLEditor::eEditorDisabledMask)) \ { \ @@ -53,6 +55,8 @@ static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); nsTextEditRules::nsTextEditRules() : mEditor(nsnull) , mFlags(0) // initialized to 0 ("no flags set"). Real initial value is given in Init() +, mActionNesting(0) +, mLockRulesSniffing(PR_FALSE) { } @@ -109,20 +113,54 @@ nsTextEditRules::SetFlags(PRUint32 aFlags) { // we are converting TO a plaintext editor // put a "white-space: pre" style on the body - nsCOMPtr bodyElement; - nsresult res = mEditor->GetBodyElement(getter_AddRefs(bodyElement)); - if (NS_FAILED(res)) return res; - if (!bodyElement) return NS_ERROR_NULL_POINTER; + nsCOMPtr bodyElement; + nsresult res = mEditor->GetBodyElement(getter_AddRefs(bodyElement)); + if (NS_FAILED(res)) return res; + if (!bodyElement) return NS_ERROR_NULL_POINTER; // not going through the editor to do this. // XXX This is not the right way to do this; we need an editor style // system so that we can add & replace style attrs. - bodyElement->SetAttribute("style", PlaintextInitalStyle); + bodyElement->SetAttribute("style", PlaintextInitalStyle); } } mFlags = aFlags; return NS_OK; } +NS_IMETHODIMP +nsTextEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection) +{ + if (mLockRulesSniffing) return NS_OK; + + nsAutoLockRulesSniffing lockIt(this); + + mActionNesting++; + return NS_OK; +} + + +NS_IMETHODIMP +nsTextEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) +{ + if (mLockRulesSniffing) return NS_OK; + + nsAutoLockRulesSniffing lockIt(this); + + NS_PRECONDITION(mActionNesting>0, "bad action nesting!"); + nsresult res = NS_OK; + if (!--mActionNesting) + { + nsCOMPtrselection; + res = mEditor->GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + // detect empty doc + res = CreateBogusNodeIfNeeded(selection); + } + return res; +} + + NS_IMETHODIMP nsTextEditRules::WillDoAction(nsIDOMSelection *aSelection, nsRulesInfo *aInfo, @@ -336,7 +374,7 @@ nsTextEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, P if (NS_FAILED(res)) return res; if (!bCollapsed) { - res = mEditor->DeleteSelection(nsIEditor::eDoNothing); + res = mEditor->DeleteSelection(nsIEditor::eNone); if (NS_FAILED(res)) return res; } @@ -378,6 +416,11 @@ nsTextEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel, P nsresult nsTextEditRules::DidInsertBreak(nsIDOMSelection *aSelection, nsresult aResult) { + // we only need to execute the stuff below if we are a plaintext editor. + // html editors have a different mechanism for putting in mozBR's + // (because there are a bunch more placesyou have to worry about it in html) + if (!nsIHTMLEditor::eEditorPlaintextMask & mFlags) return NS_OK; + // if we are at the end of the document, we need to insert // a special mozBR following the normal br, and then set the // selection to after the mozBR. @@ -412,16 +455,16 @@ nsTextEditRules::DidInsertBreak(nsIDOMSelection *aSelection, nsresult aResult) // with a mozBR already exiting after it. In this case we have to // move the selection to after the mozBR so it will show up on the // empty line. - nsCOMPtr nextNode; - res = GetNextHTMLNode(nearNode, &nextNode); - if (NS_FAILED(res)) return res; - if (IsMozBR(nextNode)) - { - res = nsEditor::GetNodeLocation(nextNode, &selNode, &selOffset); - if (NS_FAILED(res)) return res; - res = aSelection->Collapse(selNode,selOffset+1); - if (NS_FAILED(res)) return res; - } + nsCOMPtr nextNode; + res = GetNextHTMLNode(nearNode, &nextNode); + if (NS_FAILED(res)) return res; + if (IsMozBR(nextNode)) + { + res = nsEditor::GetNodeLocation(nextNode, &selNode, &selOffset); + if (NS_FAILED(res)) return res; + res = aSelection->Collapse(selNode,selOffset+1); + if (NS_FAILED(res)) return res; + } } } return res; @@ -465,7 +508,7 @@ nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection, if (NS_FAILED(res)) return res; if (!bCollapsed) { - res = mEditor->DeleteSelection(nsIEditor::eDoNothing); + res = mEditor->DeleteSelection(nsIEditor::eNone); if (NS_FAILED(res)) return res; } @@ -533,9 +576,9 @@ nsTextEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, TypeInSta // splitting anchor twice sets newTextNode as an empty text node between // two halves of the original text node res = mEditor->SplitNode(anchorAsText, offset, getter_AddRefs(newTextNode)); - if (NS_SUCCEEDED(res)) { - res = mEditor->SplitNode(anchorAsText, 0, getter_AddRefs(newTextNode)); - } + if (NS_SUCCEEDED(res)) { + res = mEditor->SplitNode(anchorAsText, 0, getter_AddRefs(newTextNode)); + } } } // now we have the new text node we are going to insert into. @@ -552,8 +595,8 @@ nsTextEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, TypeInSta { nsCOMPtrparent; res = newTextNode->GetParentNode(getter_AddRefs(parent)); - if (NS_FAILED(res)) return res; - if (!parent) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(res)) return res; + if (!parent) return NS_ERROR_NULL_POINTER; res = mEditor->RemoveTextPropertiesForNode (newTextNode, parent, 0, 0, nsIEditProperty::b, nsnull); } } @@ -566,8 +609,8 @@ nsTextEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, TypeInSta { nsCOMPtrparent; res = newTextNode->GetParentNode(getter_AddRefs(parent)); - if (NS_FAILED(res)) return res; - if (!parent) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(res)) return res; + if (!parent) return NS_ERROR_NULL_POINTER; res = mEditor->RemoveTextPropertiesForNode (newTextNode, parent, 0, 0, nsIEditProperty::i, nsnull); } } @@ -580,8 +623,8 @@ nsTextEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, TypeInSta { nsCOMPtrparent; res = newTextNode->GetParentNode(getter_AddRefs(parent)); - if (NS_FAILED(res)) return res; - if (!parent) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(res)) return res; + if (!parent) return NS_ERROR_NULL_POINTER; res = mEditor->RemoveTextPropertiesForNode (newTextNode, parent, 0, 0, nsIEditProperty::u, nsnull); } } @@ -633,7 +676,7 @@ nsTextEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, TypeInSta if (!bodyElement) return NS_ERROR_NULL_POINTER; parent = do_QueryInterface(bodyElement); // offset already set to 0 - } + } if (!parent) { return NS_ERROR_NULL_POINTER; } nsAutoString attr, value; @@ -719,8 +762,8 @@ nsTextEditRules::CreateFontStyleForInsertText(nsIDOMNode *aNewTextNode, { nsCOMPtrparent; res = aNewTextNode->GetParentNode(getter_AddRefs(parent)); - if (NS_FAILED(res)) return res; - if (!parent) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(res)) return res; + if (!parent) return NS_ERROR_NULL_POINTER; res = mEditor->RemoveTextPropertiesForNode (aNewTextNode, parent, 0, 0, nsIEditProperty::font, &aAttr); } return res; @@ -738,8 +781,8 @@ nsTextEditRules::InsertStyleNode(nsIDOMNode *aNode, nsresult res; nsCOMPtrparent; res = aNode->GetParentNode(getter_AddRefs(parent)); - if (NS_FAILED(res)) return res; - if (!parent) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(res)) return res; + if (!parent) return NS_ERROR_NULL_POINTER; nsAutoString tag; aTag->ToString(tag); @@ -754,8 +797,8 @@ nsTextEditRules::InsertStyleNode(nsIDOMNode *aNode, if (NS_FAILED(res)) return res; res = mEditor->CreateNode(tag, parent, offsetInParent, aNewNode); - if (NS_FAILED(res)) return res; - if (!aNewNode) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(res)) return res; + if (!aNewNode) return NS_ERROR_NULL_POINTER; res = mEditor->DeleteNode(aNode); if (NS_SUCCEEDED(res)) @@ -849,8 +892,8 @@ nsTextEditRules::InsertStyleAndNewTextNode(nsIDOMNode *aParentNode, if (NS_FAILED(res)) { return res; } res = mEditor->CreateNode(textNodeTag, newStyleNode, 0, getter_AddRefs(newTextNode)); - if (NS_FAILED(res)) return res; - if (!newTextNode) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(res)) return res; + if (!newTextNode) return NS_ERROR_NULL_POINTER; // if we have a selection collapse the selection to the beginning of the new text node if (aInOutSelection) { @@ -901,7 +944,7 @@ nsTextEditRules::DidRemoveTextProperty(nsIDOMSelection *aSelection, nsresult aRe nsresult nsTextEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, - nsIEditor::ESelectionCollapseDirection aCollapsedAction, + nsIEditor::EDirection aCollapsedAction, PRBool *aCancel, PRBool *aHandled) { @@ -924,10 +967,10 @@ nsTextEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, mEditor->GetTextSelectionOffsets(aSelection, start, end); if (end==start) { // collapsed selection - if (nsIEditor::eDeletePrevious==aCollapsedAction && 0anchor; PRInt32 offset; - res = aSelection->GetAnchorNode(getter_AddRefs(anchor)); + res = aSelection->GetAnchorNode(getter_AddRefs(anchor)); if (NS_FAILED(res)) return res; if (!anchor) return NS_ERROR_NULL_POINTER; res = aSelection->GetAnchorOffset(&offset); @@ -1005,8 +1047,8 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection, siblingNodeAsText->GetLength(&siblingLength); nsCOMPtr parentNode; res = selectedNode->GetParentNode(getter_AddRefs(parentNode)); - if (NS_FAILED(res)) return res; - if (!parentNode) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(res)) return res; + if (!parentNode) return NS_ERROR_NULL_POINTER; res = mEditor->JoinNodes(siblingNode, selectedNode, parentNode); // selectedNode will remain after the join, siblingNode is removed } @@ -1022,11 +1064,11 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection, selectedNodeAsText->GetLength(&selectedNodeLength); nsCOMPtr parentNode; res = selectedNode->GetParentNode(getter_AddRefs(parentNode)); - if (NS_FAILED(res)) return res; - if (!parentNode) return NS_ERROR_NULL_POINTER; + if (NS_FAILED(res)) return res; + if (!parentNode) return NS_ERROR_NULL_POINTER; res = mEditor->JoinNodes(selectedNode, siblingNode, parentNode); - if (NS_FAILED(res)) return res; + if (NS_FAILED(res)) return res; // selectedNode will remain after the join, siblingNode is removed // set selection res = aSelection->Collapse(siblingNode, selectedNodeLength); @@ -1206,12 +1248,16 @@ nsTextEditRules::CreateBogusNodeIfNeeded(nsIDOMSelection *aSelection) if (!aSelection) { return NS_ERROR_NULL_POINTER; } if (!mEditor) { return NS_ERROR_NULL_POINTER; } if (mBogusNode) return NS_OK; // let's not create more than one, ok? + + // tell rules system to not do any post-processing + nsAutoRules beginRulesSniffing(mEditor, nsEditor::kOpIgnore, nsIEditor::eNone); - nsCOMPtr bodyElement; - nsresult res = mEditor->GetBodyElement(getter_AddRefs(bodyElement)); - if (NS_FAILED(res)) return res; - if (!bodyElement) return NS_ERROR_NULL_POINTER; - nsCOMPtrbodyNode = do_QueryInterface(bodyElement); + nsCOMPtr bodyElement; + + nsresult res = mEditor->GetBodyElement(getter_AddRefs(bodyElement)); + if (NS_FAILED(res)) return res; + if (!bodyElement) return NS_ERROR_NULL_POINTER; + nsCOMPtrbodyNode = do_QueryInterface(bodyElement); // now we've got the body tag. // iterate the body tag, looking for editable content @@ -1270,14 +1316,14 @@ nsTextEditRules::TruncateInsertionIfNeeded(nsIDOMSelection *aSelection, { // Get the current text length. // Get the length of inString. - // Get the length of the selection. - // If selection is collapsed, it is length 0. - // Subtract the length of the selection from the len(doc) - // since we'll delete the selection on insert. - // This is resultingDocLength. + // Get the length of the selection. + // If selection is collapsed, it is length 0. + // Subtract the length of the selection from the len(doc) + // since we'll delete the selection on insert. + // This is resultingDocLength. // If (resultingDocLength) is at or over max, cancel the insert // If (resultingDocLength) + (length of input) > max, - // set aOutString to subset of inString so length = max + // set aOutString to subset of inString so length = max PRInt32 docLength; res = mEditor->GetDocumentLength(&docLength); if (NS_FAILED(res)) { return res; } diff --git a/mozilla/editor/libeditor/text/nsTextEditRules.h b/mozilla/editor/libeditor/text/nsTextEditRules.h index 8b76f37afce..db89294a7e2 100644 --- a/mozilla/editor/libeditor/text/nsTextEditRules.h +++ b/mozilla/editor/libeditor/text/nsTextEditRules.h @@ -51,6 +51,8 @@ public: // nsEditRules 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); NS_IMETHOD GetFlags(PRUint32 *aFlags); @@ -103,11 +105,11 @@ protected: nsresult DidInsert(nsIDOMSelection *aSelection, nsresult aResult); nsresult WillDeleteSelection(nsIDOMSelection *aSelection, - nsIEditor::ESelectionCollapseDirection aCollapsedAction, + nsIEditor::EDirection aCollapsedAction, PRBool *aCancel, PRBool *aHandled); nsresult DidDeleteSelection(nsIDOMSelection *aSelection, - nsIEditor::ESelectionCollapseDirection aCollapsedAction, + nsIEditor::EDirection aCollapsedAction, nsresult aResult); nsresult WillSetTextProperty(nsIDOMSelection *aSelection, PRBool *aCancel, PRBool *aHandled); @@ -222,11 +224,17 @@ protected: nsresult CreateMozBR(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr *outBRNode); - // data + // data members nsHTMLEditor *mEditor; // note that we do not refcount the editor nsString mPasswordText; // a buffer we use to store the real value of password editors nsCOMPtr mBogusNode; // magic node acts as placeholder in empty doc PRUint32 mFlags; + PRUint32 mActionNesting; + PRBool mLockRulesSniffing; + + // friends + friend class nsAutoLockRulesSniffing; + }; @@ -242,7 +250,7 @@ class nsTextRulesInfo : public nsRulesInfo outputFormat(0), typeInState(), maxLength(-1), - collapsedAction(nsIEditor::eDeleteNext), + collapsedAction(nsIEditor::eNext), bOrdered(PR_FALSE), alignType(0), blockType(0), @@ -259,7 +267,7 @@ class nsTextRulesInfo : public nsRulesInfo PRInt32 maxLength; // kDeleteSelection - nsIEditor::ESelectionCollapseDirection collapsedAction; + nsIEditor::EDirection collapsedAction; // kMakeList PRBool bOrdered; @@ -274,5 +282,26 @@ class nsTextRulesInfo : public nsRulesInfo const nsIDOMElement* insertElement; }; + +/*************************************************************************** + * stack based helper class for StartOperation()/EndOperation() sandwich. + * this class sets a bool letting us know to ignore any rules sniffing + * that tries to occur reentrantly. + */ +class nsAutoLockRulesSniffing +{ + public: + + nsAutoLockRulesSniffing(nsTextEditRules *rules) : mRules(rules) + {if (mRules) mRules->mLockRulesSniffing = PR_TRUE;} + ~nsAutoLockRulesSniffing() + {if (mRules) mRules->mLockRulesSniffing = PR_FALSE;} + + protected: + nsTextEditRules *mRules; +}; + + + #endif //nsTextEditRules_h__ diff --git a/mozilla/editor/libeditor/txtsvc/nsTSDNotifier.cpp b/mozilla/editor/libeditor/txtsvc/nsTSDNotifier.cpp index 4fb99b62581..1af8bff87f6 100644 --- a/mozilla/editor/libeditor/txtsvc/nsTSDNotifier.cpp +++ b/mozilla/editor/libeditor/txtsvc/nsTSDNotifier.cpp @@ -208,3 +208,17 @@ nsTSDNotifier::DidDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PR return NS_OK; } +NS_IMETHODIMP +nsTSDNotifier::WillDeleteSelection(nsIDOMSelection *aSelection) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsTSDNotifier::DidDeleteSelection(nsIDOMSelection *aSelection) +{ + return NS_OK; +} + + + diff --git a/mozilla/editor/libeditor/txtsvc/nsTSDNotifier.h b/mozilla/editor/libeditor/txtsvc/nsTSDNotifier.h index 55ab3667ae3..86efb335374 100644 --- a/mozilla/editor/libeditor/txtsvc/nsTSDNotifier.h +++ b/mozilla/editor/libeditor/txtsvc/nsTSDNotifier.h @@ -24,9 +24,9 @@ #define nsTSDNotifier_h__ #include "nsCOMPtr.h" +#include "nsIEditActionListener.h" class nsTextServicesDocument; -class nsIEditActioinListener; class nsTSDNotifier : public nsIEditActionListener { @@ -80,6 +80,8 @@ public: 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); }; #endif // nsTSDNotifier_h__ diff --git a/mozilla/editor/libeditor/txtsvc/nsTextServicesDocument.cpp b/mozilla/editor/libeditor/txtsvc/nsTextServicesDocument.cpp index 415b4600f41..7e2b7268dd8 100644 --- a/mozilla/editor/libeditor/txtsvc/nsTextServicesDocument.cpp +++ b/mozilla/editor/libeditor/txtsvc/nsTextServicesDocument.cpp @@ -1871,7 +1871,7 @@ nsTextServicesDocument::DeleteSelection() // Now delete the actual content! - result = mEditor->DeleteSelection(nsIEditor::eDeletePrevious); + result = mEditor->DeleteSelection(nsIEditor::ePrevious); if (NS_FAILED(result)) { diff --git a/mozilla/editor/public/nsIEditActionListener.h b/mozilla/editor/public/nsIEditActionListener.h index a852fc6d256..8adb5a6a41d 100644 --- a/mozilla/editor/public/nsIEditActionListener.h +++ b/mozilla/editor/public/nsIEditActionListener.h @@ -28,6 +28,7 @@ class nsIDOMNode; class nsString; class nsIDOMCharacterData; +class nsIDOMSelection; /* Editor Action Listener interface to outside world @@ -209,6 +210,17 @@ public: PRInt32 aLength, nsresult aResult)=0; + /** + * Called before the editor deletes the selection. + * @param aSelection The selection to be deleted + */ + NS_IMETHOD WillDeleteSelection(nsIDOMSelection *aSelection)=0; + + /** + * Called after the editor deletes the selection. + * @param aSelection The selection, after deletion + */ + NS_IMETHOD DidDeleteSelection(nsIDOMSelection *aSelection)=0; }; #endif //nsIEditActionListener_h__ diff --git a/mozilla/editor/public/nsIEditor.h b/mozilla/editor/public/nsIEditor.h index dc247d6372c..889c23d9ff3 100644 --- a/mozilla/editor/public/nsIEditor.h +++ b/mozilla/editor/public/nsIEditor.h @@ -52,13 +52,13 @@ public: /* An enum used to describe how to collpase a non-collapsed selection */ typedef enum { - eDoNothing, - eDeleteNext, - eDeletePrevious, - eDeleteNextWord, - eDeletePreviousWord, - eDeleteToEndOfLine - } ESelectionCollapseDirection; + eNone, + eNext, + ePrevious, + eNextWord, + ePreviousWord, + eToEndOfLine + } EDirection; /** @@ -112,7 +112,7 @@ public: * @param aDir if eLTR, delete to the right (for example, the DEL key) * if eRTL, delete to the left (for example, the BACKSPACE key) */ - NS_IMETHOD DeleteSelection(ESelectionCollapseDirection aAction)=0; + NS_IMETHOD DeleteSelection(EDirection aAction)=0; /* ------------ Document info and file methods -------------- */ diff --git a/mozilla/editor/public/nsIHTMLEditor.h b/mozilla/editor/public/nsIHTMLEditor.h index 03ba55a490e..d9b521908e2 100644 --- a/mozilla/editor/public/nsIHTMLEditor.h +++ b/mozilla/editor/public/nsIHTMLEditor.h @@ -267,35 +267,6 @@ public: */ NS_IMETHOD GetListTags(nsStringArray *aTagList)=0; - /** Add a block parent node around the selected content. - * Only legal nestings are allowed. - * An example of use is for indenting using blockquote nodes. - * - * @param aParentTag The tag from which the new parent is created. - */ - NS_IMETHOD AddBlockParent(nsString& aParentTag)=0; - - /** Replace the block parent node around the selected content with a new block - * parent node of type aParentTag. - * Only legal replacements are allowed. - * An example of use are is transforming H1 to LI ("paragraph type transformations"). - * For containing block transformations (transforming UL to OL, for example), - * the caller should RemoveParent("UL"), set the selection appropriately, - * and call AddBlockParent("OL"). - * - * @param aParentTag The tag from which the new parent is created. - */ - NS_IMETHOD ReplaceBlockParent(nsString& aParentTag)=0; - - /** remove the paragraph style from the selection */ - NS_IMETHOD RemoveParagraphStyle()=0; - - /** remove block parent of type aTagToRemove from the selection. - * if aTagToRemove is null, the nearest enclosing block that - * is not a root is removed. - */ - NS_IMETHOD RemoveParent(const nsString &aParentTag)=0; - /** * Document me! * @@ -391,6 +362,42 @@ public: * */ NS_IMETHOD SetBodyAttribute(const nsString& aAttr, const nsString& aValue)=0; + + // ------------------------------------------------ + // THE FOLLOWING ARE IF'D OUT SINCE THEY ARE UNUSED +#if 0 + /** Add a block parent node around the selected content. + * Only legal nestings are allowed. + * An example of use is for indenting using blockquote nodes. + * + * @param aParentTag The tag from which the new parent is created. + */ + NS_IMETHOD AddBlockParent(nsString& aParentTag)=0; + + /** Replace the block parent node around the selected content with a new block + * parent node of type aParentTag. + * Only legal replacements are allowed. + * An example of use are is transforming H1 to LI ("paragraph type transformations"). + * For containing block transformations (transforming UL to OL, for example), + * the caller should RemoveParent("UL"), set the selection appropriately, + * and call AddBlockParent("OL"). + * + * @param aParentTag The tag from which the new parent is created. + */ + NS_IMETHOD ReplaceBlockParent(nsString& aParentTag)=0; + + /** remove the paragraph style from the selection */ + NS_IMETHOD RemoveParagraphStyle()=0; + + /** remove block parent of type aTagToRemove from the selection. + * if aTagToRemove is null, the nearest enclosing block that + * is not a root is removed. + */ + NS_IMETHOD RemoveParent(const nsString &aParentTag)=0; +#endif + // END OF UNUSED API POINTS + // ------------------------------------------------ + }; #endif // nsIHTMLEditor_h__ diff --git a/mozilla/editor/txtsvc/src/nsTSDNotifier.cpp b/mozilla/editor/txtsvc/src/nsTSDNotifier.cpp index 4fb99b62581..1af8bff87f6 100644 --- a/mozilla/editor/txtsvc/src/nsTSDNotifier.cpp +++ b/mozilla/editor/txtsvc/src/nsTSDNotifier.cpp @@ -208,3 +208,17 @@ nsTSDNotifier::DidDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PR return NS_OK; } +NS_IMETHODIMP +nsTSDNotifier::WillDeleteSelection(nsIDOMSelection *aSelection) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsTSDNotifier::DidDeleteSelection(nsIDOMSelection *aSelection) +{ + return NS_OK; +} + + + diff --git a/mozilla/editor/txtsvc/src/nsTSDNotifier.h b/mozilla/editor/txtsvc/src/nsTSDNotifier.h index 55ab3667ae3..86efb335374 100644 --- a/mozilla/editor/txtsvc/src/nsTSDNotifier.h +++ b/mozilla/editor/txtsvc/src/nsTSDNotifier.h @@ -24,9 +24,9 @@ #define nsTSDNotifier_h__ #include "nsCOMPtr.h" +#include "nsIEditActionListener.h" class nsTextServicesDocument; -class nsIEditActioinListener; class nsTSDNotifier : public nsIEditActionListener { @@ -80,6 +80,8 @@ public: 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); }; #endif // nsTSDNotifier_h__ diff --git a/mozilla/editor/txtsvc/src/nsTextServicesDocument.cpp b/mozilla/editor/txtsvc/src/nsTextServicesDocument.cpp index 415b4600f41..7e2b7268dd8 100644 --- a/mozilla/editor/txtsvc/src/nsTextServicesDocument.cpp +++ b/mozilla/editor/txtsvc/src/nsTextServicesDocument.cpp @@ -1871,7 +1871,7 @@ nsTextServicesDocument::DeleteSelection() // Now delete the actual content! - result = mEditor->DeleteSelection(nsIEditor::eDeletePrevious); + result = mEditor->DeleteSelection(nsIEditor::ePrevious); if (NS_FAILED(result)) { diff --git a/mozilla/layout/html/content/src/nsEditorController.cpp b/mozilla/layout/html/content/src/nsEditorController.cpp index 339bd38e1fe..bc8a7d43a22 100644 --- a/mozilla/layout/html/content/src/nsEditorController.cpp +++ b/mozilla/layout/html/content/src/nsEditorController.cpp @@ -193,7 +193,7 @@ NS_IMETHODIMP nsEditorController::DoCommand(const PRUnichar *aCommand) } else if (PR_TRUE==mDeleteString.Equals(aCommand)) { - NS_ENSURE_SUCCESS(editor->DeleteSelection(nsIEditor::eDeleteNext), NS_ERROR_FAILURE); + NS_ENSURE_SUCCESS(editor->DeleteSelection(nsIEditor::eNext), NS_ERROR_FAILURE); } else if (PR_TRUE==mSelectAllString.Equals(aCommand)) {