khtml Library API Documentation

khtml_caret.cpp

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