KHtml

khtml_caret.cpp
1 /* This file is part of the KDE project
2  *
3  * Copyright (C) 2003-2004 Leo Savernik <[email protected]>
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 #include "khtml_caret_p.h"
22 
23 #include "html/html_documentimpl.h"
24 
25 namespace khtml
26 {
27 
28 /** Flags representing the type of advance that has been made.
29  * @param LeftObject a render object was left and an ascent to its parent has
30  * taken place
31  * @param AdvancedToSibling an actual advance to a sibling has taken place
32  * @param EnteredObject a render object was entered by descending into it from
33  * its parent object.
34  */
36  LeftObject = 0x01, AdvancedToSibling = 0x02, EnteredObject = 0x04
37 };
38 
39 /** All possible states that may occur during render object traversal.
40  * @param OutsideDescending outside of the current object, ready to descend
41  * into children
42  * @param InsideDescending inside the current object, descending into
43  * children
44  * @param InsideAscending inside the current object, ascending to parents
45  * @param OutsideAscending outsie the current object, ascending to parents
46  */
48  OutsideDescending, InsideDescending, InsideAscending, OutsideAscending
49 };
50 
51 /** Traverses the render object tree in a fine granularity.
52  * @param obj render object
53  * @param trav object traversal state
54  * @param toBegin traverse towards beginning
55  * @param base base render object which this method must not advance beyond
56  * (0 means document)
57  * @param state object advance state (enum ObjectAdvanceState)
58  * @return the render object according to the state. May be the same as \c obj
59  */
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) {
90  r = 0;
91  }
92  trav = InsideAscending;
93  state |= LeftObject;
94  }
95  obj = r;
96  break;
97  }/*end switch*/
98 
99  return obj;
100 }
101 
102 /** Like RenderObject::objectBelow, but confined to stay within \c base.
103  * @param obj render object to begin with
104  * @param trav object traversal state, will be reset within this function
105  * @param base base render object (0: no base)
106  */
108 {
109  trav = InsideDescending;
110  int state; // we don't need the state, so we don't initialize it
111  RenderObject *r = obj;
112  while (r && trav != OutsideDescending) {
113  r = traverseRenderObjects(r, trav, false, base, state);
114 #if DEBUG_CARETMODE > 3
115  // qCDebug(KHTML_LOG) << "renderObjectBelow: r " << r << " trav " << trav;
116 #endif
117  }
118  trav = InsideDescending;
119  return r;
120 }
121 
122 /** Like RenderObject::objectAbove, but confined to stay within \c base.
123  * @param obj render object to begin with
124  * @param trav object traversal state, will be reset within this function
125  * @param base base render object (0: no base)
126  */
128 {
129  trav = OutsideAscending;
130  int state; // we don't need the state, so we don't initialize it
131  RenderObject *r = obj;
132  while (r && trav != InsideAscending) {
133  r = traverseRenderObjects(r, trav, true, base, state);
134 #if DEBUG_CARETMODE > 3
135  // qCDebug(KHTML_LOG) << "renderObjectAbove: r " << r << " trav " << trav;
136 #endif
137  }
138  trav = InsideAscending;
139  return r;
140 }
141 
142 /** Checks whether the given inline box matches the IndicatedFlows policy
143  * @param box inline box to test
144  * @return true on match
145  */
146 static inline bool isIndicatedInlineBox(InlineBox *box)
147 {
148  // text boxes are never indicated.
149  if (box->isInlineTextBox()) {
150  return false;
151  }
152  RenderStyle *s = box->object()->style();
153  return s->borderLeftWidth() || s->borderRightWidth()
154  || s->borderTopWidth() || s->borderBottomWidth()
155  || s->paddingLeft().value() || s->paddingRight().value()
156  || s->paddingTop().value() || s->paddingBottom().value()
157  // ### Can inline elements have top/bottom margins? Couldn't find
158  // it in the CSS 2 spec, but Mozilla ignores them, so we do, too.
159  || s->marginLeft().value() || s->marginRight().value();
160 }
161 
162 /** Checks whether the given render object matches the IndicatedFlows policy
163  * @param r render object to test
164  * @return true on match
165  */
166 static inline bool isIndicatedFlow(RenderObject *r)
167 {
168  RenderStyle *s = r->style();
169  return s->borderLeftStyle() != BNONE || s->borderRightStyle() != BNONE
170  || s->borderTopStyle() != BNONE || s->borderBottomStyle() != BNONE
171 // || s->paddingLeft().value() || s->paddingRight().value()
172 // || s->paddingTop().value() || s->paddingBottom().value()
173 // || s->marginLeft().value() || s->marginRight().value()
174  || s->hasClip() || s->hidesOverflow()
175  || s->backgroundColor().isValid() || s->backgroundImage();
176 }
177 
178 /** Advances to the next render object, taking into account the current
179  * traversal state.
180  *
181  * @param r render object
182  * @param trav object traversal state
183  * @param toBegin @p true, advance towards beginning, @p false, advance toward end
184  * @param base base render object which this method must not advance beyond
185  * (0 means document)
186  * @param state object advance state (enum ObjectAdvanceState) (unchanged
187  * on LeafsOnly)
188  * @return a pointer to the render object which we advanced to,
189  * or 0 if the last possible object has been reached.
190  */
192  ObjectTraversalState &trav, bool toBegin,
193  RenderObject *base, int &state)
194 {
195 
196  ObjectTraversalState origtrav = trav;
197  RenderObject *a = traverseRenderObjects(r, trav, toBegin, base, state);
198 
199  bool ignoreOutsideDesc = toBegin && origtrav == OutsideAscending;
200 
201  // render object and traversal state at which look ahead has been started
202  RenderObject *la = 0;
203  ObjectTraversalState latrav = trav;
204  ObjectTraversalState lasttrav = origtrav;
205 
206  while (a) {
207 #if DEBUG_CARETMODE > 5
208 // qCDebug(KHTML_LOG) << "a " << a << " trav " << trav;
209 #endif
210  if (a->element()) {
211 #if DEBUG_CARETMODE > 4
212 // qCDebug(KHTML_LOG) << "a " << a << " trav " << trav << " origtrav " << origtrav << " ignoreOD " << ignoreOutsideDesc;
213 #endif
214  if (toBegin) {
215 
216  switch (origtrav) {
217  case OutsideDescending:
218  if (trav == InsideAscending) {
219  return a;
220  }
221  if (trav == OutsideDescending) {
222  return a;
223  }
224  break;
225  case InsideDescending:
226  if (trav == OutsideDescending) {
227  return a;
228  }
229  // fall through
230  case InsideAscending:
231  if (trav == OutsideAscending) {
232  return a;
233  }
234  break;
235  case OutsideAscending:
236  if (trav == OutsideAscending) {
237  return a;
238  }
239  if (trav == InsideAscending && lasttrav == InsideDescending) {
240  return a;
241  }
242  if (trav == OutsideDescending && !ignoreOutsideDesc) {
243  return a;
244  }
245  // ignore this outside descending position, as it effectively
246  // demarkates the same position, but remember it in case we fall off
247  // the document.
248  la = a; latrav = trav;
249  ignoreOutsideDesc = false;
250  break;
251  }/*end switch*/
252 
253  } else {
254 
255  switch (origtrav) {
256  case OutsideDescending:
257  if (trav == InsideAscending) {
258  return a;
259  }
260  if (trav == OutsideDescending) {
261  return a;
262  }
263  break;
264  case InsideDescending:
265 // if (trav == OutsideDescending) return a;
266  // fall through
267  case InsideAscending:
268 // if (trav == OutsideAscending) return a;
269 // break;
270  case OutsideAscending:
271  // ### what if origtrav == OA, and immediately afterwards trav
272  // becomes OD? In this case the effective position hasn't changed ->
273  // the caret gets stuck. Otherwise, it apparently cannot happen in
274  // real usage patterns.
275  if (trav == OutsideDescending) {
276  return a;
277  }
278  if (trav == OutsideAscending) {
279  if (la) {
280  return la;
281  }
282  // starting lookahead here. Remember old object in case we fall off
283  // the document.
284  la = a; latrav = trav;
285  }
286  break;
287  }/*end switch*/
288 
289  }/*end if*/
290  }/*end if*/
291 
292  lasttrav = trav;
293  a = traverseRenderObjects(a, trav, toBegin, base, state);
294  }/*wend*/
295 
296  if (la) {
297  trav = latrav, a = la;
298  }
299  return a;
300 
301 }
302 
303 /** Check whether the current render object is unsuitable in caret mode handling.
304  *
305  * Some render objects cannot be handled correctly in caret mode. These objects
306  * are therefore considered to be unsuitable. The null object is suitable, as
307  * it denotes reaching the end.
308  * @param r current render object
309  * @param trav current traversal state
310  */
311 static inline bool isUnsuitable(RenderObject *r, ObjectTraversalState trav)
312 {
313  if (!r) {
314  return false;
315  }
316  return r->isTableCol() || r->isTableSection() || r->isTableRow()
317  || (r->isText() && !static_cast<RenderText *>(r)->firstTextBox());
318  ;
319  Q_UNUSED(trav);
320 }
321 
322 /** Advances to the next render object, taking into account the current
323  * traversal state, but skipping render objects which are not suitable for
324  * having placed the caret into them.
325  * @param r render object
326  * @param trav object traversal state (unchanged on LeafsOnly)
327  * @param toBegin @p true, advance towards beginning, @p false, advance toward end
328  * @param base base render object which this method must not advance beyond
329  * (0 means document)
330  * @param state object advance state (enum ObjectAdvanceState) (unchanged
331  * on LeafsOnly)
332  * @return a pointer to the advanced render object or 0 if the last possible
333  * object has been reached.
334  */
336  ObjectTraversalState &trav, bool toBegin,
337  RenderObject *base, int &state)
338 {
339  do {
340  r = advanceObject(r, trav, toBegin, base, state);
341 #if DEBUG_CARETMODE > 2
342  // qCDebug(KHTML_LOG) << "after advanceSWP: r " << r << " trav " << trav << " toBegin " << toBegin;
343 #endif
344  } while (isUnsuitable(r, trav));
345  return r;
346 }
347 
348 /**
349  * Returns the next leaf node.
350  *
351  * Using this function delivers leaf nodes as if the whole DOM tree
352  * were a linear chain of its leaf nodes.
353  * @param r dom node
354  * @param baseElem base element not to search beyond
355  * @return next leaf node or 0 if there are no more.
356  */
357 static NodeImpl *nextLeafNode(NodeImpl *r, NodeImpl *baseElem)
358 {
359  NodeImpl *n = r->firstChild();
360  if (n) {
361  while (n) {
362  r = n;
363  n = n->firstChild();
364  }
365  return const_cast<NodeImpl *>(r);
366  }/*end if*/
367  n = r->nextSibling();
368  if (n) {
369  r = n;
370  while (n) {
371  r = n;
372  n = n->firstChild();
373  }
374  return const_cast<NodeImpl *>(r);
375  }/*end if*/
376 
377  n = r->parentNode();
378  if (n == baseElem) {
379  n = 0;
380  }
381  while (n) {
382  r = n;
383  n = r->nextSibling();
384  if (n) {
385  r = n;
386  n = r->firstChild();
387  while (n) {
388  r = n;
389  n = n->firstChild();
390  }
391  return const_cast<NodeImpl *>(r);
392  }/*end if*/
393  n = r->parentNode();
394  if (n == baseElem) {
395  n = 0;
396  }
397  }/*wend*/
398  return 0;
399 }
400 
401 #if 0 // currently not used
402 /** (Not part of the official DOM)
403  * Returns the previous leaf node.
404  *
405  * Using this function delivers leaf nodes as if the whole DOM tree
406  * were a linear chain of its leaf nodes.
407  * @param r dom node
408  * @param baseElem base element not to search beyond
409  * @return previous leaf node or 0 if there are no more.
410  */
411 static NodeImpl *prevLeafNode(NodeImpl *r, NodeImpl *baseElem)
412 {
413  NodeImpl *n = r->firstChild();
414  if (n) {
415  while (n) {
416  r = n;
417  n = n->firstChild();
418  }
419  return const_cast<NodeImpl *>(r);
420  }/*end if*/
421  n = r->previousSibling();
422  if (n) {
423  r = n;
424  while (n) {
425  r = n;
426  n = n->firstChild();
427  }
428  return const_cast<NodeImpl *>(r);
429  }/*end if*/
430 
431  n = r->parentNode();
432  if (n == baseElem) {
433  n = 0;
434  }
435  while (n) {
436  r = n;
437  n = r->previousSibling();
438  if (n) {
439  r = n;
440  n = r->lastChild();
441  while (n) {
442  r = n;
443  n = n->lastChild();
444  }
445  return const_cast<NodeImpl *>(r);
446  }/*end if*/
447  n = r->parentNode();
448  if (n == baseElem) {
449  n = 0;
450  }
451  }/*wend*/
452  return 0;
453 }
454 #endif
455 
456 /** Maps a DOM Range position to the corresponding caret position.
457  *
458  * The offset boundary is not checked for validity.
459  * @param node DOM node
460  * @param offset zero-based offset within node
461  * @param r returns render object (may be 0 if DOM node has no render object)
462  * @param r_ofs returns the appropriate offset for the found render object r
463  * @param outside returns true when offset is applied to the outside of
464  * \c r, or false for the inside.
465  * @param outsideEnd return true when the caret position is at the outside end.
466  */
467 void /*KHTML_NO_EXPORT*/ mapDOMPosToRenderPos(NodeImpl *node, long offset,
468  RenderObject *&r, long &r_ofs, bool &outside, bool &outsideEnd)
469 {
470  if (node->nodeType() == Node::TEXT_NODE) {
471  outside = false;
472  outsideEnd = false;
473  r = node->renderer();
474  r_ofs = offset;
475  } else if (node->nodeType() == Node::ELEMENT_NODE || node->nodeType() == Node::DOCUMENT_NODE) {
476 
477  // Though offset points between two children, attach it to the visually
478  // most suitable one (and only there, because the mapping must stay bijective)
479  if (node->firstChild()) {
480  outside = true;
481  NodeImpl *child = offset <= 0 ? node->firstChild()
482  // childNode is expensive
483  : node->childNode((unsigned long)offset);
484  // index was child count or out of bounds
485  bool atEnd = !child;
486 #if DEBUG_CARETMODE > 5
487  // qCDebug(KHTML_LOG) << "mapDTR: child " << child << "@" << (child ? child->nodeName().string() : QString()) << " atEnd " << atEnd;
488 #endif
489  if (atEnd) {
490  child = node->lastChild();
491  }
492 
493  r = child->renderer();
494  r_ofs = 0;
495  outsideEnd = atEnd;
496 
497  // Outside text nodes most likely stem from a continuation. Seek
498  // the enclosing continued render object and use this one instead.
499  if (r && child->nodeType() == Node::TEXT_NODE) {
500  r = r->parent();
501  RenderObject *o = node->renderer();
502  while (o->continuation() && o->continuation() != r) {
503  o = o->continuation();
504  }
505  if (!r || o->continuation() != r) {
506  r = child->renderer();
507  }
508  }/*end if*/
509 
510  // BRs cause troubles. Returns the previous render object instead,
511  // giving it the attributes outside, outside end.
512  if (r && r->isBR()) {
513  r = r->objectAbove();
514  outsideEnd = true;
515  }/*end if*/
516 
517  } else {
518  // Element has no children, treat offset to be inside the node.
519  outside = false;
520  outsideEnd = false;
521  r = node->renderer();
522  r_ofs = 0; // only offset 0 possible
523  }
524 
525  } else {
526  r = 0;
527  qCWarning(KHTML_LOG) << "Mapping from nodes of type " << node->nodeType()
528  << " not supported!";
529  }
530 }
531 
532 /** Maps a caret position to the corresponding DOM Range position.
533  *
534  * @param r render object
535  * @param r_ofs offset within render object
536  * @param outside true when offset is interpreted to be on the outside of
537  * \c r, or false if on the inside.
538  * @param outsideEnd true when the caret position is at the outside end.
539  * @param node returns DOM node
540  * @param offset returns zero-based offset within node
541  */
542 void /*KHTML_NO_EXPORT*/ mapRenderPosToDOMPos(RenderObject *r, long r_ofs,
543  bool outside, bool outsideEnd, NodeImpl *&node, long &offset)
544 {
545  node = r->element();
546  Q_ASSERT(node);
547 #if DEBUG_CARETMODE > 5
548  // qCDebug(KHTML_LOG) << "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;
549 #endif
550  if (node->nodeType() == Node::ELEMENT_NODE || node->nodeType() == Node::TEXT_NODE) {
551 
552  if (outside) {
553  NodeImpl *parent = node->parent();
554 
555  // If this is part of a continuation, use the actual node as the parent,
556  // and the first render child as the node.
557  if (r != node->renderer()) {
558  RenderObject *o = node->renderer();
559  while (o->continuation() && o->continuation() != r) {
560  o = o->continuation();
561  }
562  if (o->continuation() == r) {
563  parent = node;
564  // ### What if the first render child does not map to a child of
565  // the continued node?
566  node = r->firstChild() ? r->firstChild()->element() : node;
567  }
568  }/*end if*/
569 
570  if (!parent) {
571  goto inside;
572  }
573 
574  offset = (long)node->nodeIndex() + outsideEnd;
575  node = parent;
576 #if DEBUG_CARETMODE > 5
577  // qCDebug(KHTML_LOG) << node << "@" << (node ? node->nodeName().string() : QString()) << " offset " << offset;
578 #endif
579  } else { // !outside
580  inside:
581  offset = r_ofs;
582  }
583 
584  } else {
585  offset = 0;
586  qCWarning(KHTML_LOG) << "Mapping to nodes of type " << node->nodeType()
587  << " not supported!";
588  }
589 }
590 
591 /** Make sure the given node is a leaf node. */
592 static inline void ensureLeafNode(NodeImpl *&node, NodeImpl *base)
593 {
594  if (node && node->hasChildNodes()) {
595  node = nextLeafNode(node, base);
596  }
597 }
598 
599 /** Converts a caret position to its respective object traversal state.
600  * @param outside whether the caret is outside the object
601  * @param atEnd whether the caret position is at the end
602  * @param toBegin \c true when advancing towards the beginning
603  * @param trav returns the corresponding traversal state
604  */
605 static inline void mapRenderPosToTraversalState(bool outside, bool atEnd,
606  bool toBegin, ObjectTraversalState &trav)
607 {
608  if (!outside) {
609  atEnd = !toBegin;
610  }
611  if (!atEnd ^ toBegin) {
612  trav = outside ? OutsideDescending : InsideDescending;
613  } else {
614  trav = outside ? OutsideAscending : InsideAscending;
615  }
616 }
617 
618 /** Converts a traversal state to its respective caret position
619  * @param trav object traversal state
620  * @param toBegin \c true when advancing towards the beginning
621  * @param outside whether the caret is outside the object
622  * @param atEnd whether the caret position is at the end
623  */
625  bool toBegin, bool &outside, bool &atEnd)
626 {
627  outside = false;
628  switch (trav) {
629  case OutsideDescending: outside = true; // fall through
630  case InsideDescending: atEnd = toBegin; break;
631  case OutsideAscending: outside = true; // fall through
632  case InsideAscending: atEnd = !toBegin; break;
633  }
634 }
635 
636 /** Finds the next node that has a renderer.
637  *
638  * Note that if the initial @p node has a renderer, this will be returned,
639  * regardless of the caret advance policy.
640  * Otherwise, for the next nodes, only leaf nodes are considered.
641  * @param node node to start with, will be updated accordingly
642  * @param offset offset of caret within \c node
643  * @param base base render object which this method must not advance beyond
644  * (0 means document)
645  * @param r_ofs return the caret offset within the returned renderer
646  * @param outside returns whether offset is to be interpreted to the outside
647  * (true) or the inside (false) of the render object.
648  * @param outsideEnd returns whether the end of the outside position is meant
649  * @return renderer or 0 if no following node has a renderer.
650  */
651 static RenderObject *findRenderer(NodeImpl *&node, long offset,
652  RenderObject *base, long &r_ofs,
653  bool &outside, bool &outsideEnd)
654 {
655  if (!node) {
656  return 0;
657  }
658  RenderObject *r;
659  mapDOMPosToRenderPos(node, offset, r, r_ofs, outside, outsideEnd);
660 #if DEBUG_CARETMODE > 2
661  // qCDebug(KHTML_LOG) << "findRenderer: node " << node << " " << (node ? node->nodeName().string() : QString()) << " offset " << offset << " r " << r << "[" << (r ? r->renderName() : QString()) << "] r_ofs " << r_ofs << " outside " << outside << " outsideEnd " << outsideEnd;
662 #endif
663  if (r) {
664  return r;
665  }
666  NodeImpl *baseElem = base ? base->element() : 0;
667  while (!r) {
668  node = nextLeafNode(node, baseElem);
669  if (!node) {
670  break;
671  }
672  r = node->renderer();
673  if (r) {
674  r_ofs = offset;
675  }
676  }
677 #if DEBUG_CARETMODE > 3
678  // qCDebug(KHTML_LOG) << "1r " << r;
679 #endif
681  int state; // not used
682  mapRenderPosToTraversalState(outside, outsideEnd, false, trav);
683  if (r && isUnsuitable(r, trav)) {
684  r = advanceSuitableObject(r, trav, false, base, state);
685  mapTraversalStateToRenderPos(trav, false, outside, outsideEnd);
686  if (r) {
687  r_ofs = r->minOffset();
688  }
689  }
690 #if DEBUG_CARETMODE > 3
691  // qCDebug(KHTML_LOG) << "2r " << r;
692 #endif
693  return r;
694 }
695 
696 /** returns a suitable base element
697  * @param caretNode current node containing caret.
698  */
699 static ElementImpl *determineBaseElement(NodeImpl *caretNode)
700 {
701  // ### for now, only body is delivered for html documents,
702  // and 0 for xml documents.
703 
704  DocumentImpl *doc = caretNode->getDocument();
705  if (!doc) {
706  return 0; // should not happen, but who knows.
707  }
708 
709  if (doc->isHTMLDocument()) {
710  return static_cast<HTMLDocumentImpl *>(doc)->body();
711  }
712 
713  return 0;
714 }
715 
716 // == class CaretBox implementation
717 
718 #if DEBUG_CARETMODE > 0
719 void CaretBox::dump(QTextStream &ts, const QString &ind) const
720 {
721  ts << ind << "[email protected]" << _box;
722 
723  if (_box) {
724  ts << "<" << _box->object() << ":" << _box->object()->renderName() << ">";
725  }/*end if*/
726 
727  ts << " " << _x << "+" << _y << "+" << _w << "*" << _h;
728 
729  ts << " [email protected]" << cb;
730  if (cb) {
731  ts << ":" << cb->renderName();
732  }
733 
734  ts << " " << (_outside ? (outside_end ? "oe" : "o-") : "i-");
735 // ts << endl;
736 }
737 #endif
738 
739 // == class CaretBoxLine implementation
740 
741 #if DEBUG_CARETMODE > 0
742 # define DEBUG_ACIB 1
743 #else
744 # define DEBUG_ACIB DEBUG_CARETMODE
745 #endif
746 void CaretBoxLine::addConvertedInlineBox(InlineBox *box, SeekBoxParams &sbp) /*KHTML_NO_EXPORT*/
747 {
748  // Generate only one outside caret box between two elements. If
749  // coalesceOutsideBoxes is true, generating left outside boxes is inhibited.
750  bool coalesceOutsideBoxes = false;
751  CaretBoxIterator lastCoalescedBox;
752  for (; box; box = box->nextOnLine()) {
753 #if DEBUG_ACIB
754 // qCDebug(KHTML_LOG) << "box " << box;
755 // qCDebug(KHTML_LOG) << "box->object " << box->object();
756 // qCDebug(KHTML_LOG) << "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();
757 #endif
758  // ### Why the hell can object() ever be 0?!
759  if (!box->object()) {
760  continue;
761  }
762 
763  RenderStyle *s = box->object()->style(box->m_firstLine);
764  // parent style for outside caret boxes
765  RenderStyle *ps = box->parent() && box->parent()->object()
766  ? box->parent()->object()->style(box->parent()->m_firstLine)
767  : s;
768 
769  if (box->isInlineFlowBox()) {
770 #if DEBUG_ACIB
771 // qCDebug(KHTML_LOG) << "isinlineflowbox " << box;
772 #endif
773  InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(box);
774  bool rtl = ps->direction() == RTL;
775  const QFontMetrics &pfm = ps->fontMetrics();
776 
777  if (flowBox->includeLeftEdge()) {
778  // If this box is to be coalesced with the outside end box of its
779  // predecessor, then check if it is the searched box. If it is, we
780  // substitute the outside end box.
781  if (coalesceOutsideBoxes) {
782  if (sbp.equalsBox(flowBox, true, false)) {
783  sbp.it = lastCoalescedBox;
784  Q_ASSERT(!sbp.found);
785  sbp.found = true;
786  }
787  } else {
788  addCreatedFlowBoxEdge(flowBox, pfm, true, rtl);
789  sbp.check(preEnd());
790  }
791  }/*end if*/
792 
793  if (flowBox->firstChild()) {
794 #if DEBUG_ACIB
795 // qCDebug(KHTML_LOG) << "this " << this << " flowBox " << flowBox << " firstChild " << flowBox->firstChild();
796 // qCDebug(KHTML_LOG) << "== recursive invocation";
797 #endif
798  addConvertedInlineBox(flowBox->firstChild(), sbp);
799 #if DEBUG_ACIB
800 // qCDebug(KHTML_LOG) << "== recursive invocation end";
801 #endif
802  } else {
803  addCreatedFlowBoxInside(flowBox, s->fontMetrics());
804  sbp.check(preEnd());
805  }
806 
807  if (flowBox->includeRightEdge()) {
808  addCreatedFlowBoxEdge(flowBox, pfm, false, rtl);
809  lastCoalescedBox = preEnd();
810  sbp.check(lastCoalescedBox);
811  coalesceOutsideBoxes = true;
812  }
813 
814  } else if (box->isInlineTextBox()) {
815 #if DEBUG_ACIB
816 // qCDebug(KHTML_LOG) << "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());
817 #endif
818  caret_boxes.append(new CaretBox(box, false, false));
819  sbp.check(preEnd());
820  // coalescing has been interrupted
821  coalesceOutsideBoxes = false;
822 
823  } else {
824 #if DEBUG_ACIB
825 // qCDebug(KHTML_LOG) << "some replaced or what " << box;
826 #endif
827  // must be an inline-block, inline-table, or any RenderReplaced
828  bool rtl = ps->direction() == RTL;
829  const QFontMetrics &pfm = ps->fontMetrics();
830 
831  if (coalesceOutsideBoxes) {
832  if (sbp.equalsBox(box, true, false)) {
833  sbp.it = lastCoalescedBox;
834  Q_ASSERT(!sbp.found);
835  sbp.found = true;
836  }
837  } else {
838  addCreatedInlineBoxEdge(box, pfm, true, rtl);
839  sbp.check(preEnd());
840  }
841 
842  caret_boxes.append(new CaretBox(box, false, false));
843  sbp.check(preEnd());
844 
845  addCreatedInlineBoxEdge(box, pfm, false, rtl);
846  lastCoalescedBox = preEnd();
847  sbp.check(lastCoalescedBox);
848  coalesceOutsideBoxes = true;
849  }/*end if*/
850  }/*next box*/
851 }
852 #undef DEBUG_ACIB
853 
854 void CaretBoxLine::addCreatedFlowBoxInside(InlineFlowBox *flowBox, const QFontMetrics &fm) /*KHTML_NO_EXPORT*/
855 {
856 
857  CaretBox *caretBox = new CaretBox(flowBox, false, false);
858  caret_boxes.append(caretBox);
859 
860  // afaik an inner flow box can only have the width 0, therefore we don't
861  // have to care for rtl or alignment
862  // ### can empty inline elements have a width? css 2 spec isn't verbose about it
863 
864  caretBox->_y += flowBox->baseline() - fm.ascent();
865  caretBox->_h = fm.height();
866 }
867 
868 void CaretBoxLine::addCreatedFlowBoxEdge(InlineFlowBox *flowBox, const QFontMetrics &fm, bool left, bool rtl) /*KHTML_NO_EXPORT*/
869 {
870  CaretBox *caretBox = new CaretBox(flowBox, true, !left);
871  caret_boxes.append(caretBox);
872 
873  if (left ^ rtl) {
874  caretBox->_x -= flowBox->paddingLeft() + flowBox->borderLeft() + 1;
875  } else {
876  caretBox->_x += caretBox->_w + flowBox->paddingRight() + flowBox->borderRight();
877  }
878 
879  caretBox->_y += flowBox->baseline() - fm.ascent();
880  caretBox->_h = fm.height();
881  caretBox->_w = 1;
882 }
883 
884 void CaretBoxLine::addCreatedInlineBoxEdge(InlineBox *box, const QFontMetrics &fm, bool left, bool rtl) /*KHTML_NO_EXPORT*/
885 {
886  CaretBox *caretBox = new CaretBox(box, true, !left);
887  caret_boxes.append(caretBox);
888 
889  if (left ^ rtl) {
890  caretBox->_x--;
891  } else {
892  caretBox->_x += caretBox->_w;
893  }
894 
895  caretBox->_y += box->baseline() - fm.ascent();
896  caretBox->_h = fm.height();
897  caretBox->_w = 1;
898 }
899 
900 CaretBoxLine *CaretBoxLine::constructCaretBoxLine(CaretBoxLineDeleter *deleter,
901  InlineFlowBox *basicFlowBox, InlineBox *seekBox, bool seekOutside,
902  bool seekOutsideEnd, CaretBoxIterator &iter, RenderObject *seekObject)
903 // KHTML_NO_EXPORT
904 {
905  // Iterate all inline boxes within this inline flow box.
906  // Caret boxes will be created for each
907  // - outside begin of an inline flow box (except for the basic inline flow box)
908  // - outside end of an inline flow box (except for the basic inline flow box)
909  // - inside of an empty inline flow box
910  // - outside begin of an inline box resembling a replaced element
911  // - outside end of an inline box resembling a replaced element
912  // - inline text box
913  // - inline replaced box
914 
915  CaretBoxLine *result = new CaretBoxLine(basicFlowBox);
916  deleter->append(result);
917 
918  SeekBoxParams sbp(seekBox, seekOutside, seekOutsideEnd, seekObject, iter);
919 
920  // iterate recursively, I'm too lazy to do it iteratively
921  result->addConvertedInlineBox(basicFlowBox, sbp);
922 
923  if (!sbp.found) {
924  sbp.it = result->end();
925  }
926 
927  return result;
928 }
929 
930 CaretBoxLine *CaretBoxLine::constructCaretBoxLine(CaretBoxLineDeleter *deleter,
931  RenderBox *cb, bool outside, bool outsideEnd, CaretBoxIterator &iter) /*KHTML_NO_EXPORT*/
932 {
933  int _x = cb->xPos();
934  int _y = cb->yPos();
935  int height;
936  int width = 1; // no override is indicated in boxes
937 
938  if (outside) {
939 
940  RenderStyle *s = cb->element() && cb->element()->parent()
941  && cb->element()->parent()->renderer()
942  ? cb->element()->parent()->renderer()->style()
943  : cb->style();
944  bool rtl = s->direction() == RTL;
945 
946  const QFontMetrics &fm = s->fontMetrics();
947  height = fm.height();
948 
949  if (!outsideEnd) {
950  _x--;
951  } else {
952  _x += cb->width();
953  }
954 
955  int hl = fm.leading() / 2;
956  int baseline = cb->baselinePosition(false);
957  if (!cb->isReplaced() || cb->style()->display() == BLOCK) {
958  if (!outsideEnd ^ rtl) {
959  _y -= fm.leading() / 2;
960  } else {
961  _y += qMax(cb->height() - fm.ascent() - hl, 0);
962  }
963  } else {
964  _y += baseline - fm.ascent() - hl;
965  }
966 
967  } else { // !outside
968 
969  RenderStyle *s = cb->style();
970  const QFontMetrics &fm = s->fontMetrics();
971  height = fm.height();
972 
973  _x += cb->borderLeft() + cb->paddingLeft();
974  _y += cb->borderTop() + cb->paddingTop();
975 
976  // ### regard direction
977  switch (s->textAlign()) {
978  case LEFT:
979  case KHTML_LEFT:
980  case TAAUTO: // ### find out what this does
981  case JUSTIFY:
982  break;
983  case CENTER:
984  case KHTML_CENTER:
985  _x += cb->contentWidth() / 2;
986  break;
987  case KHTML_RIGHT:
988  case RIGHT:
989  _x += cb->contentWidth();
990  break;
991  }/*end switch*/
992  }/*end if*/
993 
994  CaretBoxLine *result = new CaretBoxLine;
995  deleter->append(result);
996  result->caret_boxes.append(new CaretBox(_x, _y, width, height, cb,
997  outside, outsideEnd));
998  iter = result->begin();
999  return result;
1000 }
1001 
1002 #if DEBUG_CARETMODE > 0
1003 void CaretBoxLine::dump(QTextStream &ts, const QString &ind) const
1004 {
1005  ts << ind << "cbl: [email protected]" << basefb << endl;
1006  QString ind2 = ind + " ";
1007  for (size_t i = 0; i < caret_boxes.size(); i++) {
1008  if (i > 0) {
1009  ts << endl;
1010  }
1011  caret_boxes[i]->dump(ts, ind2);
1012  }
1013 }
1014 #endif
1015 
1016 // == caret mode related helper functions
1017 
1018 /** seeks the root line box that is the parent of the given inline box.
1019  * @param b given inline box
1020  * @param base base render object which not to step over. If \c base's
1021  * inline flow box is hit before the root line box, the flow box
1022  * is returned instead.
1023  * @return the root line box or the base flow box.
1024  */
1025 inline InlineFlowBox *seekBaseFlowBox(InlineBox *b, RenderObject *base = 0)
1026 {
1027  // Seek root line box or base inline flow box, if \c base is interfering.
1028  while (b->parent() && b->object() != base) {
1029  b = b->parent();
1030  }/*wend*/
1031  Q_ASSERT(b->isInlineFlowBox());
1032  return static_cast<InlineFlowBox *>(b);
1033 }
1034 
1035 /** determines whether the given element is a block level replaced element.
1036  */
1038 {
1039  return r->isRenderReplaced() && r->style()->display() == BLOCK;
1040 }
1041 
1042 /** determines the caret line box that contains the given position.
1043  *
1044  * If the node does not map to a render object, the function will snap to
1045  * the next suitable render object following it.
1046  *
1047  * @param node node to begin with
1048  * @param offset zero-based offset within node.
1049  * @param cblDeleter deleter for caret box lines
1050  * @param base base render object which the caret must not be placed beyond.
1051  * @param r_ofs adjusted offset within render object
1052  * @param caretBoxIt returns an iterator to the caret box that contains the
1053  * given position.
1054  * @return the determined caret box lineor 0 if either the node is 0 or
1055  * there is no inline flow box containing this node. The containing block
1056  * will still be set. If it is 0 too, @p node was invalid.
1057  */
1058 static CaretBoxLine *findCaretBoxLine(DOM::NodeImpl *node, long offset,
1059  CaretBoxLineDeleter *cblDeleter, RenderObject *base,
1060  long &r_ofs, CaretBoxIterator &caretBoxIt)
1061 {
1062  bool outside, outsideEnd;
1063  RenderObject *r = findRenderer(node, offset, base, r_ofs, outside, outsideEnd);
1064  if (!r) {
1065  return 0;
1066  }
1067 #if DEBUG_CARETMODE > 0
1068  // qCDebug(KHTML_LOG) << "=================== findCaretBoxLine";
1069  // qCDebug(KHTML_LOG) << "node " << node << " offset: " << offset << " r " << r->renderName() << "[" << r << "].node " << r->element()->nodeName().string() << "[" << r->element() << "]" << " r_ofs " << r_ofs << " outside " << outside << " outsideEnd " << outsideEnd;
1070 #endif
1071 
1072  // There are two strategies to find the correct line box. (The third is failsafe)
1073  // (A) First, if node's renderer is a RenderText, we only traverse its text
1074  // runs and return the root line box (saves much time for long blocks).
1075  // This should be the case 99% of the time.
1076  // (B) Second, we derive the inline flow box directly when the renderer is
1077  // a RenderBlock, RenderInline, or blocked RenderReplaced.
1078  // (C) Otherwise, we iterate linearly through all line boxes in order to find
1079  // the renderer.
1080 
1081  // (A)
1082  if (r->isText()) do {
1083  RenderText *t = static_cast<RenderText *>(r);
1084  int dummy;
1085  InlineBox *b = t->findInlineTextBox(offset, dummy, true);
1086  // Actually b should never be 0, but some render texts don't have text
1087  // boxes, so we insert the last run as an error correction.
1088  // If there is no last run, we resort to (B)
1089  if (!b) {
1090  if (!t->lastTextBox()) {
1091  break;
1092  }
1093  b = t->lastTextBox();
1094  }/*end if*/
1095  Q_ASSERT(b);
1096  outside = false; // text boxes cannot have outside positions
1097  InlineFlowBox *baseFlowBox = seekBaseFlowBox(b, base);
1098 #if DEBUG_CARETMODE > 2
1099  // qCDebug(KHTML_LOG) << "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());
1100 #endif
1101 #if 0
1102  if (t->containingBlock()->isListItem()) {
1103  dumpLineBoxes(static_cast<RenderFlow *>(t->containingBlock()));
1104  }
1105 #endif
1106 #if DEBUG_CARETMODE > 0
1107  // qCDebug(KHTML_LOG) << "=================== end findCaretBoxLine (renderText)";
1108 #endif
1109  return CaretBoxLine::constructCaretBoxLine(cblDeleter, baseFlowBox,
1110  b, outside, outsideEnd, caretBoxIt);
1111  } while (false); /*end if*/
1112 
1113  // (B)
1114  bool isrepl = isBlockRenderReplaced(r);
1115  if (r->isRenderBlock() || r->isRenderInline() || isrepl) {
1116  RenderFlow *flow = static_cast<RenderFlow *>(r);
1117  InlineFlowBox *firstLineBox = isrepl ? 0 : flow->firstLineBox();
1118 
1119  // On render blocks, if we are outside, or have a totally empty render
1120  // block, we simply construct a special caret box line.
1121  // The latter case happens only when the render block is a leaf object itself.
1122  if (isrepl || r->isRenderBlock() && (outside || !firstLineBox)
1123  || r->isRenderInline() && !firstLineBox) {
1124 #if DEBUG_CARETMODE > 0
1125  // qCDebug(KHTML_LOG) << "=================== end findCaretBoxLine (box " << (outside ? (outsideEnd ? "outside end" : "outside begin") : "inside") << ")";
1126 #endif
1127  Q_ASSERT(r->isBox());
1128  return CaretBoxLine::constructCaretBoxLine(cblDeleter,
1129  static_cast<RenderBox *>(r), outside, outsideEnd, caretBoxIt);
1130  }/*end if*/
1131 
1132  // qCDebug(KHTML_LOG) << "firstlinebox " << firstLineBox;
1133  InlineFlowBox *baseFlowBox = seekBaseFlowBox(firstLineBox, base);
1134  return CaretBoxLine::constructCaretBoxLine(cblDeleter, baseFlowBox,
1135  firstLineBox, outside, outsideEnd, caretBoxIt);
1136  }/*end if*/
1137 
1138  RenderBlock *cb = r->containingBlock();
1139  //if ( !cb ) return 0L;
1140  Q_ASSERT(cb);
1141 
1142  // ### which element doesn't have a block as its containing block?
1143  // Is it still possible after the RenderBlock/RenderInline merge?
1144  if (!cb->isRenderBlock()) {
1145  qCWarning(KHTML_LOG) << "containing block is no render block!!! crash imminent";
1146  }/*end if*/
1147 
1148  InlineFlowBox *flowBox = cb->firstLineBox();
1149  // (C)
1150  // This case strikes when the element is replaced, but neither a
1151  // RenderBlock nor a RenderInline
1152  if (!flowBox) { // ### utter emergency (why is this possible at all?)
1153 // flowBox = generateDummyFlowBox(arena, cb, r);
1154 // if (ibox) *ibox = flowBox->firstChild();
1155 // outside = outside_end = true;
1156 
1157 // qCWarning(KHTML_LOG) << "containing block contains no inline flow boxes!!! crash imminent";
1158 #if DEBUG_CARETMODE > 0
1159  // qCDebug(KHTML_LOG) << "=================== end findCaretBoxLine (2)";
1160 #endif
1161  return CaretBoxLine::constructCaretBoxLine(cblDeleter, cb,
1162  outside, outsideEnd, caretBoxIt);
1163  }/*end if*/
1164 
1165  // We iterate the inline flow boxes of the containing block until
1166  // we find the given node. This has one major flaw: it is linear, and therefore
1167  // painfully slow for really large blocks.
1168  for (; flowBox; flowBox = static_cast<InlineFlowBox *>(flowBox->nextLineBox())) {
1169 #if DEBUG_CARETMODE > 0
1170  // qCDebug(KHTML_LOG) << "[scan line]";
1171 #endif
1172 
1173  // construct a caret line box and stop when the element is contained within
1174  InlineFlowBox *baseFlowBox = seekBaseFlowBox(flowBox, base);
1175  CaretBoxLine *cbl = CaretBoxLine::constructCaretBoxLine(cblDeleter,
1176  baseFlowBox, 0, outside, outsideEnd, caretBoxIt, r);
1177 #if DEBUG_CARETMODE > 5
1178  // qCDebug(KHTML_LOG) << cbl->information();
1179 #endif
1180  if (caretBoxIt != cbl->end()) {
1181 #if DEBUG_CARETMODE > 0
1182  // qCDebug(KHTML_LOG) << "=================== end findCaretBoxLine (3)";
1183 #endif
1184  return cbl;
1185  }
1186  }/*next flowBox*/
1187 
1188  // no inline flow box found, approximate to nearest following node.
1189  // Danger: this is O(n^2). It's only called to recover from
1190  // errors, that means, theoretically, never. (Practically, far too often :-( )
1191  Q_ASSERT(!flowBox);
1192  CaretBoxLine *cbl = findCaretBoxLine(nextLeafNode(node, base ? base->element() : 0), 0, cblDeleter, base, r_ofs, caretBoxIt);
1193 #if DEBUG_CARETMODE > 0
1194  // qCDebug(KHTML_LOG) << "=================== end findCaretBoxLine";
1195 #endif
1196  return cbl;
1197 }
1198 
1199 /** finds the innermost table object @p r is contained within, but no
1200  * farther than @p cb.
1201  * @param r leaf element to begin with
1202  * @param cb bottom element where to stop search at least.
1203  * @return the table object or 0 if none found.
1204  */
1205 static inline RenderTable *findTableUpTo(RenderObject *r, RenderFlow *cb)
1206 {
1207  while (r && r != cb && !r->isTable()) {
1208  r = r->parent();
1209  }
1210  return r && r->isTable() ? static_cast<RenderTable *>(r) : 0;
1211 }
1212 
1213 /** checks whether @p r is a descendant of @p cb, or r == cb
1214  */
1215 static inline bool isDescendant(RenderObject *r, RenderObject *cb)
1216 {
1217  while (r && r != cb) {
1218  r = r->parent();
1219  }
1220  return r;
1221 }
1222 
1223 /** checks whether the given block contains at least one editable element.
1224  *
1225  * Warning: This function has linear complexity, and therefore is expensive.
1226  * Use it sparingly, and cache the result.
1227  * @param part part
1228  * @param cb block to be searched
1229  * @param table returns the nested table if there is one directly at the beginning
1230  * or at the end.
1231  * @param fromEnd begin search from end (default: begin from beginning)
1232  */
1233 static bool containsEditableElement(KHTMLPart *part, RenderBlock *cb,
1234  RenderTable *&table, bool fromEnd = false)
1235 {
1236  RenderObject *r = cb;
1237  if (fromEnd)
1238  while (r->lastChild()) {
1239  r = r->lastChild();
1240  }
1241  else
1242  while (r->firstChild()) {
1243  r = r->firstChild();
1244  }
1245 
1246  RenderTable *tempTable = 0;
1247  table = 0;
1248  bool withinCb;
1249 // int state; // not used
1250  ObjectTraversalState trav = InsideDescending;
1251  do {
1252  bool modWithinCb = withinCb = isDescendant(r, cb);
1253 
1254  // treat cb extra, it would not be considered otherwise
1255  if (!modWithinCb) {
1256  modWithinCb = true;
1257  r = cb;
1258  } else {
1259  tempTable = findTableUpTo(r, cb);
1260  }
1261 
1262 #if DEBUG_CARETMODE > 1
1263  // qCDebug(KHTML_LOG) << "cee: r " << (r ? r->renderName() : QString()) << "@" << r << " cb " << cb << " withinCb " << withinCb << " modWithinCb " << modWithinCb << " tempTable " << tempTable;
1264 #endif
1265  if (r && modWithinCb && r->element() && !isUnsuitable(r, trav)
1266  && (part->isCaretMode() || part->isEditable()
1267  || r->style()->userInput() == UI_ENABLED)) {
1268  table = tempTable;
1269 #if DEBUG_CARETMODE > 1
1270  // qCDebug(KHTML_LOG) << "cee: editable";
1271 #endif
1272  return true;
1273  }/*end if*/
1274 
1275 // RenderObject *oldr = r;
1276 // while (r && r == oldr)
1277 // r = advanceSuitableObject(r, trav, fromEnd, cb->parent(), state);
1278  r = fromEnd ? r->objectAbove() : r->objectBelow();
1279  } while (r && withinCb);
1280  return false;
1281 }
1282 
1283 /** checks whether the given block contains at least one editable child
1284  * element, beginning with but excluding @p start.
1285  *
1286  * Warning: This function has linear complexity, and therefore is expensive.
1287  * Use it sparingly, and cache the result.
1288  * @param part part
1289  * @param cb block to be searched
1290  * @param table returns the nested table if there is one directly before/after
1291  * the start object.
1292  * @param fromEnd begin search from end (default: begin from beginning)
1293  * @param start object after which to begin search.
1294  */
1295 static bool containsEditableChildElement(KHTMLPart *part, RenderBlock *cb,
1296  RenderTable *&table, bool fromEnd, RenderObject *start)
1297 {
1298  int state = 0;
1299  ObjectTraversalState trav = OutsideAscending;
1300 // qCDebug(KHTML_LOG) << "start: " << start;
1301  RenderObject *r = start;
1302  do {
1303  r = traverseRenderObjects(r, trav, fromEnd, cb->parent(), state);
1304  } while (r && !(state & AdvancedToSibling));
1305 // qCDebug(KHTML_LOG) << "r: " << r;
1306  //advanceObject(start, trav, fromEnd, cb->parent(), state);
1307 // RenderObject *oldr = r;
1308 // while (r && r == oldr)
1309  if (!r) {
1310  return false;
1311  }
1312 
1313  if (fromEnd)
1314  while (r->firstChild()) {
1315  r = r->firstChild();
1316  }
1317  else
1318  while (r->lastChild()) {
1319  r = r->lastChild();
1320  }
1321 // qCDebug(KHTML_LOG) << "child r: " << r;
1322  if (!r) {
1323  return false;
1324  }
1325 
1326  RenderTable *tempTable = 0;
1327  table = 0;
1328  bool withinCb = false;
1329  do {
1330 
1331  bool modWithinCb = withinCb = isDescendant(r, cb);
1332 
1333  // treat cb extra, it would not be considered otherwise
1334  if (!modWithinCb) {
1335  modWithinCb = true;
1336  r = cb;
1337  } else {
1338  tempTable = findTableUpTo(r, cb);
1339  }
1340 
1341 #if DEBUG_CARETMODE > 1
1342  // qCDebug(KHTML_LOG) << "cece: r " << (r ? r->renderName() : QString()) << "@" << r << " cb " << cb << " withinCb " << withinCb << " modWithinCb " << modWithinCb << " tempTable " << tempTable;
1343 #endif
1344  if (r && withinCb && r->element() && !isUnsuitable(r, trav)
1345  && (part->isCaretMode() || part->isEditable()
1346  || r->style()->userInput() == UI_ENABLED)) {
1347  table = tempTable;
1348 #if DEBUG_CARETMODE > 1
1349  // qCDebug(KHTML_LOG) << "cece: editable";
1350 #endif
1351  return true;
1352  }/*end if*/
1353 
1354  r = fromEnd ? r->objectAbove() : r->objectBelow();
1355  } while (withinCb);
1356  return false;
1357 }
1358 
1359 // == class LinearDocument implementation
1360 
1361 LinearDocument::LinearDocument(KHTMLPart *part, NodeImpl *node, long offset,
1362  CaretAdvancePolicy advancePolicy, ElementImpl *baseElem)
1363  : node(node), offset(offset), m_part(part),
1364  advPol(advancePolicy), base(0)
1365 {
1366  if (node == 0) {
1367  return;
1368  }
1369 
1370  if (baseElem) {
1371  RenderObject *b = baseElem->renderer();
1372  if (b && (b->isRenderBlock() || b->isRenderInline())) {
1373  base = b;
1374  }
1375  }
1376 
1377  initPreBeginIterator();
1378  initEndIterator();
1379 }
1380 
1381 LinearDocument::~LinearDocument()
1382 {
1383 }
1384 
1385 int LinearDocument::count() const
1386 {
1387  // FIXME: not implemented
1388  return 1;
1389 }
1390 
1391 LinearDocument::Iterator LinearDocument::current()
1392 {
1393  return LineIterator(this, node, offset);
1394 }
1395 
1396 LinearDocument::Iterator LinearDocument::begin()
1397 {
1398  NodeImpl *n = base ? base->element() : 0;
1399  if (!base) {
1400  n = node ? node->getDocument() : 0;
1401  }
1402  if (!n) {
1403  return end();
1404  }
1405 
1406  n = n->firstChild();
1407  if (advPol == LeafsOnly)
1408  while (n->firstChild()) {
1409  n = n->firstChild();
1410  }
1411 
1412  if (!n) {
1413  return end(); // must be empty document or empty base element
1414  }
1415  return LineIterator(this, n, n->minOffset());
1416 }
1417 
1418 LinearDocument::Iterator LinearDocument::preEnd()
1419 {
1420  NodeImpl *n = base ? base->element() : 0;
1421  if (!base) {
1422  n = node ? node->getDocument() : 0;
1423  }
1424  if (!n) {
1425  return preBegin();
1426  }
1427 
1428  n = n->lastChild();
1429  if (advPol == LeafsOnly)
1430  while (n->lastChild()) {
1431  n = n->lastChild();
1432  }
1433 
1434  if (!n) {
1435  return preBegin(); // must be empty document or empty base element
1436  }
1437  return LineIterator(this, n, n->maxOffset());
1438 }
1439 
1440 void LinearDocument::initPreBeginIterator()
1441 {
1442  _preBegin = LineIterator(this, 0, 0);
1443 }
1444 
1445 void LinearDocument::initEndIterator()
1446 {
1447  _end = LineIterator(this, 0, 1);
1448 }
1449 
1450 // == class LineIterator implementation
1451 
1452 CaretBoxIterator LineIterator::currentBox /*KHTML_NO_EXPORT*/;
1453 long LineIterator::currentOffset /*KHTML_NO_EXPORT*/;
1454 
1455 LineIterator::LineIterator(LinearDocument *l, DOM::NodeImpl *node, long offset)
1456  : lines(l)
1457 {
1458 // qCDebug(KHTML_LOG) << "LineIterator: node " << node << " offset " << offset;
1459  if (!node) {
1460  cbl = 0;
1461  return;
1462  }
1463  cbl = findCaretBoxLine(node, offset, &lines->cblDeleter,
1464  l->baseObject(), currentOffset, currentBox);
1465  // can happen on partially loaded documents
1466 #if DEBUG_CARETMODE > 0
1467  if (!cbl) // qCDebug(KHTML_LOG) << "no render object found!";
1468 #endif
1469  if (!cbl) {
1470  return;
1471  }
1472 #if DEBUG_CARETMODE > 1
1473  // qCDebug(KHTML_LOG) << "LineIterator: offset " << offset << " outside " << cbl->isOutside();
1474 #endif
1475 #if DEBUG_CARETMODE > 3
1476  // qCDebug(KHTML_LOG) << cbl->information();
1477 #endif
1478  if (currentBox == cbl->end()) {
1479 #if DEBUG_CARETMODE > 0
1480  // qCDebug(KHTML_LOG) << "LineIterator: findCaretBoxLine failed";
1481 #endif
1482  cbl = 0;
1483  }/*end if*/
1484 }
1485 
1486 void LineIterator::nextBlock()
1487 {
1488  RenderObject *base = lines->baseObject();
1489 
1490  bool cb_outside = cbl->isOutside();
1491  bool cb_outside_end = cbl->isOutsideEnd();
1492 
1493  {
1494  RenderObject *r = cbl->enclosingObject();
1495 
1496  ObjectTraversalState trav;
1497  int state; // not used
1498  mapRenderPosToTraversalState(cb_outside, cb_outside_end, false, trav);
1499 #if DEBUG_CARETMODE > 1
1500  // qCDebug(KHTML_LOG) << "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;
1501 #endif
1502  r = advanceSuitableObject(r, trav, false, base, state);
1503  if (!r) {
1504  cbl = 0;
1505  return;
1506  }/*end if*/
1507 
1508  mapTraversalStateToRenderPos(trav, false, cb_outside, cb_outside_end);
1509 #if DEBUG_CARETMODE > 1
1510  // qCDebug(KHTML_LOG) << "nextBlock: after r" << r << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end;
1511 #endif
1512 #if DEBUG_CARETMODE > 0
1513  // qCDebug(KHTML_LOG) << "++: r " << r << "[" << (r?r->renderName():QString()) << "]";
1514 #endif
1515 
1516  RenderBlock *cb;
1517 
1518  // If we hit a block or replaced object, use this as its enclosing object
1519  bool isrepl = isBlockRenderReplaced(r);
1520  if (r->isRenderBlock() || isrepl) {
1521  RenderBox *cb = static_cast<RenderBox *>(r);
1522 
1523  cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
1524  cb_outside, cb_outside_end, currentBox);
1525 
1526 #if DEBUG_CARETMODE > 0
1527  // qCDebug(KHTML_LOG) << "r->isFlow is cb. continuation @" << cb->continuation();
1528 #endif
1529  return;
1530  } else {
1531  cb = r->containingBlock();
1532  Q_ASSERT(cb->isRenderBlock());
1533  }/*end if*/
1534  InlineFlowBox *flowBox = cb->firstLineBox();
1535 #if DEBUG_CARETMODE > 0
1536  // qCDebug(KHTML_LOG) << "++: flowBox " << flowBox << " cb " << cb << '[' << (cb?cb->renderName()+QString(".node ")+QString::number((unsigned)cb->element(),16)+(cb->element()?'@'+cb->element()->nodeName().string():QString()):QString()) << ']';
1537 #endif
1538  Q_ASSERT(flowBox);
1539  if (!flowBox) { // ### utter emergency (why is this possible at all?)
1540  cb_outside = cb_outside_end = true;
1541  cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
1542  cb_outside, cb_outside_end, currentBox);
1543  return;
1544  }
1545 
1546  bool seekOutside = false, seekOutsideEnd = false; // silence gcc uninit warning
1547  CaretBoxIterator it;
1548  cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
1549  flowBox, flowBox->firstChild(), seekOutside, seekOutsideEnd, it);
1550  }
1551 }
1552 
1553 void LineIterator::prevBlock()
1554 {
1555  RenderObject *base = lines->baseObject();
1556 
1557  bool cb_outside = cbl->isOutside();
1558  bool cb_outside_end = cbl->isOutsideEnd();
1559 
1560  {
1561  RenderObject *r = cbl->enclosingObject();
1562  if (r->isAnonymous() && !cb_outside) {
1563  cb_outside = true, cb_outside_end = false;
1564  }
1565 
1566  ObjectTraversalState trav;
1567  int state; // not used
1568  mapRenderPosToTraversalState(cb_outside, cb_outside_end, true, trav);
1569 #if DEBUG_CARETMODE > 1
1570  // qCDebug(KHTML_LOG) << "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;
1571 #endif
1572  r = advanceSuitableObject(r, trav, true, base, state);
1573  if (!r) {
1574  cbl = 0;
1575  return;
1576  }/*end if*/
1577 
1578  mapTraversalStateToRenderPos(trav, true, cb_outside, cb_outside_end);
1579 #if DEBUG_CARETMODE > 1
1580  // qCDebug(KHTML_LOG) << "prevBlock: after r" << r << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end;
1581 #endif
1582 #if DEBUG_CARETMODE > 0
1583  // qCDebug(KHTML_LOG) << "--: r " << r << "[" << (r?r->renderName():QString()) << "]";
1584 #endif
1585 
1586  RenderBlock *cb;
1587 
1588  // If we hit a block, use this as its enclosing object
1589  bool isrepl = isBlockRenderReplaced(r);
1590 // qCDebug(KHTML_LOG) << "isrepl " << isrepl << " isblock " << r->isRenderBlock();
1591  if (r->isRenderBlock() || isrepl) {
1592  RenderBox *cb = static_cast<RenderBox *>(r);
1593 
1594  cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
1595  cb_outside, cb_outside_end, currentBox);
1596 
1597 #if DEBUG_CARETMODE > 0
1598  // qCDebug(KHTML_LOG) << "r->isFlow is cb. continuation @" << cb->continuation();
1599 #endif
1600  return;
1601  } else {
1602  cb = r->containingBlock();
1603  Q_ASSERT(cb->isRenderBlock());
1604  }/*end if*/
1605  InlineFlowBox *flowBox = cb->lastLineBox();
1606 #if DEBUG_CARETMODE > 0
1607  // qCDebug(KHTML_LOG) << "--: flowBox " << flowBox << " cb " << cb << "[" << (cb?cb->renderName()+QString(".node ")+QString::number((unsigned)cb->element(),16)+(cb->element()?"@"+cb->element()->nodeName().string():QString()):QString()) << "]";
1608 #endif
1609  Q_ASSERT(flowBox);
1610  if (!flowBox) { // ### utter emergency (why is this possible at all?)
1611  cb_outside = true; cb_outside_end = false;
1612  cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
1613  cb_outside, cb_outside_end, currentBox);
1614  return;
1615  }
1616 
1617  bool seekOutside = false, seekOutsideEnd = false; // silence gcc uninit warning
1618  CaretBoxIterator it;
1619  cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
1620  flowBox, flowBox->firstChild(), seekOutside, seekOutsideEnd, it);
1621  }
1622 }
1623 
1624 void LineIterator::advance(bool toBegin)
1625 {
1626  InlineFlowBox *flowBox = cbl->baseFlowBox();
1627  if (flowBox) {
1628  flowBox = static_cast<InlineFlowBox *>(toBegin ? flowBox->prevLineBox() : flowBox->nextLineBox());
1629  if (flowBox) {
1630  bool seekOutside = false, seekOutsideEnd = false; // silence gcc uninit warning
1631  CaretBoxIterator it;
1632  cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
1633  flowBox, flowBox->firstChild(), seekOutside, seekOutsideEnd, it);
1634  }/*end if*/
1635  }/*end if*/
1636 
1637  // if there are no more lines in this block, move towards block to come
1638  if (!flowBox) {
1639  if (toBegin) {
1640  prevBlock();
1641  } else {
1642  nextBlock();
1643  }
1644  }
1645 
1646 #if DEBUG_CARETMODE > 3
1647  if (cbl) // qCDebug(KHTML_LOG) << cbl->information();
1648 #endif
1649  }
1650 
1651 // == class EditableCaretBoxIterator implementation
1652 
1653 void EditableCaretBoxIterator::advance(bool toBegin)
1654 {
1655 #if DEBUG_CARETMODE > 3
1656  // qCDebug(KHTML_LOG) << "---------------" << "toBegin " << toBegin;
1657 #endif
1658  const CaretBoxIterator preBegin = cbl->preBegin();
1659  const CaretBoxIterator end = cbl->end();
1660 
1661  CaretBoxIterator lastbox = *this, curbox;
1662  bool islastuseable = true; // silence gcc
1663  bool iscuruseable;
1664  // Assume adjacency of caret boxes. Will be falsified later if applicable.
1665  adjacent = true;
1666 
1667 #if DEBUG_CARETMODE > 4
1668 // qCDebug(KHTML_LOG) << "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();
1669 #endif
1670 
1671  if (toBegin) {
1672  CaretBoxIterator::operator --();
1673  } else {
1674  CaretBoxIterator::operator ++();
1675  }
1676  bool curAtEnd = *this == preBegin || *this == end;
1677  curbox = *this;
1678  bool atEnd = true;
1679  if (!curAtEnd) {
1680  iscuruseable = isEditable(curbox, toBegin);
1681  if (toBegin) {
1682  CaretBoxIterator::operator --();
1683  } else {
1684  CaretBoxIterator::operator ++();
1685  }
1686  atEnd = *this == preBegin || *this == end;
1687  }
1688  while (!curAtEnd) {
1689  bool haslast = lastbox != end && lastbox != preBegin;
1690  bool hascoming = !atEnd;
1691  bool iscominguseable = true; // silence gcc
1692 
1693  if (!atEnd) {
1694  iscominguseable = isEditable(*this, toBegin);
1695  }
1696  if (iscuruseable) {
1697 #if DEBUG_CARETMODE > 3
1698  // qCDebug(KHTML_LOG) << "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();
1699 #endif
1700 
1701  CaretBox *box = *curbox;
1702  if (box->isOutside()) {
1703  // if this caret box represents no inline box, it is an outside box
1704  // which has to be considered unconditionally
1705  if (!box->isInline()) {
1706  break;
1707  }
1708 
1709  if (advpol == VisibleFlows) {
1710  break;
1711  }
1712 
1713  // IndicatedFlows and LeafsOnly are treated equally in caret box lines
1714 
1715  InlineBox *ibox = box->inlineBox();
1716  // get previous inline box
1717  InlineBox *prev = box->isOutsideEnd() ? ibox : ibox->prevOnLine();
1718  // get next inline box
1719  InlineBox *next = box->isOutsideEnd() ? ibox->nextOnLine() : ibox;
1720 
1721  const bool isprevindicated = !prev || isIndicatedInlineBox(prev);
1722  const bool isnextindicated = !next || isIndicatedInlineBox(next);
1723  const bool last = haslast && !islastuseable;
1724  const bool coming = hascoming && !iscominguseable;
1725  const bool left = !prev || prev->isInlineFlowBox() && isprevindicated
1726  || (toBegin && coming || !toBegin && last);
1727  const bool right = !next || next->isInlineFlowBox() && isnextindicated
1728  || (!toBegin && coming || toBegin && last);
1729  const bool text2indicated = toBegin && next && next->isInlineTextBox()
1730  && isprevindicated
1731  || !toBegin && prev && prev->isInlineTextBox() && isnextindicated;
1732  const bool indicated2text = !toBegin && next && next->isInlineTextBox()
1733  && prev && isprevindicated
1734  // ### this code is so broken.
1735  /*|| toBegin && prev && prev->isInlineTextBox() && isnextindicated*/;
1736 #if DEBUG_CARETMODE > 5
1737  // qCDebug(KHTML_LOG) << "prev " << prev << " haslast " << haslast << " islastuseable " << islastuseable << " left " << left << " next " << next << " hascoming " << hascoming << " iscominguseable " << iscominguseable << " right " << right << " text2indicated " << text2indicated << " indicated2text " << indicated2text;
1738 #endif
1739 
1740  if (left && right && !text2indicated || indicated2text) {
1741  adjacent = false;
1742 #if DEBUG_CARETMODE > 4
1743  // qCDebug(KHTML_LOG) << "left && right && !text2indicated || indicated2text";
1744 #endif
1745  break;
1746  }
1747 
1748  } else {
1749  // inside boxes are *always* valid
1750 #if DEBUG_CARETMODE > 4
1751  if (box->isInline()) {
1752  InlineBox *ibox = box->inlineBox();
1753  // qCDebug(KHTML_LOG) << "inside " << (!ibox->isInlineFlowBox() || static_cast<InlineFlowBox *>(ibox)->firstChild() ? "non-empty" : "empty") << (isIndicatedInlineBox(ibox) ? " indicated" : "") << " adjacent=" << adjacent;
1754  }
1755 #if 0
1756  RenderStyle *s = ibox->object()->style();
1757  // qCDebug(KHTML_LOG) << "bordls " << s->borderLeftStyle()
1758  << " bordl " << (s->borderLeftStyle() != BNONE)
1759  << " bordr " << (s->borderRightStyle() != BNONE)
1760  << " bordt " << (s->borderTopStyle() != BNONE)
1761  << " bordb " << (s->borderBottomStyle() != BNONE)
1762  << " padl " << s->paddingLeft().value()
1763  << " padr " << s->paddingRight().value()
1764  << " padt " << s->paddingTop().value()
1765  << " padb " << s->paddingBottom().value()
1766  // ### Can inline elements have top/bottom margins? Couldn't find
1767  // it in the CSS 2 spec, but Mozilla ignores them, so we do, too.
1768  << " marl " << s->marginLeft().value()
1769  << " marr " << s->marginRight().value();
1770 #endif
1771 #endif
1772  break;
1773  }/*end if*/
1774 
1775  } else {
1776 
1777  if (!(*curbox)->isOutside()) {
1778  // cannot be adjacent anymore
1779  adjacent = false;
1780  }
1781 
1782  }/*end if*/
1783  lastbox = curbox;
1784  islastuseable = iscuruseable;
1785  curbox = *this;
1786  iscuruseable = iscominguseable;
1787  curAtEnd = atEnd;
1788  if (!atEnd) {
1789  if (toBegin) {
1790  CaretBoxIterator::operator --();
1791  } else {
1792  CaretBoxIterator::operator ++();
1793  }
1794  atEnd = *this == preBegin || *this == end;
1795  }/*end if*/
1796  }/*wend*/
1797 
1798  *static_cast<CaretBoxIterator *>(this) = curbox;
1799 #if DEBUG_CARETMODE > 4
1800 // qCDebug(KHTML_LOG) << "still valid? " << (*this != preBegin && *this != end);
1801 #endif
1802 #if DEBUG_CARETMODE > 3
1803  // qCDebug(KHTML_LOG) << "---------------" << "end ";
1804 #endif
1805 }
1806 
1807 bool EditableCaretBoxIterator::isEditable(const CaretBoxIterator &boxit, bool fromEnd)
1808 {
1809  Q_ASSERT(boxit != cbl->end() && boxit != cbl->preBegin());
1810  CaretBox *b = *boxit;
1811  RenderObject *r = b->object();
1812 #if DEBUG_CARETMODE > 0
1813 // if (b->isInlineFlowBox()) qCDebug(KHTML_LOG) << "b is inline flow box" << (outside ? " (outside)" : "");
1814  // qCDebug(KHTML_LOG) << "isEditable r" << r << ": " << (r ? r->renderName() : QString()) << (r && r->isText() ? " contains \"" + QString(((RenderText *)r)->str->s, qMin(((RenderText *)r)->str->l,15)) + "\"" : QString());
1815 #endif
1816  // Must check caret mode or design mode *after* r->element(), otherwise
1817  // lines without a backing DOM node get regarded, leading to a crash.
1818  // ### check should actually be in InlineBoxIterator
1819  NodeImpl *node = r->element();
1820  ObjectTraversalState trav;
1821  mapRenderPosToTraversalState(b->isOutside(), b->isOutsideEnd(), fromEnd, trav);
1822  if (isUnsuitable(r, trav) || !node) {
1823  return false;
1824  }
1825 
1826  // generally exclude replaced elements with no children from navigation
1827  if (!b->isOutside() && r->isRenderReplaced() && !r->firstChild()) {
1828  return false;
1829  }
1830 
1831  RenderObject *eff_r = r;
1832  bool globallyNavigable = m_part->isCaretMode() || m_part->isEditable();
1833 
1834  // calculate the parent element's editability if this inline box is outside.
1835  if (b->isOutside() && !globallyNavigable) {
1836  NodeImpl *par = node->parent();
1837  // I wonder whether par can be 0. It shouldn't be possible if the
1838  // algorithm contained no bugs.
1839  Q_ASSERT(par);
1840  if (par) {
1841  node = par;
1842  }
1843  eff_r = node->renderer();
1844  Q_ASSERT(eff_r); // this is a hard requirement
1845  }
1846 
1847  bool result = globallyNavigable || eff_r->style()->userInput() == UI_ENABLED;
1848 #if DEBUG_CARETMODE > 0
1849  // qCDebug(KHTML_LOG) << result;
1850 #endif
1851  return result;
1852 }
1853 
1854 // == class EditableLineIterator implementation
1855 
1856 void EditableLineIterator::advance(bool toBegin)
1857 {
1858  CaretAdvancePolicy advpol = lines->advancePolicy();
1859  LineIterator lasteditable, lastindicated;
1860  bool haslasteditable = false;
1861  bool haslastindicated = false;
1862  bool uselasteditable = false;
1863 
1864  LineIterator::advance(toBegin);
1865  while (cbl) {
1866  if (isEditable(*this)) {
1867 #if DEBUG_CARETMODE > 3
1868  // qCDebug(KHTML_LOG) << "advance: " << cbl->enclosingObject() << "@" << cbl->enclosingObject()->renderName() << ".node " << cbl->enclosingObject()->element() << "[" << (cbl->enclosingObject()->element() ? cbl->enclosingObject()->element()->nodeName().string() : QString()) << "]";
1869 #endif
1870 
1871  bool hasindicated = isIndicatedFlow(cbl->enclosingObject());
1872  if (hasindicated) {
1873  haslastindicated = true;
1874  lastindicated = *this;
1875  }
1876 
1877  switch (advpol) {
1878  case IndicatedFlows:
1879  if (hasindicated) {
1880  goto wend;
1881  }
1882  // fall through
1883  case LeafsOnly:
1884  if (cbl->isOutside()) {
1885  break;
1886  }
1887  // fall through
1888  case VisibleFlows: goto wend;
1889  }/*end switch*/
1890 
1891  // remember rejected editable element
1892  lasteditable = *this;
1893  haslasteditable = true;
1894 #if DEBUG_CARETMODE > 4
1895  // qCDebug(KHTML_LOG) << "remembered lasteditable " << *lasteditable;
1896 #endif
1897  } else {
1898 
1899  // If this element isn't editable, but the last one was, and it was only
1900  // rejected because it didn't match the caret advance policy, force it.
1901  // Otherwise certain combinations of editable and uneditable elements
1902  // could never be reached with some policies.
1903  if (haslasteditable) {
1904  uselasteditable = true;
1905  break;
1906  }
1907 
1908  }
1909  LineIterator::advance(toBegin);
1910  }/*wend*/
1911 wend:
1912 
1913  if (uselasteditable) {
1914  *this = haslastindicated ? lastindicated : lasteditable;
1915  }
1916  if (!cbl && haslastindicated) {
1917  *this = lastindicated;
1918  }
1919 }
1920 
1921 // == class EditableCharacterIterator implementation
1922 
1923 void EditableCharacterIterator::initFirstChar()
1924 {
1925  CaretBox *box = *ebit;
1926  InlineBox *b = box->inlineBox();
1927  if (_offset == box->maxOffset()) {
1928  peekNext();
1929  } else if (b && !box->isOutside() && b->isInlineTextBox()) {
1930  _char = static_cast<RenderText *>(b->object())->str->s[_offset].unicode();
1931  } else {
1932  _char = -1;
1933  }
1934 }
1935 
1936 /** returns true when the given caret box is empty, i. e. should not
1937  * take place in caret movement.
1938  */
1939 static inline bool isCaretBoxEmpty(CaretBox *box)
1940 {
1941  if (!box->isInline()) {
1942  return false;
1943  }
1944  InlineBox *ibox = box->inlineBox();
1945  return ibox->isInlineFlowBox()
1946  && !static_cast<InlineFlowBox *>(ibox)->firstChild()
1947  && !isIndicatedInlineBox(ibox);
1948 }
1949 
1950 EditableCharacterIterator &EditableCharacterIterator::operator ++()
1951 {
1952  _offset++;
1953 
1954  CaretBox *box = *ebit;
1955  InlineBox *b = box->inlineBox();
1956  long maxofs = box->maxOffset();
1957 #if DEBUG_CARETMODE > 0
1958  // qCDebug(KHTML_LOG) << "box->maxOffset() " << box->maxOffset() << " box->minOffset() " << box->minOffset();
1959 #endif
1960  if (_offset == maxofs) {
1961 #if DEBUG_CARETMODE > 2
1962 // qCDebug(KHTML_LOG) << "_offset == maxofs: " << _offset << " == " << maxofs;
1963 #endif
1964  peekNext();
1965  } else if (_offset > maxofs) {
1966 #if DEBUG_CARETMODE > 2
1967 // qCDebug(KHTML_LOG) << "_offset > maxofs: " << _offset << " > " << maxofs /*<< " _peekNext: " << _peekNext*/;
1968 #endif
1969  if (/*!_peekNext*/true) {
1970  ++ebit;
1971  if (ebit == (*_it)->end()) { // end of line reached, go to next line
1972  ++_it;
1973 #if DEBUG_CARETMODE > 3
1974 // qCDebug(KHTML_LOG) << "++_it";
1975 #endif
1976  if (_it != _it.lines->end()) {
1977  ebit = _it;
1978  box = *ebit;
1979  b = box->inlineBox();
1980 #if DEBUG_CARETMODE > 3
1981 // qCDebug(KHTML_LOG) << "box " << box << " b " << b << " isText " << box->isInlineTextBox();
1982 #endif
1983 
1984 #if DEBUG_CARETMODE > 3
1985  RenderObject *_r = box->object();
1986 // qCDebug(KHTML_LOG) << "_r " << _r << ":" << _r->element()->nodeName().string();
1987 #endif
1988  _offset = box->minOffset();
1989 #if DEBUG_CARETMODE > 3
1990 // qCDebug(KHTML_LOG) << "_offset " << _offset;
1991 #endif
1992  } else {
1993  b = 0;
1994  _end = true;
1995  }/*end if*/
1996  goto readchar;
1997  }/*end if*/
1998  }/*end if*/
1999 
2000  bool adjacent = ebit.isAdjacent();
2001 #if 0
2002  // Jump over element if this one is not a text node.
2003  if (adjacent && !(*ebit)->isInlineTextBox()) {
2004  EditableCaretBoxIterator copy = ebit;
2005  ++ebit;
2006  if (ebit != (*_it)->end() && (*ebit)->isInlineTextBox()
2007  /*&& (!(*ebit)->isInlineFlowBox()
2008  || static_cast<InlineFlowBox *>(*ebit)->)*/) {
2009  adjacent = false;
2010  } else {
2011  ebit = copy;
2012  }
2013  }/*end if*/
2014 #endif
2015  // Jump over empty elements.
2016  if (adjacent && !(*ebit)->isInlineTextBox()) {
2017  bool noemptybox = true;
2018  while (isCaretBoxEmpty(*ebit)) {
2019  noemptybox = false;
2020  EditableCaretBoxIterator copy = ebit;
2021  ++ebit;
2022  if (ebit == (*_it)->end()) {
2023  ebit = copy;
2024  break;
2025  }
2026  }
2027  if (noemptybox) {
2028  adjacent = false;
2029  }
2030  }/*end if*/
2031 // _r = (*ebit)->object();
2032  /*if (!_it.outside) */_offset = (*ebit)->minOffset() + adjacent;
2033  //_peekNext = 0;
2034  box = *ebit;
2035  b = box->inlineBox();
2036  goto readchar;
2037  } else {
2038  readchar:
2039  // get character
2040  if (b && !box->isOutside() && b->isInlineTextBox() && _offset < b->maxOffset()) {
2041  _char = static_cast<RenderText *>(b->object())->str->s[_offset].unicode();
2042  } else {
2043  _char = -1;
2044  }
2045  }/*end if*/
2046 #if DEBUG_CARETMODE > 2
2047 // qCDebug(KHTML_LOG) << "_offset: " << _offset /*<< " _peekNext: " << _peekNext*/ << " char '" << (char)_char << "'";
2048 #endif
2049 
2050 #if DEBUG_CARETMODE > 0
2051  if (!_end && ebit != (*_it)->end()) {
2052  CaretBox *box = *ebit;
2053  RenderObject *_r = box->object();
2054  // qCDebug(KHTML_LOG) << "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>"));
2055  }
2056 #endif
2057  return *this;
2058 }
2059 
2060 EditableCharacterIterator &EditableCharacterIterator::operator --()
2061 {
2062  _offset--;
2063  //qCDebug(KHTML_LOG) << "--: _offset=" << _offset;
2064 
2065  CaretBox *box = *ebit;
2066  CaretBox *_peekPrev = 0;
2067  CaretBox *_peekNext = 0;
2068  InlineBox *b = box->inlineBox();
2069  long minofs = box->minOffset();
2070 #if DEBUG_CARETMODE > 0
2071  // qCDebug(KHTML_LOG) << "box->maxOffset() " << box->maxOffset() << " box->minOffset() " << box->minOffset();
2072 #endif
2073  if (_offset == minofs) {
2074 #if DEBUG_CARETMODE > 2
2075 // qCDebug(KHTML_LOG) << "_offset == minofs: " << _offset << " == " << minofs;
2076 #endif
2077 // _peekNext = b;
2078  // get character
2079  if (b && !box->isOutside() && b->isInlineTextBox()) {
2080  _char = static_cast<RenderText *>(b->object())->text()[_offset].unicode();
2081  } else {
2082  _char = -1;
2083  }
2084 
2085  //peekPrev();
2086  bool do_prev = false;
2087  {
2088  EditableCaretBoxIterator copy;
2089  _peekPrev = 0;
2090  do {
2091  copy = ebit;
2092  --ebit;
2093  if (ebit == (*_it)->preBegin()) {
2094  ebit = copy;
2095  break;
2096  }
2097  } while (isCaretBoxEmpty(*ebit));
2098  // Jump to end of previous element if it's adjacent, and a text box
2099  if (ebit.isAdjacent() && ebit != (*_it)->preBegin() && (*ebit)->isInlineTextBox()) {
2100  _peekPrev = *ebit;
2101  do_prev = true;
2102  } else {
2103  ebit = copy;
2104  }
2105  }
2106  if (do_prev) {
2107  goto prev;
2108  }
2109  } else if (_offset < minofs) {
2110  prev:
2111 #if DEBUG_CARETMODE > 2
2112 // qCDebug(KHTML_LOG) << "_offset < minofs: " << _offset << " < " << minofs /*<< " _peekNext: " << _peekNext*/;
2113 #endif
2114  if (!_peekPrev) {
2115  _peekNext = *ebit;
2116  --ebit;
2117  if (ebit == (*_it)->preBegin()) { // end of line reached, go to previous line
2118  --_it;
2119 #if DEBUG_CARETMODE > 3
2120 // qCDebug(KHTML_LOG) << "--_it";
2121 #endif
2122  if (_it != _it.lines->preBegin()) {
2123 // qCDebug(KHTML_LOG) << "begin from end!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
2124  ebit = EditableCaretBoxIterator(_it, true);
2125  box = *ebit;
2126 // RenderObject *r = box->object();
2127 #if DEBUG_CARETMODE > 3
2128 // qCDebug(KHTML_LOG) << "box " << box << " b " << box->inlineBox() << " isText " << box->isInlineTextBox();
2129 #endif
2130  _offset = box->maxOffset();
2131 // if (!_it.outside) _offset = r->isBR() ? (*ebit)->minOffset() : (*ebit)->maxOffset();
2132  _char = -1;
2133 #if DEBUG_CARETMODE > 0
2134  // qCDebug(KHTML_LOG) << "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());
2135 #endif
2136  } else {
2137  _end = true;
2138  }
2139  return *this;
2140  }/*end if*/
2141  }/*end if*/
2142 
2143 #if DEBUG_CARETMODE > 0
2144  bool adjacent = ebit.isAdjacent();
2145  // qCDebug(KHTML_LOG) << "adjacent " << adjacent << " _peekNext " << _peekNext << " _peekNext->isInlineTextBox: " << (_peekNext ? _peekNext->isInlineTextBox() : false) << " !((*ebit)->isInlineTextBox): " << (*ebit ? !(*ebit)->isInlineTextBox() : true);
2146 #endif
2147 #if 0
2148  // Ignore this box if it isn't a text box, but the previous box was
2149  if (adjacent && _peekNext && _peekNext->isInlineTextBox()
2150  && !(*ebit)->isInlineTextBox()) {
2151  EditableCaretBoxIterator copy = ebit;
2152  --ebit;
2153  if (ebit == (*_it)->preBegin()) /*adjacent = false;
2154  else */{
2155  ebit = copy;
2156  }
2157  }/*end if*/
2158 #endif
2159 #if 0
2160  // Jump over empty elements.
2161  if (adjacent //&& _peekNext && _peekNext->isInlineTextBox()
2162  && !(*ebit)->isInlineTextBox()) {
2163  bool noemptybox = true;
2164  while (isCaretBoxEmpty(*ebit)) {
2165  noemptybox = false;
2166  EditableCaretBoxIterator copy = ebit;
2167  --ebit;
2168  if (ebit == (*_it)->preBegin()) {
2169  ebit = copy;
2170  break;
2171  } else {
2172  _peekNext = *copy;
2173  }
2174  }
2175  if (noemptybox) {
2176  adjacent = false;
2177  }
2178  }/*end if*/
2179 #endif
2180 #if DEBUG_CARETMODE > 0
2181  // qCDebug(KHTML_LOG) << "(*ebit)->obj " << (*ebit)->object()->renderName() << "[" << (*ebit)->object() << "]" << " minOffset: " << (*ebit)->minOffset() << " maxOffset: " << (*ebit)->maxOffset();
2182 #endif
2183 #if DEBUG_CARETMODE > 3
2184  RenderObject *_r = (*ebit)->object();
2185 // qCDebug(KHTML_LOG) << "_r " << _r << ":" << _r->element()->nodeName().string();
2186 #endif
2187  _offset = (*ebit)->maxOffset();
2188 // if (!_it.outside) _offset = (*ebit)->maxOffset()/* - adjacent*/;
2189 #if DEBUG_CARETMODE > 3
2190 // qCDebug(KHTML_LOG) << "_offset " << _offset;
2191 #endif
2192  _peekPrev = 0;
2193  } else {
2194 #if DEBUG_CARETMODE > 0
2195 // qCDebug(KHTML_LOG) << "_offset: " << _offset << " _peekNext: " << _peekNext;
2196 #endif
2197  // get character
2198  if (_peekNext && _offset >= box->maxOffset() && _peekNext->isInlineTextBox()) {
2199  _char = static_cast<RenderText *>(_peekNext->object())->text()[_peekNext->minOffset()].unicode();
2200  } else if (b && _offset < b->maxOffset() && b->isInlineTextBox()) {
2201  _char = static_cast<RenderText *>(b->object())->text()[_offset].unicode();
2202  } else {
2203  _char = -1;
2204  }
2205  }/*end if*/
2206 
2207 #if DEBUG_CARETMODE > 0
2208  if (!_end && ebit != (*_it)->preBegin()) {
2209  CaretBox *box = *ebit;
2210  // qCDebug(KHTML_LOG) << "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());
2211  }
2212 #endif
2213  return *this;
2214 }
2215 
2216 // == class TableRowIterator implementation
2217 
2218 TableRowIterator::TableRowIterator(RenderTable *table, bool fromEnd,
2219  RenderTableSection::RowStruct *row)
2220  : sec(table, fromEnd)
2221 {
2222  // set index
2223  if (*sec) {
2224  if (fromEnd) {
2225  index = (*sec)->grid.size() - 1;
2226  } else {
2227  index = 0;
2228  }
2229  }/*end if*/
2230 
2231  // initialize with given row
2232  if (row && *sec) {
2233  while (operator *() != row)
2234  if (fromEnd) {
2235  operator --();
2236  } else {
2237  operator ++();
2238  }
2239  }/*end if*/
2240 }
2241 
2242 TableRowIterator &TableRowIterator::operator ++()
2243 {
2244  index++;
2245 
2246  if (index >= (int)(*sec)->grid.size()) {
2247  ++sec;
2248 
2249  if (*sec) {
2250  index = 0;
2251  }
2252  }/*end if*/
2253  return *this;
2254 }
2255 
2256 TableRowIterator &TableRowIterator::operator --()
2257 {
2258  index--;
2259 
2260  if (index < 0) {
2261  --sec;
2262 
2263  if (*sec) {
2264  index = (*sec)->grid.size() - 1;
2265  }
2266  }/*end if*/
2267  return *this;
2268 }
2269 
2270 // == class ErgonomicEditableLineIterator implementation
2271 
2272 // some decls
2273 static RenderTableCell *findNearestTableCellInRow(KHTMLPart *part, int x,
2274  RenderTableSection::RowStruct *row, bool fromEnd);
2275 
2276 /** finds the cell corresponding to absolute x-coordinate @p x in the given
2277  * table.
2278  *
2279  * If there is no direct cell, or the cell is not accessible, the function
2280  * will return the nearest suitable cell.
2281  * @param part part containing the document
2282  * @param x absolute x-coordinate
2283  * @param it table row iterator, will be adapted accordingly as more rows are
2284  * investigated.
2285  * @param fromEnd @p true to begin search from end and work towards the
2286  * beginning
2287  * @return the cell, or 0 if no editable cell was found.
2288  */
2289 static inline RenderTableCell *findNearestTableCell(KHTMLPart *part, int x,
2290  TableRowIterator &it, bool fromEnd)
2291 {
2292  RenderTableCell *result = 0;
2293 
2294  while (*it) {
2295  result = findNearestTableCellInRow(part, x, *it, fromEnd);
2296  if (result) {
2297  break;
2298  }
2299 
2300  if (fromEnd) {
2301  --it;
2302  } else {
2303  ++it;
2304  }
2305  }/*wend*/
2306 
2307  return result;
2308 }
2309 
2310 /** finds the nearest editable cell around the given absolute x-coordinate
2311  *
2312  * It will dive into nested tables as necessary to provide seamless navigation.
2313  *
2314  * If the cell at @p x is not editable, its left neighbor is tried, then its
2315  * right neighbor, then the left neighbor's left neighbor etc. If no
2316  * editable cell can be found, 0 is returned.
2317  * @param part khtml part
2318  * @param x absolute x-coordinate
2319  * @param row table row to be searched
2320  * @param fromEnd @p true, begin from end (applies only to nested tables)
2321  * @return the found cell or 0 if no editable cell was found
2322  */
2323 static RenderTableCell *findNearestTableCellInRow(KHTMLPart *part, int x,
2324  RenderTableSection::RowStruct *row, bool fromEnd)
2325 {
2326  // First pass. Find spatially nearest cell.
2327  int n = (int)row->row->size();
2328  int i;
2329  for (i = 0; i < n; i++) {
2330  RenderTableCell *cell = row->row->at(i);
2331  if (!cell || (long)cell == -1) {
2332  continue;
2333  }
2334 
2335  int absx, absy;
2336  cell->absolutePosition(absx, absy, false); // ### position: fixed?
2337 #if DEBUG_CARETMODE > 1
2338  // qCDebug(KHTML_LOG) << "i/n " << i << "/" << n << " absx " << absx << " absy " << absy;
2339 #endif
2340 
2341  // I rely on the assumption that all cells are in ascending visual order
2342  // ### maybe this assumption is wrong for bidi?
2343 #if DEBUG_CARETMODE > 1
2344  // qCDebug(KHTML_LOG) << "x " << x << " < " << (absx + cell->width()) << "?";
2345 #endif
2346  if (x < absx + cell->width()) {
2347  break;
2348  }
2349  }/*next i*/
2350  if (i >= n) {
2351  i = n - 1;
2352  }
2353 
2354  // Second pass. Find editable cell, beginning with the currently found,
2355  // extending to the left, and to the right, alternating.
2356  for (int cnt = 0; cnt < 2 * n; cnt++) {
2357  int index = i - ((cnt >> 1) + 1) * (cnt & 1) + (cnt >> 1) * !(cnt & 1);
2358  if (index < 0 || index >= n) {
2359  continue;
2360  }
2361 
2362  RenderTableCell *cell = row->row->at(index);
2363  if (!cell || (long)cell == -1) {
2364  continue;
2365  }
2366 
2367 #if DEBUG_CARETMODE > 1
2368  // qCDebug(KHTML_LOG) << "index " << index << " cell " << cell;
2369 #endif
2370  RenderTable *nestedTable;
2371  if (containsEditableElement(part, cell, nestedTable, fromEnd)) {
2372 
2373  if (nestedTable) {
2374  TableRowIterator it(nestedTable, fromEnd);
2375  while (*it) {
2376 // qCDebug(KHTML_LOG) << "=== recursive invocation";
2377  cell = findNearestTableCell(part, x, it, fromEnd);
2378  if (cell) {
2379  break;
2380  }
2381  if (fromEnd) {
2382  --it;
2383  } else {
2384  ++it;
2385  }
2386  }/*wend*/
2387  }/*end if*/
2388 
2389  return cell;
2390  }/*end if*/
2391  }/*next i*/
2392  return 0;
2393 }
2394 
2395 /** returns the nearest common ancestor of two objects that is a table cell,
2396  * a table section, or 0 if not inside a common table.
2397  *
2398  * If @p r1 and @p r2 belong to the same table, but different sections, @p r1's
2399  * section is returned.
2400  */
2402  RenderObject *r2)
2403 {
2404  if (!r1 || !r2) {
2405  return 0;
2406  }
2407  RenderTableSection *sec = 0;
2408  int start_depth = 0, end_depth = 0;
2409  // First we find the depths of the two objects in the tree (start_depth, end_depth)
2410  RenderObject *n = r1;
2411  while (n->parent()) {
2412  n = n->parent();
2413  start_depth++;
2414  }/*wend*/
2415  n = r2;
2416  while (n->parent()) {
2417  n = n->parent();
2418  end_depth++;
2419  }/*wend*/
2420  // here we climb up the tree with the deeper object, until both objects have equal depth
2421  while (end_depth > start_depth) {
2422  r2 = r2->parent();
2423  end_depth--;
2424  }/*wend*/
2425  while (start_depth > end_depth) {
2426  r1 = r1->parent();
2427 // if (r1->isTableSection()) sec = static_cast<RenderTableSection *>(r1);
2428  start_depth--;
2429  }/*wend*/
2430  // Climb the tree with both r1 and r2 until they are the same
2431  while (r1 != r2) {
2432  r1 = r1->parent();
2433  if (r1->isTableSection()) {
2434  sec = static_cast<RenderTableSection *>(r1);
2435  }
2436  r2 = r2->parent();
2437  }/*wend*/
2438 
2439  // At this point, we found the most approximate common ancestor. Now climb
2440  // up until the condition of the function return value is satisfied.
2441  while (r1 && !r1->isTableCell() && !r1->isTableSection() && !r1->isTable()) {
2442  r1 = r1->parent();
2443  }
2444 
2445  return r1 && r1->isTable() ? sec : r1;
2446 }
2447 
2448 /** Finds the row that contains the given cell, directly, or indirectly
2449  * @param section section to be searched
2450  * @param cell table cell
2451  * @param row returns the row
2452  * @param directCell returns the direct cell that contains @p cell
2453  * @return the index of the row.
2454  */
2455 static int findRowInSection(RenderTableSection *section, RenderTableCell *cell,
2456  RenderTableSection::RowStruct *&row, RenderTableCell *&directCell)
2457 {
2458  // Seek direct cell
2459  RenderObject *r = cell;
2460  while (r != section) {
2461  if (r->isTableCell()) {
2462  directCell = static_cast<RenderTableCell *>(r);
2463  }
2464  r = r->parent();
2465  }/*wend*/
2466 
2467  // So, and this is really nasty: As we have no indices, we have to do a
2468  // linear comparison. Oh, that sucks so much for long tables, you can't
2469  // imagine.
2470  int n = section->numRows();
2471  for (int i = 0; i < n; i++) {
2472  row = &section->grid[i];
2473 
2474  // check for cell
2475  int m = row->row->size();
2476  for (int j = 0; j < m; j++) {
2477  RenderTableCell *c = row->row->at(j);
2478  if (c == directCell) {
2479  return i;
2480  }
2481  }/*next j*/
2482 
2483  }/*next i*/
2484  Q_ASSERT(false);
2485  return -1;
2486 }
2487 
2488 /** finds the table that is the first direct or indirect descendant of @p block.
2489  * @param leaf object to begin search from.
2490  * @param block object to search to, or 0 to search up to top.
2491  * @return the table or 0 if there were none.
2492  */
2493 static inline RenderTable *findFirstDescendantTable(RenderObject *leaf, RenderBlock *block)
2494 {
2495  RenderTable *result = 0;
2496  while (leaf && leaf != block) {
2497  if (leaf->isTable()) {
2498  result = static_cast<RenderTable *>(leaf);
2499  }
2500  leaf = leaf->parent();
2501  }/*wend*/
2502  return result;
2503 }
2504 
2505 /** looks for the table cell the given object @p r is contained within.
2506  * @return the table cell or 0 if not contained in any table.
2507  */
2508 static inline RenderTableCell *containingTableCell(RenderObject *r)
2509 {
2510  while (r && !r->isTableCell()) {
2511  r = r->parent();
2512  }
2513  return static_cast<RenderTableCell *>(r);
2514 }
2515 
2516 inline void ErgonomicEditableLineIterator::calcAndStoreNewLine(
2517  RenderBlock *newBlock, bool toBegin)
2518 {
2519  // take the first/last editable element in the found cell as the new
2520  // value for the iterator
2521  CaretBoxIterator it;
2522  cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
2523  newBlock, true, toBegin, it);
2524 #if DEBUG_CARETMODE > 3
2525  // qCDebug(KHTML_LOG) << cbl->information();
2526 #endif
2527 // if (toBegin) prevBlock(); else nextBlock();
2528 
2529  if (!cbl) {
2530  return;
2531  }/*end if*/
2532 
2533  EditableLineIterator::advance(toBegin);
2534 }
2535 
2536 void ErgonomicEditableLineIterator::determineTopologicalElement(
2537  RenderTableCell *oldCell, RenderObject *newObject, bool toBegin)
2538 {
2539  // When we arrive here, a transition between cells has happened.
2540  // Now determine the type of the transition. This can be
2541  // (1) a transition from this cell into a table inside this cell.
2542  // (2) a transition from this cell into another cell of this table
2543 
2544  TableRowIterator it;
2545 
2546  RenderObject *commonAncestor = commonAncestorTableSectionOrCell(oldCell, newObject);
2547 #if DEBUG_CARETMODE > 1
2548  // qCDebug(KHTML_LOG) << " ancestor " << commonAncestor;
2549 #endif
2550 
2551  // The whole document is treated as a table cell.
2552  if (!commonAncestor || commonAncestor->isTableCell()) { // (1)
2553 
2554  RenderTableCell *cell = static_cast<RenderTableCell *>(commonAncestor);
2555  RenderTable *table = findFirstDescendantTable(newObject, cell);
2556 
2557 #if DEBUG_CARETMODE > 0
2558  // qCDebug(KHTML_LOG) << "table cell: " << cell;
2559 #endif
2560 
2561  // if there is no table, we fell out of the previous table, and are now
2562  // in some table-less block. Therefore, done.
2563  if (!table) {
2564  return;
2565  }
2566 
2567  it = TableRowIterator(table, toBegin);
2568 
2569  } else if (commonAncestor->isTableSection()) { // (2)
2570 
2571  RenderTableSection *section = static_cast<RenderTableSection *>(commonAncestor);
2572  RenderTableSection::RowStruct *row;
2573  int idx = findRowInSection(section, oldCell, row, oldCell);
2574 #if DEBUG_CARETMODE > 1
2575  // qCDebug(KHTML_LOG) << "table section: row idx " << idx;
2576 #endif
2577 
2578  it = TableRowIterator(section, idx);
2579 
2580  // advance rowspan rows
2581  int rowspan = oldCell->rowSpan();
2582  while (*it && rowspan--) {
2583  if (toBegin) {
2584  --it;
2585  } else {
2586  ++it;
2587  }
2588  }/*wend*/
2589 
2590  } else {
2591  kError(6201) << "Neither common cell nor section! " << commonAncestor->renderName();
2592  // will crash on uninitialized table row iterator
2593  }/*end if*/
2594 
2595  RenderTableCell *cell = findNearestTableCell(lines->m_part, xCoor, it, toBegin);
2596 #if DEBUG_CARETMODE > 1
2597  // qCDebug(KHTML_LOG) << "findNearestTableCell result: " << cell;
2598 #endif
2599 
2600  RenderBlock *newBlock = cell;
2601  if (!cell) {
2602  Q_ASSERT(commonAncestor->isTableSection());
2603  RenderTableSection *section = static_cast<RenderTableSection *>(commonAncestor);
2604  cell = containingTableCell(section);
2605 #if DEBUG_CARETMODE > 1
2606  // qCDebug(KHTML_LOG) << "containing cell: " << cell;
2607 #endif
2608 
2609  RenderTable *nestedTable;
2610  bool editableChild = cell && containsEditableChildElement(lines->m_part,
2611  cell, nestedTable, toBegin, section->table());
2612 
2613  if (cell && !editableChild) {
2614 #if DEBUG_CARETMODE > 1
2615  // qCDebug(KHTML_LOG) << "========= recursive invocation outer =========";
2616 #endif
2617  determineTopologicalElement(cell, cell->section(), toBegin);
2618 #if DEBUG_CARETMODE > 1
2619  // qCDebug(KHTML_LOG) << "========= end recursive invocation outer =========";
2620 #endif
2621  return;
2622 
2623  } else if (cell && nestedTable) {
2624 #if DEBUG_CARETMODE > 1
2625  // qCDebug(KHTML_LOG) << "========= recursive invocation inner =========";
2626 #endif
2627  determineTopologicalElement(cell, nestedTable, toBegin);
2628 #if DEBUG_CARETMODE > 1
2629  // qCDebug(KHTML_LOG) << "========= end recursive invocation inner =========";
2630 #endif
2631  return;
2632 
2633  } else {
2634 #if DEBUG_CARETMODE > 1
2635  // qCDebug(KHTML_LOG) << "newBlock is table: " << section->table();
2636 #endif
2637  RenderObject *r = section->table();
2638  int state; // not used
2639  ObjectTraversalState trav = OutsideAscending;
2640  r = advanceSuitableObject(r, trav, toBegin, lines->baseObject(), state);
2641  if (!r) {
2642  cbl = 0;
2643  return;
2644  }
2645 // if (toBegin) prevBlock(); else nextBlock();
2646  newBlock = static_cast<RenderBlock *>(!r || r->isRenderBlock() ? r : r->containingBlock());
2647  }/*end if*/
2648 #if 0
2649  } else {
2650  // adapt cell so that prevBlock/nextBlock works as expected
2651  newBlock = cell;
2652  // on forward advancing, we must start from the outside end of the
2653  // previous object
2654  if (!toBegin) {
2655  RenderObject *r = newBlock;
2656  int state; // not used
2657  ObjectTraversalState trav = OutsideAscending;
2658  r = advanceSuitableObject(r, trav, true, lines->advancePolicy(), lines->baseObject(), state);
2659  newBlock = static_cast<RenderBlock *>(!r || r->isRenderBlock() ? r : r->containingBlock());
2660  }/*end if*/
2661 #endif
2662  }/*end if*/
2663 
2664  calcAndStoreNewLine(newBlock, toBegin);
2665 }
2666 
2667 ErgonomicEditableLineIterator &ErgonomicEditableLineIterator::operator ++()
2668 {
2669  RenderTableCell *oldCell = containingTableCell(cbl->enclosingObject());
2670 
2671  EditableLineIterator::operator ++();
2672  if (*this == lines->end() || *this == lines->preBegin()) {
2673  return *this;
2674  }
2675 
2676  RenderTableCell *newCell = containingTableCell(cbl->enclosingObject());
2677 
2678  if (!newCell || newCell == oldCell) {
2679  return *this;
2680  }
2681 
2682  determineTopologicalElement(oldCell, newCell, false);
2683 
2684  return *this;
2685 }
2686 
2687 ErgonomicEditableLineIterator &ErgonomicEditableLineIterator::operator --()
2688 {
2689  RenderTableCell *oldCell = containingTableCell(cbl->enclosingObject());
2690 
2691  EditableLineIterator::operator --();
2692  if (*this == lines->end() || *this == lines->preBegin()) {
2693  return *this;
2694  }
2695 
2696  RenderTableCell *newCell = containingTableCell(cbl->enclosingObject());
2697 
2698  if (!newCell || newCell == oldCell) {
2699  return *this;
2700  }
2701 
2702  determineTopologicalElement(oldCell, newCell, true);
2703 
2704  return *this;
2705 }
2706 
2707 // == Navigational helper functions ==
2708 
2709 /** seeks the caret box which contains or is the nearest to @p x
2710  * @param it line iterator pointing to line to be searched
2711  * @param cv caret view context
2712  * @param x returns the cv->origX approximation, relatively positioned to the
2713  * containing block.
2714  * @param absx returns absolute x-coordinate of containing block
2715  * @param absy returns absolute y-coordinate of containing block
2716  * @return the most suitable caret box
2717  */
2718 static CaretBox *nearestCaretBox(LineIterator &it, CaretViewContext *cv,
2719  int &x, int &absx, int &absy)
2720 {
2721  // Find containing block
2722  RenderObject *cb = (*it)->containingBlock();
2723 #if DEBUG_CARETMODE > 4
2724  // qCDebug(KHTML_LOG) << "nearestCB: cb " << cb << "@" << (cb ? cb->renderName() : "");
2725 #endif
2726 
2727  if (cb) {
2728  cb->absolutePosition(absx, absy);
2729  } else {
2730  absx = absy = 0;
2731  }
2732 
2733  // Otherwise find out in which inline box the caret is to be placed.
2734 
2735  // this horizontal position is to be approximated
2736  x = cv->origX - absx;
2737  CaretBox *caretBox = 0; // Inline box containing the caret
2738 // NodeImpl *lastnode = 0; // node of previously checked render object.
2739  int xPos; // x-coordinate of current inline box
2740  int oldXPos = -1; // x-coordinate of last inline box
2741  EditableCaretBoxIterator fbit = it;
2742 #if DEBUG_CARETMODE > 0
2743  /* if (it.linearDocument()->advancePolicy() != LeafsOnly)
2744  qCWarning(KHTML_LOG) << "nearestInlineBox is only prepared to handle the LeafsOnly advance policy";*/
2745 // qCDebug(KHTML_LOG) << "*fbit = " << *fbit;
2746 #endif
2747  // Iterate through all children
2748  for (CaretBox * b; fbit != (*it)->end(); ++fbit) {
2749  b = *fbit;
2750 
2751 #if DEBUG_CARETMODE > 0
2752 // RenderObject *r = b->object();
2753 // if (b->isInlineFlowBox()) qCDebug(KHTML_LOG) << "b is inline flow box";
2754 // qCDebug(KHTML_LOG) << "approximate r" << r << ": " << (r ? r->renderName() : QString()) << (r && r->isText() ? " contains \"" + QString(((RenderText *)r)->str->s, ((RenderText *)r)->str->l) + "\"" : QString());
2755 #endif
2756  xPos = b->xPos();
2757 
2758  // the caret is before this box
2759  if (x < xPos) {
2760  // snap to nearest box
2761  if (oldXPos < 0 || x - (oldXPos + caretBox->width()) > xPos - x) {
2762  caretBox = b; // current box is nearer
2763  }/*end if*/
2764  break; // Otherwise, preceding box is implicitly used
2765  }
2766 
2767  caretBox = b;
2768 
2769  // the caret is within this box
2770  if (x >= xPos && x < xPos + caretBox->width()) {
2771  break;
2772  }
2773  oldXPos = xPos;
2774 
2775  // the caret can only be after the last box which is automatically
2776  // contained in caretBox when we fall out of the loop.
2777  }/*next fbit*/
2778 
2779  return caretBox;
2780 }
2781 
2782 /** moves the given iterator to the beginning of the next word.
2783  *
2784  * If the end is reached, the iterator will be positioned there.
2785  * @param it character iterator to be moved
2786  */
2787 static void moveItToNextWord(EditableCharacterIterator &it)
2788 {
2789 #if DEBUG_CARETMODE > 0
2790  // qCDebug(KHTML_LOG) << "%%%%%%%%%%%%%%%%%%%%% moveItToNextWord";
2791 #endif
2792  EditableCharacterIterator copy;
2793  while (!it.isEnd() && !(*it).isSpace() && !(*it).isPunct()) {
2794 #if DEBUG_CARETMODE > 2
2795  // qCDebug(KHTML_LOG) << "reading1 '" << (*it).toLatin1().constData() << "'";
2796 #endif
2797  copy = it;
2798  ++it;
2799  }
2800 
2801  if (it.isEnd()) {
2802  it = copy;
2803  return;
2804  }/*end if*/
2805 
2806  while (!it.isEnd() && ((*it).isSpace() || (*it).isPunct())) {
2807 #if DEBUG_CARETMODE > 2
2808  // qCDebug(KHTML_LOG) << "reading2 '" << (*it).toLatin1().constData() << "'";
2809 #endif
2810  copy = it;
2811  ++it;
2812  }
2813 
2814  if (it.isEnd()) {
2815  it = copy;
2816  }
2817 }
2818 
2819 /** moves the given iterator to the beginning of the previous word.
2820  *
2821  * If the beginning is reached, the iterator will be positioned there.
2822  * @param it character iterator to be moved
2823  */
2824 static void moveItToPrevWord(EditableCharacterIterator &it)
2825 {
2826  if (it.isEnd()) {
2827  return;
2828  }
2829 
2830 #if DEBUG_CARETMODE > 0
2831  // qCDebug(KHTML_LOG) << "%%%%%%%%%%%%%%%%%%%%% moveItToPrevWord";
2832 #endif
2833  EditableCharacterIterator copy;
2834 
2835  // Jump over all space and punctuation characters first
2836  do {
2837  copy = it;
2838  --it;
2839 #if DEBUG_CARETMODE > 2
2840  if (!it.isEnd()) // qCDebug(KHTML_LOG) << "reading1 '" << (*it).toLatin1().constData() << "'";
2841 #endif
2842  } while (!it.isEnd() && ((*it).isSpace() || (*it).isPunct()));
2843 
2844  if (it.isEnd()) {
2845  it = copy;
2846  return;
2847  }/*end if*/
2848 
2849  do {
2850  copy = it;
2851  --it;
2852 #if DEBUG_CARETMODE > 0
2853  if (!it.isEnd()) // qCDebug(KHTML_LOG) << "reading2 '" << (*it).toLatin1().constData() << "' (" << (int)(*it).toLatin1().constData() << ") box " << it.caretBox();
2854 #endif
2855  } while (!it.isEnd() && !(*it).isSpace() && !(*it).isPunct());
2856 
2857  it = copy;
2858 #if DEBUG_CARETMODE > 1
2859  if (!it.isEnd()) // qCDebug(KHTML_LOG) << "effective '" << (*it).toLatin1().constData() << "' (" << (int)(*it).toLatin1().constData() << ") box " << it.caretBox();
2860 #endif
2861  }
2862 
2863 /** moves the iterator by one page.
2864  * @param ld linear document
2865  * @param it line iterator, will be updated accordingly
2866  * @param mindist minimum distance in pixel the iterator should be moved
2867  * (if possible)
2868  * @param next @p true, move downward, @p false move upward
2869  */
2870 static void moveIteratorByPage(LinearDocument &ld,
2871  ErgonomicEditableLineIterator &it, int mindist, bool next)
2872 {
2873  // ### This whole routine plainly sucks. Use an inverse strategie for pgup/pgdn.
2874 
2875  if (it == ld.end() || it == ld.preBegin()) {
2876  return;
2877  }
2878 
2879  ErgonomicEditableLineIterator copy = it;
2880 #if DEBUG_CARETMODE > 0
2881  // qCDebug(KHTML_LOG) << " mindist: " << mindist;
2882 #endif
2883 
2884  CaretBoxLine *cbl = *copy;
2885  int absx = 0, absy = 0;
2886 
2887  RenderBlock *lastcb = cbl->containingBlock();
2888  Q_ASSERT(lastcb->isRenderBlock());
2889  lastcb->absolutePosition(absx, absy, false); // ### what about fixed?
2890 
2891  int lastfby = cbl->begin().data()->yPos();
2892  int lastheight = 0;
2893  int rescue = 1000; // ### this is a hack to keep stuck carets from hanging the ua
2894  do {
2895  if (next) {
2896  ++copy;
2897  } else {
2898  --copy;
2899  }
2900  if (copy == ld.end() || copy == ld.preBegin()) {
2901  break;
2902  }
2903 
2904  cbl = *copy;
2905  RenderBlock *cb = cbl->containingBlock();
2906 
2907  int diff = 0;
2908  // ### actually flowBox->yPos() should suffice, but this is not ported
2909  // over yet from WebCore
2910  int fby = cbl->begin().data()->yPos();
2911  if (cb != lastcb) {
2912  if (next) {
2913  diff = absy + lastfby + lastheight;
2914  cb->absolutePosition(absx, absy, false); // ### what about fixed?
2915  diff = absy - diff + fby;
2916  lastfby = 0;
2917  } else {
2918  diff = absy;
2919  cb->absolutePosition(absx, absy, false); // ### what about fixed?
2920  diff -= absy + fby + lastheight;
2921  lastfby = fby - lastheight;
2922  }/*end if*/
2923 #if DEBUG_CARETMODE > 2
2924  // qCDebug(KHTML_LOG) << "absdiff " << diff;
2925 #endif
2926  } else {
2927  diff = qAbs(fby - lastfby);
2928  }/*end if*/
2929 #if DEBUG_CARETMODE > 2
2930  // qCDebug(KHTML_LOG) << "cbl->begin().data()->yPos(): " << fby << " diff " << diff;
2931 #endif
2932 
2933  mindist -= diff;
2934 
2935  lastheight = qAbs(fby - lastfby);
2936  lastfby = fby;
2937  lastcb = cb;
2938  it = copy;
2939 #if DEBUG_CARETMODE > 0
2940  // qCDebug(KHTML_LOG) << " mindist: " << mindist;
2941 #endif
2942  // trick: actually the distance is always one line short, but we cannot
2943  // calculate the height of the first line (### WebCore will make it better)
2944  // Therefore, we simply approximate that excess line by using the last
2945  // caluculated line height.
2946  } while (mindist - lastheight > 0 && --rescue);
2947 }
2948 
2949 }/*end namespace*/
static RenderTableCell * containingTableCell(RenderObject *r)
looks for the table cell the given object r is contained within.
static RenderTableCell * findNearestTableCellInRow(KHTMLPart *part, int x, RenderTableSection::RowStruct *row, bool fromEnd)
finds the nearest editable cell around the given absolute x-coordinate
static RenderObject * commonAncestorTableSectionOrCell(RenderObject *r1, RenderObject *r2)
returns the nearest common ancestor of two objects that is a table cell, a table section, or 0 if not inside a common table.
static bool containsEditableChildElement(KHTMLPart *part, RenderBlock *cb, RenderTable *&table, bool fromEnd, RenderObject *start)
checks whether the given block contains at least one editable child element, beginning with but exclu...
int ascent() const const
void mapDOMPosToRenderPos(NodeImpl *node, long offset, RenderObject *&r, long &r_ofs, bool &outside, bool &outsideEnd)
Maps a DOM Range position to the corresponding caret position.
static void mapRenderPosToTraversalState(bool outside, bool atEnd, bool toBegin, ObjectTraversalState &trav)
Converts a caret position to its respective object traversal state.
static RenderObject * advanceObject(RenderObject *r, ObjectTraversalState &trav, bool toBegin, RenderObject *base, int &state)
Advances to the next render object, taking into account the current traversal state.
static bool isDescendant(RenderObject *r, RenderObject *cb)
checks whether r is a descendant of cb, or r == cb
static void mapTraversalStateToRenderPos(ObjectTraversalState trav, bool toBegin, bool &outside, bool &atEnd)
Converts a traversal state to its respective caret position.
This file is part of the HTML rendering engine for KDE.
static RenderObject * traverseRenderObjects(RenderObject *obj, ObjectTraversalState &trav, bool toBegin, RenderObject *base, int &state)
Traverses the render object tree in a fine granularity.
Definition: khtml_caret.cpp:60
This class is khtml&#39;s main class.
Definition: khtml_part.h:208
void mapRenderPosToDOMPos(RenderObject *r, long r_ofs, bool outside, bool outsideEnd, NodeImpl *&node, long &offset)
Maps a caret position to the corresponding DOM Range position.
ObjectAdvanceState
Flags representing the type of advance that has been made.
Definition: khtml_caret.cpp:35
ObjectTraversalState
All possible states that may occur during render object traversal.
Definition: khtml_caret.cpp:47
static RenderTable * findTableUpTo(RenderObject *r, RenderFlow *cb)
finds the innermost table object r is contained within, but no farther than cb.
static RenderTable * findFirstDescendantTable(RenderObject *leaf, RenderBlock *block)
finds the table that is the first direct or indirect descendant of block.
InlineFlowBox * seekBaseFlowBox(InlineBox *b, RenderObject *base=0)
seeks the root line box that is the parent of the given inline box.
static bool isIndicatedInlineBox(InlineBox *box)
Checks whether the given inline box matches the IndicatedFlows policy.
static bool containsEditableElement(KHTMLPart *part, RenderBlock *cb, RenderTable *&table, bool fromEnd=false)
checks whether the given block contains at least one editable element.
static RenderObject * findRenderer(NodeImpl *&node, long offset, RenderObject *base, long &r_ofs, bool &outside, bool &outsideEnd)
Finds the next node that has a renderer.
static NodeImpl * nextLeafNode(NodeImpl *r, NodeImpl *baseElem)
Returns the next leaf node.
bool isBlockRenderReplaced(RenderObject *r)
determines whether the given element is a block level replaced element.
static CaretBox * nearestCaretBox(LineIterator &it, CaretViewContext *cv, int &x, int &absx, int &absy)
seeks the caret box which contains or is the nearest to x
static int findRowInSection(RenderTableSection *section, RenderTableCell *cell, RenderTableSection::RowStruct *&row, RenderTableCell *&directCell)
Finds the row that contains the given cell, directly, or indirectly.
static RenderObject * advanceSuitableObject(RenderObject *r, ObjectTraversalState &trav, bool toBegin, RenderObject *base, int &state)
Advances to the next render object, taking into account the current traversal state, but skipping render objects which are not suitable for having placed the caret into them.
static void moveIteratorByPage(LinearDocument &ld, ErgonomicEditableLineIterator &it, int mindist, bool next)
moves the iterator by one page.
int leading() const const
all geometry managing stuff is only in the block elements.
Definition: render_flow.h:44
static bool isUnsuitable(RenderObject *r, ObjectTraversalState trav)
Check whether the current render object is unsuitable in caret mode handling.
static ElementImpl * determineBaseElement(NodeImpl *caretNode)
returns a suitable base element
bool isEditable() const
Returns true if the document is editable, false otherwise.
static void moveItToNextWord(EditableCharacterIterator &it)
moves the given iterator to the beginning of the next word.
int height() const const
Base Class for all rendering tree objects.
static void ensureLeafNode(NodeImpl *&node, NodeImpl *base)
Make sure the given node is a leaf node.
static void moveItToPrevWord(EditableCharacterIterator &it)
moves the given iterator to the beginning of the previous word.
bool isCaretMode() const
Returns whether caret mode is on/off.
static RenderObject * renderObjectAbove(RenderObject *obj, ObjectTraversalState &trav, RenderObject *base)
Like RenderObject::objectAbove, but confined to stay within base.
static RenderTableCell * findNearestTableCell(KHTMLPart *part, int x, TableRowIterator &it, bool fromEnd)
finds the cell corresponding to absolute x-coordinate x in the given table.
static RenderObject * renderObjectBelow(RenderObject *obj, ObjectTraversalState &trav, RenderObject *base)
Like RenderObject::objectBelow, but confined to stay within base.
static bool isIndicatedFlow(RenderObject *r)
Checks whether the given render object matches the IndicatedFlows policy.
static bool isCaretBoxEmpty(CaretBox *box)
returns true when the given caret box is empty, i.
static CaretBoxLine * findCaretBoxLine(DOM::NodeImpl *node, long offset, CaretBoxLineDeleter *cblDeleter, RenderObject *base, long &r_ofs, CaretBoxIterator &caretBoxIt)
determines the caret line box that contains the given position.
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Thu Jul 29 2021 22:46:28 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.