khtml Library API Documentation

khtml_caret.cpp

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