2009-03-17 Darin Adler <darin@apple.com>
[WebKit-https.git] / WebCore / editing / EditCommand.cpp
1 /*
2  * Copyright (C) 2005, 2006, 2007 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 "EditCommand.h"
28
29 #include "CompositeEditCommand.h"
30 #include "CSSComputedStyleDeclaration.h"
31 #include "CSSMutableStyleDeclaration.h"
32 #include "DeleteButtonController.h"
33 #include "Document.h"
34 #include "Editor.h"
35 #include "Element.h"
36 #include "EventNames.h"
37 #include "Frame.h"
38 #include "SelectionController.h"
39 #include "VisiblePosition.h"
40 #include "htmlediting.h"
41
42 namespace WebCore {
43
44 EditCommand::EditCommand(Document* document) 
45     : m_document(document)
46     , m_parent(0)
47 {
48     ASSERT(m_document);
49     ASSERT(m_document->frame());
50     setStartingSelection(avoidIntersectionWithNode(m_document->frame()->selection()->selection(), m_document->frame()->editor()->deleteButtonController()->containerElement()));
51     setEndingSelection(m_startingSelection);
52 }
53
54 EditCommand::~EditCommand()
55 {
56 }
57
58 void EditCommand::apply()
59 {
60     ASSERT(m_document);
61     ASSERT(m_document->frame());
62  
63     Frame* frame = m_document->frame();
64     
65     if (!m_parent) {
66         if (!endingSelection().isContentRichlyEditable()) {
67             switch (editingAction()) {
68                 case EditActionTyping:
69                 case EditActionPaste:
70                 case EditActionDrag:
71                 case EditActionSetWritingDirection:
72                 case EditActionCut:
73                 case EditActionUnspecified:
74                     break;
75                 default:
76                     ASSERT_NOT_REACHED();
77                     return;
78             }
79         }
80     }
81     
82     // Changes to the document may have been made since the last editing operation that 
83     // require a layout, as in <rdar://problem/5658603>.  Low level operations, like 
84     // RemoveNodeCommand, don't require a layout because the high level operations that 
85     // use them perform one if one is necessary (like for the creation of VisiblePositions).
86     if (!m_parent)
87         updateLayout();
88
89     DeleteButtonController* deleteButtonController = frame->editor()->deleteButtonController();
90     deleteButtonController->disable();
91     doApply();
92     deleteButtonController->enable();
93
94     if (!m_parent) {
95         updateLayout();
96         // Only need to call appliedEditing for top-level commands, and TypingCommands do it on their
97         // own (see TypingCommand::typingAddedToOpenCommand).
98         if (!isTypingCommand())
99             frame->editor()->appliedEditing(this);
100     }
101 }
102
103 void EditCommand::unapply()
104 {
105     ASSERT(m_document);
106     ASSERT(m_document->frame());
107  
108     Frame* frame = m_document->frame();
109     
110     // Changes to the document may have been made since the last editing operation that 
111     // require a layout, as in <rdar://problem/5658603>.  Low level operations, like 
112     // RemoveNodeCommand, don't require a layout because the high level operations that 
113     // use them perform one if one is necessary (like for the creation of VisiblePositions).
114     if (!m_parent)
115         updateLayout();
116     
117     DeleteButtonController* deleteButtonController = frame->editor()->deleteButtonController();
118     deleteButtonController->disable();
119     doUnapply();
120     deleteButtonController->enable();
121
122     if (!m_parent) {
123         updateLayout();
124         frame->editor()->unappliedEditing(this);
125     }
126 }
127
128 void EditCommand::reapply()
129 {
130     ASSERT(m_document);
131     ASSERT(m_document->frame());
132  
133     Frame* frame = m_document->frame();
134     
135     // Changes to the document may have been made since the last editing operation that 
136     // require a layout, as in <rdar://problem/5658603>.  Low level operations, like 
137     // RemoveNodeCommand, don't require a layout because the high level operations that 
138     // use them perform one if one is necessary (like for the creation of VisiblePositions).
139     if (!m_parent)
140         updateLayout();
141
142     DeleteButtonController* deleteButtonController = frame->editor()->deleteButtonController();
143     deleteButtonController->disable();
144     doReapply();
145     deleteButtonController->enable();
146
147     if (!m_parent) {
148         updateLayout();
149         frame->editor()->reappliedEditing(this);
150     }
151 }
152
153 void EditCommand::doReapply()
154 {
155     doApply();
156 }
157
158 EditAction EditCommand::editingAction() const
159 {
160     return EditActionUnspecified;
161 }
162
163 void EditCommand::setStartingSelection(const VisibleSelection& s)
164 {
165     Element* root = s.rootEditableElement();
166     for (EditCommand* cmd = this; ; cmd = cmd->m_parent) {
167         cmd->m_startingSelection = s;
168         cmd->m_startingRootEditableElement = root;
169         if (!cmd->m_parent || cmd->m_parent->isFirstCommand(cmd))
170             break;
171     }
172 }
173
174 void EditCommand::setEndingSelection(const VisibleSelection &s)
175 {
176     Element* root = s.rootEditableElement();
177     for (EditCommand* cmd = this; cmd; cmd = cmd->m_parent) {
178         cmd->m_endingSelection = s;
179         cmd->m_endingRootEditableElement = root;
180     }
181 }
182
183 bool EditCommand::preservesTypingStyle() const
184 {
185     return false;
186 }
187
188 bool EditCommand::isInsertTextCommand() const
189 {
190     return false;
191 }
192
193 bool EditCommand::isTypingCommand() const
194 {
195     return false;
196 }
197
198 PassRefPtr<CSSMutableStyleDeclaration> EditCommand::styleAtPosition(const Position &pos)
199 {
200     RefPtr<CSSMutableStyleDeclaration> style = positionBeforeTabSpan(pos).computedStyle()->copyInheritableProperties();
201  
202     // FIXME: It seems misleading to also include the typing style when returning the style at some arbitrary
203     // position in the document.
204     CSSMutableStyleDeclaration* typingStyle = document()->frame()->typingStyle();
205     if (typingStyle)
206         style->merge(typingStyle);
207
208     return style.release();
209 }
210
211 void EditCommand::updateLayout() const
212 {
213     document()->updateLayoutIgnorePendingStylesheets();
214 }
215
216 void EditCommand::setParent(CompositeEditCommand* parent)
217 {
218     ASSERT(parent);
219     ASSERT(!m_parent);
220     m_parent = parent;
221     m_startingSelection = parent->m_endingSelection;
222     m_endingSelection = parent->m_endingSelection;
223     m_startingRootEditableElement = parent->m_endingRootEditableElement;
224     m_endingRootEditableElement = parent->m_endingRootEditableElement;
225 }
226
227 void applyCommand(PassRefPtr<EditCommand> command)
228 {
229     command->apply();
230 }
231
232 } // namespace WebCore