Fix uses of qRound on non-floating-point types.
[profile/ivi/qtbase.git] / src / gui / text / qtexttable.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtGui module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
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
61     \ingroup richtext-processing
62
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.
66
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
69     a table is resized.
70
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()
73     and columnSpan().
74
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.
78
79     \sa QTextTable QTextTableFormat
80 */
81
82 /*!
83     \fn QTextTableCell::QTextTableCell()
84
85     Constructs an invalid table cell.
86
87     \sa isValid()
88 */
89
90 /*!
91     \fn QTextTableCell::QTextTableCell(const QTextTableCell &other)
92
93     Copy constructor. Creates a new QTextTableCell object based on the
94     \a other cell.
95 */
96
97 /*!
98     \fn QTextTableCell& QTextTableCell::operator=(const QTextTableCell &other)
99
100     Assigns the \a other table cell to this table cell.
101 */
102
103 /*!
104     \since 4.2
105
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:
108
109     QTextTableCell cell = table->cellAt(2, 3);
110     QTextCharFormat format = cell.format();
111     format.setBackground(Qt::blue);
112     cell.setFormat(format);
113
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.
116
117     \sa format()
118 */
119 void QTextTableCell::setFormat(const QTextCharFormat &format)
120 {
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);
126
127     QTextFormatCollection *c = p->formatCollection();
128     QTextCharFormat oldFormat = c->charFormat(frag->format);
129     fmt.setTableCellRowSpan(oldFormat.tableCellRowSpan());
130     fmt.setTableCellColumnSpan(oldFormat.tableCellColumnSpan());
131
132     p->setCharFormat(frag.position(), 1, fmt, QTextDocumentPrivate::SetFormatAndPreserveObjectIndices);
133 }
134
135 /*!
136     Returns the cell's character format.
137 */
138 QTextCharFormat QTextTableCell::format() const
139 {
140     QTextDocumentPrivate *p = table->docHandle();
141     QTextFormatCollection *c = p->formatCollection();
142
143     QTextCharFormat fmt = c->charFormat(tableCellFormatIndex());
144     fmt.setObjectType(QTextFormat::TableCellObject);
145     return fmt;
146 }
147
148 /*!
149     \since 4.5
150
151     Returns the index of the tableCell's format in the document's internal list of formats.
152
153     \sa QTextDocument::allFormats()
154 */
155 int QTextTableCell::tableCellFormatIndex() const
156 {
157     QTextDocumentPrivate *p = table->docHandle();
158     return QTextDocumentPrivate::FragmentIterator(&p->fragmentMap(), fragment)->format;
159 }
160
161 /*!
162     Returns the number of the row in the table that contains this cell.
163
164     \sa column()
165 */
166 int QTextTableCell::row() const
167 {
168     const QTextTablePrivate *tp = table->d_func();
169     if (tp->dirty)
170         tp->update();
171
172     int idx = tp->findCellIndex(fragment);
173     if (idx == -1)
174         return idx;
175     return tp->cellIndices.at(idx) / tp->nCols;
176 }
177
178 /*!
179     Returns the number of the column in the table that contains this cell.
180
181     \sa row()
182 */
183 int QTextTableCell::column() const
184 {
185     const QTextTablePrivate *tp = table->d_func();
186     if (tp->dirty)
187         tp->update();
188
189     int idx = tp->findCellIndex(fragment);
190     if (idx == -1)
191         return idx;
192     return tp->cellIndices.at(idx) % tp->nCols;
193 }
194
195 /*!
196     Returns the number of rows this cell spans. The default is 1.
197
198     \sa columnSpan()
199 */
200 int QTextTableCell::rowSpan() const
201 {
202     return format().tableCellRowSpan();
203 }
204
205 /*!
206     Returns the number of columns this cell spans. The default is 1.
207
208     \sa rowSpan()
209 */
210 int QTextTableCell::columnSpan() const
211 {
212     return format().tableCellColumnSpan();
213 }
214
215 /*!
216     \fn bool QTextTableCell::isValid() const
217
218     Returns true if this is a valid table cell; otherwise returns
219     false.
220 */
221
222
223 /*!
224     Returns the first valid cursor position in this cell.
225
226     \sa lastCursorPosition()
227 */
228 QTextCursor QTextTableCell::firstCursorPosition() const
229 {
230     return QTextCursor(table->d_func()->pieceTable, firstPosition());
231 }
232
233 /*!
234     Returns the last valid cursor position in this cell.
235
236     \sa firstCursorPosition()
237 */
238 QTextCursor QTextTableCell::lastCursorPosition() const
239 {
240     return QTextCursor(table->d_func()->pieceTable, lastPosition());
241 }
242
243
244 /*!
245     \internal
246
247     Returns the first valid position in the document occupied by this cell.
248 */
249 int QTextTableCell::firstPosition() const
250 {
251     QTextDocumentPrivate *p = table->docHandle();
252     return p->fragmentMap().position(fragment) + 1;
253 }
254
255 /*!
256     \internal
257
258     Returns the last valid position in the document occupied by this cell.
259 */
260 int QTextTableCell::lastPosition() const
261 {
262     QTextDocumentPrivate *p = table->docHandle();
263     const QTextTablePrivate *td = table->d_func();
264     int index = table->d_func()->findCellIndex(fragment);
265     int f;
266     if (index != -1)
267         f = td->cells.value(index + 1, td->fragment_end);
268     else
269         f = td->fragment_end;
270     return p->fragmentMap().position(f);
271 }
272
273
274 /*!
275     Returns a frame iterator pointing to the beginning of the table's cell.
276
277     \sa end()
278 */
279 QTextFrame::iterator QTextTableCell::begin() const
280 {
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);
285 }
286
287 /*!
288     Returns a frame iterator pointing to the end of the table's cell.
289
290     \sa begin()
291 */
292 QTextFrame::iterator QTextTableCell::end() const
293 {
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);
298 }
299
300
301 /*!
302     \fn QTextCursor QTextTableCell::operator==(const QTextTableCell &other) const
303
304     Returns true if this cell object and the \a other cell object
305     describe the same cell; otherwise returns false.
306 */
307
308 /*!
309     \fn QTextCursor QTextTableCell::operator!=(const QTextTableCell &other) const
310
311     Returns true if this cell object and the \a other cell object
312     describe different cells; otherwise returns false.
313 */
314
315 /*!
316     \fn QTextTableCell::~QTextTableCell()
317
318     Destroys the table cell.
319 */
320
321 QTextTablePrivate::~QTextTablePrivate()
322 {
323     if (grid)
324         free(grid);
325 }
326
327
328 QTextTable *QTextTablePrivate::createTable(QTextDocumentPrivate *pieceTable, int pos, int rows, int cols, const QTextTableFormat &tableFormat)
329 {
330     QTextTableFormat fmt = tableFormat;
331     fmt.setColumns(cols);
332     QTextTable *table = qobject_cast<QTextTable *>(pieceTable->createObject(fmt));
333     Q_ASSERT(table);
334
335     pieceTable->beginEditBlock();
336
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);
342
343
344     int charIdx = pieceTable->formatCollection()->indexForFormat(charFmt);
345     int cellIdx = pieceTable->formatCollection()->indexForFormat(QTextBlockFormat());
346
347     QTextTablePrivate *d = table->d_func();
348     d->blockFragmentUpdates = true;
349
350     d->fragment_start = pieceTable->insertBlock(QTextBeginningOfFrame, pos, cellIdx, charIdx);
351     d->cells.append(d->fragment_start);
352     ++pos;
353
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);
357         ++pos;
358     }
359
360     d->fragment_end = pieceTable->insertBlock(QTextEndOfFrame, pos, cellIdx, charIdx);
361 //      qDebug("      addEOR at %d", pos);
362     ++pos;
363
364     d->blockFragmentUpdates = false;
365     d->dirty = true;
366
367     pieceTable->endEditBlock();
368
369     return table;
370 }
371
372 struct QFragmentFindHelper
373 {
374     inline QFragmentFindHelper(int _pos, const QTextDocumentPrivate::FragmentMap &map)
375         : pos(_pos), fragmentMap(map) {}
376     uint pos;
377     const QTextDocumentPrivate::FragmentMap &fragmentMap;
378 };
379
380 Q_STATIC_GLOBAL_INLINE_OPERATOR bool operator<(int fragment, const QFragmentFindHelper &helper)
381 {
382     return helper.fragmentMap.position(fragment) < helper.pos;
383 }
384
385 Q_STATIC_GLOBAL_INLINE_OPERATOR bool operator<(const QFragmentFindHelper &helper, int fragment)
386 {
387     return helper.pos < helper.fragmentMap.position(fragment);
388 }
389
390 int QTextTablePrivate::findCellIndex(int fragment) const
391 {
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())
396         return -1;
397     return it - cells.begin();
398 }
399
400 void QTextTablePrivate::fragmentAdded(const QChar &type, uint fragment)
401 {
402     dirty = true;
403     if (blockFragmentUpdates)
404         return;
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;
413         return;
414     }
415     QTextFramePrivate::fragmentAdded(type, fragment);
416 }
417
418 void QTextTablePrivate::fragmentRemoved(const QChar &type, uint fragment)
419 {
420     dirty = true;
421     if (blockFragmentUpdates)
422         return;
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);
428         }
429         if (fragment_start != fragment)
430             return;
431     }
432     QTextFramePrivate::fragmentRemoved(type, fragment);
433 }
434
435 /*!
436     /fn void QTextTablePrivate::update() const
437
438     This function is usually called when the table is "dirty".
439     It seems to update all kind of table information.
440
441 */
442 void QTextTablePrivate::update() const
443 {
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);
448
449     grid = q_check_ptr((int *)realloc(grid, nRows*nCols*sizeof(int)));
450     memset(grid, 0, nRows*nCols*sizeof(int));
451
452     QTextDocumentPrivate *p = pieceTable;
453     QTextFormatCollection *c = p->formatCollection();
454
455     cellIndices.resize(cells.size());
456
457     int cell = 0;
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();
463
464         // skip taken cells
465         while (cell < nRows*nCols && grid[cell])
466             ++cell;
467
468         int r = cell/nCols;
469         int c = cell%nCols;
470         cellIndices[i] = cell;
471
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);
475             nRows = r + rowspan;
476         }
477
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);
484             }
485         }
486     }
487 //     qDebug("<<<< end: nRows=%d, nCols=%d", nRows, nCols);
488
489     dirty = false;
490 }
491
492
493
494
495
496 /*!
497     \class QTextTable
498     \reentrant
499
500     \brief The QTextTable class represents a table in a QTextDocument.
501
502     \ingroup richtext-processing
503
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.
507
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:
512
513     \snippet doc/src/snippets/textdocument-tables/mainwindow.cpp 1
514     \codeline
515     \snippet doc/src/snippets/textdocument-tables/mainwindow.cpp 3
516
517     The table format is either defined when the table is created or changed
518     later with setFormat().
519
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.
523
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.
527
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.
531
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.) 
536
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.
541
542     \table 80%
543     \row
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
549
550     \row
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
554         and \a numCols = 1.
555         \snippet doc/src/snippets/textdocument-texttable/main.cpp 1
556
557     \row
558         \o \inlineimage texttable-split.png Split Table
559         \o This results in the original table.
560     \endtable
561
562     \sa QTextTableFormat
563 */
564
565 /*! \internal
566  */
567 QTextTable::QTextTable(QTextDocument *doc)
568     : QTextFrame(*new QTextTablePrivate(doc), doc)
569 {
570 }
571
572 /*! \internal
573
574 Destroys the table.
575  */
576 QTextTable::~QTextTable()
577 {
578 }
579
580
581 /*!
582     \fn QTextTableCell QTextTable::cellAt(int row, int column) const
583
584     Returns the table cell at the given \a row and \a column in the table.
585
586     \sa columns() rows()
587 */
588 QTextTableCell QTextTable::cellAt(int row, int col) const
589 {
590     Q_D(const QTextTable);
591     if (d->dirty)
592         d->update();
593
594     if (row < 0 || row >= d->nRows || col < 0 || col >= d->nCols)
595         return QTextTableCell();
596
597     return QTextTableCell(this, d->grid[row*d->nCols + col]);
598 }
599
600 /*!
601     \overload
602
603     Returns the table cell that contains the character at the given \a position
604     in the document.
605 */
606 QTextTableCell QTextTable::cellAt(int position) const
607 {
608     Q_D(const QTextTable);
609     if (d->dirty)
610         d->update();
611
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();
616
617     QFragmentFindHelper helper(position, map);
618     QList<int>::ConstIterator it = qLowerBound(d->cells.begin(), d->cells.end(), helper);
619     if (it != d->cells.begin())
620         --it;
621
622     return QTextTableCell(this, *it);
623 }
624
625 /*!
626     \fn QTextTableCell QTextTable::cellAt(const QTextCursor &cursor) const
627
628     \overload
629
630     Returns the table cell containing the given \a cursor.
631 */
632 QTextTableCell QTextTable::cellAt(const QTextCursor &c) const
633 {
634     return cellAt(c.position());
635 }
636
637 /*!
638     \fn void QTextTable::resize(int rows, int columns)
639
640     Resizes the table to contain the required number of \a rows and \a columns.
641
642     \sa insertRows() insertColumns() removeRows() removeColumns()
643 */
644 void QTextTable::resize(int rows, int cols)
645 {
646     Q_D(QTextTable);
647     if (d->dirty)
648         d->update();
649
650     int nRows = this->rows();
651     int nCols = this->columns();
652
653     if (rows == nRows && cols == nCols)
654         return;
655
656     d->pieceTable->beginEditBlock();
657
658     if (nCols < cols)
659         insertColumns(nCols, cols - nCols);
660     else if (nCols > cols)
661         removeColumns(cols, nCols - cols);
662
663     if (nRows < rows)
664         insertRows(nRows, rows-nRows);
665     else if (nRows > rows)
666         removeRows(rows, nRows-rows);
667
668     d->pieceTable->endEditBlock();
669 }
670
671 /*!
672     \fn void QTextTable::insertRows(int index, int rows)
673
674     Inserts a number of \a rows before the row with the specified \a index.
675
676     \sa resize() insertColumns() removeRows() removeColumns() appendRows() appendColumns()
677 */
678 void QTextTable::insertRows(int pos, int num)
679 {
680     Q_D(QTextTable);
681     if (num <= 0)
682         return;
683
684     if (d->dirty)
685         d->update();
686
687     if (pos > d->nRows || pos < 0)
688         pos = d->nRows;
689
690 //     qDebug() << "-------- insertRows" << pos << num;
691     QTextDocumentPrivate *p = d->pieceTable;
692     QTextFormatCollection *c = p->formatCollection();
693     p->beginEditBlock();
694
695     int extended = 0;
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);
706                 extended++;
707             } else if (!insert_before) {
708                 insert_before = cell;
709             }
710         }
711     } else {
712         insert_before = (pos == 0 ? d->grid[0] : d->fragment_end);
713     }
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);
727     }
728
729 //     qDebug() << "-------- end insertRows" << pos << num;
730     p->endEditBlock();
731 }
732
733 /*!
734     \fn void QTextTable::insertColumns(int index, int columns)
735
736     Inserts a number of \a columns before the column with the specified \a index.
737
738     \sa insertRows() resize() removeRows() removeColumns() appendRows() appendColumns()
739 */
740 void QTextTable::insertColumns(int pos, int num)
741 {
742     Q_D(QTextTable);
743     if (num <= 0)
744         return;
745
746     if (d->dirty)
747         d->update();
748
749     if (pos > d->nCols || pos < 0)
750         pos = d->nCols;
751
752 //     qDebug() << "-------- insertCols" << pos << num;
753     QTextDocumentPrivate *p = d->pieceTable;
754     QTextFormatCollection *c = p->formatCollection();
755     p->beginEditBlock();
756
757     QList<int> extendedSpans;
758     for (int i = 0; i < d->nRows; ++i) {
759         int cell;
760         if (i == d->nRows - 1 && pos == d->nCols)
761             cell = d->fragment_end;
762         else
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);
771                 d->dirty = true;
772                 extendedSpans << cell;
773             }
774         } else {
775             /* If the next cell is spanned from the row above, we need to find the right position
776             to insert to */
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]) {
781                     ++gridIndex;
782                 }
783                 if (gridIndex == gridEnd)
784                     cell = d->fragment_end;
785                 else
786                     cell = d->grid[gridIndex];
787             }
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);
798         }
799     }
800
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)]);
807     }
808     tfmt.setColumnWidthConstraints (columnWidths);
809     QTextObject::setFormat(tfmt);
810
811 //     qDebug() << "-------- end insertCols" << pos << num;
812     p->endEditBlock();
813 }
814
815 /*!
816     \since 4.5
817     Appends \a count rows at the bottom of the table.
818
819     \sa insertColumns() insertRows() resize() removeRows() removeColumns() appendColumns()
820 */
821 void QTextTable::appendRows(int count)
822 {
823     insertRows(rows(), count);
824 }
825
826 /*!
827     \since 4.5
828     Appends \a count columns at the right side of the table.
829
830     \sa insertColumns() insertRows() resize() removeRows() removeColumns() appendRows()
831 */
832 void QTextTable::appendColumns(int count)
833 {
834     insertColumns(columns(), count);
835 }
836
837 /*!
838     \fn void QTextTable::removeRows(int index, int rows)
839
840     Removes a number of \a rows starting with the row at the specified \a index.
841
842     \sa insertRows(), insertColumns(), resize(), removeColumns() appendRows() appendColumns()
843 */
844 void QTextTable::removeRows(int pos, int num)
845 {
846     Q_D(QTextTable);
847 //     qDebug() << "-------- removeRows" << pos << num;
848
849     if (num <= 0 || pos < 0)
850         return;
851     if (d->dirty)
852         d->update();
853     if (pos >= d->nRows)
854         return;
855     if (pos+num > d->nRows)
856         num = d->nRows - pos;
857
858     QTextDocumentPrivate *p = d->pieceTable;
859     QTextFormatCollection *collection = p->formatCollection();
860     p->beginEditBlock();
861
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);
866         p->endEditBlock();
867         return;
868     }
869
870     p->aboutToRemoveCell(cellAt(pos, 0).firstPosition(), cellAt(pos + num - 1, d->nCols - 1).lastPosition());
871
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))
877                 continue;
878             touchedCells << cell;
879             QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
880             QTextCharFormat fmt = collection->charFormat(it->format);
881             int span = fmt.tableCellRowSpan();
882             if (span > 1) {
883                 fmt.setTableCellRowSpan(span - 1);
884                 p->setCharFormat(it.position(), 1, fmt);
885             } else {
886                 // remove cell
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());
890             }
891         }
892     }
893
894     p->endEditBlock();
895 //     qDebug() << "-------- end removeRows" << pos << num;
896 }
897
898 /*!
899     \fn void QTextTable::removeColumns(int index, int columns)
900
901     Removes a number of \a columns starting with the column at the specified
902     \a index.
903
904     \sa insertRows() insertColumns() removeRows() resize() appendRows() appendColumns()
905 */
906 void QTextTable::removeColumns(int pos, int num)
907 {
908     Q_D(QTextTable);
909 //     qDebug() << "-------- removeCols" << pos << num;
910
911     if (num <= 0 || pos < 0)
912         return;
913     if (d->dirty)
914         d->update();
915     if (pos >= d->nCols)
916         return;
917     if (pos + num > d->nCols)
918         pos = d->nCols - num;
919
920     QTextDocumentPrivate *p = d->pieceTable;
921     QTextFormatCollection *collection = p->formatCollection();
922     p->beginEditBlock();
923
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);
928         p->endEditBlock();
929         return;
930     }
931
932     p->aboutToRemoveCell(cellAt(0, pos).firstPosition(), cellAt(d->nRows - 1, pos + num - 1).lastPosition());
933
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)
942                 continue;
943             touchedCells << cell;
944
945             if (span > 1) {
946                 fmt.setTableCellColumnSpan(span - 1);
947                 p->setCharFormat(it.position(), 1, fmt);
948             } else {
949                 // remove cell
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());
953             }
954         }
955     }
956
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);
963     }
964     QTextObject::setFormat(tfmt);
965
966     p->endEditBlock();
967 //     qDebug() << "-------- end removeCols" << pos << num;
968 }
969
970 /*!
971     \since 4.1
972
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.
977
978     \sa splitCell()
979 */
980 void QTextTable::mergeCells(int row, int column, int numRows, int numCols)
981 {
982     Q_D(QTextTable);
983
984     if (d->dirty)
985         d->update();
986
987     QTextDocumentPrivate *p = d->pieceTable;
988     QTextFormatCollection *fc = p->formatCollection();
989
990     const QTextTableCell cell = cellAt(row, column);
991     if (!cell.isValid() || row != cell.row() || column != cell.column())
992         return;
993
994     QTextCharFormat fmt = cell.format();
995     const int rowSpan = fmt.tableCellRowSpan();
996     const int colSpan = fmt.tableCellColumnSpan();
997
998     numRows = qMin(numRows, rows() - cell.row());
999     numCols = qMin(numCols, columns() - cell.column());
1000
1001     // nothing to merge?
1002     if (numRows < rowSpan || numCols < colSpan)
1003         return;
1004
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))
1008             return;
1009         if (cellAt(r, column + numCols) == cellAt(r, column + numCols - 1))
1010             return;
1011     }
1012
1013     for (int c = column; c < column + numCols; ++c) {
1014         if (cellAt(row, c) == cellAt(row - 1, c))
1015             return;
1016         if (cellAt(row + numRows, c) == cellAt(row + numRows - 1, c))
1017             return;
1018     }
1019
1020     p->beginEditBlock();
1021
1022     const int origCellPosition = cell.firstPosition() - 1;
1023
1024     const int cellFragment = d->grid[row * d->nCols + column];
1025
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);
1034
1035     d->blockFragmentUpdates = true;
1036
1037     bool rowHasText = cell.firstCursorPosition().block().length();
1038     bool needsParagraph = rowHasText && colSpan == numCols;
1039
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;
1043
1044         // don't recompute the cell index for the first row
1045         int firstCellIndex = r == row ? insertCellIndex + 1 : -1;
1046         int cellIndex = firstCellIndex;
1047
1048         for (int c = firstColumn; c < column + numCols; ++c) {
1049             const int fragment = d->grid[r * d->nCols + c];
1050
1051             // already handled?
1052             if (fragment == cellFragment)
1053                 continue;
1054
1055             QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), fragment);
1056             uint pos = it.position();
1057
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();
1064             }
1065
1066             ++cellIndex;
1067
1068             QTextCharFormat fmt = fc->charFormat(it->format);
1069
1070             const int cellRowSpan = fmt.tableCellRowSpan();
1071             const int cellColSpan = fmt.tableCellColumnSpan();
1072
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;
1077
1078             // erase the cell marker
1079             p->remove(pos, 1);
1080
1081             const int nextFragment = d->cells.value(cellIndex, d->fragment_end);
1082             const uint nextPos = p->fragmentMap().position(nextFragment);
1083
1084             Q_ASSERT(nextPos >= pos);
1085
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);
1095                 } else {
1096                     p->move(pos, insertPos, nextPos - pos);
1097                 }
1098
1099                 insertPos += nextPos - pos;
1100                 rowHasText = true;
1101             }
1102         }
1103
1104         if (rowHasText) {
1105             needsParagraph = true;
1106             rowHasText = false;
1107         }
1108
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);
1113         }
1114     }
1115
1116     d->fragment_start = d->cells.first();
1117
1118     fmt.setTableCellRowSpan(numRows);
1119     fmt.setTableCellColumnSpan(numCols);
1120     p->setCharFormat(origCellPosition, 1, fmt);
1121
1122     d->blockFragmentUpdates = false;
1123     d->dirty = false;
1124
1125     p->endEditBlock();
1126 }
1127
1128 /*!
1129     \overload
1130     \since 4.1
1131
1132     Merges the cells selected by the provided \a cursor.
1133
1134     \sa splitCell()
1135 */
1136 void QTextTable::mergeCells(const QTextCursor &cursor)
1137 {
1138     if (!cursor.hasComplexSelection())
1139         return;
1140
1141     int firstRow, numRows, firstColumn, numColumns;
1142     cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
1143     mergeCells(firstRow, firstColumn, numRows, numColumns);
1144 }
1145
1146 /*!
1147     \since 4.1
1148
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.
1151
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().
1154
1155     \sa mergeCells()
1156 */
1157 void QTextTable::splitCell(int row, int column, int numRows, int numCols)
1158 {
1159     Q_D(QTextTable);
1160
1161     if (d->dirty)
1162         d->update();
1163
1164     QTextDocumentPrivate *p = d->pieceTable;
1165     QTextFormatCollection *c = p->formatCollection();
1166
1167     const QTextTableCell cell = cellAt(row, column);
1168     if (!cell.isValid())
1169         return;
1170     row = cell.row();
1171     column = cell.column();
1172
1173     QTextCharFormat fmt = cell.format();
1174     const int rowSpan = fmt.tableCellRowSpan();
1175     const int colSpan = fmt.tableCellColumnSpan();
1176
1177     // nothing to split?
1178     if (numRows > rowSpan || numCols > colSpan)
1179         return;
1180
1181     p->beginEditBlock();
1182
1183     const int origCellPosition = cell.firstPosition() - 1;
1184
1185     QVarLengthArray<int> rowPositions(rowSpan);
1186
1187     rowPositions[0] = cell.lastPosition();
1188
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);
1196     }
1197
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;
1202
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;
1208     }
1209
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;
1214     }
1215
1216     fmt.setTableCellRowSpan(numRows);
1217     fmt.setTableCellColumnSpan(numCols);
1218     p->setCharFormat(origCellPosition, 1, fmt);
1219
1220     p->endEditBlock();
1221 }
1222
1223 /*!
1224     Returns the number of rows in the table.
1225
1226     \sa columns()
1227 */
1228 int QTextTable::rows() const
1229 {
1230     Q_D(const QTextTable);
1231     if (d->dirty)
1232         d->update();
1233
1234     return d->nRows;
1235 }
1236
1237 /*!
1238     Returns the number of columns in the table.
1239
1240     \sa rows()
1241 */
1242 int QTextTable::columns() const
1243 {
1244     Q_D(const QTextTable);
1245     if (d->dirty)
1246         d->update();
1247
1248     return d->nCols;
1249 }
1250
1251 #if 0
1252 void QTextTable::mergeCells(const QTextCursor &selection)
1253 {
1254 }
1255 #endif
1256
1257 /*!
1258     \fn QTextCursor QTextTable::rowStart(const QTextCursor &cursor) const
1259
1260     Returns a cursor pointing to the start of the row that contains the
1261     given \a cursor.
1262
1263     \sa rowEnd()
1264 */
1265 QTextCursor QTextTable::rowStart(const QTextCursor &c) const
1266 {
1267     Q_D(const QTextTable);
1268     QTextTableCell cell = cellAt(c);
1269     if (!cell.isValid())
1270         return QTextCursor();
1271
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());
1276 }
1277
1278 /*!
1279     \fn QTextCursor QTextTable::rowEnd(const QTextCursor &cursor) const
1280
1281     Returns a cursor pointing to the end of the row that contains the given
1282     \a cursor.
1283
1284     \sa rowStart()
1285 */
1286 QTextCursor QTextTable::rowEnd(const QTextCursor &c) const
1287 {
1288     Q_D(const QTextTable);
1289     QTextTableCell cell = cellAt(c);
1290     if (!cell.isValid())
1291         return QTextCursor();
1292
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);
1298 }
1299
1300 /*!
1301     \fn void QTextTable::setFormat(const QTextTableFormat &format)
1302
1303     Sets the table's \a format.
1304
1305     \sa format()
1306 */
1307 void QTextTable::setFormat(const QTextTableFormat &format)
1308 {
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);
1313 }
1314
1315 /*!
1316     \fn QTextTableFormat QTextTable::format() const
1317
1318     Returns the table's format.
1319
1320     \sa setFormat()
1321 */
1322
1323 QT_END_NAMESPACE