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