~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebCore/editing/ModifySelectionListLevel.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 "ModifySelectionListLevel.h"
 
28
 
 
29
#include "Document.h"
 
30
#include "Frame.h"
 
31
#include "FrameSelection.h"
 
32
#include "HTMLElement.h"
 
33
#include "RenderObject.h"
 
34
#include "htmlediting.h"
 
35
 
 
36
namespace WebCore {
 
37
 
 
38
ModifySelectionListLevelCommand::ModifySelectionListLevelCommand(Document* document) 
 
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*& start, 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 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();
 
75
        if (!endListChild)
 
76
            return false;
 
77
    }
 
78
    
 
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();
 
84
    }
 
85
 
 
86
    start = startListChild;
 
87
    end = endListChild;
 
88
    return true;
 
89
}
 
90
 
 
91
void ModifySelectionListLevelCommand::insertSiblingNodeRangeBefore(Node* startNode, 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* startNode, 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, Node* 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* document, 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, static_cast<Element*>(previousItem));
 
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(), startListChild, endListChild);
 
210
}
 
211
 
 
212
PassRefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevel(Document* document, Type type)
 
213
{
 
214
    ASSERT(document);
 
215
    ASSERT(document->frame());
 
216
    RefPtr<IncreaseSelectionListLevelCommand> command = create(document, type);
 
217
    command->apply();
 
218
    return command->m_listElement.release();
 
219
}
 
220
 
 
221
PassRefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevel(Document* document)
 
222
{
 
223
    return increaseSelectionListLevel(document, InheritedListType);
 
224
}
 
225
 
 
226
PassRefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevelOrdered(Document* document)
 
227
{
 
228
    return increaseSelectionListLevel(document, OrderedList);
 
229
}
 
230
 
 
231
PassRefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevelUnordered(Document* document)
 
232
{
 
233
    return increaseSelectionListLevel(document, UnorderedList);
 
234
}
 
235
 
 
236
DecreaseSelectionListLevelCommand::DecreaseSelectionListLevelCommand(Document* document) 
 
237
    : ModifySelectionListLevelCommand(document)
 
238
{
 
239
}
 
240
 
 
241
// This needs to be static so it can be called by canDecreaseSelectionListLevel
 
242
static bool canDecreaseListLevel(const VisibleSelection& selection, Node*& start, Node*& end)
 
243
{
 
244
    if (!getStartEndListChildren(selection, start, end))
 
245
        return false;
 
246
    
 
247
    // there must be a destination list to move the items to
 
248
    if (!isListElement(start->parentNode()->parentNode()))
 
249
        return false;
 
250
        
 
251
    return true;
 
252
}
 
253
 
 
254
void DecreaseSelectionListLevelCommand::doApply()
 
255
{
 
256
    Node* startListChild;
 
257
    Node* endListChild;
 
258
    if (!canDecreaseListLevel(endingSelection(), startListChild, endListChild))
 
259
        return;
 
260
 
 
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();
 
264
 
 
265
    if (!previousItem) {
 
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
 
269
        if (!nextItem)
 
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);
 
278
    }
 
279
}
 
280
 
 
281
bool DecreaseSelectionListLevelCommand::canDecreaseSelectionListLevel(Document* document)
 
282
{
 
283
    Node* startListChild;
 
284
    Node* endListChild;
 
285
    return canDecreaseListLevel(document->frame()->selection()->selection(), startListChild, endListChild);
 
286
}
 
287
 
 
288
void DecreaseSelectionListLevelCommand::decreaseSelectionListLevel(Document* document)
 
289
{
 
290
    ASSERT(document);
 
291
    ASSERT(document->frame());
 
292
    applyCommand(create(document));
 
293
}
 
294
 
 
295
}