revised UNDO; introduced first cut at the "mozdiv" typing rules

This commit is contained in:
jfrancis%netscape.com 1999-09-29 20:08:15 +00:00
Родитель 1c960f2fad
Коммит 75ecd98021
23 изменённых файлов: 2130 добавлений и 864 удалений

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

@ -18,6 +18,8 @@
#include "PlaceholderTxn.h"
#include "nsVoidArray.h"
#include "nsHTMLEditor.h"
#include "nsIPresShell.h"
#if defined(NS_DEBUG) && defined(DEBUG_buster)
static PRBool gNoisy = PR_TRUE;
@ -26,10 +28,11 @@ static const PRBool gNoisy = PR_FALSE;
#endif
PlaceholderTxn::PlaceholderTxn()
: EditAggregateTxn()
PlaceholderTxn::PlaceholderTxn() : EditAggregateTxn(),
mPresShellWeak(nsnull),
mAbsorb(PR_TRUE),
mForwarding(nsnull)
{
mAbsorb=PR_TRUE;
SetTransactionDescriptionID( kTransactionID );
/* log description initialized in parent constructor */
}
@ -39,39 +42,153 @@ PlaceholderTxn::~PlaceholderTxn()
{
}
NS_IMPL_ADDREF_INHERITED(PlaceholderTxn, EditAggregateTxn)
NS_IMPL_RELEASE_INHERITED(PlaceholderTxn, EditAggregateTxn)
//NS_IMPL_QUERY_INTERFACE_INHERITED(Class, Super, AdditionalInterface)
NS_IMETHODIMP PlaceholderTxn::QueryInterface(REFNSIID aIID, void** aInstancePtr)
{
if (!aInstancePtr) return NS_ERROR_NULL_POINTER;
if (aIID.Equals(nsIAbsorbingTransaction::GetIID())) {
*aInstancePtr = (nsISupports*)(nsIAbsorbingTransaction*)(this);
NS_ADDREF_THIS();
return NS_OK;
}
if (aIID.Equals(nsCOMTypeInfo<nsISupportsWeakReference>::GetIID())) {
*aInstancePtr = (nsISupports*)(nsISupportsWeakReference*) this;
NS_ADDREF_THIS();
return NS_OK;
}
return EditAggregateTxn::QueryInterface(aIID, aInstancePtr);
}
NS_IMETHODIMP PlaceholderTxn::Init(nsWeakPtr aPresShellWeak, nsIAtom *aName,
nsIDOMNode *aStartNode, PRInt32 aStartOffset)
{
NS_ASSERTION(aPresShellWeak, "bad args");
if (!aPresShellWeak) return NS_ERROR_NULL_POINTER;
mPresShellWeak = aPresShellWeak;
mName = aName;
mStartNode = do_QueryInterface(aStartNode);
mStartOffset = aStartOffset;
return NS_OK;
}
NS_IMETHODIMP PlaceholderTxn::Do(void)
{
if (gNoisy) { printf("PlaceholderTxn Do\n"); }
return NS_OK;
}
NS_IMETHODIMP PlaceholderTxn::Undo(void)
{
// using this to debug
return EditAggregateTxn::Undo();
}
NS_IMETHODIMP PlaceholderTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction)
{
if (!aDidMerge || !aTransaction) return NS_ERROR_NULL_POINTER;
// set out param default value
if (nsnull!=aDidMerge)
*aDidMerge=PR_FALSE;
nsresult result = NS_OK;
if ((nsnull!=aDidMerge) && (nsnull!=aTransaction))
*aDidMerge=PR_FALSE;
nsresult res = NS_OK;
if (mForwarding)
{
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;
if (gNoisy) { printf("Placeholder txn assimilated %p\n", aTransaction); }
}
else
{ // let our last child txn make the choice
PRInt32 count = mChildren->Count();
if (0<count)
NS_NOTREACHED("tried to merge into a placeholder that was in forwarding mode!");
return NS_ERROR_FAILURE;
}
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;
if (gNoisy) { printf("Placeholder txn assimilated %p\n", aTransaction); }
}
else
{ // merge typing transactions if the selection matches
if (mName.get() == nsHTMLEditor::gTypingTxnName)
{
nsCOMPtr<nsIAbsorbingTransaction> plcTxn;// = do_QueryInterface(editTxn);
// cant do_QueryInterface() above due to our broken transaction interfaces.
// instead have to brute it below. ugh.
editTxn->QueryInterface(nsIAbsorbingTransaction::GetIID(), getter_AddRefs(plcTxn));
if (plcTxn)
{
EditTxn *lastTxn = (EditTxn*)(mChildren->ElementAt(count-1));
if (lastTxn)
nsIAtom *atom;
plcTxn->GetTxnName(&atom);
if (atom && (atom == nsHTMLEditor::gTypingTxnName))
{
lastTxn->Merge(aDidMerge, aTransaction);
nsCOMPtr<nsIDOMNode> otherTxnStartNode;
PRInt32 otherTxnStartOffset;
res = plcTxn->GetStartNodeAndOffset(&otherTxnStartNode, &otherTxnStartOffset);
if (NS_FAILED(res)) return res;
if ((otherTxnStartNode == mEndNode) && (otherTxnStartOffset == mEndOffset))
{
mAbsorb = PR_TRUE; // we need to start absorbing again
plcTxn->ForwardEndBatchTo(this);
AppendChild(editTxn);
*aDidMerge = PR_TRUE;
}
}
}
}
}
return result;
return res;
}
NS_IMETHODIMP PlaceholderTxn::GetTxnName(nsIAtom **aName)
{
return GetName(aName);
}
NS_IMETHODIMP PlaceholderTxn::GetStartNodeAndOffset(nsCOMPtr<nsIDOMNode> *aTxnStartNode, PRInt32 *aTxnStartOffset)
{
if (!aTxnStartNode || !aTxnStartOffset) return NS_ERROR_NULL_POINTER;
*aTxnStartNode = mStartNode;
*aTxnStartOffset = mStartOffset;
return NS_OK;
}
NS_IMETHODIMP PlaceholderTxn::EndPlaceHolderBatch()
{
mAbsorb = PR_FALSE;
if (mForwarding)
{
nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryReferent(mForwarding);
if (plcTxn) plcTxn->EndPlaceHolderBatch();
}
// if we are a typing transaction, remember our selection state
if (mName.get() == nsHTMLEditor::gTypingTxnName)
{
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
if (!ps) return NS_ERROR_NOT_INITIALIZED;
nsCOMPtr<nsIDOMSelection> selection;
nsresult res = ps->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
if (!selection) return NS_ERROR_NULL_POINTER;
res = nsEditor::GetStartNodeAndOffset(selection, &mEndNode, &mEndOffset);
if (NS_FAILED(res)) return res;
}
return NS_OK;
};
NS_IMETHODIMP PlaceholderTxn::ForwardEndBatchTo(nsIAbsorbingTransaction *aForwardingAddress)
{
mForwarding = getter_AddRefs( NS_GetWeakReference(aForwardingAddress) );
return NS_OK;
}

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

@ -20,24 +20,36 @@
#define AggregatePlaceholderTxn_h__
#include "EditAggregateTxn.h"
#include "nsIAbsorbingTransaction.h"
#include "nsIDOMNode.h"
#include "nsCOMPtr.h"
#include "nsWeakPtr.h"
#include "nsWeakReference.h"
#define PLACEHOLDER_TXN_CID \
{/* {0CE9FB00-D9D1-11d2-86DE-000064657374} */ \
0x0CE9FB00, 0xD9D1, 0x11d2, \
{0x86, 0xde, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
class nsHTMLEditor;
/**
* 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
class PlaceholderTxn : public EditAggregateTxn,
public nsIAbsorbingTransaction,
public nsSupportsWeakReference
{
public:
static const nsIID& GetCID() { static nsIID iid = PLACEHOLDER_TXN_CID; return iid; }
NS_DECL_ISUPPORTS_INHERITED
private:
PlaceholderTxn();
@ -45,11 +57,25 @@ public:
virtual ~PlaceholderTxn();
// ------------ EditAggregateTxn -----------------------
NS_IMETHOD Do(void);
NS_IMETHOD Undo(void);
NS_IMETHOD Merge(PRBool *aDidMerge, nsITransaction *aTransaction);
NS_IMETHOD SetAbsorb(PRBool aAbsorb);
// ------------ nsIAbsorbingTransaction -----------------------
NS_IMETHOD Init(nsWeakPtr aPresShellWeak, nsIAtom *aName, nsIDOMNode *aStartNode, PRInt32 aStartOffset);
NS_IMETHOD GetTxnName(nsIAtom **aName);
NS_IMETHOD GetStartNodeAndOffset(nsCOMPtr<nsIDOMNode> *aTxnStartNode, PRInt32 *aTxnStartOffset);
NS_IMETHOD EndPlaceHolderBatch();
NS_IMETHOD ForwardEndBatchTo(nsIAbsorbingTransaction *aForwardingAddress);
friend class TransactionFactory;
@ -57,14 +83,12 @@ public:
protected:
PRBool mAbsorb;
};
inline NS_IMETHODIMP PlaceholderTxn::SetAbsorb(PRBool aAbsorb)
{
mAbsorb = aAbsorb;
return NS_OK;
/** the presentation shell, which we'll need to get the selection */
nsWeakPtr mPresShellWeak; // weak reference to the nsIPresShell
PRBool mAbsorb;
nsCOMPtr<nsIDOMNode> mStartNode, mEndNode; // selection nodes at beginning and end of operation
PRInt32 mStartOffset, mEndOffset; // selection offsets at beginning and end of operation
nsWeakPtr mForwarding;
};

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

@ -36,6 +36,7 @@
#include "nsIServiceManager.h"
#include "nsTransactionManagerCID.h"
#include "nsITransactionManager.h"
#include "nsIAbsorbingTransaction.h"
#include "nsIPresShell.h"
#include "nsIPresContext.h"
#include "nsIViewManager.h"
@ -54,6 +55,7 @@
#include "nsIDocumentObserver.h"
#include "nsIDocumentStateListener.h"
#include "nsIStringStream.h"
#include "nsITextContent.h"
#ifdef NECKO
#include "nsNeckoUtil.h"
@ -68,6 +70,7 @@
// transactions the editor knows how to build
#include "TransactionFactory.h"
#include "EditAggregateTxn.h"
#include "PlaceholderTxn.h"
#include "ChangeAttributeTxn.h"
#include "CreateElementTxn.h"
#include "InsertElementTxn.h"
@ -85,6 +88,7 @@
#include "nsEditorCID.h"
#include "nsEditor.h"
#include "nsEditorUtils.h"
#ifdef HACK_FORCE_REDRAW
@ -143,6 +147,12 @@ nsEditor::nsEditor()
, mActionListeners(nsnull)
, mDocDirtyState(-1)
, mDocWeak(nsnull)
, mPlaceHolderTxn(nsnull)
, mPlaceHolderName(nsnull)
, mPlaceHolderBatch(0)
, mTxnStartNode(nsnull)
, mTxnStartOffset(0)
{
//initialize member variables here
NS_INIT_REFCNT();
@ -332,6 +342,31 @@ nsEditor::Do(nsITransaction *aTxn)
{
if (gNoisy) { printf("Editor::Do ----------\n"); }
nsresult result = NS_OK;
if (mPlaceHolderBatch && !mPlaceHolderTxn)
{
// it's pretty darn amazing how many different types of pointers
// this transcation goes through here. I bet this is a record.
EditTxn *editTxn;
nsCOMPtr<nsIAbsorbingTransaction> plcTxn;
result = TransactionFactory::GetNewTransaction(PlaceholderTxn::GetCID(), &editTxn);
if (NS_FAILED(result)) { return result; }
if (!editTxn) { return NS_ERROR_NULL_POINTER; }
editTxn->QueryInterface(nsIAbsorbingTransaction::GetIID(), getter_AddRefs(plcTxn));
// have to use line above instead of line below due to our broken
// interface model for transactions.
// plcTxn = do_QueryInterface(editTxn);
// save off weak reference to placeholder txn
mPlaceHolderTxn = getter_AddRefs( NS_GetWeakReference(plcTxn) );
plcTxn->Init(mPresShellWeak, mPlaceHolderName, mTxnStartNode, mTxnStartOffset);
// we will recurse, but will not hit this case in the nested call
nsCOMPtr<nsITransaction> theTxn = do_QueryInterface(plcTxn);
nsITransaction* txn = theTxn;
// we want to escape from this routine with a positive refcount
txn->AddRef();
Do(txn);
}
nsCOMPtr<nsIDOMSelection>selection;
nsresult selectionResult = GetSelection(getter_AddRefs(selection));
if (NS_SUCCEEDED(selectionResult) && selection) {
@ -504,6 +539,72 @@ nsEditor::EndTransaction()
return NS_OK;
}
// These two routines are similar to the above, but do not use
// the transaction managers batching feature. Instead we use
// a placeholder transaction to wrap up any further transaction
// while the batch is open. The advantage of this is that
// placeholder transactions can later merge, if needed. Merging
// is unavailable between transaction manager batches.
NS_IMETHODIMP
nsEditor::BeginPlaceHolderTransaction(nsIAtom *aName)
{
NS_PRECONDITION(mPlaceHolderBatch >= 0, "negative placeholder batch count!");
if (!mPlaceHolderBatch)
{
// time to turn on the batch
BeginUpdateViewBatch();
mPlaceHolderTxn = nsnull;
mPlaceHolderName = aName;
nsCOMPtr<nsIDOMSelection> selection;
nsresult res = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
PRBool collapsed;
res = selection->GetIsCollapsed(&collapsed);
if (NS_FAILED(res)) return res;
if (collapsed) // we cant merge with previous typing if selection not collapsed
{
// need to remember colapsed selection point to Init the placeholder with later.
// this is because we dont actually make the placeholder until we need it, and we
// might have moved the selection as part of the typing processing by then.
res = GetStartNodeAndOffset(selection, &mTxnStartNode, &mTxnStartOffset);
if (NS_FAILED(res)) return res;
}
}
mPlaceHolderBatch++;
return NS_OK;
}
NS_IMETHODIMP
nsEditor::EndPlaceHolderTransaction()
{
NS_PRECONDITION(mPlaceHolderBatch > 0, "zero or negative placeholder batch count when ending batch!");
if (mPlaceHolderBatch == 1)
{
// time to turn off the batch
EndUpdateViewBatch();
mTxnStartNode = nsnull;
mTxnStartOffset = 0;
if (mPlaceHolderTxn) // we might have never made a placeholder if no action took place
{
nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryReferent(mPlaceHolderTxn);
if (plcTxn)
{
plcTxn->EndPlaceHolderBatch();
}
else // but if we did make one it should be around
{
NS_NOTREACHED("should this ever happen?");
}
}
}
mPlaceHolderBatch--;
return NS_OK;
}
// XXX: the rule system should tell us which node to select all on (ie, the root, or the body)
NS_IMETHODIMP nsEditor::SelectAll()
{
@ -1489,8 +1590,9 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert)
result = CreateTxnForInsertText(aStringToInsert, nsnull, &txn); // insert at the current selection
if ((NS_SUCCEEDED(result)) && txn) {
BeginUpdateViewBatch();
aggTxn->AppendChild(txn);
// aggTxn->AppendChild(txn);
result = Do(aggTxn);
result = Do(txn);
EndUpdateViewBatch();
}
else if (NS_ERROR_EDITOR_NO_SELECTION==result) {
@ -1769,7 +1871,7 @@ NS_IMETHODIMP nsEditor::CreateTxnForInsertText(const nsString & aStringToInsert,
// This may be OK, now that I fixed InsertText so it inserts
// at the offset of the selection anchor (i.e., the caret offset)
// instead of at offset+1
PRBool collapsed
PRBool collapsed;
result = selection->GetIsCollapsed(&collapsed);
if (NS_SUCCEEDED(result) && collapsed)
{
@ -2559,8 +2661,8 @@ nsEditor::GetPriorNode(nsIDOMNode *aParentNode,
nsresult result = NS_OK;
if (!aParentNode || !aResultNode) { return NS_ERROR_NULL_POINTER; }
// if we are at beginning of node than just look before it
if (!aOffset)
// if we are at beginning of node, or it is a textnode, then just look before it
if (!aOffset || IsTextNode(aParentNode))
{
return GetPriorNode(aParentNode, aEditableNode, aResultNode);
}
@ -2601,6 +2703,14 @@ nsEditor::GetNextNode(nsIDOMNode *aParentNode,
nsresult result = NS_OK;
if (!aParentNode || !aResultNode) { return NS_ERROR_NULL_POINTER; }
// if aParentNode is a text node, use it's location instead
if (IsTextNode(aParentNode))
{
nsCOMPtr<nsIDOMNode> parent;
nsEditor::GetNodeLocation(aParentNode, &parent, &aOffset);
aParentNode = parent;
aOffset++; // _after_ the text node
}
// look at the child at 'aOffset'
nsCOMPtr<nsIDOMNode> child = GetChildAt(aParentNode, aOffset);
if (child)
@ -2828,6 +2938,18 @@ nsEditor::CanContainTag(nsIDOMNode* aParent, const nsString &aTag)
return mDTD->CanContain(parentTagEnum, childTagEnum);
}
PRBool
nsEditor::IsContainer(nsIDOMNode *aNode)
{
if (!aNode) return PR_FALSE;
nsAutoString stringTag;
PRInt32 tagEnum;
nsresult res = aNode->GetNodeName(stringTag);
if (NS_FAILED(res)) return PR_FALSE;
res = mDTD->StringTagToIntTag(stringTag,&tagEnum);
if (NS_FAILED(res)) return PR_FALSE;
return mDTD->IsContainer(tagEnum);
}
PRBool
nsEditor::IsEditable(nsIDOMNode *aNode)
@ -2885,13 +3007,27 @@ nsEditor::IsEditable(nsIDOMNode *aNode)
if (NS_FAILED(result) || !resultFrame) { // if it has no frame, it is not editable
return PR_FALSE;
}
else { // it has a frame, so it is editable
else {
// it has a frame, so it might editable
// but not if it's a formatting whitespace node
if (IsEmptyTextContent(content)) return PR_FALSE;
return PR_TRUE;
}
}
return PR_FALSE; // it's not a content object (???) so it's not editable
}
PRBool
nsEditor::IsEmptyTextContent(nsIContent* aContent)
{
PRBool result = PR_FALSE;
nsCOMPtr<nsITextContent> tc(do_QueryInterface(aContent));
if (tc) {
tc->IsOnlyWhitespace(&result);
}
return result;
}
nsresult
nsEditor::CountEditableChildren(nsIDOMNode *aNode, PRUint32 &outCount)
{
@ -3108,7 +3244,7 @@ nsEditor::SetInputMethodText(const nsString& aStringToInsert, nsIPrivateTextRang
}
else if (NS_ERROR_EDITOR_NO_TEXTNODE==result)
{
BeginTransaction();
nsAutoEditBatch batchIt (this);
nsCOMPtr<nsIDOMSelection> selection;
result = GetSelection(getter_AddRefs(selection));
if ((NS_SUCCEEDED(result)) && selection)
@ -3138,8 +3274,6 @@ nsEditor::SetInputMethodText(const nsString& aStringToInsert, nsIPrivateTextRang
}
}
}
EndTransaction();
}
return result;

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

@ -121,6 +121,9 @@ public:
NS_IMETHOD BeginTransaction();
NS_IMETHOD EndTransaction();
NS_IMETHOD BeginPlaceHolderTransaction(nsIAtom *aName);
NS_IMETHOD EndPlaceHolderTransaction();
// pure virtual, because the definition of 'empty' depends on the doc type
NS_IMETHOD GetDocumentIsEmpty(PRBool *aDocumentIsEmpty)=0;
@ -553,11 +556,17 @@ public:
static PRBool NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag);
/** returns PR_TRUE if aParent can contain a child of type aTag */
PRBool CanContainTag(nsIDOMNode* aParent, const nsString &aTag);
PRBool CanContainTag(nsIDOMNode* aParent, const nsString &aTag);
/** returns PR_TRUE if aNode is a container */
PRBool IsContainer(nsIDOMNode *aNode);
/** returns PR_TRUE if aNode is an editable node */
PRBool IsEditable(nsIDOMNode *aNode);
/** returns PR_TRUE if content is an merely formatting whitespacce */
PRBool IsEmptyTextContent(nsIContent* aContent);
/** counts number of editable child nodes */
nsresult CountEditableChildren(nsIDOMNode *aNode, PRUint32 &outCount);
@ -608,6 +617,11 @@ protected:
nsCOMPtr<nsITransactionManager> mTxnMgr;
nsCOMPtr<nsIEditProperty> mEditProperty;
nsCOMPtr<nsICSSStyleSheet> mLastStyleSheet; // is owning this dangerous?
nsWeakPtr mPlaceHolderTxn;
nsIAtom *mPlaceHolderName;
PRInt32 mPlaceHolderBatch;
nsCOMPtr<nsIDOMNode> mTxnStartNode;
PRInt32 mTxnStartOffset;
//
// data necessary to build IME transactions

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

@ -25,6 +25,7 @@
#include "nsIDOMNode.h"
#include "nsIDOMSelection.h"
#include "nsIEditor.h"
#include "nsIAtom.h"
class nsAutoEditBatch
{
@ -43,11 +44,21 @@ class nsAutoEditMayBatch
PRBool mDidBatch;
public:
nsAutoEditMayBatch( nsIEditor *aEd) : mEd(do_QueryInterface(aEd)), mDidBatch(PR_FALSE) {}
nsAutoEditMayBatch() { if (mEd && mDidBatch) mEd->EndTransaction(); }
~nsAutoEditMayBatch() { if (mEd && mDidBatch) mEd->EndTransaction(); }
void batch() { if (mEd && !mDidBatch) {mEd->BeginTransaction(); mDidBatch=PR_TRUE;} }
};
class nsAutoPlaceHolderBatch
{
private:
nsCOMPtr<nsIEditor> mEd;
public:
nsAutoPlaceHolderBatch( nsIEditor *aEd, nsIAtom *atom) : mEd(do_QueryInterface(aEd))
{ if (mEd) mEd->BeginPlaceHolderTransaction(atom); }
~nsAutoPlaceHolderBatch() { if (mEd) mEd->EndPlaceHolderTransaction(); }
};
class nsAutoSelectionReset
{

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

@ -21,9 +21,6 @@
#include "nsEditor.h"
#include "nsHTMLEditor.h"
#include "PlaceholderTxn.h"
#include "InsertTextTxn.h"
#include "nsIContent.h"
#include "nsIContentIterator.h"
#include "nsIDOMNode.h"
@ -90,7 +87,6 @@ nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection,
case kInsertText:
return WillInsertText(aSelection,
aCancel,
info->placeTxn,
info->inString,
info->outString,
info->typeInState,
@ -127,11 +123,13 @@ nsHTMLEditRules::DidDoAction(nsIDOMSelection *aSelection,
switch (info->action)
{
case kInsertBreak:
return NS_OK;
case kUndo:
return nsTextEditRules::DidDoAction(aSelection, aInfo, aResult);
case kRedo:
return nsTextEditRules::DidDoAction(aSelection, aInfo, aResult);
}
// clean up any empty nodes in the selection
// other than undo and redo, clean up any empty nodes in the selection
CleanUpSelection(aSelection);
return nsTextEditRules::DidDoAction(aSelection, aInfo, aResult);
@ -145,7 +143,6 @@ nsHTMLEditRules::DidDoAction(nsIDOMSelection *aSelection,
nsresult
nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
PRBool *aCancel,
PlaceholderTxn **aTxn,
const nsString *inString,
nsString *outString,
TypeInState typeInState,
@ -155,7 +152,8 @@ nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
// initialize out param
*aCancel = PR_TRUE;
nsresult res;
nsAutoEditMayBatch optionalBatch;
nsCOMPtr<nsIDOMNode> selNode;
PRInt32 selOffset;
char specialChars[] = {'\t',' ',nbsp,'\n',0};
@ -169,20 +167,27 @@ nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
if (NS_FAILED(res)) return res;
}
// get the (collapsed) selection location
res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset);
if (NS_FAILED(res)) return res;
res = WillInsert(aSelection, aCancel);
if (NS_FAILED(res)) return res;
// initialize out param
// we want to ignore result of WillInsert()
*aCancel = PR_TRUE;
// split any mailcites in the way
if (1 || mFlags & nsIHTMLEditor::eEditorMailMask)
if (mFlags & nsIHTMLEditor::eEditorMailMask)
{
nsCOMPtr<nsIDOMNode> citeNode, selNode;
PRInt32 selOffset, newOffset;
res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset);
if (NS_FAILED(res)) return res;
nsCOMPtr<nsIDOMNode> citeNode;
PRInt32 newOffset;
res = GetTopEnclosingMailCite(selNode, &citeNode);
if (NS_FAILED(res)) return res;
if (citeNode)
{
// turn batching on
optionalBatch.batch();
res = mEditor->SplitNodeDeep(citeNode, selNode, selOffset, &newOffset);
if (NS_FAILED(res)) return res;
res = citeNode->GetParentNode(getter_AddRefs(selNode));
@ -192,9 +197,73 @@ nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
}
}
// identify the block
nsCOMPtr<nsIDOMNode> blockParent;
if (nsEditor::IsBlockNode(selNode))
blockParent = selNode;
else
blockParent = mEditor->GetBlockNodeParent(selNode);
if (!blockParent) return NS_ERROR_FAILURE;
// are we not in a textnode?
if (!mEditor->IsTextNode(selNode))
{
// find a nearby text node if possible
nsCOMPtr<nsIDOMNode> priorNode, nextNode;
res = GetPriorHTMLNode(selNode, selOffset, &priorNode);
if (NS_SUCCEEDED(res) && priorNode && mEditor->IsTextNode(priorNode)
&& (blockParent == mEditor->GetBlockNodeParent(priorNode)))
{
// put selection at end of prior node
PRUint32 strLength;
nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(priorNode);
res = textNode->GetLength(&strLength);
if (NS_FAILED(res)) return res;
res = aSelection->Collapse(priorNode, strLength);
if (NS_FAILED(res)) return res;
}
else
{
res = GetNextHTMLNode(selNode, selOffset, &nextNode);
if (NS_SUCCEEDED(res) && nextNode && mEditor->IsTextNode(nextNode)
&& (blockParent == mEditor->GetBlockNodeParent(nextNode)))
{
// put selection at begining of next node
res = aSelection->Collapse(nextNode, 0);
if (NS_FAILED(res)) return res;
}
}
// if we are right after a moz br, delete it and make a new moz div
PRBool needMozDiv = PR_FALSE;
if (priorNode && IsBreak(priorNode) && HasMozAttr(priorNode)
&& (blockParent == mEditor->GetBlockNodeParent(priorNode)))
{
needMozDiv = PR_TRUE;
res = mEditor->DeleteNode(priorNode);
if (NS_FAILED(res)) return res;
}
// if we are directly in a body or (non-moz) div, create a moz-div.
// Also creat one if we detected a prior moz br (see above).
res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset);
if (NS_FAILED(res)) return res;
if (needMozDiv || IsBody(selNode) || IsNormalDiv(selNode))
{
// wrap things up in a moz-div
nsCOMPtr<nsIDOMNode> mozDiv;
res = CreateMozDiv(selNode, selOffset, &mozDiv);
if (NS_FAILED(res)) return res;
// put selection in it
res = aSelection->Collapse(mozDiv, 0);
if (NS_FAILED(res)) return res;
}
}
char nbspStr[2] = {nbsp, 0};
nsString theString(*inString); // copy instr for now
nsString theString(*inString); // copy instring for now
PRInt32 pos = theString.FindCharInSet(specialChars);
while (theString.Length())
{
@ -208,23 +277,23 @@ nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
// is it a solo tab?
if (partialString == "\t" )
{
res = InsertTab(aSelection,&bCancel,aTxn,outString);
res = InsertTab(aSelection,&bCancel,outString);
if (NS_FAILED(res)) return res;
res = DoTextInsertion(aSelection, aCancel, aTxn, outString, typeInState);
res = DoTextInsertion(aSelection, aCancel, outString, typeInState);
}
// is it a solo space?
else if (partialString == " ")
{
res = InsertSpace(aSelection,&bCancel,aTxn,outString);
res = InsertSpace(aSelection,&bCancel,outString);
if (NS_FAILED(res)) return res;
res = DoTextInsertion(aSelection, aCancel, aTxn, outString, typeInState);
res = DoTextInsertion(aSelection, aCancel, outString, typeInState);
}
// is it a solo nbsp?
else if (partialString == nbspStr)
{
res = InsertSpace(aSelection,&bCancel,aTxn,outString);
res = InsertSpace(aSelection,&bCancel,outString);
if (NS_FAILED(res)) return res;
res = DoTextInsertion(aSelection, aCancel, aTxn, outString, typeInState);
res = DoTextInsertion(aSelection, aCancel, outString, typeInState);
}
// is it a solo return?
else if (partialString == "\n")
@ -233,7 +302,7 @@ nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
}
else
{
res = DoTextInsertion(aSelection, aCancel, aTxn, &partialString, typeInState);
res = DoTextInsertion(aSelection, aCancel, &partialString, typeInState);
}
if (NS_FAILED(res)) return res;
pos = theString.FindCharInSet(specialChars);
@ -250,7 +319,6 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel)
*aCancel = PR_FALSE;
nsresult res;
nsAutoEditMayBatch optionalBatch;
res = WillInsert(aSelection, aCancel);
if (NS_FAILED(res)) return res;
@ -269,7 +337,7 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel)
}
// split any mailcites in the way
if (1 || mFlags & nsIHTMLEditor::eEditorMailMask)
if (mFlags & nsIHTMLEditor::eEditorMailMask)
{
nsCOMPtr<nsIDOMNode> citeNode, selNode;
PRInt32 selOffset, newOffset;
@ -280,8 +348,6 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel)
if (citeNode)
{
// turn batching on
optionalBatch.batch();
res = mEditor->SplitNodeDeep(citeNode, selNode, selOffset, &newOffset);
if (NS_FAILED(res)) return res;
res = citeNode->GetParentNode(getter_AddRefs(selNode));
@ -310,6 +376,7 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel)
return mEditor->InsertTextImpl(theString);
}
// identify the block
nsCOMPtr<nsIDOMNode> blockParent;
if (nsEditor::IsBlockNode(node))
@ -319,8 +386,67 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel)
if (!blockParent) return NS_ERROR_FAILURE;
// break action depends on type of block
PRBool bIsMozDiv = IsMozDiv(blockParent);
PRBool bIsNormalDiv = IsNormalDiv(blockParent);
// body, div, or mozdiv: insert a moz br
if (IsBody(blockParent) || bIsNormalDiv ||
(bIsMozDiv && AtEndOfBlock(node, offset, blockParent)))
{
if (bIsMozDiv)
{
// put selection beyond the mozdiv first
nsCOMPtr<nsIDOMNode> parent;
PRInt32 pOffset;
res = nsEditor::GetNodeLocation(blockParent, &parent, &pOffset);
if (NS_FAILED(res)) return res;
if (!parent) return NS_ERROR_FAILURE;
res = aSelection->Collapse(parent, pOffset+1);
if (NS_FAILED(res)) return res;
}
nsCOMPtr<nsIDOMNode> brNode;
res = mEditor->InsertBR(&brNode); // only inserts a br node
if (NS_FAILED(res)) return res;
// give it special moz attr
nsCOMPtr<nsIDOMElement> brElem = do_QueryInterface(brNode);
if (brElem)
{
res = mEditor->SetAttribute(brElem, "type", "_moz");
if (NS_FAILED(res)) return res;
}
*aCancel = PR_TRUE;
}
else if (bIsMozDiv && AtStartOfBlock(node, offset, blockParent))
{
// position selection in front of mozdiv, and let default code
// make a normal br
nsCOMPtr<nsIDOMNode> parent;
PRInt32 pOffset;
res = nsEditor::GetNodeLocation(blockParent, &parent, &pOffset);
if (NS_FAILED(res)) return res;
if (!parent) return NS_ERROR_FAILURE;
res = aSelection->Collapse(parent, pOffset);
if (NS_FAILED(res)) return res;
}
else if (bIsMozDiv)
{
// we are in the middle of the mozdiv: split it
PRInt32 newOffset;
res = mEditor->SplitNodeDeep( blockParent, node, offset, &newOffset);
if (NS_FAILED(res)) return res;
// put selection at beginning of new mozdiv
nsCOMPtr<nsIDOMNode> parent;
blockParent->GetParentNode(getter_AddRefs(parent));
nsCOMPtr <nsIDOMNode> newDiv = nsEditor::GetChildAt(parent, newOffset);
if (!newDiv || !IsMozDiv(newDiv)) return NS_ERROR_FAILURE;
res = aSelection->Collapse(newDiv, 0);
if (NS_FAILED(res)) return res;
*aCancel = PR_TRUE;
}
// headers: close (or split) header
if (IsHeader(blockParent))
else if (IsHeader(blockParent))
{
res = ReturnInHeader(aSelection, blockParent, node, offset);
*aCancel = PR_TRUE;
@ -328,14 +454,14 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel)
}
// paragraphs: special rules to look for <br>s
if (IsParagraph(blockParent))
else if (IsParagraph(blockParent))
{
res = ReturnInParagraph(aSelection, blockParent, node, offset, aCancel);
return NS_OK;
}
// list items: special rules to make new list items
if (IsListItem(blockParent))
else if (IsListItem(blockParent))
{
res = ReturnInListItem(aSelection, blockParent, node, offset);
*aCancel = PR_TRUE;
@ -411,10 +537,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESe
if (!leftParent || !rightParent)
return NS_OK; // bail to default
nsCOMPtr<nsIAtom> leftAtom = mEditor->GetTag(leftParent);
nsCOMPtr<nsIAtom> rightAtom = mEditor->GetTag(rightParent);
if (leftAtom.get() == rightAtom.get())
if (mEditor->NodesSameType(leftParent, rightParent))
{
nsCOMPtr<nsIDOMNode> topParent;
leftParent->GetParentNode(getter_AddRefs(topParent));
@ -454,10 +577,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESe
// are the blocks of same type?
nsCOMPtr<nsIDOMNode> leftParent = mEditor->GetBlockNodeParent(node);
nsCOMPtr<nsIDOMNode> rightParent = mEditor->GetBlockNodeParent(nextNode);
nsCOMPtr<nsIAtom> leftAtom = mEditor->GetTag(leftParent);
nsCOMPtr<nsIAtom> rightAtom = mEditor->GetTag(rightParent);
if (leftAtom.get() == rightAtom.get())
if (mEditor->NodesSameType(leftParent, rightParent))
{
nsCOMPtr<nsIDOMNode> topParent;
leftParent->GetParentNode(getter_AddRefs(topParent));
@ -561,10 +681,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESe
// bail to default if blocks aren't siblings
if (leftBlockParent.get() != rightBlockParent.get()) return NS_OK;
nsCOMPtr<nsIAtom> leftAtom = mEditor->GetTag(leftParent);
nsCOMPtr<nsIAtom> rightAtom = mEditor->GetTag(rightParent);
if (leftAtom.get() == rightAtom.get())
if (mEditor->NodesSameType(leftParent, rightParent))
{
nsCOMPtr<nsIDOMNode> topParent;
leftParent->GetParentNode(getter_AddRefs(topParent));
@ -810,8 +927,15 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo
}
else
{
nsAutoString listItemType = "li";
res = InsertContainerAbove(curNode, &listItem, listItemType);
// don't wrap li around a moz-div. instead replace moz-div with li
if (IsMozDiv(curNode))
{
res = ReplaceContainer(curNode, &listItem, "li");
}
else
{
res = InsertContainerAbove(curNode, &listItem, "li");
}
if (NS_FAILED(res)) return res;
if (nsEditor::IsInlineNode(curNode))
prevListItem = listItem;
@ -1291,6 +1415,7 @@ nsHTMLEditRules::IsHeader(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsHeader");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if ( (tag == "h1") ||
(tag == "h2") ||
(tag == "h3") ||
@ -1313,6 +1438,7 @@ nsHTMLEditRules::IsParagraph(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsParagraph");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag == "p")
{
return PR_TRUE;
@ -1330,6 +1456,7 @@ nsHTMLEditRules::IsListItem(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsListItem");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag == "li")
{
return PR_TRUE;
@ -1347,6 +1474,7 @@ nsHTMLEditRules::IsList(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsList");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if ( (tag == "ol") ||
(tag == "ul") )
{
@ -1365,6 +1493,7 @@ nsHTMLEditRules::IsOrderedList(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsOrderedList");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag == "ol")
{
return PR_TRUE;
@ -1382,6 +1511,7 @@ nsHTMLEditRules::IsUnorderedList(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsUnorderedList");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag == "ul")
{
return PR_TRUE;
@ -1399,6 +1529,7 @@ nsHTMLEditRules::IsBreak(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsBreak");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag == "br")
{
return PR_TRUE;
@ -1416,6 +1547,7 @@ nsHTMLEditRules::IsBody(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsBody");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag == "body")
{
return PR_TRUE;
@ -1433,6 +1565,7 @@ nsHTMLEditRules::IsBlockquote(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsBlockquote");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag == "blockquote")
{
return PR_TRUE;
@ -1441,6 +1574,24 @@ nsHTMLEditRules::IsBlockquote(nsIDOMNode *node)
}
///////////////////////////////////////////////////////////////////////////
// IsAnchor: true if node an html anchor node
//
PRBool
nsHTMLEditRules::IsAnchor(nsIDOMNode *node)
{
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsAnchor");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag == "a")
{
return PR_TRUE;
}
return PR_FALSE;
}
///////////////////////////////////////////////////////////////////////////
// IsDiv: true if node an html div node
//
@ -1450,6 +1601,7 @@ nsHTMLEditRules::IsDiv(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsDiv");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag == "div")
{
return PR_TRUE;
@ -1458,6 +1610,51 @@ nsHTMLEditRules::IsDiv(nsIDOMNode *node)
}
///////////////////////////////////////////////////////////////////////////
// IsNormalDiv: true if node an html div node, without type = _moz
//
PRBool
nsHTMLEditRules::IsNormalDiv(nsIDOMNode *node)
{
if (IsDiv(node) && !HasMozAttr(node)) return PR_TRUE;
return PR_FALSE;
}
///////////////////////////////////////////////////////////////////////////
// IsMozDiv: true if node an html div node with type = _moz
//
PRBool
nsHTMLEditRules::IsMozDiv(nsIDOMNode *node)
{
if (IsDiv(node) && HasMozAttr(node)) return PR_TRUE;
return PR_FALSE;
}
///////////////////////////////////////////////////////////////////////////
// HasMozAttr: true if node has type attribute = _moz
// (used to indicate the div's and br's we use in
// mail compose rules)
//
PRBool
nsHTMLEditRules::HasMozAttr(nsIDOMNode *node)
{
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::HasMozAttr");
nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(node);
if (elem)
{
nsAutoString typeAttrName("type");
nsAutoString typeAttrVal;
nsresult res = elem->GetAttribute(typeAttrName, typeAttrVal);
typeAttrVal.ToLowerCase();
if (NS_SUCCEEDED(res) && (typeAttrVal == "_moz"))
return PR_TRUE;
}
return PR_FALSE;
}
///////////////////////////////////////////////////////////////////////////
// IsMailCite: true if node an html blockquote with type=cite
//
@ -1471,6 +1668,7 @@ nsHTMLEditRules::IsMailCite(nsIDOMNode *node)
nsAutoString typeAttrName("type");
nsAutoString typeAttrVal;
nsresult res = bqElem->GetAttribute(typeAttrName, typeAttrVal);
typeAttrVal.ToLowerCase();
if (NS_SUCCEEDED(res))
{
if (typeAttrVal == "cite")
@ -1544,6 +1742,17 @@ nsHTMLEditRules::IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyNode)
return NS_OK;
}
// if it's not a text node (handled above) and it's not a container,
// then we dont call it empty (it's an <hr>, or <br>, etc).
// Also, if it's an anchor then dont treat it as empty - even though
// anchors are containers, named anchors are "empty" but we don't
// want to treat them as such.
if (!mEditor->IsContainer(aNode) || IsAnchor(aNode))
{
*outIsEmptyNode = PR_FALSE;
return NS_OK;
}
// iterate over node. if no children, or all children are either
// empty text nodes or non-editable, then node qualifies as empty
nsCOMPtr<nsIContentIterator> iter;
@ -1593,6 +1802,24 @@ nsHTMLEditRules::IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyNode)
}
///////////////////////////////////////////////////////////////////////////
// CreateMozDiv: makes a div with type = _moz
//
nsresult
nsHTMLEditRules::CreateMozDiv(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outDiv)
{
if (!inParent || !outDiv) return NS_ERROR_NULL_POINTER;
nsAutoString divType= "div";
*outDiv = nsnull;
nsresult res = mEditor->CreateNode(divType, inParent, inOffset, getter_AddRefs(*outDiv));
if (NS_FAILED(res)) return res;
// give it special moz attr
nsCOMPtr<nsIDOMElement> mozDivElem = do_QueryInterface(*outDiv);
res = mEditor->SetAttribute(mozDivElem, "type", "_moz");
return res;
}
///////////////////////////////////////////////////////////////////////////
// GetPriorHTMLNode: returns the previous editable leaf node, if there is
// one within the <body>
@ -1757,6 +1984,48 @@ nsHTMLEditRules::IsLastNode(nsIDOMNode *aNode)
}
///////////////////////////////////////////////////////////////////////////
// AtStartOfBlock: is node/offset at the start of the editable material in this block?
//
PRBool
nsHTMLEditRules::AtStartOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock)
{
nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(aNode);
if (nodeAsText && aOffset) return PR_FALSE; // there are chars in front of us
nsCOMPtr<nsIDOMNode> priorNode;
nsresult res = GetPriorHTMLNode(aNode, aOffset, &priorNode);
if (NS_FAILED(res)) return PR_TRUE;
if (!priorNode) return PR_TRUE;
nsCOMPtr<nsIDOMNode> blockParent = mEditor->GetBlockNodeParent(priorNode);
if (blockParent && (blockParent.get() == aBlock)) return PR_FALSE;
return PR_TRUE;
}
///////////////////////////////////////////////////////////////////////////
// AtEndOfBlock: is node/offset at the end of the editable material in this block?
//
PRBool
nsHTMLEditRules::AtEndOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock)
{
nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(aNode);
if (nodeAsText)
{
PRUint32 strLength;
nodeAsText->GetLength(&strLength);
if (strLength > aOffset) return PR_FALSE; // there are chars in after us
}
nsCOMPtr<nsIDOMNode> nextNode;
nsresult res = GetNextHTMLNode(aNode, aOffset, &nextNode);
if (NS_FAILED(res)) return PR_TRUE;
if (!nextNode) return PR_TRUE;
nsCOMPtr<nsIDOMNode> blockParent = mEditor->GetBlockNodeParent(nextNode);
if (blockParent && (blockParent.get() == aBlock)) return PR_FALSE;
return PR_TRUE;
}
///////////////////////////////////////////////////////////////////////////
// GetPromotedPoint: figure out where a start or end point for a block
// operation really is
@ -2171,7 +2440,7 @@ nsHTMLEditRules::RemoveContainer(nsIDOMNode *inNode)
nsresult
nsHTMLEditRules::InsertContainerAbove(nsIDOMNode *inNode,
nsCOMPtr<nsIDOMNode> *outNode,
nsString &aNodeType)
const nsString &aNodeType)
{
if (!inNode || !outNode)
return NS_ERROR_NULL_POINTER;
@ -2207,7 +2476,6 @@ nsHTMLEditRules::InsertContainerAbove(nsIDOMNode *inNode,
nsresult
nsHTMLEditRules::InsertTab(nsIDOMSelection *aSelection,
PRBool *aCancel,
PlaceholderTxn **aTxn,
nsString *outString)
{
nsCOMPtr<nsIDOMNode> parentNode;
@ -2271,7 +2539,6 @@ nsHTMLEditRules::InsertTab(nsIDOMSelection *aSelection,
nsresult
nsHTMLEditRules::InsertSpace(nsIDOMSelection *aSelection,
PRBool *aCancel,
PlaceholderTxn **aTxn,
nsString *outString)
{
nsCOMPtr<nsIDOMNode> parentNode;
@ -2351,10 +2618,6 @@ nsHTMLEditRules::ReturnInHeader(nsIDOMSelection *aSelection,
res = mEditor->SplitNodeDeep( aHeader, aNode, aOffset, &newOffset);
if (NS_FAILED(res)) return res;
// revisit the below when we move to using divs as standard paragraphs.
// need to create an "empty" div in the fisrt case below, and need to
// use a div instead of a <p> in the second case below
// if the new (righthand) header node is empty, delete it
PRBool isEmpty;
res = IsEmptyBlock(aHeader, &isEmpty);
@ -2366,10 +2629,13 @@ nsHTMLEditRules::ReturnInHeader(nsIDOMSelection *aSelection,
res = aSelection->Collapse(headerParent,offset+1);
return res;
}
// else rewrap it in a paragraph
// else rewrap it in a mozdiv
nsCOMPtr<nsIDOMNode> newBlock;
nsAutoString blockType("p");
res = ReplaceContainer(aHeader,&newBlock,blockType);
res = ReplaceContainer(aHeader,&newBlock,"div");
if (NS_FAILED(res)) return res;
// give it special moz attr
nsCOMPtr<nsIDOMElement> mozDivElem = do_QueryInterface(newBlock);
res = mEditor->SetAttribute(mozDivElem, "type", "_moz");
if (NS_FAILED(res)) return res;
res = aSelection->Collapse(newBlock,0);
return res;
@ -2685,10 +2951,11 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString
// to breaks, and runs of spaces to nbsps.
// xxx floppy moose
// if curNode is a p, header, address, or pre, replace
// if curNode is a mozdiv, p, header, address, or pre, replace
// it with a new block of correct type.
// xxx floppy moose: pre cant hold everything the others can
if ((curNodeTag == "pre") ||
if (IsMozDiv(curNode) ||
(curNodeTag == "pre") ||
(curNodeTag == "p") ||
(curNodeTag == "h1") ||
(curNodeTag == "h2") ||
@ -2710,7 +2977,7 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString
(curNodeTag == "ul") ||
(curNodeTag == "li") ||
(curNodeTag == "blockquote") ||
(curNodeTag == "div"))
(curNodeTag == "div")) // div's other than mozdivs
{
curBlock = 0; // forget any previous block used for previous inline nodes
// recursion time
@ -2769,24 +3036,14 @@ nsHTMLEditRules::IsFirstEditableChild( nsIDOMNode *aNode, PRBool *aOutIsFirst)
*aOutIsFirst = PR_FALSE;
// find first editable child and compare it to aNode
nsCOMPtr<nsIDOMNode> parent;
nsCOMPtr<nsIDOMNode> parent, firstChild;
nsresult res = aNode->GetParentNode(getter_AddRefs(parent));
if (NS_FAILED(res)) return res;
if (!parent) return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMNode> child;
parent->GetFirstChild(getter_AddRefs(child));
if (!child) return NS_ERROR_FAILURE;
if (!mEditor->IsEditable(child))
{
nsCOMPtr<nsIDOMNode> tmp;
res = GetNextHTMLNode(child, &tmp);
if (NS_FAILED(res)) return res;
if (!tmp) return NS_ERROR_FAILURE;
child = tmp;
}
res = GetFirstEditableChild(parent, &firstChild);
if (NS_FAILED(res)) return res;
*aOutIsFirst = (child.get() == aNode);
*aOutIsFirst = (firstChild.get() == aNode);
return res;
}
@ -2801,24 +3058,70 @@ nsHTMLEditRules::IsLastEditableChild( nsIDOMNode *aNode, PRBool *aOutIsLast)
*aOutIsLast = PR_FALSE;
// find last editable child and compare it to aNode
nsCOMPtr<nsIDOMNode> parent;
nsCOMPtr<nsIDOMNode> parent, lastChild;
nsresult res = aNode->GetParentNode(getter_AddRefs(parent));
if (NS_FAILED(res)) return res;
if (!parent) return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMNode> child;
parent->GetLastChild(getter_AddRefs(child));
if (!child) return NS_ERROR_FAILURE;
res = GetLastEditableChild(parent, &lastChild);
if (NS_FAILED(res)) return res;
*aOutIsLast = (lastChild.get() == aNode);
return res;
}
if (!mEditor->IsEditable(child))
nsresult
nsHTMLEditRules::GetFirstEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstChild)
{
// check parms
if (!aOutFirstChild || !aNode) return NS_ERROR_NULL_POINTER;
// init out parms
*aOutFirstChild = nsnull;
// find first editable child
nsCOMPtr<nsIDOMNode> child;
nsresult res = aNode->GetFirstChild(getter_AddRefs(child));
if (NS_FAILED(res)) return res;
while (child && !mEditor->IsEditable(child))
{
nsCOMPtr<nsIDOMNode> tmp;
res = GetPriorHTMLNode(child, &tmp);
res = child->GetNextSibling(getter_AddRefs(tmp));
if (NS_FAILED(res)) return res;
if (!tmp) return NS_ERROR_FAILURE;
child = tmp;
}
*aOutIsLast = (child.get() == aNode);
*aOutFirstChild = child;
return res;
}
nsresult
nsHTMLEditRules::GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastChild)
{
// check parms
if (!aOutLastChild || !aNode) return NS_ERROR_NULL_POINTER;
// init out parms
*aOutLastChild = nsnull;
// find last editable child
nsCOMPtr<nsIDOMNode> child;
nsresult res = aNode->GetLastChild(getter_AddRefs(child));
if (NS_FAILED(res)) return res;
while (child && !mEditor->IsEditable(child))
{
nsCOMPtr<nsIDOMNode> tmp;
res = child->GetPreviousSibling(getter_AddRefs(tmp));
if (NS_FAILED(res)) return res;
if (!tmp) return NS_ERROR_FAILURE;
child = tmp;
}
*aOutLastChild = child;
return res;
}
@ -2843,10 +3146,21 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft,
nsresult res = NS_OK;
// caller responsible for:
// left & right node are same type
// left & right node have same parent
nsCOMPtr<nsIDOMNode> parent;
aNodeLeft->GetParentNode(getter_AddRefs(parent));
PRInt32 parOffset;
nsCOMPtr<nsIDOMNode> parent, rightParent;
res = nsEditor::GetNodeLocation(aNodeLeft, &parent, &parOffset);
if (NS_FAILED(res)) return res;
aNodeRight->GetParentNode(getter_AddRefs(rightParent));
// if they don't have the same parent, first move the 'right' node
// to after the 'left' one
if (parent != rightParent)
{
res = mEditor->DeleteNode(aNodeRight);
if (NS_FAILED(res)) return res;
res = mEditor->InsertNode(aNodeRight, parent, parOffset);
if (NS_FAILED(res)) return res;
}
// defaults for outParams
*aOutMergeParent = aNodeRight;
@ -2891,14 +3205,23 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft,
}
else
{
// remember the last left child, and firt right child
nsCOMPtr<nsIDOMNode> lastLeft, firstRight;
res = GetLastEditableChild(aNodeLeft, &lastLeft);
if (NS_FAILED(res)) return res;
res = GetFirstEditableChild(aNodeRight, &firstRight);
if (NS_FAILED(res)) return res;
// for list items, divs, etc, merge smart
res = mEditor->JoinNodes(aNodeLeft, aNodeRight, parent);
if (NS_FAILED(res)) return res;
// XXX floppy moose - figure out newNodeLeft & newNodeRight and recurse
// res = JoinNodesSmart(newNodeLeft, newNodeRight, aOutMergeParent, aOutMergeOffset);
// if (NS_FAILED(res)) return res;
return res;
if (lastLeft && firstRight && mEditor->NodesSameType(lastLeft, firstRight))
{
return JoinNodesSmart(lastLeft, firstRight, aOutMergeParent, aOutMergeOffset);
}
}
return res;
}
@ -3057,10 +3380,17 @@ nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList)
res = mEditor->InsertNode(curNode, curParPar, parOffset);
if (NS_FAILED(res)) return res;
// remove list items container if we promoted them out of list
// change list items container to mozdiv if we promoted them out of list
if (!IsList(curParPar) && IsListItem(curNode))
{
res = RemoveContainer(curNode);
nsCOMPtr<nsIDOMNode> mozDiv;
res = ReplaceContainer(curNode, &mozDiv, "div");
if (NS_FAILED(res)) return res;
// give it special moz attr
nsCOMPtr<nsIDOMElement> mozDivElem = do_QueryInterface(mozDiv);
res = mEditor->SetAttribute(mozDivElem, "type", "_moz");
if (NS_FAILED(res)) return res;
*aOutOfList = PR_TRUE;
if (NS_FAILED(res)) return res;
}

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

@ -49,7 +49,6 @@ protected:
// nsHTMLEditRules implementation methods
nsresult WillInsertText(nsIDOMSelection *aSelection,
PRBool *aCancel,
PlaceholderTxn **aTxn,
const nsString *inString,
nsString *outString,
TypeInState typeInState,
@ -63,8 +62,8 @@ protected:
nsresult WillAlign(nsIDOMSelection *aSelection, const nsString *alignType, PRBool *aCancel);
nsresult WillMakeBasicBlock(nsIDOMSelection *aSelection, const nsString *aBlockType, PRBool *aCancel);
nsresult InsertTab(nsIDOMSelection *aSelection, PRBool *aCancel, PlaceholderTxn **aTxn, nsString *outString);
nsresult InsertSpace(nsIDOMSelection *aSelection, PRBool *aCancel, PlaceholderTxn **aTxn, nsString *outString);
nsresult InsertTab(nsIDOMSelection *aSelection, PRBool *aCancel, nsString *outString);
nsresult InsertSpace(nsIDOMSelection *aSelection, PRBool *aCancel, nsString *outString);
nsresult ReturnInHeader(nsIDOMSelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset);
nsresult ReturnInParagraph(nsIDOMSelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset, PRBool *aCancel);
@ -83,8 +82,12 @@ protected:
static PRBool IsBreak(nsIDOMNode *aNode);
static PRBool IsBody(nsIDOMNode *aNode);
static PRBool IsBlockquote(nsIDOMNode *aNode);
static PRBool IsAnchor(nsIDOMNode *aNode);
static PRBool IsDiv(nsIDOMNode *aNode);
static PRBool IsNormalDiv(nsIDOMNode *aNode);
static PRBool IsMozDiv(nsIDOMNode *aNode);
static PRBool IsMailCite(nsIDOMNode *aNode);
static PRBool HasMozAttr(nsIDOMNode *aNode);
static PRBool InBody(nsIDOMNode *aNode);
@ -92,6 +95,9 @@ protected:
nsresult IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyNode);
PRBool IsFirstNode(nsIDOMNode *aNode);
PRBool IsLastNode(nsIDOMNode *aNode);
PRBool AtStartOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock);
PRBool AtEndOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock);
nsresult CreateMozDiv(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outDiv);
nsresult GetPriorHTMLNode(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode);
nsresult GetPriorHTMLNode(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outNode);
nsresult GetNextHTMLNode(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode);
@ -115,10 +121,12 @@ protected:
nsresult ReplaceContainer(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode, const nsString &aNodeType);
nsresult RemoveContainer(nsIDOMNode *inNode);
nsresult InsertContainerAbove(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode, nsString &aNodeType);
nsresult InsertContainerAbove(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode, const nsString &aNodeType);
nsresult IsFirstEditableChild( nsIDOMNode *aNode, PRBool *aOutIsFirst);
nsresult IsLastEditableChild( nsIDOMNode *aNode, PRBool *aOutIsLast);
nsresult GetFirstEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstChild);
nsresult GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastChild);
nsresult JoinNodesSmart( nsIDOMNode *aNodeLeft,
nsIDOMNode *aNodeRight,

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -243,7 +243,7 @@ protected:
// key event helpers
NS_IMETHOD TabInTable(PRBool inIsShift, PRBool *outHandled);
NS_IMETHOD InsertBR();
NS_IMETHOD InsertBR(nsCOMPtr<nsIDOMNode> *outBRNode);
// Table Editing (implemented in EditTable.cpp)
@ -474,6 +474,9 @@ protected:
PRBool mIsComposing;
PRInt32 mMaxTextLength;
public:
static nsIAtom *gTypingTxnName;
// friends
friend class nsHTMLEditRules;
friend class nsTextEditRules;

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

@ -19,8 +19,6 @@
#include "nsTextEditRules.h"
#include "nsEditor.h"
#include "PlaceholderTxn.h"
#include "InsertTextTxn.h"
#include "nsCOMPtr.h"
#include "nsIDOMNode.h"
@ -140,7 +138,6 @@ nsTextEditRules::WillDoAction(nsIDOMSelection *aSelection,
case kInsertText:
return WillInsertText(aSelection,
aCancel,
info->placeTxn,
info->inString,
info->outString,
info->typeInState,
@ -265,7 +262,6 @@ nsTextEditRules::DidInsertBreak(nsIDOMSelection *aSelection, nsresult aResult)
nsresult
nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection,
PRBool *aCancel,
PlaceholderTxn **aTxn,
const nsString *aInString,
nsString *aOutString,
TypeInState aTypeInState,
@ -293,7 +289,7 @@ nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection,
// do text insertion
PRBool bCancel;
res = DoTextInsertion(aSelection, &bCancel, aTxn, aOutString, aTypeInState);
res = DoTextInsertion(aSelection, &bCancel, aOutString, aTypeInState);
return res;
}
@ -1059,7 +1055,6 @@ nsTextEditRules::EchoInsertionToPWBuff(nsIDOMSelection *aSelection, nsString *aO
nsresult
nsTextEditRules::DoTextInsertion(nsIDOMSelection *aSelection,
PRBool *aCancel,
PlaceholderTxn **aTxn,
const nsString *aInString,
TypeInState aTypeInState)
{
@ -1070,14 +1065,6 @@ nsTextEditRules::DoTextInsertion(nsIDOMSelection *aSelection,
// rules code always does the insertion
*aCancel = PR_TRUE;
if (mBogusNode || (PR_TRUE==aTypeInState.IsAnySet()))
{
res = TransactionFactory::GetNewTransaction(PlaceholderTxn::GetCID(), (EditTxn **)aTxn);
if (NS_FAILED(res)) { return res; }
if (!*aTxn) { return NS_ERROR_NULL_POINTER; }
(*aTxn)->SetName(InsertTextTxn::gInsertTextTxnName);
mEditor->Do(*aTxn);
}
PRBool bCancel;
res = WillInsert(aSelection, &bCancel);
if (NS_SUCCEEDED(res) && (!bCancel))

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

@ -27,7 +27,6 @@
#include "nsEditRules.h"
#include "TypeInState.h"
class PlaceholderTxn;
class nsTextEditor;
/** Object that encapsulates HTML text-specific editing rules.
@ -86,7 +85,6 @@ protected:
// nsTextEditRules implementation methods
nsresult WillInsertText(nsIDOMSelection *aSelection,
PRBool *aCancel,
PlaceholderTxn **aTxn,
const nsString *inString,
nsString *outString,
TypeInState typeInState,
@ -180,7 +178,6 @@ protected:
/** do the actual text insertion */
nsresult DoTextInsertion(nsIDOMSelection *aSelection,
PRBool *aCancel,
PlaceholderTxn **aTxn,
const nsString *aInString,
TypeInState aTypeInState);
@ -199,7 +196,6 @@ class nsTextRulesInfo : public nsRulesInfo
nsTextRulesInfo(int aAction) :
nsRulesInfo(aAction),
placeTxn(0),
inString(0),
outString(0),
outputFormat(0),
@ -215,7 +211,6 @@ class nsTextRulesInfo : public nsRulesInfo
virtual ~nsTextRulesInfo() {};
// kInsertText
PlaceholderTxn **placeTxn;
const nsString *inString;
nsString *outString;
const nsString *outputFormat;

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

@ -18,6 +18,8 @@
#include "PlaceholderTxn.h"
#include "nsVoidArray.h"
#include "nsHTMLEditor.h"
#include "nsIPresShell.h"
#if defined(NS_DEBUG) && defined(DEBUG_buster)
static PRBool gNoisy = PR_TRUE;
@ -26,10 +28,11 @@ static const PRBool gNoisy = PR_FALSE;
#endif
PlaceholderTxn::PlaceholderTxn()
: EditAggregateTxn()
PlaceholderTxn::PlaceholderTxn() : EditAggregateTxn(),
mPresShellWeak(nsnull),
mAbsorb(PR_TRUE),
mForwarding(nsnull)
{
mAbsorb=PR_TRUE;
SetTransactionDescriptionID( kTransactionID );
/* log description initialized in parent constructor */
}
@ -39,39 +42,153 @@ PlaceholderTxn::~PlaceholderTxn()
{
}
NS_IMPL_ADDREF_INHERITED(PlaceholderTxn, EditAggregateTxn)
NS_IMPL_RELEASE_INHERITED(PlaceholderTxn, EditAggregateTxn)
//NS_IMPL_QUERY_INTERFACE_INHERITED(Class, Super, AdditionalInterface)
NS_IMETHODIMP PlaceholderTxn::QueryInterface(REFNSIID aIID, void** aInstancePtr)
{
if (!aInstancePtr) return NS_ERROR_NULL_POINTER;
if (aIID.Equals(nsIAbsorbingTransaction::GetIID())) {
*aInstancePtr = (nsISupports*)(nsIAbsorbingTransaction*)(this);
NS_ADDREF_THIS();
return NS_OK;
}
if (aIID.Equals(nsCOMTypeInfo<nsISupportsWeakReference>::GetIID())) {
*aInstancePtr = (nsISupports*)(nsISupportsWeakReference*) this;
NS_ADDREF_THIS();
return NS_OK;
}
return EditAggregateTxn::QueryInterface(aIID, aInstancePtr);
}
NS_IMETHODIMP PlaceholderTxn::Init(nsWeakPtr aPresShellWeak, nsIAtom *aName,
nsIDOMNode *aStartNode, PRInt32 aStartOffset)
{
NS_ASSERTION(aPresShellWeak, "bad args");
if (!aPresShellWeak) return NS_ERROR_NULL_POINTER;
mPresShellWeak = aPresShellWeak;
mName = aName;
mStartNode = do_QueryInterface(aStartNode);
mStartOffset = aStartOffset;
return NS_OK;
}
NS_IMETHODIMP PlaceholderTxn::Do(void)
{
if (gNoisy) { printf("PlaceholderTxn Do\n"); }
return NS_OK;
}
NS_IMETHODIMP PlaceholderTxn::Undo(void)
{
// using this to debug
return EditAggregateTxn::Undo();
}
NS_IMETHODIMP PlaceholderTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransaction)
{
if (!aDidMerge || !aTransaction) return NS_ERROR_NULL_POINTER;
// set out param default value
if (nsnull!=aDidMerge)
*aDidMerge=PR_FALSE;
nsresult result = NS_OK;
if ((nsnull!=aDidMerge) && (nsnull!=aTransaction))
*aDidMerge=PR_FALSE;
nsresult res = NS_OK;
if (mForwarding)
{
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;
if (gNoisy) { printf("Placeholder txn assimilated %p\n", aTransaction); }
}
else
{ // let our last child txn make the choice
PRInt32 count = mChildren->Count();
if (0<count)
NS_NOTREACHED("tried to merge into a placeholder that was in forwarding mode!");
return NS_ERROR_FAILURE;
}
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;
if (gNoisy) { printf("Placeholder txn assimilated %p\n", aTransaction); }
}
else
{ // merge typing transactions if the selection matches
if (mName.get() == nsHTMLEditor::gTypingTxnName)
{
nsCOMPtr<nsIAbsorbingTransaction> plcTxn;// = do_QueryInterface(editTxn);
// cant do_QueryInterface() above due to our broken transaction interfaces.
// instead have to brute it below. ugh.
editTxn->QueryInterface(nsIAbsorbingTransaction::GetIID(), getter_AddRefs(plcTxn));
if (plcTxn)
{
EditTxn *lastTxn = (EditTxn*)(mChildren->ElementAt(count-1));
if (lastTxn)
nsIAtom *atom;
plcTxn->GetTxnName(&atom);
if (atom && (atom == nsHTMLEditor::gTypingTxnName))
{
lastTxn->Merge(aDidMerge, aTransaction);
nsCOMPtr<nsIDOMNode> otherTxnStartNode;
PRInt32 otherTxnStartOffset;
res = plcTxn->GetStartNodeAndOffset(&otherTxnStartNode, &otherTxnStartOffset);
if (NS_FAILED(res)) return res;
if ((otherTxnStartNode == mEndNode) && (otherTxnStartOffset == mEndOffset))
{
mAbsorb = PR_TRUE; // we need to start absorbing again
plcTxn->ForwardEndBatchTo(this);
AppendChild(editTxn);
*aDidMerge = PR_TRUE;
}
}
}
}
}
return result;
return res;
}
NS_IMETHODIMP PlaceholderTxn::GetTxnName(nsIAtom **aName)
{
return GetName(aName);
}
NS_IMETHODIMP PlaceholderTxn::GetStartNodeAndOffset(nsCOMPtr<nsIDOMNode> *aTxnStartNode, PRInt32 *aTxnStartOffset)
{
if (!aTxnStartNode || !aTxnStartOffset) return NS_ERROR_NULL_POINTER;
*aTxnStartNode = mStartNode;
*aTxnStartOffset = mStartOffset;
return NS_OK;
}
NS_IMETHODIMP PlaceholderTxn::EndPlaceHolderBatch()
{
mAbsorb = PR_FALSE;
if (mForwarding)
{
nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryReferent(mForwarding);
if (plcTxn) plcTxn->EndPlaceHolderBatch();
}
// if we are a typing transaction, remember our selection state
if (mName.get() == nsHTMLEditor::gTypingTxnName)
{
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
if (!ps) return NS_ERROR_NOT_INITIALIZED;
nsCOMPtr<nsIDOMSelection> selection;
nsresult res = ps->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
if (!selection) return NS_ERROR_NULL_POINTER;
res = nsEditor::GetStartNodeAndOffset(selection, &mEndNode, &mEndOffset);
if (NS_FAILED(res)) return res;
}
return NS_OK;
};
NS_IMETHODIMP PlaceholderTxn::ForwardEndBatchTo(nsIAbsorbingTransaction *aForwardingAddress)
{
mForwarding = getter_AddRefs( NS_GetWeakReference(aForwardingAddress) );
return NS_OK;
}

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

@ -20,24 +20,36 @@
#define AggregatePlaceholderTxn_h__
#include "EditAggregateTxn.h"
#include "nsIAbsorbingTransaction.h"
#include "nsIDOMNode.h"
#include "nsCOMPtr.h"
#include "nsWeakPtr.h"
#include "nsWeakReference.h"
#define PLACEHOLDER_TXN_CID \
{/* {0CE9FB00-D9D1-11d2-86DE-000064657374} */ \
0x0CE9FB00, 0xD9D1, 0x11d2, \
{0x86, 0xde, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
class nsHTMLEditor;
/**
* 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
class PlaceholderTxn : public EditAggregateTxn,
public nsIAbsorbingTransaction,
public nsSupportsWeakReference
{
public:
static const nsIID& GetCID() { static nsIID iid = PLACEHOLDER_TXN_CID; return iid; }
NS_DECL_ISUPPORTS_INHERITED
private:
PlaceholderTxn();
@ -45,11 +57,25 @@ public:
virtual ~PlaceholderTxn();
// ------------ EditAggregateTxn -----------------------
NS_IMETHOD Do(void);
NS_IMETHOD Undo(void);
NS_IMETHOD Merge(PRBool *aDidMerge, nsITransaction *aTransaction);
NS_IMETHOD SetAbsorb(PRBool aAbsorb);
// ------------ nsIAbsorbingTransaction -----------------------
NS_IMETHOD Init(nsWeakPtr aPresShellWeak, nsIAtom *aName, nsIDOMNode *aStartNode, PRInt32 aStartOffset);
NS_IMETHOD GetTxnName(nsIAtom **aName);
NS_IMETHOD GetStartNodeAndOffset(nsCOMPtr<nsIDOMNode> *aTxnStartNode, PRInt32 *aTxnStartOffset);
NS_IMETHOD EndPlaceHolderBatch();
NS_IMETHOD ForwardEndBatchTo(nsIAbsorbingTransaction *aForwardingAddress);
friend class TransactionFactory;
@ -57,14 +83,12 @@ public:
protected:
PRBool mAbsorb;
};
inline NS_IMETHODIMP PlaceholderTxn::SetAbsorb(PRBool aAbsorb)
{
mAbsorb = aAbsorb;
return NS_OK;
/** the presentation shell, which we'll need to get the selection */
nsWeakPtr mPresShellWeak; // weak reference to the nsIPresShell
PRBool mAbsorb;
nsCOMPtr<nsIDOMNode> mStartNode, mEndNode; // selection nodes at beginning and end of operation
PRInt32 mStartOffset, mEndOffset; // selection offsets at beginning and end of operation
nsWeakPtr mForwarding;
};

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

@ -36,6 +36,7 @@
#include "nsIServiceManager.h"
#include "nsTransactionManagerCID.h"
#include "nsITransactionManager.h"
#include "nsIAbsorbingTransaction.h"
#include "nsIPresShell.h"
#include "nsIPresContext.h"
#include "nsIViewManager.h"
@ -54,6 +55,7 @@
#include "nsIDocumentObserver.h"
#include "nsIDocumentStateListener.h"
#include "nsIStringStream.h"
#include "nsITextContent.h"
#ifdef NECKO
#include "nsNeckoUtil.h"
@ -68,6 +70,7 @@
// transactions the editor knows how to build
#include "TransactionFactory.h"
#include "EditAggregateTxn.h"
#include "PlaceholderTxn.h"
#include "ChangeAttributeTxn.h"
#include "CreateElementTxn.h"
#include "InsertElementTxn.h"
@ -85,6 +88,7 @@
#include "nsEditorCID.h"
#include "nsEditor.h"
#include "nsEditorUtils.h"
#ifdef HACK_FORCE_REDRAW
@ -143,6 +147,12 @@ nsEditor::nsEditor()
, mActionListeners(nsnull)
, mDocDirtyState(-1)
, mDocWeak(nsnull)
, mPlaceHolderTxn(nsnull)
, mPlaceHolderName(nsnull)
, mPlaceHolderBatch(0)
, mTxnStartNode(nsnull)
, mTxnStartOffset(0)
{
//initialize member variables here
NS_INIT_REFCNT();
@ -332,6 +342,31 @@ nsEditor::Do(nsITransaction *aTxn)
{
if (gNoisy) { printf("Editor::Do ----------\n"); }
nsresult result = NS_OK;
if (mPlaceHolderBatch && !mPlaceHolderTxn)
{
// it's pretty darn amazing how many different types of pointers
// this transcation goes through here. I bet this is a record.
EditTxn *editTxn;
nsCOMPtr<nsIAbsorbingTransaction> plcTxn;
result = TransactionFactory::GetNewTransaction(PlaceholderTxn::GetCID(), &editTxn);
if (NS_FAILED(result)) { return result; }
if (!editTxn) { return NS_ERROR_NULL_POINTER; }
editTxn->QueryInterface(nsIAbsorbingTransaction::GetIID(), getter_AddRefs(plcTxn));
// have to use line above instead of line below due to our broken
// interface model for transactions.
// plcTxn = do_QueryInterface(editTxn);
// save off weak reference to placeholder txn
mPlaceHolderTxn = getter_AddRefs( NS_GetWeakReference(plcTxn) );
plcTxn->Init(mPresShellWeak, mPlaceHolderName, mTxnStartNode, mTxnStartOffset);
// we will recurse, but will not hit this case in the nested call
nsCOMPtr<nsITransaction> theTxn = do_QueryInterface(plcTxn);
nsITransaction* txn = theTxn;
// we want to escape from this routine with a positive refcount
txn->AddRef();
Do(txn);
}
nsCOMPtr<nsIDOMSelection>selection;
nsresult selectionResult = GetSelection(getter_AddRefs(selection));
if (NS_SUCCEEDED(selectionResult) && selection) {
@ -504,6 +539,72 @@ nsEditor::EndTransaction()
return NS_OK;
}
// These two routines are similar to the above, but do not use
// the transaction managers batching feature. Instead we use
// a placeholder transaction to wrap up any further transaction
// while the batch is open. The advantage of this is that
// placeholder transactions can later merge, if needed. Merging
// is unavailable between transaction manager batches.
NS_IMETHODIMP
nsEditor::BeginPlaceHolderTransaction(nsIAtom *aName)
{
NS_PRECONDITION(mPlaceHolderBatch >= 0, "negative placeholder batch count!");
if (!mPlaceHolderBatch)
{
// time to turn on the batch
BeginUpdateViewBatch();
mPlaceHolderTxn = nsnull;
mPlaceHolderName = aName;
nsCOMPtr<nsIDOMSelection> selection;
nsresult res = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
PRBool collapsed;
res = selection->GetIsCollapsed(&collapsed);
if (NS_FAILED(res)) return res;
if (collapsed) // we cant merge with previous typing if selection not collapsed
{
// need to remember colapsed selection point to Init the placeholder with later.
// this is because we dont actually make the placeholder until we need it, and we
// might have moved the selection as part of the typing processing by then.
res = GetStartNodeAndOffset(selection, &mTxnStartNode, &mTxnStartOffset);
if (NS_FAILED(res)) return res;
}
}
mPlaceHolderBatch++;
return NS_OK;
}
NS_IMETHODIMP
nsEditor::EndPlaceHolderTransaction()
{
NS_PRECONDITION(mPlaceHolderBatch > 0, "zero or negative placeholder batch count when ending batch!");
if (mPlaceHolderBatch == 1)
{
// time to turn off the batch
EndUpdateViewBatch();
mTxnStartNode = nsnull;
mTxnStartOffset = 0;
if (mPlaceHolderTxn) // we might have never made a placeholder if no action took place
{
nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryReferent(mPlaceHolderTxn);
if (plcTxn)
{
plcTxn->EndPlaceHolderBatch();
}
else // but if we did make one it should be around
{
NS_NOTREACHED("should this ever happen?");
}
}
}
mPlaceHolderBatch--;
return NS_OK;
}
// XXX: the rule system should tell us which node to select all on (ie, the root, or the body)
NS_IMETHODIMP nsEditor::SelectAll()
{
@ -1489,8 +1590,9 @@ NS_IMETHODIMP nsEditor::InsertTextImpl(const nsString& aStringToInsert)
result = CreateTxnForInsertText(aStringToInsert, nsnull, &txn); // insert at the current selection
if ((NS_SUCCEEDED(result)) && txn) {
BeginUpdateViewBatch();
aggTxn->AppendChild(txn);
// aggTxn->AppendChild(txn);
result = Do(aggTxn);
result = Do(txn);
EndUpdateViewBatch();
}
else if (NS_ERROR_EDITOR_NO_SELECTION==result) {
@ -1769,7 +1871,7 @@ NS_IMETHODIMP nsEditor::CreateTxnForInsertText(const nsString & aStringToInsert,
// This may be OK, now that I fixed InsertText so it inserts
// at the offset of the selection anchor (i.e., the caret offset)
// instead of at offset+1
PRBool collapsed
PRBool collapsed;
result = selection->GetIsCollapsed(&collapsed);
if (NS_SUCCEEDED(result) && collapsed)
{
@ -2559,8 +2661,8 @@ nsEditor::GetPriorNode(nsIDOMNode *aParentNode,
nsresult result = NS_OK;
if (!aParentNode || !aResultNode) { return NS_ERROR_NULL_POINTER; }
// if we are at beginning of node than just look before it
if (!aOffset)
// if we are at beginning of node, or it is a textnode, then just look before it
if (!aOffset || IsTextNode(aParentNode))
{
return GetPriorNode(aParentNode, aEditableNode, aResultNode);
}
@ -2601,6 +2703,14 @@ nsEditor::GetNextNode(nsIDOMNode *aParentNode,
nsresult result = NS_OK;
if (!aParentNode || !aResultNode) { return NS_ERROR_NULL_POINTER; }
// if aParentNode is a text node, use it's location instead
if (IsTextNode(aParentNode))
{
nsCOMPtr<nsIDOMNode> parent;
nsEditor::GetNodeLocation(aParentNode, &parent, &aOffset);
aParentNode = parent;
aOffset++; // _after_ the text node
}
// look at the child at 'aOffset'
nsCOMPtr<nsIDOMNode> child = GetChildAt(aParentNode, aOffset);
if (child)
@ -2828,6 +2938,18 @@ nsEditor::CanContainTag(nsIDOMNode* aParent, const nsString &aTag)
return mDTD->CanContain(parentTagEnum, childTagEnum);
}
PRBool
nsEditor::IsContainer(nsIDOMNode *aNode)
{
if (!aNode) return PR_FALSE;
nsAutoString stringTag;
PRInt32 tagEnum;
nsresult res = aNode->GetNodeName(stringTag);
if (NS_FAILED(res)) return PR_FALSE;
res = mDTD->StringTagToIntTag(stringTag,&tagEnum);
if (NS_FAILED(res)) return PR_FALSE;
return mDTD->IsContainer(tagEnum);
}
PRBool
nsEditor::IsEditable(nsIDOMNode *aNode)
@ -2885,13 +3007,27 @@ nsEditor::IsEditable(nsIDOMNode *aNode)
if (NS_FAILED(result) || !resultFrame) { // if it has no frame, it is not editable
return PR_FALSE;
}
else { // it has a frame, so it is editable
else {
// it has a frame, so it might editable
// but not if it's a formatting whitespace node
if (IsEmptyTextContent(content)) return PR_FALSE;
return PR_TRUE;
}
}
return PR_FALSE; // it's not a content object (???) so it's not editable
}
PRBool
nsEditor::IsEmptyTextContent(nsIContent* aContent)
{
PRBool result = PR_FALSE;
nsCOMPtr<nsITextContent> tc(do_QueryInterface(aContent));
if (tc) {
tc->IsOnlyWhitespace(&result);
}
return result;
}
nsresult
nsEditor::CountEditableChildren(nsIDOMNode *aNode, PRUint32 &outCount)
{
@ -3108,7 +3244,7 @@ nsEditor::SetInputMethodText(const nsString& aStringToInsert, nsIPrivateTextRang
}
else if (NS_ERROR_EDITOR_NO_TEXTNODE==result)
{
BeginTransaction();
nsAutoEditBatch batchIt (this);
nsCOMPtr<nsIDOMSelection> selection;
result = GetSelection(getter_AddRefs(selection));
if ((NS_SUCCEEDED(result)) && selection)
@ -3138,8 +3274,6 @@ nsEditor::SetInputMethodText(const nsString& aStringToInsert, nsIPrivateTextRang
}
}
}
EndTransaction();
}
return result;

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

@ -121,6 +121,9 @@ public:
NS_IMETHOD BeginTransaction();
NS_IMETHOD EndTransaction();
NS_IMETHOD BeginPlaceHolderTransaction(nsIAtom *aName);
NS_IMETHOD EndPlaceHolderTransaction();
// pure virtual, because the definition of 'empty' depends on the doc type
NS_IMETHOD GetDocumentIsEmpty(PRBool *aDocumentIsEmpty)=0;
@ -553,11 +556,17 @@ public:
static PRBool NodeIsType(nsIDOMNode *aNode, nsIAtom *aTag);
/** returns PR_TRUE if aParent can contain a child of type aTag */
PRBool CanContainTag(nsIDOMNode* aParent, const nsString &aTag);
PRBool CanContainTag(nsIDOMNode* aParent, const nsString &aTag);
/** returns PR_TRUE if aNode is a container */
PRBool IsContainer(nsIDOMNode *aNode);
/** returns PR_TRUE if aNode is an editable node */
PRBool IsEditable(nsIDOMNode *aNode);
/** returns PR_TRUE if content is an merely formatting whitespacce */
PRBool IsEmptyTextContent(nsIContent* aContent);
/** counts number of editable child nodes */
nsresult CountEditableChildren(nsIDOMNode *aNode, PRUint32 &outCount);
@ -608,6 +617,11 @@ protected:
nsCOMPtr<nsITransactionManager> mTxnMgr;
nsCOMPtr<nsIEditProperty> mEditProperty;
nsCOMPtr<nsICSSStyleSheet> mLastStyleSheet; // is owning this dangerous?
nsWeakPtr mPlaceHolderTxn;
nsIAtom *mPlaceHolderName;
PRInt32 mPlaceHolderBatch;
nsCOMPtr<nsIDOMNode> mTxnStartNode;
PRInt32 mTxnStartOffset;
//
// data necessary to build IME transactions

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

@ -25,6 +25,7 @@
#include "nsIDOMNode.h"
#include "nsIDOMSelection.h"
#include "nsIEditor.h"
#include "nsIAtom.h"
class nsAutoEditBatch
{
@ -43,11 +44,21 @@ class nsAutoEditMayBatch
PRBool mDidBatch;
public:
nsAutoEditMayBatch( nsIEditor *aEd) : mEd(do_QueryInterface(aEd)), mDidBatch(PR_FALSE) {}
nsAutoEditMayBatch() { if (mEd && mDidBatch) mEd->EndTransaction(); }
~nsAutoEditMayBatch() { if (mEd && mDidBatch) mEd->EndTransaction(); }
void batch() { if (mEd && !mDidBatch) {mEd->BeginTransaction(); mDidBatch=PR_TRUE;} }
};
class nsAutoPlaceHolderBatch
{
private:
nsCOMPtr<nsIEditor> mEd;
public:
nsAutoPlaceHolderBatch( nsIEditor *aEd, nsIAtom *atom) : mEd(do_QueryInterface(aEd))
{ if (mEd) mEd->BeginPlaceHolderTransaction(atom); }
~nsAutoPlaceHolderBatch() { if (mEd) mEd->EndPlaceHolderTransaction(); }
};
class nsAutoSelectionReset
{

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

@ -21,9 +21,6 @@
#include "nsEditor.h"
#include "nsHTMLEditor.h"
#include "PlaceholderTxn.h"
#include "InsertTextTxn.h"
#include "nsIContent.h"
#include "nsIContentIterator.h"
#include "nsIDOMNode.h"
@ -90,7 +87,6 @@ nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection,
case kInsertText:
return WillInsertText(aSelection,
aCancel,
info->placeTxn,
info->inString,
info->outString,
info->typeInState,
@ -127,11 +123,13 @@ nsHTMLEditRules::DidDoAction(nsIDOMSelection *aSelection,
switch (info->action)
{
case kInsertBreak:
return NS_OK;
case kUndo:
return nsTextEditRules::DidDoAction(aSelection, aInfo, aResult);
case kRedo:
return nsTextEditRules::DidDoAction(aSelection, aInfo, aResult);
}
// clean up any empty nodes in the selection
// other than undo and redo, clean up any empty nodes in the selection
CleanUpSelection(aSelection);
return nsTextEditRules::DidDoAction(aSelection, aInfo, aResult);
@ -145,7 +143,6 @@ nsHTMLEditRules::DidDoAction(nsIDOMSelection *aSelection,
nsresult
nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
PRBool *aCancel,
PlaceholderTxn **aTxn,
const nsString *inString,
nsString *outString,
TypeInState typeInState,
@ -155,7 +152,8 @@ nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
// initialize out param
*aCancel = PR_TRUE;
nsresult res;
nsAutoEditMayBatch optionalBatch;
nsCOMPtr<nsIDOMNode> selNode;
PRInt32 selOffset;
char specialChars[] = {'\t',' ',nbsp,'\n',0};
@ -169,20 +167,27 @@ nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
if (NS_FAILED(res)) return res;
}
// get the (collapsed) selection location
res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset);
if (NS_FAILED(res)) return res;
res = WillInsert(aSelection, aCancel);
if (NS_FAILED(res)) return res;
// initialize out param
// we want to ignore result of WillInsert()
*aCancel = PR_TRUE;
// split any mailcites in the way
if (1 || mFlags & nsIHTMLEditor::eEditorMailMask)
if (mFlags & nsIHTMLEditor::eEditorMailMask)
{
nsCOMPtr<nsIDOMNode> citeNode, selNode;
PRInt32 selOffset, newOffset;
res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset);
if (NS_FAILED(res)) return res;
nsCOMPtr<nsIDOMNode> citeNode;
PRInt32 newOffset;
res = GetTopEnclosingMailCite(selNode, &citeNode);
if (NS_FAILED(res)) return res;
if (citeNode)
{
// turn batching on
optionalBatch.batch();
res = mEditor->SplitNodeDeep(citeNode, selNode, selOffset, &newOffset);
if (NS_FAILED(res)) return res;
res = citeNode->GetParentNode(getter_AddRefs(selNode));
@ -192,9 +197,73 @@ nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
}
}
// identify the block
nsCOMPtr<nsIDOMNode> blockParent;
if (nsEditor::IsBlockNode(selNode))
blockParent = selNode;
else
blockParent = mEditor->GetBlockNodeParent(selNode);
if (!blockParent) return NS_ERROR_FAILURE;
// are we not in a textnode?
if (!mEditor->IsTextNode(selNode))
{
// find a nearby text node if possible
nsCOMPtr<nsIDOMNode> priorNode, nextNode;
res = GetPriorHTMLNode(selNode, selOffset, &priorNode);
if (NS_SUCCEEDED(res) && priorNode && mEditor->IsTextNode(priorNode)
&& (blockParent == mEditor->GetBlockNodeParent(priorNode)))
{
// put selection at end of prior node
PRUint32 strLength;
nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(priorNode);
res = textNode->GetLength(&strLength);
if (NS_FAILED(res)) return res;
res = aSelection->Collapse(priorNode, strLength);
if (NS_FAILED(res)) return res;
}
else
{
res = GetNextHTMLNode(selNode, selOffset, &nextNode);
if (NS_SUCCEEDED(res) && nextNode && mEditor->IsTextNode(nextNode)
&& (blockParent == mEditor->GetBlockNodeParent(nextNode)))
{
// put selection at begining of next node
res = aSelection->Collapse(nextNode, 0);
if (NS_FAILED(res)) return res;
}
}
// if we are right after a moz br, delete it and make a new moz div
PRBool needMozDiv = PR_FALSE;
if (priorNode && IsBreak(priorNode) && HasMozAttr(priorNode)
&& (blockParent == mEditor->GetBlockNodeParent(priorNode)))
{
needMozDiv = PR_TRUE;
res = mEditor->DeleteNode(priorNode);
if (NS_FAILED(res)) return res;
}
// if we are directly in a body or (non-moz) div, create a moz-div.
// Also creat one if we detected a prior moz br (see above).
res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset);
if (NS_FAILED(res)) return res;
if (needMozDiv || IsBody(selNode) || IsNormalDiv(selNode))
{
// wrap things up in a moz-div
nsCOMPtr<nsIDOMNode> mozDiv;
res = CreateMozDiv(selNode, selOffset, &mozDiv);
if (NS_FAILED(res)) return res;
// put selection in it
res = aSelection->Collapse(mozDiv, 0);
if (NS_FAILED(res)) return res;
}
}
char nbspStr[2] = {nbsp, 0};
nsString theString(*inString); // copy instr for now
nsString theString(*inString); // copy instring for now
PRInt32 pos = theString.FindCharInSet(specialChars);
while (theString.Length())
{
@ -208,23 +277,23 @@ nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
// is it a solo tab?
if (partialString == "\t" )
{
res = InsertTab(aSelection,&bCancel,aTxn,outString);
res = InsertTab(aSelection,&bCancel,outString);
if (NS_FAILED(res)) return res;
res = DoTextInsertion(aSelection, aCancel, aTxn, outString, typeInState);
res = DoTextInsertion(aSelection, aCancel, outString, typeInState);
}
// is it a solo space?
else if (partialString == " ")
{
res = InsertSpace(aSelection,&bCancel,aTxn,outString);
res = InsertSpace(aSelection,&bCancel,outString);
if (NS_FAILED(res)) return res;
res = DoTextInsertion(aSelection, aCancel, aTxn, outString, typeInState);
res = DoTextInsertion(aSelection, aCancel, outString, typeInState);
}
// is it a solo nbsp?
else if (partialString == nbspStr)
{
res = InsertSpace(aSelection,&bCancel,aTxn,outString);
res = InsertSpace(aSelection,&bCancel,outString);
if (NS_FAILED(res)) return res;
res = DoTextInsertion(aSelection, aCancel, aTxn, outString, typeInState);
res = DoTextInsertion(aSelection, aCancel, outString, typeInState);
}
// is it a solo return?
else if (partialString == "\n")
@ -233,7 +302,7 @@ nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
}
else
{
res = DoTextInsertion(aSelection, aCancel, aTxn, &partialString, typeInState);
res = DoTextInsertion(aSelection, aCancel, &partialString, typeInState);
}
if (NS_FAILED(res)) return res;
pos = theString.FindCharInSet(specialChars);
@ -250,7 +319,6 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel)
*aCancel = PR_FALSE;
nsresult res;
nsAutoEditMayBatch optionalBatch;
res = WillInsert(aSelection, aCancel);
if (NS_FAILED(res)) return res;
@ -269,7 +337,7 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel)
}
// split any mailcites in the way
if (1 || mFlags & nsIHTMLEditor::eEditorMailMask)
if (mFlags & nsIHTMLEditor::eEditorMailMask)
{
nsCOMPtr<nsIDOMNode> citeNode, selNode;
PRInt32 selOffset, newOffset;
@ -280,8 +348,6 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel)
if (citeNode)
{
// turn batching on
optionalBatch.batch();
res = mEditor->SplitNodeDeep(citeNode, selNode, selOffset, &newOffset);
if (NS_FAILED(res)) return res;
res = citeNode->GetParentNode(getter_AddRefs(selNode));
@ -310,6 +376,7 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel)
return mEditor->InsertTextImpl(theString);
}
// identify the block
nsCOMPtr<nsIDOMNode> blockParent;
if (nsEditor::IsBlockNode(node))
@ -319,8 +386,67 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel)
if (!blockParent) return NS_ERROR_FAILURE;
// break action depends on type of block
PRBool bIsMozDiv = IsMozDiv(blockParent);
PRBool bIsNormalDiv = IsNormalDiv(blockParent);
// body, div, or mozdiv: insert a moz br
if (IsBody(blockParent) || bIsNormalDiv ||
(bIsMozDiv && AtEndOfBlock(node, offset, blockParent)))
{
if (bIsMozDiv)
{
// put selection beyond the mozdiv first
nsCOMPtr<nsIDOMNode> parent;
PRInt32 pOffset;
res = nsEditor::GetNodeLocation(blockParent, &parent, &pOffset);
if (NS_FAILED(res)) return res;
if (!parent) return NS_ERROR_FAILURE;
res = aSelection->Collapse(parent, pOffset+1);
if (NS_FAILED(res)) return res;
}
nsCOMPtr<nsIDOMNode> brNode;
res = mEditor->InsertBR(&brNode); // only inserts a br node
if (NS_FAILED(res)) return res;
// give it special moz attr
nsCOMPtr<nsIDOMElement> brElem = do_QueryInterface(brNode);
if (brElem)
{
res = mEditor->SetAttribute(brElem, "type", "_moz");
if (NS_FAILED(res)) return res;
}
*aCancel = PR_TRUE;
}
else if (bIsMozDiv && AtStartOfBlock(node, offset, blockParent))
{
// position selection in front of mozdiv, and let default code
// make a normal br
nsCOMPtr<nsIDOMNode> parent;
PRInt32 pOffset;
res = nsEditor::GetNodeLocation(blockParent, &parent, &pOffset);
if (NS_FAILED(res)) return res;
if (!parent) return NS_ERROR_FAILURE;
res = aSelection->Collapse(parent, pOffset);
if (NS_FAILED(res)) return res;
}
else if (bIsMozDiv)
{
// we are in the middle of the mozdiv: split it
PRInt32 newOffset;
res = mEditor->SplitNodeDeep( blockParent, node, offset, &newOffset);
if (NS_FAILED(res)) return res;
// put selection at beginning of new mozdiv
nsCOMPtr<nsIDOMNode> parent;
blockParent->GetParentNode(getter_AddRefs(parent));
nsCOMPtr <nsIDOMNode> newDiv = nsEditor::GetChildAt(parent, newOffset);
if (!newDiv || !IsMozDiv(newDiv)) return NS_ERROR_FAILURE;
res = aSelection->Collapse(newDiv, 0);
if (NS_FAILED(res)) return res;
*aCancel = PR_TRUE;
}
// headers: close (or split) header
if (IsHeader(blockParent))
else if (IsHeader(blockParent))
{
res = ReturnInHeader(aSelection, blockParent, node, offset);
*aCancel = PR_TRUE;
@ -328,14 +454,14 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel)
}
// paragraphs: special rules to look for <br>s
if (IsParagraph(blockParent))
else if (IsParagraph(blockParent))
{
res = ReturnInParagraph(aSelection, blockParent, node, offset, aCancel);
return NS_OK;
}
// list items: special rules to make new list items
if (IsListItem(blockParent))
else if (IsListItem(blockParent))
{
res = ReturnInListItem(aSelection, blockParent, node, offset);
*aCancel = PR_TRUE;
@ -411,10 +537,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESe
if (!leftParent || !rightParent)
return NS_OK; // bail to default
nsCOMPtr<nsIAtom> leftAtom = mEditor->GetTag(leftParent);
nsCOMPtr<nsIAtom> rightAtom = mEditor->GetTag(rightParent);
if (leftAtom.get() == rightAtom.get())
if (mEditor->NodesSameType(leftParent, rightParent))
{
nsCOMPtr<nsIDOMNode> topParent;
leftParent->GetParentNode(getter_AddRefs(topParent));
@ -454,10 +577,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESe
// are the blocks of same type?
nsCOMPtr<nsIDOMNode> leftParent = mEditor->GetBlockNodeParent(node);
nsCOMPtr<nsIDOMNode> rightParent = mEditor->GetBlockNodeParent(nextNode);
nsCOMPtr<nsIAtom> leftAtom = mEditor->GetTag(leftParent);
nsCOMPtr<nsIAtom> rightAtom = mEditor->GetTag(rightParent);
if (leftAtom.get() == rightAtom.get())
if (mEditor->NodesSameType(leftParent, rightParent))
{
nsCOMPtr<nsIDOMNode> topParent;
leftParent->GetParentNode(getter_AddRefs(topParent));
@ -561,10 +681,7 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESe
// bail to default if blocks aren't siblings
if (leftBlockParent.get() != rightBlockParent.get()) return NS_OK;
nsCOMPtr<nsIAtom> leftAtom = mEditor->GetTag(leftParent);
nsCOMPtr<nsIAtom> rightAtom = mEditor->GetTag(rightParent);
if (leftAtom.get() == rightAtom.get())
if (mEditor->NodesSameType(leftParent, rightParent))
{
nsCOMPtr<nsIDOMNode> topParent;
leftParent->GetParentNode(getter_AddRefs(topParent));
@ -810,8 +927,15 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo
}
else
{
nsAutoString listItemType = "li";
res = InsertContainerAbove(curNode, &listItem, listItemType);
// don't wrap li around a moz-div. instead replace moz-div with li
if (IsMozDiv(curNode))
{
res = ReplaceContainer(curNode, &listItem, "li");
}
else
{
res = InsertContainerAbove(curNode, &listItem, "li");
}
if (NS_FAILED(res)) return res;
if (nsEditor::IsInlineNode(curNode))
prevListItem = listItem;
@ -1291,6 +1415,7 @@ nsHTMLEditRules::IsHeader(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsHeader");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if ( (tag == "h1") ||
(tag == "h2") ||
(tag == "h3") ||
@ -1313,6 +1438,7 @@ nsHTMLEditRules::IsParagraph(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsParagraph");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag == "p")
{
return PR_TRUE;
@ -1330,6 +1456,7 @@ nsHTMLEditRules::IsListItem(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsListItem");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag == "li")
{
return PR_TRUE;
@ -1347,6 +1474,7 @@ nsHTMLEditRules::IsList(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsList");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if ( (tag == "ol") ||
(tag == "ul") )
{
@ -1365,6 +1493,7 @@ nsHTMLEditRules::IsOrderedList(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsOrderedList");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag == "ol")
{
return PR_TRUE;
@ -1382,6 +1511,7 @@ nsHTMLEditRules::IsUnorderedList(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsUnorderedList");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag == "ul")
{
return PR_TRUE;
@ -1399,6 +1529,7 @@ nsHTMLEditRules::IsBreak(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsBreak");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag == "br")
{
return PR_TRUE;
@ -1416,6 +1547,7 @@ nsHTMLEditRules::IsBody(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsBody");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag == "body")
{
return PR_TRUE;
@ -1433,6 +1565,7 @@ nsHTMLEditRules::IsBlockquote(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsBlockquote");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag == "blockquote")
{
return PR_TRUE;
@ -1441,6 +1574,24 @@ nsHTMLEditRules::IsBlockquote(nsIDOMNode *node)
}
///////////////////////////////////////////////////////////////////////////
// IsAnchor: true if node an html anchor node
//
PRBool
nsHTMLEditRules::IsAnchor(nsIDOMNode *node)
{
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsAnchor");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag == "a")
{
return PR_TRUE;
}
return PR_FALSE;
}
///////////////////////////////////////////////////////////////////////////
// IsDiv: true if node an html div node
//
@ -1450,6 +1601,7 @@ nsHTMLEditRules::IsDiv(nsIDOMNode *node)
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsDiv");
nsAutoString tag;
nsEditor::GetTagString(node,tag);
tag.ToLowerCase();
if (tag == "div")
{
return PR_TRUE;
@ -1458,6 +1610,51 @@ nsHTMLEditRules::IsDiv(nsIDOMNode *node)
}
///////////////////////////////////////////////////////////////////////////
// IsNormalDiv: true if node an html div node, without type = _moz
//
PRBool
nsHTMLEditRules::IsNormalDiv(nsIDOMNode *node)
{
if (IsDiv(node) && !HasMozAttr(node)) return PR_TRUE;
return PR_FALSE;
}
///////////////////////////////////////////////////////////////////////////
// IsMozDiv: true if node an html div node with type = _moz
//
PRBool
nsHTMLEditRules::IsMozDiv(nsIDOMNode *node)
{
if (IsDiv(node) && HasMozAttr(node)) return PR_TRUE;
return PR_FALSE;
}
///////////////////////////////////////////////////////////////////////////
// HasMozAttr: true if node has type attribute = _moz
// (used to indicate the div's and br's we use in
// mail compose rules)
//
PRBool
nsHTMLEditRules::HasMozAttr(nsIDOMNode *node)
{
NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::HasMozAttr");
nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(node);
if (elem)
{
nsAutoString typeAttrName("type");
nsAutoString typeAttrVal;
nsresult res = elem->GetAttribute(typeAttrName, typeAttrVal);
typeAttrVal.ToLowerCase();
if (NS_SUCCEEDED(res) && (typeAttrVal == "_moz"))
return PR_TRUE;
}
return PR_FALSE;
}
///////////////////////////////////////////////////////////////////////////
// IsMailCite: true if node an html blockquote with type=cite
//
@ -1471,6 +1668,7 @@ nsHTMLEditRules::IsMailCite(nsIDOMNode *node)
nsAutoString typeAttrName("type");
nsAutoString typeAttrVal;
nsresult res = bqElem->GetAttribute(typeAttrName, typeAttrVal);
typeAttrVal.ToLowerCase();
if (NS_SUCCEEDED(res))
{
if (typeAttrVal == "cite")
@ -1544,6 +1742,17 @@ nsHTMLEditRules::IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyNode)
return NS_OK;
}
// if it's not a text node (handled above) and it's not a container,
// then we dont call it empty (it's an <hr>, or <br>, etc).
// Also, if it's an anchor then dont treat it as empty - even though
// anchors are containers, named anchors are "empty" but we don't
// want to treat them as such.
if (!mEditor->IsContainer(aNode) || IsAnchor(aNode))
{
*outIsEmptyNode = PR_FALSE;
return NS_OK;
}
// iterate over node. if no children, or all children are either
// empty text nodes or non-editable, then node qualifies as empty
nsCOMPtr<nsIContentIterator> iter;
@ -1593,6 +1802,24 @@ nsHTMLEditRules::IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyNode)
}
///////////////////////////////////////////////////////////////////////////
// CreateMozDiv: makes a div with type = _moz
//
nsresult
nsHTMLEditRules::CreateMozDiv(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outDiv)
{
if (!inParent || !outDiv) return NS_ERROR_NULL_POINTER;
nsAutoString divType= "div";
*outDiv = nsnull;
nsresult res = mEditor->CreateNode(divType, inParent, inOffset, getter_AddRefs(*outDiv));
if (NS_FAILED(res)) return res;
// give it special moz attr
nsCOMPtr<nsIDOMElement> mozDivElem = do_QueryInterface(*outDiv);
res = mEditor->SetAttribute(mozDivElem, "type", "_moz");
return res;
}
///////////////////////////////////////////////////////////////////////////
// GetPriorHTMLNode: returns the previous editable leaf node, if there is
// one within the <body>
@ -1757,6 +1984,48 @@ nsHTMLEditRules::IsLastNode(nsIDOMNode *aNode)
}
///////////////////////////////////////////////////////////////////////////
// AtStartOfBlock: is node/offset at the start of the editable material in this block?
//
PRBool
nsHTMLEditRules::AtStartOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock)
{
nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(aNode);
if (nodeAsText && aOffset) return PR_FALSE; // there are chars in front of us
nsCOMPtr<nsIDOMNode> priorNode;
nsresult res = GetPriorHTMLNode(aNode, aOffset, &priorNode);
if (NS_FAILED(res)) return PR_TRUE;
if (!priorNode) return PR_TRUE;
nsCOMPtr<nsIDOMNode> blockParent = mEditor->GetBlockNodeParent(priorNode);
if (blockParent && (blockParent.get() == aBlock)) return PR_FALSE;
return PR_TRUE;
}
///////////////////////////////////////////////////////////////////////////
// AtEndOfBlock: is node/offset at the end of the editable material in this block?
//
PRBool
nsHTMLEditRules::AtEndOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock)
{
nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(aNode);
if (nodeAsText)
{
PRUint32 strLength;
nodeAsText->GetLength(&strLength);
if (strLength > aOffset) return PR_FALSE; // there are chars in after us
}
nsCOMPtr<nsIDOMNode> nextNode;
nsresult res = GetNextHTMLNode(aNode, aOffset, &nextNode);
if (NS_FAILED(res)) return PR_TRUE;
if (!nextNode) return PR_TRUE;
nsCOMPtr<nsIDOMNode> blockParent = mEditor->GetBlockNodeParent(nextNode);
if (blockParent && (blockParent.get() == aBlock)) return PR_FALSE;
return PR_TRUE;
}
///////////////////////////////////////////////////////////////////////////
// GetPromotedPoint: figure out where a start or end point for a block
// operation really is
@ -2171,7 +2440,7 @@ nsHTMLEditRules::RemoveContainer(nsIDOMNode *inNode)
nsresult
nsHTMLEditRules::InsertContainerAbove(nsIDOMNode *inNode,
nsCOMPtr<nsIDOMNode> *outNode,
nsString &aNodeType)
const nsString &aNodeType)
{
if (!inNode || !outNode)
return NS_ERROR_NULL_POINTER;
@ -2207,7 +2476,6 @@ nsHTMLEditRules::InsertContainerAbove(nsIDOMNode *inNode,
nsresult
nsHTMLEditRules::InsertTab(nsIDOMSelection *aSelection,
PRBool *aCancel,
PlaceholderTxn **aTxn,
nsString *outString)
{
nsCOMPtr<nsIDOMNode> parentNode;
@ -2271,7 +2539,6 @@ nsHTMLEditRules::InsertTab(nsIDOMSelection *aSelection,
nsresult
nsHTMLEditRules::InsertSpace(nsIDOMSelection *aSelection,
PRBool *aCancel,
PlaceholderTxn **aTxn,
nsString *outString)
{
nsCOMPtr<nsIDOMNode> parentNode;
@ -2351,10 +2618,6 @@ nsHTMLEditRules::ReturnInHeader(nsIDOMSelection *aSelection,
res = mEditor->SplitNodeDeep( aHeader, aNode, aOffset, &newOffset);
if (NS_FAILED(res)) return res;
// revisit the below when we move to using divs as standard paragraphs.
// need to create an "empty" div in the fisrt case below, and need to
// use a div instead of a <p> in the second case below
// if the new (righthand) header node is empty, delete it
PRBool isEmpty;
res = IsEmptyBlock(aHeader, &isEmpty);
@ -2366,10 +2629,13 @@ nsHTMLEditRules::ReturnInHeader(nsIDOMSelection *aSelection,
res = aSelection->Collapse(headerParent,offset+1);
return res;
}
// else rewrap it in a paragraph
// else rewrap it in a mozdiv
nsCOMPtr<nsIDOMNode> newBlock;
nsAutoString blockType("p");
res = ReplaceContainer(aHeader,&newBlock,blockType);
res = ReplaceContainer(aHeader,&newBlock,"div");
if (NS_FAILED(res)) return res;
// give it special moz attr
nsCOMPtr<nsIDOMElement> mozDivElem = do_QueryInterface(newBlock);
res = mEditor->SetAttribute(mozDivElem, "type", "_moz");
if (NS_FAILED(res)) return res;
res = aSelection->Collapse(newBlock,0);
return res;
@ -2685,10 +2951,11 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString
// to breaks, and runs of spaces to nbsps.
// xxx floppy moose
// if curNode is a p, header, address, or pre, replace
// if curNode is a mozdiv, p, header, address, or pre, replace
// it with a new block of correct type.
// xxx floppy moose: pre cant hold everything the others can
if ((curNodeTag == "pre") ||
if (IsMozDiv(curNode) ||
(curNodeTag == "pre") ||
(curNodeTag == "p") ||
(curNodeTag == "h1") ||
(curNodeTag == "h2") ||
@ -2710,7 +2977,7 @@ nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString
(curNodeTag == "ul") ||
(curNodeTag == "li") ||
(curNodeTag == "blockquote") ||
(curNodeTag == "div"))
(curNodeTag == "div")) // div's other than mozdivs
{
curBlock = 0; // forget any previous block used for previous inline nodes
// recursion time
@ -2769,24 +3036,14 @@ nsHTMLEditRules::IsFirstEditableChild( nsIDOMNode *aNode, PRBool *aOutIsFirst)
*aOutIsFirst = PR_FALSE;
// find first editable child and compare it to aNode
nsCOMPtr<nsIDOMNode> parent;
nsCOMPtr<nsIDOMNode> parent, firstChild;
nsresult res = aNode->GetParentNode(getter_AddRefs(parent));
if (NS_FAILED(res)) return res;
if (!parent) return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMNode> child;
parent->GetFirstChild(getter_AddRefs(child));
if (!child) return NS_ERROR_FAILURE;
if (!mEditor->IsEditable(child))
{
nsCOMPtr<nsIDOMNode> tmp;
res = GetNextHTMLNode(child, &tmp);
if (NS_FAILED(res)) return res;
if (!tmp) return NS_ERROR_FAILURE;
child = tmp;
}
res = GetFirstEditableChild(parent, &firstChild);
if (NS_FAILED(res)) return res;
*aOutIsFirst = (child.get() == aNode);
*aOutIsFirst = (firstChild.get() == aNode);
return res;
}
@ -2801,24 +3058,70 @@ nsHTMLEditRules::IsLastEditableChild( nsIDOMNode *aNode, PRBool *aOutIsLast)
*aOutIsLast = PR_FALSE;
// find last editable child and compare it to aNode
nsCOMPtr<nsIDOMNode> parent;
nsCOMPtr<nsIDOMNode> parent, lastChild;
nsresult res = aNode->GetParentNode(getter_AddRefs(parent));
if (NS_FAILED(res)) return res;
if (!parent) return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMNode> child;
parent->GetLastChild(getter_AddRefs(child));
if (!child) return NS_ERROR_FAILURE;
res = GetLastEditableChild(parent, &lastChild);
if (NS_FAILED(res)) return res;
*aOutIsLast = (lastChild.get() == aNode);
return res;
}
if (!mEditor->IsEditable(child))
nsresult
nsHTMLEditRules::GetFirstEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstChild)
{
// check parms
if (!aOutFirstChild || !aNode) return NS_ERROR_NULL_POINTER;
// init out parms
*aOutFirstChild = nsnull;
// find first editable child
nsCOMPtr<nsIDOMNode> child;
nsresult res = aNode->GetFirstChild(getter_AddRefs(child));
if (NS_FAILED(res)) return res;
while (child && !mEditor->IsEditable(child))
{
nsCOMPtr<nsIDOMNode> tmp;
res = GetPriorHTMLNode(child, &tmp);
res = child->GetNextSibling(getter_AddRefs(tmp));
if (NS_FAILED(res)) return res;
if (!tmp) return NS_ERROR_FAILURE;
child = tmp;
}
*aOutIsLast = (child.get() == aNode);
*aOutFirstChild = child;
return res;
}
nsresult
nsHTMLEditRules::GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastChild)
{
// check parms
if (!aOutLastChild || !aNode) return NS_ERROR_NULL_POINTER;
// init out parms
*aOutLastChild = nsnull;
// find last editable child
nsCOMPtr<nsIDOMNode> child;
nsresult res = aNode->GetLastChild(getter_AddRefs(child));
if (NS_FAILED(res)) return res;
while (child && !mEditor->IsEditable(child))
{
nsCOMPtr<nsIDOMNode> tmp;
res = child->GetPreviousSibling(getter_AddRefs(tmp));
if (NS_FAILED(res)) return res;
if (!tmp) return NS_ERROR_FAILURE;
child = tmp;
}
*aOutLastChild = child;
return res;
}
@ -2843,10 +3146,21 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft,
nsresult res = NS_OK;
// caller responsible for:
// left & right node are same type
// left & right node have same parent
nsCOMPtr<nsIDOMNode> parent;
aNodeLeft->GetParentNode(getter_AddRefs(parent));
PRInt32 parOffset;
nsCOMPtr<nsIDOMNode> parent, rightParent;
res = nsEditor::GetNodeLocation(aNodeLeft, &parent, &parOffset);
if (NS_FAILED(res)) return res;
aNodeRight->GetParentNode(getter_AddRefs(rightParent));
// if they don't have the same parent, first move the 'right' node
// to after the 'left' one
if (parent != rightParent)
{
res = mEditor->DeleteNode(aNodeRight);
if (NS_FAILED(res)) return res;
res = mEditor->InsertNode(aNodeRight, parent, parOffset);
if (NS_FAILED(res)) return res;
}
// defaults for outParams
*aOutMergeParent = aNodeRight;
@ -2891,14 +3205,23 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft,
}
else
{
// remember the last left child, and firt right child
nsCOMPtr<nsIDOMNode> lastLeft, firstRight;
res = GetLastEditableChild(aNodeLeft, &lastLeft);
if (NS_FAILED(res)) return res;
res = GetFirstEditableChild(aNodeRight, &firstRight);
if (NS_FAILED(res)) return res;
// for list items, divs, etc, merge smart
res = mEditor->JoinNodes(aNodeLeft, aNodeRight, parent);
if (NS_FAILED(res)) return res;
// XXX floppy moose - figure out newNodeLeft & newNodeRight and recurse
// res = JoinNodesSmart(newNodeLeft, newNodeRight, aOutMergeParent, aOutMergeOffset);
// if (NS_FAILED(res)) return res;
return res;
if (lastLeft && firstRight && mEditor->NodesSameType(lastLeft, firstRight))
{
return JoinNodesSmart(lastLeft, firstRight, aOutMergeParent, aOutMergeOffset);
}
}
return res;
}
@ -3057,10 +3380,17 @@ nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList)
res = mEditor->InsertNode(curNode, curParPar, parOffset);
if (NS_FAILED(res)) return res;
// remove list items container if we promoted them out of list
// change list items container to mozdiv if we promoted them out of list
if (!IsList(curParPar) && IsListItem(curNode))
{
res = RemoveContainer(curNode);
nsCOMPtr<nsIDOMNode> mozDiv;
res = ReplaceContainer(curNode, &mozDiv, "div");
if (NS_FAILED(res)) return res;
// give it special moz attr
nsCOMPtr<nsIDOMElement> mozDivElem = do_QueryInterface(mozDiv);
res = mEditor->SetAttribute(mozDivElem, "type", "_moz");
if (NS_FAILED(res)) return res;
*aOutOfList = PR_TRUE;
if (NS_FAILED(res)) return res;
}

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

@ -49,7 +49,6 @@ protected:
// nsHTMLEditRules implementation methods
nsresult WillInsertText(nsIDOMSelection *aSelection,
PRBool *aCancel,
PlaceholderTxn **aTxn,
const nsString *inString,
nsString *outString,
TypeInState typeInState,
@ -63,8 +62,8 @@ protected:
nsresult WillAlign(nsIDOMSelection *aSelection, const nsString *alignType, PRBool *aCancel);
nsresult WillMakeBasicBlock(nsIDOMSelection *aSelection, const nsString *aBlockType, PRBool *aCancel);
nsresult InsertTab(nsIDOMSelection *aSelection, PRBool *aCancel, PlaceholderTxn **aTxn, nsString *outString);
nsresult InsertSpace(nsIDOMSelection *aSelection, PRBool *aCancel, PlaceholderTxn **aTxn, nsString *outString);
nsresult InsertTab(nsIDOMSelection *aSelection, PRBool *aCancel, nsString *outString);
nsresult InsertSpace(nsIDOMSelection *aSelection, PRBool *aCancel, nsString *outString);
nsresult ReturnInHeader(nsIDOMSelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset);
nsresult ReturnInParagraph(nsIDOMSelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset, PRBool *aCancel);
@ -83,8 +82,12 @@ protected:
static PRBool IsBreak(nsIDOMNode *aNode);
static PRBool IsBody(nsIDOMNode *aNode);
static PRBool IsBlockquote(nsIDOMNode *aNode);
static PRBool IsAnchor(nsIDOMNode *aNode);
static PRBool IsDiv(nsIDOMNode *aNode);
static PRBool IsNormalDiv(nsIDOMNode *aNode);
static PRBool IsMozDiv(nsIDOMNode *aNode);
static PRBool IsMailCite(nsIDOMNode *aNode);
static PRBool HasMozAttr(nsIDOMNode *aNode);
static PRBool InBody(nsIDOMNode *aNode);
@ -92,6 +95,9 @@ protected:
nsresult IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyNode);
PRBool IsFirstNode(nsIDOMNode *aNode);
PRBool IsLastNode(nsIDOMNode *aNode);
PRBool AtStartOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock);
PRBool AtEndOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock);
nsresult CreateMozDiv(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outDiv);
nsresult GetPriorHTMLNode(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode);
nsresult GetPriorHTMLNode(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outNode);
nsresult GetNextHTMLNode(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode);
@ -115,10 +121,12 @@ protected:
nsresult ReplaceContainer(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode, const nsString &aNodeType);
nsresult RemoveContainer(nsIDOMNode *inNode);
nsresult InsertContainerAbove(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode, nsString &aNodeType);
nsresult InsertContainerAbove(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode, const nsString &aNodeType);
nsresult IsFirstEditableChild( nsIDOMNode *aNode, PRBool *aOutIsFirst);
nsresult IsLastEditableChild( nsIDOMNode *aNode, PRBool *aOutIsLast);
nsresult GetFirstEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutFirstChild);
nsresult GetLastEditableChild( nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutLastChild);
nsresult JoinNodesSmart( nsIDOMNode *aNodeLeft,
nsIDOMNode *aNodeRight,

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -243,7 +243,7 @@ protected:
// key event helpers
NS_IMETHOD TabInTable(PRBool inIsShift, PRBool *outHandled);
NS_IMETHOD InsertBR();
NS_IMETHOD InsertBR(nsCOMPtr<nsIDOMNode> *outBRNode);
// Table Editing (implemented in EditTable.cpp)
@ -474,6 +474,9 @@ protected:
PRBool mIsComposing;
PRInt32 mMaxTextLength;
public:
static nsIAtom *gTypingTxnName;
// friends
friend class nsHTMLEditRules;
friend class nsTextEditRules;

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

@ -19,8 +19,6 @@
#include "nsTextEditRules.h"
#include "nsEditor.h"
#include "PlaceholderTxn.h"
#include "InsertTextTxn.h"
#include "nsCOMPtr.h"
#include "nsIDOMNode.h"
@ -140,7 +138,6 @@ nsTextEditRules::WillDoAction(nsIDOMSelection *aSelection,
case kInsertText:
return WillInsertText(aSelection,
aCancel,
info->placeTxn,
info->inString,
info->outString,
info->typeInState,
@ -265,7 +262,6 @@ nsTextEditRules::DidInsertBreak(nsIDOMSelection *aSelection, nsresult aResult)
nsresult
nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection,
PRBool *aCancel,
PlaceholderTxn **aTxn,
const nsString *aInString,
nsString *aOutString,
TypeInState aTypeInState,
@ -293,7 +289,7 @@ nsTextEditRules::WillInsertText(nsIDOMSelection *aSelection,
// do text insertion
PRBool bCancel;
res = DoTextInsertion(aSelection, &bCancel, aTxn, aOutString, aTypeInState);
res = DoTextInsertion(aSelection, &bCancel, aOutString, aTypeInState);
return res;
}
@ -1059,7 +1055,6 @@ nsTextEditRules::EchoInsertionToPWBuff(nsIDOMSelection *aSelection, nsString *aO
nsresult
nsTextEditRules::DoTextInsertion(nsIDOMSelection *aSelection,
PRBool *aCancel,
PlaceholderTxn **aTxn,
const nsString *aInString,
TypeInState aTypeInState)
{
@ -1070,14 +1065,6 @@ nsTextEditRules::DoTextInsertion(nsIDOMSelection *aSelection,
// rules code always does the insertion
*aCancel = PR_TRUE;
if (mBogusNode || (PR_TRUE==aTypeInState.IsAnySet()))
{
res = TransactionFactory::GetNewTransaction(PlaceholderTxn::GetCID(), (EditTxn **)aTxn);
if (NS_FAILED(res)) { return res; }
if (!*aTxn) { return NS_ERROR_NULL_POINTER; }
(*aTxn)->SetName(InsertTextTxn::gInsertTextTxnName);
mEditor->Do(*aTxn);
}
PRBool bCancel;
res = WillInsert(aSelection, &bCancel);
if (NS_SUCCEEDED(res) && (!bCancel))

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

@ -27,7 +27,6 @@
#include "nsEditRules.h"
#include "TypeInState.h"
class PlaceholderTxn;
class nsTextEditor;
/** Object that encapsulates HTML text-specific editing rules.
@ -86,7 +85,6 @@ protected:
// nsTextEditRules implementation methods
nsresult WillInsertText(nsIDOMSelection *aSelection,
PRBool *aCancel,
PlaceholderTxn **aTxn,
const nsString *inString,
nsString *outString,
TypeInState typeInState,
@ -180,7 +178,6 @@ protected:
/** do the actual text insertion */
nsresult DoTextInsertion(nsIDOMSelection *aSelection,
PRBool *aCancel,
PlaceholderTxn **aTxn,
const nsString *aInString,
TypeInState aTypeInState);
@ -199,7 +196,6 @@ class nsTextRulesInfo : public nsRulesInfo
nsTextRulesInfo(int aAction) :
nsRulesInfo(aAction),
placeTxn(0),
inString(0),
outString(0),
outputFormat(0),
@ -215,7 +211,6 @@ class nsTextRulesInfo : public nsRulesInfo
virtual ~nsTextRulesInfo() {};
// kInsertText
PlaceholderTxn **placeTxn;
const nsString *inString;
nsString *outString;
const nsString *outputFormat;

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

@ -203,6 +203,8 @@ public:
*/
NS_IMETHOD EndTransaction()=0;
NS_IMETHOD BeginPlaceHolderTransaction(nsIAtom *aName)=0;
NS_IMETHOD EndPlaceHolderTransaction()=0;
/* ------------ Clipboard methods -------------- */