InsertListCommand.cpp [plain text]
#include "config.h"
#include "Element.h"
#include "InsertListCommand.h"
#include "DocumentFragment.h"
#include "htmlediting.h"
#include "HTMLElement.h"
#include "HTMLNames.h"
#include "TextIterator.h"
#include "visible_units.h"
namespace WebCore {
using namespace HTMLNames;
PassRefPtr<HTMLElement> InsertListCommand::insertList(Document* document, Type type)
{
RefPtr<InsertListCommand> insertCommand = new InsertListCommand(document, type);
insertCommand->apply();
return insertCommand->m_listElement;
}
HTMLElement* InsertListCommand::fixOrphanedListChild(Node* node)
{
RefPtr<HTMLElement> listElement = createUnorderedListElement(document());
insertNodeBefore(listElement, node);
removeNode(node);
appendNode(node, listElement);
m_listElement = listElement;
return listElement.get();
}
InsertListCommand::InsertListCommand(Document* document, Type type)
: CompositeEditCommand(document), m_type(type), m_forceCreateList(false)
{
}
bool InsertListCommand::modifyRange()
{
VisibleSelection selection = selectionForParagraphIteration(endingSelection());
ASSERT(selection.isRange());
VisiblePosition startOfSelection = selection.visibleStart();
VisiblePosition endOfSelection = selection.visibleEnd();
VisiblePosition startOfLastParagraph = startOfParagraph(endOfSelection);
if (startOfParagraph(startOfSelection) == startOfLastParagraph)
return false;
Node* startList = enclosingList(startOfSelection.deepEquivalent().node());
Node* endList = enclosingList(endOfSelection.deepEquivalent().node());
if (!startList || startList != endList)
m_forceCreateList = true;
setEndingSelection(startOfSelection);
doApply();
startOfSelection = endingSelection().visibleStart();
VisiblePosition startOfCurrentParagraph = startOfNextParagraph(startOfSelection);
while (startOfCurrentParagraph != startOfLastParagraph) {
if (!startOfLastParagraph.deepEquivalent().node()->inDocument())
return true;
setEndingSelection(startOfCurrentParagraph);
doApply();
startOfCurrentParagraph = startOfNextParagraph(endingSelection().visibleStart());
}
setEndingSelection(endOfSelection);
doApply();
endOfSelection = endingSelection().visibleEnd();
setEndingSelection(VisibleSelection(startOfSelection, endOfSelection));
m_forceCreateList = false;
return true;
}
void InsertListCommand::doApply()
{
if (endingSelection().isNone())
return;
if (!endingSelection().rootEditableElement())
return;
VisiblePosition visibleEnd = endingSelection().visibleEnd();
VisiblePosition visibleStart = endingSelection().visibleStart();
if (visibleEnd != visibleStart && isStartOfParagraph(visibleEnd))
setEndingSelection(VisibleSelection(visibleStart, visibleEnd.previous(true)));
if (endingSelection().isRange() && modifyRange())
return;
Node* selectionNode = endingSelection().start().node();
const QualifiedName listTag = (m_type == OrderedList) ? olTag : ulTag;
Node* listChildNode = enclosingListChild(selectionNode);
bool switchListType = false;
if (listChildNode) {
HTMLElement* listNode = enclosingList(listChildNode);
if (!listNode)
listNode = fixOrphanedListChild(listChildNode);
if (!listNode->hasTagName(listTag))
switchListType = true;
Node* nextListChild;
Node* previousListChild;
VisiblePosition start;
VisiblePosition end;
if (listChildNode->hasTagName(liTag)) {
start = firstDeepEditingPositionForNode(listChildNode);
end = lastDeepEditingPositionForNode(listChildNode);
nextListChild = listChildNode->nextSibling();
previousListChild = listChildNode->previousSibling();
} else {
start = startOfParagraph(endingSelection().visibleStart());
end = endOfParagraph(endingSelection().visibleEnd());
nextListChild = enclosingListChild(end.next().deepEquivalent().node());
ASSERT(nextListChild != listChildNode);
if (enclosingList(nextListChild) != listNode)
nextListChild = 0;
previousListChild = enclosingListChild(start.previous().deepEquivalent().node());
ASSERT(previousListChild != listChildNode);
if (enclosingList(previousListChild) != listNode)
previousListChild = 0;
}
RefPtr<Element> placeholder = createBreakElement(document());
RefPtr<Element> nodeToInsert = placeholder;
if (enclosingList(listNode)) {
nodeToInsert = createListItemElement(document());
appendNode(placeholder, nodeToInsert);
}
if (nextListChild && previousListChild) {
splitElement(listNode, splitTreeToNode(nextListChild, listNode));
insertNodeBefore(nodeToInsert, listNode);
} else if (nextListChild || listChildNode->parentNode() != listNode) {
if (listChildNode->parentNode() != listNode)
splitElement(listNode, splitTreeToNode(listChildNode, listNode).get());
insertNodeBefore(nodeToInsert, listNode);
} else
insertNodeAfter(nodeToInsert, listNode);
VisiblePosition insertionPoint = VisiblePosition(Position(placeholder.get(), 0));
moveParagraphs(start, end, insertionPoint, true);
}
if (!listChildNode || switchListType || m_forceCreateList) {
VisiblePosition originalStart = endingSelection().visibleStart();
VisiblePosition start = startOfParagraph(originalStart);
VisiblePosition end = endOfParagraph(endingSelection().visibleEnd());
VisiblePosition previousPosition = start.previous(true);
VisiblePosition nextPosition = end.next(true);
RefPtr<HTMLElement> listItemElement = createListItemElement(document());
RefPtr<HTMLElement> placeholder = createBreakElement(document());
appendNode(placeholder, listItemElement);
Element* previousList = outermostEnclosingList(previousPosition.deepEquivalent().node());
Element* nextList = outermostEnclosingList(nextPosition.deepEquivalent().node());
Node* startNode = start.deepEquivalent().node();
Node* previousCell = enclosingTableCell(previousPosition.deepEquivalent());
Node* nextCell = enclosingTableCell(nextPosition.deepEquivalent());
Node* currentCell = enclosingTableCell(start.deepEquivalent());
if (previousList && (!previousList->hasTagName(listTag) || startNode->isDescendantOf(previousList) || previousCell != currentCell))
previousList = 0;
if (nextList && (!nextList->hasTagName(listTag) || startNode->isDescendantOf(nextList) || nextCell != currentCell))
nextList = 0;
if (previousList)
appendNode(listItemElement, previousList);
else if (nextList)
insertNodeAt(listItemElement, Position(nextList, 0));
else {
RefPtr<HTMLElement> listElement = m_type == OrderedList ? createOrderedListElement(document()) : createUnorderedListElement(document());
m_listElement = listElement;
appendNode(listItemElement, listElement);
if (start == end && isBlock(start.deepEquivalent().node())) {
RefPtr<Node> placeholder = insertBlockPlaceholder(start.deepEquivalent());
start = VisiblePosition(Position(placeholder.get(), 0));
end = start;
}
Position insertionPos(start.deepEquivalent().upstream());
Node* listChild = enclosingListChild(insertionPos.node());
if (listChild && listChild->hasTagName(liTag))
insertionPos = positionInParentBeforeNode(listChild);
insertNodeAt(listElement, insertionPos);
if (insertionPos == start.deepEquivalent())
start = startOfParagraph(originalStart);
previousList = outermostEnclosingList(previousPosition.deepEquivalent().node(), enclosingList(listElement.get()));
nextList = outermostEnclosingList(nextPosition.deepEquivalent().node(), enclosingList(listElement.get()));
}
moveParagraph(start, end, VisiblePosition(Position(placeholder.get(), 0)), true);
if (m_listElement) {
if (canMergeLists(previousList, m_listElement.get()))
mergeIdenticalElements(previousList, m_listElement.get());
if (canMergeLists(m_listElement.get(), nextList))
mergeIdenticalElements(m_listElement.get(), nextList);
} else if (canMergeLists(nextList, previousList))
mergeIdenticalElements(previousList, nextList);
}
}
}