2 * Copyright (C) 2006 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
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.
28 #include "InsertListCommand.h"
29 #include "DocumentFragment.h"
30 #include "htmlediting.h"
31 #include "HTMLElement.h"
32 #include "HTMLNames.h"
33 #include "visible_units.h"
37 using namespace HTMLNames;
39 Node* InsertListCommand::fixOrphanedListChild(Node* node)
41 RefPtr<Element> listElement = createUnorderedListElement(document());
42 insertNodeBefore(listElement.get(), node);
44 appendNode(node, listElement.get());
45 return listElement.get();
48 InsertListCommand::InsertListCommand(Document* document, Type type, const String& id)
49 : CompositeEditCommand(document), m_type(type), m_id(id), m_forceCreateList(false)
53 bool InsertListCommand::modifyRange()
55 ASSERT(endingSelection().isRange());
56 VisiblePosition visibleStart = endingSelection().visibleStart();
57 VisiblePosition visibleEnd = endingSelection().visibleEnd();
58 VisiblePosition startOfLastParagraph = startOfParagraph(visibleEnd);
60 if (startOfParagraph(visibleStart) == startOfLastParagraph)
63 Node* startList = enclosingList(visibleStart.deepEquivalent().node());
64 Node* endList = enclosingList(visibleEnd.deepEquivalent().node());
65 if (!startList || startList != endList)
66 m_forceCreateList = true;
68 setEndingSelection(visibleStart);
70 visibleStart = endingSelection().visibleStart();
71 VisiblePosition nextParagraph = endOfParagraph(visibleStart).next();
72 while (nextParagraph.isNotNull() && nextParagraph != startOfLastParagraph) {
73 setEndingSelection(nextParagraph);
75 nextParagraph = endOfParagraph(endingSelection().visibleStart()).next();
77 setEndingSelection(visibleEnd);
79 visibleEnd = endingSelection().visibleEnd();
80 setEndingSelection(Selection(visibleStart.deepEquivalent(), visibleEnd.deepEquivalent(), DOWNSTREAM));
81 m_forceCreateList = false;
86 void InsertListCommand::doApply()
88 if (endingSelection().isNone())
91 if (endingSelection().isRange() && modifyRange())
94 if (!endingSelection().rootEditableElement())
97 Node* selectionNode = endingSelection().start().node();
98 const QualifiedName listTag = (m_type == OrderedList) ? olTag : ulTag;
99 Node* listChildNode = enclosingListChild(selectionNode);
100 bool switchListType = false;
102 // Remove the list chlild.
103 Node* listNode = enclosingList(listChildNode);
105 listNode = fixOrphanedListChild(listChildNode);
106 if (!listNode->hasTagName(listTag))
107 // listChildNode will be removed from the list and a list of type m_type will be created.
108 switchListType = true;
110 Node* previousListChild;
111 VisiblePosition start;
113 if (listChildNode->hasTagName(liTag)) {
114 start = VisiblePosition(Position(listChildNode, 0));
115 end = VisiblePosition(Position(listChildNode, maxDeepOffset(listChildNode)));
116 nextListChild = listChildNode->nextSibling();
117 previousListChild = listChildNode->previousSibling();
119 // A paragraph is visually a list item minus a list marker. The paragraph will be moved.
120 start = startOfParagraph(endingSelection().visibleStart());
121 end = endOfParagraph(endingSelection().visibleEnd());
122 nextListChild = enclosingListChild(end.next().deepEquivalent().node());
123 ASSERT(nextListChild != listChildNode);
124 if (enclosingList(nextListChild) != listNode)
126 previousListChild = enclosingListChild(start.previous().deepEquivalent().node());
127 ASSERT(previousListChild != listChildNode);
128 if (enclosingList(previousListChild) != listNode)
129 previousListChild = 0;
131 // When removing a list, we must always create a placeholder to act as a point of insertion
132 // for the list content being removed.
133 RefPtr<Element> placeholder = createBreakElement(document());
134 if (nextListChild && previousListChild) {
135 splitElement(static_cast<Element *>(listNode), nextListChild);
136 insertNodeBefore(placeholder.get(), listNode);
137 } else if (nextListChild)
138 insertNodeBefore(placeholder.get(), listNode);
140 insertNodeAfter(placeholder.get(), listNode);
141 VisiblePosition insertionPoint = VisiblePosition(Position(placeholder.get(), 0));
142 moveParagraphs(start, end, insertionPoint, true);
144 if (!listChildNode || switchListType || m_forceCreateList) {
146 VisiblePosition start = startOfParagraph(endingSelection().visibleStart());
147 VisiblePosition end = endOfParagraph(endingSelection().visibleEnd());
149 // Check for adjoining lists.
150 VisiblePosition previousPosition = start.previous(true);
151 VisiblePosition nextPosition = end.next(true);
152 RefPtr<Element> listItemElement = createListItemElement(document());
153 Node* previousList = outermostEnclosingList(previousPosition.deepEquivalent().node());
154 Node* nextList = outermostEnclosingList(nextPosition.deepEquivalent().node());
155 if (previousList && !previousList->hasTagName(listTag))
157 if (nextList && !nextList->hasTagName(listTag))
159 // Stitch matching adjoining lists together.
161 appendNode(listItemElement.get(), previousList);
163 appendNode(listItemElement.get(), nextList);
166 RefPtr<Element> listElement = m_type == OrderedList ? createOrderedListElement(document()) : createUnorderedListElement(document());
167 static_cast<HTMLElement*>(listElement.get())->setId(m_id);
168 appendNode(listItemElement.get(), listElement.get());
169 insertNodeAt(listElement.get(), start.deepEquivalent().node(), start.deepEquivalent().offset());
171 moveParagraph(start, end, VisiblePosition(Position(listItemElement.get(), 0)), true);
172 if (nextList && previousList)
173 mergeIdenticalElements(static_cast<Element*>(previousList), static_cast<Element*>(nextList));