2008-11-04 Anders Carlsson <andersca@apple.com>
[webkit/qt.git] / WebCore / editing / BreakBlockquoteCommand.cpp
bloba1729536864dd2abe5172c97c97773c0dff05eaf
1 /*
2 * Copyright (C) 2005 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "config.h"
27 #include "BreakBlockquoteCommand.h"
29 #include "Element.h"
30 #include "HTMLNames.h"
31 #include "Text.h"
32 #include "VisiblePosition.h"
33 #include "htmlediting.h"
34 #include "RenderListItem.h"
36 namespace WebCore {
38 using namespace HTMLNames;
40 BreakBlockquoteCommand::BreakBlockquoteCommand(Document *document)
41 : CompositeEditCommand(document)
45 void BreakBlockquoteCommand::doApply()
47 if (endingSelection().isNone())
48 return;
50 // Delete the current selection.
51 if (endingSelection().isRange())
52 deleteSelection(false, false);
54 VisiblePosition visiblePos = endingSelection().visibleStart();
55 // pos is a position equivalent to the caret. We use downstream() so that pos will
56 // be in the first node that we need to move (there are a few exceptions to this, see below).
57 Position pos = endingSelection().start().downstream();
59 // startNode is the first node that we need to move to the new blockquote.
60 Node* startNode = pos.node();
61 // Find the top-most blockquote from the start.
62 Node* topBlockquote = 0;
63 for (Node *node = startNode->parentNode(); node; node = node->parentNode()) {
64 if (isMailBlockquote(node))
65 topBlockquote = node;
67 if (!topBlockquote || !topBlockquote->parentNode())
68 return;
70 // Insert a break after the top blockquote.
71 RefPtr<Element> breakNode = createBreakElement(document());
72 insertNodeAfter(breakNode.get(), topBlockquote);
74 if (isLastVisiblePositionInNode(visiblePos, topBlockquote)) {
75 setEndingSelection(Selection(Position(breakNode.get(), 0), DOWNSTREAM));
76 rebalanceWhitespace();
77 return;
80 // Don't move a line break just after the caret. Doing so would create an extra, empty paragraph
81 // in the new blockquote.
82 if (lineBreakExistsAtPosition(visiblePos))
83 pos = pos.next();
85 // Split at pos if in the middle of a text node.
86 if (startNode->isTextNode()) {
87 Text* textNode = static_cast<Text*>(startNode);
88 if ((unsigned)pos.offset() >= textNode->length()) {
89 startNode = startNode->traverseNextNode();
90 ASSERT(startNode);
91 } else if (pos.offset() > 0)
92 splitTextNode(textNode, pos.offset());
93 } else if (pos.offset() > 0) {
94 startNode = startNode->traverseNextNode();
95 ASSERT(startNode);
98 // If there's nothing inside topBlockquote to move, we're finished.
99 if (!startNode->isDescendantOf(topBlockquote)) {
100 setEndingSelection(Selection(VisiblePosition(Position(startNode, 0))));
101 return;
104 // Build up list of ancestors in between the start node and the top blockquote.
105 Vector<Node*> ancestors;
106 for (Node* node = startNode->parentNode(); node != topBlockquote; node = node->parentNode())
107 ancestors.append(node);
109 // Insert a clone of the top blockquote after the break.
110 RefPtr<Node> clonedBlockquote = topBlockquote->cloneNode(false);
111 insertNodeAfter(clonedBlockquote.get(), breakNode.get());
113 // Clone startNode's ancestors into the cloned blockquote.
114 // On exiting this loop, clonedAncestor is the lowest ancestor
115 // that was cloned (i.e. the clone of either ancestors.last()
116 // or clonedBlockquote if ancestors is empty).
117 RefPtr<Node> clonedAncestor = clonedBlockquote;
118 for (size_t i = ancestors.size(); i != 0; --i) {
119 RefPtr<Node> clonedChild = ancestors[i - 1]->cloneNode(false); // shallow clone
120 // Preserve list item numbering in cloned lists.
121 if (clonedChild->isElementNode() && clonedChild->hasTagName(olTag)) {
122 Node* listChildNode = i > 1 ? ancestors[i - 2] : startNode;
123 // The first child of the cloned list might not be a list item element,
124 // find the first one so that we know where to start numbering.
125 while (listChildNode && !listChildNode->hasTagName(liTag))
126 listChildNode = listChildNode->nextSibling();
127 if (listChildNode && listChildNode->renderer())
128 setNodeAttribute(static_cast<Element*>(clonedChild.get()), startAttr, String::number(static_cast<RenderListItem*>(listChildNode->renderer())->value()));
131 appendNode(clonedChild.get(), clonedAncestor.get());
132 clonedAncestor = clonedChild;
135 // Move the startNode and its siblings.
136 Node *moveNode = startNode;
137 while (moveNode) {
138 Node *next = moveNode->nextSibling();
139 removeNode(moveNode);
140 appendNode(moveNode, clonedAncestor.get());
141 moveNode = next;
144 // Hold open startNode's original parent if we emptied it
145 if (!ancestors.isEmpty()) {
146 addBlockPlaceholderIfNeeded(ancestors.first());
148 // Split the tree up the ancestor chain until the topBlockquote
149 // Throughout this loop, clonedParent is the clone of ancestor's parent.
150 // This is so we can clone ancestor's siblings and place the clones
151 // into the clone corresponding to the ancestor's parent.
152 Node* ancestor;
153 Node* clonedParent;
154 for (ancestor = ancestors.first(), clonedParent = clonedAncestor->parentNode();
155 ancestor && ancestor != topBlockquote;
156 ancestor = ancestor->parentNode(), clonedParent = clonedParent->parentNode()) {
157 moveNode = ancestor->nextSibling();
158 while (moveNode) {
159 Node *next = moveNode->nextSibling();
160 removeNode(moveNode);
161 appendNode(moveNode, clonedParent);
162 moveNode = next;
167 // Make sure the cloned block quote renders.
168 addBlockPlaceholderIfNeeded(clonedBlockquote.get());
170 // Put the selection right before the break.
171 setEndingSelection(Selection(Position(breakNode.get(), 0), DOWNSTREAM));
172 rebalanceWhitespace();
175 } // namespace WebCore