File indexing completed on 2024-04-21 14:58:49

0001 /* This file is part of the KDE project
0002  *
0003  * Copyright (C) 2003-2004 Leo Savernik <l.savernik@aon.at>
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Library General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2 of the License, or (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Library General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Library General Public License
0016  * along with this library; see the file COPYING.LIB.  If not, write to
0017  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019  */
0020 
0021 #include "khtml_caret_p.h"
0022 
0023 #include "html/html_documentimpl.h"
0024 
0025 namespace khtml
0026 {
0027 
0028 /** Flags representing the type of advance that has been made.
0029  * @param LeftObject a render object was left and an ascent to its parent has
0030  *  taken place
0031  * @param AdvancedToSibling an actual advance to a sibling has taken place
0032  * @param EnteredObject a render object was entered by descending into it from
0033  *  its parent object.
0034  */
0035 enum ObjectAdvanceState {
0036     LeftObject = 0x01, AdvancedToSibling = 0x02, EnteredObject = 0x04
0037 };
0038 
0039 /** All possible states that may occur during render object traversal.
0040  * @param OutsideDescending outside of the current object, ready to descend
0041  *  into children
0042  * @param InsideDescending inside the current object, descending into
0043  *  children
0044  * @param InsideAscending inside the current object, ascending to parents
0045  * @param OutsideAscending outsie the current object, ascending to parents
0046  */
0047 enum ObjectTraversalState {
0048     OutsideDescending, InsideDescending, InsideAscending, OutsideAscending
0049 };
0050 
0051 /** Traverses the render object tree in a fine granularity.
0052  * @param obj render object
0053  * @param trav object traversal state
0054  * @param toBegin traverse towards beginning
0055  * @param base base render object which this method must not advance beyond
0056  *  (0 means document)
0057  * @param state object advance state (enum ObjectAdvanceState)
0058  * @return the render object according to the state. May be the same as \c obj
0059  */
0060 static RenderObject *traverseRenderObjects(RenderObject *obj,
0061         ObjectTraversalState &trav, bool toBegin, RenderObject *base,
0062         int &state)
0063 {
0064     RenderObject *r;
0065     switch (trav) {
0066     case OutsideDescending:
0067         trav = InsideDescending;
0068         break;
0069     case InsideDescending:
0070         r = toBegin ? obj->lastChild() : obj->firstChild();
0071         if (r) {
0072             trav = OutsideDescending;
0073             obj = r;
0074             state |= EnteredObject;
0075         } else {
0076             trav = InsideAscending;
0077         }
0078         break;
0079     case InsideAscending:
0080         trav = OutsideAscending;
0081         break;
0082     case OutsideAscending:
0083         r = toBegin ? obj->previousSibling() : obj->nextSibling();
0084         if (r) {
0085             trav = OutsideDescending;
0086             state |= AdvancedToSibling;
0087         } else {
0088             r = obj->parent();
0089             if (r == base) {
0090                 r = 0;
0091             }
0092             trav = InsideAscending;
0093             state |= LeftObject;
0094         }
0095         obj = r;
0096         break;
0097     }/*end switch*/
0098 
0099     return obj;
0100 }
0101 
0102 /** Like RenderObject::objectBelow, but confined to stay within \c base.
0103  * @param obj render object to begin with
0104  * @param trav object traversal state, will be reset within this function
0105  * @param base base render object (0: no base)
0106  */
0107 static inline RenderObject *renderObjectBelow(RenderObject *obj, ObjectTraversalState &trav, RenderObject *base)
0108 {
0109     trav = InsideDescending;
0110     int state;        // we don't need the state, so we don't initialize it
0111     RenderObject *r = obj;
0112     while (r && trav != OutsideDescending) {
0113         r = traverseRenderObjects(r, trav, false, base, state);
0114 #if DEBUG_CARETMODE > 3
0115         // qCDebug(KHTML_LOG) << "renderObjectBelow: r " << r << " trav " << trav;
0116 #endif
0117     }
0118     trav = InsideDescending;
0119     return r;
0120 }
0121 
0122 /** Like RenderObject::objectAbove, but confined to stay within \c base.
0123  * @param obj render object to begin with
0124  * @param trav object traversal state, will be reset within this function
0125  * @param base base render object (0: no base)
0126  */
0127 static inline RenderObject *renderObjectAbove(RenderObject *obj, ObjectTraversalState &trav, RenderObject *base)
0128 {
0129     trav = OutsideAscending;
0130     int state;        // we don't need the state, so we don't initialize it
0131     RenderObject *r = obj;
0132     while (r && trav != InsideAscending) {
0133         r = traverseRenderObjects(r, trav, true, base, state);
0134 #if DEBUG_CARETMODE > 3
0135         // qCDebug(KHTML_LOG) << "renderObjectAbove: r " << r << " trav " << trav;
0136 #endif
0137     }
0138     trav = InsideAscending;
0139     return r;
0140 }
0141 
0142 /** Checks whether the given inline box matches the IndicatedFlows policy
0143  * @param box inline box to test
0144  * @return true on match
0145  */
0146 static inline bool isIndicatedInlineBox(InlineBox *box)
0147 {
0148     // text boxes are never indicated.
0149     if (box->isInlineTextBox()) {
0150         return false;
0151     }
0152     RenderStyle *s = box->object()->style();
0153     return s->borderLeftWidth() || s->borderRightWidth()
0154            || s->borderTopWidth() || s->borderBottomWidth()
0155            || s->paddingLeft().value() || s->paddingRight().value()
0156            || s->paddingTop().value() || s->paddingBottom().value()
0157            // ### Can inline elements have top/bottom margins? Couldn't find
0158            // it in the CSS 2 spec, but Mozilla ignores them, so we do, too.
0159            || s->marginLeft().value() || s->marginRight().value();
0160 }
0161 
0162 /** Checks whether the given render object matches the IndicatedFlows policy
0163  * @param r render object to test
0164  * @return true on match
0165  */
0166 static inline bool isIndicatedFlow(RenderObject *r)
0167 {
0168     RenderStyle *s = r->style();
0169     return s->borderLeftStyle() != BNONE || s->borderRightStyle() != BNONE
0170            || s->borderTopStyle() != BNONE || s->borderBottomStyle() != BNONE
0171 //  || s->paddingLeft().value() || s->paddingRight().value()
0172 //  || s->paddingTop().value() || s->paddingBottom().value()
0173 //  || s->marginLeft().value() || s->marginRight().value()
0174            || s->hasClip() || s->hidesOverflow()
0175            || s->backgroundColor().isValid() || s->backgroundImage();
0176 }
0177 
0178 /** Advances to the next render object, taking into account the current
0179  * traversal state.
0180  *
0181  * @param r render object
0182  * @param trav object traversal state
0183  * @param toBegin @p true, advance towards beginning, @p false, advance toward end
0184  * @param base base render object which this method must not advance beyond
0185  *  (0 means document)
0186  * @param state object advance state (enum ObjectAdvanceState) (unchanged
0187  *  on LeafsOnly)
0188  * @return a pointer to the render object which we advanced to,
0189  *  or 0 if the last possible object has been reached.
0190  */
0191 static RenderObject *advanceObject(RenderObject *r,
0192                                    ObjectTraversalState &trav, bool toBegin,
0193                                    RenderObject *base, int &state)
0194 {
0195 
0196     ObjectTraversalState origtrav = trav;
0197     RenderObject *a = traverseRenderObjects(r, trav, toBegin, base, state);
0198 
0199     bool ignoreOutsideDesc = toBegin && origtrav == OutsideAscending;
0200 
0201     // render object and traversal state at which look ahead has been started
0202     RenderObject *la = 0;
0203     ObjectTraversalState latrav = trav;
0204     ObjectTraversalState lasttrav = origtrav;
0205 
0206     while (a) {
0207 #if DEBUG_CARETMODE > 5
0208 // qCDebug(KHTML_LOG) << "a " << a << " trav " << trav;
0209 #endif
0210         if (a->element()) {
0211 #if DEBUG_CARETMODE > 4
0212 // qCDebug(KHTML_LOG) << "a " << a << " trav " << trav << " origtrav " << origtrav << " ignoreOD " << ignoreOutsideDesc;
0213 #endif
0214             if (toBegin) {
0215 
0216                 switch (origtrav) {
0217                 case OutsideDescending:
0218                     if (trav == InsideAscending) {
0219                         return a;
0220                     }
0221                     if (trav == OutsideDescending) {
0222                         return a;
0223                     }
0224                     break;
0225                 case InsideDescending:
0226                     if (trav == OutsideDescending) {
0227                         return a;
0228                     }
0229                 // fall through
0230                 case InsideAscending:
0231                     if (trav == OutsideAscending) {
0232                         return a;
0233                     }
0234                     break;
0235                 case OutsideAscending:
0236                     if (trav == OutsideAscending) {
0237                         return a;
0238                     }
0239                     if (trav == InsideAscending && lasttrav == InsideDescending) {
0240                         return a;
0241                     }
0242                     if (trav == OutsideDescending && !ignoreOutsideDesc) {
0243                         return a;
0244                     }
0245                     // ignore this outside descending position, as it effectively
0246                     // demarkates the same position, but remember it in case we fall off
0247                     // the document.
0248                     la = a; latrav = trav;
0249                     ignoreOutsideDesc = false;
0250                     break;
0251                 }/*end switch*/
0252 
0253             } else {
0254 
0255                 switch (origtrav) {
0256                 case OutsideDescending:
0257                     if (trav == InsideAscending) {
0258                         return a;
0259                     }
0260                     if (trav == OutsideDescending) {
0261                         return a;
0262                     }
0263                     break;
0264                 case InsideDescending:
0265 //      if (trav == OutsideDescending) return a;
0266                 // fall through
0267                 case InsideAscending:
0268 //            if (trav == OutsideAscending) return a;
0269 //      break;
0270                 case OutsideAscending:
0271                     // ### what if origtrav == OA, and immediately afterwards trav
0272                     // becomes OD? In this case the effective position hasn't changed ->
0273                     // the caret gets stuck. Otherwise, it apparently cannot happen in
0274                     // real usage patterns.
0275                     if (trav == OutsideDescending) {
0276                         return a;
0277                     }
0278                     if (trav == OutsideAscending) {
0279                         if (la) {
0280                             return la;
0281                         }
0282                         // starting lookahead here. Remember old object in case we fall off
0283                         // the document.
0284                         la = a; latrav = trav;
0285                     }
0286                     break;
0287                 }/*end switch*/
0288 
0289             }/*end if*/
0290         }/*end if*/
0291 
0292         lasttrav = trav;
0293         a = traverseRenderObjects(a, trav, toBegin, base, state);
0294     }/*wend*/
0295 
0296     if (la) {
0297         trav = latrav, a = la;
0298     }
0299     return a;
0300 
0301 }
0302 
0303 /** Check whether the current render object is unsuitable in caret mode handling.
0304  *
0305  * Some render objects cannot be handled correctly in caret mode. These objects
0306  * are therefore considered to be unsuitable. The null object is suitable, as
0307  * it denotes reaching the end.
0308  * @param r current render object
0309  * @param trav current traversal state
0310  */
0311 static inline bool isUnsuitable(RenderObject *r, ObjectTraversalState trav)
0312 {
0313     if (!r) {
0314         return false;
0315     }
0316     return r->isTableCol() || r->isTableSection() || r->isTableRow()
0317            || (r->isText() && !static_cast<RenderText *>(r)->firstTextBox());
0318     ;
0319     Q_UNUSED(trav);
0320 }
0321 
0322 /** Advances to the next render object, taking into account the current
0323  * traversal state, but skipping render objects which are not suitable for
0324  * having placed the caret into them.
0325  * @param r render object
0326  * @param trav object traversal state (unchanged on LeafsOnly)
0327  * @param toBegin @p true, advance towards beginning, @p false, advance toward end
0328  * @param base base render object which this method must not advance beyond
0329  *  (0 means document)
0330  * @param state object advance state (enum ObjectAdvanceState) (unchanged
0331  *  on LeafsOnly)
0332  * @return a pointer to the advanced render object or 0 if the last possible
0333  *  object has been reached.
0334  */
0335 static inline RenderObject *advanceSuitableObject(RenderObject *r,
0336         ObjectTraversalState &trav, bool toBegin,
0337         RenderObject *base, int &state)
0338 {
0339     do {
0340         r = advanceObject(r, trav, toBegin, base, state);
0341 #if DEBUG_CARETMODE > 2
0342         // qCDebug(KHTML_LOG) << "after advanceSWP: r " << r << " trav " << trav << " toBegin " << toBegin;
0343 #endif
0344     } while (isUnsuitable(r, trav));
0345     return r;
0346 }
0347 
0348 /**
0349   * Returns the next leaf node.
0350   *
0351   * Using this function delivers leaf nodes as if the whole DOM tree
0352   * were a linear chain of its leaf nodes.
0353   * @param r dom node
0354   * @param baseElem base element not to search beyond
0355   * @return next leaf node or 0 if there are no more.
0356   */
0357 static NodeImpl *nextLeafNode(NodeImpl *r, NodeImpl *baseElem)
0358 {
0359     NodeImpl *n = r->firstChild();
0360     if (n) {
0361         while (n) {
0362             r = n;
0363             n = n->firstChild();
0364         }
0365         return const_cast<NodeImpl *>(r);
0366     }/*end if*/
0367     n = r->nextSibling();
0368     if (n) {
0369         r = n;
0370         while (n) {
0371             r = n;
0372             n = n->firstChild();
0373         }
0374         return const_cast<NodeImpl *>(r);
0375     }/*end if*/
0376 
0377     n = r->parentNode();
0378     if (n == baseElem) {
0379         n = 0;
0380     }
0381     while (n) {
0382         r = n;
0383         n = r->nextSibling();
0384         if (n) {
0385             r = n;
0386             n = r->firstChild();
0387             while (n) {
0388                 r = n;
0389                 n = n->firstChild();
0390             }
0391             return const_cast<NodeImpl *>(r);
0392         }/*end if*/
0393         n = r->parentNode();
0394         if (n == baseElem) {
0395             n = 0;
0396         }
0397     }/*wend*/
0398     return 0;
0399 }
0400 
0401 #if 0       // currently not used
0402 /** (Not part of the official DOM)
0403   * Returns the previous leaf node.
0404   *
0405   * Using this function delivers leaf nodes as if the whole DOM tree
0406   * were a linear chain of its leaf nodes.
0407   * @param r dom node
0408   * @param baseElem base element not to search beyond
0409   * @return previous leaf node or 0 if there are no more.
0410   */
0411 static NodeImpl *prevLeafNode(NodeImpl *r, NodeImpl *baseElem)
0412 {
0413     NodeImpl *n = r->firstChild();
0414     if (n) {
0415         while (n) {
0416             r = n;
0417             n = n->firstChild();
0418         }
0419         return const_cast<NodeImpl *>(r);
0420     }/*end if*/
0421     n = r->previousSibling();
0422     if (n) {
0423         r = n;
0424         while (n) {
0425             r = n;
0426             n = n->firstChild();
0427         }
0428         return const_cast<NodeImpl *>(r);
0429     }/*end if*/
0430 
0431     n = r->parentNode();
0432     if (n == baseElem) {
0433         n = 0;
0434     }
0435     while (n) {
0436         r = n;
0437         n = r->previousSibling();
0438         if (n) {
0439             r = n;
0440             n = r->lastChild();
0441             while (n) {
0442                 r = n;
0443                 n = n->lastChild();
0444             }
0445             return const_cast<NodeImpl *>(r);
0446         }/*end if*/
0447         n = r->parentNode();
0448         if (n == baseElem) {
0449             n = 0;
0450         }
0451     }/*wend*/
0452     return 0;
0453 }
0454 #endif
0455 
0456 /** Maps a DOM Range position to the corresponding caret position.
0457  *
0458  * The offset boundary is not checked for validity.
0459  * @param node DOM node
0460  * @param offset zero-based offset within node
0461  * @param r returns render object (may be 0 if DOM node has no render object)
0462  * @param r_ofs returns the appropriate offset for the found render object r
0463  * @param outside returns true when offset is applied to the outside of
0464  *  \c r, or false for the inside.
0465  * @param outsideEnd return true when the caret position is at the outside end.
0466  */
0467 void /*KHTML_NO_EXPORT*/ mapDOMPosToRenderPos(NodeImpl *node, long offset,
0468         RenderObject *&r, long &r_ofs, bool &outside, bool &outsideEnd)
0469 {
0470     if (node->nodeType() == Node::TEXT_NODE) {
0471         outside = false;
0472         outsideEnd = false;
0473         r = node->renderer();
0474         r_ofs = offset;
0475     } else if (node->nodeType() == Node::ELEMENT_NODE || node->nodeType() == Node::DOCUMENT_NODE) {
0476 
0477         // Though offset points between two children, attach it to the visually
0478         // most suitable one (and only there, because the mapping must stay bijective)
0479         if (node->firstChild()) {
0480             outside = true;
0481             NodeImpl *child = offset <= 0 ? node->firstChild()
0482                               // childNode is expensive
0483                               : node->childNode((unsigned long)offset);
0484             // index was child count or out of bounds
0485             bool atEnd = !child;
0486 #if DEBUG_CARETMODE > 5
0487             // qCDebug(KHTML_LOG) << "mapDTR: child " << child << "@" << (child ? child->nodeName().string() : QString()) << " atEnd " << atEnd;
0488 #endif
0489             if (atEnd) {
0490                 child = node->lastChild();
0491             }
0492 
0493             r = child->renderer();
0494             r_ofs = 0;
0495             outsideEnd = atEnd;
0496 
0497             // Outside text nodes most likely stem from a continuation. Seek
0498             // the enclosing continued render object and use this one instead.
0499             if (r && child->nodeType() == Node::TEXT_NODE) {
0500                 r = r->parent();
0501                 RenderObject *o = node->renderer();
0502                 while (o->continuation() && o->continuation() != r) {
0503                     o = o->continuation();
0504                 }
0505                 if (!r || o->continuation() != r) {
0506                     r = child->renderer();
0507                 }
0508             }/*end if*/
0509 
0510             // BRs cause troubles. Returns the previous render object instead,
0511             // giving it the attributes outside, outside end.
0512             if (r && r->isBR()) {
0513                 r = r->objectAbove();
0514                 outsideEnd = true;
0515             }/*end if*/
0516 
0517         } else {
0518             // Element has no children, treat offset to be inside the node.
0519             outside = false;
0520             outsideEnd = false;
0521             r = node->renderer();
0522             r_ofs = 0;    // only offset 0 possible
0523         }
0524 
0525     } else {
0526         r = 0;
0527         qCWarning(KHTML_LOG) << "Mapping from nodes of type " << node->nodeType()
0528                    << " not supported!";
0529     }
0530 }
0531 
0532 /** Maps a caret position to the corresponding DOM Range position.
0533  *
0534  * @param r render object
0535  * @param r_ofs offset within render object
0536  * @param outside true when offset is interpreted to be on the outside of
0537  *  \c r, or false if on the inside.
0538  * @param outsideEnd true when the caret position is at the outside end.
0539  * @param node returns DOM node
0540  * @param offset returns zero-based offset within node
0541  */
0542 void /*KHTML_NO_EXPORT*/ mapRenderPosToDOMPos(RenderObject *r, long r_ofs,
0543         bool outside, bool outsideEnd, NodeImpl *&node, long &offset)
0544 {
0545     node = r->element();
0546     Q_ASSERT(node);
0547 #if DEBUG_CARETMODE > 5
0548     // qCDebug(KHTML_LOG) << "mapRTD: r " << r << '@' << (r ? r->renderName() : QString()) << (r && r->element() ? QString(".node ") + QString::number((unsigned)r->element(),16) + '@' + r->element()->nodeName().string() : QString()) << " outside " << outside << " outsideEnd " << outsideEnd;
0549 #endif
0550     if (node->nodeType() == Node::ELEMENT_NODE || node->nodeType() == Node::TEXT_NODE) {
0551 
0552         if (outside) {
0553             NodeImpl *parent = node->parent();
0554 
0555             // If this is part of a continuation, use the actual node as the parent,
0556             // and the first render child as the node.
0557             if (r != node->renderer()) {
0558                 RenderObject *o = node->renderer();
0559                 while (o->continuation() && o->continuation() != r) {
0560                     o = o->continuation();
0561                 }
0562                 if (o->continuation() == r) {
0563                     parent = node;
0564                     // ### What if the first render child does not map to a child of
0565                     // the continued node?
0566                     node = r->firstChild() ? r->firstChild()->element() : node;
0567                 }
0568             }/*end if*/
0569 
0570             if (!parent) {
0571                 goto inside;
0572             }
0573 
0574             offset = (long)node->nodeIndex() + outsideEnd;
0575             node = parent;
0576 #if DEBUG_CARETMODE > 5
0577             // qCDebug(KHTML_LOG) << node << "@" << (node ? node->nodeName().string() : QString()) << " offset " << offset;
0578 #endif
0579         } else {    // !outside
0580         inside:
0581             offset = r_ofs;
0582         }
0583 
0584     } else {
0585         offset = 0;
0586         qCWarning(KHTML_LOG) << "Mapping to nodes of type " << node->nodeType()
0587                    << " not supported!";
0588     }
0589 }
0590 
0591 /** Make sure the given node is a leaf node. */
0592 static inline void ensureLeafNode(NodeImpl *&node, NodeImpl *base)
0593 {
0594     if (node && node->hasChildNodes()) {
0595         node = nextLeafNode(node, base);
0596     }
0597 }
0598 
0599 /** Converts a caret position to its respective object traversal state.
0600  * @param outside whether the caret is outside the object
0601  * @param atEnd whether the caret position is at the end
0602  * @param toBegin \c true when advancing towards the beginning
0603  * @param trav returns the corresponding traversal state
0604  */
0605 static inline void mapRenderPosToTraversalState(bool outside, bool atEnd,
0606         bool toBegin, ObjectTraversalState &trav)
0607 {
0608     if (!outside) {
0609         atEnd = !toBegin;
0610     }
0611     if (!atEnd ^ toBegin) {
0612         trav = outside ? OutsideDescending : InsideDescending;
0613     } else {
0614         trav = outside ? OutsideAscending : InsideAscending;
0615     }
0616 }
0617 
0618 /** Converts a traversal state to its respective caret position
0619  * @param trav object traversal state
0620  * @param toBegin \c true when advancing towards the beginning
0621  * @param outside whether the caret is outside the object
0622  * @param atEnd whether the caret position is at the end
0623  */
0624 static inline void mapTraversalStateToRenderPos(ObjectTraversalState trav,
0625         bool toBegin, bool &outside, bool &atEnd)
0626 {
0627     outside = false;
0628     switch (trav) {
0629     case OutsideDescending: outside = true; // fall through
0630     case InsideDescending: atEnd = toBegin; break;
0631     case OutsideAscending: outside = true; // fall through
0632     case InsideAscending: atEnd = !toBegin; break;
0633     }
0634 }
0635 
0636 /** Finds the next node that has a renderer.
0637  *
0638  * Note that if the initial @p node has a renderer, this will be returned,
0639  * regardless of the caret advance policy.
0640  * Otherwise, for the next nodes, only leaf nodes are considered.
0641  * @param node node to start with, will be updated accordingly
0642  * @param offset offset of caret within \c node
0643  * @param base base render object which this method must not advance beyond
0644  *  (0 means document)
0645  * @param r_ofs return the caret offset within the returned renderer
0646  * @param outside returns whether offset is to be interpreted to the outside
0647   * (true) or the inside (false) of the render object.
0648  * @param outsideEnd returns whether the end of the outside position is meant
0649  * @return renderer or 0 if no following node has a renderer.
0650  */
0651 static RenderObject *findRenderer(NodeImpl *&node, long offset,
0652                                   RenderObject *base, long &r_ofs,
0653                                   bool &outside, bool &outsideEnd)
0654 {
0655     if (!node) {
0656         return 0;
0657     }
0658     RenderObject *r;
0659     mapDOMPosToRenderPos(node, offset, r, r_ofs, outside, outsideEnd);
0660 #if DEBUG_CARETMODE > 2
0661     // qCDebug(KHTML_LOG) << "findRenderer: node " << node << " " << (node ? node->nodeName().string() : QString()) << " offset " << offset << " r " << r << "[" << (r ? r->renderName() : QString()) << "] r_ofs " << r_ofs << " outside " << outside << " outsideEnd " << outsideEnd;
0662 #endif
0663     if (r) {
0664         return r;
0665     }
0666     NodeImpl *baseElem = base ? base->element() : 0;
0667     while (!r) {
0668         node = nextLeafNode(node, baseElem);
0669         if (!node) {
0670             break;
0671         }
0672         r = node->renderer();
0673         if (r) {
0674             r_ofs = offset;
0675         }
0676     }
0677 #if DEBUG_CARETMODE > 3
0678     // qCDebug(KHTML_LOG) << "1r " << r;
0679 #endif
0680     ObjectTraversalState trav;
0681     int state;        // not used
0682     mapRenderPosToTraversalState(outside, outsideEnd, false, trav);
0683     if (r && isUnsuitable(r, trav)) {
0684         r = advanceSuitableObject(r, trav, false, base, state);
0685         mapTraversalStateToRenderPos(trav, false, outside, outsideEnd);
0686         if (r) {
0687             r_ofs = r->minOffset();
0688         }
0689     }
0690 #if DEBUG_CARETMODE > 3
0691     // qCDebug(KHTML_LOG) << "2r " << r;
0692 #endif
0693     return r;
0694 }
0695 
0696 /** returns a suitable base element
0697  * @param caretNode current node containing caret.
0698  */
0699 static ElementImpl *determineBaseElement(NodeImpl *caretNode)
0700 {
0701     // ### for now, only body is delivered for html documents,
0702     // and 0 for xml documents.
0703 
0704     DocumentImpl *doc = caretNode->getDocument();
0705     if (!doc) {
0706         return 0;    // should not happen, but who knows.
0707     }
0708 
0709     if (doc->isHTMLDocument()) {
0710         return static_cast<HTMLDocumentImpl *>(doc)->body();
0711     }
0712 
0713     return 0;
0714 }
0715 
0716 // == class CaretBox implementation
0717 
0718 #if DEBUG_CARETMODE > 0
0719 void CaretBox::dump(QTextStream &ts, const QString &ind) const
0720 {
0721     ts << ind << "b@" << _box;
0722 
0723     if (_box) {
0724         ts << "<" << _box->object() << ":" << _box->object()->renderName() << ">";
0725     }/*end if*/
0726 
0727     ts << " " << _x << "+" << _y << "+" << _w << "*" << _h;
0728 
0729     ts << " cb@" << cb;
0730     if (cb) {
0731         ts << ":" << cb->renderName();
0732     }
0733 
0734     ts << " " << (_outside ? (outside_end ? "oe" : "o-") : "i-");
0735 //  ts << endl;
0736 }
0737 #endif
0738 
0739 // == class CaretBoxLine implementation
0740 
0741 #if DEBUG_CARETMODE > 0
0742 #  define DEBUG_ACIB 1
0743 #else
0744 #  define DEBUG_ACIB DEBUG_CARETMODE
0745 #endif
0746 void CaretBoxLine::addConvertedInlineBox(InlineBox *box, SeekBoxParams &sbp) /*KHTML_NO_EXPORT*/
0747 {
0748     // Generate only one outside caret box between two elements. If
0749     // coalesceOutsideBoxes is true, generating left outside boxes is inhibited.
0750     bool coalesceOutsideBoxes = false;
0751     CaretBoxIterator lastCoalescedBox;
0752     for (; box; box = box->nextOnLine()) {
0753 #if DEBUG_ACIB
0754 // qCDebug(KHTML_LOG) << "box " << box;
0755 // qCDebug(KHTML_LOG) << "box->object " << box->object();
0756 // qCDebug(KHTML_LOG) << "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();
0757 #endif
0758         // ### Why the hell can object() ever be 0?!
0759         if (!box->object()) {
0760             continue;
0761         }
0762 
0763         RenderStyle *s = box->object()->style(box->m_firstLine);
0764         // parent style for outside caret boxes
0765         RenderStyle *ps = box->parent() && box->parent()->object()
0766                           ? box->parent()->object()->style(box->parent()->m_firstLine)
0767                           : s;
0768 
0769         if (box->isInlineFlowBox()) {
0770 #if DEBUG_ACIB
0771 // qCDebug(KHTML_LOG) << "isinlineflowbox " << box;
0772 #endif
0773             InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(box);
0774             bool rtl = ps->direction() == RTL;
0775             const QFontMetrics &pfm = ps->fontMetrics();
0776 
0777             if (flowBox->includeLeftEdge()) {
0778                 // If this box is to be coalesced with the outside end box of its
0779                 // predecessor, then check if it is the searched box. If it is, we
0780                 // substitute the outside end box.
0781                 if (coalesceOutsideBoxes) {
0782                     if (sbp.equalsBox(flowBox, true, false)) {
0783                         sbp.it = lastCoalescedBox;
0784                         Q_ASSERT(!sbp.found);
0785                         sbp.found = true;
0786                     }
0787                 } else {
0788                     addCreatedFlowBoxEdge(flowBox, pfm, true, rtl);
0789                     sbp.check(preEnd());
0790                 }
0791             }/*end if*/
0792 
0793             if (flowBox->firstChild()) {
0794 #if DEBUG_ACIB
0795 // qCDebug(KHTML_LOG) << "this " << this << " flowBox " << flowBox << " firstChild " << flowBox->firstChild();
0796 // qCDebug(KHTML_LOG) << "== recursive invocation";
0797 #endif
0798                 addConvertedInlineBox(flowBox->firstChild(), sbp);
0799 #if DEBUG_ACIB
0800 // qCDebug(KHTML_LOG) << "== recursive invocation end";
0801 #endif
0802             } else {
0803                 addCreatedFlowBoxInside(flowBox, s->fontMetrics());
0804                 sbp.check(preEnd());
0805             }
0806 
0807             if (flowBox->includeRightEdge()) {
0808                 addCreatedFlowBoxEdge(flowBox, pfm, false, rtl);
0809                 lastCoalescedBox = preEnd();
0810                 sbp.check(lastCoalescedBox);
0811                 coalesceOutsideBoxes = true;
0812             }
0813 
0814         } else if (box->isInlineTextBox()) {
0815 #if DEBUG_ACIB
0816 // qCDebug(KHTML_LOG) << "isinlinetextbox " << box << (box->object() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText *>(box->object())->str->s+box->minOffset(), qMin(box->maxOffset() - box->minOffset(), 15L)).string()) : QString());
0817 #endif
0818             caret_boxes.append(new CaretBox(box, false, false));
0819             sbp.check(preEnd());
0820             // coalescing has been interrupted
0821             coalesceOutsideBoxes = false;
0822 
0823         } else {
0824 #if DEBUG_ACIB
0825 // qCDebug(KHTML_LOG) << "some replaced or what " << box;
0826 #endif
0827             // must be an inline-block, inline-table, or any RenderReplaced
0828             bool rtl = ps->direction() == RTL;
0829             const QFontMetrics &pfm = ps->fontMetrics();
0830 
0831             if (coalesceOutsideBoxes) {
0832                 if (sbp.equalsBox(box, true, false)) {
0833                     sbp.it = lastCoalescedBox;
0834                     Q_ASSERT(!sbp.found);
0835                     sbp.found = true;
0836                 }
0837             } else {
0838                 addCreatedInlineBoxEdge(box, pfm, true, rtl);
0839                 sbp.check(preEnd());
0840             }
0841 
0842             caret_boxes.append(new CaretBox(box, false, false));
0843             sbp.check(preEnd());
0844 
0845             addCreatedInlineBoxEdge(box, pfm, false, rtl);
0846             lastCoalescedBox = preEnd();
0847             sbp.check(lastCoalescedBox);
0848             coalesceOutsideBoxes = true;
0849         }/*end if*/
0850     }/*next box*/
0851 }
0852 #undef DEBUG_ACIB
0853 
0854 void CaretBoxLine::addCreatedFlowBoxInside(InlineFlowBox *flowBox, const QFontMetrics &fm) /*KHTML_NO_EXPORT*/
0855 {
0856 
0857     CaretBox *caretBox = new CaretBox(flowBox, false, false);
0858     caret_boxes.append(caretBox);
0859 
0860     // afaik an inner flow box can only have the width 0, therefore we don't
0861     // have to care for rtl or alignment
0862     // ### can empty inline elements have a width? css 2 spec isn't verbose about it
0863 
0864     caretBox->_y += flowBox->baseline() - fm.ascent();
0865     caretBox->_h = fm.height();
0866 }
0867 
0868 void CaretBoxLine::addCreatedFlowBoxEdge(InlineFlowBox *flowBox, const QFontMetrics &fm, bool left, bool rtl) /*KHTML_NO_EXPORT*/
0869 {
0870     CaretBox *caretBox = new CaretBox(flowBox, true, !left);
0871     caret_boxes.append(caretBox);
0872 
0873     if (left ^ rtl) {
0874         caretBox->_x -= flowBox->paddingLeft() + flowBox->borderLeft() + 1;
0875     } else {
0876         caretBox->_x += caretBox->_w + flowBox->paddingRight() + flowBox->borderRight();
0877     }
0878 
0879     caretBox->_y += flowBox->baseline() - fm.ascent();
0880     caretBox->_h = fm.height();
0881     caretBox->_w = 1;
0882 }
0883 
0884 void CaretBoxLine::addCreatedInlineBoxEdge(InlineBox *box, const QFontMetrics &fm, bool left, bool rtl) /*KHTML_NO_EXPORT*/
0885 {
0886     CaretBox *caretBox = new CaretBox(box, true, !left);
0887     caret_boxes.append(caretBox);
0888 
0889     if (left ^ rtl) {
0890         caretBox->_x--;
0891     } else {
0892         caretBox->_x += caretBox->_w;
0893     }
0894 
0895     caretBox->_y += box->baseline() - fm.ascent();
0896     caretBox->_h = fm.height();
0897     caretBox->_w = 1;
0898 }
0899 
0900 CaretBoxLine *CaretBoxLine::constructCaretBoxLine(CaretBoxLineDeleter *deleter,
0901         InlineFlowBox *basicFlowBox, InlineBox *seekBox, bool seekOutside,
0902         bool seekOutsideEnd, CaretBoxIterator &iter, RenderObject *seekObject)
0903 //  KHTML_NO_EXPORT
0904 {
0905     // Iterate all inline boxes within this inline flow box.
0906     // Caret boxes will be created for each
0907     // - outside begin of an inline flow box (except for the basic inline flow box)
0908     // - outside end of an inline flow box (except for the basic inline flow box)
0909     // - inside of an empty inline flow box
0910     // - outside begin of an inline box resembling a replaced element
0911     // - outside end of an inline box resembling a replaced element
0912     // - inline text box
0913     // - inline replaced box
0914 
0915     CaretBoxLine *result = new CaretBoxLine(basicFlowBox);
0916     deleter->append(result);
0917 
0918     SeekBoxParams sbp(seekBox, seekOutside, seekOutsideEnd, seekObject, iter);
0919 
0920     // iterate recursively, I'm too lazy to do it iteratively
0921     result->addConvertedInlineBox(basicFlowBox, sbp);
0922 
0923     if (!sbp.found) {
0924         sbp.it = result->end();
0925     }
0926 
0927     return result;
0928 }
0929 
0930 CaretBoxLine *CaretBoxLine::constructCaretBoxLine(CaretBoxLineDeleter *deleter,
0931         RenderBox *cb, bool outside, bool outsideEnd, CaretBoxIterator &iter) /*KHTML_NO_EXPORT*/
0932 {
0933     int _x = cb->xPos();
0934     int _y = cb->yPos();
0935     int height;
0936     int width = 1;        // no override is indicated in boxes
0937 
0938     if (outside) {
0939 
0940         RenderStyle *s = cb->element() && cb->element()->parent()
0941                          && cb->element()->parent()->renderer()
0942                          ? cb->element()->parent()->renderer()->style()
0943                          : cb->style();
0944         bool rtl = s->direction() == RTL;
0945 
0946         const QFontMetrics &fm = s->fontMetrics();
0947         height = fm.height();
0948 
0949         if (!outsideEnd) {
0950             _x--;
0951         } else {
0952             _x += cb->width();
0953         }
0954 
0955         int hl = fm.leading() / 2;
0956         int baseline = cb->baselinePosition(false);
0957         if (!cb->isReplaced() || cb->style()->display() == BLOCK) {
0958             if (!outsideEnd ^ rtl) {
0959                 _y -= fm.leading() / 2;
0960             } else {
0961                 _y += qMax(cb->height() - fm.ascent() - hl, 0);
0962             }
0963         } else {
0964             _y += baseline - fm.ascent() - hl;
0965         }
0966 
0967     } else {      // !outside
0968 
0969         RenderStyle *s = cb->style();
0970         const QFontMetrics &fm = s->fontMetrics();
0971         height = fm.height();
0972 
0973         _x += cb->borderLeft() + cb->paddingLeft();
0974         _y += cb->borderTop() + cb->paddingTop();
0975 
0976         // ### regard direction
0977         switch (s->textAlign()) {
0978         case LEFT:
0979         case KHTML_LEFT:
0980         case TAAUTO:  // ### find out what this does
0981         case JUSTIFY:
0982             break;
0983         case CENTER:
0984         case KHTML_CENTER:
0985             _x += cb->contentWidth() / 2;
0986             break;
0987         case KHTML_RIGHT:
0988         case RIGHT:
0989             _x += cb->contentWidth();
0990             break;
0991         }/*end switch*/
0992     }/*end if*/
0993 
0994     CaretBoxLine *result = new CaretBoxLine;
0995     deleter->append(result);
0996     result->caret_boxes.append(new CaretBox(_x, _y, width, height, cb,
0997                                             outside, outsideEnd));
0998     iter = result->begin();
0999     return result;
1000 }
1001 
1002 #if DEBUG_CARETMODE > 0
1003 void CaretBoxLine::dump(QTextStream &ts, const QString &ind) const
1004 {
1005     ts << ind << "cbl: baseFlowBox@" << basefb << endl;
1006     QString ind2 = ind + "  ";
1007     for (size_t i = 0; i < caret_boxes.size(); i++) {
1008         if (i > 0) {
1009             ts << endl;
1010         }
1011         caret_boxes[i]->dump(ts, ind2);
1012     }
1013 }
1014 #endif
1015 
1016 // == caret mode related helper functions
1017 
1018 /** seeks the root line box that is the parent of the given inline box.
1019  * @param b given inline box
1020  * @param base base render object which not to step over. If \c base's
1021  *  inline flow box is hit before the root line box, the flow box
1022  *  is returned instead.
1023  * @return the root line box or the base flow box.
1024  */
1025 inline InlineFlowBox *seekBaseFlowBox(InlineBox *b, RenderObject *base = 0)
1026 {
1027     // Seek root line box or base inline flow box, if \c base is interfering.
1028     while (b->parent() && b->object() != base) {
1029         b = b->parent();
1030     }/*wend*/
1031     Q_ASSERT(b->isInlineFlowBox());
1032     return static_cast<InlineFlowBox *>(b);
1033 }
1034 
1035 /** determines whether the given element is a block level replaced element.
1036  */
1037 inline bool isBlockRenderReplaced(RenderObject *r)
1038 {
1039     return r->isRenderReplaced() && r->style()->display() == BLOCK;
1040 }
1041 
1042 /** determines the caret line box that contains the given position.
1043  *
1044  * If the node does not map to a render object, the function will snap to
1045  * the next suitable render object following it.
1046  *
1047  * @param node node to begin with
1048  * @param offset zero-based offset within node.
1049  * @param cblDeleter deleter for caret box lines
1050  * @param base base render object which the caret must not be placed beyond.
1051  * @param r_ofs adjusted offset within render object
1052  * @param caretBoxIt returns an iterator to the caret box that contains the
1053  *  given position.
1054  * @return the determined caret box lineor 0 if either the node is 0 or
1055  *  there is no inline flow box containing this node. The containing block
1056  *  will still be set. If it is 0 too, @p node was invalid.
1057  */
1058 static CaretBoxLine *findCaretBoxLine(DOM::NodeImpl *node, long offset,
1059                                       CaretBoxLineDeleter *cblDeleter, RenderObject *base,
1060                                       long &r_ofs, CaretBoxIterator &caretBoxIt)
1061 {
1062     bool outside, outsideEnd;
1063     RenderObject *r = findRenderer(node, offset, base, r_ofs, outside, outsideEnd);
1064     if (!r) {
1065         return 0;
1066     }
1067 #if DEBUG_CARETMODE > 0
1068     // qCDebug(KHTML_LOG) << "=================== findCaretBoxLine";
1069     // qCDebug(KHTML_LOG) << "node " << node << " offset: " << offset << " r " << r->renderName() << "[" << r << "].node " << r->element()->nodeName().string() << "[" << r->element() << "]" << " r_ofs " << r_ofs << " outside " << outside << " outsideEnd " << outsideEnd;
1070 #endif
1071 
1072     // There are two strategies to find the correct line box. (The third is failsafe)
1073     // (A) First, if node's renderer is a RenderText, we only traverse its text
1074     // runs and return the root line box (saves much time for long blocks).
1075     // This should be the case 99% of the time.
1076     // (B) Second, we derive the inline flow box directly when the renderer is
1077     // a RenderBlock, RenderInline, or blocked RenderReplaced.
1078     // (C) Otherwise, we iterate linearly through all line boxes in order to find
1079     // the renderer.
1080 
1081     // (A)
1082     if (r->isText()) do {
1083             RenderText *t = static_cast<RenderText *>(r);
1084             int dummy;
1085             InlineBox *b = t->findInlineTextBox(offset, dummy, true);
1086             // Actually b should never be 0, but some render texts don't have text
1087             // boxes, so we insert the last run as an error correction.
1088             // If there is no last run, we resort to (B)
1089             if (!b) {
1090                 if (!t->lastTextBox()) {
1091                     break;
1092                 }
1093                 b = t->lastTextBox();
1094             }/*end if*/
1095             Q_ASSERT(b);
1096             outside = false;    // text boxes cannot have outside positions
1097             InlineFlowBox *baseFlowBox = seekBaseFlowBox(b, base);
1098 #if DEBUG_CARETMODE > 2
1099             // qCDebug(KHTML_LOG) << "text-box b: " << b << " baseFlowBox: " << baseFlowBox << (b && b->object() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText *>(b->object())->str->s+b->minOffset(), qMin(b->maxOffset() - b->minOffset(), 15L)).string()) : QString());
1100 #endif
1101 #if 0
1102             if (t->containingBlock()->isListItem()) {
1103                 dumpLineBoxes(static_cast<RenderFlow *>(t->containingBlock()));
1104             }
1105 #endif
1106 #if DEBUG_CARETMODE > 0
1107             // qCDebug(KHTML_LOG) << "=================== end findCaretBoxLine (renderText)";
1108 #endif
1109             return CaretBoxLine::constructCaretBoxLine(cblDeleter, baseFlowBox,
1110                     b, outside, outsideEnd, caretBoxIt);
1111         } while (false); /*end if*/
1112 
1113     // (B)
1114     bool isrepl = isBlockRenderReplaced(r);
1115     if (r->isRenderBlock() || r->isRenderInline() || isrepl) {
1116         RenderFlow *flow = static_cast<RenderFlow *>(r);
1117         InlineFlowBox *firstLineBox = isrepl ? 0 : flow->firstLineBox();
1118 
1119         // On render blocks, if we are outside, or have a totally empty render
1120         // block, we simply construct a special caret box line.
1121         // The latter case happens only when the render block is a leaf object itself.
1122         if (isrepl || r->isRenderBlock() && (outside || !firstLineBox)
1123                 || r->isRenderInline() && !firstLineBox) {
1124 #if DEBUG_CARETMODE > 0
1125             // qCDebug(KHTML_LOG) << "=================== end findCaretBoxLine (box " << (outside ? (outsideEnd ? "outside end" : "outside begin") : "inside") << ")";
1126 #endif
1127             Q_ASSERT(r->isBox());
1128             return CaretBoxLine::constructCaretBoxLine(cblDeleter,
1129                     static_cast<RenderBox *>(r), outside, outsideEnd, caretBoxIt);
1130         }/*end if*/
1131 
1132         // qCDebug(KHTML_LOG) << "firstlinebox " << firstLineBox;
1133         InlineFlowBox *baseFlowBox = seekBaseFlowBox(firstLineBox, base);
1134         return CaretBoxLine::constructCaretBoxLine(cblDeleter, baseFlowBox,
1135                 firstLineBox, outside, outsideEnd, caretBoxIt);
1136     }/*end if*/
1137 
1138     RenderBlock *cb = r->containingBlock();
1139     //if ( !cb ) return 0L;
1140     Q_ASSERT(cb);
1141 
1142     // ### which element doesn't have a block as its containing block?
1143     // Is it still possible after the RenderBlock/RenderInline merge?
1144     if (!cb->isRenderBlock()) {
1145         qCWarning(KHTML_LOG) << "containing block is no render block!!! crash imminent";
1146     }/*end if*/
1147 
1148     InlineFlowBox *flowBox = cb->firstLineBox();
1149     // (C)
1150     // This case strikes when the element is replaced, but neither a
1151     // RenderBlock nor a RenderInline
1152     if (!flowBox) {   // ### utter emergency (why is this possible at all?)
1153 //    flowBox = generateDummyFlowBox(arena, cb, r);
1154 //    if (ibox) *ibox = flowBox->firstChild();
1155 //    outside = outside_end = true;
1156 
1157 //    qCWarning(KHTML_LOG) << "containing block contains no inline flow boxes!!! crash imminent";
1158 #if DEBUG_CARETMODE > 0
1159         // qCDebug(KHTML_LOG) << "=================== end findCaretBoxLine (2)";
1160 #endif
1161         return CaretBoxLine::constructCaretBoxLine(cblDeleter, cb,
1162                 outside, outsideEnd, caretBoxIt);
1163     }/*end if*/
1164 
1165     // We iterate the inline flow boxes of the containing block until
1166     // we find the given node. This has one major flaw: it is linear, and therefore
1167     // painfully slow for really large blocks.
1168     for (; flowBox; flowBox = static_cast<InlineFlowBox *>(flowBox->nextLineBox())) {
1169 #if DEBUG_CARETMODE > 0
1170         // qCDebug(KHTML_LOG) << "[scan line]";
1171 #endif
1172 
1173         // construct a caret line box and stop when the element is contained within
1174         InlineFlowBox *baseFlowBox = seekBaseFlowBox(flowBox, base);
1175         CaretBoxLine *cbl = CaretBoxLine::constructCaretBoxLine(cblDeleter,
1176                             baseFlowBox, 0, outside, outsideEnd, caretBoxIt, r);
1177 #if DEBUG_CARETMODE > 5
1178         // qCDebug(KHTML_LOG) << cbl->information();
1179 #endif
1180         if (caretBoxIt != cbl->end()) {
1181 #if DEBUG_CARETMODE > 0
1182             // qCDebug(KHTML_LOG) << "=================== end findCaretBoxLine (3)";
1183 #endif
1184             return cbl;
1185         }
1186     }/*next flowBox*/
1187 
1188     // no inline flow box found, approximate to nearest following node.
1189     // Danger: this is O(n^2). It's only called to recover from
1190     // errors, that means, theoretically, never. (Practically, far too often :-( )
1191     Q_ASSERT(!flowBox);
1192     CaretBoxLine *cbl = findCaretBoxLine(nextLeafNode(node, base ? base->element() : 0), 0, cblDeleter, base, r_ofs, caretBoxIt);
1193 #if DEBUG_CARETMODE > 0
1194     // qCDebug(KHTML_LOG) << "=================== end findCaretBoxLine";
1195 #endif
1196     return cbl;
1197 }
1198 
1199 /** finds the innermost table object @p r is contained within, but no
1200  * farther than @p cb.
1201  * @param r leaf element to begin with
1202  * @param cb bottom element where to stop search at least.
1203  * @return the table object or 0 if none found.
1204  */
1205 static inline RenderTable *findTableUpTo(RenderObject *r, RenderFlow *cb)
1206 {
1207     while (r && r != cb && !r->isTable()) {
1208         r = r->parent();
1209     }
1210     return r && r->isTable() ? static_cast<RenderTable *>(r) : 0;
1211 }
1212 
1213 /** checks whether @p r is a descendant of @p cb, or r == cb
1214  */
1215 static inline bool isDescendant(RenderObject *r, RenderObject *cb)
1216 {
1217     while (r && r != cb) {
1218         r = r->parent();
1219     }
1220     return r;
1221 }
1222 
1223 /** checks whether the given block contains at least one editable element.
1224  *
1225  * Warning: This function has linear complexity, and therefore is expensive.
1226  * Use it sparingly, and cache the result.
1227  * @param part part
1228  * @param cb block to be searched
1229  * @param table returns the nested table if there is one directly at the beginning
1230  *  or at the end.
1231  * @param fromEnd begin search from end (default: begin from beginning)
1232  */
1233 static bool containsEditableElement(KHTMLPart *part, RenderBlock *cb,
1234                                     RenderTable *&table, bool fromEnd = false)
1235 {
1236     RenderObject *r = cb;
1237     if (fromEnd)
1238         while (r->lastChild()) {
1239             r = r->lastChild();
1240         }
1241     else
1242         while (r->firstChild()) {
1243             r = r->firstChild();
1244         }
1245 
1246     RenderTable *tempTable = 0;
1247     table = 0;
1248     bool withinCb;
1249 //  int state;      // not used
1250     ObjectTraversalState trav = InsideDescending;
1251     do {
1252         bool modWithinCb = withinCb = isDescendant(r, cb);
1253 
1254         // treat cb extra, it would not be considered otherwise
1255         if (!modWithinCb) {
1256             modWithinCb = true;
1257             r = cb;
1258         } else {
1259             tempTable = findTableUpTo(r, cb);
1260         }
1261 
1262 #if DEBUG_CARETMODE > 1
1263         // qCDebug(KHTML_LOG) << "cee: r " << (r ? r->renderName() : QString()) << "@" << r << " cb " << cb << " withinCb " << withinCb << " modWithinCb " << modWithinCb << " tempTable " << tempTable;
1264 #endif
1265         if (r && modWithinCb && r->element() && !isUnsuitable(r, trav)
1266                 && (part->isCaretMode() || part->isEditable()
1267                     || r->style()->userInput() == UI_ENABLED)) {
1268             table = tempTable;
1269 #if DEBUG_CARETMODE > 1
1270             // qCDebug(KHTML_LOG) << "cee: editable";
1271 #endif
1272             return true;
1273         }/*end if*/
1274 
1275 //    RenderObject *oldr = r;
1276 //    while (r && r == oldr)
1277 //      r = advanceSuitableObject(r, trav, fromEnd, cb->parent(), state);
1278         r = fromEnd ? r->objectAbove() : r->objectBelow();
1279     } while (r && withinCb);
1280     return false;
1281 }
1282 
1283 /** checks whether the given block contains at least one editable child
1284  * element, beginning with but excluding @p start.
1285  *
1286  * Warning: This function has linear complexity, and therefore is expensive.
1287  * Use it sparingly, and cache the result.
1288  * @param part part
1289  * @param cb block to be searched
1290  * @param table returns the nested table if there is one directly before/after
1291  *  the start object.
1292  * @param fromEnd begin search from end (default: begin from beginning)
1293  * @param start object after which to begin search.
1294  */
1295 static bool containsEditableChildElement(KHTMLPart *part, RenderBlock *cb,
1296         RenderTable *&table, bool fromEnd, RenderObject *start)
1297 {
1298     int state = 0;
1299     ObjectTraversalState trav = OutsideAscending;
1300 // qCDebug(KHTML_LOG) << "start: " << start;
1301     RenderObject *r = start;
1302     do {
1303         r = traverseRenderObjects(r, trav, fromEnd, cb->parent(), state);
1304     } while (r && !(state & AdvancedToSibling));
1305 // qCDebug(KHTML_LOG) << "r: " << r;
1306     //advanceObject(start, trav, fromEnd, cb->parent(), state);
1307 //     RenderObject *oldr = r;
1308 //     while (r && r == oldr)
1309     if (!r) {
1310         return false;
1311     }
1312 
1313     if (fromEnd)
1314         while (r->firstChild()) {
1315             r = r->firstChild();
1316         }
1317     else
1318         while (r->lastChild()) {
1319             r = r->lastChild();
1320         }
1321 // qCDebug(KHTML_LOG) << "child r: " << r;
1322     if (!r) {
1323         return false;
1324     }
1325 
1326     RenderTable *tempTable = 0;
1327     table = 0;
1328     bool withinCb = false;
1329     do {
1330 
1331         bool modWithinCb = withinCb = isDescendant(r, cb);
1332 
1333         // treat cb extra, it would not be considered otherwise
1334         if (!modWithinCb) {
1335             modWithinCb = true;
1336             r = cb;
1337         } else {
1338             tempTable = findTableUpTo(r, cb);
1339         }
1340 
1341 #if DEBUG_CARETMODE > 1
1342         // qCDebug(KHTML_LOG) << "cece: r " << (r ? r->renderName() : QString()) << "@" << r << " cb " << cb << " withinCb " << withinCb << " modWithinCb " << modWithinCb << " tempTable " << tempTable;
1343 #endif
1344         if (r && withinCb && r->element() && !isUnsuitable(r, trav)
1345                 && (part->isCaretMode() || part->isEditable()
1346                     || r->style()->userInput() == UI_ENABLED)) {
1347             table = tempTable;
1348 #if DEBUG_CARETMODE > 1
1349             // qCDebug(KHTML_LOG) << "cece: editable";
1350 #endif
1351             return true;
1352         }/*end if*/
1353 
1354         r = fromEnd ? r->objectAbove() : r->objectBelow();
1355     } while (withinCb);
1356     return false;
1357 }
1358 
1359 // == class LinearDocument implementation
1360 
1361 LinearDocument::LinearDocument(KHTMLPart *part, NodeImpl *node, long offset,
1362                                CaretAdvancePolicy advancePolicy, ElementImpl *baseElem)
1363     : node(node), offset(offset), m_part(part),
1364       advPol(advancePolicy), base(0)
1365 {
1366     if (node == 0) {
1367         return;
1368     }
1369 
1370     if (baseElem) {
1371         RenderObject *b = baseElem->renderer();
1372         if (b && (b->isRenderBlock() || b->isRenderInline())) {
1373             base = b;
1374         }
1375     }
1376 
1377     initPreBeginIterator();
1378     initEndIterator();
1379 }
1380 
1381 LinearDocument::~LinearDocument()
1382 {
1383 }
1384 
1385 int LinearDocument::count() const
1386 {
1387     // FIXME: not implemented
1388     return 1;
1389 }
1390 
1391 LinearDocument::Iterator LinearDocument::current()
1392 {
1393     return LineIterator(this, node, offset);
1394 }
1395 
1396 LinearDocument::Iterator LinearDocument::begin()
1397 {
1398     NodeImpl *n = base ? base->element() : 0;
1399     if (!base) {
1400         n = node ? node->getDocument() : 0;
1401     }
1402     if (!n) {
1403         return end();
1404     }
1405 
1406     n = n->firstChild();
1407     if (advPol == LeafsOnly)
1408         while (n->firstChild()) {
1409             n = n->firstChild();
1410         }
1411 
1412     if (!n) {
1413         return end();    // must be empty document or empty base element
1414     }
1415     return LineIterator(this, n, n->minOffset());
1416 }
1417 
1418 LinearDocument::Iterator LinearDocument::preEnd()
1419 {
1420     NodeImpl *n = base ? base->element() : 0;
1421     if (!base) {
1422         n = node ? node->getDocument() : 0;
1423     }
1424     if (!n) {
1425         return preBegin();
1426     }
1427 
1428     n = n->lastChild();
1429     if (advPol == LeafsOnly)
1430         while (n->lastChild()) {
1431             n = n->lastChild();
1432         }
1433 
1434     if (!n) {
1435         return preBegin();    // must be empty document or empty base element
1436     }
1437     return LineIterator(this, n, n->maxOffset());
1438 }
1439 
1440 void LinearDocument::initPreBeginIterator()
1441 {
1442     _preBegin = LineIterator(this, 0, 0);
1443 }
1444 
1445 void LinearDocument::initEndIterator()
1446 {
1447     _end = LineIterator(this, 0, 1);
1448 }
1449 
1450 // == class LineIterator implementation
1451 
1452 CaretBoxIterator LineIterator::currentBox /*KHTML_NO_EXPORT*/;
1453 long LineIterator::currentOffset /*KHTML_NO_EXPORT*/;
1454 
1455 LineIterator::LineIterator(LinearDocument *l, DOM::NodeImpl *node, long offset)
1456     : lines(l)
1457 {
1458 //  qCDebug(KHTML_LOG) << "LineIterator: node " << node << " offset " << offset;
1459     if (!node) {
1460         cbl = 0;
1461         return;
1462     }
1463     cbl = findCaretBoxLine(node, offset, &lines->cblDeleter,
1464                            l->baseObject(), currentOffset, currentBox);
1465     // can happen on partially loaded documents
1466 #if DEBUG_CARETMODE > 0
1467     if (!cbl) // qCDebug(KHTML_LOG) << "no render object found!";
1468 #endif
1469         if (!cbl) {
1470             return;
1471         }
1472 #if DEBUG_CARETMODE > 1
1473     // qCDebug(KHTML_LOG) << "LineIterator: offset " << offset << " outside " << cbl->isOutside();
1474 #endif
1475 #if DEBUG_CARETMODE > 3
1476     // qCDebug(KHTML_LOG) << cbl->information();
1477 #endif
1478     if (currentBox == cbl->end()) {
1479 #if DEBUG_CARETMODE > 0
1480         // qCDebug(KHTML_LOG) << "LineIterator: findCaretBoxLine failed";
1481 #endif
1482         cbl = 0;
1483     }/*end if*/
1484 }
1485 
1486 void LineIterator::nextBlock()
1487 {
1488     RenderObject *base = lines->baseObject();
1489 
1490     bool cb_outside = cbl->isOutside();
1491     bool cb_outside_end = cbl->isOutsideEnd();
1492 
1493     {
1494         RenderObject *r = cbl->enclosingObject();
1495 
1496         ObjectTraversalState trav;
1497         int state;      // not used
1498         mapRenderPosToTraversalState(cb_outside, cb_outside_end, false, trav);
1499 #if DEBUG_CARETMODE > 1
1500         // qCDebug(KHTML_LOG) << "nextBlock: before adv r" << r << ' ' << (r ? r->renderName() : QString()) << (r && r->isText() ? " contains \"" + QString(((RenderText *)r)->str->s, qMin(((RenderText *)r)->str->l,15)) + "\"" : QString()) << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end;
1501 #endif
1502         r = advanceSuitableObject(r, trav, false, base, state);
1503         if (!r) {
1504             cbl = 0;
1505             return;
1506         }/*end if*/
1507 
1508         mapTraversalStateToRenderPos(trav, false, cb_outside, cb_outside_end);
1509 #if DEBUG_CARETMODE > 1
1510         // qCDebug(KHTML_LOG) << "nextBlock: after r" << r << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end;
1511 #endif
1512 #if DEBUG_CARETMODE > 0
1513         // qCDebug(KHTML_LOG) << "++: r " << r << "[" << (r?r->renderName():QString()) << "]";
1514 #endif
1515 
1516         RenderBlock *cb;
1517 
1518         // If we hit a block or replaced object, use this as its enclosing object
1519         bool isrepl = isBlockRenderReplaced(r);
1520         if (r->isRenderBlock() || isrepl) {
1521             RenderBox *cb = static_cast<RenderBox *>(r);
1522 
1523             cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
1524                     cb_outside, cb_outside_end, currentBox);
1525 
1526 #if DEBUG_CARETMODE > 0
1527             // qCDebug(KHTML_LOG) << "r->isFlow is cb. continuation @" << cb->continuation();
1528 #endif
1529             return;
1530         } else {
1531             cb = r->containingBlock();
1532             Q_ASSERT(cb->isRenderBlock());
1533         }/*end if*/
1534         InlineFlowBox *flowBox = cb->firstLineBox();
1535 #if DEBUG_CARETMODE > 0
1536         // qCDebug(KHTML_LOG) << "++: flowBox " << flowBox << " cb " << cb << '[' << (cb?cb->renderName()+QString(".node ")+QString::number((unsigned)cb->element(),16)+(cb->element()?'@'+cb->element()->nodeName().string():QString()):QString()) << ']';
1537 #endif
1538         Q_ASSERT(flowBox);
1539         if (!flowBox) { // ### utter emergency (why is this possible at all?)
1540             cb_outside = cb_outside_end = true;
1541             cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
1542                     cb_outside, cb_outside_end, currentBox);
1543             return;
1544         }
1545 
1546         bool seekOutside = false, seekOutsideEnd = false; // silence gcc uninit warning
1547         CaretBoxIterator it;
1548         cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
1549                 flowBox, flowBox->firstChild(), seekOutside, seekOutsideEnd, it);
1550     }
1551 }
1552 
1553 void LineIterator::prevBlock()
1554 {
1555     RenderObject *base = lines->baseObject();
1556 
1557     bool cb_outside = cbl->isOutside();
1558     bool cb_outside_end = cbl->isOutsideEnd();
1559 
1560     {
1561         RenderObject *r = cbl->enclosingObject();
1562         if (r->isAnonymous() && !cb_outside) {
1563             cb_outside = true, cb_outside_end = false;
1564         }
1565 
1566         ObjectTraversalState trav;
1567         int state;      // not used
1568         mapRenderPosToTraversalState(cb_outside, cb_outside_end, true, trav);
1569 #if DEBUG_CARETMODE > 1
1570         // qCDebug(KHTML_LOG) << "prevBlock: before adv r" << r << " " << (r ? r->renderName() : QString()) << (r && r->isText() ? " contains \"" + QString(((RenderText *)r)->str->s, qMin(((RenderText *)r)->str->l,15)) + "\"" : QString()) << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end;
1571 #endif
1572         r = advanceSuitableObject(r, trav, true, base, state);
1573         if (!r) {
1574             cbl = 0;
1575             return;
1576         }/*end if*/
1577 
1578         mapTraversalStateToRenderPos(trav, true, cb_outside, cb_outside_end);
1579 #if DEBUG_CARETMODE > 1
1580         // qCDebug(KHTML_LOG) << "prevBlock: after r" << r << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end;
1581 #endif
1582 #if DEBUG_CARETMODE > 0
1583         // qCDebug(KHTML_LOG) << "--: r " << r << "[" << (r?r->renderName():QString()) << "]";
1584 #endif
1585 
1586         RenderBlock *cb;
1587 
1588         // If we hit a block, use this as its enclosing object
1589         bool isrepl = isBlockRenderReplaced(r);
1590 //    qCDebug(KHTML_LOG) << "isrepl " << isrepl << " isblock " << r->isRenderBlock();
1591         if (r->isRenderBlock() || isrepl) {
1592             RenderBox *cb = static_cast<RenderBox *>(r);
1593 
1594             cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
1595                     cb_outside, cb_outside_end, currentBox);
1596 
1597 #if DEBUG_CARETMODE > 0
1598             // qCDebug(KHTML_LOG) << "r->isFlow is cb. continuation @" << cb->continuation();
1599 #endif
1600             return;
1601         } else {
1602             cb = r->containingBlock();
1603             Q_ASSERT(cb->isRenderBlock());
1604         }/*end if*/
1605         InlineFlowBox *flowBox = cb->lastLineBox();
1606 #if DEBUG_CARETMODE > 0
1607         // qCDebug(KHTML_LOG) << "--: flowBox " << flowBox << " cb " << cb << "[" << (cb?cb->renderName()+QString(".node ")+QString::number((unsigned)cb->element(),16)+(cb->element()?"@"+cb->element()->nodeName().string():QString()):QString()) << "]";
1608 #endif
1609         Q_ASSERT(flowBox);
1610         if (!flowBox) { // ### utter emergency (why is this possible at all?)
1611             cb_outside = true; cb_outside_end = false;
1612             cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb,
1613                     cb_outside, cb_outside_end, currentBox);
1614             return;
1615         }
1616 
1617         bool seekOutside = false, seekOutsideEnd = false; // silence gcc uninit warning
1618         CaretBoxIterator it;
1619         cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
1620                 flowBox, flowBox->firstChild(), seekOutside, seekOutsideEnd, it);
1621     }
1622 }
1623 
1624 void LineIterator::advance(bool toBegin)
1625 {
1626     InlineFlowBox *flowBox = cbl->baseFlowBox();
1627     if (flowBox) {
1628         flowBox = static_cast<InlineFlowBox *>(toBegin ? flowBox->prevLineBox() : flowBox->nextLineBox());
1629         if (flowBox) {
1630             bool seekOutside = false, seekOutsideEnd = false; // silence gcc uninit warning
1631             CaretBoxIterator it;
1632             cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
1633                     flowBox, flowBox->firstChild(), seekOutside, seekOutsideEnd, it);
1634         }/*end if*/
1635     }/*end if*/
1636 
1637     // if there are no more lines in this block, move towards block to come
1638     if (!flowBox) {
1639         if (toBegin) {
1640             prevBlock();
1641         } else {
1642             nextBlock();
1643         }
1644     }
1645 
1646 #if DEBUG_CARETMODE > 3
1647     if (cbl) // qCDebug(KHTML_LOG) << cbl->information();
1648 #endif
1649     }
1650 
1651 // == class EditableCaretBoxIterator implementation
1652 
1653 void EditableCaretBoxIterator::advance(bool toBegin)
1654 {
1655 #if DEBUG_CARETMODE > 3
1656     // qCDebug(KHTML_LOG) << "---------------" << "toBegin " << toBegin;
1657 #endif
1658     const CaretBoxIterator preBegin = cbl->preBegin();
1659     const CaretBoxIterator end = cbl->end();
1660 
1661     CaretBoxIterator lastbox = *this, curbox;
1662     bool islastuseable = true;    // silence gcc
1663     bool iscuruseable;
1664     // Assume adjacency of caret boxes. Will be falsified later if applicable.
1665     adjacent = true;
1666 
1667 #if DEBUG_CARETMODE > 4
1668 //       qCDebug(KHTML_LOG) << "ebit::advance: before: " << (**this)->object() << "@" << (**this)->object()->renderName() << ".node " << (**this)->object()->element() << "[" << ((**this)->object()->element() ? (**this)->object()->element()->nodeName().string() : QString()) << "] inline " << (**this)->isInline() << " outside " << (**this)->isOutside() << " outsideEnd " << (**this)->isOutsideEnd();
1669 #endif
1670 
1671     if (toBegin) {
1672         CaretBoxIterator::operator --();
1673     } else {
1674         CaretBoxIterator::operator ++();
1675     }
1676     bool curAtEnd = *this == preBegin || *this == end;
1677     curbox = *this;
1678     bool atEnd = true;
1679     if (!curAtEnd) {
1680         iscuruseable = isEditable(curbox, toBegin);
1681         if (toBegin) {
1682             CaretBoxIterator::operator --();
1683         } else {
1684             CaretBoxIterator::operator ++();
1685         }
1686         atEnd = *this == preBegin || *this == end;
1687     }
1688     while (!curAtEnd) {
1689         bool haslast = lastbox != end && lastbox != preBegin;
1690         bool hascoming = !atEnd;
1691         bool iscominguseable = true; // silence gcc
1692 
1693         if (!atEnd) {
1694             iscominguseable = isEditable(*this, toBegin);
1695         }
1696         if (iscuruseable) {
1697 #if DEBUG_CARETMODE > 3
1698             // qCDebug(KHTML_LOG) << "ebit::advance: " << (*curbox)->object() << "@" << (*curbox)->object()->renderName() << ".node " << (*curbox)->object()->element() << "[" << ((*curbox)->object()->element() ? (*curbox)->object()->element()->nodeName().string() : QString()) << "] inline " << (*curbox)->isInline() << " outside " << (*curbox)->isOutside() << " outsideEnd " << (*curbox)->isOutsideEnd();
1699 #endif
1700 
1701             CaretBox *box = *curbox;
1702             if (box->isOutside()) {
1703                 // if this caret box represents no inline box, it is an outside box
1704                 // which has to be considered unconditionally
1705                 if (!box->isInline()) {
1706                     break;
1707                 }
1708 
1709                 if (advpol == VisibleFlows) {
1710                     break;
1711                 }
1712 
1713                 // IndicatedFlows and LeafsOnly are treated equally in caret box lines
1714 
1715                 InlineBox *ibox = box->inlineBox();
1716                 // get previous inline box
1717                 InlineBox *prev = box->isOutsideEnd() ? ibox : ibox->prevOnLine();
1718                 // get next inline box
1719                 InlineBox *next = box->isOutsideEnd() ? ibox->nextOnLine() : ibox;
1720 
1721                 const bool isprevindicated = !prev || isIndicatedInlineBox(prev);
1722                 const bool isnextindicated = !next || isIndicatedInlineBox(next);
1723                 const bool last = haslast && !islastuseable;
1724                 const bool coming = hascoming && !iscominguseable;
1725                 const bool left = !prev || prev->isInlineFlowBox() && isprevindicated
1726                                   || (toBegin && coming || !toBegin && last);
1727                 const bool right = !next || next->isInlineFlowBox() && isnextindicated
1728                                    || (!toBegin && coming || toBegin && last);
1729                 const bool text2indicated = toBegin && next && next->isInlineTextBox()
1730                                             && isprevindicated
1731                                             || !toBegin && prev && prev->isInlineTextBox() && isnextindicated;
1732                 const bool indicated2text = !toBegin && next && next->isInlineTextBox()
1733                                             && prev && isprevindicated
1734                                             // ### this code is so broken.
1735                                             /*|| toBegin && prev && prev->isInlineTextBox() && isnextindicated*/;
1736 #if DEBUG_CARETMODE > 5
1737                 // qCDebug(KHTML_LOG) << "prev " << prev << " haslast " << haslast << " islastuseable " << islastuseable << " left " << left << " next " << next << " hascoming " << hascoming << " iscominguseable " << iscominguseable << " right " << right << " text2indicated " << text2indicated << " indicated2text " << indicated2text;
1738 #endif
1739 
1740                 if (left && right && !text2indicated || indicated2text) {
1741                     adjacent = false;
1742 #if DEBUG_CARETMODE > 4
1743                     // qCDebug(KHTML_LOG) << "left && right && !text2indicated || indicated2text";
1744 #endif
1745                     break;
1746                 }
1747 
1748             } else {
1749                 // inside boxes are *always* valid
1750 #if DEBUG_CARETMODE > 4
1751                 if (box->isInline()) {
1752                     InlineBox *ibox = box->inlineBox();
1753                     // qCDebug(KHTML_LOG) << "inside " << (!ibox->isInlineFlowBox() || static_cast<InlineFlowBox *>(ibox)->firstChild() ? "non-empty" : "empty") << (isIndicatedInlineBox(ibox) ? " indicated" : "") << " adjacent=" << adjacent;
1754                 }
1755 #if 0
1756                 RenderStyle *s = ibox->object()->style();
1757                 // qCDebug(KHTML_LOG)   << "bordls " << s->borderLeftStyle()
1758                         << " bordl " << (s->borderLeftStyle() != BNONE)
1759                         << " bordr " << (s->borderRightStyle() != BNONE)
1760                         << " bordt " << (s->borderTopStyle() != BNONE)
1761                         << " bordb " << (s->borderBottomStyle() != BNONE)
1762                         << " padl " << s->paddingLeft().value()
1763                         << " padr " << s->paddingRight().value()
1764                         << " padt " << s->paddingTop().value()
1765                         << " padb " << s->paddingBottom().value()
1766                         // ### Can inline elements have top/bottom margins? Couldn't find
1767                         // it in the CSS 2 spec, but Mozilla ignores them, so we do, too.
1768                         << " marl " << s->marginLeft().value()
1769                         << " marr " << s->marginRight().value();
1770 #endif
1771 #endif
1772                 break;
1773             }/*end if*/
1774 
1775         } else {
1776 
1777             if (!(*curbox)->isOutside()) {
1778                 // cannot be adjacent anymore
1779                 adjacent = false;
1780             }
1781 
1782         }/*end if*/
1783         lastbox = curbox;
1784         islastuseable = iscuruseable;
1785         curbox = *this;
1786         iscuruseable = iscominguseable;
1787         curAtEnd = atEnd;
1788         if (!atEnd) {
1789             if (toBegin) {
1790                 CaretBoxIterator::operator --();
1791             } else {
1792                 CaretBoxIterator::operator ++();
1793             }
1794             atEnd = *this == preBegin || *this == end;
1795         }/*end if*/
1796     }/*wend*/
1797 
1798     *static_cast<CaretBoxIterator *>(this) = curbox;
1799 #if DEBUG_CARETMODE > 4
1800 //  qCDebug(KHTML_LOG) << "still valid? " << (*this != preBegin && *this != end);
1801 #endif
1802 #if DEBUG_CARETMODE > 3
1803     // qCDebug(KHTML_LOG) << "---------------" << "end ";
1804 #endif
1805 }
1806 
1807 bool EditableCaretBoxIterator::isEditable(const CaretBoxIterator &boxit, bool fromEnd)
1808 {
1809     Q_ASSERT(boxit != cbl->end() && boxit != cbl->preBegin());
1810     CaretBox *b = *boxit;
1811     RenderObject *r = b->object();
1812 #if DEBUG_CARETMODE > 0
1813 //  if (b->isInlineFlowBox()) qCDebug(KHTML_LOG) << "b is inline flow box" << (outside ? " (outside)" : "");
1814     // qCDebug(KHTML_LOG) << "isEditable r" << r << ": " << (r ? r->renderName() : QString()) << (r && r->isText() ? " contains \"" + QString(((RenderText *)r)->str->s, qMin(((RenderText *)r)->str->l,15)) + "\"" : QString());
1815 #endif
1816     // Must check caret mode or design mode *after* r->element(), otherwise
1817     // lines without a backing DOM node get regarded, leading to a crash.
1818     // ### check should actually be in InlineBoxIterator
1819     NodeImpl *node = r->element();
1820     ObjectTraversalState trav;
1821     mapRenderPosToTraversalState(b->isOutside(), b->isOutsideEnd(), fromEnd, trav);
1822     if (isUnsuitable(r, trav) || !node) {
1823         return false;
1824     }
1825 
1826     // generally exclude replaced elements with no children from navigation
1827     if (!b->isOutside() && r->isRenderReplaced() && !r->firstChild()) {
1828         return false;
1829     }
1830 
1831     RenderObject *eff_r = r;
1832     bool globallyNavigable = m_part->isCaretMode() || m_part->isEditable();
1833 
1834     // calculate the parent element's editability if this inline box is outside.
1835     if (b->isOutside() && !globallyNavigable) {
1836         NodeImpl *par = node->parent();
1837         // I wonder whether par can be 0. It shouldn't be possible if the
1838         // algorithm contained no bugs.
1839         Q_ASSERT(par);
1840         if (par) {
1841             node = par;
1842         }
1843         eff_r = node->renderer();
1844         Q_ASSERT(eff_r);    // this is a hard requirement
1845     }
1846 
1847     bool result = globallyNavigable || eff_r->style()->userInput() == UI_ENABLED;
1848 #if DEBUG_CARETMODE > 0
1849     // qCDebug(KHTML_LOG) << result;
1850 #endif
1851     return result;
1852 }
1853 
1854 // == class EditableLineIterator implementation
1855 
1856 void EditableLineIterator::advance(bool toBegin)
1857 {
1858     CaretAdvancePolicy advpol = lines->advancePolicy();
1859     LineIterator lasteditable, lastindicated;
1860     bool haslasteditable = false;
1861     bool haslastindicated = false;
1862     bool uselasteditable = false;
1863 
1864     LineIterator::advance(toBegin);
1865     while (cbl) {
1866         if (isEditable(*this)) {
1867 #if DEBUG_CARETMODE > 3
1868             // qCDebug(KHTML_LOG) << "advance: " << cbl->enclosingObject() << "@" << cbl->enclosingObject()->renderName() << ".node " << cbl->enclosingObject()->element() << "[" << (cbl->enclosingObject()->element() ? cbl->enclosingObject()->element()->nodeName().string() : QString()) << "]";
1869 #endif
1870 
1871             bool hasindicated = isIndicatedFlow(cbl->enclosingObject());
1872             if (hasindicated) {
1873                 haslastindicated = true;
1874                 lastindicated = *this;
1875             }
1876 
1877             switch (advpol) {
1878             case IndicatedFlows:
1879                 if (hasindicated) {
1880                     goto wend;
1881                 }
1882             // fall through
1883             case LeafsOnly:
1884                 if (cbl->isOutside()) {
1885                     break;
1886                 }
1887             // fall through
1888             case VisibleFlows: goto wend;
1889             }/*end switch*/
1890 
1891             // remember rejected editable element
1892             lasteditable = *this;
1893             haslasteditable = true;
1894 #if DEBUG_CARETMODE > 4
1895             // qCDebug(KHTML_LOG) << "remembered lasteditable " << *lasteditable;
1896 #endif
1897         } else {
1898 
1899             // If this element isn't editable, but the last one was, and it was only
1900             // rejected because it didn't match the caret advance policy, force it.
1901             // Otherwise certain combinations of editable and uneditable elements
1902             // could never be reached with some policies.
1903             if (haslasteditable) {
1904                 uselasteditable = true;
1905                 break;
1906             }
1907 
1908         }
1909         LineIterator::advance(toBegin);
1910     }/*wend*/
1911 wend:
1912 
1913     if (uselasteditable) {
1914         *this = haslastindicated ? lastindicated : lasteditable;
1915     }
1916     if (!cbl && haslastindicated) {
1917         *this = lastindicated;
1918     }
1919 }
1920 
1921 // == class EditableCharacterIterator implementation
1922 
1923 void EditableCharacterIterator::initFirstChar()
1924 {
1925     CaretBox *box = *ebit;
1926     InlineBox *b = box->inlineBox();
1927     if (_offset == box->maxOffset()) {
1928         peekNext();
1929     } else if (b && !box->isOutside() && b->isInlineTextBox()) {
1930         _char = static_cast<RenderText *>(b->object())->str->s[_offset].unicode();
1931     } else {
1932         _char = -1;
1933     }
1934 }
1935 
1936 /** returns true when the given caret box is empty, i. e. should not
1937  * take place in caret movement.
1938  */
1939 static inline bool isCaretBoxEmpty(CaretBox *box)
1940 {
1941     if (!box->isInline()) {
1942         return false;
1943     }
1944     InlineBox *ibox = box->inlineBox();
1945     return ibox->isInlineFlowBox()
1946            && !static_cast<InlineFlowBox *>(ibox)->firstChild()
1947            && !isIndicatedInlineBox(ibox);
1948 }
1949 
1950 EditableCharacterIterator &EditableCharacterIterator::operator ++()
1951 {
1952     _offset++;
1953 
1954     CaretBox *box = *ebit;
1955     InlineBox *b = box->inlineBox();
1956     long maxofs = box->maxOffset();
1957 #if DEBUG_CARETMODE > 0
1958     // qCDebug(KHTML_LOG) << "box->maxOffset() " << box->maxOffset() << " box->minOffset() " << box->minOffset();
1959 #endif
1960     if (_offset == maxofs) {
1961 #if DEBUG_CARETMODE > 2
1962 // qCDebug(KHTML_LOG) << "_offset == maxofs: " << _offset << " == " << maxofs;
1963 #endif
1964         peekNext();
1965     } else if (_offset > maxofs) {
1966 #if DEBUG_CARETMODE > 2
1967 // qCDebug(KHTML_LOG) << "_offset > maxofs: " << _offset << " > " << maxofs /*<< " _peekNext: " << _peekNext*/;
1968 #endif
1969         if (/*!_peekNext*/true) {
1970             ++ebit;
1971             if (ebit == (*_it)->end()) {  // end of line reached, go to next line
1972                 ++_it;
1973 #if DEBUG_CARETMODE > 3
1974 // qCDebug(KHTML_LOG) << "++_it";
1975 #endif
1976                 if (_it != _it.lines->end()) {
1977                     ebit = _it;
1978                     box = *ebit;
1979                     b = box->inlineBox();
1980 #if DEBUG_CARETMODE > 3
1981 // qCDebug(KHTML_LOG) << "box " << box << " b " << b << " isText " << box->isInlineTextBox();
1982 #endif
1983 
1984 #if DEBUG_CARETMODE > 3
1985                     RenderObject *_r = box->object();
1986 // qCDebug(KHTML_LOG) << "_r " << _r << ":" << _r->element()->nodeName().string();
1987 #endif
1988                     _offset = box->minOffset();
1989 #if DEBUG_CARETMODE > 3
1990 // qCDebug(KHTML_LOG) << "_offset " << _offset;
1991 #endif
1992                 } else {
1993                     b = 0;
1994                     _end = true;
1995                 }/*end if*/
1996                 goto readchar;
1997             }/*end if*/
1998         }/*end if*/
1999 
2000         bool adjacent = ebit.isAdjacent();
2001 #if 0
2002         // Jump over element if this one is not a text node.
2003         if (adjacent && !(*ebit)->isInlineTextBox()) {
2004             EditableCaretBoxIterator copy = ebit;
2005             ++ebit;
2006             if (ebit != (*_it)->end() && (*ebit)->isInlineTextBox()
2007                     /*&& (!(*ebit)->isInlineFlowBox()
2008                         || static_cast<InlineFlowBox *>(*ebit)->)*/) {
2009                 adjacent = false;
2010             } else {
2011                 ebit = copy;
2012             }
2013         }/*end if*/
2014 #endif
2015         // Jump over empty elements.
2016         if (adjacent && !(*ebit)->isInlineTextBox()) {
2017             bool noemptybox = true;
2018             while (isCaretBoxEmpty(*ebit)) {
2019                 noemptybox = false;
2020                 EditableCaretBoxIterator copy = ebit;
2021                 ++ebit;
2022                 if (ebit == (*_it)->end()) {
2023                     ebit = copy;
2024                     break;
2025                 }
2026             }
2027             if (noemptybox) {
2028                 adjacent = false;
2029             }
2030         }/*end if*/
2031 //     _r = (*ebit)->object();
2032         /*if (!_it.outside) */_offset = (*ebit)->minOffset() + adjacent;
2033         //_peekNext = 0;
2034         box = *ebit;
2035         b = box->inlineBox();
2036         goto readchar;
2037     } else {
2038     readchar:
2039         // get character
2040         if (b && !box->isOutside() && b->isInlineTextBox() && _offset < b->maxOffset()) {
2041             _char = static_cast<RenderText *>(b->object())->str->s[_offset].unicode();
2042         } else {
2043             _char = -1;
2044         }
2045     }/*end if*/
2046 #if DEBUG_CARETMODE > 2
2047 // qCDebug(KHTML_LOG) << "_offset: " << _offset /*<< " _peekNext: " << _peekNext*/ << " char '" << (char)_char << "'";
2048 #endif
2049 
2050 #if DEBUG_CARETMODE > 0
2051     if (!_end && ebit != (*_it)->end()) {
2052         CaretBox *box = *ebit;
2053         RenderObject *_r = box->object();
2054         // qCDebug(KHTML_LOG) << "echit++(1): box " << box << (box && box->isInlineTextBox() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText *>(box->object())->str->s+box->minOffset(), box->maxOffset() - box->minOffset()).string()) : QString()) << " _r " << (_r ? _r->element()->nodeName().string() : QString("<nil>"));
2055     }
2056 #endif
2057     return *this;
2058 }
2059 
2060 EditableCharacterIterator &EditableCharacterIterator::operator --()
2061 {
2062     _offset--;
2063     //qCDebug(KHTML_LOG) << "--: _offset=" << _offset;
2064 
2065     CaretBox *box = *ebit;
2066     CaretBox *_peekPrev = 0;
2067     CaretBox *_peekNext = 0;
2068     InlineBox *b = box->inlineBox();
2069     long minofs = box->minOffset();
2070 #if DEBUG_CARETMODE > 0
2071     // qCDebug(KHTML_LOG) << "box->maxOffset() " << box->maxOffset() << " box->minOffset() " << box->minOffset();
2072 #endif
2073     if (_offset == minofs) {
2074 #if DEBUG_CARETMODE > 2
2075 // qCDebug(KHTML_LOG) << "_offset == minofs: " << _offset << " == " << minofs;
2076 #endif
2077 //     _peekNext = b;
2078         // get character
2079         if (b && !box->isOutside() && b->isInlineTextBox()) {
2080             _char = static_cast<RenderText *>(b->object())->text()[_offset].unicode();
2081         } else {
2082             _char = -1;
2083         }
2084 
2085         //peekPrev();
2086         bool do_prev = false;
2087         {
2088             EditableCaretBoxIterator copy;
2089             _peekPrev = 0;
2090             do {
2091                 copy = ebit;
2092                 --ebit;
2093                 if (ebit == (*_it)->preBegin()) {
2094                     ebit = copy;
2095                     break;
2096                 }
2097             } while (isCaretBoxEmpty(*ebit));
2098             // Jump to end of previous element if it's adjacent, and a text box
2099             if (ebit.isAdjacent() && ebit != (*_it)->preBegin() && (*ebit)->isInlineTextBox()) {
2100                 _peekPrev = *ebit;
2101                 do_prev = true;
2102             } else {
2103                 ebit = copy;
2104             }
2105         }
2106         if (do_prev) {
2107             goto prev;
2108         }
2109     } else if (_offset < minofs) {
2110     prev:
2111 #if DEBUG_CARETMODE > 2
2112 // qCDebug(KHTML_LOG) << "_offset < minofs: " << _offset << " < " << minofs /*<< " _peekNext: " << _peekNext*/;
2113 #endif
2114         if (!_peekPrev) {
2115             _peekNext = *ebit;
2116             --ebit;
2117             if (ebit == (*_it)->preBegin()) { // end of line reached, go to previous line
2118                 --_it;
2119 #if DEBUG_CARETMODE > 3
2120 // qCDebug(KHTML_LOG) << "--_it";
2121 #endif
2122                 if (_it != _it.lines->preBegin()) {
2123 //    qCDebug(KHTML_LOG) << "begin from end!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
2124                     ebit = EditableCaretBoxIterator(_it, true);
2125                     box = *ebit;
2126 //    RenderObject *r = box->object();
2127 #if DEBUG_CARETMODE > 3
2128 // qCDebug(KHTML_LOG) << "box " << box << " b " << box->inlineBox() << " isText " << box->isInlineTextBox();
2129 #endif
2130                     _offset = box->maxOffset();
2131 //    if (!_it.outside) _offset = r->isBR() ? (*ebit)->minOffset() : (*ebit)->maxOffset();
2132                     _char = -1;
2133 #if DEBUG_CARETMODE > 0
2134                     // qCDebug(KHTML_LOG) << "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());
2135 #endif
2136                 } else {
2137                     _end = true;
2138                 }
2139                 return *this;
2140             }/*end if*/
2141         }/*end if*/
2142 
2143 #if DEBUG_CARETMODE > 0
2144         bool adjacent = ebit.isAdjacent();
2145         // qCDebug(KHTML_LOG) << "adjacent " << adjacent << " _peekNext " << _peekNext << " _peekNext->isInlineTextBox: " << (_peekNext ? _peekNext->isInlineTextBox() : false) << " !((*ebit)->isInlineTextBox): " << (*ebit ? !(*ebit)->isInlineTextBox() : true);
2146 #endif
2147 #if 0
2148         // Ignore this box if it isn't a text box, but the previous box was
2149         if (adjacent && _peekNext && _peekNext->isInlineTextBox()
2150                 && !(*ebit)->isInlineTextBox()) {
2151             EditableCaretBoxIterator copy = ebit;
2152             --ebit;
2153             if (ebit == (*_it)->preBegin()) /*adjacent = false;
2154       else */{
2155                 ebit = copy;
2156             }
2157         }/*end if*/
2158 #endif
2159 #if 0
2160         // Jump over empty elements.
2161         if (adjacent //&& _peekNext && _peekNext->isInlineTextBox()
2162                 && !(*ebit)->isInlineTextBox()) {
2163             bool noemptybox = true;
2164             while (isCaretBoxEmpty(*ebit)) {
2165                 noemptybox = false;
2166                 EditableCaretBoxIterator copy = ebit;
2167                 --ebit;
2168                 if (ebit == (*_it)->preBegin()) {
2169                     ebit = copy;
2170                     break;
2171                 } else {
2172                     _peekNext = *copy;
2173                 }
2174             }
2175             if (noemptybox) {
2176                 adjacent = false;
2177             }
2178         }/*end if*/
2179 #endif
2180 #if DEBUG_CARETMODE > 0
2181         // qCDebug(KHTML_LOG) << "(*ebit)->obj " << (*ebit)->object()->renderName() << "[" << (*ebit)->object() << "]" << " minOffset: " << (*ebit)->minOffset() << " maxOffset: " << (*ebit)->maxOffset();
2182 #endif
2183 #if DEBUG_CARETMODE > 3
2184         RenderObject *_r = (*ebit)->object();
2185 // qCDebug(KHTML_LOG) << "_r " << _r << ":" << _r->element()->nodeName().string();
2186 #endif
2187         _offset = (*ebit)->maxOffset();
2188 //     if (!_it.outside) _offset = (*ebit)->maxOffset()/* - adjacent*/;
2189 #if DEBUG_CARETMODE > 3
2190 // qCDebug(KHTML_LOG) << "_offset " << _offset;
2191 #endif
2192         _peekPrev = 0;
2193     } else {
2194 #if DEBUG_CARETMODE > 0
2195 // qCDebug(KHTML_LOG) << "_offset: " << _offset << " _peekNext: " << _peekNext;
2196 #endif
2197         // get character
2198         if (_peekNext && _offset >= box->maxOffset() && _peekNext->isInlineTextBox()) {
2199             _char = static_cast<RenderText *>(_peekNext->object())->text()[_peekNext->minOffset()].unicode();
2200         } else if (b && _offset < b->maxOffset() && b->isInlineTextBox()) {
2201             _char = static_cast<RenderText *>(b->object())->text()[_offset].unicode();
2202         } else {
2203             _char = -1;
2204         }
2205     }/*end if*/
2206 
2207 #if DEBUG_CARETMODE > 0
2208     if (!_end && ebit != (*_it)->preBegin()) {
2209         CaretBox *box = *ebit;
2210         // qCDebug(KHTML_LOG) << "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());
2211     }
2212 #endif
2213     return *this;
2214 }
2215 
2216 // == class TableRowIterator implementation
2217 
2218 TableRowIterator::TableRowIterator(RenderTable *table, bool fromEnd,
2219                                    RenderTableSection::RowStruct *row)
2220     : sec(table, fromEnd)
2221 {
2222     // set index
2223     if (*sec) {
2224         if (fromEnd) {
2225             index = (*sec)->grid.size() - 1;
2226         } else {
2227             index = 0;
2228         }
2229     }/*end if*/
2230 
2231     // initialize with given row
2232     if (row && *sec) {
2233         while (operator *() != row)
2234             if (fromEnd) {
2235                 operator --();
2236             } else {
2237                 operator ++();
2238             }
2239     }/*end if*/
2240 }
2241 
2242 TableRowIterator &TableRowIterator::operator ++()
2243 {
2244     index++;
2245 
2246     if (index >= (int)(*sec)->grid.size()) {
2247         ++sec;
2248 
2249         if (*sec) {
2250             index = 0;
2251         }
2252     }/*end if*/
2253     return *this;
2254 }
2255 
2256 TableRowIterator &TableRowIterator::operator --()
2257 {
2258     index--;
2259 
2260     if (index < 0) {
2261         --sec;
2262 
2263         if (*sec) {
2264             index = (*sec)->grid.size() - 1;
2265         }
2266     }/*end if*/
2267     return *this;
2268 }
2269 
2270 // == class ErgonomicEditableLineIterator implementation
2271 
2272 // some decls
2273 static RenderTableCell *findNearestTableCellInRow(KHTMLPart *part, int x,
2274         RenderTableSection::RowStruct *row, bool fromEnd);
2275 
2276 /** finds the cell corresponding to absolute x-coordinate @p x in the given
2277  * table.
2278  *
2279  * If there is no direct cell, or the cell is not accessible, the function
2280  * will return the nearest suitable cell.
2281  * @param part part containing the document
2282  * @param x absolute x-coordinate
2283  * @param it table row iterator, will be adapted accordingly as more rows are
2284  *  investigated.
2285  * @param fromEnd @p true to begin search from end and work towards the
2286  *  beginning
2287  * @return the cell, or 0 if no editable cell was found.
2288  */
2289 static inline RenderTableCell *findNearestTableCell(KHTMLPart *part, int x,
2290         TableRowIterator &it, bool fromEnd)
2291 {
2292     RenderTableCell *result = 0;
2293 
2294     while (*it) {
2295         result = findNearestTableCellInRow(part, x, *it, fromEnd);
2296         if (result) {
2297             break;
2298         }
2299 
2300         if (fromEnd) {
2301             --it;
2302         } else {
2303             ++it;
2304         }
2305     }/*wend*/
2306 
2307     return result;
2308 }
2309 
2310 /** finds the nearest editable cell around the given absolute x-coordinate
2311  *
2312  * It will dive into nested tables as necessary to provide seamless navigation.
2313  *
2314  * If the cell at @p x is not editable, its left neighbor is tried, then its
2315  * right neighbor, then the left neighbor's left neighbor etc. If no
2316  * editable cell can be found, 0 is returned.
2317  * @param part khtml part
2318  * @param x absolute x-coordinate
2319  * @param row table row to be searched
2320  * @param fromEnd @p true, begin from end (applies only to nested tables)
2321  * @return the found cell or 0 if no editable cell was found
2322  */
2323 static RenderTableCell *findNearestTableCellInRow(KHTMLPart *part, int x,
2324         RenderTableSection::RowStruct *row, bool fromEnd)
2325 {
2326     // First pass. Find spatially nearest cell.
2327     int n = (int)row->row->size();
2328     int i;
2329     for (i = 0; i < n; i++) {
2330         RenderTableCell *cell = row->row->at(i);
2331         if (!cell || (long)cell == -1) {
2332             continue;
2333         }
2334 
2335         int absx, absy;
2336         cell->absolutePosition(absx, absy, false); // ### position: fixed?
2337 #if DEBUG_CARETMODE > 1
2338         // qCDebug(KHTML_LOG) << "i/n " << i << "/" << n << " absx " << absx << " absy " << absy;
2339 #endif
2340 
2341         // I rely on the assumption that all cells are in ascending visual order
2342         // ### maybe this assumption is wrong for bidi?
2343 #if DEBUG_CARETMODE > 1
2344         // qCDebug(KHTML_LOG) << "x " << x << " < " << (absx + cell->width()) << "?";
2345 #endif
2346         if (x < absx + cell->width()) {
2347             break;
2348         }
2349     }/*next i*/
2350     if (i >= n) {
2351         i = n - 1;
2352     }
2353 
2354     // Second pass. Find editable cell, beginning with the currently found,
2355     // extending to the left, and to the right, alternating.
2356     for (int cnt = 0; cnt < 2 * n; cnt++) {
2357         int index = i - ((cnt >> 1) + 1) * (cnt & 1) + (cnt >> 1) * !(cnt & 1);
2358         if (index < 0 || index >= n) {
2359             continue;
2360         }
2361 
2362         RenderTableCell *cell = row->row->at(index);
2363         if (!cell || (long)cell == -1) {
2364             continue;
2365         }
2366 
2367 #if DEBUG_CARETMODE > 1
2368         // qCDebug(KHTML_LOG) << "index " << index << " cell " << cell;
2369 #endif
2370         RenderTable *nestedTable;
2371         if (containsEditableElement(part, cell, nestedTable, fromEnd)) {
2372 
2373             if (nestedTable) {
2374                 TableRowIterator it(nestedTable, fromEnd);
2375                 while (*it) {
2376 // qCDebug(KHTML_LOG) << "=== recursive invocation";
2377                     cell = findNearestTableCell(part, x, it, fromEnd);
2378                     if (cell) {
2379                         break;
2380                     }
2381                     if (fromEnd) {
2382                         --it;
2383                     } else {
2384                         ++it;
2385                     }
2386                 }/*wend*/
2387             }/*end if*/
2388 
2389             return cell;
2390         }/*end if*/
2391     }/*next i*/
2392     return 0;
2393 }
2394 
2395 /** returns the nearest common ancestor of two objects that is a table cell,
2396  * a table section, or 0 if not inside a common table.
2397  *
2398  * If @p r1 and @p r2 belong to the same table, but different sections, @p r1's
2399  * section is returned.
2400  */
2401 static RenderObject *commonAncestorTableSectionOrCell(RenderObject *r1,
2402         RenderObject *r2)
2403 {
2404     if (!r1 || !r2) {
2405         return 0;
2406     }
2407     RenderTableSection *sec = 0;
2408     int start_depth = 0, end_depth = 0;
2409     // First we find the depths of the two objects in the tree (start_depth, end_depth)
2410     RenderObject *n = r1;
2411     while (n->parent()) {
2412         n = n->parent();
2413         start_depth++;
2414     }/*wend*/
2415     n = r2;
2416     while (n->parent()) {
2417         n = n->parent();
2418         end_depth++;
2419     }/*wend*/
2420     // here we climb up the tree with the deeper object, until both objects have equal depth
2421     while (end_depth > start_depth) {
2422         r2 = r2->parent();
2423         end_depth--;
2424     }/*wend*/
2425     while (start_depth > end_depth) {
2426         r1 = r1->parent();
2427 //    if (r1->isTableSection()) sec = static_cast<RenderTableSection *>(r1);
2428         start_depth--;
2429     }/*wend*/
2430     // Climb the tree with both r1 and r2 until they are the same
2431     while (r1 != r2) {
2432         r1 = r1->parent();
2433         if (r1->isTableSection()) {
2434             sec = static_cast<RenderTableSection *>(r1);
2435         }
2436         r2 = r2->parent();
2437     }/*wend*/
2438 
2439     // At this point, we found the most approximate common ancestor. Now climb
2440     // up until the condition of the function return value is satisfied.
2441     while (r1 && !r1->isTableCell() && !r1->isTableSection() && !r1->isTable()) {
2442         r1 = r1->parent();
2443     }
2444 
2445     return r1 && r1->isTable() ? sec : r1;
2446 }
2447 
2448 /** Finds the row that contains the given cell, directly, or indirectly
2449  * @param section section to be searched
2450  * @param cell table cell
2451  * @param row returns the row
2452  * @param directCell returns the direct cell that contains @p cell
2453  * @return the index of the row.
2454  */
2455 static int findRowInSection(RenderTableSection *section, RenderTableCell *cell,
2456                             RenderTableSection::RowStruct *&row, RenderTableCell *&directCell)
2457 {
2458     // Seek direct cell
2459     RenderObject *r = cell;
2460     while (r != section) {
2461         if (r->isTableCell()) {
2462             directCell = static_cast<RenderTableCell *>(r);
2463         }
2464         r = r->parent();
2465     }/*wend*/
2466 
2467     // So, and this is really nasty: As we have no indices, we have to do a
2468     // linear comparison. Oh, that sucks so much for long tables, you can't
2469     // imagine.
2470     int n = section->numRows();
2471     for (int i = 0; i < n; i++) {
2472         row = &section->grid[i];
2473 
2474         // check for cell
2475         int m = row->row->size();
2476         for (int j = 0; j < m; j++) {
2477             RenderTableCell *c = row->row->at(j);
2478             if (c == directCell) {
2479                 return i;
2480             }
2481         }/*next j*/
2482 
2483     }/*next i*/
2484     Q_ASSERT(false);
2485     return -1;
2486 }
2487 
2488 /** finds the table that is the first direct or indirect descendant of @p block.
2489  * @param leaf object to begin search from.
2490  * @param block object to search to, or 0 to search up to top.
2491  * @return the table or 0 if there were none.
2492  */
2493 static inline RenderTable *findFirstDescendantTable(RenderObject *leaf, RenderBlock *block)
2494 {
2495     RenderTable *result = 0;
2496     while (leaf && leaf != block) {
2497         if (leaf->isTable()) {
2498             result = static_cast<RenderTable *>(leaf);
2499         }
2500         leaf = leaf->parent();
2501     }/*wend*/
2502     return result;
2503 }
2504 
2505 /** looks for the table cell the given object @p r is contained within.
2506  * @return the table cell or 0 if not contained in any table.
2507  */
2508 static inline RenderTableCell *containingTableCell(RenderObject *r)
2509 {
2510     while (r && !r->isTableCell()) {
2511         r = r->parent();
2512     }
2513     return static_cast<RenderTableCell *>(r);
2514 }
2515 
2516 inline void ErgonomicEditableLineIterator::calcAndStoreNewLine(
2517     RenderBlock *newBlock, bool toBegin)
2518 {
2519     // take the first/last editable element in the found cell as the new
2520     // value for the iterator
2521     CaretBoxIterator it;
2522     cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
2523             newBlock, true, toBegin, it);
2524 #if DEBUG_CARETMODE > 3
2525     // qCDebug(KHTML_LOG) << cbl->information();
2526 #endif
2527 //  if (toBegin) prevBlock(); else nextBlock();
2528 
2529     if (!cbl) {
2530         return;
2531     }/*end if*/
2532 
2533     EditableLineIterator::advance(toBegin);
2534 }
2535 
2536 void ErgonomicEditableLineIterator::determineTopologicalElement(
2537     RenderTableCell *oldCell, RenderObject *newObject, bool toBegin)
2538 {
2539     // When we arrive here, a transition between cells has happened.
2540     // Now determine the type of the transition. This can be
2541     // (1) a transition from this cell into a table inside this cell.
2542     // (2) a transition from this cell into another cell of this table
2543 
2544     TableRowIterator it;
2545 
2546     RenderObject *commonAncestor = commonAncestorTableSectionOrCell(oldCell, newObject);
2547 #if DEBUG_CARETMODE > 1
2548     // qCDebug(KHTML_LOG) << " ancestor " << commonAncestor;
2549 #endif
2550 
2551     // The whole document is treated as a table cell.
2552     if (!commonAncestor || commonAncestor->isTableCell()) {   // (1)
2553 
2554         RenderTableCell *cell = static_cast<RenderTableCell *>(commonAncestor);
2555         RenderTable *table = findFirstDescendantTable(newObject, cell);
2556 
2557 #if DEBUG_CARETMODE > 0
2558         // qCDebug(KHTML_LOG) << "table cell: " << cell;
2559 #endif
2560 
2561         // if there is no table, we fell out of the previous table, and are now
2562         // in some table-less block. Therefore, done.
2563         if (!table) {
2564             return;
2565         }
2566 
2567         it = TableRowIterator(table, toBegin);
2568 
2569     } else if (commonAncestor->isTableSection()) {        // (2)
2570 
2571         RenderTableSection *section = static_cast<RenderTableSection *>(commonAncestor);
2572         RenderTableSection::RowStruct *row;
2573         int idx = findRowInSection(section, oldCell, row, oldCell);
2574 #if DEBUG_CARETMODE > 1
2575         // qCDebug(KHTML_LOG) << "table section: row idx " << idx;
2576 #endif
2577 
2578         it = TableRowIterator(section, idx);
2579 
2580         // advance rowspan rows
2581         int rowspan = oldCell->rowSpan();
2582         while (*it && rowspan--) {
2583             if (toBegin) {
2584                 --it;
2585             } else {
2586                 ++it;
2587             }
2588         }/*wend*/
2589 
2590     } else {
2591         kError(6201) << "Neither common cell nor section! " << commonAncestor->renderName();
2592         // will crash on uninitialized table row iterator
2593     }/*end if*/
2594 
2595     RenderTableCell *cell = findNearestTableCell(lines->m_part, xCoor, it, toBegin);
2596 #if DEBUG_CARETMODE > 1
2597     // qCDebug(KHTML_LOG) << "findNearestTableCell result: " << cell;
2598 #endif
2599 
2600     RenderBlock *newBlock = cell;
2601     if (!cell) {
2602         Q_ASSERT(commonAncestor->isTableSection());
2603         RenderTableSection *section = static_cast<RenderTableSection *>(commonAncestor);
2604         cell = containingTableCell(section);
2605 #if DEBUG_CARETMODE > 1
2606         // qCDebug(KHTML_LOG) << "containing cell: " << cell;
2607 #endif
2608 
2609         RenderTable *nestedTable;
2610         bool editableChild = cell && containsEditableChildElement(lines->m_part,
2611                              cell, nestedTable, toBegin, section->table());
2612 
2613         if (cell && !editableChild) {
2614 #if DEBUG_CARETMODE > 1
2615             // qCDebug(KHTML_LOG) << "========= recursive invocation outer =========";
2616 #endif
2617             determineTopologicalElement(cell, cell->section(), toBegin);
2618 #if DEBUG_CARETMODE > 1
2619             // qCDebug(KHTML_LOG) << "========= end recursive invocation outer =========";
2620 #endif
2621             return;
2622 
2623         } else if (cell && nestedTable) {
2624 #if DEBUG_CARETMODE > 1
2625             // qCDebug(KHTML_LOG) << "========= recursive invocation inner =========";
2626 #endif
2627             determineTopologicalElement(cell, nestedTable, toBegin);
2628 #if DEBUG_CARETMODE > 1
2629             // qCDebug(KHTML_LOG) << "========= end recursive invocation inner =========";
2630 #endif
2631             return;
2632 
2633         } else {
2634 #if DEBUG_CARETMODE > 1
2635             // qCDebug(KHTML_LOG) << "newBlock is table: " << section->table();
2636 #endif
2637             RenderObject *r = section->table();
2638             int state;        // not used
2639             ObjectTraversalState trav = OutsideAscending;
2640             r = advanceSuitableObject(r, trav, toBegin, lines->baseObject(), state);
2641             if (!r) {
2642                 cbl = 0;
2643                 return;
2644             }
2645 //      if (toBegin) prevBlock(); else nextBlock();
2646             newBlock = static_cast<RenderBlock *>(!r || r->isRenderBlock() ? r : r->containingBlock());
2647         }/*end if*/
2648 #if 0
2649     } else {
2650         // adapt cell so that prevBlock/nextBlock works as expected
2651         newBlock = cell;
2652         // on forward advancing, we must start from the outside end of the
2653         // previous object
2654         if (!toBegin) {
2655             RenderObject *r = newBlock;
2656             int state;        // not used
2657             ObjectTraversalState trav = OutsideAscending;
2658             r = advanceSuitableObject(r, trav, true, lines->advancePolicy(), lines->baseObject(), state);
2659             newBlock = static_cast<RenderBlock *>(!r || r->isRenderBlock() ? r : r->containingBlock());
2660         }/*end if*/
2661 #endif
2662     }/*end if*/
2663 
2664     calcAndStoreNewLine(newBlock, toBegin);
2665 }
2666 
2667 ErgonomicEditableLineIterator &ErgonomicEditableLineIterator::operator ++()
2668 {
2669     RenderTableCell *oldCell = containingTableCell(cbl->enclosingObject());
2670 
2671     EditableLineIterator::operator ++();
2672     if (*this == lines->end() || *this == lines->preBegin()) {
2673         return *this;
2674     }
2675 
2676     RenderTableCell *newCell = containingTableCell(cbl->enclosingObject());
2677 
2678     if (!newCell || newCell == oldCell) {
2679         return *this;
2680     }
2681 
2682     determineTopologicalElement(oldCell, newCell, false);
2683 
2684     return *this;
2685 }
2686 
2687 ErgonomicEditableLineIterator &ErgonomicEditableLineIterator::operator --()
2688 {
2689     RenderTableCell *oldCell = containingTableCell(cbl->enclosingObject());
2690 
2691     EditableLineIterator::operator --();
2692     if (*this == lines->end() || *this == lines->preBegin()) {
2693         return *this;
2694     }
2695 
2696     RenderTableCell *newCell = containingTableCell(cbl->enclosingObject());
2697 
2698     if (!newCell || newCell == oldCell) {
2699         return *this;
2700     }
2701 
2702     determineTopologicalElement(oldCell, newCell, true);
2703 
2704     return *this;
2705 }
2706 
2707 // == Navigational helper functions ==
2708 
2709 /** seeks the caret box which contains or is the nearest to @p x
2710  * @param it line iterator pointing to line to be searched
2711  * @param cv caret view context
2712  * @param x returns the cv->origX approximation, relatively positioned to the
2713  *  containing block.
2714  * @param absx returns absolute x-coordinate of containing block
2715  * @param absy returns absolute y-coordinate of containing block
2716  * @return the most suitable caret box
2717  */
2718 static CaretBox *nearestCaretBox(LineIterator &it, CaretViewContext *cv,
2719                                  int &x, int &absx, int &absy)
2720 {
2721     // Find containing block
2722     RenderObject *cb = (*it)->containingBlock();
2723 #if DEBUG_CARETMODE > 4
2724     // qCDebug(KHTML_LOG) << "nearestCB: cb " << cb << "@" << (cb ? cb->renderName() : "");
2725 #endif
2726 
2727     if (cb) {
2728         cb->absolutePosition(absx, absy);
2729     } else {
2730         absx = absy = 0;
2731     }
2732 
2733     // Otherwise find out in which inline box the caret is to be placed.
2734 
2735     // this horizontal position is to be approximated
2736     x = cv->origX - absx;
2737     CaretBox *caretBox = 0; // Inline box containing the caret
2738 //  NodeImpl *lastnode = 0;  // node of previously checked render object.
2739     int xPos;        // x-coordinate of current inline box
2740     int oldXPos = -1;    // x-coordinate of last inline box
2741     EditableCaretBoxIterator fbit = it;
2742 #if DEBUG_CARETMODE > 0
2743     /*  if (it.linearDocument()->advancePolicy() != LeafsOnly)
2744         qCWarning(KHTML_LOG) << "nearestInlineBox is only prepared to handle the LeafsOnly advance policy";*/
2745 //   qCDebug(KHTML_LOG) << "*fbit = " << *fbit;
2746 #endif
2747     // Iterate through all children
2748     for (CaretBox * b; fbit != (*it)->end(); ++fbit) {
2749         b = *fbit;
2750 
2751 #if DEBUG_CARETMODE > 0
2752 //    RenderObject *r = b->object();
2753 //  if (b->isInlineFlowBox()) qCDebug(KHTML_LOG) << "b is inline flow box";
2754 //  qCDebug(KHTML_LOG) << "approximate r" << r << ": " << (r ? r->renderName() : QString()) << (r && r->isText() ? " contains \"" + QString(((RenderText *)r)->str->s, ((RenderText *)r)->str->l) + "\"" : QString());
2755 #endif
2756         xPos = b->xPos();
2757 
2758         // the caret is before this box
2759         if (x < xPos) {
2760             // snap to nearest box
2761             if (oldXPos < 0 || x - (oldXPos + caretBox->width()) > xPos - x) {
2762                 caretBox = b;   // current box is nearer
2763             }/*end if*/
2764             break;        // Otherwise, preceding box is implicitly used
2765         }
2766 
2767         caretBox = b;
2768 
2769         // the caret is within this box
2770         if (x >= xPos && x < xPos + caretBox->width()) {
2771             break;
2772         }
2773         oldXPos = xPos;
2774 
2775         // the caret can only be after the last box which is automatically
2776         // contained in caretBox when we fall out of the loop.
2777     }/*next fbit*/
2778 
2779     return caretBox;
2780 }
2781 
2782 /** moves the given iterator to the beginning of the next word.
2783  *
2784  * If the end is reached, the iterator will be positioned there.
2785  * @param it character iterator to be moved
2786  */
2787 static void moveItToNextWord(EditableCharacterIterator &it)
2788 {
2789 #if DEBUG_CARETMODE > 0
2790     // qCDebug(KHTML_LOG) << "%%%%%%%%%%%%%%%%%%%%% moveItToNextWord";
2791 #endif
2792     EditableCharacterIterator copy;
2793     while (!it.isEnd() && !(*it).isSpace() && !(*it).isPunct()) {
2794 #if DEBUG_CARETMODE > 2
2795         // qCDebug(KHTML_LOG) << "reading1 '" << (*it).toLatin1().constData() << "'";
2796 #endif
2797         copy = it;
2798         ++it;
2799     }
2800 
2801     if (it.isEnd()) {
2802         it = copy;
2803         return;
2804     }/*end if*/
2805 
2806     while (!it.isEnd() && ((*it).isSpace() || (*it).isPunct())) {
2807 #if DEBUG_CARETMODE > 2
2808         // qCDebug(KHTML_LOG) << "reading2 '" << (*it).toLatin1().constData() << "'";
2809 #endif
2810         copy = it;
2811         ++it;
2812     }
2813 
2814     if (it.isEnd()) {
2815         it = copy;
2816     }
2817 }
2818 
2819 /** moves the given iterator to the beginning of the previous word.
2820  *
2821  * If the beginning is reached, the iterator will be positioned there.
2822  * @param it character iterator to be moved
2823  */
2824 static void moveItToPrevWord(EditableCharacterIterator &it)
2825 {
2826     if (it.isEnd()) {
2827         return;
2828     }
2829 
2830 #if DEBUG_CARETMODE > 0
2831     // qCDebug(KHTML_LOG) << "%%%%%%%%%%%%%%%%%%%%% moveItToPrevWord";
2832 #endif
2833     EditableCharacterIterator copy;
2834 
2835     // Jump over all space and punctuation characters first
2836     do {
2837         copy = it;
2838         --it;
2839 #if DEBUG_CARETMODE > 2
2840         if (!it.isEnd()) // qCDebug(KHTML_LOG) << "reading1 '" << (*it).toLatin1().constData() << "'";
2841 #endif
2842         } while (!it.isEnd() && ((*it).isSpace() || (*it).isPunct()));
2843 
2844     if (it.isEnd()) {
2845         it = copy;
2846         return;
2847     }/*end if*/
2848 
2849     do {
2850         copy = it;
2851         --it;
2852 #if DEBUG_CARETMODE > 0
2853         if (!it.isEnd()) // qCDebug(KHTML_LOG) << "reading2 '" << (*it).toLatin1().constData() << "' (" << (int)(*it).toLatin1().constData() << ") box " << it.caretBox();
2854 #endif
2855         } while (!it.isEnd() && !(*it).isSpace() && !(*it).isPunct());
2856 
2857     it = copy;
2858 #if DEBUG_CARETMODE > 1
2859     if (!it.isEnd()) // qCDebug(KHTML_LOG) << "effective '" << (*it).toLatin1().constData() << "' (" << (int)(*it).toLatin1().constData() << ") box " << it.caretBox();
2860 #endif
2861     }
2862 
2863 /** moves the iterator by one page.
2864  * @param ld linear document
2865  * @param it line iterator, will be updated accordingly
2866  * @param mindist minimum distance in pixel the iterator should be moved
2867  *  (if possible)
2868  * @param next @p true, move downward, @p false move upward
2869  */
2870 static void moveIteratorByPage(LinearDocument &ld,
2871                                ErgonomicEditableLineIterator &it, int mindist, bool next)
2872 {
2873     // ### This whole routine plainly sucks. Use an inverse strategie for pgup/pgdn.
2874 
2875     if (it == ld.end() || it == ld.preBegin()) {
2876         return;
2877     }
2878 
2879     ErgonomicEditableLineIterator copy = it;
2880 #if DEBUG_CARETMODE > 0
2881     // qCDebug(KHTML_LOG) << " mindist: " << mindist;
2882 #endif
2883 
2884     CaretBoxLine *cbl = *copy;
2885     int absx = 0, absy = 0;
2886 
2887     RenderBlock *lastcb = cbl->containingBlock();
2888     Q_ASSERT(lastcb->isRenderBlock());
2889     lastcb->absolutePosition(absx, absy, false);  // ### what about fixed?
2890 
2891     int lastfby = cbl->begin().data()->yPos();
2892     int lastheight = 0;
2893     int rescue = 1000;    // ### this is a hack to keep stuck carets from hanging the ua
2894     do {
2895         if (next) {
2896             ++copy;
2897         } else {
2898             --copy;
2899         }
2900         if (copy == ld.end() || copy == ld.preBegin()) {
2901             break;
2902         }
2903 
2904         cbl = *copy;
2905         RenderBlock *cb = cbl->containingBlock();
2906 
2907         int diff = 0;
2908         // ### actually flowBox->yPos() should suffice, but this is not ported
2909         // over yet from WebCore
2910         int fby = cbl->begin().data()->yPos();
2911         if (cb != lastcb) {
2912             if (next) {
2913                 diff = absy + lastfby + lastheight;
2914                 cb->absolutePosition(absx, absy, false);    // ### what about fixed?
2915                 diff = absy - diff + fby;
2916                 lastfby = 0;
2917             } else {
2918                 diff = absy;
2919                 cb->absolutePosition(absx, absy, false);    // ### what about fixed?
2920                 diff -= absy + fby + lastheight;
2921                 lastfby = fby - lastheight;
2922             }/*end if*/
2923 #if DEBUG_CARETMODE > 2
2924             // qCDebug(KHTML_LOG) << "absdiff " << diff;
2925 #endif
2926         } else {
2927             diff = qAbs(fby - lastfby);
2928         }/*end if*/
2929 #if DEBUG_CARETMODE > 2
2930         // qCDebug(KHTML_LOG) << "cbl->begin().data()->yPos(): " << fby << " diff " << diff;
2931 #endif
2932 
2933         mindist -= diff;
2934 
2935         lastheight = qAbs(fby - lastfby);
2936         lastfby = fby;
2937         lastcb = cb;
2938         it = copy;
2939 #if DEBUG_CARETMODE > 0
2940         // qCDebug(KHTML_LOG) << " mindist: " << mindist;
2941 #endif
2942         // trick: actually the distance is always one line short, but we cannot
2943         // calculate the height of the first line (### WebCore will make it better)
2944         // Therefore, we simply approximate that excess line by using the last
2945         // caluculated line height.
2946     } while (mindist - lastheight > 0 && --rescue);
2947 }
2948 
2949 }/*end namespace*/