Qt 4.8
qtexttable.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 "qtexttable.h"
43 #include "qtextcursor.h"
44 #include "qtextformat.h"
45 #include <qdebug.h>
46 #include "qtexttable_p.h"
47 #include "qvarlengtharray.h"
48 #include "private/qfunctions_p.h"
49 
50 #include <stdlib.h>
51 
53 
135 {
136  QTextCharFormat fmt = format;
141 
143  QTextCharFormat oldFormat = c->charFormat(frag->format);
144  fmt.setTableCellRowSpan(oldFormat.tableCellRowSpan());
146 
148 }
149 
154 {
157 
160  return fmt;
161 }
162 
174 {
177 }
178 
185 {
186  const QTextTablePrivate *tp = table->d_func();
187  if (tp->dirty)
188  tp->update();
189 
190  int idx = tp->findCellIndex(fragment);
191  if (idx == -1)
192  return idx;
193  return tp->cellIndices.at(idx) / tp->nCols;
194 }
195 
202 {
203  const QTextTablePrivate *tp = table->d_func();
204  if (tp->dirty)
205  tp->update();
206 
207  int idx = tp->findCellIndex(fragment);
208  if (idx == -1)
209  return idx;
210  return tp->cellIndices.at(idx) % tp->nCols;
211 }
212 
219 {
220  return format().tableCellRowSpan();
221 }
222 
229 {
230  return format().tableCellColumnSpan();
231 }
232 
250 {
251  return QTextCursor(table->d_func()->pieceTable, firstPosition());
252 }
253 
260 {
261  return QTextCursor(table->d_func()->pieceTable, lastPosition());
262 }
263 
264 
274 {
276  return p->fragmentMap().position(fragment) + 1;
277 }
278 
288 {
290  const QTextTablePrivate *td = table->d_func();
291  int index = table->d_func()->findCellIndex(fragment);
292  int f;
293  if (index != -1)
294  f = td->cells.value(index + 1, td->fragment_end);
295  else
296  f = td->fragment_end;
297  return p->fragmentMap().position(f);
298 }
299 
300 
307 {
309  int b = p->blockMap().findNode(firstPosition());
310  int e = p->blockMap().findNode(lastPosition()+1);
311  return QTextFrame::iterator(const_cast<QTextTable *>(table), b, b, e);
312 }
313 
320 {
322  int b = p->blockMap().findNode(firstPosition());
323  int e = p->blockMap().findNode(lastPosition()+1);
324  return QTextFrame::iterator(const_cast<QTextTable *>(table), e, b, e);
325 }
326 
327 
358 {
359  if (grid)
360  free(grid);
361 }
362 
363 
364 QTextTable *QTextTablePrivate::createTable(QTextDocumentPrivate *pieceTable, int pos, int rows, int cols, const QTextTableFormat &tableFormat)
365 {
366  QTextTableFormat fmt = tableFormat;
367  fmt.setColumns(cols);
368  QTextTable *table = qobject_cast<QTextTable *>(pieceTable->createObject(fmt));
369  Q_ASSERT(table);
370 
371  pieceTable->beginEditBlock();
372 
373 // qDebug("---> createTable: rows=%d, cols=%d at %d", rows, cols, pos);
374  // add block after table
375  QTextCharFormat charFmt;
376  charFmt.setObjectIndex(table->objectIndex());
378 
379 
380  int charIdx = pieceTable->formatCollection()->indexForFormat(charFmt);
381  int cellIdx = pieceTable->formatCollection()->indexForFormat(QTextBlockFormat());
382 
383  QTextTablePrivate *d = table->d_func();
384  d->blockFragmentUpdates = true;
385 
386  d->fragment_start = pieceTable->insertBlock(QTextBeginningOfFrame, pos, cellIdx, charIdx);
387  d->cells.append(d->fragment_start);
388  ++pos;
389 
390  for (int i = 1; i < rows*cols; ++i) {
391  d->cells.append(pieceTable->insertBlock(QTextBeginningOfFrame, pos, cellIdx, charIdx));
392 // qDebug(" addCell at %d", pos);
393  ++pos;
394  }
395 
396  d->fragment_end = pieceTable->insertBlock(QTextEndOfFrame, pos, cellIdx, charIdx);
397 // qDebug(" addEOR at %d", pos);
398  ++pos;
399 
400  d->blockFragmentUpdates = false;
401  d->dirty = true;
402 
403  pieceTable->endEditBlock();
404 
405  return table;
406 }
407 
409 {
411  : pos(_pos), fragmentMap(map) {}
414 };
415 
417 {
418  return helper.fragmentMap.position(fragment) < helper.pos;
419 }
420 
422 {
423  return helper.pos < helper.fragmentMap.position(fragment);
424 }
425 
427 {
428  QFragmentFindHelper helper(pieceTable->fragmentMap().position(fragment),
429  pieceTable->fragmentMap());
430  QList<int>::ConstIterator it = qBinaryFind(cells.begin(), cells.end(), helper);
431  if (it == cells.end())
432  return -1;
433  return it - cells.begin();
434 }
435 
437 {
438  dirty = true;
439  if (blockFragmentUpdates)
440  return;
441  if (type == QTextBeginningOfFrame) {
442  Q_ASSERT(cells.indexOf(fragment) == -1);
443  const uint pos = pieceTable->fragmentMap().position(fragment);
444  QFragmentFindHelper helper(pos, pieceTable->fragmentMap());
445  QList<int>::Iterator it = qLowerBound(cells.begin(), cells.end(), helper);
446  cells.insert(it, fragment);
447  if (!fragment_start || pos < pieceTable->fragmentMap().position(fragment_start))
448  fragment_start = fragment;
449  return;
450  }
451  QTextFramePrivate::fragmentAdded(type, fragment);
452 }
453 
455 {
456  dirty = true;
457  if (blockFragmentUpdates)
458  return;
459  if (type == QTextBeginningOfFrame) {
460  Q_ASSERT(cells.indexOf(fragment) != -1);
461  cells.removeAll(fragment);
462  if (fragment_start == fragment && cells.size()) {
463  fragment_start = cells.at(0);
464  }
465  if (fragment_start != fragment)
466  return;
467  }
468  QTextFramePrivate::fragmentRemoved(type, fragment);
469 }
470 
479 {
480  Q_Q(const QTextTable);
481  nCols = q->format().columns();
482  nRows = (cells.size() + nCols-1)/nCols;
483 // qDebug(">>>> QTextTablePrivate::update, nRows=%d, nCols=%d", nRows, nCols);
484 
485  grid = q_check_ptr((int *)realloc(grid, nRows*nCols*sizeof(int)));
486  memset(grid, 0, nRows*nCols*sizeof(int));
487 
488  QTextDocumentPrivate *p = pieceTable;
490 
491  cellIndices.resize(cells.size());
492 
493  int cell = 0;
494  for (int i = 0; i < cells.size(); ++i) {
495  int fragment = cells.at(i);
497  int rowspan = fmt.tableCellRowSpan();
498  int colspan = fmt.tableCellColumnSpan();
499 
500  // skip taken cells
501  while (cell < nRows*nCols && grid[cell])
502  ++cell;
503 
504  int r = cell/nCols;
505  int c = cell%nCols;
506  cellIndices[i] = cell;
507 
508  if (r + rowspan > nRows) {
509  grid = q_check_ptr((int *)realloc(grid, sizeof(int)*(r + rowspan)*nCols));
510  memset(grid + (nRows*nCols), 0, sizeof(int)*(r+rowspan-nRows)*nCols);
511  nRows = r + rowspan;
512  }
513 
514  Q_ASSERT(c + colspan <= nCols);
515  for (int ii = 0; ii < rowspan; ++ii) {
516  for (int jj = 0; jj < colspan; ++jj) {
517  Q_ASSERT(grid[(r+ii)*nCols + c+jj] == 0);
518  grid[(r+ii)*nCols + c+jj] = fragment;
519 // qDebug(" setting cell %d span=%d/%d at %d/%d", fragment, rowspan, colspan, r+ii, c+jj);
520  }
521  }
522  }
523 // qDebug("<<<< end: nRows=%d, nCols=%d", nRows, nCols);
524 
525  dirty = false;
526 }
527 
528 
529 
530 
531 
607  : QTextFrame(*new QTextTablePrivate(doc), doc)
608 {
609 }
610 
616 {
617 }
618 
619 
630 QTextTableCell QTextTable::cellAt(int row, int col) const
631 {
632  Q_D(const QTextTable);
633  if (d->dirty)
634  d->update();
635 
636  if (row < 0 || row >= d->nRows || col < 0 || col >= d->nCols)
637  return QTextTableCell();
638 
639  return QTextTableCell(this, d->grid[row*d->nCols + col]);
640 }
641 
652 {
653  Q_D(const QTextTable);
654  if (d->dirty)
655  d->update();
656 
657  uint pos = (uint)position;
658  const QTextDocumentPrivate::FragmentMap &map = d->pieceTable->fragmentMap();
659  if (position < 0 || map.position(d->fragment_start) >= pos || map.position(d->fragment_end) < pos)
660  return QTextTableCell();
661 
662  QFragmentFindHelper helper(position, map);
663  QList<int>::ConstIterator it = qLowerBound(d->cells.begin(), d->cells.end(), helper);
664  if (it != d->cells.begin())
665  --it;
666 
667  return QTextTableCell(this, *it);
668 }
669 
681 {
682  return cellAt(c.position());
683 }
684 
695 void QTextTable::resize(int rows, int cols)
696 {
697  Q_D(QTextTable);
698  if (d->dirty)
699  d->update();
700 
701  int nRows = this->rows();
702  int nCols = this->columns();
703 
704  if (rows == nRows && cols == nCols)
705  return;
706 
707  d->pieceTable->beginEditBlock();
708 
709  if (nCols < cols)
710  insertColumns(nCols, cols - nCols);
711  else if (nCols > cols)
712  removeColumns(cols, nCols - cols);
713 
714  if (nRows < rows)
715  insertRows(nRows, rows-nRows);
716  else if (nRows > rows)
717  removeRows(rows, nRows-rows);
718 
719  d->pieceTable->endEditBlock();
720 }
721 
732 void QTextTable::insertRows(int pos, int num)
733 {
734  Q_D(QTextTable);
735  if (num <= 0)
736  return;
737 
738  if (d->dirty)
739  d->update();
740 
741  if (pos > d->nRows || pos < 0)
742  pos = d->nRows;
743 
744 // qDebug() << "-------- insertRows" << pos << num;
745  QTextDocumentPrivate *p = d->pieceTable;
747  p->beginEditBlock();
748 
749  int extended = 0;
750  int insert_before = 0;
751  if (pos > 0 && pos < d->nRows) {
752  for (int i = 0; i < d->nCols; ++i) {
753  int cell = d->grid[pos*d->nCols + i];
754  if (cell == d->grid[(pos-1)*d->nCols+i]) {
755  // cell spans the insertion place, extend it
757  QTextCharFormat fmt = c->charFormat(it->format);
758  fmt.setTableCellRowSpan(fmt.tableCellRowSpan() + num);
759  p->setCharFormat(it.position(), 1, fmt);
760  extended++;
761  } else if (!insert_before) {
762  insert_before = cell;
763  }
764  }
765  } else {
766  insert_before = (pos == 0 ? d->grid[0] : d->fragment_end);
767  }
768  if (extended < d->nCols) {
769  Q_ASSERT(insert_before);
771  QTextCharFormat fmt = c->charFormat(it->format);
772  fmt.setTableCellRowSpan(1);
773  fmt.setTableCellColumnSpan(1);
774  Q_ASSERT(fmt.objectIndex() == objectIndex());
775  int pos = it.position();
776  int cfmt = p->formatCollection()->indexForFormat(fmt);
778 // qDebug("inserting %d cells, nCols=%d extended=%d", num*(d->nCols-extended), d->nCols, extended);
779  for (int i = 0; i < num*(d->nCols-extended); ++i)
781  }
782 
783 // qDebug() << "-------- end insertRows" << pos << num;
784  p->endEditBlock();
785 }
786 
797 void QTextTable::insertColumns(int pos, int num)
798 {
799  Q_D(QTextTable);
800  if (num <= 0)
801  return;
802 
803  if (d->dirty)
804  d->update();
805 
806  if (pos > d->nCols || pos < 0)
807  pos = d->nCols;
808 
809 // qDebug() << "-------- insertCols" << pos << num;
810  QTextDocumentPrivate *p = d->pieceTable;
812  p->beginEditBlock();
813 
814  QList<int> extendedSpans;
815  for (int i = 0; i < d->nRows; ++i) {
816  int cell;
817  if (i == d->nRows - 1 && pos == d->nCols) {
818  cell = d->fragment_end;
819  } else {
820  int logicalGridIndexBeforePosition = pos > 0
821  ? d->findCellIndex(d->grid[i*d->nCols + pos - 1])
822  : -1;
823 
824  // Search for the logical insertion point by skipping past cells which are not the first
825  // cell in a rowspan. This means any cell for which the logical grid index is
826  // less than the logical cell index of the cell before the insertion.
827  int logicalGridIndex;
828  int gridArrayOffset = i*d->nCols + pos;
829  do {
830  cell = d->grid[gridArrayOffset];
831  logicalGridIndex = d->findCellIndex(cell);
832  gridArrayOffset++;
833  } while (logicalGridIndex < logicalGridIndexBeforePosition
834  && gridArrayOffset < d->nRows*d->nCols);
835 
836  if (logicalGridIndex < logicalGridIndexBeforePosition
837  && gridArrayOffset == d->nRows*d->nCols)
838  cell = d->fragment_end;
839  }
840 
841  if (pos > 0 && pos < d->nCols && cell == d->grid[i*d->nCols + pos - 1]) {
842  // cell spans the insertion place, extend it
843  if (!extendedSpans.contains(cell)) {
845  QTextCharFormat fmt = c->charFormat(it->format);
846  fmt.setTableCellColumnSpan(fmt.tableCellColumnSpan() + num);
847  p->setCharFormat(it.position(), 1, fmt);
848  d->dirty = true;
849  extendedSpans << cell;
850  }
851  } else {
852  /* If the next cell is spanned from the row above, we need to find the right position
853  to insert to */
854  if (i > 0 && pos < d->nCols && cell == d->grid[(i-1) * d->nCols + pos]) {
855  int gridIndex = i*d->nCols + pos;
856  const int gridEnd = d->nRows * d->nCols - 1;
857  while (gridIndex < gridEnd && cell == d->grid[gridIndex]) {
858  ++gridIndex;
859  }
860  if (gridIndex == gridEnd)
861  cell = d->fragment_end;
862  else
863  cell = d->grid[gridIndex];
864  }
866  QTextCharFormat fmt = c->charFormat(it->format);
867  fmt.setTableCellRowSpan(1);
868  fmt.setTableCellColumnSpan(1);
869  Q_ASSERT(fmt.objectIndex() == objectIndex());
870  int position = it.position();
871  int cfmt = p->formatCollection()->indexForFormat(fmt);
873  for (int i = 0; i < num; ++i)
875  }
876  }
877 
878  QTextTableFormat tfmt = format();
879  tfmt.setColumns(tfmt.columns()+num);
880  QVector<QTextLength> columnWidths = tfmt.columnWidthConstraints();
881  if (! columnWidths.isEmpty()) {
882  for (int i = num; i > 0; --i)
883  columnWidths.insert(pos, columnWidths[qMax(0, pos-1)]);
884  }
885  tfmt.setColumnWidthConstraints (columnWidths);
887 
888 // qDebug() << "-------- end insertCols" << pos << num;
889  p->endEditBlock();
890 }
891 
901 void QTextTable::appendRows(int count)
902 {
903  insertRows(rows(), count);
904 }
905 
916 {
917  insertColumns(columns(), count);
918 }
919 
930 void QTextTable::removeRows(int pos, int num)
931 {
932  Q_D(QTextTable);
933 // qDebug() << "-------- removeRows" << pos << num;
934 
935  if (num <= 0 || pos < 0)
936  return;
937  if (d->dirty)
938  d->update();
939  if (pos >= d->nRows)
940  return;
941  if (pos+num > d->nRows)
942  num = d->nRows - pos;
943 
944  QTextDocumentPrivate *p = d->pieceTable;
945  QTextFormatCollection *collection = p->formatCollection();
946  p->beginEditBlock();
947 
948  // delete whole table?
949  if (pos == 0 && num == d->nRows) {
950  const int pos = p->fragmentMap().position(d->fragment_start);
951  p->remove(pos, p->fragmentMap().position(d->fragment_end) - pos + 1);
952  p->endEditBlock();
953  return;
954  }
955 
956  p->aboutToRemoveCell(cellAt(pos, 0).firstPosition(), cellAt(pos + num - 1, d->nCols - 1).lastPosition());
957 
958  QList<int> touchedCells;
959  for (int r = pos; r < pos + num; ++r) {
960  for (int c = 0; c < d->nCols; ++c) {
961  int cell = d->grid[r*d->nCols + c];
962  if (touchedCells.contains(cell))
963  continue;
964  touchedCells << cell;
966  QTextCharFormat fmt = collection->charFormat(it->format);
967  int span = fmt.tableCellRowSpan();
968  if (span > 1) {
969  fmt.setTableCellRowSpan(span - 1);
970  p->setCharFormat(it.position(), 1, fmt);
971  } else {
972  // remove cell
973  int index = d->cells.indexOf(cell) + 1;
974  int f_end = index < d->cells.size() ? d->cells.at(index) : d->fragment_end;
975  p->remove(it.position(), p->fragmentMap().position(f_end) - it.position());
976  }
977  }
978  }
979 
980  p->endEditBlock();
981 // qDebug() << "-------- end removeRows" << pos << num;
982 }
983 
995 void QTextTable::removeColumns(int pos, int num)
996 {
997  Q_D(QTextTable);
998 // qDebug() << "-------- removeCols" << pos << num;
999 
1000  if (num <= 0 || pos < 0)
1001  return;
1002  if (d->dirty)
1003  d->update();
1004  if (pos >= d->nCols)
1005  return;
1006  if (pos + num > d->nCols)
1007  pos = d->nCols - num;
1008 
1009  QTextDocumentPrivate *p = d->pieceTable;
1010  QTextFormatCollection *collection = p->formatCollection();
1011  p->beginEditBlock();
1012 
1013  // delete whole table?
1014  if (pos == 0 && num == d->nCols) {
1015  const int pos = p->fragmentMap().position(d->fragment_start);
1016  p->remove(pos, p->fragmentMap().position(d->fragment_end) - pos + 1);
1017  p->endEditBlock();
1018  return;
1019  }
1020 
1021  p->aboutToRemoveCell(cellAt(0, pos).firstPosition(), cellAt(d->nRows - 1, pos + num - 1).lastPosition());
1022 
1023  QList<int> touchedCells;
1024  for (int r = 0; r < d->nRows; ++r) {
1025  for (int c = pos; c < pos + num; ++c) {
1026  int cell = d->grid[r*d->nCols + c];
1028  QTextCharFormat fmt = collection->charFormat(it->format);
1029  int span = fmt.tableCellColumnSpan();
1030  if (touchedCells.contains(cell) && span <= 1)
1031  continue;
1032  touchedCells << cell;
1033 
1034  if (span > 1) {
1035  fmt.setTableCellColumnSpan(span - 1);
1036  p->setCharFormat(it.position(), 1, fmt);
1037  } else {
1038  // remove cell
1039  int index = d->cells.indexOf(cell) + 1;
1040  int f_end = index < d->cells.size() ? d->cells.at(index) : d->fragment_end;
1041  p->remove(it.position(), p->fragmentMap().position(f_end) - it.position());
1042  }
1043  }
1044  }
1045 
1046  QTextTableFormat tfmt = format();
1047  tfmt.setColumns(tfmt.columns()-num);
1048  QVector<QTextLength> columnWidths = tfmt.columnWidthConstraints();
1049  if (columnWidths.count() > pos) {
1050  columnWidths.remove(pos, num);
1051  tfmt.setColumnWidthConstraints (columnWidths);
1052  }
1053  QTextObject::setFormat(tfmt);
1054 
1055  p->endEditBlock();
1056 // qDebug() << "-------- end removeCols" << pos << num;
1057 }
1058 
1072 void QTextTable::mergeCells(int row, int column, int numRows, int numCols)
1073 {
1074  Q_D(QTextTable);
1075 
1076  if (d->dirty)
1077  d->update();
1078 
1079  QTextDocumentPrivate *p = d->pieceTable;
1081 
1082  const QTextTableCell cell = cellAt(row, column);
1083  if (!cell.isValid() || row != cell.row() || column != cell.column())
1084  return;
1085 
1086  QTextCharFormat fmt = cell.format();
1087  const int rowSpan = fmt.tableCellRowSpan();
1088  const int colSpan = fmt.tableCellColumnSpan();
1089 
1090  numRows = qMin(numRows, rows() - cell.row());
1091  numCols = qMin(numCols, columns() - cell.column());
1092 
1093  // nothing to merge?
1094  if (numRows < rowSpan || numCols < colSpan)
1095  return;
1096 
1097  // check the edges of the merge rect to make sure no cell spans the edge
1098  for (int r = row; r < row + numRows; ++r) {
1099  if (cellAt(r, column) == cellAt(r, column - 1))
1100  return;
1101  if (cellAt(r, column + numCols) == cellAt(r, column + numCols - 1))
1102  return;
1103  }
1104 
1105  for (int c = column; c < column + numCols; ++c) {
1106  if (cellAt(row, c) == cellAt(row - 1, c))
1107  return;
1108  if (cellAt(row + numRows, c) == cellAt(row + numRows - 1, c))
1109  return;
1110  }
1111 
1112  p->beginEditBlock();
1113 
1114  const int origCellPosition = cell.firstPosition() - 1;
1115 
1116  const int cellFragment = d->grid[row * d->nCols + column];
1117 
1118  // find the position at which to insert the contents of the merged cells
1119  QFragmentFindHelper helper(origCellPosition, p->fragmentMap());
1120  QList<int>::Iterator it = qBinaryFind(d->cells.begin(), d->cells.end(), helper);
1121  Q_ASSERT(it != d->cells.end());
1122  Q_ASSERT(*it == cellFragment);
1123  const int insertCellIndex = it - d->cells.begin();
1124  int insertFragment = d->cells.value(insertCellIndex + 1, d->fragment_end);
1125  uint insertPos = p->fragmentMap().position(insertFragment);
1126 
1127  d->blockFragmentUpdates = true;
1128 
1129  bool rowHasText = cell.firstCursorPosition().block().length();
1130  bool needsParagraph = rowHasText && colSpan == numCols;
1131 
1132  // find all cells that will be erased by the merge
1133  for (int r = row; r < row + numRows; ++r) {
1134  int firstColumn = r < row + rowSpan ? column + colSpan : column;
1135 
1136  // don't recompute the cell index for the first row
1137  int firstCellIndex = r == row ? insertCellIndex + 1 : -1;
1138  int cellIndex = firstCellIndex;
1139 
1140  for (int c = firstColumn; c < column + numCols; ++c) {
1141  const int fragment = d->grid[r * d->nCols + c];
1142 
1143  // already handled?
1144  if (fragment == cellFragment)
1145  continue;
1146 
1148  uint pos = it.position();
1149 
1150  if (firstCellIndex == -1) {
1151  QFragmentFindHelper helper(pos, p->fragmentMap());
1152  QList<int>::Iterator it = qBinaryFind(d->cells.begin(), d->cells.end(), helper);
1153  Q_ASSERT(it != d->cells.end());
1154  Q_ASSERT(*it == fragment);
1155  firstCellIndex = cellIndex = it - d->cells.begin();
1156  }
1157 
1158  ++cellIndex;
1159 
1160  QTextCharFormat fmt = fc->charFormat(it->format);
1161 
1162  const int cellRowSpan = fmt.tableCellRowSpan();
1163  const int cellColSpan = fmt.tableCellColumnSpan();
1164 
1165  // update the grid for this cell
1166  for (int i = r; i < r + cellRowSpan; ++i)
1167  for (int j = c; j < c + cellColSpan; ++j)
1168  d->grid[i * d->nCols + j] = cellFragment;
1169 
1170  // erase the cell marker
1171  p->remove(pos, 1);
1172 
1173  const int nextFragment = d->cells.value(cellIndex, d->fragment_end);
1174  const uint nextPos = p->fragmentMap().position(nextFragment);
1175 
1176  Q_ASSERT(nextPos >= pos);
1177 
1178  // merge the contents of the cell (if not empty)
1179  if (nextPos > pos) {
1180  if (needsParagraph) {
1181  needsParagraph = false;
1182  QTextCursor(p, insertPos++).insertBlock();
1183  p->move(pos + 1, insertPos, nextPos - pos);
1184  } else if (rowHasText) {
1185  QTextCursor(p, insertPos++).insertText(QLatin1String(" "));
1186  p->move(pos + 1, insertPos, nextPos - pos);
1187  } else {
1188  p->move(pos, insertPos, nextPos - pos);
1189  }
1190 
1191  insertPos += nextPos - pos;
1192  rowHasText = true;
1193  }
1194  }
1195 
1196  if (rowHasText) {
1197  needsParagraph = true;
1198  rowHasText = false;
1199  }
1200 
1201  // erase cells from last row
1202  if (firstCellIndex >= 0) {
1203  d->cellIndices.remove(firstCellIndex, cellIndex - firstCellIndex);
1204  d->cells.erase(d->cells.begin() + firstCellIndex, d->cells.begin() + cellIndex);
1205  }
1206  }
1207 
1208  d->fragment_start = d->cells.first();
1209 
1210  fmt.setTableCellRowSpan(numRows);
1211  fmt.setTableCellColumnSpan(numCols);
1212  p->setCharFormat(origCellPosition, 1, fmt);
1213 
1214  d->blockFragmentUpdates = false;
1215  d->dirty = false;
1216 
1217  p->endEditBlock();
1218 }
1219 
1232 {
1233  if (!cursor.hasComplexSelection())
1234  return;
1235 
1236  int firstRow, numRows, firstColumn, numColumns;
1237  cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
1238  mergeCells(firstRow, firstColumn, numRows, numColumns);
1239 }
1240 
1255 void QTextTable::splitCell(int row, int column, int numRows, int numCols)
1256 {
1257  Q_D(QTextTable);
1258 
1259  if (d->dirty)
1260  d->update();
1261 
1262  QTextDocumentPrivate *p = d->pieceTable;
1264 
1265  const QTextTableCell cell = cellAt(row, column);
1266  if (!cell.isValid())
1267  return;
1268  row = cell.row();
1269  column = cell.column();
1270 
1271  QTextCharFormat fmt = cell.format();
1272  const int rowSpan = fmt.tableCellRowSpan();
1273  const int colSpan = fmt.tableCellColumnSpan();
1274 
1275  // nothing to split?
1276  if (numRows > rowSpan || numCols > colSpan)
1277  return;
1278 
1279  p->beginEditBlock();
1280 
1281  const int origCellPosition = cell.firstPosition() - 1;
1282 
1283  QVarLengthArray<int> rowPositions(rowSpan);
1284 
1285  rowPositions[0] = cell.lastPosition();
1286 
1287  for (int r = row + 1; r < row + rowSpan; ++r) {
1288  // find the cell before which to insert the new cell markers
1289  int gridIndex = r * d->nCols + column;
1290  QVector<int>::iterator it = qUpperBound(d->cellIndices.begin(), d->cellIndices.end(), gridIndex);
1291  int cellIndex = it - d->cellIndices.begin();
1292  int fragment = d->cells.value(cellIndex, d->fragment_end);
1293  rowPositions[r - row] = p->fragmentMap().position(fragment);
1294  }
1295 
1296  fmt.setTableCellColumnSpan(1);
1297  fmt.setTableCellRowSpan(1);
1298  const int fmtIndex = c->indexForFormat(fmt);
1299  const int blockIndex = p->blockMap().find(cell.lastPosition())->format;
1300 
1301  int insertAdjustement = 0;
1302  for (int i = 0; i < numRows; ++i) {
1303  for (int c = 0; c < colSpan - numCols; ++c)
1304  p->insertBlock(QTextBeginningOfFrame, rowPositions[i] + insertAdjustement + c, blockIndex, fmtIndex);
1305  insertAdjustement += colSpan - numCols;
1306  }
1307 
1308  for (int i = numRows; i < rowSpan; ++i) {
1309  for (int c = 0; c < colSpan; ++c)
1310  p->insertBlock(QTextBeginningOfFrame, rowPositions[i] + insertAdjustement + c, blockIndex, fmtIndex);
1311  insertAdjustement += colSpan;
1312  }
1313 
1314  fmt.setTableCellRowSpan(numRows);
1315  fmt.setTableCellColumnSpan(numCols);
1316  p->setCharFormat(origCellPosition, 1, fmt);
1317 
1318  p->endEditBlock();
1319 }
1320 
1326 int QTextTable::rows() const
1327 {
1328  Q_D(const QTextTable);
1329  if (d->dirty)
1330  d->update();
1331 
1332  return d->nRows;
1333 }
1334 
1341 {
1342  Q_D(const QTextTable);
1343  if (d->dirty)
1344  d->update();
1345 
1346  return d->nCols;
1347 }
1348 
1349 #if 0
1350 void QTextTable::mergeCells(const QTextCursor &selection)
1351 {
1352 }
1353 #endif
1354 
1367 {
1368  Q_D(const QTextTable);
1369  QTextTableCell cell = cellAt(c);
1370  if (!cell.isValid())
1371  return QTextCursor();
1372 
1373  int row = cell.row();
1374  QTextDocumentPrivate *p = d->pieceTable;
1375  QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), d->grid[row*d->nCols]);
1376  return QTextCursor(p, it.position());
1377 }
1378 
1391 {
1392  Q_D(const QTextTable);
1393  QTextTableCell cell = cellAt(c);
1394  if (!cell.isValid())
1395  return QTextCursor();
1396 
1397  int row = cell.row() + 1;
1398  int fragment = row < d->nRows ? d->grid[row*d->nCols] : d->fragment_end;
1399  QTextDocumentPrivate *p = d->pieceTable;
1401  return QTextCursor(p, it.position() - 1);
1402 }
1403 
1415 {
1416  QTextTableFormat fmt = format;
1417  // don't try to change the number of table columns from here
1418  fmt.setColumns(columns());
1420 }
1421 
QTextCursor rowStart(const QTextCursor &c) const
Returns a cursor pointing to the start of the row that contains the given cursor. ...
T qobject_cast(QObject *object)
Definition: qobject.h:375
int columns() const
Returns the number of columns in the table.
double d
Definition: qnumeric_p.h:62
T * q_check_ptr(T *p)
Definition: qglobal.h:1857
QTextCharFormat charFormat(int index) const
Definition: qtextformat_p.h:85
Q_OUTOFLINE_TEMPLATE RandomAccessIterator qUpperBound(RandomAccessIterator begin, RandomAccessIterator end, const T &value)
Definition: qalgorithms.h:262
int type
Definition: qmetatype.cpp:239
The QTextCharFormat class provides formatting information for characters in a QTextDocument.
Definition: qtextformat.h:372
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
int columns() const
Returns the number of columns specified by the table format.
Definition: qtextformat.h:849
void remove(int i)
Removes the element at index position i.
Definition: qvector.h:374
void fragmentAdded(const QChar &type, uint fragment)
Definition: qtexttable.cpp:436
void remove(int pos, int length, QTextUndoCommand::Operation=QTextUndoCommand::MoveCursor)
#define it(className, varName)
int count(const T &t) const
Returns the number of occurrences of value in the vector.
Definition: qvector.h:742
The QTextFrame class represents a frame in a QTextDocument.
Definition: qtextobject.h:122
QTextFrame::iterator begin() const
Returns a frame iterator pointing to the beginning of the table&#39;s cell.
Definition: qtexttable.cpp:306
void insertRows(int pos, int num)
Inserts a number of rows before the row with the specified index.
Definition: qtexttable.cpp:732
Q_STATIC_GLOBAL_INLINE_OPERATOR bool operator<(int fragment, const QFragmentFindHelper &helper)
Definition: qtexttable.cpp:416
void update() const
/fn void QTextTablePrivate::update() const
Definition: qtexttable.cpp:478
void fragmentRemoved(const QChar &type, uint fragment)
Definition: qtexttable.cpp:454
static qreal position(QGraphicsObject *item, QDeclarativeAnchorLine::AnchorLine anchorLine)
QTextCursor rowEnd(const QTextCursor &c) const
Returns a cursor pointing to the end of the row that contains the given cursor.
QLatin1String(DBUS_INTERFACE_DBUS))) Q_GLOBAL_STATIC_WITH_ARGS(QString
#define Q_STATIC_GLOBAL_INLINE_OPERATOR
Definition: qfunctions_p.h:72
Q_OUTOFLINE_TEMPLATE RandomAccessIterator qBinaryFind(RandomAccessIterator begin, RandomAccessIterator end, const T &value)
Definition: qalgorithms.h:295
#define QTextBeginningOfFrame
QVector< int > cellIndices
Definition: qtexttable_p.h:79
void setTableCellRowSpan(int tableCellRowSpan)
If this character format is applied to characters in a table cell, the cell will span tableCellRowSpa...
Definition: qtextformat.h:529
QTextTable(QTextDocument *doc)
Definition: qtexttable.cpp:606
void mergeCells(int row, int col, int numRows, int numCols)
Merges the cell at the specified row and column with the adjacent cells into one cell.
void appendRows(int count)
Appends count rows at the bottom of the table.
Definition: qtexttable.cpp:901
#define Q_ASSERT(cond)
Definition: qglobal.h:1823
The QVector class is a template class that provides a dynamic array.
Definition: qdatastream.h:64
int firstPosition() const
Returns the first document position inside the frame.
#define Q_D(Class)
Definition: qglobal.h:2482
QTextCursor lastCursorPosition() const
Returns the last valid cursor position in this cell.
Definition: qtexttable.cpp:259
void removeRows(int pos, int num)
Removes a number of rows starting with the row at the specified index.
Definition: qtexttable.cpp:930
The QChar class provides a 16-bit Unicode character.
Definition: qchar.h:72
int insertBlock(int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation=QTextUndoCommand::MoveCursor)
void insertText(const QString &text)
Inserts text at the current position, using the current character format.
void insertColumns(int pos, int num)
Inserts a number of columns before the column with the specified index.
Definition: qtexttable.cpp:797
FragmentMap::ConstIterator FragmentIterator
Q_DECL_CONSTEXPR const T & qMax(const T &a, const T &b)
Definition: qglobal.h:1217
#define Q_Q(Class)
Definition: qglobal.h:2483
void setColumnWidthConstraints(const QVector< QTextLength > &constraints)
Sets the column width constraints for the table.
Definition: qtextformat.h:853
int rowSpan() const
Returns the number of rows this cell spans.
Definition: qtexttable.cpp:218
void removeColumns(int pos, int num)
Removes a number of columns starting with the column at the specified index.
Definition: qtexttable.cpp:995
void setTableCellColumnSpan(int tableCellColumnSpan)
If this character format is applied to characters in a table cell, the cell will span tableCellColumn...
Definition: qtextformat.h:537
void selectedTableCells(int *firstRow, int *numRows, int *firstColumn, int *numColumns) const
If the selection spans over table cells, firstRow is populated with the number of the first row in th...
QTextTableCell cellAt(int row, int col) const
Returns the table cell at the given row and column in the table.
Definition: qtexttable.cpp:630
void append(const T &t)
Inserts value at the end of the list.
Definition: qlist.h:507
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
QBool contains(const T &t) const
Returns true if the list contains an occurrence of value; otherwise returns false.
Definition: qlist.h:880
QTextCharFormat format() const
Returns the cell&#39;s character format.
Definition: qtexttable.cpp:153
QTextTableFormat format() const
Returns the table&#39;s format.
Definition: qtexttable.h:133
virtual void fragmentAdded(const QChar &type, uint fragment)
int column() const
Returns the number of the column in the table that contains this cell.
Definition: qtexttable.cpp:201
int objectIndex() const
Returns the object index of this object.
The QTextCursor class offers an API to access and modify QTextDocuments.
Definition: qtextcursor.h:70
void resize(int rows, int cols)
Resizes the table to contain the required number of rows and columns.
Definition: qtexttable.cpp:695
QTextFormatCollection * formatCollection()
QTextDocumentPrivate * docHandle() const
QTextObject * createObject(const QTextFormat &newFormat, int objectIndex=-1)
void setFormat(const QTextCharFormat &format)
Sets the cell&#39;s character format to format.
Definition: qtexttable.cpp:134
unsigned int uint
Definition: qglobal.h:996
void appendColumns(int count)
Appends count columns at the right side of the table.
Definition: qtexttable.cpp:915
uint position(uint node, uint field=0) const
const QTextTable * table
Definition: qtexttable.h:99
int row() const
Returns the number of the row in the table that contains this cell.
Definition: qtexttable.cpp:184
T value(int i) const
Returns the value at index position i in the list.
Definition: qlist.h:661
int position() const
Returns the absolute position of the cursor within the document.
int length() const
Returns the length of the block in characters.
The QTextTable class represents a table in a QTextDocument.
Definition: qtexttable.h:103
const BlockMap & blockMap() const
QList< int > cells
Definition: qtexttable_p.h:76
void setObjectType(int type)
Sets the text format&#39;s object type to type.
Definition: qtextformat.h:367
QTextBlock block() const
Returns the block that contains the cursor.
const T & at(int i) const
Returns the item at index position i in the vector.
Definition: qvector.h:350
int tableCellRowSpan() const
If this character format is applied to characters in a table cell, this function returns the number o...
Definition: qtextformat.h:518
Iterator find(int k, uint field=0)
The iterator class provides an iterator for reading the contents of a QTextFrame. ...
Definition: qtextobject.h:144
const QTextDocumentPrivate::FragmentMap & fragmentMap
Definition: qtexttable.cpp:413
void insertBlock()
Inserts a new empty block at the cursor position() with the current blockFormat() and charFormat()...
void setColumns(int columns)
Sets the number of columns required by the table format.
Definition: qtextformat.h:885
void insert(int i, const T &t)
Inserts value at index position i in the vector.
Definition: qvector.h:362
Q_OUTOFLINE_TEMPLATE RandomAccessIterator qLowerBound(RandomAccessIterator begin, RandomAccessIterator end, const T &value)
Definition: qalgorithms.h:227
void setFormat(const QTextTableFormat &format)
Sets the table&#39;s format.
void setFormat(const QTextFormat &format)
Sets the text object&#39;s format.
int firstPosition() const
Returns the first valid position in the document occupied by this cell.
Definition: qtexttable.cpp:273
static QTextTable * createTable(QTextDocumentPrivate *, int pos, int rows, int cols, const QTextTableFormat &tableFormat)
Definition: qtexttable.cpp:364
QTextFrame::iterator end() const
Returns a frame iterator pointing to the end of the table&#39;s cell.
Definition: qtexttable.cpp:319
uint findNode(int k, uint field=0) const
QFragmentFindHelper(int _pos, const QTextDocumentPrivate::FragmentMap &map)
Definition: qtexttable.cpp:410
int rows() const
Returns the number of rows in the table.
int tableCellColumnSpan() const
If this character format is applied to characters in a table cell, this function returns the number o...
Definition: qtextformat.h:521
The QTextTableCell class represents the properties of a cell in a QTextTable.
Definition: qtexttable.h:59
bool hasComplexSelection() const
Returns true if the cursor contains a selection that is not simply a range from selectionStart() to s...
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 lastPosition() const
Returns the last valid position in the document occupied by this cell.
Definition: qtexttable.cpp:287
void splitCell(int row, int col, int numRows, int numCols)
Splits the specified cell at row and column into an array of multiple cells with dimensions specified...
QTextCursor firstCursorPosition() const
Returns the first valid cursor position in this cell.
Definition: qtexttable.cpp:249
bool isValid() const
Returns true if this is a valid table cell; otherwise returns false.
Definition: qtexttable.h:77
quint16 index
The QTextDocument class holds formatted text that can be viewed and edited using a QTextEdit...
int tableCellFormatIndex() const
Returns the index of the tableCell&#39;s format in the document&#39;s internal list of formats.
Definition: qtexttable.cpp:173
friend class iterator
Definition: qtextobject.h:175
The QTextTableFormat class provides formatting information for tables in a QTextDocument.
Definition: qtextformat.h:842
friend class QTextTableCell
Definition: qtexttable.h:138
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 columnSpan() const
Returns the number of columns this cell spans.
Definition: qtexttable.cpp:228
virtual void fragmentRemoved(const QChar &type, uint fragment)
void setCharFormat(int pos, int length, const QTextCharFormat &newFormat, FormatChangeMode mode=SetFormat)
void setObjectIndex(int object)
Sets the format object&#39;s object index.
const FragmentMap & fragmentMap() const
int indexForFormat(const QTextFormat &f)
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...
int findCellIndex(int fragment) const
Definition: qtexttable.cpp:426
QVector< QTextLength > columnWidthConstraints() const
Returns a list of constraints used by this table format to control the appearance of columns in a tab...
Definition: qtextformat.h:856
The QList class is a template class that provides lists.
Definition: qdatastream.h:62
#define QTextEndOfFrame