1 /* This file is part of the KDE project
3 * Copyright (C) 2003-2004 Leo Savernik <l.savernik@aon.at>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
22 #include "khtml_caret_p.h"
24 #include "html/html_documentimpl.h"
28 /** Flags representing the type of advance that has been made.
29 * @param LeftObject a render object was left and an ascent to its parent has
31 * @param AdvancedToSibling an actual advance to a sibling has taken place
32 * @param EnteredObject a render object was entered by descending into it from
35 enum ObjectAdvanceState
{
36 LeftObject
= 0x01, AdvancedToSibling
= 0x02, EnteredObject
= 0x04
39 /** All possible states that may occur during render object traversal.
40 * @param OutsideDescending outside of the current object, ready to descend
42 * @param InsideDescending inside the current object, descending into
44 * @param InsideAscending inside the current object, ascending to parents
45 * @param OutsideAscending outsie the current object, ascending to parents
47 enum ObjectTraversalState
{
48 OutsideDescending
, InsideDescending
, InsideAscending
, OutsideAscending
51 /** Traverses the render object tree in a fine granularity.
52 * @param obj render object
53 * @param trav object traversal state
54 * @param toBegin traverse towards beginning
55 * @param base base render object which this method must not advance beyond
57 * @param state object advance state (enum ObjectAdvanceState)
58 * @return the render object according to the state. May be the same as \c obj
60 static RenderObject
* traverseRenderObjects(RenderObject
*obj
,
61 ObjectTraversalState
&trav
, bool toBegin
, RenderObject
*base
,
66 case OutsideDescending
:
67 trav
= InsideDescending
;
69 case InsideDescending
:
70 r
= toBegin
? obj
->lastChild() : obj
->firstChild();
72 trav
= OutsideDescending
;
74 state
|= EnteredObject
;
76 trav
= InsideAscending
;
80 trav
= OutsideAscending
;
82 case OutsideAscending
:
83 r
= toBegin
? obj
->previousSibling() : obj
->nextSibling();
85 trav
= OutsideDescending
;
86 state
|= AdvancedToSibling
;
90 trav
= InsideAscending
;
100 /** Like RenderObject::objectBelow, but confined to stay within \c base.
101 * @param obj render object to begin with
102 * @param trav object traversal state, will be reset within this function
103 * @param base base render object (0: no base)
105 static inline RenderObject
*renderObjectBelow(RenderObject
*obj
, ObjectTraversalState
&trav
, RenderObject
*base
)
107 trav
= InsideDescending
;
108 int state
; // we don't need the state, so we don't initialize it
109 RenderObject
*r
= obj
;
110 while (r
&& trav
!= OutsideDescending
) {
111 r
= traverseRenderObjects(r
, trav
, false, base
, state
);
112 #if DEBUG_CARETMODE > 3
113 kDebug(6200) << "renderObjectBelow: r " << r
<< " trav " << trav
;
116 trav
= InsideDescending
;
120 /** Like RenderObject::objectAbove, but confined to stay within \c base.
121 * @param obj render object to begin with
122 * @param trav object traversal state, will be reset within this function
123 * @param base base render object (0: no base)
125 static inline RenderObject
*renderObjectAbove(RenderObject
*obj
, ObjectTraversalState
&trav
, RenderObject
*base
)
127 trav
= OutsideAscending
;
128 int state
; // we don't need the state, so we don't initialize it
129 RenderObject
*r
= obj
;
130 while (r
&& trav
!= InsideAscending
) {
131 r
= traverseRenderObjects(r
, trav
, true, base
, state
);
132 #if DEBUG_CARETMODE > 3
133 kDebug(6200) << "renderObjectAbove: r " << r
<< " trav " << trav
;
136 trav
= InsideAscending
;
140 /** Checks whether the given inline box matches the IndicatedFlows policy
141 * @param box inline box to test
142 * @return true on match
144 static inline bool isIndicatedInlineBox(InlineBox
*box
)
146 // text boxes are never indicated.
147 if (box
->isInlineTextBox()) return false;
148 RenderStyle
*s
= box
->object()->style();
149 return s
->borderLeftWidth() || s
->borderRightWidth()
150 || s
->borderTopWidth() || s
->borderBottomWidth()
151 || s
->paddingLeft().value() || s
->paddingRight().value()
152 || s
->paddingTop().value() || s
->paddingBottom().value()
153 // ### Can inline elements have top/bottom margins? Couldn't find
154 // it in the CSS 2 spec, but Mozilla ignores them, so we do, too.
155 || s
->marginLeft().value() || s
->marginRight().value();
158 /** Checks whether the given render object matches the IndicatedFlows policy
159 * @param r render object to test
160 * @return true on match
162 static inline bool isIndicatedFlow(RenderObject
*r
)
164 RenderStyle
*s
= r
->style();
165 return s
->borderLeftStyle() != BNONE
|| s
->borderRightStyle() != BNONE
166 || s
->borderTopStyle() != BNONE
|| s
->borderBottomStyle() != BNONE
167 // || s->paddingLeft().value() || s->paddingRight().value()
168 // || s->paddingTop().value() || s->paddingBottom().value()
169 // || s->marginLeft().value() || s->marginRight().value()
170 || s
->hasClip() || s
->hidesOverflow()
171 || s
->backgroundColor().isValid() || s
->backgroundImage();
174 /** Advances to the next render object, taking into account the current
177 * @param r render object
178 * @param trav object traversal state
179 * @param toBegin @p true, advance towards beginning, @p false, advance toward end
180 * @param base base render object which this method must not advance beyond
182 * @param state object advance state (enum ObjectAdvanceState) (unchanged
184 * @return a pointer to the render object which we advanced to,
185 * or 0 if the last possible object has been reached.
187 static RenderObject
*advanceObject(RenderObject
*r
,
188 ObjectTraversalState
&trav
, bool toBegin
,
189 RenderObject
*base
, int &state
)
192 ObjectTraversalState origtrav
= trav
;
193 RenderObject
*a
= traverseRenderObjects(r
, trav
, toBegin
, base
, state
);
195 bool ignoreOutsideDesc
= toBegin
&& origtrav
== OutsideAscending
;
197 // render object and traversal state at which look ahead has been started
198 RenderObject
*la
= 0;
199 ObjectTraversalState latrav
= trav
;
200 ObjectTraversalState lasttrav
= origtrav
;
203 #if DEBUG_CARETMODE > 5
204 kDebug(6200) << "a " << a
<< " trav " << trav
;
207 #if DEBUG_CARETMODE > 4
208 kDebug(6200) << "a " << a
<< " trav " << trav
<< " origtrav " << origtrav
<< " ignoreOD " << ignoreOutsideDesc
;
213 case OutsideDescending
:
214 if (trav
== InsideAscending
) return a
;
215 if (trav
== OutsideDescending
) return a
;
217 case InsideDescending
:
218 if (trav
== OutsideDescending
) return a
;
220 case InsideAscending
:
221 if (trav
== OutsideAscending
) return a
;
223 case OutsideAscending
:
224 if (trav
== OutsideAscending
) return a
;
225 if (trav
== InsideAscending
&& lasttrav
== InsideDescending
) return a
;
226 if (trav
== OutsideDescending
&& !ignoreOutsideDesc
) return a
;
227 // ignore this outside descending position, as it effectively
228 // demarkates the same position, but remember it in case we fall off
230 la
= a
; latrav
= trav
;
231 ignoreOutsideDesc
= false;
238 case OutsideDescending
:
239 if (trav
== InsideAscending
) return a
;
240 if (trav
== OutsideDescending
) return a
;
242 case InsideDescending
:
243 // if (trav == OutsideDescending) return a;
245 case InsideAscending
:
246 // if (trav == OutsideAscending) return a;
248 case OutsideAscending
:
249 // ### what if origtrav == OA, and immediately afterwards trav
250 // becomes OD? In this case the effective position hasn't changed ->
251 // the caret gets stuck. Otherwise, it apparently cannot happen in
252 // real usage patterns.
253 if (trav
== OutsideDescending
) return a
;
254 if (trav
== OutsideAscending
) {
256 // starting lookahead here. Remember old object in case we fall off
258 la
= a
; latrav
= trav
;
267 a
= traverseRenderObjects(a
, trav
, toBegin
, base
, state
);
270 if (la
) trav
= latrav
, a
= la
;
275 /** Check whether the current render object is unsuitable in caret mode handling.
277 * Some render objects cannot be handled correctly in caret mode. These objects
278 * are therefore considered to be unsuitable. The null object is suitable, as
279 * it denotes reaching the end.
280 * @param r current render object
281 * @param trav current traversal state
283 static inline bool isUnsuitable(RenderObject
*r
, ObjectTraversalState trav
)
285 if (!r
) return false;
286 return r
->isTableCol() || r
->isTableSection() || r
->isTableRow()
287 || (r
->isText() && !static_cast<RenderText
*>(r
)->firstTextBox());
292 /** Advances to the next render object, taking into account the current
293 * traversal state, but skipping render objects which are not suitable for
294 * having placed the caret into them.
295 * @param r render object
296 * @param trav object traversal state (unchanged on LeafsOnly)
297 * @param toBegin @p true, advance towards beginning, @p false, advance toward end
298 * @param base base render object which this method must not advance beyond
300 * @param state object advance state (enum ObjectAdvanceState) (unchanged
302 * @return a pointer to the advanced render object or 0 if the last possible
303 * object has been reached.
305 static inline RenderObject
*advanceSuitableObject(RenderObject
*r
,
306 ObjectTraversalState
&trav
, bool toBegin
,
307 RenderObject
*base
, int &state
)
310 r
= advanceObject(r
, trav
, toBegin
, base
, state
);
311 #if DEBUG_CARETMODE > 2
312 kDebug(6200) << "after advanceSWP: r " << r
<< " trav " << trav
<< " toBegin " << toBegin
;
314 } while (isUnsuitable(r
, trav
));
319 * Returns the next leaf node.
321 * Using this function delivers leaf nodes as if the whole DOM tree
322 * were a linear chain of its leaf nodes.
324 * @param baseElem base element not to search beyond
325 * @return next leaf node or 0 if there are no more.
327 static NodeImpl
*nextLeafNode(NodeImpl
*r
, NodeImpl
*baseElem
)
329 NodeImpl
*n
= r
->firstChild();
331 while (n
) { r
= n
; n
= n
->firstChild(); }
332 return const_cast<NodeImpl
*>(r
);
334 n
= r
->nextSibling();
337 while (n
) { r
= n
; n
= n
->firstChild(); }
338 return const_cast<NodeImpl
*>(r
);
342 if (n
== baseElem
) n
= 0;
345 n
= r
->nextSibling();
349 while (n
) { r
= n
; n
= n
->firstChild(); }
350 return const_cast<NodeImpl
*>(r
);
353 if (n
== baseElem
) n
= 0;
358 #if 0 // currently not used
359 /** (Not part of the official DOM)
360 * Returns the previous leaf node.
362 * Using this function delivers leaf nodes as if the whole DOM tree
363 * were a linear chain of its leaf nodes.
365 * @param baseElem base element not to search beyond
366 * @return previous leaf node or 0 if there are no more.
368 static NodeImpl
*prevLeafNode(NodeImpl
*r
, NodeImpl
*baseElem
)
370 NodeImpl
*n
= r
->firstChild();
372 while (n
) { r
= n
; n
= n
->firstChild(); }
373 return const_cast<NodeImpl
*>(r
);
375 n
= r
->previousSibling();
378 while (n
) { r
= n
; n
= n
->firstChild(); }
379 return const_cast<NodeImpl
*>(r
);
383 if (n
== baseElem
) n
= 0;
386 n
= r
->previousSibling();
390 while (n
) { r
= n
; n
= n
->lastChild(); }
391 return const_cast<NodeImpl
*>(r
);
394 if (n
== baseElem
) n
= 0;
400 /** Maps a DOM Range position to the corresponding caret position.
402 * The offset boundary is not checked for validity.
403 * @param node DOM node
404 * @param offset zero-based offset within node
405 * @param r returns render object (may be 0 if DOM node has no render object)
406 * @param r_ofs returns the appropriate offset for the found render object r
407 * @param outside returns true when offset is applied to the outside of
408 * \c r, or false for the inside.
409 * @param outsideEnd return true when the caret position is at the outside end.
411 void /*KDE_NO_EXPORT*/ mapDOMPosToRenderPos(NodeImpl
*node
, long offset
,
412 RenderObject
*&r
, long &r_ofs
, bool &outside
, bool &outsideEnd
)
414 if (node
->nodeType() == Node::TEXT_NODE
) {
417 r
= node
->renderer();
419 } else if (node
->nodeType() == Node::ELEMENT_NODE
|| node
->nodeType() == Node::DOCUMENT_NODE
) {
421 // Though offset points between two children, attach it to the visually
422 // most suitable one (and only there, because the mapping must stay bijective)
423 if (node
->firstChild()) {
425 NodeImpl
*child
= offset
<= 0 ? node
->firstChild()
426 // childNode is expensive
427 : node
->childNode((unsigned long)offset
);
428 // index was child count or out of bounds
430 #if DEBUG_CARETMODE > 5
431 kDebug(6200) << "mapDTR: child " << child
<< "@" << (child
? child
->nodeName().string() : QString()) << " atEnd " << atEnd
;
433 if (atEnd
) child
= node
->lastChild();
435 r
= child
->renderer();
439 // Outside text nodes most likely stem from a continuation. Seek
440 // the enclosing continued render object and use this one instead.
441 if (r
&& child
->nodeType() == Node::TEXT_NODE
) {
443 RenderObject
*o
= node
->renderer();
444 while (o
->continuation() && o
->continuation() != r
)
445 o
= o
->continuation();
446 if (!r
|| o
->continuation() != r
) {
447 r
= child
->renderer();
451 // BRs cause troubles. Returns the previous render object instead,
452 // giving it the attributes outside, outside end.
453 if (r
&& r
->isBR()) {
454 r
= r
->objectAbove();
459 // Element has no children, treat offset to be inside the node.
462 r
= node
->renderer();
463 r_ofs
= 0; // only offset 0 possible
468 kWarning() << "Mapping from nodes of type " << node
->nodeType()
469 << " not supported!" << endl
;
473 /** Maps a caret position to the corresponding DOM Range position.
475 * @param r render object
476 * @param r_ofs offset within render object
477 * @param outside true when offset is interpreted to be on the outside of
478 * \c r, or false if on the inside.
479 * @param outsideEnd true when the caret position is at the outside end.
480 * @param node returns DOM node
481 * @param offset returns zero-based offset within node
483 void /*KDE_NO_EXPORT*/ mapRenderPosToDOMPos(RenderObject
*r
, long r_ofs
,
484 bool outside
, bool outsideEnd
, NodeImpl
*&node
, long &offset
)
488 #if DEBUG_CARETMODE > 5
489 kDebug(6200) << "mapRTD: r " << r
<< '@' << (r
? r
->renderName() : QString()) << (r
&& r
->element() ? QString(".node ") + QString::number((unsigned)r
->element(),16) + '@' + r
->element()->nodeName().string() : QString()) << " outside " << outside
<< " outsideEnd " << outsideEnd
;
491 if (node
->nodeType() == Node::ELEMENT_NODE
|| node
->nodeType() == Node::TEXT_NODE
) {
494 NodeImpl
*parent
= node
->parent();
496 // If this is part of a continuation, use the actual node as the parent,
497 // and the first render child as the node.
498 if (r
!= node
->renderer()) {
499 RenderObject
*o
= node
->renderer();
500 while (o
->continuation() && o
->continuation() != r
)
501 o
= o
->continuation();
502 if (o
->continuation() == r
) {
504 // ### What if the first render child does not map to a child of
505 // the continued node?
506 node
= r
->firstChild() ? r
->firstChild()->element() : node
;
510 if (!parent
) goto inside
;
512 offset
= (long)node
->nodeIndex() + outsideEnd
;
514 #if DEBUG_CARETMODE > 5
515 kDebug(6200) << node
<< "@" << (node
? node
->nodeName().string() : QString()) << " offset " << offset
;
524 kWarning() << "Mapping to nodes of type " << node
->nodeType()
525 << " not supported!" << endl
;
529 /** Make sure the given node is a leaf node. */
530 static inline void ensureLeafNode(NodeImpl
*&node
, NodeImpl
*base
)
532 if (node
&& node
->hasChildNodes()) node
= nextLeafNode(node
, base
);
535 /** Converts a caret position to its respective object traversal state.
536 * @param outside whether the caret is outside the object
537 * @param atEnd whether the caret position is at the end
538 * @param toBegin \c true when advancing towards the beginning
539 * @param trav returns the corresponding traversal state
541 static inline void mapRenderPosToTraversalState(bool outside
, bool atEnd
,
542 bool toBegin
, ObjectTraversalState
&trav
)
544 if (!outside
) atEnd
= !toBegin
;
545 if (!atEnd
^ toBegin
)
546 trav
= outside
? OutsideDescending
: InsideDescending
;
548 trav
= outside
? OutsideAscending
: InsideAscending
;
551 /** Converts a traversal state to its respective caret position
552 * @param trav object traversal state
553 * @param toBegin \c true when advancing towards the beginning
554 * @param outside whether the caret is outside the object
555 * @param atEnd whether the caret position is at the end
557 static inline void mapTraversalStateToRenderPos(ObjectTraversalState trav
,
558 bool toBegin
, bool &outside
, bool &atEnd
)
562 case OutsideDescending
: outside
= true; // fall through
563 case InsideDescending
: atEnd
= toBegin
; break;
564 case OutsideAscending
: outside
= true; // fall through
565 case InsideAscending
: atEnd
= !toBegin
; break;
569 /** Finds the next node that has a renderer.
571 * Note that if the initial @p node has a renderer, this will be returned,
572 * regardless of the caret advance policy.
573 * Otherwise, for the next nodes, only leaf nodes are considered.
574 * @param node node to start with, will be updated accordingly
575 * @param offset offset of caret within \c node
576 * @param base base render object which this method must not advance beyond
578 * @param r_ofs return the caret offset within the returned renderer
579 * @param outside returns whether offset is to be interpreted to the outside
580 * (true) or the inside (false) of the render object.
581 * @param outsideEnd returns whether the end of the outside position is meant
582 * @return renderer or 0 if no following node has a renderer.
584 static RenderObject
* findRenderer(NodeImpl
*&node
, long offset
,
585 RenderObject
*base
, long &r_ofs
,
586 bool &outside
, bool &outsideEnd
)
590 mapDOMPosToRenderPos(node
, offset
, r
, r_ofs
, outside
, outsideEnd
);
591 #if DEBUG_CARETMODE > 2
592 kDebug(6200) << "findRenderer: node " << node
<< " " << (node
? node
->nodeName().string() : QString()) << " offset " << offset
<< " r " << r
<< "[" << (r
? r
->renderName() : QString()) << "] r_ofs " << r_ofs
<< " outside " << outside
<< " outsideEnd " << outsideEnd
;
595 NodeImpl
*baseElem
= base
? base
->element() : 0;
597 node
= nextLeafNode(node
, baseElem
);
599 r
= node
->renderer();
600 if (r
) r_ofs
= offset
;
602 #if DEBUG_CARETMODE > 3
603 kDebug(6200) << "1r " << r
;
605 ObjectTraversalState trav
;
606 int state
; // not used
607 mapRenderPosToTraversalState(outside
, outsideEnd
, false, trav
);
608 if (r
&& isUnsuitable(r
, trav
)) {
609 r
= advanceSuitableObject(r
, trav
, false, base
, state
);
610 mapTraversalStateToRenderPos(trav
, false, outside
, outsideEnd
);
611 if (r
) r_ofs
= r
->minOffset();
613 #if DEBUG_CARETMODE > 3
614 kDebug(6200) << "2r " << r
;
619 /** returns a suitable base element
620 * @param caretNode current node containing caret.
622 static ElementImpl
*determineBaseElement(NodeImpl
*caretNode
)
624 // ### for now, only body is delivered for html documents,
625 // and 0 for xml documents.
627 DocumentImpl
*doc
= caretNode
->getDocument();
628 if (!doc
) return 0; // should not happen, but who knows.
630 if (doc
->isHTMLDocument())
631 return static_cast<HTMLDocumentImpl
*>(doc
)->body();
636 // == class CaretBox implementation
638 #if DEBUG_CARETMODE > 0
639 void CaretBox::dump(QTextStream
&ts
, const QString
&ind
) const
641 ts
<< ind
<< "b@" << _box
;
644 ts
<< "<" << _box
->object() << ":" << _box
->object()->renderName() << ">";
647 ts
<< " " << _x
<< "+" << _y
<< "+" << _w
<< "*" << _h
;
650 if (cb
) ts
<< ":" << cb
->renderName();
652 ts
<< " " << (_outside
? (outside_end
? "oe" : "o-") : "i-");
657 // == class CaretBoxLine implementation
659 #if DEBUG_CARETMODE > 0
660 # define DEBUG_ACIB 1
662 # define DEBUG_ACIB DEBUG_CARETMODE
664 void CaretBoxLine::addConvertedInlineBox(InlineBox
*box
, SeekBoxParams
&sbp
) /*KDE_NO_EXPORT*/
666 // Generate only one outside caret box between two elements. If
667 // coalesceOutsideBoxes is true, generating left outside boxes is inhibited.
668 bool coalesceOutsideBoxes
= false;
669 CaretBoxIterator lastCoalescedBox
;
670 for (; box
; box
= box
->nextOnLine()) {
672 kDebug(6200) << "box " << box
;
673 kDebug(6200) << "box->object " << box
->object();
674 kDebug(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();
676 // ### Why the hell can object() ever be 0?!
677 if (!box
->object()) continue;
679 RenderStyle
*s
= box
->object()->style(box
->m_firstLine
);
680 // parent style for outside caret boxes
681 RenderStyle
*ps
= box
->parent() && box
->parent()->object()
682 ? box
->parent()->object()->style(box
->parent()->m_firstLine
)
685 if (box
->isInlineFlowBox()) {
687 kDebug(6200) << "isinlineflowbox " << box
;
689 InlineFlowBox
*flowBox
= static_cast<InlineFlowBox
*>(box
);
690 bool rtl
= ps
->direction() == RTL
;
691 const QFontMetrics
&pfm
= ps
->fontMetrics();
693 if (flowBox
->includeLeftEdge()) {
694 // If this box is to be coalesced with the outside end box of its
695 // predecessor, then check if it is the searched box. If it is, we
696 // substitute the outside end box.
697 if (coalesceOutsideBoxes
) {
698 if (sbp
.equalsBox(flowBox
, true, false)) {
699 sbp
.it
= lastCoalescedBox
;
700 Q_ASSERT(!sbp
.found
);
704 addCreatedFlowBoxEdge(flowBox
, pfm
, true, rtl
);
709 if (flowBox
->firstChild()) {
711 kDebug(6200) << "this " << this << " flowBox " << flowBox
<< " firstChild " << flowBox
->firstChild();
712 kDebug(6200) << "== recursive invocation";
714 addConvertedInlineBox(flowBox
->firstChild(), sbp
);
716 kDebug(6200) << "== recursive invocation end";
720 addCreatedFlowBoxInside(flowBox
, s
->fontMetrics());
724 if (flowBox
->includeRightEdge()) {
725 addCreatedFlowBoxEdge(flowBox
, pfm
, false, rtl
);
726 lastCoalescedBox
= preEnd();
727 sbp
.check(lastCoalescedBox
);
728 coalesceOutsideBoxes
= true;
731 } else if (box
->isInlineTextBox()) {
733 kDebug(6200) << "isinlinetextbox " << box
<< (box
->object() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText
*>(box
->object())->str
->s
+box
->minOffset(), qMin(box
->maxOffset() - box
->minOffset(), 15L)).string()) : QString());
735 caret_boxes
.append(new CaretBox(box
, false, false));
737 // coalescing has been interrupted
738 coalesceOutsideBoxes
= false;
742 kDebug(6200) << "some replaced or what " << box
;
744 // must be an inline-block, inline-table, or any RenderReplaced
745 bool rtl
= ps
->direction() == RTL
;
746 const QFontMetrics
&pfm
= ps
->fontMetrics();
748 if (coalesceOutsideBoxes
) {
749 if (sbp
.equalsBox(box
, true, false)) {
750 sbp
.it
= lastCoalescedBox
;
751 Q_ASSERT(!sbp
.found
);
755 addCreatedInlineBoxEdge(box
, pfm
, true, rtl
);
759 caret_boxes
.append(new CaretBox(box
, false, false));
762 addCreatedInlineBoxEdge(box
, pfm
, false, rtl
);
763 lastCoalescedBox
= preEnd();
764 sbp
.check(lastCoalescedBox
);
765 coalesceOutsideBoxes
= true;
771 void CaretBoxLine::addCreatedFlowBoxInside(InlineFlowBox
*flowBox
, const QFontMetrics
&fm
) /*KDE_NO_EXPORT*/
774 CaretBox
*caretBox
= new CaretBox(flowBox
, false, false);
775 caret_boxes
.append(caretBox
);
777 // afaik an inner flow box can only have the width 0, therefore we don't
778 // have to care for rtl or alignment
779 // ### can empty inline elements have a width? css 2 spec isn't verbose about it
781 caretBox
->_y
+= flowBox
->baseline() - fm
.ascent();
782 caretBox
->_h
= fm
.height();
785 void CaretBoxLine::addCreatedFlowBoxEdge(InlineFlowBox
*flowBox
, const QFontMetrics
&fm
, bool left
, bool rtl
) /*KDE_NO_EXPORT*/
787 CaretBox
*caretBox
= new CaretBox(flowBox
, true, !left
);
788 caret_boxes
.append(caretBox
);
790 if (left
^ rtl
) caretBox
->_x
-= flowBox
->paddingLeft() + flowBox
->borderLeft() + 1;
791 else caretBox
->_x
+= caretBox
->_w
+ flowBox
->paddingRight() + flowBox
->borderRight();
793 caretBox
->_y
+= flowBox
->baseline() - fm
.ascent();
794 caretBox
->_h
= fm
.height();
798 void CaretBoxLine::addCreatedInlineBoxEdge(InlineBox
*box
, const QFontMetrics
&fm
, bool left
, bool rtl
) /*KDE_NO_EXPORT*/
800 CaretBox
*caretBox
= new CaretBox(box
, true, !left
);
801 caret_boxes
.append(caretBox
);
803 if (left
^ rtl
) caretBox
->_x
--;
804 else caretBox
->_x
+= caretBox
->_w
;
806 caretBox
->_y
+= box
->baseline() - fm
.ascent();
807 caretBox
->_h
= fm
.height();
811 CaretBoxLine
*CaretBoxLine::constructCaretBoxLine(CaretBoxLineDeleter
*deleter
,
812 InlineFlowBox
*basicFlowBox
, InlineBox
*seekBox
, bool seekOutside
,
813 bool seekOutsideEnd
, CaretBoxIterator
&iter
, RenderObject
*seekObject
)
816 // Iterate all inline boxes within this inline flow box.
817 // Caret boxes will be created for each
818 // - outside begin of an inline flow box (except for the basic inline flow box)
819 // - outside end of an inline flow box (except for the basic inline flow box)
820 // - inside of an empty inline flow box
821 // - outside begin of an inline box resembling a replaced element
822 // - outside end of an inline box resembling a replaced element
824 // - inline replaced box
826 CaretBoxLine
*result
= new CaretBoxLine(basicFlowBox
);
827 deleter
->append(result
);
829 SeekBoxParams
sbp(seekBox
, seekOutside
, seekOutsideEnd
, seekObject
, iter
);
831 // iterate recursively, I'm too lazy to do it iteratively
832 result
->addConvertedInlineBox(basicFlowBox
, sbp
);
834 if (!sbp
.found
) sbp
.it
= result
->end();
839 CaretBoxLine
*CaretBoxLine::constructCaretBoxLine(CaretBoxLineDeleter
*deleter
,
840 RenderBox
*cb
, bool outside
, bool outsideEnd
, CaretBoxIterator
&iter
) /*KDE_NO_EXPORT*/
845 int width
= 1; // no override is indicated in boxes
849 RenderStyle
*s
= cb
->element() && cb
->element()->parent()
850 && cb
->element()->parent()->renderer()
851 ? cb
->element()->parent()->renderer()->style()
853 bool rtl
= s
->direction() == RTL
;
855 const QFontMetrics
&fm
= s
->fontMetrics();
856 height
= fm
.height();
864 int hl
= fm
.leading() / 2;
865 int baseline
= cb
->baselinePosition(false);
866 if (!cb
->isReplaced() || cb
->style()->display() == BLOCK
) {
867 if (!outsideEnd
^ rtl
)
868 _y
-= fm
.leading() / 2;
870 _y
+= qMax(cb
->height() - fm
.ascent() - hl
, 0);
872 _y
+= baseline
- fm
.ascent() - hl
;
877 RenderStyle
*s
= cb
->style();
878 const QFontMetrics
&fm
= s
->fontMetrics();
879 height
= fm
.height();
881 _x
+= cb
->borderLeft() + cb
->paddingLeft();
882 _y
+= cb
->borderTop() + cb
->paddingTop();
884 // ### regard direction
885 switch (s
->textAlign()) {
888 case TAAUTO
: // ### find out what this does
893 _x
+= cb
->contentWidth() / 2;
897 _x
+= cb
->contentWidth();
902 CaretBoxLine
*result
= new CaretBoxLine
;
903 deleter
->append(result
);
904 result
->caret_boxes
.append(new CaretBox(_x
, _y
, width
, height
, cb
,
905 outside
, outsideEnd
));
906 iter
= result
->begin();
910 #if DEBUG_CARETMODE > 0
911 void CaretBoxLine::dump(QTextStream
&ts
, const QString
&ind
) const
913 ts
<< ind
<< "cbl: baseFlowBox@" << basefb
<< endl
;
914 QString ind2
= ind
+ " ";
915 for (size_t i
= 0; i
< caret_boxes
.size(); i
++) {
916 if (i
> 0) ts
<< endl
;
917 caret_boxes
[i
]->dump(ts
, ind2
);
922 // == caret mode related helper functions
924 /** seeks the root line box that is the parent of the given inline box.
925 * @param b given inline box
926 * @param base base render object which not to step over. If \c base's
927 * inline flow box is hit before the root line box, the flow box
928 * is returned instead.
929 * @return the root line box or the base flow box.
931 inline InlineFlowBox
*seekBaseFlowBox(InlineBox
*b
, RenderObject
*base
= 0)
933 // Seek root line box or base inline flow box, if \c base is interfering.
934 while (b
->parent() && b
->object() != base
) {
937 Q_ASSERT(b
->isInlineFlowBox());
938 return static_cast<InlineFlowBox
*>(b
);
941 /** determines whether the given element is a block level replaced element.
943 inline bool isBlockRenderReplaced(RenderObject
*r
)
945 return r
->isRenderReplaced() && r
->style()->display() == BLOCK
;
948 /** determines the caret line box that contains the given position.
950 * If the node does not map to a render object, the function will snap to
951 * the next suitable render object following it.
953 * @param node node to begin with
954 * @param offset zero-based offset within node.
955 * @param cblDeleter deleter for caret box lines
956 * @param base base render object which the caret must not be placed beyond.
957 * @param r_ofs adjusted offset within render object
958 * @param caretBoxIt returns an iterator to the caret box that contains the
960 * @return the determined caret box lineor 0 if either the node is 0 or
961 * there is no inline flow box containing this node. The containing block
962 * will still be set. If it is 0 too, @p node was invalid.
964 static CaretBoxLine
* findCaretBoxLine(DOM::NodeImpl
*node
, long offset
,
965 CaretBoxLineDeleter
*cblDeleter
, RenderObject
*base
,
966 long &r_ofs
, CaretBoxIterator
&caretBoxIt
)
968 bool outside
, outsideEnd
;
969 RenderObject
*r
= findRenderer(node
, offset
, base
, r_ofs
, outside
, outsideEnd
);
970 if (!r
) { return 0; }
971 #if DEBUG_CARETMODE > 0
972 kDebug(6200) << "=================== findCaretBoxLine";
973 kDebug(6200) << "node " << node
<< " offset: " << offset
<< " r " << r
->renderName() << "[" << r
<< "].node " << r
->element()->nodeName().string() << "[" << r
->element() << "]" << " r_ofs " << r_ofs
<< " outside " << outside
<< " outsideEnd " << outsideEnd
;
976 // There are two strategies to find the correct line box. (The third is failsafe)
977 // (A) First, if node's renderer is a RenderText, we only traverse its text
978 // runs and return the root line box (saves much time for long blocks).
979 // This should be the case 99% of the time.
980 // (B) Second, we derive the inline flow box directly when the renderer is
981 // a RenderBlock, RenderInline, or blocked RenderReplaced.
982 // (C) Otherwise, we iterate linearly through all line boxes in order to find
986 if (r
->isText()) do {
987 RenderText
*t
= static_cast<RenderText
*>(r
);
989 InlineBox
*b
= t
->findInlineTextBox(offset
, dummy
, true);
990 // Actually b should never be 0, but some render texts don't have text
991 // boxes, so we insert the last run as an error correction.
992 // If there is no last run, we resort to (B)
994 if (!t
->lastTextBox())
996 b
= t
->lastTextBox();
999 outside
= false; // text boxes cannot have outside positions
1000 InlineFlowBox
*baseFlowBox
= seekBaseFlowBox(b
, base
);
1001 #if DEBUG_CARETMODE > 2
1002 kDebug(6200) << "text-box b: " << b
<< " baseFlowBox: " << baseFlowBox
<< (b
&& b
->object() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText
*>(b
->object())->str
->s
+b
->minOffset(), qMin(b
->maxOffset() - b
->minOffset(), 15L)).string()) : QString());
1005 if (t
->containingBlock()->isListItem()) dumpLineBoxes(static_cast<RenderFlow
*>(t
->containingBlock()));
1007 #if DEBUG_CARETMODE > 0
1008 kDebug(6200) << "=================== end findCaretBoxLine (renderText)";
1010 return CaretBoxLine::constructCaretBoxLine(cblDeleter
, baseFlowBox
,
1011 b
, outside
, outsideEnd
, caretBoxIt
);
1012 } while(false);/*end if*/
1015 bool isrepl
= isBlockRenderReplaced(r
);
1016 if (r
->isRenderBlock() || r
->isRenderInline() || isrepl
) {
1017 RenderFlow
*flow
= static_cast<RenderFlow
*>(r
);
1018 InlineFlowBox
*firstLineBox
= isrepl
? 0 : flow
->firstLineBox();
1020 // On render blocks, if we are outside, or have a totally empty render
1021 // block, we simply construct a special caret box line.
1022 // The latter case happens only when the render block is a leaf object itself.
1023 if (isrepl
|| r
->isRenderBlock() && (outside
|| !firstLineBox
)
1024 || r
->isRenderInline() && !firstLineBox
) {
1025 #if DEBUG_CARETMODE > 0
1026 kDebug(6200) << "=================== end findCaretBoxLine (box " << (outside
? (outsideEnd
? "outside end" : "outside begin") : "inside") << ")";
1028 Q_ASSERT(r
->isBox());
1029 return CaretBoxLine::constructCaretBoxLine(cblDeleter
,
1030 static_cast<RenderBox
*>(r
), outside
, outsideEnd
, caretBoxIt
);
1033 kDebug(6200) << "firstlinebox " << firstLineBox
;
1034 InlineFlowBox
*baseFlowBox
= seekBaseFlowBox(firstLineBox
, base
);
1035 return CaretBoxLine::constructCaretBoxLine(cblDeleter
, baseFlowBox
,
1036 firstLineBox
, outside
, outsideEnd
, caretBoxIt
);
1039 RenderBlock
*cb
= r
->containingBlock();
1040 //if ( !cb ) return 0L;
1043 // ### which element doesn't have a block as its containing block?
1044 // Is it still possible after the RenderBlock/RenderInline merge?
1045 if (!cb
->isRenderBlock()) {
1046 kWarning() << "containing block is no render block!!! crash imminent";
1049 InlineFlowBox
*flowBox
= cb
->firstLineBox();
1051 // This case strikes when the element is replaced, but neither a
1052 // RenderBlock nor a RenderInline
1053 if (!flowBox
) { // ### utter emergency (why is this possible at all?)
1054 // flowBox = generateDummyFlowBox(arena, cb, r);
1055 // if (ibox) *ibox = flowBox->firstChild();
1056 // outside = outside_end = true;
1058 // kWarning() << "containing block contains no inline flow boxes!!! crash imminent";
1059 #if DEBUG_CARETMODE > 0
1060 kDebug(6200) << "=================== end findCaretBoxLine (2)";
1062 return CaretBoxLine::constructCaretBoxLine(cblDeleter
, cb
,
1063 outside
, outsideEnd
, caretBoxIt
);
1066 // We iterate the inline flow boxes of the containing block until
1067 // we find the given node. This has one major flaw: it is linear, and therefore
1068 // painfully slow for really large blocks.
1069 for (; flowBox
; flowBox
= static_cast<InlineFlowBox
*>(flowBox
->nextLineBox())) {
1070 #if DEBUG_CARETMODE > 0
1071 kDebug(6200) << "[scan line]";
1074 // construct a caret line box and stop when the element is contained within
1075 InlineFlowBox
*baseFlowBox
= seekBaseFlowBox(flowBox
, base
);
1076 CaretBoxLine
*cbl
= CaretBoxLine::constructCaretBoxLine(cblDeleter
,
1077 baseFlowBox
, 0, outside
, outsideEnd
, caretBoxIt
, r
);
1078 #if DEBUG_CARETMODE > 5
1079 kDebug(6200) << cbl
->information();
1081 if (caretBoxIt
!= cbl
->end()) {
1082 #if DEBUG_CARETMODE > 0
1083 kDebug(6200) << "=================== end findCaretBoxLine (3)";
1089 // no inline flow box found, approximate to nearest following node.
1090 // Danger: this is O(n^2). It's only called to recover from
1091 // errors, that means, theoretically, never. (Practically, far too often :-( )
1093 CaretBoxLine
*cbl
= findCaretBoxLine(nextLeafNode(node
, base
? base
->element() : 0), 0, cblDeleter
, base
, r_ofs
, caretBoxIt
);
1094 #if DEBUG_CARETMODE > 0
1095 kDebug(6200) << "=================== end findCaretBoxLine";
1100 /** finds the innermost table object @p r is contained within, but no
1101 * farther than @p cb.
1102 * @param r leaf element to begin with
1103 * @param cb bottom element where to stop search at least.
1104 * @return the table object or 0 if none found.
1106 static inline RenderTable
*findTableUpTo(RenderObject
*r
, RenderFlow
*cb
)
1108 while (r
&& r
!= cb
&& !r
->isTable()) r
= r
->parent();
1109 return r
&& r
->isTable() ? static_cast<RenderTable
*>(r
) : 0;
1112 /** checks whether @p r is a descendant of @p cb, or r == cb
1114 static inline bool isDescendant(RenderObject
*r
, RenderObject
*cb
)
1116 while (r
&& r
!= cb
) r
= r
->parent();
1120 /** checks whether the given block contains at least one editable element.
1122 * Warning: This function has linear complexity, and therefore is expensive.
1123 * Use it sparingly, and cache the result.
1125 * @param cb block to be searched
1126 * @param table returns the nested table if there is one directly at the beginning
1128 * @param fromEnd begin search from end (default: begin from beginning)
1130 static bool containsEditableElement(KHTMLPart
*part
, RenderBlock
*cb
,
1131 RenderTable
*&table
, bool fromEnd
= false)
1133 RenderObject
*r
= cb
;
1135 while (r
->lastChild()) r
= r
->lastChild();
1137 while (r
->firstChild()) r
= r
->firstChild();
1139 RenderTable
*tempTable
= 0;
1142 // int state; // not used
1143 ObjectTraversalState trav
= InsideDescending
;
1145 bool modWithinCb
= withinCb
= isDescendant(r
, cb
);
1147 // treat cb extra, it would not be considered otherwise
1152 tempTable
= findTableUpTo(r
, cb
);
1154 #if DEBUG_CARETMODE > 1
1155 kDebug(6201) << "cee: r " << (r
? r
->renderName() : QString()) << "@" << r
<< " cb " << cb
<< " withinCb " << withinCb
<< " modWithinCb " << modWithinCb
<< " tempTable " << tempTable
;
1157 if (r
&& modWithinCb
&& r
->element() && !isUnsuitable(r
, trav
)
1158 && (part
->isCaretMode() || part
->isEditable()
1159 || r
->style()->userInput() == UI_ENABLED
)) {
1161 #if DEBUG_CARETMODE > 1
1162 kDebug(6201) << "cee: editable";
1167 // RenderObject *oldr = r;
1168 // while (r && r == oldr)
1169 // r = advanceSuitableObject(r, trav, fromEnd, cb->parent(), state);
1170 r
= fromEnd
? r
->objectAbove() : r
->objectBelow();
1171 } while (r
&& withinCb
);
1175 /** checks whether the given block contains at least one editable child
1176 * element, beginning with but excluding @p start.
1178 * Warning: This function has linear complexity, and therefore is expensive.
1179 * Use it sparingly, and cache the result.
1181 * @param cb block to be searched
1182 * @param table returns the nested table if there is one directly before/after
1184 * @param fromEnd begin search from end (default: begin from beginning)
1185 * @param start object after which to begin search.
1187 static bool containsEditableChildElement(KHTMLPart
*part
, RenderBlock
*cb
,
1188 RenderTable
*&table
, bool fromEnd
, RenderObject
*start
)
1191 ObjectTraversalState trav
= OutsideAscending
;
1192 // kDebug(6201) << "start: " << start;
1193 RenderObject
*r
= start
;
1195 r
= traverseRenderObjects(r
, trav
, fromEnd
, cb
->parent(), state
);
1196 } while(r
&& !(state
& AdvancedToSibling
));
1197 // kDebug(6201) << "r: " << r;
1198 //advanceObject(start, trav, fromEnd, cb->parent(), state);
1199 // RenderObject *oldr = r;
1200 // while (r && r == oldr)
1201 if (!r
) return false;
1204 while (r
->firstChild()) r
= r
->firstChild();
1206 while (r
->lastChild()) r
= r
->lastChild();
1207 // kDebug(6201) << "child r: " << r;
1208 if (!r
) return false;
1210 RenderTable
*tempTable
= 0;
1212 bool withinCb
= false;
1215 bool modWithinCb
= withinCb
= isDescendant(r
, cb
);
1217 // treat cb extra, it would not be considered otherwise
1222 tempTable
= findTableUpTo(r
, cb
);
1224 #if DEBUG_CARETMODE > 1
1225 kDebug(6201) << "cece: r " << (r
? r
->renderName() : QString()) << "@" << r
<< " cb " << cb
<< " withinCb " << withinCb
<< " modWithinCb " << modWithinCb
<< " tempTable " << tempTable
;
1227 if (r
&& withinCb
&& r
->element() && !isUnsuitable(r
, trav
)
1228 && (part
->isCaretMode() || part
->isEditable()
1229 || r
->style()->userInput() == UI_ENABLED
)) {
1231 #if DEBUG_CARETMODE > 1
1232 kDebug(6201) << "cece: editable";
1237 r
= fromEnd
? r
->objectAbove() : r
->objectBelow();
1242 // == class LinearDocument implementation
1244 LinearDocument::LinearDocument(KHTMLPart
*part
, NodeImpl
*node
, long offset
,
1245 CaretAdvancePolicy advancePolicy
, ElementImpl
*baseElem
)
1246 : node(node
), offset(offset
), m_part(part
),
1247 advPol(advancePolicy
), base(0)
1249 if (node
== 0) return;
1252 RenderObject
*b
= baseElem
->renderer();
1253 if (b
&& (b
->isRenderBlock() || b
->isRenderInline()))
1257 initPreBeginIterator();
1261 LinearDocument::~LinearDocument()
1265 int LinearDocument::count() const
1267 // FIXME: not implemented
1271 LinearDocument::Iterator
LinearDocument::current()
1273 return LineIterator(this, node
, offset
);
1276 LinearDocument::Iterator
LinearDocument::begin()
1278 NodeImpl
*n
= base
? base
->element() : 0;
1279 if (!base
) n
= node
? node
->getDocument() : 0;
1280 if (!n
) return end();
1282 n
= n
->firstChild();
1283 if (advPol
== LeafsOnly
)
1284 while (n
->firstChild()) n
= n
->firstChild();
1286 if (!n
) return end(); // must be empty document or empty base element
1287 return LineIterator(this, n
, n
->minOffset());
1290 LinearDocument::Iterator
LinearDocument::preEnd()
1292 NodeImpl
*n
= base
? base
->element() : 0;
1293 if (!base
) n
= node
? node
->getDocument() : 0;
1294 if (!n
) return preBegin();
1297 if (advPol
== LeafsOnly
)
1298 while (n
->lastChild()) n
= n
->lastChild();
1300 if (!n
) return preBegin(); // must be empty document or empty base element
1301 return LineIterator(this, n
, n
->maxOffset());
1304 void LinearDocument::initPreBeginIterator()
1306 _preBegin
= LineIterator(this, 0, 0);
1309 void LinearDocument::initEndIterator()
1311 _end
= LineIterator(this, 0, 1);
1314 // == class LineIterator implementation
1316 CaretBoxIterator
LineIterator::currentBox
/*KDE_NO_EXPORT*/;
1317 long LineIterator::currentOffset
/*KDE_NO_EXPORT*/;
1319 LineIterator::LineIterator(LinearDocument
*l
, DOM::NodeImpl
*node
, long offset
)
1322 // kDebug(6200) << "LineIterator: node " << node << " offset " << offset;
1323 if (!node
) { cbl
= 0; return; }
1324 cbl
= findCaretBoxLine(node
, offset
, &lines
->cblDeleter
,
1325 l
->baseObject(), currentOffset
, currentBox
);
1326 // can happen on partially loaded documents
1327 #if DEBUG_CARETMODE > 0
1328 if (!cbl
) kDebug(6200) << "no render object found!";
1331 #if DEBUG_CARETMODE > 1
1332 kDebug(6200) << "LineIterator: offset " << offset
<< " outside " << cbl
->isOutside();
1334 #if DEBUG_CARETMODE > 3
1335 kDebug(6200) << cbl
->information();
1337 if (currentBox
== cbl
->end()) {
1338 #if DEBUG_CARETMODE > 0
1339 kDebug(6200) << "LineIterator: findCaretBoxLine failed";
1345 void LineIterator::nextBlock()
1347 RenderObject
*base
= lines
->baseObject();
1349 bool cb_outside
= cbl
->isOutside();
1350 bool cb_outside_end
= cbl
->isOutsideEnd();
1353 RenderObject
*r
= cbl
->enclosingObject();
1355 ObjectTraversalState trav
;
1356 int state
; // not used
1357 mapRenderPosToTraversalState(cb_outside
, cb_outside_end
, false, trav
);
1358 #if DEBUG_CARETMODE > 1
1359 kDebug(6200) << "nextBlock: before adv r" << r
<< ' ' << (r
? r
->renderName() : QString()) << (r
&& r
->isText() ? " contains \"" + QString(((RenderText
*)r
)->str
->s
, qMin(((RenderText
*)r
)->str
->l
,15)) + "\"" : QString()) << " trav " << trav
<< " cb_outside " << cb_outside
<< " cb_outside_end " << cb_outside_end
;
1361 r
= advanceSuitableObject(r
, trav
, false, base
, state
);
1367 mapTraversalStateToRenderPos(trav
, false, cb_outside
, cb_outside_end
);
1368 #if DEBUG_CARETMODE > 1
1369 kDebug(6200) << "nextBlock: after r" << r
<< " trav " << trav
<< " cb_outside " << cb_outside
<< " cb_outside_end " << cb_outside_end
;
1371 #if DEBUG_CARETMODE > 0
1372 kDebug(6200) << "++: r " << r
<< "[" << (r
?r
->renderName():QString()) << "]";
1377 // If we hit a block or replaced object, use this as its enclosing object
1378 bool isrepl
= isBlockRenderReplaced(r
);
1379 if (r
->isRenderBlock() || isrepl
) {
1380 RenderBox
*cb
= static_cast<RenderBox
*>(r
);
1382 cbl
= CaretBoxLine::constructCaretBoxLine(&lines
->cblDeleter
, cb
,
1383 cb_outside
, cb_outside_end
, currentBox
);
1385 #if DEBUG_CARETMODE > 0
1386 kDebug(6200) << "r->isFlow is cb. continuation @" << cb
->continuation();
1390 cb
= r
->containingBlock();
1391 Q_ASSERT(cb
->isRenderBlock());
1393 InlineFlowBox
*flowBox
= cb
->firstLineBox();
1394 #if DEBUG_CARETMODE > 0
1395 kDebug(6200) << "++: flowBox " << flowBox
<< " cb " << cb
<< '[' << (cb
?cb
->renderName()+QString(".node ")+QString::number((unsigned)cb
->element(),16)+(cb
->element()?'@'+cb
->element()->nodeName().string():QString()):QString()) << ']';
1398 if (!flowBox
) { // ### utter emergency (why is this possible at all?)
1399 cb_outside
= cb_outside_end
= true;
1400 cbl
= CaretBoxLine::constructCaretBoxLine(&lines
->cblDeleter
, cb
,
1401 cb_outside
, cb_outside_end
, currentBox
);
1405 bool seekOutside
= false, seekOutsideEnd
= false; // silence gcc uninit warning
1406 CaretBoxIterator it
;
1407 cbl
= CaretBoxLine::constructCaretBoxLine(&lines
->cblDeleter
,
1408 flowBox
, flowBox
->firstChild(), seekOutside
, seekOutsideEnd
, it
);
1412 void LineIterator::prevBlock()
1414 RenderObject
*base
= lines
->baseObject();
1416 bool cb_outside
= cbl
->isOutside();
1417 bool cb_outside_end
= cbl
->isOutsideEnd();
1420 RenderObject
*r
= cbl
->enclosingObject();
1421 if (r
->isAnonymous() && !cb_outside
)
1422 cb_outside
= true, cb_outside_end
= false;
1424 ObjectTraversalState trav
;
1425 int state
; // not used
1426 mapRenderPosToTraversalState(cb_outside
, cb_outside_end
, true, trav
);
1427 #if DEBUG_CARETMODE > 1
1428 kDebug(6200) << "prevBlock: before adv r" << r
<< " " << (r
? r
->renderName() : QString()) << (r
&& r
->isText() ? " contains \"" + QString(((RenderText
*)r
)->str
->s
, qMin(((RenderText
*)r
)->str
->l
,15)) + "\"" : QString()) << " trav " << trav
<< " cb_outside " << cb_outside
<< " cb_outside_end " << cb_outside_end
;
1430 r
= advanceSuitableObject(r
, trav
, true, base
, state
);
1436 mapTraversalStateToRenderPos(trav
, true, cb_outside
, cb_outside_end
);
1437 #if DEBUG_CARETMODE > 1
1438 kDebug(6200) << "prevBlock: after r" << r
<< " trav " << trav
<< " cb_outside " << cb_outside
<< " cb_outside_end " << cb_outside_end
;
1440 #if DEBUG_CARETMODE > 0
1441 kDebug(6200) << "--: r " << r
<< "[" << (r
?r
->renderName():QString()) << "]";
1446 // If we hit a block, use this as its enclosing object
1447 bool isrepl
= isBlockRenderReplaced(r
);
1448 // kDebug(6200) << "isrepl " << isrepl << " isblock " << r->isRenderBlock();
1449 if (r
->isRenderBlock() || isrepl
) {
1450 RenderBox
*cb
= static_cast<RenderBox
*>(r
);
1452 cbl
= CaretBoxLine::constructCaretBoxLine(&lines
->cblDeleter
, cb
,
1453 cb_outside
, cb_outside_end
, currentBox
);
1455 #if DEBUG_CARETMODE > 0
1456 kDebug(6200) << "r->isFlow is cb. continuation @" << cb
->continuation();
1460 cb
= r
->containingBlock();
1461 Q_ASSERT(cb
->isRenderBlock());
1463 InlineFlowBox
*flowBox
= cb
->lastLineBox();
1464 #if DEBUG_CARETMODE > 0
1465 kDebug(6200) << "--: flowBox " << flowBox
<< " cb " << cb
<< "[" << (cb
?cb
->renderName()+QString(".node ")+QString::number((unsigned)cb
->element(),16)+(cb
->element()?"@"+cb
->element()->nodeName().string():QString()):QString()) << "]";
1468 if (!flowBox
) { // ### utter emergency (why is this possible at all?)
1469 cb_outside
= true; cb_outside_end
= false;
1470 cbl
= CaretBoxLine::constructCaretBoxLine(&lines
->cblDeleter
, cb
,
1471 cb_outside
, cb_outside_end
, currentBox
);
1475 bool seekOutside
= false, seekOutsideEnd
= false; // silence gcc uninit warning
1476 CaretBoxIterator it
;
1477 cbl
= CaretBoxLine::constructCaretBoxLine(&lines
->cblDeleter
,
1478 flowBox
, flowBox
->firstChild(), seekOutside
, seekOutsideEnd
, it
);
1482 void LineIterator::advance(bool toBegin
)
1484 InlineFlowBox
*flowBox
= cbl
->baseFlowBox();
1486 flowBox
= static_cast<InlineFlowBox
*>(toBegin
? flowBox
->prevLineBox() : flowBox
->nextLineBox());
1488 bool seekOutside
= false, seekOutsideEnd
= false; // silence gcc uninit warning
1489 CaretBoxIterator it
;
1490 cbl
= CaretBoxLine::constructCaretBoxLine(&lines
->cblDeleter
,
1491 flowBox
, flowBox
->firstChild(), seekOutside
, seekOutsideEnd
, it
);
1495 // if there are no more lines in this block, move towards block to come
1496 if (!flowBox
) { if (toBegin
) prevBlock(); else nextBlock(); }
1498 #if DEBUG_CARETMODE > 3
1499 if (cbl
) kDebug(6200) << cbl
->information();
1503 // == class EditableCaretBoxIterator implementation
1505 void EditableCaretBoxIterator::advance(bool toBegin
)
1507 #if DEBUG_CARETMODE > 3
1508 kDebug(6200) << "---------------" << "toBegin " << toBegin
;
1510 const CaretBoxIterator preBegin
= cbl
->preBegin();
1511 const CaretBoxIterator end
= cbl
->end();
1513 CaretBoxIterator lastbox
= *this, curbox
;
1514 bool islastuseable
= true; // silence gcc
1516 // Assume adjacency of caret boxes. Will be falsified later if applicable.
1519 #if DEBUG_CARETMODE > 4
1520 // kDebug(6200) << "ebit::advance: before: " << (**this)->object() << "@" << (**this)->object()->renderName() << ".node " << (**this)->object()->element() << "[" << ((**this)->object()->element() ? (**this)->object()->element()->nodeName().string() : QString()) << "] inline " << (**this)->isInline() << " outside " << (**this)->isOutside() << " outsideEnd " << (**this)->isOutsideEnd();
1523 if (toBegin
) CaretBoxIterator::operator --(); else CaretBoxIterator::operator ++();
1524 bool curAtEnd
= *this == preBegin
|| *this == end
;
1528 iscuruseable
= isEditable(curbox
, toBegin
);
1529 if (toBegin
) CaretBoxIterator::operator --(); else CaretBoxIterator::operator ++();
1530 atEnd
= *this == preBegin
|| *this == end
;
1533 bool haslast
= lastbox
!= end
&& lastbox
!= preBegin
;
1534 bool hascoming
= !atEnd
;
1535 bool iscominguseable
= true; // silence gcc
1537 if (!atEnd
) iscominguseable
= isEditable(*this, toBegin
);
1539 #if DEBUG_CARETMODE > 3
1540 kDebug(6200) << "ebit::advance: " << (*curbox
)->object() << "@" << (*curbox
)->object()->renderName() << ".node " << (*curbox
)->object()->element() << "[" << ((*curbox
)->object()->element() ? (*curbox
)->object()->element()->nodeName().string() : QString()) << "] inline " << (*curbox
)->isInline() << " outside " << (*curbox
)->isOutside() << " outsideEnd " << (*curbox
)->isOutsideEnd();
1543 CaretBox
*box
= *curbox
;
1544 if (box
->isOutside()) {
1545 // if this caret box represents no inline box, it is an outside box
1546 // which has to be considered unconditionally
1547 if (!box
->isInline()) break;
1549 if (advpol
== VisibleFlows
) break;
1551 // IndicatedFlows and LeafsOnly are treated equally in caret box lines
1553 InlineBox
*ibox
= box
->inlineBox();
1554 // get previous inline box
1555 InlineBox
*prev
= box
->isOutsideEnd() ? ibox
: ibox
->prevOnLine();
1556 // get next inline box
1557 InlineBox
*next
= box
->isOutsideEnd() ? ibox
->nextOnLine() : ibox
;
1559 const bool isprevindicated
= !prev
|| isIndicatedInlineBox(prev
);
1560 const bool isnextindicated
= !next
|| isIndicatedInlineBox(next
);
1561 const bool last
= haslast
&& !islastuseable
;
1562 const bool coming
= hascoming
&& !iscominguseable
;
1563 const bool left
= !prev
|| prev
->isInlineFlowBox() && isprevindicated
1564 || (toBegin
&& coming
|| !toBegin
&& last
);
1565 const bool right
= !next
|| next
->isInlineFlowBox() && isnextindicated
1566 || (!toBegin
&& coming
|| toBegin
&& last
);
1567 const bool text2indicated
= toBegin
&& next
&& next
->isInlineTextBox()
1569 || !toBegin
&& prev
&& prev
->isInlineTextBox() && isnextindicated
;
1570 const bool indicated2text
= !toBegin
&& next
&& next
->isInlineTextBox()
1571 && prev
&& isprevindicated
1572 // ### this code is so broken.
1573 /*|| toBegin && prev && prev->isInlineTextBox() && isnextindicated*/;
1574 #if DEBUG_CARETMODE > 5
1575 kDebug(6200) << "prev " << prev
<< " haslast " << haslast
<< " islastuseable " << islastuseable
<< " left " << left
<< " next " << next
<< " hascoming " << hascoming
<< " iscominguseable " << iscominguseable
<< " right " << right
<< " text2indicated " << text2indicated
<< " indicated2text " << indicated2text
;
1578 if (left
&& right
&& !text2indicated
|| indicated2text
) {
1580 #if DEBUG_CARETMODE > 4
1581 kDebug(6200) << "left && right && !text2indicated || indicated2text";
1587 // inside boxes are *always* valid
1588 #if DEBUG_CARETMODE > 4
1589 if (box
->isInline()) {
1590 InlineBox
*ibox
= box
->inlineBox();
1591 kDebug(6200) << "inside " << (!ibox
->isInlineFlowBox() || static_cast<InlineFlowBox
*>(ibox
)->firstChild() ? "non-empty" : "empty") << (isIndicatedInlineBox(ibox
) ? " indicated" : "") << " adjacent=" << adjacent
;
1594 RenderStyle
*s
= ibox
->object()->style();
1595 kDebug(6200) << "bordls " << s
->borderLeftStyle()
1596 << " bordl " << (s
->borderLeftStyle() != BNONE
)
1597 << " bordr " << (s
->borderRightStyle() != BNONE
)
1598 << " bordt " << (s
->borderTopStyle() != BNONE
)
1599 << " bordb " << (s
->borderBottomStyle() != BNONE
)
1600 << " padl " << s
->paddingLeft().value()
1601 << " padr " << s
->paddingRight().value()
1602 << " padt " << s
->paddingTop().value()
1603 << " padb " << s
->paddingBottom().value()
1604 // ### Can inline elements have top/bottom margins? Couldn't find
1605 // it in the CSS 2 spec, but Mozilla ignores them, so we do, too.
1606 << " marl " << s
->marginLeft().value()
1607 << " marr " << s
->marginRight().value()
1616 if (!(*curbox
)->isOutside()) {
1617 // cannot be adjacent anymore
1623 islastuseable
= iscuruseable
;
1625 iscuruseable
= iscominguseable
;
1628 if (toBegin
) CaretBoxIterator::operator --(); else CaretBoxIterator::operator ++();
1629 atEnd
= *this == preBegin
|| *this == end
;
1633 *static_cast<CaretBoxIterator
*>(this) = curbox
;
1634 #if DEBUG_CARETMODE > 4
1635 // kDebug(6200) << "still valid? " << (*this != preBegin && *this != end);
1637 #if DEBUG_CARETMODE > 3
1638 kDebug(6200) << "---------------" << "end ";
1642 bool EditableCaretBoxIterator::isEditable(const CaretBoxIterator
&boxit
, bool fromEnd
)
1644 Q_ASSERT(boxit
!= cbl
->end() && boxit
!= cbl
->preBegin());
1645 CaretBox
*b
= *boxit
;
1646 RenderObject
*r
= b
->object();
1647 #if DEBUG_CARETMODE > 0
1648 // if (b->isInlineFlowBox()) kDebug(6200) << "b is inline flow box" << (outside ? " (outside)" : "");
1649 kDebug(6200) << "isEditable r" << r
<< ": " << (r
? r
->renderName() : QString()) << (r
&& r
->isText() ? " contains \"" + QString(((RenderText
*)r
)->str
->s
, qMin(((RenderText
*)r
)->str
->l
,15)) + "\"" : QString());
1651 // Must check caret mode or design mode *after* r->element(), otherwise
1652 // lines without a backing DOM node get regarded, leading to a crash.
1653 // ### check should actually be in InlineBoxIterator
1654 NodeImpl
*node
= r
->element();
1655 ObjectTraversalState trav
;
1656 mapRenderPosToTraversalState(b
->isOutside(), b
->isOutsideEnd(), fromEnd
, trav
);
1657 if (isUnsuitable(r
, trav
) || !node
) {
1661 // generally exclude replaced elements with no children from navigation
1662 if (!b
->isOutside() && r
->isRenderReplaced() && !r
->firstChild())
1665 RenderObject
*eff_r
= r
;
1666 bool globallyNavigable
= m_part
->isCaretMode() || m_part
->isEditable();
1668 // calculate the parent element's editability if this inline box is outside.
1669 if (b
->isOutside() && !globallyNavigable
) {
1670 NodeImpl
*par
= node
->parent();
1671 // I wonder whether par can be 0. It shouldn't be possible if the
1672 // algorithm contained no bugs.
1674 if (par
) node
= par
;
1675 eff_r
= node
->renderer();
1676 Q_ASSERT(eff_r
); // this is a hard requirement
1679 bool result
= globallyNavigable
|| eff_r
->style()->userInput() == UI_ENABLED
;
1680 #if DEBUG_CARETMODE > 0
1681 kDebug(6200) << result
;
1686 // == class EditableLineIterator implementation
1688 void EditableLineIterator::advance(bool toBegin
)
1690 CaretAdvancePolicy advpol
= lines
->advancePolicy();
1691 LineIterator lasteditable
, lastindicated
;
1692 bool haslasteditable
= false;
1693 bool haslastindicated
= false;
1694 bool uselasteditable
= false;
1696 LineIterator::advance(toBegin
);
1698 if (isEditable(*this)) {
1699 #if DEBUG_CARETMODE > 3
1700 kDebug(6200) << "advance: " << cbl
->enclosingObject() << "@" << cbl
->enclosingObject()->renderName() << ".node " << cbl
->enclosingObject()->element() << "[" << (cbl
->enclosingObject()->element() ? cbl
->enclosingObject()->element()->nodeName().string() : QString()) << "]";
1703 bool hasindicated
= isIndicatedFlow(cbl
->enclosingObject());
1705 haslastindicated
= true;
1706 lastindicated
= *this;
1710 case IndicatedFlows
:
1711 if (hasindicated
) goto wend
;
1714 if (cbl
->isOutside()) break;
1716 case VisibleFlows
: goto wend
;
1719 // remember rejected editable element
1720 lasteditable
= *this;
1721 haslasteditable
= true;
1722 #if DEBUG_CARETMODE > 4
1723 kDebug(6200) << "remembered lasteditable " << *lasteditable
;
1727 // If this element isn't editable, but the last one was, and it was only
1728 // rejected because it didn't match the caret advance policy, force it.
1729 // Otherwise certain combinations of editable and uneditable elements
1730 // could never be reached with some policies.
1731 if (haslasteditable
) { uselasteditable
= true; break; }
1734 LineIterator::advance(toBegin
);
1738 if (uselasteditable
) *this = haslastindicated
? lastindicated
: lasteditable
;
1739 if (!cbl
&& haslastindicated
) *this = lastindicated
;
1742 // == class EditableCharacterIterator implementation
1744 void EditableCharacterIterator::initFirstChar()
1746 CaretBox
*box
= *ebit
;
1747 InlineBox
*b
= box
->inlineBox();
1748 if (_offset
== box
->maxOffset())
1750 else if (b
&& !box
->isOutside() && b
->isInlineTextBox())
1751 _char
= static_cast<RenderText
*>(b
->object())->str
->s
[_offset
].unicode();
1756 /** returns true when the given caret box is empty, i. e. should not
1757 * take place in caret movement.
1759 static inline bool isCaretBoxEmpty(CaretBox
*box
) {
1760 if (!box
->isInline()) return false;
1761 InlineBox
*ibox
= box
->inlineBox();
1762 return ibox
->isInlineFlowBox()
1763 && !static_cast<InlineFlowBox
*>(ibox
)->firstChild()
1764 && !isIndicatedInlineBox(ibox
);
1767 EditableCharacterIterator
&EditableCharacterIterator::operator ++()
1771 CaretBox
*box
= *ebit
;
1772 InlineBox
*b
= box
->inlineBox();
1773 long maxofs
= box
->maxOffset();
1774 #if DEBUG_CARETMODE > 0
1775 kDebug(6200) << "box->maxOffset() " << box
->maxOffset() << " box->minOffset() " << box
->minOffset();
1777 if (_offset
== maxofs
) {
1778 #if DEBUG_CARETMODE > 2
1779 kDebug(6200) << "_offset == maxofs: " << _offset
<< " == " << maxofs
;
1782 } else if (_offset
> maxofs
) {
1783 #if DEBUG_CARETMODE > 2
1784 kDebug(6200) << "_offset > maxofs: " << _offset
<< " > " << maxofs
/*<< " _peekNext: " << _peekNext*/;
1786 if (/*!_peekNext*/true) {
1788 if (ebit
== (*_it
)->end()) { // end of line reached, go to next line
1790 #if DEBUG_CARETMODE > 3
1791 kDebug(6200) << "++_it";
1793 if (_it
!= _it
.lines
->end()) {
1796 b
= box
->inlineBox();
1797 #if DEBUG_CARETMODE > 3
1798 kDebug(6200) << "box " << box
<< " b " << b
<< " isText " << box
->isInlineTextBox();
1801 #if DEBUG_CARETMODE > 3
1802 RenderObject
*_r
= box
->object();
1803 kDebug(6200) << "_r " << _r
<< ":" << _r
->element()->nodeName().string();
1805 _offset
= box
->minOffset();
1806 #if DEBUG_CARETMODE > 3
1807 kDebug(6200) << "_offset " << _offset
;
1817 bool adjacent
= ebit
.isAdjacent();
1819 // Jump over element if this one is not a text node.
1820 if (adjacent
&& !(*ebit
)->isInlineTextBox()) {
1821 EditableCaretBoxIterator copy
= ebit
;
1823 if (ebit
!= (*_it
)->end() && (*ebit
)->isInlineTextBox()
1824 /*&& (!(*ebit)->isInlineFlowBox()
1825 || static_cast<InlineFlowBox *>(*ebit)->)*/)
1830 // Jump over empty elements.
1831 if (adjacent
&& !(*ebit
)->isInlineTextBox()) {
1832 bool noemptybox
= true;
1833 while (isCaretBoxEmpty(*ebit
)) {
1835 EditableCaretBoxIterator copy
= ebit
;
1837 if (ebit
== (*_it
)->end()) { ebit
= copy
; break; }
1839 if (noemptybox
) adjacent
= false;
1841 // _r = (*ebit)->object();
1842 /*if (!_it.outside) */_offset
= (*ebit
)->minOffset() + adjacent
;
1845 b
= box
->inlineBox();
1850 if (b
&& !box
->isOutside() && b
->isInlineTextBox() && _offset
< b
->maxOffset())
1851 _char
= static_cast<RenderText
*>(b
->object())->str
->s
[_offset
].unicode();
1855 #if DEBUG_CARETMODE > 2
1856 kDebug(6200) << "_offset: " << _offset
/*<< " _peekNext: " << _peekNext*/ << " char '" << (char)_char
<< "'";
1859 #if DEBUG_CARETMODE > 0
1860 if (!_end
&& ebit
!= (*_it
)->end()) {
1861 CaretBox
*box
= *ebit
;
1862 RenderObject
*_r
= box
->object();
1863 kDebug(6200) << "echit++(1): box " << box
<< (box
&& box
->isInlineTextBox() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText
*>(box
->object())->str
->s
+box
->minOffset(), box
->maxOffset() - box
->minOffset()).string()) : QString()) << " _r " << (_r
? _r
->element()->nodeName().string() : QString("<nil>"));
1869 EditableCharacterIterator
&EditableCharacterIterator::operator --()
1872 //kDebug(6200) << "--: _offset=" << _offset;
1874 CaretBox
*box
= *ebit
;
1875 CaretBox
*_peekPrev
= 0;
1876 CaretBox
*_peekNext
= 0;
1877 InlineBox
*b
= box
->inlineBox();
1878 long minofs
= box
->minOffset();
1879 #if DEBUG_CARETMODE > 0
1880 kDebug(6200) << "box->maxOffset() " << box
->maxOffset() << " box->minOffset() " << box
->minOffset();
1882 if (_offset
== minofs
) {
1883 #if DEBUG_CARETMODE > 2
1884 kDebug(6200) << "_offset == minofs: " << _offset
<< " == " << minofs
;
1888 if (b
&& !box
->isOutside() && b
->isInlineTextBox())
1889 _char
= static_cast<RenderText
*>(b
->object())->text()[_offset
].unicode();
1894 bool do_prev
= false;
1896 EditableCaretBoxIterator copy
;
1901 if (ebit
== (*_it
)->preBegin()) { ebit
= copy
; break; }
1902 } while (isCaretBoxEmpty(*ebit
));
1903 // Jump to end of previous element if it's adjacent, and a text box
1904 if (ebit
.isAdjacent() && ebit
!= (*_it
)->preBegin() && (*ebit
)->isInlineTextBox()) {
1910 if (do_prev
) goto prev
;
1911 } else if (_offset
< minofs
) {
1913 #if DEBUG_CARETMODE > 2
1914 kDebug(6200) << "_offset < minofs: " << _offset
<< " < " << minofs
/*<< " _peekNext: " << _peekNext*/;
1919 if (ebit
== (*_it
)->preBegin()) { // end of line reached, go to previous line
1921 #if DEBUG_CARETMODE > 3
1922 kDebug(6200) << "--_it";
1924 if (_it
!= _it
.lines
->preBegin()) {
1925 // kDebug(6200) << "begin from end!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
1926 ebit
= EditableCaretBoxIterator(_it
, true);
1928 // RenderObject *r = box->object();
1929 #if DEBUG_CARETMODE > 3
1930 kDebug(6200) << "box " << box
<< " b " << box
->inlineBox() << " isText " << box
->isInlineTextBox();
1932 _offset
= box
->maxOffset();
1933 // if (!_it.outside) _offset = r->isBR() ? (*ebit)->minOffset() : (*ebit)->maxOffset();
1935 #if DEBUG_CARETMODE > 0
1936 kDebug(6200) << "echit--(2): box " << box
<< " b " << box
->inlineBox() << (box
->isInlineTextBox() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText
*>(box
->object())->str
->s
+box
->minOffset(), box
->maxOffset() - box
->minOffset()).string()) : QString());
1944 #if DEBUG_CARETMODE > 0
1945 bool adjacent
= ebit
.isAdjacent();
1946 kDebug(6200) << "adjacent " << adjacent
<< " _peekNext " << _peekNext
<< " _peekNext->isInlineTextBox: " << (_peekNext
? _peekNext
->isInlineTextBox() : false) << " !((*ebit)->isInlineTextBox): " << (*ebit
? !(*ebit
)->isInlineTextBox() : true);
1949 // Ignore this box if it isn't a text box, but the previous box was
1950 if (adjacent
&& _peekNext
&& _peekNext
->isInlineTextBox()
1951 && !(*ebit
)->isInlineTextBox()) {
1952 EditableCaretBoxIterator copy
= ebit
;
1954 if (ebit
== (*_it
)->preBegin()) /*adjacent = false;
1959 // Jump over empty elements.
1960 if (adjacent
//&& _peekNext && _peekNext->isInlineTextBox()
1961 && !(*ebit
)->isInlineTextBox()) {
1962 bool noemptybox
= true;
1963 while (isCaretBoxEmpty(*ebit
)) {
1965 EditableCaretBoxIterator copy
= ebit
;
1967 if (ebit
== (*_it
)->preBegin()) { ebit
= copy
; break; }
1968 else _peekNext
= *copy
;
1970 if (noemptybox
) adjacent
= false;
1973 #if DEBUG_CARETMODE > 0
1974 kDebug(6200) << "(*ebit)->obj " << (*ebit
)->object()->renderName() << "[" << (*ebit
)->object() << "]" << " minOffset: " << (*ebit
)->minOffset() << " maxOffset: " << (*ebit
)->maxOffset();
1976 #if DEBUG_CARETMODE > 3
1977 RenderObject
*_r
= (*ebit
)->object();
1978 kDebug(6200) << "_r " << _r
<< ":" << _r
->element()->nodeName().string();
1980 _offset
= (*ebit
)->maxOffset();
1981 // if (!_it.outside) _offset = (*ebit)->maxOffset()/* - adjacent*/;
1982 #if DEBUG_CARETMODE > 3
1983 kDebug(6200) << "_offset " << _offset
;
1987 #if DEBUG_CARETMODE > 0
1988 kDebug(6200) << "_offset: " << _offset
<< " _peekNext: " << _peekNext
;
1991 if (_peekNext
&& _offset
>= box
->maxOffset() && _peekNext
->isInlineTextBox())
1992 _char
= static_cast<RenderText
*>(_peekNext
->object())->text()[_peekNext
->minOffset()].unicode();
1993 else if (b
&& _offset
< b
->maxOffset() && b
->isInlineTextBox())
1994 _char
= static_cast<RenderText
*>(b
->object())->text()[_offset
].unicode();
1999 #if DEBUG_CARETMODE > 0
2000 if (!_end
&& ebit
!= (*_it
)->preBegin()) {
2001 CaretBox
*box
= *ebit
;
2002 kDebug(6200) << "echit--(1): box " << box
<< " b " << box
->inlineBox() << (box
->isInlineTextBox() ? QString(" contains \"%1\"").arg(QConstString(static_cast<RenderText
*>(box
->object())->str
->s
+box
->minOffset(), box
->maxOffset() - box
->minOffset()).string()) : QString());
2008 // == class TableRowIterator implementation
2010 TableRowIterator::TableRowIterator(RenderTable
*table
, bool fromEnd
,
2011 RenderTableSection::RowStruct
*row
)
2012 : sec(table
, fromEnd
)
2016 if (fromEnd
) index
= (*sec
)->grid
.size() - 1;
2020 // initialize with given row
2022 while (operator *() != row
)
2023 if (fromEnd
) operator --(); else operator ++();
2027 TableRowIterator
&TableRowIterator::operator ++()
2031 if (index
>= (int)(*sec
)->grid
.size()) {
2034 if (*sec
) index
= 0;
2039 TableRowIterator
&TableRowIterator::operator --()
2046 if (*sec
) index
= (*sec
)->grid
.size() - 1;
2051 // == class ErgonomicEditableLineIterator implementation
2054 static RenderTableCell
*findNearestTableCellInRow(KHTMLPart
*part
, int x
,
2055 RenderTableSection::RowStruct
*row
, bool fromEnd
);
2057 /** finds the cell corresponding to absolute x-coordinate @p x in the given
2060 * If there is no direct cell, or the cell is not accessible, the function
2061 * will return the nearest suitable cell.
2062 * @param part part containing the document
2063 * @param x absolute x-coordinate
2064 * @param it table row iterator, will be adapted accordingly as more rows are
2066 * @param fromEnd @p true to begin search from end and work towards the
2068 * @return the cell, or 0 if no editable cell was found.
2070 static inline RenderTableCell
*findNearestTableCell(KHTMLPart
*part
, int x
,
2071 TableRowIterator
&it
, bool fromEnd
)
2073 RenderTableCell
*result
= 0;
2076 result
= findNearestTableCellInRow(part
, x
, *it
, fromEnd
);
2079 if (fromEnd
) --it
; else ++it
;
2085 /** finds the nearest editable cell around the given absolute x-coordinate
2087 * It will dive into nested tables as necessary to provide seamless navigation.
2089 * If the cell at @p x is not editable, its left neighbor is tried, then its
2090 * right neighbor, then the left neighbor's left neighbor etc. If no
2091 * editable cell can be found, 0 is returned.
2092 * @param part khtml part
2093 * @param x absolute x-coordinate
2094 * @param row table row to be searched
2095 * @param fromEnd @p true, begin from end (applies only to nested tables)
2096 * @return the found cell or 0 if no editable cell was found
2098 static RenderTableCell
*findNearestTableCellInRow(KHTMLPart
*part
, int x
,
2099 RenderTableSection::RowStruct
*row
, bool fromEnd
)
2101 // First pass. Find spatially nearest cell.
2102 int n
= (int)row
->row
->size();
2104 for (i
= 0; i
< n
; i
++) {
2105 RenderTableCell
*cell
= row
->row
->at(i
);
2106 if (!cell
|| (long)cell
== -1) continue;
2109 cell
->absolutePosition(absx
, absy
, false); // ### position: fixed?
2110 #if DEBUG_CARETMODE > 1
2111 kDebug(6201) << "i/n " << i
<< "/" << n
<< " absx " << absx
<< " absy " << absy
;
2114 // I rely on the assumption that all cells are in ascending visual order
2115 // ### maybe this assumption is wrong for bidi?
2116 #if DEBUG_CARETMODE > 1
2117 kDebug(6201) << "x " << x
<< " < " << (absx
+ cell
->width()) << "?";
2119 if (x
< absx
+ cell
->width()) break;
2121 if (i
>= n
) i
= n
- 1;
2123 // Second pass. Find editable cell, beginning with the currently found,
2124 // extending to the left, and to the right, alternating.
2125 for (int cnt
= 0; cnt
< 2*n
; cnt
++) {
2126 int index
= i
- ((cnt
>> 1) + 1)*(cnt
& 1) + (cnt
>> 1)*!(cnt
& 1);
2127 if (index
< 0 || index
>= n
) continue;
2129 RenderTableCell
*cell
= row
->row
->at(index
);
2130 if (!cell
|| (long)cell
== -1) continue;
2132 #if DEBUG_CARETMODE > 1
2133 kDebug(6201) << "index " << index
<< " cell " << cell
;
2135 RenderTable
*nestedTable
;
2136 if (containsEditableElement(part
, cell
, nestedTable
, fromEnd
)) {
2139 TableRowIterator
it(nestedTable
, fromEnd
);
2141 // kDebug(6201) << "=== recursive invocation";
2142 cell
= findNearestTableCell(part
, x
, it
, fromEnd
);
2144 if (fromEnd
) --it
; else ++it
;
2154 /** returns the nearest common ancestor of two objects that is a table cell,
2155 * a table section, or 0 if not inside a common table.
2157 * If @p r1 and @p r2 belong to the same table, but different sections, @p r1's
2158 * section is returned.
2160 static RenderObject
*commonAncestorTableSectionOrCell(RenderObject
*r1
,
2163 if (!r1
|| !r2
) return 0;
2164 RenderTableSection
*sec
= 0;
2165 int start_depth
=0, end_depth
=0;
2166 // First we find the depths of the two objects in the tree (start_depth, end_depth)
2167 RenderObject
*n
= r1
;
2168 while (n
->parent()) {
2173 while( n
->parent()) {
2177 // here we climb up the tree with the deeper object, until both objects have equal depth
2178 while (end_depth
> start_depth
) {
2182 while (start_depth
> end_depth
) {
2184 // if (r1->isTableSection()) sec = static_cast<RenderTableSection *>(r1);
2187 // Climb the tree with both r1 and r2 until they are the same
2190 if (r1
->isTableSection()) sec
= static_cast<RenderTableSection
*>(r1
);
2194 // At this point, we found the most approximate common ancestor. Now climb
2195 // up until the condition of the function return value is satisfied.
2196 while (r1
&& !r1
->isTableCell() && !r1
->isTableSection() && !r1
->isTable())
2199 return r1
&& r1
->isTable() ? sec
: r1
;
2202 /** Finds the row that contains the given cell, directly, or indirectly
2203 * @param section section to be searched
2204 * @param cell table cell
2205 * @param row returns the row
2206 * @param directCell returns the direct cell that contains @p cell
2207 * @return the index of the row.
2209 static int findRowInSection(RenderTableSection
*section
, RenderTableCell
*cell
,
2210 RenderTableSection::RowStruct
*&row
, RenderTableCell
*&directCell
)
2213 RenderObject
*r
= cell
;
2214 while (r
!= section
) {
2215 if (r
->isTableCell()) directCell
= static_cast<RenderTableCell
*>(r
);
2219 // So, and this is really nasty: As we have no indices, we have to do a
2220 // linear comparison. Oh, that sucks so much for long tables, you can't
2222 int n
= section
->numRows();
2223 for (int i
= 0; i
< n
; i
++) {
2224 row
= §ion
->grid
[i
];
2227 int m
= row
->row
->size();
2228 for (int j
= 0; j
< m
; j
++) {
2229 RenderTableCell
*c
= row
->row
->at(j
);
2230 if (c
== directCell
) return i
;
2238 /** finds the table that is the first direct or indirect descendant of @p block.
2239 * @param leaf object to begin search from.
2240 * @param block object to search to, or 0 to search up to top.
2241 * @return the table or 0 if there were none.
2243 static inline RenderTable
*findFirstDescendantTable(RenderObject
*leaf
, RenderBlock
*block
)
2245 RenderTable
*result
= 0;
2246 while (leaf
&& leaf
!= block
) {
2247 if (leaf
->isTable()) result
= static_cast<RenderTable
*>(leaf
);
2248 leaf
= leaf
->parent();
2253 /** looks for the table cell the given object @p r is contained within.
2254 * @return the table cell or 0 if not contained in any table.
2256 static inline RenderTableCell
*containingTableCell(RenderObject
*r
)
2258 while (r
&& !r
->isTableCell()) r
= r
->parent();
2259 return static_cast<RenderTableCell
*>(r
);
2262 inline void ErgonomicEditableLineIterator::calcAndStoreNewLine(
2263 RenderBlock
*newBlock
, bool toBegin
)
2265 // take the first/last editable element in the found cell as the new
2266 // value for the iterator
2267 CaretBoxIterator it
;
2268 cbl
= CaretBoxLine::constructCaretBoxLine(&lines
->cblDeleter
,
2269 newBlock
, true, toBegin
, it
);
2270 #if DEBUG_CARETMODE > 3
2271 kDebug(6201) << cbl
->information();
2273 // if (toBegin) prevBlock(); else nextBlock();
2279 EditableLineIterator::advance(toBegin
);
2282 void ErgonomicEditableLineIterator::determineTopologicalElement(
2283 RenderTableCell
*oldCell
, RenderObject
*newObject
, bool toBegin
)
2285 // When we arrive here, a transition between cells has happened.
2286 // Now determine the type of the transition. This can be
2287 // (1) a transition from this cell into a table inside this cell.
2288 // (2) a transition from this cell into another cell of this table
2290 TableRowIterator it
;
2292 RenderObject
*commonAncestor
= commonAncestorTableSectionOrCell(oldCell
, newObject
);
2293 #if DEBUG_CARETMODE > 1
2294 kDebug(6201) << " ancestor " << commonAncestor
;
2297 // The whole document is treated as a table cell.
2298 if (!commonAncestor
|| commonAncestor
->isTableCell()) { // (1)
2300 RenderTableCell
*cell
= static_cast<RenderTableCell
*>(commonAncestor
);
2301 RenderTable
*table
= findFirstDescendantTable(newObject
, cell
);
2303 #if DEBUG_CARETMODE > 0
2304 kDebug(6201) << "table cell: " << cell
;
2307 // if there is no table, we fell out of the previous table, and are now
2308 // in some table-less block. Therefore, done.
2311 it
= TableRowIterator(table
, toBegin
);
2313 } else if (commonAncestor
->isTableSection()) { // (2)
2315 RenderTableSection
*section
= static_cast<RenderTableSection
*>(commonAncestor
);
2316 RenderTableSection::RowStruct
*row
;
2317 int idx
= findRowInSection(section
, oldCell
, row
, oldCell
);
2318 #if DEBUG_CARETMODE > 1
2319 kDebug(6201) << "table section: row idx " << idx
;
2322 it
= TableRowIterator(section
, idx
);
2324 // advance rowspan rows
2325 int rowspan
= oldCell
->rowSpan();
2326 while (*it
&& rowspan
--) {
2327 if (toBegin
) --it
; else ++it
;
2331 kError(6201) << "Neither common cell nor section! " << commonAncestor
->renderName() << endl
;
2332 // will crash on uninitialized table row iterator
2335 RenderTableCell
*cell
= findNearestTableCell(lines
->m_part
, xCoor
, it
, toBegin
);
2336 #if DEBUG_CARETMODE > 1
2337 kDebug(6201) << "findNearestTableCell result: " << cell
;
2340 RenderBlock
*newBlock
= cell
;
2342 Q_ASSERT(commonAncestor
->isTableSection());
2343 RenderTableSection
*section
= static_cast<RenderTableSection
*>(commonAncestor
);
2344 cell
= containingTableCell(section
);
2345 #if DEBUG_CARETMODE > 1
2346 kDebug(6201) << "containing cell: " << cell
;
2349 RenderTable
*nestedTable
;
2350 bool editableChild
= cell
&& containsEditableChildElement(lines
->m_part
,
2351 cell
, nestedTable
, toBegin
, section
->table());
2353 if (cell
&& !editableChild
) {
2354 #if DEBUG_CARETMODE > 1
2355 kDebug(6201) << "========= recursive invocation outer =========";
2357 determineTopologicalElement(cell
, cell
->section(), toBegin
);
2358 #if DEBUG_CARETMODE > 1
2359 kDebug(6201) << "========= end recursive invocation outer =========";
2363 } else if (cell
&& nestedTable
) {
2364 #if DEBUG_CARETMODE > 1
2365 kDebug(6201) << "========= recursive invocation inner =========";
2367 determineTopologicalElement(cell
, nestedTable
, toBegin
);
2368 #if DEBUG_CARETMODE > 1
2369 kDebug(6201) << "========= end recursive invocation inner =========";
2374 #if DEBUG_CARETMODE > 1
2375 kDebug(6201) << "newBlock is table: " << section
->table();
2377 RenderObject
*r
= section
->table();
2378 int state
; // not used
2379 ObjectTraversalState trav
= OutsideAscending
;
2380 r
= advanceSuitableObject(r
, trav
, toBegin
, lines
->baseObject(), state
);
2381 if (!r
) { cbl
= 0; return; }
2382 // if (toBegin) prevBlock(); else nextBlock();
2383 newBlock
= static_cast<RenderBlock
*>(!r
|| r
->isRenderBlock() ? r
: r
->containingBlock());
2387 // adapt cell so that prevBlock/nextBlock works as expected
2389 // on forward advancing, we must start from the outside end of the
2392 RenderObject
*r
= newBlock
;
2393 int state
; // not used
2394 ObjectTraversalState trav
= OutsideAscending
;
2395 r
= advanceSuitableObject(r
, trav
, true, lines
->advancePolicy(), lines
->baseObject(), state
);
2396 newBlock
= static_cast<RenderBlock
*>(!r
|| r
->isRenderBlock() ? r
: r
->containingBlock());
2401 calcAndStoreNewLine(newBlock
, toBegin
);
2404 ErgonomicEditableLineIterator
&ErgonomicEditableLineIterator::operator ++()
2406 RenderTableCell
*oldCell
= containingTableCell(cbl
->enclosingObject());
2408 EditableLineIterator::operator ++();
2409 if (*this == lines
->end() || *this == lines
->preBegin()) return *this;
2411 RenderTableCell
*newCell
= containingTableCell(cbl
->enclosingObject());
2413 if (!newCell
|| newCell
== oldCell
) return *this;
2415 determineTopologicalElement(oldCell
, newCell
, false);
2420 ErgonomicEditableLineIterator
&ErgonomicEditableLineIterator::operator --()
2422 RenderTableCell
*oldCell
= containingTableCell(cbl
->enclosingObject());
2424 EditableLineIterator::operator --();
2425 if (*this == lines
->end() || *this == lines
->preBegin()) return *this;
2427 RenderTableCell
*newCell
= containingTableCell(cbl
->enclosingObject());
2429 if (!newCell
|| newCell
== oldCell
) return *this;
2431 determineTopologicalElement(oldCell
, newCell
, true);
2436 // == Navigational helper functions ==
2438 /** seeks the caret box which contains or is the nearest to @p x
2439 * @param it line iterator pointing to line to be searched
2440 * @param cv caret view context
2441 * @param x returns the cv->origX approximation, relatively positioned to the
2443 * @param absx returns absolute x-coordinate of containing block
2444 * @param absy returns absolute y-coordinate of containing block
2445 * @return the most suitable caret box
2447 static CaretBox
*nearestCaretBox(LineIterator
&it
, CaretViewContext
*cv
,
2448 int &x
, int &absx
, int &absy
)
2450 // Find containing block
2451 RenderObject
*cb
= (*it
)->containingBlock();
2452 #if DEBUG_CARETMODE > 4
2453 kDebug(6200) << "nearestCB: cb " << cb
<< "@" << (cb
? cb
->renderName() : "");
2456 if (cb
) cb
->absolutePosition(absx
, absy
);
2457 else absx
= absy
= 0;
2459 // Otherwise find out in which inline box the caret is to be placed.
2461 // this horizontal position is to be approximated
2462 x
= cv
->origX
- absx
;
2463 CaretBox
*caretBox
= 0; // Inline box containing the caret
2464 // NodeImpl *lastnode = 0; // node of previously checked render object.
2465 int xPos
; // x-coordinate of current inline box
2466 int oldXPos
= -1; // x-coordinate of last inline box
2467 EditableCaretBoxIterator fbit
= it
;
2468 #if DEBUG_CARETMODE > 0
2469 /* if (it.linearDocument()->advancePolicy() != LeafsOnly)
2470 kWarning() << "nearestInlineBox is only prepared to handle the LeafsOnly advance policy";*/
2471 // kDebug(6200) << "*fbit = " << *fbit;
2473 // Iterate through all children
2474 for (CaretBox
*b
; fbit
!= (*it
)->end(); ++fbit
) {
2477 #if DEBUG_CARETMODE > 0
2478 // RenderObject *r = b->object();
2479 // if (b->isInlineFlowBox()) kDebug(6200) << "b is inline flow box";
2480 // kDebug(6200) << "approximate r" << r << ": " << (r ? r->renderName() : QString()) << (r && r->isText() ? " contains \"" + QString(((RenderText *)r)->str->s, ((RenderText *)r)->str->l) + "\"" : QString());
2484 // the caret is before this box
2486 // snap to nearest box
2487 if (oldXPos
< 0 || x
- (oldXPos
+ caretBox
->width()) > xPos
- x
) {
2488 caretBox
= b
; // current box is nearer
2490 break; // Otherwise, preceding box is implicitly used
2495 // the caret is within this box
2496 if (x
>= xPos
&& x
< xPos
+ caretBox
->width())
2500 // the caret can only be after the last box which is automatically
2501 // contained in caretBox when we fall out of the loop.
2507 /** moves the given iterator to the beginning of the next word.
2509 * If the end is reached, the iterator will be positioned there.
2510 * @param it character iterator to be moved
2512 static void moveItToNextWord(EditableCharacterIterator
&it
)
2514 #if DEBUG_CARETMODE > 0
2515 kDebug(6200) << "%%%%%%%%%%%%%%%%%%%%% moveItToNextWord";
2517 EditableCharacterIterator copy
;
2518 while (!it
.isEnd() && !(*it
).isSpace() && !(*it
).isPunct()) {
2519 #if DEBUG_CARETMODE > 2
2520 kDebug(6200) << "reading1 '" << (*it
).toLatin1().constData() << "'";
2531 while (!it
.isEnd() && ((*it
).isSpace() || (*it
).isPunct())) {
2532 #if DEBUG_CARETMODE > 2
2533 kDebug(6200) << "reading2 '" << (*it
).toLatin1().constData() << "'";
2539 if (it
.isEnd()) it
= copy
;
2542 /** moves the given iterator to the beginning of the previous word.
2544 * If the beginning is reached, the iterator will be positioned there.
2545 * @param it character iterator to be moved
2547 static void moveItToPrevWord(EditableCharacterIterator
&it
)
2549 if (it
.isEnd()) return;
2551 #if DEBUG_CARETMODE > 0
2552 kDebug(6200) << "%%%%%%%%%%%%%%%%%%%%% moveItToPrevWord";
2554 EditableCharacterIterator copy
;
2556 // Jump over all space and punctuation characters first
2560 #if DEBUG_CARETMODE > 2
2561 if (!it
.isEnd()) kDebug(6200) << "reading1 '" << (*it
).toLatin1().constData() << "'";
2563 } while (!it
.isEnd() && ((*it
).isSpace() || (*it
).isPunct()));
2573 #if DEBUG_CARETMODE > 0
2574 if (!it
.isEnd()) kDebug(6200) << "reading2 '" << (*it
).toLatin1().constData() << "' (" << (int)(*it
).toLatin1().constData() << ") box " << it
.caretBox();
2576 } while (!it
.isEnd() && !(*it
).isSpace() && !(*it
).isPunct());
2579 #if DEBUG_CARETMODE > 1
2580 if (!it
.isEnd()) kDebug(6200) << "effective '" << (*it
).toLatin1().constData() << "' (" << (int)(*it
).toLatin1().constData() << ") box " << it
.caretBox();
2584 /** moves the iterator by one page.
2585 * @param ld linear document
2586 * @param it line iterator, will be updated accordingly
2587 * @param mindist minimum distance in pixel the iterator should be moved
2589 * @param next @p true, move downward, @p false move upward
2591 static void moveIteratorByPage(LinearDocument
&ld
,
2592 ErgonomicEditableLineIterator
&it
, int mindist
, bool next
)
2594 // ### This whole routine plainly sucks. Use an inverse strategie for pgup/pgdn.
2596 if (it
== ld
.end() || it
== ld
.preBegin()) return;
2598 ErgonomicEditableLineIterator copy
= it
;
2599 #if DEBUG_CARETMODE > 0
2600 kDebug(6200) << " mindist: " << mindist
;
2603 CaretBoxLine
*cbl
= *copy
;
2604 int absx
= 0, absy
= 0;
2606 RenderBlock
*lastcb
= cbl
->containingBlock();
2607 Q_ASSERT(lastcb
->isRenderBlock());
2608 lastcb
->absolutePosition(absx
, absy
, false); // ### what about fixed?
2610 int lastfby
= cbl
->begin().data()->yPos();
2612 int rescue
= 1000; // ### this is a hack to keep stuck carets from hanging the ua
2614 if (next
) ++copy
; else --copy
;
2615 if (copy
== ld
.end() || copy
== ld
.preBegin()) break;
2618 RenderBlock
*cb
= cbl
->containingBlock();
2621 // ### actually flowBox->yPos() should suffice, but this is not ported
2622 // over yet from WebCore
2623 int fby
= cbl
->begin().data()->yPos();
2626 diff
= absy
+ lastfby
+ lastheight
;
2627 cb
->absolutePosition(absx
, absy
, false); // ### what about fixed?
2628 diff
= absy
- diff
+ fby
;
2632 cb
->absolutePosition(absx
, absy
, false); // ### what about fixed?
2633 diff
-= absy
+ fby
+ lastheight
;
2634 lastfby
= fby
- lastheight
;
2636 #if DEBUG_CARETMODE > 2
2637 kDebug(6200) << "absdiff " << diff
;
2640 diff
= qAbs(fby
- lastfby
);
2642 #if DEBUG_CARETMODE > 2
2643 kDebug(6200) << "cbl->begin().data()->yPos(): " << fby
<< " diff " << diff
;
2648 lastheight
= qAbs(fby
- lastfby
);
2652 #if DEBUG_CARETMODE > 0
2653 kDebug(6200) << " mindist: " << mindist
;
2655 // trick: actually the distance is always one line short, but we cannot
2656 // calculate the height of the first line (### WebCore will make it better)
2657 // Therefore, we simply approximate that excess line by using the last
2658 // caluculated line height.
2659 } while (mindist
- lastheight
> 0 && --rescue
);