DirectWrite font engine: don't leak the font table buffer
[profile/ivi/qtbase.git] / src / gui / text / qtexttable.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qtexttable.h"
43 #include "qtextcursor.h"
44 #include "qtextformat.h"
45 #include <qdebug.h>
46 #include "qtexttable_p.h"
47 #include "qvarlengtharray.h"
48 #include "private/qfunctions_p.h"
49
50 #include <stdlib.h>
51
52 QT_BEGIN_NAMESPACE
53
54 /*!
55     \class QTextTableCell
56     \reentrant
57
58     \brief The QTextTableCell class represents the properties of a
59     cell in a QTextTable.
60     \inmodule QtGui
61
62     \ingroup richtext-processing
63
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.
67
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
70     a table is resized.
71
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()
74     and columnSpan().
75
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.
79
80     \sa QTextTable, QTextTableFormat
81 */
82
83 /*!
84     \fn QTextTableCell::QTextTableCell()
85
86     Constructs an invalid table cell.
87
88     \sa isValid()
89 */
90
91 /*!
92     \fn QTextTableCell::QTextTableCell(const QTextTableCell &other)
93
94     Copy constructor. Creates a new QTextTableCell object based on the
95     \a other cell.
96 */
97
98 /*!
99     \fn QTextTableCell& QTextTableCell::operator=(const QTextTableCell &other)
100
101     Assigns the \a other table cell to this table cell.
102 */
103
104 /*!
105     \since 4.2
106
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:
109
110     QTextTableCell cell = table->cellAt(2, 3);
111     QTextCharFormat format = cell.format();
112     format.setBackground(Qt::blue);
113     cell.setFormat(format);
114
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.
117
118     \sa format()
119 */
120 void QTextTableCell::setFormat(const QTextCharFormat &format)
121 {
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);
127
128     QTextFormatCollection *c = p->formatCollection();
129     QTextCharFormat oldFormat = c->charFormat(frag->format);
130     fmt.setTableCellRowSpan(oldFormat.tableCellRowSpan());
131     fmt.setTableCellColumnSpan(oldFormat.tableCellColumnSpan());
132
133     p->setCharFormat(frag.position(), 1, fmt, QTextDocumentPrivate::SetFormatAndPreserveObjectIndices);
134 }
135
136 /*!
137     Returns the cell's character format.
138 */
139 QTextCharFormat QTextTableCell::format() const
140 {
141     QTextDocumentPrivate *p = table->docHandle();
142     QTextFormatCollection *c = p->formatCollection();
143
144     QTextCharFormat fmt = c->charFormat(tableCellFormatIndex());
145     fmt.setObjectType(QTextFormat::TableCellObject);
146     return fmt;
147 }
148
149 /*!
150     \since 4.5
151
152     Returns the index of the tableCell's format in the document's internal list of formats.
153
154     \sa QTextDocument::allFormats()
155 */
156 int QTextTableCell::tableCellFormatIndex() const
157 {
158     QTextDocumentPrivate *p = table->docHandle();
159     return QTextDocumentPrivate::FragmentIterator(&p->fragmentMap(), fragment)->format;
160 }
161
162 /*!
163     Returns the number of the row in the table that contains this cell.
164
165     \sa column()
166 */
167 int QTextTableCell::row() const
168 {
169     const QTextTablePrivate *tp = table->d_func();
170     if (tp->dirty)
171         tp->update();
172
173     int idx = tp->findCellIndex(fragment);
174     if (idx == -1)
175         return idx;
176     return tp->cellIndices.at(idx) / tp->nCols;
177 }
178
179 /*!
180     Returns the number of the column in the table that contains this cell.
181
182     \sa row()
183 */
184 int QTextTableCell::column() const
185 {
186     const QTextTablePrivate *tp = table->d_func();
187     if (tp->dirty)
188         tp->update();
189
190     int idx = tp->findCellIndex(fragment);
191     if (idx == -1)
192         return idx;
193     return tp->cellIndices.at(idx) % tp->nCols;
194 }
195
196 /*!
197     Returns the number of rows this cell spans. The default is 1.
198
199     \sa columnSpan()
200 */
201 int QTextTableCell::rowSpan() const
202 {
203     return format().tableCellRowSpan();
204 }
205
206 /*!
207     Returns the number of columns this cell spans. The default is 1.
208
209     \sa rowSpan()
210 */
211 int QTextTableCell::columnSpan() const
212 {
213     return format().tableCellColumnSpan();
214 }
215
216 /*!
217     \fn bool QTextTableCell::isValid() const
218
219     Returns true if this is a valid table cell; otherwise returns
220     false.
221 */
222
223
224 /*!
225     Returns the first valid cursor position in this cell.
226
227     \sa lastCursorPosition()
228 */
229 QTextCursor QTextTableCell::firstCursorPosition() const
230 {
231     return QTextCursor(table->d_func()->pieceTable, firstPosition());
232 }
233
234 /*!
235     Returns the last valid cursor position in this cell.
236
237     \sa firstCursorPosition()
238 */
239 QTextCursor QTextTableCell::lastCursorPosition() const
240 {
241     return QTextCursor(table->d_func()->pieceTable, lastPosition());
242 }
243
244
245 /*!
246     \internal
247
248     Returns the first valid position in the document occupied by this cell.
249 */
250 int QTextTableCell::firstPosition() const
251 {
252     QTextDocumentPrivate *p = table->docHandle();
253     return p->fragmentMap().position(fragment) + 1;
254 }
255
256 /*!
257     \internal
258
259     Returns the last valid position in the document occupied by this cell.
260 */
261 int QTextTableCell::lastPosition() const
262 {
263     QTextDocumentPrivate *p = table->docHandle();
264     const QTextTablePrivate *td = table->d_func();
265     int index = table->d_func()->findCellIndex(fragment);
266     int f;
267     if (index != -1)
268         f = td->cells.value(index + 1, td->fragment_end);
269     else
270         f = td->fragment_end;
271     return p->fragmentMap().position(f);
272 }
273
274
275 /*!
276     Returns a frame iterator pointing to the beginning of the table's cell.
277
278     \sa end()
279 */
280 QTextFrame::iterator QTextTableCell::begin() const
281 {
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);
286 }
287
288 /*!
289     Returns a frame iterator pointing to the end of the table's cell.
290
291     \sa begin()
292 */
293 QTextFrame::iterator QTextTableCell::end() const
294 {
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);
299 }
300
301
302 /*!
303     \fn QTextCursor QTextTableCell::operator==(const QTextTableCell &other) const
304
305     Returns true if this cell object and the \a other cell object
306     describe the same cell; otherwise returns false.
307 */
308
309 /*!
310     \fn QTextCursor QTextTableCell::operator!=(const QTextTableCell &other) const
311
312     Returns true if this cell object and the \a other cell object
313     describe different cells; otherwise returns false.
314 */
315
316 /*!
317     \fn QTextTableCell::~QTextTableCell()
318
319     Destroys the table cell.
320 */
321
322 QTextTablePrivate::~QTextTablePrivate()
323 {
324     if (grid)
325         free(grid);
326 }
327
328
329 QTextTable *QTextTablePrivate::createTable(QTextDocumentPrivate *pieceTable, int pos, int rows, int cols, const QTextTableFormat &tableFormat)
330 {
331     QTextTableFormat fmt = tableFormat;
332     fmt.setColumns(cols);
333     QTextTable *table = qobject_cast<QTextTable *>(pieceTable->createObject(fmt));
334     Q_ASSERT(table);
335
336     pieceTable->beginEditBlock();
337
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);
343
344
345     int charIdx = pieceTable->formatCollection()->indexForFormat(charFmt);
346     int cellIdx = pieceTable->formatCollection()->indexForFormat(QTextBlockFormat());
347
348     QTextTablePrivate *d = table->d_func();
349     d->blockFragmentUpdates = true;
350
351     d->fragment_start = pieceTable->insertBlock(QTextBeginningOfFrame, pos, cellIdx, charIdx);
352     d->cells.append(d->fragment_start);
353     ++pos;
354
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);
358         ++pos;
359     }
360
361     d->fragment_end = pieceTable->insertBlock(QTextEndOfFrame, pos, cellIdx, charIdx);
362 //      qDebug("      addEOR at %d", pos);
363     ++pos;
364
365     d->blockFragmentUpdates = false;
366     d->dirty = true;
367
368     pieceTable->endEditBlock();
369
370     return table;
371 }
372
373 struct QFragmentFindHelper
374 {
375     inline QFragmentFindHelper(int _pos, const QTextDocumentPrivate::FragmentMap &map)
376         : pos(_pos), fragmentMap(map) {}
377     uint pos;
378     const QTextDocumentPrivate::FragmentMap &fragmentMap;
379 };
380
381 Q_STATIC_GLOBAL_INLINE_OPERATOR bool operator<(int fragment, const QFragmentFindHelper &helper)
382 {
383     return helper.fragmentMap.position(fragment) < helper.pos;
384 }
385
386 Q_STATIC_GLOBAL_INLINE_OPERATOR bool operator<(const QFragmentFindHelper &helper, int fragment)
387 {
388     return helper.pos < helper.fragmentMap.position(fragment);
389 }
390
391 int QTextTablePrivate::findCellIndex(int fragment) const
392 {
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())
397         return -1;
398     return it - cells.begin();
399 }
400
401 void QTextTablePrivate::fragmentAdded(QChar type, uint fragment)
402 {
403     dirty = true;
404     if (blockFragmentUpdates)
405         return;
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;
414         return;
415     }
416     QTextFramePrivate::fragmentAdded(type, fragment);
417 }
418
419 void QTextTablePrivate::fragmentRemoved(QChar type, uint fragment)
420 {
421     dirty = true;
422     if (blockFragmentUpdates)
423         return;
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);
429         }
430         if (fragment_start != fragment)
431             return;
432     }
433     QTextFramePrivate::fragmentRemoved(type, fragment);
434 }
435
436 /*!
437     /fn void QTextTablePrivate::update() const
438
439     This function is usually called when the table is "dirty".
440     It seems to update all kind of table information.
441
442 */
443 void QTextTablePrivate::update() const
444 {
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);
449
450     grid = q_check_ptr((int *)realloc(grid, nRows*nCols*sizeof(int)));
451     memset(grid, 0, nRows*nCols*sizeof(int));
452
453     QTextDocumentPrivate *p = pieceTable;
454     QTextFormatCollection *c = p->formatCollection();
455
456     cellIndices.resize(cells.size());
457
458     int cell = 0;
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();
464
465         // skip taken cells
466         while (cell < nRows*nCols && grid[cell])
467             ++cell;
468
469         int r = cell/nCols;
470         int c = cell%nCols;
471         cellIndices[i] = cell;
472
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);
476             nRows = r + rowspan;
477         }
478
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);
485             }
486         }
487     }
488 //     qDebug("<<<< end: nRows=%d, nCols=%d", nRows, nCols);
489
490     dirty = false;
491 }
492
493
494
495
496
497 /*!
498     \class QTextTable
499     \reentrant
500
501     \brief The QTextTable class represents a table in a QTextDocument.
502     \inmodule QtGui
503
504     \ingroup richtext-processing
505
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.
509
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:
514
515     \snippet textdocument-tables/mainwindow.cpp 1
516     \codeline
517     \snippet textdocument-tables/mainwindow.cpp 3
518
519     The table format is either defined when the table is created or changed
520     later with setFormat().
521
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.
525
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.
529
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.
533
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.) 
538
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.
543
544     \table 80%
545     \row
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
551
552     \row
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
556         and \a numCols = 1.
557         \snippet textdocument-texttable/main.cpp 1
558
559     \row
560         \li \inlineimage texttable-split.png Split Table
561         \li This results in the original table.
562     \endtable
563
564     \sa QTextTableFormat
565 */
566
567 /*! \internal
568  */
569 QTextTable::QTextTable(QTextDocument *doc)
570     : QTextFrame(*new QTextTablePrivate(doc), doc)
571 {
572 }
573
574 /*! \internal
575
576 Destroys the table.
577  */
578 QTextTable::~QTextTable()
579 {
580 }
581
582
583 /*!
584     \fn QTextTableCell QTextTable::cellAt(int row, int column) const
585
586     Returns the table cell at the given \a row and \a column in the table.
587
588     \sa columns(), rows()
589 */
590 QTextTableCell QTextTable::cellAt(int row, int col) const
591 {
592     Q_D(const QTextTable);
593     if (d->dirty)
594         d->update();
595
596     if (row < 0 || row >= d->nRows || col < 0 || col >= d->nCols)
597         return QTextTableCell();
598
599     return QTextTableCell(this, d->grid[row*d->nCols + col]);
600 }
601
602 /*!
603     \overload
604
605     Returns the table cell that contains the character at the given \a position
606     in the document.
607 */
608 QTextTableCell QTextTable::cellAt(int position) const
609 {
610     Q_D(const QTextTable);
611     if (d->dirty)
612         d->update();
613
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();
618
619     QFragmentFindHelper helper(position, map);
620     QList<int>::ConstIterator it = qLowerBound(d->cells.begin(), d->cells.end(), helper);
621     if (it != d->cells.begin())
622         --it;
623
624     return QTextTableCell(this, *it);
625 }
626
627 /*!
628     \fn QTextTableCell QTextTable::cellAt(const QTextCursor &cursor) const
629
630     \overload
631
632     Returns the table cell containing the given \a cursor.
633 */
634 QTextTableCell QTextTable::cellAt(const QTextCursor &c) const
635 {
636     return cellAt(c.position());
637 }
638
639 /*!
640     \fn void QTextTable::resize(int rows, int columns)
641
642     Resizes the table to contain the required number of \a rows and \a columns.
643
644     \sa insertRows(), insertColumns(), removeRows(), removeColumns()
645 */
646 void QTextTable::resize(int rows, int cols)
647 {
648     Q_D(QTextTable);
649     if (d->dirty)
650         d->update();
651
652     int nRows = this->rows();
653     int nCols = this->columns();
654
655     if (rows == nRows && cols == nCols)
656         return;
657
658     d->pieceTable->beginEditBlock();
659
660     if (nCols < cols)
661         insertColumns(nCols, cols - nCols);
662     else if (nCols > cols)
663         removeColumns(cols, nCols - cols);
664
665     if (nRows < rows)
666         insertRows(nRows, rows-nRows);
667     else if (nRows > rows)
668         removeRows(rows, nRows-rows);
669
670     d->pieceTable->endEditBlock();
671 }
672
673 /*!
674     \fn void QTextTable::insertRows(int index, int rows)
675
676     Inserts a number of \a rows before the row with the specified \a index.
677
678     \sa resize(), insertColumns(), removeRows(), removeColumns(), appendRows(), appendColumns()
679 */
680 void QTextTable::insertRows(int pos, int num)
681 {
682     Q_D(QTextTable);
683     if (num <= 0)
684         return;
685
686     if (d->dirty)
687         d->update();
688
689     if (pos > d->nRows || pos < 0)
690         pos = d->nRows;
691
692 //     qDebug() << "-------- insertRows" << pos << num;
693     QTextDocumentPrivate *p = d->pieceTable;
694     QTextFormatCollection *c = p->formatCollection();
695     p->beginEditBlock();
696
697     int extended = 0;
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);
708                 extended++;
709             } else if (!insert_before) {
710                 insert_before = cell;
711             }
712         }
713     } else {
714         insert_before = (pos == 0 ? d->grid[0] : d->fragment_end);
715     }
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);
729     }
730
731 //     qDebug() << "-------- end insertRows" << pos << num;
732     p->endEditBlock();
733 }
734
735 /*!
736     \fn void QTextTable::insertColumns(int index, int columns)
737
738     Inserts a number of \a columns before the column with the specified \a index.
739
740     \sa insertRows(), resize(), removeRows(), removeColumns(), appendRows(), appendColumns()
741 */
742 void QTextTable::insertColumns(int pos, int num)
743 {
744     Q_D(QTextTable);
745     if (num <= 0)
746         return;
747
748     if (d->dirty)
749         d->update();
750
751     if (pos > d->nCols || pos < 0)
752         pos = d->nCols;
753
754 //     qDebug() << "-------- insertCols" << pos << num;
755     QTextDocumentPrivate *p = d->pieceTable;
756     QTextFormatCollection *c = p->formatCollection();
757     p->beginEditBlock();
758
759     QList<int> extendedSpans;
760     for (int i = 0; i < d->nRows; ++i) {
761         int cell;
762         if (i == d->nRows - 1 && pos == d->nCols)
763             cell = d->fragment_end;
764         else
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);
773                 d->dirty = true;
774                 extendedSpans << cell;
775             }
776         } else {
777             /* If the next cell is spanned from the row above, we need to find the right position
778             to insert to */
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]) {
783                     ++gridIndex;
784                 }
785                 if (gridIndex == gridEnd)
786                     cell = d->fragment_end;
787                 else
788                     cell = d->grid[gridIndex];
789             }
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);
800         }
801     }
802
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)]);
809     }
810     tfmt.setColumnWidthConstraints (columnWidths);
811     QTextObject::setFormat(tfmt);
812
813 //     qDebug() << "-------- end insertCols" << pos << num;
814     p->endEditBlock();
815 }
816
817 /*!
818     \since 4.5
819     Appends \a count rows at the bottom of the table.
820
821     \sa insertColumns(), insertRows(), resize(), removeRows(), removeColumns(), appendColumns()
822 */
823 void QTextTable::appendRows(int count)
824 {
825     insertRows(rows(), count);
826 }
827
828 /*!
829     \since 4.5
830     Appends \a count columns at the right side of the table.
831
832     \sa insertColumns(), insertRows(), resize(), removeRows(), removeColumns(), appendRows()
833 */
834 void QTextTable::appendColumns(int count)
835 {
836     insertColumns(columns(), count);
837 }
838
839 /*!
840     \fn void QTextTable::removeRows(int index, int rows)
841
842     Removes a number of \a rows starting with the row at the specified \a index.
843
844     \sa insertRows(), insertColumns(), resize(), removeColumns(), appendRows(), appendColumns()
845 */
846 void QTextTable::removeRows(int pos, int num)
847 {
848     Q_D(QTextTable);
849 //     qDebug() << "-------- removeRows" << pos << num;
850
851     if (num <= 0 || pos < 0)
852         return;
853     if (d->dirty)
854         d->update();
855     if (pos >= d->nRows)
856         return;
857     if (pos+num > d->nRows)
858         num = d->nRows - pos;
859
860     QTextDocumentPrivate *p = d->pieceTable;
861     QTextFormatCollection *collection = p->formatCollection();
862     p->beginEditBlock();
863
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);
868         p->endEditBlock();
869         return;
870     }
871
872     p->aboutToRemoveCell(cellAt(pos, 0).firstPosition(), cellAt(pos + num - 1, d->nCols - 1).lastPosition());
873
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))
879                 continue;
880             touchedCells << cell;
881             QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
882             QTextCharFormat fmt = collection->charFormat(it->format);
883             int span = fmt.tableCellRowSpan();
884             if (span > 1) {
885                 fmt.setTableCellRowSpan(span - 1);
886                 p->setCharFormat(it.position(), 1, fmt);
887             } else {
888                 // remove cell
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());
892             }
893         }
894     }
895
896     p->endEditBlock();
897 //     qDebug() << "-------- end removeRows" << pos << num;
898 }
899
900 /*!
901     \fn void QTextTable::removeColumns(int index, int columns)
902
903     Removes a number of \a columns starting with the column at the specified
904     \a index.
905
906     \sa insertRows(), insertColumns(), removeRows(), resize(), appendRows(), appendColumns()
907 */
908 void QTextTable::removeColumns(int pos, int num)
909 {
910     Q_D(QTextTable);
911 //     qDebug() << "-------- removeCols" << pos << num;
912
913     if (num <= 0 || pos < 0)
914         return;
915     if (d->dirty)
916         d->update();
917     if (pos >= d->nCols)
918         return;
919     if (pos + num > d->nCols)
920         pos = d->nCols - num;
921
922     QTextDocumentPrivate *p = d->pieceTable;
923     QTextFormatCollection *collection = p->formatCollection();
924     p->beginEditBlock();
925
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);
930         p->endEditBlock();
931         return;
932     }
933
934     p->aboutToRemoveCell(cellAt(0, pos).firstPosition(), cellAt(d->nRows - 1, pos + num - 1).lastPosition());
935
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)
944                 continue;
945             touchedCells << cell;
946
947             if (span > 1) {
948                 fmt.setTableCellColumnSpan(span - 1);
949                 p->setCharFormat(it.position(), 1, fmt);
950             } else {
951                 // remove cell
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());
955             }
956         }
957     }
958
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);
965     }
966     QTextObject::setFormat(tfmt);
967
968     p->endEditBlock();
969 //     qDebug() << "-------- end removeCols" << pos << num;
970 }
971
972 /*!
973     \since 4.1
974
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.
979
980     \sa splitCell()
981 */
982 void QTextTable::mergeCells(int row, int column, int numRows, int numCols)
983 {
984     Q_D(QTextTable);
985
986     if (d->dirty)
987         d->update();
988
989     QTextDocumentPrivate *p = d->pieceTable;
990     QTextFormatCollection *fc = p->formatCollection();
991
992     const QTextTableCell cell = cellAt(row, column);
993     if (!cell.isValid() || row != cell.row() || column != cell.column())
994         return;
995
996     QTextCharFormat fmt = cell.format();
997     const int rowSpan = fmt.tableCellRowSpan();
998     const int colSpan = fmt.tableCellColumnSpan();
999
1000     numRows = qMin(numRows, rows() - cell.row());
1001     numCols = qMin(numCols, columns() - cell.column());
1002
1003     // nothing to merge?
1004     if (numRows < rowSpan || numCols < colSpan)
1005         return;
1006
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))
1010             return;
1011         if (cellAt(r, column + numCols) == cellAt(r, column + numCols - 1))
1012             return;
1013     }
1014
1015     for (int c = column; c < column + numCols; ++c) {
1016         if (cellAt(row, c) == cellAt(row - 1, c))
1017             return;
1018         if (cellAt(row + numRows, c) == cellAt(row + numRows - 1, c))
1019             return;
1020     }
1021
1022     p->beginEditBlock();
1023
1024     const int origCellPosition = cell.firstPosition() - 1;
1025
1026     const int cellFragment = d->grid[row * d->nCols + column];
1027
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);
1036
1037     d->blockFragmentUpdates = true;
1038
1039     bool rowHasText = cell.firstCursorPosition().block().length();
1040     bool needsParagraph = rowHasText && colSpan == numCols;
1041
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;
1045
1046         // don't recompute the cell index for the first row
1047         int firstCellIndex = r == row ? insertCellIndex + 1 : -1;
1048         int cellIndex = firstCellIndex;
1049
1050         for (int c = firstColumn; c < column + numCols; ++c) {
1051             const int fragment = d->grid[r * d->nCols + c];
1052
1053             // already handled?
1054             if (fragment == cellFragment)
1055                 continue;
1056
1057             QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), fragment);
1058             uint pos = it.position();
1059
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();
1066             }
1067
1068             ++cellIndex;
1069
1070             QTextCharFormat fmt = fc->charFormat(it->format);
1071
1072             const int cellRowSpan = fmt.tableCellRowSpan();
1073             const int cellColSpan = fmt.tableCellColumnSpan();
1074
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;
1079
1080             // erase the cell marker
1081             p->remove(pos, 1);
1082
1083             const int nextFragment = d->cells.value(cellIndex, d->fragment_end);
1084             const uint nextPos = p->fragmentMap().position(nextFragment);
1085
1086             Q_ASSERT(nextPos >= pos);
1087
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);
1097                 } else {
1098                     p->move(pos, insertPos, nextPos - pos);
1099                 }
1100
1101                 insertPos += nextPos - pos;
1102                 rowHasText = true;
1103             }
1104         }
1105
1106         if (rowHasText) {
1107             needsParagraph = true;
1108             rowHasText = false;
1109         }
1110
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);
1115         }
1116     }
1117
1118     d->fragment_start = d->cells.first();
1119
1120     fmt.setTableCellRowSpan(numRows);
1121     fmt.setTableCellColumnSpan(numCols);
1122     p->setCharFormat(origCellPosition, 1, fmt);
1123
1124     d->blockFragmentUpdates = false;
1125     d->dirty = false;
1126
1127     p->endEditBlock();
1128 }
1129
1130 /*!
1131     \overload
1132     \since 4.1
1133
1134     Merges the cells selected by the provided \a cursor.
1135
1136     \sa splitCell()
1137 */
1138 void QTextTable::mergeCells(const QTextCursor &cursor)
1139 {
1140     if (!cursor.hasComplexSelection())
1141         return;
1142
1143     int firstRow, numRows, firstColumn, numColumns;
1144     cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
1145     mergeCells(firstRow, firstColumn, numRows, numColumns);
1146 }
1147
1148 /*!
1149     \since 4.1
1150
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.
1153
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().
1156
1157     \sa mergeCells()
1158 */
1159 void QTextTable::splitCell(int row, int column, int numRows, int numCols)
1160 {
1161     Q_D(QTextTable);
1162
1163     if (d->dirty)
1164         d->update();
1165
1166     QTextDocumentPrivate *p = d->pieceTable;
1167     QTextFormatCollection *c = p->formatCollection();
1168
1169     const QTextTableCell cell = cellAt(row, column);
1170     if (!cell.isValid())
1171         return;
1172     row = cell.row();
1173     column = cell.column();
1174
1175     QTextCharFormat fmt = cell.format();
1176     const int rowSpan = fmt.tableCellRowSpan();
1177     const int colSpan = fmt.tableCellColumnSpan();
1178
1179     // nothing to split?
1180     if (numRows > rowSpan || numCols > colSpan)
1181         return;
1182
1183     p->beginEditBlock();
1184
1185     const int origCellPosition = cell.firstPosition() - 1;
1186
1187     QVarLengthArray<int> rowPositions(rowSpan);
1188
1189     rowPositions[0] = cell.lastPosition();
1190
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);
1198     }
1199
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;
1204
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;
1210     }
1211
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;
1216     }
1217
1218     fmt.setTableCellRowSpan(numRows);
1219     fmt.setTableCellColumnSpan(numCols);
1220     p->setCharFormat(origCellPosition, 1, fmt);
1221
1222     p->endEditBlock();
1223 }
1224
1225 /*!
1226     Returns the number of rows in the table.
1227
1228     \sa columns()
1229 */
1230 int QTextTable::rows() const
1231 {
1232     Q_D(const QTextTable);
1233     if (d->dirty)
1234         d->update();
1235
1236     return d->nRows;
1237 }
1238
1239 /*!
1240     Returns the number of columns in the table.
1241
1242     \sa rows()
1243 */
1244 int QTextTable::columns() const
1245 {
1246     Q_D(const QTextTable);
1247     if (d->dirty)
1248         d->update();
1249
1250     return d->nCols;
1251 }
1252
1253 #if 0
1254 void QTextTable::mergeCells(const QTextCursor &selection)
1255 {
1256 }
1257 #endif
1258
1259 /*!
1260     \fn QTextCursor QTextTable::rowStart(const QTextCursor &cursor) const
1261
1262     Returns a cursor pointing to the start of the row that contains the
1263     given \a cursor.
1264
1265     \sa rowEnd()
1266 */
1267 QTextCursor QTextTable::rowStart(const QTextCursor &c) const
1268 {
1269     Q_D(const QTextTable);
1270     QTextTableCell cell = cellAt(c);
1271     if (!cell.isValid())
1272         return QTextCursor();
1273
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());
1278 }
1279
1280 /*!
1281     \fn QTextCursor QTextTable::rowEnd(const QTextCursor &cursor) const
1282
1283     Returns a cursor pointing to the end of the row that contains the given
1284     \a cursor.
1285
1286     \sa rowStart()
1287 */
1288 QTextCursor QTextTable::rowEnd(const QTextCursor &c) const
1289 {
1290     Q_D(const QTextTable);
1291     QTextTableCell cell = cellAt(c);
1292     if (!cell.isValid())
1293         return QTextCursor();
1294
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);
1300 }
1301
1302 /*!
1303     \fn void QTextTable::setFormat(const QTextTableFormat &format)
1304
1305     Sets the table's \a format.
1306
1307     \sa format()
1308 */
1309 void QTextTable::setFormat(const QTextTableFormat &format)
1310 {
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);
1315 }
1316
1317 /*!
1318     \fn QTextTableFormat QTextTable::format() const
1319
1320     Returns the table's format.
1321
1322     \sa setFormat()
1323 */
1324
1325 QT_END_NAMESPACE