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