cca3005e8c48983cbee8f5b57f5210039669b871
[WebKit-https.git] / WebCore / khtml / editing / edit_command.cpp
1 /*
2  * Copyright (C) 2005 Apple Computer, 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 "edit_command.h"
27
28 #include "xml/dom_position.h"
29 #include "xml/dom_docimpl.h"
30 #include "css/css_valueimpl.h"
31 #include "css/css_computedstyle.h"
32
33 #if APPLE_CHANGES
34 #include "KWQAssertions.h"
35 #include "KWQLogging.h"
36 #include "KWQKHTMLPart.h"
37 #else
38 #define ASSERT(assertion) ((void)0)
39 #define ASSERT_WITH_MESSAGE(assertion, formatAndArgs...) ((void)0)
40 #define ASSERT_NOT_REACHED() ((void)0)
41 #define LOG(channel, formatAndArgs...) ((void)0)
42 #define ERROR(formatAndArgs...) ((void)0)
43 #define ASSERT(assertion) assert(assertion)
44 #if LOG_DISABLED
45 #define debugPosition(a,b) ((void)0)
46 #define debugNode(a,b) ((void)0)
47 #endif
48 #endif
49
50 using DOM::DocumentImpl;
51 using DOM::Position;
52 using DOM::CSSMutableStyleDeclarationImpl;
53 using DOM::CSSComputedStyleDeclarationImpl;
54
55 #define IF_IMPL_NULL_RETURN_ARG(arg) do { \
56         if (isNull()) { return arg; } \
57     } while (0)
58         
59 #define IF_IMPL_NULL_RETURN do { \
60         if (isNull()) { return; } \
61     } while (0)
62
63 namespace khtml {
64
65 //------------------------------------------------------------------------------------------
66 // EditCommandPtr
67
68 EditCommandPtr::EditCommandPtr()
69 {
70 }
71
72 EditCommandPtr::EditCommandPtr(EditCommand *impl) : SharedPtr<EditCommand>(impl)
73 {
74 }
75
76 EditCommandPtr::EditCommandPtr(const EditCommandPtr &o) : SharedPtr<EditCommand>(o)
77 {
78 }
79
80 EditCommandPtr::~EditCommandPtr()
81 {
82 }
83
84 EditCommandPtr &EditCommandPtr::operator=(const EditCommandPtr &c)
85 {
86     static_cast<SharedPtr<EditCommand> &>(*this) = c;
87     return *this;
88 }
89
90 bool EditCommandPtr::isCompositeStep() const
91 {
92     IF_IMPL_NULL_RETURN_ARG(false);        
93     return get()->isCompositeStep();
94 }
95
96 bool EditCommandPtr::isInsertTextCommand() const
97 {
98     IF_IMPL_NULL_RETURN_ARG(false);        
99     return get()->isInsertTextCommand();
100 }
101
102 bool EditCommandPtr::isTypingCommand() const
103 {
104     IF_IMPL_NULL_RETURN_ARG(false);        
105     return get()->isTypingCommand();
106 }
107
108 void EditCommandPtr::apply() const
109 {
110     IF_IMPL_NULL_RETURN;
111     get()->apply();
112 }
113
114 void EditCommandPtr::unapply() const
115 {
116     IF_IMPL_NULL_RETURN;
117     get()->unapply();
118 }
119
120 void EditCommandPtr::reapply() const
121 {
122     IF_IMPL_NULL_RETURN;
123     get()->reapply();
124 }
125
126 EditAction EditCommandPtr::editingAction() const
127 {
128     IF_IMPL_NULL_RETURN_ARG(EditActionUnspecified);
129     return get()->editingAction();
130 }
131
132 DocumentImpl * const EditCommandPtr::document() const
133 {
134     IF_IMPL_NULL_RETURN_ARG(0);
135     return get()->document();
136 }
137
138 Selection EditCommandPtr::startingSelection() const
139 {
140     IF_IMPL_NULL_RETURN_ARG(Selection());
141     return get()->startingSelection();
142 }
143
144 Selection EditCommandPtr::endingSelection() const
145 {
146     IF_IMPL_NULL_RETURN_ARG(Selection());
147     return get()->endingSelection();
148 }
149
150 void EditCommandPtr::setStartingSelection(const Selection &s) const
151 {
152     IF_IMPL_NULL_RETURN;
153     get()->setStartingSelection(s);
154 }
155
156 void EditCommandPtr::setStartingSelection(const VisiblePosition &p) const
157 {
158     IF_IMPL_NULL_RETURN;
159     get()->setStartingSelection(p);
160 }
161
162 void EditCommandPtr::setStartingSelection(const Position &p, EAffinity affinity) const
163 {
164     IF_IMPL_NULL_RETURN;
165     Selection s = Selection(p, affinity);
166     get()->setStartingSelection(s);
167 }
168
169 void EditCommandPtr::setEndingSelection(const Selection &s) const
170 {
171     IF_IMPL_NULL_RETURN;
172     get()->setEndingSelection(s);
173 }
174
175 void EditCommandPtr::setEndingSelection(const VisiblePosition &p) const
176 {
177     IF_IMPL_NULL_RETURN;
178     get()->setEndingSelection(p);
179 }
180
181 void EditCommandPtr::setEndingSelection(const Position &p, EAffinity affinity) const
182 {
183     IF_IMPL_NULL_RETURN;
184     Selection s = Selection(p, affinity);
185     get()->setEndingSelection(s);
186 }
187
188 CSSMutableStyleDeclarationImpl *EditCommandPtr::typingStyle() const
189 {
190     IF_IMPL_NULL_RETURN_ARG(0);
191     return get()->typingStyle();
192 }
193
194 void EditCommandPtr::setTypingStyle(CSSMutableStyleDeclarationImpl *style) const
195 {
196     IF_IMPL_NULL_RETURN;
197     get()->setTypingStyle(style);
198 }
199
200 EditCommandPtr EditCommandPtr::parent() const
201 {
202     IF_IMPL_NULL_RETURN_ARG(0);
203     return get()->parent();
204 }
205
206 void EditCommandPtr::setParent(const EditCommandPtr &cmd) const
207 {
208     IF_IMPL_NULL_RETURN;
209     get()->setParent(cmd.get());
210 }
211
212 EditCommandPtr &EditCommandPtr::emptyCommand()
213 {
214     static EditCommandPtr m_emptyCommand;
215     return m_emptyCommand;
216 }
217
218 //------------------------------------------------------------------------------------------
219 // EditCommand
220
221 EditCommand::EditCommand(DocumentImpl *document) 
222     : m_document(document), m_state(NotApplied), m_typingStyle(0), m_parent(0)
223 {
224     ASSERT(m_document);
225     ASSERT(m_document->part());
226     m_document->ref();
227     m_startingSelection = m_document->part()->selection();
228     m_endingSelection = m_startingSelection;
229
230     m_document->part()->setSelection(Selection(), false, true);
231 }
232
233 EditCommand::~EditCommand()
234 {
235     ASSERT(m_document);
236     m_document->deref();
237     if (m_typingStyle)
238         m_typingStyle->deref();
239 }
240
241 void EditCommand::apply()
242 {
243     ASSERT(m_document);
244     ASSERT(m_document->part());
245     ASSERT(state() == NotApplied);
246  
247     KHTMLPart *part = m_document->part();
248
249     ASSERT(part->selection().isNone());
250
251     doApply();
252     
253     m_state = Applied;
254
255     // FIXME: Improve typing style.
256     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
257     if (!preservesTypingStyle())
258         setTypingStyle(0);
259
260     if (!isCompositeStep()) {
261         document()->updateLayout();
262         EditCommandPtr cmd(this);
263         part->appliedEditing(cmd);
264     }
265 }
266
267 void EditCommand::unapply()
268 {
269     ASSERT(m_document);
270     ASSERT(m_document->part());
271     ASSERT(state() == Applied);
272
273     bool topLevel = !isCompositeStep();
274  
275     KHTMLPart *part = m_document->part();
276
277     if (topLevel) {
278         part->setSelection(Selection(), false, true);
279     }
280     ASSERT(part->selection().isNone());
281     
282     doUnapply();
283     
284     m_state = NotApplied;
285
286     if (topLevel) {
287         document()->updateLayout();
288         EditCommandPtr cmd(this);
289         part->unappliedEditing(cmd);
290     }
291 }
292
293 void EditCommand::reapply()
294 {
295     ASSERT(m_document);
296     ASSERT(m_document->part());
297     ASSERT(state() == NotApplied);
298     
299     bool topLevel = !isCompositeStep();
300  
301     KHTMLPart *part = m_document->part();
302
303     if (topLevel) {
304         part->setSelection(Selection(), false, true);
305     }
306     ASSERT(part->selection().isNone());
307     
308     doReapply();
309     
310     m_state = Applied;
311
312     if (topLevel) {
313         document()->updateLayout();
314         EditCommandPtr cmd(this);
315         part->reappliedEditing(cmd);
316     }
317 }
318
319 void EditCommand::doReapply()
320 {
321     doApply();
322 }
323
324 EditAction EditCommand::editingAction() const
325 {
326     return EditActionUnspecified;
327 }
328
329 void EditCommand::setStartingSelection(const Selection &s)
330 {
331     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent)
332         cmd->m_startingSelection = s;
333 }
334
335 void EditCommand::setStartingSelection(const VisiblePosition &p)
336 {
337     Selection s = Selection(p);
338     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent)
339         cmd->m_startingSelection = s;
340 }
341
342 void EditCommand::setStartingSelection(const Position &p, EAffinity affinity)
343 {
344     Selection s = Selection(p, affinity);
345     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent)
346         cmd->m_startingSelection = s;
347 }
348
349 void EditCommand::setEndingSelection(const Selection &s)
350 {
351     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent)
352         cmd->m_endingSelection = s;
353 }
354
355 void EditCommand::setEndingSelection(const VisiblePosition &p)
356 {
357     Selection s = Selection(p);
358     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent)
359         cmd->m_endingSelection = s;
360 }
361
362 void EditCommand::setEndingSelection(const Position &p, EAffinity affinity)
363 {
364     Selection s = Selection(p, affinity);
365     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent)
366         cmd->m_endingSelection = s;
367 }
368
369 void EditCommand::assignTypingStyle(CSSMutableStyleDeclarationImpl *style)
370 {
371     if (m_typingStyle == style)
372         return;
373         
374     CSSMutableStyleDeclarationImpl *old = m_typingStyle;
375     m_typingStyle = style;
376     if (m_typingStyle)
377         m_typingStyle->ref();
378     if (old)
379         old->deref();
380 }
381
382 void EditCommand::setTypingStyle(CSSMutableStyleDeclarationImpl *style)
383 {
384     // FIXME: Improve typing style.
385     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
386     for (EditCommand *cmd = this; cmd; cmd = cmd->m_parent)
387         cmd->assignTypingStyle(style);
388 }
389
390 bool EditCommand::preservesTypingStyle() const
391 {
392     return false;
393 }
394
395 bool EditCommand::isInsertTextCommand() const
396 {
397     return false;
398 }
399
400 bool EditCommand::isTypingCommand() const
401 {
402     return false;
403 }
404
405 CSSMutableStyleDeclarationImpl *EditCommand::styleAtPosition(const Position &pos)
406 {
407     CSSComputedStyleDeclarationImpl *computedStyle = pos.computedStyle();
408     computedStyle->ref();
409     CSSMutableStyleDeclarationImpl *style = computedStyle->copyInheritableProperties();
410     computedStyle->deref();
411  
412     // FIXME: Improve typing style.
413     // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
414     CSSMutableStyleDeclarationImpl *typingStyle = document()->part()->typingStyle();
415     if (typingStyle)
416         style->merge(typingStyle);
417     
418     return style;
419 }
420
421 } // namespace khtml