00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "htmlediting_impl.h"
00027 #include "editor.h"
00028
00029 #include "css/cssproperties.h"
00030 #include "css/css_valueimpl.h"
00031 #include "dom/css_value.h"
00032 #include "html/html_elementimpl.h"
00033 #include "html/html_imageimpl.h"
00034 #include "misc/htmlattrs.h"
00035 #include "misc/htmltags.h"
00036 #include "rendering/render_object.h"
00037 #include "rendering/render_style.h"
00038 #include "rendering/render_text.h"
00039 #include "xml/dom_docimpl.h"
00040 #include "xml/dom_elementimpl.h"
00041 #include "xml/dom_position.h"
00042 #include "xml/dom_positioniterator.h"
00043 #include "xml/dom_nodeimpl.h"
00044 #include "xml/dom_selection.h"
00045 #include "xml/dom_stringimpl.h"
00046 #include "xml/dom_textimpl.h"
00047 #include "xml/dom2_rangeimpl.h"
00048 #include "xml/dom2_viewsimpl.h"
00049
00050 #include "khtml_part.h"
00051 #include "khtmlview.h"
00052
00053 #include <QList>
00054 #include <limits.h>
00055
00056 using DOM::AttrImpl;
00057 using DOM::CSSPrimitiveValue;
00058 using DOM::CSSPrimitiveValueImpl;
00059 using DOM::CSSProperty;
00060 using DOM::CSSStyleDeclarationImpl;
00061 using DOM::CSSValueImpl;
00062 using DOM::DocumentFragmentImpl;
00063 using DOM::DocumentImpl;
00064 using DOM::DOMString;
00065 using DOM::DOMStringImpl;
00066 using DOM::EditingTextImpl;
00067 using DOM::PositionIterator;
00068 using DOM::ElementImpl;
00069 using DOM::HTMLElementImpl;
00070 using DOM::HTMLImageElementImpl;
00071 using DOM::NamedAttrMapImpl;
00072 using DOM::Node;
00073 using DOM::NodeImpl;
00074 using DOM::NodeListImpl;
00075 using DOM::Position;
00076 using DOM::Range;
00077 using DOM::RangeImpl;
00078 using DOM::Selection;
00079 using DOM::TextImpl;
00080 using DOM::TreeWalkerImpl;
00081
00082 #ifdef LOG_DISABLED
00083 #define debugPosition(a,b) ((void)0)
00084 #endif
00085
00086 namespace khtml {
00087
00088
00089 static inline bool isNBSP(const QChar &c)
00090 {
00091 return c == QChar(0xa0);
00092 }
00093
00094 static inline bool isWS(const QChar &c)
00095 {
00096 return c.isSpace() && c != QChar(0xa0);
00097 }
00098
00099 static inline bool isWS(const DOMString &text)
00100 {
00101 if (text.length() != 1)
00102 return false;
00103
00104 return isWS(text[0]);
00105 }
00106
00107 static inline bool isWS(const Position &pos)
00108 {
00109 if (!pos.node())
00110 return false;
00111
00112 if (!pos.node()->isTextNode())
00113 return false;
00114
00115 const DOMString &string = static_cast<TextImpl *>(pos.node())->data();
00116 return isWS(string[pos.offset()]);
00117 }
00118
00119 static bool shouldPruneNode(NodeImpl *node)
00120 {
00121 if (!node)
00122 return false;
00123
00124 RenderObject *renderer = node->renderer();
00125 if (!renderer)
00126 return true;
00127
00128 if (node->hasChildNodes())
00129 return false;
00130
00131 if (node->rootEditableElement() == node)
00132 return false;
00133
00134 if (renderer->isBR() || renderer->isReplaced())
00135 return false;
00136
00137 if (node->isTextNode()) {
00138 TextImpl *text = static_cast<TextImpl *>(node);
00139 if (text->length() == 0)
00140 return true;
00141 return false;
00142 }
00143
00144 if (!node->isHTMLElement())
00145 return false;
00146
00147 if (node->id() == ID_BODY)
00148 return false;
00149
00150 if (!node->isContentEditable())
00151 return false;
00152
00153 return true;
00154 }
00155
00156 static Position leadingWhitespacePosition(const Position &pos)
00157 {
00158 assert(pos.notEmpty());
00159
00160 Selection selection(pos);
00161 Position prev = pos.previousCharacterPosition();
00162 if (prev != pos && prev.node()->inSameContainingBlockFlowElement(pos.node()) && prev.node()->isTextNode()) {
00163 DOMString string = static_cast<TextImpl *>(prev.node())->data();
00164 if (isWS(string[prev.offset()]))
00165 return prev;
00166 }
00167
00168 return Position();
00169 }
00170
00171 static Position trailingWhitespacePosition(const Position &pos)
00172 {
00173 assert(pos.notEmpty());
00174
00175 if (pos.node()->isTextNode()) {
00176 TextImpl *textNode = static_cast<TextImpl *>(pos.node());
00177 if (pos.offset() >= (long)textNode->length()) {
00178 Position next = pos.nextCharacterPosition();
00179 if (next != pos && next.node()->inSameContainingBlockFlowElement(pos.node()) && next.node()->isTextNode()) {
00180 DOMString string = static_cast<TextImpl *>(next.node())->data();
00181 if (isWS(string[0]))
00182 return next;
00183 }
00184 }
00185 else {
00186 DOMString string = static_cast<TextImpl *>(pos.node())->data();
00187 if (isWS(string[pos.offset()]))
00188 return pos;
00189 }
00190 }
00191
00192 return Position();
00193 }
00194
00195 static bool textNodesAreJoinable(TextImpl *text1, TextImpl *text2)
00196 {
00197 assert(text1);
00198 assert(text2);
00199
00200 return (text1->nextSibling() == text2);
00201 }
00202
00203 static DOMString &nonBreakingSpaceString()
00204 {
00205 static DOMString nonBreakingSpaceString = QString(QChar(0xa0));
00206 return nonBreakingSpaceString;
00207 }
00208
00209 static DOMString &styleSpanClassString()
00210 {
00211 static DOMString styleSpanClassString = "khtml-style-span";
00212 return styleSpanClassString;
00213 }
00214
00215 #ifndef LOG_DISABLED
00216 static void debugPosition(const char *prefix, const Position &pos)
00217 {
00218 kDebug(6200) << prefix << getTagName(pos.node()->id()) << pos.node() << pos.offset();
00219 }
00220 #endif
00221
00222
00223
00224
00225 EditCommandImpl::EditCommandImpl(DocumentImpl *document)
00226 : SharedCommandImpl(), m_document(document), m_state(NotApplied), m_parent(0)
00227 {
00228 assert(m_document);
00229 assert(m_document->part());
00230 m_document->ref();
00231 m_startingSelection = m_document->part()->caret();
00232 m_endingSelection = m_startingSelection;
00233 }
00234
00235 EditCommandImpl::~EditCommandImpl()
00236 {
00237 m_document->deref();
00238 }
00239
00240 int EditCommandImpl::commandID() const
00241 {
00242 return EditCommandID;
00243 }
00244
00245 void EditCommandImpl::apply()
00246 {
00247 assert(m_document);
00248 assert(m_document->part());
00249 assert(state() == NotApplied);
00250
00251 doApply();
00252
00253 m_state = Applied;
00254
00255 if (!isCompositeStep()) {
00256 EditCommand cmd(this);
00257 m_document->part()->editor()->appliedEditing(cmd);
00258 }
00259 }
00260
00261 void EditCommandImpl::unapply()
00262 {
00263 assert(m_document);
00264 assert(m_document->part());
00265 assert(state() == Applied);
00266
00267 doUnapply();
00268
00269 m_state = NotApplied;
00270
00271 if (!isCompositeStep()) {
00272 EditCommand cmd(this);
00273 m_document->part()->editor()->unappliedEditing(cmd);
00274 }
00275 }
00276
00277 void EditCommandImpl::reapply()
00278 {
00279 assert(m_document);
00280 assert(m_document->part());
00281 assert(state() == NotApplied);
00282
00283 doReapply();
00284
00285 m_state = Applied;
00286
00287 if (!isCompositeStep()) {
00288 EditCommand cmd(this);
00289 m_document->part()->editor()->reappliedEditing(cmd);
00290 }
00291 }
00292
00293 void EditCommandImpl::doReapply()
00294 {
00295 doApply();
00296 }
00297
00298 void EditCommandImpl::setStartingSelection(const Selection &s)
00299 {
00300 m_startingSelection = s;
00301 EditCommand cmd( parent() );
00302 while (cmd.notNull()) {
00303 cmd.handle()->m_startingSelection = s;
00304 cmd = cmd.handle()->parent();
00305 }
00306 }
00307
00308 void EditCommandImpl::setEndingSelection(const Selection &s)
00309 {
00310 m_endingSelection = s;
00311 EditCommand cmd = parent();
00312 while (cmd.notNull()) {
00313 cmd.handle()->m_endingSelection = s;
00314 cmd = cmd.handle()->parent();
00315 }
00316 }
00317
00318 EditCommandImpl* EditCommandImpl::parent() const
00319 {
00320 return m_parent;
00321 }
00322
00323 void EditCommandImpl::setParent(EditCommandImpl* cmd)
00324 {
00325 m_parent = cmd;
00326 }
00327
00328
00329
00330
00331 CompositeEditCommandImpl::CompositeEditCommandImpl(DocumentImpl *document)
00332 : EditCommandImpl(document)
00333 {
00334 }
00335
00336 CompositeEditCommandImpl::~CompositeEditCommandImpl()
00337 {
00338 }
00339
00340 int CompositeEditCommandImpl::commandID() const
00341 {
00342 return CompositeEditCommandID;
00343 }
00344
00345 void CompositeEditCommandImpl::doUnapply()
00346 {
00347 if (m_cmds.count() == 0) {
00348 return;
00349 }
00350
00351 for (int i = m_cmds.count() - 1; i >= 0; --i)
00352 m_cmds[i].unapply();
00353
00354 setState(NotApplied);
00355 }
00356
00357 void CompositeEditCommandImpl::doReapply()
00358 {
00359 if (m_cmds.count() == 0) {
00360 return;
00361 }
00362 QMutableListIterator<EditCommand> it(m_cmds);
00363 while (it.hasNext())
00364 it.next().reapply();
00365
00366 setState(Applied);
00367 }
00368
00369
00370
00371
00372 void CompositeEditCommandImpl::applyCommandToComposite(EditCommand &cmd)
00373 {
00374 cmd.setStartingSelection(endingSelection());
00375 cmd.setEndingSelection(endingSelection());
00376 cmd.handle()->setParent(this);
00377 cmd.apply();
00378 m_cmds.append(cmd);
00379 }
00380
00381 void CompositeEditCommandImpl::insertNodeBefore(NodeImpl *insertChild, NodeImpl *refChild)
00382 {
00383 InsertNodeBeforeCommand cmd(document(), insertChild, refChild);
00384 applyCommandToComposite(cmd);
00385 }
00386
00387 void CompositeEditCommandImpl::insertNodeAfter(NodeImpl *insertChild, NodeImpl *refChild)
00388 {
00389 if (refChild->parentNode()->lastChild() == refChild) {
00390 appendNode(refChild->parentNode(), insertChild);
00391 }
00392 else {
00393 assert(refChild->nextSibling());
00394 insertNodeBefore(insertChild, refChild->nextSibling());
00395 }
00396 }
00397
00398 void CompositeEditCommandImpl::insertNodeAt(NodeImpl *insertChild, NodeImpl *refChild, long offset)
00399 {
00400 if (refChild->hasChildNodes() || (refChild->renderer() && refChild->renderer()->isBlockFlow())) {
00401 NodeImpl *child = refChild->firstChild();
00402 for (long i = 0; child && i < offset; i++)
00403 child = child->nextSibling();
00404 if (child)
00405 insertNodeBefore(insertChild, child);
00406 else
00407 appendNode(refChild, insertChild);
00408 }
00409 else if (refChild->caretMinOffset() >= offset) {
00410 insertNodeBefore(insertChild, refChild);
00411 }
00412 else if (refChild->isTextNode() && refChild->caretMaxOffset() > offset) {
00413 splitTextNode(static_cast<TextImpl *>(refChild), offset);
00414 insertNodeBefore(insertChild, refChild);
00415 }
00416 else {
00417 insertNodeAfter(insertChild, refChild);
00418 }
00419 }
00420
00421 void CompositeEditCommandImpl::appendNode(NodeImpl *parent, NodeImpl *appendChild)
00422 {
00423 AppendNodeCommand cmd(document(), parent, appendChild);
00424 applyCommandToComposite(cmd);
00425 }
00426
00427 void CompositeEditCommandImpl::removeNode(NodeImpl *removeChild)
00428 {
00429 RemoveNodeCommand cmd(document(), removeChild);
00430 applyCommandToComposite(cmd);
00431 }
00432
00433 void CompositeEditCommandImpl::removeNodeAndPrune(NodeImpl *pruneNode, NodeImpl *stopNode)
00434 {
00435 RemoveNodeAndPruneCommand cmd(document(), pruneNode, stopNode);
00436 applyCommandToComposite(cmd);
00437 }
00438
00439 void CompositeEditCommandImpl::removeNodePreservingChildren(NodeImpl *removeChild)
00440 {
00441 RemoveNodePreservingChildrenCommand cmd(document(), removeChild);
00442 applyCommandToComposite(cmd);
00443 }
00444
00445 void CompositeEditCommandImpl::splitTextNode(TextImpl *text, long offset)
00446 {
00447 SplitTextNodeCommand cmd(document(), text, offset);
00448 applyCommandToComposite(cmd);
00449 }
00450
00451 void CompositeEditCommandImpl::joinTextNodes(TextImpl *text1, TextImpl *text2)
00452 {
00453 JoinTextNodesCommand cmd(document(), text1, text2);
00454 applyCommandToComposite(cmd);
00455 }
00456
00457 void CompositeEditCommandImpl::inputText(const DOMString &text)
00458 {
00459 InputTextCommand cmd(document());
00460 applyCommandToComposite(cmd);
00461 cmd.input(text);
00462 }
00463
00464 void CompositeEditCommandImpl::insertText(TextImpl *node, long offset, const DOMString &text)
00465 {
00466 InsertTextCommand cmd(document(), node, offset, text);
00467 applyCommandToComposite(cmd);
00468 }
00469
00470 void CompositeEditCommandImpl::deleteText(TextImpl *node, long offset, long count)
00471 {
00472 DeleteTextCommand cmd(document(), node, offset, count);
00473 applyCommandToComposite(cmd);
00474 }
00475
00476 void CompositeEditCommandImpl::replaceText(TextImpl *node, long offset, long count, const DOMString &replacementText)
00477 {
00478 DeleteTextCommand deleteCommand(document(), node, offset, count);
00479 applyCommandToComposite(deleteCommand);
00480 InsertTextCommand insertCommand(document(), node, offset, replacementText);
00481 applyCommandToComposite(insertCommand);
00482 }
00483
00484 void CompositeEditCommandImpl::deleteSelection()
00485 {
00486 if (endingSelection().state() == Selection::RANGE) {
00487 DeleteSelectionCommand cmd(document());
00488 applyCommandToComposite(cmd);
00489 }
00490 }
00491
00492 void CompositeEditCommandImpl::deleteSelection(const Selection &selection)
00493 {
00494 if (selection.state() == Selection::RANGE) {
00495 DeleteSelectionCommand cmd(document(), selection);
00496 applyCommandToComposite(cmd);
00497 }
00498 }
00499
00500 void CompositeEditCommandImpl::deleteCollapsibleWhitespace()
00501 {
00502 DeleteCollapsibleWhitespaceCommand cmd(document());
00503 applyCommandToComposite(cmd);
00504 }
00505
00506 void CompositeEditCommandImpl::deleteCollapsibleWhitespace(const Selection &selection)
00507 {
00508 DeleteCollapsibleWhitespaceCommand cmd(document(), selection);
00509 applyCommandToComposite(cmd);
00510 }
00511
00512 void CompositeEditCommandImpl::removeCSSProperty(CSSStyleDeclarationImpl *decl, int property)
00513 {
00514 RemoveCSSPropertyCommand cmd(document(), decl, property);
00515 applyCommandToComposite(cmd);
00516 }
00517
00518 void CompositeEditCommandImpl::removeNodeAttribute(ElementImpl *element, int attribute)
00519 {
00520 RemoveNodeAttributeCommand cmd(document(), element, attribute);
00521 applyCommandToComposite(cmd);
00522 }
00523
00524 void CompositeEditCommandImpl::setNodeAttribute(ElementImpl *element, int attribute, const DOMString &value)
00525 {
00526 SetNodeAttributeCommand cmd(document(), element, attribute, value);
00527 applyCommandToComposite(cmd);
00528 }
00529
00530 ElementImpl *CompositeEditCommandImpl::createTypingStyleElement() const
00531 {
00532 int exceptionCode = 0;
00533 ElementImpl *styleElement = document()->createHTMLElement("SPAN");
00534
00535
00536 styleElement->setAttribute(ATTR_STYLE, document()->part()->editor()->typingStyle()->cssText().implementation());
00537
00538
00539 styleElement->setAttribute(ATTR_CLASS, styleSpanClassString());
00540 assert(exceptionCode == 0);
00541
00542 return styleElement;
00543 }
00544
00545
00546
00547
00548
00549
00550 AppendNodeCommandImpl::AppendNodeCommandImpl(DocumentImpl *document, NodeImpl *parentNode, NodeImpl *appendChild)
00551 : EditCommandImpl(document), m_parentNode(parentNode), m_appendChild(appendChild)
00552 {
00553 assert(m_parentNode);
00554 m_parentNode->ref();
00555
00556 assert(m_appendChild);
00557 m_appendChild->ref();
00558 }
00559
00560 AppendNodeCommandImpl::~AppendNodeCommandImpl()
00561 {
00562 if (m_parentNode)
00563 m_parentNode->deref();
00564 if (m_appendChild)
00565 m_appendChild->deref();
00566 }
00567
00568 int AppendNodeCommandImpl::commandID() const
00569 {
00570 return AppendNodeCommandID;
00571 }
00572
00573 void AppendNodeCommandImpl::doApply()
00574 {
00575 assert(m_parentNode);
00576 assert(m_appendChild);
00577
00578 int exceptionCode = 0;
00579 m_parentNode->appendChild(m_appendChild, exceptionCode);
00580 assert(exceptionCode == 0);
00581 }
00582
00583 void AppendNodeCommandImpl::doUnapply()
00584 {
00585 assert(m_parentNode);
00586 assert(m_appendChild);
00587 assert(state() == Applied);
00588
00589 int exceptionCode = 0;
00590 m_parentNode->removeChild(m_appendChild, exceptionCode);
00591 assert(exceptionCode == 0);
00592 }
00593
00594
00595
00596
00597 ApplyStyleCommandImpl::ApplyStyleCommandImpl(DocumentImpl *document, CSSStyleDeclarationImpl *style)
00598 : CompositeEditCommandImpl(document), m_style(style)
00599 {
00600 assert(m_style);
00601 m_style->ref();
00602 }
00603
00604 ApplyStyleCommandImpl::~ApplyStyleCommandImpl()
00605 {
00606 assert(m_style);
00607 m_style->deref();
00608 }
00609
00610 int ApplyStyleCommandImpl::commandID() const
00611 {
00612 return ApplyStyleCommandID;
00613 }
00614
00615 void ApplyStyleCommandImpl::doApply()
00616 {
00617 if (endingSelection().state() != Selection::RANGE)
00618 return;
00619
00620
00621 Position start(endingSelection().start().equivalentDownstreamPosition().equivalentRangeCompliantPosition());
00622 Position end(endingSelection().end().equivalentUpstreamPosition());
00623
00624
00625 removeStyle(start, end);
00626 bool splitStart = splitTextAtStartIfNeeded(start, end);
00627 if (splitStart) {
00628 start = endingSelection().start();
00629 end = endingSelection().end();
00630 }
00631 splitTextAtEndIfNeeded(start, end);
00632 start = endingSelection().start();
00633 end = endingSelection().end();
00634
00635
00636 if (start.node() == end.node()) {
00637
00638 applyStyleIfNeeded(start.node(), end.node());
00639 }
00640 else {
00641 NodeImpl *node = start.node();
00642 while (1) {
00643 if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
00644 NodeImpl *runStart = node;
00645 while (1) {
00646 if (runStart->parentNode() != node->parentNode() || node->isHTMLElement() || node == end.node() ||
00647 (node->renderer() && !node->renderer()->isInline())) {
00648 applyStyleIfNeeded(runStart, node);
00649 break;
00650 }
00651 node = node->traverseNextNode();
00652 }
00653 }
00654 if (node == end.node())
00655 break;
00656 node = node->traverseNextNode();
00657 }
00658 }
00659 }
00660
00661
00662
00663
00664 bool ApplyStyleCommandImpl::isHTMLStyleNode(HTMLElementImpl *elem)
00665 {
00666 QListIterator<CSSProperty*> it(*(style()->values()));
00667 while (it.hasNext()) {
00668 CSSProperty *property = it.next();
00669 switch (property->id()) {
00670 case CSS_PROP_FONT_WEIGHT:
00671 if (elem->id() == ID_B)
00672 return true;
00673 break;
00674 case CSS_PROP_FONT_STYLE:
00675 if (elem->id() == ID_I)
00676 return true;
00677 break;
00678 }
00679 }
00680
00681 return false;
00682 }
00683
00684 void ApplyStyleCommandImpl::removeHTMLStyleNode(HTMLElementImpl *elem)
00685 {
00686
00687
00688
00689
00690 assert(elem);
00691 removeNodePreservingChildren(elem);
00692 }
00693
00694 void ApplyStyleCommandImpl::removeCSSStyle(HTMLElementImpl *elem)
00695 {
00696 assert(elem);
00697
00698 CSSStyleDeclarationImpl *decl = elem->inlineStyleDecls();
00699 if (!decl)
00700 return;
00701
00702 QListIterator<CSSProperty*> it(*(style()->values()));
00703 while ( it.hasNext() ) {
00704 CSSProperty *property = it.next();
00705 if (decl->getPropertyCSSValue(property->id()))
00706 removeCSSProperty(decl, property->id());
00707 }
00708
00709 if (elem->id() == ID_SPAN) {
00710
00711
00712
00713 NamedAttrMapImpl *map = elem->attributes();
00714 if (map && map->length() == 1 && elem->getAttribute(ATTR_CLASS) == styleSpanClassString())
00715 removeNodePreservingChildren(elem);
00716 }
00717 }
00718
00719 void ApplyStyleCommandImpl::removeStyle(const Position &start, const Position &end)
00720 {
00721 NodeImpl *node = start.node();
00722 while (1) {
00723 NodeImpl *next = node->traverseNextNode();
00724 if (node->isHTMLElement() && nodeFullySelected(node)) {
00725 HTMLElementImpl *elem = static_cast<HTMLElementImpl *>(node);
00726 if (isHTMLStyleNode(elem))
00727 removeHTMLStyleNode(elem);
00728 else
00729 removeCSSStyle(elem);
00730 }
00731 if (node == end.node())
00732 break;
00733 node = next;
00734 }
00735 }
00736
00737 bool ApplyStyleCommandImpl::nodeFullySelected(const NodeImpl *node) const
00738 {
00739 assert(node);
00740
00741 Position end(endingSelection().end().equivalentUpstreamPosition());
00742
00743 if (node == end.node())
00744 return end.offset() >= node->caretMaxOffset();
00745
00746 for (NodeImpl *child = node->lastChild(); child; child = child->lastChild()) {
00747 if (child == end.node())
00748 return end.offset() >= child->caretMaxOffset();
00749 }
00750
00751 return node == end.node() || !node->isAncestor(end.node());
00752 }
00753
00754
00755
00756
00757
00758 bool ApplyStyleCommandImpl::splitTextAtStartIfNeeded(const Position &start, const Position &end)
00759 {
00760 if (start.node()->isTextNode() && start.offset() > start.node()->caretMinOffset() && start.offset() < start.node()->caretMaxOffset()) {
00761 long endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0;
00762 TextImpl *text = static_cast<TextImpl *>(start.node());
00763 SplitTextNodeCommand cmd(document(), text, start.offset());
00764 applyCommandToComposite(cmd);
00765 setEndingSelection(Selection(Position(start.node(), 0), Position(end.node(), end.offset() - endOffsetAdjustment)));
00766 return true;
00767 }
00768 return false;
00769 }
00770
00771 NodeImpl *ApplyStyleCommandImpl::splitTextAtEndIfNeeded(const Position &start, const Position &end)
00772 {
00773 if (end.node()->isTextNode() && end.offset() > end.node()->caretMinOffset() && end.offset() < end.node()->caretMaxOffset()) {
00774 TextImpl *text = static_cast<TextImpl *>(end.node());
00775 SplitTextNodeCommand cmd(document(), text, end.offset());
00776 applyCommandToComposite(cmd);
00777 NodeImpl *startNode = start.node() == end.node() ? cmd.node()->previousSibling() : start.node();
00778 assert(startNode);
00779 setEndingSelection(Selection(Position(startNode, start.offset()), Position(cmd.node()->previousSibling(), cmd.node()->previousSibling()->caretMaxOffset())));
00780 return cmd.node()->previousSibling();
00781 }
00782 return end.node();
00783 }
00784
00785 void ApplyStyleCommandImpl::surroundNodeRangeWithElement(NodeImpl *startNode, NodeImpl *endNode, ElementImpl *element)
00786 {
00787 assert(startNode);
00788 assert(endNode);
00789 assert(element);
00790
00791 NodeImpl *node = startNode;
00792 while (1) {
00793 NodeImpl *next = node->traverseNextNode();
00794 if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
00795 removeNode(node);
00796 appendNode(element, node);
00797 }
00798 if (node == endNode)
00799 break;
00800 node = next;
00801 }
00802 }
00803
00804 void ApplyStyleCommandImpl::applyStyleIfNeeded(DOM::NodeImpl *startNode, DOM::NodeImpl *endNode)
00805 {
00806 StyleChange styleChange = computeStyleChange(Position(startNode, 0), style());
00807 int exceptionCode = 0;
00808
00809 if (styleChange.cssStyle.length() > 0) {
00810 ElementImpl *styleElement = document()->createHTMLElement("SPAN");
00811 assert(exceptionCode == 0);
00812 styleElement->setAttribute(ATTR_STYLE, styleChange.cssStyle);
00813 styleElement->setAttribute(ATTR_CLASS, styleSpanClassString());
00814 insertNodeBefore(styleElement, startNode);
00815 surroundNodeRangeWithElement(startNode, endNode, styleElement);
00816 }
00817
00818 if (styleChange.applyBold) {
00819 ElementImpl *boldElement = document()->createHTMLElement("B");
00820 assert(exceptionCode == 0);
00821 insertNodeBefore(boldElement, startNode);
00822 surroundNodeRangeWithElement(startNode, endNode, boldElement);
00823 }
00824
00825 if (styleChange.applyItalic) {
00826 ElementImpl *italicElement = document()->createHTMLElement("I");
00827 assert(exceptionCode == 0);
00828 insertNodeBefore(italicElement, startNode);
00829 surroundNodeRangeWithElement(startNode, endNode, italicElement);
00830 }
00831 }
00832
00833 bool ApplyStyleCommandImpl::currentlyHasStyle(const Position &pos, const CSSProperty *property) const
00834 {
00835 assert(pos.notEmpty());
00836 CSSStyleDeclarationImpl *decl = document()->defaultView()->getComputedStyle(pos.element(), 0);
00837 assert(decl);
00838 CSSValueImpl *value = decl->getPropertyCSSValue(property->id());
00839 return strcasecmp(value->cssText(), property->value()->cssText()) == 0;
00840 }
00841
00842 ApplyStyleCommandImpl::StyleChange ApplyStyleCommandImpl::computeStyleChange(const Position &insertionPoint, CSSStyleDeclarationImpl *style)
00843 {
00844 assert(insertionPoint.notEmpty());
00845 assert(style);
00846
00847 StyleChange styleChange;
00848
00849 QListIterator<CSSProperty*> it(*(style->values()));
00850 while ( it.hasNext() ) {
00851 CSSProperty *property = it.next();
00852 if (!currentlyHasStyle(insertionPoint, property)) {
00853 switch (property->id()) {
00854 case CSS_PROP_FONT_WEIGHT:
00855 if (strcasecmp(property->value()->cssText(), "bold") == 0)
00856 styleChange.applyBold = true;
00857 else
00858 styleChange.cssStyle += property->cssText();
00859 break;
00860 case CSS_PROP_FONT_STYLE: {
00861 DOMString cssText(property->value()->cssText());
00862 if (strcasecmp(cssText, "italic") == 0 || strcasecmp(cssText, "oblique") == 0)
00863 styleChange.applyItalic = true;
00864 else
00865 styleChange.cssStyle += property->cssText();
00866 }
00867 break;
00868 default:
00869 styleChange.cssStyle += property->cssText();
00870 break;
00871 }
00872 }
00873 }
00874 return styleChange;
00875 }
00876
00877 Position ApplyStyleCommandImpl::positionInsertionPoint(Position pos)
00878 {
00879 if (pos.node()->isTextNode() && (pos.offset() > 0 && pos.offset() < pos.node()->maxOffset())) {
00880 SplitTextNodeCommand split(document(), static_cast<TextImpl *>(pos.node()), pos.offset());
00881 split.apply();
00882 pos = Position(split.node(), 0);
00883 }
00884
00885 #if 0
00886
00887
00888
00889
00890
00891 if (currentlyHasStyle(pos))
00892 return pos;
00893
00894
00895 if (pos.offset() >= pos.node()->caretMaxOffset()) {
00896 NodeImpl *nextNode = pos.node()->traverseNextNode();
00897 if (nextNode) {
00898 Position next = Position(nextNode, 0);
00899 if (currentlyHasStyle(next))
00900 return next;
00901 }
00902 }
00903
00904
00905 if (pos.offset() <= pos.node()->caretMinOffset()) {
00906 NodeImpl *prevNode = pos.node()->traversePreviousNode();
00907 if (prevNode) {
00908 Position prev = Position(prevNode, prevNode->maxOffset());
00909 if (currentlyHasStyle(prev))
00910 return prev;
00911 }
00912 }
00913 #endif
00914
00915 return pos;
00916 }
00917
00918
00919
00920
00921 DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl(DocumentImpl *document)
00922 : CompositeEditCommandImpl(document), m_charactersDeleted(0), m_hasSelectionToCollapse(false)
00923 {
00924 }
00925
00926 DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl(DocumentImpl *document, const Selection &selection)
00927 : CompositeEditCommandImpl(document), m_charactersDeleted(0), m_selectionToCollapse(selection), m_hasSelectionToCollapse(true)
00928 {
00929 }
00930
00931 DeleteCollapsibleWhitespaceCommandImpl::~DeleteCollapsibleWhitespaceCommandImpl()
00932 {
00933 }
00934
00935 int DeleteCollapsibleWhitespaceCommandImpl::commandID() const
00936 {
00937 return DeleteCollapsibleWhitespaceCommandID;
00938 }
00939
00940 static bool shouldDeleteUpstreamPosition(const Position &pos)
00941 {
00942 if (!pos.node()->isTextNode())
00943 return false;
00944
00945 RenderObject *renderer = pos.node()->renderer();
00946 if (!renderer)
00947 return true;
00948
00949 TextImpl *textNode = static_cast<TextImpl *>(pos.node());
00950 if (pos.offset() >= (long)textNode->length())
00951 return false;
00952
00953 if (pos.isLastRenderedPositionInEditableBlock())
00954 return false;
00955
00956 if (pos.isFirstRenderedPositionOnLine() || pos.isLastRenderedPositionOnLine())
00957 return false;
00958
00959 RenderText *textRenderer = static_cast<RenderText *>(renderer);
00960 for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
00961 if (pos.offset() < box->m_start) {
00962 return true;
00963 }
00964 if (pos.offset() >= box->m_start && pos.offset() < box->m_start + box->m_len)
00965 return false;
00966 }
00967
00968 return true;
00969 }
00970
00971 Position DeleteCollapsibleWhitespaceCommandImpl::deleteWhitespace(const Position &pos)
00972 {
00973 Position upstream = pos.equivalentUpstreamPosition();
00974 Position downstream = pos.equivalentDownstreamPosition();
00975
00976 bool del = shouldDeleteUpstreamPosition(upstream);
00977
00978 kDebug(6200) << "pos:" << getTagName(pos.node()->id()) << "["<< pos.node() << ":" << pos.offset() << "]";
00979 if (upstream == downstream) {
00980 kDebug(6200) << "same:" << getTagName(upstream.node()->id()) << "["<< upstream.node()<< ":" << upstream.offset()<< "]";
00981 }
00982 else {
00983 kDebug(6200) << "upstream:" << ( del ? "DELETE" : "SKIP") << getTagName(upstream.node()->id())<< "["<< upstream.node() << ":" << upstream.offset()<< "]";
00984 PositionIterator it(upstream);
00985 for (it.next(); it.current() != downstream; it.next()) {
00986 if (it.current().node()->isTextNode() && (long)static_cast<TextImpl *>(it.current().node())->length() == it.current().offset())
00987 kDebug(6200) << " node: AT END"<< getTagName(it.current().node()->id())<< "["<< it.current().node()<< ":" << it.current().offset()<< "]";
00988 else
00989 kDebug(6200) << " node: DELETE"<< getTagName(it.current().node()->id())<< "["<< it.current().node()<< ":" << it.current().offset()<< "]";
00990 }
00991 kDebug(6200) << "downstream:" << getTagName(downstream.node()->id()) << "["<< downstream.node() << ":" << downstream.offset()<< "]";
00992 }
00993
00994 if (upstream == downstream)
00995 return upstream;
00996
00997 PositionIterator it(upstream);
00998 Position deleteStart = upstream;
00999 if (!del) {
01000 deleteStart = it.peekNext();
01001 if (deleteStart == downstream)
01002 return upstream;
01003 }
01004
01005 Position endingPosition = upstream;
01006
01007 while (it.current() != downstream) {
01008
01009 Position next = it.peekNext();
01010 if (next.node() != deleteStart.node()) {
01011 assert(deleteStart.node()->isTextNode());
01012 TextImpl *textNode = static_cast<TextImpl *>(deleteStart.node());
01013 unsigned long count = it.current().offset() - deleteStart.offset();
01014 if (count == textNode->length()) {
01015 kDebug(6200) << " removeNodeAndPrune 1:" << textNode;
01016 if (textNode == endingPosition.node())
01017 endingPosition = Position(next.node(), next.node()->caretMinOffset());
01018 removeNodeAndPrune(textNode);
01019 }
01020 else {
01021 kDebug(6200) << " deleteText 1:" << textNode << "t len:" << textNode->length()<<"start:" << deleteStart.offset() << "del len:" << (it.current().offset() - deleteStart.offset());
01022 deleteText(textNode, deleteStart.offset(), count);
01023 }
01024 deleteStart = next;
01025 }
01026 else if (next == downstream) {
01027 assert(deleteStart.node() == downstream.node());
01028 assert(downstream.node()->isTextNode());
01029 TextImpl *textNode = static_cast<TextImpl *>(deleteStart.node());
01030 unsigned long count = downstream.offset() - deleteStart.offset();
01031 assert(count <= textNode->length());
01032 if (count == textNode->length()) {
01033 kDebug(6200) << " removeNodeAndPrune 2:"<<textNode;
01034 removeNodeAndPrune(textNode);
01035 }
01036 else {
01037 kDebug(6200) << " deleteText 2:"<< textNode<< "t len:" << textNode->length() <<"start:" <<deleteStart.offset() << "del len:" << count;
01038 deleteText(textNode, deleteStart.offset(), count);
01039 m_charactersDeleted = count;
01040 endingPosition = Position(downstream.node(), downstream.offset() - m_charactersDeleted);
01041 }
01042 }
01043
01044 it.setPosition(next);
01045 }
01046
01047 return endingPosition;
01048 }
01049
01050 void DeleteCollapsibleWhitespaceCommandImpl::doApply()
01051 {
01052
01053
01054 if (!m_hasSelectionToCollapse)
01055 m_selectionToCollapse = endingSelection();
01056 int state = m_selectionToCollapse.state();
01057 if (state == Selection::CARET) {
01058 Position endPosition = deleteWhitespace(m_selectionToCollapse.start());
01059 setEndingSelection(endPosition);
01060 kDebug(6200) << "-----------------------------------------------------";
01061 }
01062 else if (state == Selection::RANGE) {
01063 Position startPosition = deleteWhitespace(m_selectionToCollapse.start());
01064 kDebug(6200) << "-----------------------------------------------------";
01065 Position endPosition = m_selectionToCollapse.end();
01066 if (m_charactersDeleted > 0 && startPosition.node() == endPosition.node()) {
01067 kDebug(6200) << "adjust end position by" << m_charactersDeleted;
01068 endPosition = Position(endPosition.node(), endPosition.offset() - m_charactersDeleted);
01069 }
01070 endPosition = deleteWhitespace(endPosition);
01071 setEndingSelection(Selection(startPosition, endPosition));
01072 kDebug(6200) << "=====================================================";
01073 }
01074 }
01075
01076
01077
01078
01079 DeleteSelectionCommandImpl::DeleteSelectionCommandImpl(DocumentImpl *document)
01080 : CompositeEditCommandImpl(document), m_hasSelectionToDelete(false)
01081 {
01082 }
01083
01084 DeleteSelectionCommandImpl::DeleteSelectionCommandImpl(DocumentImpl *document, const Selection &selection)
01085 : CompositeEditCommandImpl(document), m_selectionToDelete(selection), m_hasSelectionToDelete(true)
01086 {
01087 }
01088
01089 DeleteSelectionCommandImpl::~DeleteSelectionCommandImpl()
01090 {
01091 }
01092
01093 int DeleteSelectionCommandImpl::commandID() const
01094 {
01095 return DeleteSelectionCommandID;
01096 }
01097
01098 void DeleteSelectionCommandImpl::joinTextNodesWithSameStyle()
01099 {
01100 Selection selection = endingSelection();
01101
01102 if (selection.state() != Selection::CARET)
01103 return;
01104
01105 Position pos(selection.start());
01106
01107 if (!pos.node()->isTextNode())
01108 return;
01109
01110 TextImpl *textNode = static_cast<TextImpl *>(pos.node());
01111
01112 if (pos.offset() == 0) {
01113 PositionIterator it(pos);
01114 Position prev = it.previous();
01115 if (prev == pos)
01116 return;
01117 if (prev.node()->isTextNode()) {
01118 TextImpl *prevTextNode = static_cast<TextImpl *>(prev.node());
01119 if (textNodesAreJoinable(prevTextNode, textNode)) {
01120 joinTextNodes(prevTextNode, textNode);
01121 setEndingSelection(Position(textNode, prevTextNode->length()));
01122 kDebug(6200) << "joinTextNodesWithSameStyle [1]";
01123 }
01124 }
01125 }
01126 else if (pos.offset() == (long)textNode->length()) {
01127 PositionIterator it(pos);
01128 Position next = it.next();
01129 if (next == pos)
01130 return;
01131 if (next.node()->isTextNode()) {
01132 TextImpl *nextTextNode = static_cast<TextImpl *>(next.node());
01133 if (textNodesAreJoinable(textNode, nextTextNode)) {
01134 joinTextNodes(textNode, nextTextNode);
01135 setEndingSelection(Position(nextTextNode, pos.offset()));
01136 kDebug(6200) << "joinTextNodesWithSameStyle [2]";
01137 }
01138 }
01139 }
01140 }
01141
01142 bool DeleteSelectionCommandImpl::containsOnlyWhitespace(const Position &start, const Position &end)
01143 {
01144
01145
01146 PositionIterator it(start);
01147 while (!it.atEnd()) {
01148 if (!it.current().node()->isTextNode())
01149 return false;
01150 const DOMString &text = static_cast<TextImpl *>(it.current().node())->data();
01151
01152 if (text.length() > INT_MAX)
01153 return false;
01154 if (it.current().offset() < (int)text.length() && !isWS(text[it.current().offset()]))
01155 return false;
01156 it.next();
01157 if (it.current() == end)
01158 break;
01159 }
01160 return true;
01161 }
01162
01163 void DeleteSelectionCommandImpl::doApply()
01164 {
01165
01166
01167 if (!m_hasSelectionToDelete)
01168 m_selectionToDelete = endingSelection();
01169
01170 if (m_selectionToDelete.state() != Selection::RANGE)
01171 return;
01172
01173 deleteCollapsibleWhitespace(m_selectionToDelete);
01174 Selection selection = endingSelection();
01175
01176 Position upstreamStart(selection.start().equivalentUpstreamPosition());
01177 Position downstreamStart(selection.start().equivalentDownstreamPosition());
01178 Position upstreamEnd(selection.end().equivalentUpstreamPosition());
01179 Position downstreamEnd(selection.end().equivalentDownstreamPosition());
01180
01181 if (upstreamStart == downstreamEnd)
01182
01183 return;
01184
01185 Position endingPosition;
01186 bool adjustEndingPositionDownstream = false;
01187
01188 bool onlyWhitespace = containsOnlyWhitespace(upstreamStart, downstreamEnd);
01189
01190 bool startCompletelySelected = !onlyWhitespace &&
01191 (downstreamStart.offset() <= downstreamStart.node()->caretMinOffset() &&
01192 ((downstreamStart.node() != upstreamEnd.node()) ||
01193 (upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset())));
01194
01195 bool endCompletelySelected = !onlyWhitespace &&
01196 (upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset() &&
01197 ((downstreamStart.node() != upstreamEnd.node()) ||
01198 (downstreamStart.offset() <= downstreamStart.node()->caretMinOffset())));
01199
01200 unsigned long startRenderedOffset = downstreamStart.renderedOffset();
01201
01202 bool startAtStartOfRootEditableElement = startRenderedOffset == 0 && downstreamStart.inFirstEditableInRootEditableElement();
01203 bool startAtStartOfBlock = startAtStartOfRootEditableElement ||
01204 (startRenderedOffset == 0 && downstreamStart.inFirstEditableInContainingEditableBlock());
01205 bool endAtEndOfBlock = downstreamEnd.isLastRenderedPositionInEditableBlock();
01206
01207 NodeImpl *startBlock = upstreamStart.node()->enclosingBlockFlowElement();
01208 NodeImpl *endBlock = downstreamEnd.node()->enclosingBlockFlowElement();
01209 bool startBlockEndBlockAreSiblings = startBlock->parentNode() == endBlock->parentNode();
01210
01211 debugPosition("upstreamStart: ", upstreamStart);
01212 debugPosition("downstreamStart: ", downstreamStart);
01213 debugPosition("upstreamEnd: ", upstreamEnd);
01214 debugPosition("downstreamEnd: ", downstreamEnd);
01215 kDebug(6200) << "start selected:" << (startCompletelySelected ? "YES" : "NO");
01216 kDebug(6200) << "at start block:" << (startAtStartOfBlock ? "YES" : "NO");
01217 kDebug(6200) << "at start root block:"<< (startAtStartOfRootEditableElement ? "YES" : "NO");
01218 kDebug(6200) << "at end block:"<< (endAtEndOfBlock ? "YES" : "NO");
01219 kDebug(6200) << "only whitespace:"<< (onlyWhitespace ? "YES" : "NO");
01220
01221
01222 if (startAtStartOfBlock) {
01223 kDebug(6200) << "ending position case 1";
01224 endingPosition = Position(startBlock, 0);
01225 adjustEndingPositionDownstream = true;
01226 }
01227 else if (!startCompletelySelected) {
01228 kDebug(6200) << "ending position case 2";
01229 endingPosition = upstreamStart;
01230 if (upstreamStart.node()->id() == ID_BR && upstreamStart.offset() == 1)
01231 adjustEndingPositionDownstream = true;
01232 }
01233 else if (upstreamStart != downstreamStart) {
01234 kDebug(6200) << "ending position case 3";
01235 endingPosition = upstreamStart;
01236 if (upstreamStart.node()->id() == ID_BR && upstreamStart.offset() == 1)
01237 adjustEndingPositionDownstream = true;
01238 }
01239
01240
01241
01242
01243 if ((startAtStartOfBlock && !endAtEndOfBlock) || (!startCompletelySelected && adjustEndingPositionDownstream)) {
01244
01245 Position trailing = trailingWhitespacePosition(downstreamEnd.equivalentDownstreamPosition());
01246 if (trailing.notEmpty()) {
01247 debugPosition("convertTrailingWhitespace: ", trailing);
01248 Position collapse = trailing.nextCharacterPosition();
01249 if (collapse != trailing)
01250 deleteCollapsibleWhitespace(collapse);
01251 TextImpl *textNode = static_cast<TextImpl *>(trailing.node());
01252 replaceText(textNode, trailing.offset(), 1, nonBreakingSpaceString());
01253 }
01254 }
01255 else if (!startAtStartOfBlock && endAtEndOfBlock) {
01256
01257 Position leading = leadingWhitespacePosition(upstreamStart.equivalentUpstreamPosition());
01258 if (leading.notEmpty()) {
01259 debugPosition("convertLeadingWhitespace: ", leading);
01260 TextImpl *textNode = static_cast<TextImpl *>(leading.node());
01261 replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
01262 }
01263 }
01264 else if (!startAtStartOfBlock && !endAtEndOfBlock) {
01265
01266 Position leading = leadingWhitespacePosition(upstreamStart.equivalentUpstreamPosition());
01267 Position trailing = trailingWhitespacePosition(downstreamEnd.equivalentDownstreamPosition());
01268 if (leading.notEmpty() && trailing.notEmpty()) {
01269 debugPosition("convertLeadingWhitespace [contiguous]: ", leading);
01270 TextImpl *textNode = static_cast<TextImpl *>(leading.node());
01271 replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
01272 }
01273 }
01274
01275
01276
01277
01278 NodeImpl *n = downstreamStart.node()->traverseNextNode();
01279
01280
01281 if (startCompletelySelected) {
01282 kDebug(6200) << "start node delete case 1";
01283 removeNodeAndPrune(downstreamStart.node(), startBlock);
01284 }
01285 else if (onlyWhitespace) {
01286
01287
01288
01289 kDebug(6200) << "start node delete case 2";
01290 assert(upstreamStart.node()->isTextNode());
01291 TextImpl *text = static_cast<TextImpl *>(upstreamStart.node());
01292 int offset = upstreamStart.offset();
01293
01294 int length = text->length();
01295 if (length == upstreamStart.offset())
01296 offset--;
01297 deleteText(text, offset, 1);
01298 }
01299 else if (downstreamStart.node()->isTextNode()) {
01300 kDebug(6200) << "start node delete case 3";
01301 TextImpl *text = static_cast<TextImpl *>(downstreamStart.node());
01302 int endOffset = text == upstreamEnd.node() ? upstreamEnd.offset() : text->length();
01303 if (endOffset > downstreamStart.offset()) {
01304 deleteText(text, downstreamStart.offset(), endOffset - downstreamStart.offset());
01305 }
01306 }
01307 else {
01308
01309
01310 kDebug(6200) << "start node delete case 4";
01311 assert(downstreamStart.offset() == 1);
01312 }
01313
01314 if (!onlyWhitespace && downstreamStart.node() != upstreamEnd.node()) {
01315
01316 while (n != upstreamEnd.node()) {
01317 NodeImpl *d = n;
01318 n = n->traverseNextNode();
01319 if (d->renderer() && d->renderer()->isEditable())
01320 removeNodeAndPrune(d, startBlock);
01321 }
01322
01323
01324 assert(n == upstreamEnd.node());
01325 if (endCompletelySelected) {
01326 removeNodeAndPrune(upstreamEnd.node(), startBlock);
01327 }
01328 else if (upstreamEnd.node()->isTextNode()) {
01329 if (upstreamEnd.offset() > 0) {
01330 TextImpl *text = static_cast<TextImpl *>(upstreamEnd.node());
01331 deleteText(text, 0, upstreamEnd.offset());
01332 }
01333 }
01334 else {
01335
01336
01337 assert(downstreamStart.offset() == 0);
01338 }
01339 }
01340
01341
01342
01343
01344
01345
01346 if (startBlock != endBlock && startBlockEndBlockAreSiblings) {
01347 kDebug(6200) << "merging content to start block";
01348 NodeImpl *node = endBlock->firstChild();
01349 while (node) {
01350 NodeImpl *moveNode = node;
01351 node = node->nextSibling();
01352 removeNode(moveNode);
01353 appendNode(startBlock, moveNode);
01354 }
01355 }
01356
01357 if (adjustEndingPositionDownstream) {
01358 kDebug(6200) << "adjust ending position downstream";
01359 endingPosition = endingPosition.equivalentDownstreamPosition();
01360 }
01361
01362 debugPosition("ending position: ", endingPosition);
01363 setEndingSelection(endingPosition);
01364
01365 kDebug(6200) << "-----------------------------------------------------";
01366 }
01367
01368
01369
01370
01371 DeleteTextCommandImpl::DeleteTextCommandImpl(DocumentImpl *document, TextImpl *node, long offset, long count)
01372 : EditCommandImpl(document), m_node(node), m_offset(offset), m_count(count)
01373 {
01374 assert(m_node);
01375 assert(m_offset >= 0);
01376 assert(m_count >= 0);
01377
01378 m_node->ref();
01379 }
01380
01381 DeleteTextCommandImpl::~DeleteTextCommandImpl()
01382 {
01383 if (m_node)
01384 m_node->deref();
01385 }
01386
01387 int DeleteTextCommandImpl::commandID() const
01388 {
01389 return DeleteTextCommandID;
01390 }
01391
01392 void DeleteTextCommandImpl::doApply()
01393 {
01394 assert(m_node);
01395
01396 int exceptionCode = 0;
01397 m_text = m_node->substringData(m_offset, m_count, exceptionCode);
01398 assert(exceptionCode == 0);
01399
01400 m_node->deleteData(m_offset, m_count, exceptionCode);
01401 assert(exceptionCode == 0);
01402 }
01403
01404 void DeleteTextCommandImpl::doUnapply()
01405 {
01406 assert(m_node);
01407 assert(!m_text.isEmpty());
01408
01409 int exceptionCode = 0;
01410 m_node->insertData(m_offset, m_text, exceptionCode);
01411 assert(exceptionCode == 0);
01412 }
01413
01414
01415
01416
01417 InputNewlineCommandImpl::InputNewlineCommandImpl(DocumentImpl *document)
01418 : CompositeEditCommandImpl(document)
01419 {
01420 }
01421
01422 InputNewlineCommandImpl::~InputNewlineCommandImpl()
01423 {
01424 }
01425
01426 int InputNewlineCommandImpl::commandID() const
01427 {
01428 return InputNewlineCommandID;
01429 }
01430
01431 void InputNewlineCommandImpl::insertNodeAfterPosition(NodeImpl *node, const Position &pos)
01432 {
01433
01434
01435
01436 Position upstream(pos.equivalentUpstreamPosition());
01437 NodeImpl *cb = pos.node()->enclosingBlockFlowElement();
01438 if (cb == pos.node())
01439 appendNode(cb, node);
01440 else
01441 insertNodeAfter(node, pos.node());
01442 }
01443
01444 void InputNewlineCommandImpl::insertNodeBeforePosition(NodeImpl *node, const Position &pos)
01445 {
01446
01447
01448
01449 Position upstream(pos.equivalentUpstreamPosition());
01450 NodeImpl *cb = pos.node()->enclosingBlockFlowElement();
01451 if (cb == pos.node())
01452 appendNode(cb, node);
01453 else
01454 insertNodeBefore(node, pos.node());
01455 }
01456
01457 void InputNewlineCommandImpl::doApply()
01458 {
01459 deleteSelection();
01460 Selection selection = endingSelection();
01461
01462 int exceptionCode = 0;
01463 ElementImpl *breakNode = document()->createHTMLElement("BR");
01464
01465
01466 NodeImpl *nodeToInsert = breakNode;
01467
01468
01469 if (document()->part()->editor()->typingStyle()) {
01470 int exceptionCode = 0;
01471 ElementImpl *styleElement = createTypingStyleElement();
01472 styleElement->appendChild(breakNode, exceptionCode);
01473 assert(exceptionCode == 0);
01474 nodeToInsert = styleElement;
01475 }
01476
01477 Position pos(selection.start().equivalentDownstreamPosition());
01478 bool atStart = pos.offset() <= pos.node()->caretMinOffset();
01479 bool atEndOfBlock = pos.isLastRenderedPositionInEditableBlock();
01480
01481 if (atEndOfBlock) {
01482 kDebug(6200) << "input newline case 1";
01483
01484
01485
01486 insertNodeAfterPosition(nodeToInsert, pos);
01487 exceptionCode = 0;
01488 ElementImpl *extraBreakNode = document()->createHTMLElement("BR");
01489
01490 insertNodeAfter(extraBreakNode, nodeToInsert);
01491 setEndingSelection(Position(extraBreakNode, 0));
01492 }
01493 else if (atStart) {
01494 kDebug(6200) << "input newline case 2";
01495
01496
01497
01498 insertNodeBeforePosition(nodeToInsert, pos);
01499 setEndingSelection(Position(pos.node(), 0));
01500 }
01501 else {
01502
01503 kDebug(6200) << "input newline case 3";
01504 assert(pos.node()->isTextNode());
01505 TextImpl *textNode = static_cast<TextImpl *>(pos.node());
01506 TextImpl *textBeforeNode = document()->createTextNode(textNode->substringData(0, selection.start().offset(), exceptionCode));
01507 deleteText(textNode, 0, selection.start().offset());
01508 insertNodeBefore(textBeforeNode, textNode);
01509 insertNodeBefore(nodeToInsert, textNode);
01510 setEndingSelection(Position(textNode, 0));
01511 }
01512 }
01513
01514
01515
01516
01517 InputTextCommandImpl::InputTextCommandImpl(DocumentImpl *document)
01518 : CompositeEditCommandImpl(document), m_charactersAdded(0)
01519 {
01520 }
01521
01522 InputTextCommandImpl::~InputTextCommandImpl()
01523 {
01524 }
01525
01526 int InputTextCommandImpl::commandID() const
01527 {
01528 return InputTextCommandID;
01529 }
01530
01531 void InputTextCommandImpl::doApply()
01532 {
01533 }
01534
01535 void InputTextCommandImpl::input(const DOMString &text)
01536 {
01537 execute(text);
01538 }
01539
01540 void InputTextCommandImpl::deleteCharacter()
01541 {
01542 assert(state() == Applied);
01543
01544 Selection selection = endingSelection();
01545
01546 if (!selection.start().node()->isTextNode())
01547 return;
01548
01549 int exceptionCode = 0;
01550 int offset = selection.start().offset() - 1;
01551 if (offset >= selection.start().node()->caretMinOffset()) {
01552 TextImpl *textNode = static_cast<TextImpl *>(selection.start().node());
01553 textNode->deleteData(offset, 1, exceptionCode);
01554 assert(exceptionCode == 0);
01555 selection = Selection(Position(textNode, offset));
01556 setEndingSelection(selection);
01557 m_charactersAdded--;
01558 }
01559 }
01560
01561 Position InputTextCommandImpl::prepareForTextInsertion(bool adjustDownstream)
01562 {
01563
01564
01565 Selection selection = endingSelection();
01566 assert(selection.state() == Selection::CARET);
01567
01568 Position pos = selection.start();
01569 if (adjustDownstream)
01570 pos = pos.equivalentDownstreamPosition();
01571 else
01572 pos = pos.equivalentUpstreamPosition();
01573
01574 if (!pos.node()->isTextNode()) {
01575 NodeImpl *textNode = document()->createEditingTextNode("");
01576 NodeImpl *nodeToInsert = textNode;
01577 if (document()->part()->editor()->typingStyle()) {
01578 int exceptionCode = 0;
01579 ElementImpl *styleElement = createTypingStyleElement();
01580 styleElement->appendChild(textNode, exceptionCode);
01581 assert(exceptionCode == 0);
01582 nodeToInsert = styleElement;
01583 }
01584
01585
01586 if (pos.node()->isEditableBlock()) {
01587 kDebug(6200) << "prepareForTextInsertion case 1";
01588 appendNode(pos.node(), nodeToInsert);
01589 }
01590 else if (pos.node()->id() == ID_BR && pos.offset() == 1) {
01591 kDebug(6200) << "prepareForTextInsertion case 2";
01592 insertNodeAfter(nodeToInsert, pos.node());
01593 }
01594 else if (pos.node()->caretMinOffset() == pos.offset()) {
01595 kDebug(6200) << "prepareForTextInsertion case 3";
01596 insertNodeBefore(nodeToInsert, pos.node());
01597 }
01598 else if (pos.node()->caretMaxOffset() == pos.offset()) {
01599 kDebug(6200) << "prepareForTextInsertion case 4";
01600 insertNodeAfter(nodeToInsert, pos.node());
01601 }
01602 else
01603 assert(false);
01604
01605 pos = Position(textNode, 0);
01606 }
01607 else {
01608
01609 if (document()->part()->editor()->typingStyle()) {
01610 if (pos.node()->isTextNode() && pos.offset() > pos.node()->caretMinOffset() && pos.offset() < pos.node()->caretMaxOffset()) {
01611
01612 TextImpl *text = static_cast<TextImpl *>(pos.node());
01613 SplitTextNodeCommand cmd(document(), text, pos.offset());
01614 applyCommandToComposite(cmd);
01615 setEndingSelection(Position(cmd.node(), 0));
01616 }
01617
01618 int exceptionCode = 0;
01619 TextImpl *editingTextNode = document()->createEditingTextNode("");
01620
01621 ElementImpl *styleElement = createTypingStyleElement();
01622 styleElement->appendChild(editingTextNode, exceptionCode);
01623 assert(exceptionCode == 0);
01624
01625 NodeImpl *node = endingSelection().start().node();
01626 if (endingSelection().start().isLastRenderedPositionOnLine())
01627 insertNodeAfter(styleElement, node);
01628 else
01629 insertNodeBefore(styleElement, node);
01630 pos = Position(editingTextNode, 0);
01631 }
01632 }
01633 return pos;
01634 }
01635
01636 void InputTextCommandImpl::execute(const DOMString &text)
01637 {
01638 Selection selection = endingSelection();
01639 bool adjustDownstream = selection.start().isFirstRenderedPositionOnLine();
01640
01641
01642 if (selection.state() == Selection::RANGE)
01643 deleteSelection();
01644 else
01645 deleteCollapsibleWhitespace();
01646
01647
01648
01649
01650 Position pos = prepareForTextInsertion(adjustDownstream);
01651
01652 TextImpl *textNode = static_cast<TextImpl *>(pos.node());
01653 long offset = pos.offset();
01654
01655
01656
01657
01658 if (isWS(text))
01659 insertSpace(textNode, offset);
01660 else {
01661 const DOMString &existingText = textNode->data();
01662 if (textNode->length() >= 2 && offset >= 2 && isNBSP(existingText[offset - 1]) && !isWS(existingText[offset - 2])) {
01663
01664
01665
01666
01667
01668
01669 replaceText(textNode, offset - 1, 1, " ");
01670 }
01671 insertText(textNode, offset, text);
01672 }
01673 setEndingSelection(Position(textNode, offset + text.length()));
01674 m_charactersAdded += text.length();
01675 }
01676
01677 void InputTextCommandImpl::insertSpace(TextImpl *textNode, unsigned long offset)
01678 {
01679 assert(textNode);
01680
01681 DOMString text(textNode->data());
01682
01683
01684
01685
01686 int count = 0;
01687 for (unsigned int i = offset; i < text.length(); i++) {
01688 if (isWS(text[i]))
01689 count++;
01690 else
01691 break;
01692 }
01693 if (count > 0) {
01694
01695
01696 Position pos(textNode, offset);
01697 Position downstream = pos.equivalentDownstreamPosition();
01698 if (downstream.offset() < (long)text.length() && isWS(text[downstream.offset()]))
01699 count--;
01700 if (count > 0)
01701 deleteText(textNode, offset, count);
01702 }
01703
01704 if (offset > 0 && offset <= text.length() - 1 && !isWS(text[offset]) && !isWS(text[offset - 1])) {
01705
01706 insertText(textNode, offset, " ");
01707 return;
01708 }
01709
01710 if (text.length() >= 2 && offset >= 2 && isNBSP(text[offset - 2]) && isNBSP(text[offset - 1])) {
01711
01712
01713
01714 insertText(textNode, offset - 1, " ");
01715 return;
01716 }
01717
01718
01719 insertText(textNode, offset, nonBreakingSpaceString());
01720 }
01721
01722
01723
01724
01725 InsertNodeBeforeCommandImpl::InsertNodeBeforeCommandImpl(DocumentImpl *document, NodeImpl *insertChild, NodeImpl *refChild)
01726 : EditCommandImpl(document), m_insertChild(insertChild), m_refChild(refChild)
01727 {
01728 assert(m_insertChild);
01729 m_insertChild->ref();
01730
01731 assert(m_refChild);
01732 m_refChild->ref();
01733 }
01734
01735 InsertNodeBeforeCommandImpl::~InsertNodeBeforeCommandImpl()
01736 {
01737 if (m_insertChild)
01738 m_insertChild->deref();
01739 if (m_refChild)
01740 m_refChild->deref();
01741 }
01742
01743 int InsertNodeBeforeCommandImpl::commandID() const
01744 {
01745 return InsertNodeBeforeCommandID;
01746 }
01747
01748 void InsertNodeBeforeCommandImpl::doApply()
01749 {
01750 assert(m_insertChild);
01751 assert(m_refChild);
01752 assert(m_refChild->parentNode());
01753
01754 int exceptionCode = 0;
01755 m_refChild->parentNode()->insertBefore(m_insertChild, m_refChild, exceptionCode);
01756 assert(exceptionCode == 0);
01757 }
01758
01759 void InsertNodeBeforeCommandImpl::doUnapply()
01760 {
01761 assert(m_insertChild);
01762 assert(m_refChild);
01763 assert(m_refChild->parentNode());
01764
01765 int exceptionCode = 0;
01766 m_refChild->parentNode()->removeChild(m_insertChild, exceptionCode);
01767 assert(exceptionCode == 0);
01768 }
01769
01770
01771
01772
01773 InsertTextCommandImpl::InsertTextCommandImpl(DocumentImpl *document, TextImpl *node, long offset, const DOMString &text)
01774 : EditCommandImpl(document), m_node(node), m_offset(offset)
01775 {
01776 assert(m_node);
01777 assert(m_offset >= 0);
01778 assert(text.length() > 0);
01779
01780 m_node->ref();
01781 m_text = text.copy();
01782 }
01783
01784 InsertTextCommandImpl::~InsertTextCommandImpl()
01785 {
01786 if (m_node)
01787 m_node->deref();
01788 }
01789
01790 int InsertTextCommandImpl::commandID() const
01791 {
01792 return InsertTextCommandID;
01793 }
01794
01795 void InsertTextCommandImpl::doApply()
01796 {
01797 assert(m_node);
01798 assert(!m_text.isEmpty());
01799
01800 int exceptionCode = 0;
01801 m_node->insertData(m_offset, m_text, exceptionCode);
01802 assert(exceptionCode == 0);
01803 }
01804
01805 void InsertTextCommandImpl::doUnapply()
01806 {
01807 assert(m_node);
01808 assert(!m_text.isEmpty());
01809
01810 int exceptionCode = 0;
01811 m_node->deleteData(m_offset, m_text.length(), exceptionCode);
01812 assert(exceptionCode == 0);
01813 }
01814
01815
01816
01817
01818 JoinTextNodesCommandImpl::JoinTextNodesCommandImpl(DocumentImpl *document, TextImpl *text1, TextImpl *text2)
01819 : EditCommandImpl(document), m_text1(text1), m_text2(text2)
01820 {
01821 assert(m_text1);
01822 assert(m_text2);
01823 assert(m_text1->nextSibling() == m_text2);
01824 assert(m_text1->length() > 0);
01825 assert(m_text2->length() > 0);
01826
01827 m_text1->ref();
01828 m_text2->ref();
01829 }
01830
01831 JoinTextNodesCommandImpl::~JoinTextNodesCommandImpl()
01832 {
01833 if (m_text1)
01834 m_text1->deref();
01835 if (m_text2)
01836 m_text2->deref();
01837 }
01838
01839 int JoinTextNodesCommandImpl::commandID() const
01840 {
01841 return JoinTextNodesCommandID;
01842 }
01843
01844 void JoinTextNodesCommandImpl::doApply()
01845 {
01846 assert(m_text1);
01847 assert(m_text2);
01848 assert(m_text1->nextSibling() == m_text2);
01849
01850 int exceptionCode = 0;
01851 m_text2->insertData(0, m_text1->data(), exceptionCode);
01852 assert(exceptionCode == 0);
01853
01854 m_text2->parentNode()->removeChild(m_text1, exceptionCode);
01855 assert(exceptionCode == 0);
01856
01857 m_offset = m_text1->length();
01858 }
01859
01860 void JoinTextNodesCommandImpl::doUnapply()
01861 {
01862 assert(m_text2);
01863 assert(m_offset > 0);
01864
01865 int exceptionCode = 0;
01866
01867 m_text2->deleteData(0, m_offset, exceptionCode);
01868 assert(exceptionCode == 0);
01869
01870 m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
01871 assert(exceptionCode == 0);
01872
01873 assert(m_text2->previousSibling()->isTextNode());
01874 assert(m_text2->previousSibling() == m_text1);
01875 }
01876
01877
01878
01879
01880 ReplaceSelectionCommandImpl::ReplaceSelectionCommandImpl(DocumentImpl *document, DOM::DocumentFragmentImpl *fragment, bool selectReplacement)
01881 : CompositeEditCommandImpl(document), m_fragment(fragment), m_selectReplacement(selectReplacement)
01882 {
01883 }
01884
01885 ReplaceSelectionCommandImpl::~ReplaceSelectionCommandImpl()
01886 {
01887 }
01888
01889 int ReplaceSelectionCommandImpl::commandID() const
01890 {
01891 return ReplaceSelectionCommandID;
01892 }
01893
01894 void ReplaceSelectionCommandImpl::doApply()
01895 {
01896 NodeImpl *firstChild = m_fragment->firstChild();
01897 NodeImpl *lastChild = m_fragment->lastChild();
01898
01899 Selection selection = endingSelection();
01900
01901
01902 if (selection.state() == Selection::RANGE)
01903 deleteSelection();
01904 else
01905 deleteCollapsibleWhitespace();
01906
01907 selection = endingSelection();
01908 assert(!selection.isEmpty());
01909
01910 if (!firstChild) {
01911
01912 assert(!lastChild);
01913 } else if (firstChild == lastChild && firstChild->isTextNode()) {
01914
01915 Position base = selection.base();
01916 inputText(static_cast<TextImpl *>(firstChild)->data());
01917 if (m_selectReplacement) {
01918 setEndingSelection(Selection(base, endingSelection().extent()));
01919 }
01920 }
01921 else {
01922
01923 NodeImpl *beforeNode = firstChild;
01924 NodeImpl *node = firstChild->nextSibling();
01925
01926 insertNodeAt(firstChild, selection.start().node(), selection.start().offset());
01927
01928
01929 while (node) {
01930 NodeImpl *next = node->nextSibling();
01931 insertNodeAfter(node, beforeNode);
01932 beforeNode = node;
01933 node = next;
01934 }
01935 assert(beforeNode);
01936
01937
01938 NodeImpl *lastLeaf = lastChild;
01939 while (1) {
01940 NodeImpl *nextChild = lastLeaf->lastChild();
01941 if (!nextChild)
01942 break;
01943 lastLeaf = nextChild;
01944 }
01945
01946 if (m_selectReplacement) {
01947
01948 NodeImpl *firstLeaf = firstChild;
01949 while (1) {
01950 NodeImpl *nextChild = firstLeaf->firstChild();
01951 if (!nextChild)
01952 break;
01953 firstLeaf = nextChild;
01954 }
01955
01956 setEndingSelection(Selection(Position(firstLeaf, firstLeaf->caretMinOffset()), Position(lastLeaf, lastLeaf->caretMaxOffset())));
01957 } else {
01958
01959 setEndingSelection(Position(lastLeaf, lastLeaf->caretMaxOffset()));
01960 }
01961 }
01962 }
01963
01964
01965
01966
01967 MoveSelectionCommandImpl::MoveSelectionCommandImpl(DocumentImpl *document, DOM::DocumentFragmentImpl *fragment, DOM::Position &position)
01968 : CompositeEditCommandImpl(document), m_fragment(fragment), m_position(position)
01969 {
01970 }
01971
01972 MoveSelectionCommandImpl::~MoveSelectionCommandImpl()
01973 {
01974 }
01975
01976 int MoveSelectionCommandImpl::commandID() const
01977 {
01978 return MoveSelectionCommandID;
01979 }
01980
01981 void MoveSelectionCommandImpl::doApply()
01982 {
01983 Selection selection = endingSelection();
01984 assert(selection.state() == Selection::RANGE);
01985
01986
01987 NodeImpl *positionNode = m_position.node();
01988 long positionOffset = m_position.offset();
01989 Position selectionEnd = selection.end();
01990 long selectionEndOffset = selectionEnd.offset();
01991 if (selectionEnd.node() == positionNode && selectionEndOffset < positionOffset) {
01992 positionOffset -= selectionEndOffset;
01993 Position selectionStart = selection.start();
01994 if (selectionStart.node() == positionNode) {
01995 positionOffset += selectionStart.offset();
01996 }
01997 }
01998
01999 deleteSelection();
02000
02001 setEndingSelection(Position(positionNode, positionOffset));
02002 ReplaceSelectionCommand cmd(document(), m_fragment, true);
02003 applyCommandToComposite(cmd);
02004 }
02005
02006
02007
02008
02009 RemoveCSSPropertyCommandImpl::RemoveCSSPropertyCommandImpl(DocumentImpl *document, CSSStyleDeclarationImpl *decl, int property)
02010 : EditCommandImpl(document), m_decl(decl), m_property(property), m_important(false)
02011 {
02012 assert(m_decl);
02013 m_decl->ref();
02014 }
02015
02016 RemoveCSSPropertyCommandImpl::~RemoveCSSPropertyCommandImpl()
02017 {
02018 assert(m_decl);
02019 m_decl->deref();
02020 }
02021
02022 int RemoveCSSPropertyCommandImpl::commandID() const
02023 {
02024 return RemoveCSSPropertyCommandID;
02025 }
02026
02027 void RemoveCSSPropertyCommandImpl::doApply()
02028 {
02029 assert(m_decl);
02030
02031 m_oldValue = m_decl->getPropertyValue(m_property);
02032 assert(!m_oldValue.isNull());
02033
02034 m_important = m_decl->getPropertyPriority(m_property);
02035 m_decl->removeProperty(m_property);
02036 }
02037
02038 void RemoveCSSPropertyCommandImpl::doUnapply()
02039 {
02040 assert(m_decl);
02041 assert(!m_oldValue.isNull());
02042
02043 m_decl->setProperty(m_property, m_oldValue, m_important);
02044 }
02045
02046
02047
02048
02049 RemoveNodeAttributeCommandImpl::RemoveNodeAttributeCommandImpl(DocumentImpl *document, ElementImpl *element, NodeImpl::Id attribute)
02050 : EditCommandImpl(document), m_element(element), m_attribute(attribute)
02051 {
02052 assert(m_element);
02053 m_element->ref();
02054 }
02055
02056 RemoveNodeAttributeCommandImpl::~RemoveNodeAttributeCommandImpl()
02057 {
02058 assert(m_element);
02059 m_element->deref();
02060 }
02061
02062 int RemoveNodeAttributeCommandImpl::commandID() const
02063 {
02064 return RemoveNodeAttributeCommandID;
02065 }
02066
02067 void RemoveNodeAttributeCommandImpl::doApply()
02068 {
02069 assert(m_element);
02070
02071 m_oldValue = m_element->getAttribute(m_attribute);
02072 assert(!m_oldValue.isNull());
02073
02074 int exceptionCode = 0;
02075 m_element->removeAttribute(m_attribute, exceptionCode);
02076 assert(exceptionCode == 0);
02077 }
02078
02079 void RemoveNodeAttributeCommandImpl::doUnapply()
02080 {
02081 assert(m_element);
02082 assert(!m_oldValue.isNull());
02083
02084
02085 m_element->setAttribute(m_attribute, m_oldValue.implementation());
02086
02087 }
02088
02089
02090
02091
02092 RemoveNodeCommandImpl::RemoveNodeCommandImpl(DocumentImpl *document, NodeImpl *removeChild)
02093 : EditCommandImpl(document), m_parent(0), m_removeChild(removeChild), m_refChild(0)
02094 {
02095 assert(m_removeChild);
02096 m_removeChild->ref();
02097
02098 m_parent = m_removeChild->parentNode();
02099 assert(m_parent);
02100 m_parent->ref();
02101
02102 NodeListImpl *children = m_parent->childNodes();
02103 for (int i = children->length(); i >= 0; i--) {
02104 NodeImpl *node = children->item(i);
02105 if (node == m_removeChild)
02106 break;
02107 m_refChild = node;
02108 }
02109
02110 if (m_refChild)
02111 m_refChild->ref();
02112 }
02113
02114 RemoveNodeCommandImpl::~RemoveNodeCommandImpl()
02115 {
02116 if (m_parent)
02117 m_parent->deref();
02118 if (m_removeChild)
02119 m_removeChild->deref();
02120 if (m_refChild)
02121 m_refChild->deref();
02122 }
02123
02124 int RemoveNodeCommandImpl::commandID() const
02125 {
02126 return RemoveNodeCommandID;
02127 }
02128
02129 void RemoveNodeCommandImpl::doApply()
02130 {
02131 assert(m_parent);
02132 assert(m_removeChild);
02133
02134 int exceptionCode = 0;
02135 m_parent->removeChild(m_removeChild, exceptionCode);
02136 assert(exceptionCode == 0);
02137 }
02138
02139 void RemoveNodeCommandImpl::doUnapply()
02140 {
02141 assert(m_parent);
02142 assert(m_removeChild);
02143
02144 int exceptionCode = 0;
02145 if (m_refChild)
02146 m_parent->insertBefore(m_removeChild, m_refChild, exceptionCode);
02147 else
02148 m_parent->appendChild(m_removeChild, exceptionCode);
02149 assert(exceptionCode == 0);
02150 }
02151
02152
02153
02154
02155 RemoveNodeAndPruneCommandImpl::RemoveNodeAndPruneCommandImpl(DocumentImpl *document, NodeImpl *pruneNode, NodeImpl *stopNode)
02156 : CompositeEditCommandImpl(document), m_pruneNode(pruneNode), m_stopNode(stopNode)
02157 {
02158 assert(m_pruneNode);
02159 m_pruneNode->ref();
02160 if (m_stopNode)
02161 m_stopNode->ref();
02162 }
02163
02164 RemoveNodeAndPruneCommandImpl::~RemoveNodeAndPruneCommandImpl()
02165 {
02166 m_pruneNode->deref();
02167 if (m_stopNode)
02168 m_stopNode->deref();
02169 }
02170
02171 int RemoveNodeAndPruneCommandImpl::commandID() const
02172 {
02173 return RemoveNodeAndPruneCommandID;
02174 }
02175
02176 void RemoveNodeAndPruneCommandImpl::doApply()
02177 {
02178 NodeImpl *editableBlock = m_pruneNode->enclosingBlockFlowElement();
02179 NodeImpl *pruneNode = m_pruneNode;
02180 NodeImpl *node = pruneNode->traversePreviousNode();
02181 removeNode(pruneNode);
02182 while (1) {
02183 if (node == m_stopNode || editableBlock != node->enclosingBlockFlowElement() || !shouldPruneNode(node))
02184 break;
02185 pruneNode = node;
02186 node = node->traversePreviousNode();
02187 removeNode(pruneNode);
02188 }
02189 }
02190
02191
02192
02193
02194 RemoveNodePreservingChildrenCommandImpl::RemoveNodePreservingChildrenCommandImpl(DocumentImpl *document, NodeImpl *node)
02195 : CompositeEditCommandImpl(document), m_node(node)
02196 {
02197 assert(m_node);
02198 m_node->ref();
02199 }
02200
02201 RemoveNodePreservingChildrenCommandImpl::~RemoveNodePreservingChildrenCommandImpl()
02202 {
02203 if (m_node)
02204 m_node->deref();
02205 }
02206
02207 int RemoveNodePreservingChildrenCommandImpl::commandID() const
02208 {
02209 return RemoveNodePreservingChildrenCommandID;
02210 }
02211
02212 void RemoveNodePreservingChildrenCommandImpl::doApply()
02213 {
02214 NodeListImpl *children = node()->childNodes();
02215 int length = children->length();
02216 for (int i = 0; i < length; i++) {
02217 NodeImpl *child = children->item(0);
02218 removeNode(child);
02219 insertNodeBefore(child, node());
02220 }
02221 removeNode(node());
02222 }
02223
02224
02225
02226
02227 SetNodeAttributeCommandImpl::SetNodeAttributeCommandImpl(DocumentImpl *document, ElementImpl *element, NodeImpl::Id attribute, const DOMString &value)
02228 : EditCommandImpl(document), m_element(element), m_attribute(attribute), m_value(value)
02229 {
02230 assert(m_element);
02231 m_element->ref();
02232 assert(!m_value.isNull());
02233 }
02234
02235 SetNodeAttributeCommandImpl::~SetNodeAttributeCommandImpl()
02236 {
02237 if (m_element)
02238 m_element->deref();
02239 }
02240
02241 int SetNodeAttributeCommandImpl::commandID() const
02242 {
02243 return SetNodeAttributeCommandID;
02244 }
02245
02246 void SetNodeAttributeCommandImpl::doApply()
02247 {
02248 assert(m_element);
02249 assert(!m_value.isNull());
02250
02251
02252 m_oldValue = m_element->getAttribute(m_attribute);
02253 m_element->setAttribute(m_attribute, m_value.implementation());
02254
02255 }
02256
02257 void SetNodeAttributeCommandImpl::doUnapply()
02258 {
02259 assert(m_element);
02260 assert(!m_oldValue.isNull());
02261
02262
02263 m_element->setAttribute(m_attribute, m_oldValue.implementation());
02264
02265 }
02266
02267
02268
02269
02270 SplitTextNodeCommandImpl::SplitTextNodeCommandImpl(DocumentImpl *document, TextImpl *text, long offset)
02271 : EditCommandImpl(document), m_text1(0), m_text2(text), m_offset(offset)
02272 {
02273 assert(m_text2);
02274 assert(m_text2->length() > 0);
02275
02276 m_text2->ref();
02277 }
02278
02279 SplitTextNodeCommandImpl::~SplitTextNodeCommandImpl()
02280 {
02281 if (m_text1)
02282 m_text1->deref();
02283 if (m_text2)
02284 m_text2->deref();
02285 }
02286
02287 int SplitTextNodeCommandImpl::commandID() const
02288 {
02289 return SplitTextNodeCommandID;
02290 }
02291
02292 void SplitTextNodeCommandImpl::doApply()
02293 {
02294 assert(m_text2);
02295 assert(m_offset > 0);
02296
02297 int exceptionCode = 0;
02298
02299
02300
02301
02302
02303 if (!m_text1) {
02304
02305
02306 m_text1 = document()->createTextNode(m_text2->substringData(0, m_offset, exceptionCode));
02307 assert(exceptionCode == 0);
02308 assert(m_text1);
02309 m_text1->ref();
02310 }
02311
02312 m_text2->deleteData(0, m_offset, exceptionCode);
02313 assert(exceptionCode == 0);
02314
02315 m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
02316 assert(exceptionCode == 0);
02317
02318 assert(m_text2->previousSibling()->isTextNode());
02319 assert(m_text2->previousSibling() == m_text1);
02320 }
02321
02322 void SplitTextNodeCommandImpl::doUnapply()
02323 {
02324 assert(m_text1);
02325 assert(m_text2);
02326
02327 assert(m_text1->nextSibling() == m_text2);
02328
02329 int exceptionCode = 0;
02330 m_text2->insertData(0, m_text1->data(), exceptionCode);
02331 assert(exceptionCode == 0);
02332
02333 m_text2->parentNode()->removeChild(m_text1, exceptionCode);
02334 assert(exceptionCode == 0);
02335
02336 m_offset = m_text1->length();
02337 }
02338
02339
02340
02341
02342 TypingCommandImpl::TypingCommandImpl(DocumentImpl *document)
02343 : CompositeEditCommandImpl(document), m_openForMoreTyping(true)
02344 {
02345 }
02346
02347 TypingCommandImpl::~TypingCommandImpl()
02348 {
02349 }
02350
02351 int TypingCommandImpl::commandID() const
02352 {
02353 return TypingCommandID;
02354 }
02355
02356 void TypingCommandImpl::doApply()
02357 {
02358 }
02359
02360 void TypingCommandImpl::typingAddedToOpenCommand()
02361 {
02362 assert(document());
02363 assert(document()->part());
02364 EditCommand cmd(this);
02365 document()->part()->editor()->appliedEditing(cmd);
02366 }
02367
02368 void TypingCommandImpl::insertText(const DOMString &text)
02369 {
02370 if (document()->part()->editor()->typingStyle() || m_cmds.count() == 0) {
02371 InputTextCommand cmd(document());
02372 applyCommandToComposite(cmd);
02373 cmd.input(text);
02374 }
02375 else {
02376 EditCommand lastCommand = m_cmds.last();
02377 if (lastCommand.commandID() == InputTextCommandID) {
02378 static_cast<InputTextCommand &>(lastCommand).input(text);
02379 }
02380 else {
02381 InputTextCommand cmd(document());
02382 applyCommandToComposite(cmd);
02383 cmd.input(text);
02384 }
02385 }
02386 typingAddedToOpenCommand();
02387 }
02388
02389 void TypingCommandImpl::insertNewline()
02390 {
02391 InputNewlineCommand cmd(document());
02392 applyCommandToComposite(cmd);
02393 typingAddedToOpenCommand();
02394 }
02395
02396 void TypingCommandImpl::issueCommandForDeleteKey()
02397 {
02398 Selection selectionToDelete = endingSelection();
02399 assert(selectionToDelete.state() != Selection::NONE);
02400
02401 if (selectionToDelete.state() == Selection::CARET) {
02402 Position pos(selectionToDelete.start());
02403 if (pos.inFirstEditableInRootEditableElement() && pos.offset() <= pos.node()->caretMinOffset()) {
02404
02405 return;
02406 }
02407 selectionToDelete = Selection(pos.previousCharacterPosition(), pos);
02408 }
02409 deleteSelection(selectionToDelete);
02410 typingAddedToOpenCommand();
02411 }
02412
02413 void TypingCommandImpl::deleteKeyPressed()
02414 {
02415
02416
02417
02418
02419
02420
02421
02422
02423 issueCommandForDeleteKey();
02424 #if 0
02425 if (m_cmds.count() == 0) {
02426 issueCommandForDeleteKey();
02427 }
02428 else {
02429 EditCommand lastCommand = m_cmds.last();
02430 if (lastCommand.commandID() == InputTextCommandID) {
02431 InputTextCommand cmd = static_cast<InputTextCommand &>(lastCommand);
02432 cmd.deleteCharacter();
02433 if (cmd.charactersAdded() == 0) {
02434 removeCommand(cmd);
02435 }
02436 }
02437 else if (lastCommand.commandID() == InputNewlineCommandID) {
02438 lastCommand.unapply();
02439 removeCommand(lastCommand);
02440 }
02441 else {
02442 issueCommandForDeleteKey();
02443 }
02444 }
02445 #endif
02446 }
02447
02448 void TypingCommandImpl::removeCommand(const EditCommand &cmd)
02449 {
02450
02451
02452
02453
02454
02455 m_cmds.removeAll(cmd);
02456 if (m_cmds.count() == 0)
02457 setEndingSelection(startingSelection());
02458 else
02459 setEndingSelection(m_cmds.last().endingSelection());
02460 }
02461
02462
02463
02464 }