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

KHTML

  • sources
  • kde-4.12
  • kdelibs
  • khtml
khtml_caret.cpp
Go to the documentation of this file.
1 /* This file is part of the KDE project
2  *
3  * Copyright (C) 2003-2004 Leo Savernik <l.savernik@aon.at>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB. If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 
22 #include "khtml_caret_p.h"
23 
24 #include "html/html_documentimpl.h"
25 
26 namespace khtml {
27 
35 enum ObjectAdvanceState {
36  LeftObject = 0x01, AdvancedToSibling = 0x02, EnteredObject = 0x04
37 };
38 
47 enum ObjectTraversalState {
48  OutsideDescending, InsideDescending, InsideAscending, OutsideAscending
49 };
50 
60 static RenderObject* traverseRenderObjects(RenderObject *obj,
61  ObjectTraversalState &trav, bool toBegin, RenderObject *base,
62  int &state)
63 {
64  RenderObject *r;
65  switch (trav) {
66  case OutsideDescending:
67  trav = InsideDescending;
68  break;
69  case InsideDescending:
70  r = toBegin ? obj->lastChild() : obj->firstChild();
71  if (r) {
72  trav = OutsideDescending;
73  obj = r;
74  state |= EnteredObject;
75  } else {
76  trav = InsideAscending;
77  }
78  break;
79  case InsideAscending:
80  trav = OutsideAscending;
81  break;
82  case OutsideAscending:
83  r = toBegin ? obj->previousSibling() : obj->nextSibling();
84  if (r) {
85  trav = OutsideDescending;
86  state |= AdvancedToSibling;
87  } else {
88  r = obj->parent();
89  if (r == base) r = 0;
90  trav = InsideAscending;
91  state |= LeftObject;
92  }
93  obj = r;
94  break;
95  }/*end switch*/
96 
97  return obj;
98 }
99 
105 static inline RenderObject *renderObjectBelow(RenderObject *obj, ObjectTraversalState &trav, RenderObject *base)
106 {
107  trav = InsideDescending;
108  int state; // we don't need the state, so we don't initialize it
109  RenderObject *r = obj;
110  while (r && trav != OutsideDescending) {
111  r = traverseRenderObjects(r, trav, false, base, state);
112 #if DEBUG_CARETMODE > 3
113  kDebug(6200) << "renderObjectBelow: r " << r << " trav " << trav;
114 #endif
115  }
116  trav = InsideDescending;
117  return r;
118 }
119 
125 static inline RenderObject *renderObjectAbove(RenderObject *obj, ObjectTraversalState &trav, RenderObject *base)
126 {
127  trav = OutsideAscending;
128  int state; // we don't need the state, so we don't initialize it
129  RenderObject *r = obj;
130  while (r && trav != InsideAscending) {
131  r = traverseRenderObjects(r, trav, true, base, state);
132 #if DEBUG_CARETMODE > 3
133  kDebug(6200) << "renderObjectAbove: r " << r << " trav " << trav;
134 #endif
135  }
136  trav = InsideAscending;
137  return r;
138 }
139 
144 static inline bool isIndicatedInlineBox(InlineBox *box)
145 {
146  // text boxes are never indicated.
147  if (box->isInlineTextBox()) return false;
148  RenderStyle *s = box->object()->style();
149  return s->borderLeftWidth() || s->borderRightWidth()
150  || s->borderTopWidth() || s->borderBottomWidth()
151  || s->paddingLeft().value() || s->paddingRight().value()
152  || s->paddingTop().value() || s->paddingBottom().value()
153  // ### Can inline elements have top/bottom margins? Couldn't find
154  // it in the CSS 2 spec, but Mozilla ignores them, so we do, too.
155  || s->marginLeft().value() || s->marginRight().value();
156 }
157 
162 static inline bool isIndicatedFlow(RenderObject *r)
163 {
164  RenderStyle *s = r->style();
165  return s->borderLeftStyle() != BNONE || s->borderRightStyle() != BNONE
166  || s->borderTopStyle() != BNONE || s->borderBottomStyle() != BNONE
167 // || s->paddingLeft().value() || s->paddingRight().value()
168 // || s->paddingTop().value() || s->paddingBottom().value()
169 // || s->marginLeft().value() || s->marginRight().value()
170  || s->hasClip() || s->hidesOverflow()
171  || s->backgroundColor().isValid() || s->backgroundImage();
172 }
173 
187 static RenderObject *advanceObject(RenderObject *r,
188  ObjectTraversalState &trav, bool toBegin,
189  RenderObject *base, int &state)
190 {
191 
192  ObjectTraversalState origtrav = trav;
193  RenderObject *a = traverseRenderObjects(r, trav, toBegin, base, state);
194 
195  bool ignoreOutsideDesc = toBegin && origtrav == OutsideAscending;
196 
197  // render object and traversal state at which look ahead has been started
198  RenderObject *la = 0;
199  ObjectTraversalState latrav = trav;
200  ObjectTraversalState lasttrav = origtrav;
201 
202  while (a) {
203 #if DEBUG_CARETMODE > 5
204 kDebug(6200) << "a " << a << " trav " << trav;
205 #endif
206  if (a->element()) {
207 #if DEBUG_CARETMODE > 4
208 kDebug(6200) << "a " << a << " trav " << trav << " origtrav " << origtrav << " ignoreOD " << ignoreOutsideDesc;
209 #endif
210  if (toBegin) {
211 
212  switch (origtrav) {
213  case OutsideDescending:
214  if (trav == InsideAscending) return a;
215  if (trav == OutsideDescending) return a;
216  break;
217  case InsideDescending:
218  if (trav == OutsideDescending) return a;
219  // fall through
220  case InsideAscending:
221  if (trav == OutsideAscending) return a;
222  break;
223  case OutsideAscending:
224  if (trav == OutsideAscending) return a;
225  if (trav == InsideAscending && lasttrav == InsideDescending) return a;
226  if (trav == OutsideDescending && !ignoreOutsideDesc) return a;
227  // ignore this outside descending position, as it effectively
228  // demarkates the same position, but remember it in case we fall off
229  // the document.
230  la = a; latrav = trav;
231  ignoreOutsideDesc = false;
232  break;
233  }/*end switch*/
234 
235  } else {
236 
237  switch (origtrav) {
238  case OutsideDescending:
239  if (trav == InsideAscending) return a;
240  if (trav == OutsideDescending) return a;
241  break;
242  case InsideDescending:
243 // if (trav == OutsideDescending) return a;
244  // fall through
245  case InsideAscending:
246 // if (trav == OutsideAscending) return a;
247 // break;
248  case OutsideAscending:
249  // ### what if origtrav == OA, and immediately afterwards trav
250  // becomes OD? In this case the effective position hasn't changed ->
251  // the caret gets stuck. Otherwise, it apparently cannot happen in
252  // real usage patterns.
253  if (trav == OutsideDescending) return a;
254  if (trav == OutsideAscending) {
255  if (la) return la;
256  // starting lookahead here. Remember old object in case we fall off
257  // the document.
258  la = a; latrav = trav;
259  }
260  break;
261  }/*end switch*/
262 
263  }/*end if*/
264  }/*end if*/
265 
266  lasttrav = trav;
267  a = traverseRenderObjects(a, trav, toBegin, base, state);
268  }/*wend*/
269 
270  if (la) trav = latrav, a = la;
271  return a;
272 
273 }
274 
283 static inline bool isUnsuitable(RenderObject *r, ObjectTraversalState trav)
284 {
285  if (!r) return false;
286  return r->isTableCol() || r->isTableSection() || r->isTableRow()
287  || (r->isText() && !static_cast<RenderText *>(r)->firstTextBox());
288  ;
289  Q_UNUSED(trav);
290 }
291 
305 static inline RenderObject *advanceSuitableObject(RenderObject *r,
306  ObjectTraversalState &trav, bool toBegin,
307  RenderObject *base, int &state)
308 {
309  do {
310  r = advanceObject(r, trav, toBegin, base, state);
311 #if DEBUG_CARETMODE > 2
312  kDebug(6200) << "after advanceSWP: r " << r << " trav " << trav << " toBegin " << toBegin;
313 #endif
314  } while (isUnsuitable(r, trav));
315  return r;
316 }
317 
327 static NodeImpl *nextLeafNode(NodeImpl *r, NodeImpl *baseElem)
328 {
329  NodeImpl *n = r->firstChild();
330  if (n) {
331  while (n) { r = n; n = n->firstChild(); }
332  return const_cast<NodeImpl *>(r);
333  }/*end if*/
334  n = r->nextSibling();
335  if (n) {
336  r = n;
337  while (n) { r = n; n = n->firstChild(); }
338  return const_cast<NodeImpl *>(r);
339  }/*end if*/
340 
341  n = r->parentNode();
342  if (n == baseElem) n = 0;
343  while (n) {
344  r = n;
345  n = r->nextSibling();
346  if (n) {
347  r = n;
348  n = r->firstChild();
349  while (n) { r = n; n = n->firstChild(); }
350  return const_cast<NodeImpl *>(r);
351  }/*end if*/
352  n = r->parentNode();
353  if (n == baseElem) n = 0;
354  }/*wend*/
355  return 0;
356 }
357 
358 #if 0 // currently not used
359 
368 static NodeImpl *prevLeafNode(NodeImpl *r, NodeImpl *baseElem)
369 {
370  NodeImpl *n = r->firstChild();
371  if (n) {
372  while (n) { r = n; n = n->firstChild(); }
373  return const_cast<NodeImpl *>(r);
374  }/*end if*/
375  n = r->previousSibling();
376  if (n) {
377  r = n;
378  while (n) { r = n; n = n->firstChild(); }
379  return const_cast<NodeImpl *>(r);
380  }/*end if*/
381 
382  n = r->parentNode();
383  if (n == baseElem) n = 0;
384  while (n) {
385  r = n;
386  n = r->previousSibling();
387  if (n) {
388  r = n;
389  n = r->lastChild();
390  while (n) { r = n; n = n->lastChild(); }
391  return const_cast<NodeImpl *>(r);
392  }/*end if*/
393  n = r->parentNode();
394  if (n == baseElem) n = 0;
395  }/*wend*/
396  return 0;
397 }
398 #endif
399 
411 void /*KDE_NO_EXPORT*/ mapDOMPosToRenderPos(NodeImpl *node, long offset,
412  RenderObject *&r, long &r_ofs, bool &outside, bool &outsideEnd)
413 {
414  if (node->nodeType() == Node::TEXT_NODE) {
415  outside = false;
416  outsideEnd = false;
417  r = node->renderer();
418  r_ofs = offset;
419  } else if (node->nodeType() == Node::ELEMENT_NODE || node->nodeType() == Node::DOCUMENT_NODE) {
420 
421  // Though offset points between two children, attach it to the visually
422  // most suitable one (and only there, because the mapping must stay bijective)
423  if (node->firstChild()) {
424  outside = true;
425  NodeImpl *child = offset <= 0 ? node->firstChild()
426  // childNode is expensive
427  : node->childNode((unsigned long)offset);
428  // index was child count or out of bounds
429  bool atEnd = !child;
430 #if DEBUG_CARETMODE > 5
431  kDebug(6200) << "mapDTR: child " << child << "@" << (child ? child->nodeName().string() : QString()) << " atEnd " << atEnd;
432 #endif
433  if (atEnd) child = node->lastChild();
434 
435  r = child->renderer();
436  r_ofs = 0;
437  outsideEnd = atEnd;
438 
439  // Outside text nodes most likely stem from a continuation. Seek
440  // the enclosing continued render object and use this one instead.
441  if (r && child->nodeType() == Node::TEXT_NODE) {
442  r = r->parent();
443  RenderObject *o = node->renderer();
444  while (o->continuation() && o->continuation() != r)
445  o = o->continuation();
446  if (!r || o->continuation() != r) {
447  r = child->renderer();
448  }
449  }/*end if*/
450 
451  // BRs cause troubles. Returns the previous render object instead,
452  // giving it the attributes outside, outside end.
453  if (r && r->isBR()) {
454  r = r->objectAbove();
455  outsideEnd = true;
456  }/*end if*/
457 
458  } else {
459  // Element has no children, treat offset to be inside the node.
460  outside = false;
461  outsideEnd = false;
462  r = node->renderer();
463  r_ofs = 0; // only offset 0 possible
464  }
465 
466  } else {
467  r = 0;
468  kWarning() << "Mapping from nodes of type " << node->nodeType()
469  << " not supported!" << endl;
470  }
471 }
472 
483 void /*KDE_NO_EXPORT*/ mapRenderPosToDOMPos(RenderObject *r, long r_ofs,
484  bool outside, bool outsideEnd, NodeImpl *&node, long &offset)
485 {
486  node = r->element();
487  Q_ASSERT(node);
488 #if DEBUG_CARETMODE > 5
489  kDebug(6200) << "mapRTD: r " << r << '@' << (r ? r->renderName() : QString()) << (r && r->element() ? QString(".node ") + QString::number((unsigned)r->element(),16) + '@' + r->element()->nodeName().string() : QString()) << " outside " << outside << " outsideEnd " << outsideEnd;
490 #endif
491  if (node->nodeType() == Node::ELEMENT_NODE || node->nodeType() == Node::TEXT_NODE) {
492 
493  if (outside) {
494  NodeImpl *parent = node->parent();
495 
496  // If this is part of a continuation, use the actual node as the parent,
497  // and the first render child as the node.
498  if (r != node->renderer()) {
499  RenderObject *o = node->renderer();
500  while (o->continuation() && o->continuation() != r)
501  o = o->continuation();
502  if (o->continuation() == r) {
503  parent = node;
504  // ### What if the first render child does not map to a child of
505  // the continued node?
506  node = r->firstChild() ? r->firstChild()->element() : node;
507  }
508  }/*end if*/
509 
510  if (!parent) goto inside;
511 
512  offset = (long)node->nodeIndex() + outsideEnd;
513  node = parent;
514 #if DEBUG_CARETMODE > 5
515  kDebug(6200) << node << "@" << (node ? node->nodeName().string() : QString()) << " offset " << offset;
516 #endif
517  } else { // !outside
518 inside:
519  offset = r_ofs;
520  }
521 
522  } else {
523  offset = 0;
524  kWarning() << "Mapping to nodes of type " << node->nodeType()
525  << " not supported!" << endl;
526  }
527 }
528 
530 static inline void ensureLeafNode(NodeImpl *&node, NodeImpl *base)
531 {
532  if (node && node->hasChildNodes()) node = nextLeafNode(node, base);
533 }
534 
541 static inline void mapRenderPosToTraversalState(bool outside, bool atEnd,
542  bool toBegin, ObjectTraversalState &trav)
543 {
544  if (!outside) atEnd = !toBegin;
545  if (!atEnd ^ toBegin)
546  trav = outside ? OutsideDescending : InsideDescending;
547  else
548  trav = outside ? OutsideAscending : InsideAscending;
549 }
550 
557 static inline void mapTraversalStateToRenderPos(ObjectTraversalState trav,
558  bool toBegin, bool &outside, bool &atEnd)
559 {
560  outside = false;
561  switch (trav) {
562  case OutsideDescending: outside = true; // fall through
563  case InsideDescending: atEnd = toBegin; break;
564  case OutsideAscending: outside = true; // fall through
565  case InsideAscending: atEnd = !toBegin; break;
566  }
567 }
568 
584 static RenderObject* findRenderer(NodeImpl *&node, long offset,
585  RenderObject *base, long &r_ofs,
586  bool &outside, bool &outsideEnd)
587 {
588  if (!node) return 0;
589  RenderObject *r;
590  mapDOMPosToRenderPos(node, offset, r, r_ofs, outside, outsideEnd);
591 #if DEBUG_CARETMODE > 2
592  kDebug(6200) << "findRenderer: node " << node << " " << (node ? node->nodeName().string() : QString()) << " offset " << offset << " r " << r << "[" << (r ? r->renderName() : QString()) << "] r_ofs " << r_ofs << " outside " << outside << " outsideEnd " << outsideEnd;
593 #endif
594  if (r) return r;
595  NodeImpl *baseElem = base ? base->element() : 0;
596  while (!r) {
597  node = nextLeafNode(node, baseElem);
598  if (!node) break;
599  r = node->renderer();
600  if (r) r_ofs = offset;
601  }
602 #if DEBUG_CARETMODE > 3
603  kDebug(6200) << "1r " << r;
604 #endif
605  ObjectTraversalState trav;
606  int state; // not used
607  mapRenderPosToTraversalState(outside, outsideEnd, false, trav);
608  if (r && isUnsuitable(r, trav)) {
609  r = advanceSuitableObject(r, trav, false, base, state);
610  mapTraversalStateToRenderPos(trav, false, outside, outsideEnd);
611  if (r) r_ofs = r->minOffset();
612  }
613 #if DEBUG_CARETMODE > 3
614  kDebug(6200) << "2r " << r;
615 #endif
616  return r;
617 }
618 
622 static ElementImpl *determineBaseElement(NodeImpl *caretNode)
623 {
624  // ### for now, only body is delivered for html documents,
625  // and 0 for xml documents.
626 
627  DocumentImpl *doc = caretNode->getDocument();
628  if (!doc) return 0; // should not happen, but who knows.
629 
630  if (doc->isHTMLDocument())
631  return static_cast<HTMLDocumentImpl *>(doc)->body();
632 
633  return 0;
634 }
635 
636 // == class CaretBox implementation
637 
638 #if DEBUG_CARETMODE > 0
639 void CaretBox::dump(QTextStream &ts, const QString &ind) const
640 {
641  ts << ind << "b@" << _box;
642 
643  if (_box) {
644  ts << "<" << _box->object() << ":" << _box->object()->renderName() << ">";
645  }/*end if*/
646 
647  ts << " " << _x << "+" << _y << "+" << _w << "*" << _h;
648 
649  ts << " cb@" << cb;
650  if (cb) ts << ":" << cb->renderName();
651 
652  ts << " " << (_outside ? (outside_end ? "oe" : "o-") : "i-");
653 // ts << endl;
654 }
655 #endif
656 
657 // == class CaretBoxLine implementation
658 
659 #if DEBUG_CARETMODE > 0
660 # define DEBUG_ACIB 1
661 #else
662 # define DEBUG_ACIB DEBUG_CARETMODE
663 #endif
664 void CaretBoxLine::addConvertedInlineBox(InlineBox *box, SeekBoxParams &sbp) /*KDE_NO_EXPORT*/
665 {
666  // Generate only one outside caret box between two elements. If
667  // coalesceOutsideBoxes is true, generating left outside boxes is inhibited.
668  bool coalesceOutsideBoxes = false;
669  CaretBoxIterator lastCoalescedBox;
670  for (; box; box = box->nextOnLine()) {
671 #if DEBUG_ACIB
672 kDebug(6200) << "box " << box;
673 kDebug(6200) << "box->object " << box->object();
674 kDebug(6200) << "x " << box->m_x << " y " << box->m_y << " w " << box->m_width << " h " << box->m_height << " baseline " << box->m_baseline << " ifb " << box->isInlineFlowBox() << " itb " << box->isInlineTextBox() << " rlb " << box->isRootInlineBox();
675 #endif
676  // ### Why the hell can object() ever be 0?!
677  if (!box->object()) continue;
678 
679  RenderStyle *s = box->object()->style(box->m_firstLine);
680  // parent style for outside caret boxes
681  RenderStyle *ps = box->parent() && box->parent()->object()
682  ? box->parent()->object()->style(box->parent()->m_firstLine)
683  : s;
684 
685  if (box->isInlineFlowBox()) {
686 #if DEBUG_ACIB
687 kDebug(6200) << "isinlineflowbox " << box;
688 #endif
689  InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(box);
690  bool rtl = ps->direction() == RTL;
691  const QFontMetrics &pfm = ps->fontMetrics();
692 
693  if (flowBox->includeLeftEdge()) {
694  // If this box is to be coalesced with the outside end box of its
695  // predecessor, then check if it is the searched box. If it is, we
696  // substitute the outside end box.
697  if (coalesceOutsideBoxes) {
698  if (sbp.equalsBox(flowBox, true, false)) {
699  sbp.it = lastCoalescedBox;
700  Q_ASSERT(!sbp.found);
701  sbp.found = true;
702  }
703  } else {
704  addCreatedFlowBoxEdge(flowBox, pfm, true, rtl);
705  sbp.check(preEnd());
706  }
707  }/*end if*/
708 
709  if (flowBox->firstChild()) {
710 #if DEBUG_ACIB
711 kDebug(6200) << "this " << this << " flowBox " << flowBox << " firstChild " << flowBox->firstChild();
712 kDebug(6200) << "== recursive invocation";
713 #endif
714  addConvertedInlineBox(flowBox->firstChild(), sbp);
715 #if DEBUG_ACIB
716 kDebug(6200) << "== recursive invocation end";
717 #endif
718 }
719  else {
720  addCreatedFlowBoxInside(flowBox, s->fontMetrics());
721  sbp.check(preEnd());
722  }
723 
724  if (flowBox->includeRightEdge()) {
725  addCreatedFlowBoxEdge(flowBox, pfm, false, rtl);
726  lastCoalescedBox = preEnd();
727  sbp.check(lastCoalescedBox);
728  coalesceOutsideBoxes = true;
729  }
730 
731  } else if (box->isInlineTextBox()) {
732 #if DEBUG_ACIB
733 kDebug(6200) << "isinlinetextbox " << box << (box->object() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText *>(box->object())->str->s+box->minOffset(), qMin(box->maxOffset() - box->minOffset(), 15L)).string()) : QString());
734 #endif
735  caret_boxes.append(new CaretBox(box, false, false));
736  sbp.check(preEnd());
737  // coalescing has been interrupted
738  coalesceOutsideBoxes = false;
739 
740  } else {
741 #if DEBUG_ACIB
742 kDebug(6200) << "some replaced or what " << box;
743 #endif
744  // must be an inline-block, inline-table, or any RenderReplaced
745  bool rtl = ps->direction() == RTL;
746  const QFontMetrics &pfm = ps->fontMetrics();
747 
748  if (coalesceOutsideBoxes) {
749  if (sbp.equalsBox(box, true, false)) {
750  sbp.it = lastCoalescedBox;
751  Q_ASSERT(!sbp.found);
752  sbp.found = true;
753  }
754  } else {
755  addCreatedInlineBoxEdge(box, pfm, true, rtl);
756  sbp.check(preEnd());
757  }
758 
759  caret_boxes.append(new CaretBox(box, false, false));
760  sbp.check(preEnd());
761 
762  addCreatedInlineBoxEdge(box, pfm, false, rtl);
763  lastCoalescedBox = preEnd();
764  sbp.check(lastCoalescedBox);
765  coalesceOutsideBoxes = true;
766  }/*end if*/
767  }/*next box*/
768 }
769 #undef DEBUG_ACIB
770 
771 void CaretBoxLine::addCreatedFlowBoxInside(InlineFlowBox *flowBox, const QFontMetrics &fm) /*KDE_NO_EXPORT*/
772 {
773 
774  CaretBox *caretBox = new CaretBox(flowBox, false, false);
775  caret_boxes.append(caretBox);
776 
777  // afaik an inner flow box can only have the width 0, therefore we don't
778  // have to care for rtl or alignment
779  // ### can empty inline elements have a width? css 2 spec isn't verbose about it
780 
781  caretBox->_y += flowBox->baseline() - fm.ascent();
782  caretBox->_h = fm.height();
783 }
784 
785 void CaretBoxLine::addCreatedFlowBoxEdge(InlineFlowBox *flowBox, const QFontMetrics &fm, bool left, bool rtl) /*KDE_NO_EXPORT*/
786 {
787  CaretBox *caretBox = new CaretBox(flowBox, true, !left);
788  caret_boxes.append(caretBox);
789 
790  if (left ^ rtl) caretBox->_x -= flowBox->paddingLeft() + flowBox->borderLeft() + 1;
791  else caretBox->_x += caretBox->_w + flowBox->paddingRight() + flowBox->borderRight();
792 
793  caretBox->_y += flowBox->baseline() - fm.ascent();
794  caretBox->_h = fm.height();
795  caretBox->_w = 1;
796 }
797 
798 void CaretBoxLine::addCreatedInlineBoxEdge(InlineBox *box, const QFontMetrics &fm, bool left, bool rtl) /*KDE_NO_EXPORT*/
799 {
800  CaretBox *caretBox = new CaretBox(box, true, !left);
801  caret_boxes.append(caretBox);
802 
803  if (left ^ rtl) caretBox->_x--;
804  else caretBox->_x += caretBox->_w;
805 
806  caretBox->_y += box->baseline() - fm.ascent();
807  caretBox->_h = fm.height();
808  caretBox->_w = 1;
809 }
810 
811 CaretBoxLine *CaretBoxLine::constructCaretBoxLine(CaretBoxLineDeleter *deleter,
812  InlineFlowBox *basicFlowBox, InlineBox *seekBox, bool seekOutside,
813  bool seekOutsideEnd, CaretBoxIterator &iter, RenderObject *seekObject)
814 // KDE_NO_EXPORT
815 {
816  // Iterate all inline boxes within this inline flow box.
817  // Caret boxes will be created for each
818  // - outside begin of an inline flow box (except for the basic inline flow box)
819  // - outside end of an inline flow box (except for the basic inline flow box)
820  // - inside of an empty inline flow box
821  // - outside begin of an inline box resembling a replaced element
822  // - outside end of an inline box resembling a replaced element
823  // - inline text box
824  // - inline replaced box
825 
826  CaretBoxLine *result = new CaretBoxLine(basicFlowBox);
827  deleter->append(result);
828 
829  SeekBoxParams sbp(seekBox, seekOutside, seekOutsideEnd, seekObject, iter);
830 
831  // iterate recursively, I'm too lazy to do it iteratively
832  result->addConvertedInlineBox(basicFlowBox, sbp);
833 
834  if (!sbp.found) sbp.it = result->end();
835 
836  return result;
837 }
838 
839 CaretBoxLine *CaretBoxLine::constructCaretBoxLine(CaretBoxLineDeleter *deleter,
840  RenderBox *cb, bool outside, bool outsideEnd, CaretBoxIterator &iter) /*KDE_NO_EXPORT*/
841 {
842  int _x = cb->xPos();
843  int _y = cb->yPos();
844  int height;
845  int width = 1; // no override is indicated in boxes
846 
847  if (outside) {
848 
849  RenderStyle *s = cb->element() && cb->element()->parent()
850  && cb->element()->parent()->renderer()
851  ? cb->element()->parent()->renderer()->style()
852  : cb->style();
853  bool rtl = s->direction() == RTL;
854 
855  const QFontMetrics &fm = s->fontMetrics();
856  height = fm.height();
857 
858  if (!outsideEnd) {
859  _x--;
860  } else {
861  _x += cb->width();
862  }
863 
864  int hl = fm.leading() / 2;
865  int baseline = cb->baselinePosition(false);
866  if (!cb->isReplaced() || cb->style()->display() == BLOCK) {
867  if (!outsideEnd ^ rtl)
868  _y -= fm.leading() / 2;
869  else
870  _y += qMax(cb->height() - fm.ascent() - hl, 0);
871  } else {
872  _y += baseline - fm.ascent() - hl;
873  }
874 
875  } else { // !outside
876 
877  RenderStyle *s = cb->style();
878  const QFontMetrics &fm = s->fontMetrics();
879  height = fm.height();
880 
881  _x += cb->borderLeft() + cb->paddingLeft();
882  _y += cb->borderTop() + cb->paddingTop();
883 
884  // ### regard direction
885  switch (s->textAlign()) {
886  case LEFT:
887  case KHTML_LEFT:
888  case TAAUTO: // ### find out what this does
889  case JUSTIFY:
890  break;
891  case CENTER:
892  case KHTML_CENTER:
893  _x += cb->contentWidth() / 2;
894  break;
895  case KHTML_RIGHT:
896  case RIGHT:
897  _x += cb->contentWidth();
898  break;
899  }/*end switch*/
900  }/*end if*/
901 
902  CaretBoxLine *result = new CaretBoxLine;
903  deleter->append(result);
904  result->caret_boxes.append(new CaretBox(_x, _y, width, height, cb,
905  outside, outsideEnd));
906  iter = result->begin();
907  return result;
908 }
909 
910 #if DEBUG_CARETMODE > 0
911 void CaretBoxLine::dump(QTextStream &ts, const QString &ind) const
912 {
913  ts << ind << "cbl: baseFlowBox@" << basefb << endl;
914  QString ind2 = ind + " ";
915  for (size_t i = 0; i < caret_boxes.size(); i++) {
916  if (i > 0) ts << endl;
917  caret_boxes[i]->dump(ts, ind2);
918  }
919 }
920 #endif
921 
922 // == caret mode related helper functions
923 
931 inline InlineFlowBox *seekBaseFlowBox(InlineBox *b, RenderObject *base = 0)
932 {
933  // Seek root line box or base inline flow box, if \c base is interfering.
934  while (b->parent() && b->object() != base) {
935  b = b->parent();
936  }/*wend*/
937  Q_ASSERT(b->isInlineFlowBox());
938  return static_cast<InlineFlowBox *>(b);
939 }
940 
943 inline bool isBlockRenderReplaced(RenderObject *r)
944 {
945  return r->isRenderReplaced() && r->style()->display() == BLOCK;
946 }
947 
964 static CaretBoxLine* findCaretBoxLine(DOM::NodeImpl *node, long offset,
965  CaretBoxLineDeleter *cblDeleter, RenderObject *base,
966  long &r_ofs, CaretBoxIterator &caretBoxIt)
967 {
968  bool outside, outsideEnd;
969  RenderObject *r = findRenderer(node, offset, base, r_ofs, outside, outsideEnd);
970  if (!r) { return 0; }
971 #if DEBUG_CARETMODE > 0
972  kDebug(6200) << "=================== findCaretBoxLine";
973  kDebug(6200) << "node " << node << " offset: " << offset << " r " << r->renderName() << "[" << r << "].node " << r->element()->nodeName().string() << "[" << r->element() << "]" << " r_ofs " << r_ofs << " outside " << outside << " outsideEnd " << outsideEnd;
974 #endif
975 
976  // There are two strategies to find the correct line box. (The third is failsafe)
977  // (A) First, if node's renderer is a RenderText, we only traverse its text
978  // runs and return the root line box (saves much time for long blocks).
979  // This should be the case 99% of the time.
980  // (B) Second, we derive the inline flow box directly when the renderer is
981  // a RenderBlock, RenderInline, or blocked RenderReplaced.
982  // (C) Otherwise, we iterate linearly through all line boxes in order to find
983  // the renderer.
984 
985  // (A)
986  if (r->isText()) do {
987  RenderText *t = static_cast<RenderText *>(r);
988  int dummy;
989  InlineBox *b = t->findInlineTextBox(offset, dummy, true);
990  // Actually b should never be 0, but some render texts don't have text
991  // boxes, so we insert the last run as an error correction.
992  // If there is no last run, we resort to (B)
993  if (!b) {
994  if (!t->lastTextBox())
995  break;
996  b = t->lastTextBox();
997  }/*end if*/
998  Q_ASSERT(b);
999  outside = false; // text boxes cannot have outside positions
1000  InlineFlowBox *baseFlowBox = seekBaseFlowBox(b, base);
1001 #if DEBUG_CARETMODE > 2
1002  kDebug(6200) << "text-box b: " << b << " baseFlowBox: " << baseFlowBox << (b && b->object() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText *>(b->object())->str->s+b->minOffset(), qMin(b->maxOffset() - b->minOffset(), 15L)).string()) : QString());
1003 #endif
1004 #if 0
1005  if (t->containingBlock()->isListItem()) dumpLineBoxes(static_cast<RenderFlow *>(t->containingBlock()));
1006 #endif
1007 #if DEBUG_CARETMODE > 0
1008  kDebug(6200) << "=================== end findCaretBoxLine (renderText)";
1009 #endif
1010  return CaretBoxLine::constructCaretBoxLine(cblDeleter, baseFlowBox,
1011  b, outside, outsideEnd, caretBoxIt);
1012  } while(false);/*end if*/
1013 
1014  // (B)
1015  bool isrepl = isBlockRenderReplaced(r);
1016  if (r->isRenderBlock() || r->isRenderInline() || isrepl) {
1017  RenderFlow *flow = static_cast<RenderFlow *>(r);
1018  InlineFlowBox *firstLineBox = isrepl ? 0 : flow->firstLineBox();
1019 
1020  // On render blocks, if we are outside, or have a totally empty render
1021  // block, we simply construct a special caret box line.
1022  // The latter case happens only when the render block is a leaf object itself.
1023  if (isrepl || r->isRenderBlock() && (outside || !firstLineBox)
1024  || r->isRenderInline() && !firstLineBox) {
1025  #if DEBUG_CARETMODE > 0
1026  kDebug(6200) << "=================== end findCaretBoxLine (box " << (outside ? (outsideEnd ? "outside end" : "outside begin") : "inside") << ")";
1027  #endif
1028  Q_ASSERT(r->isBox());
1029  return CaretBoxLine::constructCaretBoxLine(cblDeleter,
1030  static_cast<RenderBox *>(r), outside, outsideEnd, caretBoxIt);
1031  }/*end if*/
1032 
1033  kDebug(6200) << "firstlinebox " << firstLineBox;
1034  InlineFlowBox *baseFlowBox = seekBaseFlowBox(firstLineBox, base);
1035  return CaretBoxLine::constructCaretBoxLine(cblDeleter, baseFlowBox,
1036  firstLineBox, outside, outsideEnd, caretBoxIt);
1037  }/*end if*/
1038 
1039  RenderBlock *cb = r->containingBlock();
1040  //if ( !cb ) return 0L;
1041  Q_ASSERT(cb);
1042 
1043  // ### which element doesn't have a block as its containing block?
1044  // Is it still possible after the RenderBlock/RenderInline merge?
1045  if (!cb->isRenderBlock()) {
1046  kWarning() << "containing block is no render block!!! crash imminent";
1047  }/*end if*/
1048 
1049  InlineFlowBox *flowBox = cb->firstLineBox();
1050  // (C)
1051  // This case strikes when the element is replaced, but neither a
1052  // RenderBlock nor a RenderInline
1053  if (!flowBox) { // ### utter emergency (why is this possible at all?)
1054 // flowBox = generateDummyFlowBox(arena, cb, r);
1055 // if (ibox) *ibox = flowBox->firstChild();
1056 // outside = outside_end = true;
1057 
1058 // kWarning() << "containing block contains no inline flow boxes!!! crash imminent";
1059 #if DEBUG_CARETMODE > 0
1060  kDebug(6200) << "=================== end findCaretBoxLine (2)";
1061 #endif
1062  return CaretBoxLine::constructCaretBoxLine(cblDeleter, cb,
1063  outside, outsideEnd, caretBoxIt);
1064  }/*end if*/
1065 
1066  // We iterate the inline flow boxes of the containing block until
1067  // we find the given node. This has one major flaw: it is linear, and therefore
1068  // painfully slow for really large blocks.
1069  for (; flowBox; flowBox = static_cast<InlineFlowBox *>(flowBox->nextLineBox())) {
1070 #if DEBUG_CARETMODE > 0
1071  kDebug(6200) << "[scan line]";
1072 #endif
1073 
1074  // construct a caret line box and stop when the element is contained within
1075  InlineFlowBox *baseFlowBox = seekBaseFlowBox(flowBox, base);
1076  CaretBoxLine *cbl = CaretBoxLine::constructCaretBoxLine(cblDeleter,
1077  baseFlowBox, 0, outside, outsideEnd, caretBoxIt, r);
1078 #if DEBUG_CARETMODE > 5
1079  kDebug(6200) << cbl->information();
1080 #endif
1081  if (caretBoxIt != cbl->end()) {
1082 #if DEBUG_CARETMODE > 0
1083  kDebug(6200) << "=================== end findCaretBoxLine (3)";
1084 #endif
1085  return cbl;
1086  }
1087  }/*next flowBox*/
1088 
1089  // no inline flow box found, approximate to nearest following node.
1090  // Danger: this is O(n^2). It's only called to recover from
1091  // errors, that means, theoretically, never. (Practically, far too often :-( )
1092  Q_ASSERT(!flowBox);
1093  CaretBoxLine *cbl = findCaretBoxLine(nextLeafNode(node, base ? base->element() : 0), 0, cblDeleter, base, r_ofs, caretBoxIt);
1094 #if DEBUG_CARETMODE > 0
1095  kDebug(6200) << "=================== end findCaretBoxLine";
1096 #endif
1097  return cbl;
1098 }
1099 
1106 static inline RenderTable *findTableUpTo(RenderObject *r, RenderFlow *cb)
1107 {
1108  while (r && r != cb && !r->isTable()) r = r->parent();
1109  return r && r->isTable() ? static_cast<RenderTable *>(r) : 0;
1110 }
1111 
1114 static inline bool isDescendant(RenderObject *r, RenderObject *cb)
1115 {
1116  while (r && r != cb) r = r->parent();
1117  return r;
1118 }
1119 
1130 static bool containsEditableElement(KHTMLPart *part, RenderBlock *cb,
1131  RenderTable *&table, bool fromEnd = false)
1132 {
1133  RenderObject *r = cb;
1134  if (fromEnd)
1135  while (r->lastChild()) r = r->lastChild();
1136  else
1137  while (r->firstChild()) r = r->firstChild();
1138 
1139  RenderTable *tempTable = 0;
1140  table = 0;
1141  bool withinCb;
1142 // int state; // not used
1143  ObjectTraversalState trav = InsideDescending;
1144  do {
1145  bool modWithinCb = withinCb = isDescendant(r, cb);
1146 
1147  // treat cb extra, it would not be considered otherwise
1148  if (!modWithinCb) {
1149  modWithinCb = true;
1150  r = cb;
1151  } else
1152  tempTable = findTableUpTo(r, cb);
1153 
1154 #if DEBUG_CARETMODE > 1
1155  kDebug(6201) << "cee: r " << (r ? r->renderName() : QString()) << "@" << r << " cb " << cb << " withinCb " << withinCb << " modWithinCb " << modWithinCb << " tempTable " << tempTable;
1156 #endif
1157  if (r && modWithinCb && r->element() && !isUnsuitable(r, trav)
1158  && (part->isCaretMode() || part->isEditable()
1159  || r->style()->userInput() == UI_ENABLED)) {
1160  table = tempTable;
1161 #if DEBUG_CARETMODE > 1
1162  kDebug(6201) << "cee: editable";
1163 #endif
1164  return true;
1165  }/*end if*/
1166 
1167 // RenderObject *oldr = r;
1168 // while (r && r == oldr)
1169 // r = advanceSuitableObject(r, trav, fromEnd, cb->parent(), state);
1170  r = fromEnd ? r->objectAbove() : r->objectBelow();
1171  } while (r && withinCb);
1172  return false;
1173 }
1174 
1187 static bool containsEditableChildElement(KHTMLPart *part, RenderBlock *cb,
1188  RenderTable *&table, bool fromEnd, RenderObject *start)
1189 {
1190  int state = 0;
1191  ObjectTraversalState trav = OutsideAscending;
1192 // kDebug(6201) << "start: " << start;
1193  RenderObject *r = start;
1194  do {
1195  r = traverseRenderObjects(r, trav, fromEnd, cb->parent(), state);
1196  } while(r && !(state & AdvancedToSibling));
1197 // kDebug(6201) << "r: " << r;
1198  //advanceObject(start, trav, fromEnd, cb->parent(), state);
1199 // RenderObject *oldr = r;
1200 // while (r && r == oldr)
1201  if (!r) return false;
1202 
1203  if (fromEnd)
1204  while (r->firstChild()) r = r->firstChild();
1205  else
1206  while (r->lastChild()) r = r->lastChild();
1207 // kDebug(6201) << "child r: " << r;
1208  if (!r) return false;
1209 
1210  RenderTable *tempTable = 0;
1211  table = 0;
1212  bool withinCb = false;
1213  do {
1214 
1215  bool modWithinCb = withinCb = isDescendant(r, cb);
1216 
1217  // treat cb extra, it would not be considered otherwise
1218  if (!modWithinCb) {
1219  modWithinCb = true;
1220  r = cb;
1221  } else
1222  tempTable = findTableUpTo(r, cb);
1223 
1224 #if DEBUG_CARETMODE > 1
1225  kDebug(6201) << "cece: r " << (r ? r->renderName() : QString()) << "@" << r << " cb " << cb << " withinCb " << withinCb << " modWithinCb " << modWithinCb << " tempTable " << tempTable;
1226 #endif
1227  if (r && withinCb && r->element() && !isUnsuitable(r, trav)
1228  && (part->isCaretMode() || part->isEditable()
1229  || r->style()->userInput() == UI_ENABLED)) {
1230  table = tempTable;
1231 #if DEBUG_CARETMODE > 1
1232  kDebug(6201) << "cece: editable";
1233 #endif
1234  return true;
1235  }/*end if*/
1236 
1237  r = fromEnd ? r->objectAbove() : r->objectBelow();
1238  } while (withinCb);
1239  return false;
1240 }
1241 
1242 // == class LinearDocument implementation
1243 
1244 LinearDocument::LinearDocument(KHTMLPart *part, NodeImpl *node, long offset,
1245  CaretAdvancePolicy advancePolicy, ElementImpl *baseElem)
1246  : node(node), offset(offset), m_part(part),
1247  advPol(advancePolicy), base(0)
1248 {
1249  if (node == 0) return;
1250 
1251  if (baseElem) {
1252  RenderObject *b = baseElem->renderer();
1253  if (b && (b->isRenderBlock() || b->isRenderInline()))
1254  base = b;
1255  }
1256 
1257  initPreBeginIterator();
1258  initEndIterator();
1259 }
1260 
1261 LinearDocument::~LinearDocument()
1262 {
1263 }
1264 
1265 int LinearDocument::count() const
1266 {
1267  // FIXME: not implemented
1268  return 1;
1269 }
1270 
1271 LinearDocument::Iterator LinearDocument::current()
1272 {
1273  return LineIterator(this, node, offset);
1274 }
1275 
1276 LinearDocument::Iterator LinearDocument::begin()
1277 {
1278  NodeImpl *n = base ? base->element() : 0;
1279  if (!base) n = node ? node->getDocument() : 0;
1280  if (!n) return end();
1281 
1282  n = n->firstChild();
1283  if (advPol == LeafsOnly)
1284  while (n->firstChild()) n = n->firstChild();
1285 
1286  if (!n) return end(); // must be empty document or empty base element
1287  return LineIterator(this, n, n->minOffset());
1288 }
1289 
1290 LinearDocument::Iterator LinearDocument::preEnd()
1291 {
1292  NodeImpl *n = base ? base->element() : 0;
1293  if (!base) n = node ? node->getDocument() : 0;
1294  if (!n) return preBegin();
1295 
1296  n = n->lastChild();
1297  if (advPol == LeafsOnly)
1298  while (n->lastChild()) n = n->lastChild();
1299 
1300  if (!n) return preBegin(); // must be empty document or empty base element
1301  return LineIterator(this, n, n->maxOffset());
1302 }
1303 
1304 void LinearDocument::initPreBeginIterator()
1305 {
1306  _preBegin = LineIterator(this, 0, 0);
1307 }
1308 
1309 void LinearDocument::initEndIterator()
1310 {
1311  _end = LineIterator(this, 0, 1);
1312 }
1313 
1314 // == class LineIterator implementation
1315 
1316 CaretBoxIterator LineIterator::currentBox /*KDE_NO_EXPORT*/;
1317 long LineIterator::currentOffset /*KDE_NO_EXPORT*/;
1318 
1319 LineIterator::LineIterator(LinearDocument *l, DOM::NodeImpl *node, long offset)
1320  : lines(l)
1321 {
1322 // kDebug(6200) << "LineIterator: node " << node << " offset " << offset;
1323  if (!node) { cbl = 0; return; }
1324  cbl = findCaretBoxLine(node, offset, &lines->cblDeleter,
1325  l->baseObject(), currentOffset, currentBox);
1326  // can happen on partially loaded documents
1327 #if DEBUG_CARETMODE > 0
1328  if (!cbl) kDebug(6200) << "no render object found!";
1329 #endif
1330  if (!cbl) return;
1331 #if DEBUG_CARETMODE > 1
1332  kDebug(6200) << "LineIterator: offset " << offset << " outside " << cbl->isOutside();
1333 #endif
1334 #if DEBUG_CARETMODE > 3
1335  kDebug(6200) << cbl->information();
1336 #endif
1337  if (currentBox == cbl->end()) {
1338 #if DEBUG_CARETMODE > 0
1339  kDebug(6200) << "LineIterator: findCaretBoxLine failed";
1340 #endif
1341  cbl = 0;
1342  }/*end if*/
1343 }
1344 
1345 void LineIterator::nextBlock()
1346 {
1347  RenderObject *base = lines->baseObject();
1348 
1349  bool cb_outside = cbl->isOutside();
1350  bool cb_outside_end = cbl->isOutsideEnd();
1351 
1352  {
1353  RenderObject *r = cbl->enclosingObject();
1354 
1355  ObjectTraversalState trav;
1356  int state; // not used
1357  mapRenderPosToTraversalState(cb_outside, cb_outside_end, false, trav);
1358 #if DEBUG_CARETMODE > 1
1359  kDebug(6200) << "nextBlock: before adv r" << r << ' ' << (r ? r->renderName() : QString()) << (r && r->isText() ? " contains \"" + QString(((RenderText *)r)->str->s, qMin(((RenderText *)r)->str->l,15)) + "\"" : QString()) << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end;
1360 #endif
1361  r = advanceSuitableObject(r, trav, false, base, state);
1362  if (!r) {
1363  cbl = 0;
1364  return;
1365  }/*end if*/
1366 
1367  mapTraversalStateToRenderPos(trav, false, cb_outside, cb_outside_end);
1368 #if DEBUG_CARETMODE > 1
1369  kDebug(6200) << "nextBlock: after r" << r << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end;
1370 #endif
1371 #if DEBUG_CARETMODE > 0
1372  kDebug(6200) << "++: r " << r << "[" << (r?r->renderName():QString()) << "]";
1373 #endif
1374 
1375  RenderBlock *cb;
1376 
1377  // If we hit a block or replaced object, use this as its enclosing object
1378  bool isrepl = isBlockRenderReplaced(r);
1379  if (r->isRenderBlock() || isrepl) {
1380  RenderBox *cb = static_cast<RenderBox *>(r);
1381 
1382  cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
1383  cb_outside, cb_outside_end, currentBox);
1384 
1385 #if DEBUG_CARETMODE > 0
1386  kDebug(6200) << "r->isFlow is cb. continuation @" << cb->continuation();
1387 #endif
1388  return;
1389  } else {
1390  cb = r->containingBlock();
1391  Q_ASSERT(cb->isRenderBlock());
1392  }/*end if*/
1393  InlineFlowBox *flowBox = cb->firstLineBox();
1394 #if DEBUG_CARETMODE > 0
1395  kDebug(6200) << "++: flowBox " << flowBox << " cb " << cb << '[' << (cb?cb->renderName()+QString(".node ")+QString::number((unsigned)cb->element(),16)+(cb->element()?'@'+cb->element()->nodeName().string():QString()):QString()) << ']';
1396 #endif
1397  Q_ASSERT(flowBox);
1398  if (!flowBox) { // ### utter emergency (why is this possible at all?)
1399  cb_outside = cb_outside_end = true;
1400  cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
1401  cb_outside, cb_outside_end, currentBox);
1402  return;
1403  }
1404 
1405  bool seekOutside = false, seekOutsideEnd = false; // silence gcc uninit warning
1406  CaretBoxIterator it;
1407  cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
1408  flowBox, flowBox->firstChild(), seekOutside, seekOutsideEnd, it);
1409  }
1410 }
1411 
1412 void LineIterator::prevBlock()
1413 {
1414  RenderObject *base = lines->baseObject();
1415 
1416  bool cb_outside = cbl->isOutside();
1417  bool cb_outside_end = cbl->isOutsideEnd();
1418 
1419  {
1420  RenderObject *r = cbl->enclosingObject();
1421  if (r->isAnonymous() && !cb_outside)
1422  cb_outside = true, cb_outside_end = false;
1423 
1424  ObjectTraversalState trav;
1425  int state; // not used
1426  mapRenderPosToTraversalState(cb_outside, cb_outside_end, true, trav);
1427 #if DEBUG_CARETMODE > 1
1428  kDebug(6200) << "prevBlock: before adv r" << r << " " << (r ? r->renderName() : QString()) << (r && r->isText() ? " contains \"" + QString(((RenderText *)r)->str->s, qMin(((RenderText *)r)->str->l,15)) + "\"" : QString()) << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end;
1429 #endif
1430  r = advanceSuitableObject(r, trav, true, base, state);
1431  if (!r) {
1432  cbl = 0;
1433  return;
1434  }/*end if*/
1435 
1436  mapTraversalStateToRenderPos(trav, true, cb_outside, cb_outside_end);
1437 #if DEBUG_CARETMODE > 1
1438  kDebug(6200) << "prevBlock: after r" << r << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end;
1439 #endif
1440 #if DEBUG_CARETMODE > 0
1441  kDebug(6200) << "--: r " << r << "[" << (r?r->renderName():QString()) << "]";
1442 #endif
1443 
1444  RenderBlock *cb;
1445 
1446  // If we hit a block, use this as its enclosing object
1447  bool isrepl = isBlockRenderReplaced(r);
1448 // kDebug(6200) << "isrepl " << isrepl << " isblock " << r->isRenderBlock();
1449  if (r->isRenderBlock() || isrepl) {
1450  RenderBox *cb = static_cast<RenderBox *>(r);
1451 
1452  cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
1453  cb_outside, cb_outside_end, currentBox);
1454 
1455 #if DEBUG_CARETMODE > 0
1456  kDebug(6200) << "r->isFlow is cb. continuation @" << cb->continuation();
1457 #endif
1458  return;
1459  } else {
1460  cb = r->containingBlock();
1461  Q_ASSERT(cb->isRenderBlock());
1462  }/*end if*/
1463  InlineFlowBox *flowBox = cb->lastLineBox();
1464 #if DEBUG_CARETMODE > 0
1465  kDebug(6200) << "--: flowBox " << flowBox << " cb " << cb << "[" << (cb?cb->renderName()+QString(".node ")+QString::number((unsigned)cb->element(),16)+(cb->element()?"@"+cb->element()->nodeName().string():QString()):QString()) << "]";
1466 #endif
1467  Q_ASSERT(flowBox);
1468  if (!flowBox) { // ### utter emergency (why is this possible at all?)
1469  cb_outside = true; cb_outside_end = false;
1470  cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
1471  cb_outside, cb_outside_end, currentBox);
1472  return;
1473  }
1474 
1475  bool seekOutside = false, seekOutsideEnd = false; // silence gcc uninit warning
1476  CaretBoxIterator it;
1477  cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
1478  flowBox, flowBox->firstChild(), seekOutside, seekOutsideEnd, it);
1479  }
1480 }
1481 
1482 void LineIterator::advance(bool toBegin)
1483 {
1484  InlineFlowBox *flowBox = cbl->baseFlowBox();
1485  if (flowBox) {
1486  flowBox = static_cast<InlineFlowBox *>(toBegin ? flowBox->prevLineBox() : flowBox->nextLineBox());
1487  if (flowBox) {
1488  bool seekOutside = false, seekOutsideEnd = false; // silence gcc uninit warning
1489  CaretBoxIterator it;
1490  cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
1491  flowBox, flowBox->firstChild(), seekOutside, seekOutsideEnd, it);
1492  }/*end if*/
1493  }/*end if*/
1494 
1495  // if there are no more lines in this block, move towards block to come
1496  if (!flowBox) { if (toBegin) prevBlock(); else nextBlock(); }
1497 
1498 #if DEBUG_CARETMODE > 3
1499  if (cbl) kDebug(6200) << cbl->information();
1500 #endif
1501 }
1502 
1503 // == class EditableCaretBoxIterator implementation
1504 
1505 void EditableCaretBoxIterator::advance(bool toBegin)
1506 {
1507 #if DEBUG_CARETMODE > 3
1508  kDebug(6200) << "---------------" << "toBegin " << toBegin;
1509 #endif
1510  const CaretBoxIterator preBegin = cbl->preBegin();
1511  const CaretBoxIterator end = cbl->end();
1512 
1513  CaretBoxIterator lastbox = *this, curbox;
1514  bool islastuseable = true; // silence gcc
1515  bool iscuruseable;
1516  // Assume adjacency of caret boxes. Will be falsified later if applicable.
1517  adjacent = true;
1518 
1519 #if DEBUG_CARETMODE > 4
1520 // kDebug(6200) << "ebit::advance: before: " << (**this)->object() << "@" << (**this)->object()->renderName() << ".node " << (**this)->object()->element() << "[" << ((**this)->object()->element() ? (**this)->object()->element()->nodeName().string() : QString()) << "] inline " << (**this)->isInline() << " outside " << (**this)->isOutside() << " outsideEnd " << (**this)->isOutsideEnd();
1521 #endif
1522 
1523  if (toBegin) CaretBoxIterator::operator --(); else CaretBoxIterator::operator ++();
1524  bool curAtEnd = *this == preBegin || *this == end;
1525  curbox = *this;
1526  bool atEnd = true;
1527  if (!curAtEnd) {
1528  iscuruseable = isEditable(curbox, toBegin);
1529  if (toBegin) CaretBoxIterator::operator --(); else CaretBoxIterator::operator ++();
1530  atEnd = *this == preBegin || *this == end;
1531  }
1532  while (!curAtEnd) {
1533  bool haslast = lastbox != end && lastbox != preBegin;
1534  bool hascoming = !atEnd;
1535  bool iscominguseable = true; // silence gcc
1536 
1537  if (!atEnd) iscominguseable = isEditable(*this, toBegin);
1538  if (iscuruseable) {
1539 #if DEBUG_CARETMODE > 3
1540  kDebug(6200) << "ebit::advance: " << (*curbox)->object() << "@" << (*curbox)->object()->renderName() << ".node " << (*curbox)->object()->element() << "[" << ((*curbox)->object()->element() ? (*curbox)->object()->element()->nodeName().string() : QString()) << "] inline " << (*curbox)->isInline() << " outside " << (*curbox)->isOutside() << " outsideEnd " << (*curbox)->isOutsideEnd();
1541 #endif
1542 
1543  CaretBox *box = *curbox;
1544  if (box->isOutside()) {
1545  // if this caret box represents no inline box, it is an outside box
1546  // which has to be considered unconditionally
1547  if (!box->isInline()) break;
1548 
1549  if (advpol == VisibleFlows) break;
1550 
1551  // IndicatedFlows and LeafsOnly are treated equally in caret box lines
1552 
1553  InlineBox *ibox = box->inlineBox();
1554  // get previous inline box
1555  InlineBox *prev = box->isOutsideEnd() ? ibox : ibox->prevOnLine();
1556  // get next inline box
1557  InlineBox *next = box->isOutsideEnd() ? ibox->nextOnLine() : ibox;
1558 
1559  const bool isprevindicated = !prev || isIndicatedInlineBox(prev);
1560  const bool isnextindicated = !next || isIndicatedInlineBox(next);
1561  const bool last = haslast && !islastuseable;
1562  const bool coming = hascoming && !iscominguseable;
1563  const bool left = !prev || prev->isInlineFlowBox() && isprevindicated
1564  || (toBegin && coming || !toBegin && last);
1565  const bool right = !next || next->isInlineFlowBox() && isnextindicated
1566  || (!toBegin && coming || toBegin && last);
1567  const bool text2indicated = toBegin && next && next->isInlineTextBox()
1568  && isprevindicated
1569  || !toBegin && prev && prev->isInlineTextBox() && isnextindicated;
1570  const bool indicated2text = !toBegin && next && next->isInlineTextBox()
1571  && prev && isprevindicated
1572  // ### this code is so broken.
1573  /*|| toBegin && prev && prev->isInlineTextBox() && isnextindicated*/;
1574 #if DEBUG_CARETMODE > 5
1575  kDebug(6200) << "prev " << prev << " haslast " << haslast << " islastuseable " << islastuseable << " left " << left << " next " << next << " hascoming " << hascoming << " iscominguseable " << iscominguseable << " right " << right << " text2indicated " << text2indicated << " indicated2text " << indicated2text;
1576 #endif
1577 
1578  if (left && right && !text2indicated || indicated2text) {
1579  adjacent = false;
1580 #if DEBUG_CARETMODE > 4
1581  kDebug(6200) << "left && right && !text2indicated || indicated2text";
1582 #endif
1583  break;
1584  }
1585 
1586  } else {
1587  // inside boxes are *always* valid
1588 #if DEBUG_CARETMODE > 4
1589 if (box->isInline()) {
1590  InlineBox *ibox = box->inlineBox();
1591  kDebug(6200) << "inside " << (!ibox->isInlineFlowBox() || static_cast<InlineFlowBox *>(ibox)->firstChild() ? "non-empty" : "empty") << (isIndicatedInlineBox(ibox) ? " indicated" : "") << " adjacent=" << adjacent;
1592  }
1593 #if 0
1594  RenderStyle *s = ibox->object()->style();
1595  kDebug(6200) << "bordls " << s->borderLeftStyle()
1596  << " bordl " << (s->borderLeftStyle() != BNONE)
1597  << " bordr " << (s->borderRightStyle() != BNONE)
1598  << " bordt " << (s->borderTopStyle() != BNONE)
1599  << " bordb " << (s->borderBottomStyle() != BNONE)
1600  << " padl " << s->paddingLeft().value()
1601  << " padr " << s->paddingRight().value()
1602  << " padt " << s->paddingTop().value()
1603  << " padb " << s->paddingBottom().value()
1604  // ### Can inline elements have top/bottom margins? Couldn't find
1605  // it in the CSS 2 spec, but Mozilla ignores them, so we do, too.
1606  << " marl " << s->marginLeft().value()
1607  << " marr " << s->marginRight().value()
1608  << endl;
1609 #endif
1610 #endif
1611  break;
1612  }/*end if*/
1613 
1614  } else {
1615 
1616  if (!(*curbox)->isOutside()) {
1617  // cannot be adjacent anymore
1618  adjacent = false;
1619  }
1620 
1621  }/*end if*/
1622  lastbox = curbox;
1623  islastuseable = iscuruseable;
1624  curbox = *this;
1625  iscuruseable = iscominguseable;
1626  curAtEnd = atEnd;
1627  if (!atEnd) {
1628  if (toBegin) CaretBoxIterator::operator --(); else CaretBoxIterator::operator ++();
1629  atEnd = *this == preBegin || *this == end;
1630  }/*end if*/
1631  }/*wend*/
1632 
1633  *static_cast<CaretBoxIterator *>(this) = curbox;
1634 #if DEBUG_CARETMODE > 4
1635 // kDebug(6200) << "still valid? " << (*this != preBegin && *this != end);
1636 #endif
1637 #if DEBUG_CARETMODE > 3
1638  kDebug(6200) << "---------------" << "end ";
1639 #endif
1640 }
1641 
1642 bool EditableCaretBoxIterator::isEditable(const CaretBoxIterator &boxit, bool fromEnd)
1643 {
1644  Q_ASSERT(boxit != cbl->end() && boxit != cbl->preBegin());
1645  CaretBox *b = *boxit;
1646  RenderObject *r = b->object();
1647 #if DEBUG_CARETMODE > 0
1648 // if (b->isInlineFlowBox()) kDebug(6200) << "b is inline flow box" << (outside ? " (outside)" : "");
1649  kDebug(6200) << "isEditable r" << r << ": " << (r ? r->renderName() : QString()) << (r && r->isText() ? " contains \"" + QString(((RenderText *)r)->str->s, qMin(((RenderText *)r)->str->l,15)) + "\"" : QString());
1650 #endif
1651  // Must check caret mode or design mode *after* r->element(), otherwise
1652  // lines without a backing DOM node get regarded, leading to a crash.
1653  // ### check should actually be in InlineBoxIterator
1654  NodeImpl *node = r->element();
1655  ObjectTraversalState trav;
1656  mapRenderPosToTraversalState(b->isOutside(), b->isOutsideEnd(), fromEnd, trav);
1657  if (isUnsuitable(r, trav) || !node) {
1658  return false;
1659  }
1660 
1661  // generally exclude replaced elements with no children from navigation
1662  if (!b->isOutside() && r->isRenderReplaced() && !r->firstChild())
1663  return false;
1664 
1665  RenderObject *eff_r = r;
1666  bool globallyNavigable = m_part->isCaretMode() || m_part->isEditable();
1667 
1668  // calculate the parent element's editability if this inline box is outside.
1669  if (b->isOutside() && !globallyNavigable) {
1670  NodeImpl *par = node->parent();
1671  // I wonder whether par can be 0. It shouldn't be possible if the
1672  // algorithm contained no bugs.
1673  Q_ASSERT(par);
1674  if (par) node = par;
1675  eff_r = node->renderer();
1676  Q_ASSERT(eff_r); // this is a hard requirement
1677  }
1678 
1679  bool result = globallyNavigable || eff_r->style()->userInput() == UI_ENABLED;
1680 #if DEBUG_CARETMODE > 0
1681  kDebug(6200) << result;
1682 #endif
1683  return result;
1684 }
1685 
1686 // == class EditableLineIterator implementation
1687 
1688 void EditableLineIterator::advance(bool toBegin)
1689 {
1690  CaretAdvancePolicy advpol = lines->advancePolicy();
1691  LineIterator lasteditable, lastindicated;
1692  bool haslasteditable = false;
1693  bool haslastindicated = false;
1694  bool uselasteditable = false;
1695 
1696  LineIterator::advance(toBegin);
1697  while (cbl) {
1698  if (isEditable(*this)) {
1699 #if DEBUG_CARETMODE > 3
1700  kDebug(6200) << "advance: " << cbl->enclosingObject() << "@" << cbl->enclosingObject()->renderName() << ".node " << cbl->enclosingObject()->element() << "[" << (cbl->enclosingObject()->element() ? cbl->enclosingObject()->element()->nodeName().string() : QString()) << "]";
1701 #endif
1702 
1703  bool hasindicated = isIndicatedFlow(cbl->enclosingObject());
1704  if (hasindicated) {
1705  haslastindicated = true;
1706  lastindicated = *this;
1707  }
1708 
1709  switch (advpol) {
1710  case IndicatedFlows:
1711  if (hasindicated) goto wend;
1712  // fall through
1713  case LeafsOnly:
1714  if (cbl->isOutside()) break;
1715  // fall through
1716  case VisibleFlows: goto wend;
1717  }/*end switch*/
1718 
1719  // remember rejected editable element
1720  lasteditable = *this;
1721  haslasteditable = true;
1722 #if DEBUG_CARETMODE > 4
1723  kDebug(6200) << "remembered lasteditable " << *lasteditable;
1724 #endif
1725  } else {
1726 
1727  // If this element isn't editable, but the last one was, and it was only
1728  // rejected because it didn't match the caret advance policy, force it.
1729  // Otherwise certain combinations of editable and uneditable elements
1730  // could never be reached with some policies.
1731  if (haslasteditable) { uselasteditable = true; break; }
1732 
1733  }
1734  LineIterator::advance(toBegin);
1735  }/*wend*/
1736 wend:
1737 
1738  if (uselasteditable) *this = haslastindicated ? lastindicated : lasteditable;
1739  if (!cbl && haslastindicated) *this = lastindicated;
1740 }
1741 
1742 // == class EditableCharacterIterator implementation
1743 
1744 void EditableCharacterIterator::initFirstChar()
1745 {
1746  CaretBox *box = *ebit;
1747  InlineBox *b = box->inlineBox();
1748  if (_offset == box->maxOffset())
1749  peekNext();
1750  else if (b && !box->isOutside() && b->isInlineTextBox())
1751  _char = static_cast<RenderText *>(b->object())->str->s[_offset].unicode();
1752  else
1753  _char = -1;
1754 }
1755 
1759 static inline bool isCaretBoxEmpty(CaretBox *box) {
1760  if (!box->isInline()) return false;
1761  InlineBox *ibox = box->inlineBox();
1762  return ibox->isInlineFlowBox()
1763  && !static_cast<InlineFlowBox *>(ibox)->firstChild()
1764  && !isIndicatedInlineBox(ibox);
1765 }
1766 
1767 EditableCharacterIterator &EditableCharacterIterator::operator ++()
1768 {
1769  _offset++;
1770 
1771  CaretBox *box = *ebit;
1772  InlineBox *b = box->inlineBox();
1773  long maxofs = box->maxOffset();
1774 #if DEBUG_CARETMODE > 0
1775  kDebug(6200) << "box->maxOffset() " << box->maxOffset() << " box->minOffset() " << box->minOffset();
1776 #endif
1777  if (_offset == maxofs) {
1778 #if DEBUG_CARETMODE > 2
1779 kDebug(6200) << "_offset == maxofs: " << _offset << " == " << maxofs;
1780 #endif
1781  peekNext();
1782  } else if (_offset > maxofs) {
1783 #if DEBUG_CARETMODE > 2
1784 kDebug(6200) << "_offset > maxofs: " << _offset << " > " << maxofs /*<< " _peekNext: " << _peekNext*/;
1785 #endif
1786  if (true) {
1787  ++ebit;
1788  if (ebit == (*_it)->end()) { // end of line reached, go to next line
1789  ++_it;
1790 #if DEBUG_CARETMODE > 3
1791 kDebug(6200) << "++_it";
1792 #endif
1793  if (_it != _it.lines->end()) {
1794  ebit = _it;
1795  box = *ebit;
1796  b = box->inlineBox();
1797 #if DEBUG_CARETMODE > 3
1798 kDebug(6200) << "box " << box << " b " << b << " isText " << box->isInlineTextBox();
1799 #endif
1800 
1801 #if DEBUG_CARETMODE > 3
1802  RenderObject *_r = box->object();
1803 kDebug(6200) << "_r " << _r << ":" << _r->element()->nodeName().string();
1804 #endif
1805  _offset = box->minOffset();
1806 #if DEBUG_CARETMODE > 3
1807 kDebug(6200) << "_offset " << _offset;
1808 #endif
1809  } else {
1810  b = 0;
1811  _end = true;
1812  }/*end if*/
1813  goto readchar;
1814  }/*end if*/
1815  }/*end if*/
1816 
1817  bool adjacent = ebit.isAdjacent();
1818 #if 0
1819  // Jump over element if this one is not a text node.
1820  if (adjacent && !(*ebit)->isInlineTextBox()) {
1821  EditableCaretBoxIterator copy = ebit;
1822  ++ebit;
1823  if (ebit != (*_it)->end() && (*ebit)->isInlineTextBox()
1824  /*&& (!(*ebit)->isInlineFlowBox()
1825  || static_cast<InlineFlowBox *>(*ebit)->)*/)
1826  adjacent = false;
1827  else ebit = copy;
1828  }/*end if*/
1829 #endif
1830  // Jump over empty elements.
1831  if (adjacent && !(*ebit)->isInlineTextBox()) {
1832  bool noemptybox = true;
1833  while (isCaretBoxEmpty(*ebit)) {
1834  noemptybox = false;
1835  EditableCaretBoxIterator copy = ebit;
1836  ++ebit;
1837  if (ebit == (*_it)->end()) { ebit = copy; break; }
1838  }
1839  if (noemptybox) adjacent = false;
1840  }/*end if*/
1841 // _r = (*ebit)->object();
1842  /*if (!_it.outside) */_offset = (*ebit)->minOffset() + adjacent;
1843  //_peekNext = 0;
1844  box = *ebit;
1845  b = box->inlineBox();
1846  goto readchar;
1847  } else {
1848 readchar:
1849  // get character
1850  if (b && !box->isOutside() && b->isInlineTextBox() && _offset < b->maxOffset())
1851  _char = static_cast<RenderText *>(b->object())->str->s[_offset].unicode();
1852  else
1853  _char = -1;
1854  }/*end if*/
1855 #if DEBUG_CARETMODE > 2
1856 kDebug(6200) << "_offset: " << _offset /*<< " _peekNext: " << _peekNext*/ << " char '" << (char)_char << "'";
1857 #endif
1858 
1859 #if DEBUG_CARETMODE > 0
1860  if (!_end && ebit != (*_it)->end()) {
1861  CaretBox *box = *ebit;
1862  RenderObject *_r = box->object();
1863  kDebug(6200) << "echit++(1): box " << box << (box && box->isInlineTextBox() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText *>(box->object())->str->s+box->minOffset(), box->maxOffset() - box->minOffset()).string()) : QString()) << " _r " << (_r ? _r->element()->nodeName().string() : QString("<nil>"));
1864  }
1865 #endif
1866  return *this;
1867 }
1868 
1869 EditableCharacterIterator &EditableCharacterIterator::operator --()
1870 {
1871  _offset--;
1872  //kDebug(6200) << "--: _offset=" << _offset;
1873 
1874  CaretBox *box = *ebit;
1875  CaretBox *_peekPrev = 0;
1876  CaretBox *_peekNext = 0;
1877  InlineBox *b = box->inlineBox();
1878  long minofs = box->minOffset();
1879 #if DEBUG_CARETMODE > 0
1880  kDebug(6200) << "box->maxOffset() " << box->maxOffset() << " box->minOffset() " << box->minOffset();
1881 #endif
1882  if (_offset == minofs) {
1883 #if DEBUG_CARETMODE > 2
1884 kDebug(6200) << "_offset == minofs: " << _offset << " == " << minofs;
1885 #endif
1886 // _peekNext = b;
1887  // get character
1888  if (b && !box->isOutside() && b->isInlineTextBox())
1889  _char = static_cast<RenderText *>(b->object())->text()[_offset].unicode();
1890  else
1891  _char = -1;
1892 
1893  //peekPrev();
1894  bool do_prev = false;
1895  {
1896  EditableCaretBoxIterator copy;
1897  _peekPrev = 0;
1898  do {
1899  copy = ebit;
1900  --ebit;
1901  if (ebit == (*_it)->preBegin()) { ebit = copy; break; }
1902  } while (isCaretBoxEmpty(*ebit));
1903  // Jump to end of previous element if it's adjacent, and a text box
1904  if (ebit.isAdjacent() && ebit != (*_it)->preBegin() && (*ebit)->isInlineTextBox()) {
1905  _peekPrev = *ebit;
1906  do_prev = true;
1907  } else
1908  ebit = copy;
1909  }
1910  if (do_prev) goto prev;
1911  } else if (_offset < minofs) {
1912 prev:
1913 #if DEBUG_CARETMODE > 2
1914 kDebug(6200) << "_offset < minofs: " << _offset << " < " << minofs /*<< " _peekNext: " << _peekNext*/;
1915 #endif
1916  if (!_peekPrev) {
1917  _peekNext = *ebit;
1918  --ebit;
1919  if (ebit == (*_it)->preBegin()) { // end of line reached, go to previous line
1920  --_it;
1921 #if DEBUG_CARETMODE > 3
1922 kDebug(6200) << "--_it";
1923 #endif
1924  if (_it != _it.lines->preBegin()) {
1925 // kDebug(6200) << "begin from end!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
1926  ebit = EditableCaretBoxIterator(_it, true);
1927  box = *ebit;
1928 // RenderObject *r = box->object();
1929 #if DEBUG_CARETMODE > 3
1930 kDebug(6200) << "box " << box << " b " << box->inlineBox() << " isText " << box->isInlineTextBox();
1931 #endif
1932  _offset = box->maxOffset();
1933 // if (!_it.outside) _offset = r->isBR() ? (*ebit)->minOffset() : (*ebit)->maxOffset();
1934  _char = -1;
1935 #if DEBUG_CARETMODE > 0
1936  kDebug(6200) << "echit--(2): box " << box << " b " << box->inlineBox() << (box->isInlineTextBox() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText *>(box->object())->str->s+box->minOffset(), box->maxOffset() - box->minOffset()).string()) : QString());
1937 #endif
1938  } else
1939  _end = true;
1940  return *this;
1941  }/*end if*/
1942  }/*end if*/
1943 
1944 #if DEBUG_CARETMODE > 0
1945  bool adjacent = ebit.isAdjacent();
1946  kDebug(6200) << "adjacent " << adjacent << " _peekNext " << _peekNext << " _peekNext->isInlineTextBox: " << (_peekNext ? _peekNext->isInlineTextBox() : false) << " !((*ebit)->isInlineTextBox): " << (*ebit ? !(*ebit)->isInlineTextBox() : true);
1947 #endif
1948 #if 0
1949  // Ignore this box if it isn't a text box, but the previous box was
1950  if (adjacent && _peekNext && _peekNext->isInlineTextBox()
1951  && !(*ebit)->isInlineTextBox()) {
1952  EditableCaretBoxIterator copy = ebit;
1953  --ebit;
1954  if (ebit == (*_it)->preBegin()) /*adjacent = false;
1955  else */ebit = copy;
1956  }/*end if*/
1957 #endif
1958 #if 0
1959  // Jump over empty elements.
1960  if (adjacent //&& _peekNext && _peekNext->isInlineTextBox()
1961  && !(*ebit)->isInlineTextBox()) {
1962  bool noemptybox = true;
1963  while (isCaretBoxEmpty(*ebit)) {
1964  noemptybox = false;
1965  EditableCaretBoxIterator copy = ebit;
1966  --ebit;
1967  if (ebit == (*_it)->preBegin()) { ebit = copy; break; }
1968  else _peekNext = *copy;
1969  }
1970  if (noemptybox) adjacent = false;
1971  }/*end if*/
1972 #endif
1973 #if DEBUG_CARETMODE > 0
1974  kDebug(6200) << "(*ebit)->obj " << (*ebit)->object()->renderName() << "[" << (*ebit)->object() << "]" << " minOffset: " << (*ebit)->minOffset() << " maxOffset: " << (*ebit)->maxOffset();
1975 #endif
1976 #if DEBUG_CARETMODE > 3
1977  RenderObject *_r = (*ebit)->object();
1978 kDebug(6200) << "_r " << _r << ":" << _r->element()->nodeName().string();
1979 #endif
1980  _offset = (*ebit)->maxOffset();
1981 // if (!_it.outside) _offset = (*ebit)->maxOffset()/* - adjacent*/;
1982 #if DEBUG_CARETMODE > 3
1983 kDebug(6200) << "_offset " << _offset;
1984 #endif
1985  _peekPrev = 0;
1986  } else {
1987 #if DEBUG_CARETMODE > 0
1988 kDebug(6200) << "_offset: " << _offset << " _peekNext: " << _peekNext;
1989 #endif
1990  // get character
1991  if (_peekNext && _offset >= box->maxOffset() && _peekNext->isInlineTextBox())
1992  _char = static_cast<RenderText *>(_peekNext->object())->text()[_peekNext->minOffset()].unicode();
1993  else if (b && _offset < b->maxOffset() && b->isInlineTextBox())
1994  _char = static_cast<RenderText *>(b->object())->text()[_offset].unicode();
1995  else
1996  _char = -1;
1997  }/*end if*/
1998 
1999 #if DEBUG_CARETMODE > 0
2000  if (!_end && ebit != (*_it)->preBegin()) {
2001  CaretBox *box = *ebit;
2002  kDebug(6200) << "echit--(1): box " << box << " b " << box->inlineBox() << (box->isInlineTextBox() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText *>(box->object())->str->s+box->minOffset(), box->maxOffset() - box->minOffset()).string()) : QString());
2003  }
2004 #endif
2005  return *this;
2006 }
2007 
2008 // == class TableRowIterator implementation
2009 
2010 TableRowIterator::TableRowIterator(RenderTable *table, bool fromEnd,
2011  RenderTableSection::RowStruct *row)
2012  : sec(table, fromEnd)
2013 {
2014  // set index
2015  if (*sec) {
2016  if (fromEnd) index = (*sec)->grid.size() - 1;
2017  else index = 0;
2018  }/*end if*/
2019 
2020  // initialize with given row
2021  if (row && *sec) {
2022  while (operator *() != row)
2023  if (fromEnd) operator --(); else operator ++();
2024  }/*end if*/
2025 }
2026 
2027 TableRowIterator &TableRowIterator::operator ++()
2028 {
2029  index++;
2030 
2031  if (index >= (int)(*sec)->grid.size()) {
2032  ++sec;
2033 
2034  if (*sec) index = 0;
2035  }/*end if*/
2036  return *this;
2037 }
2038 
2039 TableRowIterator &TableRowIterator::operator --()
2040 {
2041  index--;
2042 
2043  if (index < 0) {
2044  --sec;
2045 
2046  if (*sec) index = (*sec)->grid.size() - 1;
2047  }/*end if*/
2048  return *this;
2049 }
2050 
2051 // == class ErgonomicEditableLineIterator implementation
2052 
2053 // some decls
2054 static RenderTableCell *findNearestTableCellInRow(KHTMLPart *part, int x,
2055  RenderTableSection::RowStruct *row, bool fromEnd);
2056 
2070 static inline RenderTableCell *findNearestTableCell(KHTMLPart *part, int x,
2071  TableRowIterator &it, bool fromEnd)
2072 {
2073  RenderTableCell *result = 0;
2074 
2075  while (*it) {
2076  result = findNearestTableCellInRow(part, x, *it, fromEnd);
2077  if (result) break;
2078 
2079  if (fromEnd) --it; else ++it;
2080  }/*wend*/
2081 
2082  return result;
2083 }
2084 
2098 static RenderTableCell *findNearestTableCellInRow(KHTMLPart *part, int x,
2099  RenderTableSection::RowStruct *row, bool fromEnd)
2100 {
2101  // First pass. Find spatially nearest cell.
2102  int n = (int)row->row->size();
2103  int i;
2104  for (i = 0; i < n; i++) {
2105  RenderTableCell *cell = row->row->at(i);
2106  if (!cell || (long)cell == -1) continue;
2107 
2108  int absx, absy;
2109  cell->absolutePosition(absx, absy, false); // ### position: fixed?
2110 #if DEBUG_CARETMODE > 1
2111  kDebug(6201) << "i/n " << i << "/" << n << " absx " << absx << " absy " << absy;
2112 #endif
2113 
2114  // I rely on the assumption that all cells are in ascending visual order
2115  // ### maybe this assumption is wrong for bidi?
2116 #if DEBUG_CARETMODE > 1
2117  kDebug(6201) << "x " << x << " < " << (absx + cell->width()) << "?";
2118 #endif
2119  if (x < absx + cell->width()) break;
2120  }/*next i*/
2121  if (i >= n) i = n - 1;
2122 
2123  // Second pass. Find editable cell, beginning with the currently found,
2124  // extending to the left, and to the right, alternating.
2125  for (int cnt = 0; cnt < 2*n; cnt++) {
2126  int index = i - ((cnt >> 1) + 1)*(cnt & 1) + (cnt >> 1)*!(cnt & 1);
2127  if (index < 0 || index >= n) continue;
2128 
2129  RenderTableCell *cell = row->row->at(index);
2130  if (!cell || (long)cell == -1) continue;
2131 
2132 #if DEBUG_CARETMODE > 1
2133  kDebug(6201) << "index " << index << " cell " << cell;
2134 #endif
2135  RenderTable *nestedTable;
2136  if (containsEditableElement(part, cell, nestedTable, fromEnd)) {
2137 
2138  if (nestedTable) {
2139  TableRowIterator it(nestedTable, fromEnd);
2140  while (*it) {
2141 // kDebug(6201) << "=== recursive invocation";
2142  cell = findNearestTableCell(part, x, it, fromEnd);
2143  if (cell) break;
2144  if (fromEnd) --it; else ++it;
2145  }/*wend*/
2146  }/*end if*/
2147 
2148  return cell;
2149  }/*end if*/
2150  }/*next i*/
2151  return 0;
2152 }
2153 
2160 static RenderObject *commonAncestorTableSectionOrCell(RenderObject *r1,
2161  RenderObject *r2)
2162 {
2163  if (!r1 || !r2) return 0;
2164  RenderTableSection *sec = 0;
2165  int start_depth=0, end_depth=0;
2166  // First we find the depths of the two objects in the tree (start_depth, end_depth)
2167  RenderObject *n = r1;
2168  while (n->parent()) {
2169  n = n->parent();
2170  start_depth++;
2171  }/*wend*/
2172  n = r2;
2173  while( n->parent()) {
2174  n = n->parent();
2175  end_depth++;
2176  }/*wend*/
2177  // here we climb up the tree with the deeper object, until both objects have equal depth
2178  while (end_depth > start_depth) {
2179  r2 = r2->parent();
2180  end_depth--;
2181  }/*wend*/
2182  while (start_depth > end_depth) {
2183  r1 = r1->parent();
2184 // if (r1->isTableSection()) sec = static_cast<RenderTableSection *>(r1);
2185  start_depth--;
2186  }/*wend*/
2187  // Climb the tree with both r1 and r2 until they are the same
2188  while (r1 != r2){
2189  r1 = r1->parent();
2190  if (r1->isTableSection()) sec = static_cast<RenderTableSection *>(r1);
2191  r2 = r2->parent();
2192  }/*wend*/
2193 
2194  // At this point, we found the most approximate common ancestor. Now climb
2195  // up until the condition of the function return value is satisfied.
2196  while (r1 && !r1->isTableCell() && !r1->isTableSection() && !r1->isTable())
2197  r1 = r1->parent();
2198 
2199  return r1 && r1->isTable() ? sec : r1;
2200 }
2201 
2209 static int findRowInSection(RenderTableSection *section, RenderTableCell *cell,
2210  RenderTableSection::RowStruct *&row, RenderTableCell *&directCell)
2211 {
2212  // Seek direct cell
2213  RenderObject *r = cell;
2214  while (r != section) {
2215  if (r->isTableCell()) directCell = static_cast<RenderTableCell *>(r);
2216  r = r->parent();
2217  }/*wend*/
2218 
2219  // So, and this is really nasty: As we have no indices, we have to do a
2220  // linear comparison. Oh, that sucks so much for long tables, you can't
2221  // imagine.
2222  int n = section->numRows();
2223  for (int i = 0; i < n; i++) {
2224  row = &section->grid[i];
2225 
2226  // check for cell
2227  int m = row->row->size();
2228  for (int j = 0; j < m; j++) {
2229  RenderTableCell *c = row->row->at(j);
2230  if (c == directCell) return i;
2231  }/*next j*/
2232 
2233  }/*next i*/
2234  Q_ASSERT(false);
2235  return -1;
2236 }
2237 
2243 static inline RenderTable *findFirstDescendantTable(RenderObject *leaf, RenderBlock *block)
2244 {
2245  RenderTable *result = 0;
2246  while (leaf && leaf != block) {
2247  if (leaf->isTable()) result = static_cast<RenderTable *>(leaf);
2248  leaf = leaf->parent();
2249  }/*wend*/
2250  return result;
2251 }
2252 
2256 static inline RenderTableCell *containingTableCell(RenderObject *r)
2257 {
2258  while (r && !r->isTableCell()) r = r->parent();
2259  return static_cast<RenderTableCell *>(r);
2260 }
2261 
2262 inline void ErgonomicEditableLineIterator::calcAndStoreNewLine(
2263  RenderBlock *newBlock, bool toBegin)
2264 {
2265  // take the first/last editable element in the found cell as the new
2266  // value for the iterator
2267  CaretBoxIterator it;
2268  cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
2269  newBlock, true, toBegin, it);
2270 #if DEBUG_CARETMODE > 3
2271  kDebug(6201) << cbl->information();
2272 #endif
2273 // if (toBegin) prevBlock(); else nextBlock();
2274 
2275  if (!cbl) {
2276  return;
2277  }/*end if*/
2278 
2279  EditableLineIterator::advance(toBegin);
2280 }
2281 
2282 void ErgonomicEditableLineIterator::determineTopologicalElement(
2283  RenderTableCell *oldCell, RenderObject *newObject, bool toBegin)
2284 {
2285  // When we arrive here, a transition between cells has happened.
2286  // Now determine the type of the transition. This can be
2287  // (1) a transition from this cell into a table inside this cell.
2288  // (2) a transition from this cell into another cell of this table
2289 
2290  TableRowIterator it;
2291 
2292  RenderObject *commonAncestor = commonAncestorTableSectionOrCell(oldCell, newObject);
2293 #if DEBUG_CARETMODE > 1
2294  kDebug(6201) << " ancestor " << commonAncestor;
2295 #endif
2296 
2297  // The whole document is treated as a table cell.
2298  if (!commonAncestor || commonAncestor->isTableCell()) { // (1)
2299 
2300  RenderTableCell *cell = static_cast<RenderTableCell *>(commonAncestor);
2301  RenderTable *table = findFirstDescendantTable(newObject, cell);
2302 
2303 #if DEBUG_CARETMODE > 0
2304  kDebug(6201) << "table cell: " << cell;
2305 #endif
2306 
2307  // if there is no table, we fell out of the previous table, and are now
2308  // in some table-less block. Therefore, done.
2309  if (!table) return;
2310 
2311  it = TableRowIterator(table, toBegin);
2312 
2313  } else if (commonAncestor->isTableSection()) { // (2)
2314 
2315  RenderTableSection *section = static_cast<RenderTableSection *>(commonAncestor);
2316  RenderTableSection::RowStruct *row;
2317  int idx = findRowInSection(section, oldCell, row, oldCell);
2318 #if DEBUG_CARETMODE > 1
2319  kDebug(6201) << "table section: row idx " << idx;
2320 #endif
2321 
2322  it = TableRowIterator(section, idx);
2323 
2324  // advance rowspan rows
2325  int rowspan = oldCell->rowSpan();
2326  while (*it && rowspan--) {
2327  if (toBegin) --it; else ++it;
2328  }/*wend*/
2329 
2330  } else {
2331  kError(6201) << "Neither common cell nor section! " << commonAncestor->renderName() << endl;
2332  // will crash on uninitialized table row iterator
2333  }/*end if*/
2334 
2335  RenderTableCell *cell = findNearestTableCell(lines->m_part, xCoor, it, toBegin);
2336 #if DEBUG_CARETMODE > 1
2337  kDebug(6201) << "findNearestTableCell result: " << cell;
2338 #endif
2339 
2340  RenderBlock *newBlock = cell;
2341  if (!cell) {
2342  Q_ASSERT(commonAncestor->isTableSection());
2343  RenderTableSection *section = static_cast<RenderTableSection *>(commonAncestor);
2344  cell = containingTableCell(section);
2345 #if DEBUG_CARETMODE > 1
2346  kDebug(6201) << "containing cell: " << cell;
2347 #endif
2348 
2349  RenderTable *nestedTable;
2350  bool editableChild = cell && containsEditableChildElement(lines->m_part,
2351  cell, nestedTable, toBegin, section->table());
2352 
2353  if (cell && !editableChild) {
2354 #if DEBUG_CARETMODE > 1
2355  kDebug(6201) << "========= recursive invocation outer =========";
2356 #endif
2357  determineTopologicalElement(cell, cell->section(), toBegin);
2358 #if DEBUG_CARETMODE > 1
2359  kDebug(6201) << "========= end recursive invocation outer =========";
2360 #endif
2361  return;
2362 
2363  } else if (cell && nestedTable) {
2364 #if DEBUG_CARETMODE > 1
2365  kDebug(6201) << "========= recursive invocation inner =========";
2366 #endif
2367  determineTopologicalElement(cell, nestedTable, toBegin);
2368 #if DEBUG_CARETMODE > 1
2369  kDebug(6201) << "========= end recursive invocation inner =========";
2370 #endif
2371  return;
2372 
2373  } else {
2374 #if DEBUG_CARETMODE > 1
2375  kDebug(6201) << "newBlock is table: " << section->table();
2376 #endif
2377  RenderObject *r = section->table();
2378  int state; // not used
2379  ObjectTraversalState trav = OutsideAscending;
2380  r = advanceSuitableObject(r, trav, toBegin, lines->baseObject(), state);
2381  if (!r) { cbl = 0; return; }
2382 // if (toBegin) prevBlock(); else nextBlock();
2383  newBlock = static_cast<RenderBlock *>(!r || r->isRenderBlock() ? r : r->containingBlock());
2384  }/*end if*/
2385 #if 0
2386  } else {
2387  // adapt cell so that prevBlock/nextBlock works as expected
2388  newBlock = cell;
2389  // on forward advancing, we must start from the outside end of the
2390  // previous object
2391  if (!toBegin) {
2392  RenderObject *r = newBlock;
2393  int state; // not used
2394  ObjectTraversalState trav = OutsideAscending;
2395  r = advanceSuitableObject(r, trav, true, lines->advancePolicy(), lines->baseObject(), state);
2396  newBlock = static_cast<RenderBlock *>(!r || r->isRenderBlock() ? r : r->containingBlock());
2397  }/*end if*/
2398 #endif
2399  }/*end if*/
2400 
2401  calcAndStoreNewLine(newBlock, toBegin);
2402 }
2403 
2404 ErgonomicEditableLineIterator &ErgonomicEditableLineIterator::operator ++()
2405 {
2406  RenderTableCell *oldCell = containingTableCell(cbl->enclosingObject());
2407 
2408  EditableLineIterator::operator ++();
2409  if (*this == lines->end() || *this == lines->preBegin()) return *this;
2410 
2411  RenderTableCell *newCell = containingTableCell(cbl->enclosingObject());
2412 
2413  if (!newCell || newCell == oldCell) return *this;
2414 
2415  determineTopologicalElement(oldCell, newCell, false);
2416 
2417  return *this;
2418 }
2419 
2420 ErgonomicEditableLineIterator &ErgonomicEditableLineIterator::operator --()
2421 {
2422  RenderTableCell *oldCell = containingTableCell(cbl->enclosingObject());
2423 
2424  EditableLineIterator::operator --();
2425  if (*this == lines->end() || *this == lines->preBegin()) return *this;
2426 
2427  RenderTableCell *newCell = containingTableCell(cbl->enclosingObject());
2428 
2429  if (!newCell || newCell == oldCell) return *this;
2430 
2431  determineTopologicalElement(oldCell, newCell, true);
2432 
2433  return *this;
2434 }
2435 
2436 // == Navigational helper functions ==
2437 
2447 static CaretBox *nearestCaretBox(LineIterator &it, CaretViewContext *cv,
2448  int &x, int &absx, int &absy)
2449 {
2450  // Find containing block
2451  RenderObject *cb = (*it)->containingBlock();
2452 #if DEBUG_CARETMODE > 4
2453  kDebug(6200) << "nearestCB: cb " << cb << "@" << (cb ? cb->renderName() : "");
2454 #endif
2455 
2456  if (cb) cb->absolutePosition(absx, absy);
2457  else absx = absy = 0;
2458 
2459  // Otherwise find out in which inline box the caret is to be placed.
2460 
2461  // this horizontal position is to be approximated
2462  x = cv->origX - absx;
2463  CaretBox *caretBox = 0; // Inline box containing the caret
2464 // NodeImpl *lastnode = 0; // node of previously checked render object.
2465  int xPos; // x-coordinate of current inline box
2466  int oldXPos = -1; // x-coordinate of last inline box
2467  EditableCaretBoxIterator fbit = it;
2468 #if DEBUG_CARETMODE > 0
2469 /* if (it.linearDocument()->advancePolicy() != LeafsOnly)
2470  kWarning() << "nearestInlineBox is only prepared to handle the LeafsOnly advance policy";*/
2471 // kDebug(6200) << "*fbit = " << *fbit;
2472 #endif
2473  // Iterate through all children
2474  for (CaretBox *b; fbit != (*it)->end(); ++fbit) {
2475  b = *fbit;
2476 
2477 #if DEBUG_CARETMODE > 0
2478 // RenderObject *r = b->object();
2479 // if (b->isInlineFlowBox()) kDebug(6200) << "b is inline flow box";
2480 // kDebug(6200) << "approximate r" << r << ": " << (r ? r->renderName() : QString()) << (r && r->isText() ? " contains \"" + QString(((RenderText *)r)->str->s, ((RenderText *)r)->str->l) + "\"" : QString());
2481 #endif
2482  xPos = b->xPos();
2483 
2484  // the caret is before this box
2485  if (x < xPos) {
2486  // snap to nearest box
2487  if (oldXPos < 0 || x - (oldXPos + caretBox->width()) > xPos - x) {
2488  caretBox = b; // current box is nearer
2489  }/*end if*/
2490  break; // Otherwise, preceding box is implicitly used
2491  }
2492 
2493  caretBox = b;
2494 
2495  // the caret is within this box
2496  if (x >= xPos && x < xPos + caretBox->width())
2497  break;
2498  oldXPos = xPos;
2499 
2500  // the caret can only be after the last box which is automatically
2501  // contained in caretBox when we fall out of the loop.
2502  }/*next fbit*/
2503 
2504  return caretBox;
2505 }
2506 
2512 static void moveItToNextWord(EditableCharacterIterator &it)
2513 {
2514 #if DEBUG_CARETMODE > 0
2515  kDebug(6200) << "%%%%%%%%%%%%%%%%%%%%% moveItToNextWord";
2516 #endif
2517  EditableCharacterIterator copy;
2518  while (!it.isEnd() && !(*it).isSpace() && !(*it).isPunct()) {
2519 #if DEBUG_CARETMODE > 2
2520  kDebug(6200) << "reading1 '" << (*it).toLatin1().constData() << "'";
2521 #endif
2522  copy = it;
2523  ++it;
2524  }
2525 
2526  if (it.isEnd()) {
2527  it = copy;
2528  return;
2529  }/*end if*/
2530 
2531  while (!it.isEnd() && ((*it).isSpace() || (*it).isPunct())) {
2532 #if DEBUG_CARETMODE > 2
2533  kDebug(6200) << "reading2 '" << (*it).toLatin1().constData() << "'";
2534 #endif
2535  copy = it;
2536  ++it;
2537  }
2538 
2539  if (it.isEnd()) it = copy;
2540 }
2541 
2547 static void moveItToPrevWord(EditableCharacterIterator &it)
2548 {
2549  if (it.isEnd()) return;
2550 
2551 #if DEBUG_CARETMODE > 0
2552  kDebug(6200) << "%%%%%%%%%%%%%%%%%%%%% moveItToPrevWord";
2553 #endif
2554  EditableCharacterIterator copy;
2555 
2556  // Jump over all space and punctuation characters first
2557  do {
2558  copy = it;
2559  --it;
2560 #if DEBUG_CARETMODE > 2
2561  if (!it.isEnd()) kDebug(6200) << "reading1 '" << (*it).toLatin1().constData() << "'";
2562 #endif
2563  } while (!it.isEnd() && ((*it).isSpace() || (*it).isPunct()));
2564 
2565  if (it.isEnd()) {
2566  it = copy;
2567  return;
2568  }/*end if*/
2569 
2570  do {
2571  copy = it;
2572  --it;
2573 #if DEBUG_CARETMODE > 0
2574  if (!it.isEnd()) kDebug(6200) << "reading2 '" << (*it).toLatin1().constData() << "' (" << (int)(*it).toLatin1().constData() << ") box " << it.caretBox();
2575 #endif
2576  } while (!it.isEnd() && !(*it).isSpace() && !(*it).isPunct());
2577 
2578  it = copy;
2579 #if DEBUG_CARETMODE > 1
2580  if (!it.isEnd()) kDebug(6200) << "effective '" << (*it).toLatin1().constData() << "' (" << (int)(*it).toLatin1().constData() << ") box " << it.caretBox();
2581 #endif
2582 }
2583 
2591 static void moveIteratorByPage(LinearDocument &ld,
2592  ErgonomicEditableLineIterator &it, int mindist, bool next)
2593 {
2594  // ### This whole routine plainly sucks. Use an inverse strategie for pgup/pgdn.
2595 
2596  if (it == ld.end() || it == ld.preBegin()) return;
2597 
2598  ErgonomicEditableLineIterator copy = it;
2599 #if DEBUG_CARETMODE > 0
2600  kDebug(6200) << " mindist: " << mindist;
2601 #endif
2602 
2603  CaretBoxLine *cbl = *copy;
2604  int absx = 0, absy = 0;
2605 
2606  RenderBlock *lastcb = cbl->containingBlock();
2607  Q_ASSERT(lastcb->isRenderBlock());
2608  lastcb->absolutePosition(absx, absy, false); // ### what about fixed?
2609 
2610  int lastfby = cbl->begin().data()->yPos();
2611  int lastheight = 0;
2612  int rescue = 1000; // ### this is a hack to keep stuck carets from hanging the ua
2613  do {
2614  if (next) ++copy; else --copy;
2615  if (copy == ld.end() || copy == ld.preBegin()) break;
2616 
2617  cbl = *copy;
2618  RenderBlock *cb = cbl->containingBlock();
2619 
2620  int diff = 0;
2621  // ### actually flowBox->yPos() should suffice, but this is not ported
2622  // over yet from WebCore
2623  int fby = cbl->begin().data()->yPos();
2624  if (cb != lastcb) {
2625  if (next) {
2626  diff = absy + lastfby + lastheight;
2627  cb->absolutePosition(absx, absy, false); // ### what about fixed?
2628  diff = absy - diff + fby;
2629  lastfby = 0;
2630  } else {
2631  diff = absy;
2632  cb->absolutePosition(absx, absy, false); // ### what about fixed?
2633  diff -= absy + fby + lastheight;
2634  lastfby = fby - lastheight;
2635  }/*end if*/
2636 #if DEBUG_CARETMODE > 2
2637  kDebug(6200) << "absdiff " << diff;
2638 #endif
2639  } else {
2640  diff = qAbs(fby - lastfby);
2641  }/*end if*/
2642 #if DEBUG_CARETMODE > 2
2643  kDebug(6200) << "cbl->begin().data()->yPos(): " << fby << " diff " << diff;
2644 #endif
2645 
2646  mindist -= diff;
2647 
2648  lastheight = qAbs(fby - lastfby);
2649  lastfby = fby;
2650  lastcb = cb;
2651  it = copy;
2652 #if DEBUG_CARETMODE > 0
2653  kDebug(6200) << " mindist: " << mindist;
2654 #endif
2655  // trick: actually the distance is always one line short, but we cannot
2656  // calculate the height of the first line (### WebCore will make it better)
2657  // Therefore, we simply approximate that excess line by using the last
2658  // caluculated line height.
2659  } while (mindist - lastheight > 0 && --rescue);
2660 }
2661 
2662 
2663 }/*end namespace*/
khtml::CaretBoxLine::CaretBoxLine
CaretBoxLine()
Definition: khtml_caret_p.h:296
khtml::CaretBox::_x
int _x
Definition: khtml_caret_p.h:131
khtml::CaretViewContext::origX
int origX
For natural traversal of lines, the original x position is saved, and the actual x is set to the firs...
Definition: khtml_caret_p.h:79
khtml::LinearDocument::advPol
CaretAdvancePolicy advPol
Definition: khtml_caret_p.h:721
khtml::LineIterator::currentBox
static CaretBoxIterator currentBox
Definition: khtml_caret_p.h:493
khtml::LinearDocument::~LinearDocument
virtual ~LinearDocument()
Definition: khtml_caret.cpp:1261
khtml::CaretBox::_box
InlineBox * _box
Definition: khtml_caret_p.h:128
khtml::LinearDocument::begin
Iterator begin()
Returns a line iterator pointing to the very first line of the document.
Definition: khtml_caret.cpp:1276
copy
KAction * copy(const QObject *recvr, const char *slot, QObject *parent)
khtml::CaretBox::containingBlock
RenderBlock * containingBlock() const
returns the containing block of this caret box.
Definition: khtml_caret_p.h:165
khtml::CaretBox::width
int width() const
Definition: khtml_caret_p.h:155
khtml::LinearDocument::preEnd
Iterator preEnd()
Returns a line iterator pointing to the very last line of the document.
Definition: khtml_caret.cpp:1290
khtml::CaretBoxLine::SeekBoxParams::check
bool check(const CaretBoxIterator &chit)
checks whether this box matches the given iterator.
Definition: khtml_caret_p.h:432
KHTMLPart
This class is khtml's main class.
Definition: khtml_part.h:206
kError
static QDebug kError(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
khtml::LinearDocument::_preBegin
Iterator _preBegin
Definition: khtml_caret_p.h:717
khtml::CaretBoxLine::preEnd
CaretBoxIterator preEnd()
Definition: khtml_caret_p.h:324
khtml::TableRowIterator::TableRowIterator
TableRowIterator()
empty constructor.
Definition: khtml_caret_p.h:893
khtml::TableRowIterator
Represents a render table as a linear list of rows.
Definition: khtml_caret_p.h:868
khtml::EditableLineIterator::operator++
EditableLineIterator & operator++()
seek next line
Definition: khtml_caret_p.h:834
QString
khtml::TableRowIterator::sec
TableSectionIterator sec
Definition: khtml_caret_p.h:870
khtml::EditableCaretBoxIterator::isAdjacent
bool isAdjacent() const
returns true when the current caret box is adjacent to the previously iterated caret box...
Definition: khtml_caret_p.h:769
kDebug
static QDebug kDebug(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
khtml::EditableCharacterIterator::_offset
long _offset
Definition: khtml_caret_p.h:988
khtml::EditableCharacterIterator::peekNext
void peekNext()
reads ahead the next node and updates the data structures accordingly
Definition: khtml_caret_p.h:1066
khtml::ErgonomicEditableLineIterator
Iterates through the editable lines of a document, in a topological order.
Definition: khtml_caret_p.h:929
khtml::CaretBoxLine::addCreatedInlineBoxEdge
void addCreatedInlineBoxEdge(InlineBox *box, const QFontMetrics &fm, bool left, bool rtl)
creates and adds the edge of a generic inline box
Definition: khtml_caret.cpp:798
khtml::TableRowIterator::operator++
TableRowIterator & operator++()
advances to the next row
Definition: khtml_caret.cpp:2027
khtml::LinearDocument::initEndIterator
void initEndIterator()
Definition: khtml_caret.cpp:1309
khtml::TableRowIterator::index
int index
Definition: khtml_caret_p.h:871
khtml::CaretBox::_y
int _y
Definition: khtml_caret_p.h:132
khtml::ErgonomicEditableLineIterator::calcAndStoreNewLine
void calcAndStoreNewLine(RenderBlock *newBlock, bool toBegin)
initializes the iterator to point to the first previous/following editable line.
Definition: khtml_caret.cpp:2262
khtml::LineIterator::prevBlock
void prevBlock()
seeks previous block.
Definition: khtml_caret.cpp:1412
khtml::EditableCaretBoxIterator::advance
void advance(bool toBegin)
advances to the editable caret box to come
Definition: khtml_caret.cpp:1505
khtml::LinearDocument::cblDeleter
CaretBoxLineDeleter cblDeleter
Definition: khtml_caret_p.h:713
khtml::LinearDocument::current
Iterator current()
Returns a line iterator containing the current position as its starting value.
Definition: khtml_caret.cpp:1271
khtml::CaretBox::cb
RenderBox * cb
Definition: khtml_caret_p.h:133
khtml::LinearDocument::preBegin
const Iterator & preBegin() const
Returns a line iterator pointing just before the very first line of the document (this is somewhat an...
Definition: khtml_caret_p.h:692
khtml::LineIterator::lines
LinearDocument * lines
Definition: khtml_caret_p.h:490
khtml::CaretBoxLine::addConvertedInlineBox
void addConvertedInlineBox(InlineBox *, SeekBoxParams &)
recursively converts the given inline box into caret boxes and adds them to this caret box line...
Definition: khtml_caret.cpp:664
khtml::TableRowIterator::operator--
TableRowIterator & operator--()
advances to the previous row
Definition: khtml_caret.cpp:2039
khtml::CaretBox::_outside
bool _outside
Definition: khtml_caret_p.h:134
khtml::LinearDocument::count
int count() const
Returns the count of lines.
Definition: khtml_caret.cpp:1265
khtml::EditableCharacterIterator::_end
bool _end
Definition: khtml_caret_p.h:990
khtml::CaretBox::outside_end
bool outside_end
Definition: khtml_caret_p.h:135
khtml::LinearDocument::end
const Iterator & end() const
Returns a line iterator pointing right after the end of the document.
Definition: khtml_caret_p.h:676
khtml::CaretBoxLine::caret_boxes
CaretBoxDeleter caret_boxes
Definition: khtml_caret_p.h:292
khtml::CaretBoxLine::SeekBoxParams::equalsBox
bool equalsBox(const InlineBox *box, bool outside, bool outsideEnd) const
compares whether this seek box matches the given specification
Definition: khtml_caret_p.h:415
khtml::CaretBox::minOffset
long minOffset() const
returns the minimum offset for this caret box.
Definition: khtml_caret_p.h:201
khtml::CaretBoxLine::addCreatedFlowBoxEdge
void addCreatedFlowBoxEdge(InlineFlowBox *flowBox, const QFontMetrics &fm, bool left, bool rtl)
creates and adds the edge of an inline flow box
Definition: khtml_caret.cpp:785
khtml::LineIterator::currentOffset
static long currentOffset
Definition: khtml_caret_p.h:494
khtml::CaretBoxLine
Resembles a line consisting of caret boxes.
Definition: khtml_caret_p.h:290
khtml::MassDeleter< CaretBoxLine >
khtml::ErgonomicEditableLineIterator::operator--
ErgonomicEditableLineIterator & operator--()
seek previous line.
Definition: khtml_caret.cpp:2420
khtml::CaretBoxLine::constructCaretBoxLine
static CaretBoxLine * constructCaretBoxLine(MassDeleter< CaretBoxLine > *deleter, InlineFlowBox *baseFlowBox, InlineBox *seekBox, bool seekOutside, bool seekOutsideEnd, CaretBoxIterator &iter, RenderObject *seekObject=0)
constructs a new caret box line out of the given inline flow box
next
KAction * next(const QObject *recvr, const char *slot, QObject *parent)
khtml::CaretBoxLine::SeekBoxParams::it
CaretBoxIterator & it
Definition: khtml_caret_p.h:408
khtml::CaretBoxIterator
Iterates over the elements of a caret box line.
Definition: khtml_caret_p.h:221
khtml::LinearDocument::_end
Iterator _end
Definition: khtml_caret_p.h:718
khtml::LinearDocument::LinearDocument
LinearDocument(KHTMLPart *part, DOM::NodeImpl *node, long offset, CaretAdvancePolicy advancePolicy, DOM::ElementImpl *baseElem)
Creates a new instance, and initializes it to the line specified by the parameters below...
Definition: khtml_caret.cpp:1244
khtml::CaretBoxIterator::operator++
CaretBoxIterator & operator++()
increments the iterator to point to the next caret box.
Definition: khtml_caret_p.h:255
khtml::CaretViewContext
contextual information about the caret which is related to the view.
Definition: khtml_caret_p.h:60
khtml::CaretBox::object
RenderObject * object() const
returns the associated render object.
Definition: khtml_caret_p.h:197
khtml::EditableCharacterIterator::_it
EditableLineIterator _it
Definition: khtml_caret_p.h:986
khtml::CaretBox::isInline
bool isInline() const
returns the replaced render object if this caret box represents one, 0 otherwise. ...
Definition: khtml_caret_p.h:175
khtml::CaretBox::isOutside
bool isOutside() const
returns true when this caret box represents an ouside position of an element.
Definition: khtml_caret_p.h:188
khtml::CaretBox::inlineBox
InlineBox * inlineBox() const
Definition: khtml_caret_p.h:160
khtml::LinearDocument::LineIterator
friend class LineIterator
Definition: khtml_caret_p.h:724
khtml::ErgonomicEditableLineIterator::determineTopologicalElement
void determineTopologicalElement(RenderTableCell *oldCell, RenderObject *newObject, bool toBegin)
determines the topologically next render object.
Definition: khtml_caret.cpp:2282
khtml::LineIterator::nextBlock
void nextBlock()
seeks next block.
Definition: khtml_caret.cpp:1345
khtml::LinearDocument::m_part
KHTMLPart * m_part
Definition: khtml_caret_p.h:720
khtml::CaretBox::isInlineTextBox
bool isInlineTextBox() const
returns true if this caret box represents an inline text box.
Definition: khtml_caret_p.h:178
KHTMLPart::isEditable
bool isEditable() const
Returns true if the document is editable, false otherwise.
Definition: khtml_part.cpp:2907
khtml::EditableCharacterIterator::initFirstChar
void initFirstChar()
initializes the _char member by reading the character at the current offset, peeking ahead as necessa...
Definition: khtml_caret.cpp:1744
khtml::ErgonomicEditableLineIterator::operator++
ErgonomicEditableLineIterator & operator++()
seek next line.
Definition: khtml_caret.cpp:2404
khtml::CaretBox::maxOffset
long maxOffset() const
returns the maximum offset for this caret box.
Definition: khtml_caret_p.h:204
khtml::CaretBox::_h
int _h
Definition: khtml_caret_p.h:130
khtml::EditableLineIterator::advance
void advance(bool toBegin)
advances to the line to come.
Definition: khtml_caret.cpp:1688
khtml::EditableCharacterIterator::ebit
EditableCaretBoxIterator ebit
Definition: khtml_caret_p.h:987
khtml::LinearDocument::advancePolicy
CaretAdvancePolicy advancePolicy() const
Returns the current caret advance policy.
Definition: khtml_caret_p.h:697
khtml::EditableCharacterIterator::_char
int _char
Definition: khtml_caret_p.h:989
khtml::LineIterator
Iterates through the lines of a document.
Definition: khtml_caret_p.h:487
KHTMLPart::isCaretMode
bool isCaretMode() const
Returns whether caret mode is on/off.
Definition: khtml_part.cpp:2887
khtml::LinearDocument
Represents the whole document in terms of lines.
Definition: khtml_caret_p.h:622
khtml::CaretBox
Represents a rectangular box within which the caret is located.
Definition: khtml_caret_p.h:126
kWarning
static QDebug kWarning(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
khtml::CaretBox::isOutsideEnd
bool isOutsideEnd() const
returns the position at which the outside is targeted at.
Definition: khtml_caret_p.h:195
khtml::EditableCharacterIterator::operator--
EditableCharacterIterator & operator--()
moves to the previous editable character.
Definition: khtml_caret.cpp:1869
khtml::CaretBox::_w
short _w
Definition: khtml_caret_p.h:129
khtml::CaretBoxIterator::operator--
CaretBoxIterator & operator--()
decrements the iterator to point to the previous caret box.
Definition: khtml_caret_p.h:258
khtml::LinearDocument::baseObject
RenderObject * baseObject() const
Returns the base render object which the caret must not advance beyond.
Definition: khtml_caret_p.h:706
khtml::CaretBoxLine::end
CaretBoxIterator end()
Definition: khtml_caret_p.h:310
khtml_caret_p.h
khtml::LinearDocument::initPreBeginIterator
void initPreBeginIterator()
Definition: khtml_caret.cpp:1304
khtml::EditableCharacterIterator
Provides iterating through the document in terms of characters.
Definition: khtml_caret_p.h:984
khtml::LineIterator::advance
void advance(bool toBegin)
advances to the line to come.
Definition: khtml_caret.cpp:1482
khtml::EditableCharacterIterator::operator++
EditableCharacterIterator & operator++()
returns whether the current line box represents the outside of its render object. ...
Definition: khtml_caret.cpp:1767
khtml::ErgonomicEditableLineIterator::xCoor
int xCoor
Definition: khtml_caret_p.h:931
khtml::CaretBoxLine::basefb
InlineFlowBox * basefb
Definition: khtml_caret_p.h:294
khtml::EditableLineIterator::operator--
EditableLineIterator & operator--()
seek previous line.
Definition: khtml_caret_p.h:840
end
const KShortcut & end()
khtml::EditableCaretBoxIterator::isEditable
bool isEditable(const CaretBoxIterator &boxit, bool fromEnd)
finds out if the given box is editable.
Definition: khtml_caret.cpp:1642
khtml::EditableCaretBoxIterator
Iterates over the editable inner elements of a caret line box.
Definition: khtml_caret_p.h:742
khtml::CaretBoxLine::SeekBoxParams
contains the seek parameters
Definition: khtml_caret_p.h:402
khtml::CaretBoxLine::addCreatedFlowBoxInside
void addCreatedFlowBoxInside(InlineFlowBox *flowBox, const QFontMetrics &fm)
creates and adds the inside of an inline flow box
Definition: khtml_caret.cpp:771
khtml::EditableLineIterator::isEditable
bool isEditable(LineIterator &it)
finds out if the current line is editable.
Definition: khtml_caret_p.h:853
khtml::LineIterator::LineIterator
LineIterator()
Default constructor, only for internal use.
Definition: khtml_caret_p.h:501
khtml::CaretBoxLine::SeekBoxParams::found
bool found
Definition: khtml_caret_p.h:406
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:51:21 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KHTML

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

kdelibs API Reference

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

Search



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

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