khtml Library API Documentation

khtml_caret_p.h

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 #ifndef KHTML_CARET_P_H
00022 #define KHTML_CARET_P_H
00023 
00024 #include "rendering/render_table.h"
00025 
00026 #define DEBUG_CARETMODE 0
00027 
00028 namespace khtml {
00029 
00033 struct CaretViewContext {
00034     int freqTimerId;        // caret blink frequency timer id
00035     int x, y;           // caret position in viewport coordinates
00036                     // (y specifies the top, not the baseline)
00037     int width;          // width of caret in pixels
00038     int height;         // height of caret in pixels
00039     bool visible;       // true if currently visible.
00040     bool displayed;     // true if caret is to be displayed at all.
00041     bool caretMoved;        // set to true once caret has been moved in page
00042                     // how to display the caret when view is not focused
00043     KHTMLPart::CaretDisplayPolicy displayNonFocused;
00044 
00052     int origX;
00053 
00054     bool keyReleasePending; // true if keypress under caret mode awaits
00055                     // corresponding release event
00056     CaretViewContext() : freqTimerId(-1), x(0), y(0), width(1), height(16),
00057         visible(true), displayed(false), caretMoved(false),
00058     displayNonFocused(KHTMLPart::CaretInvisible), origX(0),
00059     keyReleasePending(false)
00060     {}
00061 };
00062 
00066 struct EditorContext {
00067     bool override;      // true if typed characters should override
00068                     // the existing ones.
00069 
00070     EditorContext() : override(false)
00071     {}
00072 };
00073 
00074 class LinearDocument;
00075 
00085 class LineIterator
00086 {
00087 protected:
00088   LinearDocument *lines;    // associated document
00089   RenderFlow *cb;       // containing block
00090   InlineFlowBox *flowBox;   // the line itself
00091 
00092   static InlineBox *currentBox; // current inline box
00093 
00094   // Note: cb == 0 indicates a position beyond the beginning or the
00095   // end of a document.
00096 
00099   LineIterator() {}
00100 
00107   LineIterator(LinearDocument *l, DOM::NodeImpl *node, long offset);
00108 
00109 public:
00117   InlineFlowBox *operator *() const { return flowBox; }
00118 
00123   LineIterator &operator ++();
00131   LineIterator operator ++(int);
00132 
00137   LineIterator &operator --();
00145   LineIterator operator --(int);
00146 
00152   LineIterator operator +(int summand) const;
00158   LineIterator operator -(int summand) const;
00159 
00165   LineIterator &operator +=(int summand);
00171   LineIterator &operator -=(int summand);
00172 
00176   bool operator ==(const LineIterator &it) const
00177   {
00178     return lines == it.lines
00179             && flowBox == it.flowBox && cb == it.cb;
00180   }
00181 
00184   bool operator !=(const LineIterator &it) const
00185   {
00186     return !operator ==(it);
00187   }
00188 
00198   static InlineBox *currentInlineBox() { return currentBox; }
00199 
00200 protected:
00203   void nextBlock();
00206   void prevBlock();
00207 
00208   friend class InlineBoxIterator;
00209   friend class EditableInlineBoxIterator;
00210   friend class LinearDocument;
00211 };
00212 
00213 
00234 class LinearDocument {
00235 public:
00236   typedef LineIterator Iterator;
00237 
00247   LinearDocument(KHTMLPart *part, DOM::NodeImpl *node, long offset);
00248 
00249   virtual ~LinearDocument();
00250 
00259   bool isValid() const      // FIXME: not yet impl'd
00260   {
00261     return true;
00262   }
00263 
00271   int count() const;
00272 
00277   Iterator current();
00278 
00282   const Iterator &end() const { return _end; }
00283 
00287   Iterator preEnd();
00288 
00292   Iterator begin();
00293 
00298   const Iterator &preBegin() const { return _preBegin; }
00299 
00300 protected:
00301   void initPreBeginIterator();
00302   void initEndIterator();
00303 
00304 protected:
00305   RenderArena *arena;       // We need an arena for intermediate render
00306                 // objects that have no own inline box
00307   DOM::NodeImpl *node;
00308   long offset;
00309 
00310   Iterator _preBegin;
00311   Iterator _end;
00312 
00313   KHTMLPart *m_part;
00314 
00315   friend class LineIterator;
00316   friend class EditableLineIterator;
00317   friend class ErgonomicEditableLineIterator;
00318   friend class InlineBoxIterator;
00319   friend class EditableInlineBoxIterator;
00320   friend class EditableCharacterIterator;
00321 };
00322 
00323 
00334 class InlineBoxIterator {
00335 protected:
00336     RenderArena *arena; // arena for allocating transient inline boxes
00337     InlineBox *box; // currently traversed inline box
00338 
00339 public:
00342     InlineBoxIterator(RenderArena *arena, InlineFlowBox *flowBox, bool fromEnd = false);
00343 
00347     InlineBoxIterator(LineIterator &lit, bool fromEnd = false,
00348                       InlineBox *initBox = 0);
00349 
00352   InlineBoxIterator() {}
00353 
00358     InlineBox *operator *() const { return box; }
00359 
00362     InlineBoxIterator &operator ++();
00363 
00367     InlineBoxIterator &operator --();
00368 };
00369 
00381 class EditableInlineBoxIterator : public InlineBoxIterator {
00382 protected:
00383   KHTMLPart *m_part;
00384   bool adjacent;
00385 
00386 public:
00393   EditableInlineBoxIterator(KHTMLPart *part, RenderArena *arena,
00394         InlineFlowBox *flowBox, bool fromEnd = false)
00395     : InlineBoxIterator(arena, flowBox, fromEnd), m_part(part), adjacent(true)
00396   {
00397     if (box && !isEditable(box)) fromEnd ? --*this : ++*this;
00398   }
00399 
00403   EditableInlineBoxIterator(LineIterator &lit, bool fromEnd = false,
00404         InlineBox *initBox = 0)
00405         : InlineBoxIterator(lit, fromEnd, initBox), m_part(lit.lines->m_part)
00406   {
00407     if (box && !isEditable(box)) fromEnd ? --*this : ++*this;
00408   }
00409 
00412   EditableInlineBoxIterator() {}
00413 
00417   bool isAdjacent() const { return adjacent; }
00418 
00422   EditableInlineBoxIterator &operator ++()
00423   {
00424     adjacent = true;
00425     do {
00426       InlineBoxIterator::operator ++();
00427     } while (box && !isEditable(box));
00428     return *this;
00429   }
00430 
00434   EditableInlineBoxIterator &operator --()
00435   {
00436     adjacent = true;
00437     do {
00438       InlineBoxIterator::operator --();
00439     } while (box && !isEditable(box));
00440     return *this;
00441   }
00442 
00443 protected:
00448   bool isEditable(InlineBox *b)
00449   {
00450     //if (m_part->isCaretMode() || m_part->isEditable()) return true;
00451 
00452     Q_ASSERT(b);
00453     RenderObject *r = b->object();
00454 #if DEBUG_CARETMODE > 0
00455     if (b->isInlineFlowBox()) kdDebug(6200) << "b is inline flow box" << endl;
00456     kdDebug(6200) << "isEditable r" << r << ": " << (r ? r->renderName() : QString::null) << (r && r->isText() ? " contains \"" + QString(((RenderText *)r)->str->s, QMIN(((RenderText *)r)->str->l,15)) + "\"" : QString::null) << endl;
00457 #endif
00458     // Must check caret mode or design mode *after* r && r->element(), otherwise
00459     // lines without a backing DOM node get regarded, leading to a crash.
00460     // ### check should actually be in InlineBoxIterator
00461     bool result = r && r->element() && !r->isTableCol()
00462         && (m_part->isCaretMode() || m_part->isEditable()
00463             || r->style()->userInput() == UI_ENABLED);
00464     if (!result) adjacent = false;
00465 #if DEBUG_CARETMODE > 0
00466     kdDebug(6200) << result << endl;
00467 #endif
00468     return result;
00469   }
00470 
00471 };
00472 
00489 class EditableLineIterator : public LineIterator {
00490 public:
00499   EditableLineIterator(const LineIterator &it, bool fromEnd = false)
00500         : LineIterator(it)
00501   {
00502     if (flowBox && !isEditable(*this)) fromEnd ? operator--() : operator ++();
00503     if (!flowBox || !cb) {
00504 #if DEBUG_CARETMODE > 0
00505       kdDebug(6200) << "EditableLineIterator: findFlowBox failed" << endl;
00506 #endif
00507       cb = 0;
00508     }/*end if*/
00509   }
00510 
00515   EditableLineIterator() {}
00516 
00521   EditableLineIterator &operator ++()
00522   {
00523     // FIXME: MEGA-FLAW! editable empty inlines elements not
00524     // represented by an inline box aren't considered any more.
00525     do {
00526       LineIterator::operator ++();
00527     } while (cb && !isEditable(*this));
00528     return *this;
00529   }
00537   //EditableLineIterator operator ++(int);
00538 
00543   EditableLineIterator &operator --()
00544   {
00545     // FIXME: MEGA-FLAW! editable empty inlines not
00546     // represented by an inline box aren't considered any more.
00547     do {
00548       LineIterator::operator --();
00549     } while (cb && !isEditable(*this));
00550     return *this;
00551   }
00559   //EditableLineIterator operator --(int);
00560 
00561 #if 0   // implement when it's needed
00562 
00565   EditableLineIterator operator +(int summand) const;
00569   EditableLineIterator operator -(int summand) const;
00570 
00574   EditableLineIterator &operator +=(int summand);
00578   EditableLineIterator &operator -=(int summand);
00579 #endif
00580 
00581 protected:
00586   bool isEditable(LineIterator &it)
00587   {
00588 #if 0       // these shortcut evaluations are all invalid
00589     if (lines->m_part->isCaretMode() || lines->m_part->isEditable()) return true;
00590 
00591     // on dummy lines check the containing block itself for editability
00592     if (!(*it)->firstChild()) {
00593       kdDebug(6200) << "cb " << cb->renderName() << "[" << cb << "](" << (cb->element() ? cb->element()->nodeName().string() : QString::null) << ") editable? " << (cb->style()->userInput() == UI_ENABLED) << endl;
00594       return cb->style()->userInput() == UI_ENABLED;
00595     }/*end if*/
00596 #endif
00597 
00598     EditableInlineBoxIterator fbit = it;
00599     return *fbit;
00600   }
00601 
00602 };
00603 
00612 class TableRowIterator {
00613 protected:
00614   TableSectionIterator sec; // current section
00615   int index;            // index of row within section
00616 public:
00623   TableRowIterator(RenderTable *table, bool fromEnd = false,
00624         RenderTableSection::RowStruct *row = 0);
00625 
00630   TableRowIterator(RenderTableSection *section, int index)
00631     : sec(section), index(index)
00632   {}
00633 
00637   TableRowIterator() {}
00638 
00642   RenderTableSection::RowStruct *operator *()
00643   {
00644     if (!*sec) return 0;
00645     return &(*sec)->grid[index];
00646   }
00647 
00650   TableRowIterator &operator ++();
00651 
00654   TableRowIterator &operator --();
00655 
00656 protected:
00657 };
00658 
00674 class ErgonomicEditableLineIterator : public EditableLineIterator {
00675 protected:
00676   int xCoor;        // x-coordinate to determine cell position with
00677 public:
00682   ErgonomicEditableLineIterator(const LineIterator &it, int x)
00683     : EditableLineIterator(it), xCoor(x) {}
00684 
00688   ErgonomicEditableLineIterator() {}
00689 
00694   ErgonomicEditableLineIterator &operator ++();
00695 
00700   ErgonomicEditableLineIterator &operator --();
00701 
00702 protected:
00710   void determineTopologicalElement(RenderTableCell *oldCell,
00711         RenderObject *newObject, bool toBegin);
00712 
00718   void calcAndStoreNewLine(RenderFlow *newBlock, bool toBegin);
00719 
00720 #if 0
00721 
00725   static bool belongToSameTable(const RenderTableCell *t1, const RenderTableCell *t2)
00726   {
00727     return t1 && t2 && t1->table() == t2->table();
00728   }
00729 
00743   static RenderTableCell *findNearestTableCellInSection(KHTMLPart *part, int x,
00744     RenderTableSection *section, bool fromEnd = false, int startIndex = -1);
00745 
00756   RenderObject *findObjectBeyond(RenderTable *table, bool toBegin);
00757 #endif
00758 };
00759 
00767 class EditableCharacterIterator {
00768 protected:
00769   LinearDocument *ld;
00770   EditableLineIterator _it;
00771   EditableInlineBoxIterator ebit;
00772   DOM::NodeImpl *_node;
00773   long _offset;
00774   int _char;
00775 
00776 public:
00777 
00783   EditableCharacterIterator() {}
00784 
00789   EditableCharacterIterator(LinearDocument *ld)
00790         : ld(ld), _it(ld->current()),
00791                 ebit(_it, false, _it.currentInlineBox()), _char(-1)
00792   {
00793     _node = ld->node;
00794     _offset = ld->offset;
00795 
00796     // ### temporary fix for illegal nodes
00797     if (_it == ld->end()) { _node = 0; return; }
00798 
00799     // seeks the node's inline box
00800     // ### redundant, implement means to get it from ld or _it
00801     // ### if node is not there?
00802     EditableInlineBoxIterator copy = ebit;
00803     for (; *ebit; ++ebit) {
00804       copy = ebit;
00805       InlineBox *b = *ebit;
00806 
00807       if (b == _it.currentInlineBox() || b->object() == _node->renderer()) {
00808         _offset = QMIN(kMax(_offset, b->minOffset()), b->maxOffset());
00809         break;
00810       }/*end if*/
00811     }/*next ebit*/
00812     // If no node is found, we take the last editable node. This is a very
00813     // feeble approximation as it sometimes makes the caret get stuck, or
00814     // iterate over the same element indefinitely,
00815     // but this covers up a case that should never happen in theory.
00816     if (!*ebit) {
00817       // this is a really bad hack but solves the caret-gets-stuck issue
00818       static long cache_offset = -1;
00819       ebit = copy;
00820       InlineBox *b = *ebit;
00821       _node = b->object()->element();
00822       long max_ofs = b->maxOffset();
00823       _offset = cache_offset == max_ofs ? b->minOffset() : max_ofs;
00824       cache_offset = _offset;
00825 #if DEBUG_CARETMODE > 0
00826       kdDebug(6200) << "There was no node! Fixup applied!" << endl;
00827       if (cache_offset == max_ofs) kdDebug(6200) << "offset fixup applied as well" << endl;
00828 #endif
00829     }/*end if*/
00830 
00831     initFirstChar();
00832   }
00833 
00837   int chr() const { return _char; }
00838 
00842   QChar operator *() const { return QChar(_char >= 0 ? _char : ' '); }
00843 
00846   long offset() const { return _offset; }
00851   DOM::NodeImpl *node() const { return _node; }
00858   InlineBox *box() const { return *ebit; }
00861   EditableCharacterIterator &operator ++();
00862 
00865   EditableCharacterIterator &operator --();
00866 
00867 protected:
00871   void initFirstChar();
00874   void peekNext()
00875   {
00876     EditableInlineBoxIterator copy = ebit;
00877     ++copy;
00878     InlineBox *b = *copy;
00879     if (b && b->isInlineTextBox())
00880       _char = static_cast<RenderText *>(b->object())->str->s[b->minOffset()].unicode();
00881     else
00882       _char = -1;
00883   }
00886   void peekPrev()
00887   {
00888     --ebit;
00889 //    _peekPrev = *ebit;
00890   }
00891 
00892 };
00893 
00894 
00895 }
00896 
00897 
00898 #endif
KDE Logo
This file is part of the documentation for khtml Library Version 3.2.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Feb 4 12:37:17 2004 by doxygen 1.2.18 written by Dimitri van Heesch, © 1997-2003