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;
#endif
    if (r && withinCb && r->element() && !r->isTableCol()
        && (part->isCaretMode() || part->isEditable()
        || r->style()->userInput() == UI_ENABLED)) {
      table = tempTable;
      return true;
    }/*end if*/

    r = fromEnd ? prevSuitableLeafRenderObject(r) : nextSuitableLeafRenderObject(r);
  } while (r && withinCb);
  return false;
}

00496 static bool containsEditableChildElement(KHTMLPart *part, RenderFlow *cb,
    RenderTable *&table, bool fromEnd, RenderObject *start)
{
  RenderObject *r = start;
  if (fromEnd)
    while (r->firstChild()) r = r->firstChild();
  else
    while (r->lastChild()) r = r->lastChild();

  if (!r) return false;

  RenderTable *tempTable = 0;
  table = 0;
  bool withinCb = false;
  do {
    r = fromEnd ? prevSuitableLeafRenderObject(r) : nextSuitableLeafRenderObject(r);
    if (!r) break;

    withinCb = isDescendant(r, cb) && r != cb;
    tempTable = findTableUpTo(r, cb);

#if DEBUG_CARETMODE > 1
    kdDebug(6201) << "r " << (r ? r->renderName() : QString::null) << "@" << r << endl;
#endif
    if (r && withinCb && r->element() && !r->isTableCol()
        && (part->isCaretMode() || part->isEditable()
        || r->style()->userInput() == UI_ENABLED)) {
      table = tempTable;
      return true;
    }/*end if*/

  } while (withinCb);
  return false;
}

// == class LinearDocument implementation

LinearDocument::LinearDocument(KHTMLPart *part, NodeImpl *node, long offset)
    : arena(0), node(node), offset(offset), m_part(part)
{
  if (node == 0) return;
  sanitizeCaretState(this->node, this->offset);

  arena = new RenderArena(512);

  initPreBeginIterator();
  initEndIterator();
  //m_part = node->getDocument()->view()->part();
}

LinearDocument::~LinearDocument()
{
  delete arena;
}

int LinearDocument::count() const
{
  // FIXME: not implemented
  return 1;
}

LinearDocument::Iterator LinearDocument::current()
{
  return LineIterator(this, node, offset);
}

LinearDocument::Iterator LinearDocument::begin()
{
  DocumentImpl *doc = node ? node->getDocument() : 0;
  if (!doc) return end();

  NodeImpl *firstLeaf = doc->nextLeafNode();
  if (!firstLeaf) return end();     // must be empty document (is this possible?)
  return LineIterator(this, firstLeaf, firstLeaf->minOffset());
}

LinearDocument::Iterator LinearDocument::preEnd()
{
  DocumentImpl *doc = node ? node->getDocument() : 0;
  if (!doc) return preBegin();

  NodeImpl *lastLeaf = doc;
  while (lastLeaf->lastChild()) lastLeaf = lastLeaf->lastChild();

  if (!lastLeaf) return preBegin(); // must be empty document (is this possible?)
  return LineIterator(this, lastLeaf, lastLeaf->maxOffset());
}

void LinearDocument::initPreBeginIterator()
{
  _preBegin = LineIterator(this, 0, 0);
}

void LinearDocument::initEndIterator()
{
  _end = LineIterator(this, 0, 1);
}

// == class LineIterator implementation

LineIterator::LineIterator(LinearDocument *l, DOM::NodeImpl *node, long offset)
        : lines(l)
{
//  kdDebug(6200) << "LineIterator: node " << node << " offset " << offset << endl;
00497   flowBox = findFlowBox(node, offset, lines->arena, cb, &currentBox);
00498   if (!flowBox) {
00499 #if DEBUG_CARETMODE > 0
00500     kdDebug(6200) << "LineIterator: findFlowBox failed" << endl;
00501 #endif
00502     cb = 0;
00503   }/*end if*/
00504 }
00505 
00506 void LineIterator::nextBlock()
00507 {
00508   RenderObject *r = cb;
00509   RenderObject *n = r->lastChild();
00510   while (n) r = n, n = r->lastChild();
00511   r = nextSuitableLeafRenderObject(r);
00512 #if DEBUG_CARETMODE > 0
00513   kdDebug(6200) << "++: r " << r << "[" << (r?r->renderName():QString::null) << "]" << endl;
00514 #endif
00515   if (!r) {
00516     cb = 0;
00517     return;
00518   }/*end if*/
00519 
00520   // If we hit a leaf block (which can happen on empty blocks), use this
00521   // as its containing block
00522   if (r->isRenderBlock()) {
00523     cb = static_cast<RenderBlock *>(r);
00524 #if DEBUG_CARETMODE > 0
00525     kdDebug(6200) << "r->isFlow is cb. continuation @" << cb->continuation() << endl;
00526 #endif
00527     // Disregard empty continuations, they get the caret stuck otherwise.
00528     // This is because both cont_a and cont_o point to the same
00529     // DOM element. When the caret should move to cont_o, findFlowBox finds
00530     // cont_a, and the caret will be placed there.
00531     RenderFlow *flow = static_cast<RenderFlow *>(cb->element()
00532                 ? cb->element()->renderer() : 0);
00533     if (cb->continuation() || flow && flow->isRenderBlock() && flow != cb
00534             && flow->continuation()) {
00535       nextBlock();
00536       return;
00537     }/*end if*/
00538   } else {
00539     cb = static_cast<RenderFlow *>(r->containingBlock());
00540     if (!cb->isRenderBlock()) {
00541 #if DEBUG_CARETMODE > 0
00542       kdDebug(6200) << "dummy cb created " << cb << endl;
00543 #endif
00544       cb = generateDummyBlock(lines->arena, r);
00545     }/*end if*/
00546   }/*end if*/
00547   flowBox = cb->firstLineBox();
00548 #if DEBUG_CARETMODE > 0
00549   kdDebug(6200) << "++: flowBox " << flowBox << endl;
00550 #endif
00551 
00552   if (!flowBox) flowBox = generateDummyFlowBox(lines->arena, cb, r);
00553 #if DEBUG_CARETMODE > 0
00554   if (!cb->firstLineBox()) kdDebug(6200) << "++: dummy flowBox " << flowBox << endl;
00555 #endif
00556 }
00557 
00558 inline LineIterator &LineIterator::operator ++()
00559 {
00560   flowBox = static_cast<InlineFlowBox *>(flowBox->nextLineBox());
00561 
00562   // if there are no more lines in this block, begin with first line of
00563   // next block
00564   if (!flowBox) nextBlock();
00565 
00566   return *this;
00567 }
00568 
00569 inline LineIterator LineIterator::operator ++(int)
00570 {
00571   LineIterator it(*this);
00572   operator ++();
00573   return it;
00574 }
00575 
00576 void LineIterator::prevBlock()
00577 {
00578   RenderObject *r = cb;
00579   RenderObject *n = r->firstChild();
00580   while (n) r = n, n = r->firstChild();
00581   r = prevSuitableLeafRenderObject(r);
00582   if (!r) {
00583     cb = 0;
00584     return;
00585   }/*end if*/
00586 
00587   // If we hit a leaf block (which can happen on empty blocks), use this
00588   // as its containing block
00589   if (r->isRenderBlock()) {
00590     cb = static_cast<RenderFlow *>(r);
00591 #if DEBUG_CARETMODE > 0
00592     kdDebug(6200) << "r->isFlow is cb. continuation @" << cb->continuation() << endl;
00593 #endif
00594     // Disregard empty continuations, they get the caret stuck otherwise.
00595     // This is because both cont_a and cont_o point to the same
00596     // DOM element. When the caret should move to cont_o, findFlowBox finds
00597     // cont_a, and the caret will be placed there.
00598     RenderFlow *flow = static_cast<RenderFlow *>(cb->element()
00599                 ? cb->element()->renderer() : 0);
00600     if (cb->continuation() || flow && flow->isRenderBlock() && flow != cb
00601             && flow->continuation()) {
00602       prevBlock();
00603       return;
00604     }/*end if*/
00605   } else {
00606     cb = static_cast<RenderFlow *>(r->containingBlock());
00607     if (!cb->isRenderBlock()) {
00608 #if DEBUG_CARETMODE > 0
00609       kdDebug(6200) << "dummy cb created " << cb << endl;
00610 #endif
00611       cb = generateDummyBlock(lines->arena, r);
00612     }/*end if*/
00613   }/*end if*/
00614   flowBox = cb->lastLineBox();
00615 
00616   if (!flowBox) flowBox = generateDummyFlowBox(lines->arena, cb, r);
00617 }
00618 
00619 inline LineIterator &LineIterator::operator --()
00620 {
00621   flowBox = static_cast<InlineFlowBox *>(flowBox->prevLineBox());
00622 
00623   // if there are no more lines in this block, begin with last line of
00624   // previous block
00625   if (!flowBox) prevBlock();
00626 
00627   return *this;
00628 }
00629 
00630 inline LineIterator LineIterator::operator --(int)
00631 {
00632   LineIterator it(*this);
00633   operator --();
00634   return it;
00635 }
00636 
00637 #if 0 // not implemented because it's not needed
00638 LineIterator LineIterator::operator +(int /*summand*/) const
00639 {
00640   // FIXME: not implemented
00641   return LineIterator();
00642 }
00643 
00644 LineIterator LineIterator::operator -(int /*summand*/) const
00645 {
00646   // FIXME: not implemented
00647   return LineIterator();
00648 }
00649 #endif
00650 
00651 LineIterator &LineIterator::operator +=(int summand)
00652 {
00653   if (summand > 0)
00654     while (summand-- && *this != lines->end()) ++*this;
00655   else if (summand < 0)
00656     operator -=(-summand);
00657   return *this;
00658 }
00659 
00660 LineIterator &LineIterator::operator -=(int summand)
00661 {
00662   if (summand > 0)
00663     while (summand-- && *this != lines->preBegin()) --*this;
00664   else if (summand < 0)
00665     operator +=(-summand);
00666   return *this;
00667 }
00668 
00669 // == class EditableCharacterIterator implementation
00670 
00671 void EditableCharacterIterator::initFirstChar()
00672 {
00673   InlineBox *b = *ebit;
00674   if (b) {
00675     if (_offset == b->maxOffset())
00676       peekNext();
00677     else if (b->isInlineTextBox())
00678       _char = static_cast<RenderText *>(b->object())->str->s[_offset].unicode();
00679     else
00680       _char = -1;
00681   }/*end if*/
00682 }
00683 
00684 EditableCharacterIterator &EditableCharacterIterator::operator ++()
00685 {
00686   _offset++;
00687 
00688   InlineBox *b = *ebit;
00689   RenderObject *r = b->object();
00690   // BRs have no extent, so their maximum offset must be their minimum.
00691   // A block element can only be the target if it is empty -- in this case
00692   // its extent is zero, too.
00693   long maxofs = r->isBR() || r->isRenderBlock() ? b->minOffset() : b->maxOffset();
00694 #if DEBUG_CARETMODE > 0
00695   kdDebug(6200) << "b->maxOffset() " << b->maxOffset() << " b->minOffset() " << b->minOffset() << endl;
00696 #endif
00697   if (_offset == maxofs) {
00698 #if DEBUG_CARETMODE > 2
00699 kdDebug(6200) << "_offset == maxofs: " << _offset << " == " << maxofs << endl;
00700 #endif
00701 //    _peekPrev = b;
00702     peekNext();
00703   } else if (_offset > maxofs) {
00704 #if DEBUG_CARETMODE > 2
00705 kdDebug(6200) << "_offset > maxofs: " << _offset << " > " << maxofs /*<< " _peekNext: " << _peekNext*/ << endl;
00706 #endif
00707     if (true) {
00708       if (*ebit)
00709         ++ebit;
00710       if (!*ebit) {     // end of line reached, go to next line
00711         ++_it;
00712 #if DEBUG_CARETMODE > 3
00713 kdDebug(6200) << "++_it" << endl;
00714 #endif
00715         if (_it != ld->end()) {
00716       ebit = _it;
00717           b = *ebit;
00718 #if DEBUG_CARETMODE > 3
00719 kdDebug(6200) << "b " << b << " isText " << b->isInlineTextBox() << endl;
00720 #endif
00721       _node = b->object()->element();
00722 #if DEBUG_CARETMODE > 3
00723 kdDebug(6200) << "_node " << _node << ":" << _node->nodeName().string() << endl;
00724 #endif
00725       _offset = b->minOffset();
00726 #if DEBUG_CARETMODE > 3
00727 kdDebug(6200) << "_offset " << _offset << endl;
00728 #endif
00729     } else {
00730       _node = 0;
00731       b = 0;
00732     }/*end if*/
00733         goto readchar;
00734       }/*end if*/
00735     }/*end if*/
00736     bool adjacent = ebit.isAdjacent();
00737     // Jump over element if this one is not a text node.
00738     if (adjacent && !(*ebit)->isInlineTextBox()) {
00739       EditableInlineBoxIterator copy = ebit;
00740       ++ebit;
00741       if (*ebit && (*ebit)->isInlineTextBox()) adjacent = false;
00742       else ebit = copy;
00743     }/*end if*/
00744     _node = (*ebit)->object()->element();
00745     _offset = (*ebit)->minOffset() + adjacent;
00746     //_peekNext = 0;
00747     b = *ebit;
00748     goto readchar;
00749   } else {
00750 readchar:
00751     // get character
00752     if (b && b->isInlineTextBox() && _offset < b->maxOffset())
00753       _char = static_cast<RenderText *>(b->object())->str->s[_offset].unicode();
00754     else
00755       _char = -1;
00756   }/*end if*/
00757 #if DEBUG_CARETMODE > 2
00758 kdDebug(6200) << "_offset: " << _offset /*<< " _peekNext: " << _peekNext*/ << " char '" << (char)_char << "'" << endl;
00759 #endif
00760 
00761 #if DEBUG_CARETMODE > 0
00762   if (*ebit) {
00763     InlineBox *box = *ebit;
00764     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;
00765   }
00766 #endif
00767   return *this;
00768 }
00769 
00770 EditableCharacterIterator &EditableCharacterIterator::operator --()
00771 {
00772   _offset--;
00773   //kdDebug(6200) << "--: _offset=" << _offset << endl;
00774 
00775   InlineBox *b = *ebit;
00776   InlineBox *_peekPrev = 0;
00777   InlineBox *_peekNext = 0;
00778   long minofs = b ? b->minOffset() : _offset + 1;
00779 #if DEBUG_CARETMODE > 0
00780   kdDebug(6200) << "b->maxOffset() " << b->maxOffset() << " b->minOffset() " << b->minOffset() << endl;
00781 #endif
00782   if (_offset == minofs) {
00783 #if DEBUG_CARETMODE > 2
00784 kdDebug(6200) << "_offset == minofs: " << _offset << " == " << minofs << endl;
00785 #endif
00786     _peekNext = b;
00787     // get character
00788     if (b && b->isInlineTextBox())
00789       _char = static_cast<RenderText *>(b->object())->text()[_offset].unicode();
00790     else
00791       _char = -1;
00792 
00793     //peekPrev();
00794     bool do_prev = false;
00795     {
00796       EditableInlineBoxIterator copy = ebit;
00797       --ebit;
00798       _peekPrev = *ebit;
00799       // Jump to end of previous element if it's adjacent, and a text box
00800       if (ebit.isAdjacent() && *ebit && (*ebit)->isInlineTextBox())
00801         //operator --();
00802     do_prev = true;
00803       else
00804         ebit = copy;
00805     }
00806     if (do_prev) goto prev;
00807   } else if (_offset < minofs) {
00808 prev:
00809 #if DEBUG_CARETMODE > 2
00810 kdDebug(6200) << "_offset < minofs: " << _offset << " < " << minofs /*<< " _peekNext: " << _peekNext*/ << endl;
00811 #endif
00812     if (!_peekPrev) {
00813       _peekNext = *ebit;
00814       if (*ebit)
00815         --ebit;
00816       if (!*ebit) {     // end of line reached, go to previous line
00817         --_it;
00818 #if DEBUG_CARETMODE > 3
00819 kdDebug(6200) << "--_it" << endl;
00820 #endif
00821         if (_it != ld->preBegin()) {
00822 //    kdDebug(6200) << "begin from end!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << endl;
00823       ebit = EditableInlineBoxIterator(_it, true);
00824       RenderObject *r = (*ebit)->object();
00825 #if DEBUG_CARETMODE > 3
00826 kdDebug(6200) << "b " << *ebit << " isText " << (*ebit)->isInlineTextBox() << endl;
00827 #endif
00828       _node = r->element();
00829       _offset = r->isBR() ? (*ebit)->minOffset() : (*ebit)->maxOffset();
00830       _char = -1;
00831 #if DEBUG_CARETMODE > 0
00832           {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;}
00833 #endif
00834     } else
00835       _node = 0;
00836     return *this;
00837       }/*end if*/
00838     }/*end if*/
00839 
00840     bool adjacent = ebit.isAdjacent();
00841     // Ignore this box if it isn't a text box, but the previous box was
00842 #if DEBUG_CARETMODE > 0
00843     kdDebug(6200) << "adjacent " << adjacent << " _peekNext " << _peekNext << " _peekNext->isInlineTextBox: " << (_peekNext ? _peekNext->isInlineTextBox() : false) << " !((*ebit)->isInlineTextBox): " << (*ebit ? !(*ebit)->isInlineTextBox() : true) << endl;
00844 #endif
00845     if (adjacent && _peekNext && _peekNext->isInlineTextBox()
00846         && !(*ebit)->isInlineTextBox()) {
00847       EditableInlineBoxIterator copy = ebit;
00848       --ebit;
00849       if (!*ebit) /*adjacent = false;
00850       else */ebit = copy;
00851     }/*end if*/
00852 #if DEBUG_CARETMODE > 0
00853     kdDebug(6200) << "(*ebit)->obj " << (*ebit)->object()->renderName() << "[" << (*ebit)->object() << "]" << " minOffset: " << (*ebit)->minOffset() << " maxOffset: " << (*ebit)->maxOffset() << endl;
00854 #endif
00855     _node = (*ebit)->object()->element();
00856 #if DEBUG_CARETMODE > 3
00857 kdDebug(6200) << "_node " << _node << ":" << _node->nodeName().string() << endl;
00858 #endif
00859     _offset = (*ebit)->maxOffset()/* - adjacent*/;
00860 #if DEBUG_CARETMODE > 3
00861 kdDebug(6200) << "_offset " << _offset << endl;
00862 #endif
00863     _peekPrev = 0;
00864   } else {
00865 #if DEBUG_CARETMODE > 0
00866 kdDebug(6200) << "_offset: " << _offset << " _peekNext: " << _peekNext << endl;
00867 #endif
00868     // get character
00869     if (_peekNext && _offset >= b->maxOffset() && _peekNext->isInlineTextBox())
00870       _char = static_cast<RenderText *>(_peekNext->object())->text()[_peekNext->minOffset()].unicode();
00871     else if (b && _offset < b->maxOffset() && b->isInlineTextBox())
00872       _char = static_cast<RenderText *>(b->object())->text()[_offset].unicode();
00873     else
00874       _char = -1;
00875   }/*end if*/
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) << endl;
00881   }
00882 #endif
00883   return *this;
00884 }
00885 
00886 // == class TableRowIterator implementation
00887 
00888 TableRowIterator::TableRowIterator(RenderTable *table, bool fromEnd,
00889         RenderTableSection::RowStruct *row)
00890         : sec(table, fromEnd)
00891 {
00892   // set index
00893   if (*sec) {
00894     if (fromEnd) index = (*sec)->grid.size() - 1;
00895     else index = 0;
00896   }/*end if*/
00897 
00898   // initialize with given row
00899   if (row && *sec) {
00900     while (operator *() != row)
00901       if (fromEnd) operator --(); else operator ++();
00902   }/*end if*/
00903 }
00904 
00905 TableRowIterator &TableRowIterator::operator ++()
00906 {
00907   index++;
00908 
00909   if (index >= (int)(*sec)->grid.size()) {
00910     ++sec;
00911 
00912     if (*sec) index = 0;
00913   }/*end if*/
00914   return *this;
00915 }
00916 
00917 TableRowIterator &TableRowIterator::operator --()
00918 {
00919   index--;
00920 
00921   if (index < 0) {
00922     --sec;
00923 
00924     if (*sec) index = (*sec)->grid.size() - 1;
00925   }/*end if*/
00926   return *this;
00927 }
00928 
00929 // == class ErgonomicEditableLineIterator implementation
00930 
00931 // some decls
00932 static RenderTableCell *findNearestTableCellInRow(KHTMLPart *part, int x,
00933         RenderTableSection::RowStruct *row, bool fromEnd);
00934 
00948 static inline RenderTableCell *findNearestTableCell(KHTMLPart *part, int x,
00949         TableRowIterator &it, bool fromEnd)
00950 {
00951   RenderTableCell *result = 0;
00952 
00953   while (*it) {
00954     result = findNearestTableCellInRow(part, x, *it, fromEnd);
00955     if (result) break;
00956 
00957     if (fromEnd) --it; else ++it;
00958   }/*wend*/
00959 
00960   return result;
00961 }
00962 
00976 static RenderTableCell *findNearestTableCellInRow(KHTMLPart *part, int x,
00977         RenderTableSection::RowStruct *row, bool fromEnd)
00978 {
00979   // First pass. Find spatially nearest cell.
00980   int n = (int)row->row->size();
00981   int i;
00982   for (i = 0; i < n; i++) {
00983     RenderTableCell *cell = row->row->at(i);
00984     if (!cell || (int)cell == -1) continue;
00985 
00986     int absx, absy;
00987     cell->absolutePosition(absx, absy, false); // ### position: fixed?
00988 #if DEBUG_CARETMODE > 1
00989     kdDebug(6201) << "i/n " << i << "/" << n << " absx " << absx << " absy " << absy << endl;
00990 #endif
00991 
00992     // I rely on the assumption that all cells are in ascending visual order
00993     // ### maybe this assumption is wrong for bidi?
00994 #if DEBUG_CARETMODE > 1
00995     kdDebug(6201) << "x " << x << " < " << (absx + cell->width()) << "?" << endl;
00996 #endif
00997     if (x < absx + cell->width()) break;
00998   }/*next i*/
00999   if (i >= n) i = n - 1;
01000 
01001   // Second pass. Find editable cell, beginning with the currently found,
01002   // extending to the left, and to the right, alternating.
01003   for (int cnt = 0; cnt < 2*n; cnt++) {
01004     int index = i - ((cnt >> 1) + 1)*(cnt & 1) + (cnt >> 1)*!(cnt & 1);
01005     if (index < 0 || index >= n) continue;
01006 
01007     RenderTableCell *cell = row->row->at(index);
01008     if (!cell || (int)cell == -1) continue;
01009 
01010 #if DEBUG_CARETMODE > 1
01011     kdDebug(6201) << "index " << index << " cell " << cell << endl;
01012 #endif
01013     RenderTable *nestedTable;
01014     if (containsEditableElement(part, cell, nestedTable, fromEnd)) {
01015 
01016       if (nestedTable) {
01017         TableRowIterator it(nestedTable, fromEnd);
01018     while (*it) {
01019       cell = findNearestTableCell(part, x, it, fromEnd);
01020       if (cell) break;
01021       if (fromEnd) --it; else ++it;
01022     }/*wend*/
01023       }/*end if*/
01024 
01025       return cell;
01026     }/*end if*/
01027   }/*next i*/
01028   return 0;
01029 }
01030 
01037 static RenderObject *commonAncestorTableSectionOrCell(RenderObject *r1,
01038         RenderObject *r2)
01039 {
01040   if (!r1 || !r2) return 0;
01041   RenderTableSection *sec = 0;
01042   int start_depth=0, end_depth=0;
01043   // First we find the depths of the two objects in the tree (start_depth, end_depth)
01044   RenderObject *n = r1;
01045   while (n->parent()) {
01046     n = n->parent();
01047     start_depth++;
01048   }/*wend*/
01049   n = r2;
01050   while( n->parent()) {
01051     n = n->parent();
01052     end_depth++;
01053   }/*wend*/
01054   // here we climb up the tree with the deeper object, until both objects have equal depth
01055   while (end_depth > start_depth) {
01056     r2 = r2->parent();
01057     end_depth--;
01058   }/*wend*/
01059   while (start_depth > end_depth) {
01060     r1 = r1->parent();
01061 //    if (r1->isTableSection()) sec = static_cast<RenderTableSection *>(r1);
01062     start_depth--;
01063   }/*wend*/
01064   // Climb the tree with both r1 and r2 until they are the same
01065   while (r1 != r2){
01066     r1 = r1->parent();
01067     if (r1->isTableSection()) sec = static_cast<RenderTableSection *>(r1);
01068     r2 = r2->parent();
01069   }/*wend*/
01070 
01071   // At this point, we found the most approximate common ancestor. Now climb
01072   // up until the condition of the function return value is satisfied.
01073   while (r1 && !r1->isTableCell() && !r1->isTableSection() && !r1->isTable())
01074     r1 = r1->parent();
01075 
01076   return r1 && r1->isTable() ? sec : r1;
01077 }
01078 
01086 static int findRowInSection(RenderTableSection *section, RenderTableCell *cell,
01087         RenderTableSection::RowStruct *&row, RenderTableCell *&directCell)
01088 {
01089   // Seek direct cell
01090   RenderObject *r = cell;
01091   while (r != section) {
01092     if (r->isTableCell()) directCell = static_cast<RenderTableCell *>(r);
01093     r = r->parent();
01094   }/*wend*/
01095 
01096   // So, and this is really nasty: As we have no indices, we have to do a
01097   // linear comparison. Oh, that sucks so much for long tables, you can't
01098   // imagine.
01099   int n = section->numRows();
01100   for (int i = 0; i < n; i++) {
01101     row = &section->grid[i];
01102 
01103     // check for cell
01104     int m = row->row->size();
01105     for (int j = 0; j < m; j++) {
01106       RenderTableCell *c = row->row->at(j);
01107       if (c == directCell) return i;
01108     }/*next j*/
01109 
01110   }/*next i*/
01111   Q_ASSERT(false);
01112   return -1;
01113 }
01114 
01120 static inline RenderTable *findFirstDescendantTable(RenderObject *leaf, RenderFlow *block)
01121 {
01122   RenderTable *result = 0;
01123   while (leaf && leaf != block) {
01124     if (leaf->isTable()) result = static_cast<RenderTable *>(leaf);
01125     leaf = leaf->parent();
01126   }/*wend*/
01127   return result;
01128 }
01129 
01133 static inline RenderTableCell *containingTableCell(RenderObject *r)
01134 {
01135   while (r && !r->isTableCell()) r = r->parent();
01136   return static_cast<RenderTableCell *>(r);
01137 }
01138 
01139 inline void ErgonomicEditableLineIterator::calcAndStoreNewLine(
01140             RenderFlow *newBlock, bool toBegin)
01141 {
01142   // take the first/last editable element in the found cell as the new
01143   // value for the iterator
01144   cb = newBlock;
01145   if (toBegin) prevBlock(); else nextBlock();
01146 
01147   if (!cb) {
01148     flowBox = 0;
01149     return;
01150   }/*end if*/
01151 
01152   if (!isEditable(*this)) {
01153     if (toBegin) EditableLineIterator::operator --();
01154     else EditableLineIterator::operator ++();
01155   }/*end if*/
01156 }
01157 
01158 void ErgonomicEditableLineIterator::determineTopologicalElement(
01159         RenderTableCell *oldCell, RenderObject *newObject, bool toBegin)
01160 {
01161   // When we arrive here, a transition between cells has happened.
01162   // Now determine the type of the transition. This can be
01163   // (1) a transition from this cell into a table inside this cell.
01164   // (2) a transition from this cell into another cell of this table
01165 
01166   TableRowIterator it;
01167 
01168   RenderObject *commonAncestor = commonAncestorTableSectionOrCell(oldCell, newObject);
01169 #if DEBUG_CARETMODE > 1
01170   kdDebug(6201) << " ancestor " << commonAncestor << endl;
01171 #endif
01172 
01173   // The whole document is treated as a table cell.
01174   if (!commonAncestor || commonAncestor->isTableCell()) {   // (1)
01175 
01176     RenderTableCell *cell = static_cast<RenderTableCell *>(commonAncestor);
01177     RenderTable *table = findFirstDescendantTable(newObject, cell);
01178 
01179 #if DEBUG_CARETMODE > 0
01180     kdDebug(6201) << "table cell: " << cell << endl;
01181 #endif
01182 
01183     // if there is no table, we fell out of the previous table, and are now
01184     // in some table-less block. Therefore, done.
01185     if (!table) return;
01186 
01187     it = TableRowIterator(table, toBegin);
01188 
01189   } else if (commonAncestor->isTableSection()) {        // (2)
01190 
01191     RenderTableSection *section = static_cast<RenderTableSection *>(commonAncestor);
01192     RenderTableSection::RowStruct *row;
01193     int idx = findRowInSection(section, oldCell, row, oldCell);
01194 #if DEBUG_CARETMODE > 1
01195     kdDebug(6201) << "table section: row idx " << idx << endl;
01196 #endif
01197 
01198     it = TableRowIterator(section, idx);
01199 
01200     // advance rowspan rows
01201     int rowspan = oldCell->rowSpan();
01202     while (*it && rowspan--) {
01203       if (toBegin) --it; else ++it;
01204     }/*wend*/
01205 
01206   } else {
01207     kdError(6201) << "Neither common cell nor section! " << commonAncestor->renderName() << endl;
01208     // will crash on uninitialized table row iterator
01209   }/*end if*/
01210 
01211   RenderTableCell *cell = findNearestTableCell(lines->m_part, xCoor, it, toBegin);
01212 #if DEBUG_CARETMODE > 1
01213   kdDebug(6201) << "findNearestTableCell result: " << cell << endl;
01214 #endif
01215 
01216   RenderFlow *newBlock = cell;
01217   if (!cell) {
01218     Q_ASSERT(commonAncestor->isTableSection());
01219     RenderTableSection *section = static_cast<RenderTableSection *>(commonAncestor);
01220     cell = containingTableCell(section);
01221 #if DEBUG_CARETMODE > 1
01222     kdDebug(6201) << "containing cell: " << cell << endl;
01223 #endif
01224 
01225     RenderTable *nestedTable;
01226     bool editableChild = cell && containsEditableChildElement(lines->m_part,
01227             cell, nestedTable, toBegin, section->table());
01228 
01229     if (cell && !editableChild) {
01230 #if DEBUG_CARETMODE > 1
01231       kdDebug(6201) << "========= recursive invocation outer =========" << endl;
01232 #endif
01233       determineTopologicalElement(cell, cell->section(), toBegin);
01234 #if DEBUG_CARETMODE > 1
01235       kdDebug(6201) << "========= end recursive invocation outer =========" << endl;
01236 #endif
01237       return;
01238 
01239     } else if (cell && nestedTable) {
01240 #if DEBUG_CARETMODE > 1
01241       kdDebug(6201) << "========= recursive invocation inner =========" << endl;
01242 #endif
01243       determineTopologicalElement(cell, nestedTable, toBegin);
01244 #if DEBUG_CARETMODE > 1
01245       kdDebug(6201) << "========= end recursive invocation inner =========" << endl;
01246 #endif
01247       return;
01248 
01249     } else {
01250 #if DEBUG_CARETMODE > 1
01251       kdDebug(6201) << "newBlock is table: " << section->table() << endl;
01252 #endif
01253       newBlock = section->table();
01254 //      if (toBegin) prevBlock(); else nextBlock();
01255     }/*end if*/
01256   } else {
01257     // adapt cell so that prevBlock/nextBlock works as expected
01258     RenderObject *r = cell;
01259     if (toBegin) {
01260       while (r->lastChild()) r = r->lastChild();
01261       r = nextSuitableLeafRenderObject(r);
01262     } else
01263       r = prevSuitableLeafRenderObject(r);
01264     newBlock = static_cast<RenderFlow *>(!r || r->isRenderBlock() ? r : r->containingBlock());
01265   }/*end if*/
01266 
01267   calcAndStoreNewLine(newBlock, toBegin);
01268 }
01269 
01270 ErgonomicEditableLineIterator &ErgonomicEditableLineIterator::operator ++()
01271 {
01272   RenderTableCell *oldCell = containingTableCell(cb);
01273 
01274   EditableLineIterator::operator ++();
01275   if (*this == lines->end() || *this == lines->preBegin()) return *this;
01276 
01277   RenderTableCell *newCell = containingTableCell(cb);
01278 
01279   if (!newCell || newCell == oldCell) return *this;
01280 
01281   determineTopologicalElement(oldCell, newCell, false);
01282 
01283   return *this;
01284 }
01285 
01286 ErgonomicEditableLineIterator &ErgonomicEditableLineIterator::operator --()
01287 {
01288   RenderTableCell *oldCell = containingTableCell(cb);
01289 
01290   EditableLineIterator::operator --();
01291   if (*this == lines->end() || *this == lines->preBegin()) return *this;
01292 
01293   RenderTableCell *newCell = containingTableCell(cb);
01294 
01295   if (!newCell || newCell == oldCell) return *this;
01296 
01297   determineTopologicalElement(oldCell, newCell, true);
01298 
01299   return *this;
01300 }
01301 
01302 // == Navigational helper functions ==
01303 
01313 static InlineBox *nearestInlineBox(LineIterator &it, CaretViewContext *cv,
01314     int &x, int &absx, int &absy)
01315 {
01316   InlineFlowBox *fbox = *it;
01317 
01318   // Find containing block
01319   RenderObject *cb = fbox->object();
01320 
01321   if (cb) cb->absolutePosition(absx, absy);
01322   else absx = absy = 0;
01323 
01324   // Otherwise find out in which inline box the caret is to be placed.
01325 
01326   // this horizontal position is to be approximated
01327   x = cv->origX - absx;
01328   InlineBox *caretBox = 0; // Inline box containing the caret
01329 //  NodeImpl *lastnode = 0;  // node of previously checked render object.
01330   int xPos;        // x-coordinate of current inline box
01331   int oldXPos = -1;    // x-coordinate of last inline box
01332   EditableInlineBoxIterator fbit = it;
01333 #if DEBUG_CARETMODE > 0
01334   kdDebug(6200) << "*fbit = " << *fbit << endl;
01335 #endif
01336   // Either iterate through all children or take the flow box itself as a
01337   // child if it has no children
01338   for (InlineBox *b; *fbit != 0; ++fbit) {
01339     b = *fbit;
01340 
01341 //    RenderObject *r = b->object();
01342 #if DEBUG_CARETMODE > 0
01343     if (b->isInlineFlowBox()) kdDebug(6200) << "b is inline flow box" << endl;
01344 //  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;
01345 #endif
01346 //    NodeImpl *node = r->element();
01347     xPos = b->xPos();
01348 
01349     // the caret is before this box
01350     if (x < xPos) {
01351       // snap to nearest box
01352       if (oldXPos < 0 || x - (oldXPos + caretBox->width()) > xPos - x) {
01353     caretBox = b;   // current box is nearer
01354 //        lastnode = node;
01355       }/*end if*/
01356       break;        // Otherwise, preceding box is implicitly used
01357     }
01358 
01359     caretBox = b;
01360 //    lastnode = node;
01361 
01362     // the caret is within this box
01363     if (x >= xPos && x < xPos + caretBox->width())
01364       break;
01365     oldXPos = xPos;
01366 
01367     // the caret can only be after the last box which is automatically
01368     // contained in caretBox when we fall out of the loop.
01369 
01370     if (b == fbox) break;
01371   }/*next fbit*/
01372 
01373   return caretBox;
01374 }
01375 
01381 static void moveItToNextWord(EditableCharacterIterator &it)
01382 {
01383 #if DEBUG_CARETMODE > 0
01384   kdDebug(6200) << "%%%%%%%%%%%%%%%%%%%%% moveItToNextWord" << endl;
01385 #endif
01386   EditableCharacterIterator copy;
01387   while (it.node() && !(*it).isSpace() && !(*it).isPunct()) {
01388 #if DEBUG_CARETMODE > 2
01389     kdDebug(6200) << "reading1 '" << (*it).latin1() << "'" << endl;
01390 #endif
01391     copy = it;
01392     ++it;
01393   }
01394 
01395   if (!it.node()) {
01396     it = copy;
01397     return;
01398   }/*end if*/
01399 
01400   while (it.node() && ((*it).isSpace() || (*it).isPunct())) {
01401 #if DEBUG_CARETMODE > 2
01402     kdDebug(6200) << "reading2 '" << (*it).latin1() << "'" << endl;
01403 #endif
01404     copy = it;
01405     ++it;
01406   }
01407 
01408   if (!it.node()) it = copy;
01409 }
01410 
01416 static void moveItToPrevWord(EditableCharacterIterator &it)
01417 {
01418   if (!it.node()) return;
01419 
01420 #if DEBUG_CARETMODE > 0
01421   kdDebug(6200) << "%%%%%%%%%%%%%%%%%%%%% moveItToPrevWord" << endl;
01422 #endif
01423   EditableCharacterIterator copy;
01424 
01425   // Jump over all space and punctuation characters first
01426   do {
01427     copy = it;
01428     --it;
01429 #if DEBUG_CARETMODE > 2
01430     if (it.node()) kdDebug(6200) << "reading1 '" << (*it).latin1() << "'" << endl;
01431 #endif
01432   } while (it.node() && ((*it).isSpace() || (*it).isPunct()));
01433 
01434   if (!it.node()) {
01435     it = copy;
01436     return;
01437   }/*end if*/
01438 
01439   do {
01440     copy = it;
01441     --it;
01442 #if DEBUG_CARETMODE > 0
01443     if (it.node()) kdDebug(6200) << "reading2 '" << (*it).latin1() << "'" << endl;
01444 #endif
01445   } while (it.node() && !(*it).isSpace() && !(*it).isPunct());
01446 
01447   it = copy;
01448 }
01449 
01450 
01458 static void moveIteratorByPage(LinearDocument &ld,
01459         ErgonomicEditableLineIterator &it, int mindist, bool next)
01460 {
01461   if (it == ld.end() || it == ld.preBegin()) return;
01462 
01463   ErgonomicEditableLineIterator copy = it;
01464 #if DEBUG_CARETMODE > 0
01465   kdDebug(6200) << " mindist: " << mindist << endl;
01466 #endif
01467 
01468   InlineFlowBox *flowBox = *copy;
01469   int absx = 0, absy = 0;
01470 
01471   RenderFlow *lastcb = static_cast<RenderFlow *>(flowBox->object());
01472   Q_ASSERT(lastcb->isRenderBlock());
01473   lastcb->absolutePosition(absx, absy, false);  // ### what about fixed?
01474 
01475   // ### actually flowBox->yPos() should suffice, but this is not ported
01476   // over yet from WebCore
01477   int lastfby = flowBox->firstChild()->yPos();
01478   int lastheight = 0;
01479   do {
01480     if (next) ++copy; else --copy;
01481     if (copy == ld.end() || copy == ld.preBegin()) break;
01482 
01483     // ### change to RootInlineBox after full WebCore merge
01484     flowBox = static_cast<InlineFlowBox *>(*copy);
01485     Q_ASSERT(flowBox->isInlineFlowBox());
01486 
01487     RenderFlow *cb = static_cast<RenderFlow *>(flowBox->object());
01488     Q_ASSERT(cb->isRenderBlock());
01489 
01490     int diff = 0;
01491     // ### actually flowBox->yPos() should suffice, but this is not ported
01492     // over yet from WebCore
01493     int fby = flowBox->firstChild()->yPos();
01494     if (cb != lastcb) {
01495       if (next) {
01496         diff = absy + lastfby + lastheight;
01497         cb->absolutePosition(absx, absy, false);    // ### what about fixed?
01498         diff = absy - diff + fby;
01499         lastfby = 0;
01500       } else {
01501         diff = absy;
01502         cb->absolutePosition(absx, absy, false);    // ### what about fixed?
01503         diff -= absy + fby + lastheight;
01504     lastfby = fby - lastheight;
01505       }/*end if*/
01506 #if DEBUG_CARETMODE > 2
01507       kdDebug(6200) << "absdiff " << diff << endl;
01508 #endif
01509     } else {
01510       diff = QABS(fby - lastfby);
01511     }/*end if*/
01512 #if DEBUG_CARETMODE > 2
01513     kdDebug(6200) << "flowBox->firstChild->yPos: " << fby << " diff " << diff << endl;
01514 #endif
01515 
01516     mindist -= diff;
01517 
01518     lastheight = QABS(fby - lastfby);
01519     lastfby = fby;
01520     lastcb = cb;
01521     it = copy;
01522 #if DEBUG_CARETMODE > 0
01523     kdDebug(6200) << " mindist: " << mindist << endl;
01524 #endif
01525     // trick: actually the distance is always one line short, but we cannot
01526     // calculate the height of the first line (### WebCore will make it better)
01527     // Therefore, we simply approximate that excess line by using the last
01528     // caluculated line height.
01529   } while (mindist - lastheight > 0);
01530 }
01531 
01532 
01533 }/*end namespace*/
01534 
KDE Logo
This file is part of the documentation for khtml Library Version 3.2.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Mar 4 22:45:40 2004 by doxygen 1.3.6-20040222 written by Dimitri van Heesch, © 1997-2003