diff --git a/editor/base/nsEditor.cpp b/editor/base/nsEditor.cpp index b8c734ce1e24..9196164a4ba5 100644 --- a/editor/base/nsEditor.cpp +++ b/editor/base/nsEditor.cpp @@ -3153,7 +3153,8 @@ nsEditor::GetTag(nsIDOMNode *aNode) } nsCOMPtr content = do_QueryInterface(aNode); - content->GetTag(*getter_AddRefs(atom)); + if (content) + content->GetTag(*getter_AddRefs(atom)); return atom; } @@ -3407,7 +3408,7 @@ nsEditor::NextNodeInBlock(nsIDOMNode *aNode, IterDirection aDir) if (NS_FAILED(iter->CurrentNode(getter_AddRefs(content)))) return nullNode; // ignore nodes that aren't elements or text, or that are the block parent node = do_QueryInterface(content); - if (node && IsTextOrElementNode(node) && (node != blockParent) && (node!=nsCOMPtr(dont_QueryInterface(aNode)))) + if (node && IsTextOrElementNode(node) && (node != blockParent) && (node.get() != aNode)) return node; if (aDir == kIterForward) @@ -3680,7 +3681,7 @@ nsEditor::SplitNodeDeep(nsIDOMNode *aNode, if (!nodeAsText || (offset && (offset != textLen))) { bDoSplit = PR_TRUE; - nsresult res = SplitNode(nodeToSplit, offset, getter_AddRefs(tempNode)); + res = SplitNode(nodeToSplit, offset, getter_AddRefs(tempNode)); if (NS_FAILED(res)) return res; } diff --git a/editor/base/nsHTMLEditRules.cpp b/editor/base/nsHTMLEditRules.cpp index cb4f162546c9..32ae4d4e162e 100644 --- a/editor/base/nsHTMLEditRules.cpp +++ b/editor/base/nsHTMLEditRules.cpp @@ -107,12 +107,8 @@ nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection, return WillOutdent(aSelection, aCancel); case kAlign: return WillAlign(aSelection, info->alignType, aCancel); - case kMakeHeader: - return WillMakeHeader(aSelection, aCancel); - case kMakeAddress: - return WillMakeAddress(aSelection, aCancel); - case kMakePRE: - return WillMakePRE(aSelection, aCancel); + case kMakeBasicBlock: + return WillMakeBasicBlock(aSelection, info->blockType, aCancel); case kInsertElement: return WillInsert(aSelection, aCancel); } @@ -258,7 +254,13 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel) return mEditor->InsertText(theString); } - nsCOMPtr blockParent = mEditor->GetBlockNodeParent(node); + nsCOMPtr blockParent; + + if (nsEditor::IsBlockNode(node)) + blockParent = node; + else + blockParent = mEditor->GetBlockNodeParent(node); + if (!blockParent) return NS_ERROR_FAILURE; // headers: put selection after the header @@ -521,45 +523,18 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } // initialize out param *aCancel = PR_FALSE; + nsAutoString blockType("ul"); + if (aOrdered) blockType = "ol"; nsAutoSelectionReset selectionResetter(aSelection); nsresult res; - // check for collapsed selection in empty block or after a
. - // Either way, we want to get the default behavior of creating - // an empty list. - - PRBool isCollapsed; - res = aSelection->GetIsCollapsed(&isCollapsed); + PRBool outMakeEmpty; + res = ShouldMakeEmptyBlock(aSelection, &blockType, &outMakeEmpty); if (NS_FAILED(res)) return res; - if (isCollapsed) - { - // is it after a
? Two possibilities: selection right after
; - // and selection at front of text node following
- nsCOMPtr parent, prevChild; - PRInt32 offset; - res = nsEditor::GetStartNodeAndOffset(aSelection, &parent, &offset); - if (nsEditor::IsTextNode(parent) && !offset) - { - nsCOMPtr node = parent; - res = nsEditor::GetNodeLocation(node, &parent, &offset); - if (NS_FAILED(res)) return res; - } - if (offset) - { - prevChild = nsEditor::GetChildAt(parent, offset-1); - if (prevChild && IsBreak(prevChild)) - return NS_OK; // insertion point after break - } - - // otherwise check for being in an empty block - PRBool isEmptyBlock; - res = IsEmptyBlock(parent, &isEmptyBlock); - if (NS_FAILED(res)) return res; - if (isEmptyBlock) return NS_OK; - } + if (outMakeEmpty) return NS_OK; - // ok, we aren't vreating a new empty list. Instead we are converting + // ok, we aren't creating a new empty list. Instead we are converting // the set of blocks implied by the selection into a list. // convert the selection ranges into "promoted" selection ranges: @@ -578,13 +553,6 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo res = GetNodesForOperation(arrayOfRanges, &arrayOfNodes, kMakeList); if (NS_FAILED(res)) return res; - // if we ended up with any nodes in the list that aren't blocknodes, - // find their block parent instead and use that. - - // i started writing this and then the sky fell. there are big questions - // about what to do here. i may need to switch from thinking about an array of - // nodes to act on to instead think of an array of ranges to act on. - // Remove all non-editable nodes. Leave them be. PRUint32 listCount; @@ -621,7 +589,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo nsCOMPtr tmpNode = nsEditor::GetChildAt(curNode, 0); if (IsDiv(tmpNode) || IsOrderedList(tmpNode) || IsUnorderedList(tmpNode) || IsBlockquote(tmpNode)) { - // check editablility XXX moose + // check editablility XXX floppy moose curNode = tmpNode; } else break; @@ -673,7 +641,6 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo { // make a new ordered list, insert it where the current unordered list is, // and move all the children to the new list, and remove the old list - nsAutoString blockType("ol"); res = ReplaceContainer(curNode,&newBlock,blockType); if (NS_FAILED(res)) return res; curList = newBlock; @@ -692,7 +659,6 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo { // make a new unordered list, insert it where the current ordered list is, // and move all the children to the new list, and remove the old list - nsAutoString blockType("ul"); ReplaceContainer(curNode,&newBlock,blockType); if (NS_FAILED(res)) return res; curList = newBlock; @@ -707,7 +673,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo } else if (IsDiv(curNode) || IsBlockquote(curNode)) { - // XXX moose + // XXX floppy moose } } // lonely node @@ -724,7 +690,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo prevListItem = 0; } - // if curNode is a Break, delete it, and quit remember prev list item + // if curNode is a Break, delete it, and quit remembering prev list item if (IsBreak(curNode)) { res = mEditor->DeleteNode(curNode); @@ -781,53 +747,35 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo nsresult -nsHTMLEditRules::WillMakeHeader(nsIDOMSelection *aSelection, PRBool *aCancel) +nsHTMLEditRules::WillMakeBasicBlock(nsIDOMSelection *aSelection, const nsString *aBlockType, PRBool *aCancel) { if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } // initialize out param - *aCancel = PR_TRUE; + *aCancel = PR_FALSE; + PRBool makeEmpty; + nsresult res = ShouldMakeEmptyBlock(aSelection, aBlockType, &makeEmpty); + if (NS_FAILED(res)) return res; + + if (makeEmpty) return res; // just insert a new empty block + + // else it's not that easy... nsAutoSelectionReset selectionResetter(aSelection); - nsresult res; - - // Get the ranges for each break-delimited block - nsCOMPtr enumerator; - res = aSelection->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_SUCCEEDED(res) && enumerator) - { - for (enumerator->First(); NS_OK!=enumerator->IsDone(); enumerator->Next()) - { - nsCOMPtr currentItem; - res = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if ((NS_SUCCEEDED(res)) && (currentItem)) - { - nsCOMPtr range( do_QueryInterface(currentItem) ); - PRBool isCollapsed; - range->GetIsCollapsed(&isCollapsed); - if (PR_FALSE==isCollapsed) // don't make headers out of empty ranges - { - nsCOMPtr arrayOfRanges; - res = mEditor->GetBlockSectionsForRange(range, arrayOfRanges); - PRUint32 rangeCount; - res = arrayOfRanges->Count(&rangeCount); - if (NS_FAILED(res)) return res; - - PRInt32 i; - nsCOMPtr opRange; - nsCOMPtr isupports; + *aCancel = PR_TRUE; - for (i = 0; i < rangeCount; i++) - { - isupports = (dont_AddRef)(arrayOfRanges->ElementAt(i)); - opRange = do_QueryInterface(isupports); - // ConvertRangeToTag - } - } - } - } - } - // unfinished, just return NS_OK for now - return NS_OK; + nsCOMPtr arrayOfRanges; + res = GetPromotedRanges(aSelection, &arrayOfRanges, kMakeBasicBlock); + if (NS_FAILED(res)) return res; + + // use these ranges to contruct a list of nodes to act on. + nsCOMPtr arrayOfNodes; + res = GetNodesForOperation(arrayOfRanges, &arrayOfNodes, kMakeBasicBlock); + if (NS_FAILED(res)) return res; + + // Ok, now go through all the nodes and make the right kind of blocks, + // or whatever is approriate. Wohoo! + res = ApplyBlockStyle(arrayOfNodes, aBlockType); + return res; } @@ -1015,15 +963,22 @@ nsHTMLEditRules::WillAlign(nsIDOMSelection *aSelection, const nsString *alignTyp { if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } // initialize out param - *aCancel = PR_TRUE; + *aCancel = PR_FALSE; nsAutoSelectionReset selectionResetter(aSelection); nsresult res = NS_OK; + + PRBool outMakeEmpty; + res = ShouldMakeEmptyBlock(aSelection, alignType, &outMakeEmpty); + if (NS_FAILED(res)) return res; + if (outMakeEmpty) return NS_OK; + // convert the selection ranges into "promoted" selection ranges: // this basically just expands the range to include the immediate // block parent, and then further expands to include any ancestors // whose children are all in the range + *aCancel = PR_TRUE; nsCOMPtr arrayOfRanges; res = GetPromotedRanges(aSelection, &arrayOfRanges, kAlign); @@ -1100,32 +1055,6 @@ nsHTMLEditRules::WillAlign(nsIDOMSelection *aSelection, const nsString *alignTyp -nsresult -nsHTMLEditRules::WillMakeAddress(nsIDOMSelection *aSelection, PRBool *aCancel) -{ - if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } - // initialize out param - *aCancel = PR_FALSE; - - nsresult res = NS_OK; - - return res; -} - - -nsresult -nsHTMLEditRules::WillMakePRE(nsIDOMSelection *aSelection, PRBool *aCancel) -{ - if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } - // initialize out param - *aCancel = PR_FALSE; - - nsresult res = NS_OK; - - return res; -} - - /******************************************************** * helper methods ********************************************************/ @@ -1186,6 +1115,24 @@ nsHTMLEditRules::IsListItem(nsIDOMNode *node) } +/////////////////////////////////////////////////////////////////////////// +// IsList: true if node an html list +// +PRBool +nsHTMLEditRules::IsList(nsIDOMNode *node) +{ + NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsList"); + nsAutoString tag; + nsEditor::GetTagString(node,tag); + if ( (tag == "ol") || + (tag == "ul") ) + { + return PR_TRUE; + } + return PR_FALSE; +} + + /////////////////////////////////////////////////////////////////////////// // IsOrderedList: true if node an html orderd list // @@ -1345,6 +1292,9 @@ nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock) } 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; } } @@ -1451,12 +1401,21 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt nsCOMPtr parent = aNode; PRInt32 offset = aOffset; + if (IsBody(aNode)) + { + // we cant go any higher + *outNode = do_QueryInterface(aNode); + *outOffset = aOffset; + return res; + } + if (aWhere == kStart) { // some special casing for text nodes if (nsEditor::IsTextNode(aNode)) { - nsEditor::GetNodeLocation(aNode, &parent, &offset); + res = nsEditor::GetNodeLocation(aNode, &parent, &offset); + if (NS_FAILED(res)) return res; } else { @@ -1464,26 +1423,38 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt if (!node) node = parent; } - // if this is an inline node, back up through any prior inline nodes that - // aren't across a
from us, and that are enclosed in the same block. + // if this is an inline node who's block parent is the body, + // back up through any prior inline nodes that + // aren't across a
from us. if (!nsEditor::IsBlockNode(node)) { - nsCOMPtr prevNode; - prevNode = nsEditor::NextNodeInBlock(node, nsEditor::kIterBackward); - while (prevNode) + nsCOMPtr block = nsEditor::GetBlockNodeParent(node); + if (IsBody(block)) { - if (IsBreak(prevNode)) - break; - if (nsEditor::IsBlockNode(prevNode)) - break; - node = prevNode; + nsCOMPtr prevNode; prevNode = nsEditor::NextNodeInBlock(node, nsEditor::kIterBackward); + while (prevNode) + { + if (IsBreak(prevNode)) + break; + if (nsEditor::IsBlockNode(prevNode)) + break; + node = prevNode; + prevNode = nsEditor::NextNodeInBlock(node, nsEditor::kIterBackward); + } + } + else + { + // just grap the whole block + node = block; } } // finding the real start for this point. look up the tree for as long as we are the // first node in the container, and as long as we haven't hit the body node. + nsEditor::GetNodeLocation(node, &parent, &offset); + if (NS_FAILED(res)) return res; while ((IsFirstNode(node)) && (!IsBody(parent))) { node = parent; @@ -1508,39 +1479,49 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt if (!node) node = parent; } - if (node) - offset++; // since this is going to be used for a range _endpoint_, we want to be after the node - else + if (!node) node = parent; - // if this is an inline node, look ahead through any further inline nodes that + // if this is an inline node who's block parent is the body, + // look ahead through any further inline nodes that // aren't across a
from us, and that are enclosed in the same block. if (!nsEditor::IsBlockNode(node)) { - nsCOMPtr nextNode; - nextNode = nsEditor::NextNodeInBlock(node, nsEditor::kIterForward); - while (nextNode) + nsCOMPtr block = nsEditor::GetBlockNodeParent(node); + if (IsBody(block)) { - if (IsBreak(nextNode)) - break; - if (nsEditor::IsBlockNode(nextNode)) - break; - node = nextNode; + nsCOMPtr nextNode; nextNode = nsEditor::NextNodeInBlock(node, nsEditor::kIterForward); + while (nextNode) + { + if (IsBreak(nextNode)) + break; + if (nsEditor::IsBlockNode(nextNode)) + break; + node = nextNode; + nextNode = nsEditor::NextNodeInBlock(node, nsEditor::kIterForward); + } + } + else + { + // just grap the whole block + node = block; } } // finding the real end for this point. look up the tree for as long as we are the // last node in the container, and as long as we haven't hit the body node. + nsEditor::GetNodeLocation(node, &parent, &offset); + if (NS_FAILED(res)) return res; while ((IsLastNode(node)) && (!IsBody(parent))) { node = parent; res = nsEditor::GetNodeLocation(node, &parent, &offset); if (NS_FAILED(res)) return res; - offset++; } *outNode = parent; + offset++; // add one since this in an endpoint - want to be AFTER node. *outOffset = offset; return res; } @@ -1669,6 +1650,41 @@ nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges, +/////////////////////////////////////////////////////////////////////////// +// GetChildNodesForOperation: +// +nsresult +nsHTMLEditRules::GetChildNodesForOperation(nsIDOMNode *inNode, + nsCOMPtr *outArrayOfNodes) +{ + if (!inNode || !outArrayOfNodes) return NS_ERROR_NULL_POINTER; + + nsresult res = NS_NewISupportsArray(getter_AddRefs(*outArrayOfNodes)); + if (NS_FAILED(res)) return res; + + nsCOMPtr childNodes; + res = inNode->GetChildNodes(getter_AddRefs(childNodes)); + if (NS_FAILED(res)) return res; + PRUint32 childCount; + res = childNodes->GetLength(&childCount); + if (NS_FAILED(res)) return res; + + PRUint32 i; + nsCOMPtr node; + nsCOMPtr isupports; + for (i = 0; i < childCount; i++) + { + res = childNodes->Item( i, getter_AddRefs(node)); + if (!node) return NS_ERROR_FAILURE; + isupports = do_QueryInterface(node); + (*outArrayOfNodes)->AppendElement(isupports); + if (NS_FAILED(res)) return res; + } + return res; +} + + + /////////////////////////////////////////////////////////////////////////// // MakeTransitionList: detect all the transitions in the array, where a // transition means that adjacent nodes in the array @@ -1716,7 +1732,7 @@ nsHTMLEditRules::MakeTransitionList(nsISupportsArray *inArrayOfNodes, nsresult nsHTMLEditRules::ReplaceContainer(nsIDOMNode *inNode, nsCOMPtr *outNode, - nsString &aNodeType) + const nsString &aNodeType) { if (!inNode || !outNode) return NS_ERROR_NULL_POINTER; @@ -1733,13 +1749,12 @@ nsHTMLEditRules::ReplaceContainer(nsIDOMNode *inNode, offset = 0; while (bHasMoreChildren) { - inNode->GetFirstChild(getter_AddRefs(child)); + inNode->GetLastChild(getter_AddRefs(child)); res = mEditor->DeleteNode(child); if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(child, *outNode, offset); + res = mEditor->InsertNode(child, *outNode, 0); if (NS_FAILED(res)) return res; inNode->HasChildNodes(&bHasMoreChildren); - offset++; } res = mEditor->DeleteNode(inNode); return res; @@ -1952,42 +1967,43 @@ nsHTMLEditRules::InsertSpace(nsIDOMSelection *aSelection, nsresult nsHTMLEditRules::ReturnInHeader(nsIDOMSelection *aSelection, nsIDOMNode *aHeader, - nsIDOMNode *aTextNode, + nsIDOMNode *aNode, PRInt32 aOffset) { - if (!aSelection || !aHeader || !aTextNode) return NS_ERROR_NULL_POINTER; + if (!aSelection || !aHeader || !aNode) return NS_ERROR_NULL_POINTER; - nsCOMPtr leftNode; - nsCOMPtr textNode = do_QueryInterface(aTextNode); // to hold a ref across the delete call - // split the node - nsresult res = mEditor->SplitNode(aTextNode, aOffset, getter_AddRefs(leftNode)); + // remeber where the header is + nsCOMPtr headerParent; + PRInt32 offset; + nsresult res = nsEditor::GetNodeLocation(aHeader, &headerParent, &offset); if (NS_FAILED(res)) return res; - - // move the right node outside of the header, via deletion/insertion - // delete the right node - res = mEditor->DeleteNode(textNode); - if (NS_FAILED(res)) return res; - - // insert the right node - nsCOMPtr p; - aHeader->GetParentNode(getter_AddRefs(p)); - PRInt32 indx = mEditor->GetIndexOf(p,aHeader); - res = mEditor->InsertNode(textNode,p,indx+1); - if (NS_FAILED(res)) return res; - - // merge text node with like sibling, if any - nsCOMPtr sibling; - textNode->GetNextSibling(getter_AddRefs(sibling)); - if (sibling && mEditor->IsTextNode(sibling) && mEditor->IsEditable(sibling)) - { - res = mEditor->JoinNodes(textNode,sibling,p); - if (NS_FAILED(res)) return res; - textNode = sibling; // sibling was the node kept by the join; remember it in "textNode" - } - - // position selection before inserted node - res = aSelection->Collapse(textNode,0); + // split the header + PRInt32 newOffset; + res = mEditor->SplitNodeDeep( aHeader, aNode, aOffset, &newOffset); + if (NS_FAILED(res)) return res; + +// revisit the below when we move to using divs as standard paragraphs. +// need to create an "empty" div in the fisrt case below, and need to +// use a div instead of a

in the second case below + + // if the new (righthand) header node is empty, delete it + PRBool isEmpty; + res = IsEmptyBlock(aHeader, &isEmpty); + if (NS_FAILED(res)) return res; + if (isEmpty) + { + res = mEditor->DeleteNode(aHeader); + if (NS_FAILED(res)) return res; + res = aSelection->Collapse(headerParent,offset+1); + return res; + } + // else rewrap it in a paragraph + nsCOMPtr newBlock; + nsAutoString blockType("p"); + res = ReplaceContainer(aHeader,&newBlock,blockType); + if (NS_FAILED(res)) return res; + res = aSelection->Collapse(newBlock,0); return res; } @@ -2097,12 +2113,282 @@ nsHTMLEditRules::ReturnInListItem(nsIDOMSelection *aSelection, PRInt32 aOffset) { if (!aSelection || !aListItem || !aNode) return NS_ERROR_NULL_POINTER; - - PRInt32 newOffset; - nsresult res = mEditor->SplitNodeDeep( aListItem, aNode, aOffset, &newOffset); + nsresult res = NS_OK; + + nsCOMPtr listitem; + + // sanity check + NS_PRECONDITION(PR_TRUE == IsListItem(aListItem), "expected a list item and didnt get one"); + + // if we are in an empty listitem, then we want to pop up out of the list + PRBool isEmpty; + res = IsEmptyBlock(aListItem, &isEmpty); if (NS_FAILED(res)) return res; - res = aSelection->Collapse(aNode,0); + if (isEmpty) + { + nsCOMPtr list, listparent; + PRInt32 offset; + list = nsEditor::GetBlockNodeParent(aListItem); + res = nsEditor::GetNodeLocation(list, &listparent, &offset); + if (NS_FAILED(res)) return res; + + // are we in a sublist? + if (IsList(listparent)) //in a sublist + { + // if so, move this list item out of this list and into the grandparent list + res = mEditor->DeleteNode(aListItem); + if (NS_FAILED(res)) return res; + res = mEditor->InsertNode(aListItem,listparent,offset+1); + if (NS_FAILED(res)) return res; + res = aSelection->Collapse(aListItem,0); + } + else + { + // otherwise kill this listitem and set the selection to after the parent list + res = mEditor->DeleteNode(aListItem); + if (NS_FAILED(res)) return res; + res = aSelection->Collapse(listparent,offset+1); + } + return res; + } + + // else we want a new list item at the same list level + PRInt32 newOffset; + res = mEditor->SplitNodeDeep( aListItem, aNode, aOffset, &newOffset); + if (NS_FAILED(res)) return res; + res = aSelection->Collapse(aListItem,0); return res; } +/////////////////////////////////////////////////////////////////////////// +// ShouldMakeEmptyBlock: determine if a block transformation should make +// a new empty block, or instead transform a block +// +nsresult +nsHTMLEditRules::ShouldMakeEmptyBlock(nsIDOMSelection *aSelection, + const nsString *blockTag, + PRBool *outMakeEmpty) +{ + // a note about strategy: + // this routine will be called by the rules code to figure out + // if it should do something, or let the nsHTMLEditor default + // action happen. The default action is to insert a new block. + // Note that if _nothing_ should happen, ie, the selection is + // already entireyl inside a block (or blocks) or the correct type, + // then you don't want to return true in outMakeEmpty, since the + // defualt code will insert a new empty block anyway, rather than + // doing nothing. So we have to detect that case and return false. + + if (!aSelection || !outMakeEmpty) return NS_ERROR_NULL_POINTER; + nsresult res = NS_OK; + + // if the selection is collapsed, and + // if we in the body, or after a
with + // no more inline content before the next block, then we want + // a new block. Otherwise we want to trasform a block + + // xxx possible bug: selection could be not callapsed, but + // still empty. it would be nice to have a call for this: IsEmptySelection() + + PRBool isCollapsed; + res = aSelection->GetIsCollapsed(&isCollapsed); + if (NS_FAILED(res)) return res; + if (isCollapsed) + { + nsCOMPtr parent; + PRInt32 offset; + res = nsEditor::GetStartNodeAndOffset(aSelection, &parent, &offset); + if (NS_FAILED(res)) return res; + + // is selection point in the body? + if (IsBody(parent)) + { + *outMakeEmpty = PR_TRUE; + return res; + } + + // see if block parent is already right kind of block. + // See strategy comment above. + nsCOMPtr block; + if (!nsEditor::IsBlockNode(parent)) + block = nsEditor::GetBlockNodeParent(parent); + else + block = parent; + if (block) + { + nsAutoString tag; + nsEditor::GetTagString(block,tag); + if (tag == *blockTag) + { + *outMakeEmpty = PR_FALSE; + return res; + } + } + + // are we in a textnode or inline node? + if (!nsEditor::IsBlockNode(parent)) + { + // we must be in a text or inline node - convert existing block + *outMakeEmpty = PR_FALSE; + return res; + } + + // is it after a
with no inline nodes after it, or a
after it?? + if (offset) + { + nsCOMPtr prevChild, nextChild, tmp; + prevChild = nsEditor::GetChildAt(parent, offset-1); + while (prevChild && !mEditor->IsEditable(prevChild)) + { + // search back until we either find an editable node, + // or hit the beginning of the block + tmp = nsEditor::NextNodeInBlock(prevChild, nsEditor::kIterBackward); + prevChild = tmp; + } + + if (prevChild && IsBreak(prevChild)) + { + nextChild = nsEditor::GetChildAt(parent, offset); + while (nextChild && !mEditor->IsEditable(nextChild)) + { + // search back until we either find an editable node, + // or hit the beginning of the block + tmp = nsEditor::NextNodeInBlock(nextChild, nsEditor::kIterForward); + nextChild = tmp; + } + if (!nextChild || IsBreak(nextChild) || nsEditor::IsBlockNode(nextChild)) + { + // we are after a
and not before inline content, + // or we are between
s. + // make an empty block + *outMakeEmpty = PR_FALSE; + return res; + } + } + } + } + // otherwise transform an existing block + *outMakeEmpty = PR_FALSE; + return res; +} + + +/////////////////////////////////////////////////////////////////////////// +// ApplyBlockStyle: do whatever it takes to make the list of nodes into +// one or more blocks of type blockTag. +// +nsresult +nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString *aBlockTag) +{ + // intent of this routine is to be used for converting to/from + // headers, paragraphs (or moz-divs), pre, and address. Those blocks + // that pretty much just contain inline things... + + if (!arrayOfNodes || !aBlockTag) return NS_ERROR_NULL_POINTER; + nsresult res = NS_OK; + + nsCOMPtr curNode, curParent, curBlock, newBlock; + PRInt32 offset; + PRUint32 listCount; + arrayOfNodes->Count(&listCount); + + PRUint32 i; + for (i=0; i isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i)); + nsCOMPtr curNode( do_QueryInterface(isupports ) ); + res = nsEditor::GetNodeLocation(curNode, &curParent, &offset); + if (NS_FAILED(res)) return res; + nsAutoString curNodeTag; + nsEditor::GetTagString(curNode, curNodeTag); + + + // is it already the right kind of block? + if (curNodeTag == *aBlockTag) + { + curBlock = 0; // forget any previous block used for previous inline nodes + continue; // do nothing to this block + } + + // if curNode is a

 and we are converting to non-pre, we need
+    // to process the text inside the 
 so as to convert returns
+    // to breaks, and runs of spaces to nbsps.
+    // xxx floppy moose
+      
+    // if curNode is a p, header, address, or pre, replace 
+    // it with a new block of correct type.
+    // xxx floppy moose: pre cant hold everything the others can
+    if ((curNodeTag == "pre") || 
+        (curNodeTag == "p")   ||
+        (curNodeTag == "h1")  ||
+        (curNodeTag == "h2")  ||
+        (curNodeTag == "h3")  ||
+        (curNodeTag == "h4")  ||
+        (curNodeTag == "h5")  ||
+        (curNodeTag == "h6")  ||
+        (curNodeTag == "address"))
+    {
+      curBlock = 0;  // forget any previous block used for previous inline nodes
+      res = ReplaceContainer(curNode, &newBlock, *aBlockTag);
+      if (NS_FAILED(res)) return res;
+    }
+    else if ((curNodeTag == "table")      || 
+             (curNodeTag == "tbody")      ||
+             (curNodeTag == "tr")         ||
+             (curNodeTag == "td")         ||
+             (curNodeTag == "ol")         ||
+             (curNodeTag == "ul")         ||
+             (curNodeTag == "li")         ||
+             (curNodeTag == "blockquote") ||
+             (curNodeTag == "div"))
+    {
+      curBlock = 0;  // forget any previous block used for previous inline nodes
+      // recursion time
+      nsCOMPtr childArray;
+      res = GetChildNodesForOperation(curNode, &childArray);
+      if (NS_FAILED(res)) return res;
+      res = ApplyBlockStyle(childArray, aBlockTag);
+      if (NS_FAILED(res)) return res;
+    }
+    
+    // if curNode is inline, pull it into curBlock
+    // note: it's assumed that consecutive inline nodes in the 
+    // arrayOfNodes are actually members of the same block parent.
+    // this happens to be true now as a side effect of how
+    // arrayOfNodes is contructed, but some additional logic should
+    // be added here if that should change
+    
+    else if (nsEditor::IsInlineNode(curNode))
+    {
+      // if curNode is a non editable, drop it if we are going to 
+      if ((*aBlockTag == "pre") && (!mEditor->IsEditable(curNode)))
+        continue; // do nothing to this block
+      
+      // if no curBlock, make one
+      if (!curBlock)
+      {
+        res = mEditor->CreateNode(*aBlockTag, curParent, offset, getter_AddRefs(curBlock));
+        if (NS_FAILED(res)) return res;
+      }
+      
+      // if curNode is a Break, replace it with a return if we are going to 
+      // xxx floppy moose
+ 
+      // this is a continuation of some inline nodes that belong together in
+      // the same block item.  use curBlock
+      PRUint32 blockLen;
+      res = mEditor->GetLengthOfDOMNode(curBlock, blockLen);
+      if (NS_FAILED(res)) return res;
+      res = mEditor->DeleteNode(curNode);
+      if (NS_FAILED(res)) return res;
+      res = mEditor->InsertNode(curNode, curBlock, blockLen);
+      if (NS_FAILED(res)) return res;
+    }
+  }
+  return res;
+}
+
+
+
diff --git a/editor/base/nsHTMLEditRules.h b/editor/base/nsHTMLEditRules.h
index 3ad88b6b8228..95477e05067b 100644
--- a/editor/base/nsHTMLEditRules.h
+++ b/editor/base/nsHTMLEditRules.h
@@ -39,12 +39,6 @@ public:
 
 protected:
 
-  enum IterDirection
-  {
-    kIterForward,
-    kIterBackward
-  };
-
   enum RulesEndpoint
   {
     kStart,
@@ -66,9 +60,7 @@ protected:
   nsresult WillIndent(nsIDOMSelection *aSelection, PRBool *aCancel);
   nsresult WillOutdent(nsIDOMSelection *aSelection, PRBool *aCancel);
   nsresult WillAlign(nsIDOMSelection *aSelection, const nsString *alignType, PRBool *aCancel);
-  nsresult WillMakeHeader(nsIDOMSelection *aSelection, PRBool *aCancel);
-  nsresult WillMakeAddress(nsIDOMSelection *aSelection, PRBool *aCancel);
-  nsresult WillMakePRE(nsIDOMSelection *aSelection, PRBool *aCancel);
+  nsresult WillMakeBasicBlock(nsIDOMSelection *aSelection, const nsString *aBlockType, PRBool *aCancel);
 
   nsresult InsertTab(nsIDOMSelection *aSelection, PRBool *aCancel, PlaceholderTxn **aTxn, nsString *outString);
   nsresult InsertSpace(nsIDOMSelection *aSelection, PRBool *aCancel, PlaceholderTxn **aTxn, nsString *outString);
@@ -84,6 +76,7 @@ protected:
   static PRBool IsHeader(nsIDOMNode *aNode);
   static PRBool IsParagraph(nsIDOMNode *aNode);
   static PRBool IsListItem(nsIDOMNode *aNode);
+  static PRBool IsList(nsIDOMNode *aNode);
   static PRBool IsUnorderedList(nsIDOMNode *aNode);
   static PRBool IsOrderedList(nsIDOMNode *aNode);
   static PRBool IsBreak(nsIDOMNode *aNode);
@@ -94,19 +87,24 @@ protected:
   nsresult IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock);
   PRBool IsFirstNode(nsIDOMNode *aNode);
   PRBool IsLastNode(nsIDOMNode *aNode);
+
   nsresult GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt32 aOffset, 
                             PRInt32 actionID, nsCOMPtr *outNode, PRInt32 *outOffset);
-
   nsresult GetPromotedRanges(nsIDOMSelection *inSelection, 
                              nsCOMPtr *outArrayOfRanges, 
                              PRInt32 inOperationType);
-  static nsresult GetNodesForOperation(nsISupportsArray *inArrayOfRanges, 
+  nsresult GetNodesForOperation(nsISupportsArray *inArrayOfRanges, 
                                    nsCOMPtr *outArrayOfNodes, 
                                    PRInt32 inOperationType);
-  static nsresult MakeTransitionList(nsISupportsArray *inArrayOfNodes, 
+  nsresult GetChildNodesForOperation(nsIDOMNode *inNode, 
+                                   nsCOMPtr *outArrayOfNodes);
+  nsresult MakeTransitionList(nsISupportsArray *inArrayOfNodes, 
                                    nsVoidArray *inTransitionArray);
-  
-  nsresult ReplaceContainer(nsIDOMNode *inNode, nsCOMPtr *outNode, nsString &aNodeType);
+                                   
+  nsresult ShouldMakeEmptyBlock(nsIDOMSelection *aSelection, const nsString *blockTag, PRBool *outMakeEmpty);
+  nsresult ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString *aBlockTag);
+
+  nsresult ReplaceContainer(nsIDOMNode *inNode, nsCOMPtr *outNode, const nsString &aNodeType);
   nsresult RemoveContainer(nsIDOMNode *inNode);
   nsresult InsertContainerAbove(nsIDOMNode *inNode, nsCOMPtr *outNode, nsString &aNodeType);
 
diff --git a/editor/base/nsHTMLEditor.cpp b/editor/base/nsHTMLEditor.cpp
index 53c231da4103..a3c1d2cfb3db 100644
--- a/editor/base/nsHTMLEditor.cpp
+++ b/editor/base/nsHTMLEditor.cpp
@@ -1317,14 +1317,17 @@ NS_IMETHODIMP nsHTMLEditor::SetParagraphFormat(const nsString& aParagraphFormat)
   //Kinda sad to waste memory just to force lower case
   nsAutoString tag = aParagraphFormat;
   tag.ToLowerCase();
-  if (tag == "normal" || tag == "p") {
-    res = RemoveParagraphStyle();
-  } else if (tag == "li") {
+  if (tag == "normal") 
+  {
+    res = InsertBasicBlock("p");
+  } 
+  else if (tag == "li") 
+  {
     res = InsertList("ul");
-  } else if (tag[0] == 'h') {
-    res = InsertHeader(tag);
-  } else {
-    res = ReplaceBlockParent(tag);
+  } 
+  else 
+  {
+    res = InsertBasicBlock(tag);
   }
   return res;
 }
@@ -1646,6 +1649,7 @@ nsHTMLEditor::InsertList(const nsString& aListType)
     // XXX - revisit when layout is fixed
     res = selection->Collapse(newItem,0);
     if (NS_FAILED(res)) return res;
+#if 0    
     nsAutoString theText(" ");
     res = InsertText(theText);
     if (NS_FAILED(res)) return res;
@@ -1654,6 +1658,7 @@ nsHTMLEditor::InsertList(const nsString& aListType)
     if (NS_FAILED(res)) return res;
     res = selection->Collapse(node,0);
     if (NS_FAILED(res)) return res;
+#endif
   }
 
   return res;
@@ -1661,13 +1666,13 @@ nsHTMLEditor::InsertList(const nsString& aListType)
 
 
 NS_IMETHODIMP
-nsHTMLEditor::InsertHeader(const nsString& aHeaderType)
+nsHTMLEditor::InsertBasicBlock(const nsString& aBlockType)
 {
 #ifdef ENABLE_JS_EDITOR_LOG
   nsAutoJSEditorLogLock logLock(mJSEditorLog);
 
   if (mJSEditorLog)
-    mJSEditorLog->InsertHeader(aHeaderType);
+    mJSEditorLog->InsertBasicBlock(aBlockType);
 #endif // ENABLE_JS_EDITOR_LOG
 
   nsresult res;
@@ -1680,7 +1685,8 @@ nsHTMLEditor::InsertHeader(const nsString& aHeaderType)
   
   // pre-process
   nsEditor::GetSelection(getter_AddRefs(selection));
-  nsTextRulesInfo ruleInfo(nsHTMLEditRules::kMakeHeader);
+  nsTextRulesInfo ruleInfo(nsHTMLEditRules::kMakeBasicBlock);
+  ruleInfo.blockType = &aBlockType;
   res = mRules->WillDoAction(selection, &ruleInfo, &cancel);
   if (cancel || (NS_FAILED(res))) return res;
 
@@ -1700,12 +1706,12 @@ nsHTMLEditor::InsertHeader(const nsString& aHeaderType)
   
   if (isCollapsed)
   {
-    // have to find a place to put the header
+    // have to find a place to put the block
     nsCOMPtr parent = node;
     nsCOMPtr topChild = node;
     nsCOMPtr tmp;
     
-    while ( !CanContainTag(parent, aHeaderType))
+    while ( !CanContainTag(parent, aBlockType))
     {
       parent->GetParentNode(getter_AddRefs(tmp));
       if (!tmp) return NS_ERROR_FAILURE;
@@ -1720,9 +1726,23 @@ nsHTMLEditor::InsertHeader(const nsString& aHeaderType)
       if (NS_FAILED(res)) return res;
     }
 
-    // make a header
-    nsCOMPtr newHeader;
-    res = CreateNode(aHeaderType, parent, offset, getter_AddRefs(newHeader));
+    // make a block
+    nsCOMPtr newBlock;
+    res = CreateNode(aBlockType, parent, offset, getter_AddRefs(newBlock));
+    if (NS_FAILED(res)) return res;
+    
+    // xxx
+    
+    // put a space in it so layout will draw it
+    res = selection->Collapse(newBlock,0);
+    if (NS_FAILED(res)) return res;
+    nsAutoString theText(nbsp);
+    res = InsertText(theText);
+    if (NS_FAILED(res)) return res;
+    // reposition selection to before the space character
+    res = GetStartNodeAndOffset(selection, &node, &offset);
+    if (NS_FAILED(res)) return res;
+    res = selection->Collapse(node,0);
     if (NS_FAILED(res)) return res;
   }
 
diff --git a/editor/base/nsHTMLEditor.h b/editor/base/nsHTMLEditor.h
index 96b12da50506..90f448548c38 100644
--- a/editor/base/nsHTMLEditor.h
+++ b/editor/base/nsHTMLEditor.h
@@ -97,7 +97,7 @@ public:
   NS_IMETHOD RemoveParent(const nsString &aParentTag);
 
   NS_IMETHOD InsertList(const nsString& aListType);
-  NS_IMETHOD InsertHeader(const nsString& aHeaderType);
+  NS_IMETHOD InsertBasicBlock(const nsString& aBlockType);
   NS_IMETHOD Indent(const nsString& aIndent);
   NS_IMETHOD Align(const nsString& aAlign);
 
diff --git a/editor/base/nsTextEditRules.h b/editor/base/nsTextEditRules.h
index ccc7b5414ac4..58c77dd517e7 100644
--- a/editor/base/nsTextEditRules.h
+++ b/editor/base/nsTextEditRules.h
@@ -73,9 +73,7 @@ public:
     kIndent          = 3002,
     kOutdent         = 3003,
     kAlign           = 3004,
-    kMakeHeader      = 3005,
-    kMakeAddress     = 3006,
-    kMakePRE         = 3007,
+    kMakeBasicBlock  = 3005,
     kInsertElement   = 3008
   };
   
@@ -179,6 +177,7 @@ class nsTextRulesInfo : public nsRulesInfo
     collapsedAction(nsIEditor::eDeleteNext),
     bOrdered(PR_FALSE),
     alignType(0),
+    blockType(0),
     insertElement(0)
     {};
 
@@ -200,6 +199,9 @@ class nsTextRulesInfo : public nsRulesInfo
   // kAlign
   const nsString *alignType;
   
+  // kMakeBasicBlock
+  const nsString *blockType;
+  
   // kInsertElement
   const nsIDOMElement* insertElement;
 };
diff --git a/editor/libeditor/base/nsEditor.cpp b/editor/libeditor/base/nsEditor.cpp
index b8c734ce1e24..9196164a4ba5 100644
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -3153,7 +3153,8 @@ nsEditor::GetTag(nsIDOMNode *aNode)
   }
   
   nsCOMPtr content = do_QueryInterface(aNode);
-  content->GetTag(*getter_AddRefs(atom));
+  if (content)
+    content->GetTag(*getter_AddRefs(atom));
 
   return atom;
 }
@@ -3407,7 +3408,7 @@ nsEditor::NextNodeInBlock(nsIDOMNode *aNode, IterDirection aDir)
   	if (NS_FAILED(iter->CurrentNode(getter_AddRefs(content)))) return nullNode;
     // ignore nodes that aren't elements or text, or that are the block parent 
     node = do_QueryInterface(content);
-    if (node && IsTextOrElementNode(node) && (node != blockParent) && (node!=nsCOMPtr(dont_QueryInterface(aNode))))
+    if (node && IsTextOrElementNode(node) && (node != blockParent) && (node.get() != aNode))
       return node;
     
     if (aDir == kIterForward)
@@ -3680,7 +3681,7 @@ nsEditor::SplitNodeDeep(nsIDOMNode *aNode,
     if (!nodeAsText || (offset && (offset != textLen)))
     {
       bDoSplit = PR_TRUE;
-      nsresult res = SplitNode(nodeToSplit, offset, getter_AddRefs(tempNode));
+      res = SplitNode(nodeToSplit, offset, getter_AddRefs(tempNode));
       if (NS_FAILED(res)) return res;
     }
 
diff --git a/editor/libeditor/html/nsHTMLEditRules.cpp b/editor/libeditor/html/nsHTMLEditRules.cpp
index cb4f162546c9..32ae4d4e162e 100644
--- a/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -107,12 +107,8 @@ nsHTMLEditRules::WillDoAction(nsIDOMSelection *aSelection,
       return WillOutdent(aSelection, aCancel);
     case kAlign:
       return WillAlign(aSelection, info->alignType, aCancel);
-    case kMakeHeader:
-      return WillMakeHeader(aSelection, aCancel);
-    case kMakeAddress:
-      return WillMakeAddress(aSelection, aCancel);
-    case kMakePRE:
-      return WillMakePRE(aSelection, aCancel);
+    case kMakeBasicBlock:
+      return WillMakeBasicBlock(aSelection, info->blockType, aCancel);
     case kInsertElement:
       return WillInsert(aSelection, aCancel);
   }
@@ -258,7 +254,13 @@ nsHTMLEditRules::WillInsertBreak(nsIDOMSelection *aSelection, PRBool *aCancel)
     return mEditor->InsertText(theString);
   }
 
-  nsCOMPtr blockParent = mEditor->GetBlockNodeParent(node);
+  nsCOMPtr blockParent;
+  
+  if (nsEditor::IsBlockNode(node)) 
+    blockParent = node;
+  else 
+    blockParent = mEditor->GetBlockNodeParent(node);
+    
   if (!blockParent) return NS_ERROR_FAILURE;
   
   // headers: put selection after the header
@@ -521,45 +523,18 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo
   if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; }
   // initialize out param
   *aCancel = PR_FALSE;
+  nsAutoString blockType("ul");
+  if (aOrdered) blockType = "ol";
   
   nsAutoSelectionReset selectionResetter(aSelection);
   nsresult res;
   
-  // check for collapsed selection in empty block or after a 
. - // Either way, we want to get the default behavior of creating - // an empty list. - - PRBool isCollapsed; - res = aSelection->GetIsCollapsed(&isCollapsed); + PRBool outMakeEmpty; + res = ShouldMakeEmptyBlock(aSelection, &blockType, &outMakeEmpty); if (NS_FAILED(res)) return res; - if (isCollapsed) - { - // is it after a
? Two possibilities: selection right after
; - // and selection at front of text node following
- nsCOMPtr parent, prevChild; - PRInt32 offset; - res = nsEditor::GetStartNodeAndOffset(aSelection, &parent, &offset); - if (nsEditor::IsTextNode(parent) && !offset) - { - nsCOMPtr node = parent; - res = nsEditor::GetNodeLocation(node, &parent, &offset); - if (NS_FAILED(res)) return res; - } - if (offset) - { - prevChild = nsEditor::GetChildAt(parent, offset-1); - if (prevChild && IsBreak(prevChild)) - return NS_OK; // insertion point after break - } - - // otherwise check for being in an empty block - PRBool isEmptyBlock; - res = IsEmptyBlock(parent, &isEmptyBlock); - if (NS_FAILED(res)) return res; - if (isEmptyBlock) return NS_OK; - } + if (outMakeEmpty) return NS_OK; - // ok, we aren't vreating a new empty list. Instead we are converting + // ok, we aren't creating a new empty list. Instead we are converting // the set of blocks implied by the selection into a list. // convert the selection ranges into "promoted" selection ranges: @@ -578,13 +553,6 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo res = GetNodesForOperation(arrayOfRanges, &arrayOfNodes, kMakeList); if (NS_FAILED(res)) return res; - // if we ended up with any nodes in the list that aren't blocknodes, - // find their block parent instead and use that. - - // i started writing this and then the sky fell. there are big questions - // about what to do here. i may need to switch from thinking about an array of - // nodes to act on to instead think of an array of ranges to act on. - // Remove all non-editable nodes. Leave them be. PRUint32 listCount; @@ -621,7 +589,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo nsCOMPtr tmpNode = nsEditor::GetChildAt(curNode, 0); if (IsDiv(tmpNode) || IsOrderedList(tmpNode) || IsUnorderedList(tmpNode) || IsBlockquote(tmpNode)) { - // check editablility XXX moose + // check editablility XXX floppy moose curNode = tmpNode; } else break; @@ -673,7 +641,6 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo { // make a new ordered list, insert it where the current unordered list is, // and move all the children to the new list, and remove the old list - nsAutoString blockType("ol"); res = ReplaceContainer(curNode,&newBlock,blockType); if (NS_FAILED(res)) return res; curList = newBlock; @@ -692,7 +659,6 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo { // make a new unordered list, insert it where the current ordered list is, // and move all the children to the new list, and remove the old list - nsAutoString blockType("ul"); ReplaceContainer(curNode,&newBlock,blockType); if (NS_FAILED(res)) return res; curList = newBlock; @@ -707,7 +673,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo } else if (IsDiv(curNode) || IsBlockquote(curNode)) { - // XXX moose + // XXX floppy moose } } // lonely node @@ -724,7 +690,7 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo prevListItem = 0; } - // if curNode is a Break, delete it, and quit remember prev list item + // if curNode is a Break, delete it, and quit remembering prev list item if (IsBreak(curNode)) { res = mEditor->DeleteNode(curNode); @@ -781,53 +747,35 @@ nsHTMLEditRules::WillMakeList(nsIDOMSelection *aSelection, PRBool aOrdered, PRBo nsresult -nsHTMLEditRules::WillMakeHeader(nsIDOMSelection *aSelection, PRBool *aCancel) +nsHTMLEditRules::WillMakeBasicBlock(nsIDOMSelection *aSelection, const nsString *aBlockType, PRBool *aCancel) { if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } // initialize out param - *aCancel = PR_TRUE; + *aCancel = PR_FALSE; + PRBool makeEmpty; + nsresult res = ShouldMakeEmptyBlock(aSelection, aBlockType, &makeEmpty); + if (NS_FAILED(res)) return res; + + if (makeEmpty) return res; // just insert a new empty block + + // else it's not that easy... nsAutoSelectionReset selectionResetter(aSelection); - nsresult res; - - // Get the ranges for each break-delimited block - nsCOMPtr enumerator; - res = aSelection->GetEnumerator(getter_AddRefs(enumerator)); - if (NS_SUCCEEDED(res) && enumerator) - { - for (enumerator->First(); NS_OK!=enumerator->IsDone(); enumerator->Next()) - { - nsCOMPtr currentItem; - res = enumerator->CurrentItem(getter_AddRefs(currentItem)); - if ((NS_SUCCEEDED(res)) && (currentItem)) - { - nsCOMPtr range( do_QueryInterface(currentItem) ); - PRBool isCollapsed; - range->GetIsCollapsed(&isCollapsed); - if (PR_FALSE==isCollapsed) // don't make headers out of empty ranges - { - nsCOMPtr arrayOfRanges; - res = mEditor->GetBlockSectionsForRange(range, arrayOfRanges); - PRUint32 rangeCount; - res = arrayOfRanges->Count(&rangeCount); - if (NS_FAILED(res)) return res; - - PRInt32 i; - nsCOMPtr opRange; - nsCOMPtr isupports; + *aCancel = PR_TRUE; - for (i = 0; i < rangeCount; i++) - { - isupports = (dont_AddRef)(arrayOfRanges->ElementAt(i)); - opRange = do_QueryInterface(isupports); - // ConvertRangeToTag - } - } - } - } - } - // unfinished, just return NS_OK for now - return NS_OK; + nsCOMPtr arrayOfRanges; + res = GetPromotedRanges(aSelection, &arrayOfRanges, kMakeBasicBlock); + if (NS_FAILED(res)) return res; + + // use these ranges to contruct a list of nodes to act on. + nsCOMPtr arrayOfNodes; + res = GetNodesForOperation(arrayOfRanges, &arrayOfNodes, kMakeBasicBlock); + if (NS_FAILED(res)) return res; + + // Ok, now go through all the nodes and make the right kind of blocks, + // or whatever is approriate. Wohoo! + res = ApplyBlockStyle(arrayOfNodes, aBlockType); + return res; } @@ -1015,15 +963,22 @@ nsHTMLEditRules::WillAlign(nsIDOMSelection *aSelection, const nsString *alignTyp { if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } // initialize out param - *aCancel = PR_TRUE; + *aCancel = PR_FALSE; nsAutoSelectionReset selectionResetter(aSelection); nsresult res = NS_OK; + + PRBool outMakeEmpty; + res = ShouldMakeEmptyBlock(aSelection, alignType, &outMakeEmpty); + if (NS_FAILED(res)) return res; + if (outMakeEmpty) return NS_OK; + // convert the selection ranges into "promoted" selection ranges: // this basically just expands the range to include the immediate // block parent, and then further expands to include any ancestors // whose children are all in the range + *aCancel = PR_TRUE; nsCOMPtr arrayOfRanges; res = GetPromotedRanges(aSelection, &arrayOfRanges, kAlign); @@ -1100,32 +1055,6 @@ nsHTMLEditRules::WillAlign(nsIDOMSelection *aSelection, const nsString *alignTyp -nsresult -nsHTMLEditRules::WillMakeAddress(nsIDOMSelection *aSelection, PRBool *aCancel) -{ - if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } - // initialize out param - *aCancel = PR_FALSE; - - nsresult res = NS_OK; - - return res; -} - - -nsresult -nsHTMLEditRules::WillMakePRE(nsIDOMSelection *aSelection, PRBool *aCancel) -{ - if (!aSelection || !aCancel) { return NS_ERROR_NULL_POINTER; } - // initialize out param - *aCancel = PR_FALSE; - - nsresult res = NS_OK; - - return res; -} - - /******************************************************** * helper methods ********************************************************/ @@ -1186,6 +1115,24 @@ nsHTMLEditRules::IsListItem(nsIDOMNode *node) } +/////////////////////////////////////////////////////////////////////////// +// IsList: true if node an html list +// +PRBool +nsHTMLEditRules::IsList(nsIDOMNode *node) +{ + NS_PRECONDITION(node, "null parent passed to nsHTMLEditRules::IsList"); + nsAutoString tag; + nsEditor::GetTagString(node,tag); + if ( (tag == "ol") || + (tag == "ul") ) + { + return PR_TRUE; + } + return PR_FALSE; +} + + /////////////////////////////////////////////////////////////////////////// // IsOrderedList: true if node an html orderd list // @@ -1345,6 +1292,9 @@ nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock) } 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; } } @@ -1451,12 +1401,21 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt nsCOMPtr parent = aNode; PRInt32 offset = aOffset; + if (IsBody(aNode)) + { + // we cant go any higher + *outNode = do_QueryInterface(aNode); + *outOffset = aOffset; + return res; + } + if (aWhere == kStart) { // some special casing for text nodes if (nsEditor::IsTextNode(aNode)) { - nsEditor::GetNodeLocation(aNode, &parent, &offset); + res = nsEditor::GetNodeLocation(aNode, &parent, &offset); + if (NS_FAILED(res)) return res; } else { @@ -1464,26 +1423,38 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt if (!node) node = parent; } - // if this is an inline node, back up through any prior inline nodes that - // aren't across a
from us, and that are enclosed in the same block. + // if this is an inline node who's block parent is the body, + // back up through any prior inline nodes that + // aren't across a
from us. if (!nsEditor::IsBlockNode(node)) { - nsCOMPtr prevNode; - prevNode = nsEditor::NextNodeInBlock(node, nsEditor::kIterBackward); - while (prevNode) + nsCOMPtr block = nsEditor::GetBlockNodeParent(node); + if (IsBody(block)) { - if (IsBreak(prevNode)) - break; - if (nsEditor::IsBlockNode(prevNode)) - break; - node = prevNode; + nsCOMPtr prevNode; prevNode = nsEditor::NextNodeInBlock(node, nsEditor::kIterBackward); + while (prevNode) + { + if (IsBreak(prevNode)) + break; + if (nsEditor::IsBlockNode(prevNode)) + break; + node = prevNode; + prevNode = nsEditor::NextNodeInBlock(node, nsEditor::kIterBackward); + } + } + else + { + // just grap the whole block + node = block; } } // finding the real start for this point. look up the tree for as long as we are the // first node in the container, and as long as we haven't hit the body node. + nsEditor::GetNodeLocation(node, &parent, &offset); + if (NS_FAILED(res)) return res; while ((IsFirstNode(node)) && (!IsBody(parent))) { node = parent; @@ -1508,39 +1479,49 @@ nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt if (!node) node = parent; } - if (node) - offset++; // since this is going to be used for a range _endpoint_, we want to be after the node - else + if (!node) node = parent; - // if this is an inline node, look ahead through any further inline nodes that + // if this is an inline node who's block parent is the body, + // look ahead through any further inline nodes that // aren't across a
from us, and that are enclosed in the same block. if (!nsEditor::IsBlockNode(node)) { - nsCOMPtr nextNode; - nextNode = nsEditor::NextNodeInBlock(node, nsEditor::kIterForward); - while (nextNode) + nsCOMPtr block = nsEditor::GetBlockNodeParent(node); + if (IsBody(block)) { - if (IsBreak(nextNode)) - break; - if (nsEditor::IsBlockNode(nextNode)) - break; - node = nextNode; + nsCOMPtr nextNode; nextNode = nsEditor::NextNodeInBlock(node, nsEditor::kIterForward); + while (nextNode) + { + if (IsBreak(nextNode)) + break; + if (nsEditor::IsBlockNode(nextNode)) + break; + node = nextNode; + nextNode = nsEditor::NextNodeInBlock(node, nsEditor::kIterForward); + } + } + else + { + // just grap the whole block + node = block; } } // finding the real end for this point. look up the tree for as long as we are the // last node in the container, and as long as we haven't hit the body node. + nsEditor::GetNodeLocation(node, &parent, &offset); + if (NS_FAILED(res)) return res; while ((IsLastNode(node)) && (!IsBody(parent))) { node = parent; res = nsEditor::GetNodeLocation(node, &parent, &offset); if (NS_FAILED(res)) return res; - offset++; } *outNode = parent; + offset++; // add one since this in an endpoint - want to be AFTER node. *outOffset = offset; return res; } @@ -1669,6 +1650,41 @@ nsHTMLEditRules::GetNodesForOperation(nsISupportsArray *inArrayOfRanges, +/////////////////////////////////////////////////////////////////////////// +// GetChildNodesForOperation: +// +nsresult +nsHTMLEditRules::GetChildNodesForOperation(nsIDOMNode *inNode, + nsCOMPtr *outArrayOfNodes) +{ + if (!inNode || !outArrayOfNodes) return NS_ERROR_NULL_POINTER; + + nsresult res = NS_NewISupportsArray(getter_AddRefs(*outArrayOfNodes)); + if (NS_FAILED(res)) return res; + + nsCOMPtr childNodes; + res = inNode->GetChildNodes(getter_AddRefs(childNodes)); + if (NS_FAILED(res)) return res; + PRUint32 childCount; + res = childNodes->GetLength(&childCount); + if (NS_FAILED(res)) return res; + + PRUint32 i; + nsCOMPtr node; + nsCOMPtr isupports; + for (i = 0; i < childCount; i++) + { + res = childNodes->Item( i, getter_AddRefs(node)); + if (!node) return NS_ERROR_FAILURE; + isupports = do_QueryInterface(node); + (*outArrayOfNodes)->AppendElement(isupports); + if (NS_FAILED(res)) return res; + } + return res; +} + + + /////////////////////////////////////////////////////////////////////////// // MakeTransitionList: detect all the transitions in the array, where a // transition means that adjacent nodes in the array @@ -1716,7 +1732,7 @@ nsHTMLEditRules::MakeTransitionList(nsISupportsArray *inArrayOfNodes, nsresult nsHTMLEditRules::ReplaceContainer(nsIDOMNode *inNode, nsCOMPtr *outNode, - nsString &aNodeType) + const nsString &aNodeType) { if (!inNode || !outNode) return NS_ERROR_NULL_POINTER; @@ -1733,13 +1749,12 @@ nsHTMLEditRules::ReplaceContainer(nsIDOMNode *inNode, offset = 0; while (bHasMoreChildren) { - inNode->GetFirstChild(getter_AddRefs(child)); + inNode->GetLastChild(getter_AddRefs(child)); res = mEditor->DeleteNode(child); if (NS_FAILED(res)) return res; - res = mEditor->InsertNode(child, *outNode, offset); + res = mEditor->InsertNode(child, *outNode, 0); if (NS_FAILED(res)) return res; inNode->HasChildNodes(&bHasMoreChildren); - offset++; } res = mEditor->DeleteNode(inNode); return res; @@ -1952,42 +1967,43 @@ nsHTMLEditRules::InsertSpace(nsIDOMSelection *aSelection, nsresult nsHTMLEditRules::ReturnInHeader(nsIDOMSelection *aSelection, nsIDOMNode *aHeader, - nsIDOMNode *aTextNode, + nsIDOMNode *aNode, PRInt32 aOffset) { - if (!aSelection || !aHeader || !aTextNode) return NS_ERROR_NULL_POINTER; + if (!aSelection || !aHeader || !aNode) return NS_ERROR_NULL_POINTER; - nsCOMPtr leftNode; - nsCOMPtr textNode = do_QueryInterface(aTextNode); // to hold a ref across the delete call - // split the node - nsresult res = mEditor->SplitNode(aTextNode, aOffset, getter_AddRefs(leftNode)); + // remeber where the header is + nsCOMPtr headerParent; + PRInt32 offset; + nsresult res = nsEditor::GetNodeLocation(aHeader, &headerParent, &offset); if (NS_FAILED(res)) return res; - - // move the right node outside of the header, via deletion/insertion - // delete the right node - res = mEditor->DeleteNode(textNode); - if (NS_FAILED(res)) return res; - - // insert the right node - nsCOMPtr p; - aHeader->GetParentNode(getter_AddRefs(p)); - PRInt32 indx = mEditor->GetIndexOf(p,aHeader); - res = mEditor->InsertNode(textNode,p,indx+1); - if (NS_FAILED(res)) return res; - - // merge text node with like sibling, if any - nsCOMPtr sibling; - textNode->GetNextSibling(getter_AddRefs(sibling)); - if (sibling && mEditor->IsTextNode(sibling) && mEditor->IsEditable(sibling)) - { - res = mEditor->JoinNodes(textNode,sibling,p); - if (NS_FAILED(res)) return res; - textNode = sibling; // sibling was the node kept by the join; remember it in "textNode" - } - - // position selection before inserted node - res = aSelection->Collapse(textNode,0); + // split the header + PRInt32 newOffset; + res = mEditor->SplitNodeDeep( aHeader, aNode, aOffset, &newOffset); + if (NS_FAILED(res)) return res; + +// revisit the below when we move to using divs as standard paragraphs. +// need to create an "empty" div in the fisrt case below, and need to +// use a div instead of a

in the second case below + + // if the new (righthand) header node is empty, delete it + PRBool isEmpty; + res = IsEmptyBlock(aHeader, &isEmpty); + if (NS_FAILED(res)) return res; + if (isEmpty) + { + res = mEditor->DeleteNode(aHeader); + if (NS_FAILED(res)) return res; + res = aSelection->Collapse(headerParent,offset+1); + return res; + } + // else rewrap it in a paragraph + nsCOMPtr newBlock; + nsAutoString blockType("p"); + res = ReplaceContainer(aHeader,&newBlock,blockType); + if (NS_FAILED(res)) return res; + res = aSelection->Collapse(newBlock,0); return res; } @@ -2097,12 +2113,282 @@ nsHTMLEditRules::ReturnInListItem(nsIDOMSelection *aSelection, PRInt32 aOffset) { if (!aSelection || !aListItem || !aNode) return NS_ERROR_NULL_POINTER; - - PRInt32 newOffset; - nsresult res = mEditor->SplitNodeDeep( aListItem, aNode, aOffset, &newOffset); + nsresult res = NS_OK; + + nsCOMPtr listitem; + + // sanity check + NS_PRECONDITION(PR_TRUE == IsListItem(aListItem), "expected a list item and didnt get one"); + + // if we are in an empty listitem, then we want to pop up out of the list + PRBool isEmpty; + res = IsEmptyBlock(aListItem, &isEmpty); if (NS_FAILED(res)) return res; - res = aSelection->Collapse(aNode,0); + if (isEmpty) + { + nsCOMPtr list, listparent; + PRInt32 offset; + list = nsEditor::GetBlockNodeParent(aListItem); + res = nsEditor::GetNodeLocation(list, &listparent, &offset); + if (NS_FAILED(res)) return res; + + // are we in a sublist? + if (IsList(listparent)) //in a sublist + { + // if so, move this list item out of this list and into the grandparent list + res = mEditor->DeleteNode(aListItem); + if (NS_FAILED(res)) return res; + res = mEditor->InsertNode(aListItem,listparent,offset+1); + if (NS_FAILED(res)) return res; + res = aSelection->Collapse(aListItem,0); + } + else + { + // otherwise kill this listitem and set the selection to after the parent list + res = mEditor->DeleteNode(aListItem); + if (NS_FAILED(res)) return res; + res = aSelection->Collapse(listparent,offset+1); + } + return res; + } + + // else we want a new list item at the same list level + PRInt32 newOffset; + res = mEditor->SplitNodeDeep( aListItem, aNode, aOffset, &newOffset); + if (NS_FAILED(res)) return res; + res = aSelection->Collapse(aListItem,0); return res; } +/////////////////////////////////////////////////////////////////////////// +// ShouldMakeEmptyBlock: determine if a block transformation should make +// a new empty block, or instead transform a block +// +nsresult +nsHTMLEditRules::ShouldMakeEmptyBlock(nsIDOMSelection *aSelection, + const nsString *blockTag, + PRBool *outMakeEmpty) +{ + // a note about strategy: + // this routine will be called by the rules code to figure out + // if it should do something, or let the nsHTMLEditor default + // action happen. The default action is to insert a new block. + // Note that if _nothing_ should happen, ie, the selection is + // already entireyl inside a block (or blocks) or the correct type, + // then you don't want to return true in outMakeEmpty, since the + // defualt code will insert a new empty block anyway, rather than + // doing nothing. So we have to detect that case and return false. + + if (!aSelection || !outMakeEmpty) return NS_ERROR_NULL_POINTER; + nsresult res = NS_OK; + + // if the selection is collapsed, and + // if we in the body, or after a
with + // no more inline content before the next block, then we want + // a new block. Otherwise we want to trasform a block + + // xxx possible bug: selection could be not callapsed, but + // still empty. it would be nice to have a call for this: IsEmptySelection() + + PRBool isCollapsed; + res = aSelection->GetIsCollapsed(&isCollapsed); + if (NS_FAILED(res)) return res; + if (isCollapsed) + { + nsCOMPtr parent; + PRInt32 offset; + res = nsEditor::GetStartNodeAndOffset(aSelection, &parent, &offset); + if (NS_FAILED(res)) return res; + + // is selection point in the body? + if (IsBody(parent)) + { + *outMakeEmpty = PR_TRUE; + return res; + } + + // see if block parent is already right kind of block. + // See strategy comment above. + nsCOMPtr block; + if (!nsEditor::IsBlockNode(parent)) + block = nsEditor::GetBlockNodeParent(parent); + else + block = parent; + if (block) + { + nsAutoString tag; + nsEditor::GetTagString(block,tag); + if (tag == *blockTag) + { + *outMakeEmpty = PR_FALSE; + return res; + } + } + + // are we in a textnode or inline node? + if (!nsEditor::IsBlockNode(parent)) + { + // we must be in a text or inline node - convert existing block + *outMakeEmpty = PR_FALSE; + return res; + } + + // is it after a
with no inline nodes after it, or a
after it?? + if (offset) + { + nsCOMPtr prevChild, nextChild, tmp; + prevChild = nsEditor::GetChildAt(parent, offset-1); + while (prevChild && !mEditor->IsEditable(prevChild)) + { + // search back until we either find an editable node, + // or hit the beginning of the block + tmp = nsEditor::NextNodeInBlock(prevChild, nsEditor::kIterBackward); + prevChild = tmp; + } + + if (prevChild && IsBreak(prevChild)) + { + nextChild = nsEditor::GetChildAt(parent, offset); + while (nextChild && !mEditor->IsEditable(nextChild)) + { + // search back until we either find an editable node, + // or hit the beginning of the block + tmp = nsEditor::NextNodeInBlock(nextChild, nsEditor::kIterForward); + nextChild = tmp; + } + if (!nextChild || IsBreak(nextChild) || nsEditor::IsBlockNode(nextChild)) + { + // we are after a
and not before inline content, + // or we are between
s. + // make an empty block + *outMakeEmpty = PR_FALSE; + return res; + } + } + } + } + // otherwise transform an existing block + *outMakeEmpty = PR_FALSE; + return res; +} + + +/////////////////////////////////////////////////////////////////////////// +// ApplyBlockStyle: do whatever it takes to make the list of nodes into +// one or more blocks of type blockTag. +// +nsresult +nsHTMLEditRules::ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString *aBlockTag) +{ + // intent of this routine is to be used for converting to/from + // headers, paragraphs (or moz-divs), pre, and address. Those blocks + // that pretty much just contain inline things... + + if (!arrayOfNodes || !aBlockTag) return NS_ERROR_NULL_POINTER; + nsresult res = NS_OK; + + nsCOMPtr curNode, curParent, curBlock, newBlock; + PRInt32 offset; + PRUint32 listCount; + arrayOfNodes->Count(&listCount); + + PRUint32 i; + for (i=0; i isupports = (dont_AddRef)(arrayOfNodes->ElementAt(i)); + nsCOMPtr curNode( do_QueryInterface(isupports ) ); + res = nsEditor::GetNodeLocation(curNode, &curParent, &offset); + if (NS_FAILED(res)) return res; + nsAutoString curNodeTag; + nsEditor::GetTagString(curNode, curNodeTag); + + + // is it already the right kind of block? + if (curNodeTag == *aBlockTag) + { + curBlock = 0; // forget any previous block used for previous inline nodes + continue; // do nothing to this block + } + + // if curNode is a

 and we are converting to non-pre, we need
+    // to process the text inside the 
 so as to convert returns
+    // to breaks, and runs of spaces to nbsps.
+    // xxx floppy moose
+      
+    // if curNode is a p, header, address, or pre, replace 
+    // it with a new block of correct type.
+    // xxx floppy moose: pre cant hold everything the others can
+    if ((curNodeTag == "pre") || 
+        (curNodeTag == "p")   ||
+        (curNodeTag == "h1")  ||
+        (curNodeTag == "h2")  ||
+        (curNodeTag == "h3")  ||
+        (curNodeTag == "h4")  ||
+        (curNodeTag == "h5")  ||
+        (curNodeTag == "h6")  ||
+        (curNodeTag == "address"))
+    {
+      curBlock = 0;  // forget any previous block used for previous inline nodes
+      res = ReplaceContainer(curNode, &newBlock, *aBlockTag);
+      if (NS_FAILED(res)) return res;
+    }
+    else if ((curNodeTag == "table")      || 
+             (curNodeTag == "tbody")      ||
+             (curNodeTag == "tr")         ||
+             (curNodeTag == "td")         ||
+             (curNodeTag == "ol")         ||
+             (curNodeTag == "ul")         ||
+             (curNodeTag == "li")         ||
+             (curNodeTag == "blockquote") ||
+             (curNodeTag == "div"))
+    {
+      curBlock = 0;  // forget any previous block used for previous inline nodes
+      // recursion time
+      nsCOMPtr childArray;
+      res = GetChildNodesForOperation(curNode, &childArray);
+      if (NS_FAILED(res)) return res;
+      res = ApplyBlockStyle(childArray, aBlockTag);
+      if (NS_FAILED(res)) return res;
+    }
+    
+    // if curNode is inline, pull it into curBlock
+    // note: it's assumed that consecutive inline nodes in the 
+    // arrayOfNodes are actually members of the same block parent.
+    // this happens to be true now as a side effect of how
+    // arrayOfNodes is contructed, but some additional logic should
+    // be added here if that should change
+    
+    else if (nsEditor::IsInlineNode(curNode))
+    {
+      // if curNode is a non editable, drop it if we are going to 
+      if ((*aBlockTag == "pre") && (!mEditor->IsEditable(curNode)))
+        continue; // do nothing to this block
+      
+      // if no curBlock, make one
+      if (!curBlock)
+      {
+        res = mEditor->CreateNode(*aBlockTag, curParent, offset, getter_AddRefs(curBlock));
+        if (NS_FAILED(res)) return res;
+      }
+      
+      // if curNode is a Break, replace it with a return if we are going to 
+      // xxx floppy moose
+ 
+      // this is a continuation of some inline nodes that belong together in
+      // the same block item.  use curBlock
+      PRUint32 blockLen;
+      res = mEditor->GetLengthOfDOMNode(curBlock, blockLen);
+      if (NS_FAILED(res)) return res;
+      res = mEditor->DeleteNode(curNode);
+      if (NS_FAILED(res)) return res;
+      res = mEditor->InsertNode(curNode, curBlock, blockLen);
+      if (NS_FAILED(res)) return res;
+    }
+  }
+  return res;
+}
+
+
+
diff --git a/editor/libeditor/html/nsHTMLEditRules.h b/editor/libeditor/html/nsHTMLEditRules.h
index 3ad88b6b8228..95477e05067b 100644
--- a/editor/libeditor/html/nsHTMLEditRules.h
+++ b/editor/libeditor/html/nsHTMLEditRules.h
@@ -39,12 +39,6 @@ public:
 
 protected:
 
-  enum IterDirection
-  {
-    kIterForward,
-    kIterBackward
-  };
-
   enum RulesEndpoint
   {
     kStart,
@@ -66,9 +60,7 @@ protected:
   nsresult WillIndent(nsIDOMSelection *aSelection, PRBool *aCancel);
   nsresult WillOutdent(nsIDOMSelection *aSelection, PRBool *aCancel);
   nsresult WillAlign(nsIDOMSelection *aSelection, const nsString *alignType, PRBool *aCancel);
-  nsresult WillMakeHeader(nsIDOMSelection *aSelection, PRBool *aCancel);
-  nsresult WillMakeAddress(nsIDOMSelection *aSelection, PRBool *aCancel);
-  nsresult WillMakePRE(nsIDOMSelection *aSelection, PRBool *aCancel);
+  nsresult WillMakeBasicBlock(nsIDOMSelection *aSelection, const nsString *aBlockType, PRBool *aCancel);
 
   nsresult InsertTab(nsIDOMSelection *aSelection, PRBool *aCancel, PlaceholderTxn **aTxn, nsString *outString);
   nsresult InsertSpace(nsIDOMSelection *aSelection, PRBool *aCancel, PlaceholderTxn **aTxn, nsString *outString);
@@ -84,6 +76,7 @@ protected:
   static PRBool IsHeader(nsIDOMNode *aNode);
   static PRBool IsParagraph(nsIDOMNode *aNode);
   static PRBool IsListItem(nsIDOMNode *aNode);
+  static PRBool IsList(nsIDOMNode *aNode);
   static PRBool IsUnorderedList(nsIDOMNode *aNode);
   static PRBool IsOrderedList(nsIDOMNode *aNode);
   static PRBool IsBreak(nsIDOMNode *aNode);
@@ -94,19 +87,24 @@ protected:
   nsresult IsEmptyBlock(nsIDOMNode *aNode, PRBool *outIsEmptyBlock);
   PRBool IsFirstNode(nsIDOMNode *aNode);
   PRBool IsLastNode(nsIDOMNode *aNode);
+
   nsresult GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode *aNode, PRInt32 aOffset, 
                             PRInt32 actionID, nsCOMPtr *outNode, PRInt32 *outOffset);
-
   nsresult GetPromotedRanges(nsIDOMSelection *inSelection, 
                              nsCOMPtr *outArrayOfRanges, 
                              PRInt32 inOperationType);
-  static nsresult GetNodesForOperation(nsISupportsArray *inArrayOfRanges, 
+  nsresult GetNodesForOperation(nsISupportsArray *inArrayOfRanges, 
                                    nsCOMPtr *outArrayOfNodes, 
                                    PRInt32 inOperationType);
-  static nsresult MakeTransitionList(nsISupportsArray *inArrayOfNodes, 
+  nsresult GetChildNodesForOperation(nsIDOMNode *inNode, 
+                                   nsCOMPtr *outArrayOfNodes);
+  nsresult MakeTransitionList(nsISupportsArray *inArrayOfNodes, 
                                    nsVoidArray *inTransitionArray);
-  
-  nsresult ReplaceContainer(nsIDOMNode *inNode, nsCOMPtr *outNode, nsString &aNodeType);
+                                   
+  nsresult ShouldMakeEmptyBlock(nsIDOMSelection *aSelection, const nsString *blockTag, PRBool *outMakeEmpty);
+  nsresult ApplyBlockStyle(nsISupportsArray *arrayOfNodes, const nsString *aBlockTag);
+
+  nsresult ReplaceContainer(nsIDOMNode *inNode, nsCOMPtr *outNode, const nsString &aNodeType);
   nsresult RemoveContainer(nsIDOMNode *inNode);
   nsresult InsertContainerAbove(nsIDOMNode *inNode, nsCOMPtr *outNode, nsString &aNodeType);
 
diff --git a/editor/libeditor/html/nsHTMLEditor.cpp b/editor/libeditor/html/nsHTMLEditor.cpp
index 53c231da4103..a3c1d2cfb3db 100644
--- a/editor/libeditor/html/nsHTMLEditor.cpp
+++ b/editor/libeditor/html/nsHTMLEditor.cpp
@@ -1317,14 +1317,17 @@ NS_IMETHODIMP nsHTMLEditor::SetParagraphFormat(const nsString& aParagraphFormat)
   //Kinda sad to waste memory just to force lower case
   nsAutoString tag = aParagraphFormat;
   tag.ToLowerCase();
-  if (tag == "normal" || tag == "p") {
-    res = RemoveParagraphStyle();
-  } else if (tag == "li") {
+  if (tag == "normal") 
+  {
+    res = InsertBasicBlock("p");
+  } 
+  else if (tag == "li") 
+  {
     res = InsertList("ul");
-  } else if (tag[0] == 'h') {
-    res = InsertHeader(tag);
-  } else {
-    res = ReplaceBlockParent(tag);
+  } 
+  else 
+  {
+    res = InsertBasicBlock(tag);
   }
   return res;
 }
@@ -1646,6 +1649,7 @@ nsHTMLEditor::InsertList(const nsString& aListType)
     // XXX - revisit when layout is fixed
     res = selection->Collapse(newItem,0);
     if (NS_FAILED(res)) return res;
+#if 0    
     nsAutoString theText(" ");
     res = InsertText(theText);
     if (NS_FAILED(res)) return res;
@@ -1654,6 +1658,7 @@ nsHTMLEditor::InsertList(const nsString& aListType)
     if (NS_FAILED(res)) return res;
     res = selection->Collapse(node,0);
     if (NS_FAILED(res)) return res;
+#endif
   }
 
   return res;
@@ -1661,13 +1666,13 @@ nsHTMLEditor::InsertList(const nsString& aListType)
 
 
 NS_IMETHODIMP
-nsHTMLEditor::InsertHeader(const nsString& aHeaderType)
+nsHTMLEditor::InsertBasicBlock(const nsString& aBlockType)
 {
 #ifdef ENABLE_JS_EDITOR_LOG
   nsAutoJSEditorLogLock logLock(mJSEditorLog);
 
   if (mJSEditorLog)
-    mJSEditorLog->InsertHeader(aHeaderType);
+    mJSEditorLog->InsertBasicBlock(aBlockType);
 #endif // ENABLE_JS_EDITOR_LOG
 
   nsresult res;
@@ -1680,7 +1685,8 @@ nsHTMLEditor::InsertHeader(const nsString& aHeaderType)
   
   // pre-process
   nsEditor::GetSelection(getter_AddRefs(selection));
-  nsTextRulesInfo ruleInfo(nsHTMLEditRules::kMakeHeader);
+  nsTextRulesInfo ruleInfo(nsHTMLEditRules::kMakeBasicBlock);
+  ruleInfo.blockType = &aBlockType;
   res = mRules->WillDoAction(selection, &ruleInfo, &cancel);
   if (cancel || (NS_FAILED(res))) return res;
 
@@ -1700,12 +1706,12 @@ nsHTMLEditor::InsertHeader(const nsString& aHeaderType)
   
   if (isCollapsed)
   {
-    // have to find a place to put the header
+    // have to find a place to put the block
     nsCOMPtr parent = node;
     nsCOMPtr topChild = node;
     nsCOMPtr tmp;
     
-    while ( !CanContainTag(parent, aHeaderType))
+    while ( !CanContainTag(parent, aBlockType))
     {
       parent->GetParentNode(getter_AddRefs(tmp));
       if (!tmp) return NS_ERROR_FAILURE;
@@ -1720,9 +1726,23 @@ nsHTMLEditor::InsertHeader(const nsString& aHeaderType)
       if (NS_FAILED(res)) return res;
     }
 
-    // make a header
-    nsCOMPtr newHeader;
-    res = CreateNode(aHeaderType, parent, offset, getter_AddRefs(newHeader));
+    // make a block
+    nsCOMPtr newBlock;
+    res = CreateNode(aBlockType, parent, offset, getter_AddRefs(newBlock));
+    if (NS_FAILED(res)) return res;
+    
+    // xxx
+    
+    // put a space in it so layout will draw it
+    res = selection->Collapse(newBlock,0);
+    if (NS_FAILED(res)) return res;
+    nsAutoString theText(nbsp);
+    res = InsertText(theText);
+    if (NS_FAILED(res)) return res;
+    // reposition selection to before the space character
+    res = GetStartNodeAndOffset(selection, &node, &offset);
+    if (NS_FAILED(res)) return res;
+    res = selection->Collapse(node,0);
     if (NS_FAILED(res)) return res;
   }
 
diff --git a/editor/libeditor/html/nsHTMLEditor.h b/editor/libeditor/html/nsHTMLEditor.h
index 96b12da50506..90f448548c38 100644
--- a/editor/libeditor/html/nsHTMLEditor.h
+++ b/editor/libeditor/html/nsHTMLEditor.h
@@ -97,7 +97,7 @@ public:
   NS_IMETHOD RemoveParent(const nsString &aParentTag);
 
   NS_IMETHOD InsertList(const nsString& aListType);
-  NS_IMETHOD InsertHeader(const nsString& aHeaderType);
+  NS_IMETHOD InsertBasicBlock(const nsString& aBlockType);
   NS_IMETHOD Indent(const nsString& aIndent);
   NS_IMETHOD Align(const nsString& aAlign);
 
diff --git a/editor/libeditor/text/nsTextEditRules.h b/editor/libeditor/text/nsTextEditRules.h
index ccc7b5414ac4..58c77dd517e7 100644
--- a/editor/libeditor/text/nsTextEditRules.h
+++ b/editor/libeditor/text/nsTextEditRules.h
@@ -73,9 +73,7 @@ public:
     kIndent          = 3002,
     kOutdent         = 3003,
     kAlign           = 3004,
-    kMakeHeader      = 3005,
-    kMakeAddress     = 3006,
-    kMakePRE         = 3007,
+    kMakeBasicBlock  = 3005,
     kInsertElement   = 3008
   };
   
@@ -179,6 +177,7 @@ class nsTextRulesInfo : public nsRulesInfo
     collapsedAction(nsIEditor::eDeleteNext),
     bOrdered(PR_FALSE),
     alignType(0),
+    blockType(0),
     insertElement(0)
     {};
 
@@ -200,6 +199,9 @@ class nsTextRulesInfo : public nsRulesInfo
   // kAlign
   const nsString *alignType;
   
+  // kMakeBasicBlock
+  const nsString *blockType;
+  
   // kInsertElement
   const nsIDOMElement* insertElement;
 };