mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
bug 65557 and cast of thousands: making the editor behave more intelligently wrt html whitespace rules. Improved ws handling for:
This commit is contained in:
parent
d1a70597de
commit
56ac4a4d19
@ -91,6 +91,7 @@ CPPSRCS += nsAOLCiter.cpp \
|
||||
nsInternetCiter.cpp \
|
||||
nsTableEditor.cpp \
|
||||
nsWrapUtils.cpp \
|
||||
nsWSRunObject.cpp \
|
||||
TextEditorTest.cpp \
|
||||
TypeInState.cpp \
|
||||
SetDocTitleTxn.cpp \
|
||||
|
@ -126,6 +126,7 @@ CPPSRCS = $(CPPSRCS) \
|
||||
nsInternetCiter.cpp \
|
||||
nsTableEditor.cpp \
|
||||
nsWrapUtils.cpp \
|
||||
nsWSRunObject.cpp \
|
||||
TextEditorTest.cpp \
|
||||
TypeInState.cpp \
|
||||
SetDocTitleTxn.cpp \
|
||||
@ -146,6 +147,7 @@ CPP_OBJS = $(CPP_OBJS) \
|
||||
.\$(OBJDIR)\nsInternetCiter.obj \
|
||||
.\$(OBJDIR)\nsTableEditor.obj \
|
||||
.\$(OBJDIR)\nsWrapUtils.obj \
|
||||
.\$(OBJDIR)\nsWSRunObject.obj \
|
||||
.\$(OBJDIR)\TextEditorTest.obj \
|
||||
.\$(OBJDIR)\TypeInState.obj \
|
||||
.\$(OBJDIR)\SetDocTitleTxn.obj \
|
||||
|
@ -244,6 +244,8 @@ protected:
|
||||
PRUint32 aOffset,
|
||||
PRUint32 aLength);
|
||||
|
||||
// NS_IMETHOD DeleteRange(nsIDOMRange *aRange);
|
||||
|
||||
NS_IMETHOD CreateTxnForDeleteText(nsIDOMCharacterData *aElement,
|
||||
PRUint32 aOffset,
|
||||
PRUint32 aLength,
|
||||
|
@ -56,6 +56,7 @@
|
||||
#endif // IBMBIDI
|
||||
|
||||
#include "nsEditorUtils.h"
|
||||
#include "nsWSRunObject.h"
|
||||
|
||||
#include "InsertTextTxn.h"
|
||||
#include "DeleteTextTxn.h"
|
||||
@ -380,20 +381,11 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
|
||||
// adjust whitespace for insert text and delete actions
|
||||
if ((action == nsEditor::kOpInsertText) ||
|
||||
(action == nsEditor::kOpInsertIMEText) ||
|
||||
(action == nsEditor::kOpDeleteSelection))
|
||||
{
|
||||
res = AdjustWhitespace(selection);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
|
||||
// replace newlines that are preformatted
|
||||
// replace newlines with breaks.
|
||||
// MOOSE: This is buttUgly. A better way to
|
||||
// organize the action enum is in order.
|
||||
if ((action == nsEditor::kOpInsertText) ||
|
||||
(action == nsEditor::kOpInsertIMEText) ||
|
||||
if (// (action == nsEditor::kOpInsertText) ||
|
||||
// (action == nsEditor::kOpInsertIMEText) ||
|
||||
(action == nsHTMLEditor::kOpInsertElement) ||
|
||||
(action == nsHTMLEditor::kOpInsertQuotation) ||
|
||||
(action == nsEditor::kOpInsertNode))
|
||||
@ -404,6 +396,13 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection
|
||||
// clean up any empty nodes in the selection
|
||||
res = RemoveEmptyNodes();
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// attempt to transform any uneeded nbsp's into spaces after doing deletions
|
||||
if (action == nsEditor::kOpDeleteSelection)
|
||||
{
|
||||
res = AdjustWhitespace(selection);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
|
||||
// adjust selection for insert text, html paste, and delete actions
|
||||
if ((action == nsEditor::kOpInsertText) ||
|
||||
@ -941,6 +940,8 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction,
|
||||
nsCOMPtr<nsIDOMNode> selNode;
|
||||
PRInt32 selOffset;
|
||||
|
||||
PRBool bPlaintext = mFlags & nsIPlaintextEditor::eEditorPlaintextMask;
|
||||
|
||||
// if the selection isn't collapsed, delete it.
|
||||
PRBool bCollapsed;
|
||||
res = aSelection->GetIsCollapsed(&bCollapsed);
|
||||
@ -1006,7 +1007,7 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction,
|
||||
// for efficiency, break out the pre case seperately. This is because
|
||||
// its a lot cheaper to search the input string for only newlines than
|
||||
// it is to search for both tabs and newlines.
|
||||
if (isPRE)
|
||||
if (isPRE || bPlaintext)
|
||||
{
|
||||
char newlineChar = '\n';
|
||||
while (unicodeBuf && (pos != -1) && (pos < (PRInt32)(*inString).Length()))
|
||||
@ -1068,21 +1069,29 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction,
|
||||
|
||||
subStr.Subsume((PRUnichar*)&unicodeBuf[oldPos], PR_FALSE, subStrLen);
|
||||
|
||||
nsWSRunObject wsObj(mHTMLEditor, curNode, curOffset);
|
||||
|
||||
// is it a tab?
|
||||
if (subStr.EqualsWithConversion("\t"))
|
||||
{
|
||||
res = mHTMLEditor->InsertTextImpl(tabString, address_of(curNode), &curOffset, doc);
|
||||
// res = mHTMLEditor->InsertTextImpl(tabString, address_of(curNode), &curOffset, doc);
|
||||
res = wsObj.InsertText(tabString, address_of(curNode), &curOffset, doc);
|
||||
if (NS_FAILED(res)) return res;
|
||||
pos++;
|
||||
}
|
||||
// is it a return?
|
||||
else if (subStr.EqualsWithConversion("\n"))
|
||||
{
|
||||
res = mHTMLEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
|
||||
// res = mHTMLEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
|
||||
res = wsObj.InsertBreak(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
|
||||
if (NS_FAILED(res)) return res;
|
||||
pos++;
|
||||
}
|
||||
else
|
||||
{
|
||||
res = mHTMLEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc);
|
||||
// res = mHTMLEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc);
|
||||
res = wsObj.InsertText(subStr, address_of(curNode), &curOffset, doc);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
@ -1121,6 +1130,7 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
|
||||
*aCancel = PR_FALSE;
|
||||
*aHandled = PR_FALSE;
|
||||
|
||||
PRBool bPlaintext = mFlags & nsIPlaintextEditor::eEditorPlaintextMask;
|
||||
|
||||
// if the selection isn't collapsed, delete it.
|
||||
PRBool bCollapsed;
|
||||
@ -1147,7 +1157,6 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
|
||||
PRInt32 selOffset, newOffset;
|
||||
res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
PRBool bPlaintext = mFlags & nsIPlaintextEditor::eEditorPlaintextMask;
|
||||
res = GetTopEnclosingMailCite(selNode, address_of(citeNode), bPlaintext);
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (citeNode)
|
||||
@ -1212,7 +1221,15 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
|
||||
else
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> brNode;
|
||||
res = mHTMLEditor->CreateBR(node, offset, address_of(brNode));
|
||||
if (bPlaintext)
|
||||
{
|
||||
res = mHTMLEditor->CreateBR(node, offset, address_of(brNode));
|
||||
}
|
||||
else
|
||||
{
|
||||
nsWSRunObject wsObj(mHTMLEditor, node, offset);
|
||||
res = wsObj.InsertBreak(address_of(node), &offset, address_of(brNode), nsIEditor::eNone);
|
||||
}
|
||||
if (NS_FAILED(res)) return res;
|
||||
res = nsEditor::GetNodeLocation(brNode, address_of(node), &offset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
@ -1266,6 +1283,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
}
|
||||
|
||||
nsresult res = NS_OK;
|
||||
PRBool bPlaintext = mFlags & nsIPlaintextEditor::eEditorPlaintextMask;
|
||||
|
||||
PRBool bCollapsed;
|
||||
res = aSelection->GetIsCollapsed(&bCollapsed);
|
||||
@ -1289,9 +1307,81 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (!startNode) return NS_ERROR_FAILURE;
|
||||
|
||||
// get the root element
|
||||
nsCOMPtr<nsIDOMElement> bodyElement;
|
||||
nsCOMPtr<nsIDOMNode> bodyNode;
|
||||
res = mHTMLEditor->GetRootElement(getter_AddRefs(bodyElement));
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (!bodyElement) return NS_ERROR_UNEXPECTED;
|
||||
bodyNode = do_QueryInterface(bodyElement);
|
||||
|
||||
if (bCollapsed)
|
||||
{
|
||||
// easy case, in a text node:
|
||||
// if we are inside an empty block, delete it.
|
||||
// Note: do NOT delete table elements this way.
|
||||
nsCOMPtr<nsIDOMNode> block;
|
||||
if (IsBlockNode(startNode))
|
||||
block = startNode;
|
||||
else
|
||||
block = mHTMLEditor->GetBlockNodeParent(startNode);
|
||||
PRBool bIsEmptyNode;
|
||||
if (block != bodyNode)
|
||||
{
|
||||
res = mHTMLEditor->IsEmptyNode(block, &bIsEmptyNode, PR_TRUE, PR_FALSE);
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (bIsEmptyNode && !nsHTMLEditUtils::IsTableElement(startNode))
|
||||
{
|
||||
// adjust selection to be right after it
|
||||
nsCOMPtr<nsIDOMNode> blockParent;
|
||||
PRInt32 offset;
|
||||
res = nsEditor::GetNodeLocation(block, address_of(blockParent), &offset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (!blockParent || offset < 0) return NS_ERROR_FAILURE;
|
||||
res = aSelection->Collapse(blockParent, offset+1);
|
||||
if (NS_FAILED(res)) return res;
|
||||
res = mHTMLEditor->DeleteNode(block);
|
||||
*aHandled = PR_TRUE;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
if (!bPlaintext)
|
||||
{
|
||||
// gather up ws data here. We may be next to non-significant ws.
|
||||
nsWSRunObject wsObj(mHTMLEditor, startNode, startOffset);
|
||||
nsCOMPtr<nsIDOMNode> visNode;
|
||||
PRInt32 visOffset;
|
||||
PRInt16 wsType;
|
||||
if (aAction == nsIEditor::ePrevious)
|
||||
{
|
||||
res = wsObj.PriorVisibleNode(startNode, startOffset, address_of(visNode), &visOffset, &wsType);
|
||||
// note that visOffset is _after_ what we are about to delete.
|
||||
}
|
||||
else if (aAction == nsIEditor::eNext)
|
||||
{
|
||||
res = wsObj.NextVisibleNode(startNode, startOffset, address_of(visNode), &visOffset, &wsType);
|
||||
// note that visOffset is _before_ what we are about to delete.
|
||||
}
|
||||
if (NS_SUCCEEDED(res))
|
||||
{
|
||||
if (wsType==nsWSRunObject::eNormalWS)
|
||||
{
|
||||
// we found some visible ws to delete. Let ws code handle it.
|
||||
if (aAction == nsIEditor::ePrevious)
|
||||
res = wsObj.DeleteWSBackward();
|
||||
else if (aAction == nsIEditor::eNext)
|
||||
res = wsObj.DeleteWSForward();
|
||||
*aHandled = PR_TRUE;
|
||||
return res;
|
||||
}
|
||||
else if (visNode)
|
||||
{
|
||||
// reposition startNode and startOffset so that we skip over any non-significant ws
|
||||
startNode = visNode;
|
||||
startOffset = visOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
// in a text node:
|
||||
if (mHTMLEditor->IsTextNode(startNode))
|
||||
{
|
||||
nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(startNode);
|
||||
@ -1299,83 +1389,6 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
res = textNode->GetLength(&strLength);
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
#ifdef IBMBIDI // Test for distance between caret and text that will be deleted
|
||||
nsCOMPtr<nsIPresShell> shell;
|
||||
mEditor->GetPresShell(getter_AddRefs(shell));
|
||||
if (shell)
|
||||
{
|
||||
nsCOMPtr<nsIPresContext> context;
|
||||
shell->GetPresContext(getter_AddRefs(context));
|
||||
if (context)
|
||||
{
|
||||
PRBool bidiEnabled;
|
||||
context->BidiEnabled(bidiEnabled);
|
||||
if (bidiEnabled)
|
||||
{
|
||||
nsCOMPtr<nsIFrameSelection> frameSelection;
|
||||
shell->GetFrameSelection(getter_AddRefs(frameSelection));
|
||||
if (frameSelection)
|
||||
{
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(startNode);
|
||||
if (content)
|
||||
{
|
||||
nsIFrame *primaryFrame;
|
||||
nsIFrame *frameBefore;
|
||||
nsIFrame *frameAfter;
|
||||
PRInt32 frameOffset;
|
||||
|
||||
shell->GetPrimaryFrameFor(content, &primaryFrame);
|
||||
if (primaryFrame)
|
||||
{
|
||||
res = primaryFrame->GetChildFrameContainingOffset(startOffset, PR_FALSE, &frameOffset, &frameBefore);
|
||||
if (NS_SUCCEEDED(res) && frameBefore)
|
||||
{
|
||||
PRInt32 start, end;
|
||||
frameBefore->GetOffsets(start, end);
|
||||
if (startOffset == end)
|
||||
{
|
||||
res = primaryFrame->GetChildFrameContainingOffset(startOffset, PR_TRUE, &frameOffset, &frameAfter);
|
||||
if (NS_SUCCEEDED(res) && frameAfter)
|
||||
{
|
||||
PRUint8 currentCursorLevel;
|
||||
long levelBefore;
|
||||
long levelAfter;
|
||||
long paragraphLevel;
|
||||
long levelOfDeletion;
|
||||
nsCOMPtr<nsIAtom> embeddingLevel = NS_NewAtom("EmbeddingLevel");
|
||||
nsCOMPtr<nsIAtom> baseLevel = NS_NewAtom("BaseLevel");
|
||||
frameBefore->GetBidiProperty(context, embeddingLevel, (void**)&levelBefore);
|
||||
if (frameBefore==frameAfter)
|
||||
{
|
||||
frameBefore->GetBidiProperty(context, baseLevel, (void**)¶graphLevel);
|
||||
levelAfter = paragraphLevel;
|
||||
}
|
||||
else
|
||||
frameAfter->GetBidiProperty(context, embeddingLevel, (void**)&levelAfter);
|
||||
shell->GetCursorBidiLevel(¤tCursorLevel);
|
||||
if (levelBefore==levelAfter && (levelAfter & 1) == (currentCursorLevel & 1))
|
||||
shell->SetCursorBidiLevel(levelBefore);
|
||||
else
|
||||
{
|
||||
levelOfDeletion = (nsIEditor::eNext==aAction) ? levelAfter : levelBefore;
|
||||
shell->SetCursorBidiLevel(levelOfDeletion);
|
||||
if ((currentCursorLevel/* & 1*/) != (levelOfDeletion/* & 1*/))
|
||||
{
|
||||
*aCancel = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // IBMBIDI
|
||||
|
||||
// at beginning of text node and backspaced?
|
||||
if (!startOffset && (aAction == nsIEditor::ePrevious))
|
||||
{
|
||||
@ -1384,7 +1397,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// if there is no prior node then cancel the deletion
|
||||
if (!priorNode)
|
||||
if (!priorNode || !nsTextEditUtils::InBody(priorNode, mHTMLEditor))
|
||||
{
|
||||
*aCancel = PR_TRUE;
|
||||
return res;
|
||||
@ -1401,7 +1414,17 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
nsCOMPtr<nsIDOMCharacterData>nodeAsText;
|
||||
nodeAsText = do_QueryInterface(priorNode);
|
||||
nodeAsText->GetLength((PRUint32*)&offset);
|
||||
NS_ENSURE_TRUE(offset, NS_ERROR_FAILURE);
|
||||
res = aSelection->Collapse(priorNode,offset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (!bPlaintext)
|
||||
{
|
||||
PRInt32 so = offset-1;
|
||||
PRInt32 eo = offset;
|
||||
res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor,
|
||||
address_of(priorNode), &so,
|
||||
address_of(priorNode), &eo);
|
||||
}
|
||||
// just return without setting handled to true.
|
||||
// default code will take care of actual deletion
|
||||
return res;
|
||||
@ -1409,6 +1432,11 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
// is prior node not a container? (ie, a br, hr, image...)
|
||||
else if (!mHTMLEditor->IsContainer(priorNode)) // MOOSE: anchors not handled
|
||||
{
|
||||
if (!bPlaintext)
|
||||
{
|
||||
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, priorNode);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
// delete the break, and join like nodes if appropriate
|
||||
res = mHTMLEditor->DeleteNode(priorNode);
|
||||
if (NS_FAILED(res)) return res;
|
||||
@ -1424,8 +1452,6 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
if (mHTMLEditor->IsTextNode(priorNode))
|
||||
{
|
||||
// if so, join them!
|
||||
nsCOMPtr<nsIDOMNode> topParent;
|
||||
priorNode->GetParentNode(getter_AddRefs(topParent));
|
||||
res = JoinNodesSmart(priorNode,startNode,address_of(selNode),&selOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// fix up selection
|
||||
@ -1436,6 +1462,11 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
}
|
||||
else if ( IsInlineNode(priorNode) )
|
||||
{
|
||||
if (!bPlaintext)
|
||||
{
|
||||
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, priorNode);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
// remember where we are
|
||||
PRInt32 offset;
|
||||
nsCOMPtr<nsIDOMNode> node;
|
||||
@ -1454,8 +1485,16 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
}
|
||||
|
||||
// deleting across blocks
|
||||
nsCOMPtr<nsIDOMNode> leftParent = mHTMLEditor->GetBlockNodeParent(priorNode);
|
||||
nsCOMPtr<nsIDOMNode> rightParent = mHTMLEditor->GetBlockNodeParent(startNode);
|
||||
nsCOMPtr<nsIDOMNode> leftParent;
|
||||
nsCOMPtr<nsIDOMNode> rightParent;
|
||||
if (IsBlockNode(priorNode))
|
||||
leftParent = priorNode;
|
||||
else
|
||||
leftParent = mHTMLEditor->GetBlockNodeParent(priorNode);
|
||||
if (IsBlockNode(startNode))
|
||||
rightParent = startNode;
|
||||
else
|
||||
rightParent = mHTMLEditor->GetBlockNodeParent(startNode);
|
||||
|
||||
// if leftParent or rightParent is null, it's because the
|
||||
// corresponding selection endpoint is in the body node.
|
||||
@ -1472,9 +1511,13 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
// are the blocks of same type?
|
||||
if (mHTMLEditor->NodesSameType(leftParent, rightParent))
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> topParent;
|
||||
leftParent->GetParentNode(getter_AddRefs(topParent));
|
||||
|
||||
if (!bPlaintext)
|
||||
{
|
||||
// adjust whitespace at block boundaries
|
||||
res = nsWSRunObject::PrepareToJoinBlocks(mHTMLEditor,leftParent,rightParent);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
// join the nodes
|
||||
*aHandled = PR_TRUE;
|
||||
res = JoinNodesSmart(leftParent,rightParent,address_of(selNode),&selOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
@ -1513,6 +1556,15 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
nsCOMPtr<nsIDOMCharacterData>nodeAsText;
|
||||
nodeAsText = do_QueryInterface(nextNode);
|
||||
res = aSelection->Collapse(nextNode,0);
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (!bPlaintext)
|
||||
{
|
||||
PRInt32 so = 0;
|
||||
PRInt32 eo = 1;
|
||||
res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor,
|
||||
address_of(nextNode), &so,
|
||||
address_of(nextNode), &eo);
|
||||
}
|
||||
// just return without setting handled to true.
|
||||
// default code will take care of actual deletion
|
||||
return res;
|
||||
@ -1520,6 +1572,11 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
// is next node not a container? (ie, a br, hr, image...)
|
||||
else if (!mHTMLEditor->IsContainer(nextNode)) // MOOSE: anchors not handled
|
||||
{
|
||||
if (!bPlaintext)
|
||||
{
|
||||
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, nextNode);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
// delete the break, and join like nodes if appropriate
|
||||
res = mHTMLEditor->DeleteNode(nextNode);
|
||||
if (NS_FAILED(res)) return res;
|
||||
@ -1535,8 +1592,6 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
if ( mHTMLEditor->IsTextNode(nextNode) )
|
||||
{
|
||||
// if so, join them!
|
||||
nsCOMPtr<nsIDOMNode> topParent;
|
||||
nextNode->GetParentNode(getter_AddRefs(topParent));
|
||||
res = JoinNodesSmart(startNode,nextNode,address_of(selNode),&selOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// fix up selection
|
||||
@ -1547,9 +1602,14 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
}
|
||||
else if ( IsInlineNode(nextNode) )
|
||||
{
|
||||
if (!bPlaintext)
|
||||
{
|
||||
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, nextNode);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
// remember where we are
|
||||
PRInt32 offset;
|
||||
nsCOMPtr<nsIDOMNode> node;
|
||||
// remember where we are
|
||||
res = mHTMLEditor->GetNodeLocation(nextNode, address_of(node), &offset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// delete it
|
||||
@ -1565,9 +1625,17 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
}
|
||||
|
||||
// deleting across blocks
|
||||
nsCOMPtr<nsIDOMNode> leftParent = mHTMLEditor->GetBlockNodeParent(startNode);
|
||||
nsCOMPtr<nsIDOMNode> rightParent = mHTMLEditor->GetBlockNodeParent(nextNode);
|
||||
|
||||
nsCOMPtr<nsIDOMNode> leftParent;
|
||||
nsCOMPtr<nsIDOMNode> rightParent;
|
||||
if (IsBlockNode(startNode))
|
||||
leftParent = startNode;
|
||||
else
|
||||
leftParent = mHTMLEditor->GetBlockNodeParent(startNode);
|
||||
if (IsBlockNode(nextNode))
|
||||
rightParent = nextNode;
|
||||
else
|
||||
rightParent = mHTMLEditor->GetBlockNodeParent(nextNode);
|
||||
|
||||
// if leftParent or rightParent is null, it's because the
|
||||
// corresponding selection endpoint is in the body node.
|
||||
if (!leftParent || !rightParent)
|
||||
@ -1583,9 +1651,13 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
// are the blocks of same type?
|
||||
if (mHTMLEditor->NodesSameType(leftParent, rightParent))
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> topParent;
|
||||
leftParent->GetParentNode(getter_AddRefs(topParent));
|
||||
|
||||
if (!bPlaintext)
|
||||
{
|
||||
// adjust whitespace at block boundaries
|
||||
res = nsWSRunObject::PrepareToJoinBlocks(mHTMLEditor,leftParent,rightParent);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
// join the nodes
|
||||
*aHandled = PR_TRUE;
|
||||
res = JoinNodesSmart(leftParent,rightParent,address_of(selNode),&selOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
@ -1596,8 +1668,28 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
|
||||
// else blocks not same type, bail to default
|
||||
return NS_OK;
|
||||
|
||||
}
|
||||
// else in middle of text node. default will do right thing.
|
||||
nsCOMPtr<nsIDOMCharacterData>nodeAsText;
|
||||
nodeAsText = do_QueryInterface(startNode);
|
||||
if (!nodeAsText) return NS_ERROR_NULL_POINTER;
|
||||
res = aSelection->Collapse(startNode,startOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (!bPlaintext)
|
||||
{
|
||||
PRInt32 so = startOffset;
|
||||
PRInt32 eo = startOffset;
|
||||
if (aAction == nsIEditor::ePrevious)
|
||||
so--; // we know so not zero - that case handled above
|
||||
else
|
||||
eo++; // we know eo not at end of text node - that case handled above
|
||||
res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor,
|
||||
address_of(startNode), &so,
|
||||
address_of(startNode), &eo);
|
||||
}
|
||||
// just return without setting handled to true.
|
||||
// default code will take care of actual deletion
|
||||
return res;
|
||||
}
|
||||
// else not in text node; we need to find right place to act on
|
||||
else
|
||||
@ -1658,7 +1750,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
*aCancel = PR_TRUE;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// if this node is text node, adjust selection
|
||||
if (nsEditor::IsTextNode(nodeToDelete))
|
||||
{
|
||||
@ -1674,10 +1766,6 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
{
|
||||
// editable leaf node is not text; delete it.
|
||||
// that's the default behavior
|
||||
PRInt32 offset;
|
||||
nsCOMPtr<nsIDOMNode> node;
|
||||
res = nsEditor::GetNodeLocation(nodeToDelete, address_of(node), &offset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// EXCEPTION: if it's a mozBR, we have to check and see if
|
||||
// there is a br in front of it. If so, we must delete both.
|
||||
// else you get this: deletion code deletes mozBR, then selection
|
||||
@ -1695,16 +1783,28 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
if (block == brBlock)
|
||||
{
|
||||
// delete both breaks
|
||||
if (!bPlaintext)
|
||||
{
|
||||
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, brNode);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
res = mHTMLEditor->DeleteNode(brNode);
|
||||
if (NS_FAILED(res)) return res;
|
||||
res = mHTMLEditor->DeleteNode(nodeToDelete);
|
||||
*aHandled = PR_TRUE;
|
||||
return res;
|
||||
// fall through to delete other br
|
||||
}
|
||||
// else fall through
|
||||
}
|
||||
// else fall through
|
||||
}
|
||||
if (!bPlaintext)
|
||||
{
|
||||
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, nodeToDelete);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
PRInt32 offset;
|
||||
nsCOMPtr<nsIDOMNode> node;
|
||||
res = nsEditor::GetNodeLocation(nodeToDelete, address_of(node), &offset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// adjust selection to be right after it
|
||||
res = aSelection->Collapse(node, offset+1);
|
||||
if (NS_FAILED(res)) return res;
|
||||
@ -1722,9 +1822,15 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
nsCOMPtr<nsIDOMNode> endNode;
|
||||
PRInt32 endOffset;
|
||||
res = mHTMLEditor->GetEndNodeAndOffset(aSelection, address_of(endNode), &endOffset);
|
||||
if (NS_FAILED(res))
|
||||
{
|
||||
return res;
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// adjust surrounding whitespace in preperation to delete selection
|
||||
if (!bPlaintext)
|
||||
{
|
||||
res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor,
|
||||
address_of(startNode), &startOffset,
|
||||
address_of(endNode), &endOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
if (endNode.get() != startNode.get())
|
||||
{
|
||||
@ -1735,20 +1841,11 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
// are the blocks of same type?
|
||||
nsCOMPtr<nsIDOMNode> leftParent;
|
||||
nsCOMPtr<nsIDOMNode> rightParent;
|
||||
|
||||
// XXX: Fix for bug #10815: Crash deleting selected text and table.
|
||||
// Make sure leftParent and rightParent are never NULL. This
|
||||
// can happen if we call GetBlockNodeParent() and the node we
|
||||
// pass in is a body node.
|
||||
//
|
||||
// Should we be calling IsBlockNode() instead of IsBody() here?
|
||||
|
||||
if (nsTextEditUtils::IsBody(startNode))
|
||||
if (IsBlockNode(startNode))
|
||||
leftParent = startNode;
|
||||
else
|
||||
leftParent = mHTMLEditor->GetBlockNodeParent(startNode);
|
||||
|
||||
if (nsTextEditUtils::IsBody(endNode))
|
||||
if (IsBlockNode(endNode))
|
||||
rightParent = endNode;
|
||||
else
|
||||
rightParent = mHTMLEditor->GetBlockNodeParent(endNode);
|
||||
@ -1763,9 +1860,6 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
if ( (leftBlockParent.get() == rightBlockParent.get())
|
||||
&& (mHTMLEditor->NodesSameType(leftParent, rightParent)) )
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> topParent;
|
||||
leftParent->GetParentNode(getter_AddRefs(topParent));
|
||||
|
||||
if (nsHTMLEditUtils::IsParagraph(leftParent))
|
||||
{
|
||||
// first delete the selection
|
||||
@ -2780,25 +2874,20 @@ nsHTMLEditRules::CreateStyleForInsertText(nsISelection *aSelection, nsIDOMDocume
|
||||
res = mEditor->DeleteNode(rightNode);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
// register a rangeStore item that points at the new heirarchy.
|
||||
// This is so we can know where to put the selection after we call
|
||||
// RemoveStyleInside(). RemoveStyleInside() could remove any and all of those nodes,
|
||||
// so I have to use the range tracking system to find the right spot to put selection.
|
||||
nsRangeStore *rangeItem = new nsRangeStore();
|
||||
if (!rangeItem) return NS_ERROR_NULL_POINTER;
|
||||
rangeItem->startNode = newSelParent;
|
||||
rangeItem->endNode = newSelParent;
|
||||
rangeItem->startOffset = 0;
|
||||
rangeItem->endOffset = 0;
|
||||
mHTMLEditor->mRangeUpdater.RegisterRangeItem(rangeItem);
|
||||
// remove the style on this new heirarchy
|
||||
res = mHTMLEditor->RemoveStyleInside(leftNode, item->tag, &(item->attr));
|
||||
if (NS_FAILED(res)) return res;
|
||||
PRInt32 newSelOffset = 0;
|
||||
{
|
||||
// track the point at the new heirarchy.
|
||||
// This is so we can know where to put the selection after we call
|
||||
// RemoveStyleInside(). RemoveStyleInside() could remove any and all of those nodes,
|
||||
// so I have to use the range tracking system to find the right spot to put selection.
|
||||
nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(newSelParent), &newSelOffset);
|
||||
res = mHTMLEditor->RemoveStyleInside(leftNode, item->tag, &(item->attr));
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
// reset our node offset values to the resulting new sel point
|
||||
mHTMLEditor->mRangeUpdater.DropRangeItem(rangeItem);
|
||||
node = rangeItem->startNode;
|
||||
offset = rangeItem->startOffset;
|
||||
delete rangeItem;
|
||||
node = newSelParent;
|
||||
offset = newSelOffset;
|
||||
}
|
||||
// we own item now (TakeClearProperty hands ownership to us)
|
||||
delete item;
|
||||
@ -4210,9 +4299,14 @@ nsHTMLEditRules::ReturnInHeader(nsISelection *aSelection,
|
||||
nsresult res = nsEditor::GetNodeLocation(aHeader, address_of(headerParent), &offset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// get ws code to adjust any ws
|
||||
nsCOMPtr<nsIDOMNode> selNode = aNode;
|
||||
res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, address_of(selNode), &aOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// split the header
|
||||
PRInt32 newOffset;
|
||||
res = mHTMLEditor->SplitNodeDeep( aHeader, aNode, aOffset, &newOffset);
|
||||
res = mHTMLEditor->SplitNodeDeep( aHeader, selNode, aOffset, &newOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// if the leftand heading is empty, put a mozbr in it
|
||||
@ -4306,8 +4400,12 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
|
||||
{
|
||||
PRInt32 newOffset;
|
||||
*aCancel = PR_TRUE;
|
||||
// get ws code to adjust any ws
|
||||
nsCOMPtr<nsIDOMNode> selNode = aNode;
|
||||
res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, address_of(selNode), &aOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// split the paragraph
|
||||
res = mHTMLEditor->SplitNodeDeep( aPara, aNode, aOffset, &newOffset);
|
||||
res = mHTMLEditor->SplitNodeDeep( aPara, selNode, aOffset, &newOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// get rid of the break
|
||||
res = mHTMLEditor->DeleteNode(sibling);
|
||||
@ -4344,8 +4442,12 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
|
||||
{
|
||||
PRInt32 newOffset;
|
||||
*aCancel = PR_TRUE;
|
||||
// get ws code to adjust any ws
|
||||
nsCOMPtr<nsIDOMNode> selNode = aNode;
|
||||
res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, address_of(selNode), &aOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// split the paragraph
|
||||
res = mHTMLEditor->SplitNodeDeep(aPara, aNode, aOffset, &newOffset);
|
||||
res = mHTMLEditor->SplitNodeDeep(aPara, selNode, aOffset, &newOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// get rid of the break
|
||||
res = mHTMLEditor->DeleteNode(sibling);
|
||||
@ -4383,8 +4485,12 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
|
||||
// else remove sibling br and split para
|
||||
PRInt32 newOffset;
|
||||
*aCancel = PR_TRUE;
|
||||
// get ws code to adjust any ws
|
||||
nsCOMPtr<nsIDOMNode> selNode = aNode;
|
||||
res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, address_of(selNode), &aOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// split the paragraph
|
||||
res = mHTMLEditor->SplitNodeDeep( aPara, aNode, aOffset, &newOffset);
|
||||
res = mHTMLEditor->SplitNodeDeep( aPara, selNode, aOffset, &newOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// get rid of the break
|
||||
res = mHTMLEditor->DeleteNode(nearNode);
|
||||
@ -4468,9 +4574,14 @@ nsHTMLEditRules::ReturnInListItem(nsISelection *aSelection,
|
||||
return res;
|
||||
}
|
||||
|
||||
// else we want a new list item at the same list level
|
||||
// else we want a new list item at the same list level.
|
||||
// get ws code to adjust any ws
|
||||
nsCOMPtr<nsIDOMNode> selNode = aNode;
|
||||
res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, address_of(selNode), &aOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// now split list item
|
||||
PRInt32 newOffset;
|
||||
res = mHTMLEditor->SplitNodeDeep( aListItem, aNode, aOffset, &newOffset);
|
||||
res = mHTMLEditor->SplitNodeDeep( aListItem, selNode, aOffset, &newOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// hack: until I can change the damaged doc range code back to being
|
||||
// extra inclusive, I have to manually detect certain list items that
|
||||
@ -4944,64 +5055,19 @@ nsHTMLEditRules::AdjustSpecialBreaks(PRBool aSafeToAskFrames)
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsHTMLEditRules::AdjustWhitespace(nsISelection *aSelection)
|
||||
{
|
||||
nsCOMPtr<nsISupportsArray> arrayOfNodes;
|
||||
nsCOMPtr<nsISupports> isupports;
|
||||
PRUint32 nodeCount,j;
|
||||
nsresult res;
|
||||
|
||||
nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
|
||||
// get selection point
|
||||
nsCOMPtr<nsIDOMNode> selNode;
|
||||
PRInt32 selOffset;
|
||||
nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// special case for mDocChangeRange entirely in one text node.
|
||||
// This is an efficiency hack for normal typing in the editor.
|
||||
nsCOMPtr<nsIDOMNode> startNode, endNode;
|
||||
PRInt32 startOffset, endOffset;
|
||||
res = mDocChangeRange->GetStartContainer(getter_AddRefs(startNode));
|
||||
if (NS_FAILED(res)) return res;
|
||||
res = mDocChangeRange->GetStartOffset(&startOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
res = mDocChangeRange->GetEndContainer(getter_AddRefs(endNode));
|
||||
if (NS_FAILED(res)) return res;
|
||||
res = mDocChangeRange->GetEndOffset(&endOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
if (startNode == endNode)
|
||||
{
|
||||
nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
|
||||
if (nodeAsText)
|
||||
{
|
||||
res = DoTextNodeWhitespace(nodeAsText, startOffset, endOffset);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// gather up a list of text nodes
|
||||
nsEditableTextFunctor functor(mHTMLEditor);
|
||||
nsDOMIterator iter;
|
||||
res = iter.Init(mDocChangeRange);
|
||||
if (NS_FAILED(res)) return res;
|
||||
res = iter.MakeList(functor, address_of(arrayOfNodes));
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// now adjust whitespace on node we found
|
||||
res = arrayOfNodes->Count(&nodeCount);
|
||||
if (NS_FAILED(res)) return res;
|
||||
for (j = 0; j < nodeCount; j++)
|
||||
{
|
||||
isupports = dont_AddRef(arrayOfNodes->ElementAt(0));
|
||||
nsCOMPtr<nsIDOMCharacterData> textNode( do_QueryInterface(isupports ) );
|
||||
arrayOfNodes->RemoveElementAt(0);
|
||||
res = DoTextNodeWhitespace(textNode, -1, -1);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
|
||||
return res;
|
||||
// ask whitespace object to tweak nbsp's
|
||||
return nsWSRunObject(mHTMLEditor, selNode, selOffset).AdjustWhitespace();
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsHTMLEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection aAction)
|
||||
{
|
||||
@ -5103,6 +5169,12 @@ nsHTMLEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection
|
||||
res = aSelection->Collapse(selNode,selOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
else if (nextNode && nsTextEditUtils::IsMozBR(nextNode))
|
||||
{
|
||||
// selection between br and mozbr. make it stick to mozbr
|
||||
// so that it will be on blank line.
|
||||
selPriv->SetInterlinePosition(PR_TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5892,6 +5964,20 @@ nsHTMLEditRules::DidDeleteText(nsIDOMCharacterData *aTextNode,
|
||||
return res;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLEditRules::WillDeleteRange(nsIDOMRange *aRange)
|
||||
{
|
||||
if (!mListenerEnabled) return NS_OK;
|
||||
// get the (collapsed) selection location
|
||||
return UpdateDocChangeRange(aRange);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLEditRules::DidDeleteRange(nsIDOMRange *aRange)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection)
|
||||
{
|
||||
|
@ -77,6 +77,8 @@ public:
|
||||
NS_IMETHOD DidInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsAReadableString &aString, nsresult aResult);
|
||||
NS_IMETHOD WillDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength);
|
||||
NS_IMETHOD DidDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength, nsresult aResult);
|
||||
NS_IMETHOD WillDeleteRange(nsIDOMRange *aRange);
|
||||
NS_IMETHOD DidDeleteRange(nsIDOMRange *aRange);
|
||||
NS_IMETHOD WillDeleteSelection(nsISelection *aSelection);
|
||||
NS_IMETHOD DidDeleteSelection(nsISelection *aSelection);
|
||||
|
||||
@ -179,13 +181,13 @@ protected:
|
||||
|
||||
// data members
|
||||
protected:
|
||||
nsHTMLEditor *mHTMLEditor;
|
||||
nsCOMPtr<nsIDOMRange> mDocChangeRange;
|
||||
PRBool mListenerEnabled;
|
||||
PRBool mReturnInEmptyLIKillsList;
|
||||
nsCOMPtr<nsIDOMRange> mUtilRange;
|
||||
PRUint32 mJoinOffset; // need to remember an int across willJoin/didJoin...
|
||||
|
||||
nsHTMLEditor *mHTMLEditor;
|
||||
nsCOMPtr<nsIDOMRange> mDocChangeRange;
|
||||
PRBool mListenerEnabled;
|
||||
PRBool mReturnInEmptyLIKillsList;
|
||||
nsCOMPtr<nsIDOMRange> mUtilRange;
|
||||
PRUint32 mJoinOffset; // need to remember an int across willJoin/didJoin...
|
||||
|
||||
};
|
||||
|
||||
nsresult NS_NewHTMLEditRules(nsIEditRules** aInstancePtrResult);
|
||||
|
@ -64,6 +64,7 @@
|
||||
#include "nsIContentIterator.h"
|
||||
#include "nsEditorCID.h"
|
||||
#include "nsLayoutCID.h"
|
||||
#include "nsContentCID.h"
|
||||
#include "nsIDOMRange.h"
|
||||
#include "nsIDOMNSRange.h"
|
||||
#include "nsISupportsArray.h"
|
||||
@ -114,6 +115,7 @@ 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_IID(kRangeUtilsCID, NS_RANGEUTILS_CID);
|
||||
static NS_DEFINE_CID(kCDOMSelectionCID, NS_DOMSELECTION_CID);
|
||||
static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID);
|
||||
static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
|
||||
@ -232,6 +234,10 @@ NS_IMETHODIMP nsHTMLEditor::Init(nsIDOMDocument *aDoc,
|
||||
|
||||
nsresult result = NS_OK, rulesRes = NS_OK;
|
||||
|
||||
// make a range util object for comparing dom points
|
||||
mRangeHelper = do_CreateInstance(kRangeUtilsCID);
|
||||
if (!mRangeHelper) return NS_ERROR_NULL_POINTER;
|
||||
|
||||
// Init mEditProperty
|
||||
result = NS_NewEditProperty(getter_AddRefs(mEditProperty));
|
||||
if (NS_FAILED(result)) { return result; }
|
||||
@ -1267,7 +1273,10 @@ NS_IMETHODIMP nsHTMLEditor::TabInTable(PRBool inIsShift, PRBool *outHandled)
|
||||
return res;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsHTMLEditor::CreateBRImpl(nsCOMPtr<nsIDOMNode> *aInOutParent, PRInt32 *aInOutOffset, nsCOMPtr<nsIDOMNode> *outBRNode, EDirection aSelect)
|
||||
NS_IMETHODIMP nsHTMLEditor::CreateBRImpl(nsCOMPtr<nsIDOMNode> *aInOutParent,
|
||||
PRInt32 *aInOutOffset,
|
||||
nsCOMPtr<nsIDOMNode> *outBRNode,
|
||||
EDirection aSelect)
|
||||
{
|
||||
if (!aInOutParent || !*aInOutParent || !aInOutOffset || !outBRNode) return NS_ERROR_NULL_POINTER;
|
||||
*outBRNode = nsnull;
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "nsICSSLoader.h"
|
||||
#include "nsICSSLoaderObserver.h"
|
||||
#include "nsITableLayout.h"
|
||||
#include "nsIRangeUtils.h"
|
||||
|
||||
#include "nsEditRules.h"
|
||||
|
||||
@ -679,11 +680,14 @@ protected:
|
||||
// Used by GetFirstSelectedCell and GetNextSelectedCell
|
||||
PRInt32 mSelectedCellIndex;
|
||||
|
||||
nsCOMPtr<nsIRangeUtils> mRangeHelper;
|
||||
|
||||
public:
|
||||
|
||||
// friends
|
||||
friend class nsHTMLEditRules;
|
||||
friend class nsTextEditRules;
|
||||
friend class nsWSRunObject;
|
||||
|
||||
};
|
||||
|
||||
|
@ -118,6 +118,41 @@ class nsRangeUpdater
|
||||
};
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* helper class for using nsSelectionState. stack based class for doing
|
||||
* preservation of dom points across editor actions
|
||||
*/
|
||||
|
||||
class nsAutoTrackDOMPoint
|
||||
{
|
||||
private:
|
||||
nsRangeUpdater &mRU;
|
||||
nsCOMPtr<nsIDOMNode> *mNode;
|
||||
PRInt32 *mOffset;
|
||||
nsRangeStore mRangeItem;
|
||||
public:
|
||||
nsAutoTrackDOMPoint(nsRangeUpdater &aRangeUpdater, nsCOMPtr<nsIDOMNode> *aNode, PRInt32 *aOffset) :
|
||||
mRU(aRangeUpdater)
|
||||
,mNode(aNode)
|
||||
,mOffset(aOffset)
|
||||
{
|
||||
mRangeItem.startNode = *mNode;
|
||||
mRangeItem.endNode = *mNode;
|
||||
mRangeItem.startOffset = *mOffset;
|
||||
mRangeItem.endOffset = *mOffset;
|
||||
mRU.RegisterRangeItem(&mRangeItem);
|
||||
}
|
||||
|
||||
~nsAutoTrackDOMPoint()
|
||||
{
|
||||
mRU.DropRangeItem(&mRangeItem);
|
||||
*mNode = mRangeItem.startNode;
|
||||
*mOffset = mRangeItem.startOffset;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* another helper class for nsSelectionState. stack based class for doing
|
||||
* Will/DidReplaceContainer()
|
||||
|
@ -119,7 +119,6 @@ nsWSRunObject::PrepareToJoinBlocks(nsHTMLEditor *aHTMLEd,
|
||||
if (!aLeftParent || !aRightParent || !aHTMLEd)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
nsresult res = NS_OK;
|
||||
|
||||
PRUint32 count;
|
||||
aHTMLEd->GetLengthOfDOMNode(aLeftParent, count);
|
||||
nsWSRunObject leftWSObj(aHTMLEd, aLeftParent, count);
|
||||
@ -168,9 +167,19 @@ nsWSRunObject::PrepareToDeleteNode(nsHTMLEditor *aHTMLEd,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsWSRunObject::PrepareToSplitAcrossBlocks(nsCOMPtr<nsIDOMNode> *aSplitNode, PRInt32 *aSplitOffset)
|
||||
nsWSRunObject::PrepareToSplitAcrossBlocks(nsHTMLEditor *aHTMLEd,
|
||||
nsCOMPtr<nsIDOMNode> *aSplitNode,
|
||||
PRInt32 *aSplitOffset)
|
||||
{
|
||||
return NS_OK;
|
||||
if (!aSplitNode || !aSplitOffset || !*aSplitNode || !aHTMLEd)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
nsresult res = NS_OK;
|
||||
|
||||
nsAutoTrackDOMPoint tracker(aHTMLEd->mRangeUpdater, aSplitNode, aSplitOffset);
|
||||
|
||||
nsWSRunObject wsObj(aHTMLEd, *aSplitNode, *aSplitOffset);
|
||||
|
||||
return wsObj.PrepareToSplitAcrossBlocksPriv();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
@ -193,29 +202,6 @@ nsWSRunObject::InsertBreak(nsCOMPtr<nsIDOMNode> *aInOutParent,
|
||||
res = FindRun(*aInOutParent, *aInOutOffset, &beforeRun, PR_FALSE);
|
||||
res = FindRun(*aInOutParent, *aInOutOffset, &afterRun, PR_TRUE);
|
||||
|
||||
// handle any changes needed to ws run before inserted br
|
||||
if (!beforeRun)
|
||||
{
|
||||
// dont need to do anything. just insert break. ws wont change.
|
||||
}
|
||||
else if (beforeRun->mType & eLeadingWS)
|
||||
{
|
||||
// dont need to do anything. just insert break. ws wont change.
|
||||
}
|
||||
else if (beforeRun->mType == eTrailingWS)
|
||||
{
|
||||
// need to delete the trailing ws that is before insertion point, because it
|
||||
// would become significant after break inserted.
|
||||
res = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset, *aInOutParent, *aInOutOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
else if (beforeRun->mType == eNormalWS)
|
||||
{
|
||||
// try to change an nbsp to a space, if possible, just to prevent nbsp proliferation
|
||||
res = CheckTrailingNBSP(beforeRun, *aInOutParent, *aInOutOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
|
||||
// handle any changes needed to ws run after inserted br
|
||||
if (!afterRun)
|
||||
{
|
||||
@ -252,6 +238,29 @@ nsWSRunObject::InsertBreak(nsCOMPtr<nsIDOMNode> *aInOutParent,
|
||||
}
|
||||
}
|
||||
|
||||
// handle any changes needed to ws run before inserted br
|
||||
if (!beforeRun)
|
||||
{
|
||||
// dont need to do anything. just insert break. ws wont change.
|
||||
}
|
||||
else if (beforeRun->mType & eLeadingWS)
|
||||
{
|
||||
// dont need to do anything. just insert break. ws wont change.
|
||||
}
|
||||
else if (beforeRun->mType == eTrailingWS)
|
||||
{
|
||||
// need to delete the trailing ws that is before insertion point, because it
|
||||
// would become significant after break inserted.
|
||||
res = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset, *aInOutParent, *aInOutOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
else if (beforeRun->mType == eNormalWS)
|
||||
{
|
||||
// try to change an nbsp to a space, if possible, just to prevent nbsp proliferation
|
||||
res = CheckTrailingNBSP(beforeRun, *aInOutParent, *aInOutOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
|
||||
// ready, aim, fire!
|
||||
return mHTMLEditor->CreateBRImpl(aInOutParent, aInOutOffset, outBRNode, aSelect);
|
||||
}
|
||||
@ -282,29 +291,6 @@ nsWSRunObject::InsertText(const nsAReadableString& aStringToInsert,
|
||||
res = FindRun(*aInOutParent, *aInOutOffset, &beforeRun, PR_FALSE);
|
||||
res = FindRun(*aInOutParent, *aInOutOffset, &afterRun, PR_TRUE);
|
||||
|
||||
// handle any changes needed to ws run before inserted text
|
||||
if (!beforeRun)
|
||||
{
|
||||
// dont need to do anything. just insert text. ws wont change.
|
||||
}
|
||||
else if (beforeRun->mType & eLeadingWS)
|
||||
{
|
||||
// dont need to do anything. just insert text. ws wont change.
|
||||
}
|
||||
else if (beforeRun->mType == eTrailingWS)
|
||||
{
|
||||
// need to delete the trailing ws that is before insertion point, because it
|
||||
// would become significant after text inserted.
|
||||
res = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset, *aInOutParent, *aInOutOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
else if (beforeRun->mType == eNormalWS)
|
||||
{
|
||||
// try to change an nbsp to a space, if possible, just to prevent nbsp proliferation
|
||||
res = CheckTrailingNBSP(beforeRun, *aInOutParent, *aInOutOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
|
||||
// handle any changes needed to ws run after inserted text
|
||||
if (!afterRun)
|
||||
{
|
||||
@ -328,6 +314,29 @@ nsWSRunObject::InsertText(const nsAReadableString& aStringToInsert,
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
|
||||
// handle any changes needed to ws run before inserted text
|
||||
if (!beforeRun)
|
||||
{
|
||||
// dont need to do anything. just insert text. ws wont change.
|
||||
}
|
||||
else if (beforeRun->mType & eLeadingWS)
|
||||
{
|
||||
// dont need to do anything. just insert text. ws wont change.
|
||||
}
|
||||
else if (beforeRun->mType == eTrailingWS)
|
||||
{
|
||||
// need to delete the trailing ws that is before insertion point, because it
|
||||
// would become significant after text inserted.
|
||||
res = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset, *aInOutParent, *aInOutOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
else if (beforeRun->mType == eNormalWS)
|
||||
{
|
||||
// try to change an nbsp to a space, if possible, just to prevent nbsp proliferation
|
||||
res = CheckTrailingNBSP(beforeRun, *aInOutParent, *aInOutOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
|
||||
// next up, tweak head and tail of string as needed.
|
||||
// first the head:
|
||||
// there are a variety of circumstances that would require us to convert a
|
||||
@ -446,7 +455,7 @@ nsWSRunObject::DeleteWSBackward()
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
// finally, delete that ws
|
||||
return DeleteChars(startNode, startOffset, mNode, mOffset);
|
||||
return DeleteChars(startNode, startOffset, endNode, endOffset);
|
||||
}
|
||||
else if (point.mChar == nbsp)
|
||||
{
|
||||
@ -489,7 +498,7 @@ nsWSRunObject::DeleteWSForward()
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
// finally, delete that ws
|
||||
return DeleteChars(startNode, startOffset, mNode, mOffset);
|
||||
return DeleteChars(startNode, startOffset, endNode, endOffset);
|
||||
}
|
||||
else if (point.mChar == nbsp)
|
||||
{
|
||||
@ -515,6 +524,8 @@ nsWSRunObject::PriorVisibleNode(nsIDOMNode *aNode,
|
||||
PRInt32 *outVisOffset,
|
||||
PRInt16 *outType)
|
||||
{
|
||||
// Find first visible thing before the point. position outVisNode/outVisOffset
|
||||
// just _after_ that thing. If we don't find anything return start of ws.
|
||||
if (!aNode || !outVisNode || !outVisOffset || !outType)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
@ -536,7 +547,7 @@ nsWSRunObject::PriorVisibleNode(nsIDOMNode *aNode,
|
||||
if (point.mTextNode)
|
||||
{
|
||||
*outVisNode = do_QueryInterface(point.mTextNode);
|
||||
*outVisOffset = point.mOffset;
|
||||
*outVisOffset = point.mOffset+1;
|
||||
if (nsCRT::IsAsciiSpace(point.mChar) || (point.mChar==nbsp))
|
||||
{
|
||||
*outType = eNormalWS;
|
||||
@ -571,6 +582,8 @@ nsWSRunObject::NextVisibleNode (nsIDOMNode *aNode,
|
||||
PRInt32 *outVisOffset,
|
||||
PRInt16 *outType)
|
||||
{
|
||||
// Find first visible thing before the point. position outVisNode/outVisOffset
|
||||
// just _before_ that thing. If we don't find anything return end of ws.
|
||||
if (!aNode || !outVisNode || !outVisOffset || !outType)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
@ -619,6 +632,27 @@ nsWSRunObject::NextVisibleNode (nsIDOMNode *aNode,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsWSRunObject::AdjustWhitespace()
|
||||
{
|
||||
// this routine examines a run of ws and tries to get rid of some unneeded nbsp's,
|
||||
// replacing them with regualr ascii space if possible. Keeping things simple
|
||||
// for now and just trying to fix up the trailing ws in the run.
|
||||
if (!mLastNBSPNode) return NS_OK; // nothing to do!
|
||||
nsresult res = NS_OK;
|
||||
WSFragment *curRun = mStartRun;
|
||||
while (curRun)
|
||||
{
|
||||
// look for normal ws run
|
||||
if (curRun->mType == eNormalWS)
|
||||
{
|
||||
res = CheckTrailingNBSPOfRun(curRun);
|
||||
break;
|
||||
}
|
||||
curRun = curRun->mRight;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
@ -1061,6 +1095,9 @@ nsWSRunObject::GetRuns()
|
||||
else
|
||||
{
|
||||
// we might have trailing ws.
|
||||
// it so happens that *if* there is an nbsp at end, {mEndNode,mEndOffset-1}
|
||||
// will point to it, even though in general start/end points not
|
||||
// guaranteed to be in text nodes.
|
||||
if ((mLastNBSPNode == mEndNode) && (mLastNBSPOffset == (mEndOffset-1)))
|
||||
{
|
||||
// normal ws runs right up to adjacent block (nbsp next to block)
|
||||
@ -1099,6 +1136,9 @@ nsWSRunObject::GetRuns()
|
||||
mStartRun->mLeftType = mStartReason;
|
||||
|
||||
// we might have trailing ws.
|
||||
// it so happens that *if* there is an nbsp at end, {mEndNode,mEndOffset-1}
|
||||
// will point to it, even though in general start/end points not
|
||||
// guaranteed to be in text nodes.
|
||||
if ((mLastNBSPNode == mEndNode) && (mLastNBSPOffset == (mEndOffset-1)))
|
||||
{
|
||||
// set up next run
|
||||
@ -1388,6 +1428,7 @@ nsWSRunObject::PrepareToDeleteRangePriv(nsWSRunObject* aEndObject)
|
||||
res = FindRun(mNode, mOffset, &beforeRun, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
res = aEndObject->FindRun(aEndObject->mNode, aEndObject->mOffset, &afterRun, PR_TRUE);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
// trim after run of any leading ws
|
||||
if (afterRun && (afterRun->mType == eLeadingWS))
|
||||
@ -1444,6 +1485,56 @@ nsWSRunObject::PrepareToDeleteRangePriv(nsWSRunObject* aEndObject)
|
||||
return res;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsWSRunObject::PrepareToSplitAcrossBlocksPriv()
|
||||
{
|
||||
// used to prepare ws to be split across two blocks. The main issue
|
||||
// here is make sure normalWS doesn't end up becoming non-significant
|
||||
// leading or trailing ws after the split.
|
||||
nsresult res = NS_OK;
|
||||
|
||||
// get the runs before and after selection
|
||||
WSFragment *beforeRun, *afterRun;
|
||||
res = FindRun(mNode, mOffset, &beforeRun, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
res = FindRun(mNode, mOffset, &afterRun, PR_TRUE);
|
||||
|
||||
// adjust normal ws in afterRun if needed
|
||||
if (afterRun && (afterRun->mType == eNormalWS))
|
||||
{
|
||||
// make sure leading char of following ws is an nbsp, so that it will show up
|
||||
WSPoint point;
|
||||
GetCharAfter(mNode, mOffset, &point);
|
||||
if (nsCRT::IsAsciiSpace(point.mChar))
|
||||
{
|
||||
res = ConvertToNBSP(point);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
}
|
||||
|
||||
// adjust normal ws in beforeRun if needed
|
||||
if (beforeRun && (beforeRun->mType == eNormalWS))
|
||||
{
|
||||
// make sure trailing char of starting ws is an nbsp, so that it will show up
|
||||
WSPoint point;
|
||||
GetCharBefore(mNode, mOffset, &point);
|
||||
if (nsCRT::IsAsciiSpace(point.mChar))
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> wsStartNode, wsEndNode;
|
||||
PRInt32 wsStartOffset, wsEndOffset;
|
||||
res = GetAsciiWSBounds(eBoth, mNode, mOffset,
|
||||
address_of(wsStartNode), &wsStartOffset,
|
||||
address_of(wsEndNode), &wsEndOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
point.mTextNode = do_QueryInterface(wsStartNode);
|
||||
point.mOffset = wsStartOffset;
|
||||
res = ConvertToNBSP(point);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsWSRunObject::DeleteChars(nsIDOMNode *aStartNode, PRInt32 aStartOffset,
|
||||
nsIDOMNode *aEndNode, PRInt32 aEndOffset)
|
||||
@ -1704,14 +1795,13 @@ nsWSRunObject::GetAsciiWSBounds(PRInt16 aDir, nsIDOMNode *aNode, PRInt32 aOffset
|
||||
nsCOMPtr<nsIDOMNode> startNode, endNode;
|
||||
PRInt32 startOffset, endOffset;
|
||||
|
||||
WSPoint point, tmp;
|
||||
nsresult res = NS_OK;
|
||||
|
||||
if (aDir & eAfter)
|
||||
{
|
||||
WSPoint point, tmp;
|
||||
res = GetCharAfter(aNode, aOffset, &point);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
if (point.mTextNode)
|
||||
if (NS_SUCCEEDED(res) && point.mTextNode)
|
||||
{ // we found a text node, at least
|
||||
endNode = do_QueryInterface(point.mTextNode);
|
||||
endOffset = point.mOffset;
|
||||
@ -1733,9 +1823,9 @@ nsWSRunObject::GetAsciiWSBounds(PRInt16 aDir, nsIDOMNode *aNode, PRInt32 aOffset
|
||||
|
||||
if (aDir & eBefore)
|
||||
{
|
||||
WSPoint point, tmp;
|
||||
res = GetCharBefore(aNode, aOffset, &point);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
if (point.mTextNode)
|
||||
if (NS_SUCCEEDED(res) && point.mTextNode)
|
||||
{ // we found a text node, at least
|
||||
startNode = do_QueryInterface(point.mTextNode);
|
||||
startOffset = point.mOffset+1;
|
||||
@ -1973,10 +2063,68 @@ nsWSRunObject::GetWSPointBefore(nsIDOMNode *aNode, PRInt32 aOffset, WSPoint *out
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsWSRunObject::CheckTrailingNBSPOfRun(WSFragment *aRun)
|
||||
{
|
||||
// try to change an nbsp to a space, if possible, just to prevent nbsp proliferation.
|
||||
// examine what is before and after the trailing nbsp, if any.
|
||||
if (!aRun) return NS_ERROR_NULL_POINTER;
|
||||
WSPoint thePoint;
|
||||
PRBool leftCheck = PR_FALSE;
|
||||
PRBool rightCheck = PR_FALSE;
|
||||
|
||||
// confirm run is normalWS
|
||||
if (aRun->mType != eNormalWS) return NS_ERROR_FAILURE;
|
||||
|
||||
// first check for trailing nbsp
|
||||
nsresult res = GetCharBefore(aRun->mEndNode, aRun->mEndOffset, &thePoint);
|
||||
if (NS_SUCCEEDED(res) && thePoint.mChar == nbsp)
|
||||
{
|
||||
// now check that what is to the left of it is compatible with replacing nbsp with space
|
||||
WSPoint prevPoint;
|
||||
res = GetCharBefore(thePoint, &prevPoint);
|
||||
if (NS_SUCCEEDED(res))
|
||||
{
|
||||
if (!nsCRT::IsAsciiSpace(prevPoint.mChar)) leftCheck = PR_TRUE;
|
||||
}
|
||||
else if (aRun->mLeftType == eText) leftCheck = PR_TRUE;
|
||||
else if (aRun->mLeftType == eSpecial) leftCheck = PR_TRUE;
|
||||
if (leftCheck)
|
||||
{
|
||||
// now check that what is to the right of it is compatible with replacing nbsp with space
|
||||
if (aRun->mRightType == eText) rightCheck = PR_TRUE;
|
||||
if (aRun->mRightType == eSpecial) rightCheck = PR_TRUE;
|
||||
if (aRun->mRightType == eBreak) rightCheck = PR_TRUE;
|
||||
}
|
||||
if (leftCheck && rightCheck)
|
||||
{
|
||||
// now replace nbsp with space
|
||||
// first, insert a space
|
||||
nsCOMPtr<nsIDOMCharacterData> textNode(do_QueryInterface(thePoint.mTextNode));
|
||||
if (!textNode)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
|
||||
nsAutoString spaceStr(PRUnichar(32));
|
||||
res = mHTMLEditor->InsertTextIntoTextNodeImpl(spaceStr, textNode, thePoint.mOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
// finally, delete that nbsp
|
||||
nsCOMPtr<nsIDOMNode> delNode(do_QueryInterface(thePoint.mTextNode));
|
||||
res = DeleteChars(delNode, thePoint.mOffset+1, delNode, thePoint.mOffset+2);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsWSRunObject::CheckTrailingNBSP(WSFragment *aRun, nsIDOMNode *aNode, PRInt32 aOffset)
|
||||
{
|
||||
// try to change an nbsp to a space, if possible, just to prevent nbsp proliferation
|
||||
// try to change an nbsp to a space, if possible, just to prevent nbsp proliferation.
|
||||
// this routine is called when we about to make this point in the ws abut an inserted break
|
||||
// or text, so we don't have to worry about what is after it. What is after it now will
|
||||
// end up after the inserted object.
|
||||
if (!aRun || !aNode) return NS_ERROR_NULL_POINTER;
|
||||
WSPoint thePoint;
|
||||
PRBool canConvert = PR_FALSE;
|
||||
nsresult res = GetCharBefore(aNode, aOffset, &thePoint);
|
||||
@ -2014,6 +2162,9 @@ nsresult
|
||||
nsWSRunObject::CheckLeadingNBSP(WSFragment *aRun, nsIDOMNode *aNode, PRInt32 aOffset)
|
||||
{
|
||||
// try to change an nbsp to a space, if possible, just to prevent nbsp proliferation
|
||||
// this routine is called when we about to make this point in the ws abut an inserted
|
||||
// text, so we don't have to worry about what is before it. What is before it now will
|
||||
// end up before the inserted text.
|
||||
WSPoint thePoint;
|
||||
PRBool canConvert = PR_FALSE;
|
||||
nsresult res = GetCharAfter(aNode, aOffset, &thePoint);
|
||||
|
@ -34,6 +34,24 @@ class nsIDOMDocument;
|
||||
class nsIDOMNode;
|
||||
class nsHTMLEditor;
|
||||
|
||||
// class nsWSRunObject represents the entire whitespace situation
|
||||
// around a given point. It collects up a list of nodes that contain
|
||||
// whitespace and categorizes in up to 3 different WSFragments (detailed
|
||||
// below). Each WSFragment is a collection of whitespace that is
|
||||
// either all insignificant, or that is significant. A WSFragment could
|
||||
// consist of insignificant whitespace because it is after a block
|
||||
// boundary or after a break. Or it could be insignificant because it
|
||||
// is before a block. Or it could be significant because it is
|
||||
// surrounded by text, or starts and ends with nbsps, etc.
|
||||
|
||||
// Throughout I refer to LeadingWS, NormalWS, TrailingWS. LeadingWS & TrailingWS
|
||||
// are runs of ascii ws that are insignificant (do not render) because they
|
||||
// are adjacent to block boundaries, or after a break. NormalWS is ws that
|
||||
// does cause soem rendering. Note that not all the ws in a NormalWS run need
|
||||
// render. For example, two ascii spaces surrounded by text on both sides
|
||||
// will only render as one space (in non-preformatted stlye html), yet both
|
||||
// spaces count as NormalWS. Together, they render as the one visible space.
|
||||
|
||||
class nsWSRunObject
|
||||
{
|
||||
public:
|
||||
@ -44,77 +62,147 @@ class nsWSRunObject
|
||||
kBackward
|
||||
} EWSDirection;
|
||||
|
||||
// constructor / destructor
|
||||
// constructor / destructor -----------------------------------------------
|
||||
nsWSRunObject(nsHTMLEditor *aEd);
|
||||
nsWSRunObject(nsHTMLEditor *aEd, nsIDOMNode *aNode, PRInt32 aOffset);
|
||||
~nsWSRunObject();
|
||||
|
||||
// public methods
|
||||
// public methods ---------------------------------------------------------
|
||||
|
||||
// PrepareToJoinBlocks fixes up ws at the end of aLeftParent and the
|
||||
// beginning of aRightParent in preperation for them to be joined.
|
||||
// example of fixup: trailingws in aLeftParent needs to be removed.
|
||||
static nsresult PrepareToJoinBlocks(nsHTMLEditor *aEd,
|
||||
nsIDOMNode *aLeftParent,
|
||||
nsIDOMNode *aRightParent);
|
||||
|
||||
// PrepareToDeleteRange fixes up ws before {aStartNode,aStartOffset}
|
||||
// and after {aEndNode,aEndOffset} in preperation for content
|
||||
// in that range to be deleted. Note that the nodes and offsets
|
||||
// are adjusted in response to any dom changes we make while
|
||||
// adjusting ws.
|
||||
// example of fixup: trailingws before {aStartNode,aStartOffset}
|
||||
// needs to be removed.
|
||||
static nsresult PrepareToDeleteRange(nsHTMLEditor *aHTMLEd,
|
||||
nsCOMPtr<nsIDOMNode> *aStartNode,
|
||||
PRInt32 *aStartOffset,
|
||||
nsCOMPtr<nsIDOMNode> *aEndNode,
|
||||
PRInt32 *aEndOffset);
|
||||
|
||||
// PrepareToDeleteNode fixes up ws before and after aNode in preperation
|
||||
// for aNode to be deleted.
|
||||
// example of fixup: trailingws before aNode needs to be removed.
|
||||
static nsresult PrepareToDeleteNode(nsHTMLEditor *aHTMLEd,
|
||||
nsIDOMNode *aNode);
|
||||
static nsresult PrepareToSplitAcrossBlocks(nsCOMPtr<nsIDOMNode> *aSplitNode,
|
||||
|
||||
// PrepareToSplitAcrossBlocks fixes up ws before and after
|
||||
// {aSplitNode,aSplitOffset} in preperation for a block
|
||||
// parent to be split. Note that the aSplitNode and aSplitOffset
|
||||
// are adjusted in response to any dom changes we make while
|
||||
// adjusting ws.
|
||||
// example of fixup: normalws before {aSplitNode,aSplitOffset}
|
||||
// needs to end with nbsp.
|
||||
static nsresult PrepareToSplitAcrossBlocks(nsHTMLEditor *aHTMLEd,
|
||||
nsCOMPtr<nsIDOMNode> *aSplitNode,
|
||||
PRInt32 *aSplitOffset);
|
||||
|
||||
// InsertBreak inserts a br node at {aInOutParent,aInOutOffset}
|
||||
// and makes any needed adjustments to ws around that point.
|
||||
// example of fixup: normalws after {aInOutParent,aInOutOffset}
|
||||
// needs to begin with nbsp.
|
||||
nsresult InsertBreak(nsCOMPtr<nsIDOMNode> *aInOutParent,
|
||||
PRInt32 *aInOutOffset,
|
||||
nsCOMPtr<nsIDOMNode> *outBRNode,
|
||||
nsIEditor::EDirection aSelect);
|
||||
|
||||
// InsertText inserts a string at {aInOutParent,aInOutOffset}
|
||||
// and makes any needed adjustments to ws around that point.
|
||||
// example of fixup: trailingws before {aInOutParent,aInOutOffset}
|
||||
// needs to be removed.
|
||||
nsresult InsertText(const nsAReadableString& aStringToInsert,
|
||||
nsCOMPtr<nsIDOMNode> *aInOutNode,
|
||||
PRInt32 *aInOutOffset,
|
||||
nsIDOMDocument *aDoc);
|
||||
|
||||
// DeleteWSBackward deletes a single visible piece of ws before
|
||||
// the ws point (the point to create the wsRunObject, passed to
|
||||
// its constructor). It makes any needed conversion to adjacent
|
||||
// ws to retain its significance.
|
||||
nsresult DeleteWSBackward();
|
||||
|
||||
// DeleteWSForward deletes a single visible piece of ws after
|
||||
// the ws point (the point to create the wsRunObject, passed to
|
||||
// its constructor). It makes any needed conversion to adjacent
|
||||
// ws to retain its significance.
|
||||
nsresult DeleteWSForward();
|
||||
|
||||
// PriorVisibleNode returns the first piece of visible thing
|
||||
// before {aNode,aOffset}. If there is no visible ws qualifying
|
||||
// it returns what is before the ws run. Note that
|
||||
// {outVisNode,outVisOffset} is set to just AFTER the visible
|
||||
// object.
|
||||
nsresult PriorVisibleNode(nsIDOMNode *aNode,
|
||||
PRInt32 aOffset,
|
||||
nsCOMPtr<nsIDOMNode> *outVisNode,
|
||||
PRInt32 *outVisOffset,
|
||||
PRInt16 *outType);
|
||||
|
||||
// NextVisibleNode returns the first piece of visible thing
|
||||
// after {aNode,aOffset}. If there is no visible ws qualifying
|
||||
// it returns what is after the ws run. Note that
|
||||
// {outVisNode,outVisOffset} is set to just BEFORE the visible
|
||||
// object.
|
||||
nsresult NextVisibleNode (nsIDOMNode *aNode,
|
||||
PRInt32 aOffset,
|
||||
nsCOMPtr<nsIDOMNode> *outVisNode,
|
||||
PRInt32 *outVisOffset,
|
||||
PRInt16 *outType);
|
||||
|
||||
// AdjustWhitespace examines the ws object for nbsp's that can
|
||||
// be safely converted to regular ascii space and converts them.
|
||||
nsresult AdjustWhitespace();
|
||||
|
||||
// public enums ---------------------------------------------------------
|
||||
enum {eNone = 0};
|
||||
enum {eLeadingWS = 1};
|
||||
enum {eTrailingWS = 1 << 1};
|
||||
enum {eNormalWS = 1 << 2};
|
||||
enum {eText = 1 << 3};
|
||||
enum {eSpecial = 1 << 4};
|
||||
enum {eBreak = 1 << 5};
|
||||
enum {eOtherBlock = 1 << 6};
|
||||
enum {eThisBlock = 1 << 7};
|
||||
enum {eBlock = eOtherBlock | eThisBlock};
|
||||
enum {eLeadingWS = 1}; // leading insignificant ws, ie, after block or br
|
||||
enum {eTrailingWS = 1 << 1}; // trailing insignificant ws, ie, before block
|
||||
enum {eNormalWS = 1 << 2}; // normal significant ws, ie, after text, image, ...
|
||||
enum {eText = 1 << 3}; // indicates regular (non-ws) text
|
||||
enum {eSpecial = 1 << 4}; // indicates an inline non-container, like image
|
||||
enum {eBreak = 1 << 5}; // indicates a br node
|
||||
enum {eOtherBlock = 1 << 6}; // indicates a block other than one ws run is in
|
||||
enum {eThisBlock = 1 << 7}; // indicates the block ws run is in
|
||||
enum {eBlock = eOtherBlock | eThisBlock}; // block found
|
||||
|
||||
enum {eBefore = 1};
|
||||
enum {eAfter = 1 << 1};
|
||||
enum {eBoth = eBefore | eAfter};
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
// WSFragment struct ---------------------------------------------------------
|
||||
// WSFragment represents a single run of ws (all leadingws, or all normalws,
|
||||
// or all trailingws, or all leading+trailingws). Note that this single run may
|
||||
// still span multiple nodes.
|
||||
struct WSFragment
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> mStartNode;
|
||||
nsCOMPtr<nsIDOMNode> mEndNode;
|
||||
PRInt16 mStartOffset;
|
||||
PRInt16 mEndOffset;
|
||||
PRInt16 mType, mLeftType, mRightType;
|
||||
WSFragment *mLeft, *mRight;
|
||||
nsCOMPtr<nsIDOMNode> mStartNode; // node where ws run starts
|
||||
nsCOMPtr<nsIDOMNode> mEndNode; // node where ws run ends
|
||||
PRInt16 mStartOffset; // offset where ws run starts
|
||||
PRInt16 mEndOffset; // offset where ws run ends
|
||||
PRInt16 mType, mLeftType, mRightType; // type of ws, and what is to left and right of it
|
||||
WSFragment *mLeft, *mRight; // other ws runs to left or right. may be null.
|
||||
|
||||
WSFragment() : mStartNode(0),mEndNode(0),mStartOffset(0),
|
||||
mEndOffset(0),mType(0),mLeftType(0),
|
||||
mRightType(0),mLeft(0),mRight(0) {}
|
||||
};
|
||||
|
||||
// WSPoint struct ------------------------------------------------------------
|
||||
// A WSPoint struct represents a unique location within the ws run. It is
|
||||
// always within a textnode that is one of the nodes stored in the list
|
||||
// in the wsRunObject. For convenience, the character at that point is also
|
||||
// stored in the struct.
|
||||
struct WSPoint
|
||||
{
|
||||
nsCOMPtr<nsITextContent> mTextNode;
|
||||
@ -128,7 +216,8 @@ class nsWSRunObject
|
||||
mTextNode(aTextNode),mOffset(aOffset),mChar(aChar) {}
|
||||
};
|
||||
|
||||
// protected methods
|
||||
// protected methods ---------------------------------------------------------
|
||||
// tons of utility methods.
|
||||
nsresult GetWSNodes();
|
||||
nsresult GetRuns();
|
||||
void ClearRuns();
|
||||
@ -150,6 +239,7 @@ class nsWSRunObject
|
||||
nsIDOMNode *aBlockParent,
|
||||
nsCOMPtr<nsIDOMNode> *aNextNode);
|
||||
nsresult PrepareToDeleteRangePriv(nsWSRunObject* aEndObject);
|
||||
nsresult PrepareToSplitAcrossBlocksPriv();
|
||||
nsresult DeleteChars(nsIDOMNode *aStartNode, PRInt32 aStartOffset,
|
||||
nsIDOMNode *aEndNode, PRInt32 aEndOffset);
|
||||
nsresult GetCharAfter(nsIDOMNode *aNode, PRInt32 aOffset, WSPoint *outPoint);
|
||||
@ -164,26 +254,36 @@ class nsWSRunObject
|
||||
PRUnichar GetCharAt(nsITextContent *aTextNode, PRInt32 aOffset);
|
||||
nsresult GetWSPointAfter(nsIDOMNode *aNode, PRInt32 aOffset, WSPoint *outPoint);
|
||||
nsresult GetWSPointBefore(nsIDOMNode *aNode, PRInt32 aOffset, WSPoint *outPoint);
|
||||
nsresult CheckTrailingNBSPOfRun(WSFragment *aRun);
|
||||
nsresult CheckTrailingNBSP(WSFragment *aRun, nsIDOMNode *aNode, PRInt32 aOffset);
|
||||
nsresult CheckLeadingNBSP(WSFragment *aRun, nsIDOMNode *aNode, PRInt32 aOffset);
|
||||
|
||||
// member variables
|
||||
nsCOMPtr<nsIDOMNode> mNode;
|
||||
PRInt32 mOffset;
|
||||
nsCOMPtr<nsIDOMNode> mStartNode;
|
||||
PRInt32 mStartOffset;
|
||||
PRInt16 mStartReason;
|
||||
nsCOMPtr<nsIDOMNode> mEndNode;
|
||||
PRInt32 mEndOffset;
|
||||
PRInt16 mEndReason;
|
||||
nsCOMPtr<nsIDOMNode> mFirstNBSPNode;
|
||||
PRInt32 mFirstNBSPOffset;
|
||||
nsCOMPtr<nsIDOMNode> mLastNBSPNode;
|
||||
PRInt32 mLastNBSPOffset;
|
||||
nsCOMPtr<nsISupportsArray> mNodeArray;
|
||||
WSFragment *mStartRun;
|
||||
WSFragment *mEndRun;
|
||||
nsHTMLEditor *mHTMLEditor; // non-owning.
|
||||
// member variables ---------------------------------------------------------
|
||||
|
||||
nsCOMPtr<nsIDOMNode> mNode; // the node passed to our constructor
|
||||
PRInt32 mOffset; // the offset passed to our contructor
|
||||
// together, the above represent the point at which we are building up ws info.
|
||||
|
||||
nsCOMPtr<nsIDOMNode> mStartNode; // node/offet where ws starts
|
||||
PRInt32 mStartOffset; // ...
|
||||
PRInt16 mStartReason; // reason why ws starts (eText, eOtherBlock, etc)
|
||||
|
||||
nsCOMPtr<nsIDOMNode> mEndNode; // node/offet where ws ends
|
||||
PRInt32 mEndOffset; // ...
|
||||
PRInt16 mEndReason; // reason why ws ends (eText, eOtherBlock, etc)
|
||||
|
||||
nsCOMPtr<nsIDOMNode> mFirstNBSPNode; // location of first nbsp in ws run, if any
|
||||
PRInt32 mFirstNBSPOffset; // ...
|
||||
|
||||
nsCOMPtr<nsIDOMNode> mLastNBSPNode; // location of last nbsp in ws run, if any
|
||||
PRInt32 mLastNBSPOffset; // ...
|
||||
|
||||
nsCOMPtr<nsISupportsArray> mNodeArray;//the list of nodes containing ws in this run
|
||||
|
||||
WSFragment *mStartRun; // the first WSFragment in the run
|
||||
WSFragment *mEndRun; // the last WSFragment in the run, may be same as first
|
||||
|
||||
nsHTMLEditor *mHTMLEditor; // non-owning.
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -244,6 +244,8 @@ protected:
|
||||
PRUint32 aOffset,
|
||||
PRUint32 aLength);
|
||||
|
||||
// NS_IMETHOD DeleteRange(nsIDOMRange *aRange);
|
||||
|
||||
NS_IMETHOD CreateTxnForDeleteText(nsIDOMCharacterData *aElement,
|
||||
PRUint32 aOffset,
|
||||
PRUint32 aLength,
|
||||
|
@ -118,6 +118,41 @@ class nsRangeUpdater
|
||||
};
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* helper class for using nsSelectionState. stack based class for doing
|
||||
* preservation of dom points across editor actions
|
||||
*/
|
||||
|
||||
class nsAutoTrackDOMPoint
|
||||
{
|
||||
private:
|
||||
nsRangeUpdater &mRU;
|
||||
nsCOMPtr<nsIDOMNode> *mNode;
|
||||
PRInt32 *mOffset;
|
||||
nsRangeStore mRangeItem;
|
||||
public:
|
||||
nsAutoTrackDOMPoint(nsRangeUpdater &aRangeUpdater, nsCOMPtr<nsIDOMNode> *aNode, PRInt32 *aOffset) :
|
||||
mRU(aRangeUpdater)
|
||||
,mNode(aNode)
|
||||
,mOffset(aOffset)
|
||||
{
|
||||
mRangeItem.startNode = *mNode;
|
||||
mRangeItem.endNode = *mNode;
|
||||
mRangeItem.startOffset = *mOffset;
|
||||
mRangeItem.endOffset = *mOffset;
|
||||
mRU.RegisterRangeItem(&mRangeItem);
|
||||
}
|
||||
|
||||
~nsAutoTrackDOMPoint()
|
||||
{
|
||||
mRU.DropRangeItem(&mRangeItem);
|
||||
*mNode = mRangeItem.startNode;
|
||||
*mOffset = mRangeItem.startOffset;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* another helper class for nsSelectionState. stack based class for doing
|
||||
* Will/DidReplaceContainer()
|
||||
|
@ -56,6 +56,7 @@
|
||||
#endif // IBMBIDI
|
||||
|
||||
#include "nsEditorUtils.h"
|
||||
#include "nsWSRunObject.h"
|
||||
|
||||
#include "InsertTextTxn.h"
|
||||
#include "DeleteTextTxn.h"
|
||||
@ -380,20 +381,11 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
|
||||
// adjust whitespace for insert text and delete actions
|
||||
if ((action == nsEditor::kOpInsertText) ||
|
||||
(action == nsEditor::kOpInsertIMEText) ||
|
||||
(action == nsEditor::kOpDeleteSelection))
|
||||
{
|
||||
res = AdjustWhitespace(selection);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
|
||||
// replace newlines that are preformatted
|
||||
// replace newlines with breaks.
|
||||
// MOOSE: This is buttUgly. A better way to
|
||||
// organize the action enum is in order.
|
||||
if ((action == nsEditor::kOpInsertText) ||
|
||||
(action == nsEditor::kOpInsertIMEText) ||
|
||||
if (// (action == nsEditor::kOpInsertText) ||
|
||||
// (action == nsEditor::kOpInsertIMEText) ||
|
||||
(action == nsHTMLEditor::kOpInsertElement) ||
|
||||
(action == nsHTMLEditor::kOpInsertQuotation) ||
|
||||
(action == nsEditor::kOpInsertNode))
|
||||
@ -404,6 +396,13 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection
|
||||
// clean up any empty nodes in the selection
|
||||
res = RemoveEmptyNodes();
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// attempt to transform any uneeded nbsp's into spaces after doing deletions
|
||||
if (action == nsEditor::kOpDeleteSelection)
|
||||
{
|
||||
res = AdjustWhitespace(selection);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
|
||||
// adjust selection for insert text, html paste, and delete actions
|
||||
if ((action == nsEditor::kOpInsertText) ||
|
||||
@ -941,6 +940,8 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction,
|
||||
nsCOMPtr<nsIDOMNode> selNode;
|
||||
PRInt32 selOffset;
|
||||
|
||||
PRBool bPlaintext = mFlags & nsIPlaintextEditor::eEditorPlaintextMask;
|
||||
|
||||
// if the selection isn't collapsed, delete it.
|
||||
PRBool bCollapsed;
|
||||
res = aSelection->GetIsCollapsed(&bCollapsed);
|
||||
@ -1006,7 +1007,7 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction,
|
||||
// for efficiency, break out the pre case seperately. This is because
|
||||
// its a lot cheaper to search the input string for only newlines than
|
||||
// it is to search for both tabs and newlines.
|
||||
if (isPRE)
|
||||
if (isPRE || bPlaintext)
|
||||
{
|
||||
char newlineChar = '\n';
|
||||
while (unicodeBuf && (pos != -1) && (pos < (PRInt32)(*inString).Length()))
|
||||
@ -1068,21 +1069,29 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction,
|
||||
|
||||
subStr.Subsume((PRUnichar*)&unicodeBuf[oldPos], PR_FALSE, subStrLen);
|
||||
|
||||
nsWSRunObject wsObj(mHTMLEditor, curNode, curOffset);
|
||||
|
||||
// is it a tab?
|
||||
if (subStr.EqualsWithConversion("\t"))
|
||||
{
|
||||
res = mHTMLEditor->InsertTextImpl(tabString, address_of(curNode), &curOffset, doc);
|
||||
// res = mHTMLEditor->InsertTextImpl(tabString, address_of(curNode), &curOffset, doc);
|
||||
res = wsObj.InsertText(tabString, address_of(curNode), &curOffset, doc);
|
||||
if (NS_FAILED(res)) return res;
|
||||
pos++;
|
||||
}
|
||||
// is it a return?
|
||||
else if (subStr.EqualsWithConversion("\n"))
|
||||
{
|
||||
res = mHTMLEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
|
||||
// res = mHTMLEditor->CreateBRImpl(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
|
||||
res = wsObj.InsertBreak(address_of(curNode), &curOffset, address_of(unused), nsIEditor::eNone);
|
||||
if (NS_FAILED(res)) return res;
|
||||
pos++;
|
||||
}
|
||||
else
|
||||
{
|
||||
res = mHTMLEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc);
|
||||
// res = mHTMLEditor->InsertTextImpl(subStr, address_of(curNode), &curOffset, doc);
|
||||
res = wsObj.InsertText(subStr, address_of(curNode), &curOffset, doc);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
@ -1121,6 +1130,7 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
|
||||
*aCancel = PR_FALSE;
|
||||
*aHandled = PR_FALSE;
|
||||
|
||||
PRBool bPlaintext = mFlags & nsIPlaintextEditor::eEditorPlaintextMask;
|
||||
|
||||
// if the selection isn't collapsed, delete it.
|
||||
PRBool bCollapsed;
|
||||
@ -1147,7 +1157,6 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
|
||||
PRInt32 selOffset, newOffset;
|
||||
res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
PRBool bPlaintext = mFlags & nsIPlaintextEditor::eEditorPlaintextMask;
|
||||
res = GetTopEnclosingMailCite(selNode, address_of(citeNode), bPlaintext);
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (citeNode)
|
||||
@ -1212,7 +1221,15 @@ nsHTMLEditRules::WillInsertBreak(nsISelection *aSelection, PRBool *aCancel, PRBo
|
||||
else
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> brNode;
|
||||
res = mHTMLEditor->CreateBR(node, offset, address_of(brNode));
|
||||
if (bPlaintext)
|
||||
{
|
||||
res = mHTMLEditor->CreateBR(node, offset, address_of(brNode));
|
||||
}
|
||||
else
|
||||
{
|
||||
nsWSRunObject wsObj(mHTMLEditor, node, offset);
|
||||
res = wsObj.InsertBreak(address_of(node), &offset, address_of(brNode), nsIEditor::eNone);
|
||||
}
|
||||
if (NS_FAILED(res)) return res;
|
||||
res = nsEditor::GetNodeLocation(brNode, address_of(node), &offset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
@ -1266,6 +1283,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
}
|
||||
|
||||
nsresult res = NS_OK;
|
||||
PRBool bPlaintext = mFlags & nsIPlaintextEditor::eEditorPlaintextMask;
|
||||
|
||||
PRBool bCollapsed;
|
||||
res = aSelection->GetIsCollapsed(&bCollapsed);
|
||||
@ -1289,9 +1307,81 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (!startNode) return NS_ERROR_FAILURE;
|
||||
|
||||
// get the root element
|
||||
nsCOMPtr<nsIDOMElement> bodyElement;
|
||||
nsCOMPtr<nsIDOMNode> bodyNode;
|
||||
res = mHTMLEditor->GetRootElement(getter_AddRefs(bodyElement));
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (!bodyElement) return NS_ERROR_UNEXPECTED;
|
||||
bodyNode = do_QueryInterface(bodyElement);
|
||||
|
||||
if (bCollapsed)
|
||||
{
|
||||
// easy case, in a text node:
|
||||
// if we are inside an empty block, delete it.
|
||||
// Note: do NOT delete table elements this way.
|
||||
nsCOMPtr<nsIDOMNode> block;
|
||||
if (IsBlockNode(startNode))
|
||||
block = startNode;
|
||||
else
|
||||
block = mHTMLEditor->GetBlockNodeParent(startNode);
|
||||
PRBool bIsEmptyNode;
|
||||
if (block != bodyNode)
|
||||
{
|
||||
res = mHTMLEditor->IsEmptyNode(block, &bIsEmptyNode, PR_TRUE, PR_FALSE);
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (bIsEmptyNode && !nsHTMLEditUtils::IsTableElement(startNode))
|
||||
{
|
||||
// adjust selection to be right after it
|
||||
nsCOMPtr<nsIDOMNode> blockParent;
|
||||
PRInt32 offset;
|
||||
res = nsEditor::GetNodeLocation(block, address_of(blockParent), &offset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (!blockParent || offset < 0) return NS_ERROR_FAILURE;
|
||||
res = aSelection->Collapse(blockParent, offset+1);
|
||||
if (NS_FAILED(res)) return res;
|
||||
res = mHTMLEditor->DeleteNode(block);
|
||||
*aHandled = PR_TRUE;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
if (!bPlaintext)
|
||||
{
|
||||
// gather up ws data here. We may be next to non-significant ws.
|
||||
nsWSRunObject wsObj(mHTMLEditor, startNode, startOffset);
|
||||
nsCOMPtr<nsIDOMNode> visNode;
|
||||
PRInt32 visOffset;
|
||||
PRInt16 wsType;
|
||||
if (aAction == nsIEditor::ePrevious)
|
||||
{
|
||||
res = wsObj.PriorVisibleNode(startNode, startOffset, address_of(visNode), &visOffset, &wsType);
|
||||
// note that visOffset is _after_ what we are about to delete.
|
||||
}
|
||||
else if (aAction == nsIEditor::eNext)
|
||||
{
|
||||
res = wsObj.NextVisibleNode(startNode, startOffset, address_of(visNode), &visOffset, &wsType);
|
||||
// note that visOffset is _before_ what we are about to delete.
|
||||
}
|
||||
if (NS_SUCCEEDED(res))
|
||||
{
|
||||
if (wsType==nsWSRunObject::eNormalWS)
|
||||
{
|
||||
// we found some visible ws to delete. Let ws code handle it.
|
||||
if (aAction == nsIEditor::ePrevious)
|
||||
res = wsObj.DeleteWSBackward();
|
||||
else if (aAction == nsIEditor::eNext)
|
||||
res = wsObj.DeleteWSForward();
|
||||
*aHandled = PR_TRUE;
|
||||
return res;
|
||||
}
|
||||
else if (visNode)
|
||||
{
|
||||
// reposition startNode and startOffset so that we skip over any non-significant ws
|
||||
startNode = visNode;
|
||||
startOffset = visOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
// in a text node:
|
||||
if (mHTMLEditor->IsTextNode(startNode))
|
||||
{
|
||||
nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(startNode);
|
||||
@ -1299,83 +1389,6 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
res = textNode->GetLength(&strLength);
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
#ifdef IBMBIDI // Test for distance between caret and text that will be deleted
|
||||
nsCOMPtr<nsIPresShell> shell;
|
||||
mEditor->GetPresShell(getter_AddRefs(shell));
|
||||
if (shell)
|
||||
{
|
||||
nsCOMPtr<nsIPresContext> context;
|
||||
shell->GetPresContext(getter_AddRefs(context));
|
||||
if (context)
|
||||
{
|
||||
PRBool bidiEnabled;
|
||||
context->BidiEnabled(bidiEnabled);
|
||||
if (bidiEnabled)
|
||||
{
|
||||
nsCOMPtr<nsIFrameSelection> frameSelection;
|
||||
shell->GetFrameSelection(getter_AddRefs(frameSelection));
|
||||
if (frameSelection)
|
||||
{
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(startNode);
|
||||
if (content)
|
||||
{
|
||||
nsIFrame *primaryFrame;
|
||||
nsIFrame *frameBefore;
|
||||
nsIFrame *frameAfter;
|
||||
PRInt32 frameOffset;
|
||||
|
||||
shell->GetPrimaryFrameFor(content, &primaryFrame);
|
||||
if (primaryFrame)
|
||||
{
|
||||
res = primaryFrame->GetChildFrameContainingOffset(startOffset, PR_FALSE, &frameOffset, &frameBefore);
|
||||
if (NS_SUCCEEDED(res) && frameBefore)
|
||||
{
|
||||
PRInt32 start, end;
|
||||
frameBefore->GetOffsets(start, end);
|
||||
if (startOffset == end)
|
||||
{
|
||||
res = primaryFrame->GetChildFrameContainingOffset(startOffset, PR_TRUE, &frameOffset, &frameAfter);
|
||||
if (NS_SUCCEEDED(res) && frameAfter)
|
||||
{
|
||||
PRUint8 currentCursorLevel;
|
||||
long levelBefore;
|
||||
long levelAfter;
|
||||
long paragraphLevel;
|
||||
long levelOfDeletion;
|
||||
nsCOMPtr<nsIAtom> embeddingLevel = NS_NewAtom("EmbeddingLevel");
|
||||
nsCOMPtr<nsIAtom> baseLevel = NS_NewAtom("BaseLevel");
|
||||
frameBefore->GetBidiProperty(context, embeddingLevel, (void**)&levelBefore);
|
||||
if (frameBefore==frameAfter)
|
||||
{
|
||||
frameBefore->GetBidiProperty(context, baseLevel, (void**)¶graphLevel);
|
||||
levelAfter = paragraphLevel;
|
||||
}
|
||||
else
|
||||
frameAfter->GetBidiProperty(context, embeddingLevel, (void**)&levelAfter);
|
||||
shell->GetCursorBidiLevel(¤tCursorLevel);
|
||||
if (levelBefore==levelAfter && (levelAfter & 1) == (currentCursorLevel & 1))
|
||||
shell->SetCursorBidiLevel(levelBefore);
|
||||
else
|
||||
{
|
||||
levelOfDeletion = (nsIEditor::eNext==aAction) ? levelAfter : levelBefore;
|
||||
shell->SetCursorBidiLevel(levelOfDeletion);
|
||||
if ((currentCursorLevel/* & 1*/) != (levelOfDeletion/* & 1*/))
|
||||
{
|
||||
*aCancel = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // IBMBIDI
|
||||
|
||||
// at beginning of text node and backspaced?
|
||||
if (!startOffset && (aAction == nsIEditor::ePrevious))
|
||||
{
|
||||
@ -1384,7 +1397,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// if there is no prior node then cancel the deletion
|
||||
if (!priorNode)
|
||||
if (!priorNode || !nsTextEditUtils::InBody(priorNode, mHTMLEditor))
|
||||
{
|
||||
*aCancel = PR_TRUE;
|
||||
return res;
|
||||
@ -1401,7 +1414,17 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
nsCOMPtr<nsIDOMCharacterData>nodeAsText;
|
||||
nodeAsText = do_QueryInterface(priorNode);
|
||||
nodeAsText->GetLength((PRUint32*)&offset);
|
||||
NS_ENSURE_TRUE(offset, NS_ERROR_FAILURE);
|
||||
res = aSelection->Collapse(priorNode,offset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (!bPlaintext)
|
||||
{
|
||||
PRInt32 so = offset-1;
|
||||
PRInt32 eo = offset;
|
||||
res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor,
|
||||
address_of(priorNode), &so,
|
||||
address_of(priorNode), &eo);
|
||||
}
|
||||
// just return without setting handled to true.
|
||||
// default code will take care of actual deletion
|
||||
return res;
|
||||
@ -1409,6 +1432,11 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
// is prior node not a container? (ie, a br, hr, image...)
|
||||
else if (!mHTMLEditor->IsContainer(priorNode)) // MOOSE: anchors not handled
|
||||
{
|
||||
if (!bPlaintext)
|
||||
{
|
||||
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, priorNode);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
// delete the break, and join like nodes if appropriate
|
||||
res = mHTMLEditor->DeleteNode(priorNode);
|
||||
if (NS_FAILED(res)) return res;
|
||||
@ -1424,8 +1452,6 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
if (mHTMLEditor->IsTextNode(priorNode))
|
||||
{
|
||||
// if so, join them!
|
||||
nsCOMPtr<nsIDOMNode> topParent;
|
||||
priorNode->GetParentNode(getter_AddRefs(topParent));
|
||||
res = JoinNodesSmart(priorNode,startNode,address_of(selNode),&selOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// fix up selection
|
||||
@ -1436,6 +1462,11 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
}
|
||||
else if ( IsInlineNode(priorNode) )
|
||||
{
|
||||
if (!bPlaintext)
|
||||
{
|
||||
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, priorNode);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
// remember where we are
|
||||
PRInt32 offset;
|
||||
nsCOMPtr<nsIDOMNode> node;
|
||||
@ -1454,8 +1485,16 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
}
|
||||
|
||||
// deleting across blocks
|
||||
nsCOMPtr<nsIDOMNode> leftParent = mHTMLEditor->GetBlockNodeParent(priorNode);
|
||||
nsCOMPtr<nsIDOMNode> rightParent = mHTMLEditor->GetBlockNodeParent(startNode);
|
||||
nsCOMPtr<nsIDOMNode> leftParent;
|
||||
nsCOMPtr<nsIDOMNode> rightParent;
|
||||
if (IsBlockNode(priorNode))
|
||||
leftParent = priorNode;
|
||||
else
|
||||
leftParent = mHTMLEditor->GetBlockNodeParent(priorNode);
|
||||
if (IsBlockNode(startNode))
|
||||
rightParent = startNode;
|
||||
else
|
||||
rightParent = mHTMLEditor->GetBlockNodeParent(startNode);
|
||||
|
||||
// if leftParent or rightParent is null, it's because the
|
||||
// corresponding selection endpoint is in the body node.
|
||||
@ -1472,9 +1511,13 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
// are the blocks of same type?
|
||||
if (mHTMLEditor->NodesSameType(leftParent, rightParent))
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> topParent;
|
||||
leftParent->GetParentNode(getter_AddRefs(topParent));
|
||||
|
||||
if (!bPlaintext)
|
||||
{
|
||||
// adjust whitespace at block boundaries
|
||||
res = nsWSRunObject::PrepareToJoinBlocks(mHTMLEditor,leftParent,rightParent);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
// join the nodes
|
||||
*aHandled = PR_TRUE;
|
||||
res = JoinNodesSmart(leftParent,rightParent,address_of(selNode),&selOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
@ -1513,6 +1556,15 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
nsCOMPtr<nsIDOMCharacterData>nodeAsText;
|
||||
nodeAsText = do_QueryInterface(nextNode);
|
||||
res = aSelection->Collapse(nextNode,0);
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (!bPlaintext)
|
||||
{
|
||||
PRInt32 so = 0;
|
||||
PRInt32 eo = 1;
|
||||
res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor,
|
||||
address_of(nextNode), &so,
|
||||
address_of(nextNode), &eo);
|
||||
}
|
||||
// just return without setting handled to true.
|
||||
// default code will take care of actual deletion
|
||||
return res;
|
||||
@ -1520,6 +1572,11 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
// is next node not a container? (ie, a br, hr, image...)
|
||||
else if (!mHTMLEditor->IsContainer(nextNode)) // MOOSE: anchors not handled
|
||||
{
|
||||
if (!bPlaintext)
|
||||
{
|
||||
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, nextNode);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
// delete the break, and join like nodes if appropriate
|
||||
res = mHTMLEditor->DeleteNode(nextNode);
|
||||
if (NS_FAILED(res)) return res;
|
||||
@ -1535,8 +1592,6 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
if ( mHTMLEditor->IsTextNode(nextNode) )
|
||||
{
|
||||
// if so, join them!
|
||||
nsCOMPtr<nsIDOMNode> topParent;
|
||||
nextNode->GetParentNode(getter_AddRefs(topParent));
|
||||
res = JoinNodesSmart(startNode,nextNode,address_of(selNode),&selOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// fix up selection
|
||||
@ -1547,9 +1602,14 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
}
|
||||
else if ( IsInlineNode(nextNode) )
|
||||
{
|
||||
if (!bPlaintext)
|
||||
{
|
||||
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, nextNode);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
// remember where we are
|
||||
PRInt32 offset;
|
||||
nsCOMPtr<nsIDOMNode> node;
|
||||
// remember where we are
|
||||
res = mHTMLEditor->GetNodeLocation(nextNode, address_of(node), &offset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// delete it
|
||||
@ -1565,9 +1625,17 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
}
|
||||
|
||||
// deleting across blocks
|
||||
nsCOMPtr<nsIDOMNode> leftParent = mHTMLEditor->GetBlockNodeParent(startNode);
|
||||
nsCOMPtr<nsIDOMNode> rightParent = mHTMLEditor->GetBlockNodeParent(nextNode);
|
||||
|
||||
nsCOMPtr<nsIDOMNode> leftParent;
|
||||
nsCOMPtr<nsIDOMNode> rightParent;
|
||||
if (IsBlockNode(startNode))
|
||||
leftParent = startNode;
|
||||
else
|
||||
leftParent = mHTMLEditor->GetBlockNodeParent(startNode);
|
||||
if (IsBlockNode(nextNode))
|
||||
rightParent = nextNode;
|
||||
else
|
||||
rightParent = mHTMLEditor->GetBlockNodeParent(nextNode);
|
||||
|
||||
// if leftParent or rightParent is null, it's because the
|
||||
// corresponding selection endpoint is in the body node.
|
||||
if (!leftParent || !rightParent)
|
||||
@ -1583,9 +1651,13 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
// are the blocks of same type?
|
||||
if (mHTMLEditor->NodesSameType(leftParent, rightParent))
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> topParent;
|
||||
leftParent->GetParentNode(getter_AddRefs(topParent));
|
||||
|
||||
if (!bPlaintext)
|
||||
{
|
||||
// adjust whitespace at block boundaries
|
||||
res = nsWSRunObject::PrepareToJoinBlocks(mHTMLEditor,leftParent,rightParent);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
// join the nodes
|
||||
*aHandled = PR_TRUE;
|
||||
res = JoinNodesSmart(leftParent,rightParent,address_of(selNode),&selOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
@ -1596,8 +1668,28 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
|
||||
// else blocks not same type, bail to default
|
||||
return NS_OK;
|
||||
|
||||
}
|
||||
// else in middle of text node. default will do right thing.
|
||||
nsCOMPtr<nsIDOMCharacterData>nodeAsText;
|
||||
nodeAsText = do_QueryInterface(startNode);
|
||||
if (!nodeAsText) return NS_ERROR_NULL_POINTER;
|
||||
res = aSelection->Collapse(startNode,startOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
if (!bPlaintext)
|
||||
{
|
||||
PRInt32 so = startOffset;
|
||||
PRInt32 eo = startOffset;
|
||||
if (aAction == nsIEditor::ePrevious)
|
||||
so--; // we know so not zero - that case handled above
|
||||
else
|
||||
eo++; // we know eo not at end of text node - that case handled above
|
||||
res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor,
|
||||
address_of(startNode), &so,
|
||||
address_of(startNode), &eo);
|
||||
}
|
||||
// just return without setting handled to true.
|
||||
// default code will take care of actual deletion
|
||||
return res;
|
||||
}
|
||||
// else not in text node; we need to find right place to act on
|
||||
else
|
||||
@ -1658,7 +1750,7 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
*aCancel = PR_TRUE;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// if this node is text node, adjust selection
|
||||
if (nsEditor::IsTextNode(nodeToDelete))
|
||||
{
|
||||
@ -1674,10 +1766,6 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
{
|
||||
// editable leaf node is not text; delete it.
|
||||
// that's the default behavior
|
||||
PRInt32 offset;
|
||||
nsCOMPtr<nsIDOMNode> node;
|
||||
res = nsEditor::GetNodeLocation(nodeToDelete, address_of(node), &offset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// EXCEPTION: if it's a mozBR, we have to check and see if
|
||||
// there is a br in front of it. If so, we must delete both.
|
||||
// else you get this: deletion code deletes mozBR, then selection
|
||||
@ -1695,16 +1783,28 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
if (block == brBlock)
|
||||
{
|
||||
// delete both breaks
|
||||
if (!bPlaintext)
|
||||
{
|
||||
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, brNode);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
res = mHTMLEditor->DeleteNode(brNode);
|
||||
if (NS_FAILED(res)) return res;
|
||||
res = mHTMLEditor->DeleteNode(nodeToDelete);
|
||||
*aHandled = PR_TRUE;
|
||||
return res;
|
||||
// fall through to delete other br
|
||||
}
|
||||
// else fall through
|
||||
}
|
||||
// else fall through
|
||||
}
|
||||
if (!bPlaintext)
|
||||
{
|
||||
res = nsWSRunObject::PrepareToDeleteNode(mHTMLEditor, nodeToDelete);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
PRInt32 offset;
|
||||
nsCOMPtr<nsIDOMNode> node;
|
||||
res = nsEditor::GetNodeLocation(nodeToDelete, address_of(node), &offset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// adjust selection to be right after it
|
||||
res = aSelection->Collapse(node, offset+1);
|
||||
if (NS_FAILED(res)) return res;
|
||||
@ -1722,9 +1822,15 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
nsCOMPtr<nsIDOMNode> endNode;
|
||||
PRInt32 endOffset;
|
||||
res = mHTMLEditor->GetEndNodeAndOffset(aSelection, address_of(endNode), &endOffset);
|
||||
if (NS_FAILED(res))
|
||||
{
|
||||
return res;
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// adjust surrounding whitespace in preperation to delete selection
|
||||
if (!bPlaintext)
|
||||
{
|
||||
res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor,
|
||||
address_of(startNode), &startOffset,
|
||||
address_of(endNode), &endOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
if (endNode.get() != startNode.get())
|
||||
{
|
||||
@ -1735,20 +1841,11 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
// are the blocks of same type?
|
||||
nsCOMPtr<nsIDOMNode> leftParent;
|
||||
nsCOMPtr<nsIDOMNode> rightParent;
|
||||
|
||||
// XXX: Fix for bug #10815: Crash deleting selected text and table.
|
||||
// Make sure leftParent and rightParent are never NULL. This
|
||||
// can happen if we call GetBlockNodeParent() and the node we
|
||||
// pass in is a body node.
|
||||
//
|
||||
// Should we be calling IsBlockNode() instead of IsBody() here?
|
||||
|
||||
if (nsTextEditUtils::IsBody(startNode))
|
||||
if (IsBlockNode(startNode))
|
||||
leftParent = startNode;
|
||||
else
|
||||
leftParent = mHTMLEditor->GetBlockNodeParent(startNode);
|
||||
|
||||
if (nsTextEditUtils::IsBody(endNode))
|
||||
if (IsBlockNode(endNode))
|
||||
rightParent = endNode;
|
||||
else
|
||||
rightParent = mHTMLEditor->GetBlockNodeParent(endNode);
|
||||
@ -1763,9 +1860,6 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection,
|
||||
if ( (leftBlockParent.get() == rightBlockParent.get())
|
||||
&& (mHTMLEditor->NodesSameType(leftParent, rightParent)) )
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> topParent;
|
||||
leftParent->GetParentNode(getter_AddRefs(topParent));
|
||||
|
||||
if (nsHTMLEditUtils::IsParagraph(leftParent))
|
||||
{
|
||||
// first delete the selection
|
||||
@ -2780,25 +2874,20 @@ nsHTMLEditRules::CreateStyleForInsertText(nsISelection *aSelection, nsIDOMDocume
|
||||
res = mEditor->DeleteNode(rightNode);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
// register a rangeStore item that points at the new heirarchy.
|
||||
// This is so we can know where to put the selection after we call
|
||||
// RemoveStyleInside(). RemoveStyleInside() could remove any and all of those nodes,
|
||||
// so I have to use the range tracking system to find the right spot to put selection.
|
||||
nsRangeStore *rangeItem = new nsRangeStore();
|
||||
if (!rangeItem) return NS_ERROR_NULL_POINTER;
|
||||
rangeItem->startNode = newSelParent;
|
||||
rangeItem->endNode = newSelParent;
|
||||
rangeItem->startOffset = 0;
|
||||
rangeItem->endOffset = 0;
|
||||
mHTMLEditor->mRangeUpdater.RegisterRangeItem(rangeItem);
|
||||
// remove the style on this new heirarchy
|
||||
res = mHTMLEditor->RemoveStyleInside(leftNode, item->tag, &(item->attr));
|
||||
if (NS_FAILED(res)) return res;
|
||||
PRInt32 newSelOffset = 0;
|
||||
{
|
||||
// track the point at the new heirarchy.
|
||||
// This is so we can know where to put the selection after we call
|
||||
// RemoveStyleInside(). RemoveStyleInside() could remove any and all of those nodes,
|
||||
// so I have to use the range tracking system to find the right spot to put selection.
|
||||
nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater, address_of(newSelParent), &newSelOffset);
|
||||
res = mHTMLEditor->RemoveStyleInside(leftNode, item->tag, &(item->attr));
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
// reset our node offset values to the resulting new sel point
|
||||
mHTMLEditor->mRangeUpdater.DropRangeItem(rangeItem);
|
||||
node = rangeItem->startNode;
|
||||
offset = rangeItem->startOffset;
|
||||
delete rangeItem;
|
||||
node = newSelParent;
|
||||
offset = newSelOffset;
|
||||
}
|
||||
// we own item now (TakeClearProperty hands ownership to us)
|
||||
delete item;
|
||||
@ -4210,9 +4299,14 @@ nsHTMLEditRules::ReturnInHeader(nsISelection *aSelection,
|
||||
nsresult res = nsEditor::GetNodeLocation(aHeader, address_of(headerParent), &offset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// get ws code to adjust any ws
|
||||
nsCOMPtr<nsIDOMNode> selNode = aNode;
|
||||
res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, address_of(selNode), &aOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// split the header
|
||||
PRInt32 newOffset;
|
||||
res = mHTMLEditor->SplitNodeDeep( aHeader, aNode, aOffset, &newOffset);
|
||||
res = mHTMLEditor->SplitNodeDeep( aHeader, selNode, aOffset, &newOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// if the leftand heading is empty, put a mozbr in it
|
||||
@ -4306,8 +4400,12 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
|
||||
{
|
||||
PRInt32 newOffset;
|
||||
*aCancel = PR_TRUE;
|
||||
// get ws code to adjust any ws
|
||||
nsCOMPtr<nsIDOMNode> selNode = aNode;
|
||||
res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, address_of(selNode), &aOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// split the paragraph
|
||||
res = mHTMLEditor->SplitNodeDeep( aPara, aNode, aOffset, &newOffset);
|
||||
res = mHTMLEditor->SplitNodeDeep( aPara, selNode, aOffset, &newOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// get rid of the break
|
||||
res = mHTMLEditor->DeleteNode(sibling);
|
||||
@ -4344,8 +4442,12 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
|
||||
{
|
||||
PRInt32 newOffset;
|
||||
*aCancel = PR_TRUE;
|
||||
// get ws code to adjust any ws
|
||||
nsCOMPtr<nsIDOMNode> selNode = aNode;
|
||||
res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, address_of(selNode), &aOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// split the paragraph
|
||||
res = mHTMLEditor->SplitNodeDeep(aPara, aNode, aOffset, &newOffset);
|
||||
res = mHTMLEditor->SplitNodeDeep(aPara, selNode, aOffset, &newOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// get rid of the break
|
||||
res = mHTMLEditor->DeleteNode(sibling);
|
||||
@ -4383,8 +4485,12 @@ nsHTMLEditRules::ReturnInParagraph(nsISelection *aSelection,
|
||||
// else remove sibling br and split para
|
||||
PRInt32 newOffset;
|
||||
*aCancel = PR_TRUE;
|
||||
// get ws code to adjust any ws
|
||||
nsCOMPtr<nsIDOMNode> selNode = aNode;
|
||||
res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, address_of(selNode), &aOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// split the paragraph
|
||||
res = mHTMLEditor->SplitNodeDeep( aPara, aNode, aOffset, &newOffset);
|
||||
res = mHTMLEditor->SplitNodeDeep( aPara, selNode, aOffset, &newOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// get rid of the break
|
||||
res = mHTMLEditor->DeleteNode(nearNode);
|
||||
@ -4468,9 +4574,14 @@ nsHTMLEditRules::ReturnInListItem(nsISelection *aSelection,
|
||||
return res;
|
||||
}
|
||||
|
||||
// else we want a new list item at the same list level
|
||||
// else we want a new list item at the same list level.
|
||||
// get ws code to adjust any ws
|
||||
nsCOMPtr<nsIDOMNode> selNode = aNode;
|
||||
res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, address_of(selNode), &aOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// now split list item
|
||||
PRInt32 newOffset;
|
||||
res = mHTMLEditor->SplitNodeDeep( aListItem, aNode, aOffset, &newOffset);
|
||||
res = mHTMLEditor->SplitNodeDeep( aListItem, selNode, aOffset, &newOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
// hack: until I can change the damaged doc range code back to being
|
||||
// extra inclusive, I have to manually detect certain list items that
|
||||
@ -4944,64 +5055,19 @@ nsHTMLEditRules::AdjustSpecialBreaks(PRBool aSafeToAskFrames)
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsHTMLEditRules::AdjustWhitespace(nsISelection *aSelection)
|
||||
{
|
||||
nsCOMPtr<nsISupportsArray> arrayOfNodes;
|
||||
nsCOMPtr<nsISupports> isupports;
|
||||
PRUint32 nodeCount,j;
|
||||
nsresult res;
|
||||
|
||||
nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
|
||||
// get selection point
|
||||
nsCOMPtr<nsIDOMNode> selNode;
|
||||
PRInt32 selOffset;
|
||||
nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// special case for mDocChangeRange entirely in one text node.
|
||||
// This is an efficiency hack for normal typing in the editor.
|
||||
nsCOMPtr<nsIDOMNode> startNode, endNode;
|
||||
PRInt32 startOffset, endOffset;
|
||||
res = mDocChangeRange->GetStartContainer(getter_AddRefs(startNode));
|
||||
if (NS_FAILED(res)) return res;
|
||||
res = mDocChangeRange->GetStartOffset(&startOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
res = mDocChangeRange->GetEndContainer(getter_AddRefs(endNode));
|
||||
if (NS_FAILED(res)) return res;
|
||||
res = mDocChangeRange->GetEndOffset(&endOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
if (startNode == endNode)
|
||||
{
|
||||
nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
|
||||
if (nodeAsText)
|
||||
{
|
||||
res = DoTextNodeWhitespace(nodeAsText, startOffset, endOffset);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// gather up a list of text nodes
|
||||
nsEditableTextFunctor functor(mHTMLEditor);
|
||||
nsDOMIterator iter;
|
||||
res = iter.Init(mDocChangeRange);
|
||||
if (NS_FAILED(res)) return res;
|
||||
res = iter.MakeList(functor, address_of(arrayOfNodes));
|
||||
if (NS_FAILED(res)) return res;
|
||||
|
||||
// now adjust whitespace on node we found
|
||||
res = arrayOfNodes->Count(&nodeCount);
|
||||
if (NS_FAILED(res)) return res;
|
||||
for (j = 0; j < nodeCount; j++)
|
||||
{
|
||||
isupports = dont_AddRef(arrayOfNodes->ElementAt(0));
|
||||
nsCOMPtr<nsIDOMCharacterData> textNode( do_QueryInterface(isupports ) );
|
||||
arrayOfNodes->RemoveElementAt(0);
|
||||
res = DoTextNodeWhitespace(textNode, -1, -1);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
|
||||
return res;
|
||||
// ask whitespace object to tweak nbsp's
|
||||
return nsWSRunObject(mHTMLEditor, selNode, selOffset).AdjustWhitespace();
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsHTMLEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection aAction)
|
||||
{
|
||||
@ -5103,6 +5169,12 @@ nsHTMLEditRules::AdjustSelection(nsISelection *aSelection, nsIEditor::EDirection
|
||||
res = aSelection->Collapse(selNode,selOffset);
|
||||
if (NS_FAILED(res)) return res;
|
||||
}
|
||||
else if (nextNode && nsTextEditUtils::IsMozBR(nextNode))
|
||||
{
|
||||
// selection between br and mozbr. make it stick to mozbr
|
||||
// so that it will be on blank line.
|
||||
selPriv->SetInterlinePosition(PR_TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5892,6 +5964,20 @@ nsHTMLEditRules::DidDeleteText(nsIDOMCharacterData *aTextNode,
|
||||
return res;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLEditRules::WillDeleteRange(nsIDOMRange *aRange)
|
||||
{
|
||||
if (!mListenerEnabled) return NS_OK;
|
||||
// get the (collapsed) selection location
|
||||
return UpdateDocChangeRange(aRange);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLEditRules::DidDeleteRange(nsIDOMRange *aRange)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection)
|
||||
{
|
||||
|
@ -77,6 +77,8 @@ public:
|
||||
NS_IMETHOD DidInsertText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, const nsAReadableString &aString, nsresult aResult);
|
||||
NS_IMETHOD WillDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength);
|
||||
NS_IMETHOD DidDeleteText(nsIDOMCharacterData *aTextNode, PRInt32 aOffset, PRInt32 aLength, nsresult aResult);
|
||||
NS_IMETHOD WillDeleteRange(nsIDOMRange *aRange);
|
||||
NS_IMETHOD DidDeleteRange(nsIDOMRange *aRange);
|
||||
NS_IMETHOD WillDeleteSelection(nsISelection *aSelection);
|
||||
NS_IMETHOD DidDeleteSelection(nsISelection *aSelection);
|
||||
|
||||
@ -179,13 +181,13 @@ protected:
|
||||
|
||||
// data members
|
||||
protected:
|
||||
nsHTMLEditor *mHTMLEditor;
|
||||
nsCOMPtr<nsIDOMRange> mDocChangeRange;
|
||||
PRBool mListenerEnabled;
|
||||
PRBool mReturnInEmptyLIKillsList;
|
||||
nsCOMPtr<nsIDOMRange> mUtilRange;
|
||||
PRUint32 mJoinOffset; // need to remember an int across willJoin/didJoin...
|
||||
|
||||
nsHTMLEditor *mHTMLEditor;
|
||||
nsCOMPtr<nsIDOMRange> mDocChangeRange;
|
||||
PRBool mListenerEnabled;
|
||||
PRBool mReturnInEmptyLIKillsList;
|
||||
nsCOMPtr<nsIDOMRange> mUtilRange;
|
||||
PRUint32 mJoinOffset; // need to remember an int across willJoin/didJoin...
|
||||
|
||||
};
|
||||
|
||||
nsresult NS_NewHTMLEditRules(nsIEditRules** aInstancePtrResult);
|
||||
|
@ -64,6 +64,7 @@
|
||||
#include "nsIContentIterator.h"
|
||||
#include "nsEditorCID.h"
|
||||
#include "nsLayoutCID.h"
|
||||
#include "nsContentCID.h"
|
||||
#include "nsIDOMRange.h"
|
||||
#include "nsIDOMNSRange.h"
|
||||
#include "nsISupportsArray.h"
|
||||
@ -114,6 +115,7 @@ 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_IID(kRangeUtilsCID, NS_RANGEUTILS_CID);
|
||||
static NS_DEFINE_CID(kCDOMSelectionCID, NS_DOMSELECTION_CID);
|
||||
static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID);
|
||||
static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
|
||||
@ -232,6 +234,10 @@ NS_IMETHODIMP nsHTMLEditor::Init(nsIDOMDocument *aDoc,
|
||||
|
||||
nsresult result = NS_OK, rulesRes = NS_OK;
|
||||
|
||||
// make a range util object for comparing dom points
|
||||
mRangeHelper = do_CreateInstance(kRangeUtilsCID);
|
||||
if (!mRangeHelper) return NS_ERROR_NULL_POINTER;
|
||||
|
||||
// Init mEditProperty
|
||||
result = NS_NewEditProperty(getter_AddRefs(mEditProperty));
|
||||
if (NS_FAILED(result)) { return result; }
|
||||
@ -1267,7 +1273,10 @@ NS_IMETHODIMP nsHTMLEditor::TabInTable(PRBool inIsShift, PRBool *outHandled)
|
||||
return res;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsHTMLEditor::CreateBRImpl(nsCOMPtr<nsIDOMNode> *aInOutParent, PRInt32 *aInOutOffset, nsCOMPtr<nsIDOMNode> *outBRNode, EDirection aSelect)
|
||||
NS_IMETHODIMP nsHTMLEditor::CreateBRImpl(nsCOMPtr<nsIDOMNode> *aInOutParent,
|
||||
PRInt32 *aInOutOffset,
|
||||
nsCOMPtr<nsIDOMNode> *outBRNode,
|
||||
EDirection aSelect)
|
||||
{
|
||||
if (!aInOutParent || !*aInOutParent || !aInOutOffset || !outBRNode) return NS_ERROR_NULL_POINTER;
|
||||
*outBRNode = nsnull;
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "nsICSSLoader.h"
|
||||
#include "nsICSSLoaderObserver.h"
|
||||
#include "nsITableLayout.h"
|
||||
#include "nsIRangeUtils.h"
|
||||
|
||||
#include "nsEditRules.h"
|
||||
|
||||
@ -679,11 +680,14 @@ protected:
|
||||
// Used by GetFirstSelectedCell and GetNextSelectedCell
|
||||
PRInt32 mSelectedCellIndex;
|
||||
|
||||
nsCOMPtr<nsIRangeUtils> mRangeHelper;
|
||||
|
||||
public:
|
||||
|
||||
// friends
|
||||
friend class nsHTMLEditRules;
|
||||
friend class nsTextEditRules;
|
||||
friend class nsWSRunObject;
|
||||
|
||||
};
|
||||
|
||||
|
@ -119,7 +119,6 @@ nsWSRunObject::PrepareToJoinBlocks(nsHTMLEditor *aHTMLEd,
|
||||
if (!aLeftParent || !aRightParent || !aHTMLEd)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
nsresult res = NS_OK;
|
||||
|
||||
PRUint32 count;
|
||||
aHTMLEd->GetLengthOfDOMNode(aLeftParent, count);
|
||||
nsWSRunObject leftWSObj(aHTMLEd, aLeftParent, count);
|
||||
@ -168,9 +167,19 @@ nsWSRunObject::PrepareToDeleteNode(nsHTMLEditor *aHTMLEd,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsWSRunObject::PrepareToSplitAcrossBlocks(nsCOMPtr<nsIDOMNode> *aSplitNode, PRInt32 *aSplitOffset)
|
||||
nsWSRunObject::PrepareToSplitAcrossBlocks(nsHTMLEditor *aHTMLEd,
|
||||
nsCOMPtr<nsIDOMNode> *aSplitNode,
|
||||
PRInt32 *aSplitOffset)
|
||||
{
|
||||
return NS_OK;
|
||||
if (!aSplitNode || !aSplitOffset || !*aSplitNode || !aHTMLEd)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
nsresult res = NS_OK;
|
||||
|
||||
nsAutoTrackDOMPoint tracker(aHTMLEd->mRangeUpdater, aSplitNode, aSplitOffset);
|
||||
|
||||
nsWSRunObject wsObj(aHTMLEd, *aSplitNode, *aSplitOffset);
|
||||
|
||||
return wsObj.PrepareToSplitAcrossBlocksPriv();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
@ -193,29 +202,6 @@ nsWSRunObject::InsertBreak(nsCOMPtr<nsIDOMNode> *aInOutParent,
|
||||
res = FindRun(*aInOutParent, *aInOutOffset, &beforeRun, PR_FALSE);
|
||||
res = FindRun(*aInOutParent, *aInOutOffset, &afterRun, PR_TRUE);
|
||||
|
||||
// handle any changes needed to ws run before inserted br
|
||||
if (!beforeRun)
|
||||
{
|
||||
// dont need to do anything. just insert break. ws wont change.
|
||||
}
|
||||
else if (beforeRun->mType & eLeadingWS)
|
||||
{
|
||||
// dont need to do anything. just insert break. ws wont change.
|
||||
}
|
||||
else if (beforeRun->mType == eTrailingWS)
|
||||
{
|
||||
// need to delete the trailing ws that is before insertion point, because it
|
||||
// would become significant after break inserted.
|
||||
res = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset, *aInOutParent, *aInOutOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
else if (beforeRun->mType == eNormalWS)
|
||||
{
|
||||
// try to change an nbsp to a space, if possible, just to prevent nbsp proliferation
|
||||
res = CheckTrailingNBSP(beforeRun, *aInOutParent, *aInOutOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
|
||||
// handle any changes needed to ws run after inserted br
|
||||
if (!afterRun)
|
||||
{
|
||||
@ -252,6 +238,29 @@ nsWSRunObject::InsertBreak(nsCOMPtr<nsIDOMNode> *aInOutParent,
|
||||
}
|
||||
}
|
||||
|
||||
// handle any changes needed to ws run before inserted br
|
||||
if (!beforeRun)
|
||||
{
|
||||
// dont need to do anything. just insert break. ws wont change.
|
||||
}
|
||||
else if (beforeRun->mType & eLeadingWS)
|
||||
{
|
||||
// dont need to do anything. just insert break. ws wont change.
|
||||
}
|
||||
else if (beforeRun->mType == eTrailingWS)
|
||||
{
|
||||
// need to delete the trailing ws that is before insertion point, because it
|
||||
// would become significant after break inserted.
|
||||
res = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset, *aInOutParent, *aInOutOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
else if (beforeRun->mType == eNormalWS)
|
||||
{
|
||||
// try to change an nbsp to a space, if possible, just to prevent nbsp proliferation
|
||||
res = CheckTrailingNBSP(beforeRun, *aInOutParent, *aInOutOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
|
||||
// ready, aim, fire!
|
||||
return mHTMLEditor->CreateBRImpl(aInOutParent, aInOutOffset, outBRNode, aSelect);
|
||||
}
|
||||
@ -282,29 +291,6 @@ nsWSRunObject::InsertText(const nsAReadableString& aStringToInsert,
|
||||
res = FindRun(*aInOutParent, *aInOutOffset, &beforeRun, PR_FALSE);
|
||||
res = FindRun(*aInOutParent, *aInOutOffset, &afterRun, PR_TRUE);
|
||||
|
||||
// handle any changes needed to ws run before inserted text
|
||||
if (!beforeRun)
|
||||
{
|
||||
// dont need to do anything. just insert text. ws wont change.
|
||||
}
|
||||
else if (beforeRun->mType & eLeadingWS)
|
||||
{
|
||||
// dont need to do anything. just insert text. ws wont change.
|
||||
}
|
||||
else if (beforeRun->mType == eTrailingWS)
|
||||
{
|
||||
// need to delete the trailing ws that is before insertion point, because it
|
||||
// would become significant after text inserted.
|
||||
res = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset, *aInOutParent, *aInOutOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
else if (beforeRun->mType == eNormalWS)
|
||||
{
|
||||
// try to change an nbsp to a space, if possible, just to prevent nbsp proliferation
|
||||
res = CheckTrailingNBSP(beforeRun, *aInOutParent, *aInOutOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
|
||||
// handle any changes needed to ws run after inserted text
|
||||
if (!afterRun)
|
||||
{
|
||||
@ -328,6 +314,29 @@ nsWSRunObject::InsertText(const nsAReadableString& aStringToInsert,
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
|
||||
// handle any changes needed to ws run before inserted text
|
||||
if (!beforeRun)
|
||||
{
|
||||
// dont need to do anything. just insert text. ws wont change.
|
||||
}
|
||||
else if (beforeRun->mType & eLeadingWS)
|
||||
{
|
||||
// dont need to do anything. just insert text. ws wont change.
|
||||
}
|
||||
else if (beforeRun->mType == eTrailingWS)
|
||||
{
|
||||
// need to delete the trailing ws that is before insertion point, because it
|
||||
// would become significant after text inserted.
|
||||
res = DeleteChars(beforeRun->mStartNode, beforeRun->mStartOffset, *aInOutParent, *aInOutOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
else if (beforeRun->mType == eNormalWS)
|
||||
{
|
||||
// try to change an nbsp to a space, if possible, just to prevent nbsp proliferation
|
||||
res = CheckTrailingNBSP(beforeRun, *aInOutParent, *aInOutOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
|
||||
// next up, tweak head and tail of string as needed.
|
||||
// first the head:
|
||||
// there are a variety of circumstances that would require us to convert a
|
||||
@ -446,7 +455,7 @@ nsWSRunObject::DeleteWSBackward()
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
// finally, delete that ws
|
||||
return DeleteChars(startNode, startOffset, mNode, mOffset);
|
||||
return DeleteChars(startNode, startOffset, endNode, endOffset);
|
||||
}
|
||||
else if (point.mChar == nbsp)
|
||||
{
|
||||
@ -489,7 +498,7 @@ nsWSRunObject::DeleteWSForward()
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
// finally, delete that ws
|
||||
return DeleteChars(startNode, startOffset, mNode, mOffset);
|
||||
return DeleteChars(startNode, startOffset, endNode, endOffset);
|
||||
}
|
||||
else if (point.mChar == nbsp)
|
||||
{
|
||||
@ -515,6 +524,8 @@ nsWSRunObject::PriorVisibleNode(nsIDOMNode *aNode,
|
||||
PRInt32 *outVisOffset,
|
||||
PRInt16 *outType)
|
||||
{
|
||||
// Find first visible thing before the point. position outVisNode/outVisOffset
|
||||
// just _after_ that thing. If we don't find anything return start of ws.
|
||||
if (!aNode || !outVisNode || !outVisOffset || !outType)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
@ -536,7 +547,7 @@ nsWSRunObject::PriorVisibleNode(nsIDOMNode *aNode,
|
||||
if (point.mTextNode)
|
||||
{
|
||||
*outVisNode = do_QueryInterface(point.mTextNode);
|
||||
*outVisOffset = point.mOffset;
|
||||
*outVisOffset = point.mOffset+1;
|
||||
if (nsCRT::IsAsciiSpace(point.mChar) || (point.mChar==nbsp))
|
||||
{
|
||||
*outType = eNormalWS;
|
||||
@ -571,6 +582,8 @@ nsWSRunObject::NextVisibleNode (nsIDOMNode *aNode,
|
||||
PRInt32 *outVisOffset,
|
||||
PRInt16 *outType)
|
||||
{
|
||||
// Find first visible thing before the point. position outVisNode/outVisOffset
|
||||
// just _before_ that thing. If we don't find anything return end of ws.
|
||||
if (!aNode || !outVisNode || !outVisOffset || !outType)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
@ -619,6 +632,27 @@ nsWSRunObject::NextVisibleNode (nsIDOMNode *aNode,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsWSRunObject::AdjustWhitespace()
|
||||
{
|
||||
// this routine examines a run of ws and tries to get rid of some unneeded nbsp's,
|
||||
// replacing them with regualr ascii space if possible. Keeping things simple
|
||||
// for now and just trying to fix up the trailing ws in the run.
|
||||
if (!mLastNBSPNode) return NS_OK; // nothing to do!
|
||||
nsresult res = NS_OK;
|
||||
WSFragment *curRun = mStartRun;
|
||||
while (curRun)
|
||||
{
|
||||
// look for normal ws run
|
||||
if (curRun->mType == eNormalWS)
|
||||
{
|
||||
res = CheckTrailingNBSPOfRun(curRun);
|
||||
break;
|
||||
}
|
||||
curRun = curRun->mRight;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
@ -1061,6 +1095,9 @@ nsWSRunObject::GetRuns()
|
||||
else
|
||||
{
|
||||
// we might have trailing ws.
|
||||
// it so happens that *if* there is an nbsp at end, {mEndNode,mEndOffset-1}
|
||||
// will point to it, even though in general start/end points not
|
||||
// guaranteed to be in text nodes.
|
||||
if ((mLastNBSPNode == mEndNode) && (mLastNBSPOffset == (mEndOffset-1)))
|
||||
{
|
||||
// normal ws runs right up to adjacent block (nbsp next to block)
|
||||
@ -1099,6 +1136,9 @@ nsWSRunObject::GetRuns()
|
||||
mStartRun->mLeftType = mStartReason;
|
||||
|
||||
// we might have trailing ws.
|
||||
// it so happens that *if* there is an nbsp at end, {mEndNode,mEndOffset-1}
|
||||
// will point to it, even though in general start/end points not
|
||||
// guaranteed to be in text nodes.
|
||||
if ((mLastNBSPNode == mEndNode) && (mLastNBSPOffset == (mEndOffset-1)))
|
||||
{
|
||||
// set up next run
|
||||
@ -1388,6 +1428,7 @@ nsWSRunObject::PrepareToDeleteRangePriv(nsWSRunObject* aEndObject)
|
||||
res = FindRun(mNode, mOffset, &beforeRun, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
res = aEndObject->FindRun(aEndObject->mNode, aEndObject->mOffset, &afterRun, PR_TRUE);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
// trim after run of any leading ws
|
||||
if (afterRun && (afterRun->mType == eLeadingWS))
|
||||
@ -1444,6 +1485,56 @@ nsWSRunObject::PrepareToDeleteRangePriv(nsWSRunObject* aEndObject)
|
||||
return res;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsWSRunObject::PrepareToSplitAcrossBlocksPriv()
|
||||
{
|
||||
// used to prepare ws to be split across two blocks. The main issue
|
||||
// here is make sure normalWS doesn't end up becoming non-significant
|
||||
// leading or trailing ws after the split.
|
||||
nsresult res = NS_OK;
|
||||
|
||||
// get the runs before and after selection
|
||||
WSFragment *beforeRun, *afterRun;
|
||||
res = FindRun(mNode, mOffset, &beforeRun, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
res = FindRun(mNode, mOffset, &afterRun, PR_TRUE);
|
||||
|
||||
// adjust normal ws in afterRun if needed
|
||||
if (afterRun && (afterRun->mType == eNormalWS))
|
||||
{
|
||||
// make sure leading char of following ws is an nbsp, so that it will show up
|
||||
WSPoint point;
|
||||
GetCharAfter(mNode, mOffset, &point);
|
||||
if (nsCRT::IsAsciiSpace(point.mChar))
|
||||
{
|
||||
res = ConvertToNBSP(point);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
}
|
||||
|
||||
// adjust normal ws in beforeRun if needed
|
||||
if (beforeRun && (beforeRun->mType == eNormalWS))
|
||||
{
|
||||
// make sure trailing char of starting ws is an nbsp, so that it will show up
|
||||
WSPoint point;
|
||||
GetCharBefore(mNode, mOffset, &point);
|
||||
if (nsCRT::IsAsciiSpace(point.mChar))
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> wsStartNode, wsEndNode;
|
||||
PRInt32 wsStartOffset, wsEndOffset;
|
||||
res = GetAsciiWSBounds(eBoth, mNode, mOffset,
|
||||
address_of(wsStartNode), &wsStartOffset,
|
||||
address_of(wsEndNode), &wsEndOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
point.mTextNode = do_QueryInterface(wsStartNode);
|
||||
point.mOffset = wsStartOffset;
|
||||
res = ConvertToNBSP(point);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsWSRunObject::DeleteChars(nsIDOMNode *aStartNode, PRInt32 aStartOffset,
|
||||
nsIDOMNode *aEndNode, PRInt32 aEndOffset)
|
||||
@ -1704,14 +1795,13 @@ nsWSRunObject::GetAsciiWSBounds(PRInt16 aDir, nsIDOMNode *aNode, PRInt32 aOffset
|
||||
nsCOMPtr<nsIDOMNode> startNode, endNode;
|
||||
PRInt32 startOffset, endOffset;
|
||||
|
||||
WSPoint point, tmp;
|
||||
nsresult res = NS_OK;
|
||||
|
||||
if (aDir & eAfter)
|
||||
{
|
||||
WSPoint point, tmp;
|
||||
res = GetCharAfter(aNode, aOffset, &point);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
if (point.mTextNode)
|
||||
if (NS_SUCCEEDED(res) && point.mTextNode)
|
||||
{ // we found a text node, at least
|
||||
endNode = do_QueryInterface(point.mTextNode);
|
||||
endOffset = point.mOffset;
|
||||
@ -1733,9 +1823,9 @@ nsWSRunObject::GetAsciiWSBounds(PRInt16 aDir, nsIDOMNode *aNode, PRInt32 aOffset
|
||||
|
||||
if (aDir & eBefore)
|
||||
{
|
||||
WSPoint point, tmp;
|
||||
res = GetCharBefore(aNode, aOffset, &point);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
if (point.mTextNode)
|
||||
if (NS_SUCCEEDED(res) && point.mTextNode)
|
||||
{ // we found a text node, at least
|
||||
startNode = do_QueryInterface(point.mTextNode);
|
||||
startOffset = point.mOffset+1;
|
||||
@ -1973,10 +2063,68 @@ nsWSRunObject::GetWSPointBefore(nsIDOMNode *aNode, PRInt32 aOffset, WSPoint *out
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsWSRunObject::CheckTrailingNBSPOfRun(WSFragment *aRun)
|
||||
{
|
||||
// try to change an nbsp to a space, if possible, just to prevent nbsp proliferation.
|
||||
// examine what is before and after the trailing nbsp, if any.
|
||||
if (!aRun) return NS_ERROR_NULL_POINTER;
|
||||
WSPoint thePoint;
|
||||
PRBool leftCheck = PR_FALSE;
|
||||
PRBool rightCheck = PR_FALSE;
|
||||
|
||||
// confirm run is normalWS
|
||||
if (aRun->mType != eNormalWS) return NS_ERROR_FAILURE;
|
||||
|
||||
// first check for trailing nbsp
|
||||
nsresult res = GetCharBefore(aRun->mEndNode, aRun->mEndOffset, &thePoint);
|
||||
if (NS_SUCCEEDED(res) && thePoint.mChar == nbsp)
|
||||
{
|
||||
// now check that what is to the left of it is compatible with replacing nbsp with space
|
||||
WSPoint prevPoint;
|
||||
res = GetCharBefore(thePoint, &prevPoint);
|
||||
if (NS_SUCCEEDED(res))
|
||||
{
|
||||
if (!nsCRT::IsAsciiSpace(prevPoint.mChar)) leftCheck = PR_TRUE;
|
||||
}
|
||||
else if (aRun->mLeftType == eText) leftCheck = PR_TRUE;
|
||||
else if (aRun->mLeftType == eSpecial) leftCheck = PR_TRUE;
|
||||
if (leftCheck)
|
||||
{
|
||||
// now check that what is to the right of it is compatible with replacing nbsp with space
|
||||
if (aRun->mRightType == eText) rightCheck = PR_TRUE;
|
||||
if (aRun->mRightType == eSpecial) rightCheck = PR_TRUE;
|
||||
if (aRun->mRightType == eBreak) rightCheck = PR_TRUE;
|
||||
}
|
||||
if (leftCheck && rightCheck)
|
||||
{
|
||||
// now replace nbsp with space
|
||||
// first, insert a space
|
||||
nsCOMPtr<nsIDOMCharacterData> textNode(do_QueryInterface(thePoint.mTextNode));
|
||||
if (!textNode)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
|
||||
nsAutoString spaceStr(PRUnichar(32));
|
||||
res = mHTMLEditor->InsertTextIntoTextNodeImpl(spaceStr, textNode, thePoint.mOffset);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
// finally, delete that nbsp
|
||||
nsCOMPtr<nsIDOMNode> delNode(do_QueryInterface(thePoint.mTextNode));
|
||||
res = DeleteChars(delNode, thePoint.mOffset+1, delNode, thePoint.mOffset+2);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsWSRunObject::CheckTrailingNBSP(WSFragment *aRun, nsIDOMNode *aNode, PRInt32 aOffset)
|
||||
{
|
||||
// try to change an nbsp to a space, if possible, just to prevent nbsp proliferation
|
||||
// try to change an nbsp to a space, if possible, just to prevent nbsp proliferation.
|
||||
// this routine is called when we about to make this point in the ws abut an inserted break
|
||||
// or text, so we don't have to worry about what is after it. What is after it now will
|
||||
// end up after the inserted object.
|
||||
if (!aRun || !aNode) return NS_ERROR_NULL_POINTER;
|
||||
WSPoint thePoint;
|
||||
PRBool canConvert = PR_FALSE;
|
||||
nsresult res = GetCharBefore(aNode, aOffset, &thePoint);
|
||||
@ -2014,6 +2162,9 @@ nsresult
|
||||
nsWSRunObject::CheckLeadingNBSP(WSFragment *aRun, nsIDOMNode *aNode, PRInt32 aOffset)
|
||||
{
|
||||
// try to change an nbsp to a space, if possible, just to prevent nbsp proliferation
|
||||
// this routine is called when we about to make this point in the ws abut an inserted
|
||||
// text, so we don't have to worry about what is before it. What is before it now will
|
||||
// end up before the inserted text.
|
||||
WSPoint thePoint;
|
||||
PRBool canConvert = PR_FALSE;
|
||||
nsresult res = GetCharAfter(aNode, aOffset, &thePoint);
|
||||
|
@ -34,6 +34,24 @@ class nsIDOMDocument;
|
||||
class nsIDOMNode;
|
||||
class nsHTMLEditor;
|
||||
|
||||
// class nsWSRunObject represents the entire whitespace situation
|
||||
// around a given point. It collects up a list of nodes that contain
|
||||
// whitespace and categorizes in up to 3 different WSFragments (detailed
|
||||
// below). Each WSFragment is a collection of whitespace that is
|
||||
// either all insignificant, or that is significant. A WSFragment could
|
||||
// consist of insignificant whitespace because it is after a block
|
||||
// boundary or after a break. Or it could be insignificant because it
|
||||
// is before a block. Or it could be significant because it is
|
||||
// surrounded by text, or starts and ends with nbsps, etc.
|
||||
|
||||
// Throughout I refer to LeadingWS, NormalWS, TrailingWS. LeadingWS & TrailingWS
|
||||
// are runs of ascii ws that are insignificant (do not render) because they
|
||||
// are adjacent to block boundaries, or after a break. NormalWS is ws that
|
||||
// does cause soem rendering. Note that not all the ws in a NormalWS run need
|
||||
// render. For example, two ascii spaces surrounded by text on both sides
|
||||
// will only render as one space (in non-preformatted stlye html), yet both
|
||||
// spaces count as NormalWS. Together, they render as the one visible space.
|
||||
|
||||
class nsWSRunObject
|
||||
{
|
||||
public:
|
||||
@ -44,77 +62,147 @@ class nsWSRunObject
|
||||
kBackward
|
||||
} EWSDirection;
|
||||
|
||||
// constructor / destructor
|
||||
// constructor / destructor -----------------------------------------------
|
||||
nsWSRunObject(nsHTMLEditor *aEd);
|
||||
nsWSRunObject(nsHTMLEditor *aEd, nsIDOMNode *aNode, PRInt32 aOffset);
|
||||
~nsWSRunObject();
|
||||
|
||||
// public methods
|
||||
// public methods ---------------------------------------------------------
|
||||
|
||||
// PrepareToJoinBlocks fixes up ws at the end of aLeftParent and the
|
||||
// beginning of aRightParent in preperation for them to be joined.
|
||||
// example of fixup: trailingws in aLeftParent needs to be removed.
|
||||
static nsresult PrepareToJoinBlocks(nsHTMLEditor *aEd,
|
||||
nsIDOMNode *aLeftParent,
|
||||
nsIDOMNode *aRightParent);
|
||||
|
||||
// PrepareToDeleteRange fixes up ws before {aStartNode,aStartOffset}
|
||||
// and after {aEndNode,aEndOffset} in preperation for content
|
||||
// in that range to be deleted. Note that the nodes and offsets
|
||||
// are adjusted in response to any dom changes we make while
|
||||
// adjusting ws.
|
||||
// example of fixup: trailingws before {aStartNode,aStartOffset}
|
||||
// needs to be removed.
|
||||
static nsresult PrepareToDeleteRange(nsHTMLEditor *aHTMLEd,
|
||||
nsCOMPtr<nsIDOMNode> *aStartNode,
|
||||
PRInt32 *aStartOffset,
|
||||
nsCOMPtr<nsIDOMNode> *aEndNode,
|
||||
PRInt32 *aEndOffset);
|
||||
|
||||
// PrepareToDeleteNode fixes up ws before and after aNode in preperation
|
||||
// for aNode to be deleted.
|
||||
// example of fixup: trailingws before aNode needs to be removed.
|
||||
static nsresult PrepareToDeleteNode(nsHTMLEditor *aHTMLEd,
|
||||
nsIDOMNode *aNode);
|
||||
static nsresult PrepareToSplitAcrossBlocks(nsCOMPtr<nsIDOMNode> *aSplitNode,
|
||||
|
||||
// PrepareToSplitAcrossBlocks fixes up ws before and after
|
||||
// {aSplitNode,aSplitOffset} in preperation for a block
|
||||
// parent to be split. Note that the aSplitNode and aSplitOffset
|
||||
// are adjusted in response to any dom changes we make while
|
||||
// adjusting ws.
|
||||
// example of fixup: normalws before {aSplitNode,aSplitOffset}
|
||||
// needs to end with nbsp.
|
||||
static nsresult PrepareToSplitAcrossBlocks(nsHTMLEditor *aHTMLEd,
|
||||
nsCOMPtr<nsIDOMNode> *aSplitNode,
|
||||
PRInt32 *aSplitOffset);
|
||||
|
||||
// InsertBreak inserts a br node at {aInOutParent,aInOutOffset}
|
||||
// and makes any needed adjustments to ws around that point.
|
||||
// example of fixup: normalws after {aInOutParent,aInOutOffset}
|
||||
// needs to begin with nbsp.
|
||||
nsresult InsertBreak(nsCOMPtr<nsIDOMNode> *aInOutParent,
|
||||
PRInt32 *aInOutOffset,
|
||||
nsCOMPtr<nsIDOMNode> *outBRNode,
|
||||
nsIEditor::EDirection aSelect);
|
||||
|
||||
// InsertText inserts a string at {aInOutParent,aInOutOffset}
|
||||
// and makes any needed adjustments to ws around that point.
|
||||
// example of fixup: trailingws before {aInOutParent,aInOutOffset}
|
||||
// needs to be removed.
|
||||
nsresult InsertText(const nsAReadableString& aStringToInsert,
|
||||
nsCOMPtr<nsIDOMNode> *aInOutNode,
|
||||
PRInt32 *aInOutOffset,
|
||||
nsIDOMDocument *aDoc);
|
||||
|
||||
// DeleteWSBackward deletes a single visible piece of ws before
|
||||
// the ws point (the point to create the wsRunObject, passed to
|
||||
// its constructor). It makes any needed conversion to adjacent
|
||||
// ws to retain its significance.
|
||||
nsresult DeleteWSBackward();
|
||||
|
||||
// DeleteWSForward deletes a single visible piece of ws after
|
||||
// the ws point (the point to create the wsRunObject, passed to
|
||||
// its constructor). It makes any needed conversion to adjacent
|
||||
// ws to retain its significance.
|
||||
nsresult DeleteWSForward();
|
||||
|
||||
// PriorVisibleNode returns the first piece of visible thing
|
||||
// before {aNode,aOffset}. If there is no visible ws qualifying
|
||||
// it returns what is before the ws run. Note that
|
||||
// {outVisNode,outVisOffset} is set to just AFTER the visible
|
||||
// object.
|
||||
nsresult PriorVisibleNode(nsIDOMNode *aNode,
|
||||
PRInt32 aOffset,
|
||||
nsCOMPtr<nsIDOMNode> *outVisNode,
|
||||
PRInt32 *outVisOffset,
|
||||
PRInt16 *outType);
|
||||
|
||||
// NextVisibleNode returns the first piece of visible thing
|
||||
// after {aNode,aOffset}. If there is no visible ws qualifying
|
||||
// it returns what is after the ws run. Note that
|
||||
// {outVisNode,outVisOffset} is set to just BEFORE the visible
|
||||
// object.
|
||||
nsresult NextVisibleNode (nsIDOMNode *aNode,
|
||||
PRInt32 aOffset,
|
||||
nsCOMPtr<nsIDOMNode> *outVisNode,
|
||||
PRInt32 *outVisOffset,
|
||||
PRInt16 *outType);
|
||||
|
||||
// AdjustWhitespace examines the ws object for nbsp's that can
|
||||
// be safely converted to regular ascii space and converts them.
|
||||
nsresult AdjustWhitespace();
|
||||
|
||||
// public enums ---------------------------------------------------------
|
||||
enum {eNone = 0};
|
||||
enum {eLeadingWS = 1};
|
||||
enum {eTrailingWS = 1 << 1};
|
||||
enum {eNormalWS = 1 << 2};
|
||||
enum {eText = 1 << 3};
|
||||
enum {eSpecial = 1 << 4};
|
||||
enum {eBreak = 1 << 5};
|
||||
enum {eOtherBlock = 1 << 6};
|
||||
enum {eThisBlock = 1 << 7};
|
||||
enum {eBlock = eOtherBlock | eThisBlock};
|
||||
enum {eLeadingWS = 1}; // leading insignificant ws, ie, after block or br
|
||||
enum {eTrailingWS = 1 << 1}; // trailing insignificant ws, ie, before block
|
||||
enum {eNormalWS = 1 << 2}; // normal significant ws, ie, after text, image, ...
|
||||
enum {eText = 1 << 3}; // indicates regular (non-ws) text
|
||||
enum {eSpecial = 1 << 4}; // indicates an inline non-container, like image
|
||||
enum {eBreak = 1 << 5}; // indicates a br node
|
||||
enum {eOtherBlock = 1 << 6}; // indicates a block other than one ws run is in
|
||||
enum {eThisBlock = 1 << 7}; // indicates the block ws run is in
|
||||
enum {eBlock = eOtherBlock | eThisBlock}; // block found
|
||||
|
||||
enum {eBefore = 1};
|
||||
enum {eAfter = 1 << 1};
|
||||
enum {eBoth = eBefore | eAfter};
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
// WSFragment struct ---------------------------------------------------------
|
||||
// WSFragment represents a single run of ws (all leadingws, or all normalws,
|
||||
// or all trailingws, or all leading+trailingws). Note that this single run may
|
||||
// still span multiple nodes.
|
||||
struct WSFragment
|
||||
{
|
||||
nsCOMPtr<nsIDOMNode> mStartNode;
|
||||
nsCOMPtr<nsIDOMNode> mEndNode;
|
||||
PRInt16 mStartOffset;
|
||||
PRInt16 mEndOffset;
|
||||
PRInt16 mType, mLeftType, mRightType;
|
||||
WSFragment *mLeft, *mRight;
|
||||
nsCOMPtr<nsIDOMNode> mStartNode; // node where ws run starts
|
||||
nsCOMPtr<nsIDOMNode> mEndNode; // node where ws run ends
|
||||
PRInt16 mStartOffset; // offset where ws run starts
|
||||
PRInt16 mEndOffset; // offset where ws run ends
|
||||
PRInt16 mType, mLeftType, mRightType; // type of ws, and what is to left and right of it
|
||||
WSFragment *mLeft, *mRight; // other ws runs to left or right. may be null.
|
||||
|
||||
WSFragment() : mStartNode(0),mEndNode(0),mStartOffset(0),
|
||||
mEndOffset(0),mType(0),mLeftType(0),
|
||||
mRightType(0),mLeft(0),mRight(0) {}
|
||||
};
|
||||
|
||||
// WSPoint struct ------------------------------------------------------------
|
||||
// A WSPoint struct represents a unique location within the ws run. It is
|
||||
// always within a textnode that is one of the nodes stored in the list
|
||||
// in the wsRunObject. For convenience, the character at that point is also
|
||||
// stored in the struct.
|
||||
struct WSPoint
|
||||
{
|
||||
nsCOMPtr<nsITextContent> mTextNode;
|
||||
@ -128,7 +216,8 @@ class nsWSRunObject
|
||||
mTextNode(aTextNode),mOffset(aOffset),mChar(aChar) {}
|
||||
};
|
||||
|
||||
// protected methods
|
||||
// protected methods ---------------------------------------------------------
|
||||
// tons of utility methods.
|
||||
nsresult GetWSNodes();
|
||||
nsresult GetRuns();
|
||||
void ClearRuns();
|
||||
@ -150,6 +239,7 @@ class nsWSRunObject
|
||||
nsIDOMNode *aBlockParent,
|
||||
nsCOMPtr<nsIDOMNode> *aNextNode);
|
||||
nsresult PrepareToDeleteRangePriv(nsWSRunObject* aEndObject);
|
||||
nsresult PrepareToSplitAcrossBlocksPriv();
|
||||
nsresult DeleteChars(nsIDOMNode *aStartNode, PRInt32 aStartOffset,
|
||||
nsIDOMNode *aEndNode, PRInt32 aEndOffset);
|
||||
nsresult GetCharAfter(nsIDOMNode *aNode, PRInt32 aOffset, WSPoint *outPoint);
|
||||
@ -164,26 +254,36 @@ class nsWSRunObject
|
||||
PRUnichar GetCharAt(nsITextContent *aTextNode, PRInt32 aOffset);
|
||||
nsresult GetWSPointAfter(nsIDOMNode *aNode, PRInt32 aOffset, WSPoint *outPoint);
|
||||
nsresult GetWSPointBefore(nsIDOMNode *aNode, PRInt32 aOffset, WSPoint *outPoint);
|
||||
nsresult CheckTrailingNBSPOfRun(WSFragment *aRun);
|
||||
nsresult CheckTrailingNBSP(WSFragment *aRun, nsIDOMNode *aNode, PRInt32 aOffset);
|
||||
nsresult CheckLeadingNBSP(WSFragment *aRun, nsIDOMNode *aNode, PRInt32 aOffset);
|
||||
|
||||
// member variables
|
||||
nsCOMPtr<nsIDOMNode> mNode;
|
||||
PRInt32 mOffset;
|
||||
nsCOMPtr<nsIDOMNode> mStartNode;
|
||||
PRInt32 mStartOffset;
|
||||
PRInt16 mStartReason;
|
||||
nsCOMPtr<nsIDOMNode> mEndNode;
|
||||
PRInt32 mEndOffset;
|
||||
PRInt16 mEndReason;
|
||||
nsCOMPtr<nsIDOMNode> mFirstNBSPNode;
|
||||
PRInt32 mFirstNBSPOffset;
|
||||
nsCOMPtr<nsIDOMNode> mLastNBSPNode;
|
||||
PRInt32 mLastNBSPOffset;
|
||||
nsCOMPtr<nsISupportsArray> mNodeArray;
|
||||
WSFragment *mStartRun;
|
||||
WSFragment *mEndRun;
|
||||
nsHTMLEditor *mHTMLEditor; // non-owning.
|
||||
// member variables ---------------------------------------------------------
|
||||
|
||||
nsCOMPtr<nsIDOMNode> mNode; // the node passed to our constructor
|
||||
PRInt32 mOffset; // the offset passed to our contructor
|
||||
// together, the above represent the point at which we are building up ws info.
|
||||
|
||||
nsCOMPtr<nsIDOMNode> mStartNode; // node/offet where ws starts
|
||||
PRInt32 mStartOffset; // ...
|
||||
PRInt16 mStartReason; // reason why ws starts (eText, eOtherBlock, etc)
|
||||
|
||||
nsCOMPtr<nsIDOMNode> mEndNode; // node/offet where ws ends
|
||||
PRInt32 mEndOffset; // ...
|
||||
PRInt16 mEndReason; // reason why ws ends (eText, eOtherBlock, etc)
|
||||
|
||||
nsCOMPtr<nsIDOMNode> mFirstNBSPNode; // location of first nbsp in ws run, if any
|
||||
PRInt32 mFirstNBSPOffset; // ...
|
||||
|
||||
nsCOMPtr<nsIDOMNode> mLastNBSPNode; // location of last nbsp in ws run, if any
|
||||
PRInt32 mLastNBSPOffset; // ...
|
||||
|
||||
nsCOMPtr<nsISupportsArray> mNodeArray;//the list of nodes containing ws in this run
|
||||
|
||||
WSFragment *mStartRun; // the first WSFragment in the run
|
||||
WSFragment *mEndRun; // the last WSFragment in the run, may be same as first
|
||||
|
||||
nsHTMLEditor *mHTMLEditor; // non-owning.
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user