khtml Library API Documentation

khtml_caret.cpp

00001 /* This file is part of the KDE project
00002  *
00003  * Copyright (C) 2003 Leo Savernik <l.savernik@aon.at>
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU Library General Public
00007  * License as published by the Free Software Foundation; either
00008  * version 2 of the License, or (at your option) any later version.
00009  *
00010  * This library is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Library General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Library General Public License
00016  * along with this library; see the file COPYING.LIB.  If not, write to
00017  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018  * Boston, MA 02111-1307, USA.
00019  */
00020 
00021 
00022 #include "khtml_caret_p.h"
00023 
00024 namespace khtml {
00025 
00026 static InlineFlowBox *findFlowBox(DOM::NodeImpl *node, long offset,
00027         RenderArena *arena, RenderFlow *&cb, InlineBox **ibox = 0);
00028 static RenderObject *nextLeafRenderObject(RenderObject *r);
00029 
00034 static inline RenderObject *nextSuitableLeafRenderObject(RenderObject *r)
00035 {
00036   do {
00037     r = nextLeafRenderObject(r);
00038   } while (r && r->isTableCol());
00039   return r;
00040 }
00041 
00043 static void ensureLeafNode(NodeImpl *&node)
00044 {
00045   if (node && node->hasChildNodes()) node = node->nextLeafNode();
00046 }
00047 
00056 static RenderObject* findRenderer(NodeImpl *&node)
00057 {
00058   if (!node) return 0;
00059   RenderObject *r = node->renderer();
00060   while (!r) {
00061     node = node->nextLeafNode();
00062     if (!node) break;
00063     r = node->renderer();
00064   }
00065   if (r && r->isTableCol()) r = nextSuitableLeafRenderObject(r);
00066   return r;
00067 }
00068 
00070 static void sanitizeCaretState(NodeImpl *&caretNode, long &offset)
00071 {
00072   ensureLeafNode(caretNode);
00073 
00074   // FIXME: this leaves caretNode untouched if there are no more renderers.
00075   // It better should search backwards then.
00076   // This still won't solve the problem what to do if *no* element has a
00077   // renderer.
00078   NodeImpl *tmpNode = caretNode;
00079   if (findRenderer(tmpNode)) caretNode = tmpNode;
00080   if (!caretNode) return;
00081 
00082   long max = caretNode->maxOffset();
00083   long min = caretNode->minOffset();
00084   if (offset < min) offset = min;
00085   else if (offset > max) offset = max;
00086 }
00087 
00089 static RenderObject *prevLeafRenderObject(RenderObject *r)
00090 {
00091   RenderObject *n = r->objectAbove();
00092   while (n && n == r->parent()) {
00093     if (n->previousSibling()) return n->objectAbove();
00094     r = n;
00095     n = r->parent();
00096   }
00097   return n;
00098 }
00099 
00101 static RenderObject *nextLeafRenderObject(RenderObject *r)
00102 {
00103   RenderObject *n = r->objectBelow();
00104   r = n;
00105   while (n) r = n, n = n->firstChild();
00106   return r;
00107 }
00108 
00113 static RenderObject *prevSuitableLeafRenderObject(RenderObject *r)
00114 {
00115   do {
00116     r = prevLeafRenderObject(r);
00117   } while (r && r->isTableCol());
00118   return r;
00119 }
00120 
00123 static inline InlineBox *seekLeafInlineBox(InlineBox *box)
00124 {
00125   while (box && box->isInlineFlowBox()) {
00126 //    if (box->isInlineFlowBox()) {
00127       box = static_cast<InlineFlowBox *>(box)->firstChild();
00128 //    else if (box->object()->isFlow())
00129 //      box = static_cast<RenderFlow *>(box->object())->firstLineBox();
00130 //    else
00131 //      break;
00132   }/*wend*/
00133   return box;
00134 }
00135 
00138 static inline InlineBox *seekLeafInlineBoxFromEnd(InlineBox *box)
00139 {
00140   while (box && box->isInlineFlowBox()) {
00141       box = static_cast<InlineFlowBox *>(box)->lastChild();
00142   }/*wend*/
00143 #if DEBUG_CARETMODE > 0
00144   kdDebug(6200) << "seekLeafFromEnd: 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) << endl;
00145 #endif
00146   return box;
00147 }
00148 
00149 
00150 
00151 InlineBox *LineIterator::currentBox;
00152 
00153 InlineBoxIterator::InlineBoxIterator(RenderArena *arena, InlineFlowBox *flowBox, bool fromEnd)
00154     : arena(arena)
00155 {
00156     box = fromEnd ? seekLeafInlineBoxFromEnd(flowBox) : seekLeafInlineBox(flowBox);
00157 }
00158 
00159 InlineBoxIterator::InlineBoxIterator(LineIterator &lit, bool fromEnd,
00160                       InlineBox *initBox)
00161     : arena(lit.lines->arena)
00162 {
00163     if (initBox) box = initBox;
00164     else box = fromEnd ? seekLeafInlineBoxFromEnd(*lit) : seekLeafInlineBox(*lit);
00165 }
00166 
00167 
00168 InlineBoxIterator& InlineBoxIterator::operator ++()
00169 {
00170     InlineBox *newBox = box->nextOnLine();
00171 
00172     if (newBox)
00173       box = seekLeafInlineBox(newBox);
00174     else {
00175       InlineFlowBox *flowBox = box->parent();
00176       box = 0;
00177       while (flowBox) {
00178         InlineBox *newBox2 = flowBox->nextOnLine();
00179     if (newBox2) {
00180       box = seekLeafInlineBox(newBox2);
00181       break;
00182     }/*end if*/
00183 
00184     flowBox = flowBox->parent();
00185       }/*wend*/
00186     }/*end if*/
00187 
00188     return *this;
00189 }
00190 
00194 InlineBoxIterator& InlineBoxIterator::operator --()
00195 {
00196     InlineBox *newBox = box->prevOnLine();
00197 
00198     if (newBox)
00199         box = seekLeafInlineBoxFromEnd(newBox);
00200     else {
00201         InlineFlowBox *flowBox = box->parent();
00202         box = 0;
00203         while (flowBox) {
00204         InlineBox *newBox2 = flowBox->prevOnLine();
00205     if (newBox2) {
00206       box = seekLeafInlineBoxFromEnd(newBox2);
00207       break;
00208     }/*end if*/
00209 
00210     flowBox = flowBox->parent();
00211       }/*wend*/
00212     }/*end if*/
00213 
00214     return *this;
00215 }
00216 
00233 static InlineFlowBox* generateDummyFlowBox(RenderArena *arena, RenderFlow *cb,
00234             RenderObject *childNodeHint = 0)
00235 {
00236   InlineFlowBox *flowBox = new(arena) InlineFlowBox(cb);
00237   int width = cb->width();
00238   // FIXME: this does neither take into regard :first-line nor :first-letter
00239   // However, as soon as some content is entered, the line boxes will be
00240   // constructed properly and this kludge is not called any more. So only
00241   // the caret size of an empty :first-line'd block is wrong, but I think we
00242   // can live with that.
00243   int height = cb->style()->fontMetrics().height();
00244   flowBox->setWidth(0);
00245   flowBox->setHeight(height);
00246 
00247   // Add single child at the right position
00248   InlineBox *child = new(arena) InlineBox(childNodeHint ? childNodeHint : cb);
00249   // ### regard direction
00250   switch (cb->style()->textAlign()) {
00251     case LEFT:
00252     case TAAUTO:    // ### find out what this does
00253     case JUSTIFY:
00254       child->setXPos(0);
00255       break;
00256     case CENTER:
00257     case KONQ_CENTER:
00258       child->setXPos(width / 2);
00259       break;
00260     case RIGHT:
00261       child->setXPos(width);
00262       break;
00263   }/*end switch*/
00264   child->setYPos(0);
00265   child->setWidth(1);
00266   child->setHeight(height);
00267 
00268   flowBox->setXPos(child->xPos());
00269   flowBox->setYPos(child->yPos());
00270   flowBox->addToLine(child);
00271   //kdDebug(6200) << "generateDummyFlowBox: " << flowBox << " with child " << child << endl;
00272   return flowBox;
00273 }
00274 
00280 static RenderFlow* generateDummyBlock(RenderArena */*arena*/, RenderObject *cb)
00281 {
00282     // ### will fail if positioned
00283   RenderFlow *result = RenderFlow::createFlow(cb->element(), cb->style(), cb->renderArena());
00284   result->setParent(cb->parent());
00285   result->setPreviousSibling(cb->previousSibling());
00286   result->setNextSibling(cb->nextSibling());
00287 
00288   result->setOverhangingContents(cb->overhangingContents());
00289   result->setPositioned(cb->isPositioned());
00290   result->setRelPositioned(cb->isRelPositioned());
00291   result->setFloating(cb->isFloating());
00292   result->setInline(cb->isInline());
00293   result->setMouseInside(cb->mouseInside());
00294 
00295   result->setPos(cb->xPos(), cb->yPos());
00296   result->setWidth(cb->width());
00297   result->setHeight(cb->height());
00298 
00299   return result;
00300 }
00301 
00317 static InlineFlowBox* findFlowBox(DOM::NodeImpl *node, long offset,
00318         RenderArena *arena, RenderFlow *&cb, InlineBox **ibox)
00319 {
00320   RenderObject *r = findRenderer(node);
00321   if (!r) { cb = 0; return 0; }
00322 #if DEBUG_CARETMODE > 0
00323   kdDebug(6200) << "=================== findFlowBox" << endl;
00324   kdDebug(6200) << "node " << node << " r " << r->renderName() << "[" << r << "].node " << r->element()->nodeName().string() << "[" << r->element() << "]" << " offset: " << offset << endl;
00325 #endif
00326 
00327   // If we have a totally empty render block, we simply construct a
00328   // transient inline flow box, and be done with it.
00329   // This case happens only when the render block is a leaf object itself.
00330   if (r->isRenderBlock() && !static_cast<RenderBlock *>(r)->firstLineBox()) {
00331     cb = static_cast<RenderBlock *>(r);
00332 #if DEBUG_CARETMODE > 0
00333   kdDebug(6200) << "=================== end findFlowBox (dummy)" << endl;
00334 #endif
00335     InlineFlowBox *fb = generateDummyFlowBox(arena, cb);
00336     if (ibox) *ibox = fb;
00337     return fb;
00338   }/*end if*/
00339 
00340   // There are two strategies to find the correct line box.
00341   // (A) First, if node's renderer is a RenderText, we only traverse its text
00342   // runs and return the root line box (saves much time for long blocks).
00343   // This should be the case 99% of the time.
00344   // (B) Otherwise, we iterate linearly through all line boxes in order to find
00345   // the renderer. (A reverse mapping would be favorable, but needs memory)
00346   if (r->isText()) do {
00347     RenderText *t = static_cast<RenderText *>(r);
00348     int dummy;
00349     InlineBox *b = t->findInlineTextBox(offset, dummy, true);
00350     // Actually b should never be 0, but some render texts don't have text
00351     // boxes, so we insert the last run as an error correction.
00352     // If there is no last run, we resort to (B)
00353     if (!b) {
00354       if (t->m_lines.count() > 0)
00355         b = t->m_lines[t->m_lines.count() - 1];
00356       else
00357         break;
00358     }/*end if*/
00359     Q_ASSERT(b);
00360     if (ibox) *ibox = b;
00361     while (b->parent()) {   // seek root line box
00362       b = b->parent();
00363     }/*wend*/
00364     // FIXME: replace with isRootInlineBox after full WebCore merge.
00365     Q_ASSERT(b->isRootInlineBox());
00366     cb = static_cast<RenderFlow *>(b->object());
00367     Q_ASSERT(cb->isRenderBlock());
00368 #if DEBUG_CARETMODE > 0
00369   kdDebug(6200) << "=================== end findFlowBox (renderText)" << endl;
00370 #endif
00371     return static_cast<InlineFlowBox *>(b);
00372   } while(false);/*end if*/
00373 
00374   cb = r->containingBlock();
00375   if ( !cb ) return 0L;
00376 
00377   if (!cb->isRenderBlock()) {
00378     cb = generateDummyBlock(arena, cb);
00379 #if DEBUG_CARETMODE > 0
00380     kdDebug(6200) << "dummy block created: " << cb << endl;
00381 #endif
00382   }/*end if*/
00383 
00384   InlineFlowBox *flowBox = cb->firstLineBox();
00385   // This case strikes when there are children but none of it is represented
00386   // by an inline box (for example, all of them are empty:
00387   // <div><b></b><i></i></div>)
00388   if (!flowBox) {
00389     flowBox = generateDummyFlowBox(arena, cb, r);
00390     if (ibox) *ibox = flowBox->firstChild();
00391 #if DEBUG_CARETMODE > 0
00392   kdDebug(6200) << "=================== end findFlowBox (2)" << endl;
00393 #endif
00394     return flowBox;
00395   }/*end if*/
00396 
00397   // We iterate the inline flow boxes of the containing block until
00398   // we find the given node. This has one major flaw: it is linear, and therefore
00399   // painfully slow for really large blocks.
00400   for (; flowBox; flowBox = static_cast<InlineFlowBox *>(flowBox->nextLineBox())) {
00401 #if DEBUG_CARETMODE > 0
00402     kdDebug(6200) << "[scan line]" << endl;
00403 #endif
00404 
00405     // Iterate children, and look for node
00406     InlineBox *box;
00407     InlineBoxIterator it(arena, flowBox);
00408     for (; (box = *it) != 0; ++it) {
00409       RenderObject *br = box->object();
00410       if (!br) continue;
00411 
00412 #if DEBUG_CARETMODE > 0
00413       kdDebug(6200) << "box->obj " << br->renderName() << "[" << br << "]" << " minOffset: " << box->minOffset() << " maxOffset: " << box->maxOffset() << endl;
00414 #endif
00415       if (br == r && offset >= box->minOffset() && offset <= box->maxOffset())
00416         break;  // If Dijkstra hadn't brainwashed me, I'd have used a goto here
00417     }/*next it*/
00418     if (box) {
00419       if (ibox) *ibox = box;
00420       break;
00421     }
00422 
00423   }/*next flowBox*/
00424 
00425   // no inline flow box found, approximate to nearest following node.
00426   // Danger: this is O(n^2). It's only called to recover from
00427   // errors, that means, theoretically, never. (Practically, far too often :-( )
00428   if (!flowBox) flowBox = findFlowBox(node->nextLeafNode(), 0, arena, cb, ibox);
00429 
00430 #if DEBUG_CARETMODE > 0
00431   kdDebug(6200) << "=================== end findFlowBox" << endl;
00432 #endif
00433   return flowBox;
00434 }
00435 
00442 static inline RenderTable *findTableUpTo(RenderObject *r, RenderFlow *cb)
00443 {
00444   while (r && r != cb && !r->isTable()) r = r->parent();
00445   return r && r->isTable() ? static_cast<RenderTable *>(r) : 0;
00446 }
00447 
00450 static inline bool isDescendant(RenderObject *r, RenderObject *cb)
00451 {
00452   while (r && r != cb) r = r->parent();
00453   return r;
00454 }
00455 
00466 static bool containsEditableElement(KHTMLPart *part, RenderFlow *cb,
00467     RenderTable *&table, bool fromEnd = false)
00468 {
00469   RenderObject *r = cb;
00470   if (fromEnd)
00471     while (r->lastChild()) r = r->lastChild();
00472   else
00473     while (r->firstChild()) r = r->firstChild();
00474 
00475   RenderTable *tempTable = 0;
00476   table = 0;
00477   bool withinCb;
00478   do {
00479     tempTable = findTableUpTo(r, cb);
00480     withinCb = isDescendant(r, cb);
00481 
00482 #if DEBUG_CARETMODE > 1
00483     kdDebug(6201) << "r " << (r ? r->renderName() : QString::null) << "@" << r << endl;
00484 #endif
00485     if (r && withinCb && r->element() && !r->isTableCol()
00486         && (part->isCaretMode() || part->isEditable()
00487         || r->style()->userInput() == UI_ENABLED)) {
00488       table = tempTable;
00489       return true;
00490     }/*end if*/
00491 
00492     r = fromEnd ? prevSuitableLeafRenderObject(r) : nextSuitableLeafRenderObject(r);
00493   } while (r && withinCb);
00494   return false;
00495 }
00496 
00509 static bool containsEditableChildElement(KHTMLPart *part, RenderFlow *cb,
00510     RenderTable *&table, bool fromEnd, RenderObject *start)
00511 {
00512   RenderObject *r = start;
00513   if (fromEnd)
00514     while (r->firstChild()) r = r->firstChild();
00515   else
00516     while (r->lastChild()) r = r->lastChild();
00517 
00518   if (!r) return false;
00519 
00520   RenderTable *tempTable = 0;
00521   table = 0;
00522   bool withinCb = false;
00523   do {
00524     r = fromEnd ? prevSuitableLeafRenderObject(r) : nextSuitableLeafRenderObject(r);
00525     if (!r) break;
00526 
00527     withinCb = isDescendant(r, cb) && r != cb;
00528     tempTable = findTableUpTo(r, cb);
00529 
00530 #if DEBUG_CARETMODE > 1
00531     kdDebug(6201) << "r " << (r ? r->renderName() : QString::null) << "@" << r << endl;
00532 #endif
00533     if (r && withinCb && r->element() && !r->isTableCol()
00534         && (part->isCaretMode() || part->isEditable()
00535         || r->style()->userInput() == UI_ENABLED)) {
00536       table = tempTable;
00537       return true;
00538     }/*end if*/
00539 
00540   } while (withinCb);
00541   return false;
00542 }
00543 
00544 // == class LinearDocument implementation
00545 
00546 LinearDocument::LinearDocument(KHTMLPart *part, NodeImpl *node, long offset)
00547     : arena(0), node(node), offset(offset), m_part(part)
00548 {
00549   if (node == 0) return;
00550   sanitizeCaretState(this->node, this->offset);
00551 
00552   arena = new RenderArena(512);
00553 
00554   initPreBeginIterator();
00555   initEndIterator();
00556   //m_part = node->getDocument()->view()->part();
00557 }
00558 
00559 LinearDocument::~LinearDocument()
00560 {
00561   delete arena;
00562 }
00563 
00564 int LinearDocument::count() const
00565 {
00566   // FIXME: not implemented
00567   return 1;
00568 }
00569 
00570 LinearDocument::Iterator LinearDocument::current()
00571 {
00572   return LineIterator(this, node, offset);
00573 }
00574 
00575 LinearDocument::Iterator LinearDocument::begin()
00576 {
00577   DocumentImpl *doc = node ? node->getDocument() : 0;
00578   if (!doc) return end();
00579 
00580   NodeImpl *firstLeaf = doc->nextLeafNode();
00581   if (!firstLeaf) return end();     // must be empty document (is this possible?)
00582   return LineIterator(this, firstLeaf, firstLeaf->minOffset());
00583 }
00584 
00585 LinearDocument::Iterator LinearDocument::preEnd()
00586 {
00587   DocumentImpl *doc = node ? node->getDocument() : 0;
00588   if (!doc) return preBegin();
00589 
00590   NodeImpl *lastLeaf = doc;
00591   while (lastLeaf->lastChild()) lastLeaf = lastLeaf->lastChild();
00592 
00593   if (!lastLeaf) return preBegin(); // must be empty document (is this possible?)
00594   return LineIterator(this, lastLeaf, lastLeaf->maxOffset());
00595 }
00596 
00597 void LinearDocument::initPreBeginIterator()
00598 {
00599   _preBegin = LineIterator(this, 0, 0);
00600 }
00601 
00602 void LinearDocument::initEndIterator()
00603 {
00604   _end = LineIterator(this, 0, 1);
00605 }
00606 
00607 // == class LineIterator implementation
00608 
00609 LineIterator::LineIterator(LinearDocument *l, DOM::NodeImpl *node, long offset)
00610         : lines(l)
00611 {
00612 //  kdDebug(6200) << "LineIterator: node " << node << " offset " << offset << endl;
00613   flowBox = findFlowBox(node, offset, lines->arena, cb, &currentBox);
00614   if (!flowBox) {
00615 #if DEBUG_CARETMODE > 0
00616     kdDebug(6200) << "LineIterator: findFlowBox failed" << endl;
00617 #endif
00618     cb = 0;
00619   }/*end if*/
00620 }
00621 
00622 void LineIterator::nextBlock()
00623 {
00624   RenderObject *r = cb;
00625   RenderObject *n = r->lastChild();
00626   while (n) r = n, n = r->lastChild();
00627   r = nextSuitableLeafRenderObject(r);
00628 #if DEBUG_CARETMODE > 0
00629   kdDebug(6200) << "++: r " << r << "[" << (r?r->renderName():QString::null) << "]" << endl;
00630 #endif
00631   if (!r) {
00632     cb = 0;
00633     return;
00634   }/*end if*/
00635 
00636   // If we hit a leaf block (which can happen on empty blocks), use this
00637   // as its containing block
00638   if (r->isRenderBlock()) {
00639     cb = static_cast<RenderBlock *>(r);
00640 #if DEBUG_CARETMODE > 0
00641     kdDebug(6200) << "r->isFlow is cb. continuation @" << cb->continuation() << endl;
00642 #endif
00643     // Disregard empty continuations, they get the caret stuck otherwise.
00644     // This is because both cont_a and cont_o point to the same
00645     // DOM element. When the caret should move to cont_o, findFlowBox finds
00646     // cont_a, and the caret will be placed there.
00647     RenderFlow *flow = static_cast<RenderFlow *>(cb->element()
00648                 ? cb->element()->renderer() : 0);
00649     if (cb->continuation() || flow && flow->isRenderBlock() && flow != cb
00650             && flow->continuation()) {
00651       nextBlock();
00652       return;
00653     }/*end if*/
00654   } else {
00655     cb = static_cast<RenderFlow *>(r->containingBlock());
00656     if (!cb->isRenderBlock()) {
00657 #if DEBUG_CARETMODE > 0
00658       kdDebug(6200) << "dummy cb created " << cb << endl;
00659 #endif
00660       cb = generateDummyBlock(lines->arena, r);
00661     }/*end if*/
00662   }/*end if*/
00663   flowBox = cb->firstLineBox();
00664 #if DEBUG_CARETMODE > 0
00665   kdDebug(6200) << "++: flowBox " << flowBox << endl;
00666 #endif
00667 
00668   if (!flowBox) flowBox = generateDummyFlowBox(lines->arena, cb, r);
00669 #if DEBUG_CARETMODE > 0
00670   if (!cb->firstLineBox()) kdDebug(6200) << "++: dummy flowBox " << flowBox << endl;
00671 #endif
00672 }
00673 
00674 inline LineIterator &LineIterator::operator ++()
00675 {
00676   flowBox = static_cast<InlineFlowBox *>(flowBox->nextLineBox());
00677 
00678   // if there are no more lines in this block, begin with first line of
00679   // next block
00680   if (!flowBox) nextBlock();
00681 
00682   return *this;
00683 }
00684 
00685 inline LineIterator LineIterator::operator ++(int)
00686 {
00687   LineIterator it(*this);
00688   operator ++();
00689   return it;
00690 }
00691 
00692 void LineIterator::prevBlock()
00693 {
00694   RenderObject *r = cb;
00695   RenderObject *n = r->firstChild();
00696   while (n) r = n, n = r->firstChild();
00697   r = prevSuitableLeafRenderObject(r);
00698   if (!r) {
00699     cb = 0;
00700     return;
00701   }/*end if*/
00702 
00703   // If we hit a leaf block (which can happen on empty blocks), use this
00704   // as its containing block
00705   if (r->isRenderBlock()) {
00706     cb = static_cast<RenderFlow *>(r);
00707 #if DEBUG_CARETMODE > 0
00708     kdDebug(6200) << "r->isFlow is cb. continuation @" << cb->continuation() << endl;
00709 #endif
00710     // Disregard empty continuations, they get the caret stuck otherwise.
00711     // This is because both cont_a and cont_o point to the same
00712     // DOM element. When the caret should move to cont_o, findFlowBox finds
00713     // cont_a, and the caret will be placed there.
00714     RenderFlow *flow = static_cast<RenderFlow *>(cb->element()
00715                 ? cb->element()->renderer() : 0);
00716     if (cb->continuation() || flow && flow->isRenderBlock() && flow != cb
00717             && flow->continuation()) {
00718       prevBlock();
00719       return;
00720     }/*end if*/
00721   } else {
00722     cb = static_cast<RenderFlow *>(r->containingBlock());
00723     if (!cb->isRenderBlock()) {
00724 #if DEBUG_CARETMODE > 0
00725       kdDebug(6200) << "dummy cb created " << cb << endl;
00726 #endif
00727       cb = generateDummyBlock(lines->arena, r);
00728     }/*end if*/
00729   }/*end if*/
00730   flowBox = cb->lastLineBox();
00731 
00732   if (!flowBox) flowBox = generateDummyFlowBox(lines->arena, cb, r);
00733 }
00734 
00735 inline LineIterator &LineIterator::operator --()
00736 {
00737   flowBox = static_cast<InlineFlowBox *>(flowBox->prevLineBox());
00738 
00739   // if there are no more lines in this block, begin with last line of
00740   // previous block
00741   if (!flowBox) prevBlock();
00742 
00743   return *this;
00744 }
00745 
00746 inline LineIterator LineIterator::operator --(int)
00747 {
00748   LineIterator it(*this);
00749   operator --();
00750   return it;
00751 }
00752 
00753 #if 0 // not implemented because it's not needed
00754 LineIterator LineIterator::operator +(int /*summand*/) const
00755 {
00756   // FIXME: not implemented
00757   return LineIterator();
00758 }
00759 
00760 LineIterator LineIterator::operator -(int /*summand*/) const
00761 {
00762   // FIXME: not implemented
00763   return LineIterator();
00764 }
00765 #endif
00766 
00767 LineIterator &LineIterator::operator +=(int summand)
00768 {
00769   if (summand > 0)
00770     while (summand-- && *this != lines->end()) ++*this;
00771   else if (summand < 0)
00772     operator -=(-summand);
00773   return *this;
00774 }
00775 
00776 LineIterator &LineIterator::operator -=(int summand)
00777 {
00778   if (summand > 0)
00779     while (summand-- && *this != lines->preBegin()) --*this;
00780   else if (summand < 0)
00781     operator +=(-summand);
00782   return *this;
00783 }
00784 
00785 // == class EditableCharacterIterator implementation
00786 
00787 void EditableCharacterIterator::initFirstChar()
00788 {
00789   InlineBox *b = *ebit;
00790   if (b) {
00791     if (_offset == b->maxOffset())
00792       peekNext();
00793     else if (b->isInlineTextBox())
00794       _char = static_cast<RenderText *>(b->object())->str->s[_offset].unicode();
00795     else
00796       _char = -1;
00797   }/*end if*/
00798 }
00799 
00800 EditableCharacterIterator &EditableCharacterIterator::operator ++()
00801 {
00802   _offset++;
00803 
00804   InlineBox *b = *ebit;
00805   RenderObject *r = b->object();
00806   // BRs have no extent, so their maximum offset must be their minimum.
00807   // A block element can only be the target if it is empty -- in this case
00808   // its extent is zero, too.
00809   long maxofs = r->isBR() || r->isRenderBlock() ? b->minOffset() : b->maxOffset();
00810 #if DEBUG_CARETMODE > 0
00811   kdDebug(6200) << "b->maxOffset() " << b->maxOffset() << " b->minOffset() " << b->minOffset() << endl;
00812 #endif
00813   if (_offset == maxofs) {
00814 #if DEBUG_CARETMODE > 2
00815 kdDebug(6200) << "_offset == maxofs: " << _offset << " == " << maxofs << endl;
00816 #endif
00817 //    _peekPrev = b;
00818     peekNext();
00819   } else if (_offset > maxofs) {
00820 #if DEBUG_CARETMODE > 2
00821 kdDebug(6200) << "_offset > maxofs: " << _offset << " > " << maxofs /*<< " _peekNext: " << _peekNext*/ << endl;
00822 #endif
00823     if (true) {
00824       if (*ebit)
00825         ++ebit;
00826       if (!*ebit) {     // end of line reached, go to next line
00827         ++_it;
00828 #if DEBUG_CARETMODE > 3
00829 kdDebug(6200) << "++_it" << endl;
00830 #endif
00831         if (_it != ld->end()) {
00832       ebit = _it;
00833           b = *ebit;
00834 #if DEBUG_CARETMODE > 3
00835 kdDebug(6200) << "b " << b << " isText " << b->isInlineTextBox() << endl;
00836 #endif
00837       _node = b->object()->element();
00838 #if DEBUG_CARETMODE > 3
00839 kdDebug(6200) << "_node " << _node << ":" << _node->nodeName().string() << endl;
00840 #endif
00841       _offset = b->minOffset();
00842 #if DEBUG_CARETMODE > 3
00843 kdDebug(6200) << "_offset " << _offset << endl;
00844 #endif
00845     } else {
00846       _node = 0;
00847       b = 0;
00848     }/*end if*/
00849         goto readchar;
00850       }/*end if*/
00851     }/*end if*/
00852     bool adjacent = ebit.isAdjacent();
00853     // Jump over element if this one is not a text node.
00854     if (adjacent && !(*ebit)->isInlineTextBox()) {
00855       EditableInlineBoxIterator copy = ebit;
00856       ++ebit;
00857       if (*ebit && (*ebit)->isInlineTextBox()) adjacent = false;
00858       else ebit = copy;
00859     }/*end if*/
00860     _node = (*ebit)->object()->element();
00861     _offset = (*ebit)->minOffset() + adjacent;
00862     //_peekNext = 0;
00863     b = *ebit;
00864     goto readchar;
00865   } else {
00866 readchar:
00867     // get character
00868     if (b && b->isInlineTextBox() && _offset < b->maxOffset())
00869       _char = static_cast<RenderText *>(b->object())->str->s[_offset].unicode();
00870     else
00871       _char = -1;
00872   }/*end if*/
00873 #if DEBUG_CARETMODE > 2
00874 kdDebug(6200) << "_offset: " << _offset /*<< " _peekNext: " << _peekNext*/ << " char '" << (char)_char << "'" << endl;
00875 #endif
00876 
00877 #if DEBUG_CARETMODE > 0
00878   if (*ebit) {
00879     InlineBox *box = *ebit;
00880     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) << " node " << (_node ? _node->nodeName().string() : QString("<nil>")) << endl;
00881   }
00882 #endif
00883   return *this;
00884 }
00885 
00886 EditableCharacterIterator &EditableCharacterIterator::operator --()
00887 {
00888   _offset--;
00889   //kdDebug(6200) << "--: _offset=" << _offset << endl;
00890 
00891   InlineBox *b = *ebit;
00892   InlineBox *_peekPrev = 0;
00893   InlineBox *_peekNext = 0;
00894   long minofs = b ? b->minOffset() : _offset + 1;
00895 #if DEBUG_CARETMODE > 0
00896   kdDebug(6200) << "b->maxOffset() " << b->maxOffset() << " b->minOffset() " << b->minOffset() << endl;
00897 #endif
00898   if (_offset == minofs) {
00899 #if DEBUG_CARETMODE > 2
00900 kdDebug(6200) << "_offset == minofs: " << _offset << " == " << minofs << endl;
00901 #endif
00902     _peekNext = b;
00903     // get character
00904     if (b && b->isInlineTextBox())
00905       _char = static_cast<RenderText *>(b->object())->text()[_offset].unicode();
00906     else
00907       _char = -1;
00908 
00909     //peekPrev();
00910     bool do_prev = false;
00911     {
00912       EditableInlineBoxIterator copy = ebit;
00913       --ebit;
00914       _peekPrev = *ebit;
00915       // Jump to end of previous element if it's adjacent, and a text box
00916       if (ebit.isAdjacent() && *ebit && (*ebit)->isInlineTextBox())
00917         //operator --();
00918     do_prev = true;
00919       else
00920         ebit = copy;
00921     }
00922     if (do_prev) goto prev;
00923   } else if (_offset < minofs) {
00924 prev:
00925 #if DEBUG_CARETMODE > 2
00926 kdDebug(6200) << "_offset < minofs: " << _offset << " < " << minofs /*<< " _peekNext: " << _peekNext*/ << endl;
00927 #endif
00928     if (!_peekPrev) {
00929       _peekNext = *ebit;
00930       if (*ebit)
00931         --ebit;
00932       if (!*ebit) {     // end of line reached, go to previous line
00933         --_it;
00934 #if DEBUG_CARETMODE > 3
00935 kdDebug(6200) << "--_it" << endl;
00936 #endif
00937         if (_it != ld->preBegin()) {
00938 //    kdDebug(6200) << "begin from end!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << endl;
00939       ebit = EditableInlineBoxIterator(_it, true);
00940       RenderObject *r = (*ebit)->object();
00941 #if DEBUG_CARETMODE > 3
00942 kdDebug(6200) << "b " << *ebit << " isText " << (*ebit)->isInlineTextBox() << endl;
00943 #endif
00944       _node = r->element();
00945       _offset = r->isBR() ? (*ebit)->minOffset() : (*ebit)->maxOffset();
00946       _char = -1;
00947 #if DEBUG_CARETMODE > 0
00948           {InlineBox *box = *ebit; kdDebug(6200) << "echit--(2): 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) << endl;}
00949 #endif
00950     } else
00951       _node = 0;
00952     return *this;
00953       }/*end if*/
00954     }/*end if*/
00955 
00956     bool adjacent = ebit.isAdjacent();
00957     // Ignore this box if it isn't a text box, but the previous box was
00958 #if DEBUG_CARETMODE > 0
00959     kdDebug(6200) << "adjacent " << adjacent << " _peekNext " << _peekNext << " _peekNext->isInlineTextBox: " << (_peekNext ? _peekNext->isInlineTextBox() : false) << " !((*ebit)->isInlineTextBox): " << (*ebit ? !(*ebit)->isInlineTextBox() : true) << endl;
00960 #endif
00961     if (adjacent && _peekNext && _peekNext->isInlineTextBox()
00962         && !(*ebit)->isInlineTextBox()) {
00963       EditableInlineBoxIterator copy = ebit;
00964       --ebit;
00965       if (!*ebit) /*adjacent = false;
00966       else */ebit = copy;
00967     }/*end if*/
00968 #if DEBUG_CARETMODE > 0
00969     kdDebug(6200) << "(*ebit)->obj " << (*ebit)->object()->renderName() << "[" << (*ebit)->object() << "]" << " minOffset: " << (*ebit)->minOffset() << " maxOffset: " << (*ebit)->maxOffset() << endl;
00970 #endif
00971     _node = (*ebit)->object()->element();
00972 #if DEBUG_CARETMODE > 3
00973 kdDebug(6200) << "_node " << _node << ":" << _node->nodeName().string() << endl;
00974 #endif
00975     _offset = (*ebit)->maxOffset()/* - adjacent*/;
00976 #if DEBUG_CARETMODE > 3
00977 kdDebug(6200) << "_offset " << _offset << endl;
00978 #endif
00979     _peekPrev = 0;
00980   } else {
00981 #if DEBUG_CARETMODE > 0
00982 kdDebug(6200) << "_offset: " << _offset << " _peekNext: " << _peekNext << endl;
00983 #endif
00984     // get character
00985     if (_peekNext && _offset >= b->maxOffset() && _peekNext->isInlineTextBox())
00986       _char = static_cast<RenderText *>(_peekNext->object())->text()[_peekNext->minOffset()].unicode();
00987     else if (b && _offset < b->maxOffset() && b->isInlineTextBox())
00988       _char = static_cast<RenderText *>(b->object())->text()[_offset].unicode();
00989     else
00990       _char = -1;
00991   }/*end if*/
00992 
00993 #if DEBUG_CARETMODE > 0
00994   if (*ebit) {
00995     InlineBox *box = *ebit;
00996     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) << endl;
00997   }
00998 #endif
00999   return *this;
01000 }
01001 
01002 // == class TableRowIterator implementation
01003 
01004 TableRowIterator::TableRowIterator(RenderTable *table, bool fromEnd,
01005         RenderTableSection::RowStruct *row)
01006         : sec(table, fromEnd)
01007 {
01008   // set index
01009   if (*sec) {
01010     if (fromEnd) index = (*sec)->grid.size() - 1;
01011     else index = 0;
01012   }/*end if*/
01013 
01014   // initialize with given row
01015   if (row && *sec) {
01016     while (operator *() != row)
01017       if (fromEnd) operator --(); else operator ++();
01018   }/*end if*/
01019 }
01020 
01021 TableRowIterator &TableRowIterator::operator ++()
01022 {
01023   index++;
01024 
01025   if (index >= (int)(*sec)->grid.size()) {
01026     ++sec;
01027 
01028     if (*sec) index = 0;
01029   }/*end if*/
01030   return *this;
01031 }
01032 
01033 TableRowIterator &TableRowIterator::operator --()
01034 {
01035   index--;
01036 
01037   if (index < 0) {
01038     --sec;
01039 
01040     if (*sec) index = (*sec)->grid.size() - 1;
01041   }/*end if*/
01042   return *this;
01043 }
01044 
01045 // == class ErgonomicEditableLineIterator implementation
01046 
01047 // some decls
01048 static RenderTableCell *findNearestTableCellInRow(KHTMLPart *part, int x,
01049         RenderTableSection::RowStruct *row, bool fromEnd);
01050 
01064 static inline RenderTableCell *findNearestTableCell(KHTMLPart *part, int x,
01065         TableRowIterator &it, bool fromEnd)
01066 {
01067   RenderTableCell *result = 0;
01068 
01069   while (*it) {
01070     result = findNearestTableCellInRow(part, x, *it, fromEnd);
01071     if (result) break;
01072 
01073     if (fromEnd) --it; else ++it;
01074   }/*wend*/
01075 
01076   return result;
01077 }
01078 
01092 static RenderTableCell *findNearestTableCellInRow(KHTMLPart *part, int x,
01093         RenderTableSection::RowStruct *row, bool fromEnd)
01094 {
01095   // First pass. Find spatially nearest cell.
01096   int n = (int)row->row->size();
01097   int i;
01098   for (i = 0; i < n; i++) {
01099     RenderTableCell *cell = row->row->at(i);
01100     if (!cell || (int)cell == -1) continue;
01101 
01102     int absx, absy;
01103     cell->absolutePosition(absx, absy, false); // ### position: fixed?
01104 #if DEBUG_CARETMODE > 1
01105     kdDebug(6201) << "i/n " << i << "/" << n << " absx " << absx << " absy " << absy << endl;
01106 #endif
01107 
01108     // I rely on the assumption that all cells are in ascending visual order
01109     // ### maybe this assumption is wrong for bidi?
01110 #if DEBUG_CARETMODE > 1
01111     kdDebug(6201) << "x " << x << " < " << (absx + cell->width()) << "?" << endl;
01112 #endif
01113     if (x < absx + cell->width()) break;
01114   }/*next i*/
01115   if (i >= n) i = n - 1;
01116 
01117   // Second pass. Find editable cell, beginning with the currently found,
01118   // extending to the left, and to the right, alternating.
01119   for (int cnt = 0; cnt < 2*n; cnt++) {
01120     int index = i - ((cnt >> 1) + 1)*(cnt & 1) + (cnt >> 1)*!(cnt & 1);
01121     if (index < 0 || index >= n) continue;
01122 
01123     RenderTableCell *cell = row->row->at(index);
01124     if (!cell || (int)cell == -1) continue;
01125 
01126 #if DEBUG_CARETMODE > 1
01127     kdDebug(6201) << "index " << index << " cell " << cell << endl;
01128 #endif
01129     RenderTable *nestedTable;
01130     if (containsEditableElement(part, cell, nestedTable, fromEnd)) {
01131 
01132       if (nestedTable) {
01133         TableRowIterator it(nestedTable, fromEnd);
01134     while (*it) {
01135       cell = findNearestTableCell(part, x, it, fromEnd);
01136       if (cell) break;
01137       if (fromEnd) --it; else ++it;
01138     }/*wend*/
01139       }/*end if*/
01140 
01141       return cell;
01142     }/*end if*/
01143   }/*next i*/
01144   return 0;
01145 }
01146 
01153 static RenderObject *commonAncestorTableSectionOrCell(RenderObject *r1,
01154         RenderObject *r2)
01155 {
01156   if (!r1 || !r2) return 0;
01157   RenderTableSection *sec = 0;
01158   int start_depth=0, end_depth=0;
01159   // First we find the depths of the two objects in the tree (start_depth, end_depth)
01160   RenderObject *n = r1;
01161   while (n->parent()) {
01162     n = n->parent();
01163     start_depth++;
01164   }/*wend*/
01165   n = r2;
01166   while( n->parent()) {
01167     n = n->parent();
01168     end_depth++;
01169   }/*wend*/
01170   // here we climb up the tree with the deeper object, until both objects have equal depth
01171   while (end_depth > start_depth) {
01172     r2 = r2->parent();
01173     end_depth--;
01174   }/*wend*/
01175   while (start_depth > end_depth) {
01176     r1 = r1->parent();
01177 //    if (r1->isTableSection()) sec = static_cast<RenderTableSection *>(r1);
01178     start_depth--;
01179   }/*wend*/
01180   // Climb the tree with both r1 and r2 until they are the same
01181   while (r1 != r2){
01182     r1 = r1->parent();
01183     if (r1->isTableSection()) sec = static_cast<RenderTableSection *>(r1);
01184     r2 = r2->parent();
01185   }/*wend*/
01186 
01187   // At this point, we found the most approximate common ancestor. Now climb
01188   // up until the condition of the function return value is satisfied.
01189   while (r1 && !r1->isTableCell() && !r1->isTableSection() && !r1->isTable())
01190     r1 = r1->parent();
01191 
01192   return r1 && r1->isTable() ? sec : r1;
01193 }
01194 
01202 static int findRowInSection(RenderTableSection *section, RenderTableCell *cell,
01203         RenderTableSection::RowStruct *&row, RenderTableCell *&directCell)
01204 {
01205   // Seek direct cell
01206   RenderObject *r = cell;
01207   while (r != section) {
01208     if (r->isTableCell()) directCell = static_cast<RenderTableCell *>(r);
01209     r = r->parent();
01210   }/*wend*/
01211 
01212   // So, and this is really nasty: As we have no indices, we have to do a
01213   // linear comparison. Oh, that sucks so much for long tables, you can't
01214   // imagine.
01215   int n = section->numRows();
01216   for (int i = 0; i < n; i++) {
01217     row = &section->grid[i];
01218 
01219     // check for cell
01220     int m = row->row->size();
01221     for (int j = 0; j < m; j++) {
01222       RenderTableCell *c = row->row->at(j);
01223       if (c == directCell) return i;
01224     }/*next j*/
01225 
01226   }/*next i*/
01227   Q_ASSERT(false);
01228   return -1;
01229 }
01230 
01236 static inline RenderTable *findFirstDescendantTable(RenderObject *leaf, RenderFlow *block)
01237 {
01238   RenderTable *result = 0;
01239   while (leaf && leaf != block) {
01240     if (leaf->isTable()) result = static_cast<RenderTable *>(leaf);
01241     leaf = leaf->parent();
01242   }/*wend*/
01243   return result;
01244 }
01245 
01249 static inline RenderTableCell *containingTableCell(RenderObject *r)
01250 {
01251   while (r && !r->isTableCell()) r = r->parent();
01252   return static_cast<RenderTableCell *>(r);
01253 }
01254 
01255 inline void ErgonomicEditableLineIterator::calcAndStoreNewLine(
01256             RenderFlow *newBlock, bool toBegin)
01257 {
01258   // take the first/last editable element in the found cell as the new
01259   // value for the iterator
01260   cb = newBlock;
01261   if (toBegin) prevBlock(); else nextBlock();
01262 
01263   if (!cb) {
01264     flowBox = 0;
01265     return;
01266   }/*end if*/
01267 
01268   if (!isEditable(*this)) {
01269     if (toBegin) EditableLineIterator::operator --();
01270     else EditableLineIterator::operator ++();
01271   }/*end if*/
01272 }
01273 
01274 void ErgonomicEditableLineIterator::determineTopologicalElement(
01275         RenderTableCell *oldCell, RenderObject *newObject, bool toBegin)
01276 {
01277   // When we arrive here, a transition between cells has happened.
01278   // Now determine the type of the transition. This can be
01279   // (1) a transition from this cell into a table inside this cell.
01280   // (2) a transition from this cell into another cell of this table
01281 
01282   TableRowIterator it;
01283 
01284   RenderObject *commonAncestor = commonAncestorTableSectionOrCell(oldCell, newObject);
01285 #if DEBUG_CARETMODE > 1
01286   kdDebug(6201) << " ancestor " << commonAncestor << endl;
01287 #endif
01288 
01289   // The whole document is treated as a table cell.
01290   if (!commonAncestor || commonAncestor->isTableCell()) {   // (1)
01291 
01292     RenderTableCell *cell = static_cast<RenderTableCell *>(commonAncestor);
01293     RenderTable *table = findFirstDescendantTable(newObject, cell);
01294 
01295 #if DEBUG_CARETMODE > 0
01296     kdDebug(6201) << "table cell: " << cell << endl;
01297 #endif
01298 
01299     // if there is no table, we fell out of the previous table, and are now
01300     // in some table-less block. Therefore, done.
01301     if (!table) return;
01302 
01303     it = TableRowIterator(table, toBegin);
01304 
01305   } else if (commonAncestor->isTableSection()) {        // (2)
01306 
01307     RenderTableSection *section = static_cast<RenderTableSection *>(commonAncestor);
01308     RenderTableSection::RowStruct *row;
01309     int idx = findRowInSection(section, oldCell, row, oldCell);
01310 #if DEBUG_CARETMODE > 1
01311     kdDebug(6201) << "table section: row idx " << idx << endl;
01312 #endif
01313 
01314     it = TableRowIterator(section, idx);
01315 
01316     // advance rowspan rows
01317     int rowspan = oldCell->rowSpan();
01318     while (*it && rowspan--) {
01319       if (toBegin) --it; else ++it;
01320     }/*wend*/
01321 
01322   } else {
01323     kdError(6201) << "Neither common cell nor section! " << commonAncestor->renderName() << endl;
01324     // will crash on uninitialized table row iterator
01325   }/*end if*/
01326 
01327   RenderTableCell *cell = findNearestTableCell(lines->m_part, xCoor, it, toBegin);
01328 #if DEBUG_CARETMODE > 1
01329   kdDebug(6201) << "findNearestTableCell result: " << cell << endl;
01330 #endif
01331 
01332   RenderFlow *newBlock = cell;
01333   if (!cell) {
01334     Q_ASSERT(commonAncestor->isTableSection());
01335     RenderTableSection *section = static_cast<RenderTableSection *>(commonAncestor);
01336     cell = containingTableCell(section);
01337 #if DEBUG_CARETMODE > 1
01338     kdDebug(6201) << "containing cell: " << cell << endl;
01339 #endif
01340 
01341     RenderTable *nestedTable;
01342     bool editableChild = cell && containsEditableChildElement(lines->m_part,
01343             cell, nestedTable, toBegin, section->table());
01344 
01345     if (cell && !editableChild) {
01346 #if DEBUG_CARETMODE > 1
01347       kdDebug(6201) << "========= recursive invocation outer =========" << endl;
01348 #endif
01349       determineTopologicalElement(cell, cell->section(), toBegin);
01350 #if DEBUG_CARETMODE > 1
01351       kdDebug(6201) << "========= end recursive invocation outer =========" << endl;
01352 #endif
01353       return;
01354 
01355     } else if (cell && nestedTable) {
01356 #if DEBUG_CARETMODE > 1
01357       kdDebug(6201) << "========= recursive invocation inner =========" << endl;
01358 #endif
01359       determineTopologicalElement(cell, nestedTable, toBegin);
01360 #if DEBUG_CARETMODE > 1
01361       kdDebug(6201) << "========= end recursive invocation inner =========" << endl;
01362 #endif
01363       return;
01364 
01365     } else {
01366 #if DEBUG_CARETMODE > 1
01367       kdDebug(6201) << "newBlock is table: " << section->table() << endl;
01368 #endif
01369       newBlock = section->table();
01370 //      if (toBegin) prevBlock(); else nextBlock();
01371     }/*end if*/
01372   } else {
01373     // adapt cell so that prevBlock/nextBlock works as expected
01374     RenderObject *r = cell;
01375     if (toBegin) {
01376       while (r->lastChild()) r = r->lastChild();
01377       r = nextSuitableLeafRenderObject(r);
01378     } else
01379       r = prevSuitableLeafRenderObject(r);
01380     newBlock = static_cast<RenderFlow *>(!r || r->isRenderBlock() ? r : r->containingBlock());
01381   }/*end if*/
01382 
01383   calcAndStoreNewLine(newBlock, toBegin);
01384 }
01385 
01386 ErgonomicEditableLineIterator &ErgonomicEditableLineIterator::operator ++()
01387 {
01388   RenderTableCell *oldCell = containingTableCell(cb);
01389 
01390   EditableLineIterator::operator ++();
01391   if (*this == lines->end() || *this == lines->preBegin()) return *this;
01392 
01393   RenderTableCell *newCell = containingTableCell(cb);
01394 
01395   if (!newCell || newCell == oldCell) return *this;
01396 
01397   determineTopologicalElement(oldCell, newCell, false);
01398 
01399   return *this;
01400 }
01401 
01402 ErgonomicEditableLineIterator &ErgonomicEditableLineIterator::operator --()
01403 {
01404   RenderTableCell *oldCell = containingTableCell(cb);
01405 
01406   EditableLineIterator::operator --();
01407   if (*this == lines->end() || *this == lines->preBegin()) return *this;
01408 
01409   RenderTableCell *newCell = containingTableCell(cb);
01410 
01411   if (!newCell || newCell == oldCell) return *this;
01412 
01413   determineTopologicalElement(oldCell, newCell, true);
01414 
01415   return *this;
01416 }
01417 
01418 // == Navigational helper functions ==
01419 
01429 static InlineBox *nearestInlineBox(LineIterator &it, CaretViewContext *cv,
01430     int &x, int &absx, int &absy)
01431 {
01432   InlineFlowBox *fbox = *it;
01433 
01434   // Find containing block
01435   RenderObject *cb = fbox->object();
01436 
01437   if (cb) cb->absolutePosition(absx, absy);
01438   else absx = absy = 0;
01439 
01440   // Otherwise find out in which inline box the caret is to be placed.
01441 
01442   // this horizontal position is to be approximated
01443   x = cv->origX - absx;
01444   InlineBox *caretBox = 0; // Inline box containing the caret
01445 //  NodeImpl *lastnode = 0;  // node of previously checked render object.
01446   int xPos;        // x-coordinate of current inline box
01447   int oldXPos = -1;    // x-coordinate of last inline box
01448   EditableInlineBoxIterator fbit = it;
01449 #if DEBUG_CARETMODE > 0
01450   kdDebug(6200) << "*fbit = " << *fbit << endl;
01451 #endif
01452   // Either iterate through all children or take the flow box itself as a
01453   // child if it has no children
01454   for (InlineBox *b; *fbit != 0; ++fbit) {
01455     b = *fbit;
01456 
01457 //    RenderObject *r = b->object();
01458 #if DEBUG_CARETMODE > 0
01459     if (b->isInlineFlowBox()) kdDebug(6200) << "b is inline flow box" << endl;
01460 //  kdDebug(6200) << "approximate r" << r << ": " << (r ? r->renderName() : QString::null) << (r && r->isText() ? " contains \"" + QString(((RenderText *)r)->str->s, ((RenderText *)r)->str->l) + "\"" : QString::null) << endl;
01461 #endif
01462 //    NodeImpl *node = r->element();
01463     xPos = b->xPos();
01464 
01465     // the caret is before this box
01466     if (x < xPos) {
01467       // snap to nearest box
01468       if (oldXPos < 0 || x - (oldXPos + caretBox->width()) > xPos - x) {
01469     caretBox = b;   // current box is nearer
01470 //        lastnode = node;
01471       }/*end if*/
01472       break;        // Otherwise, preceding box is implicitly used
01473     }
01474 
01475     caretBox = b;
01476 //    lastnode = node;
01477 
01478     // the caret is within this box
01479     if (x >= xPos && x < xPos + caretBox->width())
01480       break;
01481     oldXPos = xPos;
01482 
01483     // the caret can only be after the last box which is automatically
01484     // contained in caretBox when we fall out of the loop.
01485 
01486     if (b == fbox) break;
01487   }/*next fbit*/
01488 
01489   return caretBox;
01490 }
01491 
01497 static void moveItToNextWord(EditableCharacterIterator &it)
01498 {
01499 #if DEBUG_CARETMODE > 0
01500   kdDebug(6200) << "%%%%%%%%%%%%%%%%%%%%% moveItToNextWord" << endl;
01501 #endif
01502   EditableCharacterIterator copy;
01503   while (it.node() && !(*it).isSpace() && !(*it).isPunct()) {
01504 #if DEBUG_CARETMODE > 2
01505     kdDebug(6200) << "reading1 '" << (*it).latin1() << "'" << endl;
01506 #endif
01507     copy = it;
01508     ++it;
01509   }
01510 
01511   if (!it.node()) {
01512     it = copy;
01513     return;
01514   }/*end if*/
01515 
01516   while (it.node() && ((*it).isSpace() || (*it).isPunct())) {
01517 #if DEBUG_CARETMODE > 2
01518     kdDebug(6200) << "reading2 '" << (*it).latin1() << "'" << endl;
01519 #endif
01520     copy = it;
01521     ++it;
01522   }
01523 
01524   if (!it.node()) it = copy;
01525 }
01526 
01532 static void moveItToPrevWord(EditableCharacterIterator &it)
01533 {
01534   if (!it.node()) return;
01535 
01536 #if DEBUG_CARETMODE > 0
01537   kdDebug(6200) << "%%%%%%%%%%%%%%%%%%%%% moveItToPrevWord" << endl;
01538 #endif
01539   EditableCharacterIterator copy;
01540 
01541   // Jump over all space and punctuation characters first
01542   do {
01543     copy = it;
01544     --it;
01545 #if DEBUG_CARETMODE > 2
01546     if (it.node()) kdDebug(6200) << "reading1 '" << (*it).latin1() << "'" << endl;
01547 #endif
01548   } while (it.node() && ((*it).isSpace() || (*it).isPunct()));
01549 
01550   if (!it.node()) {
01551     it = copy;
01552     return;
01553   }/*end if*/
01554 
01555   do {
01556     copy = it;
01557     --it;
01558 #if DEBUG_CARETMODE > 0
01559     if (it.node()) kdDebug(6200) << "reading2 '" << (*it).latin1() << "'" << endl;
01560 #endif
01561   } while (it.node() && !(*it).isSpace() && !(*it).isPunct());
01562 
01563   it = copy;
01564 }
01565 
01566 
01574 static void moveIteratorByPage(LinearDocument &ld,
01575         ErgonomicEditableLineIterator &it, int mindist, bool next)
01576 {
01577   if (it == ld.end() || it == ld.preBegin()) return;
01578 
01579   ErgonomicEditableLineIterator copy = it;
01580 #if DEBUG_CARETMODE > 0
01581   kdDebug(6200) << " mindist: " << mindist << endl;
01582 #endif
01583 
01584   InlineFlowBox *flowBox = *copy;
01585   int absx = 0, absy = 0;
01586 
01587   RenderFlow *lastcb = static_cast<RenderFlow *>(flowBox->object());
01588   Q_ASSERT(lastcb->isRenderBlock());
01589   lastcb->absolutePosition(absx, absy, false);  // ### what about fixed?
01590 
01591   // ### actually flowBox->yPos() should suffice, but this is not ported
01592   // over yet from WebCore
01593   int lastfby = flowBox->firstChild()->yPos();
01594   int lastheight = 0;
01595   do {
01596     if (next) ++copy; else --copy;
01597     if (copy == ld.end() || copy == ld.preBegin()) break;
01598 
01599     // ### change to RootInlineBox after full WebCore merge
01600     flowBox = static_cast<InlineFlowBox *>(*copy);
01601     Q_ASSERT(flowBox->isInlineFlowBox());
01602 
01603     RenderFlow *cb = static_cast<RenderFlow *>(flowBox->object());
01604     Q_ASSERT(cb->isRenderBlock());
01605 
01606     int diff = 0;
01607     // ### actually flowBox->yPos() should suffice, but this is not ported
01608     // over yet from WebCore
01609     int fby = flowBox->firstChild()->yPos();
01610     if (cb != lastcb) {
01611       if (next) {
01612         diff = absy + lastfby + lastheight;
01613         cb->absolutePosition(absx, absy, false);    // ### what about fixed?
01614         diff = absy - diff + fby;
01615         lastfby = 0;
01616       } else {
01617         diff = absy;
01618         cb->absolutePosition(absx, absy, false);    // ### what about fixed?
01619         diff -= absy + fby + lastheight;
01620     lastfby = fby - lastheight;
01621       }/*end if*/
01622 #if DEBUG_CARETMODE > 2
01623       kdDebug(6200) << "absdiff " << diff << endl;
01624 #endif
01625     } else {
01626       diff = QABS(fby - lastfby);
01627     }/*end if*/
01628 #if DEBUG_CARETMODE > 2
01629     kdDebug(6200) << "flowBox->firstChild->yPos: " << fby << " diff " << diff << endl;
01630 #endif
01631 
01632     mindist -= diff;
01633 
01634     lastheight = QABS(fby - lastfby);
01635     lastfby = fby;
01636     lastcb = cb;
01637     it = copy;
01638 #if DEBUG_CARETMODE > 0
01639     kdDebug(6200) << " mindist: " << mindist << endl;
01640 #endif
01641     // trick: actually the distance is always one line short, but we cannot
01642     // calculate the height of the first line (### WebCore will make it better)
01643     // Therefore, we simply approximate that excess line by using the last
01644     // caluculated line height.
01645   } while (mindist - lastheight > 0);
01646 }
01647 
01648 
01649 }/*end namespace*/
KDE Logo
This file is part of the documentation for khtml Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed May 12 09:09:48 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2003