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

KHTML

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

KDE's Doxygen guidelines are available online.

KHTML

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

kdelibs API Reference

Skip menu "kdelibs API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  • kjsembed
  •   WTF
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Nepomuk-Core
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

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