Warning: That file was not part of the compilation database. It may have many parsing errors.

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

Warning: That file was not part of the compilation database. It may have many parsing errors.