Warning: That file was not part of the compilation database. It may have many parsing errors.
1 | /* This file is part of the KDE project |
---|---|
2 | * |
3 | * Copyright (C) 2003-2004 Leo Savernik <l.savernik@aon.at> |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Library General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Library General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Library General Public License |
16 | * along with this library; see the file COPYING.LIB. If not, write to |
17 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
18 | * Boston, MA 02110-1301, USA. |
19 | */ |
20 | |
21 | #ifndef KHTML_CARET_P_H |
22 | #define KHTML_CARET_P_H |
23 | |
24 | #include "rendering/render_table.h" |
25 | |
26 | |
27 | #define DEBUG_CARETMODE 0 |
28 | |
29 | class QFontMetrics; |
30 | |
31 | namespace DOM { |
32 | class NodeImpl; |
33 | class ElementImpl; |
34 | } |
35 | |
36 | namespace khtml { |
37 | |
38 | /** caret advance policy. |
39 | * |
40 | * Used to determine which elements are taken into account when the caret is |
41 | * advanced. Later policies pose refinements of all former |
42 | * policies. |
43 | * @param LeafsOnly advance from leave render object to leaf render object |
44 | * (It will allow outside flow positions if a flow wouldn't be reachable |
45 | * otherwise). |
46 | * @param IndicatedFlows place caret also at the beginning/end of flows |
47 | * that have at least one visible border at any side. |
48 | * (It will allow not indicated flow positions if a flow wouldn't |
49 | * be reachable otherwise). |
50 | * @param VisibleFlows place caret also at the beginning/end of any flow |
51 | * that has a renderer. |
52 | */ |
53 | enum CaretAdvancePolicy { |
54 | LeafsOnly, IndicatedFlows, VisibleFlows |
55 | }; |
56 | |
57 | /** contextual information about the caret which is related to the view. |
58 | * An object of this class is only instantiated when it is needed. |
59 | */ |
60 | struct CaretViewContext { |
61 | int freqTimerId; // caret blink frequency timer id |
62 | int x, y; // caret position in viewport coordinates |
63 | // (y specifies the top, not the baseline) |
64 | int width; // width of caret in pixels |
65 | int height; // height of caret in pixels |
66 | bool visible; // true if currently visible. |
67 | bool displayed; // true if caret is to be displayed at all. |
68 | bool caretMoved; // set to true once caret has been moved in page |
69 | // how to display the caret when view is not focused |
70 | KHTMLPart::CaretDisplayPolicy displayNonFocused; |
71 | |
72 | /** For natural traversal of lines, the original x position is saved, and |
73 | * the actual x is set to the first character whose x position is |
74 | * greater than origX. |
75 | * |
76 | * origX is reset to x whenever the caret is moved horizontally or placed |
77 | * by the mouse. |
78 | */ |
79 | int origX; |
80 | |
81 | bool keyReleasePending; // true if keypress under caret mode awaits |
82 | // corresponding release event |
83 | CaretViewContext() : freqTimerId(-1), x(0), y(0), width(1), height(16), |
84 | visible(true), displayed(false), caretMoved(false), |
85 | displayNonFocused(KHTMLPart::CaretInvisible), origX(0), |
86 | keyReleasePending(false) |
87 | {} |
88 | }; |
89 | |
90 | class LinearDocument; |
91 | |
92 | /** |
93 | * Stores objects of a certain type, and calls delete on each of them |
94 | * when this data structure is destroyed. |
95 | * |
96 | * As this structure merely consists of a vector of pointers, all objects |
97 | * allocated can be traversed as seen fit. |
98 | * |
99 | * @author Leo Savernik |
100 | * @internal |
101 | */ |
102 | template<class T> class MassDeleter : public QVector<T *> { |
103 | public: |
104 | MassDeleter(size_t reserved = 1) { this->reserve(reserved); } |
105 | ~MassDeleter() |
106 | { |
107 | typename QVector<T *>::Iterator nd = this->end(); |
108 | for (typename QVector<T *>::Iterator it = this->begin(); it != nd; ++it) |
109 | delete *it; |
110 | } |
111 | }; |
112 | |
113 | class CaretBoxLine; |
114 | |
115 | /** |
116 | * Represents a rectangular box within which the caret is located. |
117 | * |
118 | * The caret box serves as a wrapper for inline boxes of all kind. It either |
119 | * wraps an InlineBox, InlineTextBox, or InlineFlowBox, or if no such boxes |
120 | * exist for a certain context, it contains the relevant information directly. |
121 | * |
122 | * This class will be constructed whenever a caret position has to be described. |
123 | * @author Leo Savernik |
124 | * @internal |
125 | */ |
126 | class CaretBox { |
127 | protected: |
128 | InlineBox *_box; // associated inline box if available. |
129 | short _w; // width of box in pixels |
130 | int _h; // height of box in pixels |
131 | int _x; // x coordinate relative to containing block |
132 | int _y; // y coordinate relative to containing block |
133 | RenderBox *cb; // containing block |
134 | bool _outside:1; // true when representing the outside of the element |
135 | bool outside_end:1; // at ending outside of element rather than at beginning |
136 | // 29 bits unused |
137 | |
138 | public: |
139 | /** empty constructor for later assignment */ |
140 | CaretBox() {} |
141 | /** initializes the caret box from the given inline box */ |
142 | CaretBox(InlineBox *ibox, bool outside, bool outsideEnd) : _box(ibox), |
143 | _w((short)ibox->width()), _h(ibox->height()), _x(ibox->xPos()), |
144 | _y(ibox->yPos()), cb(0), _outside(outside), outside_end(outsideEnd) |
145 | { |
146 | RenderObject *r = ibox->object(); |
147 | if (r) cb = r->containingBlock(); |
148 | } |
149 | /** initializes the caret box from scratch */ |
150 | CaretBox(int x, int y, int w, int h, RenderBox *cb, bool outside, bool outsideEnd) : |
151 | _box(0), _w((short)w), _h(h), _x(x), _y(y), cb(cb), _outside(outside), |
152 | outside_end(outsideEnd) |
153 | {} |
154 | |
155 | int width() const { return _w; } |
156 | int height() const { return _h; } |
157 | int xPos() const { return _x; } |
158 | int yPos() const { return _y; } |
159 | RenderBox *enclosingObject() const { return cb; } |
160 | InlineBox *inlineBox() const { return _box; } |
161 | |
162 | /** returns the containing block of this caret box. If the caret box |
163 | * resembles a block itself, its containing block is returned. |
164 | */ |
165 | RenderBlock *containingBlock() const { return _box ? static_cast<RenderBlock *>(cb) : cb->containingBlock(); } |
166 | |
167 | /** returns the replaced render object if this caret box represents one, |
168 | * 0 otherwise. |
169 | */ |
170 | |
171 | |
172 | /** returns true if this caret box represents an inline element, or text box, |
173 | * otherwise false. |
174 | */ |
175 | bool isInline() const { return _box; } |
176 | /** returns true if this caret box represents an inline text box. |
177 | */ |
178 | bool isInlineTextBox() const { return _box && _box->isInlineTextBox(); } |
179 | /** returns true if this caret box represents a line break |
180 | */ |
181 | bool isLineBreak() const |
182 | { |
183 | return _box && _box->object() && _box->object()->isBR(); |
184 | } |
185 | /** returns true when this caret box represents an ouside position of an |
186 | * element. |
187 | */ |
188 | bool isOutside() const { return _outside; } |
189 | /** returns the position at which the outside is targeted at. |
190 | * |
191 | * This method's return value is meaningless if isOutside() is not true. |
192 | * @return true if the outside end is meant, false if the outside beginning |
193 | * is meant. |
194 | */ |
195 | bool isOutsideEnd() const { return outside_end; } |
196 | /** returns the associated render object. */ |
197 | RenderObject *object() const { return _box ? _box->object() : cb; } |
198 | |
199 | /** returns the minimum offset for this caret box. |
200 | */ |
201 | long minOffset() const { return _box && !isLineBreak() ? _box->minOffset() : 0; } |
202 | /** returns the maximum offset for this caret box. |
203 | */ |
204 | long maxOffset() const { return _box && !isLineBreak() ? _box->maxOffset() : 0; } |
205 | |
206 | #if DEBUG_CARETMODE > 0 |
207 | void dump(QTextStream &ts, const QString &ind) const; |
208 | #endif |
209 | |
210 | friend class CaretBoxLine; |
211 | }; |
212 | |
213 | typedef MassDeleter<CaretBox> CaretBoxDeleter; |
214 | |
215 | /** |
216 | * Iterates over the elements of a caret box line. |
217 | * |
218 | * @author Leo Savernik |
219 | * @internal |
220 | */ |
221 | class CaretBoxIterator { |
222 | protected: |
223 | CaretBoxLine *cbl; // associated caret box line |
224 | int index; // current index |
225 | |
226 | public: |
227 | // Let standard constructor/copy constructor/destructor/assignment operator |
228 | // be defined by the compiler. They do exactly what we want. |
229 | CaretBoxIterator() |
230 | : cbl( 0 ), index( 0 ) |
231 | { |
232 | } |
233 | |
234 | bool operator ==(const CaretBoxIterator &it) const |
235 | { |
236 | return cbl == it.cbl && index == it.index; |
237 | } |
238 | |
239 | bool operator !=(const CaretBoxIterator &it) const |
240 | { |
241 | return !operator ==(it); |
242 | } |
243 | |
244 | /** returns the current caret box. |
245 | * @return current caret box |
246 | */ |
247 | CaretBox *data() const; |
248 | /** shortcut for \c data |
249 | * @return current caret box |
250 | */ |
251 | CaretBox *operator *() const { return data(); } |
252 | |
253 | /** increments the iterator to point to the next caret box. |
254 | */ |
255 | CaretBoxIterator &operator ++() { index++; return *this; } |
256 | /** decrements the iterator to point to the previous caret box. |
257 | */ |
258 | CaretBoxIterator &operator --() { index--; return *this; } |
259 | |
260 | friend class CaretBoxLine; |
261 | friend class EditableCaretBoxIterator; |
262 | }; |
263 | |
264 | /** |
265 | * Resembles a line consisting of caret boxes. |
266 | * |
267 | * To the contrary of InlineFlowBoxes which are nested as needed to map the |
268 | * DOM to the rendered representation, it is sufficient for caret navigation |
269 | * to provide a linear list of unnested caret boxes. |
270 | * |
271 | * \code |
272 | * Example: The document fragment <p>a <i><b>c</b> f</i> g</p> will be |
273 | * represented by three caret box lines which each one consists of caret boxes |
274 | * as follows: |
275 | * CaretBoxLine 1: |
276 | * CaretBox(cb=<p>, _box=0, _outside=true, outside_end=false) |
277 | * CaretBoxLine 2: |
278 | * CaretBox(cb=<p>, _box=InlineTextBox("a "), _outside=false) |
279 | * CaretBox(cb=<p>, _box=InlineFlowBox(<i>), _outside=true, outside_end=false) |
280 | * CaretBox(cb=<p>, _box=InlineFlowBox(<b>), _outside=true, outside_end=false) |
281 | * CaretBox(cb=<p>, _box=InlineTextBox("c"), _outside=false) |
282 | * CaretBox(cb=<p>, _box=InlineFlowBox(<b>), _outside=true, outside_end=true) |
283 | * CaretBox(cb=<p>, _box=InlineTextBox(" f"), _outside=false) |
284 | * CaretBox(cb=<p>, _box=InlineFlowBox(<i>), _outside=true, outside_end=true) |
285 | * CaretBox(cb=<p>, _box=InlineTextBox(" g"), _outside=true, outside_end=true) |
286 | * CaretBoxLine 3: |
287 | * CaretBox(cb=<p>, _box=0, _outside=true, outside_end=true) |
288 | * \endcode |
289 | */ |
290 | class CaretBoxLine { |
291 | protected: |
292 | CaretBoxDeleter caret_boxes; |
293 | // base flow box which caret boxes have been constructed for |
294 | InlineFlowBox *basefb; |
295 | |
296 | CaretBoxLine() : caret_boxes(8), basefb(0) {} |
297 | CaretBoxLine(InlineFlowBox *basefb) : caret_boxes(8), basefb(basefb) {} |
298 | public: |
299 | #if DEBUG_CARETMODE > 3 |
300 | ~CaretBoxLine() { kDebug(6200) << "called"; } |
301 | #endif |
302 | |
303 | CaretBoxIterator begin() |
304 | { |
305 | CaretBoxIterator it; |
306 | it.cbl = this; |
307 | it.index = 0; |
308 | return it; |
309 | } |
310 | CaretBoxIterator end() |
311 | { |
312 | CaretBoxIterator it; |
313 | it.cbl = this; |
314 | it.index = caret_boxes.size(); |
315 | return it; |
316 | } |
317 | CaretBoxIterator preBegin() |
318 | { |
319 | CaretBoxIterator it; |
320 | it.cbl = this; |
321 | it.index = -1; |
322 | return it; |
323 | } |
324 | CaretBoxIterator preEnd() |
325 | { |
326 | CaretBoxIterator it; |
327 | it.cbl = this; |
328 | it.index = caret_boxes.size() - 1; |
329 | return it; |
330 | } |
331 | |
332 | /** returns the base inline flow box which the caret boxes of this |
333 | * caret box line have been constructed from. |
334 | * |
335 | * This is generally a root line box, but may be an inline flow box when the |
336 | * base is restricted to an inline element. |
337 | */ |
338 | InlineFlowBox *baseFlowBox() const { return basefb; } |
339 | |
340 | /** returns the containing block */ |
341 | RenderBlock *containingBlock() const { return caret_boxes[0]->containingBlock(); } |
342 | /** returns the enclosing object */ |
343 | RenderBox *enclosingObject() const { return caret_boxes[0]->enclosingObject(); } |
344 | |
345 | /** returns whether this caret box line is outside. |
346 | * @return true if this caret box represents an outside position of this |
347 | * line box' containing block, false otherwise. |
348 | */ |
349 | bool isOutside() const |
350 | { |
351 | const CaretBox *cbox = caret_boxes[0]; |
352 | return !cbox->isInline() && cbox->isOutside(); |
353 | } |
354 | |
355 | /** returns whether this caret box line is at the outside end. |
356 | * |
357 | * The result cannot be relied upon unless isOutside() returns true. |
358 | */ |
359 | bool isOutsideEnd() const { return caret_boxes[0]->isOutsideEnd(); } |
360 | |
361 | /** constructs a new caret box line out of the given inline flow box |
362 | * @param deleter deleter which handles alloc+dealloc of the object |
363 | * @param baseFlowBox basic flow box which to create a caret line box from |
364 | * @param seekBox seek this box within the constructed line |
365 | * @param seekOutside denoting whether position is outside of seekBox |
366 | * @param seekOutsideEnd whether at the outside end of seekBox |
367 | * @param iter returns an iterator that corresponds to seekBox. If no suitable |
368 | * caret box exists, it will return end() |
369 | * @param seekObject seek this render object within the constructed line. |
370 | * It will only be regarded if \c seekBox is 0. \c iter will then point |
371 | * to the first caret box whose render object matches. |
372 | */ |
373 | static CaretBoxLine *constructCaretBoxLine(MassDeleter<CaretBoxLine> *deleter, |
374 | InlineFlowBox *baseFlowBox, InlineBox *seekBox, bool seekOutside, |
375 | bool seekOutsideEnd, CaretBoxIterator &iter, |
376 | RenderObject *seekObject = 0) /*KDE_NO_EXPORT*/; |
377 | |
378 | /** constructs a new caret box line for the given render block. |
379 | * @param deleter deleter which handles alloc+dealloc of the object |
380 | * @param cb render block or render replaced |
381 | * @param outside true when line is to be constructed outside |
382 | * @param outsideEnd true when the ending outside is meant |
383 | * @param iter returns the iterator to the caret box representing the given |
384 | * position for \c cb |
385 | */ |
386 | static CaretBoxLine *constructCaretBoxLine(MassDeleter<CaretBoxLine> *deleter, |
387 | RenderBox *cb, bool outside, bool outsideEnd, CaretBoxIterator &iter) /*KDE_NO_EXPORT*/; |
388 | |
389 | #if DEBUG_CARETMODE > 0 |
390 | void dump(QTextStream &ts, const QString &ind) const; |
391 | QString information() const |
392 | { |
393 | QString result; |
394 | QTextStream ts(&result, QIODevice::WriteOnly); |
395 | dump(ts, QString()); |
396 | return result; |
397 | } |
398 | #endif |
399 | |
400 | protected: |
401 | /** contains the seek parameters */ |
402 | struct SeekBoxParams { |
403 | InlineBox *box; |
404 | bool outside; |
405 | bool outsideEnd; |
406 | bool found; |
407 | RenderObject *r; // if box is 0, seek for equal render objects instead |
408 | CaretBoxIterator ⁢ |
409 | |
410 | SeekBoxParams(InlineBox *box, bool outside, bool outsideEnd, RenderObject *obj, CaretBoxIterator &it) |
411 | : box(box), outside(outside), outsideEnd(outsideEnd), found(false), r(obj), it(it) |
412 | {} |
413 | |
414 | /** compares whether this seek box matches the given specification */ |
415 | bool equalsBox(const InlineBox *box, bool outside, bool outsideEnd) const |
416 | { |
417 | return (this->box && this->box == box |
418 | || this->r == box->object()) |
419 | && this->outside == outside |
420 | && (!this->outside || this->outsideEnd == outsideEnd); |
421 | } |
422 | /** compares whether this seek box matches the given caret box */ |
423 | bool operator ==(const CaretBox *cbox) const |
424 | { |
425 | return equalsBox(cbox->inlineBox(), cbox->isOutside(), cbox->isOutsideEnd()); |
426 | } |
427 | /** checks whether this box matches the given iterator. |
428 | * |
429 | * On success, it sets \c found, and assigns the iterator to \c it. |
430 | * @return true on match |
431 | */ |
432 | bool check(const CaretBoxIterator &chit) |
433 | { |
434 | if (*this == *chit) { |
435 | Q_ASSERT(!found); |
436 | found = true; |
437 | it = chit; |
438 | } |
439 | return found; |
440 | } |
441 | }; |
442 | |
443 | /** recursively converts the given inline box into caret boxes and adds them |
444 | * to this caret box line. |
445 | * |
446 | * It will additionally look for the caret box specified in SeekBoxParams. |
447 | */ |
448 | void addConvertedInlineBox(InlineBox *, SeekBoxParams &) /*KDE_NO_EXPORT*/; |
449 | |
450 | /** creates and adds the edge of a generic inline box |
451 | * @param box inline box |
452 | * @param fm font metrics of inline box |
453 | * @param left true to add left edge, false to add right edge |
454 | * @param rtl true if direction is rtl |
455 | */ |
456 | void addCreatedInlineBoxEdge(InlineBox *box, const QFontMetrics &fm, |
457 | bool left, bool rtl) /*KDE_NO_EXPORT*/; |
458 | /** creates and adds the edge of an inline flow box |
459 | * @param flowBox inline flow box |
460 | * @param fm font metrics of inline flow box |
461 | * @param left true to add left edge, false to add right edge |
462 | * @param rtl true if direction is rtl |
463 | */ |
464 | void addCreatedFlowBoxEdge(InlineFlowBox *flowBox, const QFontMetrics &fm, |
465 | bool left, bool rtl) /*KDE_NO_EXPORT*/; |
466 | /** creates and adds the inside of an inline flow box |
467 | * @param flowBox inline flow box |
468 | * @param fm font metrics of inline flow box |
469 | */ |
470 | void addCreatedFlowBoxInside(InlineFlowBox *flowBox, const QFontMetrics &fm) /*KDE_NO_EXPORT*/; |
471 | |
472 | friend class CaretBoxIterator; |
473 | }; |
474 | |
475 | typedef MassDeleter<CaretBoxLine> CaretBoxLineDeleter; |
476 | |
477 | inline CaretBox *CaretBoxIterator::data() const { return cbl->caret_boxes[index]; } |
478 | |
479 | /** |
480 | * Iterates through the lines of a document. |
481 | * |
482 | * The line iterator becomes invalid when the associated LinearDocument object |
483 | * is destroyed. |
484 | * @internal |
485 | * @author Leo Savernik |
486 | */ |
487 | class LineIterator |
488 | { |
489 | protected: |
490 | LinearDocument *lines; // associated document |
491 | CaretBoxLine *cbl; // current caret box line |
492 | |
493 | static CaretBoxIterator currentBox; // current inline box |
494 | static long currentOffset; |
495 | |
496 | // Note: cbl == 0 indicates a position beyond the beginning or the |
497 | // end of a document. |
498 | |
499 | /** Default constructor, only for internal use |
500 | */ |
501 | LineIterator() {} |
502 | |
503 | /** Initializes a new iterator. |
504 | * |
505 | * Note: This constructor neither cares about the correctness of @p node |
506 | * nor about @p offset. It is the responsibility of the caller to ensure |
507 | * that both point to valid places. |
508 | */ |
509 | LineIterator(LinearDocument *l, DOM::NodeImpl *node, long offset); |
510 | |
511 | public: |
512 | /** dereferences current caret box line. |
513 | * |
514 | * @returns the caret line box or 0 if end of document |
515 | */ |
516 | CaretBoxLine *operator *() const { return cbl; } |
517 | |
518 | /** returns the associated linear document |
519 | */ |
520 | LinearDocument *linearDocument() const { return lines; } |
521 | |
522 | /** seek next line |
523 | * |
524 | * Guaranteed to crash if beyond beginning/end of document. |
525 | */ |
526 | LineIterator &operator ++() { advance(false); return *this; } |
527 | |
528 | /** seek previous line. |
529 | * |
530 | * Guaranteed to crash if beyond beginning/end of document. |
531 | */ |
532 | LineIterator &operator --() { advance(true); return *this; } |
533 | |
534 | /** compares two iterators. The comparator actually works only for |
535 | * comparing arbitrary iterators to begin() and end(). |
536 | */ |
537 | bool operator ==(const LineIterator &it) const |
538 | { |
539 | return lines == it.lines && cbl == it.cbl; |
540 | } |
541 | |
542 | /** compares two iterators |
543 | */ |
544 | bool operator !=(const LineIterator &it) const |
545 | { |
546 | return !operator ==(it); |
547 | } |
548 | |
549 | /** Returns whether this line represents the outside end of the containing |
550 | * block. |
551 | * |
552 | * This result can only be relied on when isOutside is true. |
553 | */ |
554 | bool isOutsideEnd() { return cbl->isOutsideEnd(); } |
555 | |
556 | /** Tells whether the offset is meant to be outside or inside the |
557 | * containing block. |
558 | */ |
559 | bool isOutside() const { return cbl->isOutside(); } |
560 | |
561 | /** advances to the line to come. |
562 | * @param toBegin true, move to previous line, false, move to next line. |
563 | */ |
564 | void advance(bool toBegin); |
565 | |
566 | /** Whenever a new line iterator is created, it gets a caret box created. |
567 | * For memory reasons, it's saved in a static instance, |
568 | * thus making this function not thread-safe. |
569 | * |
570 | * This value can only be trusted immediately after having instantiated |
571 | * a line iterator or one of its derivatives. |
572 | * @return an iterator onto the corresponing caret box within the |
573 | * line represented by the last instantiation of a line iterator, |
574 | * or 0 if there was none. |
575 | */ |
576 | static CaretBoxIterator ¤tCaretBox() { return currentBox; } |
577 | |
578 | /** Whenever a new line iterator is created, it calculates a modified offset |
579 | * that is to be used with respect to the current render object. |
580 | * This offset can be queried with this function. |
581 | * |
582 | * This value can only be trusted immediately after having instantiated |
583 | * a line iterator or one of its derivatives. |
584 | * @return the modified offset. |
585 | */ |
586 | static long currentModifiedOffset() { return currentOffset; } |
587 | |
588 | protected: |
589 | /** seeks next block. |
590 | */ |
591 | void nextBlock(); |
592 | /** seeks previous block. |
593 | */ |
594 | void prevBlock(); |
595 | |
596 | friend class CaretBoxIterator; |
597 | friend class EditableLineIterator; |
598 | friend class EditableCaretBoxIterator; |
599 | friend class EditableCharacterIterator; |
600 | friend class LinearDocument; |
601 | }; |
602 | |
603 | /** |
604 | * Represents the whole document in terms of lines. |
605 | * |
606 | * SGML documents are trees. But for navigation, this representation is |
607 | * not practical. Therefore this class serves as a helper to represent the |
608 | * document as a linear list of lines. Its usage somewhat resembles STL |
609 | * semantics like begin and end as well as iterators. |
610 | * |
611 | * The lines itself are represented as caret line boxes. |
612 | * |
613 | * LinearDocument instances are not meant to be kept over the lifetime of their |
614 | * associated document, but constructed from (node, offset) pairs whenever line |
615 | * traversal is needed. This is because the underlying InlineFlowBox objects |
616 | * may be destroyed and recreated (e. g. by resizing the window, adding/removing |
617 | * elements). |
618 | * |
619 | * @author Leo Savernik |
620 | * @internal |
621 | */ |
622 | class LinearDocument { |
623 | public: |
624 | typedef LineIterator Iterator; |
625 | |
626 | /** |
627 | * Creates a new instance, and initializes it to the line specified by |
628 | * the parameters below. |
629 | * |
630 | * Creation will fail if @p node is invisible or defect. |
631 | * @param part part within which everything is taking place. |
632 | * @param node document node with which to start |
633 | * @param offset zero-based offset within this node. |
634 | * @param advancePolicy caret advance policy |
635 | * @param baseElem base element which the caret must not advance beyond |
636 | * (0 means whole document). The base element will be ignored if it |
637 | * cannot serve as a base (to see if this is the case, check whether |
638 | * LinearDocument::baseFlow()->element() != base) |
639 | */ |
640 | LinearDocument(KHTMLPart *part, DOM::NodeImpl *node, long offset, |
641 | CaretAdvancePolicy advancePolicy, DOM::ElementImpl *baseElem); |
642 | |
643 | virtual ~LinearDocument(); |
644 | |
645 | /** |
646 | * Tells whether this list contains any lines. |
647 | * |
648 | * @returns @p true if this document contains lines, @p false otherwise. Note |
649 | * that an empty document contains at least one line, so this method |
650 | * only returns @p false if the document could not be initialised for |
651 | * some reason. |
652 | */ |
653 | bool isValid() const // FIXME: not yet impl'd |
654 | { |
655 | return true; |
656 | } |
657 | |
658 | /** |
659 | * Returns the count of lines. |
660 | * |
661 | * Warning: This function is expensive. Call it once and cache the value. |
662 | * |
663 | * FIXME: It's not implemented yet (and maybe never will) |
664 | */ |
665 | int count() const; |
666 | |
667 | /** |
668 | * Returns a line iterator containing the current position as its starting |
669 | * value. |
670 | */ |
671 | Iterator current(); |
672 | |
673 | /** |
674 | * Returns a line iterator pointing right after the end of the document. |
675 | */ |
676 | const Iterator &end() const { return _end; } |
677 | |
678 | /** |
679 | * Returns a line iterator pointing to the very last line of the document. |
680 | */ |
681 | Iterator preEnd(); |
682 | |
683 | /** |
684 | * Returns a line iterator pointing to the very first line of the document. |
685 | */ |
686 | Iterator begin(); |
687 | |
688 | /** |
689 | * Returns a line iterator pointing just before the very first line of the |
690 | * document (this is somewhat an emulation of reverse iterators). |
691 | */ |
692 | const Iterator &preBegin() const { return _preBegin; } |
693 | |
694 | /** |
695 | * Returns the current caret advance policy |
696 | */ |
697 | CaretAdvancePolicy advancePolicy() const { return advPol; } |
698 | |
699 | /** |
700 | * Returns the base render object which the caret must not advance beyond. |
701 | * |
702 | * Note that HTML documents are usually restricted to the body element. |
703 | * |
704 | * @return the base render object or 0 if the whole document is valid. |
705 | */ |
706 | RenderObject *baseObject() const { return base; } |
707 | |
708 | protected: |
709 | void initPreBeginIterator(); |
710 | void initEndIterator(); |
711 | |
712 | protected: |
713 | CaretBoxLineDeleter cblDeleter; // mass deleter for caret box lines |
714 | DOM::NodeImpl *node; |
715 | long offset; |
716 | |
717 | Iterator _preBegin; |
718 | Iterator _end; |
719 | |
720 | KHTMLPart *m_part; |
721 | CaretAdvancePolicy advPol; |
722 | RenderObject *base; |
723 | |
724 | friend class LineIterator; |
725 | friend class EditableLineIterator; |
726 | friend class ErgonomicEditableLineIterator; |
727 | friend class CaretBoxIterator; |
728 | friend class EditableCaretBoxIterator; |
729 | friend class EditableCharacterIterator; |
730 | }; |
731 | |
732 | /** |
733 | * Iterates over the editable inner elements of a caret line box. |
734 | * |
735 | * The incrementor will traverse all caret boxes according to the associated |
736 | * linear document's caret advance policy. In contrast to \c CaretBoxIterator |
737 | * this iterator only regards caret boxes which are editable. |
738 | * |
739 | * @author Leo Savernik |
740 | * @internal |
741 | */ |
742 | class EditableCaretBoxIterator : public CaretBoxIterator { |
743 | KHTMLPart *m_part; |
744 | bool adjacent; |
745 | CaretAdvancePolicy advpol; // caret advance policy |
746 | |
747 | public: |
748 | /** initializes a new iterator from the given line iterator, |
749 | * beginning with the given caret box iterator, if specified |
750 | */ |
751 | EditableCaretBoxIterator(LineIterator &lit, bool fromEnd = false, |
752 | CaretBoxIterator *it = 0) |
753 | : CaretBoxIterator(it ? *it : (fromEnd ? (*lit)->end() : (*lit)->preBegin())), |
754 | m_part(lit.lines->m_part), adjacent(false), |
755 | advpol(lit.lines->advancePolicy()) |
756 | { |
757 | if (!it) { |
758 | if (fromEnd) --*this; else ++*this; |
759 | } |
760 | } |
761 | |
762 | /** empty constructor. Use only to copy another iterator into this one. |
763 | */ |
764 | EditableCaretBoxIterator() {} |
765 | |
766 | /** returns @p true when the current caret box is adjacent to the |
767 | * previously iterated caret box, i. e. no intervening caret boxes. |
768 | */ |
769 | bool isAdjacent() const { return adjacent; } |
770 | |
771 | /** increments the iterator to point to the next editable caret box. |
772 | */ |
773 | EditableCaretBoxIterator &operator ++() { advance(false); return *this; } |
774 | |
775 | /** decrements the iterator to point to the previous editable caret box. |
776 | */ |
777 | EditableCaretBoxIterator &operator --() { advance(true); return *this; } |
778 | |
779 | /** advances to the editable caret box to come |
780 | * @param toBegin true, move towards beginning, false, move towards end. |
781 | */ |
782 | void advance(bool toBegin); |
783 | |
784 | protected: |
785 | /** finds out if the given box is editable. |
786 | * @param boxit iterator to given caret box |
787 | * @param fromEnd true when advancing towards the beginning |
788 | * @return @p true if box is editable |
789 | */ |
790 | bool isEditable(const CaretBoxIterator &boxit, bool fromEnd); |
791 | }; |
792 | |
793 | /** |
794 | * Iterates through the editable lines of a document. |
795 | * |
796 | * This iterator, opposing to @p LineIterator, only regards editable lines. |
797 | * Additionally, this iterator enforces the caret advance policy. |
798 | * |
799 | * The iterator can be compared to normal LineIterators, especially to |
800 | * @ref LinearDocument::preBegin and @ref LinearDocument::end |
801 | * |
802 | * The line iterator becomes invalid when the associated LinearDocument object |
803 | * is destroyed. |
804 | * @internal |
805 | * @author Leo Savernik |
806 | */ |
807 | class EditableLineIterator : public LineIterator { |
808 | public: |
809 | /** Initializes a new iterator. |
810 | * |
811 | * The iterator is set to the first following editable line or to the |
812 | * end if no editable line follows. |
813 | * @param it a line iterator to initialize this from |
814 | * @param fromEnd @p true, traverse towards the beginning in search of an |
815 | * editable line |
816 | */ |
817 | EditableLineIterator(const LineIterator &it, bool fromEnd = false) |
818 | : LineIterator(it) |
819 | { |
820 | if (!cbl) return; |
821 | if (!isEditable(*this)) advance(fromEnd); |
822 | } |
823 | |
824 | /** empty constructor. |
825 | * |
826 | * Only use if you want to copy another iterator onto it later. |
827 | */ |
828 | EditableLineIterator() {} |
829 | |
830 | /** seek next line |
831 | * |
832 | * Guaranteed to crash if beyond beginning/end of document. |
833 | */ |
834 | EditableLineIterator &operator ++() { advance(false); return *this; } |
835 | |
836 | /** seek previous line. |
837 | * |
838 | * Guaranteed to crash if beyond beginning/end of document. |
839 | */ |
840 | EditableLineIterator &operator --() { advance(true); return *this; } |
841 | |
842 | /** advances to the line to come. |
843 | * @param toBegin true, move to previous line, false, move to next line. |
844 | */ |
845 | void advance(bool toBegin); |
846 | |
847 | protected: |
848 | /** finds out if the current line is editable. |
849 | * |
850 | * @param it check caret box line iterator points to |
851 | * @return @p true if line is editable |
852 | */ |
853 | bool isEditable(LineIterator &it) |
854 | { |
855 | EditableCaretBoxIterator fbit = it; |
856 | return fbit != (*it)->end(); |
857 | } |
858 | |
859 | }; |
860 | |
861 | /** Represents a render table as a linear list of rows. |
862 | * |
863 | * This iterator abstracts from table sections and treats tables as a linear |
864 | * representation of all rows they contain. |
865 | * @author Leo Savernik |
866 | * @internal |
867 | */ |
868 | class TableRowIterator { |
869 | protected: |
870 | TableSectionIterator sec; // current section |
871 | int index; // index of row within section |
872 | public: |
873 | /** Constructs a new iterator. |
874 | * @param table table to iterate through. |
875 | * @param fromEnd @p true to iterate towards the beginning |
876 | * @param row pointer to row to start with, 0 starts at the first/last |
877 | * row. |
878 | */ |
879 | TableRowIterator(RenderTable *table, bool fromEnd = false, |
880 | RenderTableSection::RowStruct *row = 0); |
881 | |
882 | /** Constructs a new iterator. |
883 | * @param section table section to begin with |
884 | * @param index index within table section |
885 | */ |
886 | TableRowIterator(RenderTableSection *section, int index) |
887 | : sec(section), index(index) |
888 | {} |
889 | |
890 | /** empty constructor. This must be assigned another iterator before it is |
891 | * useable. |
892 | */ |
893 | TableRowIterator() {} |
894 | |
895 | /** returns the current table row. |
896 | * @return the row or 0 if the end of the table has been reached. |
897 | */ |
898 | RenderTableSection::RowStruct *operator *() |
899 | { |
900 | if (!*sec) return 0; |
901 | return &(*sec)->grid[index]; |
902 | } |
903 | |
904 | /** advances to the next row |
905 | */ |
906 | TableRowIterator &operator ++(); |
907 | |
908 | /** advances to the previous row |
909 | */ |
910 | TableRowIterator &operator --(); |
911 | |
912 | protected: |
913 | }; |
914 | |
915 | /** Iterates through the editable lines of a document, in a topological order. |
916 | * |
917 | * The differences between this and the EditableLineIterator lies in the way |
918 | * lines are inquired. While the latter steps through the lines in document |
919 | * order, the former takes into consideration ergonomics. |
920 | * |
921 | * This is especially useful for tables. EditableLineIterator traverses all |
922 | * table cells from left to right, top to bottom, while this one will |
923 | * actually snap to the cell in the right position, and traverse only |
924 | * upwards/downwards, thus providing a more intuitive navigation. |
925 | * |
926 | * @author Leo Savernik |
927 | * @internal |
928 | */ |
929 | class ErgonomicEditableLineIterator : public EditableLineIterator { |
930 | protected: |
931 | int xCoor; // x-coordinate to determine cell position |
932 | public: |
933 | /** Initializes a new ergonomic editable line iterator from the given one. |
934 | * @param it line iterator |
935 | * @param x absolute x-coordinate for cell determination |
936 | */ |
937 | ErgonomicEditableLineIterator(const LineIterator &it, int x) |
938 | : EditableLineIterator(it), xCoor(x) {} |
939 | |
940 | /** Constructs an uninitialized iterator which must be assigned a line iterator before |
941 | * it can be used. |
942 | */ |
943 | ErgonomicEditableLineIterator() {} |
944 | |
945 | /** seek next line. |
946 | * |
947 | * The next line will be one that is visually situated below this line. |
948 | */ |
949 | ErgonomicEditableLineIterator &operator ++(); |
950 | |
951 | /** seek previous line. |
952 | * |
953 | * The previous line will be one that is visually situated above this line. |
954 | */ |
955 | ErgonomicEditableLineIterator &operator --(); |
956 | |
957 | protected: |
958 | /** determines the topologically next render object. |
959 | * @param oldCell table cell the original object was under. |
960 | * @param newObject object to determine whether and which transition |
961 | * between cells is to be handled. It does not have to be an object in the correct |
962 | * topological cell, a simple delivery from an editable line iterator suffices. |
963 | * @param toBegin if @p true, iterate towards the beginning |
964 | */ |
965 | void determineTopologicalElement(RenderTableCell *oldCell, |
966 | RenderObject *newObject, bool toBegin); |
967 | |
968 | /** initializes the iterator to point to the first previous/following editable |
969 | * line. |
970 | * @param newBlock take this as base block. |
971 | * @param toBegin @p true, iterate towards beginning. |
972 | */ |
973 | void calcAndStoreNewLine(RenderBlock *newBlock, bool toBegin); |
974 | |
975 | }; |
976 | |
977 | /** |
978 | * Provides iterating through the document in terms of characters. Only the |
979 | * editable characters are regarded. |
980 | * |
981 | * This iterator represents the document, which is structured as a tree itself, |
982 | * as a linear stream of characters. |
983 | */ |
984 | class EditableCharacterIterator { |
985 | protected: |
986 | EditableLineIterator _it; |
987 | EditableCaretBoxIterator ebit; |
988 | long _offset; // offset within current caret box. |
989 | int _char; |
990 | bool _end:1; // true when end of document has been reached |
991 | |
992 | public: |
993 | |
994 | /** empty constructor. |
995 | * |
996 | * Only use if you want to assign another iterator as no fields will |
997 | * be initialized. |
998 | */ |
999 | EditableCharacterIterator() {} |
1000 | |
1001 | /** constructs a new iterator from the given linear document. |
1002 | * |
1003 | * @param ld linear representation of document. |
1004 | */ |
1005 | EditableCharacterIterator(LinearDocument *ld) |
1006 | : _it(ld->current()), |
1007 | ebit(_it, false, &_it.currentCaretBox()), |
1008 | _offset(_it.currentModifiedOffset()), _char(-1), _end(false) |
1009 | { |
1010 | // ### temporary fix for illegal nodes |
1011 | if (_it == ld->end()) { _end = true; return; } |
1012 | initFirstChar(); |
1013 | } |
1014 | |
1015 | /** returns the current character, or -1 if not on a text node, or beyond |
1016 | * the end. |
1017 | */ |
1018 | int chr() const { return _char; } |
1019 | |
1020 | /** returns the current character as a unicode symbol, substituting |
1021 | * a blank for a non-text node. |
1022 | */ |
1023 | QChar operator *() const { return QChar(_char >= 0 ? _char : |
1024 | |
1025 | /** returns true when the end of the document has been reached. |
1026 | */ |
1027 | bool isEnd() const { return _end; } |
1028 | /** returns the current offset |
1029 | */ |
1030 | long offset() const { return _offset; } |
1031 | /** returns the current render object. |
1032 | */ |
1033 | RenderObject *renderer() const { return (*ebit)->object(); } |
1034 | /** returns the current caret box. |
1035 | * |
1036 | * Will crash if beyond end. |
1037 | */ |
1038 | CaretBox *caretBox() const { return *ebit; } |
1039 | /** returns the current inline box. |
1040 | * |
1041 | * May be 0 if the current element has none, or if the end has been reached. |
1042 | * Therefore, do *not* use this to test for the end condition, use node() |
1043 | * instead. |
1044 | */ |
1045 | InlineBox *inlineBox() const { return (*ebit)->inlineBox(); } |
1046 | /** returns whether the current line box represents the outside of its |
1047 | * render object. |
1048 | */ |
1049 | // bool boxIsOutside() const { return _it.isOutside(); } |
1050 | |
1051 | /** moves to the next editable character. |
1052 | */ |
1053 | EditableCharacterIterator &operator ++(); |
1054 | |
1055 | /** moves to the previous editable character. |
1056 | */ |
1057 | EditableCharacterIterator &operator --(); |
1058 | |
1059 | protected: |
1060 | /** initializes the _char member by reading the character at the current |
1061 | * offset, peeking ahead as necessary. |
1062 | */ |
1063 | void initFirstChar(); |
1064 | /** reads ahead the next node and updates the data structures accordingly |
1065 | */ |
1066 | void peekNext() |
1067 | { |
1068 | EditableCaretBoxIterator copy = ebit; |
1069 | ++copy; |
1070 | if (copy == (*_it)->end()) { _char = -1; return; } |
1071 | |
1072 | CaretBox *box = *copy; |
1073 | InlineBox *b = box->inlineBox(); |
1074 | if (b && !box->isOutside() && b->isInlineTextBox()) |
1075 | _char = static_cast<RenderText *>(b->object())->str->s[b->minOffset()].unicode(); |
1076 | else |
1077 | _char = -1; |
1078 | } |
1079 | /** reads ahead the previous node and updates the data structures accordingly |
1080 | */ |
1081 | void peekPrev() |
1082 | { |
1083 | --ebit; |
1084 | } |
1085 | |
1086 | }; |
1087 | |
1088 | |
1089 | }/*namespace khtml*/ |
1090 | |
1091 | |
1092 | #endif |
1093 |
Warning: That file was not part of the compilation database. It may have many parsing errors.