• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.14.38 API Reference
  • KDE Home
  • Contact Us
 

KHTML

  • khtml
  • editing
htmlediting_impl.cpp
Go to the documentation of this file.
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#include "htmlediting_impl.h"
27#include "editor.h"
28
29#include "css/cssproperties.h"
30#include "css/css_valueimpl.h"
31#include "dom/css_value.h"
32#include "html/html_elementimpl.h"
33#include "html/html_imageimpl.h"
34#include "rendering/render_object.h"
35#include "rendering/render_style.h"
36#include "rendering/render_text.h"
37#include "xml/dom_docimpl.h"
38#include "xml/dom_elementimpl.h"
39#include "xml/dom_position.h"
40#include "xml/dom_positioniterator.h"
41#include "xml/dom_nodeimpl.h"
42#include "xml/dom_selection.h"
43#include "xml/dom_stringimpl.h"
44#include "xml/dom_textimpl.h"
45#include "xml/dom2_rangeimpl.h"
46#include "xml/dom2_viewsimpl.h"
47
48#include "khtml_part.h"
49#include "khtmlview.h"
50
51#include <QList>
52#include <limits.h>
53
54using DOM::AttrImpl;
55using DOM::CSSPrimitiveValue;
56using DOM::CSSPrimitiveValueImpl;
57using DOM::CSSProperty;
58using DOM::CSSStyleDeclarationImpl;
59using DOM::CSSValueImpl;
60using DOM::DocumentFragmentImpl;
61using DOM::DocumentImpl;
62using DOM::DOMString;
63using DOM::DOMStringImpl;
64using DOM::EditingTextImpl;
65using DOM::PositionIterator;
66using DOM::ElementImpl;
67using DOM::HTMLElementImpl;
68using DOM::HTMLImageElementImpl;
69using DOM::NamedAttrMapImpl;
70using DOM::Node;
71using DOM::NodeImpl;
72using DOM::NodeListImpl;
73using DOM::Position;
74using DOM::Range;
75using DOM::RangeImpl;
76using DOM::Selection;
77using DOM::TextImpl;
78using DOM::TreeWalkerImpl;
79using DOM::Editor;
80
81#define DEBUG_COMMANDS 1
82
83namespace khtml {
84
85
86static inline bool isNBSP(const QChar &c)
87{
88 return c == QChar(0xa0);
89}
90
91static inline bool isWS(const QChar &c)
92{
93 return c.isSpace() && c != QChar(0xa0);
94}
95
96static inline bool isWS(const DOMString &text)
97{
98 if (text.length() != 1)
99 return false;
100
101 return isWS(text[0]);
102}
103
104static inline bool isWS(const Position &pos)
105{
106 if (!pos.node())
107 return false;
108
109 if (!pos.node()->isTextNode())
110 return false;
111
112 const DOMString &string = static_cast<TextImpl *>(pos.node())->data();
113 return isWS(string[pos.offset()]);
114}
115
116static bool shouldPruneNode(NodeImpl *node)
117{
118 if (!node)
119 return false;
120
121 RenderObject *renderer = node->renderer();
122 if (!renderer)
123 return true;
124
125 if (node->hasChildNodes())
126 return false;
127
128 if (node->rootEditableElement() == node)
129 return false;
130
131 if (renderer->isBR() || renderer->isReplaced())
132 return false;
133
134 if (node->isTextNode()) {
135 TextImpl *text = static_cast<TextImpl *>(node);
136 if (text->length() == 0)
137 return true;
138 return false;
139 }
140
141 if (!node->isHTMLElement()/* && !node->isXMLElementNode()*/)
142 return false;
143
144 if (node->id() == ID_BODY)
145 return false;
146
147 if (!node->isContentEditable())
148 return false;
149
150 return true;
151}
152
153static Position leadingWhitespacePosition(const Position &pos)
154{
155 assert(pos.notEmpty());
156
157 Selection selection(pos);
158 Position prev = pos.previousCharacterPosition();
159 if (prev != pos && prev.node()->inSameContainingBlockFlowElement(pos.node()) && prev.node()->isTextNode()) {
160 DOMString string = static_cast<TextImpl *>(prev.node())->data();
161 if (isWS(string[prev.offset()]))
162 return prev;
163 }
164
165 return Position();
166}
167
168static Position trailingWhitespacePosition(const Position &pos)
169{
170 assert(pos.notEmpty());
171
172 if (pos.node()->isTextNode()) {
173 TextImpl *textNode = static_cast<TextImpl *>(pos.node());
174 if (pos.offset() >= (long)textNode->length()) {
175 Position next = pos.nextCharacterPosition();
176 if (next != pos && next.node()->inSameContainingBlockFlowElement(pos.node()) && next.node()->isTextNode()) {
177 DOMString string = static_cast<TextImpl *>(next.node())->data();
178 if (isWS(string[0]))
179 return next;
180 }
181 }
182 else {
183 DOMString string = static_cast<TextImpl *>(pos.node())->data();
184 if (isWS(string[pos.offset()]))
185 return pos;
186 }
187 }
188
189 return Position();
190}
191
192static bool textNodesAreJoinable(TextImpl *text1, TextImpl *text2)
193{
194 assert(text1);
195 assert(text2);
196
197 return (text1->nextSibling() == text2);
198}
199
200static DOMString &nonBreakingSpaceString()
201{
202 static DOMString nonBreakingSpaceString = QString(QChar(0xa0));
203 return nonBreakingSpaceString;
204}
205
206static DOMString &styleSpanClassString()
207{
208 static DOMString styleSpanClassString = "khtml-style-span";
209 return styleSpanClassString;
210}
211
212//------------------------------------------------------------------------------------------
213// EditCommandImpl
214
215EditCommandImpl::EditCommandImpl(DocumentImpl *document)
216 : SharedCommandImpl(), m_document(document), m_state(NotApplied), m_parent(0)
217{
218 assert(m_document);
219 assert(m_document->part());
220 m_document->ref();
221 m_startingSelection = m_document->part()->caret();
222 m_endingSelection = m_startingSelection;
223}
224
225EditCommandImpl::~EditCommandImpl()
226{
227 m_document->deref();
228}
229
230void EditCommandImpl::apply()
231{
232 assert(m_document);
233 assert(m_document->part());
234 assert(state() == NotApplied);
235
236 doApply();
237
238 m_state = Applied;
239
240 if (!isCompositeStep())
241 m_document->part()->editor()->appliedEditing(this);
242}
243
244void EditCommandImpl::unapply()
245{
246 assert(m_document);
247 assert(m_document->part());
248 assert(state() == Applied);
249
250 doUnapply();
251
252 m_state = NotApplied;
253
254 if (!isCompositeStep())
255 m_document->part()->editor()->unappliedEditing(this);
256}
257
258void EditCommandImpl::reapply()
259{
260 assert(m_document);
261 assert(m_document->part());
262 assert(state() == NotApplied);
263
264 doReapply();
265
266 m_state = Applied;
267
268 if (!isCompositeStep())
269 m_document->part()->editor()->reappliedEditing(this);
270}
271
272void EditCommandImpl::doReapply()
273{
274 doApply();
275}
276
277void EditCommandImpl::setStartingSelection(const Selection &s)
278{
279 m_startingSelection = s;
280 EditCommandImpl *cmd = parent();
281 while (cmd) {
282 cmd->m_startingSelection = s;
283 cmd = cmd->parent();
284 }
285}
286
287void EditCommandImpl::setEndingSelection(const Selection &s)
288{
289 m_endingSelection = s;
290 EditCommandImpl *cmd = parent();
291 while (cmd) {
292 cmd->m_endingSelection = s;
293 cmd = cmd->parent();
294 }
295}
296
297EditCommandImpl* EditCommandImpl::parent() const
298{
299 return m_parent.get();
300}
301
302void EditCommandImpl::setParent(EditCommandImpl* cmd)
303{
304 m_parent = cmd;
305}
306
307//------------------------------------------------------------------------------------------
308// CompositeEditCommandImpl
309
310CompositeEditCommandImpl::CompositeEditCommandImpl(DocumentImpl *document)
311 : EditCommandImpl(document)
312{
313}
314
315CompositeEditCommandImpl::~CompositeEditCommandImpl()
316{
317}
318
319void CompositeEditCommandImpl::doUnapply()
320{
321 if (m_cmds.count() == 0) {
322 return;
323 }
324
325 for (int i = m_cmds.count() - 1; i >= 0; --i)
326 m_cmds[i]->unapply();
327
328 setState(NotApplied);
329}
330
331void CompositeEditCommandImpl::doReapply()
332{
333 if (m_cmds.count() == 0) {
334 return;
335 }
336 QMutableListIterator<RefPtr<EditCommandImpl> > it(m_cmds);
337 while (it.hasNext())
338 it.next()->reapply();
339
340 setState(Applied);
341}
342
343//
344// sugary-sweet convenience functions to help create and apply edit commands in composite commands
345//
346void CompositeEditCommandImpl::applyCommandToComposite(PassRefPtr<EditCommandImpl> cmd)
347{
348 cmd->setStartingSelection(endingSelection());//###?
349 cmd->setEndingSelection(endingSelection());
350 cmd->setParent(this);
351 cmd->apply();
352 m_cmds.append(cmd);
353}
354
355void CompositeEditCommandImpl::insertNodeBefore(NodeImpl *insertChild, NodeImpl *refChild)
356{
357 RefPtr<InsertNodeBeforeCommandImpl> cmd = new InsertNodeBeforeCommandImpl(document(), insertChild, refChild);
358 applyCommandToComposite(cmd);
359}
360
361void CompositeEditCommandImpl::insertNodeAfter(NodeImpl *insertChild, NodeImpl *refChild)
362{
363 if (refChild->parentNode()->lastChild() == refChild) {
364 appendNode(refChild->parentNode(), insertChild);
365 }
366 else {
367 assert(refChild->nextSibling());
368 insertNodeBefore(insertChild, refChild->nextSibling());
369 }
370}
371
372void CompositeEditCommandImpl::insertNodeAt(NodeImpl *insertChild, NodeImpl *refChild, long offset)
373{
374 if (refChild->hasChildNodes() || (refChild->renderer() && refChild->renderer()->isBlockFlow())) {
375 NodeImpl *child = refChild->firstChild();
376 for (long i = 0; child && i < offset; i++)
377 child = child->nextSibling();
378 if (child)
379 insertNodeBefore(insertChild, child);
380 else
381 appendNode(refChild, insertChild);
382 }
383 else if (refChild->caretMinOffset() >= offset) {
384 insertNodeBefore(insertChild, refChild);
385 }
386 else if (refChild->isTextNode() && refChild->caretMaxOffset() > offset) {
387 splitTextNode(static_cast<TextImpl *>(refChild), offset);
388 insertNodeBefore(insertChild, refChild);
389 }
390 else {
391 insertNodeAfter(insertChild, refChild);
392 }
393}
394
395void CompositeEditCommandImpl::appendNode(NodeImpl *parent, NodeImpl *appendChild)
396{
397 RefPtr<AppendNodeCommandImpl> cmd = new AppendNodeCommandImpl(document(), parent, appendChild);
398 applyCommandToComposite(cmd);
399}
400
401void CompositeEditCommandImpl::removeNode(NodeImpl *removeChild)
402{
403 RefPtr<RemoveNodeCommandImpl> cmd = new RemoveNodeCommandImpl(document(), removeChild);
404 applyCommandToComposite(cmd);
405}
406
407void CompositeEditCommandImpl::removeNodeAndPrune(NodeImpl *pruneNode, NodeImpl *stopNode)
408{
409 RefPtr<RemoveNodeAndPruneCommandImpl> cmd = new RemoveNodeAndPruneCommandImpl(document(), pruneNode, stopNode);
410 applyCommandToComposite(cmd);
411}
412
413void CompositeEditCommandImpl::removeNodePreservingChildren(NodeImpl *removeChild)
414{
415 RefPtr<RemoveNodePreservingChildrenCommandImpl> cmd = new RemoveNodePreservingChildrenCommandImpl(document(), removeChild);
416 applyCommandToComposite(cmd);
417}
418
419void CompositeEditCommandImpl::splitTextNode(TextImpl *text, long offset)
420{
421 RefPtr<SplitTextNodeCommandImpl> cmd = new SplitTextNodeCommandImpl(document(), text, offset);
422 applyCommandToComposite(cmd);
423}
424
425void CompositeEditCommandImpl::joinTextNodes(TextImpl *text1, TextImpl *text2)
426{
427 RefPtr<JoinTextNodesCommandImpl> cmd = new JoinTextNodesCommandImpl(document(), text1, text2);
428 applyCommandToComposite(cmd);
429}
430
431void CompositeEditCommandImpl::inputText(const DOMString &text)
432{
433 RefPtr<InputTextCommandImpl> cmd = new InputTextCommandImpl(document());
434 applyCommandToComposite(cmd);
435 cmd->input(text);
436}
437
438void CompositeEditCommandImpl::insertText(TextImpl *node, long offset, const DOMString &text)
439{
440 RefPtr<InsertTextCommandImpl> cmd = new InsertTextCommandImpl(document(), node, offset, text);
441 applyCommandToComposite(cmd);
442}
443
444void CompositeEditCommandImpl::deleteText(TextImpl *node, long offset, long count)
445{
446 RefPtr<DeleteTextCommandImpl> cmd = new DeleteTextCommandImpl(document(), node, offset, count);
447 applyCommandToComposite(cmd);
448}
449
450void CompositeEditCommandImpl::replaceText(TextImpl *node, long offset, long count, const DOMString &replacementText)
451{
452 RefPtr<DeleteTextCommandImpl> deleteCommand = new DeleteTextCommandImpl(document(), node, offset, count);
453 applyCommandToComposite(deleteCommand);
454 RefPtr<InsertTextCommandImpl> insertCommand = new InsertTextCommandImpl(document(), node, offset, replacementText);
455 applyCommandToComposite(insertCommand);
456}
457
458void CompositeEditCommandImpl::deleteSelection()
459{
460 if (endingSelection().state() == Selection::RANGE) {
461 RefPtr<DeleteSelectionCommandImpl> cmd = new DeleteSelectionCommandImpl(document());
462 applyCommandToComposite(cmd);
463 }
464}
465
466void CompositeEditCommandImpl::deleteSelection(const Selection &selection)
467{
468 if (selection.state() == Selection::RANGE) {
469 RefPtr<DeleteSelectionCommandImpl> cmd = new DeleteSelectionCommandImpl(document(), selection);
470 applyCommandToComposite(cmd);
471 }
472}
473
474void CompositeEditCommandImpl::deleteCollapsibleWhitespace()
475{
476 RefPtr<DeleteCollapsibleWhitespaceCommandImpl> cmd = new DeleteCollapsibleWhitespaceCommandImpl(document());
477 applyCommandToComposite(cmd);
478}
479
480void CompositeEditCommandImpl::deleteCollapsibleWhitespace(const Selection &selection)
481{
482 RefPtr<DeleteCollapsibleWhitespaceCommandImpl> cmd = new DeleteCollapsibleWhitespaceCommandImpl(document(), selection);
483 applyCommandToComposite(cmd);
484}
485
486void CompositeEditCommandImpl::removeCSSProperty(CSSStyleDeclarationImpl *decl, int property)
487{
488 RefPtr<RemoveCSSPropertyCommandImpl> cmd = new RemoveCSSPropertyCommandImpl(document(), decl, property);
489 applyCommandToComposite(cmd);
490}
491
492void CompositeEditCommandImpl::removeNodeAttribute(ElementImpl *element, int attribute)
493{
494 RefPtr<RemoveNodeAttributeCommandImpl> cmd = new RemoveNodeAttributeCommandImpl(document(), element, attribute);
495 applyCommandToComposite(cmd);
496}
497
498void CompositeEditCommandImpl::setNodeAttribute(ElementImpl *element, int attribute, const DOMString &value)
499{
500 RefPtr<SetNodeAttributeCommandImpl> cmd = new SetNodeAttributeCommandImpl(document(), element, attribute, value);
501 applyCommandToComposite(cmd);
502}
503
504ElementImpl *CompositeEditCommandImpl::createTypingStyleElement() const
505{
506 int exceptionCode = 0;
507 ElementImpl *styleElement = document()->createHTMLElement("SPAN");
508// assert(exceptionCode == 0);
509
510 styleElement->setAttribute(ATTR_STYLE, document()->part()->editor()->typingStyle()->cssText().implementation());
511// assert(exceptionCode == 0);
512
513 styleElement->setAttribute(ATTR_CLASS, styleSpanClassString());
514 assert(exceptionCode == 0);
515
516 return styleElement;
517}
518
519//==========================================================================================
520// Concrete commands
521//------------------------------------------------------------------------------------------
522// AppendNodeCommandImpl
523
524AppendNodeCommandImpl::AppendNodeCommandImpl(DocumentImpl *document, NodeImpl *parentNode, NodeImpl *appendChild)
525 : EditCommandImpl(document), m_parentNode(parentNode), m_appendChild(appendChild)
526{
527 assert(m_parentNode);
528 m_parentNode->ref();
529
530 assert(m_appendChild);
531 m_appendChild->ref();
532}
533
534AppendNodeCommandImpl::~AppendNodeCommandImpl()
535{
536 if (m_parentNode)
537 m_parentNode->deref();
538 if (m_appendChild)
539 m_appendChild->deref();
540}
541
542void AppendNodeCommandImpl::doApply()
543{
544 assert(m_parentNode);
545 assert(m_appendChild);
546
547 int exceptionCode = 0;
548 m_parentNode->appendChild(m_appendChild, exceptionCode);
549 assert(exceptionCode == 0);
550}
551
552void AppendNodeCommandImpl::doUnapply()
553{
554 assert(m_parentNode);
555 assert(m_appendChild);
556 assert(state() == Applied);
557
558 int exceptionCode = 0;
559 m_parentNode->removeChild(m_appendChild, exceptionCode);
560 assert(exceptionCode == 0);
561}
562
563//------------------------------------------------------------------------------------------
564// ApplyStyleCommandImpl
565
566ApplyStyleCommandImpl::ApplyStyleCommandImpl(DocumentImpl *document, CSSStyleDeclarationImpl *style)
567 : CompositeEditCommandImpl(document), m_style(style)
568{
569 assert(m_style);
570 m_style->ref();
571}
572
573ApplyStyleCommandImpl::~ApplyStyleCommandImpl()
574{
575 assert(m_style);
576 m_style->deref();
577}
578
579static bool isBlockLevelStyle(const CSSStyleDeclarationImpl* style)
580{
581 QListIterator<CSSProperty*> it(*(style->values()));
582 while (it.hasNext()) {
583 CSSProperty *property = it.next();
584 switch (property->id()) {
585 case CSS_PROP_TEXT_ALIGN:
586 return true;
587 /*case CSS_PROP_FONT_WEIGHT:
588 if (strcasecmp(property->value()->cssText(), "bold") == 0)
589 styleChange.applyBold = true;
590 else
591 styleChange.cssStyle += property->cssText();
592 break;
593 case CSS_PROP_FONT_STYLE: {
594 DOMString cssText(property->value()->cssText());
595 if (strcasecmp(cssText, "italic") == 0 || strcasecmp(cssText, "oblique") == 0)
596 styleChange.applyItalic = true;
597 else
598 styleChange.cssStyle += property->cssText();
599 }
600 break;
601 default:
602 styleChange.cssStyle += property->cssText();
603 break;*/
604 }
605 }
606 return false;
607}
608
609static void applyStyleChangeOnTheNode(ElementImpl* element, CSSStyleDeclarationImpl* style)
610{
611 QScopedPointer<CSSStyleDeclarationImpl> computedStyle(
612 element->document()->defaultView()->getComputedStyle(element, 0));
613 assert(!computedStyle.isNull());
614#ifdef DEBUG_COMMANDS
615 kDebug() << "[change style]" << element << endl;
616#endif
617
618 QListIterator<CSSProperty*> it(*(style->values()));
619 while ( it.hasNext() ) {
620 CSSProperty *property = it.next();
621 CSSValueImpl *computedValue = computedStyle->getPropertyCSSValue(property->id());
622 DOMString newValue = property->value()->cssText();
623#ifdef DEBUG_COMMANDS
624 kDebug() << "[new value]:" << property->cssText() << endl;
625 kDebug() << "[computedValue]:" << computedValue->cssText() << endl;
626#endif
627 if (strcasecmp(computedValue->cssText(), newValue)) {
628 // we can do better and avoid parsing property
629 element->getInlineStyleDecls()->setProperty(property->id(), newValue);
630 }
631 }
632}
633
634void ApplyStyleCommandImpl::doApply()
635{
636 if (endingSelection().state() != Selection::RANGE)
637 return;
638
639 // adjust to the positions we want to use for applying style
640 Position start(endingSelection().start().equivalentDownstreamPosition().equivalentRangeCompliantPosition());
641 Position end(endingSelection().end().equivalentUpstreamPosition());
642#ifdef DEBUG_COMMANDS
643 kDebug() << "[APPLY STYLE]" << start << end << endl;
644 printEnclosingBlockTree(start.node()->enclosingBlockFlowElement());
645#endif
646
647 if (isBlockLevelStyle(m_style)) {
648#ifdef DEBUG_COMMANDS
649 kDebug() << "[APPLY BLOCK LEVEL STYLE]" << endl;
650#endif
651 ElementImpl *startBlock = start.node()->enclosingBlockFlowElement();
652 ElementImpl *endBlock = end.node()->enclosingBlockFlowElement();
653#ifdef DEBUG_COMMANDS
654 kDebug() << startBlock << startBlock->nodeName() << endl;
655#endif
656 if (startBlock == endBlock && startBlock == start.node()->rootEditableElement()) {
657 ElementImpl* block = document()->createHTMLElement("DIV");
658#ifdef DEBUG_COMMANDS
659 kDebug() << "[Create DIV with Style:]" << m_style->cssText() << endl;
660#endif
661 block->setAttribute(ATTR_STYLE, m_style->cssText());
662 for (NodeImpl* node = startBlock->firstChild(); node; node = startBlock->firstChild()) {
663#ifdef DEBUG_COMMANDS
664 kDebug() << "[reparent node]" << node << node->nodeName() << endl;
665#endif
666 removeNode(node);
667 appendNode(block, node);
668 }
669 appendNode(startBlock, block);
670 } else if (startBlock == endBlock) {
671 // StyleChange styleChange = computeStyleChange(Position(startBlock, 0), m_style);
672 // kDebug() << "[Modify block with style change:]" << styleChange.cssStyle << endl;
673 applyStyleChangeOnTheNode(startBlock, m_style);
674 // startBlock->setAttribute(ATTR_STYLE, styleChange.cssStyle);
675 }
676 return;
677 }
678
679 // remove style from the selection
680 removeStyle(start, end);
681 bool splitStart = splitTextAtStartIfNeeded(start, end);
682 if (splitStart) {
683 start = endingSelection().start();
684 end = endingSelection().end();
685 }
686 splitTextAtEndIfNeeded(start, end);
687 start = endingSelection().start();
688 end = endingSelection().end();
689
690#ifdef DEBUG_COMMANDS
691 kDebug() << "[start;end]" << start << end << endl;
692#endif
693 if (start.node() == end.node()) {
694 // simple case...start and end are the same node
695 applyStyleIfNeeded(start.node(), end.node());
696 } else {
697 NodeImpl *node = start.node();
698 while (1) {
699 if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
700 NodeImpl *runStart = node;
701 while (1) {
702 if (runStart->parentNode() != node->parentNode() || node->isHTMLElement() || node == end.node() ||
703 (node->renderer() && !node->renderer()->isInline())) {
704 applyStyleIfNeeded(runStart, node);
705 break;
706 }
707 node = node->traverseNextNode();
708 }
709 }
710 if (node == end.node())
711 break;
712 node = node->traverseNextNode();
713 }
714 }
715}
716
717//------------------------------------------------------------------------------------------
718// ApplyStyleCommandImpl: style-removal helpers
719
720bool ApplyStyleCommandImpl::isHTMLStyleNode(HTMLElementImpl *elem)
721{
722 QListIterator<CSSProperty*> it(*(style()->values()));
723 while (it.hasNext()) {
724 CSSProperty *property = it.next();
725 switch (property->id()) {
726 case CSS_PROP_FONT_WEIGHT:
727 if (elem->id() == ID_B)
728 return true;
729 break;
730 case CSS_PROP_FONT_STYLE:
731 if (elem->id() == ID_I)
732 return true;
733 break;
734 }
735 }
736
737 return false;
738}
739
740void ApplyStyleCommandImpl::removeHTMLStyleNode(HTMLElementImpl *elem)
741{
742 // This node can be removed.
743 // EDIT FIXME: This does not handle the case where the node
744 // has attributes. But how often do people add attributes to <B> tags?
745 // Not so often I think.
746 assert(elem);
747 removeNodePreservingChildren(elem);
748}
749
750void ApplyStyleCommandImpl::removeCSSStyle(HTMLElementImpl *elem)
751{
752 assert(elem);
753
754 CSSStyleDeclarationImpl *decl = elem->inlineStyleDecls();
755 if (!decl)
756 return;
757
758 QListIterator<CSSProperty*> it(*(style()->values()));
759 while ( it.hasNext() ) {
760 CSSProperty *property = it.next();
761 if (decl->getPropertyCSSValue(property->id()))
762 removeCSSProperty(decl, property->id());
763 }
764
765 if (elem->id() == ID_SPAN) {
766 // Check to see if the span is one we added to apply style.
767 // If it is, and there are no more attributes on the span other than our
768 // class marker, remove the span.
769 NamedAttrMapImpl *map = elem->attributes();
770 if (map && (map->length() == 1 || (map->length() == 2 && elem->getAttribute(ATTR_STYLE).isEmpty())) &&
771 elem->getAttribute(ATTR_CLASS) == styleSpanClassString())
772 removeNodePreservingChildren(elem);
773 }
774}
775
776void ApplyStyleCommandImpl::removeStyle(const Position &start, const Position &end)
777{
778 NodeImpl *node = start.node();
779 while (1) {
780 NodeImpl *next = node->traverseNextNode();
781 if (node->isHTMLElement() && nodeFullySelected(node)) {
782 HTMLElementImpl *elem = static_cast<HTMLElementImpl *>(node);
783 if (isHTMLStyleNode(elem))
784 removeHTMLStyleNode(elem);
785 else
786 removeCSSStyle(elem);
787 }
788 if (node == end.node())
789 break;
790 node = next;
791 }
792}
793
794bool ApplyStyleCommandImpl::nodeFullySelected(const NodeImpl *node) const
795{
796 assert(node);
797
798 Position end(endingSelection().end().equivalentUpstreamPosition());
799
800 if (node == end.node())
801 return end.offset() >= node->caretMaxOffset();
802
803 for (NodeImpl *child = node->lastChild(); child; child = child->lastChild()) {
804 if (child == end.node())
805 return end.offset() >= child->caretMaxOffset();
806 }
807
808 return node == end.node() || !node->isAncestor(end.node());
809}
810
811//------------------------------------------------------------------------------------------
812// ApplyStyleCommandImpl: style-application helpers
813
814bool ApplyStyleCommandImpl::splitTextAtStartIfNeeded(const Position &start, const Position &end)
815{
816 if (start.node()->isTextNode() && start.offset() > start.node()->caretMinOffset() && start.offset() < start.node()->caretMaxOffset()) {
817#ifdef DEBUG_COMMANDS
818 kDebug() << "[split start]" << start.offset() << start.node()->caretMinOffset() << start.node()->caretMaxOffset() << endl;
819#endif
820 long endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0;
821 TextImpl *text = static_cast<TextImpl *>(start.node());
822 RefPtr<SplitTextNodeCommandImpl> cmd = new SplitTextNodeCommandImpl(document(), text, start.offset());
823 applyCommandToComposite(cmd);
824 setEndingSelection(Selection(Position(start.node(), 0), Position(end.node(), end.offset() - endOffsetAdjustment)));
825 return true;
826 }
827 return false;
828}
829
830NodeImpl *ApplyStyleCommandImpl::splitTextAtEndIfNeeded(const Position &start, const Position &end)
831{
832 if (end.node()->isTextNode() && end.offset() > end.node()->caretMinOffset() && end.offset() < end.node()->caretMaxOffset()) {
833#ifdef DEBUG_COMMANDS
834 kDebug() << "[split end]" << end.offset() << end.node()->caretMinOffset() << end.node()->caretMaxOffset() << endl;
835#endif
836 TextImpl *text = static_cast<TextImpl *>(end.node());
837 RefPtr<SplitTextNodeCommandImpl> cmd = new SplitTextNodeCommandImpl(document(), text, end.offset());
838 applyCommandToComposite(cmd);
839 NodeImpl *startNode = start.node() == end.node() ? cmd->node()->previousSibling() : start.node();
840 assert(startNode);
841 setEndingSelection(Selection(Position(startNode, start.offset()), Position(cmd->node()->previousSibling(), cmd->node()->previousSibling()->caretMaxOffset())));
842 return cmd->node()->previousSibling();
843 }
844 return end.node();
845}
846
847void ApplyStyleCommandImpl::surroundNodeRangeWithElement(NodeImpl *startNode, NodeImpl *endNode, ElementImpl *element)
848{
849 assert(startNode);
850 assert(endNode);
851 assert(element);
852
853 NodeImpl *node = startNode;
854 while (1) {
855 NodeImpl *next = node->traverseNextNode();
856 if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
857 removeNode(node);
858 appendNode(element, node);
859 }
860 if (node == endNode)
861 break;
862 node = next;
863 }
864}
865
866static bool /*ApplyStyleCommandImpl::*/checkIfNewStylingNeeded(ElementImpl* element, CSSStyleDeclarationImpl *style)
867{
868 QScopedPointer<CSSStyleDeclarationImpl> computedStyle(
869 element->document()->defaultView()->getComputedStyle(element, 0));
870 assert(!computedStyle.isNull());
871#ifdef DEBUG_COMMANDS
872 kDebug() << "[check styling]" << element << endl;
873#endif
874
875 QListIterator<CSSProperty*> it(*(style->values()));
876 while ( it.hasNext() ) {
877 CSSProperty *property = it.next();
878 CSSValueImpl *computedValue = computedStyle->getPropertyCSSValue(property->id());
879 DOMString newValue = property->value()->cssText();
880#ifdef DEBUG_COMMANDS
881 kDebug() << "[new value]:" << property->cssText() << endl;
882 kDebug() << "[computedValue]:" << computedValue->cssText() << endl;
883#endif
884 if (strcasecmp(computedValue->cssText(), newValue))
885 return true;
886 }
887 return false;
888}
889
890void ApplyStyleCommandImpl::applyStyleIfNeeded(DOM::NodeImpl *startNode, DOM::NodeImpl *endNode)
891{
892 ElementImpl *parent = Position(startNode, 0).element();
893 if (!checkIfNewStylingNeeded(parent, style()))
894 return;
895 ElementImpl *styleElement = 0;
896 if (parent->id() == ID_SPAN && parent->firstChild() == startNode && parent->lastChild() == endNode) {
897 styleElement = parent;
898 } else {
899 styleElement = document()->createHTMLElement("SPAN");
900 styleElement->setAttribute(ATTR_CLASS, styleSpanClassString());
901 insertNodeBefore(styleElement, startNode);
902 surroundNodeRangeWithElement(startNode, endNode, styleElement);
903 }
904 applyStyleChangeOnTheNode(styleElement, style());
905}
906
907bool ApplyStyleCommandImpl::currentlyHasStyle(const Position &pos, const CSSProperty *property) const
908{
909 assert(pos.notEmpty());
910 kDebug() << pos << endl;
911 CSSStyleDeclarationImpl *decl = document()->defaultView()->getComputedStyle(pos.element(), 0);
912 assert(decl);
913 CSSValueImpl *value = decl->getPropertyCSSValue(property->id());
914 return strcasecmp(value->cssText(), property->value()->cssText()) == 0;
915}
916
917ApplyStyleCommandImpl::StyleChange ApplyStyleCommandImpl::computeStyleChange(const Position &insertionPoint, CSSStyleDeclarationImpl *style)
918{
919 assert(insertionPoint.notEmpty());
920 assert(style);
921
922 StyleChange styleChange;
923
924 QListIterator<CSSProperty*> it(*(style->values()));
925 while ( it.hasNext() ) {
926 CSSProperty *property = it.next();
927#ifdef DEBUG_COMMANDS
928 kDebug() << "[CSS property]:" << property->cssText() << endl;
929#endif
930 if (!currentlyHasStyle(insertionPoint, property)) {
931#ifdef DEBUG_COMMANDS
932 kDebug() << "[Add to style change]" << endl;
933#endif
934 switch (property->id()) {
935 case CSS_PROP_FONT_WEIGHT:
936 if (strcasecmp(property->value()->cssText(), "bold") == 0)
937 styleChange.applyBold = true;
938 else
939 styleChange.cssStyle += property->cssText();
940 break;
941 case CSS_PROP_FONT_STYLE: {
942 DOMString cssText(property->value()->cssText());
943 if (strcasecmp(cssText, "italic") == 0 || strcasecmp(cssText, "oblique") == 0)
944 styleChange.applyItalic = true;
945 else
946 styleChange.cssStyle += property->cssText();
947 }
948 break;
949 default:
950 styleChange.cssStyle += property->cssText();
951 break;
952 }
953 }
954 }
955 return styleChange;
956}
957
958Position ApplyStyleCommandImpl::positionInsertionPoint(Position pos)
959{
960 if (pos.node()->isTextNode() && (pos.offset() > 0 && pos.offset() < pos.node()->maxOffset())) {
961 RefPtr<SplitTextNodeCommandImpl> split = new SplitTextNodeCommandImpl(document(), static_cast<TextImpl *>(pos.node()), pos.offset());
962 split->apply();
963 pos = Position(split->node(), 0);
964 }
965#if 0
966 // EDIT FIXME: If modified to work with the internals of applying style,
967 // this code can work to optimize cases where a style change is taking place on
968 // a boundary between nodes where one of the nodes has the desired style. In other
969 // words, it is possible for content to be merged into existing nodes rather than adding
970 // additional markup.
971 if (currentlyHasStyle(pos))
972 return pos;
973
974 // try next node
975 if (pos.offset() >= pos.node()->caretMaxOffset()) {
976 NodeImpl *nextNode = pos.node()->traverseNextNode();
977 if (nextNode) {
978 Position next = Position(nextNode, 0);
979 if (currentlyHasStyle(next))
980 return next;
981 }
982 }
983
984 // try previous node
985 if (pos.offset() <= pos.node()->caretMinOffset()) {
986 NodeImpl *prevNode = pos.node()->traversePreviousNode();
987 if (prevNode) {
988 Position prev = Position(prevNode, prevNode->maxOffset());
989 if (currentlyHasStyle(prev))
990 return prev;
991 }
992 }
993#endif
994
995 return pos;
996}
997
998//------------------------------------------------------------------------------------------
999// DeleteCollapsibleWhitespaceCommandImpl
1000
1001DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl(DocumentImpl *document)
1002 : CompositeEditCommandImpl(document), m_charactersDeleted(0), m_hasSelectionToCollapse(false)
1003{
1004}
1005
1006DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl(DocumentImpl *document, const Selection &selection)
1007 : CompositeEditCommandImpl(document), m_charactersDeleted(0), m_selectionToCollapse(selection), m_hasSelectionToCollapse(true)
1008{
1009}
1010
1011DeleteCollapsibleWhitespaceCommandImpl::~DeleteCollapsibleWhitespaceCommandImpl()
1012{
1013}
1014
1015static bool shouldDeleteUpstreamPosition(const Position &pos)
1016{
1017 if (!pos.node()->isTextNode())
1018 return false;
1019
1020 RenderObject *renderer = pos.node()->renderer();
1021 if (!renderer)
1022 return true;
1023
1024 TextImpl *textNode = static_cast<TextImpl *>(pos.node());
1025 if (pos.offset() >= (long)textNode->length())
1026 return false;
1027
1028 if (pos.isLastRenderedPositionInEditableBlock())
1029 return false;
1030
1031 if (pos.isFirstRenderedPositionOnLine() || pos.isLastRenderedPositionOnLine())
1032 return false;
1033
1034 return false;
1035 // TODO we need to match DOM - Rendered offset first
1036// RenderText *textRenderer = static_cast<RenderText *>(renderer);
1037// for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
1038// if (pos.offset() < box->m_start) {
1039// return true;
1040// }
1041// if (pos.offset() >= box->m_start && pos.offset() < box->m_start + box->m_len)
1042// return false;
1043// }
1044//
1045// return true;
1046}
1047
1048Position DeleteCollapsibleWhitespaceCommandImpl::deleteWhitespace(const Position &pos)
1049{
1050 Position upstream = pos.equivalentUpstreamPosition();
1051 Position downstream = pos.equivalentDownstreamPosition();
1052#ifdef DEBUG_COMMANDS
1053 kDebug() << "[pos]" << pos << endl;
1054 kDebug() << "[upstream:downstream]" << upstream << downstream << endl;
1055 printEnclosingBlockTree(pos.node());
1056#endif
1057
1058 bool del = shouldDeleteUpstreamPosition(upstream);
1059#ifdef DEBUG_COMMANDS
1060 kDebug() << "[delete upstream]" << del << endl;
1061#endif
1062
1063 if (upstream == downstream)
1064 return upstream;
1065
1066#ifdef DEBUG_COMMANDS
1067 PositionIterator iter(upstream);
1068 kDebug() << "[before print]" << endl;
1069 for (iter.next(); iter.current() != downstream; iter.next())
1070 kDebug() << "[iterate]" << iter.current() << endl;
1071 kDebug() << "[after print]" << endl;
1072#endif
1073
1074 PositionIterator it(upstream);
1075 Position deleteStart = upstream;
1076 if (!del) {
1077 deleteStart = it.peekNext();
1078 if (deleteStart == downstream)
1079 return upstream;
1080 }
1081
1082 Position endingPosition = upstream;
1083
1084 while (it.current() != downstream) {
1085 Position next = it.peekNext();
1086#ifdef DEBUG_COMMANDS
1087 kDebug() << "[iterate and delete]" << next << endl;
1088#endif
1089 if (next.node() != deleteStart.node()) {
1090 // TODO assert(deleteStart.node()->isTextNode());
1091 if (deleteStart.node()->isTextNode()) {
1092 TextImpl *textNode = static_cast<TextImpl *>(deleteStart.node());
1093 unsigned long count = it.current().offset() - deleteStart.offset();
1094 if (count == textNode->length()) {
1095#ifdef DEBUG_COMMANDS
1096 kDebug(6200) << " removeNodeAndPrune 1:" << textNode;
1097#endif
1098 if (textNode == endingPosition.node())
1099 endingPosition = Position(next.node(), next.node()->caretMinOffset());
1100 removeNodeAndPrune(textNode);
1101 } else {
1102#ifdef DEBUG_COMMANDS
1103 kDebug(6200) << " deleteText 1:" << textNode << "t len:" << textNode->length()<<"start:" << deleteStart.offset() << "del len:" << (it.current().offset() - deleteStart.offset());
1104#endif
1105 deleteText(textNode, deleteStart.offset(), count);
1106 }
1107 } else {
1108#ifdef DEBUG_COMMANDS
1109 kDebug() << "[not text node is not supported yet]" << endl;
1110#endif
1111 }
1112 deleteStart = next;
1113 } else if (next == downstream) {
1114 assert(deleteStart.node() == downstream.node());
1115 assert(downstream.node()->isTextNode());
1116 TextImpl *textNode = static_cast<TextImpl *>(deleteStart.node());
1117 unsigned long count = downstream.offset() - deleteStart.offset();
1118 assert(count <= textNode->length());
1119 if (count == textNode->length()) {
1120#ifdef DEBUG_COMMANDS
1121 kDebug(6200) << " removeNodeAndPrune 2:"<<textNode;
1122#endif
1123 removeNodeAndPrune(textNode);
1124 } else {
1125#ifdef DEBUG_COMMANDS
1126 kDebug(6200) << " deleteText 2:"<< textNode<< "t len:" << textNode->length() <<"start:" <<deleteStart.offset() << "del len:" << count;
1127#endif
1128 deleteText(textNode, deleteStart.offset(), count);
1129 m_charactersDeleted = count;
1130 endingPosition = Position(downstream.node(), downstream.offset() - m_charactersDeleted);
1131 }
1132 }
1133
1134 it.setPosition(next);
1135 }
1136
1137 return endingPosition;
1138}
1139
1140void DeleteCollapsibleWhitespaceCommandImpl::doApply()
1141{
1142 // If selection has not been set to a custom selection when the command was created,
1143 // use the current ending selection.
1144 if (!m_hasSelectionToCollapse)
1145 m_selectionToCollapse = endingSelection();
1146 int state = m_selectionToCollapse.state();
1147 if (state == Selection::CARET) {
1148 Position endPosition = deleteWhitespace(m_selectionToCollapse.start());
1149 setEndingSelection(endPosition);
1150#ifdef DEBUG_COMMANDS
1151 kDebug(6200) << "-----------------------------------------------------";
1152#endif
1153 }
1154 else if (state == Selection::RANGE) {
1155 Position startPosition = deleteWhitespace(m_selectionToCollapse.start());
1156#ifdef DEBUG_COMMANDS
1157 kDebug(6200) << "-----------------------------------------------------";
1158#endif
1159 Position endPosition = m_selectionToCollapse.end();
1160 if (m_charactersDeleted > 0 && startPosition.node() == endPosition.node()) {
1161#ifdef DEBUG_COMMANDS
1162 kDebug(6200) << "adjust end position by" << m_charactersDeleted;
1163#endif
1164 endPosition = Position(endPosition.node(), endPosition.offset() - m_charactersDeleted);
1165 }
1166 endPosition = deleteWhitespace(endPosition);
1167 setEndingSelection(Selection(startPosition, endPosition));
1168#ifdef DEBUG_COMMANDS
1169 kDebug(6200) << "=====================================================";
1170#endif
1171 }
1172}
1173
1174//------------------------------------------------------------------------------------------
1175// DeleteSelectionCommandImpl
1176
1177DeleteSelectionCommandImpl::DeleteSelectionCommandImpl(DocumentImpl *document)
1178 : CompositeEditCommandImpl(document), m_hasSelectionToDelete(false)
1179{
1180}
1181
1182DeleteSelectionCommandImpl::DeleteSelectionCommandImpl(DocumentImpl *document, const Selection &selection)
1183 : CompositeEditCommandImpl(document), m_selectionToDelete(selection), m_hasSelectionToDelete(true)
1184{
1185}
1186
1187DeleteSelectionCommandImpl::~DeleteSelectionCommandImpl()
1188{
1189}
1190
1191void DeleteSelectionCommandImpl::joinTextNodesWithSameStyle()
1192{
1193 Selection selection = endingSelection();
1194
1195 if (selection.state() != Selection::CARET)
1196 return;
1197
1198 Position pos(selection.start());
1199
1200 if (!pos.node()->isTextNode())
1201 return;
1202
1203 TextImpl *textNode = static_cast<TextImpl *>(pos.node());
1204
1205 if (pos.offset() == 0) {
1206 PositionIterator it(pos);
1207 Position prev = it.previous();
1208 if (prev == pos)
1209 return;
1210 if (prev.node()->isTextNode()) {
1211 TextImpl *prevTextNode = static_cast<TextImpl *>(prev.node());
1212 if (textNodesAreJoinable(prevTextNode, textNode)) {
1213 joinTextNodes(prevTextNode, textNode);
1214 setEndingSelection(Position(textNode, prevTextNode->length()));
1215#ifdef DEBUG_COMMANDS
1216 kDebug(6200) << "joinTextNodesWithSameStyle [1]";
1217#endif
1218 }
1219 }
1220 } else if (pos.offset() == (long)textNode->length()) {
1221 PositionIterator it(pos);
1222 Position next = it.next();
1223 if (next == pos)
1224 return;
1225 if (next.node()->isTextNode()) {
1226 TextImpl *nextTextNode = static_cast<TextImpl *>(next.node());
1227 if (textNodesAreJoinable(textNode, nextTextNode)) {
1228 joinTextNodes(textNode, nextTextNode);
1229 setEndingSelection(Position(nextTextNode, pos.offset()));
1230#ifdef DEBUG_COMMANDS
1231 kDebug(6200) << "joinTextNodesWithSameStyle [2]";
1232#endif
1233 }
1234 }
1235 }
1236}
1237
1238bool DeleteSelectionCommandImpl::containsOnlyWhitespace(const Position &start, const Position &end)
1239{
1240 // Returns whether the range contains only whitespace characters.
1241 // This is inclusive of the start, but not of the end.
1242 PositionIterator it(start);
1243 while (!it.atEnd()) {
1244 if (!it.current().node()->isTextNode())
1245 return false;
1246 const DOMString &text = static_cast<TextImpl *>(it.current().node())->data();
1247 // EDIT FIXME: signed/unsigned mismatch
1248 if (text.length() > INT_MAX)
1249 return false;
1250 if (it.current().offset() < (int)text.length() && !isWS(text[it.current().offset()]))
1251 return false;
1252 it.next();
1253 if (it.current() == end)
1254 break;
1255 }
1256 return true;
1257}
1258
1259void DeleteSelectionCommandImpl::deleteContentInsideNode(NodeImpl *node, int startOffset, int endOffset)
1260{
1261#ifdef DEBUG_COMMANDS
1262 kDebug() << "[Delete content inside node]" << node << startOffset << endOffset << endl;
1263#endif
1264 if (node->isTextNode()) {
1265 // check if nothing to delete
1266 if (startOffset == endOffset)
1267 return;
1268 // check if node is fully covered then remove node completely
1269 if (!startOffset && endOffset == node->maxOffset()) {
1270 removeNodeAndPrune(node);
1271 return;
1272 }
1273 // delete only substring
1274 deleteText(static_cast<TextImpl*>(node), startOffset, endOffset - startOffset);
1275 return;
1276 }
1277#ifdef DEBUG_COMMANDS
1278 kDebug() << "[non-text node] not supported" << endl;
1279#endif
1280}
1281
1282void DeleteSelectionCommandImpl::deleteContentBeforeOffset(NodeImpl *node, int offset)
1283{
1284 deleteContentInsideNode(node, 0, offset);
1285}
1286
1287void DeleteSelectionCommandImpl::deleteContentAfterOffset(NodeImpl *node, int offset)
1288{
1289 if (node->isTextNode())
1290 deleteContentInsideNode(node, offset, node->maxOffset());
1291}
1292
1293void DeleteSelectionCommandImpl::doApply()
1294{
1295 // If selection has not been set to a custom selection when the command was created,
1296 // use the current ending selection.
1297 if (!m_hasSelectionToDelete)
1298 m_selectionToDelete = endingSelection();
1299
1300 if (m_selectionToDelete.state() != Selection::RANGE)
1301 return;
1302
1303 deleteCollapsibleWhitespace(m_selectionToDelete);
1304 Selection selection = endingSelection();
1305
1306 Position upstreamStart(selection.start().equivalentUpstreamPosition());
1307 Position downstreamStart(selection.start().equivalentDownstreamPosition());
1308 Position upstreamEnd(selection.end().equivalentUpstreamPosition());
1309 Position downstreamEnd(selection.end().equivalentDownstreamPosition());
1310
1311 NodeImpl *startBlock = upstreamStart.node()->enclosingBlockFlowElement();
1312 NodeImpl *endBlock = downstreamEnd.node()->enclosingBlockFlowElement();
1313
1314#ifdef DEBUG_COMMANDS
1315 kDebug() << "[Delete:Start]" << upstreamStart << downstreamStart << endl;
1316 kDebug() << "[Delete:End]" << upstreamEnd << downstreamEnd << endl;
1317 printEnclosingBlockTree(upstreamStart.node());
1318#endif
1319 if (startBlock != endBlock)
1320 printEnclosingBlockTree(downstreamEnd.node());
1321
1322 if (upstreamStart == downstreamEnd)
1323 // after collapsing whitespace, selection is empty...no work to do
1324 return;
1325
1326 // remove all the nodes that are completely covered by the selection
1327 if (upstreamStart.node() != downstreamEnd.node()) {
1328 NodeImpl *node, *next;
1329 for (node = upstreamStart.node()->traverseNextNode(); node && node != downstreamEnd.node(); node = next) {
1330#ifdef DEBUG_COMMANDS
1331 kDebug() << "[traverse and delete]" << node << (node->renderer() && node->renderer()->isEditable()) << endl;
1332#endif
1333 next = node->traverseNextNode();
1334 if (node->renderer() && node->renderer()->isEditable())
1335 removeNode(node); // removeAndPrune?
1336 }
1337 }
1338
1339 // if we have different blocks then merge content of the second into first one
1340 if (startBlock != endBlock && startBlock->parentNode() == endBlock->parentNode()) {
1341 NodeImpl *node = endBlock->firstChild();
1342 while (node) {
1343 NodeImpl *moveNode = node;
1344 node = node->nextSibling();
1345 removeNode(moveNode);
1346 appendNode(startBlock, moveNode);
1347 }
1348 }
1349
1350 if (upstreamStart.node() == downstreamEnd.node())
1351 deleteContentInsideNode(upstreamEnd.node(), upstreamStart.offset(), downstreamEnd.offset());
1352 else {
1353 deleteContentAfterOffset(upstreamStart.node(), upstreamStart.offset());
1354 deleteContentBeforeOffset(downstreamEnd.node(), downstreamEnd.offset());
1355 }
1356
1357 setEndingSelection(upstreamStart);
1358#if 0
1359 Position endingPosition;
1360 bool adjustEndingPositionDownstream = false;
1361
1362 bool onlyWhitespace = containsOnlyWhitespace(upstreamStart, downstreamEnd);
1363 kDebug() << "[OnlyWhitespace]" << onlyWhitespace << endl;
1364
1365 bool startCompletelySelected = !onlyWhitespace &&
1366 (downstreamStart.offset() <= downstreamStart.node()->caretMinOffset() &&
1367 ((downstreamStart.node() != upstreamEnd.node()) ||
1368 (upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset())));
1369
1370 bool endCompletelySelected = !onlyWhitespace &&
1371 (upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset() &&
1372 ((downstreamStart.node() != upstreamEnd.node()) ||
1373 (downstreamStart.offset() <= downstreamStart.node()->caretMinOffset())));
1374
1375 kDebug() << "[{start:end}CompletelySelected]" << startCompletelySelected << endCompletelySelected << endl;
1376
1377 unsigned long startRenderedOffset = downstreamStart.renderedOffset();
1378
1379 bool startAtStartOfRootEditableElement = startRenderedOffset == 0 && downstreamStart.inFirstEditableInRootEditableElement();
1380 bool startAtStartOfBlock = startAtStartOfRootEditableElement ||
1381 (startRenderedOffset == 0 && downstreamStart.inFirstEditableInContainingEditableBlock());
1382 bool endAtEndOfBlock = downstreamEnd.isLastRenderedPositionInEditableBlock();
1383
1384 kDebug() << "[startAtStartOfRootEditableElement]" << startAtStartOfRootEditableElement << endl;
1385 kDebug() << "[startAtStartOfBlock]" << startAtStartOfBlock << endl;
1386 kDebug() << "[endAtEndOfBlock]" << endAtEndOfBlock << endl;
1387
1388 NodeImpl *startBlock = upstreamStart.node()->enclosingBlockFlowElement();
1389 NodeImpl *endBlock = downstreamEnd.node()->enclosingBlockFlowElement();
1390 bool startBlockEndBlockAreSiblings = startBlock->parentNode() == endBlock->parentNode();
1391
1392 kDebug() << "[startBlockEndBlockAreSiblings]" << startBlockEndBlockAreSiblings << startBlock << endBlock << endl;
1393
1394 debugPosition("upstreamStart: ", upstreamStart);
1395 debugPosition("downstreamStart: ", downstreamStart);
1396 debugPosition("upstreamEnd: ", upstreamEnd);
1397 debugPosition("downstreamEnd: ", downstreamEnd);
1398 kDebug(6200) << "start selected:" << (startCompletelySelected ? "YES" : "NO");
1399 kDebug(6200) << "at start block:" << (startAtStartOfBlock ? "YES" : "NO");
1400 kDebug(6200) << "at start root block:"<< (startAtStartOfRootEditableElement ? "YES" : "NO");
1401 kDebug(6200) << "at end block:"<< (endAtEndOfBlock ? "YES" : "NO");
1402 kDebug(6200) << "only whitespace:"<< (onlyWhitespace ? "YES" : "NO");
1403
1404 // Determine where to put the caret after the deletion
1405 if (startAtStartOfBlock) {
1406 kDebug(6200) << "ending position case 1";
1407 endingPosition = Position(startBlock, 0);
1408 adjustEndingPositionDownstream = true;
1409 } else if (!startCompletelySelected) {
1410 kDebug(6200) << "ending position case 2";
1411 endingPosition = upstreamEnd; // FIXME ??????????? upstreamStart;
1412 if (upstreamStart.node()->id() == ID_BR && upstreamStart.offset() == 1)
1413 adjustEndingPositionDownstream = true;
1414 } else if (upstreamStart != downstreamStart) {
1415 kDebug(6200) << "ending position case 3";
1416 endingPosition = upstreamStart;
1417 if (upstreamStart.node()->id() == ID_BR && upstreamStart.offset() == 1)
1418 adjustEndingPositionDownstream = true;
1419 }
1420
1421 //
1422 // Figure out the whitespace conversions to do
1423 //
1424 if ((startAtStartOfBlock && !endAtEndOfBlock) || (!startCompletelySelected && adjustEndingPositionDownstream)) {
1425 // convert trailing whitespace
1426 Position trailing = trailingWhitespacePosition(downstreamEnd.equivalentDownstreamPosition());
1427 if (trailing.notEmpty()) {
1428 debugPosition("convertTrailingWhitespace: ", trailing);
1429 Position collapse = trailing.nextCharacterPosition();
1430 if (collapse != trailing)
1431 deleteCollapsibleWhitespace(collapse);
1432 TextImpl *textNode = static_cast<TextImpl *>(trailing.node());
1433 replaceText(textNode, trailing.offset(), 1, nonBreakingSpaceString());
1434 }
1435 } else if (!startAtStartOfBlock && endAtEndOfBlock) {
1436 // convert leading whitespace
1437 Position leading = leadingWhitespacePosition(upstreamStart.equivalentUpstreamPosition());
1438 if (leading.notEmpty()) {
1439 debugPosition("convertLeadingWhitespace: ", leading);
1440 TextImpl *textNode = static_cast<TextImpl *>(leading.node());
1441 replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
1442 }
1443 } else if (!startAtStartOfBlock && !endAtEndOfBlock) {
1444 // convert contiguous whitespace
1445 Position leading = leadingWhitespacePosition(upstreamStart.equivalentUpstreamPosition());
1446 Position trailing = trailingWhitespacePosition(downstreamEnd.equivalentDownstreamPosition());
1447 if (leading.notEmpty() && trailing.notEmpty()) {
1448 debugPosition("convertLeadingWhitespace [contiguous]: ", leading);
1449 TextImpl *textNode = static_cast<TextImpl *>(leading.node());
1450 replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
1451 }
1452 }
1453
1454 //
1455 // Do the delete
1456 //
1457 NodeImpl *n = downstreamStart.node()->traverseNextNode();
1458 kDebug() << "[n]" << n << endl;
1459
1460 // work on start node
1461 if (startCompletelySelected) {
1462 kDebug(6200) << "start node delete case 1";
1463 removeNodeAndPrune(downstreamStart.node(), startBlock);
1464 } else if (onlyWhitespace) {
1465 // Selection only contains whitespace. This is really a special-case to
1466 // handle significant whitespace that is collapsed at the end of a line,
1467 // but also handles deleting a space in mid-line.
1468 kDebug(6200) << "start node delete case 2";
1469 assert(upstreamStart.node()->isTextNode());
1470 TextImpl *text = static_cast<TextImpl *>(upstreamStart.node());
1471 int offset = upstreamStart.offset();
1472 // EDIT FIXME: Signed/unsigned mismatch
1473 int length = text->length();
1474 if (length == upstreamStart.offset())
1475 offset--;
1476 // FIXME ??? deleteText(text, offset, 1);
1477 } else if (downstreamStart.node()->isTextNode()) {
1478 kDebug(6200) << "start node delete case 3";
1479 TextImpl *text = static_cast<TextImpl *>(downstreamStart.node());
1480 int endOffset = text == upstreamEnd.node() ? upstreamEnd.offset() : text->length();
1481 if (endOffset > downstreamStart.offset()) {
1482 deleteText(text, downstreamStart.offset(), endOffset - downstreamStart.offset());
1483 }
1484 } else {
1485 // we have clipped the end of a non-text element
1486 // the offset must be 1 here. if it is, do nothing and move on.
1487 kDebug(6200) << "start node delete case 4";
1488 assert(downstreamStart.offset() == 1);
1489 }
1490
1491 if (n && !onlyWhitespace && downstreamStart.node() != upstreamEnd.node()) {
1492 // work on intermediate nodes
1493 while (n && n != upstreamEnd.node()) {
1494 NodeImpl *d = n;
1495 n = n->traverseNextNode();
1496 if (d->renderer() && d->renderer()->isEditable())
1497 removeNodeAndPrune(d, startBlock);
1498 }
1499 if (!n)
1500 return;
1501
1502 // work on end node
1503 assert(n == upstreamEnd.node());
1504 if (endCompletelySelected) {
1505 removeNodeAndPrune(upstreamEnd.node(), startBlock);
1506 }
1507 else if (upstreamEnd.node()->isTextNode()) {
1508 if (upstreamEnd.offset() > 0) {
1509 TextImpl *text = static_cast<TextImpl *>(upstreamEnd.node());
1510 deleteText(text, 0, upstreamEnd.offset());
1511 }
1512 }
1513 else {
1514 // we have clipped the beginning of a non-text element
1515 // the offset must be 0 here. if it is, do nothing and move on.
1516 assert(downstreamStart.offset() == 0);
1517 }
1518 }
1519
1520 // Do block merge if start and end of selection are in different blocks
1521 // and the blocks are siblings. This is a first cut at this rule arrived
1522 // at by doing a bunch of edits and settling on the behavior that made
1523 // the most sense. This could change in the future as we get more
1524 // experience with how this should behave.
1525 if (startBlock != endBlock && startBlockEndBlockAreSiblings) {
1526 kDebug(6200) << "merging content to start block";
1527 NodeImpl *node = endBlock->firstChild();
1528 while (node) {
1529 NodeImpl *moveNode = node;
1530 node = node->nextSibling();
1531 removeNode(moveNode);
1532 appendNode(startBlock, moveNode);
1533 }
1534 }
1535
1536 if (adjustEndingPositionDownstream) {
1537 kDebug(6200) << "adjust ending position downstream";
1538 endingPosition = endingPosition.equivalentDownstreamPosition();
1539 }
1540
1541 debugPosition("ending position: ", endingPosition);
1542 setEndingSelection(endingPosition);
1543
1544 kDebug(6200) << "-----------------------------------------------------";
1545#endif
1546}
1547
1548//------------------------------------------------------------------------------------------
1549// DeleteTextCommandImpl
1550
1551DeleteTextCommandImpl::DeleteTextCommandImpl(DocumentImpl *document, TextImpl *node, long offset, long count)
1552 : EditCommandImpl(document), m_node(node), m_offset(offset), m_count(count)
1553{
1554 assert(m_node);
1555 assert(m_offset >= 0);
1556 assert(m_count >= 0);
1557
1558 m_node->ref();
1559}
1560
1561DeleteTextCommandImpl::~DeleteTextCommandImpl()
1562{
1563 if (m_node)
1564 m_node->deref();
1565}
1566
1567void DeleteTextCommandImpl::doApply()
1568{
1569 assert(m_node);
1570
1571 int exceptionCode = 0;
1572 m_text = m_node->substringData(m_offset, m_count, exceptionCode);
1573 assert(exceptionCode == 0);
1574
1575 m_node->deleteData(m_offset, m_count, exceptionCode);
1576 assert(exceptionCode == 0);
1577}
1578
1579void DeleteTextCommandImpl::doUnapply()
1580{
1581 assert(m_node);
1582 assert(!m_text.isEmpty());
1583
1584 int exceptionCode = 0;
1585 m_node->insertData(m_offset, m_text, exceptionCode);
1586 assert(exceptionCode == 0);
1587}
1588
1589//------------------------------------------------------------------------------------------
1590// InputNewlineCommandImpl
1591
1592InputNewlineCommandImpl::InputNewlineCommandImpl(DocumentImpl *document)
1593 : CompositeEditCommandImpl(document)
1594{
1595}
1596
1597InputNewlineCommandImpl::~InputNewlineCommandImpl()
1598{
1599}
1600
1601void InputNewlineCommandImpl::insertNodeAfterPosition(NodeImpl *node, const Position &pos)
1602{
1603 // Insert the BR after the caret position. In the case the
1604 // position is a block, do an append. We don't want to insert
1605 // the BR *after* the block.
1606 Position upstream(pos.equivalentUpstreamPosition());
1607 NodeImpl *cb = pos.node()->enclosingBlockFlowElement();
1608 if (cb == pos.node())
1609 appendNode(cb, node);
1610 else
1611 insertNodeAfter(node, pos.node());
1612}
1613
1614void InputNewlineCommandImpl::insertNodeBeforePosition(NodeImpl *node, const Position &pos)
1615{
1616 // Insert the BR after the caret position. In the case the
1617 // position is a block, do an append. We don't want to insert
1618 // the BR *before* the block.
1619 Position upstream(pos.equivalentUpstreamPosition());
1620 NodeImpl *cb = pos.node()->enclosingBlockFlowElement();
1621 if (cb == pos.node())
1622 appendNode(cb, node);
1623 else
1624 insertNodeBefore(node, pos.node());
1625}
1626
1627void InputNewlineCommandImpl::doApply()
1628{
1629 deleteSelection();
1630 Selection selection = endingSelection();
1631 int exceptionCode = 0;
1632
1633 NodeImpl *enclosingBlock = selection.start().node()->enclosingBlockFlowElement();
1634 kDebug() << enclosingBlock->nodeName() << endl;
1635 if (enclosingBlock->id() == ID_LI) {
1636 // need to insert new list item or split existing one into 2
1637 // consider example: <li>x<u>x<b>x|x</b>x</u>x</li> (| - caret position)
1638 // result should look like: <li>x<u>x<b>x</b></u></li><li><u>|x<b>x</b></u></li>
1639 // idea is to walk up to the li item and split and reattach correspondent nodes
1640#ifdef DEBUG_COMMANDS
1641 kDebug() << "[insert new list item]" << selection << endl;
1642 printEnclosingBlockTree(selection.start().node());
1643#endif
1644 Position pos(selection.start().equivalentDownstreamPosition());
1645 NodeImpl *node = pos.node();
1646 bool atBlockStart = pos.atStartOfContainingEditableBlock();
1647 bool atBlockEnd = pos.isLastRenderedPositionInEditableBlock();
1648 // split text node into 2 if we are in the middle
1649 if (node->isTextNode() && !atBlockStart && !atBlockEnd) {
1650 TextImpl *textNode = static_cast<TextImpl*>(node);
1651 TextImpl *textBeforeNode = document()->createTextNode(textNode->substringData(0, selection.start().offset(), exceptionCode));
1652 deleteText(textNode, 0, pos.offset());
1653 insertNodeBefore(textBeforeNode, textNode);
1654 pos = Position(textNode, 0);
1655 setEndingSelection(pos);
1656
1657 // walk up and reattach
1658 while (true) {
1659#ifdef DEBUG_COMMANDS
1660 kDebug() << "[handle node]" << node << endl;
1661 printEnclosingBlockTree(enclosingBlock->parent());
1662#endif
1663 NodeImpl *parent = node->parent();
1664 // FIXME copy attributes, styles etc too
1665 RefPtr<NodeImpl> newParent = parent->cloneNode(false);
1666 insertNodeAfter(newParent.get(), parent);
1667 for (NodeImpl *nextSibling = 0; node; node = nextSibling) {
1668#ifdef DEBUG_COMMANDS
1669 kDebug() << "[reattach sibling]" << node << endl;
1670#endif
1671 nextSibling = node->nextSibling();
1672 removeNode(node);
1673 appendNode(newParent.get(), node);
1674 }
1675 node = newParent.get();
1676 if (parent == enclosingBlock)
1677 break;
1678 }
1679 } else if (node->isTextNode()) {
1680 // insert <br> node either as previous list or the next one
1681 if (atBlockStart) {
1682 ElementImpl *listItem = document()->createHTMLElement("LI");
1683 insertNodeBefore(listItem, enclosingBlock);
1684 } else {
1685 ElementImpl *listItem = document()->createHTMLElement("LI");
1686 insertNodeAfter(listItem, enclosingBlock);
1687 }
1688 }
1689
1690#ifdef DEBUG_COMMANDS
1691 kDebug() << "[result]" << endl;
1692 printEnclosingBlockTree(enclosingBlock->parent());
1693#endif
1694 // FIXME set selection after operation
1695 return;
1696 }
1697
1698 ElementImpl *breakNode = document()->createHTMLElement("BR");
1699 // assert(exceptionCode == 0);
1700
1701#ifdef DEBUG_COMMANDS
1702 kDebug() << "[insert break]" << selection << endl;
1703 printEnclosingBlockTree(enclosingBlock);
1704#endif
1705
1706 NodeImpl *nodeToInsert = breakNode;
1707 // Handle the case where there is a typing style.
1708 if (document()->part()->editor()->typingStyle()) {
1709 int exceptionCode = 0;
1710 ElementImpl *styleElement = createTypingStyleElement();
1711 styleElement->appendChild(breakNode, exceptionCode);
1712 assert(exceptionCode == 0);
1713 nodeToInsert = styleElement;
1714 }
1715
1716 Position pos(selection.start().equivalentDownstreamPosition());
1717 bool atStart = pos.offset() <= pos.node()->caretMinOffset();
1718 bool atEndOfBlock = pos.isLastRenderedPositionInEditableBlock();
1719
1720#ifdef DEBUG_COMMANDS
1721 kDebug() << "[pos]" << pos << atStart << atEndOfBlock << endl;
1722#endif
1723
1724 if (atEndOfBlock) {
1725#ifdef DEBUG_COMMANDS
1726 kDebug(6200) << "input newline case 1";
1727#endif
1728 // Insert an "extra" BR at the end of the block. This makes the "real" BR we want
1729 // to insert appear in the rendering without any significant side effects (and no
1730 // real worries either since you can't arrow past this extra one.
1731 insertNodeAfterPosition(nodeToInsert, pos);
1732 exceptionCode = 0;
1733 ElementImpl *extraBreakNode = document()->createHTMLElement("BR");
1734// assert(exceptionCode == 0);
1735 insertNodeAfter(extraBreakNode, nodeToInsert);
1736 setEndingSelection(Position(extraBreakNode, 0));
1737 } else if (atStart) {
1738#ifdef DEBUG_COMMANDS
1739 kDebug(6200) << "input newline case 2";
1740#endif
1741 // Insert node, but place the caret into index 0 of the downstream
1742 // position. This will make the caret appear after the break, and as we know
1743 // there is content at that location, this is OK.
1744 insertNodeBeforePosition(nodeToInsert, pos);
1745 setEndingSelection(Position(pos.node(), 0));
1746 } else {
1747 // Split a text node
1748 // FIXME it's possible that we create empty text node now if we're at the end of text
1749 // maybe we should handle this case specially and not create it
1750#ifdef DEBUG_COMMANDS
1751 kDebug(6200) << "input newline case 3";
1752#endif
1753 assert(pos.node()->isTextNode());
1754 TextImpl *textNode = static_cast<TextImpl *>(pos.node());
1755 TextImpl *textBeforeNode = document()->createTextNode(textNode->substringData(0, selection.start().offset(), exceptionCode));
1756 deleteText(textNode, 0, selection.start().offset());
1757 insertNodeBefore(textBeforeNode, textNode);
1758 insertNodeBefore(nodeToInsert, textNode);
1759 setEndingSelection(Position(textNode, 0));
1760 }
1761}
1762
1763//------------------------------------------------------------------------------------------
1764// InputTextCommandImpl
1765
1766InputTextCommandImpl::InputTextCommandImpl(DocumentImpl *document)
1767 : CompositeEditCommandImpl(document), m_charactersAdded(0)
1768{
1769}
1770
1771InputTextCommandImpl::~InputTextCommandImpl()
1772{
1773}
1774
1775void InputTextCommandImpl::doApply()
1776{
1777}
1778
1779void InputTextCommandImpl::input(const DOMString &text)
1780{
1781 execute(text);
1782}
1783
1784void InputTextCommandImpl::deleteCharacter()
1785{
1786 assert(state() == Applied);
1787
1788 Selection selection = endingSelection();
1789
1790 if (!selection.start().node()->isTextNode())
1791 return;
1792
1793 int exceptionCode = 0;
1794 int offset = selection.start().offset() - 1;
1795 if (offset >= selection.start().node()->caretMinOffset()) {
1796 TextImpl *textNode = static_cast<TextImpl *>(selection.start().node());
1797 textNode->deleteData(offset, 1, exceptionCode);
1798 assert(exceptionCode == 0);
1799 selection = Selection(Position(textNode, offset));
1800 setEndingSelection(selection);
1801 m_charactersAdded--;
1802 }
1803}
1804
1805Position InputTextCommandImpl::prepareForTextInsertion(bool adjustDownstream)
1806{
1807 // Prepare for text input by looking at the current position.
1808 // It may be necessary to insert a text node to receive characters.
1809 Selection selection = endingSelection();
1810 assert(selection.state() == Selection::CARET);
1811
1812#ifdef DEBUG_COMMANDS
1813 kDebug() << "[prepare selection]" << selection << endl;
1814#endif
1815
1816 Position pos = selection.start();
1817 if (adjustDownstream)
1818 pos = pos.equivalentDownstreamPosition();
1819 else
1820 pos = pos.equivalentUpstreamPosition();
1821
1822#ifdef DEBUG_COMMANDS
1823 kDebug() << "[prepare position]" << pos << endl;
1824#endif
1825
1826 if (!pos.node()->isTextNode()) {
1827 NodeImpl *textNode = document()->createEditingTextNode("");
1828 NodeImpl *nodeToInsert = textNode;
1829 if (document()->part()->editor()->typingStyle()) {
1830 int exceptionCode = 0;
1831 ElementImpl *styleElement = createTypingStyleElement();
1832 styleElement->appendChild(textNode, exceptionCode);
1833 assert(exceptionCode == 0);
1834 nodeToInsert = styleElement;
1835 }
1836
1837 // Now insert the node in the right place
1838 if (pos.node()->isEditableBlock()) {
1839 kDebug(6200) << "prepareForTextInsertion case 1";
1840 appendNode(pos.node(), nodeToInsert);
1841 } else if (pos.node()->id() == ID_BR && pos.offset() == 1) {
1842 kDebug(6200) << "prepareForTextInsertion case 2";
1843 insertNodeAfter(nodeToInsert, pos.node());
1844 } else if (pos.node()->caretMinOffset() == pos.offset()) {
1845 kDebug(6200) << "prepareForTextInsertion case 3";
1846 insertNodeBefore(nodeToInsert, pos.node());
1847 } else if (pos.node()->caretMaxOffset() == pos.offset()) {
1848 kDebug(6200) << "prepareForTextInsertion case 4";
1849 insertNodeAfter(nodeToInsert, pos.node());
1850 } else
1851 assert(false);
1852
1853 pos = Position(textNode, 0);
1854 } else {
1855 // Handle the case where there is a typing style.
1856 if (document()->part()->editor()->typingStyle()) {
1857 if (pos.node()->isTextNode() && pos.offset() > pos.node()->caretMinOffset() && pos.offset() < pos.node()->caretMaxOffset()) {
1858 // Need to split current text node in order to insert a span.
1859 TextImpl *text = static_cast<TextImpl *>(pos.node());
1860 RefPtr<SplitTextNodeCommandImpl> cmd = new SplitTextNodeCommandImpl(document(), text, pos.offset());
1861 applyCommandToComposite(cmd);
1862 setEndingSelection(Position(cmd->node(), 0));
1863 }
1864
1865 int exceptionCode = 0;
1866 TextImpl *editingTextNode = document()->createEditingTextNode("");
1867
1868 ElementImpl *styleElement = createTypingStyleElement();
1869 styleElement->appendChild(editingTextNode, exceptionCode);
1870 assert(exceptionCode == 0);
1871
1872 NodeImpl *node = endingSelection().start().node();
1873 if (endingSelection().start().isLastRenderedPositionOnLine())
1874 insertNodeAfter(styleElement, node);
1875 else
1876 insertNodeBefore(styleElement, node);
1877 pos = Position(editingTextNode, 0);
1878 }
1879 }
1880 return pos;
1881}
1882
1883void InputTextCommandImpl::execute(const DOMString &text)
1884{
1885#ifdef DEBUG_COMMANDS
1886 kDebug() << "[execute command]" << text << endl;
1887#endif
1888 Selection selection = endingSelection();
1889#ifdef DEBUG_COMMANDS
1890 kDebug() << "[ending selection]" << selection << endl;
1891#endif
1892 bool adjustDownstream = selection.start().isFirstRenderedPositionOnLine();
1893#ifdef DEBUG_COMMANDS
1894 kDebug() << "[adjust]" << adjustDownstream << endl;
1895#endif
1896
1897#ifdef DEBUG_COMMANDS
1898 printEnclosingBlockTree(selection.start().node());
1899#endif
1900
1901 // Delete the current selection, or collapse whitespace, as needed
1902 if (selection.state() == Selection::RANGE)
1903 deleteSelection();
1904 else
1905 deleteCollapsibleWhitespace();
1906
1907#ifdef DEBUG_COMMANDS
1908 kDebug() << "[after collapsible whitespace deletion]" << endl;
1909 printEnclosingBlockTree(selection.start().node());
1910#endif
1911
1912 // EDIT FIXME: Need to take typing style from upstream text, if any.
1913
1914 // Make sure the document is set up to receive text
1915 Position pos = prepareForTextInsertion(adjustDownstream);
1916#ifdef DEBUG_COMMANDS
1917 kDebug() << "[after prepare]" << pos << endl;
1918#endif
1919
1920 TextImpl *textNode = static_cast<TextImpl *>(pos.node());
1921 long offset = pos.offset();
1922
1923#ifdef DEBUG_COMMANDS
1924 kDebug() << "[insert at]" << textNode << offset << endl;
1925#endif
1926
1927 // This is a temporary implementation for inserting adjoining spaces
1928 // into a document. We are working on a CSS-related whitespace solution
1929 // that will replace this some day.
1930 if (isWS(text))
1931 insertSpace(textNode, offset);
1932 else {
1933 const DOMString &existingText = textNode->data();
1934 if (textNode->length() >= 2 && offset >= 2 && isNBSP(existingText[offset - 1]) && !isWS(existingText[offset - 2])) {
1935 // DOM looks like this:
1936 // character nbsp caret
1937 // As we are about to insert a non-whitespace character at the caret
1938 // convert the nbsp to a regular space.
1939 // EDIT FIXME: This needs to be improved some day to convert back only
1940 // those nbsp's added by the editor to make rendering come out right.
1941 replaceText(textNode, offset - 1, 1, " ");
1942 }
1943 insertText(textNode, offset, text);
1944 }
1945 setEndingSelection(Position(textNode, offset + text.length()));
1946 m_charactersAdded += text.length();
1947}
1948
1949void InputTextCommandImpl::insertSpace(TextImpl *textNode, unsigned long offset)
1950{
1951 assert(textNode);
1952
1953 DOMString text(textNode->data());
1954
1955 // count up all spaces and newlines in front of the caret
1956 // delete all collapsed ones
1957 // this will work out OK since the offset we have been passed has been upstream-ized
1958 int count = 0;
1959 for (unsigned int i = offset; i < text.length(); i++) {
1960 if (isWS(text[i]))
1961 count++;
1962 else
1963 break;
1964 }
1965 if (count > 0) {
1966 // By checking the character at the downstream position, we can
1967 // check if there is a rendered WS at the caret
1968 Position pos(textNode, offset);
1969 Position downstream = pos.equivalentDownstreamPosition();
1970 if (downstream.offset() < (long)text.length() && isWS(text[downstream.offset()]))
1971 count--; // leave this WS in
1972 if (count > 0)
1973 deleteText(textNode, offset, count);
1974 }
1975
1976 if (offset > 0 && offset <= text.length() - 1 && !isWS(text[offset]) && !isWS(text[offset - 1])) {
1977 // insert a "regular" space
1978 insertText(textNode, offset, " ");
1979 return;
1980 }
1981
1982 if (text.length() >= 2 && offset >= 2 && isNBSP(text[offset - 2]) && isNBSP(text[offset - 1])) {
1983 // DOM looks like this:
1984 // nbsp nbsp caret
1985 // insert a space between the two nbsps
1986 insertText(textNode, offset - 1, " ");
1987 return;
1988 }
1989
1990 // insert an nbsp
1991 insertText(textNode, offset, nonBreakingSpaceString());
1992}
1993
1994//------------------------------------------------------------------------------------------
1995// InsertNodeBeforeCommandImpl
1996
1997InsertNodeBeforeCommandImpl::InsertNodeBeforeCommandImpl(DocumentImpl *document, NodeImpl *insertChild, NodeImpl *refChild)
1998 : EditCommandImpl(document), m_insertChild(insertChild), m_refChild(refChild)
1999{
2000 assert(m_insertChild);
2001 m_insertChild->ref();
2002
2003 assert(m_refChild);
2004 m_refChild->ref();
2005}
2006
2007InsertNodeBeforeCommandImpl::~InsertNodeBeforeCommandImpl()
2008{
2009 if (m_insertChild)
2010 m_insertChild->deref();
2011 if (m_refChild)
2012 m_refChild->deref();
2013}
2014
2015void InsertNodeBeforeCommandImpl::doApply()
2016{
2017 assert(m_insertChild);
2018 assert(m_refChild);
2019 assert(m_refChild->parentNode());
2020
2021 int exceptionCode = 0;
2022 m_refChild->parentNode()->insertBefore(m_insertChild, m_refChild, exceptionCode);
2023 assert(exceptionCode == 0);
2024}
2025
2026void InsertNodeBeforeCommandImpl::doUnapply()
2027{
2028 assert(m_insertChild);
2029 assert(m_refChild);
2030 assert(m_refChild->parentNode());
2031
2032 int exceptionCode = 0;
2033 m_refChild->parentNode()->removeChild(m_insertChild, exceptionCode);
2034 assert(exceptionCode == 0);
2035}
2036
2037//------------------------------------------------------------------------------------------
2038// InsertTextCommandImpl
2039
2040InsertTextCommandImpl::InsertTextCommandImpl(DocumentImpl *document, TextImpl *node, long offset, const DOMString &text)
2041 : EditCommandImpl(document), m_node(node), m_offset(offset)
2042{
2043 assert(m_node);
2044 assert(m_offset >= 0);
2045 assert(text.length() > 0);
2046
2047 m_node->ref();
2048 m_text = text.copy(); // make a copy to ensure that the string never changes
2049}
2050
2051InsertTextCommandImpl::~InsertTextCommandImpl()
2052{
2053 if (m_node)
2054 m_node->deref();
2055}
2056
2057void InsertTextCommandImpl::doApply()
2058{
2059 assert(m_node);
2060 assert(!m_text.isEmpty());
2061
2062 int exceptionCode = 0;
2063 m_node->insertData(m_offset, m_text, exceptionCode);
2064 assert(exceptionCode == 0);
2065}
2066
2067void InsertTextCommandImpl::doUnapply()
2068{
2069 assert(m_node);
2070 assert(!m_text.isEmpty());
2071
2072 int exceptionCode = 0;
2073 m_node->deleteData(m_offset, m_text.length(), exceptionCode);
2074 assert(exceptionCode == 0);
2075}
2076
2077//------------------------------------------------------------------------------------------
2078// JoinTextNodesCommandImpl
2079
2080JoinTextNodesCommandImpl::JoinTextNodesCommandImpl(DocumentImpl *document, TextImpl *text1, TextImpl *text2)
2081 : EditCommandImpl(document), m_text1(text1), m_text2(text2)
2082{
2083 assert(m_text1);
2084 assert(m_text2);
2085 assert(m_text1->nextSibling() == m_text2);
2086 assert(m_text1->length() > 0);
2087 assert(m_text2->length() > 0);
2088
2089 m_text1->ref();
2090 m_text2->ref();
2091}
2092
2093JoinTextNodesCommandImpl::~JoinTextNodesCommandImpl()
2094{
2095 if (m_text1)
2096 m_text1->deref();
2097 if (m_text2)
2098 m_text2->deref();
2099}
2100
2101void JoinTextNodesCommandImpl::doApply()
2102{
2103 assert(m_text1);
2104 assert(m_text2);
2105 assert(m_text1->nextSibling() == m_text2);
2106
2107 int exceptionCode = 0;
2108 m_text2->insertData(0, m_text1->data(), exceptionCode);
2109 assert(exceptionCode == 0);
2110
2111 m_text2->parentNode()->removeChild(m_text1, exceptionCode);
2112 assert(exceptionCode == 0);
2113
2114 m_offset = m_text1->length();
2115}
2116
2117void JoinTextNodesCommandImpl::doUnapply()
2118{
2119 assert(m_text2);
2120 assert(m_offset > 0);
2121
2122 int exceptionCode = 0;
2123
2124 m_text2->deleteData(0, m_offset, exceptionCode);
2125 assert(exceptionCode == 0);
2126
2127 m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
2128 assert(exceptionCode == 0);
2129
2130 assert(m_text2->previousSibling()->isTextNode());
2131 assert(m_text2->previousSibling() == m_text1);
2132}
2133
2134//------------------------------------------------------------------------------------------
2135// ReplaceSelectionCommandImpl
2136
2137ReplaceSelectionCommandImpl::ReplaceSelectionCommandImpl(DocumentImpl *document, DOM::DocumentFragmentImpl *fragment, bool selectReplacement)
2138 : CompositeEditCommandImpl(document), m_fragment(fragment), m_selectReplacement(selectReplacement)
2139{
2140}
2141
2142ReplaceSelectionCommandImpl::~ReplaceSelectionCommandImpl()
2143{
2144}
2145
2146void ReplaceSelectionCommandImpl::doApply()
2147{
2148 NodeImpl *firstChild = m_fragment->firstChild();
2149 NodeImpl *lastChild = m_fragment->lastChild();
2150
2151 Selection selection = endingSelection();
2152
2153 // Delete the current selection, or collapse whitespace, as needed
2154 if (selection.state() == Selection::RANGE)
2155 deleteSelection();
2156 else
2157 deleteCollapsibleWhitespace();
2158
2159 selection = endingSelection();
2160 assert(!selection.isEmpty());
2161
2162 if (!firstChild) {
2163 // Pasting something that didn't parse or was empty.
2164 assert(!lastChild);
2165 } else if (firstChild == lastChild && firstChild->isTextNode()) {
2166 // Simple text paste. Treat as if the text were typed.
2167 Position base = selection.base();
2168 inputText(static_cast<TextImpl *>(firstChild)->data());
2169 if (m_selectReplacement) {
2170 setEndingSelection(Selection(base, endingSelection().extent()));
2171 }
2172 }
2173 else {
2174 // HTML fragment paste.
2175 NodeImpl *beforeNode = firstChild;
2176 NodeImpl *node = firstChild->nextSibling();
2177
2178 insertNodeAt(firstChild, selection.start().node(), selection.start().offset());
2179
2180 // Insert the nodes from the fragment
2181 while (node) {
2182 NodeImpl *next = node->nextSibling();
2183 insertNodeAfter(node, beforeNode);
2184 beforeNode = node;
2185 node = next;
2186 }
2187 assert(beforeNode);
2188
2189 // Find the last leaf.
2190 NodeImpl *lastLeaf = lastChild;
2191 while (1) {
2192 NodeImpl *nextChild = lastLeaf->lastChild();
2193 if (!nextChild)
2194 break;
2195 lastLeaf = nextChild;
2196 }
2197
2198 if (m_selectReplacement) {
2199 // Find the first leaf.
2200 NodeImpl *firstLeaf = firstChild;
2201 while (1) {
2202 NodeImpl *nextChild = firstLeaf->firstChild();
2203 if (!nextChild)
2204 break;
2205 firstLeaf = nextChild;
2206 }
2207 // Select what was inserted.
2208 setEndingSelection(Selection(Position(firstLeaf, firstLeaf->caretMinOffset()), Position(lastLeaf, lastLeaf->caretMaxOffset())));
2209 } else {
2210 // Place the cursor after what was inserted.
2211 setEndingSelection(Position(lastLeaf, lastLeaf->caretMaxOffset()));
2212 }
2213 }
2214}
2215
2216//------------------------------------------------------------------------------------------
2217// MoveSelectionCommandImpl
2218
2219MoveSelectionCommandImpl::MoveSelectionCommandImpl(DocumentImpl *document, DOM::DocumentFragmentImpl *fragment, DOM::Position &position)
2220: CompositeEditCommandImpl(document), m_fragment(fragment), m_position(position)
2221{
2222}
2223
2224MoveSelectionCommandImpl::~MoveSelectionCommandImpl()
2225{
2226}
2227
2228void MoveSelectionCommandImpl::doApply()
2229{
2230 Selection selection = endingSelection();
2231 assert(selection.state() == Selection::RANGE);
2232
2233 // Update the position otherwise it may become invalid after the selection is deleted.
2234 NodeImpl *positionNode = m_position.node();
2235 long positionOffset = m_position.offset();
2236 Position selectionEnd = selection.end();
2237 long selectionEndOffset = selectionEnd.offset();
2238 if (selectionEnd.node() == positionNode && selectionEndOffset < positionOffset) {
2239 positionOffset -= selectionEndOffset;
2240 Position selectionStart = selection.start();
2241 if (selectionStart.node() == positionNode) {
2242 positionOffset += selectionStart.offset();
2243 }
2244 }
2245
2246 deleteSelection();
2247
2248 setEndingSelection(Position(positionNode, positionOffset));
2249 RefPtr<ReplaceSelectionCommandImpl> cmd = new ReplaceSelectionCommandImpl(document(), m_fragment, true);
2250 applyCommandToComposite(cmd);
2251}
2252
2253//------------------------------------------------------------------------------------------
2254// RemoveCSSPropertyCommandImpl
2255
2256RemoveCSSPropertyCommandImpl::RemoveCSSPropertyCommandImpl(DocumentImpl *document, CSSStyleDeclarationImpl *decl, int property)
2257 : EditCommandImpl(document), m_decl(decl), m_property(property), m_important(false)
2258{
2259 assert(m_decl);
2260 m_decl->ref();
2261}
2262
2263RemoveCSSPropertyCommandImpl::~RemoveCSSPropertyCommandImpl()
2264{
2265 assert(m_decl);
2266 m_decl->deref();
2267}
2268
2269void RemoveCSSPropertyCommandImpl::doApply()
2270{
2271 assert(m_decl);
2272
2273 m_oldValue = m_decl->getPropertyValue(m_property);
2274 assert(!m_oldValue.isNull());
2275
2276 m_important = m_decl->getPropertyPriority(m_property);
2277 m_decl->removeProperty(m_property);
2278}
2279
2280void RemoveCSSPropertyCommandImpl::doUnapply()
2281{
2282 assert(m_decl);
2283 assert(!m_oldValue.isNull());
2284
2285 m_decl->setProperty(m_property, m_oldValue, m_important);
2286}
2287
2288//------------------------------------------------------------------------------------------
2289// RemoveNodeAttributeCommandImpl
2290
2291RemoveNodeAttributeCommandImpl::RemoveNodeAttributeCommandImpl(DocumentImpl *document, ElementImpl *element, NodeImpl::Id attribute)
2292 : EditCommandImpl(document), m_element(element), m_attribute(attribute)
2293{
2294 assert(m_element);
2295 m_element->ref();
2296}
2297
2298RemoveNodeAttributeCommandImpl::~RemoveNodeAttributeCommandImpl()
2299{
2300 assert(m_element);
2301 m_element->deref();
2302}
2303
2304void RemoveNodeAttributeCommandImpl::doApply()
2305{
2306 assert(m_element);
2307
2308 m_oldValue = m_element->getAttribute(m_attribute);
2309 assert(!m_oldValue.isNull());
2310
2311 int exceptionCode = 0;
2312 m_element->removeAttribute(m_attribute, exceptionCode);
2313 assert(exceptionCode == 0);
2314}
2315
2316void RemoveNodeAttributeCommandImpl::doUnapply()
2317{
2318 assert(m_element);
2319 assert(!m_oldValue.isNull());
2320
2321// int exceptionCode = 0;
2322 m_element->setAttribute(m_attribute, m_oldValue.implementation());
2323// assert(exceptionCode == 0);
2324}
2325
2326//------------------------------------------------------------------------------------------
2327// RemoveNodeCommandImpl
2328
2329RemoveNodeCommandImpl::RemoveNodeCommandImpl(DocumentImpl *document, NodeImpl *removeChild)
2330 : EditCommandImpl(document), m_parent(0), m_removeChild(removeChild), m_refChild(0)
2331{
2332 assert(m_removeChild);
2333 m_removeChild->ref();
2334
2335 m_parent = m_removeChild->parentNode();
2336 assert(m_parent);
2337 m_parent->ref();
2338
2339 RefPtr<DOM::NodeListImpl> children = m_parent->childNodes();
2340 for (long i = children->length() - 1; i >= 0; --i) {
2341 NodeImpl *node = children->item(i);
2342 if (node == m_removeChild)
2343 break;
2344 m_refChild = node;
2345 }
2346
2347 if (m_refChild)
2348 m_refChild->ref();
2349}
2350
2351RemoveNodeCommandImpl::~RemoveNodeCommandImpl()
2352{
2353 if (m_parent)
2354 m_parent->deref();
2355 if (m_removeChild)
2356 m_removeChild->deref();
2357 if (m_refChild)
2358 m_refChild->deref();
2359}
2360
2361void RemoveNodeCommandImpl::doApply()
2362{
2363 assert(m_parent);
2364 assert(m_removeChild);
2365
2366 int exceptionCode = 0;
2367 m_parent->removeChild(m_removeChild, exceptionCode);
2368 assert(exceptionCode == 0);
2369}
2370
2371void RemoveNodeCommandImpl::doUnapply()
2372{
2373 assert(m_parent);
2374 assert(m_removeChild);
2375
2376 int exceptionCode = 0;
2377 if (m_refChild)
2378 m_parent->insertBefore(m_removeChild, m_refChild, exceptionCode);
2379 else
2380 m_parent->appendChild(m_removeChild, exceptionCode);
2381 assert(exceptionCode == 0);
2382}
2383
2384//------------------------------------------------------------------------------------------
2385// RemoveNodeAndPruneCommandImpl
2386
2387RemoveNodeAndPruneCommandImpl::RemoveNodeAndPruneCommandImpl(DocumentImpl *document, NodeImpl *pruneNode, NodeImpl *stopNode)
2388 : CompositeEditCommandImpl(document), m_pruneNode(pruneNode), m_stopNode(stopNode)
2389{
2390 assert(m_pruneNode);
2391 m_pruneNode->ref();
2392 if (m_stopNode)
2393 m_stopNode->ref();
2394}
2395
2396RemoveNodeAndPruneCommandImpl::~RemoveNodeAndPruneCommandImpl()
2397{
2398 m_pruneNode->deref();
2399 if (m_stopNode)
2400 m_stopNode->deref();
2401}
2402
2403void RemoveNodeAndPruneCommandImpl::doApply()
2404{
2405 NodeImpl *editableBlock = m_pruneNode->enclosingBlockFlowElement();
2406 NodeImpl *pruneNode = m_pruneNode;
2407 NodeImpl *node = pruneNode->traversePreviousNode();
2408 removeNode(pruneNode);
2409 while (1) {
2410 if (node == m_stopNode || editableBlock != node->enclosingBlockFlowElement() || !shouldPruneNode(node))
2411 break;
2412 pruneNode = node;
2413 node = node->traversePreviousNode();
2414 removeNode(pruneNode);
2415 }
2416}
2417
2418//------------------------------------------------------------------------------------------
2419// RemoveNodePreservingChildrenCommandImpl
2420
2421RemoveNodePreservingChildrenCommandImpl::RemoveNodePreservingChildrenCommandImpl(DocumentImpl *document, NodeImpl *node)
2422 : CompositeEditCommandImpl(document), m_node(node)
2423{
2424 assert(m_node);
2425 m_node->ref();
2426}
2427
2428RemoveNodePreservingChildrenCommandImpl::~RemoveNodePreservingChildrenCommandImpl()
2429{
2430 if (m_node)
2431 m_node->deref();
2432}
2433
2434void RemoveNodePreservingChildrenCommandImpl::doApply()
2435{
2436 RefPtr<DOM::NodeListImpl> children = node()->childNodes();
2437 const unsigned int length = children->length();
2438 for (unsigned int i = 0; i < length; ++i) {
2439 NodeImpl *child = children->item(0);
2440 removeNode(child);
2441 insertNodeBefore(child, node());
2442 }
2443 removeNode(node());
2444}
2445
2446//------------------------------------------------------------------------------------------
2447// SetNodeAttributeCommandImpl
2448
2449SetNodeAttributeCommandImpl::SetNodeAttributeCommandImpl(DocumentImpl *document, ElementImpl *element, NodeImpl::Id attribute, const DOMString &value)
2450 : EditCommandImpl(document), m_element(element), m_attribute(attribute), m_value(value)
2451{
2452 assert(m_element);
2453 m_element->ref();
2454 assert(!m_value.isNull());
2455}
2456
2457SetNodeAttributeCommandImpl::~SetNodeAttributeCommandImpl()
2458{
2459 if (m_element)
2460 m_element->deref();
2461}
2462
2463void SetNodeAttributeCommandImpl::doApply()
2464{
2465 assert(m_element);
2466 assert(!m_value.isNull());
2467
2468// int exceptionCode = 0;
2469 m_oldValue = m_element->getAttribute(m_attribute);
2470 m_element->setAttribute(m_attribute, m_value.implementation());
2471// assert(exceptionCode == 0);
2472}
2473
2474void SetNodeAttributeCommandImpl::doUnapply()
2475{
2476 assert(m_element);
2477 assert(!m_oldValue.isNull());
2478
2479// int exceptionCode = 0;
2480 m_element->setAttribute(m_attribute, m_oldValue.implementation());
2481// assert(exceptionCode == 0);
2482}
2483
2484//------------------------------------------------------------------------------------------
2485// SplitTextNodeCommandImpl
2486
2487SplitTextNodeCommandImpl::SplitTextNodeCommandImpl(DocumentImpl *document, TextImpl *text, long offset)
2488 : EditCommandImpl(document), m_text1(0), m_text2(text), m_offset(offset)
2489{
2490 assert(m_text2);
2491 assert(m_text2->length() > 0);
2492
2493 m_text2->ref();
2494}
2495
2496SplitTextNodeCommandImpl::~SplitTextNodeCommandImpl()
2497{
2498 if (m_text1)
2499 m_text1->deref();
2500 if (m_text2)
2501 m_text2->deref();
2502}
2503
2504void SplitTextNodeCommandImpl::doApply()
2505{
2506 assert(m_text2);
2507 assert(m_offset > 0);
2508
2509 int exceptionCode = 0;
2510
2511 // EDIT FIXME: This should use better smarts for figuring out which portion
2512 // of the split to copy (based on their comparative sizes). We should also
2513 // just use the DOM's splitText function.
2514
2515 if (!m_text1) {
2516 // create only if needed.
2517 // if reapplying, this object will already exist.
2518 m_text1 = document()->createTextNode(m_text2->substringData(0, m_offset, exceptionCode));
2519 assert(exceptionCode == 0);
2520 assert(m_text1);
2521 m_text1->ref();
2522 }
2523
2524 m_text2->deleteData(0, m_offset, exceptionCode);
2525 assert(exceptionCode == 0);
2526
2527 m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
2528 assert(exceptionCode == 0);
2529
2530 assert(m_text2->previousSibling()->isTextNode());
2531 assert(m_text2->previousSibling() == m_text1);
2532}
2533
2534void SplitTextNodeCommandImpl::doUnapply()
2535{
2536 assert(m_text1);
2537 assert(m_text2);
2538
2539 assert(m_text1->nextSibling() == m_text2);
2540
2541 int exceptionCode = 0;
2542 m_text2->insertData(0, m_text1->data(), exceptionCode);
2543 assert(exceptionCode == 0);
2544
2545 m_text2->parentNode()->removeChild(m_text1, exceptionCode);
2546 assert(exceptionCode == 0);
2547
2548 m_offset = m_text1->length();
2549}
2550
2551//------------------------------------------------------------------------------------------
2552// TypingCommandImpl
2553
2554TypingCommandImpl::TypingCommandImpl(DocumentImpl *document)
2555 : CompositeEditCommandImpl(document), m_openForMoreTyping(true)
2556{
2557}
2558
2559TypingCommandImpl::~TypingCommandImpl()
2560{
2561}
2562
2563void TypingCommandImpl::doApply()
2564{
2565}
2566
2567void TypingCommandImpl::typingAddedToOpenCommand()
2568{
2569 assert(document());
2570 assert(document()->part());
2571 document()->part()->editor()->appliedEditing(this);
2572}
2573
2574void TypingCommandImpl::insertText(const DOMString &text)
2575{
2576 if (document()->part()->editor()->typingStyle() || m_cmds.count() == 0) {
2577 RefPtr<InputTextCommandImpl> cmd = new InputTextCommandImpl(document());
2578 applyCommandToComposite(cmd);
2579 cmd->input(text);
2580 } else {
2581 EditCommandImpl *lastCommand = m_cmds.last().get();
2582 if (lastCommand->isInputTextCommand()) {
2583 static_cast<InputTextCommandImpl*>(lastCommand)->input(text);
2584 } else {
2585 RefPtr<InputTextCommandImpl> cmd = new InputTextCommandImpl(document());
2586 applyCommandToComposite(cmd);
2587 cmd->input(text);
2588 }
2589 }
2590 typingAddedToOpenCommand();
2591}
2592
2593void TypingCommandImpl::insertNewline()
2594{
2595 RefPtr<InputNewlineCommandImpl> cmd = new InputNewlineCommandImpl(document());
2596 applyCommandToComposite(cmd);
2597 typingAddedToOpenCommand();
2598}
2599
2600void TypingCommandImpl::issueCommandForDeleteKey()
2601{
2602 Selection selectionToDelete = endingSelection();
2603 assert(selectionToDelete.state() != Selection::NONE);
2604
2605#ifdef DEBUG_COMMANDS
2606 kDebug() << "[selection]" << selectionToDelete << endl;
2607#endif
2608 if (selectionToDelete.state() == Selection::CARET) {
2609#ifdef DEBUG_COMMANDS
2610 kDebug() << "[caret selection]" << endl;
2611#endif
2612 Position pos(selectionToDelete.start());
2613 if (pos.inFirstEditableInRootEditableElement() && pos.offset() <= pos.node()->caretMinOffset()) {
2614 // we're at the start of a root editable block...do nothing
2615 return;
2616 }
2617 selectionToDelete = Selection(pos.previousCharacterPosition(), pos);
2618#ifdef DEBUG_COMMANDS
2619 kDebug() << "[modified selection]" << selectionToDelete << endl;
2620#endif
2621 }
2622 deleteSelection(selectionToDelete);
2623 typingAddedToOpenCommand();
2624}
2625
2626void TypingCommandImpl::deleteKeyPressed()
2627{
2628// EDIT FIXME: The ifdef'ed out code below should be re-enabled.
2629// In order for this to happen, the deleteCharacter case
2630// needs work. Specifically, the caret-positioning code
2631// and whitespace-handling code in DeleteSelectionCommandImpl::doApply()
2632// needs to be factored out so it can be used again here.
2633// Until that work is done, issueCommandForDeleteKey() does the
2634// right thing, but less efficiently and with the cost of more
2635// objects.
2636 issueCommandForDeleteKey();
2637#if 0
2638 if (m_cmds.count() == 0) {
2639 issueCommandForDeleteKey();
2640 }
2641 else {
2642 EditCommand lastCommand = m_cmds.last();
2643 if (lastCommand.commandID() == InputTextCommandID) {
2644 InputTextCommand cmd = static_cast<InputTextCommand &>(lastCommand);
2645 cmd.deleteCharacter();
2646 if (cmd.charactersAdded() == 0) {
2647 removeCommand(cmd);
2648 }
2649 }
2650 else if (lastCommand.commandID() == InputNewlineCommandID) {
2651 lastCommand.unapply();
2652 removeCommand(lastCommand);
2653 }
2654 else {
2655 issueCommandForDeleteKey();
2656 }
2657 }
2658#endif
2659}
2660
2661void TypingCommandImpl::removeCommand(const PassRefPtr<EditCommandImpl> cmd)
2662{
2663 // NOTE: If the passed-in command is the last command in the
2664 // composite, we could remove all traces of this typing command
2665 // from the system, including the undo chain. Other editors do
2666 // not do this, but we could.
2667
2668 m_cmds.removeAll(cmd);
2669 if (m_cmds.count() == 0)
2670 setEndingSelection(startingSelection());
2671 else
2672 setEndingSelection(m_cmds.last()->endingSelection());
2673}
2674
2675static bool isOpenForMoreTypingCommand(const EditCommandImpl *command)
2676{
2677 return command && command->isTypingCommand() &&
2678 static_cast<const TypingCommandImpl*>(command)->openForMoreTyping();
2679}
2680
2681void TypingCommandImpl::deleteKeyPressed0(DocumentImpl *document)
2682{
2683 //Editor *editor = document->part()->editor();
2684 // FIXME reenable after properly modify selection of the lastEditCommand
2685 // if (isOpenForMoreTypingCommand(lastEditCommand)) {
2686 // static_cast<TypingCommand &>(lastEditCommand).deleteKeyPressed();
2687 // } else {
2688 RefPtr<TypingCommandImpl> command = new TypingCommandImpl(document);
2689 command->apply();
2690 command->deleteKeyPressed();
2691 // }
2692}
2693
2694void TypingCommandImpl::insertNewline0(DocumentImpl *document)
2695{
2696 assert(document);
2697 Editor *ed = document->part()->editor();
2698 assert(ed);
2699 EditCommandImpl *lastEditCommand = ed->lastEditCommand().get();
2700 if (isOpenForMoreTypingCommand(lastEditCommand)) {
2701 static_cast<TypingCommandImpl*>(lastEditCommand)->insertNewline();
2702 } else {
2703 RefPtr<TypingCommandImpl> command = new TypingCommandImpl(document);
2704 command->apply();
2705 command->insertNewline();
2706 }
2707}
2708
2709void TypingCommandImpl::insertText0(DocumentImpl *document, const DOMString &text)
2710{
2711#ifdef DEBUG_COMMANDS
2712 kDebug() << "[insert text]" << text << endl;
2713#endif
2714 assert(document);
2715 Editor *ed = document->part()->editor();
2716 assert(ed);
2717 EditCommandImpl *lastEditCommand = ed->lastEditCommand().get();
2718 if (isOpenForMoreTypingCommand(lastEditCommand)) {
2719 static_cast<TypingCommandImpl*>(lastEditCommand)->insertText(text);
2720 } else {
2721 RefPtr<TypingCommandImpl> command = new TypingCommandImpl(document);
2722 command->apply();
2723 command->insertText(text);
2724 }
2725}
2726
2727
2728//------------------------------------------------------------------------------------------
2729// InsertListCommandImpl
2730
2731InsertListCommandImpl::InsertListCommandImpl(DocumentImpl *document, Type type)
2732 : CompositeEditCommandImpl(document), m_listType(type)
2733{
2734}
2735
2736InsertListCommandImpl::~InsertListCommandImpl()
2737{
2738}
2739
2740void InsertListCommandImpl::doApply()
2741{
2742#ifdef DEBUG_COMMANDS
2743 kDebug() << "[make current selection/paragraph a list]" << endingSelection() << endl;
2744#endif
2745 Position start = endingSelection().start();
2746 Position end = endingSelection().end();
2747 ElementImpl *startBlock = start.node()->enclosingBlockFlowElement();
2748 ElementImpl *endBlock = end.node()->enclosingBlockFlowElement();
2749#ifdef DEBUG_COMMANDS
2750 kDebug() << "[start:end blocks]" << startBlock << endBlock << endl;
2751 printEnclosingBlockTree(start.node());
2752#endif
2753 if (startBlock == endBlock) {
2754 if (startBlock->id() == ID_LI) {
2755 // we already have a list item, remove it then
2756#ifdef DEBUG_COMMANDS
2757 kDebug() << "[remove list item]" << endl;
2758#endif
2759 NodeImpl *listBlock = startBlock->parent(); // it's either <ol> or <ul>
2760 // we need to properly split or even remove the list leaving 2 lists:
2761 // [listBlock->firstChild(), startBlock) and (startBlock, listBlock->lastChild()]
2762 if (listBlock->firstChild() == listBlock->lastChild() && listBlock->firstChild() == startBlock) {
2763 // get rid of list completely
2764#ifdef DEBUG_COMMANDS
2765 kDebug() << "[remove list completely]" << endl;
2766#endif
2767 removeNodePreservingChildren(listBlock);
2768 removeNodePreservingChildren(startBlock);
2769 } else if (!startBlock->previousSibling()) {
2770 // move nodes from this list item before the list
2771 NodeImpl *nextSibling;
2772 for (NodeImpl *node = startBlock->firstChild(); node; node = nextSibling) {
2773 nextSibling = node->nextSibling();
2774 removeNode(node);
2775 insertNodeBefore(node, listBlock);
2776 }
2777 removeNode(startBlock);
2778 } else if (!startBlock->nextSibling()) {
2779 // move nodes from this list item after the list
2780 NodeImpl *nextSibling;
2781 for (NodeImpl *node = startBlock->lastChild(); node; node = nextSibling) {
2782 nextSibling = node->previousSibling();
2783 removeNode(node);
2784 insertNodeAfter(node, listBlock);
2785 }
2786 removeNode(startBlock);
2787 } else {
2788 // split list into 2 and nodes from this list item goes between lists
2789 WTF::PassRefPtr<NodeImpl> newListBlock = listBlock->cloneNode(false);
2790 insertNodeAfter(newListBlock.get(), listBlock);
2791 NodeImpl *node, *nextSibling;
2792 for (node = startBlock->nextSibling(); node; node = nextSibling) {
2793 nextSibling = node->nextSibling();
2794 removeNode(node);
2795 appendNode(newListBlock.get(), node);
2796 }
2797 for (node = startBlock->firstChild(); node; node = nextSibling) {
2798 nextSibling = node->nextSibling();
2799 removeNode(node);
2800 insertNodeBefore(node, newListBlock.get());
2801 }
2802 removeNode(startBlock);
2803 }
2804 } else {
2805 ElementImpl *ol = document()->createHTMLElement(m_listType == OrderedList ? "OL" : "UL");
2806 ElementImpl *li = document()->createHTMLElement("LI");
2807 appendNode(ol, li);
2808 NodeImpl *nextNode;
2809 for (NodeImpl *node = startBlock->firstChild(); node; node = nextNode) {
2810#ifdef DEBUG_COMMANDS
2811 kDebug() << "[reattach node]" << node << endl;
2812#endif
2813 nextNode = node->nextSibling();
2814 removeNode(node);
2815 appendNode(li, node);
2816 }
2817 appendNode(startBlock, ol);
2818 }
2819 } else {
2820#ifdef DEBUG_COMMANDS
2821 kDebug() << "[different blocks are not supported yet]" << endl;
2822#endif
2823 }
2824}
2825
2826void InsertListCommandImpl::insertList(DocumentImpl *document, Type type)
2827{
2828 RefPtr<InsertListCommandImpl> insertCommand = new InsertListCommandImpl(document, type);
2829 insertCommand->apply();
2830}
2831
2832//------------------------------------------------------------------------------------------
2833
2834//------------------------------------------------------------------------------------------
2835// IndentOutdentCommandImpl
2836
2837IndentOutdentCommandImpl::IndentOutdentCommandImpl(DocumentImpl *document, Type type)
2838 : CompositeEditCommandImpl(document), m_commandType(type)
2839{
2840}
2841
2842IndentOutdentCommandImpl::~IndentOutdentCommandImpl()
2843{
2844}
2845
2846void IndentOutdentCommandImpl::indent()
2847{
2848 Selection selection = endingSelection();
2849#ifdef DEBUG_COMMANDS
2850 kDebug() << "[indent selection]" << selection << endl;
2851#endif
2852 NodeImpl *startBlock = selection.start().node()->enclosingBlockFlowElement();
2853 NodeImpl *endBlock = selection.end().node()->enclosingBlockFlowElement();
2854
2855 if (startBlock == endBlock) {
2856 // check if selection is the list, but not fully covered
2857 if (startBlock->id() == ID_LI && (startBlock->previousSibling() || startBlock->nextSibling())) {
2858#ifdef DEBUG_COMMANDS
2859 kDebug() << "[modify list]" << endl;
2860#endif
2861 RefPtr<NodeImpl> newList = startBlock->parent()->cloneNode(false);
2862 insertNodeAfter(newList.get(), startBlock);
2863 removeNode(startBlock);
2864 appendNode(newList.get(), startBlock);
2865 } else {
2866 NodeImpl *blockquoteElement = document()->createHTMLElement("blockquote");
2867 if (startBlock->id() == ID_LI) {
2868 startBlock = startBlock->parent();
2869 NodeImpl *parent = startBlock->parent();
2870 removeNode(startBlock);
2871 appendNode(parent, blockquoteElement);
2872 appendNode(blockquoteElement, startBlock);
2873 } else {
2874 NodeImpl *parent = startBlock->parent();
2875 removeNode(startBlock);
2876 appendNode(parent, blockquoteElement);
2877 appendNode(blockquoteElement, startBlock);
2878 }
2879 }
2880 } else {
2881 if (startBlock->id() == ID_LI && endBlock->id() == ID_LI && startBlock->parent() == endBlock->parent()) {
2882#ifdef DEBUG_COMMANDS
2883 kDebug() << "[indent some items inside list]" << endl;
2884#endif
2885 RefPtr<NodeImpl> nestedList = startBlock->parent()->cloneNode(false);
2886 insertNodeBefore(nestedList.get(), startBlock);
2887 NodeImpl *nextNode = 0;
2888 for (NodeImpl *node = startBlock;; node = nextNode) {
2889 nextNode = node->nextSibling();
2890 removeNode(node);
2891 appendNode(nestedList.get(), node);
2892 if (node == endBlock)
2893 break;
2894 }
2895 } else {
2896#ifdef DEBUG_COMMANDS
2897 kDebug() << "[blocks not from one list are not supported yet]" << endl;
2898#endif
2899 }
2900 }
2901}
2902
2903static bool hasPreviousListItem(NodeImpl *node)
2904{
2905 while (node) {
2906 node = node->previousSibling();
2907 if (node && node->id() == ID_LI)
2908 return true;
2909 }
2910 return false;
2911}
2912
2913static bool hasNextListItem(NodeImpl *node)
2914{
2915 while (node) {
2916 node = node->nextSibling();
2917 if (node && node->id() == ID_LI)
2918 return true;
2919 }
2920 return false;
2921}
2922
2923void IndentOutdentCommandImpl::outdent()
2924{
2925 Selection selection = endingSelection();
2926#ifdef DEBUG_COMMANDS
2927 kDebug() << "[indent selection]" << selection << endl;
2928#endif
2929 NodeImpl *startBlock = selection.start().node()->enclosingBlockFlowElement();
2930 NodeImpl *endBlock = selection.end().node()->enclosingBlockFlowElement();
2931
2932 if (startBlock->id() == ID_LI && endBlock->id() == ID_LI && startBlock->parent() == endBlock->parent()) {
2933#ifdef DEBUG_COMMANDS
2934 kDebug() << "[list items selected]" << endl;
2935#endif
2936 bool firstItemSelected = !hasPreviousListItem(startBlock);
2937 bool lastItemSelected = !hasNextListItem(endBlock);
2938 bool listFullySelected = firstItemSelected && lastItemSelected;
2939
2940#ifdef DEBUG_COMMANDS
2941 kDebug() << "[first/last item selected]" << firstItemSelected << lastItemSelected << endl;
2942#endif
2943
2944 NodeImpl *listNode = startBlock->parent();
2945 printEnclosingBlockTree(listNode);
2946 bool hasParentList = listNode->parent()->id() == ID_OL || listNode->parent()->id() == ID_UL;
2947
2948 if (!firstItemSelected && !lastItemSelected) {
2949 // split the list into 2 and reattach all the nodes before the first selected item to the second list
2950 RefPtr<NodeImpl> clonedList = listNode->cloneNode(false);
2951 NodeImpl *nextNode = 0;
2952 for (NodeImpl *node = listNode->firstChild(); node != startBlock; node = nextNode) {
2953 nextNode = node->nextSibling();
2954 removeNode(node);
2955 appendNode(clonedList.get(), node);
2956 }
2957 insertNodeBefore(clonedList.get(), listNode);
2958 // so now the first item selected
2959 firstItemSelected = true;
2960 }
2961
2962 NodeImpl *nextNode = 0;
2963 for (NodeImpl *node = firstItemSelected ? startBlock : endBlock;; node = nextNode) {
2964 nextNode = firstItemSelected ? node->nextSibling() : node->previousSibling();
2965 removeNode(node);
2966 if (firstItemSelected)
2967 insertNodeBefore(node, listNode);
2968 else
2969 insertNodeAfter(node, listNode);
2970 if (!hasParentList && node->id() == ID_LI) {
2971 insertNodeAfter(document()->createHTMLElement("BR"), node);
2972 removeNodePreservingChildren(node);
2973 }
2974 if (node == (firstItemSelected ? endBlock : startBlock))
2975 break;
2976 }
2977 if (listFullySelected)
2978 removeNode(listNode);
2979 return;
2980 }
2981
2982
2983 if (startBlock == endBlock) {
2984 if (startBlock->id() == ID_BLOCKQUOTE) {
2985 removeNodePreservingChildren(startBlock);
2986 } else {
2987#ifdef DEBUG_COMMANDS
2988 kDebug() << "[not the list or blockquote]" << endl;
2989#endif
2990 }
2991 } else {
2992#ifdef DEBUG_COMMANDS
2993 kDebug() << "[blocks not from one list are not supported yet]" << endl;
2994#endif
2995 }
2996}
2997
2998void IndentOutdentCommandImpl::doApply()
2999{
3000 if (m_commandType == Indent)
3001 indent();
3002 else
3003 outdent();
3004}
3005
3006//------------------------------------------------------------------------------------------
3007
3008} // namespace khtml
3009
DOM::CSSPrimitiveValue
The CSSPrimitiveValue interface represents a single CSS value .
Definition: css_value.h:373
DOM::DOMString
This class implements the basic string we use in the DOM.
Definition: dom_string.h:44
DOM::DOMString::length
uint length() const
Definition: dom_string.cpp:185
DOM::DOMString::isNull
bool isNull() const
Definition: dom_string.h:121
DOM::DOMString::implementation
DOMStringImpl * implementation() const
Definition: dom_string.h:147
DOM::DOMString::copy
DOMString copy() const
Definition: dom_string.cpp:275
DOM::DOMString::isEmpty
bool isEmpty() const
Definition: dom_string.cpp:368
DOM::Editor
This class resembles the editing API when the associated khtml document is editable (in design mode),...
Definition: editor.h:61
DOM::Editor::lastEditCommand
WTF::PassRefPtr< khtml::EditCommandImpl > lastEditCommand() const
Returns the most recent edit command applied.
Definition: editor.cpp:399
DOM::Node
The Node interface is the primary datatype for the entire Document Object Model.
Definition: dom_node.h:271
DOM::Range
Definition: dom2_range.h:80
khtml::AppendNodeCommandImpl
Definition: htmlediting_impl.h:185
khtml::AppendNodeCommandImpl::AppendNodeCommandImpl
AppendNodeCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *parentNode, DOM::NodeImpl *appendChild)
Definition: htmlediting_impl.cpp:524
khtml::AppendNodeCommandImpl::~AppendNodeCommandImpl
virtual ~AppendNodeCommandImpl()
Definition: htmlediting_impl.cpp:534
khtml::AppendNodeCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:542
khtml::AppendNodeCommandImpl::doUnapply
virtual void doUnapply()
Definition: htmlediting_impl.cpp:552
khtml::ApplyStyleCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:634
khtml::ApplyStyleCommandImpl::ApplyStyleCommandImpl
ApplyStyleCommandImpl(DOM::DocumentImpl *, DOM::CSSStyleDeclarationImpl *style)
Definition: htmlediting_impl.cpp:566
khtml::ApplyStyleCommandImpl::style
DOM::CSSStyleDeclarationImpl * style() const
Definition: htmlediting_impl.h:212
khtml::ApplyStyleCommandImpl::~ApplyStyleCommandImpl
virtual ~ApplyStyleCommandImpl()
Definition: htmlediting_impl.cpp:573
khtml::CompositeEditCommandImpl
Definition: htmlediting_impl.h:138
khtml::CompositeEditCommandImpl::removeCSSProperty
void removeCSSProperty(DOM::CSSStyleDeclarationImpl *, int property)
Definition: htmlediting_impl.cpp:486
khtml::CompositeEditCommandImpl::removeNodeAttribute
void removeNodeAttribute(DOM::ElementImpl *, int attribute)
Definition: htmlediting_impl.cpp:492
khtml::CompositeEditCommandImpl::joinTextNodes
void joinTextNodes(DOM::TextImpl *text1, DOM::TextImpl *text2)
Definition: htmlediting_impl.cpp:425
khtml::CompositeEditCommandImpl::removeNodeAndPrune
void removeNodeAndPrune(DOM::NodeImpl *pruneNode, DOM::NodeImpl *stopNode=0)
Definition: htmlediting_impl.cpp:407
khtml::CompositeEditCommandImpl::m_cmds
QList< RefPtr< EditCommandImpl > > m_cmds
Definition: htmlediting_impl.h:176
khtml::CompositeEditCommandImpl::CompositeEditCommandImpl
CompositeEditCommandImpl(DOM::DocumentImpl *)
Definition: htmlediting_impl.cpp:310
khtml::CompositeEditCommandImpl::insertNodeAt
void insertNodeAt(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild, long offset)
Definition: htmlediting_impl.cpp:372
khtml::CompositeEditCommandImpl::doReapply
virtual void doReapply()
Definition: htmlediting_impl.cpp:331
khtml::CompositeEditCommandImpl::splitTextNode
void splitTextNode(DOM::TextImpl *text, long offset)
Definition: htmlediting_impl.cpp:419
khtml::CompositeEditCommandImpl::inputText
void inputText(const DOM::DOMString &text)
Definition: htmlediting_impl.cpp:431
khtml::CompositeEditCommandImpl::doUnapply
virtual void doUnapply()
Definition: htmlediting_impl.cpp:319
khtml::CompositeEditCommandImpl::replaceText
void replaceText(DOM::TextImpl *node, long offset, long count, const DOM::DOMString &replacementText)
Definition: htmlediting_impl.cpp:450
khtml::CompositeEditCommandImpl::removeNodePreservingChildren
void removeNodePreservingChildren(DOM::NodeImpl *node)
Definition: htmlediting_impl.cpp:413
khtml::CompositeEditCommandImpl::createTypingStyleElement
DOM::ElementImpl * createTypingStyleElement() const
Definition: htmlediting_impl.cpp:504
khtml::CompositeEditCommandImpl::applyCommandToComposite
void applyCommandToComposite(PassRefPtr< EditCommandImpl >)
Definition: htmlediting_impl.cpp:346
khtml::CompositeEditCommandImpl::removeNode
void removeNode(DOM::NodeImpl *removeChild)
Definition: htmlediting_impl.cpp:401
khtml::CompositeEditCommandImpl::insertNodeBefore
void insertNodeBefore(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild)
Definition: htmlediting_impl.cpp:355
khtml::CompositeEditCommandImpl::deleteSelection
void deleteSelection()
Definition: htmlediting_impl.cpp:458
khtml::CompositeEditCommandImpl::appendNode
void appendNode(DOM::NodeImpl *parent, DOM::NodeImpl *appendChild)
Definition: htmlediting_impl.cpp:395
khtml::CompositeEditCommandImpl::deleteText
void deleteText(DOM::TextImpl *node, long offset, long count)
Definition: htmlediting_impl.cpp:444
khtml::CompositeEditCommandImpl::setNodeAttribute
void setNodeAttribute(DOM::ElementImpl *, int attribute, const DOM::DOMString &)
Definition: htmlediting_impl.cpp:498
khtml::CompositeEditCommandImpl::~CompositeEditCommandImpl
virtual ~CompositeEditCommandImpl()
Definition: htmlediting_impl.cpp:315
khtml::CompositeEditCommandImpl::insertText
void insertText(DOM::TextImpl *node, long offset, const DOM::DOMString &text)
Definition: htmlediting_impl.cpp:438
khtml::CompositeEditCommandImpl::deleteCollapsibleWhitespace
void deleteCollapsibleWhitespace()
Definition: htmlediting_impl.cpp:474
khtml::CompositeEditCommandImpl::insertNodeAfter
void insertNodeAfter(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild)
Definition: htmlediting_impl.cpp:361
khtml::DeleteCollapsibleWhitespaceCommandImpl
Definition: htmlediting_impl.h:245
khtml::DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl
DeleteCollapsibleWhitespaceCommandImpl(DOM::DocumentImpl *document)
khtml::DeleteSelectionCommandImpl
Definition: htmlediting_impl.h:266
khtml::DeleteSelectionCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:1293
khtml::DeleteSelectionCommandImpl::DeleteSelectionCommandImpl
DeleteSelectionCommandImpl(DOM::DocumentImpl *document)
khtml::DeleteSelectionCommandImpl::~DeleteSelectionCommandImpl
virtual ~DeleteSelectionCommandImpl()
Definition: htmlediting_impl.cpp:1187
khtml::DeleteTextCommandImpl
Definition: htmlediting_impl.h:291
khtml::DeleteTextCommandImpl::DeleteTextCommandImpl
DeleteTextCommandImpl(DOM::DocumentImpl *document, DOM::TextImpl *node, long offset, long count)
Definition: htmlediting_impl.cpp:1551
khtml::DeleteTextCommandImpl::~DeleteTextCommandImpl
virtual ~DeleteTextCommandImpl()
Definition: htmlediting_impl.cpp:1561
khtml::DeleteTextCommandImpl::doUnapply
virtual void doUnapply()
Definition: htmlediting_impl.cpp:1579
khtml::DeleteTextCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:1567
khtml::EditCommandImpl
Definition: htmlediting_impl.h:92
khtml::EditCommandImpl::state
ECommandState state() const
Definition: htmlediting_impl.h:116
khtml::EditCommandImpl::setEndingSelection
void setEndingSelection(const DOM::Selection &s)
Definition: htmlediting_impl.cpp:287
khtml::EditCommandImpl::isInputTextCommand
virtual bool isInputTextCommand() const
Definition: htmlediting_impl.h:124
khtml::EditCommandImpl::apply
void apply()
Definition: htmlediting_impl.cpp:230
khtml::EditCommandImpl::setStartingSelection
void setStartingSelection(const DOM::Selection &s)
Definition: htmlediting_impl.cpp:277
khtml::EditCommandImpl::document
virtual DOM::DocumentImpl * document() const
Definition: htmlediting_impl.h:111
khtml::EditCommandImpl::doReapply
virtual void doReapply()
Definition: htmlediting_impl.cpp:272
khtml::EditCommandImpl::parent
EditCommandImpl * parent() const
Definition: htmlediting_impl.cpp:297
khtml::EditCommandImpl::doApply
virtual void doApply()=0
khtml::EditCommandImpl::isCompositeStep
bool isCompositeStep() const
Definition: htmlediting_impl.h:97
khtml::EditCommandImpl::EditCommandImpl
EditCommandImpl(DOM::DocumentImpl *)
Definition: htmlediting_impl.cpp:215
khtml::EditCommandImpl::isTypingCommand
virtual bool isTypingCommand() const
Definition: htmlediting_impl.h:123
khtml::EditCommandImpl::reapply
void reapply()
Definition: htmlediting_impl.cpp:258
khtml::EditCommandImpl::setParent
void setParent(EditCommandImpl *)
Definition: htmlediting_impl.cpp:302
khtml::EditCommandImpl::~EditCommandImpl
virtual ~EditCommandImpl()
Definition: htmlediting_impl.cpp:225
khtml::EditCommandImpl::endingSelection
DOM::Selection endingSelection() const
Definition: htmlediting_impl.h:114
khtml::EditCommandImpl::startingSelection
DOM::Selection startingSelection() const
Definition: htmlediting_impl.h:113
khtml::EditCommandImpl::Applied
@ Applied
Definition: htmlediting_impl.h:101
khtml::EditCommandImpl::NotApplied
@ NotApplied
Definition: htmlediting_impl.h:101
khtml::EditCommandImpl::setState
void setState(ECommandState state)
Definition: htmlediting_impl.h:117
khtml::EditCommandImpl::doUnapply
virtual void doUnapply()=0
khtml::EditCommandImpl::unapply
void unapply()
Definition: htmlediting_impl.cpp:244
khtml::IndentOutdentCommandImpl::Type
Type
Definition: htmlediting_impl.h:648
khtml::IndentOutdentCommandImpl::Indent
@ Indent
Definition: htmlediting_impl.h:648
khtml::IndentOutdentCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2998
khtml::IndentOutdentCommandImpl::IndentOutdentCommandImpl
IndentOutdentCommandImpl(DocumentImpl *document, Type type)
Definition: htmlediting_impl.cpp:2837
khtml::IndentOutdentCommandImpl::~IndentOutdentCommandImpl
virtual ~IndentOutdentCommandImpl()
Definition: htmlediting_impl.cpp:2842
khtml::InputNewlineCommandImpl
Definition: htmlediting_impl.h:314
khtml::InputNewlineCommandImpl::~InputNewlineCommandImpl
virtual ~InputNewlineCommandImpl()
Definition: htmlediting_impl.cpp:1597
khtml::InputNewlineCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:1627
khtml::InputNewlineCommandImpl::InputNewlineCommandImpl
InputNewlineCommandImpl(DOM::DocumentImpl *document)
Definition: htmlediting_impl.cpp:1592
khtml::InputTextCommandImpl
Definition: htmlediting_impl.h:330
khtml::InputTextCommandImpl::deleteCharacter
void deleteCharacter()
Definition: htmlediting_impl.cpp:1784
khtml::InputTextCommandImpl::InputTextCommandImpl
InputTextCommandImpl(DOM::DocumentImpl *document)
Definition: htmlediting_impl.cpp:1766
khtml::InputTextCommandImpl::input
void input(const DOM::DOMString &text)
Definition: htmlediting_impl.cpp:1779
khtml::InputTextCommandImpl::~InputTextCommandImpl
virtual ~InputTextCommandImpl()
Definition: htmlediting_impl.cpp:1771
khtml::InputTextCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:1775
khtml::InsertListCommandImpl
Definition: htmlediting_impl.h:625
khtml::InsertListCommandImpl::InsertListCommandImpl
InsertListCommandImpl(DOM::DocumentImpl *document, Type type)
Definition: htmlediting_impl.cpp:2731
khtml::InsertListCommandImpl::~InsertListCommandImpl
virtual ~InsertListCommandImpl()
Definition: htmlediting_impl.cpp:2736
khtml::InsertListCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2740
khtml::InsertListCommandImpl::insertList
static void insertList(DocumentImpl *document, Type type)
Definition: htmlediting_impl.cpp:2826
khtml::InsertListCommandImpl::Type
Type
Definition: htmlediting_impl.h:627
khtml::InsertListCommandImpl::OrderedList
@ OrderedList
Definition: htmlediting_impl.h:627
khtml::InsertNodeBeforeCommandImpl
Definition: htmlediting_impl.h:355
khtml::InsertNodeBeforeCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2015
khtml::InsertNodeBeforeCommandImpl::InsertNodeBeforeCommandImpl
InsertNodeBeforeCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild)
Definition: htmlediting_impl.cpp:1997
khtml::InsertNodeBeforeCommandImpl::~InsertNodeBeforeCommandImpl
virtual ~InsertNodeBeforeCommandImpl()
Definition: htmlediting_impl.cpp:2007
khtml::InsertNodeBeforeCommandImpl::doUnapply
virtual void doUnapply()
Definition: htmlediting_impl.cpp:2026
khtml::InsertTextCommandImpl
Definition: htmlediting_impl.h:375
khtml::InsertTextCommandImpl::text
DOM::DOMString text() const
Definition: htmlediting_impl.h:385
khtml::InsertTextCommandImpl::doUnapply
virtual void doUnapply()
Definition: htmlediting_impl.cpp:2067
khtml::InsertTextCommandImpl::~InsertTextCommandImpl
virtual ~InsertTextCommandImpl()
Definition: htmlediting_impl.cpp:2051
khtml::InsertTextCommandImpl::InsertTextCommandImpl
InsertTextCommandImpl(DOM::DocumentImpl *document, DOM::TextImpl *, long, const DOM::DOMString &)
Definition: htmlediting_impl.cpp:2040
khtml::InsertTextCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2057
khtml::JoinTextNodesCommandImpl
Definition: htmlediting_impl.h:397
khtml::JoinTextNodesCommandImpl::doUnapply
virtual void doUnapply()
Definition: htmlediting_impl.cpp:2117
khtml::JoinTextNodesCommandImpl::JoinTextNodesCommandImpl
JoinTextNodesCommandImpl(DOM::DocumentImpl *, DOM::TextImpl *, DOM::TextImpl *)
Definition: htmlediting_impl.cpp:2080
khtml::JoinTextNodesCommandImpl::~JoinTextNodesCommandImpl
virtual ~JoinTextNodesCommandImpl()
Definition: htmlediting_impl.cpp:2093
khtml::JoinTextNodesCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2101
khtml::MoveSelectionCommandImpl::MoveSelectionCommandImpl
MoveSelectionCommandImpl(DOM::DocumentImpl *document, DOM::DocumentFragmentImpl *fragment, DOM::Position &position)
Definition: htmlediting_impl.cpp:2219
khtml::MoveSelectionCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2228
khtml::MoveSelectionCommandImpl::~MoveSelectionCommandImpl
virtual ~MoveSelectionCommandImpl()
Definition: htmlediting_impl.cpp:2224
khtml::RemoveCSSPropertyCommandImpl
Definition: htmlediting_impl.h:450
khtml::RemoveCSSPropertyCommandImpl::~RemoveCSSPropertyCommandImpl
virtual ~RemoveCSSPropertyCommandImpl()
Definition: htmlediting_impl.cpp:2263
khtml::RemoveCSSPropertyCommandImpl::doUnapply
virtual void doUnapply()
Definition: htmlediting_impl.cpp:2280
khtml::RemoveCSSPropertyCommandImpl::RemoveCSSPropertyCommandImpl
RemoveCSSPropertyCommandImpl(DOM::DocumentImpl *, DOM::CSSStyleDeclarationImpl *, int property)
Definition: htmlediting_impl.cpp:2256
khtml::RemoveCSSPropertyCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2269
khtml::RemoveNodeAndPruneCommandImpl
Definition: htmlediting_impl.h:513
khtml::RemoveNodeAndPruneCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2403
khtml::RemoveNodeAndPruneCommandImpl::pruneNode
DOM::NodeImpl * pruneNode() const
Definition: htmlediting_impl.h:520
khtml::RemoveNodeAndPruneCommandImpl::~RemoveNodeAndPruneCommandImpl
virtual ~RemoveNodeAndPruneCommandImpl()
Definition: htmlediting_impl.cpp:2396
khtml::RemoveNodeAndPruneCommandImpl::RemoveNodeAndPruneCommandImpl
RemoveNodeAndPruneCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *pruneNode, DOM::NodeImpl *stopNode=0)
Definition: htmlediting_impl.cpp:2387
khtml::RemoveNodeAttributeCommandImpl
Definition: htmlediting_impl.h:472
khtml::RemoveNodeAttributeCommandImpl::RemoveNodeAttributeCommandImpl
RemoveNodeAttributeCommandImpl(DOM::DocumentImpl *, DOM::ElementImpl *, DOM::NodeImpl::Id attribute)
Definition: htmlediting_impl.cpp:2291
khtml::RemoveNodeAttributeCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2304
khtml::RemoveNodeAttributeCommandImpl::~RemoveNodeAttributeCommandImpl
virtual ~RemoveNodeAttributeCommandImpl()
Definition: htmlediting_impl.cpp:2298
khtml::RemoveNodeAttributeCommandImpl::doUnapply
virtual void doUnapply()
Definition: htmlediting_impl.cpp:2316
khtml::RemoveNodeCommandImpl
Definition: htmlediting_impl.h:493
khtml::RemoveNodeCommandImpl::RemoveNodeCommandImpl
RemoveNodeCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *)
Definition: htmlediting_impl.cpp:2329
khtml::RemoveNodeCommandImpl::~RemoveNodeCommandImpl
virtual ~RemoveNodeCommandImpl()
Definition: htmlediting_impl.cpp:2351
khtml::RemoveNodeCommandImpl::node
DOM::NodeImpl * node() const
Definition: htmlediting_impl.h:501
khtml::RemoveNodeCommandImpl::doUnapply
virtual void doUnapply()
Definition: htmlediting_impl.cpp:2371
khtml::RemoveNodeCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2361
khtml::RemoveNodePreservingChildrenCommandImpl
Definition: htmlediting_impl.h:532
khtml::RemoveNodePreservingChildrenCommandImpl::~RemoveNodePreservingChildrenCommandImpl
virtual ~RemoveNodePreservingChildrenCommandImpl()
Definition: htmlediting_impl.cpp:2428
khtml::RemoveNodePreservingChildrenCommandImpl::RemoveNodePreservingChildrenCommandImpl
RemoveNodePreservingChildrenCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *)
Definition: htmlediting_impl.cpp:2421
khtml::RemoveNodePreservingChildrenCommandImpl::node
DOM::NodeImpl * node() const
Definition: htmlediting_impl.h:539
khtml::RemoveNodePreservingChildrenCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2434
khtml::ReplaceSelectionCommandImpl
Definition: htmlediting_impl.h:418
khtml::ReplaceSelectionCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2146
khtml::ReplaceSelectionCommandImpl::~ReplaceSelectionCommandImpl
virtual ~ReplaceSelectionCommandImpl()
Definition: htmlediting_impl.cpp:2142
khtml::ReplaceSelectionCommandImpl::ReplaceSelectionCommandImpl
ReplaceSelectionCommandImpl(DOM::DocumentImpl *document, DOM::DocumentFragmentImpl *fragment, bool selectReplacement=true)
Definition: htmlediting_impl.cpp:2137
khtml::SetNodeAttributeCommandImpl
Definition: htmlediting_impl.h:549
khtml::SetNodeAttributeCommandImpl::~SetNodeAttributeCommandImpl
virtual ~SetNodeAttributeCommandImpl()
Definition: htmlediting_impl.cpp:2457
khtml::SetNodeAttributeCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2463
khtml::SetNodeAttributeCommandImpl::doUnapply
virtual void doUnapply()
Definition: htmlediting_impl.cpp:2474
khtml::SetNodeAttributeCommandImpl::SetNodeAttributeCommandImpl
SetNodeAttributeCommandImpl(DOM::DocumentImpl *, DOM::ElementImpl *, DOM::NodeImpl::Id attribute, const DOM::DOMString &value)
Definition: htmlediting_impl.cpp:2449
khtml::SharedCommandImpl
Definition: htmlediting_impl.h:65
khtml::SplitTextNodeCommandImpl
Definition: htmlediting_impl.h:572
khtml::SplitTextNodeCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2504
khtml::SplitTextNodeCommandImpl::SplitTextNodeCommandImpl
SplitTextNodeCommandImpl(DOM::DocumentImpl *, DOM::TextImpl *, long)
Definition: htmlediting_impl.cpp:2487
khtml::SplitTextNodeCommandImpl::~SplitTextNodeCommandImpl
virtual ~SplitTextNodeCommandImpl()
Definition: htmlediting_impl.cpp:2496
khtml::SplitTextNodeCommandImpl::doUnapply
virtual void doUnapply()
Definition: htmlediting_impl.cpp:2534
khtml::TypingCommandImpl
Definition: htmlediting_impl.h:593
khtml::TypingCommandImpl::insertNewline
void insertNewline()
Definition: htmlediting_impl.cpp:2593
khtml::TypingCommandImpl::deleteKeyPressed
void deleteKeyPressed()
Definition: htmlediting_impl.cpp:2626
khtml::TypingCommandImpl::~TypingCommandImpl
virtual ~TypingCommandImpl()
Definition: htmlediting_impl.cpp:2559
khtml::TypingCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2563
khtml::TypingCommandImpl::insertNewline0
static void insertNewline0(DocumentImpl *document)
Definition: htmlediting_impl.cpp:2694
khtml::TypingCommandImpl::insertText0
static void insertText0(DocumentImpl *document, const DOMString &text)
Definition: htmlediting_impl.cpp:2709
khtml::TypingCommandImpl::deleteKeyPressed0
static void deleteKeyPressed0(DocumentImpl *document)
Definition: htmlediting_impl.cpp:2681
khtml::TypingCommandImpl::insertText
void insertText(const DOM::DOMString &text)
Definition: htmlediting_impl.cpp:2574
khtml::TypingCommandImpl::TypingCommandImpl
TypingCommandImpl(DOM::DocumentImpl *document)
Definition: htmlediting_impl.cpp:2554
css_value.h
assert
#define assert(x)
Definition: editor.cpp:43
editor.h
kDebug
#define kDebug
htmlediting_impl.h
khtml_part.h
d
#define d
Definition: khtmlfind.cpp:42
khtmlview.h
DOM::strcasecmp
bool strcasecmp(const DOMString &a, const DOMString &b)
Definition: dom_string.cpp:348
next
KAction * next(const QObject *recvr, const char *slot, QObject *parent)
del
KGuiItem del()
end
const KShortcut & end()
khtml
khtml::isBlockLevelStyle
static bool isBlockLevelStyle(const CSSStyleDeclarationImpl *style)
Definition: htmlediting_impl.cpp:579
khtml::leadingWhitespacePosition
static Position leadingWhitespacePosition(const Position &pos)
Definition: htmlediting_impl.cpp:153
khtml::shouldPruneNode
static bool shouldPruneNode(NodeImpl *node)
Definition: htmlediting_impl.cpp:116
khtml::isOpenForMoreTypingCommand
static bool isOpenForMoreTypingCommand(const EditCommandImpl *command)
Definition: htmlediting_impl.cpp:2675
khtml::textNodesAreJoinable
static bool textNodesAreJoinable(TextImpl *text1, TextImpl *text2)
Definition: htmlediting_impl.cpp:192
khtml::checkIfNewStylingNeeded
static bool checkIfNewStylingNeeded(ElementImpl *element, CSSStyleDeclarationImpl *style)
Definition: htmlediting_impl.cpp:866
khtml::nonBreakingSpaceString
static DOMString & nonBreakingSpaceString()
Definition: htmlediting_impl.cpp:200
khtml::shouldDeleteUpstreamPosition
static bool shouldDeleteUpstreamPosition(const Position &pos)
Definition: htmlediting_impl.cpp:1015
khtml::applyStyleChangeOnTheNode
static void applyStyleChangeOnTheNode(ElementImpl *element, CSSStyleDeclarationImpl *style)
Definition: htmlediting_impl.cpp:609
khtml::trailingWhitespacePosition
static Position trailingWhitespacePosition(const Position &pos)
Definition: htmlediting_impl.cpp:168
khtml::hasNextListItem
static bool hasNextListItem(NodeImpl *node)
Definition: htmlediting_impl.cpp:2913
khtml::isNBSP
static bool isNBSP(const QChar &c)
Definition: htmlediting_impl.cpp:86
khtml::isWS
static bool isWS(const QChar &c)
Definition: htmlediting_impl.cpp:91
khtml::hasPreviousListItem
static bool hasPreviousListItem(NodeImpl *node)
Definition: htmlediting_impl.cpp:2903
khtml::styleSpanClassString
static DOMString & styleSpanClassString()
Definition: htmlediting_impl.cpp:206
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 20 2023 00:00:00 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KHTML

Skip menu "KHTML"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal