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);
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) {
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());
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)
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:
609 static void applyStyleChangeOnTheNode(ElementImpl* element, CSSStyleDeclarationImpl* style)
611 CSSStyleDeclarationImpl *computedStyle = element->document()->defaultView()->getComputedStyle(element, 0);
613 #ifdef DEBUG_COMMANDS
614 kDebug() <<
"[change style]" << element << endl;
617 QListIterator<CSSProperty*> it(*(style->values()));
618 while ( it.hasNext() ) {
619 CSSProperty *
property = it.next();
620 CSSValueImpl *computedValue = computedStyle->getPropertyCSSValue(property->id());
621 DOMString newValue =
property->value()->cssText();
622 #ifdef DEBUG_COMMANDS
623 kDebug() <<
"[new value]:" <<
property->cssText() << endl;
624 kDebug() <<
"[computedValue]:" << computedValue->cssText() << endl;
626 if (
strcasecmp(computedValue->cssText(), newValue)) {
628 element->getInlineStyleDecls()->setProperty(property->id(), newValue);
639 Position start(
endingSelection().start().equivalentDownstreamPosition().equivalentRangeCompliantPosition());
641 #ifdef DEBUG_COMMANDS
642 kDebug() <<
"[APPLY STYLE]" << start << end << endl;
643 printEnclosingBlockTree(start.node()->enclosingBlockFlowElement());
646 if (isBlockLevelStyle(m_style)) {
647 #ifdef DEBUG_COMMANDS
648 kDebug() <<
"[APPLY BLOCK LEVEL STYLE]" << endl;
650 ElementImpl *startBlock = start.node()->enclosingBlockFlowElement();
651 ElementImpl *endBlock = end.node()->enclosingBlockFlowElement();
652 #ifdef DEBUG_COMMANDS
653 kDebug() << startBlock << startBlock->nodeName() << endl;
655 if (startBlock == endBlock && startBlock == start.node()->rootEditableElement()) {
656 ElementImpl* block =
document()->createHTMLElement(
"DIV");
657 #ifdef DEBUG_COMMANDS
658 kDebug() <<
"[Create DIV with Style:]" << m_style->cssText() << endl;
660 block->setAttribute(ATTR_STYLE, m_style->cssText());
661 for (NodeImpl* node = startBlock->firstChild(); node; node = startBlock->firstChild()) {
662 #ifdef DEBUG_COMMANDS
663 kDebug() <<
"[reparent node]" << node << node->nodeName() << endl;
669 }
else if (startBlock == endBlock) {
672 applyStyleChangeOnTheNode(startBlock, m_style);
679 removeStyle(start, end);
680 bool splitStart = splitTextAtStartIfNeeded(start, end);
685 splitTextAtEndIfNeeded(start, end);
689 #ifdef DEBUG_COMMANDS
690 kDebug() <<
"[start;end]" << start << end << endl;
692 if (start.node() == end.node()) {
694 applyStyleIfNeeded(start.node(), end.node());
696 NodeImpl *node = start.node();
698 if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
699 NodeImpl *runStart = node;
701 if (runStart->parentNode() != node->parentNode() || node->isHTMLElement() || node == end.node() ||
702 (node->renderer() && !node->renderer()->isInline())) {
703 applyStyleIfNeeded(runStart, node);
706 node = node->traverseNextNode();
709 if (node == end.node())
711 node = node->traverseNextNode();
719 bool ApplyStyleCommandImpl::isHTMLStyleNode(HTMLElementImpl *elem)
721 QListIterator<CSSProperty*> it(*(
style()->values()));
722 while (it.hasNext()) {
723 CSSProperty *
property = it.next();
724 switch (property->id()) {
725 case CSS_PROP_FONT_WEIGHT:
726 if (elem->id() == ID_B)
729 case CSS_PROP_FONT_STYLE:
730 if (elem->id() == ID_I)
739 void ApplyStyleCommandImpl::removeHTMLStyleNode(HTMLElementImpl *elem)
749 void ApplyStyleCommandImpl::removeCSSStyle(HTMLElementImpl *elem)
753 CSSStyleDeclarationImpl *decl = elem->inlineStyleDecls();
757 QListIterator<CSSProperty*> it(*(
style()->values()));
758 while ( it.hasNext() ) {
759 CSSProperty *
property = it.next();
760 if (decl->getPropertyCSSValue(property->id()))
764 if (elem->id() == ID_SPAN) {
768 NamedAttrMapImpl *map = elem->attributes();
769 if (map && (map->length() == 1 || (map->length() == 2 && elem->getAttribute(ATTR_STYLE).isEmpty())) &&
770 elem->getAttribute(ATTR_CLASS) == styleSpanClassString())
775 void ApplyStyleCommandImpl::removeStyle(
const Position &start,
const Position &end)
777 NodeImpl *node = start.node();
779 NodeImpl *
next = node->traverseNextNode();
780 if (node->isHTMLElement() && nodeFullySelected(node)) {
781 HTMLElementImpl *elem =
static_cast<HTMLElementImpl *
>(node);
782 if (isHTMLStyleNode(elem))
783 removeHTMLStyleNode(elem);
785 removeCSSStyle(elem);
787 if (node == end.node())
793 bool ApplyStyleCommandImpl::nodeFullySelected(
const NodeImpl *node)
const
799 if (node == end.node())
800 return end.offset() >= node->caretMaxOffset();
802 for (NodeImpl *child = node->lastChild(); child; child = child->lastChild()) {
803 if (child == end.node())
804 return end.offset() >= child->caretMaxOffset();
807 return node == end.node() || !node->isAncestor(end.node());
813 bool ApplyStyleCommandImpl::splitTextAtStartIfNeeded(
const Position &start,
const Position &end)
815 if (start.node()->isTextNode() && start.offset() > start.node()->caretMinOffset() && start.offset() < start.node()->caretMaxOffset()) {
816 #ifdef DEBUG_COMMANDS
817 kDebug() <<
"[split start]" << start.offset() << start.node()->caretMinOffset() << start.node()->caretMaxOffset() << endl;
819 long endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0;
820 TextImpl *text =
static_cast<TextImpl *
>(start.node());
821 RefPtr<SplitTextNodeCommandImpl> cmd =
new SplitTextNodeCommandImpl(
document(), text, start.offset());
823 setEndingSelection(Selection(Position(start.node(), 0), Position(end.node(), end.offset() - endOffsetAdjustment)));
829 NodeImpl *ApplyStyleCommandImpl::splitTextAtEndIfNeeded(
const Position &start,
const Position &end)
831 if (end.node()->isTextNode() && end.offset() > end.node()->caretMinOffset() && end.offset() < end.node()->caretMaxOffset()) {
832 #ifdef DEBUG_COMMANDS
833 kDebug() <<
"[split end]" << end.offset() << end.node()->caretMinOffset() << end.node()->caretMaxOffset() << endl;
835 TextImpl *text =
static_cast<TextImpl *
>(end.node());
836 RefPtr<SplitTextNodeCommandImpl> cmd =
new SplitTextNodeCommandImpl(
document(), text, end.offset());
838 NodeImpl *startNode = start.node() == end.node() ? cmd->node()->previousSibling() : start.node();
840 setEndingSelection(Selection(Position(startNode, start.offset()), Position(cmd->node()->previousSibling(), cmd->node()->previousSibling()->caretMaxOffset())));
841 return cmd->node()->previousSibling();
846 void ApplyStyleCommandImpl::surroundNodeRangeWithElement(NodeImpl *startNode, NodeImpl *endNode, ElementImpl *element)
852 NodeImpl *node = startNode;
854 NodeImpl *next = node->traverseNextNode();
855 if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
865 static bool checkIfNewStylingNeeded(ElementImpl* element, CSSStyleDeclarationImpl *style)
867 CSSStyleDeclarationImpl *computedStyle = element->document()->defaultView()->getComputedStyle(element, 0);
869 #ifdef DEBUG_COMMANDS
870 kDebug() <<
"[check styling]" << element << endl;
873 QListIterator<CSSProperty*> it(*(style->values()));
874 while ( it.hasNext() ) {
875 CSSProperty *
property = it.next();
876 CSSValueImpl *computedValue = computedStyle->getPropertyCSSValue(property->id());
877 DOMString newValue =
property->value()->cssText();
878 #ifdef DEBUG_COMMANDS
879 kDebug() <<
"[new value]:" <<
property->cssText() << endl;
880 kDebug() <<
"[computedValue]:" << computedValue->cssText() << endl;
882 if (
strcasecmp(computedValue->cssText(), newValue))
888 void ApplyStyleCommandImpl::applyStyleIfNeeded(DOM::NodeImpl *startNode, DOM::NodeImpl *endNode)
890 ElementImpl *
parent = Position(startNode, 0).element();
891 if (!checkIfNewStylingNeeded(parent,
style()))
893 ElementImpl *styleElement = 0;
894 if (parent->id() == ID_SPAN && parent->firstChild() == startNode && parent->lastChild() == endNode) {
897 styleElement =
document()->createHTMLElement(
"SPAN");
898 styleElement->setAttribute(ATTR_CLASS, styleSpanClassString());
900 surroundNodeRangeWithElement(startNode, endNode, styleElement);
902 applyStyleChangeOnTheNode(styleElement,
style());
905 bool ApplyStyleCommandImpl::currentlyHasStyle(
const Position &pos,
const CSSProperty *property)
const
909 CSSStyleDeclarationImpl *decl =
document()->defaultView()->getComputedStyle(pos.element(), 0);
911 CSSValueImpl *value = decl->getPropertyCSSValue(property->id());
912 return strcasecmp(value->cssText(),
property->value()->cssText()) == 0;
915 ApplyStyleCommandImpl::StyleChange ApplyStyleCommandImpl::computeStyleChange(
const Position &insertionPoint, CSSStyleDeclarationImpl *style)
917 assert(insertionPoint.notEmpty());
920 StyleChange styleChange;
922 QListIterator<CSSProperty*> it(*(style->values()));
923 while ( it.hasNext() ) {
924 CSSProperty *
property = it.next();
925 #ifdef DEBUG_COMMANDS
926 kDebug() <<
"[CSS property]:" <<
property->cssText() << endl;
928 if (!currentlyHasStyle(insertionPoint, property)) {
929 #ifdef DEBUG_COMMANDS
930 kDebug() <<
"[Add to style change]" << endl;
932 switch (property->id()) {
933 case CSS_PROP_FONT_WEIGHT:
934 if (
strcasecmp(property->value()->cssText(),
"bold") == 0)
935 styleChange.applyBold =
true;
937 styleChange.cssStyle +=
property->cssText();
939 case CSS_PROP_FONT_STYLE: {
940 DOMString cssText(property->value()->cssText());
942 styleChange.applyItalic =
true;
944 styleChange.cssStyle += property->cssText();
948 styleChange.cssStyle +=
property->cssText();
956 Position ApplyStyleCommandImpl::positionInsertionPoint(Position pos)
958 if (pos.node()->isTextNode() && (pos.offset() > 0 && pos.offset() < pos.node()->maxOffset())) {
959 RefPtr<SplitTextNodeCommandImpl> split =
new SplitTextNodeCommandImpl(
document(), static_cast<TextImpl *>(pos.node()), pos.offset());
961 pos = Position(split->node(), 0);
969 if (currentlyHasStyle(pos))
973 if (pos.offset() >= pos.node()->caretMaxOffset()) {
974 NodeImpl *nextNode = pos.node()->traverseNextNode();
976 Position next = Position(nextNode, 0);
977 if (currentlyHasStyle(next))
983 if (pos.offset() <= pos.node()->caretMinOffset()) {
984 NodeImpl *prevNode = pos.node()->traversePreviousNode();
986 Position prev = Position(prevNode, prevNode->maxOffset());
987 if (currentlyHasStyle(prev))
1000 : CompositeEditCommandImpl(document), m_charactersDeleted(0), m_hasSelectionToCollapse(false)
1005 : CompositeEditCommandImpl(document), m_charactersDeleted(0), m_selectionToCollapse(selection), m_hasSelectionToCollapse(true)
1013 static bool shouldDeleteUpstreamPosition(
const Position &pos)
1015 if (!pos.node()->isTextNode())
1018 RenderObject *renderer = pos.node()->renderer();
1022 TextImpl *textNode =
static_cast<TextImpl *
>(pos.node());
1023 if (pos.offset() >= (long)textNode->length())
1026 if (pos.isLastRenderedPositionInEditableBlock())
1029 if (pos.isFirstRenderedPositionOnLine() || pos.isLastRenderedPositionOnLine())
1046 Position DeleteCollapsibleWhitespaceCommandImpl::deleteWhitespace(
const Position &pos)
1048 Position upstream = pos.equivalentUpstreamPosition();
1049 Position downstream = pos.equivalentDownstreamPosition();
1050 #ifdef DEBUG_COMMANDS
1051 kDebug() <<
"[pos]" << pos << endl;
1052 kDebug() <<
"[upstream:downstream]" << upstream << downstream << endl;
1053 printEnclosingBlockTree(pos.node());
1056 bool del = shouldDeleteUpstreamPosition(upstream);
1057 #ifdef DEBUG_COMMANDS
1058 kDebug() <<
"[delete upstream]" << del << endl;
1061 if (upstream == downstream)
1064 #ifdef DEBUG_COMMANDS
1065 PositionIterator iter(upstream);
1066 kDebug() <<
"[before print]" << endl;
1067 for (iter.next(); iter.current() != downstream; iter.next())
1068 kDebug() <<
"[iterate]" << iter.current() << endl;
1069 kDebug() <<
"[after print]" << endl;
1072 PositionIterator it(upstream);
1073 Position deleteStart = upstream;
1075 deleteStart = it.peekNext();
1076 if (deleteStart == downstream)
1080 Position endingPosition = upstream;
1082 while (it.current() != downstream) {
1083 Position next = it.peekNext();
1084 #ifdef DEBUG_COMMANDS
1085 kDebug() <<
"[iterate and delete]" << next << endl;
1087 if (next.node() != deleteStart.node()) {
1089 if (deleteStart.node()->isTextNode()) {
1090 TextImpl *textNode =
static_cast<TextImpl *
>(deleteStart.node());
1091 unsigned long count = it.current().offset() - deleteStart.offset();
1092 if (count == textNode->length()) {
1093 #ifdef DEBUG_COMMANDS
1094 kDebug(6200) <<
" removeNodeAndPrune 1:" << textNode;
1096 if (textNode == endingPosition.node())
1097 endingPosition = Position(next.node(), next.node()->caretMinOffset());
1100 #ifdef DEBUG_COMMANDS
1101 kDebug(6200) <<
" deleteText 1:" << textNode <<
"t len:" << textNode->length()<<
"start:" << deleteStart.offset() <<
"del len:" << (it.current().offset() - deleteStart.offset());
1103 deleteText(textNode, deleteStart.offset(), count);
1106 #ifdef DEBUG_COMMANDS
1107 kDebug() <<
"[not text node is not supported yet]" << endl;
1111 }
else if (next == downstream) {
1112 assert(deleteStart.node() == downstream.node());
1113 assert(downstream.node()->isTextNode());
1114 TextImpl *textNode =
static_cast<TextImpl *
>(deleteStart.node());
1115 unsigned long count = downstream.offset() - deleteStart.offset();
1116 assert(count <= textNode->length());
1117 if (count == textNode->length()) {
1118 #ifdef DEBUG_COMMANDS
1119 kDebug(6200) <<
" removeNodeAndPrune 2:"<<textNode;
1123 #ifdef DEBUG_COMMANDS
1124 kDebug(6200) <<
" deleteText 2:"<< textNode<<
"t len:" << textNode->length() <<
"start:" <<deleteStart.offset() <<
"del len:" << count;
1126 deleteText(textNode, deleteStart.offset(), count);
1127 m_charactersDeleted = count;
1128 endingPosition = Position(downstream.node(), downstream.offset() - m_charactersDeleted);
1132 it.setPosition(next);
1135 return endingPosition;
1142 if (!m_hasSelectionToCollapse)
1144 int state = m_selectionToCollapse.state();
1145 if (state == Selection::CARET) {
1146 Position endPosition = deleteWhitespace(m_selectionToCollapse.start());
1148 #ifdef DEBUG_COMMANDS
1149 kDebug(6200) <<
"-----------------------------------------------------";
1152 else if (state == Selection::RANGE) {
1153 Position startPosition = deleteWhitespace(m_selectionToCollapse.start());
1154 #ifdef DEBUG_COMMANDS
1155 kDebug(6200) <<
"-----------------------------------------------------";
1157 Position endPosition = m_selectionToCollapse.end();
1158 if (m_charactersDeleted > 0 && startPosition.node() == endPosition.node()) {
1159 #ifdef DEBUG_COMMANDS
1160 kDebug(6200) <<
"adjust end position by" << m_charactersDeleted;
1162 endPosition = Position(endPosition.node(), endPosition.offset() - m_charactersDeleted);
1164 endPosition = deleteWhitespace(endPosition);
1166 #ifdef DEBUG_COMMANDS
1167 kDebug(6200) <<
"=====================================================";
1181 : CompositeEditCommandImpl(document), m_selectionToDelete(selection), m_hasSelectionToDelete(true)
1189 void DeleteSelectionCommandImpl::joinTextNodesWithSameStyle()
1193 if (selection.state() != Selection::CARET)
1196 Position pos(selection.start());
1198 if (!pos.node()->isTextNode())
1201 TextImpl *textNode =
static_cast<TextImpl *
>(pos.node());
1203 if (pos.offset() == 0) {
1204 PositionIterator it(pos);
1205 Position prev = it.previous();
1208 if (prev.node()->isTextNode()) {
1209 TextImpl *prevTextNode =
static_cast<TextImpl *
>(prev.node());
1210 if (textNodesAreJoinable(prevTextNode, textNode)) {
1213 #ifdef DEBUG_COMMANDS
1214 kDebug(6200) <<
"joinTextNodesWithSameStyle [1]";
1218 }
else if (pos.offset() == (long)textNode->length()) {
1219 PositionIterator it(pos);
1220 Position next = it.next();
1223 if (next.node()->isTextNode()) {
1224 TextImpl *nextTextNode =
static_cast<TextImpl *
>(next.node());
1225 if (textNodesAreJoinable(textNode, nextTextNode)) {
1228 #ifdef DEBUG_COMMANDS
1229 kDebug(6200) <<
"joinTextNodesWithSameStyle [2]";
1236 bool DeleteSelectionCommandImpl::containsOnlyWhitespace(
const Position &start,
const Position &end)
1240 PositionIterator it(start);
1241 while (!it.atEnd()) {
1242 if (!it.current().node()->isTextNode())
1244 const DOMString &text =
static_cast<TextImpl *
>(it.current().node())->data();
1246 if (text.
length() > INT_MAX)
1248 if (it.current().offset() < (int)text.
length() && !isWS(text[it.current().offset()]))
1251 if (it.current() == end)
1257 void DeleteSelectionCommandImpl::deleteContentInsideNode(NodeImpl *node,
int startOffset,
int endOffset)
1259 #ifdef DEBUG_COMMANDS
1260 kDebug() <<
"[Delete content inside node]" << node << startOffset << endOffset << endl;
1262 if (node->isTextNode()) {
1264 if (startOffset == endOffset)
1267 if (!startOffset && endOffset == node->maxOffset()) {
1272 deleteText(static_cast<TextImpl*>(node), startOffset, endOffset - startOffset);
1275 #ifdef DEBUG_COMMANDS
1276 kDebug() <<
"[non-text node] not supported" << endl;
1280 void DeleteSelectionCommandImpl::deleteContentBeforeOffset(NodeImpl *node,
int offset)
1282 deleteContentInsideNode(node, 0, offset);
1285 void DeleteSelectionCommandImpl::deleteContentAfterOffset(NodeImpl *node,
int offset)
1287 if (node->isTextNode())
1288 deleteContentInsideNode(node, offset, node->maxOffset());
1295 if (!m_hasSelectionToDelete)
1298 if (m_selectionToDelete.state() != Selection::RANGE)
1304 Position upstreamStart(selection.start().equivalentUpstreamPosition());
1305 Position downstreamStart(selection.start().equivalentDownstreamPosition());
1306 Position upstreamEnd(selection.end().equivalentUpstreamPosition());
1307 Position downstreamEnd(selection.end().equivalentDownstreamPosition());
1309 NodeImpl *startBlock = upstreamStart.node()->enclosingBlockFlowElement();
1310 NodeImpl *endBlock = downstreamEnd.node()->enclosingBlockFlowElement();
1312 #ifdef DEBUG_COMMANDS
1313 kDebug() <<
"[Delete:Start]" << upstreamStart << downstreamStart << endl;
1314 kDebug() <<
"[Delete:End]" << upstreamEnd << downstreamEnd << endl;
1315 printEnclosingBlockTree(upstreamStart.node());
1317 if (startBlock != endBlock)
1318 printEnclosingBlockTree(downstreamEnd.node());
1320 if (upstreamStart == downstreamEnd)
1325 if (upstreamStart.node() != downstreamEnd.node()) {
1326 NodeImpl *node, *next;
1327 for (node = upstreamStart.node()->traverseNextNode(); node && node != downstreamEnd.node(); node = next) {
1328 #ifdef DEBUG_COMMANDS
1329 kDebug() <<
"[traverse and delete]" << node << (node->renderer() && node->renderer()->isEditable()) << endl;
1331 next = node->traverseNextNode();
1332 if (node->renderer() && node->renderer()->isEditable())
1338 if (startBlock != endBlock && startBlock->parentNode() == endBlock->parentNode()) {
1339 NodeImpl *node = endBlock->firstChild();
1341 NodeImpl *moveNode = node;
1342 node = node->nextSibling();
1348 if (upstreamStart.node() == downstreamEnd.node())
1349 deleteContentInsideNode(upstreamEnd.node(), upstreamStart.offset(), downstreamEnd.offset());
1351 deleteContentAfterOffset(upstreamStart.node(), upstreamStart.offset());
1352 deleteContentBeforeOffset(downstreamEnd.node(), downstreamEnd.offset());
1357 Position endingPosition;
1358 bool adjustEndingPositionDownstream =
false;
1360 bool onlyWhitespace = containsOnlyWhitespace(upstreamStart, downstreamEnd);
1361 kDebug() <<
"[OnlyWhitespace]" << onlyWhitespace << endl;
1363 bool startCompletelySelected = !onlyWhitespace &&
1364 (downstreamStart.offset() <= downstreamStart.node()->caretMinOffset() &&
1365 ((downstreamStart.node() != upstreamEnd.node()) ||
1366 (upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset())));
1368 bool endCompletelySelected = !onlyWhitespace &&
1369 (upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset() &&
1370 ((downstreamStart.node() != upstreamEnd.node()) ||
1371 (downstreamStart.offset() <= downstreamStart.node()->caretMinOffset())));
1373 kDebug() <<
"[{start:end}CompletelySelected]" << startCompletelySelected << endCompletelySelected << endl;
1375 unsigned long startRenderedOffset = downstreamStart.renderedOffset();
1377 bool startAtStartOfRootEditableElement = startRenderedOffset == 0 && downstreamStart.inFirstEditableInRootEditableElement();
1378 bool startAtStartOfBlock = startAtStartOfRootEditableElement ||
1379 (startRenderedOffset == 0 && downstreamStart.inFirstEditableInContainingEditableBlock());
1380 bool endAtEndOfBlock = downstreamEnd.isLastRenderedPositionInEditableBlock();
1382 kDebug() <<
"[startAtStartOfRootEditableElement]" << startAtStartOfRootEditableElement << endl;
1383 kDebug() <<
"[startAtStartOfBlock]" << startAtStartOfBlock << endl;
1384 kDebug() <<
"[endAtEndOfBlock]" << endAtEndOfBlock << endl;
1386 NodeImpl *startBlock = upstreamStart.node()->enclosingBlockFlowElement();
1387 NodeImpl *endBlock = downstreamEnd.node()->enclosingBlockFlowElement();
1388 bool startBlockEndBlockAreSiblings = startBlock->parentNode() == endBlock->parentNode();
1390 kDebug() <<
"[startBlockEndBlockAreSiblings]" << startBlockEndBlockAreSiblings << startBlock << endBlock << endl;
1392 debugPosition(
"upstreamStart: ", upstreamStart);
1393 debugPosition(
"downstreamStart: ", downstreamStart);
1394 debugPosition(
"upstreamEnd: ", upstreamEnd);
1395 debugPosition(
"downstreamEnd: ", downstreamEnd);
1396 kDebug(6200) <<
"start selected:" << (startCompletelySelected ?
"YES" :
"NO");
1397 kDebug(6200) <<
"at start block:" << (startAtStartOfBlock ?
"YES" :
"NO");
1398 kDebug(6200) <<
"at start root block:"<< (startAtStartOfRootEditableElement ?
"YES" :
"NO");
1399 kDebug(6200) <<
"at end block:"<< (endAtEndOfBlock ?
"YES" :
"NO");
1400 kDebug(6200) <<
"only whitespace:"<< (onlyWhitespace ?
"YES" :
"NO");
1403 if (startAtStartOfBlock) {
1404 kDebug(6200) <<
"ending position case 1";
1405 endingPosition = Position(startBlock, 0);
1406 adjustEndingPositionDownstream =
true;
1407 }
else if (!startCompletelySelected) {
1408 kDebug(6200) <<
"ending position case 2";
1409 endingPosition = upstreamEnd;
1410 if (upstreamStart.node()->id() == ID_BR && upstreamStart.offset() == 1)
1411 adjustEndingPositionDownstream =
true;
1412 }
else if (upstreamStart != downstreamStart) {
1413 kDebug(6200) <<
"ending position case 3";
1414 endingPosition = upstreamStart;
1415 if (upstreamStart.node()->id() == ID_BR && upstreamStart.offset() == 1)
1416 adjustEndingPositionDownstream =
true;
1422 if ((startAtStartOfBlock && !endAtEndOfBlock) || (!startCompletelySelected && adjustEndingPositionDownstream)) {
1424 Position trailing = trailingWhitespacePosition(downstreamEnd.equivalentDownstreamPosition());
1425 if (trailing.notEmpty()) {
1426 debugPosition(
"convertTrailingWhitespace: ", trailing);
1427 Position collapse = trailing.nextCharacterPosition();
1428 if (collapse != trailing)
1430 TextImpl *textNode =
static_cast<TextImpl *
>(trailing.node());
1431 replaceText(textNode, trailing.offset(), 1, nonBreakingSpaceString());
1433 }
else if (!startAtStartOfBlock && endAtEndOfBlock) {
1435 Position leading = leadingWhitespacePosition(upstreamStart.equivalentUpstreamPosition());
1436 if (leading.notEmpty()) {
1437 debugPosition(
"convertLeadingWhitespace: ", leading);
1438 TextImpl *textNode =
static_cast<TextImpl *
>(leading.node());
1439 replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
1441 }
else if (!startAtStartOfBlock && !endAtEndOfBlock) {
1443 Position leading = leadingWhitespacePosition(upstreamStart.equivalentUpstreamPosition());
1444 Position trailing = trailingWhitespacePosition(downstreamEnd.equivalentDownstreamPosition());
1445 if (leading.notEmpty() && trailing.notEmpty()) {
1446 debugPosition(
"convertLeadingWhitespace [contiguous]: ", leading);
1447 TextImpl *textNode =
static_cast<TextImpl *
>(leading.node());
1448 replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
1455 NodeImpl *n = downstreamStart.node()->traverseNextNode();
1456 kDebug() <<
"[n]" << n << endl;
1459 if (startCompletelySelected) {
1460 kDebug(6200) <<
"start node delete case 1";
1462 }
else if (onlyWhitespace) {
1466 kDebug(6200) <<
"start node delete case 2";
1467 assert(upstreamStart.node()->isTextNode());
1468 TextImpl *text =
static_cast<TextImpl *
>(upstreamStart.node());
1469 int offset = upstreamStart.offset();
1471 int length = text->
length();
1472 if (length == upstreamStart.offset())
1475 }
else if (downstreamStart.node()->isTextNode()) {
1476 kDebug(6200) <<
"start node delete case 3";
1477 TextImpl *text =
static_cast<TextImpl *
>(downstreamStart.node());
1478 int endOffset = text == upstreamEnd.node() ? upstreamEnd.offset() : text->
length();
1479 if (endOffset > downstreamStart.offset()) {
1480 deleteText(text, downstreamStart.offset(), endOffset - downstreamStart.offset());
1485 kDebug(6200) <<
"start node delete case 4";
1486 assert(downstreamStart.offset() == 1);
1489 if (n && !onlyWhitespace && downstreamStart.node() != upstreamEnd.node()) {
1491 while (n && n != upstreamEnd.node()) {
1493 n = n->traverseNextNode();
1494 if (d->renderer() && d->renderer()->isEditable())
1501 assert(n == upstreamEnd.node());
1502 if (endCompletelySelected) {
1505 else if (upstreamEnd.node()->isTextNode()) {
1506 if (upstreamEnd.offset() > 0) {
1507 TextImpl *text =
static_cast<TextImpl *
>(upstreamEnd.node());
1514 assert(downstreamStart.offset() == 0);
1523 if (startBlock != endBlock && startBlockEndBlockAreSiblings) {
1524 kDebug(6200) <<
"merging content to start block";
1525 NodeImpl *node = endBlock->firstChild();
1527 NodeImpl *moveNode = node;
1528 node = node->nextSibling();
1534 if (adjustEndingPositionDownstream) {
1535 kDebug(6200) <<
"adjust ending position downstream";
1536 endingPosition = endingPosition.equivalentDownstreamPosition();
1539 debugPosition(
"ending position: ", endingPosition);
1542 kDebug(6200) <<
"-----------------------------------------------------";
1550 :
EditCommandImpl(document), m_node(node), m_offset(offset), m_count(count)
1569 int exceptionCode = 0;
1570 m_text = m_node->substringData(m_offset, m_count, exceptionCode);
1571 assert(exceptionCode == 0);
1573 m_node->deleteData(m_offset, m_count, exceptionCode);
1574 assert(exceptionCode == 0);
1582 int exceptionCode = 0;
1583 m_node->insertData(m_offset, m_text, exceptionCode);
1584 assert(exceptionCode == 0);
1599 void InputNewlineCommandImpl::insertNodeAfterPosition(NodeImpl *node,
const Position &pos)
1604 Position upstream(pos.equivalentUpstreamPosition());
1605 NodeImpl *cb = pos.node()->enclosingBlockFlowElement();
1606 if (cb == pos.node())
1612 void InputNewlineCommandImpl::insertNodeBeforePosition(NodeImpl *node,
const Position &pos)
1617 Position upstream(pos.equivalentUpstreamPosition());
1618 NodeImpl *cb = pos.node()->enclosingBlockFlowElement();
1619 if (cb == pos.node())
1629 int exceptionCode = 0;
1631 NodeImpl *enclosingBlock = selection.start().node()->enclosingBlockFlowElement();
1632 kDebug() << enclosingBlock->nodeName() << endl;
1633 if (enclosingBlock->id() == ID_LI) {
1638 #ifdef DEBUG_COMMANDS
1639 kDebug() <<
"[insert new list item]" << selection << endl;
1640 printEnclosingBlockTree(selection.start().node());
1642 Position pos(selection.start().equivalentDownstreamPosition());
1643 NodeImpl *node = pos.node();
1644 bool atBlockStart = pos.atStartOfContainingEditableBlock();
1645 bool atBlockEnd = pos.isLastRenderedPositionInEditableBlock();
1647 if (node->isTextNode() && !atBlockStart && !atBlockEnd) {
1648 TextImpl *textNode =
static_cast<TextImpl*
>(node);
1649 TextImpl *textBeforeNode =
document()->createTextNode(textNode->substringData(0, selection.start().offset(), exceptionCode));
1652 pos = Position(textNode, 0);
1657 #ifdef DEBUG_COMMANDS
1658 kDebug() <<
"[handle node]" << node << endl;
1659 printEnclosingBlockTree(enclosingBlock->parent());
1661 NodeImpl *parent = node->parent();
1663 RefPtr<NodeImpl> newParent = parent->cloneNode(
false);
1665 for (NodeImpl *nextSibling = 0; node; node = nextSibling) {
1666 #ifdef DEBUG_COMMANDS
1667 kDebug() <<
"[reattach sibling]" << node << endl;
1669 nextSibling = node->nextSibling();
1673 node = newParent.get();
1674 if (parent == enclosingBlock)
1677 }
else if (node->isTextNode()) {
1680 ElementImpl *listItem =
document()->createHTMLElement(
"LI");
1683 ElementImpl *listItem =
document()->createHTMLElement(
"LI");
1688 #ifdef DEBUG_COMMANDS
1689 kDebug() <<
"[result]" << endl;
1690 printEnclosingBlockTree(enclosingBlock->parent());
1696 ElementImpl *breakNode =
document()->createHTMLElement(
"BR");
1699 #ifdef DEBUG_COMMANDS
1700 kDebug() <<
"[insert break]" << selection << endl;
1701 printEnclosingBlockTree(enclosingBlock);
1704 NodeImpl *nodeToInsert = breakNode;
1706 if (
document()->part()->editor()->typingStyle()) {
1707 int exceptionCode = 0;
1709 styleElement->appendChild(breakNode, exceptionCode);
1710 assert(exceptionCode == 0);
1711 nodeToInsert = styleElement;
1714 Position pos(selection.start().equivalentDownstreamPosition());
1715 bool atStart = pos.offset() <= pos.node()->caretMinOffset();
1716 bool atEndOfBlock = pos.isLastRenderedPositionInEditableBlock();
1718 #ifdef DEBUG_COMMANDS
1719 kDebug() <<
"[pos]" << pos << atStart << atEndOfBlock << endl;
1723 #ifdef DEBUG_COMMANDS
1724 kDebug(6200) <<
"input newline case 1";
1729 insertNodeAfterPosition(nodeToInsert, pos);
1731 ElementImpl *extraBreakNode =
document()->createHTMLElement(
"BR");
1735 }
else if (atStart) {
1736 #ifdef DEBUG_COMMANDS
1737 kDebug(6200) <<
"input newline case 2";
1742 insertNodeBeforePosition(nodeToInsert, pos);
1748 #ifdef DEBUG_COMMANDS
1749 kDebug(6200) <<
"input newline case 3";
1751 assert(pos.node()->isTextNode());
1752 TextImpl *textNode =
static_cast<TextImpl *
>(pos.node());
1753 TextImpl *textBeforeNode =
document()->createTextNode(textNode->substringData(0, selection.start().offset(), exceptionCode));
1754 deleteText(textNode, 0, selection.start().offset());
1788 if (!selection.start().node()->isTextNode())
1791 int exceptionCode = 0;
1792 int offset = selection.start().offset() - 1;
1793 if (offset >= selection.start().node()->caretMinOffset()) {
1794 TextImpl *textNode =
static_cast<TextImpl *
>(selection.start().node());
1795 textNode->deleteData(offset, 1, exceptionCode);
1796 assert(exceptionCode == 0);
1797 selection = Selection(Position(textNode, offset));
1799 m_charactersAdded--;
1803 Position InputTextCommandImpl::prepareForTextInsertion(
bool adjustDownstream)
1808 assert(selection.state() == Selection::CARET);
1810 #ifdef DEBUG_COMMANDS
1811 kDebug() <<
"[prepare selection]" << selection << endl;
1814 Position pos = selection.start();
1815 if (adjustDownstream)
1816 pos = pos.equivalentDownstreamPosition();
1818 pos = pos.equivalentUpstreamPosition();
1820 #ifdef DEBUG_COMMANDS
1821 kDebug() <<
"[prepare position]" << pos << endl;
1824 if (!pos.node()->isTextNode()) {
1825 NodeImpl *textNode =
document()->createEditingTextNode(
"");
1826 NodeImpl *nodeToInsert = textNode;
1827 if (
document()->part()->editor()->typingStyle()) {
1828 int exceptionCode = 0;
1830 styleElement->appendChild(textNode, exceptionCode);
1831 assert(exceptionCode == 0);
1832 nodeToInsert = styleElement;
1836 if (pos.node()->isEditableBlock()) {
1837 kDebug(6200) <<
"prepareForTextInsertion case 1";
1839 }
else if (pos.node()->id() == ID_BR && pos.offset() == 1) {
1840 kDebug(6200) <<
"prepareForTextInsertion case 2";
1842 }
else if (pos.node()->caretMinOffset() == pos.offset()) {
1843 kDebug(6200) <<
"prepareForTextInsertion case 3";
1845 }
else if (pos.node()->caretMaxOffset() == pos.offset()) {
1846 kDebug(6200) <<
"prepareForTextInsertion case 4";
1851 pos = Position(textNode, 0);
1854 if (
document()->part()->editor()->typingStyle()) {
1855 if (pos.node()->isTextNode() && pos.offset() > pos.node()->caretMinOffset() && pos.offset() < pos.node()->caretMaxOffset()) {
1857 TextImpl *text =
static_cast<TextImpl *
>(pos.node());
1858 RefPtr<SplitTextNodeCommandImpl> cmd =
new SplitTextNodeCommandImpl(
document(), text, pos.offset());
1863 int exceptionCode = 0;
1864 TextImpl *editingTextNode =
document()->createEditingTextNode(
"");
1867 styleElement->appendChild(editingTextNode, exceptionCode);
1868 assert(exceptionCode == 0);
1875 pos = Position(editingTextNode, 0);
1881 void InputTextCommandImpl::execute(
const DOMString &text)
1883 #ifdef DEBUG_COMMANDS
1884 kDebug() <<
"[execute command]" << text << endl;
1887 #ifdef DEBUG_COMMANDS
1888 kDebug() <<
"[ending selection]" << selection << endl;
1890 bool adjustDownstream = selection.start().isFirstRenderedPositionOnLine();
1891 #ifdef DEBUG_COMMANDS
1892 kDebug() <<
"[adjust]" << adjustDownstream << endl;
1895 #ifdef DEBUG_COMMANDS
1896 printEnclosingBlockTree(selection.start().node());
1900 if (selection.state() == Selection::RANGE)
1905 #ifdef DEBUG_COMMANDS
1906 kDebug() <<
"[after collapsible whitespace deletion]" << endl;
1907 printEnclosingBlockTree(selection.start().node());
1913 Position pos = prepareForTextInsertion(adjustDownstream);
1914 #ifdef DEBUG_COMMANDS
1915 kDebug() <<
"[after prepare]" << pos << endl;
1918 TextImpl *textNode =
static_cast<TextImpl *
>(pos.node());
1919 long offset = pos.offset();
1921 #ifdef DEBUG_COMMANDS
1922 kDebug() <<
"[insert at]" << textNode << offset << endl;
1929 insertSpace(textNode, offset);
1931 const DOMString &existingText = textNode->data();
1932 if (textNode->length() >= 2 && offset >= 2 && isNBSP(existingText[offset - 1]) && !isWS(existingText[offset - 2])) {
1944 m_charactersAdded += text.
length();
1947 void InputTextCommandImpl::insertSpace(TextImpl *textNode,
unsigned long offset)
1957 for (
unsigned int i = offset; i < text.
length(); i++) {
1966 Position pos(textNode, offset);
1967 Position downstream = pos.equivalentDownstreamPosition();
1968 if (downstream.offset() < (long)text.
length() && isWS(text[downstream.offset()]))
1974 if (offset > 0 && offset <= text.
length() - 1 && !isWS(text[offset]) && !isWS(text[offset - 1])) {
1980 if (text.
length() >= 2 && offset >= 2 && isNBSP(text[offset - 2]) && isNBSP(text[offset - 1])) {
1989 insertText(textNode, offset, nonBreakingSpaceString());
1996 :
EditCommandImpl(document), m_insertChild(insertChild), m_refChild(refChild)
1999 m_insertChild->ref();
2008 m_insertChild->deref();
2010 m_refChild->deref();
2017 assert(m_refChild->parentNode());
2019 int exceptionCode = 0;
2020 m_refChild->parentNode()->insertBefore(m_insertChild, m_refChild, exceptionCode);
2021 assert(exceptionCode == 0);
2028 assert(m_refChild->parentNode());
2030 int exceptionCode = 0;
2031 m_refChild->parentNode()->removeChild(m_insertChild, exceptionCode);
2032 assert(exceptionCode == 0);
2046 m_text = text.
copy();
2060 int exceptionCode = 0;
2061 m_node->insertData(m_offset, m_text, exceptionCode);
2062 assert(exceptionCode == 0);
2070 int exceptionCode = 0;
2071 m_node->deleteData(m_offset, m_text.
length(), exceptionCode);
2072 assert(exceptionCode == 0);
2083 assert(m_text1->nextSibling() == m_text2);
2084 assert(m_text1->length() > 0);
2085 assert(m_text2->length() > 0);
2103 assert(m_text1->nextSibling() == m_text2);
2105 int exceptionCode = 0;
2106 m_text2->insertData(0, m_text1->data(), exceptionCode);
2107 assert(exceptionCode == 0);
2109 m_text2->parentNode()->removeChild(m_text1, exceptionCode);
2110 assert(exceptionCode == 0);
2112 m_offset = m_text1->length();
2120 int exceptionCode = 0;
2122 m_text2->deleteData(0, m_offset, exceptionCode);
2123 assert(exceptionCode == 0);
2125 m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
2126 assert(exceptionCode == 0);
2128 assert(m_text2->previousSibling()->isTextNode());
2129 assert(m_text2->previousSibling() == m_text1);
2146 NodeImpl *firstChild = m_fragment->firstChild();
2147 NodeImpl *lastChild = m_fragment->lastChild();
2152 if (selection.state() == Selection::RANGE)
2158 assert(!selection.isEmpty());
2163 }
else if (firstChild == lastChild && firstChild->isTextNode()) {
2165 Position base = selection.base();
2166 inputText(static_cast<TextImpl *>(firstChild)->data());
2167 if (m_selectReplacement) {
2173 NodeImpl *beforeNode = firstChild;
2174 NodeImpl *node = firstChild->nextSibling();
2176 insertNodeAt(firstChild, selection.start().node(), selection.start().offset());
2180 NodeImpl *next = node->nextSibling();
2188 NodeImpl *lastLeaf = lastChild;
2190 NodeImpl *nextChild = lastLeaf->lastChild();
2193 lastLeaf = nextChild;
2196 if (m_selectReplacement) {
2198 NodeImpl *firstLeaf = firstChild;
2200 NodeImpl *nextChild = firstLeaf->firstChild();
2203 firstLeaf = nextChild;
2206 setEndingSelection(Selection(Position(firstLeaf, firstLeaf->caretMinOffset()), Position(lastLeaf, lastLeaf->caretMaxOffset())));
2229 assert(selection.state() == Selection::RANGE);
2232 NodeImpl *positionNode = m_position.node();
2233 long positionOffset = m_position.offset();
2234 Position selectionEnd = selection.end();
2235 long selectionEndOffset = selectionEnd.offset();
2236 if (selectionEnd.node() == positionNode && selectionEndOffset < positionOffset) {
2237 positionOffset -= selectionEndOffset;
2238 Position selectionStart = selection.start();
2239 if (selectionStart.node() == positionNode) {
2240 positionOffset += selectionStart.offset();
2255 :
EditCommandImpl(document), m_decl(decl), m_property(property), m_important(false)
2271 m_oldValue = m_decl->getPropertyValue(m_property);
2274 m_important = m_decl->getPropertyPriority(m_property);
2275 m_decl->removeProperty(m_property);
2283 m_decl->setProperty(m_property, m_oldValue, m_important);
2290 :
EditCommandImpl(document), m_element(element), m_attribute(attribute)
2306 m_oldValue = m_element->getAttribute(m_attribute);
2309 int exceptionCode = 0;
2310 m_element->removeAttribute(m_attribute, exceptionCode);
2311 assert(exceptionCode == 0);
2320 m_element->setAttribute(m_attribute, m_oldValue.
implementation());
2328 :
EditCommandImpl(document), m_parent(0), m_removeChild(removeChild), m_refChild(0)
2331 m_removeChild->ref();
2333 m_parent = m_removeChild->parentNode();
2337 RefPtr<DOM::NodeListImpl> children = m_parent->childNodes();
2338 for (
long i = children->length() - 1; i >= 0; --i) {
2339 NodeImpl *node = children->item(i);
2340 if (node == m_removeChild)
2354 m_removeChild->deref();
2356 m_refChild->deref();
2364 int exceptionCode = 0;
2365 m_parent->removeChild(m_removeChild, exceptionCode);
2366 assert(exceptionCode == 0);
2374 int exceptionCode = 0;
2376 m_parent->insertBefore(m_removeChild, m_refChild, exceptionCode);
2378 m_parent->appendChild(m_removeChild, exceptionCode);
2379 assert(exceptionCode == 0);
2396 m_pruneNode->deref();
2398 m_stopNode->deref();
2403 NodeImpl *editableBlock = m_pruneNode->enclosingBlockFlowElement();
2405 NodeImpl *node = pruneNode->traversePreviousNode();
2408 if (node == m_stopNode || editableBlock != node->enclosingBlockFlowElement() || !shouldPruneNode(node))
2411 node = node->traversePreviousNode();
2434 RefPtr<DOM::NodeListImpl> children =
node()->childNodes();
2435 const unsigned int length = children->length();
2436 for (
unsigned int i = 0; i < length; ++i) {
2437 NodeImpl *child = children->item(0);
2448 :
EditCommandImpl(document), m_element(element), m_attribute(attribute), m_value(value)
2467 m_oldValue = m_element->getAttribute(m_attribute);
2478 m_element->setAttribute(m_attribute, m_oldValue.
implementation());
2486 :
EditCommandImpl(document), m_text1(0), m_text2(text), m_offset(offset)
2489 assert(m_text2->length() > 0);
2507 int exceptionCode = 0;
2516 m_text1 =
document()->createTextNode(m_text2->substringData(0, m_offset, exceptionCode));
2517 assert(exceptionCode == 0);
2522 m_text2->deleteData(0, m_offset, exceptionCode);
2523 assert(exceptionCode == 0);
2525 m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
2526 assert(exceptionCode == 0);
2528 assert(m_text2->previousSibling()->isTextNode());
2529 assert(m_text2->previousSibling() == m_text1);
2537 assert(m_text1->nextSibling() == m_text2);
2539 int exceptionCode = 0;
2540 m_text2->insertData(0, m_text1->data(), exceptionCode);
2541 assert(exceptionCode == 0);
2543 m_text2->parentNode()->removeChild(m_text1, exceptionCode);
2544 assert(exceptionCode == 0);
2546 m_offset = m_text1->length();
2565 void TypingCommandImpl::typingAddedToOpenCommand()
2569 document()->part()->editor()->appliedEditing(
this);
2574 if (
document()->part()->editor()->typingStyle() ||
m_cmds.count() == 0) {
2588 typingAddedToOpenCommand();
2595 typingAddedToOpenCommand();
2598 void TypingCommandImpl::issueCommandForDeleteKey()
2601 assert(selectionToDelete.state() != Selection::NONE);
2603 #ifdef DEBUG_COMMANDS
2604 kDebug() <<
"[selection]" << selectionToDelete << endl;
2606 if (selectionToDelete.state() == Selection::CARET) {
2607 #ifdef DEBUG_COMMANDS
2608 kDebug() <<
"[caret selection]" << endl;
2610 Position pos(selectionToDelete.start());
2611 if (pos.inFirstEditableInRootEditableElement() && pos.offset() <= pos.node()->caretMinOffset()) {
2615 selectionToDelete = Selection(pos.previousCharacterPosition(), pos);
2616 #ifdef DEBUG_COMMANDS
2617 kDebug() <<
"[modified selection]" << selectionToDelete << endl;
2621 typingAddedToOpenCommand();
2634 issueCommandForDeleteKey();
2636 if (
m_cmds.count() == 0) {
2637 issueCommandForDeleteKey();
2640 EditCommand lastCommand =
m_cmds.last();
2641 if (lastCommand.commandID() == InputTextCommandID) {
2642 InputTextCommand cmd =
static_cast<InputTextCommand &
>(lastCommand);
2643 cmd.deleteCharacter();
2644 if (cmd.charactersAdded() == 0) {
2648 else if (lastCommand.commandID() == InputNewlineCommandID) {
2649 lastCommand.unapply();
2650 removeCommand(lastCommand);
2653 issueCommandForDeleteKey();
2659 void TypingCommandImpl::removeCommand(
const PassRefPtr<EditCommandImpl> cmd)
2673 static bool isOpenForMoreTypingCommand(
const EditCommandImpl *command)
2675 return command && command->isTypingCommand() &&
2676 static_cast<const TypingCommandImpl*
>(command)->openForMoreTyping();
2688 command->deleteKeyPressed();
2695 Editor *ed = document->part()->editor();
2698 if (isOpenForMoreTypingCommand(lastEditCommand)) {
2703 command->insertNewline();
2709 #ifdef DEBUG_COMMANDS
2710 kDebug() <<
"[insert text]" << text << endl;
2713 Editor *ed = document->part()->editor();
2716 if (isOpenForMoreTypingCommand(lastEditCommand)) {
2721 command->insertText(text);
2740 #ifdef DEBUG_COMMANDS
2745 ElementImpl *startBlock = start.node()->enclosingBlockFlowElement();
2746 ElementImpl *endBlock = end.node()->enclosingBlockFlowElement();
2747 #ifdef DEBUG_COMMANDS
2748 kDebug() <<
"[start:end blocks]" << startBlock << endBlock << endl;
2749 printEnclosingBlockTree(start.node());
2751 if (startBlock == endBlock) {
2752 if (startBlock->id() == ID_LI) {
2754 #ifdef DEBUG_COMMANDS
2755 kDebug() <<
"[remove list item]" << endl;
2757 NodeImpl *listBlock = startBlock->parent();
2760 if (listBlock->firstChild() == listBlock->lastChild() && listBlock->firstChild() == startBlock) {
2762 #ifdef DEBUG_COMMANDS
2763 kDebug() <<
"[remove list completely]" << endl;
2767 }
else if (!startBlock->previousSibling()) {
2769 NodeImpl *nextSibling;
2770 for (NodeImpl *node = startBlock->firstChild(); node; node = nextSibling) {
2771 nextSibling = node->nextSibling();
2776 }
else if (!startBlock->nextSibling()) {
2778 NodeImpl *nextSibling;
2779 for (NodeImpl *node = startBlock->lastChild(); node; node = nextSibling) {
2780 nextSibling = node->previousSibling();
2787 WTF::PassRefPtr<NodeImpl> newListBlock = listBlock->cloneNode(
false);
2789 NodeImpl *node, *nextSibling;
2790 for (node = startBlock->nextSibling(); node; node = nextSibling) {
2791 nextSibling = node->nextSibling();
2795 for (node = startBlock->firstChild(); node; node = nextSibling) {
2796 nextSibling = node->nextSibling();
2804 ElementImpl *li =
document()->createHTMLElement(
"LI");
2807 for (NodeImpl *node = startBlock->firstChild(); node; node = nextNode) {
2808 #ifdef DEBUG_COMMANDS
2809 kDebug() <<
"[reattach node]" << node << endl;
2811 nextNode = node->nextSibling();
2818 #ifdef DEBUG_COMMANDS
2819 kDebug() <<
"[different blocks are not supported yet]" << endl;
2827 insertCommand->apply();
2844 void IndentOutdentCommandImpl::indent()
2847 #ifdef DEBUG_COMMANDS
2848 kDebug() <<
"[indent selection]" << selection << endl;
2850 NodeImpl *startBlock = selection.start().node()->enclosingBlockFlowElement();
2851 NodeImpl *endBlock = selection.end().node()->enclosingBlockFlowElement();
2853 if (startBlock == endBlock) {
2855 if (startBlock->id() == ID_LI && (startBlock->previousSibling() || startBlock->nextSibling())) {
2856 #ifdef DEBUG_COMMANDS
2857 kDebug() <<
"[modify list]" << endl;
2859 RefPtr<NodeImpl> newList = startBlock->parent()->cloneNode(
false);
2864 NodeImpl *blockquoteElement =
document()->createHTMLElement(
"blockquote");
2865 if (startBlock->id() == ID_LI) {
2866 startBlock = startBlock->parent();
2867 NodeImpl *parent = startBlock->parent();
2872 NodeImpl *parent = startBlock->parent();
2879 if (startBlock->id() == ID_LI && endBlock->id() == ID_LI && startBlock->parent() == endBlock->parent()) {
2880 #ifdef DEBUG_COMMANDS
2881 kDebug() <<
"[indent some items inside list]" << endl;
2883 RefPtr<NodeImpl> nestedList = startBlock->parent()->cloneNode(
false);
2885 NodeImpl *nextNode = 0;
2886 for (NodeImpl *node = startBlock;; node = nextNode) {
2887 nextNode = node->nextSibling();
2890 if (node == endBlock)
2894 #ifdef DEBUG_COMMANDS
2895 kDebug() <<
"[blocks not from one list are not supported yet]" << endl;
2901 static bool hasPreviousListItem(NodeImpl *node)
2904 node = node->previousSibling();
2905 if (node && node->id() == ID_LI)
2911 static bool hasNextListItem(NodeImpl *node)
2914 node = node->nextSibling();
2915 if (node && node->id() == ID_LI)
2921 void IndentOutdentCommandImpl::outdent()
2924 #ifdef DEBUG_COMMANDS
2925 kDebug() <<
"[indent selection]" << selection << endl;
2927 NodeImpl *startBlock = selection.start().node()->enclosingBlockFlowElement();
2928 NodeImpl *endBlock = selection.end().node()->enclosingBlockFlowElement();
2930 if (startBlock->id() == ID_LI && endBlock->id() == ID_LI && startBlock->parent() == endBlock->parent()) {
2931 #ifdef DEBUG_COMMANDS
2932 kDebug() <<
"[list items selected]" << endl;
2934 bool firstItemSelected = !hasPreviousListItem(startBlock);
2935 bool lastItemSelected = !hasNextListItem(endBlock);
2936 bool listFullySelected = firstItemSelected && lastItemSelected;
2938 #ifdef DEBUG_COMMANDS
2939 kDebug() <<
"[first/last item selected]" << firstItemSelected << lastItemSelected << endl;
2942 NodeImpl *listNode = startBlock->parent();
2943 printEnclosingBlockTree(listNode);
2944 bool hasParentList = listNode->parent()->id() == ID_OL || listNode->parent()->id() == ID_UL;
2946 if (!firstItemSelected && !lastItemSelected) {
2948 RefPtr<NodeImpl> clonedList = listNode->cloneNode(
false);
2949 NodeImpl *nextNode = 0;
2950 for (NodeImpl *node = listNode->firstChild(); node != startBlock; node = nextNode) {
2951 nextNode = node->nextSibling();
2957 firstItemSelected =
true;
2960 NodeImpl *nextNode = 0;
2961 for (NodeImpl *node = firstItemSelected ? startBlock : endBlock;; node = nextNode) {
2962 nextNode = firstItemSelected ? node->nextSibling() : node->previousSibling();
2964 if (firstItemSelected)
2968 if (!hasParentList && node->id() == ID_LI) {
2972 if (node == (firstItemSelected ? endBlock : startBlock))
2975 if (listFullySelected)
2981 if (startBlock == endBlock) {
2982 if (startBlock->id() == ID_BLOCKQUOTE) {
2985 #ifdef DEBUG_COMMANDS
2986 kDebug() <<
"[not the list or blockquote]" << endl;
2990 #ifdef DEBUG_COMMANDS
2991 kDebug() <<
"[blocks not from one list are not supported yet]" << endl;
2998 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)
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 *)