khtml Library API Documentation

khtml_caret.cpp

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