• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • tdehtml
 

tdehtml

  • tdehtml
tdehtml_caret.cpp
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 "tdehtml_caret_p.h"
23
24#include "html/html_documentimpl.h"
25
26namespace tdehtml {
27
35enum ObjectAdvanceState {
36 LeftObject = 0x01, AdvancedToSibling = 0x02, EnteredObject = 0x04
37};
38
47enum ObjectTraversalState {
48 OutsideDescending, InsideDescending, InsideAscending, OutsideAscending
49};
50
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
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 kdDebug(6200) << "renderObjectBelow: r " << r << " trav " << trav << endl;
114#endif
115 }
116 trav = InsideDescending;
117 return r;
118}
119
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 kdDebug(6200) << "renderObjectAbove: r " << r << " trav " << trav << endl;
134#endif
135 }
136 trav = InsideAscending;
137 return r;
138}
139
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
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
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
204kdDebug(6200) << "a " << a << " trav " << trav << endl;
205#endif
206 if (a->element()) {
207#if DEBUG_CARETMODE > 4
208kdDebug(6200) << "a " << a << " trav " << trav << " origtrav " << origtrav << " ignoreOD " << ignoreOutsideDesc << endl;
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
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)->inlineTextBoxCount() == 0);
288 ;
289 Q_UNUSED(trav);
290}
291
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 kdDebug(6200) << "after advanceSWP: r " << r << " trav " << trav << " toBegin " << toBegin << endl;
313#endif
314 } while (isUnsuitable(r, trav));
315 return r;
316}
317
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
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
411void /*TDE_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 kdDebug(6200) << "mapDTR: child " << child << "@" << (child ? child->nodeName().string() : TQString::null) << " atEnd " << atEnd << endl;
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 kdWarning() << k_funcinfo << "Mapping from nodes of type " << node->nodeType()
469 << " not supported!" << endl;
470 }
471}
472
483void /*TDE_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 kdDebug(6200) << "mapRTD: r " << r << "@" << (r ? r->renderName() : TQString::null) << (r && r->element() ? TQString(".node ") + TQString::number((unsigned)r->element(),16) + "@" + r->element()->nodeName().string() : TQString::null) << " outside " << outside << " outsideEnd " << outsideEnd << endl;
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 kdDebug(6200) << node << "@" << (node ? node->nodeName().string() : TQString::null) << " offset " << offset << endl;
516#endif
517 } else { // !outside
518inside:
519 offset = r_ofs;
520 }
521
522 } else {
523 offset = 0;
524 kdWarning() << k_funcinfo << "Mapping to nodes of type " << node->nodeType()
525 << " not supported!" << endl;
526 }
527}
528
530static inline void ensureLeafNode(NodeImpl *&node, NodeImpl *base)
531{
532 if (node && node->hasChildNodes()) node = nextLeafNode(node, base);
533}
534
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
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
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 kdDebug(6200) << "findRenderer: node " << node << " " << (node ? node->nodeName().string() : TQString::null) << " offset " << offset << " r " << r << "[" << (r ? r->renderName() : TQString::null) << "] r_ofs " << r_ofs << " outside " << outside << " outsideEnd " << outsideEnd << endl;
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 kdDebug(6200) << "1r " << r << endl;
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 kdDebug(6200) << "2r " << r << endl;
615#endif
616 return r;
617}
618
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(TQTextStream &ts, const TQString &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) /*TDE_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
672kdDebug(6200) << "box " << box << endl;
673kdDebug(6200) << "box->object " << box->object() << endl;
674kdDebug(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() << endl;
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
687kdDebug(6200) << "isinlineflowbox " << box << endl;
688#endif
689 InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(box);
690 bool rtl = ps->direction() == RTL;
691 const TQFontMetrics &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
711kdDebug(6200) << "this " << this << " flowBox " << flowBox << " firstChild " << flowBox->firstChild() << endl;
712kdDebug(6200) << "== recursive invocation" << endl;
713#endif
714 addConvertedInlineBox(flowBox->firstChild(), sbp);
715#if DEBUG_ACIB
716kdDebug(6200) << "== recursive invocation end" << endl;
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
733kdDebug(6200) << "isinlinetextbox " << box << (box->object() ? TQString(" contains \"%1\"").arg(TQConstString(static_cast<RenderText *>(box->object())->str->s+box->minOffset(), kMin(box->maxOffset() - box->minOffset(), 15L)).string()) : TQString::null) << endl;
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
742kdDebug(6200) << "some replaced or what " << box << endl;
743#endif
744 // must be an inline-block, inline-table, or any RenderReplaced
745 bool rtl = ps->direction() == RTL;
746 const TQFontMetrics &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 TQFontMetrics &fm) /*TDE_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 TQFontMetrics &fm, bool left, bool rtl) /*TDE_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 TQFontMetrics &fm, bool left, bool rtl) /*TDE_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// TDE_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) /*TDE_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 TQFontMetrics &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 += kMax(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 TQFontMetrics &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 TDEHTML_LEFT:
888 case TAAUTO: // ### find out what this does
889 case JUSTIFY:
890 break;
891 case CENTER:
892 case TDEHTML_CENTER:
893 _x += cb->contentWidth() / 2;
894 break;
895 case TDEHTML_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(TQTextStream &ts, const TQString &ind) const
912{
913 ts << ind << "cbl: baseFlowBox@" << basefb << endl;
914 TQString 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
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
943inline bool isBlockRenderReplaced(RenderObject *r)
944{
945 return r->isRenderReplaced() && r->style()->display() == BLOCK;
946}
947
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 kdDebug(6200) << "=================== findCaretBoxLine" << endl;
973 kdDebug(6200) << "node " << node << " offset: " << offset << " r " << r->renderName() << "[" << r << "].node " << r->element()->nodeName().string() << "[" << r->element() << "]" << " r_ofs " << r_ofs << " outside " << outside << " outsideEnd " << outsideEnd << endl;
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->m_lines.count() > 0)
995 b = t->m_lines[t->m_lines.count() - 1];
996 else
997 break;
998 }/*end if*/
999 Q_ASSERT(b);
1000 outside = false; // text boxes cannot have outside positions
1001 InlineFlowBox *baseFlowBox = seekBaseFlowBox(b, base);
1002#if DEBUG_CARETMODE > 2
1003 kdDebug(6200) << "text-box b: " << b << " baseFlowBox: " << baseFlowBox << (b && b->object() ? TQString(" contains \"%1\"").arg(TQConstString(static_cast<RenderText *>(b->object())->str->s+b->minOffset(), kMin(b->maxOffset() - b->minOffset(), 15L)).string()) : TQString::null) << endl;
1004#endif
1005#if 0
1006 if (t->containingBlock()->isListItem()) dumpLineBoxes(static_cast<RenderFlow *>(t->containingBlock()));
1007#endif
1008#if DEBUG_CARETMODE > 0
1009 kdDebug(6200) << "=================== end findCaretBoxLine (renderText)" << endl;
1010#endif
1011 return CaretBoxLine::constructCaretBoxLine(cblDeleter, baseFlowBox,
1012 b, outside, outsideEnd, caretBoxIt);
1013 } while(false);/*end if*/
1014
1015 // (B)
1016 bool isrepl = isBlockRenderReplaced(r);
1017 if (r->isRenderBlock() || r->isRenderInline() || isrepl) {
1018 RenderFlow *flow = static_cast<RenderFlow *>(r);
1019 InlineFlowBox *firstLineBox = isrepl ? 0 : flow->firstLineBox();
1020
1021 // On render blocks, if we are outside, or have a totally empty render
1022 // block, we simply construct a special caret box line.
1023 // The latter case happens only when the render block is a leaf object itself.
1024 if (isrepl || r->isRenderBlock() && (outside || !firstLineBox)
1025 || r->isRenderInline() && !firstLineBox) {
1026 #if DEBUG_CARETMODE > 0
1027 kdDebug(6200) << "=================== end findCaretBoxLine (box " << (outside ? (outsideEnd ? "outside end" : "outside begin") : "inside") << ")" << endl;
1028 #endif
1029 Q_ASSERT(r->isBox());
1030 return CaretBoxLine::constructCaretBoxLine(cblDeleter,
1031 static_cast<RenderBox *>(r), outside, outsideEnd, caretBoxIt);
1032 }/*end if*/
1033
1034 kdDebug(6200) << "firstlinebox " << firstLineBox << endl;
1035 InlineFlowBox *baseFlowBox = seekBaseFlowBox(firstLineBox, base);
1036 return CaretBoxLine::constructCaretBoxLine(cblDeleter, baseFlowBox,
1037 firstLineBox, outside, outsideEnd, caretBoxIt);
1038 }/*end if*/
1039
1040 RenderBlock *cb = r->containingBlock();
1041 //if ( !cb ) return 0L;
1042 Q_ASSERT(cb);
1043
1044 // ### which element doesn't have a block as its containing block?
1045 // Is it still possible after the RenderBlock/RenderInline merge?
1046 if (!cb->isRenderBlock()) {
1047 kdWarning() << "containing block is no render block!!! crash imminent" << endl;
1048 }/*end if*/
1049
1050 InlineFlowBox *flowBox = cb->firstLineBox();
1051 // (C)
1052 // This case strikes when the element is replaced, but neither a
1053 // RenderBlock nor a RenderInline
1054 if (!flowBox) { // ### utter emergency (why is this possible at all?)
1055// flowBox = generateDummyFlowBox(arena, cb, r);
1056// if (ibox) *ibox = flowBox->firstChild();
1057// outside = outside_end = true;
1058
1059// kdWarning() << "containing block contains no inline flow boxes!!! crash imminent" << endl;
1060#if DEBUG_CARETMODE > 0
1061 kdDebug(6200) << "=================== end findCaretBoxLine (2)" << endl;
1062#endif
1063 return CaretBoxLine::constructCaretBoxLine(cblDeleter, cb,
1064 outside, outsideEnd, caretBoxIt);
1065 }/*end if*/
1066
1067 // We iterate the inline flow boxes of the containing block until
1068 // we find the given node. This has one major flaw: it is linear, and therefore
1069 // painfully slow for really large blocks.
1070 for (; flowBox; flowBox = static_cast<InlineFlowBox *>(flowBox->nextLineBox())) {
1071#if DEBUG_CARETMODE > 0
1072 kdDebug(6200) << "[scan line]" << endl;
1073#endif
1074
1075 // construct a caret line box and stop when the element is contained within
1076 InlineFlowBox *baseFlowBox = seekBaseFlowBox(flowBox, base);
1077 CaretBoxLine *cbl = CaretBoxLine::constructCaretBoxLine(cblDeleter,
1078 baseFlowBox, 0, outside, outsideEnd, caretBoxIt, r);
1079#if DEBUG_CARETMODE > 5
1080 kdDebug(6200) << cbl->information() << endl;
1081#endif
1082 if (caretBoxIt != cbl->end()) {
1083#if DEBUG_CARETMODE > 0
1084 kdDebug(6200) << "=================== end findCaretBoxLine (3)" << endl;
1085#endif
1086 return cbl;
1087 }
1088 }/*next flowBox*/
1089
1090 // no inline flow box found, approximate to nearest following node.
1091 // Danger: this is O(n^2). It's only called to recover from
1092 // errors, that means, theoretically, never. (Practically, far too often :-( )
1093 Q_ASSERT(!flowBox);
1094 CaretBoxLine *cbl = findCaretBoxLine(nextLeafNode(node, base ? base->element() : 0), 0, cblDeleter, base, r_ofs, caretBoxIt);
1095#if DEBUG_CARETMODE > 0
1096 kdDebug(6200) << "=================== end findCaretBoxLine" << endl;
1097#endif
1098 return cbl;
1099}
1100
1107static inline RenderTable *findTableUpTo(RenderObject *r, RenderFlow *cb)
1108{
1109 while (r && r != cb && !r->isTable()) r = r->parent();
1110 return r && r->isTable() ? static_cast<RenderTable *>(r) : 0;
1111}
1112
1115static inline bool isDescendant(RenderObject *r, RenderObject *cb)
1116{
1117 while (r && r != cb) r = r->parent();
1118 return r;
1119}
1120
1131static bool containsEditableElement(TDEHTMLPart *part, RenderBlock *cb,
1132 RenderTable *&table, bool fromEnd = false)
1133{
1134 RenderObject *r = cb;
1135 if (fromEnd)
1136 while (r->lastChild()) r = r->lastChild();
1137 else
1138 while (r->firstChild()) r = r->firstChild();
1139
1140 RenderTable *tempTable = 0;
1141 table = 0;
1142 bool withinCb;
1143// int state; // not used
1144 ObjectTraversalState trav = InsideDescending;
1145 do {
1146 bool modWithinCb = withinCb = isDescendant(r, cb);
1147
1148 // treat cb extra, it would not be considered otherwise
1149 if (!modWithinCb) {
1150 modWithinCb = true;
1151 r = cb;
1152 } else
1153 tempTable = findTableUpTo(r, cb);
1154
1155#if DEBUG_CARETMODE > 1
1156 kdDebug(6201) << "cee: r " << (r ? r->renderName() : TQString::null) << "@" << r << " cb " << cb << " withinCb " << withinCb << " modWithinCb " << modWithinCb << " tempTable " << tempTable << endl;
1157#endif
1158 if (r && modWithinCb && r->element() && !isUnsuitable(r, trav)
1159 && (part->isCaretMode() || part->isEditable()
1160 || r->style()->userInput() == UI_ENABLED)) {
1161 table = tempTable;
1162#if DEBUG_CARETMODE > 1
1163 kdDebug(6201) << "cee: editable" << endl;
1164#endif
1165 return true;
1166 }/*end if*/
1167
1168// RenderObject *oldr = r;
1169// while (r && r == oldr)
1170// r = advanceSuitableObject(r, trav, fromEnd, cb->parent(), state);
1171 r = fromEnd ? r->objectAbove() : r->objectBelow();
1172 } while (r && withinCb);
1173 return false;
1174}
1175
1188static bool containsEditableChildElement(TDEHTMLPart *part, RenderBlock *cb,
1189 RenderTable *&table, bool fromEnd, RenderObject *start)
1190{
1191 int state = 0;
1192 ObjectTraversalState trav = OutsideAscending;
1193// kdDebug(6201) << "start: " << start << endl;
1194 RenderObject *r = start;
1195 do {
1196 r = traverseRenderObjects(r, trav, fromEnd, cb->parent(), state);
1197 } while(r && !(state & AdvancedToSibling));
1198// kdDebug(6201) << "r: " << r << endl;
1199 //advanceObject(start, trav, fromEnd, cb->parent(), state);
1200// RenderObject *oldr = r;
1201// while (r && r == oldr)
1202 if (!r) return false;
1203
1204 if (fromEnd)
1205 while (r->firstChild()) r = r->firstChild();
1206 else
1207 while (r->lastChild()) r = r->lastChild();
1208// kdDebug(6201) << "child r: " << r << endl;
1209 if (!r) return false;
1210
1211 RenderTable *tempTable = 0;
1212 table = 0;
1213 bool withinCb = false;
1214 do {
1215
1216 bool modWithinCb = withinCb = isDescendant(r, cb);
1217
1218 // treat cb extra, it would not be considered otherwise
1219 if (!modWithinCb) {
1220 modWithinCb = true;
1221 r = cb;
1222 } else
1223 tempTable = findTableUpTo(r, cb);
1224
1225#if DEBUG_CARETMODE > 1
1226 kdDebug(6201) << "cece: r " << (r ? r->renderName() : TQString::null) << "@" << r << " cb " << cb << " withinCb " << withinCb << " modWithinCb " << modWithinCb << " tempTable " << tempTable << endl;
1227#endif
1228 if (r && withinCb && r->element() && !isUnsuitable(r, trav)
1229 && (part->isCaretMode() || part->isEditable()
1230 || r->style()->userInput() == UI_ENABLED)) {
1231 table = tempTable;
1232#if DEBUG_CARETMODE > 1
1233 kdDebug(6201) << "cece: editable" << endl;
1234#endif
1235 return true;
1236 }/*end if*/
1237
1238 r = fromEnd ? r->objectAbove() : r->objectBelow();
1239 } while (withinCb);
1240 return false;
1241}
1242
1243// == class LinearDocument implementation
1244
1245LinearDocument::LinearDocument(TDEHTMLPart *part, NodeImpl *node, long offset,
1246 CaretAdvancePolicy advancePolicy, ElementImpl *baseElem)
1247 : node(node), offset(offset), m_part(part),
1248 advPol(advancePolicy), base(0)
1249{
1250 if (node == 0) return;
1251
1252 if (baseElem) {
1253 RenderObject *b = baseElem->renderer();
1254 if (b && (b->isRenderBlock() || b->isRenderInline()))
1255 base = b;
1256 }
1257
1258 initPreBeginIterator();
1259 initEndIterator();
1260}
1261
1262LinearDocument::~LinearDocument()
1263{
1264}
1265
1266int LinearDocument::count() const
1267{
1268 // FIXME: not implemented
1269 return 1;
1270}
1271
1272LinearDocument::Iterator LinearDocument::current()
1273{
1274 return LineIterator(this, node, offset);
1275}
1276
1277LinearDocument::Iterator LinearDocument::begin()
1278{
1279 NodeImpl *n = base ? base->element() : 0;
1280 if (!base) n = node ? node->getDocument() : 0;
1281 if (!n) return end();
1282
1283 n = n->firstChild();
1284 if (advPol == LeafsOnly)
1285 while (n->firstChild()) n = n->firstChild();
1286
1287 if (!n) return end(); // must be empty document or empty base element
1288 return LineIterator(this, n, n->minOffset());
1289}
1290
1291LinearDocument::Iterator LinearDocument::preEnd()
1292{
1293 NodeImpl *n = base ? base->element() : 0;
1294 if (!base) n = node ? node->getDocument() : 0;
1295 if (!n) return preBegin();
1296
1297 n = n->lastChild();
1298 if (advPol == LeafsOnly)
1299 while (n->lastChild()) n = n->lastChild();
1300
1301 if (!n) return preBegin(); // must be empty document or empty base element
1302 return LineIterator(this, n, n->maxOffset());
1303}
1304
1305void LinearDocument::initPreBeginIterator()
1306{
1307 _preBegin = LineIterator(this, 0, 0);
1308}
1309
1310void LinearDocument::initEndIterator()
1311{
1312 _end = LineIterator(this, 0, 1);
1313}
1314
1315// == class LineIterator implementation
1316
1317CaretBoxIterator LineIterator::currentBox /*TDE_NO_EXPORT*/;
1318long LineIterator::currentOffset /*TDE_NO_EXPORT*/;
1319
1320LineIterator::LineIterator(LinearDocument *l, DOM::NodeImpl *node, long offset)
1321 : lines(l)
1322{
1323// kdDebug(6200) << "LineIterator: node " << node << " offset " << offset << endl;
1324 if (!node) { cbl = 0; return; }
1325 cbl = findCaretBoxLine(node, offset, &lines->cblDeleter,
1326 l->baseObject(), currentOffset, currentBox);
1327 // can happen on partially loaded documents
1328#if DEBUG_CARETMODE > 0
1329 if (!cbl) kdDebug(6200) << "no render object found!" << endl;
1330#endif
1331 if (!cbl) return;
1332#if DEBUG_CARETMODE > 1
1333 kdDebug(6200) << "LineIterator: offset " << offset << " outside " << cbl->isOutside() << endl;
1334#endif
1335#if DEBUG_CARETMODE > 3
1336 kdDebug(6200) << cbl->information() << endl;
1337#endif
1338 if (currentBox == cbl->end()) {
1339#if DEBUG_CARETMODE > 0
1340 kdDebug(6200) << "LineIterator: findCaretBoxLine failed" << endl;
1341#endif
1342 cbl = 0;
1343 }/*end if*/
1344}
1345
1346void LineIterator::nextBlock()
1347{
1348 RenderObject *base = lines->baseObject();
1349
1350 bool cb_outside = cbl->isOutside();
1351 bool cb_outside_end = cbl->isOutsideEnd();
1352
1353 {
1354 RenderObject *r = cbl->enclosingObject();
1355
1356 ObjectTraversalState trav;
1357 int state; // not used
1358 mapRenderPosToTraversalState(cb_outside, cb_outside_end, false, trav);
1359#if DEBUG_CARETMODE > 1
1360 kdDebug(6200) << "nextBlock: before adv r" << r << " " << (r ? r->renderName() : TQString::null) << (r && r->isText() ? " contains \"" + TQString(((RenderText *)r)->str->s, TQMIN(((RenderText *)r)->str->l,15)) + "\"" : TQString::null) << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end << endl;
1361#endif
1362 r = advanceSuitableObject(r, trav, false, base, state);
1363 if (!r) {
1364 cbl = 0;
1365 return;
1366 }/*end if*/
1367
1368 mapTraversalStateToRenderPos(trav, false, cb_outside, cb_outside_end);
1369#if DEBUG_CARETMODE > 1
1370 kdDebug(6200) << "nextBlock: after r" << r << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end << endl;
1371#endif
1372#if DEBUG_CARETMODE > 0
1373 kdDebug(6200) << "++: r " << r << "[" << (r?r->renderName():TQString::null) << "]" << endl;
1374#endif
1375
1376 RenderBlock *cb;
1377
1378 // If we hit a block or replaced object, use this as its enclosing object
1379 bool isrepl = isBlockRenderReplaced(r);
1380 if (r->isRenderBlock() || isrepl) {
1381 RenderBox *cb = static_cast<RenderBox *>(r);
1382
1383 cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
1384 cb_outside, cb_outside_end, currentBox);
1385
1386#if DEBUG_CARETMODE > 0
1387 kdDebug(6200) << "r->isFlow is cb. continuation @" << cb->continuation() << endl;
1388#endif
1389 return;
1390 } else {
1391 cb = r->containingBlock();
1392 Q_ASSERT(cb->isRenderBlock());
1393 }/*end if*/
1394 InlineFlowBox *flowBox = cb->firstLineBox();
1395#if DEBUG_CARETMODE > 0
1396 kdDebug(6200) << "++: flowBox " << flowBox << " cb " << cb << "[" << (cb?cb->renderName()+TQString(".node ")+TQString::number((unsigned)cb->element(),16)+(cb->element()?"@"+cb->element()->nodeName().string():TQString::null):TQString::null) << "]" << endl;
1397#endif
1398 Q_ASSERT(flowBox);
1399 if (!flowBox) { // ### utter emergency (why is this possible at all?)
1400 cb_outside = cb_outside_end = true;
1401 cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
1402 cb_outside, cb_outside_end, currentBox);
1403 return;
1404 }
1405
1406 bool seekOutside = false, seekOutsideEnd = false; // silence gcc uninit warning
1407 CaretBoxIterator it;
1408 cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
1409 flowBox, flowBox->firstChild(), seekOutside, seekOutsideEnd, it);
1410 }
1411}
1412
1413void LineIterator::prevBlock()
1414{
1415 RenderObject *base = lines->baseObject();
1416
1417 bool cb_outside = cbl->isOutside();
1418 bool cb_outside_end = cbl->isOutsideEnd();
1419
1420 {
1421 RenderObject *r = cbl->enclosingObject();
1422 if (r->isAnonymous() && !cb_outside)
1423 cb_outside = true, cb_outside_end = false;
1424
1425 ObjectTraversalState trav;
1426 int state; // not used
1427 mapRenderPosToTraversalState(cb_outside, cb_outside_end, true, trav);
1428#if DEBUG_CARETMODE > 1
1429 kdDebug(6200) << "prevBlock: before adv r" << r << " " << (r ? r->renderName() : TQString::null) << (r && r->isText() ? " contains \"" + TQString(((RenderText *)r)->str->s, TQMIN(((RenderText *)r)->str->l,15)) + "\"" : TQString::null) << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end << endl;
1430#endif
1431 r = advanceSuitableObject(r, trav, true, base, state);
1432 if (!r) {
1433 cbl = 0;
1434 return;
1435 }/*end if*/
1436
1437 mapTraversalStateToRenderPos(trav, true, cb_outside, cb_outside_end);
1438#if DEBUG_CARETMODE > 1
1439 kdDebug(6200) << "prevBlock: after r" << r << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end << endl;
1440#endif
1441#if DEBUG_CARETMODE > 0
1442 kdDebug(6200) << "--: r " << r << "[" << (r?r->renderName():TQString::null) << "]" << endl;
1443#endif
1444
1445 RenderBlock *cb;
1446
1447 // If we hit a block, use this as its enclosing object
1448 bool isrepl = isBlockRenderReplaced(r);
1449// kdDebug(6200) << "isrepl " << isrepl << " isblock " << r->isRenderBlock() << endl;
1450 if (r->isRenderBlock() || isrepl) {
1451 RenderBox *cb = static_cast<RenderBox *>(r);
1452
1453 cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
1454 cb_outside, cb_outside_end, currentBox);
1455
1456#if DEBUG_CARETMODE > 0
1457 kdDebug(6200) << "r->isFlow is cb. continuation @" << cb->continuation() << endl;
1458#endif
1459 return;
1460 } else {
1461 cb = r->containingBlock();
1462 Q_ASSERT(cb->isRenderBlock());
1463 }/*end if*/
1464 InlineFlowBox *flowBox = cb->lastLineBox();
1465#if DEBUG_CARETMODE > 0
1466 kdDebug(6200) << "--: flowBox " << flowBox << " cb " << cb << "[" << (cb?cb->renderName()+TQString(".node ")+TQString::number((unsigned)cb->element(),16)+(cb->element()?"@"+cb->element()->nodeName().string():TQString::null):TQString::null) << "]" << endl;
1467#endif
1468 Q_ASSERT(flowBox);
1469 if (!flowBox) { // ### utter emergency (why is this possible at all?)
1470 cb_outside = true; cb_outside_end = false;
1471 cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
1472 cb_outside, cb_outside_end, currentBox);
1473 return;
1474 }
1475
1476 bool seekOutside = false, seekOutsideEnd = false; // silence gcc uninit warning
1477 CaretBoxIterator it;
1478 cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
1479 flowBox, flowBox->firstChild(), seekOutside, seekOutsideEnd, it);
1480 }
1481}
1482
1483void LineIterator::advance(bool toBegin)
1484{
1485 InlineFlowBox *flowBox = cbl->baseFlowBox();
1486 if (flowBox) {
1487 flowBox = static_cast<InlineFlowBox *>(toBegin ? flowBox->prevLineBox() : flowBox->nextLineBox());
1488 if (flowBox) {
1489 bool seekOutside = false, seekOutsideEnd = false; // silence gcc uninit warning
1490 CaretBoxIterator it;
1491 cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
1492 flowBox, flowBox->firstChild(), seekOutside, seekOutsideEnd, it);
1493 }/*end if*/
1494 }/*end if*/
1495
1496 // if there are no more lines in this block, move towards block to come
1497 if (!flowBox) { if (toBegin) prevBlock(); else nextBlock(); }
1498
1499#if DEBUG_CARETMODE > 3
1500 if (cbl) kdDebug(6200) << cbl->information() << endl;
1501#endif
1502}
1503
1504// == class EditableCaretBoxIterator implementation
1505
1506void EditableCaretBoxIterator::advance(bool toBegin)
1507{
1508#if DEBUG_CARETMODE > 3
1509 kdDebug(6200) << "---------------" << k_funcinfo << "toBegin " << toBegin << endl;
1510#endif
1511 const CaretBoxIterator preBegin = cbl->preBegin();
1512 const CaretBoxIterator end = cbl->end();
1513
1514 CaretBoxIterator lastbox = *this, curbox;
1515 bool islastuseable = true; // silence gcc
1516 bool iscuruseable;
1517 // Assume adjacency of caret boxes. Will be falsified later if applicable.
1518 adjacent = true;
1519
1520#if DEBUG_CARETMODE > 4
1521// kdDebug(6200) << "ebit::advance: before: " << (**this)->object() << "@" << (**this)->object()->renderName() << ".node " << (**this)->object()->element() << "[" << ((**this)->object()->element() ? (**this)->object()->element()->nodeName().string() : TQString::null) << "] inline " << (**this)->isInline() << " outside " << (**this)->isOutside() << " outsideEnd " << (**this)->isOutsideEnd() << endl;
1522#endif
1523
1524 if (toBegin) CaretBoxIterator::operator --(); else CaretBoxIterator::operator ++();
1525 bool curAtEnd = *this == preBegin || *this == end;
1526 curbox = *this;
1527 bool atEnd = true;
1528 if (!curAtEnd) {
1529 iscuruseable = isEditable(curbox, toBegin);
1530 if (toBegin) CaretBoxIterator::operator --(); else CaretBoxIterator::operator ++();
1531 atEnd = *this == preBegin || *this == end;
1532 }
1533 while (!curAtEnd) {
1534 bool haslast = lastbox != end && lastbox != preBegin;
1535 bool hascoming = !atEnd;
1536 bool iscominguseable = true; // silence gcc
1537
1538 if (!atEnd) iscominguseable = isEditable(*this, toBegin);
1539 if (iscuruseable) {
1540#if DEBUG_CARETMODE > 3
1541 kdDebug(6200) << "ebit::advance: " << (*curbox)->object() << "@" << (*curbox)->object()->renderName() << ".node " << (*curbox)->object()->element() << "[" << ((*curbox)->object()->element() ? (*curbox)->object()->element()->nodeName().string() : TQString::null) << "] inline " << (*curbox)->isInline() << " outside " << (*curbox)->isOutside() << " outsideEnd " << (*curbox)->isOutsideEnd() << endl;
1542#endif
1543
1544 CaretBox *box = *curbox;
1545 if (box->isOutside()) {
1546 // if this caret box represents no inline box, it is an outside box
1547 // which has to be considered unconditionally
1548 if (!box->isInline()) break;
1549
1550 if (advpol == VisibleFlows) break;
1551
1552 // IndicatedFlows and LeafsOnly are treated equally in caret box lines
1553
1554 InlineBox *ibox = box->inlineBox();
1555 // get previous inline box
1556 InlineBox *prev = box->isOutsideEnd() ? ibox : ibox->prevOnLine();
1557 // get next inline box
1558 InlineBox *next = box->isOutsideEnd() ? ibox->nextOnLine() : ibox;
1559
1560 const bool isprevindicated = !prev || isIndicatedInlineBox(prev);
1561 const bool isnextindicated = !next || isIndicatedInlineBox(next);
1562 const bool last = haslast && !islastuseable;
1563 const bool coming = hascoming && !iscominguseable;
1564 const bool left = !prev || prev->isInlineFlowBox() && isprevindicated
1565 || (toBegin && coming || !toBegin && last);
1566 const bool right = !next || next->isInlineFlowBox() && isnextindicated
1567 || (!toBegin && coming || toBegin && last);
1568 const bool text2indicated = toBegin && next && next->isInlineTextBox()
1569 && isprevindicated
1570 || !toBegin && prev && prev->isInlineTextBox() && isnextindicated;
1571 const bool indicated2text = !toBegin && next && next->isInlineTextBox()
1572 && prev && isprevindicated
1573 // ### this code is so broken.
1574 /*|| toBegin && prev && prev->isInlineTextBox() && isnextindicated*/;
1575#if DEBUG_CARETMODE > 5
1576 kdDebug(6200) << "prev " << prev << " haslast " << haslast << " islastuseable " << islastuseable << " left " << left << " next " << next << " hascoming " << hascoming << " iscominguseable " << iscominguseable << " right " << right << " text2indicated " << text2indicated << " indicated2text " << indicated2text << endl;
1577#endif
1578
1579 if (left && right && !text2indicated || indicated2text) {
1580 adjacent = false;
1581#if DEBUG_CARETMODE > 4
1582 kdDebug(6200) << "left && right && !text2indicated || indicated2text" << endl;
1583#endif
1584 break;
1585 }
1586
1587 } else {
1588 // inside boxes are *always* valid
1589#if DEBUG_CARETMODE > 4
1590if (box->isInline()) {
1591 InlineBox *ibox = box->inlineBox();
1592 kdDebug(6200) << "inside " << (!ibox->isInlineFlowBox() || static_cast<InlineFlowBox *>(ibox)->firstChild() ? "non-empty" : "empty") << (isIndicatedInlineBox(ibox) ? " indicated" : "") << " adjacent=" << adjacent << endl;
1593 }
1594#if 0
1595 RenderStyle *s = ibox->object()->style();
1596 kdDebug(6200) << "bordls " << s->borderLeftStyle()
1597 << " bordl " << (s->borderLeftStyle() != BNONE)
1598 << " bordr " << (s->borderRightStyle() != BNONE)
1599 << " bordt " << (s->borderTopStyle() != BNONE)
1600 << " bordb " << (s->borderBottomStyle() != BNONE)
1601 << " padl " << s->paddingLeft().value()
1602 << " padr " << s->paddingRight().value()
1603 << " padt " << s->paddingTop().value()
1604 << " padb " << s->paddingBottom().value()
1605 // ### Can inline elements have top/bottom margins? Couldn't find
1606 // it in the CSS 2 spec, but Mozilla ignores them, so we do, too.
1607 << " marl " << s->marginLeft().value()
1608 << " marr " << s->marginRight().value()
1609 << endl;
1610#endif
1611#endif
1612 break;
1613 }/*end if*/
1614
1615 } else {
1616
1617 if (!(*curbox)->isOutside()) {
1618 // cannot be adjacent anymore
1619 adjacent = false;
1620 }
1621
1622 }/*end if*/
1623 lastbox = curbox;
1624 islastuseable = iscuruseable;
1625 curbox = *this;
1626 iscuruseable = iscominguseable;
1627 curAtEnd = atEnd;
1628 if (!atEnd) {
1629 if (toBegin) CaretBoxIterator::operator --(); else CaretBoxIterator::operator ++();
1630 atEnd = *this == preBegin || *this == end;
1631 }/*end if*/
1632 }/*wend*/
1633
1634 *static_cast<CaretBoxIterator *>(this) = curbox;
1635#if DEBUG_CARETMODE > 4
1636// kdDebug(6200) << "still valid? " << (*this != preBegin && *this != end) << endl;
1637#endif
1638#if DEBUG_CARETMODE > 3
1639 kdDebug(6200) << "---------------" << k_funcinfo << "end " << endl;
1640#endif
1641}
1642
1643bool EditableCaretBoxIterator::isEditable(const CaretBoxIterator &boxit, bool fromEnd)
1644{
1645 Q_ASSERT(boxit != cbl->end() && boxit != cbl->preBegin());
1646 CaretBox *b = *boxit;
1647 RenderObject *r = b->object();
1648#if DEBUG_CARETMODE > 0
1649// if (b->isInlineFlowBox()) kdDebug(6200) << "b is inline flow box" << (outside ? " (outside)" : "") << endl;
1650 kdDebug(6200) << "isEditable r" << r << ": " << (r ? r->renderName() : TQString::null) << (r && r->isText() ? " contains \"" + TQString(((RenderText *)r)->str->s, TQMIN(((RenderText *)r)->str->l,15)) + "\"" : TQString::null) << endl;
1651#endif
1652 // Must check caret mode or design mode *after* r->element(), otherwise
1653 // lines without a backing DOM node get regarded, leading to a crash.
1654 // ### check should actually be in InlineBoxIterator
1655 NodeImpl *node = r->element();
1656 ObjectTraversalState trav;
1657 mapRenderPosToTraversalState(b->isOutside(), b->isOutsideEnd(), fromEnd, trav);
1658 if (isUnsuitable(r, trav) || !node) {
1659 return false;
1660 }
1661
1662 // generally exclude replaced elements with no children from navigation
1663 if (!b->isOutside() && r->isRenderReplaced() && !r->firstChild())
1664 return false;
1665
1666 RenderObject *eff_r = r;
1667 bool globallyNavigable = m_part->isCaretMode() || m_part->isEditable();
1668
1669 // calculate the parent element's editability if this inline box is outside.
1670 if (b->isOutside() && !globallyNavigable) {
1671 NodeImpl *par = node->parent();
1672 // I wonder whether par can be 0. It shouldn't be possible if the
1673 // algorithm contained no bugs.
1674 Q_ASSERT(par);
1675 if (par) node = par;
1676 eff_r = node->renderer();
1677 Q_ASSERT(eff_r); // this is a hard requirement
1678 }
1679
1680 bool result = globallyNavigable || eff_r->style()->userInput() == UI_ENABLED;
1681#if DEBUG_CARETMODE > 0
1682 kdDebug(6200) << result << endl;
1683#endif
1684 return result;
1685}
1686
1687// == class EditableLineIterator implementation
1688
1689void EditableLineIterator::advance(bool toBegin)
1690{
1691 CaretAdvancePolicy advpol = lines->advancePolicy();
1692 LineIterator lasteditable, lastindicated;
1693 bool haslasteditable = false;
1694 bool haslastindicated = false;
1695 bool uselasteditable = false;
1696
1697 LineIterator::advance(toBegin);
1698 while (cbl) {
1699 if (isEditable(*this)) {
1700#if DEBUG_CARETMODE > 3
1701 kdDebug(6200) << "advance: " << cbl->enclosingObject() << "@" << cbl->enclosingObject()->renderName() << ".node " << cbl->enclosingObject()->element() << "[" << (cbl->enclosingObject()->element() ? cbl->enclosingObject()->element()->nodeName().string() : TQString::null) << "]" << endl;
1702#endif
1703
1704 bool hasindicated = isIndicatedFlow(cbl->enclosingObject());
1705 if (hasindicated) {
1706 haslastindicated = true;
1707 lastindicated = *this;
1708 }
1709
1710 switch (advpol) {
1711 case IndicatedFlows:
1712 if (hasindicated) goto wend;
1713 // fall through
1714 case LeafsOnly:
1715 if (cbl->isOutside()) break;
1716 // fall through
1717 case VisibleFlows: goto wend;
1718 }/*end switch*/
1719
1720 // remember rejected editable element
1721 lasteditable = *this;
1722 haslasteditable = true;
1723#if DEBUG_CARETMODE > 4
1724 kdDebug(6200) << "remembered lasteditable " << *lasteditable << endl;
1725#endif
1726 } else {
1727
1728 // If this element isn't editable, but the last one was, and it was only
1729 // rejected because it didn't match the caret advance policy, force it.
1730 // Otherwise certain combinations of editable and uneditable elements
1731 // could never be reached with some policies.
1732 if (haslasteditable) { uselasteditable = true; break; }
1733
1734 }
1735 LineIterator::advance(toBegin);
1736 }/*wend*/
1737wend:
1738
1739 if (uselasteditable) *this = haslastindicated ? lastindicated : lasteditable;
1740 if (!cbl && haslastindicated) *this = lastindicated;
1741}
1742
1743// == class EditableCharacterIterator implementation
1744
1745void EditableCharacterIterator::initFirstChar()
1746{
1747 CaretBox *box = *ebit;
1748 InlineBox *b = box->inlineBox();
1749 if (_offset == box->maxOffset())
1750 peekNext();
1751 else if (b && !box->isOutside() && b->isInlineTextBox())
1752 _char = static_cast<RenderText *>(b->object())->str->s[_offset].unicode();
1753 else
1754 _char = -1;
1755}
1756
1760static inline bool isCaretBoxEmpty(CaretBox *box) {
1761 if (!box->isInline()) return false;
1762 InlineBox *ibox = box->inlineBox();
1763 return ibox->isInlineFlowBox()
1764 && !static_cast<InlineFlowBox *>(ibox)->firstChild()
1765 && !isIndicatedInlineBox(ibox);
1766}
1767
1768EditableCharacterIterator &EditableCharacterIterator::operator ++()
1769{
1770 _offset++;
1771
1772 CaretBox *box = *ebit;
1773 InlineBox *b = box->inlineBox();
1774 long maxofs = box->maxOffset();
1775#if DEBUG_CARETMODE > 0
1776 kdDebug(6200) << "box->maxOffset() " << box->maxOffset() << " box->minOffset() " << box->minOffset() << endl;
1777#endif
1778 if (_offset == maxofs) {
1779#if DEBUG_CARETMODE > 2
1780kdDebug(6200) << "_offset == maxofs: " << _offset << " == " << maxofs << endl;
1781#endif
1782 peekNext();
1783 } else if (_offset > maxofs) {
1784#if DEBUG_CARETMODE > 2
1785kdDebug(6200) << "_offset > maxofs: " << _offset << " > " << maxofs /*<< " _peekNext: " << _peekNext*/ << endl;
1786#endif
1787 if (true) {
1788 ++ebit;
1789 if (ebit == (*_it)->end()) { // end of line reached, go to next line
1790 ++_it;
1791#if DEBUG_CARETMODE > 3
1792kdDebug(6200) << "++_it" << endl;
1793#endif
1794 if (_it != _it.lines->end()) {
1795 ebit = _it;
1796 box = *ebit;
1797 b = box->inlineBox();
1798#if DEBUG_CARETMODE > 3
1799kdDebug(6200) << "box " << box << " b " << b << " isText " << box->isInlineTextBox() << endl;
1800#endif
1801
1802#if DEBUG_CARETMODE > 3
1803 RenderObject *_r = box->object();
1804kdDebug(6200) << "_r " << _r << ":" << _r->element()->nodeName().string() << endl;
1805#endif
1806 _offset = box->minOffset();
1807#if DEBUG_CARETMODE > 3
1808kdDebug(6200) << "_offset " << _offset << endl;
1809#endif
1810 } else {
1811 b = 0;
1812 _end = true;
1813 }/*end if*/
1814 goto readchar;
1815 }/*end if*/
1816 }/*end if*/
1817
1818 bool adjacent = ebit.isAdjacent();
1819#if 0
1820 // Jump over element if this one is not a text node.
1821 if (adjacent && !(*ebit)->isInlineTextBox()) {
1822 EditableCaretBoxIterator copy = ebit;
1823 ++ebit;
1824 if (ebit != (*_it)->end() && (*ebit)->isInlineTextBox()
1825 /*&& (!(*ebit)->isInlineFlowBox()
1826 || static_cast<InlineFlowBox *>(*ebit)->)*/)
1827 adjacent = false;
1828 else ebit = copy;
1829 }/*end if*/
1830#endif
1831 // Jump over empty elements.
1832 if (adjacent && !(*ebit)->isInlineTextBox()) {
1833 bool noemptybox = true;
1834 while (isCaretBoxEmpty(*ebit)) {
1835 noemptybox = false;
1836 EditableCaretBoxIterator copy = ebit;
1837 ++ebit;
1838 if (ebit == (*_it)->end()) { ebit = copy; break; }
1839 }
1840 if (noemptybox) adjacent = false;
1841 }/*end if*/
1842// _r = (*ebit)->object();
1843 /*if (!_it.outside) */_offset = (*ebit)->minOffset() + adjacent;
1844 //_peekNext = 0;
1845 box = *ebit;
1846 b = box->inlineBox();
1847 goto readchar;
1848 } else {
1849readchar:
1850 // get character
1851 if (b && !box->isOutside() && b->isInlineTextBox() && _offset < b->maxOffset())
1852 _char = static_cast<RenderText *>(b->object())->str->s[_offset].unicode();
1853 else
1854 _char = -1;
1855 }/*end if*/
1856#if DEBUG_CARETMODE > 2
1857kdDebug(6200) << "_offset: " << _offset /*<< " _peekNext: " << _peekNext*/ << " char '" << (char)_char << "'" << endl;
1858#endif
1859
1860#if DEBUG_CARETMODE > 0
1861 if (!_end && ebit != (*_it)->end()) {
1862 CaretBox *box = *ebit;
1863 RenderObject *_r = box->object();
1864 kdDebug(6200) << "echit++(1): box " << box << (box && box->isInlineTextBox() ? TQString(" contains \"%1\"").arg(TQConstString(static_cast<RenderText *>(box->object())->str->s+box->minOffset(), box->maxOffset() - box->minOffset()).string()) : TQString::null) << " _r " << (_r ? _r->element()->nodeName().string() : TQString("<nil>")) << endl;
1865 }
1866#endif
1867 return *this;
1868}
1869
1870EditableCharacterIterator &EditableCharacterIterator::operator --()
1871{
1872 _offset--;
1873 //kdDebug(6200) << "--: _offset=" << _offset << endl;
1874
1875 CaretBox *box = *ebit;
1876 CaretBox *_peekPrev = 0;
1877 CaretBox *_peekNext = 0;
1878 InlineBox *b = box->inlineBox();
1879 long minofs = box->minOffset();
1880#if DEBUG_CARETMODE > 0
1881 kdDebug(6200) << "box->maxOffset() " << box->maxOffset() << " box->minOffset() " << box->minOffset() << endl;
1882#endif
1883 if (_offset == minofs) {
1884#if DEBUG_CARETMODE > 2
1885kdDebug(6200) << "_offset == minofs: " << _offset << " == " << minofs << endl;
1886#endif
1887// _peekNext = b;
1888 // get character
1889 if (b && !box->isOutside() && b->isInlineTextBox())
1890 _char = static_cast<RenderText *>(b->object())->text()[_offset].unicode();
1891 else
1892 _char = -1;
1893
1894 //peekPrev();
1895 bool do_prev = false;
1896 {
1897 EditableCaretBoxIterator copy;
1898 _peekPrev = 0;
1899 do {
1900 copy = ebit;
1901 --ebit;
1902 if (ebit == (*_it)->preBegin()) { ebit = copy; break; }
1903 } while (isCaretBoxEmpty(*ebit));
1904 // Jump to end of previous element if it's adjacent, and a text box
1905 if (ebit.isAdjacent() && ebit != (*_it)->preBegin() && (*ebit)->isInlineTextBox()) {
1906 _peekPrev = *ebit;
1907 do_prev = true;
1908 } else
1909 ebit = copy;
1910 }
1911 if (do_prev) goto prev;
1912 } else if (_offset < minofs) {
1913prev:
1914#if DEBUG_CARETMODE > 2
1915kdDebug(6200) << "_offset < minofs: " << _offset << " < " << minofs /*<< " _peekNext: " << _peekNext*/ << endl;
1916#endif
1917 if (!_peekPrev) {
1918 _peekNext = *ebit;
1919 --ebit;
1920 if (ebit == (*_it)->preBegin()) { // end of line reached, go to previous line
1921 --_it;
1922#if DEBUG_CARETMODE > 3
1923kdDebug(6200) << "--_it" << endl;
1924#endif
1925 if (_it != _it.lines->preBegin()) {
1926// kdDebug(6200) << "begin from end!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << endl;
1927 ebit = EditableCaretBoxIterator(_it, true);
1928 box = *ebit;
1929// RenderObject *r = box->object();
1930#if DEBUG_CARETMODE > 3
1931kdDebug(6200) << "box " << box << " b " << box->inlineBox() << " isText " << box->isInlineTextBox() << endl;
1932#endif
1933 _offset = box->maxOffset();
1934// if (!_it.outside) _offset = r->isBR() ? (*ebit)->minOffset() : (*ebit)->maxOffset();
1935 _char = -1;
1936#if DEBUG_CARETMODE > 0
1937 kdDebug(6200) << "echit--(2): box " << box << " b " << box->inlineBox() << (box->isInlineTextBox() ? TQString(" contains \"%1\"").arg(TQConstString(static_cast<RenderText *>(box->object())->str->s+box->minOffset(), box->maxOffset() - box->minOffset()).string()) : TQString::null) << endl;
1938#endif
1939 } else
1940 _end = true;
1941 return *this;
1942 }/*end if*/
1943 }/*end if*/
1944
1945#if DEBUG_CARETMODE > 0
1946 bool adjacent = ebit.isAdjacent();
1947 kdDebug(6200) << "adjacent " << adjacent << " _peekNext " << _peekNext << " _peekNext->isInlineTextBox: " << (_peekNext ? _peekNext->isInlineTextBox() : false) << " !((*ebit)->isInlineTextBox): " << (*ebit ? !(*ebit)->isInlineTextBox() : true) << endl;
1948#endif
1949#if 0
1950 // Ignore this box if it isn't a text box, but the previous box was
1951 if (adjacent && _peekNext && _peekNext->isInlineTextBox()
1952 && !(*ebit)->isInlineTextBox()) {
1953 EditableCaretBoxIterator copy = ebit;
1954 --ebit;
1955 if (ebit == (*_it)->preBegin())
1956 /*adjacent = false;
1957 else */
1958 ebit = copy;
1959 }/*end if*/
1960#endif
1961#if 0
1962 // Jump over empty elements.
1963 if (adjacent //&& _peekNext && _peekNext->isInlineTextBox()
1964 && !(*ebit)->isInlineTextBox()) {
1965 bool noemptybox = true;
1966 while (isCaretBoxEmpty(*ebit)) {
1967 noemptybox = false;
1968 EditableCaretBoxIterator copy = ebit;
1969 --ebit;
1970 if (ebit == (*_it)->preBegin()) { ebit = copy; break; }
1971 else _peekNext = *copy;
1972 }
1973 if (noemptybox) adjacent = false;
1974 }/*end if*/
1975#endif
1976#if DEBUG_CARETMODE > 0
1977 kdDebug(6200) << "(*ebit)->obj " << (*ebit)->object()->renderName() << "[" << (*ebit)->object() << "]" << " minOffset: " << (*ebit)->minOffset() << " maxOffset: " << (*ebit)->maxOffset() << endl;
1978#endif
1979#if DEBUG_CARETMODE > 3
1980 RenderObject *_r = (*ebit)->object();
1981kdDebug(6200) << "_r " << _r << ":" << _r->element()->nodeName().string() << endl;
1982#endif
1983 _offset = (*ebit)->maxOffset();
1984// if (!_it.outside) _offset = (*ebit)->maxOffset()/* - adjacent*/;
1985#if DEBUG_CARETMODE > 3
1986kdDebug(6200) << "_offset " << _offset << endl;
1987#endif
1988 _peekPrev = 0;
1989 } else {
1990#if DEBUG_CARETMODE > 0
1991kdDebug(6200) << "_offset: " << _offset << " _peekNext: " << _peekNext << endl;
1992#endif
1993 // get character
1994 if (_peekNext && _offset >= box->maxOffset() && _peekNext->isInlineTextBox())
1995 _char = static_cast<RenderText *>(_peekNext->object())->text()[_peekNext->minOffset()].unicode();
1996 else if (b && _offset < b->maxOffset() && b->isInlineTextBox())
1997 _char = static_cast<RenderText *>(b->object())->text()[_offset].unicode();
1998 else
1999 _char = -1;
2000 }/*end if*/
2001
2002#if DEBUG_CARETMODE > 0
2003 if (!_end && ebit != (*_it)->preBegin()) {
2004 CaretBox *box = *ebit;
2005 kdDebug(6200) << "echit--(1): box " << box << " b " << box->inlineBox() << (box->isInlineTextBox() ? TQString(" contains \"%1\"").arg(TQConstString(static_cast<RenderText *>(box->object())->str->s+box->minOffset(), box->maxOffset() - box->minOffset()).string()) : TQString::null) << endl;
2006 }
2007#endif
2008 return *this;
2009}
2010
2011// == class TableRowIterator implementation
2012
2013TableRowIterator::TableRowIterator(RenderTable *table, bool fromEnd,
2014 RenderTableSection::RowStruct *row)
2015 : sec(table, fromEnd)
2016{
2017 // set index
2018 if (*sec) {
2019 if (fromEnd) index = (*sec)->grid.size() - 1;
2020 else index = 0;
2021 }/*end if*/
2022
2023 // initialize with given row
2024 if (row && *sec) {
2025 while (operator *() != row)
2026 if (fromEnd) operator --(); else operator ++();
2027 }/*end if*/
2028}
2029
2030TableRowIterator &TableRowIterator::operator ++()
2031{
2032 index++;
2033
2034 if (index >= (int)(*sec)->grid.size()) {
2035 ++sec;
2036
2037 if (*sec) index = 0;
2038 }/*end if*/
2039 return *this;
2040}
2041
2042TableRowIterator &TableRowIterator::operator --()
2043{
2044 index--;
2045
2046 if (index < 0) {
2047 --sec;
2048
2049 if (*sec) index = (*sec)->grid.size() - 1;
2050 }/*end if*/
2051 return *this;
2052}
2053
2054// == class ErgonomicEditableLineIterator implementation
2055
2056// some decls
2057static RenderTableCell *findNearestTableCellInRow(TDEHTMLPart *part, int x,
2058 RenderTableSection::RowStruct *row, bool fromEnd);
2059
2073static inline RenderTableCell *findNearestTableCell(TDEHTMLPart *part, int x,
2074 TableRowIterator &it, bool fromEnd)
2075{
2076 RenderTableCell *result = 0;
2077
2078 while (*it) {
2079 result = findNearestTableCellInRow(part, x, *it, fromEnd);
2080 if (result) break;
2081
2082 if (fromEnd) --it; else ++it;
2083 }/*wend*/
2084
2085 return result;
2086}
2087
2101static RenderTableCell *findNearestTableCellInRow(TDEHTMLPart *part, int x,
2102 RenderTableSection::RowStruct *row, bool fromEnd)
2103{
2104 // First pass. Find spatially nearest cell.
2105 int n = (int)row->row->size();
2106 int i;
2107 for (i = 0; i < n; i++) {
2108 RenderTableCell *cell = row->row->at(i);
2109 if (!cell || (long)cell == -1) continue;
2110
2111 int absx, absy;
2112 cell->absolutePosition(absx, absy, false); // ### position: fixed?
2113#if DEBUG_CARETMODE > 1
2114 kdDebug(6201) << "i/n " << i << "/" << n << " absx " << absx << " absy " << absy << endl;
2115#endif
2116
2117 // I rely on the assumption that all cells are in ascending visual order
2118 // ### maybe this assumption is wrong for bidi?
2119#if DEBUG_CARETMODE > 1
2120 kdDebug(6201) << "x " << x << " < " << (absx + cell->width()) << "?" << endl;
2121#endif
2122 if (x < absx + cell->width()) break;
2123 }/*next i*/
2124 if (i >= n) i = n - 1;
2125
2126 // Second pass. Find editable cell, beginning with the currently found,
2127 // extending to the left, and to the right, alternating.
2128 for (int cnt = 0; cnt < 2*n; cnt++) {
2129 int index = i - ((cnt >> 1) + 1)*(cnt & 1) + (cnt >> 1)*!(cnt & 1);
2130 if (index < 0 || index >= n) continue;
2131
2132 RenderTableCell *cell = row->row->at(index);
2133 if (!cell || (long)cell == -1) continue;
2134
2135#if DEBUG_CARETMODE > 1
2136 kdDebug(6201) << "index " << index << " cell " << cell << endl;
2137#endif
2138 RenderTable *nestedTable;
2139 if (containsEditableElement(part, cell, nestedTable, fromEnd)) {
2140
2141 if (nestedTable) {
2142 TableRowIterator it(nestedTable, fromEnd);
2143 while (*it) {
2144// kdDebug(6201) << "=== recursive invocation" << endl;
2145 cell = findNearestTableCell(part, x, it, fromEnd);
2146 if (cell) break;
2147 if (fromEnd) --it; else ++it;
2148 }/*wend*/
2149 }/*end if*/
2150
2151 return cell;
2152 }/*end if*/
2153 }/*next i*/
2154 return 0;
2155}
2156
2163static RenderObject *commonAncestorTableSectionOrCell(RenderObject *r1,
2164 RenderObject *r2)
2165{
2166 if (!r1 || !r2) return 0;
2167 RenderTableSection *sec = 0;
2168 int start_depth=0, end_depth=0;
2169 // First we find the depths of the two objects in the tree (start_depth, end_depth)
2170 RenderObject *n = r1;
2171 while (n->parent()) {
2172 n = n->parent();
2173 start_depth++;
2174 }/*wend*/
2175 n = r2;
2176 while( n->parent()) {
2177 n = n->parent();
2178 end_depth++;
2179 }/*wend*/
2180 // here we climb up the tree with the deeper object, until both objects have equal depth
2181 while (end_depth > start_depth) {
2182 r2 = r2->parent();
2183 end_depth--;
2184 }/*wend*/
2185 while (start_depth > end_depth) {
2186 r1 = r1->parent();
2187// if (r1->isTableSection()) sec = static_cast<RenderTableSection *>(r1);
2188 start_depth--;
2189 }/*wend*/
2190 // Climb the tree with both r1 and r2 until they are the same
2191 while (r1 != r2){
2192 r1 = r1->parent();
2193 if (r1->isTableSection()) sec = static_cast<RenderTableSection *>(r1);
2194 r2 = r2->parent();
2195 }/*wend*/
2196
2197 // At this point, we found the most approximate common ancestor. Now climb
2198 // up until the condition of the function return value is satisfied.
2199 while (r1 && !r1->isTableCell() && !r1->isTableSection() && !r1->isTable())
2200 r1 = r1->parent();
2201
2202 return r1 && r1->isTable() ? sec : r1;
2203}
2204
2212static int findRowInSection(RenderTableSection *section, RenderTableCell *cell,
2213 RenderTableSection::RowStruct *&row, RenderTableCell *&directCell)
2214{
2215 // Seek direct cell
2216 RenderObject *r = cell;
2217 while (r != section) {
2218 if (r->isTableCell()) directCell = static_cast<RenderTableCell *>(r);
2219 r = r->parent();
2220 }/*wend*/
2221
2222 // So, and this is really nasty: As we have no indices, we have to do a
2223 // linear comparison. Oh, that sucks so much for long tables, you can't
2224 // imagine.
2225 int n = section->numRows();
2226 for (int i = 0; i < n; i++) {
2227 row = &section->grid[i];
2228
2229 // check for cell
2230 int m = row->row->size();
2231 for (int j = 0; j < m; j++) {
2232 RenderTableCell *c = row->row->at(j);
2233 if (c == directCell) return i;
2234 }/*next j*/
2235
2236 }/*next i*/
2237 Q_ASSERT(false);
2238 return -1;
2239}
2240
2246static inline RenderTable *findFirstDescendantTable(RenderObject *leaf, RenderBlock *block)
2247{
2248 RenderTable *result = 0;
2249 while (leaf && leaf != block) {
2250 if (leaf->isTable()) result = static_cast<RenderTable *>(leaf);
2251 leaf = leaf->parent();
2252 }/*wend*/
2253 return result;
2254}
2255
2259static inline RenderTableCell *containingTableCell(RenderObject *r)
2260{
2261 while (r && !r->isTableCell()) r = r->parent();
2262 return static_cast<RenderTableCell *>(r);
2263}
2264
2265inline void ErgonomicEditableLineIterator::calcAndStoreNewLine(
2266 RenderBlock *newBlock, bool toBegin)
2267{
2268 // take the first/last editable element in the found cell as the new
2269 // value for the iterator
2270 CaretBoxIterator it;
2271 cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
2272 newBlock, true, toBegin, it);
2273#if DEBUG_CARETMODE > 3
2274 kdDebug(6201) << cbl->information() << endl;
2275#endif
2276// if (toBegin) prevBlock(); else nextBlock();
2277
2278 if (!cbl) {
2279 return;
2280 }/*end if*/
2281
2282 EditableLineIterator::advance(toBegin);
2283}
2284
2285void ErgonomicEditableLineIterator::determineTopologicalElement(
2286 RenderTableCell *oldCell, RenderObject *newObject, bool toBegin)
2287{
2288 // When we arrive here, a transition between cells has happened.
2289 // Now determine the type of the transition. This can be
2290 // (1) a transition from this cell into a table inside this cell.
2291 // (2) a transition from this cell into another cell of this table
2292
2293 TableRowIterator it;
2294
2295 RenderObject *commonAncestor = commonAncestorTableSectionOrCell(oldCell, newObject);
2296#if DEBUG_CARETMODE > 1
2297 kdDebug(6201) << " ancestor " << commonAncestor << endl;
2298#endif
2299
2300 // The whole document is treated as a table cell.
2301 if (!commonAncestor || commonAncestor->isTableCell()) { // (1)
2302
2303 RenderTableCell *cell = static_cast<RenderTableCell *>(commonAncestor);
2304 RenderTable *table = findFirstDescendantTable(newObject, cell);
2305
2306#if DEBUG_CARETMODE > 0
2307 kdDebug(6201) << "table cell: " << cell << endl;
2308#endif
2309
2310 // if there is no table, we fell out of the previous table, and are now
2311 // in some table-less block. Therefore, done.
2312 if (!table) return;
2313
2314 it = TableRowIterator(table, toBegin);
2315
2316 } else if (commonAncestor->isTableSection()) { // (2)
2317
2318 RenderTableSection *section = static_cast<RenderTableSection *>(commonAncestor);
2319 RenderTableSection::RowStruct *row;
2320 int idx = findRowInSection(section, oldCell, row, oldCell);
2321#if DEBUG_CARETMODE > 1
2322 kdDebug(6201) << "table section: row idx " << idx << endl;
2323#endif
2324
2325 it = TableRowIterator(section, idx);
2326
2327 // advance rowspan rows
2328 int rowspan = oldCell->rowSpan();
2329 while (*it && rowspan--) {
2330 if (toBegin) --it; else ++it;
2331 }/*wend*/
2332
2333 } else {
2334 kdError(6201) << "Neither common cell nor section! " << commonAncestor->renderName() << endl;
2335 // will crash on uninitialized table row iterator
2336 }/*end if*/
2337
2338 RenderTableCell *cell = findNearestTableCell(lines->m_part, xCoor, it, toBegin);
2339#if DEBUG_CARETMODE > 1
2340 kdDebug(6201) << "findNearestTableCell result: " << cell << endl;
2341#endif
2342
2343 RenderBlock *newBlock = cell;
2344 if (!cell) {
2345 Q_ASSERT(commonAncestor->isTableSection());
2346 RenderTableSection *section = static_cast<RenderTableSection *>(commonAncestor);
2347 cell = containingTableCell(section);
2348#if DEBUG_CARETMODE > 1
2349 kdDebug(6201) << "containing cell: " << cell << endl;
2350#endif
2351
2352 RenderTable *nestedTable;
2353 bool editableChild = cell && containsEditableChildElement(lines->m_part,
2354 cell, nestedTable, toBegin, section->table());
2355
2356 if (cell && !editableChild) {
2357#if DEBUG_CARETMODE > 1
2358 kdDebug(6201) << "========= recursive invocation outer =========" << endl;
2359#endif
2360 determineTopologicalElement(cell, cell->section(), toBegin);
2361#if DEBUG_CARETMODE > 1
2362 kdDebug(6201) << "========= end recursive invocation outer =========" << endl;
2363#endif
2364 return;
2365
2366 } else if (cell && nestedTable) {
2367#if DEBUG_CARETMODE > 1
2368 kdDebug(6201) << "========= recursive invocation inner =========" << endl;
2369#endif
2370 determineTopologicalElement(cell, nestedTable, toBegin);
2371#if DEBUG_CARETMODE > 1
2372 kdDebug(6201) << "========= end recursive invocation inner =========" << endl;
2373#endif
2374 return;
2375
2376 } else {
2377#if DEBUG_CARETMODE > 1
2378 kdDebug(6201) << "newBlock is table: " << section->table() << endl;
2379#endif
2380 RenderObject *r = section->table();
2381 int state; // not used
2382 ObjectTraversalState trav = OutsideAscending;
2383 r = advanceSuitableObject(r, trav, toBegin, lines->baseObject(), state);
2384 if (!r) { cbl = 0; return; }
2385// if (toBegin) prevBlock(); else nextBlock();
2386 newBlock = static_cast<RenderBlock *>(!r || r->isRenderBlock() ? r : r->containingBlock());
2387 }/*end if*/
2388#if 0
2389 } else {
2390 // adapt cell so that prevBlock/nextBlock works as expected
2391 newBlock = cell;
2392 // on forward advancing, we must start from the outside end of the
2393 // previous object
2394 if (!toBegin) {
2395 RenderObject *r = newBlock;
2396 int state; // not used
2397 ObjectTraversalState trav = OutsideAscending;
2398 r = advanceSuitableObject(r, trav, true, lines->advancePolicy(), lines->baseObject(), state);
2399 newBlock = static_cast<RenderBlock *>(!r || r->isRenderBlock() ? r : r->containingBlock());
2400 }/*end if*/
2401#endif
2402 }/*end if*/
2403
2404 calcAndStoreNewLine(newBlock, toBegin);
2405}
2406
2407ErgonomicEditableLineIterator &ErgonomicEditableLineIterator::operator ++()
2408{
2409 RenderTableCell *oldCell = containingTableCell(cbl->enclosingObject());
2410
2411 EditableLineIterator::operator ++();
2412 if (*this == lines->end() || *this == lines->preBegin()) return *this;
2413
2414 RenderTableCell *newCell = containingTableCell(cbl->enclosingObject());
2415
2416 if (!newCell || newCell == oldCell) return *this;
2417
2418 determineTopologicalElement(oldCell, newCell, false);
2419
2420 return *this;
2421}
2422
2423ErgonomicEditableLineIterator &ErgonomicEditableLineIterator::operator --()
2424{
2425 RenderTableCell *oldCell = containingTableCell(cbl->enclosingObject());
2426
2427 EditableLineIterator::operator --();
2428 if (*this == lines->end() || *this == lines->preBegin()) return *this;
2429
2430 RenderTableCell *newCell = containingTableCell(cbl->enclosingObject());
2431
2432 if (!newCell || newCell == oldCell) return *this;
2433
2434 determineTopologicalElement(oldCell, newCell, true);
2435
2436 return *this;
2437}
2438
2439// == Navigational helper functions ==
2440
2450static CaretBox *nearestCaretBox(LineIterator &it, CaretViewContext *cv,
2451 int &x, int &absx, int &absy)
2452{
2453 // Find containing block
2454 RenderObject *cb = (*it)->containingBlock();
2455#if DEBUG_CARETMODE > 4
2456 kdDebug(6200) << "nearestCB: cb " << cb << "@" << (cb ? cb->renderName() : "") << endl;
2457#endif
2458
2459 if (cb) cb->absolutePosition(absx, absy);
2460 else absx = absy = 0;
2461
2462 // Otherwise find out in which inline box the caret is to be placed.
2463
2464 // this horizontal position is to be approximated
2465 x = cv->origX - absx;
2466 CaretBox *caretBox = 0; // Inline box containing the caret
2467// NodeImpl *lastnode = 0; // node of previously checked render object.
2468 int xPos; // x-coordinate of current inline box
2469 int oldXPos = -1; // x-coordinate of last inline box
2470 EditableCaretBoxIterator fbit = it;
2471#if DEBUG_CARETMODE > 0
2472/* if (it.linearDocument()->advancePolicy() != LeafsOnly)
2473 kdWarning() << "nearestInlineBox is only prepared to handle the LeafsOnly advance policy" << endl;*/
2474// kdDebug(6200) << "*fbit = " << *fbit << endl;
2475#endif
2476 // Iterate through all children
2477 for (CaretBox *b; fbit != (*it)->end(); ++fbit) {
2478 b = *fbit;
2479
2480#if DEBUG_CARETMODE > 0
2481// RenderObject *r = b->object();
2482// if (b->isInlineFlowBox()) kdDebug(6200) << "b is inline flow box" << endl;
2483// kdDebug(6200) << "approximate r" << r << ": " << (r ? r->renderName() : TQString::null) << (r && r->isText() ? " contains \"" + TQString(((RenderText *)r)->str->s, ((RenderText *)r)->str->l) + "\"" : TQString::null) << endl;
2484#endif
2485 xPos = b->xPos();
2486
2487 // the caret is before this box
2488 if (x < xPos) {
2489 // snap to nearest box
2490 if (oldXPos < 0 || x - (oldXPos + caretBox->width()) > xPos - x) {
2491 caretBox = b; // current box is nearer
2492 }/*end if*/
2493 break; // Otherwise, preceding box is implicitly used
2494 }
2495
2496 caretBox = b;
2497
2498 // the caret is within this box
2499 if (x >= xPos && x < xPos + caretBox->width())
2500 break;
2501 oldXPos = xPos;
2502
2503 // the caret can only be after the last box which is automatically
2504 // contained in caretBox when we fall out of the loop.
2505 }/*next fbit*/
2506
2507 return caretBox;
2508}
2509
2515static void moveItToNextWord(EditableCharacterIterator &it)
2516{
2517#if DEBUG_CARETMODE > 0
2518 kdDebug(6200) << "%%%%%%%%%%%%%%%%%%%%% moveItToNextWord" << endl;
2519#endif
2520 EditableCharacterIterator copy;
2521 while (!it.isEnd() && !(*it).isSpace() && !(*it).isPunct()) {
2522#if DEBUG_CARETMODE > 2
2523 kdDebug(6200) << "reading1 '" << (*it).latin1() << "'" << endl;
2524#endif
2525 copy = it;
2526 ++it;
2527 }
2528
2529 if (it.isEnd()) {
2530 it = copy;
2531 return;
2532 }/*end if*/
2533
2534 while (!it.isEnd() && ((*it).isSpace() || (*it).isPunct())) {
2535#if DEBUG_CARETMODE > 2
2536 kdDebug(6200) << "reading2 '" << (*it).latin1() << "'" << endl;
2537#endif
2538 copy = it;
2539 ++it;
2540 }
2541
2542 if (it.isEnd()) it = copy;
2543}
2544
2550static void moveItToPrevWord(EditableCharacterIterator &it)
2551{
2552 if (it.isEnd()) return;
2553
2554#if DEBUG_CARETMODE > 0
2555 kdDebug(6200) << "%%%%%%%%%%%%%%%%%%%%% moveItToPrevWord" << endl;
2556#endif
2557 EditableCharacterIterator copy;
2558
2559 // Jump over all space and punctuation characters first
2560 do {
2561 copy = it;
2562 --it;
2563#if DEBUG_CARETMODE > 2
2564 if (!it.isEnd()) kdDebug(6200) << "reading1 '" << (*it).latin1() << "'" << endl;
2565#endif
2566 } while (!it.isEnd() && ((*it).isSpace() || (*it).isPunct()));
2567
2568 if (it.isEnd()) {
2569 it = copy;
2570 return;
2571 }/*end if*/
2572
2573 do {
2574 copy = it;
2575 --it;
2576#if DEBUG_CARETMODE > 0
2577 if (!it.isEnd()) kdDebug(6200) << "reading2 '" << (*it).latin1() << "' (" << (int)(*it).latin1() << ") box " << it.caretBox() << endl;
2578#endif
2579 } while (!it.isEnd() && !(*it).isSpace() && !(*it).isPunct());
2580
2581 it = copy;
2582#if DEBUG_CARETMODE > 1
2583 if (!it.isEnd()) kdDebug(6200) << "effective '" << (*it).latin1() << "' (" << (int)(*it).latin1() << ") box " << it.caretBox() << endl;
2584#endif
2585}
2586
2594static void moveIteratorByPage(LinearDocument &ld,
2595 ErgonomicEditableLineIterator &it, int mindist, bool next)
2596{
2597 // ### This whole routine plainly sucks. Use an inverse strategie for pgup/pgdn.
2598
2599 if (it == ld.end() || it == ld.preBegin()) return;
2600
2601 ErgonomicEditableLineIterator copy = it;
2602#if DEBUG_CARETMODE > 0
2603 kdDebug(6200) << " mindist: " << mindist << endl;
2604#endif
2605
2606 CaretBoxLine *cbl = *copy;
2607 int absx = 0, absy = 0;
2608
2609 RenderBlock *lastcb = cbl->containingBlock();
2610 Q_ASSERT(lastcb->isRenderBlock());
2611 lastcb->absolutePosition(absx, absy, false); // ### what about fixed?
2612
2613 int lastfby = cbl->begin().data()->yPos();
2614 int lastheight = 0;
2615 int rescue = 1000; // ### this is a hack to keep stuck carets from hanging the ua
2616 do {
2617 if (next) ++copy; else --copy;
2618 if (copy == ld.end() || copy == ld.preBegin()) break;
2619
2620 cbl = *copy;
2621 RenderBlock *cb = cbl->containingBlock();
2622
2623 int diff = 0;
2624 // ### actually flowBox->yPos() should suffice, but this is not ported
2625 // over yet from WebCore
2626 int fby = cbl->begin().data()->yPos();
2627 if (cb != lastcb) {
2628 if (next) {
2629 diff = absy + lastfby + lastheight;
2630 cb->absolutePosition(absx, absy, false); // ### what about fixed?
2631 diff = absy - diff + fby;
2632 lastfby = 0;
2633 } else {
2634 diff = absy;
2635 cb->absolutePosition(absx, absy, false); // ### what about fixed?
2636 diff -= absy + fby + lastheight;
2637 lastfby = fby - lastheight;
2638 }/*end if*/
2639#if DEBUG_CARETMODE > 2
2640 kdDebug(6200) << "absdiff " << diff << endl;
2641#endif
2642 } else {
2643 diff = kAbs(fby - lastfby);
2644 }/*end if*/
2645#if DEBUG_CARETMODE > 2
2646 kdDebug(6200) << "cbl->begin().data()->yPos(): " << fby << " diff " << diff << endl;
2647#endif
2648
2649 mindist -= diff;
2650
2651 lastheight = kAbs(fby - lastfby);
2652 lastfby = fby;
2653 lastcb = cb;
2654 it = copy;
2655#if DEBUG_CARETMODE > 0
2656 kdDebug(6200) << " mindist: " << mindist << endl;
2657#endif
2658 // trick: actually the distance is always one line short, but we cannot
2659 // calculate the height of the first line (### WebCore will make it better)
2660 // Therefore, we simply approximate that excess line by using the last
2661 // caluculated line height.
2662 } while (mindist - lastheight > 0 && --rescue);
2663}
2664
2665
2666}/*end namespace*/
TDEHTMLPart
This class is tdehtml's main class.
Definition: tdehtml_part.h:184
TDEHTMLPart::isCaretMode
bool isCaretMode() const
Returns whether caret mode is on/off.
Definition: tdehtml_part.cpp:2662
TDEHTMLPart::isEditable
bool isEditable() const
Returns true if the document is editable, false otherwise.
Definition: tdehtml_part.cpp:2683
kdWarning
kdbgstream kdWarning(int area=0)
kdError
kdbgstream kdError(int area=0)
endl
kndbgstream & endl(kndbgstream &s)
kdDebug
kdbgstream kdDebug(int area=0)
TDEStdAccel::copy
const TDEShortcut & copy()
TDEStdAccel::next
const TDEShortcut & next()
TDEStdAccel::end
const TDEShortcut & end()

tdehtml

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

tdehtml

Skip menu "tdehtml"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdehtml by doxygen 1.9.4
This website is maintained by Timothy Pearson.