util/src/gui/text/qtextdocument_p.cpp
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 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     revision = -1; // init() inserts a block, bringing it to 0
       
   199 
       
   200     lout = 0;
       
   201 
       
   202     modified = false;
       
   203     modifiedState = 0;
       
   204 
       
   205     undoEnabled = true;
       
   206     inContentsChange = 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 : revision;
       
   433     b = blocks.next(b);
       
   434     if (b) {
       
   435         B = blocks.fragment(b);
       
   436         B->revision = atBlockStart ? oldRevision : revision;
       
   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     insert_string(pos, strPos, strLength, format, QTextUndoCommand::MoveCursor);
       
   460     if (undoEnabled) {
       
   461         int b = blocks.findNode(pos);
       
   462         QTextBlockData *B = blocks.fragment(b);
       
   463 
       
   464         QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::Inserted, (editBlock != 0),
       
   465                                 QTextUndoCommand::MoveCursor, format, strPos, pos, strLength,
       
   466                                 B->revision);
       
   467         appendUndoItem(c);
       
   468         B->revision = revision;
       
   469         Q_ASSERT(undoState == undoStack.size());
       
   470     }
       
   471     finishEdit();
       
   472 }
       
   473 
       
   474 void QTextDocumentPrivate::insert(int pos, const QString &str, int format)
       
   475 {
       
   476     if (str.size() == 0)
       
   477         return;
       
   478 
       
   479     Q_ASSERT(noBlockInString(str));
       
   480 
       
   481     int strPos = text.length();
       
   482     text.append(str);
       
   483     insert(pos, strPos, str.length(), format);
       
   484 }
       
   485 
       
   486 int QTextDocumentPrivate::remove_string(int pos, uint length, QTextUndoCommand::Operation op)
       
   487 {
       
   488     Q_ASSERT(pos >= 0);
       
   489     Q_ASSERT(blocks.length() == fragments.length());
       
   490     Q_ASSERT(blocks.length() >= pos+(int)length);
       
   491 
       
   492     int b = blocks.findNode(pos);
       
   493     uint x = fragments.findNode(pos);
       
   494 
       
   495     Q_ASSERT(blocks.size(b) > length);
       
   496     Q_ASSERT(x && fragments.position(x) == (uint)pos && fragments.size(x) == length);
       
   497     Q_ASSERT(noBlockInString(text.mid(fragments.fragment(x)->stringPosition, length)));
       
   498 
       
   499     blocks.setSize(b, blocks.size(b)-length);
       
   500 
       
   501     QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(fragments.fragment(x)->format));
       
   502     if (frame) {
       
   503         frame->d_func()->fragmentRemoved(text.at(fragments.fragment(x)->stringPosition), x);
       
   504         framesDirty = true;
       
   505     }
       
   506 
       
   507     const int w = fragments.erase_single(x);
       
   508 
       
   509     if (!undoEnabled)
       
   510         unreachableCharacterCount += length;
       
   511 
       
   512     adjustDocumentChangesAndCursors(pos, -int(length), op);
       
   513 
       
   514     return w;
       
   515 }
       
   516 
       
   517 int QTextDocumentPrivate::remove_block(int pos, int *blockFormat, int command, QTextUndoCommand::Operation op)
       
   518 {
       
   519     Q_ASSERT(pos >= 0);
       
   520     Q_ASSERT(blocks.length() == fragments.length());
       
   521     Q_ASSERT(blocks.length() > pos);
       
   522 
       
   523     int b = blocks.findNode(pos);
       
   524     uint x = fragments.findNode(pos);
       
   525 
       
   526     Q_ASSERT(x && (int)fragments.position(x) == pos);
       
   527     Q_ASSERT(fragments.size(x) == 1);
       
   528     Q_ASSERT(isValidBlockSeparator(text.at(fragments.fragment(x)->stringPosition)));
       
   529     Q_ASSERT(b);
       
   530 
       
   531     if (blocks.size(b) == 1 && command == QTextUndoCommand::BlockAdded) {
       
   532 	Q_ASSERT((int)blocks.position(b) == pos);
       
   533 //  	qDebug("removing empty block");
       
   534 	// empty block remove the block itself
       
   535     } else {
       
   536 	// non empty block, merge with next one into this block
       
   537 //  	qDebug("merging block with next");
       
   538 	int n = blocks.next(b);
       
   539 	Q_ASSERT((int)blocks.position(n) == pos + 1);
       
   540 	blocks.setSize(b, blocks.size(b) + blocks.size(n) - 1);
       
   541 	b = n;
       
   542     }
       
   543     *blockFormat = blocks.fragment(b)->format;
       
   544 
       
   545     QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(blocks.fragment(b)->format));
       
   546     if (group)
       
   547         group->blockRemoved(QTextBlock(this, b));
       
   548 
       
   549     QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(fragments.fragment(x)->format));
       
   550     if (frame) {
       
   551         frame->d_func()->fragmentRemoved(text.at(fragments.fragment(x)->stringPosition), x);
       
   552         framesDirty = true;
       
   553     }
       
   554 
       
   555     blocks.erase_single(b);
       
   556     const int w = fragments.erase_single(x);
       
   557 
       
   558     adjustDocumentChangesAndCursors(pos, -1, op);
       
   559 
       
   560     return w;
       
   561 }
       
   562 
       
   563 #if !defined(QT_NO_DEBUG)
       
   564 static bool isAncestorFrame(QTextFrame *possibleAncestor, QTextFrame *child)
       
   565 {
       
   566     while (child) {
       
   567         if (child == possibleAncestor)
       
   568             return true;
       
   569         child = child->parentFrame();
       
   570     }
       
   571     return false;
       
   572 }
       
   573 #endif
       
   574 
       
   575 void QTextDocumentPrivate::move(int pos, int to, int length, QTextUndoCommand::Operation op)
       
   576 {
       
   577     Q_ASSERT(to <= fragments.length() && to <= pos);
       
   578     Q_ASSERT(pos >= 0 && pos+length <= fragments.length());
       
   579     Q_ASSERT(blocks.length() == fragments.length());
       
   580 
       
   581     if (pos == to)
       
   582         return;
       
   583 
       
   584     const bool needsInsert = to != -1;
       
   585 
       
   586 #if !defined(QT_NO_DEBUG)
       
   587     const bool startAndEndInSameFrame = (frameAt(pos) == frameAt(pos + length - 1));
       
   588 
       
   589     const bool endIsEndOfChildFrame = (isAncestorFrame(frameAt(pos), frameAt(pos + length - 1))
       
   590                                        && text.at(find(pos + length - 1)->stringPosition) == QTextEndOfFrame);
       
   591 
       
   592     const bool startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent
       
   593                = (text.at(find(pos)->stringPosition) == QTextBeginningOfFrame
       
   594                   && text.at(find(pos + length - 1)->stringPosition) == QTextEndOfFrame
       
   595                   && frameAt(pos)->parentFrame() == frameAt(pos + length - 1)->parentFrame());
       
   596 
       
   597     const bool isFirstTableCell = (qobject_cast<QTextTable *>(frameAt(pos + length - 1))
       
   598                                   && frameAt(pos + length - 1)->parentFrame() == frameAt(pos));
       
   599 
       
   600     Q_ASSERT(startAndEndInSameFrame || endIsEndOfChildFrame || startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent || isFirstTableCell);
       
   601 #endif
       
   602 
       
   603     split(pos);
       
   604     split(pos+length);
       
   605 
       
   606     uint dst = needsInsert ? fragments.findNode(to) : 0;
       
   607     uint dstKey = needsInsert ? fragments.position(dst) : 0;
       
   608 
       
   609     uint x = fragments.findNode(pos);
       
   610     uint end = fragments.findNode(pos+length);
       
   611 
       
   612     uint w = 0;
       
   613     while (x != end) {
       
   614         uint n = fragments.next(x);
       
   615 
       
   616         uint key = fragments.position(x);
       
   617         uint b = blocks.findNode(key+1);
       
   618         QTextBlockData *B = blocks.fragment(b);
       
   619         int blockRevision = B->revision;
       
   620 
       
   621         QTextFragmentData *X = fragments.fragment(x);
       
   622         QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::Removed, (editBlock != 0),
       
   623                                 op, X->format, X->stringPosition, key, X->size_array[0],
       
   624                                 blockRevision);
       
   625         QT_INIT_TEXTUNDOCOMMAND(cInsert, QTextUndoCommand::Inserted, (editBlock != 0),
       
   626                                 op, X->format, X->stringPosition, dstKey, X->size_array[0],
       
   627                                 blockRevision);
       
   628 
       
   629         if (key+1 != blocks.position(b)) {
       
   630 //	    qDebug("remove_string from %d length %d", key, X->size_array[0]);
       
   631             Q_ASSERT(noBlockInString(text.mid(X->stringPosition, X->size_array[0])));
       
   632             w = remove_string(key, X->size_array[0], op);
       
   633 
       
   634             if (needsInsert) {
       
   635                 insert_string(dstKey, X->stringPosition, X->size_array[0], X->format, op);
       
   636                 dstKey += X->size_array[0];
       
   637             }
       
   638         } else {
       
   639 //	    qDebug("remove_block at %d", key);
       
   640             Q_ASSERT(X->size_array[0] == 1 && isValidBlockSeparator(text.at(X->stringPosition)));
       
   641             b = blocks.previous(b);
       
   642             B = 0;
       
   643             c.command = blocks.size(b) == 1 ? QTextUndoCommand::BlockDeleted : QTextUndoCommand::BlockRemoved;
       
   644             w = remove_block(key, &c.blockFormat, QTextUndoCommand::BlockAdded, op);
       
   645 
       
   646             if (needsInsert) {
       
   647                 insert_block(dstKey++, X->stringPosition, X->format, c.blockFormat, op, QTextUndoCommand::BlockRemoved);
       
   648                 cInsert.command = blocks.size(b) == 1 ? QTextUndoCommand::BlockAdded : QTextUndoCommand::BlockInserted;
       
   649                 cInsert.blockFormat = c.blockFormat;
       
   650             }
       
   651         }
       
   652         appendUndoItem(c);
       
   653         if (B)
       
   654             B->revision = revision;
       
   655         x = n;
       
   656 
       
   657         if (needsInsert)
       
   658             appendUndoItem(cInsert);
       
   659     }
       
   660     if (w)
       
   661         unite(w);
       
   662 
       
   663     Q_ASSERT(blocks.length() == fragments.length());
       
   664 
       
   665     finishEdit();
       
   666 }
       
   667 
       
   668 void QTextDocumentPrivate::remove(int pos, int length, QTextUndoCommand::Operation op)
       
   669 {
       
   670     if (length == 0)
       
   671         return;
       
   672     move(pos, -1, length, op);
       
   673 }
       
   674 
       
   675 void QTextDocumentPrivate::setCharFormat(int pos, int length, const QTextCharFormat &newFormat, FormatChangeMode mode)
       
   676 {
       
   677     beginEditBlock();
       
   678 
       
   679     Q_ASSERT(newFormat.isValid());
       
   680 
       
   681     int newFormatIdx = -1;
       
   682     if (mode == SetFormatAndPreserveObjectIndices) {
       
   683         QTextCharFormat cleanFormat = newFormat;
       
   684         cleanFormat.clearProperty(QTextFormat::ObjectIndex);
       
   685         newFormatIdx = formats.indexForFormat(cleanFormat);
       
   686     } else if (mode == SetFormat) {
       
   687         newFormatIdx = formats.indexForFormat(newFormat);
       
   688     }
       
   689 
       
   690     if (pos == -1) {
       
   691         if (mode == MergeFormat) {
       
   692             QTextFormat format = formats.format(initialBlockCharFormatIndex);
       
   693             format.merge(newFormat);
       
   694             initialBlockCharFormatIndex = formats.indexForFormat(format);
       
   695         } else if (mode == SetFormatAndPreserveObjectIndices
       
   696                    && formats.format(initialBlockCharFormatIndex).objectIndex() != -1) {
       
   697             QTextCharFormat f = newFormat;
       
   698             f.setObjectIndex(formats.format(initialBlockCharFormatIndex).objectIndex());
       
   699             initialBlockCharFormatIndex = formats.indexForFormat(f);
       
   700         } else {
       
   701             initialBlockCharFormatIndex = newFormatIdx;
       
   702         }
       
   703 
       
   704         ++pos;
       
   705         --length;
       
   706     }
       
   707 
       
   708     const int startPos = pos;
       
   709     const int endPos = pos + length;
       
   710 
       
   711     split(startPos);
       
   712     split(endPos);
       
   713 
       
   714     while (pos < endPos) {
       
   715         FragmentMap::Iterator it = fragments.find(pos);
       
   716         Q_ASSERT(!it.atEnd());
       
   717 
       
   718         QTextFragmentData *fragment = it.value();
       
   719 
       
   720         Q_ASSERT(formats.format(fragment->format).type() == QTextFormat::CharFormat);
       
   721 
       
   722         int offset = pos - it.position();
       
   723         int length = qMin(endPos - pos, int(fragment->size_array[0] - offset));
       
   724         int oldFormat = fragment->format;
       
   725 
       
   726         if (mode == MergeFormat) {
       
   727             QTextFormat format = formats.format(fragment->format);
       
   728             format.merge(newFormat);
       
   729             fragment->format = formats.indexForFormat(format);
       
   730         } else if (mode == SetFormatAndPreserveObjectIndices
       
   731                    && formats.format(oldFormat).objectIndex() != -1) {
       
   732             QTextCharFormat f = newFormat;
       
   733             f.setObjectIndex(formats.format(oldFormat).objectIndex());
       
   734             fragment->format = formats.indexForFormat(f);
       
   735         } else {
       
   736             fragment->format = newFormatIdx;
       
   737         }
       
   738 
       
   739         QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::CharFormatChanged, true, QTextUndoCommand::MoveCursor, oldFormat,
       
   740                                 0, pos, length, 0);
       
   741         appendUndoItem(c);
       
   742 
       
   743         pos += length;
       
   744         Q_ASSERT(pos == (int)(it.position() + fragment->size_array[0]) || pos >= endPos);
       
   745     }
       
   746 
       
   747     int n = fragments.findNode(startPos - 1);
       
   748     if (n)
       
   749         unite(n);
       
   750 
       
   751     n = fragments.findNode(endPos);
       
   752     if (n)
       
   753         unite(n);
       
   754 
       
   755     QTextBlock blockIt = blocksFind(startPos);
       
   756     QTextBlock endIt = blocksFind(endPos);
       
   757     if (endIt.isValid())
       
   758         endIt = endIt.next();
       
   759     for (; blockIt.isValid() && blockIt != endIt; blockIt = blockIt.next())
       
   760         QTextDocumentPrivate::block(blockIt)->invalidate();
       
   761 
       
   762     documentChange(startPos, length);
       
   763 
       
   764     endEditBlock();
       
   765 }
       
   766 
       
   767 void QTextDocumentPrivate::setBlockFormat(const QTextBlock &from, const QTextBlock &to,
       
   768 				     const QTextBlockFormat &newFormat, FormatChangeMode mode)
       
   769 {
       
   770     beginEditBlock();
       
   771 
       
   772     Q_ASSERT(mode != SetFormatAndPreserveObjectIndices); // only implemented for setCharFormat
       
   773 
       
   774     Q_ASSERT(newFormat.isValid());
       
   775 
       
   776     int newFormatIdx = -1;
       
   777     if (mode == SetFormat)
       
   778         newFormatIdx = formats.indexForFormat(newFormat);
       
   779     QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(newFormat));
       
   780 
       
   781     QTextBlock it = from;
       
   782     QTextBlock end = to;
       
   783     if (end.isValid())
       
   784 	end = end.next();
       
   785 
       
   786     for (; it != end; it = it.next()) {
       
   787         int oldFormat = block(it)->format;
       
   788         QTextBlockFormat format = formats.blockFormat(oldFormat);
       
   789         QTextBlockGroup *oldGroup = qobject_cast<QTextBlockGroup *>(objectForFormat(format));
       
   790         if (mode == MergeFormat) {
       
   791             format.merge(newFormat);
       
   792             newFormatIdx = formats.indexForFormat(format);
       
   793             group = qobject_cast<QTextBlockGroup *>(objectForFormat(format));
       
   794         }
       
   795         block(it)->format = newFormatIdx;
       
   796 
       
   797         block(it)->invalidate();
       
   798 
       
   799         QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::BlockFormatChanged, true, QTextUndoCommand::MoveCursor, oldFormat,
       
   800                                 0, it.position(), 1, 0);
       
   801         appendUndoItem(c);
       
   802 
       
   803         if (group != oldGroup) {
       
   804             if (oldGroup)
       
   805                 oldGroup->blockRemoved(it);
       
   806             if (group)
       
   807                 group->blockInserted(it);
       
   808         } else if (group) {
       
   809 	    group->blockFormatChanged(it);
       
   810 	}
       
   811     }
       
   812 
       
   813     documentChange(from.position(), to.position() + to.length() - from.position());
       
   814 
       
   815     endEditBlock();
       
   816 }
       
   817 
       
   818 
       
   819 bool QTextDocumentPrivate::split(int pos)
       
   820 {
       
   821     uint x = fragments.findNode(pos);
       
   822     if (x) {
       
   823         int k = fragments.position(x);
       
   824 //          qDebug("found fragment with key %d, size_left=%d, size=%d to split at %d",
       
   825 //                k, (*it)->size_left[0], (*it)->size_array[0], pos);
       
   826         if (k != pos) {
       
   827             Q_ASSERT(k <= pos);
       
   828             // need to resize the first fragment and add a new one
       
   829             QTextFragmentData *X = fragments.fragment(x);
       
   830             int oldsize = X->size_array[0];
       
   831             fragments.setSize(x, pos-k);
       
   832             uint n = fragments.insert_single(pos, oldsize-(pos-k));
       
   833             X = fragments.fragment(x);
       
   834             QTextFragmentData *N = fragments.fragment(n);
       
   835             N->stringPosition = X->stringPosition + pos-k;
       
   836             N->format = X->format;
       
   837             return true;
       
   838         }
       
   839     }
       
   840     return false;
       
   841 }
       
   842 
       
   843 bool QTextDocumentPrivate::unite(uint f)
       
   844 {
       
   845     uint n = fragments.next(f);
       
   846     if (!n)
       
   847         return false;
       
   848 
       
   849     QTextFragmentData *ff = fragments.fragment(f);
       
   850     QTextFragmentData *nf = fragments.fragment(n);
       
   851 
       
   852     if (nf->format == ff->format && (ff->stringPosition + (int)ff->size_array[0] == nf->stringPosition)) {
       
   853         if (isValidBlockSeparator(text.at(ff->stringPosition))
       
   854             || isValidBlockSeparator(text.at(nf->stringPosition)))
       
   855             return false;
       
   856 
       
   857         fragments.setSize(f, ff->size_array[0] + nf->size_array[0]);
       
   858         fragments.erase_single(n);
       
   859         return true;
       
   860     }
       
   861     return false;
       
   862 }
       
   863 
       
   864 
       
   865 int QTextDocumentPrivate::undoRedo(bool undo)
       
   866 {
       
   867     PMDEBUG("%s, undoState=%d, undoStack size=%d", undo ? "undo:" : "redo:", undoState, undoStack.size());
       
   868     if (!undoEnabled || (undo && undoState == 0) || (!undo && undoState == undoStack.size()))
       
   869         return -1;
       
   870 
       
   871     undoEnabled = false;
       
   872     beginEditBlock();
       
   873     int editPos = -1;
       
   874     while (1) {
       
   875         if (undo)
       
   876             --undoState;
       
   877         QTextUndoCommand &c = undoStack[undoState];
       
   878         int resetBlockRevision = c.pos;
       
   879 
       
   880 	switch(c.command) {
       
   881         case QTextUndoCommand::Inserted:
       
   882             remove(c.pos, c.length, (QTextUndoCommand::Operation)c.operation);
       
   883             PMDEBUG("   erase: from %d, length %d", c.pos, c.length);
       
   884             c.command = QTextUndoCommand::Removed;
       
   885             editPos = c.pos;
       
   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             editPos = c.pos + c.length;
       
   892 	    break;
       
   893 	case QTextUndoCommand::BlockInserted:
       
   894 	case QTextUndoCommand::BlockAdded:
       
   895             remove_block(c.pos, &c.blockFormat, c.command, (QTextUndoCommand::Operation)c.operation);
       
   896             PMDEBUG("   blockremove: from %d", c.pos);
       
   897 	    if (c.command == QTextUndoCommand::BlockInserted)
       
   898 		c.command = QTextUndoCommand::BlockRemoved;
       
   899 	    else
       
   900 		c.command = QTextUndoCommand::BlockDeleted;
       
   901             editPos = c.pos;
       
   902 	    break;
       
   903 	case QTextUndoCommand::BlockRemoved:
       
   904 	case QTextUndoCommand::BlockDeleted:
       
   905             PMDEBUG("   blockinsert: charformat %d blockformat %d (pos %d, strpos=%d)", c.format, c.blockFormat, c.pos, c.strPos);
       
   906             insert_block(c.pos, c.strPos, c.format, c.blockFormat, (QTextUndoCommand::Operation)c.operation, c.command);
       
   907             resetBlockRevision += 1;
       
   908 	    if (c.command == QTextUndoCommand::BlockRemoved)
       
   909 		c.command = QTextUndoCommand::BlockInserted;
       
   910 	    else
       
   911 		c.command = QTextUndoCommand::BlockAdded;
       
   912             editPos = c.pos + 1;
       
   913 	    break;
       
   914 	case QTextUndoCommand::CharFormatChanged: {
       
   915             resetBlockRevision = -1; // ## TODO
       
   916             PMDEBUG("   charFormat: format %d (from %d, length %d)", c.format, c.pos, c.length);
       
   917             FragmentIterator it = find(c.pos);
       
   918             Q_ASSERT(!it.atEnd());
       
   919 
       
   920             int oldFormat = it.value()->format;
       
   921             setCharFormat(c.pos, c.length, formats.charFormat(c.format));
       
   922             c.format = oldFormat;
       
   923             editPos = c.pos + c.length;
       
   924 	    break;
       
   925 	}
       
   926 	case QTextUndoCommand::BlockFormatChanged: {
       
   927             resetBlockRevision = -1; // ## TODO
       
   928             PMDEBUG("   blockformat: format %d pos %d", c.format, c.pos);
       
   929             QTextBlock it = blocksFind(c.pos);
       
   930             Q_ASSERT(it.isValid());
       
   931 
       
   932             int oldFormat = block(it)->format;
       
   933             block(it)->format = c.format;
       
   934             QTextBlockGroup *oldGroup = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(oldFormat)));
       
   935             QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(c.format)));
       
   936             c.format = oldFormat;
       
   937             if (group != oldGroup) {
       
   938                 if (oldGroup)
       
   939                     oldGroup->blockRemoved(it);
       
   940                 if (group)
       
   941                     group->blockInserted(it);
       
   942             } else if (group) {
       
   943                 group->blockFormatChanged(it);
       
   944             }
       
   945             documentChange(it.position(), it.length());
       
   946             editPos = -1;
       
   947 	    break;
       
   948 	}
       
   949 	case QTextUndoCommand::GroupFormatChange: {
       
   950             resetBlockRevision = -1; // ## TODO
       
   951             PMDEBUG("   group format change");
       
   952             QTextObject *object = objectForIndex(c.objectIndex);
       
   953             int oldFormat = formats.objectFormatIndex(c.objectIndex);
       
   954             changeObjectFormat(object, c.format);
       
   955             c.format = oldFormat;
       
   956             editPos = -1;
       
   957 	    break;
       
   958 	}
       
   959 	case QTextUndoCommand::Custom:
       
   960             resetBlockRevision = -1; // ## TODO
       
   961             if (undo)
       
   962                 c.custom->undo();
       
   963             else
       
   964                 c.custom->redo();
       
   965             editPos = -1;
       
   966 	    break;
       
   967 	default:
       
   968 	    Q_ASSERT(false);
       
   969         }
       
   970 
       
   971         if (resetBlockRevision >= 0) {
       
   972             int b = blocks.findNode(resetBlockRevision);
       
   973             QTextBlockData *B = blocks.fragment(b);
       
   974             B->revision = c.revision;
       
   975         }
       
   976 
       
   977         if (!undo)
       
   978             ++undoState;
       
   979 
       
   980         bool inBlock = (
       
   981                 undoState > 0
       
   982                 && undoState < undoStack.size()
       
   983                 && undoStack[undoState].block_part
       
   984                 && undoStack[undoState-1].block_part
       
   985                 && !undoStack[undoState-1].block_end
       
   986                 );
       
   987         if (!inBlock)
       
   988             break;
       
   989     }
       
   990     undoEnabled = true;
       
   991     if (editPos < 0 && docChangeFrom >= 0) {
       
   992         editPos = qMin(docChangeFrom + docChangeLength, length() - 1);
       
   993     }
       
   994     endEditBlock();
       
   995     emitUndoAvailable(isUndoAvailable());
       
   996     emitRedoAvailable(isRedoAvailable());
       
   997     return editPos;
       
   998 }
       
   999 
       
  1000 /*!
       
  1001     Appends a custom undo \a item to the undo stack.
       
  1002 */
       
  1003 void QTextDocumentPrivate::appendUndoItem(QAbstractUndoItem *item)
       
  1004 {
       
  1005     if (!undoEnabled) {
       
  1006         delete item;
       
  1007         return;
       
  1008     }
       
  1009 
       
  1010     QTextUndoCommand c;
       
  1011     c.command = QTextUndoCommand::Custom;
       
  1012     c.block_part = editBlock != 0;
       
  1013     c.block_end = 0;
       
  1014     c.operation = QTextUndoCommand::MoveCursor;
       
  1015     c.format = 0;
       
  1016     c.strPos = 0;
       
  1017     c.pos = 0;
       
  1018     c.blockFormat = 0;
       
  1019 
       
  1020     c.custom = item;
       
  1021     appendUndoItem(c);
       
  1022 }
       
  1023 
       
  1024 void QTextDocumentPrivate::appendUndoItem(const QTextUndoCommand &c)
       
  1025 {
       
  1026     PMDEBUG("appendUndoItem, command=%d enabled=%d", c.command, undoEnabled);
       
  1027     if (!undoEnabled)
       
  1028         return;
       
  1029     if (undoState < undoStack.size())
       
  1030         truncateUndoStack();
       
  1031 
       
  1032     if (!undoStack.isEmpty() && modified) {
       
  1033         QTextUndoCommand &last = undoStack[undoState - 1];
       
  1034 
       
  1035         if ( (last.block_part && c.block_part && !last.block_end) // part of the same block => can merge
       
  1036             || (!c.block_part && !last.block_part)) {  // two single undo items => can merge
       
  1037 
       
  1038             if (last.tryMerge(c))
       
  1039                 return;
       
  1040         }
       
  1041     }
       
  1042     if (modifiedState > undoState)
       
  1043         modifiedState = -1;
       
  1044     undoStack.append(c);
       
  1045     undoState++;
       
  1046     emitUndoAvailable(true);
       
  1047     emitRedoAvailable(false);
       
  1048 
       
  1049     if (!c.block_part)
       
  1050         emit document()->undoCommandAdded();
       
  1051 }
       
  1052 
       
  1053 void QTextDocumentPrivate::truncateUndoStack()
       
  1054 {
       
  1055     if (undoState == undoStack.size())
       
  1056         return;
       
  1057 
       
  1058     for (int i = undoState; i < undoStack.size(); ++i) {
       
  1059         QTextUndoCommand c = undoStack[i];
       
  1060         if (c.command & QTextUndoCommand::Removed) {
       
  1061             // ########
       
  1062 //             QTextFragment *f = c.fragment_list;
       
  1063 //             while (f) {
       
  1064 //                 QTextFragment *n = f->right;
       
  1065 //                 delete f;
       
  1066 //                 f = n;
       
  1067 //             }
       
  1068         } else if (c.command & QTextUndoCommand::Custom) {
       
  1069             delete c.custom;
       
  1070         }
       
  1071     }
       
  1072     undoStack.resize(undoState);
       
  1073 }
       
  1074 
       
  1075 void QTextDocumentPrivate::emitUndoAvailable(bool available)
       
  1076 {
       
  1077     if (available != wasUndoAvailable) {
       
  1078         Q_Q(QTextDocument);
       
  1079         emit q->undoAvailable(available);
       
  1080         wasUndoAvailable = available;
       
  1081     }
       
  1082 }
       
  1083 
       
  1084 void QTextDocumentPrivate::emitRedoAvailable(bool available)
       
  1085 {
       
  1086     if (available != wasRedoAvailable) {
       
  1087         Q_Q(QTextDocument);
       
  1088         emit q->redoAvailable(available);
       
  1089         wasRedoAvailable = available;
       
  1090     }
       
  1091 }
       
  1092 
       
  1093 void QTextDocumentPrivate::enableUndoRedo(bool enable)
       
  1094 {
       
  1095     if (enable && maximumBlockCount > 0)
       
  1096         return;
       
  1097 
       
  1098     if (!enable) {
       
  1099         undoState = 0;
       
  1100         truncateUndoStack();
       
  1101         emitUndoAvailable(false);
       
  1102         emitRedoAvailable(false);
       
  1103     }
       
  1104     modifiedState = modified ? -1 : undoState;
       
  1105     undoEnabled = enable;
       
  1106     if (!undoEnabled)
       
  1107         compressPieceTable();
       
  1108 }
       
  1109 
       
  1110 void QTextDocumentPrivate::joinPreviousEditBlock()
       
  1111 {
       
  1112     beginEditBlock();
       
  1113 
       
  1114     if (undoEnabled && undoState)
       
  1115         undoStack[undoState - 1].block_end = false;
       
  1116 }
       
  1117 
       
  1118 void QTextDocumentPrivate::endEditBlock()
       
  1119 {
       
  1120     Q_ASSERT(editBlock > 0);
       
  1121     if (--editBlock)
       
  1122         return;
       
  1123 
       
  1124     if (undoEnabled && undoState > 0) {
       
  1125         const bool wasBlocking = !undoStack[undoState - 1].block_end;
       
  1126         if (undoStack[undoState - 1].block_part) {
       
  1127             undoStack[undoState - 1].block_end = true;
       
  1128             if (wasBlocking)
       
  1129                 emit document()->undoCommandAdded();
       
  1130         }
       
  1131     }
       
  1132 
       
  1133     finishEdit();
       
  1134 }
       
  1135 
       
  1136 void QTextDocumentPrivate::finishEdit()
       
  1137 {
       
  1138     Q_Q(QTextDocument);
       
  1139 
       
  1140     if (editBlock)
       
  1141         return;
       
  1142 
       
  1143     if (framesDirty)
       
  1144         scan_frames(docChangeFrom, docChangeOldLength, docChangeLength);
       
  1145 
       
  1146     if (lout && docChangeFrom >= 0) {
       
  1147         if (!inContentsChange) {
       
  1148             inContentsChange = true;
       
  1149             emit q->contentsChange(docChangeFrom, docChangeOldLength, docChangeLength);
       
  1150             inContentsChange = false;
       
  1151         }
       
  1152         lout->documentChanged(docChangeFrom, docChangeOldLength, docChangeLength);
       
  1153     }
       
  1154 
       
  1155     docChangeFrom = -1;
       
  1156 
       
  1157     if (needsEnsureMaximumBlockCount) {
       
  1158         needsEnsureMaximumBlockCount = false;
       
  1159         if (ensureMaximumBlockCount()) {
       
  1160             // if ensureMaximumBlockCount() returns true
       
  1161             // it will have called endEditBlock() and
       
  1162             // compressPieceTable() itself, so we return here
       
  1163             // to prevent getting two contentsChanged emits
       
  1164             return;
       
  1165         }
       
  1166     }
       
  1167 
       
  1168     while (!changedCursors.isEmpty()) {
       
  1169         QTextCursorPrivate *curs = changedCursors.takeFirst();
       
  1170         emit q->cursorPositionChanged(QTextCursor(curs));
       
  1171     }
       
  1172 
       
  1173     contentsChanged();
       
  1174 
       
  1175     if (blocks.numNodes() != lastBlockCount) {
       
  1176         lastBlockCount = blocks.numNodes();
       
  1177         emit q->blockCountChanged(lastBlockCount);
       
  1178     }
       
  1179 
       
  1180     if (!undoEnabled && unreachableCharacterCount)
       
  1181         compressPieceTable();
       
  1182 }
       
  1183 
       
  1184 void QTextDocumentPrivate::documentChange(int from, int length)
       
  1185 {
       
  1186 //     qDebug("QTextDocumentPrivate::documentChange: from=%d,length=%d", from, length);
       
  1187     if (docChangeFrom < 0) {
       
  1188         docChangeFrom = from;
       
  1189         docChangeOldLength = length;
       
  1190         docChangeLength = length;
       
  1191         return;
       
  1192     }
       
  1193     int start = qMin(from, docChangeFrom);
       
  1194     int end = qMax(from + length, docChangeFrom + docChangeLength);
       
  1195     int diff = qMax(0, end - start - docChangeLength);
       
  1196     docChangeFrom = start;
       
  1197     docChangeOldLength += diff;
       
  1198     docChangeLength += diff;
       
  1199 }
       
  1200 
       
  1201 /*
       
  1202     adjustDocumentChangesAndCursors is called whenever there is an insert or remove of characters.
       
  1203     param from is the cursor position in the document
       
  1204     param addedOrRemoved is the amount of characters added or removed.  A negative number means characters are removed.
       
  1205 
       
  1206     The function stores information to be emitted when finishEdit() is called.
       
  1207 */
       
  1208 void QTextDocumentPrivate::adjustDocumentChangesAndCursors(int from, int addedOrRemoved, QTextUndoCommand::Operation op)
       
  1209 {
       
  1210     if (!editBlock)
       
  1211         ++revision;
       
  1212 
       
  1213     for (int i = 0; i < cursors.size(); ++i) {
       
  1214         QTextCursorPrivate *curs = cursors.at(i);
       
  1215         if (curs->adjustPosition(from, addedOrRemoved, op) == QTextCursorPrivate::CursorMoved) {
       
  1216             if (!changedCursors.contains(curs))
       
  1217                 changedCursors.append(curs);
       
  1218         }
       
  1219     }
       
  1220 
       
  1221 //     qDebug("QTextDocumentPrivate::adjustDocumentChanges: from=%d,addedOrRemoved=%d", from, addedOrRemoved);
       
  1222     if (docChangeFrom < 0) {
       
  1223         docChangeFrom = from;
       
  1224         if (addedOrRemoved > 0) {
       
  1225             docChangeOldLength = 0;
       
  1226             docChangeLength = addedOrRemoved;
       
  1227         } else {
       
  1228             docChangeOldLength = -addedOrRemoved;
       
  1229             docChangeLength = 0;
       
  1230         }
       
  1231 //         qDebug("adjustDocumentChanges:");
       
  1232 //         qDebug("    -> %d %d %d", docChangeFrom, docChangeOldLength, docChangeLength);
       
  1233         return;
       
  1234     }
       
  1235 
       
  1236     // have to merge the new change with the already existing one.
       
  1237     int added = qMax(0, addedOrRemoved);
       
  1238     int removed = qMax(0, -addedOrRemoved);
       
  1239 
       
  1240     int diff = 0;
       
  1241     if(from + removed < docChangeFrom)
       
  1242         diff = docChangeFrom - from - removed;
       
  1243     else if(from > docChangeFrom + docChangeLength)
       
  1244         diff = from - (docChangeFrom + docChangeLength);
       
  1245 
       
  1246     int overlap_start = qMax(from, docChangeFrom);
       
  1247     int overlap_end = qMin(from + removed, docChangeFrom + docChangeLength);
       
  1248     int removedInside = qMax(0, overlap_end - overlap_start);
       
  1249     removed -= removedInside;
       
  1250 
       
  1251 //     qDebug("adjustDocumentChanges: from=%d, addedOrRemoved=%d, diff=%d, removedInside=%d", from, addedOrRemoved, diff, removedInside);
       
  1252     docChangeFrom = qMin(docChangeFrom, from);
       
  1253     docChangeOldLength += removed + diff;
       
  1254     docChangeLength += added - removedInside + diff;
       
  1255 //     qDebug("    -> %d %d %d", docChangeFrom, docChangeOldLength, docChangeLength);
       
  1256 
       
  1257 }
       
  1258 
       
  1259 
       
  1260 QString QTextDocumentPrivate::plainText() const
       
  1261 {
       
  1262     QString result;
       
  1263     result.resize(length());
       
  1264     const QChar *text_unicode = text.unicode();
       
  1265     QChar *data = result.data();
       
  1266     for (QTextDocumentPrivate::FragmentIterator it = begin(); it != end(); ++it) {
       
  1267         const QTextFragmentData *f = *it;
       
  1268         ::memcpy(data, text_unicode + f->stringPosition, f->size_array[0] * sizeof(QChar));
       
  1269         data += f->size_array[0];
       
  1270     }
       
  1271     // remove trailing block separator
       
  1272     result.chop(1);
       
  1273     return result;
       
  1274 }
       
  1275 
       
  1276 int QTextDocumentPrivate::blockCharFormatIndex(int node) const
       
  1277 {
       
  1278     int pos = blocks.position(node);
       
  1279     if (pos == 0)
       
  1280         return initialBlockCharFormatIndex;
       
  1281 
       
  1282     return fragments.find(pos - 1)->format;
       
  1283 }
       
  1284 
       
  1285 int QTextDocumentPrivate::nextCursorPosition(int position, QTextLayout::CursorMode mode) const
       
  1286 {
       
  1287     if (position == length()-1)
       
  1288         return position;
       
  1289 
       
  1290     QTextBlock it = blocksFind(position);
       
  1291     int start = it.position();
       
  1292     int end = start + it.length() - 1;
       
  1293     if (position == end)
       
  1294         return end + 1;
       
  1295 
       
  1296     return it.layout()->nextCursorPosition(position-start, mode) + start;
       
  1297 }
       
  1298 
       
  1299 int QTextDocumentPrivate::previousCursorPosition(int position, QTextLayout::CursorMode mode) const
       
  1300 {
       
  1301     if (position == 0)
       
  1302         return position;
       
  1303 
       
  1304     QTextBlock it = blocksFind(position);
       
  1305     int start = it.position();
       
  1306     if (position == start)
       
  1307         return start - 1;
       
  1308 
       
  1309     return it.layout()->previousCursorPosition(position-start, mode) + start;
       
  1310 }
       
  1311 
       
  1312 void QTextDocumentPrivate::changeObjectFormat(QTextObject *obj, int format)
       
  1313 {
       
  1314     beginEditBlock();
       
  1315     int objectIndex = obj->objectIndex();
       
  1316     int oldFormatIndex = formats.objectFormatIndex(objectIndex);
       
  1317     formats.setObjectFormatIndex(objectIndex, format);
       
  1318 
       
  1319     QTextBlockGroup *b = qobject_cast<QTextBlockGroup *>(obj);
       
  1320     if (b) {
       
  1321         b->d_func()->markBlocksDirty();
       
  1322     }
       
  1323     QTextFrame *f = qobject_cast<QTextFrame *>(obj);
       
  1324     if (f)
       
  1325         documentChange(f->firstPosition(), f->lastPosition() - f->firstPosition());
       
  1326 
       
  1327     QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::GroupFormatChange, (editBlock != 0), QTextUndoCommand::MoveCursor, oldFormatIndex,
       
  1328                             0, 0, obj->d_func()->objectIndex, 0);
       
  1329     appendUndoItem(c);
       
  1330 
       
  1331     endEditBlock();
       
  1332 }
       
  1333 
       
  1334 static QTextFrame *findChildFrame(QTextFrame *f, int pos)
       
  1335 {
       
  1336     // ##### use binary search
       
  1337     QList<QTextFrame *> children = f->childFrames();
       
  1338     for (int i = 0; i < children.size(); ++i) {
       
  1339         QTextFrame *c = children.at(i);
       
  1340         if (pos >= c->firstPosition() && pos <= c->lastPosition())
       
  1341             return c;
       
  1342     }
       
  1343     return 0;
       
  1344 }
       
  1345 
       
  1346 QTextFrame *QTextDocumentPrivate::rootFrame() const
       
  1347 {
       
  1348     if (!rtFrame) {
       
  1349         QTextFrameFormat defaultRootFrameFormat;
       
  1350         defaultRootFrameFormat.setMargin(documentMargin);
       
  1351         rtFrame = qobject_cast<QTextFrame *>(const_cast<QTextDocumentPrivate *>(this)->createObject(defaultRootFrameFormat));
       
  1352     }
       
  1353     return rtFrame;
       
  1354 }
       
  1355 
       
  1356 QTextFrame *QTextDocumentPrivate::frameAt(int pos) const
       
  1357 {
       
  1358     QTextFrame *f = rootFrame();
       
  1359 
       
  1360     while (1) {
       
  1361         QTextFrame *c = findChildFrame(f, pos);
       
  1362         if (!c)
       
  1363             return f;
       
  1364         f = c;
       
  1365     }
       
  1366 }
       
  1367 
       
  1368 void QTextDocumentPrivate::clearFrame(QTextFrame *f)
       
  1369 {
       
  1370     for (int i = 0; i < f->d_func()->childFrames.count(); ++i)
       
  1371         clearFrame(f->d_func()->childFrames.at(i));
       
  1372     f->d_func()->childFrames.clear();
       
  1373     f->d_func()->parentFrame = 0;
       
  1374 }
       
  1375 
       
  1376 void QTextDocumentPrivate::scan_frames(int pos, int charsRemoved, int charsAdded)
       
  1377 {
       
  1378     // ###### optimise
       
  1379     Q_UNUSED(pos);
       
  1380     Q_UNUSED(charsRemoved);
       
  1381     Q_UNUSED(charsAdded);
       
  1382 
       
  1383     QTextFrame *f = rootFrame();
       
  1384     clearFrame(f);
       
  1385 
       
  1386     for (FragmentIterator it = begin(); it != end(); ++it) {
       
  1387         // QTextFormat fmt = formats.format(it->format);
       
  1388         QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(it->format));
       
  1389         if (!frame)
       
  1390             continue;
       
  1391 
       
  1392         Q_ASSERT(it.size() == 1);
       
  1393         QChar ch = text.at(it->stringPosition);
       
  1394 
       
  1395         if (ch == QTextBeginningOfFrame) {
       
  1396             if (f != frame) {
       
  1397                 // f == frame happens for tables
       
  1398                 Q_ASSERT(frame->d_func()->fragment_start == it.n || frame->d_func()->fragment_start == 0);
       
  1399                 frame->d_func()->parentFrame = f;
       
  1400                 f->d_func()->childFrames.append(frame);
       
  1401                 f = frame;
       
  1402             }
       
  1403         } else if (ch == QTextEndOfFrame) {
       
  1404             Q_ASSERT(f == frame);
       
  1405             Q_ASSERT(frame->d_func()->fragment_end == it.n || frame->d_func()->fragment_end == 0);
       
  1406             f = frame->d_func()->parentFrame;
       
  1407         } else if (ch == QChar::ObjectReplacementCharacter) {
       
  1408             Q_ASSERT(f != frame);
       
  1409             Q_ASSERT(frame->d_func()->fragment_start == it.n || frame->d_func()->fragment_start == 0);
       
  1410             Q_ASSERT(frame->d_func()->fragment_end == it.n || frame->d_func()->fragment_end == 0);
       
  1411             frame->d_func()->parentFrame = f;
       
  1412             f->d_func()->childFrames.append(frame);
       
  1413         } else {
       
  1414             Q_ASSERT(false);
       
  1415         }
       
  1416     }
       
  1417     Q_ASSERT(f == rtFrame);
       
  1418     framesDirty = false;
       
  1419 }
       
  1420 
       
  1421 void QTextDocumentPrivate::insert_frame(QTextFrame *f)
       
  1422 {
       
  1423     int start = f->firstPosition();
       
  1424     int end = f->lastPosition();
       
  1425     QTextFrame *parent = frameAt(start-1);
       
  1426     Q_ASSERT(parent == frameAt(end+1));
       
  1427 
       
  1428     if (start != end) {
       
  1429         // iterator over the parent and move all children contained in my frame to myself
       
  1430         for (int i = 0; i < parent->d_func()->childFrames.size(); ++i) {
       
  1431             QTextFrame *c = parent->d_func()->childFrames.at(i);
       
  1432             if (start < c->firstPosition() && end > c->lastPosition()) {
       
  1433                 parent->d_func()->childFrames.removeAt(i);
       
  1434                 f->d_func()->childFrames.append(c);
       
  1435                 c->d_func()->parentFrame = f;
       
  1436             }
       
  1437         }
       
  1438     }
       
  1439     // insert at the correct position
       
  1440     int i = 0;
       
  1441     for (; i < parent->d_func()->childFrames.size(); ++i) {
       
  1442         QTextFrame *c = parent->d_func()->childFrames.at(i);
       
  1443         if (c->firstPosition() > end)
       
  1444             break;
       
  1445     }
       
  1446     parent->d_func()->childFrames.insert(i, f);
       
  1447     f->d_func()->parentFrame = parent;
       
  1448 }
       
  1449 
       
  1450 QTextFrame *QTextDocumentPrivate::insertFrame(int start, int end, const QTextFrameFormat &format)
       
  1451 {
       
  1452     Q_ASSERT(start >= 0 && start < length());
       
  1453     Q_ASSERT(end >= 0 && end < length());
       
  1454     Q_ASSERT(start <= end || end == -1);
       
  1455 
       
  1456     if (start != end && frameAt(start) != frameAt(end))
       
  1457         return 0;
       
  1458 
       
  1459     beginEditBlock();
       
  1460 
       
  1461     QTextFrame *frame = qobject_cast<QTextFrame *>(createObject(format));
       
  1462     Q_ASSERT(frame);
       
  1463 
       
  1464     // #### using the default block and char format below might be wrong
       
  1465     int idx = formats.indexForFormat(QTextBlockFormat());
       
  1466     QTextCharFormat cfmt;
       
  1467     cfmt.setObjectIndex(frame->objectIndex());
       
  1468     int charIdx = formats.indexForFormat(cfmt);
       
  1469 
       
  1470     insertBlock(QTextBeginningOfFrame, start, idx, charIdx, QTextUndoCommand::MoveCursor);
       
  1471     insertBlock(QTextEndOfFrame, ++end, idx, charIdx, QTextUndoCommand::KeepCursor);
       
  1472 
       
  1473     frame->d_func()->fragment_start = find(start).n;
       
  1474     frame->d_func()->fragment_end = find(end).n;
       
  1475 
       
  1476     insert_frame(frame);
       
  1477 
       
  1478     endEditBlock();
       
  1479 
       
  1480     return frame;
       
  1481 }
       
  1482 
       
  1483 void QTextDocumentPrivate::removeFrame(QTextFrame *frame)
       
  1484 {
       
  1485     QTextFrame *parent = frame->d_func()->parentFrame;
       
  1486     if (!parent)
       
  1487         return;
       
  1488 
       
  1489     int start = frame->firstPosition();
       
  1490     int end = frame->lastPosition();
       
  1491     Q_ASSERT(end >= start);
       
  1492 
       
  1493     beginEditBlock();
       
  1494 
       
  1495     // remove already removes the frames from the tree
       
  1496     remove(end, 1);
       
  1497     remove(start-1, 1);
       
  1498 
       
  1499     endEditBlock();
       
  1500 }
       
  1501 
       
  1502 QTextObject *QTextDocumentPrivate::objectForIndex(int objectIndex) const
       
  1503 {
       
  1504     if (objectIndex < 0)
       
  1505         return 0;
       
  1506 
       
  1507     QTextObject *object = objects.value(objectIndex, 0);
       
  1508     if (!object) {
       
  1509         QTextDocumentPrivate *that = const_cast<QTextDocumentPrivate *>(this);
       
  1510         QTextFormat fmt = formats.objectFormat(objectIndex);
       
  1511         object = that->createObject(fmt, objectIndex);
       
  1512     }
       
  1513     return object;
       
  1514 }
       
  1515 
       
  1516 QTextObject *QTextDocumentPrivate::objectForFormat(int formatIndex) const
       
  1517 {
       
  1518     int objectIndex = formats.format(formatIndex).objectIndex();
       
  1519     return objectForIndex(objectIndex);
       
  1520 }
       
  1521 
       
  1522 QTextObject *QTextDocumentPrivate::objectForFormat(const QTextFormat &f) const
       
  1523 {
       
  1524     return objectForIndex(f.objectIndex());
       
  1525 }
       
  1526 
       
  1527 QTextObject *QTextDocumentPrivate::createObject(const QTextFormat &f, int objectIndex)
       
  1528 {
       
  1529     QTextObject *obj = document()->createObject(f);
       
  1530 
       
  1531     if (obj) {
       
  1532         obj->d_func()->objectIndex = objectIndex == -1 ? formats.createObjectIndex(f) : objectIndex;
       
  1533         objects[obj->d_func()->objectIndex] = obj;
       
  1534     }
       
  1535 
       
  1536     return obj;
       
  1537 }
       
  1538 
       
  1539 void QTextDocumentPrivate::deleteObject(QTextObject *object)
       
  1540 {
       
  1541     const int objIdx = object->d_func()->objectIndex;
       
  1542     objects.remove(objIdx);
       
  1543     delete object;
       
  1544 }
       
  1545 
       
  1546 void QTextDocumentPrivate::contentsChanged()
       
  1547 {
       
  1548     Q_Q(QTextDocument);
       
  1549     if (editBlock)
       
  1550         return;
       
  1551 
       
  1552     bool m = undoEnabled ? (modifiedState != undoState) : true;
       
  1553     if (modified != m) {
       
  1554         modified = m;
       
  1555         emit q->modificationChanged(modified);
       
  1556     }
       
  1557 
       
  1558     emit q->contentsChanged();
       
  1559 }
       
  1560 
       
  1561 void QTextDocumentPrivate::compressPieceTable()
       
  1562 {
       
  1563     if (undoEnabled)
       
  1564         return;
       
  1565 
       
  1566     const uint garbageCollectionThreshold = 96 * 1024; // bytes
       
  1567 
       
  1568     //qDebug() << "unreachable bytes:" << unreachableCharacterCount * sizeof(QChar) << " -- limit" << garbageCollectionThreshold << "text size =" << text.size() << "capacity:" << text.capacity();
       
  1569 
       
  1570     bool compressTable = unreachableCharacterCount * sizeof(QChar) > garbageCollectionThreshold
       
  1571                          && text.size() >= text.capacity() * 0.9;
       
  1572     if (!compressTable)
       
  1573         return;
       
  1574 
       
  1575     QString newText;
       
  1576     newText.resize(text.size());
       
  1577     QChar *newTextPtr = newText.data();
       
  1578     int newLen = 0;
       
  1579 
       
  1580     for (FragmentMap::Iterator it = fragments.begin(); !it.atEnd(); ++it) {
       
  1581         qMemCopy(newTextPtr, text.constData() + it->stringPosition, it->size_array[0] * sizeof(QChar));
       
  1582         it->stringPosition = newLen;
       
  1583         newTextPtr += it->size_array[0];
       
  1584         newLen += it->size_array[0];
       
  1585     }
       
  1586 
       
  1587     newText.resize(newLen);
       
  1588     newText.squeeze();
       
  1589     //qDebug() << "removed" << text.size() - newText.size() << "characters";
       
  1590     text = newText;
       
  1591     unreachableCharacterCount = 0;
       
  1592 }
       
  1593 
       
  1594 void QTextDocumentPrivate::setModified(bool m)
       
  1595 {
       
  1596     Q_Q(QTextDocument);
       
  1597     if (m == modified)
       
  1598         return;
       
  1599 
       
  1600     modified = m;
       
  1601     if (!modified)
       
  1602         modifiedState = undoState;
       
  1603     else
       
  1604         modifiedState = -1;
       
  1605 
       
  1606     emit q->modificationChanged(modified);
       
  1607 }
       
  1608 
       
  1609 bool QTextDocumentPrivate::ensureMaximumBlockCount()
       
  1610 {
       
  1611     if (maximumBlockCount <= 0)
       
  1612         return false;
       
  1613     if (blocks.numNodes() <= maximumBlockCount)
       
  1614         return false;
       
  1615 
       
  1616     beginEditBlock();
       
  1617 
       
  1618     const int blocksToRemove = blocks.numNodes() - maximumBlockCount;
       
  1619     QTextCursor cursor(this, 0);
       
  1620     cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor, blocksToRemove);
       
  1621 
       
  1622     unreachableCharacterCount += cursor.selectionEnd() - cursor.selectionStart();
       
  1623 
       
  1624     // preserve the char format of the paragraph that is to become the new first one
       
  1625     QTextCharFormat charFmt = cursor.blockCharFormat();
       
  1626     cursor.removeSelectedText();
       
  1627     cursor.setBlockCharFormat(charFmt);
       
  1628 
       
  1629     endEditBlock();
       
  1630 
       
  1631     compressPieceTable();
       
  1632 
       
  1633     return true;
       
  1634 }
       
  1635 
       
  1636 /// This method is called from QTextTable when it is about to remove a table-cell to allow cursors to update their selection.
       
  1637 void QTextDocumentPrivate::aboutToRemoveCell(int from, int to)
       
  1638 {
       
  1639     Q_ASSERT(from <= to);
       
  1640     for (int i = 0; i < cursors.size(); ++i)
       
  1641         cursors.at(i)->aboutToRemoveCell(from, to);
       
  1642 }
       
  1643 
       
  1644 QT_END_NAMESPACE