Qt 4.8
qtextdocument_p.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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 
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, quint8(a3), a4, quint32(a5), quint32(a6), { int(a7) }, quint32(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 necessarily 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 
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 {
141  && !str.contains(QTextEndOfFrame);
142 }
143 #endif
144 
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 
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;
196  docChangeFrom = -1;
197 
198  undoState = 0;
199  revision = -1; // init() inserts a block, bringing it to 0
200 
201  lout = 0;
202 
203  modified = false;
204  modifiedState = 0;
205 
206  undoEnabled = true;
207  inContentsChange = false;
208  blockCursorAdjustment = false;
209 
210  defaultTextOption.setTabStop(80); // same as in qtextengine.cpp
213 
214  indentWidth = 40;
215  documentMargin = 4;
216 
217  maximumBlockCount = 0;
220  lastBlockCount = 0;
221 }
222 
224 {
225  framesDirty = false;
226 
227  bool undoState = undoEnabled;
228  undoEnabled = false;
232  modified = false;
233  modifiedState = 0;
234 }
235 
237 {
239 
240  foreach (QTextCursorPrivate *curs, cursors) {
241  curs->setPosition(0);
242  curs->currentCharFormat = -1;
243  curs->anchor = 0;
244  curs->adjusted_anchor = 0;
245  }
246 
248  QT_TRY{
249  cursors.clear();
250 
252  while (objectIt != objects.end()) {
253  if (*objectIt != rtFrame) {
254  delete *objectIt;
255  objectIt = objects.erase(objectIt);
256  } else {
257  ++objectIt;
258  }
259  }
260  // also clear out the remaining root frame pointer
261  // (we're going to delete the object further down)
262  objects.clear();
263 
264  title.clear();
266  text = QString();
268  modifiedState = 0;
269  modified = false;
271  int len = fragments.length();
272  fragments.clear();
273  blocks.clear();
275  delete rtFrame;
276  rtFrame = 0;
277  init();
278  cursors = oldCursors;
279  inContentsChange = true;
280  q->contentsChange(0, len, 0);
281  inContentsChange = false;
282  if (lout)
283  lout->documentChanged(0, len, 0);
284  } QT_CATCH(...) {
285  cursors = oldCursors; // at least recover the cursors
286  QT_RETHROW;
287  }
288 }
289 
291 {
292  foreach (QTextCursorPrivate *curs, cursors)
293  curs->priv = 0;
294  cursors.clear();
295  undoState = 0;
296  undoEnabled = true;
298 }
299 
301 {
303  if (lout == layout)
304  return;
305  const bool firstLayout = !lout;
306  delete lout;
307  lout = layout;
308 
309  if (!firstLayout)
310  for (BlockMap::Iterator it = blocks.begin(); !it.atEnd(); ++it)
311  it->free();
312 
313  emit q->documentLayoutChanged();
314  inContentsChange = true;
315  emit q->contentsChange(0, 0, length());
316  inContentsChange = false;
317  if (lout)
318  lout->documentChanged(0, 0, length());
319 }
320 
321 
323 {
324  // ##### optimize when only appending to the fragment!
325  Q_ASSERT(noBlockInString(text.mid(strPos, length)));
326 
327  split(pos);
328  uint x = fragments.insert_single(pos, length);
330  X->format = format;
331  X->stringPosition = strPos;
332  uint w = fragments.previous(x);
333  if (w)
334  unite(w);
335 
336  int b = blocks.findNode(pos);
338 
340 
341  QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(format));
342  if (frame) {
343  frame->d_func()->fragmentAdded(text.at(strPos), x);
344  framesDirty = true;
345  }
346 
347  adjustDocumentChangesAndCursors(pos, length, op);
348 }
349 
350 int QTextDocumentPrivate::insert_block(int pos, uint strPos, int format, int blockFormat, QTextUndoCommand::Operation op, int command)
351 {
352  split(pos);
353  uint x = fragments.insert_single(pos, 1);
355  X->format = format;
356  X->stringPosition = strPos;
357  // no need trying to unite, since paragraph separators are always in a fragment of their own
358 
361 
362  int block_pos = pos;
363  if (blocks.length() && command == QTextUndoCommand::BlockRemoved)
364  ++block_pos;
365  int size = 1;
366  int n = blocks.findNode(block_pos);
367  int key = n ? blocks.position(n) : blocks.length();
368 
369  Q_ASSERT(n || (!n && block_pos == blocks.length()));
370  if (key != block_pos) {
371  Q_ASSERT(key < block_pos);
372  int oldSize = blocks.size(n);
373  blocks.setSize(n, block_pos-key);
374  size += oldSize - (block_pos-key);
375  }
376  int b = blocks.insert_single(block_pos, size);
378  B->format = blockFormat;
379 
381 
383  if (group)
384  group->blockInserted(QTextBlock(this, b));
385 
387  if (frame) {
388  frame->d_func()->fragmentAdded(text.at(strPos), x);
389  framesDirty = true;
390  }
391 
393  return x;
394 }
395 
396 int QTextDocumentPrivate::insertBlock(const QChar &blockSeparator,
397  int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation op)
398 {
399  Q_ASSERT(formats.format(blockFormat).isBlockFormat());
400  Q_ASSERT(formats.format(charFormat).isCharFormat());
401  Q_ASSERT(pos >= 0 && (pos < fragments.length() || (pos == 0 && fragments.length() == 0)));
402  Q_ASSERT(isValidBlockSeparator(blockSeparator));
403 
404  beginEditBlock();
405 
406  int strPos = text.length();
407  text.append(blockSeparator);
408 
409  int ob = blocks.findNode(pos);
410  bool atBlockEnd = true;
411  bool atBlockStart = true;
412  int oldRevision = 0;
413  if (ob) {
414  atBlockEnd = (pos - blocks.position(ob) == blocks.size(ob)-1);
415  atBlockStart = ((int)blocks.position(ob) == pos);
416  oldRevision = blocks.fragment(ob)->revision;
417  }
418 
419  const int fragment = insert_block(pos, strPos, charFormat, blockFormat, op, QTextUndoCommand::BlockRemoved);
420 
422 
423  int b = blocks.findNode(pos);
425 
427  op, charFormat, strPos, pos, blockFormat,
428  B->revision);
429 
430  appendUndoItem(c);
432 
433  // update revision numbers of the modified blocks.
434  B->revision = (atBlockEnd && !atBlockStart)? oldRevision : revision;
435  b = blocks.next(b);
436  if (b) {
437  B = blocks.fragment(b);
438  B->revision = atBlockStart ? oldRevision : revision;
439  }
440 
441  if (formats.charFormat(charFormat).objectIndex() == -1)
443 
444  endEditBlock();
445  return fragment;
446 }
447 
448 int QTextDocumentPrivate::insertBlock(int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation op)
449 {
450  return insertBlock(QChar::ParagraphSeparator, pos, blockFormat, charFormat, op);
451 }
452 
453 void QTextDocumentPrivate::insert(int pos, int strPos, int strLength, int format)
454 {
455  if (strLength <= 0)
456  return;
457 
458  Q_ASSERT(pos >= 0 && pos < fragments.length());
459  Q_ASSERT(formats.format(format).isCharFormat());
460 
461  insert_string(pos, strPos, strLength, format, QTextUndoCommand::MoveCursor);
462  if (undoEnabled) {
463  int b = blocks.findNode(pos);
465 
467  QTextUndoCommand::MoveCursor, format, strPos, pos, strLength,
468  B->revision);
469  appendUndoItem(c);
470  B->revision = revision;
472  }
473  finishEdit();
474 }
475 
476 void QTextDocumentPrivate::insert(int pos, const QString &str, int format)
477 {
478  if (str.size() == 0)
479  return;
480 
482 
483  int strPos = text.length();
484  text.append(str);
485  insert(pos, strPos, str.length(), format);
486 }
487 
489 {
490  Q_ASSERT(pos >= 0);
492  Q_ASSERT(blocks.length() >= pos+(int)length);
493 
494  int b = blocks.findNode(pos);
495  uint x = fragments.findNode(pos);
496 
497  Q_ASSERT(blocks.size(b) > length);
498  Q_ASSERT(x && fragments.position(x) == (uint)pos && fragments.size(x) == length);
500 
502 
504  if (frame) {
505  frame->d_func()->fragmentRemoved(text.at(fragments.fragment(x)->stringPosition), x);
506  framesDirty = true;
507  }
508 
509  const int w = fragments.erase_single(x);
510 
511  if (!undoEnabled)
513 
514  adjustDocumentChangesAndCursors(pos, -int(length), op);
515 
516  return w;
517 }
518 
519 int QTextDocumentPrivate::remove_block(int pos, int *blockFormat, int command, QTextUndoCommand::Operation op)
520 {
521  Q_ASSERT(pos >= 0);
523  Q_ASSERT(blocks.length() > pos);
524 
525  int b = blocks.findNode(pos);
526  uint x = fragments.findNode(pos);
527 
528  Q_ASSERT(x && (int)fragments.position(x) == pos);
529  Q_ASSERT(fragments.size(x) == 1);
531  Q_ASSERT(b);
532 
533  if (blocks.size(b) == 1 && command == QTextUndoCommand::BlockAdded) {
534  Q_ASSERT((int)blocks.position(b) == pos);
535 // qDebug("removing empty block");
536  // empty block remove the block itself
537  } else {
538  // non empty block, merge with next one into this block
539 // qDebug("merging block with next");
540  int n = blocks.next(b);
541  Q_ASSERT((int)blocks.position(n) == pos + 1);
542  blocks.setSize(b, blocks.size(b) + blocks.size(n) - 1);
544  b = n;
545  }
546  *blockFormat = blocks.fragment(b)->format;
547 
549  if (group)
550  group->blockRemoved(QTextBlock(this, b));
551 
553  if (frame) {
554  frame->d_func()->fragmentRemoved(text.at(fragments.fragment(x)->stringPosition), x);
555  framesDirty = true;
556  }
557 
558  blocks.erase_single(b);
559  const int w = fragments.erase_single(x);
560 
561  adjustDocumentChangesAndCursors(pos, -1, op);
562 
563  return w;
564 }
565 
566 #if !defined(QT_NO_DEBUG)
567 static bool isAncestorFrame(QTextFrame *possibleAncestor, QTextFrame *child)
568 {
569  while (child) {
570  if (child == possibleAncestor)
571  return true;
572  child = child->parentFrame();
573  }
574  return false;
575 }
576 #endif
577 
579 {
580  Q_ASSERT(to <= fragments.length() && to <= pos);
581  Q_ASSERT(pos >= 0 && pos+length <= fragments.length());
583 
584  if (pos == to)
585  return;
586 
587  const bool needsInsert = to != -1;
588 
589 #if !defined(QT_NO_DEBUG)
590  const bool startAndEndInSameFrame = (frameAt(pos) == frameAt(pos + length - 1));
591 
592  const bool endIsEndOfChildFrame = (isAncestorFrame(frameAt(pos), frameAt(pos + length - 1))
593  && text.at(find(pos + length - 1)->stringPosition) == QTextEndOfFrame);
594 
595  const bool startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent
596  = (text.at(find(pos)->stringPosition) == QTextBeginningOfFrame
597  && text.at(find(pos + length - 1)->stringPosition) == QTextEndOfFrame
598  && frameAt(pos)->parentFrame() == frameAt(pos + length - 1)->parentFrame());
599 
600  const bool isFirstTableCell = (qobject_cast<QTextTable *>(frameAt(pos + length - 1))
601  && frameAt(pos + length - 1)->parentFrame() == frameAt(pos));
602 
603  Q_ASSERT(startAndEndInSameFrame || endIsEndOfChildFrame || startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent || isFirstTableCell);
604 #endif
605 
606  split(pos);
607  split(pos+length);
608 
609  uint dst = needsInsert ? fragments.findNode(to) : 0;
610  uint dstKey = needsInsert ? fragments.position(dst) : 0;
611 
612  uint x = fragments.findNode(pos);
613  uint end = fragments.findNode(pos+length);
614 
615  uint w = 0;
616  while (x != end) {
617  uint n = fragments.next(x);
618 
619  uint key = fragments.position(x);
620  uint b = blocks.findNode(key+1);
622  int blockRevision = B->revision;
623 
626  op, X->format, X->stringPosition, key, X->size_array[0],
627  blockRevision);
629  op, X->format, X->stringPosition, dstKey, X->size_array[0],
630  blockRevision);
631 
632  if (key+1 != blocks.position(b)) {
633 // qDebug("remove_string from %d length %d", key, X->size_array[0]);
635  w = remove_string(key, X->size_array[0], op);
636 
637  if (needsInsert) {
638  insert_string(dstKey, X->stringPosition, X->size_array[0], X->format, op);
639  dstKey += X->size_array[0];
640  }
641  } else {
642 // qDebug("remove_block at %d", key);
644  b = blocks.previous(b);
645  B = 0;
647  w = remove_block(key, &c.blockFormat, QTextUndoCommand::BlockAdded, op);
648 
649  if (needsInsert) {
650  insert_block(dstKey++, X->stringPosition, X->format, c.blockFormat, op, QTextUndoCommand::BlockRemoved);
652  cInsert.blockFormat = c.blockFormat;
653  }
654  }
655  appendUndoItem(c);
656  if (B)
657  B->revision = revision;
658  x = n;
659 
660  if (needsInsert)
661  appendUndoItem(cInsert);
662  }
663  if (w)
664  unite(w);
665 
667 
669  finishEdit();
670 }
671 
673 {
674  if (length == 0)
675  return;
676  blockCursorAdjustment = true;
677  move(pos, -1, length, op);
678  blockCursorAdjustment = false;
679  foreach (QTextCursorPrivate *curs, cursors) {
680  if (curs->adjustPosition(pos, -length, op) == QTextCursorPrivate::CursorMoved) {
681  curs->changed = true;
682  }
683  }
684  finishEdit();
685 }
686 
688 {
689  beginEditBlock();
690 
691  Q_ASSERT(newFormat.isValid());
692 
693  int newFormatIdx = -1;
694  if (mode == SetFormatAndPreserveObjectIndices) {
695  QTextCharFormat cleanFormat = newFormat;
697  newFormatIdx = formats.indexForFormat(cleanFormat);
698  } else if (mode == SetFormat) {
699  newFormatIdx = formats.indexForFormat(newFormat);
700  }
701 
702  if (pos == -1) {
703  if (mode == MergeFormat) {
705  format.merge(newFormat);
707  } else if (mode == SetFormatAndPreserveObjectIndices
709  QTextCharFormat f = newFormat;
712  } else {
713  initialBlockCharFormatIndex = newFormatIdx;
714  }
715 
716  ++pos;
717  --length;
718  }
719 
720  const int startPos = pos;
721  const int endPos = pos + length;
722 
723  split(startPos);
724  split(endPos);
725 
726  while (pos < endPos) {
728  Q_ASSERT(!it.atEnd());
729 
730  QTextFragmentData *fragment = it.value();
731 
732  Q_ASSERT(formats.format(fragment->format).type() == QTextFormat::CharFormat);
733 
734  int offset = pos - it.position();
735  int length = qMin(endPos - pos, int(fragment->size_array[0] - offset));
736  int oldFormat = fragment->format;
737 
738  if (mode == MergeFormat) {
739  QTextFormat format = formats.format(fragment->format);
740  format.merge(newFormat);
741  fragment->format = formats.indexForFormat(format);
742  } else if (mode == SetFormatAndPreserveObjectIndices
743  && formats.format(oldFormat).objectIndex() != -1) {
744  QTextCharFormat f = newFormat;
745  f.setObjectIndex(formats.format(oldFormat).objectIndex());
746  fragment->format = formats.indexForFormat(f);
747  } else {
748  fragment->format = newFormatIdx;
749  }
750 
752  0, pos, length, 0);
753  appendUndoItem(c);
754 
755  pos += length;
756  Q_ASSERT(pos == (int)(it.position() + fragment->size_array[0]) || pos >= endPos);
757  }
758 
759  int n = fragments.findNode(startPos - 1);
760  if (n)
761  unite(n);
762 
763  n = fragments.findNode(endPos);
764  if (n)
765  unite(n);
766 
767  QTextBlock blockIt = blocksFind(startPos);
768  QTextBlock endIt = blocksFind(endPos);
769  if (endIt.isValid())
770  endIt = endIt.next();
771  for (; blockIt.isValid() && blockIt != endIt; blockIt = blockIt.next())
773 
774  documentChange(startPos, length);
775 
776  endEditBlock();
777 }
778 
780  const QTextBlockFormat &newFormat, FormatChangeMode mode)
781 {
782  beginEditBlock();
783 
784  Q_ASSERT(mode != SetFormatAndPreserveObjectIndices); // only implemented for setCharFormat
785 
786  Q_ASSERT(newFormat.isValid());
787 
788  int newFormatIdx = -1;
789  if (mode == SetFormat)
790  newFormatIdx = formats.indexForFormat(newFormat);
792 
793  QTextBlock it = from;
794  QTextBlock end = to;
795  if (end.isValid())
796  end = end.next();
797 
798  for (; it != end; it = it.next()) {
799  int oldFormat = block(it)->format;
802  if (mode == MergeFormat) {
803  format.merge(newFormat);
804  newFormatIdx = formats.indexForFormat(format);
805  group = qobject_cast<QTextBlockGroup *>(objectForFormat(format));
806  }
807  block(it)->format = newFormatIdx;
808 
809  block(it)->invalidate();
810 
812  0, it.position(), 1, 0);
813  appendUndoItem(c);
814 
815  if (group != oldGroup) {
816  if (oldGroup)
817  oldGroup->blockRemoved(it);
818  if (group)
819  group->blockInserted(it);
820  } else if (group) {
821  group->blockFormatChanged(it);
822  }
823  }
824 
825  documentChange(from.position(), to.position() + to.length() - from.position());
826 
827  endEditBlock();
828 }
829 
830 
832 {
833  uint x = fragments.findNode(pos);
834  if (x) {
835  int k = fragments.position(x);
836 // qDebug("found fragment with key %d, size_left=%d, size=%d to split at %d",
837 // k, (*it)->size_left[0], (*it)->size_array[0], pos);
838  if (k != pos) {
839  Q_ASSERT(k <= pos);
840  // need to resize the first fragment and add a new one
842  int oldsize = X->size_array[0];
843  fragments.setSize(x, pos-k);
844  uint n = fragments.insert_single(pos, oldsize-(pos-k));
845  X = fragments.fragment(x);
847  N->stringPosition = X->stringPosition + pos-k;
848  N->format = X->format;
849  return true;
850  }
851  }
852  return false;
853 }
854 
856 {
857  uint n = fragments.next(f);
858  if (!n)
859  return false;
860 
863 
864  if (nf->format == ff->format && (ff->stringPosition + (int)ff->size_array[0] == nf->stringPosition)) {
867  return false;
868 
869  fragments.setSize(f, ff->size_array[0] + nf->size_array[0]);
871  return true;
872  }
873  return false;
874 }
875 
876 
878 {
879  PMDEBUG("%s, undoState=%d, undoStack size=%d", undo ? "undo:" : "redo:", undoState, undoStack.size());
880  if (!undoEnabled || (undo && undoState == 0) || (!undo && undoState == undoStack.size()))
881  return -1;
882 
883  undoEnabled = false;
884  beginEditBlock();
885  int editPos = -1;
886  int editLength = -1;
887  while (1) {
888  if (undo)
889  --undoState;
891  int resetBlockRevision = c.pos;
892 
893  switch(c.command) {
896  PMDEBUG(" erase: from %d, length %d", c.pos, c.length);
898  editPos = c.pos;
899  editLength = 0;
900  break;
902  PMDEBUG(" insert: format %d (from %d, length %d, strpos=%d)", c.format, c.pos, c.length, c.strPos);
905  if (editPos != (int)c.pos)
906  editLength = 0;
907  editPos = c.pos;
908  editLength += c.length;
909  break;
913  PMDEBUG(" blockremove: from %d", c.pos);
916  else
918  editPos = c.pos;
919  editLength = 0;
920  break;
923  PMDEBUG(" blockinsert: charformat %d blockformat %d (pos %d, strpos=%d)", c.format, c.blockFormat, c.pos, c.strPos);
925  resetBlockRevision += 1;
928  else
930  if (editPos != (int)c.pos)
931  editLength = 0;
932  editPos = c.pos;
933  editLength += 1;
934  break;
936  resetBlockRevision = -1; // ## TODO
937  PMDEBUG(" charFormat: format %d (from %d, length %d)", c.format, c.pos, c.length);
939  Q_ASSERT(!it.atEnd());
940 
941  int oldFormat = it.value()->format;
943  c.format = oldFormat;
944  if (editPos != (int)c.pos)
945  editLength = 0;
946  editPos = c.pos;
947  editLength += c.length;
948  break;
949  }
951  resetBlockRevision = -1; // ## TODO
952  PMDEBUG(" blockformat: format %d pos %d", c.format, c.pos);
954  Q_ASSERT(it.isValid());
955 
956  int oldFormat = block(it)->format;
957  block(it)->format = c.format;
959  QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(c.format)));
960  c.format = oldFormat;
961  if (group != oldGroup) {
962  if (oldGroup)
963  oldGroup->blockRemoved(it);
964  if (group)
965  group->blockInserted(it);
966  } else if (group) {
967  group->blockFormatChanged(it);
968  }
969  documentChange(it.position(), it.length());
970  editPos = -1;
971  break;
972  }
974  resetBlockRevision = -1; // ## TODO
975  PMDEBUG(" group format change");
977  int oldFormat = formats.objectFormatIndex(c.objectIndex);
978  changeObjectFormat(object, c.format);
979  c.format = oldFormat;
980  editPos = -1;
981  break;
982  }
984  editPos = c.pos;
985  editLength = 0;
986  break;
988  resetBlockRevision = -1; // ## TODO
989  if (undo)
990  c.custom->undo();
991  else
992  c.custom->redo();
993  editPos = -1;
994  break;
995  default:
996  Q_ASSERT(false);
997  }
998 
999  if (resetBlockRevision >= 0) {
1000  int b = blocks.findNode(resetBlockRevision);
1001  QTextBlockData *B = blocks.fragment(b);
1002  B->revision = c.revision;
1003  }
1004 
1005  if (!undo)
1006  ++undoState;
1007 
1008  bool inBlock = (
1009  undoState > 0
1010  && undoState < undoStack.size()
1011  && undoStack[undoState].block_part
1012  && undoStack[undoState-1].block_part
1013  && !undoStack[undoState-1].block_end
1014  );
1015  if (!inBlock)
1016  break;
1017  }
1018  undoEnabled = true;
1019 
1020  int newCursorPos = -1;
1021 
1022  if (editPos >=0)
1023  newCursorPos = editPos + editLength;
1024  else if (docChangeFrom >= 0)
1025  newCursorPos= qMin(docChangeFrom + docChangeLength, length() - 1);
1026 
1027  endEditBlock();
1030 
1031  return newCursorPos;
1032 }
1033 
1038 {
1039  if (!undoEnabled) {
1040  delete item;
1041  return;
1042  }
1043 
1046  c.block_part = editBlock != 0;
1047  c.block_end = 0;
1049  c.format = 0;
1050  c.strPos = 0;
1051  c.pos = 0;
1052  c.blockFormat = 0;
1053 
1054  c.custom = item;
1055  appendUndoItem(c);
1056 }
1057 
1059 {
1060  PMDEBUG("appendUndoItem, command=%d enabled=%d", c.command, undoEnabled);
1061  if (!undoEnabled)
1062  return;
1063  if (undoState < undoStack.size())
1065 
1066  if (editBlock != 0 && editBlockCursorPosition >= 0) { // we had a beginEditBlock() with a cursor position
1067  if (c.pos != (quint32) editBlockCursorPosition) { // and that cursor position is different from the command
1068  // generate a CursorMoved undo item
1070  0, 0, editBlockCursorPosition, 0, 0);
1071  undoStack.append(cc);
1072  undoState++;
1074  }
1075  }
1076 
1077 
1078  if (!undoStack.isEmpty() && modified) {
1079  QTextUndoCommand &last = undoStack[undoState - 1];
1080 
1081  if ( (last.block_part && c.block_part && !last.block_end) // part of the same block => can merge
1082  || (!c.block_part && !last.block_part)) { // two single undo items => can merge
1083 
1084  if (last.tryMerge(c))
1085  return;
1086  }
1087  }
1088  if (modifiedState > undoState)
1089  modifiedState = -1;
1090  undoStack.append(c);
1091  undoState++;
1092  emitUndoAvailable(true);
1093  emitRedoAvailable(false);
1094 
1095  if (!c.block_part)
1097 }
1098 
1100  bool emitSignals)
1101 {
1102  bool undoCommandsAvailable = undoState != 0;
1103  bool redoCommandsAvailable = undoState != undoStack.size();
1104  if (stacksToClear == QTextDocument::UndoStack && undoCommandsAvailable) {
1105  for (int i = 0; i < undoState; ++i) {
1108  delete c.custom;
1109  }
1110  undoStack.remove(0, undoState);
1112  undoState = 0;
1113  if (emitSignals)
1114  emitUndoAvailable(false);
1115  } else if (stacksToClear == QTextDocument::RedoStack
1116  && redoCommandsAvailable) {
1117  for (int i = undoState; i < undoStack.size(); ++i) {
1120  delete c.custom;
1121  }
1123  if (emitSignals)
1124  emitRedoAvailable(false);
1125  } else if (stacksToClear == QTextDocument::UndoAndRedoStacks
1126  && !undoStack.isEmpty()) {
1127  for (int i = 0; i < undoStack.size(); ++i) {
1130  delete c.custom;
1131  }
1132  undoState = 0;
1133  undoStack.resize(0);
1134  if (emitSignals && undoCommandsAvailable)
1135  emitUndoAvailable(false);
1136  if (emitSignals && redoCommandsAvailable)
1137  emitRedoAvailable(false);
1138  }
1139 }
1140 
1142 {
1143  if (available != wasUndoAvailable) {
1144  Q_Q(QTextDocument);
1145  emit q->undoAvailable(available);
1146  wasUndoAvailable = available;
1147  }
1148 }
1149 
1151 {
1152  if (available != wasRedoAvailable) {
1153  Q_Q(QTextDocument);
1154  emit q->redoAvailable(available);
1155  wasRedoAvailable = available;
1156  }
1157 }
1158 
1160 {
1161  if (enable && maximumBlockCount > 0)
1162  return;
1163 
1164  if (!enable) {
1165  undoState = 0;
1167  emitUndoAvailable(false);
1168  emitRedoAvailable(false);
1169  }
1170  modifiedState = modified ? -1 : undoState;
1171  undoEnabled = enable;
1172  if (!undoEnabled)
1174 }
1175 
1177 {
1178  beginEditBlock();
1179 
1180  if (undoEnabled && undoState)
1181  undoStack[undoState - 1].block_end = false;
1182 }
1183 
1185 {
1186  Q_ASSERT(editBlock > 0);
1187  if (--editBlock)
1188  return;
1189 
1190  if (undoEnabled && undoState > 0) {
1191  const bool wasBlocking = !undoStack[undoState - 1].block_end;
1192  if (undoStack[undoState - 1].block_part) {
1193  undoStack[undoState - 1].block_end = true;
1194  if (wasBlocking)
1196  }
1197  }
1198 
1200 
1201  finishEdit();
1202 }
1203 
1205 {
1206  Q_Q(QTextDocument);
1207 
1208  if (editBlock)
1209  return;
1210 
1211  if (framesDirty)
1213 
1214  if (lout && docChangeFrom >= 0) {
1215  if (!inContentsChange) {
1216  inContentsChange = true;
1218  inContentsChange = false;
1219  }
1221  }
1222 
1223  docChangeFrom = -1;
1224 
1227  if (ensureMaximumBlockCount()) {
1228  // if ensureMaximumBlockCount() returns true
1229  // it will have called endEditBlock() and
1230  // compressPieceTable() itself, so we return here
1231  // to prevent getting two contentsChanged emits
1232  return;
1233  }
1234  }
1235 
1236  QList<QTextCursor> changedCursors;
1237  foreach (QTextCursorPrivate *curs, cursors) {
1238  if (curs->changed) {
1239  curs->changed = false;
1240  changedCursors.append(QTextCursor(curs));
1241  }
1242  }
1243  foreach (const QTextCursor &cursor, changedCursors)
1244  emit q->cursorPositionChanged(cursor);
1245 
1246  contentsChanged();
1247 
1248  if (blocks.numNodes() != lastBlockCount) {
1250  emit q->blockCountChanged(lastBlockCount);
1251  }
1252 
1255 }
1256 
1258 {
1259 // qDebug("QTextDocumentPrivate::documentChange: from=%d,length=%d", from, length);
1260  if (docChangeFrom < 0) {
1261  docChangeFrom = from;
1264  return;
1265  }
1266  int start = qMin(from, docChangeFrom);
1267  int end = qMax(from + length, docChangeFrom + docChangeLength);
1268  int diff = qMax(0, end - start - docChangeLength);
1269  docChangeFrom = start;
1271  docChangeLength += diff;
1272 }
1273 
1274 /*
1275  adjustDocumentChangesAndCursors is called whenever there is an insert or remove of characters.
1276  param from is the cursor position in the document
1277  param addedOrRemoved is the amount of characters added or removed. A negative number means characters are removed.
1278 
1279  The function stores information to be emitted when finishEdit() is called.
1280 */
1282 {
1283  if (!editBlock)
1284  ++revision;
1285 
1286  if (blockCursorAdjustment) {
1287  ; // postpone, will be called again from QTextDocumentPrivate::remove()
1288  } else {
1289  foreach (QTextCursorPrivate *curs, cursors) {
1290  if (curs->adjustPosition(from, addedOrRemoved, op) == QTextCursorPrivate::CursorMoved) {
1291  curs->changed = true;
1292  }
1293  }
1294  }
1295 
1296 // qDebug("QTextDocumentPrivate::adjustDocumentChanges: from=%d,addedOrRemoved=%d", from, addedOrRemoved);
1297  if (docChangeFrom < 0) {
1298  docChangeFrom = from;
1299  if (addedOrRemoved > 0) {
1300  docChangeOldLength = 0;
1301  docChangeLength = addedOrRemoved;
1302  } else {
1303  docChangeOldLength = -addedOrRemoved;
1304  docChangeLength = 0;
1305  }
1306 // qDebug("adjustDocumentChanges:");
1307 // qDebug(" -> %d %d %d", docChangeFrom, docChangeOldLength, docChangeLength);
1308  return;
1309  }
1310 
1311  // have to merge the new change with the already existing one.
1312  int added = qMax(0, addedOrRemoved);
1313  int removed = qMax(0, -addedOrRemoved);
1314 
1315  int diff = 0;
1316  if(from + removed < docChangeFrom)
1317  diff = docChangeFrom - from - removed;
1318  else if(from > docChangeFrom + docChangeLength)
1319  diff = from - (docChangeFrom + docChangeLength);
1320 
1321  int overlap_start = qMax(from, docChangeFrom);
1322  int overlap_end = qMin(from + removed, docChangeFrom + docChangeLength);
1323  int removedInside = qMax(0, overlap_end - overlap_start);
1324  removed -= removedInside;
1325 
1326 // qDebug("adjustDocumentChanges: from=%d, addedOrRemoved=%d, diff=%d, removedInside=%d", from, addedOrRemoved, diff, removedInside);
1327  docChangeFrom = qMin(docChangeFrom, from);
1328  docChangeOldLength += removed + diff;
1329  docChangeLength += added - removedInside + diff;
1330 // qDebug(" -> %d %d %d", docChangeFrom, docChangeOldLength, docChangeLength);
1331 
1332 }
1333 
1334 
1336 {
1337  QString result;
1338  result.resize(length());
1339  const QChar *text_unicode = text.unicode();
1340  QChar *data = result.data();
1342  const QTextFragmentData *f = *it;
1343  ::memcpy(data, text_unicode + f->stringPosition, f->size_array[0] * sizeof(QChar));
1344  data += f->size_array[0];
1345  }
1346  // remove trailing block separator
1347  result.chop(1);
1348  return result;
1349 }
1350 
1352 {
1353  int pos = blocks.position(node);
1354  if (pos == 0)
1356 
1357  return fragments.find(pos - 1)->format;
1358 }
1359 
1361 {
1362  if (position == length()-1)
1363  return position;
1364 
1365  QTextBlock it = blocksFind(position);
1366  int start = it.position();
1367  int end = start + it.length() - 1;
1368  if (position == end)
1369  return end + 1;
1370 
1371  return it.layout()->nextCursorPosition(position-start, mode) + start;
1372 }
1373 
1375 {
1376  if (position == 0)
1377  return position;
1378 
1379  QTextBlock it = blocksFind(position);
1380  int start = it.position();
1381  if (position == start)
1382  return start - 1;
1383 
1384  return it.layout()->previousCursorPosition(position-start, mode) + start;
1385 }
1386 
1388 {
1389  QTextBlock it = blocksFind(position);
1390  int start = it.position();
1391  return it.layout()->leftCursorPosition(position-start) + start;
1392 }
1393 
1395 {
1396  QTextBlock it = blocksFind(position);
1397  int start = it.position();
1398  return it.layout()->rightCursorPosition(position-start) + start;
1399 }
1400 
1402 {
1403  beginEditBlock();
1404  int objectIndex = obj->objectIndex();
1405  int oldFormatIndex = formats.objectFormatIndex(objectIndex);
1406  formats.setObjectFormatIndex(objectIndex, format);
1407 
1409  if (b) {
1410  b->d_func()->markBlocksDirty();
1411  }
1412  QTextFrame *f = qobject_cast<QTextFrame *>(obj);
1413  if (f)
1415 
1417  0, 0, obj->d_func()->objectIndex, 0);
1418  appendUndoItem(c);
1419 
1420  endEditBlock();
1421 }
1422 
1424 {
1425  /* Binary search for frame at pos */
1427  int first = 0;
1428  int last = children.size() - 1;
1429  while (first <= last) {
1430  int mid = (first + last) / 2;
1431  QTextFrame *c = children.at(mid);
1432  if (pos > c->lastPosition())
1433  first = mid + 1;
1434  else if (pos < c->firstPosition())
1435  last = mid - 1;
1436  else
1437  return c;
1438  }
1439  return 0;
1440 }
1441 
1443 {
1444  if (!rtFrame) {
1445  QTextFrameFormat defaultRootFrameFormat;
1446  defaultRootFrameFormat.setMargin(documentMargin);
1447  rtFrame = qobject_cast<QTextFrame *>(const_cast<QTextDocumentPrivate *>(this)->createObject(defaultRootFrameFormat));
1448  }
1449  return rtFrame;
1450 }
1451 
1453 {
1454  QTextFrame *f = rootFrame();
1455 
1456  while (1) {
1457  QTextFrame *c = findChildFrame(f, pos);
1458  if (!c)
1459  return f;
1460  f = c;
1461  }
1462 }
1463 
1465 {
1466  for (int i = 0; i < f->d_func()->childFrames.count(); ++i)
1467  clearFrame(f->d_func()->childFrames.at(i));
1468  f->d_func()->childFrames.clear();
1469  f->d_func()->parentFrame = 0;
1470 }
1471 
1472 void QTextDocumentPrivate::scan_frames(int pos, int charsRemoved, int charsAdded)
1473 {
1474  // ###### optimize
1475  Q_UNUSED(pos);
1476  Q_UNUSED(charsRemoved);
1477  Q_UNUSED(charsAdded);
1478 
1479  QTextFrame *f = rootFrame();
1480  clearFrame(f);
1481 
1482  for (FragmentIterator it = begin(); it != end(); ++it) {
1483  // QTextFormat fmt = formats.format(it->format);
1484  QTextFrame *frame = qobject_cast<QTextFrame *>(objectForFormat(it->format));
1485  if (!frame)
1486  continue;
1487 
1488  Q_ASSERT(it.size() == 1);
1489  QChar ch = text.at(it->stringPosition);
1490 
1491  if (ch == QTextBeginningOfFrame) {
1492  if (f != frame) {
1493  // f == frame happens for tables
1494  Q_ASSERT(frame->d_func()->fragment_start == it.n || frame->d_func()->fragment_start == 0);
1495  frame->d_func()->parentFrame = f;
1496  f->d_func()->childFrames.append(frame);
1497  f = frame;
1498  }
1499  } else if (ch == QTextEndOfFrame) {
1500  Q_ASSERT(f == frame);
1501  Q_ASSERT(frame->d_func()->fragment_end == it.n || frame->d_func()->fragment_end == 0);
1502  f = frame->d_func()->parentFrame;
1503  } else if (ch == QChar::ObjectReplacementCharacter) {
1504  Q_ASSERT(f != frame);
1505  Q_ASSERT(frame->d_func()->fragment_start == it.n || frame->d_func()->fragment_start == 0);
1506  Q_ASSERT(frame->d_func()->fragment_end == it.n || frame->d_func()->fragment_end == 0);
1507  frame->d_func()->parentFrame = f;
1508  f->d_func()->childFrames.append(frame);
1509  } else {
1510  Q_ASSERT(false);
1511  }
1512  }
1513  Q_ASSERT(f == rtFrame);
1514  framesDirty = false;
1515 }
1516 
1518 {
1519  int start = f->firstPosition();
1520  int end = f->lastPosition();
1521  QTextFrame *parent = frameAt(start-1);
1522  Q_ASSERT(parent == frameAt(end+1));
1523 
1524  if (start != end) {
1525  // iterator over the parent and move all children contained in my frame to myself
1526  for (int i = 0; i < parent->d_func()->childFrames.size(); ++i) {
1527  QTextFrame *c = parent->d_func()->childFrames.at(i);
1528  if (start < c->firstPosition() && end > c->lastPosition()) {
1529  parent->d_func()->childFrames.removeAt(i);
1530  f->d_func()->childFrames.append(c);
1531  c->d_func()->parentFrame = f;
1532  }
1533  }
1534  }
1535  // insert at the correct position
1536  int i = 0;
1537  for (; i < parent->d_func()->childFrames.size(); ++i) {
1538  QTextFrame *c = parent->d_func()->childFrames.at(i);
1539  if (c->firstPosition() > end)
1540  break;
1541  }
1542  parent->d_func()->childFrames.insert(i, f);
1543  f->d_func()->parentFrame = parent;
1544 }
1545 
1547 {
1548  Q_ASSERT(start >= 0 && start < length());
1549  Q_ASSERT(end >= 0 && end < length());
1550  Q_ASSERT(start <= end || end == -1);
1551 
1552  if (start != end && frameAt(start) != frameAt(end))
1553  return 0;
1554 
1555  beginEditBlock();
1556 
1557  QTextFrame *frame = qobject_cast<QTextFrame *>(createObject(format));
1558  Q_ASSERT(frame);
1559 
1560  // #### using the default block and char format below might be wrong
1562  QTextCharFormat cfmt;
1563  cfmt.setObjectIndex(frame->objectIndex());
1564  int charIdx = formats.indexForFormat(cfmt);
1565 
1568 
1569  frame->d_func()->fragment_start = find(start).n;
1570  frame->d_func()->fragment_end = find(end).n;
1571 
1572  insert_frame(frame);
1573 
1574  endEditBlock();
1575 
1576  return frame;
1577 }
1578 
1580 {
1581  QTextFrame *parent = frame->d_func()->parentFrame;
1582  if (!parent)
1583  return;
1584 
1585  int start = frame->firstPosition();
1586  int end = frame->lastPosition();
1587  Q_ASSERT(end >= start);
1588 
1589  beginEditBlock();
1590 
1591  // remove already removes the frames from the tree
1592  remove(end, 1);
1593  remove(start-1, 1);
1594 
1595  endEditBlock();
1596 }
1597 
1599 {
1600  if (objectIndex < 0)
1601  return 0;
1602 
1603  QTextObject *object = objects.value(objectIndex, 0);
1604  if (!object) {
1605  QTextDocumentPrivate *that = const_cast<QTextDocumentPrivate *>(this);
1606  QTextFormat fmt = formats.objectFormat(objectIndex);
1607  object = that->createObject(fmt, objectIndex);
1608  }
1609  return object;
1610 }
1611 
1613 {
1614  int objectIndex = formats.format(formatIndex).objectIndex();
1615  return objectForIndex(objectIndex);
1616 }
1617 
1619 {
1620  return objectForIndex(f.objectIndex());
1621 }
1622 
1624 {
1625  QTextObject *obj = document()->createObject(f);
1626 
1627  if (obj) {
1628  obj->d_func()->objectIndex = objectIndex == -1 ? formats.createObjectIndex(f) : objectIndex;
1629  objects[obj->d_func()->objectIndex] = obj;
1630  }
1631 
1632  return obj;
1633 }
1634 
1636 {
1637  const int objIdx = object->d_func()->objectIndex;
1638  objects.remove(objIdx);
1639  delete object;
1640 }
1641 
1643 {
1644  Q_Q(QTextDocument);
1645  if (editBlock)
1646  return;
1647 
1648  bool m = undoEnabled ? (modifiedState != undoState) : true;
1649  if (modified != m) {
1650  modified = m;
1651  emit q->modificationChanged(modified);
1652  }
1653 
1654  emit q->contentsChanged();
1655 }
1656 
1658 {
1659  if (undoEnabled)
1660  return;
1661 
1662  const uint garbageCollectionThreshold = 96 * 1024; // bytes
1663 
1664  //qDebug() << "unreachable bytes:" << unreachableCharacterCount * sizeof(QChar) << " -- limit" << garbageCollectionThreshold << "text size =" << text.size() << "capacity:" << text.capacity();
1665 
1666  bool compressTable = unreachableCharacterCount * sizeof(QChar) > garbageCollectionThreshold
1667  && text.size() >= text.capacity() * 0.9;
1668  if (!compressTable)
1669  return;
1670 
1671  QString newText;
1672  newText.resize(text.size());
1673  QChar *newTextPtr = newText.data();
1674  int newLen = 0;
1675 
1676  for (FragmentMap::Iterator it = fragments.begin(); !it.atEnd(); ++it) {
1677  memcpy(newTextPtr, text.constData() + it->stringPosition, it->size_array[0] * sizeof(QChar));
1678  it->stringPosition = newLen;
1679  newTextPtr += it->size_array[0];
1680  newLen += it->size_array[0];
1681  }
1682 
1683  newText.resize(newLen);
1684  newText.squeeze();
1685  //qDebug() << "removed" << text.size() - newText.size() << "characters";
1686  text = newText;
1688 }
1689 
1691 {
1692  Q_Q(QTextDocument);
1693  if (m == modified)
1694  return;
1695 
1696  modified = m;
1697  if (!modified)
1699  else
1700  modifiedState = -1;
1701 
1702  emit q->modificationChanged(modified);
1703 }
1704 
1706 {
1707  if (maximumBlockCount <= 0)
1708  return false;
1710  return false;
1711 
1712  beginEditBlock();
1713 
1714  const int blocksToRemove = blocks.numNodes() - maximumBlockCount;
1715  QTextCursor cursor(this, 0);
1717 
1718  unreachableCharacterCount += cursor.selectionEnd() - cursor.selectionStart();
1719 
1720  // preserve the char format of the paragraph that is to become the new first one
1721  QTextCharFormat charFmt = cursor.blockCharFormat();
1722  cursor.removeSelectedText();
1723  cursor.setBlockCharFormat(charFmt);
1724 
1725  endEditBlock();
1726 
1728 
1729  return true;
1730 }
1731 
1734 {
1735  Q_ASSERT(from <= to);
1736  foreach (QTextCursorPrivate *curs, cursors)
1737  curs->aboutToRemoveCell(from, to);
1738 }
1739 
void appendUndoItem(QAbstractUndoItem *)
Appends a custom undo item to the undo stack.
int objectIndex() const
Returns the index of the format object, or -1 if the format object is invalid.
T qobject_cast(QObject *object)
Definition: qobject.h:375
QBool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition: qstring.h:904
QTextCharFormat charFormat(int index) const
Definition: qtextformat_p.h:85
QTextEngine * engine() const
Definition: qtextlayout.h:180
QTextObject * objectForIndex(int objectIndex) const
int objectFormatIndex(int objectIndex) const
virtual void undo()=0
The QTextCharFormat class provides formatting information for characters in a QTextDocument.
Definition: qtextformat.h:372
int remove_block(int pos, int *blockformat, int command, QTextUndoCommand::Operation op)
unsigned char c[8]
Definition: qnumeric_p.h:62
Q_DECL_CONSTEXPR const T & qMin(const T &a, const T &b)
Definition: qglobal.h:1215
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
FragmentIterator end() const
QTextFormatCollection formats
static QTextFrame * findChildFrame(QTextFrame *f, int pos)
const QChar at(int i) const
Returns the character at the given index position in the string.
Definition: qstring.h:698
void remove(int i)
Removes the element at index position i.
Definition: qvector.h:374
void remove(int pos, int length, QTextUndoCommand::Operation=QTextUndoCommand::MoveCursor)
#define it(className, varName)
QAbstractTextDocumentLayout * lout
The QTextFrame class represents a frame in a QTextDocument.
Definition: qtextobject.h:122
void deleteObject(QTextObject *object)
void emitUndoAvailable(bool available)
Iterator begin()
int selectionEnd() const
Returns the end of the selection or position() if the cursor doesn&#39;t have a selection.
void chop(int n)
Removes n characters from the end of the string.
Definition: qstring.cpp:4623
int length() const
Returns the number of characters in this string.
Definition: qstring.h:696
void setBlockCharFormat(const QTextCharFormat &format)
Sets the block char format of the current block (or all blocks that are contained in the selection) t...
QList< QTextCursorPrivate * > cursors
void insert_string(int pos, uint strPos, uint length, int format, QTextUndoCommand::Operation op)
static qreal position(QGraphicsObject *item, QDeclarativeAnchorLine::AnchorLine anchorLine)
int createObjectIndex(const QTextFormat &f)
void insert(int i, const T &t)
Inserts value at index position i in the list.
Definition: qlist.h:575
int nextCursorPosition(int oldPos, CursorMode mode=SkipCharacters) const
Returns the next valid cursor position after oldPos that respects the given cursor mode...
static bool noBlockInString(const QString &str)
void removeFrame(QTextFrame *frame)
bool isValid() const
Returns true if this block format is valid; otherwise returns false.
Definition: qtextformat.h:558
int count(const T &t) const
Returns the number of occurrences of value in the list.
Definition: qlist.h:891
#define QTextBeginningOfFrame
void setBlockFormat(const QTextBlock &from, const QTextBlock &to, const QTextBlockFormat &newFormat, FormatChangeMode mode=SetFormat)
QAbstractTextDocumentLayout * layout() const
void setLayout(QAbstractTextDocumentLayout *layout)
bool isRedoAvailable() const
The QString class provides a Unicode character string.
Definition: qstring.h:83
QTextFormat format(int idx) const
bool isValid() const
Returns true if this character format is valid; otherwise returns false.
Definition: qtextformat.h:397
QTextFormat objectFormat(int objectIndex) const
QString plainText() const
void setSize(uint node, int new_size, uint field=0)
#define Q_ASSERT(cond)
Definition: qglobal.h:1823
int firstPosition() const
Returns the first document position inside the frame.
uint insert_single(int key, uint length)
int blockCharFormatIndex(int node) const
uint erase_single(uint f)
void aboutToRemoveCell(int from, int to)
QObjectList children
Definition: qobject.h:93
The QChar class provides a 16-bit Unicode character.
Definition: qchar.h:72
QChar * data()
Returns a pointer to the data stored in the QString.
Definition: qstring.h:710
int insertBlock(int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation=QTextUndoCommand::MoveCursor)
QTextOption defaultTextOption
void insert(int pos, const QString &text, int format)
FragmentMap::ConstIterator FragmentIterator
Q_DECL_CONSTEXPR const T & qMax(const T &a, const T &b)
Definition: qglobal.h:1217
void resize(int size)
Sets the size of the vector to size.
Definition: qvector.h:342
void documentChange(int from, int length)
QTextBlock next() const
Returns the text block in the document after this block, or an empty text block if this is the last o...
#define Q_Q(Class)
Definition: qglobal.h:2483
int position() const
Returns the index of the block&#39;s first character within the document.
int leftCursorPosition(int position) const
void adjustDocumentChangesAndCursors(int from, int addedOrRemoved, QTextUndoCommand::Operation op)
static void clearFrame(QTextFrame *f)
bool setPosition(int newPosition)
Definition: qtextcursor_p.h:78
static bool isValidBlockSeparator(const QChar &ch)
int type() const
Returns the type of this format.
#define QT_RETHROW
Definition: qglobal.h:1539
void append(const T &t)
Inserts value at the end of the list.
Definition: qlist.h:507
void clearUndoRedoStacks(QTextDocument::Stacks stacksToClear, bool emitSignals=false)
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
FragmentIterator find(int pos) const
int objectIndex() const
Returns the object index of this object.
QTextFrame * frameAt(int pos) const
int lastPosition() const
Returns the last document position inside the frame.
int size() const
Returns the number of characters in this string.
Definition: qstring.h:102
const QChar * unicode() const
Returns a &#39;\0&#39;-terminated Unicode representation of the string.
Definition: qstring.h:706
The QTextFormat class provides formatting information for a QTextDocument.
Definition: qtextformat.h:129
QTextFrame * parentFrame() const
Returns the frame&#39;s parent frame.
The QTextCursor class offers an API to access and modify QTextDocuments.
Definition: qtextcursor.h:70
QTextFrame * rootFrame() const
bool isUndoAvailable() const
static bool isAncestorFrame(QTextFrame *possibleAncestor, QTextFrame *child)
void enableUndoRedo(bool enable)
const T value(const Key &key) const
Returns the value associated with the key key.
Definition: qmap.h:499
void changeObjectFormat(QTextObject *group, int format)
void insert_frame(QTextFrame *f)
const T & at(int i) const
Returns the item at index position i in the list.
Definition: qlist.h:468
#define emit
Definition: qobjectdefs.h:76
QTextObject * objectForFormat(int formatIndex) const
QTextObject * createObject(const QTextFormat &newFormat, int objectIndex=-1)
#define PMDEBUG
void invalidate() const
QAbstractUndoItem * custom
void append(const T &t)
Inserts value at the end of the vector.
Definition: qvector.h:573
QMap< QUrl, QVariant > cachedResources
QTextLayout * layout
int length(uint field=0) const
The QTextBlock class provides a container for text fragments in a QTextDocument.
Definition: qtextobject.h:199
static const char * data(const QByteArray &arr)
unsigned int uint
Definition: qglobal.h:996
static Bigint * diff(Bigint *a, Bigint *b)
#define QT_INIT_TEXTUNDOCOMMAND(c, a1, a2, a3, a4, a5, a6, a7, a8)
uint position(uint node, uint field=0) const
void undoCommandAdded()
This signal is emitted every time a new level of undo is added to the QTextDocument.
static const QTextBlockData * block(const QTextBlock &it)
void clear()
Removes all items from the list.
Definition: qlist.h:764
int length() const
Returns the length of the block in characters.
QTextBlockFormat blockFormat(int index) const
Definition: qtextformat_p.h:83
The QTextTable class represents a table in a QTextDocument.
Definition: qtexttable.h:103
void emitRedoAvailable(bool available)
#define QT_CATCH(A)
Definition: qglobal.h:1537
uint next(uint n) const
void squeeze()
Releases any memory not required to store the character data.
Definition: qstring.h:114
void removeSelectedText()
If there is a selection, its content is deleted; otherwise does nothing.
int rightCursorPosition(int position) const
virtual void blockInserted(const QTextBlock &block)
Appends the given block to the end of the group.
virtual void redo()=0
friend class QTextCursor
uint size(uint node, uint field=0) const
virtual QTextObject * createObject(const QTextFormat &f)
Creates and returns a new document object (a QTextObject), based on the given format.
QTextCharFormat blockCharFormat() const
Returns the block character format of the block the cursor is in.
Iterator find(int k, uint field=0)
iterator begin()
Returns an STL-style iterator pointing to the first item in the map.
Definition: qmap.h:372
void resize(int size)
Sets the size of the string to size characters.
Definition: qstring.cpp:1353
void setObjectFormatIndex(int objectIndex, int formatIndex)
QList< QTextFrame * > childFrames() const
Returns a (possibly empty) list of the frame&#39;s child frames.
Fragment * fragment(uint index)
int remove(const Key &key)
Removes all the items that have the key key from the map.
Definition: qmap.h:662
QString mid(int position, int n=-1) const Q_REQUIRED_RESULT
Returns a string that contains n characters of this string, starting at the specified position index...
Definition: qstring.cpp:3706
void invalidate()
virtual void blockRemoved(const QTextBlock &block)
Removes the given block from the group; the block itself is not deleted, it simply isn&#39;t a member of ...
int previousCursorPosition(int position, QTextLayout::CursorMode mode) const
QString & append(QChar c)
Definition: qstring.cpp:1777
The QMap::iterator class provides an STL-style non-const iterator for QMap and QMultiMap.
Definition: qmap.h:233
void merge(const QTextFormat &other)
Merges the other format with this format; where there are conflicts the other format takes precedence...
int capacity() const
Returns the maximum number of characters that can be stored in the string without forcing a reallocat...
Definition: qstring.h:727
int nextCursorPosition(int position, QTextLayout::CursorMode mode) const
FragmentIterator begin() const
The QTextBlockGroup class provides a container for text blocks within a QTextDocument.
Definition: qtextobject.h:94
void clear()
Clears the contents of the string and makes it empty.
Definition: qstring.h:723
iterator end()
Returns an STL-style iterator pointing to the imaginary item after the last item in the map...
Definition: qmap.h:375
uint previous(uint n) const
AdjustResult adjustPosition(int positionOfChange, int charsAddedOrRemoved, QTextUndoCommand::Operation op)
Definition: qtextcursor.cpp:94
uint findNode(int k, uint field=0) const
quint32 size_array[N]
bool isBlockFormat() const
Returns true if this text format is a BlockFormat; otherwise returns false.
Definition: qtextformat.h:319
int key
The QTextFrameFormat class provides formatting information for frames in a QTextDocument.
Definition: qtextformat.h:727
unsigned int quint32
Definition: qglobal.h:938
int size() const
Returns the number of items in the list.
Definition: qlist.h:137
The QTextBlockFormat class provides formatting information for blocks of text in a QTextDocument...
Definition: qtextformat.h:545
void move(int from, int to, int length, QTextUndoCommand::Operation=QTextUndoCommand::MoveCursor)
int numNodes() const
QFactoryLoader * l
QTextDocumentPrivate * priv
int rightCursorPosition(int oldPos) const
Returns the cursor position to the right of oldPos, next to it.
int remove_string(int pos, uint length, QTextUndoCommand::Operation op)
bool isCharFormat() const
Returns true if this text format is a CharFormat; otherwise returns false.
Definition: qtextformat.h:318
QVector< QTextUndoCommand > undoStack
int insert_block(int pos, uint strPos, int format, int blockformat, QTextUndoCommand::Operation op, int command)
bool movePosition(MoveOperation op, MoveMode=MoveAnchor, int n=1)
Moves the cursor by performing the given operation n times, using the specified mode, and returns true if all operations were completed successfully; otherwise returns false.
QObject * parent
Definition: qobject.h:92
The QTextDocument class holds formatted text that can be viewed and edited using a QTextEdit...
bool isValid() const
Returns true if this text block is valid; otherwise returns false.
Definition: qtextobject.h:208
iterator erase(iterator it)
Removes the (key, value) pair pointed to by the iterator pos from the map, and returns an iterator to...
Definition: qmap.h:717
void scan_frames(int pos, int charsRemoved, int charsAdded)
void setMargin(qreal margin)
Sets the frame&#39;s margin in pixels.
void setWrapMode(WrapMode wrap)
Sets the option&#39;s text wrap mode to the given mode.
Definition: qtextoption.h:109
Qt::CursorMoveStyle defaultCursorMoveStyle
bool isEmpty() const
Returns true if the vector has size 0; otherwise returns false.
Definition: qvector.h:139
void clearProperty(int propertyId)
Clears the value of the property given by propertyId.
int leftCursorPosition(int oldPos) const
Returns the cursor position to the left of oldPos, next to it.
QTextFrame * insertFrame(int start, int end, const QTextFrameFormat &format)
virtual void blockFormatChanged(const QTextBlock &block)
This function is called whenever the specified block of text is changed.
QTextBlock blocksFind(int pos) const
void setCharFormat(int pos, int length, const QTextCharFormat &newFormat, FormatChangeMode mode=SetFormat)
void setObjectIndex(int object)
Sets the format object&#39;s object index.
bool tryMerge(const QTextUndoCommand &other)
The QTextObject class is a base class for different kinds of objects that can group parts of a QTextD...
Definition: qtextobject.h:64
int previousCursorPosition(int oldPos, CursorMode mode=SkipCharacters) const
Returns the first valid cursor position before oldPos that respects the given cursor mode...
int indexForFormat(const QTextFormat &f)
#define Q_UNUSED(x)
Indicates to the compiler that the parameter with the specified name is not used in the body of a fun...
Definition: qglobal.h:1729
int size() const
Returns the number of items in the vector.
Definition: qvector.h:137
#define QT_TRY
Definition: qglobal.h:1536
void aboutToRemoveCell(int cursorFrom, int cursorEnd)
This method is called from QTextTable when it is about to remove a table-cell to allow cursors to upd...
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition: qstring.h:712
QMap< int, QTextObject * > objects
void clear()
Removes all items from the map.
Definition: qmap.h:444
int selectionStart() const
Returns the start of the selection or position() if the cursor doesn&#39;t have a selection.
void setTabStop(qreal tabStop)
Sets the default distance in device units between tab stops to the value specified by tabStop...
Definition: qtextoption.h:154
virtual void documentChanged(int from, int charsRemoved, int charsAdded)=0
This function is called whenever the contents of the document change.
QTextDocument * document()
The QList class is a template class that provides lists.
Definition: qdatastream.h:62
#define QTextEndOfFrame
The QAbstractTextDocumentLayout class is an abstract base class used to implement custom layouts for ...
void removeAt(int i)
Removes the item at index position i.
Definition: qlist.h:480
QTextLayout * layout() const
Returns the QTextLayout that is used to lay out and display the block&#39;s contents. ...