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)
96 static inline bool isWS(
const DOMString &text)
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()]);
116 static bool shouldPruneNode(NodeImpl *node)
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())
153 static Position leadingWhitespacePosition(
const Position &pos)
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()]))
168 static Position trailingWhitespacePosition(
const Position &pos)
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()]))
192 static bool textNodesAreJoinable(TextImpl *text1, TextImpl *text2)
197 return (text1->nextSibling() == text2);
200 static DOMString &nonBreakingSpaceString()
203 return nonBreakingSpaceString;
208 static DOMString styleSpanClassString =
"khtml-style-span";
209 return styleSpanClassString;
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) {
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());
513 styleElement->setAttribute(ATTR_CLASS, styleSpanClassString());
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);
579 static bool isBlockLevelStyle(
const CSSStyleDeclarationImpl* style)
582 while (it.hasNext()) {
583 CSSProperty *
property = it.next();
584 switch (property->id()) {
585 case CSS_PROP_TEXT_ALIGN:
609 static void applyStyleChangeOnTheNode(ElementImpl* element, CSSStyleDeclarationImpl* style)
612 element->document()->defaultView()->getComputedStyle(element, 0));
613 assert(!computedStyle.isNull());
614 #ifdef DEBUG_COMMANDS
615 kDebug() <<
"[change style]" << element << endl;
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());
647 if (isBlockLevelStyle(m_style)) {
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) {
673 applyStyleChangeOnTheNode(startBlock, m_style);
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)
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();
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())) &&
771 elem->getAttribute(ATTR_CLASS) == styleSpanClassString())
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());
824 setEndingSelection(Selection(Position(start.node(), 0), Position(end.node(), end.offset() - endOffsetAdjustment)));
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()) {
866 static bool checkIfNewStylingNeeded(ElementImpl* element, CSSStyleDeclarationImpl *style)
869 element->document()->defaultView()->getComputedStyle(element, 0));
870 assert(!computedStyle.isNull());
871 #ifdef DEBUG_COMMANDS
872 kDebug() <<
"[check styling]" << element << endl;
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();
893 if (!checkIfNewStylingNeeded(parent,
style()))
895 ElementImpl *styleElement = 0;
896 if (parent->id() == ID_SPAN && parent->firstChild() == startNode && parent->lastChild() == endNode) {
899 styleElement =
document()->createHTMLElement(
"SPAN");
900 styleElement->setAttribute(ATTR_CLASS, styleSpanClassString());
902 surroundNodeRangeWithElement(startNode, endNode, styleElement);
904 applyStyleChangeOnTheNode(styleElement,
style());
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;
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)
1007 : CompositeEditCommandImpl(document), m_charactersDeleted(0), m_selectionToCollapse(selection), m_hasSelectionToCollapse(true)
1015 static bool shouldDeleteUpstreamPosition(
const Position &pos)
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());
1058 bool del = shouldDeleteUpstreamPosition(upstream);
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());
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;
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;
1144 if (!m_hasSelectionToCollapse)
1146 int state = m_selectionToCollapse.state();
1147 if (state == Selection::CARET) {
1148 Position endPosition = deleteWhitespace(m_selectionToCollapse.start());
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);
1168 #ifdef DEBUG_COMMANDS
1169 kDebug(6200) <<
"=====================================================";
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());
1212 if (textNodesAreJoinable(prevTextNode, textNode)) {
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());
1227 if (textNodesAreJoinable(textNode, nextTextNode)) {
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)) {
1426 Position trailing = trailingWhitespacePosition(downstreamEnd.equivalentDownstreamPosition());
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());
1433 replaceText(textNode, trailing.offset(), 1, nonBreakingSpaceString());
1435 }
else if (!startAtStartOfBlock && endAtEndOfBlock) {
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());
1443 }
else if (!startAtStartOfBlock && !endAtEndOfBlock) {
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());
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());
1663 NodeImpl *parent = node->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])) {
1991 insertText(textNode, offset, nonBreakingSpaceString());
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);
2048 m_text = text.
copy();
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) {
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)
2675 static bool isOpenForMoreTypingCommand(
const EditCommandImpl *command)
2677 return command && command->isTypingCommand() &&
2678 static_cast<const TypingCommandImpl*
>(command)->openForMoreTyping();
2690 command->deleteKeyPressed();
2697 Editor *ed = document->part()->editor();
2700 if (isOpenForMoreTypingCommand(lastEditCommand)) {
2705 command->insertNewline();
2711 #ifdef DEBUG_COMMANDS
2712 kDebug() <<
"[insert text]" << text << endl;
2715 Editor *ed = document->part()->editor();
2718 if (isOpenForMoreTypingCommand(lastEditCommand)) {
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();
2869 NodeImpl *parent = startBlock->parent();
2874 NodeImpl *parent = 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;
2903 static bool hasPreviousListItem(NodeImpl *node)
2906 node = node->previousSibling();
2907 if (node && node->id() == ID_LI)
2913 static bool hasNextListItem(NodeImpl *node)
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;
2936 bool firstItemSelected = !hasPreviousListItem(startBlock);
2937 bool lastItemSelected = !hasNextListItem(endBlock);
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()
AppendNodeCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *parentNode, DOM::NodeImpl *appendChild)
void removeNodeAttribute(DOM::ElementImpl *, int attribute)
DOM::NodeImpl * node() const
DOM::NodeImpl * node() const
void insertNodeBefore(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild)
DOM::NodeImpl * pruneNode() const
virtual DOM::DocumentImpl * document() const
void replaceText(DOM::TextImpl *node, long offset, long count, const DOM::DOMString &replacementText)
DeleteSelectionCommandImpl(DOM::DocumentImpl *document)
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)
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)
DOM::Selection startingSelection() const
DOM::Selection endingSelection() 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)
SetNodeAttributeCommandImpl(DOM::DocumentImpl *, DOM::ElementImpl *, DOM::NodeImpl::Id attribute, const DOM::DOMString &value)
DOM::CSSStyleDeclarationImpl * style() const
void joinTextNodes(DOM::TextImpl *text1, DOM::TextImpl *text2)
virtual ~DeleteCollapsibleWhitespaceCommandImpl()
static void insertList(DocumentImpl *document, Type type)
void setState(ECommandState state)
virtual ~ApplyStyleCommandImpl()
virtual ~EditCommandImpl()
virtual ~CompositeEditCommandImpl()
InsertTextCommandImpl(DOM::DocumentImpl *document, DOM::TextImpl *, long, const DOM::DOMString &)
void splitTextNode(DOM::TextImpl *text, long offset)
EditCommandImpl(DOM::DocumentImpl *)
void removeCSSProperty(DOM::CSSStyleDeclarationImpl *, int property)
ECommandState state() const
void deleteText(DOM::TextImpl *node, long offset, long count)
virtual ~RemoveNodeAttributeCommandImpl()
The CSSPrimitiveValue interface represents a single CSS value .
void applyCommandToComposite(PassRefPtr< EditCommandImpl >)
This class implements the basic string we use in the DOM.
ApplyStyleCommandImpl(DOM::DocumentImpl *, DOM::CSSStyleDeclarationImpl *style)
EditCommandImpl * parent() const
DeleteCollapsibleWhitespaceCommandImpl(DOM::DocumentImpl *document)
QFuture< void > map(Sequence &sequence, MapFunction function)
virtual ~JoinTextNodesCommandImpl()
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()
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 *)
void appendNode(DOM::NodeImpl *parent, DOM::NodeImpl *appendChild)
static void insertText0(DocumentImpl *document, const DOMString &text)
virtual ~RemoveNodePreservingChildrenCommandImpl()
virtual ~InsertNodeBeforeCommandImpl()
virtual ~RemoveNodeCommandImpl()
MoveSelectionCommandImpl(DOM::DocumentImpl *document, DOM::DocumentFragmentImpl *fragment, DOM::Position &position)
bool isCompositeStep() const
JoinTextNodesCommandImpl(DOM::DocumentImpl *, DOM::TextImpl *, DOM::TextImpl *)
virtual ~InsertTextCommandImpl()
void removeNodePreservingChildren(DOM::NodeImpl *node)
virtual ~DeleteTextCommandImpl()
DOM::ElementImpl * createTypingStyleElement() const
void removeNode(DOM::NodeImpl *removeChild)
DOMStringImpl * implementation() const
RemoveCSSPropertyCommandImpl(DOM::DocumentImpl *, DOM::CSSStyleDeclarationImpl *, int property)
virtual bool isInputTextCommand() const
WTF::PassRefPtr< khtml::EditCommandImpl > lastEditCommand() const
Returns the most recent edit command applied.
virtual ~AppendNodeCommandImpl()
virtual ~IndentOutdentCommandImpl()
RemoveNodeCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *)
void deleteCollapsibleWhitespace()
void input(const DOM::DOMString &text)
void insertText(DOM::TextImpl *node, long offset, const DOM::DOMString &text)
RemoveNodeAttributeCommandImpl(DOM::DocumentImpl *, DOM::ElementImpl *, DOM::NodeImpl::Id attribute)
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 *)