src/gui/text/qtextdocument_p.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtGui module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include <private/qtools_p.h>
       
    43 #include <qdebug.h>
       
    44 
       
    45 #include "qtextdocument_p.h"
       
    46 #include "qtextdocument.h"
       
    47 #include <qtextformat.h>
       
    48 #include "qtextformat_p.h"
       
    49 #include "qtextobject_p.h"
       
    50 #include "qtextcursor.h"
       
    51 #include "qtextimagehandler_p.h"
       
    52 #include "qtextcursor_p.h"
       
    53 #include "qtextdocumentlayout_p.h"
       
    54 #include "qtexttable.h"
       
    55 #include "qtextengine_p.h"
       
    56 
       
    57 #include <stdlib.h>
       
    58 
       
    59 QT_BEGIN_NAMESPACE
       
    60 
       
    61 #define PMDEBUG if(0) qDebug
       
    62 
       
    63 // The VxWorks DIAB compiler crashes when initializing the anonymouse union with { a7 }
       
    64 #if !defined(Q_CC_DIAB)
       
    65 #  define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8) \
       
    66           QTextUndoCommand c = { a1, a2, 0, 0, a3, a4, a5, a6, { a7 }, a8 }
       
    67 #else
       
    68 #  define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8) \
       
    69           QTextUndoCommand c = { a1, a2, 0, 0, a3, a4, a5, a6 }; c.blockFormat = a7; c.revision = a8
       
    70 #endif
       
    71 
       
    72 /*
       
    73   Structure of a document:
       
    74 
       
    75   DOCUMENT :== FRAME_CONTENTS
       
    76   FRAME :== START_OF_FRAME  FRAME_CONTENTS END_OF_FRAME
       
    77   FRAME_CONTENTS = LIST_OF_BLOCKS ((FRAME | TABLE) LIST_OF_BLOCKS)*
       
    78   TABLE :== (START_OF_FRAME TABLE_CELL)+ END_OF_FRAME
       
    79   TABLE_CELL = FRAME_CONTENTS
       
    80   LIST_OF_BLOCKS :== (BLOCK END_OF_PARA)* BLOCK
       
    81   BLOCK :== (FRAGMENT)*
       
    82   FRAGMENT :== String of characters
       
    83 
       
    84   END_OF_PARA :== 0x2029 # Paragraph separator in Unicode
       
    85   START_OF_FRAME :== 0xfdd0
       
    86   END_OF_FRAME := 0xfdd1
       
    87 
       
    88   Note also that LIST_OF_BLOCKS can be empty. Nevertheless, there is
       
    89   at least one valid cursor position there where you could start
       
    90   typing. The block format is in this case determined by the last
       
    91   END_OF_PARA/START_OF_FRAME/END_OF_FRAME (see below).
       
    92 
       
    93   Lists are not in here, as they are treated specially. A list is just
       
    94   a collection of (not neccessarily connected) blocks, that share the
       
    95   same objectIndex() in the format that refers to the list format and
       
    96   object.
       
    97 
       
    98   The above does not clearly note where formats are. Here's
       
    99   how it looks currently:
       
   100 
       
   101   FRAGMENT: one charFormat associated
       
   102 
       
   103   END_OF_PARA: one charFormat, and a blockFormat for the _next_ block.
       
   104 
       
   105   START_OF_FRAME: one char format, and a blockFormat (for the next
       
   106   block). The format associated with the objectIndex() of the
       
   107   charFormat decides whether this is a frame or table and its
       
   108   properties
       
   109 
       
   110   END_OF_FRAME: one charFormat and a blockFormat (for the next
       
   111   block). The object() of the charFormat is the same as for the
       
   112   corresponding START_OF_BLOCK.
       
   113 
       
   114 
       
   115   The document is independent of the layout with certain restrictions:
       
   116 
       
   117   * Cursor movement (esp. up and down) depend on the layout.
       
   118   * You cannot have more than one layout, as the layout data of QTextObjects
       
   119     is stored in the text object itself.
       
   120 
       
   121 */
       
   122 
       
   123 void QTextBlockData::invalidate() const
       
   124 {
       
   125     if (layout)
       
   126         layout->engine()->invalidate();
       
   127 }
       
   128 
       
   129 static bool isValidBlockSeparator(const QChar &ch)
       
   130 {
       
   131     return ch == QChar::ParagraphSeparator
       
   132         || ch == QTextBeginningOfFrame
       
   133         || ch == QTextEndOfFrame;
       
   134 }
       
   135 
       
   136 #ifndef QT_NO_DEBUG
       
   137 static bool noBlockInString(const QString &str)
       
   138 {
       
   139     return !str.contains(QChar::ParagraphSeparator)
       
   140         && !str.contains(QTextBeginningOfFrame)
       
   141         && !str.contains(QTextEndOfFrame);
       
   142 }
       
   143 #endif
       
   144 
       
   145 bool QTextUndoCommand::tryMerge(const QTextUndoCommand &other)
       
   146 {
       
   147     if (command != other.command)
       
   148         return false;
       
   149 
       
   150     if (command == Inserted
       
   151         && (pos + length == other.pos)
       
   152         && (strPos + length == other.strPos)
       
   153         && format == other.format) {
       
   154 
       
   155         length += other.length;
       
   156         return true;
       
   157     }
       
   158 
       
   159     // removal to the 'right' using 'Delete' key
       
   160     if (command == Removed
       
   161         && pos == other.pos
       
   162         && (strPos + length == other.strPos)
       
   163         && format == other.format) {
       
   164 
       
   165         length += other.length;
       
   166         return true;
       
   167     }
       
   168 
       
   169     // removal to the 'left' using 'Backspace'
       
   170     if (command == Removed
       
   171         && (other.pos + other.length == pos)
       
   172         && (other.strPos + other.length == strPos)
       
   173         && (format == other.format)) {
       
   174 
       
   175         int l = length;
       
   176         (*this) = other;
       
   177 
       
   178         length += l;
       
   179         return true;
       
   180     }
       
   181 
       
   182     return false;
       
   183 }
       
   184 
       
   185 QTextDocumentPrivate::QTextDocumentPrivate()
       
   186     : wasUndoAvailable(false),
       
   187     wasRedoAvailable(false),
       
   188     docChangeOldLength(0),
       
   189     docChangeLength(0),
       
   190     framesDirty(true),
       
   191     rtFrame(0),
       
   192     initialBlockCharFormatIndex(-1) // set correctly later in init()
       
   193 {
       
   194     editBlock = 0;
       
   195     docChangeFrom = -1;
       
   196 
       
   197     undoState = 0;
       
   198     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     while (1) {
       
   874         if (undo)
       
   875             --undoState;
       
   876         QTextUndoCommand &c = undoStack[undoState];
       
   877         int resetBlockRevision = c.pos;
       
   878 
       
   879 	switch(c.command) {
       
   880         case QTextUndoCommand::Inserted:
       
   881             remove(c.pos, c.length, (QTextUndoCommand::Operation)c.operation);
       
   882             PMDEBUG("   erase: from %d, length %d", c.pos, c.length);
       
   883             c.command = QTextUndoCommand::Removed;
       
   884 	    break;
       
   885         case QTextUndoCommand::Removed:
       
   886             PMDEBUG("   insert: format %d (from %d, length %d, strpos=%d)", c.format, c.pos, c.length, c.strPos);
       
   887             insert_string(c.pos, c.strPos, c.length, c.format, (QTextUndoCommand::Operation)c.operation);
       
   888             c.command = QTextUndoCommand::Inserted;
       
   889 	    break;
       
   890 	case QTextUndoCommand::BlockInserted:
       
   891 	case QTextUndoCommand::BlockAdded:
       
   892             remove_block(c.pos, &c.blockFormat, c.command, (QTextUndoCommand::Operation)c.operation);
       
   893             PMDEBUG("   blockremove: from %d", c.pos);
       
   894 	    if (c.command == QTextUndoCommand::BlockInserted)
       
   895 		c.command = QTextUndoCommand::BlockRemoved;
       
   896 	    else
       
   897 		c.command = QTextUndoCommand::BlockDeleted;
       
   898 	    break;
       
   899 	case QTextUndoCommand::BlockRemoved:
       
   900 	case QTextUndoCommand::BlockDeleted:
       
   901             PMDEBUG("   blockinsert: charformat %d blockformat %d (pos %d, strpos=%d)", c.format, c.blockFormat, c.pos, c.strPos);
       
   902             insert_block(c.pos, c.strPos, c.format, c.blockFormat, (QTextUndoCommand::Operation)c.operation, c.command);
       
   903             resetBlockRevision += 1;
       
   904 	    if (c.command == QTextUndoCommand::BlockRemoved)
       
   905 		c.command = QTextUndoCommand::BlockInserted;
       
   906 	    else
       
   907 		c.command = QTextUndoCommand::BlockAdded;
       
   908 	    break;
       
   909 	case QTextUndoCommand::CharFormatChanged: {
       
   910             resetBlockRevision = -1; // ## TODO
       
   911             PMDEBUG("   charFormat: format %d (from %d, length %d)", c.format, c.pos, c.length);
       
   912             FragmentIterator it = find(c.pos);
       
   913             Q_ASSERT(!it.atEnd());
       
   914 
       
   915             int oldFormat = it.value()->format;
       
   916             setCharFormat(c.pos, c.length, formats.charFormat(c.format));
       
   917             c.format = oldFormat;
       
   918 	    break;
       
   919 	}
       
   920 	case QTextUndoCommand::BlockFormatChanged: {
       
   921             resetBlockRevision = -1; // ## TODO
       
   922             PMDEBUG("   blockformat: format %d pos %d", c.format, c.pos);
       
   923             QTextBlock it = blocksFind(c.pos);
       
   924             Q_ASSERT(it.isValid());
       
   925 
       
   926             int oldFormat = block(it)->format;
       
   927             block(it)->format = c.format;
       
   928             QTextBlockGroup *oldGroup = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(oldFormat)));
       
   929             QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(c.format)));
       
   930             c.format = oldFormat;
       
   931             if (group != oldGroup) {
       
   932                 if (oldGroup)
       
   933                     oldGroup->blockRemoved(it);
       
   934                 if (group)
       
   935                     group->blockInserted(it);
       
   936             } else if (group) {
       
   937                 group->blockFormatChanged(it);
       
   938             }
       
   939             documentChange(it.position(), it.length());
       
   940 	    break;
       
   941 	}
       
   942 	case QTextUndoCommand::GroupFormatChange: {
       
   943             resetBlockRevision = -1; // ## TODO
       
   944             PMDEBUG("   group format change");
       
   945             QTextObject *object = objectForIndex(c.objectIndex);
       
   946             int oldFormat = formats.objectFormatIndex(c.objectIndex);
       
   947             changeObjectFormat(object, c.format);
       
   948             c.format = oldFormat;
       
   949 	    break;
       
   950 	}
       
   951 	case QTextUndoCommand::Custom:
       
   952             resetBlockRevision = -1; // ## TODO
       
   953             if (undo)
       
   954                 c.custom->undo();
       
   955             else
       
   956                 c.custom->redo();
       
   957 	    break;
       
   958 	default:
       
   959 	    Q_ASSERT(false);
       
   960         }
       
   961 
       
   962         if (resetBlockRevision >= 0) {
       
   963             int b = blocks.findNode(resetBlockRevision);
       
   964             QTextBlockData *B = blocks.fragment(b);
       
   965             B->revision = c.revision;
       
   966         }
       
   967 
       
   968         if (!undo)
       
   969             ++undoState;
       
   970 
       
   971         bool inBlock = (
       
   972                 undoState > 0
       
   973                 && undoState < undoStack.size()
       
   974                 && undoStack[undoState].block_part
       
   975                 && undoStack[undoState-1].block_part
       
   976                 && !undoStack[undoState-1].block_end
       
   977                 );
       
   978         if (!inBlock)
       
   979             break;
       
   980     }
       
   981     undoEnabled = true;
       
   982     int editPos = -1;
       
   983     if (docChangeFrom >= 0) {
       
   984         editPos = qMin(docChangeFrom + docChangeLength, length() - 1);
       
   985     }
       
   986     endEditBlock();
       
   987     emitUndoAvailable(isUndoAvailable());
       
   988     emitRedoAvailable(isRedoAvailable());
       
   989     return editPos;
       
   990 }
       
   991 
       
   992 /*!
       
   993     Appends a custom undo \a item to the undo stack.
       
   994 */
       
   995 void QTextDocumentPrivate::appendUndoItem(QAbstractUndoItem *item)
       
   996 {
       
   997     if (!undoEnabled) {
       
   998         delete item;
       
   999         return;
       
  1000     }
       
  1001 
       
  1002     QTextUndoCommand c;
       
  1003     c.command = QTextUndoCommand::Custom;
       
  1004     c.block_part = editBlock != 0;
       
  1005     c.block_end = 0;
       
  1006     c.operation = QTextUndoCommand::MoveCursor;
       
  1007     c.format = 0;
       
  1008     c.strPos = 0;
       
  1009     c.pos = 0;
       
  1010     c.blockFormat = 0;
       
  1011 
       
  1012     c.custom = item;
       
  1013     appendUndoItem(c);
       
  1014 }
       
  1015 
       
  1016 void QTextDocumentPrivate::appendUndoItem(const QTextUndoCommand &c)
       
  1017 {
       
  1018     PMDEBUG("appendUndoItem, command=%d enabled=%d", c.command, undoEnabled);
       
  1019     if (!undoEnabled)
       
  1020         return;
       
  1021     if (undoState < undoStack.size())
       
  1022         truncateUndoStack();
       
  1023 
       
  1024     if (!undoStack.isEmpty() && modified) {
       
  1025         QTextUndoCommand &last = undoStack[undoState - 1];
       
  1026 
       
  1027         if ( (last.block_part && c.block_part && !last.block_end) // part of the same block => can merge
       
  1028             || (!c.block_part && !last.block_part)) {  // two single undo items => can merge
       
  1029 
       
  1030             if (last.tryMerge(c))
       
  1031                 return;
       
  1032         }
       
  1033     }
       
  1034     if (modifiedState > undoState)
       
  1035         modifiedState = -1;
       
  1036     undoStack.append(c);
       
  1037     undoState++;
       
  1038     emitUndoAvailable(true);
       
  1039     emitRedoAvailable(false);
       
  1040 
       
  1041     if (!c.block_part)
       
  1042         emit document()->undoCommandAdded();
       
  1043 }
       
  1044 
       
  1045 void QTextDocumentPrivate::truncateUndoStack()
       
  1046 {
       
  1047     if (undoState == undoStack.size())
       
  1048         return;
       
  1049 
       
  1050     for (int i = undoState; i < undoStack.size(); ++i) {
       
  1051         QTextUndoCommand c = undoStack[i];
       
  1052         if (c.command & QTextUndoCommand::Removed) {
       
  1053             // ########
       
  1054 //             QTextFragment *f = c.fragment_list;
       
  1055 //             while (f) {
       
  1056 //                 QTextFragment *n = f->right;
       
  1057 //                 delete f;
       
  1058 //                 f = n;
       
  1059 //             }
       
  1060         } else if (c.command & QTextUndoCommand::Custom) {
       
  1061             delete c.custom;
       
  1062         }
       
  1063     }
       
  1064     undoStack.resize(undoState);
       
  1065 }
       
  1066 
       
  1067 void QTextDocumentPrivate::emitUndoAvailable(bool available)
       
  1068 {
       
  1069     if (available != wasUndoAvailable) {
       
  1070         Q_Q(QTextDocument);
       
  1071         emit q->undoAvailable(available);
       
  1072         wasUndoAvailable = available;
       
  1073     }
       
  1074 }
       
  1075 
       
  1076 void QTextDocumentPrivate::emitRedoAvailable(bool available)
       
  1077 {
       
  1078     if (available != wasRedoAvailable) {
       
  1079         Q_Q(QTextDocument);
       
  1080         emit q->redoAvailable(available);
       
  1081         wasRedoAvailable = available;
       
  1082     }
       
  1083 }
       
  1084 
       
  1085 void QTextDocumentPrivate::enableUndoRedo(bool enable)
       
  1086 {
       
  1087     if (enable && maximumBlockCount > 0)
       
  1088         return;
       
  1089 
       
  1090     if (!enable) {
       
  1091         undoState = 0;
       
  1092         truncateUndoStack();
       
  1093         emitUndoAvailable(false);
       
  1094         emitRedoAvailable(false);
       
  1095     }
       
  1096     modifiedState = modified ? -1 : undoState;
       
  1097     undoEnabled = enable;
       
  1098     if (!undoEnabled)
       
  1099         compressPieceTable();
       
  1100 }
       
  1101 
       
  1102 void QTextDocumentPrivate::joinPreviousEditBlock()
       
  1103 {
       
  1104     beginEditBlock();
       
  1105 
       
  1106     if (undoEnabled && undoState)
       
  1107         undoStack[undoState - 1].block_end = false;
       
  1108 }
       
  1109 
       
  1110 void QTextDocumentPrivate::endEditBlock()
       
  1111 {
       
  1112     Q_ASSERT(editBlock > 0);
       
  1113     if (--editBlock)
       
  1114         return;
       
  1115 
       
  1116     if (undoEnabled && undoState > 0) {
       
  1117         if (undoStack[undoState - 1].block_part) {
       
  1118             undoStack[undoState - 1].block_end = true;
       
  1119             emit document()->undoCommandAdded();
       
  1120         }
       
  1121     }
       
  1122 
       
  1123     finishEdit();
       
  1124 }
       
  1125 
       
  1126 void QTextDocumentPrivate::finishEdit()
       
  1127 {
       
  1128     Q_Q(QTextDocument);
       
  1129 
       
  1130     if (editBlock)
       
  1131         return;
       
  1132 
       
  1133     if (framesDirty)
       
  1134         scan_frames(docChangeFrom, docChangeOldLength, docChangeLength);
       
  1135 
       
  1136     if (lout && docChangeFrom >= 0) {
       
  1137         if (!inContentsChange) {
       
  1138             inContentsChange = true;
       
  1139             emit q->contentsChange(docChangeFrom, docChangeOldLength, docChangeLength);
       
  1140             inContentsChange = false;
       
  1141         }
       
  1142         lout->documentChanged(docChangeFrom, docChangeOldLength, docChangeLength);
       
  1143     }
       
  1144 
       
  1145     docChangeFrom = -1;
       
  1146 
       
  1147     if (needsEnsureMaximumBlockCount) {
       
  1148         needsEnsureMaximumBlockCount = false;
       
  1149         if (ensureMaximumBlockCount()) {
       
  1150             // if ensureMaximumBlockCount() returns true
       
  1151             // it will have called endEditBlock() and
       
  1152             // compressPieceTable() itself, so we return here
       
  1153             // to prevent getting two contentsChanged emits
       
  1154             return;
       
  1155         }
       
  1156     }
       
  1157 
       
  1158     while (!changedCursors.isEmpty()) {
       
  1159         QTextCursorPrivate *curs = changedCursors.takeFirst();
       
  1160         emit q->cursorPositionChanged(QTextCursor(curs));
       
  1161     }
       
  1162 
       
  1163     contentsChanged();
       
  1164 
       
  1165     if (blocks.numNodes() != lastBlockCount) {
       
  1166         lastBlockCount = blocks.numNodes();
       
  1167         emit q->blockCountChanged(lastBlockCount);
       
  1168     }
       
  1169 
       
  1170     if (!undoEnabled && unreachableCharacterCount)
       
  1171         compressPieceTable();
       
  1172 }
       
  1173 
       
  1174 void QTextDocumentPrivate::documentChange(int from, int length)
       
  1175 {
       
  1176 //     qDebug("QTextDocumentPrivate::documentChange: from=%d,length=%d", from, length);
       
  1177     if (docChangeFrom < 0) {
       
  1178         docChangeFrom = from;
       
  1179         docChangeOldLength = length;
       
  1180         docChangeLength = length;
       
  1181         return;
       
  1182     }
       
  1183     int start = qMin(from, docChangeFrom);
       
  1184     int end = qMax(from + length, docChangeFrom + docChangeLength);
       
  1185     int diff = qMax(0, end - start - docChangeLength);
       
  1186     docChangeFrom = start;
       
  1187     docChangeOldLength += diff;
       
  1188     docChangeLength += diff;
       
  1189 }
       
  1190 
       
  1191 /*
       
  1192     adjustDocumentChangesAndCursors is called whenever there is an insert or remove of characters.
       
  1193     param from is the cursor position in the document
       
  1194     param addedOrRemoved is the amount of characters added or removed.  A negative number means characters are removed.
       
  1195 
       
  1196     The function stores information to be emitted when finishEdit() is called.
       
  1197 */
       
  1198 void QTextDocumentPrivate::adjustDocumentChangesAndCursors(int from, int addedOrRemoved, QTextUndoCommand::Operation op)
       
  1199 {
       
  1200     if (!editBlock)
       
  1201         ++revision;
       
  1202 
       
  1203     for (int i = 0; i < cursors.size(); ++i) {
       
  1204         QTextCursorPrivate *curs = cursors.at(i);
       
  1205         if (curs->adjustPosition(from, addedOrRemoved, op) == QTextCursorPrivate::CursorMoved) {
       
  1206             if (!changedCursors.contains(curs))
       
  1207                 changedCursors.append(curs);
       
  1208         }
       
  1209     }
       
  1210 
       
  1211 //     qDebug("QTextDocumentPrivate::adjustDocumentChanges: from=%d,addedOrRemoved=%d", from, addedOrRemoved);
       
  1212     if (docChangeFrom < 0) {
       
  1213         docChangeFrom = from;
       
  1214         if (addedOrRemoved > 0) {
       
  1215             docChangeOldLength = 0;
       
  1216             docChangeLength = addedOrRemoved;
       
  1217         } else {
       
  1218             docChangeOldLength = -addedOrRemoved;
       
  1219             docChangeLength = 0;
       
  1220         }
       
  1221 //         qDebug("adjustDocumentChanges:");
       
  1222 //         qDebug("    -> %d %d %d", docChangeFrom, docChangeOldLength, docChangeLength);
       
  1223         return;
       
  1224     }
       
  1225 
       
  1226     // have to merge the new change with the already existing one.
       
  1227     int added = qMax(0, addedOrRemoved);
       
  1228     int removed = qMax(0, -addedOrRemoved);
       
  1229 
       
  1230     int diff = 0;
       
  1231     if(from + removed < docChangeFrom)
       
  1232         diff = docChangeFrom - from - removed;
       
  1233     else if(from > docChangeFrom + docChangeLength)
       
  1234         diff = from - (docChangeFrom + docChangeLength);
       
  1235 
       
  1236     int overlap_start = qMax(from, docChangeFrom);
       
  1237     int overlap_end = qMin(from + removed, docChangeFrom + docChangeLength);
       
  1238     int removedInside = qMax(0, overlap_end - overlap_start);
       
  1239     removed -= removedInside;
       
  1240 
       
  1241 //     qDebug("adjustDocumentChanges: from=%d, addedOrRemoved=%d, diff=%d, removedInside=%d", from, addedOrRemoved, diff, removedInside);
       
  1242     docChangeFrom = qMin(docChangeFrom, from);
       
  1243     docChangeOldLength += removed + diff;
       
  1244     docChangeLength += added - removedInside + diff;
       
  1245 //     qDebug("    -> %d %d %d", docChangeFrom, docChangeOldLength, docChangeLength);
       
  1246 
       
  1247 }
       
  1248 
       
  1249 
       
  1250 QString QTextDocumentPrivate::plainText() const
       
  1251 {
       
  1252     QString result;
       
  1253     result.resize(length());
       
  1254     const QChar *text_unicode = text.unicode();
       
  1255     QChar *data = result.data();
       
  1256     for (QTextDocumentPrivate::FragmentIterator it = begin(); it != end(); ++it) {
       
  1257         const QTextFragmentData *f = *it;
       
  1258         ::memcpy(data, text_unicode + f->stringPosition, f->size_array[0] * sizeof(QChar));
       
  1259         data += f->size_array[0];
       
  1260     }
       
  1261     // remove trailing block separator
       
  1262     result.chop(1);
       
  1263     return result;
       
  1264 }
       
  1265 
       
  1266 int QTextDocumentPrivate::blockCharFormatIndex(int node) const
       
  1267 {
       
  1268     int pos = blocks.position(node);
       
  1269     if (pos == 0)
       
  1270         return initialBlockCharFormatIndex;
       
  1271 
       
  1272     return fragments.find(pos - 1)->format;
       
  1273 }
       
  1274 
       
  1275 int QTextDocumentPrivate::nextCursorPosition(int position, QTextLayout::CursorMode mode) const
       
  1276 {
       
  1277     if (position == length()-1)
       
  1278         return position;
       
  1279 
       
  1280     QTextBlock it = blocksFind(position);
       
  1281     int start = it.position();
       
  1282     int end = start + it.length() - 1;
       
  1283     if (position == end)
       
  1284         return end + 1;
       
  1285 
       
  1286     return it.layout()->nextCursorPosition(position-start, mode) + start;
       
  1287 }
       
  1288 
       
  1289 int QTextDocumentPrivate::previousCursorPosition(int position, QTextLayout::CursorMode mode) const
       
  1290 {
       
  1291     if (position == 0)
       
  1292         return position;
       
  1293 
       
  1294     QTextBlock it = blocksFind(position);
       
  1295     int start = it.position();
       
  1296     if (position == start)
       
  1297         return start - 1;
       
  1298 
       
  1299     return it.layout()->previousCursorPosition(position-start, mode) + start;
       
  1300 }
       
  1301 
       
  1302 void QTextDocumentPrivate::changeObjectFormat(QTextObject *obj, int format)
       
  1303 {
       
  1304     beginEditBlock();
       
  1305     int objectIndex = obj->objectIndex();
       
  1306     int oldFormatIndex = formats.objectFormatIndex(objectIndex);
       
  1307     formats.setObjectFormatIndex(objectIndex, format);
       
  1308 
       
  1309     QTextBlockGroup *b = qobject_cast<QTextBlockGroup *>(obj);
       
  1310     if (b) {
       
  1311         b->d_func()->markBlocksDirty();
       
  1312     }
       
  1313     QTextFrame *f = qobject_cast<QTextFrame *>(obj);
       
  1314     if (f)
       
  1315         documentChange(f->firstPosition(), f->lastPosition() - f->firstPosition());
       
  1316 
       
  1317     QT_INIT_TEXTUNDOCOMMAND(c, QTextUndoCommand::GroupFormatChange, (editBlock != 0), QTextUndoCommand::MoveCursor, oldFormatIndex,
       
  1318                             0, 0, obj->d_func()->objectIndex, 0);
       
  1319     appendUndoItem(c);
       
  1320 
       
  1321     endEditBlock();
       
  1322 }
       
  1323 
       
  1324 static QTextFrame *findChildFrame(QTextFrame *f, int pos)
       
  1325 {
       
  1326     // ##### use binary search
       
  1327     QList<QTextFrame *> children = f->childFrames();
       
  1328     for (int i = 0; i < children.size(); ++i) {
       
  1329         QTextFrame *c = children.at(i);
       
  1330         if (pos >= c->firstPosition() && pos <= c->lastPosition())
       
  1331             return c;
       
  1332     }
       
  1333     return 0;
       
  1334 }
       
  1335 
       
  1336 QTextFrame *QTextDocumentPrivate::rootFrame() const
       
  1337 {
       
  1338     if (!rtFrame) {
       
  1339         QTextFrameFormat defaultRootFrameFormat;
       
  1340         defaultRootFrameFormat.setMargin(documentMargin);
       
  1341         rtFrame = qobject_cast<QTextFrame *>(const_cast<QTextDocumentPrivate *>(this)->createObject(defaultRootFrameFormat));
       
  1342     }
       
  1343     return rtFrame;
       
  1344 }
       
  1345 
       
  1346 QTextFrame *QTextDocumentPrivate::frameAt(int pos) const
       
  1347 {
       
  1348     QTextFrame *f = rootFrame();
       
  1349 
       
  1350     while (1) {
       
  1351         QTextFrame *c = findChildFrame(f, pos);
       
  1352         if (!c)
       
  1353             return f;
       
  1354         f = c;
       
  1355     }
       
  1356 }
       
  1357 
       
  1358 void QTextDocumentPrivate::clearFrame(QTextFrame *f)
       
  1359 {
       
  1360     for (int i = 0; i < f->d_func()->childFrames.count(); ++i)
       
  1361         clearFrame(f->d_func()->childFrames.at(i));
       
  1362     f->d_func()->childFrames.clear();
       
  1363     f->d_func()->parentFrame = 0;
       
  1364 }
       
  1365 
       
  1366 void QTextDocumentPrivate::scan_frames(int pos, int charsRemoved, int charsAdded)
       
  1367 {
       
  1368     // ###### optimise
       
  1369     Q_UNUSED(pos);
       
  1370     Q_UNUSED(charsRemoved);
       
  1371     Q_UNUSED(charsAdded);
       
  1372 
       
  1373     QTextFrame *f = rootFrame();
       
  1374     clearFrame(f);
       
  1375 
       
  1376     for (FragmentIterator it = begin(); it != end(); ++it) {
       
  1377         // QTextFormat fmt = formats.format(it->format);
       
  1378         QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(it->format));
       
  1379         if (!frame)
       
  1380             continue;
       
  1381 
       
  1382         Q_ASSERT(it.size() == 1);
       
  1383         QChar ch = text.at(it->stringPosition);
       
  1384 
       
  1385         if (ch == QTextBeginningOfFrame) {
       
  1386             if (f != frame) {
       
  1387                 // f == frame happens for tables
       
  1388                 Q_ASSERT(frame->d_func()->fragment_start == it.n || frame->d_func()->fragment_start == 0);
       
  1389                 frame->d_func()->parentFrame = f;
       
  1390                 f->d_func()->childFrames.append(frame);
       
  1391                 f = frame;
       
  1392             }
       
  1393         } else if (ch == QTextEndOfFrame) {
       
  1394             Q_ASSERT(f == frame);
       
  1395             Q_ASSERT(frame->d_func()->fragment_end == it.n || frame->d_func()->fragment_end == 0);
       
  1396             f = frame->d_func()->parentFrame;
       
  1397         } else if (ch == QChar::ObjectReplacementCharacter) {
       
  1398             Q_ASSERT(f != frame);
       
  1399             Q_ASSERT(frame->d_func()->fragment_start == it.n || frame->d_func()->fragment_start == 0);
       
  1400             Q_ASSERT(frame->d_func()->fragment_end == it.n || frame->d_func()->fragment_end == 0);
       
  1401             frame->d_func()->parentFrame = f;
       
  1402             f->d_func()->childFrames.append(frame);
       
  1403         } else {
       
  1404             Q_ASSERT(false);
       
  1405         }
       
  1406     }
       
  1407     Q_ASSERT(f == rtFrame);
       
  1408     framesDirty = false;
       
  1409 }
       
  1410 
       
  1411 void QTextDocumentPrivate::insert_frame(QTextFrame *f)
       
  1412 {
       
  1413     int start = f->firstPosition();
       
  1414     int end = f->lastPosition();
       
  1415     QTextFrame *parent = frameAt(start-1);
       
  1416     Q_ASSERT(parent == frameAt(end+1));
       
  1417 
       
  1418     if (start != end) {
       
  1419         // iterator over the parent and move all children contained in my frame to myself
       
  1420         for (int i = 0; i < parent->d_func()->childFrames.size(); ++i) {
       
  1421             QTextFrame *c = parent->d_func()->childFrames.at(i);
       
  1422             if (start < c->firstPosition() && end > c->lastPosition()) {
       
  1423                 parent->d_func()->childFrames.removeAt(i);
       
  1424                 f->d_func()->childFrames.append(c);
       
  1425                 c->d_func()->parentFrame = f;
       
  1426             }
       
  1427         }
       
  1428     }
       
  1429     // insert at the correct position
       
  1430     int i = 0;
       
  1431     for (; i < parent->d_func()->childFrames.size(); ++i) {
       
  1432         QTextFrame *c = parent->d_func()->childFrames.at(i);
       
  1433         if (c->firstPosition() > end)
       
  1434             break;
       
  1435     }
       
  1436     parent->d_func()->childFrames.insert(i, f);
       
  1437     f->d_func()->parentFrame = parent;
       
  1438 }
       
  1439 
       
  1440 QTextFrame *QTextDocumentPrivate::insertFrame(int start, int end, const QTextFrameFormat &format)
       
  1441 {
       
  1442     Q_ASSERT(start >= 0 && start < length());
       
  1443     Q_ASSERT(end >= 0 && end < length());
       
  1444     Q_ASSERT(start <= end || end == -1);
       
  1445 
       
  1446     if (start != end && frameAt(start) != frameAt(end))
       
  1447         return 0;
       
  1448 
       
  1449     beginEditBlock();
       
  1450 
       
  1451     QTextFrame *frame = qobject_cast<QTextFrame *>(createObject(format));
       
  1452     Q_ASSERT(frame);
       
  1453 
       
  1454     // #### using the default block and char format below might be wrong
       
  1455     int idx = formats.indexForFormat(QTextBlockFormat());
       
  1456     QTextCharFormat cfmt;
       
  1457     cfmt.setObjectIndex(frame->objectIndex());
       
  1458     int charIdx = formats.indexForFormat(cfmt);
       
  1459 
       
  1460     insertBlock(QTextBeginningOfFrame, start, idx, charIdx, QTextUndoCommand::MoveCursor);
       
  1461     insertBlock(QTextEndOfFrame, ++end, idx, charIdx, QTextUndoCommand::KeepCursor);
       
  1462 
       
  1463     frame->d_func()->fragment_start = find(start).n;
       
  1464     frame->d_func()->fragment_end = find(end).n;
       
  1465 
       
  1466     insert_frame(frame);
       
  1467 
       
  1468     endEditBlock();
       
  1469 
       
  1470     return frame;
       
  1471 }
       
  1472 
       
  1473 void QTextDocumentPrivate::removeFrame(QTextFrame *frame)
       
  1474 {
       
  1475     QTextFrame *parent = frame->d_func()->parentFrame;
       
  1476     if (!parent)
       
  1477         return;
       
  1478 
       
  1479     int start = frame->firstPosition();
       
  1480     int end = frame->lastPosition();
       
  1481     Q_ASSERT(end >= start);
       
  1482 
       
  1483     beginEditBlock();
       
  1484 
       
  1485     // remove already removes the frames from the tree
       
  1486     remove(end, 1);
       
  1487     remove(start-1, 1);
       
  1488 
       
  1489     endEditBlock();
       
  1490 }
       
  1491 
       
  1492 QTextObject *QTextDocumentPrivate::objectForIndex(int objectIndex) const
       
  1493 {
       
  1494     if (objectIndex < 0)
       
  1495         return 0;
       
  1496 
       
  1497     QTextObject *object = objects.value(objectIndex, 0);
       
  1498     if (!object) {
       
  1499         QTextDocumentPrivate *that = const_cast<QTextDocumentPrivate *>(this);
       
  1500         QTextFormat fmt = formats.objectFormat(objectIndex);
       
  1501         object = that->createObject(fmt, objectIndex);
       
  1502     }
       
  1503     return object;
       
  1504 }
       
  1505 
       
  1506 QTextObject *QTextDocumentPrivate::objectForFormat(int formatIndex) const
       
  1507 {
       
  1508     int objectIndex = formats.format(formatIndex).objectIndex();
       
  1509     return objectForIndex(objectIndex);
       
  1510 }
       
  1511 
       
  1512 QTextObject *QTextDocumentPrivate::objectForFormat(const QTextFormat &f) const
       
  1513 {
       
  1514     return objectForIndex(f.objectIndex());
       
  1515 }
       
  1516 
       
  1517 QTextObject *QTextDocumentPrivate::createObject(const QTextFormat &f, int objectIndex)
       
  1518 {
       
  1519     QTextObject *obj = document()->createObject(f);
       
  1520 
       
  1521     if (obj) {
       
  1522         obj->d_func()->objectIndex = objectIndex == -1 ? formats.createObjectIndex(f) : objectIndex;
       
  1523         objects[obj->d_func()->objectIndex] = obj;
       
  1524     }
       
  1525 
       
  1526     return obj;
       
  1527 }
       
  1528 
       
  1529 void QTextDocumentPrivate::deleteObject(QTextObject *object)
       
  1530 {
       
  1531     const int objIdx = object->d_func()->objectIndex;
       
  1532     objects.remove(objIdx);
       
  1533     delete object;
       
  1534 }
       
  1535 
       
  1536 void QTextDocumentPrivate::contentsChanged()
       
  1537 {
       
  1538     Q_Q(QTextDocument);
       
  1539     if (editBlock)
       
  1540         return;
       
  1541 
       
  1542     bool m = undoEnabled ? (modifiedState != undoState) : true;
       
  1543     if (modified != m) {
       
  1544         modified = m;
       
  1545         emit q->modificationChanged(modified);
       
  1546     }
       
  1547 
       
  1548     emit q->contentsChanged();
       
  1549 }
       
  1550 
       
  1551 void QTextDocumentPrivate::compressPieceTable()
       
  1552 {
       
  1553     if (undoEnabled)
       
  1554         return;
       
  1555 
       
  1556     const uint garbageCollectionThreshold = 96 * 1024; // bytes
       
  1557 
       
  1558     //qDebug() << "unreachable bytes:" << unreachableCharacterCount * sizeof(QChar) << " -- limit" << garbageCollectionThreshold << "text size =" << text.size() << "capacity:" << text.capacity();
       
  1559 
       
  1560     bool compressTable = unreachableCharacterCount * sizeof(QChar) > garbageCollectionThreshold
       
  1561                          && text.size() >= text.capacity() * 0.9;
       
  1562     if (!compressTable)
       
  1563         return;
       
  1564 
       
  1565     QString newText;
       
  1566     newText.resize(text.size());
       
  1567     QChar *newTextPtr = newText.data();
       
  1568     int newLen = 0;
       
  1569 
       
  1570     for (FragmentMap::Iterator it = fragments.begin(); !it.atEnd(); ++it) {
       
  1571         qMemCopy(newTextPtr, text.constData() + it->stringPosition, it->size_array[0] * sizeof(QChar));
       
  1572         it->stringPosition = newLen;
       
  1573         newTextPtr += it->size_array[0];
       
  1574         newLen += it->size_array[0];
       
  1575     }
       
  1576 
       
  1577     newText.resize(newLen);
       
  1578     newText.squeeze();
       
  1579     //qDebug() << "removed" << text.size() - newText.size() << "characters";
       
  1580     text = newText;
       
  1581     unreachableCharacterCount = 0;
       
  1582 }
       
  1583 
       
  1584 void QTextDocumentPrivate::setModified(bool m)
       
  1585 {
       
  1586     Q_Q(QTextDocument);
       
  1587     if (m == modified)
       
  1588         return;
       
  1589 
       
  1590     modified = m;
       
  1591     if (!modified)
       
  1592         modifiedState = undoState;
       
  1593     else
       
  1594         modifiedState = -1;
       
  1595 
       
  1596     emit q->modificationChanged(modified);
       
  1597 }
       
  1598 
       
  1599 bool QTextDocumentPrivate::ensureMaximumBlockCount()
       
  1600 {
       
  1601     if (maximumBlockCount <= 0)
       
  1602         return false;
       
  1603     if (blocks.numNodes() <= maximumBlockCount)
       
  1604         return false;
       
  1605 
       
  1606     beginEditBlock();
       
  1607 
       
  1608     const int blocksToRemove = blocks.numNodes() - maximumBlockCount;
       
  1609     QTextCursor cursor(this, 0);
       
  1610     cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor, blocksToRemove);
       
  1611 
       
  1612     unreachableCharacterCount += cursor.selectionEnd() - cursor.selectionStart();
       
  1613 
       
  1614     // preserve the char format of the paragraph that is to become the new first one
       
  1615     QTextCharFormat charFmt = cursor.blockCharFormat();
       
  1616     cursor.removeSelectedText();
       
  1617     cursor.setBlockCharFormat(charFmt);
       
  1618 
       
  1619     endEditBlock();
       
  1620 
       
  1621     compressPieceTable();
       
  1622 
       
  1623     return true;
       
  1624 }
       
  1625 
       
  1626 /// This method is called from QTextTable when it is about to remove a table-cell to allow cursors to update their selection.
       
  1627 void QTextDocumentPrivate::aboutToRemoveCell(int from, int to)
       
  1628 {
       
  1629     Q_ASSERT(from <= to);
       
  1630     for (int i = 0; i < cursors.size(); ++i)
       
  1631         cursors.at(i)->aboutToRemoveCell(from, to);
       
  1632 }
       
  1633 
       
  1634 QT_END_NAMESPACE