From edba5add24731c8900b8b3b61b4278db8e6573b3 Mon Sep 17 00:00:00 2001 From: "jfrancis%netscape.com" Date: Tue, 7 Dec 1999 08:30:19 +0000 Subject: [PATCH] fix for 20457: deletion in editor pretty darn screwy. Lots of changes to improve this. Document modifying entry points in editor now go thru a standard sandwich, which allows tracking of what changed in the doc, which allows proper post-processing of what changed. Also, all deletions now go thru the rules code, if applicable. Also some bugs in tracking the changed document portion have been fixed. Finally selection setting after a delete is improved. git-svn-id: svn://10.0.0.236/trunk@55568 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/editor/base/DeleteRangeTxn.cpp | 8 +- mozilla/editor/base/DeleteRangeTxn.h | 2 +- mozilla/editor/base/TextEditorTest.cpp | 2 +- mozilla/editor/base/nsEditRules.h | 3 + mozilla/editor/base/nsEditor.cpp | 385 +++++++----------- mozilla/editor/base/nsEditor.h | 53 ++- .../editor/base/nsEditorEventListeners.cpp | 4 +- mozilla/editor/base/nsEditorShell.cpp | 18 +- mozilla/editor/base/nsEditorUtils.h | 21 + mozilla/editor/base/nsHTMLEditRules.cpp | 239 +++++++---- mozilla/editor/base/nsHTMLEditRules.h | 12 +- mozilla/editor/base/nsHTMLEditor.cpp | 81 +++- mozilla/editor/base/nsHTMLEditor.h | 36 +- mozilla/editor/base/nsHTMLEditorLog.cpp | 2 +- mozilla/editor/base/nsHTMLEditorLog.h | 2 +- mozilla/editor/base/nsTextEditRules.cpp | 160 +++++--- mozilla/editor/base/nsTextEditRules.h | 39 +- mozilla/editor/composer/src/nsEditorShell.cpp | 18 +- .../editor/libeditor/base/DeleteRangeTxn.cpp | 8 +- .../editor/libeditor/base/DeleteRangeTxn.h | 2 +- mozilla/editor/libeditor/base/nsEditRules.h | 3 + mozilla/editor/libeditor/base/nsEditor.cpp | 385 +++++++----------- mozilla/editor/libeditor/base/nsEditor.h | 53 ++- mozilla/editor/libeditor/base/nsEditorUtils.h | 21 + .../editor/libeditor/html/TextEditorTest.cpp | 2 +- .../editor/libeditor/html/nsHTMLEditRules.cpp | 239 +++++++---- .../editor/libeditor/html/nsHTMLEditRules.h | 12 +- .../editor/libeditor/html/nsHTMLEditor.cpp | 81 +++- mozilla/editor/libeditor/html/nsHTMLEditor.h | 36 +- .../editor/libeditor/html/nsHTMLEditorLog.cpp | 2 +- .../editor/libeditor/html/nsHTMLEditorLog.h | 2 +- .../libeditor/text/nsEditorEventListeners.cpp | 4 +- .../editor/libeditor/text/nsTextEditRules.cpp | 160 +++++--- .../editor/libeditor/text/nsTextEditRules.h | 39 +- .../editor/libeditor/txtsvc/nsTSDNotifier.cpp | 14 + .../editor/libeditor/txtsvc/nsTSDNotifier.h | 4 +- .../txtsvc/nsTextServicesDocument.cpp | 2 +- mozilla/editor/public/nsIEditActionListener.h | 12 + mozilla/editor/public/nsIEditor.h | 16 +- mozilla/editor/public/nsIHTMLEditor.h | 65 +-- mozilla/editor/txtsvc/src/nsTSDNotifier.cpp | 14 + mozilla/editor/txtsvc/src/nsTSDNotifier.h | 4 +- .../txtsvc/src/nsTextServicesDocument.cpp | 2 +- .../html/content/src/nsEditorController.cpp | 2 +- 44 files changed, 1339 insertions(+), 930 deletions(-) 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)) {