* added TransactionFactory. Editor no longer allocates its own transactions. This gives us an oppurtunity for a recycler.

* added DeleteSelection to editor.  Doesn't work very well yet because selection is giving me random offsets into text content.
* lots of work in the various transactions.
This commit is contained in:
buster%netscape.com 1999-01-21 01:51:09 +00:00
Родитель 5d9c7b3a64
Коммит 83a4e325ce
76 изменённых файлов: 3803 добавлений и 1054 удалений

Просмотреть файл

@ -17,23 +17,33 @@
*/ */
#include "ChangeAttributeTxn.h" #include "ChangeAttributeTxn.h"
#include "editor.h"
#include "nsIDOMElement.h" #include "nsIDOMElement.h"
#include "editor.h"
// note that aEditor is not refcounted ChangeAttributeTxn::ChangeAttributeTxn()
ChangeAttributeTxn::ChangeAttributeTxn(nsEditor *aEditor, : EditTxn()
nsIDOMElement *aElement,
const nsString& aAttribute,
const nsString& aValue,
PRBool aRemoveAttribute)
: EditTxn(aEditor)
{ {
mElement = aElement; }
mAttribute = aAttribute;
mValue = aValue; nsresult ChangeAttributeTxn::Init(nsIEditor *aEditor,
mRemoveAttribute = aRemoveAttribute; nsIDOMElement *aElement,
mAttributeWasSet=PR_FALSE; const nsString& aAttribute,
mUndoValue=""; const nsString& aValue,
PRBool aRemoveAttribute)
{
if (nsnull!=aEditor && nsnull!=aElement)
{
mEditor = aEditor;
mElement = aElement;
mAttribute = aAttribute;
mValue = aValue;
mRemoveAttribute = aRemoveAttribute;
mAttributeWasSet=PR_FALSE;
mUndoValue="";
return NS_OK;
}
else
return NS_ERROR_NULL_POINTER;
} }
nsresult ChangeAttributeTxn::Do(void) nsresult ChangeAttributeTxn::Do(void)

Просмотреть файл

@ -20,8 +20,14 @@
#define ChangeAttributeTxn_h__ #define ChangeAttributeTxn_h__
#include "EditTxn.h" #include "EditTxn.h"
#include "nsCOMPtr.h"
#include "nsIDOMElement.h"
#include "nsIEditor.h"
class nsIDOMElement; #define CHANGE_ATTRIBUTE_TXN_IID \
{/* 97818860-ac48-11d2-86d8-000064657374 */ \
0x97818860, 0xac48, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* A transaction that changes an attribute of a content node. * A transaction that changes an attribute of a content node.
@ -31,11 +37,16 @@ class ChangeAttributeTxn : public EditTxn
{ {
public: public:
ChangeAttributeTxn(nsEditor *aEditor, virtual nsresult Init(nsIEditor *aEditor,
nsIDOMElement *aElement, nsIDOMElement *aElement,
const nsString& aAttribute, const nsString& aAttribute,
const nsString& aValue, const nsString& aValue,
PRBool aRemoveAttribute); PRBool aRemoveAttribute);
private:
ChangeAttributeTxn();
public:
virtual nsresult Do(void); virtual nsresult Do(void);
@ -55,8 +66,11 @@ public:
protected: protected:
/** the editor that created this transaction */
nsCOMPtr<nsIEditor> mEditor;
/** the element to operate upon */ /** the element to operate upon */
nsIDOMElement *mElement; nsCOMPtr<nsIDOMElement> mElement;
/** the attribute to change */ /** the attribute to change */
nsString mAttribute; nsString mAttribute;
@ -72,6 +86,8 @@ protected:
/** PR_TRUE if the operation is to remove mAttribute from mElement */ /** PR_TRUE if the operation is to remove mAttribute from mElement */
PRBool mRemoveAttribute; PRBool mRemoveAttribute;
friend class TransactionFactory;
}; };
#endif #endif

Просмотреть файл

@ -17,52 +17,50 @@
*/ */
#include "CreateElementTxn.h" #include "CreateElementTxn.h"
#include "editor.h"
#include "nsIDOMNode.h"
#include "nsIDOMNodeList.h" #include "nsIDOMNodeList.h"
#include "nsIDOMDocument.h"
#include "nsIDOMElement.h"
// note that aEditor is not refcounted CreateElementTxn::CreateElementTxn()
CreateElementTxn::CreateElementTxn(nsEditor *aEditor, : EditTxn()
nsIDOMDocument *aDoc,
const nsString& aTag,
nsIDOMNode *aParent,
PRUint32 aOffsetInParent)
: EditTxn(aEditor)
{ {
mDoc = aDoc;
NS_ADDREF(mDoc);
mTag = aTag;
mParent = aParent;
NS_ADDREF(mParent);
mOffsetInParent = aOffsetInParent;
mNewNode = nsnull;
mRefNode = nsnull;
} }
nsresult CreateElementTxn::Init(nsIDOMDocument *aDoc,
const nsString& aTag,
nsIDOMNode *aParent,
PRUint32 aOffsetInParent)
{
if ((nsnull!=aDoc) && (nsnull!=aParent))
{
mDoc = aDoc;
mTag = aTag;
mParent = aParent;
mOffsetInParent = aOffsetInParent;
mNewNode = nsnull;
mRefNode = nsnull;
return NS_OK;
}
else
return NS_ERROR_NULL_POINTER;
}
CreateElementTxn::~CreateElementTxn() CreateElementTxn::~CreateElementTxn()
{ {
NS_IF_RELEASE(mDoc);
NS_IF_RELEASE(mParent);
NS_IF_RELEASE(mNewNode);
} }
nsresult CreateElementTxn::Do(void) nsresult CreateElementTxn::Do(void)
{ {
// create a new node // create a new node
nsresult result = mDoc->CreateElement(mTag, &mNewNode); nsresult result = mDoc->CreateElement(mTag, getter_AddRefs(mNewNode));
NS_ASSERTION(((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)), "could not create element."); NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewNode)), "could not create element.");
if ((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)) if ((NS_SUCCEEDED(result)) && (mNewNode))
{ {
// insert the new node // insert the new node
nsIDOMNode *resultNode=nsnull; nsCOMPtr<nsIDOMNode> resultNode;
if (CreateElementTxn::eAppend==mOffsetInParent) if (CreateElementTxn::eAppend==mOffsetInParent)
{ {
result = mParent->AppendChild(mNewNode, &resultNode); result = mParent->AppendChild(mNewNode, getter_AddRefs(resultNode));
if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode))
NS_RELEASE(resultNode); // this object already holds a ref from CreateElement
} }
else else
{ {
@ -70,12 +68,10 @@ nsresult CreateElementTxn::Do(void)
result = mParent->GetChildNodes(getter_AddRefs(childNodes)); result = mParent->GetChildNodes(getter_AddRefs(childNodes));
if ((NS_SUCCEEDED(result)) && (childNodes)) if ((NS_SUCCEEDED(result)) && (childNodes))
{ {
result = childNodes->Item(mOffsetInParent, &mRefNode); result = childNodes->Item(mOffsetInParent, getter_AddRefs(mRefNode));
if ((NS_SUCCEEDED(result)) && (nsnull!=mRefNode)) if ((NS_SUCCEEDED(result)) && (mRefNode))
{ {
result = mParent->InsertBefore(mNewNode, mRefNode, &resultNode); result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode));
if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode))
NS_RELEASE(resultNode); // this object already holds a ref from CreateElement
} }
} }
} }
@ -85,20 +81,16 @@ nsresult CreateElementTxn::Do(void)
nsresult CreateElementTxn::Undo(void) nsresult CreateElementTxn::Undo(void)
{ {
nsIDOMNode *resultNode=nsnull; nsCOMPtr<nsIDOMNode> resultNode;
nsresult result = mParent->RemoveChild(mNewNode, &resultNode); nsresult result = mParent->RemoveChild(mNewNode, getter_AddRefs(resultNode));
if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode))
NS_RELEASE(resultNode);
return result; return result;
} }
nsresult CreateElementTxn::Redo(void) nsresult CreateElementTxn::Redo(void)
{ {
nsIDOMNode *resultNode=nsnull; nsCOMPtr<nsIDOMNode> resultNode;
nsresult result = mParent->InsertBefore(mNewNode, mRefNode, &resultNode); nsresult result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode));
if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode)) return result;
NS_RELEASE(resultNode);
return result;
} }
nsresult CreateElementTxn::GetIsTransient(PRBool *aIsTransient) nsresult CreateElementTxn::GetIsTransient(PRBool *aIsTransient)

Просмотреть файл

@ -20,10 +20,15 @@
#define CreateElementTxn_h__ #define CreateElementTxn_h__
#include "EditTxn.h" #include "EditTxn.h"
#include "nsIDOMDocument.h"
#include "nsIDOMNode.h"
#include "nsIDOMElement.h"
#include "nsCOMPtr.h"
class nsIDOMDocument; #define CREATE_ELEMENT_TXN_IID \
class nsIDOMNode; {/* 7a6393c0-ac48-11d2-86d8-000064657374 */ \
class nsIDOMElement; 0x7a6393c0, 0xac48, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* A transaction that creates a new node in the content tree. * A transaction that creates a new node in the content tree.
@ -34,11 +39,15 @@ public:
enum { eAppend=-1 }; enum { eAppend=-1 };
CreateElementTxn(nsEditor *aEditor, virtual nsresult Init(nsIDOMDocument *aDoc,
nsIDOMDocument *aDoc, const nsString& aTag,
const nsString& aTag, nsIDOMNode *aParent,
nsIDOMNode *aParent, PRUint32 aOffsetInParent);
PRUint32 aOffsetInParent);
private:
CreateElementTxn();
public:
virtual ~CreateElementTxn(); virtual ~CreateElementTxn();
@ -61,22 +70,24 @@ public:
protected: protected:
/** the document into which the new node will be inserted */ /** the document into which the new node will be inserted */
nsIDOMDocument *mDoc; nsCOMPtr<nsIDOMDocument> mDoc;
/** the tag (mapping to object type) for the new element */ /** the tag (mapping to object type) for the new element */
nsString mTag; nsString mTag;
/** the node into which the new node will be inserted */ /** the node into which the new node will be inserted */
nsIDOMNode *mParent; nsCOMPtr<nsIDOMNode> mParent;
/** the index in mParent for the new node */ /** the index in mParent for the new node */
PRUint32 mOffsetInParent; PRUint32 mOffsetInParent;
/** the new node to insert */ /** the new node to insert */
nsIDOMElement *mNewNode; nsCOMPtr<nsIDOMElement> mNewNode;
/** the node we will insert mNewNode before. We compute this ourselves. */ /** the node we will insert mNewNode before. We compute this ourselves. */
nsIDOMNode *mRefNode; nsCOMPtr<nsIDOMNode> mRefNode;
friend class TransactionFactory;
}; };

Просмотреть файл

@ -17,30 +17,32 @@
*/ */
#include "DeleteElementTxn.h" #include "DeleteElementTxn.h"
#include "editor.h" #ifdef NS_DEBUG
#include "nsIDOMDocument.h"
#include "nsIDOMElement.h" #include "nsIDOMElement.h"
#endif
// note that aEditor is not refcounted
DeleteElementTxn::DeleteElementTxn(nsEditor * aEditor, DeleteElementTxn::DeleteElementTxn()
nsIDOMDocument *aDoc, : EditTxn()
nsIDOMNode * aElement,
nsIDOMNode * aParent)
: EditTxn(aEditor)
{ {
mDoc = aDoc;
NS_ADDREF(mDoc);
mElement = aElement;
NS_ADDREF(mElement);
mParent = aParent;
NS_ADDREF(mParent);
} }
nsresult DeleteElementTxn::Init(nsIDOMNode *aElement,
nsIDOMNode *aParent)
{
if ((nsnull!=aElement) && (nsnull!=aParent))
{
mElement = aElement;
mParent = aParent;
return NS_OK;
}
else
return NS_ERROR_NULL_POINTER;
}
DeleteElementTxn::~DeleteElementTxn() DeleteElementTxn::~DeleteElementTxn()
{ {
NS_IF_RELEASE(mDoc);
NS_IF_RELEASE(mParent);
NS_IF_RELEASE(mElement);
} }
nsresult DeleteElementTxn::Do(void) nsresult DeleteElementTxn::Do(void)
@ -48,11 +50,32 @@ nsresult DeleteElementTxn::Do(void)
if (!mParent || !mElement) if (!mParent || !mElement)
return NS_ERROR_NULL_POINTER; return NS_ERROR_NULL_POINTER;
#ifdef NS_DEBUG #ifdef NS_DEBUG
// begin debug output
nsCOMPtr<nsIDOMElement> element=mElement;
nsAutoString elementTag="text node";
if (element)
element->GetTagName(elementTag);
nsCOMPtr<nsIDOMElement> parentElement=mParent;
nsAutoString parentElementTag="text node";
if (parentElement)
parentElement->GetTagName(parentElementTag);
char *c, *p;
c = elementTag.ToNewCString();
p = parentElementTag.ToNewCString();
if (c&&p)
{
printf("DeleteElementTxn: deleting child %s from parent %s\n", c, p);
delete [] c;
delete [] p;
}
// end debug output
// begin sanity check 1: parent-child relationship
nsresult testResult; nsresult testResult;
nsCOMPtr<nsIDOMNode> parentNode; nsCOMPtr<nsIDOMNode> parentNode;
testResult = mElement->GetParentNode(getter_AddRefs(parentNode)); testResult = mElement->GetParentNode(getter_AddRefs(parentNode));
NS_ASSERTION((NS_SUCCEEDED(testResult)), "bad mElement, couldn't get parent"); NS_ASSERTION((NS_SUCCEEDED(testResult)), "bad mElement, couldn't get parent");
NS_ASSERTION((parentNode==mParent), "bad mParent, mParent!=mElement->GetParent() "); NS_ASSERTION((parentNode==mParent), "bad mParent, mParent!=mElement->GetParent() ");
// end sanity check 1.
#endif #endif
// remember which child mElement was (by remembering which child was next) // remember which child mElement was (by remembering which child was next)

Просмотреть файл

@ -23,8 +23,10 @@
#include "nsIDOMNode.h" #include "nsIDOMNode.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
class nsIDOMDocument; #define DELETE_ELEMENT_TXN_IID \
class nsIDOMElement; {/* 6fd77770-ac49-11d2-86d8-000064657374 */ \
0x6fd77770, 0xac49, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* A transaction that deletes a single element * A transaction that deletes a single element
@ -33,10 +35,13 @@ class DeleteElementTxn : public EditTxn
{ {
public: public:
DeleteElementTxn(nsEditor *aEditor, virtual nsresult Init(nsIDOMNode *aElement,
nsIDOMDocument *aDoc, nsIDOMNode *aParent);
nsIDOMNode *aElement,
nsIDOMNode *aParent); private:
DeleteElementTxn();
public:
virtual ~DeleteElementTxn(); virtual ~DeleteElementTxn();
@ -58,14 +63,11 @@ public:
protected: protected:
/** the document into which the new node will be inserted */
nsIDOMDocument *mDoc;
/** the element to delete */ /** the element to delete */
nsIDOMNode *mElement; nsCOMPtr<nsIDOMNode> mElement;
/** the node into which the new node will be inserted */ /** the node into which the new node will be inserted */
nsIDOMNode *mParent; nsCOMPtr<nsIDOMNode> mParent;
/** the index in mParent for the new node */ /** the index in mParent for the new node */
PRUint32 mOffsetInParent; PRUint32 mOffsetInParent;
@ -73,6 +75,8 @@ protected:
/** the node we will insert mNewNode before. We compute this ourselves. */ /** the node we will insert mNewNode before. We compute this ourselves. */
nsCOMPtr<nsIDOMNode> mRefNode; nsCOMPtr<nsIDOMNode> mRefNode;
friend class TransactionFactory;
}; };
#endif #endif

Просмотреть файл

@ -17,18 +17,73 @@
*/ */
#include "DeleteRangeTxn.h" #include "DeleteRangeTxn.h"
#include "editor.h"
#include "nsIDOMRange.h" #include "nsIDOMRange.h"
#include "nsIDOMCharacterData.h"
#include "nsIDOMNodeList.h"
#include "DeleteTextTxn.h"
#include "DeleteElementTxn.h"
#include "TransactionFactory.h"
#include "nsISupportsArray.h"
static NS_DEFINE_IID(kDeleteTextTxnIID, DELETE_TEXT_TXN_IID);
static NS_DEFINE_IID(kDeleteElementTxnIID, DELETE_ELEMENT_TXN_IID);
// note that aEditor is not refcounted // note that aEditor is not refcounted
DeleteRangeTxn::DeleteRangeTxn(nsEditor *aEditor, DeleteRangeTxn::DeleteRangeTxn()
nsIDOMRange *aRange) : EditAggregateTxn()
: EditTxn(aEditor)
{ {
aRange->GetStartParent(getter_AddRefs(mStartParent)); }
aRange->GetEndParent(getter_AddRefs(mEndParent));
aRange->GetStartOffset(&mStartOffset); nsresult DeleteRangeTxn::Init(nsIDOMRange *aRange)
aRange->GetEndOffset(&mEndOffset); {
if (nsnull!=aRange)
{
nsresult result = aRange->GetStartParent(getter_AddRefs(mStartParent));
NS_ASSERTION((NS_SUCCEEDED(result)), "GetStartParent failed.");
result = aRange->GetEndParent(getter_AddRefs(mEndParent));
NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndParent failed.");
result = aRange->GetStartOffset(&mStartOffset);
NS_ASSERTION((NS_SUCCEEDED(result)), "GetStartOffset failed.");
result = aRange->GetEndOffset(&mEndOffset);
NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndOffset failed.");
result = aRange->GetCommonParent(getter_AddRefs(mCommonParent));
NS_ASSERTION((NS_SUCCEEDED(result)), "GetCommonParent failed.");
#ifdef NS_DEBUG
PRUint32 count;
nsCOMPtr<nsIDOMCharacterData> textNode;
textNode = mStartParent;
if (textNode)
textNode->GetLength(&count);
else
{
nsCOMPtr<nsIDOMNodeList> children;
result = mStartParent->GetChildNodes(getter_AddRefs(children));
NS_ASSERTION(((NS_SUCCEEDED(result)) && children), "bad start child list");
children->GetLength(&count);
}
NS_ASSERTION(mStartOffset<count, "bad start offset");
textNode = mEndParent;
if (textNode)
textNode->GetLength(&count);
else
{
nsCOMPtr<nsIDOMNodeList> children;
result = mEndParent->GetChildNodes(getter_AddRefs(children));
NS_ASSERTION(((NS_SUCCEEDED(result)) && children), "bad end child list");
children->GetLength(&count);
}
NS_ASSERTION(mEndOffset<count, "bad end offset");
printf ("DeleteRange: %d of %p to %d of %p\n",
mStartOffset, (void *)mStartParent, mEndOffset, (void *)mEndParent);
#endif
return result;
}
else
return NS_ERROR_NULL_POINTER;
} }
DeleteRangeTxn::~DeleteRangeTxn() DeleteRangeTxn::~DeleteRangeTxn()
@ -37,28 +92,59 @@ DeleteRangeTxn::~DeleteRangeTxn()
nsresult DeleteRangeTxn::Do(void) nsresult DeleteRangeTxn::Do(void)
{ {
if (!mStartParent || !mEndParent) if (!mStartParent || !mEndParent || !mCommonParent)
return NS_ERROR_NULL_POINTER; return NS_ERROR_NULL_POINTER;
nsresult result; nsresult result;
// build the child transactions
if (mStartParent==mEndParent)
{ // the selection begins and ends in the same node
result = CreateTxnsToDeleteBetween(mStartParent, mStartOffset, mEndOffset);
}
else
{ // the selection ends in a different node from where it started
// delete the relevant content in the start node
result = CreateTxnsToDeleteContent(mStartParent, mStartOffset, nsIEditor::eLTR);
if (NS_SUCCEEDED(result))
{
// delete the intervening nodes
result = CreateTxnsToDeleteNodesBetween(mCommonParent, mStartParent, mEndParent);
if (NS_SUCCEEDED(result))
{
// delete the relevant content in the end node
result = CreateTxnsToDeleteContent(mEndParent, mEndOffset, nsIEditor::eRTL);
if (NS_SUCCEEDED(result))
{ // now we have all our child transactions, do them
result = EditAggregateTxn::Do();
}
}
}
}
// if we've successfully built this aggregate transaction, then do it.
if (NS_SUCCEEDED(result))
result = EditAggregateTxn::Do();
return result; return result;
} }
nsresult DeleteRangeTxn::Undo(void) nsresult DeleteRangeTxn::Undo(void)
{ {
if (!mStartParent || !mEndParent) if (!mStartParent || !mEndParent || !mCommonParent)
return NS_ERROR_NULL_POINTER; return NS_ERROR_NULL_POINTER;
nsresult result; nsresult result = EditAggregateTxn::Undo();
return result; return result;
} }
nsresult DeleteRangeTxn::Redo(void) nsresult DeleteRangeTxn::Redo(void)
{ {
if (!mStartParent || !mEndParent) if (!mStartParent || !mEndParent || !mCommonParent)
return NS_ERROR_NULL_POINTER; return NS_ERROR_NULL_POINTER;
nsresult result; nsresult result = EditAggregateTxn::Redo();
return result; return result;
} }
@ -98,3 +184,265 @@ nsresult DeleteRangeTxn::GetRedoString(nsString **aString)
} }
return NS_OK; return NS_OK;
} }
nsresult DeleteRangeTxn::CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent,
PRUint32 aStartOffset,
PRUint32 aEndOffset)
{
nsresult result;
// see what kind of node we have
nsCOMPtr<nsIDOMCharacterData> textNode;
textNode = aStartParent; // this uses implicit nsCOMPtr QI
if (textNode)
{ // if the node is a text node, then delete text content
DeleteTextTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(textNode, aStartOffset, (aEndOffset-aStartOffset)+1);
AppendChild(txn);
}
}
else
{
PRUint32 childCount;
nsCOMPtr<nsIDOMNodeList> children;
result = aStartParent->GetChildNodes(getter_AddRefs(children));
if ((NS_SUCCEEDED(result)) && children)
{
children->GetLength(&childCount);
NS_ASSERTION(aEndOffset<childCount, "bad aEndOffset");
PRUint32 i;
for (i=aStartOffset; i<=aEndOffset; i++)
{
nsCOMPtr<nsIDOMNode> child;
result = children->Item(i, getter_AddRefs(child));
if ((NS_SUCCEEDED(result)) && child)
{
DeleteElementTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(child, aStartParent);
AppendChild(txn);
}
else
return NS_ERROR_NULL_POINTER;
}
}
}
}
return result;
}
nsresult DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent,
PRUint32 aOffset,
nsIEditor::Direction aDir)
{
nsresult result;
// see what kind of node we have
nsCOMPtr<nsIDOMCharacterData> textNode;
textNode = aParent; // this uses implicit nsCOMPtr QI
if (textNode)
{ // if the node is a text node, then delete text content
PRUint32 start, numToDelete;
if (nsIEditor::eLTR==aDir)
{
start=aOffset;
textNode->GetLength(&numToDelete);
numToDelete -= (aOffset+1);
}
else
{
start=0;
numToDelete=aOffset;
}
DeleteTextTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(textNode, start, numToDelete);
AppendChild(txn);
}
else
return NS_ERROR_NULL_POINTER;
}
else
{ // we have an interior node, so delete some of its children
if (nsIEditor::eLTR==aDir)
{ // delete from aOffset to end
PRUint32 childCount;
nsCOMPtr<nsIDOMNodeList> children;
result = aParent->GetChildNodes(getter_AddRefs(children));
if ((NS_SUCCEEDED(result)) && children)
{
children->GetLength(&childCount);
PRUint32 i;
for (i=aOffset; i<childCount; i++)
{
nsCOMPtr<nsIDOMNode> child;
result = children->Item(i, getter_AddRefs(child));
if ((NS_SUCCEEDED(result)) && child)
{
DeleteElementTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(child, aParent);
AppendChild(txn);
}
else
return NS_ERROR_NULL_POINTER;
}
}
}
}
else
{ // delete from 0 to aOffset
nsCOMPtr<nsIDOMNode> child;
result = aParent->GetFirstChild(getter_AddRefs(child));
for (PRUint32 i=0; i<aOffset; i++)
{
if ((NS_SUCCEEDED(result)) && child)
{
DeleteElementTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(child, aParent);
AppendChild(txn);
}
else
return NS_ERROR_NULL_POINTER;
}
nsCOMPtr<nsIDOMNode> temp = child;
result = temp->GetNextSibling(getter_AddRefs(child));
}
}
}
return result;
}
nsresult DeleteRangeTxn::CreateTxnsToDeleteNodesBetween(nsIDOMNode *aCommonParent,
nsIDOMNode *aFirstChild,
nsIDOMNode *aLastChild)
{
nsresult result;
PRBool needToProcessLastChild=PR_TRUE; // set to false if we discover we can delete all required nodes by just walking up aFirstChild's parent list
nsCOMPtr<nsIDOMNode> parent; // the current parent in the iteration up the ancestors
nsCOMPtr<nsIDOMNode> child; // the current child of parent
nsISupportsArray *ancestorList; // the ancestorList of the other endpoint, used to gate deletion
NS_NewISupportsArray(&ancestorList);
if (nsnull==ancestorList)
return NS_ERROR_NULL_POINTER;
// Walk up the parent list of aFirstChild to aCommonParent,
// deleting all siblings to the right of the ancestors of aFirstChild.
BuildAncestorList(aLastChild, ancestorList);
child = aFirstChild;
result = child->GetParentNode(getter_AddRefs(parent));
while ((NS_SUCCEEDED(result)) && parent)
{
while ((NS_SUCCEEDED(result)) && child)
{ // this loop starts with the first sibling of an ancestor of aFirstChild
nsCOMPtr<nsIDOMNode> temp = child;
result = temp->GetNextSibling(getter_AddRefs(child));
if ((NS_SUCCEEDED(result)) && child)
{
if (child==aLastChild)
{ // aFirstChild and aLastChild have the same parent, and we've reached aLastChild
needToProcessLastChild = PR_FALSE;
break;
}
// test if child is an ancestor of the other node. If it is, don't process this parent anymore
PRInt32 index;
index = ancestorList->IndexOf((nsISupports*)child);
if (-1!=index)
break;
DeleteElementTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(child, parent);
AppendChild(txn);
}
else
return NS_ERROR_NULL_POINTER;
}
}
if (parent==aCommonParent)
break;
child = parent;
nsCOMPtr<nsIDOMNode> temp=parent;
result = temp->GetParentNode(getter_AddRefs(parent));
}
// Walk up the parent list of aLastChild to aCommonParent,
// deleting all siblings to the left of the ancestors of aLastChild.
BuildAncestorList(aFirstChild, ancestorList);
if (PR_TRUE==needToProcessLastChild)
{
child = aLastChild;
result = child->GetParentNode(getter_AddRefs(parent));
while ((NS_SUCCEEDED(result)) && parent)
{
while ((NS_SUCCEEDED(result)) && child)
{ // this loop starts with the first sibling of an ancestor of aFirstChild
nsCOMPtr<nsIDOMNode> temp = child;
result = temp->GetPreviousSibling(getter_AddRefs(child));
if ((NS_SUCCEEDED(result)) && child)
{
// test if child is an ancestor of the other node. If it is, don't process this parent anymore
PRInt32 index;
index = ancestorList->IndexOf((nsISupports*)child);
if (-1!=index)
break;
DeleteElementTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(child, parent);
AppendChild(txn);
}
else
return NS_ERROR_NULL_POINTER;
}
}
if (parent==aCommonParent)
break;
child = parent;
nsCOMPtr<nsIDOMNode> temp=parent;
result = temp->GetParentNode(getter_AddRefs(parent));
}
}
NS_RELEASE(ancestorList);
return result;
}
nsresult DeleteRangeTxn::BuildAncestorList(nsIDOMNode *aNode, nsISupportsArray *aList)
{
nsresult result=NS_OK;
if (nsnull!=aNode && nsnull!=aList)
{
aList->Clear();
nsCOMPtr<nsIDOMNode> parent;
nsCOMPtr<nsIDOMNode> child = aNode;
result = child->GetParentNode(getter_AddRefs(parent));
while ((NS_SUCCEEDED(result)) && child && parent)
{
nsISupports * parentAsISupports;
parent->QueryInterface(nsISupports::IID(), (void **)&parentAsISupports);
aList->AppendElement(parentAsISupports);
child = parent;
nsCOMPtr<nsIDOMNode> temp=parent;
result = temp->GetParentNode(getter_AddRefs(parent));
}
}
else
result = NS_ERROR_NULL_POINTER;
return result;
}

Просмотреть файл

@ -19,22 +19,33 @@
#ifndef DeleteRangeTxn_h__ #ifndef DeleteRangeTxn_h__
#define DeleteRangeTxn_h__ #define DeleteRangeTxn_h__
#include "EditTxn.h" #include "EditAggregateTxn.h"
#include "nsIDOMNode.h" #include "nsIDOMNode.h"
#include "nsIEditor.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
class nsIDOMDocument; class nsIDOMDocument;
class nsIDOMRange; class nsIDOMRange;
class nsISupportsArray;
#define DELETE_RANGE_TXN_IID \
{/* 5ec6b260-ac49-11d2-86d8-000064657374 */ \
0x5ec6b260, 0xac49, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* A transaction that deletes an entire range in the content tree * A transaction that deletes an entire range in the content tree
*/ */
class DeleteRangeTxn : public EditTxn class DeleteRangeTxn : public EditAggregateTxn
{ {
public: public:
DeleteRangeTxn(nsEditor *aEditor, virtual nsresult Init(nsIDOMRange *aRange);
nsIDOMRange *aRange);
private:
DeleteRangeTxn();
public:
virtual ~DeleteRangeTxn(); virtual ~DeleteRangeTxn();
@ -54,6 +65,23 @@ public:
virtual nsresult GetRedoString(nsString **aString); virtual nsresult GetRedoString(nsString **aString);
protected:
virtual nsresult CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent,
PRUint32 aStartOffset,
PRUint32 aEndOffset);
virtual nsresult CreateTxnsToDeleteNodesBetween(nsIDOMNode *aParent,
nsIDOMNode *aFirstChild,
nsIDOMNode *aLastChild);
virtual nsresult CreateTxnsToDeleteContent(nsIDOMNode *aParent,
PRUint32 aOffset,
nsIEditor::Direction aDir);
virtual nsresult BuildAncestorList(nsIDOMNode *aNode,
nsISupportsArray *aList);
protected: protected:
/** p1 in the range */ /** p1 in the range */
@ -65,9 +93,14 @@ protected:
/** p2 in the range */ /** p2 in the range */
nsCOMPtr<nsIDOMNode> mEndParent; nsCOMPtr<nsIDOMNode> mEndParent;
/** the closest common parent of p1 and p2 */
nsCOMPtr<nsIDOMNode> mCommonParent;
/** p2 offset */ /** p2 offset */
PRInt32 mEndOffset; PRInt32 mEndOffset;
friend class TransactionFactory;
}; };
#endif #endif

Просмотреть файл

@ -17,20 +17,23 @@
*/ */
#include "DeleteTextTxn.h" #include "DeleteTextTxn.h"
#include "editor.h"
#include "nsIDOMCharacterData.h" #include "nsIDOMCharacterData.h"
// note that aEditor is not refcounted
DeleteTextTxn::DeleteTextTxn(nsEditor *aEditor, DeleteTextTxn::DeleteTextTxn()
nsIDOMCharacterData *aElement, : EditTxn()
{
}
nsresult DeleteTextTxn::Init(nsIDOMCharacterData *aElement,
PRUint32 aOffset, PRUint32 aOffset,
PRUint32 aNumCharsToDelete) PRUint32 aNumCharsToDelete)
: EditTxn(aEditor)
{ {
mElement = aElement; mElement = aElement;
mOffset = aOffset; mOffset = aOffset;
mNumCharsToDelete = aNumCharsToDelete; mNumCharsToDelete = aNumCharsToDelete;
mDeletedText = ""; mDeletedText = "";
return NS_OK;
} }
nsresult DeleteTextTxn::Do(void) nsresult DeleteTextTxn::Do(void)

Просмотреть файл

@ -20,8 +20,13 @@
#define DeleteTextTxn_h__ #define DeleteTextTxn_h__
#include "EditTxn.h" #include "EditTxn.h"
#include "nsIDOMCharacterData.h"
#include "nsCOMPtr.h"
class nsIDOMCharacterData; #define DELETE_TEXT_TXN_IID \
{/* 4d3a2720-ac49-11d2-86d8-000064657374 */ \
0x4d3a2720, 0xac49, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* A transaction that changes an attribute of a content node. * A transaction that changes an attribute of a content node.
@ -31,10 +36,14 @@ class DeleteTextTxn : public EditTxn
{ {
public: public:
DeleteTextTxn(nsEditor *aEditor, virtual nsresult Init(nsIDOMCharacterData *aElement,
nsIDOMCharacterData *aElement, PRUint32 aOffset,
PRUint32 aOffset, PRUint32 aNumCharsToDelete);
PRUint32 aNumCharsToDelete);
private:
DeleteTextTxn();
public:
virtual nsresult Do(void); virtual nsresult Do(void);
@ -53,7 +62,7 @@ public:
protected: protected:
/** the text element to operate upon */ /** the text element to operate upon */
nsIDOMCharacterData *mElement; nsCOMPtr<nsIDOMCharacterData> mElement;
/** the offset into mElement where the deletion is to take place */ /** the offset into mElement where the deletion is to take place */
PRUint32 mOffset; PRUint32 mOffset;
@ -64,6 +73,8 @@ protected:
/** the text that was deleted */ /** the text that was deleted */
nsString mDeletedText; nsString mDeletedText;
friend class TransactionFactory;
}; };
#endif #endif

Просмотреть файл

@ -0,0 +1,145 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL") you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "EditAggregateTxn.h"
#include "nsCOMPtr.h"
#include "nsIDOMNode.h"
#include "nsVoidArray.h"
EditAggregateTxn::EditAggregateTxn()
: EditTxn()
{
mChildren = new nsVoidArray();
}
EditAggregateTxn::~EditAggregateTxn()
{
if (nsnull!=mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
for (i=0; i<count; i++)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
NS_IF_RELEASE(txn);
}
delete mChildren;
}
}
nsresult EditAggregateTxn::Do(void)
{
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
if (nsnull!=mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
for (i=0; i<count; i++)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
result = txn->Do();
if (NS_FAILED(result))
break;
}
}
return result;
}
nsresult EditAggregateTxn::Undo(void)
{
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
if (nsnull!=mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
// undo goes through children backwards
for (i=count-1; i>=0; i--)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
result = txn->Undo();
if (NS_FAILED(result))
break;
}
}
return result;
}
nsresult EditAggregateTxn::Redo(void)
{
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
if (nsnull!=mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
for (i=0; i<count; i++)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
result = txn->Redo();
if (NS_FAILED(result))
break;
}
}
return result;
}
nsresult EditAggregateTxn::GetIsTransient(PRBool *aIsTransient)
{
if (nsnull!=aIsTransient)
*aIsTransient = PR_TRUE;
return NS_OK;
}
nsresult EditAggregateTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction)
{
return NS_OK;
}
nsresult EditAggregateTxn::Write(nsIOutputStream *aOutputStream)
{
return NS_OK;
}
nsresult EditAggregateTxn::GetUndoString(nsString **aString)
{
if (nsnull!=aString)
*aString=nsnull;
return NS_OK;
}
nsresult EditAggregateTxn::GetRedoString(nsString **aString)
{
if (nsnull!=aString)
*aString=nsnull;
return NS_OK;
}
nsresult EditAggregateTxn::AppendChild(EditTxn *aTxn)
{
if ((nsnull!=mChildren) && (nsnull!=aTxn))
{
mChildren->AppendElement(aTxn);
return NS_OK;
}
return NS_ERROR_NULL_POINTER;
}

Просмотреть файл

@ -0,0 +1,67 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef EditAggregateTxn_h__
#define EditAggregateTxn_h__
#include "EditTxn.h"
#define EDIT_AGGREGATE_TXN_IID \
{/* 345921a0-ac49-11d2-86d8-000064657374 */ \
0x345921a0, 0xac49, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
class nsVoidArray;
/**
* base class for all document editing transactions that require aggregation.
* provides a list of child transactions.
*/
class EditAggregateTxn : public EditTxn
{
public:
EditAggregateTxn();
virtual ~EditAggregateTxn();
virtual nsresult Do(void);
virtual nsresult Undo(void);
virtual nsresult Redo(void);
virtual nsresult GetIsTransient(PRBool *aIsTransient);
virtual nsresult Merge(PRBool *aDidMerge, nsITransaction *aTransaction);
virtual nsresult Write(nsIOutputStream *aOutputStream);
virtual nsresult GetUndoString(nsString **aString);
virtual nsresult GetRedoString(nsString **aString);
virtual nsresult AppendChild(EditTxn *aTxn);
protected:
nsVoidArray *mChildren;
};
#endif

Просмотреть файл

@ -28,10 +28,8 @@ NS_IMPL_ADDREF(EditTxn)
NS_IMPL_RELEASE(EditTxn) NS_IMPL_RELEASE(EditTxn)
// note that aEditor is not refcounted // note that aEditor is not refcounted
EditTxn::EditTxn(nsEditor *aEditor) EditTxn::EditTxn()
{ {
NS_ASSERTION(nsnull!=aEditor, "null aEditor arg to EditTxn constructor");
mEditor = aEditor;
} }
nsresult EditTxn::Do(void) nsresult EditTxn::Do(void)

Просмотреть файл

@ -20,13 +20,17 @@
#define EditTxn_h__ #define EditTxn_h__
#include "nsITransaction.h" #include "nsITransaction.h"
class nsEditor;
class nsIDOMNode; class nsIDOMNode;
#define EDIT_TXN_IID \
{/* c5ea31b0-ac48-11d2-86d8-000064657374 */ \
0xc5ea31b0, 0xac48, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* base class for all document editing transactions. * base class for all document editing transactions.
* provides access to the nsEditor that created this transaction. * provides default concrete behavior for all nsITransaction methods.
*/ */
class EditTxn : public nsITransaction class EditTxn : public nsITransaction
{ {
@ -34,7 +38,7 @@ public:
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
EditTxn(nsEditor *aEditor); EditTxn();
virtual nsresult Do(void); virtual nsresult Do(void);
@ -52,10 +56,6 @@ public:
virtual nsresult GetRedoString(nsString **aString); virtual nsresult GetRedoString(nsString **aString);
protected:
nsEditor *mEditor;
}; };
#endif #endif

Просмотреть файл

@ -20,18 +20,22 @@
#include "editor.h" #include "editor.h"
#include "nsIDOMCharacterData.h" #include "nsIDOMCharacterData.h"
static NS_DEFINE_IID(kInsertTextTxnIID, INSERTTEXTTXN_IID); static NS_DEFINE_IID(kInsertTextTxnIID, INSERT_TEXT_TXN_IID);
// note that aEditor is not refcounted
InsertTextTxn::InsertTextTxn(nsEditor *aEditor, InsertTextTxn::InsertTextTxn()
nsIDOMCharacterData *aElement, : EditTxn()
{
}
nsresult InsertTextTxn::Init(nsIDOMCharacterData *aElement,
PRUint32 aOffset, PRUint32 aOffset,
const nsString& aStringToInsert) const nsString& aStringToInsert)
: EditTxn(aEditor)
{ {
mElement = aElement; mElement = aElement;
mOffset = aOffset; mOffset = aOffset;
mStringToInsert = aStringToInsert; mStringToInsert = aStringToInsert;
return NS_OK;
} }
nsresult InsertTextTxn::Do(void) nsresult InsertTextTxn::Do(void)
@ -60,11 +64,11 @@ nsresult InsertTextTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction)
if ((nsnull!=aDidMerge) && (nsnull!=aTransaction)) if ((nsnull!=aDidMerge) && (nsnull!=aTransaction))
{ {
// if aTransaction isa InsertTextTxn, absorb it // if aTransaction isa InsertTextTxn, absorb it
nsCOMPtr<InsertTextTxn> otherTxn; nsCOMPtr<InsertTextTxn> otherTxn = aTransaction;
nsresult result = aTransaction->QueryInterface(kInsertTextTxnIID, getter_AddRefs(otherTxn)); nsresult result=NS_OK;// = aTransaction->QueryInterface(kInsertTextTxnIID, getter_AddRefs(otherTxn));
if (NS_SUCCEEDED(result) && (otherTxn)) if (NS_SUCCEEDED(result) && (otherTxn))
{ {
nsString otherData; nsAutoString otherData;
otherTxn->GetData(otherData); otherTxn->GetData(otherData);
mStringToInsert += otherData; mStringToInsert += otherData;
} }

Просмотреть файл

@ -20,14 +20,14 @@
#define InsertTextTxn_h__ #define InsertTextTxn_h__
#include "EditTxn.h" #include "EditTxn.h"
#include "nsIDOMCharacterData.h"
#include "nsCOMPtr.h"
#define INSERTTEXTTXN_IID \ #define INSERT_TEXT_TXN_IID \
{/* 93276f00-ab2c-11d2-8f4b-006008159b0c*/ \ {/* 93276f00-ab2c-11d2-8f4b-006008159b0c*/ \
0x93276f00, 0xab2c, 0x11d2, \ 0x93276f00, 0xab2c, 0x11d2, \
{0x8f, 0xb4, 0x0, 0x60, 0x8, 0x15, 0x9b, 0xc} } {0x8f, 0xb4, 0x0, 0x60, 0x8, 0x15, 0x9b, 0xc} }
class nsIDOMCharacterData;
/** /**
* A transaction that changes an attribute of a content node. * A transaction that changes an attribute of a content node.
* This transaction covers add, remove, and change attribute. * This transaction covers add, remove, and change attribute.
@ -36,15 +36,13 @@ class InsertTextTxn : public EditTxn
{ {
public: public:
InsertTextTxn(nsEditor *aEditor, virtual nsresult Init(nsIDOMCharacterData *aElement,
nsIDOMCharacterData *aElement, PRUint32 aOffset,
PRUint32 aOffset, const nsString& aStringToInsert);
const nsString& aStringToInsert);
private: private:
// default ctor so that nsCOMPtr is happy InsertTextTxn();
InsertTextTxn() : EditTxn(nsnull) {}
public: public:
@ -67,7 +65,7 @@ public:
// override QueryInterface to handle InsertTextTxn request // override QueryInterface to handle InsertTextTxn request
NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
static const nsIID& IID() { static nsIID iid = INSERTTEXTTXN_IID; return iid; } static const nsIID& IID() { static nsIID iid = INSERT_TEXT_TXN_IID; return iid; }
virtual nsresult GetData(nsString& aResult); virtual nsresult GetData(nsString& aResult);
@ -75,7 +73,7 @@ public:
protected: protected:
/** the text element to operate upon */ /** the text element to operate upon */
nsIDOMCharacterData *mElement; nsCOMPtr<nsIDOMCharacterData> mElement;
/** the offset into mElement where the insertion is to take place */ /** the offset into mElement where the insertion is to take place */
PRUint32 mOffset; PRUint32 mOffset;
@ -86,6 +84,8 @@ protected:
/** the text to insert into mElement at mOffset */ /** the text to insert into mElement at mOffset */
nsString mStringToInsert; nsString mStringToInsert;
friend class TransactionFactory;
}; };
#endif #endif

Просмотреть файл

@ -0,0 +1,122 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "JoinElementTxn.h"
#include "nsIDOMNodeList.h"
#include "editor.h"
JoinElementTxn::JoinElementTxn()
: EditTxn()
{
}
nsresult JoinElementTxn::Init(nsIDOMNode *aLeftNode,
nsIDOMNode *aRightNode)
{
mLeftNode = aLeftNode;
mRightNode = aRightNode;
mOffset=0;
return NS_OK;
}
JoinElementTxn::~JoinElementTxn()
{
}
nsresult JoinElementTxn::Do(void)
{
nsresult result;
if ((mLeftNode) && (mRightNode))
{ // get the parent node
nsCOMPtr<nsIDOMNode>leftParent;
result = mLeftNode->GetParentNode(getter_AddRefs(leftParent));
if ((NS_SUCCEEDED(result)) && (leftParent))
{ // verify that mLeftNode and mRightNode have the same parent
nsCOMPtr<nsIDOMNode>rightParent;
result = mRightNode->GetParentNode(getter_AddRefs(rightParent));
if ((NS_SUCCEEDED(result)) && (rightParent))
{
if (leftParent==rightParent)
{
mParent=leftParent; // set this instance mParent.
// Other methods see a non-null mParent and know all is well
nsCOMPtr<nsIDOMNodeList> childNodes;
result = mLeftNode->GetChildNodes(getter_AddRefs(childNodes));
if ((NS_SUCCEEDED(result)) && (childNodes))
{
childNodes->GetLength(&mOffset);
}
result = nsEditor::JoinNodes(mLeftNode, mRightNode, mParent, PR_FALSE);
}
}
}
}
return result;
}
nsresult JoinElementTxn::Undo(void)
{
nsresult result = nsEditor::SplitNode(mRightNode, mOffset, mLeftNode, mParent);
return result;
}
nsresult JoinElementTxn::Redo(void)
{
nsresult result = nsEditor::JoinNodes(mLeftNode, mRightNode, mParent, PR_FALSE);
return result;
}
nsresult JoinElementTxn::GetIsTransient(PRBool *aIsTransient)
{
if (nsnull!=aIsTransient)
*aIsTransient = PR_FALSE;
return NS_OK;
}
nsresult JoinElementTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction)
{
if (nsnull!=aDidMerge)
*aDidMerge=PR_FALSE;
return NS_OK;
}
nsresult JoinElementTxn::Write(nsIOutputStream *aOutputStream)
{
return NS_OK;
}
nsresult JoinElementTxn::GetUndoString(nsString **aString)
{
if (nsnull!=aString)
{
**aString="Join Element";
}
return NS_OK;
}
nsresult JoinElementTxn::GetRedoString(nsString **aString)
{
if (nsnull!=aString)
{
**aString="Split Element";
}
return NS_OK;
}

Просмотреть файл

@ -0,0 +1,86 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef JoinElementTxn_h__
#define JoinElementTxn_h__
#include "EditTxn.h"
#include "nsIDOMNode.h"
#include "nsCOMPtr.h"
#define JOIN_ELEMENT_TXN_IID \
{/* 9bc5f9f0-ac48-11d2-86d8-000064657374 */ \
0x9bc5f9f0, 0xac48, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/**
* A transaction that joins two elements E1 and E2 into a single node E.
* The children of E are the children of E1 followed by the children of E2.
*/
class JoinElementTxn : public EditTxn
{
public:
virtual nsresult Init(nsIDOMNode *aLeftNode,
nsIDOMNode *aRightNode);
protected:
JoinElementTxn();
public:
virtual ~JoinElementTxn();
virtual nsresult Do(void);
virtual nsresult Undo(void);
virtual nsresult Redo(void);
virtual nsresult GetIsTransient(PRBool *aIsTransient);
virtual nsresult Merge(PRBool *aDidMerge, nsITransaction *aTransaction);
virtual nsresult Write(nsIOutputStream *aOutputStream);
virtual nsresult GetUndoString(nsString **aString);
virtual nsresult GetRedoString(nsString **aString);
protected:
/** the elements to operate upon.
* After the merge, mRightNode remains and mLeftNode is removed from the content tree.
*/
nsCOMPtr<nsIDOMNode> mLeftNode;
nsCOMPtr<nsIDOMNode> mRightNode;
/** the offset into mNode where the children of mElement are split (for undo).<BR>
* mOffset is the index of the last child in the left node.
* -1 means the left node gets no children.
*/
PRUint32 mOffset;
/** the parent node containing mLeftNode and mRightNode */
nsCOMPtr<nsIDOMNode> mParent;
friend class TransactionFactory;
};
#endif

Просмотреть файл

@ -30,12 +30,15 @@ CPPSRCS = \
nsEditFactory.cpp \ nsEditFactory.cpp \
ChangeAttributeTxn.cpp \ ChangeAttributeTxn.cpp \
EditTxn.cpp \ EditTxn.cpp \
EditAggregateTxn.cpp \
InsertTextTxn.cpp \ InsertTextTxn.cpp \
DeleteTextTxn.cpp \ DeleteTextTxn.cpp \
CreateElementTxn.cpp \ CreateElementTxn.cpp \
DeleteElementTxn.cpp \ DeleteElementTxn.cpp \
DeleteRangeTxn.cpp \ DeleteRangeTxn.cpp \
SplitElementTxn.cpp \ SplitElementTxn.cpp \
JoinElementTxn.cpp \
TransactionFactory.cpp \
$(NULL) $(NULL)
MODULE = editor MODULE = editor

Просмотреть файл

@ -17,44 +17,42 @@
*/ */
#include "SplitElementTxn.h" #include "SplitElementTxn.h"
#include "editor.h"
#include "nsIDOMNode.h" #include "nsIDOMNode.h"
#include "nsIDOMElement.h" #include "nsIDOMElement.h"
#include "editor.h"
// note that aEditor is not refcounted // note that aEditor is not refcounted
SplitElementTxn::SplitElementTxn(nsEditor *aEditor, SplitElementTxn::SplitElementTxn()
nsIDOMNode *aNode, : EditTxn()
PRInt32 aOffset)
: EditTxn(aEditor)
{ {
mNode = aNode; }
NS_ADDREF(mNode);
nsresult SplitElementTxn::Init(nsIDOMNode *aNode,
PRInt32 aOffset)
{
mExistingRightNode = aNode;
mOffset = aOffset; mOffset = aOffset;
mNewNode = nsnull; return NS_OK;
mParent = nsnull;
} }
SplitElementTxn::~SplitElementTxn() SplitElementTxn::~SplitElementTxn()
{ {
NS_IF_RELEASE(mNode);
NS_IF_RELEASE(mNewNode);
NS_IF_RELEASE(mParent);
} }
nsresult SplitElementTxn::Do(void) nsresult SplitElementTxn::Do(void)
{ {
// create a new node // create a new node
nsresult result = mNode->CloneNode(PR_FALSE, &mNewNode); nsresult result = mExistingRightNode->CloneNode(PR_FALSE, getter_AddRefs(mNewLeftNode));
NS_ASSERTION(((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)), "could not create element."); NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewLeftNode)), "could not create element.");
if ((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)) if ((NS_SUCCEEDED(result)) && (mNewLeftNode))
{ {
// get the parent node // get the parent node
result = mNode->GetParentNode(&mParent); result = mExistingRightNode->GetParentNode(getter_AddRefs(mParent));
// insert the new node // insert the new node
if ((NS_SUCCEEDED(result)) && (nsnull!=mParent)) if ((NS_SUCCEEDED(result)) && (mParent))
{ {
result = mEditor->SplitNode(mNode, mOffset, mNewNode, mParent); result = nsEditor::SplitNode(mExistingRightNode, mOffset, mNewLeftNode, mParent);
} }
} }
return result; return result;
@ -63,13 +61,13 @@ nsresult SplitElementTxn::Do(void)
nsresult SplitElementTxn::Undo(void) nsresult SplitElementTxn::Undo(void)
{ {
// this assumes Do inserted the new node in front of the prior existing node // this assumes Do inserted the new node in front of the prior existing node
nsresult result = mEditor->JoinNodes(mNode, mNewNode, mParent, PR_FALSE); nsresult result = nsEditor::JoinNodes(mExistingRightNode, mNewLeftNode, mParent, PR_FALSE);
return result; return result;
} }
nsresult SplitElementTxn::Redo(void) nsresult SplitElementTxn::Redo(void)
{ {
nsresult result = mEditor->SplitNode(mNode, mOffset, mNewNode, mParent); nsresult result = nsEditor::SplitNode(mExistingRightNode, mOffset, mNewLeftNode, mParent);
return result; return result;
} }

Просмотреть файл

@ -20,10 +20,13 @@
#define SplitElementTxn_h__ #define SplitElementTxn_h__
#include "EditTxn.h" #include "EditTxn.h"
#include "nsIDOMNode.h"
#include "nsCOMPtr.h"
class nsIDOMNode; #define SPLIT_ELEMENT_TXN_IID \
class nsIDOMElement; {/* 690c6290-ac48-11d2-86d8-000064657374 */ \
class nsIDOMDocument; 0x690c6290, 0xac48, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* A transaction that splits an element E into two identical nodes, E1 and E2 * A transaction that splits an element E into two identical nodes, E1 and E2
@ -33,10 +36,12 @@ class SplitElementTxn : public EditTxn
{ {
public: public:
SplitElementTxn(nsEditor *aEditor, virtual nsresult Init (nsIDOMNode *aNode,
nsIDOMNode *aNode, PRInt32 aOffset);
PRInt32 aOffset); protected:
SplitElementTxn();
public:
virtual ~SplitElementTxn(); virtual ~SplitElementTxn();
virtual nsresult Do(void); virtual nsresult Do(void);
@ -58,7 +63,7 @@ public:
protected: protected:
/** the element to operate upon */ /** the element to operate upon */
nsIDOMNode *mNode; nsCOMPtr<nsIDOMNode> mExistingRightNode;
/** the offset into mElement where the children of mElement are split.<BR> /** the offset into mElement where the children of mElement are split.<BR>
* mOffset is the index of the last child in the left node. * mOffset is the index of the last child in the left node.
@ -67,8 +72,12 @@ protected:
PRInt32 mOffset; PRInt32 mOffset;
/** the element we create when splitting mElement */ /** the element we create when splitting mElement */
nsIDOMNode *mNewNode; nsCOMPtr<nsIDOMNode> mNewLeftNode;
nsIDOMNode *mParent;
/** the parent shared by mExistingRightNode and mNewLeftNode */
nsCOMPtr<nsIDOMNode> mParent;
friend class TransactionFactory;
}; };

Просмотреть файл

@ -0,0 +1,75 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "TransactionFactory.h"
// transactions this factory knows how to build
#include "InsertTextTxn.h"
#include "DeleteTextTxn.h"
#include "CreateElementTxn.h"
#include "DeleteElementTxn.h"
#include "DeleteRangeTxn.h"
#include "ChangeAttributeTxn.h"
#include "SplitElementTxn.h"
#include "JoinElementTxn.h"
static NS_DEFINE_IID(kInsertTextTxnIID, INSERT_TEXT_TXN_IID);
static NS_DEFINE_IID(kDeleteTextTxnIID, DELETE_TEXT_TXN_IID);
static NS_DEFINE_IID(kCreateElementTxnIID, CREATE_ELEMENT_TXN_IID);
static NS_DEFINE_IID(kDeleteElementTxnIID, DELETE_ELEMENT_TXN_IID);
static NS_DEFINE_IID(kDeleteRangeTxnIID, DELETE_RANGE_TXN_IID);
static NS_DEFINE_IID(kChangeAttributeTxnIID,CHANGE_ATTRIBUTE_TXN_IID);
static NS_DEFINE_IID(kSplitElementTxnIID, SPLIT_ELEMENT_TXN_IID);
static NS_DEFINE_IID(kJoinElementTxnIID, JOIN_ELEMENT_TXN_IID);
TransactionFactory::TransactionFactory()
{
}
TransactionFactory::~TransactionFactory()
{
}
nsresult
TransactionFactory::GetNewTransaction(REFNSIID aTxnType, EditTxn **aResult)
{
nsresult result = NS_OK;
*aResult = nsnull;
if (aTxnType.Equals(kInsertTextTxnIID))
*aResult = new InsertTextTxn();
else if (aTxnType.Equals(kDeleteTextTxnIID))
*aResult = new DeleteTextTxn();
else if (aTxnType.Equals(kCreateElementTxnIID))
*aResult = new CreateElementTxn();
else if (aTxnType.Equals(kDeleteElementTxnIID))
*aResult = new DeleteElementTxn();
else if (aTxnType.Equals(kDeleteRangeTxnIID))
*aResult = new DeleteRangeTxn();
else if (aTxnType.Equals(kChangeAttributeTxnIID))
*aResult = new ChangeAttributeTxn();
else if (aTxnType.Equals(kSplitElementTxnIID))
*aResult = new SplitElementTxn();
else if (aTxnType.Equals(kJoinElementTxnIID))
*aResult = new JoinElementTxn();
if (nsnull==*aResult)
result = NS_ERROR_INVALID_ARG;
return result;
}

Просмотреть файл

@ -0,0 +1,41 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef TransactionFactory_h__
#define TransactionFactory_h__
#include "nsISupports.h"
class EditTxn;
/**
* This class instantiates and optionally recycles edit transactions
* A recycler would be a separate static object, since this class does not get instantiated
*/
class TransactionFactory
{
protected:
TransactionFactory();
virtual ~TransactionFactory();
public:
static nsresult GetNewTransaction(REFNSIID aTxnType, EditTxn **aResult);
};
#endif

Просмотреть файл

@ -33,16 +33,22 @@
#include "nsTransactionManagerCID.h" #include "nsTransactionManagerCID.h"
#include "nsITransactionManager.h" #include "nsITransactionManager.h"
#include "nsIPresShell.h" #include "nsIPresShell.h"
#include "nsIViewManager.h"
#include "nsISelection.h" #include "nsISelection.h"
#include "nsICollection.h"
#include "nsIEnumerator.h"
#include "nsIAtom.h" #include "nsIAtom.h"
// transactions the editor knows how to build // transactions the editor knows how to build
#include "TransactionFactory.h"
#include "ChangeAttributeTxn.h" #include "ChangeAttributeTxn.h"
#include "CreateElementTxn.h" #include "CreateElementTxn.h"
#include "DeleteElementTxn.h" #include "DeleteElementTxn.h"
#include "InsertTextTxn.h" #include "InsertTextTxn.h"
#include "DeleteTextTxn.h" #include "DeleteTextTxn.h"
#include "DeleteRangeTxn.h" #include "DeleteRangeTxn.h"
#include "SplitElementTxn.h"
#include "JoinElementTxn.h"
static NS_DEFINE_IID(kIDOMEventReceiverIID, NS_IDOMEVENTRECEIVER_IID); static NS_DEFINE_IID(kIDOMEventReceiverIID, NS_IDOMEVENTRECEIVER_IID);
@ -58,8 +64,18 @@ static NS_DEFINE_IID(kIEditFactoryIID, NS_IEDITORFACTORY_IID);
static NS_DEFINE_IID(kIEditorIID, NS_IEDITOR_IID); static NS_DEFINE_IID(kIEditorIID, NS_IEDITOR_IID);
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
static NS_DEFINE_IID(kEditorCID, NS_EDITOR_CID); static NS_DEFINE_IID(kEditorCID, NS_EDITOR_CID);
// transaction manager
static NS_DEFINE_IID(kITransactionManagerIID, NS_ITRANSACTIONMANAGER_IID); static NS_DEFINE_IID(kITransactionManagerIID, NS_ITRANSACTIONMANAGER_IID);
static NS_DEFINE_CID(kCTransactionManagerFactoryCID, NS_TRANSACTION_MANAGER_FACTORY_CID); static NS_DEFINE_CID(kCTransactionManagerFactoryCID, NS_TRANSACTION_MANAGER_FACTORY_CID);
// transactions
static NS_DEFINE_IID(kInsertTextTxnIID, INSERT_TEXT_TXN_IID);
static NS_DEFINE_IID(kDeleteTextTxnIID, DELETE_TEXT_TXN_IID);
static NS_DEFINE_IID(kCreateElementTxnIID, CREATE_ELEMENT_TXN_IID);
static NS_DEFINE_IID(kDeleteElementTxnIID, DELETE_ELEMENT_TXN_IID);
static NS_DEFINE_IID(kDeleteRangeTxnIID, DELETE_RANGE_TXN_IID);
static NS_DEFINE_IID(kChangeAttributeTxnIID,CHANGE_ATTRIBUTE_TXN_IID);
static NS_DEFINE_IID(kSplitElementTxnIID, SPLIT_ELEMENT_TXN_IID);
static NS_DEFINE_IID(kJoinElementTxnIID, JOIN_ELEMENT_TXN_IID);
#ifdef XP_PC #ifdef XP_PC
@ -165,6 +181,7 @@ nsEditor::nsEditor()
nsEditor::~nsEditor() nsEditor::~nsEditor()
{ {
NS_IF_RELEASE(mPresShell); NS_IF_RELEASE(mPresShell);
NS_IF_RELEASE(mViewManager);
NS_IF_RELEASE(mTxnMgr); NS_IF_RELEASE(mTxnMgr);
//the autopointers will clear themselves up. //the autopointers will clear themselves up.
@ -229,6 +246,8 @@ nsEditor::Init(nsIDOMDocument *aDomInterface, nsIPresShell* aPresShell)
mDomInterfaceP = aDomInterface; mDomInterfaceP = aDomInterface;
mPresShell = aPresShell; mPresShell = aPresShell;
NS_ADDREF(mPresShell); NS_ADDREF(mPresShell);
mViewManager = mPresShell->GetViewManager();
mUpdateCount=0;
nsresult t_result = NS_NewEditorKeyListener(getter_AddRefs(mKeyListenerP), this); nsresult t_result = NS_NewEditorKeyListener(getter_AddRefs(mKeyListenerP), this);
if (NS_OK != t_result) if (NS_OK != t_result)
@ -308,13 +327,13 @@ nsEditor::Init(nsIDOMDocument *aDomInterface, nsIPresShell* aPresShell)
nsresult nsresult
nsEditor::InsertString(nsString *aString) nsEditor::InsertString(nsString *aString)
{ {
return AppendText(aString); return NS_OK;
} }
nsresult nsresult
nsEditor::SetProperties(PROPERTIES aProperty) nsEditor::SetProperties(Properties aProperties)
{ {
return NS_OK; return NS_OK;
} }
@ -322,7 +341,7 @@ nsEditor::SetProperties(PROPERTIES aProperty)
nsresult nsresult
nsEditor::GetProperties(PROPERTIES **) nsEditor::GetProperties(Properties **aProperties)
{ {
return NS_OK; return NS_OK;
} }
@ -334,9 +353,13 @@ nsEditor::SetAttribute(nsIDOMElement *aElement, const nsString& aAttribute, cons
nsresult result; nsresult result;
if (nsnull != aElement) if (nsnull != aElement)
{ {
ChangeAttributeTxn *txn = new ChangeAttributeTxn(this, aElement, aAttribute, aValue, PR_FALSE); ChangeAttributeTxn *txn;
result = TransactionFactory::GetNewTransaction(kChangeAttributeTxnIID, (EditTxn **)&txn);
if (nsnull!=txn) if (nsnull!=txn)
result = ExecuteTransaction(txn); {
txn->Init(this, aElement, aAttribute, aValue, PR_FALSE);
result = Do(txn);
}
} }
return result; return result;
} }
@ -369,10 +392,14 @@ nsEditor::RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute)
nsresult result; nsresult result;
if (nsnull != aElement) if (nsnull != aElement)
{ {
nsString value; ChangeAttributeTxn *txn;
ChangeAttributeTxn *txn = new ChangeAttributeTxn(this, aElement, aAttribute, value, PR_TRUE); result = TransactionFactory::GetNewTransaction(kChangeAttributeTxnIID, (EditTxn **)&txn);
if (nsnull!=txn) if (nsnull!=txn)
result = ExecuteTransaction(txn); {
nsAutoString value;
txn->Init(this, aElement, aAttribute, value, PR_TRUE);
result = Do(txn);
}
} }
return result; return result;
} }
@ -420,26 +447,6 @@ nsEditor::MouseClick(int aX,int aY)
//BEGIN nsEditor Private methods //BEGIN nsEditor Private methods
nsresult
nsEditor::AppendText(nsString *aStr)
{
nsCOMPtr<nsIDOMNode> currentNode;
nsCOMPtr<nsIDOMNode> textNode;
nsCOMPtr<nsIDOMText> text;
if (!aStr)
return NS_ERROR_NULL_POINTER;
if (NS_SUCCEEDED(GetCurrentNode(getter_AddRefs(currentNode))) &&
NS_SUCCEEDED(GetFirstTextNode(currentNode,getter_AddRefs(textNode))) &&
NS_SUCCEEDED(textNode->QueryInterface(kIDOMTextIID, getter_AddRefs(text)))) {
text->AppendData(*aStr);
}
return NS_OK;
}
nsresult nsresult
nsEditor::GetCurrentNode(nsIDOMNode ** aNode) nsEditor::GetCurrentNode(nsIDOMNode ** aNode)
{ {
@ -485,7 +492,7 @@ nsEditor::GetFirstNodeOfType(nsIDOMNode *aStartNode, const nsString &aTag, nsIDO
while (childNode) while (childNode)
{ {
result = childNode->QueryInterface(kIDOMNodeIID,getter_AddRefs(element)); result = childNode->QueryInterface(kIDOMNodeIID,getter_AddRefs(element));
nsString tag; nsAutoString tag;
if (NS_SUCCEEDED(result) && (element)) if (NS_SUCCEEDED(result) && (element))
{ {
element->GetTagName(tag); element->GetTagName(tag);
@ -500,9 +507,8 @@ nsEditor::GetFirstNodeOfType(nsIDOMNode *aStartNode, const nsString &aTag, nsIDO
return result; return result;
} }
} }
nsCOMPtr<nsIDOMNode> xNode; nsCOMPtr<nsIDOMNode> temp = childNode;
childNode->GetNextSibling(getter_AddRefs(xNode)); temp->GetNextSibling(getter_AddRefs(childNode));
childNode=xNode;
} }
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@ -559,7 +565,7 @@ nsEditor::GetFirstTextNode(nsIDOMNode *aNode, nsIDOMNode **aRetNode)
} }
nsresult nsresult
nsEditor::ExecuteTransaction(nsITransaction *aTxn) nsEditor::Do(nsITransaction *aTxn)
{ {
nsresult result = NS_OK; nsresult result = NS_OK;
if (nsnull!=aTxn) if (nsnull!=aTxn)
@ -577,27 +583,65 @@ nsEditor::ExecuteTransaction(nsITransaction *aTxn)
} }
nsresult nsresult
nsEditor::Undo() nsEditor::Undo(PRUint32 aCount)
{ {
nsresult result = NS_OK; nsresult result = NS_OK;
if (nsnull!=mTxnMgr) if (nsnull!=mTxnMgr)
{ {
result = mTxnMgr->Undo(); PRUint32 i=0;
for ( ; i<aCount; i++)
{
result = mTxnMgr->Undo();
if (NS_FAILED(result))
break;
}
} }
return result; return result;
} }
nsresult nsresult
nsEditor::Redo() nsEditor::Redo(PRUint32 aCount)
{ {
nsresult result = NS_OK; nsresult result = NS_OK;
if (nsnull!=mTxnMgr) if (nsnull!=mTxnMgr)
{ {
result = mTxnMgr->Redo(); PRUint32 i=0;
for ( ; i<aCount; i++)
{
result = mTxnMgr->Redo();
if (NS_FAILED(result))
break;
}
} }
return result; return result;
} }
nsresult
nsEditor::BeginUpdate()
{
NS_PRECONDITION(mUpdateCount>=0, "bad state");
if (nsnull!=mViewManager)
{
if (0==mUpdateCount)
mViewManager->DisableRefresh();
mUpdateCount++;
}
return NS_OK;
}
nsresult
nsEditor::EndUpdate()
{
NS_PRECONDITION(mUpdateCount>0, "bad state");
if (nsnull!=mViewManager)
{
mUpdateCount--;
if (0==mUpdateCount)
mViewManager->EnableRefresh();
}
return NS_OK;
}
nsresult nsEditor::Delete(PRBool aForward, PRUint32 aCount) nsresult nsEditor::Delete(PRBool aForward, PRUint32 aCount)
{ {
return NS_OK; return NS_OK;
@ -610,9 +654,13 @@ nsresult nsEditor::CreateElement(const nsString& aTag,
nsresult result; nsresult result;
if (nsnull != aParent) if (nsnull != aParent)
{ {
CreateElementTxn *txn = new CreateElementTxn(this, mDomInterfaceP, aTag, aParent, aPosition); CreateElementTxn *txn;
result = TransactionFactory::GetNewTransaction(kCreateElementTxnIID, (EditTxn **)&txn);
if (nsnull!=txn) if (nsnull!=txn)
result = ExecuteTransaction(txn); {
txn->Init(mDomInterfaceP, aTag, aParent, aPosition);
result = Do(txn);
}
else else
result = NS_ERROR_OUT_OF_MEMORY; result = NS_ERROR_OUT_OF_MEMORY;
} }
@ -628,9 +676,13 @@ nsresult nsEditor::DeleteElement(nsIDOMNode * aParent,
nsresult result; nsresult result;
if ((nsnull != aParent) && (nsnull != aElement)) if ((nsnull != aParent) && (nsnull != aElement))
{ {
DeleteElementTxn *txn = new DeleteElementTxn(this, mDomInterfaceP, aElement, aParent); DeleteElementTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn);
if (nsnull!=txn) if (nsnull!=txn)
result = ExecuteTransaction(txn); {
txn->Init(aElement, aParent);
result = Do(txn);
}
else else
result = NS_ERROR_OUT_OF_MEMORY; result = NS_ERROR_OUT_OF_MEMORY;
} }
@ -640,18 +692,51 @@ nsresult nsEditor::DeleteElement(nsIDOMNode * aParent,
return result; return result;
} }
nsresult nsEditor::InsertText(nsIDOMCharacterData *aElement, // XXX; factor -- should call BuildInsertTextTransaction to do most of the work
PRUint32 aOffset, nsresult nsEditor::InsertText(const nsString& aStringToInsert)
const nsString& aStringToInsert)
{ {
nsresult result; nsresult result;
if (nsnull != aElement) nsISelection* selection;
result = mPresShell->GetSelection(&selection);
if ((NS_SUCCEEDED(result)) && (nsnull!=selection))
{ {
InsertTextTxn *txn = new InsertTextTxn(this, aElement, aOffset, aStringToInsert); nsCOMPtr<nsIEnumerator> enumerator;
if (nsnull!=txn) enumerator = selection;
result = ExecuteTransaction(txn); if (enumerator)
else {
result = NS_ERROR_OUT_OF_MEMORY; enumerator->First();
nsISupports *currentItem;
result = enumerator->CurrentItem(&currentItem);
if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem))
{
// XXX: we'll want to deleteRange if the selection isn't just an insertion point
// for now, just insert text after the start of the first node
nsCOMPtr<nsIDOMRange> range = currentItem;
if (range)
{
nsCOMPtr<nsIDOMNode> node;
result = range->GetStartParent(getter_AddRefs(node));
if ((NS_SUCCEEDED(result)) && (node))
{
nsCOMPtr<nsIDOMCharacterData> nodeAsText = node;
if (nodeAsText)
{
PRInt32 offset;
range->GetStartOffset(&offset);
InsertTextTxn *txn;
result = TransactionFactory::GetNewTransaction(kInsertTextTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(nodeAsText, offset, aStringToInsert);
result = Do(txn);
}
else
result = NS_ERROR_OUT_OF_MEMORY;
}
}
}
}
}
} }
else else
result = NS_ERROR_INVALID_ARG; result = NS_ERROR_INVALID_ARG;
@ -659,6 +744,7 @@ nsresult nsEditor::InsertText(nsIDOMCharacterData *aElement,
return result; return result;
} }
// XXX; factor -- should call BuildDeleteTextTransaction to do most of the work
nsresult nsEditor::DeleteText(nsIDOMCharacterData *aElement, nsresult nsEditor::DeleteText(nsIDOMCharacterData *aElement,
PRUint32 aOffset, PRUint32 aOffset,
PRUint32 aLength) PRUint32 aLength)
@ -666,9 +752,13 @@ nsresult nsEditor::DeleteText(nsIDOMCharacterData *aElement,
nsresult result=NS_OK; nsresult result=NS_OK;
if (nsnull != aElement) if (nsnull != aElement)
{ {
DeleteTextTxn *txn = new DeleteTextTxn(this, aElement, aOffset, aLength); DeleteTextTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn);
if (nsnull!=txn) if (nsnull!=txn)
result = ExecuteTransaction(txn); {
txn->Init(aElement, aOffset, aLength);
result = Do(txn);
}
else else
result = NS_ERROR_OUT_OF_MEMORY; result = NS_ERROR_OUT_OF_MEMORY;
} }
@ -678,6 +768,10 @@ nsresult nsEditor::DeleteText(nsIDOMCharacterData *aElement,
return result; return result;
} }
// XXX; factor -- should call DeleteSelectionTransaction to do most of the work
// XXX: these should get wrapped up in a single composite transaction
// rather than executing each individually, maybe I should alloc a generic aggregate
// and stick each in there, then execute the aggregate
nsresult nsEditor::DeleteSelection() nsresult nsEditor::DeleteSelection()
{ {
nsresult result; nsresult result;
@ -685,15 +779,34 @@ nsresult nsEditor::DeleteSelection()
result = mPresShell->GetSelection(&selection); result = mPresShell->GetSelection(&selection);
if ((NS_SUCCEEDED(result)) && (nsnull!=selection)) if ((NS_SUCCEEDED(result)) && (nsnull!=selection))
{ {
nsCOMPtr<nsIDOMRange> range; nsCOMPtr<nsIEnumerator> enumerator;
result = selection->QueryInterface(kIDOMRangeIID, getter_AddRefs(range)); enumerator = selection;
if ((NS_SUCCEEDED(result)) && (range)) if (enumerator)
{ {
DeleteRangeTxn *txn = new DeleteRangeTxn(this, range); enumerator->First();
if (nsnull!=txn) nsISupports *currentItem;
result = ExecuteTransaction(txn); result = enumerator->CurrentItem(&currentItem);
else if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem))
result = NS_ERROR_OUT_OF_MEMORY; {
nsCOMPtr<nsIDOMRange> range = currentItem;
DeleteRangeTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteRangeTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(range);
result = Do(txn);
}
else
result = NS_ERROR_OUT_OF_MEMORY;
if (NS_SUCCEEDED(result))
{
nsresult nextResult = enumerator->Next();
if (NS_SUCCEEDED(nextResult))
{
result = enumerator->CurrentItem(&currentItem);
}
}
}
} }
} }
else else
@ -703,30 +816,31 @@ nsresult nsEditor::DeleteSelection()
} }
nsresult nsresult
nsEditor::SplitNode(nsIDOMNode * aNode, nsEditor::SplitNode(nsIDOMNode * aExistingRightNode,
PRInt32 aOffset, PRInt32 aOffset,
nsIDOMNode* aNewNode, nsIDOMNode* aNewLeftNode,
nsIDOMNode* aParent) nsIDOMNode* aParent)
{ {
nsresult result; nsresult result;
NS_ASSERTION(((nsnull!=aNode) && NS_ASSERTION(((nsnull!=aExistingRightNode) &&
(nsnull!=aNewNode) && (nsnull!=aNewLeftNode) &&
(nsnull!=aParent)), (nsnull!=aParent)),
"null arg"); "null arg");
if ((nsnull!=aNode) && if ((nsnull!=aExistingRightNode) &&
(nsnull!=aNewNode) && (nsnull!=aNewLeftNode) &&
(nsnull!=aParent)) (nsnull!=aParent))
{ {
nsCOMPtr<nsIDOMNode> resultNode; nsCOMPtr<nsIDOMNode> resultNode;
result = aParent->InsertBefore(aNewNode, aNode, getter_AddRefs(resultNode)); result = aParent->InsertBefore(aNewLeftNode, aExistingRightNode, getter_AddRefs(resultNode));
if (NS_SUCCEEDED(result)) if (NS_SUCCEEDED(result))
{ {
// split the children between the 2 nodes // split the children between the 2 nodes
// at this point, nNode has all the children // at this point, aExistingRightNode has all the children
// move all the children whose index is < aOffset to aNewLeftNode
if (0<=aOffset) // don't bother unless we're going to move at least one child if (0<=aOffset) // don't bother unless we're going to move at least one child
{ {
nsCOMPtr<nsIDOMNodeList> childNodes; nsCOMPtr<nsIDOMNodeList> childNodes;
result = aParent->GetChildNodes(getter_AddRefs(childNodes)); result = aExistingRightNode->GetChildNodes(getter_AddRefs(childNodes));
if ((NS_SUCCEEDED(result)) && (childNodes)) if ((NS_SUCCEEDED(result)) && (childNodes))
{ {
PRInt32 i=0; PRInt32 i=0;
@ -736,10 +850,10 @@ nsEditor::SplitNode(nsIDOMNode * aNode,
result = childNodes->Item(i, getter_AddRefs(childNode)); result = childNodes->Item(i, getter_AddRefs(childNode));
if ((NS_SUCCEEDED(result)) && (childNode)) if ((NS_SUCCEEDED(result)) && (childNode))
{ {
result = aNode->RemoveChild(childNode, getter_AddRefs(resultNode)); result = aExistingRightNode->RemoveChild(childNode, getter_AddRefs(resultNode));
if (NS_SUCCEEDED(result)) if (NS_SUCCEEDED(result))
{ {
result = aNewNode->AppendChild(childNode, getter_AddRefs(resultNode)); result = aNewLeftNode->AppendChild(childNode, getter_AddRefs(resultNode));
} }
} }
} }

Просмотреть файл

@ -16,6 +16,9 @@
* Reserved. * Reserved.
*/ */
#ifndef __editor_h__
#define __editor_h__
#include "prmon.h" #include "prmon.h"
#include "nsIEditor.h" #include "nsIEditor.h"
#include "nsIContextLoader.h" #include "nsIContextLoader.h"
@ -24,11 +27,13 @@
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "editorInterfaces.h" #include "editorInterfaces.h"
#include "nsITransactionManager.h" #include "nsITransactionManager.h"
#include "TransactionFactory.h"
#include "nsRepository.h" #include "nsRepository.h"
//#include "nsISelection.h" //#include "nsISelection.h"
class nsIDOMCharacterData; class nsIDOMCharacterData;
class nsIPresShell; class nsIPresShell;
class nsIViewManager;
//This is the monitor for the editor. //This is the monitor for the editor.
PRMonitor *getEditorMonitor(); PRMonitor *getEditorMonitor();
@ -42,15 +47,19 @@ PRMonitor *getEditorMonitor();
class nsEditor : public nsIEditor class nsEditor : public nsIEditor
{ {
private: private:
nsIPresShell *mPresShell; nsIPresShell *mPresShell;
nsIViewManager *mViewManager;
PRUint32 mUpdateCount;
nsCOMPtr<nsIDOMDocument> mDomInterfaceP; nsCOMPtr<nsIDOMDocument> mDomInterfaceP;
nsCOMPtr<nsIDOMEventListener> mKeyListenerP; nsCOMPtr<nsIDOMEventListener> mKeyListenerP;
nsCOMPtr<nsIDOMEventListener> mMouseListenerP; nsCOMPtr<nsIDOMEventListener> mMouseListenerP;
// nsCOMPtr<nsISelection> mSelectionP; // nsCOMPtr<nsISelection> mSelectionP;
//nsCOMPtr<nsITransactionManager> mTxnMgrP; //nsCOMPtr<nsITransactionManager> mTxnMgrP;
nsITransactionManager * mTxnMgr; nsITransactionManager * mTxnMgr;
friend PRBool NSCanUnload(void); friend PRBool NSCanUnload(void);
static PRInt32 gInstanceCount; static PRInt32 gInstanceCount;
public: public:
/** The default constructor. This should suffice. the setting of the interfaces is done /** The default constructor. This should suffice. the setting of the interfaces is done
* after the construction of the editor class. * after the construction of the editor class.
@ -70,9 +79,9 @@ public:
virtual nsresult GetDomInterface(nsIDOMDocument **aDomInterface); virtual nsresult GetDomInterface(nsIDOMDocument **aDomInterface);
virtual nsresult SetProperties(PROPERTIES aProperty); virtual nsresult SetProperties(Properties aProperties);
virtual nsresult GetProperties(PROPERTIES **); virtual nsresult GetProperties(Properties **aProperties);
virtual nsresult SetAttribute(nsIDOMElement * aElement, virtual nsresult SetAttribute(nsIDOMElement * aElement,
const nsString& aAttribute, const nsString& aAttribute,
@ -85,11 +94,15 @@ public:
virtual nsresult RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute); virtual nsresult RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute);
virtual nsresult InsertString(nsString *aString); virtual nsresult Do(nsITransaction *aTxn);
virtual nsresult Commit(PRBool aCtrlKey); virtual nsresult Undo(PRUint32 aCount);
virtual nsresult Redo(PRUint32 aCount);
virtual nsresult BeginUpdate();
virtual nsresult EndUpdate();
/*END nsIEditor interfaces*/ /*END nsIEditor interfaces*/
@ -113,12 +126,6 @@ public:
/*BEGIN private methods used by the implementations of the above functions*/ /*BEGIN private methods used by the implementations of the above functions*/
/** AppendText is a private method that accepts a pointer to a string
* and will append it to the current node. this will be depricated
* @param nsString *aStr is the pointer to the valid string
*/
nsresult AppendText(nsString *aStr);
/** GetCurrentNode ADDREFFS and will get the current node from selection. /** GetCurrentNode ADDREFFS and will get the current node from selection.
* now it simply returns the first node in the dom * now it simply returns the first node in the dom
* @param nsIDOMNode **aNode is the return location of the dom node * @param nsIDOMNode **aNode is the return location of the dom node
@ -142,33 +149,6 @@ public:
*/ */
nsresult GetFirstNodeOfType(nsIDOMNode *aStartNode, const nsString &aTag, nsIDOMNode **aResult); nsresult GetFirstNodeOfType(nsIDOMNode *aStartNode, const nsString &aTag, nsIDOMNode **aResult);
/** ExecuteTransaction fires a transaction. It is provided here so
* clients need no knowledge of whether the editor has a transaction manager or not.
* If a transaction manager is present, it is used.
* Otherwise, the transaction is just executed directly.
*
* @param aTxn the transaction to execute
*/
nsresult ExecuteTransaction(nsITransaction *aTxn);
/** Undo reverses the effects of the last ExecuteTransaction operation
* It is provided here so clients need no knowledge of whether the editor has a transaction manager or not.
* If a transaction manager is present, it is told to undo and the result of
* that undo is returned.
* Otherwise, the Undo request is ignored.
*
*/
nsresult Undo();
/** Redo reverses the effects of the last Undo operation
* It is provided here so clients need no knowledge of whether the editor has a transaction manager or not.
* If a transaction manager is present, it is told to redo and the result of
* that redo is returned.
* Otherwise, the Redo request is ignored.
*
*/
nsresult Redo();
nsresult CreateElement(const nsString& aTag, nsresult CreateElement(const nsString& aTag,
nsIDOMNode * aParent, nsIDOMNode * aParent,
PRInt32 aPosition); PRInt32 aPosition);
@ -178,17 +158,15 @@ public:
nsresult DeleteSelection(); nsresult DeleteSelection();
nsresult InsertText(nsIDOMCharacterData *aElement, nsresult InsertText(const nsString& aStringToInsert);
PRUint32 aOffset,
const nsString& aStringToInsert);
nsresult DeleteText(nsIDOMCharacterData *aElement, nsresult DeleteText(nsIDOMCharacterData *aElement,
PRUint32 aOffset, PRUint32 aOffset,
PRUint32 aLength); PRUint32 aLength);
static nsresult SplitNode(nsIDOMNode * aNode, static nsresult SplitNode(nsIDOMNode * aExistingRightNode,
PRInt32 aOffset, PRInt32 aOffset,
nsIDOMNode * aNewNode, nsIDOMNode * aNewLeftNode,
nsIDOMNode * aParent); nsIDOMNode * aParent);
static nsresult JoinNodes(nsIDOMNode * aNodeToKeep, static nsresult JoinNodes(nsIDOMNode * aNodeToKeep,
@ -198,6 +176,10 @@ public:
nsresult Delete(PRBool aForward, PRUint32 aCount); nsresult Delete(PRBool aForward, PRUint32 aCount);
virtual nsresult InsertString(nsString *aString);
virtual nsresult Commit(PRBool aCtrlKey);
/*END private methods of nsEditor*/ /*END private methods of nsEditor*/
}; };
@ -207,3 +189,6 @@ factory method(s)
*/ */
nsresult NS_MakeEditorLoader(nsIContextLoader **aResult); nsresult NS_MakeEditorLoader(nsIContextLoader **aResult);
#endif

Просмотреть файл

@ -19,7 +19,6 @@
#include "editor.h" #include "editor.h"
#include "CreateElementTxn.h" #include "CreateElementTxn.h"
#include "SplitElementTxn.h"
#include "nsIDOMDocument.h" #include "nsIDOMDocument.h"
#include "nsIDOMElement.h" #include "nsIDOMElement.h"
@ -178,7 +177,7 @@ nsEditorKeyListener::KeyDown(nsIDOMEvent* aKeyEvent)
} }
else else
{ // XXX: delete the first P we find { // XXX: delete the first P we find
nsString pTag("P"); nsAutoString pTag("P");
nsCOMPtr<nsIDOMNode> currentNode; nsCOMPtr<nsIDOMNode> currentNode;
nsCOMPtr<nsIDOMNode> parentNode; nsCOMPtr<nsIDOMNode> parentNode;
nsCOMPtr<nsIDOMElement> element; nsCOMPtr<nsIDOMElement> element;
@ -199,25 +198,12 @@ nsEditorKeyListener::KeyDown(nsIDOMEvent* aKeyEvent)
{ {
// XXX Replace with x-platform NS-virtkeycode transform. // XXX Replace with x-platform NS-virtkeycode transform.
if (NS_OK == GetCharFromKeyCode(keyCode, isShift, & character)) { if (NS_OK == GetCharFromKeyCode(keyCode, isShift, & character)) {
nsString key; nsAutoString key;
key += character; key += character;
if (!isShift) { if (!isShift) {
key.ToLowerCase(); key.ToLowerCase();
} }
mEditor->InsertText(key);
// XXX: for now, just grab the first text node
nsCOMPtr<nsIDOMNode> currentNode;
nsCOMPtr<nsIDOMNode> textNode;
nsCOMPtr<nsIDOMCharacterData> text;
if (NS_SUCCEEDED(mEditor->GetCurrentNode(getter_AddRefs(currentNode))) &&
NS_SUCCEEDED(mEditor->GetFirstTextNode(currentNode,getter_AddRefs(textNode))) &&
NS_SUCCEEDED(textNode->QueryInterface(kIDOMCharacterDataIID, getter_AddRefs(text))))
{
// XXX: for now, just append the text
PRUint32 offset;
text->GetLength(&offset);
mEditor->InsertText(text, offset, key);
}
} }
} }
break; break;
@ -244,6 +230,10 @@ nsEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent)
return NS_OK; return NS_OK;
} }
/* these includes are for debug only. this module should never instantiate it's own transactions */
#include "SplitElementTxn.h"
#include "TransactionFactory.h"
static NS_DEFINE_IID(kSplitElementTxnIID, SPLIT_ELEMENT_TXN_IID);
nsresult nsresult
nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProcessed) nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProcessed)
{ {
@ -265,7 +255,7 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces
if (PR_TRUE==ctrlKey) if (PR_TRUE==ctrlKey)
{ {
if (nsnull!=mEditor) if (nsnull!=mEditor)
mEditor->Undo(); mEditor->Undo(1);
} }
aProcessed=PR_TRUE; aProcessed=PR_TRUE;
break; break;
@ -275,7 +265,7 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces
if (PR_TRUE==ctrlKey) if (PR_TRUE==ctrlKey)
{ {
if (nsnull!=mEditor) if (nsnull!=mEditor)
mEditor->Redo(); mEditor->Redo(1);
} }
aProcessed=PR_TRUE; aProcessed=PR_TRUE;
break; break;
@ -283,17 +273,27 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces
// hard-coded split node test: works on first <P> in the document // hard-coded split node test: works on first <P> in the document
case nsIDOMEvent::VK_S: case nsIDOMEvent::VK_S:
{ {
nsString pTag("P"); nsAutoString pTag("P");
nsCOMPtr<nsIDOMNode> currentNode; nsCOMPtr<nsIDOMNode> currentNode;
nsCOMPtr<nsIDOMElement> element; nsCOMPtr<nsIDOMElement> element;
if (NS_SUCCEEDED(mEditor->GetFirstNodeOfType(nsnull, pTag, getter_AddRefs(currentNode)))) if (NS_SUCCEEDED(mEditor->GetFirstNodeOfType(nsnull, pTag, getter_AddRefs(currentNode))))
{ {
nsresult result;
SplitElementTxn *txn; SplitElementTxn *txn;
if (PR_FALSE==isShift) // split the element so there are 0 children in the first half if (PR_FALSE==isShift) // split the element so there are 0 children in the first half
txn = new SplitElementTxn(mEditor, currentNode, -1); {
result = TransactionFactory::GetNewTransaction(kSplitElementTxnIID, (EditTxn **)&txn);
if (txn)
txn->Init(currentNode, -1);
}
else // split the element so there are 2 children in the first half else // split the element so there are 2 children in the first half
txn = new SplitElementTxn(mEditor, currentNode, 1); {
mEditor->ExecuteTransaction(txn); result = TransactionFactory::GetNewTransaction(kSplitElementTxnIID, (EditTxn **)&txn);
if (txn)
txn->Init(currentNode, 1);
}
if (txn)
mEditor->Do(txn);
} }
} }
aProcessed=PR_TRUE; aProcessed=PR_TRUE;
@ -305,10 +305,10 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces
{ {
//XXX: should be from a factory //XXX: should be from a factory
//XXX: should manage the refcount of txn //XXX: should manage the refcount of txn
nsString attribute("width"); nsAutoString attribute("width");
nsString value("400"); nsAutoString value("400");
nsString tableTag("TABLE"); nsAutoString tableTag("TABLE");
nsCOMPtr<nsIDOMNode> currentNode; nsCOMPtr<nsIDOMNode> currentNode;
nsCOMPtr<nsIDOMElement> element; nsCOMPtr<nsIDOMElement> element;
if (NS_SUCCEEDED(mEditor->GetFirstNodeOfType(nsnull, tableTag, getter_AddRefs(currentNode)))) if (NS_SUCCEEDED(mEditor->GetFirstNodeOfType(nsnull, tableTag, getter_AddRefs(currentNode))))
@ -331,11 +331,11 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces
nsresult result; nsresult result;
//XXX: should be from a factory //XXX: should be from a factory
//XXX: should manage the refcount of txn //XXX: should manage the refcount of txn
nsString attribute("src"); nsAutoString attribute("src");
nsString value("resource:/res/samples/raptor.jpg"); nsAutoString value("resource:/res/samples/raptor.jpg");
nsString imgTag("HR"); nsAutoString imgTag("HR");
nsString bodyTag("BODY"); nsAutoString bodyTag("BODY");
nsCOMPtr<nsIDOMNode> currentNode; nsCOMPtr<nsIDOMNode> currentNode;
result = mEditor->GetFirstNodeOfType(nsnull, bodyTag, getter_AddRefs(currentNode)); result = mEditor->GetFirstNodeOfType(nsnull, bodyTag, getter_AddRefs(currentNode));
if (NS_SUCCEEDED(result)) if (NS_SUCCEEDED(result))
@ -356,7 +356,7 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces
{ {
ChangeAttributeTxn *txn; ChangeAttributeTxn *txn;
txn = new ChangeAttributeTxn(mEditor, element, attribute, value, PR_FALSE); txn = new ChangeAttributeTxn(mEditor, element, attribute, value, PR_FALSE);
mEditor->ExecuteTransaction(txn); mEditor->Do(txn);
} }
} }
*/ */
@ -523,3 +523,4 @@ NS_NewEditorMouseListener(nsIDOMEventListener ** aInstancePtrResult, nsEditor *a
return it->QueryInterface(kIDOMEventListenerIID, (void **) aInstancePtrResult); return it->QueryInterface(kIDOMEventListenerIID, (void **) aInstancePtrResult);
} }

Просмотреть файл

@ -25,6 +25,7 @@ CPPSRCS = \
editorInterfaces.cpp \ editorInterfaces.cpp \
nsEditFactory.cpp \ nsEditFactory.cpp \
EditTxn.cpp \ EditTxn.cpp \
EditAggregateTxn.cpp \
ChangeAttributeTxn.cpp \ ChangeAttributeTxn.cpp \
InsertTextTxn.cpp \ InsertTextTxn.cpp \
DeleteTextTxn.cpp \ DeleteTextTxn.cpp \
@ -32,6 +33,8 @@ CPPSRCS = \
DeleteElementTxn.cpp \ DeleteElementTxn.cpp \
DeleteRangeTxn.cpp \ DeleteRangeTxn.cpp \
SplitElementTxn.cpp \ SplitElementTxn.cpp \
JoinElementTxn.cpp \
TransactionFactory.cpp \
$(NULL) $(NULL)
CPP_OBJS = \ CPP_OBJS = \
@ -39,6 +42,7 @@ CPP_OBJS = \
.\$(OBJDIR)\nsEditFactory.obj \ .\$(OBJDIR)\nsEditFactory.obj \
.\$(OBJDIR)\editorInterfaces.obj \ .\$(OBJDIR)\editorInterfaces.obj \
.\$(OBJDIR)\EditTxn.obj \ .\$(OBJDIR)\EditTxn.obj \
.\$(OBJDIR)\EditAggregateTxn.obj \
.\$(OBJDIR)\ChangeAttributeTxn.obj \ .\$(OBJDIR)\ChangeAttributeTxn.obj \
.\$(OBJDIR)\InsertTextTxn.obj \ .\$(OBJDIR)\InsertTextTxn.obj \
.\$(OBJDIR)\DeleteTextTxn.obj \ .\$(OBJDIR)\DeleteTextTxn.obj \
@ -46,6 +50,8 @@ CPP_OBJS = \
.\$(OBJDIR)\DeleteElementTxn.obj \ .\$(OBJDIR)\DeleteElementTxn.obj \
.\$(OBJDIR)\DeleteRangeTxn.obj \ .\$(OBJDIR)\DeleteRangeTxn.obj \
.\$(OBJDIR)\SplitElementTxn.obj \ .\$(OBJDIR)\SplitElementTxn.obj \
.\$(OBJDIR)\JoinElementTxn.obj \
.\$(OBJDIR)\TransactionFactory.obj \
$(NULL) $(NULL)
MODULE=editor MODULE=editor

Просмотреть файл

@ -17,23 +17,33 @@
*/ */
#include "ChangeAttributeTxn.h" #include "ChangeAttributeTxn.h"
#include "editor.h"
#include "nsIDOMElement.h" #include "nsIDOMElement.h"
#include "editor.h"
// note that aEditor is not refcounted ChangeAttributeTxn::ChangeAttributeTxn()
ChangeAttributeTxn::ChangeAttributeTxn(nsEditor *aEditor, : EditTxn()
nsIDOMElement *aElement,
const nsString& aAttribute,
const nsString& aValue,
PRBool aRemoveAttribute)
: EditTxn(aEditor)
{ {
mElement = aElement; }
mAttribute = aAttribute;
mValue = aValue; nsresult ChangeAttributeTxn::Init(nsIEditor *aEditor,
mRemoveAttribute = aRemoveAttribute; nsIDOMElement *aElement,
mAttributeWasSet=PR_FALSE; const nsString& aAttribute,
mUndoValue=""; const nsString& aValue,
PRBool aRemoveAttribute)
{
if (nsnull!=aEditor && nsnull!=aElement)
{
mEditor = aEditor;
mElement = aElement;
mAttribute = aAttribute;
mValue = aValue;
mRemoveAttribute = aRemoveAttribute;
mAttributeWasSet=PR_FALSE;
mUndoValue="";
return NS_OK;
}
else
return NS_ERROR_NULL_POINTER;
} }
nsresult ChangeAttributeTxn::Do(void) nsresult ChangeAttributeTxn::Do(void)

Просмотреть файл

@ -20,8 +20,14 @@
#define ChangeAttributeTxn_h__ #define ChangeAttributeTxn_h__
#include "EditTxn.h" #include "EditTxn.h"
#include "nsCOMPtr.h"
#include "nsIDOMElement.h"
#include "nsIEditor.h"
class nsIDOMElement; #define CHANGE_ATTRIBUTE_TXN_IID \
{/* 97818860-ac48-11d2-86d8-000064657374 */ \
0x97818860, 0xac48, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* A transaction that changes an attribute of a content node. * A transaction that changes an attribute of a content node.
@ -31,11 +37,16 @@ class ChangeAttributeTxn : public EditTxn
{ {
public: public:
ChangeAttributeTxn(nsEditor *aEditor, virtual nsresult Init(nsIEditor *aEditor,
nsIDOMElement *aElement, nsIDOMElement *aElement,
const nsString& aAttribute, const nsString& aAttribute,
const nsString& aValue, const nsString& aValue,
PRBool aRemoveAttribute); PRBool aRemoveAttribute);
private:
ChangeAttributeTxn();
public:
virtual nsresult Do(void); virtual nsresult Do(void);
@ -55,8 +66,11 @@ public:
protected: protected:
/** the editor that created this transaction */
nsCOMPtr<nsIEditor> mEditor;
/** the element to operate upon */ /** the element to operate upon */
nsIDOMElement *mElement; nsCOMPtr<nsIDOMElement> mElement;
/** the attribute to change */ /** the attribute to change */
nsString mAttribute; nsString mAttribute;
@ -72,6 +86,8 @@ protected:
/** PR_TRUE if the operation is to remove mAttribute from mElement */ /** PR_TRUE if the operation is to remove mAttribute from mElement */
PRBool mRemoveAttribute; PRBool mRemoveAttribute;
friend class TransactionFactory;
}; };
#endif #endif

Просмотреть файл

@ -17,52 +17,50 @@
*/ */
#include "CreateElementTxn.h" #include "CreateElementTxn.h"
#include "editor.h"
#include "nsIDOMNode.h"
#include "nsIDOMNodeList.h" #include "nsIDOMNodeList.h"
#include "nsIDOMDocument.h"
#include "nsIDOMElement.h"
// note that aEditor is not refcounted CreateElementTxn::CreateElementTxn()
CreateElementTxn::CreateElementTxn(nsEditor *aEditor, : EditTxn()
nsIDOMDocument *aDoc,
const nsString& aTag,
nsIDOMNode *aParent,
PRUint32 aOffsetInParent)
: EditTxn(aEditor)
{ {
mDoc = aDoc;
NS_ADDREF(mDoc);
mTag = aTag;
mParent = aParent;
NS_ADDREF(mParent);
mOffsetInParent = aOffsetInParent;
mNewNode = nsnull;
mRefNode = nsnull;
} }
nsresult CreateElementTxn::Init(nsIDOMDocument *aDoc,
const nsString& aTag,
nsIDOMNode *aParent,
PRUint32 aOffsetInParent)
{
if ((nsnull!=aDoc) && (nsnull!=aParent))
{
mDoc = aDoc;
mTag = aTag;
mParent = aParent;
mOffsetInParent = aOffsetInParent;
mNewNode = nsnull;
mRefNode = nsnull;
return NS_OK;
}
else
return NS_ERROR_NULL_POINTER;
}
CreateElementTxn::~CreateElementTxn() CreateElementTxn::~CreateElementTxn()
{ {
NS_IF_RELEASE(mDoc);
NS_IF_RELEASE(mParent);
NS_IF_RELEASE(mNewNode);
} }
nsresult CreateElementTxn::Do(void) nsresult CreateElementTxn::Do(void)
{ {
// create a new node // create a new node
nsresult result = mDoc->CreateElement(mTag, &mNewNode); nsresult result = mDoc->CreateElement(mTag, getter_AddRefs(mNewNode));
NS_ASSERTION(((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)), "could not create element."); NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewNode)), "could not create element.");
if ((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)) if ((NS_SUCCEEDED(result)) && (mNewNode))
{ {
// insert the new node // insert the new node
nsIDOMNode *resultNode=nsnull; nsCOMPtr<nsIDOMNode> resultNode;
if (CreateElementTxn::eAppend==mOffsetInParent) if (CreateElementTxn::eAppend==mOffsetInParent)
{ {
result = mParent->AppendChild(mNewNode, &resultNode); result = mParent->AppendChild(mNewNode, getter_AddRefs(resultNode));
if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode))
NS_RELEASE(resultNode); // this object already holds a ref from CreateElement
} }
else else
{ {
@ -70,12 +68,10 @@ nsresult CreateElementTxn::Do(void)
result = mParent->GetChildNodes(getter_AddRefs(childNodes)); result = mParent->GetChildNodes(getter_AddRefs(childNodes));
if ((NS_SUCCEEDED(result)) && (childNodes)) if ((NS_SUCCEEDED(result)) && (childNodes))
{ {
result = childNodes->Item(mOffsetInParent, &mRefNode); result = childNodes->Item(mOffsetInParent, getter_AddRefs(mRefNode));
if ((NS_SUCCEEDED(result)) && (nsnull!=mRefNode)) if ((NS_SUCCEEDED(result)) && (mRefNode))
{ {
result = mParent->InsertBefore(mNewNode, mRefNode, &resultNode); result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode));
if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode))
NS_RELEASE(resultNode); // this object already holds a ref from CreateElement
} }
} }
} }
@ -85,20 +81,16 @@ nsresult CreateElementTxn::Do(void)
nsresult CreateElementTxn::Undo(void) nsresult CreateElementTxn::Undo(void)
{ {
nsIDOMNode *resultNode=nsnull; nsCOMPtr<nsIDOMNode> resultNode;
nsresult result = mParent->RemoveChild(mNewNode, &resultNode); nsresult result = mParent->RemoveChild(mNewNode, getter_AddRefs(resultNode));
if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode))
NS_RELEASE(resultNode);
return result; return result;
} }
nsresult CreateElementTxn::Redo(void) nsresult CreateElementTxn::Redo(void)
{ {
nsIDOMNode *resultNode=nsnull; nsCOMPtr<nsIDOMNode> resultNode;
nsresult result = mParent->InsertBefore(mNewNode, mRefNode, &resultNode); nsresult result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode));
if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode)) return result;
NS_RELEASE(resultNode);
return result;
} }
nsresult CreateElementTxn::GetIsTransient(PRBool *aIsTransient) nsresult CreateElementTxn::GetIsTransient(PRBool *aIsTransient)

Просмотреть файл

@ -20,10 +20,15 @@
#define CreateElementTxn_h__ #define CreateElementTxn_h__
#include "EditTxn.h" #include "EditTxn.h"
#include "nsIDOMDocument.h"
#include "nsIDOMNode.h"
#include "nsIDOMElement.h"
#include "nsCOMPtr.h"
class nsIDOMDocument; #define CREATE_ELEMENT_TXN_IID \
class nsIDOMNode; {/* 7a6393c0-ac48-11d2-86d8-000064657374 */ \
class nsIDOMElement; 0x7a6393c0, 0xac48, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* A transaction that creates a new node in the content tree. * A transaction that creates a new node in the content tree.
@ -34,11 +39,15 @@ public:
enum { eAppend=-1 }; enum { eAppend=-1 };
CreateElementTxn(nsEditor *aEditor, virtual nsresult Init(nsIDOMDocument *aDoc,
nsIDOMDocument *aDoc, const nsString& aTag,
const nsString& aTag, nsIDOMNode *aParent,
nsIDOMNode *aParent, PRUint32 aOffsetInParent);
PRUint32 aOffsetInParent);
private:
CreateElementTxn();
public:
virtual ~CreateElementTxn(); virtual ~CreateElementTxn();
@ -61,22 +70,24 @@ public:
protected: protected:
/** the document into which the new node will be inserted */ /** the document into which the new node will be inserted */
nsIDOMDocument *mDoc; nsCOMPtr<nsIDOMDocument> mDoc;
/** the tag (mapping to object type) for the new element */ /** the tag (mapping to object type) for the new element */
nsString mTag; nsString mTag;
/** the node into which the new node will be inserted */ /** the node into which the new node will be inserted */
nsIDOMNode *mParent; nsCOMPtr<nsIDOMNode> mParent;
/** the index in mParent for the new node */ /** the index in mParent for the new node */
PRUint32 mOffsetInParent; PRUint32 mOffsetInParent;
/** the new node to insert */ /** the new node to insert */
nsIDOMElement *mNewNode; nsCOMPtr<nsIDOMElement> mNewNode;
/** the node we will insert mNewNode before. We compute this ourselves. */ /** the node we will insert mNewNode before. We compute this ourselves. */
nsIDOMNode *mRefNode; nsCOMPtr<nsIDOMNode> mRefNode;
friend class TransactionFactory;
}; };

Просмотреть файл

@ -17,30 +17,32 @@
*/ */
#include "DeleteElementTxn.h" #include "DeleteElementTxn.h"
#include "editor.h" #ifdef NS_DEBUG
#include "nsIDOMDocument.h"
#include "nsIDOMElement.h" #include "nsIDOMElement.h"
#endif
// note that aEditor is not refcounted
DeleteElementTxn::DeleteElementTxn(nsEditor * aEditor, DeleteElementTxn::DeleteElementTxn()
nsIDOMDocument *aDoc, : EditTxn()
nsIDOMNode * aElement,
nsIDOMNode * aParent)
: EditTxn(aEditor)
{ {
mDoc = aDoc;
NS_ADDREF(mDoc);
mElement = aElement;
NS_ADDREF(mElement);
mParent = aParent;
NS_ADDREF(mParent);
} }
nsresult DeleteElementTxn::Init(nsIDOMNode *aElement,
nsIDOMNode *aParent)
{
if ((nsnull!=aElement) && (nsnull!=aParent))
{
mElement = aElement;
mParent = aParent;
return NS_OK;
}
else
return NS_ERROR_NULL_POINTER;
}
DeleteElementTxn::~DeleteElementTxn() DeleteElementTxn::~DeleteElementTxn()
{ {
NS_IF_RELEASE(mDoc);
NS_IF_RELEASE(mParent);
NS_IF_RELEASE(mElement);
} }
nsresult DeleteElementTxn::Do(void) nsresult DeleteElementTxn::Do(void)
@ -48,11 +50,32 @@ nsresult DeleteElementTxn::Do(void)
if (!mParent || !mElement) if (!mParent || !mElement)
return NS_ERROR_NULL_POINTER; return NS_ERROR_NULL_POINTER;
#ifdef NS_DEBUG #ifdef NS_DEBUG
// begin debug output
nsCOMPtr<nsIDOMElement> element=mElement;
nsAutoString elementTag="text node";
if (element)
element->GetTagName(elementTag);
nsCOMPtr<nsIDOMElement> parentElement=mParent;
nsAutoString parentElementTag="text node";
if (parentElement)
parentElement->GetTagName(parentElementTag);
char *c, *p;
c = elementTag.ToNewCString();
p = parentElementTag.ToNewCString();
if (c&&p)
{
printf("DeleteElementTxn: deleting child %s from parent %s\n", c, p);
delete [] c;
delete [] p;
}
// end debug output
// begin sanity check 1: parent-child relationship
nsresult testResult; nsresult testResult;
nsCOMPtr<nsIDOMNode> parentNode; nsCOMPtr<nsIDOMNode> parentNode;
testResult = mElement->GetParentNode(getter_AddRefs(parentNode)); testResult = mElement->GetParentNode(getter_AddRefs(parentNode));
NS_ASSERTION((NS_SUCCEEDED(testResult)), "bad mElement, couldn't get parent"); NS_ASSERTION((NS_SUCCEEDED(testResult)), "bad mElement, couldn't get parent");
NS_ASSERTION((parentNode==mParent), "bad mParent, mParent!=mElement->GetParent() "); NS_ASSERTION((parentNode==mParent), "bad mParent, mParent!=mElement->GetParent() ");
// end sanity check 1.
#endif #endif
// remember which child mElement was (by remembering which child was next) // remember which child mElement was (by remembering which child was next)

Просмотреть файл

@ -23,8 +23,10 @@
#include "nsIDOMNode.h" #include "nsIDOMNode.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
class nsIDOMDocument; #define DELETE_ELEMENT_TXN_IID \
class nsIDOMElement; {/* 6fd77770-ac49-11d2-86d8-000064657374 */ \
0x6fd77770, 0xac49, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* A transaction that deletes a single element * A transaction that deletes a single element
@ -33,10 +35,13 @@ class DeleteElementTxn : public EditTxn
{ {
public: public:
DeleteElementTxn(nsEditor *aEditor, virtual nsresult Init(nsIDOMNode *aElement,
nsIDOMDocument *aDoc, nsIDOMNode *aParent);
nsIDOMNode *aElement,
nsIDOMNode *aParent); private:
DeleteElementTxn();
public:
virtual ~DeleteElementTxn(); virtual ~DeleteElementTxn();
@ -58,14 +63,11 @@ public:
protected: protected:
/** the document into which the new node will be inserted */
nsIDOMDocument *mDoc;
/** the element to delete */ /** the element to delete */
nsIDOMNode *mElement; nsCOMPtr<nsIDOMNode> mElement;
/** the node into which the new node will be inserted */ /** the node into which the new node will be inserted */
nsIDOMNode *mParent; nsCOMPtr<nsIDOMNode> mParent;
/** the index in mParent for the new node */ /** the index in mParent for the new node */
PRUint32 mOffsetInParent; PRUint32 mOffsetInParent;
@ -73,6 +75,8 @@ protected:
/** the node we will insert mNewNode before. We compute this ourselves. */ /** the node we will insert mNewNode before. We compute this ourselves. */
nsCOMPtr<nsIDOMNode> mRefNode; nsCOMPtr<nsIDOMNode> mRefNode;
friend class TransactionFactory;
}; };
#endif #endif

Просмотреть файл

@ -17,18 +17,73 @@
*/ */
#include "DeleteRangeTxn.h" #include "DeleteRangeTxn.h"
#include "editor.h"
#include "nsIDOMRange.h" #include "nsIDOMRange.h"
#include "nsIDOMCharacterData.h"
#include "nsIDOMNodeList.h"
#include "DeleteTextTxn.h"
#include "DeleteElementTxn.h"
#include "TransactionFactory.h"
#include "nsISupportsArray.h"
static NS_DEFINE_IID(kDeleteTextTxnIID, DELETE_TEXT_TXN_IID);
static NS_DEFINE_IID(kDeleteElementTxnIID, DELETE_ELEMENT_TXN_IID);
// note that aEditor is not refcounted // note that aEditor is not refcounted
DeleteRangeTxn::DeleteRangeTxn(nsEditor *aEditor, DeleteRangeTxn::DeleteRangeTxn()
nsIDOMRange *aRange) : EditAggregateTxn()
: EditTxn(aEditor)
{ {
aRange->GetStartParent(getter_AddRefs(mStartParent)); }
aRange->GetEndParent(getter_AddRefs(mEndParent));
aRange->GetStartOffset(&mStartOffset); nsresult DeleteRangeTxn::Init(nsIDOMRange *aRange)
aRange->GetEndOffset(&mEndOffset); {
if (nsnull!=aRange)
{
nsresult result = aRange->GetStartParent(getter_AddRefs(mStartParent));
NS_ASSERTION((NS_SUCCEEDED(result)), "GetStartParent failed.");
result = aRange->GetEndParent(getter_AddRefs(mEndParent));
NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndParent failed.");
result = aRange->GetStartOffset(&mStartOffset);
NS_ASSERTION((NS_SUCCEEDED(result)), "GetStartOffset failed.");
result = aRange->GetEndOffset(&mEndOffset);
NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndOffset failed.");
result = aRange->GetCommonParent(getter_AddRefs(mCommonParent));
NS_ASSERTION((NS_SUCCEEDED(result)), "GetCommonParent failed.");
#ifdef NS_DEBUG
PRUint32 count;
nsCOMPtr<nsIDOMCharacterData> textNode;
textNode = mStartParent;
if (textNode)
textNode->GetLength(&count);
else
{
nsCOMPtr<nsIDOMNodeList> children;
result = mStartParent->GetChildNodes(getter_AddRefs(children));
NS_ASSERTION(((NS_SUCCEEDED(result)) && children), "bad start child list");
children->GetLength(&count);
}
NS_ASSERTION(mStartOffset<count, "bad start offset");
textNode = mEndParent;
if (textNode)
textNode->GetLength(&count);
else
{
nsCOMPtr<nsIDOMNodeList> children;
result = mEndParent->GetChildNodes(getter_AddRefs(children));
NS_ASSERTION(((NS_SUCCEEDED(result)) && children), "bad end child list");
children->GetLength(&count);
}
NS_ASSERTION(mEndOffset<count, "bad end offset");
printf ("DeleteRange: %d of %p to %d of %p\n",
mStartOffset, (void *)mStartParent, mEndOffset, (void *)mEndParent);
#endif
return result;
}
else
return NS_ERROR_NULL_POINTER;
} }
DeleteRangeTxn::~DeleteRangeTxn() DeleteRangeTxn::~DeleteRangeTxn()
@ -37,28 +92,59 @@ DeleteRangeTxn::~DeleteRangeTxn()
nsresult DeleteRangeTxn::Do(void) nsresult DeleteRangeTxn::Do(void)
{ {
if (!mStartParent || !mEndParent) if (!mStartParent || !mEndParent || !mCommonParent)
return NS_ERROR_NULL_POINTER; return NS_ERROR_NULL_POINTER;
nsresult result; nsresult result;
// build the child transactions
if (mStartParent==mEndParent)
{ // the selection begins and ends in the same node
result = CreateTxnsToDeleteBetween(mStartParent, mStartOffset, mEndOffset);
}
else
{ // the selection ends in a different node from where it started
// delete the relevant content in the start node
result = CreateTxnsToDeleteContent(mStartParent, mStartOffset, nsIEditor::eLTR);
if (NS_SUCCEEDED(result))
{
// delete the intervening nodes
result = CreateTxnsToDeleteNodesBetween(mCommonParent, mStartParent, mEndParent);
if (NS_SUCCEEDED(result))
{
// delete the relevant content in the end node
result = CreateTxnsToDeleteContent(mEndParent, mEndOffset, nsIEditor::eRTL);
if (NS_SUCCEEDED(result))
{ // now we have all our child transactions, do them
result = EditAggregateTxn::Do();
}
}
}
}
// if we've successfully built this aggregate transaction, then do it.
if (NS_SUCCEEDED(result))
result = EditAggregateTxn::Do();
return result; return result;
} }
nsresult DeleteRangeTxn::Undo(void) nsresult DeleteRangeTxn::Undo(void)
{ {
if (!mStartParent || !mEndParent) if (!mStartParent || !mEndParent || !mCommonParent)
return NS_ERROR_NULL_POINTER; return NS_ERROR_NULL_POINTER;
nsresult result; nsresult result = EditAggregateTxn::Undo();
return result; return result;
} }
nsresult DeleteRangeTxn::Redo(void) nsresult DeleteRangeTxn::Redo(void)
{ {
if (!mStartParent || !mEndParent) if (!mStartParent || !mEndParent || !mCommonParent)
return NS_ERROR_NULL_POINTER; return NS_ERROR_NULL_POINTER;
nsresult result; nsresult result = EditAggregateTxn::Redo();
return result; return result;
} }
@ -98,3 +184,265 @@ nsresult DeleteRangeTxn::GetRedoString(nsString **aString)
} }
return NS_OK; return NS_OK;
} }
nsresult DeleteRangeTxn::CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent,
PRUint32 aStartOffset,
PRUint32 aEndOffset)
{
nsresult result;
// see what kind of node we have
nsCOMPtr<nsIDOMCharacterData> textNode;
textNode = aStartParent; // this uses implicit nsCOMPtr QI
if (textNode)
{ // if the node is a text node, then delete text content
DeleteTextTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(textNode, aStartOffset, (aEndOffset-aStartOffset)+1);
AppendChild(txn);
}
}
else
{
PRUint32 childCount;
nsCOMPtr<nsIDOMNodeList> children;
result = aStartParent->GetChildNodes(getter_AddRefs(children));
if ((NS_SUCCEEDED(result)) && children)
{
children->GetLength(&childCount);
NS_ASSERTION(aEndOffset<childCount, "bad aEndOffset");
PRUint32 i;
for (i=aStartOffset; i<=aEndOffset; i++)
{
nsCOMPtr<nsIDOMNode> child;
result = children->Item(i, getter_AddRefs(child));
if ((NS_SUCCEEDED(result)) && child)
{
DeleteElementTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(child, aStartParent);
AppendChild(txn);
}
else
return NS_ERROR_NULL_POINTER;
}
}
}
}
return result;
}
nsresult DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent,
PRUint32 aOffset,
nsIEditor::Direction aDir)
{
nsresult result;
// see what kind of node we have
nsCOMPtr<nsIDOMCharacterData> textNode;
textNode = aParent; // this uses implicit nsCOMPtr QI
if (textNode)
{ // if the node is a text node, then delete text content
PRUint32 start, numToDelete;
if (nsIEditor::eLTR==aDir)
{
start=aOffset;
textNode->GetLength(&numToDelete);
numToDelete -= (aOffset+1);
}
else
{
start=0;
numToDelete=aOffset;
}
DeleteTextTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(textNode, start, numToDelete);
AppendChild(txn);
}
else
return NS_ERROR_NULL_POINTER;
}
else
{ // we have an interior node, so delete some of its children
if (nsIEditor::eLTR==aDir)
{ // delete from aOffset to end
PRUint32 childCount;
nsCOMPtr<nsIDOMNodeList> children;
result = aParent->GetChildNodes(getter_AddRefs(children));
if ((NS_SUCCEEDED(result)) && children)
{
children->GetLength(&childCount);
PRUint32 i;
for (i=aOffset; i<childCount; i++)
{
nsCOMPtr<nsIDOMNode> child;
result = children->Item(i, getter_AddRefs(child));
if ((NS_SUCCEEDED(result)) && child)
{
DeleteElementTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(child, aParent);
AppendChild(txn);
}
else
return NS_ERROR_NULL_POINTER;
}
}
}
}
else
{ // delete from 0 to aOffset
nsCOMPtr<nsIDOMNode> child;
result = aParent->GetFirstChild(getter_AddRefs(child));
for (PRUint32 i=0; i<aOffset; i++)
{
if ((NS_SUCCEEDED(result)) && child)
{
DeleteElementTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(child, aParent);
AppendChild(txn);
}
else
return NS_ERROR_NULL_POINTER;
}
nsCOMPtr<nsIDOMNode> temp = child;
result = temp->GetNextSibling(getter_AddRefs(child));
}
}
}
return result;
}
nsresult DeleteRangeTxn::CreateTxnsToDeleteNodesBetween(nsIDOMNode *aCommonParent,
nsIDOMNode *aFirstChild,
nsIDOMNode *aLastChild)
{
nsresult result;
PRBool needToProcessLastChild=PR_TRUE; // set to false if we discover we can delete all required nodes by just walking up aFirstChild's parent list
nsCOMPtr<nsIDOMNode> parent; // the current parent in the iteration up the ancestors
nsCOMPtr<nsIDOMNode> child; // the current child of parent
nsISupportsArray *ancestorList; // the ancestorList of the other endpoint, used to gate deletion
NS_NewISupportsArray(&ancestorList);
if (nsnull==ancestorList)
return NS_ERROR_NULL_POINTER;
// Walk up the parent list of aFirstChild to aCommonParent,
// deleting all siblings to the right of the ancestors of aFirstChild.
BuildAncestorList(aLastChild, ancestorList);
child = aFirstChild;
result = child->GetParentNode(getter_AddRefs(parent));
while ((NS_SUCCEEDED(result)) && parent)
{
while ((NS_SUCCEEDED(result)) && child)
{ // this loop starts with the first sibling of an ancestor of aFirstChild
nsCOMPtr<nsIDOMNode> temp = child;
result = temp->GetNextSibling(getter_AddRefs(child));
if ((NS_SUCCEEDED(result)) && child)
{
if (child==aLastChild)
{ // aFirstChild and aLastChild have the same parent, and we've reached aLastChild
needToProcessLastChild = PR_FALSE;
break;
}
// test if child is an ancestor of the other node. If it is, don't process this parent anymore
PRInt32 index;
index = ancestorList->IndexOf((nsISupports*)child);
if (-1!=index)
break;
DeleteElementTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(child, parent);
AppendChild(txn);
}
else
return NS_ERROR_NULL_POINTER;
}
}
if (parent==aCommonParent)
break;
child = parent;
nsCOMPtr<nsIDOMNode> temp=parent;
result = temp->GetParentNode(getter_AddRefs(parent));
}
// Walk up the parent list of aLastChild to aCommonParent,
// deleting all siblings to the left of the ancestors of aLastChild.
BuildAncestorList(aFirstChild, ancestorList);
if (PR_TRUE==needToProcessLastChild)
{
child = aLastChild;
result = child->GetParentNode(getter_AddRefs(parent));
while ((NS_SUCCEEDED(result)) && parent)
{
while ((NS_SUCCEEDED(result)) && child)
{ // this loop starts with the first sibling of an ancestor of aFirstChild
nsCOMPtr<nsIDOMNode> temp = child;
result = temp->GetPreviousSibling(getter_AddRefs(child));
if ((NS_SUCCEEDED(result)) && child)
{
// test if child is an ancestor of the other node. If it is, don't process this parent anymore
PRInt32 index;
index = ancestorList->IndexOf((nsISupports*)child);
if (-1!=index)
break;
DeleteElementTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(child, parent);
AppendChild(txn);
}
else
return NS_ERROR_NULL_POINTER;
}
}
if (parent==aCommonParent)
break;
child = parent;
nsCOMPtr<nsIDOMNode> temp=parent;
result = temp->GetParentNode(getter_AddRefs(parent));
}
}
NS_RELEASE(ancestorList);
return result;
}
nsresult DeleteRangeTxn::BuildAncestorList(nsIDOMNode *aNode, nsISupportsArray *aList)
{
nsresult result=NS_OK;
if (nsnull!=aNode && nsnull!=aList)
{
aList->Clear();
nsCOMPtr<nsIDOMNode> parent;
nsCOMPtr<nsIDOMNode> child = aNode;
result = child->GetParentNode(getter_AddRefs(parent));
while ((NS_SUCCEEDED(result)) && child && parent)
{
nsISupports * parentAsISupports;
parent->QueryInterface(nsISupports::IID(), (void **)&parentAsISupports);
aList->AppendElement(parentAsISupports);
child = parent;
nsCOMPtr<nsIDOMNode> temp=parent;
result = temp->GetParentNode(getter_AddRefs(parent));
}
}
else
result = NS_ERROR_NULL_POINTER;
return result;
}

Просмотреть файл

@ -19,22 +19,33 @@
#ifndef DeleteRangeTxn_h__ #ifndef DeleteRangeTxn_h__
#define DeleteRangeTxn_h__ #define DeleteRangeTxn_h__
#include "EditTxn.h" #include "EditAggregateTxn.h"
#include "nsIDOMNode.h" #include "nsIDOMNode.h"
#include "nsIEditor.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
class nsIDOMDocument; class nsIDOMDocument;
class nsIDOMRange; class nsIDOMRange;
class nsISupportsArray;
#define DELETE_RANGE_TXN_IID \
{/* 5ec6b260-ac49-11d2-86d8-000064657374 */ \
0x5ec6b260, 0xac49, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* A transaction that deletes an entire range in the content tree * A transaction that deletes an entire range in the content tree
*/ */
class DeleteRangeTxn : public EditTxn class DeleteRangeTxn : public EditAggregateTxn
{ {
public: public:
DeleteRangeTxn(nsEditor *aEditor, virtual nsresult Init(nsIDOMRange *aRange);
nsIDOMRange *aRange);
private:
DeleteRangeTxn();
public:
virtual ~DeleteRangeTxn(); virtual ~DeleteRangeTxn();
@ -54,6 +65,23 @@ public:
virtual nsresult GetRedoString(nsString **aString); virtual nsresult GetRedoString(nsString **aString);
protected:
virtual nsresult CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent,
PRUint32 aStartOffset,
PRUint32 aEndOffset);
virtual nsresult CreateTxnsToDeleteNodesBetween(nsIDOMNode *aParent,
nsIDOMNode *aFirstChild,
nsIDOMNode *aLastChild);
virtual nsresult CreateTxnsToDeleteContent(nsIDOMNode *aParent,
PRUint32 aOffset,
nsIEditor::Direction aDir);
virtual nsresult BuildAncestorList(nsIDOMNode *aNode,
nsISupportsArray *aList);
protected: protected:
/** p1 in the range */ /** p1 in the range */
@ -65,9 +93,14 @@ protected:
/** p2 in the range */ /** p2 in the range */
nsCOMPtr<nsIDOMNode> mEndParent; nsCOMPtr<nsIDOMNode> mEndParent;
/** the closest common parent of p1 and p2 */
nsCOMPtr<nsIDOMNode> mCommonParent;
/** p2 offset */ /** p2 offset */
PRInt32 mEndOffset; PRInt32 mEndOffset;
friend class TransactionFactory;
}; };
#endif #endif

Просмотреть файл

@ -17,20 +17,23 @@
*/ */
#include "DeleteTextTxn.h" #include "DeleteTextTxn.h"
#include "editor.h"
#include "nsIDOMCharacterData.h" #include "nsIDOMCharacterData.h"
// note that aEditor is not refcounted
DeleteTextTxn::DeleteTextTxn(nsEditor *aEditor, DeleteTextTxn::DeleteTextTxn()
nsIDOMCharacterData *aElement, : EditTxn()
{
}
nsresult DeleteTextTxn::Init(nsIDOMCharacterData *aElement,
PRUint32 aOffset, PRUint32 aOffset,
PRUint32 aNumCharsToDelete) PRUint32 aNumCharsToDelete)
: EditTxn(aEditor)
{ {
mElement = aElement; mElement = aElement;
mOffset = aOffset; mOffset = aOffset;
mNumCharsToDelete = aNumCharsToDelete; mNumCharsToDelete = aNumCharsToDelete;
mDeletedText = ""; mDeletedText = "";
return NS_OK;
} }
nsresult DeleteTextTxn::Do(void) nsresult DeleteTextTxn::Do(void)

Просмотреть файл

@ -20,8 +20,13 @@
#define DeleteTextTxn_h__ #define DeleteTextTxn_h__
#include "EditTxn.h" #include "EditTxn.h"
#include "nsIDOMCharacterData.h"
#include "nsCOMPtr.h"
class nsIDOMCharacterData; #define DELETE_TEXT_TXN_IID \
{/* 4d3a2720-ac49-11d2-86d8-000064657374 */ \
0x4d3a2720, 0xac49, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* A transaction that changes an attribute of a content node. * A transaction that changes an attribute of a content node.
@ -31,10 +36,14 @@ class DeleteTextTxn : public EditTxn
{ {
public: public:
DeleteTextTxn(nsEditor *aEditor, virtual nsresult Init(nsIDOMCharacterData *aElement,
nsIDOMCharacterData *aElement, PRUint32 aOffset,
PRUint32 aOffset, PRUint32 aNumCharsToDelete);
PRUint32 aNumCharsToDelete);
private:
DeleteTextTxn();
public:
virtual nsresult Do(void); virtual nsresult Do(void);
@ -53,7 +62,7 @@ public:
protected: protected:
/** the text element to operate upon */ /** the text element to operate upon */
nsIDOMCharacterData *mElement; nsCOMPtr<nsIDOMCharacterData> mElement;
/** the offset into mElement where the deletion is to take place */ /** the offset into mElement where the deletion is to take place */
PRUint32 mOffset; PRUint32 mOffset;
@ -64,6 +73,8 @@ protected:
/** the text that was deleted */ /** the text that was deleted */
nsString mDeletedText; nsString mDeletedText;
friend class TransactionFactory;
}; };
#endif #endif

Просмотреть файл

@ -0,0 +1,145 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL") you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "EditAggregateTxn.h"
#include "nsCOMPtr.h"
#include "nsIDOMNode.h"
#include "nsVoidArray.h"
EditAggregateTxn::EditAggregateTxn()
: EditTxn()
{
mChildren = new nsVoidArray();
}
EditAggregateTxn::~EditAggregateTxn()
{
if (nsnull!=mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
for (i=0; i<count; i++)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
NS_IF_RELEASE(txn);
}
delete mChildren;
}
}
nsresult EditAggregateTxn::Do(void)
{
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
if (nsnull!=mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
for (i=0; i<count; i++)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
result = txn->Do();
if (NS_FAILED(result))
break;
}
}
return result;
}
nsresult EditAggregateTxn::Undo(void)
{
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
if (nsnull!=mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
// undo goes through children backwards
for (i=count-1; i>=0; i--)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
result = txn->Undo();
if (NS_FAILED(result))
break;
}
}
return result;
}
nsresult EditAggregateTxn::Redo(void)
{
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
if (nsnull!=mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
for (i=0; i<count; i++)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
result = txn->Redo();
if (NS_FAILED(result))
break;
}
}
return result;
}
nsresult EditAggregateTxn::GetIsTransient(PRBool *aIsTransient)
{
if (nsnull!=aIsTransient)
*aIsTransient = PR_TRUE;
return NS_OK;
}
nsresult EditAggregateTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction)
{
return NS_OK;
}
nsresult EditAggregateTxn::Write(nsIOutputStream *aOutputStream)
{
return NS_OK;
}
nsresult EditAggregateTxn::GetUndoString(nsString **aString)
{
if (nsnull!=aString)
*aString=nsnull;
return NS_OK;
}
nsresult EditAggregateTxn::GetRedoString(nsString **aString)
{
if (nsnull!=aString)
*aString=nsnull;
return NS_OK;
}
nsresult EditAggregateTxn::AppendChild(EditTxn *aTxn)
{
if ((nsnull!=mChildren) && (nsnull!=aTxn))
{
mChildren->AppendElement(aTxn);
return NS_OK;
}
return NS_ERROR_NULL_POINTER;
}

Просмотреть файл

@ -0,0 +1,67 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef EditAggregateTxn_h__
#define EditAggregateTxn_h__
#include "EditTxn.h"
#define EDIT_AGGREGATE_TXN_IID \
{/* 345921a0-ac49-11d2-86d8-000064657374 */ \
0x345921a0, 0xac49, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
class nsVoidArray;
/**
* base class for all document editing transactions that require aggregation.
* provides a list of child transactions.
*/
class EditAggregateTxn : public EditTxn
{
public:
EditAggregateTxn();
virtual ~EditAggregateTxn();
virtual nsresult Do(void);
virtual nsresult Undo(void);
virtual nsresult Redo(void);
virtual nsresult GetIsTransient(PRBool *aIsTransient);
virtual nsresult Merge(PRBool *aDidMerge, nsITransaction *aTransaction);
virtual nsresult Write(nsIOutputStream *aOutputStream);
virtual nsresult GetUndoString(nsString **aString);
virtual nsresult GetRedoString(nsString **aString);
virtual nsresult AppendChild(EditTxn *aTxn);
protected:
nsVoidArray *mChildren;
};
#endif

Просмотреть файл

@ -28,10 +28,8 @@ NS_IMPL_ADDREF(EditTxn)
NS_IMPL_RELEASE(EditTxn) NS_IMPL_RELEASE(EditTxn)
// note that aEditor is not refcounted // note that aEditor is not refcounted
EditTxn::EditTxn(nsEditor *aEditor) EditTxn::EditTxn()
{ {
NS_ASSERTION(nsnull!=aEditor, "null aEditor arg to EditTxn constructor");
mEditor = aEditor;
} }
nsresult EditTxn::Do(void) nsresult EditTxn::Do(void)

Просмотреть файл

@ -1,61 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef EditTxn_h__
#define EditTxn_h__
#include "nsITransaction.h"
class nsEditor;
class nsIDOMNode;
/**
* base class for all document editing transactions.
* provides access to the nsEditor that created this transaction.
*/
class EditTxn : public nsITransaction
{
public:
NS_DECL_ISUPPORTS
EditTxn(nsEditor *aEditor);
virtual nsresult Do(void);
virtual nsresult Undo(void);
virtual nsresult Redo(void);
virtual nsresult GetIsTransient(PRBool *aIsTransient);
virtual nsresult Merge(PRBool *aDidMerge, nsITransaction *aTransaction);
virtual nsresult Write(nsIOutputStream *aOutputStream);
virtual nsresult GetUndoString(nsString **aString);
virtual nsresult GetRedoString(nsString **aString);
protected:
nsEditor *mEditor;
};
#endif

Просмотреть файл

@ -20,18 +20,22 @@
#include "editor.h" #include "editor.h"
#include "nsIDOMCharacterData.h" #include "nsIDOMCharacterData.h"
static NS_DEFINE_IID(kInsertTextTxnIID, INSERTTEXTTXN_IID); static NS_DEFINE_IID(kInsertTextTxnIID, INSERT_TEXT_TXN_IID);
// note that aEditor is not refcounted
InsertTextTxn::InsertTextTxn(nsEditor *aEditor, InsertTextTxn::InsertTextTxn()
nsIDOMCharacterData *aElement, : EditTxn()
{
}
nsresult InsertTextTxn::Init(nsIDOMCharacterData *aElement,
PRUint32 aOffset, PRUint32 aOffset,
const nsString& aStringToInsert) const nsString& aStringToInsert)
: EditTxn(aEditor)
{ {
mElement = aElement; mElement = aElement;
mOffset = aOffset; mOffset = aOffset;
mStringToInsert = aStringToInsert; mStringToInsert = aStringToInsert;
return NS_OK;
} }
nsresult InsertTextTxn::Do(void) nsresult InsertTextTxn::Do(void)
@ -60,11 +64,11 @@ nsresult InsertTextTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction)
if ((nsnull!=aDidMerge) && (nsnull!=aTransaction)) if ((nsnull!=aDidMerge) && (nsnull!=aTransaction))
{ {
// if aTransaction isa InsertTextTxn, absorb it // if aTransaction isa InsertTextTxn, absorb it
nsCOMPtr<InsertTextTxn> otherTxn; nsCOMPtr<InsertTextTxn> otherTxn = aTransaction;
nsresult result = aTransaction->QueryInterface(kInsertTextTxnIID, getter_AddRefs(otherTxn)); nsresult result=NS_OK;// = aTransaction->QueryInterface(kInsertTextTxnIID, getter_AddRefs(otherTxn));
if (NS_SUCCEEDED(result) && (otherTxn)) if (NS_SUCCEEDED(result) && (otherTxn))
{ {
nsString otherData; nsAutoString otherData;
otherTxn->GetData(otherData); otherTxn->GetData(otherData);
mStringToInsert += otherData; mStringToInsert += otherData;
} }

Просмотреть файл

@ -20,14 +20,14 @@
#define InsertTextTxn_h__ #define InsertTextTxn_h__
#include "EditTxn.h" #include "EditTxn.h"
#include "nsIDOMCharacterData.h"
#include "nsCOMPtr.h"
#define INSERTTEXTTXN_IID \ #define INSERT_TEXT_TXN_IID \
{/* 93276f00-ab2c-11d2-8f4b-006008159b0c*/ \ {/* 93276f00-ab2c-11d2-8f4b-006008159b0c*/ \
0x93276f00, 0xab2c, 0x11d2, \ 0x93276f00, 0xab2c, 0x11d2, \
{0x8f, 0xb4, 0x0, 0x60, 0x8, 0x15, 0x9b, 0xc} } {0x8f, 0xb4, 0x0, 0x60, 0x8, 0x15, 0x9b, 0xc} }
class nsIDOMCharacterData;
/** /**
* A transaction that changes an attribute of a content node. * A transaction that changes an attribute of a content node.
* This transaction covers add, remove, and change attribute. * This transaction covers add, remove, and change attribute.
@ -36,15 +36,13 @@ class InsertTextTxn : public EditTxn
{ {
public: public:
InsertTextTxn(nsEditor *aEditor, virtual nsresult Init(nsIDOMCharacterData *aElement,
nsIDOMCharacterData *aElement, PRUint32 aOffset,
PRUint32 aOffset, const nsString& aStringToInsert);
const nsString& aStringToInsert);
private: private:
// default ctor so that nsCOMPtr is happy InsertTextTxn();
InsertTextTxn() : EditTxn(nsnull) {}
public: public:
@ -67,7 +65,7 @@ public:
// override QueryInterface to handle InsertTextTxn request // override QueryInterface to handle InsertTextTxn request
NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
static const nsIID& IID() { static nsIID iid = INSERTTEXTTXN_IID; return iid; } static const nsIID& IID() { static nsIID iid = INSERT_TEXT_TXN_IID; return iid; }
virtual nsresult GetData(nsString& aResult); virtual nsresult GetData(nsString& aResult);
@ -75,7 +73,7 @@ public:
protected: protected:
/** the text element to operate upon */ /** the text element to operate upon */
nsIDOMCharacterData *mElement; nsCOMPtr<nsIDOMCharacterData> mElement;
/** the offset into mElement where the insertion is to take place */ /** the offset into mElement where the insertion is to take place */
PRUint32 mOffset; PRUint32 mOffset;
@ -86,6 +84,8 @@ protected:
/** the text to insert into mElement at mOffset */ /** the text to insert into mElement at mOffset */
nsString mStringToInsert; nsString mStringToInsert;
friend class TransactionFactory;
}; };
#endif #endif

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -1,48 +0,0 @@
#!gmake
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
DEPTH=../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
LIBRARY_NAME = ender
CPPSRCS = \
editor.cpp \
editorInterfaces.cpp \
nsEditFactory.cpp \
ChangeAttributeTxn.cpp \
EditTxn.cpp \
InsertTextTxn.cpp \
DeleteTextTxn.cpp \
CreateElementTxn.cpp \
DeleteElementTxn.cpp \
DeleteRangeTxn.cpp \
SplitElementTxn.cpp \
$(NULL)
MODULE = editor
REQUIRES = xpcom raptor dom base
include $(topsrcdir)/config/config.mk
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -17,44 +17,42 @@
*/ */
#include "SplitElementTxn.h" #include "SplitElementTxn.h"
#include "editor.h"
#include "nsIDOMNode.h" #include "nsIDOMNode.h"
#include "nsIDOMElement.h" #include "nsIDOMElement.h"
#include "editor.h"
// note that aEditor is not refcounted // note that aEditor is not refcounted
SplitElementTxn::SplitElementTxn(nsEditor *aEditor, SplitElementTxn::SplitElementTxn()
nsIDOMNode *aNode, : EditTxn()
PRInt32 aOffset)
: EditTxn(aEditor)
{ {
mNode = aNode; }
NS_ADDREF(mNode);
nsresult SplitElementTxn::Init(nsIDOMNode *aNode,
PRInt32 aOffset)
{
mExistingRightNode = aNode;
mOffset = aOffset; mOffset = aOffset;
mNewNode = nsnull; return NS_OK;
mParent = nsnull;
} }
SplitElementTxn::~SplitElementTxn() SplitElementTxn::~SplitElementTxn()
{ {
NS_IF_RELEASE(mNode);
NS_IF_RELEASE(mNewNode);
NS_IF_RELEASE(mParent);
} }
nsresult SplitElementTxn::Do(void) nsresult SplitElementTxn::Do(void)
{ {
// create a new node // create a new node
nsresult result = mNode->CloneNode(PR_FALSE, &mNewNode); nsresult result = mExistingRightNode->CloneNode(PR_FALSE, getter_AddRefs(mNewLeftNode));
NS_ASSERTION(((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)), "could not create element."); NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewLeftNode)), "could not create element.");
if ((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)) if ((NS_SUCCEEDED(result)) && (mNewLeftNode))
{ {
// get the parent node // get the parent node
result = mNode->GetParentNode(&mParent); result = mExistingRightNode->GetParentNode(getter_AddRefs(mParent));
// insert the new node // insert the new node
if ((NS_SUCCEEDED(result)) && (nsnull!=mParent)) if ((NS_SUCCEEDED(result)) && (mParent))
{ {
result = mEditor->SplitNode(mNode, mOffset, mNewNode, mParent); result = nsEditor::SplitNode(mExistingRightNode, mOffset, mNewLeftNode, mParent);
} }
} }
return result; return result;
@ -63,13 +61,13 @@ nsresult SplitElementTxn::Do(void)
nsresult SplitElementTxn::Undo(void) nsresult SplitElementTxn::Undo(void)
{ {
// this assumes Do inserted the new node in front of the prior existing node // this assumes Do inserted the new node in front of the prior existing node
nsresult result = mEditor->JoinNodes(mNode, mNewNode, mParent, PR_FALSE); nsresult result = nsEditor::JoinNodes(mExistingRightNode, mNewLeftNode, mParent, PR_FALSE);
return result; return result;
} }
nsresult SplitElementTxn::Redo(void) nsresult SplitElementTxn::Redo(void)
{ {
nsresult result = mEditor->SplitNode(mNode, mOffset, mNewNode, mParent); nsresult result = nsEditor::SplitNode(mExistingRightNode, mOffset, mNewLeftNode, mParent);
return result; return result;
} }

Просмотреть файл

@ -20,10 +20,13 @@
#define SplitElementTxn_h__ #define SplitElementTxn_h__
#include "EditTxn.h" #include "EditTxn.h"
#include "nsIDOMNode.h"
#include "nsCOMPtr.h"
class nsIDOMNode; #define SPLIT_ELEMENT_TXN_IID \
class nsIDOMElement; {/* 690c6290-ac48-11d2-86d8-000064657374 */ \
class nsIDOMDocument; 0x690c6290, 0xac48, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* A transaction that splits an element E into two identical nodes, E1 and E2 * A transaction that splits an element E into two identical nodes, E1 and E2
@ -33,10 +36,12 @@ class SplitElementTxn : public EditTxn
{ {
public: public:
SplitElementTxn(nsEditor *aEditor, virtual nsresult Init (nsIDOMNode *aNode,
nsIDOMNode *aNode, PRInt32 aOffset);
PRInt32 aOffset); protected:
SplitElementTxn();
public:
virtual ~SplitElementTxn(); virtual ~SplitElementTxn();
virtual nsresult Do(void); virtual nsresult Do(void);
@ -58,7 +63,7 @@ public:
protected: protected:
/** the element to operate upon */ /** the element to operate upon */
nsIDOMNode *mNode; nsCOMPtr<nsIDOMNode> mExistingRightNode;
/** the offset into mElement where the children of mElement are split.<BR> /** the offset into mElement where the children of mElement are split.<BR>
* mOffset is the index of the last child in the left node. * mOffset is the index of the last child in the left node.
@ -67,8 +72,12 @@ protected:
PRInt32 mOffset; PRInt32 mOffset;
/** the element we create when splitting mElement */ /** the element we create when splitting mElement */
nsIDOMNode *mNewNode; nsCOMPtr<nsIDOMNode> mNewLeftNode;
nsIDOMNode *mParent;
/** the parent shared by mExistingRightNode and mNewLeftNode */
nsCOMPtr<nsIDOMNode> mParent;
friend class TransactionFactory;
}; };

Просмотреть файл

@ -0,0 +1,75 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "TransactionFactory.h"
// transactions this factory knows how to build
#include "InsertTextTxn.h"
#include "DeleteTextTxn.h"
#include "CreateElementTxn.h"
#include "DeleteElementTxn.h"
#include "DeleteRangeTxn.h"
#include "ChangeAttributeTxn.h"
#include "SplitElementTxn.h"
#include "JoinElementTxn.h"
static NS_DEFINE_IID(kInsertTextTxnIID, INSERT_TEXT_TXN_IID);
static NS_DEFINE_IID(kDeleteTextTxnIID, DELETE_TEXT_TXN_IID);
static NS_DEFINE_IID(kCreateElementTxnIID, CREATE_ELEMENT_TXN_IID);
static NS_DEFINE_IID(kDeleteElementTxnIID, DELETE_ELEMENT_TXN_IID);
static NS_DEFINE_IID(kDeleteRangeTxnIID, DELETE_RANGE_TXN_IID);
static NS_DEFINE_IID(kChangeAttributeTxnIID,CHANGE_ATTRIBUTE_TXN_IID);
static NS_DEFINE_IID(kSplitElementTxnIID, SPLIT_ELEMENT_TXN_IID);
static NS_DEFINE_IID(kJoinElementTxnIID, JOIN_ELEMENT_TXN_IID);
TransactionFactory::TransactionFactory()
{
}
TransactionFactory::~TransactionFactory()
{
}
nsresult
TransactionFactory::GetNewTransaction(REFNSIID aTxnType, EditTxn **aResult)
{
nsresult result = NS_OK;
*aResult = nsnull;
if (aTxnType.Equals(kInsertTextTxnIID))
*aResult = new InsertTextTxn();
else if (aTxnType.Equals(kDeleteTextTxnIID))
*aResult = new DeleteTextTxn();
else if (aTxnType.Equals(kCreateElementTxnIID))
*aResult = new CreateElementTxn();
else if (aTxnType.Equals(kDeleteElementTxnIID))
*aResult = new DeleteElementTxn();
else if (aTxnType.Equals(kDeleteRangeTxnIID))
*aResult = new DeleteRangeTxn();
else if (aTxnType.Equals(kChangeAttributeTxnIID))
*aResult = new ChangeAttributeTxn();
else if (aTxnType.Equals(kSplitElementTxnIID))
*aResult = new SplitElementTxn();
else if (aTxnType.Equals(kJoinElementTxnIID))
*aResult = new JoinElementTxn();
if (nsnull==*aResult)
result = NS_ERROR_INVALID_ARG;
return result;
}

Просмотреть файл

Просмотреть файл

@ -33,16 +33,22 @@
#include "nsTransactionManagerCID.h" #include "nsTransactionManagerCID.h"
#include "nsITransactionManager.h" #include "nsITransactionManager.h"
#include "nsIPresShell.h" #include "nsIPresShell.h"
#include "nsIViewManager.h"
#include "nsISelection.h" #include "nsISelection.h"
#include "nsICollection.h"
#include "nsIEnumerator.h"
#include "nsIAtom.h" #include "nsIAtom.h"
// transactions the editor knows how to build // transactions the editor knows how to build
#include "TransactionFactory.h"
#include "ChangeAttributeTxn.h" #include "ChangeAttributeTxn.h"
#include "CreateElementTxn.h" #include "CreateElementTxn.h"
#include "DeleteElementTxn.h" #include "DeleteElementTxn.h"
#include "InsertTextTxn.h" #include "InsertTextTxn.h"
#include "DeleteTextTxn.h" #include "DeleteTextTxn.h"
#include "DeleteRangeTxn.h" #include "DeleteRangeTxn.h"
#include "SplitElementTxn.h"
#include "JoinElementTxn.h"
static NS_DEFINE_IID(kIDOMEventReceiverIID, NS_IDOMEVENTRECEIVER_IID); static NS_DEFINE_IID(kIDOMEventReceiverIID, NS_IDOMEVENTRECEIVER_IID);
@ -58,8 +64,18 @@ static NS_DEFINE_IID(kIEditFactoryIID, NS_IEDITORFACTORY_IID);
static NS_DEFINE_IID(kIEditorIID, NS_IEDITOR_IID); static NS_DEFINE_IID(kIEditorIID, NS_IEDITOR_IID);
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
static NS_DEFINE_IID(kEditorCID, NS_EDITOR_CID); static NS_DEFINE_IID(kEditorCID, NS_EDITOR_CID);
// transaction manager
static NS_DEFINE_IID(kITransactionManagerIID, NS_ITRANSACTIONMANAGER_IID); static NS_DEFINE_IID(kITransactionManagerIID, NS_ITRANSACTIONMANAGER_IID);
static NS_DEFINE_CID(kCTransactionManagerFactoryCID, NS_TRANSACTION_MANAGER_FACTORY_CID); static NS_DEFINE_CID(kCTransactionManagerFactoryCID, NS_TRANSACTION_MANAGER_FACTORY_CID);
// transactions
static NS_DEFINE_IID(kInsertTextTxnIID, INSERT_TEXT_TXN_IID);
static NS_DEFINE_IID(kDeleteTextTxnIID, DELETE_TEXT_TXN_IID);
static NS_DEFINE_IID(kCreateElementTxnIID, CREATE_ELEMENT_TXN_IID);
static NS_DEFINE_IID(kDeleteElementTxnIID, DELETE_ELEMENT_TXN_IID);
static NS_DEFINE_IID(kDeleteRangeTxnIID, DELETE_RANGE_TXN_IID);
static NS_DEFINE_IID(kChangeAttributeTxnIID,CHANGE_ATTRIBUTE_TXN_IID);
static NS_DEFINE_IID(kSplitElementTxnIID, SPLIT_ELEMENT_TXN_IID);
static NS_DEFINE_IID(kJoinElementTxnIID, JOIN_ELEMENT_TXN_IID);
#ifdef XP_PC #ifdef XP_PC
@ -165,6 +181,7 @@ nsEditor::nsEditor()
nsEditor::~nsEditor() nsEditor::~nsEditor()
{ {
NS_IF_RELEASE(mPresShell); NS_IF_RELEASE(mPresShell);
NS_IF_RELEASE(mViewManager);
NS_IF_RELEASE(mTxnMgr); NS_IF_RELEASE(mTxnMgr);
//the autopointers will clear themselves up. //the autopointers will clear themselves up.
@ -229,6 +246,8 @@ nsEditor::Init(nsIDOMDocument *aDomInterface, nsIPresShell* aPresShell)
mDomInterfaceP = aDomInterface; mDomInterfaceP = aDomInterface;
mPresShell = aPresShell; mPresShell = aPresShell;
NS_ADDREF(mPresShell); NS_ADDREF(mPresShell);
mViewManager = mPresShell->GetViewManager();
mUpdateCount=0;
nsresult t_result = NS_NewEditorKeyListener(getter_AddRefs(mKeyListenerP), this); nsresult t_result = NS_NewEditorKeyListener(getter_AddRefs(mKeyListenerP), this);
if (NS_OK != t_result) if (NS_OK != t_result)
@ -308,13 +327,13 @@ nsEditor::Init(nsIDOMDocument *aDomInterface, nsIPresShell* aPresShell)
nsresult nsresult
nsEditor::InsertString(nsString *aString) nsEditor::InsertString(nsString *aString)
{ {
return AppendText(aString); return NS_OK;
} }
nsresult nsresult
nsEditor::SetProperties(PROPERTIES aProperty) nsEditor::SetProperties(Properties aProperties)
{ {
return NS_OK; return NS_OK;
} }
@ -322,7 +341,7 @@ nsEditor::SetProperties(PROPERTIES aProperty)
nsresult nsresult
nsEditor::GetProperties(PROPERTIES **) nsEditor::GetProperties(Properties **aProperties)
{ {
return NS_OK; return NS_OK;
} }
@ -334,9 +353,13 @@ nsEditor::SetAttribute(nsIDOMElement *aElement, const nsString& aAttribute, cons
nsresult result; nsresult result;
if (nsnull != aElement) if (nsnull != aElement)
{ {
ChangeAttributeTxn *txn = new ChangeAttributeTxn(this, aElement, aAttribute, aValue, PR_FALSE); ChangeAttributeTxn *txn;
result = TransactionFactory::GetNewTransaction(kChangeAttributeTxnIID, (EditTxn **)&txn);
if (nsnull!=txn) if (nsnull!=txn)
result = ExecuteTransaction(txn); {
txn->Init(this, aElement, aAttribute, aValue, PR_FALSE);
result = Do(txn);
}
} }
return result; return result;
} }
@ -369,10 +392,14 @@ nsEditor::RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute)
nsresult result; nsresult result;
if (nsnull != aElement) if (nsnull != aElement)
{ {
nsString value; ChangeAttributeTxn *txn;
ChangeAttributeTxn *txn = new ChangeAttributeTxn(this, aElement, aAttribute, value, PR_TRUE); result = TransactionFactory::GetNewTransaction(kChangeAttributeTxnIID, (EditTxn **)&txn);
if (nsnull!=txn) if (nsnull!=txn)
result = ExecuteTransaction(txn); {
nsAutoString value;
txn->Init(this, aElement, aAttribute, value, PR_TRUE);
result = Do(txn);
}
} }
return result; return result;
} }
@ -420,26 +447,6 @@ nsEditor::MouseClick(int aX,int aY)
//BEGIN nsEditor Private methods //BEGIN nsEditor Private methods
nsresult
nsEditor::AppendText(nsString *aStr)
{
nsCOMPtr<nsIDOMNode> currentNode;
nsCOMPtr<nsIDOMNode> textNode;
nsCOMPtr<nsIDOMText> text;
if (!aStr)
return NS_ERROR_NULL_POINTER;
if (NS_SUCCEEDED(GetCurrentNode(getter_AddRefs(currentNode))) &&
NS_SUCCEEDED(GetFirstTextNode(currentNode,getter_AddRefs(textNode))) &&
NS_SUCCEEDED(textNode->QueryInterface(kIDOMTextIID, getter_AddRefs(text)))) {
text->AppendData(*aStr);
}
return NS_OK;
}
nsresult nsresult
nsEditor::GetCurrentNode(nsIDOMNode ** aNode) nsEditor::GetCurrentNode(nsIDOMNode ** aNode)
{ {
@ -485,7 +492,7 @@ nsEditor::GetFirstNodeOfType(nsIDOMNode *aStartNode, const nsString &aTag, nsIDO
while (childNode) while (childNode)
{ {
result = childNode->QueryInterface(kIDOMNodeIID,getter_AddRefs(element)); result = childNode->QueryInterface(kIDOMNodeIID,getter_AddRefs(element));
nsString tag; nsAutoString tag;
if (NS_SUCCEEDED(result) && (element)) if (NS_SUCCEEDED(result) && (element))
{ {
element->GetTagName(tag); element->GetTagName(tag);
@ -500,9 +507,8 @@ nsEditor::GetFirstNodeOfType(nsIDOMNode *aStartNode, const nsString &aTag, nsIDO
return result; return result;
} }
} }
nsCOMPtr<nsIDOMNode> xNode; nsCOMPtr<nsIDOMNode> temp = childNode;
childNode->GetNextSibling(getter_AddRefs(xNode)); temp->GetNextSibling(getter_AddRefs(childNode));
childNode=xNode;
} }
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@ -559,7 +565,7 @@ nsEditor::GetFirstTextNode(nsIDOMNode *aNode, nsIDOMNode **aRetNode)
} }
nsresult nsresult
nsEditor::ExecuteTransaction(nsITransaction *aTxn) nsEditor::Do(nsITransaction *aTxn)
{ {
nsresult result = NS_OK; nsresult result = NS_OK;
if (nsnull!=aTxn) if (nsnull!=aTxn)
@ -577,27 +583,65 @@ nsEditor::ExecuteTransaction(nsITransaction *aTxn)
} }
nsresult nsresult
nsEditor::Undo() nsEditor::Undo(PRUint32 aCount)
{ {
nsresult result = NS_OK; nsresult result = NS_OK;
if (nsnull!=mTxnMgr) if (nsnull!=mTxnMgr)
{ {
result = mTxnMgr->Undo(); PRUint32 i=0;
for ( ; i<aCount; i++)
{
result = mTxnMgr->Undo();
if (NS_FAILED(result))
break;
}
} }
return result; return result;
} }
nsresult nsresult
nsEditor::Redo() nsEditor::Redo(PRUint32 aCount)
{ {
nsresult result = NS_OK; nsresult result = NS_OK;
if (nsnull!=mTxnMgr) if (nsnull!=mTxnMgr)
{ {
result = mTxnMgr->Redo(); PRUint32 i=0;
for ( ; i<aCount; i++)
{
result = mTxnMgr->Redo();
if (NS_FAILED(result))
break;
}
} }
return result; return result;
} }
nsresult
nsEditor::BeginUpdate()
{
NS_PRECONDITION(mUpdateCount>=0, "bad state");
if (nsnull!=mViewManager)
{
if (0==mUpdateCount)
mViewManager->DisableRefresh();
mUpdateCount++;
}
return NS_OK;
}
nsresult
nsEditor::EndUpdate()
{
NS_PRECONDITION(mUpdateCount>0, "bad state");
if (nsnull!=mViewManager)
{
mUpdateCount--;
if (0==mUpdateCount)
mViewManager->EnableRefresh();
}
return NS_OK;
}
nsresult nsEditor::Delete(PRBool aForward, PRUint32 aCount) nsresult nsEditor::Delete(PRBool aForward, PRUint32 aCount)
{ {
return NS_OK; return NS_OK;
@ -610,9 +654,13 @@ nsresult nsEditor::CreateElement(const nsString& aTag,
nsresult result; nsresult result;
if (nsnull != aParent) if (nsnull != aParent)
{ {
CreateElementTxn *txn = new CreateElementTxn(this, mDomInterfaceP, aTag, aParent, aPosition); CreateElementTxn *txn;
result = TransactionFactory::GetNewTransaction(kCreateElementTxnIID, (EditTxn **)&txn);
if (nsnull!=txn) if (nsnull!=txn)
result = ExecuteTransaction(txn); {
txn->Init(mDomInterfaceP, aTag, aParent, aPosition);
result = Do(txn);
}
else else
result = NS_ERROR_OUT_OF_MEMORY; result = NS_ERROR_OUT_OF_MEMORY;
} }
@ -628,9 +676,13 @@ nsresult nsEditor::DeleteElement(nsIDOMNode * aParent,
nsresult result; nsresult result;
if ((nsnull != aParent) && (nsnull != aElement)) if ((nsnull != aParent) && (nsnull != aElement))
{ {
DeleteElementTxn *txn = new DeleteElementTxn(this, mDomInterfaceP, aElement, aParent); DeleteElementTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn);
if (nsnull!=txn) if (nsnull!=txn)
result = ExecuteTransaction(txn); {
txn->Init(aElement, aParent);
result = Do(txn);
}
else else
result = NS_ERROR_OUT_OF_MEMORY; result = NS_ERROR_OUT_OF_MEMORY;
} }
@ -640,18 +692,51 @@ nsresult nsEditor::DeleteElement(nsIDOMNode * aParent,
return result; return result;
} }
nsresult nsEditor::InsertText(nsIDOMCharacterData *aElement, // XXX; factor -- should call BuildInsertTextTransaction to do most of the work
PRUint32 aOffset, nsresult nsEditor::InsertText(const nsString& aStringToInsert)
const nsString& aStringToInsert)
{ {
nsresult result; nsresult result;
if (nsnull != aElement) nsISelection* selection;
result = mPresShell->GetSelection(&selection);
if ((NS_SUCCEEDED(result)) && (nsnull!=selection))
{ {
InsertTextTxn *txn = new InsertTextTxn(this, aElement, aOffset, aStringToInsert); nsCOMPtr<nsIEnumerator> enumerator;
if (nsnull!=txn) enumerator = selection;
result = ExecuteTransaction(txn); if (enumerator)
else {
result = NS_ERROR_OUT_OF_MEMORY; enumerator->First();
nsISupports *currentItem;
result = enumerator->CurrentItem(&currentItem);
if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem))
{
// XXX: we'll want to deleteRange if the selection isn't just an insertion point
// for now, just insert text after the start of the first node
nsCOMPtr<nsIDOMRange> range = currentItem;
if (range)
{
nsCOMPtr<nsIDOMNode> node;
result = range->GetStartParent(getter_AddRefs(node));
if ((NS_SUCCEEDED(result)) && (node))
{
nsCOMPtr<nsIDOMCharacterData> nodeAsText = node;
if (nodeAsText)
{
PRInt32 offset;
range->GetStartOffset(&offset);
InsertTextTxn *txn;
result = TransactionFactory::GetNewTransaction(kInsertTextTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(nodeAsText, offset, aStringToInsert);
result = Do(txn);
}
else
result = NS_ERROR_OUT_OF_MEMORY;
}
}
}
}
}
} }
else else
result = NS_ERROR_INVALID_ARG; result = NS_ERROR_INVALID_ARG;
@ -659,6 +744,7 @@ nsresult nsEditor::InsertText(nsIDOMCharacterData *aElement,
return result; return result;
} }
// XXX; factor -- should call BuildDeleteTextTransaction to do most of the work
nsresult nsEditor::DeleteText(nsIDOMCharacterData *aElement, nsresult nsEditor::DeleteText(nsIDOMCharacterData *aElement,
PRUint32 aOffset, PRUint32 aOffset,
PRUint32 aLength) PRUint32 aLength)
@ -666,9 +752,13 @@ nsresult nsEditor::DeleteText(nsIDOMCharacterData *aElement,
nsresult result=NS_OK; nsresult result=NS_OK;
if (nsnull != aElement) if (nsnull != aElement)
{ {
DeleteTextTxn *txn = new DeleteTextTxn(this, aElement, aOffset, aLength); DeleteTextTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn);
if (nsnull!=txn) if (nsnull!=txn)
result = ExecuteTransaction(txn); {
txn->Init(aElement, aOffset, aLength);
result = Do(txn);
}
else else
result = NS_ERROR_OUT_OF_MEMORY; result = NS_ERROR_OUT_OF_MEMORY;
} }
@ -678,6 +768,10 @@ nsresult nsEditor::DeleteText(nsIDOMCharacterData *aElement,
return result; return result;
} }
// XXX; factor -- should call DeleteSelectionTransaction to do most of the work
// XXX: these should get wrapped up in a single composite transaction
// rather than executing each individually, maybe I should alloc a generic aggregate
// and stick each in there, then execute the aggregate
nsresult nsEditor::DeleteSelection() nsresult nsEditor::DeleteSelection()
{ {
nsresult result; nsresult result;
@ -685,15 +779,34 @@ nsresult nsEditor::DeleteSelection()
result = mPresShell->GetSelection(&selection); result = mPresShell->GetSelection(&selection);
if ((NS_SUCCEEDED(result)) && (nsnull!=selection)) if ((NS_SUCCEEDED(result)) && (nsnull!=selection))
{ {
nsCOMPtr<nsIDOMRange> range; nsCOMPtr<nsIEnumerator> enumerator;
result = selection->QueryInterface(kIDOMRangeIID, getter_AddRefs(range)); enumerator = selection;
if ((NS_SUCCEEDED(result)) && (range)) if (enumerator)
{ {
DeleteRangeTxn *txn = new DeleteRangeTxn(this, range); enumerator->First();
if (nsnull!=txn) nsISupports *currentItem;
result = ExecuteTransaction(txn); result = enumerator->CurrentItem(&currentItem);
else if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem))
result = NS_ERROR_OUT_OF_MEMORY; {
nsCOMPtr<nsIDOMRange> range = currentItem;
DeleteRangeTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteRangeTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(range);
result = Do(txn);
}
else
result = NS_ERROR_OUT_OF_MEMORY;
if (NS_SUCCEEDED(result))
{
nsresult nextResult = enumerator->Next();
if (NS_SUCCEEDED(nextResult))
{
result = enumerator->CurrentItem(&currentItem);
}
}
}
} }
} }
else else
@ -703,30 +816,31 @@ nsresult nsEditor::DeleteSelection()
} }
nsresult nsresult
nsEditor::SplitNode(nsIDOMNode * aNode, nsEditor::SplitNode(nsIDOMNode * aExistingRightNode,
PRInt32 aOffset, PRInt32 aOffset,
nsIDOMNode* aNewNode, nsIDOMNode* aNewLeftNode,
nsIDOMNode* aParent) nsIDOMNode* aParent)
{ {
nsresult result; nsresult result;
NS_ASSERTION(((nsnull!=aNode) && NS_ASSERTION(((nsnull!=aExistingRightNode) &&
(nsnull!=aNewNode) && (nsnull!=aNewLeftNode) &&
(nsnull!=aParent)), (nsnull!=aParent)),
"null arg"); "null arg");
if ((nsnull!=aNode) && if ((nsnull!=aExistingRightNode) &&
(nsnull!=aNewNode) && (nsnull!=aNewLeftNode) &&
(nsnull!=aParent)) (nsnull!=aParent))
{ {
nsCOMPtr<nsIDOMNode> resultNode; nsCOMPtr<nsIDOMNode> resultNode;
result = aParent->InsertBefore(aNewNode, aNode, getter_AddRefs(resultNode)); result = aParent->InsertBefore(aNewLeftNode, aExistingRightNode, getter_AddRefs(resultNode));
if (NS_SUCCEEDED(result)) if (NS_SUCCEEDED(result))
{ {
// split the children between the 2 nodes // split the children between the 2 nodes
// at this point, nNode has all the children // at this point, aExistingRightNode has all the children
// move all the children whose index is < aOffset to aNewLeftNode
if (0<=aOffset) // don't bother unless we're going to move at least one child if (0<=aOffset) // don't bother unless we're going to move at least one child
{ {
nsCOMPtr<nsIDOMNodeList> childNodes; nsCOMPtr<nsIDOMNodeList> childNodes;
result = aParent->GetChildNodes(getter_AddRefs(childNodes)); result = aExistingRightNode->GetChildNodes(getter_AddRefs(childNodes));
if ((NS_SUCCEEDED(result)) && (childNodes)) if ((NS_SUCCEEDED(result)) && (childNodes))
{ {
PRInt32 i=0; PRInt32 i=0;
@ -736,10 +850,10 @@ nsEditor::SplitNode(nsIDOMNode * aNode,
result = childNodes->Item(i, getter_AddRefs(childNode)); result = childNodes->Item(i, getter_AddRefs(childNode));
if ((NS_SUCCEEDED(result)) && (childNode)) if ((NS_SUCCEEDED(result)) && (childNode))
{ {
result = aNode->RemoveChild(childNode, getter_AddRefs(resultNode)); result = aExistingRightNode->RemoveChild(childNode, getter_AddRefs(resultNode));
if (NS_SUCCEEDED(result)) if (NS_SUCCEEDED(result))
{ {
result = aNewNode->AppendChild(childNode, getter_AddRefs(resultNode)); result = aNewLeftNode->AppendChild(childNode, getter_AddRefs(resultNode));
} }
} }
} }

Просмотреть файл

@ -16,6 +16,9 @@
* Reserved. * Reserved.
*/ */
#ifndef __editor_h__
#define __editor_h__
#include "prmon.h" #include "prmon.h"
#include "nsIEditor.h" #include "nsIEditor.h"
#include "nsIContextLoader.h" #include "nsIContextLoader.h"
@ -24,11 +27,13 @@
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "editorInterfaces.h" #include "editorInterfaces.h"
#include "nsITransactionManager.h" #include "nsITransactionManager.h"
#include "TransactionFactory.h"
#include "nsRepository.h" #include "nsRepository.h"
//#include "nsISelection.h" //#include "nsISelection.h"
class nsIDOMCharacterData; class nsIDOMCharacterData;
class nsIPresShell; class nsIPresShell;
class nsIViewManager;
//This is the monitor for the editor. //This is the monitor for the editor.
PRMonitor *getEditorMonitor(); PRMonitor *getEditorMonitor();
@ -42,15 +47,19 @@ PRMonitor *getEditorMonitor();
class nsEditor : public nsIEditor class nsEditor : public nsIEditor
{ {
private: private:
nsIPresShell *mPresShell; nsIPresShell *mPresShell;
nsIViewManager *mViewManager;
PRUint32 mUpdateCount;
nsCOMPtr<nsIDOMDocument> mDomInterfaceP; nsCOMPtr<nsIDOMDocument> mDomInterfaceP;
nsCOMPtr<nsIDOMEventListener> mKeyListenerP; nsCOMPtr<nsIDOMEventListener> mKeyListenerP;
nsCOMPtr<nsIDOMEventListener> mMouseListenerP; nsCOMPtr<nsIDOMEventListener> mMouseListenerP;
// nsCOMPtr<nsISelection> mSelectionP; // nsCOMPtr<nsISelection> mSelectionP;
//nsCOMPtr<nsITransactionManager> mTxnMgrP; //nsCOMPtr<nsITransactionManager> mTxnMgrP;
nsITransactionManager * mTxnMgr; nsITransactionManager * mTxnMgr;
friend PRBool NSCanUnload(void); friend PRBool NSCanUnload(void);
static PRInt32 gInstanceCount; static PRInt32 gInstanceCount;
public: public:
/** The default constructor. This should suffice. the setting of the interfaces is done /** The default constructor. This should suffice. the setting of the interfaces is done
* after the construction of the editor class. * after the construction of the editor class.
@ -70,9 +79,9 @@ public:
virtual nsresult GetDomInterface(nsIDOMDocument **aDomInterface); virtual nsresult GetDomInterface(nsIDOMDocument **aDomInterface);
virtual nsresult SetProperties(PROPERTIES aProperty); virtual nsresult SetProperties(Properties aProperties);
virtual nsresult GetProperties(PROPERTIES **); virtual nsresult GetProperties(Properties **aProperties);
virtual nsresult SetAttribute(nsIDOMElement * aElement, virtual nsresult SetAttribute(nsIDOMElement * aElement,
const nsString& aAttribute, const nsString& aAttribute,
@ -85,11 +94,15 @@ public:
virtual nsresult RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute); virtual nsresult RemoveAttribute(nsIDOMElement *aElement, const nsString& aAttribute);
virtual nsresult InsertString(nsString *aString); virtual nsresult Do(nsITransaction *aTxn);
virtual nsresult Commit(PRBool aCtrlKey); virtual nsresult Undo(PRUint32 aCount);
virtual nsresult Redo(PRUint32 aCount);
virtual nsresult BeginUpdate();
virtual nsresult EndUpdate();
/*END nsIEditor interfaces*/ /*END nsIEditor interfaces*/
@ -113,12 +126,6 @@ public:
/*BEGIN private methods used by the implementations of the above functions*/ /*BEGIN private methods used by the implementations of the above functions*/
/** AppendText is a private method that accepts a pointer to a string
* and will append it to the current node. this will be depricated
* @param nsString *aStr is the pointer to the valid string
*/
nsresult AppendText(nsString *aStr);
/** GetCurrentNode ADDREFFS and will get the current node from selection. /** GetCurrentNode ADDREFFS and will get the current node from selection.
* now it simply returns the first node in the dom * now it simply returns the first node in the dom
* @param nsIDOMNode **aNode is the return location of the dom node * @param nsIDOMNode **aNode is the return location of the dom node
@ -142,33 +149,6 @@ public:
*/ */
nsresult GetFirstNodeOfType(nsIDOMNode *aStartNode, const nsString &aTag, nsIDOMNode **aResult); nsresult GetFirstNodeOfType(nsIDOMNode *aStartNode, const nsString &aTag, nsIDOMNode **aResult);
/** ExecuteTransaction fires a transaction. It is provided here so
* clients need no knowledge of whether the editor has a transaction manager or not.
* If a transaction manager is present, it is used.
* Otherwise, the transaction is just executed directly.
*
* @param aTxn the transaction to execute
*/
nsresult ExecuteTransaction(nsITransaction *aTxn);
/** Undo reverses the effects of the last ExecuteTransaction operation
* It is provided here so clients need no knowledge of whether the editor has a transaction manager or not.
* If a transaction manager is present, it is told to undo and the result of
* that undo is returned.
* Otherwise, the Undo request is ignored.
*
*/
nsresult Undo();
/** Redo reverses the effects of the last Undo operation
* It is provided here so clients need no knowledge of whether the editor has a transaction manager or not.
* If a transaction manager is present, it is told to redo and the result of
* that redo is returned.
* Otherwise, the Redo request is ignored.
*
*/
nsresult Redo();
nsresult CreateElement(const nsString& aTag, nsresult CreateElement(const nsString& aTag,
nsIDOMNode * aParent, nsIDOMNode * aParent,
PRInt32 aPosition); PRInt32 aPosition);
@ -178,17 +158,15 @@ public:
nsresult DeleteSelection(); nsresult DeleteSelection();
nsresult InsertText(nsIDOMCharacterData *aElement, nsresult InsertText(const nsString& aStringToInsert);
PRUint32 aOffset,
const nsString& aStringToInsert);
nsresult DeleteText(nsIDOMCharacterData *aElement, nsresult DeleteText(nsIDOMCharacterData *aElement,
PRUint32 aOffset, PRUint32 aOffset,
PRUint32 aLength); PRUint32 aLength);
static nsresult SplitNode(nsIDOMNode * aNode, static nsresult SplitNode(nsIDOMNode * aExistingRightNode,
PRInt32 aOffset, PRInt32 aOffset,
nsIDOMNode * aNewNode, nsIDOMNode * aNewLeftNode,
nsIDOMNode * aParent); nsIDOMNode * aParent);
static nsresult JoinNodes(nsIDOMNode * aNodeToKeep, static nsresult JoinNodes(nsIDOMNode * aNodeToKeep,
@ -198,6 +176,10 @@ public:
nsresult Delete(PRBool aForward, PRUint32 aCount); nsresult Delete(PRBool aForward, PRUint32 aCount);
virtual nsresult InsertString(nsString *aString);
virtual nsresult Commit(PRBool aCtrlKey);
/*END private methods of nsEditor*/ /*END private methods of nsEditor*/
}; };
@ -207,3 +189,6 @@ factory method(s)
*/ */
nsresult NS_MakeEditorLoader(nsIContextLoader **aResult); nsresult NS_MakeEditorLoader(nsIContextLoader **aResult);
#endif

Просмотреть файл

@ -19,7 +19,6 @@
#include "editor.h" #include "editor.h"
#include "CreateElementTxn.h" #include "CreateElementTxn.h"
#include "SplitElementTxn.h"
#include "nsIDOMDocument.h" #include "nsIDOMDocument.h"
#include "nsIDOMElement.h" #include "nsIDOMElement.h"
@ -178,7 +177,7 @@ nsEditorKeyListener::KeyDown(nsIDOMEvent* aKeyEvent)
} }
else else
{ // XXX: delete the first P we find { // XXX: delete the first P we find
nsString pTag("P"); nsAutoString pTag("P");
nsCOMPtr<nsIDOMNode> currentNode; nsCOMPtr<nsIDOMNode> currentNode;
nsCOMPtr<nsIDOMNode> parentNode; nsCOMPtr<nsIDOMNode> parentNode;
nsCOMPtr<nsIDOMElement> element; nsCOMPtr<nsIDOMElement> element;
@ -199,25 +198,12 @@ nsEditorKeyListener::KeyDown(nsIDOMEvent* aKeyEvent)
{ {
// XXX Replace with x-platform NS-virtkeycode transform. // XXX Replace with x-platform NS-virtkeycode transform.
if (NS_OK == GetCharFromKeyCode(keyCode, isShift, & character)) { if (NS_OK == GetCharFromKeyCode(keyCode, isShift, & character)) {
nsString key; nsAutoString key;
key += character; key += character;
if (!isShift) { if (!isShift) {
key.ToLowerCase(); key.ToLowerCase();
} }
mEditor->InsertText(key);
// XXX: for now, just grab the first text node
nsCOMPtr<nsIDOMNode> currentNode;
nsCOMPtr<nsIDOMNode> textNode;
nsCOMPtr<nsIDOMCharacterData> text;
if (NS_SUCCEEDED(mEditor->GetCurrentNode(getter_AddRefs(currentNode))) &&
NS_SUCCEEDED(mEditor->GetFirstTextNode(currentNode,getter_AddRefs(textNode))) &&
NS_SUCCEEDED(textNode->QueryInterface(kIDOMCharacterDataIID, getter_AddRefs(text))))
{
// XXX: for now, just append the text
PRUint32 offset;
text->GetLength(&offset);
mEditor->InsertText(text, offset, key);
}
} }
} }
break; break;
@ -244,6 +230,10 @@ nsEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent)
return NS_OK; return NS_OK;
} }
/* these includes are for debug only. this module should never instantiate it's own transactions */
#include "SplitElementTxn.h"
#include "TransactionFactory.h"
static NS_DEFINE_IID(kSplitElementTxnIID, SPLIT_ELEMENT_TXN_IID);
nsresult nsresult
nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProcessed) nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProcessed)
{ {
@ -265,7 +255,7 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces
if (PR_TRUE==ctrlKey) if (PR_TRUE==ctrlKey)
{ {
if (nsnull!=mEditor) if (nsnull!=mEditor)
mEditor->Undo(); mEditor->Undo(1);
} }
aProcessed=PR_TRUE; aProcessed=PR_TRUE;
break; break;
@ -275,7 +265,7 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces
if (PR_TRUE==ctrlKey) if (PR_TRUE==ctrlKey)
{ {
if (nsnull!=mEditor) if (nsnull!=mEditor)
mEditor->Redo(); mEditor->Redo(1);
} }
aProcessed=PR_TRUE; aProcessed=PR_TRUE;
break; break;
@ -283,17 +273,27 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces
// hard-coded split node test: works on first <P> in the document // hard-coded split node test: works on first <P> in the document
case nsIDOMEvent::VK_S: case nsIDOMEvent::VK_S:
{ {
nsString pTag("P"); nsAutoString pTag("P");
nsCOMPtr<nsIDOMNode> currentNode; nsCOMPtr<nsIDOMNode> currentNode;
nsCOMPtr<nsIDOMElement> element; nsCOMPtr<nsIDOMElement> element;
if (NS_SUCCEEDED(mEditor->GetFirstNodeOfType(nsnull, pTag, getter_AddRefs(currentNode)))) if (NS_SUCCEEDED(mEditor->GetFirstNodeOfType(nsnull, pTag, getter_AddRefs(currentNode))))
{ {
nsresult result;
SplitElementTxn *txn; SplitElementTxn *txn;
if (PR_FALSE==isShift) // split the element so there are 0 children in the first half if (PR_FALSE==isShift) // split the element so there are 0 children in the first half
txn = new SplitElementTxn(mEditor, currentNode, -1); {
result = TransactionFactory::GetNewTransaction(kSplitElementTxnIID, (EditTxn **)&txn);
if (txn)
txn->Init(currentNode, -1);
}
else // split the element so there are 2 children in the first half else // split the element so there are 2 children in the first half
txn = new SplitElementTxn(mEditor, currentNode, 1); {
mEditor->ExecuteTransaction(txn); result = TransactionFactory::GetNewTransaction(kSplitElementTxnIID, (EditTxn **)&txn);
if (txn)
txn->Init(currentNode, 1);
}
if (txn)
mEditor->Do(txn);
} }
} }
aProcessed=PR_TRUE; aProcessed=PR_TRUE;
@ -305,10 +305,10 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces
{ {
//XXX: should be from a factory //XXX: should be from a factory
//XXX: should manage the refcount of txn //XXX: should manage the refcount of txn
nsString attribute("width"); nsAutoString attribute("width");
nsString value("400"); nsAutoString value("400");
nsString tableTag("TABLE"); nsAutoString tableTag("TABLE");
nsCOMPtr<nsIDOMNode> currentNode; nsCOMPtr<nsIDOMNode> currentNode;
nsCOMPtr<nsIDOMElement> element; nsCOMPtr<nsIDOMElement> element;
if (NS_SUCCEEDED(mEditor->GetFirstNodeOfType(nsnull, tableTag, getter_AddRefs(currentNode)))) if (NS_SUCCEEDED(mEditor->GetFirstNodeOfType(nsnull, tableTag, getter_AddRefs(currentNode))))
@ -331,11 +331,11 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces
nsresult result; nsresult result;
//XXX: should be from a factory //XXX: should be from a factory
//XXX: should manage the refcount of txn //XXX: should manage the refcount of txn
nsString attribute("src"); nsAutoString attribute("src");
nsString value("resource:/res/samples/raptor.jpg"); nsAutoString value("resource:/res/samples/raptor.jpg");
nsString imgTag("HR"); nsAutoString imgTag("HR");
nsString bodyTag("BODY"); nsAutoString bodyTag("BODY");
nsCOMPtr<nsIDOMNode> currentNode; nsCOMPtr<nsIDOMNode> currentNode;
result = mEditor->GetFirstNodeOfType(nsnull, bodyTag, getter_AddRefs(currentNode)); result = mEditor->GetFirstNodeOfType(nsnull, bodyTag, getter_AddRefs(currentNode));
if (NS_SUCCEEDED(result)) if (NS_SUCCEEDED(result))
@ -356,7 +356,7 @@ nsEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aProces
{ {
ChangeAttributeTxn *txn; ChangeAttributeTxn *txn;
txn = new ChangeAttributeTxn(mEditor, element, attribute, value, PR_FALSE); txn = new ChangeAttributeTxn(mEditor, element, attribute, value, PR_FALSE);
mEditor->ExecuteTransaction(txn); mEditor->Do(txn);
} }
} }
*/ */
@ -523,3 +523,4 @@ NS_NewEditorMouseListener(nsIDOMEventListener ** aInstancePtrResult, nsEditor *a
return it->QueryInterface(kIDOMEventListenerIID, (void **) aInstancePtrResult); return it->QueryInterface(kIDOMEventListenerIID, (void **) aInstancePtrResult);
} }

Просмотреть файл

@ -1,90 +0,0 @@
#!nmake
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
DEPTH=..\..
IGNORE_MANIFEST=1
LIBRARY_NAME=ender
CPPSRCS = \
editor.cpp \
editorInterfaces.cpp \
nsEditFactory.cpp \
EditTxn.cpp \
ChangeAttributeTxn.cpp \
InsertTextTxn.cpp \
DeleteTextTxn.cpp \
CreateElementTxn.cpp \
DeleteElementTxn.cpp \
DeleteRangeTxn.cpp \
SplitElementTxn.cpp \
$(NULL)
CPP_OBJS = \
.\$(OBJDIR)\editor.obj \
.\$(OBJDIR)\nsEditFactory.obj \
.\$(OBJDIR)\editorInterfaces.obj \
.\$(OBJDIR)\EditTxn.obj \
.\$(OBJDIR)\ChangeAttributeTxn.obj \
.\$(OBJDIR)\InsertTextTxn.obj \
.\$(OBJDIR)\DeleteTextTxn.obj \
.\$(OBJDIR)\CreateElementTxn.obj \
.\$(OBJDIR)\DeleteElementTxn.obj \
.\$(OBJDIR)\DeleteRangeTxn.obj \
.\$(OBJDIR)\SplitElementTxn.obj \
$(NULL)
MODULE=editor
REQUIRES=xpcom raptor dom base
LINCS=-I$(PUBLIC)\editor \
-I$(PUBLIC)\xpcom \
-I$(PUBLIC)\raptor \
-I$(PUBLIC)\js \
-I$(PUBLIC)\txmgr \
-I$(PUBLIC)\dom
MAKE_OBJ_TYPE = DLL
DLLNAME = ender
DLL=.\$(OBJDIR)\$(DLLNAME).dll
LCFLAGS = \
$(LCFLAGS) \
$(DEFINES) \
$(NULL)
# These are the libraries we need to link with to create the dll
LLIBS= \
$(DIST)\lib\xpcom32.lib \
$(DIST)\lib\libplc21.lib \
$(DIST)\lib\raptorbase.lib \
$(LIBNSPR)
!if "$(MOZ_BITS)"=="32" && defined(MOZ_DEBUG) && defined(GLOWCODE)
LLIBS=$(LLIBS) $(GLOWDIR)\glowcode.lib
!endif
include <$(DEPTH)\config\rules.mak>
libs:: $(DLL)
$(MAKE_INSTALL) .\$(OBJDIR)\$(DLLNAME).dll $(DIST)\bin
$(MAKE_INSTALL) .\$(OBJDIR)\$(DLLNAME).lib $(DIST)\lib
clobber::
rm -f $(DIST)\bin\$(DLLNAME).dll
rm -f $(DIST)\lib\$(DLLNAME).lib

Просмотреть файл

@ -17,23 +17,33 @@
*/ */
#include "ChangeAttributeTxn.h" #include "ChangeAttributeTxn.h"
#include "editor.h"
#include "nsIDOMElement.h" #include "nsIDOMElement.h"
#include "editor.h"
// note that aEditor is not refcounted ChangeAttributeTxn::ChangeAttributeTxn()
ChangeAttributeTxn::ChangeAttributeTxn(nsEditor *aEditor, : EditTxn()
nsIDOMElement *aElement,
const nsString& aAttribute,
const nsString& aValue,
PRBool aRemoveAttribute)
: EditTxn(aEditor)
{ {
mElement = aElement; }
mAttribute = aAttribute;
mValue = aValue; nsresult ChangeAttributeTxn::Init(nsIEditor *aEditor,
mRemoveAttribute = aRemoveAttribute; nsIDOMElement *aElement,
mAttributeWasSet=PR_FALSE; const nsString& aAttribute,
mUndoValue=""; const nsString& aValue,
PRBool aRemoveAttribute)
{
if (nsnull!=aEditor && nsnull!=aElement)
{
mEditor = aEditor;
mElement = aElement;
mAttribute = aAttribute;
mValue = aValue;
mRemoveAttribute = aRemoveAttribute;
mAttributeWasSet=PR_FALSE;
mUndoValue="";
return NS_OK;
}
else
return NS_ERROR_NULL_POINTER;
} }
nsresult ChangeAttributeTxn::Do(void) nsresult ChangeAttributeTxn::Do(void)

Просмотреть файл

@ -20,8 +20,14 @@
#define ChangeAttributeTxn_h__ #define ChangeAttributeTxn_h__
#include "EditTxn.h" #include "EditTxn.h"
#include "nsCOMPtr.h"
#include "nsIDOMElement.h"
#include "nsIEditor.h"
class nsIDOMElement; #define CHANGE_ATTRIBUTE_TXN_IID \
{/* 97818860-ac48-11d2-86d8-000064657374 */ \
0x97818860, 0xac48, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* A transaction that changes an attribute of a content node. * A transaction that changes an attribute of a content node.
@ -31,11 +37,16 @@ class ChangeAttributeTxn : public EditTxn
{ {
public: public:
ChangeAttributeTxn(nsEditor *aEditor, virtual nsresult Init(nsIEditor *aEditor,
nsIDOMElement *aElement, nsIDOMElement *aElement,
const nsString& aAttribute, const nsString& aAttribute,
const nsString& aValue, const nsString& aValue,
PRBool aRemoveAttribute); PRBool aRemoveAttribute);
private:
ChangeAttributeTxn();
public:
virtual nsresult Do(void); virtual nsresult Do(void);
@ -55,8 +66,11 @@ public:
protected: protected:
/** the editor that created this transaction */
nsCOMPtr<nsIEditor> mEditor;
/** the element to operate upon */ /** the element to operate upon */
nsIDOMElement *mElement; nsCOMPtr<nsIDOMElement> mElement;
/** the attribute to change */ /** the attribute to change */
nsString mAttribute; nsString mAttribute;
@ -72,6 +86,8 @@ protected:
/** PR_TRUE if the operation is to remove mAttribute from mElement */ /** PR_TRUE if the operation is to remove mAttribute from mElement */
PRBool mRemoveAttribute; PRBool mRemoveAttribute;
friend class TransactionFactory;
}; };
#endif #endif

Просмотреть файл

@ -17,52 +17,50 @@
*/ */
#include "CreateElementTxn.h" #include "CreateElementTxn.h"
#include "editor.h"
#include "nsIDOMNode.h"
#include "nsIDOMNodeList.h" #include "nsIDOMNodeList.h"
#include "nsIDOMDocument.h"
#include "nsIDOMElement.h"
// note that aEditor is not refcounted CreateElementTxn::CreateElementTxn()
CreateElementTxn::CreateElementTxn(nsEditor *aEditor, : EditTxn()
nsIDOMDocument *aDoc,
const nsString& aTag,
nsIDOMNode *aParent,
PRUint32 aOffsetInParent)
: EditTxn(aEditor)
{ {
mDoc = aDoc;
NS_ADDREF(mDoc);
mTag = aTag;
mParent = aParent;
NS_ADDREF(mParent);
mOffsetInParent = aOffsetInParent;
mNewNode = nsnull;
mRefNode = nsnull;
} }
nsresult CreateElementTxn::Init(nsIDOMDocument *aDoc,
const nsString& aTag,
nsIDOMNode *aParent,
PRUint32 aOffsetInParent)
{
if ((nsnull!=aDoc) && (nsnull!=aParent))
{
mDoc = aDoc;
mTag = aTag;
mParent = aParent;
mOffsetInParent = aOffsetInParent;
mNewNode = nsnull;
mRefNode = nsnull;
return NS_OK;
}
else
return NS_ERROR_NULL_POINTER;
}
CreateElementTxn::~CreateElementTxn() CreateElementTxn::~CreateElementTxn()
{ {
NS_IF_RELEASE(mDoc);
NS_IF_RELEASE(mParent);
NS_IF_RELEASE(mNewNode);
} }
nsresult CreateElementTxn::Do(void) nsresult CreateElementTxn::Do(void)
{ {
// create a new node // create a new node
nsresult result = mDoc->CreateElement(mTag, &mNewNode); nsresult result = mDoc->CreateElement(mTag, getter_AddRefs(mNewNode));
NS_ASSERTION(((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)), "could not create element."); NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewNode)), "could not create element.");
if ((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)) if ((NS_SUCCEEDED(result)) && (mNewNode))
{ {
// insert the new node // insert the new node
nsIDOMNode *resultNode=nsnull; nsCOMPtr<nsIDOMNode> resultNode;
if (CreateElementTxn::eAppend==mOffsetInParent) if (CreateElementTxn::eAppend==mOffsetInParent)
{ {
result = mParent->AppendChild(mNewNode, &resultNode); result = mParent->AppendChild(mNewNode, getter_AddRefs(resultNode));
if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode))
NS_RELEASE(resultNode); // this object already holds a ref from CreateElement
} }
else else
{ {
@ -70,12 +68,10 @@ nsresult CreateElementTxn::Do(void)
result = mParent->GetChildNodes(getter_AddRefs(childNodes)); result = mParent->GetChildNodes(getter_AddRefs(childNodes));
if ((NS_SUCCEEDED(result)) && (childNodes)) if ((NS_SUCCEEDED(result)) && (childNodes))
{ {
result = childNodes->Item(mOffsetInParent, &mRefNode); result = childNodes->Item(mOffsetInParent, getter_AddRefs(mRefNode));
if ((NS_SUCCEEDED(result)) && (nsnull!=mRefNode)) if ((NS_SUCCEEDED(result)) && (mRefNode))
{ {
result = mParent->InsertBefore(mNewNode, mRefNode, &resultNode); result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode));
if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode))
NS_RELEASE(resultNode); // this object already holds a ref from CreateElement
} }
} }
} }
@ -85,20 +81,16 @@ nsresult CreateElementTxn::Do(void)
nsresult CreateElementTxn::Undo(void) nsresult CreateElementTxn::Undo(void)
{ {
nsIDOMNode *resultNode=nsnull; nsCOMPtr<nsIDOMNode> resultNode;
nsresult result = mParent->RemoveChild(mNewNode, &resultNode); nsresult result = mParent->RemoveChild(mNewNode, getter_AddRefs(resultNode));
if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode))
NS_RELEASE(resultNode);
return result; return result;
} }
nsresult CreateElementTxn::Redo(void) nsresult CreateElementTxn::Redo(void)
{ {
nsIDOMNode *resultNode=nsnull; nsCOMPtr<nsIDOMNode> resultNode;
nsresult result = mParent->InsertBefore(mNewNode, mRefNode, &resultNode); nsresult result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode));
if ((NS_SUCCEEDED(result)) && (nsnull!=resultNode)) return result;
NS_RELEASE(resultNode);
return result;
} }
nsresult CreateElementTxn::GetIsTransient(PRBool *aIsTransient) nsresult CreateElementTxn::GetIsTransient(PRBool *aIsTransient)

Просмотреть файл

@ -20,10 +20,15 @@
#define CreateElementTxn_h__ #define CreateElementTxn_h__
#include "EditTxn.h" #include "EditTxn.h"
#include "nsIDOMDocument.h"
#include "nsIDOMNode.h"
#include "nsIDOMElement.h"
#include "nsCOMPtr.h"
class nsIDOMDocument; #define CREATE_ELEMENT_TXN_IID \
class nsIDOMNode; {/* 7a6393c0-ac48-11d2-86d8-000064657374 */ \
class nsIDOMElement; 0x7a6393c0, 0xac48, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* A transaction that creates a new node in the content tree. * A transaction that creates a new node in the content tree.
@ -34,11 +39,15 @@ public:
enum { eAppend=-1 }; enum { eAppend=-1 };
CreateElementTxn(nsEditor *aEditor, virtual nsresult Init(nsIDOMDocument *aDoc,
nsIDOMDocument *aDoc, const nsString& aTag,
const nsString& aTag, nsIDOMNode *aParent,
nsIDOMNode *aParent, PRUint32 aOffsetInParent);
PRUint32 aOffsetInParent);
private:
CreateElementTxn();
public:
virtual ~CreateElementTxn(); virtual ~CreateElementTxn();
@ -61,22 +70,24 @@ public:
protected: protected:
/** the document into which the new node will be inserted */ /** the document into which the new node will be inserted */
nsIDOMDocument *mDoc; nsCOMPtr<nsIDOMDocument> mDoc;
/** the tag (mapping to object type) for the new element */ /** the tag (mapping to object type) for the new element */
nsString mTag; nsString mTag;
/** the node into which the new node will be inserted */ /** the node into which the new node will be inserted */
nsIDOMNode *mParent; nsCOMPtr<nsIDOMNode> mParent;
/** the index in mParent for the new node */ /** the index in mParent for the new node */
PRUint32 mOffsetInParent; PRUint32 mOffsetInParent;
/** the new node to insert */ /** the new node to insert */
nsIDOMElement *mNewNode; nsCOMPtr<nsIDOMElement> mNewNode;
/** the node we will insert mNewNode before. We compute this ourselves. */ /** the node we will insert mNewNode before. We compute this ourselves. */
nsIDOMNode *mRefNode; nsCOMPtr<nsIDOMNode> mRefNode;
friend class TransactionFactory;
}; };

Просмотреть файл

@ -17,30 +17,32 @@
*/ */
#include "DeleteElementTxn.h" #include "DeleteElementTxn.h"
#include "editor.h" #ifdef NS_DEBUG
#include "nsIDOMDocument.h"
#include "nsIDOMElement.h" #include "nsIDOMElement.h"
#endif
// note that aEditor is not refcounted
DeleteElementTxn::DeleteElementTxn(nsEditor * aEditor, DeleteElementTxn::DeleteElementTxn()
nsIDOMDocument *aDoc, : EditTxn()
nsIDOMNode * aElement,
nsIDOMNode * aParent)
: EditTxn(aEditor)
{ {
mDoc = aDoc;
NS_ADDREF(mDoc);
mElement = aElement;
NS_ADDREF(mElement);
mParent = aParent;
NS_ADDREF(mParent);
} }
nsresult DeleteElementTxn::Init(nsIDOMNode *aElement,
nsIDOMNode *aParent)
{
if ((nsnull!=aElement) && (nsnull!=aParent))
{
mElement = aElement;
mParent = aParent;
return NS_OK;
}
else
return NS_ERROR_NULL_POINTER;
}
DeleteElementTxn::~DeleteElementTxn() DeleteElementTxn::~DeleteElementTxn()
{ {
NS_IF_RELEASE(mDoc);
NS_IF_RELEASE(mParent);
NS_IF_RELEASE(mElement);
} }
nsresult DeleteElementTxn::Do(void) nsresult DeleteElementTxn::Do(void)
@ -48,11 +50,32 @@ nsresult DeleteElementTxn::Do(void)
if (!mParent || !mElement) if (!mParent || !mElement)
return NS_ERROR_NULL_POINTER; return NS_ERROR_NULL_POINTER;
#ifdef NS_DEBUG #ifdef NS_DEBUG
// begin debug output
nsCOMPtr<nsIDOMElement> element=mElement;
nsAutoString elementTag="text node";
if (element)
element->GetTagName(elementTag);
nsCOMPtr<nsIDOMElement> parentElement=mParent;
nsAutoString parentElementTag="text node";
if (parentElement)
parentElement->GetTagName(parentElementTag);
char *c, *p;
c = elementTag.ToNewCString();
p = parentElementTag.ToNewCString();
if (c&&p)
{
printf("DeleteElementTxn: deleting child %s from parent %s\n", c, p);
delete [] c;
delete [] p;
}
// end debug output
// begin sanity check 1: parent-child relationship
nsresult testResult; nsresult testResult;
nsCOMPtr<nsIDOMNode> parentNode; nsCOMPtr<nsIDOMNode> parentNode;
testResult = mElement->GetParentNode(getter_AddRefs(parentNode)); testResult = mElement->GetParentNode(getter_AddRefs(parentNode));
NS_ASSERTION((NS_SUCCEEDED(testResult)), "bad mElement, couldn't get parent"); NS_ASSERTION((NS_SUCCEEDED(testResult)), "bad mElement, couldn't get parent");
NS_ASSERTION((parentNode==mParent), "bad mParent, mParent!=mElement->GetParent() "); NS_ASSERTION((parentNode==mParent), "bad mParent, mParent!=mElement->GetParent() ");
// end sanity check 1.
#endif #endif
// remember which child mElement was (by remembering which child was next) // remember which child mElement was (by remembering which child was next)

Просмотреть файл

@ -23,8 +23,10 @@
#include "nsIDOMNode.h" #include "nsIDOMNode.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
class nsIDOMDocument; #define DELETE_ELEMENT_TXN_IID \
class nsIDOMElement; {/* 6fd77770-ac49-11d2-86d8-000064657374 */ \
0x6fd77770, 0xac49, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* A transaction that deletes a single element * A transaction that deletes a single element
@ -33,10 +35,13 @@ class DeleteElementTxn : public EditTxn
{ {
public: public:
DeleteElementTxn(nsEditor *aEditor, virtual nsresult Init(nsIDOMNode *aElement,
nsIDOMDocument *aDoc, nsIDOMNode *aParent);
nsIDOMNode *aElement,
nsIDOMNode *aParent); private:
DeleteElementTxn();
public:
virtual ~DeleteElementTxn(); virtual ~DeleteElementTxn();
@ -58,14 +63,11 @@ public:
protected: protected:
/** the document into which the new node will be inserted */
nsIDOMDocument *mDoc;
/** the element to delete */ /** the element to delete */
nsIDOMNode *mElement; nsCOMPtr<nsIDOMNode> mElement;
/** the node into which the new node will be inserted */ /** the node into which the new node will be inserted */
nsIDOMNode *mParent; nsCOMPtr<nsIDOMNode> mParent;
/** the index in mParent for the new node */ /** the index in mParent for the new node */
PRUint32 mOffsetInParent; PRUint32 mOffsetInParent;
@ -73,6 +75,8 @@ protected:
/** the node we will insert mNewNode before. We compute this ourselves. */ /** the node we will insert mNewNode before. We compute this ourselves. */
nsCOMPtr<nsIDOMNode> mRefNode; nsCOMPtr<nsIDOMNode> mRefNode;
friend class TransactionFactory;
}; };
#endif #endif

Просмотреть файл

@ -17,18 +17,73 @@
*/ */
#include "DeleteRangeTxn.h" #include "DeleteRangeTxn.h"
#include "editor.h"
#include "nsIDOMRange.h" #include "nsIDOMRange.h"
#include "nsIDOMCharacterData.h"
#include "nsIDOMNodeList.h"
#include "DeleteTextTxn.h"
#include "DeleteElementTxn.h"
#include "TransactionFactory.h"
#include "nsISupportsArray.h"
static NS_DEFINE_IID(kDeleteTextTxnIID, DELETE_TEXT_TXN_IID);
static NS_DEFINE_IID(kDeleteElementTxnIID, DELETE_ELEMENT_TXN_IID);
// note that aEditor is not refcounted // note that aEditor is not refcounted
DeleteRangeTxn::DeleteRangeTxn(nsEditor *aEditor, DeleteRangeTxn::DeleteRangeTxn()
nsIDOMRange *aRange) : EditAggregateTxn()
: EditTxn(aEditor)
{ {
aRange->GetStartParent(getter_AddRefs(mStartParent)); }
aRange->GetEndParent(getter_AddRefs(mEndParent));
aRange->GetStartOffset(&mStartOffset); nsresult DeleteRangeTxn::Init(nsIDOMRange *aRange)
aRange->GetEndOffset(&mEndOffset); {
if (nsnull!=aRange)
{
nsresult result = aRange->GetStartParent(getter_AddRefs(mStartParent));
NS_ASSERTION((NS_SUCCEEDED(result)), "GetStartParent failed.");
result = aRange->GetEndParent(getter_AddRefs(mEndParent));
NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndParent failed.");
result = aRange->GetStartOffset(&mStartOffset);
NS_ASSERTION((NS_SUCCEEDED(result)), "GetStartOffset failed.");
result = aRange->GetEndOffset(&mEndOffset);
NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndOffset failed.");
result = aRange->GetCommonParent(getter_AddRefs(mCommonParent));
NS_ASSERTION((NS_SUCCEEDED(result)), "GetCommonParent failed.");
#ifdef NS_DEBUG
PRUint32 count;
nsCOMPtr<nsIDOMCharacterData> textNode;
textNode = mStartParent;
if (textNode)
textNode->GetLength(&count);
else
{
nsCOMPtr<nsIDOMNodeList> children;
result = mStartParent->GetChildNodes(getter_AddRefs(children));
NS_ASSERTION(((NS_SUCCEEDED(result)) && children), "bad start child list");
children->GetLength(&count);
}
NS_ASSERTION(mStartOffset<count, "bad start offset");
textNode = mEndParent;
if (textNode)
textNode->GetLength(&count);
else
{
nsCOMPtr<nsIDOMNodeList> children;
result = mEndParent->GetChildNodes(getter_AddRefs(children));
NS_ASSERTION(((NS_SUCCEEDED(result)) && children), "bad end child list");
children->GetLength(&count);
}
NS_ASSERTION(mEndOffset<count, "bad end offset");
printf ("DeleteRange: %d of %p to %d of %p\n",
mStartOffset, (void *)mStartParent, mEndOffset, (void *)mEndParent);
#endif
return result;
}
else
return NS_ERROR_NULL_POINTER;
} }
DeleteRangeTxn::~DeleteRangeTxn() DeleteRangeTxn::~DeleteRangeTxn()
@ -37,28 +92,59 @@ DeleteRangeTxn::~DeleteRangeTxn()
nsresult DeleteRangeTxn::Do(void) nsresult DeleteRangeTxn::Do(void)
{ {
if (!mStartParent || !mEndParent) if (!mStartParent || !mEndParent || !mCommonParent)
return NS_ERROR_NULL_POINTER; return NS_ERROR_NULL_POINTER;
nsresult result; nsresult result;
// build the child transactions
if (mStartParent==mEndParent)
{ // the selection begins and ends in the same node
result = CreateTxnsToDeleteBetween(mStartParent, mStartOffset, mEndOffset);
}
else
{ // the selection ends in a different node from where it started
// delete the relevant content in the start node
result = CreateTxnsToDeleteContent(mStartParent, mStartOffset, nsIEditor::eLTR);
if (NS_SUCCEEDED(result))
{
// delete the intervening nodes
result = CreateTxnsToDeleteNodesBetween(mCommonParent, mStartParent, mEndParent);
if (NS_SUCCEEDED(result))
{
// delete the relevant content in the end node
result = CreateTxnsToDeleteContent(mEndParent, mEndOffset, nsIEditor::eRTL);
if (NS_SUCCEEDED(result))
{ // now we have all our child transactions, do them
result = EditAggregateTxn::Do();
}
}
}
}
// if we've successfully built this aggregate transaction, then do it.
if (NS_SUCCEEDED(result))
result = EditAggregateTxn::Do();
return result; return result;
} }
nsresult DeleteRangeTxn::Undo(void) nsresult DeleteRangeTxn::Undo(void)
{ {
if (!mStartParent || !mEndParent) if (!mStartParent || !mEndParent || !mCommonParent)
return NS_ERROR_NULL_POINTER; return NS_ERROR_NULL_POINTER;
nsresult result; nsresult result = EditAggregateTxn::Undo();
return result; return result;
} }
nsresult DeleteRangeTxn::Redo(void) nsresult DeleteRangeTxn::Redo(void)
{ {
if (!mStartParent || !mEndParent) if (!mStartParent || !mEndParent || !mCommonParent)
return NS_ERROR_NULL_POINTER; return NS_ERROR_NULL_POINTER;
nsresult result; nsresult result = EditAggregateTxn::Redo();
return result; return result;
} }
@ -98,3 +184,265 @@ nsresult DeleteRangeTxn::GetRedoString(nsString **aString)
} }
return NS_OK; return NS_OK;
} }
nsresult DeleteRangeTxn::CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent,
PRUint32 aStartOffset,
PRUint32 aEndOffset)
{
nsresult result;
// see what kind of node we have
nsCOMPtr<nsIDOMCharacterData> textNode;
textNode = aStartParent; // this uses implicit nsCOMPtr QI
if (textNode)
{ // if the node is a text node, then delete text content
DeleteTextTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(textNode, aStartOffset, (aEndOffset-aStartOffset)+1);
AppendChild(txn);
}
}
else
{
PRUint32 childCount;
nsCOMPtr<nsIDOMNodeList> children;
result = aStartParent->GetChildNodes(getter_AddRefs(children));
if ((NS_SUCCEEDED(result)) && children)
{
children->GetLength(&childCount);
NS_ASSERTION(aEndOffset<childCount, "bad aEndOffset");
PRUint32 i;
for (i=aStartOffset; i<=aEndOffset; i++)
{
nsCOMPtr<nsIDOMNode> child;
result = children->Item(i, getter_AddRefs(child));
if ((NS_SUCCEEDED(result)) && child)
{
DeleteElementTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(child, aStartParent);
AppendChild(txn);
}
else
return NS_ERROR_NULL_POINTER;
}
}
}
}
return result;
}
nsresult DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent,
PRUint32 aOffset,
nsIEditor::Direction aDir)
{
nsresult result;
// see what kind of node we have
nsCOMPtr<nsIDOMCharacterData> textNode;
textNode = aParent; // this uses implicit nsCOMPtr QI
if (textNode)
{ // if the node is a text node, then delete text content
PRUint32 start, numToDelete;
if (nsIEditor::eLTR==aDir)
{
start=aOffset;
textNode->GetLength(&numToDelete);
numToDelete -= (aOffset+1);
}
else
{
start=0;
numToDelete=aOffset;
}
DeleteTextTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteTextTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(textNode, start, numToDelete);
AppendChild(txn);
}
else
return NS_ERROR_NULL_POINTER;
}
else
{ // we have an interior node, so delete some of its children
if (nsIEditor::eLTR==aDir)
{ // delete from aOffset to end
PRUint32 childCount;
nsCOMPtr<nsIDOMNodeList> children;
result = aParent->GetChildNodes(getter_AddRefs(children));
if ((NS_SUCCEEDED(result)) && children)
{
children->GetLength(&childCount);
PRUint32 i;
for (i=aOffset; i<childCount; i++)
{
nsCOMPtr<nsIDOMNode> child;
result = children->Item(i, getter_AddRefs(child));
if ((NS_SUCCEEDED(result)) && child)
{
DeleteElementTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(child, aParent);
AppendChild(txn);
}
else
return NS_ERROR_NULL_POINTER;
}
}
}
}
else
{ // delete from 0 to aOffset
nsCOMPtr<nsIDOMNode> child;
result = aParent->GetFirstChild(getter_AddRefs(child));
for (PRUint32 i=0; i<aOffset; i++)
{
if ((NS_SUCCEEDED(result)) && child)
{
DeleteElementTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(child, aParent);
AppendChild(txn);
}
else
return NS_ERROR_NULL_POINTER;
}
nsCOMPtr<nsIDOMNode> temp = child;
result = temp->GetNextSibling(getter_AddRefs(child));
}
}
}
return result;
}
nsresult DeleteRangeTxn::CreateTxnsToDeleteNodesBetween(nsIDOMNode *aCommonParent,
nsIDOMNode *aFirstChild,
nsIDOMNode *aLastChild)
{
nsresult result;
PRBool needToProcessLastChild=PR_TRUE; // set to false if we discover we can delete all required nodes by just walking up aFirstChild's parent list
nsCOMPtr<nsIDOMNode> parent; // the current parent in the iteration up the ancestors
nsCOMPtr<nsIDOMNode> child; // the current child of parent
nsISupportsArray *ancestorList; // the ancestorList of the other endpoint, used to gate deletion
NS_NewISupportsArray(&ancestorList);
if (nsnull==ancestorList)
return NS_ERROR_NULL_POINTER;
// Walk up the parent list of aFirstChild to aCommonParent,
// deleting all siblings to the right of the ancestors of aFirstChild.
BuildAncestorList(aLastChild, ancestorList);
child = aFirstChild;
result = child->GetParentNode(getter_AddRefs(parent));
while ((NS_SUCCEEDED(result)) && parent)
{
while ((NS_SUCCEEDED(result)) && child)
{ // this loop starts with the first sibling of an ancestor of aFirstChild
nsCOMPtr<nsIDOMNode> temp = child;
result = temp->GetNextSibling(getter_AddRefs(child));
if ((NS_SUCCEEDED(result)) && child)
{
if (child==aLastChild)
{ // aFirstChild and aLastChild have the same parent, and we've reached aLastChild
needToProcessLastChild = PR_FALSE;
break;
}
// test if child is an ancestor of the other node. If it is, don't process this parent anymore
PRInt32 index;
index = ancestorList->IndexOf((nsISupports*)child);
if (-1!=index)
break;
DeleteElementTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(child, parent);
AppendChild(txn);
}
else
return NS_ERROR_NULL_POINTER;
}
}
if (parent==aCommonParent)
break;
child = parent;
nsCOMPtr<nsIDOMNode> temp=parent;
result = temp->GetParentNode(getter_AddRefs(parent));
}
// Walk up the parent list of aLastChild to aCommonParent,
// deleting all siblings to the left of the ancestors of aLastChild.
BuildAncestorList(aFirstChild, ancestorList);
if (PR_TRUE==needToProcessLastChild)
{
child = aLastChild;
result = child->GetParentNode(getter_AddRefs(parent));
while ((NS_SUCCEEDED(result)) && parent)
{
while ((NS_SUCCEEDED(result)) && child)
{ // this loop starts with the first sibling of an ancestor of aFirstChild
nsCOMPtr<nsIDOMNode> temp = child;
result = temp->GetPreviousSibling(getter_AddRefs(child));
if ((NS_SUCCEEDED(result)) && child)
{
// test if child is an ancestor of the other node. If it is, don't process this parent anymore
PRInt32 index;
index = ancestorList->IndexOf((nsISupports*)child);
if (-1!=index)
break;
DeleteElementTxn *txn;
result = TransactionFactory::GetNewTransaction(kDeleteElementTxnIID, (EditTxn **)&txn);
if (nsnull!=txn)
{
txn->Init(child, parent);
AppendChild(txn);
}
else
return NS_ERROR_NULL_POINTER;
}
}
if (parent==aCommonParent)
break;
child = parent;
nsCOMPtr<nsIDOMNode> temp=parent;
result = temp->GetParentNode(getter_AddRefs(parent));
}
}
NS_RELEASE(ancestorList);
return result;
}
nsresult DeleteRangeTxn::BuildAncestorList(nsIDOMNode *aNode, nsISupportsArray *aList)
{
nsresult result=NS_OK;
if (nsnull!=aNode && nsnull!=aList)
{
aList->Clear();
nsCOMPtr<nsIDOMNode> parent;
nsCOMPtr<nsIDOMNode> child = aNode;
result = child->GetParentNode(getter_AddRefs(parent));
while ((NS_SUCCEEDED(result)) && child && parent)
{
nsISupports * parentAsISupports;
parent->QueryInterface(nsISupports::IID(), (void **)&parentAsISupports);
aList->AppendElement(parentAsISupports);
child = parent;
nsCOMPtr<nsIDOMNode> temp=parent;
result = temp->GetParentNode(getter_AddRefs(parent));
}
}
else
result = NS_ERROR_NULL_POINTER;
return result;
}

Просмотреть файл

@ -19,22 +19,33 @@
#ifndef DeleteRangeTxn_h__ #ifndef DeleteRangeTxn_h__
#define DeleteRangeTxn_h__ #define DeleteRangeTxn_h__
#include "EditTxn.h" #include "EditAggregateTxn.h"
#include "nsIDOMNode.h" #include "nsIDOMNode.h"
#include "nsIEditor.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
class nsIDOMDocument; class nsIDOMDocument;
class nsIDOMRange; class nsIDOMRange;
class nsISupportsArray;
#define DELETE_RANGE_TXN_IID \
{/* 5ec6b260-ac49-11d2-86d8-000064657374 */ \
0x5ec6b260, 0xac49, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* A transaction that deletes an entire range in the content tree * A transaction that deletes an entire range in the content tree
*/ */
class DeleteRangeTxn : public EditTxn class DeleteRangeTxn : public EditAggregateTxn
{ {
public: public:
DeleteRangeTxn(nsEditor *aEditor, virtual nsresult Init(nsIDOMRange *aRange);
nsIDOMRange *aRange);
private:
DeleteRangeTxn();
public:
virtual ~DeleteRangeTxn(); virtual ~DeleteRangeTxn();
@ -54,6 +65,23 @@ public:
virtual nsresult GetRedoString(nsString **aString); virtual nsresult GetRedoString(nsString **aString);
protected:
virtual nsresult CreateTxnsToDeleteBetween(nsIDOMNode *aStartParent,
PRUint32 aStartOffset,
PRUint32 aEndOffset);
virtual nsresult CreateTxnsToDeleteNodesBetween(nsIDOMNode *aParent,
nsIDOMNode *aFirstChild,
nsIDOMNode *aLastChild);
virtual nsresult CreateTxnsToDeleteContent(nsIDOMNode *aParent,
PRUint32 aOffset,
nsIEditor::Direction aDir);
virtual nsresult BuildAncestorList(nsIDOMNode *aNode,
nsISupportsArray *aList);
protected: protected:
/** p1 in the range */ /** p1 in the range */
@ -65,9 +93,14 @@ protected:
/** p2 in the range */ /** p2 in the range */
nsCOMPtr<nsIDOMNode> mEndParent; nsCOMPtr<nsIDOMNode> mEndParent;
/** the closest common parent of p1 and p2 */
nsCOMPtr<nsIDOMNode> mCommonParent;
/** p2 offset */ /** p2 offset */
PRInt32 mEndOffset; PRInt32 mEndOffset;
friend class TransactionFactory;
}; };
#endif #endif

Просмотреть файл

@ -17,20 +17,23 @@
*/ */
#include "DeleteTextTxn.h" #include "DeleteTextTxn.h"
#include "editor.h"
#include "nsIDOMCharacterData.h" #include "nsIDOMCharacterData.h"
// note that aEditor is not refcounted
DeleteTextTxn::DeleteTextTxn(nsEditor *aEditor, DeleteTextTxn::DeleteTextTxn()
nsIDOMCharacterData *aElement, : EditTxn()
{
}
nsresult DeleteTextTxn::Init(nsIDOMCharacterData *aElement,
PRUint32 aOffset, PRUint32 aOffset,
PRUint32 aNumCharsToDelete) PRUint32 aNumCharsToDelete)
: EditTxn(aEditor)
{ {
mElement = aElement; mElement = aElement;
mOffset = aOffset; mOffset = aOffset;
mNumCharsToDelete = aNumCharsToDelete; mNumCharsToDelete = aNumCharsToDelete;
mDeletedText = ""; mDeletedText = "";
return NS_OK;
} }
nsresult DeleteTextTxn::Do(void) nsresult DeleteTextTxn::Do(void)

Просмотреть файл

@ -20,8 +20,13 @@
#define DeleteTextTxn_h__ #define DeleteTextTxn_h__
#include "EditTxn.h" #include "EditTxn.h"
#include "nsIDOMCharacterData.h"
#include "nsCOMPtr.h"
class nsIDOMCharacterData; #define DELETE_TEXT_TXN_IID \
{/* 4d3a2720-ac49-11d2-86d8-000064657374 */ \
0x4d3a2720, 0xac49, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* A transaction that changes an attribute of a content node. * A transaction that changes an attribute of a content node.
@ -31,10 +36,14 @@ class DeleteTextTxn : public EditTxn
{ {
public: public:
DeleteTextTxn(nsEditor *aEditor, virtual nsresult Init(nsIDOMCharacterData *aElement,
nsIDOMCharacterData *aElement, PRUint32 aOffset,
PRUint32 aOffset, PRUint32 aNumCharsToDelete);
PRUint32 aNumCharsToDelete);
private:
DeleteTextTxn();
public:
virtual nsresult Do(void); virtual nsresult Do(void);
@ -53,7 +62,7 @@ public:
protected: protected:
/** the text element to operate upon */ /** the text element to operate upon */
nsIDOMCharacterData *mElement; nsCOMPtr<nsIDOMCharacterData> mElement;
/** the offset into mElement where the deletion is to take place */ /** the offset into mElement where the deletion is to take place */
PRUint32 mOffset; PRUint32 mOffset;
@ -64,6 +73,8 @@ protected:
/** the text that was deleted */ /** the text that was deleted */
nsString mDeletedText; nsString mDeletedText;
friend class TransactionFactory;
}; };
#endif #endif

Просмотреть файл

@ -0,0 +1,145 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL") you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "EditAggregateTxn.h"
#include "nsCOMPtr.h"
#include "nsIDOMNode.h"
#include "nsVoidArray.h"
EditAggregateTxn::EditAggregateTxn()
: EditTxn()
{
mChildren = new nsVoidArray();
}
EditAggregateTxn::~EditAggregateTxn()
{
if (nsnull!=mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
for (i=0; i<count; i++)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
NS_IF_RELEASE(txn);
}
delete mChildren;
}
}
nsresult EditAggregateTxn::Do(void)
{
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
if (nsnull!=mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
for (i=0; i<count; i++)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
result = txn->Do();
if (NS_FAILED(result))
break;
}
}
return result;
}
nsresult EditAggregateTxn::Undo(void)
{
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
if (nsnull!=mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
// undo goes through children backwards
for (i=count-1; i>=0; i--)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
result = txn->Undo();
if (NS_FAILED(result))
break;
}
}
return result;
}
nsresult EditAggregateTxn::Redo(void)
{
nsresult result=NS_OK; // it's legal (but not very useful) to have an empty child list
if (nsnull!=mChildren)
{
PRInt32 i;
PRInt32 count = mChildren->Count();
for (i=0; i<count; i++)
{
EditTxn *txn = (EditTxn*)(mChildren->ElementAt(i));
result = txn->Redo();
if (NS_FAILED(result))
break;
}
}
return result;
}
nsresult EditAggregateTxn::GetIsTransient(PRBool *aIsTransient)
{
if (nsnull!=aIsTransient)
*aIsTransient = PR_TRUE;
return NS_OK;
}
nsresult EditAggregateTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction)
{
return NS_OK;
}
nsresult EditAggregateTxn::Write(nsIOutputStream *aOutputStream)
{
return NS_OK;
}
nsresult EditAggregateTxn::GetUndoString(nsString **aString)
{
if (nsnull!=aString)
*aString=nsnull;
return NS_OK;
}
nsresult EditAggregateTxn::GetRedoString(nsString **aString)
{
if (nsnull!=aString)
*aString=nsnull;
return NS_OK;
}
nsresult EditAggregateTxn::AppendChild(EditTxn *aTxn)
{
if ((nsnull!=mChildren) && (nsnull!=aTxn))
{
mChildren->AppendElement(aTxn);
return NS_OK;
}
return NS_ERROR_NULL_POINTER;
}

Просмотреть файл

@ -0,0 +1,67 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef EditAggregateTxn_h__
#define EditAggregateTxn_h__
#include "EditTxn.h"
#define EDIT_AGGREGATE_TXN_IID \
{/* 345921a0-ac49-11d2-86d8-000064657374 */ \
0x345921a0, 0xac49, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
class nsVoidArray;
/**
* base class for all document editing transactions that require aggregation.
* provides a list of child transactions.
*/
class EditAggregateTxn : public EditTxn
{
public:
EditAggregateTxn();
virtual ~EditAggregateTxn();
virtual nsresult Do(void);
virtual nsresult Undo(void);
virtual nsresult Redo(void);
virtual nsresult GetIsTransient(PRBool *aIsTransient);
virtual nsresult Merge(PRBool *aDidMerge, nsITransaction *aTransaction);
virtual nsresult Write(nsIOutputStream *aOutputStream);
virtual nsresult GetUndoString(nsString **aString);
virtual nsresult GetRedoString(nsString **aString);
virtual nsresult AppendChild(EditTxn *aTxn);
protected:
nsVoidArray *mChildren;
};
#endif

Просмотреть файл

@ -28,10 +28,8 @@ NS_IMPL_ADDREF(EditTxn)
NS_IMPL_RELEASE(EditTxn) NS_IMPL_RELEASE(EditTxn)
// note that aEditor is not refcounted // note that aEditor is not refcounted
EditTxn::EditTxn(nsEditor *aEditor) EditTxn::EditTxn()
{ {
NS_ASSERTION(nsnull!=aEditor, "null aEditor arg to EditTxn constructor");
mEditor = aEditor;
} }
nsresult EditTxn::Do(void) nsresult EditTxn::Do(void)

Просмотреть файл

@ -20,13 +20,17 @@
#define EditTxn_h__ #define EditTxn_h__
#include "nsITransaction.h" #include "nsITransaction.h"
class nsEditor;
class nsIDOMNode; class nsIDOMNode;
#define EDIT_TXN_IID \
{/* c5ea31b0-ac48-11d2-86d8-000064657374 */ \
0xc5ea31b0, 0xac48, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* base class for all document editing transactions. * base class for all document editing transactions.
* provides access to the nsEditor that created this transaction. * provides default concrete behavior for all nsITransaction methods.
*/ */
class EditTxn : public nsITransaction class EditTxn : public nsITransaction
{ {
@ -34,7 +38,7 @@ public:
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
EditTxn(nsEditor *aEditor); EditTxn();
virtual nsresult Do(void); virtual nsresult Do(void);
@ -52,10 +56,6 @@ public:
virtual nsresult GetRedoString(nsString **aString); virtual nsresult GetRedoString(nsString **aString);
protected:
nsEditor *mEditor;
}; };
#endif #endif

Просмотреть файл

@ -20,18 +20,22 @@
#include "editor.h" #include "editor.h"
#include "nsIDOMCharacterData.h" #include "nsIDOMCharacterData.h"
static NS_DEFINE_IID(kInsertTextTxnIID, INSERTTEXTTXN_IID); static NS_DEFINE_IID(kInsertTextTxnIID, INSERT_TEXT_TXN_IID);
// note that aEditor is not refcounted
InsertTextTxn::InsertTextTxn(nsEditor *aEditor, InsertTextTxn::InsertTextTxn()
nsIDOMCharacterData *aElement, : EditTxn()
{
}
nsresult InsertTextTxn::Init(nsIDOMCharacterData *aElement,
PRUint32 aOffset, PRUint32 aOffset,
const nsString& aStringToInsert) const nsString& aStringToInsert)
: EditTxn(aEditor)
{ {
mElement = aElement; mElement = aElement;
mOffset = aOffset; mOffset = aOffset;
mStringToInsert = aStringToInsert; mStringToInsert = aStringToInsert;
return NS_OK;
} }
nsresult InsertTextTxn::Do(void) nsresult InsertTextTxn::Do(void)
@ -60,11 +64,11 @@ nsresult InsertTextTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction)
if ((nsnull!=aDidMerge) && (nsnull!=aTransaction)) if ((nsnull!=aDidMerge) && (nsnull!=aTransaction))
{ {
// if aTransaction isa InsertTextTxn, absorb it // if aTransaction isa InsertTextTxn, absorb it
nsCOMPtr<InsertTextTxn> otherTxn; nsCOMPtr<InsertTextTxn> otherTxn = aTransaction;
nsresult result = aTransaction->QueryInterface(kInsertTextTxnIID, getter_AddRefs(otherTxn)); nsresult result=NS_OK;// = aTransaction->QueryInterface(kInsertTextTxnIID, getter_AddRefs(otherTxn));
if (NS_SUCCEEDED(result) && (otherTxn)) if (NS_SUCCEEDED(result) && (otherTxn))
{ {
nsString otherData; nsAutoString otherData;
otherTxn->GetData(otherData); otherTxn->GetData(otherData);
mStringToInsert += otherData; mStringToInsert += otherData;
} }

Просмотреть файл

@ -20,14 +20,14 @@
#define InsertTextTxn_h__ #define InsertTextTxn_h__
#include "EditTxn.h" #include "EditTxn.h"
#include "nsIDOMCharacterData.h"
#include "nsCOMPtr.h"
#define INSERTTEXTTXN_IID \ #define INSERT_TEXT_TXN_IID \
{/* 93276f00-ab2c-11d2-8f4b-006008159b0c*/ \ {/* 93276f00-ab2c-11d2-8f4b-006008159b0c*/ \
0x93276f00, 0xab2c, 0x11d2, \ 0x93276f00, 0xab2c, 0x11d2, \
{0x8f, 0xb4, 0x0, 0x60, 0x8, 0x15, 0x9b, 0xc} } {0x8f, 0xb4, 0x0, 0x60, 0x8, 0x15, 0x9b, 0xc} }
class nsIDOMCharacterData;
/** /**
* A transaction that changes an attribute of a content node. * A transaction that changes an attribute of a content node.
* This transaction covers add, remove, and change attribute. * This transaction covers add, remove, and change attribute.
@ -36,15 +36,13 @@ class InsertTextTxn : public EditTxn
{ {
public: public:
InsertTextTxn(nsEditor *aEditor, virtual nsresult Init(nsIDOMCharacterData *aElement,
nsIDOMCharacterData *aElement, PRUint32 aOffset,
PRUint32 aOffset, const nsString& aStringToInsert);
const nsString& aStringToInsert);
private: private:
// default ctor so that nsCOMPtr is happy InsertTextTxn();
InsertTextTxn() : EditTxn(nsnull) {}
public: public:
@ -67,7 +65,7 @@ public:
// override QueryInterface to handle InsertTextTxn request // override QueryInterface to handle InsertTextTxn request
NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
static const nsIID& IID() { static nsIID iid = INSERTTEXTTXN_IID; return iid; } static const nsIID& IID() { static nsIID iid = INSERT_TEXT_TXN_IID; return iid; }
virtual nsresult GetData(nsString& aResult); virtual nsresult GetData(nsString& aResult);
@ -75,7 +73,7 @@ public:
protected: protected:
/** the text element to operate upon */ /** the text element to operate upon */
nsIDOMCharacterData *mElement; nsCOMPtr<nsIDOMCharacterData> mElement;
/** the offset into mElement where the insertion is to take place */ /** the offset into mElement where the insertion is to take place */
PRUint32 mOffset; PRUint32 mOffset;
@ -86,6 +84,8 @@ protected:
/** the text to insert into mElement at mOffset */ /** the text to insert into mElement at mOffset */
nsString mStringToInsert; nsString mStringToInsert;
friend class TransactionFactory;
}; };
#endif #endif

Просмотреть файл

@ -0,0 +1,122 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "JoinElementTxn.h"
#include "nsIDOMNodeList.h"
#include "editor.h"
JoinElementTxn::JoinElementTxn()
: EditTxn()
{
}
nsresult JoinElementTxn::Init(nsIDOMNode *aLeftNode,
nsIDOMNode *aRightNode)
{
mLeftNode = aLeftNode;
mRightNode = aRightNode;
mOffset=0;
return NS_OK;
}
JoinElementTxn::~JoinElementTxn()
{
}
nsresult JoinElementTxn::Do(void)
{
nsresult result;
if ((mLeftNode) && (mRightNode))
{ // get the parent node
nsCOMPtr<nsIDOMNode>leftParent;
result = mLeftNode->GetParentNode(getter_AddRefs(leftParent));
if ((NS_SUCCEEDED(result)) && (leftParent))
{ // verify that mLeftNode and mRightNode have the same parent
nsCOMPtr<nsIDOMNode>rightParent;
result = mRightNode->GetParentNode(getter_AddRefs(rightParent));
if ((NS_SUCCEEDED(result)) && (rightParent))
{
if (leftParent==rightParent)
{
mParent=leftParent; // set this instance mParent.
// Other methods see a non-null mParent and know all is well
nsCOMPtr<nsIDOMNodeList> childNodes;
result = mLeftNode->GetChildNodes(getter_AddRefs(childNodes));
if ((NS_SUCCEEDED(result)) && (childNodes))
{
childNodes->GetLength(&mOffset);
}
result = nsEditor::JoinNodes(mLeftNode, mRightNode, mParent, PR_FALSE);
}
}
}
}
return result;
}
nsresult JoinElementTxn::Undo(void)
{
nsresult result = nsEditor::SplitNode(mRightNode, mOffset, mLeftNode, mParent);
return result;
}
nsresult JoinElementTxn::Redo(void)
{
nsresult result = nsEditor::JoinNodes(mLeftNode, mRightNode, mParent, PR_FALSE);
return result;
}
nsresult JoinElementTxn::GetIsTransient(PRBool *aIsTransient)
{
if (nsnull!=aIsTransient)
*aIsTransient = PR_FALSE;
return NS_OK;
}
nsresult JoinElementTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction)
{
if (nsnull!=aDidMerge)
*aDidMerge=PR_FALSE;
return NS_OK;
}
nsresult JoinElementTxn::Write(nsIOutputStream *aOutputStream)
{
return NS_OK;
}
nsresult JoinElementTxn::GetUndoString(nsString **aString)
{
if (nsnull!=aString)
{
**aString="Join Element";
}
return NS_OK;
}
nsresult JoinElementTxn::GetRedoString(nsString **aString)
{
if (nsnull!=aString)
{
**aString="Split Element";
}
return NS_OK;
}

Просмотреть файл

@ -0,0 +1,86 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef JoinElementTxn_h__
#define JoinElementTxn_h__
#include "EditTxn.h"
#include "nsIDOMNode.h"
#include "nsCOMPtr.h"
#define JOIN_ELEMENT_TXN_IID \
{/* 9bc5f9f0-ac48-11d2-86d8-000064657374 */ \
0x9bc5f9f0, 0xac48, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/**
* A transaction that joins two elements E1 and E2 into a single node E.
* The children of E are the children of E1 followed by the children of E2.
*/
class JoinElementTxn : public EditTxn
{
public:
virtual nsresult Init(nsIDOMNode *aLeftNode,
nsIDOMNode *aRightNode);
protected:
JoinElementTxn();
public:
virtual ~JoinElementTxn();
virtual nsresult Do(void);
virtual nsresult Undo(void);
virtual nsresult Redo(void);
virtual nsresult GetIsTransient(PRBool *aIsTransient);
virtual nsresult Merge(PRBool *aDidMerge, nsITransaction *aTransaction);
virtual nsresult Write(nsIOutputStream *aOutputStream);
virtual nsresult GetUndoString(nsString **aString);
virtual nsresult GetRedoString(nsString **aString);
protected:
/** the elements to operate upon.
* After the merge, mRightNode remains and mLeftNode is removed from the content tree.
*/
nsCOMPtr<nsIDOMNode> mLeftNode;
nsCOMPtr<nsIDOMNode> mRightNode;
/** the offset into mNode where the children of mElement are split (for undo).<BR>
* mOffset is the index of the last child in the left node.
* -1 means the left node gets no children.
*/
PRUint32 mOffset;
/** the parent node containing mLeftNode and mRightNode */
nsCOMPtr<nsIDOMNode> mParent;
friend class TransactionFactory;
};
#endif

Просмотреть файл

@ -17,44 +17,42 @@
*/ */
#include "SplitElementTxn.h" #include "SplitElementTxn.h"
#include "editor.h"
#include "nsIDOMNode.h" #include "nsIDOMNode.h"
#include "nsIDOMElement.h" #include "nsIDOMElement.h"
#include "editor.h"
// note that aEditor is not refcounted // note that aEditor is not refcounted
SplitElementTxn::SplitElementTxn(nsEditor *aEditor, SplitElementTxn::SplitElementTxn()
nsIDOMNode *aNode, : EditTxn()
PRInt32 aOffset)
: EditTxn(aEditor)
{ {
mNode = aNode; }
NS_ADDREF(mNode);
nsresult SplitElementTxn::Init(nsIDOMNode *aNode,
PRInt32 aOffset)
{
mExistingRightNode = aNode;
mOffset = aOffset; mOffset = aOffset;
mNewNode = nsnull; return NS_OK;
mParent = nsnull;
} }
SplitElementTxn::~SplitElementTxn() SplitElementTxn::~SplitElementTxn()
{ {
NS_IF_RELEASE(mNode);
NS_IF_RELEASE(mNewNode);
NS_IF_RELEASE(mParent);
} }
nsresult SplitElementTxn::Do(void) nsresult SplitElementTxn::Do(void)
{ {
// create a new node // create a new node
nsresult result = mNode->CloneNode(PR_FALSE, &mNewNode); nsresult result = mExistingRightNode->CloneNode(PR_FALSE, getter_AddRefs(mNewLeftNode));
NS_ASSERTION(((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)), "could not create element."); NS_ASSERTION(((NS_SUCCEEDED(result)) && (mNewLeftNode)), "could not create element.");
if ((NS_SUCCEEDED(result)) && (nsnull!=mNewNode)) if ((NS_SUCCEEDED(result)) && (mNewLeftNode))
{ {
// get the parent node // get the parent node
result = mNode->GetParentNode(&mParent); result = mExistingRightNode->GetParentNode(getter_AddRefs(mParent));
// insert the new node // insert the new node
if ((NS_SUCCEEDED(result)) && (nsnull!=mParent)) if ((NS_SUCCEEDED(result)) && (mParent))
{ {
result = mEditor->SplitNode(mNode, mOffset, mNewNode, mParent); result = nsEditor::SplitNode(mExistingRightNode, mOffset, mNewLeftNode, mParent);
} }
} }
return result; return result;
@ -63,13 +61,13 @@ nsresult SplitElementTxn::Do(void)
nsresult SplitElementTxn::Undo(void) nsresult SplitElementTxn::Undo(void)
{ {
// this assumes Do inserted the new node in front of the prior existing node // this assumes Do inserted the new node in front of the prior existing node
nsresult result = mEditor->JoinNodes(mNode, mNewNode, mParent, PR_FALSE); nsresult result = nsEditor::JoinNodes(mExistingRightNode, mNewLeftNode, mParent, PR_FALSE);
return result; return result;
} }
nsresult SplitElementTxn::Redo(void) nsresult SplitElementTxn::Redo(void)
{ {
nsresult result = mEditor->SplitNode(mNode, mOffset, mNewNode, mParent); nsresult result = nsEditor::SplitNode(mExistingRightNode, mOffset, mNewLeftNode, mParent);
return result; return result;
} }

Просмотреть файл

@ -20,10 +20,13 @@
#define SplitElementTxn_h__ #define SplitElementTxn_h__
#include "EditTxn.h" #include "EditTxn.h"
#include "nsIDOMNode.h"
#include "nsCOMPtr.h"
class nsIDOMNode; #define SPLIT_ELEMENT_TXN_IID \
class nsIDOMElement; {/* 690c6290-ac48-11d2-86d8-000064657374 */ \
class nsIDOMDocument; 0x690c6290, 0xac48, 0x11d2, \
{0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/** /**
* A transaction that splits an element E into two identical nodes, E1 and E2 * A transaction that splits an element E into two identical nodes, E1 and E2
@ -33,10 +36,12 @@ class SplitElementTxn : public EditTxn
{ {
public: public:
SplitElementTxn(nsEditor *aEditor, virtual nsresult Init (nsIDOMNode *aNode,
nsIDOMNode *aNode, PRInt32 aOffset);
PRInt32 aOffset); protected:
SplitElementTxn();
public:
virtual ~SplitElementTxn(); virtual ~SplitElementTxn();
virtual nsresult Do(void); virtual nsresult Do(void);
@ -58,7 +63,7 @@ public:
protected: protected:
/** the element to operate upon */ /** the element to operate upon */
nsIDOMNode *mNode; nsCOMPtr<nsIDOMNode> mExistingRightNode;
/** the offset into mElement where the children of mElement are split.<BR> /** the offset into mElement where the children of mElement are split.<BR>
* mOffset is the index of the last child in the left node. * mOffset is the index of the last child in the left node.
@ -67,8 +72,12 @@ protected:
PRInt32 mOffset; PRInt32 mOffset;
/** the element we create when splitting mElement */ /** the element we create when splitting mElement */
nsIDOMNode *mNewNode; nsCOMPtr<nsIDOMNode> mNewLeftNode;
nsIDOMNode *mParent;
/** the parent shared by mExistingRightNode and mNewLeftNode */
nsCOMPtr<nsIDOMNode> mParent;
friend class TransactionFactory;
}; };

Просмотреть файл

@ -0,0 +1,75 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "TransactionFactory.h"
// transactions this factory knows how to build
#include "InsertTextTxn.h"
#include "DeleteTextTxn.h"
#include "CreateElementTxn.h"
#include "DeleteElementTxn.h"
#include "DeleteRangeTxn.h"
#include "ChangeAttributeTxn.h"
#include "SplitElementTxn.h"
#include "JoinElementTxn.h"
static NS_DEFINE_IID(kInsertTextTxnIID, INSERT_TEXT_TXN_IID);
static NS_DEFINE_IID(kDeleteTextTxnIID, DELETE_TEXT_TXN_IID);
static NS_DEFINE_IID(kCreateElementTxnIID, CREATE_ELEMENT_TXN_IID);
static NS_DEFINE_IID(kDeleteElementTxnIID, DELETE_ELEMENT_TXN_IID);
static NS_DEFINE_IID(kDeleteRangeTxnIID, DELETE_RANGE_TXN_IID);
static NS_DEFINE_IID(kChangeAttributeTxnIID,CHANGE_ATTRIBUTE_TXN_IID);
static NS_DEFINE_IID(kSplitElementTxnIID, SPLIT_ELEMENT_TXN_IID);
static NS_DEFINE_IID(kJoinElementTxnIID, JOIN_ELEMENT_TXN_IID);
TransactionFactory::TransactionFactory()
{
}
TransactionFactory::~TransactionFactory()
{
}
nsresult
TransactionFactory::GetNewTransaction(REFNSIID aTxnType, EditTxn **aResult)
{
nsresult result = NS_OK;
*aResult = nsnull;
if (aTxnType.Equals(kInsertTextTxnIID))
*aResult = new InsertTextTxn();
else if (aTxnType.Equals(kDeleteTextTxnIID))
*aResult = new DeleteTextTxn();
else if (aTxnType.Equals(kCreateElementTxnIID))
*aResult = new CreateElementTxn();
else if (aTxnType.Equals(kDeleteElementTxnIID))
*aResult = new DeleteElementTxn();
else if (aTxnType.Equals(kDeleteRangeTxnIID))
*aResult = new DeleteRangeTxn();
else if (aTxnType.Equals(kChangeAttributeTxnIID))
*aResult = new ChangeAttributeTxn();
else if (aTxnType.Equals(kSplitElementTxnIID))
*aResult = new SplitElementTxn();
else if (aTxnType.Equals(kJoinElementTxnIID))
*aResult = new JoinElementTxn();
if (nsnull==*aResult)
result = NS_ERROR_INVALID_ARG;
return result;
}

Просмотреть файл

@ -0,0 +1,41 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#ifndef TransactionFactory_h__
#define TransactionFactory_h__
#include "nsISupports.h"
class EditTxn;
/**
* This class instantiates and optionally recycles edit transactions
* A recycler would be a separate static object, since this class does not get instantiated
*/
class TransactionFactory
{
protected:
TransactionFactory();
virtual ~TransactionFactory();
public:
static nsresult GetNewTransaction(REFNSIID aTxnType, EditTxn **aResult);
};
#endif