2
* Copyright (C) 2006, 2008 Apple 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.
27
#include "ModifySelectionListLevel.h"
31
#include "FrameSelection.h"
32
#include "HTMLElement.h"
33
#include "RenderObject.h"
34
#include "htmlediting.h"
38
ModifySelectionListLevelCommand::ModifySelectionListLevelCommand(Document* document)
39
: CompositeEditCommand(document)
43
bool ModifySelectionListLevelCommand::preservesTypingStyle() const
48
// This needs to be static so it can be called by canIncreaseSelectionListLevel and canDecreaseSelectionListLevel
49
static bool getStartEndListChildren(const VisibleSelection& selection, Node*& start, Node*& end)
51
if (selection.isNone())
54
// start must be in a list child
55
Node* startListChild = enclosingListChild(selection.start().anchorNode());
59
// end must be in a list child
60
Node* endListChild = selection.isRange() ? enclosingListChild(selection.end().anchorNode()) : startListChild;
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 sublist 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();
79
// if the selection ends on a list item with a sublist, include the entire sublist
80
if (endListChild->renderer()->isListItem()) {
81
RenderObject* r = endListChild->renderer()->nextSibling();
82
if (r && isListElement(r->node()))
83
endListChild = r->node();
86
start = startListChild;
91
void ModifySelectionListLevelCommand::insertSiblingNodeRangeBefore(Node* startNode, Node* endNode, Node* refNode)
93
Node* node = startNode;
95
Node* next = node->nextSibling();
97
insertNodeBefore(node, refNode);
106
void ModifySelectionListLevelCommand::insertSiblingNodeRangeAfter(Node* startNode, Node* endNode, Node* refNode)
108
Node* node = startNode;
110
Node* next = node->nextSibling();
112
insertNodeAfter(node, refNode);
122
void ModifySelectionListLevelCommand::appendSiblingNodeRange(Node* startNode, Node* endNode, Element* newParent)
124
Node* node = startNode;
126
Node* next = node->nextSibling();
128
appendNode(node, newParent);
137
IncreaseSelectionListLevelCommand::IncreaseSelectionListLevelCommand(Document* document, Type listType)
138
: ModifySelectionListLevelCommand(document)
139
, m_listType(listType)
143
// This needs to be static so it can be called by canIncreaseSelectionListLevel
144
static bool canIncreaseListLevel(const VisibleSelection& selection, Node*& start, Node*& end)
146
if (!getStartEndListChildren(selection, start, end))
149
// start must not be the first child (because you need a prior one
150
// to increase relative to)
151
if (!start->renderer()->previousSibling())
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()
173
Node* startListChild;
175
if (!canIncreaseListLevel(endingSelection(), startListChild, endListChild))
178
Node* previousItem = startListChild->renderer()->previousSibling()->node();
179
if (isListElement(previousItem)) {
180
// move nodes up into preceding list
181
appendSiblingNodeRange(startListChild, endListChild, static_cast<Element*>(previousItem));
182
m_listElement = previousItem;
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();
190
newParent = newParent->cloneElementWithoutChildren();
193
newParent = createOrderedListElement(document());
196
newParent = createUnorderedListElement(document());
199
insertNodeBefore(newParent, startListChild);
200
appendSiblingNodeRange(startListChild, endListChild, newParent.get());
201
m_listElement = newParent.release();
205
bool IncreaseSelectionListLevelCommand::canIncreaseSelectionListLevel(Document* document)
207
Node* startListChild;
209
return canIncreaseListLevel(document->frame()->selection()->selection(), startListChild, endListChild);
212
PassRefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevel(Document* document, Type type)
215
ASSERT(document->frame());
216
RefPtr<IncreaseSelectionListLevelCommand> command = create(document, type);
218
return command->m_listElement.release();
221
PassRefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevel(Document* document)
223
return increaseSelectionListLevel(document, InheritedListType);
226
PassRefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevelOrdered(Document* document)
228
return increaseSelectionListLevel(document, OrderedList);
231
PassRefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevelUnordered(Document* document)
233
return increaseSelectionListLevel(document, UnorderedList);
236
DecreaseSelectionListLevelCommand::DecreaseSelectionListLevelCommand(Document* document)
237
: ModifySelectionListLevelCommand(document)
241
// This needs to be static so it can be called by canDecreaseSelectionListLevel
242
static bool canDecreaseListLevel(const VisibleSelection& selection, Node*& start, Node*& end)
244
if (!getStartEndListChildren(selection, start, end))
247
// there must be a destination list to move the items to
248
if (!isListElement(start->parentNode()->parentNode()))
254
void DecreaseSelectionListLevelCommand::doApply()
256
Node* startListChild;
258
if (!canDecreaseListLevel(endingSelection(), startListChild, endListChild))
261
Node* previousItem = startListChild->renderer()->previousSibling() ? startListChild->renderer()->previousSibling()->node() : 0;
262
Node* nextItem = endListChild->renderer()->nextSibling() ? endListChild->renderer()->nextSibling()->node() : 0;
263
Element* listNode = startListChild->parentElement();
266
// at start of sublist, move the child(ren) to before the sublist
267
insertSiblingNodeRangeBefore(startListChild, endListChild, listNode);
268
// if that was the whole sublist we moved, remove the sublist node
270
removeNode(listNode);
271
} else if (!nextItem) {
272
// at end of list, move the child(ren) to after the sublist
273
insertSiblingNodeRangeAfter(startListChild, endListChild, listNode);
274
} else if (listNode) {
275
// in the middle of list, split the list and move the children to the divide
276
splitElement(listNode, startListChild);
277
insertSiblingNodeRangeBefore(startListChild, endListChild, listNode);
281
bool DecreaseSelectionListLevelCommand::canDecreaseSelectionListLevel(Document* document)
283
Node* startListChild;
285
return canDecreaseListLevel(document->frame()->selection()->selection(), startListChild, endListChild);
288
void DecreaseSelectionListLevelCommand::decreaseSelectionListLevel(Document* document)
291
ASSERT(document->frame());
292
applyCommand(create(document));