• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KHTML

khtml_caret.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002  *
00003  * Copyright (C) 2003-2004 Leo Savernik <l.savernik@aon.at>
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU Library General Public
00007  * License as published by the Free Software Foundation; either
00008  * version 2 of the License, or (at your option) any later version.
00009  *
00010  * This library is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Library General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Library General Public License
00016  * along with this library; see the file COPYING.LIB.  If not, write to
00017  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019  */
00020 
00021 
00022 #include "khtml_caret_p.h"
00023 
00024 #include "html/html_documentimpl.h"
00025 
00026 namespace khtml {
00027 
00035 enum ObjectAdvanceState {
00036   LeftObject = 0x01, AdvancedToSibling = 0x02, EnteredObject = 0x04
00037 };
00038 
00047 enum ObjectTraversalState {
00048   OutsideDescending, InsideDescending, InsideAscending, OutsideAscending
00049 };
00050 
00060 static RenderObject* traverseRenderObjects(RenderObject *obj,
00061         ObjectTraversalState &trav, bool toBegin, RenderObject *base,
00062                 int &state)
00063 {
00064   RenderObject *r;
00065   switch (trav) {
00066     case OutsideDescending:
00067       trav = InsideDescending;
00068       break;
00069     case InsideDescending:
00070       r = toBegin ? obj->lastChild() : obj->firstChild();
00071       if (r) {
00072         trav = OutsideDescending;
00073         obj = r;
00074         state |= EnteredObject;
00075       } else {
00076         trav = InsideAscending;
00077       }
00078       break;
00079     case InsideAscending:
00080       trav = OutsideAscending;
00081       break;
00082     case OutsideAscending:
00083       r = toBegin ? obj->previousSibling() : obj->nextSibling();
00084       if (r) {
00085         trav = OutsideDescending;
00086         state |= AdvancedToSibling;
00087       } else {
00088         r = obj->parent();
00089         if (r == base) r = 0;
00090         trav = InsideAscending;
00091         state |= LeftObject;
00092       }
00093       obj = r;
00094       break;
00095   }/*end switch*/
00096 
00097   return obj;
00098 }
00099 
00105 static inline RenderObject *renderObjectBelow(RenderObject *obj, ObjectTraversalState &trav, RenderObject *base)
00106 {
00107   trav = InsideDescending;
00108   int state;        // we don't need the state, so we don't initialize it
00109   RenderObject *r = obj;
00110   while (r && trav != OutsideDescending) {
00111     r = traverseRenderObjects(r, trav, false, base, state);
00112 #if DEBUG_CARETMODE > 3
00113     kdDebug(6200) << "renderObjectBelow: r " << r << " trav " << trav << endl;
00114 #endif
00115   }
00116   trav = InsideDescending;
00117   return r;
00118 }
00119 
00125 static inline RenderObject *renderObjectAbove(RenderObject *obj, ObjectTraversalState &trav, RenderObject *base)
00126 {
00127   trav = OutsideAscending;
00128   int state;        // we don't need the state, so we don't initialize it
00129   RenderObject *r = obj;
00130   while (r && trav != InsideAscending) {
00131     r = traverseRenderObjects(r, trav, true, base, state);
00132 #if DEBUG_CARETMODE > 3
00133     kdDebug(6200) << "renderObjectAbove: r " << r << " trav " << trav << endl;
00134 #endif
00135   }
00136   trav = InsideAscending;
00137   return r;
00138 }
00139 
00144 static inline bool isIndicatedInlineBox(InlineBox *box)
00145 {
00146   // text boxes are never indicated.
00147   if (box->isInlineTextBox()) return false;
00148   RenderStyle *s = box->object()->style();
00149   return s->borderLeftWidth() || s->borderRightWidth()
00150     || s->borderTopWidth() || s->borderBottomWidth()
00151     || s->paddingLeft().value() || s->paddingRight().value()
00152     || s->paddingTop().value() || s->paddingBottom().value()
00153     // ### Can inline elements have top/bottom margins? Couldn't find
00154     // it in the CSS 2 spec, but Mozilla ignores them, so we do, too.
00155     || s->marginLeft().value() || s->marginRight().value();
00156 }
00157 
00162 static inline bool isIndicatedFlow(RenderObject *r)
00163 {
00164   RenderStyle *s = r->style();
00165   return s->borderLeftStyle() != BNONE || s->borderRightStyle() != BNONE
00166     || s->borderTopStyle() != BNONE || s->borderBottomStyle() != BNONE
00167 //  || s->paddingLeft().value() || s->paddingRight().value()
00168 //  || s->paddingTop().value() || s->paddingBottom().value()
00169 //  || s->marginLeft().value() || s->marginRight().value()
00170     || s->hasClip() || s->hidesOverflow()
00171     || s->backgroundColor().isValid() || s->backgroundImage();
00172 }
00173 
00187 static RenderObject *advanceObject(RenderObject *r,
00188         ObjectTraversalState &trav, bool toBegin,
00189                 RenderObject *base, int &state)
00190 {
00191 
00192   ObjectTraversalState origtrav = trav;
00193   RenderObject *a = traverseRenderObjects(r, trav, toBegin, base, state);
00194 
00195   bool ignoreOutsideDesc = toBegin && origtrav == OutsideAscending;
00196 
00197   // render object and traversal state at which look ahead has been started
00198   RenderObject *la = 0;
00199   ObjectTraversalState latrav = trav;
00200   ObjectTraversalState lasttrav = origtrav;
00201 
00202   while (a) {
00203 #if DEBUG_CARETMODE > 5
00204 kdDebug(6200) << "a " << a << " trav " << trav << endl;
00205 #endif
00206     if (a->element()) {
00207 #if DEBUG_CARETMODE > 4
00208 kdDebug(6200) << "a " << a << " trav " << trav << " origtrav " << origtrav << " ignoreOD " << ignoreOutsideDesc << endl;
00209 #endif
00210       if (toBegin) {
00211 
00212         switch (origtrav) {
00213       case OutsideDescending:
00214             if (trav == InsideAscending) return a;
00215         if (trav == OutsideDescending) return a;
00216         break;
00217           case InsideDescending:
00218         if (trav == OutsideDescending) return a;
00219         // fall through
00220           case InsideAscending:
00221             if (trav == OutsideAscending) return a;
00222         break;
00223       case OutsideAscending:
00224         if (trav == OutsideAscending) return a;
00225             if (trav == InsideAscending && lasttrav == InsideDescending) return a;
00226         if (trav == OutsideDescending && !ignoreOutsideDesc) return a;
00227         // ignore this outside descending position, as it effectively
00228         // demarkates the same position, but remember it in case we fall off
00229         // the document.
00230         la = a; latrav = trav;
00231         ignoreOutsideDesc = false;
00232         break;
00233         }/*end switch*/
00234 
00235       } else {
00236 
00237         switch (origtrav) {
00238       case OutsideDescending:
00239             if (trav == InsideAscending) return a;
00240         if (trav == OutsideDescending) return a;
00241         break;
00242           case InsideDescending:
00243 //      if (trav == OutsideDescending) return a;
00244         // fall through
00245           case InsideAscending:
00246 //            if (trav == OutsideAscending) return a;
00247 //      break;
00248       case OutsideAscending:
00249         // ### what if origtrav == OA, and immediately afterwards trav
00250         // becomes OD? In this case the effective position hasn't changed ->
00251         // the caret gets stuck. Otherwise, it apparently cannot happen in
00252         // real usage patterns.
00253         if (trav == OutsideDescending) return a;
00254         if (trav == OutsideAscending) {
00255           if (la) return la;
00256               // starting lookahead here. Remember old object in case we fall off
00257           // the document.
00258           la = a; latrav = trav;
00259         }
00260         break;
00261     }/*end switch*/
00262 
00263       }/*end if*/
00264     }/*end if*/
00265 
00266     lasttrav = trav;
00267     a = traverseRenderObjects(a, trav, toBegin, base, state);
00268   }/*wend*/
00269 
00270   if (la) trav = latrav, a = la;
00271   return a;
00272 
00273 }
00274 
00283 static inline bool isUnsuitable(RenderObject *r, ObjectTraversalState trav)
00284 {
00285   if (!r) return false;
00286   return r->isTableCol() || r->isTableSection() || r->isTableRow()
00287     || (r->isText() && static_cast<RenderText *>(r)->inlineTextBoxCount() == 0);
00288       ;
00289   Q_UNUSED(trav);
00290 }
00291 
00305 static inline RenderObject *advanceSuitableObject(RenderObject *r,
00306         ObjectTraversalState &trav, bool toBegin,
00307         RenderObject *base, int &state)
00308 {
00309   do {
00310     r = advanceObject(r, trav, toBegin, base, state);
00311 #if DEBUG_CARETMODE > 2
00312     kdDebug(6200) << "after advanceSWP: r " << r << " trav " << trav << " toBegin " << toBegin << endl;
00313 #endif
00314   } while (isUnsuitable(r, trav));
00315   return r;
00316 }
00317 
00327 static NodeImpl *nextLeafNode(NodeImpl *r, NodeImpl *baseElem)
00328 {
00329   NodeImpl *n = r->firstChild();
00330   if (n) {
00331     while (n) { r = n; n = n->firstChild(); }
00332     return const_cast<NodeImpl *>(r);
00333   }/*end if*/
00334   n = r->nextSibling();
00335   if (n) {
00336     r = n;
00337     while (n) { r = n; n = n->firstChild(); }
00338     return const_cast<NodeImpl *>(r);
00339   }/*end if*/
00340 
00341   n = r->parentNode();
00342   if (n == baseElem) n = 0;
00343   while (n) {
00344     r = n;
00345     n = r->nextSibling();
00346     if (n) {
00347       r = n;
00348       n = r->firstChild();
00349       while (n) { r = n; n = n->firstChild(); }
00350       return const_cast<NodeImpl *>(r);
00351     }/*end if*/
00352     n = r->parentNode();
00353     if (n == baseElem) n = 0;
00354   }/*wend*/
00355   return 0;
00356 }
00357 
00358 #if 0       // currently not used
00359 
00368 static NodeImpl *prevLeafNode(NodeImpl *r, NodeImpl *baseElem)
00369 {
00370   NodeImpl *n = r->firstChild();
00371   if (n) {
00372     while (n) { r = n; n = n->firstChild(); }
00373     return const_cast<NodeImpl *>(r);
00374   }/*end if*/
00375   n = r->previousSibling();
00376   if (n) {
00377     r = n;
00378     while (n) { r = n; n = n->firstChild(); }
00379     return const_cast<NodeImpl *>(r);
00380   }/*end if*/
00381 
00382   n = r->parentNode();
00383   if (n == baseElem) n = 0;
00384   while (n) {
00385     r = n;
00386     n = r->previousSibling();
00387     if (n) {
00388       r = n;
00389       n = r->lastChild();
00390       while (n) { r = n; n = n->lastChild(); }
00391       return const_cast<NodeImpl *>(r);
00392     }/*end if*/
00393     n = r->parentNode();
00394     if (n == baseElem) n = 0;
00395   }/*wend*/
00396   return 0;
00397 }
00398 #endif
00399 
00411 void /*KDE_NO_EXPORT*/ mapDOMPosToRenderPos(NodeImpl *node, long offset,
00412         RenderObject *&r, long &r_ofs, bool &outside, bool &outsideEnd)
00413 {
00414   if (node->nodeType() == Node::TEXT_NODE) {
00415     outside = false;
00416     outsideEnd = false;
00417     r = node->renderer();
00418     r_ofs = offset;
00419   } else if (node->nodeType() == Node::ELEMENT_NODE || node->nodeType() == Node::DOCUMENT_NODE) {
00420 
00421     // Though offset points between two children, attach it to the visually
00422     // most suitable one (and only there, because the mapping must stay bijective)
00423     if (node->firstChild()) {
00424       outside = true;
00425       NodeImpl *child = offset <= 0 ? node->firstChild()
00426                     // childNode is expensive
00427                     : node->childNode((unsigned long)offset);
00428       // index was child count or out of bounds
00429       bool atEnd = !child;
00430 #if DEBUG_CARETMODE > 5
00431       kdDebug(6200) << "mapDTR: child " << child << "@" << (child ? child->nodeName().string() : QString::null) << " atEnd " << atEnd << endl;
00432 #endif
00433       if (atEnd) child = node->lastChild();
00434 
00435       r = child->renderer();
00436       r_ofs = 0;
00437       outsideEnd = atEnd;
00438 
00439       // Outside text nodes most likely stem from a continuation. Seek
00440       // the enclosing continued render object and use this one instead.
00441       if (r && child->nodeType() == Node::TEXT_NODE) {
00442         r = r->parent();
00443         RenderObject *o = node->renderer();
00444     while (o->continuation() && o->continuation() != r)
00445       o = o->continuation();
00446     if (!r || o->continuation() != r) {
00447       r = child->renderer();
00448     }
00449       }/*end if*/
00450 
00451       // BRs cause troubles. Returns the previous render object instead,
00452       // giving it the attributes outside, outside end.
00453       if (r && r->isBR()) {
00454         r = r->objectAbove();
00455         outsideEnd = true;
00456       }/*end if*/
00457 
00458     } else {
00459       // Element has no children, treat offset to be inside the node.
00460       outside = false;
00461       outsideEnd = false;
00462       r = node->renderer();
00463       r_ofs = 0;    // only offset 0 possible
00464     }
00465 
00466   } else {
00467     r = 0;
00468     kdWarning() << k_funcinfo << "Mapping from nodes of type " << node->nodeType()
00469         << " not supported!" << endl;
00470   }
00471 }
00472 
00483 void /*KDE_NO_EXPORT*/ mapRenderPosToDOMPos(RenderObject *r, long r_ofs,
00484         bool outside, bool outsideEnd, NodeImpl *&node, long &offset)
00485 {
00486   node = r->element();
00487   Q_ASSERT(node);
00488 #if DEBUG_CARETMODE > 5
00489       kdDebug(6200) << "mapRTD: r " << r << "@" << (r ? r->renderName() : QString::null) << (r && r->element() ? QString(".node ") + QString::number((unsigned)r->element(),16) + "@" + r->element()->nodeName().string() : QString::null) << " outside " << outside << " outsideEnd " << outsideEnd << endl;
00490 #endif
00491   if (node->nodeType() == Node::ELEMENT_NODE || node->nodeType() == Node::TEXT_NODE) {
00492 
00493     if (outside) {
00494       NodeImpl *parent = node->parent();
00495 
00496       // If this is part of a continuation, use the actual node as the parent,
00497       // and the first render child as the node.
00498       if (r != node->renderer()) {
00499         RenderObject *o = node->renderer();
00500     while (o->continuation() && o->continuation() != r)
00501       o = o->continuation();
00502     if (o->continuation() == r) {
00503       parent = node;
00504       // ### What if the first render child does not map to a child of
00505       // the continued node?
00506       node = r->firstChild() ? r->firstChild()->element() : node;
00507     }
00508       }/*end if*/
00509 
00510       if (!parent) goto inside;
00511 
00512       offset = (long)node->nodeIndex() + outsideEnd;
00513       node = parent;
00514 #if DEBUG_CARETMODE > 5
00515    kdDebug(6200) << node << "@" << (node ? node->nodeName().string() : QString::null) << " offset " << offset << endl;
00516 #endif
00517     } else {    // !outside
00518 inside:
00519       offset = r_ofs;
00520     }
00521 
00522   } else {
00523     offset = 0;
00524     kdWarning() << k_funcinfo << "Mapping to nodes of type " << node->nodeType()
00525         << " not supported!" << endl;
00526   }
00527 }
00528 
00530 static inline void ensureLeafNode(NodeImpl *&node, NodeImpl *base)
00531 {
00532   if (node && node->hasChildNodes()) node = nextLeafNode(node, base);
00533 }
00534 
00541 static inline void mapRenderPosToTraversalState(bool outside, bool atEnd,
00542         bool toBegin, ObjectTraversalState &trav)
00543 {
00544   if (!outside) atEnd = !toBegin;
00545   if (!atEnd ^ toBegin)
00546     trav = outside ? OutsideDescending : InsideDescending;
00547   else
00548     trav = outside ? OutsideAscending : InsideAscending;
00549 }
00550 
00557 static inline void mapTraversalStateToRenderPos(ObjectTraversalState trav,
00558         bool toBegin, bool &outside, bool &atEnd)
00559 {
00560   outside = false;
00561   switch (trav) {
00562     case OutsideDescending: outside = true; // fall through
00563     case InsideDescending: atEnd = toBegin; break;
00564     case OutsideAscending: outside = true; // fall through
00565     case InsideAscending: atEnd = !toBegin; break;
00566   }
00567 }
00568 
00584 static RenderObject* findRenderer(NodeImpl *&node, long offset,
00585             RenderObject *base, long &r_ofs,
00586                         bool &outside, bool &outsideEnd)
00587 {
00588   if (!node) return 0;
00589   RenderObject *r;
00590   mapDOMPosToRenderPos(node, offset, r, r_ofs, outside, outsideEnd);
00591 #if DEBUG_CARETMODE > 2
00592    kdDebug(6200) << "findRenderer: node " << node << " " << (node ? node->nodeName().string() : QString::null) << " offset " << offset << " r " << r << "[" << (r ? r->renderName() : QString::null) << "] r_ofs " << r_ofs << " outside " << outside << " outsideEnd " << outsideEnd << endl;
00593 #endif
00594   if (r) return r;
00595   NodeImpl *baseElem = base ? base->element() : 0;
00596   while (!r) {
00597     node = nextLeafNode(node, baseElem);
00598     if (!node) break;
00599     r = node->renderer();
00600     if (r) r_ofs = offset;
00601   }
00602 #if DEBUG_CARETMODE > 3
00603   kdDebug(6200) << "1r " << r << endl;
00604 #endif
00605   ObjectTraversalState trav;
00606   int state;        // not used
00607   mapRenderPosToTraversalState(outside, outsideEnd, false, trav);
00608   if (r && isUnsuitable(r, trav)) {
00609     r = advanceSuitableObject(r, trav, false, base, state);
00610     mapTraversalStateToRenderPos(trav, false, outside, outsideEnd);
00611     if (r) r_ofs = r->minOffset();
00612   }
00613 #if DEBUG_CARETMODE > 3
00614   kdDebug(6200) << "2r " << r << endl;
00615 #endif
00616   return r;
00617 }
00618 
00622 static ElementImpl *determineBaseElement(NodeImpl *caretNode)
00623 {
00624   // ### for now, only body is delivered for html documents,
00625   // and 0 for xml documents.
00626 
00627   DocumentImpl *doc = caretNode->getDocument();
00628   if (!doc) return 0;   // should not happen, but who knows.
00629 
00630   if (doc->isHTMLDocument())
00631     return static_cast<HTMLDocumentImpl *>(doc)->body();
00632 
00633   return 0;
00634 }
00635 
00636 // == class CaretBox implementation
00637 
00638 #if DEBUG_CARETMODE > 0
00639 void CaretBox::dump(QTextStream &ts, const QString &ind) const
00640 {
00641   ts << ind << "b@" << _box;
00642 
00643   if (_box) {
00644     ts << "<" << _box->object() << ":" << _box->object()->renderName() << ">";
00645   }/*end if*/
00646 
00647   ts << " " << _x << "+" << _y << "+" << _w << "*" << _h;
00648 
00649   ts << " cb@" << cb;
00650   if (cb) ts << ":" << cb->renderName();
00651 
00652   ts << " " << (_outside ? (outside_end ? "oe" : "o-") : "i-");
00653 //  ts << endl;
00654 }
00655 #endif
00656 
00657 // == class CaretBoxLine implementation
00658 
00659 #if DEBUG_CARETMODE > 0
00660 #  define DEBUG_ACIB 1
00661 #else
00662 #  define DEBUG_ACIB DEBUG_CARETMODE
00663 #endif
00664 void CaretBoxLine::addConvertedInlineBox(InlineBox *box, SeekBoxParams &sbp) /*KDE_NO_EXPORT*/
00665 {
00666   // Generate only one outside caret box between two elements. If
00667   // coalesceOutsideBoxes is true, generating left outside boxes is inhibited.
00668   bool coalesceOutsideBoxes = false;
00669   CaretBoxIterator lastCoalescedBox;
00670   for (; box; box = box->nextOnLine()) {
00671 #if DEBUG_ACIB
00672 kdDebug(6200) << "box " << box << endl;
00673 kdDebug(6200) << "box->object " << box->object() << endl;
00674 kdDebug(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;
00675 #endif
00676     // ### Why the hell can object() ever be 0?!
00677     if (!box->object()) continue;
00678 
00679     RenderStyle *s = box->object()->style(box->m_firstLine);
00680     // parent style for outside caret boxes
00681     RenderStyle *ps = box->parent() && box->parent()->object()
00682             ? box->parent()->object()->style(box->parent()->m_firstLine)
00683         : s;
00684 
00685     if (box->isInlineFlowBox()) {
00686 #if DEBUG_ACIB
00687 kdDebug(6200) << "isinlineflowbox " << box << endl;
00688 #endif
00689       InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(box);
00690       bool rtl = ps->direction() == RTL;
00691       const QFontMetrics &pfm = ps->fontMetrics();
00692 
00693       if (flowBox->includeLeftEdge()) {
00694         // If this box is to be coalesced with the outside end box of its
00695         // predecessor, then check if it is the searched box. If it is, we
00696         // substitute the outside end box.
00697         if (coalesceOutsideBoxes) {
00698           if (sbp.equalsBox(flowBox, true, false)) {
00699             sbp.it = lastCoalescedBox;
00700             Q_ASSERT(!sbp.found);
00701             sbp.found = true;
00702           }
00703         } else {
00704           addCreatedFlowBoxEdge(flowBox, pfm, true, rtl);
00705           sbp.check(preEnd());
00706         }
00707       }/*end if*/
00708 
00709       if (flowBox->firstChild()) {
00710 #if DEBUG_ACIB
00711 kdDebug(6200) << "this " << this << " flowBox " << flowBox << " firstChild " << flowBox->firstChild() << endl;
00712 kdDebug(6200) << "== recursive invocation" << endl;
00713 #endif
00714         addConvertedInlineBox(flowBox->firstChild(), sbp);
00715 #if DEBUG_ACIB
00716 kdDebug(6200) << "== recursive invocation end" << endl;
00717 #endif
00718 }
00719       else {
00720         addCreatedFlowBoxInside(flowBox, s->fontMetrics());
00721         sbp.check(preEnd());
00722       }
00723 
00724       if (flowBox->includeRightEdge()) {
00725         addCreatedFlowBoxEdge(flowBox, pfm, false, rtl);
00726         lastCoalescedBox = preEnd();
00727         sbp.check(lastCoalescedBox);
00728         coalesceOutsideBoxes = true;
00729       }
00730 
00731     } else if (box->isInlineTextBox()) {
00732 #if DEBUG_ACIB
00733 kdDebug(6200) << "isinlinetextbox " << box << (box->object() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText *>(box->object())->str->s+box->minOffset(), kMin(box->maxOffset() - box->minOffset(), 15L)).string()) : QString::null) << endl;
00734 #endif
00735       caret_boxes.append(new CaretBox(box, false, false));
00736       sbp.check(preEnd());
00737       // coalescing has been interrupted
00738       coalesceOutsideBoxes = false;
00739 
00740     } else {
00741 #if DEBUG_ACIB
00742 kdDebug(6200) << "some replaced or what " << box << endl;
00743 #endif
00744       // must be an inline-block, inline-table, or any RenderReplaced
00745       bool rtl = ps->direction() == RTL;
00746       const QFontMetrics &pfm = ps->fontMetrics();
00747 
00748       if (coalesceOutsideBoxes) {
00749         if (sbp.equalsBox(box, true, false)) {
00750           sbp.it = lastCoalescedBox;
00751           Q_ASSERT(!sbp.found);
00752           sbp.found = true;
00753         }
00754       } else {
00755         addCreatedInlineBoxEdge(box, pfm, true, rtl);
00756         sbp.check(preEnd());
00757       }
00758 
00759       caret_boxes.append(new CaretBox(box, false, false));
00760       sbp.check(preEnd());
00761 
00762       addCreatedInlineBoxEdge(box, pfm, false, rtl);
00763       lastCoalescedBox = preEnd();
00764       sbp.check(lastCoalescedBox);
00765       coalesceOutsideBoxes = true;
00766     }/*end if*/
00767   }/*next box*/
00768 }
00769 #undef DEBUG_ACIB
00770 
00771 void CaretBoxLine::addCreatedFlowBoxInside(InlineFlowBox *flowBox, const QFontMetrics &fm) /*KDE_NO_EXPORT*/
00772 {
00773 
00774   CaretBox *caretBox = new CaretBox(flowBox, false, false);
00775   caret_boxes.append(caretBox);
00776 
00777   // afaik an inner flow box can only have the width 0, therefore we don't
00778   // have to care for rtl or alignment
00779   // ### can empty inline elements have a width? css 2 spec isn't verbose about it
00780 
00781   caretBox->_y += flowBox->baseline() - fm.ascent();
00782   caretBox->_h = fm.height();
00783 }
00784 
00785 void CaretBoxLine::addCreatedFlowBoxEdge(InlineFlowBox *flowBox, const QFontMetrics &fm, bool left, bool rtl) /*KDE_NO_EXPORT*/
00786 {
00787   CaretBox *caretBox = new CaretBox(flowBox, true, !left);
00788   caret_boxes.append(caretBox);
00789 
00790   if (left ^ rtl) caretBox->_x -= flowBox->paddingLeft() + flowBox->borderLeft() + 1;
00791   else caretBox->_x += caretBox->_w + flowBox->paddingRight() + flowBox->borderRight();
00792 
00793   caretBox->_y += flowBox->baseline() - fm.ascent();
00794   caretBox->_h = fm.height();
00795   caretBox->_w = 1;
00796 }
00797 
00798 void CaretBoxLine::addCreatedInlineBoxEdge(InlineBox *box, const QFontMetrics &fm, bool left, bool rtl) /*KDE_NO_EXPORT*/
00799 {
00800   CaretBox *caretBox = new CaretBox(box, true, !left);
00801   caret_boxes.append(caretBox);
00802 
00803   if (left ^ rtl) caretBox->_x--;
00804   else caretBox->_x += caretBox->_w;
00805 
00806   caretBox->_y += box->baseline() - fm.ascent();
00807   caretBox->_h = fm.height();
00808   caretBox->_w = 1;
00809 }
00810 
00811 CaretBoxLine *CaretBoxLine::constructCaretBoxLine(CaretBoxLineDeleter *deleter,
00812     InlineFlowBox *basicFlowBox, InlineBox *seekBox, bool seekOutside,
00813         bool seekOutsideEnd, CaretBoxIterator &iter, RenderObject *seekObject)
00814 //  KDE_NO_EXPORT
00815 {
00816   // Iterate all inline boxes within this inline flow box.
00817   // Caret boxes will be created for each
00818   // - outside begin of an inline flow box (except for the basic inline flow box)
00819   // - outside end of an inline flow box (except for the basic inline flow box)
00820   // - inside of an empty inline flow box
00821   // - outside begin of an inline box resembling a replaced element
00822   // - outside end of an inline box resembling a replaced element
00823   // - inline text box
00824   // - inline replaced box
00825 
00826   CaretBoxLine *result = new CaretBoxLine(basicFlowBox);
00827   deleter->append(result);
00828 
00829   SeekBoxParams sbp(seekBox, seekOutside, seekOutsideEnd, seekObject, iter);
00830 
00831   // iterate recursively, I'm too lazy to do it iteratively
00832   result->addConvertedInlineBox(basicFlowBox, sbp);
00833 
00834   if (!sbp.found) sbp.it = result->end();
00835 
00836   return result;
00837 }
00838 
00839 CaretBoxLine *CaretBoxLine::constructCaretBoxLine(CaretBoxLineDeleter *deleter,
00840     RenderBox *cb, bool outside, bool outsideEnd, CaretBoxIterator &iter) /*KDE_NO_EXPORT*/
00841 {
00842   int _x = cb->xPos();
00843   int _y = cb->yPos();
00844   int height;
00845   int width = 1;        // no override is indicated in boxes
00846 
00847   if (outside) {
00848 
00849     RenderStyle *s = cb->element() && cb->element()->parent()
00850             && cb->element()->parent()->renderer()
00851             ? cb->element()->parent()->renderer()->style()
00852             : cb->style();
00853     bool rtl = s->direction() == RTL;
00854 
00855     const QFontMetrics &fm = s->fontMetrics();
00856     height = fm.height();
00857 
00858     if (!outsideEnd) {
00859         _x--;
00860     } else {
00861         _x += cb->width();
00862     }
00863 
00864     int hl = fm.leading() / 2;
00865     int baseline = cb->baselinePosition(false);
00866     if (!cb->isReplaced() || cb->style()->display() == BLOCK) {
00867         if (!outsideEnd ^ rtl)
00868             _y -= fm.leading() / 2;
00869         else
00870             _y += kMax(cb->height() - fm.ascent() - hl, 0);
00871     } else {
00872         _y += baseline - fm.ascent() - hl;
00873     }
00874 
00875   } else {      // !outside
00876 
00877     RenderStyle *s = cb->style();
00878     const QFontMetrics &fm = s->fontMetrics();
00879     height = fm.height();
00880 
00881     _x += cb->borderLeft() + cb->paddingLeft();
00882     _y += cb->borderTop() + cb->paddingTop();
00883 
00884     // ### regard direction
00885     switch (s->textAlign()) {
00886       case LEFT:
00887       case KHTML_LEFT:
00888       case TAAUTO:  // ### find out what this does
00889       case JUSTIFY:
00890         break;
00891       case CENTER:
00892       case KHTML_CENTER:
00893         _x += cb->contentWidth() / 2;
00894         break;
00895       case KHTML_RIGHT:
00896       case RIGHT:
00897         _x += cb->contentWidth();
00898         break;
00899     }/*end switch*/
00900   }/*end if*/
00901 
00902   CaretBoxLine *result = new CaretBoxLine;
00903   deleter->append(result);
00904   result->caret_boxes.append(new CaretBox(_x, _y, width, height, cb,
00905             outside, outsideEnd));
00906   iter = result->begin();
00907   return result;
00908 }
00909 
00910 #if DEBUG_CARETMODE > 0
00911 void CaretBoxLine::dump(QTextStream &ts, const QString &ind) const
00912 {
00913   ts << ind << "cbl: baseFlowBox@" << basefb << endl;
00914   QString ind2 = ind + "  ";
00915   for (size_t i = 0; i < caret_boxes.size(); i++) {
00916     if (i > 0) ts << endl;
00917     caret_boxes[i]->dump(ts, ind2);
00918   }
00919 }
00920 #endif
00921 
00922 // == caret mode related helper functions
00923 
00931 inline InlineFlowBox *seekBaseFlowBox(InlineBox *b, RenderObject *base = 0)
00932 {
00933   // Seek root line box or base inline flow box, if \c base is interfering.
00934   while (b->parent() && b->object() != base) {
00935     b = b->parent();
00936   }/*wend*/
00937   Q_ASSERT(b->isInlineFlowBox());
00938   return static_cast<InlineFlowBox *>(b);
00939 }
00940 
00943 inline bool isBlockRenderReplaced(RenderObject *r)
00944 {
00945   return r->isRenderReplaced() && r->style()->display() == BLOCK;
00946 }
00947 
00964 static CaretBoxLine* findCaretBoxLine(DOM::NodeImpl *node, long offset,
00965         CaretBoxLineDeleter *cblDeleter, RenderObject *base,
00966                 long &r_ofs, CaretBoxIterator &caretBoxIt)
00967 {
00968   bool outside, outsideEnd;
00969   RenderObject *r = findRenderer(node, offset, base, r_ofs, outside, outsideEnd);
00970   if (!r) { return 0; }
00971 #if DEBUG_CARETMODE > 0
00972   kdDebug(6200) << "=================== findCaretBoxLine" << endl;
00973   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;
00974 #endif
00975 
00976   // There are two strategies to find the correct line box. (The third is failsafe)
00977   // (A) First, if node's renderer is a RenderText, we only traverse its text
00978   // runs and return the root line box (saves much time for long blocks).
00979   // This should be the case 99% of the time.
00980   // (B) Second, we derive the inline flow box directly when the renderer is
00981   // a RenderBlock, RenderInline, or blocked RenderReplaced.
00982   // (C) Otherwise, we iterate linearly through all line boxes in order to find
00983   // the renderer.
00984 
00985   // (A)
00986   if (r->isText()) do {
00987     RenderText *t = static_cast<RenderText *>(r);
00988     int dummy;
00989     InlineBox *b = t->findInlineTextBox(offset, dummy, true);
00990     // Actually b should never be 0, but some render texts don't have text
00991     // boxes, so we insert the last run as an error correction.
00992     // If there is no last run, we resort to (B)
00993     if (!b) {
00994       if (t->m_lines.count() > 0)
00995         b = t->m_lines[t->m_lines.count() - 1];
00996       else
00997         break;
00998     }/*end if*/
00999     Q_ASSERT(b);
01000     outside = false;    // text boxes cannot have outside positions
01001     InlineFlowBox *baseFlowBox = seekBaseFlowBox(b, base);
01002 #if DEBUG_CARETMODE > 2
01003   kdDebug(6200) << "text-box b: " << b << " baseFlowBox: " << baseFlowBox << (b && b->object() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText *>(b->object())->str->s+b->minOffset(), kMin(b->maxOffset() - b->minOffset(), 15L)).string()) : QString::null) << endl;
01004 #endif
01005 #if 0
01006     if (t->containingBlock()->isListItem()) dumpLineBoxes(static_cast<RenderFlow *>(t->containingBlock()));
01007 #endif
01008 #if DEBUG_CARETMODE > 0
01009   kdDebug(6200) << "=================== end findCaretBoxLine (renderText)" << endl;
01010 #endif
01011     return CaretBoxLine::constructCaretBoxLine(cblDeleter, baseFlowBox,
01012         b, outside, outsideEnd, caretBoxIt);
01013   } while(false);/*end if*/
01014 
01015   // (B)
01016   bool isrepl = isBlockRenderReplaced(r);
01017   if (r->isRenderBlock() || r->isRenderInline() || isrepl) {
01018     RenderFlow *flow = static_cast<RenderFlow *>(r);
01019     InlineFlowBox *firstLineBox = isrepl ? 0 : flow->firstLineBox();
01020 
01021     // On render blocks, if we are outside, or have a totally empty render
01022     // block, we simply construct a special caret box line.
01023     // The latter case happens only when the render block is a leaf object itself.
01024     if (isrepl || r->isRenderBlock() && (outside || !firstLineBox)
01025             || r->isRenderInline() && !firstLineBox) {
01026   #if DEBUG_CARETMODE > 0
01027     kdDebug(6200) << "=================== end findCaretBoxLine (box " << (outside ? (outsideEnd ? "outside end" : "outside begin") : "inside") << ")" << endl;
01028   #endif
01029       Q_ASSERT(r->isBox());
01030       return CaretBoxLine::constructCaretBoxLine(cblDeleter,
01031             static_cast<RenderBox *>(r), outside, outsideEnd, caretBoxIt);
01032     }/*end if*/
01033 
01034   kdDebug(6200) << "firstlinebox " << firstLineBox << endl;
01035     InlineFlowBox *baseFlowBox = seekBaseFlowBox(firstLineBox, base);
01036     return CaretBoxLine::constructCaretBoxLine(cblDeleter, baseFlowBox,
01037         firstLineBox, outside, outsideEnd, caretBoxIt);
01038   }/*end if*/
01039 
01040   RenderBlock *cb = r->containingBlock();
01041   //if ( !cb ) return 0L;
01042   Q_ASSERT(cb);
01043 
01044   // ### which element doesn't have a block as its containing block?
01045   // Is it still possible after the RenderBlock/RenderInline merge?
01046   if (!cb->isRenderBlock()) {
01047     kdWarning() << "containing block is no render block!!! crash imminent" << endl;
01048   }/*end if*/
01049 
01050   InlineFlowBox *flowBox = cb->firstLineBox();
01051   // (C)
01052   // This case strikes when the element is replaced, but neither a
01053   // RenderBlock nor a RenderInline
01054   if (!flowBox) {   // ### utter emergency (why is this possible at all?)
01055 //    flowBox = generateDummyFlowBox(arena, cb, r);
01056 //    if (ibox) *ibox = flowBox->firstChild();
01057 //    outside = outside_end = true;
01058 
01059 //    kdWarning() << "containing block contains no inline flow boxes!!! crash imminent" << endl;
01060 #if DEBUG_CARETMODE > 0
01061    kdDebug(6200) << "=================== end findCaretBoxLine (2)" << endl;
01062 #endif
01063     return CaretBoxLine::constructCaretBoxLine(cblDeleter, cb,
01064             outside, outsideEnd, caretBoxIt);
01065   }/*end if*/
01066 
01067   // We iterate the inline flow boxes of the containing block until
01068   // we find the given node. This has one major flaw: it is linear, and therefore
01069   // painfully slow for really large blocks.
01070   for (; flowBox; flowBox = static_cast<InlineFlowBox *>(flowBox->nextLineBox())) {
01071 #if DEBUG_CARETMODE > 0
01072     kdDebug(6200) << "[scan line]" << endl;
01073 #endif
01074 
01075     // construct a caret line box and stop when the element is contained within
01076     InlineFlowBox *baseFlowBox = seekBaseFlowBox(flowBox, base);
01077     CaretBoxLine *cbl = CaretBoxLine::constructCaretBoxLine(cblDeleter,
01078         baseFlowBox, 0, outside, outsideEnd, caretBoxIt, r);
01079 #if DEBUG_CARETMODE > 5
01080     kdDebug(6200) << cbl->information() << endl;
01081 #endif
01082     if (caretBoxIt != cbl->end()) {
01083 #if DEBUG_CARETMODE > 0
01084    kdDebug(6200) << "=================== end findCaretBoxLine (3)" << endl;
01085 #endif
01086       return cbl;
01087     }
01088   }/*next flowBox*/
01089 
01090   // no inline flow box found, approximate to nearest following node.
01091   // Danger: this is O(n^2). It's only called to recover from
01092   // errors, that means, theoretically, never. (Practically, far too often :-( )
01093   Q_ASSERT(!flowBox);
01094   CaretBoxLine *cbl = findCaretBoxLine(nextLeafNode(node, base ? base->element() : 0), 0, cblDeleter, base, r_ofs, caretBoxIt);
01095 #if DEBUG_CARETMODE > 0
01096   kdDebug(6200) << "=================== end findCaretBoxLine" << endl;
01097 #endif
01098   return cbl;
01099 }
01100 
01107 static inline RenderTable *findTableUpTo(RenderObject *r, RenderFlow *cb)
01108 {
01109   while (r && r != cb && !r->isTable()) r = r->parent();
01110   return r && r->isTable() ? static_cast<RenderTable *>(r) : 0;
01111 }
01112 
01115 static inline bool isDescendant(RenderObject *r, RenderObject *cb)
01116 {
01117   while (r && r != cb) r = r->parent();
01118   return r;
01119 }
01120 
01131 static bool containsEditableElement(KHTMLPart *part, RenderBlock *cb,
01132     RenderTable *&table, bool fromEnd = false)
01133 {
01134   RenderObject *r = cb;
01135   if (fromEnd)
01136     while (r->lastChild()) r = r->lastChild();
01137   else
01138     while (r->firstChild()) r = r->firstChild();
01139 
01140   RenderTable *tempTable = 0;
01141   table = 0;
01142   bool withinCb;
01143 //  int state;      // not used
01144   ObjectTraversalState trav = InsideDescending;
01145   do {
01146     bool modWithinCb = withinCb = isDescendant(r, cb);
01147 
01148     // treat cb extra, it would not be considered otherwise
01149     if (!modWithinCb) {
01150       modWithinCb = true;
01151       r = cb;
01152     } else
01153       tempTable = findTableUpTo(r, cb);
01154 
01155 #if DEBUG_CARETMODE > 1
01156     kdDebug(6201) << "cee: r " << (r ? r->renderName() : QString::null) << "@" << r << " cb " << cb << " withinCb " << withinCb << " modWithinCb " << modWithinCb << " tempTable " << tempTable << endl;
01157 #endif
01158     if (r && modWithinCb && r->element() && !isUnsuitable(r, trav)
01159         && (part->isCaretMode() || part->isEditable()
01160         || r->style()->userInput() == UI_ENABLED)) {
01161       table = tempTable;
01162 #if DEBUG_CARETMODE > 1
01163     kdDebug(6201) << "cee: editable" << endl;
01164 #endif
01165       return true;
01166     }/*end if*/
01167 
01168 //    RenderObject *oldr = r;
01169 //    while (r && r == oldr)
01170 //      r = advanceSuitableObject(r, trav, fromEnd, cb->parent(), state);
01171     r = fromEnd ? r->objectAbove() : r->objectBelow();
01172   } while (r && withinCb);
01173   return false;
01174 }
01175 
01188 static bool containsEditableChildElement(KHTMLPart *part, RenderBlock *cb,
01189     RenderTable *&table, bool fromEnd, RenderObject *start)
01190 {
01191   int state = 0;
01192   ObjectTraversalState trav = OutsideAscending;
01193 // kdDebug(6201) << "start: " << start << endl;
01194   RenderObject *r = start;
01195   do {
01196     r = traverseRenderObjects(r, trav, fromEnd, cb->parent(), state);
01197   } while(r && !(state & AdvancedToSibling));
01198 // kdDebug(6201) << "r: " << r << endl;
01199   //advanceObject(start, trav, fromEnd, cb->parent(), state);
01200 //     RenderObject *oldr = r;
01201 //     while (r && r == oldr)
01202   if (!r) return false;
01203 
01204   if (fromEnd)
01205     while (r->firstChild()) r = r->firstChild();
01206   else
01207     while (r->lastChild()) r = r->lastChild();
01208 // kdDebug(6201) << "child r: " << r << endl;
01209   if (!r) return false;
01210 
01211   RenderTable *tempTable = 0;
01212   table = 0;
01213   bool withinCb = false;
01214   do {
01215 
01216     bool modWithinCb = withinCb = isDescendant(r, cb);
01217 
01218     // treat cb extra, it would not be considered otherwise
01219     if (!modWithinCb) {
01220       modWithinCb = true;
01221       r = cb;
01222     } else
01223       tempTable = findTableUpTo(r, cb);
01224 
01225 #if DEBUG_CARETMODE > 1
01226     kdDebug(6201) << "cece: r " << (r ? r->renderName() : QString::null) << "@" << r << " cb " << cb << " withinCb " << withinCb << " modWithinCb " << modWithinCb << " tempTable " << tempTable << endl;
01227 #endif
01228     if (r && withinCb && r->element() && !isUnsuitable(r, trav)
01229         && (part->isCaretMode() || part->isEditable()
01230         || r->style()->userInput() == UI_ENABLED)) {
01231       table = tempTable;
01232 #if DEBUG_CARETMODE > 1
01233     kdDebug(6201) << "cece: editable" << endl;
01234 #endif
01235       return true;
01236     }/*end if*/
01237 
01238     r = fromEnd ? r->objectAbove() : r->objectBelow();
01239   } while (withinCb);
01240   return false;
01241 }
01242 
01243 // == class LinearDocument implementation
01244 
01245 LinearDocument::LinearDocument(KHTMLPart *part, NodeImpl *node, long offset,
01246         CaretAdvancePolicy advancePolicy, ElementImpl *baseElem)
01247     : node(node), offset(offset), m_part(part),
01248     advPol(advancePolicy), base(0)
01249 {
01250   if (node == 0) return;
01251 
01252   if (baseElem) {
01253     RenderObject *b = baseElem->renderer();
01254     if (b && (b->isRenderBlock() || b->isRenderInline()))
01255       base = b;
01256   }
01257 
01258   initPreBeginIterator();
01259   initEndIterator();
01260 }
01261 
01262 LinearDocument::~LinearDocument()
01263 {
01264 }
01265 
01266 int LinearDocument::count() const
01267 {
01268   // FIXME: not implemented
01269   return 1;
01270 }
01271 
01272 LinearDocument::Iterator LinearDocument::current()
01273 {
01274   return LineIterator(this, node, offset);
01275 }
01276 
01277 LinearDocument::Iterator LinearDocument::begin()
01278 {
01279   NodeImpl *n = base ? base->element() : 0;
01280   if (!base) n = node ? node->getDocument() : 0;
01281   if (!n) return end();
01282 
01283   n = n->firstChild();
01284   if (advPol == LeafsOnly)
01285     while (n->firstChild()) n = n->firstChild();
01286 
01287   if (!n) return end();     // must be empty document or empty base element
01288   return LineIterator(this, n, n->minOffset());
01289 }
01290 
01291 LinearDocument::Iterator LinearDocument::preEnd()
01292 {
01293   NodeImpl *n = base ? base->element() : 0;
01294   if (!base) n = node ? node->getDocument() : 0;
01295   if (!n) return preBegin();
01296 
01297   n = n->lastChild();
01298   if (advPol == LeafsOnly)
01299     while (n->lastChild()) n = n->lastChild();
01300 
01301   if (!n) return preBegin();    // must be empty document or empty base element
01302   return LineIterator(this, n, n->maxOffset());
01303 }
01304 
01305 void LinearDocument::initPreBeginIterator()
01306 {
01307   _preBegin = LineIterator(this, 0, 0);
01308 }
01309 
01310 void LinearDocument::initEndIterator()
01311 {
01312   _end = LineIterator(this, 0, 1);
01313 }
01314 
01315 // == class LineIterator implementation
01316 
01317 CaretBoxIterator LineIterator::currentBox /*KDE_NO_EXPORT*/;
01318 long LineIterator::currentOffset /*KDE_NO_EXPORT*/;
01319 
01320 LineIterator::LineIterator(LinearDocument *l, DOM::NodeImpl *node, long offset)
01321         : lines(l)
01322 {
01323 //  kdDebug(6200) << "LineIterator: node " << node << " offset " << offset << endl;
01324   if (!node) { cbl = 0; return; }
01325   cbl = findCaretBoxLine(node, offset, &lines->cblDeleter,
01326         l->baseObject(), currentOffset, currentBox);
01327   // can happen on partially loaded documents
01328 #if DEBUG_CARETMODE > 0
01329   if (!cbl) kdDebug(6200) << "no render object found!" << endl;
01330 #endif
01331   if (!cbl) return;
01332 #if DEBUG_CARETMODE > 1
01333   kdDebug(6200) << "LineIterator: offset " << offset << " outside " << cbl->isOutside() << endl;
01334 #endif
01335 #if DEBUG_CARETMODE > 3
01336   kdDebug(6200) << cbl->information() << endl;
01337 #endif
01338   if (currentBox == cbl->end()) {
01339 #if DEBUG_CARETMODE > 0
01340     kdDebug(6200) << "LineIterator: findCaretBoxLine failed" << endl;
01341 #endif
01342     cbl = 0;
01343   }/*end if*/
01344 }
01345 
01346 void LineIterator::nextBlock()
01347 {
01348   RenderObject *base = lines->baseObject();
01349 
01350   bool cb_outside = cbl->isOutside();
01351   bool cb_outside_end = cbl->isOutsideEnd();
01352 
01353   {
01354     RenderObject *r = cbl->enclosingObject();
01355 
01356     ObjectTraversalState trav;
01357     int state;      // not used
01358     mapRenderPosToTraversalState(cb_outside, cb_outside_end, false, trav);
01359 #if DEBUG_CARETMODE > 1
01360     kdDebug(6200) << "nextBlock: before adv r" << r << " " << (r ? r->renderName() : QString::null) << (r && r->isText() ? " contains \"" + QString(((RenderText *)r)->str->s, QMIN(((RenderText *)r)->str->l,15)) + "\"" : QString::null) << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end << endl;
01361 #endif
01362     r = advanceSuitableObject(r, trav, false, base, state);
01363     if (!r) {
01364       cbl = 0;
01365       return;
01366     }/*end if*/
01367 
01368     mapTraversalStateToRenderPos(trav, false, cb_outside, cb_outside_end);
01369 #if DEBUG_CARETMODE > 1
01370     kdDebug(6200) << "nextBlock: after r" << r << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end << endl;
01371 #endif
01372 #if DEBUG_CARETMODE > 0
01373     kdDebug(6200) << "++: r " << r << "[" << (r?r->renderName():QString::null) << "]" << endl;
01374 #endif
01375 
01376     RenderBlock *cb;
01377 
01378     // If we hit a block or replaced object, use this as its enclosing object
01379     bool isrepl = isBlockRenderReplaced(r);
01380     if (r->isRenderBlock() || isrepl) {
01381       RenderBox *cb = static_cast<RenderBox *>(r);
01382 
01383       cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
01384             cb_outside, cb_outside_end, currentBox);
01385 
01386 #if DEBUG_CARETMODE > 0
01387       kdDebug(6200) << "r->isFlow is cb. continuation @" << cb->continuation() << endl;
01388 #endif
01389       return;
01390     } else {
01391       cb = r->containingBlock();
01392       Q_ASSERT(cb->isRenderBlock());
01393     }/*end if*/
01394     InlineFlowBox *flowBox = cb->firstLineBox();
01395 #if DEBUG_CARETMODE > 0
01396     kdDebug(6200) << "++: flowBox " << flowBox << " cb " << cb << "[" << (cb?cb->renderName()+QString(".node ")+QString::number((unsigned)cb->element(),16)+(cb->element()?"@"+cb->element()->nodeName().string():QString::null):QString::null) << "]" << endl;
01397 #endif
01398     Q_ASSERT(flowBox);
01399     if (!flowBox) { // ### utter emergency (why is this possible at all?)
01400       cb_outside = cb_outside_end = true;
01401       cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
01402             cb_outside, cb_outside_end, currentBox);
01403       return;
01404     }
01405 
01406     bool seekOutside = false, seekOutsideEnd = false; // silence gcc uninit warning
01407     CaretBoxIterator it;
01408     cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
01409     flowBox, flowBox->firstChild(), seekOutside, seekOutsideEnd, it);
01410   }
01411 }
01412 
01413 void LineIterator::prevBlock()
01414 {
01415   RenderObject *base = lines->baseObject();
01416 
01417   bool cb_outside = cbl->isOutside();
01418   bool cb_outside_end = cbl->isOutsideEnd();
01419 
01420   {
01421     RenderObject *r = cbl->enclosingObject();
01422     if (r->isAnonymous() && !cb_outside)
01423       cb_outside = true, cb_outside_end = false;
01424 
01425     ObjectTraversalState trav;
01426     int state;      // not used
01427     mapRenderPosToTraversalState(cb_outside, cb_outside_end, true, trav);
01428 #if DEBUG_CARETMODE > 1
01429     kdDebug(6200) << "prevBlock: before adv r" << r << " " << (r ? r->renderName() : QString::null) << (r && r->isText() ? " contains \"" + QString(((RenderText *)r)->str->s, QMIN(((RenderText *)r)->str->l,15)) + "\"" : QString::null) << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end << endl;
01430 #endif
01431     r = advanceSuitableObject(r, trav, true, base, state);
01432     if (!r) {
01433       cbl = 0;
01434       return;
01435     }/*end if*/
01436 
01437     mapTraversalStateToRenderPos(trav, true, cb_outside, cb_outside_end);
01438 #if DEBUG_CARETMODE > 1
01439     kdDebug(6200) << "prevBlock: after r" << r << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end << endl;
01440 #endif
01441 #if DEBUG_CARETMODE > 0
01442     kdDebug(6200) << "--: r " << r << "[" << (r?r->renderName():QString::null) << "]" << endl;
01443 #endif
01444 
01445     RenderBlock *cb;
01446 
01447     // If we hit a block, use this as its enclosing object
01448     bool isrepl = isBlockRenderReplaced(r);
01449 //    kdDebug(6200) << "isrepl " << isrepl << " isblock " << r->isRenderBlock() << endl;
01450     if (r->isRenderBlock() || isrepl) {
01451       RenderBox *cb = static_cast<RenderBox *>(r);
01452 
01453       cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
01454             cb_outside, cb_outside_end, currentBox);
01455 
01456 #if DEBUG_CARETMODE > 0
01457       kdDebug(6200) << "r->isFlow is cb. continuation @" << cb->continuation() << endl;
01458 #endif
01459       return;
01460     } else {
01461       cb = r->containingBlock();
01462       Q_ASSERT(cb->isRenderBlock());
01463     }/*end if*/
01464     InlineFlowBox *flowBox = cb->lastLineBox();
01465 #if DEBUG_CARETMODE > 0
01466     kdDebug(6200) << "--: flowBox " << flowBox << " cb " << cb << "[" << (cb?cb->renderName()+QString(".node ")+QString::number((unsigned)cb->element(),16)+(cb->element()?"@"+cb->element()->nodeName().string():QString::null):QString::null) << "]" << endl;
01467 #endif
01468     Q_ASSERT(flowBox);
01469     if (!flowBox) { // ### utter emergency (why is this possible at all?)
01470       cb_outside = true; cb_outside_end = false;
01471       cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
01472             cb_outside, cb_outside_end, currentBox);
01473       return;
01474     }
01475 
01476     bool seekOutside = false, seekOutsideEnd = false; // silence gcc uninit warning
01477     CaretBoxIterator it;
01478     cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
01479     flowBox, flowBox->firstChild(), seekOutside, seekOutsideEnd, it);
01480   }
01481 }
01482 
01483 void LineIterator::advance(bool toBegin)
01484 {
01485   InlineFlowBox *flowBox = cbl->baseFlowBox();
01486   if (flowBox) {
01487     flowBox = static_cast<InlineFlowBox *>(toBegin ? flowBox->prevLineBox() : flowBox->nextLineBox());
01488     if (flowBox) {
01489       bool seekOutside = false, seekOutsideEnd = false; // silence gcc uninit warning
01490       CaretBoxIterator it;
01491       cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
01492           flowBox, flowBox->firstChild(), seekOutside, seekOutsideEnd, it);
01493     }/*end if*/
01494   }/*end if*/
01495 
01496   // if there are no more lines in this block, move towards block to come
01497   if (!flowBox) { if (toBegin) prevBlock(); else nextBlock(); }
01498 
01499 #if DEBUG_CARETMODE > 3
01500   if (cbl) kdDebug(6200) << cbl->information() << endl;
01501 #endif
01502 }
01503 
01504 // == class EditableCaretBoxIterator implementation
01505 
01506 void EditableCaretBoxIterator::advance(bool toBegin)
01507 {
01508 #if DEBUG_CARETMODE > 3
01509       kdDebug(6200) << "---------------" << k_funcinfo << "toBegin " << toBegin << endl;
01510 #endif
01511   const CaretBoxIterator preBegin = cbl->preBegin();
01512   const CaretBoxIterator end = cbl->end();
01513 
01514   CaretBoxIterator lastbox = *this, curbox;
01515   bool islastuseable = true;    // silence gcc
01516   bool iscuruseable;
01517   // Assume adjacency of caret boxes. Will be falsified later if applicable.
01518   adjacent = true;
01519 
01520 #if DEBUG_CARETMODE > 4
01521 //       kdDebug(6200) << "ebit::advance: before: " << (**this)->object() << "@" << (**this)->object()->renderName() << ".node " << (**this)->object()->element() << "[" << ((**this)->object()->element() ? (**this)->object()->element()->nodeName().string() : QString::null) << "] inline " << (**this)->isInline() << " outside " << (**this)->isOutside() << " outsideEnd " << (**this)->isOutsideEnd() << endl;
01522 #endif
01523 
01524   if (toBegin) CaretBoxIterator::operator --(); else CaretBoxIterator::operator ++();
01525   bool curAtEnd = *this == preBegin || *this == end;
01526   curbox = *this;
01527   bool atEnd = true;
01528   if (!curAtEnd) {
01529     iscuruseable = isEditable(curbox, toBegin);
01530     if (toBegin) CaretBoxIterator::operator --(); else CaretBoxIterator::operator ++();
01531     atEnd = *this == preBegin || *this == end;
01532   }
01533   while (!curAtEnd) {
01534     bool haslast = lastbox != end && lastbox != preBegin;
01535     bool hascoming = !atEnd;
01536     bool iscominguseable = true; // silence gcc
01537 
01538     if (!atEnd) iscominguseable = isEditable(*this, toBegin);
01539     if (iscuruseable) {
01540 #if DEBUG_CARETMODE > 3
01541       kdDebug(6200) << "ebit::advance: " << (*curbox)->object() << "@" << (*curbox)->object()->renderName() << ".node " << (*curbox)->object()->element() << "[" << ((*curbox)->object()->element() ? (*curbox)->object()->element()->nodeName().string() : QString::null) << "] inline " << (*curbox)->isInline() << " outside " << (*curbox)->isOutside() << " outsideEnd " << (*curbox)->isOutsideEnd() << endl;
01542 #endif
01543 
01544       CaretBox *box = *curbox;
01545       if (box->isOutside()) {
01546         // if this caret box represents no inline box, it is an outside box
01547     // which has to be considered unconditionally
01548         if (!box->isInline()) break;
01549 
01550         if (advpol == VisibleFlows) break;
01551 
01552     // IndicatedFlows and LeafsOnly are treated equally in caret box lines
01553 
01554     InlineBox *ibox = box->inlineBox();
01555     // get previous inline box
01556     InlineBox *prev = box->isOutsideEnd() ? ibox : ibox->prevOnLine();
01557     // get next inline box
01558     InlineBox *next = box->isOutsideEnd() ? ibox->nextOnLine() : ibox;
01559 
01560     const bool isprevindicated = !prev || isIndicatedInlineBox(prev);
01561     const bool isnextindicated = !next || isIndicatedInlineBox(next);
01562     const bool last = haslast && !islastuseable;
01563     const bool coming = hascoming && !iscominguseable;
01564     const bool left = !prev || prev->isInlineFlowBox() && isprevindicated
01565         || (toBegin && coming || !toBegin && last);
01566     const bool right = !next || next->isInlineFlowBox() && isnextindicated
01567         || (!toBegin && coming || toBegin && last);
01568         const bool text2indicated = toBegin && next && next->isInlineTextBox()
01569                 && isprevindicated
01570             || !toBegin && prev && prev->isInlineTextBox() && isnextindicated;
01571         const bool indicated2text = !toBegin && next && next->isInlineTextBox()
01572                 && prev && isprevindicated
01573             // ### this code is so broken.
01574             /*|| toBegin && prev && prev->isInlineTextBox() && isnextindicated*/;
01575 #if DEBUG_CARETMODE > 5
01576       kdDebug(6200) << "prev " << prev << " haslast " << haslast << " islastuseable " << islastuseable << " left " << left << " next " << next << " hascoming " << hascoming << " iscominguseable " << iscominguseable << " right " << right << " text2indicated " << text2indicated << " indicated2text " << indicated2text << endl;
01577 #endif
01578 
01579     if (left && right && !text2indicated || indicated2text) {
01580       adjacent = false;
01581 #if DEBUG_CARETMODE > 4
01582       kdDebug(6200) << "left && right && !text2indicated || indicated2text" << endl;
01583 #endif
01584       break;
01585     }
01586 
01587       } else {
01588         // inside boxes are *always* valid
01589 #if DEBUG_CARETMODE > 4
01590 if (box->isInline()) {
01591         InlineBox *ibox = box->inlineBox();
01592       kdDebug(6200) << "inside " << (!ibox->isInlineFlowBox() || static_cast<InlineFlowBox *>(ibox)->firstChild() ? "non-empty" : "empty") << (isIndicatedInlineBox(ibox) ? " indicated" : "") << " adjacent=" << adjacent << endl;
01593     }
01594 #if 0
01595   RenderStyle *s = ibox->object()->style();
01596   kdDebug(6200) << "bordls " << s->borderLeftStyle()
01597         << " bordl " << (s->borderLeftStyle() != BNONE)
01598         << " bordr " << (s->borderRightStyle() != BNONE)
01599         << " bordt " << (s->borderTopStyle() != BNONE)
01600             << " bordb " << (s->borderBottomStyle() != BNONE)
01601         << " padl " << s->paddingLeft().value()
01602                 << " padr " << s->paddingRight().value()
01603             << " padt " << s->paddingTop().value()
01604                 << " padb " << s->paddingBottom().value()
01605     // ### Can inline elements have top/bottom margins? Couldn't find
01606     // it in the CSS 2 spec, but Mozilla ignores them, so we do, too.
01607         << " marl " << s->marginLeft().value()
01608                 << " marr " << s->marginRight().value()
01609             << endl;
01610 #endif
01611 #endif
01612         break;
01613       }/*end if*/
01614 
01615     } else {
01616 
01617       if (!(*curbox)->isOutside()) {
01618         // cannot be adjacent anymore
01619     adjacent = false;
01620       }
01621 
01622     }/*end if*/
01623     lastbox = curbox;
01624     islastuseable = iscuruseable;
01625     curbox = *this;
01626     iscuruseable = iscominguseable;
01627     curAtEnd = atEnd;
01628     if (!atEnd) {
01629       if (toBegin) CaretBoxIterator::operator --(); else CaretBoxIterator::operator ++();
01630       atEnd = *this == preBegin || *this == end;
01631     }/*end if*/
01632   }/*wend*/
01633 
01634   *static_cast<CaretBoxIterator *>(this) = curbox;
01635 #if DEBUG_CARETMODE > 4
01636 //  kdDebug(6200) << "still valid? " << (*this != preBegin && *this != end) << endl;
01637 #endif
01638 #if DEBUG_CARETMODE > 3
01639       kdDebug(6200) << "---------------" << k_funcinfo << "end " << endl;
01640 #endif
01641 }
01642 
01643 bool EditableCaretBoxIterator::isEditable(const CaretBoxIterator &boxit, bool fromEnd)
01644 {
01645   Q_ASSERT(boxit != cbl->end() && boxit != cbl->preBegin());
01646   CaretBox *b = *boxit;
01647   RenderObject *r = b->object();
01648 #if DEBUG_CARETMODE > 0
01649 //  if (b->isInlineFlowBox()) kdDebug(6200) << "b is inline flow box" << (outside ? " (outside)" : "") << endl;
01650   kdDebug(6200) << "isEditable r" << r << ": " << (r ? r->renderName() : QString::null) << (r && r->isText() ? " contains \"" + QString(((RenderText *)r)->str->s, QMIN(((RenderText *)r)->str->l,15)) + "\"" : QString::null) << endl;
01651 #endif
01652   // Must check caret mode or design mode *after* r->element(), otherwise
01653   // lines without a backing DOM node get regarded, leading to a crash.
01654   // ### check should actually be in InlineBoxIterator
01655   NodeImpl *node = r->element();
01656   ObjectTraversalState trav;
01657   mapRenderPosToTraversalState(b->isOutside(), b->isOutsideEnd(), fromEnd, trav);
01658   if (isUnsuitable(r, trav) || !node) {
01659     return false;
01660   }
01661 
01662   // generally exclude replaced elements with no children from navigation
01663   if (!b->isOutside() && r->isRenderReplaced() && !r->firstChild())
01664     return false;
01665 
01666   RenderObject *eff_r = r;
01667   bool globallyNavigable = m_part->isCaretMode() || m_part->isEditable();
01668 
01669   // calculate the parent element's editability if this inline box is outside.
01670   if (b->isOutside() && !globallyNavigable) {
01671     NodeImpl *par = node->parent();
01672     // I wonder whether par can be 0. It shouldn't be possible if the
01673     // algorithm contained no bugs.
01674     Q_ASSERT(par);
01675     if (par) node = par;
01676     eff_r = node->renderer();
01677     Q_ASSERT(eff_r);    // this is a hard requirement
01678   }
01679 
01680   bool result = globallyNavigable || eff_r->style()->userInput() == UI_ENABLED;
01681 #if DEBUG_CARETMODE > 0
01682   kdDebug(6200) << result << endl;
01683 #endif
01684   return result;
01685 }
01686 
01687 // == class EditableLineIterator implementation
01688 
01689 void EditableLineIterator::advance(bool toBegin)
01690 {
01691   CaretAdvancePolicy advpol = lines->advancePolicy();
01692   LineIterator lasteditable, lastindicated;
01693   bool haslasteditable = false;
01694   bool haslastindicated = false;
01695   bool uselasteditable = false;
01696 
01697   LineIterator::advance(toBegin);
01698   while (cbl) {
01699     if (isEditable(*this)) {
01700 #if DEBUG_CARETMODE > 3
01701       kdDebug(6200) << "advance: " << cbl->enclosingObject() << "@" << cbl->enclosingObject()->renderName() << ".node " << cbl->enclosingObject()->element() << "[" << (cbl->enclosingObject()->element() ? cbl->enclosingObject()->element()->nodeName().string() : QString::null) << "]" << endl;
01702 #endif
01703 
01704       bool hasindicated = isIndicatedFlow(cbl->enclosingObject());
01705       if (hasindicated) {
01706         haslastindicated = true;
01707         lastindicated = *this;
01708       }
01709 
01710       switch (advpol) {
01711         case IndicatedFlows:
01712           if (hasindicated) goto wend;
01713           // fall through
01714         case LeafsOnly:
01715           if (cbl->isOutside()) break;
01716           // fall through
01717         case VisibleFlows: goto wend;
01718       }/*end switch*/
01719 
01720       // remember rejected editable element
01721       lasteditable = *this;
01722       haslasteditable = true;
01723 #if DEBUG_CARETMODE > 4
01724       kdDebug(6200) << "remembered lasteditable " << *lasteditable << endl;
01725 #endif
01726     } else {
01727 
01728       // If this element isn't editable, but the last one was, and it was only
01729       // rejected because it didn't match the caret advance policy, force it.
01730       // Otherwise certain combinations of editable and uneditable elements
01731       // could never be reached with some policies.
01732       if (haslasteditable) { uselasteditable = true; break; }
01733 
01734     }
01735     LineIterator::advance(toBegin);
01736   }/*wend*/
01737 wend:
01738 
01739   if (uselasteditable) *this = haslastindicated ? lastindicated : lasteditable;
01740   if (!cbl && haslastindicated) *this = lastindicated;
01741 }
01742 
01743 // == class EditableCharacterIterator implementation
01744 
01745 void EditableCharacterIterator::initFirstChar()
01746 {
01747   CaretBox *box = *ebit;
01748   InlineBox *b = box->inlineBox();
01749   if (_offset == box->maxOffset())
01750     peekNext();
01751   else if (b && !box->isOutside() && b->isInlineTextBox())
01752     _char = static_cast<RenderText *>(b->object())->str->s[_offset].unicode();
01753   else
01754     _char = -1;
01755 }
01756 
01760 static inline bool isCaretBoxEmpty(CaretBox *box) {
01761   if (!box->isInline()) return false;
01762   InlineBox *ibox = box->inlineBox();
01763   return ibox->isInlineFlowBox()
01764         && !static_cast<InlineFlowBox *>(ibox)->firstChild()
01765             && !isIndicatedInlineBox(ibox);
01766 }
01767 
01768 EditableCharacterIterator &EditableCharacterIterator::operator ++()
01769 {
01770   _offset++;
01771 
01772   CaretBox *box = *ebit;
01773   InlineBox *b = box->inlineBox();
01774   long maxofs = box->maxOffset();
01775 #if DEBUG_CARETMODE > 0
01776   kdDebug(6200) << "box->maxOffset() " << box->maxOffset() << " box->minOffset() " << box->minOffset() << endl;
01777 #endif
01778   if (_offset == maxofs) {
01779 #if DEBUG_CARETMODE > 2
01780 kdDebug(6200) << "_offset == maxofs: " << _offset << " == " << maxofs << endl;
01781 #endif
01782     peekNext();
01783   } else if (_offset > maxofs) {
01784 #if DEBUG_CARETMODE > 2
01785 kdDebug(6200) << "_offset > maxofs: " << _offset << " > " << maxofs /*<< " _peekNext: " << _peekNext*/ << endl;
01786 #endif
01787     if (true) {
01788       ++ebit;
01789       if (ebit == (*_it)->end()) {  // end of line reached, go to next line
01790         ++_it;
01791 #if DEBUG_CARETMODE > 3
01792 kdDebug(6200) << "++_it" << endl;
01793 #endif
01794         if (_it != _it.lines->end()) {
01795       ebit = _it;
01796           box = *ebit;
01797           b = box->inlineBox();
01798 #if DEBUG_CARETMODE > 3
01799 kdDebug(6200) << "box " << box << " b " << b << " isText " << box->isInlineTextBox() << endl;
01800 #endif
01801 
01802 #if DEBUG_CARETMODE > 3
01803       RenderObject *_r = box->object();
01804 kdDebug(6200) << "_r " << _r << ":" << _r->element()->nodeName().string() << endl;
01805 #endif
01806       _offset = box->minOffset();
01807 #if DEBUG_CARETMODE > 3
01808 kdDebug(6200) << "_offset " << _offset << endl;
01809 #endif
01810     } else {
01811           b = 0;
01812       _end = true;
01813     }/*end if*/
01814         goto readchar;
01815       }/*end if*/
01816     }/*end if*/
01817 
01818     bool adjacent = ebit.isAdjacent();
01819 #if 0
01820     // Jump over element if this one is not a text node.
01821     if (adjacent && !(*ebit)->isInlineTextBox()) {
01822       EditableCaretBoxIterator copy = ebit;
01823       ++ebit;
01824       if (ebit != (*_it)->end() && (*ebit)->isInlineTextBox()
01825           /*&& (!(*ebit)->isInlineFlowBox()
01826               || static_cast<InlineFlowBox *>(*ebit)->)*/)
01827         adjacent = false;
01828       else ebit = copy;
01829     }/*end if*/
01830 #endif
01831     // Jump over empty elements.
01832     if (adjacent && !(*ebit)->isInlineTextBox()) {
01833       bool noemptybox = true;
01834       while (isCaretBoxEmpty(*ebit)) {
01835         noemptybox = false;
01836         EditableCaretBoxIterator copy = ebit;
01837         ++ebit;
01838         if (ebit == (*_it)->end()) { ebit = copy; break; }
01839       }
01840       if (noemptybox) adjacent = false;
01841     }/*end if*/
01842 //     _r = (*ebit)->object();
01843     /*if (!_it.outside) */_offset = (*ebit)->minOffset() + adjacent;
01844     //_peekNext = 0;
01845     box = *ebit;
01846     b = box->inlineBox();
01847     goto readchar;
01848   } else {
01849 readchar:
01850     // get character
01851     if (b && !box->isOutside() && b->isInlineTextBox() && _offset < b->maxOffset())
01852       _char = static_cast<RenderText *>(b->object())->str->s[_offset].unicode();
01853     else
01854       _char = -1;
01855   }/*end if*/
01856 #if DEBUG_CARETMODE > 2
01857 kdDebug(6200) << "_offset: " << _offset /*<< " _peekNext: " << _peekNext*/ << " char '" << (char)_char << "'" << endl;
01858 #endif
01859 
01860 #if DEBUG_CARETMODE > 0
01861   if (!_end && ebit != (*_it)->end()) {
01862     CaretBox *box = *ebit;
01863     RenderObject *_r = box->object();
01864     kdDebug(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::null) << " _r " << (_r ? _r->element()->nodeName().string() : QString("<nil>")) << endl;
01865   }
01866 #endif
01867   return *this;
01868 }
01869 
01870 EditableCharacterIterator &EditableCharacterIterator::operator --()
01871 {
01872   _offset--;
01873   //kdDebug(6200) << "--: _offset=" << _offset << endl;
01874 
01875   CaretBox *box = *ebit;
01876   CaretBox *_peekPrev = 0;
01877   CaretBox *_peekNext = 0;
01878   InlineBox *b = box->inlineBox();
01879   long minofs = box->minOffset();
01880 #if DEBUG_CARETMODE > 0
01881   kdDebug(6200) << "box->maxOffset() " << box->maxOffset() << " box->minOffset() " << box->minOffset() << endl;
01882 #endif
01883   if (_offset == minofs) {
01884 #if DEBUG_CARETMODE > 2
01885 kdDebug(6200) << "_offset == minofs: " << _offset << " == " << minofs << endl;
01886 #endif
01887 //     _peekNext = b;
01888     // get character
01889     if (b && !box->isOutside() && b->isInlineTextBox())
01890       _char = static_cast<RenderText *>(b->object())->text()[_offset].unicode();
01891     else
01892       _char = -1;
01893 
01894     //peekPrev();
01895     bool do_prev = false;
01896     {
01897       EditableCaretBoxIterator copy;
01898       _peekPrev = 0;
01899       do {
01900         copy = ebit;
01901         --ebit;
01902         if (ebit == (*_it)->preBegin()) { ebit = copy; break; }
01903       } while (isCaretBoxEmpty(*ebit));
01904       // Jump to end of previous element if it's adjacent, and a text box
01905       if (ebit.isAdjacent() && ebit != (*_it)->preBegin() && (*ebit)->isInlineTextBox()) {
01906         _peekPrev = *ebit;
01907     do_prev = true;
01908       } else
01909         ebit = copy;
01910     }
01911     if (do_prev) goto prev;
01912   } else if (_offset < minofs) {
01913 prev:
01914 #if DEBUG_CARETMODE > 2
01915 kdDebug(6200) << "_offset < minofs: " << _offset << " < " << minofs /*<< " _peekNext: " << _peekNext*/ << endl;
01916 #endif
01917     if (!_peekPrev) {
01918       _peekNext = *ebit;
01919       --ebit;
01920       if (ebit == (*_it)->preBegin()) { // end of line reached, go to previous line
01921         --_it;
01922 #if DEBUG_CARETMODE > 3
01923 kdDebug(6200) << "--_it" << endl;
01924 #endif
01925         if (_it != _it.lines->preBegin()) {
01926 //    kdDebug(6200) << "begin from end!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << endl;
01927       ebit = EditableCaretBoxIterator(_it, true);
01928           box = *ebit;
01929 //    RenderObject *r = box->object();
01930 #if DEBUG_CARETMODE > 3
01931 kdDebug(6200) << "box " << box << " b " << box->inlineBox() << " isText " << box->isInlineTextBox() << endl;
01932 #endif
01933           _offset = box->maxOffset();
01934 //    if (!_it.outside) _offset = r->isBR() ? (*ebit)->minOffset() : (*ebit)->maxOffset();
01935       _char = -1;
01936 #if DEBUG_CARETMODE > 0
01937           kdDebug(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::null) << endl;
01938 #endif
01939     } else
01940       _end = true;
01941     return *this;
01942       }/*end if*/
01943     }/*end if*/
01944 
01945 #if DEBUG_CARETMODE > 0
01946     bool adjacent = ebit.isAdjacent();
01947     kdDebug(6200) << "adjacent " << adjacent << " _peekNext " << _peekNext << " _peekNext->isInlineTextBox: " << (_peekNext ? _peekNext->isInlineTextBox() : false) << " !((*ebit)->isInlineTextBox): " << (*ebit ? !(*ebit)->isInlineTextBox() : true) << endl;
01948 #endif
01949 #if 0
01950     // Ignore this box if it isn't a text box, but the previous box was
01951     if (adjacent && _peekNext && _peekNext->isInlineTextBox()
01952         && !(*ebit)->isInlineTextBox()) {
01953       EditableCaretBoxIterator copy = ebit;
01954       --ebit;
01955       if (ebit == (*_it)->preBegin()) /*adjacent = false;
01956       else */ebit = copy;
01957     }/*end if*/
01958 #endif
01959 #if 0
01960     // Jump over empty elements.
01961     if (adjacent //&& _peekNext && _peekNext->isInlineTextBox()
01962         && !(*ebit)->isInlineTextBox()) {
01963       bool noemptybox = true;
01964       while (isCaretBoxEmpty(*ebit)) {
01965         noemptybox = false;
01966         EditableCaretBoxIterator copy = ebit;
01967         --ebit;
01968         if (ebit == (*_it)->preBegin()) { ebit = copy; break; }
01969         else _peekNext = *copy;
01970       }
01971       if (noemptybox) adjacent = false;
01972     }/*end if*/
01973 #endif
01974 #if DEBUG_CARETMODE > 0
01975     kdDebug(6200) << "(*ebit)->obj " << (*ebit)->object()->renderName() << "[" << (*ebit)->object() << "]" << " minOffset: " << (*ebit)->minOffset() << " maxOffset: " << (*ebit)->maxOffset() << endl;
01976 #endif
01977 #if DEBUG_CARETMODE > 3
01978     RenderObject *_r = (*ebit)->object();
01979 kdDebug(6200) << "_r " << _r << ":" << _r->element()->nodeName().string() << endl;
01980 #endif
01981     _offset = (*ebit)->maxOffset();
01982 //     if (!_it.outside) _offset = (*ebit)->maxOffset()/* - adjacent*/;
01983 #if DEBUG_CARETMODE > 3
01984 kdDebug(6200) << "_offset " << _offset << endl;
01985 #endif
01986     _peekPrev = 0;
01987   } else {
01988 #if DEBUG_CARETMODE > 0
01989 kdDebug(6200) << "_offset: " << _offset << " _peekNext: " << _peekNext << endl;
01990 #endif
01991     // get character
01992     if (_peekNext && _offset >= box->maxOffset() && _peekNext->isInlineTextBox())
01993       _char = static_cast<RenderText *>(_peekNext->object())->text()[_peekNext->minOffset()].unicode();
01994     else if (b && _offset < b->maxOffset() && b->isInlineTextBox())
01995       _char = static_cast<RenderText *>(b->object())->text()[_offset].unicode();
01996     else
01997       _char = -1;
01998   }/*end if*/
01999 
02000 #if DEBUG_CARETMODE > 0
02001   if (!_end && ebit != (*_it)->preBegin()) {
02002     CaretBox *box = *ebit;
02003     kdDebug(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::null) << endl;
02004   }
02005 #endif
02006   return *this;
02007 }
02008 
02009 // == class TableRowIterator implementation
02010 
02011 TableRowIterator::TableRowIterator(RenderTable *table, bool fromEnd,
02012         RenderTableSection::RowStruct *row)
02013         : sec(table, fromEnd)
02014 {
02015   // set index
02016   if (*sec) {
02017     if (fromEnd) index = (*sec)->grid.size() - 1;
02018     else index = 0;
02019   }/*end if*/
02020 
02021   // initialize with given row
02022   if (row && *sec) {
02023     while (operator *() != row)
02024       if (fromEnd) operator --(); else operator ++();
02025   }/*end if*/
02026 }
02027 
02028 TableRowIterator &TableRowIterator::operator ++()
02029 {
02030   index++;
02031 
02032   if (index >= (int)(*sec)->grid.size()) {
02033     ++sec;
02034 
02035     if (*sec) index = 0;
02036   }/*end if*/
02037   return *this;
02038 }
02039 
02040 TableRowIterator &TableRowIterator::operator --()
02041 {
02042   index--;
02043 
02044   if (index < 0) {
02045     --sec;
02046 
02047     if (*sec) index = (*sec)->grid.size() - 1;
02048   }/*end if*/
02049   return *this;
02050 }
02051 
02052 // == class ErgonomicEditableLineIterator implementation
02053 
02054 // some decls
02055 static RenderTableCell *findNearestTableCellInRow(KHTMLPart *part, int x,
02056         RenderTableSection::RowStruct *row, bool fromEnd);
02057 
02071 static inline RenderTableCell *findNearestTableCell(KHTMLPart *part, int x,
02072         TableRowIterator &it, bool fromEnd)
02073 {
02074   RenderTableCell *result = 0;
02075 
02076   while (*it) {
02077     result = findNearestTableCellInRow(part, x, *it, fromEnd);
02078     if (result) break;
02079 
02080     if (fromEnd) --it; else ++it;
02081   }/*wend*/
02082 
02083   return result;
02084 }
02085 
02099 static RenderTableCell *findNearestTableCellInRow(KHTMLPart *part, int x,
02100         RenderTableSection::RowStruct *row, bool fromEnd)
02101 {
02102   // First pass. Find spatially nearest cell.
02103   int n = (int)row->row->size();
02104   int i;
02105   for (i = 0; i < n; i++) {
02106     RenderTableCell *cell = row->row->at(i);
02107     if (!cell || (long)cell == -1) continue;
02108 
02109     int absx, absy;
02110     cell->absolutePosition(absx, absy, false); // ### position: fixed?
02111 #if DEBUG_CARETMODE > 1
02112     kdDebug(6201) << "i/n " << i << "/" << n << " absx " << absx << " absy " << absy << endl;
02113 #endif
02114 
02115     // I rely on the assumption that all cells are in ascending visual order
02116     // ### maybe this assumption is wrong for bidi?
02117 #if DEBUG_CARETMODE > 1
02118     kdDebug(6201) << "x " << x << " < " << (absx + cell->width()) << "?" << endl;
02119 #endif
02120     if (x < absx + cell->width()) break;
02121   }/*next i*/
02122   if (i >= n) i = n - 1;
02123 
02124   // Second pass. Find editable cell, beginning with the currently found,
02125   // extending to the left, and to the right, alternating.
02126   for (int cnt = 0; cnt < 2*n; cnt++) {
02127     int index = i - ((cnt >> 1) + 1)*(cnt & 1) + (cnt >> 1)*!(cnt & 1);
02128     if (index < 0 || index >= n) continue;
02129 
02130     RenderTableCell *cell = row->row->at(index);
02131     if (!cell || (long)cell == -1) continue;
02132 
02133 #if DEBUG_CARETMODE > 1
02134     kdDebug(6201) << "index " << index << " cell " << cell << endl;
02135 #endif
02136     RenderTable *nestedTable;
02137     if (containsEditableElement(part, cell, nestedTable, fromEnd)) {
02138 
02139       if (nestedTable) {
02140         TableRowIterator it(nestedTable, fromEnd);
02141     while (*it) {
02142 // kdDebug(6201) << "=== recursive invocation" << endl;
02143           cell = findNearestTableCell(part, x, it, fromEnd);
02144       if (cell) break;
02145       if (fromEnd) --it; else ++it;
02146     }/*wend*/
02147       }/*end if*/
02148 
02149       return cell;
02150     }/*end if*/
02151   }/*next i*/
02152   return 0;
02153 }
02154 
02161 static RenderObject *commonAncestorTableSectionOrCell(RenderObject *r1,
02162         RenderObject *r2)
02163 {
02164   if (!r1 || !r2) return 0;
02165   RenderTableSection *sec = 0;
02166   int start_depth=0, end_depth=0;
02167   // First we find the depths of the two objects in the tree (start_depth, end_depth)
02168   RenderObject *n = r1;
02169   while (n->parent()) {
02170     n = n->parent();
02171     start_depth++;
02172   }/*wend*/
02173   n = r2;
02174   while( n->parent()) {
02175     n = n->parent();
02176     end_depth++;
02177   }/*wend*/
02178   // here we climb up the tree with the deeper object, until both objects have equal depth
02179   while (end_depth > start_depth) {
02180     r2 = r2->parent();
02181     end_depth--;
02182   }/*wend*/
02183   while (start_depth > end_depth) {
02184     r1 = r1->parent();
02185 //    if (r1->isTableSection()) sec = static_cast<RenderTableSection *>(r1);
02186     start_depth--;
02187   }/*wend*/
02188   // Climb the tree with both r1 and r2 until they are the same
02189   while (r1 != r2){
02190     r1 = r1->parent();
02191     if (r1->isTableSection()) sec = static_cast<RenderTableSection *>(r1);
02192     r2 = r2->parent();
02193   }/*wend*/
02194 
02195   // At this point, we found the most approximate common ancestor. Now climb
02196   // up until the condition of the function return value is satisfied.
02197   while (r1 && !r1->isTableCell() && !r1->isTableSection() && !r1->isTable())
02198     r1 = r1->parent();
02199 
02200   return r1 && r1->isTable() ? sec : r1;
02201 }
02202 
02210 static int findRowInSection(RenderTableSection *section, RenderTableCell *cell,
02211         RenderTableSection::RowStruct *&row, RenderTableCell *&directCell)
02212 {
02213   // Seek direct cell
02214   RenderObject *r = cell;
02215   while (r != section) {
02216     if (r->isTableCell()) directCell = static_cast<RenderTableCell *>(r);
02217     r = r->parent();
02218   }/*wend*/
02219 
02220   // So, and this is really nasty: As we have no indices, we have to do a
02221   // linear comparison. Oh, that sucks so much for long tables, you can't
02222   // imagine.
02223   int n = section->numRows();
02224   for (int i = 0; i < n; i++) {
02225     row = &section->grid[i];
02226 
02227     // check for cell
02228     int m = row->row->size();
02229     for (int j = 0; j < m; j++) {
02230       RenderTableCell *c = row->row->at(j);
02231       if (c == directCell) return i;
02232     }/*next j*/
02233 
02234   }/*next i*/
02235   Q_ASSERT(false);
02236   return -1;
02237 }
02238 
02244 static inline RenderTable *findFirstDescendantTable(RenderObject *leaf, RenderBlock *block)
02245 {
02246   RenderTable *result = 0;
02247   while (leaf && leaf != block) {
02248     if (leaf->isTable()) result = static_cast<RenderTable *>(leaf);
02249     leaf = leaf->parent();
02250   }/*wend*/
02251   return result;
02252 }
02253 
02257 static inline RenderTableCell *containingTableCell(RenderObject *r)
02258 {
02259   while (r && !r->isTableCell()) r = r->parent();
02260   return static_cast<RenderTableCell *>(r);
02261 }
02262 
02263 inline void ErgonomicEditableLineIterator::calcAndStoreNewLine(
02264             RenderBlock *newBlock, bool toBegin)
02265 {
02266   // take the first/last editable element in the found cell as the new
02267   // value for the iterator
02268   CaretBoxIterator it;
02269   cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
02270     newBlock, true, toBegin, it);
02271 #if DEBUG_CARETMODE > 3
02272   kdDebug(6201) << cbl->information() << endl;
02273 #endif
02274 //  if (toBegin) prevBlock(); else nextBlock();
02275 
02276   if (!cbl) {
02277     return;
02278   }/*end if*/
02279 
02280   EditableLineIterator::advance(toBegin);
02281 }
02282 
02283 void ErgonomicEditableLineIterator::determineTopologicalElement(
02284         RenderTableCell *oldCell, RenderObject *newObject, bool toBegin)
02285 {
02286   // When we arrive here, a transition between cells has happened.
02287   // Now determine the type of the transition. This can be
02288   // (1) a transition from this cell into a table inside this cell.
02289   // (2) a transition from this cell into another cell of this table
02290 
02291   TableRowIterator it;
02292 
02293   RenderObject *commonAncestor = commonAncestorTableSectionOrCell(oldCell, newObject);
02294 #if DEBUG_CARETMODE > 1
02295   kdDebug(6201) << " ancestor " << commonAncestor << endl;
02296 #endif
02297 
02298   // The whole document is treated as a table cell.
02299   if (!commonAncestor || commonAncestor->isTableCell()) {   // (1)
02300 
02301     RenderTableCell *cell = static_cast<RenderTableCell *>(commonAncestor);
02302     RenderTable *table = findFirstDescendantTable(newObject, cell);
02303 
02304 #if DEBUG_CARETMODE > 0
02305     kdDebug(6201) << "table cell: " << cell << endl;
02306 #endif
02307 
02308     // if there is no table, we fell out of the previous table, and are now
02309     // in some table-less block. Therefore, done.
02310     if (!table) return;
02311 
02312     it = TableRowIterator(table, toBegin);
02313 
02314   } else if (commonAncestor->isTableSection()) {        // (2)
02315 
02316     RenderTableSection *section = static_cast<RenderTableSection *>(commonAncestor);
02317     RenderTableSection::RowStruct *row;
02318     int idx = findRowInSection(section, oldCell, row, oldCell);
02319 #if DEBUG_CARETMODE > 1
02320     kdDebug(6201) << "table section: row idx " << idx << endl;
02321 #endif
02322 
02323     it = TableRowIterator(section, idx);
02324 
02325     // advance rowspan rows
02326     int rowspan = oldCell->rowSpan();
02327     while (*it && rowspan--) {
02328       if (toBegin) --it; else ++it;
02329     }/*wend*/
02330 
02331   } else {
02332     kdError(6201) << "Neither common cell nor section! " << commonAncestor->renderName() << endl;
02333     // will crash on uninitialized table row iterator
02334   }/*end if*/
02335 
02336   RenderTableCell *cell = findNearestTableCell(lines->m_part, xCoor, it, toBegin);
02337 #if DEBUG_CARETMODE > 1
02338   kdDebug(6201) << "findNearestTableCell result: " << cell << endl;
02339 #endif
02340 
02341   RenderBlock *newBlock = cell;
02342   if (!cell) {
02343     Q_ASSERT(commonAncestor->isTableSection());
02344     RenderTableSection *section = static_cast<RenderTableSection *>(commonAncestor);
02345     cell = containingTableCell(section);
02346 #if DEBUG_CARETMODE > 1
02347     kdDebug(6201) << "containing cell: " << cell << endl;
02348 #endif
02349 
02350     RenderTable *nestedTable;
02351     bool editableChild = cell && containsEditableChildElement(lines->m_part,
02352             cell, nestedTable, toBegin, section->table());
02353 
02354     if (cell && !editableChild) {
02355 #if DEBUG_CARETMODE > 1
02356       kdDebug(6201) << "========= recursive invocation outer =========" << endl;
02357 #endif
02358       determineTopologicalElement(cell, cell->section(), toBegin);
02359 #if DEBUG_CARETMODE > 1
02360       kdDebug(6201) << "========= end recursive invocation outer =========" << endl;
02361 #endif
02362       return;
02363 
02364     } else if (cell && nestedTable) {
02365 #if DEBUG_CARETMODE > 1
02366       kdDebug(6201) << "========= recursive invocation inner =========" << endl;
02367 #endif
02368       determineTopologicalElement(cell, nestedTable, toBegin);
02369 #if DEBUG_CARETMODE > 1
02370       kdDebug(6201) << "========= end recursive invocation inner =========" << endl;
02371 #endif
02372       return;
02373 
02374     } else {
02375 #if DEBUG_CARETMODE > 1
02376       kdDebug(6201) << "newBlock is table: " << section->table() << endl;
02377 #endif
02378       RenderObject *r = section->table();
02379       int state;        // not used
02380       ObjectTraversalState trav = OutsideAscending;
02381       r = advanceSuitableObject(r, trav, toBegin, lines->baseObject(), state);
02382       if (!r) { cbl = 0;  return; }
02383 //      if (toBegin) prevBlock(); else nextBlock();
02384       newBlock = static_cast<RenderBlock *>(!r || r->isRenderBlock() ? r : r->containingBlock());
02385     }/*end if*/
02386 #if 0
02387   } else {
02388     // adapt cell so that prevBlock/nextBlock works as expected
02389     newBlock = cell;
02390     // on forward advancing, we must start from the outside end of the
02391     // previous object
02392     if (!toBegin) {
02393       RenderObject *r = newBlock;
02394       int state;        // not used
02395       ObjectTraversalState trav = OutsideAscending;
02396       r = advanceSuitableObject(r, trav, true, lines->advancePolicy(), lines->baseObject(), state);
02397       newBlock = static_cast<RenderBlock *>(!r || r->isRenderBlock() ? r : r->containingBlock());
02398     }/*end if*/
02399 #endif
02400   }/*end if*/
02401 
02402   calcAndStoreNewLine(newBlock, toBegin);
02403 }
02404 
02405 ErgonomicEditableLineIterator &ErgonomicEditableLineIterator::operator ++()
02406 {
02407   RenderTableCell *oldCell = containingTableCell(cbl->enclosingObject());
02408 
02409   EditableLineIterator::operator ++();
02410   if (*this == lines->end() || *this == lines->preBegin()) return *this;
02411 
02412   RenderTableCell *newCell = containingTableCell(cbl->enclosingObject());
02413 
02414   if (!newCell || newCell == oldCell) return *this;
02415 
02416   determineTopologicalElement(oldCell, newCell, false);
02417 
02418   return *this;
02419 }
02420 
02421 ErgonomicEditableLineIterator &ErgonomicEditableLineIterator::operator --()
02422 {
02423   RenderTableCell *oldCell = containingTableCell(cbl->enclosingObject());
02424 
02425   EditableLineIterator::operator --();
02426   if (*this == lines->end() || *this == lines->preBegin()) return *this;
02427 
02428   RenderTableCell *newCell = containingTableCell(cbl->enclosingObject());
02429 
02430   if (!newCell || newCell == oldCell) return *this;
02431 
02432   determineTopologicalElement(oldCell, newCell, true);
02433 
02434   return *this;
02435 }
02436 
02437 // == Navigational helper functions ==
02438 
02448 static CaretBox *nearestCaretBox(LineIterator &it, CaretViewContext *cv,
02449     int &x, int &absx, int &absy)
02450 {
02451   // Find containing block
02452   RenderObject *cb = (*it)->containingBlock();
02453 #if DEBUG_CARETMODE > 4
02454   kdDebug(6200) << "nearestCB: cb " << cb << "@" << (cb ? cb->renderName() : "") << endl;
02455 #endif
02456 
02457   if (cb) cb->absolutePosition(absx, absy);
02458   else absx = absy = 0;
02459 
02460   // Otherwise find out in which inline box the caret is to be placed.
02461 
02462   // this horizontal position is to be approximated
02463   x = cv->origX - absx;
02464   CaretBox *caretBox = 0; // Inline box containing the caret
02465 //  NodeImpl *lastnode = 0;  // node of previously checked render object.
02466   int xPos;        // x-coordinate of current inline box
02467   int oldXPos = -1;    // x-coordinate of last inline box
02468   EditableCaretBoxIterator fbit = it;
02469 #if DEBUG_CARETMODE > 0
02470 /*  if (it.linearDocument()->advancePolicy() != LeafsOnly)
02471     kdWarning() << "nearestInlineBox is only prepared to handle the LeafsOnly advance policy" << endl;*/
02472 //   kdDebug(6200) << "*fbit = " << *fbit << endl;
02473 #endif
02474   // Iterate through all children
02475   for (CaretBox *b; fbit != (*it)->end(); ++fbit) {
02476     b = *fbit;
02477 
02478 #if DEBUG_CARETMODE > 0
02479 //    RenderObject *r = b->object();
02480 //  if (b->isInlineFlowBox()) kdDebug(6200) << "b is inline flow box" << endl;
02481 //  kdDebug(6200) << "approximate r" << r << ": " << (r ? r->renderName() : QString::null) << (r && r->isText() ? " contains \"" + QString(((RenderText *)r)->str->s, ((RenderText *)r)->str->l) + "\"" : QString::null) << endl;
02482 #endif
02483     xPos = b->xPos();
02484 
02485     // the caret is before this box
02486     if (x < xPos) {
02487       // snap to nearest box
02488       if (oldXPos < 0 || x - (oldXPos + caretBox->width()) > xPos - x) {
02489     caretBox = b;   // current box is nearer
02490       }/*end if*/
02491       break;        // Otherwise, preceding box is implicitly used
02492     }
02493 
02494     caretBox = b;
02495 
02496     // the caret is within this box
02497     if (x >= xPos && x < xPos + caretBox->width())
02498       break;
02499     oldXPos = xPos;
02500 
02501     // the caret can only be after the last box which is automatically
02502     // contained in caretBox when we fall out of the loop.
02503   }/*next fbit*/
02504 
02505   return caretBox;
02506 }
02507 
02513 static void moveItToNextWord(EditableCharacterIterator &it)
02514 {
02515 #if DEBUG_CARETMODE > 0
02516   kdDebug(6200) << "%%%%%%%%%%%%%%%%%%%%% moveItToNextWord" << endl;
02517 #endif
02518   EditableCharacterIterator copy;
02519   while (!it.isEnd() && !(*it).isSpace() && !(*it).isPunct()) {
02520 #if DEBUG_CARETMODE > 2
02521     kdDebug(6200) << "reading1 '" << (*it).latin1() << "'" << endl;
02522 #endif
02523     copy = it;
02524     ++it;
02525   }
02526 
02527   if (it.isEnd()) {
02528     it = copy;
02529     return;
02530   }/*end if*/
02531 
02532   while (!it.isEnd() && ((*it).isSpace() || (*it).isPunct())) {
02533 #if DEBUG_CARETMODE > 2
02534     kdDebug(6200) << "reading2 '" << (*it).latin1() << "'" << endl;
02535 #endif
02536     copy = it;
02537     ++it;
02538   }
02539 
02540   if (it.isEnd()) it = copy;
02541 }
02542 
02548 static void moveItToPrevWord(EditableCharacterIterator &it)
02549 {
02550   if (it.isEnd()) return;
02551 
02552 #if DEBUG_CARETMODE > 0
02553   kdDebug(6200) << "%%%%%%%%%%%%%%%%%%%%% moveItToPrevWord" << endl;
02554 #endif
02555   EditableCharacterIterator copy;
02556 
02557   // Jump over all space and punctuation characters first
02558   do {
02559     copy = it;
02560     --it;
02561 #if DEBUG_CARETMODE > 2
02562     if (!it.isEnd()) kdDebug(6200) << "reading1 '" << (*it).latin1() << "'" << endl;
02563 #endif
02564   } while (!it.isEnd() && ((*it).isSpace() || (*it).isPunct()));
02565 
02566   if (it.isEnd()) {
02567     it = copy;
02568     return;
02569   }/*end if*/
02570 
02571   do {
02572     copy = it;
02573     --it;
02574 #if DEBUG_CARETMODE > 0
02575     if (!it.isEnd()) kdDebug(6200) << "reading2 '" << (*it).latin1() << "' (" << (int)(*it).latin1() << ") box " << it.caretBox() << endl;
02576 #endif
02577   } while (!it.isEnd() && !(*it).isSpace() && !(*it).isPunct());
02578 
02579   it = copy;
02580 #if DEBUG_CARETMODE > 1
02581     if (!it.isEnd()) kdDebug(6200) << "effective '" << (*it).latin1() << "' (" << (int)(*it).latin1() << ") box " << it.caretBox() << endl;
02582 #endif
02583 }
02584 
02592 static void moveIteratorByPage(LinearDocument &ld,
02593         ErgonomicEditableLineIterator &it, int mindist, bool next)
02594 {
02595   // ### This whole routine plainly sucks. Use an inverse strategie for pgup/pgdn.
02596 
02597   if (it == ld.end() || it == ld.preBegin()) return;
02598 
02599   ErgonomicEditableLineIterator copy = it;
02600 #if DEBUG_CARETMODE > 0
02601   kdDebug(6200) << " mindist: " << mindist << endl;
02602 #endif
02603 
02604   CaretBoxLine *cbl = *copy;
02605   int absx = 0, absy = 0;
02606 
02607   RenderBlock *lastcb = cbl->containingBlock();
02608   Q_ASSERT(lastcb->isRenderBlock());
02609   lastcb->absolutePosition(absx, absy, false);  // ### what about fixed?
02610 
02611   int lastfby = cbl->begin().data()->yPos();
02612   int lastheight = 0;
02613   int rescue = 1000;    // ### this is a hack to keep stuck carets from hanging the ua
02614   do {
02615     if (next) ++copy; else --copy;
02616     if (copy == ld.end() || copy == ld.preBegin()) break;
02617 
02618     cbl = *copy;
02619     RenderBlock *cb = cbl->containingBlock();
02620 
02621     int diff = 0;
02622     // ### actually flowBox->yPos() should suffice, but this is not ported
02623     // over yet from WebCore
02624     int fby = cbl->begin().data()->yPos();
02625     if (cb != lastcb) {
02626       if (next) {
02627         diff = absy + lastfby + lastheight;
02628         cb->absolutePosition(absx, absy, false);    // ### what about fixed?
02629         diff = absy - diff + fby;
02630         lastfby = 0;
02631       } else {
02632         diff = absy;
02633         cb->absolutePosition(absx, absy, false);    // ### what about fixed?
02634         diff -= absy + fby + lastheight;
02635     lastfby = fby - lastheight;
02636       }/*end if*/
02637 #if DEBUG_CARETMODE > 2
02638       kdDebug(6200) << "absdiff " << diff << endl;
02639 #endif
02640     } else {
02641       diff = kAbs(fby - lastfby);
02642     }/*end if*/
02643 #if DEBUG_CARETMODE > 2
02644     kdDebug(6200) << "cbl->begin().data()->yPos(): " << fby << " diff " << diff << endl;
02645 #endif
02646 
02647     mindist -= diff;
02648 
02649     lastheight = kAbs(fby - lastfby);
02650     lastfby = fby;
02651     lastcb = cb;
02652     it = copy;
02653 #if DEBUG_CARETMODE > 0
02654     kdDebug(6200) << " mindist: " << mindist << endl;
02655 #endif
02656     // trick: actually the distance is always one line short, but we cannot
02657     // calculate the height of the first line (### WebCore will make it better)
02658     // Therefore, we simply approximate that excess line by using the last
02659     // caluculated line height.
02660   } while (mindist - lastheight > 0 && --rescue);
02661 }
02662 
02663 
02664 }/*end namespace*/

KHTML

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

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal