QTextDocument::contentsChanged() was emitted twice for a single
[qt:qt-iphone-clone.git] / src / gui / text / qtextdocument_p.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtGui module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <private/qtools_p.h>
43 #include <qdebug.h>
44
45 #include "qtextdocument_p.h"
46 #include "qtextdocument.h"
47 #include <qtextformat.h>
48 #include "qtextformat_p.h"
49 #include "qtextobject_p.h"
50 #include "qtextcursor.h"
51 #include "qtextimagehandler_p.h"
52 #include "qtextcursor_p.h"
53 #include "qtextdocumentlayout_p.h"
54 #include "qtexttable.h"
55 #include "qtextengine_p.h"
56
57 #include <stdlib.h>
58
59 QT_BEGIN_NAMESPACE
60
61 #define PMDEBUG if(0) qDebug
62
63 // The VxWorks DIAB compiler crashes when initializing the anonymouse union with { a7 }
64 #if !defined(Q_CC_DIAB)
65 #  define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8) \
66           QTextUndoCommand c = { a1, a2, 0, 0, a3, a4, a5, a6, { a7 }, a8 }
67 #else
68 #  define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8) \
69           QTextUndoCommand c = { a1, a2, 0, 0, a3, a4, a5, a6 }; c.blockFormat = a7; c.revision = a8
70 #endif
71
72 /*
73   Structure of a document:
74
75   DOCUMENT :== FRAME_CONTENTS
76   FRAME :== START_OF_FRAME  FRAME_CONTENTS END_OF_FRAME
77   FRAME_CONTENTS = LIST_OF_BLOCKS ((FRAME | TABLE) LIST_OF_BLOCKS)*
78   TABLE :== (START_OF_FRAME TABLE_CELL)+ END_OF_FRAME
79   TABLE_CELL = FRAME_CONTENTS
80   LIST_OF_BLOCKS :== (BLOCK END_OF_PARA)* BLOCK
81   BLOCK :== (FRAGMENT)*
82   FRAGMENT :== String of characters
83
84   END_OF_PARA :== 0x2029 # Paragraph separator in Unicode
85   START_OF_FRAME :== 0xfdd0
86   END_OF_FRAME := 0xfdd1
87
88   Note also that LIST_OF_BLOCKS can be empty. Nevertheless, there is
89   at least one valid cursor position there where you could start
90   typing. The block format is in this case determined by the last
91   END_OF_PARA/START_OF_FRAME/END_OF_FRAME (see below).
92
93   Lists are not in here, as they are treated specially. A list is just
94   a collection of (not neccessarily connected) blocks, that share the
95   same objectIndex() in the format that refers to the list format and
96   object.
97
98   The above does not clearly note where formats are. Here's
99   how it looks currently:
100
101   FRAGMENT: one charFormat associated
102
103   END_OF_PARA: one charFormat, and a blockFormat for the _next_ block.
104
105   START_OF_FRAME: one char format, and a blockFormat (for the next
106   block). The format associated with the objectIndex() of the
107   charFormat decides whether this is a frame or table and its
108   properties
109
110   END_OF_FRAME: one charFormat and a blockFormat (for the next
111   block). The object() of the charFormat is the same as for the
112   corresponding START_OF_BLOCK.
113
114
115   The document is independent of the layout with certain restrictions:
116
117   * Cursor movement (esp. up and down) depend on the layout.
118   * You cannot have more than one layout, as the layout data of QTextObjects
119     is stored in the text object itself.
120
121 */
122
123 void QTextBlockData::invalidate() const
124 {
125     if (layout)
126         layout->engine()->invalidate();
127 }
128
129 static bool isValidBlockSeparator(const QChar &ch)
130 {
131     return ch == QChar::ParagraphSeparator
132         || ch == QTextBeginningOfFrame
133         || ch == QTextEndOfFrame;
134 }
135
136 #ifndef QT_NO_DEBUG
137 static bool noBlockInString(const QString &str)
138 {
139     return !str.contains(QChar::ParagraphSeparator)
140         && !str.contains(QTextBeginningOfFrame)
141         && !str.contains(QTextEndOfFrame);
142 }
143 #endif
144
145 bool QTextUndoCommand::tryMerge(const QTextUndoCommand &other)
146 {
147     if (command != other.command)
148         return false;
149
150     if (command == Inserted
151         && (pos + length == other.pos)
152         && (strPos + length == other.strPos)
153         && format == other.format) {
154
155         length += other.length;
156         return true;
157     }
158
159     // removal to the 'right' using 'Delete' key
160     if (command == Removed
161         && pos == other.pos
162         && (strPos + length == other.strPos)
163         && format == other.format) {
164
165         length += other.length;
166         return true;
167     }
168
169     // removal to the 'left' using 'Backspace'
170     if (command == Removed
171         && (other.pos + other.length == pos)
172         && (other.strPos + other.length == strPos)
173         && (format == other.format)) {
174
175         int l = length;
176         (*this) = other;
177
178         length += l;
179         return true;
180     }
181
182     return false;
183 }
184
185 QTextDocumentPrivate::QTextDocumentPrivate()
186     : wasUndoAvailable(false),
187     wasRedoAvailable(false),
188     docChangeOldLength(0),
189     docChangeLength(0),
190     framesDirty(true),
191     rtFrame(0),
192     initialBlockCharFormatIndex(-1) // set correctly later in init()
193 {
194     editBlock = 0;
195     docChangeFrom = -1;
196
197     undoState = 0;
198
199     lout = 0;
200
201     modified = false;
202     modifiedState = 0;
203
204     undoEnabled = true;
205     inContentsChange = false;
206     inEdit = false;
207
208     defaultTextOption.setTabStop(80); // same as in qtextengine.cpp
209     defaultTextOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
210
211     indentWidth = 40;
212     documentMargin = 4;
213
214     maximumBlockCount = 0;
215     needsEnsureMaximumBlockCount = false;
216     unreachableCharacterCount = 0;
217     lastBlockCount = 0;
218 }
219
220 void QTextDocumentPrivate::init()
221 {
222     framesDirty = false;
223
224     bool undoState = undoEnabled;
225     undoEnabled = false;
226     initialBlockCharFormatIndex = formats.indexForFormat(QTextCharFormat());
227     insertBlock(0, formats.indexForFormat(QTextBlockFormat()), formats.indexForFormat(QTextCharFormat()));
228     undoEnabled = undoState;
229     modified = false;
230     modifiedState = 0;
231 }
232
233 void QTextDocumentPrivate::clear()
234 {
235     Q_Q(QTextDocument);
236     for (int i = 0; i < cursors.count(); ++i) {
237         cursors.at(i)->setPosition(0);
238         cursors.at(i)->currentCharFormat = -1;
239         cursors.at(i)->anchor = 0;
240         cursors.at(i)->adjusted_anchor = 0;
241     }
242
243     QList<QTextCursorPrivate *>oldCursors = cursors;
244     QT_TRY{
245         cursors.clear();
246         changedCursors.clear();
247
248         QMap<int, QTextObject *>::Iterator objectIt = objects.begin();
249         while (objectIt != objects.end()) {
250             if (*objectIt != rtFrame) {
251                 delete *objectIt;
252                 objectIt = objects.erase(objectIt);
253             } else {
254                 ++objectIt;
255             }
256         }
257         // also clear out the remaining root frame pointer
258         // (we're going to delete the object further down)
259         objects.clear();
260
261         title.clear();
262         undoState = 0;
263         truncateUndoStack();
264         text = QString();
265         unreachableCharacterCount = 0;
266         modifiedState = 0;
267         modified = false;
268         formats = QTextFormatCollection();
269         int len = fragments.length();
270         fragments.clear();
271         blocks.clear();
272         cachedResources.clear();
273         delete rtFrame;
274         rtFrame = 0;
275         init();
276         cursors = oldCursors;
277         inContentsChange = true;
278         q->contentsChange(0, len, 0);
279         inContentsChange = false;
280         if (lout)
281             lout->documentChanged(0, len, 0);
282     } QT_CATCH(...) {
283         cursors = oldCursors; // at least recover the cursors
284         QT_RETHROW;
285     }
286 }
287
288 QTextDocumentPrivate::~QTextDocumentPrivate()
289 {
290     for (int i = 0; i < cursors.count(); ++i)
291         cursors.at(i)->priv = 0;
292     cursors.clear();
293     undoState = 0;
294     undoEnabled = true;
295     truncateUndoStack();
296 }
297
298 void QTextDocumentPrivate::setLayout(QAbstractTextDocumentLayout *layout)
299 {
300     Q_Q(QTextDocument);
301     if (lout == layout)
302         return;
303     const bool firstLayout = !lout;
304     delete lout;
305     lout = layout;
306
307     if (!firstLayout)
308         for (BlockMap::Iterator it = blocks.begin(); !it.atEnd(); ++it)
309             it->free();
310
311     emit q->documentLayoutChanged();
312     inContentsChange = true;
313     emit q->contentsChange(0, 0, length());
314     inContentsChange = false;
315     if (lout)
316         lout->documentChanged(0, 0, length());
317 }
318
319
320 void QTextDocumentPrivate::insert_string(int pos, uint strPos, uint length, int format, QTextUndoCommand::Operation op)
321 {
322     // ##### optimise when only appending to the fragment!
323     Q_ASSERT(noBlockInString(text.mid(strPos, length)));
324
325     split(pos);
326     uint x = fragments.insert_single(pos, length);
327     QTextFragmentData *X = fragments.fragment(x);
328     X->format = format;
329     X->stringPosition = strPos;
330     uint w = fragments.previous(x);
331     if (w)
332         unite(w);
333
334     int b = blocks.findNode(pos);
335     blocks.setSize(b, blocks.size(b)+length);
336
337     Q_ASSERT(blocks.length() == fragments.length());
338
339     QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(format));
340     if (frame) {
341         frame->d_func()->fragmentAdded(text.at(strPos), x);
342         framesDirty = true;
343     }
344
345     adjustDocumentChangesAndCursors(pos, length, op);
346 }
347
348 int QTextDocumentPrivate::insert_block(int pos, uint strPos, int format, int blockFormat, QTextUndoCommand::Operation op, int command)
349 {
350     split(pos);
351     uint x = fragments.insert_single(pos, 1);
352     QTextFragmentData *X = fragments.fragment(x);
353     X->format = format;
354     X->stringPosition = strPos;
355     // no need trying to unite, since paragraph separators are always in a fragment of their own
356
357     Q_ASSERT(isValidBlockSeparator(text.at(strPos)));
358     Q_ASSERT(blocks.length()+1 == fragments.length());
359
360     int block_pos = pos;
361     if (blocks.length() && command == QTextUndoCommand::BlockRemoved)
362         ++block_pos;
363     int size = 1;
364     int n = blocks.findNode(block_pos);
365     int key = n ? blocks.position(n) : blocks.length();
366
367     Q_ASSERT(n || (!n && block_pos == blocks.length()));
368     if (key != block_pos) {
369         Q_ASSERT(key < block_pos);
370         int oldSize = blocks.size(n);
371         blocks.setSize(n, block_pos-key);
372         size += oldSize - (block_pos-key);
373     }
374     int b = blocks.insert_single(block_pos, size);
375     QTextBlockData *B = blocks.fragment(b);
376     B->format = blockFormat;
377
378     Q_ASSERT(blocks.length() == fragments.length());
379
380     QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blockFormat));
381     if (group)
382         group->blockInserted(QTextBlock(this, b));
383
384     QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(formats.format(format)));
385     if (frame) {
386         frame->d_func()->fragmentAdded(text.at(strPos), x);
387         framesDirty = true;
388     }
389
390     adjustDocumentChangesAndCursors(pos, 1, op);
391     return x;
392 }
393
394 int QTextDocumentPrivate::insertBlock(const QChar &blockSeparator,
395                                   int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation op)
396 {
397     Q_ASSERT(formats.format(blockFormat).isBlockFormat());
398     Q_ASSERT(formats.format(charFormat).isCharFormat());
399     Q_ASSERT(pos >= 0 && (pos < fragments.length() || (pos == 0 && fragments.length() == 0)));
400     Q_ASSERT(isValidBlockSeparator(blockSeparator));
401
402     beginEditBlock();
403
404     int strPos = text.length();
405     text.append(blockSeparator);
406
407     int ob = blocks.findNode(pos);
408     bool atBlockEnd = true;
409     bool atBlockStart = true;
410     int oldRevision = 0;
411     if (ob) {
412         atBlockEnd = (pos - blocks.position(ob) == blocks.size(ob)-1);
413         atBlockStart = ((int)blocks.position(ob) == pos);
414         oldRevision = blocks.fragment(ob)->revision;
415     }
416
417     const int fragment = insert_block(pos, strPos, charFormat, blockFormat, op, QTextUndoCommand::BlockRemoved);
418
419     Q_ASSERT(blocks.length() == fragments.length());
420
421     int b = blocks.findNode(pos);
422     QTextBlockData *B = blocks.fragment(b);
423
424     QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::BlockInserted, (editBlock != 0),
425                             op, charFormat, strPos, pos, blockFormat,
426                             B->revision);
427
428     appendUndoItem(c);
429     Q_ASSERT(undoState == undoStack.size());
430
431     // update revision numbers of the modified blocks.
432     B->revision = (atBlockEnd && !atBlockStart)? oldRevision : undoState;
433     b = blocks.next(b);
434     if (b) {
435         B = blocks.fragment(b);
436         B->revision = atBlockStart ? oldRevision : undoState;
437     }
438
439     if (formats.charFormat(charFormat).objectIndex() == -1)
440         needsEnsureMaximumBlockCount = true;
441
442     endEditBlock();
443     return fragment;
444 }
445
446 int QTextDocumentPrivate::insertBlock(int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation op)
447 {
448     return insertBlock(QChar::ParagraphSeparator, pos, blockFormat, charFormat, op);
449 }
450
451 void QTextDocumentPrivate::insert(int pos, int strPos, int strLength, int format)
452 {
453     if (strLength <= 0)
454         return;
455
456     Q_ASSERT(pos >= 0 && pos < fragments.length());
457     Q_ASSERT(formats.format(format).isCharFormat());
458
459     beginEdit();
460     insert_string(pos, strPos, strLength, format, QTextUndoCommand::MoveCursor);
461     if (undoEnabled) {
462         int b = blocks.findNode(pos);
463         QTextBlockData *B = blocks.fragment(b);
464
465         QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::Inserted, (editBlock != 0),
466                                 QTextUndoCommand::MoveCursor, format, strPos, pos, strLength,
467                                 B->revision);
468         appendUndoItem(c);
469         B->revision = undoState;
470         Q_ASSERT(undoState == undoStack.size());
471     }
472     finishEdit();
473 }
474
475 void QTextDocumentPrivate::insert(int pos, const QString &str, int format)
476 {
477     if (str.size() == 0)
478         return;
479
480     Q_ASSERT(noBlockInString(str));
481
482     int strPos = text.length();
483     text.append(str);
484     insert(pos, strPos, str.length(), format);
485 }
486
487 int QTextDocumentPrivate::remove_string(int pos, uint length, QTextUndoCommand::Operation op)
488 {
489     Q_ASSERT(pos >= 0);
490     Q_ASSERT(blocks.length() == fragments.length());
491     Q_ASSERT(blocks.length() >= pos+(int)length);
492
493     int b = blocks.findNode(pos);
494     uint x = fragments.findNode(pos);
495
496     Q_ASSERT(blocks.size(b) > length);
497     Q_ASSERT(x && fragments.position(x) == (uint)pos && fragments.size(x) == length);
498     Q_ASSERT(noBlockInString(text.mid(fragments.fragment(x)->stringPosition, length)));
499
500     blocks.setSize(b, blocks.size(b)-length);
501
502     QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(fragments.fragment(x)->format));
503     if (frame) {
504         frame->d_func()->fragmentRemoved(text.at(fragments.fragment(x)->stringPosition), x);
505         framesDirty = true;
506     }
507
508     const int w = fragments.erase_single(x);
509
510     if (!undoEnabled)
511         unreachableCharacterCount += length;
512
513     adjustDocumentChangesAndCursors(pos, -int(length), op);
514
515     return w;
516 }
517
518 int QTextDocumentPrivate::remove_block(int pos, int *blockFormat, int command, QTextUndoCommand::Operation op)
519 {
520     Q_ASSERT(pos >= 0);
521     Q_ASSERT(blocks.length() == fragments.length());
522     Q_ASSERT(blocks.length() > pos);
523
524     int b = blocks.findNode(pos);
525     uint x = fragments.findNode(pos);
526
527     Q_ASSERT(x && (int)fragments.position(x) == pos);
528     Q_ASSERT(fragments.size(x) == 1);
529     Q_ASSERT(isValidBlockSeparator(text.at(fragments.fragment(x)->stringPosition)));
530     Q_ASSERT(b);
531
532     if (blocks.size(b) == 1 && command == QTextUndoCommand::BlockAdded) {
533         Q_ASSERT((int)blocks.position(b) == pos);
534 //      qDebug("removing empty block");
535         // empty block remove the block itself
536     } else {
537         // non empty block, merge with next one into this block
538 //      qDebug("merging block with next");
539         int n = blocks.next(b);
540         Q_ASSERT((int)blocks.position(n) == pos + 1);
541         blocks.setSize(b, blocks.size(b) + blocks.size(n) - 1);
542         b = n;
543     }
544     *blockFormat = blocks.fragment(b)->format;
545
546     QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blocks.fragment(b)->format));
547     if (group)
548         group->blockRemoved(QTextBlock(this, b));
549
550     QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(fragments.fragment(x)->format));
551     if (frame) {
552         frame->d_func()->fragmentRemoved(text.at(fragments.fragment(x)->stringPosition), x);
553         framesDirty = true;
554     }
555
556     blocks.erase_single(b);
557     const int w = fragments.erase_single(x);
558
559     adjustDocumentChangesAndCursors(pos, -1, op);
560
561     return w;
562 }
563
564 #if !defined(QT_NO_DEBUG)
565 static bool isAncestorFrame(QTextFrame *possibleAncestor, QTextFrame *child)
566 {
567     while (child) {
568         if (child == possibleAncestor)
569             return true;
570         child = child->parentFrame();
571     }
572     return false;
573 }
574 #endif
575
576 void QTextDocumentPrivate::move(int pos, int to, int length, QTextUndoCommand::Operation op)
577 {
578     Q_ASSERT(to <= fragments.length() && to <= pos);
579     Q_ASSERT(pos >= 0 && pos+length <= fragments.length());
580     Q_ASSERT(blocks.length() == fragments.length());
581
582     if (pos == to)
583         return;
584
585     beginEdit();
586     const bool needsInsert = to != -1;
587
588 #if !defined(QT_NO_DEBUG)
589     const bool startAndEndInSameFrame = (frameAt(pos) == frameAt(pos + length - 1));
590
591     const bool endIsEndOfChildFrame = (isAncestorFrame(frameAt(pos), frameAt(pos + length - 1))
592                                        && text.at(find(pos + length - 1)->stringPosition) == QTextEndOfFrame);
593
594     const bool startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent
595                = (text.at(find(pos)->stringPosition) == QTextBeginningOfFrame
596                   && text.at(find(pos + length - 1)->stringPosition) == QTextEndOfFrame
597                   && frameAt(pos)->parentFrame() == frameAt(pos + length - 1)->parentFrame());
598
599     const bool isFirstTableCell = (qobject_cast<QTextTable *>(frameAt(pos + length - 1))
600                                   && frameAt(pos + length - 1)->parentFrame() == frameAt(pos));
601
602     Q_ASSERT(startAndEndInSameFrame || endIsEndOfChildFrame || startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent || isFirstTableCell);
603 #endif
604
605     split(pos);
606     split(pos+length);
607
608     uint dst = needsInsert ? fragments.findNode(to) : 0;
609     uint dstKey = needsInsert ? fragments.position(dst) : 0;
610
611     uint x = fragments.findNode(pos);
612     uint end = fragments.findNode(pos+length);
613
614     uint w = 0;
615     while (x != end) {
616         uint n = fragments.next(x);
617
618         uint key = fragments.position(x);
619         uint b = blocks.findNode(key+1);
620         QTextBlockData *B = blocks.fragment(b);
621         int blockRevision = B->revision;
622
623         QTextFragmentData *X = fragments.fragment(x);
624         QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::Removed, (editBlock != 0),
625                                 op, X->format, X->stringPosition, key, X->size_array[0],
626                                 blockRevision);
627         QT_INIT_TEXTUNDOCOMMAND(cInsert, QTextUndoCommand::Inserted, (editBlock != 0),
628                                 op, X->format, X->stringPosition, dstKey, X->size_array[0],
629                                 blockRevision);
630
631         if (key+1 != blocks.position(b)) {
632 //          qDebug("remove_string from %d length %d", key, X->size_array[0]);
633             Q_ASSERT(noBlockInString(text.mid(X->stringPosition, X->size_array[0])));
634             w = remove_string(key, X->size_array[0], op);
635
636             if (needsInsert) {
637                 insert_string(dstKey, X->stringPosition, X->size_array[0], X->format, op);
638                 dstKey += X->size_array[0];
639             }
640         } else {
641 //          qDebug("remove_block at %d", key);
642             Q_ASSERT(X->size_array[0] == 1 && isValidBlockSeparator(text.at(X->stringPosition)));
643             b = blocks.previous(b);
644             B = 0;
645             c.command = blocks.size(b) == 1 ? QTextUndoCommand::BlockDeleted : QTextUndoCommand::BlockRemoved;
646             w = remove_block(key, &c.blockFormat, QTextUndoCommand::BlockAdded, op);
647
648             if (needsInsert) {
649                 insert_block(dstKey++, X->stringPosition, X->format, c.blockFormat, op, QTextUndoCommand::BlockRemoved);
650                 cInsert.command = blocks.size(b) == 1 ? QTextUndoCommand::BlockAdded : QTextUndoCommand::BlockInserted;
651                 cInsert.blockFormat = c.blockFormat;
652             }
653         }
654         appendUndoItem(c);
655         if (B)
656             B->revision = undoState;
657         x = n;
658
659         if (needsInsert)
660             appendUndoItem(cInsert);
661     }
662     if (w)
663         unite(w);
664
665     Q_ASSERT(blocks.length() == fragments.length());
666
667     finishEdit();
668 }
669
670 void QTextDocumentPrivate::remove(int pos, int length, QTextUndoCommand::Operation op)
671 {
672     if (length == 0)
673         return;
674     move(pos, -1, length, op);
675 }
676
677 void QTextDocumentPrivate::setCharFormat(int pos, int length, const QTextCharFormat &newFormat, FormatChangeMode mode)
678 {
679     beginEditBlock();
680
681     Q_ASSERT(newFormat.isValid());
682
683     int newFormatIdx = -1;
684     if (mode == SetFormatAndPreserveObjectIndices) {
685         QTextCharFormat cleanFormat = newFormat;
686         cleanFormat.clearProperty(QTextFormat::ObjectIndex);
687         newFormatIdx = formats.indexForFormat(cleanFormat);
688     } else if (mode == SetFormat) {
689         newFormatIdx = formats.indexForFormat(newFormat);
690     }
691
692     if (pos == -1) {
693         if (mode == MergeFormat) {
694             QTextFormat format = formats.format(initialBlockCharFormatIndex);
695             format.merge(newFormat);
696             initialBlockCharFormatIndex = formats.indexForFormat(format);
697         } else if (mode == SetFormatAndPreserveObjectIndices
698                    && formats.format(initialBlockCharFormatIndex).objectIndex() != -1) {
699             QTextCharFormat f = newFormat;
700             f.setObjectIndex(formats.format(initialBlockCharFormatIndex).objectIndex());
701             initialBlockCharFormatIndex = formats.indexForFormat(f);
702         } else {
703             initialBlockCharFormatIndex = newFormatIdx;
704         }
705
706         ++pos;
707         --length;
708     }
709
710     const int startPos = pos;
711     const int endPos = pos + length;
712
713     split(startPos);
714     split(endPos);
715
716     while (pos < endPos) {
717         FragmentMap::Iterator it = fragments.find(pos);
718         Q_ASSERT(!it.atEnd());
719
720         QTextFragmentData *fragment = it.value();
721
722         Q_ASSERT(formats.format(fragment->format).type() == QTextFormat::CharFormat);
723
724         int offset = pos - it.position();
725         int length = qMin(endPos - pos, int(fragment->size_array[0] - offset));
726         int oldFormat = fragment->format;
727
728         if (mode == MergeFormat) {
729             QTextFormat format = formats.format(fragment->format);
730             format.merge(newFormat);
731             fragment->format = formats.indexForFormat(format);
732         } else if (mode == SetFormatAndPreserveObjectIndices
733                    && formats.format(oldFormat).objectIndex() != -1) {
734             QTextCharFormat f = newFormat;
735             f.setObjectIndex(formats.format(oldFormat).objectIndex());
736             fragment->format = formats.indexForFormat(f);
737         } else {
738             fragment->format = newFormatIdx;
739         }
740
741         QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::CharFormatChanged, true, QTextUndoCommand::MoveCursor, oldFormat,
742                                 0, pos, length, 0);
743         appendUndoItem(c);
744
745         pos += length;
746         Q_ASSERT(pos == (int)(it.position() + fragment->size_array[0]) || pos >= endPos);
747     }
748
749     int n = fragments.findNode(startPos - 1);
750     if (n)
751         unite(n);
752
753     n = fragments.findNode(endPos);
754     if (n)
755         unite(n);
756
757     QTextBlock blockIt = blocksFind(startPos);
758     QTextBlock endIt = blocksFind(endPos);
759     if (endIt.isValid())
760         endIt = endIt.next();
761     for (; blockIt.isValid() && blockIt != endIt; blockIt = blockIt.next())
762         QTextDocumentPrivate::block(blockIt)->invalidate();
763
764     documentChange(startPos, length);
765
766     endEditBlock();
767 }
768
769 void QTextDocumentPrivate::setBlockFormat(const QTextBlock &from, const QTextBlock &to,
770                                      const QTextBlockFormat &newFormat, FormatChangeMode mode)
771 {
772     beginEditBlock();
773
774     Q_ASSERT(mode != SetFormatAndPreserveObjectIndices); // only implemented for setCharFormat
775
776     Q_ASSERT(newFormat.isValid());
777
778     int newFormatIdx = -1;
779     if (mode == SetFormat)
780         newFormatIdx = formats.indexForFormat(newFormat);
781     QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(newFormat));
782
783     QTextBlock it = from;
784     QTextBlock end = to;
785     if (end.isValid())
786         end = end.next();
787
788     for (; it != end; it = it.next()) {
789         int oldFormat = block(it)->format;
790         QTextBlockFormat format = formats.blockFormat(oldFormat);
791         QTextBlockGroup *oldGroup = qobject_cast<QTextBlockGroup *>(objectForFormat(format));
792         if (mode == MergeFormat) {
793             format.merge(newFormat);
794             newFormatIdx = formats.indexForFormat(format);
795             group = qobject_cast<QTextBlockGroup *>(objectForFormat(format));
796         }
797         block(it)->format = newFormatIdx;
798
799         block(it)->invalidate();
800
801         QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::BlockFormatChanged, true, QTextUndoCommand::MoveCursor, oldFormat,
802                                 0, it.position(), 1, 0);
803         appendUndoItem(c);
804
805         if (group != oldGroup) {
806             if (oldGroup)
807                 oldGroup->blockRemoved(it);
808             if (group)
809                 group->blockInserted(it);
810         } else if (group) {
811             group->blockFormatChanged(it);
812         }
813     }
814
815     documentChange(from.position(), to.position() + to.length() - from.position());
816
817     endEditBlock();
818 }
819
820
821 bool QTextDocumentPrivate::split(int pos)
822 {
823     uint x = fragments.findNode(pos);
824     if (x) {
825         int k = fragments.position(x);
826 //          qDebug("found fragment with key %d, size_left=%d, size=%d to split at %d",
827 //                k, (*it)->size_left[0], (*it)->size_array[0], pos);
828         if (k != pos) {
829             Q_ASSERT(k <= pos);
830             // need to resize the first fragment and add a new one
831             QTextFragmentData *X = fragments.fragment(x);
832             int oldsize = X->size_array[0];
833             fragments.setSize(x, pos-k);
834             uint n = fragments.insert_single(pos, oldsize-(pos-k));
835             X = fragments.fragment(x);
836             QTextFragmentData *N = fragments.fragment(n);
837             N->stringPosition = X->stringPosition + pos-k;
838             N->format = X->format;
839             return true;
840         }
841     }
842     return false;
843 }
844
845 bool QTextDocumentPrivate::unite(uint f)
846 {
847     uint n = fragments.next(f);
848     if (!n)
849         return false;
850
851     QTextFragmentData *ff = fragments.fragment(f);
852     QTextFragmentData *nf = fragments.fragment(n);
853
854     if (nf->format == ff->format && (ff->stringPosition + (int)ff->size_array[0] == nf->stringPosition)) {
855         if (isValidBlockSeparator(text.at(ff->stringPosition))
856             || isValidBlockSeparator(text.at(nf->stringPosition)))
857             return false;
858
859         fragments.setSize(f, ff->size_array[0] + nf->size_array[0]);
860         fragments.erase_single(n);
861         return true;
862     }
863     return false;
864 }
865
866
867 int QTextDocumentPrivate::undoRedo(bool undo)
868 {
869     PMDEBUG("%s, undoState=%d, undoStack size=%d", undo ? "undo:" : "redo:", undoState, undoStack.size());
870     if (!undoEnabled || (undo && undoState == 0) || (!undo && undoState == undoStack.size()))
871         return -1;
872
873     undoEnabled = false;
874     beginEditBlock();
875     while (1) {
876         if (undo)
877             --undoState;
878         QTextUndoCommand &c = undoStack[undoState];
879         int resetBlockRevision = c.pos;
880
881         switch(c.command) {
882         case QTextUndoCommand::Inserted:
883             remove(c.pos, c.length, (QTextUndoCommand::Operation)c.operation);
884             PMDEBUG("   erase: from %d, length %d", c.pos, c.length);
885             c.command = QTextUndoCommand::Removed;
886             break;
887         case QTextUndoCommand::Removed:
888             PMDEBUG("   insert: format %d (from %d, length %d, strpos=%d)", c.format, c.pos, c.length, c.strPos);
889             insert_string(c.pos, c.strPos, c.length, c.format, (QTextUndoCommand::Operation)c.operation);
890             c.command = QTextUndoCommand::Inserted;
891             break;
892         case QTextUndoCommand::BlockInserted:
893         case QTextUndoCommand::BlockAdded:
894             remove_block(c.pos, &c.blockFormat, c.command, (QTextUndoCommand::Operation)c.operation);
895             PMDEBUG("   blockremove: from %d", c.pos);
896             if (c.command == QTextUndoCommand::BlockInserted)
897                 c.command = QTextUndoCommand::BlockRemoved;
898             else
899                 c.command = QTextUndoCommand::BlockDeleted;
900             break;
901         case QTextUndoCommand::BlockRemoved:
902         case QTextUndoCommand::BlockDeleted:
903             PMDEBUG("   blockinsert: charformat %d blockformat %d (pos %d, strpos=%d)", c.format, c.blockFormat, c.pos, c.strPos);
904             insert_block(c.pos, c.strPos, c.format, c.blockFormat, (QTextUndoCommand::Operation)c.operation, c.command);
905             resetBlockRevision += 1;
906             if (c.command == QTextUndoCommand::BlockRemoved)
907                 c.command = QTextUndoCommand::BlockInserted;
908             else
909                 c.command = QTextUndoCommand::BlockAdded;
910             break;
911         case QTextUndoCommand::CharFormatChanged: {
912             resetBlockRevision = -1; // ## TODO
913             PMDEBUG("   charFormat: format %d (from %d, length %d)", c.format, c.pos, c.length);
914             FragmentIterator it = find(c.pos);
915             Q_ASSERT(!it.atEnd());
916
917             int oldFormat = it.value()->format;
918             setCharFormat(c.pos, c.length, formats.charFormat(c.format));
919             c.format = oldFormat;
920             break;
921         }
922         case QTextUndoCommand::BlockFormatChanged: {
923             resetBlockRevision = -1; // ## TODO
924             PMDEBUG("   blockformat: format %d pos %d", c.format, c.pos);
925             QTextBlock it = blocksFind(c.pos);
926             Q_ASSERT(it.isValid());
927
928             int oldFormat = block(it)->format;
929             block(it)->format = c.format;
930             QTextBlockGroup *oldGroup = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(oldFormat)));
931             QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(c.format)));
932             c.format = oldFormat;
933             if (group != oldGroup) {
934                 if (oldGroup)
935                     oldGroup->blockRemoved(it);
936                 if (group)
937                     group->blockInserted(it);
938             } else if (group) {
939                 group->blockFormatChanged(it);
940             }
941             documentChange(it.position(), it.length());
942             break;
943         }
944         case QTextUndoCommand::GroupFormatChange: {
945             resetBlockRevision = -1; // ## TODO
946             PMDEBUG("   group format change");
947             QTextObject *object = objectForIndex(c.objectIndex);
948             int oldFormat = formats.objectFormatIndex(c.objectIndex);
949             changeObjectFormat(object, c.format);
950             c.format = oldFormat;
951             break;
952         }
953         case QTextUndoCommand::Custom:
954             resetBlockRevision = -1; // ## TODO
955             if (undo)
956                 c.custom->undo();
957             else
958                 c.custom->redo();
959             break;
960         default:
961             Q_ASSERT(false);
962         }
963
964         if (resetBlockRevision >= 0) {
965             int b = blocks.findNode(resetBlockRevision);
966             QTextBlockData *B = blocks.fragment(b);
967             B->revision = c.revision;
968         }
969
970         if (!undo)
971             ++undoState;
972
973         bool inBlock = (
974                 undoState > 0
975                 && undoState < undoStack.size()
976                 && undoStack[undoState].block_part
977                 && undoStack[undoState-1].block_part
978                 && !undoStack[undoState-1].block_end
979                 );
980         if (!inBlock)
981             break;
982     }
983     undoEnabled = true;
984     int editPos = -1;
985     if (docChangeFrom >= 0) {
986         editPos = qMin(docChangeFrom + docChangeLength, length() - 1);
987     }
988     endEditBlock();
989     emitUndoAvailable(isUndoAvailable());
990     emitRedoAvailable(isRedoAvailable());
991     return editPos;
992 }
993
994 /*!
995     Appends a custom undo \a item to the undo stack.
996 */
997 void QTextDocumentPrivate::appendUndoItem(QAbstractUndoItem *item)
998 {
999     if (!undoEnabled) {
1000         delete item;
1001         return;
1002     }
1003
1004     QTextUndoCommand c;
1005     c.command = QTextUndoCommand::Custom;
1006     c.block_part = editBlock != 0;
1007     c.block_end = 0;
1008     c.operation = QTextUndoCommand::MoveCursor;
1009     c.format = 0;
1010     c.strPos = 0;
1011     c.pos = 0;
1012     c.blockFormat = 0;
1013
1014     c.custom = item;
1015     appendUndoItem(c);
1016 }
1017
1018 void QTextDocumentPrivate::appendUndoItem(const QTextUndoCommand &c)
1019 {
1020     PMDEBUG("appendUndoItem, command=%d enabled=%d", c.command, undoEnabled);
1021     if (!undoEnabled)
1022         return;
1023     if (undoState < undoStack.size())
1024         truncateUndoStack();
1025
1026     if (!undoStack.isEmpty() && modified) {
1027         QTextUndoCommand &last = undoStack[undoState - 1];
1028
1029         if ( (last.block_part && c.block_part && !last.block_end) // part of the same block => can merge
1030             || (!c.block_part && !last.block_part)) {  // two single undo items => can merge
1031
1032             if (last.tryMerge(c))
1033                 return;
1034         }
1035     }
1036     if (modifiedState > undoState)
1037         modifiedState = -1;
1038     undoStack.append(c);
1039     undoState++;
1040     emitUndoAvailable(true);
1041     emitRedoAvailable(false);
1042
1043     if (!c.block_part)
1044         emit document()->undoCommandAdded();
1045 }
1046
1047 void QTextDocumentPrivate::truncateUndoStack()
1048 {
1049     if (undoState == undoStack.size())
1050         return;
1051
1052     for (int i = undoState; i < undoStack.size(); ++i) {
1053         QTextUndoCommand c = undoStack[i];
1054         if (c.command & QTextUndoCommand::Removed) {
1055             // ########
1056 //             QTextFragment *f = c.fragment_list;
1057 //             while (f) {
1058 //                 QTextFragment *n = f->right;
1059 //                 delete f;
1060 //                 f = n;
1061 //             }
1062         } else if (c.command & QTextUndoCommand::Custom) {
1063             delete c.custom;
1064         }
1065     }
1066     undoStack.resize(undoState);
1067 }
1068
1069 void QTextDocumentPrivate::emitUndoAvailable(bool available)
1070 {
1071     if (available != wasUndoAvailable) {
1072         Q_Q(QTextDocument);
1073         emit q->undoAvailable(available);
1074         wasUndoAvailable = available;
1075     }
1076 }
1077
1078 void QTextDocumentPrivate::emitRedoAvailable(bool available)
1079 {
1080     if (available != wasRedoAvailable) {
1081         Q_Q(QTextDocument);
1082         emit q->redoAvailable(available);
1083         wasRedoAvailable = available;
1084     }
1085 }
1086
1087 void QTextDocumentPrivate::enableUndoRedo(bool enable)
1088 {
1089     if (enable && maximumBlockCount > 0)
1090         return;
1091
1092     if (!enable) {
1093         undoState = 0;
1094         truncateUndoStack();
1095         emitUndoAvailable(false);
1096         emitRedoAvailable(false);
1097     }
1098     modifiedState = modified ? -1 : undoState;
1099     undoEnabled = enable;
1100     if (!undoEnabled)
1101         compressPieceTable();
1102 }
1103
1104 void QTextDocumentPrivate::joinPreviousEditBlock()
1105 {
1106     beginEditBlock();
1107
1108     if (undoEnabled && undoState)
1109         undoStack[undoState - 1].block_end = false;
1110 }
1111
1112 void QTextDocumentPrivate::endEditBlock()
1113 {
1114     if (--editBlock)
1115         return;
1116
1117     if (undoEnabled && undoState > 0) {
1118         if (undoStack[undoState - 1].block_part) {
1119             undoStack[undoState - 1].block_end = true;
1120             emit document()->undoCommandAdded();
1121         }
1122     }
1123
1124     finishEdit();
1125 }
1126
1127 void QTextDocumentPrivate::finishEdit()
1128 {
1129     Q_Q(QTextDocument);
1130
1131     if (editBlock)
1132         return;
1133
1134     inEdit = false;
1135
1136     if (framesDirty)
1137         scan_frames(docChangeFrom, docChangeOldLength, docChangeLength);
1138
1139     if (lout && docChangeFrom >= 0) {
1140         if (!inContentsChange) {
1141             inContentsChange = true;
1142             emit q->contentsChange(docChangeFrom, docChangeOldLength, docChangeLength);
1143             inContentsChange = false;
1144         }
1145         lout->documentChanged(docChangeFrom, docChangeOldLength, docChangeLength);
1146     }
1147
1148     docChangeFrom = -1;
1149
1150     if (needsEnsureMaximumBlockCount) {
1151         needsEnsureMaximumBlockCount = false;
1152         if (ensureMaximumBlockCount()) {
1153             // if ensureMaximumBlockCount() returns true
1154             // it will have called endEditBlock() and
1155             // compressPieceTable() itself, so we return here
1156             // to prevent getting two contentsChanged emits
1157             return;
1158         }
1159     }
1160
1161     while (!changedCursors.isEmpty()) {
1162         QTextCursorPrivate *curs = changedCursors.takeFirst();
1163         emit q->cursorPositionChanged(QTextCursor(curs));
1164     }
1165
1166     contentsChanged();
1167
1168     if (blocks.numNodes() != lastBlockCount) {
1169         lastBlockCount = blocks.numNodes();
1170         emit q->blockCountChanged(lastBlockCount);
1171     }
1172
1173     if (!undoEnabled && unreachableCharacterCount)
1174         compressPieceTable();
1175 }
1176
1177 void QTextDocumentPrivate::documentChange(int from, int length)
1178 {
1179 //     qDebug("QTextDocumentPrivate::documentChange: from=%d,length=%d", from, length);
1180     if (docChangeFrom < 0) {
1181         docChangeFrom = from;
1182         docChangeOldLength = length;
1183         docChangeLength = length;
1184         return;
1185     }
1186     int start = qMin(from, docChangeFrom);
1187     int end = qMax(from + length, docChangeFrom + docChangeLength);
1188     int diff = qMax(0, end - start - docChangeLength);
1189     docChangeFrom = start;
1190     docChangeOldLength += diff;
1191     docChangeLength += diff;
1192 }
1193
1194 /*
1195     adjustDocumentChangesAndCursors is called whenever there is an insert or remove of characters.
1196     param from is the cursor position in the document
1197     param addedOrRemoved is the amount of characters added or removed.  A negative number means characters are removed.
1198 */
1199 void QTextDocumentPrivate::adjustDocumentChangesAndCursors(int from, int addedOrRemoved, QTextUndoCommand::Operation op)
1200 {
1201     Q_Q(QTextDocument);
1202     for (int i = 0; i < cursors.size(); ++i) {
1203         QTextCursorPrivate *curs = cursors.at(i);
1204         if (curs->adjustPosition(from, addedOrRemoved, op) == QTextCursorPrivate::CursorMoved) {
1205             if (editBlock || inEdit) {
1206                 if (!changedCursors.contains(curs))
1207                     changedCursors.append(curs);
1208             } else {
1209                 emit q->cursorPositionChanged(QTextCursor(curs));
1210             }
1211         }
1212     }
1213
1214 //     qDebug("QTextDocumentPrivate::adjustDocumentChanges: from=%d,addedOrRemoved=%d", from, addedOrRemoved);
1215     if (docChangeFrom < 0) {
1216         docChangeFrom = from;
1217         if (addedOrRemoved > 0) {
1218             docChangeOldLength = 0;
1219             docChangeLength = addedOrRemoved;
1220         } else {
1221             docChangeOldLength = -addedOrRemoved;
1222             docChangeLength = 0;
1223         }
1224 //         qDebug("adjustDocumentChanges:");
1225 //         qDebug("    -> %d %d %d", docChangeFrom, docChangeOldLength, docChangeLength);
1226         contentsChanged();
1227         return;
1228     }
1229
1230     // have to merge the new change with the already existing one.
1231     int added = qMax(0, addedOrRemoved);
1232     int removed = qMax(0, -addedOrRemoved);
1233
1234     int diff = 0;
1235     if(from + removed < docChangeFrom)
1236         diff = docChangeFrom - from - removed;
1237     else if(from > docChangeFrom + docChangeLength)
1238         diff = from - (docChangeFrom + docChangeLength);
1239
1240     int overlap_start = qMax(from, docChangeFrom);
1241     int overlap_end = qMin(from + removed, docChangeFrom + docChangeLength);
1242     int removedInside = qMax(0, overlap_end - overlap_start);
1243     removed -= removedInside;
1244
1245 //     qDebug("adjustDocumentChanges: from=%d, addedOrRemoved=%d, diff=%d, removedInside=%d", from, addedOrRemoved, diff, removedInside);
1246     docChangeFrom = qMin(docChangeFrom, from);
1247     docChangeOldLength += removed + diff;
1248     docChangeLength += added - removedInside + diff;
1249 //     qDebug("    -> %d %d %d", docChangeFrom, docChangeOldLength, docChangeLength);
1250
1251     contentsChanged();
1252 }
1253
1254
1255 QString QTextDocumentPrivate::plainText() const
1256 {
1257     QString result;
1258     result.resize(length());
1259     const QChar *text_unicode = text.unicode();
1260     QChar *data = result.data();
1261     for (QTextDocumentPrivate::FragmentIterator it = begin(); it != end(); ++it) {
1262         const QTextFragmentData *f = *it;
1263         ::memcpy(data, text_unicode + f->stringPosition, f->size_array[0] * sizeof(QChar));
1264         data += f->size_array[0];
1265     }
1266     // remove trailing block separator
1267     result.chop(1);
1268     return result;
1269 }
1270
1271 int QTextDocumentPrivate::blockCharFormatIndex(int node) const
1272 {
1273     int pos = blocks.position(node);
1274     if (pos == 0)
1275         return initialBlockCharFormatIndex;
1276
1277     return fragments.find(pos - 1)->format;
1278 }
1279
1280 int QTextDocumentPrivate::nextCursorPosition(int position, QTextLayout::CursorMode mode) const
1281 {
1282     if (position == length()-1)
1283         return position;
1284
1285     QTextBlock it = blocksFind(position);
1286     int start = it.position();
1287     int end = start + it.length() - 1;
1288     if (position == end)
1289         return end + 1;
1290
1291     return it.layout()->nextCursorPosition(position-start, mode) + start;
1292 }
1293
1294 int QTextDocumentPrivate::previousCursorPosition(int position, QTextLayout::CursorMode mode) const
1295 {
1296     if (position == 0)
1297         return position;
1298
1299     QTextBlock it = blocksFind(position);
1300     int start = it.position();
1301     if (position == start)
1302         return start - 1;
1303
1304     return it.layout()->previousCursorPosition(position-start, mode) + start;
1305 }
1306
1307 void QTextDocumentPrivate::changeObjectFormat(QTextObject *obj, int format)
1308 {
1309     beginEditBlock();
1310     int objectIndex = obj->objectIndex();
1311     int oldFormatIndex = formats.objectFormatIndex(objectIndex);
1312     formats.setObjectFormatIndex(objectIndex, format);
1313
1314     QTextBlockGroup *b = qobject_cast<QTextBlockGroup *>(obj);
1315     if (b) {
1316         b->d_func()->markBlocksDirty();
1317     }
1318     QTextFrame *f = qobject_cast<QTextFrame *>(obj);
1319     if (f)
1320         documentChange(f->firstPosition(), f->lastPosition() - f->firstPosition());
1321
1322     QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::GroupFormatChange, (editBlock != 0), QTextUndoCommand::MoveCursor, oldFormatIndex,
1323                             0, 0, obj->d_func()->objectIndex, 0);
1324     appendUndoItem(c);
1325
1326     endEditBlock();
1327 }
1328
1329 static QTextFrame *findChildFrame(QTextFrame *f, int pos)
1330 {
1331     // ##### use binary search
1332     QList<QTextFrame *> children = f->childFrames();
1333     for (int i = 0; i < children.size(); ++i) {
1334         QTextFrame *c = children.at(i);
1335         if (pos >= c->firstPosition() && pos <= c->lastPosition())
1336             return c;
1337     }
1338     return 0;
1339 }
1340
1341 QTextFrame *QTextDocumentPrivate::rootFrame() const
1342 {
1343     if (!rtFrame) {
1344         QTextFrameFormat defaultRootFrameFormat;
1345         defaultRootFrameFormat.setMargin(documentMargin);
1346         rtFrame = qobject_cast<QTextFrame *>(const_cast<QTextDocumentPrivate *>(this)->createObject(defaultRootFrameFormat));
1347     }
1348     return rtFrame;
1349 }
1350
1351 QTextFrame *QTextDocumentPrivate::frameAt(int pos) const
1352 {
1353     QTextFrame *f = rootFrame();
1354
1355     while (1) {
1356         QTextFrame *c = findChildFrame(f, pos);
1357         if (!c)
1358             return f;
1359         f = c;
1360     }
1361 }
1362
1363 void QTextDocumentPrivate::clearFrame(QTextFrame *f)
1364 {
1365     for (int i = 0; i < f->d_func()->childFrames.count(); ++i)
1366         clearFrame(f->d_func()->childFrames.at(i));
1367     f->d_func()->childFrames.clear();
1368     f->d_func()->parentFrame = 0;
1369 }
1370
1371 void QTextDocumentPrivate::scan_frames(int pos, int charsRemoved, int charsAdded)
1372 {
1373     // ###### optimise
1374     Q_UNUSED(pos);
1375     Q_UNUSED(charsRemoved);
1376     Q_UNUSED(charsAdded);
1377
1378     QTextFrame *f = rootFrame();
1379     clearFrame(f);
1380
1381     for (FragmentIterator it = begin(); it != end(); ++it) {
1382         // QTextFormat fmt = formats.format(it->format);
1383         QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(it->format));
1384         if (!frame)
1385             continue;
1386
1387         Q_ASSERT(it.size() == 1);
1388         QChar ch = text.at(it->stringPosition);
1389
1390         if (ch == QTextBeginningOfFrame) {
1391             if (f != frame) {
1392                 // f == frame happens for tables
1393                 Q_ASSERT(frame->d_func()->fragment_start == it.n || frame->d_func()->fragment_start == 0);
1394                 frame->d_func()->parentFrame = f;
1395                 f->d_func()->childFrames.append(frame);
1396                 f = frame;
1397             }
1398         } else if (ch == QTextEndOfFrame) {
1399             Q_ASSERT(f == frame);
1400             Q_ASSERT(frame->d_func()->fragment_end == it.n || frame->d_func()->fragment_end == 0);
1401             f = frame->d_func()->parentFrame;
1402         } else if (ch == QChar::ObjectReplacementCharacter) {
1403             Q_ASSERT(f != frame);
1404             Q_ASSERT(frame->d_func()->fragment_start == it.n || frame->d_func()->fragment_start == 0);
1405             Q_ASSERT(frame->d_func()->fragment_end == it.n || frame->d_func()->fragment_end == 0);
1406             frame->d_func()->parentFrame = f;
1407             f->d_func()->childFrames.append(frame);
1408         } else {
1409             Q_ASSERT(false);
1410         }
1411     }
1412     Q_ASSERT(f == rtFrame);
1413     framesDirty = false;
1414 }
1415
1416 void QTextDocumentPrivate::insert_frame(QTextFrame *f)
1417 {
1418     int start = f->firstPosition();
1419     int end = f->lastPosition();
1420     QTextFrame *parent = frameAt(start-1);
1421     Q_ASSERT(parent == frameAt(end+1));
1422
1423     if (start != end) {
1424         // iterator over the parent and move all children contained in my frame to myself
1425         for (int i = 0; i < parent->d_func()->childFrames.size(); ++i) {
1426             QTextFrame *c = parent->d_func()->childFrames.at(i);
1427             if (start < c->firstPosition() && end > c->lastPosition()) {
1428                 parent->d_func()->childFrames.removeAt(i);
1429                 f->d_func()->childFrames.append(c);
1430                 c->d_func()->parentFrame = f;
1431             }
1432         }
1433     }
1434     // insert at the correct position
1435     int i = 0;
1436     for (; i < parent->d_func()->childFrames.size(); ++i) {
1437         QTextFrame *c = parent->d_func()->childFrames.at(i);
1438         if (c->firstPosition() > end)
1439             break;
1440     }
1441     parent->d_func()->childFrames.insert(i, f);
1442     f->d_func()->parentFrame = parent;
1443 }
1444
1445 QTextFrame *QTextDocumentPrivate::insertFrame(int start, int end, const QTextFrameFormat &format)
1446 {
1447     Q_ASSERT(start >= 0 && start < length());
1448     Q_ASSERT(end >= 0 && end < length());
1449     Q_ASSERT(start <= end || end == -1);
1450
1451     if (start != end && frameAt(start) != frameAt(end))
1452         return 0;
1453
1454     beginEditBlock();
1455
1456     QTextFrame *frame = qobject_cast<QTextFrame *>(createObject(format));
1457     Q_ASSERT(frame);
1458
1459     // #### using the default block and char format below might be wrong
1460     int idx = formats.indexForFormat(QTextBlockFormat());
1461     QTextCharFormat cfmt;
1462     cfmt.setObjectIndex(frame->objectIndex());
1463     int charIdx = formats.indexForFormat(cfmt);
1464
1465     insertBlock(QTextBeginningOfFrame, start, idx, charIdx, QTextUndoCommand::MoveCursor);
1466     insertBlock(QTextEndOfFrame, ++end, idx, charIdx, QTextUndoCommand::KeepCursor);
1467
1468     frame->d_func()->fragment_start = find(start).n;
1469     frame->d_func()->fragment_end = find(end).n;
1470
1471     insert_frame(frame);
1472
1473     endEditBlock();
1474
1475     return frame;
1476 }
1477
1478 void QTextDocumentPrivate::removeFrame(QTextFrame *frame)
1479 {
1480     QTextFrame *parent = frame->d_func()->parentFrame;
1481     if (!parent)
1482         return;
1483
1484     int start = frame->firstPosition();
1485     int end = frame->lastPosition();
1486     Q_ASSERT(end >= start);
1487
1488     beginEditBlock();
1489
1490     // remove already removes the frames from the tree
1491     remove(end, 1);
1492     remove(start-1, 1);
1493
1494     endEditBlock();
1495 }
1496
1497 QTextObject *QTextDocumentPrivate::objectForIndex(int objectIndex) const
1498 {
1499     if (objectIndex < 0)
1500         return 0;
1501
1502     QTextObject *object = objects.value(objectIndex, 0);
1503     if (!object) {
1504         QTextDocumentPrivate *that = const_cast<QTextDocumentPrivate *>(this);
1505         QTextFormat fmt = formats.objectFormat(objectIndex);
1506         object = that->createObject(fmt, objectIndex);
1507     }
1508     return object;
1509 }
1510
1511 QTextObject *QTextDocumentPrivate::objectForFormat(int formatIndex) const
1512 {
1513     int objectIndex = formats.format(formatIndex).objectIndex();
1514     return objectForIndex(objectIndex);
1515 }
1516
1517 QTextObject *QTextDocumentPrivate::objectForFormat(const QTextFormat &f) const
1518 {
1519     return objectForIndex(f.objectIndex());
1520 }
1521
1522 QTextObject *QTextDocumentPrivate::createObject(const QTextFormat &f, int objectIndex)
1523 {
1524     QTextObject *obj = document()->createObject(f);
1525
1526     if (obj) {
1527         obj->d_func()->objectIndex = objectIndex == -1 ? formats.createObjectIndex(f) : objectIndex;
1528         objects[obj->d_func()->objectIndex] = obj;
1529     }
1530
1531     return obj;
1532 }
1533
1534 void QTextDocumentPrivate::deleteObject(QTextObject *object)
1535 {
1536     const int objIdx = object->d_func()->objectIndex;
1537     objects.remove(objIdx);
1538     delete object;
1539 }
1540
1541 void QTextDocumentPrivate::contentsChanged()
1542 {
1543     Q_Q(QTextDocument);
1544     if (editBlock || inEdit)
1545         return;
1546
1547     bool m = undoEnabled ? (modifiedState != undoState) : true;
1548     if (modified != m) {
1549         modified = m;
1550         emit q->modificationChanged(modified);
1551     }
1552
1553     emit q->contentsChanged();
1554 }
1555
1556 void QTextDocumentPrivate::compressPieceTable()
1557 {
1558     if (undoEnabled)
1559         return;
1560
1561     const uint garbageCollectionThreshold = 96 * 1024; // bytes
1562
1563     //qDebug() << "unreachable bytes:" << unreachableCharacterCount * sizeof(QChar) << " -- limit" << garbageCollectionThreshold << "text size =" << text.size() << "capacity:" << text.capacity();
1564
1565     bool compressTable = unreachableCharacterCount * sizeof(QChar) > garbageCollectionThreshold
1566                          && text.size() >= text.capacity() * 0.9;
1567     if (!compressTable)
1568         return;
1569
1570     QString newText;
1571     newText.resize(text.size());
1572     QChar *newTextPtr = newText.data();
1573     int newLen = 0;
1574
1575     for (FragmentMap::Iterator it = fragments.begin(); !it.atEnd(); ++it) {
1576         qMemCopy(newTextPtr, text.constData() + it->stringPosition, it->size_array[0] * sizeof(QChar));
1577         it->stringPosition = newLen;
1578         newTextPtr += it->size_array[0];
1579         newLen += it->size_array[0];
1580     }
1581
1582     newText.resize(newLen);
1583     newText.squeeze();
1584     //qDebug() << "removed" << text.size() - newText.size() << "characters";
1585     text = newText;
1586     unreachableCharacterCount = 0;
1587 }
1588
1589 void QTextDocumentPrivate::setModified(bool m)
1590 {
1591     Q_Q(QTextDocument);
1592     if (m == modified)
1593         return;
1594
1595     modified = m;
1596     if (!modified)
1597         modifiedState = undoState;
1598     else
1599         modifiedState = -1;
1600
1601     emit q->modificationChanged(modified);
1602 }
1603
1604 bool QTextDocumentPrivate::ensureMaximumBlockCount()
1605 {
1606     if (maximumBlockCount <= 0)
1607         return false;
1608     if (blocks.numNodes() <= maximumBlockCount)
1609         return false;
1610
1611     beginEditBlock();
1612
1613     const int blocksToRemove = blocks.numNodes() - maximumBlockCount;
1614     QTextCursor cursor(this, 0);
1615     cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor, blocksToRemove);
1616
1617     unreachableCharacterCount += cursor.selectionEnd() - cursor.selectionStart();
1618
1619     // preserve the char format of the paragraph that is to become the new first one
1620     QTextCharFormat charFmt = cursor.blockCharFormat();
1621     cursor.removeSelectedText();
1622     cursor.setBlockCharFormat(charFmt);
1623
1624     endEditBlock();
1625
1626     compressPieceTable();
1627
1628     return true;
1629 }
1630
1631 /// This method is called from QTextTable when it is about to remove a table-cell to allow cursors to update their selection.
1632 void QTextDocumentPrivate::aboutToRemoveCell(int from, int to)
1633 {
1634     Q_ASSERT(from <= to);
1635     for (int i = 0; i < cursors.size(); ++i)
1636         cursors.at(i)->aboutToRemoveCell(from, to);
1637 }
1638
1639 QT_END_NAMESPACE