~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/gui/text/qtextdocument_p.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
 
4
**
 
5
** This file is part of the text module of the Qt Toolkit.
 
6
**
 
7
** This file may be distributed under the terms of the Q Public License
 
8
** as defined by Trolltech AS of Norway and appearing in the file
 
9
** LICENSE.QPL included in the packaging of this file.
 
10
**
 
11
** This file may be distributed and/or modified under the terms of the
 
12
** GNU General Public License version 2 as published by the Free Software
 
13
** Foundation and appearing in the file LICENSE.GPL included in the
 
14
** packaging of this file.
 
15
**
 
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
 
17
**   information about Qt Commercial License Agreements.
 
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
 
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
 
20
**
 
21
** Contact info@trolltech.com if any conditions of this licensing are
 
22
** not clear to you.
 
23
**
 
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
26
**
 
27
****************************************************************************/
 
28
 
 
29
#include <private/qtools_p.h>
 
30
#include <qdebug.h>
 
31
 
 
32
#include "qtextdocument_p.h"
 
33
#include "qtextdocument.h"
 
34
#include <qtextformat.h>
 
35
#include "qtextformat_p.h"
 
36
#include "qtextobject_p.h"
 
37
#include "qtextcursor.h"
 
38
#include "qtextimagehandler_p.h"
 
39
#include "qtextcursor_p.h"
 
40
#include "qtextdocumentlayout_p.h"
 
41
#include "qtexttable.h"
 
42
#include "qtextengine_p.h"
 
43
 
 
44
#include <stdlib.h>
 
45
#include <new>
 
46
 
 
47
#define PMDEBUG if(0) qDebug
 
48
 
 
49
/*
 
50
  Structure of a document:
 
51
 
 
52
  DOCUMENT :== FRAME_CONTENTS
 
53
  FRAME :== START_OF_FRAME  FRAME_CONTENTS END_OF_FRAME
 
54
  FRAME_CONTENTS = LIST_OF_BLOCKS ((FRAME | TABLE) LIST_OF_BLOCKS)*
 
55
  TABLE :== (START_OF_FRAME TABLE_CELL)+ END_OF_FRAME
 
56
  TABLE_CELL = FRAME_CONTENTS
 
57
  LIST_OF_BLOCKS :== (BLOCK END_OF_PARA)* BLOCK
 
58
  BLOCK :== (FRAGMENT)*
 
59
  FRAGMENT :== String of characters
 
60
 
 
61
  END_OF_PARA :== 0x2029 # Paragraph separator in Unicode
 
62
  START_OF_FRAME :== 0xfdd0
 
63
  END_OF_FRAME := 0xfdd1
 
64
 
 
65
  Note also that LIST_OF_BLOCKS can be empty. Nevertheless, there is
 
66
  at least one valid cursor position there where you could start
 
67
  typing. The block format is in this case determined by the last
 
68
  END_OF_PARA/START_OF_FRAME/END_OF_FRAME (see below).
 
69
 
 
70
  Lists are not in here, as they are treated specially. A list is just
 
71
  a collection of (not neccessarily connected) blocks, that share the
 
72
  same objectIndex() in the format that refers to the list format and
 
73
  object.
 
74
 
 
75
  The above does not clearly note where formats are. Here's
 
76
  how it looks currently:
 
77
 
 
78
  FRAGMENT: one charFormat associated
 
79
 
 
80
  END_OF_PARA: one charFormat, and a blockFormat for the _next_ block.
 
81
 
 
82
  START_OF_FRAME: one char format, and a blockFormat (for the next
 
83
  block). The format associated with the objectIndex() of the
 
84
  charFormat decides whether this is a frame or table and it's
 
85
  properties
 
86
 
 
87
  END_OF_FRAME: one charFormat and a blockFormat (for the next
 
88
  block). The object() of the charFormat is the same as for the
 
89
  corresponding START_OF_BLOCK.
 
90
 
 
91
 
 
92
  The document is independent of the layout with certain restrictions:
 
93
 
 
94
  * Cursor movement (esp. up and down) depend on the layout.
 
95
  * You cannot have more than one layout, as the layout data of QTextObjects
 
96
    is stored in the text object itself.
 
97
 
 
98
*/
 
99
 
 
100
void QTextBlockData::invalidate() const
 
101
{
 
102
    if (layout)
 
103
        layout->engine()->invalidate();
 
104
}
 
105
 
 
106
static bool isValidBlockSeparator(const QChar &ch)
 
107
{
 
108
    return ch == QChar::ParagraphSeparator
 
109
        || ch == QTextBeginningOfFrame
 
110
        || ch == QTextEndOfFrame;
 
111
}
 
112
 
 
113
#ifndef QT_NO_DEBUG
 
114
static bool noBlockInString(const QString &str)
 
115
{
 
116
    return !str.contains(QChar::ParagraphSeparator)
 
117
        && !str.contains(QTextBeginningOfFrame)
 
118
        && !str.contains(QTextEndOfFrame);
 
119
}
 
120
#endif
 
121
 
 
122
bool QTextUndoCommand::tryMerge(const QTextUndoCommand &other)
 
123
{
 
124
    if (command != other.command)
 
125
        return false;
 
126
 
 
127
    if (command == Inserted
 
128
        && (pos + length == other.pos)
 
129
        && (strPos + length == other.strPos)
 
130
        && format == other.format) {
 
131
 
 
132
        length += other.length;
 
133
        return true;
 
134
    }
 
135
 
 
136
    // removal to the 'right' using 'Delete' key
 
137
    if (command == Removed
 
138
        && pos == other.pos
 
139
        && (strPos + length == other.strPos)
 
140
        && format == other.format) {
 
141
 
 
142
        length += other.length;
 
143
        return true;
 
144
    }
 
145
 
 
146
    // removal to the 'left' using 'Backspace'
 
147
    if (command == Removed
 
148
        && (other.pos + other.length == pos)
 
149
        && (other.strPos + other.length == strPos)
 
150
        && (format == other.format)) {
 
151
 
 
152
        int l = length;
 
153
        (*this) = other;
 
154
 
 
155
        length += l;
 
156
        return true;
 
157
    }
 
158
 
 
159
    return false;
 
160
}
 
161
 
 
162
QTextDocumentPrivate::QTextDocumentPrivate()
 
163
{
 
164
    editBlock = 0;
 
165
    docChangeFrom = -1;
 
166
 
 
167
    undoState = 0;
 
168
 
 
169
    lout = 0;
 
170
 
 
171
    modified = false;
 
172
    modifiedState = 0;
 
173
 
 
174
    undoEnabled = true;
 
175
}
 
176
 
 
177
void QTextDocumentPrivate::init()
 
178
{
 
179
    frame = qobject_cast<QTextFrame *>(createObject(QTextFrameFormat()));
 
180
    framesDirty = false;
 
181
 
 
182
    bool undoState = undoEnabled; 
 
183
    undoEnabled = false;
 
184
    insertBlock(0, formats.indexForFormat(QTextBlockFormat()), formats.indexForFormat(QTextCharFormat()));
 
185
    undoEnabled = undoState;
 
186
    modified = false;
 
187
    modifiedState = 0;
 
188
}
 
189
 
 
190
void QTextDocumentPrivate::clear()
 
191
{
 
192
    Q_Q(QTextDocument);
 
193
    for (int i = 0; i < cursors.count(); ++i) {
 
194
        cursors.at(i)->setPosition(0);
 
195
        cursors.at(i)->anchor = 0;
 
196
    }
 
197
 
 
198
    QList<QTextCursorPrivate *>oldCursors = cursors;
 
199
    cursors.clear();
 
200
    changedCursors.clear();
 
201
    objects.clear();
 
202
    docConfig = QTextDocumentConfig();
 
203
    undoState = 0;
 
204
    truncateUndoStack();
 
205
    text = QString();
 
206
    modifiedState = 0;
 
207
    modified = false;
 
208
    formats = QTextFormatCollection();
 
209
    int len = fragments.length();
 
210
    fragments.clear();
 
211
    blocks.clear();
 
212
    resources.clear();
 
213
    q->contentsChange(0, len, 0);
 
214
    lout->documentChanged(0, len, 0);
 
215
    delete frame;
 
216
    init();
 
217
    cursors = oldCursors;
 
218
}
 
219
 
 
220
QTextDocumentPrivate::~QTextDocumentPrivate()
 
221
{
 
222
    for (int i = 0; i < cursors.count(); ++i)
 
223
        cursors.at(i)->priv = 0;
 
224
    cursors.clear();
 
225
    undoState = 0;
 
226
    undoEnabled = true;
 
227
    truncateUndoStack();
 
228
}
 
229
 
 
230
void QTextDocumentPrivate::setLayout(QAbstractTextDocumentLayout *layout)
 
231
{
 
232
    Q_Q(QTextDocument);
 
233
    if (lout)
 
234
        delete lout;
 
235
    lout = layout;
 
236
    emit q->contentsChange(0, 0, length());
 
237
    lout->documentChanged(0, 0, length());
 
238
}
 
239
 
 
240
 
 
241
void QTextDocumentPrivate::insert_string(int pos, uint strPos, uint length, int format, QTextUndoCommand::Operation op)
 
242
{
 
243
    // ##### optimise when only appending to the fragment!
 
244
    Q_ASSERT(noBlockInString(text.mid(strPos, length)));
 
245
 
 
246
    split(pos);
 
247
    uint x = fragments.insert_single(pos, length);
 
248
    QTextFragmentData *X = fragments.fragment(x);
 
249
    X->format = format;
 
250
    X->stringPosition = strPos;
 
251
    uint w = fragments.previous(x);
 
252
    if (w)
 
253
        unite(w);
 
254
 
 
255
    int b = blocks.findNode(pos);
 
256
    blocks.setSize(b, blocks.size(b)+length);
 
257
 
 
258
    Q_ASSERT(blocks.length() == fragments.length());
 
259
 
 
260
    QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(format));
 
261
    if (frame) {
 
262
        frame->d_func()->fragmentAdded(text.at(strPos), x);
 
263
        framesDirty = true;
 
264
    }
 
265
 
 
266
    adjustDocumentChangesAndCursors(pos, length, op);
 
267
}
 
268
 
 
269
void QTextDocumentPrivate::insert_block(int pos, uint strPos, int format, int blockFormat, QTextUndoCommand::Operation op, int command)
 
270
{
 
271
    split(pos);
 
272
    uint x = fragments.insert_single(pos, 1);
 
273
    QTextFragmentData *X = fragments.fragment(x);
 
274
    X->format = format;
 
275
    X->stringPosition = strPos;
 
276
    // no need trying to unite, since paragraph separators are always in a fragment of their own
 
277
 
 
278
    Q_ASSERT(isValidBlockSeparator(text.at(strPos)));
 
279
    Q_ASSERT(blocks.length()+1 == fragments.length());
 
280
 
 
281
    int block_pos = pos;
 
282
    if (blocks.length() && command == QTextUndoCommand::BlockRemoved)
 
283
        ++block_pos;
 
284
    int size = 1;
 
285
    int n = blocks.findNode(block_pos);
 
286
    int key = n ? blocks.position(n) : blocks.length();
 
287
 
 
288
    Q_ASSERT(n || (!n && block_pos == blocks.length()));
 
289
    if (key != block_pos) {
 
290
        Q_ASSERT(key < block_pos);
 
291
        int oldSize = blocks.size(n);
 
292
        blocks.setSize(n, block_pos-key);
 
293
        size += oldSize - (block_pos-key);
 
294
    }
 
295
    int b = blocks.insert_single(block_pos, size);
 
296
    QTextBlockData *B = blocks.fragment(b);
 
297
    B->format = blockFormat;
 
298
 
 
299
    Q_ASSERT(blocks.length() == fragments.length());
 
300
 
 
301
    QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blockFormat));
 
302
    if (group)
 
303
        group->blockInserted(QTextBlock(this, b));
 
304
 
 
305
    QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(formats.format(format)));
 
306
    if (frame) {
 
307
        frame->d_func()->fragmentAdded(text.at(strPos), x);
 
308
        framesDirty = true;
 
309
    }
 
310
 
 
311
    adjustDocumentChangesAndCursors(pos, 1, op);
 
312
}
 
313
 
 
314
void QTextDocumentPrivate::insertBlock(const QChar &blockSeparator,
 
315
                                  int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation op)
 
316
{
 
317
    Q_ASSERT(formats.format(blockFormat).isBlockFormat());
 
318
    Q_ASSERT(formats.format(charFormat).isCharFormat());
 
319
    Q_ASSERT(pos >= 0 && (pos < fragments.length() || (pos == 0 && fragments.length() == 0)));
 
320
    Q_ASSERT(isValidBlockSeparator(blockSeparator));
 
321
 
 
322
    beginEditBlock();
 
323
 
 
324
    int strPos = text.length();
 
325
    text.append(blockSeparator);
 
326
    insert_block(pos, strPos, charFormat, blockFormat, op, QTextUndoCommand::BlockRemoved);
 
327
 
 
328
    Q_ASSERT(blocks.length() == fragments.length());
 
329
 
 
330
    QTextUndoCommand c = { QTextUndoCommand::BlockInserted, true,
 
331
                      op, charFormat, strPos, pos, { blockFormat } };
 
332
 
 
333
    appendUndoItem(c);
 
334
    Q_ASSERT(undoState == undoStack.size());
 
335
 
 
336
    endEditBlock();
 
337
}
 
338
 
 
339
void QTextDocumentPrivate::insertBlock(int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation op)
 
340
{
 
341
    insertBlock(QChar::ParagraphSeparator, pos, blockFormat, charFormat, op);
 
342
}
 
343
 
 
344
void QTextDocumentPrivate::insert(int pos, int strPos, int strLength, int format)
 
345
{
 
346
    if (strLength <= 0)
 
347
        return;
 
348
 
 
349
    Q_ASSERT(pos >= 0 && pos < fragments.length());
 
350
    Q_ASSERT(formats.format(format).isCharFormat());
 
351
 
 
352
    insert_string(pos, strPos, strLength, format, QTextUndoCommand::MoveCursor);
 
353
 
 
354
    beginEditBlock();
 
355
 
 
356
    QTextUndoCommand c = { QTextUndoCommand::Inserted, true,
 
357
                      QTextUndoCommand::MoveCursor, format, strPos, pos, { strLength } };
 
358
    appendUndoItem(c);
 
359
    Q_ASSERT(undoState == undoStack.size());
 
360
 
 
361
    endEditBlock();
 
362
}
 
363
 
 
364
void QTextDocumentPrivate::insert(int pos, const QString &str, int format)
 
365
{
 
366
    if (str.size() == 0)
 
367
        return;
 
368
 
 
369
    Q_ASSERT(noBlockInString(str));
 
370
 
 
371
    int strPos = text.length();
 
372
    text.append(str);
 
373
    insert(pos, strPos, str.length(), format);
 
374
}
 
375
 
 
376
int QTextDocumentPrivate::remove_string(int pos, uint length, QTextUndoCommand::Operation op)
 
377
{
 
378
    Q_ASSERT(pos >= 0);
 
379
    Q_ASSERT(blocks.length() == fragments.length());
 
380
    Q_ASSERT(blocks.length() >= pos+(int)length);
 
381
 
 
382
    int b = blocks.findNode(pos);
 
383
    uint x = fragments.findNode(pos);
 
384
 
 
385
    Q_ASSERT(blocks.size(b) > length);
 
386
    Q_ASSERT(x && fragments.position(x) == (uint)pos && fragments.size(x) == length);
 
387
    Q_ASSERT(noBlockInString(text.mid(fragments.fragment(x)->stringPosition, length)));
 
388
 
 
389
    blocks.setSize(b, blocks.size(b)-length);
 
390
 
 
391
    QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(fragments.fragment(x)->format));
 
392
    if (frame) {
 
393
        frame->d_func()->fragmentRemoved(text.at(fragments.fragment(x)->stringPosition), x);
 
394
        framesDirty = true;
 
395
    }
 
396
 
 
397
    const int w = fragments.erase_single(x);
 
398
 
 
399
    adjustDocumentChangesAndCursors(pos, -int(length), op);
 
400
 
 
401
    return w;
 
402
}
 
403
 
 
404
int QTextDocumentPrivate::remove_block(int pos, int *blockFormat, int command, QTextUndoCommand::Operation op)
 
405
{
 
406
    Q_ASSERT(pos >= 0);
 
407
    Q_ASSERT(blocks.length() == fragments.length());
 
408
    Q_ASSERT(blocks.length() > pos);
 
409
 
 
410
    int b = blocks.findNode(pos);
 
411
    uint x = fragments.findNode(pos);
 
412
 
 
413
    Q_ASSERT(x && (int)fragments.position(x) == pos);
 
414
    Q_ASSERT(fragments.size(x) == 1);
 
415
    Q_ASSERT(isValidBlockSeparator(text.at(fragments.fragment(x)->stringPosition)));
 
416
    Q_ASSERT(b);
 
417
 
 
418
    if (blocks.size(b) == 1 && command == QTextUndoCommand::BlockAdded) {
 
419
        Q_ASSERT((int)blocks.position(b) == pos);
 
420
//      qDebug("removing empty block");
 
421
        // empty block remove the block itself
 
422
    } else {
 
423
        // non empty block, merge with next one into this block
 
424
//      qDebug("merging block with next");
 
425
        int n = blocks.next(b);
 
426
        Q_ASSERT((int)blocks.position(n) == pos + 1);
 
427
        blocks.setSize(b, blocks.size(b) + blocks.size(n) - 1);
 
428
        b = n;
 
429
    }
 
430
    *blockFormat = blocks.fragment(b)->format;
 
431
 
 
432
    QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blocks.fragment(b)->format));
 
433
    if (group)
 
434
        group->blockRemoved(QTextBlock(this, b));
 
435
 
 
436
    QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(fragments.fragment(x)->format));
 
437
    if (frame) {
 
438
        frame->d_func()->fragmentRemoved(text.at(fragments.fragment(x)->stringPosition), x);
 
439
        framesDirty = true;
 
440
    }
 
441
 
 
442
    blocks.erase_single(b);
 
443
    const int w = fragments.erase_single(x);
 
444
 
 
445
    adjustDocumentChangesAndCursors(pos, -1, op);
 
446
 
 
447
    return w;
 
448
}
 
449
 
 
450
#if !defined(QT_NO_DEBUG)
 
451
static bool isAncestorFrame(QTextFrame *possibleAncestor, QTextFrame *child)
 
452
{
 
453
    while (child) {
 
454
        if (child == possibleAncestor)
 
455
            return true;
 
456
        child = child->parentFrame();
 
457
    }
 
458
    return false;
 
459
}
 
460
#endif
 
461
 
 
462
void QTextDocumentPrivate::remove(int pos, int length, QTextUndoCommand::Operation op)
 
463
{
 
464
    Q_ASSERT(pos >= 0 && pos+length <= fragments.length());
 
465
    Q_ASSERT(blocks.length() == fragments.length());
 
466
 
 
467
#if !defined(QT_NO_DEBUG)
 
468
    const bool startAndEndInSameFrame = (frameAt(pos) == frameAt(pos + length - 1));
 
469
 
 
470
    const bool endIsEndOfChildFrame = (isAncestorFrame(frameAt(pos), frameAt(pos + length - 1))
 
471
                                       && text.at(find(pos + length - 1)->stringPosition) == QTextEndOfFrame);
 
472
 
 
473
    const bool startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent
 
474
               = (text.at(find(pos)->stringPosition) == QTextBeginningOfFrame
 
475
                  && text.at(find(pos + length - 1)->stringPosition) == QTextEndOfFrame
 
476
                  && frameAt(pos)->parentFrame() == frameAt(pos + length - 1)->parentFrame());
 
477
 
 
478
    Q_ASSERT(startAndEndInSameFrame || endIsEndOfChildFrame || startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent);
 
479
#endif
 
480
 
 
481
    beginEditBlock();
 
482
 
 
483
    split(pos);
 
484
    split(pos+length);
 
485
 
 
486
    uint x = fragments.findNode(pos);
 
487
    uint end = fragments.findNode(pos+length);
 
488
 
 
489
    uint w = 0;
 
490
    while (x != end) {
 
491
        uint n = fragments.next(x);
 
492
 
 
493
        uint key = fragments.position(x);
 
494
        uint b = blocks.findNode(key+1);
 
495
 
 
496
        QTextFragmentData *X = fragments.fragment(x);
 
497
        QTextUndoCommand c = { QTextUndoCommand::Removed, true,
 
498
                          op, X->format, X->stringPosition, key, { X->size } };
 
499
 
 
500
        if (key+1 != blocks.position(b)) {
 
501
//          qDebug("remove_string from %d length %d", key, X->size);
 
502
            Q_ASSERT(noBlockInString(text.mid(X->stringPosition, X->size)));
 
503
            w = remove_string(key, X->size, op);
 
504
        } else {
 
505
//          qDebug("remove_block at %d", key);
 
506
            Q_ASSERT(X->size == 1 && isValidBlockSeparator(text.at(X->stringPosition)));
 
507
            b = blocks.previous(b);
 
508
            c.command = blocks.size(b) == 1 ? QTextUndoCommand::BlockDeleted : QTextUndoCommand::BlockRemoved;
 
509
            w = remove_block(key, &c.blockFormat, QTextUndoCommand::BlockAdded, op);
 
510
        }
 
511
        appendUndoItem(c);
 
512
        x = n;
 
513
 
 
514
    }
 
515
    if (w)
 
516
        unite(w);
 
517
 
 
518
    Q_ASSERT(blocks.length() == fragments.length());
 
519
 
 
520
    endEditBlock();
 
521
}
 
522
 
 
523
void QTextDocumentPrivate::setCharFormat(int pos, int length, const QTextCharFormat &newFormat, FormatChangeMode mode)
 
524
{
 
525
    Q_ASSERT(newFormat.isValid());
 
526
 
 
527
    beginEditBlock();
 
528
 
 
529
    int newFormatIdx = -1;
 
530
    if (mode == SetFormat)
 
531
        newFormatIdx = formats.indexForFormat(newFormat);
 
532
 
 
533
    const int startPos = pos;
 
534
    const int endPos = pos + length;
 
535
 
 
536
    split(startPos);
 
537
    split(endPos);
 
538
 
 
539
    while (pos < endPos) {
 
540
        FragmentMap::Iterator it = fragments.find(pos);
 
541
        Q_ASSERT(!it.atEnd());
 
542
 
 
543
        QTextFragmentData *fragment = it.value();
 
544
 
 
545
        Q_ASSERT(formats.format(fragment->format).type() == QTextFormat::CharFormat);
 
546
 
 
547
        int offset = pos - it.position();
 
548
        int length = qMin(endPos - pos, int(fragment->size - offset));
 
549
        int oldFormat = fragment->format;
 
550
 
 
551
        if (mode == MergeFormat) {
 
552
            QTextFormat format = formats.format(fragment->format);
 
553
            format.merge(newFormat);
 
554
            fragment->format = formats.indexForFormat(format);
 
555
        } else {
 
556
            fragment->format = newFormatIdx;
 
557
        }
 
558
 
 
559
        QTextUndoCommand c = { QTextUndoCommand::CharFormatChanged, true, QTextUndoCommand::MoveCursor, oldFormat,
 
560
                          0, pos, { length } };
 
561
        appendUndoItem(c);
 
562
 
 
563
        pos += length;
 
564
        Q_ASSERT(pos == (int)(it.position() + fragment->size) || pos >= endPos);
 
565
    }
 
566
 
 
567
    int n = fragments.findNode(startPos - 1);
 
568
    if (n)
 
569
        unite(n);
 
570
 
 
571
    n = fragments.findNode(endPos);
 
572
    if (n)
 
573
        unite(n);
 
574
 
 
575
    QTextBlock blockIt = blocksFind(startPos);
 
576
    QTextBlock endIt = blocksFind(endPos);
 
577
    if (endIt.isValid())
 
578
        endIt = endIt.next();
 
579
    for (; blockIt.isValid() && blockIt != endIt; blockIt = blockIt.next())
 
580
        QTextDocumentPrivate::block(blockIt)->invalidate();
 
581
 
 
582
    documentChange(startPos, length);
 
583
 
 
584
    endEditBlock();
 
585
}
 
586
 
 
587
void QTextDocumentPrivate::setBlockFormat(const QTextBlock &from, const QTextBlock &to,
 
588
                                     const QTextBlockFormat &newFormat, FormatChangeMode mode)
 
589
{
 
590
    beginEditBlock();
 
591
 
 
592
    Q_ASSERT(newFormat.isValid());
 
593
 
 
594
    int newFormatIdx = -1;
 
595
    if (mode == SetFormat)
 
596
        newFormatIdx = formats.indexForFormat(newFormat);
 
597
    QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(newFormat));
 
598
 
 
599
    QTextBlock it = from;
 
600
    QTextBlock end = to;
 
601
    if (end.isValid())
 
602
        end = end.next();
 
603
 
 
604
    for (; it != end; it = it.next()) {
 
605
        int oldFormat = block(it)->format;
 
606
        QTextBlockFormat format = formats.blockFormat(oldFormat);
 
607
        QTextBlockGroup *oldGroup = qobject_cast<QTextBlockGroup *>(objectForFormat(format));
 
608
        if (mode == MergeFormat) {
 
609
            format.merge(newFormat);
 
610
            newFormatIdx = formats.indexForFormat(format);
 
611
            group = qobject_cast<QTextBlockGroup *>(objectForFormat(format));
 
612
        }
 
613
        block(it)->format = newFormatIdx;
 
614
 
 
615
        block(it)->invalidate();
 
616
 
 
617
        QTextUndoCommand c = { QTextUndoCommand::BlockFormatChanged, true, QTextUndoCommand::MoveCursor, oldFormat,
 
618
                              0, it.position(), { 1 } };
 
619
        appendUndoItem(c);
 
620
 
 
621
        if (group != oldGroup) {
 
622
            if (oldGroup)
 
623
                oldGroup->blockRemoved(it);
 
624
            if (group)
 
625
                group->blockInserted(it);
 
626
        } else if (group) {
 
627
            group->blockFormatChanged(it);
 
628
        }
 
629
    }
 
630
 
 
631
    documentChange(from.position(), to.position() + to.length() - from.position());
 
632
 
 
633
    endEditBlock();
 
634
}
 
635
 
 
636
 
 
637
bool QTextDocumentPrivate::split(int pos)
 
638
{
 
639
    uint x = fragments.findNode(pos);
 
640
    if (x) {
 
641
        int k = fragments.position(x);
 
642
//          qDebug("found fragment with key %d, size_left=%d, size=%d to split at %d",
 
643
//                k, (*it)->size_left, (*it)->size, pos);
 
644
        if (k != pos) {
 
645
            Q_ASSERT(k <= pos);
 
646
            // need to resize the first fragment and add a new one
 
647
            QTextFragmentData *X = fragments.fragment(x);
 
648
            int oldsize = X->size;
 
649
            fragments.setSize(x, pos-k);
 
650
            uint n = fragments.insert_single(pos, oldsize-(pos-k));
 
651
            X = fragments.fragment(x);
 
652
            QTextFragmentData *N = fragments.fragment(n);
 
653
            N->stringPosition = X->stringPosition + pos-k;
 
654
            N->format = X->format;
 
655
            return true;
 
656
        }
 
657
    }
 
658
    return false;
 
659
}
 
660
 
 
661
bool QTextDocumentPrivate::unite(uint f)
 
662
{
 
663
    uint n = fragments.next(f);
 
664
    if (!n)
 
665
        return false;
 
666
 
 
667
    QTextFragmentData *ff = fragments.fragment(f);
 
668
    QTextFragmentData *nf = fragments.fragment(n);
 
669
 
 
670
    if (nf->format == ff->format && (ff->stringPosition + (int)ff->size == nf->stringPosition)) {
 
671
        if (isValidBlockSeparator(text.at(ff->stringPosition))
 
672
            || isValidBlockSeparator(text.at(nf->stringPosition)))
 
673
            return false;
 
674
 
 
675
        fragments.setSize(f, ff->size + nf->size);
 
676
        fragments.erase_single(n);
 
677
        return true;
 
678
    }
 
679
    return false;
 
680
}
 
681
 
 
682
 
 
683
void QTextDocumentPrivate::undoRedo(bool undo)
 
684
{
 
685
    PMDEBUG("%s, undoState=%d, undoStack size=%d", undo ? "undo:" : "redo:", undoState, undoStack.size());
 
686
    if (!undoEnabled || (undo && undoState == 0) || (!undo && undoState == undoStack.size()))
 
687
        return;
 
688
 
 
689
    undoEnabled = false;
 
690
    beginEditBlock();
 
691
    while (1) {
 
692
        if (undo)
 
693
            --undoState;
 
694
        QTextUndoCommand &c = undoStack[undoState];
 
695
 
 
696
        switch(c.command) {
 
697
        case QTextUndoCommand::Inserted:
 
698
            remove(c.pos, c.length, (QTextUndoCommand::Operation)c.operation);
 
699
            PMDEBUG("   erase: from %d, length %d", c.pos, c.length);
 
700
            c.command = QTextUndoCommand::Removed;
 
701
            break;
 
702
        case QTextUndoCommand::Removed:
 
703
            PMDEBUG("   insert: format %d (from %d, length %d, strpos=%d)", c.format, c.pos, c.length, c.strPos);
 
704
            insert_string(c.pos, c.strPos, c.length, c.format, (QTextUndoCommand::Operation)c.operation);
 
705
            c.command = QTextUndoCommand::Inserted;
 
706
            break;
 
707
        case QTextUndoCommand::BlockInserted:
 
708
        case QTextUndoCommand::BlockAdded:
 
709
            remove_block(c.pos, &c.blockFormat, c.command, (QTextUndoCommand::Operation)c.operation);
 
710
            PMDEBUG("   blockremove: from %d", c.pos);
 
711
            if (c.command == QTextUndoCommand::BlockInserted)
 
712
                c.command = QTextUndoCommand::BlockRemoved;
 
713
            else
 
714
                c.command = QTextUndoCommand::BlockDeleted;
 
715
            break;
 
716
        case QTextUndoCommand::BlockRemoved:
 
717
        case QTextUndoCommand::BlockDeleted:
 
718
            PMDEBUG("   blockinsert: charformat %d blockformat %d (pos %d, strpos=%d)", c.format, c.blockFormat, c.pos, c.strPos);
 
719
            insert_block(c.pos, c.strPos, c.format, c.blockFormat, (QTextUndoCommand::Operation)c.operation, c.command);
 
720
            if (c.command == QTextUndoCommand::BlockRemoved)
 
721
                c.command = QTextUndoCommand::BlockInserted;
 
722
            else
 
723
                c.command = QTextUndoCommand::BlockAdded;
 
724
            break;
 
725
        case QTextUndoCommand::CharFormatChanged: {
 
726
            PMDEBUG("   charFormat: format %d (from %d, length %d)", c.format, c.pos, c.length);
 
727
            FragmentIterator it = find(c.pos);
 
728
            Q_ASSERT(!it.atEnd());
 
729
 
 
730
            int oldFormat = it.value()->format;
 
731
            setCharFormat(c.pos, c.length, formats.charFormat(c.format));
 
732
            c.format = oldFormat;
 
733
            break;
 
734
        }
 
735
        case QTextUndoCommand::BlockFormatChanged: {
 
736
            PMDEBUG("   blockformat: format %d pos %d", c.format, c.pos);
 
737
            QTextBlock it = blocksFind(c.pos);
 
738
            Q_ASSERT(it.isValid());
 
739
 
 
740
            int oldFormat = block(it)->format;
 
741
            block(it)->format = c.format;
 
742
            QTextBlockGroup *oldGroup = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(oldFormat)));
 
743
            QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(c.format)));
 
744
            c.format = oldFormat;
 
745
            if (group != oldGroup) {
 
746
                if (oldGroup)
 
747
                    oldGroup->blockRemoved(it);
 
748
                if (group)
 
749
                    group->blockInserted(it);
 
750
            } else if (group) {
 
751
                group->blockFormatChanged(it);
 
752
            }
 
753
            documentChange(it.position(), it.length());
 
754
            break;
 
755
        }
 
756
        case QTextUndoCommand::GroupFormatChange: {
 
757
            PMDEBUG("   group format change");
 
758
            QTextObject *object = c.object;
 
759
            int oldFormat = formats.objectFormatIndex(object->objectIndex());
 
760
            changeObjectFormat(object, c.format);
 
761
            c.format = oldFormat;
 
762
            break;
 
763
        }
 
764
        case QTextUndoCommand::Custom:
 
765
            if (undo)
 
766
                c.custom->undo();
 
767
            else
 
768
                c.custom->redo();
 
769
            break;
 
770
        default:
 
771
            Q_ASSERT(false);
 
772
        }
 
773
        if (undo) {
 
774
            if (undoState == 0 || !undoStack[undoState-1].block)
 
775
                break;
 
776
        } else {
 
777
            ++undoState;
 
778
            if (undoState == undoStack.size() || !undoStack[undoState-1].block)
 
779
                break;
 
780
        }
 
781
    }
 
782
    undoEnabled = true;
 
783
    endEditBlock();
 
784
    Q_Q(QTextDocument);
 
785
    emit q->undoAvailable(isUndoAvailable());
 
786
    emit q->redoAvailable(isRedoAvailable());
 
787
}
 
788
 
 
789
/*!
 
790
    Appends a custom undo \a item to the undo stack.
 
791
*/
 
792
void QTextDocumentPrivate::appendUndoItem(QAbstractUndoItem *item)
 
793
{
 
794
    if (!undoEnabled) {
 
795
        delete item;
 
796
        return;
 
797
    }
 
798
 
 
799
    QTextUndoCommand c;
 
800
    c.command = QTextUndoCommand::Custom;
 
801
    c.block = editBlock != 0;
 
802
    c.operation = QTextUndoCommand::MoveCursor;
 
803
    c.format = 0;
 
804
    c.strPos = 0;
 
805
    c.pos = 0;
 
806
    c.blockFormat = 0;
 
807
 
 
808
    c.custom = item;
 
809
    appendUndoItem(c);
 
810
}
 
811
 
 
812
void QTextDocumentPrivate::appendUndoItem(const QTextUndoCommand &c)
 
813
{
 
814
    Q_Q(QTextDocument);
 
815
    PMDEBUG("appendUndoItem, command=%d enabled=%d", c.command, undoEnabled);
 
816
    if (!undoEnabled)
 
817
        return;
 
818
    if (undoState < undoStack.size())
 
819
        truncateUndoStack();
 
820
 
 
821
    if (!undoStack.isEmpty() && modified) {
 
822
        QTextUndoCommand &last = undoStack[undoState - 1];
 
823
        if (last.tryMerge(c))
 
824
            return;
 
825
    }
 
826
    undoStack.append(c);
 
827
    undoState++;
 
828
    emit q->undoAvailable(true);
 
829
    emit q->redoAvailable(false);
 
830
}
 
831
 
 
832
void QTextDocumentPrivate::truncateUndoStack() {
 
833
    if (undoState == undoStack.size())
 
834
        return;
 
835
 
 
836
    for (int i = undoState; i < undoStack.size(); ++i) {
 
837
        QTextUndoCommand c = undoStack[i];
 
838
        if (c.command & QTextUndoCommand::Removed) {
 
839
            // ########
 
840
//             QTextFragment *f = c.fragment_list;
 
841
//             while (f) {
 
842
//                 QTextFragment *n = f->right;
 
843
//                 delete f;
 
844
//                 f = n;
 
845
//             }
 
846
        } else if (c.command & QTextUndoCommand::Custom) {
 
847
            delete c.custom;
 
848
        }
 
849
    }
 
850
    undoStack.resize(undoState);
 
851
}
 
852
 
 
853
void QTextDocumentPrivate::enableUndoRedo(bool enable)
 
854
{
 
855
    Q_Q(QTextDocument);
 
856
    if (!enable) {
 
857
        undoState = 0;
 
858
        truncateUndoStack();
 
859
        emit q->undoAvailable(false);
 
860
        emit q->redoAvailable(false);
 
861
    }
 
862
    modifiedState = modified ? -1 : undoState;
 
863
    undoEnabled = enable;
 
864
}
 
865
 
 
866
void QTextDocumentPrivate::joinPreviousEditBlock()
 
867
{
 
868
    beginEditBlock();
 
869
 
 
870
    if (undoEnabled && undoState)
 
871
        undoStack[undoState - 1].block = true;
 
872
}
 
873
 
 
874
void QTextDocumentPrivate::endEditBlock()
 
875
{
 
876
    Q_Q(QTextDocument);
 
877
    if (--editBlock)
 
878
        return;
 
879
 
 
880
    if (undoEnabled && undoState > 0)
 
881
        undoStack[undoState - 1].block = false;
 
882
 
 
883
    if (framesDirty)
 
884
        scan_frames(docChangeFrom, docChangeOldLength, docChangeLength);
 
885
 
 
886
    if (lout && docChangeFrom >= 0) {
 
887
        emit q->contentsChange(docChangeFrom, docChangeOldLength, docChangeLength);
 
888
        lout->documentChanged(docChangeFrom, docChangeOldLength, docChangeLength);
 
889
    }
 
890
 
 
891
    docChangeFrom = -1;
 
892
 
 
893
    while (!changedCursors.isEmpty()) {
 
894
        QTextCursorPrivate *curs = changedCursors.takeFirst();
 
895
        emit q->cursorPositionChanged(QTextCursor(curs));
 
896
    }
 
897
 
 
898
    contentsChanged();
 
899
}
 
900
 
 
901
void QTextDocumentPrivate::documentChange(int from, int length)
 
902
{
 
903
//     qDebug("QTextDocumentPrivate::documentChange: from=%d,length=%d", from, length);
 
904
    if (docChangeFrom < 0) {
 
905
        docChangeFrom = from;
 
906
        docChangeOldLength = length;
 
907
        docChangeLength = length;
 
908
        return;
 
909
    }
 
910
    int start = qMin(from, docChangeFrom);
 
911
    int end = qMax(from + length, docChangeFrom + docChangeLength);
 
912
    int diff = qMax(0, end - start - docChangeLength);
 
913
    docChangeFrom = start;
 
914
    docChangeOldLength += diff;
 
915
    docChangeLength += diff;
 
916
}
 
917
 
 
918
void QTextDocumentPrivate::adjustDocumentChangesAndCursors(int from, int addedOrRemoved, QTextUndoCommand::Operation op)
 
919
{
 
920
    Q_Q(QTextDocument);
 
921
    for (int i = 0; i < cursors.size(); ++i) {
 
922
        QTextCursorPrivate *curs = cursors.at(i);
 
923
        if (curs->adjustPosition(from, addedOrRemoved, op) == QTextCursorPrivate::CursorMoved) {
 
924
            if (editBlock)
 
925
                changedCursors.append(curs);
 
926
            else
 
927
                emit q->cursorPositionChanged(QTextCursor(curs));
 
928
        }
 
929
    }
 
930
 
 
931
//     qDebug("QTextDocumentPrivate::adjustDocumentChanges: from=%d,addedOrRemoved=%d", from, addedOrRemoved);
 
932
    if (docChangeFrom < 0) {
 
933
        docChangeFrom = from;
 
934
        if (addedOrRemoved > 0) {
 
935
            docChangeOldLength = 0;
 
936
            docChangeLength = addedOrRemoved;
 
937
        } else {
 
938
            docChangeOldLength = -addedOrRemoved;
 
939
            docChangeLength = 0;
 
940
        }
 
941
//         qDebug("adjustDocumentChanges:");
 
942
//         qDebug("    -> %d %d %d", docChangeFrom, docChangeOldLength, docChangeLength);
 
943
        contentsChanged();
 
944
        return;
 
945
    }
 
946
 
 
947
    // have to merge the new change with the already existing one.
 
948
    int added = qMax(0, addedOrRemoved);
 
949
    int removed = qMax(0, -addedOrRemoved);
 
950
 
 
951
    int diff = 0;
 
952
    if(from + removed < docChangeFrom)
 
953
        diff = docChangeFrom - from - removed;
 
954
    else if(from > docChangeFrom + docChangeLength)
 
955
        diff = from - (docChangeFrom + docChangeLength);
 
956
 
 
957
    int overlap_start = qMax(from, docChangeFrom);
 
958
    int overlap_end = qMin(from + removed, docChangeFrom + docChangeLength);
 
959
    int removedInside = qMax(0, overlap_end - overlap_start);
 
960
    removed -= removedInside;
 
961
 
 
962
//     qDebug("adjustDocumentChanges: from=%d, addedOrRemoved=%d, diff=%d, removedInside=%d", from, addedOrRemoved, diff, removedInside);
 
963
    docChangeFrom = qMin(docChangeFrom, from);
 
964
    docChangeOldLength += removed + diff;
 
965
    docChangeLength += added - removedInside + diff;
 
966
//     qDebug("    -> %d %d %d", docChangeFrom, docChangeOldLength, docChangeLength);
 
967
 
 
968
    contentsChanged();
 
969
}
 
970
 
 
971
 
 
972
QString QTextDocumentPrivate::plainText() const
 
973
{
 
974
    QString result;
 
975
    for (QTextDocumentPrivate::FragmentIterator it = begin(); it != end(); ++it) {
 
976
        const QTextFragmentData *f = *it;
 
977
        result += QString(text.unicode() + f->stringPosition, f->size);
 
978
    }
 
979
    // remove trailing block separator
 
980
    result.chop(1);
 
981
    return result;
 
982
}
 
983
 
 
984
 
 
985
 
 
986
int QTextDocumentPrivate::nextCursorPosition(int position, QTextLayout::CursorMode mode) const
 
987
{
 
988
    if (position == length()-1)
 
989
        return position;
 
990
 
 
991
    QTextBlock it = blocksFind(position);
 
992
    int start = it.position();
 
993
    int end = start + it.length() - 1;
 
994
    if (position == end)
 
995
        return end + 1;
 
996
 
 
997
    return it.layout()->nextCursorPosition(position-start, mode) + start;
 
998
}
 
999
 
 
1000
int QTextDocumentPrivate::previousCursorPosition(int position, QTextLayout::CursorMode mode) const
 
1001
{
 
1002
    if (position == 0)
 
1003
        return position;
 
1004
 
 
1005
    QTextBlock it = blocksFind(position);
 
1006
    int start = it.position();
 
1007
    if (position == start)
 
1008
        return start - 1;
 
1009
 
 
1010
    return it.layout()->previousCursorPosition(position-start, mode) + start;
 
1011
}
 
1012
 
 
1013
void QTextDocumentPrivate::changeObjectFormat(QTextObject *obj, int format)
 
1014
{
 
1015
    beginEditBlock();
 
1016
    int objectIndex = obj->objectIndex();
 
1017
    int oldFormatIndex = formats.objectFormatIndex(objectIndex);
 
1018
    formats.setObjectFormatIndex(objectIndex, format);
 
1019
 
 
1020
    QTextBlockGroup *b = qobject_cast<QTextBlockGroup *>(obj);
 
1021
    if (b) {
 
1022
        QList<QTextBlock> blocks = b->blockList();
 
1023
        for (int i = 0; i < blocks.size(); ++i) {
 
1024
            // invalidate blocks and tell layout
 
1025
            const QTextBlock &block = blocks.at(i);
 
1026
            documentChange(block.position(), block.length());
 
1027
        }
 
1028
    }
 
1029
    QTextFrame *f = qobject_cast<QTextFrame *>(obj);
 
1030
    if (f)
 
1031
        documentChange(f->firstPosition(), f->lastPosition() - f->firstPosition());
 
1032
 
 
1033
    QTextUndoCommand c = { QTextUndoCommand::GroupFormatChange, true, QTextUndoCommand::MoveCursor, oldFormatIndex,
 
1034
                      0, 0, { 1 } };
 
1035
    c.object = obj;
 
1036
    appendUndoItem(c);
 
1037
 
 
1038
    endEditBlock();
 
1039
}
 
1040
 
 
1041
static QTextFrame *findChildFrame(QTextFrame *f, int pos)
 
1042
{
 
1043
    // ##### use binary search
 
1044
    QList<QTextFrame *> children = f->childFrames();
 
1045
    for (int i = 0; i < children.size(); ++i) {
 
1046
        QTextFrame *c = children.at(i);
 
1047
        if (pos >= c->firstPosition() && pos <= c->lastPosition())
 
1048
            return c;
 
1049
    }
 
1050
    return 0;
 
1051
}
 
1052
 
 
1053
QTextFrame *QTextDocumentPrivate::frameAt(int pos) const
 
1054
{
 
1055
    QTextFrame *f = frame;
 
1056
 
 
1057
    while (1) {
 
1058
        QTextFrame *c = findChildFrame(f, pos);
 
1059
        if (!c)
 
1060
            return f;
 
1061
        f = c;
 
1062
    }
 
1063
}
 
1064
 
 
1065
void QTextDocumentPrivate::clearFrame(QTextFrame *f)
 
1066
{
 
1067
    for (int i = 0; i < f->d_func()->childFrames.count(); ++i)
 
1068
        clearFrame(f->d_func()->childFrames.at(i));
 
1069
    f->d_func()->childFrames.clear();
 
1070
    f->d_func()->parentFrame = 0;
 
1071
}
 
1072
 
 
1073
void QTextDocumentPrivate::scan_frames(int pos, int charsRemoved, int charsAdded)
 
1074
{
 
1075
    // ###### optimise
 
1076
    Q_UNUSED(pos);
 
1077
    Q_UNUSED(charsRemoved);
 
1078
    Q_UNUSED(charsAdded);
 
1079
 
 
1080
    QTextFrame *f = frame;
 
1081
    clearFrame(f);
 
1082
 
 
1083
    for (FragmentIterator it = begin(); it != end(); ++it) {
 
1084
        // QTextFormat fmt = formats.format(it->format);
 
1085
        QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(it->format));
 
1086
        if (!frame)
 
1087
            continue;
 
1088
 
 
1089
        Q_ASSERT(it.size() == 1);
 
1090
        QChar ch = text.at(it->stringPosition);
 
1091
 
 
1092
        if (ch == QTextBeginningOfFrame) {
 
1093
            if (f != frame) {
 
1094
                // f == frame happens for tables
 
1095
                Q_ASSERT(frame->d_func()->fragment_start == it.n || frame->d_func()->fragment_start == 0);
 
1096
                frame->d_func()->parentFrame = f;
 
1097
                f->d_func()->childFrames.append(frame);
 
1098
                f = frame;
 
1099
            }
 
1100
        } else if (ch == QTextEndOfFrame) {
 
1101
            Q_ASSERT(f == frame);
 
1102
            Q_ASSERT(frame->d_func()->fragment_end == it.n || frame->d_func()->fragment_end == 0);
 
1103
            f = frame->d_func()->parentFrame;
 
1104
        } else if (ch == QChar::ObjectReplacementCharacter) {
 
1105
            Q_ASSERT(f != frame);
 
1106
            Q_ASSERT(frame->d_func()->fragment_start == it.n || frame->d_func()->fragment_start == 0);
 
1107
            Q_ASSERT(frame->d_func()->fragment_end == it.n || frame->d_func()->fragment_end == 0);
 
1108
            frame->d_func()->parentFrame = f;
 
1109
            f->d_func()->childFrames.append(frame);
 
1110
        } else {
 
1111
            Q_ASSERT(false);
 
1112
        }
 
1113
    }
 
1114
    Q_ASSERT(f == frame);
 
1115
    framesDirty = false;
 
1116
}
 
1117
 
 
1118
void QTextDocumentPrivate::insert_frame(QTextFrame *f)
 
1119
{
 
1120
    int start = f->firstPosition();
 
1121
    int end = f->lastPosition();
 
1122
    QTextFrame *parent = frameAt(start-1);
 
1123
    Q_ASSERT(parent == frameAt(end+1));
 
1124
 
 
1125
    if (start != end) {
 
1126
        // iterator over the parent and move all children contained in my frame to myself
 
1127
        for (int i = 0; i < parent->d_func()->childFrames.size(); ++i) {
 
1128
            QTextFrame *c = parent->d_func()->childFrames.at(i);
 
1129
            if (start < c->firstPosition() && end > c->lastPosition()) {
 
1130
                parent->d_func()->childFrames.removeAt(i);
 
1131
                f->d_func()->childFrames.append(c);
 
1132
                c->d_func()->parentFrame = f;
 
1133
            }
 
1134
        }
 
1135
    }
 
1136
    // insert at the correct position
 
1137
    int i = 0;
 
1138
    for (; i < parent->d_func()->childFrames.size(); ++i) {
 
1139
        QTextFrame *c = parent->d_func()->childFrames.at(i);
 
1140
        if (c->firstPosition() > end)
 
1141
            break;
 
1142
    }
 
1143
    parent->d_func()->childFrames.insert(i, f);
 
1144
    f->d_func()->parentFrame = parent;
 
1145
}
 
1146
 
 
1147
QTextFrame *QTextDocumentPrivate::insertFrame(int start, int end, const QTextFrameFormat &format)
 
1148
{
 
1149
    Q_ASSERT(start >= 0 && start < length());
 
1150
    Q_ASSERT(end >= 0 && end < length());
 
1151
    Q_ASSERT(start <= end || end == -1);
 
1152
 
 
1153
    if (start != end && frameAt(start) != frameAt(end))
 
1154
        return 0;
 
1155
 
 
1156
    beginEditBlock();
 
1157
 
 
1158
    QTextFrame *frame = qobject_cast<QTextFrame *>(createObject(format));
 
1159
    Q_ASSERT(frame);
 
1160
 
 
1161
    // #### using the default block and char format below might be wrong
 
1162
    int idx = formats.indexForFormat(QTextBlockFormat());
 
1163
    QTextCharFormat cfmt;
 
1164
    cfmt.setObjectIndex(frame->objectIndex());
 
1165
    int charIdx = formats.indexForFormat(cfmt);
 
1166
 
 
1167
    insertBlock(QTextBeginningOfFrame, start, idx, charIdx, QTextUndoCommand::MoveCursor);
 
1168
    insertBlock(QTextEndOfFrame, ++end, idx, charIdx, QTextUndoCommand::KeepCursor);
 
1169
 
 
1170
    frame->d_func()->fragment_start = find(start).n;
 
1171
    frame->d_func()->fragment_end = find(end).n;
 
1172
 
 
1173
    insert_frame(frame);
 
1174
 
 
1175
    endEditBlock();
 
1176
 
 
1177
    return frame;
 
1178
}
 
1179
 
 
1180
void QTextDocumentPrivate::removeFrame(QTextFrame *frame)
 
1181
{
 
1182
    QTextFrame *parent = frame->d_func()->parentFrame;
 
1183
    if (!parent)
 
1184
        return;
 
1185
 
 
1186
    int start = frame->firstPosition();
 
1187
    int end = frame->lastPosition();
 
1188
    Q_ASSERT(end >= start);
 
1189
 
 
1190
    beginEditBlock();
 
1191
 
 
1192
    // remove already removes the frames from the tree
 
1193
    remove(end, 1);
 
1194
    remove(start-1, 1);
 
1195
 
 
1196
    endEditBlock();
 
1197
}
 
1198
 
 
1199
QTextObject *QTextDocumentPrivate::objectForIndex(int objectIndex) const
 
1200
{
 
1201
    if (objectIndex < 0)
 
1202
        return 0;
 
1203
 
 
1204
    QTextObject *object = objects.value(objectIndex, 0);
 
1205
    if (!object) {
 
1206
        QTextDocumentPrivate *that = const_cast<QTextDocumentPrivate *>(this);
 
1207
        QTextFormat fmt = formats.objectFormat(objectIndex);
 
1208
        object = that->createObject(fmt, objectIndex);
 
1209
    }
 
1210
    return object;
 
1211
}
 
1212
 
 
1213
QTextObject *QTextDocumentPrivate::objectForFormat(int formatIndex) const
 
1214
{
 
1215
    int objectIndex = formats.format(formatIndex).objectIndex();
 
1216
    return objectForIndex(objectIndex);
 
1217
}
 
1218
 
 
1219
QTextObject *QTextDocumentPrivate::objectForFormat(const QTextFormat &f) const
 
1220
{
 
1221
    return objectForIndex(f.objectIndex());
 
1222
}
 
1223
 
 
1224
QTextObject *QTextDocumentPrivate::createObject(const QTextFormat &f, int objectIndex)
 
1225
{
 
1226
    QTextObject *obj = document()->createObject(f);
 
1227
 
 
1228
    if (obj) {
 
1229
        obj->d_func()->pieceTable = this;
 
1230
        obj->d_func()->objectIndex = objectIndex == -1 ? formats.createObjectIndex(f) : objectIndex;
 
1231
        objects[obj->d_func()->objectIndex] = obj;
 
1232
    }
 
1233
 
 
1234
    return obj;
 
1235
}
 
1236
 
 
1237
void QTextDocumentPrivate::deleteObject(QTextObject *object)
 
1238
{
 
1239
    const int objIdx = object->d_func()->objectIndex;
 
1240
    objects.remove(objIdx);
 
1241
    delete object;
 
1242
}
 
1243
 
 
1244
void QTextDocumentPrivate::contentsChanged()
 
1245
{
 
1246
    Q_Q(QTextDocument);
 
1247
    if (editBlock)
 
1248
        return;
 
1249
 
 
1250
    bool m = undoEnabled ? (modifiedState != undoState) : true;
 
1251
    if (modified != m) {
 
1252
        modified = m;
 
1253
        emit q->modificationChanged(modified);
 
1254
    }
 
1255
 
 
1256
    emit q->contentsChanged();
 
1257
}
 
1258
 
 
1259
void QTextDocumentPrivate::setModified(bool m)
 
1260
{
 
1261
    Q_Q(QTextDocument);
 
1262
    if (m == modified)
 
1263
        return;
 
1264
 
 
1265
    modified = m;
 
1266
    if (!modified)
 
1267
        modifiedState = undoState;
 
1268
    else
 
1269
        modifiedState = -1;
 
1270
 
 
1271
    emit q->modificationChanged(modified);
 
1272
}
 
1273