OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. | |
3 * | |
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. | |
12 * | |
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. | |
24 */ | |
25 | |
26 #include "config.h" | |
27 #include "core/editing/ModifySelectionListLevel.h" | |
28 | |
29 #include "core/dom/Document.h" | |
30 #include "core/editing/FrameSelection.h" | |
31 #include "core/editing/htmlediting.h" | |
32 #include "core/html/HTMLElement.h" | |
33 #include "core/frame/Frame.h" | |
34 #include "core/rendering/RenderObject.h" | |
35 | |
36 namespace WebCore { | |
37 | |
38 ModifySelectionListLevelCommand::ModifySelectionListLevelCommand(Document& docum
ent) | |
39 : CompositeEditCommand(document) | |
40 { | |
41 } | |
42 | |
43 bool ModifySelectionListLevelCommand::preservesTypingStyle() const | |
44 { | |
45 return true; | |
46 } | |
47 | |
48 // This needs to be static so it can be called by canIncreaseSelectionListLevel
and canDecreaseSelectionListLevel | |
49 static bool getStartEndListChildren(const VisibleSelection& selection, Node*& st
art, Node*& end) | |
50 { | |
51 if (selection.isNone()) | |
52 return false; | |
53 | |
54 // start must be in a list child | |
55 Node* startListChild = enclosingListChild(selection.start().anchorNode()); | |
56 if (!startListChild) | |
57 return false; | |
58 | |
59 // end must be in a list child | |
60 Node* endListChild = selection.isRange() ? enclosingListChild(selection.end(
).anchorNode()) : startListChild; | |
61 if (!endListChild) | |
62 return false; | |
63 | |
64 // For a range selection we want the following behavior: | |
65 // - the start and end must be within the same overall list | |
66 // - the start must be at or above the level of the rest of the range | |
67 // - if the end is anywhere in a sublist lower than start, the whole su
blist gets moved | |
68 // In terms of this function, this means: | |
69 // - endListChild must start out being be a sibling of startListChild,
or be in a | |
70 // sublist of startListChild or a sibling | |
71 // - if endListChild is in a sublist of startListChild or a sibling, it
must be adjusted | |
72 // to be the ancestor that is startListChild or its sibling | |
73 while (startListChild->parentNode() != endListChild->parentNode()) { | |
74 endListChild = endListChild->parentNode(); | |
75 if (!endListChild) | |
76 return false; | |
77 } | |
78 | |
79 // if the selection ends on a list item with a sublist, include the entire s
ublist | |
80 if (endListChild->renderer()->isListItem()) { | |
81 RenderObject* r = endListChild->renderer()->nextSibling(); | |
82 if (r && isListElement(r->node())) | |
83 endListChild = r->node(); | |
84 } | |
85 | |
86 start = startListChild; | |
87 end = endListChild; | |
88 return true; | |
89 } | |
90 | |
91 void ModifySelectionListLevelCommand::insertSiblingNodeRangeBefore(Node* startNo
de, Node* endNode, Node* refNode) | |
92 { | |
93 Node* node = startNode; | |
94 while (1) { | |
95 Node* next = node->nextSibling(); | |
96 removeNode(node); | |
97 insertNodeBefore(node, refNode); | |
98 | |
99 if (node == endNode) | |
100 break; | |
101 | |
102 node = next; | |
103 } | |
104 } | |
105 | |
106 void ModifySelectionListLevelCommand::insertSiblingNodeRangeAfter(Node* startNod
e, Node* endNode, Node* refNode) | |
107 { | |
108 Node* node = startNode; | |
109 while (1) { | |
110 Node* next = node->nextSibling(); | |
111 removeNode(node); | |
112 insertNodeAfter(node, refNode); | |
113 | |
114 if (node == endNode) | |
115 break; | |
116 | |
117 refNode = node; | |
118 node = next; | |
119 } | |
120 } | |
121 | |
122 void ModifySelectionListLevelCommand::appendSiblingNodeRange(Node* startNode, No
de* endNode, Element* newParent) | |
123 { | |
124 Node* node = startNode; | |
125 while (1) { | |
126 Node* next = node->nextSibling(); | |
127 removeNode(node); | |
128 appendNode(node, newParent); | |
129 | |
130 if (node == endNode) | |
131 break; | |
132 | |
133 node = next; | |
134 } | |
135 } | |
136 | |
137 IncreaseSelectionListLevelCommand::IncreaseSelectionListLevelCommand(Document& d
ocument, Type listType) | |
138 : ModifySelectionListLevelCommand(document) | |
139 , m_listType(listType) | |
140 { | |
141 } | |
142 | |
143 // This needs to be static so it can be called by canIncreaseSelectionListLevel | |
144 static bool canIncreaseListLevel(const VisibleSelection& selection, Node*& start
, Node*& end) | |
145 { | |
146 if (!getStartEndListChildren(selection, start, end)) | |
147 return false; | |
148 | |
149 // start must not be the first child (because you need a prior one | |
150 // to increase relative to) | |
151 if (!start->renderer()->previousSibling()) | |
152 return false; | |
153 | |
154 return true; | |
155 } | |
156 | |
157 // For the moment, this is SPI and the only client (Mail.app) is satisfied. | |
158 // Here are two things to re-evaluate when making into API. | |
159 // 1. Currently, InheritedListType uses clones whereas OrderedList and | |
160 // UnorderedList create a new list node of the specified type. That is | |
161 // inconsistent wrt style. If that is not OK, here are some alternatives: | |
162 // - new nodes always inherit style (probably the best choice) | |
163 // - new nodes have always have no style | |
164 // - new nodes of the same type inherit style | |
165 // 2. Currently, the node we return may be either a pre-existing one or | |
166 // a new one. Is it confusing to return the pre-existing one without | |
167 // somehow indicating that it is not new? If so, here are some alternatives: | |
168 // - only return the list node if we created it | |
169 // - indicate whether the list node is new or pre-existing | |
170 // - (silly) client specifies whether to return pre-existing list nodes | |
171 void IncreaseSelectionListLevelCommand::doApply() | |
172 { | |
173 Node* startListChild; | |
174 Node* endListChild; | |
175 if (!canIncreaseListLevel(endingSelection(), startListChild, endListChild)) | |
176 return; | |
177 | |
178 Node* previousItem = startListChild->renderer()->previousSibling()->node(); | |
179 if (isListElement(previousItem)) { | |
180 // move nodes up into preceding list | |
181 appendSiblingNodeRange(startListChild, endListChild, toElement(previousI
tem)); | |
182 m_listElement = previousItem; | |
183 } else { | |
184 // create a sublist for the preceding element and move nodes there | |
185 RefPtr<Element> newParent; | |
186 switch (m_listType) { | |
187 case InheritedListType: | |
188 newParent = startListChild->parentElement(); | |
189 if (newParent) | |
190 newParent = newParent->cloneElementWithoutChildren(); | |
191 break; | |
192 case OrderedList: | |
193 newParent = createOrderedListElement(document()); | |
194 break; | |
195 case UnorderedList: | |
196 newParent = createUnorderedListElement(document()); | |
197 break; | |
198 } | |
199 insertNodeBefore(newParent, startListChild); | |
200 appendSiblingNodeRange(startListChild, endListChild, newParent.get()); | |
201 m_listElement = newParent.release(); | |
202 } | |
203 } | |
204 | |
205 bool IncreaseSelectionListLevelCommand::canIncreaseSelectionListLevel(Document&
document) | |
206 { | |
207 Node* startListChild; | |
208 Node* endListChild; | |
209 return canIncreaseListLevel(document.frame()->selection().selection(), start
ListChild, endListChild); | |
210 } | |
211 | |
212 PassRefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevel(D
ocument& document, Type type) | |
213 { | |
214 ASSERT(document.frame()); | |
215 RefPtr<IncreaseSelectionListLevelCommand> command = create(document, type); | |
216 command->apply(); | |
217 return command->m_listElement.release(); | |
218 } | |
219 | |
220 PassRefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevel(D
ocument& document) | |
221 { | |
222 return increaseSelectionListLevel(document, InheritedListType); | |
223 } | |
224 | |
225 PassRefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevelOr
dered(Document& document) | |
226 { | |
227 return increaseSelectionListLevel(document, OrderedList); | |
228 } | |
229 | |
230 PassRefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevelUn
ordered(Document& document) | |
231 { | |
232 return increaseSelectionListLevel(document, UnorderedList); | |
233 } | |
234 | |
235 DecreaseSelectionListLevelCommand::DecreaseSelectionListLevelCommand(Document& d
ocument) | |
236 : ModifySelectionListLevelCommand(document) | |
237 { | |
238 } | |
239 | |
240 // This needs to be static so it can be called by canDecreaseSelectionListLevel | |
241 static bool canDecreaseListLevel(const VisibleSelection& selection, Node*& start
, Node*& end) | |
242 { | |
243 if (!getStartEndListChildren(selection, start, end)) | |
244 return false; | |
245 | |
246 // there must be a destination list to move the items to | |
247 if (!isListElement(start->parentNode()->parentNode())) | |
248 return false; | |
249 | |
250 return true; | |
251 } | |
252 | |
253 void DecreaseSelectionListLevelCommand::doApply() | |
254 { | |
255 Node* startListChild; | |
256 Node* endListChild; | |
257 if (!canDecreaseListLevel(endingSelection(), startListChild, endListChild)) | |
258 return; | |
259 | |
260 Node* previousItem = startListChild->renderer()->previousSibling() ? startLi
stChild->renderer()->previousSibling()->node() : 0; | |
261 Node* nextItem = endListChild->renderer()->nextSibling() ? endListChild->ren
derer()->nextSibling()->node() : 0; | |
262 Element* listNode = startListChild->parentElement(); | |
263 | |
264 if (!previousItem) { | |
265 // at start of sublist, move the child(ren) to before the sublist | |
266 insertSiblingNodeRangeBefore(startListChild, endListChild, listNode); | |
267 // if that was the whole sublist we moved, remove the sublist node | |
268 if (!nextItem) | |
269 removeNode(listNode); | |
270 } else if (!nextItem) { | |
271 // at end of list, move the child(ren) to after the sublist | |
272 insertSiblingNodeRangeAfter(startListChild, endListChild, listNode); | |
273 } else if (listNode) { | |
274 // in the middle of list, split the list and move the children to the di
vide | |
275 splitElement(listNode, startListChild); | |
276 insertSiblingNodeRangeBefore(startListChild, endListChild, listNode); | |
277 } | |
278 } | |
279 | |
280 bool DecreaseSelectionListLevelCommand::canDecreaseSelectionListLevel(Document&
document) | |
281 { | |
282 Node* startListChild; | |
283 Node* endListChild; | |
284 return canDecreaseListLevel(document.frame()->selection().selection(), start
ListChild, endListChild); | |
285 } | |
286 | |
287 void DecreaseSelectionListLevelCommand::decreaseSelectionListLevel(Document& doc
ument) | |
288 { | |
289 ASSERT(document.frame()); | |
290 create(document)->apply(); | |
291 } | |
292 | |
293 } | |
OLD | NEW |