| 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 |