1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the text module of the Qt Toolkit.
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.
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.
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.
21
** Contact info@trolltech.com if any conditions of this licensing are
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.
27
****************************************************************************/
29
#include <private/qtools_p.h>
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"
47
#define PMDEBUG if(0) qDebug
50
Structure of a document:
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
59
FRAGMENT :== String of characters
61
END_OF_PARA :== 0x2029 # Paragraph separator in Unicode
62
START_OF_FRAME :== 0xfdd0
63
END_OF_FRAME := 0xfdd1
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).
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
75
The above does not clearly note where formats are. Here's
76
how it looks currently:
78
FRAGMENT: one charFormat associated
80
END_OF_PARA: one charFormat, and a blockFormat for the _next_ block.
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
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.
92
The document is independent of the layout with certain restrictions:
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.
100
void QTextBlockData::invalidate() const
103
layout->engine()->invalidate();
106
static bool isValidBlockSeparator(const QChar &ch)
108
return ch == QChar::ParagraphSeparator
109
|| ch == QTextBeginningOfFrame
110
|| ch == QTextEndOfFrame;
114
static bool noBlockInString(const QString &str)
116
return !str.contains(QChar::ParagraphSeparator)
117
&& !str.contains(QTextBeginningOfFrame)
118
&& !str.contains(QTextEndOfFrame);
122
bool QTextUndoCommand::tryMerge(const QTextUndoCommand &other)
124
if (command != other.command)
127
if (command == Inserted
128
&& (pos + length == other.pos)
129
&& (strPos + length == other.strPos)
130
&& format == other.format) {
132
length += other.length;
136
// removal to the 'right' using 'Delete' key
137
if (command == Removed
139
&& (strPos + length == other.strPos)
140
&& format == other.format) {
142
length += other.length;
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)) {
162
QTextDocumentPrivate::QTextDocumentPrivate()
177
void QTextDocumentPrivate::init()
179
frame = qobject_cast<QTextFrame *>(createObject(QTextFrameFormat()));
182
bool undoState = undoEnabled;
184
insertBlock(0, formats.indexForFormat(QTextBlockFormat()), formats.indexForFormat(QTextCharFormat()));
185
undoEnabled = undoState;
190
void QTextDocumentPrivate::clear()
193
for (int i = 0; i < cursors.count(); ++i) {
194
cursors.at(i)->setPosition(0);
195
cursors.at(i)->anchor = 0;
198
QList<QTextCursorPrivate *>oldCursors = cursors;
200
changedCursors.clear();
202
docConfig = QTextDocumentConfig();
208
formats = QTextFormatCollection();
209
int len = fragments.length();
213
q->contentsChange(0, len, 0);
214
lout->documentChanged(0, len, 0);
217
cursors = oldCursors;
220
QTextDocumentPrivate::~QTextDocumentPrivate()
222
for (int i = 0; i < cursors.count(); ++i)
223
cursors.at(i)->priv = 0;
230
void QTextDocumentPrivate::setLayout(QAbstractTextDocumentLayout *layout)
236
emit q->contentsChange(0, 0, length());
237
lout->documentChanged(0, 0, length());
241
void QTextDocumentPrivate::insert_string(int pos, uint strPos, uint length, int format, QTextUndoCommand::Operation op)
243
// ##### optimise when only appending to the fragment!
244
Q_ASSERT(noBlockInString(text.mid(strPos, length)));
247
uint x = fragments.insert_single(pos, length);
248
QTextFragmentData *X = fragments.fragment(x);
250
X->stringPosition = strPos;
251
uint w = fragments.previous(x);
255
int b = blocks.findNode(pos);
256
blocks.setSize(b, blocks.size(b)+length);
258
Q_ASSERT(blocks.length() == fragments.length());
260
QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(format));
262
frame->d_func()->fragmentAdded(text.at(strPos), x);
266
adjustDocumentChangesAndCursors(pos, length, op);
269
void QTextDocumentPrivate::insert_block(int pos, uint strPos, int format, int blockFormat, QTextUndoCommand::Operation op, int command)
272
uint x = fragments.insert_single(pos, 1);
273
QTextFragmentData *X = fragments.fragment(x);
275
X->stringPosition = strPos;
276
// no need trying to unite, since paragraph separators are always in a fragment of their own
278
Q_ASSERT(isValidBlockSeparator(text.at(strPos)));
279
Q_ASSERT(blocks.length()+1 == fragments.length());
282
if (blocks.length() && command == QTextUndoCommand::BlockRemoved)
285
int n = blocks.findNode(block_pos);
286
int key = n ? blocks.position(n) : blocks.length();
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);
295
int b = blocks.insert_single(block_pos, size);
296
QTextBlockData *B = blocks.fragment(b);
297
B->format = blockFormat;
299
Q_ASSERT(blocks.length() == fragments.length());
301
QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blockFormat));
303
group->blockInserted(QTextBlock(this, b));
305
QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(formats.format(format)));
307
frame->d_func()->fragmentAdded(text.at(strPos), x);
311
adjustDocumentChangesAndCursors(pos, 1, op);
314
void QTextDocumentPrivate::insertBlock(const QChar &blockSeparator,
315
int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation op)
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));
324
int strPos = text.length();
325
text.append(blockSeparator);
326
insert_block(pos, strPos, charFormat, blockFormat, op, QTextUndoCommand::BlockRemoved);
328
Q_ASSERT(blocks.length() == fragments.length());
330
QTextUndoCommand c = { QTextUndoCommand::BlockInserted, true,
331
op, charFormat, strPos, pos, { blockFormat } };
334
Q_ASSERT(undoState == undoStack.size());
339
void QTextDocumentPrivate::insertBlock(int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation op)
341
insertBlock(QChar::ParagraphSeparator, pos, blockFormat, charFormat, op);
344
void QTextDocumentPrivate::insert(int pos, int strPos, int strLength, int format)
349
Q_ASSERT(pos >= 0 && pos < fragments.length());
350
Q_ASSERT(formats.format(format).isCharFormat());
352
insert_string(pos, strPos, strLength, format, QTextUndoCommand::MoveCursor);
356
QTextUndoCommand c = { QTextUndoCommand::Inserted, true,
357
QTextUndoCommand::MoveCursor, format, strPos, pos, { strLength } };
359
Q_ASSERT(undoState == undoStack.size());
364
void QTextDocumentPrivate::insert(int pos, const QString &str, int format)
369
Q_ASSERT(noBlockInString(str));
371
int strPos = text.length();
373
insert(pos, strPos, str.length(), format);
376
int QTextDocumentPrivate::remove_string(int pos, uint length, QTextUndoCommand::Operation op)
379
Q_ASSERT(blocks.length() == fragments.length());
380
Q_ASSERT(blocks.length() >= pos+(int)length);
382
int b = blocks.findNode(pos);
383
uint x = fragments.findNode(pos);
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)));
389
blocks.setSize(b, blocks.size(b)-length);
391
QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(fragments.fragment(x)->format));
393
frame->d_func()->fragmentRemoved(text.at(fragments.fragment(x)->stringPosition), x);
397
const int w = fragments.erase_single(x);
399
adjustDocumentChangesAndCursors(pos, -int(length), op);
404
int QTextDocumentPrivate::remove_block(int pos, int *blockFormat, int command, QTextUndoCommand::Operation op)
407
Q_ASSERT(blocks.length() == fragments.length());
408
Q_ASSERT(blocks.length() > pos);
410
int b = blocks.findNode(pos);
411
uint x = fragments.findNode(pos);
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)));
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
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);
430
*blockFormat = blocks.fragment(b)->format;
432
QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blocks.fragment(b)->format));
434
group->blockRemoved(QTextBlock(this, b));
436
QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(fragments.fragment(x)->format));
438
frame->d_func()->fragmentRemoved(text.at(fragments.fragment(x)->stringPosition), x);
442
blocks.erase_single(b);
443
const int w = fragments.erase_single(x);
445
adjustDocumentChangesAndCursors(pos, -1, op);
450
#if !defined(QT_NO_DEBUG)
451
static bool isAncestorFrame(QTextFrame *possibleAncestor, QTextFrame *child)
454
if (child == possibleAncestor)
456
child = child->parentFrame();
462
void QTextDocumentPrivate::remove(int pos, int length, QTextUndoCommand::Operation op)
464
Q_ASSERT(pos >= 0 && pos+length <= fragments.length());
465
Q_ASSERT(blocks.length() == fragments.length());
467
#if !defined(QT_NO_DEBUG)
468
const bool startAndEndInSameFrame = (frameAt(pos) == frameAt(pos + length - 1));
470
const bool endIsEndOfChildFrame = (isAncestorFrame(frameAt(pos), frameAt(pos + length - 1))
471
&& text.at(find(pos + length - 1)->stringPosition) == QTextEndOfFrame);
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());
478
Q_ASSERT(startAndEndInSameFrame || endIsEndOfChildFrame || startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent);
486
uint x = fragments.findNode(pos);
487
uint end = fragments.findNode(pos+length);
491
uint n = fragments.next(x);
493
uint key = fragments.position(x);
494
uint b = blocks.findNode(key+1);
496
QTextFragmentData *X = fragments.fragment(x);
497
QTextUndoCommand c = { QTextUndoCommand::Removed, true,
498
op, X->format, X->stringPosition, key, { X->size } };
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);
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);
518
Q_ASSERT(blocks.length() == fragments.length());
523
void QTextDocumentPrivate::setCharFormat(int pos, int length, const QTextCharFormat &newFormat, FormatChangeMode mode)
525
Q_ASSERT(newFormat.isValid());
529
int newFormatIdx = -1;
530
if (mode == SetFormat)
531
newFormatIdx = formats.indexForFormat(newFormat);
533
const int startPos = pos;
534
const int endPos = pos + length;
539
while (pos < endPos) {
540
FragmentMap::Iterator it = fragments.find(pos);
541
Q_ASSERT(!it.atEnd());
543
QTextFragmentData *fragment = it.value();
545
Q_ASSERT(formats.format(fragment->format).type() == QTextFormat::CharFormat);
547
int offset = pos - it.position();
548
int length = qMin(endPos - pos, int(fragment->size - offset));
549
int oldFormat = fragment->format;
551
if (mode == MergeFormat) {
552
QTextFormat format = formats.format(fragment->format);
553
format.merge(newFormat);
554
fragment->format = formats.indexForFormat(format);
556
fragment->format = newFormatIdx;
559
QTextUndoCommand c = { QTextUndoCommand::CharFormatChanged, true, QTextUndoCommand::MoveCursor, oldFormat,
560
0, pos, { length } };
564
Q_ASSERT(pos == (int)(it.position() + fragment->size) || pos >= endPos);
567
int n = fragments.findNode(startPos - 1);
571
n = fragments.findNode(endPos);
575
QTextBlock blockIt = blocksFind(startPos);
576
QTextBlock endIt = blocksFind(endPos);
578
endIt = endIt.next();
579
for (; blockIt.isValid() && blockIt != endIt; blockIt = blockIt.next())
580
QTextDocumentPrivate::block(blockIt)->invalidate();
582
documentChange(startPos, length);
587
void QTextDocumentPrivate::setBlockFormat(const QTextBlock &from, const QTextBlock &to,
588
const QTextBlockFormat &newFormat, FormatChangeMode mode)
592
Q_ASSERT(newFormat.isValid());
594
int newFormatIdx = -1;
595
if (mode == SetFormat)
596
newFormatIdx = formats.indexForFormat(newFormat);
597
QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(newFormat));
599
QTextBlock it = from;
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));
613
block(it)->format = newFormatIdx;
615
block(it)->invalidate();
617
QTextUndoCommand c = { QTextUndoCommand::BlockFormatChanged, true, QTextUndoCommand::MoveCursor, oldFormat,
618
0, it.position(), { 1 } };
621
if (group != oldGroup) {
623
oldGroup->blockRemoved(it);
625
group->blockInserted(it);
627
group->blockFormatChanged(it);
631
documentChange(from.position(), to.position() + to.length() - from.position());
637
bool QTextDocumentPrivate::split(int pos)
639
uint x = fragments.findNode(pos);
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);
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;
661
bool QTextDocumentPrivate::unite(uint f)
663
uint n = fragments.next(f);
667
QTextFragmentData *ff = fragments.fragment(f);
668
QTextFragmentData *nf = fragments.fragment(n);
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)))
675
fragments.setSize(f, ff->size + nf->size);
676
fragments.erase_single(n);
683
void QTextDocumentPrivate::undoRedo(bool undo)
685
PMDEBUG("%s, undoState=%d, undoStack size=%d", undo ? "undo:" : "redo:", undoState, undoStack.size());
686
if (!undoEnabled || (undo && undoState == 0) || (!undo && undoState == undoStack.size()))
694
QTextUndoCommand &c = undoStack[undoState];
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;
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;
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;
714
c.command = QTextUndoCommand::BlockDeleted;
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;
723
c.command = QTextUndoCommand::BlockAdded;
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());
730
int oldFormat = it.value()->format;
731
setCharFormat(c.pos, c.length, formats.charFormat(c.format));
732
c.format = oldFormat;
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());
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) {
747
oldGroup->blockRemoved(it);
749
group->blockInserted(it);
751
group->blockFormatChanged(it);
753
documentChange(it.position(), it.length());
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;
764
case QTextUndoCommand::Custom:
774
if (undoState == 0 || !undoStack[undoState-1].block)
778
if (undoState == undoStack.size() || !undoStack[undoState-1].block)
785
emit q->undoAvailable(isUndoAvailable());
786
emit q->redoAvailable(isRedoAvailable());
790
Appends a custom undo \a item to the undo stack.
792
void QTextDocumentPrivate::appendUndoItem(QAbstractUndoItem *item)
800
c.command = QTextUndoCommand::Custom;
801
c.block = editBlock != 0;
802
c.operation = QTextUndoCommand::MoveCursor;
812
void QTextDocumentPrivate::appendUndoItem(const QTextUndoCommand &c)
815
PMDEBUG("appendUndoItem, command=%d enabled=%d", c.command, undoEnabled);
818
if (undoState < undoStack.size())
821
if (!undoStack.isEmpty() && modified) {
822
QTextUndoCommand &last = undoStack[undoState - 1];
823
if (last.tryMerge(c))
828
emit q->undoAvailable(true);
829
emit q->redoAvailable(false);
832
void QTextDocumentPrivate::truncateUndoStack() {
833
if (undoState == undoStack.size())
836
for (int i = undoState; i < undoStack.size(); ++i) {
837
QTextUndoCommand c = undoStack[i];
838
if (c.command & QTextUndoCommand::Removed) {
840
// QTextFragment *f = c.fragment_list;
842
// QTextFragment *n = f->right;
846
} else if (c.command & QTextUndoCommand::Custom) {
850
undoStack.resize(undoState);
853
void QTextDocumentPrivate::enableUndoRedo(bool enable)
859
emit q->undoAvailable(false);
860
emit q->redoAvailable(false);
862
modifiedState = modified ? -1 : undoState;
863
undoEnabled = enable;
866
void QTextDocumentPrivate::joinPreviousEditBlock()
870
if (undoEnabled && undoState)
871
undoStack[undoState - 1].block = true;
874
void QTextDocumentPrivate::endEditBlock()
880
if (undoEnabled && undoState > 0)
881
undoStack[undoState - 1].block = false;
884
scan_frames(docChangeFrom, docChangeOldLength, docChangeLength);
886
if (lout && docChangeFrom >= 0) {
887
emit q->contentsChange(docChangeFrom, docChangeOldLength, docChangeLength);
888
lout->documentChanged(docChangeFrom, docChangeOldLength, docChangeLength);
893
while (!changedCursors.isEmpty()) {
894
QTextCursorPrivate *curs = changedCursors.takeFirst();
895
emit q->cursorPositionChanged(QTextCursor(curs));
901
void QTextDocumentPrivate::documentChange(int from, int length)
903
// qDebug("QTextDocumentPrivate::documentChange: from=%d,length=%d", from, length);
904
if (docChangeFrom < 0) {
905
docChangeFrom = from;
906
docChangeOldLength = length;
907
docChangeLength = length;
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;
918
void QTextDocumentPrivate::adjustDocumentChangesAndCursors(int from, int addedOrRemoved, QTextUndoCommand::Operation op)
921
for (int i = 0; i < cursors.size(); ++i) {
922
QTextCursorPrivate *curs = cursors.at(i);
923
if (curs->adjustPosition(from, addedOrRemoved, op) == QTextCursorPrivate::CursorMoved) {
925
changedCursors.append(curs);
927
emit q->cursorPositionChanged(QTextCursor(curs));
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;
938
docChangeOldLength = -addedOrRemoved;
941
// qDebug("adjustDocumentChanges:");
942
// qDebug(" -> %d %d %d", docChangeFrom, docChangeOldLength, docChangeLength);
947
// have to merge the new change with the already existing one.
948
int added = qMax(0, addedOrRemoved);
949
int removed = qMax(0, -addedOrRemoved);
952
if(from + removed < docChangeFrom)
953
diff = docChangeFrom - from - removed;
954
else if(from > docChangeFrom + docChangeLength)
955
diff = from - (docChangeFrom + docChangeLength);
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;
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);
972
QString QTextDocumentPrivate::plainText() const
975
for (QTextDocumentPrivate::FragmentIterator it = begin(); it != end(); ++it) {
976
const QTextFragmentData *f = *it;
977
result += QString(text.unicode() + f->stringPosition, f->size);
979
// remove trailing block separator
986
int QTextDocumentPrivate::nextCursorPosition(int position, QTextLayout::CursorMode mode) const
988
if (position == length()-1)
991
QTextBlock it = blocksFind(position);
992
int start = it.position();
993
int end = start + it.length() - 1;
997
return it.layout()->nextCursorPosition(position-start, mode) + start;
1000
int QTextDocumentPrivate::previousCursorPosition(int position, QTextLayout::CursorMode mode) const
1005
QTextBlock it = blocksFind(position);
1006
int start = it.position();
1007
if (position == start)
1010
return it.layout()->previousCursorPosition(position-start, mode) + start;
1013
void QTextDocumentPrivate::changeObjectFormat(QTextObject *obj, int format)
1016
int objectIndex = obj->objectIndex();
1017
int oldFormatIndex = formats.objectFormatIndex(objectIndex);
1018
formats.setObjectFormatIndex(objectIndex, format);
1020
QTextBlockGroup *b = qobject_cast<QTextBlockGroup *>(obj);
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());
1029
QTextFrame *f = qobject_cast<QTextFrame *>(obj);
1031
documentChange(f->firstPosition(), f->lastPosition() - f->firstPosition());
1033
QTextUndoCommand c = { QTextUndoCommand::GroupFormatChange, true, QTextUndoCommand::MoveCursor, oldFormatIndex,
1041
static QTextFrame *findChildFrame(QTextFrame *f, int pos)
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())
1053
QTextFrame *QTextDocumentPrivate::frameAt(int pos) const
1055
QTextFrame *f = frame;
1058
QTextFrame *c = findChildFrame(f, pos);
1065
void QTextDocumentPrivate::clearFrame(QTextFrame *f)
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;
1073
void QTextDocumentPrivate::scan_frames(int pos, int charsRemoved, int charsAdded)
1077
Q_UNUSED(charsRemoved);
1078
Q_UNUSED(charsAdded);
1080
QTextFrame *f = frame;
1083
for (FragmentIterator it = begin(); it != end(); ++it) {
1084
// QTextFormat fmt = formats.format(it->format);
1085
QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(it->format));
1089
Q_ASSERT(it.size() == 1);
1090
QChar ch = text.at(it->stringPosition);
1092
if (ch == QTextBeginningOfFrame) {
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);
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);
1114
Q_ASSERT(f == frame);
1115
framesDirty = false;
1118
void QTextDocumentPrivate::insert_frame(QTextFrame *f)
1120
int start = f->firstPosition();
1121
int end = f->lastPosition();
1122
QTextFrame *parent = frameAt(start-1);
1123
Q_ASSERT(parent == frameAt(end+1));
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;
1136
// insert at the correct position
1138
for (; i < parent->d_func()->childFrames.size(); ++i) {
1139
QTextFrame *c = parent->d_func()->childFrames.at(i);
1140
if (c->firstPosition() > end)
1143
parent->d_func()->childFrames.insert(i, f);
1144
f->d_func()->parentFrame = parent;
1147
QTextFrame *QTextDocumentPrivate::insertFrame(int start, int end, const QTextFrameFormat &format)
1149
Q_ASSERT(start >= 0 && start < length());
1150
Q_ASSERT(end >= 0 && end < length());
1151
Q_ASSERT(start <= end || end == -1);
1153
if (start != end && frameAt(start) != frameAt(end))
1158
QTextFrame *frame = qobject_cast<QTextFrame *>(createObject(format));
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);
1167
insertBlock(QTextBeginningOfFrame, start, idx, charIdx, QTextUndoCommand::MoveCursor);
1168
insertBlock(QTextEndOfFrame, ++end, idx, charIdx, QTextUndoCommand::KeepCursor);
1170
frame->d_func()->fragment_start = find(start).n;
1171
frame->d_func()->fragment_end = find(end).n;
1173
insert_frame(frame);
1180
void QTextDocumentPrivate::removeFrame(QTextFrame *frame)
1182
QTextFrame *parent = frame->d_func()->parentFrame;
1186
int start = frame->firstPosition();
1187
int end = frame->lastPosition();
1188
Q_ASSERT(end >= start);
1192
// remove already removes the frames from the tree
1199
QTextObject *QTextDocumentPrivate::objectForIndex(int objectIndex) const
1201
if (objectIndex < 0)
1204
QTextObject *object = objects.value(objectIndex, 0);
1206
QTextDocumentPrivate *that = const_cast<QTextDocumentPrivate *>(this);
1207
QTextFormat fmt = formats.objectFormat(objectIndex);
1208
object = that->createObject(fmt, objectIndex);
1213
QTextObject *QTextDocumentPrivate::objectForFormat(int formatIndex) const
1215
int objectIndex = formats.format(formatIndex).objectIndex();
1216
return objectForIndex(objectIndex);
1219
QTextObject *QTextDocumentPrivate::objectForFormat(const QTextFormat &f) const
1221
return objectForIndex(f.objectIndex());
1224
QTextObject *QTextDocumentPrivate::createObject(const QTextFormat &f, int objectIndex)
1226
QTextObject *obj = document()->createObject(f);
1229
obj->d_func()->pieceTable = this;
1230
obj->d_func()->objectIndex = objectIndex == -1 ? formats.createObjectIndex(f) : objectIndex;
1231
objects[obj->d_func()->objectIndex] = obj;
1237
void QTextDocumentPrivate::deleteObject(QTextObject *object)
1239
const int objIdx = object->d_func()->objectIndex;
1240
objects.remove(objIdx);
1244
void QTextDocumentPrivate::contentsChanged()
1250
bool m = undoEnabled ? (modifiedState != undoState) : true;
1251
if (modified != m) {
1253
emit q->modificationChanged(modified);
1256
emit q->contentsChanged();
1259
void QTextDocumentPrivate::setModified(bool m)
1267
modifiedState = undoState;
1271
emit q->modificationChanged(modified);