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