File indexing completed on 2023-05-30 09:09:50
0001 /* This file is part of the KDE project 0002 * 0003 * Copyright (C) 2003-2004 Leo Savernik <l.savernik@aon.at> 0004 * 0005 * This library is free software; you can redistribute it and/or 0006 * modify it under the terms of the GNU Library General Public 0007 * License as published by the Free Software Foundation; either 0008 * version 2 of the License, or (at your option) any later version. 0009 * 0010 * This library is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 * Library General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU Library General Public License 0016 * along with this library; see the file COPYING.LIB. If not, write to 0017 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0018 * Boston, MA 02110-1301, USA. 0019 */ 0020 0021 #ifndef KHTML_CARET_P_H 0022 #define KHTML_CARET_P_H 0023 0024 #include "rendering/render_table.h" 0025 0026 #define DEBUG_CARETMODE 0 0027 0028 class QFontMetrics; 0029 0030 namespace DOM 0031 { 0032 class NodeImpl; 0033 class ElementImpl; 0034 } 0035 0036 namespace khtml 0037 { 0038 0039 /** caret advance policy. 0040 * 0041 * Used to determine which elements are taken into account when the caret is 0042 * advanced. Later policies pose refinements of all former 0043 * policies. 0044 * @param LeafsOnly advance from leave render object to leaf render object 0045 * (It will allow outside flow positions if a flow wouldn't be reachable 0046 * otherwise). 0047 * @param IndicatedFlows place caret also at the beginning/end of flows 0048 * that have at least one visible border at any side. 0049 * (It will allow not indicated flow positions if a flow wouldn't 0050 * be reachable otherwise). 0051 * @param VisibleFlows place caret also at the beginning/end of any flow 0052 * that has a renderer. 0053 */ 0054 enum CaretAdvancePolicy { 0055 LeafsOnly, IndicatedFlows, VisibleFlows 0056 }; 0057 0058 /** contextual information about the caret which is related to the view. 0059 * An object of this class is only instantiated when it is needed. 0060 */ 0061 struct CaretViewContext { 0062 int freqTimerId; // caret blink frequency timer id 0063 int x, y; // caret position in viewport coordinates 0064 // (y specifies the top, not the baseline) 0065 int width; // width of caret in pixels 0066 int height; // height of caret in pixels 0067 bool visible; // true if currently visible. 0068 bool displayed; // true if caret is to be displayed at all. 0069 bool caretMoved; // set to true once caret has been moved in page 0070 // how to display the caret when view is not focused 0071 KHTMLPart::CaretDisplayPolicy displayNonFocused; 0072 0073 /** For natural traversal of lines, the original x position is saved, and 0074 * the actual x is set to the first character whose x position is 0075 * greater than origX. 0076 * 0077 * origX is reset to x whenever the caret is moved horizontally or placed 0078 * by the mouse. 0079 */ 0080 int origX; 0081 0082 bool keyReleasePending; // true if keypress under caret mode awaits 0083 // corresponding release event 0084 CaretViewContext() : freqTimerId(-1), x(0), y(0), width(1), height(16), 0085 visible(true), displayed(false), caretMoved(false), 0086 displayNonFocused(KHTMLPart::CaretInvisible), origX(0), 0087 keyReleasePending(false) 0088 {} 0089 }; 0090 0091 class LinearDocument; 0092 0093 /** 0094 * Stores objects of a certain type, and calls delete on each of them 0095 * when this data structure is destroyed. 0096 * 0097 * As this structure merely consists of a vector of pointers, all objects 0098 * allocated can be traversed as seen fit. 0099 * 0100 * @author Leo Savernik 0101 * @internal 0102 */ 0103 template<class T> class MassDeleter : public QVector<T *> 0104 { 0105 public: 0106 MassDeleter(size_t reserved = 1) 0107 { 0108 this->reserve(reserved); 0109 } 0110 ~MassDeleter() 0111 { 0112 typename QVector<T *>::Iterator nd = this->end(); 0113 for (typename QVector<T *>::Iterator it = this->begin(); it != nd; ++it) { 0114 delete *it; 0115 } 0116 } 0117 }; 0118 0119 class CaretBoxLine; 0120 0121 /** 0122 * Represents a rectangular box within which the caret is located. 0123 * 0124 * The caret box serves as a wrapper for inline boxes of all kind. It either 0125 * wraps an InlineBox, InlineTextBox, or InlineFlowBox, or if no such boxes 0126 * exist for a certain context, it contains the relevant information directly. 0127 * 0128 * This class will be constructed whenever a caret position has to be described. 0129 * @author Leo Savernik 0130 * @internal 0131 */ 0132 class CaretBox 0133 { 0134 protected: 0135 InlineBox *_box; // associated inline box if available. 0136 short _w; // width of box in pixels 0137 int _h; // height of box in pixels 0138 int _x; // x coordinate relative to containing block 0139 int _y; // y coordinate relative to containing block 0140 RenderBox *cb; // containing block 0141 bool _outside: 1; // true when representing the outside of the element 0142 bool outside_end: 1; // at ending outside of element rather than at beginning 0143 // 29 bits unused 0144 0145 public: 0146 /** empty constructor for later assignment */ 0147 CaretBox() {} 0148 /** initializes the caret box from the given inline box */ 0149 CaretBox(InlineBox *ibox, bool outside, bool outsideEnd) : _box(ibox), 0150 _w((short)ibox->width()), _h(ibox->height()), _x(ibox->xPos()), 0151 _y(ibox->yPos()), cb(0), _outside(outside), outside_end(outsideEnd) 0152 { 0153 RenderObject *r = ibox->object(); 0154 if (r) { 0155 cb = r->containingBlock(); 0156 } 0157 } 0158 /** initializes the caret box from scratch */ 0159 CaretBox(int x, int y, int w, int h, RenderBox *cb, bool outside, bool outsideEnd) : 0160 _box(0), _w((short)w), _h(h), _x(x), _y(y), cb(cb), _outside(outside), 0161 outside_end(outsideEnd) 0162 {} 0163 0164 int width() const 0165 { 0166 return _w; 0167 } 0168 int height() const 0169 { 0170 return _h; 0171 } 0172 int xPos() const 0173 { 0174 return _x; 0175 } 0176 int yPos() const 0177 { 0178 return _y; 0179 } 0180 RenderBox *enclosingObject() const 0181 { 0182 return cb; 0183 } 0184 InlineBox *inlineBox() const 0185 { 0186 return _box; 0187 } 0188 0189 /** returns the containing block of this caret box. If the caret box 0190 * resembles a block itself, its containing block is returned. 0191 */ 0192 RenderBlock *containingBlock() const 0193 { 0194 return _box ? static_cast<RenderBlock *>(cb) : cb->containingBlock(); 0195 } 0196 0197 /** returns the replaced render object if this caret box represents one, 0198 * 0 otherwise. 0199 */ 0200 0201 /** returns true if this caret box represents an inline element, or text box, 0202 * otherwise false. 0203 */ 0204 bool isInline() const 0205 { 0206 return _box; 0207 } 0208 /** returns true if this caret box represents an inline text box. 0209 */ 0210 bool isInlineTextBox() const 0211 { 0212 return _box && _box->isInlineTextBox(); 0213 } 0214 /** returns true if this caret box represents a line break 0215 */ 0216 bool isLineBreak() const 0217 { 0218 return _box && _box->object() && _box->object()->isBR(); 0219 } 0220 /** returns true when this caret box represents an ouside position of an 0221 * element. 0222 */ 0223 bool isOutside() const 0224 { 0225 return _outside; 0226 } 0227 /** returns the position at which the outside is targeted at. 0228 * 0229 * This method's return value is meaningless if isOutside() is not true. 0230 * @return true if the outside end is meant, false if the outside beginning 0231 * is meant. 0232 */ 0233 bool isOutsideEnd() const 0234 { 0235 return outside_end; 0236 } 0237 /** returns the associated render object. */ 0238 RenderObject *object() const 0239 { 0240 return _box ? _box->object() : cb; 0241 } 0242 0243 /** returns the minimum offset for this caret box. 0244 */ 0245 long minOffset() const 0246 { 0247 return _box && !isLineBreak() ? _box->minOffset() : 0; 0248 } 0249 /** returns the maximum offset for this caret box. 0250 */ 0251 long maxOffset() const 0252 { 0253 return _box && !isLineBreak() ? _box->maxOffset() : 0; 0254 } 0255 0256 #if DEBUG_CARETMODE > 0 0257 void dump(QTextStream &ts, const QString &ind) const; 0258 #endif 0259 0260 friend class CaretBoxLine; 0261 }; 0262 0263 typedef MassDeleter<CaretBox> CaretBoxDeleter; 0264 0265 /** 0266 * Iterates over the elements of a caret box line. 0267 * 0268 * @author Leo Savernik 0269 * @internal 0270 */ 0271 class CaretBoxIterator 0272 { 0273 protected: 0274 CaretBoxLine *cbl; // associated caret box line 0275 int index; // current index 0276 0277 public: 0278 // Let standard constructor/copy constructor/destructor/assignment operator 0279 // be defined by the compiler. They do exactly what we want. 0280 CaretBoxIterator() 0281 : cbl(0), index(0) 0282 { 0283 } 0284 0285 bool operator ==(const CaretBoxIterator &it) const 0286 { 0287 return cbl == it.cbl && index == it.index; 0288 } 0289 0290 bool operator !=(const CaretBoxIterator &it) const 0291 { 0292 return !operator ==(it); 0293 } 0294 0295 /** returns the current caret box. 0296 * @return current caret box 0297 */ 0298 CaretBox *data() const; 0299 /** shortcut for \c data 0300 * @return current caret box 0301 */ 0302 CaretBox *operator *() const 0303 { 0304 return data(); 0305 } 0306 0307 /** increments the iterator to point to the next caret box. 0308 */ 0309 CaretBoxIterator &operator ++() 0310 { 0311 index++; 0312 return *this; 0313 } 0314 /** decrements the iterator to point to the previous caret box. 0315 */ 0316 CaretBoxIterator &operator --() 0317 { 0318 index--; 0319 return *this; 0320 } 0321 0322 friend class CaretBoxLine; 0323 friend class EditableCaretBoxIterator; 0324 }; 0325 0326 /** 0327 * Resembles a line consisting of caret boxes. 0328 * 0329 * To the contrary of InlineFlowBoxes which are nested as needed to map the 0330 * DOM to the rendered representation, it is sufficient for caret navigation 0331 * to provide a linear list of unnested caret boxes. 0332 * 0333 * \code 0334 * Example: The document fragment <p>a <i><b>c</b> f</i> g</p> will be 0335 * represented by three caret box lines which each one consists of caret boxes 0336 * as follows: 0337 * CaretBoxLine 1: 0338 * CaretBox(cb=<p>, _box=0, _outside=true, outside_end=false) 0339 * CaretBoxLine 2: 0340 * CaretBox(cb=<p>, _box=InlineTextBox("a "), _outside=false) 0341 * CaretBox(cb=<p>, _box=InlineFlowBox(<i>), _outside=true, outside_end=false) 0342 * CaretBox(cb=<p>, _box=InlineFlowBox(<b>), _outside=true, outside_end=false) 0343 * CaretBox(cb=<p>, _box=InlineTextBox("c"), _outside=false) 0344 * CaretBox(cb=<p>, _box=InlineFlowBox(<b>), _outside=true, outside_end=true) 0345 * CaretBox(cb=<p>, _box=InlineTextBox(" f"), _outside=false) 0346 * CaretBox(cb=<p>, _box=InlineFlowBox(<i>), _outside=true, outside_end=true) 0347 * CaretBox(cb=<p>, _box=InlineTextBox(" g"), _outside=true, outside_end=true) 0348 * CaretBoxLine 3: 0349 * CaretBox(cb=<p>, _box=0, _outside=true, outside_end=true) 0350 * \endcode 0351 */ 0352 class CaretBoxLine 0353 { 0354 protected: 0355 CaretBoxDeleter caret_boxes; 0356 // base flow box which caret boxes have been constructed for 0357 InlineFlowBox *basefb; 0358 0359 CaretBoxLine() : caret_boxes(8), basefb(0) {} 0360 CaretBoxLine(InlineFlowBox *basefb) : caret_boxes(8), basefb(basefb) {} 0361 public: 0362 #if DEBUG_CARETMODE > 3 0363 ~CaretBoxLine() 0364 { 0365 qCDebug(KHTML_LOG) << "called"; 0366 } 0367 #endif 0368 0369 CaretBoxIterator begin() 0370 { 0371 CaretBoxIterator it; 0372 it.cbl = this; 0373 it.index = 0; 0374 return it; 0375 } 0376 CaretBoxIterator end() 0377 { 0378 CaretBoxIterator it; 0379 it.cbl = this; 0380 it.index = caret_boxes.size(); 0381 return it; 0382 } 0383 CaretBoxIterator preBegin() 0384 { 0385 CaretBoxIterator it; 0386 it.cbl = this; 0387 it.index = -1; 0388 return it; 0389 } 0390 CaretBoxIterator preEnd() 0391 { 0392 CaretBoxIterator it; 0393 it.cbl = this; 0394 it.index = caret_boxes.size() - 1; 0395 return it; 0396 } 0397 0398 /** returns the base inline flow box which the caret boxes of this 0399 * caret box line have been constructed from. 0400 * 0401 * This is generally a root line box, but may be an inline flow box when the 0402 * base is restricted to an inline element. 0403 */ 0404 InlineFlowBox *baseFlowBox() const 0405 { 0406 return basefb; 0407 } 0408 0409 /** returns the containing block */ 0410 RenderBlock *containingBlock() const 0411 { 0412 return caret_boxes[0]->containingBlock(); 0413 } 0414 /** returns the enclosing object */ 0415 RenderBox *enclosingObject() const 0416 { 0417 return caret_boxes[0]->enclosingObject(); 0418 } 0419 0420 /** returns whether this caret box line is outside. 0421 * @return true if this caret box represents an outside position of this 0422 * line box' containing block, false otherwise. 0423 */ 0424 bool isOutside() const 0425 { 0426 const CaretBox *cbox = caret_boxes[0]; 0427 return !cbox->isInline() && cbox->isOutside(); 0428 } 0429 0430 /** returns whether this caret box line is at the outside end. 0431 * 0432 * The result cannot be relied upon unless isOutside() returns true. 0433 */ 0434 bool isOutsideEnd() const 0435 { 0436 return caret_boxes[0]->isOutsideEnd(); 0437 } 0438 0439 /** constructs a new caret box line out of the given inline flow box 0440 * @param deleter deleter which handles alloc+dealloc of the object 0441 * @param baseFlowBox basic flow box which to create a caret line box from 0442 * @param seekBox seek this box within the constructed line 0443 * @param seekOutside denoting whether position is outside of seekBox 0444 * @param seekOutsideEnd whether at the outside end of seekBox 0445 * @param iter returns an iterator that corresponds to seekBox. If no suitable 0446 * caret box exists, it will return end() 0447 * @param seekObject seek this render object within the constructed line. 0448 * It will only be regarded if \c seekBox is 0. \c iter will then point 0449 * to the first caret box whose render object matches. 0450 */ 0451 static CaretBoxLine *constructCaretBoxLine(MassDeleter<CaretBoxLine> *deleter, 0452 InlineFlowBox *baseFlowBox, InlineBox *seekBox, bool seekOutside, 0453 bool seekOutsideEnd, CaretBoxIterator &iter, 0454 RenderObject *seekObject = 0) /*KHTML_NO_EXPORT*/; 0455 0456 /** constructs a new caret box line for the given render block. 0457 * @param deleter deleter which handles alloc+dealloc of the object 0458 * @param cb render block or render replaced 0459 * @param outside true when line is to be constructed outside 0460 * @param outsideEnd true when the ending outside is meant 0461 * @param iter returns the iterator to the caret box representing the given 0462 * position for \c cb 0463 */ 0464 static CaretBoxLine *constructCaretBoxLine(MassDeleter<CaretBoxLine> *deleter, 0465 RenderBox *cb, bool outside, bool outsideEnd, CaretBoxIterator &iter) /*KHTML_NO_EXPORT*/; 0466 0467 #if DEBUG_CARETMODE > 0 0468 void dump(QTextStream &ts, const QString &ind) const; 0469 QString information() const 0470 { 0471 QString result; 0472 QTextStream ts(&result, QIODevice::WriteOnly); 0473 dump(ts, QString()); 0474 return result; 0475 } 0476 #endif 0477 0478 protected: 0479 /** contains the seek parameters */ 0480 struct SeekBoxParams { 0481 InlineBox *box; 0482 bool outside; 0483 bool outsideEnd; 0484 bool found; 0485 RenderObject *r; // if box is 0, seek for equal render objects instead 0486 CaretBoxIterator ⁢ 0487 0488 SeekBoxParams(InlineBox *box, bool outside, bool outsideEnd, RenderObject *obj, CaretBoxIterator &it) 0489 : box(box), outside(outside), outsideEnd(outsideEnd), found(false), r(obj), it(it) 0490 {} 0491 0492 /** compares whether this seek box matches the given specification */ 0493 bool equalsBox(const InlineBox *box, bool outside, bool outsideEnd) const 0494 { 0495 return (this->box && this->box == box 0496 || this->r == box->object()) 0497 && this->outside == outside 0498 && (!this->outside || this->outsideEnd == outsideEnd); 0499 } 0500 /** compares whether this seek box matches the given caret box */ 0501 bool operator ==(const CaretBox *cbox) const 0502 { 0503 return equalsBox(cbox->inlineBox(), cbox->isOutside(), cbox->isOutsideEnd()); 0504 } 0505 /** checks whether this box matches the given iterator. 0506 * 0507 * On success, it sets \c found, and assigns the iterator to \c it. 0508 * @return true on match 0509 */ 0510 bool check(const CaretBoxIterator &chit) 0511 { 0512 if (*this == *chit) { 0513 Q_ASSERT(!found); 0514 found = true; 0515 it = chit; 0516 } 0517 return found; 0518 } 0519 }; 0520 0521 /** recursively converts the given inline box into caret boxes and adds them 0522 * to this caret box line. 0523 * 0524 * It will additionally look for the caret box specified in SeekBoxParams. 0525 */ 0526 void addConvertedInlineBox(InlineBox *, SeekBoxParams &) /*KHTML_NO_EXPORT*/; 0527 0528 /** creates and adds the edge of a generic inline box 0529 * @param box inline box 0530 * @param fm font metrics of inline box 0531 * @param left true to add left edge, false to add right edge 0532 * @param rtl true if direction is rtl 0533 */ 0534 void addCreatedInlineBoxEdge(InlineBox *box, const QFontMetrics &fm, 0535 bool left, bool rtl) /*KHTML_NO_EXPORT*/; 0536 /** creates and adds the edge of an inline flow box 0537 * @param flowBox inline flow box 0538 * @param fm font metrics of inline flow box 0539 * @param left true to add left edge, false to add right edge 0540 * @param rtl true if direction is rtl 0541 */ 0542 void addCreatedFlowBoxEdge(InlineFlowBox *flowBox, const QFontMetrics &fm, 0543 bool left, bool rtl) /*KHTML_NO_EXPORT*/; 0544 /** creates and adds the inside of an inline flow box 0545 * @param flowBox inline flow box 0546 * @param fm font metrics of inline flow box 0547 */ 0548 void addCreatedFlowBoxInside(InlineFlowBox *flowBox, const QFontMetrics &fm) /*KHTML_NO_EXPORT*/; 0549 0550 friend class CaretBoxIterator; 0551 }; 0552 0553 typedef MassDeleter<CaretBoxLine> CaretBoxLineDeleter; 0554 0555 inline CaretBox *CaretBoxIterator::data() const 0556 { 0557 return cbl->caret_boxes[index]; 0558 } 0559 0560 /** 0561 * Iterates through the lines of a document. 0562 * 0563 * The line iterator becomes invalid when the associated LinearDocument object 0564 * is destroyed. 0565 * @internal 0566 * @author Leo Savernik 0567 */ 0568 class LineIterator 0569 { 0570 protected: 0571 LinearDocument *lines; // associated document 0572 CaretBoxLine *cbl; // current caret box line 0573 0574 static CaretBoxIterator currentBox; // current inline box 0575 static long currentOffset; 0576 0577 // Note: cbl == 0 indicates a position beyond the beginning or the 0578 // end of a document. 0579 0580 /** Default constructor, only for internal use 0581 */ 0582 LineIterator() {} 0583 0584 /** Initializes a new iterator. 0585 * 0586 * Note: This constructor neither cares about the correctness of @p node 0587 * nor about @p offset. It is the responsibility of the caller to ensure 0588 * that both point to valid places. 0589 */ 0590 LineIterator(LinearDocument *l, DOM::NodeImpl *node, long offset); 0591 0592 public: 0593 /** dereferences current caret box line. 0594 * 0595 * @returns the caret line box or 0 if end of document 0596 */ 0597 CaretBoxLine *operator *() const 0598 { 0599 return cbl; 0600 } 0601 0602 /** returns the associated linear document 0603 */ 0604 LinearDocument *linearDocument() const 0605 { 0606 return lines; 0607 } 0608 0609 /** seek next line 0610 * 0611 * Guaranteed to crash if beyond beginning/end of document. 0612 */ 0613 LineIterator &operator ++() 0614 { 0615 advance(false); 0616 return *this; 0617 } 0618 0619 /** seek previous line. 0620 * 0621 * Guaranteed to crash if beyond beginning/end of document. 0622 */ 0623 LineIterator &operator --() 0624 { 0625 advance(true); 0626 return *this; 0627 } 0628 0629 /** compares two iterators. The comparator actually works only for 0630 * comparing arbitrary iterators to begin() and end(). 0631 */ 0632 bool operator ==(const LineIterator &it) const 0633 { 0634 return lines == it.lines && cbl == it.cbl; 0635 } 0636 0637 /** compares two iterators 0638 */ 0639 bool operator !=(const LineIterator &it) const 0640 { 0641 return !operator ==(it); 0642 } 0643 0644 /** Returns whether this line represents the outside end of the containing 0645 * block. 0646 * 0647 * This result can only be relied on when isOutside is true. 0648 */ 0649 bool isOutsideEnd() 0650 { 0651 return cbl->isOutsideEnd(); 0652 } 0653 0654 /** Tells whether the offset is meant to be outside or inside the 0655 * containing block. 0656 */ 0657 bool isOutside() const 0658 { 0659 return cbl->isOutside(); 0660 } 0661 0662 /** advances to the line to come. 0663 * @param toBegin true, move to previous line, false, move to next line. 0664 */ 0665 void advance(bool toBegin); 0666 0667 /** Whenever a new line iterator is created, it gets a caret box created. 0668 * For memory reasons, it's saved in a static instance, 0669 * thus making this function not thread-safe. 0670 * 0671 * This value can only be trusted immediately after having instantiated 0672 * a line iterator or one of its derivatives. 0673 * @return an iterator onto the corresponing caret box within the 0674 * line represented by the last instantiation of a line iterator, 0675 * or 0 if there was none. 0676 */ 0677 static CaretBoxIterator ¤tCaretBox() 0678 { 0679 return currentBox; 0680 } 0681 0682 /** Whenever a new line iterator is created, it calculates a modified offset 0683 * that is to be used with respect to the current render object. 0684 * This offset can be queried with this function. 0685 * 0686 * This value can only be trusted immediately after having instantiated 0687 * a line iterator or one of its derivatives. 0688 * @return the modified offset. 0689 */ 0690 static long currentModifiedOffset() 0691 { 0692 return currentOffset; 0693 } 0694 0695 protected: 0696 /** seeks next block. 0697 */ 0698 void nextBlock(); 0699 /** seeks previous block. 0700 */ 0701 void prevBlock(); 0702 0703 friend class CaretBoxIterator; 0704 friend class EditableLineIterator; 0705 friend class EditableCaretBoxIterator; 0706 friend class EditableCharacterIterator; 0707 friend class LinearDocument; 0708 }; 0709 0710 /** 0711 * Represents the whole document in terms of lines. 0712 * 0713 * SGML documents are trees. But for navigation, this representation is 0714 * not practical. Therefore this class serves as a helper to represent the 0715 * document as a linear list of lines. Its usage somewhat resembles STL 0716 * semantics like begin and end as well as iterators. 0717 * 0718 * The lines itself are represented as caret line boxes. 0719 * 0720 * LinearDocument instances are not meant to be kept over the lifetime of their 0721 * associated document, but constructed from (node, offset) pairs whenever line 0722 * traversal is needed. This is because the underlying InlineFlowBox objects 0723 * may be destroyed and recreated (e. g. by resizing the window, adding/removing 0724 * elements). 0725 * 0726 * @author Leo Savernik 0727 * @internal 0728 */ 0729 class LinearDocument 0730 { 0731 public: 0732 typedef LineIterator Iterator; 0733 0734 /** 0735 * Creates a new instance, and initializes it to the line specified by 0736 * the parameters below. 0737 * 0738 * Creation will fail if @p node is invisible or defect. 0739 * @param part part within which everything is taking place. 0740 * @param node document node with which to start 0741 * @param offset zero-based offset within this node. 0742 * @param advancePolicy caret advance policy 0743 * @param baseElem base element which the caret must not advance beyond 0744 * (0 means whole document). The base element will be ignored if it 0745 * cannot serve as a base (to see if this is the case, check whether 0746 * LinearDocument::baseFlow()->element() != base) 0747 */ 0748 LinearDocument(KHTMLPart *part, DOM::NodeImpl *node, long offset, 0749 CaretAdvancePolicy advancePolicy, DOM::ElementImpl *baseElem); 0750 0751 virtual ~LinearDocument(); 0752 0753 /** 0754 * Tells whether this list contains any lines. 0755 * 0756 * @returns @p true if this document contains lines, @p false otherwise. Note 0757 * that an empty document contains at least one line, so this method 0758 * only returns @p false if the document could not be initialised for 0759 * some reason. 0760 */ 0761 bool isValid() const // FIXME: not yet impl'd 0762 { 0763 return true; 0764 } 0765 0766 /** 0767 * Returns the count of lines. 0768 * 0769 * Warning: This function is expensive. Call it once and cache the value. 0770 * 0771 * FIXME: It's not implemented yet (and maybe never will) 0772 */ 0773 int count() const; 0774 0775 /** 0776 * Returns a line iterator containing the current position as its starting 0777 * value. 0778 */ 0779 Iterator current(); 0780 0781 /** 0782 * Returns a line iterator pointing right after the end of the document. 0783 */ 0784 const Iterator &end() const 0785 { 0786 return _end; 0787 } 0788 0789 /** 0790 * Returns a line iterator pointing to the very last line of the document. 0791 */ 0792 Iterator preEnd(); 0793 0794 /** 0795 * Returns a line iterator pointing to the very first line of the document. 0796 */ 0797 Iterator begin(); 0798 0799 /** 0800 * Returns a line iterator pointing just before the very first line of the 0801 * document (this is somewhat an emulation of reverse iterators). 0802 */ 0803 const Iterator &preBegin() const 0804 { 0805 return _preBegin; 0806 } 0807 0808 /** 0809 * Returns the current caret advance policy 0810 */ 0811 CaretAdvancePolicy advancePolicy() const 0812 { 0813 return advPol; 0814 } 0815 0816 /** 0817 * Returns the base render object which the caret must not advance beyond. 0818 * 0819 * Note that HTML documents are usually restricted to the body element. 0820 * 0821 * @return the base render object or 0 if the whole document is valid. 0822 */ 0823 RenderObject *baseObject() const 0824 { 0825 return base; 0826 } 0827 0828 protected: 0829 void initPreBeginIterator(); 0830 void initEndIterator(); 0831 0832 protected: 0833 CaretBoxLineDeleter cblDeleter; // mass deleter for caret box lines 0834 DOM::NodeImpl *node; 0835 long offset; 0836 0837 Iterator _preBegin; 0838 Iterator _end; 0839 0840 KHTMLPart *m_part; 0841 CaretAdvancePolicy advPol; 0842 RenderObject *base; 0843 0844 friend class LineIterator; 0845 friend class EditableLineIterator; 0846 friend class ErgonomicEditableLineIterator; 0847 friend class CaretBoxIterator; 0848 friend class EditableCaretBoxIterator; 0849 friend class EditableCharacterIterator; 0850 }; 0851 0852 /** 0853 * Iterates over the editable inner elements of a caret line box. 0854 * 0855 * The incrementor will traverse all caret boxes according to the associated 0856 * linear document's caret advance policy. In contrast to \c CaretBoxIterator 0857 * this iterator only regards caret boxes which are editable. 0858 * 0859 * @author Leo Savernik 0860 * @internal 0861 */ 0862 class EditableCaretBoxIterator : public CaretBoxIterator 0863 { 0864 KHTMLPart *m_part; 0865 bool adjacent; 0866 CaretAdvancePolicy advpol; // caret advance policy 0867 0868 public: 0869 /** initializes a new iterator from the given line iterator, 0870 * beginning with the given caret box iterator, if specified 0871 */ 0872 EditableCaretBoxIterator(LineIterator &lit, bool fromEnd = false, 0873 CaretBoxIterator *it = 0) 0874 : CaretBoxIterator(it ? *it : (fromEnd ? (*lit)->end() : (*lit)->preBegin())), 0875 m_part(lit.lines->m_part), adjacent(false), 0876 advpol(lit.lines->advancePolicy()) 0877 { 0878 if (!it) { 0879 if (fromEnd) { 0880 --*this; 0881 } else { 0882 ++*this; 0883 } 0884 } 0885 } 0886 0887 /** empty constructor. Use only to copy another iterator into this one. 0888 */ 0889 EditableCaretBoxIterator() {} 0890 0891 /** returns @p true when the current caret box is adjacent to the 0892 * previously iterated caret box, i. e. no intervening caret boxes. 0893 */ 0894 bool isAdjacent() const 0895 { 0896 return adjacent; 0897 } 0898 0899 /** increments the iterator to point to the next editable caret box. 0900 */ 0901 EditableCaretBoxIterator &operator ++() 0902 { 0903 advance(false); 0904 return *this; 0905 } 0906 0907 /** decrements the iterator to point to the previous editable caret box. 0908 */ 0909 EditableCaretBoxIterator &operator --() 0910 { 0911 advance(true); 0912 return *this; 0913 } 0914 0915 /** advances to the editable caret box to come 0916 * @param toBegin true, move towards beginning, false, move towards end. 0917 */ 0918 void advance(bool toBegin); 0919 0920 protected: 0921 /** finds out if the given box is editable. 0922 * @param boxit iterator to given caret box 0923 * @param fromEnd true when advancing towards the beginning 0924 * @return @p true if box is editable 0925 */ 0926 bool isEditable(const CaretBoxIterator &boxit, bool fromEnd); 0927 }; 0928 0929 /** 0930 * Iterates through the editable lines of a document. 0931 * 0932 * This iterator, opposing to @p LineIterator, only regards editable lines. 0933 * Additionally, this iterator enforces the caret advance policy. 0934 * 0935 * The iterator can be compared to normal LineIterators, especially to 0936 * @ref LinearDocument::preBegin and @ref LinearDocument::end 0937 * 0938 * The line iterator becomes invalid when the associated LinearDocument object 0939 * is destroyed. 0940 * @internal 0941 * @author Leo Savernik 0942 */ 0943 class EditableLineIterator : public LineIterator 0944 { 0945 public: 0946 /** Initializes a new iterator. 0947 * 0948 * The iterator is set to the first following editable line or to the 0949 * end if no editable line follows. 0950 * @param it a line iterator to initialize this from 0951 * @param fromEnd @p true, traverse towards the beginning in search of an 0952 * editable line 0953 */ 0954 EditableLineIterator(const LineIterator &it, bool fromEnd = false) 0955 : LineIterator(it) 0956 { 0957 if (!cbl) { 0958 return; 0959 } 0960 if (!isEditable(*this)) { 0961 advance(fromEnd); 0962 } 0963 } 0964 0965 /** empty constructor. 0966 * 0967 * Only use if you want to copy another iterator onto it later. 0968 */ 0969 EditableLineIterator() {} 0970 0971 /** seek next line 0972 * 0973 * Guaranteed to crash if beyond beginning/end of document. 0974 */ 0975 EditableLineIterator &operator ++() 0976 { 0977 advance(false); 0978 return *this; 0979 } 0980 0981 /** seek previous line. 0982 * 0983 * Guaranteed to crash if beyond beginning/end of document. 0984 */ 0985 EditableLineIterator &operator --() 0986 { 0987 advance(true); 0988 return *this; 0989 } 0990 0991 /** advances to the line to come. 0992 * @param toBegin true, move to previous line, false, move to next line. 0993 */ 0994 void advance(bool toBegin); 0995 0996 protected: 0997 /** finds out if the current line is editable. 0998 * 0999 * @param it check caret box line iterator points to 1000 * @return @p true if line is editable 1001 */ 1002 bool isEditable(LineIterator &it) 1003 { 1004 EditableCaretBoxIterator fbit = it; 1005 return fbit != (*it)->end(); 1006 } 1007 1008 }; 1009 1010 /** Represents a render table as a linear list of rows. 1011 * 1012 * This iterator abstracts from table sections and treats tables as a linear 1013 * representation of all rows they contain. 1014 * @author Leo Savernik 1015 * @internal 1016 */ 1017 class TableRowIterator 1018 { 1019 protected: 1020 TableSectionIterator sec; // current section 1021 int index; // index of row within section 1022 public: 1023 /** Constructs a new iterator. 1024 * @param table table to iterate through. 1025 * @param fromEnd @p true to iterate towards the beginning 1026 * @param row pointer to row to start with, 0 starts at the first/last 1027 * row. 1028 */ 1029 TableRowIterator(RenderTable *table, bool fromEnd = false, 1030 RenderTableSection::RowStruct *row = 0); 1031 1032 /** Constructs a new iterator. 1033 * @param section table section to begin with 1034 * @param index index within table section 1035 */ 1036 TableRowIterator(RenderTableSection *section, int index) 1037 : sec(section), index(index) 1038 {} 1039 1040 /** empty constructor. This must be assigned another iterator before it is 1041 * useable. 1042 */ 1043 TableRowIterator() {} 1044 1045 /** returns the current table row. 1046 * @return the row or 0 if the end of the table has been reached. 1047 */ 1048 RenderTableSection::RowStruct *operator *() 1049 { 1050 if (!*sec) { 1051 return 0; 1052 } 1053 return &(*sec)->grid[index]; 1054 } 1055 1056 /** advances to the next row 1057 */ 1058 TableRowIterator &operator ++(); 1059 1060 /** advances to the previous row 1061 */ 1062 TableRowIterator &operator --(); 1063 1064 protected: 1065 }; 1066 1067 /** Iterates through the editable lines of a document, in a topological order. 1068 * 1069 * The differences between this and the EditableLineIterator lies in the way 1070 * lines are inquired. While the latter steps through the lines in document 1071 * order, the former takes into consideration ergonomics. 1072 * 1073 * This is especially useful for tables. EditableLineIterator traverses all 1074 * table cells from left to right, top to bottom, while this one will 1075 * actually snap to the cell in the right position, and traverse only 1076 * upwards/downwards, thus providing a more intuitive navigation. 1077 * 1078 * @author Leo Savernik 1079 * @internal 1080 */ 1081 class ErgonomicEditableLineIterator : public EditableLineIterator 1082 { 1083 protected: 1084 int xCoor; // x-coordinate to determine cell position 1085 public: 1086 /** Initializes a new ergonomic editable line iterator from the given one. 1087 * @param it line iterator 1088 * @param x absolute x-coordinate for cell determination 1089 */ 1090 ErgonomicEditableLineIterator(const LineIterator &it, int x) 1091 : EditableLineIterator(it), xCoor(x) {} 1092 1093 /** Constructs an uninitialized iterator which must be assigned a line iterator before 1094 * it can be used. 1095 */ 1096 ErgonomicEditableLineIterator() {} 1097 1098 /** seek next line. 1099 * 1100 * The next line will be one that is visually situated below this line. 1101 */ 1102 ErgonomicEditableLineIterator &operator ++(); 1103 1104 /** seek previous line. 1105 * 1106 * The previous line will be one that is visually situated above this line. 1107 */ 1108 ErgonomicEditableLineIterator &operator --(); 1109 1110 protected: 1111 /** determines the topologically next render object. 1112 * @param oldCell table cell the original object was under. 1113 * @param newObject object to determine whether and which transition 1114 * between cells is to be handled. It does not have to be an object in the correct 1115 * topological cell, a simple delivery from an editable line iterator suffices. 1116 * @param toBegin if @p true, iterate towards the beginning 1117 */ 1118 void determineTopologicalElement(RenderTableCell *oldCell, 1119 RenderObject *newObject, bool toBegin); 1120 1121 /** initializes the iterator to point to the first previous/following editable 1122 * line. 1123 * @param newBlock take this as base block. 1124 * @param toBegin @p true, iterate towards beginning. 1125 */ 1126 void calcAndStoreNewLine(RenderBlock *newBlock, bool toBegin); 1127 1128 }; 1129 1130 /** 1131 * Provides iterating through the document in terms of characters. Only the 1132 * editable characters are regarded. 1133 * 1134 * This iterator represents the document, which is structured as a tree itself, 1135 * as a linear stream of characters. 1136 */ 1137 class EditableCharacterIterator 1138 { 1139 protected: 1140 EditableLineIterator _it; 1141 EditableCaretBoxIterator ebit; 1142 long _offset; // offset within current caret box. 1143 int _char; 1144 bool _end: 1; // true when end of document has been reached 1145 1146 public: 1147 1148 /** empty constructor. 1149 * 1150 * Only use if you want to assign another iterator as no fields will 1151 * be initialized. 1152 */ 1153 EditableCharacterIterator() {} 1154 1155 /** constructs a new iterator from the given linear document. 1156 * 1157 * @param ld linear representation of document. 1158 */ 1159 EditableCharacterIterator(LinearDocument *ld) 1160 : _it(ld->current()), 1161 ebit(_it, false, &_it.currentCaretBox()), 1162 _offset(_it.currentModifiedOffset()), _char(-1), _end(false) 1163 { 1164 // ### temporary fix for illegal nodes 1165 if (_it == ld->end()) { 1166 _end = true; 1167 return; 1168 } 1169 initFirstChar(); 1170 } 1171 1172 /** returns the current character, or -1 if not on a text node, or beyond 1173 * the end. 1174 */ 1175 int chr() const 1176 { 1177 return _char; 1178 } 1179 1180 /** returns the current character as a unicode symbol, substituting 1181 * a blank for a non-text node. 1182 */ 1183 QChar operator *() const 1184 { 1185 return QChar(_char >= 0 ? _char : ' '); 1186 } 1187 1188 /** returns true when the end of the document has been reached. 1189 */ 1190 bool isEnd() const 1191 { 1192 return _end; 1193 } 1194 /** returns the current offset 1195 */ 1196 long offset() const 1197 { 1198 return _offset; 1199 } 1200 /** returns the current render object. 1201 */ 1202 RenderObject *renderer() const 1203 { 1204 return (*ebit)->object(); 1205 } 1206 /** returns the current caret box. 1207 * 1208 * Will crash if beyond end. 1209 */ 1210 CaretBox *caretBox() const 1211 { 1212 return *ebit; 1213 } 1214 /** returns the current inline box. 1215 * 1216 * May be 0 if the current element has none, or if the end has been reached. 1217 * Therefore, do *not* use this to test for the end condition, use node() 1218 * instead. 1219 */ 1220 InlineBox *inlineBox() const 1221 { 1222 return (*ebit)->inlineBox(); 1223 } 1224 /** returns whether the current line box represents the outside of its 1225 * render object. 1226 */ 1227 // bool boxIsOutside() const { return _it.isOutside(); } 1228 1229 /** moves to the next editable character. 1230 */ 1231 EditableCharacterIterator &operator ++(); 1232 1233 /** moves to the previous editable character. 1234 */ 1235 EditableCharacterIterator &operator --(); 1236 1237 protected: 1238 /** initializes the _char member by reading the character at the current 1239 * offset, peeking ahead as necessary. 1240 */ 1241 void initFirstChar(); 1242 /** reads ahead the next node and updates the data structures accordingly 1243 */ 1244 void peekNext() 1245 { 1246 EditableCaretBoxIterator copy = ebit; 1247 ++copy; 1248 if (copy == (*_it)->end()) { 1249 _char = -1; 1250 return; 1251 } 1252 1253 CaretBox *box = *copy; 1254 InlineBox *b = box->inlineBox(); 1255 if (b && !box->isOutside() && b->isInlineTextBox()) { 1256 _char = static_cast<RenderText *>(b->object())->str->s[b->minOffset()].unicode(); 1257 } else { 1258 _char = -1; 1259 } 1260 } 1261 /** reads ahead the previous node and updates the data structures accordingly 1262 */ 1263 void peekPrev() 1264 { 1265 --ebit; 1266 } 1267 1268 }; 1269 1270 }/*namespace khtml*/ 1271 1272 #endif