diff --git a/mozilla/editor/base/CreateElementTxn.cpp b/mozilla/editor/base/CreateElementTxn.cpp index 4b0436e4287..824b266bcf4 100644 --- a/mozilla/editor/base/CreateElementTxn.cpp +++ b/mozilla/editor/base/CreateElementTxn.cpp @@ -24,15 +24,21 @@ #include "nsIDOMElement.h" #include "nsIEditorSupport.h" +#ifdef NS_DEBUG +static PRBool gNoisy = PR_TRUE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif + CreateElementTxn::CreateElementTxn() : EditTxn() { } NS_IMETHODIMP CreateElementTxn::Init(nsIEditor *aEditor, - const nsString& aTag, - nsIDOMNode *aParent, - PRUint32 aOffsetInParent) + const nsString& aTag, + nsIDOMNode *aParent, + PRUint32 aOffsetInParent) { NS_ASSERTION(aEditor&&aParent, "null args"); if (aEditor && aParent) @@ -41,8 +47,6 @@ NS_IMETHODIMP CreateElementTxn::Init(nsIEditor *aEditor, mTag = aTag; mParent = do_QueryInterface(aParent); mOffsetInParent = aOffsetInParent; - mNewNode = do_QueryInterface(nsnull); - mRefNode = do_QueryInterface(nsnull); #ifdef NS_DEBUG { nsCOMPtr testChildNodes; @@ -63,6 +67,8 @@ CreateElementTxn::~CreateElementTxn() NS_IMETHODIMP CreateElementTxn::Do(void) { + if (gNoisy) { printf("Do Create Element parent = %p, offset = %d\n", + mParent.get(), mOffsetInParent); } NS_ASSERTION(mEditor, "bad state -- null editor"); nsresult result = NS_ERROR_NULL_POINTER; if (mEditor) @@ -92,6 +98,7 @@ NS_IMETHODIMP CreateElementTxn::Do(void) NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewNode)), "could not create element."); if ((NS_SUCCEEDED(result)) && (mNewNode)) { + if (gNoisy) { printf(" newNode = %p\n", mNewNode.get()); } // insert the new node nsCOMPtr resultNode; if (CreateElementTxn::eAppend==mOffsetInParent) @@ -134,6 +141,8 @@ NS_IMETHODIMP CreateElementTxn::Do(void) NS_IMETHODIMP CreateElementTxn::Undo(void) { + if (gNoisy) { printf("Undo Create Element, mParent = %p, node = %p\n", + mParent.get(), mNewNode.get()); } nsCOMPtr resultNode; nsresult result = mParent->RemoveChild(mNewNode, getter_AddRefs(resultNode)); if (NS_SUCCEEDED(result)) @@ -154,6 +163,18 @@ NS_IMETHODIMP CreateElementTxn::Undo(void) NS_IMETHODIMP CreateElementTxn::Redo(void) { + if (gNoisy) { printf("Redo Create Element\n"); } + + // first, reset mNewNode so it has no attributes or content + nsCOMPtrnodeAsText; + nodeAsText = do_QueryInterface(mNewNode); + if (nodeAsText) + { + nsAutoString nullString; + nodeAsText->SetData(nullString); + } + + // now, reinsert mNewNode nsCOMPtr resultNode; nsresult result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); if (NS_SUCCEEDED(result)) diff --git a/mozilla/editor/base/DeleteElementTxn.cpp b/mozilla/editor/base/DeleteElementTxn.cpp index 484c5d0a0bd..15b9bd004f8 100644 --- a/mozilla/editor/base/DeleteElementTxn.cpp +++ b/mozilla/editor/base/DeleteElementTxn.cpp @@ -22,7 +22,7 @@ #endif #ifdef NS_DEBUG -static PRBool gNoisy = PR_FALSE; +static PRBool gNoisy = PR_TRUE; #else static const PRBool gNoisy = PR_FALSE; #endif @@ -50,6 +50,7 @@ DeleteElementTxn::~DeleteElementTxn() NS_IMETHODIMP DeleteElementTxn::Do(void) { + if (gNoisy) { printf("Do Delete Element element = %p\n", mElement.get()); } if (!mElement) return NS_ERROR_NULL_POINTER; @@ -96,6 +97,7 @@ NS_IMETHODIMP DeleteElementTxn::Do(void) NS_IMETHODIMP DeleteElementTxn::Undo(void) { + if (gNoisy) { printf("Do Delete Element element = %p, parent = %p\n", mElement.get(), mParent.get()); } if (!mParent || !mElement) return NS_ERROR_NULL_POINTER; @@ -106,6 +108,7 @@ NS_IMETHODIMP DeleteElementTxn::Undo(void) NS_IMETHODIMP DeleteElementTxn::Redo(void) { + if (gNoisy) { printf("Redo Delete Element\n"); } if (!mParent || !mElement) return NS_ERROR_NULL_POINTER; @@ -114,6 +117,7 @@ NS_IMETHODIMP DeleteElementTxn::Redo(void) return result; } + NS_IMETHODIMP DeleteElementTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction) { if (nsnull!=aDidMerge) diff --git a/mozilla/editor/base/DeleteRangeTxn.cpp b/mozilla/editor/base/DeleteRangeTxn.cpp index 063cc1448b4..c0014792e15 100644 --- a/mozilla/editor/base/DeleteRangeTxn.cpp +++ b/mozilla/editor/base/DeleteRangeTxn.cpp @@ -36,7 +36,7 @@ static NS_DEFINE_IID(kDeleteTextTxnIID, DELETE_TEXT_TXN_IID); static NS_DEFINE_IID(kDeleteElementTxnIID, DELETE_ELEMENT_TXN_IID); #ifdef NS_DEBUG -static PRBool gNoisy = PR_FALSE; +static PRBool gNoisy = PR_TRUE; #else static const PRBool gNoisy = PR_FALSE; #endif @@ -111,6 +111,7 @@ DeleteRangeTxn::~DeleteRangeTxn() NS_IMETHODIMP DeleteRangeTxn::Do(void) { + if (gNoisy) { printf("Do Delete Range\n"); } if (!mStartParent || !mEndParent || !mCommonParent) return NS_ERROR_NULL_POINTER; @@ -157,6 +158,7 @@ NS_IMETHODIMP DeleteRangeTxn::Do(void) NS_IMETHODIMP DeleteRangeTxn::Undo(void) { + if (gNoisy) { printf("Undo Delete Range\n"); } if (!mStartParent || !mEndParent || !mCommonParent) return NS_ERROR_NULL_POINTER; @@ -177,6 +179,7 @@ NS_IMETHODIMP DeleteRangeTxn::Undo(void) NS_IMETHODIMP DeleteRangeTxn::Redo(void) { + if (gNoisy) { printf("Redo Delete Range\n"); } if (!mStartParent || !mEndParent || !mCommonParent) return NS_ERROR_NULL_POINTER; diff --git a/mozilla/editor/base/DeleteTextTxn.cpp b/mozilla/editor/base/DeleteTextTxn.cpp index b321d49b53b..8f5b0114fdf 100644 --- a/mozilla/editor/base/DeleteTextTxn.cpp +++ b/mozilla/editor/base/DeleteTextTxn.cpp @@ -20,6 +20,11 @@ #include "nsIDOMCharacterData.h" #include "nsIDOMSelection.h" +#ifdef NS_DEBUG +static PRBool gNoisy = PR_TRUE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif DeleteTextTxn::DeleteTextTxn() : EditTxn() @@ -47,6 +52,7 @@ NS_IMETHODIMP DeleteTextTxn::Init(nsIEditor *aEditor, NS_IMETHODIMP DeleteTextTxn::Do(void) { + if (gNoisy) { printf("Do Delete Text\n"); } nsresult result = NS_ERROR_NULL_POINTER; if (mEditor && mElement) { @@ -71,6 +77,7 @@ NS_IMETHODIMP DeleteTextTxn::Do(void) // was it an insertion point or an extended selection? NS_IMETHODIMP DeleteTextTxn::Undo(void) { + if (gNoisy) { printf("Undo Delete Text\n"); } nsresult result = NS_ERROR_NULL_POINTER; if (mEditor && mElement) { diff --git a/mozilla/editor/base/EditAggregateTxn.cpp b/mozilla/editor/base/EditAggregateTxn.cpp index e6a93f29632..27f3032aa0e 100644 --- a/mozilla/editor/base/EditAggregateTxn.cpp +++ b/mozilla/editor/base/EditAggregateTxn.cpp @@ -24,7 +24,7 @@ EditAggregateTxn::EditAggregateTxn() : EditTxn() { - NS_INIT_REFCNT(); + // base class does this: NS_INIT_REFCNT(); mChildren = new nsVoidArray(); } @@ -205,6 +205,30 @@ NS_IMETHODIMP EditAggregateTxn::GetName(nsIAtom **aName) return NS_ERROR_NULL_POINTER; } +NS_IMETHODIMP_(nsrefcnt) EditAggregateTxn::AddRef(void) +{ + return EditTxn::AddRef(); +} + +//NS_IMPL_RELEASE_INHERITED(Class, Super) +NS_IMETHODIMP_(nsrefcnt) EditAggregateTxn::Release(void) +{ + return EditTxn::Release(); +} + +//NS_IMPL_QUERY_INTERFACE_INHERITED(Class, Super, AdditionalInterface) +NS_IMETHODIMP EditAggregateTxn::QueryInterface(REFNSIID aIID, void** aInstancePtr) +{ + if (!aInstancePtr) return NS_ERROR_NULL_POINTER; + + if (aIID.Equals(EditAggregateTxn::GetIID())) { + *aInstancePtr = (nsISupports*)(EditAggregateTxn*)(this); + NS_ADDREF_THIS(); + return NS_OK; + } + return EditTxn::QueryInterface(aIID, aInstancePtr); +} + diff --git a/mozilla/editor/base/EditAggregateTxn.h b/mozilla/editor/base/EditAggregateTxn.h index 2e83a62e4a5..60a2ce9a306 100644 --- a/mozilla/editor/base/EditAggregateTxn.h +++ b/mozilla/editor/base/EditAggregateTxn.h @@ -38,6 +38,10 @@ class EditAggregateTxn : public EditTxn { public: + NS_DECL_ISUPPORTS_INHERITED + + static const nsIID& GetIID() { static nsIID iid = EDIT_AGGREGATE_TXN_IID; return iid; } + EditAggregateTxn(); virtual ~EditAggregateTxn(); diff --git a/mozilla/editor/base/InsertElementTxn.cpp b/mozilla/editor/base/InsertElementTxn.cpp index 05122a67247..5dfae44f06a 100644 --- a/mozilla/editor/base/InsertElementTxn.cpp +++ b/mozilla/editor/base/InsertElementTxn.cpp @@ -17,9 +17,10 @@ */ #include "InsertElementTxn.h" +#include "nsIDOMSelection.h" #ifdef NS_DEBUG -static PRBool gNoisy = PR_FALSE; +static PRBool gNoisy = PR_TRUE; #else static const PRBool gNoisy = PR_FALSE; #endif @@ -32,14 +33,17 @@ InsertElementTxn::InsertElementTxn() NS_IMETHODIMP InsertElementTxn::Init(nsIDOMNode *aNode, nsIDOMNode *aParent, - PRInt32 aOffset) + PRInt32 aOffset, + nsIEditor *aEditor) { - if (!aNode || !aParent) + NS_ASSERTION(aNode && aParent && aEditor, "bad arg"); + if (!aNode || !aParent || !aEditor) return NS_ERROR_NULL_POINTER; mNode = do_QueryInterface(aNode); mParent = do_QueryInterface(aParent); mOffset = aOffset; + mEditor = do_QueryInterface(aEditor); return NS_OK; } @@ -50,6 +54,7 @@ InsertElementTxn::~InsertElementTxn() NS_IMETHODIMP InsertElementTxn::Do(void) { + if (gNoisy) { printf("Do Insert Element\n"); } if (!mNode || !mParent) return NS_ERROR_NULL_POINTER; @@ -75,11 +80,21 @@ NS_IMETHODIMP InsertElementTxn::Do(void) nsCOMPtr resultNode; result = mParent->InsertBefore(mNode, refNode, getter_AddRefs(resultNode)); + if (NS_SUCCEEDED(result) && resultNode) + { + nsCOMPtr selection; + result = mEditor->GetSelection(getter_AddRefs(selection)); + if ((NS_SUCCEEDED(result)) && selection) + { + selection->Collapse(mParent, mOffset); + } + } return result; } NS_IMETHODIMP InsertElementTxn::Undo(void) { + if (gNoisy) { printf("Undo Insert Element\n"); } if (!mNode || !mParent) return NS_ERROR_NULL_POINTER; diff --git a/mozilla/editor/base/InsertElementTxn.h b/mozilla/editor/base/InsertElementTxn.h index f4dafc1f0db..4f04676a3d6 100644 --- a/mozilla/editor/base/InsertElementTxn.h +++ b/mozilla/editor/base/InsertElementTxn.h @@ -20,6 +20,7 @@ #define InsertElementTxn_h__ #include "EditTxn.h" +#include "nsIEditor.h" #include "nsIDOMNode.h" #include "nsCOMPtr.h" @@ -42,7 +43,8 @@ public: */ NS_IMETHOD Init(nsIDOMNode *aNode, nsIDOMNode *aParent, - PRInt32 aOffset); + PRInt32 aOffset, + nsIEditor *aEditor); private: InsertElementTxn(); @@ -71,6 +73,9 @@ protected: /** the node into which the new node will be inserted */ nsCOMPtr mParent; + /** the editor for this transaction */ + nsCOMPtr mEditor; + /** the index in mParent for the new node */ PRInt32 mOffset; diff --git a/mozilla/editor/base/InsertTextTxn.cpp b/mozilla/editor/base/InsertTextTxn.cpp index 70c86cfc48a..11e9fc0ff2c 100644 --- a/mozilla/editor/base/InsertTextTxn.cpp +++ b/mozilla/editor/base/InsertTextTxn.cpp @@ -26,6 +26,12 @@ static NS_DEFINE_IID(kInsertTextTxnIID, INSERT_TEXT_TXN_IID); static NS_DEFINE_IID(kIDOMSelectionIID, NS_IDOMSELECTION_IID); +#ifdef NS_DEBUG +static PRBool gNoisy = PR_TRUE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif + nsIAtom *InsertTextTxn::gInsertTextTxnName; nsresult InsertTextTxn::ClassInit() @@ -45,9 +51,9 @@ InsertTextTxn::~InsertTextTxn() } NS_IMETHODIMP InsertTextTxn::Init(nsIDOMCharacterData *aElement, - PRUint32 aOffset, - const nsString& aStringToInsert, - nsIPresShell* aPresShell) + PRUint32 aOffset, + const nsString &aStringToInsert, + nsIPresShell *aPresShell) { mElement = do_QueryInterface(aElement); mOffset = aOffset; @@ -58,6 +64,7 @@ NS_IMETHODIMP InsertTextTxn::Init(nsIDOMCharacterData *aElement, NS_IMETHODIMP InsertTextTxn::Do(void) { + if (gNoisy) { printf("Do Insert Text element = %p\n", mElement.get()); } // advance caret: This requires the presentation shell to get the selection. nsCOMPtr selection; nsresult result = mPresShell->GetSelection(getter_AddRefs(selection)); @@ -74,6 +81,7 @@ NS_IMETHODIMP InsertTextTxn::Do(void) NS_IMETHODIMP InsertTextTxn::Undo(void) { + if (gNoisy) { printf("Undo Insert Text element = %p\n", mElement.get()); } nsresult result; PRUint32 length = mStringToInsert.Length(); result = mElement->DeleteData(mOffset, length); @@ -108,6 +116,7 @@ NS_IMETHODIMP InsertTextTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransacti otherTxn->GetData(otherData); mStringToInsert += otherData; *aDidMerge = PR_TRUE; + if (gNoisy) { printf("InsertTextTxn assimilated %p\n", aTransaction); } } } else @@ -117,7 +126,7 @@ NS_IMETHODIMP InsertTextTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransacti { nsCOMPtr txnName; otherTxn->GetName(getter_AddRefs(txnName)); - if (txnName.get()==gInsertTextTxnName) + if (txnName && txnName.get()==gInsertTextTxnName) { // yep, it's one of ours. By definition, it must contain only // another aggregate with a single child, // or a single InsertTextTxn @@ -135,6 +144,7 @@ NS_IMETHODIMP InsertTextTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransacti otherInsertTxn->GetData(otherData); mStringToInsert += otherData; *aDidMerge = PR_TRUE; + if (gNoisy) { printf("InsertTextTxn assimilated %p\n", aTransaction); } } } } diff --git a/mozilla/editor/base/JoinElementTxn.cpp b/mozilla/editor/base/JoinElementTxn.cpp index 3d6c0d662f9..c12a638480f 100644 --- a/mozilla/editor/base/JoinElementTxn.cpp +++ b/mozilla/editor/base/JoinElementTxn.cpp @@ -22,6 +22,12 @@ #include "nsIDOMSelection.h" #include "nsIEditorSupport.h" +#ifdef NS_DEBUG +static PRBool gNoisy = PR_TRUE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif + static NS_DEFINE_IID(kIEditorSupportIID, NS_IEDITORSUPPORT_IID); @@ -45,8 +51,10 @@ JoinElementTxn::~JoinElementTxn() { } +// After Do() and Redo(), the left node is removed from the content tree and right node remains. NS_IMETHODIMP JoinElementTxn::Do(void) { + if (gNoisy) { printf("%p Do Join of %p and %p\n", this, mLeftNode.get(), mRightNode.get()); } nsresult result; if ((mLeftNode) && (mRightNode)) @@ -82,6 +90,7 @@ NS_IMETHODIMP JoinElementTxn::Do(void) result = editor->JoinNodesImpl(mRightNode, mLeftNode, mParent, PR_FALSE); if (NS_SUCCEEDED(result)) { + if (gNoisy) { printf(" left node = %p removed\n", this, mLeftNode.get()); } nsCOMPtrselection; mEditor->GetSelection(getter_AddRefs(selection)); if (selection) @@ -102,9 +111,11 @@ NS_IMETHODIMP JoinElementTxn::Do(void) return result; } - +//XXX: what if instead of split, we just deleted the unneeded children of mRight +// and re-inserted mLeft? NS_IMETHODIMP JoinElementTxn::Undo(void) { + /* nsresult result; nsCOMPtr editor; result = mEditor->QueryInterface(kIEditorSupportIID, getter_AddRefs(editor)); @@ -112,6 +123,7 @@ NS_IMETHODIMP JoinElementTxn::Undo(void) result = editor->SplitNodeImpl(mRightNode, mOffset, mLeftNode, mParent); if (NS_SUCCEEDED(result) && mLeftNode) { + if (gNoisy) { printf(" created left node = %p\n", this, mLeftNode.get()); } nsCOMPtrselection; mEditor->GetSelection(getter_AddRefs(selection)); if (selection) @@ -124,10 +136,47 @@ NS_IMETHODIMP JoinElementTxn::Undo(void) result = NS_ERROR_NOT_IMPLEMENTED; } return result; + */ + + if (gNoisy) { printf("%p Undo Join, right node = %p\n", this, mRightNode.get()); } + NS_ASSERTION(mRightNode && mLeftNode && mParent, "bad state"); + if (!mRightNode || !mLeftNode || !mParent) { + return NS_ERROR_NOT_INITIALIZED; + } + nsresult result; + nsCOMPtrresultNode; + // first, massage the existing node so it is in its post-split state + nsCOMPtrrightNodeAsText; + rightNodeAsText = do_QueryInterface(mRightNode); + if (rightNodeAsText) + { + result = rightNodeAsText->DeleteData(0, mOffset); + } + else + { + nsCOMPtrchild; + nsCOMPtrnextSibling; + result = mRightNode->GetFirstChild(getter_AddRefs(child)); + PRInt32 i; + for (i=0; iGetNextSibling(getter_AddRefs(nextSibling)); + result = mRightNode->RemoveChild(child, getter_AddRefs(resultNode)); + child = do_QueryInterface(nextSibling); + } + } + // second, re-insert the left node into the tree + result = mParent->InsertBefore(mLeftNode, mRightNode, getter_AddRefs(resultNode)); + return result; + } +/* NS_IMETHODIMP JoinElementTxn::Redo(void) { + if (gNoisy) { printf("Redo Join\n"); } nsresult result; nsCOMPtr editor; result = mEditor->QueryInterface(kIEditorSupportIID, getter_AddRefs(editor)); @@ -149,7 +198,7 @@ NS_IMETHODIMP JoinElementTxn::Redo(void) } return result; } - +*/ NS_IMETHODIMP JoinElementTxn::GetIsTransient(PRBool *aIsTransient) { diff --git a/mozilla/editor/base/JoinElementTxn.h b/mozilla/editor/base/JoinElementTxn.h index 0d7fa2d699c..a85c0abb217 100644 --- a/mozilla/editor/base/JoinElementTxn.h +++ b/mozilla/editor/base/JoinElementTxn.h @@ -31,8 +31,10 @@ /** - * A transaction that joins two elements E1 and E2 into a single node E. + * A transaction that joins two elements E1 (left node) and E2 (right node) + * into a single node E. * The children of E are the children of E1 followed by the children of E2. + * After Do() and Redo(), E1 is removed from the content tree and E2 remains. */ class JoinElementTxn : public EditTxn { @@ -57,7 +59,7 @@ public: NS_IMETHOD Undo(void); - NS_IMETHOD Redo(void); +// NS_IMETHOD Redo(void); NS_IMETHOD GetIsTransient(PRBool *aIsTransient); diff --git a/mozilla/editor/base/PlaceholderTxn.cpp b/mozilla/editor/base/PlaceholderTxn.cpp index 941d6e05c95..83f7feaa694 100644 --- a/mozilla/editor/base/PlaceholderTxn.cpp +++ b/mozilla/editor/base/PlaceholderTxn.cpp @@ -39,6 +39,7 @@ PlaceholderTxn::~PlaceholderTxn() NS_IMETHODIMP PlaceholderTxn::Do(void) { + if (gNoisy) { printf("PlaceholderTxn Do\n"); } return NS_OK; } @@ -55,6 +56,7 @@ NS_IMETHODIMP PlaceholderTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransact { // yep, it's one of ours. Assimilate it. AppendChild(editTxn); *aDidMerge = PR_TRUE; + if (gNoisy) { printf("Placeholder txn assimilated %p\n", aTransaction); } } else { // let our last child txn make the choice diff --git a/mozilla/editor/base/SplitElementTxn.cpp b/mozilla/editor/base/SplitElementTxn.cpp index bfb5f09f188..3c9aa43e843 100644 --- a/mozilla/editor/base/SplitElementTxn.cpp +++ b/mozilla/editor/base/SplitElementTxn.cpp @@ -19,8 +19,15 @@ #include "SplitElementTxn.h" #include "nsIDOMNode.h" #include "nsIDOMSelection.h" +#include "nsIDOMCharacterData.h" #include "nsIEditorSupport.h" +#ifdef NS_DEBUG +static PRBool gNoisy = PR_TRUE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif + static NS_DEFINE_IID(kIEditorSupportIID, NS_IEDITORSUPPORT_IID); // note that aEditor is not refcounted @@ -45,6 +52,7 @@ SplitElementTxn::~SplitElementTxn() NS_IMETHODIMP SplitElementTxn::Do(void) { + if (gNoisy) { printf("%p Do Split of node %p offset %d\n", this, mExistingRightNode.get(), mOffset); } NS_ASSERTION(mExistingRightNode, "bad state"); if (!mExistingRightNode) { return NS_ERROR_NOT_INITIALIZED; @@ -55,6 +63,7 @@ NS_IMETHODIMP SplitElementTxn::Do(void) if ((NS_SUCCEEDED(result)) && (mNewLeftNode)) { + if (gNoisy) { printf(" created left node = %p\n", this, mNewLeftNode.get()); } // get the parent node result = mExistingRightNode->GetParentNode(getter_AddRefs(mParent)); // insert the new node @@ -85,6 +94,10 @@ NS_IMETHODIMP SplitElementTxn::Do(void) NS_IMETHODIMP SplitElementTxn::Undo(void) { + if (gNoisy) { + printf("%p Undo Split of existing node %p and new node %p offset %d\n", + this, mExistingRightNode.get(), mNewLeftNode.get(), mOffset); + } NS_ASSERTION(mExistingRightNode && mNewLeftNode && mParent, "bad state"); if (!mExistingRightNode || !mNewLeftNode || !mParent) { return NS_ERROR_NOT_INITIALIZED; @@ -112,8 +125,9 @@ NS_IMETHODIMP SplitElementTxn::Undo(void) if (NS_SUCCEEDED(result) && editor) { result = editor->JoinNodesImpl(mExistingRightNode, mNewLeftNode, mParent, PR_FALSE); - if (NS_SUCCEEDED(result) && mNewLeftNode) + if (NS_SUCCEEDED(result)) { + if (gNoisy) { printf(" left node = %p removed\n", this, mNewLeftNode.get()); } nsCOMPtrselection; mEditor->GetSelection(getter_AddRefs(selection)); if (selection) @@ -128,25 +142,48 @@ NS_IMETHODIMP SplitElementTxn::Undo(void) return result; } -/* +/* redo cannot simply resplit the right node, because subsequent transactions + * on the redo stack may depend on the left node existing in its previous state. + */ NS_IMETHODIMP SplitElementTxn::Redo(void) { NS_ASSERTION(mExistingRightNode && mNewLeftNode && mParent, "bad state"); if (!mExistingRightNode || !mNewLeftNode || !mParent) { return NS_ERROR_NOT_INITIALIZED; } + if (gNoisy) { + printf("%p Redo Split of existing node %p and new node %p offset %d\n", + this, mExistingRightNode.get(), mNewLeftNode.get(), mOffset); + } nsresult result; - nsCOMPtr editor; - result = mEditor->QueryInterface(kIEditorSupportIID, getter_AddRefs(editor)); - if (NS_SUCCEEDED(result) && editor) { - result = editor->SplitNodeImpl(mExistingRightNode, mOffset, mNewLeftNode, mParent); + nsCOMPtrresultNode; + // first, massage the existing node so it is in its post-split state + nsCOMPtrrightNodeAsText; + rightNodeAsText = do_QueryInterface(mExistingRightNode); + if (rightNodeAsText) + { + result = rightNodeAsText->DeleteData(0, mOffset); } - else { - result = NS_ERROR_NOT_IMPLEMENTED; + else + { + nsCOMPtrchild; + nsCOMPtrnextSibling; + result = mExistingRightNode->GetFirstChild(getter_AddRefs(child)); + PRInt32 i; + for (i=0; iGetNextSibling(getter_AddRefs(nextSibling)); + result = mExistingRightNode->RemoveChild(child, getter_AddRefs(resultNode)); + child = do_QueryInterface(nextSibling); + } } + // second, re-insert the left node into the tree + result = mParent->InsertBefore(mNewLeftNode, mExistingRightNode, getter_AddRefs(resultNode)); return result; } -*/ + NS_IMETHODIMP SplitElementTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction) { diff --git a/mozilla/editor/base/SplitElementTxn.h b/mozilla/editor/base/SplitElementTxn.h index 3d39e6f326c..a2c399bcc58 100644 --- a/mozilla/editor/base/SplitElementTxn.h +++ b/mozilla/editor/base/SplitElementTxn.h @@ -57,7 +57,7 @@ public: NS_IMETHOD Undo(void); - //NS_IMETHOD Redo(void); + NS_IMETHOD Redo(void); NS_IMETHOD Merge(PRBool *aDidMerge, nsITransaction *aTransaction); diff --git a/mozilla/editor/base/nsEditProperty.cpp b/mozilla/editor/base/nsEditProperty.cpp index de516d284d4..5690a29a010 100644 --- a/mozilla/editor/base/nsEditProperty.cpp +++ b/mozilla/editor/base/nsEditProperty.cpp @@ -26,8 +26,56 @@ NS_IMPL_ADDREF(nsEditProperty) NS_IMPL_RELEASE(nsEditProperty) -nsIAtom * nsIEditProperty::bold = NS_NewAtom("BOLD"); -nsIAtom * nsIEditProperty::italic = NS_NewAtom("ITALIC"); +// XXX: remove when html atoms are exported from layout +// tags +nsIAtom * nsIEditProperty::a; +nsIAtom * nsIEditProperty::b; +nsIAtom * nsIEditProperty::big; +nsIAtom * nsIEditProperty::font; +nsIAtom * nsIEditProperty::i; +nsIAtom * nsIEditProperty::span; +nsIAtom * nsIEditProperty::small; +nsIAtom * nsIEditProperty::strike; +nsIAtom * nsIEditProperty::sub; +nsIAtom * nsIEditProperty::sup; +nsIAtom * nsIEditProperty::tt; +nsIAtom * nsIEditProperty::u; + +void +nsEditProperty::InstanceInit() +{ + // tags + nsIEditProperty::a = NS_NewAtom("A"); + nsIEditProperty::b = NS_NewAtom("B"); + nsIEditProperty::big = NS_NewAtom("BIG"); + nsIEditProperty::font = NS_NewAtom("FONT"); + nsIEditProperty::i = NS_NewAtom("I"); + nsIEditProperty::span = NS_NewAtom("SPAN"); + nsIEditProperty::small =NS_NewAtom("SMALL"); + nsIEditProperty::strike=NS_NewAtom("STRIKE"); + nsIEditProperty::sub = NS_NewAtom("SUB"); + nsIEditProperty::sup = NS_NewAtom("SUP"); + nsIEditProperty::tt = NS_NewAtom("TT"); + nsIEditProperty::u = NS_NewAtom("U"); +} + +void +nsEditProperty::InstanceShutdown() +{ + // tags + NS_IF_RELEASE(nsIEditProperty::a); + NS_IF_RELEASE(nsIEditProperty::b); + NS_IF_RELEASE(nsIEditProperty::big); + NS_IF_RELEASE(nsIEditProperty::font); + NS_IF_RELEASE(nsIEditProperty::i); + NS_IF_RELEASE(nsIEditProperty::span); + NS_IF_RELEASE(nsIEditProperty::small); + NS_IF_RELEASE(nsIEditProperty::strike); + NS_IF_RELEASE(nsIEditProperty::sub); + NS_IF_RELEASE(nsIEditProperty::sup); + NS_IF_RELEASE(nsIEditProperty::tt); + NS_IF_RELEASE(nsIEditProperty::u); +} NS_IMETHODIMP nsEditProperty::QueryInterface(REFNSIID aIID, void** aInstancePtr) diff --git a/mozilla/editor/base/nsEditProperty.h b/mozilla/editor/base/nsEditProperty.h index 5ce7692a3c5..d6726e21b3b 100644 --- a/mozilla/editor/base/nsEditProperty.h +++ b/mozilla/editor/base/nsEditProperty.h @@ -44,6 +44,10 @@ public: NS_IMETHOD GetValue(nsIAtom **aValue) const; NS_IMETHOD GetAppliesToAll(PRBool *aAppliesToAll) const; + // temporary methods + static void InstanceInit(); + static void InstanceShutdown(); + protected: nsCOMPtrmProperty; nsCOMPtrmValue; diff --git a/mozilla/editor/base/nsEditor.cpp b/mozilla/editor/base/nsEditor.cpp index 63c745f4505..15a2d326fa1 100644 --- a/mozilla/editor/base/nsEditor.cpp +++ b/mozilla/editor/base/nsEditor.cpp @@ -69,6 +69,7 @@ #include "nsIViewManager.h" #include "nsIView.h" // END +#endif //#define NEW_CLIPBOARD_SUPPORT @@ -94,7 +95,6 @@ static NS_DEFINE_IID(kCXIFFormatConverterCID, NS_XIFFORMATCONVERTER_CID); static NS_DEFINE_IID(kIFormatConverterIID, NS_IFORMATCONVERTER_IID); #endif -#endif static NS_DEFINE_IID(kIContentIID, NS_ICONTENT_IID); static NS_DEFINE_IID(kIDOMTextIID, NS_IDOMTEXT_IID); static NS_DEFINE_IID(kIDOMElementIID, NS_IDOMELEMENT_IID); @@ -146,6 +146,12 @@ static NS_DEFINE_CID(kComponentManagerCID, NS_COMPONENTMANAGER_CID); #define NS_ERROR_EDITOR_NO_SELECTION NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_EDITOR,1) #define NS_ERROR_EDITOR_NO_TEXTNODE NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_EDITOR,2) +#ifdef NS_DEBUG +static PRBool gNoisy = PR_TRUE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif + /* ----- TEST METHODS DECLARATIONS ----- */ @@ -676,6 +682,7 @@ nsEditor::GetFirstTextNode(nsIDOMNode *aNode, nsIDOMNode **aRetNode) NS_IMETHODIMP nsEditor::Do(nsITransaction *aTxn) { + if (gNoisy) { printf("Editor::Do ----------\n"); } nsresult result = NS_OK; nsCOMPtrselection; nsresult selectionResult = GetSelection(getter_AddRefs(selection)); @@ -698,6 +705,7 @@ nsEditor::Do(nsITransaction *aTxn) NS_IMETHODIMP nsEditor::Undo(PRUint32 aCount) { + if (gNoisy) { printf("Editor::Undo ----------\n"); } nsresult result = NS_OK; nsCOMPtrselection; nsresult selectionResult = GetSelection(getter_AddRefs(selection)); @@ -721,6 +729,7 @@ nsEditor::Undo(PRUint32 aCount) NS_IMETHODIMP nsEditor::Redo(PRUint32 aCount) { + if (gNoisy) { printf("Editor::Redo ----------\n"); } nsresult result = NS_OK; nsCOMPtrselection; nsresult selectionResult = GetSelection(getter_AddRefs(selection)); @@ -955,8 +964,8 @@ NS_IMETHODIMP nsEditor::CreateTxnForCreateElement(const nsString& aTag, } NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode, - nsIDOMNode * aParent, - PRInt32 aPosition) + nsIDOMNode * aParent, + PRInt32 aPosition) { InsertElementTxn *txn; nsresult result = CreateTxnForInsertElement(aNode, aParent, aPosition, &txn); @@ -976,7 +985,7 @@ NS_IMETHODIMP nsEditor::CreateTxnForInsertElement(nsIDOMNode * aNode, { result = TransactionFactory::GetNewTransaction(kInsertElementTxnIID, (EditTxn **)aTxn); if (NS_SUCCEEDED(result)) { - result = (*aTxn)->Init(aNode, aParent, aPosition); + result = (*aTxn)->Init(aNode, aParent, aPosition, this); } } return result; diff --git a/mozilla/editor/base/nsEditorEventListeners.cpp b/mozilla/editor/base/nsEditorEventListeners.cpp index 8ada50ad02e..2e548a9157c 100644 --- a/mozilla/editor/base/nsEditorEventListeners.cpp +++ b/mozilla/editor/base/nsEditorEventListeners.cpp @@ -310,7 +310,7 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr aProcessed=PR_TRUE; if (mEditor) { - mEditor->SetTextProperty(nsIEditProperty::italic); + mEditor->SetTextProperty(nsIEditProperty::i); } } @@ -336,81 +336,22 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr aProcessed=PR_TRUE; if (mEditor) { - mEditor->SetTextProperty(nsIEditProperty::bold); + mEditor->SetTextProperty(nsIEditProperty::b); } } break; + // hard-coded ChangeTextAttributes test -- underline case nsIDOMEvent::VK_U: if (PR_TRUE==ctrlKey) { aProcessed=PR_TRUE; if (mEditor) { - PRBool any, all; - mEditor->GetTextProperty(nsIEditProperty::bold, any, all); - printf("the selection has BOLD any=%d all=%d\n", any, all); + mEditor->SetTextProperty(nsIEditProperty::u); } } break; - - - //XXX: test for change and remove attribute, hard-coded to be width on first table in doc - case nsIDOMEvent::VK_TAB: - { - //XXX: should be from a factory - //XXX: should manage the refcount of txn - /* - nsAutoString attribute("width"); - nsAutoString value("400"); - - nsAutoString tableTag("TABLE"); - nsCOMPtr currentNode; - nsCOMPtr element; - if (NS_SUCCEEDED(mEditor->GetFirstNodeOfType(nsnull, tableTag, getter_AddRefs(currentNode)))) - { - if (NS_SUCCEEDED(currentNode->QueryInterface(kIDOMElementIID, getter_AddRefs(element)))) - { - nsresult result; - if (PR_TRUE==ctrlKey) // remove the attribute - result = mEditor->RemoveAttribute(element, attribute); - else // change the attribute - result = mEditor->SetAttribute(element, attribute, value); - } - } - */ - } -// aProcessed=PR_TRUE; - break; - - case nsIDOMEvent::VK_INSERT: - { - //XXX: should be from a factory - //XXX: should manage the refcount of txn - /* - nsresult result; - nsAutoString attribute("src"); - nsAutoString value("resource:/res/samples/raptor.jpg"); - - nsAutoString imgTag("HR"); - nsAutoString bodyTag("BODY"); - nsCOMPtr currentNode; - result = mEditor->GetFirstNodeOfType(nsnull, bodyTag, getter_AddRefs(currentNode)); - if (NS_SUCCEEDED(result)) - { - PRInt32 position; - if (PR_TRUE==ctrlKey) - position=CreateElementTxn::eAppend; - else - position=0; - result = mEditor->CreateNode(imgTag, currentNode, position); - } - mEditor->InsertNode(nsnull, nsnull, 0); - */ - } - aProcessed=PR_TRUE; - break; - } } return NS_OK; diff --git a/mozilla/editor/base/nsIEditProperty.h b/mozilla/editor/base/nsIEditProperty.h index cf21b7ad0bb..e7180bd7af7 100644 --- a/mozilla/editor/base/nsIEditProperty.h +++ b/mozilla/editor/base/nsIEditProperty.h @@ -44,8 +44,33 @@ public: /* we're still trying to decide how edit atoms will work. Until then, use these */ // XXX: fix ASAP! - static nsIAtom *bold; - static nsIAtom *italic; + // tags + static nsIAtom *a; + static nsIAtom *b; + static nsIAtom *big; + static nsIAtom *font; + static nsIAtom *i; + static nsIAtom *span; + static nsIAtom *small; + static nsIAtom *strike; + static nsIAtom *sub; + static nsIAtom *sup; + static nsIAtom *tt; + static nsIAtom *u; + +/* from HTML 3.2 spec +TT teletype or monospaced text +I italic text style +B bold text style +U underlined text style +STRIKE strike-through text style +BIG places text in a large font +SMALL places text in a small font +SUB places text in subscript style +SUP places text in superscript style +*/ + + // XXX: end temp code }; extern nsresult NS_NewEditProperty(nsIEditProperty **aResult); diff --git a/mozilla/editor/base/nsTextEditRules.cpp b/mozilla/editor/base/nsTextEditRules.cpp index 4ad59e1a040..c2d48234dad 100644 --- a/mozilla/editor/base/nsTextEditRules.cpp +++ b/mozilla/editor/base/nsTextEditRules.cpp @@ -17,7 +17,7 @@ */ #include "nsTextEditRules.h" -#include "nsEditor.h" +#include "nsTextEditor.h" #include "PlaceholderTxn.h" #include "InsertTextTxn.h" #include "nsCOMPtr.h" @@ -29,6 +29,7 @@ #include "nsIDOMCharacterData.h" #include "nsIEnumerator.h" #include "nsIContent.h" +#include "nsIEditProperty.h" const static char* kMOZEditorBogusNodeAttr="MOZ_EDITOR_BOGUS_NODE"; const static char* kMOZEditorBogusNodeValue="TRUE"; @@ -118,7 +119,7 @@ nsTextEditRules::~nsTextEditRules() NS_IMETHODIMP -nsTextEditRules::Init(nsEditor *aEditor) +nsTextEditRules::Init(nsTextEditor *aEditor) { // null aNextRule is ok if (!aEditor) { return NS_ERROR_NULL_POINTER; } @@ -165,9 +166,10 @@ nsTextEditRules::DidInsert(nsIDOMSelection *aSelection, nsresult aResult) NS_IMETHODIMP nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection, - const nsString& aInputString, - PRBool *aCancel, - nsString& aOutputString, + const nsString &aInputString, + PRBool *aCancel, + nsString &aOutputString, + TypeInState &aTypeInState, PlaceholderTxn **aTxn) { if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } @@ -175,7 +177,8 @@ nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection, *aCancel = PR_FALSE; // by default, we insert what we're told to insert aOutputString = aInputString; - if (mBogusNode) + TypeInState typeInState = aTypeInState; // remember the initial type-in state + if (mBogusNode || (PR_TRUE==typeInState.IsAnySet())) { nsresult result = TransactionFactory::GetNewTransaction(kPlaceholderTxnIID, (EditTxn **)aTxn); if (NS_FAILED(result)) { return result; } @@ -184,6 +187,13 @@ nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection, mEditor->Do(*aTxn); } nsresult result = WillInsert(aSelection, aCancel); + if (NS_SUCCEEDED(result) && (PR_FALSE==*aCancel)) + { + if (PR_TRUE==typeInState.IsAnySet()) + { // for every property that is set, insert a new inline style node + result = CreateStyleForInsertText(aSelection, typeInState); + } + } return result; } @@ -195,6 +205,128 @@ nsTextEditRules::DidInsertText(nsIDOMSelection *aSelection, return DidInsert(aSelection, aResult); } +NS_IMETHODIMP +nsTextEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, TypeInState &aTypeInState) +{ + // private method, we know aSelection is not null, and that it is collapsed + NS_ASSERTION(nsnull!=aSelection, "bad selection"); + + // We know at least one style is set and we're about to insert at least one character. + // If the selection is in a text node, split the node (even if we're at the beginning or end) + // then put the text node inside new inline style parents. + // Otherwise, create the text node and the new inline style parents. + nsCOMPtranchor; + PRInt32 offset; + nsresult result = aSelection->GetAnchorNodeAndOffset(getter_AddRefs(anchor), &offset); + if ((NS_SUCCEEDED(result)) && anchor) + { + nsCOMPtranchorAsText; + anchorAsText = do_QueryInterface(anchor); + if (anchorAsText) + { + nsCOMPtrnewTextNode; + // create an empty text node by splitting the selected text node according to offset + if (0==offset) + { + result = mEditor->SplitNode(anchorAsText, offset, getter_AddRefs(newTextNode)); + } + else + { + PRUint32 length; + anchorAsText->GetLength(&length); + if (length==offset) + { + // newTextNode will be the left node + result = mEditor->SplitNode(anchorAsText, offset, getter_AddRefs(newTextNode)); + // but we want the right node in this case + newTextNode = do_QueryInterface(anchor); + } + else + { + // splitting anchor twice sets newTextNode as an empty text node between + // two halves of the original text node + result = mEditor->SplitNode(anchorAsText, offset, getter_AddRefs(newTextNode)); + result = mEditor->SplitNode(anchorAsText, 0, getter_AddRefs(newTextNode)); + } + } + // now we have the new text node we are going to insert into. + // create style nodes or move it up the content hierarchy as needed. + if ((NS_SUCCEEDED(result)) && newTextNode) + { + if (aTypeInState.IsSet(NS_TYPEINSTATE_BOLD)) + { + if (PR_TRUE==aTypeInState.GetBold()) + { // make the next char bold + nsCOMPtrparent; + newTextNode->GetParentNode(getter_AddRefs(parent)); + PRInt32 offsetInParent; + nsIEditorSupport::GetChildOffset(newTextNode, parent, offsetInParent); + nsAutoString tag; + nsIEditProperty::b->ToString(tag); + nsCOMPtrnewStyleNode; + result = mEditor->CreateNode(tag, parent, offsetInParent, getter_AddRefs(newStyleNode)); + result = mEditor->DeleteNode(newTextNode); + result = mEditor->InsertNode(newTextNode, newStyleNode, 0); + aSelection->Collapse(newTextNode, 0); + } + else + { + printf("not yet implemented, make unbold in a bold context\n"); + } + } + } + } + else + { + printf("not yet implemented. selection is not text.\n"); + } + } + else // we have no selection, so insert a style tag in the body + { + nsCOMPtrdoc; + mEditor->GetDocument(getter_AddRefs(doc)); + nsCOMPtrnodeList; + nsAutoString bodyTag = "body"; + 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!"); + nsCOMPtrbodyNode; + result = nodeList->Item(0, getter_AddRefs(bodyNode)); + if ((NS_SUCCEEDED(result)) && bodyNode) + { // now we've got the body tag. insert the style tag + if (aTypeInState.IsSet(NS_TYPEINSTATE_BOLD)) + { + if (PR_TRUE==aTypeInState.GetBold()) + { // make the next char bold + nsAutoString tag; + nsIEditProperty::b->ToString(tag); + nsCOMPtrnewStyleNode; + nsCOMPtrnewTextNode; + result = mEditor->CreateNode(tag, bodyNode, 0, getter_AddRefs(newStyleNode)); + result = mEditor->CreateNode(nsIEditor::GetTextNodeTag(), newStyleNode, 0, getter_AddRefs(newTextNode)); + aSelection->Collapse(newTextNode, 0); + } + } + } + } + } + return result; +} + + +/* +NS_IMETHODIMP +nsTextEditRules::GetInsertBreakTag(nsIAtom **aTag) +{ + if (!aTag) { return NS_ERROR_NULL_POINTER; } + *aTag = NS_NewAtom("BR"); + return NS_OK; +} +*/ + NS_IMETHODIMP nsTextEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, PRBool *aCancel) { @@ -325,10 +457,6 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection, nsresult aResul nsCOMPtr parentNode; selectedNode->GetParentNode(getter_AddRefs(parentNode)); result = mEditor->JoinNodes(siblingNode, selectedNode, parentNode); - // set selection to point between the end of siblingNode and start of selectedNode - aSelection->Collapse(siblingNode, siblingLength); - selectedNode = do_QueryInterface(siblingNode); // subsequent code relies on selectedNode - // being the real node in the DOM tree } } selectedNode->GetNextSibling(getter_AddRefs(siblingNode)); diff --git a/mozilla/editor/base/nsTextEditRules.h b/mozilla/editor/base/nsTextEditRules.h index 8aecebc37c4..afda44444c6 100644 --- a/mozilla/editor/base/nsTextEditRules.h +++ b/mozilla/editor/base/nsTextEditRules.h @@ -22,8 +22,9 @@ #include "nsIEditor.h" #include "nsCOMPtr.h" #include "nsIDOMNode.h" +#include "TypeInState.h" -class nsEditor; +class nsTextEditor; class PlaceholderTxn; /** Object that encapsulates HTML text-specific editing rules. @@ -44,17 +45,19 @@ public: nsTextEditRules(); virtual ~nsTextEditRules(); - NS_IMETHOD Init(nsEditor *aEditor); + NS_IMETHOD Init(nsTextEditor *aEditor); NS_IMETHOD WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel); NS_IMETHOD DidInsertBreak(nsIDOMSelection *aSelection, nsresult aResult); - NS_IMETHOD WillInsertText(nsIDOMSelection *aSelection, - const nsString& aInputString, - PRBool *aCancel, - nsString& aOutputString, - PlaceholderTxn ** aTxn); + NS_IMETHOD WillInsertText(nsIDOMSelection *aSelection, + const nsString &aInputString, + PRBool *aCancel, + nsString &aOutputString, + TypeInState &aTypeInState, + PlaceholderTxn **aTxn); NS_IMETHOD DidInsertText(nsIDOMSelection *aSelection, const nsString& aStringToInsert, nsresult aResult); + NS_IMETHOD CreateStyleForInsertText(nsIDOMSelection *aSelection, TypeInState &aTypeInState); NS_IMETHOD WillInsert(nsIDOMSelection *aSelection, PRBool *aCancel); NS_IMETHOD DidInsert(nsIDOMSelection *aSelection, nsresult aResult); @@ -74,7 +77,7 @@ protected: static PRBool NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag); static PRBool IsEditable(nsIDOMNode *aNode); - nsEditor *mEditor; // note that we do not refcount the editor + nsTextEditor *mEditor; // note that we do not refcount the editor nsCOMPtr mBogusNode; // magic node acts as placeholder in empty doc }; diff --git a/mozilla/editor/base/nsTextEditor.cpp b/mozilla/editor/base/nsTextEditor.cpp index 2e03823fdce..2b0f1742795 100644 --- a/mozilla/editor/base/nsTextEditor.cpp +++ b/mozilla/editor/base/nsTextEditor.cpp @@ -20,6 +20,7 @@ #include "nsIEditorSupport.h" #include "nsEditorEventListeners.h" #include "nsIEditProperty.h" +#include "nsEditProperty.h" // temporary, to get html atoms #include "nsIStreamListener.h" #include "nsIParser.h" @@ -40,6 +41,7 @@ #include "nsIDOMRange.h" #include "nsIDOMNodeList.h" #include "nsIDOMCharacterData.h" +#include "nsIDOMElement.h" #include "nsIDOMTextListener.h" #include "nsEditorCID.h" #include "nsISupportsArray.h" @@ -82,22 +84,64 @@ static NS_DEFINE_IID(kIDOMMouseListenerIID, NS_IDOMMOUSELISTENER_IID); static NS_DEFINE_IID(kIDOMKeyListenerIID, NS_IDOMKEYLISTENER_IID); static NS_DEFINE_IID(kIDOMTextListenerIID, NS_IDOMTEXTLISTENER_IID); static NS_DEFINE_IID(kIDOMDragListenerIID, NS_IDOMDRAGLISTENER_IID); -static NS_DEFINE_IID(kIEditPropertyIID, NS_IEDITPROPERTY_IID); +static NS_DEFINE_IID(kIDOMSelectionListenerIID, NS_IDOMSELECTIONLISTENER_IID); -static NS_DEFINE_CID(kEditorCID, NS_EDITOR_CID); -static NS_DEFINE_IID(kIEditorIID, NS_IEDITOR_IID); -static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); -static NS_DEFINE_IID(kITextEditorIID, NS_ITEXTEDITOR_IID); -static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID); +static NS_DEFINE_IID(kIEditPropertyIID, NS_IEDITPROPERTY_IID); +static NS_DEFINE_CID(kEditorCID, NS_EDITOR_CID); +static NS_DEFINE_IID(kIEditorIID, NS_IEDITOR_IID); +static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); +static NS_DEFINE_IID(kITextEditorIID, NS_ITEXTEDITOR_IID); +static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID); -static NS_DEFINE_IID(kIContentIteratorIID, NS_ICONTENTITERTOR_IID); -static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); +static NS_DEFINE_IID(kIContentIteratorIID, NS_ICONTENTITERTOR_IID); +static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID); + + +/* ---------- TypeInState implementation ---------- */ +// most methods are defined inline in TypeInState.h + +NS_IMPL_ADDREF(TypeInState) + +NS_IMPL_RELEASE(TypeInState) + +NS_IMETHODIMP +TypeInState::QueryInterface(REFNSIID aIID, void** aInstancePtr) +{ + if (nsnull == aInstancePtr) { + return NS_ERROR_NULL_POINTER; + } + if (aIID.Equals(kISupportsIID)) { + *aInstancePtr = (void*)(nsISupports*)this; + NS_ADDREF_THIS(); + return NS_OK; + } + if (aIID.Equals(kIDOMSelectionListenerIID)) { + *aInstancePtr = (void*)(nsIDOMSelectionListener*)this; + NS_ADDREF_THIS(); + return NS_OK; + } + return NS_NOINTERFACE; +} + +TypeInState::~TypeInState() +{ +}; + +NS_IMETHODIMP TypeInState::NotifySelectionChanged() +{ + Reset(); + return NS_OK; +}; + + +/* ---------- nsTextEditor implementation ---------- */ nsTextEditor::nsTextEditor() { // Done in nsEditor // NS_INIT_REFCNT(); mRules = nsnull; + nsEditProperty::InstanceInit(); } nsTextEditor::~nsTextEditor() @@ -134,6 +178,7 @@ nsTextEditor::~nsTextEditor() if (mRules) { delete mRules; } + nsEditProperty::InstanceShutdown(); } // Adds appropriate AddRef, Release, and QueryInterface methods for derived class @@ -172,8 +217,18 @@ NS_IMETHODIMP nsTextEditor::Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell) { // Init the base editor result = nsEditor::Init(aDoc, aPresShell); - if (NS_OK != result) - return result; + if (NS_OK != result) { return result; } + + nsCOMPtrselection; + result = nsEditor::GetSelection(getter_AddRefs(selection)); + if (NS_OK != result) { return result; } + if (selection) { + nsCOMPtrlistener; + listener = do_QueryInterface(&mTypeInState); + if (listener) { + selection->AddSelectionListener(listener); + } + } // Init the rules system InitRules(); @@ -232,7 +287,6 @@ NS_IMETHODIMP nsTextEditor::Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell) return result; } - void nsTextEditor::InitRules() { // instantiate the rules for this text editor @@ -242,8 +296,6 @@ void nsTextEditor::InitRules() } -// this is a total hack for now. We don't yet have a way of getting the style properties -// of the current selection, so we can't do anything useful here except show off a little. NS_IMETHODIMP nsTextEditor::SetTextProperty(nsIAtom *aProperty) { if (!aProperty) @@ -254,72 +306,93 @@ NS_IMETHODIMP nsTextEditor::SetTextProperty(nsIAtom *aProperty) result = nsEditor::GetSelection(getter_AddRefs(selection)); if ((NS_SUCCEEDED(result)) && selection) { - nsEditor::BeginTransaction(); - nsCOMPtr enumerator; - enumerator = do_QueryInterface(selection, &result); - if ((NS_SUCCEEDED(result)) && enumerator) + PRBool isCollapsed; + selection->IsCollapsed(&isCollapsed); + if (PR_TRUE==isCollapsed) { - enumerator->First(); - nsISupports *currentItem; - result = enumerator->CurrentItem(¤tItem); - if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem)) + // manipulating text attributes on a collapsed selection only sets state for the next text insertion + SetTypeInStateForProperty(mTypeInState, aProperty); + } + else + { + nsEditor::BeginTransaction(); + nsCOMPtr enumerator; + enumerator = do_QueryInterface(selection, &result); + if ((NS_SUCCEEDED(result)) && enumerator) { - nsCOMPtr range( do_QueryInterface(currentItem) ); - nsCOMPtrcommonParent; - result = range->GetCommonParent(getter_AddRefs(commonParent)); - if ((NS_SUCCEEDED(result)) && commonParent) + enumerator->First(); + nsISupports *currentItem; + result = enumerator->CurrentItem(¤tItem); + if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem)) { - PRInt32 startOffset, endOffset; - range->GetStartOffset(&startOffset); - range->GetEndOffset(&endOffset); - nsCOMPtr startParent; nsCOMPtr endParent; - range->GetStartParent(getter_AddRefs(startParent)); - range->GetEndParent(getter_AddRefs(endParent)); - if (startParent.get()==endParent.get()) - { // the range is entirely contained within a single text node - // commonParent==aStartParent, so get the "real" parent of the selection - startParent->GetParentNode(getter_AddRefs(commonParent)); - result = SetTextPropertiesForNode(startParent, commonParent, - startOffset, endOffset, - aProperty); - } - else + nsCOMPtr range( do_QueryInterface(currentItem) ); + nsCOMPtrcommonParent; + result = range->GetCommonParent(getter_AddRefs(commonParent)); + if ((NS_SUCCEEDED(result)) && commonParent) { - nsCOMPtr startGrandParent; - startParent->GetParentNode(getter_AddRefs(startGrandParent)); - nsCOMPtr endGrandParent; - endParent->GetParentNode(getter_AddRefs(endGrandParent)); - if (NS_SUCCEEDED(result)) + PRInt32 startOffset, endOffset; + range->GetStartOffset(&startOffset); + range->GetEndOffset(&endOffset); + nsCOMPtr startParent; nsCOMPtr endParent; + range->GetStartParent(getter_AddRefs(startParent)); + range->GetEndParent(getter_AddRefs(endParent)); + if (startParent.get()==endParent.get()) + { // the range is entirely contained within a single text node + // commonParent==aStartParent, so get the "real" parent of the selection + startParent->GetParentNode(getter_AddRefs(commonParent)); + result = SetTextPropertiesForNode(startParent, commonParent, + startOffset, endOffset, + aProperty); + } + else { - if (endGrandParent.get()==startGrandParent.get()) - { // the range is between 2 nodes that have a common (immediate) grandparent - result = SetTextPropertiesForNodesWithSameParent(startParent,startOffset, - endParent, endOffset, - commonParent, - aProperty); - } - else - { // the range is between 2 nodes that have no simple relationship - result = SetTextPropertiesForNodeWithDifferentParents(range, - startParent,startOffset, - endParent, endOffset, - commonParent, - aProperty); + nsCOMPtr startGrandParent; + startParent->GetParentNode(getter_AddRefs(startGrandParent)); + nsCOMPtr endGrandParent; + endParent->GetParentNode(getter_AddRefs(endGrandParent)); + if (NS_SUCCEEDED(result)) + { + PRBool canCollapseStyleNode = PR_FALSE; + if (endGrandParent.get()==startGrandParent.get()) + { + result = IntermediateNodesAreInline(range, startParent, startOffset, + endParent, endOffset, + startGrandParent, canCollapseStyleNode); + } + if (NS_SUCCEEDED(result)) + { + if (PR_TRUE==canCollapseStyleNode) + { // the range is between 2 nodes that have a common (immediate) grandparent, + // and any intermediate nodes are just inline style nodes + result = SetTextPropertiesForNodesWithSameParent(startParent,startOffset, + endParent, endOffset, + commonParent, + aProperty); + } + else + { // the range is between 2 nodes that have no simple relationship + result = SetTextPropertiesForNodeWithDifferentParents(range, + startParent,startOffset, + endParent, endOffset, + commonParent, + aProperty); + } + } } } - } - if (NS_SUCCEEDED(result)) - { // compute a range for the selection - // don't want to actually do anything with selection, because - // we are still iterating through it. Just want to create and remember - // an nsIDOMRange, and later add the range to the selection after clearing it. - // XXX: I'm blocked here because nsIDOMSelection doesn't provide a mechanism - // for setting a compound selection yet. + if (NS_SUCCEEDED(result)) + { // compute a range for the selection + // don't want to actually do anything with selection, because + // we are still iterating through it. Just want to create and remember + // an nsIDOMRange, and later add the range to the selection after clearing it. + // XXX: I'm blocked here because nsIDOMSelection doesn't provide a mechanism + // for setting a compound selection yet. + } } } } + nsEditor::EndTransaction(); } - nsEditor::EndTransaction(); if (NS_SUCCEEDED(result)) { // set the selection // XXX: can't do anything until I can create ranges @@ -352,8 +425,8 @@ NS_IMETHODIMP nsTextEditor::GetTextProperty(nsIAtom *aProperty, PRBool &aAny, PR nsCOMPtr range( do_QueryInterface(currentItem) ); nsCOMPtr iter; result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, - kIContentIteratorIID, - getter_AddRefs(iter)); + kIContentIteratorIID, + getter_AddRefs(iter)); if ((NS_SUCCEEDED(result)) && iter) { iter->Init(range); @@ -412,17 +485,52 @@ void nsTextEditor::IsTextStyleSet(nsIStyleContext *aSC, if (aSC && aProperty) { nsStyleFont* font = (nsStyleFont*)aSC->GetStyleData(eStyleStruct_Font); - if (nsIEditProperty::italic==aProperty) + if (nsIEditProperty::i==aProperty) { aIsSet = PRBool(font->mFont.style & NS_FONT_STYLE_ITALIC); } - else if (nsIEditProperty::bold==aProperty) + else if (nsIEditProperty::b==aProperty) { // XXX: check this logic with Peter aIsSet = PRBool(font->mFont.weight > NS_FONT_WEIGHT_NORMAL); } } } +void nsTextEditor::IsTextPropertySetByContent(nsIDOMNode *aNode, + nsIAtom *aProperty, + PRBool &aIsSet) const +{ + nsresult result; + aIsSet = PR_FALSE; + nsAutoString propName; + aProperty->ToString(propName); + nsCOMPtrparent; + result = aNode->GetParentNode(getter_AddRefs(parent)); + while (NS_SUCCEEDED(result) && parent) + { + nsCOMPtrelement; + element = do_QueryInterface(parent); + if (element) + { + nsString tag; + element->GetTagName(tag); + if (propName.Equals(tag)) + { + aIsSet = PR_TRUE; + break; + } + } + nsCOMPtrtemp; + result = parent->GetParentNode(getter_AddRefs(temp)); + if (NS_SUCCEEDED(result) && temp) { + parent = do_QueryInterface(temp); + } + else { + parent = do_QueryInterface(nsnull); + } + } +} + NS_IMETHODIMP nsTextEditor::RemoveTextProperty(nsIAtom *aProperty) { @@ -475,7 +583,7 @@ NS_IMETHODIMP nsTextEditor::InsertText(const nsString& aStringToInsert) nsAutoString stringToInsert; PlaceholderTxn *placeholderTxn=nsnull; nsresult result = mRules->WillInsertText(selection, aStringToInsert, &cancel, stringToInsert, - &placeholderTxn); + mTypeInState, &placeholderTxn); if ((PR_FALSE==cancel) && (NS_SUCCEEDED(result))) { result = nsEditor::InsertText(stringToInsert); @@ -496,7 +604,6 @@ NS_IMETHODIMP nsTextEditor::InsertBreak() // For plainttext just pass newlines through nsAutoString key; key += '\n'; - return InsertText(key); } @@ -658,7 +765,7 @@ static void WriteFromOstrstream(ostrstream& aIn, nsString& aOutputString) // in ostrstreams if you call the str() function // then you are responsible for deleting the string - delete[] strData; + delete [] strData; } #endif @@ -805,30 +912,33 @@ NS_IMETHODIMP nsTextEditor::SetTextPropertiesForNode(nsIDOMNode *aNode, nodeAsChar = do_QueryInterface(aNode); if (!nodeAsChar) return NS_ERROR_FAILURE; - PRUint32 count; - nodeAsChar->GetLength(&count); - nsCOMPtrnewTextNode; // this will be the text node we move into the new style node - if (aStartOffset!=0) + PRBool textPropertySet; + IsTextPropertySetByContent(aNode, aPropName, textPropertySet); + if (PR_FALSE==textPropertySet) { - result = nsEditor::SplitNode(aNode, aStartOffset, getter_AddRefs(newTextNode)); - } - if (NS_SUCCEEDED(result)) - { - if (aEndOffset!=(PRInt32)count) + PRUint32 count; + nodeAsChar->GetLength(&count); + + nsCOMPtrnewTextNode; // this will be the text node we move into the new style node + if (aStartOffset!=0) { - result = nsEditor::SplitNode(aNode, aEndOffset-aStartOffset, getter_AddRefs(newTextNode)); - } - else - { - newTextNode = do_QueryInterface(aNode); + result = nsEditor::SplitNode(aNode, aStartOffset, getter_AddRefs(newTextNode)); } if (NS_SUCCEEDED(result)) { - nsAutoString tag; - result = SetTagFromProperty(tag, aPropName); + if (aEndOffset!=(PRInt32)count) + { + result = nsEditor::SplitNode(aNode, aEndOffset-aStartOffset, getter_AddRefs(newTextNode)); + } + else + { + newTextNode = do_QueryInterface(aNode); + } if (NS_SUCCEEDED(result)) { + nsAutoString tag; + aPropName->ToString(tag); PRInt32 offsetInParent; result = nsIEditorSupport::GetChildOffset(aNode, aParent, offsetInParent); if (NS_SUCCEEDED(result)) @@ -849,6 +959,103 @@ NS_IMETHODIMP nsTextEditor::SetTextPropertiesForNode(nsIDOMNode *aNode, return result; } +// XXX: WRITE ME! +// We can do this content based or style based +// content-based has the advantage of being "correct" regardless of the style sheet +// it has the disadvantage of not supporting nonHTML tags +NS_IMETHODIMP nsTextEditor::IsNodeInline(nsIDOMNode *aNode, PRBool &aIsInline) const +{ + // this is a content-based implementation + if (!aNode) { return NS_ERROR_NULL_POINTER; } + + nsresult result; + aIsInline = PR_FALSE; + nsCOMPtrelement; + element = do_QueryInterface(aNode); + if (element) + { + nsAutoString tag; + result = element->GetTagName(tag); + if (NS_SUCCEEDED(result)) + { + nsIAtom *tagAtom = NS_NewAtom(tag); + if (!tagAtom) { return NS_ERROR_NULL_POINTER; } + if (tagAtom==nsIEditProperty::a || + tagAtom==nsIEditProperty::b || + tagAtom==nsIEditProperty::font || + tagAtom==nsIEditProperty::i || + tagAtom==nsIEditProperty::span) + { + aIsInline = PR_TRUE; + } + } + } + return NS_OK; +} + +NS_IMETHODIMP +nsTextEditor::IntermediateNodesAreInline(nsIDOMRange *aRange, + nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aParent, + PRBool &aResult) const +{ + aResult = PR_TRUE; // init out param. we assume the condition is true unless we find a node that violates it + if (!aStartNode || !aEndNode || !aParent) { return NS_ERROR_NULL_POINTER; } + + nsCOMPtriter; + nsresult result; + result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, + kIContentIteratorIID, getter_AddRefs(iter)); + //XXX: maybe CreateInstance is expensive, and I should keep around a static iter? + // as long as this method can't be called recursively or re-entrantly! + + if ((NS_SUCCEEDED(result)) && iter) + { + nsCOMPtrstartContent; + startContent = do_QueryInterface(aStartNode); + nsCOMPtrendContent; + endContent = do_QueryInterface(aEndNode); + if (startContent && endContent) + { + iter->Init(aRange); + nsCOMPtr content; + iter->CurrentNode(getter_AddRefs(content)); + while (NS_COMFALSE == iter->IsDone()) + { + if ((content.get() != startContent.get()) && + (content.get() != endContent.get())) + { + nsCOMPtrcurrentNode; + currentNode = do_QueryInterface(content); + PRBool isInline=PR_FALSE; + IsNodeInline(currentNode, isInline); + if (PR_FALSE==isInline) + { + nsCOMPtrnodeAsText; + nodeAsText = do_QueryInterface(currentNode); + if (!nodeAsText) // text nodes don't count in this check, so ignore them + { + aResult = PR_FALSE; + break; + } + } + } + /* do not check result here, and especially do not return the result code. + * we rely on iter->IsDone to tell us when the iteration is complete + */ + iter->Next(); + iter->CurrentNode(getter_AddRefs(content)); + } + } + } + + return result; +} + +/* this should only get called if the only intervening nodes are inline style nodes */ NS_IMETHODIMP nsTextEditor::SetTextPropertiesForNodesWithSameParent(nsIDOMNode *aStartNode, PRInt32 aStartOffset, @@ -858,40 +1065,43 @@ nsTextEditor::SetTextPropertiesForNodesWithSameParent(nsIDOMNode *aStartNode, nsIAtom *aPropName) { nsresult result=NS_OK; - nsCOMPtrnewLeftTextNode; // this will be the middle text node - if (0!=aStartOffset) { - result = nsEditor::SplitNode(aStartNode, aStartOffset, getter_AddRefs(newLeftTextNode)); - } - if (NS_SUCCEEDED(result)) + PRBool textPropertySet; + IsTextPropertySetByContent(aStartNode, aPropName, textPropertySet); + if (PR_FALSE==textPropertySet) { - nsCOMPtrendNodeAsChar; - endNodeAsChar = do_QueryInterface(aEndNode); - if (!endNodeAsChar) - return NS_ERROR_FAILURE; - PRUint32 count; - endNodeAsChar->GetLength(&count); - nsCOMPtrnewRightTextNode; // this will be the middle text node - if ((PRInt32)count!=aEndOffset) { - result = nsEditor::SplitNode(aEndNode, aEndOffset, getter_AddRefs(newRightTextNode)); - } - else { - newRightTextNode = do_QueryInterface(aEndNode); + nsCOMPtrnewLeftTextNode; // this will be the middle text node + if (0!=aStartOffset) { + result = nsEditor::SplitNode(aStartNode, aStartOffset, getter_AddRefs(newLeftTextNode)); } if (NS_SUCCEEDED(result)) { - PRInt32 offsetInParent; - if (newLeftTextNode) { - result = nsIEditorSupport::GetChildOffset(newLeftTextNode, aParent, offsetInParent); + nsCOMPtrendNodeAsChar; + endNodeAsChar = do_QueryInterface(aEndNode); + if (!endNodeAsChar) + return NS_ERROR_FAILURE; + PRUint32 count; + endNodeAsChar->GetLength(&count); + nsCOMPtrnewRightTextNode; // this will be the middle text node + if ((PRInt32)count!=aEndOffset) { + result = nsEditor::SplitNode(aEndNode, aEndOffset, getter_AddRefs(newRightTextNode)); } else { - offsetInParent = -1; // relies on +1 below in call to CreateNode + newRightTextNode = do_QueryInterface(aEndNode); } if (NS_SUCCEEDED(result)) { - nsAutoString tag; - result = SetTagFromProperty(tag, aPropName); + PRInt32 offsetInParent; + if (newLeftTextNode) { + result = nsIEditorSupport::GetChildOffset(newLeftTextNode, aParent, offsetInParent); + } + else { + offsetInParent = -1; // relies on +1 below in call to CreateNode + } if (NS_SUCCEEDED(result)) - { // create the new style node, which will be the new parent for the selected nodes + { + nsAutoString tag; + aPropName->ToString(tag); + // create the new style node, which will be the new parent for the selected nodes nsCOMPtrnewStyleNode; result = nsEditor::CreateNode(tag, aParent, offsetInParent+1, getter_AddRefs(newStyleNode)); if (NS_SUCCEEDED(result)) @@ -944,6 +1154,13 @@ nsTextEditor::SetTextPropertiesForNodesWithSameParent(nsIDOMNode *aStartNode, return result; } +/* this wraps every selected text node in a new inline style node if needed + the text nodes are treated as being unique -- each needs it's own style node + if the style is not already present. + each action has immediate effect on the content tree and resolved style, so + doing outermost text nodes first removes the need for interior style nodes in some cases. + XXX: need to code test to see if new style node is needed +*/ NS_IMETHODIMP nsTextEditor::SetTextPropertiesForNodeWithDifferentParents(nsIDOMRange *aRange, nsIDOMNode *aStartNode, @@ -962,6 +1179,71 @@ nsTextEditor::SetTextPropertiesForNodeWithDifferentParents(nsIDOMRange *aRange, if (NS_FAILED(result)) { return result; } + + // create style nodes for all the content between the start and end nodes + nsCOMPtriter; + result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, + kIContentIteratorIID, getter_AddRefs(iter)); + if ((NS_SUCCEEDED(result)) && iter) + { + nsCOMPtrstartContent; + startContent = do_QueryInterface(aStartNode); + nsCOMPtrendContent; + endContent = do_QueryInterface(aEndNode); + if (startContent && endContent) + { + iter->Init(aRange); + nsCOMPtr content; + iter->CurrentNode(getter_AddRefs(content)); + nsAutoString tag; + aPropName->ToString(tag); + while (NS_COMFALSE == iter->IsDone()) + { + if ((content.get() != startContent.get()) && + (content.get() != endContent.get())) + { + nsCOMPtrcharNode; + charNode = do_QueryInterface(content); + if (charNode) + { + // only want to wrap the text node in a new style node if it doesn't already have that style + nsCOMPtrnode; + node = do_QueryInterface(content); + PRBool textPropertySet; + IsTextPropertySetByContent(node, aPropName, textPropertySet); + if (PR_FALSE==textPropertySet) + { + nsCOMPtrparent; + charNode->GetParentNode(getter_AddRefs(parent)); + if (!parent) { + return NS_ERROR_NULL_POINTER; + } + nsCOMPtrparentContent; + parentContent = do_QueryInterface(parent); + + PRInt32 offsetInParent; + parentContent->IndexOf(content, offsetInParent); + + nsCOMPtrnewStyleNode; + result = nsEditor::CreateNode(tag, parent, offsetInParent, getter_AddRefs(newStyleNode)); + if (NS_SUCCEEDED(result) && newStyleNode) { + nsCOMPtrcontentNode; + contentNode = do_QueryInterface(content); + result = nsEditor::DeleteNode(contentNode); + if (NS_SUCCEEDED(result)) { + result = nsEditor::InsertNode(contentNode, newStyleNode, 0); + } + } + } + } + } + // note we don't check the result, we just rely on iter->IsDone + iter->Next(); + result = iter->CurrentNode(getter_AddRefs(content)); + } + } + } + nsCOMPtrnodeAsChar; nodeAsChar = do_QueryInterface(aStartNode); if (!nodeAsChar) @@ -981,86 +1263,45 @@ nsTextEditor::SetTextPropertiesForNodeWithDifferentParents(nsIDOMRange *aRange, nodeAsChar->GetLength(&count); result = SetTextPropertiesForNode(aEndNode, parent, 0, aEndOffset, aPropName); - // create style nodes for all the content between the start and end nodes - nsCOMPtriter; - result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull, - kIContentIteratorIID, getter_AddRefs(iter)); - if ((NS_SUCCEEDED(result)) && iter) - { - nsCOMPtrstartContent; - startContent = do_QueryInterface(aStartNode); - nsCOMPtrendContent; - endContent = do_QueryInterface(aEndNode); - if (startContent && endContent) - { - iter->Init(aRange); - nsCOMPtr content; - iter->CurrentNode(getter_AddRefs(content)); - nsAutoString tag; - result = SetTagFromProperty(tag, aPropName); - if (NS_FAILED(result)) { - return result; - } - while (NS_COMFALSE == iter->IsDone()) - { - if ((content.get() != startContent.get()) && - (content.get() != endContent.get())) - { - nsCOMPtrcharNode; - charNode = do_QueryInterface(content); - if (charNode) - { - // only want to wrap the text node in a new style node if it doesn't already have that style - nsCOMPtrparent; - charNode->GetParentNode(getter_AddRefs(parent)); - if (!parent) { - return NS_ERROR_NULL_POINTER; - } - nsCOMPtrparentContent; - parentContent = do_QueryInterface(parent); - - PRInt32 offsetInParent; - parentContent->IndexOf(content, offsetInParent); - - nsCOMPtrnewStyleNode; - result = nsEditor::CreateNode(tag, parent, offsetInParent, getter_AddRefs(newStyleNode)); - if (NS_SUCCEEDED(result) && newStyleNode) { - nsCOMPtrcontentNode; - contentNode = do_QueryInterface(content); - result = nsEditor::DeleteNode(contentNode); - if (NS_SUCCEEDED(result)) { - result = nsEditor::InsertNode(contentNode, newStyleNode, 0); - } - } - } - } - iter->Next(); - result = iter->CurrentNode(getter_AddRefs(content)); - } - } - } - - return result; } - - -NS_IMETHODIMP -nsTextEditor::SetTagFromProperty(nsAutoString &aTag, nsIAtom *aPropName) const +NS_IMETHODIMP +nsTextEditor::SetTypeInStateForProperty(TypeInState &aTypeInState, nsIAtom *aPropName) { if (!aPropName) { return NS_ERROR_NULL_POINTER; } - if (nsIEditProperty::bold==aPropName) { - aTag = "b"; + if (nsIEditProperty::b==aPropName) + { + if (PR_TRUE==aTypeInState.IsSet(NS_TYPEINSTATE_BOLD)) + { // toggle currently set boldness + aTypeInState.UnSet(NS_TYPEINSTATE_BOLD); + } + else + { // get the current style and set boldness to the opposite of the current state + PRBool any = PR_FALSE; + PRBool all = PR_FALSE; + GetTextProperty(aPropName, any, all); // operates on current selection + aTypeInState.SetBold(!any); + } } - else if (nsIEditProperty::italic==aPropName) { - aTag = "i"; + else if (nsIEditProperty::i==aPropName) + { + if (PR_TRUE==aTypeInState.IsSet(NS_TYPEINSTATE_ITALIC)) + { // toggle currently set italicness + aTypeInState.UnSet(NS_TYPEINSTATE_ITALIC); + } + else + { // get the current style and set boldness to the opposite of the current state + PRBool any = PR_FALSE; + PRBool all = PR_FALSE; + GetTextProperty(aPropName, any, all); // operates on current selection + aTypeInState.SetItalic(!any); + } } else { return NS_ERROR_FAILURE; } return NS_OK; } - diff --git a/mozilla/editor/base/nsTextEditor.h b/mozilla/editor/base/nsTextEditor.h index 01faa2fbf04..10e050ef46c 100644 --- a/mozilla/editor/base/nsTextEditor.h +++ b/mozilla/editor/base/nsTextEditor.h @@ -24,6 +24,7 @@ #include "nsIDOMEventListener.h" #include "nsEditor.h" #include "nsTextEditRules.h" +#include "TypeInState.h" class nsIStyleContext; class nsIDOMRange; @@ -33,7 +34,7 @@ class nsIDOMRange; * Use to edit text represented as a DOM tree. * This class is used for editing both plain text and rich text (attributed text). */ -class nsTextEditor : public nsEditor, public nsITextEditor +class nsTextEditor : public nsEditor, public nsITextEditor { public: // see nsITextEditor for documentation @@ -88,6 +89,7 @@ public: NS_IMETHOD OutputText(nsString& aOutputString); NS_IMETHOD OutputHTML(nsString& aOutputString); + protected: // rules initialization @@ -96,9 +98,23 @@ protected: // Utility Methods + virtual void IsTextPropertySetByContent(nsIDOMNode *aNode, + nsIAtom *aProperty, + PRBool &aIsSet) const; + virtual void IsTextStyleSet(nsIStyleContext *aSC, - nsIAtom *aProperty, - PRBool &aIsSet) const; + nsIAtom *aProperty, + PRBool &aIsSet) const; + + NS_IMETHOD IsNodeInline(nsIDOMNode *aNode, PRBool &aIsInline) const; + + NS_IMETHOD IntermediateNodesAreInline(nsIDOMRange *aRange, + nsIDOMNode *aStartNode, + PRInt32 aStartOffset, + nsIDOMNode *aEndNode, + PRInt32 aEndOffset, + nsIDOMNode *aParent, + PRBool &aResult) const; NS_IMETHOD SetTextPropertiesForNode(nsIDOMNode *aNode, nsIDOMNode *aParent, @@ -121,11 +137,13 @@ protected: nsIDOMNode *aParent, nsIAtom *aPropName); - NS_IMETHOD SetTagFromProperty(nsAutoString &aTag, nsIAtom *aPropName) const; + + NS_IMETHOD SetTypeInStateForProperty(TypeInState &aTypeInState, nsIAtom *aPropName); // Data members protected: + TypeInState mTypeInState; nsTextEditRules* mRules; nsCOMPtr mKeyListenerP; nsCOMPtr mMouseListenerP; diff --git a/mozilla/editor/libeditor/base/CreateElementTxn.cpp b/mozilla/editor/libeditor/base/CreateElementTxn.cpp index 4b0436e4287..824b266bcf4 100644 --- a/mozilla/editor/libeditor/base/CreateElementTxn.cpp +++ b/mozilla/editor/libeditor/base/CreateElementTxn.cpp @@ -24,15 +24,21 @@ #include "nsIDOMElement.h" #include "nsIEditorSupport.h" +#ifdef NS_DEBUG +static PRBool gNoisy = PR_TRUE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif + CreateElementTxn::CreateElementTxn() : EditTxn() { } NS_IMETHODIMP CreateElementTxn::Init(nsIEditor *aEditor, - const nsString& aTag, - nsIDOMNode *aParent, - PRUint32 aOffsetInParent) + const nsString& aTag, + nsIDOMNode *aParent, + PRUint32 aOffsetInParent) { NS_ASSERTION(aEditor&&aParent, "null args"); if (aEditor && aParent) @@ -41,8 +47,6 @@ NS_IMETHODIMP CreateElementTxn::Init(nsIEditor *aEditor, mTag = aTag; mParent = do_QueryInterface(aParent); mOffsetInParent = aOffsetInParent; - mNewNode = do_QueryInterface(nsnull); - mRefNode = do_QueryInterface(nsnull); #ifdef NS_DEBUG { nsCOMPtr testChildNodes; @@ -63,6 +67,8 @@ CreateElementTxn::~CreateElementTxn() NS_IMETHODIMP CreateElementTxn::Do(void) { + if (gNoisy) { printf("Do Create Element parent = %p, offset = %d\n", + mParent.get(), mOffsetInParent); } NS_ASSERTION(mEditor, "bad state -- null editor"); nsresult result = NS_ERROR_NULL_POINTER; if (mEditor) @@ -92,6 +98,7 @@ NS_IMETHODIMP CreateElementTxn::Do(void) NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewNode)), "could not create element."); if ((NS_SUCCEEDED(result)) && (mNewNode)) { + if (gNoisy) { printf(" newNode = %p\n", mNewNode.get()); } // insert the new node nsCOMPtr resultNode; if (CreateElementTxn::eAppend==mOffsetInParent) @@ -134,6 +141,8 @@ NS_IMETHODIMP CreateElementTxn::Do(void) NS_IMETHODIMP CreateElementTxn::Undo(void) { + if (gNoisy) { printf("Undo Create Element, mParent = %p, node = %p\n", + mParent.get(), mNewNode.get()); } nsCOMPtr resultNode; nsresult result = mParent->RemoveChild(mNewNode, getter_AddRefs(resultNode)); if (NS_SUCCEEDED(result)) @@ -154,6 +163,18 @@ NS_IMETHODIMP CreateElementTxn::Undo(void) NS_IMETHODIMP CreateElementTxn::Redo(void) { + if (gNoisy) { printf("Redo Create Element\n"); } + + // first, reset mNewNode so it has no attributes or content + nsCOMPtrnodeAsText; + nodeAsText = do_QueryInterface(mNewNode); + if (nodeAsText) + { + nsAutoString nullString; + nodeAsText->SetData(nullString); + } + + // now, reinsert mNewNode nsCOMPtr resultNode; nsresult result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); if (NS_SUCCEEDED(result)) diff --git a/mozilla/editor/libeditor/base/DeleteElementTxn.cpp b/mozilla/editor/libeditor/base/DeleteElementTxn.cpp index 484c5d0a0bd..15b9bd004f8 100644 --- a/mozilla/editor/libeditor/base/DeleteElementTxn.cpp +++ b/mozilla/editor/libeditor/base/DeleteElementTxn.cpp @@ -22,7 +22,7 @@ #endif #ifdef NS_DEBUG -static PRBool gNoisy = PR_FALSE; +static PRBool gNoisy = PR_TRUE; #else static const PRBool gNoisy = PR_FALSE; #endif @@ -50,6 +50,7 @@ DeleteElementTxn::~DeleteElementTxn() NS_IMETHODIMP DeleteElementTxn::Do(void) { + if (gNoisy) { printf("Do Delete Element element = %p\n", mElement.get()); } if (!mElement) return NS_ERROR_NULL_POINTER; @@ -96,6 +97,7 @@ NS_IMETHODIMP DeleteElementTxn::Do(void) NS_IMETHODIMP DeleteElementTxn::Undo(void) { + if (gNoisy) { printf("Do Delete Element element = %p, parent = %p\n", mElement.get(), mParent.get()); } if (!mParent || !mElement) return NS_ERROR_NULL_POINTER; @@ -106,6 +108,7 @@ NS_IMETHODIMP DeleteElementTxn::Undo(void) NS_IMETHODIMP DeleteElementTxn::Redo(void) { + if (gNoisy) { printf("Redo Delete Element\n"); } if (!mParent || !mElement) return NS_ERROR_NULL_POINTER; @@ -114,6 +117,7 @@ NS_IMETHODIMP DeleteElementTxn::Redo(void) return result; } + NS_IMETHODIMP DeleteElementTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction) { if (nsnull!=aDidMerge) diff --git a/mozilla/editor/libeditor/base/DeleteRangeTxn.cpp b/mozilla/editor/libeditor/base/DeleteRangeTxn.cpp index 063cc1448b4..c0014792e15 100644 --- a/mozilla/editor/libeditor/base/DeleteRangeTxn.cpp +++ b/mozilla/editor/libeditor/base/DeleteRangeTxn.cpp @@ -36,7 +36,7 @@ static NS_DEFINE_IID(kDeleteTextTxnIID, DELETE_TEXT_TXN_IID); static NS_DEFINE_IID(kDeleteElementTxnIID, DELETE_ELEMENT_TXN_IID); #ifdef NS_DEBUG -static PRBool gNoisy = PR_FALSE; +static PRBool gNoisy = PR_TRUE; #else static const PRBool gNoisy = PR_FALSE; #endif @@ -111,6 +111,7 @@ DeleteRangeTxn::~DeleteRangeTxn() NS_IMETHODIMP DeleteRangeTxn::Do(void) { + if (gNoisy) { printf("Do Delete Range\n"); } if (!mStartParent || !mEndParent || !mCommonParent) return NS_ERROR_NULL_POINTER; @@ -157,6 +158,7 @@ NS_IMETHODIMP DeleteRangeTxn::Do(void) NS_IMETHODIMP DeleteRangeTxn::Undo(void) { + if (gNoisy) { printf("Undo Delete Range\n"); } if (!mStartParent || !mEndParent || !mCommonParent) return NS_ERROR_NULL_POINTER; @@ -177,6 +179,7 @@ NS_IMETHODIMP DeleteRangeTxn::Undo(void) NS_IMETHODIMP DeleteRangeTxn::Redo(void) { + if (gNoisy) { printf("Redo Delete Range\n"); } if (!mStartParent || !mEndParent || !mCommonParent) return NS_ERROR_NULL_POINTER; diff --git a/mozilla/editor/libeditor/base/DeleteTextTxn.cpp b/mozilla/editor/libeditor/base/DeleteTextTxn.cpp index b321d49b53b..8f5b0114fdf 100644 --- a/mozilla/editor/libeditor/base/DeleteTextTxn.cpp +++ b/mozilla/editor/libeditor/base/DeleteTextTxn.cpp @@ -20,6 +20,11 @@ #include "nsIDOMCharacterData.h" #include "nsIDOMSelection.h" +#ifdef NS_DEBUG +static PRBool gNoisy = PR_TRUE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif DeleteTextTxn::DeleteTextTxn() : EditTxn() @@ -47,6 +52,7 @@ NS_IMETHODIMP DeleteTextTxn::Init(nsIEditor *aEditor, NS_IMETHODIMP DeleteTextTxn::Do(void) { + if (gNoisy) { printf("Do Delete Text\n"); } nsresult result = NS_ERROR_NULL_POINTER; if (mEditor && mElement) { @@ -71,6 +77,7 @@ NS_IMETHODIMP DeleteTextTxn::Do(void) // was it an insertion point or an extended selection? NS_IMETHODIMP DeleteTextTxn::Undo(void) { + if (gNoisy) { printf("Undo Delete Text\n"); } nsresult result = NS_ERROR_NULL_POINTER; if (mEditor && mElement) { diff --git a/mozilla/editor/libeditor/base/EditAggregateTxn.cpp b/mozilla/editor/libeditor/base/EditAggregateTxn.cpp index e6a93f29632..27f3032aa0e 100644 --- a/mozilla/editor/libeditor/base/EditAggregateTxn.cpp +++ b/mozilla/editor/libeditor/base/EditAggregateTxn.cpp @@ -24,7 +24,7 @@ EditAggregateTxn::EditAggregateTxn() : EditTxn() { - NS_INIT_REFCNT(); + // base class does this: NS_INIT_REFCNT(); mChildren = new nsVoidArray(); } @@ -205,6 +205,30 @@ NS_IMETHODIMP EditAggregateTxn::GetName(nsIAtom **aName) return NS_ERROR_NULL_POINTER; } +NS_IMETHODIMP_(nsrefcnt) EditAggregateTxn::AddRef(void) +{ + return EditTxn::AddRef(); +} + +//NS_IMPL_RELEASE_INHERITED(Class, Super) +NS_IMETHODIMP_(nsrefcnt) EditAggregateTxn::Release(void) +{ + return EditTxn::Release(); +} + +//NS_IMPL_QUERY_INTERFACE_INHERITED(Class, Super, AdditionalInterface) +NS_IMETHODIMP EditAggregateTxn::QueryInterface(REFNSIID aIID, void** aInstancePtr) +{ + if (!aInstancePtr) return NS_ERROR_NULL_POINTER; + + if (aIID.Equals(EditAggregateTxn::GetIID())) { + *aInstancePtr = (nsISupports*)(EditAggregateTxn*)(this); + NS_ADDREF_THIS(); + return NS_OK; + } + return EditTxn::QueryInterface(aIID, aInstancePtr); +} + diff --git a/mozilla/editor/libeditor/base/EditAggregateTxn.h b/mozilla/editor/libeditor/base/EditAggregateTxn.h index 2e83a62e4a5..60a2ce9a306 100644 --- a/mozilla/editor/libeditor/base/EditAggregateTxn.h +++ b/mozilla/editor/libeditor/base/EditAggregateTxn.h @@ -38,6 +38,10 @@ class EditAggregateTxn : public EditTxn { public: + NS_DECL_ISUPPORTS_INHERITED + + static const nsIID& GetIID() { static nsIID iid = EDIT_AGGREGATE_TXN_IID; return iid; } + EditAggregateTxn(); virtual ~EditAggregateTxn(); diff --git a/mozilla/editor/libeditor/base/InsertElementTxn.cpp b/mozilla/editor/libeditor/base/InsertElementTxn.cpp index 05122a67247..5dfae44f06a 100644 --- a/mozilla/editor/libeditor/base/InsertElementTxn.cpp +++ b/mozilla/editor/libeditor/base/InsertElementTxn.cpp @@ -17,9 +17,10 @@ */ #include "InsertElementTxn.h" +#include "nsIDOMSelection.h" #ifdef NS_DEBUG -static PRBool gNoisy = PR_FALSE; +static PRBool gNoisy = PR_TRUE; #else static const PRBool gNoisy = PR_FALSE; #endif @@ -32,14 +33,17 @@ InsertElementTxn::InsertElementTxn() NS_IMETHODIMP InsertElementTxn::Init(nsIDOMNode *aNode, nsIDOMNode *aParent, - PRInt32 aOffset) + PRInt32 aOffset, + nsIEditor *aEditor) { - if (!aNode || !aParent) + NS_ASSERTION(aNode && aParent && aEditor, "bad arg"); + if (!aNode || !aParent || !aEditor) return NS_ERROR_NULL_POINTER; mNode = do_QueryInterface(aNode); mParent = do_QueryInterface(aParent); mOffset = aOffset; + mEditor = do_QueryInterface(aEditor); return NS_OK; } @@ -50,6 +54,7 @@ InsertElementTxn::~InsertElementTxn() NS_IMETHODIMP InsertElementTxn::Do(void) { + if (gNoisy) { printf("Do Insert Element\n"); } if (!mNode || !mParent) return NS_ERROR_NULL_POINTER; @@ -75,11 +80,21 @@ NS_IMETHODIMP InsertElementTxn::Do(void) nsCOMPtr resultNode; result = mParent->InsertBefore(mNode, refNode, getter_AddRefs(resultNode)); + if (NS_SUCCEEDED(result) && resultNode) + { + nsCOMPtr selection; + result = mEditor->GetSelection(getter_AddRefs(selection)); + if ((NS_SUCCEEDED(result)) && selection) + { + selection->Collapse(mParent, mOffset); + } + } return result; } NS_IMETHODIMP InsertElementTxn::Undo(void) { + if (gNoisy) { printf("Undo Insert Element\n"); } if (!mNode || !mParent) return NS_ERROR_NULL_POINTER; diff --git a/mozilla/editor/libeditor/base/InsertElementTxn.h b/mozilla/editor/libeditor/base/InsertElementTxn.h index f4dafc1f0db..4f04676a3d6 100644 --- a/mozilla/editor/libeditor/base/InsertElementTxn.h +++ b/mozilla/editor/libeditor/base/InsertElementTxn.h @@ -20,6 +20,7 @@ #define InsertElementTxn_h__ #include "EditTxn.h" +#include "nsIEditor.h" #include "nsIDOMNode.h" #include "nsCOMPtr.h" @@ -42,7 +43,8 @@ public: */ NS_IMETHOD Init(nsIDOMNode *aNode, nsIDOMNode *aParent, - PRInt32 aOffset); + PRInt32 aOffset, + nsIEditor *aEditor); private: InsertElementTxn(); @@ -71,6 +73,9 @@ protected: /** the node into which the new node will be inserted */ nsCOMPtr mParent; + /** the editor for this transaction */ + nsCOMPtr mEditor; + /** the index in mParent for the new node */ PRInt32 mOffset; diff --git a/mozilla/editor/libeditor/base/InsertTextTxn.cpp b/mozilla/editor/libeditor/base/InsertTextTxn.cpp index 70c86cfc48a..11e9fc0ff2c 100644 --- a/mozilla/editor/libeditor/base/InsertTextTxn.cpp +++ b/mozilla/editor/libeditor/base/InsertTextTxn.cpp @@ -26,6 +26,12 @@ static NS_DEFINE_IID(kInsertTextTxnIID, INSERT_TEXT_TXN_IID); static NS_DEFINE_IID(kIDOMSelectionIID, NS_IDOMSELECTION_IID); +#ifdef NS_DEBUG +static PRBool gNoisy = PR_TRUE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif + nsIAtom *InsertTextTxn::gInsertTextTxnName; nsresult InsertTextTxn::ClassInit() @@ -45,9 +51,9 @@ InsertTextTxn::~InsertTextTxn() } NS_IMETHODIMP InsertTextTxn::Init(nsIDOMCharacterData *aElement, - PRUint32 aOffset, - const nsString& aStringToInsert, - nsIPresShell* aPresShell) + PRUint32 aOffset, + const nsString &aStringToInsert, + nsIPresShell *aPresShell) { mElement = do_QueryInterface(aElement); mOffset = aOffset; @@ -58,6 +64,7 @@ NS_IMETHODIMP InsertTextTxn::Init(nsIDOMCharacterData *aElement, NS_IMETHODIMP InsertTextTxn::Do(void) { + if (gNoisy) { printf("Do Insert Text element = %p\n", mElement.get()); } // advance caret: This requires the presentation shell to get the selection. nsCOMPtr selection; nsresult result = mPresShell->GetSelection(getter_AddRefs(selection)); @@ -74,6 +81,7 @@ NS_IMETHODIMP InsertTextTxn::Do(void) NS_IMETHODIMP InsertTextTxn::Undo(void) { + if (gNoisy) { printf("Undo Insert Text element = %p\n", mElement.get()); } nsresult result; PRUint32 length = mStringToInsert.Length(); result = mElement->DeleteData(mOffset, length); @@ -108,6 +116,7 @@ NS_IMETHODIMP InsertTextTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransacti otherTxn->GetData(otherData); mStringToInsert += otherData; *aDidMerge = PR_TRUE; + if (gNoisy) { printf("InsertTextTxn assimilated %p\n", aTransaction); } } } else @@ -117,7 +126,7 @@ NS_IMETHODIMP InsertTextTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransacti { nsCOMPtr txnName; otherTxn->GetName(getter_AddRefs(txnName)); - if (txnName.get()==gInsertTextTxnName) + if (txnName && txnName.get()==gInsertTextTxnName) { // yep, it's one of ours. By definition, it must contain only // another aggregate with a single child, // or a single InsertTextTxn @@ -135,6 +144,7 @@ NS_IMETHODIMP InsertTextTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransacti otherInsertTxn->GetData(otherData); mStringToInsert += otherData; *aDidMerge = PR_TRUE; + if (gNoisy) { printf("InsertTextTxn assimilated %p\n", aTransaction); } } } } diff --git a/mozilla/editor/libeditor/base/JoinElementTxn.cpp b/mozilla/editor/libeditor/base/JoinElementTxn.cpp index 3d6c0d662f9..c12a638480f 100644 --- a/mozilla/editor/libeditor/base/JoinElementTxn.cpp +++ b/mozilla/editor/libeditor/base/JoinElementTxn.cpp @@ -22,6 +22,12 @@ #include "nsIDOMSelection.h" #include "nsIEditorSupport.h" +#ifdef NS_DEBUG +static PRBool gNoisy = PR_TRUE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif + static NS_DEFINE_IID(kIEditorSupportIID, NS_IEDITORSUPPORT_IID); @@ -45,8 +51,10 @@ JoinElementTxn::~JoinElementTxn() { } +// After Do() and Redo(), the left node is removed from the content tree and right node remains. NS_IMETHODIMP JoinElementTxn::Do(void) { + if (gNoisy) { printf("%p Do Join of %p and %p\n", this, mLeftNode.get(), mRightNode.get()); } nsresult result; if ((mLeftNode) && (mRightNode)) @@ -82,6 +90,7 @@ NS_IMETHODIMP JoinElementTxn::Do(void) result = editor->JoinNodesImpl(mRightNode, mLeftNode, mParent, PR_FALSE); if (NS_SUCCEEDED(result)) { + if (gNoisy) { printf(" left node = %p removed\n", this, mLeftNode.get()); } nsCOMPtrselection; mEditor->GetSelection(getter_AddRefs(selection)); if (selection) @@ -102,9 +111,11 @@ NS_IMETHODIMP JoinElementTxn::Do(void) return result; } - +//XXX: what if instead of split, we just deleted the unneeded children of mRight +// and re-inserted mLeft? NS_IMETHODIMP JoinElementTxn::Undo(void) { + /* nsresult result; nsCOMPtr editor; result = mEditor->QueryInterface(kIEditorSupportIID, getter_AddRefs(editor)); @@ -112,6 +123,7 @@ NS_IMETHODIMP JoinElementTxn::Undo(void) result = editor->SplitNodeImpl(mRightNode, mOffset, mLeftNode, mParent); if (NS_SUCCEEDED(result) && mLeftNode) { + if (gNoisy) { printf(" created left node = %p\n", this, mLeftNode.get()); } nsCOMPtrselection; mEditor->GetSelection(getter_AddRefs(selection)); if (selection) @@ -124,10 +136,47 @@ NS_IMETHODIMP JoinElementTxn::Undo(void) result = NS_ERROR_NOT_IMPLEMENTED; } return result; + */ + + if (gNoisy) { printf("%p Undo Join, right node = %p\n", this, mRightNode.get()); } + NS_ASSERTION(mRightNode && mLeftNode && mParent, "bad state"); + if (!mRightNode || !mLeftNode || !mParent) { + return NS_ERROR_NOT_INITIALIZED; + } + nsresult result; + nsCOMPtrresultNode; + // first, massage the existing node so it is in its post-split state + nsCOMPtrrightNodeAsText; + rightNodeAsText = do_QueryInterface(mRightNode); + if (rightNodeAsText) + { + result = rightNodeAsText->DeleteData(0, mOffset); + } + else + { + nsCOMPtrchild; + nsCOMPtrnextSibling; + result = mRightNode->GetFirstChild(getter_AddRefs(child)); + PRInt32 i; + for (i=0; iGetNextSibling(getter_AddRefs(nextSibling)); + result = mRightNode->RemoveChild(child, getter_AddRefs(resultNode)); + child = do_QueryInterface(nextSibling); + } + } + // second, re-insert the left node into the tree + result = mParent->InsertBefore(mLeftNode, mRightNode, getter_AddRefs(resultNode)); + return result; + } +/* NS_IMETHODIMP JoinElementTxn::Redo(void) { + if (gNoisy) { printf("Redo Join\n"); } nsresult result; nsCOMPtr editor; result = mEditor->QueryInterface(kIEditorSupportIID, getter_AddRefs(editor)); @@ -149,7 +198,7 @@ NS_IMETHODIMP JoinElementTxn::Redo(void) } return result; } - +*/ NS_IMETHODIMP JoinElementTxn::GetIsTransient(PRBool *aIsTransient) { diff --git a/mozilla/editor/libeditor/base/JoinElementTxn.h b/mozilla/editor/libeditor/base/JoinElementTxn.h index 0d7fa2d699c..a85c0abb217 100644 --- a/mozilla/editor/libeditor/base/JoinElementTxn.h +++ b/mozilla/editor/libeditor/base/JoinElementTxn.h @@ -31,8 +31,10 @@ /** - * A transaction that joins two elements E1 and E2 into a single node E. + * A transaction that joins two elements E1 (left node) and E2 (right node) + * into a single node E. * The children of E are the children of E1 followed by the children of E2. + * After Do() and Redo(), E1 is removed from the content tree and E2 remains. */ class JoinElementTxn : public EditTxn { @@ -57,7 +59,7 @@ public: NS_IMETHOD Undo(void); - NS_IMETHOD Redo(void); +// NS_IMETHOD Redo(void); NS_IMETHOD GetIsTransient(PRBool *aIsTransient); diff --git a/mozilla/editor/libeditor/base/PlaceholderTxn.cpp b/mozilla/editor/libeditor/base/PlaceholderTxn.cpp index 941d6e05c95..83f7feaa694 100644 --- a/mozilla/editor/libeditor/base/PlaceholderTxn.cpp +++ b/mozilla/editor/libeditor/base/PlaceholderTxn.cpp @@ -39,6 +39,7 @@ PlaceholderTxn::~PlaceholderTxn() NS_IMETHODIMP PlaceholderTxn::Do(void) { + if (gNoisy) { printf("PlaceholderTxn Do\n"); } return NS_OK; } @@ -55,6 +56,7 @@ NS_IMETHODIMP PlaceholderTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransact { // yep, it's one of ours. Assimilate it. AppendChild(editTxn); *aDidMerge = PR_TRUE; + if (gNoisy) { printf("Placeholder txn assimilated %p\n", aTransaction); } } else { // let our last child txn make the choice diff --git a/mozilla/editor/libeditor/base/SplitElementTxn.cpp b/mozilla/editor/libeditor/base/SplitElementTxn.cpp index bfb5f09f188..3c9aa43e843 100644 --- a/mozilla/editor/libeditor/base/SplitElementTxn.cpp +++ b/mozilla/editor/libeditor/base/SplitElementTxn.cpp @@ -19,8 +19,15 @@ #include "SplitElementTxn.h" #include "nsIDOMNode.h" #include "nsIDOMSelection.h" +#include "nsIDOMCharacterData.h" #include "nsIEditorSupport.h" +#ifdef NS_DEBUG +static PRBool gNoisy = PR_TRUE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif + static NS_DEFINE_IID(kIEditorSupportIID, NS_IEDITORSUPPORT_IID); // note that aEditor is not refcounted @@ -45,6 +52,7 @@ SplitElementTxn::~SplitElementTxn() NS_IMETHODIMP SplitElementTxn::Do(void) { + if (gNoisy) { printf("%p Do Split of node %p offset %d\n", this, mExistingRightNode.get(), mOffset); } NS_ASSERTION(mExistingRightNode, "bad state"); if (!mExistingRightNode) { return NS_ERROR_NOT_INITIALIZED; @@ -55,6 +63,7 @@ NS_IMETHODIMP SplitElementTxn::Do(void) if ((NS_SUCCEEDED(result)) && (mNewLeftNode)) { + if (gNoisy) { printf(" created left node = %p\n", this, mNewLeftNode.get()); } // get the parent node result = mExistingRightNode->GetParentNode(getter_AddRefs(mParent)); // insert the new node @@ -85,6 +94,10 @@ NS_IMETHODIMP SplitElementTxn::Do(void) NS_IMETHODIMP SplitElementTxn::Undo(void) { + if (gNoisy) { + printf("%p Undo Split of existing node %p and new node %p offset %d\n", + this, mExistingRightNode.get(), mNewLeftNode.get(), mOffset); + } NS_ASSERTION(mExistingRightNode && mNewLeftNode && mParent, "bad state"); if (!mExistingRightNode || !mNewLeftNode || !mParent) { return NS_ERROR_NOT_INITIALIZED; @@ -112,8 +125,9 @@ NS_IMETHODIMP SplitElementTxn::Undo(void) if (NS_SUCCEEDED(result) && editor) { result = editor->JoinNodesImpl(mExistingRightNode, mNewLeftNode, mParent, PR_FALSE); - if (NS_SUCCEEDED(result) && mNewLeftNode) + if (NS_SUCCEEDED(result)) { + if (gNoisy) { printf(" left node = %p removed\n", this, mNewLeftNode.get()); } nsCOMPtrselection; mEditor->GetSelection(getter_AddRefs(selection)); if (selection) @@ -128,25 +142,48 @@ NS_IMETHODIMP SplitElementTxn::Undo(void) return result; } -/* +/* redo cannot simply resplit the right node, because subsequent transactions + * on the redo stack may depend on the left node existing in its previous state. + */ NS_IMETHODIMP SplitElementTxn::Redo(void) { NS_ASSERTION(mExistingRightNode && mNewLeftNode && mParent, "bad state"); if (!mExistingRightNode || !mNewLeftNode || !mParent) { return NS_ERROR_NOT_INITIALIZED; } + if (gNoisy) { + printf("%p Redo Split of existing node %p and new node %p offset %d\n", + this, mExistingRightNode.get(), mNewLeftNode.get(), mOffset); + } nsresult result; - nsCOMPtr editor; - result = mEditor->QueryInterface(kIEditorSupportIID, getter_AddRefs(editor)); - if (NS_SUCCEEDED(result) && editor) { - result = editor->SplitNodeImpl(mExistingRightNode, mOffset, mNewLeftNode, mParent); + nsCOMPtrresultNode; + // first, massage the existing node so it is in its post-split state + nsCOMPtrrightNodeAsText; + rightNodeAsText = do_QueryInterface(mExistingRightNode); + if (rightNodeAsText) + { + result = rightNodeAsText->DeleteData(0, mOffset); } - else { - result = NS_ERROR_NOT_IMPLEMENTED; + else + { + nsCOMPtrchild; + nsCOMPtrnextSibling; + result = mExistingRightNode->GetFirstChild(getter_AddRefs(child)); + PRInt32 i; + for (i=0; iGetNextSibling(getter_AddRefs(nextSibling)); + result = mExistingRightNode->RemoveChild(child, getter_AddRefs(resultNode)); + child = do_QueryInterface(nextSibling); + } } + // second, re-insert the left node into the tree + result = mParent->InsertBefore(mNewLeftNode, mExistingRightNode, getter_AddRefs(resultNode)); return result; } -*/ + NS_IMETHODIMP SplitElementTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction) { diff --git a/mozilla/editor/libeditor/base/SplitElementTxn.h b/mozilla/editor/libeditor/base/SplitElementTxn.h index 3d39e6f326c..a2c399bcc58 100644 --- a/mozilla/editor/libeditor/base/SplitElementTxn.h +++ b/mozilla/editor/libeditor/base/SplitElementTxn.h @@ -57,7 +57,7 @@ public: NS_IMETHOD Undo(void); - //NS_IMETHOD Redo(void); + NS_IMETHOD Redo(void); NS_IMETHOD Merge(PRBool *aDidMerge, nsITransaction *aTransaction); diff --git a/mozilla/editor/libeditor/base/nsEditor.cpp b/mozilla/editor/libeditor/base/nsEditor.cpp index 63c745f4505..15a2d326fa1 100644 --- a/mozilla/editor/libeditor/base/nsEditor.cpp +++ b/mozilla/editor/libeditor/base/nsEditor.cpp @@ -69,6 +69,7 @@ #include "nsIViewManager.h" #include "nsIView.h" // END +#endif //#define NEW_CLIPBOARD_SUPPORT @@ -94,7 +95,6 @@ static NS_DEFINE_IID(kCXIFFormatConverterCID, NS_XIFFORMATCONVERTER_CID); static NS_DEFINE_IID(kIFormatConverterIID, NS_IFORMATCONVERTER_IID); #endif -#endif static NS_DEFINE_IID(kIContentIID, NS_ICONTENT_IID); static NS_DEFINE_IID(kIDOMTextIID, NS_IDOMTEXT_IID); static NS_DEFINE_IID(kIDOMElementIID, NS_IDOMELEMENT_IID); @@ -146,6 +146,12 @@ static NS_DEFINE_CID(kComponentManagerCID, NS_COMPONENTMANAGER_CID); #define NS_ERROR_EDITOR_NO_SELECTION NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_EDITOR,1) #define NS_ERROR_EDITOR_NO_TEXTNODE NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_EDITOR,2) +#ifdef NS_DEBUG +static PRBool gNoisy = PR_TRUE; +#else +static const PRBool gNoisy = PR_FALSE; +#endif + /* ----- TEST METHODS DECLARATIONS ----- */ @@ -676,6 +682,7 @@ nsEditor::GetFirstTextNode(nsIDOMNode *aNode, nsIDOMNode **aRetNode) NS_IMETHODIMP nsEditor::Do(nsITransaction *aTxn) { + if (gNoisy) { printf("Editor::Do ----------\n"); } nsresult result = NS_OK; nsCOMPtrselection; nsresult selectionResult = GetSelection(getter_AddRefs(selection)); @@ -698,6 +705,7 @@ nsEditor::Do(nsITransaction *aTxn) NS_IMETHODIMP nsEditor::Undo(PRUint32 aCount) { + if (gNoisy) { printf("Editor::Undo ----------\n"); } nsresult result = NS_OK; nsCOMPtrselection; nsresult selectionResult = GetSelection(getter_AddRefs(selection)); @@ -721,6 +729,7 @@ nsEditor::Undo(PRUint32 aCount) NS_IMETHODIMP nsEditor::Redo(PRUint32 aCount) { + if (gNoisy) { printf("Editor::Redo ----------\n"); } nsresult result = NS_OK; nsCOMPtrselection; nsresult selectionResult = GetSelection(getter_AddRefs(selection)); @@ -955,8 +964,8 @@ NS_IMETHODIMP nsEditor::CreateTxnForCreateElement(const nsString& aTag, } NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode, - nsIDOMNode * aParent, - PRInt32 aPosition) + nsIDOMNode * aParent, + PRInt32 aPosition) { InsertElementTxn *txn; nsresult result = CreateTxnForInsertElement(aNode, aParent, aPosition, &txn); @@ -976,7 +985,7 @@ NS_IMETHODIMP nsEditor::CreateTxnForInsertElement(nsIDOMNode * aNode, { result = TransactionFactory::GetNewTransaction(kInsertElementTxnIID, (EditTxn **)aTxn); if (NS_SUCCEEDED(result)) { - result = (*aTxn)->Init(aNode, aParent, aPosition); + result = (*aTxn)->Init(aNode, aParent, aPosition, this); } } return result; diff --git a/mozilla/editor/libeditor/base/nsIEditProperty.h b/mozilla/editor/libeditor/base/nsIEditProperty.h index cf21b7ad0bb..e7180bd7af7 100644 --- a/mozilla/editor/libeditor/base/nsIEditProperty.h +++ b/mozilla/editor/libeditor/base/nsIEditProperty.h @@ -44,8 +44,33 @@ public: /* we're still trying to decide how edit atoms will work. Until then, use these */ // XXX: fix ASAP! - static nsIAtom *bold; - static nsIAtom *italic; + // tags + static nsIAtom *a; + static nsIAtom *b; + static nsIAtom *big; + static nsIAtom *font; + static nsIAtom *i; + static nsIAtom *span; + static nsIAtom *small; + static nsIAtom *strike; + static nsIAtom *sub; + static nsIAtom *sup; + static nsIAtom *tt; + static nsIAtom *u; + +/* from HTML 3.2 spec +TT teletype or monospaced text +I italic text style +B bold text style +U underlined text style +STRIKE strike-through text style +BIG places text in a large font +SMALL places text in a small font +SUB places text in subscript style +SUP places text in superscript style +*/ + + // XXX: end temp code }; extern nsresult NS_NewEditProperty(nsIEditProperty **aResult); diff --git a/mozilla/editor/libeditor/html/nsEditProperty.cpp b/mozilla/editor/libeditor/html/nsEditProperty.cpp index de516d284d4..5690a29a010 100644 --- a/mozilla/editor/libeditor/html/nsEditProperty.cpp +++ b/mozilla/editor/libeditor/html/nsEditProperty.cpp @@ -26,8 +26,56 @@ NS_IMPL_ADDREF(nsEditProperty) NS_IMPL_RELEASE(nsEditProperty) -nsIAtom * nsIEditProperty::bold = NS_NewAtom("BOLD"); -nsIAtom * nsIEditProperty::italic = NS_NewAtom("ITALIC"); +// XXX: remove when html atoms are exported from layout +// tags +nsIAtom * nsIEditProperty::a; +nsIAtom * nsIEditProperty::b; +nsIAtom * nsIEditProperty::big; +nsIAtom * nsIEditProperty::font; +nsIAtom * nsIEditProperty::i; +nsIAtom * nsIEditProperty::span; +nsIAtom * nsIEditProperty::small; +nsIAtom * nsIEditProperty::strike; +nsIAtom * nsIEditProperty::sub; +nsIAtom * nsIEditProperty::sup; +nsIAtom * nsIEditProperty::tt; +nsIAtom * nsIEditProperty::u; + +void +nsEditProperty::InstanceInit() +{ + // tags + nsIEditProperty::a = NS_NewAtom("A"); + nsIEditProperty::b = NS_NewAtom("B"); + nsIEditProperty::big = NS_NewAtom("BIG"); + nsIEditProperty::font = NS_NewAtom("FONT"); + nsIEditProperty::i = NS_NewAtom("I"); + nsIEditProperty::span = NS_NewAtom("SPAN"); + nsIEditProperty::small =NS_NewAtom("SMALL"); + nsIEditProperty::strike=NS_NewAtom("STRIKE"); + nsIEditProperty::sub = NS_NewAtom("SUB"); + nsIEditProperty::sup = NS_NewAtom("SUP"); + nsIEditProperty::tt = NS_NewAtom("TT"); + nsIEditProperty::u = NS_NewAtom("U"); +} + +void +nsEditProperty::InstanceShutdown() +{ + // tags + NS_IF_RELEASE(nsIEditProperty::a); + NS_IF_RELEASE(nsIEditProperty::b); + NS_IF_RELEASE(nsIEditProperty::big); + NS_IF_RELEASE(nsIEditProperty::font); + NS_IF_RELEASE(nsIEditProperty::i); + NS_IF_RELEASE(nsIEditProperty::span); + NS_IF_RELEASE(nsIEditProperty::small); + NS_IF_RELEASE(nsIEditProperty::strike); + NS_IF_RELEASE(nsIEditProperty::sub); + NS_IF_RELEASE(nsIEditProperty::sup); + NS_IF_RELEASE(nsIEditProperty::tt); + NS_IF_RELEASE(nsIEditProperty::u); +} NS_IMETHODIMP nsEditProperty::QueryInterface(REFNSIID aIID, void** aInstancePtr) diff --git a/mozilla/editor/libeditor/html/nsEditProperty.h b/mozilla/editor/libeditor/html/nsEditProperty.h index 5ce7692a3c5..d6726e21b3b 100644 --- a/mozilla/editor/libeditor/html/nsEditProperty.h +++ b/mozilla/editor/libeditor/html/nsEditProperty.h @@ -44,6 +44,10 @@ public: NS_IMETHOD GetValue(nsIAtom **aValue) const; NS_IMETHOD GetAppliesToAll(PRBool *aAppliesToAll) const; + // temporary methods + static void InstanceInit(); + static void InstanceShutdown(); + protected: nsCOMPtrmProperty; nsCOMPtrmValue; diff --git a/mozilla/editor/libeditor/text/nsEditorEventListeners.cpp b/mozilla/editor/libeditor/text/nsEditorEventListeners.cpp index 8ada50ad02e..2e548a9157c 100644 --- a/mozilla/editor/libeditor/text/nsEditorEventListeners.cpp +++ b/mozilla/editor/libeditor/text/nsEditorEventListeners.cpp @@ -310,7 +310,7 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr aProcessed=PR_TRUE; if (mEditor) { - mEditor->SetTextProperty(nsIEditProperty::italic); + mEditor->SetTextProperty(nsIEditProperty::i); } } @@ -336,81 +336,22 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr aProcessed=PR_TRUE; if (mEditor) { - mEditor->SetTextProperty(nsIEditProperty::bold); + mEditor->SetTextProperty(nsIEditProperty::b); } } break; + // hard-coded ChangeTextAttributes test -- underline case nsIDOMEvent::VK_U: if (PR_TRUE==ctrlKey) { aProcessed=PR_TRUE; if (mEditor) { - PRBool any, all; - mEditor->GetTextProperty(nsIEditProperty::bold, any, all); - printf("the selection has BOLD any=%d all=%d\n", any, all); + mEditor->SetTextProperty(nsIEditProperty::u); } } break; - - - //XXX: test for change and remove attribute, hard-coded to be width on first table in doc - case nsIDOMEvent::VK_TAB: - { - //XXX: should be from a factory - //XXX: should manage the refcount of txn - /* - nsAutoString attribute("width"); - nsAutoString value("400"); - - nsAutoString tableTag("TABLE"); - nsCOMPtr currentNode; - nsCOMPtr element; - if (NS_SUCCEEDED(mEditor->GetFirstNodeOfType(nsnull, tableTag, getter_AddRefs(currentNode)))) - { - if (NS_SUCCEEDED(currentNode->QueryInterface(kIDOMElementIID, getter_AddRefs(element)))) - { - nsresult result; - if (PR_TRUE==ctrlKey) // remove the attribute - result = mEditor->RemoveAttribute(element, attribute); - else // change the attribute - result = mEditor->SetAttribute(element, attribute, value); - } - } - */ - } -// aProcessed=PR_TRUE; - break; - - case nsIDOMEvent::VK_INSERT: - { - //XXX: should be from a factory - //XXX: should manage the refcount of txn - /* - nsresult result; - nsAutoString attribute("src"); - nsAutoString value("resource:/res/samples/raptor.jpg"); - - nsAutoString imgTag("HR"); - nsAutoString bodyTag("BODY"); - nsCOMPtr currentNode; - result = mEditor->GetFirstNodeOfType(nsnull, bodyTag, getter_AddRefs(currentNode)); - if (NS_SUCCEEDED(result)) - { - PRInt32 position; - if (PR_TRUE==ctrlKey) - position=CreateElementTxn::eAppend; - else - position=0; - result = mEditor->CreateNode(imgTag, currentNode, position); - } - mEditor->InsertNode(nsnull, nsnull, 0); - */ - } - aProcessed=PR_TRUE; - break; - } } return NS_OK; diff --git a/mozilla/editor/libeditor/text/nsTextEditRules.cpp b/mozilla/editor/libeditor/text/nsTextEditRules.cpp index 4ad59e1a040..c2d48234dad 100644 --- a/mozilla/editor/libeditor/text/nsTextEditRules.cpp +++ b/mozilla/editor/libeditor/text/nsTextEditRules.cpp @@ -17,7 +17,7 @@ */ #include "nsTextEditRules.h" -#include "nsEditor.h" +#include "nsTextEditor.h" #include "PlaceholderTxn.h" #include "InsertTextTxn.h" #include "nsCOMPtr.h" @@ -29,6 +29,7 @@ #include "nsIDOMCharacterData.h" #include "nsIEnumerator.h" #include "nsIContent.h" +#include "nsIEditProperty.h" const static char* kMOZEditorBogusNodeAttr="MOZ_EDITOR_BOGUS_NODE"; const static char* kMOZEditorBogusNodeValue="TRUE"; @@ -118,7 +119,7 @@ nsTextEditRules::~nsTextEditRules() NS_IMETHODIMP -nsTextEditRules::Init(nsEditor *aEditor) +nsTextEditRules::Init(nsTextEditor *aEditor) { // null aNextRule is ok if (!aEditor) { return NS_ERROR_NULL_POINTER; } @@ -165,9 +166,10 @@ nsTextEditRules::DidInsert(nsIDOMSelection *aSelection, nsresult aResult) NS_IMETHODIMP nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection, - const nsString& aInputString, - PRBool *aCancel, - nsString& aOutputString, + const nsString &aInputString, + PRBool *aCancel, + nsString &aOutputString, + TypeInState &aTypeInState, PlaceholderTxn **aTxn) { if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } @@ -175,7 +177,8 @@ nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection, *aCancel = PR_FALSE; // by default, we insert what we're told to insert aOutputString = aInputString; - if (mBogusNode) + TypeInState typeInState = aTypeInState; // remember the initial type-in state + if (mBogusNode || (PR_TRUE==typeInState.IsAnySet())) { nsresult result = TransactionFactory::GetNewTransaction(kPlaceholderTxnIID, (EditTxn **)aTxn); if (NS_FAILED(result)) { return result; } @@ -184,6 +187,13 @@ nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection, mEditor->Do(*aTxn); } nsresult result = WillInsert(aSelection, aCancel); + if (NS_SUCCEEDED(result) && (PR_FALSE==*aCancel)) + { + if (PR_TRUE==typeInState.IsAnySet()) + { // for every property that is set, insert a new inline style node + result = CreateStyleForInsertText(aSelection, typeInState); + } + } return result; } @@ -195,6 +205,128 @@ nsTextEditRules::DidInsertText(nsIDOMSelection *aSelection, return DidInsert(aSelection, aResult); } +NS_IMETHODIMP +nsTextEditRules::CreateStyleForInsertText(nsIDOMSelection *aSelection, TypeInState &aTypeInState) +{ + // private method, we know aSelection is not null, and that it is collapsed + NS_ASSERTION(nsnull!=aSelection, "bad selection"); + + // We know at least one style is set and we're about to insert at least one character. + // If the selection is in a text node, split the node (even if we're at the beginning or end) + // then put the text node inside new inline style parents. + // Otherwise, create the text node and the new inline style parents. + nsCOMPtranchor; + PRInt32 offset; + nsresult result = aSelection->GetAnchorNodeAndOffset(getter_AddRefs(anchor), &offset); + if ((NS_SUCCEEDED(result)) && anchor) + { + nsCOMPtranchorAsText; + anchorAsText = do_QueryInterface(anchor); + if (anchorAsText) + { + nsCOMPtrnewTextNode; + // create an empty text node by splitting the selected text node according to offset + if (0==offset) + { + result = mEditor->SplitNode(anchorAsText, offset, getter_AddRefs(newTextNode)); + } + else + { + PRUint32 length; + anchorAsText->GetLength(&length); + if (length==offset) + { + // newTextNode will be the left node + result = mEditor->SplitNode(anchorAsText, offset, getter_AddRefs(newTextNode)); + // but we want the right node in this case + newTextNode = do_QueryInterface(anchor); + } + else + { + // splitting anchor twice sets newTextNode as an empty text node between + // two halves of the original text node + result = mEditor->SplitNode(anchorAsText, offset, getter_AddRefs(newTextNode)); + result = mEditor->SplitNode(anchorAsText, 0, getter_AddRefs(newTextNode)); + } + } + // now we have the new text node we are going to insert into. + // create style nodes or move it up the content hierarchy as needed. + if ((NS_SUCCEEDED(result)) && newTextNode) + { + if (aTypeInState.IsSet(NS_TYPEINSTATE_BOLD)) + { + if (PR_TRUE==aTypeInState.GetBold()) + { // make the next char bold + nsCOMPtrparent; + newTextNode->GetParentNode(getter_AddRefs(parent)); + PRInt32 offsetInParent; + nsIEditorSupport::GetChildOffset(newTextNode, parent, offsetInParent); + nsAutoString tag; + nsIEditProperty::b->ToString(tag); + nsCOMPtrnewStyleNode; + result = mEditor->CreateNode(tag, parent, offsetInParent, getter_AddRefs(newStyleNode)); + result = mEditor->DeleteNode(newTextNode); + result = mEditor->InsertNode(newTextNode, newStyleNode, 0); + aSelection->Collapse(newTextNode, 0); + } + else + { + printf("not yet implemented, make unbold in a bold context\n"); + } + } + } + } + else + { + printf("not yet implemented. selection is not text.\n"); + } + } + else // we have no selection, so insert a style tag in the body + { + nsCOMPtrdoc; + mEditor->GetDocument(getter_AddRefs(doc)); + nsCOMPtrnodeList; + nsAutoString bodyTag = "body"; + 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!"); + nsCOMPtrbodyNode; + result = nodeList->Item(0, getter_AddRefs(bodyNode)); + if ((NS_SUCCEEDED(result)) && bodyNode) + { // now we've got the body tag. insert the style tag + if (aTypeInState.IsSet(NS_TYPEINSTATE_BOLD)) + { + if (PR_TRUE==aTypeInState.GetBold()) + { // make the next char bold + nsAutoString tag; + nsIEditProperty::b->ToString(tag); + nsCOMPtrnewStyleNode; + nsCOMPtrnewTextNode; + result = mEditor->CreateNode(tag, bodyNode, 0, getter_AddRefs(newStyleNode)); + result = mEditor->CreateNode(nsIEditor::GetTextNodeTag(), newStyleNode, 0, getter_AddRefs(newTextNode)); + aSelection->Collapse(newTextNode, 0); + } + } + } + } + } + return result; +} + + +/* +NS_IMETHODIMP +nsTextEditRules::GetInsertBreakTag(nsIAtom **aTag) +{ + if (!aTag) { return NS_ERROR_NULL_POINTER; } + *aTag = NS_NewAtom("BR"); + return NS_OK; +} +*/ + NS_IMETHODIMP nsTextEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, PRBool *aCancel) { @@ -325,10 +457,6 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection, nsresult aResul nsCOMPtr parentNode; selectedNode->GetParentNode(getter_AddRefs(parentNode)); result = mEditor->JoinNodes(siblingNode, selectedNode, parentNode); - // set selection to point between the end of siblingNode and start of selectedNode - aSelection->Collapse(siblingNode, siblingLength); - selectedNode = do_QueryInterface(siblingNode); // subsequent code relies on selectedNode - // being the real node in the DOM tree } } selectedNode->GetNextSibling(getter_AddRefs(siblingNode)); diff --git a/mozilla/editor/libeditor/text/nsTextEditRules.h b/mozilla/editor/libeditor/text/nsTextEditRules.h index 8aecebc37c4..afda44444c6 100644 --- a/mozilla/editor/libeditor/text/nsTextEditRules.h +++ b/mozilla/editor/libeditor/text/nsTextEditRules.h @@ -22,8 +22,9 @@ #include "nsIEditor.h" #include "nsCOMPtr.h" #include "nsIDOMNode.h" +#include "TypeInState.h" -class nsEditor; +class nsTextEditor; class PlaceholderTxn; /** Object that encapsulates HTML text-specific editing rules. @@ -44,17 +45,19 @@ public: nsTextEditRules(); virtual ~nsTextEditRules(); - NS_IMETHOD Init(nsEditor *aEditor); + NS_IMETHOD Init(nsTextEditor *aEditor); NS_IMETHOD WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel); NS_IMETHOD DidInsertBreak(nsIDOMSelection *aSelection, nsresult aResult); - NS_IMETHOD WillInsertText(nsIDOMSelection *aSelection, - const nsString& aInputString, - PRBool *aCancel, - nsString& aOutputString, - PlaceholderTxn ** aTxn); + NS_IMETHOD WillInsertText(nsIDOMSelection *aSelection, + const nsString &aInputString, + PRBool *aCancel, + nsString &aOutputString, + TypeInState &aTypeInState, + PlaceholderTxn **aTxn); NS_IMETHOD DidInsertText(nsIDOMSelection *aSelection, const nsString& aStringToInsert, nsresult aResult); + NS_IMETHOD CreateStyleForInsertText(nsIDOMSelection *aSelection, TypeInState &aTypeInState); NS_IMETHOD WillInsert(nsIDOMSelection *aSelection, PRBool *aCancel); NS_IMETHOD DidInsert(nsIDOMSelection *aSelection, nsresult aResult); @@ -74,7 +77,7 @@ protected: static PRBool NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag); static PRBool IsEditable(nsIDOMNode *aNode); - nsEditor *mEditor; // note that we do not refcount the editor + nsTextEditor *mEditor; // note that we do not refcount the editor nsCOMPtr mBogusNode; // magic node acts as placeholder in empty doc };