1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qtexttable.h"
43 #include "qtextcursor.h"
44 #include "qtextformat.h"
46 #include "qtexttable_p.h"
47 #include "qvarlengtharray.h"
48 #include "private/qfunctions_p.h"
58 \brief The QTextTableCell class represents the properties of a
62 \ingroup richtext-processing
64 Table cells are pieces of document structure that belong to a table.
65 The table orders cells into particular rows and columns; cells can
66 also span multiple columns and rows.
68 Cells are usually created when a table is inserted into a document with
69 QTextCursor::insertTable(), but they are also created and destroyed when
72 Cells contain information about their location in a table; you can
73 obtain the row() and column() numbers of a cell, and its rowSpan()
76 The format() of a cell describes the default character format of its
77 contents. The firstCursorPosition() and lastCursorPosition() functions
78 are used to obtain the extent of the cell in the document.
80 \sa QTextTable, QTextTableFormat
84 \fn QTextTableCell::QTextTableCell()
86 Constructs an invalid table cell.
92 \fn QTextTableCell::QTextTableCell(const QTextTableCell &other)
94 Copy constructor. Creates a new QTextTableCell object based on the
99 \fn QTextTableCell& QTextTableCell::operator=(const QTextTableCell &other)
101 Assigns the \a other table cell to this table cell.
107 Sets the cell's character format to \a format. This can for example be used to change
108 the background color of the entire cell:
110 QTextTableCell cell = table->cellAt(2, 3);
111 QTextCharFormat format = cell.format();
112 format.setBackground(Qt::blue);
113 cell.setFormat(format);
115 Note that the cell's row or column span cannot be changed through this function. You have
116 to use QTextTable::mergeCells and QTextTable::splitCell instead.
120 void QTextTableCell::setFormat(const QTextCharFormat &format)
122 QTextCharFormat fmt = format;
123 fmt.clearProperty(QTextFormat::ObjectIndex);
124 fmt.setObjectType(QTextFormat::TableCellObject);
125 QTextDocumentPrivate *p = table->docHandle();
126 QTextDocumentPrivate::FragmentIterator frag(&p->fragmentMap(), fragment);
128 QTextFormatCollection *c = p->formatCollection();
129 QTextCharFormat oldFormat = c->charFormat(frag->format);
130 fmt.setTableCellRowSpan(oldFormat.tableCellRowSpan());
131 fmt.setTableCellColumnSpan(oldFormat.tableCellColumnSpan());
133 p->setCharFormat(frag.position(), 1, fmt, QTextDocumentPrivate::SetFormatAndPreserveObjectIndices);
137 Returns the cell's character format.
139 QTextCharFormat QTextTableCell::format() const
141 QTextDocumentPrivate *p = table->docHandle();
142 QTextFormatCollection *c = p->formatCollection();
144 QTextCharFormat fmt = c->charFormat(tableCellFormatIndex());
145 fmt.setObjectType(QTextFormat::TableCellObject);
152 Returns the index of the tableCell's format in the document's internal list of formats.
154 \sa QTextDocument::allFormats()
156 int QTextTableCell::tableCellFormatIndex() const
158 QTextDocumentPrivate *p = table->docHandle();
159 return QTextDocumentPrivate::FragmentIterator(&p->fragmentMap(), fragment)->format;
163 Returns the number of the row in the table that contains this cell.
167 int QTextTableCell::row() const
169 const QTextTablePrivate *tp = table->d_func();
173 int idx = tp->findCellIndex(fragment);
176 return tp->cellIndices.at(idx) / tp->nCols;
180 Returns the number of the column in the table that contains this cell.
184 int QTextTableCell::column() const
186 const QTextTablePrivate *tp = table->d_func();
190 int idx = tp->findCellIndex(fragment);
193 return tp->cellIndices.at(idx) % tp->nCols;
197 Returns the number of rows this cell spans. The default is 1.
201 int QTextTableCell::rowSpan() const
203 return format().tableCellRowSpan();
207 Returns the number of columns this cell spans. The default is 1.
211 int QTextTableCell::columnSpan() const
213 return format().tableCellColumnSpan();
217 \fn bool QTextTableCell::isValid() const
219 Returns true if this is a valid table cell; otherwise returns
225 Returns the first valid cursor position in this cell.
227 \sa lastCursorPosition()
229 QTextCursor QTextTableCell::firstCursorPosition() const
231 return QTextCursor(table->d_func()->pieceTable, firstPosition());
235 Returns the last valid cursor position in this cell.
237 \sa firstCursorPosition()
239 QTextCursor QTextTableCell::lastCursorPosition() const
241 return QTextCursor(table->d_func()->pieceTable, lastPosition());
248 Returns the first valid position in the document occupied by this cell.
250 int QTextTableCell::firstPosition() const
252 QTextDocumentPrivate *p = table->docHandle();
253 return p->fragmentMap().position(fragment) + 1;
259 Returns the last valid position in the document occupied by this cell.
261 int QTextTableCell::lastPosition() const
263 QTextDocumentPrivate *p = table->docHandle();
264 const QTextTablePrivate *td = table->d_func();
265 int index = table->d_func()->findCellIndex(fragment);
268 f = td->cells.value(index + 1, td->fragment_end);
270 f = td->fragment_end;
271 return p->fragmentMap().position(f);
276 Returns a frame iterator pointing to the beginning of the table's cell.
280 QTextFrame::iterator QTextTableCell::begin() const
282 QTextDocumentPrivate *p = table->docHandle();
283 int b = p->blockMap().findNode(firstPosition());
284 int e = p->blockMap().findNode(lastPosition()+1);
285 return QTextFrame::iterator(const_cast<QTextTable *>(table), b, b, e);
289 Returns a frame iterator pointing to the end of the table's cell.
293 QTextFrame::iterator QTextTableCell::end() const
295 QTextDocumentPrivate *p = table->docHandle();
296 int b = p->blockMap().findNode(firstPosition());
297 int e = p->blockMap().findNode(lastPosition()+1);
298 return QTextFrame::iterator(const_cast<QTextTable *>(table), e, b, e);
303 \fn QTextCursor QTextTableCell::operator==(const QTextTableCell &other) const
305 Returns true if this cell object and the \a other cell object
306 describe the same cell; otherwise returns false.
310 \fn QTextCursor QTextTableCell::operator!=(const QTextTableCell &other) const
312 Returns true if this cell object and the \a other cell object
313 describe different cells; otherwise returns false.
317 \fn QTextTableCell::~QTextTableCell()
319 Destroys the table cell.
322 QTextTablePrivate::~QTextTablePrivate()
329 QTextTable *QTextTablePrivate::createTable(QTextDocumentPrivate *pieceTable, int pos, int rows, int cols, const QTextTableFormat &tableFormat)
331 QTextTableFormat fmt = tableFormat;
332 fmt.setColumns(cols);
333 QTextTable *table = qobject_cast<QTextTable *>(pieceTable->createObject(fmt));
336 pieceTable->beginEditBlock();
338 // qDebug("---> createTable: rows=%d, cols=%d at %d", rows, cols, pos);
339 // add block after table
340 QTextCharFormat charFmt;
341 charFmt.setObjectIndex(table->objectIndex());
342 charFmt.setObjectType(QTextFormat::TableCellObject);
345 int charIdx = pieceTable->formatCollection()->indexForFormat(charFmt);
346 int cellIdx = pieceTable->formatCollection()->indexForFormat(QTextBlockFormat());
348 QTextTablePrivate *d = table->d_func();
349 d->blockFragmentUpdates = true;
351 d->fragment_start = pieceTable->insertBlock(QTextBeginningOfFrame, pos, cellIdx, charIdx);
352 d->cells.append(d->fragment_start);
355 for (int i = 1; i < rows*cols; ++i) {
356 d->cells.append(pieceTable->insertBlock(QTextBeginningOfFrame, pos, cellIdx, charIdx));
357 // qDebug(" addCell at %d", pos);
361 d->fragment_end = pieceTable->insertBlock(QTextEndOfFrame, pos, cellIdx, charIdx);
362 // qDebug(" addEOR at %d", pos);
365 d->blockFragmentUpdates = false;
368 pieceTable->endEditBlock();
373 struct QFragmentFindHelper
375 inline QFragmentFindHelper(int _pos, const QTextDocumentPrivate::FragmentMap &map)
376 : pos(_pos), fragmentMap(map) {}
378 const QTextDocumentPrivate::FragmentMap &fragmentMap;
381 Q_STATIC_GLOBAL_INLINE_OPERATOR bool operator<(int fragment, const QFragmentFindHelper &helper)
383 return helper.fragmentMap.position(fragment) < helper.pos;
386 Q_STATIC_GLOBAL_INLINE_OPERATOR bool operator<(const QFragmentFindHelper &helper, int fragment)
388 return helper.pos < helper.fragmentMap.position(fragment);
391 int QTextTablePrivate::findCellIndex(int fragment) const
393 QFragmentFindHelper helper(pieceTable->fragmentMap().position(fragment),
394 pieceTable->fragmentMap());
395 QList<int>::ConstIterator it = qBinaryFind(cells.begin(), cells.end(), helper);
396 if (it == cells.end())
398 return it - cells.begin();
401 void QTextTablePrivate::fragmentAdded(QChar type, uint fragment)
404 if (blockFragmentUpdates)
406 if (type == QTextBeginningOfFrame) {
407 Q_ASSERT(cells.indexOf(fragment) == -1);
408 const uint pos = pieceTable->fragmentMap().position(fragment);
409 QFragmentFindHelper helper(pos, pieceTable->fragmentMap());
410 QList<int>::Iterator it = qLowerBound(cells.begin(), cells.end(), helper);
411 cells.insert(it, fragment);
412 if (!fragment_start || pos < pieceTable->fragmentMap().position(fragment_start))
413 fragment_start = fragment;
416 QTextFramePrivate::fragmentAdded(type, fragment);
419 void QTextTablePrivate::fragmentRemoved(QChar type, uint fragment)
422 if (blockFragmentUpdates)
424 if (type == QTextBeginningOfFrame) {
425 Q_ASSERT(cells.indexOf(fragment) != -1);
426 cells.removeAll(fragment);
427 if (fragment_start == fragment && cells.size()) {
428 fragment_start = cells.at(0);
430 if (fragment_start != fragment)
433 QTextFramePrivate::fragmentRemoved(type, fragment);
437 /fn void QTextTablePrivate::update() const
439 This function is usually called when the table is "dirty".
440 It seems to update all kind of table information.
443 void QTextTablePrivate::update() const
445 Q_Q(const QTextTable);
446 nCols = q->format().columns();
447 nRows = (cells.size() + nCols-1)/nCols;
448 // qDebug(">>>> QTextTablePrivate::update, nRows=%d, nCols=%d", nRows, nCols);
450 grid = q_check_ptr((int *)realloc(grid, nRows*nCols*sizeof(int)));
451 memset(grid, 0, nRows*nCols*sizeof(int));
453 QTextDocumentPrivate *p = pieceTable;
454 QTextFormatCollection *c = p->formatCollection();
456 cellIndices.resize(cells.size());
459 for (int i = 0; i < cells.size(); ++i) {
460 int fragment = cells.at(i);
461 QTextCharFormat fmt = c->charFormat(QTextDocumentPrivate::FragmentIterator(&p->fragmentMap(), fragment)->format);
462 int rowspan = fmt.tableCellRowSpan();
463 int colspan = fmt.tableCellColumnSpan();
466 while (cell < nRows*nCols && grid[cell])
471 cellIndices[i] = cell;
473 if (r + rowspan > nRows) {
474 grid = q_check_ptr((int *)realloc(grid, sizeof(int)*(r + rowspan)*nCols));
475 memset(grid + (nRows*nCols), 0, sizeof(int)*(r+rowspan-nRows)*nCols);
479 Q_ASSERT(c + colspan <= nCols);
480 for (int ii = 0; ii < rowspan; ++ii) {
481 for (int jj = 0; jj < colspan; ++jj) {
482 Q_ASSERT(grid[(r+ii)*nCols + c+jj] == 0);
483 grid[(r+ii)*nCols + c+jj] = fragment;
484 // qDebug(" setting cell %d span=%d/%d at %d/%d", fragment, rowspan, colspan, r+ii, c+jj);
488 // qDebug("<<<< end: nRows=%d, nCols=%d", nRows, nCols);
501 \brief The QTextTable class represents a table in a QTextDocument.
504 \ingroup richtext-processing
506 A table is a group of cells ordered into rows and columns. Each table
507 contains at least one row and one column. Each cell contains a block, and
508 is surrounded by a frame.
510 Tables are usually created and inserted into a document with the
511 QTextCursor::insertTable() function.
512 For example, we can insert a table with three rows and two columns at the
513 current cursor position in an editor using the following lines of code:
515 \snippet textdocument-tables/mainwindow.cpp 1
517 \snippet textdocument-tables/mainwindow.cpp 3
519 The table format is either defined when the table is created or changed
520 later with setFormat().
522 The table currently being edited by the cursor is found with
523 QTextCursor::currentTable(). This allows its format or dimensions to be
524 changed after it has been inserted into a document.
526 A table's size can be changed with resize(), or by using
527 insertRows(), insertColumns(), removeRows(), or removeColumns().
528 Use cellAt() to retrieve table cells.
530 The starting and ending positions of table rows can be found by moving
531 a cursor within a table, and using the rowStart() and rowEnd() functions
532 to obtain cursors at the start and end of each row.
534 Rows and columns within a QTextTable can be merged and split using
535 the mergeCells() and splitCell() functions. However, only cells that span multiple
536 rows or columns can be split. (Merging or splitting does not increase or decrease
537 the number of rows and columns.)
539 Note that if you have merged multiple columns and rows into one cell, you will not
540 be able to split the merged cell into new cells spanning over more than one row
541 or column. To be able to split cells spanning over several rows and columns you
542 need to do this over several iterations.
546 \li \inlineimage texttable-split.png Original Table
547 \li Suppose we have a 2x3 table of names and addresses. To merge both
548 columns in the first row we invoke mergeCells() with \a row = 0,
549 \a column = 0, \a numRows = 1 and \a numColumns = 2.
550 \snippet textdocument-texttable/main.cpp 0
553 \li \inlineimage texttable-merge.png
554 \li This gives us the following table. To split the first row of the table
555 back into two cells, we invoke the splitCell() function with \a numRows
557 \snippet textdocument-texttable/main.cpp 1
560 \li \inlineimage texttable-split.png Split Table
561 \li This results in the original table.
569 QTextTable::QTextTable(QTextDocument *doc)
570 : QTextFrame(*new QTextTablePrivate(doc), doc)
578 QTextTable::~QTextTable()
584 \fn QTextTableCell QTextTable::cellAt(int row, int column) const
586 Returns the table cell at the given \a row and \a column in the table.
588 \sa columns(), rows()
590 QTextTableCell QTextTable::cellAt(int row, int col) const
592 Q_D(const QTextTable);
596 if (row < 0 || row >= d->nRows || col < 0 || col >= d->nCols)
597 return QTextTableCell();
599 return QTextTableCell(this, d->grid[row*d->nCols + col]);
605 Returns the table cell that contains the character at the given \a position
608 QTextTableCell QTextTable::cellAt(int position) const
610 Q_D(const QTextTable);
614 uint pos = (uint)position;
615 const QTextDocumentPrivate::FragmentMap &map = d->pieceTable->fragmentMap();
616 if (position < 0 || map.position(d->fragment_start) >= pos || map.position(d->fragment_end) < pos)
617 return QTextTableCell();
619 QFragmentFindHelper helper(position, map);
620 QList<int>::ConstIterator it = qLowerBound(d->cells.begin(), d->cells.end(), helper);
621 if (it != d->cells.begin())
624 return QTextTableCell(this, *it);
628 \fn QTextTableCell QTextTable::cellAt(const QTextCursor &cursor) const
632 Returns the table cell containing the given \a cursor.
634 QTextTableCell QTextTable::cellAt(const QTextCursor &c) const
636 return cellAt(c.position());
640 \fn void QTextTable::resize(int rows, int columns)
642 Resizes the table to contain the required number of \a rows and \a columns.
644 \sa insertRows(), insertColumns(), removeRows(), removeColumns()
646 void QTextTable::resize(int rows, int cols)
652 int nRows = this->rows();
653 int nCols = this->columns();
655 if (rows == nRows && cols == nCols)
658 d->pieceTable->beginEditBlock();
661 insertColumns(nCols, cols - nCols);
662 else if (nCols > cols)
663 removeColumns(cols, nCols - cols);
666 insertRows(nRows, rows-nRows);
667 else if (nRows > rows)
668 removeRows(rows, nRows-rows);
670 d->pieceTable->endEditBlock();
674 \fn void QTextTable::insertRows(int index, int rows)
676 Inserts a number of \a rows before the row with the specified \a index.
678 \sa resize(), insertColumns(), removeRows(), removeColumns(), appendRows(), appendColumns()
680 void QTextTable::insertRows(int pos, int num)
689 if (pos > d->nRows || pos < 0)
692 // qDebug() << "-------- insertRows" << pos << num;
693 QTextDocumentPrivate *p = d->pieceTable;
694 QTextFormatCollection *c = p->formatCollection();
698 int insert_before = 0;
699 if (pos > 0 && pos < d->nRows) {
700 for (int i = 0; i < d->nCols; ++i) {
701 int cell = d->grid[pos*d->nCols + i];
702 if (cell == d->grid[(pos-1)*d->nCols+i]) {
703 // cell spans the insertion place, extend it
704 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
705 QTextCharFormat fmt = c->charFormat(it->format);
706 fmt.setTableCellRowSpan(fmt.tableCellRowSpan() + num);
707 p->setCharFormat(it.position(), 1, fmt);
709 } else if (!insert_before) {
710 insert_before = cell;
714 insert_before = (pos == 0 ? d->grid[0] : d->fragment_end);
716 if (extended < d->nCols) {
717 Q_ASSERT(insert_before);
718 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), insert_before);
719 QTextCharFormat fmt = c->charFormat(it->format);
720 fmt.setTableCellRowSpan(1);
721 fmt.setTableCellColumnSpan(1);
722 Q_ASSERT(fmt.objectIndex() == objectIndex());
723 int pos = it.position();
724 int cfmt = p->formatCollection()->indexForFormat(fmt);
725 int bfmt = p->formatCollection()->indexForFormat(QTextBlockFormat());
726 // qDebug("inserting %d cells, nCols=%d extended=%d", num*(d->nCols-extended), d->nCols, extended);
727 for (int i = 0; i < num*(d->nCols-extended); ++i)
728 p->insertBlock(QTextBeginningOfFrame, pos, bfmt, cfmt, QTextUndoCommand::MoveCursor);
731 // qDebug() << "-------- end insertRows" << pos << num;
736 \fn void QTextTable::insertColumns(int index, int columns)
738 Inserts a number of \a columns before the column with the specified \a index.
740 \sa insertRows(), resize(), removeRows(), removeColumns(), appendRows(), appendColumns()
742 void QTextTable::insertColumns(int pos, int num)
751 if (pos > d->nCols || pos < 0)
754 // qDebug() << "-------- insertCols" << pos << num;
755 QTextDocumentPrivate *p = d->pieceTable;
756 QTextFormatCollection *c = p->formatCollection();
759 QList<int> extendedSpans;
760 for (int i = 0; i < d->nRows; ++i) {
762 if (i == d->nRows - 1 && pos == d->nCols)
763 cell = d->fragment_end;
765 cell = d->grid[i*d->nCols + pos];
766 if (pos > 0 && pos < d->nCols && cell == d->grid[i*d->nCols + pos - 1]) {
767 // cell spans the insertion place, extend it
768 if (!extendedSpans.contains(cell)) {
769 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
770 QTextCharFormat fmt = c->charFormat(it->format);
771 fmt.setTableCellColumnSpan(fmt.tableCellColumnSpan() + num);
772 p->setCharFormat(it.position(), 1, fmt);
774 extendedSpans << cell;
777 /* If the next cell is spanned from the row above, we need to find the right position
779 if (i > 0 && pos < d->nCols && cell == d->grid[(i-1) * d->nCols + pos]) {
780 int gridIndex = i*d->nCols + pos;
781 const int gridEnd = d->nRows * d->nCols - 1;
782 while (gridIndex < gridEnd && cell == d->grid[gridIndex]) {
785 if (gridIndex == gridEnd)
786 cell = d->fragment_end;
788 cell = d->grid[gridIndex];
790 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
791 QTextCharFormat fmt = c->charFormat(it->format);
792 fmt.setTableCellRowSpan(1);
793 fmt.setTableCellColumnSpan(1);
794 Q_ASSERT(fmt.objectIndex() == objectIndex());
795 int position = it.position();
796 int cfmt = p->formatCollection()->indexForFormat(fmt);
797 int bfmt = p->formatCollection()->indexForFormat(QTextBlockFormat());
798 for (int i = 0; i < num; ++i)
799 p->insertBlock(QTextBeginningOfFrame, position, bfmt, cfmt, QTextUndoCommand::MoveCursor);
803 QTextTableFormat tfmt = format();
804 tfmt.setColumns(tfmt.columns()+num);
805 QVector<QTextLength> columnWidths = tfmt.columnWidthConstraints();
806 if (! columnWidths.isEmpty()) {
807 for (int i = num; i > 0; --i)
808 columnWidths.insert(pos, columnWidths[qMax(0, pos-1)]);
810 tfmt.setColumnWidthConstraints (columnWidths);
811 QTextObject::setFormat(tfmt);
813 // qDebug() << "-------- end insertCols" << pos << num;
819 Appends \a count rows at the bottom of the table.
821 \sa insertColumns(), insertRows(), resize(), removeRows(), removeColumns(), appendColumns()
823 void QTextTable::appendRows(int count)
825 insertRows(rows(), count);
830 Appends \a count columns at the right side of the table.
832 \sa insertColumns(), insertRows(), resize(), removeRows(), removeColumns(), appendRows()
834 void QTextTable::appendColumns(int count)
836 insertColumns(columns(), count);
840 \fn void QTextTable::removeRows(int index, int rows)
842 Removes a number of \a rows starting with the row at the specified \a index.
844 \sa insertRows(), insertColumns(), resize(), removeColumns(), appendRows(), appendColumns()
846 void QTextTable::removeRows(int pos, int num)
849 // qDebug() << "-------- removeRows" << pos << num;
851 if (num <= 0 || pos < 0)
857 if (pos+num > d->nRows)
858 num = d->nRows - pos;
860 QTextDocumentPrivate *p = d->pieceTable;
861 QTextFormatCollection *collection = p->formatCollection();
864 // delete whole table?
865 if (pos == 0 && num == d->nRows) {
866 const int pos = p->fragmentMap().position(d->fragment_start);
867 p->remove(pos, p->fragmentMap().position(d->fragment_end) - pos + 1);
872 p->aboutToRemoveCell(cellAt(pos, 0).firstPosition(), cellAt(pos + num - 1, d->nCols - 1).lastPosition());
874 QList<int> touchedCells;
875 for (int r = pos; r < pos + num; ++r) {
876 for (int c = 0; c < d->nCols; ++c) {
877 int cell = d->grid[r*d->nCols + c];
878 if (touchedCells.contains(cell))
880 touchedCells << cell;
881 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
882 QTextCharFormat fmt = collection->charFormat(it->format);
883 int span = fmt.tableCellRowSpan();
885 fmt.setTableCellRowSpan(span - 1);
886 p->setCharFormat(it.position(), 1, fmt);
889 int index = d->cells.indexOf(cell) + 1;
890 int f_end = index < d->cells.size() ? d->cells.at(index) : d->fragment_end;
891 p->remove(it.position(), p->fragmentMap().position(f_end) - it.position());
897 // qDebug() << "-------- end removeRows" << pos << num;
901 \fn void QTextTable::removeColumns(int index, int columns)
903 Removes a number of \a columns starting with the column at the specified
906 \sa insertRows(), insertColumns(), removeRows(), resize(), appendRows(), appendColumns()
908 void QTextTable::removeColumns(int pos, int num)
911 // qDebug() << "-------- removeCols" << pos << num;
913 if (num <= 0 || pos < 0)
919 if (pos + num > d->nCols)
920 pos = d->nCols - num;
922 QTextDocumentPrivate *p = d->pieceTable;
923 QTextFormatCollection *collection = p->formatCollection();
926 // delete whole table?
927 if (pos == 0 && num == d->nCols) {
928 const int pos = p->fragmentMap().position(d->fragment_start);
929 p->remove(pos, p->fragmentMap().position(d->fragment_end) - pos + 1);
934 p->aboutToRemoveCell(cellAt(0, pos).firstPosition(), cellAt(d->nRows - 1, pos + num - 1).lastPosition());
936 QList<int> touchedCells;
937 for (int r = 0; r < d->nRows; ++r) {
938 for (int c = pos; c < pos + num; ++c) {
939 int cell = d->grid[r*d->nCols + c];
940 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
941 QTextCharFormat fmt = collection->charFormat(it->format);
942 int span = fmt.tableCellColumnSpan();
943 if (touchedCells.contains(cell) && span <= 1)
945 touchedCells << cell;
948 fmt.setTableCellColumnSpan(span - 1);
949 p->setCharFormat(it.position(), 1, fmt);
952 int index = d->cells.indexOf(cell) + 1;
953 int f_end = index < d->cells.size() ? d->cells.at(index) : d->fragment_end;
954 p->remove(it.position(), p->fragmentMap().position(f_end) - it.position());
959 QTextTableFormat tfmt = format();
960 tfmt.setColumns(tfmt.columns()-num);
961 QVector<QTextLength> columnWidths = tfmt.columnWidthConstraints();
962 if (columnWidths.count() > pos) {
963 columnWidths.remove(pos, num);
964 tfmt.setColumnWidthConstraints (columnWidths);
966 QTextObject::setFormat(tfmt);
969 // qDebug() << "-------- end removeCols" << pos << num;
975 Merges the cell at the specified \a row and \a column with the adjacent cells
976 into one cell. The new cell will span \a numRows rows and \a numCols columns.
977 If \a numRows or \a numCols is less than the current number of rows or columns
978 the cell spans then this method does nothing.
982 void QTextTable::mergeCells(int row, int column, int numRows, int numCols)
989 QTextDocumentPrivate *p = d->pieceTable;
990 QTextFormatCollection *fc = p->formatCollection();
992 const QTextTableCell cell = cellAt(row, column);
993 if (!cell.isValid() || row != cell.row() || column != cell.column())
996 QTextCharFormat fmt = cell.format();
997 const int rowSpan = fmt.tableCellRowSpan();
998 const int colSpan = fmt.tableCellColumnSpan();
1000 numRows = qMin(numRows, rows() - cell.row());
1001 numCols = qMin(numCols, columns() - cell.column());
1003 // nothing to merge?
1004 if (numRows < rowSpan || numCols < colSpan)
1007 // check the edges of the merge rect to make sure no cell spans the edge
1008 for (int r = row; r < row + numRows; ++r) {
1009 if (cellAt(r, column) == cellAt(r, column - 1))
1011 if (cellAt(r, column + numCols) == cellAt(r, column + numCols - 1))
1015 for (int c = column; c < column + numCols; ++c) {
1016 if (cellAt(row, c) == cellAt(row - 1, c))
1018 if (cellAt(row + numRows, c) == cellAt(row + numRows - 1, c))
1022 p->beginEditBlock();
1024 const int origCellPosition = cell.firstPosition() - 1;
1026 const int cellFragment = d->grid[row * d->nCols + column];
1028 // find the position at which to insert the contents of the merged cells
1029 QFragmentFindHelper helper(origCellPosition, p->fragmentMap());
1030 QList<int>::Iterator it = qBinaryFind(d->cells.begin(), d->cells.end(), helper);
1031 Q_ASSERT(it != d->cells.end());
1032 Q_ASSERT(*it == cellFragment);
1033 const int insertCellIndex = it - d->cells.begin();
1034 int insertFragment = d->cells.value(insertCellIndex + 1, d->fragment_end);
1035 uint insertPos = p->fragmentMap().position(insertFragment);
1037 d->blockFragmentUpdates = true;
1039 bool rowHasText = cell.firstCursorPosition().block().length();
1040 bool needsParagraph = rowHasText && colSpan == numCols;
1042 // find all cells that will be erased by the merge
1043 for (int r = row; r < row + numRows; ++r) {
1044 int firstColumn = r < row + rowSpan ? column + colSpan : column;
1046 // don't recompute the cell index for the first row
1047 int firstCellIndex = r == row ? insertCellIndex + 1 : -1;
1048 int cellIndex = firstCellIndex;
1050 for (int c = firstColumn; c < column + numCols; ++c) {
1051 const int fragment = d->grid[r * d->nCols + c];
1054 if (fragment == cellFragment)
1057 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), fragment);
1058 uint pos = it.position();
1060 if (firstCellIndex == -1) {
1061 QFragmentFindHelper helper(pos, p->fragmentMap());
1062 QList<int>::Iterator it = qBinaryFind(d->cells.begin(), d->cells.end(), helper);
1063 Q_ASSERT(it != d->cells.end());
1064 Q_ASSERT(*it == fragment);
1065 firstCellIndex = cellIndex = it - d->cells.begin();
1070 QTextCharFormat fmt = fc->charFormat(it->format);
1072 const int cellRowSpan = fmt.tableCellRowSpan();
1073 const int cellColSpan = fmt.tableCellColumnSpan();
1075 // update the grid for this cell
1076 for (int i = r; i < r + cellRowSpan; ++i)
1077 for (int j = c; j < c + cellColSpan; ++j)
1078 d->grid[i * d->nCols + j] = cellFragment;
1080 // erase the cell marker
1083 const int nextFragment = d->cells.value(cellIndex, d->fragment_end);
1084 const uint nextPos = p->fragmentMap().position(nextFragment);
1086 Q_ASSERT(nextPos >= pos);
1088 // merge the contents of the cell (if not empty)
1089 if (nextPos > pos) {
1090 if (needsParagraph) {
1091 needsParagraph = false;
1092 QTextCursor(p, insertPos++).insertBlock();
1093 p->move(pos + 1, insertPos, nextPos - pos);
1094 } else if (rowHasText) {
1095 QTextCursor(p, insertPos++).insertText(QLatin1String(" "));
1096 p->move(pos + 1, insertPos, nextPos - pos);
1098 p->move(pos, insertPos, nextPos - pos);
1101 insertPos += nextPos - pos;
1107 needsParagraph = true;
1111 // erase cells from last row
1112 if (firstCellIndex >= 0) {
1113 d->cellIndices.remove(firstCellIndex, cellIndex - firstCellIndex);
1114 d->cells.erase(d->cells.begin() + firstCellIndex, d->cells.begin() + cellIndex);
1118 d->fragment_start = d->cells.first();
1120 fmt.setTableCellRowSpan(numRows);
1121 fmt.setTableCellColumnSpan(numCols);
1122 p->setCharFormat(origCellPosition, 1, fmt);
1124 d->blockFragmentUpdates = false;
1134 Merges the cells selected by the provided \a cursor.
1138 void QTextTable::mergeCells(const QTextCursor &cursor)
1140 if (!cursor.hasComplexSelection())
1143 int firstRow, numRows, firstColumn, numColumns;
1144 cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
1145 mergeCells(firstRow, firstColumn, numRows, numColumns);
1151 Splits the specified cell at \a row and \a column into an array of multiple
1152 cells with dimensions specified by \a numRows and \a numCols.
1154 \note It is only possible to split cells that span multiple rows or columns, such as rows
1155 that have been merged using mergeCells().
1159 void QTextTable::splitCell(int row, int column, int numRows, int numCols)
1166 QTextDocumentPrivate *p = d->pieceTable;
1167 QTextFormatCollection *c = p->formatCollection();
1169 const QTextTableCell cell = cellAt(row, column);
1170 if (!cell.isValid())
1173 column = cell.column();
1175 QTextCharFormat fmt = cell.format();
1176 const int rowSpan = fmt.tableCellRowSpan();
1177 const int colSpan = fmt.tableCellColumnSpan();
1179 // nothing to split?
1180 if (numRows > rowSpan || numCols > colSpan)
1183 p->beginEditBlock();
1185 const int origCellPosition = cell.firstPosition() - 1;
1187 QVarLengthArray<int> rowPositions(rowSpan);
1189 rowPositions[0] = cell.lastPosition();
1191 for (int r = row + 1; r < row + rowSpan; ++r) {
1192 // find the cell before which to insert the new cell markers
1193 int gridIndex = r * d->nCols + column;
1194 QVector<int>::iterator it = qUpperBound(d->cellIndices.begin(), d->cellIndices.end(), gridIndex);
1195 int cellIndex = it - d->cellIndices.begin();
1196 int fragment = d->cells.value(cellIndex, d->fragment_end);
1197 rowPositions[r - row] = p->fragmentMap().position(fragment);
1200 fmt.setTableCellColumnSpan(1);
1201 fmt.setTableCellRowSpan(1);
1202 const int fmtIndex = c->indexForFormat(fmt);
1203 const int blockIndex = p->blockMap().find(cell.lastPosition())->format;
1205 int insertAdjustement = 0;
1206 for (int i = 0; i < numRows; ++i) {
1207 for (int c = 0; c < colSpan - numCols; ++c)
1208 p->insertBlock(QTextBeginningOfFrame, rowPositions[i] + insertAdjustement + c, blockIndex, fmtIndex);
1209 insertAdjustement += colSpan - numCols;
1212 for (int i = numRows; i < rowSpan; ++i) {
1213 for (int c = 0; c < colSpan; ++c)
1214 p->insertBlock(QTextBeginningOfFrame, rowPositions[i] + insertAdjustement + c, blockIndex, fmtIndex);
1215 insertAdjustement += colSpan;
1218 fmt.setTableCellRowSpan(numRows);
1219 fmt.setTableCellColumnSpan(numCols);
1220 p->setCharFormat(origCellPosition, 1, fmt);
1226 Returns the number of rows in the table.
1230 int QTextTable::rows() const
1232 Q_D(const QTextTable);
1240 Returns the number of columns in the table.
1244 int QTextTable::columns() const
1246 Q_D(const QTextTable);
1254 void QTextTable::mergeCells(const QTextCursor &selection)
1260 \fn QTextCursor QTextTable::rowStart(const QTextCursor &cursor) const
1262 Returns a cursor pointing to the start of the row that contains the
1267 QTextCursor QTextTable::rowStart(const QTextCursor &c) const
1269 Q_D(const QTextTable);
1270 QTextTableCell cell = cellAt(c);
1271 if (!cell.isValid())
1272 return QTextCursor();
1274 int row = cell.row();
1275 QTextDocumentPrivate *p = d->pieceTable;
1276 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), d->grid[row*d->nCols]);
1277 return QTextCursor(p, it.position());
1281 \fn QTextCursor QTextTable::rowEnd(const QTextCursor &cursor) const
1283 Returns a cursor pointing to the end of the row that contains the given
1288 QTextCursor QTextTable::rowEnd(const QTextCursor &c) const
1290 Q_D(const QTextTable);
1291 QTextTableCell cell = cellAt(c);
1292 if (!cell.isValid())
1293 return QTextCursor();
1295 int row = cell.row() + 1;
1296 int fragment = row < d->nRows ? d->grid[row*d->nCols] : d->fragment_end;
1297 QTextDocumentPrivate *p = d->pieceTable;
1298 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), fragment);
1299 return QTextCursor(p, it.position() - 1);
1303 \fn void QTextTable::setFormat(const QTextTableFormat &format)
1305 Sets the table's \a format.
1309 void QTextTable::setFormat(const QTextTableFormat &format)
1311 QTextTableFormat fmt = format;
1312 // don't try to change the number of table columns from here
1313 fmt.setColumns(columns());
1314 QTextObject::setFormat(fmt);
1318 \fn QTextTableFormat QTextTable::format() const
1320 Returns the table's format.