IndentOutdentCommand.cpp [plain text]
#include "config.h"
#include "Element.h"
#include "IndentOutdentCommand.h"
#include "InsertListCommand.h"
#include "Document.h"
#include "htmlediting.h"
#include "HTMLElement.h"
#include "HTMLNames.h"
#include "InsertLineBreakCommand.h"
#include "Range.h"
#include "SplitElementCommand.h"
#include "visible_units.h"
namespace WebCore {
using namespace HTMLNames;
IndentOutdentCommand::IndentOutdentCommand(Document* document, EIndentType typeOfAction, int marginInPixels)
: CompositeEditCommand(document), m_typeOfAction(typeOfAction), m_marginInPixels(marginInPixels)
{}
static Node* enclosingListOrBlockquote(Node* node)
{
if (!node)
return 0;
Node* root = (node->inDocument()) ? node->rootEditableElement() : highestAncestor(node);
ASSERT(root);
for (Node* n = node->parentNode(); n && (n == root || n->isAncestor(root)); n = n->parentNode())
if (n->hasTagName(ulTag) || n->hasTagName(olTag) || n->hasTagName(blockquoteTag))
return n;
return 0;
}
Node* IndentOutdentCommand::prepareBlockquoteLevelForInsertion(VisiblePosition& currentParagraph, Node** lastBlockquote)
{
int currentBlockquoteLevel = 0;
int lastBlockquoteLevel = 0;
Node* node = currentParagraph.deepEquivalent().node();
while ((node = enclosingNodeWithTag(node, blockquoteTag)))
currentBlockquoteLevel++;
node = *lastBlockquote;
while ((node = enclosingNodeWithTag(node, blockquoteTag)))
lastBlockquoteLevel++;
while (currentBlockquoteLevel > lastBlockquoteLevel) {
RefPtr<Node> newBlockquote = createElement(document(), "blockquote");
appendNode(newBlockquote.get(), *lastBlockquote);
*lastBlockquote = newBlockquote.get();
lastBlockquoteLevel++;
}
while (currentBlockquoteLevel < lastBlockquoteLevel) {
*lastBlockquote = enclosingNodeWithTag(*lastBlockquote, blockquoteTag);
lastBlockquoteLevel--;
}
RefPtr<Node> placeholder = createBreakElement(document());
if ((*lastBlockquote)->firstChild() && !(*lastBlockquote)->lastChild()->hasTagName(brTag)) {
RefPtr<Node> collapsedPlaceholder = createBreakElement(document());
appendNode(collapsedPlaceholder.get(), (*lastBlockquote));
}
appendNode(placeholder.get(), *lastBlockquote);
return placeholder.get();
}
Node* IndentOutdentCommand::splitTreeToNode(Node* start, Node* end, bool splitAncestor)
{
Node* node;
for (node = start; node && node->parent() != end; node = node->parent()) {
VisiblePosition positionInParent(Position(node->parent(), 0), DOWNSTREAM);
VisiblePosition positionInNode(Position(node, 0), DOWNSTREAM);
if (positionInParent != positionInNode) {
EditCommandPtr cmd(new SplitElementCommand(document(), static_cast<Element*>(node->parent()), node));
applyCommandToComposite(cmd);
}
}
if (splitAncestor)
return splitTreeToNode(end, end->parent());
return node;
}
void IndentOutdentCommand::indentRegion()
{
VisiblePosition startOfSelection = endingSelection().visibleStart();
VisiblePosition endOfSelection = endingSelection().visibleEnd();
ASSERT(!startOfSelection.isNull());
ASSERT(!endOfSelection.isNull());
Node* previousListNode = 0;
Node* newListNode = 0;
Node* newBlockquote = 0;
VisiblePosition endOfCurrentParagraph = endOfParagraph(startOfSelection);
VisiblePosition endAfterSelection = endOfParagraph(endOfParagraph(endOfSelection).next());
while (endOfCurrentParagraph != endAfterSelection) {
VisiblePosition endOfNextParagraph = endOfParagraph(endOfCurrentParagraph.next());
Node* listNode = enclosingList(endOfCurrentParagraph.deepEquivalent().node());
Node* insertionPoint;
if (listNode) {
RefPtr<Node> placeholder = createBreakElement(document());
insertionPoint = placeholder.get();
newBlockquote = 0;
RefPtr<Node> listItem = createListItemElement(document());
if (listNode == previousListNode) {
appendNode(listItem.get(), newListNode);
appendNode(placeholder.get(), listItem.get());
} else {
RefPtr<Node> clonedList = static_cast<Element*>(listNode)->cloneNode(false);
insertNodeBefore(clonedList.get(), enclosingListChild(endOfCurrentParagraph.deepEquivalent().node()));
appendNode(listItem.get(), clonedList.get());
appendNode(placeholder.get(), listItem.get());
newListNode = clonedList.get();
previousListNode = listNode;
}
} else if (newBlockquote)
insertionPoint = prepareBlockquoteLevelForInsertion(endOfCurrentParagraph, &newBlockquote);
else {
RefPtr<Node> blockquote = createElement(document(), "blockquote");
Node* startNode = startOfParagraph(endOfCurrentParagraph).deepEquivalent().node();
Node* startOfNewBlock = splitTreeToNode(startNode, startNode->rootEditableElement());
insertNodeBefore(blockquote.get(), startOfNewBlock);
newBlockquote = blockquote.get();
insertionPoint = prepareBlockquoteLevelForInsertion(endOfCurrentParagraph, &newBlockquote);
}
moveParagraph(startOfParagraph(endOfCurrentParagraph), endOfCurrentParagraph, VisiblePosition(Position(insertionPoint, 0)), true);
endOfCurrentParagraph = endOfNextParagraph;
}
}
void IndentOutdentCommand::outdentParagraph()
{
VisiblePosition visibleStartOfParagraph = startOfParagraph(endingSelection().visibleStart());
VisiblePosition visibleEndOfParagraph = endOfParagraph(visibleStartOfParagraph);
Node* enclosingNode = enclosingListOrBlockquote(visibleStartOfParagraph.deepEquivalent().node());
if (!enclosingNode)
return;
bool inList = false;
InsertListCommand::EListType typeOfList;
if (enclosingNode->hasTagName(olTag)) {
inList = true;
typeOfList = InsertListCommand::OrderedListType;
} else if (enclosingNode->hasTagName(ulTag)) {
inList = true;
typeOfList = InsertListCommand::UnorderedListType;
}
if (inList) {
EditCommandPtr cmd(new InsertListCommand(document(), typeOfList, ""));
applyCommandToComposite(cmd);
return;
}
VisiblePosition positionInEnclosingBlock = VisiblePosition(Position(enclosingNode, 0));
VisiblePosition startOfEnclosingBlock = startOfBlock(positionInEnclosingBlock);
VisiblePosition endOfEnclosingBlock = endOfBlock(positionInEnclosingBlock);
if (visibleStartOfParagraph == startOfEnclosingBlock &&
visibleEndOfParagraph == endOfEnclosingBlock) {
removeNodePreservingChildren(enclosingNode);
updateLayout();
return;
}
Node* enclosingBlockFlow = enclosingBlockFlowElement(visibleStartOfParagraph);
Node* splitBlockquoteNode = enclosingNode;
if (enclosingBlockFlow != enclosingNode)
splitBlockquoteNode = splitTreeToNode(enclosingBlockFlowElement(visibleStartOfParagraph), enclosingNode, true);
RefPtr<Node> placeholder = createBreakElement(document());
insertNodeBefore(placeholder.get(), splitBlockquoteNode);
moveParagraph(startOfParagraph(visibleStartOfParagraph), endOfParagraph(visibleEndOfParagraph), VisiblePosition(Position(placeholder.get(), 0)), true);
}
void IndentOutdentCommand::outdentRegion()
{
VisiblePosition startOfSelection = endingSelection().visibleStart();
VisiblePosition endOfSelection = endingSelection().visibleEnd();
VisiblePosition endOfLastParagraph = endOfParagraph(endOfSelection);
ASSERT(!startOfSelection.isNull());
ASSERT(!endOfSelection.isNull());
if (endOfParagraph(startOfSelection) == endOfLastParagraph) {
outdentParagraph();
return;
}
Position originalSelectionEnd = endingSelection().end();
setEndingSelection(endingSelection().visibleStart());
outdentParagraph();
Position originalSelectionStart = endingSelection().start();
VisiblePosition endOfCurrentParagraph = endOfParagraph(endOfParagraph(endingSelection().visibleStart()).next(true));
VisiblePosition endAfterSelection = endOfParagraph(endOfParagraph(endOfSelection).next());
while (endOfCurrentParagraph != endAfterSelection) {
VisiblePosition endOfNextParagraph = endOfParagraph(endOfCurrentParagraph.next());
if (endOfCurrentParagraph == endOfLastParagraph)
setEndingSelection(originalSelectionEnd, DOWNSTREAM);
else
setEndingSelection(endOfCurrentParagraph);
outdentParagraph();
endOfCurrentParagraph = endOfNextParagraph;
}
setEndingSelection(Selection(originalSelectionStart, endingSelection().end(), DOWNSTREAM));
}
void IndentOutdentCommand::doApply()
{
if (endingSelection().isNone())
return;
if (!endingSelection().rootEditableElement())
return;
if (m_typeOfAction == Indent)
indentRegion();
else
outdentRegion();
}
}