diff --git a/editor/base/nsHTMLEditRules.cpp b/editor/base/nsHTMLEditRules.cpp
index 76030917e9c..a7d48136023 100644
--- a/editor/base/nsHTMLEditRules.cpp
+++ b/editor/base/nsHTMLEditRules.cpp
@@ -109,6 +109,8 @@ nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection,
return WillAlign(aSelection, info->alignType, aCancel);
case kMakeBasicBlock:
return WillMakeBasicBlock(aSelection, info->blockType, aCancel);
+ case kRemoveList:
+ return WillRemoveList(aSelection, info->bOrdered, aCancel);
case kInsertElement:
return WillInsert(aSelection, aCancel);
}
@@ -129,6 +131,9 @@ nsHTMLEditRules::DidDoAction(nsIDOMSelection *aSelection,
return NS_OK;
}
+ // clean up any empty nodes in the selection
+ CleanUpSelection(aSelection);
+
return nsTextEditRules::DidDoAction(aSelection, aInfo, aResult);
}
@@ -157,6 +162,37 @@ nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
// Insert Break txns don't auto merge with insert text txns
nsAutoEditBatch beginBatching(mEditor);
+ // if the selection isn't collapsed, delete it.
+ PRBool bCollapsed;
+ res = aSelection->GetIsCollapsed(&bCollapsed);
+ if (NS_FAILED(res)) return res;
+ if (!bCollapsed)
+ {
+ res = mEditor->DeleteSelection(nsIEditor::eDoNothing);
+ if (NS_FAILED(res)) return res;
+ }
+
+ // split any mailcites in the way
+ if (1 || mFlags & nsIHTMLEditor::eEditorMailMask)
+ {
+ nsCOMPtr citeNode, selNode;
+ PRInt32 selOffset, newOffset;
+ res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset);
+ if (NS_FAILED(res)) return res;
+ res = GetTopEnclosingMailCite(selNode, &citeNode);
+ if (NS_FAILED(res)) return res;
+
+ if (citeNode)
+ {
+ res = mEditor->SplitNodeDeep(citeNode, selNode, selOffset, &newOffset);
+ if (NS_FAILED(res)) return res;
+ res = citeNode->GetParentNode(getter_AddRefs(selNode));
+ if (NS_FAILED(res)) return res;
+ res = aSelection->Collapse(selNode, newOffset);
+ if (NS_FAILED(res)) return res;
+ }
+ }
+
// strategy: there are simple cases and harder cases. The harder cases
// we handle recursively by breaking them into a series of simple cases.
// The simple cases are:
@@ -227,6 +263,10 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel)
res = WillInsert(aSelection, aCancel);
if (NS_FAILED(res)) return res;
+ // initialize out param
+ // we want to ignore result of WillInsert()
+ *aCancel = PR_FALSE;
+
// if the selection isn't collapsed, delete it.
PRBool bCollapsed;
res = aSelection->GetIsCollapsed(&bCollapsed);
@@ -300,6 +340,13 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESe
// initialize out param
*aCancel = PR_FALSE;
+ // if there is only bogus content, cancel the operation
+ if (mBogusNode)
+ {
+ *aCancel = PR_TRUE;
+ return NS_OK;
+ }
+
nsresult res = NS_OK;
PRBool bCollapsed;
@@ -344,6 +391,12 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESe
// are the blocks of same type?
nsCOMPtr leftParent = mEditor->GetBlockNodeParent(priorNode);
nsCOMPtr rightParent = mEditor->GetBlockNodeParent(node);
+
+ // if leftParent or rightParent is null, it's because the
+ // corresponding selection endpoint is in the body node.
+ if (!leftParent || !rightParent)
+ return NS_OK; // bail to default
+
nsCOMPtr leftAtom = mEditor->GetTag(leftParent);
nsCOMPtr rightAtom = mEditor->GetTag(rightParent);
@@ -411,30 +464,34 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESe
// else not in text node; we need to find right place to act on
else
{
-
- // XXX add (aAction == nsIEditor::eDeleteNext) case!!
+ nsCOMPtr nodeToDelete;
- nsCOMPtr nodeToBackspace;
-
- res = mEditor->GetPriorNode(node, offset, PR_TRUE, getter_AddRefs(nodeToBackspace));
+ if (aAction == nsIEditor::eDeletePrevious)
+ res = mEditor->GetPriorNode(node, offset, PR_TRUE, getter_AddRefs(nodeToDelete));
+ else if (aAction == nsIEditor::eDeleteNext)
+ res = mEditor->GetNextNode(node, offset, PR_TRUE, getter_AddRefs(nodeToDelete));
+ else
+ return NS_OK;
+
if (NS_FAILED(res)) return res;
- if (!nodeToBackspace) return NS_ERROR_NULL_POINTER;
+ if (!nodeToDelete) return NS_ERROR_NULL_POINTER;
// if this node is text node, adjust selection
- if (nsEditor::IsTextNode(nodeToBackspace))
+ if (nsEditor::IsTextNode(nodeToDelete))
{
- PRUint32 len;
+ PRUint32 selPoint = 0;
nsCOMPtrnodeAsText;
- nodeAsText = do_QueryInterface(nodeToBackspace);
- nodeAsText->GetLength(&len);
- res = aSelection->Collapse(nodeToBackspace,len);
+ nodeAsText = do_QueryInterface(nodeToDelete);
+ if (aAction == nsIEditor::eDeletePrevious)
+ nodeAsText->GetLength(&selPoint);
+ res = aSelection->Collapse(nodeToDelete,selPoint);
return res;
}
else
{
// editable leaf node is not text; delete it.
// that's the default behavior
- res = nsEditor::GetNodeLocation(nodeToBackspace, &node, &offset);
+ res = nsEditor::GetNodeLocation(nodeToDelete, &node, &offset);
if (NS_FAILED(res)) return res;
// adjust selection to be right after it, for benefit of
// deletion code
@@ -538,13 +595,18 @@ nsresult
nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBool *aCancel)
{
if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; }
+
+ nsresult res = WillInsert(aSelection, aCancel);
+ if (NS_FAILED(res)) return res;
+
// initialize out param
+ // we want to ignore result of WillInsert()
*aCancel = PR_FALSE;
+
nsAutoString blockType("ul");
if (aOrdered) blockType = "ol";
nsAutoSelectionReset selectionResetter(aSelection);
- nsresult res;
PRBool outMakeEmpty;
res = ShouldMakeEmptyBlock(aSelection, &blockType, &outMakeEmpty);
@@ -763,6 +825,95 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo
}
+nsresult
+nsHTMLEditRules::WillRemoveList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBool *aCancel)
+{
+ if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; }
+ // initialize out param
+ *aCancel = PR_TRUE;
+
+ nsAutoString blockType("ul");
+ if (aOrdered) blockType = "ol";
+
+ nsAutoSelectionReset selectionResetter(aSelection);
+
+ nsCOMPtr arrayOfRanges;
+ nsresult res = GetPromotedRanges(aSelection, &arrayOfRanges, kMakeList);
+ if (NS_FAILED(res)) return res;
+
+ // use these ranges to contruct a list of nodes to act on.
+ nsCOMPtr arrayOfNodes;
+ res = GetNodesForOperation(arrayOfRanges, &arrayOfNodes, kMakeList);
+ if (NS_FAILED(res)) return res;
+
+ // Remove all non-editable nodes. Leave them be.
+
+ PRUint32 listCount;
+ PRInt32 i;
+ arrayOfNodes->Count(&listCount);
+ for (i=listCount-1; i>=0; i--)
+ {
+ nsCOMPtr isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i));
+ nsCOMPtr testNode( do_QueryInterface(isupports ) );
+ if (!mEditor->IsEditable(testNode))
+ {
+ arrayOfNodes->RemoveElementAt(i);
+ }
+ }
+
+ // Only act on lists or list items in the array
+ nsCOMPtr curParent;
+ for (i=0; i isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i));
+ nsCOMPtr curNode( do_QueryInterface(isupports ) );
+ PRInt32 offset;
+ res = nsEditor::GetNodeLocation(curNode, &curParent, &offset);
+ if (NS_FAILED(res)) return res;
+
+ if (IsListItem(curNode)) // unlist this listitem
+ {
+ PRBool bOutOfList;
+ do
+ {
+ res = PopListItem(curNode, &bOutOfList);
+ if (NS_FAILED(res)) return res;
+ } while (!bOutOfList); // keep popping it out until it's not in a list anymore
+ }
+ else if (IsList(curNode)) // node is a list, move list items out
+ {
+ nsCOMPtr child;
+ curNode->GetLastChild(getter_AddRefs(child));
+
+ while (child)
+ {
+ if (IsListItem(child))
+ {
+ PRBool bOutOfList;
+ do
+ {
+ res = PopListItem(child, &bOutOfList);
+ if (NS_FAILED(res)) return res;
+ } while (!bOutOfList); // keep popping it out until it's not in a list anymore
+ }
+ else
+ {
+ // delete any non- list items for now
+ res = mEditor->DeleteNode(child);
+ if (NS_FAILED(res)) return res;
+ }
+ curNode->GetLastChild(getter_AddRefs(child));
+ }
+ // delete the now-empty list
+ res = mEditor->DeleteNode(curNode);
+ if (NS_FAILED(res)) return res;
+ }
+ }
+ return res;
+}
+
+
nsresult
nsHTMLEditRules::WillMakeBasicBlock(nsIDOMSelection *aSelection, const nsString *aBlockType, PRBool *aCancel)
{
@@ -800,11 +951,15 @@ nsresult
nsHTMLEditRules::WillIndent(nsIDOMSelection *aSelection, PRBool *aCancel)
{
if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; }
- // initialize out param
- *aCancel = PR_TRUE;
+ nsresult res = WillInsert(aSelection, aCancel);
+ if (NS_FAILED(res)) return res;
+
+ // initialize out param
+ // we want to ignore result of WillInsert()
+ *aCancel = PR_TRUE;
+
nsAutoSelectionReset selectionResetter(aSelection);
- nsresult res;
// convert the selection ranges into "promoted" selection ranges:
// this basically just expands the range to include the immediate
@@ -956,67 +1111,34 @@ nsHTMLEditRules::WillOutdent(nsIDOMSelection *aSelection, PRBool *aCancel)
}
else // we are moving a list item, but not whole list
{
- // if it's first or last list item, dont need to split the list
- // otherwise we do.
- nsCOMPtr curParPar;
- PRInt32 parOffset;
- res = nsEditor::GetNodeLocation(curParent, &curParPar, &parOffset);
+ PRBool bOutOfList;
+ res = PopListItem(curNode, &bOutOfList);
if (NS_FAILED(res)) return res;
-
- PRBool bIsFirstListItem;
- res = IsFirstEditableChild(curNode, &bIsFirstListItem);
- if (NS_FAILED(res)) return res;
-
- PRBool bIsLastListItem;
- res = IsLastEditableChild(curNode, &bIsLastListItem);
- if (NS_FAILED(res)) return res;
-
- if (!bIsFirstListItem && !bIsLastListItem)
- {
- // split the list
- nsCOMPtr newBlock;
- res = mEditor->SplitNode(curParent, offset, getter_AddRefs(newBlock));
- if (NS_FAILED(res)) return res;
- }
-
- if (!bIsFirstListItem) parOffset++;
-
- res = mEditor->DeleteNode(curNode);
- if (NS_FAILED(res)) return res;
- res = mEditor->InsertNode(curNode, curParPar, parOffset);
- if (NS_FAILED(res)) return res;
-
- // convert list items to divs if we promoted them out of list
- if (!IsList(curParPar) && IsListItem(curNode))
- {
- nsAutoString blockType("div");
- nsCOMPtr newBlock;
- res = ReplaceContainer(curNode,&newBlock,blockType);
- if (NS_FAILED(res)) return res;
- }
}
}
else if (IsList(curNode)) // node is a list, but parent is non-list: move list items out
{
nsCOMPtr child;
curNode->GetLastChild(getter_AddRefs(child));
-
while (child)
{
- res = mEditor->DeleteNode(child);
- if (NS_FAILED(res)) return res;
- res = mEditor->InsertNode(child, curParent, offset);
- if (NS_FAILED(res)) return res;
-
if (IsListItem(child))
{
- nsAutoString blockType("div");
- nsCOMPtr newBlock;
- res = ReplaceContainer(child,&newBlock,blockType);
+ PRBool bOutOfList;
+ res = PopListItem(child, &bOutOfList);
+ if (NS_FAILED(res)) return res;
+ }
+ else
+ {
+ // delete any non- list items for now
+ res = mEditor->DeleteNode(child);
if (NS_FAILED(res)) return res;
}
curNode->GetLastChild(getter_AddRefs(child));
}
+ // delete the now-empty list
+ res = mEditor->DeleteNode(curNode);
+ if (NS_FAILED(res)) return res;
}
else if (transitionList[i]) // not list related - look for enclosing blockquotes and remove
{
@@ -1045,11 +1167,15 @@ nsresult
nsHTMLEditRules::WillAlign(nsIDOMSelection *aSelection, const nsString *alignType, PRBool *aCancel)
{
if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; }
- // initialize out param
- *aCancel = PR_FALSE;
+ nsresult res = WillInsert(aSelection, aCancel);
+ if (NS_FAILED(res)) return res;
+
+ // initialize out param
+ // we want to ignore result of WillInsert()
+ *aCancel = PR_FALSE;
+
nsAutoSelectionReset selectionResetter(aSelection);
- nsresult res = NS_OK;
PRBool outMakeEmpty;
res = ShouldMakeEmptyBlock(aSelection, alignType, &outMakeEmpty);
@@ -1318,6 +1444,29 @@ nsHTMLEditRules::IsDiv(nsIDOMNode *node)
}
+///////////////////////////////////////////////////////////////////////////
+// IsMailCite: true if node an html blockquote with type=cite
+//
+PRBool
+nsHTMLEditRules::IsMailCite(nsIDOMNode *node)
+{
+ NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsMailCite");
+ if (IsBlockquote(node))
+ {
+ nsCOMPtr bqElem = do_QueryInterface(node);
+ nsAutoString typeAttrName("type");
+ nsAutoString typeAttrVal;
+ nsresult res = bqElem->GetAttribute(typeAttrName, typeAttrVal);
+ if (NS_SUCCEEDED(res))
+ {
+ if (typeAttrVal == "cite")
+ return PR_TRUE;
+ }
+ }
+ return PR_FALSE;
+}
+
+
///////////////////////////////////////////////////////////////////////////
// IsEmptyBlock: figure out if aNode is (or is inside) an empty block.
// A block can have children and still be considered empty,
@@ -1330,27 +1479,48 @@ nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock)
*outIsEmptyBlock = PR_TRUE;
nsresult res = NS_OK;
- nsCOMPtr blockContent;
- if (nsEditor::IsBlockNode(aNode)) blockContent = do_QueryInterface(aNode);
- else
+ nsCOMPtr nodeToTest;
+ if (nsEditor::IsBlockNode(aNode)) nodeToTest = do_QueryInterface(aNode);
+ else nsCOMPtr block;
+
+ if (!nodeToTest) return NS_ERROR_NULL_POINTER;
+ return IsEmptyNode(nodeToTest, outIsEmptyBlock);
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+// IsEmptyNode: figure out if aNode is an empty node.
+// A block can have children and still be considered empty,
+// if the children are empty or non-editable.
+//
+nsresult
+nsHTMLEditRules::IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyNode)
+{
+ if (!aNode || !outIsEmptyNode) return NS_ERROR_NULL_POINTER;
+ *outIsEmptyNode = PR_TRUE;
+
+ // effeciency hack - special case if it's a text node
+ if (nsEditor::IsTextNode(aNode))
{
- nsCOMPtr block;
- res = nsEditor::GetBlockParent(aNode, getter_AddRefs(block));
- if (NS_FAILED(res)) return res;
- blockContent = do_QueryInterface(block);
+ PRUint32 length = 0;
+ nsCOMPtrnodeAsText;
+ nodeAsText = do_QueryInterface(aNode);
+ nodeAsText->GetLength(&length);
+ if (length) *outIsEmptyNode = PR_FALSE;
+ return NS_OK;
}
-
- if (!blockContent) return NS_ERROR_NULL_POINTER;
-
- // iterate over block. if no children, or all children are either
- // empty text nodes or non-editable, then block qualifies as empty
+
+ // iterate over node. if no children, or all children are either
+ // empty text nodes or non-editable, then node qualifies as empty
nsCOMPtr iter;
- res = nsComponentManager::CreateInstance(kContentIteratorCID,
+ nsCOMPtr nodeAsContent = do_QueryInterface(aNode);
+ if (!nodeAsContent) return NS_ERROR_FAILURE;
+ nsresult res = nsComponentManager::CreateInstance(kContentIteratorCID,
nsnull,
nsIContentIterator::GetIID(),
getter_AddRefs(iter));
if (NS_FAILED(res)) return res;
- res = iter->Init(blockContent);
+ res = iter->Init(nodeAsContent);
if (NS_FAILED(res)) return res;
while (NS_COMFALSE == iter->IsDone())
@@ -1371,14 +1541,14 @@ nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock)
nsCOMPtrnodeAsText;
nodeAsText = do_QueryInterface(node);
nodeAsText->GetLength(&length);
- if (length) *outIsEmptyBlock = PR_FALSE;
+ if (length) *outIsEmptyNode = PR_FALSE;
}
else // an editable, non-text node. we aren't an empty block
{
// is it the node we are iterating over?
if (node.get() == aNode) break;
// otherwise it ain't empty
- *outIsEmptyBlock = PR_FALSE;
+ *outIsEmptyNode = PR_FALSE;
}
}
res = iter->Next();
@@ -2538,6 +2708,10 @@ nsHTMLEditRules::IsLastEditableChild( nsIDOMNode *aNode, PRBool *aOutIsLast)
}
+///////////////////////////////////////////////////////////////////////////
+// JoinNodesSmart: join two nodes, doing whatever makes sense for their
+// children (which often means joining them, too).
+// aNodeLeft & aNodeRight must be same type of node.
nsresult
nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft,
nsIDOMNode *aNodeRight,
@@ -2553,8 +2727,8 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft,
nsresult res = NS_OK;
// caller responsible for:
- // left & right node are smae type
- // left & right node have smae parent
+ // left & right node are same type
+ // left & right node have same parent
nsCOMPtr parent;
aNodeLeft->GetParentNode(getter_AddRefs(parent));
@@ -2587,8 +2761,181 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft,
else
{
// for list items, divs, etc, merge smart
- res = JoinNodesSmart(aNodeLeft, aNodeRight, aOutMergeParent, aOutMergeOffset);
+ res = mEditor->JoinNodes(aNodeLeft, aNodeRight, parent);
if (NS_FAILED(res)) return res;
+ // figure out newNodeLeft & newNodeRight and recurse
+ // res = JoinNodesSmart(newNodeLeft, newNodeRight, aOutMergeParent, aOutMergeOffset);
+ // if (NS_FAILED(res)) return res;
return res;
}
}
+
+
+nsresult
+nsHTMLEditRules::GetTopEnclosingMailCite(nsIDOMNode *aNode, nsCOMPtr *aOutCiteNode)
+{
+ // check parms
+ if (!aNode || !aOutCiteNode)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult res = NS_OK;
+ nsCOMPtr node, parentNode;
+ node = do_QueryInterface(aNode);
+
+ while (node)
+ {
+ if (IsMailCite(node)) *aOutCiteNode = node;
+ if (IsBody(node)) break;
+
+ res = node->GetParentNode(getter_AddRefs(parentNode));
+ if (NS_FAILED(res)) return res;
+ node = parentNode;
+ }
+
+ return res;
+}
+
+
+nsresult
+nsHTMLEditRules::CleanUpSelection(nsIDOMSelection *aSelection)
+{
+ // check parms
+ if (!aSelection)
+ return NS_ERROR_NULL_POINTER;
+
+ // grab anything that can fit in the selection
+ nsCOMPtr arrayOfRanges;
+ nsresult res = GetPromotedRanges(aSelection, &arrayOfRanges, kMakeList);
+ if (NS_FAILED(res)) return res;
+
+ // and walk it looking for empty nodes
+ PRUint32 rangeCount, nodeCount;
+ res = arrayOfRanges->Count(&rangeCount);
+ if (NS_FAILED(res)) return res;
+
+ PRInt32 i, j;
+ nsCOMPtr opRange;
+ nsCOMPtr isupports;
+ nsCOMPtr iter;
+
+ for (i = 0; i < rangeCount; i++)
+ {
+ isupports = (dont_AddRef)(arrayOfRanges->ElementAt(i));
+ opRange = do_QueryInterface(isupports);
+ nsCOMPtr arrayOfNodes;
+ res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes));
+ if (NS_FAILED(res)) return res;
+ res = nsComponentManager::CreateInstance(kContentIteratorCID,
+ nsnull,
+ nsIContentIterator::GetIID(),
+ getter_AddRefs(iter));
+ if (NS_FAILED(res)) return res;
+
+ do
+ {
+ res = iter->Init(opRange);
+ if (NS_FAILED(res)) return res;
+
+ // gather up a list of empty nodes
+ while (NS_COMFALSE == iter->IsDone())
+ {
+ nsCOMPtr node;
+ nsCOMPtr content;
+ res = iter->CurrentNode(getter_AddRefs(content));
+ if (NS_FAILED(res)) return res;
+ node = do_QueryInterface(content);
+ if (!node) return NS_ERROR_FAILURE;
+
+ PRBool bIsEmptyNode;
+ res = IsEmptyNode(node, &bIsEmptyNode);
+ if (NS_FAILED(res)) return res;
+ if (bIsEmptyNode && !IsBody(node))
+ {
+ isupports = do_QueryInterface(node);
+ arrayOfNodes->AppendElement(isupports);
+ }
+ res = iter->Next();
+ if (NS_FAILED(res)) return res;
+ }
+
+ // now delete the empty nodes
+ res = arrayOfNodes->Count(&nodeCount);
+ if (NS_FAILED(res)) return res;
+ for (j = 0; j < nodeCount; j++)
+ {
+ isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0));
+ nsCOMPtr delNode( do_QueryInterface(isupports ) );
+ arrayOfNodes->RemoveElementAt(0);
+ res = mEditor->DeleteNode(delNode);
+ if (NS_FAILED(res)) return res;
+ }
+
+ } while (nodeCount); // if we deleted any, loop again
+ // deleting some nodes may make some parents now empty
+ }
+ return res;
+}
+
+
+nsresult
+nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList)
+{
+ // check parms
+ if (!aListItem || !aOutOfList)
+ return NS_ERROR_NULL_POINTER;
+
+ // init out params
+ *aOutOfList = PR_FALSE;
+
+ nsCOMPtr curParent;
+ nsCOMPtr curNode( do_QueryInterface(aListItem));
+ PRInt32 offset;
+ nsresult res = nsEditor::GetNodeLocation(curNode, &curParent, &offset);
+ if (NS_FAILED(res)) return res;
+
+ if (!IsListItem(curNode))
+ return NS_ERROR_FAILURE;
+
+ // if it's first or last list item, dont need to split the list
+ // otherwise we do.
+ nsCOMPtr curParPar;
+ PRInt32 parOffset;
+ res = nsEditor::GetNodeLocation(curParent, &curParPar, &parOffset);
+ if (NS_FAILED(res)) return res;
+
+ PRBool bIsFirstListItem;
+ res = IsFirstEditableChild(curNode, &bIsFirstListItem);
+ if (NS_FAILED(res)) return res;
+
+ PRBool bIsLastListItem;
+ res = IsLastEditableChild(curNode, &bIsLastListItem);
+ if (NS_FAILED(res)) return res;
+
+ if (!bIsFirstListItem && !bIsLastListItem)
+ {
+ // split the list
+ nsCOMPtr newBlock;
+ res = mEditor->SplitNode(curParent, offset, getter_AddRefs(newBlock));
+ if (NS_FAILED(res)) return res;
+ }
+
+ if (!bIsFirstListItem) parOffset++;
+
+ res = mEditor->DeleteNode(curNode);
+ if (NS_FAILED(res)) return res;
+ res = mEditor->InsertNode(curNode, curParPar, parOffset);
+ if (NS_FAILED(res)) return res;
+
+ // convert list items to divs if we promoted them out of list
+ if (!IsList(curParPar) && IsListItem(curNode))
+ {
+ nsAutoString blockType("div");
+ nsCOMPtr newBlock;
+ res = ReplaceContainer(curNode,&newBlock,blockType);
+ *aOutOfList = PR_TRUE;
+ if (NS_FAILED(res)) return res;
+ }
+
+ return res;
+}
+
diff --git a/editor/base/nsHTMLEditRules.h b/editor/base/nsHTMLEditRules.h
index 2d09f56881a..3d771cd81d1 100644
--- a/editor/base/nsHTMLEditRules.h
+++ b/editor/base/nsHTMLEditRules.h
@@ -57,6 +57,7 @@ protected:
nsresult WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel);
nsresult WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESelectionCollapseDirection aAction, PRBool *aCancel);
nsresult WillMakeList(nsIDOMSelection *aSelection, PRBool aOrderd, PRBool *aCancel);
+ nsresult WillRemoveList(nsIDOMSelection *aSelection, PRBool aOrderd, PRBool *aCancel);
nsresult WillIndent(nsIDOMSelection *aSelection, PRBool *aCancel);
nsresult WillOutdent(nsIDOMSelection *aSelection, PRBool *aCancel);
nsresult WillAlign(nsIDOMSelection *aSelection, const nsString *alignType, PRBool *aCancel);
@@ -83,8 +84,10 @@ protected:
static PRBool IsBody(nsIDOMNode *aNode);
static PRBool IsBlockquote(nsIDOMNode *aNode);
static PRBool IsDiv(nsIDOMNode *aNode);
+ static PRBool IsMailCite(nsIDOMNode *aNode);
nsresult IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock);
+ nsresult IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyNode);
PRBool IsFirstNode(nsIDOMNode *aNode);
PRBool IsLastNode(nsIDOMNode *aNode);
@@ -116,6 +119,10 @@ protected:
nsCOMPtr *aOutMergeParent,
PRInt32 *aOutMergeOffset);
+ nsresult GetTopEnclosingMailCite(nsIDOMNode *aNode, nsCOMPtr *aOutCiteNode);
+ nsresult CleanUpSelection(nsIDOMSelection *aSelection);
+ nsresult PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList);
+
};
#endif //nsHTMLEditRules_h__
diff --git a/editor/libeditor/html/nsHTMLEditRules.cpp b/editor/libeditor/html/nsHTMLEditRules.cpp
index 76030917e9c..a7d48136023 100644
--- a/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -109,6 +109,8 @@ nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection,
return WillAlign(aSelection, info->alignType, aCancel);
case kMakeBasicBlock:
return WillMakeBasicBlock(aSelection, info->blockType, aCancel);
+ case kRemoveList:
+ return WillRemoveList(aSelection, info->bOrdered, aCancel);
case kInsertElement:
return WillInsert(aSelection, aCancel);
}
@@ -129,6 +131,9 @@ nsHTMLEditRules::DidDoAction(nsIDOMSelection *aSelection,
return NS_OK;
}
+ // clean up any empty nodes in the selection
+ CleanUpSelection(aSelection);
+
return nsTextEditRules::DidDoAction(aSelection, aInfo, aResult);
}
@@ -157,6 +162,37 @@ nsHTMLEditRules::WillInsertText(nsIDOMSelection *aSelection,
// Insert Break txns don't auto merge with insert text txns
nsAutoEditBatch beginBatching(mEditor);
+ // if the selection isn't collapsed, delete it.
+ PRBool bCollapsed;
+ res = aSelection->GetIsCollapsed(&bCollapsed);
+ if (NS_FAILED(res)) return res;
+ if (!bCollapsed)
+ {
+ res = mEditor->DeleteSelection(nsIEditor::eDoNothing);
+ if (NS_FAILED(res)) return res;
+ }
+
+ // split any mailcites in the way
+ if (1 || mFlags & nsIHTMLEditor::eEditorMailMask)
+ {
+ nsCOMPtr citeNode, selNode;
+ PRInt32 selOffset, newOffset;
+ res = mEditor->GetStartNodeAndOffset(aSelection, &selNode, &selOffset);
+ if (NS_FAILED(res)) return res;
+ res = GetTopEnclosingMailCite(selNode, &citeNode);
+ if (NS_FAILED(res)) return res;
+
+ if (citeNode)
+ {
+ res = mEditor->SplitNodeDeep(citeNode, selNode, selOffset, &newOffset);
+ if (NS_FAILED(res)) return res;
+ res = citeNode->GetParentNode(getter_AddRefs(selNode));
+ if (NS_FAILED(res)) return res;
+ res = aSelection->Collapse(selNode, newOffset);
+ if (NS_FAILED(res)) return res;
+ }
+ }
+
// strategy: there are simple cases and harder cases. The harder cases
// we handle recursively by breaking them into a series of simple cases.
// The simple cases are:
@@ -227,6 +263,10 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel)
res = WillInsert(aSelection, aCancel);
if (NS_FAILED(res)) return res;
+ // initialize out param
+ // we want to ignore result of WillInsert()
+ *aCancel = PR_FALSE;
+
// if the selection isn't collapsed, delete it.
PRBool bCollapsed;
res = aSelection->GetIsCollapsed(&bCollapsed);
@@ -300,6 +340,13 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESe
// initialize out param
*aCancel = PR_FALSE;
+ // if there is only bogus content, cancel the operation
+ if (mBogusNode)
+ {
+ *aCancel = PR_TRUE;
+ return NS_OK;
+ }
+
nsresult res = NS_OK;
PRBool bCollapsed;
@@ -344,6 +391,12 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESe
// are the blocks of same type?
nsCOMPtr leftParent = mEditor->GetBlockNodeParent(priorNode);
nsCOMPtr rightParent = mEditor->GetBlockNodeParent(node);
+
+ // if leftParent or rightParent is null, it's because the
+ // corresponding selection endpoint is in the body node.
+ if (!leftParent || !rightParent)
+ return NS_OK; // bail to default
+
nsCOMPtr leftAtom = mEditor->GetTag(leftParent);
nsCOMPtr rightAtom = mEditor->GetTag(rightParent);
@@ -411,30 +464,34 @@ nsHTMLEditRules::WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESe
// else not in text node; we need to find right place to act on
else
{
-
- // XXX add (aAction == nsIEditor::eDeleteNext) case!!
+ nsCOMPtr nodeToDelete;
- nsCOMPtr nodeToBackspace;
-
- res = mEditor->GetPriorNode(node, offset, PR_TRUE, getter_AddRefs(nodeToBackspace));
+ if (aAction == nsIEditor::eDeletePrevious)
+ res = mEditor->GetPriorNode(node, offset, PR_TRUE, getter_AddRefs(nodeToDelete));
+ else if (aAction == nsIEditor::eDeleteNext)
+ res = mEditor->GetNextNode(node, offset, PR_TRUE, getter_AddRefs(nodeToDelete));
+ else
+ return NS_OK;
+
if (NS_FAILED(res)) return res;
- if (!nodeToBackspace) return NS_ERROR_NULL_POINTER;
+ if (!nodeToDelete) return NS_ERROR_NULL_POINTER;
// if this node is text node, adjust selection
- if (nsEditor::IsTextNode(nodeToBackspace))
+ if (nsEditor::IsTextNode(nodeToDelete))
{
- PRUint32 len;
+ PRUint32 selPoint = 0;
nsCOMPtrnodeAsText;
- nodeAsText = do_QueryInterface(nodeToBackspace);
- nodeAsText->GetLength(&len);
- res = aSelection->Collapse(nodeToBackspace,len);
+ nodeAsText = do_QueryInterface(nodeToDelete);
+ if (aAction == nsIEditor::eDeletePrevious)
+ nodeAsText->GetLength(&selPoint);
+ res = aSelection->Collapse(nodeToDelete,selPoint);
return res;
}
else
{
// editable leaf node is not text; delete it.
// that's the default behavior
- res = nsEditor::GetNodeLocation(nodeToBackspace, &node, &offset);
+ res = nsEditor::GetNodeLocation(nodeToDelete, &node, &offset);
if (NS_FAILED(res)) return res;
// adjust selection to be right after it, for benefit of
// deletion code
@@ -538,13 +595,18 @@ nsresult
nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBool *aCancel)
{
if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; }
+
+ nsresult res = WillInsert(aSelection, aCancel);
+ if (NS_FAILED(res)) return res;
+
// initialize out param
+ // we want to ignore result of WillInsert()
*aCancel = PR_FALSE;
+
nsAutoString blockType("ul");
if (aOrdered) blockType = "ol";
nsAutoSelectionReset selectionResetter(aSelection);
- nsresult res;
PRBool outMakeEmpty;
res = ShouldMakeEmptyBlock(aSelection, &blockType, &outMakeEmpty);
@@ -763,6 +825,95 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo
}
+nsresult
+nsHTMLEditRules::WillRemoveList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBool *aCancel)
+{
+ if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; }
+ // initialize out param
+ *aCancel = PR_TRUE;
+
+ nsAutoString blockType("ul");
+ if (aOrdered) blockType = "ol";
+
+ nsAutoSelectionReset selectionResetter(aSelection);
+
+ nsCOMPtr arrayOfRanges;
+ nsresult res = GetPromotedRanges(aSelection, &arrayOfRanges, kMakeList);
+ if (NS_FAILED(res)) return res;
+
+ // use these ranges to contruct a list of nodes to act on.
+ nsCOMPtr arrayOfNodes;
+ res = GetNodesForOperation(arrayOfRanges, &arrayOfNodes, kMakeList);
+ if (NS_FAILED(res)) return res;
+
+ // Remove all non-editable nodes. Leave them be.
+
+ PRUint32 listCount;
+ PRInt32 i;
+ arrayOfNodes->Count(&listCount);
+ for (i=listCount-1; i>=0; i--)
+ {
+ nsCOMPtr isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i));
+ nsCOMPtr testNode( do_QueryInterface(isupports ) );
+ if (!mEditor->IsEditable(testNode))
+ {
+ arrayOfNodes->RemoveElementAt(i);
+ }
+ }
+
+ // Only act on lists or list items in the array
+ nsCOMPtr curParent;
+ for (i=0; i isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i));
+ nsCOMPtr curNode( do_QueryInterface(isupports ) );
+ PRInt32 offset;
+ res = nsEditor::GetNodeLocation(curNode, &curParent, &offset);
+ if (NS_FAILED(res)) return res;
+
+ if (IsListItem(curNode)) // unlist this listitem
+ {
+ PRBool bOutOfList;
+ do
+ {
+ res = PopListItem(curNode, &bOutOfList);
+ if (NS_FAILED(res)) return res;
+ } while (!bOutOfList); // keep popping it out until it's not in a list anymore
+ }
+ else if (IsList(curNode)) // node is a list, move list items out
+ {
+ nsCOMPtr child;
+ curNode->GetLastChild(getter_AddRefs(child));
+
+ while (child)
+ {
+ if (IsListItem(child))
+ {
+ PRBool bOutOfList;
+ do
+ {
+ res = PopListItem(child, &bOutOfList);
+ if (NS_FAILED(res)) return res;
+ } while (!bOutOfList); // keep popping it out until it's not in a list anymore
+ }
+ else
+ {
+ // delete any non- list items for now
+ res = mEditor->DeleteNode(child);
+ if (NS_FAILED(res)) return res;
+ }
+ curNode->GetLastChild(getter_AddRefs(child));
+ }
+ // delete the now-empty list
+ res = mEditor->DeleteNode(curNode);
+ if (NS_FAILED(res)) return res;
+ }
+ }
+ return res;
+}
+
+
nsresult
nsHTMLEditRules::WillMakeBasicBlock(nsIDOMSelection *aSelection, const nsString *aBlockType, PRBool *aCancel)
{
@@ -800,11 +951,15 @@ nsresult
nsHTMLEditRules::WillIndent(nsIDOMSelection *aSelection, PRBool *aCancel)
{
if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; }
- // initialize out param
- *aCancel = PR_TRUE;
+ nsresult res = WillInsert(aSelection, aCancel);
+ if (NS_FAILED(res)) return res;
+
+ // initialize out param
+ // we want to ignore result of WillInsert()
+ *aCancel = PR_TRUE;
+
nsAutoSelectionReset selectionResetter(aSelection);
- nsresult res;
// convert the selection ranges into "promoted" selection ranges:
// this basically just expands the range to include the immediate
@@ -956,67 +1111,34 @@ nsHTMLEditRules::WillOutdent(nsIDOMSelection *aSelection, PRBool *aCancel)
}
else // we are moving a list item, but not whole list
{
- // if it's first or last list item, dont need to split the list
- // otherwise we do.
- nsCOMPtr curParPar;
- PRInt32 parOffset;
- res = nsEditor::GetNodeLocation(curParent, &curParPar, &parOffset);
+ PRBool bOutOfList;
+ res = PopListItem(curNode, &bOutOfList);
if (NS_FAILED(res)) return res;
-
- PRBool bIsFirstListItem;
- res = IsFirstEditableChild(curNode, &bIsFirstListItem);
- if (NS_FAILED(res)) return res;
-
- PRBool bIsLastListItem;
- res = IsLastEditableChild(curNode, &bIsLastListItem);
- if (NS_FAILED(res)) return res;
-
- if (!bIsFirstListItem && !bIsLastListItem)
- {
- // split the list
- nsCOMPtr newBlock;
- res = mEditor->SplitNode(curParent, offset, getter_AddRefs(newBlock));
- if (NS_FAILED(res)) return res;
- }
-
- if (!bIsFirstListItem) parOffset++;
-
- res = mEditor->DeleteNode(curNode);
- if (NS_FAILED(res)) return res;
- res = mEditor->InsertNode(curNode, curParPar, parOffset);
- if (NS_FAILED(res)) return res;
-
- // convert list items to divs if we promoted them out of list
- if (!IsList(curParPar) && IsListItem(curNode))
- {
- nsAutoString blockType("div");
- nsCOMPtr newBlock;
- res = ReplaceContainer(curNode,&newBlock,blockType);
- if (NS_FAILED(res)) return res;
- }
}
}
else if (IsList(curNode)) // node is a list, but parent is non-list: move list items out
{
nsCOMPtr child;
curNode->GetLastChild(getter_AddRefs(child));
-
while (child)
{
- res = mEditor->DeleteNode(child);
- if (NS_FAILED(res)) return res;
- res = mEditor->InsertNode(child, curParent, offset);
- if (NS_FAILED(res)) return res;
-
if (IsListItem(child))
{
- nsAutoString blockType("div");
- nsCOMPtr newBlock;
- res = ReplaceContainer(child,&newBlock,blockType);
+ PRBool bOutOfList;
+ res = PopListItem(child, &bOutOfList);
+ if (NS_FAILED(res)) return res;
+ }
+ else
+ {
+ // delete any non- list items for now
+ res = mEditor->DeleteNode(child);
if (NS_FAILED(res)) return res;
}
curNode->GetLastChild(getter_AddRefs(child));
}
+ // delete the now-empty list
+ res = mEditor->DeleteNode(curNode);
+ if (NS_FAILED(res)) return res;
}
else if (transitionList[i]) // not list related - look for enclosing blockquotes and remove
{
@@ -1045,11 +1167,15 @@ nsresult
nsHTMLEditRules::WillAlign(nsIDOMSelection *aSelection, const nsString *alignType, PRBool *aCancel)
{
if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; }
- // initialize out param
- *aCancel = PR_FALSE;
+ nsresult res = WillInsert(aSelection, aCancel);
+ if (NS_FAILED(res)) return res;
+
+ // initialize out param
+ // we want to ignore result of WillInsert()
+ *aCancel = PR_FALSE;
+
nsAutoSelectionReset selectionResetter(aSelection);
- nsresult res = NS_OK;
PRBool outMakeEmpty;
res = ShouldMakeEmptyBlock(aSelection, alignType, &outMakeEmpty);
@@ -1318,6 +1444,29 @@ nsHTMLEditRules::IsDiv(nsIDOMNode *node)
}
+///////////////////////////////////////////////////////////////////////////
+// IsMailCite: true if node an html blockquote with type=cite
+//
+PRBool
+nsHTMLEditRules::IsMailCite(nsIDOMNode *node)
+{
+ NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsMailCite");
+ if (IsBlockquote(node))
+ {
+ nsCOMPtr bqElem = do_QueryInterface(node);
+ nsAutoString typeAttrName("type");
+ nsAutoString typeAttrVal;
+ nsresult res = bqElem->GetAttribute(typeAttrName, typeAttrVal);
+ if (NS_SUCCEEDED(res))
+ {
+ if (typeAttrVal == "cite")
+ return PR_TRUE;
+ }
+ }
+ return PR_FALSE;
+}
+
+
///////////////////////////////////////////////////////////////////////////
// IsEmptyBlock: figure out if aNode is (or is inside) an empty block.
// A block can have children and still be considered empty,
@@ -1330,27 +1479,48 @@ nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock)
*outIsEmptyBlock = PR_TRUE;
nsresult res = NS_OK;
- nsCOMPtr blockContent;
- if (nsEditor::IsBlockNode(aNode)) blockContent = do_QueryInterface(aNode);
- else
+ nsCOMPtr nodeToTest;
+ if (nsEditor::IsBlockNode(aNode)) nodeToTest = do_QueryInterface(aNode);
+ else nsCOMPtr block;
+
+ if (!nodeToTest) return NS_ERROR_NULL_POINTER;
+ return IsEmptyNode(nodeToTest, outIsEmptyBlock);
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+// IsEmptyNode: figure out if aNode is an empty node.
+// A block can have children and still be considered empty,
+// if the children are empty or non-editable.
+//
+nsresult
+nsHTMLEditRules::IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyNode)
+{
+ if (!aNode || !outIsEmptyNode) return NS_ERROR_NULL_POINTER;
+ *outIsEmptyNode = PR_TRUE;
+
+ // effeciency hack - special case if it's a text node
+ if (nsEditor::IsTextNode(aNode))
{
- nsCOMPtr block;
- res = nsEditor::GetBlockParent(aNode, getter_AddRefs(block));
- if (NS_FAILED(res)) return res;
- blockContent = do_QueryInterface(block);
+ PRUint32 length = 0;
+ nsCOMPtrnodeAsText;
+ nodeAsText = do_QueryInterface(aNode);
+ nodeAsText->GetLength(&length);
+ if (length) *outIsEmptyNode = PR_FALSE;
+ return NS_OK;
}
-
- if (!blockContent) return NS_ERROR_NULL_POINTER;
-
- // iterate over block. if no children, or all children are either
- // empty text nodes or non-editable, then block qualifies as empty
+
+ // iterate over node. if no children, or all children are either
+ // empty text nodes or non-editable, then node qualifies as empty
nsCOMPtr iter;
- res = nsComponentManager::CreateInstance(kContentIteratorCID,
+ nsCOMPtr nodeAsContent = do_QueryInterface(aNode);
+ if (!nodeAsContent) return NS_ERROR_FAILURE;
+ nsresult res = nsComponentManager::CreateInstance(kContentIteratorCID,
nsnull,
nsIContentIterator::GetIID(),
getter_AddRefs(iter));
if (NS_FAILED(res)) return res;
- res = iter->Init(blockContent);
+ res = iter->Init(nodeAsContent);
if (NS_FAILED(res)) return res;
while (NS_COMFALSE == iter->IsDone())
@@ -1371,14 +1541,14 @@ nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock)
nsCOMPtrnodeAsText;
nodeAsText = do_QueryInterface(node);
nodeAsText->GetLength(&length);
- if (length) *outIsEmptyBlock = PR_FALSE;
+ if (length) *outIsEmptyNode = PR_FALSE;
}
else // an editable, non-text node. we aren't an empty block
{
// is it the node we are iterating over?
if (node.get() == aNode) break;
// otherwise it ain't empty
- *outIsEmptyBlock = PR_FALSE;
+ *outIsEmptyNode = PR_FALSE;
}
}
res = iter->Next();
@@ -2538,6 +2708,10 @@ nsHTMLEditRules::IsLastEditableChild( nsIDOMNode *aNode, PRBool *aOutIsLast)
}
+///////////////////////////////////////////////////////////////////////////
+// JoinNodesSmart: join two nodes, doing whatever makes sense for their
+// children (which often means joining them, too).
+// aNodeLeft & aNodeRight must be same type of node.
nsresult
nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft,
nsIDOMNode *aNodeRight,
@@ -2553,8 +2727,8 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft,
nsresult res = NS_OK;
// caller responsible for:
- // left & right node are smae type
- // left & right node have smae parent
+ // left & right node are same type
+ // left & right node have same parent
nsCOMPtr parent;
aNodeLeft->GetParentNode(getter_AddRefs(parent));
@@ -2587,8 +2761,181 @@ nsHTMLEditRules::JoinNodesSmart( nsIDOMNode *aNodeLeft,
else
{
// for list items, divs, etc, merge smart
- res = JoinNodesSmart(aNodeLeft, aNodeRight, aOutMergeParent, aOutMergeOffset);
+ res = mEditor->JoinNodes(aNodeLeft, aNodeRight, parent);
if (NS_FAILED(res)) return res;
+ // figure out newNodeLeft & newNodeRight and recurse
+ // res = JoinNodesSmart(newNodeLeft, newNodeRight, aOutMergeParent, aOutMergeOffset);
+ // if (NS_FAILED(res)) return res;
return res;
}
}
+
+
+nsresult
+nsHTMLEditRules::GetTopEnclosingMailCite(nsIDOMNode *aNode, nsCOMPtr *aOutCiteNode)
+{
+ // check parms
+ if (!aNode || !aOutCiteNode)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult res = NS_OK;
+ nsCOMPtr node, parentNode;
+ node = do_QueryInterface(aNode);
+
+ while (node)
+ {
+ if (IsMailCite(node)) *aOutCiteNode = node;
+ if (IsBody(node)) break;
+
+ res = node->GetParentNode(getter_AddRefs(parentNode));
+ if (NS_FAILED(res)) return res;
+ node = parentNode;
+ }
+
+ return res;
+}
+
+
+nsresult
+nsHTMLEditRules::CleanUpSelection(nsIDOMSelection *aSelection)
+{
+ // check parms
+ if (!aSelection)
+ return NS_ERROR_NULL_POINTER;
+
+ // grab anything that can fit in the selection
+ nsCOMPtr arrayOfRanges;
+ nsresult res = GetPromotedRanges(aSelection, &arrayOfRanges, kMakeList);
+ if (NS_FAILED(res)) return res;
+
+ // and walk it looking for empty nodes
+ PRUint32 rangeCount, nodeCount;
+ res = arrayOfRanges->Count(&rangeCount);
+ if (NS_FAILED(res)) return res;
+
+ PRInt32 i, j;
+ nsCOMPtr opRange;
+ nsCOMPtr isupports;
+ nsCOMPtr iter;
+
+ for (i = 0; i < rangeCount; i++)
+ {
+ isupports = (dont_AddRef)(arrayOfRanges->ElementAt(i));
+ opRange = do_QueryInterface(isupports);
+ nsCOMPtr arrayOfNodes;
+ res = NS_NewISupportsArray(getter_AddRefs(arrayOfNodes));
+ if (NS_FAILED(res)) return res;
+ res = nsComponentManager::CreateInstance(kContentIteratorCID,
+ nsnull,
+ nsIContentIterator::GetIID(),
+ getter_AddRefs(iter));
+ if (NS_FAILED(res)) return res;
+
+ do
+ {
+ res = iter->Init(opRange);
+ if (NS_FAILED(res)) return res;
+
+ // gather up a list of empty nodes
+ while (NS_COMFALSE == iter->IsDone())
+ {
+ nsCOMPtr node;
+ nsCOMPtr content;
+ res = iter->CurrentNode(getter_AddRefs(content));
+ if (NS_FAILED(res)) return res;
+ node = do_QueryInterface(content);
+ if (!node) return NS_ERROR_FAILURE;
+
+ PRBool bIsEmptyNode;
+ res = IsEmptyNode(node, &bIsEmptyNode);
+ if (NS_FAILED(res)) return res;
+ if (bIsEmptyNode && !IsBody(node))
+ {
+ isupports = do_QueryInterface(node);
+ arrayOfNodes->AppendElement(isupports);
+ }
+ res = iter->Next();
+ if (NS_FAILED(res)) return res;
+ }
+
+ // now delete the empty nodes
+ res = arrayOfNodes->Count(&nodeCount);
+ if (NS_FAILED(res)) return res;
+ for (j = 0; j < nodeCount; j++)
+ {
+ isupports = (dont_AddRef)(arrayOfNodes->ElementAt(0));
+ nsCOMPtr delNode( do_QueryInterface(isupports ) );
+ arrayOfNodes->RemoveElementAt(0);
+ res = mEditor->DeleteNode(delNode);
+ if (NS_FAILED(res)) return res;
+ }
+
+ } while (nodeCount); // if we deleted any, loop again
+ // deleting some nodes may make some parents now empty
+ }
+ return res;
+}
+
+
+nsresult
+nsHTMLEditRules::PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList)
+{
+ // check parms
+ if (!aListItem || !aOutOfList)
+ return NS_ERROR_NULL_POINTER;
+
+ // init out params
+ *aOutOfList = PR_FALSE;
+
+ nsCOMPtr curParent;
+ nsCOMPtr curNode( do_QueryInterface(aListItem));
+ PRInt32 offset;
+ nsresult res = nsEditor::GetNodeLocation(curNode, &curParent, &offset);
+ if (NS_FAILED(res)) return res;
+
+ if (!IsListItem(curNode))
+ return NS_ERROR_FAILURE;
+
+ // if it's first or last list item, dont need to split the list
+ // otherwise we do.
+ nsCOMPtr curParPar;
+ PRInt32 parOffset;
+ res = nsEditor::GetNodeLocation(curParent, &curParPar, &parOffset);
+ if (NS_FAILED(res)) return res;
+
+ PRBool bIsFirstListItem;
+ res = IsFirstEditableChild(curNode, &bIsFirstListItem);
+ if (NS_FAILED(res)) return res;
+
+ PRBool bIsLastListItem;
+ res = IsLastEditableChild(curNode, &bIsLastListItem);
+ if (NS_FAILED(res)) return res;
+
+ if (!bIsFirstListItem && !bIsLastListItem)
+ {
+ // split the list
+ nsCOMPtr newBlock;
+ res = mEditor->SplitNode(curParent, offset, getter_AddRefs(newBlock));
+ if (NS_FAILED(res)) return res;
+ }
+
+ if (!bIsFirstListItem) parOffset++;
+
+ res = mEditor->DeleteNode(curNode);
+ if (NS_FAILED(res)) return res;
+ res = mEditor->InsertNode(curNode, curParPar, parOffset);
+ if (NS_FAILED(res)) return res;
+
+ // convert list items to divs if we promoted them out of list
+ if (!IsList(curParPar) && IsListItem(curNode))
+ {
+ nsAutoString blockType("div");
+ nsCOMPtr newBlock;
+ res = ReplaceContainer(curNode,&newBlock,blockType);
+ *aOutOfList = PR_TRUE;
+ if (NS_FAILED(res)) return res;
+ }
+
+ return res;
+}
+
diff --git a/editor/libeditor/html/nsHTMLEditRules.h b/editor/libeditor/html/nsHTMLEditRules.h
index 2d09f56881a..3d771cd81d1 100644
--- a/editor/libeditor/html/nsHTMLEditRules.h
+++ b/editor/libeditor/html/nsHTMLEditRules.h
@@ -57,6 +57,7 @@ protected:
nsresult WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel);
nsresult WillDeleteSelection(nsIDOMSelection *aSelection, nsIEditor::ESelectionCollapseDirection aAction, PRBool *aCancel);
nsresult WillMakeList(nsIDOMSelection *aSelection, PRBool aOrderd, PRBool *aCancel);
+ nsresult WillRemoveList(nsIDOMSelection *aSelection, PRBool aOrderd, PRBool *aCancel);
nsresult WillIndent(nsIDOMSelection *aSelection, PRBool *aCancel);
nsresult WillOutdent(nsIDOMSelection *aSelection, PRBool *aCancel);
nsresult WillAlign(nsIDOMSelection *aSelection, const nsString *alignType, PRBool *aCancel);
@@ -83,8 +84,10 @@ protected:
static PRBool IsBody(nsIDOMNode *aNode);
static PRBool IsBlockquote(nsIDOMNode *aNode);
static PRBool IsDiv(nsIDOMNode *aNode);
+ static PRBool IsMailCite(nsIDOMNode *aNode);
nsresult IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock);
+ nsresult IsEmptyNode(nsIDOMNode *aNode, PRBool *outIsEmptyNode);
PRBool IsFirstNode(nsIDOMNode *aNode);
PRBool IsLastNode(nsIDOMNode *aNode);
@@ -116,6 +119,10 @@ protected:
nsCOMPtr *aOutMergeParent,
PRInt32 *aOutMergeOffset);
+ nsresult GetTopEnclosingMailCite(nsIDOMNode *aNode, nsCOMPtr *aOutCiteNode);
+ nsresult CleanUpSelection(nsIDOMSelection *aSelection);
+ nsresult PopListItem(nsIDOMNode *aListItem, PRBool *aOutOfList);
+
};
#endif //nsHTMLEditRules_h__