From 0fe8e1324dc6bb9c5c626b200415b3c8e4aca7be Mon Sep 17 00:00:00 2001 From: "buster%netscape.com" Date: Thu, 1 Apr 1999 17:58:07 +0000 Subject: [PATCH] As a reminder, we decided to do this based strictly content. Some support for style-based text properties is written, but not used anywhere any more. * Cleaned up split and join undo/redo. * Added TypeInState, a data struct that remembers things about text properties for collapsed selections, so you can type * Ctrl-B with an insertion point and the next character will be bold. * Added all the logic to handle inline vs. block elements when setting text properties. * Added some support for italic and underline as well. Adding these things is pretty easy now. Ctrl-B, Ctrl-I, Ctrl-U for testing bold, italic, underline. * Added all the logic to make sure we only add style tags where they're needed, so you should never get the same style tag nested within itself, except as needed for block elements. * Added methods for testing a node to see if a particular style is set. This isn't 100% done yet, but with very little work we could have toolbar buttons that respond to selection changed notification that show the state of bold, italic, underline, etc. in real time. Supports tri-state: whole selection is bold, some of selection is bold, none of selection is bold, ... * Fully undoable and redoable. * Added some debug printfs to transactions and editors. all controlled by a gNoisy static in each module. helps me track down undo/redo problems. if the output bugs people enough, I'll shut it off and re-enable it in my local tree. Noticably missing: make un-bold, make un-italic, etc. This is coming soon. git-svn-id: svn://10.0.0.236/trunk@25869 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/editor/base/CreateElementTxn.cpp | 31 +- mozilla/editor/base/DeleteElementTxn.cpp | 6 +- mozilla/editor/base/DeleteRangeTxn.cpp | 5 +- mozilla/editor/base/DeleteTextTxn.cpp | 7 + mozilla/editor/base/EditAggregateTxn.cpp | 26 +- mozilla/editor/base/EditAggregateTxn.h | 4 + mozilla/editor/base/InsertElementTxn.cpp | 21 +- mozilla/editor/base/InsertElementTxn.h | 7 +- mozilla/editor/base/InsertTextTxn.cpp | 18 +- mozilla/editor/base/JoinElementTxn.cpp | 53 +- mozilla/editor/base/JoinElementTxn.h | 6 +- mozilla/editor/base/PlaceholderTxn.cpp | 2 + mozilla/editor/base/SplitElementTxn.cpp | 55 +- mozilla/editor/base/SplitElementTxn.h | 2 +- mozilla/editor/base/nsEditProperty.cpp | 52 +- mozilla/editor/base/nsEditProperty.h | 4 + mozilla/editor/base/nsEditor.cpp | 17 +- .../editor/base/nsEditorEventListeners.cpp | 67 +- mozilla/editor/base/nsIEditProperty.h | 29 +- mozilla/editor/base/nsTextEditRules.cpp | 148 ++++- mozilla/editor/base/nsTextEditRules.h | 19 +- mozilla/editor/base/nsTextEditor.cpp | 609 ++++++++++++------ mozilla/editor/base/nsTextEditor.h | 26 +- .../libeditor/base/CreateElementTxn.cpp | 31 +- .../libeditor/base/DeleteElementTxn.cpp | 6 +- .../editor/libeditor/base/DeleteRangeTxn.cpp | 5 +- .../editor/libeditor/base/DeleteTextTxn.cpp | 7 + .../libeditor/base/EditAggregateTxn.cpp | 26 +- .../editor/libeditor/base/EditAggregateTxn.h | 4 + .../libeditor/base/InsertElementTxn.cpp | 21 +- .../editor/libeditor/base/InsertElementTxn.h | 7 +- .../editor/libeditor/base/InsertTextTxn.cpp | 18 +- .../editor/libeditor/base/JoinElementTxn.cpp | 53 +- .../editor/libeditor/base/JoinElementTxn.h | 6 +- .../editor/libeditor/base/PlaceholderTxn.cpp | 2 + .../editor/libeditor/base/SplitElementTxn.cpp | 55 +- .../editor/libeditor/base/SplitElementTxn.h | 2 +- mozilla/editor/libeditor/base/nsEditor.cpp | 17 +- .../editor/libeditor/base/nsIEditProperty.h | 29 +- .../editor/libeditor/html/nsEditProperty.cpp | 52 +- .../editor/libeditor/html/nsEditProperty.h | 4 + .../libeditor/text/nsEditorEventListeners.cpp | 67 +- .../editor/libeditor/text/nsTextEditRules.cpp | 148 ++++- .../editor/libeditor/text/nsTextEditRules.h | 19 +- 44 files changed, 1367 insertions(+), 426 deletions(-) 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 };