Reviewed by John
[WebKit-https.git] / WebCore / khtml / editing / htmlediting.h
1 /*
2  * Copyright (C) 2004 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 #ifndef __htmlediting_h__
27 #define __htmlediting_h__
28
29 #include "dom_nodeimpl.h"
30 #include "qvaluelist.h"
31 #include "selection.h"
32 #include "shared.h"
33
34 namespace DOM {
35     class CSSProperty;
36     class DocumentFragmentImpl;
37     class HTMLElementImpl;
38     class TextImpl;
39 }
40
41 namespace khtml {
42
43 class EditCommand;
44 class Selection;
45
46 //------------------------------------------------------------------------------------------
47 // EditCommandPtr
48
49 class EditCommandPtr : public SharedPtr<EditCommand>
50 {
51 public:
52     EditCommandPtr();
53     EditCommandPtr(EditCommand *);
54     EditCommandPtr(const EditCommandPtr &);
55     ~EditCommandPtr();
56
57     EditCommandPtr &operator=(const EditCommandPtr &);
58
59     bool isCompositeStep() const;
60
61     void apply() const;
62     void unapply() const;
63     void reapply() const;
64
65     DOM::DocumentImpl * const document() const;
66
67     khtml::Selection startingSelection() const;
68     khtml::Selection endingSelection() const;
69
70     void setStartingSelection(const khtml::Selection &s) const;
71     void setEndingSelection(const khtml::Selection &s) const;
72
73     DOM::CSSStyleDeclarationImpl *typingStyle() const;
74     void setTypingStyle(DOM::CSSStyleDeclarationImpl *) const;
75
76     EditCommandPtr parent() const;
77     void setParent(const EditCommandPtr &) const;
78
79     bool isInputTextCommand() const;
80     bool isInputNewlineCommand() const;
81     bool isTypingCommand() const;
82
83     static EditCommandPtr &emptyCommand();
84 };
85
86 //------------------------------------------------------------------------------------------
87 // StyleChange
88
89 class StyleChange {
90 public:
91     StyleChange() : m_applyBold(false), m_applyItalic(false) { }
92     explicit StyleChange(DOM::CSSStyleDeclarationImpl *);
93     StyleChange(DOM::CSSStyleDeclarationImpl *, const DOM::Position &);
94
95     DOM::DOMString cssStyle() const { return m_cssStyle; }
96     bool applyBold() const { return m_applyBold; }
97     bool applyItalic() const { return m_applyItalic; }
98
99 private:
100     void init(DOM::CSSStyleDeclarationImpl *, const DOM::Position &);
101     static bool currentlyHasStyle(const DOM::Position &, const DOM::CSSProperty *);
102     
103     DOM::DOMString m_cssStyle;
104     bool m_applyBold;
105     bool m_applyItalic;
106 };
107
108 //------------------------------------------------------------------------------------------
109 // EditCommand
110
111 class EditCommand : public Shared<EditCommand>
112 {
113 public:
114     EditCommand(DOM::DocumentImpl *);
115     virtual ~EditCommand();
116
117     bool isCompositeStep() const { return m_parent.notNull(); }
118     EditCommand *parent() const { return m_parent.get(); }
119     void setParent(EditCommand *parent) { m_parent = parent; }
120
121     enum ECommandState { NotApplied, Applied };
122     
123     void apply();       
124     void unapply();
125     void reapply();
126
127     virtual void doApply() = 0;
128     virtual void doUnapply() = 0;
129     virtual void doReapply();  // calls doApply()
130
131     virtual DOM::DocumentImpl * const document() const { return m_document; }
132
133     khtml::Selection startingSelection() const { return m_startingSelection; }
134     khtml::Selection endingSelection() const { return m_endingSelection; }
135         
136     ECommandState state() const { return m_state; }
137     void setState(ECommandState state) { m_state = state; }
138
139     void setStartingSelection(const khtml::Selection &s);
140     void setEndingSelection(const khtml::Selection &s);
141
142     DOM::CSSStyleDeclarationImpl *typingStyle() const { return m_typingStyle; };
143     void setTypingStyle(DOM::CSSStyleDeclarationImpl *);
144     
145     virtual bool isInputTextCommand() const;
146     virtual bool isTypingCommand() const;
147
148 private:
149     void assignTypingStyle(DOM::CSSStyleDeclarationImpl *);
150
151     virtual bool preservesTypingStyle() const;
152
153     DOM::DocumentImpl *m_document;
154     ECommandState m_state;
155     khtml::Selection m_startingSelection;
156     khtml::Selection m_endingSelection;
157     DOM::CSSStyleDeclarationImpl *m_typingStyle;
158     EditCommandPtr m_parent;
159 };
160
161 //------------------------------------------------------------------------------------------
162 // CompositeEditCommand
163
164 class CompositeEditCommand : public EditCommand
165 {
166 public:
167     CompositeEditCommand(DOM::DocumentImpl *);
168         
169     virtual void doUnapply();
170     virtual void doReapply();
171
172 protected:
173     //
174     // sugary-sweet convenience functions to help create and apply edit commands in composite commands
175     //
176     void appendNode(DOM::NodeImpl *appendChild, DOM::NodeImpl *parentNode);
177     void applyCommandToComposite(EditCommandPtr &);
178     void deleteKeyPressed();
179     void deleteSelection(bool smartDelete=false);
180     void deleteSelection(const khtml::Selection &selection, bool smartDelete=false);
181     void deleteText(DOM::TextImpl *node, long offset, long count);
182     void inputText(const DOM::DOMString &text, bool selectInsertedText = false);
183     void insertNodeAfter(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild);
184     void insertNodeAt(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild, long offset);
185     void insertNodeBefore(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild);
186     void insertText(DOM::TextImpl *node, long offset, const DOM::DOMString &text);
187     void joinTextNodes(DOM::TextImpl *text1, DOM::TextImpl *text2);
188     void removeCSSProperty(DOM::CSSStyleDeclarationImpl *, int property);
189     void removeFullySelectedNode(DOM::NodeImpl *);
190     void removeNodeAttribute(DOM::ElementImpl *, int attribute);
191     void removeNode(DOM::NodeImpl *removeChild);
192     void removeNodePreservingChildren(DOM::NodeImpl *node);
193     void replaceText(DOM::TextImpl *node, long offset, long count, const DOM::DOMString &replacementText);
194     void setNodeAttribute(DOM::ElementImpl *, int attribute, const DOM::DOMString &);
195     void splitTextNode(DOM::TextImpl *text, long offset);
196
197     DOM::NodeImpl *applyTypingStyle(DOM::NodeImpl *) const;
198     void deleteUnrenderedText(DOM::NodeImpl *);
199     void deleteUnrenderedText(const DOM::Position &pos);
200
201     void insertBlockPlaceholderIfNeeded(DOM::NodeImpl *);
202     void removeBlockPlaceholderIfNeeded(DOM::NodeImpl *);
203
204     QValueList<EditCommandPtr> m_cmds;
205 };
206
207 //==========================================================================================
208 // Concrete commands
209 //------------------------------------------------------------------------------------------
210 // AppendNodeCommand
211
212 class AppendNodeCommand : public EditCommand
213 {
214 public:
215     AppendNodeCommand(DOM::DocumentImpl *, DOM::NodeImpl *appendChild, DOM::NodeImpl *parentNode);
216     virtual ~AppendNodeCommand();
217
218     virtual void doApply();
219     virtual void doUnapply();
220
221     DOM::NodeImpl *appendChild() const { return m_appendChild; }
222     DOM::NodeImpl *parentNode() const { return m_parentNode; }
223
224 private:
225     DOM::NodeImpl *m_appendChild;
226     DOM::NodeImpl *m_parentNode;    
227 };
228
229 //------------------------------------------------------------------------------------------
230 // ApplyStyleCommand
231
232 class ApplyStyleCommand : public CompositeEditCommand
233 {
234 public:
235     ApplyStyleCommand(DOM::DocumentImpl *, DOM::CSSStyleDeclarationImpl *style);
236     virtual ~ApplyStyleCommand();
237         
238     virtual void doApply();
239
240     DOM::CSSStyleDeclarationImpl *style() const { return m_style; }
241
242 private:
243     // style-removal helpers
244     bool isHTMLStyleNode(DOM::HTMLElementImpl *);
245     void removeHTMLStyleNode(DOM::HTMLElementImpl *);
246     void removeCSSStyle(DOM::HTMLElementImpl *);
247     void removeStyle(const DOM::Position &start, const DOM::Position &end);
248     bool nodeFullySelected(const DOM::Position &, const DOM::NodeImpl *node) const;
249
250     // style-application helpers
251     bool splitTextAtStartIfNeeded(const DOM::Position &start, const DOM::Position &end);
252     DOM::NodeImpl *splitTextAtEndIfNeeded(const DOM::Position &start, const DOM::Position &end);
253     void surroundNodeRangeWithElement(DOM::NodeImpl *start, DOM::NodeImpl *end, DOM::ElementImpl *element);
254     DOM::Position positionInsertionPoint(DOM::Position);
255     void applyStyleIfNeeded(DOM::NodeImpl *start, DOM::NodeImpl *end);
256     
257     DOM::CSSStyleDeclarationImpl *m_style;
258 };
259
260 //------------------------------------------------------------------------------------------
261 // DeleteSelectionCommand
262
263 class DeleteSelectionCommand : public CompositeEditCommand
264
265 public:
266     DeleteSelectionCommand(DOM::DocumentImpl *document, bool smartDelete=false);
267     DeleteSelectionCommand(DOM::DocumentImpl *document, const khtml::Selection &selection, bool smartDelete=false);
268         
269     virtual void doApply();
270     
271 private:
272     virtual bool preservesTypingStyle() const;
273
274     void deleteDownstreamWS(const DOM::Position &start);
275     bool containsOnlyWhitespace(const DOM::Position &start, const DOM::Position &end);
276     void moveNodesAfterNode(DOM::NodeImpl *startNode, DOM::NodeImpl *dstNode);
277
278     khtml::Selection m_selectionToDelete;
279     bool m_hasSelectionToDelete;
280     bool m_smartDelete;
281 };
282
283 //------------------------------------------------------------------------------------------
284 // DeleteTextCommand
285
286 class DeleteTextCommand : public EditCommand
287 {
288 public:
289     DeleteTextCommand(DOM::DocumentImpl *document, DOM::TextImpl *node, long offset, long count);
290     virtual ~DeleteTextCommand();
291         
292     virtual void doApply();
293     virtual void doUnapply();
294
295     DOM::TextImpl *node() const { return m_node; }
296     long offset() const { return m_offset; }
297     long count() const { return m_count; }
298
299 private:
300     DOM::TextImpl *m_node;
301     long m_offset;
302     long m_count;
303     DOM::DOMString m_text;
304 };
305
306 //------------------------------------------------------------------------------------------
307 // InputNewlineCommand
308
309 class InputNewlineCommand : public CompositeEditCommand
310 {
311 public:
312     InputNewlineCommand(DOM::DocumentImpl *document);
313
314     virtual void doApply();
315
316 private:
317     void insertNodeAfterPosition(DOM::NodeImpl *node, const DOM::Position &pos);
318     void insertNodeBeforePosition(DOM::NodeImpl *node, const DOM::Position &pos);
319 };
320
321 //------------------------------------------------------------------------------------------
322 // InputTextCommand
323
324 class InputTextCommand : public CompositeEditCommand
325 {
326 public:
327     InputTextCommand(DOM::DocumentImpl *document);
328
329     virtual void doApply();
330
331     void deleteCharacter();
332     void input(const DOM::DOMString &text, bool selectInsertedText = false);
333     
334     unsigned long charactersAdded() const { return m_charactersAdded; }
335     
336 private:
337     virtual bool isInputTextCommand() const;
338
339     DOM::Position prepareForTextInsertion(bool adjustDownstream);
340     void insertSpace(DOM::TextImpl *textNode, unsigned long offset);
341
342     unsigned long m_charactersAdded;
343 };
344
345 //------------------------------------------------------------------------------------------
346 // InsertNodeBeforeCommand
347
348 class InsertNodeBeforeCommand : public EditCommand
349 {
350 public:
351     InsertNodeBeforeCommand(DOM::DocumentImpl *, DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild);
352     virtual ~InsertNodeBeforeCommand();
353
354     virtual void doApply();
355     virtual void doUnapply();
356
357     DOM::NodeImpl *insertChild() const { return m_insertChild; }
358     DOM::NodeImpl *refChild() const { return m_refChild; }
359
360 private:
361     DOM::NodeImpl *m_insertChild;
362     DOM::NodeImpl *m_refChild; 
363 };
364
365 //------------------------------------------------------------------------------------------
366 // InsertTextCommand
367
368 class InsertTextCommand : public EditCommand
369 {
370 public:
371     InsertTextCommand(DOM::DocumentImpl *document, DOM::TextImpl *, long, const DOM::DOMString &);
372     virtual ~InsertTextCommand();
373         
374     virtual void doApply();
375     virtual void doUnapply();
376
377     DOM::TextImpl *node() const { return m_node; }
378     long offset() const { return m_offset; }
379     DOM::DOMString text() const { return m_text; }
380
381 private:
382     DOM::TextImpl *m_node;
383     long m_offset;
384     DOM::DOMString m_text;
385 };
386
387 //------------------------------------------------------------------------------------------
388 // JoinTextNodesCommand
389
390 class JoinTextNodesCommand : public EditCommand
391 {
392 public:
393     JoinTextNodesCommand(DOM::DocumentImpl *, DOM::TextImpl *, DOM::TextImpl *);
394     virtual ~JoinTextNodesCommand();
395         
396     virtual void doApply();
397     virtual void doUnapply();
398
399     DOM::TextImpl *firstNode() const { return m_text1; }
400     DOM::TextImpl *secondNode() const { return m_text2; }
401
402 private:
403     DOM::TextImpl *m_text1;
404     DOM::TextImpl *m_text2;
405     unsigned long m_offset;
406 };
407
408 //------------------------------------------------------------------------------------------
409 // ReplaceSelectionCommand
410
411 class ReplaceSelectionCommand : public CompositeEditCommand
412 {
413 public:
414     ReplaceSelectionCommand(DOM::DocumentImpl *document, DOM::DocumentFragmentImpl *fragment, bool selectReplacement=true, bool smartReplace=false);
415     virtual ~ReplaceSelectionCommand();
416     
417     virtual void doApply();
418
419 private:
420     DOM::DocumentFragmentImpl *m_fragment;
421     bool m_selectReplacement;
422     bool m_smartReplace;
423 };
424
425 //------------------------------------------------------------------------------------------
426 // MoveSelectionCommand
427
428 class MoveSelectionCommand : public CompositeEditCommand
429 {
430 public:
431     MoveSelectionCommand(DOM::DocumentImpl *document, DOM::DocumentFragmentImpl *fragment, DOM::Position &position, bool smartMove=false);
432     virtual ~MoveSelectionCommand();
433     
434     virtual void doApply();
435     
436 private:
437     DOM::DocumentFragmentImpl *m_fragment;
438     DOM::Position m_position;
439     bool m_smartMove;
440 };
441
442 //------------------------------------------------------------------------------------------
443 // RemoveCSSPropertyCommand
444
445 class RemoveCSSPropertyCommand : public EditCommand
446 {
447 public:
448     RemoveCSSPropertyCommand(DOM::DocumentImpl *, DOM::CSSStyleDeclarationImpl *, int property);
449     virtual ~RemoveCSSPropertyCommand();
450
451     virtual void doApply();
452     virtual void doUnapply();
453
454     DOM::CSSStyleDeclarationImpl *styleDeclaration() const { return m_decl; }
455     int property() const { return m_property; }
456     
457 private:
458     DOM::CSSStyleDeclarationImpl *m_decl;
459     int m_property;
460     DOM::DOMString m_oldValue;
461     bool m_important;
462 };
463
464 //------------------------------------------------------------------------------------------
465 // RemoveNodeAttributeCommand
466
467 class RemoveNodeAttributeCommand : public EditCommand
468 {
469 public:
470     RemoveNodeAttributeCommand(DOM::DocumentImpl *, DOM::ElementImpl *, DOM::NodeImpl::Id attribute);
471     virtual ~RemoveNodeAttributeCommand();
472
473     virtual void doApply();
474     virtual void doUnapply();
475
476     DOM::ElementImpl *element() const { return m_element; }
477     DOM::NodeImpl::Id attribute() const { return m_attribute; }
478     
479 private:
480     DOM::ElementImpl *m_element;
481     DOM::NodeImpl::Id m_attribute;
482     DOM::DOMString m_oldValue;
483 };
484
485 //------------------------------------------------------------------------------------------
486 // RemoveNodeCommand
487
488 class RemoveNodeCommand : public EditCommand
489 {
490 public:
491     RemoveNodeCommand(DOM::DocumentImpl *, DOM::NodeImpl *);
492     virtual ~RemoveNodeCommand();
493         
494     virtual void doApply();
495     virtual void doUnapply();
496
497     DOM::NodeImpl *node() const { return m_removeChild; }
498
499 private:
500     DOM::NodeImpl *m_parent;    
501     DOM::NodeImpl *m_removeChild;
502     DOM::NodeImpl *m_refChild;    
503 };
504
505 //------------------------------------------------------------------------------------------
506 // RemoveNodePreservingChildrenCommand
507
508 class RemoveNodePreservingChildrenCommand : public CompositeEditCommand
509 {
510 public:
511     RemoveNodePreservingChildrenCommand(DOM::DocumentImpl *, DOM::NodeImpl *);
512     virtual ~RemoveNodePreservingChildrenCommand();
513         
514     virtual void doApply();
515
516     DOM::NodeImpl *node() const { return m_node; }
517
518 private:
519     DOM::NodeImpl *m_node;
520 };
521
522 //------------------------------------------------------------------------------------------
523 // SetNodeAttributeCommand
524
525 class SetNodeAttributeCommand : public EditCommand
526 {
527 public:
528     SetNodeAttributeCommand(DOM::DocumentImpl *, DOM::ElementImpl *, DOM::NodeImpl::Id attribute, const DOM::DOMString &value);
529     virtual ~SetNodeAttributeCommand();
530
531     virtual void doApply();
532     virtual void doUnapply();
533
534     DOM::ElementImpl *element() const { return m_element; }
535     DOM::NodeImpl::Id attribute() const { return m_attribute; }
536     DOM::DOMString value() const { return m_value; }
537     
538 private:
539     DOM::ElementImpl *m_element;
540     DOM::NodeImpl::Id m_attribute;
541     DOM::DOMString m_value;
542     DOM::DOMString m_oldValue;
543 };
544
545 //------------------------------------------------------------------------------------------
546 // SplitTextNodeCommand
547
548 class SplitTextNodeCommand : public EditCommand
549 {
550 public:
551     SplitTextNodeCommand(DOM::DocumentImpl *, DOM::TextImpl *, long);
552     virtual ~SplitTextNodeCommand();
553         
554     virtual void doApply();
555     virtual void doUnapply();
556
557     DOM::TextImpl *node() const { return m_text2; }
558     long offset() const { return m_offset; }
559
560 private:
561     DOM::TextImpl *m_text1;
562     DOM::TextImpl *m_text2;
563     unsigned long m_offset;
564 };
565
566 //------------------------------------------------------------------------------------------
567 // TypingCommand
568
569 class TypingCommand : public CompositeEditCommand
570 {
571 public:
572     enum ETypingCommand { DeleteKey, InsertText, InsertNewline };
573
574     TypingCommand(DOM::DocumentImpl *document, ETypingCommand, const DOM::DOMString &text = "", bool selectInsertedText = false);
575
576     static void deleteKeyPressed(DOM::DocumentImpl *document);
577     static void insertText(DOM::DocumentImpl *document, const DOM::DOMString &text, bool selectInsertedText = false);
578     static void insertNewline(DOM::DocumentImpl *document);
579     static bool isOpenForMoreTypingCommand(const EditCommandPtr &);
580     static void closeTyping(const EditCommandPtr &);
581     
582     virtual void doApply();
583
584     bool openForMoreTyping() const { return m_openForMoreTyping; }
585     void closeTyping() { m_openForMoreTyping = false; }
586
587     void insertText(const DOM::DOMString &text, bool selectInsertedText);
588     void insertNewline();
589     void deleteKeyPressed();
590
591 private:
592     virtual bool isTypingCommand() const;
593     virtual bool preservesTypingStyle() const;
594
595     void issueCommandForDeleteKey();
596     void removeCommand(const EditCommandPtr &);
597     void markMisspellingsAfterTyping();
598     void typingAddedToOpenCommand();
599     
600     ETypingCommand m_commandType;
601     DOM::DOMString m_textToInsert;
602     bool m_openForMoreTyping;
603     bool m_applyEditing;
604     bool m_selectInsertedText;
605 };
606
607 //------------------------------------------------------------------------------------------
608
609 } // end namespace khtml
610
611 #endif