00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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 }
00096
00097 return obj;
00098 }
00099
00105 static inline RenderObject *renderObjectBelow(RenderObject *obj, ObjectTraversalState &trav, RenderObject *base)
00106 {
00107 trav = InsideDescending;
00108 int state;
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;
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
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
00154
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
00168
00169
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
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
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
00228
00229
00230 la = a; latrav = trav;
00231 ignoreOutsideDesc = false;
00232 break;
00233 }
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
00244
00245 case InsideAscending:
00246
00247
00248 case OutsideAscending:
00249
00250
00251
00252
00253 if (trav == OutsideDescending) return a;
00254 if (trav == OutsideAscending) {
00255 if (la) return la;
00256
00257
00258 la = a; latrav = trav;
00259 }
00260 break;
00261 }
00262
00263 }
00264 }
00265
00266 lasttrav = trav;
00267 a = traverseRenderObjects(a, trav, toBegin, base, state);
00268 }
00269
00270 if (la) trav = latrav, a = la;
00271 return a;
00272
00273 }
00274
00283 static inline bool isUnsuitable(RenderObject *r, ObjectTraversalState )
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 }
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 }
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 }
00351 n = r->parentNode();
00352 if (n == baseElem) n = 0;
00353 }
00354 return 0;
00355 }
00356
00357 #if 0
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 }
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 }
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 }
00392 n = r->parentNode();
00393 if (n == baseElem) n = 0;
00394 }
00395 return 0;
00396 }
00397 #endif
00398
00410 void 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
00421
00422 if (node->firstChild()) {
00423 outside = true;
00424 NodeImpl *child = offset <= 0 ? node->firstChild()
00425
00426 : node->childNode((unsigned long)offset);
00427
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
00439
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 }
00449
00450
00451
00452 if (r && r->isBR()) {
00453 r = r->objectAbove();
00454 outsideEnd = true;
00455 }
00456
00457 } else {
00458
00459 outside = false;
00460 outsideEnd = false;
00461 r = node->renderer();
00462 r_ofs = 0;
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 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
00496
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
00504
00505 node = r->firstChild() ? r->firstChild()->element() : node;
00506 }
00507 }
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 {
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;
00562 case InsideDescending: atEnd = toBegin; break;
00563 case OutsideAscending: outside = true;
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;
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
00624
00625
00626 DocumentImpl *doc = caretNode->getDocument();
00627 if (!doc) return 0;
00628
00629 if (doc->isHTMLDocument())
00630 return static_cast<HTMLDocumentImpl *>(doc)->body();
00631
00632 return 0;
00633 }
00634
00635
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 }
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
00653 }
00654 #endif
00655
00656
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)
00664 {
00665
00666
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
00676 if (!box->object()) continue;
00677
00678 RenderStyle *s = box->object()->style(box->m_firstLine);
00679
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
00694
00695
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 }
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
00737 coalesceOutsideBoxes = false;
00738
00739 } else {
00740 #if DEBUG_ACIB
00741 kdDebug(6200) << "some replaced or what " << box << endl;
00742 #endif
00743
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 }
00766 }
00767 }
00768 #undef DEBUG_ACIB
00769
00770 void CaretBoxLine::addCreatedFlowBoxInside(InlineFlowBox *flowBox, const QFontMetrics &fm)
00771 {
00772
00773 CaretBox *caretBox = new CaretBox(flowBox, false, false);
00774 caret_boxes.append(caretBox);
00775
00776
00777
00778
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)
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)
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
00814 {
00815
00816
00817
00818
00819
00820
00821
00822
00823
00824
00825 CaretBoxLine *result = new CaretBoxLine(basicFlowBox);
00826 deleter->append(result);
00827
00828 SeekBoxParams sbp(seekBox, seekOutside, seekOutsideEnd, seekObject, iter);
00829
00830
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)
00840 {
00841 int _x = cb->xPos();
00842 int _y = cb->yPos();
00843 int height;
00844 int width = 1;
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 {
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
00884 switch (s->textAlign()) {
00885 case LEFT:
00886 case KHTML_LEFT:
00887 case TAAUTO:
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 }
00899 }
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
00922
00930 inline InlineFlowBox *seekBaseFlowBox(InlineBox *b, RenderObject *base = 0)
00931 {
00932
00933 while (b->parent() && b->object() != base) {
00934 b = b->parent();
00935 }
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
00976
00977
00978
00979
00980
00981
00982
00983
00984
00985 if (r->isText()) do {
00986 RenderText *t = static_cast<RenderText *>(r);
00987 int dummy;
00988 InlineBox *b = t->findInlineTextBox(offset, dummy, true);
00989
00990
00991
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 }
00998 Q_ASSERT(b);
00999 outside = false;
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);
01013
01014
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
01021
01022
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 }
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 }
01038
01039 RenderBlock *cb = r->containingBlock();
01040
01041 Q_ASSERT(cb);
01042
01043
01044
01045 if (!cb->isRenderBlock()) {
01046 kdWarning() << "containing block is no render block!!! crash imminent" << endl;
01047 }
01048
01049 InlineFlowBox *flowBox = cb->firstLineBox();
01050
01051
01052
01053 if (!flowBox) {
01054
01055
01056
01057
01058
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 }
01065
01066
01067
01068
01069 for (; flowBox; flowBox = static_cast<InlineFlowBox *>(flowBox->nextLineBox())) {
01070 #if DEBUG_CARETMODE > 0
01071 kdDebug(6200) << "[scan line]" << endl;
01072 #endif
01073
01074
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 }
01088
01089
01090
01091
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
01143 ObjectTraversalState trav = InsideDescending;
01144 do {
01145 bool modWithinCb = withinCb = isDescendant(r, cb);
01146
01147
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 }
01166
01167
01168
01169
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
01193 RenderObject *r = start;
01194 do {
01195 r = traverseRenderObjects(r, trav, fromEnd, cb->parent(), state);
01196 } while(r && !(state & AdvancedToSibling));
01197
01198
01199
01200
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
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
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 }
01236
01237 r = fromEnd ? r->objectAbove() : r->objectBelow();
01238 } while (withinCb);
01239 return false;
01240 }
01241
01242
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
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();
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();
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
01315
01316 CaretBoxIterator LineIterator::currentBox ;
01317 long LineIterator::currentOffset ;
01318
01319 LineIterator::LineIterator(LinearDocument *l, DOM::NodeImpl *node, long offset)
01320 : lines(l)
01321 {
01322
01323 if (!node) { cbl = 0; return; }
01324 cbl = findCaretBoxLine(node, offset, &lines->cblDeleter,
01325 l->baseObject(), currentOffset, currentBox);
01326
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 }
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;
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 }
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
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 }
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) {
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;
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;
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 }
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
01447 bool isrepl = isBlockRenderReplaced(r);
01448
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 }
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) {
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;
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;
01489 CaretBoxIterator it;
01490 cbl = CaretBoxLine::constructCaretBoxLine(&lines->cblDeleter,
01491 flowBox, flowBox->firstChild(), seekOutside, seekOutsideEnd, it);
01492 }
01493 }
01494
01495
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
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;
01515 bool iscuruseable;
01516
01517 adjacent = true;
01518
01519 #if DEBUG_CARETMODE > 4
01520
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;
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
01546
01547 if (!box->isInline()) break;
01548
01549 if (advpol == VisibleFlows) break;
01550
01551
01552
01553 InlineBox *ibox = box->inlineBox();
01554
01555 InlineBox *prev = box->isOutsideEnd() ? ibox : ibox->prevOnLine();
01556
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
01573 ;
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
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
01605
01606 << " marl " << s->marginLeft().value()
01607 << " marr " << s->marginRight().value()
01608 << endl;
01609 #endif
01610 #endif
01611 break;
01612 }
01613
01614 } else {
01615
01616 if (!(*curbox)->isOutside()) {
01617
01618 adjacent = false;
01619 }
01620
01621 }
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 }
01631 }
01632
01633 *static_cast<CaretBoxIterator *>(this) = curbox;
01634 #if DEBUG_CARETMODE > 4
01635
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
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
01652
01653
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
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
01669 if (b->isOutside() && !globallyNavigable) {
01670 NodeImpl *par = node->parent();
01671
01672
01673 Q_ASSERT(par);
01674 if (par) node = par;
01675 eff_r = node->renderer();
01676 Q_ASSERT(eff_r);
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
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
01713 case LeafsOnly:
01714 if (cbl->isOutside()) break;
01715
01716 case VisibleFlows: goto wend;
01717 }
01718
01719
01720 lasteditable = *this;
01721 haslasteditable = true;
01722 #if DEBUG_CARETMODE > 4
01723 kdDebug(6200) << "remembered lasteditable " << *lasteditable << endl;
01724 #endif
01725 } else {
01726
01727
01728
01729
01730
01731 if (haslasteditable) { uselasteditable = true; break; }
01732
01733 }
01734 LineIterator::advance(toBegin);
01735 }
01736 wend:
01737
01738 if (uselasteditable) *this = haslastindicated ? lastindicated : lasteditable;
01739 if (!cbl && haslastindicated) *this = lastindicated;
01740 }
01741
01742
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 << endl;
01785 #endif
01786 if (true) {
01787 ++ebit;
01788 if (ebit == (*_it)->end()) {
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 }
01813 goto readchar;
01814 }
01815 }
01816
01817 bool adjacent = ebit.isAdjacent();
01818 #if 0
01819
01820 if (adjacent && !(*ebit)->isInlineTextBox()) {
01821 EditableCaretBoxIterator copy = ebit;
01822 ++ebit;
01823 if (ebit != (*_it)->end() && (*ebit)->isInlineTextBox()
01824
01825 )
01826 adjacent = false;
01827 else ebit = copy;
01828 }
01829 #endif
01830
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 }
01841
01842 _offset = (*ebit)->minOffset() + adjacent;
01843
01844 box = *ebit;
01845 b = box->inlineBox();
01846 goto readchar;
01847 } else {
01848 readchar:
01849
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 }
01855 #if DEBUG_CARETMODE > 2
01856 kdDebug(6200) << "_offset: " << _offset << " 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
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
01887
01888 if (b && !box->isOutside() && b->isInlineTextBox())
01889 _char = static_cast<RenderText *>(b->object())->text()[_offset].unicode();
01890 else
01891 _char = -1;
01892
01893
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
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 << endl;
01915 #endif
01916 if (!_peekPrev) {
01917 _peekNext = *ebit;
01918 --ebit;
01919 if (ebit == (*_it)->preBegin()) {
01920 --_it;
01921 #if DEBUG_CARETMODE > 3
01922 kdDebug(6200) << "--_it" << endl;
01923 #endif
01924 if (_it != _it.lines->preBegin()) {
01925
01926 ebit = EditableCaretBoxIterator(_it, true);
01927 box = *ebit;
01928
01929 #if DEBUG_CARETMODE > 3
01930 kdDebug(6200) << "box " << box << " b " << box->inlineBox() << " isText " << box->isInlineTextBox() << endl;
01931 #endif
01932 _offset = box->maxOffset();
01933
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 }
01942 }
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
01950 if (adjacent && _peekNext && _peekNext->isInlineTextBox()
01951 && !(*ebit)->isInlineTextBox()) {
01952 EditableCaretBoxIterator copy = ebit;
01953 --ebit;
01954 if (ebit == (*_it)->preBegin())
01955 ebit = copy;
01956 }
01957 #endif
01958 #if 0
01959
01960 if (adjacent
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 }
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
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
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 }
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
02009
02010 TableRowIterator::TableRowIterator(RenderTable *table, bool fromEnd,
02011 RenderTableSection::RowStruct *row)
02012 : sec(table, fromEnd)
02013 {
02014
02015 if (*sec) {
02016 if (fromEnd) index = (*sec)->grid.size() - 1;
02017 else index = 0;
02018 }
02019
02020
02021 if (row && *sec) {
02022 while (operator *() != row)
02023 if (fromEnd) operator --(); else operator ++();
02024 }
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 }
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 }
02048 return *this;
02049 }
02050
02051
02052
02053
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 }
02081
02082 return result;
02083 }
02084
02098 static RenderTableCell *findNearestTableCellInRow(KHTMLPart *part, int x,
02099 RenderTableSection::RowStruct *row, bool fromEnd)
02100 {
02101
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);
02110 #if DEBUG_CARETMODE > 1
02111 kdDebug(6201) << "i/n " << i << "/" << n << " absx " << absx << " absy " << absy << endl;
02112 #endif
02113
02114
02115
02116 #if DEBUG_CARETMODE > 1
02117 kdDebug(6201) << "x " << x << " < " << (absx + cell->width()) << "?" << endl;
02118 #endif
02119 if (x < absx + cell->width()) break;
02120 }
02121 if (i >= n) i = n - 1;
02122
02123
02124
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
02142 cell = findNearestTableCell(part, x, it, fromEnd);
02143 if (cell) break;
02144 if (fromEnd) --it; else ++it;
02145 }
02146 }
02147
02148 return cell;
02149 }
02150 }
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
02167 RenderObject *n = r1;
02168 while (n->parent()) {
02169 n = n->parent();
02170 start_depth++;
02171 }
02172 n = r2;
02173 while( n->parent()) {
02174 n = n->parent();
02175 end_depth++;
02176 }
02177
02178 while (end_depth > start_depth) {
02179 r2 = r2->parent();
02180 end_depth--;
02181 }
02182 while (start_depth > end_depth) {
02183 r1 = r1->parent();
02184
02185 start_depth--;
02186 }
02187
02188 while (r1 != r2){
02189 r1 = r1->parent();
02190 if (r1->isTableSection()) sec = static_cast<RenderTableSection *>(r1);
02191 r2 = r2->parent();
02192 }
02193
02194
02195
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
02213 RenderObject *r = cell;
02214 while (r != section) {
02215 if (r->isTableCell()) directCell = static_cast<RenderTableCell *>(r);
02216 r = r->parent();
02217 }
02218
02219
02220
02221
02222 int n = section->numRows();
02223 for (int i = 0; i < n; i++) {
02224 row = §ion->grid[i];
02225
02226
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 }
02232
02233 }
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 }
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
02266
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
02274
02275 if (!cbl) {
02276 return;
02277 }
02278
02279 EditableLineIterator::advance(toBegin);
02280 }
02281
02282 void ErgonomicEditableLineIterator::determineTopologicalElement(
02283 RenderTableCell *oldCell, RenderObject *newObject, bool toBegin)
02284 {
02285
02286
02287
02288
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
02298 if (!commonAncestor || commonAncestor->isTableCell()) {
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
02308
02309 if (!table) return;
02310
02311 it = TableRowIterator(table, toBegin);
02312
02313 } else if (commonAncestor->isTableSection()) {
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
02325 int rowspan = oldCell->rowSpan();
02326 while (*it && rowspan--) {
02327 if (toBegin) --it; else ++it;
02328 }
02329
02330 } else {
02331 kdError(6201) << "Neither common cell nor section! " << commonAncestor->renderName() << endl;
02332
02333 }
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;
02379 ObjectTraversalState trav = OutsideAscending;
02380 r = advanceSuitableObject(r, trav, toBegin, lines->baseObject(), state);
02381 if (!r) { cbl = 0; return; }
02382
02383 newBlock = static_cast<RenderBlock *>(!r || r->isRenderBlock() ? r : r->containingBlock());
02384 }
02385 #if 0
02386 } else {
02387
02388 newBlock = cell;
02389
02390
02391 if (!toBegin) {
02392 RenderObject *r = newBlock;
02393 int state;
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 }
02398 #endif
02399 }
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
02437
02447 static CaretBox *nearestCaretBox(LineIterator &it, CaretViewContext *cv,
02448 int &x, int &absx, int &absy)
02449 {
02450
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
02460
02461
02462 x = cv->origX - absx;
02463 CaretBox *caretBox = 0;
02464
02465 int xPos;
02466 int oldXPos = -1;
02467 EditableCaretBoxIterator fbit = it;
02468 #if DEBUG_CARETMODE > 0
02469
02470
02471
02472 #endif
02473
02474 for (CaretBox *b; fbit != (*it)->end(); ++fbit) {
02475 b = *fbit;
02476
02477 #if DEBUG_CARETMODE > 0
02478
02479
02480
02481 #endif
02482 xPos = b->xPos();
02483
02484
02485 if (x < xPos) {
02486
02487 if (oldXPos < 0 || x - (oldXPos + caretBox->width()) > xPos - x) {
02488 caretBox = b;
02489 }
02490 break;
02491 }
02492
02493 caretBox = b;
02494
02495
02496 if (x >= xPos && x < xPos + caretBox->width())
02497 break;
02498 oldXPos = xPos;
02499
02500
02501
02502 }
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 }
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
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 }
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
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);
02609
02610 int lastfby = cbl->begin().data()->yPos();
02611 int lastheight = 0;
02612 int rescue = 1000;
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
02622
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);
02628 diff = absy - diff + fby;
02629 lastfby = 0;
02630 } else {
02631 diff = absy;
02632 cb->absolutePosition(absx, absy, false);
02633 diff -= absy + fby + lastheight;
02634 lastfby = fby - lastheight;
02635 }
02636 #if DEBUG_CARETMODE > 2
02637 kdDebug(6200) << "absdiff " << diff << endl;
02638 #endif
02639 } else {
02640 diff = kAbs(fby - lastfby);
02641 }
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
02656
02657
02658
02659 } while (mindist - lastheight > 0 && --rescue);
02660 }
02661
02662
02663 }