QtBase  v6.3.1
qtexttable.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
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 The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qtexttable.h"
41 #include "qtextcursor.h"
42 #include "qtextformat.h"
43 #include <qdebug.h>
44 #include "qtextcursor_p.h"
45 #include "qtexttable_p.h"
46 #include "qvarlengtharray.h"
47 
48 #include <algorithm>
49 #include <stdlib.h>
50 
52 
120 {
121  QTextCharFormat fmt = format;
125  QTextDocumentPrivate::FragmentIterator frag(&p->fragmentMap(), fragment);
126 
127  QTextFormatCollection *c = p->formatCollection();
128  QTextCharFormat oldFormat = c->charFormat(frag->format);
129  fmt.setTableCellRowSpan(oldFormat.tableCellRowSpan());
131 
132  p->setCharFormat(frag.position(), 1, fmt, QTextDocumentPrivate::SetFormatAndPreserveObjectIndices);
133 }
134 
139 {
141  const QTextFormatCollection *c = p->formatCollection();
142 
143  QTextCharFormat fmt = c->charFormat(tableCellFormatIndex());
145  return fmt;
146 }
147 
156 {
158  return QTextDocumentPrivate::FragmentIterator(&p->fragmentMap(), fragment)->format;
159 }
160 
167 {
168  const QTextTablePrivate *tp = table->d_func();
169  if (tp->dirty)
170  tp->update();
171 
172  int idx = tp->findCellIndex(fragment);
173  if (idx == -1)
174  return idx;
175  return tp->cellIndices.at(idx) / tp->nCols;
176 }
177 
184 {
185  const QTextTablePrivate *tp = table->d_func();
186  if (tp->dirty)
187  tp->update();
188 
189  int idx = tp->findCellIndex(fragment);
190  if (idx == -1)
191  return idx;
192  return tp->cellIndices.at(idx) % tp->nCols;
193 }
194 
201 {
202  return format().tableCellRowSpan();
203 }
204 
211 {
212  return format().tableCellColumnSpan();
213 }
214 
229 {
230  return QTextCursorPrivate::fromPosition(table->d_func()->pieceTable, firstPosition());
231 }
232 
239 {
240  return QTextCursorPrivate::fromPosition(table->d_func()->pieceTable, lastPosition());
241 }
242 
243 
250 {
252  return p->fragmentMap().position(fragment) + 1;
253 }
254 
261 {
263  const QTextTablePrivate *td = table->d_func();
264  int index = table->d_func()->findCellIndex(fragment);
265  int f;
266  if (index != -1)
267  f = td->cells.value(index + 1, td->fragment_end);
268  else
269  f = td->fragment_end;
270  return p->fragmentMap().position(f);
271 }
272 
273 
280 {
282  int b = p->blockMap().findNode(firstPosition());
283  int e = p->blockMap().findNode(lastPosition()+1);
284  return QTextFrame::iterator(const_cast<QTextTable *>(table), b, b, e);
285 }
286 
293 {
295  int b = p->blockMap().findNode(firstPosition());
296  int e = p->blockMap().findNode(lastPosition()+1);
297  return QTextFrame::iterator(const_cast<QTextTable *>(table), e, b, e);
298 }
299 
300 
321 QTextTable *QTextTablePrivate::createTable(QTextDocumentPrivate *pieceTable, int pos, int rows, int cols, const QTextTableFormat &tableFormat)
322 {
323  QTextTableFormat fmt = tableFormat;
324  fmt.setColumns(cols);
325  QTextTable *table = qobject_cast<QTextTable *>(pieceTable->createObject(fmt));
326  Q_ASSERT(table);
327 
329 
330 // qDebug("---> createTable: rows=%d, cols=%d at %d", rows, cols, pos);
331  // add block after table
332  QTextCharFormat charFmt;
333  charFmt.setObjectIndex(table->objectIndex());
335 
336 
337  int charIdx = pieceTable->formatCollection()->indexForFormat(charFmt);
339 
340  QTextTablePrivate *d = table->d_func();
341  d->blockFragmentUpdates = true;
342 
343  d->fragment_start = pieceTable->insertBlock(QTextBeginningOfFrame, pos, cellIdx, charIdx);
344  d->cells.append(d->fragment_start);
345  ++pos;
346 
347  for (int i = 1; i < rows*cols; ++i) {
348  d->cells.append(pieceTable->insertBlock(QTextBeginningOfFrame, pos, cellIdx, charIdx));
349 // qDebug(" addCell at %d", pos);
350  ++pos;
351  }
352 
353  d->fragment_end = pieceTable->insertBlock(QTextEndOfFrame, pos, cellIdx, charIdx);
354 // qDebug(" addEOR at %d", pos);
355  ++pos;
356 
357  d->blockFragmentUpdates = false;
358  d->dirty = true;
359 
361 
362  return table;
363 }
364 
366 {
368  : pos(_pos), fragmentMap(map) {}
371 };
372 
373 static inline bool operator<(int fragment, const QFragmentFindHelper &helper)
374 {
375  return helper.fragmentMap.position(fragment) < helper.pos;
376 }
377 
378 static inline bool operator<(const QFragmentFindHelper &helper, int fragment)
379 {
380  return helper.pos < helper.fragmentMap.position(fragment);
381 }
382 
383 int QTextTablePrivate::findCellIndex(int fragment) const
384 {
387  const auto it = std::lower_bound(cells.constBegin(), cells.constEnd(), helper);
388  if ((it == cells.constEnd()) || (helper < *it))
389  return -1;
390  return it - cells.constBegin();
391 }
392 
394 {
395  dirty = true;
397  return;
398  if (type == QTextBeginningOfFrame) {
399  Q_ASSERT(cells.indexOf(int(fragment)) == -1);
400  const uint pos = pieceTable->fragmentMap().position(fragment);
402  auto it = std::lower_bound(cells.begin(), cells.end(), helper);
403  cells.insert(it, fragment);
404  if (!fragment_start || pos < pieceTable->fragmentMap().position(fragment_start))
405  fragment_start = fragment;
406  return;
407  }
409 }
410 
412 {
413  dirty = true;
415  return;
416  if (type == QTextBeginningOfFrame) {
417  Q_ASSERT(cells.indexOf(int(fragment)) != -1);
418  cells.removeAll(int(fragment));
419  if (fragment_start == fragment && cells.size()) {
420  fragment_start = cells.at(0);
421  }
422  if (fragment_start != fragment)
423  return;
424  }
426 }
427 
436 {
437  Q_Q(const QTextTable);
438  nCols = q->format().columns();
439  nRows = (cells.size() + nCols-1)/nCols;
440 // qDebug(">>>> QTextTablePrivate::update, nRows=%d, nCols=%d", nRows, nCols);
441 
442  grid.assign(nRows * nCols, 0);
443 
445  QTextFormatCollection *c = p->formatCollection();
446 
448 
449  int cell = 0;
450  for (int i = 0; i < cells.size(); ++i) {
451  int fragment = cells.at(i);
452  QTextCharFormat fmt = c->charFormat(QTextDocumentPrivate::FragmentIterator(&p->fragmentMap(), fragment)->format);
453  int rowspan = fmt.tableCellRowSpan();
454  int colspan = fmt.tableCellColumnSpan();
455 
456  // skip taken cells
457  while (cell < nRows*nCols && grid[cell])
458  ++cell;
459 
460  int r = cell/nCols;
461  int c = cell%nCols;
462  cellIndices[i] = cell;
463 
464  if (r + rowspan > nRows) {
465  grid.resize((r + rowspan) * nCols, 0);
466  nRows = r + rowspan;
467  }
468 
469  Q_ASSERT(c + colspan <= nCols);
470  for (int ii = 0; ii < rowspan; ++ii) {
471  for (int jj = 0; jj < colspan; ++jj) {
472  Q_ASSERT(grid[(r+ii)*nCols + c+jj] == 0);
473  grid[(r+ii)*nCols + c+jj] = fragment;
474 // qDebug(" setting cell %d span=%d/%d at %d/%d", fragment, rowspan, colspan, r+ii, c+jj);
475  }
476  }
477  }
478 // qDebug("<<<< end: nRows=%d, nCols=%d", nRows, nCols);
479 
480  dirty = false;
481 }
482 
483 
484 
485 
486 
560  : QTextFrame(*new QTextTablePrivate(doc), doc)
561 {
562 }
563 
569 {
570 }
571 
572 
581 {
582  Q_D(const QTextTable);
583  if (d->dirty)
584  d->update();
585 
586  if (row < 0 || row >= d->nRows || col < 0 || col >= d->nCols)
587  return QTextTableCell();
588 
589  return QTextTableCell(this, d->grid[row*d->nCols + col]);
590 }
591 
599 {
600  Q_D(const QTextTable);
601  if (d->dirty)
602  d->update();
603 
604  uint pos = (uint)position;
605  const QTextDocumentPrivate::FragmentMap &map = d->pieceTable->fragmentMap();
606  if (position < 0 || map.position(d->fragment_start) >= pos || map.position(d->fragment_end) < pos)
607  return QTextTableCell();
608 
610  auto it = std::lower_bound(d->cells.begin(), d->cells.end(), helper);
611  if (it != d->cells.begin())
612  --it;
613 
614  return QTextTableCell(this, *it);
615 }
616 
625 {
626  return cellAt(c.position());
627 }
628 
636 void QTextTable::resize(int rows, int cols)
637 {
638  Q_D(QTextTable);
639  if (d->dirty)
640  d->update();
641 
642  int nRows = this->rows();
643  int nCols = this->columns();
644 
645  if (rows == nRows && cols == nCols)
646  return;
647 
648  d->pieceTable->beginEditBlock();
649 
650  if (nCols < cols)
651  insertColumns(nCols, cols - nCols);
652  else if (nCols > cols)
653  removeColumns(cols, nCols - cols);
654 
655  if (nRows < rows)
656  insertRows(nRows, rows-nRows);
657  else if (nRows > rows)
658  removeRows(rows, nRows-rows);
659 
660  d->pieceTable->endEditBlock();
661 }
662 
671 {
672  Q_D(QTextTable);
673  if (num <= 0)
674  return;
675 
676  if (d->dirty)
677  d->update();
678 
679  if (pos > d->nRows || pos < 0)
680  pos = d->nRows;
681 
682 // qDebug() << "-------- insertRows" << pos << num;
683  QTextDocumentPrivate *p = d->pieceTable;
684  QTextFormatCollection *c = p->formatCollection();
685  p->beginEditBlock();
686 
687  int extended = 0;
688  int insert_before = 0;
689  if (pos > 0 && pos < d->nRows) {
690  int lastCell = -1;
691  for (int i = 0; i < d->nCols; ++i) {
692  int cell = d->grid[pos*d->nCols + i];
693  if (cell == d->grid[(pos-1)*d->nCols+i]) {
694  // cell spans the insertion place, extend it
695  if (cell != lastCell) {
696  QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
697  QTextCharFormat fmt = c->charFormat(it->format);
699  p->setCharFormat(it.position(), 1, fmt);
700  }
701  extended++;
702  } else if (!insert_before) {
703  insert_before = cell;
704  }
705  lastCell = cell;
706  }
707  } else {
708  insert_before = (pos == 0 ? d->grid[0] : d->fragment_end);
709  }
710  if (extended < d->nCols) {
711  Q_ASSERT(insert_before);
712  QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), insert_before);
713  QTextCharFormat fmt = c->charFormat(it->format);
714  fmt.setTableCellRowSpan(1);
715  fmt.setTableCellColumnSpan(1);
716  Q_ASSERT(fmt.objectIndex() == objectIndex());
717  int pos = it.position();
718  int cfmt = p->formatCollection()->indexForFormat(fmt);
719  int bfmt = p->formatCollection()->indexForFormat(QTextBlockFormat());
720 // qDebug("inserting %d cells, nCols=%d extended=%d", num*(d->nCols-extended), d->nCols, extended);
721  for (int i = 0; i < num*(d->nCols-extended); ++i)
722  p->insertBlock(QTextBeginningOfFrame, pos, bfmt, cfmt, QTextUndoCommand::MoveCursor);
723  }
724 
725 // qDebug() << "-------- end insertRows" << pos << num;
726  p->endEditBlock();
727 }
728 
737 {
738  Q_D(QTextTable);
739  if (num <= 0)
740  return;
741 
742  if (d->dirty)
743  d->update();
744 
745  if (pos > d->nCols || pos < 0)
746  pos = d->nCols;
747 
748 // qDebug() << "-------- insertCols" << pos << num;
749  QTextDocumentPrivate *p = d->pieceTable;
750  QTextFormatCollection *c = p->formatCollection();
751  p->beginEditBlock();
752 
753  QList<int> extendedSpans;
754  for (int i = 0; i < d->nRows; ++i) {
755  int cell;
756  if (i == d->nRows - 1 && pos == d->nCols) {
757  cell = d->fragment_end;
758  } else {
759  int logicalGridIndexBeforePosition = pos > 0
760  ? d->findCellIndex(d->grid[i*d->nCols + pos - 1])
761  : -1;
762 
763  // Search for the logical insertion point by skipping past cells which are not the first
764  // cell in a rowspan. This means any cell for which the logical grid index is
765  // less than the logical cell index of the cell before the insertion.
766  int logicalGridIndex;
767  int gridArrayOffset = i*d->nCols + pos;
768  do {
769  cell = d->grid[gridArrayOffset];
770  logicalGridIndex = d->findCellIndex(cell);
771  gridArrayOffset++;
772  } while (logicalGridIndex < logicalGridIndexBeforePosition
773  && gridArrayOffset < d->nRows*d->nCols);
774 
775  if (logicalGridIndex < logicalGridIndexBeforePosition
776  && gridArrayOffset == d->nRows*d->nCols)
777  cell = d->fragment_end;
778  }
779 
780  if (pos > 0 && pos < d->nCols && cell == d->grid[i*d->nCols + pos - 1]) {
781  // cell spans the insertion place, extend it
782  if (!extendedSpans.contains(cell)) {
783  QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
784  QTextCharFormat fmt = c->charFormat(it->format);
786  p->setCharFormat(it.position(), 1, fmt);
787  d->dirty = true;
788  extendedSpans << cell;
789  }
790  } else {
791  /* If the next cell is spanned from the row above, we need to find the right position
792  to insert to */
793  if (i > 0 && pos < d->nCols && cell == d->grid[(i-1) * d->nCols + pos]) {
794  int gridIndex = i*d->nCols + pos;
795  const int gridEnd = d->nRows * d->nCols - 1;
796  while (gridIndex < gridEnd && cell == d->grid[gridIndex]) {
797  ++gridIndex;
798  }
799  if (gridIndex == gridEnd)
800  cell = d->fragment_end;
801  else
802  cell = d->grid[gridIndex];
803  }
804  QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
805  QTextCharFormat fmt = c->charFormat(it->format);
806  fmt.setTableCellRowSpan(1);
807  fmt.setTableCellColumnSpan(1);
808  Q_ASSERT(fmt.objectIndex() == objectIndex());
809  int position = it.position();
810  int cfmt = p->formatCollection()->indexForFormat(fmt);
811  int bfmt = p->formatCollection()->indexForFormat(QTextBlockFormat());
812  for (int i = 0; i < num; ++i)
813  p->insertBlock(QTextBeginningOfFrame, position, bfmt, cfmt, QTextUndoCommand::MoveCursor);
814  }
815  }
816 
817  QTextTableFormat tfmt = format();
818  tfmt.setColumns(tfmt.columns()+num);
819  QList<QTextLength> columnWidths = tfmt.columnWidthConstraints();
820  if (! columnWidths.isEmpty()) {
821  for (int i = num; i > 0; --i)
822  columnWidths.insert(pos, columnWidths.at(qMax(0, pos - 1)));
823  }
824  tfmt.setColumnWidthConstraints (columnWidths);
826 
827 // qDebug() << "-------- end insertCols" << pos << num;
828  p->endEditBlock();
829 }
830 
838 {
839  insertRows(rows(), count);
840 }
841 
849 {
851 }
852 
861 {
862  Q_D(QTextTable);
863 // qDebug() << "-------- removeRows" << pos << num;
864 
865  if (num <= 0 || pos < 0)
866  return;
867  if (d->dirty)
868  d->update();
869  if (pos >= d->nRows)
870  return;
871  if (pos+num > d->nRows)
872  num = d->nRows - pos;
873 
874  QTextDocumentPrivate *p = d->pieceTable;
875  QTextFormatCollection *collection = p->formatCollection();
876  p->beginEditBlock();
877 
878  // delete whole table?
879  if (pos == 0 && num == d->nRows) {
880  const int pos = p->fragmentMap().position(d->fragment_start);
881  p->remove(pos, p->fragmentMap().position(d->fragment_end) - pos + 1);
882  p->endEditBlock();
883  return;
884  }
885 
886  p->aboutToRemoveCell(cellAt(pos, 0).firstPosition(), cellAt(pos + num - 1, d->nCols - 1).lastPosition());
887 
888  QList<int> touchedCells;
889  for (int r = pos; r < pos + num; ++r) {
890  for (int c = 0; c < d->nCols; ++c) {
891  int cell = d->grid[r*d->nCols + c];
892  if (touchedCells.contains(cell))
893  continue;
894  touchedCells << cell;
895  QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
896  QTextCharFormat fmt = collection->charFormat(it->format);
897  int span = fmt.tableCellRowSpan();
898  if (span > 1) {
899  fmt.setTableCellRowSpan(span - 1);
900  p->setCharFormat(it.position(), 1, fmt);
901  } else {
902  // remove cell
903  int index = d->cells.indexOf(cell) + 1;
904  int f_end = index < d->cells.size() ? d->cells.at(index) : d->fragment_end;
905  p->remove(it.position(), p->fragmentMap().position(f_end) - it.position());
906  }
907  }
908  }
909 
910  p->endEditBlock();
911 // qDebug() << "-------- end removeRows" << pos << num;
912 }
913 
923 {
924  Q_D(QTextTable);
925 // qDebug() << "-------- removeCols" << pos << num;
926 
927  if (num <= 0 || pos < 0)
928  return;
929  if (d->dirty)
930  d->update();
931  if (pos >= d->nCols)
932  return;
933  if (pos + num > d->nCols)
934  pos = d->nCols - num;
935 
936  QTextDocumentPrivate *p = d->pieceTable;
937  QTextFormatCollection *collection = p->formatCollection();
938  p->beginEditBlock();
939 
940  // delete whole table?
941  if (pos == 0 && num == d->nCols) {
942  const int pos = p->fragmentMap().position(d->fragment_start);
943  p->remove(pos, p->fragmentMap().position(d->fragment_end) - pos + 1);
944  p->endEditBlock();
945  return;
946  }
947 
948  p->aboutToRemoveCell(cellAt(0, pos).firstPosition(), cellAt(d->nRows - 1, pos + num - 1).lastPosition());
949 
950  QList<int> touchedCells;
951  for (int r = 0; r < d->nRows; ++r) {
952  for (int c = pos; c < pos + num; ++c) {
953  int cell = d->grid[r*d->nCols + c];
954  QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
955  QTextCharFormat fmt = collection->charFormat(it->format);
956  int span = fmt.tableCellColumnSpan();
957  if (touchedCells.contains(cell) && span <= 1)
958  continue;
959  touchedCells << cell;
960 
961  if (span > 1) {
962  fmt.setTableCellColumnSpan(span - 1);
963  p->setCharFormat(it.position(), 1, fmt);
964  } else {
965  // remove cell
966  int index = d->cells.indexOf(cell) + 1;
967  int f_end = index < d->cells.size() ? d->cells.at(index) : d->fragment_end;
968  p->remove(it.position(), p->fragmentMap().position(f_end) - it.position());
969  }
970  }
971  }
972 
973  QTextTableFormat tfmt = format();
974  tfmt.setColumns(tfmt.columns()-num);
975  QList<QTextLength> columnWidths = tfmt.columnWidthConstraints();
976  if (columnWidths.count() > pos) {
977  columnWidths.remove(pos, num);
978  tfmt.setColumnWidthConstraints (columnWidths);
979  }
981 
982  p->endEditBlock();
983 // qDebug() << "-------- end removeCols" << pos << num;
984 }
985 
996 void QTextTable::mergeCells(int row, int column, int numRows, int numCols)
997 {
998  Q_D(QTextTable);
999 
1000  if (d->dirty)
1001  d->update();
1002 
1003  QTextDocumentPrivate *p = d->pieceTable;
1004  QTextFormatCollection *fc = p->formatCollection();
1005 
1006  const QTextTableCell cell = cellAt(row, column);
1007  if (!cell.isValid() || row != cell.row() || column != cell.column())
1008  return;
1009 
1010  QTextCharFormat fmt = cell.format();
1011  const int rowSpan = fmt.tableCellRowSpan();
1012  const int colSpan = fmt.tableCellColumnSpan();
1013 
1014  numRows = qMin(numRows, rows() - cell.row());
1015  numCols = qMin(numCols, columns() - cell.column());
1016 
1017  // nothing to merge?
1018  if (numRows < rowSpan || numCols < colSpan)
1019  return;
1020 
1021  // check the edges of the merge rect to make sure no cell spans the edge
1022  for (int r = row; r < row + numRows; ++r) {
1023  if (cellAt(r, column) == cellAt(r, column - 1))
1024  return;
1025  if (cellAt(r, column + numCols) == cellAt(r, column + numCols - 1))
1026  return;
1027  }
1028 
1029  for (int c = column; c < column + numCols; ++c) {
1030  if (cellAt(row, c) == cellAt(row - 1, c))
1031  return;
1032  if (cellAt(row + numRows, c) == cellAt(row + numRows - 1, c))
1033  return;
1034  }
1035 
1036  p->beginEditBlock();
1037 
1038  const int origCellPosition = cell.firstPosition() - 1;
1039 
1040  const int cellFragment = d->grid[row * d->nCols + column];
1041 
1042  // find the position at which to insert the contents of the merged cells
1043  QFragmentFindHelper helper(origCellPosition, p->fragmentMap());
1044  const auto begin = d->cells.cbegin();
1045  const auto it = std::lower_bound(begin, d->cells.cend(), helper);
1046  Q_ASSERT(it != d->cells.cend());
1047  Q_ASSERT(!(helper < *it));
1048  Q_ASSERT(*it == cellFragment);
1049  const int insertCellIndex = it - begin;
1050  int insertFragment = d->cells.value(insertCellIndex + 1, d->fragment_end);
1051  uint insertPos = p->fragmentMap().position(insertFragment);
1052 
1053  d->blockFragmentUpdates = true;
1054 
1055  bool rowHasText = cell.firstCursorPosition().block().length();
1056  bool needsParagraph = rowHasText && colSpan == numCols;
1057 
1058  // find all cells that will be erased by the merge
1059  for (int r = row; r < row + numRows; ++r) {
1060  int firstColumn = r < row + rowSpan ? column + colSpan : column;
1061 
1062  // don't recompute the cell index for the first row
1063  int firstCellIndex = r == row ? insertCellIndex + 1 : -1;
1064  int cellIndex = firstCellIndex;
1065 
1066  for (int c = firstColumn; c < column + numCols; ++c) {
1067  const int fragment = d->grid[r * d->nCols + c];
1068 
1069  // already handled?
1070  if (fragment == cellFragment)
1071  continue;
1072 
1073  QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), fragment);
1074  uint pos = it.position();
1075 
1076  if (firstCellIndex == -1) {
1077  QFragmentFindHelper helper(pos, p->fragmentMap());
1078  const auto begin = d->cells.cbegin();
1079  const auto it = std::lower_bound(begin, d->cells.cend(), helper);
1080  Q_ASSERT(it != d->cells.cend());
1081  Q_ASSERT(!(helper < *it));
1082  Q_ASSERT(*it == fragment);
1083  firstCellIndex = cellIndex = it - begin;
1084  }
1085 
1086  ++cellIndex;
1087 
1088  QTextCharFormat fmt = fc->charFormat(it->format);
1089 
1090  const int cellRowSpan = fmt.tableCellRowSpan();
1091  const int cellColSpan = fmt.tableCellColumnSpan();
1092 
1093  // update the grid for this cell
1094  for (int i = r; i < r + cellRowSpan; ++i)
1095  for (int j = c; j < c + cellColSpan; ++j)
1096  d->grid[i * d->nCols + j] = cellFragment;
1097 
1098  // erase the cell marker
1099  p->remove(pos, 1);
1100 
1101  const int nextFragment = d->cells.value(cellIndex, d->fragment_end);
1102  const uint nextPos = p->fragmentMap().position(nextFragment);
1103 
1104  Q_ASSERT(nextPos >= pos);
1105 
1106  // merge the contents of the cell (if not empty)
1107  if (nextPos > pos) {
1108  if (needsParagraph) {
1109  needsParagraph = false;
1111  p->move(pos + 1, insertPos, nextPos - pos);
1112  } else if (rowHasText) {
1114  p->move(pos + 1, insertPos, nextPos - pos);
1115  } else {
1116  p->move(pos, insertPos, nextPos - pos);
1117  }
1118 
1119  insertPos += nextPos - pos;
1120  rowHasText = true;
1121  }
1122  }
1123 
1124  if (rowHasText) {
1125  needsParagraph = true;
1126  rowHasText = false;
1127  }
1128 
1129  // erase cells from last row
1130  if (firstCellIndex >= 0) {
1131  d->cellIndices.remove(firstCellIndex, cellIndex - firstCellIndex);
1132  d->cells.erase(d->cells.begin() + firstCellIndex, d->cells.begin() + cellIndex);
1133  }
1134  }
1135 
1136  d->fragment_start = d->cells.constFirst();
1137 
1138  fmt.setTableCellRowSpan(numRows);
1139  fmt.setTableCellColumnSpan(numCols);
1140  p->setCharFormat(origCellPosition, 1, fmt);
1141 
1142  d->blockFragmentUpdates = false;
1143  d->dirty = false;
1144 
1145  p->endEditBlock();
1146 }
1147 
1157 {
1158  if (!cursor.hasComplexSelection())
1159  return;
1160 
1161  int firstRow, numRows, firstColumn, numColumns;
1162  cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
1163  mergeCells(firstRow, firstColumn, numRows, numColumns);
1164 }
1165 
1177 void QTextTable::splitCell(int row, int column, int numRows, int numCols)
1178 {
1179  Q_D(QTextTable);
1180 
1181  if (d->dirty)
1182  d->update();
1183 
1184  QTextDocumentPrivate *p = d->pieceTable;
1185  QTextFormatCollection *c = p->formatCollection();
1186 
1187  const QTextTableCell cell = cellAt(row, column);
1188  if (!cell.isValid())
1189  return;
1190  row = cell.row();
1191  column = cell.column();
1192 
1193  QTextCharFormat fmt = cell.format();
1194  const int rowSpan = fmt.tableCellRowSpan();
1195  const int colSpan = fmt.tableCellColumnSpan();
1196 
1197  // nothing to split?
1198  if (numRows > rowSpan || numCols > colSpan)
1199  return;
1200 
1201  p->beginEditBlock();
1202 
1203  const int origCellPosition = cell.firstPosition() - 1;
1204 
1205  QVarLengthArray<int> rowPositions(rowSpan);
1206 
1207  rowPositions[0] = cell.lastPosition();
1208 
1209  for (int r = row + 1; r < row + rowSpan; ++r) {
1210  // find the cell before which to insert the new cell markers
1211  int gridIndex = r * d->nCols + column;
1212  const auto begin = d->cellIndices.cbegin();
1213  const auto it = std::upper_bound(begin, d->cellIndices.cend(), gridIndex);
1214  int fragment = d->cells.value(it - begin, d->fragment_end);
1215  rowPositions[r - row] = p->fragmentMap().position(fragment);
1216  }
1217 
1218  fmt.setTableCellColumnSpan(1);
1219  fmt.setTableCellRowSpan(1);
1220  const int fmtIndex = c->indexForFormat(fmt);
1221  const int blockIndex = p->blockMap().find(cell.lastPosition())->format;
1222 
1223  int insertAdjustement = 0;
1224  for (int i = 0; i < numRows; ++i) {
1225  for (int c = 0; c < colSpan - numCols; ++c)
1226  p->insertBlock(QTextBeginningOfFrame, rowPositions[i] + insertAdjustement + c, blockIndex, fmtIndex);
1227  insertAdjustement += colSpan - numCols;
1228  }
1229 
1230  for (int i = numRows; i < rowSpan; ++i) {
1231  for (int c = 0; c < colSpan; ++c)
1232  p->insertBlock(QTextBeginningOfFrame, rowPositions[i] + insertAdjustement + c, blockIndex, fmtIndex);
1233  insertAdjustement += colSpan;
1234  }
1235 
1236  fmt.setTableCellRowSpan(numRows);
1237  fmt.setTableCellColumnSpan(numCols);
1238  p->setCharFormat(origCellPosition, 1, fmt);
1239 
1240  p->endEditBlock();
1241 }
1242 
1248 int QTextTable::rows() const
1249 {
1250  Q_D(const QTextTable);
1251  if (d->dirty)
1252  d->update();
1253 
1254  return d->nRows;
1255 }
1256 
1263 {
1264  Q_D(const QTextTable);
1265  if (d->dirty)
1266  d->update();
1267 
1268  return d->nCols;
1269 }
1270 
1280 {
1281  Q_D(const QTextTable);
1282  QTextTableCell cell = cellAt(c);
1283  if (!cell.isValid())
1284  return QTextCursor();
1285 
1286  int row = cell.row();
1287  QTextDocumentPrivate *p = d->pieceTable;
1288  QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), d->grid[row*d->nCols]);
1289  return QTextCursorPrivate::fromPosition(p, it.position());
1290 }
1291 
1301 {
1302  Q_D(const QTextTable);
1303  QTextTableCell cell = cellAt(c);
1304  if (!cell.isValid())
1305  return QTextCursor();
1306 
1307  int row = cell.row() + 1;
1308  int fragment = row < d->nRows ? d->grid[row*d->nCols] : d->fragment_end;
1309  QTextDocumentPrivate *p = d->pieceTable;
1310  QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), fragment);
1311  return QTextCursorPrivate::fromPosition(p, it.position() - 1);
1312 }
1313 
1322 {
1323  QTextTableFormat fmt = format;
1324  // don't try to change the number of table columns from here
1325  fmt.setColumns(columns());
1327 }
1328 
1338 
1339 #include "moc_qtexttable.cpp"
small capitals from c petite p scientific i
[1]
Definition: afcover.h:80
FT_UInt idx
Definition: cffcmap.c:135
The QChar class provides a 16-bit Unicode character.
Definition: qchar.h:84
bool operator<(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
uint position(uint node, uint field=0) const
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal.
Definition: qstring.h:84
qsizetype size() const noexcept
Definition: qlist.h:414
bool isEmpty() const noexcept
Definition: qlist.h:418
iterator insert(qsizetype i, parameter_type t)
Definition: qlist.h:499
iterator end()
Definition: qlist.h:624
const_reference at(qsizetype i) const noexcept
Definition: qlist.h:457
T value(qsizetype i) const
Definition: qlist.h:676
const_iterator constBegin() const noexcept
Definition: qlist.h:630
void remove(qsizetype i, qsizetype n=1)
Definition: qlist.h:798
qsizetype removeAll(const AT &t)
Definition: qlist.h:590
qsizetype count() const noexcept
Definition: qlist.h:415
iterator begin()
Definition: qlist.h:623
void resize(qsizetype size)
Definition: qlist.h:420
const_iterator constEnd() const noexcept
Definition: qlist.h:631
The QTextBlockFormat class provides formatting information for blocks of text in a QTextDocument....
Definition: qtextformat.h:640
int length() const
The QTextCharFormat class provides formatting information for characters in a QTextDocument....
Definition: qtextformat.h:416
int tableCellColumnSpan() const
Definition: qtextformat.h:611
void setTableCellColumnSpan(int tableCellColumnSpan)
Definition: qtextformat.h:631
int tableCellRowSpan() const
Definition: qtextformat.h:608
void setTableCellRowSpan(int tableCellRowSpan)
Definition: qtextformat.h:623
The QTextCursor class offers an API to access and modify QTextDocuments.
Definition: qtextcursor.h:67
QTextBlock block() const
void insertText(const QString &text)
void insertBlock()
static QTextCursor fromPosition(QTextDocumentPrivate *d, int pos)
The QTextDocument class holds formatted text.
Definition: qtextdocument.h:93
int insertBlock(int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation=QTextUndoCommand::MoveCursor)
QTextFormatCollection * formatCollection()
QTextObject * createObject(const QTextFormat &newFormat, int objectIndex=-1)
FragmentMap::ConstIterator FragmentIterator
static const QTextDocumentPrivate * get(const QTextDocument *document)
const FragmentMap & fragmentMap() const
int indexForFormat(const QTextFormat &f)
QTextCharFormat charFormat(int index) const
Definition: qtextformat_p.h:84
int objectIndex() const
void setObjectType(int type)
Definition: qtextformat.h:410
void setObjectIndex(int object)
void clearProperty(int propertyId)
The iterator class provides an iterator for reading the contents of a QTextFrame.
Definition: qtextobject.h:138
The QTextFrame class represents a frame in a QTextDocument. \inmodule QtGui.
Definition: qtextobject.h:117
friend class iterator
Definition: qtextobject.h:168
iterator begin() const
int firstPosition() const
virtual void fragmentRemoved(QChar type, uint fragment)
virtual void fragmentAdded(QChar type, uint fragment)
int objectIndex() const
void setFormat(const QTextFormat &format)
QTextDocumentPrivate * pieceTable
Definition: qtextobject_p.h:71
The QTextTableCell class represents the properties of a cell in a QTextTable. \inmodule QtGui.
Definition: qtexttable.h:55
QTextCharFormat format() const
Definition: qtexttable.cpp:138
int columnSpan() const
Definition: qtexttable.cpp:210
int firstPosition() const
Definition: qtexttable.cpp:249
QTextFrame::iterator end() const
Definition: qtexttable.cpp:292
int row() const
Definition: qtexttable.cpp:166
QTextCursor firstCursorPosition() const
Definition: qtexttable.cpp:228
int rowSpan() const
Definition: qtexttable.cpp:200
void setFormat(const QTextCharFormat &format)
Definition: qtexttable.cpp:119
QTextCursor lastCursorPosition() const
Definition: qtexttable.cpp:238
int lastPosition() const
Definition: qtexttable.cpp:260
bool isValid() const
Definition: qtexttable.h:72
int column() const
Definition: qtexttable.cpp:183
int tableCellFormatIndex() const
Definition: qtexttable.cpp:155
QTextFrame::iterator begin() const
Definition: qtexttable.cpp:279
The QTextTableFormat class provides formatting information for tables in a QTextDocument....
Definition: qtextformat.h:978
QList< QTextLength > columnWidthConstraints() const
Definition: qtextformat.h:991
void setColumnWidthConstraints(const QList< QTextLength > &constraints)
Definition: qtextformat.h:988
int columns() const
Definition: qtextformat.h:984
void setColumns(int columns)
Definition: qtextformat.h:1027
The QTextTable class represents a table in a QTextDocument. \inmodule QtGui.
Definition: qtexttable.h:99
int columns() const
void removeRows(int pos, int num)
Definition: qtexttable.cpp:860
QTextTableFormat format() const
Definition: qtexttable.h:128
QTextCursor rowStart(const QTextCursor &c) const
friend class QTextTableCell
Definition: qtexttable.h:133
void appendRows(int count)
Definition: qtexttable.cpp:837
void removeColumns(int pos, int num)
Definition: qtexttable.cpp:922
void insertRows(int pos, int num)
Definition: qtexttable.cpp:670
void splitCell(int row, int col, int numRows, int numCols)
QTextTable(QTextDocument *doc)
Definition: qtexttable.cpp:559
void appendColumns(int count)
Definition: qtexttable.cpp:848
QTextCursor rowEnd(const QTextCursor &c) const
void setFormat(const QTextTableFormat &format)
int rows() const
void insertColumns(int pos, int num)
Definition: qtexttable.cpp:736
QTextTableCell cellAt(int row, int col) const
Definition: qtexttable.cpp:580
void mergeCells(int row, int col, int numRows, int numCols)
Definition: qtexttable.cpp:996
void resize(int rows, int cols)
Definition: qtexttable.cpp:636
QList< int > cells
Definition: qtexttable_p.h:76
static QTextTable * createTable(QTextDocumentPrivate *, int pos, int rows, int cols, const QTextTableFormat &tableFormat)
Definition: qtexttable.cpp:321
void fragmentRemoved(QChar type, uint fragment) override
Definition: qtexttable.cpp:411
int findCellIndex(int fragment) const
Definition: qtexttable.cpp:383
void fragmentAdded(QChar type, uint fragment) override
Definition: qtexttable.cpp:393
void update() const
Definition: qtexttable.cpp:435
std::vector< int > grid
Definition: qtexttable_p.h:80
QList< int > cellIndices
Definition: qtexttable_p.h:79
QMap< QString, QString > map
[6]
QCursor cursor
double e
QHighDpiScaling::Point position(T, QHighDpiScaling::Point::Kind)
Definition: helper.py:1
unsigned int uint
Definition: qglobal.h:334
GLenum type
Definition: qopengl.h:270
GLboolean GLboolean GLboolean b
GLboolean r
[2]
GLuint index
[2]
GLenum GLenum GLsizei count
GLfloat GLfloat f
GLint GLsizei GLsizei GLenum format
GLenum GLenum GLsizei void GLsizei void * column
Definition: qopenglext.h:2747
const GLubyte * c
Definition: qopenglext.h:12701
GLdouble GLdouble GLdouble GLdouble q
Definition: qopenglext.h:259
GLenum GLenum GLsizei void * row
Definition: qopenglext.h:2747
GLenum GLenum GLsizei void GLsizei void void * span
Definition: qopenglext.h:2747
GLfloat GLfloat p
[1]
Definition: qopenglext.h:12698
GLuint num
Definition: qopenglext.h:5654
GLenum GLenum GLsizei void * table
Definition: qopenglext.h:2745
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
#define QTextBeginningOfFrame
#define QTextEndOfFrame
QStringList::Iterator it
QFragmentFindHelper(int _pos, const QTextDocumentPrivate::FragmentMap &map)
Definition: qtexttable.cpp:367
const QTextDocumentPrivate::FragmentMap & fragmentMap
Definition: qtexttable.cpp:370
qsizetype indexOf(const AT &t, qsizetype from=0) const noexcept
Definition: qlist.h:966
bool contains(const AT &t) const noexcept
Definition: qlist.h:78