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.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Oct 8 11:16:07 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003