/* This file is part of the KDE project * * Copyright (C) 2003-2004 Leo Savernik * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef TDEHTML_CARET_P_H #define TDEHTML_CARET_P_H #include "rendering/render_table.h" #include #define DEBUG_CARETMODE 0 class TQFontMetrics; namespace DOM { class NodeImpl; class ElementImpl; } namespace tdehtml { /** caret advance policy. * * Used to determine which elements are taken into account when the caret is * advanced. Later policies pose refinements of all former * policies. * @param LeafsOnly advance from leave render object to leaf render object * (It will allow outside flow positions if a flow wouldn't be reachable * otherwise). * @param IndicatedFlows place caret also at the beginning/end of flows * that have at least one visible border at any side. * (It will allow not indicated flow positions if a flow wouldn't * be reachable otherwise). * @param VisibleFlows place caret also at the beginning/end of any flow * that has a renderer. */ enum CaretAdvancePolicy { LeafsOnly, IndicatedFlows, VisibleFlows }; /** contextual information about the caret which is related to the view. * An object of this class is only instantiated when it is needed. */ struct CaretViewContext { int freqTimerId; // caret blink frequency timer id int x, y; // caret position in viewport coordinates // (y specifies the top, not the baseline) int width; // width of caret in pixels int height; // height of caret in pixels bool visible; // true if currently visible. bool displayed; // true if caret is to be displayed at all. bool caretMoved; // set to true once caret has been moved in page // how to display the caret when view is not focused TDEHTMLPart::CaretDisplayPolicy displayNonFocused; /** For natural traversal of lines, the original x position is saved, and * the actual x is set to the first character whose x position is * greater than origX. * * origX is reset to x whenever the caret is moved horizontally or placed * by the mouse. */ int origX; bool keyReleasePending; // true if keypress under caret mode awaits // corresponding release event CaretViewContext() : freqTimerId(-1), x(0), y(0), width(1), height(16), visible(true), displayed(false), caretMoved(false), displayNonFocused(TDEHTMLPart::CaretInvisible), origX(0), keyReleasePending(false) {} }; /** contextual information about the editing state. * An object of this class is only instantiated when it is needed. */ struct EditorContext { bool override; // true if typed characters should override // the existing ones. EditorContext() : override(false) {} }; class LinearDocument; /** * Stores objects of a certain type, and calls delete on each of them * when this data structure is destroyed. * * As this structure merely consists of a vector of pointers, all objects * allocated can be traversed as seen fit. * * @author Leo Savernik * @since 3.3 * @internal */ template class MassDeleter : public TQValueVector { public: MassDeleter(size_t reserved = 1) { this->reserve(reserved); } ~MassDeleter() { typename TQValueVector::Iterator nd = this->end(); for (typename TQValueVector::Iterator it = this->begin(); it != nd; ++it) delete *it; } }; class CaretBoxLine; /** * Represents a rectangular box within which the caret is located. * * The caret box serves as a wrapper for inline boxes of all kind. It either * wraps an InlineBox, InlineTextBox, or InlineFlowBox, or if no such boxes * exist for a certain context, it contains the relevant information directly. * * This class will be constructed whenever a caret position has to be described. * @since 3.3 * @author Leo Savernik * @internal */ class CaretBox { protected: InlineBox *_box; // associated inline box if available. short _w; // width of box in pixels int _h; // height of box in pixels int _x; // x coordinate relative to containing block int _y; // y coordinate relative to containing block RenderBox *cb; // containing block bool _outside:1; // true when representing the outside of the element bool outside_end:1; // at ending outside of element rather than at beginning // 29 bits unused public: /** empty constructor for later assignment */ CaretBox() {} /** initializes the caret box from the given inline box */ CaretBox(InlineBox *ibox, bool outside, bool outsideEnd) : _box(ibox), _w((short)ibox->width()), _h(ibox->height()), _x(ibox->xPos()), _y(ibox->yPos()), cb(0), _outside(outside), outside_end(outsideEnd) { RenderObject *r = ibox->object(); if (r) cb = r->containingBlock(); } /** initializes the caret box from scratch */ CaretBox(int x, int y, int w, int h, RenderBox *cb, bool outside, bool outsideEnd) : _box(0), _w((short)w), _h(h), _x(x), _y(y), cb(cb), _outside(outside), outside_end(outsideEnd) {} int width() const { return _w; } int height() const { return _h; } int xPos() const { return _x; } int yPos() const { return _y; } RenderBox *enclosingObject() const { return cb; } InlineBox *inlineBox() const { return _box; } /** returns the containing block of this caret box. If the caret box * resembles a block itself, its containing block is returned. */ RenderBlock *containingBlock() const { return _box ? static_cast(cb) : cb->containingBlock(); } /** returns the replaced render object if this caret box represents one, * 0 otherwise. */ /** returns true if this caret box represents an inline element, or text box, * otherwise false. */ bool isInline() const { return _box; } /** returns true if this caret box represents an inline text box. */ bool isInlineTextBox() const { return _box && _box->isInlineTextBox(); } /** returns true if this caret box represents a line break */ bool isLineBreak() const { return _box && _box->object() && _box->object()->isBR(); } /** returns true when this caret box represents an ouside position of an * element. */ bool isOutside() const { return _outside; } /** returns the position at which the outside is targeted at. * * This method's return value is meaningless if isOutside() is not true. * @return true if the outside end is meant, false if the outside beginning * is meant. */ bool isOutsideEnd() const { return outside_end; } /** returns the associated render object. */ RenderObject *object() const { return _box ? _box->object() : cb; } /** returns the minimum offset for this caret box. */ long minOffset() const { return _box && !isLineBreak() ? _box->minOffset() : 0; } /** returns the maximum offset for this caret box. */ long maxOffset() const { return _box && !isLineBreak() ? _box->maxOffset() : 0; } #if DEBUG_CARETMODE > 0 void dump(TQTextStream &ts, const TQString &ind) const; #endif friend class CaretBoxLine; }; typedef MassDeleter CaretBoxDeleter; /** * Iterates over the elements of a caret box line. * * @author Leo Savernik * @internal * @since 3.3 */ class CaretBoxIterator { protected: CaretBoxLine *cbl; // associated caret box line int index; // current index public: // Let standard constructor/copy constructor/destructor/assignment operator // be defined by the compiler. They do exactly what we want. bool operator ==(const CaretBoxIterator &it) const { return cbl == it.cbl && index == it.index; } bool operator !=(const CaretBoxIterator &it) const { return !operator ==(it); } /** returns the current caret box. * @return current caret box */ CaretBox *data() const; /** shortcut for \c data * @return current caret box */ CaretBox *operator *() const { return data(); } /** increments the iterator to point to the next caret box. */ CaretBoxIterator &operator ++() { index++; return *this; } /** decrements the iterator to point to the previous caret box. */ CaretBoxIterator &operator --() { index--; return *this; } friend class CaretBoxLine; friend class EditableCaretBoxIterator; }; /** * Resembles a line consisting of caret boxes. * * To the contrary of InlineFlowBoxes which are nested as needed to map the * DOM to the rendered representation, it is sufficient for caret navigation * to provide a linear list of unnested caret boxes. * * \code * Example: The document fragment

a c f g

will be * represented by three caret box lines which each one consists of caret boxes * as follows: * CaretBoxLine 1: * CaretBox(cb=

, _box=0, _outside=true, outside_end=false) * CaretBoxLine 2: * CaretBox(cb=

, _box=InlineTextBox("a "), _outside=false) * CaretBox(cb=

, _box=InlineFlowBox(), _outside=true, outside_end=false) * CaretBox(cb=

, _box=InlineFlowBox(), _outside=true, outside_end=false) * CaretBox(cb=

, _box=InlineTextBox("c"), _outside=false) * CaretBox(cb=

, _box=InlineFlowBox(), _outside=true, outside_end=true) * CaretBox(cb=

, _box=InlineTextBox(" f"), _outside=false) * CaretBox(cb=

, _box=InlineFlowBox(), _outside=true, outside_end=true) * CaretBox(cb=

, _box=InlineTextBox(" g"), _outside=true, outside_end=true) * CaretBoxLine 3: * CaretBox(cb=

, _box=0, _outside=true, outside_end=true) * \endcode */ class CaretBoxLine { protected: CaretBoxDeleter caret_boxes; // base flow box which caret boxes have been constructed for InlineFlowBox *basefb; CaretBoxLine() : caret_boxes(8), basefb(0) {} CaretBoxLine(InlineFlowBox *basefb) : caret_boxes(8), basefb(basefb) {} public: #if DEBUG_CARETMODE > 3 ~CaretBoxLine() { kdDebug(6200) << k_funcinfo << "called" << endl; } #endif CaretBoxIterator begin() { CaretBoxIterator it; it.cbl = this; it.index = 0; return it; } CaretBoxIterator end() { CaretBoxIterator it; it.cbl = this; it.index = caret_boxes.size(); return it; } CaretBoxIterator preBegin() { CaretBoxIterator it; it.cbl = this; it.index = -1; return it; } CaretBoxIterator preEnd() { CaretBoxIterator it; it.cbl = this; it.index = caret_boxes.size() - 1; return it; } /** returns the base inline flow box which the caret boxes of this * caret box line have been constructed from. * * This is generally a root line box, but may be an inline flow box when the * base is restricted to an inline element. */ InlineFlowBox *baseFlowBox() const { return basefb; } /** returns the containing block */ RenderBlock *containingBlock() const { return caret_boxes[0]->containingBlock(); } /** returns the enclosing object */ RenderBox *enclosingObject() const { return caret_boxes[0]->enclosingObject(); } /** returns whether this caret box line is outside. * @return true if this caret box represents an outside position of this * line box' containing block, false otherwise. */ bool isOutside() const { const CaretBox *cbox = caret_boxes[0]; return !cbox->isInline() && cbox->isOutside(); } /** returns whether this caret box line is at the outside end. * * The result cannot be relied upon unless isOutside() returns true. */ bool isOutsideEnd() const { return caret_boxes[0]->isOutsideEnd(); } /** constructs a new caret box line out of the given inline flow box * @param deleter deleter which handles alloc+dealloc of the object * @param baseFlowBox basic flow box which to create a caret line box from * @param seekBox seek this box within the constructed line * @param seekOutside denoting whether position is outside of seekBox * @param seekOutsideEnd whether at the outside end of seekBox * @param iter returns an iterator that corresponds to seekBox. If no suitable * caret box exists, it will return end() * @param seekObject seek this render object within the constructed line. * It will only be regarded if \c seekBox is 0. \c iter will then point * to the first caret box whose render object matches. */ static CaretBoxLine *constructCaretBoxLine(MassDeleter *deleter, InlineFlowBox *baseFlowBox, InlineBox *seekBox, bool seekOutside, bool seekOutsideEnd, CaretBoxIterator &iter, RenderObject *seekObject = 0) /*KDE_NO_EXPORT*/; /** constructs a new caret box line for the given render block. * @param deleter deleter which handles alloc+dealloc of the object * @param cb render block or render replaced * @param outside true when line is to be constructed outside * @param outsideEnd true when the ending outside is meant * @param iter returns the iterator to the caret box representing the given * position for \c cb */ static CaretBoxLine *constructCaretBoxLine(MassDeleter *deleter, RenderBox *cb, bool outside, bool outsideEnd, CaretBoxIterator &iter) /*KDE_NO_EXPORT*/; #if DEBUG_CARETMODE > 0 void dump(TQTextStream &ts, const TQString &ind) const; TQString information() const { TQString result; TQTextStream ts(&result, IO_WriteOnly); dump(ts, TQString::null); return result; } #endif protected: /** contains the seek parameters */ struct SeekBoxParams { InlineBox *box; bool outside; bool outsideEnd; bool found; RenderObject *r; // if box is 0, seek for equal render objects instead CaretBoxIterator ⁢ SeekBoxParams(InlineBox *box, bool outside, bool outsideEnd, RenderObject *obj, CaretBoxIterator &it) : box(box), outside(outside), outsideEnd(outsideEnd), found(false), r(obj), it(it) {} /** compares whether this seek box matches the given specification */ bool equalsBox(const InlineBox *box, bool outside, bool outsideEnd) const { return (this->box && this->box == box || this->r == box->object()) && this->outside == outside && (!this->outside || this->outsideEnd == outsideEnd); } /** compares whether this seek box matches the given caret box */ bool operator ==(const CaretBox *cbox) const { return equalsBox(cbox->inlineBox(), cbox->isOutside(), cbox->isOutsideEnd()); } /** checks whether this box matches the given iterator. * * On success, it sets \c found, and assigns the iterator to \c it. * @return true on match */ bool check(const CaretBoxIterator &chit) { if (*this == *chit) { Q_ASSERT(!found); found = true; it = chit; } return found; } }; /** recursively converts the given inline box into caret boxes and adds them * to this caret box line. * * It will additionally look for the caret box specified in SeekBoxParams. */ void addConvertedInlineBox(InlineBox *, SeekBoxParams &) /*KDE_NO_EXPORT*/; /** creates and adds the edge of a generic inline box * @param box inline box * @param fm font metrics of inline box * @param left true to add left edge, false to add right edge * @param rtl true if direction is rtl */ void addCreatedInlineBoxEdge(InlineBox *box, const TQFontMetrics &fm, bool left, bool rtl) /*KDE_NO_EXPORT*/; /** creates and adds the edge of an inline flow box * @param flowBox inline flow box * @param fm font metrics of inline flow box * @param left true to add left edge, false to add right edge * @param rtl true if direction is rtl */ void addCreatedFlowBoxEdge(InlineFlowBox *flowBox, const TQFontMetrics &fm, bool left, bool rtl) /*KDE_NO_EXPORT*/; /** creates and adds the inside of an inline flow box * @param flowBox inline flow box * @param fm font metrics of inline flow box */ void addCreatedFlowBoxInside(InlineFlowBox *flowBox, const TQFontMetrics &fm) /*KDE_NO_EXPORT*/; friend class CaretBoxIterator; }; typedef MassDeleter CaretBoxLineDeleter; inline CaretBox *CaretBoxIterator::data() const { return cbl->caret_boxes[index]; } /** * Iterates through the lines of a document. * * The line iterator becomes invalid when the associated LinearDocument object * is destroyed. * @since 3.2 * @internal * @author Leo Savernik */ class LineIterator { protected: LinearDocument *lines; // associated document CaretBoxLine *cbl; // current caret box line static CaretBoxIterator currentBox; // current inline box static long currentOffset; // Note: cbl == 0 indicates a position beyond the beginning or the // end of a document. /** Default constructor, only for internal use */ LineIterator() {} /** Initializes a new iterator. * * Note: This constructor neither cares about the correctness of @p node * nor about @p offset. It is the responsibility of the caller to ensure * that both point to valid places. */ LineIterator(LinearDocument *l, DOM::NodeImpl *node, long offset); public: /** dereferences current caret box line. * * @returns the caret line box or 0 if end of document */ CaretBoxLine *operator *() const { return cbl; } /** returns the associated linear document */ LinearDocument *linearDocument() const { return lines; } /** seek next line * * Guaranteed to crash if beyond beginning/end of document. */ LineIterator &operator ++() { advance(false); return *this; } /** seek previous line. * * Guaranteed to crash if beyond beginning/end of document. */ LineIterator &operator --() { advance(true); return *this; } /** compares two iterators. The comparator actually works only for * comparing arbitrary iterators to begin() and end(). */ bool operator ==(const LineIterator &it) const { return lines == it.lines && cbl == it.cbl; } /** compares two iterators */ bool operator !=(const LineIterator &it) const { return !operator ==(it); } /** Returns whether this line represents the outside end of the containing * block. * * This result can only be relied on when isOutside is true. */ bool isOutsideEnd() { return cbl->isOutsideEnd(); } /** Tells whether the offset is meant to be outside or inside the * containing block. */ bool isOutside() const { return cbl->isOutside(); } /** advances to the line to come. * @param toBegin true, move to previous line, false, move to next line. */ void advance(bool toBegin); /** Whenever a new line iterator is created, it gets a caret box created. * For memory reasons, it's saved in a static instance, * thus making this function not thread-safe. * * This value can only be trusted immediately after having instantiated * a line iterator or one of its derivatives. * @return an iterator onto the corresponing caret box within the * line represented by the last instantiation of a line iterator, * or 0 if there was none. */ static CaretBoxIterator ¤tCaretBox() { return currentBox; } /** Whenever a new line iterator is created, it calculates a modified offset * that is to be used with respect to the current render object. * This offset can be queried with this function. * * This value can only be trusted immediately after having instantiated * a line iterator or one of its derivatives. * @return the modified offset. */ static long currentModifiedOffset() { return currentOffset; } protected: /** seeks next block. */ void nextBlock(); /** seeks previous block. */ void prevBlock(); friend class CaretBoxIterator; friend class EditableLineIterator; friend class EditableCaretBoxIterator; friend class EditableCharacterIterator; friend class LinearDocument; }; /** * Represents the whole document in terms of lines. * * SGML documents are trees. But for navigation, this representation is * not practical. Therefore this class serves as a helper to represent the * document as a linear list of lines. Its usage somewhat resembles STL * semantics like begin and end as well as iterators. * * The lines itself are represented as caret line boxes. * * LinearDocument instances are not meant to be kept over the lifetime of their * associated document, but constructed from (node, offset) pairs whenever line * traversal is needed. This is because the underlying InlineFlowBox objects * may be destroyed and recreated (e. g. by resizing the window, adding/removing * elements). * * @author Leo Savernik * @since 3.2 * @internal */ class LinearDocument { public: typedef LineIterator Iterator; /** * Creates a new instance, and initializes it to the line specified by * the parameters below. * * Creation will fail if @p node is invisible or defect. * @param part part within which everything is taking place. * @param node document node with which to start * @param offset zero-based offset within this node. * @param advancePolicy caret advance policy * @param baseElem base element which the caret must not advance beyond * (0 means whole document). The base element will be ignored if it * cannot serve as a base (to see if this is the case, check whether * LinearDocument::baseFlow()->element() != base) */ LinearDocument(TDEHTMLPart *part, DOM::NodeImpl *node, long offset, CaretAdvancePolicy advancePolicy, DOM::ElementImpl *baseElem); virtual ~LinearDocument(); /** * Tells whether this list contains any lines. * * @returns @p true if this document contains lines, @p false otherwise. Note * that an empty document contains at least one line, so this method * only returns @p false if the document could not be initialised for * some reason. */ bool isValid() const // FIXME: not yet impl'd { return true; } /** * Returns the count of lines. * * Warning: This function is expensive. Call it once and cache the value. * * FIXME: It's not implemented yet (and maybe never will) */ int count() const; /** * Returns a line iterator containing the current position as its starting * value. */ Iterator current(); /** * Returns a line iterator pointing right after the end of the document. */ const Iterator &end() const { return _end; } /** * Returns a line iterator pointing to the very last line of the document. */ Iterator preEnd(); /** * Returns a line iterator pointing to the very first line of the document. */ Iterator begin(); /** * Returns a line iterator pointing just before the very first line of the * document (this is somewhat an emulation of reverse iterators). */ const Iterator &preBegin() const { return _preBegin; } /** * Returns the current caret advance policy */ CaretAdvancePolicy advancePolicy() const { return advPol; } /** * Returns the base render object which the caret must not advance beyond. * * Note that HTML documents are usually restricted to the body element. * * @return the base render object or 0 if the whole document is valid. */ RenderObject *baseObject() const { return base; } protected: void initPreBeginIterator(); void initEndIterator(); protected: CaretBoxLineDeleter cblDeleter; // mass deleter for caret box lines DOM::NodeImpl *node; long offset; Iterator _preBegin; Iterator _end; TDEHTMLPart *m_part; CaretAdvancePolicy advPol; RenderObject *base; friend class LineIterator; friend class EditableLineIterator; friend class ErgonomicEditableLineIterator; friend class CaretBoxIterator; friend class EditableCaretBoxIterator; friend class EditableCharacterIterator; }; /** * Iterates over the editable inner elements of a caret line box. * * The incrementor will traverse all caret boxes according to the associated * linear document's caret advance policy. In contrast to \c CaretBoxIterator * this iterator only regards caret boxes which are editable. * * @author Leo Savernik * @internal * @since 3.3 */ class EditableCaretBoxIterator : public CaretBoxIterator { TDEHTMLPart *m_part; bool adjacent; CaretAdvancePolicy advpol; // caret advance policy public: /** initializes a new iterator from the given line iterator, * beginning with the given caret box iterator, if specified */ EditableCaretBoxIterator(LineIterator &lit, bool fromEnd = false, CaretBoxIterator *it = 0) : CaretBoxIterator(it ? *it : (fromEnd ? (*lit)->end() : (*lit)->preBegin())), m_part(lit.lines->m_part), adjacent(false), advpol(lit.lines->advancePolicy()) { if (!it) { if (fromEnd) --*this; else ++*this; } } /** empty constructor. Use only to copy another iterator into this one. */ EditableCaretBoxIterator() {} /** returns @p true when the current caret box is adjacent to the * previously iterated caret box, i. e. no intervening caret boxes. */ bool isAdjacent() const { return adjacent; } /** increments the iterator to point to the next editable caret box. */ EditableCaretBoxIterator &operator ++() { advance(false); return *this; } /** decrements the iterator to point to the previous editable caret box. */ EditableCaretBoxIterator &operator --() { advance(true); return *this; } /** advances to the editable caret box to come * @param toBegin true, move towards beginning, false, move towards end. */ void advance(bool toBegin); protected: /** finds out if the given box is editable. * @param boxit iterator to given caret box * @param fromEnd true when advancing towards the beginning * @return @p true if box is editable */ bool isEditable(const CaretBoxIterator &boxit, bool fromEnd); }; /** * Iterates through the editable lines of a document. * * This iterator, opposing to @p LineIterator, only regards editable lines. * Additionally, this iterator enforces the caret advance policy. * * The iterator can be compared to normal LineIterators, especially to * @ref LinearDocument::preBegin and @ref LinearDocument::end * * The line iterator becomes invalid when the associated LinearDocument object * is destroyed. * @since 3.2 * @internal * @author Leo Savernik */ class EditableLineIterator : public LineIterator { public: /** Initializes a new iterator. * * The iterator is set to the first following editable line or to the * end if no editable line follows. * @param it a line iterator to initialize this from * @param fromEnd @p true, traverse towards the beginning in search of an * editable line */ EditableLineIterator(const LineIterator &it, bool fromEnd = false) : LineIterator(it) { if (!cbl) return; if (!isEditable(*this)) advance(fromEnd); } /** empty constructor. * * Only use if you want to copy another iterator onto it later. */ EditableLineIterator() {} /** seek next line * * Guaranteed to crash if beyond beginning/end of document. */ EditableLineIterator &operator ++() { advance(false); return *this; } /** seek previous line. * * Guaranteed to crash if beyond beginning/end of document. */ EditableLineIterator &operator --() { advance(true); return *this; } /** advances to the line to come. * @param toBegin true, move to previous line, false, move to next line. */ void advance(bool toBegin); protected: /** finds out if the current line is editable. * * @param it check caret box line iterator points to * @return @p true if line is editable */ bool isEditable(LineIterator &it) { EditableCaretBoxIterator fbit = it; return fbit != (*it)->end(); } }; /** Represents a render table as a linear list of rows. * * This iterator abstracts from table sections and treats tables as a linear * representation of all rows they contain. * @author Leo Savernik * @internal * @since 3.2 */ class TableRowIterator { protected: TableSectionIterator sec; // current section int index; // index of row within section public: /** Constructs a new iterator. * @param table table to iterate through. * @param fromEnd @p true to iterate towards the beginning * @param row pointer to row to start with, 0 starts at the first/last * row. */ TableRowIterator(RenderTable *table, bool fromEnd = false, RenderTableSection::RowStruct *row = 0); /** Constructs a new iterator. * @param section table section to begin with * @param index index within table section */ TableRowIterator(RenderTableSection *section, int index) : sec(section), index(index) {} /** empty constructor. This must be assigned another iterator before it is * useable. */ TableRowIterator() {} /** returns the current table row. * @return the row or 0 if the end of the table has been reached. */ RenderTableSection::RowStruct *operator *() { if (!*sec) return 0; return &(*sec)->grid[index]; } /** advances to the next row */ TableRowIterator &operator ++(); /** advances to the previous row */ TableRowIterator &operator --(); protected: }; /** Iterates through the editable lines of a document, in a topological order. * * The differences between this and the EditableLineIterator lies in the way * lines are inquired. While the latter steps through the lines in document * order, the former takes into consideration ergonomics. * * This is especially useful for tables. EditableLineIterator traverses all * table cells from left to right, top to bottom, while this one will * actually snap to the cell in the right position, and traverse only * upwards/downwards, thus providing a more intuitive navigation. * * @author Leo Savernik * @internal * @since 3.2 */ class ErgonomicEditableLineIterator : public EditableLineIterator { protected: int xCoor; // x-coordinate to determine cell position public: /** Initializes a new ergonomic editable line iterator from the given one. * @param it line iterator * @param x absolute x-coordinate for cell determination */ ErgonomicEditableLineIterator(const LineIterator &it, int x) : EditableLineIterator(it), xCoor(x) {} /** Constructs an uninitialized iterator which must be assigned a line iterator before * it can be used. */ ErgonomicEditableLineIterator() {} /** seek next line. * * The next line will be one that is visually situated below this line. */ ErgonomicEditableLineIterator &operator ++(); /** seek previous line. * * The previous line will be one that is visually situated above this line. */ ErgonomicEditableLineIterator &operator --(); protected: /** determines the topologically next render object. * @param oldCell table cell the original object was under. * @param newObject object to determine whether and which transition * between cells is to be handled. It does not have to be an object in the correct * topological cell, a simple delivery from an editable line iterator suffices. * @param toBegin if @p true, iterate towards the beginning */ void determineTopologicalElement(RenderTableCell *oldCell, RenderObject *newObject, bool toBegin); /** initializes the iterator to point to the first previous/following editable * line. * @param newBlock take this as base block. * @param toBegin @p true, iterate towards beginning. */ void calcAndStoreNewLine(RenderBlock *newBlock, bool toBegin); }; /** * Provides iterating through the document in terms of characters. Only the * editable characters are regarded. * * This iterator represents the document, which is structured as a tree itself, * as a linear stream of characters. */ class EditableCharacterIterator { protected: EditableLineIterator _it; EditableCaretBoxIterator ebit; long _offset; // offset within current caret box. int _char; bool _end:1; // true when end of document has been reached public: /** empty constructor. * * Only use if you want to assign another iterator as no fields will * be initialized. */ EditableCharacterIterator() {} /** constructs a new iterator from the given linear document. * * @param ld linear representation of document. */ EditableCharacterIterator(LinearDocument *ld) : _it(ld->current()), ebit(_it, false, &_it.currentCaretBox()), _offset(_it.currentModifiedOffset()), _char(-1), _end(false) { // ### temporary fix for illegal nodes if (_it == ld->end()) { _end = true; return; } initFirstChar(); } /** returns the current character, or -1 if not on a text node, or beyond * the end. */ int chr() const { return _char; } /** returns the current character as a unicode symbol, substituting * a blank for a non-text node. */ TQChar operator *() const { return TQChar(_char >= 0 ? _char : ' '); } /** returns true when the end of the document has been reached. */ bool isEnd() const { return _end; } /** returns the current offset */ long offset() const { return _offset; } /** returns the current render object. */ RenderObject *renderer() const { return (*ebit)->object(); } /** returns the current caret box. * * Will crash if beyond end. */ CaretBox *caretBox() const { return *ebit; } /** returns the current inline box. * * May be 0 if the current element has none, or if the end has been reached. * Therefore, do *not* use this to test for the end condition, use node() * instead. */ InlineBox *inlineBox() const { return (*ebit)->inlineBox(); } /** returns whether the current line box represents the outside of its * render object. */ // bool boxIsOutside() const { return _it.isOutside(); } /** moves to the next editable character. */ EditableCharacterIterator &operator ++(); /** moves to the previous editable character. */ EditableCharacterIterator &operator --(); protected: /** initializes the _char member by reading the character at the current * offset, peeking ahead as necessary. */ void initFirstChar(); /** reads ahead the next node and updates the data structures accordingly */ void peekNext() { EditableCaretBoxIterator copy = ebit; ++copy; if (copy == (*_it)->end()) { _char = -1; return; } CaretBox *box = *copy; InlineBox *b = box->inlineBox(); if (b && !box->isOutside() && b->isInlineTextBox()) _char = static_cast(b->object())->str->s[b->minOffset()].unicode(); else _char = -1; } /** reads ahead the previous node and updates the data structures accordingly */ void peekPrev() { --ebit; } }; }/*namespace tdehtml*/ #endif