added PlaceholderTxn. This is an aggregate transaction that sits on the undo stack

and merges in subsequent transactions indiscriminately until it's told to stop.
It also gives the last transaction in its child list a chance to merge the
next transaction.
All this is in support of complex transactions that result in text insertion
being able to collapse into a single undoable event.
Also improved tracking of bogus content node used when document is empty.
This commit is contained in:
buster%netscape.com 1999-03-15 00:57:32 +00:00
Родитель d7a2ff8486
Коммит 2b8c73f66e
25 изменённых файлов: 476 добавлений и 230 удалений

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

@ -167,7 +167,8 @@ NS_IMETHODIMP DeleteRangeTxn::Undo(void)
nsCOMPtr<nsIDOMSelection> selection;
result = mEditor->GetSelection(getter_AddRefs(selection));
if (NS_SUCCEEDED(result)) {
result = selection->Collapse(mStartParent, mStartOffset);
selection->Collapse(mStartParent, mStartOffset);
selection->Extend(mEndParent, mEndOffset);
}
}

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

@ -153,26 +153,6 @@ NS_IMETHODIMP EditAggregateTxn::AppendChild(EditTxn *aTxn)
return NS_ERROR_NULL_POINTER;
}
NS_IMETHODIMP EditAggregateTxn::SetName(nsIAtom *aName)
{
mName = do_QueryInterface(aName);
return NS_OK;
}
NS_IMETHODIMP EditAggregateTxn::GetName(nsIAtom **aName)
{
if (aName)
{
if (mName)
{
*aName = mName;
NS_ADDREF(*aName);
return NS_OK;
}
}
return NS_ERROR_NULL_POINTER;
}
NS_IMETHODIMP EditAggregateTxn::GetCount(PRInt32 *aCount)
{
if (!aCount) {
@ -205,6 +185,28 @@ NS_IMETHODIMP EditAggregateTxn::GetTxnAt(PRInt32 aIndex, EditTxn **aTxn)
}
NS_IMETHODIMP EditAggregateTxn::SetName(nsIAtom *aName)
{
mName = do_QueryInterface(aName);
return NS_OK;
}
NS_IMETHODIMP EditAggregateTxn::GetName(nsIAtom **aName)
{
if (aName)
{
if (mName)
{
*aName = mName;
NS_ADDREF(*aName);
return NS_OK;
}
}
return NS_ERROR_NULL_POINTER;
}

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

@ -71,10 +71,10 @@ public:
*/
NS_IMETHOD GetTxnAt(PRInt32 aIndex, EditTxn **aTxn);
/** set the name assigned to this aggregate txn */
/** set the name assigned to this txn */
NS_IMETHOD SetName(nsIAtom *aName);
/** get the name assigned to this aggregate txn */
/** get the name assigned to this txn */
NS_IMETHOD GetName(nsIAtom **aName);
protected:
@ -82,7 +82,6 @@ protected:
//XXX: if this was an nsISupportsArray, it would handle refcounting for us
nsVoidArray * mChildren;
nsCOMPtr<nsIAtom> mName;
};
#endif

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

@ -20,8 +20,7 @@
#define EditTxn_h__
#include "nsITransaction.h"
class nsIDOMNode;
#include "nsCOMPtr.h"
#define EDIT_TXN_IID \
{/* c5ea31b0-ac48-11d2-86d8-000064657374 */ \
@ -31,6 +30,8 @@ class nsIDOMNode;
/**
* base class for all document editing transactions.
* provides default concrete behavior for all nsITransaction methods.
* EditTxns optionally have a name. This name is for internal purposes only,
* it is never seen by the user or by any external entity.
*/
class EditTxn : public nsITransaction
{

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

@ -119,19 +119,23 @@ NS_IMETHODIMP InsertTextTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransacti
otherTxn->GetName(getter_AddRefs(txnName));
if (txnName.get()==gInsertTextTxnName)
{ // yep, it's one of ours. By definition, it must contain only
// a single InsertTextTxn
// another aggregate with a single child,
// or a single InsertTextTxn
nsCOMPtr<EditTxn> childTxn;
otherTxn->GetTxnAt(0, getter_AddRefs(childTxn));
nsCOMPtr<InsertTextTxn> otherInsertTxn;
otherInsertTxn = do_QueryInterface(childTxn, &result);
if (otherInsertTxn)
if (childTxn)
{
if (PR_TRUE==IsSequentialInsert(otherInsertTxn))
nsCOMPtr<InsertTextTxn> otherInsertTxn;
otherInsertTxn = do_QueryInterface(childTxn, &result);
if (otherInsertTxn)
{
nsAutoString otherData;
otherInsertTxn->GetData(otherData);
mStringToInsert += otherData;
*aDidMerge = PR_TRUE;
if (PR_TRUE==IsSequentialInsert(otherInsertTxn))
{
nsAutoString otherData;
otherInsertTxn->GetData(otherData);
mStringToInsert += otherData;
*aDidMerge = PR_TRUE;
}
}
}
}

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

@ -49,6 +49,7 @@ CPPSRCS = \
DeleteTableRowTxn.cpp \
JoinTableCellsTxn.cpp \
InsertTextTxn.cpp \
PlaceholderTxn.cpp \
DeleteTextTxn.cpp \
CreateElementTxn.cpp \
InsertElementTxn.cpp \

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

@ -0,0 +1,74 @@
/* -*- 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 "PlaceholderTxn.h"
#include "nsVoidArray.h"
#ifdef NS_DEBUG
static PRBool gNoisy = PR_TRUE;
#else
static const PRBool gNoisy = PR_FALSE;
#endif
PlaceholderTxn::PlaceholderTxn()
: EditAggregateTxn()
{
mAbsorb=PR_TRUE;
}
PlaceholderTxn::~PlaceholderTxn()
{
}
NS_IMETHODIMP PlaceholderTxn::Do(void)
{
return NS_OK;
}
NS_IMETHODIMP PlaceholderTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction)
{
// set out param default value
if (nsnull!=aDidMerge)
*aDidMerge=PR_FALSE;
nsresult result = NS_OK;
if ((nsnull!=aDidMerge) && (nsnull!=aTransaction))
{
EditTxn *editTxn = (EditTxn*)aTransaction; //XXX: hack, not safe! need nsIEditTransaction!
if (PR_TRUE==mAbsorb)
{ // yep, it's one of ours. Assimilate it.
AppendChild(editTxn);
*aDidMerge = PR_TRUE;
}
else
{ // let our last child txn make the choice
PRInt32 count = mChildren->Count();
if (0<count)
{
EditTxn *lastTxn = (EditTxn*)(mChildren->ElementAt(count-1));
if (lastTxn)
{
lastTxn->Merge(aDidMerge, aTransaction);
}
}
}
}
return result;
}

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

@ -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 AggregatePlaceholderTxn_h__
#define AggregatePlaceholderTxn_h__
#include "EditAggregateTxn.h"
#define PLACEHOLDER_TXN_IID \
{/* {0CE9FB00-D9D1-11d2-86DE-000064657374} */ \
0x0CE9FB00, 0xD9D1, 0x11d2, \
{0x86, 0xde, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/**
* An aggregate transaction that knows how to absorb all subsequent
* transactions with the same name. This transaction does not "Do" anything.
* But it absorbs other transactions via merge, and can undo/redo the
* transactions it has absorbed.
*/
class PlaceholderTxn : public EditAggregateTxn
{
public:
private:
PlaceholderTxn();
public:
virtual ~PlaceholderTxn();
NS_IMETHOD Do(void);
NS_IMETHOD Merge(PRBool *aDidMerge, nsITransaction *aTransaction);
NS_IMETHOD SetAbsorb(PRBool aAbsorb);
friend class TransactionFactory;
protected:
PRBool mAbsorb;
};
inline NS_IMETHODIMP PlaceholderTxn::SetAbsorb(PRBool aAbsorb)
{
mAbsorb = aAbsorb;
return NS_OK;
};
#endif

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

@ -19,6 +19,7 @@
#include "TransactionFactory.h"
// transactions this factory knows how to build
#include "EditAggregateTxn.h"
#include "PlaceholderTxn.h"
#include "InsertTextTxn.h"
#include "DeleteTextTxn.h"
#include "CreateElementTxn.h"
@ -39,6 +40,7 @@
#include "JoinTableCellsTxn.h"
static NS_DEFINE_IID(kEditAggregateTxnIID, EDIT_AGGREGATE_TXN_IID);
static NS_DEFINE_IID(kPlaceholderTxnIID, PLACEHOLDER_TXN_IID);
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);
@ -91,6 +93,8 @@ TransactionFactory::GetNewTransaction(REFNSIID aTxnType, EditTxn **aResult)
*aResult = new JoinElementTxn();
else if (aTxnType.Equals(kEditAggregateTxnIID))
*aResult = new EditAggregateTxn();
else if (aTxnType.Equals(kPlaceholderTxnIID))
*aResult = new PlaceholderTxn();
else
result = NS_ERROR_NO_INTERFACE;

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

@ -33,6 +33,7 @@ CPPSRCS = \
ChangeAttributeTxn.cpp \
InsertTextTxn.cpp \
DeleteTextTxn.cpp \
PlaceholderTxn.cpp \
CreateElementTxn.cpp \
InsertElementTxn.cpp \
DeleteElementTxn.cpp \
@ -67,6 +68,7 @@ CPP_OBJS = \
.\$(OBJDIR)\ChangeAttributeTxn.obj \
.\$(OBJDIR)\InsertTextTxn.obj \
.\$(OBJDIR)\DeleteTextTxn.obj \
.\$(OBJDIR)\PlaceholderTxn.obj \
.\$(OBJDIR)\CreateElementTxn.obj \
.\$(OBJDIR)\InsertElementTxn.obj \
.\$(OBJDIR)\DeleteElementTxn.obj \

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

@ -1184,10 +1184,18 @@ NS_IMETHODIMP nsEditor::DeleteSelectionAndCreateNode(const nsString& aTag, nsIDO
return result;
}
#ifdef NS_DEBUG
PRBool testCollapsed;
nsresult debugResult = selection->IsCollapsed(&testCollapsed);
NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion");
NS_ASSERTION(PR_TRUE==testCollapsed, "selection not reset after deletion");;
nsCOMPtr<nsIDOMNode>testSelectedNode;
PRInt32 testOffset;
nsresult debugResult = selection->GetAnchorNodeAndOffset(getter_AddRefs(testSelectedNode), &testOffset);
// no selection is ok.
// if there is a selection, it must be collapsed
if (testSelectedNode)
{
PRBool testCollapsed;
debugResult = selection->IsCollapsed(&testCollapsed);
NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion");
NS_ASSERTION(PR_TRUE==testCollapsed, "selection not reset after deletion");
}
#endif
}
// split the text node

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

@ -18,6 +18,8 @@
#include "nsTextEditRules.h"
#include "nsTextEditor.h"
#include "PlaceholderTxn.h"
#include "InsertTextTxn.h"
#include "nsCOMPtr.h"
#include "nsIDOMNode.h"
#include "nsIDOMElement.h"
@ -31,6 +33,8 @@
const static char* kMOZEditorBogusNodeAttr="MOZ_EDITOR_BOGUS_NODE";
const static char* kMOZEditorBogusNodeValue="TRUE";
static NS_DEFINE_IID(kPlaceholderTxnIID, PLACEHOLDER_TXN_IID);
static PRBool NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag)
{
nsCOMPtr<nsIDOMElement>element;
@ -115,45 +119,12 @@ nsTextEditRules::WillInsert(nsIDOMSelection *aSelection, PRBool *aCancel)
*aCancel = PR_FALSE;
// check for the magic content node and delete it if it exists
nsCOMPtr<nsIDOMDocument>doc;
mEditor->GetDocument(getter_AddRefs(doc));
nsCOMPtr<nsIDOMNodeList>nodeList;
nsAutoString bodyTag = "body";
nsresult result = doc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList));
if ((NS_SUCCEEDED(result)) && nodeList)
if (mBogusNode)
{
PRUint32 count;
nodeList->GetLength(&count);
NS_ASSERTION(1==count, "there is not exactly 1 body in the document!");
nsCOMPtr<nsIDOMNode>bodyNode;
result = nodeList->Item(0, getter_AddRefs(bodyNode));
if ((NS_SUCCEEDED(result)) && bodyNode)
{ // now we've got the body tag.
// iterate the body tag, looking for editable content
// if the magic node is found, delete it
PRBool foundBogusContent=PR_TRUE;
nsCOMPtr<nsIDOMNode>bodyChild; // a child of the body, for iteration
nsCOMPtr<nsIDOMNode>bogusNode; // this will be the magic node
result = bodyNode->GetFirstChild(getter_AddRefs(bodyChild));
while ((NS_SUCCEEDED(result)) && bodyChild)
{
bogusNode = do_QueryInterface(bodyChild);
if (PR_TRUE==IsEditable(bodyChild))
{
foundBogusContent = PR_FALSE;
break;
}
nsCOMPtr<nsIDOMNode>temp;
bodyChild->GetNextSibling(getter_AddRefs(temp));
bodyChild = do_QueryInterface(temp);
}
if (PR_TRUE==foundBogusContent)
{
mEditor->DeleteNode(bogusNode);
// there is no longer any legit selection, so clear it.
aSelection->ClearSelection();
}
}
mEditor->DeleteNode(mBogusNode);
mBogusNode = do_QueryInterface(nsnull);
// there is no longer any legit selection, so clear it.
aSelection->ClearSelection();
}
return NS_OK;
@ -169,13 +140,22 @@ NS_IMETHODIMP
nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection,
const nsString& aInputString,
PRBool *aCancel,
nsString& aOutputString)
nsString& aOutputString,
PlaceholderTxn **aTxn)
{
if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; }
// initialize out param
*aCancel = PR_FALSE;
// by default, we insert what we're told to insert
aOutputString = aInputString;
if (mBogusNode)
{
nsresult result = TransactionFactory::GetNewTransaction(kPlaceholderTxnIID, (EditTxn **)aTxn);
if (NS_FAILED(result)) { return result; }
if (!*aTxn) { return NS_ERROR_NULL_POINTER; }
(*aTxn)->SetName(InsertTextTxn::gInsertTextTxnName);
mEditor->Do(*aTxn);
}
return WillInsert(aSelection, aCancel);
}
@ -315,31 +295,9 @@ nsTextEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, PRBool *aCance
*aCancel = PR_FALSE;
// if there is only bogus content, cancel the operation
nsCOMPtr<nsIDOMNode>node;
PRInt32 offset;
nsresult result = aSelection->GetAnchorNodeAndOffset(getter_AddRefs(node), &offset);
if ((NS_SUCCEEDED(result)) && node)
{
nsCOMPtr<nsIDOMNode>parent;
parent = do_QueryInterface(node);
while (node)
{ //if we find the bogus node, cancel the operation
nsCOMPtr<nsIDOMElement>element;
element = do_QueryInterface(parent);
if (element)
{
nsAutoString att(kMOZEditorBogusNodeAttr);
nsAutoString val;
nsresult result = element->GetAttribute(att, val);
if (val.Equals(kMOZEditorBogusNodeValue)) {
*aCancel = PR_TRUE;
return NS_OK;
}
}
// walk up the content hierarchy
parent->GetParentNode(getter_AddRefs(node));
parent = do_QueryInterface(node);
}
if (mBogusNode) {
*aCancel = PR_TRUE;
return NS_OK;
}
return NS_OK;
}
@ -389,13 +347,13 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection, nsresult aResul
}
if (PR_TRUE==needsBogusContent)
{
nsCOMPtr<nsIDOMNode>newPNode;
// set mBogusNode to be the newly created <P>
result = mEditor->CreateNode(nsAutoString("P"), bodyNode, 0,
getter_AddRefs(newPNode));
if ((NS_SUCCEEDED(result)) && newPNode)
getter_AddRefs(mBogusNode));
if ((NS_SUCCEEDED(result)) && mBogusNode)
{
nsCOMPtr<nsIDOMNode>newTNode;
result = mEditor->CreateNode(nsIEditor::GetTextNodeTag(), newPNode, 0,
result = mEditor->CreateNode(nsIEditor::GetTextNodeTag(), mBogusNode, 0,
getter_AddRefs(newTNode));
if ((NS_SUCCEEDED(result)) && newTNode)
{
@ -411,7 +369,7 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection, nsresult aResul
}
// make sure we know the PNode is bogus
nsCOMPtr<nsIDOMElement>newPElement;
newPElement = do_QueryInterface(newPNode);
newPElement = do_QueryInterface(mBogusNode);
if (newPElement)
{
nsAutoString att(kMOZEditorBogusNodeAttr);

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

@ -21,9 +21,10 @@
#include "nsIEditor.h"
#include "nsCOMPtr.h"
#include "nsIDOMNode.h"
class nsTextEditor;
class PlaceholderTxn;
/** Object that encapsulates HTML text-specific editing rules.
*
@ -52,7 +53,8 @@ public:
NS_IMETHOD WillInsertText(nsIDOMSelection *aSelection,
const nsString& aInputString,
PRBool *aCancel,
nsString& aOutputString);
nsString& aOutputString,
PlaceholderTxn ** aTxn);
NS_IMETHOD DidInsertText(nsIDOMSelection *aSelection, const nsString& aStringToInsert, nsresult aResult);
NS_IMETHOD WillInsert(nsIDOMSelection *aSelection, PRBool *aCancel);
@ -64,7 +66,7 @@ public:
protected:
nsTextEditor *mEditor; // note that we do not refcount the editor
nsCOMPtr<nsIDOMNode> mBogusNode; // magic node acts as placeholder in empty doc
};
#endif //nsTextEditRules_h__

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

@ -49,6 +49,11 @@
#include "nsIPresShell.h"
#include "nsIStyleContext.h"
// transactions the text editor knows how to build itself
#include "TransactionFactory.h"
#include "PlaceholderTxn.h"
#include "InsertTextTxn.h"
class nsIFrame;
@ -430,22 +435,20 @@ NS_IMETHODIMP nsTextEditor::InsertText(const nsString& aStringToInsert)
nsCOMPtr<nsIDOMSelection> selection;
PRBool cancel= PR_FALSE;
nsresult result = nsEditor::BeginTransaction();
if (NS_FAILED(result)) { return result; }
// pre-process
nsEditor::GetSelection(getter_AddRefs(selection));
nsString stringToInsert;
result = mRules->WillInsertText(selection, aStringToInsert, &cancel, stringToInsert);
nsAutoString stringToInsert;
PlaceholderTxn *placeholderTxn=nsnull;
nsresult result = mRules->WillInsertText(selection, aStringToInsert, &cancel, stringToInsert,
&placeholderTxn);
if ((PR_FALSE==cancel) && (NS_SUCCEEDED(result)))
{
result = nsEditor::InsertText(stringToInsert);
// post-process
result = mRules->DidInsertText(selection, stringToInsert, result);
}
nsresult endTxnResult = nsEditor::EndTransaction(); // don't return this result!
NS_ASSERTION ((NS_SUCCEEDED(endTxnResult)), "bad end transaction result");
if (placeholderTxn)
placeholderTxn->SetAbsorb(PR_FALSE); // this ends the merging of txns into placeholderTxn
// BEGIN HACK!!!
HACKForceRedraw();

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

@ -167,7 +167,8 @@ NS_IMETHODIMP DeleteRangeTxn::Undo(void)
nsCOMPtr<nsIDOMSelection> selection;
result = mEditor->GetSelection(getter_AddRefs(selection));
if (NS_SUCCEEDED(result)) {
result = selection->Collapse(mStartParent, mStartOffset);
selection->Collapse(mStartParent, mStartOffset);
selection->Extend(mEndParent, mEndOffset);
}
}

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

@ -153,26 +153,6 @@ NS_IMETHODIMP EditAggregateTxn::AppendChild(EditTxn *aTxn)
return NS_ERROR_NULL_POINTER;
}
NS_IMETHODIMP EditAggregateTxn::SetName(nsIAtom *aName)
{
mName = do_QueryInterface(aName);
return NS_OK;
}
NS_IMETHODIMP EditAggregateTxn::GetName(nsIAtom **aName)
{
if (aName)
{
if (mName)
{
*aName = mName;
NS_ADDREF(*aName);
return NS_OK;
}
}
return NS_ERROR_NULL_POINTER;
}
NS_IMETHODIMP EditAggregateTxn::GetCount(PRInt32 *aCount)
{
if (!aCount) {
@ -205,6 +185,28 @@ NS_IMETHODIMP EditAggregateTxn::GetTxnAt(PRInt32 aIndex, EditTxn **aTxn)
}
NS_IMETHODIMP EditAggregateTxn::SetName(nsIAtom *aName)
{
mName = do_QueryInterface(aName);
return NS_OK;
}
NS_IMETHODIMP EditAggregateTxn::GetName(nsIAtom **aName)
{
if (aName)
{
if (mName)
{
*aName = mName;
NS_ADDREF(*aName);
return NS_OK;
}
}
return NS_ERROR_NULL_POINTER;
}

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

@ -71,10 +71,10 @@ public:
*/
NS_IMETHOD GetTxnAt(PRInt32 aIndex, EditTxn **aTxn);
/** set the name assigned to this aggregate txn */
/** set the name assigned to this txn */
NS_IMETHOD SetName(nsIAtom *aName);
/** get the name assigned to this aggregate txn */
/** get the name assigned to this txn */
NS_IMETHOD GetName(nsIAtom **aName);
protected:
@ -82,7 +82,6 @@ protected:
//XXX: if this was an nsISupportsArray, it would handle refcounting for us
nsVoidArray * mChildren;
nsCOMPtr<nsIAtom> mName;
};
#endif

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

@ -20,8 +20,7 @@
#define EditTxn_h__
#include "nsITransaction.h"
class nsIDOMNode;
#include "nsCOMPtr.h"
#define EDIT_TXN_IID \
{/* c5ea31b0-ac48-11d2-86d8-000064657374 */ \
@ -31,6 +30,8 @@ class nsIDOMNode;
/**
* base class for all document editing transactions.
* provides default concrete behavior for all nsITransaction methods.
* EditTxns optionally have a name. This name is for internal purposes only,
* it is never seen by the user or by any external entity.
*/
class EditTxn : public nsITransaction
{

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

@ -119,19 +119,23 @@ NS_IMETHODIMP InsertTextTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransacti
otherTxn->GetName(getter_AddRefs(txnName));
if (txnName.get()==gInsertTextTxnName)
{ // yep, it's one of ours. By definition, it must contain only
// a single InsertTextTxn
// another aggregate with a single child,
// or a single InsertTextTxn
nsCOMPtr<EditTxn> childTxn;
otherTxn->GetTxnAt(0, getter_AddRefs(childTxn));
nsCOMPtr<InsertTextTxn> otherInsertTxn;
otherInsertTxn = do_QueryInterface(childTxn, &result);
if (otherInsertTxn)
if (childTxn)
{
if (PR_TRUE==IsSequentialInsert(otherInsertTxn))
nsCOMPtr<InsertTextTxn> otherInsertTxn;
otherInsertTxn = do_QueryInterface(childTxn, &result);
if (otherInsertTxn)
{
nsAutoString otherData;
otherInsertTxn->GetData(otherData);
mStringToInsert += otherData;
*aDidMerge = PR_TRUE;
if (PR_TRUE==IsSequentialInsert(otherInsertTxn))
{
nsAutoString otherData;
otherInsertTxn->GetData(otherData);
mStringToInsert += otherData;
*aDidMerge = PR_TRUE;
}
}
}
}

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

@ -0,0 +1,74 @@
/* -*- 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 "PlaceholderTxn.h"
#include "nsVoidArray.h"
#ifdef NS_DEBUG
static PRBool gNoisy = PR_TRUE;
#else
static const PRBool gNoisy = PR_FALSE;
#endif
PlaceholderTxn::PlaceholderTxn()
: EditAggregateTxn()
{
mAbsorb=PR_TRUE;
}
PlaceholderTxn::~PlaceholderTxn()
{
}
NS_IMETHODIMP PlaceholderTxn::Do(void)
{
return NS_OK;
}
NS_IMETHODIMP PlaceholderTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction)
{
// set out param default value
if (nsnull!=aDidMerge)
*aDidMerge=PR_FALSE;
nsresult result = NS_OK;
if ((nsnull!=aDidMerge) && (nsnull!=aTransaction))
{
EditTxn *editTxn = (EditTxn*)aTransaction; //XXX: hack, not safe! need nsIEditTransaction!
if (PR_TRUE==mAbsorb)
{ // yep, it's one of ours. Assimilate it.
AppendChild(editTxn);
*aDidMerge = PR_TRUE;
}
else
{ // let our last child txn make the choice
PRInt32 count = mChildren->Count();
if (0<count)
{
EditTxn *lastTxn = (EditTxn*)(mChildren->ElementAt(count-1));
if (lastTxn)
{
lastTxn->Merge(aDidMerge, aTransaction);
}
}
}
}
return result;
}

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

@ -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 AggregatePlaceholderTxn_h__
#define AggregatePlaceholderTxn_h__
#include "EditAggregateTxn.h"
#define PLACEHOLDER_TXN_IID \
{/* {0CE9FB00-D9D1-11d2-86DE-000064657374} */ \
0x0CE9FB00, 0xD9D1, 0x11d2, \
{0x86, 0xde, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
/**
* An aggregate transaction that knows how to absorb all subsequent
* transactions with the same name. This transaction does not "Do" anything.
* But it absorbs other transactions via merge, and can undo/redo the
* transactions it has absorbed.
*/
class PlaceholderTxn : public EditAggregateTxn
{
public:
private:
PlaceholderTxn();
public:
virtual ~PlaceholderTxn();
NS_IMETHOD Do(void);
NS_IMETHOD Merge(PRBool *aDidMerge, nsITransaction *aTransaction);
NS_IMETHOD SetAbsorb(PRBool aAbsorb);
friend class TransactionFactory;
protected:
PRBool mAbsorb;
};
inline NS_IMETHODIMP PlaceholderTxn::SetAbsorb(PRBool aAbsorb)
{
mAbsorb = aAbsorb;
return NS_OK;
};
#endif

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

@ -19,6 +19,7 @@
#include "TransactionFactory.h"
// transactions this factory knows how to build
#include "EditAggregateTxn.h"
#include "PlaceholderTxn.h"
#include "InsertTextTxn.h"
#include "DeleteTextTxn.h"
#include "CreateElementTxn.h"
@ -39,6 +40,7 @@
#include "JoinTableCellsTxn.h"
static NS_DEFINE_IID(kEditAggregateTxnIID, EDIT_AGGREGATE_TXN_IID);
static NS_DEFINE_IID(kPlaceholderTxnIID, PLACEHOLDER_TXN_IID);
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);
@ -91,6 +93,8 @@ TransactionFactory::GetNewTransaction(REFNSIID aTxnType, EditTxn **aResult)
*aResult = new JoinElementTxn();
else if (aTxnType.Equals(kEditAggregateTxnIID))
*aResult = new EditAggregateTxn();
else if (aTxnType.Equals(kPlaceholderTxnIID))
*aResult = new PlaceholderTxn();
else
result = NS_ERROR_NO_INTERFACE;

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

@ -1184,10 +1184,18 @@ NS_IMETHODIMP nsEditor::DeleteSelectionAndCreateNode(const nsString& aTag, nsIDO
return result;
}
#ifdef NS_DEBUG
PRBool testCollapsed;
nsresult debugResult = selection->IsCollapsed(&testCollapsed);
NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion");
NS_ASSERTION(PR_TRUE==testCollapsed, "selection not reset after deletion");;
nsCOMPtr<nsIDOMNode>testSelectedNode;
PRInt32 testOffset;
nsresult debugResult = selection->GetAnchorNodeAndOffset(getter_AddRefs(testSelectedNode), &testOffset);
// no selection is ok.
// if there is a selection, it must be collapsed
if (testSelectedNode)
{
PRBool testCollapsed;
debugResult = selection->IsCollapsed(&testCollapsed);
NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion");
NS_ASSERTION(PR_TRUE==testCollapsed, "selection not reset after deletion");
}
#endif
}
// split the text node

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

@ -18,6 +18,8 @@
#include "nsTextEditRules.h"
#include "nsTextEditor.h"
#include "PlaceholderTxn.h"
#include "InsertTextTxn.h"
#include "nsCOMPtr.h"
#include "nsIDOMNode.h"
#include "nsIDOMElement.h"
@ -31,6 +33,8 @@
const static char* kMOZEditorBogusNodeAttr="MOZ_EDITOR_BOGUS_NODE";
const static char* kMOZEditorBogusNodeValue="TRUE";
static NS_DEFINE_IID(kPlaceholderTxnIID, PLACEHOLDER_TXN_IID);
static PRBool NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag)
{
nsCOMPtr<nsIDOMElement>element;
@ -115,45 +119,12 @@ nsTextEditRules::WillInsert(nsIDOMSelection *aSelection, PRBool *aCancel)
*aCancel = PR_FALSE;
// check for the magic content node and delete it if it exists
nsCOMPtr<nsIDOMDocument>doc;
mEditor->GetDocument(getter_AddRefs(doc));
nsCOMPtr<nsIDOMNodeList>nodeList;
nsAutoString bodyTag = "body";
nsresult result = doc->GetElementsByTagName(bodyTag, getter_AddRefs(nodeList));
if ((NS_SUCCEEDED(result)) && nodeList)
if (mBogusNode)
{
PRUint32 count;
nodeList->GetLength(&count);
NS_ASSERTION(1==count, "there is not exactly 1 body in the document!");
nsCOMPtr<nsIDOMNode>bodyNode;
result = nodeList->Item(0, getter_AddRefs(bodyNode));
if ((NS_SUCCEEDED(result)) && bodyNode)
{ // now we've got the body tag.
// iterate the body tag, looking for editable content
// if the magic node is found, delete it
PRBool foundBogusContent=PR_TRUE;
nsCOMPtr<nsIDOMNode>bodyChild; // a child of the body, for iteration
nsCOMPtr<nsIDOMNode>bogusNode; // this will be the magic node
result = bodyNode->GetFirstChild(getter_AddRefs(bodyChild));
while ((NS_SUCCEEDED(result)) && bodyChild)
{
bogusNode = do_QueryInterface(bodyChild);
if (PR_TRUE==IsEditable(bodyChild))
{
foundBogusContent = PR_FALSE;
break;
}
nsCOMPtr<nsIDOMNode>temp;
bodyChild->GetNextSibling(getter_AddRefs(temp));
bodyChild = do_QueryInterface(temp);
}
if (PR_TRUE==foundBogusContent)
{
mEditor->DeleteNode(bogusNode);
// there is no longer any legit selection, so clear it.
aSelection->ClearSelection();
}
}
mEditor->DeleteNode(mBogusNode);
mBogusNode = do_QueryInterface(nsnull);
// there is no longer any legit selection, so clear it.
aSelection->ClearSelection();
}
return NS_OK;
@ -169,13 +140,22 @@ NS_IMETHODIMP
nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection,
const nsString& aInputString,
PRBool *aCancel,
nsString& aOutputString)
nsString& aOutputString,
PlaceholderTxn **aTxn)
{
if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; }
// initialize out param
*aCancel = PR_FALSE;
// by default, we insert what we're told to insert
aOutputString = aInputString;
if (mBogusNode)
{
nsresult result = TransactionFactory::GetNewTransaction(kPlaceholderTxnIID, (EditTxn **)aTxn);
if (NS_FAILED(result)) { return result; }
if (!*aTxn) { return NS_ERROR_NULL_POINTER; }
(*aTxn)->SetName(InsertTextTxn::gInsertTextTxnName);
mEditor->Do(*aTxn);
}
return WillInsert(aSelection, aCancel);
}
@ -315,31 +295,9 @@ nsTextEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, PRBool *aCance
*aCancel = PR_FALSE;
// if there is only bogus content, cancel the operation
nsCOMPtr<nsIDOMNode>node;
PRInt32 offset;
nsresult result = aSelection->GetAnchorNodeAndOffset(getter_AddRefs(node), &offset);
if ((NS_SUCCEEDED(result)) && node)
{
nsCOMPtr<nsIDOMNode>parent;
parent = do_QueryInterface(node);
while (node)
{ //if we find the bogus node, cancel the operation
nsCOMPtr<nsIDOMElement>element;
element = do_QueryInterface(parent);
if (element)
{
nsAutoString att(kMOZEditorBogusNodeAttr);
nsAutoString val;
nsresult result = element->GetAttribute(att, val);
if (val.Equals(kMOZEditorBogusNodeValue)) {
*aCancel = PR_TRUE;
return NS_OK;
}
}
// walk up the content hierarchy
parent->GetParentNode(getter_AddRefs(node));
parent = do_QueryInterface(node);
}
if (mBogusNode) {
*aCancel = PR_TRUE;
return NS_OK;
}
return NS_OK;
}
@ -389,13 +347,13 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection, nsresult aResul
}
if (PR_TRUE==needsBogusContent)
{
nsCOMPtr<nsIDOMNode>newPNode;
// set mBogusNode to be the newly created <P>
result = mEditor->CreateNode(nsAutoString("P"), bodyNode, 0,
getter_AddRefs(newPNode));
if ((NS_SUCCEEDED(result)) && newPNode)
getter_AddRefs(mBogusNode));
if ((NS_SUCCEEDED(result)) && mBogusNode)
{
nsCOMPtr<nsIDOMNode>newTNode;
result = mEditor->CreateNode(nsIEditor::GetTextNodeTag(), newPNode, 0,
result = mEditor->CreateNode(nsIEditor::GetTextNodeTag(), mBogusNode, 0,
getter_AddRefs(newTNode));
if ((NS_SUCCEEDED(result)) && newTNode)
{
@ -411,7 +369,7 @@ nsTextEditRules::DidDeleteSelection(nsIDOMSelection *aSelection, nsresult aResul
}
// make sure we know the PNode is bogus
nsCOMPtr<nsIDOMElement>newPElement;
newPElement = do_QueryInterface(newPNode);
newPElement = do_QueryInterface(mBogusNode);
if (newPElement)
{
nsAutoString att(kMOZEditorBogusNodeAttr);

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

@ -21,9 +21,10 @@
#include "nsIEditor.h"
#include "nsCOMPtr.h"
#include "nsIDOMNode.h"
class nsTextEditor;
class PlaceholderTxn;
/** Object that encapsulates HTML text-specific editing rules.
*
@ -52,7 +53,8 @@ public:
NS_IMETHOD WillInsertText(nsIDOMSelection *aSelection,
const nsString& aInputString,
PRBool *aCancel,
nsString& aOutputString);
nsString& aOutputString,
PlaceholderTxn ** aTxn);
NS_IMETHOD DidInsertText(nsIDOMSelection *aSelection, const nsString& aStringToInsert, nsresult aResult);
NS_IMETHOD WillInsert(nsIDOMSelection *aSelection, PRBool *aCancel);
@ -64,7 +66,7 @@ public:
protected:
nsTextEditor *mEditor; // note that we do not refcount the editor
nsCOMPtr<nsIDOMNode> mBogusNode; // magic node acts as placeholder in empty doc
};
#endif //nsTextEditRules_h__