29 #include "css/cssproperties.h" 30 #include "css/css_valueimpl.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" 56 using DOM::CSSPrimitiveValueImpl;
57 using DOM::CSSProperty;
58 using DOM::CSSStyleDeclarationImpl;
59 using DOM::CSSValueImpl;
60 using DOM::DocumentFragmentImpl;
61 using DOM::DocumentImpl;
63 using DOM::DOMStringImpl;
64 using DOM::EditingTextImpl;
65 using DOM::PositionIterator;
66 using DOM::ElementImpl;
67 using DOM::HTMLElementImpl;
68 using DOM::HTMLImageElementImpl;
69 using DOM::NamedAttrMapImpl;
72 using DOM::NodeListImpl;
78 using DOM::TreeWalkerImpl;
81 #define DEBUG_COMMANDS 1 86 static inline bool isNBSP(
const QChar &c)
88 return c == QChar(0xa0);
91 static inline bool isWS(
const QChar &c)
93 return c.isSpace() && c != QChar(0xa0);
101 return isWS(text[0]);
104 static inline bool isWS(
const Position &pos)
109 if (!pos.node()->isTextNode())
112 const DOMString &
string = static_cast<TextImpl *>(pos.node())->data();
113 return isWS(
string[pos.offset()]);
121 RenderObject *renderer = node->renderer();
125 if (node->hasChildNodes())
128 if (node->rootEditableElement() == node)
131 if (renderer->isBR() || renderer->isReplaced())
134 if (node->isTextNode()) {
135 TextImpl *text = static_cast<TextImpl *>(node);
136 if (text->length() == 0)
141 if (!node->isHTMLElement())
144 if (node->id() == ID_BODY)
147 if (!node->isContentEditable())
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()]))
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();
183 DOMString string = static_cast<TextImpl *>(pos.node())->data();
184 if (
isWS(
string[pos.offset()]))
197 return (text1->nextSibling() == text2);
219 assert(m_document->part());
221 m_startingSelection = m_document->part()->caret();
222 m_endingSelection = m_startingSelection;
233 assert(m_document->part());
241 m_document->part()->editor()->appliedEditing(
this);
247 assert(m_document->part());
255 m_document->part()->editor()->unappliedEditing(
this);
261 assert(m_document->part());
269 m_document->part()->editor()->reappliedEditing(
this);
279 m_startingSelection = s;
282 cmd->m_startingSelection = s;
289 m_endingSelection = s;
292 cmd->m_endingSelection = s;
299 return m_parent.get();
321 if (
m_cmds.count() == 0) {
325 for (
int i =
m_cmds.count() - 1; i >= 0; --i)
333 if (
m_cmds.count() == 0) {
336 QMutableListIterator<RefPtr<EditCommandImpl> > it(
m_cmds);
338 it.next()->reapply();
350 cmd->setParent(
this);
363 if (refChild->parentNode()->lastChild() == refChild) {
364 appendNode(refChild->parentNode(), insertChild);
367 assert(refChild->nextSibling());
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();
383 else if (refChild->caretMinOffset() >= offset) {
386 else if (refChild->isTextNode() && refChild->caretMaxOffset() > offset) {
468 if (selection.state() == Selection::RANGE) {
506 int exceptionCode = 0;
507 ElementImpl *styleElement =
document()->createHTMLElement(
"SPAN");
510 styleElement->setAttribute(ATTR_STYLE,
document()->part()->editor()->typingStyle()->cssText().implementation());
514 assert(exceptionCode == 0);
525 :
EditCommandImpl(document), m_parentNode(parentNode), m_appendChild(appendChild)
531 m_appendChild->ref();
537 m_parentNode->deref();
539 m_appendChild->deref();
547 int exceptionCode = 0;
548 m_parentNode->appendChild(m_appendChild, exceptionCode);
549 assert(exceptionCode == 0);
558 int exceptionCode = 0;
559 m_parentNode->removeChild(m_appendChild, exceptionCode);
560 assert(exceptionCode == 0);
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:
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;
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;
627 if (
strcasecmp(computedValue->cssText(), newValue)) {
629 element->getInlineStyleDecls()->setProperty(property->id(), newValue);
640 Position start(
endingSelection().start().equivalentDownstreamPosition().equivalentRangeCompliantPosition());
642 #ifdef DEBUG_COMMANDS 643 kDebug() <<
"[APPLY STYLE]" << start <<
end << endl;
644 printEnclosingBlockTree(start.node()->enclosingBlockFlowElement());
648 #ifdef DEBUG_COMMANDS 649 kDebug() <<
"[APPLY BLOCK LEVEL STYLE]" << endl;
651 ElementImpl *startBlock = start.node()->enclosingBlockFlowElement();
652 ElementImpl *endBlock =
end.node()->enclosingBlockFlowElement();
653 #ifdef DEBUG_COMMANDS 654 kDebug() << startBlock << startBlock->nodeName() << endl;
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;
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;
670 }
else if (startBlock == endBlock) {
680 removeStyle(start,
end);
681 bool splitStart = splitTextAtStartIfNeeded(start,
end);
686 splitTextAtEndIfNeeded(start,
end);
690 #ifdef DEBUG_COMMANDS 691 kDebug() <<
"[start;end]" << start <<
end << endl;
693 if (start.node() ==
end.node()) {
695 applyStyleIfNeeded(start.node(),
end.node());
697 NodeImpl *node = start.node();
699 if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
700 NodeImpl *runStart = node;
702 if (runStart->parentNode() != node->parentNode() || node->isHTMLElement() || node ==
end.node() ||
703 (node->renderer() && !node->renderer()->isInline())) {
704 applyStyleIfNeeded(runStart, node);
707 node = node->traverseNextNode();
710 if (node ==
end.node())
712 node = node->traverseNextNode();
720 bool ApplyStyleCommandImpl::isHTMLStyleNode(HTMLElementImpl *elem)
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)
730 case CSS_PROP_FONT_STYLE:
731 if (elem->id() == ID_I)
740 void ApplyStyleCommandImpl::removeHTMLStyleNode(HTMLElementImpl *elem)
750 void ApplyStyleCommandImpl::removeCSSStyle(HTMLElementImpl *elem)
754 CSSStyleDeclarationImpl *decl = elem->inlineStyleDecls();
758 QListIterator<CSSProperty*> it(*(
style()->values()));
759 while ( it.hasNext() ) {
760 CSSProperty *
property = it.next();
761 if (decl->getPropertyCSSValue(property->id()))
765 if (elem->id() == ID_SPAN) {
769 NamedAttrMapImpl *map = elem->attributes();
770 if (map && (map->length() == 1 || (map->length() == 2 && elem->getAttribute(ATTR_STYLE).isEmpty())) &&
776 void ApplyStyleCommandImpl::removeStyle(
const Position &start,
const Position &end)
778 NodeImpl *node = start.node();
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);
786 removeCSSStyle(elem);
788 if (node ==
end.node())
794 bool ApplyStyleCommandImpl::nodeFullySelected(
const NodeImpl *node)
const 800 if (node ==
end.node())
801 return end.offset() >= node->caretMaxOffset();
803 for (NodeImpl *child = node->lastChild(); child; child = child->lastChild()) {
804 if (child ==
end.node())
805 return end.offset() >= child->caretMaxOffset();
808 return node ==
end.node() || !node->isAncestor(
end.node());
814 bool ApplyStyleCommandImpl::splitTextAtStartIfNeeded(
const Position &start,
const Position &end)
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;
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());
830 NodeImpl *ApplyStyleCommandImpl::splitTextAtEndIfNeeded(
const Position &start,
const Position &end)
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;
836 TextImpl *text = static_cast<TextImpl *>(
end.node());
837 RefPtr<SplitTextNodeCommandImpl> cmd =
new SplitTextNodeCommandImpl(
document(), text,
end.offset());
839 NodeImpl *startNode = start.node() ==
end.node() ? cmd->node()->previousSibling() : start.node();
841 setEndingSelection(Selection(Position(startNode, start.offset()), Position(cmd->node()->previousSibling(), cmd->node()->previousSibling()->caretMaxOffset())));
842 return cmd->node()->previousSibling();
847 void ApplyStyleCommandImpl::surroundNodeRangeWithElement(NodeImpl *startNode, NodeImpl *endNode, ElementImpl *element)
853 NodeImpl *node = startNode;
855 NodeImpl *
next = node->traverseNextNode();
856 if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
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;
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;
884 if (
strcasecmp(computedValue->cssText(), newValue))
890 void ApplyStyleCommandImpl::applyStyleIfNeeded(DOM::NodeImpl *startNode, DOM::NodeImpl *endNode)
892 ElementImpl *
parent = Position(startNode, 0).element();
895 ElementImpl *styleElement = 0;
896 if (
parent->id() == ID_SPAN &&
parent->firstChild() == startNode &&
parent->lastChild() == endNode) {
899 styleElement =
document()->createHTMLElement(
"SPAN");
902 surroundNodeRangeWithElement(startNode, endNode, styleElement);
907 bool ApplyStyleCommandImpl::currentlyHasStyle(
const Position &pos,
const CSSProperty *property)
const 911 CSSStyleDeclarationImpl *decl =
document()->defaultView()->getComputedStyle(pos.element(), 0);
913 CSSValueImpl *value = decl->getPropertyCSSValue(property->id());
914 return strcasecmp(value->cssText(),
property->value()->cssText()) == 0;
917 ApplyStyleCommandImpl::StyleChange ApplyStyleCommandImpl::computeStyleChange(
const Position &insertionPoint, CSSStyleDeclarationImpl *style)
919 assert(insertionPoint.notEmpty());
922 StyleChange styleChange;
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;
930 if (!currentlyHasStyle(insertionPoint, property)) {
931 #ifdef DEBUG_COMMANDS 932 kDebug() <<
"[Add to style change]" << endl;
934 switch (property->id()) {
935 case CSS_PROP_FONT_WEIGHT:
936 if (
strcasecmp(property->value()->cssText(),
"bold") == 0)
937 styleChange.applyBold =
true;
939 styleChange.cssStyle +=
property->cssText();
941 case CSS_PROP_FONT_STYLE: {
942 DOMString cssText(property->value()->cssText());
944 styleChange.applyItalic =
true;
946 styleChange.cssStyle += property->cssText();
950 styleChange.cssStyle +=
property->cssText();
958 Position ApplyStyleCommandImpl::positionInsertionPoint(Position pos)
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());
963 pos = Position(split->node(), 0);
971 if (currentlyHasStyle(pos))
975 if (pos.offset() >= pos.node()->caretMaxOffset()) {
976 NodeImpl *nextNode = pos.node()->traverseNextNode();
978 Position
next = Position(nextNode, 0);
979 if (currentlyHasStyle(next))
985 if (pos.offset() <= pos.node()->caretMinOffset()) {
986 NodeImpl *prevNode = pos.node()->traversePreviousNode();
988 Position prev = Position(prevNode, prevNode->maxOffset());
989 if (currentlyHasStyle(prev))
1002 : CompositeEditCommandImpl(document), m_charactersDeleted(0), m_hasSelectionToCollapse(false)
1006 DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl(DocumentImpl *document,
const Selection &selection)
1007 : CompositeEditCommandImpl(document), m_charactersDeleted(0), m_selectionToCollapse(selection), m_hasSelectionToCollapse(true)
1011 DeleteCollapsibleWhitespaceCommandImpl::~DeleteCollapsibleWhitespaceCommandImpl()
1017 if (!pos.node()->isTextNode())
1020 RenderObject *renderer = pos.node()->renderer();
1024 TextImpl *textNode = static_cast<TextImpl *>(pos.node());
1025 if (pos.offset() >= (long)textNode->length())
1028 if (pos.isLastRenderedPositionInEditableBlock())
1031 if (pos.isFirstRenderedPositionOnLine() || pos.isLastRenderedPositionOnLine())
1048 Position DeleteCollapsibleWhitespaceCommandImpl::deleteWhitespace(
const Position &pos)
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());
1059 #ifdef DEBUG_COMMANDS 1060 kDebug() <<
"[delete upstream]" <<
del << endl;
1063 if (upstream == downstream)
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;
1074 PositionIterator it(upstream);
1075 Position deleteStart = upstream;
1077 deleteStart = it.peekNext();
1078 if (deleteStart == downstream)
1082 Position endingPosition = upstream;
1084 while (it.current() != downstream) {
1085 Position
next = it.peekNext();
1086 #ifdef DEBUG_COMMANDS 1087 kDebug() <<
"[iterate and delete]" <<
next << endl;
1089 if (
next.node() != deleteStart.node()) {
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;
1098 if (textNode == endingPosition.node())
1099 endingPosition = Position(
next.node(),
next.node()->caretMinOffset());
1100 removeNodeAndPrune(textNode);
1102 #ifdef DEBUG_COMMANDS 1103 kDebug(6200) <<
" deleteText 1:" << textNode <<
"t len:" << textNode->length()<<
"start:" << deleteStart.offset() <<
"del len:" << (it.current().offset() - deleteStart.offset());
1105 deleteText(textNode, deleteStart.offset(), count);
1108 #ifdef DEBUG_COMMANDS 1109 kDebug() <<
"[not text node is not supported yet]" << endl;
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;
1123 removeNodeAndPrune(textNode);
1125 #ifdef DEBUG_COMMANDS 1126 kDebug(6200) <<
" deleteText 2:"<< textNode<<
"t len:" << textNode->length() <<
"start:" <<deleteStart.offset() <<
"del len:" << count;
1128 deleteText(textNode, deleteStart.offset(), count);
1129 m_charactersDeleted = count;
1130 endingPosition = Position(downstream.node(), downstream.offset() - m_charactersDeleted);
1134 it.setPosition(next);
1137 return endingPosition;
1140 void DeleteCollapsibleWhitespaceCommandImpl::doApply()
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) <<
"-----------------------------------------------------";
1154 else if (state == Selection::RANGE) {
1155 Position startPosition = deleteWhitespace(m_selectionToCollapse.start());
1156 #ifdef DEBUG_COMMANDS 1157 kDebug(6200) <<
"-----------------------------------------------------";
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;
1164 endPosition = Position(endPosition.node(), endPosition.offset() - m_charactersDeleted);
1166 endPosition = deleteWhitespace(endPosition);
1167 setEndingSelection(Selection(startPosition, endPosition));
1168 #ifdef DEBUG_COMMANDS 1169 kDebug(6200) <<
"=====================================================";
1177 DeleteSelectionCommandImpl::DeleteSelectionCommandImpl(DocumentImpl *document)
1183 : CompositeEditCommandImpl(document), m_selectionToDelete(selection), m_hasSelectionToDelete(true)
1191 void DeleteSelectionCommandImpl::joinTextNodesWithSameStyle()
1195 if (selection.state() != Selection::CARET)
1198 Position pos(selection.start());
1200 if (!pos.node()->isTextNode())
1203 TextImpl *textNode = static_cast<TextImpl *>(pos.node());
1205 if (pos.offset() == 0) {
1206 PositionIterator it(pos);
1207 Position prev = it.previous();
1210 if (prev.node()->isTextNode()) {
1211 TextImpl *prevTextNode = static_cast<TextImpl *>(prev.node());
1215 #ifdef DEBUG_COMMANDS 1216 kDebug(6200) <<
"joinTextNodesWithSameStyle [1]";
1220 }
else if (pos.offset() == (long)textNode->length()) {
1221 PositionIterator it(pos);
1222 Position
next = it.next();
1225 if (
next.node()->isTextNode()) {
1226 TextImpl *nextTextNode = static_cast<TextImpl *>(
next.node());
1230 #ifdef DEBUG_COMMANDS 1231 kDebug(6200) <<
"joinTextNodesWithSameStyle [2]";
1238 bool DeleteSelectionCommandImpl::containsOnlyWhitespace(
const Position &start,
const Position &end)
1242 PositionIterator it(start);
1243 while (!it.atEnd()) {
1244 if (!it.current().node()->isTextNode())
1246 const DOMString &text = static_cast<TextImpl *>(it.current().node())->data();
1248 if (text.
length() > INT_MAX)
1250 if (it.current().offset() < (int)text.
length() && !
isWS(text[it.current().offset()]))
1253 if (it.current() ==
end)
1259 void DeleteSelectionCommandImpl::deleteContentInsideNode(NodeImpl *node,
int startOffset,
int endOffset)
1261 #ifdef DEBUG_COMMANDS 1262 kDebug() <<
"[Delete content inside node]" << node << startOffset << endOffset << endl;
1264 if (node->isTextNode()) {
1266 if (startOffset == endOffset)
1269 if (!startOffset && endOffset == node->maxOffset()) {
1274 deleteText(static_cast<TextImpl*>(node), startOffset, endOffset - startOffset);
1277 #ifdef DEBUG_COMMANDS 1278 kDebug() <<
"[non-text node] not supported" << endl;
1282 void DeleteSelectionCommandImpl::deleteContentBeforeOffset(NodeImpl *node,
int offset)
1284 deleteContentInsideNode(node, 0, offset);
1287 void DeleteSelectionCommandImpl::deleteContentAfterOffset(NodeImpl *node,
int offset)
1289 if (node->isTextNode())
1290 deleteContentInsideNode(node, offset, node->maxOffset());
1297 if (!m_hasSelectionToDelete)
1300 if (m_selectionToDelete.state() != Selection::RANGE)
1306 Position upstreamStart(selection.start().equivalentUpstreamPosition());
1307 Position downstreamStart(selection.start().equivalentDownstreamPosition());
1308 Position upstreamEnd(selection.end().equivalentUpstreamPosition());
1309 Position downstreamEnd(selection.end().equivalentDownstreamPosition());
1311 NodeImpl *startBlock = upstreamStart.node()->enclosingBlockFlowElement();
1312 NodeImpl *endBlock = downstreamEnd.node()->enclosingBlockFlowElement();
1314 #ifdef DEBUG_COMMANDS 1315 kDebug() <<
"[Delete:Start]" << upstreamStart << downstreamStart << endl;
1316 kDebug() <<
"[Delete:End]" << upstreamEnd << downstreamEnd << endl;
1317 printEnclosingBlockTree(upstreamStart.node());
1319 if (startBlock != endBlock)
1320 printEnclosingBlockTree(downstreamEnd.node());
1322 if (upstreamStart == downstreamEnd)
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;
1333 next = node->traverseNextNode();
1334 if (node->renderer() && node->renderer()->isEditable())
1340 if (startBlock != endBlock && startBlock->parentNode() == endBlock->parentNode()) {
1341 NodeImpl *node = endBlock->firstChild();
1343 NodeImpl *moveNode = node;
1344 node = node->nextSibling();
1350 if (upstreamStart.node() == downstreamEnd.node())
1351 deleteContentInsideNode(upstreamEnd.node(), upstreamStart.offset(), downstreamEnd.offset());
1353 deleteContentAfterOffset(upstreamStart.node(), upstreamStart.offset());
1354 deleteContentBeforeOffset(downstreamEnd.node(), downstreamEnd.offset());
1359 Position endingPosition;
1360 bool adjustEndingPositionDownstream =
false;
1362 bool onlyWhitespace = containsOnlyWhitespace(upstreamStart, downstreamEnd);
1363 kDebug() <<
"[OnlyWhitespace]" << onlyWhitespace << endl;
1365 bool startCompletelySelected = !onlyWhitespace &&
1366 (downstreamStart.offset() <= downstreamStart.node()->caretMinOffset() &&
1367 ((downstreamStart.node() != upstreamEnd.node()) ||
1368 (upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset())));
1370 bool endCompletelySelected = !onlyWhitespace &&
1371 (upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset() &&
1372 ((downstreamStart.node() != upstreamEnd.node()) ||
1373 (downstreamStart.offset() <= downstreamStart.node()->caretMinOffset())));
1375 kDebug() <<
"[{start:end}CompletelySelected]" << startCompletelySelected << endCompletelySelected << endl;
1377 unsigned long startRenderedOffset = downstreamStart.renderedOffset();
1379 bool startAtStartOfRootEditableElement = startRenderedOffset == 0 && downstreamStart.inFirstEditableInRootEditableElement();
1380 bool startAtStartOfBlock = startAtStartOfRootEditableElement ||
1381 (startRenderedOffset == 0 && downstreamStart.inFirstEditableInContainingEditableBlock());
1382 bool endAtEndOfBlock = downstreamEnd.isLastRenderedPositionInEditableBlock();
1384 kDebug() <<
"[startAtStartOfRootEditableElement]" << startAtStartOfRootEditableElement << endl;
1385 kDebug() <<
"[startAtStartOfBlock]" << startAtStartOfBlock << endl;
1386 kDebug() <<
"[endAtEndOfBlock]" << endAtEndOfBlock << endl;
1388 NodeImpl *startBlock = upstreamStart.node()->enclosingBlockFlowElement();
1389 NodeImpl *endBlock = downstreamEnd.node()->enclosingBlockFlowElement();
1390 bool startBlockEndBlockAreSiblings = startBlock->parentNode() == endBlock->parentNode();
1392 kDebug() <<
"[startBlockEndBlockAreSiblings]" << startBlockEndBlockAreSiblings << startBlock << endBlock << endl;
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");
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;
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;
1424 if ((startAtStartOfBlock && !endAtEndOfBlock) || (!startCompletelySelected && adjustEndingPositionDownstream)) {
1427 if (trailing.notEmpty()) {
1428 debugPosition(
"convertTrailingWhitespace: ", trailing);
1429 Position collapse = trailing.nextCharacterPosition();
1430 if (collapse != trailing)
1432 TextImpl *textNode = static_cast<TextImpl *>(trailing.node());
1435 }
else if (!startAtStartOfBlock && endAtEndOfBlock) {
1438 if (leading.notEmpty()) {
1439 debugPosition(
"convertLeadingWhitespace: ", leading);
1440 TextImpl *textNode = static_cast<TextImpl *>(leading.node());
1443 }
else if (!startAtStartOfBlock && !endAtEndOfBlock) {
1447 if (leading.notEmpty() && trailing.notEmpty()) {
1448 debugPosition(
"convertLeadingWhitespace [contiguous]: ", leading);
1449 TextImpl *textNode = static_cast<TextImpl *>(leading.node());
1457 NodeImpl *n = downstreamStart.node()->traverseNextNode();
1458 kDebug() <<
"[n]" << n << endl;
1461 if (startCompletelySelected) {
1462 kDebug(6200) <<
"start node delete case 1";
1464 }
else if (onlyWhitespace) {
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();
1473 int length = text->
length();
1474 if (length == upstreamStart.offset())
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());
1487 kDebug(6200) <<
"start node delete case 4";
1488 assert(downstreamStart.offset() == 1);
1491 if (n && !onlyWhitespace && downstreamStart.node() != upstreamEnd.node()) {
1493 while (n && n != upstreamEnd.node()) {
1495 n = n->traverseNextNode();
1496 if (
d->renderer() &&
d->renderer()->isEditable())
1503 assert(n == upstreamEnd.node());
1504 if (endCompletelySelected) {
1507 else if (upstreamEnd.node()->isTextNode()) {
1508 if (upstreamEnd.offset() > 0) {
1509 TextImpl *text = static_cast<TextImpl *>(upstreamEnd.node());
1516 assert(downstreamStart.offset() == 0);
1525 if (startBlock != endBlock && startBlockEndBlockAreSiblings) {
1526 kDebug(6200) <<
"merging content to start block";
1527 NodeImpl *node = endBlock->firstChild();
1529 NodeImpl *moveNode = node;
1530 node = node->nextSibling();
1536 if (adjustEndingPositionDownstream) {
1537 kDebug(6200) <<
"adjust ending position downstream";
1538 endingPosition = endingPosition.equivalentDownstreamPosition();
1541 debugPosition(
"ending position: ", endingPosition);
1544 kDebug(6200) <<
"-----------------------------------------------------";
1552 :
EditCommandImpl(document), m_node(node), m_offset(offset), m_count(count)
1571 int exceptionCode = 0;
1572 m_text = m_node->substringData(m_offset, m_count, exceptionCode);
1573 assert(exceptionCode == 0);
1575 m_node->deleteData(m_offset, m_count, exceptionCode);
1576 assert(exceptionCode == 0);
1584 int exceptionCode = 0;
1585 m_node->insertData(m_offset, m_text, exceptionCode);
1586 assert(exceptionCode == 0);
1601 void InputNewlineCommandImpl::insertNodeAfterPosition(NodeImpl *node,
const Position &pos)
1606 Position upstream(pos.equivalentUpstreamPosition());
1607 NodeImpl *cb = pos.node()->enclosingBlockFlowElement();
1608 if (cb == pos.node())
1614 void InputNewlineCommandImpl::insertNodeBeforePosition(NodeImpl *node,
const Position &pos)
1619 Position upstream(pos.equivalentUpstreamPosition());
1620 NodeImpl *cb = pos.node()->enclosingBlockFlowElement();
1621 if (cb == pos.node())
1631 int exceptionCode = 0;
1633 NodeImpl *enclosingBlock = selection.start().node()->enclosingBlockFlowElement();
1634 kDebug() << enclosingBlock->nodeName() << endl;
1635 if (enclosingBlock->id() == ID_LI) {
1640 #ifdef DEBUG_COMMANDS 1641 kDebug() <<
"[insert new list item]" << selection << endl;
1642 printEnclosingBlockTree(selection.start().node());
1644 Position pos(selection.start().equivalentDownstreamPosition());
1645 NodeImpl *node = pos.node();
1646 bool atBlockStart = pos.atStartOfContainingEditableBlock();
1647 bool atBlockEnd = pos.isLastRenderedPositionInEditableBlock();
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));
1654 pos = Position(textNode, 0);
1659 #ifdef DEBUG_COMMANDS 1660 kDebug() <<
"[handle node]" << node << endl;
1661 printEnclosingBlockTree(enclosingBlock->parent());
1665 RefPtr<NodeImpl> newParent =
parent->cloneNode(
false);
1667 for (NodeImpl *nextSibling = 0; node; node = nextSibling) {
1668 #ifdef DEBUG_COMMANDS 1669 kDebug() <<
"[reattach sibling]" << node << endl;
1671 nextSibling = node->nextSibling();
1675 node = newParent.get();
1676 if (
parent == enclosingBlock)
1679 }
else if (node->isTextNode()) {
1682 ElementImpl *listItem =
document()->createHTMLElement(
"LI");
1685 ElementImpl *listItem =
document()->createHTMLElement(
"LI");
1690 #ifdef DEBUG_COMMANDS 1691 kDebug() <<
"[result]" << endl;
1692 printEnclosingBlockTree(enclosingBlock->parent());
1698 ElementImpl *breakNode =
document()->createHTMLElement(
"BR");
1701 #ifdef DEBUG_COMMANDS 1702 kDebug() <<
"[insert break]" << selection << endl;
1703 printEnclosingBlockTree(enclosingBlock);
1706 NodeImpl *nodeToInsert = breakNode;
1708 if (
document()->part()->editor()->typingStyle()) {
1709 int exceptionCode = 0;
1711 styleElement->appendChild(breakNode, exceptionCode);
1712 assert(exceptionCode == 0);
1713 nodeToInsert = styleElement;
1716 Position pos(selection.start().equivalentDownstreamPosition());
1717 bool atStart = pos.offset() <= pos.node()->caretMinOffset();
1718 bool atEndOfBlock = pos.isLastRenderedPositionInEditableBlock();
1720 #ifdef DEBUG_COMMANDS 1721 kDebug() <<
"[pos]" << pos << atStart << atEndOfBlock << endl;
1725 #ifdef DEBUG_COMMANDS 1726 kDebug(6200) <<
"input newline case 1";
1731 insertNodeAfterPosition(nodeToInsert, pos);
1733 ElementImpl *extraBreakNode =
document()->createHTMLElement(
"BR");
1737 }
else if (atStart) {
1738 #ifdef DEBUG_COMMANDS 1739 kDebug(6200) <<
"input newline case 2";
1744 insertNodeBeforePosition(nodeToInsert, pos);
1750 #ifdef DEBUG_COMMANDS 1751 kDebug(6200) <<
"input newline case 3";
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());
1790 if (!selection.start().node()->isTextNode())
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));
1801 m_charactersAdded--;
1805 Position InputTextCommandImpl::prepareForTextInsertion(
bool adjustDownstream)
1810 assert(selection.state() == Selection::CARET);
1812 #ifdef DEBUG_COMMANDS 1813 kDebug() <<
"[prepare selection]" << selection << endl;
1816 Position pos = selection.start();
1817 if (adjustDownstream)
1818 pos = pos.equivalentDownstreamPosition();
1820 pos = pos.equivalentUpstreamPosition();
1822 #ifdef DEBUG_COMMANDS 1823 kDebug() <<
"[prepare position]" << pos << endl;
1826 if (!pos.node()->isTextNode()) {
1827 NodeImpl *textNode =
document()->createEditingTextNode(
"");
1828 NodeImpl *nodeToInsert = textNode;
1829 if (
document()->part()->editor()->typingStyle()) {
1830 int exceptionCode = 0;
1832 styleElement->appendChild(textNode, exceptionCode);
1833 assert(exceptionCode == 0);
1834 nodeToInsert = styleElement;
1838 if (pos.node()->isEditableBlock()) {
1839 kDebug(6200) <<
"prepareForTextInsertion case 1";
1841 }
else if (pos.node()->id() == ID_BR && pos.offset() == 1) {
1842 kDebug(6200) <<
"prepareForTextInsertion case 2";
1844 }
else if (pos.node()->caretMinOffset() == pos.offset()) {
1845 kDebug(6200) <<
"prepareForTextInsertion case 3";
1847 }
else if (pos.node()->caretMaxOffset() == pos.offset()) {
1848 kDebug(6200) <<
"prepareForTextInsertion case 4";
1853 pos = Position(textNode, 0);
1856 if (
document()->part()->editor()->typingStyle()) {
1857 if (pos.node()->isTextNode() && pos.offset() > pos.node()->caretMinOffset() && pos.offset() < pos.node()->caretMaxOffset()) {
1859 TextImpl *text = static_cast<TextImpl *>(pos.node());
1860 RefPtr<SplitTextNodeCommandImpl> cmd =
new SplitTextNodeCommandImpl(
document(), text, pos.offset());
1865 int exceptionCode = 0;
1866 TextImpl *editingTextNode =
document()->createEditingTextNode(
"");
1869 styleElement->appendChild(editingTextNode, exceptionCode);
1870 assert(exceptionCode == 0);
1877 pos = Position(editingTextNode, 0);
1883 void InputTextCommandImpl::execute(
const DOMString &text)
1885 #ifdef DEBUG_COMMANDS 1886 kDebug() <<
"[execute command]" << text << endl;
1889 #ifdef DEBUG_COMMANDS 1890 kDebug() <<
"[ending selection]" << selection << endl;
1892 bool adjustDownstream = selection.start().isFirstRenderedPositionOnLine();
1893 #ifdef DEBUG_COMMANDS 1894 kDebug() <<
"[adjust]" << adjustDownstream << endl;
1897 #ifdef DEBUG_COMMANDS 1898 printEnclosingBlockTree(selection.start().node());
1902 if (selection.state() == Selection::RANGE)
1907 #ifdef DEBUG_COMMANDS 1908 kDebug() <<
"[after collapsible whitespace deletion]" << endl;
1909 printEnclosingBlockTree(selection.start().node());
1915 Position pos = prepareForTextInsertion(adjustDownstream);
1916 #ifdef DEBUG_COMMANDS 1917 kDebug() <<
"[after prepare]" << pos << endl;
1920 TextImpl *textNode = static_cast<TextImpl *>(pos.node());
1921 long offset = pos.offset();
1923 #ifdef DEBUG_COMMANDS 1924 kDebug() <<
"[insert at]" << textNode << offset << endl;
1931 insertSpace(textNode, offset);
1933 const DOMString &existingText = textNode->data();
1934 if (textNode->length() >= 2 && offset >= 2 &&
isNBSP(existingText[offset - 1]) && !
isWS(existingText[offset - 2])) {
1946 m_charactersAdded += text.
length();
1949 void InputTextCommandImpl::insertSpace(TextImpl *textNode,
unsigned long offset)
1959 for (
unsigned int i = offset; i < text.
length(); i++) {
1968 Position pos(textNode, offset);
1969 Position downstream = pos.equivalentDownstreamPosition();
1970 if (downstream.offset() < (long)text.
length() &&
isWS(text[downstream.offset()]))
1976 if (offset > 0 && offset <= text.
length() - 1 && !
isWS(text[offset]) && !
isWS(text[offset - 1])) {
1982 if (text.
length() >= 2 && offset >= 2 &&
isNBSP(text[offset - 2]) &&
isNBSP(text[offset - 1])) {
1998 :
EditCommandImpl(document), m_insertChild(insertChild), m_refChild(refChild)
2001 m_insertChild->ref();
2010 m_insertChild->deref();
2012 m_refChild->deref();
2019 assert(m_refChild->parentNode());
2021 int exceptionCode = 0;
2022 m_refChild->parentNode()->insertBefore(m_insertChild, m_refChild, exceptionCode);
2023 assert(exceptionCode == 0);
2030 assert(m_refChild->parentNode());
2032 int exceptionCode = 0;
2033 m_refChild->parentNode()->removeChild(m_insertChild, exceptionCode);
2034 assert(exceptionCode == 0);
2062 int exceptionCode = 0;
2063 m_node->insertData(m_offset, m_text, exceptionCode);
2064 assert(exceptionCode == 0);
2072 int exceptionCode = 0;
2073 m_node->deleteData(m_offset, m_text.
length(), exceptionCode);
2074 assert(exceptionCode == 0);
2085 assert(m_text1->nextSibling() == m_text2);
2086 assert(m_text1->length() > 0);
2087 assert(m_text2->length() > 0);
2105 assert(m_text1->nextSibling() == m_text2);
2107 int exceptionCode = 0;
2108 m_text2->insertData(0, m_text1->data(), exceptionCode);
2109 assert(exceptionCode == 0);
2111 m_text2->parentNode()->removeChild(m_text1, exceptionCode);
2112 assert(exceptionCode == 0);
2114 m_offset = m_text1->length();
2122 int exceptionCode = 0;
2124 m_text2->deleteData(0, m_offset, exceptionCode);
2125 assert(exceptionCode == 0);
2127 m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
2128 assert(exceptionCode == 0);
2130 assert(m_text2->previousSibling()->isTextNode());
2131 assert(m_text2->previousSibling() == m_text1);
2148 NodeImpl *firstChild = m_fragment->firstChild();
2149 NodeImpl *lastChild = m_fragment->lastChild();
2154 if (selection.state() == Selection::RANGE)
2160 assert(!selection.isEmpty());
2165 }
else if (firstChild == lastChild && firstChild->isTextNode()) {
2167 Position base = selection.base();
2168 inputText(static_cast<TextImpl *>(firstChild)->data());
2169 if (m_selectReplacement) {
2175 NodeImpl *beforeNode = firstChild;
2176 NodeImpl *node = firstChild->nextSibling();
2178 insertNodeAt(firstChild, selection.start().node(), selection.start().offset());
2182 NodeImpl *
next = node->nextSibling();
2190 NodeImpl *lastLeaf = lastChild;
2192 NodeImpl *nextChild = lastLeaf->lastChild();
2195 lastLeaf = nextChild;
2198 if (m_selectReplacement) {
2200 NodeImpl *firstLeaf = firstChild;
2202 NodeImpl *nextChild = firstLeaf->firstChild();
2205 firstLeaf = nextChild;
2208 setEndingSelection(Selection(Position(firstLeaf, firstLeaf->caretMinOffset()), Position(lastLeaf, lastLeaf->caretMaxOffset())));
2231 assert(selection.state() == Selection::RANGE);
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();
2257 :
EditCommandImpl(document), m_decl(decl), m_property(property), m_important(false)
2273 m_oldValue = m_decl->getPropertyValue(m_property);
2276 m_important = m_decl->getPropertyPriority(m_property);
2277 m_decl->removeProperty(m_property);
2285 m_decl->setProperty(m_property, m_oldValue, m_important);
2292 :
EditCommandImpl(document), m_element(element), m_attribute(attribute)
2308 m_oldValue = m_element->getAttribute(m_attribute);
2311 int exceptionCode = 0;
2312 m_element->removeAttribute(m_attribute, exceptionCode);
2313 assert(exceptionCode == 0);
2322 m_element->setAttribute(m_attribute, m_oldValue.
implementation());
2330 :
EditCommandImpl(document), m_parent(0), m_removeChild(removeChild), m_refChild(0)
2333 m_removeChild->ref();
2335 m_parent = m_removeChild->parentNode();
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)
2356 m_removeChild->deref();
2358 m_refChild->deref();
2366 int exceptionCode = 0;
2367 m_parent->removeChild(m_removeChild, exceptionCode);
2368 assert(exceptionCode == 0);
2376 int exceptionCode = 0;
2378 m_parent->insertBefore(m_removeChild, m_refChild, exceptionCode);
2380 m_parent->appendChild(m_removeChild, exceptionCode);
2381 assert(exceptionCode == 0);
2398 m_pruneNode->deref();
2400 m_stopNode->deref();
2405 NodeImpl *editableBlock = m_pruneNode->enclosingBlockFlowElement();
2407 NodeImpl *node =
pruneNode->traversePreviousNode();
2410 if (node == m_stopNode || editableBlock != node->enclosingBlockFlowElement() || !
shouldPruneNode(node))
2413 node = node->traversePreviousNode();
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);
2450 :
EditCommandImpl(document), m_element(element), m_attribute(attribute), m_value(value)
2469 m_oldValue = m_element->getAttribute(m_attribute);
2480 m_element->setAttribute(m_attribute, m_oldValue.
implementation());
2488 :
EditCommandImpl(document), m_text1(0), m_text2(text), m_offset(offset)
2491 assert(m_text2->length() > 0);
2509 int exceptionCode = 0;
2518 m_text1 =
document()->createTextNode(m_text2->substringData(0, m_offset, exceptionCode));
2519 assert(exceptionCode == 0);
2524 m_text2->deleteData(0, m_offset, exceptionCode);
2525 assert(exceptionCode == 0);
2527 m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
2528 assert(exceptionCode == 0);
2530 assert(m_text2->previousSibling()->isTextNode());
2531 assert(m_text2->previousSibling() == m_text1);
2539 assert(m_text1->nextSibling() == m_text2);
2541 int exceptionCode = 0;
2542 m_text2->insertData(0, m_text1->data(), exceptionCode);
2543 assert(exceptionCode == 0);
2545 m_text2->parentNode()->removeChild(m_text1, exceptionCode);
2546 assert(exceptionCode == 0);
2548 m_offset = m_text1->length();
2567 void TypingCommandImpl::typingAddedToOpenCommand()
2571 document()->part()->editor()->appliedEditing(
this);
2576 if (
document()->part()->editor()->typingStyle() ||
m_cmds.count() == 0) {
2583 static_cast<InputTextCommandImpl*>(lastCommand)->input(text);
2590 typingAddedToOpenCommand();
2597 typingAddedToOpenCommand();
2600 void TypingCommandImpl::issueCommandForDeleteKey()
2603 assert(selectionToDelete.state() != Selection::NONE);
2605 #ifdef DEBUG_COMMANDS 2606 kDebug() <<
"[selection]" << selectionToDelete << endl;
2608 if (selectionToDelete.state() == Selection::CARET) {
2609 #ifdef DEBUG_COMMANDS 2610 kDebug() <<
"[caret selection]" << endl;
2612 Position pos(selectionToDelete.start());
2613 if (pos.inFirstEditableInRootEditableElement() && pos.offset() <= pos.node()->caretMinOffset()) {
2617 selectionToDelete = Selection(pos.previousCharacterPosition(), pos);
2618 #ifdef DEBUG_COMMANDS 2619 kDebug() <<
"[modified selection]" << selectionToDelete << endl;
2623 typingAddedToOpenCommand();
2636 issueCommandForDeleteKey();
2638 if (
m_cmds.count() == 0) {
2639 issueCommandForDeleteKey();
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) {
2650 else if (lastCommand.commandID() == InputNewlineCommandID) {
2651 lastCommand.unapply();
2652 removeCommand(lastCommand);
2655 issueCommandForDeleteKey();
2661 void TypingCommandImpl::removeCommand(
const PassRefPtr<EditCommandImpl> cmd)
2678 static_cast<const TypingCommandImpl*>(command)->openForMoreTyping();
2690 command->deleteKeyPressed();
2701 static_cast<TypingCommandImpl*>(lastEditCommand)->insertNewline();
2705 command->insertNewline();
2711 #ifdef DEBUG_COMMANDS 2712 kDebug() <<
"[insert text]" << text << endl;
2719 static_cast<TypingCommandImpl*>(lastEditCommand)->insertText(text);
2723 command->insertText(text);
2742 #ifdef DEBUG_COMMANDS 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());
2753 if (startBlock == endBlock) {
2754 if (startBlock->id() == ID_LI) {
2756 #ifdef DEBUG_COMMANDS 2757 kDebug() <<
"[remove list item]" << endl;
2759 NodeImpl *listBlock = startBlock->parent();
2762 if (listBlock->firstChild() == listBlock->lastChild() && listBlock->firstChild() == startBlock) {
2764 #ifdef DEBUG_COMMANDS 2765 kDebug() <<
"[remove list completely]" << endl;
2769 }
else if (!startBlock->previousSibling()) {
2771 NodeImpl *nextSibling;
2772 for (NodeImpl *node = startBlock->firstChild(); node; node = nextSibling) {
2773 nextSibling = node->nextSibling();
2778 }
else if (!startBlock->nextSibling()) {
2780 NodeImpl *nextSibling;
2781 for (NodeImpl *node = startBlock->lastChild(); node; node = nextSibling) {
2782 nextSibling = node->previousSibling();
2789 WTF::PassRefPtr<NodeImpl> newListBlock = listBlock->cloneNode(
false);
2791 NodeImpl *node, *nextSibling;
2792 for (node = startBlock->nextSibling(); node; node = nextSibling) {
2793 nextSibling = node->nextSibling();
2797 for (node = startBlock->firstChild(); node; node = nextSibling) {
2798 nextSibling = node->nextSibling();
2806 ElementImpl *li =
document()->createHTMLElement(
"LI");
2809 for (NodeImpl *node = startBlock->firstChild(); node; node = nextNode) {
2810 #ifdef DEBUG_COMMANDS 2811 kDebug() <<
"[reattach node]" << node << endl;
2813 nextNode = node->nextSibling();
2820 #ifdef DEBUG_COMMANDS 2821 kDebug() <<
"[different blocks are not supported yet]" << endl;
2829 insertCommand->apply();
2846 void IndentOutdentCommandImpl::indent()
2849 #ifdef DEBUG_COMMANDS 2850 kDebug() <<
"[indent selection]" << selection << endl;
2852 NodeImpl *startBlock = selection.start().node()->enclosingBlockFlowElement();
2853 NodeImpl *endBlock = selection.end().node()->enclosingBlockFlowElement();
2855 if (startBlock == endBlock) {
2857 if (startBlock->id() == ID_LI && (startBlock->previousSibling() || startBlock->nextSibling())) {
2858 #ifdef DEBUG_COMMANDS 2859 kDebug() <<
"[modify list]" << endl;
2861 RefPtr<NodeImpl> newList = startBlock->parent()->cloneNode(
false);
2866 NodeImpl *blockquoteElement =
document()->createHTMLElement(
"blockquote");
2867 if (startBlock->id() == ID_LI) {
2868 startBlock = startBlock->parent();
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;
2885 RefPtr<NodeImpl> nestedList = startBlock->parent()->cloneNode(
false);
2887 NodeImpl *nextNode = 0;
2888 for (NodeImpl *node = startBlock;; node = nextNode) {
2889 nextNode = node->nextSibling();
2892 if (node == endBlock)
2896 #ifdef DEBUG_COMMANDS 2897 kDebug() <<
"[blocks not from one list are not supported yet]" << endl;
2906 node = node->previousSibling();
2907 if (node && node->id() == ID_LI)
2916 node = node->nextSibling();
2917 if (node && node->id() == ID_LI)
2923 void IndentOutdentCommandImpl::outdent()
2926 #ifdef DEBUG_COMMANDS 2927 kDebug() <<
"[indent selection]" << selection << endl;
2929 NodeImpl *startBlock = selection.start().node()->enclosingBlockFlowElement();
2930 NodeImpl *endBlock = selection.end().node()->enclosingBlockFlowElement();
2932 if (startBlock->id() == ID_LI && endBlock->id() == ID_LI && startBlock->parent() == endBlock->parent()) {
2933 #ifdef DEBUG_COMMANDS 2934 kDebug() <<
"[list items selected]" << endl;
2938 bool listFullySelected = firstItemSelected && lastItemSelected;
2940 #ifdef DEBUG_COMMANDS 2941 kDebug() <<
"[first/last item selected]" << firstItemSelected << lastItemSelected << endl;
2944 NodeImpl *listNode = startBlock->parent();
2945 printEnclosingBlockTree(listNode);
2946 bool hasParentList = listNode->parent()->id() == ID_OL || listNode->parent()->id() == ID_UL;
2948 if (!firstItemSelected && !lastItemSelected) {
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();
2959 firstItemSelected =
true;
2962 NodeImpl *nextNode = 0;
2963 for (NodeImpl *node = firstItemSelected ? startBlock : endBlock;; node = nextNode) {
2964 nextNode = firstItemSelected ? node->nextSibling() : node->previousSibling();
2966 if (firstItemSelected)
2970 if (!hasParentList && node->id() == ID_LI) {
2974 if (node == (firstItemSelected ? endBlock : startBlock))
2977 if (listFullySelected)
2983 if (startBlock == endBlock) {
2984 if (startBlock->id() == ID_BLOCKQUOTE) {
2987 #ifdef DEBUG_COMMANDS 2988 kDebug() <<
"[not the list or blockquote]" << endl;
2992 #ifdef DEBUG_COMMANDS 2993 kDebug() <<
"[blocks not from one list are not supported yet]" << endl;
3000 if (m_commandType ==
Indent)
virtual ~DeleteSelectionCommandImpl()
static bool hasPreviousListItem(NodeImpl *node)
static void applyStyleChangeOnTheNode(ElementImpl *element, CSSStyleDeclarationImpl *style)
AppendNodeCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *parentNode, DOM::NodeImpl *appendChild)
virtual DOM::DocumentImpl * document() const
void removeNodeAttribute(DOM::ElementImpl *, int attribute)
void insertNodeBefore(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild)
void replaceText(DOM::TextImpl *node, long offset, long count, const DOM::DOMString &replacementText)
DeleteSelectionCommandImpl(DOM::DocumentImpl *document)
DOM::NodeImpl * pruneNode() const
void removeNodeAndPrune(DOM::NodeImpl *pruneNode, DOM::NodeImpl *stopNode=0)
virtual ~SetNodeAttributeCommandImpl()
The Node interface is the primary datatype for the entire Document Object Model.
This class resembles the editing API when the associated khtml document is editable (in design mode),...
void insertNodeAfter(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild)
ReplaceSelectionCommandImpl(DOM::DocumentImpl *document, DOM::DocumentFragmentImpl *fragment, bool selectReplacement=true)
RemoveNodeAndPruneCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *pruneNode, DOM::NodeImpl *stopNode=0)
static bool isNBSP(const QChar &c)
virtual ~RemoveCSSPropertyCommandImpl()
virtual void doUnapply()=0
virtual ~RemoveNodeAndPruneCommandImpl()
void insertText(const DOM::DOMString &text)
InputTextCommandImpl(DOM::DocumentImpl *document)
void setParent(EditCommandImpl *)
bool strcasecmp(const DOMString &a, const DOMString &b)
TypingCommandImpl(DOM::DocumentImpl *document)
QList< RefPtr< EditCommandImpl > > m_cmds
static void deleteKeyPressed0(DocumentImpl *document)
static Position leadingWhitespacePosition(const Position &pos)
DOM::NodeImpl * node() const
DeleteTextCommandImpl(DOM::DocumentImpl *document, DOM::TextImpl *node, long offset, long count)
virtual ~InsertListCommandImpl()
static QDebug kDebug(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
static bool textNodesAreJoinable(TextImpl *text1, TextImpl *text2)
ECommandState state() const
SetNodeAttributeCommandImpl(DOM::DocumentImpl *, DOM::ElementImpl *, DOM::NodeImpl::Id attribute, const DOM::DOMString &value)
void joinTextNodes(DOM::TextImpl *text1, DOM::TextImpl *text2)
DOM::NodeImpl * node() const
static bool shouldPruneNode(NodeImpl *node)
static void insertList(DocumentImpl *document, Type type)
void setState(ECommandState state)
virtual ~ApplyStyleCommandImpl()
virtual ~EditCommandImpl()
virtual bool isTypingCommand() const
virtual ~CompositeEditCommandImpl()
DOMStringImpl * implementation() const
InsertTextCommandImpl(DOM::DocumentImpl *document, DOM::TextImpl *, long, const DOM::DOMString &)
void splitTextNode(DOM::TextImpl *text, long offset)
static bool hasNextListItem(NodeImpl *node)
EditCommandImpl(DOM::DocumentImpl *)
void removeCSSProperty(DOM::CSSStyleDeclarationImpl *, int property)
DOM::ElementImpl * createTypingStyleElement() const
void deleteText(DOM::TextImpl *node, long offset, long count)
virtual ~RemoveNodeAttributeCommandImpl()
WTF::PassRefPtr< khtml::EditCommandImpl > lastEditCommand() const
Returns the most recent edit command applied.
The CSSPrimitiveValue interface represents a single CSS value .
DOM::DOMString text() const
static bool checkIfNewStylingNeeded(ElementImpl *element, CSSStyleDeclarationImpl *style)
void applyCommandToComposite(PassRefPtr< EditCommandImpl >)
static Position trailingWhitespacePosition(const Position &pos)
This class implements the basic string we use in the DOM.
ApplyStyleCommandImpl(DOM::DocumentImpl *, DOM::CSSStyleDeclarationImpl *style)
DeleteCollapsibleWhitespaceCommandImpl(DOM::DocumentImpl *document)
virtual ~JoinTextNodesCommandImpl()
EditCommandImpl * parent() const
void setNodeAttribute(DOM::ElementImpl *, int attribute, const DOM::DOMString &)
static void insertNewline0(DocumentImpl *document)
KAction * next(const QObject *recvr, const char *slot, QObject *parent)
virtual ~TypingCommandImpl()
virtual ~ReplaceSelectionCommandImpl()
DOM::Selection endingSelection() const
static DOMString & nonBreakingSpaceString()
static bool isOpenForMoreTypingCommand(const EditCommandImpl *command)
virtual ~MoveSelectionCommandImpl()
void setStartingSelection(const DOM::Selection &s)
void inputText(const DOM::DOMString &text)
SplitTextNodeCommandImpl(DOM::DocumentImpl *, DOM::TextImpl *, long)
void insertNodeAt(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild, long offset)
InsertListCommandImpl(DOM::DocumentImpl *document, Type type)
CompositeEditCommandImpl(DOM::DocumentImpl *)
static bool isBlockLevelStyle(const CSSStyleDeclarationImpl *style)
void appendNode(DOM::NodeImpl *parent, DOM::NodeImpl *appendChild)
static void insertText0(DocumentImpl *document, const DOMString &text)
virtual ~RemoveNodePreservingChildrenCommandImpl()
virtual ~InsertNodeBeforeCommandImpl()
virtual ~RemoveNodeCommandImpl()
static bool shouldDeleteUpstreamPosition(const Position &pos)
MoveSelectionCommandImpl(DOM::DocumentImpl *document, DOM::DocumentFragmentImpl *fragment, DOM::Position &position)
JoinTextNodesCommandImpl(DOM::DocumentImpl *, DOM::TextImpl *, DOM::TextImpl *)
virtual ~InsertTextCommandImpl()
void removeNodePreservingChildren(DOM::NodeImpl *node)
virtual ~DeleteTextCommandImpl()
DOM::CSSStyleDeclarationImpl * style() const
static DOMString & styleSpanClassString()
void removeNode(DOM::NodeImpl *removeChild)
RemoveCSSPropertyCommandImpl(DOM::DocumentImpl *, DOM::CSSStyleDeclarationImpl *, int property)
bool isCompositeStep() const
virtual ~AppendNodeCommandImpl()
virtual ~IndentOutdentCommandImpl()
virtual bool isInputTextCommand() const
RemoveNodeCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *)
void deleteCollapsibleWhitespace()
void input(const DOM::DOMString &text)
static bool isWS(const QChar &c)
void insertText(DOM::TextImpl *node, long offset, const DOM::DOMString &text)
RemoveNodeAttributeCommandImpl(DOM::DocumentImpl *, DOM::ElementImpl *, DOM::NodeImpl::Id attribute)
DOM::Selection startingSelection() const
InsertNodeBeforeCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild)
virtual ~InputTextCommandImpl()
IndentOutdentCommandImpl(DocumentImpl *document, Type type)
void setEndingSelection(const DOM::Selection &s)
virtual ~SplitTextNodeCommandImpl()
RemoveNodePreservingChildrenCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *)