📄 khtml_caret.cpp
字号:
/* 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->overflow() != OVISIBLE || 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 > 5kdDebug(6200) << "a " << a << " trav " << trav << endl;#endif if (a->element()) {#if DEBUG_CARETMODE > 4kdDebug(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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -