diff --git a/editor/base/nsEditorEventListeners.cpp b/editor/base/nsEditorEventListeners.cpp
index a8a714674bf53fe359902862ad17accbd11f3f8a..6b9e2fa4bf6fe227a21c5ee9da2107a32b9b4a7f 100644
--- a/editor/base/nsEditorEventListeners.cpp
+++ b/editor/base/nsEditorEventListeners.cpp
@@ -428,7 +428,6 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr
 
       // hard-coded ChangeTextAttributes test -- font color red
       case nsIDOMEvent::VK_1:
-        case nsIDOMEvent::VK_F:
         if (PR_TRUE==ctrlKey)
         {
           aProcessed=PR_TRUE;
@@ -454,7 +453,6 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr
 
       // hard-coded ChangeTextAttributes test -- remove font color
       case nsIDOMEvent::VK_2:
-        case nsIDOMEvent::VK_R:
         if (PR_TRUE==ctrlKey)
         {
           aProcessed=PR_TRUE;
@@ -553,9 +551,8 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr
         }
         break;
 
-      // hard-coded change structure test -- block H1
+      // hard-coded change structure test -- transform block H1
       case nsIDOMEvent::VK_7:
-        case nsIDOMEvent::VK_Q:
         if (PR_TRUE==ctrlKey)
         {
           aProcessed=PR_TRUE;
@@ -567,15 +564,14 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr
             {
               nsAutoString tag;
               nsIEditProperty::h1->ToString(tag);
-              htmlEditor->AddBlockParent(tag);
+              htmlEditor->ReplaceBlockParent(tag);
             }
           }
         }
         break;
 
-      // hard-coded change structure test -- block blockquote (indent)
+      // hard-coded change structure test -- transform block H2
       case nsIDOMEvent::VK_8:
-        case nsIDOMEvent::VK_W:
         if (PR_TRUE==ctrlKey)
         {
           aProcessed=PR_TRUE;
@@ -586,8 +582,8 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr
             if (htmlEditor) 
             {
               nsAutoString tag;
-              nsIEditProperty::blockquote->ToString(tag);
-              htmlEditor->AddBlockParent(tag);
+              nsIEditProperty::h2->ToString(tag);
+              htmlEditor->ReplaceBlockParent(tag);
             }
           }
         }
@@ -595,7 +591,6 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr
 
       // hard-coded change structure test -- normal
       case nsIDOMEvent::VK_9:
-        case nsIDOMEvent::VK_E:
         if (PR_TRUE==ctrlKey)
         {
           aProcessed=PR_TRUE;
@@ -610,9 +605,27 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr
         }
         break;
 
+      // hard-coded change structure test -- block blockquote (indent)
+      case nsIDOMEvent::VK_COMMA:
+        if (PR_TRUE==ctrlKey)
+        {
+          aProcessed=PR_TRUE;
+          if (mEditor)
+          {
+            nsCOMPtr<nsIHTMLEditor>htmlEditor;
+            htmlEditor = do_QueryInterface(mEditor);
+            if (htmlEditor) 
+            {
+              nsAutoString tag;
+              nsIEditProperty::blockquote->ToString(tag);
+              htmlEditor->AddBlockParent(tag);
+            }
+          }
+        }
+        break;
+
       // hard-coded change structure test -- un-BlockQuote
-      case nsIDOMEvent::VK_0:
-        case nsIDOMEvent::VK_G:
+      case nsIDOMEvent::VK_PERIOD:
         if (PR_TRUE==ctrlKey)
         {
           aProcessed=PR_TRUE;
diff --git a/editor/base/nsHTMLEditor.cpp b/editor/base/nsHTMLEditor.cpp
index b73d4c4af65589585321823be79ce29c0dad637d..bb0593b037b1ed2db0c649701b09057c42919195 100644
--- a/editor/base/nsHTMLEditor.cpp
+++ b/editor/base/nsHTMLEditor.cpp
@@ -35,6 +35,8 @@
 #include "nsEditorCID.h"
 #include "nsLayoutCID.h"
 #include "nsIDOMRange.h"
+#include "nsISupportsArray.h"
+#include "nsVoidArray.h"
 
 #include "nsIComponentManager.h"
 #include "nsIServiceManager.h"
@@ -360,10 +362,74 @@ nsHTMLEditor::CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode)
 // Note: Table Editing methods are implemented in EditTable.cpp
 //
 
+// get the paragraph style(s) for the selection
 NS_IMETHODIMP 
-nsHTMLEditor::AddBlockParent(nsString& aParentTag)
+nsHTMLEditor::GetParagraphStyle(nsStringArray *aTagList)
 {
+  if (gNoisy) { printf("---------- nsHTMLEditor::GetParagraphStyle ----------\n"); }
+  if (!aTagList) { return NS_ERROR_NULL_POINTER; }
 
+  nsresult result;
+  nsCOMPtr<nsIDOMSelection>selection;
+  result = nsEditor::GetSelection(getter_AddRefs(selection));
+  if ((NS_SUCCEEDED(result)) && selection)
+  {
+    nsCOMPtr<nsIEnumerator> enumerator;
+    enumerator = do_QueryInterface(selection);
+    if (enumerator)
+    {
+      enumerator->First(); 
+      nsISupports *currentItem;
+      result = enumerator->CurrentItem(&currentItem);
+      if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem))
+      {
+        nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
+        // scan the range for all the independent block content subranges
+        // and get the block parent of each
+        nsISupportsArray *subRanges;
+        result = NS_NewISupportsArray(&subRanges);
+        if ((NS_SUCCEEDED(result)) && subRanges)
+        {
+          result = GetBlockRanges(range, subRanges);
+          if (NS_SUCCEEDED(result))
+          {
+            nsIDOMRange *subRange;
+            subRange = (nsIDOMRange *)(subRanges->ElementAt(0));
+            while (subRange && (NS_SUCCEEDED(result)))
+            {
+              nsCOMPtr<nsIDOMNode>startParent;
+              result = subRange->GetStartParent(getter_AddRefs(startParent));
+              if (NS_SUCCEEDED(result) && startParent) 
+              {
+                nsCOMPtr<nsIDOMElement> blockParent;
+                result = GetBlockParent(startParent, getter_AddRefs(blockParent));
+                if (NS_SUCCEEDED(result) && blockParent)
+                {
+                  nsAutoString blockParentTag;
+                  blockParent->GetTagName(blockParentTag);
+                  if (-1==aTagList->IndexOf(blockParentTag)) {
+                    aTagList->AppendString(blockParentTag);
+                  }
+                }
+              }
+              NS_RELEASE(subRange);
+              subRanges->RemoveElementAt(0);
+              subRange = (nsIDOMRange *)(subRanges->ElementAt(0));
+            }
+          }
+          NS_RELEASE(subRanges);
+        }
+      }
+    }
+  }
+
+  return result;
+}
+
+// use this when block parents are to be added regardless of current state
+NS_IMETHODIMP 
+nsHTMLEditor::AddBlockParent(nsString& aParentTag)
+{
   if (gNoisy) 
   { 
     char *tag = aParentTag.ToNewCString();
@@ -388,58 +454,9 @@ nsHTMLEditor::AddBlockParent(nsString& aParentTag)
       if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem))
       {
         nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
-        nsCOMPtr<nsIDOMNode>commonParent;
-        result = range->GetCommonParent(getter_AddRefs(commonParent));
-        if ((NS_SUCCEEDED(result)) && commonParent)
-        {
-          PRInt32 startOffset, endOffset;
-          range->GetStartOffset(&startOffset);
-          range->GetEndOffset(&endOffset);
-          nsCOMPtr<nsIDOMNode> startParent;  nsCOMPtr<nsIDOMNode> endParent;
-          range->GetStartParent(getter_AddRefs(startParent));
-          range->GetEndParent(getter_AddRefs(endParent));
-          if (startParent.get()==endParent.get()) 
-          { // the range is entirely contained within a single node
-            result = ReParentContentOfNode(startParent, aParentTag);
-          }
-          else
-          {
-            nsCOMPtr<nsIDOMNode> startGrandParent;
-            startParent->GetParentNode(getter_AddRefs(startGrandParent));
-            nsCOMPtr<nsIDOMNode> endGrandParent;
-            endParent->GetParentNode(getter_AddRefs(endGrandParent));
-            if (NS_SUCCEEDED(result))
-            {
-              PRBool canCollapse = PR_FALSE;
-              if (endGrandParent.get()==startGrandParent.get())
-              {
-                result = IntermediateNodesAreInline(range, startParent, startOffset, 
-                                                    endParent, endOffset, 
-                                                    canCollapse);
-              }
-              if (NS_SUCCEEDED(result)) 
-              {
-                if (PR_TRUE==canCollapse)
-                { // the range is between 2 nodes that have a common (immediate) grandparent,
-                  // and any intermediate nodes are just inline style nodes
-                  result = ReParentContentOfNode(startParent, aParentTag);
-                }
-                else
-                { // the range is between 2 nodes that have no simple relationship
-                  result = ReParentContentOfRange(range, aParentTag);
-                }
-              }
-            }
-          }
-          if (NS_SUCCEEDED(result))
-          { // compute a range for the selection
-            // don't want to actually do anything with selection, because
-            // we are still iterating through it.  Just want to create and remember
-            // an nsIDOMRange, and later add the range to the selection after clearing it.
-            // XXX: I'm blocked here because nsIDOMSelection doesn't provide a mechanism
-            //      for setting a compound selection yet.
-          }
-        }
+        // scan the range for all the independent block content subranges
+        // and apply the transformation to them
+        result = ReParentContentOfRange(range, aParentTag, eInsertParent);
       }
     }
     nsEditor::EndTransaction();
@@ -453,8 +470,74 @@ nsHTMLEditor::AddBlockParent(nsString& aParentTag)
   return result;
 }
 
+// use this when a paragraph type is being transformed from one type to another
 NS_IMETHODIMP 
-nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, nsString &aParentTag)
+nsHTMLEditor::ReplaceBlockParent(nsString& aParentTag)
+{
+
+  if (gNoisy) 
+  { 
+    char *tag = aParentTag.ToNewCString();
+    printf("---------- nsHTMLEditor::ReplaceBlockParent %s ----------\n", tag); 
+    delete [] tag;
+  }
+  
+  nsresult result=NS_ERROR_NOT_INITIALIZED;
+  nsCOMPtr<nsIDOMSelection>selection;
+  result = nsEditor::GetSelection(getter_AddRefs(selection));
+  if ((NS_SUCCEEDED(result)) && selection)
+  {
+    // set the block parent for all selected ranges
+    nsEditor::BeginTransaction();
+    nsCOMPtr<nsIEnumerator> enumerator;
+    enumerator = do_QueryInterface(selection);
+    if (enumerator)
+    {
+      enumerator->First(); 
+      nsISupports *currentItem;
+      result = enumerator->CurrentItem(&currentItem);
+      if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem))
+      {
+        nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
+        // scan the range for all the independent block content subranges
+        // and apply the transformation to them
+        result = ReParentContentOfRange(range, aParentTag, eReplaceParent);
+      }
+    }
+    nsEditor::EndTransaction();
+    if (NS_SUCCEEDED(result))
+    { // set the selection
+      // XXX: can't do anything until I can create ranges
+    }
+  }
+  if (gNoisy) {printf("Finished nsHTMLEditor::AddBlockParent with this content:\n"); DebugDumpContent(); } // DEBUG
+
+  return result;
+}
+
+void IsRootTag(nsString &aTag, PRBool &aIsTag)
+{
+  static nsAutoString bodyTag = "body";
+  static nsAutoString tdTag = "td";
+  static nsAutoString thTag = "th";
+  static nsAutoString captionTag = "caption";
+  if (PR_TRUE==aTag.EqualsIgnoreCase(bodyTag) ||
+      PR_TRUE==aTag.EqualsIgnoreCase(tdTag) ||
+      PR_TRUE==aTag.EqualsIgnoreCase(thTag) ||
+      PR_TRUE==aTag.EqualsIgnoreCase(captionTag) )
+  {
+    aIsTag = PR_TRUE;
+  }
+  else {
+    aIsTag = PR_FALSE;
+  }
+}
+
+
+NS_IMETHODIMP 
+nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, 
+                                    nsString &aParentTag,
+                                    BlockTransformationType aTransformation)
 {
   if (!aNode) { return NS_ERROR_NULL_POINTER; }
   // find the current block parent, or just use aNode if it is a block node
@@ -473,7 +556,7 @@ nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, nsString &aParentTag)
       nodeIsBlock=PR_TRUE;
     }
   }
-  // if aNode is the block parent, then we're operating on one of its children
+  // if aNode is the block parent, then the node to reparent is one of its children
   if (PR_TRUE==nodeIsBlock) 
   {
     result = aNode->QueryInterface(nsIDOMNode::GetIID(), getter_AddRefs(blockParentElement));
@@ -481,7 +564,7 @@ nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, nsString &aParentTag)
       result = aNode->GetFirstChild(getter_AddRefs(nodeToReParent));
     }
   }
-  else {
+  else { // we just need to get the block parent of aNode
     result = nsTextEditor::GetBlockParent(aNode, getter_AddRefs(blockParentElement));
   }
   // at this point, we must have a good result, a node to reparent, and a block parent
@@ -494,38 +577,44 @@ nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, nsString &aParentTag)
     // we need to treat nodes directly inside the body differently
     nsAutoString parentTag;
     blockParentElement->GetTagName(parentTag);
-    nsAutoString bodyTag = "body";
-    if (PR_TRUE==parentTag.EqualsIgnoreCase(bodyTag))
+    PRBool isRoot;
+    IsRootTag(parentTag, isRoot);
+    if (PR_TRUE==isRoot)    
     {
-      // if nodeToReParent is a text node, we have <BODY><TEXT>text.
-      // re-parent <TEXT> into a new <aTag> at the offset of <TEXT> in <BODY>
+      // if nodeToReParent is a text node, we have <ROOT>Text.
+      // re-parent Text into a new <aTag> at the offset of Text in <ROOT>
+      // so we end up with <ROOT><aTag>Text
+      // ignore aTransformation, replaces act like inserts
       nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(nodeToReParent);
       if (nodeAsText)
       {
         result = ReParentBlockContent(nodeToReParent, aParentTag, blockParentNode, parentTag, 
-                                      getter_AddRefs(newParentNode));
+                                      aTransformation, getter_AddRefs(newParentNode));
       }
       else
       { // this is the case of an insertion point between 2 non-text objects
+        // XXX: how to you know it's an insertion point???
         PRInt32 offsetInParent=0;
         result = nsIEditorSupport::GetChildOffset(nodeToReParent, blockParentNode, offsetInParent);
         NS_ASSERTION((NS_SUCCEEDED(result)), "bad result from GetChildOffset");
         // otherwise, just create the block parent at the selection
         result = nsTextEditor::CreateNode(aParentTag, blockParentNode, offsetInParent, 
                                           getter_AddRefs(newParentNode));
-        // XXX: need to create a bogus text node inside this new block!
+        // XXX: need to move some of the children of blockParentNode into the newParentNode?
+        // XXX: need to create a bogus text node inside this new block?
         //      that means, I need to generalize bogus node handling
       }
     }
     else
-    { 
-      // for the selected block content, replace blockParentNode with a new node of the correct type
-      if (PR_FALSE==parentTag.EqualsIgnoreCase(aParentTag))
+    { // the block parent is not a ROOT, 
+      // for the selected block content, transform blockParentNode 
+      if (((eReplaceParent==aTransformation) && (PR_FALSE==parentTag.EqualsIgnoreCase(aParentTag))) ||
+           (eInsertParent==aTransformation))
       {
-		if (gNoisy) { DebugDumpContent(); } // DEBUG
+		    if (gNoisy) { DebugDumpContent(); } // DEBUG
         result = ReParentBlockContent(nodeToReParent, aParentTag, blockParentNode, parentTag, 
-                                      getter_AddRefs(newParentNode));
-        if (NS_SUCCEEDED(result) && newParentNode)
+                                      aTransformation, getter_AddRefs(newParentNode));
+        if ((NS_SUCCEEDED(result)) && (newParentNode) && (eReplaceParent==aTransformation))
         {
           PRBool hasChildren;
           blockParentNode->HasChildNodes(&hasChildren);
@@ -554,6 +643,7 @@ nsHTMLEditor::ReParentBlockContent(nsIDOMNode  *aNode,
                                    nsString    &aParentTag,
                                    nsIDOMNode  *aBlockParentNode,
                                    nsString    &aBlockParentTag,
+                                   BlockTransformationType aTranformation,
                                    nsIDOMNode **aNewParentNode)
 {
   if (!aNode || !aBlockParentNode || !aNewParentNode) { return NS_ERROR_NULL_POINTER; }
@@ -574,16 +664,13 @@ nsHTMLEditor::ReParentBlockContent(nsIDOMNode  *aNode,
   }
   // now, previousAncestor is the node we are operating on
   nsCOMPtr<nsIDOMNode>leftNode, rightNode;
-  result = GetBlockDelimitedContent(aBlockParentNode, 
-                                    previousAncestor, 
+  result = GetBlockDelimitedContent(previousAncestor, 
                                     getter_AddRefs(leftNode), 
                                     getter_AddRefs(rightNode));
   if ((NS_SUCCEEDED(result)) && leftNode && rightNode)
   {
     PRInt32 offsetInParent=0;
-    PRBool canContain;
-    CanContainBlock(aParentTag, aBlockParentTag, canContain);
-    if (PR_TRUE==canContain)
+    if (eInsertParent==aTranformation)
     {
       result = nsIEditorSupport::GetChildOffset(leftNode, aBlockParentNode, offsetInParent);
       NS_ASSERTION((NS_SUCCEEDED(result)), "bad result from GetChildOffset");
@@ -644,10 +731,36 @@ nsHTMLEditor::ReParentBlockContent(nsIDOMNode  *aNode,
 }
 
 NS_IMETHODIMP 
-nsHTMLEditor::ReParentContentOfRange(nsIDOMRange *aRange, nsString &aParentTag)
+nsHTMLEditor::ReParentContentOfRange(nsIDOMRange *aRange, 
+                                     nsString &aParentTag,
+                                     BlockTransformationType aTranformation)
 {
-  nsresult result=NS_ERROR_NOT_IMPLEMENTED;
-  printf("transformations over complex selections not implemented.\n");
+  if (!aRange) { return NS_ERROR_NULL_POINTER; }
+  nsresult result;
+  nsISupportsArray *subRanges;
+  result = NS_NewISupportsArray(&subRanges);
+  if ((NS_SUCCEEDED(result)) && subRanges)
+  {
+    result = GetBlockRanges(aRange, subRanges);
+    if (NS_SUCCEEDED(result)) 
+    {
+      nsIDOMRange *subRange;
+      subRange = (nsIDOMRange *)(subRanges->ElementAt(0));
+      while (subRange && (NS_SUCCEEDED(result)))
+      {
+        nsCOMPtr<nsIDOMNode>startParent;
+        result = subRange->GetStartParent(getter_AddRefs(startParent));
+        if (NS_SUCCEEDED(result) && startParent) 
+        {
+          result = ReParentContentOfNode(startParent, aParentTag, aTranformation);
+        }
+        NS_RELEASE(subRange);
+        subRanges->RemoveElementAt(0);
+        subRange = (nsIDOMRange *)(subRanges->ElementAt(0));
+      }
+    }
+    NS_RELEASE(subRanges);
+  }
   return result;
 }
 
diff --git a/editor/base/nsHTMLEditor.h b/editor/base/nsHTMLEditor.h
index 70fccbf0231a4480ca90fdc3b26b6b10fbd1d5d4..a1915de5a32e36c67ab1faf3d234ce891f442620 100644
--- a/editor/base/nsHTMLEditor.h
+++ b/editor/base/nsHTMLEditor.h
@@ -32,6 +32,9 @@
  */
 class nsHTMLEditor  : public nsTextEditor, public nsIHTMLEditor
 {
+
+  typedef enum {eNoOp=0, eReplaceParent=1, eInsertParent=2} BlockTransformationType;
+
 public:
   // see nsIHTMLEditor for documentation
 
@@ -96,7 +99,9 @@ public:
 // End of methods implemented in nsEditor
 //=============================================================
 // HTML Editing methods
+  NS_IMETHOD GetParagraphStyle(nsStringArray *aTagList);
   NS_IMETHOD AddBlockParent(nsString& aParentTag);
+  NS_IMETHOD ReplaceBlockParent(nsString& aParentTag);
   NS_IMETHOD RemoveBlockParent();
   NS_IMETHOD RemoveParent(const nsString &aParentTag);
 
@@ -138,15 +143,20 @@ protected:
 
   virtual void  InitRules();
 
-  NS_IMETHOD ReParentContentOfNode(nsIDOMNode *aNode, nsString &aParentTag);
+  NS_IMETHOD ReParentContentOfNode(nsIDOMNode *aNode, 
+                                   nsString   &aParentTag,
+                                   BlockTransformationType aTranformation);
 
   NS_IMETHOD ReParentBlockContent(nsIDOMNode  *aNode, 
                                   nsString    &aParentTag,
                                   nsIDOMNode  *aBlockParentNode,
                                   nsString    &aBlockParentTag,
+                                  BlockTransformationType aTranformation,
                                   nsIDOMNode **aNewParentNode);
   
-  NS_IMETHOD ReParentContentOfRange(nsIDOMRange *aRange, nsString &aParentTag);
+  NS_IMETHOD ReParentContentOfRange(nsIDOMRange *aRange, 
+                                    nsString    &aParentTag,
+                                    BlockTransformationType aTranformation);
 
   NS_IMETHOD CanContainBlock(nsString &aBlockChild, nsString &aBlockParent, PRBool &aCanContain);
 
diff --git a/editor/base/nsTextEditor.cpp b/editor/base/nsTextEditor.cpp
index b3b07f87d1ec06fc3a80d429a373cd71e525c52b..91a8cc798e58d2691d079449d45d68a6bdd6ba83 100644
--- a/editor/base/nsTextEditor.cpp
+++ b/editor/base/nsTextEditor.cpp
@@ -187,6 +187,7 @@ nsTextEditor::~nsTextEditor()
   if (mRules) {
     delete mRules;
   }
+  NS_IF_RELEASE(mTypeInState);
   nsEditProperty::InstanceShutdown();
 }
 
@@ -228,12 +229,18 @@ NS_IMETHODIMP nsTextEditor::Init(nsIDOMDocument *aDoc, nsIPresShell *aPresShell)
     result = nsEditor::Init(aDoc, aPresShell);
     if (NS_OK != result) { return result; }
 
+    // init the type-in state
+    mTypeInState = new TypeInState();
+    if (!mTypeInState) {return NS_ERROR_NULL_POINTER;}
+    NS_ADDREF(mTypeInState);
+
     nsCOMPtr<nsIDOMSelection>selection;
     result = nsEditor::GetSelection(getter_AddRefs(selection));
     if (NS_OK != result) { return result; }
-    if (selection) {
+    if (selection) 
+    {
       nsCOMPtr<nsIDOMSelectionListener>listener;
-      listener = do_QueryInterface(&mTypeInState);
+      listener = do_QueryInterface(mTypeInState);
       if (listener) {
         selection->AddSelectionListener(listener); 
       }
@@ -331,7 +338,7 @@ NS_IMETHODIMP nsTextEditor::SetTextProperty(nsIAtom        *aProperty,
     if (PR_TRUE==isCollapsed)
     {
       // manipulating text attributes on a collapsed selection only sets state for the next text insertion
-      SetTypeInStateForProperty(mTypeInState, aProperty, aAttribute, aValue);
+      SetTypeInStateForProperty(*mTypeInState, aProperty, aAttribute, aValue);
     }
     else
     {
@@ -646,7 +653,7 @@ NS_IMETHODIMP nsTextEditor::RemoveTextProperty(nsIAtom *aProperty, const nsStrin
     if (PR_TRUE==isCollapsed)
     {
       // manipulating text attributes on a collapsed selection only sets state for the next text insertion
-      SetTypeInStateForProperty(mTypeInState, aProperty, aAttribute, nsnull);
+      SetTypeInStateForProperty(*mTypeInState, aProperty, aAttribute, nsnull);
     }
     else
     {
@@ -787,7 +794,7 @@ NS_IMETHODIMP nsTextEditor::InsertText(const nsString& aStringToInsert)
   ruleInfo.placeTxn = &placeholderTxn;
   ruleInfo.inString = &aStringToInsert;
   ruleInfo.outString = &resultString;
-  ruleInfo.typeInState = mTypeInState;
+  ruleInfo.typeInState = *mTypeInState;
 
   nsresult result = mRules->WillDoAction(selection, &ruleInfo, &cancel);
   if ((PR_FALSE==cancel) && (NS_SUCCEEDED(result)))
@@ -1379,13 +1386,12 @@ nsTextEditor::GetBlockParent(nsIDOMNode *aNode, nsIDOMElement **aBlockParent) co
 }
 
 NS_IMETHODIMP
-nsTextEditor::GetBlockDelimitedContent(nsIDOMNode *aParent, 
-                                       nsIDOMNode *aChild,
+nsTextEditor::GetBlockDelimitedContent(nsIDOMNode *aChild,
                                        nsIDOMNode **aLeftNode, 
                                        nsIDOMNode **aRightNode) const
 {
   nsresult result = NS_OK;
-  if (!aParent || !aChild || !aLeftNode || !aRightNode) {return NS_ERROR_NULL_POINTER;}
+  if (!aChild || !aLeftNode || !aRightNode) {return NS_ERROR_NULL_POINTER;}
   *aLeftNode = aChild;
   *aRightNode = aChild;
 
@@ -1429,6 +1435,94 @@ nsTextEditor::GetBlockDelimitedContent(nsIDOMNode *aParent,
   return result;
 }
 
+NS_IMETHODIMP
+nsTextEditor::GetBlockRanges(nsIDOMRange *aRange, nsISupportsArray *aSubRanges) const
+{
+  if (!aRange || !aSubRanges) {return NS_ERROR_NULL_POINTER;}
+
+  nsresult result;
+  nsCOMPtr<nsIContentIterator>iter;
+  result = nsComponentManager::CreateInstance(kCContentIteratorCID, nsnull,
+                                              kIContentIteratorIID, getter_AddRefs(iter));
+  if ((NS_SUCCEEDED(result)) && iter)
+  {
+    nsCOMPtr<nsIDOMRange> lastRange;
+    iter->Init(aRange);
+    nsCOMPtr<nsIContent> currentContent;
+    iter->CurrentNode(getter_AddRefs(currentContent));
+    while (NS_COMFALSE == iter->IsDone())
+    {
+      nsCOMPtr<nsIDOMNode>currentNode = do_QueryInterface(currentContent);
+      if (currentNode)
+      {
+        PRBool isInlineOrText;
+        result = IsNodeInline(currentNode, isInlineOrText);
+        if (PR_FALSE==isInlineOrText)
+        {
+          PRUint16 nodeType;
+          currentNode->GetNodeType(&nodeType);
+          if (nsIDOMNode::TEXT_NODE == nodeType) {
+            isInlineOrText = PR_TRUE;
+          }
+        }
+        if (PR_TRUE==isInlineOrText)
+        {
+          nsCOMPtr<nsIDOMNode>leftNode;
+          nsCOMPtr<nsIDOMNode>rightNode;
+          result = GetBlockDelimitedContent(currentNode,
+                                            getter_AddRefs(leftNode),
+                                            getter_AddRefs(rightNode));
+          if (gNoisy) {printf("currentNode %p has block content (%p,%p)\n", currentNode.get(), leftNode.get(), rightNode.get());}
+          if ((NS_SUCCEEDED(result)) && leftNode && rightNode)
+          {
+            // add range to the list if it doesn't overlap with the previous range
+            PRBool addRange=PR_TRUE;
+            if (lastRange)
+            {
+              nsCOMPtr<nsIDOMNode> lastStartNode;
+              nsCOMPtr<nsIDOMElement> blockParentOfLastStartNode;
+              lastRange->GetStartParent(getter_AddRefs(lastStartNode));
+              result = GetBlockParent(lastStartNode, getter_AddRefs(blockParentOfLastStartNode));
+              if ((NS_SUCCEEDED(result)) && blockParentOfLastStartNode)
+              {
+                if (gNoisy) {printf("lastStartNode %p has block parent %p\n", lastStartNode.get(), blockParentOfLastStartNode.get());}
+                nsCOMPtr<nsIDOMElement> blockParentOfLeftNode;
+                result = GetBlockParent(leftNode, getter_AddRefs(blockParentOfLeftNode));
+                if ((NS_SUCCEEDED(result)) && blockParentOfLeftNode)
+                {
+                  if (gNoisy) {printf("leftNode %p has block parent %p\n", leftNode.get(), blockParentOfLeftNode.get());}
+                  if (blockParentOfLastStartNode==blockParentOfLeftNode) {
+                    addRange = PR_FALSE;
+                  }
+                }
+              }
+            }
+            if (PR_TRUE==addRange) 
+            {
+              if (gNoisy) {printf("adding range, setting lastRange with start node %p\n", leftNode.get());}
+              nsCOMPtr<nsIDOMRange> range;
+              result = nsComponentManager::CreateInstance(kCRangeCID, nsnull, 
+                                                          kIDOMRangeIID, getter_AddRefs(range));
+              if ((NS_SUCCEEDED(result)) && range)
+              { // initialize the range
+                range->SetStart(leftNode, 0);
+                range->SetEnd(rightNode, 0);
+                aSubRanges->AppendElement(range);
+                lastRange = do_QueryInterface(range);
+              }
+            }        
+          }
+        }
+      }
+      /* do not check result here, and especially do not return the result code.
+       * we rely on iter->IsDone to tell us when the iteration is complete
+       */
+      iter->Next();
+      iter->CurrentNode(getter_AddRefs(currentContent));
+    }
+  }
+  return result;
+}
 
 NS_IMETHODIMP
 nsTextEditor::IntermediateNodesAreInline(nsIDOMRange *aRange,
@@ -2236,6 +2330,14 @@ nsTextEditor::RemoveTextPropertiesForNodeWithDifferentParents(nsIDOMNode  *aStar
   return result;
 }
 
+TypeInState * nsTextEditor::GetTypeInState()
+{
+  if (mTypeInState) {
+    NS_ADDREF(mTypeInState);
+  }
+  return mTypeInState;
+}
+
 NS_IMETHODIMP
 nsTextEditor::SetTypeInStateForProperty(TypeInState    &aTypeInState, 
                                         nsIAtom        *aPropName, 
diff --git a/editor/base/nsTextEditor.h b/editor/base/nsTextEditor.h
index 4529148c5d8011e1d3115d95576bc488dd7aa50e..f4341227d6c5f786e13e63bf668a67bb65fa4bd9 100644
--- a/editor/base/nsTextEditor.h
+++ b/editor/base/nsTextEditor.h
@@ -151,8 +151,11 @@ protected:
     */
   NS_IMETHOD GetBlockParent(nsIDOMNode *aNode, nsIDOMElement **aBlockParent) const;
 
-  NS_IMETHOD GetBlockDelimitedContent(nsIDOMNode *aParent, nsIDOMNode *aChild,
-                                      nsIDOMNode **aLeftNode, nsIDOMNode **aRightNode) const;
+  NS_IMETHOD GetBlockDelimitedContent(nsIDOMNode *aChild,
+                                      nsIDOMNode **aLeftNode, 
+                                      nsIDOMNode **aRightNode) const;
+
+  NS_IMETHOD GetBlockRanges(nsIDOMRange *aRange, nsISupportsArray *aSubRanges) const;
 
   /** returns PR_TRUE in out-param aResult if all nodes between (aStartNode, aStartOffset)
     * and (aEndNode, aEndOffset) are inline as defined by HTML DTD. 
@@ -265,13 +268,13 @@ protected:
                                        const nsString *aAttribute,
                                        const nsString *aValue);
   
-  TypeInState GetTypeInState() { return mTypeInState;}
+  TypeInState *GetTypeInState(); 
 
 
 // Data members
 protected:
-  TypeInState      mTypeInState;  // xxx - isn't it wrong to have xpcom classes as members?  shouldn't it be a pointer?
-  nsTextEditRules* mRules;
+  TypeInState     *mTypeInState;
+  nsTextEditRules *mRules;
   nsCOMPtr<nsIDOMEventListener> mKeyListenerP;
   nsCOMPtr<nsIDOMEventListener> mMouseListenerP;
   nsCOMPtr<nsIDOMEventListener> mTextListenerP;
diff --git a/editor/libeditor/html/nsHTMLEditor.cpp b/editor/libeditor/html/nsHTMLEditor.cpp
index b73d4c4af65589585321823be79ce29c0dad637d..bb0593b037b1ed2db0c649701b09057c42919195 100644
--- a/editor/libeditor/html/nsHTMLEditor.cpp
+++ b/editor/libeditor/html/nsHTMLEditor.cpp
@@ -35,6 +35,8 @@
 #include "nsEditorCID.h"
 #include "nsLayoutCID.h"
 #include "nsIDOMRange.h"
+#include "nsISupportsArray.h"
+#include "nsVoidArray.h"
 
 #include "nsIComponentManager.h"
 #include "nsIServiceManager.h"
@@ -360,10 +362,74 @@ nsHTMLEditor::CopyAttributes(nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode)
 // Note: Table Editing methods are implemented in EditTable.cpp
 //
 
+// get the paragraph style(s) for the selection
 NS_IMETHODIMP 
-nsHTMLEditor::AddBlockParent(nsString& aParentTag)
+nsHTMLEditor::GetParagraphStyle(nsStringArray *aTagList)
 {
+  if (gNoisy) { printf("---------- nsHTMLEditor::GetParagraphStyle ----------\n"); }
+  if (!aTagList) { return NS_ERROR_NULL_POINTER; }
 
+  nsresult result;
+  nsCOMPtr<nsIDOMSelection>selection;
+  result = nsEditor::GetSelection(getter_AddRefs(selection));
+  if ((NS_SUCCEEDED(result)) && selection)
+  {
+    nsCOMPtr<nsIEnumerator> enumerator;
+    enumerator = do_QueryInterface(selection);
+    if (enumerator)
+    {
+      enumerator->First(); 
+      nsISupports *currentItem;
+      result = enumerator->CurrentItem(&currentItem);
+      if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem))
+      {
+        nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
+        // scan the range for all the independent block content subranges
+        // and get the block parent of each
+        nsISupportsArray *subRanges;
+        result = NS_NewISupportsArray(&subRanges);
+        if ((NS_SUCCEEDED(result)) && subRanges)
+        {
+          result = GetBlockRanges(range, subRanges);
+          if (NS_SUCCEEDED(result))
+          {
+            nsIDOMRange *subRange;
+            subRange = (nsIDOMRange *)(subRanges->ElementAt(0));
+            while (subRange && (NS_SUCCEEDED(result)))
+            {
+              nsCOMPtr<nsIDOMNode>startParent;
+              result = subRange->GetStartParent(getter_AddRefs(startParent));
+              if (NS_SUCCEEDED(result) && startParent) 
+              {
+                nsCOMPtr<nsIDOMElement> blockParent;
+                result = GetBlockParent(startParent, getter_AddRefs(blockParent));
+                if (NS_SUCCEEDED(result) && blockParent)
+                {
+                  nsAutoString blockParentTag;
+                  blockParent->GetTagName(blockParentTag);
+                  if (-1==aTagList->IndexOf(blockParentTag)) {
+                    aTagList->AppendString(blockParentTag);
+                  }
+                }
+              }
+              NS_RELEASE(subRange);
+              subRanges->RemoveElementAt(0);
+              subRange = (nsIDOMRange *)(subRanges->ElementAt(0));
+            }
+          }
+          NS_RELEASE(subRanges);
+        }
+      }
+    }
+  }
+
+  return result;
+}
+
+// use this when block parents are to be added regardless of current state
+NS_IMETHODIMP 
+nsHTMLEditor::AddBlockParent(nsString& aParentTag)
+{
   if (gNoisy) 
   { 
     char *tag = aParentTag.ToNewCString();
@@ -388,58 +454,9 @@ nsHTMLEditor::AddBlockParent(nsString& aParentTag)
       if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem))
       {
         nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
-        nsCOMPtr<nsIDOMNode>commonParent;
-        result = range->GetCommonParent(getter_AddRefs(commonParent));
-        if ((NS_SUCCEEDED(result)) && commonParent)
-        {
-          PRInt32 startOffset, endOffset;
-          range->GetStartOffset(&startOffset);
-          range->GetEndOffset(&endOffset);
-          nsCOMPtr<nsIDOMNode> startParent;  nsCOMPtr<nsIDOMNode> endParent;
-          range->GetStartParent(getter_AddRefs(startParent));
-          range->GetEndParent(getter_AddRefs(endParent));
-          if (startParent.get()==endParent.get()) 
-          { // the range is entirely contained within a single node
-            result = ReParentContentOfNode(startParent, aParentTag);
-          }
-          else
-          {
-            nsCOMPtr<nsIDOMNode> startGrandParent;
-            startParent->GetParentNode(getter_AddRefs(startGrandParent));
-            nsCOMPtr<nsIDOMNode> endGrandParent;
-            endParent->GetParentNode(getter_AddRefs(endGrandParent));
-            if (NS_SUCCEEDED(result))
-            {
-              PRBool canCollapse = PR_FALSE;
-              if (endGrandParent.get()==startGrandParent.get())
-              {
-                result = IntermediateNodesAreInline(range, startParent, startOffset, 
-                                                    endParent, endOffset, 
-                                                    canCollapse);
-              }
-              if (NS_SUCCEEDED(result)) 
-              {
-                if (PR_TRUE==canCollapse)
-                { // the range is between 2 nodes that have a common (immediate) grandparent,
-                  // and any intermediate nodes are just inline style nodes
-                  result = ReParentContentOfNode(startParent, aParentTag);
-                }
-                else
-                { // the range is between 2 nodes that have no simple relationship
-                  result = ReParentContentOfRange(range, aParentTag);
-                }
-              }
-            }
-          }
-          if (NS_SUCCEEDED(result))
-          { // compute a range for the selection
-            // don't want to actually do anything with selection, because
-            // we are still iterating through it.  Just want to create and remember
-            // an nsIDOMRange, and later add the range to the selection after clearing it.
-            // XXX: I'm blocked here because nsIDOMSelection doesn't provide a mechanism
-            //      for setting a compound selection yet.
-          }
-        }
+        // scan the range for all the independent block content subranges
+        // and apply the transformation to them
+        result = ReParentContentOfRange(range, aParentTag, eInsertParent);
       }
     }
     nsEditor::EndTransaction();
@@ -453,8 +470,74 @@ nsHTMLEditor::AddBlockParent(nsString& aParentTag)
   return result;
 }
 
+// use this when a paragraph type is being transformed from one type to another
 NS_IMETHODIMP 
-nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, nsString &aParentTag)
+nsHTMLEditor::ReplaceBlockParent(nsString& aParentTag)
+{
+
+  if (gNoisy) 
+  { 
+    char *tag = aParentTag.ToNewCString();
+    printf("---------- nsHTMLEditor::ReplaceBlockParent %s ----------\n", tag); 
+    delete [] tag;
+  }
+  
+  nsresult result=NS_ERROR_NOT_INITIALIZED;
+  nsCOMPtr<nsIDOMSelection>selection;
+  result = nsEditor::GetSelection(getter_AddRefs(selection));
+  if ((NS_SUCCEEDED(result)) && selection)
+  {
+    // set the block parent for all selected ranges
+    nsEditor::BeginTransaction();
+    nsCOMPtr<nsIEnumerator> enumerator;
+    enumerator = do_QueryInterface(selection);
+    if (enumerator)
+    {
+      enumerator->First(); 
+      nsISupports *currentItem;
+      result = enumerator->CurrentItem(&currentItem);
+      if ((NS_SUCCEEDED(result)) && (nsnull!=currentItem))
+      {
+        nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
+        // scan the range for all the independent block content subranges
+        // and apply the transformation to them
+        result = ReParentContentOfRange(range, aParentTag, eReplaceParent);
+      }
+    }
+    nsEditor::EndTransaction();
+    if (NS_SUCCEEDED(result))
+    { // set the selection
+      // XXX: can't do anything until I can create ranges
+    }
+  }
+  if (gNoisy) {printf("Finished nsHTMLEditor::AddBlockParent with this content:\n"); DebugDumpContent(); } // DEBUG
+
+  return result;
+}
+
+void IsRootTag(nsString &aTag, PRBool &aIsTag)
+{
+  static nsAutoString bodyTag = "body";
+  static nsAutoString tdTag = "td";
+  static nsAutoString thTag = "th";
+  static nsAutoString captionTag = "caption";
+  if (PR_TRUE==aTag.EqualsIgnoreCase(bodyTag) ||
+      PR_TRUE==aTag.EqualsIgnoreCase(tdTag) ||
+      PR_TRUE==aTag.EqualsIgnoreCase(thTag) ||
+      PR_TRUE==aTag.EqualsIgnoreCase(captionTag) )
+  {
+    aIsTag = PR_TRUE;
+  }
+  else {
+    aIsTag = PR_FALSE;
+  }
+}
+
+
+NS_IMETHODIMP 
+nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, 
+                                    nsString &aParentTag,
+                                    BlockTransformationType aTransformation)
 {
   if (!aNode) { return NS_ERROR_NULL_POINTER; }
   // find the current block parent, or just use aNode if it is a block node
@@ -473,7 +556,7 @@ nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, nsString &aParentTag)
       nodeIsBlock=PR_TRUE;
     }
   }
-  // if aNode is the block parent, then we're operating on one of its children
+  // if aNode is the block parent, then the node to reparent is one of its children
   if (PR_TRUE==nodeIsBlock) 
   {
     result = aNode->QueryInterface(nsIDOMNode::GetIID(), getter_AddRefs(blockParentElement));
@@ -481,7 +564,7 @@ nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, nsString &aParentTag)
       result = aNode->GetFirstChild(getter_AddRefs(nodeToReParent));
     }
   }
-  else {
+  else { // we just need to get the block parent of aNode
     result = nsTextEditor::GetBlockParent(aNode, getter_AddRefs(blockParentElement));
   }
   // at this point, we must have a good result, a node to reparent, and a block parent
@@ -494,38 +577,44 @@ nsHTMLEditor::ReParentContentOfNode(nsIDOMNode *aNode, nsString &aParentTag)
     // we need to treat nodes directly inside the body differently
     nsAutoString parentTag;
     blockParentElement->GetTagName(parentTag);
-    nsAutoString bodyTag = "body";
-    if (PR_TRUE==parentTag.EqualsIgnoreCase(bodyTag))
+    PRBool isRoot;
+    IsRootTag(parentTag, isRoot);
+    if (PR_TRUE==isRoot)    
     {
-      // if nodeToReParent is a text node, we have <BODY><TEXT>text.
-      // re-parent <TEXT> into a new <aTag> at the offset of <TEXT> in <BODY>
+      // if nodeToReParent is a text node, we have <ROOT>Text.
+      // re-parent Text into a new <aTag> at the offset of Text in <ROOT>
+      // so we end up with <ROOT><aTag>Text
+      // ignore aTransformation, replaces act like inserts
       nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(nodeToReParent);
       if (nodeAsText)
       {
         result = ReParentBlockContent(nodeToReParent, aParentTag, blockParentNode, parentTag, 
-                                      getter_AddRefs(newParentNode));
+                                      aTransformation, getter_AddRefs(newParentNode));
       }
       else
       { // this is the case of an insertion point between 2 non-text objects
+        // XXX: how to you know it's an insertion point???
         PRInt32 offsetInParent=0;
         result = nsIEditorSupport::GetChildOffset(nodeToReParent, blockParentNode, offsetInParent);
         NS_ASSERTION((NS_SUCCEEDED(result)), "bad result from GetChildOffset");
         // otherwise, just create the block parent at the selection
         result = nsTextEditor::CreateNode(aParentTag, blockParentNode, offsetInParent, 
                                           getter_AddRefs(newParentNode));
-        // XXX: need to create a bogus text node inside this new block!
+        // XXX: need to move some of the children of blockParentNode into the newParentNode?
+        // XXX: need to create a bogus text node inside this new block?
         //      that means, I need to generalize bogus node handling
       }
     }
     else
-    { 
-      // for the selected block content, replace blockParentNode with a new node of the correct type
-      if (PR_FALSE==parentTag.EqualsIgnoreCase(aParentTag))
+    { // the block parent is not a ROOT, 
+      // for the selected block content, transform blockParentNode 
+      if (((eReplaceParent==aTransformation) && (PR_FALSE==parentTag.EqualsIgnoreCase(aParentTag))) ||
+           (eInsertParent==aTransformation))
       {
-		if (gNoisy) { DebugDumpContent(); } // DEBUG
+		    if (gNoisy) { DebugDumpContent(); } // DEBUG
         result = ReParentBlockContent(nodeToReParent, aParentTag, blockParentNode, parentTag, 
-                                      getter_AddRefs(newParentNode));
-        if (NS_SUCCEEDED(result) && newParentNode)
+                                      aTransformation, getter_AddRefs(newParentNode));
+        if ((NS_SUCCEEDED(result)) && (newParentNode) && (eReplaceParent==aTransformation))
         {
           PRBool hasChildren;
           blockParentNode->HasChildNodes(&hasChildren);
@@ -554,6 +643,7 @@ nsHTMLEditor::ReParentBlockContent(nsIDOMNode  *aNode,
                                    nsString    &aParentTag,
                                    nsIDOMNode  *aBlockParentNode,
                                    nsString    &aBlockParentTag,
+                                   BlockTransformationType aTranformation,
                                    nsIDOMNode **aNewParentNode)
 {
   if (!aNode || !aBlockParentNode || !aNewParentNode) { return NS_ERROR_NULL_POINTER; }
@@ -574,16 +664,13 @@ nsHTMLEditor::ReParentBlockContent(nsIDOMNode  *aNode,
   }
   // now, previousAncestor is the node we are operating on
   nsCOMPtr<nsIDOMNode>leftNode, rightNode;
-  result = GetBlockDelimitedContent(aBlockParentNode, 
-                                    previousAncestor, 
+  result = GetBlockDelimitedContent(previousAncestor, 
                                     getter_AddRefs(leftNode), 
                                     getter_AddRefs(rightNode));
   if ((NS_SUCCEEDED(result)) && leftNode && rightNode)
   {
     PRInt32 offsetInParent=0;
-    PRBool canContain;
-    CanContainBlock(aParentTag, aBlockParentTag, canContain);
-    if (PR_TRUE==canContain)
+    if (eInsertParent==aTranformation)
     {
       result = nsIEditorSupport::GetChildOffset(leftNode, aBlockParentNode, offsetInParent);
       NS_ASSERTION((NS_SUCCEEDED(result)), "bad result from GetChildOffset");
@@ -644,10 +731,36 @@ nsHTMLEditor::ReParentBlockContent(nsIDOMNode  *aNode,
 }
 
 NS_IMETHODIMP 
-nsHTMLEditor::ReParentContentOfRange(nsIDOMRange *aRange, nsString &aParentTag)
+nsHTMLEditor::ReParentContentOfRange(nsIDOMRange *aRange, 
+                                     nsString &aParentTag,
+                                     BlockTransformationType aTranformation)
 {
-  nsresult result=NS_ERROR_NOT_IMPLEMENTED;
-  printf("transformations over complex selections not implemented.\n");
+  if (!aRange) { return NS_ERROR_NULL_POINTER; }
+  nsresult result;
+  nsISupportsArray *subRanges;
+  result = NS_NewISupportsArray(&subRanges);
+  if ((NS_SUCCEEDED(result)) && subRanges)
+  {
+    result = GetBlockRanges(aRange, subRanges);
+    if (NS_SUCCEEDED(result)) 
+    {
+      nsIDOMRange *subRange;
+      subRange = (nsIDOMRange *)(subRanges->ElementAt(0));
+      while (subRange && (NS_SUCCEEDED(result)))
+      {
+        nsCOMPtr<nsIDOMNode>startParent;
+        result = subRange->GetStartParent(getter_AddRefs(startParent));
+        if (NS_SUCCEEDED(result) && startParent) 
+        {
+          result = ReParentContentOfNode(startParent, aParentTag, aTranformation);
+        }
+        NS_RELEASE(subRange);
+        subRanges->RemoveElementAt(0);
+        subRange = (nsIDOMRange *)(subRanges->ElementAt(0));
+      }
+    }
+    NS_RELEASE(subRanges);
+  }
   return result;
 }
 
diff --git a/editor/libeditor/html/nsHTMLEditor.h b/editor/libeditor/html/nsHTMLEditor.h
index 70fccbf0231a4480ca90fdc3b26b6b10fbd1d5d4..a1915de5a32e36c67ab1faf3d234ce891f442620 100644
--- a/editor/libeditor/html/nsHTMLEditor.h
+++ b/editor/libeditor/html/nsHTMLEditor.h
@@ -32,6 +32,9 @@
  */
 class nsHTMLEditor  : public nsTextEditor, public nsIHTMLEditor
 {
+
+  typedef enum {eNoOp=0, eReplaceParent=1, eInsertParent=2} BlockTransformationType;
+
 public:
   // see nsIHTMLEditor for documentation
 
@@ -96,7 +99,9 @@ public:
 // End of methods implemented in nsEditor
 //=============================================================
 // HTML Editing methods
+  NS_IMETHOD GetParagraphStyle(nsStringArray *aTagList);
   NS_IMETHOD AddBlockParent(nsString& aParentTag);
+  NS_IMETHOD ReplaceBlockParent(nsString& aParentTag);
   NS_IMETHOD RemoveBlockParent();
   NS_IMETHOD RemoveParent(const nsString &aParentTag);
 
@@ -138,15 +143,20 @@ protected:
 
   virtual void  InitRules();
 
-  NS_IMETHOD ReParentContentOfNode(nsIDOMNode *aNode, nsString &aParentTag);
+  NS_IMETHOD ReParentContentOfNode(nsIDOMNode *aNode, 
+                                   nsString   &aParentTag,
+                                   BlockTransformationType aTranformation);
 
   NS_IMETHOD ReParentBlockContent(nsIDOMNode  *aNode, 
                                   nsString    &aParentTag,
                                   nsIDOMNode  *aBlockParentNode,
                                   nsString    &aBlockParentTag,
+                                  BlockTransformationType aTranformation,
                                   nsIDOMNode **aNewParentNode);
   
-  NS_IMETHOD ReParentContentOfRange(nsIDOMRange *aRange, nsString &aParentTag);
+  NS_IMETHOD ReParentContentOfRange(nsIDOMRange *aRange, 
+                                    nsString    &aParentTag,
+                                    BlockTransformationType aTranformation);
 
   NS_IMETHOD CanContainBlock(nsString &aBlockChild, nsString &aBlockParent, PRBool &aCanContain);
 
diff --git a/editor/libeditor/text/nsEditorEventListeners.cpp b/editor/libeditor/text/nsEditorEventListeners.cpp
index a8a714674bf53fe359902862ad17accbd11f3f8a..6b9e2fa4bf6fe227a21c5ee9da2107a32b9b4a7f 100644
--- a/editor/libeditor/text/nsEditorEventListeners.cpp
+++ b/editor/libeditor/text/nsEditorEventListeners.cpp
@@ -428,7 +428,6 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr
 
       // hard-coded ChangeTextAttributes test -- font color red
       case nsIDOMEvent::VK_1:
-        case nsIDOMEvent::VK_F:
         if (PR_TRUE==ctrlKey)
         {
           aProcessed=PR_TRUE;
@@ -454,7 +453,6 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr
 
       // hard-coded ChangeTextAttributes test -- remove font color
       case nsIDOMEvent::VK_2:
-        case nsIDOMEvent::VK_R:
         if (PR_TRUE==ctrlKey)
         {
           aProcessed=PR_TRUE;
@@ -553,9 +551,8 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr
         }
         break;
 
-      // hard-coded change structure test -- block H1
+      // hard-coded change structure test -- transform block H1
       case nsIDOMEvent::VK_7:
-        case nsIDOMEvent::VK_Q:
         if (PR_TRUE==ctrlKey)
         {
           aProcessed=PR_TRUE;
@@ -567,15 +564,14 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr
             {
               nsAutoString tag;
               nsIEditProperty::h1->ToString(tag);
-              htmlEditor->AddBlockParent(tag);
+              htmlEditor->ReplaceBlockParent(tag);
             }
           }
         }
         break;
 
-      // hard-coded change structure test -- block blockquote (indent)
+      // hard-coded change structure test -- transform block H2
       case nsIDOMEvent::VK_8:
-        case nsIDOMEvent::VK_W:
         if (PR_TRUE==ctrlKey)
         {
           aProcessed=PR_TRUE;
@@ -586,8 +582,8 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr
             if (htmlEditor) 
             {
               nsAutoString tag;
-              nsIEditProperty::blockquote->ToString(tag);
-              htmlEditor->AddBlockParent(tag);
+              nsIEditProperty::h2->ToString(tag);
+              htmlEditor->ReplaceBlockParent(tag);
             }
           }
         }
@@ -595,7 +591,6 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr
 
       // hard-coded change structure test -- normal
       case nsIDOMEvent::VK_9:
-        case nsIDOMEvent::VK_E:
         if (PR_TRUE==ctrlKey)
         {
           aProcessed=PR_TRUE;
@@ -610,9 +605,27 @@ nsTextEditorKeyListener::ProcessShortCutKeys(nsIDOMEvent* aKeyEvent, PRBool& aPr
         }
         break;
 
+      // hard-coded change structure test -- block blockquote (indent)
+      case nsIDOMEvent::VK_COMMA:
+        if (PR_TRUE==ctrlKey)
+        {
+          aProcessed=PR_TRUE;
+          if (mEditor)
+          {
+            nsCOMPtr<nsIHTMLEditor>htmlEditor;
+            htmlEditor = do_QueryInterface(mEditor);
+            if (htmlEditor) 
+            {
+              nsAutoString tag;
+              nsIEditProperty::blockquote->ToString(tag);
+              htmlEditor->AddBlockParent(tag);
+            }
+          }
+        }
+        break;
+
       // hard-coded change structure test -- un-BlockQuote
-      case nsIDOMEvent::VK_0:
-        case nsIDOMEvent::VK_G:
+      case nsIDOMEvent::VK_PERIOD:
         if (PR_TRUE==ctrlKey)
         {
           aProcessed=PR_TRUE;
diff --git a/editor/public/nsIHTMLEditor.h b/editor/public/nsIHTMLEditor.h
index 50b6e582f3279731b3da7e9af528f63fa9213d05..1c1b09a4385ba8c521568b3204591a97795bc8db 100644
--- a/editor/public/nsIHTMLEditor.h
+++ b/editor/public/nsIHTMLEditor.h
@@ -31,6 +31,7 @@
 
 class nsIEditorCallback;
 class nsISupportsArray;
+class nsStringArray;
 class nsIAtom;
 class nsIInputStream;
 class nsIOutputStream;
@@ -98,14 +99,28 @@ public:
    * trigger off of ContentChanged notifications.
    */
 
+  NS_IMETHOD GetParagraphStyle(nsStringArray *aTagList)=0;
+
   /** Add a block parent node around the selected content.
-    * If the selected content already has a block parent that is not a sub-document,
-    * that block parent is changed to  the type given by aParentTag.
-    * Otherwise, the new block parent is nested in the closest sub-document.
+    * Only legal nestings are allowed.
+    * An example of use is for indenting using blockquote nodes.
+    *
     * @param aParentTag  The tag from which the new parent is created.
     */
   NS_IMETHOD AddBlockParent(nsString& aParentTag)=0;
 
+  /** Replace the block parent node around the selected content with a new block
+    * parent node of type aParentTag.
+    * Only legal replacements are allowed.
+    * An example of use are is transforming H1 to LI ("paragraph type transformations").
+    * For containing block transformations (transforming UL to OL, for example),
+    * the caller should RemoveParent("UL"), set the selection appropriately,
+    * and call AddBlockParent("OL").
+    *
+    * @param aParentTag  The tag from which the new parent is created.
+    */
+  NS_IMETHOD ReplaceBlockParent(nsString& aParentTag)=0;
+
   /** remove block parent of type aTagToRemove from the selection.
     * if aTagToRemove is null, the nearest enclosing block that 
     * is <B>not</B> a sub-document is removed.