Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qtexttable.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qtexttable.h"
5#include "qtextcursor.h"
6#include "qtextformat.h"
7#include <qdebug.h>
8#include "qtextcursor_p.h"
9#include "qtexttable_p.h"
10#include "qvarlengtharray.h"
11
12#include <algorithm>
13#include <stdlib.h>
14
16
17using namespace Qt::StringLiterals;
18
86{
89 fmt.setObjectType(QTextFormat::TableCellObject);
91 QTextDocumentPrivate::FragmentIterator frag(&p->fragmentMap(), fragment);
92
93 QTextFormatCollection *c = p->formatCollection();
94 QTextCharFormat oldFormat = c->charFormat(frag->format);
95 fmt.setTableCellRowSpan(oldFormat.tableCellRowSpan());
96 fmt.setTableCellColumnSpan(oldFormat.tableCellColumnSpan());
97
98 p->setCharFormat(frag.position(), 1, fmt, QTextDocumentPrivate::SetFormatAndPreserveObjectIndices);
99}
100
113
122{
124 return QTextDocumentPrivate::FragmentIterator(&p->fragmentMap(), fragment)->format;
125}
126
133{
134 const QTextTablePrivate *tp = table->d_func();
135 if (tp->dirty)
136 tp->update();
137
138 int idx = tp->findCellIndex(fragment);
139 if (idx == -1)
140 return idx;
141 return tp->cellIndices.at(idx) / tp->nCols;
142}
143
150{
151 const QTextTablePrivate *tp = table->d_func();
152 if (tp->dirty)
153 tp->update();
154
155 int idx = tp->findCellIndex(fragment);
156 if (idx == -1)
157 return idx;
158 return tp->cellIndices.at(idx) % tp->nCols;
159}
160
167{
168 return format().tableCellRowSpan();
169}
170
177{
178 return format().tableCellColumnSpan();
179}
180
198
208
209
216{
218 return p->fragmentMap().position(fragment) + 1;
219}
220
227{
229 const QTextTablePrivate *td = table->d_func();
230 int index = table->d_func()->findCellIndex(fragment);
231 int f;
232 if (index != -1)
233 f = td->cells.value(index + 1, td->fragment_end);
234 else
235 f = td->fragment_end;
236 return p->fragmentMap().position(f);
237}
238
239
246{
248 int b = p->blockMap().findNode(firstPosition());
249 int e = p->blockMap().findNode(lastPosition()+1);
250 return QTextFrame::iterator(const_cast<QTextTable *>(table), b, b, e);
251}
252
259{
261 int b = p->blockMap().findNode(firstPosition());
262 int e = p->blockMap().findNode(lastPosition()+1);
263 return QTextFrame::iterator(const_cast<QTextTable *>(table), e, b, e);
264}
265
266
287QTextTable *QTextTablePrivate::createTable(QTextDocumentPrivate *pieceTable, int pos, int rows, int cols, const QTextTableFormat &tableFormat)
288{
289 QTextTableFormat fmt = tableFormat;
290 fmt.setColumns(cols);
291 QTextTable *table = qobject_cast<QTextTable *>(pieceTable->createObject(fmt));
293
295
296// qDebug("---> createTable: rows=%d, cols=%d at %d", rows, cols, pos);
297 // add block after table
298 QTextCharFormat charFmt;
299 charFmt.setObjectIndex(table->objectIndex());
300 charFmt.setObjectType(QTextFormat::TableCellObject);
301
302
303 int charIdx = pieceTable->formatCollection()->indexForFormat(charFmt);
305
306 QTextTablePrivate *d = table->d_func();
307 d->blockFragmentUpdates = true;
308
309 d->fragment_start = pieceTable->insertBlock(QTextBeginningOfFrame, pos, cellIdx, charIdx);
310 d->cells.append(d->fragment_start);
311 ++pos;
312
313 for (int i = 1; i < rows*cols; ++i) {
314 d->cells.append(pieceTable->insertBlock(QTextBeginningOfFrame, pos, cellIdx, charIdx));
315// qDebug(" addCell at %d", pos);
316 ++pos;
317 }
318
319 d->fragment_end = pieceTable->insertBlock(QTextEndOfFrame, pos, cellIdx, charIdx);
320// qDebug(" addEOR at %d", pos);
321 ++pos;
322
323 d->blockFragmentUpdates = false;
324 d->dirty = true;
325
327
328 return table;
329}
330
338
339static inline bool operator<(int fragment, const QFragmentFindHelper &helper)
340{
341 return helper.fragmentMap.position(fragment) < helper.pos;
342}
343
344static inline bool operator<(const QFragmentFindHelper &helper, int fragment)
345{
346 return helper.pos < helper.fragmentMap.position(fragment);
347}
348
349int QTextTablePrivate::findCellIndex(int fragment) const
350{
353 const auto it = std::lower_bound(cells.constBegin(), cells.constEnd(), helper);
354 if ((it == cells.constEnd()) || (helper < *it))
355 return -1;
356 return it - cells.constBegin();
357}
358
360{
361 dirty = true;
363 return;
365 Q_ASSERT(cells.indexOf(int(fragment)) == -1);
366 const uint pos = pieceTable->fragmentMap().position(fragment);
368 auto it = std::lower_bound(cells.begin(), cells.end(), helper);
369 cells.insert(it, fragment);
370 if (!fragment_start || pos < pieceTable->fragmentMap().position(fragment_start))
371 fragment_start = fragment;
372 return;
373 }
375}
376
378{
379 dirty = true;
381 return;
383 Q_ASSERT(cells.indexOf(int(fragment)) != -1);
384 cells.removeAll(int(fragment));
385 if (fragment_start == fragment && cells.size()) {
387 }
388 if (fragment_start != fragment)
389 return;
390 }
392}
393
402{
403 Q_Q(const QTextTable);
404 nCols = q->format().columns();
405 nRows = (cells.size() + nCols-1)/nCols;
406// qDebug(">>>> QTextTablePrivate::update, nRows=%d, nCols=%d", nRows, nCols);
407
408 grid.assign(nRows * nCols, 0);
409
411 QTextFormatCollection *c = p->formatCollection();
412
414
415 int cell = 0;
416 for (int i = 0; i < cells.size(); ++i) {
417 int fragment = cells.at(i);
418 QTextCharFormat fmt = c->charFormat(QTextDocumentPrivate::FragmentIterator(&p->fragmentMap(), fragment)->format);
419 int rowspan = fmt.tableCellRowSpan();
420 int colspan = fmt.tableCellColumnSpan();
421
422 // skip taken cells
423 while (cell < nRows*nCols && grid[cell])
424 ++cell;
425
426 int r = cell/nCols;
427 int c = cell%nCols;
428 cellIndices[i] = cell;
429
430 if (r + rowspan > nRows) {
431 grid.resize((r + rowspan) * nCols, 0);
432 nRows = r + rowspan;
433 }
434
435 Q_ASSERT(c + colspan <= nCols);
436 for (int ii = 0; ii < rowspan; ++ii) {
437 for (int jj = 0; jj < colspan; ++jj) {
438 Q_ASSERT(grid[(r+ii)*nCols + c+jj] == 0);
439 grid[(r+ii)*nCols + c+jj] = fragment;
440// qDebug(" setting cell %d span=%d/%d at %d/%d", fragment, rowspan, colspan, r+ii, c+jj);
441 }
442 }
443 }
444// qDebug("<<<< end: nRows=%d, nCols=%d", nRows, nCols);
445
446 dirty = false;
447}
448
449
450
451
452
529
537
538
547{
548 Q_D(const QTextTable);
549 if (d->dirty)
550 d->update();
551
552 if (row < 0 || row >= d->nRows || col < 0 || col >= d->nCols)
553 return QTextTableCell();
554
555 return QTextTableCell(this, d->grid[row*d->nCols + col]);
556}
557
565{
566 Q_D(const QTextTable);
567 if (d->dirty)
568 d->update();
569
571 const QTextDocumentPrivate::FragmentMap &map = d->pieceTable->fragmentMap();
572 if (position < 0 || map.position(d->fragment_start) >= pos || map.position(d->fragment_end) < pos)
573 return QTextTableCell();
574
576 auto it = std::lower_bound(d->cells.begin(), d->cells.end(), helper);
577 if (it != d->cells.begin())
578 --it;
579
580 return QTextTableCell(this, *it);
581}
582
591{
592 return cellAt(c.position());
593}
594
602void QTextTable::resize(int rows, int cols)
603{
604 Q_D(QTextTable);
605 if (d->dirty)
606 d->update();
607
608 int nRows = this->rows();
609 int nCols = this->columns();
610
611 if (rows == nRows && cols == nCols)
612 return;
613
614 d->pieceTable->beginEditBlock();
615
616 if (nCols < cols)
617 insertColumns(nCols, cols - nCols);
618 else if (nCols > cols)
619 removeColumns(cols, nCols - cols);
620
621 if (nRows < rows)
622 insertRows(nRows, rows-nRows);
623 else if (nRows > rows)
624 removeRows(rows, nRows-rows);
625
626 d->pieceTable->endEditBlock();
627}
628
637{
638 Q_D(QTextTable);
639 if (num <= 0)
640 return;
641
642 if (d->dirty)
643 d->update();
644
645 if (pos > d->nRows || pos < 0)
646 pos = d->nRows;
647
648// qDebug() << "-------- insertRows" << pos << num;
649 QTextDocumentPrivate *p = d->pieceTable;
650 QTextFormatCollection *c = p->formatCollection();
651 p->beginEditBlock();
652
653 int extended = 0;
654 int insert_before = 0;
655 if (pos > 0 && pos < d->nRows) {
656 int lastCell = -1;
657 for (int i = 0; i < d->nCols; ++i) {
658 int cell = d->grid[pos*d->nCols + i];
659 if (cell == d->grid[(pos-1)*d->nCols+i]) {
660 // cell spans the insertion place, extend it
661 if (cell != lastCell) {
662 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
663 QTextCharFormat fmt = c->charFormat(it->format);
664 fmt.setTableCellRowSpan(fmt.tableCellRowSpan() + num);
665 p->setCharFormat(it.position(), 1, fmt);
666 }
667 extended++;
668 } else if (!insert_before) {
669 insert_before = cell;
670 }
671 lastCell = cell;
672 }
673 } else {
674 insert_before = (pos == 0 ? d->grid[0] : d->fragment_end);
675 }
676 if (extended < d->nCols) {
677 Q_ASSERT(insert_before);
678 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), insert_before);
679 QTextCharFormat fmt = c->charFormat(it->format);
681 fmt.setTableCellColumnSpan(1);
682 Q_ASSERT(fmt.objectIndex() == objectIndex());
683 int pos = it.position();
684 int cfmt = p->formatCollection()->indexForFormat(fmt);
685 int bfmt = p->formatCollection()->indexForFormat(QTextBlockFormat());
686// qDebug("inserting %d cells, nCols=%d extended=%d", num*(d->nCols-extended), d->nCols, extended);
687 for (int i = 0; i < num*(d->nCols-extended); ++i)
688 p->insertBlock(QTextBeginningOfFrame, pos, bfmt, cfmt, QTextUndoCommand::MoveCursor);
689 }
690
691// qDebug() << "-------- end insertRows" << pos << num;
692 p->endEditBlock();
693}
694
703{
704 Q_D(QTextTable);
705 if (num <= 0)
706 return;
707
708 if (d->dirty)
709 d->update();
710
711 if (pos > d->nCols || pos < 0)
712 pos = d->nCols;
713
714// qDebug() << "-------- insertCols" << pos << num;
715 QTextDocumentPrivate *p = d->pieceTable;
716 QTextFormatCollection *c = p->formatCollection();
717 p->beginEditBlock();
718
719 QList<int> extendedSpans;
720 for (int i = 0; i < d->nRows; ++i) {
721 int cell;
722 if (i == d->nRows - 1 && pos == d->nCols) {
723 cell = d->fragment_end;
724 } else {
725 int logicalGridIndexBeforePosition = pos > 0
726 ? d->findCellIndex(d->grid[i*d->nCols + pos - 1])
727 : -1;
728
729 // Search for the logical insertion point by skipping past cells which are not the first
730 // cell in a rowspan. This means any cell for which the logical grid index is
731 // less than the logical cell index of the cell before the insertion.
732 int logicalGridIndex;
733 int gridArrayOffset = i*d->nCols + pos;
734 do {
735 cell = d->grid[gridArrayOffset];
736 logicalGridIndex = d->findCellIndex(cell);
737 gridArrayOffset++;
738 } while (logicalGridIndex < logicalGridIndexBeforePosition
739 && gridArrayOffset < d->nRows*d->nCols);
740
741 if (logicalGridIndex < logicalGridIndexBeforePosition
742 && gridArrayOffset == d->nRows*d->nCols)
743 cell = d->fragment_end;
744 }
745
746 if (pos > 0 && pos < d->nCols && cell == d->grid[i*d->nCols + pos - 1]) {
747 // cell spans the insertion place, extend it
748 if (!extendedSpans.contains(cell)) {
749 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
750 QTextCharFormat fmt = c->charFormat(it->format);
751 fmt.setTableCellColumnSpan(fmt.tableCellColumnSpan() + num);
752 p->setCharFormat(it.position(), 1, fmt);
753 d->dirty = true;
754 extendedSpans << cell;
755 }
756 } else {
757 /* If the next cell is spanned from the row above, we need to find the right position
758 to insert to */
759 if (i > 0 && pos < d->nCols && cell == d->grid[(i-1) * d->nCols + pos]) {
760 int gridIndex = i*d->nCols + pos;
761 const int gridEnd = d->nRows * d->nCols - 1;
762 while (gridIndex < gridEnd && cell == d->grid[gridIndex]) {
763 ++gridIndex;
764 }
765 if (gridIndex == gridEnd)
766 cell = d->fragment_end;
767 else
768 cell = d->grid[gridIndex];
769 }
770 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
771 QTextCharFormat fmt = c->charFormat(it->format);
773 fmt.setTableCellColumnSpan(1);
774 Q_ASSERT(fmt.objectIndex() == objectIndex());
775 int position = it.position();
776 int cfmt = p->formatCollection()->indexForFormat(fmt);
777 int bfmt = p->formatCollection()->indexForFormat(QTextBlockFormat());
778 for (int i = 0; i < num; ++i)
780 }
781 }
782
783 QTextTableFormat tfmt = format();
784 tfmt.setColumns(tfmt.columns()+num);
785 QList<QTextLength> columnWidths = tfmt.columnWidthConstraints();
786 if (! columnWidths.isEmpty()) {
787 for (int i = num; i > 0; --i)
788 columnWidths.insert(pos, columnWidths.at(qMax(0, pos - 1)));
789 }
790 tfmt.setColumnWidthConstraints (columnWidths);
792
793// qDebug() << "-------- end insertCols" << pos << num;
794 p->endEditBlock();
795}
796
804{
806}
807
818
827{
828 Q_D(QTextTable);
829// qDebug() << "-------- removeRows" << pos << num;
830
831 if (num <= 0 || pos < 0)
832 return;
833 if (d->dirty)
834 d->update();
835 if (pos >= d->nRows)
836 return;
837 if (pos+num > d->nRows)
838 num = d->nRows - pos;
839
840 QTextDocumentPrivate *p = d->pieceTable;
841 QTextFormatCollection *collection = p->formatCollection();
842 p->beginEditBlock();
843
844 // delete whole table?
845 if (pos == 0 && num == d->nRows) {
846 const int pos = p->fragmentMap().position(d->fragment_start);
847 p->remove(pos, p->fragmentMap().position(d->fragment_end) - pos + 1);
848 p->endEditBlock();
849 return;
850 }
851
852 p->aboutToRemoveCell(cellAt(pos, 0).firstPosition(), cellAt(pos + num - 1, d->nCols - 1).lastPosition());
853
854 QList<int> touchedCells;
855 for (int r = pos; r < pos + num; ++r) {
856 for (int c = 0; c < d->nCols; ++c) {
857 int cell = d->grid[r*d->nCols + c];
858 if (touchedCells.contains(cell))
859 continue;
860 touchedCells << cell;
861 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
862 QTextCharFormat fmt = collection->charFormat(it->format);
863 int span = fmt.tableCellRowSpan();
864 if (span > 1) {
865 fmt.setTableCellRowSpan(span - 1);
866 p->setCharFormat(it.position(), 1, fmt);
867 } else {
868 // remove cell
869 int index = d->cells.indexOf(cell) + 1;
870 int f_end = index < d->cells.size() ? d->cells.at(index) : d->fragment_end;
871 p->remove(it.position(), p->fragmentMap().position(f_end) - it.position());
872 }
873 }
874 }
875
876 p->endEditBlock();
877// qDebug() << "-------- end removeRows" << pos << num;
878}
879
889{
890 Q_D(QTextTable);
891// qDebug() << "-------- removeCols" << pos << num;
892
893 if (num <= 0 || pos < 0)
894 return;
895 if (d->dirty)
896 d->update();
897 if (pos >= d->nCols)
898 return;
899 if (pos + num > d->nCols)
900 pos = d->nCols - num;
901
902 QTextDocumentPrivate *p = d->pieceTable;
903 QTextFormatCollection *collection = p->formatCollection();
904 p->beginEditBlock();
905
906 // delete whole table?
907 if (pos == 0 && num == d->nCols) {
908 const int pos = p->fragmentMap().position(d->fragment_start);
909 p->remove(pos, p->fragmentMap().position(d->fragment_end) - pos + 1);
910 p->endEditBlock();
911 return;
912 }
913
914 p->aboutToRemoveCell(cellAt(0, pos).firstPosition(), cellAt(d->nRows - 1, pos + num - 1).lastPosition());
915
916 QList<int> touchedCells;
917 for (int r = 0; r < d->nRows; ++r) {
918 for (int c = pos; c < pos + num; ++c) {
919 int cell = d->grid[r*d->nCols + c];
920 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
921 QTextCharFormat fmt = collection->charFormat(it->format);
923 if (touchedCells.contains(cell) && span <= 1)
924 continue;
925 touchedCells << cell;
926
927 if (span > 1) {
928 fmt.setTableCellColumnSpan(span - 1);
929 p->setCharFormat(it.position(), 1, fmt);
930 } else {
931 // remove cell
932 int index = d->cells.indexOf(cell) + 1;
933 int f_end = index < d->cells.size() ? d->cells.at(index) : d->fragment_end;
934 p->remove(it.position(), p->fragmentMap().position(f_end) - it.position());
935 }
936 }
937 }
938
939 QTextTableFormat tfmt = format();
940 tfmt.setColumns(tfmt.columns()-num);
941 QList<QTextLength> columnWidths = tfmt.columnWidthConstraints();
942 if (columnWidths.size() > pos) {
943 columnWidths.remove(pos, num);
944 tfmt.setColumnWidthConstraints (columnWidths);
945 }
947
948 p->endEditBlock();
949// qDebug() << "-------- end removeCols" << pos << num;
950}
951
962void QTextTable::mergeCells(int row, int column, int numRows, int numCols)
963{
964 Q_D(QTextTable);
965
966 if (d->dirty)
967 d->update();
968
969 QTextDocumentPrivate *p = d->pieceTable;
970 QTextFormatCollection *fc = p->formatCollection();
971
972 const QTextTableCell cell = cellAt(row, column);
973 if (!cell.isValid() || row != cell.row() || column != cell.column())
974 return;
975
976 QTextCharFormat fmt = cell.format();
977 const int rowSpan = fmt.tableCellRowSpan();
978 const int colSpan = fmt.tableCellColumnSpan();
979
980 numRows = qMin(numRows, rows() - cell.row());
981 numCols = qMin(numCols, columns() - cell.column());
982
983 // nothing to merge?
984 if (numRows < rowSpan || numCols < colSpan)
985 return;
986
987 // check the edges of the merge rect to make sure no cell spans the edge
988 for (int r = row; r < row + numRows; ++r) {
989 if (cellAt(r, column) == cellAt(r, column - 1))
990 return;
991 if (cellAt(r, column + numCols) == cellAt(r, column + numCols - 1))
992 return;
993 }
994
995 for (int c = column; c < column + numCols; ++c) {
996 if (cellAt(row, c) == cellAt(row - 1, c))
997 return;
998 if (cellAt(row + numRows, c) == cellAt(row + numRows - 1, c))
999 return;
1000 }
1001
1002 p->beginEditBlock();
1003
1004 const int origCellPosition = cell.firstPosition() - 1;
1005
1006 const int cellFragment = d->grid[row * d->nCols + column];
1007
1008 // find the position at which to insert the contents of the merged cells
1009 QFragmentFindHelper helper(origCellPosition, p->fragmentMap());
1010 const auto begin = d->cells.cbegin();
1011 const auto it = std::lower_bound(begin, d->cells.cend(), helper);
1012 Q_ASSERT(it != d->cells.cend());
1013 Q_ASSERT(!(helper < *it));
1014 Q_ASSERT(*it == cellFragment);
1015 const int insertCellIndex = it - begin;
1016 int insertFragment = d->cells.value(insertCellIndex + 1, d->fragment_end);
1017 uint insertPos = p->fragmentMap().position(insertFragment);
1018
1019 d->blockFragmentUpdates = true;
1020
1021 bool rowHasText = cell.firstCursorPosition().block().length();
1022 bool needsParagraph = rowHasText && colSpan == numCols;
1023
1024 // find all cells that will be erased by the merge
1025 for (int r = row; r < row + numRows; ++r) {
1026 int firstColumn = r < row + rowSpan ? column + colSpan : column;
1027
1028 // don't recompute the cell index for the first row
1029 int firstCellIndex = r == row ? insertCellIndex + 1 : -1;
1030 int cellIndex = firstCellIndex;
1031
1032 for (int c = firstColumn; c < column + numCols; ++c) {
1033 const int fragment = d->grid[r * d->nCols + c];
1034
1035 // already handled?
1036 if (fragment == cellFragment)
1037 continue;
1038
1039 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), fragment);
1040 uint pos = it.position();
1041
1042 if (firstCellIndex == -1) {
1043 QFragmentFindHelper helper(pos, 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 == fragment);
1049 firstCellIndex = cellIndex = it - begin;
1050 }
1051
1052 ++cellIndex;
1053
1054 QTextCharFormat fmt = fc->charFormat(it->format);
1055
1056 const int cellRowSpan = fmt.tableCellRowSpan();
1057 const int cellColSpan = fmt.tableCellColumnSpan();
1058
1059 // update the grid for this cell
1060 for (int i = r; i < r + cellRowSpan; ++i)
1061 for (int j = c; j < c + cellColSpan; ++j)
1062 d->grid[i * d->nCols + j] = cellFragment;
1063
1064 // erase the cell marker
1065 p->remove(pos, 1);
1066
1067 const int nextFragment = d->cells.value(cellIndex, d->fragment_end);
1068 const uint nextPos = p->fragmentMap().position(nextFragment);
1069
1070 Q_ASSERT(nextPos >= pos);
1071
1072 // merge the contents of the cell (if not empty)
1073 if (nextPos > pos) {
1074 if (needsParagraph) {
1075 needsParagraph = false;
1076 QTextCursorPrivate::fromPosition(p, insertPos++).insertBlock();
1077 p->move(pos + 1, insertPos, nextPos - pos);
1078 } else if (rowHasText) {
1079 QTextCursorPrivate::fromPosition(p, insertPos++).insertText(" "_L1);
1080 p->move(pos + 1, insertPos, nextPos - pos);
1081 } else {
1082 p->move(pos, insertPos, nextPos - pos);
1083 }
1084
1085 insertPos += nextPos - pos;
1086 rowHasText = true;
1087 }
1088 }
1089
1090 if (rowHasText) {
1091 needsParagraph = true;
1092 rowHasText = false;
1093 }
1094
1095 // erase cells from last row
1096 if (firstCellIndex >= 0) {
1097 d->cellIndices.remove(firstCellIndex, cellIndex - firstCellIndex);
1098 d->cells.erase(d->cells.begin() + firstCellIndex, d->cells.begin() + cellIndex);
1099 }
1100 }
1101
1102 d->fragment_start = d->cells.constFirst();
1103
1104 fmt.setTableCellRowSpan(numRows);
1105 fmt.setTableCellColumnSpan(numCols);
1106 p->setCharFormat(origCellPosition, 1, fmt);
1107
1108 d->blockFragmentUpdates = false;
1109 d->dirty = false;
1110
1111 p->endEditBlock();
1112}
1113
1123{
1124 if (!cursor.hasComplexSelection())
1125 return;
1126
1127 int firstRow, numRows, firstColumn, numColumns;
1128 cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
1129 mergeCells(firstRow, firstColumn, numRows, numColumns);
1130}
1131
1143void QTextTable::splitCell(int row, int column, int numRows, int numCols)
1144{
1145 Q_D(QTextTable);
1146
1147 if (d->dirty)
1148 d->update();
1149
1150 QTextDocumentPrivate *p = d->pieceTable;
1151 QTextFormatCollection *c = p->formatCollection();
1152
1153 const QTextTableCell cell = cellAt(row, column);
1154 if (!cell.isValid())
1155 return;
1156 row = cell.row();
1157 column = cell.column();
1158
1159 QTextCharFormat fmt = cell.format();
1160 const int rowSpan = fmt.tableCellRowSpan();
1161 const int colSpan = fmt.tableCellColumnSpan();
1162
1163 // nothing to split?
1164 if (numRows > rowSpan || numCols > colSpan)
1165 return;
1166
1167 p->beginEditBlock();
1168
1169 const int origCellPosition = cell.firstPosition() - 1;
1170
1171 QVarLengthArray<int> rowPositions(rowSpan);
1172
1173 rowPositions[0] = cell.lastPosition();
1174
1175 for (int r = row + 1; r < row + rowSpan; ++r) {
1176 // find the cell before which to insert the new cell markers
1177 int gridIndex = r * d->nCols + column;
1178 const auto begin = d->cellIndices.cbegin();
1179 const auto it = std::upper_bound(begin, d->cellIndices.cend(), gridIndex);
1180 int fragment = d->cells.value(it - begin, d->fragment_end);
1181 rowPositions[r - row] = p->fragmentMap().position(fragment);
1182 }
1183
1184 fmt.setTableCellColumnSpan(1);
1185 fmt.setTableCellRowSpan(1);
1186 const int fmtIndex = c->indexForFormat(fmt);
1187 const int blockIndex = p->blockMap().find(cell.lastPosition())->format;
1188
1189 int insertAdjustement = 0;
1190 for (int i = 0; i < numRows; ++i) {
1191 for (int c = 0; c < colSpan - numCols; ++c)
1192 p->insertBlock(QTextBeginningOfFrame, rowPositions[i] + insertAdjustement + c, blockIndex, fmtIndex);
1193 insertAdjustement += colSpan - numCols;
1194 }
1195
1196 for (int i = numRows; i < rowSpan; ++i) {
1197 for (int c = 0; c < colSpan; ++c)
1198 p->insertBlock(QTextBeginningOfFrame, rowPositions[i] + insertAdjustement + c, blockIndex, fmtIndex);
1199 insertAdjustement += colSpan;
1200 }
1201
1202 fmt.setTableCellRowSpan(numRows);
1203 fmt.setTableCellColumnSpan(numCols);
1204 p->setCharFormat(origCellPosition, 1, fmt);
1205
1206 p->endEditBlock();
1207}
1208
1215{
1216 Q_D(const QTextTable);
1217 if (d->dirty)
1218 d->update();
1219
1220 return d->nRows;
1221}
1222
1229{
1230 Q_D(const QTextTable);
1231 if (d->dirty)
1232 d->update();
1233
1234 return d->nCols;
1235}
1236
1246{
1247 Q_D(const QTextTable);
1248 QTextTableCell cell = cellAt(c);
1249 if (!cell.isValid())
1250 return QTextCursor();
1251
1252 int row = cell.row();
1253 QTextDocumentPrivate *p = d->pieceTable;
1254 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), d->grid[row*d->nCols]);
1255 return QTextCursorPrivate::fromPosition(p, it.position());
1256}
1257
1267{
1268 Q_D(const QTextTable);
1269 QTextTableCell cell = cellAt(c);
1270 if (!cell.isValid())
1271 return QTextCursor();
1272
1273 int row = cell.row() + 1;
1274 int fragment = row < d->nRows ? d->grid[row*d->nCols] : d->fragment_end;
1275 QTextDocumentPrivate *p = d->pieceTable;
1276 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), fragment);
1277 return QTextCursorPrivate::fromPosition(p, it.position() - 1);
1278}
1279
1288{
1290 // don't try to change the number of table columns from here
1293}
1294
1304
1305#include "moc_qtexttable.cpp"
\inmodule QtCore
uint position(uint node, uint field=0) const
qsizetype size() const noexcept
Definition qlist.h:397
iterator insert(qsizetype i, parameter_type t)
Definition qlist.h:488
iterator end()
Definition qlist.h:626
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
T value(qsizetype i) const
Definition qlist.h:664
const_iterator constBegin() const noexcept
Definition qlist.h:632
qsizetype removeAll(const AT &t)
Definition qlist.h:592
iterator begin()
Definition qlist.h:625
void resize(qsizetype size)
Definition qlist.h:403
const_iterator constEnd() const noexcept
Definition qlist.h:633
iterator begin()
Definition qset.h:136
const_iterator cend() const noexcept
Definition qset.h:142
int length() const
Returns the length of the block in characters.
int tableCellColumnSpan() const
void setTableCellColumnSpan(int tableCellColumnSpan)
int tableCellRowSpan() const
void setTableCellRowSpan(int tableCellRowSpan)
static QTextCursor fromPosition(QTextDocumentPrivate *d, int pos)
\reentrant \inmodule QtGui
Definition qtextcursor.h:30
QTextBlock block() const
Returns the block that contains the cursor.
QTextFormatCollection * formatCollection()
int insertBlock(int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation=QTextUndoCommand::MoveCursor)
const FragmentMap & fragmentMap() const
QTextObject * createObject(const QTextFormat &newFormat, int objectIndex=-1)
static const QTextDocumentPrivate * get(const QTextDocument *document)
FragmentMap::ConstIterator FragmentIterator
\reentrant \inmodule QtGui
int indexForFormat(const QTextFormat &f)
void setObjectType(int type)
Sets the text format's object type to type.
void setObjectIndex(int object)
Sets the format object's object index.
void clearProperty(int propertyId)
Clears the value of the property given by propertyId.
virtual void fragmentRemoved(QChar type, uint fragment)
virtual void fragmentAdded(QChar type, uint fragment)
\reentrant
Definition qtextobject.h:81
friend class iterator
iterator begin() const
Returns an iterator pointing to the first document element inside the frame.
int firstPosition() const
Returns the first document position inside the frame.
QTextDocumentPrivate * pieceTable
int objectIndex() const
Returns the object index of this object.
void setFormat(const QTextFormat &format)
Sets the text object's format.
\reentrant
Definition qtexttable.h:19
QTextCharFormat format() const
Returns the cell's character format.
int columnSpan() const
Returns the number of columns this cell spans.
int firstPosition() const
QTextFrame::iterator end() const
Returns a frame iterator pointing to the end of the table's cell.
int row() const
Returns the number of the row in the table that contains this cell.
QTextCursor firstCursorPosition() const
Returns the first valid cursor position in this cell.
int rowSpan() const
Returns the number of rows this cell spans.
void setFormat(const QTextCharFormat &format)
QTextCursor lastCursorPosition() const
Returns the last valid cursor position in this cell.
int lastPosition() const
bool isValid() const
Returns true if this is a valid table cell; otherwise returns false.
Definition qtexttable.h:36
int column() const
Returns the number of the column in the table that contains this cell.
int tableCellFormatIndex() const
QTextFrame::iterator begin() const
Returns a frame iterator pointing to the beginning of the table's cell.
void setColumns(int columns)
QList< int > cells
static QTextTable * createTable(QTextDocumentPrivate *, int pos, int rows, int cols, const QTextTableFormat &tableFormat)
void fragmentRemoved(QChar type, uint fragment) override
int findCellIndex(int fragment) const
void fragmentAdded(QChar type, uint fragment) override
void update() const
/fn void QTextTablePrivate::update() const
std::vector< int > grid
QList< int > cellIndices
\reentrant
Definition qtexttable.h:63
int columns() const
Returns the number of columns in the table.
void removeRows(int pos, int num)
Removes a number of rows starting with the row at the specified index.
QTextTableFormat format() const
Returns the table's format.
Definition qtexttable.h:92
QTextCursor rowStart(const QTextCursor &c) const
Returns a cursor pointing to the start of the row that contains the given cursor.
friend class QTextTableCell
Definition qtexttable.h:97
void appendRows(int count)
void removeColumns(int pos, int num)
Removes a number of columns starting with the column at the specified index.
void insertRows(int pos, int num)
Inserts a number of rows before the row with the specified index.
void splitCell(int row, int col, int numRows, int numCols)
QTextTable(QTextDocument *doc)
void appendColumns(int count)
QTextCursor rowEnd(const QTextCursor &c) const
Returns a cursor pointing to the end of the row that contains the given cursor.
void setFormat(const QTextTableFormat &format)
Sets the table's format.
int rows() const
Returns the number of rows in the table.
void insertColumns(int pos, int num)
Inserts a number of columns before the column with the specified index.
QTextTableCell cellAt(int row, int col) const
Returns the table cell at the given row and column in the table.
void mergeCells(int row, int col, int numRows, int numCols)
void resize(int rows, int cols)
Resizes the table to contain the required number of rows and columns.
QMap< QString, QString > map
[6]
QCursor cursor
QSet< QString >::iterator it
Combined button and popup list for selecting options.
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLboolean GLboolean GLboolean b
GLuint index
[2]
GLboolean r
[2]
GLenum GLenum GLsizei count
GLfloat GLfloat f
GLenum type
GLint GLsizei GLsizei GLenum format
GLenum GLenum GLsizei void GLsizei void * column
const GLubyte * c
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLenum GLenum GLsizei void * row
GLenum GLenum GLsizei void GLsizei void void * span
GLfloat GLfloat p
[1]
GLuint num
GLenum GLenum GLsizei void * table
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QTextBeginningOfFrame
#define QTextEndOfFrame
static bool operator<(int fragment, const QFragmentFindHelper &helper)
unsigned int uint
Definition qtypes.h:34
QVideoFrameFormat::PixelFormat fmt
QFragmentFindHelper(int _pos, const QTextDocumentPrivate::FragmentMap &map)
const QTextDocumentPrivate::FragmentMap & fragmentMap
qsizetype indexOf(const AT &t, qsizetype from=0) const noexcept
Definition qlist.h:962