1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
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
61 \ingroup richtext-processing
63 Table cells are pieces of document structure that belong to a table.
64 The table orders cells into particular rows and columns; cells can
65 also span multiple columns and rows.
67 Cells are usually created when a table is inserted into a document with
68 QTextCursor::insertTable(), but they are also created and destroyed when
71 Cells contain information about their location in a table; you can
72 obtain the row() and column() numbers of a cell, and its rowSpan()
75 The format() of a cell describes the default character format of its
76 contents. The firstCursorPosition() and lastCursorPosition() functions
77 are used to obtain the extent of the cell in the document.
79 \sa QTextTable QTextTableFormat
83 \fn QTextTableCell::QTextTableCell()
85 Constructs an invalid table cell.
91 \fn QTextTableCell::QTextTableCell(const QTextTableCell &other)
93 Copy constructor. Creates a new QTextTableCell object based on the
98 \fn QTextTableCell& QTextTableCell::operator=(const QTextTableCell &other)
100 Assigns the \a other table cell to this table cell.
106 Sets the cell's character format to \a format. This can for example be used to change
107 the background color of the entire cell:
109 QTextTableCell cell = table->cellAt(2, 3);
110 QTextCharFormat format = cell.format();
111 format.setBackground(Qt::blue);
112 cell.setFormat(format);
114 Note that the cell's row or column span cannot be changed through this function. You have
115 to use QTextTable::mergeCells and QTextTable::splitCell instead.
119 void QTextTableCell::setFormat(const QTextCharFormat &format)
121 QTextCharFormat fmt = format;
122 fmt.clearProperty(QTextFormat::ObjectIndex);
123 fmt.setObjectType(QTextFormat::TableCellObject);
124 QTextDocumentPrivate *p = table->docHandle();
125 QTextDocumentPrivate::FragmentIterator frag(&p->fragmentMap(), fragment);
127 QTextFormatCollection *c = p->formatCollection();
128 QTextCharFormat oldFormat = c->charFormat(frag->format);
129 fmt.setTableCellRowSpan(oldFormat.tableCellRowSpan());
130 fmt.setTableCellColumnSpan(oldFormat.tableCellColumnSpan());
132 p->setCharFormat(frag.position(), 1, fmt, QTextDocumentPrivate::SetFormatAndPreserveObjectIndices);
136 Returns the cell's character format.
138 QTextCharFormat QTextTableCell::format() const
140 QTextDocumentPrivate *p = table->docHandle();
141 QTextFormatCollection *c = p->formatCollection();
143 QTextCharFormat fmt = c->charFormat(tableCellFormatIndex());
144 fmt.setObjectType(QTextFormat::TableCellObject);
151 Returns the index of the tableCell's format in the document's internal list of formats.
153 \sa QTextDocument::allFormats()
155 int QTextTableCell::tableCellFormatIndex() const
157 QTextDocumentPrivate *p = table->docHandle();
158 return QTextDocumentPrivate::FragmentIterator(&p->fragmentMap(), fragment)->format;
162 Returns the number of the row in the table that contains this cell.
166 int QTextTableCell::row() const
168 const QTextTablePrivate *tp = table->d_func();
172 int idx = tp->findCellIndex(fragment);
175 return tp->cellIndices.at(idx) / tp->nCols;
179 Returns the number of the column in the table that contains this cell.
183 int QTextTableCell::column() const
185 const QTextTablePrivate *tp = table->d_func();
189 int idx = tp->findCellIndex(fragment);
192 return tp->cellIndices.at(idx) % tp->nCols;
196 Returns the number of rows this cell spans. The default is 1.
200 int QTextTableCell::rowSpan() const
202 return format().tableCellRowSpan();
206 Returns the number of columns this cell spans. The default is 1.
210 int QTextTableCell::columnSpan() const
212 return format().tableCellColumnSpan();
216 \fn bool QTextTableCell::isValid() const
218 Returns true if this is a valid table cell; otherwise returns
224 Returns the first valid cursor position in this cell.
226 \sa lastCursorPosition()
228 QTextCursor QTextTableCell::firstCursorPosition() const
230 return QTextCursor(table->d_func()->pieceTable, firstPosition());
234 Returns the last valid cursor position in this cell.
236 \sa firstCursorPosition()
238 QTextCursor QTextTableCell::lastCursorPosition() const
240 return QTextCursor(table->d_func()->pieceTable, lastPosition());
247 Returns the first valid position in the document occupied by this cell.
249 int QTextTableCell::firstPosition() const
251 QTextDocumentPrivate *p = table->docHandle();
252 return p->fragmentMap().position(fragment) + 1;
258 Returns the last valid position in the document occupied by this cell.
260 int QTextTableCell::lastPosition() const
262 QTextDocumentPrivate *p = table->docHandle();
263 const QTextTablePrivate *td = table->d_func();
264 int index = table->d_func()->findCellIndex(fragment);
267 f = td->cells.value(index + 1, td->fragment_end);
269 f = td->fragment_end;
270 return p->fragmentMap().position(f);
275 Returns a frame iterator pointing to the beginning of the table's cell.
279 QTextFrame::iterator QTextTableCell::begin() const
281 QTextDocumentPrivate *p = table->docHandle();
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);
288 Returns a frame iterator pointing to the end of the table's cell.
292 QTextFrame::iterator QTextTableCell::end() const
294 QTextDocumentPrivate *p = table->docHandle();
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);
302 \fn QTextCursor QTextTableCell::operator==(const QTextTableCell &other) const
304 Returns true if this cell object and the \a other cell object
305 describe the same cell; otherwise returns false.
309 \fn QTextCursor QTextTableCell::operator!=(const QTextTableCell &other) const
311 Returns true if this cell object and the \a other cell object
312 describe different cells; otherwise returns false.
316 \fn QTextTableCell::~QTextTableCell()
318 Destroys the table cell.
321 QTextTablePrivate::~QTextTablePrivate()
328 QTextTable *QTextTablePrivate::createTable(QTextDocumentPrivate *pieceTable, int pos, int rows, int cols, const QTextTableFormat &tableFormat)
330 QTextTableFormat fmt = tableFormat;
331 fmt.setColumns(cols);
332 QTextTable *table = qobject_cast<QTextTable *>(pieceTable->createObject(fmt));
335 pieceTable->beginEditBlock();
337 // qDebug("---> createTable: rows=%d, cols=%d at %d", rows, cols, pos);
338 // add block after table
339 QTextCharFormat charFmt;
340 charFmt.setObjectIndex(table->objectIndex());
341 charFmt.setObjectType(QTextFormat::TableCellObject);
344 int charIdx = pieceTable->formatCollection()->indexForFormat(charFmt);
345 int cellIdx = pieceTable->formatCollection()->indexForFormat(QTextBlockFormat());
347 QTextTablePrivate *d = table->d_func();
348 d->blockFragmentUpdates = true;
350 d->fragment_start = pieceTable->insertBlock(QTextBeginningOfFrame, pos, cellIdx, charIdx);
351 d->cells.append(d->fragment_start);
354 for (int i = 1; i < rows*cols; ++i) {
355 d->cells.append(pieceTable->insertBlock(QTextBeginningOfFrame, pos, cellIdx, charIdx));
356 // qDebug(" addCell at %d", pos);
360 d->fragment_end = pieceTable->insertBlock(QTextEndOfFrame, pos, cellIdx, charIdx);
361 // qDebug(" addEOR at %d", pos);
364 d->blockFragmentUpdates = false;
367 pieceTable->endEditBlock();
372 struct QFragmentFindHelper
374 inline QFragmentFindHelper(int _pos, const QTextDocumentPrivate::FragmentMap &map)
375 : pos(_pos), fragmentMap(map) {}
377 const QTextDocumentPrivate::FragmentMap &fragmentMap;
380 Q_STATIC_GLOBAL_INLINE_OPERATOR bool operator<(int fragment, const QFragmentFindHelper &helper)
382 return helper.fragmentMap.position(fragment) < helper.pos;
385 Q_STATIC_GLOBAL_INLINE_OPERATOR bool operator<(const QFragmentFindHelper &helper, int fragment)
387 return helper.pos < helper.fragmentMap.position(fragment);
390 int QTextTablePrivate::findCellIndex(int fragment) const
392 QFragmentFindHelper helper(pieceTable->fragmentMap().position(fragment),
393 pieceTable->fragmentMap());
394 QList<int>::ConstIterator it = qBinaryFind(cells.begin(), cells.end(), helper);
395 if (it == cells.end())
397 return it - cells.begin();
400 void QTextTablePrivate::fragmentAdded(const QChar &type, uint fragment)
403 if (blockFragmentUpdates)
405 if (type == QTextBeginningOfFrame) {
406 Q_ASSERT(cells.indexOf(fragment) == -1);
407 const uint pos = pieceTable->fragmentMap().position(fragment);
408 QFragmentFindHelper helper(pos, pieceTable->fragmentMap());
409 QList<int>::Iterator it = qLowerBound(cells.begin(), cells.end(), helper);
410 cells.insert(it, fragment);
411 if (!fragment_start || pos < pieceTable->fragmentMap().position(fragment_start))
412 fragment_start = fragment;
415 QTextFramePrivate::fragmentAdded(type, fragment);
418 void QTextTablePrivate::fragmentRemoved(const QChar &type, uint fragment)
421 if (blockFragmentUpdates)
423 if (type == QTextBeginningOfFrame) {
424 Q_ASSERT(cells.indexOf(fragment) != -1);
425 cells.removeAll(fragment);
426 if (fragment_start == fragment && cells.size()) {
427 fragment_start = cells.at(0);
429 if (fragment_start != fragment)
432 QTextFramePrivate::fragmentRemoved(type, fragment);
436 /fn void QTextTablePrivate::update() const
438 This function is usually called when the table is "dirty".
439 It seems to update all kind of table information.
442 void QTextTablePrivate::update() const
444 Q_Q(const QTextTable);
445 nCols = q->format().columns();
446 nRows = (cells.size() + nCols-1)/nCols;
447 // qDebug(">>>> QTextTablePrivate::update, nRows=%d, nCols=%d", nRows, nCols);
449 grid = q_check_ptr((int *)realloc(grid, nRows*nCols*sizeof(int)));
450 memset(grid, 0, nRows*nCols*sizeof(int));
452 QTextDocumentPrivate *p = pieceTable;
453 QTextFormatCollection *c = p->formatCollection();
455 cellIndices.resize(cells.size());
458 for (int i = 0; i < cells.size(); ++i) {
459 int fragment = cells.at(i);
460 QTextCharFormat fmt = c->charFormat(QTextDocumentPrivate::FragmentIterator(&p->fragmentMap(), fragment)->format);
461 int rowspan = fmt.tableCellRowSpan();
462 int colspan = fmt.tableCellColumnSpan();
465 while (cell < nRows*nCols && grid[cell])
470 cellIndices[i] = cell;
472 if (r + rowspan > nRows) {
473 grid = q_check_ptr((int *)realloc(grid, sizeof(int)*(r + rowspan)*nCols));
474 memset(grid + (nRows*nCols), 0, sizeof(int)*(r+rowspan-nRows)*nCols);
478 Q_ASSERT(c + colspan <= nCols);
479 for (int ii = 0; ii < rowspan; ++ii) {
480 for (int jj = 0; jj < colspan; ++jj) {
481 Q_ASSERT(grid[(r+ii)*nCols + c+jj] == 0);
482 grid[(r+ii)*nCols + c+jj] = fragment;
483 // qDebug(" setting cell %d span=%d/%d at %d/%d", fragment, rowspan, colspan, r+ii, c+jj);
487 // qDebug("<<<< end: nRows=%d, nCols=%d", nRows, nCols);
500 \brief The QTextTable class represents a table in a QTextDocument.
502 \ingroup richtext-processing
504 A table is a group of cells ordered into rows and columns. Each table
505 contains at least one row and one column. Each cell contains a block, and
506 is surrounded by a frame.
508 Tables are usually created and inserted into a document with the
509 QTextCursor::insertTable() function.
510 For example, we can insert a table with three rows and two columns at the
511 current cursor position in an editor using the following lines of code:
513 \snippet doc/src/snippets/textdocument-tables/mainwindow.cpp 1
515 \snippet doc/src/snippets/textdocument-tables/mainwindow.cpp 3
517 The table format is either defined when the table is created or changed
518 later with setFormat().
520 The table currently being edited by the cursor is found with
521 QTextCursor::currentTable(). This allows its format or dimensions to be
522 changed after it has been inserted into a document.
524 A table's size can be changed with resize(), or by using
525 insertRows(), insertColumns(), removeRows(), or removeColumns().
526 Use cellAt() to retrieve table cells.
528 The starting and ending positions of table rows can be found by moving
529 a cursor within a table, and using the rowStart() and rowEnd() functions
530 to obtain cursors at the start and end of each row.
532 Rows and columns within a QTextTable can be merged and split using
533 the mergeCells() and splitCell() functions. However, only cells that span multiple
534 rows or columns can be split. (Merging or splitting does not increase or decrease
535 the number of rows and columns.)
537 Note that if you have merged multiple columns and rows into one cell, you will not
538 be able to split the merged cell into new cells spanning over more than one row
539 or column. To be able to split cells spanning over several rows and columns you
540 need to do this over several iterations.
544 \o \inlineimage texttable-split.png Original Table
545 \o Suppose we have a 2x3 table of names and addresses. To merge both
546 columns in the first row we invoke mergeCells() with \a row = 0,
547 \a column = 0, \a numRows = 1 and \a numColumns = 2.
548 \snippet doc/src/snippets/textdocument-texttable/main.cpp 0
551 \o \inlineimage texttable-merge.png
552 \o This gives us the following table. To split the first row of the table
553 back into two cells, we invoke the splitCell() function with \a numRows
555 \snippet doc/src/snippets/textdocument-texttable/main.cpp 1
558 \o \inlineimage texttable-split.png Split Table
559 \o This results in the original table.
567 QTextTable::QTextTable(QTextDocument *doc)
568 : QTextFrame(*new QTextTablePrivate(doc), doc)
576 QTextTable::~QTextTable()
582 \fn QTextTableCell QTextTable::cellAt(int row, int column) const
584 Returns the table cell at the given \a row and \a column in the table.
588 QTextTableCell QTextTable::cellAt(int row, int col) const
590 Q_D(const QTextTable);
594 if (row < 0 || row >= d->nRows || col < 0 || col >= d->nCols)
595 return QTextTableCell();
597 return QTextTableCell(this, d->grid[row*d->nCols + col]);
603 Returns the table cell that contains the character at the given \a position
606 QTextTableCell QTextTable::cellAt(int position) const
608 Q_D(const QTextTable);
612 uint pos = (uint)position;
613 const QTextDocumentPrivate::FragmentMap &map = d->pieceTable->fragmentMap();
614 if (position < 0 || map.position(d->fragment_start) >= pos || map.position(d->fragment_end) < pos)
615 return QTextTableCell();
617 QFragmentFindHelper helper(position, map);
618 QList<int>::ConstIterator it = qLowerBound(d->cells.begin(), d->cells.end(), helper);
619 if (it != d->cells.begin())
622 return QTextTableCell(this, *it);
626 \fn QTextTableCell QTextTable::cellAt(const QTextCursor &cursor) const
630 Returns the table cell containing the given \a cursor.
632 QTextTableCell QTextTable::cellAt(const QTextCursor &c) const
634 return cellAt(c.position());
638 \fn void QTextTable::resize(int rows, int columns)
640 Resizes the table to contain the required number of \a rows and \a columns.
642 \sa insertRows() insertColumns() removeRows() removeColumns()
644 void QTextTable::resize(int rows, int cols)
650 int nRows = this->rows();
651 int nCols = this->columns();
653 if (rows == nRows && cols == nCols)
656 d->pieceTable->beginEditBlock();
659 insertColumns(nCols, cols - nCols);
660 else if (nCols > cols)
661 removeColumns(cols, nCols - cols);
664 insertRows(nRows, rows-nRows);
665 else if (nRows > rows)
666 removeRows(rows, nRows-rows);
668 d->pieceTable->endEditBlock();
672 \fn void QTextTable::insertRows(int index, int rows)
674 Inserts a number of \a rows before the row with the specified \a index.
676 \sa resize() insertColumns() removeRows() removeColumns() appendRows() appendColumns()
678 void QTextTable::insertRows(int pos, int num)
687 if (pos > d->nRows || pos < 0)
690 // qDebug() << "-------- insertRows" << pos << num;
691 QTextDocumentPrivate *p = d->pieceTable;
692 QTextFormatCollection *c = p->formatCollection();
696 int insert_before = 0;
697 if (pos > 0 && pos < d->nRows) {
698 for (int i = 0; i < d->nCols; ++i) {
699 int cell = d->grid[pos*d->nCols + i];
700 if (cell == d->grid[(pos-1)*d->nCols+i]) {
701 // cell spans the insertion place, extend it
702 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
703 QTextCharFormat fmt = c->charFormat(it->format);
704 fmt.setTableCellRowSpan(fmt.tableCellRowSpan() + num);
705 p->setCharFormat(it.position(), 1, fmt);
707 } else if (!insert_before) {
708 insert_before = cell;
712 insert_before = (pos == 0 ? d->grid[0] : d->fragment_end);
714 if (extended < d->nCols) {
715 Q_ASSERT(insert_before);
716 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), insert_before);
717 QTextCharFormat fmt = c->charFormat(it->format);
718 fmt.setTableCellRowSpan(1);
719 fmt.setTableCellColumnSpan(1);
720 Q_ASSERT(fmt.objectIndex() == objectIndex());
721 int pos = it.position();
722 int cfmt = p->formatCollection()->indexForFormat(fmt);
723 int bfmt = p->formatCollection()->indexForFormat(QTextBlockFormat());
724 // qDebug("inserting %d cells, nCols=%d extended=%d", num*(d->nCols-extended), d->nCols, extended);
725 for (int i = 0; i < num*(d->nCols-extended); ++i)
726 p->insertBlock(QTextBeginningOfFrame, pos, bfmt, cfmt, QTextUndoCommand::MoveCursor);
729 // qDebug() << "-------- end insertRows" << pos << num;
734 \fn void QTextTable::insertColumns(int index, int columns)
736 Inserts a number of \a columns before the column with the specified \a index.
738 \sa insertRows() resize() removeRows() removeColumns() appendRows() appendColumns()
740 void QTextTable::insertColumns(int pos, int num)
749 if (pos > d->nCols || pos < 0)
752 // qDebug() << "-------- insertCols" << pos << num;
753 QTextDocumentPrivate *p = d->pieceTable;
754 QTextFormatCollection *c = p->formatCollection();
757 QList<int> extendedSpans;
758 for (int i = 0; i < d->nRows; ++i) {
760 if (i == d->nRows - 1 && pos == d->nCols)
761 cell = d->fragment_end;
763 cell = d->grid[i*d->nCols + pos];
764 if (pos > 0 && pos < d->nCols && cell == d->grid[i*d->nCols + pos - 1]) {
765 // cell spans the insertion place, extend it
766 if (!extendedSpans.contains(cell)) {
767 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
768 QTextCharFormat fmt = c->charFormat(it->format);
769 fmt.setTableCellColumnSpan(fmt.tableCellColumnSpan() + num);
770 p->setCharFormat(it.position(), 1, fmt);
772 extendedSpans << cell;
775 /* If the next cell is spanned from the row above, we need to find the right position
777 if (i > 0 && pos < d->nCols && cell == d->grid[(i-1) * d->nCols + pos]) {
778 int gridIndex = i*d->nCols + pos;
779 const int gridEnd = d->nRows * d->nCols - 1;
780 while (gridIndex < gridEnd && cell == d->grid[gridIndex]) {
783 if (gridIndex == gridEnd)
784 cell = d->fragment_end;
786 cell = d->grid[gridIndex];
788 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
789 QTextCharFormat fmt = c->charFormat(it->format);
790 fmt.setTableCellRowSpan(1);
791 fmt.setTableCellColumnSpan(1);
792 Q_ASSERT(fmt.objectIndex() == objectIndex());
793 int position = it.position();
794 int cfmt = p->formatCollection()->indexForFormat(fmt);
795 int bfmt = p->formatCollection()->indexForFormat(QTextBlockFormat());
796 for (int i = 0; i < num; ++i)
797 p->insertBlock(QTextBeginningOfFrame, position, bfmt, cfmt, QTextUndoCommand::MoveCursor);
801 QTextTableFormat tfmt = format();
802 tfmt.setColumns(tfmt.columns()+num);
803 QVector<QTextLength> columnWidths = tfmt.columnWidthConstraints();
804 if (! columnWidths.isEmpty()) {
805 for (int i = num; i > 0; --i)
806 columnWidths.insert(pos, columnWidths[qMax(0, pos-1)]);
808 tfmt.setColumnWidthConstraints (columnWidths);
809 QTextObject::setFormat(tfmt);
811 // qDebug() << "-------- end insertCols" << pos << num;
817 Appends \a count rows at the bottom of the table.
819 \sa insertColumns() insertRows() resize() removeRows() removeColumns() appendColumns()
821 void QTextTable::appendRows(int count)
823 insertRows(rows(), count);
828 Appends \a count columns at the right side of the table.
830 \sa insertColumns() insertRows() resize() removeRows() removeColumns() appendRows()
832 void QTextTable::appendColumns(int count)
834 insertColumns(columns(), count);
838 \fn void QTextTable::removeRows(int index, int rows)
840 Removes a number of \a rows starting with the row at the specified \a index.
842 \sa insertRows(), insertColumns(), resize(), removeColumns() appendRows() appendColumns()
844 void QTextTable::removeRows(int pos, int num)
847 // qDebug() << "-------- removeRows" << pos << num;
849 if (num <= 0 || pos < 0)
855 if (pos+num > d->nRows)
856 num = d->nRows - pos;
858 QTextDocumentPrivate *p = d->pieceTable;
859 QTextFormatCollection *collection = p->formatCollection();
862 // delete whole table?
863 if (pos == 0 && num == d->nRows) {
864 const int pos = p->fragmentMap().position(d->fragment_start);
865 p->remove(pos, p->fragmentMap().position(d->fragment_end) - pos + 1);
870 p->aboutToRemoveCell(cellAt(pos, 0).firstPosition(), cellAt(pos + num - 1, d->nCols - 1).lastPosition());
872 QList<int> touchedCells;
873 for (int r = pos; r < pos + num; ++r) {
874 for (int c = 0; c < d->nCols; ++c) {
875 int cell = d->grid[r*d->nCols + c];
876 if (touchedCells.contains(cell))
878 touchedCells << cell;
879 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
880 QTextCharFormat fmt = collection->charFormat(it->format);
881 int span = fmt.tableCellRowSpan();
883 fmt.setTableCellRowSpan(span - 1);
884 p->setCharFormat(it.position(), 1, fmt);
887 int index = d->cells.indexOf(cell) + 1;
888 int f_end = index < d->cells.size() ? d->cells.at(index) : d->fragment_end;
889 p->remove(it.position(), p->fragmentMap().position(f_end) - it.position());
895 // qDebug() << "-------- end removeRows" << pos << num;
899 \fn void QTextTable::removeColumns(int index, int columns)
901 Removes a number of \a columns starting with the column at the specified
904 \sa insertRows() insertColumns() removeRows() resize() appendRows() appendColumns()
906 void QTextTable::removeColumns(int pos, int num)
909 // qDebug() << "-------- removeCols" << pos << num;
911 if (num <= 0 || pos < 0)
917 if (pos + num > d->nCols)
918 pos = d->nCols - num;
920 QTextDocumentPrivate *p = d->pieceTable;
921 QTextFormatCollection *collection = p->formatCollection();
924 // delete whole table?
925 if (pos == 0 && num == d->nCols) {
926 const int pos = p->fragmentMap().position(d->fragment_start);
927 p->remove(pos, p->fragmentMap().position(d->fragment_end) - pos + 1);
932 p->aboutToRemoveCell(cellAt(0, pos).firstPosition(), cellAt(d->nRows - 1, pos + num - 1).lastPosition());
934 QList<int> touchedCells;
935 for (int r = 0; r < d->nRows; ++r) {
936 for (int c = pos; c < pos + num; ++c) {
937 int cell = d->grid[r*d->nCols + c];
938 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
939 QTextCharFormat fmt = collection->charFormat(it->format);
940 int span = fmt.tableCellColumnSpan();
941 if (touchedCells.contains(cell) && span <= 1)
943 touchedCells << cell;
946 fmt.setTableCellColumnSpan(span - 1);
947 p->setCharFormat(it.position(), 1, fmt);
950 int index = d->cells.indexOf(cell) + 1;
951 int f_end = index < d->cells.size() ? d->cells.at(index) : d->fragment_end;
952 p->remove(it.position(), p->fragmentMap().position(f_end) - it.position());
957 QTextTableFormat tfmt = format();
958 tfmt.setColumns(tfmt.columns()-num);
959 QVector<QTextLength> columnWidths = tfmt.columnWidthConstraints();
960 if (columnWidths.count() > pos) {
961 columnWidths.remove(pos, num);
962 tfmt.setColumnWidthConstraints (columnWidths);
964 QTextObject::setFormat(tfmt);
967 // qDebug() << "-------- end removeCols" << pos << num;
973 Merges the cell at the specified \a row and \a column with the adjacent cells
974 into one cell. The new cell will span \a numRows rows and \a numCols columns.
975 If \a numRows or \a numCols is less than the current number of rows or columns
976 the cell spans then this method does nothing.
980 void QTextTable::mergeCells(int row, int column, int numRows, int numCols)
987 QTextDocumentPrivate *p = d->pieceTable;
988 QTextFormatCollection *fc = p->formatCollection();
990 const QTextTableCell cell = cellAt(row, column);
991 if (!cell.isValid() || row != cell.row() || column != cell.column())
994 QTextCharFormat fmt = cell.format();
995 const int rowSpan = fmt.tableCellRowSpan();
996 const int colSpan = fmt.tableCellColumnSpan();
998 numRows = qMin(numRows, rows() - cell.row());
999 numCols = qMin(numCols, columns() - cell.column());
1001 // nothing to merge?
1002 if (numRows < rowSpan || numCols < colSpan)
1005 // check the edges of the merge rect to make sure no cell spans the edge
1006 for (int r = row; r < row + numRows; ++r) {
1007 if (cellAt(r, column) == cellAt(r, column - 1))
1009 if (cellAt(r, column + numCols) == cellAt(r, column + numCols - 1))
1013 for (int c = column; c < column + numCols; ++c) {
1014 if (cellAt(row, c) == cellAt(row - 1, c))
1016 if (cellAt(row + numRows, c) == cellAt(row + numRows - 1, c))
1020 p->beginEditBlock();
1022 const int origCellPosition = cell.firstPosition() - 1;
1024 const int cellFragment = d->grid[row * d->nCols + column];
1026 // find the position at which to insert the contents of the merged cells
1027 QFragmentFindHelper helper(origCellPosition, p->fragmentMap());
1028 QList<int>::Iterator it = qBinaryFind(d->cells.begin(), d->cells.end(), helper);
1029 Q_ASSERT(it != d->cells.end());
1030 Q_ASSERT(*it == cellFragment);
1031 const int insertCellIndex = it - d->cells.begin();
1032 int insertFragment = d->cells.value(insertCellIndex + 1, d->fragment_end);
1033 uint insertPos = p->fragmentMap().position(insertFragment);
1035 d->blockFragmentUpdates = true;
1037 bool rowHasText = cell.firstCursorPosition().block().length();
1038 bool needsParagraph = rowHasText && colSpan == numCols;
1040 // find all cells that will be erased by the merge
1041 for (int r = row; r < row + numRows; ++r) {
1042 int firstColumn = r < row + rowSpan ? column + colSpan : column;
1044 // don't recompute the cell index for the first row
1045 int firstCellIndex = r == row ? insertCellIndex + 1 : -1;
1046 int cellIndex = firstCellIndex;
1048 for (int c = firstColumn; c < column + numCols; ++c) {
1049 const int fragment = d->grid[r * d->nCols + c];
1052 if (fragment == cellFragment)
1055 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), fragment);
1056 uint pos = it.position();
1058 if (firstCellIndex == -1) {
1059 QFragmentFindHelper helper(pos, p->fragmentMap());
1060 QList<int>::Iterator it = qBinaryFind(d->cells.begin(), d->cells.end(), helper);
1061 Q_ASSERT(it != d->cells.end());
1062 Q_ASSERT(*it == fragment);
1063 firstCellIndex = cellIndex = it - d->cells.begin();
1068 QTextCharFormat fmt = fc->charFormat(it->format);
1070 const int cellRowSpan = fmt.tableCellRowSpan();
1071 const int cellColSpan = fmt.tableCellColumnSpan();
1073 // update the grid for this cell
1074 for (int i = r; i < r + cellRowSpan; ++i)
1075 for (int j = c; j < c + cellColSpan; ++j)
1076 d->grid[i * d->nCols + j] = cellFragment;
1078 // erase the cell marker
1081 const int nextFragment = d->cells.value(cellIndex, d->fragment_end);
1082 const uint nextPos = p->fragmentMap().position(nextFragment);
1084 Q_ASSERT(nextPos >= pos);
1086 // merge the contents of the cell (if not empty)
1087 if (nextPos > pos) {
1088 if (needsParagraph) {
1089 needsParagraph = false;
1090 QTextCursor(p, insertPos++).insertBlock();
1091 p->move(pos + 1, insertPos, nextPos - pos);
1092 } else if (rowHasText) {
1093 QTextCursor(p, insertPos++).insertText(QLatin1String(" "));
1094 p->move(pos + 1, insertPos, nextPos - pos);
1096 p->move(pos, insertPos, nextPos - pos);
1099 insertPos += nextPos - pos;
1105 needsParagraph = true;
1109 // erase cells from last row
1110 if (firstCellIndex >= 0) {
1111 d->cellIndices.remove(firstCellIndex, cellIndex - firstCellIndex);
1112 d->cells.erase(d->cells.begin() + firstCellIndex, d->cells.begin() + cellIndex);
1116 d->fragment_start = d->cells.first();
1118 fmt.setTableCellRowSpan(numRows);
1119 fmt.setTableCellColumnSpan(numCols);
1120 p->setCharFormat(origCellPosition, 1, fmt);
1122 d->blockFragmentUpdates = false;
1132 Merges the cells selected by the provided \a cursor.
1136 void QTextTable::mergeCells(const QTextCursor &cursor)
1138 if (!cursor.hasComplexSelection())
1141 int firstRow, numRows, firstColumn, numColumns;
1142 cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
1143 mergeCells(firstRow, firstColumn, numRows, numColumns);
1149 Splits the specified cell at \a row and \a column into an array of multiple
1150 cells with dimensions specified by \a numRows and \a numCols.
1152 \note It is only possible to split cells that span multiple rows or columns, such as rows
1153 that have been merged using mergeCells().
1157 void QTextTable::splitCell(int row, int column, int numRows, int numCols)
1164 QTextDocumentPrivate *p = d->pieceTable;
1165 QTextFormatCollection *c = p->formatCollection();
1167 const QTextTableCell cell = cellAt(row, column);
1168 if (!cell.isValid())
1171 column = cell.column();
1173 QTextCharFormat fmt = cell.format();
1174 const int rowSpan = fmt.tableCellRowSpan();
1175 const int colSpan = fmt.tableCellColumnSpan();
1177 // nothing to split?
1178 if (numRows > rowSpan || numCols > colSpan)
1181 p->beginEditBlock();
1183 const int origCellPosition = cell.firstPosition() - 1;
1185 QVarLengthArray<int> rowPositions(rowSpan);
1187 rowPositions[0] = cell.lastPosition();
1189 for (int r = row + 1; r < row + rowSpan; ++r) {
1190 // find the cell before which to insert the new cell markers
1191 int gridIndex = r * d->nCols + column;
1192 QVector<int>::iterator it = qUpperBound(d->cellIndices.begin(), d->cellIndices.end(), gridIndex);
1193 int cellIndex = it - d->cellIndices.begin();
1194 int fragment = d->cells.value(cellIndex, d->fragment_end);
1195 rowPositions[r - row] = p->fragmentMap().position(fragment);
1198 fmt.setTableCellColumnSpan(1);
1199 fmt.setTableCellRowSpan(1);
1200 const int fmtIndex = c->indexForFormat(fmt);
1201 const int blockIndex = p->blockMap().find(cell.lastPosition())->format;
1203 int insertAdjustement = 0;
1204 for (int i = 0; i < numRows; ++i) {
1205 for (int c = 0; c < colSpan - numCols; ++c)
1206 p->insertBlock(QTextBeginningOfFrame, rowPositions[i] + insertAdjustement + c, blockIndex, fmtIndex);
1207 insertAdjustement += colSpan - numCols;
1210 for (int i = numRows; i < rowSpan; ++i) {
1211 for (int c = 0; c < colSpan; ++c)
1212 p->insertBlock(QTextBeginningOfFrame, rowPositions[i] + insertAdjustement + c, blockIndex, fmtIndex);
1213 insertAdjustement += colSpan;
1216 fmt.setTableCellRowSpan(numRows);
1217 fmt.setTableCellColumnSpan(numCols);
1218 p->setCharFormat(origCellPosition, 1, fmt);
1224 Returns the number of rows in the table.
1228 int QTextTable::rows() const
1230 Q_D(const QTextTable);
1238 Returns the number of columns in the table.
1242 int QTextTable::columns() const
1244 Q_D(const QTextTable);
1252 void QTextTable::mergeCells(const QTextCursor &selection)
1258 \fn QTextCursor QTextTable::rowStart(const QTextCursor &cursor) const
1260 Returns a cursor pointing to the start of the row that contains the
1265 QTextCursor QTextTable::rowStart(const QTextCursor &c) const
1267 Q_D(const QTextTable);
1268 QTextTableCell cell = cellAt(c);
1269 if (!cell.isValid())
1270 return QTextCursor();
1272 int row = cell.row();
1273 QTextDocumentPrivate *p = d->pieceTable;
1274 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), d->grid[row*d->nCols]);
1275 return QTextCursor(p, it.position());
1279 \fn QTextCursor QTextTable::rowEnd(const QTextCursor &cursor) const
1281 Returns a cursor pointing to the end of the row that contains the given
1286 QTextCursor QTextTable::rowEnd(const QTextCursor &c) const
1288 Q_D(const QTextTable);
1289 QTextTableCell cell = cellAt(c);
1290 if (!cell.isValid())
1291 return QTextCursor();
1293 int row = cell.row() + 1;
1294 int fragment = row < d->nRows ? d->grid[row*d->nCols] : d->fragment_end;
1295 QTextDocumentPrivate *p = d->pieceTable;
1296 QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), fragment);
1297 return QTextCursor(p, it.position() - 1);
1301 \fn void QTextTable::setFormat(const QTextTableFormat &format)
1303 Sets the table's \a format.
1307 void QTextTable::setFormat(const QTextTableFormat &format)
1309 QTextTableFormat fmt = format;
1310 // don't try to change the number of table columns from here
1311 fmt.setColumns(columns());
1312 QTextObject::setFormat(fmt);
1316 \fn QTextTableFormat QTextTable::format() const
1318 Returns the table's format.