diff options
Diffstat (limited to 'khtml/khtml_caret.cpp')
-rw-r--r-- | khtml/khtml_caret.cpp | 2664 |
1 files changed, 0 insertions, 2664 deletions
diff --git a/khtml/khtml_caret.cpp b/khtml/khtml_caret.cpp deleted file mode 100644 index 9b819ee6c..000000000 --- a/khtml/khtml_caret.cpp +++ /dev/null @@ -1,2664 +0,0 @@ -/* This file is part of the KDE project - * - * Copyright (C) 2003-2004 Leo Savernik <l.savernik@aon.at> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - - -#include "khtml_caret_p.h" - -#include "html/html_documentimpl.h" - -namespace khtml { - -/** Flags representing the type of advance that has been made. - * @param LeftObject a render object was left and an ascent to its parent has - * taken place - * @param AdvancedToSibling an actual advance to a sibling has taken place - * @param EnteredObject a render object was entered by descending into it from - * its parent object. - */ -enum ObjectAdvanceState { - LeftObject = 0x01, AdvancedToSibling = 0x02, EnteredObject = 0x04 -}; - -/** All possible states that may occur during render object traversal. - * @param OutsideDescending outside of the current object, ready to descend - * into children - * @param InsideDescending inside the current object, descending into - * children - * @param InsideAscending inside the current object, ascending to parents - * @param OutsideAscending outsie the current object, ascending to parents - */ -enum ObjectTraversalState { - OutsideDescending, InsideDescending, InsideAscending, OutsideAscending -}; - -/** Traverses the render object tree in a fine granularity. - * @param obj render object - * @param trav object traversal state - * @param toBegin traverse towards beginning - * @param base base render object which this method must not advance beyond - * (0 means document) - * @param state object advance state (enum ObjectAdvanceState) - * @return the render object according to the state. May be the same as \c obj - */ -static RenderObject* traverseRenderObjects(RenderObject *obj, - ObjectTraversalState &trav, bool toBegin, RenderObject *base, - int &state) -{ - RenderObject *r; - switch (trav) { - case OutsideDescending: - trav = InsideDescending; - break; - case InsideDescending: - r = toBegin ? obj->lastChild() : obj->firstChild(); - if (r) { - trav = OutsideDescending; - obj = r; - state |= EnteredObject; - } else { - trav = InsideAscending; - } - break; - case InsideAscending: - trav = OutsideAscending; - break; - case OutsideAscending: - r = toBegin ? obj->previousSibling() : obj->nextSibling(); - if (r) { - trav = OutsideDescending; - state |= AdvancedToSibling; - } else { - r = obj->parent(); - if (r == base) r = 0; - trav = InsideAscending; - state |= LeftObject; - } - obj = r; - break; - }/*end switch*/ - - return obj; -} - -/** Like RenderObject::objectBelow, but confined to stay within \c base. - * @param obj render object to begin with - * @param trav object traversal state, will be reset within this function - * @param base base render object (0: no base) - */ -static inline RenderObject *renderObjectBelow(RenderObject *obj, ObjectTraversalState &trav, RenderObject *base) -{ - trav = InsideDescending; - int state; // we don't need the state, so we don't initialize it - RenderObject *r = obj; - while (r && trav != OutsideDescending) { - r = traverseRenderObjects(r, trav, false, base, state); -#if DEBUG_CARETMODE > 3 - kdDebug(6200) << "renderObjectBelow: r " << r << " trav " << trav << endl; -#endif - } - trav = InsideDescending; - return r; -} - -/** Like RenderObject::objectAbove, but confined to stay within \c base. - * @param obj render object to begin with - * @param trav object traversal state, will be reset within this function - * @param base base render object (0: no base) - */ -static inline RenderObject *renderObjectAbove(RenderObject *obj, ObjectTraversalState &trav, RenderObject *base) -{ - trav = OutsideAscending; - int state; // we don't need the state, so we don't initialize it - RenderObject *r = obj; - while (r && trav != InsideAscending) { - r = traverseRenderObjects(r, trav, true, base, state); -#if DEBUG_CARETMODE > 3 - kdDebug(6200) << "renderObjectAbove: r " << r << " trav " << trav << endl; -#endif - } - trav = InsideAscending; - return r; -} - -/** Checks whether the given inline box matches the IndicatedFlows policy - * @param box inline box to test - * @return true on match - */ -static inline bool isIndicatedInlineBox(InlineBox *box) -{ - // text boxes are never indicated. - if (box->isInlineTextBox()) return false; - RenderStyle *s = box->object()->style(); - return s->borderLeftWidth() || s->borderRightWidth() - || s->borderTopWidth() || s->borderBottomWidth() - || s->paddingLeft().value() || s->paddingRight().value() - || s->paddingTop().value() || s->paddingBottom().value() - // ### Can inline elements have top/bottom margins? Couldn't find - // it in the CSS 2 spec, but Mozilla ignores them, so we do, too. - || s->marginLeft().value() || s->marginRight().value(); -} - -/** Checks whether the given render object matches the IndicatedFlows policy - * @param r render object to test - * @return true on match - */ -static inline bool isIndicatedFlow(RenderObject *r) -{ - RenderStyle *s = r->style(); - return s->borderLeftStyle() != BNONE || s->borderRightStyle() != BNONE - || s->borderTopStyle() != BNONE || s->borderBottomStyle() != BNONE -// || s->paddingLeft().value() || s->paddingRight().value() -// || s->paddingTop().value() || s->paddingBottom().value() -// || s->marginLeft().value() || s->marginRight().value() - || s->hasClip() || s->hidesOverflow() - || s->backgroundColor().isValid() || s->backgroundImage(); -} - -/** Advances to the next render object, taking into account the current - * traversal state. - * - * @param r render object - * @param trav object traversal state - * @param toBegin @p true, advance towards beginning, @p false, advance toward end - * @param base base render object which this method must not advance beyond - * (0 means document) - * @param state object advance state (enum ObjectAdvanceState) (unchanged - * on LeafsOnly) - * @return a pointer to the render object which we advanced to, - * or 0 if the last possible object has been reached. - */ -static RenderObject *advanceObject(RenderObject *r, - ObjectTraversalState &trav, bool toBegin, - RenderObject *base, int &state) -{ - - ObjectTraversalState origtrav = trav; - RenderObject *a = traverseRenderObjects(r, trav, toBegin, base, state); - - bool ignoreOutsideDesc = toBegin && origtrav == OutsideAscending; - - // render object and traversal state at which look ahead has been started - RenderObject *la = 0; - ObjectTraversalState latrav = trav; - ObjectTraversalState lasttrav = origtrav; - - while (a) { -#if DEBUG_CARETMODE > 5 -kdDebug(6200) << "a " << a << " trav " << trav << endl; -#endif - if (a->element()) { -#if DEBUG_CARETMODE > 4 -kdDebug(6200) << "a " << a << " trav " << trav << " origtrav " << origtrav << " ignoreOD " << ignoreOutsideDesc << endl; -#endif - if (toBegin) { - - switch (origtrav) { - case OutsideDescending: - if (trav == InsideAscending) return a; - if (trav == OutsideDescending) return a; - break; - case InsideDescending: - if (trav == OutsideDescending) return a; - // fall through - case InsideAscending: - if (trav == OutsideAscending) return a; - break; - case OutsideAscending: - if (trav == OutsideAscending) return a; - if (trav == InsideAscending && lasttrav == InsideDescending) return a; - if (trav == OutsideDescending && !ignoreOutsideDesc) return a; - // ignore this outside descending position, as it effectively - // demarkates the same position, but remember it in case we fall off - // the document. - la = a; latrav = trav; - ignoreOutsideDesc = false; - break; - }/*end switch*/ - - } else { - - switch (origtrav) { - case OutsideDescending: - if (trav == InsideAscending) return a; - if (trav == OutsideDescending) return a; - break; - case InsideDescending: -// if (trav == OutsideDescending) return a; - // fall through - case InsideAscending: -// if (trav == OutsideAscending) return a; -// break; - case OutsideAscending: - // ### what if origtrav == OA, and immediately afterwards trav - // becomes OD? In this case the effective position hasn't changed -> - // the caret gets stuck. Otherwise, it apparently cannot happen in - // real usage patterns. - if (trav == OutsideDescending) return a; - if (trav == OutsideAscending) { - if (la) return la; - // starting lookahead here. Remember old object in case we fall off - // the document. - la = a; latrav = trav; - } - break; - }/*end switch*/ - - }/*end if*/ - }/*end if*/ - - lasttrav = trav; - a = traverseRenderObjects(a, trav, toBegin, base, state); - }/*wend*/ - - if (la) trav = latrav, a = la; - return a; - -} - -/** Check whether the current render object is unsuitable in caret mode handling. - * - * Some render objects cannot be handled correctly in caret mode. These objects - * are therefore considered to be unsuitable. The null object is suitable, as - * it denotes reaching the end. - * @param r current render object - * @param trav current traversal state - */ -static inline bool isUnsuitable(RenderObject *r, ObjectTraversalState trav) -{ - if (!r) return false; - return r->isTableCol() || r->isTableSection() || r->isTableRow() - || (r->isText() && static_cast<RenderText *>(r)->inlineTextBoxCount() == 0); - ; - Q_UNUSED(trav); -} - -/** Advances to the next render object, taking into account the current - * traversal state, but skipping render objects which are not suitable for - * having placed the caret into them. - * @param r render object - * @param trav object traversal state (unchanged on LeafsOnly) - * @param toBegin @p true, advance towards beginning, @p false, advance toward end - * @param base base render object which this method must not advance beyond - * (0 means document) - * @param state object advance state (enum ObjectAdvanceState) (unchanged - * on LeafsOnly) - * @return a pointer to the advanced render object or 0 if the last possible - * object has been reached. - */ -static inline RenderObject *advanceSuitableObject(RenderObject *r, - ObjectTraversalState &trav, bool toBegin, - RenderObject *base, int &state) -{ - do { - r = advanceObject(r, trav, toBegin, base, state); -#if DEBUG_CARETMODE > 2 - kdDebug(6200) << "after advanceSWP: r " << r << " trav " << trav << " toBegin " << toBegin << endl; -#endif - } while (isUnsuitable(r, trav)); - return r; -} - -/** - * Returns the next leaf node. - * - * Using this function delivers leaf nodes as if the whole DOM tree - * were a linear chain of its leaf nodes. - * @param r dom node - * @param baseElem base element not to search beyond - * @return next leaf node or 0 if there are no more. - */ -static NodeImpl *nextLeafNode(NodeImpl *r, NodeImpl *baseElem) -{ - NodeImpl *n = r->firstChild(); - if (n) { - while (n) { r = n; n = n->firstChild(); } - return const_cast<NodeImpl *>(r); - }/*end if*/ - n = r->nextSibling(); - if (n) { - r = n; - while (n) { r = n; n = n->firstChild(); } - return const_cast<NodeImpl *>(r); - }/*end if*/ - - n = r->parentNode(); - if (n == baseElem) n = 0; - while (n) { - r = n; - n = r->nextSibling(); - if (n) { - r = n; - n = r->firstChild(); - while (n) { r = n; n = n->firstChild(); } - return const_cast<NodeImpl *>(r); - }/*end if*/ - n = r->parentNode(); - if (n == baseElem) n = 0; - }/*wend*/ - return 0; -} - -#if 0 // currently not used -/** (Not part of the official DOM) - * Returns the previous leaf node. - * - * Using this function delivers leaf nodes as if the whole DOM tree - * were a linear chain of its leaf nodes. - * @param r dom node - * @param baseElem base element not to search beyond - * @return previous leaf node or 0 if there are no more. - */ -static NodeImpl *prevLeafNode(NodeImpl *r, NodeImpl *baseElem) -{ - NodeImpl *n = r->firstChild(); - if (n) { - while (n) { r = n; n = n->firstChild(); } - return const_cast<NodeImpl *>(r); - }/*end if*/ - n = r->previousSibling(); - if (n) { - r = n; - while (n) { r = n; n = n->firstChild(); } - return const_cast<NodeImpl *>(r); - }/*end if*/ - - n = r->parentNode(); - if (n == baseElem) n = 0; - while (n) { - r = n; - n = r->previousSibling(); - if (n) { - r = n; - n = r->lastChild(); - while (n) { r = n; n = n->lastChild(); } - return const_cast<NodeImpl *>(r); - }/*end if*/ - n = r->parentNode(); - if (n == baseElem) n = 0; - }/*wend*/ - return 0; -} -#endif - -/** Maps a DOM Range position to the corresponding caret position. - * - * The offset boundary is not checked for validity. - * @param node DOM node - * @param offset zero-based offset within node - * @param r returns render object (may be 0 if DOM node has no render object) - * @param r_ofs returns the appropriate offset for the found render object r - * @param outside returns true when offset is applied to the outside of - * \c r, or false for the inside. - * @param outsideEnd return true when the caret position is at the outside end. - */ -void /*KDE_NO_EXPORT*/ mapDOMPosToRenderPos(NodeImpl *node, long offset, - RenderObject *&r, long &r_ofs, bool &outside, bool &outsideEnd) -{ - if (node->nodeType() == Node::TEXT_NODE) { - outside = false; - outsideEnd = false; - r = node->renderer(); - r_ofs = offset; - } else if (node->nodeType() == Node::ELEMENT_NODE || node->nodeType() == Node::DOCUMENT_NODE) { - - // Though offset points between two children, attach it to the visually - // most suitable one (and only there, because the mapping must stay bijective) - if (node->firstChild()) { - outside = true; - NodeImpl *child = offset <= 0 ? node->firstChild() - // childNode is expensive - : node->childNode((unsigned long)offset); - // index was child count or out of bounds - bool atEnd = !child; -#if DEBUG_CARETMODE > 5 - kdDebug(6200) << "mapDTR: child " << child << "@" << (child ? child->nodeName().string() : TQString::null) << " atEnd " << atEnd << endl; -#endif - if (atEnd) child = node->lastChild(); - - r = child->renderer(); - r_ofs = 0; - outsideEnd = atEnd; - - // Outside text nodes most likely stem from a continuation. Seek - // the enclosing continued render object and use this one instead. - if (r && child->nodeType() == Node::TEXT_NODE) { - r = r->parent(); - RenderObject *o = node->renderer(); - while (o->continuation() && o->continuation() != r) - o = o->continuation(); - if (!r || o->continuation() != r) { - r = child->renderer(); - } - }/*end if*/ - - // BRs cause troubles. Returns the previous render object instead, - // giving it the attributes outside, outside end. - if (r && r->isBR()) { - r = r->objectAbove(); - outsideEnd = true; - }/*end if*/ - - } else { - // Element has no children, treat offset to be inside the node. - outside = false; - outsideEnd = false; - r = node->renderer(); - r_ofs = 0; // only offset 0 possible - } - - } else { - r = 0; - kdWarning() << k_funcinfo << "Mapping from nodes of type " << node->nodeType() - << " not supported!" << endl; - } -} - -/** Maps a caret position to the corresponding DOM Range position. - * - * @param r render object - * @param r_ofs offset within render object - * @param outside true when offset is interpreted to be on the outside of - * \c r, or false if on the inside. - * @param outsideEnd true when the caret position is at the outside end. - * @param node returns DOM node - * @param offset returns zero-based offset within node - */ -void /*KDE_NO_EXPORT*/ mapRenderPosToDOMPos(RenderObject *r, long r_ofs, - bool outside, bool outsideEnd, NodeImpl *&node, long &offset) -{ - node = r->element(); - Q_ASSERT(node); -#if DEBUG_CARETMODE > 5 - kdDebug(6200) << "mapRTD: r " << r << "@" << (r ? r->renderName() : TQString::null) << (r && r->element() ? TQString(".node ") + TQString::number((unsigned)r->element(),16) + "@" + r->element()->nodeName().string() : TQString::null) << " outside " << outside << " outsideEnd " << outsideEnd << endl; -#endif - if (node->nodeType() == Node::ELEMENT_NODE || node->nodeType() == Node::TEXT_NODE) { - - if (outside) { - NodeImpl *parent = node->parent(); - - // If this is part of a continuation, use the actual node as the parent, - // and the first render child as the node. - if (r != node->renderer()) { - RenderObject *o = node->renderer(); - while (o->continuation() && o->continuation() != r) - o = o->continuation(); - if (o->continuation() == r) { - parent = node; - // ### What if the first render child does not map to a child of - // the continued node? - node = r->firstChild() ? r->firstChild()->element() : node; - } - }/*end if*/ - - if (!parent) goto inside; - - offset = (long)node->nodeIndex() + outsideEnd; - node = parent; -#if DEBUG_CARETMODE > 5 - kdDebug(6200) << node << "@" << (node ? node->nodeName().string() : TQString::null) << " offset " << offset << endl; -#endif - } else { // !outside -inside: - offset = r_ofs; - } - - } else { - offset = 0; - kdWarning() << k_funcinfo << "Mapping to nodes of type " << node->nodeType() - << " not supported!" << endl; - } -} - -/** Make sure the given node is a leaf node. */ -static inline void ensureLeafNode(NodeImpl *&node, NodeImpl *base) -{ - if (node && node->hasChildNodes()) node = nextLeafNode(node, base); -} - -/** Converts a caret position to its respective object traversal state. - * @param outside whether the caret is outside the object - * @param atEnd whether the caret position is at the end - * @param toBegin \c true when advancing towards the beginning - * @param trav returns the corresponding traversal state - */ -static inline void mapRenderPosToTraversalState(bool outside, bool atEnd, - bool toBegin, ObjectTraversalState &trav) -{ - if (!outside) atEnd = !toBegin; - if (!atEnd ^ toBegin) - trav = outside ? OutsideDescending : InsideDescending; - else - trav = outside ? OutsideAscending : InsideAscending; -} - -/** Converts a traversal state to its respective caret position - * @param trav object traversal state - * @param toBegin \c true when advancing towards the beginning - * @param outside whether the caret is outside the object - * @param atEnd whether the caret position is at the end - */ -static inline void mapTraversalStateToRenderPos(ObjectTraversalState trav, - bool toBegin, bool &outside, bool &atEnd) -{ - outside = false; - switch (trav) { - case OutsideDescending: outside = true; // fall through - case InsideDescending: atEnd = toBegin; break; - case OutsideAscending: outside = true; // fall through - case InsideAscending: atEnd = !toBegin; break; - } -} - -/** Finds the next node that has a renderer. - * - * Note that if the initial @p node has a renderer, this will be returned, - * regardless of the caret advance policy. - * Otherwise, for the next nodes, only leaf nodes are considered. - * @param node node to start with, will be updated accordingly - * @param offset offset of caret within \c node - * @param base base render object which this method must not advance beyond - * (0 means document) - * @param r_ofs return the caret offset within the returned renderer - * @param outside returns whether offset is to be interpreted to the outside - * (true) or the inside (false) of the render object. - * @param outsideEnd returns whether the end of the outside position is meant - * @return renderer or 0 if no following node has a renderer. - */ -static RenderObject* findRenderer(NodeImpl *&node, long offset, - RenderObject *base, long &r_ofs, - bool &outside, bool &outsideEnd) -{ - if (!node) return 0; - RenderObject *r; - mapDOMPosToRenderPos(node, offset, r, r_ofs, outside, outsideEnd); -#if DEBUG_CARETMODE > 2 - kdDebug(6200) << "findRenderer: node " << node << " " << (node ? node->nodeName().string() : TQString::null) << " offset " << offset << " r " << r << "[" << (r ? r->renderName() : TQString::null) << "] r_ofs " << r_ofs << " outside " << outside << " outsideEnd " << outsideEnd << endl; -#endif - if (r) return r; - NodeImpl *baseElem = base ? base->element() : 0; - while (!r) { - node = nextLeafNode(node, baseElem); - if (!node) break; - r = node->renderer(); - if (r) r_ofs = offset; - } -#if DEBUG_CARETMODE > 3 - kdDebug(6200) << "1r " << r << endl; -#endif - ObjectTraversalState trav; - int state; // not used - mapRenderPosToTraversalState(outside, outsideEnd, false, trav); - if (r && isUnsuitable(r, trav)) { - r = advanceSuitableObject(r, trav, false, base, state); - mapTraversalStateToRenderPos(trav, false, outside, outsideEnd); - if (r) r_ofs = r->minOffset(); - } -#if DEBUG_CARETMODE > 3 - kdDebug(6200) << "2r " << r << endl; -#endif - return r; -} - -/** returns a suitable base element - * @param caretNode current node containing caret. - */ -static ElementImpl *determineBaseElement(NodeImpl *caretNode) -{ - // ### for now, only body is delivered for html documents, - // and 0 for xml documents. - - DocumentImpl *doc = caretNode->getDocument(); - if (!doc) return 0; // should not happen, but who knows. - - if (doc->isHTMLDocument()) - return static_cast<HTMLDocumentImpl *>(doc)->body(); - - return 0; -} - -// == class CaretBox implementation - -#if DEBUG_CARETMODE > 0 -void CaretBox::dump(TQTextStream &ts, const TQString &ind) const -{ - ts << ind << "b@" << _box; - - if (_box) { - ts << "<" << _box->object() << ":" << _box->object()->renderName() << ">"; - }/*end if*/ - - ts << " " << _x << "+" << _y << "+" << _w << "*" << _h; - - ts << " cb@" << cb; - if (cb) ts << ":" << cb->renderName(); - - ts << " " << (_outside ? (outside_end ? "oe" : "o-") : "i-"); -// ts << endl; -} -#endif - -// == class CaretBoxLine implementation - -#if DEBUG_CARETMODE > 0 -# define DEBUG_ACIB 1 -#else -# define DEBUG_ACIB DEBUG_CARETMODE -#endif -void CaretBoxLine::addConvertedInlineBox(InlineBox *box, SeekBoxParams &sbp) /*KDE_NO_EXPORT*/ -{ - // Generate only one outside caret box between two elements. If - // coalesceOutsideBoxes is true, generating left outside boxes is inhibited. - bool coalesceOutsideBoxes = false; - CaretBoxIterator lastCoalescedBox; - for (; box; box = box->nextOnLine()) { -#if DEBUG_ACIB -kdDebug(6200) << "box " << box << endl; -kdDebug(6200) << "box->object " << box->object() << endl; -kdDebug(6200) << "x " << box->m_x << " y " << box->m_y << " w " << box->m_width << " h " << box->m_height << " baseline " << box->m_baseline << " ifb " << box->isInlineFlowBox() << " itb " << box->isInlineTextBox() << " rlb " << box->isRootInlineBox() << endl; -#endif - // ### Why the hell can object() ever be 0?! - if (!box->object()) continue; - - RenderStyle *s = box->object()->style(box->m_firstLine); - // parent style for outside caret boxes - RenderStyle *ps = box->parent() && box->parent()->object() - ? box->parent()->object()->style(box->parent()->m_firstLine) - : s; - - if (box->isInlineFlowBox()) { -#if DEBUG_ACIB -kdDebug(6200) << "isinlineflowbox " << box << endl; -#endif - InlineFlowBox *flowBox = static_cast<InlineFlowBox *>(box); - bool rtl = ps->direction() == RTL; - const TQFontMetrics &pfm = ps->fontMetrics(); - - if (flowBox->includeLeftEdge()) { - // If this box is to be coalesced with the outside end box of its - // predecessor, then check if it is the searched box. If it is, we - // substitute the outside end box. - if (coalesceOutsideBoxes) { - if (sbp.equalsBox(flowBox, true, false)) { - sbp.it = lastCoalescedBox; - Q_ASSERT(!sbp.found); - sbp.found = true; - } - } else { - addCreatedFlowBoxEdge(flowBox, pfm, true, rtl); - sbp.check(preEnd()); - } - }/*end if*/ - - if (flowBox->firstChild()) { -#if DEBUG_ACIB -kdDebug(6200) << "this " << this << " flowBox " << flowBox << " firstChild " << flowBox->firstChild() << endl; -kdDebug(6200) << "== recursive invocation" << endl; -#endif - addConvertedInlineBox(flowBox->firstChild(), sbp); -#if DEBUG_ACIB -kdDebug(6200) << "== recursive invocation end" << endl; -#endif -} - else { - addCreatedFlowBoxInside(flowBox, s->fontMetrics()); - sbp.check(preEnd()); - } - - if (flowBox->includeRightEdge()) { - addCreatedFlowBoxEdge(flowBox, pfm, false, rtl); - lastCoalescedBox = preEnd(); - sbp.check(lastCoalescedBox); - coalesceOutsideBoxes = true; - } - - } else if (box->isInlineTextBox()) { -#if DEBUG_ACIB -kdDebug(6200) << "isinlinetextbox " << box << (box->object() ? TQString(" contains \"%1\"").arg(TQConstString(static_cast<RenderText *>(box->object())->str->s+box->minOffset(), kMin(box->maxOffset() - box->minOffset(), 15L)).string()) : TQString::null) << endl; -#endif - caret_boxes.append(new CaretBox(box, false, false)); - sbp.check(preEnd()); - // coalescing has been interrupted - coalesceOutsideBoxes = false; - - } else { -#if DEBUG_ACIB -kdDebug(6200) << "some replaced or what " << box << endl; -#endif - // must be an inline-block, inline-table, or any RenderReplaced - bool rtl = ps->direction() == RTL; - const TQFontMetrics &pfm = ps->fontMetrics(); - - if (coalesceOutsideBoxes) { - if (sbp.equalsBox(box, true, false)) { - sbp.it = lastCoalescedBox; - Q_ASSERT(!sbp.found); - sbp.found = true; - } - } else { - addCreatedInlineBoxEdge(box, pfm, true, rtl); - sbp.check(preEnd()); - } - - caret_boxes.append(new CaretBox(box, false, false)); - sbp.check(preEnd()); - - addCreatedInlineBoxEdge(box, pfm, false, rtl); - lastCoalescedBox = preEnd(); - sbp.check(lastCoalescedBox); - coalesceOutsideBoxes = true; - }/*end if*/ - }/*next box*/ -} -#undef DEBUG_ACIB - -void CaretBoxLine::addCreatedFlowBoxInside(InlineFlowBox *flowBox, const TQFontMetrics &fm) /*KDE_NO_EXPORT*/ -{ - - CaretBox *caretBox = new CaretBox(flowBox, false, false); - caret_boxes.append(caretBox); - - // afaik an inner flow box can only have the width 0, therefore we don't - // have to care for rtl or alignment - // ### can empty inline elements have a width? css 2 spec isn't verbose about it - - caretBox->_y += flowBox->baseline() - fm.ascent(); - caretBox->_h = fm.height(); -} - -void CaretBoxLine::addCreatedFlowBoxEdge(InlineFlowBox *flowBox, const TQFontMetrics &fm, bool left, bool rtl) /*KDE_NO_EXPORT*/ -{ - CaretBox *caretBox = new CaretBox(flowBox, true, !left); - caret_boxes.append(caretBox); - - if (left ^ rtl) caretBox->_x -= flowBox->paddingLeft() + flowBox->borderLeft() + 1; - else caretBox->_x += caretBox->_w + flowBox->paddingRight() + flowBox->borderRight(); - - caretBox->_y += flowBox->baseline() - fm.ascent(); - caretBox->_h = fm.height(); - caretBox->_w = 1; -} - -void CaretBoxLine::addCreatedInlineBoxEdge(InlineBox *box, const TQFontMetrics &fm, bool left, bool rtl) /*KDE_NO_EXPORT*/ -{ - CaretBox *caretBox = new CaretBox(box, true, !left); - caret_boxes.append(caretBox); - - if (left ^ rtl) caretBox->_x--; - else caretBox->_x += caretBox->_w; - - caretBox->_y += box->baseline() - fm.ascent(); - caretBox->_h = fm.height(); - caretBox->_w = 1; -} - -CaretBoxLine *CaretBoxLine::constructCaretBoxLine(CaretBoxLineDeleter *deleter, - InlineFlowBox *basicFlowBox, InlineBox *seekBox, bool seekOutside, - bool seekOutsideEnd, CaretBoxIterator &iter, RenderObject *seekObject) -// KDE_NO_EXPORT -{ - // Iterate all inline boxes within this inline flow box. - // Caret boxes will be created for each - // - outside begin of an inline flow box (except for the basic inline flow box) - // - outside end of an inline flow box (except for the basic inline flow box) - // - inside of an empty inline flow box - // - outside begin of an inline box resembling a replaced element - // - outside end of an inline box resembling a replaced element - // - inline text box - // - inline replaced box - - CaretBoxLine *result = new CaretBoxLine(basicFlowBox); - deleter->append(result); - - SeekBoxParams sbp(seekBox, seekOutside, seekOutsideEnd, seekObject, iter); - - // iterate recursively, I'm too lazy to do it iteratively - result->addConvertedInlineBox(basicFlowBox, sbp); - - if (!sbp.found) sbp.it = result->end(); - - return result; -} - -CaretBoxLine *CaretBoxLine::constructCaretBoxLine(CaretBoxLineDeleter *deleter, - RenderBox *cb, bool outside, bool outsideEnd, CaretBoxIterator &iter) /*KDE_NO_EXPORT*/ -{ - int _x = cb->xPos(); - int _y = cb->yPos(); - int height; - int width = 1; // no override is indicated in boxes - - if (outside) { - - RenderStyle *s = cb->element() && cb->element()->parent() - && cb->element()->parent()->renderer() - ? cb->element()->parent()->renderer()->style() - : cb->style(); - bool rtl = s->direction() == RTL; - - const TQFontMetrics &fm = s->fontMetrics(); - height = fm.height(); - - if (!outsideEnd) { - _x--; - } else { - _x += cb->width(); - } - - int hl = fm.leading() / 2; - int baseline = cb->baselinePosition(false); - if (!cb->isReplaced() || cb->style()->display() == BLOCK) { - if (!outsideEnd ^ rtl) - _y -= fm.leading() / 2; - else - _y += kMax(cb->height() - fm.ascent() - hl, 0); - } else { - _y += baseline - fm.ascent() - hl; - } - - } else { // !outside - - RenderStyle *s = cb->style(); - const TQFontMetrics &fm = s->fontMetrics(); - height = fm.height(); - - _x += cb->borderLeft() + cb->paddingLeft(); - _y += cb->borderTop() + cb->paddingTop(); - - // ### regard direction - switch (s->textAlign()) { - case LEFT: - case KHTML_LEFT: - case TAAUTO: // ### find out what this does - case JUSTIFY: - break; - case CENTER: - case KHTML_CENTER: - _x += cb->contentWidth() / 2; - break; - case KHTML_RIGHT: - case RIGHT: - _x += cb->contentWidth(); - break; - }/*end switch*/ - }/*end if*/ - - CaretBoxLine *result = new CaretBoxLine; - deleter->append(result); - result->caret_boxes.append(new CaretBox(_x, _y, width, height, cb, - outside, outsideEnd)); - iter = result->begin(); - return result; -} - -#if DEBUG_CARETMODE > 0 -void CaretBoxLine::dump(TQTextStream &ts, const TQString &ind) const -{ - ts << ind << "cbl: baseFlowBox@" << basefb << endl; - TQString ind2 = ind + " "; - for (size_t i = 0; i < caret_boxes.size(); i++) { - if (i > 0) ts << endl; - caret_boxes[i]->dump(ts, ind2); - } -} -#endif - -// == caret mode related helper functions - -/** seeks the root line box that is the parent of the given inline box. - * @param b given inline box - * @param base base render object which not to step over. If \c base's - * inline flow box is hit before the root line box, the flow box - * is returned instead. - * @return the root line box or the base flow box. - */ -inline InlineFlowBox *seekBaseFlowBox(InlineBox *b, RenderObject *base = 0) -{ - // Seek root line box or base inline flow box, if \c base is interfering. - while (b->parent() && b->object() != base) { - b = b->parent(); - }/*wend*/ - Q_ASSERT(b->isInlineFlowBox()); - return static_cast<InlineFlowBox *>(b); -} - -/** determines whether the given element is a block level replaced element. - */ -inline bool isBlockRenderReplaced(RenderObject *r) -{ - return r->isRenderReplaced() && r->style()->display() == BLOCK; -} - -/** determines the caret line box that contains the given position. - * - * If the node does not map to a render object, the function will snap to - * the next suitable render object following it. - * - * @param node node to begin with - * @param offset zero-based offset within node. - * @param cblDeleter deleter for caret box lines - * @param base base render object which the caret must not be placed beyond. - * @param r_ofs adjusted offset within render object - * @param caretBoxIt returns an iterator to the caret box that contains the - * given position. - * @return the determined caret box lineor 0 if either the node is 0 or - * there is no inline flow box containing this node. The containing block - * will still be set. If it is 0 too, @p node was invalid. - */ -static CaretBoxLine* findCaretBoxLine(DOM::NodeImpl *node, long offset, - CaretBoxLineDeleter *cblDeleter, RenderObject *base, - long &r_ofs, CaretBoxIterator &caretBoxIt) -{ - bool outside, outsideEnd; - RenderObject *r = findRenderer(node, offset, base, r_ofs, outside, outsideEnd); - if (!r) { return 0; } -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << "=================== findCaretBoxLine" << endl; - kdDebug(6200) << "node " << node << " offset: " << offset << " r " << r->renderName() << "[" << r << "].node " << r->element()->nodeName().string() << "[" << r->element() << "]" << " r_ofs " << r_ofs << " outside " << outside << " outsideEnd " << outsideEnd << endl; -#endif - - // There are two strategies to find the correct line box. (The third is failsafe) - // (A) First, if node's renderer is a RenderText, we only traverse its text - // runs and return the root line box (saves much time for long blocks). - // This should be the case 99% of the time. - // (B) Second, we derive the inline flow box directly when the renderer is - // a RenderBlock, RenderInline, or blocked RenderReplaced. - // (C) Otherwise, we iterate linearly through all line boxes in order to find - // the renderer. - - // (A) - if (r->isText()) do { - RenderText *t = static_cast<RenderText *>(r); - int dummy; - InlineBox *b = t->findInlineTextBox(offset, dummy, true); - // Actually b should never be 0, but some render texts don't have text - // boxes, so we insert the last run as an error correction. - // If there is no last run, we resort to (B) - if (!b) { - if (t->m_lines.count() > 0) - b = t->m_lines[t->m_lines.count() - 1]; - else - break; - }/*end if*/ - Q_ASSERT(b); - outside = false; // text boxes cannot have outside positions - InlineFlowBox *baseFlowBox = seekBaseFlowBox(b, base); -#if DEBUG_CARETMODE > 2 - kdDebug(6200) << "text-box b: " << b << " baseFlowBox: " << baseFlowBox << (b && b->object() ? TQString(" contains \"%1\"").arg(TQConstString(static_cast<RenderText *>(b->object())->str->s+b->minOffset(), kMin(b->maxOffset() - b->minOffset(), 15L)).string()) : TQString::null) << endl; -#endif -#if 0 - if (t->containingBlock()->isListItem()) dumpLineBoxes(static_cast<RenderFlow *>(t->containingBlock())); -#endif -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << "=================== end findCaretBoxLine (renderText)" << endl; -#endif - return CaretBoxLine::constructCaretBoxLine(cblDeleter, baseFlowBox, - b, outside, outsideEnd, caretBoxIt); - } while(false);/*end if*/ - - // (B) - bool isrepl = isBlockRenderReplaced(r); - if (r->isRenderBlock() || r->isRenderInline() || isrepl) { - RenderFlow *flow = static_cast<RenderFlow *>(r); - InlineFlowBox *firstLineBox = isrepl ? 0 : flow->firstLineBox(); - - // On render blocks, if we are outside, or have a totally empty render - // block, we simply construct a special caret box line. - // The latter case happens only when the render block is a leaf object itself. - if (isrepl || r->isRenderBlock() && (outside || !firstLineBox) - || r->isRenderInline() && !firstLineBox) { - #if DEBUG_CARETMODE > 0 - kdDebug(6200) << "=================== end findCaretBoxLine (box " << (outside ? (outsideEnd ? "outside end" : "outside begin") : "inside") << ")" << endl; - #endif - Q_ASSERT(r->isBox()); - return CaretBoxLine::constructCaretBoxLine(cblDeleter, - static_cast<RenderBox *>(r), outside, outsideEnd, caretBoxIt); - }/*end if*/ - - kdDebug(6200) << "firstlinebox " << firstLineBox << endl; - InlineFlowBox *baseFlowBox = seekBaseFlowBox(firstLineBox, base); - return CaretBoxLine::constructCaretBoxLine(cblDeleter, baseFlowBox, - firstLineBox, outside, outsideEnd, caretBoxIt); - }/*end if*/ - - RenderBlock *cb = r->containingBlock(); - //if ( !cb ) return 0L; - Q_ASSERT(cb); - - // ### which element doesn't have a block as its containing block? - // Is it still possible after the RenderBlock/RenderInline merge? - if (!cb->isRenderBlock()) { - kdWarning() << "containing block is no render block!!! crash imminent" << endl; - }/*end if*/ - - InlineFlowBox *flowBox = cb->firstLineBox(); - // (C) - // This case strikes when the element is replaced, but neither a - // RenderBlock nor a RenderInline - if (!flowBox) { // ### utter emergency (why is this possible at all?) -// flowBox = generateDummyFlowBox(arena, cb, r); -// if (ibox) *ibox = flowBox->firstChild(); -// outside = outside_end = true; - -// kdWarning() << "containing block contains no inline flow boxes!!! crash imminent" << endl; -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << "=================== end findCaretBoxLine (2)" << endl; -#endif - return CaretBoxLine::constructCaretBoxLine(cblDeleter, cb, - outside, outsideEnd, caretBoxIt); - }/*end if*/ - - // We iterate the inline flow boxes of the containing block until - // we find the given node. This has one major flaw: it is linear, and therefore - // painfully slow for really large blocks. - for (; flowBox; flowBox = static_cast<InlineFlowBox *>(flowBox->nextLineBox())) { -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << "[scan line]" << endl; -#endif - - // construct a caret line box and stop when the element is contained within - InlineFlowBox *baseFlowBox = seekBaseFlowBox(flowBox, base); - CaretBoxLine *cbl = CaretBoxLine::constructCaretBoxLine(cblDeleter, - baseFlowBox, 0, outside, outsideEnd, caretBoxIt, r); -#if DEBUG_CARETMODE > 5 - kdDebug(6200) << cbl->information() << endl; -#endif - if (caretBoxIt != cbl->end()) { -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << "=================== end findCaretBoxLine (3)" << endl; -#endif - return cbl; - } - }/*next flowBox*/ - - // no inline flow box found, approximate to nearest following node. - // Danger: this is O(n^2). It's only called to recover from - // errors, that means, theoretically, never. (Practically, far too often :-( ) - Q_ASSERT(!flowBox); - CaretBoxLine *cbl = findCaretBoxLine(nextLeafNode(node, base ? base->element() : 0), 0, cblDeleter, base, r_ofs, caretBoxIt); -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << "=================== end findCaretBoxLine" << endl; -#endif - return cbl; -} - -/** finds the innermost table object @p r is contained within, but no - * farther than @p cb. - * @param r leaf element to begin with - * @param cb bottom element where to stop search at least. - * @return the table object or 0 if none found. - */ -static inline RenderTable *findTableUpTo(RenderObject *r, RenderFlow *cb) -{ - while (r && r != cb && !r->isTable()) r = r->parent(); - return r && r->isTable() ? static_cast<RenderTable *>(r) : 0; -} - -/** checks whether @p r is a descendant of @p cb, or r == cb - */ -static inline bool isDescendant(RenderObject *r, RenderObject *cb) -{ - while (r && r != cb) r = r->parent(); - return r; -} - -/** checks whether the given block contains at least one editable element. - * - * Warning: This function has linear complexity, and therefore is expensive. - * Use it sparingly, and cache the result. - * @param part part - * @param cb block to be searched - * @param table returns the nested table if there is one directly at the beginning - * or at the end. - * @param fromEnd begin search from end (default: begin from beginning) - */ -static bool containsEditableElement(KHTMLPart *part, RenderBlock *cb, - RenderTable *&table, bool fromEnd = false) -{ - RenderObject *r = cb; - if (fromEnd) - while (r->lastChild()) r = r->lastChild(); - else - while (r->firstChild()) r = r->firstChild(); - - RenderTable *tempTable = 0; - table = 0; - bool withinCb; -// int state; // not used - ObjectTraversalState trav = InsideDescending; - do { - bool modWithinCb = withinCb = isDescendant(r, cb); - - // treat cb extra, it would not be considered otherwise - if (!modWithinCb) { - modWithinCb = true; - r = cb; - } else - tempTable = findTableUpTo(r, cb); - -#if DEBUG_CARETMODE > 1 - kdDebug(6201) << "cee: r " << (r ? r->renderName() : TQString::null) << "@" << r << " cb " << cb << " withinCb " << withinCb << " modWithinCb " << modWithinCb << " tempTable " << tempTable << endl; -#endif - if (r && modWithinCb && r->element() && !isUnsuitable(r, trav) - && (part->isCaretMode() || part->isEditable() - || r->style()->userInput() == UI_ENABLED)) { - table = tempTable; -#if DEBUG_CARETMODE > 1 - kdDebug(6201) << "cee: editable" << endl; -#endif - return true; - }/*end if*/ - -// RenderObject *oldr = r; -// while (r && r == oldr) -// r = advanceSuitableObject(r, trav, fromEnd, cb->parent(), state); - r = fromEnd ? r->objectAbove() : r->objectBelow(); - } while (r && withinCb); - return false; -} - -/** checks whether the given block contains at least one editable child - * element, beginning with but excluding @p start. - * - * Warning: This function has linear complexity, and therefore is expensive. - * Use it sparingly, and cache the result. - * @param part part - * @param cb block to be searched - * @param table returns the nested table if there is one directly before/after - * the start object. - * @param fromEnd begin search from end (default: begin from beginning) - * @param start object after which to begin search. - */ -static bool containsEditableChildElement(KHTMLPart *part, RenderBlock *cb, - RenderTable *&table, bool fromEnd, RenderObject *start) -{ - int state = 0; - ObjectTraversalState trav = OutsideAscending; -// kdDebug(6201) << "start: " << start << endl; - RenderObject *r = start; - do { - r = traverseRenderObjects(r, trav, fromEnd, cb->parent(), state); - } while(r && !(state & AdvancedToSibling)); -// kdDebug(6201) << "r: " << r << endl; - //advanceObject(start, trav, fromEnd, cb->parent(), state); -// RenderObject *oldr = r; -// while (r && r == oldr) - if (!r) return false; - - if (fromEnd) - while (r->firstChild()) r = r->firstChild(); - else - while (r->lastChild()) r = r->lastChild(); -// kdDebug(6201) << "child r: " << r << endl; - if (!r) return false; - - RenderTable *tempTable = 0; - table = 0; - bool withinCb = false; - do { - - bool modWithinCb = withinCb = isDescendant(r, cb); - - // treat cb extra, it would not be considered otherwise - if (!modWithinCb) { - modWithinCb = true; - r = cb; - } else - tempTable = findTableUpTo(r, cb); - -#if DEBUG_CARETMODE > 1 - kdDebug(6201) << "cece: r " << (r ? r->renderName() : TQString::null) << "@" << r << " cb " << cb << " withinCb " << withinCb << " modWithinCb " << modWithinCb << " tempTable " << tempTable << endl; -#endif - if (r && withinCb && r->element() && !isUnsuitable(r, trav) - && (part->isCaretMode() || part->isEditable() - || r->style()->userInput() == UI_ENABLED)) { - table = tempTable; -#if DEBUG_CARETMODE > 1 - kdDebug(6201) << "cece: editable" << endl; -#endif - return true; - }/*end if*/ - - r = fromEnd ? r->objectAbove() : r->objectBelow(); - } while (withinCb); - return false; -} - -// == class LinearDocument implementation - -LinearDocument::LinearDocument(KHTMLPart *part, NodeImpl *node, long offset, - CaretAdvancePolicy advancePolicy, ElementImpl *baseElem) - : node(node), offset(offset), m_part(part), - advPol(advancePolicy), base(0) -{ - if (node == 0) return; - - if (baseElem) { - RenderObject *b = baseElem->renderer(); - if (b && (b->isRenderBlock() || b->isRenderInline())) - base = b; - } - - initPreBeginIterator(); - initEndIterator(); -} - -LinearDocument::~LinearDocument() -{ -} - -int LinearDocument::count() const -{ - // FIXME: not implemented - return 1; -} - -LinearDocument::Iterator LinearDocument::current() -{ - return LineIterator(this, node, offset); -} - -LinearDocument::Iterator LinearDocument::begin() -{ - NodeImpl *n = base ? base->element() : 0; - if (!base) n = node ? node->getDocument() : 0; - if (!n) return end(); - - n = n->firstChild(); - if (advPol == LeafsOnly) - while (n->firstChild()) n = n->firstChild(); - - if (!n) return end(); // must be empty document or empty base element - return LineIterator(this, n, n->minOffset()); -} - -LinearDocument::Iterator LinearDocument::preEnd() -{ - NodeImpl *n = base ? base->element() : 0; - if (!base) n = node ? node->getDocument() : 0; - if (!n) return preBegin(); - - n = n->lastChild(); - if (advPol == LeafsOnly) - while (n->lastChild()) n = n->lastChild(); - - if (!n) return preBegin(); // must be empty document or empty base element - return LineIterator(this, n, n->maxOffset()); -} - -void LinearDocument::initPreBeginIterator() -{ - _preBegin = LineIterator(this, 0, 0); -} - -void LinearDocument::initEndIterator() -{ - _end = LineIterator(this, 0, 1); -} - -// == class LineIterator implementation - -CaretBoxIterator LineIterator::currentBox /*KDE_NO_EXPORT*/; -long LineIterator::currentOffset /*KDE_NO_EXPORT*/; - -LineIterator::LineIterator(LinearDocument *l, DOM::NodeImpl *node, long offset) - : lines(l) -{ -// kdDebug(6200) << "LineIterator: node " << node << " offset " << offset << endl; - if (!node) { cbl = 0; return; } - cbl = findCaretBoxLine(node, offset, &lines->cblDeleter, - l->baseObject(), currentOffset, currentBox); - // can happen on partially loaded documents -#if DEBUG_CARETMODE > 0 - if (!cbl) kdDebug(6200) << "no render object found!" << endl; -#endif - if (!cbl) return; -#if DEBUG_CARETMODE > 1 - kdDebug(6200) << "LineIterator: offset " << offset << " outside " << cbl->isOutside() << endl; -#endif -#if DEBUG_CARETMODE > 3 - kdDebug(6200) << cbl->information() << endl; -#endif - if (currentBox == cbl->end()) { -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << "LineIterator: findCaretBoxLine failed" << endl; -#endif - cbl = 0; - }/*end if*/ -} - -void LineIterator::nextBlock() -{ - RenderObject *base = lines->baseObject(); - - bool cb_outside = cbl->isOutside(); - bool cb_outside_end = cbl->isOutsideEnd(); - - { - RenderObject *r = cbl->enclosingObject(); - - ObjectTraversalState trav; - int state; // not used - mapRenderPosToTraversalState(cb_outside, cb_outside_end, false, trav); -#if DEBUG_CARETMODE > 1 - kdDebug(6200) << "nextBlock: before adv r" << r << " " << (r ? r->renderName() : TQString::null) << (r && r->isText() ? " contains \"" + TQString(((RenderText *)r)->str->s, QMIN(((RenderText *)r)->str->l,15)) + "\"" : TQString::null) << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end << endl; -#endif - r = advanceSuitableObject(r, trav, false, base, state); - if (!r) { - cbl = 0; - return; - }/*end if*/ - - mapTraversalStateToRenderPos(trav, false, cb_outside, cb_outside_end); -#if DEBUG_CARETMODE > 1 - kdDebug(6200) << "nextBlock: after r" << r << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end << endl; -#endif -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << "++: r " << r << "[" << (r?r->renderName():TQString::null) << "]" << endl; -#endif - - RenderBlock *cb; - - // If we hit a block or replaced object, use this as its enclosing object - bool isrepl = isBlockRenderReplaced(r); - if (r->isRenderBlock() || isrepl) { - RenderBox *cb = static_cast<RenderBox *>(r); - - cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb, - cb_outside, cb_outside_end, currentBox); - -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << "r->isFlow is cb. continuation @" << cb->continuation() << endl; -#endif - return; - } else { - cb = r->containingBlock(); - Q_ASSERT(cb->isRenderBlock()); - }/*end if*/ - InlineFlowBox *flowBox = cb->firstLineBox(); -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << "++: flowBox " << flowBox << " cb " << cb << "[" << (cb?cb->renderName()+TQString(".node ")+TQString::number((unsigned)cb->element(),16)+(cb->element()?"@"+cb->element()->nodeName().string():TQString::null):TQString::null) << "]" << endl; -#endif - Q_ASSERT(flowBox); - if (!flowBox) { // ### utter emergency (why is this possible at all?) - cb_outside = cb_outside_end = true; - cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb, - cb_outside, cb_outside_end, currentBox); - return; - } - - bool seekOutside = false, seekOutsideEnd = false; // silence gcc uninit warning - CaretBoxIterator it; - cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, - flowBox, flowBox->firstChild(), seekOutside, seekOutsideEnd, it); - } -} - -void LineIterator::prevBlock() -{ - RenderObject *base = lines->baseObject(); - - bool cb_outside = cbl->isOutside(); - bool cb_outside_end = cbl->isOutsideEnd(); - - { - RenderObject *r = cbl->enclosingObject(); - if (r->isAnonymous() && !cb_outside) - cb_outside = true, cb_outside_end = false; - - ObjectTraversalState trav; - int state; // not used - mapRenderPosToTraversalState(cb_outside, cb_outside_end, true, trav); -#if DEBUG_CARETMODE > 1 - kdDebug(6200) << "prevBlock: before adv r" << r << " " << (r ? r->renderName() : TQString::null) << (r && r->isText() ? " contains \"" + TQString(((RenderText *)r)->str->s, QMIN(((RenderText *)r)->str->l,15)) + "\"" : TQString::null) << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end << endl; -#endif - r = advanceSuitableObject(r, trav, true, base, state); - if (!r) { - cbl = 0; - return; - }/*end if*/ - - mapTraversalStateToRenderPos(trav, true, cb_outside, cb_outside_end); -#if DEBUG_CARETMODE > 1 - kdDebug(6200) << "prevBlock: after r" << r << " trav " << trav << " cb_outside " << cb_outside << " cb_outside_end " << cb_outside_end << endl; -#endif -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << "--: r " << r << "[" << (r?r->renderName():TQString::null) << "]" << endl; -#endif - - RenderBlock *cb; - - // If we hit a block, use this as its enclosing object - bool isrepl = isBlockRenderReplaced(r); -// kdDebug(6200) << "isrepl " << isrepl << " isblock " << r->isRenderBlock() << endl; - if (r->isRenderBlock() || isrepl) { - RenderBox *cb = static_cast<RenderBox *>(r); - - cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb, - cb_outside, cb_outside_end, currentBox); - -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << "r->isFlow is cb. continuation @" << cb->continuation() << endl; -#endif - return; - } else { - cb = r->containingBlock(); - Q_ASSERT(cb->isRenderBlock()); - }/*end if*/ - InlineFlowBox *flowBox = cb->lastLineBox(); -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << "--: flowBox " << flowBox << " cb " << cb << "[" << (cb?cb->renderName()+TQString(".node ")+TQString::number((unsigned)cb->element(),16)+(cb->element()?"@"+cb->element()->nodeName().string():TQString::null):TQString::null) << "]" << endl; -#endif - Q_ASSERT(flowBox); - if (!flowBox) { // ### utter emergency (why is this possible at all?) - cb_outside = true; cb_outside_end = false; - cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, cb, - cb_outside, cb_outside_end, currentBox); - return; - } - - bool seekOutside = false, seekOutsideEnd = false; // silence gcc uninit warning - CaretBoxIterator it; - cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, - flowBox, flowBox->firstChild(), seekOutside, seekOutsideEnd, it); - } -} - -void LineIterator::advance(bool toBegin) -{ - InlineFlowBox *flowBox = cbl->baseFlowBox(); - if (flowBox) { - flowBox = static_cast<InlineFlowBox *>(toBegin ? flowBox->prevLineBox() : flowBox->nextLineBox()); - if (flowBox) { - bool seekOutside = false, seekOutsideEnd = false; // silence gcc uninit warning - CaretBoxIterator it; - cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, - flowBox, flowBox->firstChild(), seekOutside, seekOutsideEnd, it); - }/*end if*/ - }/*end if*/ - - // if there are no more lines in this block, move towards block to come - if (!flowBox) { if (toBegin) prevBlock(); else nextBlock(); } - -#if DEBUG_CARETMODE > 3 - if (cbl) kdDebug(6200) << cbl->information() << endl; -#endif -} - -// == class EditableCaretBoxIterator implementation - -void EditableCaretBoxIterator::advance(bool toBegin) -{ -#if DEBUG_CARETMODE > 3 - kdDebug(6200) << "---------------" << k_funcinfo << "toBegin " << toBegin << endl; -#endif - const CaretBoxIterator preBegin = cbl->preBegin(); - const CaretBoxIterator end = cbl->end(); - - CaretBoxIterator lastbox = *this, curbox; - bool islastuseable = true; // silence gcc - bool iscuruseable; - // Assume adjacency of caret boxes. Will be falsified later if applicable. - adjacent = true; - -#if DEBUG_CARETMODE > 4 -// kdDebug(6200) << "ebit::advance: before: " << (**this)->object() << "@" << (**this)->object()->renderName() << ".node " << (**this)->object()->element() << "[" << ((**this)->object()->element() ? (**this)->object()->element()->nodeName().string() : TQString::null) << "] inline " << (**this)->isInline() << " outside " << (**this)->isOutside() << " outsideEnd " << (**this)->isOutsideEnd() << endl; -#endif - - if (toBegin) CaretBoxIterator::operator --(); else CaretBoxIterator::operator ++(); - bool curAtEnd = *this == preBegin || *this == end; - curbox = *this; - bool atEnd = true; - if (!curAtEnd) { - iscuruseable = isEditable(curbox, toBegin); - if (toBegin) CaretBoxIterator::operator --(); else CaretBoxIterator::operator ++(); - atEnd = *this == preBegin || *this == end; - } - while (!curAtEnd) { - bool haslast = lastbox != end && lastbox != preBegin; - bool hascoming = !atEnd; - bool iscominguseable = true; // silence gcc - - if (!atEnd) iscominguseable = isEditable(*this, toBegin); - if (iscuruseable) { -#if DEBUG_CARETMODE > 3 - kdDebug(6200) << "ebit::advance: " << (*curbox)->object() << "@" << (*curbox)->object()->renderName() << ".node " << (*curbox)->object()->element() << "[" << ((*curbox)->object()->element() ? (*curbox)->object()->element()->nodeName().string() : TQString::null) << "] inline " << (*curbox)->isInline() << " outside " << (*curbox)->isOutside() << " outsideEnd " << (*curbox)->isOutsideEnd() << endl; -#endif - - CaretBox *box = *curbox; - if (box->isOutside()) { - // if this caret box represents no inline box, it is an outside box - // which has to be considered unconditionally - if (!box->isInline()) break; - - if (advpol == VisibleFlows) break; - - // IndicatedFlows and LeafsOnly are treated equally in caret box lines - - InlineBox *ibox = box->inlineBox(); - // get previous inline box - InlineBox *prev = box->isOutsideEnd() ? ibox : ibox->prevOnLine(); - // get next inline box - InlineBox *next = box->isOutsideEnd() ? ibox->nextOnLine() : ibox; - - const bool isprevindicated = !prev || isIndicatedInlineBox(prev); - const bool isnextindicated = !next || isIndicatedInlineBox(next); - const bool last = haslast && !islastuseable; - const bool coming = hascoming && !iscominguseable; - const bool left = !prev || prev->isInlineFlowBox() && isprevindicated - || (toBegin && coming || !toBegin && last); - const bool right = !next || next->isInlineFlowBox() && isnextindicated - || (!toBegin && coming || toBegin && last); - const bool text2indicated = toBegin && next && next->isInlineTextBox() - && isprevindicated - || !toBegin && prev && prev->isInlineTextBox() && isnextindicated; - const bool indicated2text = !toBegin && next && next->isInlineTextBox() - && prev && isprevindicated - // ### this code is so broken. - /*|| toBegin && prev && prev->isInlineTextBox() && isnextindicated*/; -#if DEBUG_CARETMODE > 5 - kdDebug(6200) << "prev " << prev << " haslast " << haslast << " islastuseable " << islastuseable << " left " << left << " next " << next << " hascoming " << hascoming << " iscominguseable " << iscominguseable << " right " << right << " text2indicated " << text2indicated << " indicated2text " << indicated2text << endl; -#endif - - if (left && right && !text2indicated || indicated2text) { - adjacent = false; -#if DEBUG_CARETMODE > 4 - kdDebug(6200) << "left && right && !text2indicated || indicated2text" << endl; -#endif - break; - } - - } else { - // inside boxes are *always* valid -#if DEBUG_CARETMODE > 4 -if (box->isInline()) { - InlineBox *ibox = box->inlineBox(); - kdDebug(6200) << "inside " << (!ibox->isInlineFlowBox() || static_cast<InlineFlowBox *>(ibox)->firstChild() ? "non-empty" : "empty") << (isIndicatedInlineBox(ibox) ? " indicated" : "") << " adjacent=" << adjacent << endl; - } -#if 0 - RenderStyle *s = ibox->object()->style(); - kdDebug(6200) << "bordls " << s->borderLeftStyle() - << " bordl " << (s->borderLeftStyle() != BNONE) - << " bordr " << (s->borderRightStyle() != BNONE) - << " bordt " << (s->borderTopStyle() != BNONE) - << " bordb " << (s->borderBottomStyle() != BNONE) - << " padl " << s->paddingLeft().value() - << " padr " << s->paddingRight().value() - << " padt " << s->paddingTop().value() - << " padb " << s->paddingBottom().value() - // ### Can inline elements have top/bottom margins? Couldn't find - // it in the CSS 2 spec, but Mozilla ignores them, so we do, too. - << " marl " << s->marginLeft().value() - << " marr " << s->marginRight().value() - << endl; -#endif -#endif - break; - }/*end if*/ - - } else { - - if (!(*curbox)->isOutside()) { - // cannot be adjacent anymore - adjacent = false; - } - - }/*end if*/ - lastbox = curbox; - islastuseable = iscuruseable; - curbox = *this; - iscuruseable = iscominguseable; - curAtEnd = atEnd; - if (!atEnd) { - if (toBegin) CaretBoxIterator::operator --(); else CaretBoxIterator::operator ++(); - atEnd = *this == preBegin || *this == end; - }/*end if*/ - }/*wend*/ - - *static_cast<CaretBoxIterator *>(this) = curbox; -#if DEBUG_CARETMODE > 4 -// kdDebug(6200) << "still valid? " << (*this != preBegin && *this != end) << endl; -#endif -#if DEBUG_CARETMODE > 3 - kdDebug(6200) << "---------------" << k_funcinfo << "end " << endl; -#endif -} - -bool EditableCaretBoxIterator::isEditable(const CaretBoxIterator &boxit, bool fromEnd) -{ - Q_ASSERT(boxit != cbl->end() && boxit != cbl->preBegin()); - CaretBox *b = *boxit; - RenderObject *r = b->object(); -#if DEBUG_CARETMODE > 0 -// if (b->isInlineFlowBox()) kdDebug(6200) << "b is inline flow box" << (outside ? " (outside)" : "") << endl; - kdDebug(6200) << "isEditable r" << r << ": " << (r ? r->renderName() : TQString::null) << (r && r->isText() ? " contains \"" + TQString(((RenderText *)r)->str->s, QMIN(((RenderText *)r)->str->l,15)) + "\"" : TQString::null) << endl; -#endif - // Must check caret mode or design mode *after* r->element(), otherwise - // lines without a backing DOM node get regarded, leading to a crash. - // ### check should actually be in InlineBoxIterator - NodeImpl *node = r->element(); - ObjectTraversalState trav; - mapRenderPosToTraversalState(b->isOutside(), b->isOutsideEnd(), fromEnd, trav); - if (isUnsuitable(r, trav) || !node) { - return false; - } - - // generally exclude replaced elements with no children from navigation - if (!b->isOutside() && r->isRenderReplaced() && !r->firstChild()) - return false; - - RenderObject *eff_r = r; - bool globallyNavigable = m_part->isCaretMode() || m_part->isEditable(); - - // calculate the parent element's editability if this inline box is outside. - if (b->isOutside() && !globallyNavigable) { - NodeImpl *par = node->parent(); - // I wonder whether par can be 0. It shouldn't be possible if the - // algorithm contained no bugs. - Q_ASSERT(par); - if (par) node = par; - eff_r = node->renderer(); - Q_ASSERT(eff_r); // this is a hard requirement - } - - bool result = globallyNavigable || eff_r->style()->userInput() == UI_ENABLED; -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << result << endl; -#endif - return result; -} - -// == class EditableLineIterator implementation - -void EditableLineIterator::advance(bool toBegin) -{ - CaretAdvancePolicy advpol = lines->advancePolicy(); - LineIterator lasteditable, lastindicated; - bool haslasteditable = false; - bool haslastindicated = false; - bool uselasteditable = false; - - LineIterator::advance(toBegin); - while (cbl) { - if (isEditable(*this)) { -#if DEBUG_CARETMODE > 3 - kdDebug(6200) << "advance: " << cbl->enclosingObject() << "@" << cbl->enclosingObject()->renderName() << ".node " << cbl->enclosingObject()->element() << "[" << (cbl->enclosingObject()->element() ? cbl->enclosingObject()->element()->nodeName().string() : TQString::null) << "]" << endl; -#endif - - bool hasindicated = isIndicatedFlow(cbl->enclosingObject()); - if (hasindicated) { - haslastindicated = true; - lastindicated = *this; - } - - switch (advpol) { - case IndicatedFlows: - if (hasindicated) goto wend; - // fall through - case LeafsOnly: - if (cbl->isOutside()) break; - // fall through - case VisibleFlows: goto wend; - }/*end switch*/ - - // remember rejected editable element - lasteditable = *this; - haslasteditable = true; -#if DEBUG_CARETMODE > 4 - kdDebug(6200) << "remembered lasteditable " << *lasteditable << endl; -#endif - } else { - - // If this element isn't editable, but the last one was, and it was only - // rejected because it didn't match the caret advance policy, force it. - // Otherwise certain combinations of editable and uneditable elements - // could never be reached with some policies. - if (haslasteditable) { uselasteditable = true; break; } - - } - LineIterator::advance(toBegin); - }/*wend*/ -wend: - - if (uselasteditable) *this = haslastindicated ? lastindicated : lasteditable; - if (!cbl && haslastindicated) *this = lastindicated; -} - -// == class EditableCharacterIterator implementation - -void EditableCharacterIterator::initFirstChar() -{ - CaretBox *box = *ebit; - InlineBox *b = box->inlineBox(); - if (_offset == box->maxOffset()) - peekNext(); - else if (b && !box->isOutside() && b->isInlineTextBox()) - _char = static_cast<RenderText *>(b->object())->str->s[_offset].unicode(); - else - _char = -1; -} - -/** returns true when the given caret box is empty, i. e. should not - * take place in caret movement. - */ -static inline bool isCaretBoxEmpty(CaretBox *box) { - if (!box->isInline()) return false; - InlineBox *ibox = box->inlineBox(); - return ibox->isInlineFlowBox() - && !static_cast<InlineFlowBox *>(ibox)->firstChild() - && !isIndicatedInlineBox(ibox); -} - -EditableCharacterIterator &EditableCharacterIterator::operator ++() -{ - _offset++; - - CaretBox *box = *ebit; - InlineBox *b = box->inlineBox(); - long maxofs = box->maxOffset(); -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << "box->maxOffset() " << box->maxOffset() << " box->minOffset() " << box->minOffset() << endl; -#endif - if (_offset == maxofs) { -#if DEBUG_CARETMODE > 2 -kdDebug(6200) << "_offset == maxofs: " << _offset << " == " << maxofs << endl; -#endif - peekNext(); - } else if (_offset > maxofs) { -#if DEBUG_CARETMODE > 2 -kdDebug(6200) << "_offset > maxofs: " << _offset << " > " << maxofs /*<< " _peekNext: " << _peekNext*/ << endl; -#endif - if (/*!_peekNext*/true) { - ++ebit; - if (ebit == (*_it)->end()) { // end of line reached, go to next line - ++_it; -#if DEBUG_CARETMODE > 3 -kdDebug(6200) << "++_it" << endl; -#endif - if (_it != _it.lines->end()) { - ebit = _it; - box = *ebit; - b = box->inlineBox(); -#if DEBUG_CARETMODE > 3 -kdDebug(6200) << "box " << box << " b " << b << " isText " << box->isInlineTextBox() << endl; -#endif - -#if DEBUG_CARETMODE > 3 - RenderObject *_r = box->object(); -kdDebug(6200) << "_r " << _r << ":" << _r->element()->nodeName().string() << endl; -#endif - _offset = box->minOffset(); -#if DEBUG_CARETMODE > 3 -kdDebug(6200) << "_offset " << _offset << endl; -#endif - } else { - b = 0; - _end = true; - }/*end if*/ - goto readchar; - }/*end if*/ - }/*end if*/ - - bool adjacent = ebit.isAdjacent(); -#if 0 - // Jump over element if this one is not a text node. - if (adjacent && !(*ebit)->isInlineTextBox()) { - EditableCaretBoxIterator copy = ebit; - ++ebit; - if (ebit != (*_it)->end() && (*ebit)->isInlineTextBox() - /*&& (!(*ebit)->isInlineFlowBox() - || static_cast<InlineFlowBox *>(*ebit)->)*/) - adjacent = false; - else ebit = copy; - }/*end if*/ -#endif - // Jump over empty elements. - if (adjacent && !(*ebit)->isInlineTextBox()) { - bool noemptybox = true; - while (isCaretBoxEmpty(*ebit)) { - noemptybox = false; - EditableCaretBoxIterator copy = ebit; - ++ebit; - if (ebit == (*_it)->end()) { ebit = copy; break; } - } - if (noemptybox) adjacent = false; - }/*end if*/ -// _r = (*ebit)->object(); - /*if (!_it.outside) */_offset = (*ebit)->minOffset() + adjacent; - //_peekNext = 0; - box = *ebit; - b = box->inlineBox(); - goto readchar; - } else { -readchar: - // get character - if (b && !box->isOutside() && b->isInlineTextBox() && _offset < b->maxOffset()) - _char = static_cast<RenderText *>(b->object())->str->s[_offset].unicode(); - else - _char = -1; - }/*end if*/ -#if DEBUG_CARETMODE > 2 -kdDebug(6200) << "_offset: " << _offset /*<< " _peekNext: " << _peekNext*/ << " char '" << (char)_char << "'" << endl; -#endif - -#if DEBUG_CARETMODE > 0 - if (!_end && ebit != (*_it)->end()) { - CaretBox *box = *ebit; - RenderObject *_r = box->object(); - kdDebug(6200) << "echit++(1): box " << box << (box && box->isInlineTextBox() ? TQString(" contains \"%1\"").arg(TQConstString(static_cast<RenderText *>(box->object())->str->s+box->minOffset(), box->maxOffset() - box->minOffset()).string()) : TQString::null) << " _r " << (_r ? _r->element()->nodeName().string() : TQString("<nil>")) << endl; - } -#endif - return *this; -} - -EditableCharacterIterator &EditableCharacterIterator::operator --() -{ - _offset--; - //kdDebug(6200) << "--: _offset=" << _offset << endl; - - CaretBox *box = *ebit; - CaretBox *_peekPrev = 0; - CaretBox *_peekNext = 0; - InlineBox *b = box->inlineBox(); - long minofs = box->minOffset(); -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << "box->maxOffset() " << box->maxOffset() << " box->minOffset() " << box->minOffset() << endl; -#endif - if (_offset == minofs) { -#if DEBUG_CARETMODE > 2 -kdDebug(6200) << "_offset == minofs: " << _offset << " == " << minofs << endl; -#endif -// _peekNext = b; - // get character - if (b && !box->isOutside() && b->isInlineTextBox()) - _char = static_cast<RenderText *>(b->object())->text()[_offset].unicode(); - else - _char = -1; - - //peekPrev(); - bool do_prev = false; - { - EditableCaretBoxIterator copy; - _peekPrev = 0; - do { - copy = ebit; - --ebit; - if (ebit == (*_it)->preBegin()) { ebit = copy; break; } - } while (isCaretBoxEmpty(*ebit)); - // Jump to end of previous element if it's adjacent, and a text box - if (ebit.isAdjacent() && ebit != (*_it)->preBegin() && (*ebit)->isInlineTextBox()) { - _peekPrev = *ebit; - do_prev = true; - } else - ebit = copy; - } - if (do_prev) goto prev; - } else if (_offset < minofs) { -prev: -#if DEBUG_CARETMODE > 2 -kdDebug(6200) << "_offset < minofs: " << _offset << " < " << minofs /*<< " _peekNext: " << _peekNext*/ << endl; -#endif - if (!_peekPrev) { - _peekNext = *ebit; - --ebit; - if (ebit == (*_it)->preBegin()) { // end of line reached, go to previous line - --_it; -#if DEBUG_CARETMODE > 3 -kdDebug(6200) << "--_it" << endl; -#endif - if (_it != _it.lines->preBegin()) { -// kdDebug(6200) << "begin from end!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << endl; - ebit = EditableCaretBoxIterator(_it, true); - box = *ebit; -// RenderObject *r = box->object(); -#if DEBUG_CARETMODE > 3 -kdDebug(6200) << "box " << box << " b " << box->inlineBox() << " isText " << box->isInlineTextBox() << endl; -#endif - _offset = box->maxOffset(); -// if (!_it.outside) _offset = r->isBR() ? (*ebit)->minOffset() : (*ebit)->maxOffset(); - _char = -1; -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << "echit--(2): box " << box << " b " << box->inlineBox() << (box->isInlineTextBox() ? TQString(" contains \"%1\"").arg(TQConstString(static_cast<RenderText *>(box->object())->str->s+box->minOffset(), box->maxOffset() - box->minOffset()).string()) : TQString::null) << endl; -#endif - } else - _end = true; - return *this; - }/*end if*/ - }/*end if*/ - -#if DEBUG_CARETMODE > 0 - bool adjacent = ebit.isAdjacent(); - kdDebug(6200) << "adjacent " << adjacent << " _peekNext " << _peekNext << " _peekNext->isInlineTextBox: " << (_peekNext ? _peekNext->isInlineTextBox() : false) << " !((*ebit)->isInlineTextBox): " << (*ebit ? !(*ebit)->isInlineTextBox() : true) << endl; -#endif -#if 0 - // Ignore this box if it isn't a text box, but the previous box was - if (adjacent && _peekNext && _peekNext->isInlineTextBox() - && !(*ebit)->isInlineTextBox()) { - EditableCaretBoxIterator copy = ebit; - --ebit; - if (ebit == (*_it)->preBegin()) /*adjacent = false; - else */ebit = copy; - }/*end if*/ -#endif -#if 0 - // Jump over empty elements. - if (adjacent //&& _peekNext && _peekNext->isInlineTextBox() - && !(*ebit)->isInlineTextBox()) { - bool noemptybox = true; - while (isCaretBoxEmpty(*ebit)) { - noemptybox = false; - EditableCaretBoxIterator copy = ebit; - --ebit; - if (ebit == (*_it)->preBegin()) { ebit = copy; break; } - else _peekNext = *copy; - } - if (noemptybox) adjacent = false; - }/*end if*/ -#endif -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << "(*ebit)->obj " << (*ebit)->object()->renderName() << "[" << (*ebit)->object() << "]" << " minOffset: " << (*ebit)->minOffset() << " maxOffset: " << (*ebit)->maxOffset() << endl; -#endif -#if DEBUG_CARETMODE > 3 - RenderObject *_r = (*ebit)->object(); -kdDebug(6200) << "_r " << _r << ":" << _r->element()->nodeName().string() << endl; -#endif - _offset = (*ebit)->maxOffset(); -// if (!_it.outside) _offset = (*ebit)->maxOffset()/* - adjacent*/; -#if DEBUG_CARETMODE > 3 -kdDebug(6200) << "_offset " << _offset << endl; -#endif - _peekPrev = 0; - } else { -#if DEBUG_CARETMODE > 0 -kdDebug(6200) << "_offset: " << _offset << " _peekNext: " << _peekNext << endl; -#endif - // get character - if (_peekNext && _offset >= box->maxOffset() && _peekNext->isInlineTextBox()) - _char = static_cast<RenderText *>(_peekNext->object())->text()[_peekNext->minOffset()].unicode(); - else if (b && _offset < b->maxOffset() && b->isInlineTextBox()) - _char = static_cast<RenderText *>(b->object())->text()[_offset].unicode(); - else - _char = -1; - }/*end if*/ - -#if DEBUG_CARETMODE > 0 - if (!_end && ebit != (*_it)->preBegin()) { - CaretBox *box = *ebit; - kdDebug(6200) << "echit--(1): box " << box << " b " << box->inlineBox() << (box->isInlineTextBox() ? TQString(" contains \"%1\"").arg(TQConstString(static_cast<RenderText *>(box->object())->str->s+box->minOffset(), box->maxOffset() - box->minOffset()).string()) : TQString::null) << endl; - } -#endif - return *this; -} - -// == class TableRowIterator implementation - -TableRowIterator::TableRowIterator(RenderTable *table, bool fromEnd, - RenderTableSection::RowStruct *row) - : sec(table, fromEnd) -{ - // set index - if (*sec) { - if (fromEnd) index = (*sec)->grid.size() - 1; - else index = 0; - }/*end if*/ - - // initialize with given row - if (row && *sec) { - while (operator *() != row) - if (fromEnd) operator --(); else operator ++(); - }/*end if*/ -} - -TableRowIterator &TableRowIterator::operator ++() -{ - index++; - - if (index >= (int)(*sec)->grid.size()) { - ++sec; - - if (*sec) index = 0; - }/*end if*/ - return *this; -} - -TableRowIterator &TableRowIterator::operator --() -{ - index--; - - if (index < 0) { - --sec; - - if (*sec) index = (*sec)->grid.size() - 1; - }/*end if*/ - return *this; -} - -// == class ErgonomicEditableLineIterator implementation - -// some decls -static RenderTableCell *findNearestTableCellInRow(KHTMLPart *part, int x, - RenderTableSection::RowStruct *row, bool fromEnd); - -/** finds the cell corresponding to absolute x-coordinate @p x in the given - * table. - * - * If there is no direct cell, or the cell is not accessible, the function - * will return the nearest suitable cell. - * @param part part containing the document - * @param x absolute x-coordinate - * @param it table row iterator, will be adapted accordingly as more rows are - * investigated. - * @param fromEnd @p true to begin search from end and work towards the - * beginning - * @return the cell, or 0 if no editable cell was found. - */ -static inline RenderTableCell *findNearestTableCell(KHTMLPart *part, int x, - TableRowIterator &it, bool fromEnd) -{ - RenderTableCell *result = 0; - - while (*it) { - result = findNearestTableCellInRow(part, x, *it, fromEnd); - if (result) break; - - if (fromEnd) --it; else ++it; - }/*wend*/ - - return result; -} - -/** finds the nearest editable cell around the given absolute x-coordinate - * - * It will dive into nested tables as necessary to provide seamless navigation. - * - * If the cell at @p x is not editable, its left neighbor is tried, then its - * right neighbor, then the left neighbor's left neighbor etc. If no - * editable cell can be found, 0 is returned. - * @param part khtml part - * @param x absolute x-coordinate - * @param row table row to be searched - * @param fromEnd @p true, begin from end (applies only to nested tables) - * @return the found cell or 0 if no editable cell was found - */ -static RenderTableCell *findNearestTableCellInRow(KHTMLPart *part, int x, - RenderTableSection::RowStruct *row, bool fromEnd) -{ - // First pass. Find spatially nearest cell. - int n = (int)row->row->size(); - int i; - for (i = 0; i < n; i++) { - RenderTableCell *cell = row->row->at(i); - if (!cell || (long)cell == -1) continue; - - int absx, absy; - cell->absolutePosition(absx, absy, false); // ### position: fixed? -#if DEBUG_CARETMODE > 1 - kdDebug(6201) << "i/n " << i << "/" << n << " absx " << absx << " absy " << absy << endl; -#endif - - // I rely on the assumption that all cells are in ascending visual order - // ### maybe this assumption is wrong for bidi? -#if DEBUG_CARETMODE > 1 - kdDebug(6201) << "x " << x << " < " << (absx + cell->width()) << "?" << endl; -#endif - if (x < absx + cell->width()) break; - }/*next i*/ - if (i >= n) i = n - 1; - - // Second pass. Find editable cell, beginning with the currently found, - // extending to the left, and to the right, alternating. - for (int cnt = 0; cnt < 2*n; cnt++) { - int index = i - ((cnt >> 1) + 1)*(cnt & 1) + (cnt >> 1)*!(cnt & 1); - if (index < 0 || index >= n) continue; - - RenderTableCell *cell = row->row->at(index); - if (!cell || (long)cell == -1) continue; - -#if DEBUG_CARETMODE > 1 - kdDebug(6201) << "index " << index << " cell " << cell << endl; -#endif - RenderTable *nestedTable; - if (containsEditableElement(part, cell, nestedTable, fromEnd)) { - - if (nestedTable) { - TableRowIterator it(nestedTable, fromEnd); - while (*it) { -// kdDebug(6201) << "=== recursive invocation" << endl; - cell = findNearestTableCell(part, x, it, fromEnd); - if (cell) break; - if (fromEnd) --it; else ++it; - }/*wend*/ - }/*end if*/ - - return cell; - }/*end if*/ - }/*next i*/ - return 0; -} - -/** returns the nearest common ancestor of two objects that is a table cell, - * a table section, or 0 if not inside a common table. - * - * If @p r1 and @p r2 belong to the same table, but different sections, @p r1's - * section is returned. - */ -static RenderObject *commonAncestorTableSectionOrCell(RenderObject *r1, - RenderObject *r2) -{ - if (!r1 || !r2) return 0; - RenderTableSection *sec = 0; - int start_depth=0, end_depth=0; - // First we find the depths of the two objects in the tree (start_depth, end_depth) - RenderObject *n = r1; - while (n->parent()) { - n = n->parent(); - start_depth++; - }/*wend*/ - n = r2; - while( n->parent()) { - n = n->parent(); - end_depth++; - }/*wend*/ - // here we climb up the tree with the deeper object, until both objects have equal depth - while (end_depth > start_depth) { - r2 = r2->parent(); - end_depth--; - }/*wend*/ - while (start_depth > end_depth) { - r1 = r1->parent(); -// if (r1->isTableSection()) sec = static_cast<RenderTableSection *>(r1); - start_depth--; - }/*wend*/ - // Climb the tree with both r1 and r2 until they are the same - while (r1 != r2){ - r1 = r1->parent(); - if (r1->isTableSection()) sec = static_cast<RenderTableSection *>(r1); - r2 = r2->parent(); - }/*wend*/ - - // At this point, we found the most approximate common ancestor. Now climb - // up until the condition of the function return value is satisfied. - while (r1 && !r1->isTableCell() && !r1->isTableSection() && !r1->isTable()) - r1 = r1->parent(); - - return r1 && r1->isTable() ? sec : r1; -} - -/** Finds the row that contains the given cell, directly, or indirectly - * @param section section to be searched - * @param cell table cell - * @param row returns the row - * @param directCell returns the direct cell that contains @p cell - * @return the index of the row. - */ -static int findRowInSection(RenderTableSection *section, RenderTableCell *cell, - RenderTableSection::RowStruct *&row, RenderTableCell *&directCell) -{ - // Seek direct cell - RenderObject *r = cell; - while (r != section) { - if (r->isTableCell()) directCell = static_cast<RenderTableCell *>(r); - r = r->parent(); - }/*wend*/ - - // So, and this is really nasty: As we have no indices, we have to do a - // linear comparison. Oh, that sucks so much for long tables, you can't - // imagine. - int n = section->numRows(); - for (int i = 0; i < n; i++) { - row = §ion->grid[i]; - - // check for cell - int m = row->row->size(); - for (int j = 0; j < m; j++) { - RenderTableCell *c = row->row->at(j); - if (c == directCell) return i; - }/*next j*/ - - }/*next i*/ - Q_ASSERT(false); - return -1; -} - -/** finds the table that is the first direct or indirect descendant of @p block. - * @param leaf object to begin search from. - * @param block object to search to, or 0 to search up to top. - * @return the table or 0 if there were none. - */ -static inline RenderTable *findFirstDescendantTable(RenderObject *leaf, RenderBlock *block) -{ - RenderTable *result = 0; - while (leaf && leaf != block) { - if (leaf->isTable()) result = static_cast<RenderTable *>(leaf); - leaf = leaf->parent(); - }/*wend*/ - return result; -} - -/** looks for the table cell the given object @p r is contained within. - * @return the table cell or 0 if not contained in any table. - */ -static inline RenderTableCell *containingTableCell(RenderObject *r) -{ - while (r && !r->isTableCell()) r = r->parent(); - return static_cast<RenderTableCell *>(r); -} - -inline void ErgonomicEditableLineIterator::calcAndStoreNewLine( - RenderBlock *newBlock, bool toBegin) -{ - // take the first/last editable element in the found cell as the new - // value for the iterator - CaretBoxIterator it; - cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter, - newBlock, true, toBegin, it); -#if DEBUG_CARETMODE > 3 - kdDebug(6201) << cbl->information() << endl; -#endif -// if (toBegin) prevBlock(); else nextBlock(); - - if (!cbl) { - return; - }/*end if*/ - - EditableLineIterator::advance(toBegin); -} - -void ErgonomicEditableLineIterator::determineTopologicalElement( - RenderTableCell *oldCell, RenderObject *newObject, bool toBegin) -{ - // When we arrive here, a transition between cells has happened. - // Now determine the type of the transition. This can be - // (1) a transition from this cell into a table inside this cell. - // (2) a transition from this cell into another cell of this table - - TableRowIterator it; - - RenderObject *commonAncestor = commonAncestorTableSectionOrCell(oldCell, newObject); -#if DEBUG_CARETMODE > 1 - kdDebug(6201) << " ancestor " << commonAncestor << endl; -#endif - - // The whole document is treated as a table cell. - if (!commonAncestor || commonAncestor->isTableCell()) { // (1) - - RenderTableCell *cell = static_cast<RenderTableCell *>(commonAncestor); - RenderTable *table = findFirstDescendantTable(newObject, cell); - -#if DEBUG_CARETMODE > 0 - kdDebug(6201) << "table cell: " << cell << endl; -#endif - - // if there is no table, we fell out of the previous table, and are now - // in some table-less block. Therefore, done. - if (!table) return; - - it = TableRowIterator(table, toBegin); - - } else if (commonAncestor->isTableSection()) { // (2) - - RenderTableSection *section = static_cast<RenderTableSection *>(commonAncestor); - RenderTableSection::RowStruct *row; - int idx = findRowInSection(section, oldCell, row, oldCell); -#if DEBUG_CARETMODE > 1 - kdDebug(6201) << "table section: row idx " << idx << endl; -#endif - - it = TableRowIterator(section, idx); - - // advance rowspan rows - int rowspan = oldCell->rowSpan(); - while (*it && rowspan--) { - if (toBegin) --it; else ++it; - }/*wend*/ - - } else { - kdError(6201) << "Neither common cell nor section! " << commonAncestor->renderName() << endl; - // will crash on uninitialized table row iterator - }/*end if*/ - - RenderTableCell *cell = findNearestTableCell(lines->m_part, xCoor, it, toBegin); -#if DEBUG_CARETMODE > 1 - kdDebug(6201) << "findNearestTableCell result: " << cell << endl; -#endif - - RenderBlock *newBlock = cell; - if (!cell) { - Q_ASSERT(commonAncestor->isTableSection()); - RenderTableSection *section = static_cast<RenderTableSection *>(commonAncestor); - cell = containingTableCell(section); -#if DEBUG_CARETMODE > 1 - kdDebug(6201) << "containing cell: " << cell << endl; -#endif - - RenderTable *nestedTable; - bool editableChild = cell && containsEditableChildElement(lines->m_part, - cell, nestedTable, toBegin, section->table()); - - if (cell && !editableChild) { -#if DEBUG_CARETMODE > 1 - kdDebug(6201) << "========= recursive invocation outer =========" << endl; -#endif - determineTopologicalElement(cell, cell->section(), toBegin); -#if DEBUG_CARETMODE > 1 - kdDebug(6201) << "========= end recursive invocation outer =========" << endl; -#endif - return; - - } else if (cell && nestedTable) { -#if DEBUG_CARETMODE > 1 - kdDebug(6201) << "========= recursive invocation inner =========" << endl; -#endif - determineTopologicalElement(cell, nestedTable, toBegin); -#if DEBUG_CARETMODE > 1 - kdDebug(6201) << "========= end recursive invocation inner =========" << endl; -#endif - return; - - } else { -#if DEBUG_CARETMODE > 1 - kdDebug(6201) << "newBlock is table: " << section->table() << endl; -#endif - RenderObject *r = section->table(); - int state; // not used - ObjectTraversalState trav = OutsideAscending; - r = advanceSuitableObject(r, trav, toBegin, lines->baseObject(), state); - if (!r) { cbl = 0; return; } -// if (toBegin) prevBlock(); else nextBlock(); - newBlock = static_cast<RenderBlock *>(!r || r->isRenderBlock() ? r : r->containingBlock()); - }/*end if*/ -#if 0 - } else { - // adapt cell so that prevBlock/nextBlock works as expected - newBlock = cell; - // on forward advancing, we must start from the outside end of the - // previous object - if (!toBegin) { - RenderObject *r = newBlock; - int state; // not used - ObjectTraversalState trav = OutsideAscending; - r = advanceSuitableObject(r, trav, true, lines->advancePolicy(), lines->baseObject(), state); - newBlock = static_cast<RenderBlock *>(!r || r->isRenderBlock() ? r : r->containingBlock()); - }/*end if*/ -#endif - }/*end if*/ - - calcAndStoreNewLine(newBlock, toBegin); -} - -ErgonomicEditableLineIterator &ErgonomicEditableLineIterator::operator ++() -{ - RenderTableCell *oldCell = containingTableCell(cbl->enclosingObject()); - - EditableLineIterator::operator ++(); - if (*this == lines->end() || *this == lines->preBegin()) return *this; - - RenderTableCell *newCell = containingTableCell(cbl->enclosingObject()); - - if (!newCell || newCell == oldCell) return *this; - - determineTopologicalElement(oldCell, newCell, false); - - return *this; -} - -ErgonomicEditableLineIterator &ErgonomicEditableLineIterator::operator --() -{ - RenderTableCell *oldCell = containingTableCell(cbl->enclosingObject()); - - EditableLineIterator::operator --(); - if (*this == lines->end() || *this == lines->preBegin()) return *this; - - RenderTableCell *newCell = containingTableCell(cbl->enclosingObject()); - - if (!newCell || newCell == oldCell) return *this; - - determineTopologicalElement(oldCell, newCell, true); - - return *this; -} - -// == Navigational helper functions == - -/** seeks the caret box which contains or is the nearest to @p x - * @param it line iterator pointing to line to be searched - * @param cv caret view context - * @param x returns the cv->origX approximation, relatively positioned to the - * containing block. - * @param absx returns absolute x-coordinate of containing block - * @param absy returns absolute y-coordinate of containing block - * @return the most suitable caret box - */ -static CaretBox *nearestCaretBox(LineIterator &it, CaretViewContext *cv, - int &x, int &absx, int &absy) -{ - // Find containing block - RenderObject *cb = (*it)->containingBlock(); -#if DEBUG_CARETMODE > 4 - kdDebug(6200) << "nearestCB: cb " << cb << "@" << (cb ? cb->renderName() : "") << endl; -#endif - - if (cb) cb->absolutePosition(absx, absy); - else absx = absy = 0; - - // Otherwise find out in which inline box the caret is to be placed. - - // this horizontal position is to be approximated - x = cv->origX - absx; - CaretBox *caretBox = 0; // Inline box containing the caret -// NodeImpl *lastnode = 0; // node of previously checked render object. - int xPos; // x-coordinate of current inline box - int oldXPos = -1; // x-coordinate of last inline box - EditableCaretBoxIterator fbit = it; -#if DEBUG_CARETMODE > 0 -/* if (it.linearDocument()->advancePolicy() != LeafsOnly) - kdWarning() << "nearestInlineBox is only prepared to handle the LeafsOnly advance policy" << endl;*/ -// kdDebug(6200) << "*fbit = " << *fbit << endl; -#endif - // Iterate through all children - for (CaretBox *b; fbit != (*it)->end(); ++fbit) { - b = *fbit; - -#if DEBUG_CARETMODE > 0 -// RenderObject *r = b->object(); -// if (b->isInlineFlowBox()) kdDebug(6200) << "b is inline flow box" << endl; -// kdDebug(6200) << "approximate r" << r << ": " << (r ? r->renderName() : TQString::null) << (r && r->isText() ? " contains \"" + TQString(((RenderText *)r)->str->s, ((RenderText *)r)->str->l) + "\"" : TQString::null) << endl; -#endif - xPos = b->xPos(); - - // the caret is before this box - if (x < xPos) { - // snap to nearest box - if (oldXPos < 0 || x - (oldXPos + caretBox->width()) > xPos - x) { - caretBox = b; // current box is nearer - }/*end if*/ - break; // Otherwise, preceding box is implicitly used - } - - caretBox = b; - - // the caret is within this box - if (x >= xPos && x < xPos + caretBox->width()) - break; - oldXPos = xPos; - - // the caret can only be after the last box which is automatically - // contained in caretBox when we fall out of the loop. - }/*next fbit*/ - - return caretBox; -} - -/** moves the given iterator to the beginning of the next word. - * - * If the end is reached, the iterator will be positioned there. - * @param it character iterator to be moved - */ -static void moveItToNextWord(EditableCharacterIterator &it) -{ -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << "%%%%%%%%%%%%%%%%%%%%% moveItToNextWord" << endl; -#endif - EditableCharacterIterator copy; - while (!it.isEnd() && !(*it).isSpace() && !(*it).isPunct()) { -#if DEBUG_CARETMODE > 2 - kdDebug(6200) << "reading1 '" << (*it).latin1() << "'" << endl; -#endif - copy = it; - ++it; - } - - if (it.isEnd()) { - it = copy; - return; - }/*end if*/ - - while (!it.isEnd() && ((*it).isSpace() || (*it).isPunct())) { -#if DEBUG_CARETMODE > 2 - kdDebug(6200) << "reading2 '" << (*it).latin1() << "'" << endl; -#endif - copy = it; - ++it; - } - - if (it.isEnd()) it = copy; -} - -/** moves the given iterator to the beginning of the previous word. - * - * If the beginning is reached, the iterator will be positioned there. - * @param it character iterator to be moved - */ -static void moveItToPrevWord(EditableCharacterIterator &it) -{ - if (it.isEnd()) return; - -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << "%%%%%%%%%%%%%%%%%%%%% moveItToPrevWord" << endl; -#endif - EditableCharacterIterator copy; - - // Jump over all space and punctuation characters first - do { - copy = it; - --it; -#if DEBUG_CARETMODE > 2 - if (!it.isEnd()) kdDebug(6200) << "reading1 '" << (*it).latin1() << "'" << endl; -#endif - } while (!it.isEnd() && ((*it).isSpace() || (*it).isPunct())); - - if (it.isEnd()) { - it = copy; - return; - }/*end if*/ - - do { - copy = it; - --it; -#if DEBUG_CARETMODE > 0 - if (!it.isEnd()) kdDebug(6200) << "reading2 '" << (*it).latin1() << "' (" << (int)(*it).latin1() << ") box " << it.caretBox() << endl; -#endif - } while (!it.isEnd() && !(*it).isSpace() && !(*it).isPunct()); - - it = copy; -#if DEBUG_CARETMODE > 1 - if (!it.isEnd()) kdDebug(6200) << "effective '" << (*it).latin1() << "' (" << (int)(*it).latin1() << ") box " << it.caretBox() << endl; -#endif -} - -/** moves the iterator by one page. - * @param ld linear document - * @param it line iterator, will be updated accordingly - * @param mindist minimum distance in pixel the iterator should be moved - * (if possible) - * @param next @p true, move downward, @p false move upward - */ -static void moveIteratorByPage(LinearDocument &ld, - ErgonomicEditableLineIterator &it, int mindist, bool next) -{ - // ### This whole routine plainly sucks. Use an inverse strategie for pgup/pgdn. - - if (it == ld.end() || it == ld.preBegin()) return; - - ErgonomicEditableLineIterator copy = it; -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << " mindist: " << mindist << endl; -#endif - - CaretBoxLine *cbl = *copy; - int absx = 0, absy = 0; - - RenderBlock *lastcb = cbl->containingBlock(); - Q_ASSERT(lastcb->isRenderBlock()); - lastcb->absolutePosition(absx, absy, false); // ### what about fixed? - - int lastfby = cbl->begin().data()->yPos(); - int lastheight = 0; - int rescue = 1000; // ### this is a hack to keep stuck carets from hanging the ua - do { - if (next) ++copy; else --copy; - if (copy == ld.end() || copy == ld.preBegin()) break; - - cbl = *copy; - RenderBlock *cb = cbl->containingBlock(); - - int diff = 0; - // ### actually flowBox->yPos() should suffice, but this is not ported - // over yet from WebCore - int fby = cbl->begin().data()->yPos(); - if (cb != lastcb) { - if (next) { - diff = absy + lastfby + lastheight; - cb->absolutePosition(absx, absy, false); // ### what about fixed? - diff = absy - diff + fby; - lastfby = 0; - } else { - diff = absy; - cb->absolutePosition(absx, absy, false); // ### what about fixed? - diff -= absy + fby + lastheight; - lastfby = fby - lastheight; - }/*end if*/ -#if DEBUG_CARETMODE > 2 - kdDebug(6200) << "absdiff " << diff << endl; -#endif - } else { - diff = kAbs(fby - lastfby); - }/*end if*/ -#if DEBUG_CARETMODE > 2 - kdDebug(6200) << "cbl->begin().data()->yPos(): " << fby << " diff " << diff << endl; -#endif - - mindist -= diff; - - lastheight = kAbs(fby - lastfby); - lastfby = fby; - lastcb = cb; - it = copy; -#if DEBUG_CARETMODE > 0 - kdDebug(6200) << " mindist: " << mindist << endl; -#endif - // trick: actually the distance is always one line short, but we cannot - // calculate the height of the first line (### WebCore will make it better) - // Therefore, we simply approximate that excess line by using the last - // caluculated line height. - } while (mindist - lastheight > 0 && --rescue); -} - - -}/*end namespace*/ |