LayoutTests:
authorjusting <justing@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 14 Jun 2006 23:23:50 +0000 (23:23 +0000)
committerjusting <justing@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 14 Jun 2006 23:23:50 +0000 (23:23 +0000)
        Reviewed by justin

        <http://bugzilla.opendarwin.org/show_bug.cgi?id=7580>
        TinyMCE: Implement execCommand(formatBlock, ...)

        * editing/deleting/delete-ws-fixup-002-expected.checksum:
        * editing/deleting/delete-ws-fixup-002-expected.png:
        * editing/deleting/delete-ws-fixup-002-expected.txt:
        * editing/deleting/delete-ws-fixup-002.html:
        * editing/execCommand/format-block-expected.checksum: Added.
        * editing/execCommand/format-block-expected.png: Added.
        * editing/execCommand/format-block-expected.txt: Added.
        * editing/execCommand/format-block-from-range-selection-expected.checksum: Added.
        * editing/execCommand/format-block-from-range-selection-expected.png: Added.
        * editing/execCommand/format-block-from-range-selection-expected.txt: Added.
        * editing/execCommand/format-block-from-range-selection.html: Added.
        * editing/execCommand/format-block.html: Added.

WebCore:

        Reviewed by justin

        <http://bugzilla.opendarwin.org/show_bug.cgi?id=7580>
        TinyMCE: Implement execCommand(formatBlock, ...)

        * WebCore.xcodeproj/project.pbxproj: Added FormatBlock.{h,cpp} to the project.
        * WebCore.vcproj/WebCore/WebCore.vcproj: Ditto.
        * bridge/mac/WebCoreFrameBridge.h: Added WebUndoActions
        * editing/CompositeEditCommand.cpp:
        (WebCore::CompositeEditCommand::moveParagraph): Added a preserveStyle bool.
        (WebCore::CompositeEditCommand::moveParagraphs): Ditto.  downstream() the start
        or else we'll move collapsed whitespace and uncollapse it.
        * editing/CompositeEditCommand.h:
        * editing/DeleteSelectionCommand.cpp:
        (WebCore::DeleteSelectionCommand::initializePositionData):
        (WebCore::DeleteSelectionCommand::handleSpecialCaseBRDelete): Don't update m_endingPosition
        because that's removeNode's responsibility.
        (WebCore::updatePositionForNodeRemoval): Added.
        (WebCore::DeleteSelectionCommand::removeNode): Turned removeFullySelectedNode into a virtual
        overload of removeNode so that we can update positions as we remove nodes.
        (WebCore::updatePositionForTextRemoval): Added.
        (WebCore::DeleteSelectionCommand::deleteTextFromNode):
        (WebCore::DeleteSelectionCommand::handleGeneralDelete):
        (WebCore::DeleteSelectionCommand::fixupWhitespace): Got rid of m_trailingWhitespaceValid
        since m_trailingWhitespace is always valid (we update it as we remove nodes).
        (WebCore::DeleteSelectionCommand::mergeParagraphs):
        (WebCore::DeleteSelectionCommand::doApply): Leading and trailing spaces should
        be fixed if they have collapsed before merging paragraphs.
        * editing/DeleteSelectionCommand.h:
        * editing/EditAction.h:
        (WebCore::):
        * editing/FormatBlockCommand.cpp: Added.
        (WebCore::FormatBlockCommand::FormatBlockCommand):
        (WebCore::FormatBlockCommand::modifyRange): Similar to InsertListCommand::modifyRange().
        (WebCore::FormatBlockCommand::doApply):
        * editing/FormatBlockCommand.h: Added.
        (WebCore::FormatBlockCommand::editingAction):
        * editing/InsertListCommand.h:
        (WebCore::InsertListCommand::editingAction):
        * editing/JSEditor.cpp:
        * editing/MergeIdenticalElementsCommand.cpp:
        (WebCore::MergeIdenticalElementsCommand::doApply):
        * editing/htmlediting.cpp:
        (WebCore::validBlockTag):
        (WebCore::createElement):
        * editing/htmlediting.h:

WebKit:

        Reviewed by justin

        <http://bugzilla.opendarwin.org/show_bug.cgi?id=7580>
        TinyMCE: Implement execCommand(formatBlock, ...)

        * English.lproj/Localizable.strings:
        * WebCoreSupport/WebFrameBridge.m:
        (-[WebFrameBridge nameForUndoAction:]):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@14868 268f45cc-cd09-0410-ab3c-d52691b4dbfc

32 files changed:
LayoutTests/ChangeLog
LayoutTests/editing/deleting/delete-ws-fixup-002-expected.checksum
LayoutTests/editing/deleting/delete-ws-fixup-002-expected.png
LayoutTests/editing/deleting/delete-ws-fixup-002-expected.txt
LayoutTests/editing/deleting/delete-ws-fixup-002.html
LayoutTests/editing/execCommand/format-block-expected.checksum [new file with mode: 0644]
LayoutTests/editing/execCommand/format-block-expected.png [new file with mode: 0644]
LayoutTests/editing/execCommand/format-block-expected.txt [new file with mode: 0644]
LayoutTests/editing/execCommand/format-block-from-range-selection-expected.checksum [new file with mode: 0644]
LayoutTests/editing/execCommand/format-block-from-range-selection-expected.png [new file with mode: 0644]
LayoutTests/editing/execCommand/format-block-from-range-selection-expected.txt [new file with mode: 0644]
LayoutTests/editing/execCommand/format-block-from-range-selection.html [new file with mode: 0644]
LayoutTests/editing/execCommand/format-block.html [new file with mode: 0644]
WebCore/ChangeLog
WebCore/WebCore.vcproj/WebCore/WebCore.vcproj
WebCore/WebCore.xcodeproj/project.pbxproj
WebCore/bridge/mac/WebCoreFrameBridge.h
WebCore/editing/CompositeEditCommand.cpp
WebCore/editing/CompositeEditCommand.h
WebCore/editing/DeleteSelectionCommand.cpp
WebCore/editing/DeleteSelectionCommand.h
WebCore/editing/EditAction.h
WebCore/editing/FormatBlockCommand.cpp [new file with mode: 0644]
WebCore/editing/FormatBlockCommand.h [new file with mode: 0644]
WebCore/editing/InsertListCommand.h
WebCore/editing/JSEditor.cpp
WebCore/editing/MergeIdenticalElementsCommand.cpp
WebCore/editing/htmlediting.cpp
WebCore/editing/htmlediting.h
WebKit/ChangeLog
WebKit/English.lproj/Localizable.strings
WebKit/WebCoreSupport/WebFrameBridge.m

index c5b1cefcce87f6c682f5f17b7bf8abce23dace6a..2d26bcd774368768f9f95153c221dfff96ec0975 100644 (file)
@@ -1,3 +1,23 @@
+2006-06-14  Levi Weintraub  <lweintraub@apple.com>
+
+        Reviewed by justin
+
+        <http://bugzilla.opendarwin.org/show_bug.cgi?id=7580>
+        TinyMCE: Implement execCommand(formatBlock, ...)
+
+        * editing/deleting/delete-ws-fixup-002-expected.checksum:
+        * editing/deleting/delete-ws-fixup-002-expected.png:
+        * editing/deleting/delete-ws-fixup-002-expected.txt:
+        * editing/deleting/delete-ws-fixup-002.html:
+        * editing/execCommand/format-block-expected.checksum: Added.
+        * editing/execCommand/format-block-expected.png: Added.
+        * editing/execCommand/format-block-expected.txt: Added.
+        * editing/execCommand/format-block-from-range-selection-expected.checksum: Added.
+        * editing/execCommand/format-block-from-range-selection-expected.png: Added.
+        * editing/execCommand/format-block-from-range-selection-expected.txt: Added.
+        * editing/execCommand/format-block-from-range-selection.html: Added.
+        * editing/execCommand/format-block.html: Added.
+
 2006-06-14  Maciej Stachowiak  <mjs@apple.com>
 
         Test case by Anders, reviewed and tweaked by Maciej.
index 011a81ea8b52c0ff29bdf76382d127eafe873b92..1210591c8aaa190d5488f3d04dbf08009d09e9bd 100644 (file)
@@ -1 +1 @@
-7dd2af131f1af4b77082b09870243199
\ No newline at end of file
+15539ab6f39397a51bebded6f34a06d0
\ No newline at end of file
index 6d34e269dd0a404d37a0eb25544382585602cf2e..68292b93a4184726b57e3497cbf0d00b57785006 100644 (file)
Binary files a/LayoutTests/editing/deleting/delete-ws-fixup-002-expected.png and b/LayoutTests/editing/deleting/delete-ws-fixup-002-expected.png differ
index 57204b029181c599b75150b3c4abf89b63abcffe..d8857652f7b51cb6b7ef2af90377848a49b88c6c 100644 (file)
@@ -31,7 +31,7 @@ EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotificatio
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
-EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 1 of #text > B > SPAN > DIV > BODY > HTML > #document to 0 of #text > SPAN > DIV > BODY > HTML > #document toDOMRange:range from 7 of #text > SPAN > DIV > BODY > HTML > #document to 7 of #text > SPAN > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 7 of #text > SPAN > DIV > BODY > HTML > #document to 0 of #text > SPAN > DIV > BODY > HTML > #document toDOMRange:range from 7 of #text > SPAN > DIV > BODY > HTML > #document to 7 of #text > SPAN > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
 EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
 EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
 layer at (0,0) size 800x600
@@ -39,8 +39,14 @@ layer at (0,0) size 800x600
 layer at (0,0) size 800x600
   RenderBlock {HTML} at (0,0) size 800x600
     RenderBody {BODY} at (8,8) size 784x584
-      RenderBlock {DIV} at (0,0) size 784x84 [border: (2px solid #FF0000)]
-        RenderInline {SPAN} at (0,0) size 741x56
+      RenderBlock {P} at (0,0) size 784x36
+        RenderText {#text} at (0,0) size 751x36
+          text run at (0,0) width 259: "This testcase demonstrates a bug (9441). "
+          text run at (259,0) width 446: "When 'as' is deleted, there should be a space before and after the caret. "
+          text run at (705,0) width 46: "Editing"
+          text run at (0,18) width 531: "produces a tree that should result in two spaces, but for some reason it isn't rendered."
+      RenderBlock {DIV} at (0,52) size 784x84 [border: (2px solid #FF0000)]
+        RenderInline {SPAN} at (0,0) size 735x56
           RenderText {#text} at (14,14) size 97x28
             text run at (14,14) width 97: "in Liberty"
           RenderInline {I} at (0,0) size 117x28
@@ -49,13 +55,12 @@ layer at (0,0) size 800x600
               text run at (132,14) width 96: "and seven"
           RenderText {#text} at (228,14) size 63x28
             text run at (228,14) width 63: " years "
-          RenderInline {B} at (0,0) size 6x28
-            RenderText {#text} at (291,14) size 6x28
-              text run at (291,14) width 6: " "
-          RenderText {#text} at (297,14) size 741x56
-            text run at (297,14) width 110: "our fathers "
-            text run at (407,14) width 348: "f upon this continent, a new nation, "
+          RenderInline {B} at (0,0) size 0x0
+            RenderText {#text} at (0,0) size 0x0
+          RenderText {#text} at (291,14) size 735x56
+            text run at (291,14) width 110: "our fathers "
+            text run at (401,14) width 348: "f upon this continent, a new nation, "
             text run at (14,42) width 232: "conceived    in Liberty, "
             text run at (246,42) width 386: "and dedicated to the proposition that all"
         RenderText {#text} at (0,0) size 0x0
-caret: position 7 of child 2 {#text} of child 1 {SPAN} of child 1 {DIV} of child 1 {BODY} of child 0 {HTML} of document
+caret: position 7 of child 2 {#text} of child 1 {SPAN} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
index 1e647d633a01b39c540ac750fd245f623417f623..74c90f7ffb67ff29de9ecbf49ec29ba3b28474e7 100644 (file)
@@ -28,6 +28,7 @@ function editingTest() {
 <title>Editing Test</title> 
 </head> 
 <body>
+<p>This testcase demonstrates a bug (9441).  When 'as' is deleted, there should be a space before and after the caret.  Editing produces a tree that should result in two spaces, but for some reason it isn't rendered.</p>
 <div contenteditable id="root" class="editing">
 <span id="test">in Liberty<i>F    and seven</i> years <b>  as </b>our fathers  f upon this
 continent, a new nation, conceived &nbsp;&nbsp; in Liberty,                 and dedicated to the
diff --git a/LayoutTests/editing/execCommand/format-block-expected.checksum b/LayoutTests/editing/execCommand/format-block-expected.checksum
new file mode 100644 (file)
index 0000000..9c28535
--- /dev/null
@@ -0,0 +1 @@
+7310a63506bf17b27dcf3ad5b36a075a
\ No newline at end of file
diff --git a/LayoutTests/editing/execCommand/format-block-expected.png b/LayoutTests/editing/execCommand/format-block-expected.png
new file mode 100644 (file)
index 0000000..cde4e9b
Binary files /dev/null and b/LayoutTests/editing/execCommand/format-block-expected.png differ
diff --git a/LayoutTests/editing/execCommand/format-block-expected.txt b/LayoutTests/editing/execCommand/format-block-expected.txt
new file mode 100644 (file)
index 0000000..9fb98c6
--- /dev/null
@@ -0,0 +1,47 @@
+EDITING DELEGATE: shouldBeginEditingInDOMRange:range from 0 of DIV > BODY > HTML > #document to 11 of DIV > BODY > HTML > #document
+EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 0 of PRE > DIV > BODY > HTML > #document to 0 of PRE > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 0 of H1 > DIV > DIV > BODY > HTML > #document to 0 of H1 > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 0 of ADDRESS > DIV > BODY > HTML > #document to 0 of ADDRESS > DIV > BODY > HTML > #document toDOMRange:range from 0 of ADDRESS > DIV > BODY > HTML > #document to 0 of ADDRESS > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderBlock {HTML} at (0,0) size 800x600
+    RenderBody {BODY} at (8,8) size 784x584
+      RenderBlock {DIV} at (0,0) size 784x18
+        RenderText {#text} at (0,0) size 427x18
+          text run at (0,0) width 427: "This test uses FormatBlock to modify three of the paragraphs below"
+      RenderBlock (anonymous) at (0,18) size 784x18
+        RenderBR {BR} at (0,0) size 0x18
+      RenderBlock {DIV} at (0,36) size 784x212 [border: (1px solid #000000)]
+        RenderBlock {PRE} at (1,14) size 782x15
+          RenderText {#text} at (0,0) size 64x15
+            text run at (0,0) width 64: "Make Pre"
+        RenderBlock (anonymous) at (1,42) size 782x18
+          RenderBR {BR} at (0,0) size 0x18
+        RenderBlock {DIV} at (1,60) size 782x115
+          RenderBlock (anonymous) at (0,0) size 782x18
+            RenderText {#text} at (0,0) size 25x18
+              text run at (0,0) width 25: "Foo"
+            RenderBR {BR} at (25,14) size 0x0
+          RenderBlock {H1} at (0,39) size 782x37
+            RenderInline {SPAN} at (0,0) size 120x37
+              RenderText {#text} at (0,0) size 120x37
+                text run at (0,0) width 120: "Make h1"
+          RenderBlock (anonymous) at (0,97) size 782x18
+            RenderText {#text} at (0,0) size 22x18
+              text run at (0,0) width 22: "baz"
+        RenderBlock (anonymous) at (1,175) size 782x18
+          RenderBR {BR} at (0,0) size 0x18
+        RenderBlock {ADDRESS} at (1,193) size 782x18
+          RenderText {#text} at (0,0) size 279x18
+            text run at (0,0) width 279: "Attempt to apply the current formatting here"
+caret: position 0 of child 0 {#text} of child 9 {ADDRESS} of child 4 {DIV} of child 0 {BODY} of child 0 {HTML} of document
diff --git a/LayoutTests/editing/execCommand/format-block-from-range-selection-expected.checksum b/LayoutTests/editing/execCommand/format-block-from-range-selection-expected.checksum
new file mode 100644 (file)
index 0000000..69c9047
--- /dev/null
@@ -0,0 +1 @@
+2850169a8a5d6df7c64334e118869a1b
\ No newline at end of file
diff --git a/LayoutTests/editing/execCommand/format-block-from-range-selection-expected.png b/LayoutTests/editing/execCommand/format-block-from-range-selection-expected.png
new file mode 100644 (file)
index 0000000..712f9d5
Binary files /dev/null and b/LayoutTests/editing/execCommand/format-block-from-range-selection-expected.png differ
diff --git a/LayoutTests/editing/execCommand/format-block-from-range-selection-expected.txt b/LayoutTests/editing/execCommand/format-block-from-range-selection-expected.txt
new file mode 100644 (file)
index 0000000..f5393d0
--- /dev/null
@@ -0,0 +1,46 @@
+EDITING DELEGATE: shouldBeginEditingInDOMRange:range from 0 of DIV > BODY > HTML > #document to 9 of DIV > BODY > HTML > #document
+EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 2 of #text > DL > DIV > BODY > HTML > #document to 2 of #text > DL > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderBlock {HTML} at (0,0) size 800x600
+    RenderBody {BODY} at (8,8) size 784x584
+      RenderBlock {DIV} at (0,0) size 784x18
+        RenderText {#text} at (0,0) size 605x18
+          text run at (0,0) width 605: "This test uses FormatBlock to apply the dl tag to a range of paragraphs with different block tags."
+      RenderBlock (anonymous) at (0,18) size 784x18
+        RenderBR {BR} at (0,0) size 0x18
+      RenderBlock {DIV} at (0,36) size 784x222 [border: (1px solid #000000)]
+        RenderBlock {DL} at (1,17) size 782x18
+          RenderText {#text} at (0,0) size 25x18
+            text run at (0,0) width 25: "Foo"
+        RenderBlock {DIV} at (1,51) size 782x86
+          RenderBlock {DL} at (0,0) size 782x18
+            RenderText {#text} at (0,0) size 20x18
+              text run at (0,0) width 20: "bar"
+          RenderBlock (anonymous) at (0,34) size 782x0
+            RenderInline {SPAN} at (0,0) size 0x0
+          RenderBlock (anonymous) at (0,34) size 782x18
+            RenderBlock {DL} at (0,0) size 782x18
+              RenderInline {SPAN} at (0,0) size 22x18
+                RenderText {#text} at (0,0) size 22x18
+                  text run at (0,0) width 22: "baz"
+          RenderBlock (anonymous) at (0,68) size 782x0
+            RenderInline {SPAN} at (0,0) size 0x0
+          RenderBlock {DL} at (0,68) size 782x18
+            RenderText {#text} at (0,0) size 19x18
+              text run at (0,0) width 19: "raz"
+          RenderBlock (anonymous) at (0,102) size 782x0
+        RenderBlock {DL} at (1,153) size 782x18
+          RenderText {#text} at (0,0) size 24x18
+            text run at (0,0) width 24: "dar "
+        RenderBlock (anonymous) at (1,187) size 782x0
+        RenderBlock {DL} at (1,187) size 782x18
+          RenderText {#text} at (0,0) size 20x18
+            text run at (0,0) width 20: "yar"
+selection start: position 2 of child 0 {#text} of child 1 {DL} of child 4 {DIV} of child 0 {BODY} of child 0 {HTML} of document
+selection end:   position 2 of child 0 {#text} of child 6 {DL} of child 4 {DIV} of child 0 {BODY} of child 0 {HTML} of document
diff --git a/LayoutTests/editing/execCommand/format-block-from-range-selection.html b/LayoutTests/editing/execCommand/format-block-from-range-selection.html
new file mode 100644 (file)
index 0000000..ed10863
--- /dev/null
@@ -0,0 +1,18 @@
+<div id="description">This test uses FormatBlock to apply the dl tag to a range of paragraphs with different block tags.</div>
+<br>
+<div style="border:1px solid black" contenteditable="true">
+<pre id="start">Foo</pre>
+<div>bar<br><span>baz</span><br>raz</div>
+dar
+<br>
+<address id="end">yar</address>
+</div>
+
+<script>
+var s = window.getSelection();
+var start = document.getElementById("start").firstChild;
+var end = document.getElementById("end").firstChild;
+s.setBaseAndExtent(start, 2, end, 2);
+
+document.execCommand("FormatBlock", false, "dl");
+</script>
\ No newline at end of file
diff --git a/LayoutTests/editing/execCommand/format-block.html b/LayoutTests/editing/execCommand/format-block.html
new file mode 100644 (file)
index 0000000..a98b817
--- /dev/null
@@ -0,0 +1,23 @@
+<div id="description">This test uses FormatBlock to modify three of the paragraphs below</div>
+<br>
+<div style="border:1px solid black" contenteditable="true">
+<p id="item1">Make Pre</p>
+<br>
+<div>Foo<br><span id="item2">Make h1</span><br>baz</div>
+<br>
+<address id="item3">Attempt to apply the current formatting here</address>
+</div>
+
+<script>
+var s = window.getSelection();
+var r = document.createRange();
+var p1 = document.getElementById("item1");
+var p2 = document.getElementById("item2");
+var p3 = document.getElementById("item3");
+s.setPosition(p1, 0);
+document.execCommand("FormatBlock", false, "pre");
+s.setPosition(p2, 0);
+document.execCommand("FormatBlock", false, "h1");
+s.setPosition(p3, 0);
+document.execCommand("FormatBlock", false, "address");
+</script>
\ No newline at end of file
index a4e44c9005a69e8a77b54060163985380296721f..b57f2f3d1dcf0b071fbb25f7177eeb15e07e3d85 100644 (file)
@@ -1,3 +1,52 @@
+2006-06-14  Levi Weintraub  <lweintraub@apple.com>
+
+        Reviewed by justin
+
+        <http://bugzilla.opendarwin.org/show_bug.cgi?id=7580>
+        TinyMCE: Implement execCommand(formatBlock, ...)
+
+        * WebCore.xcodeproj/project.pbxproj: Added FormatBlock.{h,cpp} to the project.
+        * WebCore.vcproj/WebCore/WebCore.vcproj: Ditto.
+        * bridge/mac/WebCoreFrameBridge.h: Added WebUndoActions
+        * editing/CompositeEditCommand.cpp:
+        (WebCore::CompositeEditCommand::moveParagraph): Added a preserveStyle bool.
+        (WebCore::CompositeEditCommand::moveParagraphs): Ditto.  downstream() the start
+        or else we'll move collapsed whitespace and uncollapse it.
+        * editing/CompositeEditCommand.h:
+        * editing/DeleteSelectionCommand.cpp:
+        (WebCore::DeleteSelectionCommand::initializePositionData):
+        (WebCore::DeleteSelectionCommand::handleSpecialCaseBRDelete): Don't update m_endingPosition
+        because that's removeNode's responsibility.
+        (WebCore::updatePositionForNodeRemoval): Added.
+        (WebCore::DeleteSelectionCommand::removeNode): Turned removeFullySelectedNode into a virtual
+        overload of removeNode so that we can update positions as we remove nodes.
+        (WebCore::updatePositionForTextRemoval): Added.
+        (WebCore::DeleteSelectionCommand::deleteTextFromNode):
+        (WebCore::DeleteSelectionCommand::handleGeneralDelete): 
+        (WebCore::DeleteSelectionCommand::fixupWhitespace): Got rid of m_trailingWhitespaceValid
+        since m_trailingWhitespace is always valid (we update it as we remove nodes).
+        (WebCore::DeleteSelectionCommand::mergeParagraphs):
+        (WebCore::DeleteSelectionCommand::doApply): Leading and trailing spaces should
+        be fixed if they have collapsed before merging paragraphs.
+        * editing/DeleteSelectionCommand.h:
+        * editing/EditAction.h:
+        (WebCore::):
+        * editing/FormatBlockCommand.cpp: Added.
+        (WebCore::FormatBlockCommand::FormatBlockCommand):
+        (WebCore::FormatBlockCommand::modifyRange): Similar to InsertListCommand::modifyRange().
+        (WebCore::FormatBlockCommand::doApply):
+        * editing/FormatBlockCommand.h: Added.
+        (WebCore::FormatBlockCommand::editingAction):
+        * editing/InsertListCommand.h:
+        (WebCore::InsertListCommand::editingAction):
+        * editing/JSEditor.cpp:
+        * editing/MergeIdenticalElementsCommand.cpp:
+        (WebCore::MergeIdenticalElementsCommand::doApply):
+        * editing/htmlediting.cpp:
+        (WebCore::validBlockTag):
+        (WebCore::createElement):
+        * editing/htmlediting.h:
+
 2006-06-14  Maciej Stachowiak  <mjs@apple.com>
 
         Reviewed by Anders.
index d39cdfa034b85514ee2a818012d275acf574baf0..7c3eef65a9daf23d6c69788645a2c20f1bea6694 100644 (file)
                        <File\r
                                RelativePath="..\..\editing\EditCommand.h"\r
                                >\r
+                       </File>
+                       <File\r
+                               RelativePath="..\..\editing\FormatBlockCommand.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\editing\FormatBlockCommand.h"\r
+                               >\r
                        </File>\r
                        <File\r
                                RelativePath="..\..\editing\htmlediting.cpp"\r
index 05aeb7f7ec63c0b19edeef2ab1d172d8af13e780..b3a21d09cea14460a87cb1de9c850753e68fd4eb 100644 (file)
                BCFE8E320A02A1D30009E61D /* WebCoreTextRenderer.mm in Sources */ = {isa = PBXBuildFile; fileRef = BCFE8E310A02A1D30009E61D /* WebCoreTextRenderer.mm */; };
                C6D74AD509AA282E000B0A52 /* ModifySelectionListLevel.h in Headers */ = {isa = PBXBuildFile; fileRef = C6D74AD309AA282E000B0A52 /* ModifySelectionListLevel.h */; };
                C6D74AE409AA290A000B0A52 /* ModifySelectionListLevel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C6D74AE309AA290A000B0A52 /* ModifySelectionListLevel.cpp */; };
+               D05CED290A40BB2C00C5AF38 /* FormatBlockCommand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05CED270A40BB2C00C5AF38 /* FormatBlockCommand.cpp */; };
+               D05CED2A0A40BB2C00C5AF38 /* FormatBlockCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = D05CED280A40BB2C00C5AF38 /* FormatBlockCommand.h */; };
                D07DEAB90A36554A00CA30F8 /* InsertListCommand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D07DEAB70A36554A00CA30F8 /* InsertListCommand.cpp */; };
                D07DEABA0A36554A00CA30F8 /* InsertListCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = D07DEAB80A36554A00CA30F8 /* InsertListCommand.h */; };
                D086FE9809D53AAB005BC74D /* UnlinkCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = D086FE9609D53AAB005BC74D /* UnlinkCommand.h */; };
                BEF7EEA105FF8F0D009717EE /* KWQEditCommand.mm */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KWQEditCommand.mm; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
                C6D74AD309AA282E000B0A52 /* ModifySelectionListLevel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModifySelectionListLevel.h; sourceTree = "<group>"; };
                C6D74AE309AA290A000B0A52 /* ModifySelectionListLevel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ModifySelectionListLevel.cpp; sourceTree = "<group>"; };
+               D05CED270A40BB2C00C5AF38 /* FormatBlockCommand.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = FormatBlockCommand.cpp; sourceTree = "<group>"; };
+               D05CED280A40BB2C00C5AF38 /* FormatBlockCommand.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = FormatBlockCommand.h; sourceTree = "<group>"; };
                D07DEAB70A36554A00CA30F8 /* InsertListCommand.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = InsertListCommand.cpp; sourceTree = "<group>"; };
                D07DEAB80A36554A00CA30F8 /* InsertListCommand.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = InsertListCommand.h; sourceTree = "<group>"; };
                D086FE9609D53AAB005BC74D /* UnlinkCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UnlinkCommand.h; sourceTree = "<group>"; };
                93309D86099E64910056E581 /* editing */ = {
                        isa = PBXGroup;
                        children = (
+                               D05CED270A40BB2C00C5AF38 /* FormatBlockCommand.cpp */,
+                               D05CED280A40BB2C00C5AF38 /* FormatBlockCommand.h */,
                                93309D87099E64910056E581 /* AppendNodeCommand.cpp */,
                                93309D88099E64910056E581 /* AppendNodeCommand.h */,
                                93309D89099E64910056E581 /* ApplyStyleCommand.cpp */,
                                1AC694C80A3B1676003F5049 /* PluginDocument.h in Headers */,
                                51D3EA170A3D24D300BADA35 /* SQLDatabase.h in Headers */,
                                BC92F1DE0A40AEA300AC0746 /* DeprecatedSlider.h in Headers */,
+                               D05CED2A0A40BB2C00C5AF38 /* FormatBlockCommand.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                51D3EA160A3D24D300BADA35 /* SQLDatabase.cpp in Sources */,
                                51D3EA180A3D24D300BADA35 /* SQLStatement.cpp in Sources */,
                                BC92F1DD0A40AEA300AC0746 /* DeprecatedSlider.cpp in Sources */,
+                               D05CED290A40BB2C00C5AF38 /* FormatBlockCommand.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
index d8320a3a0f2e2f975a52f257362931546f983f57..59f05ccbe06cb37dca8bd9e98ddf4e0c0400eff7 100644 (file)
@@ -152,6 +152,8 @@ typedef enum {
     WebUndoActionTyping,
     WebUndoActionCreateLink,
     WebUndoActionUnlink,
+    WebUndoActionFormatBlock,
+    WebUndoActionInsertList
 } WebUndoAction;
 
 typedef enum {
index a8b9312b934806707c53a46d8269e54ff6652ed2..5c65c85eaf33834551a4766517407a4692a6d5e7 100644 (file)
@@ -649,15 +649,14 @@ void CompositeEditCommand::pushPartiallySelectedAnchorElementsDown()
 }
 
 // This moves a paragraph preserving its style.
-void CompositeEditCommand::moveParagraph(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection)
+void CompositeEditCommand::moveParagraph(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle)
 {
     ASSERT(isStartOfParagraph(startOfParagraphToMove));
     ASSERT(isEndOfParagraph(endOfParagraphToMove));
-    moveParagraphs(startOfParagraphToMove, endOfParagraphToMove, destination, preserveSelection);
+    moveParagraphs(startOfParagraphToMove, endOfParagraphToMove, destination, preserveSelection, preserveStyle);
 }
 
-// FIXME: Always preserve the selection, and pass the paragraphs to operate on using an endingSelection().
-void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection)
+void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle)
 {
     if (startOfParagraphToMove == destination)
         return;
@@ -682,16 +681,16 @@ void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagrap
     }
     
     VisiblePosition beforeParagraph = startOfParagraphToMove.previous();
-    
-    Position start = startOfParagraphToMove.deepEquivalent().upstream();
-    // We upstream() the end so that we don't include collapsed whitespace in the move.
-    // If we must later add a br after the moved paragraph, doing so would cause the moved unrendered space to become rendered.
+
+    // We upstream() the end and downstream() the start so that we don't include collapsed whitespace in the move.
+    // When we paste a fragment, spaces after the end and before the start are treated as though they were rendered.    
+    Position start = startOfParagraphToMove.deepEquivalent().downstream();
     Position end = endOfParagraphToMove.deepEquivalent().upstream();
     RefPtr<Range> range = new Range(document(), start.node(), start.offset(), end.node(), end.offset());
 
     // FIXME: This is an inefficient way to preserve style on nodes in the paragraph to move.  It 
     // shouldn't matter though, since moved paragraphs will usually be quite small.
-    RefPtr<DocumentFragment> fragment = createFragmentFromMarkup(document(), range->toHTML(), "");
+    RefPtr<DocumentFragment> fragment = startOfParagraphToMove != endOfParagraphToMove ? createFragmentFromMarkup(document(), range->toHTML(), "") : 0;
     
     setEndingSelection(Selection(start, end, DOWNSTREAM));
     deleteSelection(false, false);
@@ -719,7 +718,7 @@ void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagrap
     destinationIndex = TextIterator::rangeLength(new Range(document(), Position(document(), 0), destination.deepEquivalent()));
     
     setEndingSelection(destination);
-    EditCommandPtr cmd(new ReplaceSelectionCommand(document(), fragment.get(), true, false, false, true));
+    EditCommandPtr cmd(new ReplaceSelectionCommand(document(), fragment.get(), true, false, !preserveStyle, true));
     applyCommandToComposite(cmd);
     
     if (preserveSelection && startIndex != -1) {
index eff3414107af1dc1ec24832b79a3d88b8015f491..ef03f8d3be353293a4a76ab273baf4542f038abf 100644 (file)
@@ -72,8 +72,8 @@ protected:
     void removeCSSProperty(WebCore::CSSStyleDeclaration *, int property);
     void removeNodeAttribute(WebCore::Element *, const WebCore::QualifiedName& attribute);
     void removeChildrenInRange(WebCore::Node *node, int from, int to);
-    void removeNode(WebCore::Node *removeChild);
-    void removeNodePreservingChildren(WebCore::Node *node);
+    virtual void removeNode(WebCore::Node*);
+    void removeNodePreservingChildren(WebCore::Node*);
     void removeNodeAndPruneAncestors(Node* node);
     void prune(PassRefPtr<Node> node);
     void replaceTextInNode(WebCore::Text *node, int offset, int count, const WebCore::String &replacementText);
@@ -100,8 +100,8 @@ protected:
     void pushAnchorElementDown(WebCore::Node*);
     void pushPartiallySelectedAnchorElementsDown();
     
-    void moveParagraph(const VisiblePosition&, const VisiblePosition&, const VisiblePosition&, bool preserveSelection = false);
-    void moveParagraphs(const VisiblePosition&, const VisiblePosition&, const VisiblePosition&, bool preserveSelection = false);
+    void moveParagraph(const VisiblePosition&, const VisiblePosition&, const VisiblePosition&, bool preserveSelection = false, bool preserveStyle = true);
+    void moveParagraphs(const VisiblePosition&, const VisiblePosition&, const VisiblePosition&, bool preserveSelection = false, bool preserveStyle = true);
 
     DeprecatedValueList<EditCommandPtr> m_cmds;
 };
index 352609aa7d7275ebab0b548e2d4964742349c8ae..2808580401c26faf00622b3146d8283805125c03 100644 (file)
@@ -146,8 +146,6 @@ void DeleteSelectionCommand::initializePositionData()
         }
     }
     
-    m_trailingWhitespaceValid = true;
-    
     //
     // Handle setting start and end blocks and the start node.
     //
@@ -179,7 +177,6 @@ bool DeleteSelectionCommand::handleSpecialCaseBRDelete()
     bool downstreamStartIsBR = m_downstreamStart.node()->hasTagName(brTag);
     bool isBROnLineByItself = upstreamStartIsBR && downstreamStartIsBR && m_downstreamStart.node() == m_upstreamEnd.node();
     if (isBROnLineByItself) {
-        m_endingPosition = Position(m_downstreamStart.node()->parentNode(), m_downstreamStart.node()->nodeIndex());
         removeNode(m_downstreamStart.node());
         m_mergeBlocksAfterDelete = false;
         return true;
@@ -193,7 +190,17 @@ bool DeleteSelectionCommand::handleSpecialCaseBRDelete()
     return false;
 }
 
-void DeleteSelectionCommand::removeFullySelectedNode(Node *node)
+static void updatePositionForNodeRemoval(Node* node, Position& position)
+{
+    if (position.isNull())
+        return;
+    if (node->parent() == position.node() && node->nodeIndex() < (unsigned)position.offset())
+        position = Position(position.node(), position.offset() - 1);
+    if (position.node() == node || position.node()->isAncestor(node))
+        position = positionBeforeNode(node);
+}
+
+void DeleteSelectionCommand::removeNode(Node *node)
 {
     if (isTableStructureNode(node) || node == node->rootEditableElement()) {
         // Do not remove an element of table structure; remove its contents.
@@ -202,7 +209,7 @@ void DeleteSelectionCommand::removeFullySelectedNode(Node *node)
         while (child) {
             Node *remove = child;
             child = child->nextSibling();
-            removeFullySelectedNode(remove);
+            removeNode(remove);
         }
         
         // make sure empty cell has some height
@@ -213,25 +220,31 @@ void DeleteSelectionCommand::removeFullySelectedNode(Node *node)
         return;
     }
     
-    Position p = positionBeforeNode(node);
-    
-    if (node->parent() == m_endingPosition.node() && node->nodeIndex() < (unsigned)m_endingPosition.offset())
-        m_endingPosition = Position(m_endingPosition.node(), m_endingPosition.offset() - 1);
+    // FIXME: Update the endpoints of the range being deleted.
+    updatePositionForNodeRemoval(node, m_endingPosition);
+    updatePositionForNodeRemoval(node, m_leadingWhitespace);
+    updatePositionForNodeRemoval(node, m_trailingWhitespace);
     
-    removeNode(node);
-    
-    if (!m_endingPosition.node()->inDocument())
-        m_endingPosition = p;
+    CompositeEditCommand::removeNode(node);
 }
 
-void DeleteSelectionCommand::deleteTextFromNode(Text *node, int offset, int count)
+
+void updatePositionForTextRemoval(Node* node, int offset, int count, Position& position)
 {
-    if (m_endingPosition.node() == node) {
-        if (m_endingPosition.offset() > offset + count)
-            m_endingPosition = Position(m_endingPosition.node(), m_endingPosition.offset() - count);
-        else if (m_endingPosition.offset() > offset)
-            m_endingPosition = Position(m_endingPosition.node(), offset);
+    if (position.node() == node) {
+        if (position.offset() > offset + count)
+            position = Position(position.node(), position.offset() - count);
+        else if (position.offset() > offset)
+            position = Position(position.node(), offset);
     }
+}
+
+void DeleteSelectionCommand::deleteTextFromNode(Text *node, int offset, int count)
+{
+    // FIXME: Update the endpoints of the range being deleted.
+    updatePositionForTextRemoval(node, offset, count, m_endingPosition);
+    updatePositionForTextRemoval(node, offset, count, m_leadingWhitespace);
+    updatePositionForTextRemoval(node, offset, count, m_trailingWhitespace);
     
     CompositeEditCommand::deleteTextFromNode(node, offset, count);
 }
@@ -267,13 +280,12 @@ void DeleteSelectionCommand::handleGeneralDelete()
         if (!startNode->renderer() || 
             (startOffset == 0 && m_downstreamEnd.offset() >= maxDeepOffset(startNode))) {
             // just delete
-            removeFullySelectedNode(startNode);
+            removeNode(startNode);
         } else if (m_downstreamEnd.offset() - startOffset > 0) {
             if (startNode->isTextNode()) {
                 // in a text node that needs to be trimmed
                 Text *text = static_cast<Text *>(startNode);
                 deleteTextFromNode(text, startOffset, m_downstreamEnd.offset() - startOffset);
-                m_trailingWhitespaceValid = false;
             } else {
                 removeChildrenInRange(startNode, startOffset, m_downstreamEnd.offset());
                 m_endingPosition = m_upstreamStart;
@@ -308,13 +320,12 @@ void DeleteSelectionCommand::handleGeneralDelete()
                     ASSERT(node->nodeIndex() < (unsigned)m_downstreamEnd.offset());
                     m_downstreamEnd = Position(m_downstreamEnd.node(), m_downstreamEnd.offset() - 1);
                 }
-                removeFullySelectedNode(node);
+                removeNode(node);
                 node = nextNode;
             } else {
                 Node* n = node->lastDescendant();
                 if (m_downstreamEnd.node() == n && m_downstreamEnd.offset() >= n->caretMaxOffset()) {
-                    removeFullySelectedNode(node); 
-                    m_trailingWhitespaceValid = false;
+                    removeNode(node); 
                     node = 0;
                 } else {
                     node = node->traverseNextNode();
@@ -333,8 +344,7 @@ void DeleteSelectionCommand::handleGeneralDelete()
                     m_upstreamStart = Position(m_downstreamEnd.node()->parentNode(), m_downstreamEnd.node()->nodeIndex());
                 }
                 
-                removeFullySelectedNode(m_downstreamEnd.node());
-                m_trailingWhitespaceValid = false;
+                removeNode(m_downstreamEnd.node());
             } else {
                 if (m_downstreamEnd.node()->isTextNode()) {
                     // in a text node that needs to be trimmed
@@ -342,7 +352,6 @@ void DeleteSelectionCommand::handleGeneralDelete()
                     if (m_downstreamEnd.offset() > 0) {
                         deleteTextFromNode(text, 0, m_downstreamEnd.offset());
                         m_downstreamEnd = Position(text, 0);
-                        m_trailingWhitespaceValid = false;
                     }
                 } else {
                     int offset = 0;
@@ -361,48 +370,22 @@ void DeleteSelectionCommand::handleGeneralDelete()
     }
 }
 
-// FIXME: Can't really determine this without taking white-space mode into account.
-static inline bool nextCharacterIsCollapsibleWhitespace(const Position &pos)
-{
-    if (!pos.node())
-        return false;
-    if (!pos.node()->isTextNode())
-        return false;
-    return isCollapsibleWhitespace(static_cast<Text *>(pos.node())->data()[pos.offset()]);
-}
-
 void DeleteSelectionCommand::fixupWhitespace()
 {
     updateLayout();
-    if (m_leadingWhitespace.isNotNull() && (m_trailingWhitespace.isNotNull() || !m_leadingWhitespace.isRenderedCharacter())) {
-        LOG(Editing, "replace leading");
+    // FIXME: isRenderedCharacter should be removed, and we should use VisiblePosition::characterAfter and VisiblePosition::characterBefore
+    if (m_leadingWhitespace.isNotNull() && !m_leadingWhitespace.isRenderedCharacter()) {
         Text *textNode = static_cast<Text *>(m_leadingWhitespace.node());
         replaceTextInNode(textNode, m_leadingWhitespace.offset(), 1, nonBreakingSpaceString());
     }
-    else if (m_trailingWhitespace.isNotNull()) {
-        if (m_trailingWhitespaceValid) {
-            if (!m_trailingWhitespace.isRenderedCharacter()) {
-                LOG(Editing, "replace trailing [valid]");
-                Text *textNode = static_cast<Text *>(m_trailingWhitespace.node());
-                replaceTextInNode(textNode, m_trailingWhitespace.offset(), 1, nonBreakingSpaceString());
-            }
-        }
-        else {
-            Position pos = m_endingPosition.downstream();
-            pos = Position(pos.node(), pos.offset() - 1);
-            if (nextCharacterIsCollapsibleWhitespace(pos) && !pos.isRenderedCharacter()) {
-                LOG(Editing, "replace trailing [invalid]");
-                Text *textNode = static_cast<Text *>(pos.node());
-                replaceTextInNode(textNode, pos.offset(), 1, nonBreakingSpaceString());
-                // need to adjust ending position since the trailing position is not valid.
-                m_endingPosition = pos;
-            }
-        }
+    if (m_trailingWhitespace.isNotNull() && !m_trailingWhitespace.isRenderedCharacter()) {
+        Text *textNode = static_cast<Text *>(m_trailingWhitespace.node());
+        replaceTextInNode(textNode, m_trailingWhitespace.offset(), 1, nonBreakingSpaceString());
     }
 }
 
-// If a selection ended in a different paragraph than it started in, we must merge 
-// the two paragraphs after deleting the selection.
+// If a selection starts in one block and ends in another, we have to merge to bring content before the
+// start together with content after the end.
 void DeleteSelectionCommand::mergeParagraphs()
 {
     if (!m_mergeBlocksAfterDelete)
@@ -421,7 +404,8 @@ void DeleteSelectionCommand::mergeParagraphs()
     if (m_endBlock == m_startBlock)
         return;
         
-    // Don't move content between parts of a table.
+    // Don't move content between parts of a table or between table and non-table content.
+    // FIXME: This isn't right.  A table with two rows and a single column appears as two paragraphs.
     if (isTableStructureNode(m_downstreamEnd.node()->enclosingBlockFlowElement()) || isTableStructureNode(m_upstreamStart.node()->enclosingBlockFlowElement()))
         return;
         
@@ -548,10 +532,10 @@ void DeleteSelectionCommand::doApply()
     
     handleGeneralDelete();
     
-    mergeParagraphs();
-    
     fixupWhitespace();
     
+    mergeParagraphs();
+    
     RefPtr<Node> placeholder = needPlaceholder ? createBreakElement(document()) : 0;
     if (placeholder)
         insertNodeAt(placeholder.get(), m_endingPosition.node(), m_endingPosition.offset());
index f4972e5be232df2d3093c84cf011f0760daf095c..6c867f627147aaf5a8be34da035eabd6695570a4 100644 (file)
@@ -51,15 +51,14 @@ private:
     void fixupWhitespace();
     void mergeParagraphs();
     void calculateEndingPosition();
-    void calculateTypingStyleAfterDelete(WebCore::Node *insertedPlaceholder);
+    void calculateTypingStyleAfterDelete(WebCore::Node*);
     void clearTransientState();
-    void removeFullySelectedNode(Node* node);
-    virtual void deleteTextFromNode(Text *node, int offset, int count);
+    virtual void removeNode(Node*);
+    virtual void deleteTextFromNode(Text*, int, int);
 
     bool m_hasSelectionToDelete;
     bool m_smartDelete;
     bool m_mergeBlocksAfterDelete;
-    bool m_trailingWhitespaceValid;
 
     // This data is transient and should be cleared at the end of the doApply function.
     Selection m_selectionToDelete;
index a4df8fa211d860fd472eb2c85b0326ff05692960..88a9f051d896fdf7cf7cec71fec3f59e2a1580e7 100644 (file)
@@ -61,6 +61,8 @@ namespace WebCore {
         EditActionTyping,
         EditActionCreateLink,
         EditActionUnlink,
+        EditActionFormatBlock,
+        EditActionInsertList
     } EditAction;    
 }
 
diff --git a/WebCore/editing/FormatBlockCommand.cpp b/WebCore/editing/FormatBlockCommand.cpp
new file mode 100644 (file)
index 0000000..61cc3c8
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "Element.h"
+#include "FormatBlockCommand.h"
+#include "Document.h"
+#include "htmlediting.h"
+#include "HTMLElement.h"
+#include "HTMLNames.h"
+#include "visible_units.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+FormatBlockCommand::FormatBlockCommand(Document* document, const String& tagName) 
+    : CompositeEditCommand(document), m_tagName(tagName)
+{}
+
+
+
+bool FormatBlockCommand::modifyRange()
+{
+    ASSERT(endingSelection().isRange());
+    VisiblePosition visibleStart = endingSelection().visibleStart();
+    VisiblePosition visibleEnd = endingSelection().visibleEnd();
+    VisiblePosition startOfLastParagraph = startOfParagraph(visibleEnd);
+    
+    if (startOfParagraph(visibleStart) == startOfLastParagraph)
+        return false;
+
+    setEndingSelection(visibleStart);
+    doApply();
+    visibleStart = endingSelection().visibleStart();
+    VisiblePosition nextParagraph = endOfParagraph(visibleStart).next();
+    while (nextParagraph.isNotNull() && nextParagraph != startOfLastParagraph) {
+        setEndingSelection(nextParagraph);
+        doApply();
+        nextParagraph = endOfParagraph(endingSelection().visibleStart()).next();
+    }
+    setEndingSelection(visibleEnd);
+    doApply();
+    visibleEnd = endingSelection().visibleEnd();
+    setEndingSelection(Selection(visibleStart.deepEquivalent(), visibleEnd.deepEquivalent(), DOWNSTREAM));
+
+    return true;
+}
+
+void FormatBlockCommand::doApply()
+{
+    if (endingSelection().isNone())
+        return;
+
+    if (endingSelection().isRange() && modifyRange())
+        return;
+    
+    if (!endingSelection().rootEditableElement())
+        return;
+    
+    String localName, prefix;
+    if (!Document::parseQualifiedName(m_tagName, prefix, localName))
+        return;
+    QualifiedName qTypeOfBlock = QualifiedName(AtomicString(prefix), AtomicString(localName), xhtmlNamespaceURI);
+    
+    Node* enclosingBlock = enclosingBlockFlowElement(endingSelection().visibleStart());
+    if (enclosingBlock->hasTagName(qTypeOfBlock))
+        // We're already in a block with the format we want, so we don't have to do anything
+        return;
+    
+    VisiblePosition paragraphStart = startOfParagraph(endingSelection().visibleStart());
+    VisiblePosition paragraphEnd = endOfParagraph(endingSelection().visibleStart());
+    VisiblePosition blockStart = startOfBlock(endingSelection().visibleStart());
+    VisiblePosition blockEnd = endOfBlock(endingSelection().visibleStart());
+    RefPtr<Node> blockNode = createElement(document(), m_tagName);
+    RefPtr<Node> placeholder = createBreakElement(document());
+    
+    if (validBlockTag(enclosingBlock->nodeName().lower()) && paragraphStart == blockStart && paragraphEnd == blockEnd)
+        // Already in a valid block tag that only contains the current paragraph, so we can swap with the new tag
+        insertNodeBefore(blockNode.get(), enclosingBlock);
+    else {
+        Position upstreamStart = paragraphStart.deepEquivalent().upstream();
+        insertNodeAt(blockNode.get(), upstreamStart.node(), upstreamStart.offset());
+    }
+    appendNode(placeholder.get(), blockNode.get());
+    moveParagraph(paragraphStart, paragraphEnd, VisiblePosition(Position(placeholder.get(), 0)), true, false);
+}
+
+}
diff --git a/WebCore/editing/FormatBlockCommand.h b/WebCore/editing/FormatBlockCommand.h
new file mode 100644 (file)
index 0000000..5457a7a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef FormatBlockCommand_h
+#define FormatBlockCommand_h
+
+#include "CompositeEditCommand.h"
+
+namespace WebCore {
+
+class FormatBlockCommand : public CompositeEditCommand
+{
+public:
+    FormatBlockCommand(WebCore::Document*, const String&);
+    virtual void doApply();
+    virtual EditAction editingAction() const { return EditActionFormatBlock; }
+private:
+    bool modifyRange();
+    String m_tagName;
+};
+
+} // namespace WebCore
+
+#endif // InsertListCommand_h
index 575461b5e1e34dfc1cd3ccbd932d3cc1cfbc9186..db87b3875a8859ab0ad8db37360ab0052087cba9 100644 (file)
@@ -36,6 +36,7 @@ public:
     enum EListType { OrderedListType, UnorderedListType };
     InsertListCommand(WebCore::Document*, EListType, const String&);
     virtual void doApply();
+    virtual EditAction editingAction() const { return EditActionInsertList; }
 private:
     Node* fixOrphanedListChild(Node*);
     bool modifyRange();
index 6361a2753acd90e1edfb7020f934c3dd15091938..4fb31bafd09e0ca59200fc252d5aadcef5a42797 100644 (file)
@@ -30,6 +30,7 @@
 #include "CreateLinkCommand.h"
 #include "Document.h"
 #include "DocumentFragment.h"
+#include "FormatBlockCommand.h"
 #include "Frame.h"
 #include "HTMLNames.h"
 #include "HTMLImageElement.h"
@@ -259,6 +260,16 @@ bool execForeColor(Frame *frame, bool userInterface, const String &value)
     return execStyleChange(frame,  CSS_PROP_COLOR, value);
 }
 
+bool execFormatBlock(Frame *frame, bool userInterface, const String &value)
+{
+    String tagName = value.lower();
+    if (!validBlockTag(tagName))
+        return false;
+
+    EditCommandPtr(new FormatBlockCommand(frame->document(), tagName)).apply();
+    return true;
+}
+
 bool execInsertHorizontalRule(Frame* frame, bool userInterface, const String& value)
 {
     RefPtr<HTMLElement> hr = new HTMLElement(hrTag, frame->document());
@@ -633,6 +644,7 @@ CommandMap *createCommandDictionary()
         { "FontSize", { execFontSize, enabledAnySelection, stateNone, valueFontSize } },
         { "FontSizeDelta", { execFontSizeDelta, enabledAnySelection, stateNone, valueFontSizeDelta } },
         { "ForeColor", { execForeColor, enabledAnySelection, stateNone, valueForeColor } },
+        { "FormatBlock", { execFormatBlock, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
         { "ForwardDelete", { execForwardDelete, enabledAnyEditableSelection, stateNone, valueNull } },
         { "Indent", { execIndent, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
         { "InsertHorizontalRule", { execInsertHorizontalRule, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
@@ -680,7 +692,6 @@ CommandMap *createCommandDictionary()
         // DirLTR (not supported)
         // DirRTL (not supported)
         // EditMode (not supported)
-        // FormatBlock (not supported)
         // InlineDirLTR (not supported)
         // InlineDirRTL (not supported)
         // InsertButton (not supported)
index 7cc8266038720f326df6251f8441b064a508eb02..1adf84063685b7d0b80922d906127588e0fd3c4a 100644 (file)
@@ -45,7 +45,7 @@ void MergeIdenticalElementsCommand::doApply()
     ASSERT(m_element1);
     ASSERT(m_element2);
     ASSERT(m_element1->nextSibling() == m_element2);
-
+    
     ExceptionCode ec = 0;
 
     if (!m_atChild)
index ee6874e55f68ed746c627323e7015238c992eded..52b7cc5a5c48283b1455edd442584cdbf19fa154 100644 (file)
@@ -257,6 +257,27 @@ bool isSpecialElement(const Node *n)
     return false;
 }
 
+// Checks if a string is a valid tag for the FormatBlockCommand function of execCommand. Expects lower case strings.
+bool validBlockTag(const String& blockTag)
+{
+    if (blockTag == "address" ||
+        blockTag == "blockquote" ||
+        blockTag == "dd" ||
+        blockTag == "div" ||
+        blockTag == "dl" ||
+        blockTag == "dt" ||
+        blockTag == "h1" ||
+        blockTag == "h2" ||
+        blockTag == "h3" ||
+        blockTag == "h4" ||
+        blockTag == "h5" ||
+        blockTag == "h6" ||
+        blockTag == "p" ||
+        blockTag == "pre")
+        return true;
+    return false;
+}
+
 static Node* firstInSpecialElement(const Position& pos)
 {
     Node* rootEditableElement = pos.node()->rootEditableElement();
@@ -507,6 +528,14 @@ PassRefPtr<Element> createListItemElement(Document *document)
     return breakNode.release();
 }
 
+PassRefPtr<Element> createElement(Document* document, String& tagName)
+{
+    ExceptionCode ec = 0;
+    RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, tagName, ec);
+    ASSERT(ec == 0);
+    return breakNode.release();
+}
+
 bool isTabSpanNode(const Node *node)
 {
     return (node && node->isElementNode() && static_cast<const Element *>(node)->getAttribute("class") == AppleTabSpanClass);
index 95a4489b90c3662831a3cbbd3f6dc3a956ecd07a..44d3dc5ee8828cf67ea806b75c5646fe95674683 100644 (file)
@@ -55,12 +55,14 @@ Position positionBeforeNode(const Node*);
 Position positionAfterNode(const Node*);
 
 bool isSpecialElement(const Node*);
+bool validBlockTag(const String&);
 
 PassRefPtr<Element> createDefaultParagraphElement(Document*);
 PassRefPtr<Element> createBreakElement(Document*);
 PassRefPtr<Element> createOrderedListElement(Document*);
 PassRefPtr<Element> createUnorderedListElement(Document*);
 PassRefPtr<Element> createListItemElement(Document*);
+PassRefPtr<Element> createElement(Document*, String&);
 
 bool isTabSpanNode(const Node*);
 bool isTabSpanTextNode(const Node*);
index 548d22080c74f165a5728338c41bdef54bdb1210..42864a9673ee80de39f8303063ddfe04e615d324 100644 (file)
@@ -1,3 +1,14 @@
+2006-06-14  Levi Weintraub  <lweintraub@apple.com>
+
+        Reviewed by justin
+        
+        <http://bugzilla.opendarwin.org/show_bug.cgi?id=7580>
+        TinyMCE: Implement execCommand(formatBlock, ...)
+        
+        * English.lproj/Localizable.strings:
+        * WebCoreSupport/WebFrameBridge.m:
+        (-[WebFrameBridge nameForUndoAction:]):
+
 2006-06-14  Tim Omernick  <timo@apple.com>
 
         Reviewed by John Sullivan.
index c32b9f5cd30687bad3c80ddb671bc5ba12bc5a18..e5247a6093f1d7c71c54208cfa8d9b81a95512bf 100644 (file)
Binary files a/WebKit/English.lproj/Localizable.strings and b/WebKit/English.lproj/Localizable.strings differ
index 46938c38688a2e3601401594135b491feb24fad3..e7e301d19d267027c4445644503fa3564633243c 100644 (file)
@@ -1571,6 +1571,8 @@ static id <WebFormDelegate> formDelegate(WebFrameBridge *self)
         case WebUndoActionTyping: return UI_STRING_KEY("Typing", "Typing (Undo action name)", "Undo action name");
         case WebUndoActionCreateLink: return UI_STRING_KEY("Create Link", "Create Link (Undo action name)", "Undo action name");
         case WebUndoActionUnlink: return UI_STRING_KEY("Unlink", "Unlink (Undo action name)", "Undo action name");
+        case WebUndoActionInsertList: return UI_STRING_KEY("Insert List", "Insert List (Undo action name)", "Undo action name");
+        case WebUndoActionFormatBlock: return UI_STRING_KEY("Formatting", "Format Block (Undo action name)", "Undo action name");
     }
     return nil;
 }