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

KHTML

  • sources
  • kde-4.14
  • 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  QScopedPointer<CSSStyleDeclarationImpl> computedStyle(
612  element->document()->defaultView()->getComputedStyle(element, 0));
613  assert(!computedStyle.isNull());
614 #ifdef DEBUG_COMMANDS
615  kDebug() << "[change style]" << element << endl;
616 #endif
617 
618  QListIterator<CSSProperty*> it(*(style->values()));
619  while ( it.hasNext() ) {
620  CSSProperty *property = it.next();
621  CSSValueImpl *computedValue = computedStyle->getPropertyCSSValue(property->id());
622  DOMString newValue = property->value()->cssText();
623 #ifdef DEBUG_COMMANDS
624  kDebug() << "[new value]:" << property->cssText() << endl;
625  kDebug() << "[computedValue]:" << computedValue->cssText() << endl;
626 #endif
627  if (strcasecmp(computedValue->cssText(), newValue)) {
628  // we can do better and avoid parsing property
629  element->getInlineStyleDecls()->setProperty(property->id(), newValue);
630  }
631  }
632 }
633 
634 void ApplyStyleCommandImpl::doApply()
635 {
636  if (endingSelection().state() != Selection::RANGE)
637  return;
638 
639  // adjust to the positions we want to use for applying style
640  Position start(endingSelection().start().equivalentDownstreamPosition().equivalentRangeCompliantPosition());
641  Position end(endingSelection().end().equivalentUpstreamPosition());
642 #ifdef DEBUG_COMMANDS
643  kDebug() << "[APPLY STYLE]" << start << end << endl;
644  printEnclosingBlockTree(start.node()->enclosingBlockFlowElement());
645 #endif
646 
647  if (isBlockLevelStyle(m_style)) {
648 #ifdef DEBUG_COMMANDS
649  kDebug() << "[APPLY BLOCK LEVEL STYLE]" << endl;
650 #endif
651  ElementImpl *startBlock = start.node()->enclosingBlockFlowElement();
652  ElementImpl *endBlock = end.node()->enclosingBlockFlowElement();
653 #ifdef DEBUG_COMMANDS
654  kDebug() << startBlock << startBlock->nodeName() << endl;
655 #endif
656  if (startBlock == endBlock && startBlock == start.node()->rootEditableElement()) {
657  ElementImpl* block = document()->createHTMLElement("DIV");
658 #ifdef DEBUG_COMMANDS
659  kDebug() << "[Create DIV with Style:]" << m_style->cssText() << endl;
660 #endif
661  block->setAttribute(ATTR_STYLE, m_style->cssText());
662  for (NodeImpl* node = startBlock->firstChild(); node; node = startBlock->firstChild()) {
663 #ifdef DEBUG_COMMANDS
664  kDebug() << "[reparent node]" << node << node->nodeName() << endl;
665 #endif
666  removeNode(node);
667  appendNode(block, node);
668  }
669  appendNode(startBlock, block);
670  } else if (startBlock == endBlock) {
671  // StyleChange styleChange = computeStyleChange(Position(startBlock, 0), m_style);
672  // kDebug() << "[Modify block with style change:]" << styleChange.cssStyle << endl;
673  applyStyleChangeOnTheNode(startBlock, m_style);
674  // startBlock->setAttribute(ATTR_STYLE, styleChange.cssStyle);
675  }
676  return;
677  }
678 
679  // remove style from the selection
680  removeStyle(start, end);
681  bool splitStart = splitTextAtStartIfNeeded(start, end);
682  if (splitStart) {
683  start = endingSelection().start();
684  end = endingSelection().end();
685  }
686  splitTextAtEndIfNeeded(start, end);
687  start = endingSelection().start();
688  end = endingSelection().end();
689 
690 #ifdef DEBUG_COMMANDS
691  kDebug() << "[start;end]" << start << end << endl;
692 #endif
693  if (start.node() == end.node()) {
694  // simple case...start and end are the same node
695  applyStyleIfNeeded(start.node(), end.node());
696  } else {
697  NodeImpl *node = start.node();
698  while (1) {
699  if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
700  NodeImpl *runStart = node;
701  while (1) {
702  if (runStart->parentNode() != node->parentNode() || node->isHTMLElement() || node == end.node() ||
703  (node->renderer() && !node->renderer()->isInline())) {
704  applyStyleIfNeeded(runStart, node);
705  break;
706  }
707  node = node->traverseNextNode();
708  }
709  }
710  if (node == end.node())
711  break;
712  node = node->traverseNextNode();
713  }
714  }
715 }
716 
717 //------------------------------------------------------------------------------------------
718 // ApplyStyleCommandImpl: style-removal helpers
719 
720 bool ApplyStyleCommandImpl::isHTMLStyleNode(HTMLElementImpl *elem)
721 {
722  QListIterator<CSSProperty*> it(*(style()->values()));
723  while (it.hasNext()) {
724  CSSProperty *property = it.next();
725  switch (property->id()) {
726  case CSS_PROP_FONT_WEIGHT:
727  if (elem->id() == ID_B)
728  return true;
729  break;
730  case CSS_PROP_FONT_STYLE:
731  if (elem->id() == ID_I)
732  return true;
733  break;
734  }
735  }
736 
737  return false;
738 }
739 
740 void ApplyStyleCommandImpl::removeHTMLStyleNode(HTMLElementImpl *elem)
741 {
742  // This node can be removed.
743  // EDIT FIXME: This does not handle the case where the node
744  // has attributes. But how often do people add attributes to <B> tags?
745  // Not so often I think.
746  assert(elem);
747  removeNodePreservingChildren(elem);
748 }
749 
750 void ApplyStyleCommandImpl::removeCSSStyle(HTMLElementImpl *elem)
751 {
752  assert(elem);
753 
754  CSSStyleDeclarationImpl *decl = elem->inlineStyleDecls();
755  if (!decl)
756  return;
757 
758  QListIterator<CSSProperty*> it(*(style()->values()));
759  while ( it.hasNext() ) {
760  CSSProperty *property = it.next();
761  if (decl->getPropertyCSSValue(property->id()))
762  removeCSSProperty(decl, property->id());
763  }
764 
765  if (elem->id() == ID_SPAN) {
766  // Check to see if the span is one we added to apply style.
767  // If it is, and there are no more attributes on the span other than our
768  // class marker, remove the span.
769  NamedAttrMapImpl *map = elem->attributes();
770  if (map && (map->length() == 1 || (map->length() == 2 && elem->getAttribute(ATTR_STYLE).isEmpty())) &&
771  elem->getAttribute(ATTR_CLASS) == styleSpanClassString())
772  removeNodePreservingChildren(elem);
773  }
774 }
775 
776 void ApplyStyleCommandImpl::removeStyle(const Position &start, const Position &end)
777 {
778  NodeImpl *node = start.node();
779  while (1) {
780  NodeImpl *next = node->traverseNextNode();
781  if (node->isHTMLElement() && nodeFullySelected(node)) {
782  HTMLElementImpl *elem = static_cast<HTMLElementImpl *>(node);
783  if (isHTMLStyleNode(elem))
784  removeHTMLStyleNode(elem);
785  else
786  removeCSSStyle(elem);
787  }
788  if (node == end.node())
789  break;
790  node = next;
791  }
792 }
793 
794 bool ApplyStyleCommandImpl::nodeFullySelected(const NodeImpl *node) const
795 {
796  assert(node);
797 
798  Position end(endingSelection().end().equivalentUpstreamPosition());
799 
800  if (node == end.node())
801  return end.offset() >= node->caretMaxOffset();
802 
803  for (NodeImpl *child = node->lastChild(); child; child = child->lastChild()) {
804  if (child == end.node())
805  return end.offset() >= child->caretMaxOffset();
806  }
807 
808  return node == end.node() || !node->isAncestor(end.node());
809 }
810 
811 //------------------------------------------------------------------------------------------
812 // ApplyStyleCommandImpl: style-application helpers
813 
814 bool ApplyStyleCommandImpl::splitTextAtStartIfNeeded(const Position &start, const Position &end)
815 {
816  if (start.node()->isTextNode() && start.offset() > start.node()->caretMinOffset() && start.offset() < start.node()->caretMaxOffset()) {
817 #ifdef DEBUG_COMMANDS
818  kDebug() << "[split start]" << start.offset() << start.node()->caretMinOffset() << start.node()->caretMaxOffset() << endl;
819 #endif
820  long endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0;
821  TextImpl *text = static_cast<TextImpl *>(start.node());
822  RefPtr<SplitTextNodeCommandImpl> cmd = new SplitTextNodeCommandImpl(document(), text, start.offset());
823  applyCommandToComposite(cmd);
824  setEndingSelection(Selection(Position(start.node(), 0), Position(end.node(), end.offset() - endOffsetAdjustment)));
825  return true;
826  }
827  return false;
828 }
829 
830 NodeImpl *ApplyStyleCommandImpl::splitTextAtEndIfNeeded(const Position &start, const Position &end)
831 {
832  if (end.node()->isTextNode() && end.offset() > end.node()->caretMinOffset() && end.offset() < end.node()->caretMaxOffset()) {
833 #ifdef DEBUG_COMMANDS
834  kDebug() << "[split end]" << end.offset() << end.node()->caretMinOffset() << end.node()->caretMaxOffset() << endl;
835 #endif
836  TextImpl *text = static_cast<TextImpl *>(end.node());
837  RefPtr<SplitTextNodeCommandImpl> cmd = new SplitTextNodeCommandImpl(document(), text, end.offset());
838  applyCommandToComposite(cmd);
839  NodeImpl *startNode = start.node() == end.node() ? cmd->node()->previousSibling() : start.node();
840  assert(startNode);
841  setEndingSelection(Selection(Position(startNode, start.offset()), Position(cmd->node()->previousSibling(), cmd->node()->previousSibling()->caretMaxOffset())));
842  return cmd->node()->previousSibling();
843  }
844  return end.node();
845 }
846 
847 void ApplyStyleCommandImpl::surroundNodeRangeWithElement(NodeImpl *startNode, NodeImpl *endNode, ElementImpl *element)
848 {
849  assert(startNode);
850  assert(endNode);
851  assert(element);
852 
853  NodeImpl *node = startNode;
854  while (1) {
855  NodeImpl *next = node->traverseNextNode();
856  if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
857  removeNode(node);
858  appendNode(element, node);
859  }
860  if (node == endNode)
861  break;
862  node = next;
863  }
864 }
865 
866 static bool /*ApplyStyleCommandImpl::*/checkIfNewStylingNeeded(ElementImpl* element, CSSStyleDeclarationImpl *style)
867 {
868  QScopedPointer<CSSStyleDeclarationImpl> computedStyle(
869  element->document()->defaultView()->getComputedStyle(element, 0));
870  assert(!computedStyle.isNull());
871 #ifdef DEBUG_COMMANDS
872  kDebug() << "[check styling]" << element << endl;
873 #endif
874 
875  QListIterator<CSSProperty*> it(*(style->values()));
876  while ( it.hasNext() ) {
877  CSSProperty *property = it.next();
878  CSSValueImpl *computedValue = computedStyle->getPropertyCSSValue(property->id());
879  DOMString newValue = property->value()->cssText();
880 #ifdef DEBUG_COMMANDS
881  kDebug() << "[new value]:" << property->cssText() << endl;
882  kDebug() << "[computedValue]:" << computedValue->cssText() << endl;
883 #endif
884  if (strcasecmp(computedValue->cssText(), newValue))
885  return true;
886  }
887  return false;
888 }
889 
890 void ApplyStyleCommandImpl::applyStyleIfNeeded(DOM::NodeImpl *startNode, DOM::NodeImpl *endNode)
891 {
892  ElementImpl *parent = Position(startNode, 0).element();
893  if (!checkIfNewStylingNeeded(parent, style()))
894  return;
895  ElementImpl *styleElement = 0;
896  if (parent->id() == ID_SPAN && parent->firstChild() == startNode && parent->lastChild() == endNode) {
897  styleElement = parent;
898  } else {
899  styleElement = document()->createHTMLElement("SPAN");
900  styleElement->setAttribute(ATTR_CLASS, styleSpanClassString());
901  insertNodeBefore(styleElement, startNode);
902  surroundNodeRangeWithElement(startNode, endNode, styleElement);
903  }
904  applyStyleChangeOnTheNode(styleElement, style());
905 }
906 
907 bool ApplyStyleCommandImpl::currentlyHasStyle(const Position &pos, const CSSProperty *property) const
908 {
909  assert(pos.notEmpty());
910  kDebug() << pos << endl;
911  CSSStyleDeclarationImpl *decl = document()->defaultView()->getComputedStyle(pos.element(), 0);
912  assert(decl);
913  CSSValueImpl *value = decl->getPropertyCSSValue(property->id());
914  return strcasecmp(value->cssText(), property->value()->cssText()) == 0;
915 }
916 
917 ApplyStyleCommandImpl::StyleChange ApplyStyleCommandImpl::computeStyleChange(const Position &insertionPoint, CSSStyleDeclarationImpl *style)
918 {
919  assert(insertionPoint.notEmpty());
920  assert(style);
921 
922  StyleChange styleChange;
923 
924  QListIterator<CSSProperty*> it(*(style->values()));
925  while ( it.hasNext() ) {
926  CSSProperty *property = it.next();
927 #ifdef DEBUG_COMMANDS
928  kDebug() << "[CSS property]:" << property->cssText() << endl;
929 #endif
930  if (!currentlyHasStyle(insertionPoint, property)) {
931 #ifdef DEBUG_COMMANDS
932  kDebug() << "[Add to style change]" << endl;
933 #endif
934  switch (property->id()) {
935  case CSS_PROP_FONT_WEIGHT:
936  if (strcasecmp(property->value()->cssText(), "bold") == 0)
937  styleChange.applyBold = true;
938  else
939  styleChange.cssStyle += property->cssText();
940  break;
941  case CSS_PROP_FONT_STYLE: {
942  DOMString cssText(property->value()->cssText());
943  if (strcasecmp(cssText, "italic") == 0 || strcasecmp(cssText, "oblique") == 0)
944  styleChange.applyItalic = true;
945  else
946  styleChange.cssStyle += property->cssText();
947  }
948  break;
949  default:
950  styleChange.cssStyle += property->cssText();
951  break;
952  }
953  }
954  }
955  return styleChange;
956 }
957 
958 Position ApplyStyleCommandImpl::positionInsertionPoint(Position pos)
959 {
960  if (pos.node()->isTextNode() && (pos.offset() > 0 && pos.offset() < pos.node()->maxOffset())) {
961  RefPtr<SplitTextNodeCommandImpl> split = new SplitTextNodeCommandImpl(document(), static_cast<TextImpl *>(pos.node()), pos.offset());
962  split->apply();
963  pos = Position(split->node(), 0);
964  }
965 #if 0
966  // EDIT FIXME: If modified to work with the internals of applying style,
967  // this code can work to optimize cases where a style change is taking place on
968  // a boundary between nodes where one of the nodes has the desired style. In other
969  // words, it is possible for content to be merged into existing nodes rather than adding
970  // additional markup.
971  if (currentlyHasStyle(pos))
972  return pos;
973 
974  // try next node
975  if (pos.offset() >= pos.node()->caretMaxOffset()) {
976  NodeImpl *nextNode = pos.node()->traverseNextNode();
977  if (nextNode) {
978  Position next = Position(nextNode, 0);
979  if (currentlyHasStyle(next))
980  return next;
981  }
982  }
983 
984  // try previous node
985  if (pos.offset() <= pos.node()->caretMinOffset()) {
986  NodeImpl *prevNode = pos.node()->traversePreviousNode();
987  if (prevNode) {
988  Position prev = Position(prevNode, prevNode->maxOffset());
989  if (currentlyHasStyle(prev))
990  return prev;
991  }
992  }
993 #endif
994 
995  return pos;
996 }
997 
998 //------------------------------------------------------------------------------------------
999 // DeleteCollapsibleWhitespaceCommandImpl
1000 
1001 DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl(DocumentImpl *document)
1002  : CompositeEditCommandImpl(document), m_charactersDeleted(0), m_hasSelectionToCollapse(false)
1003 {
1004 }
1005 
1006 DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl(DocumentImpl *document, const Selection &selection)
1007  : CompositeEditCommandImpl(document), m_charactersDeleted(0), m_selectionToCollapse(selection), m_hasSelectionToCollapse(true)
1008 {
1009 }
1010 
1011 DeleteCollapsibleWhitespaceCommandImpl::~DeleteCollapsibleWhitespaceCommandImpl()
1012 {
1013 }
1014 
1015 static bool shouldDeleteUpstreamPosition(const Position &pos)
1016 {
1017  if (!pos.node()->isTextNode())
1018  return false;
1019 
1020  RenderObject *renderer = pos.node()->renderer();
1021  if (!renderer)
1022  return true;
1023 
1024  TextImpl *textNode = static_cast<TextImpl *>(pos.node());
1025  if (pos.offset() >= (long)textNode->length())
1026  return false;
1027 
1028  if (pos.isLastRenderedPositionInEditableBlock())
1029  return false;
1030 
1031  if (pos.isFirstRenderedPositionOnLine() || pos.isLastRenderedPositionOnLine())
1032  return false;
1033 
1034  return false;
1035  // TODO we need to match DOM - Rendered offset first
1036 // RenderText *textRenderer = static_cast<RenderText *>(renderer);
1037 // for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
1038 // if (pos.offset() < box->m_start) {
1039 // return true;
1040 // }
1041 // if (pos.offset() >= box->m_start && pos.offset() < box->m_start + box->m_len)
1042 // return false;
1043 // }
1044 //
1045 // return true;
1046 }
1047 
1048 Position DeleteCollapsibleWhitespaceCommandImpl::deleteWhitespace(const Position &pos)
1049 {
1050  Position upstream = pos.equivalentUpstreamPosition();
1051  Position downstream = pos.equivalentDownstreamPosition();
1052 #ifdef DEBUG_COMMANDS
1053  kDebug() << "[pos]" << pos << endl;
1054  kDebug() << "[upstream:downstream]" << upstream << downstream << endl;
1055  printEnclosingBlockTree(pos.node());
1056 #endif
1057 
1058  bool del = shouldDeleteUpstreamPosition(upstream);
1059 #ifdef DEBUG_COMMANDS
1060  kDebug() << "[delete upstream]" << del << endl;
1061 #endif
1062 
1063  if (upstream == downstream)
1064  return upstream;
1065 
1066 #ifdef DEBUG_COMMANDS
1067  PositionIterator iter(upstream);
1068  kDebug() << "[before print]" << endl;
1069  for (iter.next(); iter.current() != downstream; iter.next())
1070  kDebug() << "[iterate]" << iter.current() << endl;
1071  kDebug() << "[after print]" << endl;
1072 #endif
1073 
1074  PositionIterator it(upstream);
1075  Position deleteStart = upstream;
1076  if (!del) {
1077  deleteStart = it.peekNext();
1078  if (deleteStart == downstream)
1079  return upstream;
1080  }
1081 
1082  Position endingPosition = upstream;
1083 
1084  while (it.current() != downstream) {
1085  Position next = it.peekNext();
1086 #ifdef DEBUG_COMMANDS
1087  kDebug() << "[iterate and delete]" << next << endl;
1088 #endif
1089  if (next.node() != deleteStart.node()) {
1090  // TODO assert(deleteStart.node()->isTextNode());
1091  if (deleteStart.node()->isTextNode()) {
1092  TextImpl *textNode = static_cast<TextImpl *>(deleteStart.node());
1093  unsigned long count = it.current().offset() - deleteStart.offset();
1094  if (count == textNode->length()) {
1095 #ifdef DEBUG_COMMANDS
1096  kDebug(6200) << " removeNodeAndPrune 1:" << textNode;
1097 #endif
1098  if (textNode == endingPosition.node())
1099  endingPosition = Position(next.node(), next.node()->caretMinOffset());
1100  removeNodeAndPrune(textNode);
1101  } else {
1102 #ifdef DEBUG_COMMANDS
1103  kDebug(6200) << " deleteText 1:" << textNode << "t len:" << textNode->length()<<"start:" << deleteStart.offset() << "del len:" << (it.current().offset() - deleteStart.offset());
1104 #endif
1105  deleteText(textNode, deleteStart.offset(), count);
1106  }
1107  } else {
1108 #ifdef DEBUG_COMMANDS
1109  kDebug() << "[not text node is not supported yet]" << endl;
1110 #endif
1111  }
1112  deleteStart = next;
1113  } else if (next == downstream) {
1114  assert(deleteStart.node() == downstream.node());
1115  assert(downstream.node()->isTextNode());
1116  TextImpl *textNode = static_cast<TextImpl *>(deleteStart.node());
1117  unsigned long count = downstream.offset() - deleteStart.offset();
1118  assert(count <= textNode->length());
1119  if (count == textNode->length()) {
1120 #ifdef DEBUG_COMMANDS
1121  kDebug(6200) << " removeNodeAndPrune 2:"<<textNode;
1122 #endif
1123  removeNodeAndPrune(textNode);
1124  } else {
1125 #ifdef DEBUG_COMMANDS
1126  kDebug(6200) << " deleteText 2:"<< textNode<< "t len:" << textNode->length() <<"start:" <<deleteStart.offset() << "del len:" << count;
1127 #endif
1128  deleteText(textNode, deleteStart.offset(), count);
1129  m_charactersDeleted = count;
1130  endingPosition = Position(downstream.node(), downstream.offset() - m_charactersDeleted);
1131  }
1132  }
1133 
1134  it.setPosition(next);
1135  }
1136 
1137  return endingPosition;
1138 }
1139 
1140 void DeleteCollapsibleWhitespaceCommandImpl::doApply()
1141 {
1142  // If selection has not been set to a custom selection when the command was created,
1143  // use the current ending selection.
1144  if (!m_hasSelectionToCollapse)
1145  m_selectionToCollapse = endingSelection();
1146  int state = m_selectionToCollapse.state();
1147  if (state == Selection::CARET) {
1148  Position endPosition = deleteWhitespace(m_selectionToCollapse.start());
1149  setEndingSelection(endPosition);
1150 #ifdef DEBUG_COMMANDS
1151  kDebug(6200) << "-----------------------------------------------------";
1152 #endif
1153  }
1154  else if (state == Selection::RANGE) {
1155  Position startPosition = deleteWhitespace(m_selectionToCollapse.start());
1156 #ifdef DEBUG_COMMANDS
1157  kDebug(6200) << "-----------------------------------------------------";
1158 #endif
1159  Position endPosition = m_selectionToCollapse.end();
1160  if (m_charactersDeleted > 0 && startPosition.node() == endPosition.node()) {
1161 #ifdef DEBUG_COMMANDS
1162  kDebug(6200) << "adjust end position by" << m_charactersDeleted;
1163 #endif
1164  endPosition = Position(endPosition.node(), endPosition.offset() - m_charactersDeleted);
1165  }
1166  endPosition = deleteWhitespace(endPosition);
1167  setEndingSelection(Selection(startPosition, endPosition));
1168 #ifdef DEBUG_COMMANDS
1169  kDebug(6200) << "=====================================================";
1170 #endif
1171  }
1172 }
1173 
1174 //------------------------------------------------------------------------------------------
1175 // DeleteSelectionCommandImpl
1176 
1177 DeleteSelectionCommandImpl::DeleteSelectionCommandImpl(DocumentImpl *document)
1178  : CompositeEditCommandImpl(document), m_hasSelectionToDelete(false)
1179 {
1180 }
1181 
1182 DeleteSelectionCommandImpl::DeleteSelectionCommandImpl(DocumentImpl *document, const Selection &selection)
1183  : CompositeEditCommandImpl(document), m_selectionToDelete(selection), m_hasSelectionToDelete(true)
1184 {
1185 }
1186 
1187 DeleteSelectionCommandImpl::~DeleteSelectionCommandImpl()
1188 {
1189 }
1190 
1191 void DeleteSelectionCommandImpl::joinTextNodesWithSameStyle()
1192 {
1193  Selection selection = endingSelection();
1194 
1195  if (selection.state() != Selection::CARET)
1196  return;
1197 
1198  Position pos(selection.start());
1199 
1200  if (!pos.node()->isTextNode())
1201  return;
1202 
1203  TextImpl *textNode = static_cast<TextImpl *>(pos.node());
1204 
1205  if (pos.offset() == 0) {
1206  PositionIterator it(pos);
1207  Position prev = it.previous();
1208  if (prev == pos)
1209  return;
1210  if (prev.node()->isTextNode()) {
1211  TextImpl *prevTextNode = static_cast<TextImpl *>(prev.node());
1212  if (textNodesAreJoinable(prevTextNode, textNode)) {
1213  joinTextNodes(prevTextNode, textNode);
1214  setEndingSelection(Position(textNode, prevTextNode->length()));
1215 #ifdef DEBUG_COMMANDS
1216  kDebug(6200) << "joinTextNodesWithSameStyle [1]";
1217 #endif
1218  }
1219  }
1220  } else if (pos.offset() == (long)textNode->length()) {
1221  PositionIterator it(pos);
1222  Position next = it.next();
1223  if (next == pos)
1224  return;
1225  if (next.node()->isTextNode()) {
1226  TextImpl *nextTextNode = static_cast<TextImpl *>(next.node());
1227  if (textNodesAreJoinable(textNode, nextTextNode)) {
1228  joinTextNodes(textNode, nextTextNode);
1229  setEndingSelection(Position(nextTextNode, pos.offset()));
1230 #ifdef DEBUG_COMMANDS
1231  kDebug(6200) << "joinTextNodesWithSameStyle [2]";
1232 #endif
1233  }
1234  }
1235  }
1236 }
1237 
1238 bool DeleteSelectionCommandImpl::containsOnlyWhitespace(const Position &start, const Position &end)
1239 {
1240  // Returns whether the range contains only whitespace characters.
1241  // This is inclusive of the start, but not of the end.
1242  PositionIterator it(start);
1243  while (!it.atEnd()) {
1244  if (!it.current().node()->isTextNode())
1245  return false;
1246  const DOMString &text = static_cast<TextImpl *>(it.current().node())->data();
1247  // EDIT FIXME: signed/unsigned mismatch
1248  if (text.length() > INT_MAX)
1249  return false;
1250  if (it.current().offset() < (int)text.length() && !isWS(text[it.current().offset()]))
1251  return false;
1252  it.next();
1253  if (it.current() == end)
1254  break;
1255  }
1256  return true;
1257 }
1258 
1259 void DeleteSelectionCommandImpl::deleteContentInsideNode(NodeImpl *node, int startOffset, int endOffset)
1260 {
1261 #ifdef DEBUG_COMMANDS
1262  kDebug() << "[Delete content inside node]" << node << startOffset << endOffset << endl;
1263 #endif
1264  if (node->isTextNode()) {
1265  // check if nothing to delete
1266  if (startOffset == endOffset)
1267  return;
1268  // check if node is fully covered then remove node completely
1269  if (!startOffset && endOffset == node->maxOffset()) {
1270  removeNodeAndPrune(node);
1271  return;
1272  }
1273  // delete only substring
1274  deleteText(static_cast<TextImpl*>(node), startOffset, endOffset - startOffset);
1275  return;
1276  }
1277 #ifdef DEBUG_COMMANDS
1278  kDebug() << "[non-text node] not supported" << endl;
1279 #endif
1280 }
1281 
1282 void DeleteSelectionCommandImpl::deleteContentBeforeOffset(NodeImpl *node, int offset)
1283 {
1284  deleteContentInsideNode(node, 0, offset);
1285 }
1286 
1287 void DeleteSelectionCommandImpl::deleteContentAfterOffset(NodeImpl *node, int offset)
1288 {
1289  if (node->isTextNode())
1290  deleteContentInsideNode(node, offset, node->maxOffset());
1291 }
1292 
1293 void DeleteSelectionCommandImpl::doApply()
1294 {
1295  // If selection has not been set to a custom selection when the command was created,
1296  // use the current ending selection.
1297  if (!m_hasSelectionToDelete)
1298  m_selectionToDelete = endingSelection();
1299 
1300  if (m_selectionToDelete.state() != Selection::RANGE)
1301  return;
1302 
1303  deleteCollapsibleWhitespace(m_selectionToDelete);
1304  Selection selection = endingSelection();
1305 
1306  Position upstreamStart(selection.start().equivalentUpstreamPosition());
1307  Position downstreamStart(selection.start().equivalentDownstreamPosition());
1308  Position upstreamEnd(selection.end().equivalentUpstreamPosition());
1309  Position downstreamEnd(selection.end().equivalentDownstreamPosition());
1310 
1311  NodeImpl *startBlock = upstreamStart.node()->enclosingBlockFlowElement();
1312  NodeImpl *endBlock = downstreamEnd.node()->enclosingBlockFlowElement();
1313 
1314 #ifdef DEBUG_COMMANDS
1315  kDebug() << "[Delete:Start]" << upstreamStart << downstreamStart << endl;
1316  kDebug() << "[Delete:End]" << upstreamEnd << downstreamEnd << endl;
1317  printEnclosingBlockTree(upstreamStart.node());
1318 #endif
1319  if (startBlock != endBlock)
1320  printEnclosingBlockTree(downstreamEnd.node());
1321 
1322  if (upstreamStart == downstreamEnd)
1323  // after collapsing whitespace, selection is empty...no work to do
1324  return;
1325 
1326  // remove all the nodes that are completely covered by the selection
1327  if (upstreamStart.node() != downstreamEnd.node()) {
1328  NodeImpl *node, *next;
1329  for (node = upstreamStart.node()->traverseNextNode(); node && node != downstreamEnd.node(); node = next) {
1330 #ifdef DEBUG_COMMANDS
1331  kDebug() << "[traverse and delete]" << node << (node->renderer() && node->renderer()->isEditable()) << endl;
1332 #endif
1333  next = node->traverseNextNode();
1334  if (node->renderer() && node->renderer()->isEditable())
1335  removeNode(node); // removeAndPrune?
1336  }
1337  }
1338 
1339  // if we have different blocks then merge content of the second into first one
1340  if (startBlock != endBlock && startBlock->parentNode() == endBlock->parentNode()) {
1341  NodeImpl *node = endBlock->firstChild();
1342  while (node) {
1343  NodeImpl *moveNode = node;
1344  node = node->nextSibling();
1345  removeNode(moveNode);
1346  appendNode(startBlock, moveNode);
1347  }
1348  }
1349 
1350  if (upstreamStart.node() == downstreamEnd.node())
1351  deleteContentInsideNode(upstreamEnd.node(), upstreamStart.offset(), downstreamEnd.offset());
1352  else {
1353  deleteContentAfterOffset(upstreamStart.node(), upstreamStart.offset());
1354  deleteContentBeforeOffset(downstreamEnd.node(), downstreamEnd.offset());
1355  }
1356 
1357  setEndingSelection(upstreamStart);
1358 #if 0
1359  Position endingPosition;
1360  bool adjustEndingPositionDownstream = false;
1361 
1362  bool onlyWhitespace = containsOnlyWhitespace(upstreamStart, downstreamEnd);
1363  kDebug() << "[OnlyWhitespace]" << onlyWhitespace << endl;
1364 
1365  bool startCompletelySelected = !onlyWhitespace &&
1366  (downstreamStart.offset() <= downstreamStart.node()->caretMinOffset() &&
1367  ((downstreamStart.node() != upstreamEnd.node()) ||
1368  (upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset())));
1369 
1370  bool endCompletelySelected = !onlyWhitespace &&
1371  (upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset() &&
1372  ((downstreamStart.node() != upstreamEnd.node()) ||
1373  (downstreamStart.offset() <= downstreamStart.node()->caretMinOffset())));
1374 
1375  kDebug() << "[{start:end}CompletelySelected]" << startCompletelySelected << endCompletelySelected << endl;
1376 
1377  unsigned long startRenderedOffset = downstreamStart.renderedOffset();
1378 
1379  bool startAtStartOfRootEditableElement = startRenderedOffset == 0 && downstreamStart.inFirstEditableInRootEditableElement();
1380  bool startAtStartOfBlock = startAtStartOfRootEditableElement ||
1381  (startRenderedOffset == 0 && downstreamStart.inFirstEditableInContainingEditableBlock());
1382  bool endAtEndOfBlock = downstreamEnd.isLastRenderedPositionInEditableBlock();
1383 
1384  kDebug() << "[startAtStartOfRootEditableElement]" << startAtStartOfRootEditableElement << endl;
1385  kDebug() << "[startAtStartOfBlock]" << startAtStartOfBlock << endl;
1386  kDebug() << "[endAtEndOfBlock]" << endAtEndOfBlock << endl;
1387 
1388  NodeImpl *startBlock = upstreamStart.node()->enclosingBlockFlowElement();
1389  NodeImpl *endBlock = downstreamEnd.node()->enclosingBlockFlowElement();
1390  bool startBlockEndBlockAreSiblings = startBlock->parentNode() == endBlock->parentNode();
1391 
1392  kDebug() << "[startBlockEndBlockAreSiblings]" << startBlockEndBlockAreSiblings << startBlock << endBlock << endl;
1393 
1394  debugPosition("upstreamStart: ", upstreamStart);
1395  debugPosition("downstreamStart: ", downstreamStart);
1396  debugPosition("upstreamEnd: ", upstreamEnd);
1397  debugPosition("downstreamEnd: ", downstreamEnd);
1398  kDebug(6200) << "start selected:" << (startCompletelySelected ? "YES" : "NO");
1399  kDebug(6200) << "at start block:" << (startAtStartOfBlock ? "YES" : "NO");
1400  kDebug(6200) << "at start root block:"<< (startAtStartOfRootEditableElement ? "YES" : "NO");
1401  kDebug(6200) << "at end block:"<< (endAtEndOfBlock ? "YES" : "NO");
1402  kDebug(6200) << "only whitespace:"<< (onlyWhitespace ? "YES" : "NO");
1403 
1404  // Determine where to put the caret after the deletion
1405  if (startAtStartOfBlock) {
1406  kDebug(6200) << "ending position case 1";
1407  endingPosition = Position(startBlock, 0);
1408  adjustEndingPositionDownstream = true;
1409  } else if (!startCompletelySelected) {
1410  kDebug(6200) << "ending position case 2";
1411  endingPosition = upstreamEnd; // FIXME ??????????? upstreamStart;
1412  if (upstreamStart.node()->id() == ID_BR && upstreamStart.offset() == 1)
1413  adjustEndingPositionDownstream = true;
1414  } else if (upstreamStart != downstreamStart) {
1415  kDebug(6200) << "ending position case 3";
1416  endingPosition = upstreamStart;
1417  if (upstreamStart.node()->id() == ID_BR && upstreamStart.offset() == 1)
1418  adjustEndingPositionDownstream = true;
1419  }
1420 
1421  //
1422  // Figure out the whitespace conversions to do
1423  //
1424  if ((startAtStartOfBlock && !endAtEndOfBlock) || (!startCompletelySelected && adjustEndingPositionDownstream)) {
1425  // convert trailing whitespace
1426  Position trailing = trailingWhitespacePosition(downstreamEnd.equivalentDownstreamPosition());
1427  if (trailing.notEmpty()) {
1428  debugPosition("convertTrailingWhitespace: ", trailing);
1429  Position collapse = trailing.nextCharacterPosition();
1430  if (collapse != trailing)
1431  deleteCollapsibleWhitespace(collapse);
1432  TextImpl *textNode = static_cast<TextImpl *>(trailing.node());
1433  replaceText(textNode, trailing.offset(), 1, nonBreakingSpaceString());
1434  }
1435  } else if (!startAtStartOfBlock && endAtEndOfBlock) {
1436  // convert leading whitespace
1437  Position leading = leadingWhitespacePosition(upstreamStart.equivalentUpstreamPosition());
1438  if (leading.notEmpty()) {
1439  debugPosition("convertLeadingWhitespace: ", leading);
1440  TextImpl *textNode = static_cast<TextImpl *>(leading.node());
1441  replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
1442  }
1443  } else if (!startAtStartOfBlock && !endAtEndOfBlock) {
1444  // convert contiguous whitespace
1445  Position leading = leadingWhitespacePosition(upstreamStart.equivalentUpstreamPosition());
1446  Position trailing = trailingWhitespacePosition(downstreamEnd.equivalentDownstreamPosition());
1447  if (leading.notEmpty() && trailing.notEmpty()) {
1448  debugPosition("convertLeadingWhitespace [contiguous]: ", leading);
1449  TextImpl *textNode = static_cast<TextImpl *>(leading.node());
1450  replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
1451  }
1452  }
1453 
1454  //
1455  // Do the delete
1456  //
1457  NodeImpl *n = downstreamStart.node()->traverseNextNode();
1458  kDebug() << "[n]" << n << endl;
1459 
1460  // work on start node
1461  if (startCompletelySelected) {
1462  kDebug(6200) << "start node delete case 1";
1463  removeNodeAndPrune(downstreamStart.node(), startBlock);
1464  } else if (onlyWhitespace) {
1465  // Selection only contains whitespace. This is really a special-case to
1466  // handle significant whitespace that is collapsed at the end of a line,
1467  // but also handles deleting a space in mid-line.
1468  kDebug(6200) << "start node delete case 2";
1469  assert(upstreamStart.node()->isTextNode());
1470  TextImpl *text = static_cast<TextImpl *>(upstreamStart.node());
1471  int offset = upstreamStart.offset();
1472  // EDIT FIXME: Signed/unsigned mismatch
1473  int length = text->length();
1474  if (length == upstreamStart.offset())
1475  offset--;
1476  // FIXME ??? deleteText(text, offset, 1);
1477  } else if (downstreamStart.node()->isTextNode()) {
1478  kDebug(6200) << "start node delete case 3";
1479  TextImpl *text = static_cast<TextImpl *>(downstreamStart.node());
1480  int endOffset = text == upstreamEnd.node() ? upstreamEnd.offset() : text->length();
1481  if (endOffset > downstreamStart.offset()) {
1482  deleteText(text, downstreamStart.offset(), endOffset - downstreamStart.offset());
1483  }
1484  } else {
1485  // we have clipped the end of a non-text element
1486  // the offset must be 1 here. if it is, do nothing and move on.
1487  kDebug(6200) << "start node delete case 4";
1488  assert(downstreamStart.offset() == 1);
1489  }
1490 
1491  if (n && !onlyWhitespace && downstreamStart.node() != upstreamEnd.node()) {
1492  // work on intermediate nodes
1493  while (n && n != upstreamEnd.node()) {
1494  NodeImpl *d = n;
1495  n = n->traverseNextNode();
1496  if (d->renderer() && d->renderer()->isEditable())
1497  removeNodeAndPrune(d, startBlock);
1498  }
1499  if (!n)
1500  return;
1501 
1502  // work on end node
1503  assert(n == upstreamEnd.node());
1504  if (endCompletelySelected) {
1505  removeNodeAndPrune(upstreamEnd.node(), startBlock);
1506  }
1507  else if (upstreamEnd.node()->isTextNode()) {
1508  if (upstreamEnd.offset() > 0) {
1509  TextImpl *text = static_cast<TextImpl *>(upstreamEnd.node());
1510  deleteText(text, 0, upstreamEnd.offset());
1511  }
1512  }
1513  else {
1514  // we have clipped the beginning of a non-text element
1515  // the offset must be 0 here. if it is, do nothing and move on.
1516  assert(downstreamStart.offset() == 0);
1517  }
1518  }
1519 
1520  // Do block merge if start and end of selection are in different blocks
1521  // and the blocks are siblings. This is a first cut at this rule arrived
1522  // at by doing a bunch of edits and settling on the behavior that made
1523  // the most sense. This could change in the future as we get more
1524  // experience with how this should behave.
1525  if (startBlock != endBlock && startBlockEndBlockAreSiblings) {
1526  kDebug(6200) << "merging content to start block";
1527  NodeImpl *node = endBlock->firstChild();
1528  while (node) {
1529  NodeImpl *moveNode = node;
1530  node = node->nextSibling();
1531  removeNode(moveNode);
1532  appendNode(startBlock, moveNode);
1533  }
1534  }
1535 
1536  if (adjustEndingPositionDownstream) {
1537  kDebug(6200) << "adjust ending position downstream";
1538  endingPosition = endingPosition.equivalentDownstreamPosition();
1539  }
1540 
1541  debugPosition("ending position: ", endingPosition);
1542  setEndingSelection(endingPosition);
1543 
1544  kDebug(6200) << "-----------------------------------------------------";
1545 #endif
1546 }
1547 
1548 //------------------------------------------------------------------------------------------
1549 // DeleteTextCommandImpl
1550 
1551 DeleteTextCommandImpl::DeleteTextCommandImpl(DocumentImpl *document, TextImpl *node, long offset, long count)
1552  : EditCommandImpl(document), m_node(node), m_offset(offset), m_count(count)
1553 {
1554  assert(m_node);
1555  assert(m_offset >= 0);
1556  assert(m_count >= 0);
1557 
1558  m_node->ref();
1559 }
1560 
1561 DeleteTextCommandImpl::~DeleteTextCommandImpl()
1562 {
1563  if (m_node)
1564  m_node->deref();
1565 }
1566 
1567 void DeleteTextCommandImpl::doApply()
1568 {
1569  assert(m_node);
1570 
1571  int exceptionCode = 0;
1572  m_text = m_node->substringData(m_offset, m_count, exceptionCode);
1573  assert(exceptionCode == 0);
1574 
1575  m_node->deleteData(m_offset, m_count, exceptionCode);
1576  assert(exceptionCode == 0);
1577 }
1578 
1579 void DeleteTextCommandImpl::doUnapply()
1580 {
1581  assert(m_node);
1582  assert(!m_text.isEmpty());
1583 
1584  int exceptionCode = 0;
1585  m_node->insertData(m_offset, m_text, exceptionCode);
1586  assert(exceptionCode == 0);
1587 }
1588 
1589 //------------------------------------------------------------------------------------------
1590 // InputNewlineCommandImpl
1591 
1592 InputNewlineCommandImpl::InputNewlineCommandImpl(DocumentImpl *document)
1593  : CompositeEditCommandImpl(document)
1594 {
1595 }
1596 
1597 InputNewlineCommandImpl::~InputNewlineCommandImpl()
1598 {
1599 }
1600 
1601 void InputNewlineCommandImpl::insertNodeAfterPosition(NodeImpl *node, const Position &pos)
1602 {
1603  // Insert the BR after the caret position. In the case the
1604  // position is a block, do an append. We don't want to insert
1605  // the BR *after* the block.
1606  Position upstream(pos.equivalentUpstreamPosition());
1607  NodeImpl *cb = pos.node()->enclosingBlockFlowElement();
1608  if (cb == pos.node())
1609  appendNode(cb, node);
1610  else
1611  insertNodeAfter(node, pos.node());
1612 }
1613 
1614 void InputNewlineCommandImpl::insertNodeBeforePosition(NodeImpl *node, const Position &pos)
1615 {
1616  // Insert the BR after the caret position. In the case the
1617  // position is a block, do an append. We don't want to insert
1618  // the BR *before* the block.
1619  Position upstream(pos.equivalentUpstreamPosition());
1620  NodeImpl *cb = pos.node()->enclosingBlockFlowElement();
1621  if (cb == pos.node())
1622  appendNode(cb, node);
1623  else
1624  insertNodeBefore(node, pos.node());
1625 }
1626 
1627 void InputNewlineCommandImpl::doApply()
1628 {
1629  deleteSelection();
1630  Selection selection = endingSelection();
1631  int exceptionCode = 0;
1632 
1633  NodeImpl *enclosingBlock = selection.start().node()->enclosingBlockFlowElement();
1634  kDebug() << enclosingBlock->nodeName() << endl;
1635  if (enclosingBlock->id() == ID_LI) {
1636  // need to insert new list item or split existing one into 2
1637  // consider example: <li>x<u>x<b>x|x</b>x</u>x</li> (| - caret position)
1638  // result should look like: <li>x<u>x<b>x</b></u></li><li><u>|x<b>x</b></u></li>
1639  // idea is to walk up to the li item and split and reattach correspondent nodes
1640 #ifdef DEBUG_COMMANDS
1641  kDebug() << "[insert new list item]" << selection << endl;
1642  printEnclosingBlockTree(selection.start().node());
1643 #endif
1644  Position pos(selection.start().equivalentDownstreamPosition());
1645  NodeImpl *node = pos.node();
1646  bool atBlockStart = pos.atStartOfContainingEditableBlock();
1647  bool atBlockEnd = pos.isLastRenderedPositionInEditableBlock();
1648  // split text node into 2 if we are in the middle
1649  if (node->isTextNode() && !atBlockStart && !atBlockEnd) {
1650  TextImpl *textNode = static_cast<TextImpl*>(node);
1651  TextImpl *textBeforeNode = document()->createTextNode(textNode->substringData(0, selection.start().offset(), exceptionCode));
1652  deleteText(textNode, 0, pos.offset());
1653  insertNodeBefore(textBeforeNode, textNode);
1654  pos = Position(textNode, 0);
1655  setEndingSelection(pos);
1656 
1657  // walk up and reattach
1658  while (true) {
1659 #ifdef DEBUG_COMMANDS
1660  kDebug() << "[handle node]" << node << endl;
1661  printEnclosingBlockTree(enclosingBlock->parent());
1662 #endif
1663  NodeImpl *parent = node->parent();
1664  // FIXME copy attributes, styles etc too
1665  RefPtr<NodeImpl> newParent = parent->cloneNode(false);
1666  insertNodeAfter(newParent.get(), parent);
1667  for (NodeImpl *nextSibling = 0; node; node = nextSibling) {
1668 #ifdef DEBUG_COMMANDS
1669  kDebug() << "[reattach sibling]" << node << endl;
1670 #endif
1671  nextSibling = node->nextSibling();
1672  removeNode(node);
1673  appendNode(newParent.get(), node);
1674  }
1675  node = newParent.get();
1676  if (parent == enclosingBlock)
1677  break;
1678  }
1679  } else if (node->isTextNode()) {
1680  // insert <br> node either as previous list or the next one
1681  if (atBlockStart) {
1682  ElementImpl *listItem = document()->createHTMLElement("LI");
1683  insertNodeBefore(listItem, enclosingBlock);
1684  } else {
1685  ElementImpl *listItem = document()->createHTMLElement("LI");
1686  insertNodeAfter(listItem, enclosingBlock);
1687  }
1688  }
1689 
1690 #ifdef DEBUG_COMMANDS
1691  kDebug() << "[result]" << endl;
1692  printEnclosingBlockTree(enclosingBlock->parent());
1693 #endif
1694  // FIXME set selection after operation
1695  return;
1696  }
1697 
1698  ElementImpl *breakNode = document()->createHTMLElement("BR");
1699  // assert(exceptionCode == 0);
1700 
1701 #ifdef DEBUG_COMMANDS
1702  kDebug() << "[insert break]" << selection << endl;
1703  printEnclosingBlockTree(enclosingBlock);
1704 #endif
1705 
1706  NodeImpl *nodeToInsert = breakNode;
1707  // Handle the case where there is a typing style.
1708  if (document()->part()->editor()->typingStyle()) {
1709  int exceptionCode = 0;
1710  ElementImpl *styleElement = createTypingStyleElement();
1711  styleElement->appendChild(breakNode, exceptionCode);
1712  assert(exceptionCode == 0);
1713  nodeToInsert = styleElement;
1714  }
1715 
1716  Position pos(selection.start().equivalentDownstreamPosition());
1717  bool atStart = pos.offset() <= pos.node()->caretMinOffset();
1718  bool atEndOfBlock = pos.isLastRenderedPositionInEditableBlock();
1719 
1720 #ifdef DEBUG_COMMANDS
1721  kDebug() << "[pos]" << pos << atStart << atEndOfBlock << endl;
1722 #endif
1723 
1724  if (atEndOfBlock) {
1725 #ifdef DEBUG_COMMANDS
1726  kDebug(6200) << "input newline case 1";
1727 #endif
1728  // Insert an "extra" BR at the end of the block. This makes the "real" BR we want
1729  // to insert appear in the rendering without any significant side effects (and no
1730  // real worries either since you can't arrow past this extra one.
1731  insertNodeAfterPosition(nodeToInsert, pos);
1732  exceptionCode = 0;
1733  ElementImpl *extraBreakNode = document()->createHTMLElement("BR");
1734 // assert(exceptionCode == 0);
1735  insertNodeAfter(extraBreakNode, nodeToInsert);
1736  setEndingSelection(Position(extraBreakNode, 0));
1737  } else if (atStart) {
1738 #ifdef DEBUG_COMMANDS
1739  kDebug(6200) << "input newline case 2";
1740 #endif
1741  // Insert node, but place the caret into index 0 of the downstream
1742  // position. This will make the caret appear after the break, and as we know
1743  // there is content at that location, this is OK.
1744  insertNodeBeforePosition(nodeToInsert, pos);
1745  setEndingSelection(Position(pos.node(), 0));
1746  } else {
1747  // Split a text node
1748  // FIXME it's possible that we create empty text node now if we're at the end of text
1749  // maybe we should handle this case specially and not create it
1750 #ifdef DEBUG_COMMANDS
1751  kDebug(6200) << "input newline case 3";
1752 #endif
1753  assert(pos.node()->isTextNode());
1754  TextImpl *textNode = static_cast<TextImpl *>(pos.node());
1755  TextImpl *textBeforeNode = document()->createTextNode(textNode->substringData(0, selection.start().offset(), exceptionCode));
1756  deleteText(textNode, 0, selection.start().offset());
1757  insertNodeBefore(textBeforeNode, textNode);
1758  insertNodeBefore(nodeToInsert, textNode);
1759  setEndingSelection(Position(textNode, 0));
1760  }
1761 }
1762 
1763 //------------------------------------------------------------------------------------------
1764 // InputTextCommandImpl
1765 
1766 InputTextCommandImpl::InputTextCommandImpl(DocumentImpl *document)
1767  : CompositeEditCommandImpl(document), m_charactersAdded(0)
1768 {
1769 }
1770 
1771 InputTextCommandImpl::~InputTextCommandImpl()
1772 {
1773 }
1774 
1775 void InputTextCommandImpl::doApply()
1776 {
1777 }
1778 
1779 void InputTextCommandImpl::input(const DOMString &text)
1780 {
1781  execute(text);
1782 }
1783 
1784 void InputTextCommandImpl::deleteCharacter()
1785 {
1786  assert(state() == Applied);
1787 
1788  Selection selection = endingSelection();
1789 
1790  if (!selection.start().node()->isTextNode())
1791  return;
1792 
1793  int exceptionCode = 0;
1794  int offset = selection.start().offset() - 1;
1795  if (offset >= selection.start().node()->caretMinOffset()) {
1796  TextImpl *textNode = static_cast<TextImpl *>(selection.start().node());
1797  textNode->deleteData(offset, 1, exceptionCode);
1798  assert(exceptionCode == 0);
1799  selection = Selection(Position(textNode, offset));
1800  setEndingSelection(selection);
1801  m_charactersAdded--;
1802  }
1803 }
1804 
1805 Position InputTextCommandImpl::prepareForTextInsertion(bool adjustDownstream)
1806 {
1807  // Prepare for text input by looking at the current position.
1808  // It may be necessary to insert a text node to receive characters.
1809  Selection selection = endingSelection();
1810  assert(selection.state() == Selection::CARET);
1811 
1812 #ifdef DEBUG_COMMANDS
1813  kDebug() << "[prepare selection]" << selection << endl;
1814 #endif
1815 
1816  Position pos = selection.start();
1817  if (adjustDownstream)
1818  pos = pos.equivalentDownstreamPosition();
1819  else
1820  pos = pos.equivalentUpstreamPosition();
1821 
1822 #ifdef DEBUG_COMMANDS
1823  kDebug() << "[prepare position]" << pos << endl;
1824 #endif
1825 
1826  if (!pos.node()->isTextNode()) {
1827  NodeImpl *textNode = document()->createEditingTextNode("");
1828  NodeImpl *nodeToInsert = textNode;
1829  if (document()->part()->editor()->typingStyle()) {
1830  int exceptionCode = 0;
1831  ElementImpl *styleElement = createTypingStyleElement();
1832  styleElement->appendChild(textNode, exceptionCode);
1833  assert(exceptionCode == 0);
1834  nodeToInsert = styleElement;
1835  }
1836 
1837  // Now insert the node in the right place
1838  if (pos.node()->isEditableBlock()) {
1839  kDebug(6200) << "prepareForTextInsertion case 1";
1840  appendNode(pos.node(), nodeToInsert);
1841  } else if (pos.node()->id() == ID_BR && pos.offset() == 1) {
1842  kDebug(6200) << "prepareForTextInsertion case 2";
1843  insertNodeAfter(nodeToInsert, pos.node());
1844  } else if (pos.node()->caretMinOffset() == pos.offset()) {
1845  kDebug(6200) << "prepareForTextInsertion case 3";
1846  insertNodeBefore(nodeToInsert, pos.node());
1847  } else if (pos.node()->caretMaxOffset() == pos.offset()) {
1848  kDebug(6200) << "prepareForTextInsertion case 4";
1849  insertNodeAfter(nodeToInsert, pos.node());
1850  } else
1851  assert(false);
1852 
1853  pos = Position(textNode, 0);
1854  } else {
1855  // Handle the case where there is a typing style.
1856  if (document()->part()->editor()->typingStyle()) {
1857  if (pos.node()->isTextNode() && pos.offset() > pos.node()->caretMinOffset() && pos.offset() < pos.node()->caretMaxOffset()) {
1858  // Need to split current text node in order to insert a span.
1859  TextImpl *text = static_cast<TextImpl *>(pos.node());
1860  RefPtr<SplitTextNodeCommandImpl> cmd = new SplitTextNodeCommandImpl(document(), text, pos.offset());
1861  applyCommandToComposite(cmd);
1862  setEndingSelection(Position(cmd->node(), 0));
1863  }
1864 
1865  int exceptionCode = 0;
1866  TextImpl *editingTextNode = document()->createEditingTextNode("");
1867 
1868  ElementImpl *styleElement = createTypingStyleElement();
1869  styleElement->appendChild(editingTextNode, exceptionCode);
1870  assert(exceptionCode == 0);
1871 
1872  NodeImpl *node = endingSelection().start().node();
1873  if (endingSelection().start().isLastRenderedPositionOnLine())
1874  insertNodeAfter(styleElement, node);
1875  else
1876  insertNodeBefore(styleElement, node);
1877  pos = Position(editingTextNode, 0);
1878  }
1879  }
1880  return pos;
1881 }
1882 
1883 void InputTextCommandImpl::execute(const DOMString &text)
1884 {
1885 #ifdef DEBUG_COMMANDS
1886  kDebug() << "[execute command]" << text << endl;
1887 #endif
1888  Selection selection = endingSelection();
1889 #ifdef DEBUG_COMMANDS
1890  kDebug() << "[ending selection]" << selection << endl;
1891 #endif
1892  bool adjustDownstream = selection.start().isFirstRenderedPositionOnLine();
1893 #ifdef DEBUG_COMMANDS
1894  kDebug() << "[adjust]" << adjustDownstream << endl;
1895 #endif
1896 
1897 #ifdef DEBUG_COMMANDS
1898  printEnclosingBlockTree(selection.start().node());
1899 #endif
1900 
1901  // Delete the current selection, or collapse whitespace, as needed
1902  if (selection.state() == Selection::RANGE)
1903  deleteSelection();
1904  else
1905  deleteCollapsibleWhitespace();
1906 
1907 #ifdef DEBUG_COMMANDS
1908  kDebug() << "[after collapsible whitespace deletion]" << endl;
1909  printEnclosingBlockTree(selection.start().node());
1910 #endif
1911 
1912  // EDIT FIXME: Need to take typing style from upstream text, if any.
1913 
1914  // Make sure the document is set up to receive text
1915  Position pos = prepareForTextInsertion(adjustDownstream);
1916 #ifdef DEBUG_COMMANDS
1917  kDebug() << "[after prepare]" << pos << endl;
1918 #endif
1919 
1920  TextImpl *textNode = static_cast<TextImpl *>(pos.node());
1921  long offset = pos.offset();
1922 
1923 #ifdef DEBUG_COMMANDS
1924  kDebug() << "[insert at]" << textNode << offset << endl;
1925 #endif
1926 
1927  // This is a temporary implementation for inserting adjoining spaces
1928  // into a document. We are working on a CSS-related whitespace solution
1929  // that will replace this some day.
1930  if (isWS(text))
1931  insertSpace(textNode, offset);
1932  else {
1933  const DOMString &existingText = textNode->data();
1934  if (textNode->length() >= 2 && offset >= 2 && isNBSP(existingText[offset - 1]) && !isWS(existingText[offset - 2])) {
1935  // DOM looks like this:
1936  // character nbsp caret
1937  // As we are about to insert a non-whitespace character at the caret
1938  // convert the nbsp to a regular space.
1939  // EDIT FIXME: This needs to be improved some day to convert back only
1940  // those nbsp's added by the editor to make rendering come out right.
1941  replaceText(textNode, offset - 1, 1, " ");
1942  }
1943  insertText(textNode, offset, text);
1944  }
1945  setEndingSelection(Position(textNode, offset + text.length()));
1946  m_charactersAdded += text.length();
1947 }
1948 
1949 void InputTextCommandImpl::insertSpace(TextImpl *textNode, unsigned long offset)
1950 {
1951  assert(textNode);
1952 
1953  DOMString text(textNode->data());
1954 
1955  // count up all spaces and newlines in front of the caret
1956  // delete all collapsed ones
1957  // this will work out OK since the offset we have been passed has been upstream-ized
1958  int count = 0;
1959  for (unsigned int i = offset; i < text.length(); i++) {
1960  if (isWS(text[i]))
1961  count++;
1962  else
1963  break;
1964  }
1965  if (count > 0) {
1966  // By checking the character at the downstream position, we can
1967  // check if there is a rendered WS at the caret
1968  Position pos(textNode, offset);
1969  Position downstream = pos.equivalentDownstreamPosition();
1970  if (downstream.offset() < (long)text.length() && isWS(text[downstream.offset()]))
1971  count--; // leave this WS in
1972  if (count > 0)
1973  deleteText(textNode, offset, count);
1974  }
1975 
1976  if (offset > 0 && offset <= text.length() - 1 && !isWS(text[offset]) && !isWS(text[offset - 1])) {
1977  // insert a "regular" space
1978  insertText(textNode, offset, " ");
1979  return;
1980  }
1981 
1982  if (text.length() >= 2 && offset >= 2 && isNBSP(text[offset - 2]) && isNBSP(text[offset - 1])) {
1983  // DOM looks like this:
1984  // nbsp nbsp caret
1985  // insert a space between the two nbsps
1986  insertText(textNode, offset - 1, " ");
1987  return;
1988  }
1989 
1990  // insert an nbsp
1991  insertText(textNode, offset, nonBreakingSpaceString());
1992 }
1993 
1994 //------------------------------------------------------------------------------------------
1995 // InsertNodeBeforeCommandImpl
1996 
1997 InsertNodeBeforeCommandImpl::InsertNodeBeforeCommandImpl(DocumentImpl *document, NodeImpl *insertChild, NodeImpl *refChild)
1998  : EditCommandImpl(document), m_insertChild(insertChild), m_refChild(refChild)
1999 {
2000  assert(m_insertChild);
2001  m_insertChild->ref();
2002 
2003  assert(m_refChild);
2004  m_refChild->ref();
2005 }
2006 
2007 InsertNodeBeforeCommandImpl::~InsertNodeBeforeCommandImpl()
2008 {
2009  if (m_insertChild)
2010  m_insertChild->deref();
2011  if (m_refChild)
2012  m_refChild->deref();
2013 }
2014 
2015 void InsertNodeBeforeCommandImpl::doApply()
2016 {
2017  assert(m_insertChild);
2018  assert(m_refChild);
2019  assert(m_refChild->parentNode());
2020 
2021  int exceptionCode = 0;
2022  m_refChild->parentNode()->insertBefore(m_insertChild, m_refChild, exceptionCode);
2023  assert(exceptionCode == 0);
2024 }
2025 
2026 void InsertNodeBeforeCommandImpl::doUnapply()
2027 {
2028  assert(m_insertChild);
2029  assert(m_refChild);
2030  assert(m_refChild->parentNode());
2031 
2032  int exceptionCode = 0;
2033  m_refChild->parentNode()->removeChild(m_insertChild, exceptionCode);
2034  assert(exceptionCode == 0);
2035 }
2036 
2037 //------------------------------------------------------------------------------------------
2038 // InsertTextCommandImpl
2039 
2040 InsertTextCommandImpl::InsertTextCommandImpl(DocumentImpl *document, TextImpl *node, long offset, const DOMString &text)
2041  : EditCommandImpl(document), m_node(node), m_offset(offset)
2042 {
2043  assert(m_node);
2044  assert(m_offset >= 0);
2045  assert(text.length() > 0);
2046 
2047  m_node->ref();
2048  m_text = text.copy(); // make a copy to ensure that the string never changes
2049 }
2050 
2051 InsertTextCommandImpl::~InsertTextCommandImpl()
2052 {
2053  if (m_node)
2054  m_node->deref();
2055 }
2056 
2057 void InsertTextCommandImpl::doApply()
2058 {
2059  assert(m_node);
2060  assert(!m_text.isEmpty());
2061 
2062  int exceptionCode = 0;
2063  m_node->insertData(m_offset, m_text, exceptionCode);
2064  assert(exceptionCode == 0);
2065 }
2066 
2067 void InsertTextCommandImpl::doUnapply()
2068 {
2069  assert(m_node);
2070  assert(!m_text.isEmpty());
2071 
2072  int exceptionCode = 0;
2073  m_node->deleteData(m_offset, m_text.length(), exceptionCode);
2074  assert(exceptionCode == 0);
2075 }
2076 
2077 //------------------------------------------------------------------------------------------
2078 // JoinTextNodesCommandImpl
2079 
2080 JoinTextNodesCommandImpl::JoinTextNodesCommandImpl(DocumentImpl *document, TextImpl *text1, TextImpl *text2)
2081  : EditCommandImpl(document), m_text1(text1), m_text2(text2)
2082 {
2083  assert(m_text1);
2084  assert(m_text2);
2085  assert(m_text1->nextSibling() == m_text2);
2086  assert(m_text1->length() > 0);
2087  assert(m_text2->length() > 0);
2088 
2089  m_text1->ref();
2090  m_text2->ref();
2091 }
2092 
2093 JoinTextNodesCommandImpl::~JoinTextNodesCommandImpl()
2094 {
2095  if (m_text1)
2096  m_text1->deref();
2097  if (m_text2)
2098  m_text2->deref();
2099 }
2100 
2101 void JoinTextNodesCommandImpl::doApply()
2102 {
2103  assert(m_text1);
2104  assert(m_text2);
2105  assert(m_text1->nextSibling() == m_text2);
2106 
2107  int exceptionCode = 0;
2108  m_text2->insertData(0, m_text1->data(), exceptionCode);
2109  assert(exceptionCode == 0);
2110 
2111  m_text2->parentNode()->removeChild(m_text1, exceptionCode);
2112  assert(exceptionCode == 0);
2113 
2114  m_offset = m_text1->length();
2115 }
2116 
2117 void JoinTextNodesCommandImpl::doUnapply()
2118 {
2119  assert(m_text2);
2120  assert(m_offset > 0);
2121 
2122  int exceptionCode = 0;
2123 
2124  m_text2->deleteData(0, m_offset, exceptionCode);
2125  assert(exceptionCode == 0);
2126 
2127  m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
2128  assert(exceptionCode == 0);
2129 
2130  assert(m_text2->previousSibling()->isTextNode());
2131  assert(m_text2->previousSibling() == m_text1);
2132 }
2133 
2134 //------------------------------------------------------------------------------------------
2135 // ReplaceSelectionCommandImpl
2136 
2137 ReplaceSelectionCommandImpl::ReplaceSelectionCommandImpl(DocumentImpl *document, DOM::DocumentFragmentImpl *fragment, bool selectReplacement)
2138  : CompositeEditCommandImpl(document), m_fragment(fragment), m_selectReplacement(selectReplacement)
2139 {
2140 }
2141 
2142 ReplaceSelectionCommandImpl::~ReplaceSelectionCommandImpl()
2143 {
2144 }
2145 
2146 void ReplaceSelectionCommandImpl::doApply()
2147 {
2148  NodeImpl *firstChild = m_fragment->firstChild();
2149  NodeImpl *lastChild = m_fragment->lastChild();
2150 
2151  Selection selection = endingSelection();
2152 
2153  // Delete the current selection, or collapse whitespace, as needed
2154  if (selection.state() == Selection::RANGE)
2155  deleteSelection();
2156  else
2157  deleteCollapsibleWhitespace();
2158 
2159  selection = endingSelection();
2160  assert(!selection.isEmpty());
2161 
2162  if (!firstChild) {
2163  // Pasting something that didn't parse or was empty.
2164  assert(!lastChild);
2165  } else if (firstChild == lastChild && firstChild->isTextNode()) {
2166  // Simple text paste. Treat as if the text were typed.
2167  Position base = selection.base();
2168  inputText(static_cast<TextImpl *>(firstChild)->data());
2169  if (m_selectReplacement) {
2170  setEndingSelection(Selection(base, endingSelection().extent()));
2171  }
2172  }
2173  else {
2174  // HTML fragment paste.
2175  NodeImpl *beforeNode = firstChild;
2176  NodeImpl *node = firstChild->nextSibling();
2177 
2178  insertNodeAt(firstChild, selection.start().node(), selection.start().offset());
2179 
2180  // Insert the nodes from the fragment
2181  while (node) {
2182  NodeImpl *next = node->nextSibling();
2183  insertNodeAfter(node, beforeNode);
2184  beforeNode = node;
2185  node = next;
2186  }
2187  assert(beforeNode);
2188 
2189  // Find the last leaf.
2190  NodeImpl *lastLeaf = lastChild;
2191  while (1) {
2192  NodeImpl *nextChild = lastLeaf->lastChild();
2193  if (!nextChild)
2194  break;
2195  lastLeaf = nextChild;
2196  }
2197 
2198  if (m_selectReplacement) {
2199  // Find the first leaf.
2200  NodeImpl *firstLeaf = firstChild;
2201  while (1) {
2202  NodeImpl *nextChild = firstLeaf->firstChild();
2203  if (!nextChild)
2204  break;
2205  firstLeaf = nextChild;
2206  }
2207  // Select what was inserted.
2208  setEndingSelection(Selection(Position(firstLeaf, firstLeaf->caretMinOffset()), Position(lastLeaf, lastLeaf->caretMaxOffset())));
2209  } else {
2210  // Place the cursor after what was inserted.
2211  setEndingSelection(Position(lastLeaf, lastLeaf->caretMaxOffset()));
2212  }
2213  }
2214 }
2215 
2216 //------------------------------------------------------------------------------------------
2217 // MoveSelectionCommandImpl
2218 
2219 MoveSelectionCommandImpl::MoveSelectionCommandImpl(DocumentImpl *document, DOM::DocumentFragmentImpl *fragment, DOM::Position &position)
2220 : CompositeEditCommandImpl(document), m_fragment(fragment), m_position(position)
2221 {
2222 }
2223 
2224 MoveSelectionCommandImpl::~MoveSelectionCommandImpl()
2225 {
2226 }
2227 
2228 void MoveSelectionCommandImpl::doApply()
2229 {
2230  Selection selection = endingSelection();
2231  assert(selection.state() == Selection::RANGE);
2232 
2233  // Update the position otherwise it may become invalid after the selection is deleted.
2234  NodeImpl *positionNode = m_position.node();
2235  long positionOffset = m_position.offset();
2236  Position selectionEnd = selection.end();
2237  long selectionEndOffset = selectionEnd.offset();
2238  if (selectionEnd.node() == positionNode && selectionEndOffset < positionOffset) {
2239  positionOffset -= selectionEndOffset;
2240  Position selectionStart = selection.start();
2241  if (selectionStart.node() == positionNode) {
2242  positionOffset += selectionStart.offset();
2243  }
2244  }
2245 
2246  deleteSelection();
2247 
2248  setEndingSelection(Position(positionNode, positionOffset));
2249  RefPtr<ReplaceSelectionCommandImpl> cmd = new ReplaceSelectionCommandImpl(document(), m_fragment, true);
2250  applyCommandToComposite(cmd);
2251 }
2252 
2253 //------------------------------------------------------------------------------------------
2254 // RemoveCSSPropertyCommandImpl
2255 
2256 RemoveCSSPropertyCommandImpl::RemoveCSSPropertyCommandImpl(DocumentImpl *document, CSSStyleDeclarationImpl *decl, int property)
2257  : EditCommandImpl(document), m_decl(decl), m_property(property), m_important(false)
2258 {
2259  assert(m_decl);
2260  m_decl->ref();
2261 }
2262 
2263 RemoveCSSPropertyCommandImpl::~RemoveCSSPropertyCommandImpl()
2264 {
2265  assert(m_decl);
2266  m_decl->deref();
2267 }
2268 
2269 void RemoveCSSPropertyCommandImpl::doApply()
2270 {
2271  assert(m_decl);
2272 
2273  m_oldValue = m_decl->getPropertyValue(m_property);
2274  assert(!m_oldValue.isNull());
2275 
2276  m_important = m_decl->getPropertyPriority(m_property);
2277  m_decl->removeProperty(m_property);
2278 }
2279 
2280 void RemoveCSSPropertyCommandImpl::doUnapply()
2281 {
2282  assert(m_decl);
2283  assert(!m_oldValue.isNull());
2284 
2285  m_decl->setProperty(m_property, m_oldValue, m_important);
2286 }
2287 
2288 //------------------------------------------------------------------------------------------
2289 // RemoveNodeAttributeCommandImpl
2290 
2291 RemoveNodeAttributeCommandImpl::RemoveNodeAttributeCommandImpl(DocumentImpl *document, ElementImpl *element, NodeImpl::Id attribute)
2292  : EditCommandImpl(document), m_element(element), m_attribute(attribute)
2293 {
2294  assert(m_element);
2295  m_element->ref();
2296 }
2297 
2298 RemoveNodeAttributeCommandImpl::~RemoveNodeAttributeCommandImpl()
2299 {
2300  assert(m_element);
2301  m_element->deref();
2302 }
2303 
2304 void RemoveNodeAttributeCommandImpl::doApply()
2305 {
2306  assert(m_element);
2307 
2308  m_oldValue = m_element->getAttribute(m_attribute);
2309  assert(!m_oldValue.isNull());
2310 
2311  int exceptionCode = 0;
2312  m_element->removeAttribute(m_attribute, exceptionCode);
2313  assert(exceptionCode == 0);
2314 }
2315 
2316 void RemoveNodeAttributeCommandImpl::doUnapply()
2317 {
2318  assert(m_element);
2319  assert(!m_oldValue.isNull());
2320 
2321 // int exceptionCode = 0;
2322  m_element->setAttribute(m_attribute, m_oldValue.implementation());
2323 // assert(exceptionCode == 0);
2324 }
2325 
2326 //------------------------------------------------------------------------------------------
2327 // RemoveNodeCommandImpl
2328 
2329 RemoveNodeCommandImpl::RemoveNodeCommandImpl(DocumentImpl *document, NodeImpl *removeChild)
2330  : EditCommandImpl(document), m_parent(0), m_removeChild(removeChild), m_refChild(0)
2331 {
2332  assert(m_removeChild);
2333  m_removeChild->ref();
2334 
2335  m_parent = m_removeChild->parentNode();
2336  assert(m_parent);
2337  m_parent->ref();
2338 
2339  RefPtr<DOM::NodeListImpl> children = m_parent->childNodes();
2340  for (long i = children->length() - 1; i >= 0; --i) {
2341  NodeImpl *node = children->item(i);
2342  if (node == m_removeChild)
2343  break;
2344  m_refChild = node;
2345  }
2346 
2347  if (m_refChild)
2348  m_refChild->ref();
2349 }
2350 
2351 RemoveNodeCommandImpl::~RemoveNodeCommandImpl()
2352 {
2353  if (m_parent)
2354  m_parent->deref();
2355  if (m_removeChild)
2356  m_removeChild->deref();
2357  if (m_refChild)
2358  m_refChild->deref();
2359 }
2360 
2361 void RemoveNodeCommandImpl::doApply()
2362 {
2363  assert(m_parent);
2364  assert(m_removeChild);
2365 
2366  int exceptionCode = 0;
2367  m_parent->removeChild(m_removeChild, exceptionCode);
2368  assert(exceptionCode == 0);
2369 }
2370 
2371 void RemoveNodeCommandImpl::doUnapply()
2372 {
2373  assert(m_parent);
2374  assert(m_removeChild);
2375 
2376  int exceptionCode = 0;
2377  if (m_refChild)
2378  m_parent->insertBefore(m_removeChild, m_refChild, exceptionCode);
2379  else
2380  m_parent->appendChild(m_removeChild, exceptionCode);
2381  assert(exceptionCode == 0);
2382 }
2383 
2384 //------------------------------------------------------------------------------------------
2385 // RemoveNodeAndPruneCommandImpl
2386 
2387 RemoveNodeAndPruneCommandImpl::RemoveNodeAndPruneCommandImpl(DocumentImpl *document, NodeImpl *pruneNode, NodeImpl *stopNode)
2388  : CompositeEditCommandImpl(document), m_pruneNode(pruneNode), m_stopNode(stopNode)
2389 {
2390  assert(m_pruneNode);
2391  m_pruneNode->ref();
2392  if (m_stopNode)
2393  m_stopNode->ref();
2394 }
2395 
2396 RemoveNodeAndPruneCommandImpl::~RemoveNodeAndPruneCommandImpl()
2397 {
2398  m_pruneNode->deref();
2399  if (m_stopNode)
2400  m_stopNode->deref();
2401 }
2402 
2403 void RemoveNodeAndPruneCommandImpl::doApply()
2404 {
2405  NodeImpl *editableBlock = m_pruneNode->enclosingBlockFlowElement();
2406  NodeImpl *pruneNode = m_pruneNode;
2407  NodeImpl *node = pruneNode->traversePreviousNode();
2408  removeNode(pruneNode);
2409  while (1) {
2410  if (node == m_stopNode || editableBlock != node->enclosingBlockFlowElement() || !shouldPruneNode(node))
2411  break;
2412  pruneNode = node;
2413  node = node->traversePreviousNode();
2414  removeNode(pruneNode);
2415  }
2416 }
2417 
2418 //------------------------------------------------------------------------------------------
2419 // RemoveNodePreservingChildrenCommandImpl
2420 
2421 RemoveNodePreservingChildrenCommandImpl::RemoveNodePreservingChildrenCommandImpl(DocumentImpl *document, NodeImpl *node)
2422  : CompositeEditCommandImpl(document), m_node(node)
2423 {
2424  assert(m_node);
2425  m_node->ref();
2426 }
2427 
2428 RemoveNodePreservingChildrenCommandImpl::~RemoveNodePreservingChildrenCommandImpl()
2429 {
2430  if (m_node)
2431  m_node->deref();
2432 }
2433 
2434 void RemoveNodePreservingChildrenCommandImpl::doApply()
2435 {
2436  RefPtr<DOM::NodeListImpl> children = node()->childNodes();
2437  const unsigned int length = children->length();
2438  for (unsigned int i = 0; i < length; ++i) {
2439  NodeImpl *child = children->item(0);
2440  removeNode(child);
2441  insertNodeBefore(child, node());
2442  }
2443  removeNode(node());
2444 }
2445 
2446 //------------------------------------------------------------------------------------------
2447 // SetNodeAttributeCommandImpl
2448 
2449 SetNodeAttributeCommandImpl::SetNodeAttributeCommandImpl(DocumentImpl *document, ElementImpl *element, NodeImpl::Id attribute, const DOMString &value)
2450  : EditCommandImpl(document), m_element(element), m_attribute(attribute), m_value(value)
2451 {
2452  assert(m_element);
2453  m_element->ref();
2454  assert(!m_value.isNull());
2455 }
2456 
2457 SetNodeAttributeCommandImpl::~SetNodeAttributeCommandImpl()
2458 {
2459  if (m_element)
2460  m_element->deref();
2461 }
2462 
2463 void SetNodeAttributeCommandImpl::doApply()
2464 {
2465  assert(m_element);
2466  assert(!m_value.isNull());
2467 
2468 // int exceptionCode = 0;
2469  m_oldValue = m_element->getAttribute(m_attribute);
2470  m_element->setAttribute(m_attribute, m_value.implementation());
2471 // assert(exceptionCode == 0);
2472 }
2473 
2474 void SetNodeAttributeCommandImpl::doUnapply()
2475 {
2476  assert(m_element);
2477  assert(!m_oldValue.isNull());
2478 
2479 // int exceptionCode = 0;
2480  m_element->setAttribute(m_attribute, m_oldValue.implementation());
2481 // assert(exceptionCode == 0);
2482 }
2483 
2484 //------------------------------------------------------------------------------------------
2485 // SplitTextNodeCommandImpl
2486 
2487 SplitTextNodeCommandImpl::SplitTextNodeCommandImpl(DocumentImpl *document, TextImpl *text, long offset)
2488  : EditCommandImpl(document), m_text1(0), m_text2(text), m_offset(offset)
2489 {
2490  assert(m_text2);
2491  assert(m_text2->length() > 0);
2492 
2493  m_text2->ref();
2494 }
2495 
2496 SplitTextNodeCommandImpl::~SplitTextNodeCommandImpl()
2497 {
2498  if (m_text1)
2499  m_text1->deref();
2500  if (m_text2)
2501  m_text2->deref();
2502 }
2503 
2504 void SplitTextNodeCommandImpl::doApply()
2505 {
2506  assert(m_text2);
2507  assert(m_offset > 0);
2508 
2509  int exceptionCode = 0;
2510 
2511  // EDIT FIXME: This should use better smarts for figuring out which portion
2512  // of the split to copy (based on their comparative sizes). We should also
2513  // just use the DOM's splitText function.
2514 
2515  if (!m_text1) {
2516  // create only if needed.
2517  // if reapplying, this object will already exist.
2518  m_text1 = document()->createTextNode(m_text2->substringData(0, m_offset, exceptionCode));
2519  assert(exceptionCode == 0);
2520  assert(m_text1);
2521  m_text1->ref();
2522  }
2523 
2524  m_text2->deleteData(0, m_offset, exceptionCode);
2525  assert(exceptionCode == 0);
2526 
2527  m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
2528  assert(exceptionCode == 0);
2529 
2530  assert(m_text2->previousSibling()->isTextNode());
2531  assert(m_text2->previousSibling() == m_text1);
2532 }
2533 
2534 void SplitTextNodeCommandImpl::doUnapply()
2535 {
2536  assert(m_text1);
2537  assert(m_text2);
2538 
2539  assert(m_text1->nextSibling() == m_text2);
2540 
2541  int exceptionCode = 0;
2542  m_text2->insertData(0, m_text1->data(), exceptionCode);
2543  assert(exceptionCode == 0);
2544 
2545  m_text2->parentNode()->removeChild(m_text1, exceptionCode);
2546  assert(exceptionCode == 0);
2547 
2548  m_offset = m_text1->length();
2549 }
2550 
2551 //------------------------------------------------------------------------------------------
2552 // TypingCommandImpl
2553 
2554 TypingCommandImpl::TypingCommandImpl(DocumentImpl *document)
2555  : CompositeEditCommandImpl(document), m_openForMoreTyping(true)
2556 {
2557 }
2558 
2559 TypingCommandImpl::~TypingCommandImpl()
2560 {
2561 }
2562 
2563 void TypingCommandImpl::doApply()
2564 {
2565 }
2566 
2567 void TypingCommandImpl::typingAddedToOpenCommand()
2568 {
2569  assert(document());
2570  assert(document()->part());
2571  document()->part()->editor()->appliedEditing(this);
2572 }
2573 
2574 void TypingCommandImpl::insertText(const DOMString &text)
2575 {
2576  if (document()->part()->editor()->typingStyle() || m_cmds.count() == 0) {
2577  RefPtr<InputTextCommandImpl> cmd = new InputTextCommandImpl(document());
2578  applyCommandToComposite(cmd);
2579  cmd->input(text);
2580  } else {
2581  EditCommandImpl *lastCommand = m_cmds.last().get();
2582  if (lastCommand->isInputTextCommand()) {
2583  static_cast<InputTextCommandImpl*>(lastCommand)->input(text);
2584  } else {
2585  RefPtr<InputTextCommandImpl> cmd = new InputTextCommandImpl(document());
2586  applyCommandToComposite(cmd);
2587  cmd->input(text);
2588  }
2589  }
2590  typingAddedToOpenCommand();
2591 }
2592 
2593 void TypingCommandImpl::insertNewline()
2594 {
2595  RefPtr<InputNewlineCommandImpl> cmd = new InputNewlineCommandImpl(document());
2596  applyCommandToComposite(cmd);
2597  typingAddedToOpenCommand();
2598 }
2599 
2600 void TypingCommandImpl::issueCommandForDeleteKey()
2601 {
2602  Selection selectionToDelete = endingSelection();
2603  assert(selectionToDelete.state() != Selection::NONE);
2604 
2605 #ifdef DEBUG_COMMANDS
2606  kDebug() << "[selection]" << selectionToDelete << endl;
2607 #endif
2608  if (selectionToDelete.state() == Selection::CARET) {
2609 #ifdef DEBUG_COMMANDS
2610  kDebug() << "[caret selection]" << endl;
2611 #endif
2612  Position pos(selectionToDelete.start());
2613  if (pos.inFirstEditableInRootEditableElement() && pos.offset() <= pos.node()->caretMinOffset()) {
2614  // we're at the start of a root editable block...do nothing
2615  return;
2616  }
2617  selectionToDelete = Selection(pos.previousCharacterPosition(), pos);
2618 #ifdef DEBUG_COMMANDS
2619  kDebug() << "[modified selection]" << selectionToDelete << endl;
2620 #endif
2621  }
2622  deleteSelection(selectionToDelete);
2623  typingAddedToOpenCommand();
2624 }
2625 
2626 void TypingCommandImpl::deleteKeyPressed()
2627 {
2628 // EDIT FIXME: The ifdef'ed out code below should be re-enabled.
2629 // In order for this to happen, the deleteCharacter case
2630 // needs work. Specifically, the caret-positioning code
2631 // and whitespace-handling code in DeleteSelectionCommandImpl::doApply()
2632 // needs to be factored out so it can be used again here.
2633 // Until that work is done, issueCommandForDeleteKey() does the
2634 // right thing, but less efficiently and with the cost of more
2635 // objects.
2636  issueCommandForDeleteKey();
2637 #if 0
2638  if (m_cmds.count() == 0) {
2639  issueCommandForDeleteKey();
2640  }
2641  else {
2642  EditCommand lastCommand = m_cmds.last();
2643  if (lastCommand.commandID() == InputTextCommandID) {
2644  InputTextCommand cmd = static_cast<InputTextCommand &>(lastCommand);
2645  cmd.deleteCharacter();
2646  if (cmd.charactersAdded() == 0) {
2647  removeCommand(cmd);
2648  }
2649  }
2650  else if (lastCommand.commandID() == InputNewlineCommandID) {
2651  lastCommand.unapply();
2652  removeCommand(lastCommand);
2653  }
2654  else {
2655  issueCommandForDeleteKey();
2656  }
2657  }
2658 #endif
2659 }
2660 
2661 void TypingCommandImpl::removeCommand(const PassRefPtr<EditCommandImpl> cmd)
2662 {
2663  // NOTE: If the passed-in command is the last command in the
2664  // composite, we could remove all traces of this typing command
2665  // from the system, including the undo chain. Other editors do
2666  // not do this, but we could.
2667 
2668  m_cmds.removeAll(cmd);
2669  if (m_cmds.count() == 0)
2670  setEndingSelection(startingSelection());
2671  else
2672  setEndingSelection(m_cmds.last()->endingSelection());
2673 }
2674 
2675 static bool isOpenForMoreTypingCommand(const EditCommandImpl *command)
2676 {
2677  return command && command->isTypingCommand() &&
2678  static_cast<const TypingCommandImpl*>(command)->openForMoreTyping();
2679 }
2680 
2681 void TypingCommandImpl::deleteKeyPressed0(DocumentImpl *document)
2682 {
2683  //Editor *editor = document->part()->editor();
2684  // FIXME reenable after properly modify selection of the lastEditCommand
2685  // if (isOpenForMoreTypingCommand(lastEditCommand)) {
2686  // static_cast<TypingCommand &>(lastEditCommand).deleteKeyPressed();
2687  // } else {
2688  RefPtr<TypingCommandImpl> command = new TypingCommandImpl(document);
2689  command->apply();
2690  command->deleteKeyPressed();
2691  // }
2692 }
2693 
2694 void TypingCommandImpl::insertNewline0(DocumentImpl *document)
2695 {
2696  assert(document);
2697  Editor *ed = document->part()->editor();
2698  assert(ed);
2699  EditCommandImpl *lastEditCommand = ed->lastEditCommand().get();
2700  if (isOpenForMoreTypingCommand(lastEditCommand)) {
2701  static_cast<TypingCommandImpl*>(lastEditCommand)->insertNewline();
2702  } else {
2703  RefPtr<TypingCommandImpl> command = new TypingCommandImpl(document);
2704  command->apply();
2705  command->insertNewline();
2706  }
2707 }
2708 
2709 void TypingCommandImpl::insertText0(DocumentImpl *document, const DOMString &text)
2710 {
2711 #ifdef DEBUG_COMMANDS
2712  kDebug() << "[insert text]" << text << endl;
2713 #endif
2714  assert(document);
2715  Editor *ed = document->part()->editor();
2716  assert(ed);
2717  EditCommandImpl *lastEditCommand = ed->lastEditCommand().get();
2718  if (isOpenForMoreTypingCommand(lastEditCommand)) {
2719  static_cast<TypingCommandImpl*>(lastEditCommand)->insertText(text);
2720  } else {
2721  RefPtr<TypingCommandImpl> command = new TypingCommandImpl(document);
2722  command->apply();
2723  command->insertText(text);
2724  }
2725 }
2726 
2727 
2728 //------------------------------------------------------------------------------------------
2729 // InsertListCommandImpl
2730 
2731 InsertListCommandImpl::InsertListCommandImpl(DocumentImpl *document, Type type)
2732  : CompositeEditCommandImpl(document), m_listType(type)
2733 {
2734 }
2735 
2736 InsertListCommandImpl::~InsertListCommandImpl()
2737 {
2738 }
2739 
2740 void InsertListCommandImpl::doApply()
2741 {
2742 #ifdef DEBUG_COMMANDS
2743  kDebug() << "[make current selection/paragraph a list]" << endingSelection() << endl;
2744 #endif
2745  Position start = endingSelection().start();
2746  Position end = endingSelection().end();
2747  ElementImpl *startBlock = start.node()->enclosingBlockFlowElement();
2748  ElementImpl *endBlock = end.node()->enclosingBlockFlowElement();
2749 #ifdef DEBUG_COMMANDS
2750  kDebug() << "[start:end blocks]" << startBlock << endBlock << endl;
2751  printEnclosingBlockTree(start.node());
2752 #endif
2753  if (startBlock == endBlock) {
2754  if (startBlock->id() == ID_LI) {
2755  // we already have a list item, remove it then
2756 #ifdef DEBUG_COMMANDS
2757  kDebug() << "[remove list item]" << endl;
2758 #endif
2759  NodeImpl *listBlock = startBlock->parent(); // it's either <ol> or <ul>
2760  // we need to properly split or even remove the list leaving 2 lists:
2761  // [listBlock->firstChild(), startBlock) and (startBlock, listBlock->lastChild()]
2762  if (listBlock->firstChild() == listBlock->lastChild() && listBlock->firstChild() == startBlock) {
2763  // get rid of list completely
2764 #ifdef DEBUG_COMMANDS
2765  kDebug() << "[remove list completely]" << endl;
2766 #endif
2767  removeNodePreservingChildren(listBlock);
2768  removeNodePreservingChildren(startBlock);
2769  } else if (!startBlock->previousSibling()) {
2770  // move nodes from this list item before the list
2771  NodeImpl *nextSibling;
2772  for (NodeImpl *node = startBlock->firstChild(); node; node = nextSibling) {
2773  nextSibling = node->nextSibling();
2774  removeNode(node);
2775  insertNodeBefore(node, listBlock);
2776  }
2777  removeNode(startBlock);
2778  } else if (!startBlock->nextSibling()) {
2779  // move nodes from this list item after the list
2780  NodeImpl *nextSibling;
2781  for (NodeImpl *node = startBlock->lastChild(); node; node = nextSibling) {
2782  nextSibling = node->previousSibling();
2783  removeNode(node);
2784  insertNodeAfter(node, listBlock);
2785  }
2786  removeNode(startBlock);
2787  } else {
2788  // split list into 2 and nodes from this list item goes between lists
2789  WTF::PassRefPtr<NodeImpl> newListBlock = listBlock->cloneNode(false);
2790  insertNodeAfter(newListBlock.get(), listBlock);
2791  NodeImpl *node, *nextSibling;
2792  for (node = startBlock->nextSibling(); node; node = nextSibling) {
2793  nextSibling = node->nextSibling();
2794  removeNode(node);
2795  appendNode(newListBlock.get(), node);
2796  }
2797  for (node = startBlock->firstChild(); node; node = nextSibling) {
2798  nextSibling = node->nextSibling();
2799  removeNode(node);
2800  insertNodeBefore(node, newListBlock.get());
2801  }
2802  removeNode(startBlock);
2803  }
2804  } else {
2805  ElementImpl *ol = document()->createHTMLElement(m_listType == OrderedList ? "OL" : "UL");
2806  ElementImpl *li = document()->createHTMLElement("LI");
2807  appendNode(ol, li);
2808  NodeImpl *nextNode;
2809  for (NodeImpl *node = startBlock->firstChild(); node; node = nextNode) {
2810 #ifdef DEBUG_COMMANDS
2811  kDebug() << "[reattach node]" << node << endl;
2812 #endif
2813  nextNode = node->nextSibling();
2814  removeNode(node);
2815  appendNode(li, node);
2816  }
2817  appendNode(startBlock, ol);
2818  }
2819  } else {
2820 #ifdef DEBUG_COMMANDS
2821  kDebug() << "[different blocks are not supported yet]" << endl;
2822 #endif
2823  }
2824 }
2825 
2826 void InsertListCommandImpl::insertList(DocumentImpl *document, Type type)
2827 {
2828  RefPtr<InsertListCommandImpl> insertCommand = new InsertListCommandImpl(document, type);
2829  insertCommand->apply();
2830 }
2831 
2832 //------------------------------------------------------------------------------------------
2833 
2834 //------------------------------------------------------------------------------------------
2835 // IndentOutdentCommandImpl
2836 
2837 IndentOutdentCommandImpl::IndentOutdentCommandImpl(DocumentImpl *document, Type type)
2838  : CompositeEditCommandImpl(document), m_commandType(type)
2839 {
2840 }
2841 
2842 IndentOutdentCommandImpl::~IndentOutdentCommandImpl()
2843 {
2844 }
2845 
2846 void IndentOutdentCommandImpl::indent()
2847 {
2848  Selection selection = endingSelection();
2849 #ifdef DEBUG_COMMANDS
2850  kDebug() << "[indent selection]" << selection << endl;
2851 #endif
2852  NodeImpl *startBlock = selection.start().node()->enclosingBlockFlowElement();
2853  NodeImpl *endBlock = selection.end().node()->enclosingBlockFlowElement();
2854 
2855  if (startBlock == endBlock) {
2856  // check if selection is the list, but not fully covered
2857  if (startBlock->id() == ID_LI && (startBlock->previousSibling() || startBlock->nextSibling())) {
2858 #ifdef DEBUG_COMMANDS
2859  kDebug() << "[modify list]" << endl;
2860 #endif
2861  RefPtr<NodeImpl> newList = startBlock->parent()->cloneNode(false);
2862  insertNodeAfter(newList.get(), startBlock);
2863  removeNode(startBlock);
2864  appendNode(newList.get(), startBlock);
2865  } else {
2866  NodeImpl *blockquoteElement = document()->createHTMLElement("blockquote");
2867  if (startBlock->id() == ID_LI) {
2868  startBlock = startBlock->parent();
2869  NodeImpl *parent = startBlock->parent();
2870  removeNode(startBlock);
2871  appendNode(parent, blockquoteElement);
2872  appendNode(blockquoteElement, startBlock);
2873  } else {
2874  NodeImpl *parent = startBlock->parent();
2875  removeNode(startBlock);
2876  appendNode(parent, blockquoteElement);
2877  appendNode(blockquoteElement, startBlock);
2878  }
2879  }
2880  } else {
2881  if (startBlock->id() == ID_LI && endBlock->id() == ID_LI && startBlock->parent() == endBlock->parent()) {
2882 #ifdef DEBUG_COMMANDS
2883  kDebug() << "[indent some items inside list]" << endl;
2884 #endif
2885  RefPtr<NodeImpl> nestedList = startBlock->parent()->cloneNode(false);
2886  insertNodeBefore(nestedList.get(), startBlock);
2887  NodeImpl *nextNode = 0;
2888  for (NodeImpl *node = startBlock;; node = nextNode) {
2889  nextNode = node->nextSibling();
2890  removeNode(node);
2891  appendNode(nestedList.get(), node);
2892  if (node == endBlock)
2893  break;
2894  }
2895  } else {
2896 #ifdef DEBUG_COMMANDS
2897  kDebug() << "[blocks not from one list are not supported yet]" << endl;
2898 #endif
2899  }
2900  }
2901 }
2902 
2903 static bool hasPreviousListItem(NodeImpl *node)
2904 {
2905  while (node) {
2906  node = node->previousSibling();
2907  if (node && node->id() == ID_LI)
2908  return true;
2909  }
2910  return false;
2911 }
2912 
2913 static bool hasNextListItem(NodeImpl *node)
2914 {
2915  while (node) {
2916  node = node->nextSibling();
2917  if (node && node->id() == ID_LI)
2918  return true;
2919  }
2920  return false;
2921 }
2922 
2923 void IndentOutdentCommandImpl::outdent()
2924 {
2925  Selection selection = endingSelection();
2926 #ifdef DEBUG_COMMANDS
2927  kDebug() << "[indent selection]" << selection << endl;
2928 #endif
2929  NodeImpl *startBlock = selection.start().node()->enclosingBlockFlowElement();
2930  NodeImpl *endBlock = selection.end().node()->enclosingBlockFlowElement();
2931 
2932  if (startBlock->id() == ID_LI && endBlock->id() == ID_LI && startBlock->parent() == endBlock->parent()) {
2933 #ifdef DEBUG_COMMANDS
2934  kDebug() << "[list items selected]" << endl;
2935 #endif
2936  bool firstItemSelected = !hasPreviousListItem(startBlock);
2937  bool lastItemSelected = !hasNextListItem(endBlock);
2938  bool listFullySelected = firstItemSelected && lastItemSelected;
2939 
2940 #ifdef DEBUG_COMMANDS
2941  kDebug() << "[first/last item selected]" << firstItemSelected << lastItemSelected << endl;
2942 #endif
2943 
2944  NodeImpl *listNode = startBlock->parent();
2945  printEnclosingBlockTree(listNode);
2946  bool hasParentList = listNode->parent()->id() == ID_OL || listNode->parent()->id() == ID_UL;
2947 
2948  if (!firstItemSelected && !lastItemSelected) {
2949  // split the list into 2 and reattach all the nodes before the first selected item to the second list
2950  RefPtr<NodeImpl> clonedList = listNode->cloneNode(false);
2951  NodeImpl *nextNode = 0;
2952  for (NodeImpl *node = listNode->firstChild(); node != startBlock; node = nextNode) {
2953  nextNode = node->nextSibling();
2954  removeNode(node);
2955  appendNode(clonedList.get(), node);
2956  }
2957  insertNodeBefore(clonedList.get(), listNode);
2958  // so now the first item selected
2959  firstItemSelected = true;
2960  }
2961 
2962  NodeImpl *nextNode = 0;
2963  for (NodeImpl *node = firstItemSelected ? startBlock : endBlock;; node = nextNode) {
2964  nextNode = firstItemSelected ? node->nextSibling() : node->previousSibling();
2965  removeNode(node);
2966  if (firstItemSelected)
2967  insertNodeBefore(node, listNode);
2968  else
2969  insertNodeAfter(node, listNode);
2970  if (!hasParentList && node->id() == ID_LI) {
2971  insertNodeAfter(document()->createHTMLElement("BR"), node);
2972  removeNodePreservingChildren(node);
2973  }
2974  if (node == (firstItemSelected ? endBlock : startBlock))
2975  break;
2976  }
2977  if (listFullySelected)
2978  removeNode(listNode);
2979  return;
2980  }
2981 
2982 
2983  if (startBlock == endBlock) {
2984  if (startBlock->id() == ID_BLOCKQUOTE) {
2985  removeNodePreservingChildren(startBlock);
2986  } else {
2987 #ifdef DEBUG_COMMANDS
2988  kDebug() << "[not the list or blockquote]" << endl;
2989 #endif
2990  }
2991  } else {
2992 #ifdef DEBUG_COMMANDS
2993  kDebug() << "[blocks not from one list are not supported yet]" << endl;
2994 #endif
2995  }
2996 }
2997 
2998 void IndentOutdentCommandImpl::doApply()
2999 {
3000  if (m_commandType == Indent)
3001  indent();
3002  else
3003  outdent();
3004 }
3005 
3006 //------------------------------------------------------------------------------------------
3007 
3008 } // namespace khtml
3009 
khtml::DeleteSelectionCommandImpl::~DeleteSelectionCommandImpl
virtual ~DeleteSelectionCommandImpl()
Definition: htmlediting_impl.cpp:1187
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:2228
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:2057
khtml::RemoveNodeAttributeCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2304
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:2998
khtml::SetNodeAttributeCommandImpl::~SetNodeAttributeCommandImpl
virtual ~SetNodeAttributeCommandImpl()
Definition: htmlediting_impl.cpp:2457
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:2474
khtml::InputNewlineCommandImpl::InputNewlineCommandImpl
InputNewlineCommandImpl(DOM::DocumentImpl *document)
Definition: htmlediting_impl.cpp:1592
khtml::RemoveCSSPropertyCommandImpl::doUnapply
virtual void doUnapply()
Definition: htmlediting_impl.cpp:2280
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:2137
khtml::InsertTextCommandImpl
Definition: htmlediting_impl.h:374
khtml::RemoveNodeCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2361
khtml::RemoveNodeAndPruneCommandImpl::RemoveNodeAndPruneCommandImpl
RemoveNodeAndPruneCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *pruneNode, DOM::NodeImpl *stopNode=0)
Definition: htmlediting_impl.cpp:2387
khtml::InputTextCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:1775
khtml::RemoveCSSPropertyCommandImpl::~RemoveCSSPropertyCommandImpl
virtual ~RemoveCSSPropertyCommandImpl()
Definition: htmlediting_impl.cpp:2263
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:2396
khtml_part.h
QChar
d
#define d
Definition: khtmlfind.cpp:42
khtml::InsertNodeBeforeCommandImpl::doUnapply
virtual void doUnapply()
Definition: htmlediting_impl.cpp:2026
khtml::TypingCommandImpl::insertText
void insertText(const DOM::DOMString &text)
Definition: htmlediting_impl.cpp:2574
khtml::InputTextCommandImpl::InputTextCommandImpl
InputTextCommandImpl(DOM::DocumentImpl *document)
Definition: htmlediting_impl.cpp:1766
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:348
khtml::SplitTextNodeCommandImpl
Definition: htmlediting_impl.h:571
khtml::TypingCommandImpl::TypingCommandImpl
TypingCommandImpl(DOM::DocumentImpl *document)
Definition: htmlediting_impl.cpp:2554
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:2681
khtml::EditCommandImpl::startingSelection
DOM::Selection startingSelection() const
Definition: htmlediting_impl.h:113
khtml::DeleteSelectionCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:1293
khtml::AppendNodeCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:542
khtml::JoinTextNodesCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2101
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:634
khtml::DeleteTextCommandImpl::DeleteTextCommandImpl
DeleteTextCommandImpl(DOM::DocumentImpl *document, DOM::TextImpl *node, long offset, long count)
Definition: htmlediting_impl.cpp:1551
khtml::InsertListCommandImpl::~InsertListCommandImpl
virtual ~InsertListCommandImpl()
Definition: htmlediting_impl.cpp:2736
kDebug
static QDebug kDebug(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
khtml::TypingCommandImpl::insertNewline
void insertNewline()
Definition: htmlediting_impl.cpp:2593
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:2449
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:368
khtml::CompositeEditCommandImpl
Definition: htmlediting_impl.h:137
khtml::JoinTextNodesCommandImpl
Definition: htmlediting_impl.h:396
khtml::TypingCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2563
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:2740
khtml::DeleteCollapsibleWhitespaceCommandImpl::~DeleteCollapsibleWhitespaceCommandImpl
virtual ~DeleteCollapsibleWhitespaceCommandImpl()
Definition: htmlediting_impl.cpp:1011
khtml::InputTextCommandImpl::deleteCharacter
void deleteCharacter()
Definition: htmlediting_impl.cpp:1784
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:2826
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
QChar::isSpace
bool isSpace() const
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:2040
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:2067
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
QScopedPointer
khtml::RemoveNodeAttributeCommandImpl::~RemoveNodeAttributeCommandImpl
virtual ~RemoveNodeAttributeCommandImpl()
Definition: htmlediting_impl.cpp:2298
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:2504
QString
khtml::DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl
DeleteCollapsibleWhitespaceCommandImpl(DOM::DocumentImpl *document)
QtConcurrent::map
QFuture< void > map(Sequence &sequence, MapFunction function)
khtml::JoinTextNodesCommandImpl::~JoinTextNodesCommandImpl
virtual ~JoinTextNodesCommandImpl()
Definition: htmlediting_impl.cpp:2093
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:2694
next
KAction * next(const QObject *recvr, const char *slot, QObject *parent)
khtml::DeleteCollapsibleWhitespaceCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:1140
khtml::RemoveNodeAttributeCommandImpl::doUnapply
virtual void doUnapply()
Definition: htmlediting_impl.cpp:2316
khtml::TypingCommandImpl::~TypingCommandImpl
virtual ~TypingCommandImpl()
Definition: htmlediting_impl.cpp:2559
khtml::ReplaceSelectionCommandImpl::~ReplaceSelectionCommandImpl
virtual ~ReplaceSelectionCommandImpl()
Definition: htmlediting_impl.cpp:2142
khtml::MoveSelectionCommandImpl::~MoveSelectionCommandImpl
virtual ~MoveSelectionCommandImpl()
Definition: htmlediting_impl.cpp:2224
khtml::EditCommandImpl::doReapply
virtual void doReapply()
Definition: htmlediting_impl.cpp:272
khtml::JoinTextNodesCommandImpl::doUnapply
virtual void doUnapply()
Definition: htmlediting_impl.cpp:2117
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
QMutableListIterator::hasNext
bool hasNext() const
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:2487
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:2626
khtml::DeleteTextCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:1567
khtmlview.h
khtml::InsertListCommandImpl::InsertListCommandImpl
InsertListCommandImpl(DOM::DocumentImpl *document, Type type)
Definition: htmlediting_impl.cpp:2731
khtml::InputNewlineCommandImpl
Definition: htmlediting_impl.h:313
khtml::InputNewlineCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:1627
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:2709
khtml::RemoveNodePreservingChildrenCommandImpl::~RemoveNodePreservingChildrenCommandImpl
virtual ~RemoveNodePreservingChildrenCommandImpl()
Definition: htmlediting_impl.cpp:2428
khtml::InsertNodeBeforeCommandImpl::~InsertNodeBeforeCommandImpl
virtual ~InsertNodeBeforeCommandImpl()
Definition: htmlediting_impl.cpp:2007
khtml::RemoveNodeCommandImpl::~RemoveNodeCommandImpl
virtual ~RemoveNodeCommandImpl()
Definition: htmlediting_impl.cpp:2351
khtml::RemoveNodePreservingChildrenCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2434
khtml::MoveSelectionCommandImpl::MoveSelectionCommandImpl
MoveSelectionCommandImpl(DOM::DocumentImpl *document, DOM::DocumentFragmentImpl *fragment, DOM::Position &position)
Definition: htmlediting_impl.cpp:2219
khtml::EditCommandImpl::isCompositeStep
bool isCompositeStep() const
Definition: htmlediting_impl.h:97
QMutableListIterator::next
T & next()
QMutableListIterator
khtml::JoinTextNodesCommandImpl::JoinTextNodesCommandImpl
JoinTextNodesCommandImpl(DOM::DocumentImpl *, DOM::TextImpl *, DOM::TextImpl *)
Definition: htmlediting_impl.cpp:2080
khtml::InsertTextCommandImpl::~InsertTextCommandImpl
virtual ~InsertTextCommandImpl()
Definition: htmlediting_impl.cpp:2051
khtml::CompositeEditCommandImpl::removeNodePreservingChildren
void removeNodePreservingChildren(DOM::NodeImpl *node)
Definition: htmlediting_impl.cpp:413
khtml::SplitTextNodeCommandImpl::doUnapply
virtual void doUnapply()
Definition: htmlediting_impl.cpp:2534
khtml::DeleteTextCommandImpl::~DeleteTextCommandImpl
virtual ~DeleteTextCommandImpl()
Definition: htmlediting_impl.cpp:1561
khtml::ReplaceSelectionCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2146
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:2015
khtml::CompositeEditCommandImpl::removeNode
void removeNode(DOM::NodeImpl *removeChild)
Definition: htmlediting_impl.cpp:401
DOM::DOMString::implementation
DOMStringImpl * implementation() const
Definition: dom_string.h:147
khtml::RemoveCSSPropertyCommandImpl::RemoveCSSPropertyCommandImpl
RemoveCSSPropertyCommandImpl(DOM::DocumentImpl *, DOM::CSSStyleDeclarationImpl *, int property)
Definition: htmlediting_impl.cpp:2256
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
QListIterator
khtml::RemoveNodeAndPruneCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2403
khtml::IndentOutdentCommandImpl::~IndentOutdentCommandImpl
virtual ~IndentOutdentCommandImpl()
Definition: htmlediting_impl.cpp:2842
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:2463
khtml::InsertNodeBeforeCommandImpl
Definition: htmlediting_impl.h:354
khtml::RemoveNodeCommandImpl::RemoveNodeCommandImpl
RemoveNodeCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *)
Definition: htmlediting_impl.cpp:2329
khtml::CompositeEditCommandImpl::deleteCollapsibleWhitespace
void deleteCollapsibleWhitespace()
Definition: htmlediting_impl.cpp:474
khtml::InputTextCommandImpl::input
void input(const DOM::DOMString &text)
Definition: htmlediting_impl.cpp:1779
khtml::RemoveNodeCommandImpl::doUnapply
virtual void doUnapply()
Definition: htmlediting_impl.cpp:2371
khtml::RemoveCSSPropertyCommandImpl::doApply
virtual void doApply()
Definition: htmlediting_impl.cpp:2269
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:1579
khtml::RemoveNodeAttributeCommandImpl::RemoveNodeAttributeCommandImpl
RemoveNodeAttributeCommandImpl(DOM::DocumentImpl *, DOM::ElementImpl *, DOM::NodeImpl::Id attribute)
Definition: htmlediting_impl.cpp:2291
khtml::InputNewlineCommandImpl::~InputNewlineCommandImpl
virtual ~InputNewlineCommandImpl()
Definition: htmlediting_impl.cpp:1597
khtml::InsertNodeBeforeCommandImpl::InsertNodeBeforeCommandImpl
InsertNodeBeforeCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild)
Definition: htmlediting_impl.cpp:1997
khtml::InputTextCommandImpl::~InputTextCommandImpl
virtual ~InputTextCommandImpl()
Definition: htmlediting_impl.cpp:1771
khtml::RemoveNodeAndPruneCommandImpl
Definition: htmlediting_impl.h:512
khtml::IndentOutdentCommandImpl::IndentOutdentCommandImpl
IndentOutdentCommandImpl(DocumentImpl *document, Type type)
Definition: htmlediting_impl.cpp:2837
khtml::EditCommandImpl::setEndingSelection
void setEndingSelection(const DOM::Selection &s)
Definition: htmlediting_impl.cpp:287
khtml::SplitTextNodeCommandImpl::~SplitTextNodeCommandImpl
virtual ~SplitTextNodeCommandImpl()
Definition: htmlediting_impl.cpp:2496
khtml::RemoveNodePreservingChildrenCommandImpl::RemoveNodePreservingChildrenCommandImpl
RemoveNodePreservingChildrenCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *)
Definition: htmlediting_impl.cpp:2421
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:26:18 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
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • 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