зеркало из https://github.com/mozilla/pjs.git
make selection sticky across undo/redo (24573);
This commit is contained in:
Родитель
29efb32bce
Коммит
ae816c1a66
|
@ -25,7 +25,7 @@
|
|||
#include "nsIContent.h"
|
||||
|
||||
#ifdef NS_DEBUG
|
||||
static PRBool gNoisy = PR_TRUE;
|
||||
static PRBool gNoisy = PR_FALSE;
|
||||
#else
|
||||
static const PRBool gNoisy = PR_FALSE;
|
||||
#endif
|
||||
|
|
|
@ -39,7 +39,9 @@ PlaceholderTxn::PlaceholderTxn() : EditAggregateTxn(),
|
|||
mAbsorb(PR_TRUE),
|
||||
mForwarding(nsnull),
|
||||
mIMETextTxn(nsnull),
|
||||
mCommitted(PR_FALSE)
|
||||
mCommitted(PR_FALSE),
|
||||
mStartSel(nsnull),
|
||||
mEndSel()
|
||||
{
|
||||
SetTransactionDescriptionID( kTransactionID );
|
||||
/* log description initialized in parent constructor */
|
||||
|
@ -48,6 +50,7 @@ PlaceholderTxn::PlaceholderTxn() : EditAggregateTxn(),
|
|||
|
||||
PlaceholderTxn::~PlaceholderTxn()
|
||||
{
|
||||
delete mStartSel;
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(PlaceholderTxn, EditAggregateTxn)
|
||||
|
@ -72,15 +75,15 @@ NS_IMETHODIMP PlaceholderTxn::QueryInterface(REFNSIID aIID, void** aInstancePtr)
|
|||
}
|
||||
|
||||
NS_IMETHODIMP PlaceholderTxn::Init(nsWeakPtr aPresShellWeak, nsIAtom *aName,
|
||||
nsIDOMNode *aStartNode, PRInt32 aStartOffset)
|
||||
nsSelectionState *aSelState)
|
||||
{
|
||||
NS_ASSERTION(aPresShellWeak, "bad args");
|
||||
if (!aPresShellWeak) return NS_ERROR_NULL_POINTER;
|
||||
if (!aPresShellWeak || !aSelState) return NS_ERROR_NULL_POINTER;
|
||||
|
||||
mPresShellWeak = aPresShellWeak;
|
||||
mName = aName;
|
||||
mStartNode = do_QueryInterface(aStartNode);
|
||||
mStartOffset = aStartOffset;
|
||||
mStartSel = aSelState;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -92,8 +95,38 @@ NS_IMETHODIMP PlaceholderTxn::Do(void)
|
|||
|
||||
NS_IMETHODIMP PlaceholderTxn::Undo(void)
|
||||
{
|
||||
// using this to debug
|
||||
return EditAggregateTxn::Undo();
|
||||
// undo txns
|
||||
nsresult res = EditAggregateTxn::Undo();
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// now restore selection
|
||||
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
|
||||
if (!ps) return NS_ERROR_NOT_INITIALIZED;
|
||||
nsCOMPtr<nsIDOMSelection> selection;
|
||||
res = ps->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection));
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (!selection) return NS_ERROR_NULL_POINTER;
|
||||
if (!mStartSel) return NS_ERROR_NULL_POINTER;
|
||||
res = mStartSel->RestoreSelection(selection);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP PlaceholderTxn::Redo(void)
|
||||
{
|
||||
// redo txns
|
||||
nsresult res = EditAggregateTxn::Redo();
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// now restore selection
|
||||
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
|
||||
if (!ps) return NS_ERROR_NOT_INITIALIZED;
|
||||
nsCOMPtr<nsIDOMSelection> selection;
|
||||
res = ps->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection));
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (!selection) return NS_ERROR_NULL_POINTER;
|
||||
res = mEndSel.RestoreSelection(selection);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
@ -146,6 +179,10 @@ NS_IMETHODIMP PlaceholderTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransact
|
|||
AppendChild(editTxn);
|
||||
}
|
||||
*aDidMerge = PR_TRUE;
|
||||
// RememberEndingSelection();
|
||||
// efficiency hack: no need to remember selection here, as we haven't yet
|
||||
// finished the inital batch and we know we will be told when the batch ends.
|
||||
// we can remeber the selection then.
|
||||
if (gNoisy) { printf("Placeholder txn assimilated %p\n", aTransaction); }
|
||||
}
|
||||
else
|
||||
|
@ -155,27 +192,31 @@ NS_IMETHODIMP PlaceholderTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransact
|
|||
(mName.get() == nsHTMLEditor::gDeleteTxnName))
|
||||
&& !mCommitted )
|
||||
{
|
||||
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(NS_GET_IID(nsIAbsorbingTransaction), getter_AddRefs(plcTxn));
|
||||
if (plcTxn)
|
||||
// but only if this placeholder started with a collapsed selection
|
||||
if (mStartSel->IsCollapsed())
|
||||
{
|
||||
nsCOMPtr<nsIAtom> atom;
|
||||
plcTxn->GetTxnName(getter_AddRefs(atom));
|
||||
if (atom && (atom == mName))
|
||||
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(NS_GET_IID(nsIAbsorbingTransaction), getter_AddRefs(plcTxn));
|
||||
if (plcTxn)
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> otherTxnStartNode;
|
||||
PRInt32 otherTxnStartOffset;
|
||||
res = plcTxn->GetStartNodeAndOffset(&otherTxnStartNode, &otherTxnStartOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
if ((otherTxnStartNode == mEndNode) && (otherTxnStartOffset == mEndOffset))
|
||||
nsCOMPtr<nsIAtom> atom;
|
||||
plcTxn->GetTxnName(getter_AddRefs(atom));
|
||||
if (atom && (atom == mName))
|
||||
{
|
||||
mAbsorb = PR_TRUE; // we need to start absorbing again
|
||||
plcTxn->ForwardEndBatchTo(this);
|
||||
AppendChild(editTxn);
|
||||
*aDidMerge = PR_TRUE;
|
||||
// check if start selection of next placeholder matches
|
||||
// end selection of this placeholder
|
||||
PRBool isSame;
|
||||
plcTxn->StartSelectionEquals(&mEndSel, &isSame);
|
||||
if (isSame)
|
||||
{
|
||||
mAbsorb = PR_TRUE; // we need to start absorbing again
|
||||
plcTxn->ForwardEndBatchTo(this);
|
||||
AppendChild(editTxn);
|
||||
RememberEndingSelection();
|
||||
*aDidMerge = PR_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -189,11 +230,17 @@ NS_IMETHODIMP PlaceholderTxn::GetTxnName(nsIAtom **aName)
|
|||
return GetName(aName);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP PlaceholderTxn::GetStartNodeAndOffset(nsCOMPtr<nsIDOMNode> *aTxnStartNode, PRInt32 *aTxnStartOffset)
|
||||
NS_IMETHODIMP PlaceholderTxn::StartSelectionEquals(nsSelectionState *aSelState, PRBool *aResult)
|
||||
{
|
||||
if (!aTxnStartNode || !aTxnStartOffset) return NS_ERROR_NULL_POINTER;
|
||||
*aTxnStartNode = mStartNode;
|
||||
*aTxnStartOffset = mStartOffset;
|
||||
// determine if starting selection matches the given selection state.
|
||||
// note that we only care about collapsed selections.
|
||||
if (!aResult || !aSelState) return NS_ERROR_NULL_POINTER;
|
||||
if (!mStartSel->IsCollapsed() || !aSelState->IsCollapsed())
|
||||
{
|
||||
*aResult = PR_FALSE;
|
||||
return NS_OK;
|
||||
}
|
||||
*aResult = mStartSel->IsEqual(aSelState);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -207,21 +254,8 @@ NS_IMETHODIMP PlaceholderTxn::EndPlaceHolderBatch()
|
|||
if (plcTxn) plcTxn->EndPlaceHolderBatch();
|
||||
}
|
||||
|
||||
// if we are a typing or IME or deleting transaction, remember our selection state
|
||||
if ( (mName.get() == nsHTMLEditor::gTypingTxnName) ||
|
||||
(mName.get() == nsHTMLEditor::gIMETxnName) ||
|
||||
(mName.get() == nsHTMLEditor::gDeleteTxnName) )
|
||||
{
|
||||
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;
|
||||
// remember our selection state.
|
||||
return RememberEndingSelection();
|
||||
};
|
||||
|
||||
NS_IMETHODIMP PlaceholderTxn::ForwardEndBatchTo(nsIAbsorbingTransaction *aForwardingAddress)
|
||||
|
@ -236,5 +270,17 @@ NS_IMETHODIMP PlaceholderTxn::Commit()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP PlaceholderTxn::RememberEndingSelection()
|
||||
{
|
||||
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 = mEndSel.SaveSelection(selection);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#define AggregatePlaceholderTxn_h__
|
||||
|
||||
#include "EditAggregateTxn.h"
|
||||
#include "nsEditorUtils.h"
|
||||
#include "nsIAbsorbingTransaction.h"
|
||||
#include "nsIDOMNode.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
@ -67,16 +68,18 @@ public:
|
|||
NS_IMETHOD Do(void);
|
||||
|
||||
NS_IMETHOD Undo(void);
|
||||
|
||||
NS_IMETHOD Redo(void);
|
||||
|
||||
NS_IMETHOD Merge(PRBool *aDidMerge, nsITransaction *aTransaction);
|
||||
|
||||
// ------------ nsIAbsorbingTransaction -----------------------
|
||||
|
||||
NS_IMETHOD Init(nsWeakPtr aPresShellWeak, nsIAtom *aName, nsIDOMNode *aStartNode, PRInt32 aStartOffset);
|
||||
NS_IMETHOD Init(nsWeakPtr aPresShellWeak, nsIAtom *aName, nsSelectionState *aSelState);
|
||||
|
||||
NS_IMETHOD GetTxnName(nsIAtom **aName);
|
||||
|
||||
NS_IMETHOD GetStartNodeAndOffset(nsCOMPtr<nsIDOMNode> *aTxnStartNode, PRInt32 *aTxnStartOffset);
|
||||
NS_IMETHOD StartSelectionEquals(nsSelectionState *aSelState, PRBool *aResult);
|
||||
|
||||
NS_IMETHOD EndPlaceHolderBatch();
|
||||
|
||||
|
@ -84,6 +87,8 @@ public:
|
|||
|
||||
NS_IMETHOD Commit();
|
||||
|
||||
NS_IMETHOD RememberEndingSelection();
|
||||
|
||||
friend class TransactionFactory;
|
||||
|
||||
enum { kTransactionID = 11260 };
|
||||
|
@ -93,12 +98,15 @@ protected:
|
|||
/** the presentation shell, which we'll need to get the selection */
|
||||
nsWeakPtr mPresShellWeak; // weak reference to the nsIPresShell
|
||||
PRBool mAbsorb; // do we auto absorb any and all transaction?
|
||||
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;
|
||||
IMETextTxn *mIMETextTxn; // first IME txn in this placeholder - used for IME merging
|
||||
// non-owning for now - cant nsCOMPtr it due to broken transaction interfaces
|
||||
PRBool mCommitted; // do we stop auto absorbing any matching placeholder txns?
|
||||
// these next two members store the state of the selection in a safe way.
|
||||
// selection at the start of the txn is stored, as is the selection at the end.
|
||||
// This is so that Undo() and Redo() can restore the selection properly.
|
||||
nsSelectionState *mStartSel; // use a pointer because this is constructed before we exist
|
||||
nsSelectionState mEndSel;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -137,6 +137,169 @@ const PRUnichar nbsp = 160;
|
|||
PRInt32 nsEditor::gInstanceCount = 0;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* class for recording selection info. stores selection as collection of
|
||||
* { {startnode, startoffset} , {endnode, endoffset} } tuples. Cant store
|
||||
* ranges since dom gravity will possibly change the ranges.
|
||||
*/
|
||||
nsSelectionState::nsSelectionState() {}
|
||||
|
||||
nsSelectionState::~nsSelectionState()
|
||||
{
|
||||
// free any items in the array
|
||||
SelRangeStore *item;
|
||||
while (item = (SelRangeStore*)mArray.ElementAt(0))
|
||||
{
|
||||
delete item;
|
||||
mArray.RemoveElementAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSelectionState::SaveSelection(nsIDOMSelection *aSel)
|
||||
{
|
||||
if (!aSel) return NS_ERROR_NULL_POINTER;
|
||||
nsresult res = NS_OK;
|
||||
PRInt32 i,rangeCount, arrayCount = mArray.Count();
|
||||
SelRangeStore *item;
|
||||
aSel->GetRangeCount(&rangeCount);
|
||||
|
||||
// if we need more items in the array, new them
|
||||
if (arrayCount<rangeCount)
|
||||
{
|
||||
PRInt32 count = rangeCount-arrayCount;
|
||||
for (i=0; i<count; i++)
|
||||
{
|
||||
item = new SelRangeStore;
|
||||
mArray.AppendElement(item);
|
||||
}
|
||||
}
|
||||
|
||||
// else if we have too many, delete them
|
||||
else if (rangeCount>arrayCount)
|
||||
{
|
||||
while (item = (SelRangeStore*)mArray.ElementAt(rangeCount))
|
||||
{
|
||||
delete item;
|
||||
mArray.RemoveElementAt(rangeCount);
|
||||
}
|
||||
}
|
||||
|
||||
// now store the selection ranges
|
||||
for (i=0; i<rangeCount; i++)
|
||||
{
|
||||
item = (SelRangeStore*)mArray.ElementAt(i);
|
||||
if (!item) return NS_ERROR_UNEXPECTED;
|
||||
nsCOMPtr<nsIDOMRange> range;
|
||||
res = aSel->GetRangeAt(i, getter_AddRefs(range));
|
||||
item->StoreRange(range);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSelectionState::RestoreSelection(nsIDOMSelection *aSel)
|
||||
{
|
||||
if (!aSel) return NS_ERROR_NULL_POINTER;
|
||||
nsresult res = NS_OK;
|
||||
PRInt32 i, arrayCount = mArray.Count();
|
||||
SelRangeStore *item;
|
||||
|
||||
// clear ot cur selection
|
||||
aSel->ClearSelection();
|
||||
|
||||
// set the selection ranges anew
|
||||
for (i=0; i<arrayCount; i++)
|
||||
{
|
||||
item = (SelRangeStore*)mArray.ElementAt(i);
|
||||
if (!item) return NS_ERROR_UNEXPECTED;
|
||||
nsCOMPtr<nsIDOMRange> range;
|
||||
item->GetRange(&range);
|
||||
if (!range) return NS_ERROR_UNEXPECTED;
|
||||
|
||||
res = aSel->AddRange(range);
|
||||
if(NS_FAILED(res)) return res;
|
||||
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsSelectionState::IsCollapsed()
|
||||
{
|
||||
if (1 != mArray.Count()) return PR_FALSE;
|
||||
SelRangeStore *item;
|
||||
item = (SelRangeStore*)mArray.ElementAt(0);
|
||||
if (!item) return PR_FALSE;
|
||||
nsCOMPtr<nsIDOMRange> range;
|
||||
item->GetRange(&range);
|
||||
if (!range) return PR_FALSE;
|
||||
PRBool bIsCollapsed;
|
||||
range->GetIsCollapsed(&bIsCollapsed);
|
||||
return bIsCollapsed;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsSelectionState::IsEqual(nsSelectionState *aSelState)
|
||||
{
|
||||
if (!aSelState) return NS_ERROR_NULL_POINTER;
|
||||
PRInt32 i, myCount = mArray.Count(), itsCount = aSelState->mArray.Count();
|
||||
if (myCount != itsCount) return PR_FALSE;
|
||||
if (myCount < 1) return PR_FALSE;
|
||||
|
||||
SelRangeStore *myItem, *itsItem;
|
||||
|
||||
for (i=0; i<myCount; i++)
|
||||
{
|
||||
myItem = (SelRangeStore*)mArray.ElementAt(0);
|
||||
itsItem = (SelRangeStore*)(aSelState->mArray.ElementAt(0));
|
||||
if (!myItem || !itsItem) return PR_FALSE;
|
||||
|
||||
nsCOMPtr<nsIDOMRange> myRange, itsRange;
|
||||
myItem->GetRange(&myRange);
|
||||
itsItem->GetRange(&itsRange);
|
||||
if (!myRange || !itsRange) return PR_FALSE;
|
||||
|
||||
PRInt32 compResult;
|
||||
myRange->CompareEndPoints(nsIDOMRange::START_TO_START, itsRange, &compResult);
|
||||
if (compResult) return PR_FALSE;
|
||||
myRange->CompareEndPoints(nsIDOMRange::END_TO_END, itsRange, &compResult);
|
||||
if (compResult) return PR_FALSE;
|
||||
}
|
||||
// if we got here, they are equal
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
nsresult SelRangeStore::StoreRange(nsIDOMRange *aRange)
|
||||
{
|
||||
if (!aRange) return NS_ERROR_NULL_POINTER;
|
||||
aRange->GetStartParent(getter_AddRefs(startNode));
|
||||
aRange->GetEndParent(getter_AddRefs(endNode));
|
||||
aRange->GetStartOffset(&startOffset);
|
||||
aRange->GetEndOffset(&endOffset);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult SelRangeStore::GetRange(nsCOMPtr<nsIDOMRange> *outRange)
|
||||
{
|
||||
if (!outRange) return NS_ERROR_NULL_POINTER;
|
||||
nsresult res = nsComponentManager::CreateInstance(kCRangeCID,
|
||||
nsnull,
|
||||
NS_GET_IID(nsIDOMRange),
|
||||
getter_AddRefs(*outRange));
|
||||
if(NS_FAILED(res)) return res;
|
||||
|
||||
res = (*outRange)->SetStart(startNode, startOffset);
|
||||
if(NS_FAILED(res)) return res;
|
||||
|
||||
res = (*outRange)->SetEnd(endNode, endOffset);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
//class implementations are in order they are declared in nsEditor.h
|
||||
|
||||
nsEditor::nsEditor()
|
||||
|
@ -146,8 +309,7 @@ nsEditor::nsEditor()
|
|||
, mPlaceHolderTxn(nsnull)
|
||||
, mPlaceHolderName(nsnull)
|
||||
, mPlaceHolderBatch(0)
|
||||
, mTxnStartNode(nsnull)
|
||||
, mTxnStartOffset(0)
|
||||
, mSelState(nsnull)
|
||||
, mShouldTxnSetSelection(PR_TRUE)
|
||||
, mBodyElement(nsnull)
|
||||
, mInIMEMode(PR_FALSE)
|
||||
|
@ -376,8 +538,9 @@ nsEditor::Do(nsITransaction *aTxn)
|
|||
|
||||
// save off weak reference to placeholder txn
|
||||
mPlaceHolderTxn = getter_AddRefs( NS_GetWeakReference(plcTxn) );
|
||||
plcTxn->Init(mPresShellWeak, mPlaceHolderName, mTxnStartNode, mTxnStartOffset);
|
||||
|
||||
plcTxn->Init(mPresShellWeak, mPlaceHolderName, mSelState);
|
||||
mSelState = nsnull; // placeholder txn took ownership of this pointer
|
||||
|
||||
// finally we QI to an nsITransaction since that's what Do() expects
|
||||
nsCOMPtr<nsITransaction> theTxn = do_QueryInterface(plcTxn);
|
||||
nsITransaction* txn = theTxn;
|
||||
|
@ -588,17 +751,8 @@ nsEditor::BeginPlaceHolderTransaction(nsIAtom *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/deleting 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;
|
||||
}
|
||||
mSelState = new nsSelectionState();
|
||||
mSelState->SaveSelection(selection);
|
||||
}
|
||||
mPlaceHolderBatch++;
|
||||
|
||||
|
@ -613,8 +767,13 @@ nsEditor::EndPlaceHolderTransaction()
|
|||
{
|
||||
// time to turn off the batch
|
||||
EndUpdateViewBatch();
|
||||
mTxnStartNode = nsnull;
|
||||
mTxnStartOffset = 0;
|
||||
if (mSelState)
|
||||
{
|
||||
// we saved the selection state, but never got to hand it to placeholder
|
||||
// (else we ould have nulled out this pointer), so destroy it to prevent leaks.
|
||||
delete mSelState;
|
||||
mSelState = nsnull;
|
||||
}
|
||||
if (mPlaceHolderTxn) // we might have never made a placeholder if no action took place
|
||||
{
|
||||
nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryReferent(mPlaceHolderTxn);
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "nsICSSStyleSheet.h"
|
||||
#include "nsIDTD.h"
|
||||
#include "nsIDOMElement.h"
|
||||
#include "nsVoidArray.h"
|
||||
|
||||
class nsIEditActionListener;
|
||||
class nsIDocumentStateListener;
|
||||
|
@ -70,6 +71,38 @@ class AddStyleSheetTxn;
|
|||
class RemoveStyleSheetTxn;
|
||||
class nsFileSpec;
|
||||
|
||||
/***************************************************************************
|
||||
* class for recording selection info. stores selection as collection of
|
||||
* { {startnode, startoffset} , {endnode, endoffset} } tuples. Cant store
|
||||
* ranges since dom gravity will possibly change the ranges.
|
||||
*/
|
||||
|
||||
// first a helper struct for saving/setting ranges
|
||||
struct SelRangeStore
|
||||
{
|
||||
nsresult StoreRange(nsIDOMRange *aRange);
|
||||
nsresult GetRange(nsCOMPtr<nsIDOMRange> *outRange);
|
||||
|
||||
nsCOMPtr<nsIDOMNode> startNode;
|
||||
PRInt32 startOffset;
|
||||
nsCOMPtr<nsIDOMNode> endNode;
|
||||
PRInt32 endOffset;
|
||||
};
|
||||
|
||||
class nsSelectionState
|
||||
{
|
||||
public:
|
||||
|
||||
nsSelectionState();
|
||||
~nsSelectionState();
|
||||
|
||||
nsresult SaveSelection(nsIDOMSelection *aSel);
|
||||
nsresult RestoreSelection(nsIDOMSelection *aSel);
|
||||
PRBool IsCollapsed();
|
||||
PRBool IsEqual(nsSelectionState *aSelState);
|
||||
|
||||
nsVoidArray mArray;
|
||||
};
|
||||
|
||||
/** implementation of an editor object. it will be the controler/focal point
|
||||
* for the main editor services. i.e. the GUIManager, publishing, transaction
|
||||
|
@ -653,8 +686,7 @@ protected:
|
|||
nsWeakPtr mPlaceHolderTxn; // weak reference to placeholder for begin/end batch purposes
|
||||
nsIAtom *mPlaceHolderName; // name of placeholder transaction
|
||||
PRInt32 mPlaceHolderBatch; // nesting count for batching
|
||||
nsCOMPtr<nsIDOMNode> mTxnStartNode; // saved selection info to pass to placeholder at init time
|
||||
PRInt32 mTxnStartOffset; // " " " "
|
||||
nsSelectionState *mSelState; // saved selection state for placeholder txn batching
|
||||
PRBool mShouldTxnSetSelection; // turn off for conservative selection adjustment by txns
|
||||
nsCOMPtr<nsIDOMElement> mBodyElement; // cached body node
|
||||
//
|
||||
|
|
|
@ -785,7 +785,7 @@ nsEditorShell::SetTextProperty(const PRUnichar *prop, const PRUnichar *attr, con
|
|||
default:
|
||||
err = NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
UpdateInterfaceState();
|
||||
NS_RELEASE(styleAtom);
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -30,31 +30,13 @@
|
|||
#include "nsIDOMSelection.h"
|
||||
#include "nsIEditor.h"
|
||||
#include "nsIAtom.h"
|
||||
|
||||
#include "nsVoidArray.h"
|
||||
#include "nsEditor.h"
|
||||
|
||||
class nsAutoEditBatch
|
||||
{
|
||||
private:
|
||||
nsCOMPtr<nsIEditor> mEd;
|
||||
public:
|
||||
nsAutoEditBatch( nsIEditor *aEd) : mEd(do_QueryInterface(aEd))
|
||||
{ if (mEd) mEd->BeginTransaction(); }
|
||||
~nsAutoEditBatch() { if (mEd) mEd->EndTransaction(); }
|
||||
};
|
||||
|
||||
class nsAutoEditMayBatch
|
||||
{
|
||||
private:
|
||||
nsCOMPtr<nsIEditor> mEd;
|
||||
PRBool mDidBatch;
|
||||
public:
|
||||
nsAutoEditMayBatch( nsIEditor *aEd) : mEd(do_QueryInterface(aEd)), mDidBatch(PR_FALSE) {}
|
||||
~nsAutoEditMayBatch() { if (mEd && mDidBatch) mEd->EndTransaction(); }
|
||||
|
||||
void batch() { if (mEd && !mDidBatch) {mEd->BeginTransaction(); mDidBatch=PR_TRUE;} }
|
||||
};
|
||||
|
||||
/***************************************************************************
|
||||
* stack based helper class for batching a collection of txns inside a
|
||||
* placeholder txn.
|
||||
*/
|
||||
class nsAutoPlaceHolderBatch
|
||||
{
|
||||
private:
|
||||
|
@ -66,6 +48,22 @@ class nsAutoPlaceHolderBatch
|
|||
};
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* stack based helper class for batching a collection of txns.
|
||||
* Note: I changed this to use placeholder batching so that we get
|
||||
* proper selection save/restore across undo/redo.
|
||||
*/
|
||||
class nsAutoEditBatch : public nsAutoPlaceHolderBatch
|
||||
{
|
||||
public:
|
||||
nsAutoEditBatch( nsIEditor *aEd) : nsAutoPlaceHolderBatch(aEd,nsnull) {}
|
||||
~nsAutoEditBatch() {}
|
||||
};
|
||||
|
||||
/***************************************************************************
|
||||
* stack based helper class for saving/restoring selection. Note that this
|
||||
* assumes that the nodes involved are still around afterwards!
|
||||
*/
|
||||
class nsAutoSelectionReset
|
||||
{
|
||||
private:
|
||||
|
@ -138,5 +136,4 @@ class nsAutoTxnsConserveSelection
|
|||
};
|
||||
|
||||
|
||||
|
||||
#endif // nsEditorUtils_h__
|
||||
|
|
|
@ -2748,10 +2748,6 @@ nsHTMLEditRules::ReturnInListItem(nsIDOMSelection *aSelection,
|
|||
res = aSelection->Collapse(listparent,offset+1);
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// now attempt to adjust selection to a text node.
|
||||
// if we fail, then that means we need toinsert a break
|
||||
res = AdjustSelection(aSelection, nsIEditor::eNext);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// get the selection location
|
||||
nsCOMPtr<nsIDOMNode> selNode;
|
||||
PRInt32 selOffset;
|
||||
|
|
|
@ -218,6 +218,11 @@ nsHTMLEditor::nsHTMLEditor()
|
|||
{
|
||||
// Done in nsEditor
|
||||
// NS_INIT_REFCNT();
|
||||
mBoldAtom = getter_AddRefs(NS_NewAtom("b"));
|
||||
mItalicAtom = getter_AddRefs(NS_NewAtom("i"));
|
||||
mUnderlineAtom = getter_AddRefs(NS_NewAtom("u"));
|
||||
mFontAtom = getter_AddRefs(NS_NewAtom("font"));
|
||||
|
||||
if (!gTypingTxnName)
|
||||
gTypingTxnName = NS_NewAtom("Typing");
|
||||
else
|
||||
|
@ -1053,6 +1058,7 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty,
|
|||
|
||||
PRBool isCollapsed;
|
||||
selection->GetIsCollapsed(&isCollapsed);
|
||||
nsCOMPtr<nsIDOMNode> collapsedNode;
|
||||
nsCOMPtr<nsIEnumerator> enumerator;
|
||||
result = selection->GetEnumerator(getter_AddRefs(enumerator));
|
||||
if (NS_FAILED(result)) return result;
|
||||
|
@ -1066,7 +1072,45 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty,
|
|||
if ((NS_SUCCEEDED(result)) && currentItem)
|
||||
{
|
||||
PRBool firstNodeInRange = PR_TRUE; // for each range, set a flag
|
||||
nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
|
||||
nsCOMPtr<nsIDOMRange> range(do_QueryInterface(currentItem));
|
||||
|
||||
if (isCollapsed)
|
||||
{
|
||||
// efficiency hack. we cache prior results for being collapsed in a given text node.
|
||||
// this speeds up typing. Note that other parts of the editor code have to clear out
|
||||
// this cache after certain actions.
|
||||
range->GetStartParent(getter_AddRefs(collapsedNode));
|
||||
if (!collapsedNode) return NS_ERROR_FAILURE;
|
||||
// refresh the cache if we need to
|
||||
if (collapsedNode != mCachedNode) CacheInlineStyles(collapsedNode);
|
||||
// cache now current, use it! But *or* it with typeInState results...
|
||||
PRBool isSet;
|
||||
if (aProperty == mBoldAtom.get())
|
||||
{
|
||||
GetTypingState(aProperty, isSet);
|
||||
aFirst = aAny = aAll = (mCachedBoldStyle || isSet);
|
||||
return NS_OK;
|
||||
}
|
||||
else if (aProperty == mItalicAtom.get())
|
||||
{
|
||||
GetTypingState(aProperty, isSet);
|
||||
aFirst = aAny = aAll = (mCachedItalicStyle || isSet);
|
||||
return NS_OK;
|
||||
}
|
||||
else if (aProperty == mUnderlineAtom.get())
|
||||
{
|
||||
GetTypingState(aProperty, isSet);
|
||||
aFirst = aAny = aAll = (mCachedUnderlineStyle || isSet);
|
||||
return NS_OK;
|
||||
}
|
||||
else if (aProperty == mFontAtom.get())
|
||||
{
|
||||
// MOOSE!
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// either non-collapsed selection or no cached value: do it the hard way
|
||||
nsCOMPtr<nsIContentIterator> iter;
|
||||
result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull,
|
||||
nsIContentIterator::GetIID(),
|
||||
|
@ -1138,7 +1182,8 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty,
|
|||
iter->CurrentNode(getter_AddRefs(content));
|
||||
}
|
||||
}
|
||||
if (PR_FALSE==aAny) { // make sure that if none of the selection is set, we don't report all is set
|
||||
if (PR_FALSE==aAny)
|
||||
{ // make sure that if none of the selection is set, we don't report all is set
|
||||
aAll = PR_FALSE;
|
||||
}
|
||||
//if (gNoisy) { printf(" returning first=%d any=%d all=%d\n", aFirst, aAny, aAll); }
|
||||
|
@ -4450,6 +4495,8 @@ void nsHTMLEditor::ApplyStyleSheetToPresShellDocument(nsICSSStyleSheet* aSheet,
|
|||
NS_IMETHODIMP
|
||||
nsHTMLEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection)
|
||||
{
|
||||
if (! ((opID==kOpInsertText) || (opID==kOpInsertIMEText)) )
|
||||
ClearInlineStylesCache();
|
||||
if (mRules) return mRules->BeforeEdit(opID, aDirection);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -4460,6 +4507,8 @@ nsHTMLEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection)
|
|||
NS_IMETHODIMP
|
||||
nsHTMLEditor::EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection)
|
||||
{
|
||||
if (! ((opID==kOpInsertText) || (opID==kOpInsertIMEText)) )
|
||||
ClearInlineStylesCache();
|
||||
if (mRules) return mRules->AfterEdit(opID, aDirection);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -5882,6 +5931,22 @@ nsHTMLEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr<nsIDOMNode> &parent
|
|||
#pragma mark -
|
||||
#endif
|
||||
|
||||
void nsHTMLEditor::CacheInlineStyles(nsIDOMNode *aNode)
|
||||
{
|
||||
if (!aNode) return;
|
||||
nsCOMPtr<nsIDOMNode> resultNode;
|
||||
mCachedNode = do_QueryInterface(aNode);
|
||||
IsTextPropertySetByContent(aNode, mBoldAtom, 0, 0, mCachedBoldStyle, getter_AddRefs(resultNode));
|
||||
IsTextPropertySetByContent(aNode, mItalicAtom, 0, 0, mCachedItalicStyle, getter_AddRefs(resultNode));
|
||||
IsTextPropertySetByContent(aNode, mUnderlineAtom, 0, 0, mCachedUnderlineStyle, getter_AddRefs(resultNode));
|
||||
|
||||
}
|
||||
|
||||
void nsHTMLEditor::ClearInlineStylesCache()
|
||||
{
|
||||
mCachedNode = nsnull;
|
||||
}
|
||||
|
||||
#ifdef PRE_NODE_IN_BODY
|
||||
nsCOMPtr<nsIDOMElement> nsHTMLEditor::FindPreElement()
|
||||
{
|
||||
|
@ -6890,7 +6955,7 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar
|
|||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
skippedEndNode = PR_TRUE;
|
||||
if (gNoisy) { printf("skipping end node because aProperty not set.\n"); }
|
||||
}
|
||||
|
@ -6903,13 +6968,16 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar
|
|||
}
|
||||
else
|
||||
{
|
||||
endNode = GetChildAt(aEndNode, aEndOffset);
|
||||
// experimental hack: need to rewrite all of this
|
||||
// MOOSE!
|
||||
if (aEndOffset<0) aEndOffset=1;
|
||||
endNode = GetChildAt(aEndNode, aEndOffset-1);
|
||||
parent = do_QueryInterface(aEndNode);
|
||||
if (!endNode || !parent) {return NS_ERROR_NULL_POINTER;}
|
||||
IsTextPropertySetByContent(endNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode));
|
||||
if (PR_TRUE==textPropertySet)
|
||||
{
|
||||
result = RemoveTextPropertiesForNode(endNode, parent, aEndOffset, aEndOffset+1, aPropName, aAttribute);
|
||||
result = RemoveTextPropertiesForNode(endNode, parent, aEndOffset-1, aEndOffset, aPropName, aAttribute);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -306,7 +306,10 @@ protected:
|
|||
PRBool SetCaretInTableCell(nsIDOMElement* aElement);
|
||||
PRBool IsElementInBody(nsIDOMElement* aElement);
|
||||
|
||||
|
||||
// inline style caching
|
||||
void CacheInlineStyles(nsIDOMNode *aNode);
|
||||
void ClearInlineStylesCache();
|
||||
|
||||
// key event helpers
|
||||
NS_IMETHOD TabInTable(PRBool inIsShift, PRBool *outHandled);
|
||||
NS_IMETHOD CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPtr<nsIDOMNode> *outBRNode);
|
||||
|
@ -544,6 +547,17 @@ protected:
|
|||
nsCOMPtr<nsIDOMEventListener> mFocusListenerP;
|
||||
PRBool mIsComposing;
|
||||
PRInt32 mMaxTextLength;
|
||||
|
||||
nsCOMPtr<nsIAtom> mBoldAtom;
|
||||
nsCOMPtr<nsIAtom> mItalicAtom;
|
||||
nsCOMPtr<nsIAtom> mUnderlineAtom;
|
||||
nsCOMPtr<nsIAtom> mFontAtom;
|
||||
nsCOMPtr<nsIDOMNode> mCachedNode;
|
||||
|
||||
PRBool mCachedBoldStyle;
|
||||
PRBool mCachedItalicStyle;
|
||||
PRBool mCachedUnderlineStyle;
|
||||
nsString mCachedFontName;
|
||||
|
||||
public:
|
||||
static nsIAtom *gTypingTxnName;
|
||||
|
|
|
@ -40,6 +40,8 @@ Transaction interface to outside world
|
|||
#include "nsIAtom.h"
|
||||
#include "nsIDOMNode.h"
|
||||
|
||||
class nsSelectionState;
|
||||
|
||||
/**
|
||||
* A transaction interface mixin - for transactions that can support.
|
||||
* the placeholder absorbtion idiom.
|
||||
|
@ -49,13 +51,13 @@ public:
|
|||
|
||||
static const nsIID& GetIID() { static nsIID iid = NS_IABSORBINGTRANSACTION_IID; return iid; }
|
||||
|
||||
NS_IMETHOD Init(nsWeakPtr aPresShellWeak, nsIAtom *aName, nsIDOMNode *aStartNode, PRInt32 aStartOffset)=0;
|
||||
NS_IMETHOD Init(nsWeakPtr aPresShellWeak, nsIAtom *aName, nsSelectionState *aSelState)=0;
|
||||
|
||||
NS_IMETHOD EndPlaceHolderBatch()=0;
|
||||
|
||||
NS_IMETHOD GetTxnName(nsIAtom **aName)=0;
|
||||
|
||||
NS_IMETHOD GetStartNodeAndOffset(nsCOMPtr<nsIDOMNode> *aTxnStartNode, PRInt32 *aTxnStartOffset)=0;
|
||||
NS_IMETHOD StartSelectionEquals(nsSelectionState *aSelState, PRBool *aResult)=0;
|
||||
|
||||
NS_IMETHOD ForwardEndBatchTo(nsIAbsorbingTransaction *aForwardingAddress)=0;
|
||||
|
||||
|
|
|
@ -284,32 +284,14 @@ nsInterfaceState::UpdateFontFace(const char* observerName, const char* attribute
|
|||
|
||||
PRBool testBoolean;
|
||||
|
||||
if (SelectionIsCollapsed())
|
||||
rv = mEditor->GetInlineProperty(styleAtom, &faceStr, &thisFace, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp);
|
||||
if( !anyOfSelectionHasProp )
|
||||
{
|
||||
rv = mEditor->GetTypingStateValue(styleAtom, ioFontString);
|
||||
if (ioFontString.Length() == 0)
|
||||
{
|
||||
// We don't have a font set, so check for "tt" (= "Default Fixed Width")
|
||||
PRBool stateHasProp = PR_FALSE;
|
||||
rv = mEditor->GetTypingState(fixedStyleAtom, testBoolean);
|
||||
testBoolean = stateHasProp;
|
||||
if (stateHasProp)
|
||||
thisFace = faceStr;
|
||||
}
|
||||
else
|
||||
testBoolean = PR_TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
rv = mEditor->GetInlineProperty(styleAtom, &faceStr, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp);
|
||||
if( !anyOfSelectionHasProp )
|
||||
{
|
||||
// No font face set -- check for "tt"
|
||||
rv = mEditor->GetInlineProperty(fixedStyleAtom, nsnull, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp);
|
||||
testBoolean = anyOfSelectionHasProp;
|
||||
if (anyOfSelectionHasProp)
|
||||
thisFace = "tt";
|
||||
}
|
||||
// No font face set -- check for "tt"
|
||||
rv = mEditor->GetInlineProperty(fixedStyleAtom, nsnull, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp);
|
||||
testBoolean = anyOfSelectionHasProp;
|
||||
if (anyOfSelectionHasProp)
|
||||
thisFace = "tt";
|
||||
}
|
||||
|
||||
// TODO: HANDLE "MIXED" STATE
|
||||
|
@ -331,22 +313,13 @@ nsInterfaceState::UpdateTextState(const char* tagName, const char* observerName,
|
|||
nsCOMPtr<nsIAtom> styleAtom = getter_AddRefs(NS_NewAtom(tagName));
|
||||
|
||||
PRBool testBoolean;
|
||||
if (SelectionIsCollapsed())
|
||||
{
|
||||
PRBool stateHasProp = PR_FALSE;
|
||||
|
||||
rv = mEditor->GetTypingState(styleAtom, stateHasProp);
|
||||
testBoolean = stateHasProp;
|
||||
}
|
||||
else
|
||||
{
|
||||
PRBool firstOfSelectionHasProp = PR_FALSE;
|
||||
PRBool anyOfSelectionHasProp = PR_FALSE;
|
||||
PRBool allOfSelectionHasProp = PR_FALSE;
|
||||
PRBool firstOfSelectionHasProp = PR_FALSE;
|
||||
PRBool anyOfSelectionHasProp = PR_FALSE;
|
||||
PRBool allOfSelectionHasProp = PR_FALSE;
|
||||
|
||||
rv = mEditor->GetInlineProperty(styleAtom, nsnull, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp);
|
||||
testBoolean = allOfSelectionHasProp; // change this to alter the behaviour
|
||||
|
||||
rv = mEditor->GetInlineProperty(styleAtom, nsnull, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp);
|
||||
testBoolean = allOfSelectionHasProp; // change this to alter the behaviour
|
||||
}
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (testBoolean != ioState)
|
||||
|
|
|
@ -785,7 +785,7 @@ nsEditorShell::SetTextProperty(const PRUnichar *prop, const PRUnichar *attr, con
|
|||
default:
|
||||
err = NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
UpdateInterfaceState();
|
||||
NS_RELEASE(styleAtom);
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -284,32 +284,14 @@ nsInterfaceState::UpdateFontFace(const char* observerName, const char* attribute
|
|||
|
||||
PRBool testBoolean;
|
||||
|
||||
if (SelectionIsCollapsed())
|
||||
rv = mEditor->GetInlineProperty(styleAtom, &faceStr, &thisFace, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp);
|
||||
if( !anyOfSelectionHasProp )
|
||||
{
|
||||
rv = mEditor->GetTypingStateValue(styleAtom, ioFontString);
|
||||
if (ioFontString.Length() == 0)
|
||||
{
|
||||
// We don't have a font set, so check for "tt" (= "Default Fixed Width")
|
||||
PRBool stateHasProp = PR_FALSE;
|
||||
rv = mEditor->GetTypingState(fixedStyleAtom, testBoolean);
|
||||
testBoolean = stateHasProp;
|
||||
if (stateHasProp)
|
||||
thisFace = faceStr;
|
||||
}
|
||||
else
|
||||
testBoolean = PR_TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
rv = mEditor->GetInlineProperty(styleAtom, &faceStr, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp);
|
||||
if( !anyOfSelectionHasProp )
|
||||
{
|
||||
// No font face set -- check for "tt"
|
||||
rv = mEditor->GetInlineProperty(fixedStyleAtom, nsnull, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp);
|
||||
testBoolean = anyOfSelectionHasProp;
|
||||
if (anyOfSelectionHasProp)
|
||||
thisFace = "tt";
|
||||
}
|
||||
// No font face set -- check for "tt"
|
||||
rv = mEditor->GetInlineProperty(fixedStyleAtom, nsnull, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp);
|
||||
testBoolean = anyOfSelectionHasProp;
|
||||
if (anyOfSelectionHasProp)
|
||||
thisFace = "tt";
|
||||
}
|
||||
|
||||
// TODO: HANDLE "MIXED" STATE
|
||||
|
@ -331,22 +313,13 @@ nsInterfaceState::UpdateTextState(const char* tagName, const char* observerName,
|
|||
nsCOMPtr<nsIAtom> styleAtom = getter_AddRefs(NS_NewAtom(tagName));
|
||||
|
||||
PRBool testBoolean;
|
||||
if (SelectionIsCollapsed())
|
||||
{
|
||||
PRBool stateHasProp = PR_FALSE;
|
||||
|
||||
rv = mEditor->GetTypingState(styleAtom, stateHasProp);
|
||||
testBoolean = stateHasProp;
|
||||
}
|
||||
else
|
||||
{
|
||||
PRBool firstOfSelectionHasProp = PR_FALSE;
|
||||
PRBool anyOfSelectionHasProp = PR_FALSE;
|
||||
PRBool allOfSelectionHasProp = PR_FALSE;
|
||||
PRBool firstOfSelectionHasProp = PR_FALSE;
|
||||
PRBool anyOfSelectionHasProp = PR_FALSE;
|
||||
PRBool allOfSelectionHasProp = PR_FALSE;
|
||||
|
||||
rv = mEditor->GetInlineProperty(styleAtom, nsnull, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp);
|
||||
testBoolean = allOfSelectionHasProp; // change this to alter the behaviour
|
||||
|
||||
rv = mEditor->GetInlineProperty(styleAtom, nsnull, nsnull, firstOfSelectionHasProp, anyOfSelectionHasProp, allOfSelectionHasProp);
|
||||
testBoolean = allOfSelectionHasProp; // change this to alter the behaviour
|
||||
}
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (testBoolean != ioState)
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include "nsIContent.h"
|
||||
|
||||
#ifdef NS_DEBUG
|
||||
static PRBool gNoisy = PR_TRUE;
|
||||
static PRBool gNoisy = PR_FALSE;
|
||||
#else
|
||||
static const PRBool gNoisy = PR_FALSE;
|
||||
#endif
|
||||
|
|
|
@ -39,7 +39,9 @@ PlaceholderTxn::PlaceholderTxn() : EditAggregateTxn(),
|
|||
mAbsorb(PR_TRUE),
|
||||
mForwarding(nsnull),
|
||||
mIMETextTxn(nsnull),
|
||||
mCommitted(PR_FALSE)
|
||||
mCommitted(PR_FALSE),
|
||||
mStartSel(nsnull),
|
||||
mEndSel()
|
||||
{
|
||||
SetTransactionDescriptionID( kTransactionID );
|
||||
/* log description initialized in parent constructor */
|
||||
|
@ -48,6 +50,7 @@ PlaceholderTxn::PlaceholderTxn() : EditAggregateTxn(),
|
|||
|
||||
PlaceholderTxn::~PlaceholderTxn()
|
||||
{
|
||||
delete mStartSel;
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(PlaceholderTxn, EditAggregateTxn)
|
||||
|
@ -72,15 +75,15 @@ NS_IMETHODIMP PlaceholderTxn::QueryInterface(REFNSIID aIID, void** aInstancePtr)
|
|||
}
|
||||
|
||||
NS_IMETHODIMP PlaceholderTxn::Init(nsWeakPtr aPresShellWeak, nsIAtom *aName,
|
||||
nsIDOMNode *aStartNode, PRInt32 aStartOffset)
|
||||
nsSelectionState *aSelState)
|
||||
{
|
||||
NS_ASSERTION(aPresShellWeak, "bad args");
|
||||
if (!aPresShellWeak) return NS_ERROR_NULL_POINTER;
|
||||
if (!aPresShellWeak || !aSelState) return NS_ERROR_NULL_POINTER;
|
||||
|
||||
mPresShellWeak = aPresShellWeak;
|
||||
mName = aName;
|
||||
mStartNode = do_QueryInterface(aStartNode);
|
||||
mStartOffset = aStartOffset;
|
||||
mStartSel = aSelState;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -92,8 +95,38 @@ NS_IMETHODIMP PlaceholderTxn::Do(void)
|
|||
|
||||
NS_IMETHODIMP PlaceholderTxn::Undo(void)
|
||||
{
|
||||
// using this to debug
|
||||
return EditAggregateTxn::Undo();
|
||||
// undo txns
|
||||
nsresult res = EditAggregateTxn::Undo();
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// now restore selection
|
||||
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
|
||||
if (!ps) return NS_ERROR_NOT_INITIALIZED;
|
||||
nsCOMPtr<nsIDOMSelection> selection;
|
||||
res = ps->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection));
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (!selection) return NS_ERROR_NULL_POINTER;
|
||||
if (!mStartSel) return NS_ERROR_NULL_POINTER;
|
||||
res = mStartSel->RestoreSelection(selection);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP PlaceholderTxn::Redo(void)
|
||||
{
|
||||
// redo txns
|
||||
nsresult res = EditAggregateTxn::Redo();
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// now restore selection
|
||||
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
|
||||
if (!ps) return NS_ERROR_NOT_INITIALIZED;
|
||||
nsCOMPtr<nsIDOMSelection> selection;
|
||||
res = ps->GetSelection(SELECTION_NORMAL, getter_AddRefs(selection));
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (!selection) return NS_ERROR_NULL_POINTER;
|
||||
res = mEndSel.RestoreSelection(selection);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
@ -146,6 +179,10 @@ NS_IMETHODIMP PlaceholderTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransact
|
|||
AppendChild(editTxn);
|
||||
}
|
||||
*aDidMerge = PR_TRUE;
|
||||
// RememberEndingSelection();
|
||||
// efficiency hack: no need to remember selection here, as we haven't yet
|
||||
// finished the inital batch and we know we will be told when the batch ends.
|
||||
// we can remeber the selection then.
|
||||
if (gNoisy) { printf("Placeholder txn assimilated %p\n", aTransaction); }
|
||||
}
|
||||
else
|
||||
|
@ -155,27 +192,31 @@ NS_IMETHODIMP PlaceholderTxn::Merge(PRBool *aDidMerge, nsITransaction *aTransact
|
|||
(mName.get() == nsHTMLEditor::gDeleteTxnName))
|
||||
&& !mCommitted )
|
||||
{
|
||||
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(NS_GET_IID(nsIAbsorbingTransaction), getter_AddRefs(plcTxn));
|
||||
if (plcTxn)
|
||||
// but only if this placeholder started with a collapsed selection
|
||||
if (mStartSel->IsCollapsed())
|
||||
{
|
||||
nsCOMPtr<nsIAtom> atom;
|
||||
plcTxn->GetTxnName(getter_AddRefs(atom));
|
||||
if (atom && (atom == mName))
|
||||
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(NS_GET_IID(nsIAbsorbingTransaction), getter_AddRefs(plcTxn));
|
||||
if (plcTxn)
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> otherTxnStartNode;
|
||||
PRInt32 otherTxnStartOffset;
|
||||
res = plcTxn->GetStartNodeAndOffset(&otherTxnStartNode, &otherTxnStartOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
if ((otherTxnStartNode == mEndNode) && (otherTxnStartOffset == mEndOffset))
|
||||
nsCOMPtr<nsIAtom> atom;
|
||||
plcTxn->GetTxnName(getter_AddRefs(atom));
|
||||
if (atom && (atom == mName))
|
||||
{
|
||||
mAbsorb = PR_TRUE; // we need to start absorbing again
|
||||
plcTxn->ForwardEndBatchTo(this);
|
||||
AppendChild(editTxn);
|
||||
*aDidMerge = PR_TRUE;
|
||||
// check if start selection of next placeholder matches
|
||||
// end selection of this placeholder
|
||||
PRBool isSame;
|
||||
plcTxn->StartSelectionEquals(&mEndSel, &isSame);
|
||||
if (isSame)
|
||||
{
|
||||
mAbsorb = PR_TRUE; // we need to start absorbing again
|
||||
plcTxn->ForwardEndBatchTo(this);
|
||||
AppendChild(editTxn);
|
||||
RememberEndingSelection();
|
||||
*aDidMerge = PR_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -189,11 +230,17 @@ NS_IMETHODIMP PlaceholderTxn::GetTxnName(nsIAtom **aName)
|
|||
return GetName(aName);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP PlaceholderTxn::GetStartNodeAndOffset(nsCOMPtr<nsIDOMNode> *aTxnStartNode, PRInt32 *aTxnStartOffset)
|
||||
NS_IMETHODIMP PlaceholderTxn::StartSelectionEquals(nsSelectionState *aSelState, PRBool *aResult)
|
||||
{
|
||||
if (!aTxnStartNode || !aTxnStartOffset) return NS_ERROR_NULL_POINTER;
|
||||
*aTxnStartNode = mStartNode;
|
||||
*aTxnStartOffset = mStartOffset;
|
||||
// determine if starting selection matches the given selection state.
|
||||
// note that we only care about collapsed selections.
|
||||
if (!aResult || !aSelState) return NS_ERROR_NULL_POINTER;
|
||||
if (!mStartSel->IsCollapsed() || !aSelState->IsCollapsed())
|
||||
{
|
||||
*aResult = PR_FALSE;
|
||||
return NS_OK;
|
||||
}
|
||||
*aResult = mStartSel->IsEqual(aSelState);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -207,21 +254,8 @@ NS_IMETHODIMP PlaceholderTxn::EndPlaceHolderBatch()
|
|||
if (plcTxn) plcTxn->EndPlaceHolderBatch();
|
||||
}
|
||||
|
||||
// if we are a typing or IME or deleting transaction, remember our selection state
|
||||
if ( (mName.get() == nsHTMLEditor::gTypingTxnName) ||
|
||||
(mName.get() == nsHTMLEditor::gIMETxnName) ||
|
||||
(mName.get() == nsHTMLEditor::gDeleteTxnName) )
|
||||
{
|
||||
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;
|
||||
// remember our selection state.
|
||||
return RememberEndingSelection();
|
||||
};
|
||||
|
||||
NS_IMETHODIMP PlaceholderTxn::ForwardEndBatchTo(nsIAbsorbingTransaction *aForwardingAddress)
|
||||
|
@ -236,5 +270,17 @@ NS_IMETHODIMP PlaceholderTxn::Commit()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP PlaceholderTxn::RememberEndingSelection()
|
||||
{
|
||||
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 = mEndSel.SaveSelection(selection);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#define AggregatePlaceholderTxn_h__
|
||||
|
||||
#include "EditAggregateTxn.h"
|
||||
#include "nsEditorUtils.h"
|
||||
#include "nsIAbsorbingTransaction.h"
|
||||
#include "nsIDOMNode.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
@ -67,16 +68,18 @@ public:
|
|||
NS_IMETHOD Do(void);
|
||||
|
||||
NS_IMETHOD Undo(void);
|
||||
|
||||
NS_IMETHOD Redo(void);
|
||||
|
||||
NS_IMETHOD Merge(PRBool *aDidMerge, nsITransaction *aTransaction);
|
||||
|
||||
// ------------ nsIAbsorbingTransaction -----------------------
|
||||
|
||||
NS_IMETHOD Init(nsWeakPtr aPresShellWeak, nsIAtom *aName, nsIDOMNode *aStartNode, PRInt32 aStartOffset);
|
||||
NS_IMETHOD Init(nsWeakPtr aPresShellWeak, nsIAtom *aName, nsSelectionState *aSelState);
|
||||
|
||||
NS_IMETHOD GetTxnName(nsIAtom **aName);
|
||||
|
||||
NS_IMETHOD GetStartNodeAndOffset(nsCOMPtr<nsIDOMNode> *aTxnStartNode, PRInt32 *aTxnStartOffset);
|
||||
NS_IMETHOD StartSelectionEquals(nsSelectionState *aSelState, PRBool *aResult);
|
||||
|
||||
NS_IMETHOD EndPlaceHolderBatch();
|
||||
|
||||
|
@ -84,6 +87,8 @@ public:
|
|||
|
||||
NS_IMETHOD Commit();
|
||||
|
||||
NS_IMETHOD RememberEndingSelection();
|
||||
|
||||
friend class TransactionFactory;
|
||||
|
||||
enum { kTransactionID = 11260 };
|
||||
|
@ -93,12 +98,15 @@ protected:
|
|||
/** the presentation shell, which we'll need to get the selection */
|
||||
nsWeakPtr mPresShellWeak; // weak reference to the nsIPresShell
|
||||
PRBool mAbsorb; // do we auto absorb any and all transaction?
|
||||
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;
|
||||
IMETextTxn *mIMETextTxn; // first IME txn in this placeholder - used for IME merging
|
||||
// non-owning for now - cant nsCOMPtr it due to broken transaction interfaces
|
||||
PRBool mCommitted; // do we stop auto absorbing any matching placeholder txns?
|
||||
// these next two members store the state of the selection in a safe way.
|
||||
// selection at the start of the txn is stored, as is the selection at the end.
|
||||
// This is so that Undo() and Redo() can restore the selection properly.
|
||||
nsSelectionState *mStartSel; // use a pointer because this is constructed before we exist
|
||||
nsSelectionState mEndSel;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -137,6 +137,169 @@ const PRUnichar nbsp = 160;
|
|||
PRInt32 nsEditor::gInstanceCount = 0;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* class for recording selection info. stores selection as collection of
|
||||
* { {startnode, startoffset} , {endnode, endoffset} } tuples. Cant store
|
||||
* ranges since dom gravity will possibly change the ranges.
|
||||
*/
|
||||
nsSelectionState::nsSelectionState() {}
|
||||
|
||||
nsSelectionState::~nsSelectionState()
|
||||
{
|
||||
// free any items in the array
|
||||
SelRangeStore *item;
|
||||
while (item = (SelRangeStore*)mArray.ElementAt(0))
|
||||
{
|
||||
delete item;
|
||||
mArray.RemoveElementAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSelectionState::SaveSelection(nsIDOMSelection *aSel)
|
||||
{
|
||||
if (!aSel) return NS_ERROR_NULL_POINTER;
|
||||
nsresult res = NS_OK;
|
||||
PRInt32 i,rangeCount, arrayCount = mArray.Count();
|
||||
SelRangeStore *item;
|
||||
aSel->GetRangeCount(&rangeCount);
|
||||
|
||||
// if we need more items in the array, new them
|
||||
if (arrayCount<rangeCount)
|
||||
{
|
||||
PRInt32 count = rangeCount-arrayCount;
|
||||
for (i=0; i<count; i++)
|
||||
{
|
||||
item = new SelRangeStore;
|
||||
mArray.AppendElement(item);
|
||||
}
|
||||
}
|
||||
|
||||
// else if we have too many, delete them
|
||||
else if (rangeCount>arrayCount)
|
||||
{
|
||||
while (item = (SelRangeStore*)mArray.ElementAt(rangeCount))
|
||||
{
|
||||
delete item;
|
||||
mArray.RemoveElementAt(rangeCount);
|
||||
}
|
||||
}
|
||||
|
||||
// now store the selection ranges
|
||||
for (i=0; i<rangeCount; i++)
|
||||
{
|
||||
item = (SelRangeStore*)mArray.ElementAt(i);
|
||||
if (!item) return NS_ERROR_UNEXPECTED;
|
||||
nsCOMPtr<nsIDOMRange> range;
|
||||
res = aSel->GetRangeAt(i, getter_AddRefs(range));
|
||||
item->StoreRange(range);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSelectionState::RestoreSelection(nsIDOMSelection *aSel)
|
||||
{
|
||||
if (!aSel) return NS_ERROR_NULL_POINTER;
|
||||
nsresult res = NS_OK;
|
||||
PRInt32 i, arrayCount = mArray.Count();
|
||||
SelRangeStore *item;
|
||||
|
||||
// clear ot cur selection
|
||||
aSel->ClearSelection();
|
||||
|
||||
// set the selection ranges anew
|
||||
for (i=0; i<arrayCount; i++)
|
||||
{
|
||||
item = (SelRangeStore*)mArray.ElementAt(i);
|
||||
if (!item) return NS_ERROR_UNEXPECTED;
|
||||
nsCOMPtr<nsIDOMRange> range;
|
||||
item->GetRange(&range);
|
||||
if (!range) return NS_ERROR_UNEXPECTED;
|
||||
|
||||
res = aSel->AddRange(range);
|
||||
if(NS_FAILED(res)) return res;
|
||||
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsSelectionState::IsCollapsed()
|
||||
{
|
||||
if (1 != mArray.Count()) return PR_FALSE;
|
||||
SelRangeStore *item;
|
||||
item = (SelRangeStore*)mArray.ElementAt(0);
|
||||
if (!item) return PR_FALSE;
|
||||
nsCOMPtr<nsIDOMRange> range;
|
||||
item->GetRange(&range);
|
||||
if (!range) return PR_FALSE;
|
||||
PRBool bIsCollapsed;
|
||||
range->GetIsCollapsed(&bIsCollapsed);
|
||||
return bIsCollapsed;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsSelectionState::IsEqual(nsSelectionState *aSelState)
|
||||
{
|
||||
if (!aSelState) return NS_ERROR_NULL_POINTER;
|
||||
PRInt32 i, myCount = mArray.Count(), itsCount = aSelState->mArray.Count();
|
||||
if (myCount != itsCount) return PR_FALSE;
|
||||
if (myCount < 1) return PR_FALSE;
|
||||
|
||||
SelRangeStore *myItem, *itsItem;
|
||||
|
||||
for (i=0; i<myCount; i++)
|
||||
{
|
||||
myItem = (SelRangeStore*)mArray.ElementAt(0);
|
||||
itsItem = (SelRangeStore*)(aSelState->mArray.ElementAt(0));
|
||||
if (!myItem || !itsItem) return PR_FALSE;
|
||||
|
||||
nsCOMPtr<nsIDOMRange> myRange, itsRange;
|
||||
myItem->GetRange(&myRange);
|
||||
itsItem->GetRange(&itsRange);
|
||||
if (!myRange || !itsRange) return PR_FALSE;
|
||||
|
||||
PRInt32 compResult;
|
||||
myRange->CompareEndPoints(nsIDOMRange::START_TO_START, itsRange, &compResult);
|
||||
if (compResult) return PR_FALSE;
|
||||
myRange->CompareEndPoints(nsIDOMRange::END_TO_END, itsRange, &compResult);
|
||||
if (compResult) return PR_FALSE;
|
||||
}
|
||||
// if we got here, they are equal
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
nsresult SelRangeStore::StoreRange(nsIDOMRange *aRange)
|
||||
{
|
||||
if (!aRange) return NS_ERROR_NULL_POINTER;
|
||||
aRange->GetStartParent(getter_AddRefs(startNode));
|
||||
aRange->GetEndParent(getter_AddRefs(endNode));
|
||||
aRange->GetStartOffset(&startOffset);
|
||||
aRange->GetEndOffset(&endOffset);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult SelRangeStore::GetRange(nsCOMPtr<nsIDOMRange> *outRange)
|
||||
{
|
||||
if (!outRange) return NS_ERROR_NULL_POINTER;
|
||||
nsresult res = nsComponentManager::CreateInstance(kCRangeCID,
|
||||
nsnull,
|
||||
NS_GET_IID(nsIDOMRange),
|
||||
getter_AddRefs(*outRange));
|
||||
if(NS_FAILED(res)) return res;
|
||||
|
||||
res = (*outRange)->SetStart(startNode, startOffset);
|
||||
if(NS_FAILED(res)) return res;
|
||||
|
||||
res = (*outRange)->SetEnd(endNode, endOffset);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
//class implementations are in order they are declared in nsEditor.h
|
||||
|
||||
nsEditor::nsEditor()
|
||||
|
@ -146,8 +309,7 @@ nsEditor::nsEditor()
|
|||
, mPlaceHolderTxn(nsnull)
|
||||
, mPlaceHolderName(nsnull)
|
||||
, mPlaceHolderBatch(0)
|
||||
, mTxnStartNode(nsnull)
|
||||
, mTxnStartOffset(0)
|
||||
, mSelState(nsnull)
|
||||
, mShouldTxnSetSelection(PR_TRUE)
|
||||
, mBodyElement(nsnull)
|
||||
, mInIMEMode(PR_FALSE)
|
||||
|
@ -376,8 +538,9 @@ nsEditor::Do(nsITransaction *aTxn)
|
|||
|
||||
// save off weak reference to placeholder txn
|
||||
mPlaceHolderTxn = getter_AddRefs( NS_GetWeakReference(plcTxn) );
|
||||
plcTxn->Init(mPresShellWeak, mPlaceHolderName, mTxnStartNode, mTxnStartOffset);
|
||||
|
||||
plcTxn->Init(mPresShellWeak, mPlaceHolderName, mSelState);
|
||||
mSelState = nsnull; // placeholder txn took ownership of this pointer
|
||||
|
||||
// finally we QI to an nsITransaction since that's what Do() expects
|
||||
nsCOMPtr<nsITransaction> theTxn = do_QueryInterface(plcTxn);
|
||||
nsITransaction* txn = theTxn;
|
||||
|
@ -588,17 +751,8 @@ nsEditor::BeginPlaceHolderTransaction(nsIAtom *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/deleting 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;
|
||||
}
|
||||
mSelState = new nsSelectionState();
|
||||
mSelState->SaveSelection(selection);
|
||||
}
|
||||
mPlaceHolderBatch++;
|
||||
|
||||
|
@ -613,8 +767,13 @@ nsEditor::EndPlaceHolderTransaction()
|
|||
{
|
||||
// time to turn off the batch
|
||||
EndUpdateViewBatch();
|
||||
mTxnStartNode = nsnull;
|
||||
mTxnStartOffset = 0;
|
||||
if (mSelState)
|
||||
{
|
||||
// we saved the selection state, but never got to hand it to placeholder
|
||||
// (else we ould have nulled out this pointer), so destroy it to prevent leaks.
|
||||
delete mSelState;
|
||||
mSelState = nsnull;
|
||||
}
|
||||
if (mPlaceHolderTxn) // we might have never made a placeholder if no action took place
|
||||
{
|
||||
nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryReferent(mPlaceHolderTxn);
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "nsICSSStyleSheet.h"
|
||||
#include "nsIDTD.h"
|
||||
#include "nsIDOMElement.h"
|
||||
#include "nsVoidArray.h"
|
||||
|
||||
class nsIEditActionListener;
|
||||
class nsIDocumentStateListener;
|
||||
|
@ -70,6 +71,38 @@ class AddStyleSheetTxn;
|
|||
class RemoveStyleSheetTxn;
|
||||
class nsFileSpec;
|
||||
|
||||
/***************************************************************************
|
||||
* class for recording selection info. stores selection as collection of
|
||||
* { {startnode, startoffset} , {endnode, endoffset} } tuples. Cant store
|
||||
* ranges since dom gravity will possibly change the ranges.
|
||||
*/
|
||||
|
||||
// first a helper struct for saving/setting ranges
|
||||
struct SelRangeStore
|
||||
{
|
||||
nsresult StoreRange(nsIDOMRange *aRange);
|
||||
nsresult GetRange(nsCOMPtr<nsIDOMRange> *outRange);
|
||||
|
||||
nsCOMPtr<nsIDOMNode> startNode;
|
||||
PRInt32 startOffset;
|
||||
nsCOMPtr<nsIDOMNode> endNode;
|
||||
PRInt32 endOffset;
|
||||
};
|
||||
|
||||
class nsSelectionState
|
||||
{
|
||||
public:
|
||||
|
||||
nsSelectionState();
|
||||
~nsSelectionState();
|
||||
|
||||
nsresult SaveSelection(nsIDOMSelection *aSel);
|
||||
nsresult RestoreSelection(nsIDOMSelection *aSel);
|
||||
PRBool IsCollapsed();
|
||||
PRBool IsEqual(nsSelectionState *aSelState);
|
||||
|
||||
nsVoidArray mArray;
|
||||
};
|
||||
|
||||
/** implementation of an editor object. it will be the controler/focal point
|
||||
* for the main editor services. i.e. the GUIManager, publishing, transaction
|
||||
|
@ -653,8 +686,7 @@ protected:
|
|||
nsWeakPtr mPlaceHolderTxn; // weak reference to placeholder for begin/end batch purposes
|
||||
nsIAtom *mPlaceHolderName; // name of placeholder transaction
|
||||
PRInt32 mPlaceHolderBatch; // nesting count for batching
|
||||
nsCOMPtr<nsIDOMNode> mTxnStartNode; // saved selection info to pass to placeholder at init time
|
||||
PRInt32 mTxnStartOffset; // " " " "
|
||||
nsSelectionState *mSelState; // saved selection state for placeholder txn batching
|
||||
PRBool mShouldTxnSetSelection; // turn off for conservative selection adjustment by txns
|
||||
nsCOMPtr<nsIDOMElement> mBodyElement; // cached body node
|
||||
//
|
||||
|
|
|
@ -30,31 +30,13 @@
|
|||
#include "nsIDOMSelection.h"
|
||||
#include "nsIEditor.h"
|
||||
#include "nsIAtom.h"
|
||||
|
||||
#include "nsVoidArray.h"
|
||||
#include "nsEditor.h"
|
||||
|
||||
class nsAutoEditBatch
|
||||
{
|
||||
private:
|
||||
nsCOMPtr<nsIEditor> mEd;
|
||||
public:
|
||||
nsAutoEditBatch( nsIEditor *aEd) : mEd(do_QueryInterface(aEd))
|
||||
{ if (mEd) mEd->BeginTransaction(); }
|
||||
~nsAutoEditBatch() { if (mEd) mEd->EndTransaction(); }
|
||||
};
|
||||
|
||||
class nsAutoEditMayBatch
|
||||
{
|
||||
private:
|
||||
nsCOMPtr<nsIEditor> mEd;
|
||||
PRBool mDidBatch;
|
||||
public:
|
||||
nsAutoEditMayBatch( nsIEditor *aEd) : mEd(do_QueryInterface(aEd)), mDidBatch(PR_FALSE) {}
|
||||
~nsAutoEditMayBatch() { if (mEd && mDidBatch) mEd->EndTransaction(); }
|
||||
|
||||
void batch() { if (mEd && !mDidBatch) {mEd->BeginTransaction(); mDidBatch=PR_TRUE;} }
|
||||
};
|
||||
|
||||
/***************************************************************************
|
||||
* stack based helper class for batching a collection of txns inside a
|
||||
* placeholder txn.
|
||||
*/
|
||||
class nsAutoPlaceHolderBatch
|
||||
{
|
||||
private:
|
||||
|
@ -66,6 +48,22 @@ class nsAutoPlaceHolderBatch
|
|||
};
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* stack based helper class for batching a collection of txns.
|
||||
* Note: I changed this to use placeholder batching so that we get
|
||||
* proper selection save/restore across undo/redo.
|
||||
*/
|
||||
class nsAutoEditBatch : public nsAutoPlaceHolderBatch
|
||||
{
|
||||
public:
|
||||
nsAutoEditBatch( nsIEditor *aEd) : nsAutoPlaceHolderBatch(aEd,nsnull) {}
|
||||
~nsAutoEditBatch() {}
|
||||
};
|
||||
|
||||
/***************************************************************************
|
||||
* stack based helper class for saving/restoring selection. Note that this
|
||||
* assumes that the nodes involved are still around afterwards!
|
||||
*/
|
||||
class nsAutoSelectionReset
|
||||
{
|
||||
private:
|
||||
|
@ -138,5 +136,4 @@ class nsAutoTxnsConserveSelection
|
|||
};
|
||||
|
||||
|
||||
|
||||
#endif // nsEditorUtils_h__
|
||||
|
|
|
@ -40,6 +40,8 @@ Transaction interface to outside world
|
|||
#include "nsIAtom.h"
|
||||
#include "nsIDOMNode.h"
|
||||
|
||||
class nsSelectionState;
|
||||
|
||||
/**
|
||||
* A transaction interface mixin - for transactions that can support.
|
||||
* the placeholder absorbtion idiom.
|
||||
|
@ -49,13 +51,13 @@ public:
|
|||
|
||||
static const nsIID& GetIID() { static nsIID iid = NS_IABSORBINGTRANSACTION_IID; return iid; }
|
||||
|
||||
NS_IMETHOD Init(nsWeakPtr aPresShellWeak, nsIAtom *aName, nsIDOMNode *aStartNode, PRInt32 aStartOffset)=0;
|
||||
NS_IMETHOD Init(nsWeakPtr aPresShellWeak, nsIAtom *aName, nsSelectionState *aSelState)=0;
|
||||
|
||||
NS_IMETHOD EndPlaceHolderBatch()=0;
|
||||
|
||||
NS_IMETHOD GetTxnName(nsIAtom **aName)=0;
|
||||
|
||||
NS_IMETHOD GetStartNodeAndOffset(nsCOMPtr<nsIDOMNode> *aTxnStartNode, PRInt32 *aTxnStartOffset)=0;
|
||||
NS_IMETHOD StartSelectionEquals(nsSelectionState *aSelState, PRBool *aResult)=0;
|
||||
|
||||
NS_IMETHOD ForwardEndBatchTo(nsIAbsorbingTransaction *aForwardingAddress)=0;
|
||||
|
||||
|
|
|
@ -2748,10 +2748,6 @@ nsHTMLEditRules::ReturnInListItem(nsIDOMSelection *aSelection,
|
|||
res = aSelection->Collapse(listparent,offset+1);
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// now attempt to adjust selection to a text node.
|
||||
// if we fail, then that means we need toinsert a break
|
||||
res = AdjustSelection(aSelection, nsIEditor::eNext);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// get the selection location
|
||||
nsCOMPtr<nsIDOMNode> selNode;
|
||||
PRInt32 selOffset;
|
||||
|
|
|
@ -218,6 +218,11 @@ nsHTMLEditor::nsHTMLEditor()
|
|||
{
|
||||
// Done in nsEditor
|
||||
// NS_INIT_REFCNT();
|
||||
mBoldAtom = getter_AddRefs(NS_NewAtom("b"));
|
||||
mItalicAtom = getter_AddRefs(NS_NewAtom("i"));
|
||||
mUnderlineAtom = getter_AddRefs(NS_NewAtom("u"));
|
||||
mFontAtom = getter_AddRefs(NS_NewAtom("font"));
|
||||
|
||||
if (!gTypingTxnName)
|
||||
gTypingTxnName = NS_NewAtom("Typing");
|
||||
else
|
||||
|
@ -1053,6 +1058,7 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty,
|
|||
|
||||
PRBool isCollapsed;
|
||||
selection->GetIsCollapsed(&isCollapsed);
|
||||
nsCOMPtr<nsIDOMNode> collapsedNode;
|
||||
nsCOMPtr<nsIEnumerator> enumerator;
|
||||
result = selection->GetEnumerator(getter_AddRefs(enumerator));
|
||||
if (NS_FAILED(result)) return result;
|
||||
|
@ -1066,7 +1072,45 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty,
|
|||
if ((NS_SUCCEEDED(result)) && currentItem)
|
||||
{
|
||||
PRBool firstNodeInRange = PR_TRUE; // for each range, set a flag
|
||||
nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
|
||||
nsCOMPtr<nsIDOMRange> range(do_QueryInterface(currentItem));
|
||||
|
||||
if (isCollapsed)
|
||||
{
|
||||
// efficiency hack. we cache prior results for being collapsed in a given text node.
|
||||
// this speeds up typing. Note that other parts of the editor code have to clear out
|
||||
// this cache after certain actions.
|
||||
range->GetStartParent(getter_AddRefs(collapsedNode));
|
||||
if (!collapsedNode) return NS_ERROR_FAILURE;
|
||||
// refresh the cache if we need to
|
||||
if (collapsedNode != mCachedNode) CacheInlineStyles(collapsedNode);
|
||||
// cache now current, use it! But *or* it with typeInState results...
|
||||
PRBool isSet;
|
||||
if (aProperty == mBoldAtom.get())
|
||||
{
|
||||
GetTypingState(aProperty, isSet);
|
||||
aFirst = aAny = aAll = (mCachedBoldStyle || isSet);
|
||||
return NS_OK;
|
||||
}
|
||||
else if (aProperty == mItalicAtom.get())
|
||||
{
|
||||
GetTypingState(aProperty, isSet);
|
||||
aFirst = aAny = aAll = (mCachedItalicStyle || isSet);
|
||||
return NS_OK;
|
||||
}
|
||||
else if (aProperty == mUnderlineAtom.get())
|
||||
{
|
||||
GetTypingState(aProperty, isSet);
|
||||
aFirst = aAny = aAll = (mCachedUnderlineStyle || isSet);
|
||||
return NS_OK;
|
||||
}
|
||||
else if (aProperty == mFontAtom.get())
|
||||
{
|
||||
// MOOSE!
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// either non-collapsed selection or no cached value: do it the hard way
|
||||
nsCOMPtr<nsIContentIterator> iter;
|
||||
result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull,
|
||||
nsIContentIterator::GetIID(),
|
||||
|
@ -1138,7 +1182,8 @@ NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty,
|
|||
iter->CurrentNode(getter_AddRefs(content));
|
||||
}
|
||||
}
|
||||
if (PR_FALSE==aAny) { // make sure that if none of the selection is set, we don't report all is set
|
||||
if (PR_FALSE==aAny)
|
||||
{ // make sure that if none of the selection is set, we don't report all is set
|
||||
aAll = PR_FALSE;
|
||||
}
|
||||
//if (gNoisy) { printf(" returning first=%d any=%d all=%d\n", aFirst, aAny, aAll); }
|
||||
|
@ -4450,6 +4495,8 @@ void nsHTMLEditor::ApplyStyleSheetToPresShellDocument(nsICSSStyleSheet* aSheet,
|
|||
NS_IMETHODIMP
|
||||
nsHTMLEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection)
|
||||
{
|
||||
if (! ((opID==kOpInsertText) || (opID==kOpInsertIMEText)) )
|
||||
ClearInlineStylesCache();
|
||||
if (mRules) return mRules->BeforeEdit(opID, aDirection);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -4460,6 +4507,8 @@ nsHTMLEditor::StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection)
|
|||
NS_IMETHODIMP
|
||||
nsHTMLEditor::EndOperation(PRInt32 opID, nsIEditor::EDirection aDirection)
|
||||
{
|
||||
if (! ((opID==kOpInsertText) || (opID==kOpInsertIMEText)) )
|
||||
ClearInlineStylesCache();
|
||||
if (mRules) return mRules->AfterEdit(opID, aDirection);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -5882,6 +5931,22 @@ nsHTMLEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr<nsIDOMNode> &parent
|
|||
#pragma mark -
|
||||
#endif
|
||||
|
||||
void nsHTMLEditor::CacheInlineStyles(nsIDOMNode *aNode)
|
||||
{
|
||||
if (!aNode) return;
|
||||
nsCOMPtr<nsIDOMNode> resultNode;
|
||||
mCachedNode = do_QueryInterface(aNode);
|
||||
IsTextPropertySetByContent(aNode, mBoldAtom, 0, 0, mCachedBoldStyle, getter_AddRefs(resultNode));
|
||||
IsTextPropertySetByContent(aNode, mItalicAtom, 0, 0, mCachedItalicStyle, getter_AddRefs(resultNode));
|
||||
IsTextPropertySetByContent(aNode, mUnderlineAtom, 0, 0, mCachedUnderlineStyle, getter_AddRefs(resultNode));
|
||||
|
||||
}
|
||||
|
||||
void nsHTMLEditor::ClearInlineStylesCache()
|
||||
{
|
||||
mCachedNode = nsnull;
|
||||
}
|
||||
|
||||
#ifdef PRE_NODE_IN_BODY
|
||||
nsCOMPtr<nsIDOMElement> nsHTMLEditor::FindPreElement()
|
||||
{
|
||||
|
@ -6890,7 +6955,7 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar
|
|||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
skippedEndNode = PR_TRUE;
|
||||
if (gNoisy) { printf("skipping end node because aProperty not set.\n"); }
|
||||
}
|
||||
|
@ -6903,13 +6968,16 @@ nsHTMLEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode *aStar
|
|||
}
|
||||
else
|
||||
{
|
||||
endNode = GetChildAt(aEndNode, aEndOffset);
|
||||
// experimental hack: need to rewrite all of this
|
||||
// MOOSE!
|
||||
if (aEndOffset<0) aEndOffset=1;
|
||||
endNode = GetChildAt(aEndNode, aEndOffset-1);
|
||||
parent = do_QueryInterface(aEndNode);
|
||||
if (!endNode || !parent) {return NS_ERROR_NULL_POINTER;}
|
||||
IsTextPropertySetByContent(endNode, aPropName, aAttribute, nsnull, textPropertySet, getter_AddRefs(resultNode));
|
||||
if (PR_TRUE==textPropertySet)
|
||||
{
|
||||
result = RemoveTextPropertiesForNode(endNode, parent, aEndOffset, aEndOffset+1, aPropName, aAttribute);
|
||||
result = RemoveTextPropertiesForNode(endNode, parent, aEndOffset-1, aEndOffset, aPropName, aAttribute);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -306,7 +306,10 @@ protected:
|
|||
PRBool SetCaretInTableCell(nsIDOMElement* aElement);
|
||||
PRBool IsElementInBody(nsIDOMElement* aElement);
|
||||
|
||||
|
||||
// inline style caching
|
||||
void CacheInlineStyles(nsIDOMNode *aNode);
|
||||
void ClearInlineStylesCache();
|
||||
|
||||
// key event helpers
|
||||
NS_IMETHOD TabInTable(PRBool inIsShift, PRBool *outHandled);
|
||||
NS_IMETHOD CreateBR(nsIDOMNode *aNode, PRInt32 aOffset, nsCOMPtr<nsIDOMNode> *outBRNode);
|
||||
|
@ -544,6 +547,17 @@ protected:
|
|||
nsCOMPtr<nsIDOMEventListener> mFocusListenerP;
|
||||
PRBool mIsComposing;
|
||||
PRInt32 mMaxTextLength;
|
||||
|
||||
nsCOMPtr<nsIAtom> mBoldAtom;
|
||||
nsCOMPtr<nsIAtom> mItalicAtom;
|
||||
nsCOMPtr<nsIAtom> mUnderlineAtom;
|
||||
nsCOMPtr<nsIAtom> mFontAtom;
|
||||
nsCOMPtr<nsIDOMNode> mCachedNode;
|
||||
|
||||
PRBool mCachedBoldStyle;
|
||||
PRBool mCachedItalicStyle;
|
||||
PRBool mCachedUnderlineStyle;
|
||||
nsString mCachedFontName;
|
||||
|
||||
public:
|
||||
static nsIAtom *gTypingTxnName;
|
||||
|
|
Загрузка…
Ссылка в новой задаче