bug 66290: embedding work: refactor editor to allow smaller plaintext only library. Embedding clients may be interested inthis library if they want to embed a browser but not composer/mail-compose. Reduces library footprint for plaintext-only library by over 50% on all three platforms. r=fm; sr=kin

This commit is contained in:
jfrancis%netscape.com 2001-01-28 20:13:07 +00:00
parent 4745f05247
commit fb60392b41
58 changed files with 15114 additions and 11364 deletions

View File

@ -31,50 +31,83 @@ LIBRARY_NAME = editor
IS_COMPONENT = 1
REQUIRES = xpcom dom js locale layout uriloader widget txmgr htmlparser necko pref view appshell rdf webshell timer txtsvc intl lwbrk docshell chrome caps appcomps
CPPSRCS = \
nsEditor.cpp \
nsEditorService.cpp \
nsEditorController.cpp \
nsEditorCommands.cpp \
nsComposerCommands.cpp \
nsEditorUtils.cpp \
nsEditorRegistration.cpp \
nsEditorParserObserver.cpp \
nsTextEditRules.cpp \
nsHTMLEditUtils.cpp \
TextEditorTest.cpp \
nsHTMLEditRules.cpp \
nsEditorEventListeners.cpp \
nsEditorShellMouseListener.cpp \
nsEditProperty.cpp \
nsHTMLEditor.cpp \
ChangeAttributeTxn.cpp \
EditTxn.cpp \
EditAggregateTxn.cpp \
nsTableEditor.cpp \
InsertTextTxn.cpp \
PlaceholderTxn.cpp \
DeleteTextTxn.cpp \
CreateElementTxn.cpp \
InsertElementTxn.cpp \
DeleteElementTxn.cpp \
DeleteRangeTxn.cpp \
SplitElementTxn.cpp \
JoinElementTxn.cpp \
nsStyleSheetTxns.cpp \
TransactionFactory.cpp \
TypeInState.cpp \
nsInternetCiter.cpp \
nsAOLCiter.cpp \
nsWrapUtils.cpp \
nsInterfaceState.cpp \
nsEditorShell.cpp \
IMETextTxn.cpp \
IMECommitTxn.cpp \
nsHTMLEditorLog.cpp \
nsEditorTxnLog.cpp \
CPPSRCS = \
ChangeAttributeTxn.cpp \
CreateElementTxn.cpp \
DeleteElementTxn.cpp \
DeleteRangeTxn.cpp \
DeleteTextTxn.cpp \
EditAggregateTxn.cpp \
EditTxn.cpp \
IMECommitTxn.cpp \
IMETextTxn.cpp \
InsertElementTxn.cpp \
InsertTextTxn.cpp \
JoinElementTxn.cpp \
nsComposerCommands.cpp \
nsEditorCommands.cpp \
nsEditorController.cpp \
nsEditor.cpp \
nsEditorEventListeners.cpp \
nsEditorUtils.cpp \
nsEditProperty.cpp \
nsHTMLEditUtils.cpp \
nsPlaintextDataTransfer.cpp \
nsPlaintextEditor.cpp \
nsSelectionState.cpp \
nsStyleSheetTxns.cpp \
nsTextEditRules.cpp \
PlaceholderTxn.cpp \
SplitElementTxn.cpp \
TransactionFactory.cpp \
$(NULL)
# MOZ_BUILD_PLAINTEXT_EDITOR_CORE_ONLY=1
ifdef MOZ_BUILD_PLAINTEXT_EDITOR_CORE_ONLY
# We're only building the Core PlainText Editor Source so just include
# the plain text registration file.
CPPSRCS += nsTextEditorReg.cpp
else
# Building the full blown HTML Editor so add it's source files and objects:
CPPSRCS += nsAOLCiter.cpp \
nsEditorParserObserver.cpp \
nsEditorRegistration.cpp \
nsEditorShell.cpp \
nsEditorShellMouseListener.cpp \
nsEditorService.cpp \
nsHTMLDataTransfer.cpp \
nsHTMLEditor.cpp \
nsHTMLEditorStyle.cpp \
nsHTMLEditRules.cpp \
nsInterfaceState.cpp \
nsInternetCiter.cpp \
nsTableEditor.cpp \
nsWrapUtils.cpp \
TextEditorTest.cpp \
TypeInState.cpp \
$(NULL)
# Enable Editor API Logging!
ENABLE_EDITOR_API_LOG=1
ifdef ENABLE_EDITOR_API_LOG
CPPSRCS += nsHTMLEditorLog.cpp \
nsEditorTxnLog.cpp \
$(NULL)
DEFINES += -DENABLE_EDITOR_API_LOG
endif
endif
EXTRA_DSO_LDOPTS = \
$(MOZ_NECKO_UTIL_LIBS) \
$(MOZ_COMPONENT_LIBS) \
@ -83,5 +116,3 @@ EXTRA_DSO_LDOPTS = \
include $(topsrcdir)/config/rules.mk
DEFINES += -DENABLE_EDITOR_API_LOG

View File

@ -25,95 +25,127 @@ include <$(DEPTH)/config/config.mak>
LIBRARY_NAME=editor
CPPSRCS = \
nsEditor.cpp \
nsEditorService.cpp \
nsEditorController.cpp \
nsEditorCommands.cpp \
nsComposerCommands.cpp \
nsEditorUtils.cpp \
nsEditorRegistration.cpp \
nsEditorParserObserver.cpp \
nsTextEditRules.cpp \
nsHTMLEditRules.cpp \
nsHTMLEditUtils.cpp \
TextEditorTest.cpp \
nsEditorEventListeners.cpp \
nsEditorShellMouseListener.cpp \
nsEditProperty.cpp \
EditTxn.cpp \
EditAggregateTxn.cpp \
ChangeAttributeTxn.cpp \
InsertTextTxn.cpp \
DeleteTextTxn.cpp \
PlaceholderTxn.cpp \
CreateElementTxn.cpp \
InsertElementTxn.cpp \
DeleteElementTxn.cpp \
DeleteRangeTxn.cpp \
SplitElementTxn.cpp \
JoinElementTxn.cpp \
nsStyleSheetTxns.cpp \
TransactionFactory.cpp \
TypeInState.cpp \
nsHTMLEditor.cpp \
nsTableEditor.cpp \
nsInternetCiter.cpp \
nsAOLCiter.cpp \
nsWrapUtils.cpp \
nsInterfaceState.cpp \
nsEditorShell.cpp \
IMETextTxn.cpp \
IMECommitTxn.cpp \
$(NULL)
# Uncomment the line below, or define MOZ_BUILD_PLAINTEXT_EDITOR_CORE_ONLY
# in your environment, to build only the plain text editor core files:
# MOZ_BUILD_PLAINTEXT_EDITOR_CORE_ONLY=1
CPP_OBJS = \
.\$(OBJDIR)\nsEditor.obj \
.\$(OBJDIR)\nsEditorService.obj \
.\$(OBJDIR)\nsEditorController.obj \
.\$(OBJDIR)\nsEditorCommands.obj \
.\$(OBJDIR)\nsComposerCommands.obj \
.\$(OBJDIR)\nsEditorUtils.obj \
.\$(OBJDIR)\nsEditorRegistration.obj \
.\$(OBJDIR)\nsEditorParserObserver.obj \
.\$(OBJDIR)\nsTextEditRules.obj \
.\$(OBJDIR)\TextEditorTest.obj \
.\$(OBJDIR)\nsHTMLEditRules.obj \
.\$(OBJDIR)\nsHTMLEditUtils.obj \
.\$(OBJDIR)\nsEditorEventListeners.obj \
.\$(OBJDIR)\nsEditorShellMouseListener.obj \
.\$(OBJDIR)\nsEditProperty.obj \
.\$(OBJDIR)\EditTxn.obj \
.\$(OBJDIR)\EditAggregateTxn.obj \
.\$(OBJDIR)\ChangeAttributeTxn.obj \
.\$(OBJDIR)\InsertTextTxn.obj \
.\$(OBJDIR)\DeleteTextTxn.obj \
.\$(OBJDIR)\PlaceholderTxn.obj \
.\$(OBJDIR)\CreateElementTxn.obj \
.\$(OBJDIR)\InsertElementTxn.obj \
.\$(OBJDIR)\DeleteElementTxn.obj \
.\$(OBJDIR)\DeleteRangeTxn.obj \
.\$(OBJDIR)\SplitElementTxn.obj \
.\$(OBJDIR)\JoinElementTxn.obj \
.\$(OBJDIR)\nsStyleSheetTxns.obj \
.\$(OBJDIR)\TransactionFactory.obj \
.\$(OBJDIR)\TypeInState.obj \
.\$(OBJDIR)\nsHTMLEditor.obj \
.\$(OBJDIR)\nsTableEditor.obj \
.\$(OBJDIR)\nsInternetCiter.obj \
.\$(OBJDIR)\nsAOLCiter.obj \
.\$(OBJDIR)\nsWrapUtils.obj \
.\$(OBJDIR)\nsInterfaceState.obj \
.\$(OBJDIR)\nsEditorShell.obj \
.\$(OBJDIR)\IMETextTxn.obj \
.\$(OBJDIR)\IMECommitTxn.obj \
$(NULL)
# Editor Core Source Files and Objects:
MODULE=editor
CPPSRCS = \
ChangeAttributeTxn.cpp \
CreateElementTxn.cpp \
DeleteElementTxn.cpp \
DeleteRangeTxn.cpp \
DeleteTextTxn.cpp \
EditAggregateTxn.cpp \
EditTxn.cpp \
IMECommitTxn.cpp \
IMETextTxn.cpp \
InsertElementTxn.cpp \
InsertTextTxn.cpp \
JoinElementTxn.cpp \
nsComposerCommands.cpp \
nsEditorCommands.cpp \
nsEditorController.cpp \
nsEditor.cpp \
nsEditorEventListeners.cpp \
nsEditorUtils.cpp \
nsEditProperty.cpp \
nsHTMLEditUtils.cpp \
nsPlaintextDataTransfer.cpp \
nsPlaintextEditor.cpp \
nsSelectionState.cpp \
nsStyleSheetTxns.cpp \
nsTextEditRules.cpp \
PlaceholderTxn.cpp \
SplitElementTxn.cpp \
TransactionFactory.cpp \
$(NULL)
MAKE_OBJ_TYPE = DLL
DLLNAME = editor
DLL=.\$(OBJDIR)\$(DLLNAME).dll
CPP_OBJS = \
.\$(OBJDIR)\ChangeAttributeTxn.obj \
.\$(OBJDIR)\CreateElementTxn.obj \
.\$(OBJDIR)\DeleteElementTxn.obj \
.\$(OBJDIR)\DeleteRangeTxn.obj \
.\$(OBJDIR)\DeleteTextTxn.obj \
.\$(OBJDIR)\EditAggregateTxn.obj \
.\$(OBJDIR)\EditTxn.obj \
.\$(OBJDIR)\IMECommitTxn.obj \
.\$(OBJDIR)\IMETextTxn.obj \
.\$(OBJDIR)\InsertElementTxn.obj \
.\$(OBJDIR)\InsertTextTxn.obj \
.\$(OBJDIR)\JoinElementTxn.obj \
.\$(OBJDIR)\nsComposerCommands.obj \
.\$(OBJDIR)\nsEditorCommands.obj \
.\$(OBJDIR)\nsEditorController.obj \
.\$(OBJDIR)\nsEditorEventListeners.obj \
.\$(OBJDIR)\nsEditor.obj \
.\$(OBJDIR)\nsEditorUtils.obj \
.\$(OBJDIR)\nsEditProperty.obj \
.\$(OBJDIR)\nsHTMLEditUtils.obj \
.\$(OBJDIR)\nsPlaintextDataTransfer.obj \
.\$(OBJDIR)\nsPlaintextEditor.obj \
.\$(OBJDIR)\nsSelectionState.obj \
.\$(OBJDIR)\nsStyleSheetTxns.obj \
.\$(OBJDIR)\nsTextEditRules.obj \
.\$(OBJDIR)\PlaceholderTxn.obj \
.\$(OBJDIR)\SplitElementTxn.obj \
.\$(OBJDIR)\TransactionFactory.obj \
$(NULL)
!if defined(MOZ_BUILD_PLAINTEXT_EDITOR_CORE_ONLY)
# We're only building the Core PlainText Editor Source so just include
# the plain text registration file.
CPPSRCS = $(CPPSRCS) \
nsTextEditorReg.cpp \
$(NULL)
CPP_OBJS = $(CPP_OBJS) \
.\$(OBJDIR)\nsTextEditorReg.obj \
$(NULL)
!else
# Building the full blown HTML Editor so add it's source files and objects:
CPPSRCS = $(CPPSRCS) \
nsAOLCiter.cpp \
nsEditorParserObserver.cpp \
nsEditorRegistration.cpp \
nsEditorService.cpp \
nsEditorShell.cpp \
nsEditorShellMouseListener.cpp \
nsHTMLDataTransfer.cpp \
nsHTMLEditor.cpp \
nsHTMLEditorStyle.cpp \
nsHTMLEditRules.cpp \
nsInterfaceState.cpp \
nsInternetCiter.cpp \
nsTableEditor.cpp \
nsWrapUtils.cpp \
TextEditorTest.cpp \
TypeInState.cpp \
$(NULL)
CPP_OBJS = $(CPP_OBJS) \
.\$(OBJDIR)\nsAOLCiter.obj \
.\$(OBJDIR)\nsEditorParserObserver.obj \
.\$(OBJDIR)\nsEditorRegistration.obj \
.\$(OBJDIR)\nsEditorService.obj \
.\$(OBJDIR)\nsEditorShellMouseListener.obj \
.\$(OBJDIR)\nsEditorShell.obj \
.\$(OBJDIR)\nsHTMLDataTransfer.obj \
.\$(OBJDIR)\nsHTMLEditor.obj \
.\$(OBJDIR)\nsHTMLEditorStyle.obj \
.\$(OBJDIR)\nsHTMLEditRules.obj \
.\$(OBJDIR)\nsInterfaceState.obj \
.\$(OBJDIR)\nsInternetCiter.obj \
.\$(OBJDIR)\nsTableEditor.obj \
.\$(OBJDIR)\nsWrapUtils.obj \
.\$(OBJDIR)\TextEditorTest.obj \
.\$(OBJDIR)\TypeInState.obj \
$(NULL)
# Enable Editor API Logging!
ENABLE_EDITOR_API_LOG=1
@ -122,17 +154,26 @@ ENABLE_EDITOR_API_LOG=1
CPPSRCS = $(CPPSRCS) \
nsHTMLEditorLog.cpp \
nsEditorTxnLog.cpp \
$(NULL)
$(NULL)
CPP_OBJS = $(CPP_OBJS) \
.\$(OBJDIR)\nsHTMLEditorLog.obj \
.\$(OBJDIR)\nsEditorTxnLog.obj \
$(NULL)
$(NULL)
DEFINES = -DENABLE_EDITOR_API_LOG $(DEFINES)
!endif
!endif
MODULE=editor
MAKE_OBJ_TYPE = DLL
DLLNAME = editor
DLL=.\$(OBJDIR)\$(DLLNAME).dll
LCFLAGS = \
$(LCFLAGS) \
$(DEFINES) \

View File

@ -28,7 +28,7 @@
0xa6cf911d, 0x15b3, 0x11d2, \
{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32} }
class nsHTMLEditor;
class nsPlaintextEditor;
class nsISelection;
/***************************************************************************
@ -57,7 +57,7 @@ public:
//Interfaces for addref and release and queryinterface
//NOTE: Use NS_DECL_ISUPPORTS_INHERITED in any class inherited from nsIEditRules
NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags)=0;
NS_IMETHOD Init(nsPlaintextEditor *aEditor, PRUint32 aFlags)=0;
NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection)=0;
NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)=0;
NS_IMETHOD WillDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled)=0;

View File

@ -59,6 +59,7 @@
#include "nsIKBStateControl.h"
#include "nsIWidget.h"
#include "nsIScrollbar.h"
#include "nsIPlaintextEditor.h"
#include "nsIFrame.h" // Needed by IME code
@ -93,7 +94,7 @@
#include "JoinElementTxn.h"
#include "nsStyleSheetTxns.h"
#include "IMETextTxn.h"
#include "nsIHTMLEditor.h"
// included for nsEditor::CreateHTMLContent
#include "nsIElementFactory.h"
#include "nsINodeInfo.h"
@ -148,738 +149,6 @@ 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() : mArray(){}
nsSelectionState::~nsSelectionState()
{
MakeEmpty();
}
nsresult
nsSelectionState::SaveSelection(nsISelection *aSel)
{
if (!aSel) return NS_ERROR_NULL_POINTER;
nsresult res = NS_OK;
PRInt32 i,rangeCount, arrayCount = mArray.Count();
nsRangeStore *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 nsRangeStore;
mArray.AppendElement(item);
}
}
// else if we have too many, delete them
else if (rangeCount>arrayCount)
{
while ((item = (nsRangeStore*)mArray.ElementAt(rangeCount)))
{
delete item;
mArray.RemoveElementAt(rangeCount);
}
}
// now store the selection ranges
for (i=0; i<rangeCount; i++)
{
item = (nsRangeStore*)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(nsISelection *aSel)
{
if (!aSel) return NS_ERROR_NULL_POINTER;
nsresult res = NS_OK;
PRInt32 i, arrayCount = mArray.Count();
nsRangeStore *item;
// clear out selection
aSel->RemoveAllRanges();
// set the selection ranges anew
for (i=0; i<arrayCount; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsIDOMRange> range;
item->GetRange(address_of(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;
nsRangeStore *item;
item = (nsRangeStore*)mArray.ElementAt(0);
if (!item) return PR_FALSE;
nsCOMPtr<nsIDOMRange> range;
item->GetRange(address_of(range));
if (!range) return PR_FALSE;
PRBool bIsCollapsed;
range->GetCollapsed(&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;
nsRangeStore *myItem, *itsItem;
for (i=0; i<myCount; i++)
{
myItem = (nsRangeStore*)mArray.ElementAt(0);
itsItem = (nsRangeStore*)(aSelState->mArray.ElementAt(0));
if (!myItem || !itsItem) return PR_FALSE;
nsCOMPtr<nsIDOMRange> myRange, itsRange;
myItem->GetRange(address_of(myRange));
itsItem->GetRange(address_of(itsRange));
if (!myRange || !itsRange) return PR_FALSE;
PRInt32 compResult;
myRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START, itsRange, &compResult);
if (compResult) return PR_FALSE;
myRange->CompareBoundaryPoints(nsIDOMRange::END_TO_END, itsRange, &compResult);
if (compResult) return PR_FALSE;
}
// if we got here, they are equal
return PR_TRUE;
}
void
nsSelectionState::MakeEmpty()
{
// free any items in the array
nsRangeStore *item;
while ((item = (nsRangeStore*)mArray.ElementAt(0)))
{
delete item;
mArray.RemoveElementAt(0);
}
}
PRBool
nsSelectionState::IsEmpty()
{
return (mArray.Count() == 0);
}
/***************************************************************************
* nsRangeUpdater: class for updating nsIDOMRanges in response to editor actions.
*/
nsRangeUpdater::nsRangeUpdater() : mArray(), mLock(PR_FALSE) {}
nsRangeUpdater::~nsRangeUpdater()
{
// free any items in the array
nsRangeStore *item;
while ((item = (nsRangeStore*)mArray.ElementAt(0)))
{
delete item;
mArray.RemoveElementAt(0);
}
}
void*
nsRangeUpdater::RegisterRange(nsIDOMRange *aRange)
{
nsRangeStore *item = new nsRangeStore;
if (!item) return nsnull;
item->StoreRange(aRange);
mArray.AppendElement(item);
return item;
}
nsCOMPtr<nsIDOMRange>
nsRangeUpdater::ReclaimRange(void *aCookie)
{
nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie);
if (!item) return nsnull;
nsCOMPtr<nsIDOMRange> outRange;
item->GetRange(address_of(outRange));
mArray.RemoveElement(aCookie);
delete item;
return outRange;
}
void
nsRangeUpdater::DropRange(void *aCookie)
{
nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie);
if (!item) return;
mArray.RemoveElement(aCookie);
delete item;
}
void
nsRangeUpdater::RegisterRangeItem(nsRangeStore *aRangeItem)
{
if (!aRangeItem) return;
mArray.AppendElement(aRangeItem);
return;
}
void
nsRangeUpdater::DropRangeItem(nsRangeStore *aRangeItem)
{
if (!aRangeItem) return;
mArray.RemoveElement(aRangeItem);
return;
}
nsresult
nsRangeUpdater::RegisterSelectionState(nsSelectionState &aSelState)
{
PRInt32 i, theCount = aSelState.mArray.Count();
if (theCount < 1) return NS_ERROR_FAILURE;
nsRangeStore *item;
for (i=0; i<theCount; i++)
{
item = (nsRangeStore*)aSelState.mArray.ElementAt(i);
RegisterRangeItem(item);
}
return NS_OK;;
}
nsresult
nsRangeUpdater::DropSelectionState(nsSelectionState &aSelState)
{
PRInt32 i, theCount = aSelState.mArray.Count();
if (theCount < 1) return NS_ERROR_FAILURE;
nsRangeStore *item;
for (i=0; i<theCount; i++)
{
item = (nsRangeStore*)aSelState.mArray.ElementAt(i);
DropRangeItem(item);
}
return NS_OK;;
}
// gravity methods:
nsresult
nsRangeUpdater::SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aParent) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if ((item->startNode.get() == aParent) && (item->startOffset > aPosition))
item->startOffset++;
if ((item->endNode.get() == aParent) && (item->endOffset > aPosition))
item->endOffset++;
}
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition)
{
return SelAdjCreateNode(aParent, aPosition);
}
nsresult
nsRangeUpdater::SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aNode) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if ((item->startNode.get() == aParent) && (item->startOffset > aOffset))
item->startOffset--;
if ((item->endNode.get() == aParent) && (item->endOffset > aOffset))
item->endOffset--;
}
// MOOSE: also check inside of aNode, expensive. But in theory, we shouldn't
// actually hit this case in the usage i forsee for this.
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aOldRightNode || !aNewLeftNode) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsCOMPtr<nsIDOMNode> parent;
PRInt32 offset;
nsresult result = nsEditor::GetNodeLocation(aOldRightNode, address_of(parent), &offset);
if (NS_FAILED(result)) return result;
// first part is same as inserting aNewLeftnode
result = SelAdjInsertNode(parent,offset-1);
if (NS_FAILED(result)) return result;
// next step is to check for range enpoints inside aOldRightNode
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if (item->startNode.get() == aOldRightNode)
{
if (item->startOffset > aOffset)
{
item->startOffset -= aOffset;
}
else
{
item->startNode = aNewLeftNode;
}
}
if (item->endNode.get() == aOldRightNode)
{
if (item->endOffset > aOffset)
{
item->endOffset -= aOffset;
}
else
{
item->endNode = aNewLeftNode;
}
}
}
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjJoinNodes(nsIDOMNode *aLeftNode,
nsIDOMNode *aRightNode,
nsIDOMNode *aParent,
PRInt32 aOffset,
PRInt32 aOldLeftNodeLength)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aLeftNode || !aRightNode || !aParent) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
// adjust endpoints in aParent
if (item->startNode.get() == aParent)
{
if (item->startOffset > aOffset)
{
item->startOffset--;
}
else if (item->startOffset == aOffset)
{
// join keeps right hand node
item->startNode = aRightNode;
item->startOffset = aOldLeftNodeLength;
}
}
if (item->endNode.get() == aParent)
{
if (item->endOffset > aOffset)
{
item->endOffset--;
}
else if (item->endOffset == aOffset)
{
// join keeps right hand node
item->endNode = aRightNode;
item->endOffset = aOldLeftNodeLength;
}
}
// adjust endpoints in aRightNode
if (item->startNode.get() == aRightNode)
item->startOffset += aOldLeftNodeLength;
if (item->endNode.get() == aRightNode)
item->endOffset += aOldLeftNodeLength;
}
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
return NS_OK;
}
nsresult
nsRangeUpdater::WillReplaceContainer()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
return NS_OK;
}
nsresult
nsRangeUpdater::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode)
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
if (!aOriginalNode || !aNewNode) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if (item->startNode.get() == aOriginalNode)
item->startNode = aNewNode;
if (item->endNode.get() == aOriginalNode)
item->endNode = aNewNode;
}
return NS_OK;
}
nsresult
nsRangeUpdater::WillRemoveContainer()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
return NS_OK;
}
nsresult
nsRangeUpdater::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen)
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
if (!aNode || !aParent) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if (item->startNode.get() == aNode)
{
item->startNode = aParent;
item->startOffset += aOffset;
}
if (item->endNode.get() == aNode)
{
item->endNode = aParent;
item->endOffset += aOffset;
}
if ((item->startNode.get() == aParent) && (item->startOffset > aOffset))
item->startOffset += (PRInt32)aNodeOrigLen-1;
if ((item->endNode.get() == aParent) && (item->endOffset > aOffset))
item->endOffset += (PRInt32)aNodeOrigLen-1;
}
return NS_OK;
}
nsresult
nsRangeUpdater::WillInsertContainer()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
return NS_OK;
}
nsresult
nsRangeUpdater::DidInsertContainer()
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
return NS_OK;
}
nsresult
nsRangeUpdater::WillMoveNode()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
return NS_OK;
}
nsresult
nsRangeUpdater::DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset)
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
if (!aOldParent || !aNewParent) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
// like a delete in aOldParent
if ((item->startNode.get() == aOldParent) && (item->startOffset > aOldOffset))
item->startOffset--;
if ((item->endNode.get() == aOldParent) && (item->endOffset > aOldOffset))
item->endOffset--;
// and like an insert in aNewParent
if ((item->startNode.get() == aNewParent) && (item->startOffset > aNewOffset))
item->startOffset++;
if ((item->endNode.get() == aNewParent) && (item->endOffset > aNewOffset))
item->endOffset++;
}
return NS_OK;
}
/***************************************************************************
* helper class for nsSelectionState. nsRangeStore stores range endpoints.
*/
// DEBUG: PRInt32 nsRangeStore::n = 0;
nsRangeStore::nsRangeStore()
{
// DEBUG: n++; printf("range store alloc count=%d\n", n);
}
nsRangeStore::~nsRangeStore()
{
// DEBUG: n--; printf("range store alloc count=%d\n", n);
}
nsresult nsRangeStore::StoreRange(nsIDOMRange *aRange)
{
if (!aRange) return NS_ERROR_NULL_POINTER;
aRange->GetStartContainer(getter_AddRefs(startNode));
aRange->GetEndContainer(getter_AddRefs(endNode));
aRange->GetStartOffset(&startOffset);
aRange->GetEndOffset(&endOffset);
return NS_OK;
}
nsresult nsRangeStore::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;
}
/***************************************************************************
* another helper class for nsSelectionState. stack based class for doing
* Will/DidReplaceContainer()
*/
class nsAutoReplaceContainerSelNotify
{
private:
nsRangeUpdater &mRU;
nsIDOMNode *mOriginalNode;
nsIDOMNode *mNewNode;
public:
nsAutoReplaceContainerSelNotify(nsRangeUpdater &aRangeUpdater, nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) :
mRU(aRangeUpdater)
,mOriginalNode(aOriginalNode)
,mNewNode(aNewNode)
{
mRU.WillReplaceContainer();
}
~nsAutoReplaceContainerSelNotify()
{
mRU.DidReplaceContainer(mOriginalNode, mNewNode);
}
};
/***************************************************************************
* another helper class for nsSelectionState. stack based class for doing
* Will/DidRemoveContainer()
*/
class nsAutoRemoveContainerSelNotify
{
private:
nsRangeUpdater &mRU;
nsIDOMNode *mNode;
nsIDOMNode *mParent;
PRInt32 mOffset;
PRUint32 mNodeOrigLen;
public:
nsAutoRemoveContainerSelNotify(nsRangeUpdater &aRangeUpdater,
nsIDOMNode *aNode,
nsIDOMNode *aParent,
PRInt32 aOffset,
PRUint32 aNodeOrigLen) :
mRU(aRangeUpdater)
,mNode(aNode)
,mParent(aParent)
,mOffset(aOffset)
,mNodeOrigLen(aNodeOrigLen)
{
mRU.WillRemoveContainer();
}
~nsAutoRemoveContainerSelNotify()
{
mRU.DidRemoveContainer(mNode, mParent, mOffset, mNodeOrigLen);
}
};
/***************************************************************************
* another helper class for nsSelectionState. stack based class for doing
* Will/DidInsertContainer()
*/
class nsAutoInsertContainerSelNotify
{
private:
nsRangeUpdater &mRU;
public:
nsAutoInsertContainerSelNotify(nsRangeUpdater &aRangeUpdater) :
mRU(aRangeUpdater)
{
mRU.WillInsertContainer();
}
~nsAutoInsertContainerSelNotify()
{
mRU.DidInsertContainer();
}
};
/***************************************************************************
* another helper class for nsSelectionState. stack based class for doing
* Will/DidMoveNode()
*/
class nsAutoMoveNodeSelNotify
{
private:
nsRangeUpdater &mRU;
nsIDOMNode *mOldParent;
nsIDOMNode *mNewParent;
PRInt32 mOldOffset;
PRInt32 mNewOffset;
public:
nsAutoMoveNodeSelNotify(nsRangeUpdater &aRangeUpdater,
nsIDOMNode *aOldParent,
PRInt32 aOldOffset,
nsIDOMNode *aNewParent,
PRInt32 aNewOffset) :
mRU(aRangeUpdater)
,mOldParent(aOldParent)
,mNewParent(aNewParent)
,mOldOffset(aOldOffset)
,mNewOffset(aNewOffset)
{
mRU.WillMoveNode();
}
~nsAutoMoveNodeSelNotify()
{
mRU.DidMoveNode(mOldParent, mOldOffset, mNewParent, mNewOffset);
}
};
//---------------------------------------------------------------------------
//
// nsEditor: base editor class implementation
@ -2606,7 +1875,7 @@ nsEditor::ForceCompositionEnd()
#endif
#ifdef XP_UNIX
if(mFlags & nsIHTMLEditor::eEditorPasswordMask)
if(mFlags & nsIPlaintextEditor::eEditorPasswordMask)
return NS_OK;
#endif
@ -5059,7 +4328,6 @@ nsEditor::IsNextCharWhitespace(nsIDOMNode *aParentNode,
textNode->GetLength(&strLength);
if (strLength)
{
// you could use nsITextContent::IsOnlyWhitespace here
textNode->SubstringData(0,1,tempString);
*outIsSpace = nsCRT::IsAsciiSpace(tempString.First());
*outIsNBSP = (tempString.First() == nbsp);
@ -5391,7 +4659,7 @@ nsresult nsEditor::EndUpdateViewBatch()
PRBool forceReflow = PR_TRUE;
if (flags & nsIHTMLEditor::eEditorDisableForcedReflowsMask)
if (flags & nsIPlaintextEditor::eEditorDisableForcedReflowsMask)
forceReflow = PR_FALSE;
nsCOMPtr<nsIPresShell> presShell;
@ -5401,7 +4669,7 @@ nsresult nsEditor::EndUpdateViewBatch()
PRUint32 updateFlag = NS_VMREFRESH_IMMEDIATE;
if (flags & nsIHTMLEditor::eEditorDisableForcedUpdatesMask)
if (flags & nsIPlaintextEditor::eEditorDisableForcedUpdatesMask)
updateFlag = NS_VMREFRESH_NO_SYNC;
#ifdef HACK_FORCE_REDRAW
@ -5482,9 +4750,129 @@ nsEditor::DeleteSelectionImpl(nsIEditor::EDirection aAction)
return res;
}
// XXX: error handling in this routine needs to be cleaned up!
NS_IMETHODIMP
nsEditor::DeleteSelectionAndCreateNode(const nsString& aTag,
nsIDOMNode ** aNewNode)
{
nsCOMPtr<nsIDOMNode> parentSelectedNode;
PRInt32 offsetOfNewNode;
nsresult result = DeleteSelectionAndPrepareToCreateNode(parentSelectedNode,
offsetOfNewNode);
if (!NS_SUCCEEDED(result))
return result;
nsCOMPtr<nsIDOMNode> newNode;
result = CreateNode(aTag, parentSelectedNode, offsetOfNewNode,
getter_AddRefs(newNode));
// XXX: ERROR_HANDLING check result, and make sure aNewNode is set correctly in success/failure cases
*aNewNode = newNode;
NS_IF_ADDREF(*aNewNode);
// we want the selection to be just after the new node
nsCOMPtr<nsISelection> selection;
result = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(result)) return result;
if (!selection) return NS_ERROR_NULL_POINTER;
result = selection->Collapse(parentSelectedNode, offsetOfNewNode+1);
return result;
}
/* Non-interface, protected methods */
NS_IMETHODIMP
nsEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr<nsIDOMNode> &parentSelectedNode, PRInt32& offsetOfNewNode)
{
nsresult result=NS_ERROR_NOT_INITIALIZED;
nsCOMPtr<nsISelection> selection;
result = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(result)) return result;
if (!selection) return NS_ERROR_NULL_POINTER;
PRBool collapsed;
result = selection->GetIsCollapsed(&collapsed);
if (NS_SUCCEEDED(result) && !collapsed)
{
result = DeleteSelection(nsIEditor::eNone);
if (NS_FAILED(result)) {
return result;
}
// get the new selection
result = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(result)) {
return result;
}
#ifdef NS_DEBUG
nsCOMPtr<nsIDOMNode>testSelectedNode;
nsresult debugResult = selection->GetAnchorNode(getter_AddRefs(testSelectedNode));
// no selection is ok.
// if there is a selection, it must be collapsed
if (testSelectedNode)
{
PRBool testCollapsed;
debugResult = selection->GetIsCollapsed(&testCollapsed);
NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion");
NS_ASSERTION(testCollapsed, "selection not reset after deletion");
}
#endif
}
// split the selected node
PRInt32 offsetOfSelectedNode;
result = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode));
if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetOfSelectedNode)) && parentSelectedNode)
{
nsCOMPtr<nsIDOMNode> selectedNode;
PRUint32 selectedNodeContentCount=0;
nsCOMPtr<nsIDOMCharacterData>selectedParentNodeAsText;
selectedParentNodeAsText = do_QueryInterface(parentSelectedNode);
offsetOfNewNode = offsetOfSelectedNode;
/* if the selection is a text node, split the text node if necesary
and compute where to put the new node
*/
if (selectedParentNodeAsText)
{
PRInt32 indexOfTextNodeInParent;
selectedNode = do_QueryInterface(parentSelectedNode);
selectedNode->GetParentNode(getter_AddRefs(parentSelectedNode));
selectedParentNodeAsText->GetLength(&selectedNodeContentCount);
GetChildOffset(selectedNode, parentSelectedNode, indexOfTextNodeInParent);
if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount))
{
nsCOMPtr<nsIDOMNode> newSiblingNode;
result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode));
// now get the node's offset in it's parent, and insert the new tag there
if (NS_SUCCEEDED(result)) {
result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
}
}
else
{ // determine where to insert the new node
if (0==offsetOfSelectedNode) {
offsetOfNewNode = indexOfTextNodeInParent; // insert new node as previous sibling to selection parent
}
else { // insert new node as last child
GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
offsetOfNewNode++; // offsets are 0-based, and we need the index of the new node
}
}
}
// Here's where the new node was inserted
}
#ifdef DEBUG
else {
printf("InsertLineBreak into an empty document is not yet supported\n");
}
#endif
return result;
}
NS_IMETHODIMP
nsEditor::DoAfterDoTransaction(nsITransaction *aTxn)
{

View File

@ -46,6 +46,7 @@
#include "nsIDTD.h"
#include "nsIDOMElement.h"
#include "nsVoidArray.h"
#include "nsSelectionState.h"
class nsIEditActionListener;
class nsIDocumentStateListener;
@ -72,92 +73,6 @@ class nsIFile;
class nsISelectionController;
/***************************************************************************
* 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 nsRangeStore
{
nsRangeStore();
~nsRangeStore();
nsresult StoreRange(nsIDOMRange *aRange);
nsresult GetRange(nsCOMPtr<nsIDOMRange> *outRange);
nsCOMPtr<nsIDOMNode> startNode;
PRInt32 startOffset;
nsCOMPtr<nsIDOMNode> endNode;
PRInt32 endOffset;
// DEBUG: static PRInt32 n;
};
class nsSelectionState
{
public:
nsSelectionState();
~nsSelectionState();
nsresult SaveSelection(nsISelection *aSel);
nsresult RestoreSelection(nsISelection *aSel);
PRBool IsCollapsed();
PRBool IsEqual(nsSelectionState *aSelState);
void MakeEmpty();
PRBool IsEmpty();
protected:
nsVoidArray mArray;
friend class nsRangeUpdater;
};
class nsRangeUpdater
{
public:
nsRangeUpdater();
~nsRangeUpdater();
void* RegisterRange(nsIDOMRange *aRange);
nsCOMPtr<nsIDOMRange> ReclaimRange(void *aCookie);
void DropRange(void *aCookie);
void RegisterRangeItem(nsRangeStore *aRangeItem);
void DropRangeItem(nsRangeStore *aRangeItem);
nsresult RegisterSelectionState(nsSelectionState &aSelState);
nsresult DropSelectionState(nsSelectionState &aSelState);
// editor selection gravity routines. Note that we can't always depend on
// DOM Range gravity to do what we want to the "real" selection. For instance,
// if you move a node, that corresponds to deleting it and reinserting it.
// DOM Range gravity will promote the selection out of the node on deletion,
// which is not what you want if you know you are reinserting it.
nsresult SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition);
nsresult SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition);
nsresult SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset);
nsresult SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode);
nsresult SelAdjJoinNodes(nsIDOMNode *aLeftNode,
nsIDOMNode *aRightNode,
nsIDOMNode *aParent,
PRInt32 aOffset,
PRInt32 aOldLeftNodeLength);
nsresult SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString);
nsresult SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength);
// the following gravity routines need will/did sandwiches, because the other gravity
// routines will be called inside of these sandwiches, but should be ignored.
nsresult WillReplaceContainer();
nsresult DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode);
nsresult WillRemoveContainer();
nsresult DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen);
nsresult WillInsertContainer();
nsresult DidInsertContainer();
nsresult WillMoveNode();
nsresult DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset);
protected:
nsVoidArray mArray;
PRBool mLock;
};
/** implementation of an editor object. it will be the controller/focal point
* for the main editor services. i.e. the GUIManager, publishing, transaction
* manager, event interfaces. the idea for the event interfaces is to have them
@ -181,18 +96,17 @@ public:
kOpNone = 0,
kOpUndo,
kOpRedo,
kOpSetTextProperty,
kOpRemoveTextProperty,
kOpInsertNode,
kOpCreateNode,
kOpDeleteNode,
kOpSplitNode,
kOpJoinNode,
kOpDeleteText,
kOpInsertText,
kOpInsertIMEText,
kOpDeleteSelection,
kOpHTMLPaste
// text commands
kOpInsertBreak = 1000,
kOpInsertText = 1001,
kOpInsertIMEText = 1002,
kOpDeleteText = 1003
};
static const char* kMOZEditorBogusNodeAttr;
@ -341,6 +255,8 @@ public:
nsIDOMCharacterData *aTextNode,
PRInt32 aOffset);
NS_IMETHOD DeleteSelectionImpl(EDirection aAction);
NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag,
nsIDOMNode ** aNewNode);
/* helper routines for node/parent manipulations */
nsresult ReplaceContainer(nsIDOMNode *inNode,
@ -454,6 +370,8 @@ protected:
nsIDOMNode *aRightNode,
JoinElementTxn **aTxn);
NS_IMETHOD DeleteSelectionAndPrepareToCreateNode(nsCOMPtr<nsIDOMNode> &parentSelectedNode,
PRInt32& offsetOfNewNode);
// called each time we modify the document. Increments the mod
// count of the doc.
@ -775,7 +693,7 @@ public:
protected:
PRUint32 mFlags; // behavior flags. See nsIHTMLEditor.h for the flags we use.
PRUint32 mFlags; // behavior flags. See nsPlaintextEditor.h for the flags we use.
nsWeakPtr mPresShellWeak; // weak reference to the nsIPresShell
nsWeakPtr mSelConWeak; // weak reference to the nsISelectionController

View File

@ -21,7 +21,7 @@
* Pierre Phaneuf <pp@ludusdesign.com>
*/
#include "nsEditorEventListeners.h"
#include "nsIPlaintextEditor.h"
#include "nsIHTMLEditor.h"
#include "nsEditor.h"
#include "nsVoidArray.h"
#include "nsString.h"
@ -178,8 +178,8 @@ nsTextEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent)
// if we are readonly or disabled, then do nothing.
if (NS_SUCCEEDED(mEditor->GetFlags(&flags)))
{
if (flags & nsIHTMLEditor::eEditorReadonlyMask ||
flags & nsIHTMLEditor::eEditorDisabledMask)
if (flags & nsIPlaintextEditor::eEditorReadonlyMask ||
flags & nsIPlaintextEditor::eEditorDisabledMask)
return NS_OK;
}
else
@ -246,7 +246,7 @@ nsTextEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent)
break;
case nsIDOMKeyEvent::DOM_VK_TAB:
if ((flags & nsIHTMLEditor::eEditorSingleLineMask))
if ((flags & nsIPlaintextEditor::eEditorSingleLineMask))
return NS_OK; // let it be used for focus switching
// else we insert the tab straight through
@ -257,9 +257,8 @@ nsTextEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent)
case nsIDOMKeyEvent::DOM_VK_RETURN:
case nsIDOMKeyEvent::DOM_VK_ENTER:
if (!(flags & nsIHTMLEditor::eEditorSingleLineMask))
if (!(flags & nsIPlaintextEditor::eEditorSingleLineMask))
{
//htmlEditor->InsertBreak();
textEditor->HandleKeyPress(keyEvent);
ScrollSelectionIntoView(mEditor);
aKeyEvent->PreventDefault(); // consumed
@ -642,8 +641,8 @@ nsTextEditorDragListener::DragOver(nsIDOMEvent* aDragEvent)
if ( dragSession ) {
PRUint32 flags;
if (NS_SUCCEEDED(mEditor->GetFlags(&flags))) {
if ((flags & nsIHTMLEditor::eEditorDisabledMask) ||
(flags & nsIHTMLEditor::eEditorReadonlyMask)) {
if ((flags & nsIPlaintextEditor::eEditorDisabledMask) ||
(flags & nsIPlaintextEditor::eEditorReadonlyMask)) {
dragSession->SetCanDrop(PR_FALSE);
return NS_OK;
}
@ -1041,7 +1040,7 @@ nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent)
PRUint32 flags;
aEvent->PreventBubble();
mEditor->GetFlags(&flags);
if (! (flags & nsIHTMLEditor::eEditorDisabledMask))
if (! (flags & nsIPlaintextEditor::eEditorDisabledMask))
{ // only enable caret and selection if the editor is not disabled
nsCOMPtr<nsIEditor>editor = do_QueryInterface(mEditor);
if (editor)
@ -1050,7 +1049,7 @@ nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent)
editor->GetSelectionController(getter_AddRefs(selCon));
if (selCon)
{
if (! (flags & nsIHTMLEditor::eEditorReadonlyMask))
if (! (flags & nsIPlaintextEditor::eEditorReadonlyMask))
{ // only enable caret if the editor is not readonly
PRInt32 pixelWidth;
nsresult result;
@ -1059,7 +1058,7 @@ nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent)
if (NS_SUCCEEDED(result) && look)
{
if(flags & nsIHTMLEditor::eEditorSingleLineMask)
if(flags & nsIPlaintextEditor::eEditorSingleLineMask)
look->GetMetric(nsILookAndFeel::eMetric_SingleLineCaretWidth, pixelWidth);
else
look->GetMetric(nsILookAndFeel::eMetric_MultiLineCaretWidth, pixelWidth);
@ -1109,11 +1108,11 @@ nsTextEditorFocusListener::Blur(nsIDOMEvent* aEvent)
if (selCon)
{
selCon->SetCaretEnabled(PR_FALSE);
if((flags & nsIHTMLEditor::eEditorWidgetMask) ||
(flags & nsIHTMLEditor::eEditorPasswordMask) ||
(flags & nsIHTMLEditor::eEditorReadonlyMask) ||
(flags & nsIHTMLEditor::eEditorDisabledMask) ||
(flags & nsIHTMLEditor::eEditorFilterInputMask))
if((flags & nsIPlaintextEditor::eEditorWidgetMask) ||
(flags & nsIPlaintextEditor::eEditorPasswordMask) ||
(flags & nsIPlaintextEditor::eEditorReadonlyMask) ||
(flags & nsIPlaintextEditor::eEditorDisabledMask) ||
(flags & nsIPlaintextEditor::eEditorFilterInputMask))
{
selCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);//hide but do NOT turn off
}

View File

@ -35,7 +35,7 @@
#include "nsIDOMFocusListener.h"
#include "nsIEditor.h"
#include "nsIHTMLEditor.h"
#include "nsIPlaintextEditor.h"
/** The nsTextEditorKeyListener public nsIDOMKeyListener
* This class will delegate events to its editor according to the translation

View File

@ -27,6 +27,7 @@
#include "nsEditor.h" // for gInstanceCount
#include "nsEditorController.h" //CID
#include "nsEditorService.h"
#include "nsPlaintextEditor.h"
////////////////////////////////////////////////////////////////////////
// Define the contructor function for the objects
@ -38,6 +39,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorShell)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorController)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsComposerController)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorService)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsPlaintextEditor)
#ifdef ENABLE_EDITOR_API_LOG
#include "nsHTMLEditorLog.h"
@ -52,6 +54,8 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLEditor)
// class name.
//
static nsModuleComponentInfo components[] = {
{ "Text Editor", NS_TEXTEDITOR_CID,
"@mozilla.org/editor/texteditor;1", nsPlaintextEditorConstructor, },
#ifdef ENABLE_EDITOR_API_LOG
{ "HTML Editor", NS_HTMLEDITOR_CID,
"@mozilla.org/editor/htmleditor;1", nsHTMLEditorLogConstructor, },

View File

@ -910,7 +910,7 @@ nsEditorShell::InstantiateEditor(nsIDOMDocument *aDoc, nsIPresShell *aPresShell)
{
if (mEditorTypeString.EqualsWithConversion("text"))
{
err = editor->Init(aDoc, aPresShell, nsnull, selCon, nsIHTMLEditor::eEditorPlaintextMask | nsIHTMLEditor::eEditorEnableWrapHackMask);
err = editor->Init(aDoc, aPresShell, nsnull, selCon, nsIPlaintextEditor::eEditorPlaintextMask | nsIPlaintextEditor::eEditorEnableWrapHackMask);
mEditorType = ePlainTextEditorType;
}
else if (mEditorTypeString.EqualsWithConversion("html") || mEditorTypeString.IsEmpty()) // empty string default to HTML editor
@ -920,7 +920,7 @@ nsEditorShell::InstantiateEditor(nsIDOMDocument *aDoc, nsIPresShell *aPresShell)
}
else if (mEditorTypeString.EqualsWithConversion("htmlmail")) // HTML editor with special mail rules
{
err = editor->Init(aDoc, aPresShell, nsnull, selCon, nsIHTMLEditor::eEditorMailMask);
err = editor->Init(aDoc, aPresShell, nsnull, selCon, nsIPlaintextEditor::eEditorMailMask);
mEditorType = eHTMLTextEditorType;
}
else
@ -3423,7 +3423,7 @@ nsEditorShell::GetDocumentEditable(PRBool *aDocumentEditable)
PRUint32 editorFlags;
editor->GetFlags(&editorFlags);
if (editorFlags & nsIHTMLEditor::eEditorReadonlyMask)
if (editorFlags & nsIPlaintextEditor::eEditorReadonlyMask)
return NS_OK;
nsCOMPtr<nsIDOMDocument> doc;

View File

@ -32,6 +32,7 @@
#include "nsIAtom.h"
#include "nsVoidArray.h"
#include "nsEditor.h"
#include "nsPlaintextEditor.h"
#include "nsIContentIterator.h"
/***************************************************************************
@ -48,6 +49,22 @@ class nsAutoPlaceHolderBatch
~nsAutoPlaceHolderBatch() { if (mEd) mEd->EndPlaceHolderTransaction(); }
};
/***************************************************************************
* stack based helper class for detecting end of editor initialization, in
* order to triger "end of init" initialization of the edit rules.
*/
class nsAutoEditInitRulesTrigger
{
private:
nsPlaintextEditor *mEd;
nsresult &mRes;
public:
nsAutoEditInitRulesTrigger( nsPlaintextEditor *aEd, nsresult &aRes) : mEd(aEd), mRes(aRes)
{ if (mEd) mEd->BeginEditorInit(); }
~nsAutoEditInitRulesTrigger() { if (mEd) mRes = mEd->EndEditorInit(); }
};
/***************************************************************************
* stack based helper class for batching a collection of txns.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -34,6 +34,7 @@ class nsISupportsArray;
class nsVoidArray;
class nsIDOMElement;
class nsIEditor;
class nsHTMLEditor;
class nsHTMLEditRules : public nsIHTMLEditRules, public nsTextEditRules, public nsIEditActionListener
{
@ -46,7 +47,7 @@ public:
// nsIEditRules methods
NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags);
NS_IMETHOD Init(nsPlaintextEditor *aEditor, PRUint32 aFlags);
NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection);
NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection);
NS_IMETHOD WillDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled);
@ -98,10 +99,10 @@ protected:
nsString *outString,
PRInt32 aMaxLength);
nsresult WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBool *aHandled);
nsresult DidInsertBreak(nsISelection *aSelection, nsresult aResult);
nsresult WillDeleteSelection(nsISelection *aSelection, nsIEditor::EDirection aAction,
PRBool *aCancel, PRBool *aHandled);
nsresult DeleteNonTableElements(nsIDOMNode *aNode);
nsresult WillMakeList(nsISelection *aSelection, const nsString *aListType, PRBool aEntireList, PRBool *aCancel, PRBool *aHandled, const nsString *aItemType=nsnull);
nsresult WillRemoveList(nsISelection *aSelection, PRBool aOrderd, PRBool *aCancel, PRBool *aHandled);
nsresult WillIndent(nsISelection *aSelection, PRBool *aCancel, PRBool *aHandled);
@ -110,17 +111,13 @@ protected:
nsresult WillMakeDefListItem(nsISelection *aSelection, const nsString *aBlockType, PRBool aEntireList, PRBool *aCancel, PRBool *aHandled);
nsresult WillMakeBasicBlock(nsISelection *aSelection, const nsString *aBlockType, PRBool *aCancel, PRBool *aHandled);
nsresult DidMakeBasicBlock(nsISelection *aSelection, nsRulesInfo *aInfo, nsresult aResult);
nsresult AlignInnerBlocks(nsIDOMNode *aNode, const nsString *alignType);
nsresult AlignBlockContents(nsIDOMNode *aNode, const nsString *alignType);
nsresult GetInnerContent(nsIDOMNode *aNode, nsISupportsArray *outArrayOfNodes, PRBool aList = PR_TRUE, PRBool aTble = PR_TRUE);
nsresult InsertTab(nsISelection *aSelection, nsString *outString);
nsresult ReturnInHeader(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset);
nsresult ReturnInParagraph(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset, PRBool *aCancel, PRBool *aHandled);
nsresult ReturnInListItem(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset);
nsresult AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection);
nsresult ConvertListType(nsIDOMNode *aList, nsCOMPtr<nsIDOMNode> *outList, const nsString& aListType, const nsString& aItemType);
nsresult CreateStyleForInsertText(nsISelection *aSelection, nsIDOMDocument *aDoc);
@ -132,7 +129,6 @@ protected:
PRBool IsLastNode(nsIDOMNode *aNode);
PRBool AtStartOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock);
PRBool AtEndOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock);
nsresult GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt32 aOffset,
PRInt32 actionID, nsCOMPtr<nsIDOMNode> *outNode, PRInt32 *outOffset);
nsresult GetPromotedRanges(nsISelection *inSelection,
@ -154,20 +150,16 @@ protected:
nsCOMPtr<nsIDOMNode> GetHighestInlineParent(nsIDOMNode* aNode);
nsresult MakeTransitionList(nsISupportsArray *inArrayOfNodes,
nsVoidArray *inTransitionArray);
nsresult ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString *aBlockTag);
nsresult MakeBlockquote(nsISupportsArray *arrayOfNodes);
nsresult SplitAsNeeded(const nsString *aTag, nsCOMPtr<nsIDOMNode> *inOutParent, PRInt32 *inOutOffset);
nsresult AddTerminatingBR(nsIDOMNode *aBlock);
nsresult JoinNodesSmart( nsIDOMNode *aNodeLeft,
nsIDOMNode *aNodeRight,
nsCOMPtr<nsIDOMNode> *aOutMergeParent,
PRInt32 *aOutMergeOffset);
nsresult GetTopEnclosingMailCite(nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutCiteNode);
nsresult GetTopEnclosingMailCite(nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutCiteNode, PRBool aPlaintext);
nsresult PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList);
nsresult AdjustSpecialBreaks(PRBool aSafeToAskFrames = PR_FALSE);
nsresult AdjustWhitespace(nsISelection *aSelection);
nsresult AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection aAction);
@ -180,14 +172,13 @@ protected:
nsresult SelectionEndpointInNode(nsIDOMNode *aNode, PRBool *aResult);
nsresult DoTextNodeWhitespace(nsIDOMCharacterData *aTextNode, PRInt32 aStart, PRInt32 aEnd);
nsresult UpdateDocChangeRange(nsIDOMRange *aRange);
nsresult ConvertWhitespace(const nsString & inString, nsString & outString);
nsresult ConfirmSelectionInBody();
nsresult InsertMozBRIfNeeded(nsIDOMNode *aNode);
// data members
protected:
nsHTMLEditor *mHTMLEditor;
nsCOMPtr<nsIDOMRange> mDocChangeRange;
PRBool mListenerEnabled;
PRBool mReturnInEmptyLIKillsList;

View File

@ -26,6 +26,7 @@
#include "nsEditor.h"
#include "nsIDOMNode.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMHTMLAnchorElement.h"
/********************************************************
* helper methods from nsTextEditRules
@ -487,6 +488,34 @@ nsHTMLEditUtils::IsImage(nsIDOMNode *node)
return PR_FALSE;
}
PRBool
nsHTMLEditUtils::IsLink(nsIDOMNode *aNode)
{
if (!aNode) return PR_FALSE;
nsCOMPtr<nsIDOMHTMLAnchorElement> anchor = do_QueryInterface(aNode);
if (anchor)
{
nsAutoString tmpText;
if (NS_SUCCEEDED(anchor->GetHref(tmpText)) && tmpText.GetUnicode() && tmpText.Length() != 0)
return PR_TRUE;
}
return PR_FALSE;
}
PRBool
nsHTMLEditUtils::IsNamedAnchor(nsIDOMNode *aNode)
{
if (!aNode) return PR_FALSE;
nsCOMPtr<nsIDOMHTMLAnchorElement> anchor = do_QueryInterface(aNode);
if (anchor)
{
nsAutoString tmpText;
if (NS_SUCCEEDED(anchor->GetName(tmpText)) && tmpText.GetUnicode() && tmpText.Length() != 0)
return PR_TRUE;
}
return PR_FALSE;
}
///////////////////////////////////////////////////////////////////////////
// IsDiv: true if node an html div node

View File

@ -58,6 +58,8 @@ public:
static PRBool IsAddress(nsIDOMNode *aNode);
static PRBool IsAnchor(nsIDOMNode *aNode);
static PRBool IsImage(nsIDOMNode *aNode);
static PRBool IsLink(nsIDOMNode *aNode);
static PRBool IsNamedAnchor(nsIDOMNode *aNode);
static PRBool IsDiv(nsIDOMNode *aNode);
static PRBool IsNormalDiv(nsIDOMNode *aNode);
static PRBool IsMozDiv(nsIDOMNode *aNode);

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,8 @@
#include "nsCOMPtr.h"
#include "nsIPlaintextEditor.h"
#include "nsPlaintextEditor.h"
#include "nsIEditor.h"
#include "nsIHTMLEditor.h"
#include "nsITableEditor.h"
#include "nsIEditorMailSupport.h"
@ -51,8 +52,7 @@ class nsIDocumentEncoder;
* The HTML editor implementation.<br>
* Use to edit HTML document represented as a DOM tree.
*/
class nsHTMLEditor : public nsEditor,
public nsIPlaintextEditor,
class nsHTMLEditor : public nsPlaintextEditor,
public nsIHTMLEditor,
public nsIEditorMailSupport,
public nsITableEditor,
@ -74,7 +74,10 @@ public:
kOpRemoveList = 3006,
kOpMakeDefListItem = 3007,
kOpInsertElement = 3008,
kOpInsertQuotation = 3009
kOpInsertQuotation = 3009,
kOpSetTextProperty = 3010,
kOpRemoveTextProperty = 3011,
kOpHTMLPaste = 3012
};
@ -89,8 +92,9 @@ public:
nsHTMLEditor();
virtual ~nsHTMLEditor();
/* ------------ nsIPlaintextEditor methods -------------- */
NS_DECL_NSIPLAINTEXTEDITOR
/* ------------ nsPlaintextEditor overrides -------------- */
NS_IMETHODIMP HandleKeyPress(nsIDOMKeyEvent* aKeyEvent);
NS_IMETHODIMP CollapseSelectionToStart();
/* ------------ nsIHTMLEditor methods -------------- */
NS_IMETHOD SetInlineProperty(nsIAtom *aProperty,
@ -118,7 +122,6 @@ public:
NS_IMETHOD RebuildDocumentFromSource(const nsString& aSourceString);
NS_IMETHOD InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSelection);
NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag, nsIDOMNode ** aNewNode);
NS_IMETHOD SelectElement(nsIDOMElement* aElement);
NS_IMETHOD SetCaretAfterElement(nsIDOMElement* aElement);
@ -240,12 +243,6 @@ public:
/** prepare the editor for use */
NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags);
NS_IMETHOD GetDocumentIsEmpty(PRBool *aDocumentIsEmpty);
NS_IMETHOD DeleteSelection(EDirection aAction);
NS_IMETHOD SetDocumentCharacterSet(const PRUnichar* characterSet);
/** we override this here to install event listeners */
NS_IMETHOD PostCreate();
@ -266,15 +263,6 @@ public:
NS_IMETHOD DoDrag(nsIDOMEvent *aDragEvent);
NS_IMETHOD InsertFromDrop(nsIDOMEvent* aDropEvent);
NS_IMETHOD OutputToString(nsAWritableString& aOutputString,
const nsAReadableString& aFormatType,
PRUint32 aFlags);
NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream,
const nsAReadableString& aFormatType,
const nsAReadableString* aCharsetOverride,
PRUint32 aFlags);
NS_IMETHOD GetHeadContentsAsHTML(nsString& aOutputString);
NS_IMETHOD ReplaceHeadContentsWithHTML(const nsString &aSourceToInsert);
@ -307,7 +295,6 @@ public:
PRInt32 aOffset,
PRBool aNoEmptyNodes);
NS_IMETHOD GetBodyStyleContext(nsIStyleContext** aStyleContext);
/** returns the absolute position of the end points of aSelection
* in the document as a text stream.
@ -356,8 +343,6 @@ protected:
*/
NS_IMETHOD GetLayoutObject(nsIDOMNode *aInNode, nsISupports **aOutLayoutObject);
NS_IMETHOD DeleteSelectionAndPrepareToCreateNode(nsCOMPtr<nsIDOMNode> &parentSelectedNode, PRInt32& offsetOfNewNode);
/* StyleSheet load callback */
static void ApplyStyleSheetToPresShellDocument(nsICSSStyleSheet* aSheet, void *aData);
@ -376,11 +361,11 @@ protected:
// key event helpers
NS_IMETHOD TabInTable(PRBool inIsShift, PRBool *outHandled);
NS_IMETHOD CreateBR(nsIDOMNode *aNode, PRInt32 aOffset,
nsCOMPtr<nsIDOMNode> *outBRNode, EDirection aSelect = eNone);
nsCOMPtr<nsIDOMNode> *outBRNode, nsIEditor::EDirection aSelect = nsIEditor::eNone);
NS_IMETHOD CreateBRImpl(nsCOMPtr<nsIDOMNode> *aInOutParent,
PRInt32 *aInOutOffset,
nsCOMPtr<nsIDOMNode> *outBRNode,
EDirection aSelect);
nsIEditor::EDirection aSelect);
NS_IMETHOD InsertBR(nsCOMPtr<nsIDOMNode> *outBRNode);
// Table Editing (implemented in nsTableEditor.cpp)
@ -491,12 +476,6 @@ protected:
PRInt32 aEndOffset,
nsISelection *aSelection);
// Helpers for output routines
NS_IMETHOD GetAndInitDocEncoder(const nsAReadableString& aFormatType,
PRUint32 aFlags,
const nsAReadableString* aCharset,
nsIDocumentEncoder** encoder);
// Methods for handling plaintext quotations
NS_IMETHOD PasteAsPlaintextQuotation(PRInt32 aSelectionType);
NS_IMETHOD InsertAsPlaintextQuotation(const nsString& aQuotedText,
@ -610,38 +589,24 @@ protected:
// Data members
protected:
TypeInState* mTypeInState;
nsCOMPtr<nsIEditRules> mRules;
nsCOMPtr<nsIDOMEventListener> mKeyListenerP;
nsCOMPtr<nsIDOMEventListener> mMouseListenerP;
nsCOMPtr<nsIDOMEventListener> mTextListenerP;
nsCOMPtr<nsIDOMEventListener> mCompositionListenerP;
nsCOMPtr<nsIDOMEventListener> mDragListenerP;
nsCOMPtr<nsIDOMEventListener> mFocusListenerP;
PRBool mIsComposing;
// Used by nsIPlaintextEditor but not html editors -- factor me!
PRInt32 mMaxTextLength;
TypeInState* mTypeInState;
nsCOMPtr<nsIAtom> mBoldAtom;
nsCOMPtr<nsIAtom> mItalicAtom;
nsCOMPtr<nsIAtom> mUnderlineAtom;
nsCOMPtr<nsIAtom> mFontAtom;
nsCOMPtr<nsIAtom> mLinkAtom;
nsCOMPtr<nsIAtom> mBoldAtom;
nsCOMPtr<nsIAtom> mItalicAtom;
nsCOMPtr<nsIAtom> mUnderlineAtom;
nsCOMPtr<nsIAtom> mFontAtom;
nsCOMPtr<nsIAtom> mLinkAtom;
nsCOMPtr<nsIDOMNode> mCachedNode;
PRBool mCachedBoldStyle;
PRBool mCachedItalicStyle;
PRBool mCachedUnderlineStyle;
PRBool mCachedBoldStyle;
PRBool mCachedItalicStyle;
PRBool mCachedUnderlineStyle;
nsString mCachedFontName;
// Used by GetFirstSelectedCell and GetNextSelectedCell
PRInt32 mSelectedCellIndex;
public:
static nsIAtom *gTypingTxnName;
static nsIAtom *gIMETxnName;
static nsIAtom *gDeleteTxnName;
// friends
friend class nsHTMLEditRules;

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,8 @@
0xa6cf9121, 0x15b3, 0x11d2, \
{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32} }
#include "nsIHTMLEditor.h"
class nsIHTMLEditRules : public nsISupports
{
public:

View File

@ -0,0 +1,582 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
#include "nsICaret.h"
#include "nsPlaintextEditor.h"
#include "nsHTMLEditUtils.h"
#include "nsEditorEventListeners.h"
#include "nsIDOMText.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMDocument.h"
#include "nsIDOMAttr.h"
#include "nsIDocument.h"
#include "nsIDOMEventReceiver.h"
#include "nsIDOMKeyEvent.h"
#include "nsIDOMKeyListener.h"
#include "nsIDOMMouseListener.h"
#include "nsIDOMMouseEvent.h"
#include "nsISelection.h"
#include "nsISelectionPrivate.h"
#include "nsIDOMHTMLAnchorElement.h"
#include "nsIDOMHTMLImageElement.h"
#include "nsISelectionController.h"
#include "nsIFrameSelection.h" // For TABLESELECTION_ defines
#include "nsIIndependentSelection.h" //domselections answer to frameselection
#include "nsICSSLoader.h"
#include "nsICSSStyleSheet.h"
#include "nsIHTMLContentContainer.h"
#include "nsIStyleSet.h"
#include "nsIDocumentObserver.h"
#include "nsIDocumentStateListener.h"
#include "nsIStyleContext.h"
#include "nsIEnumerator.h"
#include "nsIContent.h"
#include "nsIContentIterator.h"
#include "nsEditorCID.h"
#include "nsLayoutCID.h"
#include "nsIDOMRange.h"
#include "nsIDOMNSRange.h"
#include "nsISupportsArray.h"
#include "nsVoidArray.h"
#include "nsFileSpec.h"
#include "nsIFile.h"
#include "nsIURL.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsWidgetsCID.h"
#include "nsIDocumentEncoder.h"
#include "nsIDOMDocumentFragment.h"
#include "nsIPresShell.h"
#include "nsIPresContext.h"
#include "nsIParser.h"
#include "nsParserCIID.h"
#include "nsIImage.h"
#include "nsAOLCiter.h"
#include "nsInternetCiter.h"
#include "nsISupportsPrimitives.h"
#include "InsertTextTxn.h"
// netwerk
#include "nsIURI.h"
#include "nsNetUtil.h"
// Drag & Drop, Clipboard
#include "nsWidgetsCID.h"
#include "nsIClipboard.h"
#include "nsITransferable.h"
#include "nsIDragService.h"
#include "nsIDOMNSUIEvent.h"
// Transactionas
#include "PlaceholderTxn.h"
#include "nsStyleSheetTxns.h"
// Misc
#include "TextEditorTest.h"
#include "nsEditorUtils.h"
#include "nsIPref.h"
const PRUnichar nbsp = 160;
// HACK - CID for NS_CTRANSITIONAL_DTD_CID so that we can get at transitional dtd
#define NS_CTRANSITIONAL_DTD_CID \
{ 0x4611d482, 0x960a, 0x11d4, { 0x8e, 0xb0, 0xb6, 0x17, 0x66, 0x1b, 0x6f, 0x7c } }
static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID);
static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
static NS_DEFINE_IID(kSubtreeIteratorCID, NS_SUBTREEITERATOR_CID);
static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID);
static NS_DEFINE_CID(kCDOMSelectionCID, NS_DOMSELECTION_CID);
static NS_DEFINE_IID(kFileWidgetCID, NS_FILEWIDGET_CID);
static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID);
static NS_DEFINE_IID(kCParserIID, NS_IPARSER_IID);
static NS_DEFINE_IID(kCParserCID, NS_PARSER_IID);
static NS_DEFINE_CID(kCTransitionalDTDCID, NS_CTRANSITIONAL_DTD_CID);
// Drag & Drop, Clipboard Support
static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID);
static NS_DEFINE_CID(kCDragServiceCID, NS_DRAGSERVICE_CID);
static NS_DEFINE_CID(kCHTMLFormatConverterCID, NS_HTMLFORMATCONVERTER_CID);
// private clipboard data flavors for html copy/paste
#define kHTMLContext "text/_moz_htmlcontext"
#define kHTMLInfo "text/_moz_htmlinfo"
#if defined(NS_DEBUG) && defined(DEBUG_buster)
static PRBool gNoisy = PR_FALSE;
#else
static const PRBool gNoisy = PR_FALSE;
#endif
NS_IMETHODIMP nsPlaintextEditor::PrepareTransferable(nsITransferable **transferable)
{
// Create generic Transferable for getting the data
nsresult rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull,
NS_GET_IID(nsITransferable),
(void**)transferable);
if (NS_FAILED(rv))
return rv;
// Get the nsITransferable interface for getting the data from the clipboard
if (transferable) (*transferable)->AddDataFlavor(kUnicodeMime);
return NS_OK;
}
NS_IMETHODIMP nsPlaintextEditor::InsertTextFromTransferable(nsITransferable *transferable)
{
nsresult rv = NS_OK;
char* bestFlavor = nsnull;
nsCOMPtr<nsISupports> genericDataObj;
PRUint32 len = 0;
if ( NS_SUCCEEDED(transferable->GetAnyTransferData(&bestFlavor, getter_AddRefs(genericDataObj), &len)) )
{
nsAutoTxnsConserveSelection dontSpazMySelection(this);
nsAutoString flavor, stuffToPaste;
flavor.AssignWithConversion( bestFlavor ); // just so we can use flavor.Equals()
if (flavor.EqualsWithConversion(kUnicodeMime))
{
nsCOMPtr<nsISupportsWString> textDataObj ( do_QueryInterface(genericDataObj) );
if (textDataObj && len > 0)
{
PRUnichar* text = nsnull;
textDataObj->ToString ( &text );
stuffToPaste.Assign ( text, len / 2 );
nsAutoEditBatch beginBatching(this);
rv = InsertText(stuffToPaste.GetUnicode());
if (text)
nsMemory::Free(text);
}
}
}
nsCRT::free(bestFlavor);
// Try to scroll the selection into view if the paste/drop succeeded
if (NS_SUCCEEDED(rv))
{
nsCOMPtr<nsISelectionController> selCon;
if (NS_SUCCEEDED(GetSelectionController(getter_AddRefs(selCon))) && selCon)
selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION);
}
return rv;
}
NS_IMETHODIMP nsPlaintextEditor::InsertFromDrop(nsIDOMEvent* aDropEvent)
{
ForceCompositionEnd();
nsresult rv;
NS_WITH_SERVICE(nsIDragService, dragService, "@mozilla.org/widget/dragservice;1", &rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDragSession> dragSession(do_QueryInterface(dragService));
if (!dragSession) return NS_OK;
// Get the nsITransferable interface for getting the data from the drop
nsCOMPtr<nsITransferable> trans;
rv = PrepareTransferable(getter_AddRefs(trans));
if (NS_FAILED(rv)) return rv;
if (!trans) return NS_OK; // NS_ERROR_FAILURE; SHOULD WE FAIL?
PRUint32 numItems = 0;
rv = dragSession->GetNumDropItems(&numItems);
if (NS_FAILED(rv)) return rv;
// Combine any deletion and drop insertion into one transaction
nsAutoEditBatch beginBatching(this);
PRUint32 i;
PRBool doPlaceCaret = PR_TRUE;
for (i = 0; i < numItems; ++i)
{
rv = dragSession->GetData(trans, i);
if (NS_FAILED(rv)) return rv;
if (!trans) return NS_OK; // NS_ERROR_FAILURE; Should we fail?
if ( doPlaceCaret )
{
// check if the user pressed the key to force a copy rather than a move
// if we run into problems here, we'll just assume the user doesn't want a copy
PRBool userWantsCopy = PR_FALSE;
nsCOMPtr<nsIDOMNSUIEvent> nsuiEvent (do_QueryInterface(aDropEvent));
if (!nsuiEvent) return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aDropEvent) );
if (mouseEvent)
#ifdef XP_MAC
mouseEvent->GetAltKey(&userWantsCopy);
#else
mouseEvent->GetCtrlKey(&userWantsCopy);
#endif
// Source doc is null if source is *not* the current editor document
nsCOMPtr<nsIDOMDocument> srcdomdoc;
rv = dragSession->GetSourceDocument(getter_AddRefs(srcdomdoc));
if (NS_FAILED(rv)) return rv;
// Current doc is destination
nsCOMPtr<nsIDOMDocument>destdomdoc;
rv = GetDocument(getter_AddRefs(destdomdoc));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsISelection> selection;
rv = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(rv)) return rv;
if (!selection) return NS_ERROR_FAILURE;
PRBool isCollapsed;
rv = selection->GetIsCollapsed(&isCollapsed);
if (NS_FAILED(rv)) return rv;
// Parent and offset under the mouse cursor
nsCOMPtr<nsIDOMNode> newSelectionParent;
PRInt32 newSelectionOffset = 0;
rv = nsuiEvent->GetRangeParent(getter_AddRefs(newSelectionParent));
if (NS_FAILED(rv)) return rv;
if (!newSelectionParent) return NS_ERROR_FAILURE;
rv = nsuiEvent->GetRangeOffset(&newSelectionOffset);
if (NS_FAILED(rv)) return rv;
/* Creating a range to store insert position because when
we delete the selection, range gravity will make sure the insertion
point is in the correct place */
nsCOMPtr<nsIDOMRange> destinationRange;
rv = CreateRange(newSelectionParent, newSelectionOffset,newSelectionParent, newSelectionOffset, getter_AddRefs(destinationRange));
if (NS_FAILED(rv))
return rv;
if(!destinationRange)
return NS_ERROR_FAILURE;
// We never have to delete if selection is already collapsed
PRBool deleteSelection = PR_FALSE;
PRBool cursorIsInSelection = PR_FALSE;
// Check if mouse is in the selection
if (!isCollapsed)
{
PRInt32 rangeCount;
rv = selection->GetRangeCount(&rangeCount);
if (NS_FAILED(rv))
return rv?rv:NS_ERROR_FAILURE;
for (PRInt32 j = 0; j < rangeCount; j++)
{
nsCOMPtr<nsIDOMRange> range;
rv = selection->GetRangeAt(j, getter_AddRefs(range));
if (NS_FAILED(rv) || !range)
continue;//dont bail yet, iterate through them all
nsCOMPtr<nsIDOMNSRange> nsrange(do_QueryInterface(range));
if (NS_FAILED(rv) || !nsrange)
continue;//dont bail yet, iterate through them all
rv = nsrange->IsPointInRange(newSelectionParent, newSelectionOffset, &cursorIsInSelection);
if(cursorIsInSelection)
break;
}
if (cursorIsInSelection)
{
// Dragging within same doc can't drop on itself -- leave!
// (We shouldn't get here - drag event shouldn't have started if over selection)
if (srcdomdoc == destdomdoc)
return NS_OK;
// Dragging from another window onto a selection
// XXX Decision made to NOT do this,
// note that 4.x does replace if dropped on
//deleteSelection = PR_TRUE;
}
else
{
// We are NOT over the selection
if (srcdomdoc == destdomdoc)
{
// Within the same doc: delete if user doesn't want to copy
deleteSelection = !userWantsCopy;
}
else
{
// Different source doc: Don't delete
deleteSelection = PR_FALSE;
}
}
}
if (deleteSelection)
{
rv = DeleteSelection(eNone);
if (NS_FAILED(rv)) return rv;
}
// If we deleted the selection because we dropped from another doc,
// then we don't have to relocate the caret (insert at the deletion point)
if (!(deleteSelection && srcdomdoc != destdomdoc))
{
// Move the selection to the point under the mouse cursor
rv = destinationRange->GetStartContainer(getter_AddRefs(newSelectionParent));
if (NS_FAILED(rv))
return rv;
if(!newSelectionParent)
return NS_ERROR_FAILURE;
rv = destinationRange->GetStartOffset(&newSelectionOffset);
if (NS_FAILED(rv))
return rv;
selection->Collapse(newSelectionParent, newSelectionOffset);
}
// We have to figure out whether to delete and relocate caret only once
doPlaceCaret = PR_FALSE;
}
rv = InsertTextFromTransferable(trans);
}
return rv;
}
NS_IMETHODIMP nsPlaintextEditor::CanDrag(nsIDOMEvent *aDragEvent, PRBool &aCanDrag)
{
/* we really should be checking the XY coordinates of the mouseevent and ensure that
* that particular point is actually within the selection (not just that there is a selection)
*/
aCanDrag = PR_FALSE;
// KLUDGE to work around bug 50703
// After double click and object property editing,
// we get a spurious drag event
if (mIgnoreSpuriousDragEvent)
{
mIgnoreSpuriousDragEvent = PR_FALSE;
return NS_OK;
}
nsCOMPtr<nsISelection> selection;
nsresult res = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
PRBool isCollapsed;
res = selection->GetIsCollapsed(&isCollapsed);
if (NS_FAILED(res)) return res;
// if we are collapsed, we have no selection so nothing to drag
if ( isCollapsed )
return NS_OK;
nsCOMPtr<nsIDOMEventTarget> eventTarget;
res = aDragEvent->GetOriginalTarget(getter_AddRefs(eventTarget));
if (NS_FAILED(res)) return res;
if ( eventTarget )
{
nsCOMPtr<nsIDOMNode> eventTargetDomNode = do_QueryInterface(eventTarget);
if ( eventTargetDomNode )
{
PRBool amTargettedCorrectly = PR_FALSE;
res = selection->ContainsNode(eventTargetDomNode, PR_FALSE, &amTargettedCorrectly);
if (NS_FAILED(res)) return res;
aCanDrag = amTargettedCorrectly;
}
}
return NS_OK;
}
NS_IMETHODIMP nsPlaintextEditor::DoDrag(nsIDOMEvent *aDragEvent)
{
nsresult rv;
nsCOMPtr<nsIDOMEventTarget> eventTarget;
rv = aDragEvent->GetTarget(getter_AddRefs(eventTarget));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDOMNode> domnode = do_QueryInterface(eventTarget);
/* get the selection to be dragged */
nsCOMPtr<nsISelection> selection;
rv = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(rv)) return rv;
/* create an array of transferables */
nsCOMPtr<nsISupportsArray> transferableArray;
NS_NewISupportsArray(getter_AddRefs(transferableArray));
if (transferableArray == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
/* get the drag service */
NS_WITH_SERVICE(nsIDragService, dragService, "@mozilla.org/widget/dragservice;1", &rv);
if (NS_FAILED(rv)) return rv;
/* create html flavor transferable */
nsCOMPtr<nsITransferable> trans;
rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull,
NS_GET_IID(nsITransferable),
getter_AddRefs(trans));
if (NS_FAILED(rv)) return rv;
if ( !trans ) return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr<nsIDOMDocument> domdoc;
rv = GetDocument(getter_AddRefs(domdoc));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
if (doc)
{
nsCOMPtr<nsIDocumentEncoder> docEncoder;
docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "text/html");
NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE);
docEncoder->Init(doc, NS_LITERAL_STRING("text/html"), 0);
docEncoder->SetSelection(selection);
nsAutoString buffer;
rv = docEncoder->EncodeToString(buffer);
if (NS_FAILED(rv))
return rv;
if ( !buffer.IsEmpty() )
{
nsCOMPtr<nsIFormatConverter> htmlConverter;
rv = nsComponentManager::CreateInstance(kCHTMLFormatConverterCID, nsnull, NS_GET_IID(nsIFormatConverter),
getter_AddRefs(htmlConverter));
if (NS_FAILED(rv)) return rv;
if (!htmlConverter) return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr<nsISupportsWString> dataWrapper;
rv = nsComponentManager::CreateInstance(NS_SUPPORTS_WSTRING_CONTRACTID, nsnull,
NS_GET_IID(nsISupportsWString), getter_AddRefs(dataWrapper));
if (NS_FAILED(rv)) return rv;
if ( !dataWrapper ) return NS_ERROR_OUT_OF_MEMORY;
rv = trans->AddDataFlavor(kHTMLMime);
if (NS_FAILED(rv)) return rv;
rv = trans->SetConverter(htmlConverter);
if (NS_FAILED(rv)) return rv;
rv = dataWrapper->SetData( NS_CONST_CAST(PRUnichar*, buffer.GetUnicode()) );
if (NS_FAILED(rv)) return rv;
// QI the data object an |nsISupports| so that when the transferable holds
// onto it, it will addref the correct interface.
nsCOMPtr<nsISupports> nsisupportsDataWrapper ( do_QueryInterface(dataWrapper) );
rv = trans->SetTransferData(kHTMLMime, nsisupportsDataWrapper, buffer.Length() * 2);
if (NS_FAILED(rv)) return rv;
/* add the transferable to the array */
rv = transferableArray->AppendElement(trans);
if (NS_FAILED(rv)) return rv;
/* invoke drag */
unsigned int flags;
// in some cases we'll want to cut rather than copy... hmmmmm...
// if ( wantToCut )
// flags = nsIDragService.DRAGDROP_ACTION_COPY + nsIDragService.DRAGDROP_ACTION_MOVE;
// else
flags = nsIDragService::DRAGDROP_ACTION_COPY + nsIDragService::DRAGDROP_ACTION_MOVE;
rv = dragService->InvokeDragSession( domnode, transferableArray, nsnull, flags);
if (NS_FAILED(rv)) return rv;
aDragEvent->PreventBubble();
}
}
return rv;
}
NS_IMETHODIMP nsPlaintextEditor::Paste(PRInt32 aSelectionType)
{
ForceCompositionEnd();
// Get Clipboard Service
nsresult rv;
NS_WITH_SERVICE ( nsIClipboard, clipboard, kCClipboardCID, &rv );
if ( NS_FAILED(rv) )
return rv;
// Get the nsITransferable interface for getting the data from the clipboard
nsCOMPtr<nsITransferable> trans;
rv = PrepareTransferable(getter_AddRefs(trans));
if (NS_SUCCEEDED(rv) && trans)
{
// Get the Data from the clipboard
if (NS_SUCCEEDED(clipboard->GetData(trans, aSelectionType)) && IsModifiable())
{
rv = InsertTextFromTransferable(trans);
}
}
return rv;
}
NS_IMETHODIMP nsPlaintextEditor::CanPaste(PRInt32 aSelectionType, PRBool &aCanPaste)
{
aCanPaste = PR_FALSE;
nsresult rv;
NS_WITH_SERVICE(nsIClipboard, clipboard, kCClipboardCID, &rv);
if (NS_FAILED(rv)) return rv;
// the flavors that we can deal with
char* textEditorFlavors[] = { kUnicodeMime, nsnull };
nsCOMPtr<nsISupportsArray> flavorsList;
rv = nsComponentManager::CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, nsnull,
NS_GET_IID(nsISupportsArray), getter_AddRefs(flavorsList));
if (NS_FAILED(rv)) return rv;
PRUint32 editorFlags;
GetFlags(&editorFlags);
// add the flavors for text editors
for (char** flavor = textEditorFlavors; *flavor; flavor++)
{
nsCOMPtr<nsISupportsString> flavorString;
nsComponentManager::CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, nsnull,
NS_GET_IID(nsISupportsString), getter_AddRefs(flavorString));
if (flavorString)
{
flavorString->SetData(*flavor);
flavorsList->AppendElement(flavorString);
}
}
PRBool haveFlavors;
rv = clipboard->HasDataMatchingFlavors(flavorsList, aSelectionType, &haveFlavors);
if (NS_FAILED(rv)) return rv;
aCanPaste = haveFlavors;
return NS_OK;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,228 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#ifndef nsPlaintextEditor_h__
#define nsPlaintextEditor_h__
#include "nsCOMPtr.h"
#include "nsIPlaintextEditor.h"
#include "nsEditor.h"
#include "nsIDOMElement.h"
#include "nsIDOMEventListener.h"
#include "TypeInState.h"
#include "nsEditRules.h"
class nsIDOMKeyEvent;
class nsITransferable;
class nsIDOMEventReceiver;
class nsIDocumentEncoder;
/**
* The text editor implementation.
* Use to edit text document represented as a DOM tree.
*/
class nsPlaintextEditor : public nsEditor,
public nsIPlaintextEditor
{
public:
// Interfaces for addref and release and queryinterface
// NOTE macro used is for classes that inherit from
// another class. Only the base class should use NS_DECL_ISUPPORTS
NS_DECL_ISUPPORTS_INHERITED
/* below used by TypedText() */
enum {
eTypedText, /* user typed text */
eTypedBR, /* user typed shift-enter to get a br */
eTypedBreak /* user typed enter */
};
nsPlaintextEditor();
virtual ~nsPlaintextEditor();
/* ------------ nsIPlaintextEditor methods -------------- */
NS_DECL_NSIPLAINTEXTEDITOR
/* ------------ nsIEditorIMESupport overrides -------------- */
NS_IMETHOD SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList,nsTextEventReply* aReply);
NS_IMETHOD GetReconversionString(nsReconversionEventReply* aReply);
/* ------------ Overrides of nsEditor interface methods -------------- */
/** prepare the editor for use */
NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags);
NS_IMETHOD GetDocumentIsEmpty(PRBool *aDocumentIsEmpty);
NS_IMETHOD DeleteSelection(EDirection aAction);
NS_IMETHOD SetDocumentCharacterSet(const PRUnichar* characterSet);
/** we override this here to install event listeners */
NS_IMETHOD PostCreate();
NS_IMETHOD GetFlags(PRUint32 *aFlags);
NS_IMETHOD SetFlags(PRUint32 aFlags);
NS_IMETHOD Undo(PRUint32 aCount);
NS_IMETHOD Redo(PRUint32 aCount);
NS_IMETHOD Cut();
NS_IMETHOD CanCut(PRBool &aCanCut);
NS_IMETHOD Copy();
NS_IMETHOD CanCopy(PRBool &aCanCopy);
NS_IMETHOD Paste(PRInt32 aSelectionType);
NS_IMETHOD CanPaste(PRInt32 aSelectionType, PRBool &aCanPaste);
NS_IMETHOD CanDrag(nsIDOMEvent *aDragEvent, PRBool &aCanDrag);
NS_IMETHOD DoDrag(nsIDOMEvent *aDragEvent);
NS_IMETHOD InsertFromDrop(nsIDOMEvent* aDropEvent);
NS_IMETHOD OutputToString(nsAWritableString& aOutputString,
const nsAReadableString& aFormatType,
PRUint32 aFlags);
NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream,
const nsAReadableString& aFormatType,
const nsAReadableString* aCharsetOverride,
PRUint32 aFlags);
/** All editor operations which alter the doc should be prefaced
* with a call to StartOperation, naming the action and direction */
NS_IMETHOD StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection);
/** All editor operations which alter the doc should be followed
* with a call to EndOperation */
NS_IMETHOD EndOperation();
/** make the given selection span the entire document */
NS_IMETHOD SelectEntireDocument(nsISelection *aSelection);
/* ------------ Utility Routines, not part of public API -------------- */
NS_IMETHOD TypedText(const PRUnichar* aString, PRInt32 aAction);
/** returns the absolute position of the end points of aSelection
* in the document as a text stream.
*/
nsresult GetTextSelectionOffsets(nsISelection *aSelection,
PRInt32 &aStartOffset,
PRInt32 &aEndOffset);
nsresult GetAbsoluteOffsetsForPoints(nsIDOMNode *aInStartNode,
PRInt32 aInStartOffset,
nsIDOMNode *aInEndNode,
PRInt32 aInEndOffset,
nsIDOMNode *aInCommonParentNode,
PRInt32 &aOutStartOffset,
PRInt32 &aEndOffset);
protected:
NS_IMETHOD InitRules();
void BeginEditorInit();
nsresult EndEditorInit();
/** install the event listeners for the editor
* used to be part of Init, but now broken out into a separate method
* called by PostCreate, giving the caller the chance to interpose
* their own listeners before we install our own backstops.
*/
NS_IMETHOD InstallEventListeners();
/** returns the layout object (nsIFrame in the real world) for aNode
* @param aNode the content to get a frame for
* @param aLayoutObject the "primary frame" for aNode, if one exists. May be null
* @return NS_OK whether a frame is found or not
* an error if some serious error occurs
*/
NS_IMETHOD GetLayoutObject(nsIDOMNode *aInNode, nsISupports **aOutLayoutObject);
NS_IMETHOD GetBodyStyleContext(nsIStyleContext** aStyleContext);
// Helpers for output routines
NS_IMETHOD GetAndInitDocEncoder(const nsAReadableString& aFormatType,
PRUint32 aFlags,
const nsAReadableString* aCharset,
nsIDocumentEncoder** encoder);
// key event helpers
NS_IMETHOD CreateBR(nsIDOMNode *aNode, PRInt32 aOffset,
nsCOMPtr<nsIDOMNode> *outBRNode, EDirection aSelect = eNone);
NS_IMETHOD CreateBRImpl(nsCOMPtr<nsIDOMNode> *aInOutParent,
PRInt32 *aInOutOffset,
nsCOMPtr<nsIDOMNode> *outBRNode,
EDirection aSelect);
NS_IMETHOD InsertBR(nsCOMPtr<nsIDOMNode> *outBRNode);
NS_IMETHOD IsRootTag(nsString &aTag, PRBool &aIsTag);
// factored methods for handling insertion of data from transferables (drag&drop or clipboard)
NS_IMETHOD PrepareTransferable(nsITransferable **transferable);
NS_IMETHOD InsertTextFromTransferable(nsITransferable *transferable);
/** simple utility to handle any error with event listener allocation or registration */
void HandleEventListenerError();
/* small utility routine to test the eEditorReadonly bit */
PRBool IsModifiable();
nsresult GetDOMEventReceiver(nsIDOMEventReceiver **aEventReceiver);
//XXX Kludge: Used to suppress spurious drag/drop events (bug 50703)
PRBool mIgnoreSpuriousDragEvent;
NS_IMETHOD IgnoreSpuriousDragEvent(PRBool aIgnoreSpuriousDragEvent) {mIgnoreSpuriousDragEvent = aIgnoreSpuriousDragEvent; return NS_OK;}
// Data members
protected:
nsCOMPtr<nsIEditRules> mRules;
nsCOMPtr<nsIDOMEventListener> mKeyListenerP;
nsCOMPtr<nsIDOMEventListener> mMouseListenerP;
nsCOMPtr<nsIDOMEventListener> mTextListenerP;
nsCOMPtr<nsIDOMEventListener> mCompositionListenerP;
nsCOMPtr<nsIDOMEventListener> mDragListenerP;
nsCOMPtr<nsIDOMEventListener> mFocusListenerP;
PRBool mIsComposing;
PRInt32 mMaxTextLength;
PRInt32 mInitTriggerCounter;
public:
static nsIAtom *gTypingTxnName;
static nsIAtom *gIMETxnName;
static nsIAtom *gDeleteTxnName;
// friends
friend class nsHTMLEditRules;
friend class nsTextEditRules;
friend class nsAutoEditInitRulesTrigger;
};
#endif //nsPlaintextEditor_h__

View File

@ -0,0 +1,640 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
#include "nsSelectionState.h"
#include "nsIDOMCharacterData.h"
#include "nsIDOMNode.h"
#include "nsIDOMRange.h"
#include "nsISelection.h"
#include "nsEditor.h"
#include "nsLayoutCID.h"
static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID);
/***************************************************************************
* 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() : mArray(){}
nsSelectionState::~nsSelectionState()
{
MakeEmpty();
}
nsresult
nsSelectionState::SaveSelection(nsISelection *aSel)
{
if (!aSel) return NS_ERROR_NULL_POINTER;
nsresult res = NS_OK;
PRInt32 i,rangeCount, arrayCount = mArray.Count();
nsRangeStore *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 nsRangeStore;
mArray.AppendElement(item);
}
}
// else if we have too many, delete them
else if (rangeCount>arrayCount)
{
while ((item = (nsRangeStore*)mArray.ElementAt(rangeCount)))
{
delete item;
mArray.RemoveElementAt(rangeCount);
}
}
// now store the selection ranges
for (i=0; i<rangeCount; i++)
{
item = (nsRangeStore*)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(nsISelection *aSel)
{
if (!aSel) return NS_ERROR_NULL_POINTER;
nsresult res = NS_OK;
PRInt32 i, arrayCount = mArray.Count();
nsRangeStore *item;
// clear out selection
aSel->RemoveAllRanges();
// set the selection ranges anew
for (i=0; i<arrayCount; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsIDOMRange> range;
item->GetRange(address_of(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;
nsRangeStore *item;
item = (nsRangeStore*)mArray.ElementAt(0);
if (!item) return PR_FALSE;
nsCOMPtr<nsIDOMRange> range;
item->GetRange(address_of(range));
if (!range) return PR_FALSE;
PRBool bIsCollapsed;
range->GetCollapsed(&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;
nsRangeStore *myItem, *itsItem;
for (i=0; i<myCount; i++)
{
myItem = (nsRangeStore*)mArray.ElementAt(0);
itsItem = (nsRangeStore*)(aSelState->mArray.ElementAt(0));
if (!myItem || !itsItem) return PR_FALSE;
nsCOMPtr<nsIDOMRange> myRange, itsRange;
myItem->GetRange(address_of(myRange));
itsItem->GetRange(address_of(itsRange));
if (!myRange || !itsRange) return PR_FALSE;
PRInt32 compResult;
myRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START, itsRange, &compResult);
if (compResult) return PR_FALSE;
myRange->CompareBoundaryPoints(nsIDOMRange::END_TO_END, itsRange, &compResult);
if (compResult) return PR_FALSE;
}
// if we got here, they are equal
return PR_TRUE;
}
void
nsSelectionState::MakeEmpty()
{
// free any items in the array
nsRangeStore *item;
while ((item = (nsRangeStore*)mArray.ElementAt(0)))
{
delete item;
mArray.RemoveElementAt(0);
}
}
PRBool
nsSelectionState::IsEmpty()
{
return (mArray.Count() == 0);
}
/***************************************************************************
* nsRangeUpdater: class for updating nsIDOMRanges in response to editor actions.
*/
nsRangeUpdater::nsRangeUpdater() : mArray(), mLock(PR_FALSE) {}
nsRangeUpdater::~nsRangeUpdater()
{
// free any items in the array
nsRangeStore *item;
while ((item = (nsRangeStore*)mArray.ElementAt(0)))
{
delete item;
mArray.RemoveElementAt(0);
}
}
void*
nsRangeUpdater::RegisterRange(nsIDOMRange *aRange)
{
nsRangeStore *item = new nsRangeStore;
if (!item) return nsnull;
item->StoreRange(aRange);
mArray.AppendElement(item);
return item;
}
nsCOMPtr<nsIDOMRange>
nsRangeUpdater::ReclaimRange(void *aCookie)
{
nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie);
if (!item) return nsnull;
nsCOMPtr<nsIDOMRange> outRange;
item->GetRange(address_of(outRange));
mArray.RemoveElement(aCookie);
delete item;
return outRange;
}
void
nsRangeUpdater::DropRange(void *aCookie)
{
nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie);
if (!item) return;
mArray.RemoveElement(aCookie);
delete item;
}
void
nsRangeUpdater::RegisterRangeItem(nsRangeStore *aRangeItem)
{
if (!aRangeItem) return;
mArray.AppendElement(aRangeItem);
return;
}
void
nsRangeUpdater::DropRangeItem(nsRangeStore *aRangeItem)
{
if (!aRangeItem) return;
mArray.RemoveElement(aRangeItem);
return;
}
nsresult
nsRangeUpdater::RegisterSelectionState(nsSelectionState &aSelState)
{
PRInt32 i, theCount = aSelState.mArray.Count();
if (theCount < 1) return NS_ERROR_FAILURE;
nsRangeStore *item;
for (i=0; i<theCount; i++)
{
item = (nsRangeStore*)aSelState.mArray.ElementAt(i);
RegisterRangeItem(item);
}
return NS_OK;;
}
nsresult
nsRangeUpdater::DropSelectionState(nsSelectionState &aSelState)
{
PRInt32 i, theCount = aSelState.mArray.Count();
if (theCount < 1) return NS_ERROR_FAILURE;
nsRangeStore *item;
for (i=0; i<theCount; i++)
{
item = (nsRangeStore*)aSelState.mArray.ElementAt(i);
DropRangeItem(item);
}
return NS_OK;;
}
// gravity methods:
nsresult
nsRangeUpdater::SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aParent) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if ((item->startNode.get() == aParent) && (item->startOffset > aPosition))
item->startOffset++;
if ((item->endNode.get() == aParent) && (item->endOffset > aPosition))
item->endOffset++;
}
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition)
{
return SelAdjCreateNode(aParent, aPosition);
}
nsresult
nsRangeUpdater::SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aNode) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if ((item->startNode.get() == aParent) && (item->startOffset > aOffset))
item->startOffset--;
if ((item->endNode.get() == aParent) && (item->endOffset > aOffset))
item->endOffset--;
}
// MOOSE: also check inside of aNode, expensive. But in theory, we shouldn't
// actually hit this case in the usage i forsee for this.
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aOldRightNode || !aNewLeftNode) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsCOMPtr<nsIDOMNode> parent;
PRInt32 offset;
nsresult result = nsEditor::GetNodeLocation(aOldRightNode, address_of(parent), &offset);
if (NS_FAILED(result)) return result;
// first part is same as inserting aNewLeftnode
result = SelAdjInsertNode(parent,offset-1);
if (NS_FAILED(result)) return result;
// next step is to check for range enpoints inside aOldRightNode
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if (item->startNode.get() == aOldRightNode)
{
if (item->startOffset > aOffset)
{
item->startOffset -= aOffset;
}
else
{
item->startNode = aNewLeftNode;
}
}
if (item->endNode.get() == aOldRightNode)
{
if (item->endOffset > aOffset)
{
item->endOffset -= aOffset;
}
else
{
item->endNode = aNewLeftNode;
}
}
}
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjJoinNodes(nsIDOMNode *aLeftNode,
nsIDOMNode *aRightNode,
nsIDOMNode *aParent,
PRInt32 aOffset,
PRInt32 aOldLeftNodeLength)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aLeftNode || !aRightNode || !aParent) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
// adjust endpoints in aParent
if (item->startNode.get() == aParent)
{
if (item->startOffset > aOffset)
{
item->startOffset--;
}
else if (item->startOffset == aOffset)
{
// join keeps right hand node
item->startNode = aRightNode;
item->startOffset = aOldLeftNodeLength;
}
}
if (item->endNode.get() == aParent)
{
if (item->endOffset > aOffset)
{
item->endOffset--;
}
else if (item->endOffset == aOffset)
{
// join keeps right hand node
item->endNode = aRightNode;
item->endOffset = aOldLeftNodeLength;
}
}
// adjust endpoints in aRightNode
if (item->startNode.get() == aRightNode)
item->startOffset += aOldLeftNodeLength;
if (item->endNode.get() == aRightNode)
item->endOffset += aOldLeftNodeLength;
}
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
return NS_OK;
}
nsresult
nsRangeUpdater::WillReplaceContainer()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
return NS_OK;
}
nsresult
nsRangeUpdater::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode)
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
if (!aOriginalNode || !aNewNode) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if (item->startNode.get() == aOriginalNode)
item->startNode = aNewNode;
if (item->endNode.get() == aOriginalNode)
item->endNode = aNewNode;
}
return NS_OK;
}
nsresult
nsRangeUpdater::WillRemoveContainer()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
return NS_OK;
}
nsresult
nsRangeUpdater::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen)
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
if (!aNode || !aParent) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if (item->startNode.get() == aNode)
{
item->startNode = aParent;
item->startOffset += aOffset;
}
if (item->endNode.get() == aNode)
{
item->endNode = aParent;
item->endOffset += aOffset;
}
if ((item->startNode.get() == aParent) && (item->startOffset > aOffset))
item->startOffset += (PRInt32)aNodeOrigLen-1;
if ((item->endNode.get() == aParent) && (item->endOffset > aOffset))
item->endOffset += (PRInt32)aNodeOrigLen-1;
}
return NS_OK;
}
nsresult
nsRangeUpdater::WillInsertContainer()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
return NS_OK;
}
nsresult
nsRangeUpdater::DidInsertContainer()
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
return NS_OK;
}
nsresult
nsRangeUpdater::WillMoveNode()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
return NS_OK;
}
nsresult
nsRangeUpdater::DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset)
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
if (!aOldParent || !aNewParent) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
// like a delete in aOldParent
if ((item->startNode.get() == aOldParent) && (item->startOffset > aOldOffset))
item->startOffset--;
if ((item->endNode.get() == aOldParent) && (item->endOffset > aOldOffset))
item->endOffset--;
// and like an insert in aNewParent
if ((item->startNode.get() == aNewParent) && (item->startOffset > aNewOffset))
item->startOffset++;
if ((item->endNode.get() == aNewParent) && (item->endOffset > aNewOffset))
item->endOffset++;
}
return NS_OK;
}
/***************************************************************************
* helper class for nsSelectionState. nsRangeStore stores range endpoints.
*/
// DEBUG: PRInt32 nsRangeStore::n = 0;
nsRangeStore::nsRangeStore()
{
// DEBUG: n++; printf("range store alloc count=%d\n", n);
}
nsRangeStore::~nsRangeStore()
{
// DEBUG: n--; printf("range store alloc count=%d\n", n);
}
nsresult nsRangeStore::StoreRange(nsIDOMRange *aRange)
{
if (!aRange) return NS_ERROR_NULL_POINTER;
aRange->GetStartContainer(getter_AddRefs(startNode));
aRange->GetEndContainer(getter_AddRefs(endNode));
aRange->GetStartOffset(&startOffset);
aRange->GetEndOffset(&endOffset);
return NS_OK;
}
nsresult nsRangeStore::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;
}

View File

@ -0,0 +1,245 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#ifndef __selectionstate_h__
#define __selectionstate_h__
#include "nsCOMPtr.h"
#include "nsVoidArray.h"
#include "nsIDOMNode.h"
class nsIDOMCharacterData;
class nsIDOMRange;
class nsISelection;
/***************************************************************************
* 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 nsRangeStore
{
nsRangeStore();
~nsRangeStore();
nsresult StoreRange(nsIDOMRange *aRange);
nsresult GetRange(nsCOMPtr<nsIDOMRange> *outRange);
nsCOMPtr<nsIDOMNode> startNode;
PRInt32 startOffset;
nsCOMPtr<nsIDOMNode> endNode;
PRInt32 endOffset;
// DEBUG: static PRInt32 n;
};
class nsSelectionState
{
public:
nsSelectionState();
~nsSelectionState();
nsresult SaveSelection(nsISelection *aSel);
nsresult RestoreSelection(nsISelection *aSel);
PRBool IsCollapsed();
PRBool IsEqual(nsSelectionState *aSelState);
void MakeEmpty();
PRBool IsEmpty();
protected:
nsVoidArray mArray;
friend class nsRangeUpdater;
};
class nsRangeUpdater
{
public:
nsRangeUpdater();
~nsRangeUpdater();
void* RegisterRange(nsIDOMRange *aRange);
nsCOMPtr<nsIDOMRange> ReclaimRange(void *aCookie);
void DropRange(void *aCookie);
void RegisterRangeItem(nsRangeStore *aRangeItem);
void DropRangeItem(nsRangeStore *aRangeItem);
nsresult RegisterSelectionState(nsSelectionState &aSelState);
nsresult DropSelectionState(nsSelectionState &aSelState);
// editor selection gravity routines. Note that we can't always depend on
// DOM Range gravity to do what we want to the "real" selection. For instance,
// if you move a node, that corresponds to deleting it and reinserting it.
// DOM Range gravity will promote the selection out of the node on deletion,
// which is not what you want if you know you are reinserting it.
nsresult SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition);
nsresult SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition);
nsresult SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset);
nsresult SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode);
nsresult SelAdjJoinNodes(nsIDOMNode *aLeftNode,
nsIDOMNode *aRightNode,
nsIDOMNode *aParent,
PRInt32 aOffset,
PRInt32 aOldLeftNodeLength);
nsresult SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString);
nsresult SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength);
// the following gravity routines need will/did sandwiches, because the other gravity
// routines will be called inside of these sandwiches, but should be ignored.
nsresult WillReplaceContainer();
nsresult DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode);
nsresult WillRemoveContainer();
nsresult DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen);
nsresult WillInsertContainer();
nsresult DidInsertContainer();
nsresult WillMoveNode();
nsresult DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset);
protected:
nsVoidArray mArray;
PRBool mLock;
};
/***************************************************************************
* another helper class for nsSelectionState. stack based class for doing
* Will/DidReplaceContainer()
*/
class nsAutoReplaceContainerSelNotify
{
private:
nsRangeUpdater &mRU;
nsIDOMNode *mOriginalNode;
nsIDOMNode *mNewNode;
public:
nsAutoReplaceContainerSelNotify(nsRangeUpdater &aRangeUpdater, nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) :
mRU(aRangeUpdater)
,mOriginalNode(aOriginalNode)
,mNewNode(aNewNode)
{
mRU.WillReplaceContainer();
}
~nsAutoReplaceContainerSelNotify()
{
mRU.DidReplaceContainer(mOriginalNode, mNewNode);
}
};
/***************************************************************************
* another helper class for nsSelectionState. stack based class for doing
* Will/DidRemoveContainer()
*/
class nsAutoRemoveContainerSelNotify
{
private:
nsRangeUpdater &mRU;
nsIDOMNode *mNode;
nsIDOMNode *mParent;
PRInt32 mOffset;
PRUint32 mNodeOrigLen;
public:
nsAutoRemoveContainerSelNotify(nsRangeUpdater &aRangeUpdater,
nsIDOMNode *aNode,
nsIDOMNode *aParent,
PRInt32 aOffset,
PRUint32 aNodeOrigLen) :
mRU(aRangeUpdater)
,mNode(aNode)
,mParent(aParent)
,mOffset(aOffset)
,mNodeOrigLen(aNodeOrigLen)
{
mRU.WillRemoveContainer();
}
~nsAutoRemoveContainerSelNotify()
{
mRU.DidRemoveContainer(mNode, mParent, mOffset, mNodeOrigLen);
}
};
/***************************************************************************
* another helper class for nsSelectionState. stack based class for doing
* Will/DidInsertContainer()
*/
class nsAutoInsertContainerSelNotify
{
private:
nsRangeUpdater &mRU;
public:
nsAutoInsertContainerSelNotify(nsRangeUpdater &aRangeUpdater) :
mRU(aRangeUpdater)
{
mRU.WillInsertContainer();
}
~nsAutoInsertContainerSelNotify()
{
mRU.DidInsertContainer();
}
};
/***************************************************************************
* another helper class for nsSelectionState. stack based class for doing
* Will/DidMoveNode()
*/
class nsAutoMoveNodeSelNotify
{
private:
nsRangeUpdater &mRU;
nsIDOMNode *mOldParent;
nsIDOMNode *mNewParent;
PRInt32 mOldOffset;
PRInt32 mNewOffset;
public:
nsAutoMoveNodeSelNotify(nsRangeUpdater &aRangeUpdater,
nsIDOMNode *aOldParent,
PRInt32 aOldOffset,
nsIDOMNode *aNewParent,
PRInt32 aNewOffset) :
mRU(aRangeUpdater)
,mOldParent(aOldParent)
,mNewParent(aNewParent)
,mOldOffset(aOldOffset)
,mNewOffset(aNewOffset)
{
mRU.WillMoveNode();
}
~nsAutoMoveNodeSelNotify()
{
mRU.DidMoveNode(mOldParent, mOldOffset, mNewParent, mNewOffset);
}
};
#endif

View File

@ -48,7 +48,7 @@ static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID);
#define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \
if ((mFlags & nsIHTMLEditor::eEditorReadonlyMask) || (mFlags & nsIHTMLEditor::eEditorDisabledMask)) \
if ((mFlags & nsIPlaintextEditor::eEditorReadonlyMask) || (mFlags & nsIPlaintextEditor::eEditorDisabledMask)) \
{ \
*aCancel = PR_TRUE; \
return NS_OK; \
@ -101,7 +101,7 @@ NS_IMPL_QUERY_INTERFACE1(nsTextEditRules, nsIEditRules)
********************************************************/
NS_IMETHODIMP
nsTextEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags)
nsTextEditRules::Init(nsPlaintextEditor *aEditor, PRUint32 aFlags)
{
if (!aEditor) { return NS_ERROR_NULL_POINTER; }
@ -125,10 +125,8 @@ nsTextEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags)
if (NS_FAILED(res)) return res;
// create a range that is the entire body contents
nsCOMPtr<nsIDOMRange> wholeDoc;
res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange),
getter_AddRefs(wholeDoc));
if (NS_FAILED(res)) return res;
nsCOMPtr<nsIDOMRange> wholeDoc = do_CreateInstance(kRangeCID);
if (!wholeDoc) return NS_ERROR_NULL_POINTER;
wholeDoc->SetStart(mBody,0);
nsCOMPtr<nsIDOMNodeList> list;
res = mBody->GetChildNodes(getter_AddRefs(list));
@ -163,11 +161,9 @@ nsTextEditRules::SetFlags(PRUint32 aFlags)
// SetFlags() is really meant to only be called once
// and at editor init time.
PRBool willBePlaintext = (aFlags & nsIHTMLEditor::eEditorPlaintextMask) != 0;
PRBool alreadyPlaintext = (mFlags & nsIHTMLEditor::eEditorPlaintextMask) != 0;
PRBool willBePlaintext = (aFlags & nsIPlaintextEditor::eEditorPlaintextMask) != 0;
PRBool alreadyPlaintext = (mFlags & nsIPlaintextEditor::eEditorPlaintextMask) != 0;
mFlags = aFlags;
return NS_OK;
}
@ -207,8 +203,6 @@ nsTextEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)
res = CreateBogusNodeIfNeeded(selection);
if (NS_FAILED(res)) return res;
// create moz-br and adjust selection if needed
res = AdjustSelection(selection, aDirection);
}
return res;
}
@ -340,42 +334,7 @@ nsTextEditRules::WillInsert(nsISelection *aSelection, PRBool *aCancel)
mBogusNode = do_QueryInterface(nsnull);
}
// this next only works for collapsed selections right now,
// because selection is a pain to work with when not collapsed.
// (no good way to extend start or end of selection)
PRBool bCollapsed;
nsresult res = aSelection->GetIsCollapsed(&bCollapsed);
if (NS_FAILED(res)) return res;
if (!bCollapsed) return NS_OK;
// if we are after a mozBR in the same block, then move selection
// to be before it
nsCOMPtr<nsIDOMNode> selNode, priorNode;
PRInt32 selOffset;
// get the (collapsed) selection location
res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
if (NS_FAILED(res)) return res;
// get prior node
res = mEditor->GetPriorHTMLNode(selNode, selOffset, address_of(priorNode));
if (NS_SUCCEEDED(res) && priorNode && nsHTMLEditUtils::IsMozBR(priorNode))
{
nsCOMPtr<nsIDOMNode> block1, block2;
if (mEditor->IsBlockNode(selNode)) block1 = selNode;
else block1 = mEditor->GetBlockNodeParent(selNode);
block2 = mEditor->GetBlockNodeParent(priorNode);
if (block1 != block2) return NS_OK;
// if we are here then the selection is right after a mozBR
// that is in the same block as the selection. We need to move
// the selection start to be before the mozBR.
res = nsEditor::GetNodeLocation(priorNode, address_of(selNode), &selOffset);
if (NS_FAILED(res)) return res;
res = aSelection->Collapse(selNode,selOffset);
if (NS_FAILED(res)) return res;
}
return res;
return NS_OK;
}
nsresult
@ -384,44 +343,13 @@ nsTextEditRules::DidInsert(nsISelection *aSelection, nsresult aResult)
return NS_OK;
}
nsresult
nsTextEditRules::GetTopEnclosingPre(nsIDOMNode *aNode,
nsIDOMNode** aOutPreNode)
{
// check parms
if (!aNode || !aOutPreNode)
return NS_ERROR_NULL_POINTER;
*aOutPreNode = 0;
nsresult res = NS_OK;
nsCOMPtr<nsIDOMNode> node, parentNode;
node = do_QueryInterface(aNode);
while (node)
{
nsAutoString tag;
nsEditor::GetTagString(node, tag);
if (tag.EqualsWithConversion("pre", PR_TRUE))
*aOutPreNode = node;
else if (tag.EqualsWithConversion("body", PR_TRUE))
break;
res = node->GetParentNode(getter_AddRefs(parentNode));
if (NS_FAILED(res)) return res;
node = parentNode;
}
NS_IF_ADDREF(*aOutPreNode);
return res;
}
nsresult
nsTextEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBool *aHandled)
{
if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
CANCEL_OPERATION_IF_READONLY_OR_DISABLED
*aHandled = PR_FALSE;
if (mFlags & nsIHTMLEditor::eEditorSingleLineMask) {
if (mFlags & nsIPlaintextEditor::eEditorSingleLineMask) {
*aCancel = PR_TRUE;
}
else
@ -444,72 +372,6 @@ nsTextEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
// we want to ignore result of WillInsert()
*aCancel = PR_FALSE;
// Mail rule: split any <pre> tags in the way,
// since they're probably quoted text.
// For now, do this for all plaintext since mail is our main customer
// and we don't currently set eEditorMailMask for plaintext mail.
if (mTheAction != nsHTMLEditor::kOpInsertQuotation) // && mFlags & nsIHTMLEditor::eEditorMailMask)
{
nsCOMPtr<nsIDOMNode> preNode, selNode;
PRInt32 selOffset, newOffset;
res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
if (NS_FAILED(res)) return res;
// If any of the following fail, then just proceed with the
// normal break insertion without worrying about the error
res = GetTopEnclosingPre(selNode, getter_AddRefs(preNode));
if (NS_SUCCEEDED(res) && preNode)
{
// Only split quote nodes: see if it has the attribute _moz_quote
nsCOMPtr<nsIDOMElement> preElement (do_QueryInterface(preNode));
if (preElement)
{
nsString mozQuote; mozQuote.AssignWithConversion("_moz_quote");
nsString mozQuoteVal;
PRBool isMozQuote = PR_FALSE;
if (NS_SUCCEEDED(mEditor->GetAttributeValue(preElement, mozQuote,
mozQuoteVal, isMozQuote))
&& isMozQuote)
{
printf("It's a moz quote -- splitting\n");
nsCOMPtr<nsIDOMNode> outLeftNode;
nsCOMPtr<nsIDOMNode> outRightNode;
res = mEditor->SplitNodeDeep(preNode, selNode, selOffset, &newOffset, PR_TRUE, address_of(outLeftNode), address_of(outRightNode));
if (NS_FAILED(res)) return res;
PRBool bIsEmptyNode;
// rememeber parent of selNode now, since we might delete selNode below
res = preNode->GetParentNode(getter_AddRefs(selNode));
if (NS_FAILED(res)) return res;
if (outLeftNode)
{
res = mEditor->IsEmptyNode(outLeftNode, &bIsEmptyNode, PR_TRUE, PR_FALSE);
if (NS_FAILED(res)) return res;
if (bIsEmptyNode) mEditor->DeleteNode(outLeftNode);
}
if (outRightNode)
{
// HACK alert: consume a br if there is one at front of node
nsCOMPtr<nsIDOMNode> firstNode;
res = mEditor->GetFirstEditableNode(outRightNode, address_of(firstNode));
if (firstNode && nsHTMLEditUtils::IsBreak(firstNode))
{
mEditor->DeleteNode(firstNode);
}
res = mEditor->IsEmptyNode(outRightNode, &bIsEmptyNode, PR_TRUE, PR_FALSE);
if (NS_FAILED(res)) return res;
if (bIsEmptyNode) mEditor->DeleteNode(outRightNode);
}
nsCOMPtr<nsIDOMNode> brNode;
// last ePrevious param causes selection to be set before the break
res = mEditor->CreateBR(selNode, newOffset, address_of(brNode), nsIEditor::ePrevious);
*aHandled = PR_TRUE;
}
}
}
}
}
return NS_OK;
}
@ -519,42 +381,44 @@ nsTextEditRules::DidInsertBreak(nsISelection *aSelection, nsresult aResult)
{
// we only need to execute the stuff below if we are a plaintext editor.
// html editors have a different mechanism for putting in mozBR's
// (because there are a bunch more placesyou have to worry about it in html)
if (!nsIHTMLEditor::eEditorPlaintextMask & mFlags) return NS_OK;
// (because there are a bunch more places you have to worry about it in html)
if (!nsIPlaintextEditor::eEditorPlaintextMask & mFlags) return NS_OK;
// if we are at the end of the document, we need to insert
// a special mozBR following the normal br, and then set the
// selection to stick to the mozBR.
PRInt32 selOffset;
nsCOMPtr<nsIDOMNode> nearNode, selNode;
nsCOMPtr<nsIDOMNode> nearNode, selNode, root, temp;
nsCOMPtr<nsIDOMElement> rootElem;
nsresult res;
res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
if (NS_FAILED(res)) return res;
res = mEditor->GetPriorHTMLNode(selNode, selOffset, address_of(nearNode));
// confirm we are at end of document
if (selOffset == 0) return NS_OK; // cant be after a br if we are at offset 0
res = mEditor->GetRootElement(getter_AddRefs(rootElem));
if (NS_FAILED(res)) return res;
root = do_QueryInterface(rootElem);
if (!root) return NS_ERROR_NULL_POINTER;
if (selNode != root) return NS_OK; // must be inside text node or somewhere other than end of root
temp = mEditor->GetChildAt(selNode, selOffset);
if (temp) return NS_OK; // cant be at end of there is a node after us.
nearNode = mEditor->GetChildAt(selNode, selOffset-1);
if (nearNode && nsHTMLEditUtils::IsBreak(nearNode) && !nsHTMLEditUtils::IsMozBR(nearNode))
{
PRBool bIsLast;
res = mEditor->IsLastEditableChild(nearNode, &bIsLast);
nsCOMPtr<nsISelection> sel(aSelection);
nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(sel));
// need to insert special moz BR. Why? Because if we don't
// the user will see no new line for the break. Also, things
// like table cells won't grow in height.
nsCOMPtr<nsIDOMNode> brNode;
res = CreateMozBR(selNode, selOffset, address_of(brNode));
if (NS_FAILED(res)) return res;
res = nsEditor::GetNodeLocation(brNode, address_of(selNode), &selOffset);
if (NS_FAILED(res)) return res;
selPrivate->SetInterlinePosition(PR_TRUE);
res = aSelection->Collapse(selNode,selOffset);
if (NS_FAILED(res)) return res;
if (bIsLast)
{
nsCOMPtr<nsISelection> sel(aSelection);
nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(sel));
// need to insert special moz BR. Why? Because if we don't
// the user will see no new line for the break. Also, things
// like table cells won't grow in height.
nsCOMPtr<nsIDOMNode> brNode;
res = CreateMozBR(selNode, selOffset, address_of(brNode));
if (NS_FAILED(res)) return res;
res = nsEditor::GetNodeLocation(brNode, address_of(selNode), &selOffset);
if (NS_FAILED(res)) return res;
selPrivate->SetInterlinePosition(PR_TRUE);
res = aSelection->Collapse(selNode,selOffset);
if (NS_FAILED(res)) return res;
}
}
// mEditor->DumpContentTree();
return res;
}
@ -595,7 +459,7 @@ nsTextEditRules::WillInsertText(PRInt32 aAction,
if (NS_FAILED(res)) return res;
// handle password field docs
if (mFlags & nsIHTMLEditor::eEditorPasswordMask)
if (mFlags & nsIPlaintextEditor::eEditorPasswordMask)
{
res = mEditor->GetTextSelectionOffsets(aSelection, start, end);
NS_ASSERTION((NS_SUCCEEDED(res)), "getTextSelectionOffsets failed!");
@ -621,7 +485,7 @@ nsTextEditRules::WillInsertText(PRInt32 aAction,
// handle password field data
// this has the side effect of changing all the characters in aOutString
// to the replacement character
if (mFlags & nsIHTMLEditor::eEditorPasswordMask)
if (mFlags & nsIPlaintextEditor::eEditorPasswordMask)
{
res = EchoInsertionToPWBuff(start, end, outString);
if (NS_FAILED(res)) return res;
@ -646,7 +510,7 @@ nsTextEditRules::WillInsertText(PRInt32 aAction,
rv = prefs->GetIntPref("editor.singleLine.pasteNewlines",
&singleLineNewlineBehavior);
if (nsIHTMLEditor::eEditorSingleLineMask & mFlags)
if (nsIPlaintextEditor::eEditorSingleLineMask & mFlags)
{
if (singleLineNewlineBehavior == eReplaceWithSpaces)
outString->ReplaceChar(CRLF, ' ');
@ -731,7 +595,7 @@ nsTextEditRules::WillInsertText(PRInt32 aAction,
// is it a return?
if (subStr.EqualsWithConversion("\n"))
{
if (nsIHTMLEditor::eEditorSingleLineMask & mFlags)
if (nsIPlaintextEditor::eEditorSingleLineMask & mFlags)
{
NS_ASSERTION((singleLineNewlineBehavior == ePasteIntact),
"Newline improperly getting into single-line edit field!");
@ -814,7 +678,7 @@ nsTextEditRules::WillSetTextProperty(nsISelection *aSelection, PRBool *aCancel,
nsresult res = NS_OK;
// XXX: should probably return a success value other than NS_OK that means "not allowed"
if (nsIHTMLEditor::eEditorPlaintextMask & mFlags) {
if (nsIPlaintextEditor::eEditorPlaintextMask & mFlags) {
*aCancel = PR_TRUE;
}
return res;
@ -834,7 +698,7 @@ nsTextEditRules::WillRemoveTextProperty(nsISelection *aSelection, PRBool *aCance
nsresult res = NS_OK;
// XXX: should probably return a success value other than NS_OK that means "not allowed"
if (nsIHTMLEditor::eEditorPlaintextMask & mFlags) {
if (nsIPlaintextEditor::eEditorPlaintextMask & mFlags) {
*aCancel = PR_TRUE;
}
return res;
@ -864,7 +728,7 @@ nsTextEditRules::WillDeleteSelection(nsISelection *aSelection,
*aCancel = PR_TRUE;
return NS_OK;
}
if (mFlags & nsIHTMLEditor::eEditorPasswordMask)
if (mFlags & nsIPlaintextEditor::eEditorPasswordMask)
{
// manage the password buffer
PRInt32 start, end;
@ -1098,7 +962,7 @@ nsTextEditRules::WillOutputText(nsISelection *aSelection,
if (PR_TRUE == aOutputFormat->EqualsWithConversion("text/plain"))
{ // only use these rules for plain text output
if (mFlags & nsIHTMLEditor::eEditorPasswordMask)
if (mFlags & nsIPlaintextEditor::eEditorPasswordMask)
{
*aOutString = mPasswordText;
*aHandled = PR_TRUE;
@ -1288,7 +1152,7 @@ nsTextEditRules::TruncateInsertionIfNeeded(nsISelection *aSelection,
nsresult res = NS_OK;
*aOutString = *aInString;
if ((-1 != aMaxLength) && (mFlags & nsIHTMLEditor::eEditorPlaintextMask))
if ((-1 != aMaxLength) && (mFlags & nsIPlaintextEditor::eEditorPlaintextMask))
{
// Get the current text length.
// Get the length of inString.
@ -1388,69 +1252,3 @@ nsTextEditRules::DeleteEmptyTextNode(nsIDOMNode *aNode)
return PR_FALSE;
}
nsresult
nsTextEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection aDirection)
{
if (!aSelection) return NS_ERROR_NULL_POINTER;
// if the selection isn't collapsed, do nothing.
PRBool bCollapsed;
nsresult res = aSelection->GetIsCollapsed(&bCollapsed);
if (NS_FAILED(res)) return res;
if (!bCollapsed) return res;
// get the (collapsed) selection location
nsCOMPtr<nsIDOMNode> selNode, temp;
PRInt32 selOffset;
res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
if (NS_FAILED(res)) return res;
temp = selNode;
// are we in an editable node?
while (!mEditor->IsEditable(selNode))
{
// scan up the tree until we find an editable place to be
res = nsEditor::GetNodeLocation(temp, address_of(selNode), &selOffset);
if (NS_FAILED(res)) return res;
if (!selNode) return NS_ERROR_FAILURE;
temp = selNode;
}
// are we in a text node?
nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(selNode);
if (textNode)
return NS_OK; // we LIKE it when we are in a text node. that RULZ
// do we need to insert a special mozBR? We do if we are:
// 1) after a block element AND
// 2) at the end of the body OR before another block
nsCOMPtr<nsIDOMNode> priorNode, nextNode;
res = mEditor->GetPriorHTMLSibling(selNode, selOffset, address_of(priorNode));
if (NS_FAILED(res)) return res;
res = mEditor->GetNextHTMLSibling(selNode, selOffset, address_of(nextNode));
if (NS_FAILED(res)) return res;
// is priorNode a block?
if (priorNode && mEditor->IsBlockNode(priorNode))
{
if (!nextNode || mEditor->IsBlockNode(nextNode))
{
nsCOMPtr<nsISelection> sel(aSelection);
nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(sel));
nsCOMPtr<nsIDOMNode> brNode;
res = CreateMozBR(selNode, selOffset, address_of(brNode));
if (NS_FAILED(res)) return res;
res = nsEditor::GetNodeLocation(brNode, address_of(selNode), &selOffset);
if (NS_FAILED(res)) return res;
// selection stays *before* moz-br, sticking to it
selPrivate->SetInterlinePosition(PR_TRUE);
res = aSelection->Collapse(selNode,selOffset);
if (NS_FAILED(res)) return res;
}
}
return res;
}

View File

@ -25,7 +25,7 @@
#include "nsCOMPtr.h"
#include "nsHTMLEditor.h"
#include "nsPlaintextEditor.h"
#include "nsIDOMNode.h"
#include "nsEditRules.h"
@ -50,7 +50,7 @@ public:
virtual ~nsTextEditRules();
// nsIEditRules methods
NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags);
NS_IMETHOD Init(nsPlaintextEditor *aEditor, PRUint32 aFlags);
NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection);
NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection);
NS_IMETHOD WillDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled);
@ -163,17 +163,15 @@ protected:
PRBool DeleteEmptyTextNode(nsIDOMNode *aNode);
nsresult AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection aDirection);
// data members
nsHTMLEditor *mEditor; // note that we do not refcount the editor
nsString mPasswordText; // a buffer we use to store the real value of password editors
nsCOMPtr<nsIDOMNode> mBogusNode; // magic node acts as placeholder in empty doc
nsCOMPtr<nsIDOMNode> mBody; // cached root node
PRUint32 mFlags;
PRUint32 mActionNesting;
PRBool mLockRulesSniffing;
PRInt32 mTheAction; // the top level editor action
nsPlaintextEditor *mEditor; // note that we do not refcount the editor
nsString mPasswordText; // a buffer we use to store the real value of password editors
nsCOMPtr<nsIDOMNode> mBogusNode; // magic node acts as placeholder in empty doc
nsCOMPtr<nsIDOMNode> mBody; // cached root node
PRUint32 mFlags;
PRUint32 mActionNesting;
PRBool mLockRulesSniffing;
PRInt32 mTheAction; // the top level editor action
// friends
friend class nsAutoLockRulesSniffing;

View File

@ -0,0 +1,58 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsIGenericFactory.h"
#include "nsEditorCID.h"
#include "nsEditor.h" // for gInstanceCount
#include "nsPlaintextEditor.h"
#include "nsEditorService.h"
#include "nsEditorController.h" //CID
////////////////////////////////////////////////////////////////////////
// Define the contructor function for the objects
//
// NOTE: This creates an instance of objects by using the default constructor
//
NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorController)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorService)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsPlaintextEditor)
////////////////////////////////////////////////////////////////////////
// Define a table of CIDs implemented by this module along with other
// information like the function to create an instance, contractid, and
// class name.
//
static nsModuleComponentInfo components[] = {
{ "Text Editor", NS_TEXTEDITOR_CID,
"@mozilla.org/editor/texteditor;1", nsPlaintextEditorConstructor, },
{ "Editor Controller", NS_EDITORCONTROLLER_CID,
"@mozilla.org/editor/editorcontroller;1", nsEditorControllerConstructor, }
};
////////////////////////////////////////////////////////////////////////
// Implement the NSGetModule() exported function for your module
// and the entire implementation of the module object.
//
NS_IMPL_NSGETMODULE("nsEditorModule", components)

View File

@ -910,7 +910,7 @@ nsEditorShell::InstantiateEditor(nsIDOMDocument *aDoc, nsIPresShell *aPresShell)
{
if (mEditorTypeString.EqualsWithConversion("text"))
{
err = editor->Init(aDoc, aPresShell, nsnull, selCon, nsIHTMLEditor::eEditorPlaintextMask | nsIHTMLEditor::eEditorEnableWrapHackMask);
err = editor->Init(aDoc, aPresShell, nsnull, selCon, nsIPlaintextEditor::eEditorPlaintextMask | nsIPlaintextEditor::eEditorEnableWrapHackMask);
mEditorType = ePlainTextEditorType;
}
else if (mEditorTypeString.EqualsWithConversion("html") || mEditorTypeString.IsEmpty()) // empty string default to HTML editor
@ -920,7 +920,7 @@ nsEditorShell::InstantiateEditor(nsIDOMDocument *aDoc, nsIPresShell *aPresShell)
}
else if (mEditorTypeString.EqualsWithConversion("htmlmail")) // HTML editor with special mail rules
{
err = editor->Init(aDoc, aPresShell, nsnull, selCon, nsIHTMLEditor::eEditorMailMask);
err = editor->Init(aDoc, aPresShell, nsnull, selCon, nsIPlaintextEditor::eEditorMailMask);
mEditorType = eHTMLTextEditorType;
}
else
@ -3423,7 +3423,7 @@ nsEditorShell::GetDocumentEditable(PRBool *aDocumentEditable)
PRUint32 editorFlags;
editor->GetFlags(&editorFlags);
if (editorFlags & nsIHTMLEditor::eEditorReadonlyMask)
if (editorFlags & nsIPlaintextEditor::eEditorReadonlyMask)
return NS_OK;
nsCOMPtr<nsIDOMDocument> doc;

View File

@ -31,6 +31,32 @@
[scriptable, uuid(b5f39ed4-1dd1-11b2-9d00-fd54d6f54962)]
interface nsIPlaintextEditor : nsISupports
{
/* the bits in an editor behavior mask. */
const short eEditorPlaintextBit = 0; /* only plain text entry is allowed via events */
const short eEditorSingleLineBit = 1; /* enter key and CR-LF handled specially */
const short eEditorPasswordBit = 2; /* text is not entered into content, only a representative character */
const short eEditorReadonlyBit = 3; /* editing events are disabled. Editor may still accept focus. */
const short eEditorDisabledBit = 4; /* all events are disabled (like scrolling). Editor will not accept focus. */
const short eEditorFilterInputBit = 5; /* text input is limited to certain character types, use mFilter */
const short eEditorMailBit = 6; /* use mail-compose editting rules */
const short eEditorDisableForcedUpdatesBit = 7; /* prevent immediate view refreshes */
const short eEditorDisableForcedReflowsBit = 8; /* prevent immediate reflows */
const short eEditorEnableWrapHackBit = 9; /* allow the editor to set font: monospace on the root node */
const short eEditorWidgetBit = 10; /* bit for widgets */
const long eEditorPlaintextMask = 1;
const long eEditorSingleLineMask = 2;
const long eEditorPasswordMask = 4;
const long eEditorReadonlyMask = 8;
const long eEditorDisabledMask = 16;
const long eEditorFilterInputMask = 32;
const long eEditorMailMask = 64;
const long eEditorDisableForcedUpdatesMask = 128;
const long eEditorDisableForcedReflowsMask = 256;
const long eEditorEnableWrapHackMask = 512;
const long eEditorWidgetMask = 1024;
/**
* The length of the contents in characters.
*/

View File

@ -28,7 +28,7 @@
0xa6cf911d, 0x15b3, 0x11d2, \
{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32} }
class nsHTMLEditor;
class nsPlaintextEditor;
class nsISelection;
/***************************************************************************
@ -57,7 +57,7 @@ public:
//Interfaces for addref and release and queryinterface
//NOTE: Use NS_DECL_ISUPPORTS_INHERITED in any class inherited from nsIEditRules
NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags)=0;
NS_IMETHOD Init(nsPlaintextEditor *aEditor, PRUint32 aFlags)=0;
NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection)=0;
NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)=0;
NS_IMETHOD WillDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled)=0;

View File

@ -59,6 +59,7 @@
#include "nsIKBStateControl.h"
#include "nsIWidget.h"
#include "nsIScrollbar.h"
#include "nsIPlaintextEditor.h"
#include "nsIFrame.h" // Needed by IME code
@ -93,7 +94,7 @@
#include "JoinElementTxn.h"
#include "nsStyleSheetTxns.h"
#include "IMETextTxn.h"
#include "nsIHTMLEditor.h"
// included for nsEditor::CreateHTMLContent
#include "nsIElementFactory.h"
#include "nsINodeInfo.h"
@ -148,738 +149,6 @@ 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() : mArray(){}
nsSelectionState::~nsSelectionState()
{
MakeEmpty();
}
nsresult
nsSelectionState::SaveSelection(nsISelection *aSel)
{
if (!aSel) return NS_ERROR_NULL_POINTER;
nsresult res = NS_OK;
PRInt32 i,rangeCount, arrayCount = mArray.Count();
nsRangeStore *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 nsRangeStore;
mArray.AppendElement(item);
}
}
// else if we have too many, delete them
else if (rangeCount>arrayCount)
{
while ((item = (nsRangeStore*)mArray.ElementAt(rangeCount)))
{
delete item;
mArray.RemoveElementAt(rangeCount);
}
}
// now store the selection ranges
for (i=0; i<rangeCount; i++)
{
item = (nsRangeStore*)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(nsISelection *aSel)
{
if (!aSel) return NS_ERROR_NULL_POINTER;
nsresult res = NS_OK;
PRInt32 i, arrayCount = mArray.Count();
nsRangeStore *item;
// clear out selection
aSel->RemoveAllRanges();
// set the selection ranges anew
for (i=0; i<arrayCount; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsIDOMRange> range;
item->GetRange(address_of(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;
nsRangeStore *item;
item = (nsRangeStore*)mArray.ElementAt(0);
if (!item) return PR_FALSE;
nsCOMPtr<nsIDOMRange> range;
item->GetRange(address_of(range));
if (!range) return PR_FALSE;
PRBool bIsCollapsed;
range->GetCollapsed(&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;
nsRangeStore *myItem, *itsItem;
for (i=0; i<myCount; i++)
{
myItem = (nsRangeStore*)mArray.ElementAt(0);
itsItem = (nsRangeStore*)(aSelState->mArray.ElementAt(0));
if (!myItem || !itsItem) return PR_FALSE;
nsCOMPtr<nsIDOMRange> myRange, itsRange;
myItem->GetRange(address_of(myRange));
itsItem->GetRange(address_of(itsRange));
if (!myRange || !itsRange) return PR_FALSE;
PRInt32 compResult;
myRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START, itsRange, &compResult);
if (compResult) return PR_FALSE;
myRange->CompareBoundaryPoints(nsIDOMRange::END_TO_END, itsRange, &compResult);
if (compResult) return PR_FALSE;
}
// if we got here, they are equal
return PR_TRUE;
}
void
nsSelectionState::MakeEmpty()
{
// free any items in the array
nsRangeStore *item;
while ((item = (nsRangeStore*)mArray.ElementAt(0)))
{
delete item;
mArray.RemoveElementAt(0);
}
}
PRBool
nsSelectionState::IsEmpty()
{
return (mArray.Count() == 0);
}
/***************************************************************************
* nsRangeUpdater: class for updating nsIDOMRanges in response to editor actions.
*/
nsRangeUpdater::nsRangeUpdater() : mArray(), mLock(PR_FALSE) {}
nsRangeUpdater::~nsRangeUpdater()
{
// free any items in the array
nsRangeStore *item;
while ((item = (nsRangeStore*)mArray.ElementAt(0)))
{
delete item;
mArray.RemoveElementAt(0);
}
}
void*
nsRangeUpdater::RegisterRange(nsIDOMRange *aRange)
{
nsRangeStore *item = new nsRangeStore;
if (!item) return nsnull;
item->StoreRange(aRange);
mArray.AppendElement(item);
return item;
}
nsCOMPtr<nsIDOMRange>
nsRangeUpdater::ReclaimRange(void *aCookie)
{
nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie);
if (!item) return nsnull;
nsCOMPtr<nsIDOMRange> outRange;
item->GetRange(address_of(outRange));
mArray.RemoveElement(aCookie);
delete item;
return outRange;
}
void
nsRangeUpdater::DropRange(void *aCookie)
{
nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie);
if (!item) return;
mArray.RemoveElement(aCookie);
delete item;
}
void
nsRangeUpdater::RegisterRangeItem(nsRangeStore *aRangeItem)
{
if (!aRangeItem) return;
mArray.AppendElement(aRangeItem);
return;
}
void
nsRangeUpdater::DropRangeItem(nsRangeStore *aRangeItem)
{
if (!aRangeItem) return;
mArray.RemoveElement(aRangeItem);
return;
}
nsresult
nsRangeUpdater::RegisterSelectionState(nsSelectionState &aSelState)
{
PRInt32 i, theCount = aSelState.mArray.Count();
if (theCount < 1) return NS_ERROR_FAILURE;
nsRangeStore *item;
for (i=0; i<theCount; i++)
{
item = (nsRangeStore*)aSelState.mArray.ElementAt(i);
RegisterRangeItem(item);
}
return NS_OK;;
}
nsresult
nsRangeUpdater::DropSelectionState(nsSelectionState &aSelState)
{
PRInt32 i, theCount = aSelState.mArray.Count();
if (theCount < 1) return NS_ERROR_FAILURE;
nsRangeStore *item;
for (i=0; i<theCount; i++)
{
item = (nsRangeStore*)aSelState.mArray.ElementAt(i);
DropRangeItem(item);
}
return NS_OK;;
}
// gravity methods:
nsresult
nsRangeUpdater::SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aParent) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if ((item->startNode.get() == aParent) && (item->startOffset > aPosition))
item->startOffset++;
if ((item->endNode.get() == aParent) && (item->endOffset > aPosition))
item->endOffset++;
}
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition)
{
return SelAdjCreateNode(aParent, aPosition);
}
nsresult
nsRangeUpdater::SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aNode) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if ((item->startNode.get() == aParent) && (item->startOffset > aOffset))
item->startOffset--;
if ((item->endNode.get() == aParent) && (item->endOffset > aOffset))
item->endOffset--;
}
// MOOSE: also check inside of aNode, expensive. But in theory, we shouldn't
// actually hit this case in the usage i forsee for this.
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aOldRightNode || !aNewLeftNode) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsCOMPtr<nsIDOMNode> parent;
PRInt32 offset;
nsresult result = nsEditor::GetNodeLocation(aOldRightNode, address_of(parent), &offset);
if (NS_FAILED(result)) return result;
// first part is same as inserting aNewLeftnode
result = SelAdjInsertNode(parent,offset-1);
if (NS_FAILED(result)) return result;
// next step is to check for range enpoints inside aOldRightNode
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if (item->startNode.get() == aOldRightNode)
{
if (item->startOffset > aOffset)
{
item->startOffset -= aOffset;
}
else
{
item->startNode = aNewLeftNode;
}
}
if (item->endNode.get() == aOldRightNode)
{
if (item->endOffset > aOffset)
{
item->endOffset -= aOffset;
}
else
{
item->endNode = aNewLeftNode;
}
}
}
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjJoinNodes(nsIDOMNode *aLeftNode,
nsIDOMNode *aRightNode,
nsIDOMNode *aParent,
PRInt32 aOffset,
PRInt32 aOldLeftNodeLength)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aLeftNode || !aRightNode || !aParent) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
// adjust endpoints in aParent
if (item->startNode.get() == aParent)
{
if (item->startOffset > aOffset)
{
item->startOffset--;
}
else if (item->startOffset == aOffset)
{
// join keeps right hand node
item->startNode = aRightNode;
item->startOffset = aOldLeftNodeLength;
}
}
if (item->endNode.get() == aParent)
{
if (item->endOffset > aOffset)
{
item->endOffset--;
}
else if (item->endOffset == aOffset)
{
// join keeps right hand node
item->endNode = aRightNode;
item->endOffset = aOldLeftNodeLength;
}
}
// adjust endpoints in aRightNode
if (item->startNode.get() == aRightNode)
item->startOffset += aOldLeftNodeLength;
if (item->endNode.get() == aRightNode)
item->endOffset += aOldLeftNodeLength;
}
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
return NS_OK;
}
nsresult
nsRangeUpdater::WillReplaceContainer()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
return NS_OK;
}
nsresult
nsRangeUpdater::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode)
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
if (!aOriginalNode || !aNewNode) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if (item->startNode.get() == aOriginalNode)
item->startNode = aNewNode;
if (item->endNode.get() == aOriginalNode)
item->endNode = aNewNode;
}
return NS_OK;
}
nsresult
nsRangeUpdater::WillRemoveContainer()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
return NS_OK;
}
nsresult
nsRangeUpdater::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen)
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
if (!aNode || !aParent) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if (item->startNode.get() == aNode)
{
item->startNode = aParent;
item->startOffset += aOffset;
}
if (item->endNode.get() == aNode)
{
item->endNode = aParent;
item->endOffset += aOffset;
}
if ((item->startNode.get() == aParent) && (item->startOffset > aOffset))
item->startOffset += (PRInt32)aNodeOrigLen-1;
if ((item->endNode.get() == aParent) && (item->endOffset > aOffset))
item->endOffset += (PRInt32)aNodeOrigLen-1;
}
return NS_OK;
}
nsresult
nsRangeUpdater::WillInsertContainer()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
return NS_OK;
}
nsresult
nsRangeUpdater::DidInsertContainer()
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
return NS_OK;
}
nsresult
nsRangeUpdater::WillMoveNode()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
return NS_OK;
}
nsresult
nsRangeUpdater::DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset)
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
if (!aOldParent || !aNewParent) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
// like a delete in aOldParent
if ((item->startNode.get() == aOldParent) && (item->startOffset > aOldOffset))
item->startOffset--;
if ((item->endNode.get() == aOldParent) && (item->endOffset > aOldOffset))
item->endOffset--;
// and like an insert in aNewParent
if ((item->startNode.get() == aNewParent) && (item->startOffset > aNewOffset))
item->startOffset++;
if ((item->endNode.get() == aNewParent) && (item->endOffset > aNewOffset))
item->endOffset++;
}
return NS_OK;
}
/***************************************************************************
* helper class for nsSelectionState. nsRangeStore stores range endpoints.
*/
// DEBUG: PRInt32 nsRangeStore::n = 0;
nsRangeStore::nsRangeStore()
{
// DEBUG: n++; printf("range store alloc count=%d\n", n);
}
nsRangeStore::~nsRangeStore()
{
// DEBUG: n--; printf("range store alloc count=%d\n", n);
}
nsresult nsRangeStore::StoreRange(nsIDOMRange *aRange)
{
if (!aRange) return NS_ERROR_NULL_POINTER;
aRange->GetStartContainer(getter_AddRefs(startNode));
aRange->GetEndContainer(getter_AddRefs(endNode));
aRange->GetStartOffset(&startOffset);
aRange->GetEndOffset(&endOffset);
return NS_OK;
}
nsresult nsRangeStore::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;
}
/***************************************************************************
* another helper class for nsSelectionState. stack based class for doing
* Will/DidReplaceContainer()
*/
class nsAutoReplaceContainerSelNotify
{
private:
nsRangeUpdater &mRU;
nsIDOMNode *mOriginalNode;
nsIDOMNode *mNewNode;
public:
nsAutoReplaceContainerSelNotify(nsRangeUpdater &aRangeUpdater, nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) :
mRU(aRangeUpdater)
,mOriginalNode(aOriginalNode)
,mNewNode(aNewNode)
{
mRU.WillReplaceContainer();
}
~nsAutoReplaceContainerSelNotify()
{
mRU.DidReplaceContainer(mOriginalNode, mNewNode);
}
};
/***************************************************************************
* another helper class for nsSelectionState. stack based class for doing
* Will/DidRemoveContainer()
*/
class nsAutoRemoveContainerSelNotify
{
private:
nsRangeUpdater &mRU;
nsIDOMNode *mNode;
nsIDOMNode *mParent;
PRInt32 mOffset;
PRUint32 mNodeOrigLen;
public:
nsAutoRemoveContainerSelNotify(nsRangeUpdater &aRangeUpdater,
nsIDOMNode *aNode,
nsIDOMNode *aParent,
PRInt32 aOffset,
PRUint32 aNodeOrigLen) :
mRU(aRangeUpdater)
,mNode(aNode)
,mParent(aParent)
,mOffset(aOffset)
,mNodeOrigLen(aNodeOrigLen)
{
mRU.WillRemoveContainer();
}
~nsAutoRemoveContainerSelNotify()
{
mRU.DidRemoveContainer(mNode, mParent, mOffset, mNodeOrigLen);
}
};
/***************************************************************************
* another helper class for nsSelectionState. stack based class for doing
* Will/DidInsertContainer()
*/
class nsAutoInsertContainerSelNotify
{
private:
nsRangeUpdater &mRU;
public:
nsAutoInsertContainerSelNotify(nsRangeUpdater &aRangeUpdater) :
mRU(aRangeUpdater)
{
mRU.WillInsertContainer();
}
~nsAutoInsertContainerSelNotify()
{
mRU.DidInsertContainer();
}
};
/***************************************************************************
* another helper class for nsSelectionState. stack based class for doing
* Will/DidMoveNode()
*/
class nsAutoMoveNodeSelNotify
{
private:
nsRangeUpdater &mRU;
nsIDOMNode *mOldParent;
nsIDOMNode *mNewParent;
PRInt32 mOldOffset;
PRInt32 mNewOffset;
public:
nsAutoMoveNodeSelNotify(nsRangeUpdater &aRangeUpdater,
nsIDOMNode *aOldParent,
PRInt32 aOldOffset,
nsIDOMNode *aNewParent,
PRInt32 aNewOffset) :
mRU(aRangeUpdater)
,mOldParent(aOldParent)
,mNewParent(aNewParent)
,mOldOffset(aOldOffset)
,mNewOffset(aNewOffset)
{
mRU.WillMoveNode();
}
~nsAutoMoveNodeSelNotify()
{
mRU.DidMoveNode(mOldParent, mOldOffset, mNewParent, mNewOffset);
}
};
//---------------------------------------------------------------------------
//
// nsEditor: base editor class implementation
@ -2606,7 +1875,7 @@ nsEditor::ForceCompositionEnd()
#endif
#ifdef XP_UNIX
if(mFlags & nsIHTMLEditor::eEditorPasswordMask)
if(mFlags & nsIPlaintextEditor::eEditorPasswordMask)
return NS_OK;
#endif
@ -5059,7 +4328,6 @@ nsEditor::IsNextCharWhitespace(nsIDOMNode *aParentNode,
textNode->GetLength(&strLength);
if (strLength)
{
// you could use nsITextContent::IsOnlyWhitespace here
textNode->SubstringData(0,1,tempString);
*outIsSpace = nsCRT::IsAsciiSpace(tempString.First());
*outIsNBSP = (tempString.First() == nbsp);
@ -5391,7 +4659,7 @@ nsresult nsEditor::EndUpdateViewBatch()
PRBool forceReflow = PR_TRUE;
if (flags & nsIHTMLEditor::eEditorDisableForcedReflowsMask)
if (flags & nsIPlaintextEditor::eEditorDisableForcedReflowsMask)
forceReflow = PR_FALSE;
nsCOMPtr<nsIPresShell> presShell;
@ -5401,7 +4669,7 @@ nsresult nsEditor::EndUpdateViewBatch()
PRUint32 updateFlag = NS_VMREFRESH_IMMEDIATE;
if (flags & nsIHTMLEditor::eEditorDisableForcedUpdatesMask)
if (flags & nsIPlaintextEditor::eEditorDisableForcedUpdatesMask)
updateFlag = NS_VMREFRESH_NO_SYNC;
#ifdef HACK_FORCE_REDRAW
@ -5482,9 +4750,129 @@ nsEditor::DeleteSelectionImpl(nsIEditor::EDirection aAction)
return res;
}
// XXX: error handling in this routine needs to be cleaned up!
NS_IMETHODIMP
nsEditor::DeleteSelectionAndCreateNode(const nsString& aTag,
nsIDOMNode ** aNewNode)
{
nsCOMPtr<nsIDOMNode> parentSelectedNode;
PRInt32 offsetOfNewNode;
nsresult result = DeleteSelectionAndPrepareToCreateNode(parentSelectedNode,
offsetOfNewNode);
if (!NS_SUCCEEDED(result))
return result;
nsCOMPtr<nsIDOMNode> newNode;
result = CreateNode(aTag, parentSelectedNode, offsetOfNewNode,
getter_AddRefs(newNode));
// XXX: ERROR_HANDLING check result, and make sure aNewNode is set correctly in success/failure cases
*aNewNode = newNode;
NS_IF_ADDREF(*aNewNode);
// we want the selection to be just after the new node
nsCOMPtr<nsISelection> selection;
result = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(result)) return result;
if (!selection) return NS_ERROR_NULL_POINTER;
result = selection->Collapse(parentSelectedNode, offsetOfNewNode+1);
return result;
}
/* Non-interface, protected methods */
NS_IMETHODIMP
nsEditor::DeleteSelectionAndPrepareToCreateNode(nsCOMPtr<nsIDOMNode> &parentSelectedNode, PRInt32& offsetOfNewNode)
{
nsresult result=NS_ERROR_NOT_INITIALIZED;
nsCOMPtr<nsISelection> selection;
result = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(result)) return result;
if (!selection) return NS_ERROR_NULL_POINTER;
PRBool collapsed;
result = selection->GetIsCollapsed(&collapsed);
if (NS_SUCCEEDED(result) && !collapsed)
{
result = DeleteSelection(nsIEditor::eNone);
if (NS_FAILED(result)) {
return result;
}
// get the new selection
result = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(result)) {
return result;
}
#ifdef NS_DEBUG
nsCOMPtr<nsIDOMNode>testSelectedNode;
nsresult debugResult = selection->GetAnchorNode(getter_AddRefs(testSelectedNode));
// no selection is ok.
// if there is a selection, it must be collapsed
if (testSelectedNode)
{
PRBool testCollapsed;
debugResult = selection->GetIsCollapsed(&testCollapsed);
NS_ASSERTION((NS_SUCCEEDED(result)), "couldn't get a selection after deletion");
NS_ASSERTION(testCollapsed, "selection not reset after deletion");
}
#endif
}
// split the selected node
PRInt32 offsetOfSelectedNode;
result = selection->GetAnchorNode(getter_AddRefs(parentSelectedNode));
if (NS_SUCCEEDED(result) && NS_SUCCEEDED(selection->GetAnchorOffset(&offsetOfSelectedNode)) && parentSelectedNode)
{
nsCOMPtr<nsIDOMNode> selectedNode;
PRUint32 selectedNodeContentCount=0;
nsCOMPtr<nsIDOMCharacterData>selectedParentNodeAsText;
selectedParentNodeAsText = do_QueryInterface(parentSelectedNode);
offsetOfNewNode = offsetOfSelectedNode;
/* if the selection is a text node, split the text node if necesary
and compute where to put the new node
*/
if (selectedParentNodeAsText)
{
PRInt32 indexOfTextNodeInParent;
selectedNode = do_QueryInterface(parentSelectedNode);
selectedNode->GetParentNode(getter_AddRefs(parentSelectedNode));
selectedParentNodeAsText->GetLength(&selectedNodeContentCount);
GetChildOffset(selectedNode, parentSelectedNode, indexOfTextNodeInParent);
if ((offsetOfSelectedNode!=0) && (((PRUint32)offsetOfSelectedNode)!=selectedNodeContentCount))
{
nsCOMPtr<nsIDOMNode> newSiblingNode;
result = SplitNode(selectedNode, offsetOfSelectedNode, getter_AddRefs(newSiblingNode));
// now get the node's offset in it's parent, and insert the new tag there
if (NS_SUCCEEDED(result)) {
result = GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
}
}
else
{ // determine where to insert the new node
if (0==offsetOfSelectedNode) {
offsetOfNewNode = indexOfTextNodeInParent; // insert new node as previous sibling to selection parent
}
else { // insert new node as last child
GetChildOffset(selectedNode, parentSelectedNode, offsetOfNewNode);
offsetOfNewNode++; // offsets are 0-based, and we need the index of the new node
}
}
}
// Here's where the new node was inserted
}
#ifdef DEBUG
else {
printf("InsertLineBreak into an empty document is not yet supported\n");
}
#endif
return result;
}
NS_IMETHODIMP
nsEditor::DoAfterDoTransaction(nsITransaction *aTxn)
{

View File

@ -46,6 +46,7 @@
#include "nsIDTD.h"
#include "nsIDOMElement.h"
#include "nsVoidArray.h"
#include "nsSelectionState.h"
class nsIEditActionListener;
class nsIDocumentStateListener;
@ -72,92 +73,6 @@ class nsIFile;
class nsISelectionController;
/***************************************************************************
* 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 nsRangeStore
{
nsRangeStore();
~nsRangeStore();
nsresult StoreRange(nsIDOMRange *aRange);
nsresult GetRange(nsCOMPtr<nsIDOMRange> *outRange);
nsCOMPtr<nsIDOMNode> startNode;
PRInt32 startOffset;
nsCOMPtr<nsIDOMNode> endNode;
PRInt32 endOffset;
// DEBUG: static PRInt32 n;
};
class nsSelectionState
{
public:
nsSelectionState();
~nsSelectionState();
nsresult SaveSelection(nsISelection *aSel);
nsresult RestoreSelection(nsISelection *aSel);
PRBool IsCollapsed();
PRBool IsEqual(nsSelectionState *aSelState);
void MakeEmpty();
PRBool IsEmpty();
protected:
nsVoidArray mArray;
friend class nsRangeUpdater;
};
class nsRangeUpdater
{
public:
nsRangeUpdater();
~nsRangeUpdater();
void* RegisterRange(nsIDOMRange *aRange);
nsCOMPtr<nsIDOMRange> ReclaimRange(void *aCookie);
void DropRange(void *aCookie);
void RegisterRangeItem(nsRangeStore *aRangeItem);
void DropRangeItem(nsRangeStore *aRangeItem);
nsresult RegisterSelectionState(nsSelectionState &aSelState);
nsresult DropSelectionState(nsSelectionState &aSelState);
// editor selection gravity routines. Note that we can't always depend on
// DOM Range gravity to do what we want to the "real" selection. For instance,
// if you move a node, that corresponds to deleting it and reinserting it.
// DOM Range gravity will promote the selection out of the node on deletion,
// which is not what you want if you know you are reinserting it.
nsresult SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition);
nsresult SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition);
nsresult SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset);
nsresult SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode);
nsresult SelAdjJoinNodes(nsIDOMNode *aLeftNode,
nsIDOMNode *aRightNode,
nsIDOMNode *aParent,
PRInt32 aOffset,
PRInt32 aOldLeftNodeLength);
nsresult SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString);
nsresult SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength);
// the following gravity routines need will/did sandwiches, because the other gravity
// routines will be called inside of these sandwiches, but should be ignored.
nsresult WillReplaceContainer();
nsresult DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode);
nsresult WillRemoveContainer();
nsresult DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen);
nsresult WillInsertContainer();
nsresult DidInsertContainer();
nsresult WillMoveNode();
nsresult DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset);
protected:
nsVoidArray mArray;
PRBool mLock;
};
/** implementation of an editor object. it will be the controller/focal point
* for the main editor services. i.e. the GUIManager, publishing, transaction
* manager, event interfaces. the idea for the event interfaces is to have them
@ -181,18 +96,17 @@ public:
kOpNone = 0,
kOpUndo,
kOpRedo,
kOpSetTextProperty,
kOpRemoveTextProperty,
kOpInsertNode,
kOpCreateNode,
kOpDeleteNode,
kOpSplitNode,
kOpJoinNode,
kOpDeleteText,
kOpInsertText,
kOpInsertIMEText,
kOpDeleteSelection,
kOpHTMLPaste
// text commands
kOpInsertBreak = 1000,
kOpInsertText = 1001,
kOpInsertIMEText = 1002,
kOpDeleteText = 1003
};
static const char* kMOZEditorBogusNodeAttr;
@ -341,6 +255,8 @@ public:
nsIDOMCharacterData *aTextNode,
PRInt32 aOffset);
NS_IMETHOD DeleteSelectionImpl(EDirection aAction);
NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag,
nsIDOMNode ** aNewNode);
/* helper routines for node/parent manipulations */
nsresult ReplaceContainer(nsIDOMNode *inNode,
@ -454,6 +370,8 @@ protected:
nsIDOMNode *aRightNode,
JoinElementTxn **aTxn);
NS_IMETHOD DeleteSelectionAndPrepareToCreateNode(nsCOMPtr<nsIDOMNode> &parentSelectedNode,
PRInt32& offsetOfNewNode);
// called each time we modify the document. Increments the mod
// count of the doc.
@ -775,7 +693,7 @@ public:
protected:
PRUint32 mFlags; // behavior flags. See nsIHTMLEditor.h for the flags we use.
PRUint32 mFlags; // behavior flags. See nsPlaintextEditor.h for the flags we use.
nsWeakPtr mPresShellWeak; // weak reference to the nsIPresShell
nsWeakPtr mSelConWeak; // weak reference to the nsISelectionController

View File

@ -32,6 +32,7 @@
#include "nsIAtom.h"
#include "nsVoidArray.h"
#include "nsEditor.h"
#include "nsPlaintextEditor.h"
#include "nsIContentIterator.h"
/***************************************************************************
@ -48,6 +49,22 @@ class nsAutoPlaceHolderBatch
~nsAutoPlaceHolderBatch() { if (mEd) mEd->EndPlaceHolderTransaction(); }
};
/***************************************************************************
* stack based helper class for detecting end of editor initialization, in
* order to triger "end of init" initialization of the edit rules.
*/
class nsAutoEditInitRulesTrigger
{
private:
nsPlaintextEditor *mEd;
nsresult &mRes;
public:
nsAutoEditInitRulesTrigger( nsPlaintextEditor *aEd, nsresult &aRes) : mEd(aEd), mRes(aRes)
{ if (mEd) mEd->BeginEditorInit(); }
~nsAutoEditInitRulesTrigger() { if (mEd) mRes = mEd->EndEditorInit(); }
};
/***************************************************************************
* stack based helper class for batching a collection of txns.

View File

@ -0,0 +1,640 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
#include "nsSelectionState.h"
#include "nsIDOMCharacterData.h"
#include "nsIDOMNode.h"
#include "nsIDOMRange.h"
#include "nsISelection.h"
#include "nsEditor.h"
#include "nsLayoutCID.h"
static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID);
/***************************************************************************
* 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() : mArray(){}
nsSelectionState::~nsSelectionState()
{
MakeEmpty();
}
nsresult
nsSelectionState::SaveSelection(nsISelection *aSel)
{
if (!aSel) return NS_ERROR_NULL_POINTER;
nsresult res = NS_OK;
PRInt32 i,rangeCount, arrayCount = mArray.Count();
nsRangeStore *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 nsRangeStore;
mArray.AppendElement(item);
}
}
// else if we have too many, delete them
else if (rangeCount>arrayCount)
{
while ((item = (nsRangeStore*)mArray.ElementAt(rangeCount)))
{
delete item;
mArray.RemoveElementAt(rangeCount);
}
}
// now store the selection ranges
for (i=0; i<rangeCount; i++)
{
item = (nsRangeStore*)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(nsISelection *aSel)
{
if (!aSel) return NS_ERROR_NULL_POINTER;
nsresult res = NS_OK;
PRInt32 i, arrayCount = mArray.Count();
nsRangeStore *item;
// clear out selection
aSel->RemoveAllRanges();
// set the selection ranges anew
for (i=0; i<arrayCount; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsIDOMRange> range;
item->GetRange(address_of(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;
nsRangeStore *item;
item = (nsRangeStore*)mArray.ElementAt(0);
if (!item) return PR_FALSE;
nsCOMPtr<nsIDOMRange> range;
item->GetRange(address_of(range));
if (!range) return PR_FALSE;
PRBool bIsCollapsed;
range->GetCollapsed(&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;
nsRangeStore *myItem, *itsItem;
for (i=0; i<myCount; i++)
{
myItem = (nsRangeStore*)mArray.ElementAt(0);
itsItem = (nsRangeStore*)(aSelState->mArray.ElementAt(0));
if (!myItem || !itsItem) return PR_FALSE;
nsCOMPtr<nsIDOMRange> myRange, itsRange;
myItem->GetRange(address_of(myRange));
itsItem->GetRange(address_of(itsRange));
if (!myRange || !itsRange) return PR_FALSE;
PRInt32 compResult;
myRange->CompareBoundaryPoints(nsIDOMRange::START_TO_START, itsRange, &compResult);
if (compResult) return PR_FALSE;
myRange->CompareBoundaryPoints(nsIDOMRange::END_TO_END, itsRange, &compResult);
if (compResult) return PR_FALSE;
}
// if we got here, they are equal
return PR_TRUE;
}
void
nsSelectionState::MakeEmpty()
{
// free any items in the array
nsRangeStore *item;
while ((item = (nsRangeStore*)mArray.ElementAt(0)))
{
delete item;
mArray.RemoveElementAt(0);
}
}
PRBool
nsSelectionState::IsEmpty()
{
return (mArray.Count() == 0);
}
/***************************************************************************
* nsRangeUpdater: class for updating nsIDOMRanges in response to editor actions.
*/
nsRangeUpdater::nsRangeUpdater() : mArray(), mLock(PR_FALSE) {}
nsRangeUpdater::~nsRangeUpdater()
{
// free any items in the array
nsRangeStore *item;
while ((item = (nsRangeStore*)mArray.ElementAt(0)))
{
delete item;
mArray.RemoveElementAt(0);
}
}
void*
nsRangeUpdater::RegisterRange(nsIDOMRange *aRange)
{
nsRangeStore *item = new nsRangeStore;
if (!item) return nsnull;
item->StoreRange(aRange);
mArray.AppendElement(item);
return item;
}
nsCOMPtr<nsIDOMRange>
nsRangeUpdater::ReclaimRange(void *aCookie)
{
nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie);
if (!item) return nsnull;
nsCOMPtr<nsIDOMRange> outRange;
item->GetRange(address_of(outRange));
mArray.RemoveElement(aCookie);
delete item;
return outRange;
}
void
nsRangeUpdater::DropRange(void *aCookie)
{
nsRangeStore *item = NS_STATIC_CAST(nsRangeStore*,aCookie);
if (!item) return;
mArray.RemoveElement(aCookie);
delete item;
}
void
nsRangeUpdater::RegisterRangeItem(nsRangeStore *aRangeItem)
{
if (!aRangeItem) return;
mArray.AppendElement(aRangeItem);
return;
}
void
nsRangeUpdater::DropRangeItem(nsRangeStore *aRangeItem)
{
if (!aRangeItem) return;
mArray.RemoveElement(aRangeItem);
return;
}
nsresult
nsRangeUpdater::RegisterSelectionState(nsSelectionState &aSelState)
{
PRInt32 i, theCount = aSelState.mArray.Count();
if (theCount < 1) return NS_ERROR_FAILURE;
nsRangeStore *item;
for (i=0; i<theCount; i++)
{
item = (nsRangeStore*)aSelState.mArray.ElementAt(i);
RegisterRangeItem(item);
}
return NS_OK;;
}
nsresult
nsRangeUpdater::DropSelectionState(nsSelectionState &aSelState)
{
PRInt32 i, theCount = aSelState.mArray.Count();
if (theCount < 1) return NS_ERROR_FAILURE;
nsRangeStore *item;
for (i=0; i<theCount; i++)
{
item = (nsRangeStore*)aSelState.mArray.ElementAt(i);
DropRangeItem(item);
}
return NS_OK;;
}
// gravity methods:
nsresult
nsRangeUpdater::SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aParent) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if ((item->startNode.get() == aParent) && (item->startOffset > aPosition))
item->startOffset++;
if ((item->endNode.get() == aParent) && (item->endOffset > aPosition))
item->endOffset++;
}
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition)
{
return SelAdjCreateNode(aParent, aPosition);
}
nsresult
nsRangeUpdater::SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aNode) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if ((item->startNode.get() == aParent) && (item->startOffset > aOffset))
item->startOffset--;
if ((item->endNode.get() == aParent) && (item->endOffset > aOffset))
item->endOffset--;
}
// MOOSE: also check inside of aNode, expensive. But in theory, we shouldn't
// actually hit this case in the usage i forsee for this.
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aOldRightNode || !aNewLeftNode) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsCOMPtr<nsIDOMNode> parent;
PRInt32 offset;
nsresult result = nsEditor::GetNodeLocation(aOldRightNode, address_of(parent), &offset);
if (NS_FAILED(result)) return result;
// first part is same as inserting aNewLeftnode
result = SelAdjInsertNode(parent,offset-1);
if (NS_FAILED(result)) return result;
// next step is to check for range enpoints inside aOldRightNode
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if (item->startNode.get() == aOldRightNode)
{
if (item->startOffset > aOffset)
{
item->startOffset -= aOffset;
}
else
{
item->startNode = aNewLeftNode;
}
}
if (item->endNode.get() == aOldRightNode)
{
if (item->endOffset > aOffset)
{
item->endOffset -= aOffset;
}
else
{
item->endNode = aNewLeftNode;
}
}
}
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjJoinNodes(nsIDOMNode *aLeftNode,
nsIDOMNode *aRightNode,
nsIDOMNode *aParent,
PRInt32 aOffset,
PRInt32 aOldLeftNodeLength)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
if (!aLeftNode || !aRightNode || !aParent) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
// adjust endpoints in aParent
if (item->startNode.get() == aParent)
{
if (item->startOffset > aOffset)
{
item->startOffset--;
}
else if (item->startOffset == aOffset)
{
// join keeps right hand node
item->startNode = aRightNode;
item->startOffset = aOldLeftNodeLength;
}
}
if (item->endNode.get() == aParent)
{
if (item->endOffset > aOffset)
{
item->endOffset--;
}
else if (item->endOffset == aOffset)
{
// join keeps right hand node
item->endNode = aRightNode;
item->endOffset = aOldLeftNodeLength;
}
}
// adjust endpoints in aRightNode
if (item->startNode.get() == aRightNode)
item->startOffset += aOldLeftNodeLength;
if (item->endNode.get() == aRightNode)
item->endOffset += aOldLeftNodeLength;
}
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
return NS_OK;
}
nsresult
nsRangeUpdater::SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength)
{
if (mLock) return NS_OK; // lock set by Will/DidReplaceParent, etc...
return NS_OK;
}
nsresult
nsRangeUpdater::WillReplaceContainer()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
return NS_OK;
}
nsresult
nsRangeUpdater::DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode)
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
if (!aOriginalNode || !aNewNode) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if (item->startNode.get() == aOriginalNode)
item->startNode = aNewNode;
if (item->endNode.get() == aOriginalNode)
item->endNode = aNewNode;
}
return NS_OK;
}
nsresult
nsRangeUpdater::WillRemoveContainer()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
return NS_OK;
}
nsresult
nsRangeUpdater::DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen)
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
if (!aNode || !aParent) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
if (item->startNode.get() == aNode)
{
item->startNode = aParent;
item->startOffset += aOffset;
}
if (item->endNode.get() == aNode)
{
item->endNode = aParent;
item->endOffset += aOffset;
}
if ((item->startNode.get() == aParent) && (item->startOffset > aOffset))
item->startOffset += (PRInt32)aNodeOrigLen-1;
if ((item->endNode.get() == aParent) && (item->endOffset > aOffset))
item->endOffset += (PRInt32)aNodeOrigLen-1;
}
return NS_OK;
}
nsresult
nsRangeUpdater::WillInsertContainer()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
return NS_OK;
}
nsresult
nsRangeUpdater::DidInsertContainer()
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
return NS_OK;
}
nsresult
nsRangeUpdater::WillMoveNode()
{
if (mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_TRUE;
return NS_OK;
}
nsresult
nsRangeUpdater::DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset)
{
if (!mLock) return NS_ERROR_UNEXPECTED;
mLock = PR_FALSE;
if (!aOldParent || !aNewParent) return NS_ERROR_NULL_POINTER;
PRInt32 i, count = mArray.Count();
if (!count) return NS_OK;
nsRangeStore *item;
for (i=0; i<count; i++)
{
item = (nsRangeStore*)mArray.ElementAt(i);
if (!item) return NS_ERROR_NULL_POINTER;
// like a delete in aOldParent
if ((item->startNode.get() == aOldParent) && (item->startOffset > aOldOffset))
item->startOffset--;
if ((item->endNode.get() == aOldParent) && (item->endOffset > aOldOffset))
item->endOffset--;
// and like an insert in aNewParent
if ((item->startNode.get() == aNewParent) && (item->startOffset > aNewOffset))
item->startOffset++;
if ((item->endNode.get() == aNewParent) && (item->endOffset > aNewOffset))
item->endOffset++;
}
return NS_OK;
}
/***************************************************************************
* helper class for nsSelectionState. nsRangeStore stores range endpoints.
*/
// DEBUG: PRInt32 nsRangeStore::n = 0;
nsRangeStore::nsRangeStore()
{
// DEBUG: n++; printf("range store alloc count=%d\n", n);
}
nsRangeStore::~nsRangeStore()
{
// DEBUG: n--; printf("range store alloc count=%d\n", n);
}
nsresult nsRangeStore::StoreRange(nsIDOMRange *aRange)
{
if (!aRange) return NS_ERROR_NULL_POINTER;
aRange->GetStartContainer(getter_AddRefs(startNode));
aRange->GetEndContainer(getter_AddRefs(endNode));
aRange->GetStartOffset(&startOffset);
aRange->GetEndOffset(&endOffset);
return NS_OK;
}
nsresult nsRangeStore::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;
}

View File

@ -0,0 +1,245 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#ifndef __selectionstate_h__
#define __selectionstate_h__
#include "nsCOMPtr.h"
#include "nsVoidArray.h"
#include "nsIDOMNode.h"
class nsIDOMCharacterData;
class nsIDOMRange;
class nsISelection;
/***************************************************************************
* 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 nsRangeStore
{
nsRangeStore();
~nsRangeStore();
nsresult StoreRange(nsIDOMRange *aRange);
nsresult GetRange(nsCOMPtr<nsIDOMRange> *outRange);
nsCOMPtr<nsIDOMNode> startNode;
PRInt32 startOffset;
nsCOMPtr<nsIDOMNode> endNode;
PRInt32 endOffset;
// DEBUG: static PRInt32 n;
};
class nsSelectionState
{
public:
nsSelectionState();
~nsSelectionState();
nsresult SaveSelection(nsISelection *aSel);
nsresult RestoreSelection(nsISelection *aSel);
PRBool IsCollapsed();
PRBool IsEqual(nsSelectionState *aSelState);
void MakeEmpty();
PRBool IsEmpty();
protected:
nsVoidArray mArray;
friend class nsRangeUpdater;
};
class nsRangeUpdater
{
public:
nsRangeUpdater();
~nsRangeUpdater();
void* RegisterRange(nsIDOMRange *aRange);
nsCOMPtr<nsIDOMRange> ReclaimRange(void *aCookie);
void DropRange(void *aCookie);
void RegisterRangeItem(nsRangeStore *aRangeItem);
void DropRangeItem(nsRangeStore *aRangeItem);
nsresult RegisterSelectionState(nsSelectionState &aSelState);
nsresult DropSelectionState(nsSelectionState &aSelState);
// editor selection gravity routines. Note that we can't always depend on
// DOM Range gravity to do what we want to the "real" selection. For instance,
// if you move a node, that corresponds to deleting it and reinserting it.
// DOM Range gravity will promote the selection out of the node on deletion,
// which is not what you want if you know you are reinserting it.
nsresult SelAdjCreateNode(nsIDOMNode *aParent, PRInt32 aPosition);
nsresult SelAdjInsertNode(nsIDOMNode *aParent, PRInt32 aPosition);
nsresult SelAdjDeleteNode(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset);
nsresult SelAdjSplitNode(nsIDOMNode *aOldRightNode, PRInt32 aOffset, nsIDOMNode *aNewLeftNode);
nsresult SelAdjJoinNodes(nsIDOMNode *aLeftNode,
nsIDOMNode *aRightNode,
nsIDOMNode *aParent,
PRInt32 aOffset,
PRInt32 aOldLeftNodeLength);
nsresult SelAdjInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsString &aString);
nsresult SelAdjDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength);
// the following gravity routines need will/did sandwiches, because the other gravity
// routines will be called inside of these sandwiches, but should be ignored.
nsresult WillReplaceContainer();
nsresult DidReplaceContainer(nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode);
nsresult WillRemoveContainer();
nsresult DidRemoveContainer(nsIDOMNode *aNode, nsIDOMNode *aParent, PRInt32 aOffset, PRUint32 aNodeOrigLen);
nsresult WillInsertContainer();
nsresult DidInsertContainer();
nsresult WillMoveNode();
nsresult DidMoveNode(nsIDOMNode *aOldParent, PRInt32 aOldOffset, nsIDOMNode *aNewParent, PRInt32 aNewOffset);
protected:
nsVoidArray mArray;
PRBool mLock;
};
/***************************************************************************
* another helper class for nsSelectionState. stack based class for doing
* Will/DidReplaceContainer()
*/
class nsAutoReplaceContainerSelNotify
{
private:
nsRangeUpdater &mRU;
nsIDOMNode *mOriginalNode;
nsIDOMNode *mNewNode;
public:
nsAutoReplaceContainerSelNotify(nsRangeUpdater &aRangeUpdater, nsIDOMNode *aOriginalNode, nsIDOMNode *aNewNode) :
mRU(aRangeUpdater)
,mOriginalNode(aOriginalNode)
,mNewNode(aNewNode)
{
mRU.WillReplaceContainer();
}
~nsAutoReplaceContainerSelNotify()
{
mRU.DidReplaceContainer(mOriginalNode, mNewNode);
}
};
/***************************************************************************
* another helper class for nsSelectionState. stack based class for doing
* Will/DidRemoveContainer()
*/
class nsAutoRemoveContainerSelNotify
{
private:
nsRangeUpdater &mRU;
nsIDOMNode *mNode;
nsIDOMNode *mParent;
PRInt32 mOffset;
PRUint32 mNodeOrigLen;
public:
nsAutoRemoveContainerSelNotify(nsRangeUpdater &aRangeUpdater,
nsIDOMNode *aNode,
nsIDOMNode *aParent,
PRInt32 aOffset,
PRUint32 aNodeOrigLen) :
mRU(aRangeUpdater)
,mNode(aNode)
,mParent(aParent)
,mOffset(aOffset)
,mNodeOrigLen(aNodeOrigLen)
{
mRU.WillRemoveContainer();
}
~nsAutoRemoveContainerSelNotify()
{
mRU.DidRemoveContainer(mNode, mParent, mOffset, mNodeOrigLen);
}
};
/***************************************************************************
* another helper class for nsSelectionState. stack based class for doing
* Will/DidInsertContainer()
*/
class nsAutoInsertContainerSelNotify
{
private:
nsRangeUpdater &mRU;
public:
nsAutoInsertContainerSelNotify(nsRangeUpdater &aRangeUpdater) :
mRU(aRangeUpdater)
{
mRU.WillInsertContainer();
}
~nsAutoInsertContainerSelNotify()
{
mRU.DidInsertContainer();
}
};
/***************************************************************************
* another helper class for nsSelectionState. stack based class for doing
* Will/DidMoveNode()
*/
class nsAutoMoveNodeSelNotify
{
private:
nsRangeUpdater &mRU;
nsIDOMNode *mOldParent;
nsIDOMNode *mNewParent;
PRInt32 mOldOffset;
PRInt32 mNewOffset;
public:
nsAutoMoveNodeSelNotify(nsRangeUpdater &aRangeUpdater,
nsIDOMNode *aOldParent,
PRInt32 aOldOffset,
nsIDOMNode *aNewParent,
PRInt32 aNewOffset) :
mRU(aRangeUpdater)
,mOldParent(aOldParent)
,mNewParent(aNewParent)
,mOldOffset(aOldOffset)
,mNewOffset(aNewOffset)
{
mRU.WillMoveNode();
}
~nsAutoMoveNodeSelNotify()
{
mRU.DidMoveNode(mOldParent, mOldOffset, mNewParent, mNewOffset);
}
};
#endif

View File

@ -27,6 +27,7 @@
#include "nsEditor.h" // for gInstanceCount
#include "nsEditorController.h" //CID
#include "nsEditorService.h"
#include "nsPlaintextEditor.h"
////////////////////////////////////////////////////////////////////////
// Define the contructor function for the objects
@ -38,6 +39,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorShell)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorController)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsComposerController)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorService)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsPlaintextEditor)
#ifdef ENABLE_EDITOR_API_LOG
#include "nsHTMLEditorLog.h"
@ -52,6 +54,8 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLEditor)
// class name.
//
static nsModuleComponentInfo components[] = {
{ "Text Editor", NS_TEXTEDITOR_CID,
"@mozilla.org/editor/texteditor;1", nsPlaintextEditorConstructor, },
#ifdef ENABLE_EDITOR_API_LOG
{ "HTML Editor", NS_HTMLEDITOR_CID,
"@mozilla.org/editor/htmleditor;1", nsHTMLEditorLogConstructor, },

View File

@ -0,0 +1,58 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsIGenericFactory.h"
#include "nsEditorCID.h"
#include "nsEditor.h" // for gInstanceCount
#include "nsPlaintextEditor.h"
#include "nsEditorService.h"
#include "nsEditorController.h" //CID
////////////////////////////////////////////////////////////////////////
// Define the contructor function for the objects
//
// NOTE: This creates an instance of objects by using the default constructor
//
NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorController)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsEditorService)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsPlaintextEditor)
////////////////////////////////////////////////////////////////////////
// Define a table of CIDs implemented by this module along with other
// information like the function to create an instance, contractid, and
// class name.
//
static nsModuleComponentInfo components[] = {
{ "Text Editor", NS_TEXTEDITOR_CID,
"@mozilla.org/editor/texteditor;1", nsPlaintextEditorConstructor, },
{ "Editor Controller", NS_EDITORCONTROLLER_CID,
"@mozilla.org/editor/editorcontroller;1", nsEditorControllerConstructor, }
};
////////////////////////////////////////////////////////////////////////
// Implement the NSGetModule() exported function for your module
// and the entire implementation of the module object.
//
NS_IMPL_NSGETMODULE("nsEditorModule", components)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -34,6 +34,7 @@ class nsISupportsArray;
class nsVoidArray;
class nsIDOMElement;
class nsIEditor;
class nsHTMLEditor;
class nsHTMLEditRules : public nsIHTMLEditRules, public nsTextEditRules, public nsIEditActionListener
{
@ -46,7 +47,7 @@ public:
// nsIEditRules methods
NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags);
NS_IMETHOD Init(nsPlaintextEditor *aEditor, PRUint32 aFlags);
NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection);
NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection);
NS_IMETHOD WillDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled);
@ -98,10 +99,10 @@ protected:
nsString *outString,
PRInt32 aMaxLength);
nsresult WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBool *aHandled);
nsresult DidInsertBreak(nsISelection *aSelection, nsresult aResult);
nsresult WillDeleteSelection(nsISelection *aSelection, nsIEditor::EDirection aAction,
PRBool *aCancel, PRBool *aHandled);
nsresult DeleteNonTableElements(nsIDOMNode *aNode);
nsresult WillMakeList(nsISelection *aSelection, const nsString *aListType, PRBool aEntireList, PRBool *aCancel, PRBool *aHandled, const nsString *aItemType=nsnull);
nsresult WillRemoveList(nsISelection *aSelection, PRBool aOrderd, PRBool *aCancel, PRBool *aHandled);
nsresult WillIndent(nsISelection *aSelection, PRBool *aCancel, PRBool *aHandled);
@ -110,17 +111,13 @@ protected:
nsresult WillMakeDefListItem(nsISelection *aSelection, const nsString *aBlockType, PRBool aEntireList, PRBool *aCancel, PRBool *aHandled);
nsresult WillMakeBasicBlock(nsISelection *aSelection, const nsString *aBlockType, PRBool *aCancel, PRBool *aHandled);
nsresult DidMakeBasicBlock(nsISelection *aSelection, nsRulesInfo *aInfo, nsresult aResult);
nsresult AlignInnerBlocks(nsIDOMNode *aNode, const nsString *alignType);
nsresult AlignBlockContents(nsIDOMNode *aNode, const nsString *alignType);
nsresult GetInnerContent(nsIDOMNode *aNode, nsISupportsArray *outArrayOfNodes, PRBool aList = PR_TRUE, PRBool aTble = PR_TRUE);
nsresult InsertTab(nsISelection *aSelection, nsString *outString);
nsresult ReturnInHeader(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset);
nsresult ReturnInParagraph(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset, PRBool *aCancel, PRBool *aHandled);
nsresult ReturnInListItem(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset);
nsresult AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection);
nsresult ConvertListType(nsIDOMNode *aList, nsCOMPtr<nsIDOMNode> *outList, const nsString& aListType, const nsString& aItemType);
nsresult CreateStyleForInsertText(nsISelection *aSelection, nsIDOMDocument *aDoc);
@ -132,7 +129,6 @@ protected:
PRBool IsLastNode(nsIDOMNode *aNode);
PRBool AtStartOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock);
PRBool AtEndOfBlock(nsIDOMNode *aNode, PRInt32 aOffset, nsIDOMNode *aBlock);
nsresult GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt32 aOffset,
PRInt32 actionID, nsCOMPtr<nsIDOMNode> *outNode, PRInt32 *outOffset);
nsresult GetPromotedRanges(nsISelection *inSelection,
@ -154,20 +150,16 @@ protected:
nsCOMPtr<nsIDOMNode> GetHighestInlineParent(nsIDOMNode* aNode);
nsresult MakeTransitionList(nsISupportsArray *inArrayOfNodes,
nsVoidArray *inTransitionArray);
nsresult ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString *aBlockTag);
nsresult MakeBlockquote(nsISupportsArray *arrayOfNodes);
nsresult SplitAsNeeded(const nsString *aTag, nsCOMPtr<nsIDOMNode> *inOutParent, PRInt32 *inOutOffset);
nsresult AddTerminatingBR(nsIDOMNode *aBlock);
nsresult JoinNodesSmart( nsIDOMNode *aNodeLeft,
nsIDOMNode *aNodeRight,
nsCOMPtr<nsIDOMNode> *aOutMergeParent,
PRInt32 *aOutMergeOffset);
nsresult GetTopEnclosingMailCite(nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutCiteNode);
nsresult GetTopEnclosingMailCite(nsIDOMNode *aNode, nsCOMPtr<nsIDOMNode> *aOutCiteNode, PRBool aPlaintext);
nsresult PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList);
nsresult AdjustSpecialBreaks(PRBool aSafeToAskFrames = PR_FALSE);
nsresult AdjustWhitespace(nsISelection *aSelection);
nsresult AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection aAction);
@ -180,14 +172,13 @@ protected:
nsresult SelectionEndpointInNode(nsIDOMNode *aNode, PRBool *aResult);
nsresult DoTextNodeWhitespace(nsIDOMCharacterData *aTextNode, PRInt32 aStart, PRInt32 aEnd);
nsresult UpdateDocChangeRange(nsIDOMRange *aRange);
nsresult ConvertWhitespace(const nsString & inString, nsString & outString);
nsresult ConfirmSelectionInBody();
nsresult InsertMozBRIfNeeded(nsIDOMNode *aNode);
// data members
protected:
nsHTMLEditor *mHTMLEditor;
nsCOMPtr<nsIDOMRange> mDocChangeRange;
PRBool mListenerEnabled;
PRBool mReturnInEmptyLIKillsList;

View File

@ -26,6 +26,7 @@
#include "nsEditor.h"
#include "nsIDOMNode.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMHTMLAnchorElement.h"
/********************************************************
* helper methods from nsTextEditRules
@ -487,6 +488,34 @@ nsHTMLEditUtils::IsImage(nsIDOMNode *node)
return PR_FALSE;
}
PRBool
nsHTMLEditUtils::IsLink(nsIDOMNode *aNode)
{
if (!aNode) return PR_FALSE;
nsCOMPtr<nsIDOMHTMLAnchorElement> anchor = do_QueryInterface(aNode);
if (anchor)
{
nsAutoString tmpText;
if (NS_SUCCEEDED(anchor->GetHref(tmpText)) && tmpText.GetUnicode() && tmpText.Length() != 0)
return PR_TRUE;
}
return PR_FALSE;
}
PRBool
nsHTMLEditUtils::IsNamedAnchor(nsIDOMNode *aNode)
{
if (!aNode) return PR_FALSE;
nsCOMPtr<nsIDOMHTMLAnchorElement> anchor = do_QueryInterface(aNode);
if (anchor)
{
nsAutoString tmpText;
if (NS_SUCCEEDED(anchor->GetName(tmpText)) && tmpText.GetUnicode() && tmpText.Length() != 0)
return PR_TRUE;
}
return PR_FALSE;
}
///////////////////////////////////////////////////////////////////////////
// IsDiv: true if node an html div node

View File

@ -58,6 +58,8 @@ public:
static PRBool IsAddress(nsIDOMNode *aNode);
static PRBool IsAnchor(nsIDOMNode *aNode);
static PRBool IsImage(nsIDOMNode *aNode);
static PRBool IsLink(nsIDOMNode *aNode);
static PRBool IsNamedAnchor(nsIDOMNode *aNode);
static PRBool IsDiv(nsIDOMNode *aNode);
static PRBool IsNormalDiv(nsIDOMNode *aNode);
static PRBool IsMozDiv(nsIDOMNode *aNode);

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,8 @@
#include "nsCOMPtr.h"
#include "nsIPlaintextEditor.h"
#include "nsPlaintextEditor.h"
#include "nsIEditor.h"
#include "nsIHTMLEditor.h"
#include "nsITableEditor.h"
#include "nsIEditorMailSupport.h"
@ -51,8 +52,7 @@ class nsIDocumentEncoder;
* The HTML editor implementation.<br>
* Use to edit HTML document represented as a DOM tree.
*/
class nsHTMLEditor : public nsEditor,
public nsIPlaintextEditor,
class nsHTMLEditor : public nsPlaintextEditor,
public nsIHTMLEditor,
public nsIEditorMailSupport,
public nsITableEditor,
@ -74,7 +74,10 @@ public:
kOpRemoveList = 3006,
kOpMakeDefListItem = 3007,
kOpInsertElement = 3008,
kOpInsertQuotation = 3009
kOpInsertQuotation = 3009,
kOpSetTextProperty = 3010,
kOpRemoveTextProperty = 3011,
kOpHTMLPaste = 3012
};
@ -89,8 +92,9 @@ public:
nsHTMLEditor();
virtual ~nsHTMLEditor();
/* ------------ nsIPlaintextEditor methods -------------- */
NS_DECL_NSIPLAINTEXTEDITOR
/* ------------ nsPlaintextEditor overrides -------------- */
NS_IMETHODIMP HandleKeyPress(nsIDOMKeyEvent* aKeyEvent);
NS_IMETHODIMP CollapseSelectionToStart();
/* ------------ nsIHTMLEditor methods -------------- */
NS_IMETHOD SetInlineProperty(nsIAtom *aProperty,
@ -118,7 +122,6 @@ public:
NS_IMETHOD RebuildDocumentFromSource(const nsString& aSourceString);
NS_IMETHOD InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSelection);
NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag, nsIDOMNode ** aNewNode);
NS_IMETHOD SelectElement(nsIDOMElement* aElement);
NS_IMETHOD SetCaretAfterElement(nsIDOMElement* aElement);
@ -240,12 +243,6 @@ public:
/** prepare the editor for use */
NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags);
NS_IMETHOD GetDocumentIsEmpty(PRBool *aDocumentIsEmpty);
NS_IMETHOD DeleteSelection(EDirection aAction);
NS_IMETHOD SetDocumentCharacterSet(const PRUnichar* characterSet);
/** we override this here to install event listeners */
NS_IMETHOD PostCreate();
@ -266,15 +263,6 @@ public:
NS_IMETHOD DoDrag(nsIDOMEvent *aDragEvent);
NS_IMETHOD InsertFromDrop(nsIDOMEvent* aDropEvent);
NS_IMETHOD OutputToString(nsAWritableString& aOutputString,
const nsAReadableString& aFormatType,
PRUint32 aFlags);
NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream,
const nsAReadableString& aFormatType,
const nsAReadableString* aCharsetOverride,
PRUint32 aFlags);
NS_IMETHOD GetHeadContentsAsHTML(nsString& aOutputString);
NS_IMETHOD ReplaceHeadContentsWithHTML(const nsString &aSourceToInsert);
@ -307,7 +295,6 @@ public:
PRInt32 aOffset,
PRBool aNoEmptyNodes);
NS_IMETHOD GetBodyStyleContext(nsIStyleContext** aStyleContext);
/** returns the absolute position of the end points of aSelection
* in the document as a text stream.
@ -356,8 +343,6 @@ protected:
*/
NS_IMETHOD GetLayoutObject(nsIDOMNode *aInNode, nsISupports **aOutLayoutObject);
NS_IMETHOD DeleteSelectionAndPrepareToCreateNode(nsCOMPtr<nsIDOMNode> &parentSelectedNode, PRInt32& offsetOfNewNode);
/* StyleSheet load callback */
static void ApplyStyleSheetToPresShellDocument(nsICSSStyleSheet* aSheet, void *aData);
@ -376,11 +361,11 @@ protected:
// key event helpers
NS_IMETHOD TabInTable(PRBool inIsShift, PRBool *outHandled);
NS_IMETHOD CreateBR(nsIDOMNode *aNode, PRInt32 aOffset,
nsCOMPtr<nsIDOMNode> *outBRNode, EDirection aSelect = eNone);
nsCOMPtr<nsIDOMNode> *outBRNode, nsIEditor::EDirection aSelect = nsIEditor::eNone);
NS_IMETHOD CreateBRImpl(nsCOMPtr<nsIDOMNode> *aInOutParent,
PRInt32 *aInOutOffset,
nsCOMPtr<nsIDOMNode> *outBRNode,
EDirection aSelect);
nsIEditor::EDirection aSelect);
NS_IMETHOD InsertBR(nsCOMPtr<nsIDOMNode> *outBRNode);
// Table Editing (implemented in nsTableEditor.cpp)
@ -491,12 +476,6 @@ protected:
PRInt32 aEndOffset,
nsISelection *aSelection);
// Helpers for output routines
NS_IMETHOD GetAndInitDocEncoder(const nsAReadableString& aFormatType,
PRUint32 aFlags,
const nsAReadableString* aCharset,
nsIDocumentEncoder** encoder);
// Methods for handling plaintext quotations
NS_IMETHOD PasteAsPlaintextQuotation(PRInt32 aSelectionType);
NS_IMETHOD InsertAsPlaintextQuotation(const nsString& aQuotedText,
@ -610,38 +589,24 @@ protected:
// Data members
protected:
TypeInState* mTypeInState;
nsCOMPtr<nsIEditRules> mRules;
nsCOMPtr<nsIDOMEventListener> mKeyListenerP;
nsCOMPtr<nsIDOMEventListener> mMouseListenerP;
nsCOMPtr<nsIDOMEventListener> mTextListenerP;
nsCOMPtr<nsIDOMEventListener> mCompositionListenerP;
nsCOMPtr<nsIDOMEventListener> mDragListenerP;
nsCOMPtr<nsIDOMEventListener> mFocusListenerP;
PRBool mIsComposing;
// Used by nsIPlaintextEditor but not html editors -- factor me!
PRInt32 mMaxTextLength;
TypeInState* mTypeInState;
nsCOMPtr<nsIAtom> mBoldAtom;
nsCOMPtr<nsIAtom> mItalicAtom;
nsCOMPtr<nsIAtom> mUnderlineAtom;
nsCOMPtr<nsIAtom> mFontAtom;
nsCOMPtr<nsIAtom> mLinkAtom;
nsCOMPtr<nsIAtom> mBoldAtom;
nsCOMPtr<nsIAtom> mItalicAtom;
nsCOMPtr<nsIAtom> mUnderlineAtom;
nsCOMPtr<nsIAtom> mFontAtom;
nsCOMPtr<nsIAtom> mLinkAtom;
nsCOMPtr<nsIDOMNode> mCachedNode;
PRBool mCachedBoldStyle;
PRBool mCachedItalicStyle;
PRBool mCachedUnderlineStyle;
PRBool mCachedBoldStyle;
PRBool mCachedItalicStyle;
PRBool mCachedUnderlineStyle;
nsString mCachedFontName;
// Used by GetFirstSelectedCell and GetNextSelectedCell
PRInt32 mSelectedCellIndex;
public:
static nsIAtom *gTypingTxnName;
static nsIAtom *gIMETxnName;
static nsIAtom *gDeleteTxnName;
// friends
friend class nsHTMLEditRules;

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,8 @@
0xa6cf9121, 0x15b3, 0x11d2, \
{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32} }
#include "nsIHTMLEditor.h"
class nsIHTMLEditRules : public nsISupports
{
public:

View File

@ -21,7 +21,7 @@
* Pierre Phaneuf <pp@ludusdesign.com>
*/
#include "nsEditorEventListeners.h"
#include "nsIPlaintextEditor.h"
#include "nsIHTMLEditor.h"
#include "nsEditor.h"
#include "nsVoidArray.h"
#include "nsString.h"
@ -178,8 +178,8 @@ nsTextEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent)
// if we are readonly or disabled, then do nothing.
if (NS_SUCCEEDED(mEditor->GetFlags(&flags)))
{
if (flags & nsIHTMLEditor::eEditorReadonlyMask ||
flags & nsIHTMLEditor::eEditorDisabledMask)
if (flags & nsIPlaintextEditor::eEditorReadonlyMask ||
flags & nsIPlaintextEditor::eEditorDisabledMask)
return NS_OK;
}
else
@ -246,7 +246,7 @@ nsTextEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent)
break;
case nsIDOMKeyEvent::DOM_VK_TAB:
if ((flags & nsIHTMLEditor::eEditorSingleLineMask))
if ((flags & nsIPlaintextEditor::eEditorSingleLineMask))
return NS_OK; // let it be used for focus switching
// else we insert the tab straight through
@ -257,9 +257,8 @@ nsTextEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent)
case nsIDOMKeyEvent::DOM_VK_RETURN:
case nsIDOMKeyEvent::DOM_VK_ENTER:
if (!(flags & nsIHTMLEditor::eEditorSingleLineMask))
if (!(flags & nsIPlaintextEditor::eEditorSingleLineMask))
{
//htmlEditor->InsertBreak();
textEditor->HandleKeyPress(keyEvent);
ScrollSelectionIntoView(mEditor);
aKeyEvent->PreventDefault(); // consumed
@ -642,8 +641,8 @@ nsTextEditorDragListener::DragOver(nsIDOMEvent* aDragEvent)
if ( dragSession ) {
PRUint32 flags;
if (NS_SUCCEEDED(mEditor->GetFlags(&flags))) {
if ((flags & nsIHTMLEditor::eEditorDisabledMask) ||
(flags & nsIHTMLEditor::eEditorReadonlyMask)) {
if ((flags & nsIPlaintextEditor::eEditorDisabledMask) ||
(flags & nsIPlaintextEditor::eEditorReadonlyMask)) {
dragSession->SetCanDrop(PR_FALSE);
return NS_OK;
}
@ -1041,7 +1040,7 @@ nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent)
PRUint32 flags;
aEvent->PreventBubble();
mEditor->GetFlags(&flags);
if (! (flags & nsIHTMLEditor::eEditorDisabledMask))
if (! (flags & nsIPlaintextEditor::eEditorDisabledMask))
{ // only enable caret and selection if the editor is not disabled
nsCOMPtr<nsIEditor>editor = do_QueryInterface(mEditor);
if (editor)
@ -1050,7 +1049,7 @@ nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent)
editor->GetSelectionController(getter_AddRefs(selCon));
if (selCon)
{
if (! (flags & nsIHTMLEditor::eEditorReadonlyMask))
if (! (flags & nsIPlaintextEditor::eEditorReadonlyMask))
{ // only enable caret if the editor is not readonly
PRInt32 pixelWidth;
nsresult result;
@ -1059,7 +1058,7 @@ nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent)
if (NS_SUCCEEDED(result) && look)
{
if(flags & nsIHTMLEditor::eEditorSingleLineMask)
if(flags & nsIPlaintextEditor::eEditorSingleLineMask)
look->GetMetric(nsILookAndFeel::eMetric_SingleLineCaretWidth, pixelWidth);
else
look->GetMetric(nsILookAndFeel::eMetric_MultiLineCaretWidth, pixelWidth);
@ -1109,11 +1108,11 @@ nsTextEditorFocusListener::Blur(nsIDOMEvent* aEvent)
if (selCon)
{
selCon->SetCaretEnabled(PR_FALSE);
if((flags & nsIHTMLEditor::eEditorWidgetMask) ||
(flags & nsIHTMLEditor::eEditorPasswordMask) ||
(flags & nsIHTMLEditor::eEditorReadonlyMask) ||
(flags & nsIHTMLEditor::eEditorDisabledMask) ||
(flags & nsIHTMLEditor::eEditorFilterInputMask))
if((flags & nsIPlaintextEditor::eEditorWidgetMask) ||
(flags & nsIPlaintextEditor::eEditorPasswordMask) ||
(flags & nsIPlaintextEditor::eEditorReadonlyMask) ||
(flags & nsIPlaintextEditor::eEditorDisabledMask) ||
(flags & nsIPlaintextEditor::eEditorFilterInputMask))
{
selCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);//hide but do NOT turn off
}

View File

@ -35,7 +35,7 @@
#include "nsIDOMFocusListener.h"
#include "nsIEditor.h"
#include "nsIHTMLEditor.h"
#include "nsIPlaintextEditor.h"
/** The nsTextEditorKeyListener public nsIDOMKeyListener
* This class will delegate events to its editor according to the translation

View File

@ -0,0 +1,582 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
#include "nsICaret.h"
#include "nsPlaintextEditor.h"
#include "nsHTMLEditUtils.h"
#include "nsEditorEventListeners.h"
#include "nsIDOMText.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMDocument.h"
#include "nsIDOMAttr.h"
#include "nsIDocument.h"
#include "nsIDOMEventReceiver.h"
#include "nsIDOMKeyEvent.h"
#include "nsIDOMKeyListener.h"
#include "nsIDOMMouseListener.h"
#include "nsIDOMMouseEvent.h"
#include "nsISelection.h"
#include "nsISelectionPrivate.h"
#include "nsIDOMHTMLAnchorElement.h"
#include "nsIDOMHTMLImageElement.h"
#include "nsISelectionController.h"
#include "nsIFrameSelection.h" // For TABLESELECTION_ defines
#include "nsIIndependentSelection.h" //domselections answer to frameselection
#include "nsICSSLoader.h"
#include "nsICSSStyleSheet.h"
#include "nsIHTMLContentContainer.h"
#include "nsIStyleSet.h"
#include "nsIDocumentObserver.h"
#include "nsIDocumentStateListener.h"
#include "nsIStyleContext.h"
#include "nsIEnumerator.h"
#include "nsIContent.h"
#include "nsIContentIterator.h"
#include "nsEditorCID.h"
#include "nsLayoutCID.h"
#include "nsIDOMRange.h"
#include "nsIDOMNSRange.h"
#include "nsISupportsArray.h"
#include "nsVoidArray.h"
#include "nsFileSpec.h"
#include "nsIFile.h"
#include "nsIURL.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsWidgetsCID.h"
#include "nsIDocumentEncoder.h"
#include "nsIDOMDocumentFragment.h"
#include "nsIPresShell.h"
#include "nsIPresContext.h"
#include "nsIParser.h"
#include "nsParserCIID.h"
#include "nsIImage.h"
#include "nsAOLCiter.h"
#include "nsInternetCiter.h"
#include "nsISupportsPrimitives.h"
#include "InsertTextTxn.h"
// netwerk
#include "nsIURI.h"
#include "nsNetUtil.h"
// Drag & Drop, Clipboard
#include "nsWidgetsCID.h"
#include "nsIClipboard.h"
#include "nsITransferable.h"
#include "nsIDragService.h"
#include "nsIDOMNSUIEvent.h"
// Transactionas
#include "PlaceholderTxn.h"
#include "nsStyleSheetTxns.h"
// Misc
#include "TextEditorTest.h"
#include "nsEditorUtils.h"
#include "nsIPref.h"
const PRUnichar nbsp = 160;
// HACK - CID for NS_CTRANSITIONAL_DTD_CID so that we can get at transitional dtd
#define NS_CTRANSITIONAL_DTD_CID \
{ 0x4611d482, 0x960a, 0x11d4, { 0x8e, 0xb0, 0xb6, 0x17, 0x66, 0x1b, 0x6f, 0x7c } }
static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID);
static NS_DEFINE_CID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
static NS_DEFINE_IID(kSubtreeIteratorCID, NS_SUBTREEITERATOR_CID);
static NS_DEFINE_CID(kCRangeCID, NS_RANGE_CID);
static NS_DEFINE_CID(kCDOMSelectionCID, NS_DOMSELECTION_CID);
static NS_DEFINE_IID(kFileWidgetCID, NS_FILEWIDGET_CID);
static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID);
static NS_DEFINE_IID(kCParserIID, NS_IPARSER_IID);
static NS_DEFINE_IID(kCParserCID, NS_PARSER_IID);
static NS_DEFINE_CID(kCTransitionalDTDCID, NS_CTRANSITIONAL_DTD_CID);
// Drag & Drop, Clipboard Support
static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID);
static NS_DEFINE_CID(kCDragServiceCID, NS_DRAGSERVICE_CID);
static NS_DEFINE_CID(kCHTMLFormatConverterCID, NS_HTMLFORMATCONVERTER_CID);
// private clipboard data flavors for html copy/paste
#define kHTMLContext "text/_moz_htmlcontext"
#define kHTMLInfo "text/_moz_htmlinfo"
#if defined(NS_DEBUG) && defined(DEBUG_buster)
static PRBool gNoisy = PR_FALSE;
#else
static const PRBool gNoisy = PR_FALSE;
#endif
NS_IMETHODIMP nsPlaintextEditor::PrepareTransferable(nsITransferable **transferable)
{
// Create generic Transferable for getting the data
nsresult rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull,
NS_GET_IID(nsITransferable),
(void**)transferable);
if (NS_FAILED(rv))
return rv;
// Get the nsITransferable interface for getting the data from the clipboard
if (transferable) (*transferable)->AddDataFlavor(kUnicodeMime);
return NS_OK;
}
NS_IMETHODIMP nsPlaintextEditor::InsertTextFromTransferable(nsITransferable *transferable)
{
nsresult rv = NS_OK;
char* bestFlavor = nsnull;
nsCOMPtr<nsISupports> genericDataObj;
PRUint32 len = 0;
if ( NS_SUCCEEDED(transferable->GetAnyTransferData(&bestFlavor, getter_AddRefs(genericDataObj), &len)) )
{
nsAutoTxnsConserveSelection dontSpazMySelection(this);
nsAutoString flavor, stuffToPaste;
flavor.AssignWithConversion( bestFlavor ); // just so we can use flavor.Equals()
if (flavor.EqualsWithConversion(kUnicodeMime))
{
nsCOMPtr<nsISupportsWString> textDataObj ( do_QueryInterface(genericDataObj) );
if (textDataObj && len > 0)
{
PRUnichar* text = nsnull;
textDataObj->ToString ( &text );
stuffToPaste.Assign ( text, len / 2 );
nsAutoEditBatch beginBatching(this);
rv = InsertText(stuffToPaste.GetUnicode());
if (text)
nsMemory::Free(text);
}
}
}
nsCRT::free(bestFlavor);
// Try to scroll the selection into view if the paste/drop succeeded
if (NS_SUCCEEDED(rv))
{
nsCOMPtr<nsISelectionController> selCon;
if (NS_SUCCEEDED(GetSelectionController(getter_AddRefs(selCon))) && selCon)
selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION);
}
return rv;
}
NS_IMETHODIMP nsPlaintextEditor::InsertFromDrop(nsIDOMEvent* aDropEvent)
{
ForceCompositionEnd();
nsresult rv;
NS_WITH_SERVICE(nsIDragService, dragService, "@mozilla.org/widget/dragservice;1", &rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDragSession> dragSession(do_QueryInterface(dragService));
if (!dragSession) return NS_OK;
// Get the nsITransferable interface for getting the data from the drop
nsCOMPtr<nsITransferable> trans;
rv = PrepareTransferable(getter_AddRefs(trans));
if (NS_FAILED(rv)) return rv;
if (!trans) return NS_OK; // NS_ERROR_FAILURE; SHOULD WE FAIL?
PRUint32 numItems = 0;
rv = dragSession->GetNumDropItems(&numItems);
if (NS_FAILED(rv)) return rv;
// Combine any deletion and drop insertion into one transaction
nsAutoEditBatch beginBatching(this);
PRUint32 i;
PRBool doPlaceCaret = PR_TRUE;
for (i = 0; i < numItems; ++i)
{
rv = dragSession->GetData(trans, i);
if (NS_FAILED(rv)) return rv;
if (!trans) return NS_OK; // NS_ERROR_FAILURE; Should we fail?
if ( doPlaceCaret )
{
// check if the user pressed the key to force a copy rather than a move
// if we run into problems here, we'll just assume the user doesn't want a copy
PRBool userWantsCopy = PR_FALSE;
nsCOMPtr<nsIDOMNSUIEvent> nsuiEvent (do_QueryInterface(aDropEvent));
if (!nsuiEvent) return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aDropEvent) );
if (mouseEvent)
#ifdef XP_MAC
mouseEvent->GetAltKey(&userWantsCopy);
#else
mouseEvent->GetCtrlKey(&userWantsCopy);
#endif
// Source doc is null if source is *not* the current editor document
nsCOMPtr<nsIDOMDocument> srcdomdoc;
rv = dragSession->GetSourceDocument(getter_AddRefs(srcdomdoc));
if (NS_FAILED(rv)) return rv;
// Current doc is destination
nsCOMPtr<nsIDOMDocument>destdomdoc;
rv = GetDocument(getter_AddRefs(destdomdoc));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsISelection> selection;
rv = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(rv)) return rv;
if (!selection) return NS_ERROR_FAILURE;
PRBool isCollapsed;
rv = selection->GetIsCollapsed(&isCollapsed);
if (NS_FAILED(rv)) return rv;
// Parent and offset under the mouse cursor
nsCOMPtr<nsIDOMNode> newSelectionParent;
PRInt32 newSelectionOffset = 0;
rv = nsuiEvent->GetRangeParent(getter_AddRefs(newSelectionParent));
if (NS_FAILED(rv)) return rv;
if (!newSelectionParent) return NS_ERROR_FAILURE;
rv = nsuiEvent->GetRangeOffset(&newSelectionOffset);
if (NS_FAILED(rv)) return rv;
/* Creating a range to store insert position because when
we delete the selection, range gravity will make sure the insertion
point is in the correct place */
nsCOMPtr<nsIDOMRange> destinationRange;
rv = CreateRange(newSelectionParent, newSelectionOffset,newSelectionParent, newSelectionOffset, getter_AddRefs(destinationRange));
if (NS_FAILED(rv))
return rv;
if(!destinationRange)
return NS_ERROR_FAILURE;
// We never have to delete if selection is already collapsed
PRBool deleteSelection = PR_FALSE;
PRBool cursorIsInSelection = PR_FALSE;
// Check if mouse is in the selection
if (!isCollapsed)
{
PRInt32 rangeCount;
rv = selection->GetRangeCount(&rangeCount);
if (NS_FAILED(rv))
return rv?rv:NS_ERROR_FAILURE;
for (PRInt32 j = 0; j < rangeCount; j++)
{
nsCOMPtr<nsIDOMRange> range;
rv = selection->GetRangeAt(j, getter_AddRefs(range));
if (NS_FAILED(rv) || !range)
continue;//dont bail yet, iterate through them all
nsCOMPtr<nsIDOMNSRange> nsrange(do_QueryInterface(range));
if (NS_FAILED(rv) || !nsrange)
continue;//dont bail yet, iterate through them all
rv = nsrange->IsPointInRange(newSelectionParent, newSelectionOffset, &cursorIsInSelection);
if(cursorIsInSelection)
break;
}
if (cursorIsInSelection)
{
// Dragging within same doc can't drop on itself -- leave!
// (We shouldn't get here - drag event shouldn't have started if over selection)
if (srcdomdoc == destdomdoc)
return NS_OK;
// Dragging from another window onto a selection
// XXX Decision made to NOT do this,
// note that 4.x does replace if dropped on
//deleteSelection = PR_TRUE;
}
else
{
// We are NOT over the selection
if (srcdomdoc == destdomdoc)
{
// Within the same doc: delete if user doesn't want to copy
deleteSelection = !userWantsCopy;
}
else
{
// Different source doc: Don't delete
deleteSelection = PR_FALSE;
}
}
}
if (deleteSelection)
{
rv = DeleteSelection(eNone);
if (NS_FAILED(rv)) return rv;
}
// If we deleted the selection because we dropped from another doc,
// then we don't have to relocate the caret (insert at the deletion point)
if (!(deleteSelection && srcdomdoc != destdomdoc))
{
// Move the selection to the point under the mouse cursor
rv = destinationRange->GetStartContainer(getter_AddRefs(newSelectionParent));
if (NS_FAILED(rv))
return rv;
if(!newSelectionParent)
return NS_ERROR_FAILURE;
rv = destinationRange->GetStartOffset(&newSelectionOffset);
if (NS_FAILED(rv))
return rv;
selection->Collapse(newSelectionParent, newSelectionOffset);
}
// We have to figure out whether to delete and relocate caret only once
doPlaceCaret = PR_FALSE;
}
rv = InsertTextFromTransferable(trans);
}
return rv;
}
NS_IMETHODIMP nsPlaintextEditor::CanDrag(nsIDOMEvent *aDragEvent, PRBool &aCanDrag)
{
/* we really should be checking the XY coordinates of the mouseevent and ensure that
* that particular point is actually within the selection (not just that there is a selection)
*/
aCanDrag = PR_FALSE;
// KLUDGE to work around bug 50703
// After double click and object property editing,
// we get a spurious drag event
if (mIgnoreSpuriousDragEvent)
{
mIgnoreSpuriousDragEvent = PR_FALSE;
return NS_OK;
}
nsCOMPtr<nsISelection> selection;
nsresult res = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
PRBool isCollapsed;
res = selection->GetIsCollapsed(&isCollapsed);
if (NS_FAILED(res)) return res;
// if we are collapsed, we have no selection so nothing to drag
if ( isCollapsed )
return NS_OK;
nsCOMPtr<nsIDOMEventTarget> eventTarget;
res = aDragEvent->GetOriginalTarget(getter_AddRefs(eventTarget));
if (NS_FAILED(res)) return res;
if ( eventTarget )
{
nsCOMPtr<nsIDOMNode> eventTargetDomNode = do_QueryInterface(eventTarget);
if ( eventTargetDomNode )
{
PRBool amTargettedCorrectly = PR_FALSE;
res = selection->ContainsNode(eventTargetDomNode, PR_FALSE, &amTargettedCorrectly);
if (NS_FAILED(res)) return res;
aCanDrag = amTargettedCorrectly;
}
}
return NS_OK;
}
NS_IMETHODIMP nsPlaintextEditor::DoDrag(nsIDOMEvent *aDragEvent)
{
nsresult rv;
nsCOMPtr<nsIDOMEventTarget> eventTarget;
rv = aDragEvent->GetTarget(getter_AddRefs(eventTarget));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDOMNode> domnode = do_QueryInterface(eventTarget);
/* get the selection to be dragged */
nsCOMPtr<nsISelection> selection;
rv = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(rv)) return rv;
/* create an array of transferables */
nsCOMPtr<nsISupportsArray> transferableArray;
NS_NewISupportsArray(getter_AddRefs(transferableArray));
if (transferableArray == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
/* get the drag service */
NS_WITH_SERVICE(nsIDragService, dragService, "@mozilla.org/widget/dragservice;1", &rv);
if (NS_FAILED(rv)) return rv;
/* create html flavor transferable */
nsCOMPtr<nsITransferable> trans;
rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull,
NS_GET_IID(nsITransferable),
getter_AddRefs(trans));
if (NS_FAILED(rv)) return rv;
if ( !trans ) return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr<nsIDOMDocument> domdoc;
rv = GetDocument(getter_AddRefs(domdoc));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
if (doc)
{
nsCOMPtr<nsIDocumentEncoder> docEncoder;
docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "text/html");
NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE);
docEncoder->Init(doc, NS_LITERAL_STRING("text/html"), 0);
docEncoder->SetSelection(selection);
nsAutoString buffer;
rv = docEncoder->EncodeToString(buffer);
if (NS_FAILED(rv))
return rv;
if ( !buffer.IsEmpty() )
{
nsCOMPtr<nsIFormatConverter> htmlConverter;
rv = nsComponentManager::CreateInstance(kCHTMLFormatConverterCID, nsnull, NS_GET_IID(nsIFormatConverter),
getter_AddRefs(htmlConverter));
if (NS_FAILED(rv)) return rv;
if (!htmlConverter) return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr<nsISupportsWString> dataWrapper;
rv = nsComponentManager::CreateInstance(NS_SUPPORTS_WSTRING_CONTRACTID, nsnull,
NS_GET_IID(nsISupportsWString), getter_AddRefs(dataWrapper));
if (NS_FAILED(rv)) return rv;
if ( !dataWrapper ) return NS_ERROR_OUT_OF_MEMORY;
rv = trans->AddDataFlavor(kHTMLMime);
if (NS_FAILED(rv)) return rv;
rv = trans->SetConverter(htmlConverter);
if (NS_FAILED(rv)) return rv;
rv = dataWrapper->SetData( NS_CONST_CAST(PRUnichar*, buffer.GetUnicode()) );
if (NS_FAILED(rv)) return rv;
// QI the data object an |nsISupports| so that when the transferable holds
// onto it, it will addref the correct interface.
nsCOMPtr<nsISupports> nsisupportsDataWrapper ( do_QueryInterface(dataWrapper) );
rv = trans->SetTransferData(kHTMLMime, nsisupportsDataWrapper, buffer.Length() * 2);
if (NS_FAILED(rv)) return rv;
/* add the transferable to the array */
rv = transferableArray->AppendElement(trans);
if (NS_FAILED(rv)) return rv;
/* invoke drag */
unsigned int flags;
// in some cases we'll want to cut rather than copy... hmmmmm...
// if ( wantToCut )
// flags = nsIDragService.DRAGDROP_ACTION_COPY + nsIDragService.DRAGDROP_ACTION_MOVE;
// else
flags = nsIDragService::DRAGDROP_ACTION_COPY + nsIDragService::DRAGDROP_ACTION_MOVE;
rv = dragService->InvokeDragSession( domnode, transferableArray, nsnull, flags);
if (NS_FAILED(rv)) return rv;
aDragEvent->PreventBubble();
}
}
return rv;
}
NS_IMETHODIMP nsPlaintextEditor::Paste(PRInt32 aSelectionType)
{
ForceCompositionEnd();
// Get Clipboard Service
nsresult rv;
NS_WITH_SERVICE ( nsIClipboard, clipboard, kCClipboardCID, &rv );
if ( NS_FAILED(rv) )
return rv;
// Get the nsITransferable interface for getting the data from the clipboard
nsCOMPtr<nsITransferable> trans;
rv = PrepareTransferable(getter_AddRefs(trans));
if (NS_SUCCEEDED(rv) && trans)
{
// Get the Data from the clipboard
if (NS_SUCCEEDED(clipboard->GetData(trans, aSelectionType)) && IsModifiable())
{
rv = InsertTextFromTransferable(trans);
}
}
return rv;
}
NS_IMETHODIMP nsPlaintextEditor::CanPaste(PRInt32 aSelectionType, PRBool &aCanPaste)
{
aCanPaste = PR_FALSE;
nsresult rv;
NS_WITH_SERVICE(nsIClipboard, clipboard, kCClipboardCID, &rv);
if (NS_FAILED(rv)) return rv;
// the flavors that we can deal with
char* textEditorFlavors[] = { kUnicodeMime, nsnull };
nsCOMPtr<nsISupportsArray> flavorsList;
rv = nsComponentManager::CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, nsnull,
NS_GET_IID(nsISupportsArray), getter_AddRefs(flavorsList));
if (NS_FAILED(rv)) return rv;
PRUint32 editorFlags;
GetFlags(&editorFlags);
// add the flavors for text editors
for (char** flavor = textEditorFlavors; *flavor; flavor++)
{
nsCOMPtr<nsISupportsString> flavorString;
nsComponentManager::CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, nsnull,
NS_GET_IID(nsISupportsString), getter_AddRefs(flavorString));
if (flavorString)
{
flavorString->SetData(*flavor);
flavorsList->AppendElement(flavorString);
}
}
PRBool haveFlavors;
rv = clipboard->HasDataMatchingFlavors(flavorsList, aSelectionType, &haveFlavors);
if (NS_FAILED(rv)) return rv;
aCanPaste = haveFlavors;
return NS_OK;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,228 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#ifndef nsPlaintextEditor_h__
#define nsPlaintextEditor_h__
#include "nsCOMPtr.h"
#include "nsIPlaintextEditor.h"
#include "nsEditor.h"
#include "nsIDOMElement.h"
#include "nsIDOMEventListener.h"
#include "TypeInState.h"
#include "nsEditRules.h"
class nsIDOMKeyEvent;
class nsITransferable;
class nsIDOMEventReceiver;
class nsIDocumentEncoder;
/**
* The text editor implementation.
* Use to edit text document represented as a DOM tree.
*/
class nsPlaintextEditor : public nsEditor,
public nsIPlaintextEditor
{
public:
// Interfaces for addref and release and queryinterface
// NOTE macro used is for classes that inherit from
// another class. Only the base class should use NS_DECL_ISUPPORTS
NS_DECL_ISUPPORTS_INHERITED
/* below used by TypedText() */
enum {
eTypedText, /* user typed text */
eTypedBR, /* user typed shift-enter to get a br */
eTypedBreak /* user typed enter */
};
nsPlaintextEditor();
virtual ~nsPlaintextEditor();
/* ------------ nsIPlaintextEditor methods -------------- */
NS_DECL_NSIPLAINTEXTEDITOR
/* ------------ nsIEditorIMESupport overrides -------------- */
NS_IMETHOD SetCompositionString(const nsString& aCompositionString, nsIPrivateTextRangeList* aTextRangeList,nsTextEventReply* aReply);
NS_IMETHOD GetReconversionString(nsReconversionEventReply* aReply);
/* ------------ Overrides of nsEditor interface methods -------------- */
/** prepare the editor for use */
NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell, nsIContent *aRoot, nsISelectionController *aSelCon, PRUint32 aFlags);
NS_IMETHOD GetDocumentIsEmpty(PRBool *aDocumentIsEmpty);
NS_IMETHOD DeleteSelection(EDirection aAction);
NS_IMETHOD SetDocumentCharacterSet(const PRUnichar* characterSet);
/** we override this here to install event listeners */
NS_IMETHOD PostCreate();
NS_IMETHOD GetFlags(PRUint32 *aFlags);
NS_IMETHOD SetFlags(PRUint32 aFlags);
NS_IMETHOD Undo(PRUint32 aCount);
NS_IMETHOD Redo(PRUint32 aCount);
NS_IMETHOD Cut();
NS_IMETHOD CanCut(PRBool &aCanCut);
NS_IMETHOD Copy();
NS_IMETHOD CanCopy(PRBool &aCanCopy);
NS_IMETHOD Paste(PRInt32 aSelectionType);
NS_IMETHOD CanPaste(PRInt32 aSelectionType, PRBool &aCanPaste);
NS_IMETHOD CanDrag(nsIDOMEvent *aDragEvent, PRBool &aCanDrag);
NS_IMETHOD DoDrag(nsIDOMEvent *aDragEvent);
NS_IMETHOD InsertFromDrop(nsIDOMEvent* aDropEvent);
NS_IMETHOD OutputToString(nsAWritableString& aOutputString,
const nsAReadableString& aFormatType,
PRUint32 aFlags);
NS_IMETHOD OutputToStream(nsIOutputStream* aOutputStream,
const nsAReadableString& aFormatType,
const nsAReadableString* aCharsetOverride,
PRUint32 aFlags);
/** All editor operations which alter the doc should be prefaced
* with a call to StartOperation, naming the action and direction */
NS_IMETHOD StartOperation(PRInt32 opID, nsIEditor::EDirection aDirection);
/** All editor operations which alter the doc should be followed
* with a call to EndOperation */
NS_IMETHOD EndOperation();
/** make the given selection span the entire document */
NS_IMETHOD SelectEntireDocument(nsISelection *aSelection);
/* ------------ Utility Routines, not part of public API -------------- */
NS_IMETHOD TypedText(const PRUnichar* aString, PRInt32 aAction);
/** returns the absolute position of the end points of aSelection
* in the document as a text stream.
*/
nsresult GetTextSelectionOffsets(nsISelection *aSelection,
PRInt32 &aStartOffset,
PRInt32 &aEndOffset);
nsresult GetAbsoluteOffsetsForPoints(nsIDOMNode *aInStartNode,
PRInt32 aInStartOffset,
nsIDOMNode *aInEndNode,
PRInt32 aInEndOffset,
nsIDOMNode *aInCommonParentNode,
PRInt32 &aOutStartOffset,
PRInt32 &aEndOffset);
protected:
NS_IMETHOD InitRules();
void BeginEditorInit();
nsresult EndEditorInit();
/** install the event listeners for the editor
* used to be part of Init, but now broken out into a separate method
* called by PostCreate, giving the caller the chance to interpose
* their own listeners before we install our own backstops.
*/
NS_IMETHOD InstallEventListeners();
/** returns the layout object (nsIFrame in the real world) for aNode
* @param aNode the content to get a frame for
* @param aLayoutObject the "primary frame" for aNode, if one exists. May be null
* @return NS_OK whether a frame is found or not
* an error if some serious error occurs
*/
NS_IMETHOD GetLayoutObject(nsIDOMNode *aInNode, nsISupports **aOutLayoutObject);
NS_IMETHOD GetBodyStyleContext(nsIStyleContext** aStyleContext);
// Helpers for output routines
NS_IMETHOD GetAndInitDocEncoder(const nsAReadableString& aFormatType,
PRUint32 aFlags,
const nsAReadableString* aCharset,
nsIDocumentEncoder** encoder);
// key event helpers
NS_IMETHOD CreateBR(nsIDOMNode *aNode, PRInt32 aOffset,
nsCOMPtr<nsIDOMNode> *outBRNode, EDirection aSelect = eNone);
NS_IMETHOD CreateBRImpl(nsCOMPtr<nsIDOMNode> *aInOutParent,
PRInt32 *aInOutOffset,
nsCOMPtr<nsIDOMNode> *outBRNode,
EDirection aSelect);
NS_IMETHOD InsertBR(nsCOMPtr<nsIDOMNode> *outBRNode);
NS_IMETHOD IsRootTag(nsString &aTag, PRBool &aIsTag);
// factored methods for handling insertion of data from transferables (drag&drop or clipboard)
NS_IMETHOD PrepareTransferable(nsITransferable **transferable);
NS_IMETHOD InsertTextFromTransferable(nsITransferable *transferable);
/** simple utility to handle any error with event listener allocation or registration */
void HandleEventListenerError();
/* small utility routine to test the eEditorReadonly bit */
PRBool IsModifiable();
nsresult GetDOMEventReceiver(nsIDOMEventReceiver **aEventReceiver);
//XXX Kludge: Used to suppress spurious drag/drop events (bug 50703)
PRBool mIgnoreSpuriousDragEvent;
NS_IMETHOD IgnoreSpuriousDragEvent(PRBool aIgnoreSpuriousDragEvent) {mIgnoreSpuriousDragEvent = aIgnoreSpuriousDragEvent; return NS_OK;}
// Data members
protected:
nsCOMPtr<nsIEditRules> mRules;
nsCOMPtr<nsIDOMEventListener> mKeyListenerP;
nsCOMPtr<nsIDOMEventListener> mMouseListenerP;
nsCOMPtr<nsIDOMEventListener> mTextListenerP;
nsCOMPtr<nsIDOMEventListener> mCompositionListenerP;
nsCOMPtr<nsIDOMEventListener> mDragListenerP;
nsCOMPtr<nsIDOMEventListener> mFocusListenerP;
PRBool mIsComposing;
PRInt32 mMaxTextLength;
PRInt32 mInitTriggerCounter;
public:
static nsIAtom *gTypingTxnName;
static nsIAtom *gIMETxnName;
static nsIAtom *gDeleteTxnName;
// friends
friend class nsHTMLEditRules;
friend class nsTextEditRules;
friend class nsAutoEditInitRulesTrigger;
};
#endif //nsPlaintextEditor_h__

View File

@ -48,7 +48,7 @@ static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID);
#define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \
if ((mFlags & nsIHTMLEditor::eEditorReadonlyMask) || (mFlags & nsIHTMLEditor::eEditorDisabledMask)) \
if ((mFlags & nsIPlaintextEditor::eEditorReadonlyMask) || (mFlags & nsIPlaintextEditor::eEditorDisabledMask)) \
{ \
*aCancel = PR_TRUE; \
return NS_OK; \
@ -101,7 +101,7 @@ NS_IMPL_QUERY_INTERFACE1(nsTextEditRules, nsIEditRules)
********************************************************/
NS_IMETHODIMP
nsTextEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags)
nsTextEditRules::Init(nsPlaintextEditor *aEditor, PRUint32 aFlags)
{
if (!aEditor) { return NS_ERROR_NULL_POINTER; }
@ -125,10 +125,8 @@ nsTextEditRules::Init(nsHTMLEditor *aEditor, PRUint32 aFlags)
if (NS_FAILED(res)) return res;
// create a range that is the entire body contents
nsCOMPtr<nsIDOMRange> wholeDoc;
res = nsComponentManager::CreateInstance(kRangeCID, nsnull, NS_GET_IID(nsIDOMRange),
getter_AddRefs(wholeDoc));
if (NS_FAILED(res)) return res;
nsCOMPtr<nsIDOMRange> wholeDoc = do_CreateInstance(kRangeCID);
if (!wholeDoc) return NS_ERROR_NULL_POINTER;
wholeDoc->SetStart(mBody,0);
nsCOMPtr<nsIDOMNodeList> list;
res = mBody->GetChildNodes(getter_AddRefs(list));
@ -163,11 +161,9 @@ nsTextEditRules::SetFlags(PRUint32 aFlags)
// SetFlags() is really meant to only be called once
// and at editor init time.
PRBool willBePlaintext = (aFlags & nsIHTMLEditor::eEditorPlaintextMask) != 0;
PRBool alreadyPlaintext = (mFlags & nsIHTMLEditor::eEditorPlaintextMask) != 0;
PRBool willBePlaintext = (aFlags & nsIPlaintextEditor::eEditorPlaintextMask) != 0;
PRBool alreadyPlaintext = (mFlags & nsIPlaintextEditor::eEditorPlaintextMask) != 0;
mFlags = aFlags;
return NS_OK;
}
@ -207,8 +203,6 @@ nsTextEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)
res = CreateBogusNodeIfNeeded(selection);
if (NS_FAILED(res)) return res;
// create moz-br and adjust selection if needed
res = AdjustSelection(selection, aDirection);
}
return res;
}
@ -340,42 +334,7 @@ nsTextEditRules::WillInsert(nsISelection *aSelection, PRBool *aCancel)
mBogusNode = do_QueryInterface(nsnull);
}
// this next only works for collapsed selections right now,
// because selection is a pain to work with when not collapsed.
// (no good way to extend start or end of selection)
PRBool bCollapsed;
nsresult res = aSelection->GetIsCollapsed(&bCollapsed);
if (NS_FAILED(res)) return res;
if (!bCollapsed) return NS_OK;
// if we are after a mozBR in the same block, then move selection
// to be before it
nsCOMPtr<nsIDOMNode> selNode, priorNode;
PRInt32 selOffset;
// get the (collapsed) selection location
res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
if (NS_FAILED(res)) return res;
// get prior node
res = mEditor->GetPriorHTMLNode(selNode, selOffset, address_of(priorNode));
if (NS_SUCCEEDED(res) && priorNode && nsHTMLEditUtils::IsMozBR(priorNode))
{
nsCOMPtr<nsIDOMNode> block1, block2;
if (mEditor->IsBlockNode(selNode)) block1 = selNode;
else block1 = mEditor->GetBlockNodeParent(selNode);
block2 = mEditor->GetBlockNodeParent(priorNode);
if (block1 != block2) return NS_OK;
// if we are here then the selection is right after a mozBR
// that is in the same block as the selection. We need to move
// the selection start to be before the mozBR.
res = nsEditor::GetNodeLocation(priorNode, address_of(selNode), &selOffset);
if (NS_FAILED(res)) return res;
res = aSelection->Collapse(selNode,selOffset);
if (NS_FAILED(res)) return res;
}
return res;
return NS_OK;
}
nsresult
@ -384,44 +343,13 @@ nsTextEditRules::DidInsert(nsISelection *aSelection, nsresult aResult)
return NS_OK;
}
nsresult
nsTextEditRules::GetTopEnclosingPre(nsIDOMNode *aNode,
nsIDOMNode** aOutPreNode)
{
// check parms
if (!aNode || !aOutPreNode)
return NS_ERROR_NULL_POINTER;
*aOutPreNode = 0;
nsresult res = NS_OK;
nsCOMPtr<nsIDOMNode> node, parentNode;
node = do_QueryInterface(aNode);
while (node)
{
nsAutoString tag;
nsEditor::GetTagString(node, tag);
if (tag.EqualsWithConversion("pre", PR_TRUE))
*aOutPreNode = node;
else if (tag.EqualsWithConversion("body", PR_TRUE))
break;
res = node->GetParentNode(getter_AddRefs(parentNode));
if (NS_FAILED(res)) return res;
node = parentNode;
}
NS_IF_ADDREF(*aOutPreNode);
return res;
}
nsresult
nsTextEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBool *aHandled)
{
if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
CANCEL_OPERATION_IF_READONLY_OR_DISABLED
*aHandled = PR_FALSE;
if (mFlags & nsIHTMLEditor::eEditorSingleLineMask) {
if (mFlags & nsIPlaintextEditor::eEditorSingleLineMask) {
*aCancel = PR_TRUE;
}
else
@ -444,72 +372,6 @@ nsTextEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
// we want to ignore result of WillInsert()
*aCancel = PR_FALSE;
// Mail rule: split any <pre> tags in the way,
// since they're probably quoted text.
// For now, do this for all plaintext since mail is our main customer
// and we don't currently set eEditorMailMask for plaintext mail.
if (mTheAction != nsHTMLEditor::kOpInsertQuotation) // && mFlags & nsIHTMLEditor::eEditorMailMask)
{
nsCOMPtr<nsIDOMNode> preNode, selNode;
PRInt32 selOffset, newOffset;
res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
if (NS_FAILED(res)) return res;
// If any of the following fail, then just proceed with the
// normal break insertion without worrying about the error
res = GetTopEnclosingPre(selNode, getter_AddRefs(preNode));
if (NS_SUCCEEDED(res) && preNode)
{
// Only split quote nodes: see if it has the attribute _moz_quote
nsCOMPtr<nsIDOMElement> preElement (do_QueryInterface(preNode));
if (preElement)
{
nsString mozQuote; mozQuote.AssignWithConversion("_moz_quote");
nsString mozQuoteVal;
PRBool isMozQuote = PR_FALSE;
if (NS_SUCCEEDED(mEditor->GetAttributeValue(preElement, mozQuote,
mozQuoteVal, isMozQuote))
&& isMozQuote)
{
printf("It's a moz quote -- splitting\n");
nsCOMPtr<nsIDOMNode> outLeftNode;
nsCOMPtr<nsIDOMNode> outRightNode;
res = mEditor->SplitNodeDeep(preNode, selNode, selOffset, &newOffset, PR_TRUE, address_of(outLeftNode), address_of(outRightNode));
if (NS_FAILED(res)) return res;
PRBool bIsEmptyNode;
// rememeber parent of selNode now, since we might delete selNode below
res = preNode->GetParentNode(getter_AddRefs(selNode));
if (NS_FAILED(res)) return res;
if (outLeftNode)
{
res = mEditor->IsEmptyNode(outLeftNode, &bIsEmptyNode, PR_TRUE, PR_FALSE);
if (NS_FAILED(res)) return res;
if (bIsEmptyNode) mEditor->DeleteNode(outLeftNode);
}
if (outRightNode)
{
// HACK alert: consume a br if there is one at front of node
nsCOMPtr<nsIDOMNode> firstNode;
res = mEditor->GetFirstEditableNode(outRightNode, address_of(firstNode));
if (firstNode && nsHTMLEditUtils::IsBreak(firstNode))
{
mEditor->DeleteNode(firstNode);
}
res = mEditor->IsEmptyNode(outRightNode, &bIsEmptyNode, PR_TRUE, PR_FALSE);
if (NS_FAILED(res)) return res;
if (bIsEmptyNode) mEditor->DeleteNode(outRightNode);
}
nsCOMPtr<nsIDOMNode> brNode;
// last ePrevious param causes selection to be set before the break
res = mEditor->CreateBR(selNode, newOffset, address_of(brNode), nsIEditor::ePrevious);
*aHandled = PR_TRUE;
}
}
}
}
}
return NS_OK;
}
@ -519,42 +381,44 @@ nsTextEditRules::DidInsertBreak(nsISelection *aSelection, nsresult aResult)
{
// we only need to execute the stuff below if we are a plaintext editor.
// html editors have a different mechanism for putting in mozBR's
// (because there are a bunch more placesyou have to worry about it in html)
if (!nsIHTMLEditor::eEditorPlaintextMask & mFlags) return NS_OK;
// (because there are a bunch more places you have to worry about it in html)
if (!nsIPlaintextEditor::eEditorPlaintextMask & mFlags) return NS_OK;
// if we are at the end of the document, we need to insert
// a special mozBR following the normal br, and then set the
// selection to stick to the mozBR.
PRInt32 selOffset;
nsCOMPtr<nsIDOMNode> nearNode, selNode;
nsCOMPtr<nsIDOMNode> nearNode, selNode, root, temp;
nsCOMPtr<nsIDOMElement> rootElem;
nsresult res;
res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
if (NS_FAILED(res)) return res;
res = mEditor->GetPriorHTMLNode(selNode, selOffset, address_of(nearNode));
// confirm we are at end of document
if (selOffset == 0) return NS_OK; // cant be after a br if we are at offset 0
res = mEditor->GetRootElement(getter_AddRefs(rootElem));
if (NS_FAILED(res)) return res;
root = do_QueryInterface(rootElem);
if (!root) return NS_ERROR_NULL_POINTER;
if (selNode != root) return NS_OK; // must be inside text node or somewhere other than end of root
temp = mEditor->GetChildAt(selNode, selOffset);
if (temp) return NS_OK; // cant be at end of there is a node after us.
nearNode = mEditor->GetChildAt(selNode, selOffset-1);
if (nearNode && nsHTMLEditUtils::IsBreak(nearNode) && !nsHTMLEditUtils::IsMozBR(nearNode))
{
PRBool bIsLast;
res = mEditor->IsLastEditableChild(nearNode, &bIsLast);
nsCOMPtr<nsISelection> sel(aSelection);
nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(sel));
// need to insert special moz BR. Why? Because if we don't
// the user will see no new line for the break. Also, things
// like table cells won't grow in height.
nsCOMPtr<nsIDOMNode> brNode;
res = CreateMozBR(selNode, selOffset, address_of(brNode));
if (NS_FAILED(res)) return res;
res = nsEditor::GetNodeLocation(brNode, address_of(selNode), &selOffset);
if (NS_FAILED(res)) return res;
selPrivate->SetInterlinePosition(PR_TRUE);
res = aSelection->Collapse(selNode,selOffset);
if (NS_FAILED(res)) return res;
if (bIsLast)
{
nsCOMPtr<nsISelection> sel(aSelection);
nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(sel));
// need to insert special moz BR. Why? Because if we don't
// the user will see no new line for the break. Also, things
// like table cells won't grow in height.
nsCOMPtr<nsIDOMNode> brNode;
res = CreateMozBR(selNode, selOffset, address_of(brNode));
if (NS_FAILED(res)) return res;
res = nsEditor::GetNodeLocation(brNode, address_of(selNode), &selOffset);
if (NS_FAILED(res)) return res;
selPrivate->SetInterlinePosition(PR_TRUE);
res = aSelection->Collapse(selNode,selOffset);
if (NS_FAILED(res)) return res;
}
}
// mEditor->DumpContentTree();
return res;
}
@ -595,7 +459,7 @@ nsTextEditRules::WillInsertText(PRInt32 aAction,
if (NS_FAILED(res)) return res;
// handle password field docs
if (mFlags & nsIHTMLEditor::eEditorPasswordMask)
if (mFlags & nsIPlaintextEditor::eEditorPasswordMask)
{
res = mEditor->GetTextSelectionOffsets(aSelection, start, end);
NS_ASSERTION((NS_SUCCEEDED(res)), "getTextSelectionOffsets failed!");
@ -621,7 +485,7 @@ nsTextEditRules::WillInsertText(PRInt32 aAction,
// handle password field data
// this has the side effect of changing all the characters in aOutString
// to the replacement character
if (mFlags & nsIHTMLEditor::eEditorPasswordMask)
if (mFlags & nsIPlaintextEditor::eEditorPasswordMask)
{
res = EchoInsertionToPWBuff(start, end, outString);
if (NS_FAILED(res)) return res;
@ -646,7 +510,7 @@ nsTextEditRules::WillInsertText(PRInt32 aAction,
rv = prefs->GetIntPref("editor.singleLine.pasteNewlines",
&singleLineNewlineBehavior);
if (nsIHTMLEditor::eEditorSingleLineMask & mFlags)
if (nsIPlaintextEditor::eEditorSingleLineMask & mFlags)
{
if (singleLineNewlineBehavior == eReplaceWithSpaces)
outString->ReplaceChar(CRLF, ' ');
@ -731,7 +595,7 @@ nsTextEditRules::WillInsertText(PRInt32 aAction,
// is it a return?
if (subStr.EqualsWithConversion("\n"))
{
if (nsIHTMLEditor::eEditorSingleLineMask & mFlags)
if (nsIPlaintextEditor::eEditorSingleLineMask & mFlags)
{
NS_ASSERTION((singleLineNewlineBehavior == ePasteIntact),
"Newline improperly getting into single-line edit field!");
@ -814,7 +678,7 @@ nsTextEditRules::WillSetTextProperty(nsISelection *aSelection, PRBool *aCancel,
nsresult res = NS_OK;
// XXX: should probably return a success value other than NS_OK that means "not allowed"
if (nsIHTMLEditor::eEditorPlaintextMask & mFlags) {
if (nsIPlaintextEditor::eEditorPlaintextMask & mFlags) {
*aCancel = PR_TRUE;
}
return res;
@ -834,7 +698,7 @@ nsTextEditRules::WillRemoveTextProperty(nsISelection *aSelection, PRBool *aCance
nsresult res = NS_OK;
// XXX: should probably return a success value other than NS_OK that means "not allowed"
if (nsIHTMLEditor::eEditorPlaintextMask & mFlags) {
if (nsIPlaintextEditor::eEditorPlaintextMask & mFlags) {
*aCancel = PR_TRUE;
}
return res;
@ -864,7 +728,7 @@ nsTextEditRules::WillDeleteSelection(nsISelection *aSelection,
*aCancel = PR_TRUE;
return NS_OK;
}
if (mFlags & nsIHTMLEditor::eEditorPasswordMask)
if (mFlags & nsIPlaintextEditor::eEditorPasswordMask)
{
// manage the password buffer
PRInt32 start, end;
@ -1098,7 +962,7 @@ nsTextEditRules::WillOutputText(nsISelection *aSelection,
if (PR_TRUE == aOutputFormat->EqualsWithConversion("text/plain"))
{ // only use these rules for plain text output
if (mFlags & nsIHTMLEditor::eEditorPasswordMask)
if (mFlags & nsIPlaintextEditor::eEditorPasswordMask)
{
*aOutString = mPasswordText;
*aHandled = PR_TRUE;
@ -1288,7 +1152,7 @@ nsTextEditRules::TruncateInsertionIfNeeded(nsISelection *aSelection,
nsresult res = NS_OK;
*aOutString = *aInString;
if ((-1 != aMaxLength) && (mFlags & nsIHTMLEditor::eEditorPlaintextMask))
if ((-1 != aMaxLength) && (mFlags & nsIPlaintextEditor::eEditorPlaintextMask))
{
// Get the current text length.
// Get the length of inString.
@ -1388,69 +1252,3 @@ nsTextEditRules::DeleteEmptyTextNode(nsIDOMNode *aNode)
return PR_FALSE;
}
nsresult
nsTextEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection aDirection)
{
if (!aSelection) return NS_ERROR_NULL_POINTER;
// if the selection isn't collapsed, do nothing.
PRBool bCollapsed;
nsresult res = aSelection->GetIsCollapsed(&bCollapsed);
if (NS_FAILED(res)) return res;
if (!bCollapsed) return res;
// get the (collapsed) selection location
nsCOMPtr<nsIDOMNode> selNode, temp;
PRInt32 selOffset;
res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
if (NS_FAILED(res)) return res;
temp = selNode;
// are we in an editable node?
while (!mEditor->IsEditable(selNode))
{
// scan up the tree until we find an editable place to be
res = nsEditor::GetNodeLocation(temp, address_of(selNode), &selOffset);
if (NS_FAILED(res)) return res;
if (!selNode) return NS_ERROR_FAILURE;
temp = selNode;
}
// are we in a text node?
nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(selNode);
if (textNode)
return NS_OK; // we LIKE it when we are in a text node. that RULZ
// do we need to insert a special mozBR? We do if we are:
// 1) after a block element AND
// 2) at the end of the body OR before another block
nsCOMPtr<nsIDOMNode> priorNode, nextNode;
res = mEditor->GetPriorHTMLSibling(selNode, selOffset, address_of(priorNode));
if (NS_FAILED(res)) return res;
res = mEditor->GetNextHTMLSibling(selNode, selOffset, address_of(nextNode));
if (NS_FAILED(res)) return res;
// is priorNode a block?
if (priorNode && mEditor->IsBlockNode(priorNode))
{
if (!nextNode || mEditor->IsBlockNode(nextNode))
{
nsCOMPtr<nsISelection> sel(aSelection);
nsCOMPtr<nsISelectionPrivate>selPrivate(do_QueryInterface(sel));
nsCOMPtr<nsIDOMNode> brNode;
res = CreateMozBR(selNode, selOffset, address_of(brNode));
if (NS_FAILED(res)) return res;
res = nsEditor::GetNodeLocation(brNode, address_of(selNode), &selOffset);
if (NS_FAILED(res)) return res;
// selection stays *before* moz-br, sticking to it
selPrivate->SetInterlinePosition(PR_TRUE);
res = aSelection->Collapse(selNode,selOffset);
if (NS_FAILED(res)) return res;
}
}
return res;
}

View File

@ -25,7 +25,7 @@
#include "nsCOMPtr.h"
#include "nsHTMLEditor.h"
#include "nsPlaintextEditor.h"
#include "nsIDOMNode.h"
#include "nsEditRules.h"
@ -50,7 +50,7 @@ public:
virtual ~nsTextEditRules();
// nsIEditRules methods
NS_IMETHOD Init(nsHTMLEditor *aEditor, PRUint32 aFlags);
NS_IMETHOD Init(nsPlaintextEditor *aEditor, PRUint32 aFlags);
NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection);
NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection);
NS_IMETHOD WillDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled);
@ -163,17 +163,15 @@ protected:
PRBool DeleteEmptyTextNode(nsIDOMNode *aNode);
nsresult AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection aDirection);
// data members
nsHTMLEditor *mEditor; // note that we do not refcount the editor
nsString mPasswordText; // a buffer we use to store the real value of password editors
nsCOMPtr<nsIDOMNode> mBogusNode; // magic node acts as placeholder in empty doc
nsCOMPtr<nsIDOMNode> mBody; // cached root node
PRUint32 mFlags;
PRUint32 mActionNesting;
PRBool mLockRulesSniffing;
PRInt32 mTheAction; // the top level editor action
nsPlaintextEditor *mEditor; // note that we do not refcount the editor
nsString mPasswordText; // a buffer we use to store the real value of password editors
nsCOMPtr<nsIDOMNode> mBogusNode; // magic node acts as placeholder in empty doc
nsCOMPtr<nsIDOMNode> mBody; // cached root node
PRUint32 mFlags;
PRUint32 mActionNesting;
PRBool mLockRulesSniffing;
PRInt32 mTheAction; // the top level editor action
// friends
friend class nsAutoLockRulesSniffing;

Binary file not shown.

View File

@ -5,6 +5,11 @@
0xd3de3431, 0x8a75, 0x11d2, \
{ 0x91, 0x8c, 0x0, 0x80, 0xc8, 0xe4, 0x4d, 0xb5 } }
#define NS_TEXTEDITOR_CID \
{/* {e197cc01-cfe1-11d4-8eb0-87ae406dfd3f}*/ \
0xe197cc01, 0xcfe1, 0x11d4, \
{ 0x8e, 0xb0, 0x87, 0xae, 0x40, 0x6d, 0xfd, 0x3f } }
#define NS_HTMLEDITOR_CID \
{/* {ed0244e0-c144-11d2-8f4c-006008159b0c}*/ \
0xed0244e0, 0xc144, 0x11d2, \

View File

@ -140,6 +140,14 @@ public:
*/
NS_IMETHOD DeleteSelection(EDirection aAction)=0;
/**
* DeleteSelectionAndCreateNode combines DeleteSelection and CreateNode
* It deletes only if there is something selected (doesn't do DEL, BACKSPACE action)
* @param aTag The type of object to create
* @param aNewNode [OUT] The node created. Caller must release aNewNode.
*/
NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag, nsIDOMNode ** aNewNode)=0;
/* ------------ Document info and file methods -------------- */

View File

@ -45,45 +45,6 @@ class nsIHTMLEditor : public nsISupports
public:
static const nsIID& GetIID() { static nsIID iid = NS_IHTMLEDITOR_IID; return iid; }
// the bits in an editor behavior mask.
enum {
eEditorPlaintextBit = 0, // only plain text entry is allowed via events
eEditorSingleLineBit, // enter key and CR-LF handled specially
eEditorPasswordBit, // text is not entered into content, only a representative character
eEditorReadonlyBit, // editing events are disabled. Editor may still accept focus.
eEditorDisabledBit, // all events are disabled (like scrolling). Editor will not accept focus.
eEditorFilterInputBit, // text input is limited to certain character types, use mFilter
eEditorMailBit, // use mail-compose editting rules
eEditorDisableForcedUpdatesBit, // prevent immediate view refreshes
eEditorDisableForcedReflowsBit, // prevent immediate reflows
eEditorEnableWrapHackBit, // allow the editor to set font: monospace on the root node
eEditorWidgetBit // bit for widgets
// max 32 bits
};
enum {
eEditorPlaintextMask = (1 << eEditorPlaintextBit),
eEditorSingleLineMask = (1 << eEditorSingleLineBit),
eEditorPasswordMask = (1 << eEditorPasswordBit),
eEditorReadonlyMask = (1 << eEditorReadonlyBit),
eEditorDisabledMask = (1 << eEditorDisabledBit),
eEditorFilterInputMask = (1 << eEditorFilterInputBit),
eEditorMailMask = (1 << eEditorMailBit),
eEditorDisableForcedUpdatesMask = (1 << eEditorDisableForcedUpdatesBit),
eEditorDisableForcedReflowsMask = (1 << eEditorDisableForcedReflowsBit),
eEditorEnableWrapHackMask = (1 << eEditorEnableWrapHackBit),
eEditorWidgetMask = (1 << eEditorWidgetBit)
};
// below used by TypedText()
enum {
eTypedText, // user typed text
eTypedBR, // user typed shift-enter to get a br
eTypedBreak // user typed enter
};
// used by GetAlignment()
typedef enum {
eLeft,
@ -232,14 +193,6 @@ public:
*/
NS_IMETHOD InsertElementAtSelection(nsIDOMElement* aElement, PRBool aDeleteSelection)=0;
/**
* DeleteSelectionAndCreateNode combines DeleteSelection and CreateNode
* It deletes only if there is something selected (doesn't do DEL, BACKSPACE action)
* @param aTag The type of object to create
* @param aNewNode [OUT] The node created. Caller must release aNewNode.
*/
NS_IMETHOD DeleteSelectionAndCreateNode(const nsString& aTag, nsIDOMNode ** aNewNode)=0;
/* ------------ Selection manipulation -------------- */
/* Should these be moved to nsISelection? */

View File

@ -31,7 +31,6 @@
#include "nsIFormControl.h"
#include "nsIServiceManager.h"
#include "nsIFrameSelection.h"
#include "nsIHTMLEditor.h"
#include "nsIPlaintextEditor.h"
#include "nsEditorCID.h"
#include "nsLayoutCID.h"
@ -101,7 +100,7 @@
#define GUESS_INPUT_SIZE 150 // 10 pixels wide
static NS_DEFINE_CID(kHTMLEditorCID, NS_HTMLEDITOR_CID);
static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID);
static NS_DEFINE_CID(kFrameSelectionCID, NS_FRAMESELECTION_CID);
@ -1612,8 +1611,8 @@ nsGfxTextControlFrame2::SetInitialValue()
// immediate reflows during any editor calls.
rv = mEditor->SetFlags(editorFlags |
nsIHTMLEditor::eEditorDisableForcedUpdatesMask |
nsIHTMLEditor::eEditorDisableForcedReflowsMask);
nsIPlaintextEditor::eEditorDisableForcedUpdatesMask |
nsIPlaintextEditor::eEditorDisableForcedReflowsMask);
if (NS_FAILED(rv))
return rv;
@ -1751,9 +1750,10 @@ nsGfxTextControlFrame2::CreateAnonymousContent(nsIPresContext* aPresContext,
// Create an editor
rv = nsComponentManager::CreateInstance(kHTMLEditorCID,
nsnull,
NS_GET_IID(nsIEditor), getter_AddRefs(mEditor));
rv = nsComponentManager::CreateInstance(kTextEditorCID,
nsnull,
NS_GET_IID(nsIEditor),
getter_AddRefs(mEditor));
if (NS_FAILED(rv))
return rv;
if (!mEditor)
@ -1784,14 +1784,14 @@ nsGfxTextControlFrame2::CreateAnonymousContent(nsIPresContext* aPresContext,
PRUint32 editorFlags = 0;
if (IsPlainTextControl())
editorFlags |= nsIHTMLEditor::eEditorPlaintextMask;
editorFlags |= nsIPlaintextEditor::eEditorPlaintextMask;
if (IsSingleLineTextControl())
editorFlags |= nsIHTMLEditor::eEditorSingleLineMask;
editorFlags |= nsIPlaintextEditor::eEditorSingleLineMask;
if (IsPasswordTextControl())
editorFlags |= nsIHTMLEditor::eEditorPasswordMask;
editorFlags |= nsIPlaintextEditor::eEditorPasswordMask;
//all gfxtextcontrolframe2's are widgets
editorFlags |= nsIHTMLEditor::eEditorWidgetMask;
editorFlags |= nsIPlaintextEditor::eEditorWidgetMask;
// Now initialize the editor.
//
@ -1949,7 +1949,7 @@ nsGfxTextControlFrame2::CreateAnonymousContent(nsIPresContext* aPresContext,
return rv;
if (NS_CONTENT_ATTR_NOT_THERE != rv)
editorFlags |= nsIHTMLEditor::eEditorReadonlyMask;
editorFlags |= nsIPlaintextEditor::eEditorReadonlyMask;
// Check if the disabled attribute is set.
@ -1959,18 +1959,18 @@ nsGfxTextControlFrame2::CreateAnonymousContent(nsIPresContext* aPresContext,
return rv;
if (NS_CONTENT_ATTR_NOT_THERE != rv)
editorFlags |= nsIHTMLEditor::eEditorDisabledMask;
editorFlags |= nsIPlaintextEditor::eEditorDisabledMask;
// Disable the caret and selection if neccessary.
if (editorFlags & nsIHTMLEditor::eEditorReadonlyMask ||
editorFlags & nsIHTMLEditor::eEditorDisabledMask)
if (editorFlags & nsIPlaintextEditor::eEditorReadonlyMask ||
editorFlags & nsIPlaintextEditor::eEditorDisabledMask)
{
if (mSelCon)
{
mSelCon->SetCaretEnabled(PR_FALSE);
if (editorFlags & nsIHTMLEditor::eEditorDisabledMask)
if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask)
mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
}
@ -2689,14 +2689,14 @@ nsGfxTextControlFrame2::AttributeChanged(nsIPresContext* aPresContext,
mEditor->GetFlags(&flags);
if (NS_CONTENT_ATTR_NOT_THERE != rv)
{ // set readonly
flags |= nsIHTMLEditor::eEditorReadonlyMask;
flags |= nsIPlaintextEditor::eEditorReadonlyMask;
if (mSelCon)
mSelCon->SetCaretEnabled(PR_FALSE);
}
else
{ // unset readonly
flags &= ~(nsIHTMLEditor::eEditorReadonlyMask);
if (mSelCon && !(flags & nsIHTMLEditor::eEditorDisabledMask))
flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
if (mSelCon && !(flags & nsIPlaintextEditor::eEditorDisabledMask))
mSelCon->SetCaretEnabled(PR_TRUE);
}
mEditor->SetFlags(flags);
@ -2713,7 +2713,7 @@ nsGfxTextControlFrame2::AttributeChanged(nsIPresContext* aPresContext,
mEditor->GetFlags(&flags);
if (NS_CONTENT_ATTR_NOT_THERE != rv)
{ // set disabled
flags |= nsIHTMLEditor::eEditorDisabledMask;
flags |= nsIPlaintextEditor::eEditorDisabledMask;
if (mSelCon)
{
mSelCon->SetCaretEnabled(PR_FALSE);
@ -2722,10 +2722,10 @@ nsGfxTextControlFrame2::AttributeChanged(nsIPresContext* aPresContext,
}
else
{ // unset disabled
flags &= ~(nsIHTMLEditor::eEditorDisabledMask);
flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
if (mSelCon)
{
if (! (flags & nsIHTMLEditor::eEditorReadonlyMask))
if (! (flags & nsIPlaintextEditor::eEditorReadonlyMask))
mSelCon->SetCaretEnabled(PR_TRUE);
mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
}
@ -3019,15 +3019,15 @@ nsGfxTextControlFrame2::SetTextControlFrameState(const nsAReadableString& aValue
if (NS_FAILED(rv)) return;
if (!domDoc) return;
mSelCon->SelectAll();
nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(mEditor);
nsCOMPtr<nsIPlaintextEditor> htmlEditor = do_QueryInterface(mEditor);
if (!htmlEditor) return;
// get the flags, remove readonly and disabled, set the value, restore flags
PRUint32 flags, savedFlags;
mEditor->GetFlags(&savedFlags);
flags = savedFlags;
flags &= ~(nsIHTMLEditor::eEditorDisabledMask);
flags &= ~(nsIHTMLEditor::eEditorReadonlyMask);
flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
mEditor->SetFlags(flags);
if (currentValue.Length() < 1)
mEditor->DeleteSelection(nsIEditor::eNone);