src/gui/text/qtexttable.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/text/qtexttable.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1302 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qtexttable.h"
+#include "qtextcursor.h"
+#include "qtextformat.h"
+#include <qdebug.h>
+#include "qtexttable_p.h"
+#include "qvarlengtharray.h"
+#include "private/qfunctions_p.h"
+
+#include <stdlib.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+    \class QTextTableCell
+    \reentrant
+
+    \brief The QTextTableCell class represents the properties of a
+    cell in a QTextTable.
+
+    \ingroup richtext-processing
+
+    Table cells are pieces of document structure that belong to a table.
+    The table orders cells into particular rows and columns; cells can
+    also span multiple columns and rows.
+
+    Cells are usually created when a table is inserted into a document with
+    QTextCursor::insertTable(), but they are also created and destroyed when
+    a table is resized.
+
+    Cells contain information about their location in a table; you can
+    obtain the row() and column() numbers of a cell, and its rowSpan()
+    and columnSpan().
+
+    The format() of a cell describes the default character format of its
+    contents. The firstCursorPosition() and lastCursorPosition() functions
+    are used to obtain the extent of the cell in the document.
+
+    \sa QTextTable QTextTableFormat
+*/
+
+/*!
+    \fn QTextTableCell::QTextTableCell()
+
+    Constructs an invalid table cell.
+
+    \sa isValid()
+*/
+
+/*!
+    \fn QTextTableCell::QTextTableCell(const QTextTableCell &other)
+
+    Copy constructor. Creates a new QTextTableCell object based on the
+    \a other cell.
+*/
+
+/*!
+    \fn QTextTableCell& QTextTableCell::operator=(const QTextTableCell &other)
+
+    Assigns the \a other table cell to this table cell.
+*/
+
+/*!
+    \since 4.2
+
+    Sets the cell's character format to \a format. This can for example be used to change
+    the background color of the entire cell:
+
+    QTextTableCell cell = table->cellAt(2, 3);
+    QTextCharFormat format = cell.format();
+    format.setBackground(Qt::blue);
+    cell.setFormat(format);
+
+    Note that the cell's row or column span cannot be changed through this function. You have
+    to use QTextTable::mergeCells and QTextTable::splitCell instead.
+
+    \sa format()
+*/
+void QTextTableCell::setFormat(const QTextCharFormat &format)
+{
+    QTextCharFormat fmt = format;
+    fmt.clearProperty(QTextFormat::ObjectIndex);
+    fmt.setObjectType(QTextFormat::TableCellObject);
+    QTextDocumentPrivate *p = table->docHandle();
+    QTextDocumentPrivate::FragmentIterator frag(&p->fragmentMap(), fragment);
+
+    QTextFormatCollection *c = p->formatCollection();
+    QTextCharFormat oldFormat = c->charFormat(frag->format);
+    fmt.setTableCellRowSpan(oldFormat.tableCellRowSpan());
+    fmt.setTableCellColumnSpan(oldFormat.tableCellColumnSpan());
+
+    p->setCharFormat(frag.position(), 1, fmt, QTextDocumentPrivate::SetFormatAndPreserveObjectIndices);
+}
+
+/*!
+    Returns the cell's character format.
+*/
+QTextCharFormat QTextTableCell::format() const
+{
+    QTextDocumentPrivate *p = table->docHandle();
+    QTextFormatCollection *c = p->formatCollection();
+
+    QTextCharFormat fmt = c->charFormat(tableCellFormatIndex());
+    fmt.setObjectType(QTextFormat::TableCellObject);
+    return fmt;
+}
+
+/*!
+    \since 4.5
+
+    Returns the index of the tableCell's format in the document's internal list of formats.
+
+    \sa QTextDocument::allFormats()
+*/
+int QTextTableCell::tableCellFormatIndex() const
+{
+    QTextDocumentPrivate *p = table->docHandle();
+    return QTextDocumentPrivate::FragmentIterator(&p->fragmentMap(), fragment)->format;
+}
+
+/*!
+    Returns the number of the row in the table that contains this cell.
+
+    \sa column()
+*/
+int QTextTableCell::row() const
+{
+    const QTextTablePrivate *tp = table->d_func();
+    if (tp->dirty)
+        tp->update();
+
+    int idx = tp->findCellIndex(fragment);
+    if (idx == -1)
+        return idx;
+    return tp->cellIndices.at(idx) / tp->nCols;
+}
+
+/*!
+    Returns the number of the column in the table that contains this cell.
+
+    \sa row()
+*/
+int QTextTableCell::column() const
+{
+    const QTextTablePrivate *tp = table->d_func();
+    if (tp->dirty)
+        tp->update();
+
+    int idx = tp->findCellIndex(fragment);
+    if (idx == -1)
+        return idx;
+    return tp->cellIndices.at(idx) % tp->nCols;
+}
+
+/*!
+    Returns the number of rows this cell spans. The default is 1.
+
+    \sa columnSpan()
+*/
+int QTextTableCell::rowSpan() const
+{
+    return format().tableCellRowSpan();
+}
+
+/*!
+    Returns the number of columns this cell spans. The default is 1.
+
+    \sa rowSpan()
+*/
+int QTextTableCell::columnSpan() const
+{
+    return format().tableCellColumnSpan();
+}
+
+/*!
+    \fn bool QTextTableCell::isValid() const
+
+    Returns true if this is a valid table cell; otherwise returns
+    false.
+*/
+
+
+/*!
+    Returns the first valid cursor position in this cell.
+
+    \sa lastCursorPosition()
+*/
+QTextCursor QTextTableCell::firstCursorPosition() const
+{
+    return QTextCursor(table->d_func()->pieceTable, firstPosition());
+}
+
+/*!
+    Returns the last valid cursor position in this cell.
+
+    \sa firstCursorPosition()
+*/
+QTextCursor QTextTableCell::lastCursorPosition() const
+{
+    return QTextCursor(table->d_func()->pieceTable, lastPosition());
+}
+
+
+/*!
+    \internal
+
+    Returns the first valid position in the document occupied by this cell.
+*/
+int QTextTableCell::firstPosition() const
+{
+    QTextDocumentPrivate *p = table->docHandle();
+    return p->fragmentMap().position(fragment) + 1;
+}
+
+/*!
+    \internal
+
+    Returns the last valid position in the document occupied by this cell.
+*/
+int QTextTableCell::lastPosition() const
+{
+    QTextDocumentPrivate *p = table->docHandle();
+    const QTextTablePrivate *td = table->d_func();
+    int index = table->d_func()->findCellIndex(fragment);
+    int f;
+    if (index != -1)
+        f = td->cells.value(index + 1, td->fragment_end);
+    else
+        f = td->fragment_end;
+    return p->fragmentMap().position(f);
+}
+
+
+/*!
+    Returns a frame iterator pointing to the beginning of the table's cell.
+
+    \sa end()
+*/
+QTextFrame::iterator QTextTableCell::begin() const
+{
+    QTextDocumentPrivate *p = table->docHandle();
+    int b = p->blockMap().findNode(firstPosition());
+    int e = p->blockMap().findNode(lastPosition()+1);
+    return QTextFrame::iterator(const_cast<QTextTable *>(table), b, b, e);
+}
+
+/*!
+    Returns a frame iterator pointing to the end of the table's cell.
+
+    \sa begin()
+*/
+QTextFrame::iterator QTextTableCell::end() const
+{
+    QTextDocumentPrivate *p = table->docHandle();
+    int b = p->blockMap().findNode(firstPosition());
+    int e = p->blockMap().findNode(lastPosition()+1);
+    return QTextFrame::iterator(const_cast<QTextTable *>(table), e, b, e);
+}
+
+
+/*!
+    \fn QTextCursor QTextTableCell::operator==(const QTextTableCell &other) const
+
+    Returns true if this cell object and the \a other cell object
+    describe the same cell; otherwise returns false.
+*/
+
+/*!
+    \fn QTextCursor QTextTableCell::operator!=(const QTextTableCell &other) const
+
+    Returns true if this cell object and the \a other cell object
+    describe different cells; otherwise returns false.
+*/
+
+/*!
+    \fn QTextTableCell::~QTextTableCell()
+
+    Destroys the table cell.
+*/
+
+QTextTablePrivate::~QTextTablePrivate()
+{
+    if (grid)
+        free(grid);
+}
+
+
+QTextTable *QTextTablePrivate::createTable(QTextDocumentPrivate *pieceTable, int pos, int rows, int cols, const QTextTableFormat &tableFormat)
+{
+    QTextTableFormat fmt = tableFormat;
+    fmt.setColumns(cols);
+    QTextTable *table = qobject_cast<QTextTable *>(pieceTable->createObject(fmt));
+    Q_ASSERT(table);
+
+    pieceTable->beginEditBlock();
+
+//     qDebug("---> createTable: rows=%d, cols=%d at %d", rows, cols, pos);
+    // add block after table
+    QTextCharFormat charFmt;
+    charFmt.setObjectIndex(table->objectIndex());
+    charFmt.setObjectType(QTextFormat::TableCellObject);
+
+
+    int charIdx = pieceTable->formatCollection()->indexForFormat(charFmt);
+    int cellIdx = pieceTable->formatCollection()->indexForFormat(QTextBlockFormat());
+
+    QTextTablePrivate *d = table->d_func();
+    d->blockFragmentUpdates = true;
+
+    d->fragment_start = pieceTable->insertBlock(QTextBeginningOfFrame, pos, cellIdx, charIdx);
+    d->cells.append(d->fragment_start);
+    ++pos;
+
+    for (int i = 1; i < rows*cols; ++i) {
+        d->cells.append(pieceTable->insertBlock(QTextBeginningOfFrame, pos, cellIdx, charIdx));
+// 	    qDebug("      addCell at %d", pos);
+        ++pos;
+    }
+
+    d->fragment_end = pieceTable->insertBlock(QTextEndOfFrame, pos, cellIdx, charIdx);
+// 	qDebug("      addEOR at %d", pos);
+    ++pos;
+
+    d->blockFragmentUpdates = false;
+    d->dirty = true;
+
+    pieceTable->endEditBlock();
+
+    return table;
+}
+
+struct QFragmentFindHelper
+{
+    inline QFragmentFindHelper(int _pos, const QTextDocumentPrivate::FragmentMap &map)
+        : pos(_pos), fragmentMap(map) {}
+    uint pos;
+    const QTextDocumentPrivate::FragmentMap &fragmentMap;
+};
+
+Q_STATIC_GLOBAL_INLINE_OPERATOR bool operator<(int fragment, const QFragmentFindHelper &helper)
+{
+    return helper.fragmentMap.position(fragment) < helper.pos;
+}
+
+Q_STATIC_GLOBAL_INLINE_OPERATOR bool operator<(const QFragmentFindHelper &helper, int fragment)
+{
+    return helper.pos < helper.fragmentMap.position(fragment);
+}
+
+int QTextTablePrivate::findCellIndex(int fragment) const
+{
+    QFragmentFindHelper helper(pieceTable->fragmentMap().position(fragment),
+                              pieceTable->fragmentMap());
+    QList<int>::ConstIterator it = qBinaryFind(cells.begin(), cells.end(), helper);
+    if (it == cells.end())
+        return -1;
+    return it - cells.begin();
+}
+
+void QTextTablePrivate::fragmentAdded(const QChar &type, uint fragment)
+{
+    dirty = true;
+    if (blockFragmentUpdates)
+        return;
+    if (type == QTextBeginningOfFrame) {
+        Q_ASSERT(cells.indexOf(fragment) == -1);
+        const uint pos = pieceTable->fragmentMap().position(fragment);
+        QFragmentFindHelper helper(pos, pieceTable->fragmentMap());
+        QList<int>::Iterator it = qLowerBound(cells.begin(), cells.end(), helper);
+        cells.insert(it, fragment);
+        if (!fragment_start || pos < pieceTable->fragmentMap().position(fragment_start))
+            fragment_start = fragment;
+        return;
+    }
+    QTextFramePrivate::fragmentAdded(type, fragment);
+}
+
+void QTextTablePrivate::fragmentRemoved(const QChar &type, uint fragment)
+{
+    dirty = true;
+    if (blockFragmentUpdates)
+        return;
+    if (type == QTextBeginningOfFrame) {
+        Q_ASSERT(cells.indexOf(fragment) != -1);
+        cells.removeAll(fragment);
+        if (fragment_start == fragment && cells.size()) {
+            fragment_start = cells.at(0);
+        }
+        if (fragment_start != fragment)
+            return;
+    }
+    QTextFramePrivate::fragmentRemoved(type, fragment);
+}
+
+/*!
+    /fn void QTextTablePrivate::update() const
+
+    This function is usually called when the table is "dirty".
+    It seems to update all kind of table information.
+
+*/
+void QTextTablePrivate::update() const
+{
+    Q_Q(const QTextTable);
+    nCols = q->format().columns();
+    nRows = (cells.size() + nCols-1)/nCols;
+//     qDebug(">>>> QTextTablePrivate::update, nRows=%d, nCols=%d", nRows, nCols);
+
+    grid = q_check_ptr((int *)realloc(grid, nRows*nCols*sizeof(int)));
+    memset(grid, 0, nRows*nCols*sizeof(int));
+
+    QTextDocumentPrivate *p = pieceTable;
+    QTextFormatCollection *c = p->formatCollection();
+
+    cellIndices.resize(cells.size());
+
+    int cell = 0;
+    for (int i = 0; i < cells.size(); ++i) {
+        int fragment = cells.at(i);
+        QTextCharFormat fmt = c->charFormat(QTextDocumentPrivate::FragmentIterator(&p->fragmentMap(), fragment)->format);
+        int rowspan = fmt.tableCellRowSpan();
+        int colspan = fmt.tableCellColumnSpan();
+
+        // skip taken cells
+        while (cell < nRows*nCols && grid[cell])
+            ++cell;
+
+        int r = cell/nCols;
+        int c = cell%nCols;
+        cellIndices[i] = cell;
+
+        if (r + rowspan > nRows) {
+            grid = q_check_ptr((int *)realloc(grid, sizeof(int)*(r + rowspan)*nCols));
+            memset(grid + (nRows*nCols), 0, sizeof(int)*(r+rowspan-nRows)*nCols);
+            nRows = r + rowspan;
+        }
+
+        Q_ASSERT(c + colspan <= nCols);
+        for (int ii = 0; ii < rowspan; ++ii) {
+            for (int jj = 0; jj < colspan; ++jj) {
+                Q_ASSERT(grid[(r+ii)*nCols + c+jj] == 0);
+                grid[(r+ii)*nCols + c+jj] = fragment;
+//  		    qDebug("    setting cell %d span=%d/%d at %d/%d", fragment, rowspan, colspan, r+ii, c+jj);
+            }
+        }
+    }
+//     qDebug("<<<< end: nRows=%d, nCols=%d", nRows, nCols);
+
+    dirty = false;
+}
+
+
+
+
+
+/*!
+    \class QTextTable
+    \reentrant
+
+    \brief The QTextTable class represents a table in a QTextDocument.
+
+    \ingroup richtext-processing
+
+    A table is a group of cells ordered into rows and columns. Each table
+    contains at least one row and one column. Each cell contains a block, and
+    is surrounded by a frame.
+
+    Tables are usually created and inserted into a document with the
+    QTextCursor::insertTable() function.
+    For example, we can insert a table with three rows and two columns at the
+    current cursor position in an editor using the following lines of code:
+
+    \snippet doc/src/snippets/textdocument-tables/mainwindow.cpp 1
+    \codeline
+    \snippet doc/src/snippets/textdocument-tables/mainwindow.cpp 3
+
+    The table format is either defined when the table is created or changed
+    later with setFormat().
+
+    The table currently being edited by the cursor is found with
+    QTextCursor::currentTable(). This allows its format or dimensions to be
+    changed after it has been inserted into a document.
+
+    A table's size can be changed with resize(), or by using
+    insertRows(), insertColumns(), removeRows(), or removeColumns().
+    Use cellAt() to retrieve table cells.
+
+    The starting and ending positions of table rows can be found by moving
+    a cursor within a table, and using the rowStart() and rowEnd() functions
+    to obtain cursors at the start and end of each row.
+
+    Rows and columns within a QTextTable can be merged and split using
+    the mergeCells() and splitCell() functions. However, only cells that span multiple
+    rows or columns can be split. (Merging or splitting does not increase or decrease
+    the number of rows and columns.) 
+
+    Note that if you have merged multiple columns and rows into one cell, you will not
+    be able to split the merged cell into new cells spanning over more than one row 
+    or column. To be able to split cells spanning over several rows and columns you 
+    need to do this over several iterations.
+
+    \table 80%
+    \row
+        \o \inlineimage texttable-split.png Original Table
+        \o Suppose we have a 2x3 table of names and addresses. To merge both
+        columns in the first row we invoke mergeCells() with \a row = 0,
+        \a column = 0, \a numRows = 1 and \a numColumns = 2.
+        \snippet doc/src/snippets/textdocument-texttable/main.cpp 0
+
+    \row
+        \o \inlineimage texttable-merge.png
+        \o  This gives us the following table. To split the first row of the table
+        back into two cells, we invoke the splitCell() function with \a numRows
+        and \a numCols = 1.
+        \snippet doc/src/snippets/textdocument-texttable/main.cpp 1
+
+    \row
+        \o \inlineimage texttable-split.png Split Table
+        \o This results in the original table.
+    \endtable
+
+    \sa QTextTableFormat
+*/
+
+/*! \internal
+ */
+QTextTable::QTextTable(QTextDocument *doc)
+    : QTextFrame(*new QTextTablePrivate(doc), doc)
+{
+}
+
+/*! \internal
+
+Destroys the table.
+ */
+QTextTable::~QTextTable()
+{
+}
+
+
+/*!
+    \fn QTextTableCell QTextTable::cellAt(int row, int column) const
+
+    Returns the table cell at the given \a row and \a column in the table.
+
+    \sa columns() rows()
+*/
+QTextTableCell QTextTable::cellAt(int row, int col) const
+{
+    Q_D(const QTextTable);
+    if (d->dirty)
+        d->update();
+
+    if (row < 0 || row >= d->nRows || col < 0 || col >= d->nCols)
+        return QTextTableCell();
+
+    return QTextTableCell(this, d->grid[row*d->nCols + col]);
+}
+
+/*!
+    \overload
+
+    Returns the table cell that contains the character at the given \a position
+    in the document.
+*/
+QTextTableCell QTextTable::cellAt(int position) const
+{
+    Q_D(const QTextTable);
+    if (d->dirty)
+        d->update();
+
+    uint pos = (uint)position;
+    const QTextDocumentPrivate::FragmentMap &map = d->pieceTable->fragmentMap();
+    if (position < 0 || map.position(d->fragment_start) >= pos || map.position(d->fragment_end) < pos)
+        return QTextTableCell();
+
+    QFragmentFindHelper helper(position, map);
+    QList<int>::ConstIterator it = qLowerBound(d->cells.begin(), d->cells.end(), helper);
+    if (it != d->cells.begin())
+        --it;
+
+    return QTextTableCell(this, *it);
+}
+
+/*!
+    \fn QTextTableCell QTextTable::cellAt(const QTextCursor &cursor) const
+
+    \overload
+
+    Returns the table cell containing the given \a cursor.
+*/
+QTextTableCell QTextTable::cellAt(const QTextCursor &c) const
+{
+    return cellAt(c.position());
+}
+
+/*!
+    \fn void QTextTable::resize(int rows, int columns)
+
+    Resizes the table to contain the required number of \a rows and \a columns.
+
+    \sa insertRows() insertColumns() removeRows() removeColumns()
+*/
+void QTextTable::resize(int rows, int cols)
+{
+    Q_D(QTextTable);
+    if (d->dirty)
+        d->update();
+
+    int nRows = this->rows();
+    int nCols = this->columns();
+
+    if (rows == nRows && cols == nCols)
+	return;
+
+    d->pieceTable->beginEditBlock();
+
+    if (nCols < cols)
+        insertColumns(nCols, cols - nCols);
+    else if (nCols > cols)
+        removeColumns(cols, nCols - cols);
+
+    if (nRows < rows)
+        insertRows(nRows, rows-nRows);
+    else if (nRows > rows)
+        removeRows(rows, nRows-rows);
+
+    d->pieceTable->endEditBlock();
+}
+
+/*!
+    \fn void QTextTable::insertRows(int index, int rows)
+
+    Inserts a number of \a rows before the row with the specified \a index.
+
+    \sa resize() insertColumns() removeRows() removeColumns() appendRows() appendColumns()
+*/
+void QTextTable::insertRows(int pos, int num)
+{
+    Q_D(QTextTable);
+    if (num <= 0)
+	return;
+
+    if (d->dirty)
+        d->update();
+
+    if (pos > d->nRows || pos < 0)
+        pos = d->nRows;
+
+//     qDebug() << "-------- insertRows" << pos << num;
+    QTextDocumentPrivate *p = d->pieceTable;
+    QTextFormatCollection *c = p->formatCollection();
+    p->beginEditBlock();
+
+    int extended = 0;
+    int insert_before = 0;
+    if (pos > 0 && pos < d->nRows) {
+        for (int i = 0; i < d->nCols; ++i) {
+            int cell = d->grid[pos*d->nCols + i];
+            if (cell == d->grid[(pos-1)*d->nCols+i]) {
+                // cell spans the insertion place, extend it
+                QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
+                QTextCharFormat fmt = c->charFormat(it->format);
+                fmt.setTableCellRowSpan(fmt.tableCellRowSpan() + num);
+                p->setCharFormat(it.position(), 1, fmt);
+                extended++;
+            } else if (!insert_before) {
+                insert_before = cell;
+            }
+        }
+    } else {
+        insert_before = (pos == 0 ? d->grid[0] : d->fragment_end);
+    }
+    if (extended < d->nCols) {
+        Q_ASSERT(insert_before);
+        QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), insert_before);
+        QTextCharFormat fmt = c->charFormat(it->format);
+        fmt.setTableCellRowSpan(1);
+        fmt.setTableCellColumnSpan(1);
+        Q_ASSERT(fmt.objectIndex() == objectIndex());
+        int pos = it.position();
+        int cfmt = p->formatCollection()->indexForFormat(fmt);
+        int bfmt = p->formatCollection()->indexForFormat(QTextBlockFormat());
+//         qDebug("inserting %d cells, nCols=%d extended=%d", num*(d->nCols-extended), d->nCols, extended);
+        for (int i = 0; i < num*(d->nCols-extended); ++i)
+            p->insertBlock(QTextBeginningOfFrame, pos, bfmt, cfmt, QTextUndoCommand::MoveCursor);
+    }
+
+//     qDebug() << "-------- end insertRows" << pos << num;
+    p->endEditBlock();
+}
+
+/*!
+    \fn void QTextTable::insertColumns(int index, int columns)
+
+    Inserts a number of \a columns before the column with the specified \a index.
+
+    \sa insertRows() resize() removeRows() removeColumns() appendRows() appendColumns()
+*/
+void QTextTable::insertColumns(int pos, int num)
+{
+    Q_D(QTextTable);
+    if (num <= 0)
+	return;
+
+    if (d->dirty)
+        d->update();
+
+    if (pos > d->nCols || pos < 0)
+        pos = d->nCols;
+
+//     qDebug() << "-------- insertCols" << pos << num;
+    QTextDocumentPrivate *p = d->pieceTable;
+    QTextFormatCollection *c = p->formatCollection();
+    p->beginEditBlock();
+
+    for (int i = 0; i < d->nRows; ++i) {
+        int cell;
+        if (i == d->nRows - 1 && pos == d->nCols)
+            cell = d->fragment_end;
+        else
+            cell = d->grid[i*d->nCols + pos];
+        QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
+        QTextCharFormat fmt = c->charFormat(it->format);
+        if (pos > 0 && pos < d->nCols && cell == d->grid[i*d->nCols + pos - 1]) {
+            // cell spans the insertion place, extend it
+            fmt.setTableCellColumnSpan(fmt.tableCellColumnSpan() + num);
+            p->setCharFormat(it.position(), 1, fmt);
+        } else {
+            fmt.setTableCellRowSpan(1);
+            fmt.setTableCellColumnSpan(1);
+            Q_ASSERT(fmt.objectIndex() == objectIndex());
+            int position = it.position();
+            int cfmt = p->formatCollection()->indexForFormat(fmt);
+            int bfmt = p->formatCollection()->indexForFormat(QTextBlockFormat());
+            for (int i = 0; i < num; ++i)
+                p->insertBlock(QTextBeginningOfFrame, position, bfmt, cfmt, QTextUndoCommand::MoveCursor);
+        }
+    }
+
+    QTextTableFormat tfmt = format();
+    tfmt.setColumns(tfmt.columns()+num);
+    QVector<QTextLength> columnWidths = tfmt.columnWidthConstraints();
+    if (! columnWidths.isEmpty()) {
+        for (int i = num; i > 0; --i)
+            columnWidths.insert(pos, columnWidths[qMax(0, pos-1)]);
+    }
+    tfmt.setColumnWidthConstraints (columnWidths);
+    QTextObject::setFormat(tfmt);
+
+//     qDebug() << "-------- end insertCols" << pos << num;
+    p->endEditBlock();
+}
+
+/*!
+    \since 4.5
+    Appends \a count rows at the bottom of the table.
+
+    \sa insertColumns() insertRows() resize() removeRows() removeColumns() appendColumns()
+*/
+void QTextTable::appendRows(int count)
+{
+    insertRows(rows(), count);
+}
+
+/*!
+    \since 4.5
+    Appends \a count columns at the right side of the table.
+
+    \sa insertColumns() insertRows() resize() removeRows() removeColumns() appendRows()
+*/
+void QTextTable::appendColumns(int count)
+{
+    insertColumns(columns(), count);
+}
+
+/*!
+    \fn void QTextTable::removeRows(int index, int rows)
+
+    Removes a number of \a rows starting with the row at the specified \a index.
+
+    \sa insertRows(), insertColumns(), resize(), removeColumns() appendRows() appendColumns()
+*/
+void QTextTable::removeRows(int pos, int num)
+{
+    Q_D(QTextTable);
+//     qDebug() << "-------- removeRows" << pos << num;
+
+    if (num <= 0 || pos < 0)
+        return;
+    if (d->dirty)
+        d->update();
+    if (pos >= d->nRows)
+        return;
+    if (pos+num > d->nRows)
+        num = d->nRows - pos;
+
+    QTextDocumentPrivate *p = d->pieceTable;
+    QTextFormatCollection *collection = p->formatCollection();
+    p->beginEditBlock();
+
+    // delete whole table?
+    if (pos == 0 && num == d->nRows) {
+        const int pos = p->fragmentMap().position(d->fragment_start);
+        p->remove(pos, p->fragmentMap().position(d->fragment_end) - pos + 1);
+        p->endEditBlock();
+        return;
+    }
+
+    p->aboutToRemoveCell(cellAt(pos, 0).firstPosition(), cellAt(pos + num - 1, d->nCols - 1).lastPosition());
+
+    QList<int> touchedCells;
+    for (int r = pos; r < pos + num; ++r) {
+        for (int c = 0; c < d->nCols; ++c) {
+            int cell = d->grid[r*d->nCols + c];
+            if (touchedCells.contains(cell))
+                continue;
+            touchedCells << cell;
+            QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
+            QTextCharFormat fmt = collection->charFormat(it->format);
+            int span = fmt.tableCellRowSpan();
+            if (span > 1) {
+                fmt.setTableCellRowSpan(span - 1);
+                p->setCharFormat(it.position(), 1, fmt);
+            } else {
+                // remove cell
+                int index = d->cells.indexOf(cell) + 1;
+                int f_end = index < d->cells.size() ? d->cells.at(index) : d->fragment_end;
+                p->remove(it.position(), p->fragmentMap().position(f_end) - it.position());
+            }
+        }
+    }
+
+    p->endEditBlock();
+//     qDebug() << "-------- end removeRows" << pos << num;
+}
+
+/*!
+    \fn void QTextTable::removeColumns(int index, int columns)
+
+    Removes a number of \a columns starting with the column at the specified
+    \a index.
+
+    \sa insertRows() insertColumns() removeRows() resize() appendRows() appendColumns()
+*/
+void QTextTable::removeColumns(int pos, int num)
+{
+    Q_D(QTextTable);
+//     qDebug() << "-------- removeCols" << pos << num;
+
+    if (num <= 0 || pos < 0)
+	return;
+    if (d->dirty)
+        d->update();
+    if (pos >= d->nCols)
+        return;
+    if (pos + num > d->nCols)
+        pos = d->nCols - num;
+
+    QTextDocumentPrivate *p = d->pieceTable;
+    QTextFormatCollection *collection = p->formatCollection();
+    p->beginEditBlock();
+
+    // delete whole table?
+    if (pos == 0 && num == d->nCols) {
+        const int pos = p->fragmentMap().position(d->fragment_start);
+        p->remove(pos, p->fragmentMap().position(d->fragment_end) - pos + 1);
+        p->endEditBlock();
+        return;
+    }
+
+    p->aboutToRemoveCell(cellAt(0, pos).firstPosition(), cellAt(d->nRows - 1, pos + num - 1).lastPosition());
+
+    QList<int> touchedCells;
+    for (int r = 0; r < d->nRows; ++r) {
+        for (int c = pos; c < pos + num; ++c) {
+            int cell = d->grid[r*d->nCols + c];
+            if (touchedCells.contains(cell))
+                continue;
+            touchedCells << cell;
+            QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
+            QTextCharFormat fmt = collection->charFormat(it->format);
+            int span = fmt.tableCellColumnSpan();
+            if (span > 1) {
+                fmt.setTableCellColumnSpan(span - 1);
+                p->setCharFormat(it.position(), 1, fmt);
+            } else {
+                // remove cell
+                int index = d->cells.indexOf(cell) + 1;
+                int f_end = index < d->cells.size() ? d->cells.at(index) : d->fragment_end;
+                p->remove(it.position(), p->fragmentMap().position(f_end) - it.position());
+            }
+        }
+    }
+
+    QTextTableFormat tfmt = format();
+    tfmt.setColumns(tfmt.columns()-num);
+    QVector<QTextLength> columnWidths = tfmt.columnWidthConstraints();
+    if (columnWidths.count() > pos) {
+        columnWidths.remove(pos, num);
+        tfmt.setColumnWidthConstraints (columnWidths);
+    }
+    QTextObject::setFormat(tfmt);
+
+    p->endEditBlock();
+//     qDebug() << "-------- end removeCols" << pos << num;
+}
+
+/*!
+    \since 4.1
+
+    Merges the cell at the specified \a row and \a column with the adjacent cells
+    into one cell. The new cell will span \a numRows rows and \a numCols columns.
+    If \a numRows or \a numCols is less than the current number of rows or columns
+    the cell spans then this method does nothing.
+
+    \sa splitCell()
+*/
+void QTextTable::mergeCells(int row, int column, int numRows, int numCols)
+{
+    Q_D(QTextTable);
+
+    if (d->dirty)
+        d->update();
+
+    QTextDocumentPrivate *p = d->pieceTable;
+    QTextFormatCollection *fc = p->formatCollection();
+
+    const QTextTableCell cell = cellAt(row, column);
+    if (!cell.isValid() || row != cell.row() || column != cell.column())
+        return;
+
+    QTextCharFormat fmt = cell.format();
+    const int rowSpan = fmt.tableCellRowSpan();
+    const int colSpan = fmt.tableCellColumnSpan();
+
+    numRows = qMin(numRows, rows() - cell.row());
+    numCols = qMin(numCols, columns() - cell.column());
+
+    // nothing to merge?
+    if (numRows < rowSpan || numCols < colSpan)
+        return;
+
+    // check the edges of the merge rect to make sure no cell spans the edge
+    for (int r = row; r < row + numRows; ++r) {
+        if (cellAt(r, column) == cellAt(r, column - 1))
+            return;
+        if (cellAt(r, column + numCols) == cellAt(r, column + numCols - 1))
+            return;
+    }
+
+    for (int c = column; c < column + numCols; ++c) {
+        if (cellAt(row, c) == cellAt(row - 1, c))
+            return;
+        if (cellAt(row + numRows, c) == cellAt(row + numRows - 1, c))
+            return;
+    }
+
+    p->beginEditBlock();
+
+    const int origCellPosition = cell.firstPosition() - 1;
+
+    const int cellFragment = d->grid[row * d->nCols + column];
+
+    // find the position at which to insert the contents of the merged cells
+    QFragmentFindHelper helper(origCellPosition, p->fragmentMap());
+    QList<int>::Iterator it = qBinaryFind(d->cells.begin(), d->cells.end(), helper);
+    Q_ASSERT(it != d->cells.end());
+    Q_ASSERT(*it == cellFragment);
+    const int insertCellIndex = it - d->cells.begin();
+    int insertFragment = d->cells.value(insertCellIndex + 1, d->fragment_end);
+    uint insertPos = p->fragmentMap().position(insertFragment);
+
+    d->blockFragmentUpdates = true;
+
+    bool rowHasText = cell.firstCursorPosition().block().length();
+    bool needsParagraph = rowHasText && colSpan == numCols;
+
+    // find all cells that will be erased by the merge
+    for (int r = row; r < row + numRows; ++r) {
+        int firstColumn = r < row + rowSpan ? column + colSpan : column;
+
+        // don't recompute the cell index for the first row
+        int firstCellIndex = r == row ? insertCellIndex + 1 : -1;
+        int cellIndex = firstCellIndex;
+
+        for (int c = firstColumn; c < column + numCols; ++c) {
+            const int fragment = d->grid[r * d->nCols + c];
+
+            // already handled?
+            if (fragment == cellFragment)
+                continue;
+
+            QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), fragment);
+            uint pos = it.position();
+
+            if (firstCellIndex == -1) {
+                QFragmentFindHelper helper(pos, p->fragmentMap());
+                QList<int>::Iterator it = qBinaryFind(d->cells.begin(), d->cells.end(), helper);
+                Q_ASSERT(it != d->cells.end());
+                Q_ASSERT(*it == fragment);
+                firstCellIndex = cellIndex = it - d->cells.begin();
+            }
+
+            ++cellIndex;
+
+            QTextCharFormat fmt = fc->charFormat(it->format);
+
+            const int cellRowSpan = fmt.tableCellRowSpan();
+            const int cellColSpan = fmt.tableCellColumnSpan();
+
+            // update the grid for this cell
+            for (int i = r; i < r + cellRowSpan; ++i)
+                for (int j = c; j < c + cellColSpan; ++j)
+                    d->grid[i * d->nCols + j] = cellFragment;
+
+            // erase the cell marker
+            p->remove(pos, 1);
+
+            const int nextFragment = d->cells.value(cellIndex, d->fragment_end);
+            const uint nextPos = p->fragmentMap().position(nextFragment);
+
+            Q_ASSERT(nextPos >= pos);
+
+            // merge the contents of the cell (if not empty)
+            if (nextPos > pos) {
+                if (needsParagraph) {
+                    needsParagraph = false;
+                    QTextCursor(p, insertPos++).insertBlock();
+                    p->move(pos + 1, insertPos, nextPos - pos);
+                } else if (rowHasText) {
+                    QTextCursor(p, insertPos++).insertText(QLatin1String(" "));
+                    p->move(pos + 1, insertPos, nextPos - pos);
+                } else {
+                    p->move(pos, insertPos, nextPos - pos);
+                }
+
+                insertPos += nextPos - pos;
+                rowHasText = true;
+            }
+        }
+
+        if (rowHasText) {
+            needsParagraph = true;
+            rowHasText = false;
+        }
+
+        // erase cells from last row
+        if (firstCellIndex >= 0) {
+            d->cellIndices.remove(firstCellIndex, cellIndex - firstCellIndex);
+            d->cells.erase(d->cells.begin() + firstCellIndex, d->cells.begin() + cellIndex);
+        }
+    }
+
+    d->fragment_start = d->cells.first();
+
+    fmt.setTableCellRowSpan(numRows);
+    fmt.setTableCellColumnSpan(numCols);
+    p->setCharFormat(origCellPosition, 1, fmt);
+
+    d->blockFragmentUpdates = false;
+    d->dirty = false;
+
+    p->endEditBlock();
+}
+
+/*!
+    \overload
+    \since 4.1
+
+    Merges the cells selected by the provided \a cursor.
+
+    \sa splitCell()
+*/
+void QTextTable::mergeCells(const QTextCursor &cursor)
+{
+    if (!cursor.hasComplexSelection())
+        return;
+
+    int firstRow, numRows, firstColumn, numColumns;
+    cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
+    mergeCells(firstRow, firstColumn, numRows, numColumns);
+}
+
+/*!
+    \since 4.1
+
+    Splits the specified cell at \a row and \a column into an array of multiple
+    cells with dimensions specified by \a numRows and \a numCols.
+
+    \note It is only possible to split cells that span multiple rows or columns, such as rows
+    that have been merged using mergeCells().
+
+    \sa mergeCells()
+*/
+void QTextTable::splitCell(int row, int column, int numRows, int numCols)
+{
+    Q_D(QTextTable);
+
+    if (d->dirty)
+        d->update();
+
+    QTextDocumentPrivate *p = d->pieceTable;
+    QTextFormatCollection *c = p->formatCollection();
+
+    const QTextTableCell cell = cellAt(row, column);
+    if (!cell.isValid())
+        return;
+    row = cell.row();
+    column = cell.column();
+
+    QTextCharFormat fmt = cell.format();
+    const int rowSpan = fmt.tableCellRowSpan();
+    const int colSpan = fmt.tableCellColumnSpan();
+
+    // nothing to split?
+    if (numRows > rowSpan || numCols > colSpan)
+        return;
+
+    p->beginEditBlock();
+
+    const int origCellPosition = cell.firstPosition() - 1;
+
+    QVarLengthArray<int> rowPositions(rowSpan);
+
+    rowPositions[0] = cell.lastPosition();
+
+    for (int r = row + 1; r < row + rowSpan; ++r) {
+        // find the cell before which to insert the new cell markers
+        int gridIndex = r * d->nCols + column;
+        QVector<int>::iterator it = qUpperBound(d->cellIndices.begin(), d->cellIndices.end(), gridIndex);
+        int cellIndex = it - d->cellIndices.begin();
+        int fragment = d->cells.value(cellIndex, d->fragment_end);
+        rowPositions[r - row] = p->fragmentMap().position(fragment);
+    }
+
+    fmt.setTableCellColumnSpan(1);
+    fmt.setTableCellRowSpan(1);
+    const int fmtIndex = c->indexForFormat(fmt);
+    const int blockIndex = p->blockMap().find(cell.lastPosition())->format;
+
+    int insertAdjustement = 0;
+    for (int i = 0; i < numRows; ++i) {
+        for (int c = 0; c < colSpan - numCols; ++c)
+            p->insertBlock(QTextBeginningOfFrame, rowPositions[i] + insertAdjustement + c, blockIndex, fmtIndex);
+        insertAdjustement += colSpan - numCols;
+    }
+
+    for (int i = numRows; i < rowSpan; ++i) {
+        for (int c = 0; c < colSpan; ++c)
+            p->insertBlock(QTextBeginningOfFrame, rowPositions[i] + insertAdjustement + c, blockIndex, fmtIndex);
+        insertAdjustement += colSpan;
+    }
+
+    fmt.setTableCellRowSpan(numRows);
+    fmt.setTableCellColumnSpan(numCols);
+    p->setCharFormat(origCellPosition, 1, fmt);
+
+    p->endEditBlock();
+}
+
+/*!
+    Returns the number of rows in the table.
+
+    \sa columns()
+*/
+int QTextTable::rows() const
+{
+    Q_D(const QTextTable);
+    if (d->dirty)
+        d->update();
+
+    return d->nRows;
+}
+
+/*!
+    Returns the number of columns in the table.
+
+    \sa rows()
+*/
+int QTextTable::columns() const
+{
+    Q_D(const QTextTable);
+    if (d->dirty)
+        d->update();
+
+    return d->nCols;
+}
+
+#if 0
+void QTextTable::mergeCells(const QTextCursor &selection)
+{
+}
+#endif
+
+/*!
+    \fn QTextCursor QTextTable::rowStart(const QTextCursor &cursor) const
+
+    Returns a cursor pointing to the start of the row that contains the
+    given \a cursor.
+
+    \sa rowEnd()
+*/
+QTextCursor QTextTable::rowStart(const QTextCursor &c) const
+{
+    Q_D(const QTextTable);
+    QTextTableCell cell = cellAt(c);
+    if (!cell.isValid())
+        return QTextCursor();
+
+    int row = cell.row();
+    QTextDocumentPrivate *p = d->pieceTable;
+    QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), d->grid[row*d->nCols]);
+    return QTextCursor(p, it.position());
+}
+
+/*!
+    \fn QTextCursor QTextTable::rowEnd(const QTextCursor &cursor) const
+
+    Returns a cursor pointing to the end of the row that contains the given
+    \a cursor.
+
+    \sa rowStart()
+*/
+QTextCursor QTextTable::rowEnd(const QTextCursor &c) const
+{
+    Q_D(const QTextTable);
+    QTextTableCell cell = cellAt(c);
+    if (!cell.isValid())
+        return QTextCursor();
+
+    int row = cell.row() + 1;
+    int fragment = row < d->nRows ? d->grid[row*d->nCols] : d->fragment_end;
+    QTextDocumentPrivate *p = d->pieceTable;
+    QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), fragment);
+    return QTextCursor(p, it.position() - 1);
+}
+
+/*!
+    \fn void QTextTable::setFormat(const QTextTableFormat &format)
+
+    Sets the table's \a format.
+
+    \sa format()
+*/
+void QTextTable::setFormat(const QTextTableFormat &format)
+{
+    QTextTableFormat fmt = format;
+    // don't try to change the number of table columns from here
+    fmt.setColumns(columns());
+    QTextObject::setFormat(fmt);
+}
+
+/*!
+    \fn QTextTableFormat QTextTable::format() const
+
+    Returns the table's format.
+
+    \sa setFormat()
+*/
+
+QT_END_NAMESPACE