# khtml Library API Documentation

Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

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

This file is part of the documentation for khtml Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:33:11 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003