Coverage Report

Created: 2026-03-12 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtbase/src/gui/text/qtexttable.cpp
Line
Count
Source
1
// Copyright (C) 2016 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4
#include "qtexttable.h"
5
#include "qtextcursor.h"
6
#include "qtextformat.h"
7
#include <qdebug.h>
8
#include "qtextcursor_p.h"
9
#include "qtexttable_p.h"
10
#include "qvarlengtharray.h"
11
12
#include <algorithm>
13
#include <stdlib.h>
14
15
QT_BEGIN_NAMESPACE
16
17
using namespace Qt::StringLiterals;
18
19
/*!
20
    \class QTextTableCell
21
    \reentrant
22
23
    \brief The QTextTableCell class represents the properties of a
24
    cell in a QTextTable.
25
    \inmodule QtGui
26
27
    \ingroup richtext-processing
28
29
    Table cells are pieces of document structure that belong to a table.
30
    The table orders cells into particular rows and columns; cells can
31
    also span multiple columns and rows.
32
33
    Cells are usually created when a table is inserted into a document with
34
    QTextCursor::insertTable(), but they are also created and destroyed when
35
    a table is resized.
36
37
    Cells contain information about their location in a table; you can
38
    obtain the row() and column() numbers of a cell, and its rowSpan()
39
    and columnSpan().
40
41
    The format() of a cell describes the default character format of its
42
    contents. The firstCursorPosition() and lastCursorPosition() functions
43
    are used to obtain the extent of the cell in the document.
44
45
    \sa QTextTable, QTextTableFormat
46
*/
47
48
/*!
49
    \fn QTextTableCell::QTextTableCell()
50
51
    Constructs an invalid table cell.
52
53
    \sa isValid()
54
*/
55
56
/*!
57
    \fn QTextTableCell::QTextTableCell(const QTextTableCell &other)
58
59
    Copy constructor. Creates a new QTextTableCell object based on the
60
    \a other cell.
61
*/
62
63
/*!
64
    \fn QTextTableCell& QTextTableCell::operator=(const QTextTableCell &other)
65
66
    Assigns the \a other table cell to this table cell.
67
*/
68
69
/*!
70
    \since 4.2
71
72
    Sets the cell's character format to \a format. This can for example be used to change
73
    the background color of the entire cell:
74
75
    \code
76
    QTextTableCell cell = table->cellAt(2, 3);
77
    QTextCharFormat format = cell.format();
78
    format.setBackground(Qt::blue);
79
    cell.setFormat(format);
80
    \endcode
81
82
    Note that the cell's row or column span cannot be changed through this function. You have
83
    to use QTextTable::mergeCells and QTextTable::splitCell instead.
84
85
    \sa format()
86
*/
87
void QTextTableCell::setFormat(const QTextCharFormat &format)
88
0
{
89
0
    QTextCharFormat fmt = format;
90
0
    fmt.clearProperty(QTextFormat::ObjectIndex);
91
0
    fmt.setObjectType(QTextFormat::TableCellObject);
92
0
    QTextDocumentPrivate *p = const_cast<QTextDocumentPrivate *>(QTextDocumentPrivate::get(table));
93
0
    QTextDocumentPrivate::FragmentIterator frag(&p->fragmentMap(), fragment);
94
95
0
    QTextFormatCollection *c = p->formatCollection();
96
0
    QTextCharFormat oldFormat = c->charFormat(frag->format);
97
0
    fmt.setTableCellRowSpan(oldFormat.tableCellRowSpan());
98
0
    fmt.setTableCellColumnSpan(oldFormat.tableCellColumnSpan());
99
100
0
    p->setCharFormat(frag.position(), 1, fmt, QTextDocumentPrivate::SetFormatAndPreserveObjectIndices);
101
0
}
102
103
/*!
104
    Returns the cell's character format.
105
*/
106
QTextCharFormat QTextTableCell::format() const
107
0
{
108
0
    const QTextDocumentPrivate *p = QTextDocumentPrivate::get(table);
109
0
    const QTextFormatCollection *c = p->formatCollection();
110
111
0
    QTextCharFormat fmt = c->charFormat(tableCellFormatIndex());
112
0
    fmt.setObjectType(QTextFormat::TableCellObject);
113
0
    return fmt;
114
0
}
115
116
/*!
117
    \since 4.5
118
119
    Returns the index of the tableCell's format in the document's internal list of formats.
120
121
    \sa QTextDocument::allFormats()
122
*/
123
int QTextTableCell::tableCellFormatIndex() const
124
0
{
125
0
    const QTextDocumentPrivate *p = QTextDocumentPrivate::get(table);
126
0
    return QTextDocumentPrivate::FragmentIterator(&p->fragmentMap(), fragment)->format;
127
0
}
128
129
/*!
130
    Returns the number of the row in the table that contains this cell.
131
132
    \sa column()
133
*/
134
int QTextTableCell::row() const
135
0
{
136
0
    const QTextTablePrivate *tp = table->d_func();
137
0
    if (tp->dirty)
138
0
        tp->update();
139
140
0
    int idx = tp->findCellIndex(fragment);
141
0
    if (idx == -1)
142
0
        return idx;
143
0
    return tp->cellIndices.at(idx) / tp->nCols;
144
0
}
145
146
/*!
147
    Returns the number of the column in the table that contains this cell.
148
149
    \sa row()
150
*/
151
int QTextTableCell::column() const
152
0
{
153
0
    const QTextTablePrivate *tp = table->d_func();
154
0
    if (tp->dirty)
155
0
        tp->update();
156
157
0
    int idx = tp->findCellIndex(fragment);
158
0
    if (idx == -1)
159
0
        return idx;
160
0
    return tp->cellIndices.at(idx) % tp->nCols;
161
0
}
162
163
/*!
164
    Returns the number of rows this cell spans. The default is 1.
165
166
    \sa columnSpan()
167
*/
168
int QTextTableCell::rowSpan() const
169
0
{
170
0
    return format().tableCellRowSpan();
171
0
}
172
173
/*!
174
    Returns the number of columns this cell spans. The default is 1.
175
176
    \sa rowSpan()
177
*/
178
int QTextTableCell::columnSpan() const
179
0
{
180
0
    return format().tableCellColumnSpan();
181
0
}
182
183
/*!
184
    \fn bool QTextTableCell::isValid() const
185
186
    Returns \c true if this is a valid table cell; otherwise returns
187
    false.
188
*/
189
190
191
/*!
192
    Returns the first valid cursor position in this cell.
193
194
    \sa lastCursorPosition()
195
*/
196
QTextCursor QTextTableCell::firstCursorPosition() const
197
0
{
198
0
    return QTextCursorPrivate::fromPosition(table->d_func()->pieceTable, firstPosition());
199
0
}
200
201
/*!
202
    Returns the last valid cursor position in this cell.
203
204
    \sa firstCursorPosition()
205
*/
206
QTextCursor QTextTableCell::lastCursorPosition() const
207
0
{
208
0
    return QTextCursorPrivate::fromPosition(table->d_func()->pieceTable, lastPosition());
209
0
}
210
211
212
/*!
213
    \internal
214
215
    Returns the first valid position in the document occupied by this cell.
216
*/
217
int QTextTableCell::firstPosition() const
218
0
{
219
0
    const QTextDocumentPrivate *p = QTextDocumentPrivate::get(table);
220
0
    return p->fragmentMap().position(fragment) + 1;
221
0
}
222
223
/*!
224
    \internal
225
226
    Returns the last valid position in the document occupied by this cell.
227
*/
228
int QTextTableCell::lastPosition() const
229
0
{
230
0
    const QTextDocumentPrivate *p = QTextDocumentPrivate::get(table);
231
0
    const QTextTablePrivate *td = table->d_func();
232
0
    int index = table->d_func()->findCellIndex(fragment);
233
0
    int f;
234
0
    if (index != -1)
235
0
        f = td->cells.value(index + 1, td->fragment_end);
236
0
    else
237
0
        f = td->fragment_end;
238
0
    return p->fragmentMap().position(f);
239
0
}
240
241
242
/*!
243
    Returns a frame iterator pointing to the beginning of the table's cell.
244
245
    \sa end()
246
*/
247
QTextFrame::iterator QTextTableCell::begin() const
248
0
{
249
0
    const QTextDocumentPrivate *p = QTextDocumentPrivate::get(table);
250
0
    int b = p->blockMap().findNode(firstPosition());
251
0
    int e = p->blockMap().findNode(lastPosition()+1);
252
0
    return QTextFrame::iterator(const_cast<QTextTable *>(table), b, b, e);
253
0
}
254
255
/*!
256
    Returns a frame iterator pointing to the end of the table's cell.
257
258
    \sa begin()
259
*/
260
QTextFrame::iterator QTextTableCell::end() const
261
0
{
262
0
    const QTextDocumentPrivate *p = QTextDocumentPrivate::get(table);
263
0
    int b = p->blockMap().findNode(firstPosition());
264
0
    int e = p->blockMap().findNode(lastPosition()+1);
265
0
    return QTextFrame::iterator(const_cast<QTextTable *>(table), e, b, e);
266
0
}
267
268
269
/*!
270
    \fn QTextCursor QTextTableCell::operator==(const QTextTableCell &other) const
271
272
    Returns \c true if this cell object and the \a other cell object
273
    describe the same cell; otherwise returns \c false.
274
*/
275
276
/*!
277
    \fn QTextCursor QTextTableCell::operator!=(const QTextTableCell &other) const
278
279
    Returns \c true if this cell object and the \a other cell object
280
    describe different cells; otherwise returns \c false.
281
*/
282
283
/*!
284
    \fn QTextTableCell::~QTextTableCell()
285
286
    Destroys the table cell.
287
*/
288
289
QTextTable *QTextTablePrivate::createTable(QTextDocumentPrivate *pieceTable, int pos, int rows, int cols, const QTextTableFormat &tableFormat)
290
0
{
291
0
    QTextTableFormat fmt = tableFormat;
292
0
    fmt.setColumns(cols);
293
0
    QTextTable *table = qobject_cast<QTextTable *>(pieceTable->createObject(fmt));
294
0
    Q_ASSERT(table);
295
296
0
    pieceTable->beginEditBlock();
297
298
//     qDebug("---> createTable: rows=%d, cols=%d at %d", rows, cols, pos);
299
    // add block after table
300
0
    QTextCharFormat charFmt;
301
0
    charFmt.setObjectIndex(table->objectIndex());
302
0
    charFmt.setObjectType(QTextFormat::TableCellObject);
303
304
305
0
    int charIdx = pieceTable->formatCollection()->indexForFormat(charFmt);
306
0
    int cellIdx = pieceTable->formatCollection()->indexForFormat(QTextBlockFormat());
307
308
0
    QTextTablePrivate *d = table->d_func();
309
0
    d->blockFragmentUpdates = true;
310
311
0
    d->fragment_start = pieceTable->insertBlock(QTextBeginningOfFrame, pos, cellIdx, charIdx);
312
0
    d->cells.append(d->fragment_start);
313
0
    ++pos;
314
315
0
    for (int i = 1; i < rows*cols; ++i) {
316
0
        d->cells.append(pieceTable->insertBlock(QTextBeginningOfFrame, pos, cellIdx, charIdx));
317
//      qDebug("      addCell at %d", pos);
318
0
        ++pos;
319
0
    }
320
321
0
    d->fragment_end = pieceTable->insertBlock(QTextEndOfFrame, pos, cellIdx, charIdx);
322
//  qDebug("      addEOR at %d", pos);
323
0
    ++pos;
324
325
0
    d->blockFragmentUpdates = false;
326
0
    d->dirty = true;
327
328
0
    pieceTable->endEditBlock();
329
330
0
    return table;
331
0
}
332
333
struct QFragmentFindHelper
334
{
335
    inline QFragmentFindHelper(int _pos, const QTextDocumentPrivate::FragmentMap &map)
336
0
        : pos(_pos), fragmentMap(map) {}
337
    uint pos;
338
    const QTextDocumentPrivate::FragmentMap &fragmentMap;
339
};
340
341
static inline bool operator<(int fragment, const QFragmentFindHelper &helper)
342
0
{
343
0
    return helper.fragmentMap.position(fragment) < helper.pos;
344
0
}
345
346
static inline bool operator<(const QFragmentFindHelper &helper, int fragment)
347
0
{
348
0
    return helper.pos < helper.fragmentMap.position(fragment);
349
0
}
350
351
int QTextTablePrivate::findCellIndex(int fragment) const
352
0
{
353
0
    QFragmentFindHelper helper(pieceTable->fragmentMap().position(fragment),
354
0
                              pieceTable->fragmentMap());
355
0
    const auto it = std::lower_bound(cells.constBegin(), cells.constEnd(), helper);
356
0
    if ((it == cells.constEnd()) || (helper < *it))
357
0
        return -1;
358
0
    return it - cells.constBegin();
359
0
}
360
361
void QTextTablePrivate::fragmentAdded(QChar type, uint fragment)
362
0
{
363
0
    dirty = true;
364
0
    if (blockFragmentUpdates)
365
0
        return;
366
0
    if (type == QTextBeginningOfFrame) {
367
0
        Q_ASSERT(cells.indexOf(int(fragment)) == -1);
368
0
        const uint pos = pieceTable->fragmentMap().position(fragment);
369
0
        QFragmentFindHelper helper(pos, pieceTable->fragmentMap());
370
0
        auto it = std::lower_bound(cells.begin(), cells.end(), helper);
371
0
        cells.insert(it, fragment);
372
0
        if (!fragment_start || pos < pieceTable->fragmentMap().position(fragment_start))
373
0
            fragment_start = fragment;
374
0
        return;
375
0
    }
376
0
    QTextFramePrivate::fragmentAdded(type, fragment);
377
0
}
378
379
void QTextTablePrivate::fragmentRemoved(QChar type, uint fragment)
380
0
{
381
0
    dirty = true;
382
0
    if (blockFragmentUpdates)
383
0
        return;
384
0
    if (type == QTextBeginningOfFrame) {
385
0
        Q_ASSERT(cells.indexOf(int(fragment)) != -1);
386
0
        cells.removeAll(int(fragment));
387
0
        if (fragment_start == fragment && cells.size()) {
388
0
            fragment_start = cells.at(0);
389
0
        }
390
0
        if (fragment_start != fragment)
391
0
            return;
392
0
    }
393
0
    QTextFramePrivate::fragmentRemoved(type, fragment);
394
0
}
395
396
/*!
397
    \internal
398
    This function is usually called when the table is "dirty".
399
    It seems to update all kind of table information.
400
*/
401
void QTextTablePrivate::update() const
402
0
{
403
0
    Q_Q(const QTextTable);
404
0
    nCols = q->format().columns();
405
0
    nRows = (cells.size() + nCols-1)/nCols;
406
//     qDebug(">>>> QTextTablePrivate::update, nRows=%d, nCols=%d", nRows, nCols);
407
408
0
    grid.assign(nRows * nCols, 0);
409
410
0
    QTextDocumentPrivate *p = pieceTable;
411
0
    QTextFormatCollection *c = p->formatCollection();
412
413
0
    cellIndices.resize(cells.size());
414
415
0
    int cell = 0;
416
0
    for (int i = 0; i < cells.size(); ++i) {
417
0
        int fragment = cells.at(i);
418
0
        QTextCharFormat fmt = c->charFormat(QTextDocumentPrivate::FragmentIterator(&p->fragmentMap(), fragment)->format);
419
0
        int rowspan = fmt.tableCellRowSpan();
420
0
        int colspan = fmt.tableCellColumnSpan();
421
422
        // skip taken cells
423
0
        while (cell < nRows*nCols && grid[cell])
424
0
            ++cell;
425
426
0
        int r = cell/nCols;
427
0
        int c = cell%nCols;
428
0
        cellIndices[i] = cell;
429
430
0
        if (r + rowspan > nRows) {
431
0
            grid.resize((r + rowspan) * nCols, 0);
432
0
            nRows = r + rowspan;
433
0
        }
434
435
0
        Q_ASSERT(c + colspan <= nCols);
436
0
        for (int ii = 0; ii < rowspan; ++ii) {
437
0
            for (int jj = 0; jj < colspan; ++jj) {
438
0
                Q_ASSERT(grid[(r+ii)*nCols + c+jj] == 0);
439
0
                grid[(r+ii)*nCols + c+jj] = fragment;
440
//              qDebug("    setting cell %d span=%d/%d at %d/%d", fragment, rowspan, colspan, r+ii, c+jj);
441
0
            }
442
0
        }
443
0
    }
444
//     qDebug("<<<< end: nRows=%d, nCols=%d", nRows, nCols);
445
446
0
    dirty = false;
447
0
}
448
449
450
451
452
453
/*!
454
    \class QTextTable
455
    \reentrant
456
457
    \brief The QTextTable class represents a table in a QTextDocument.
458
    \inmodule QtGui
459
460
    \ingroup richtext-processing
461
462
    A table is a group of cells ordered into rows and columns. Each table
463
    contains at least one row and one column. Each cell contains a block, and
464
    is surrounded by a frame.
465
466
    Tables are usually created and inserted into a document with the
467
    QTextCursor::insertTable() function.
468
    For example, we can insert a table with three rows and two columns at the
469
    current cursor position in an editor using the following lines of code:
470
471
    \snippet textdocument-tables/mainwindow.cpp 1
472
    \codeline
473
    \snippet textdocument-tables/mainwindow.cpp 3
474
475
    The table format is either defined when the table is created or changed
476
    later with setFormat().
477
478
    The table currently being edited by the cursor is found with
479
    QTextCursor::currentTable(). This allows its format or dimensions to be
480
    changed after it has been inserted into a document.
481
482
    A table's size can be changed with resize(), or by using
483
    insertRows(), insertColumns(), removeRows(), or removeColumns().
484
    Use cellAt() to retrieve table cells.
485
486
    The starting and ending positions of table rows can be found by moving
487
    a cursor within a table, and using the rowStart() and rowEnd() functions
488
    to obtain cursors at the start and end of each row.
489
490
    Rows and columns within a QTextTable can be merged and split using
491
    the mergeCells() and splitCell() functions. However, only cells that span multiple
492
    rows or columns can be split. (Merging or splitting does not increase or decrease
493
    the number of rows and columns.)
494
495
    Note that if you have merged multiple columns and rows into one cell, you will not
496
    be able to split the merged cell into new cells spanning over more than one row
497
    or column. To be able to split cells spanning over several rows and columns you
498
    need to do this over several iterations.
499
500
    \table 80%
501
    \row
502
        \li \inlineimage texttable-split.png Original Table
503
        \li Suppose we have a 2x3 table of names and addresses. To merge both
504
        columns in the first row we invoke mergeCells() with \a row = 0,
505
        \a column = 0, \a numRows = 1 and \a numColumns = 2.
506
        \snippet textdocument-texttable/main.cpp 0
507
508
    \row
509
        \li \inlineimage texttable-merge.png
510
        \li  This gives us the following table. To split the first row of the table
511
        back into two cells, we invoke the splitCell() function with \a numRows
512
        and \a numCols = 1.
513
        \snippet textdocument-texttable/main.cpp 1
514
515
    \row
516
        \li \inlineimage texttable-split.png Split Table
517
        \li This results in the original table.
518
    \endtable
519
520
    \sa QTextTableFormat
521
*/
522
523
/*! \internal
524
 */
525
QTextTable::QTextTable(QTextDocument *doc)
526
0
    : QTextFrame(*new QTextTablePrivate(doc), doc)
527
0
{
528
0
}
529
530
/*! \internal
531
532
Destroys the table.
533
 */
534
QTextTable::~QTextTable()
535
0
{
536
0
}
537
538
539
/*!
540
    \fn QTextTableCell QTextTable::cellAt(int row, int column) const
541
542
    Returns the table cell at the given \a row and \a column in the table.
543
544
    \sa columns(), rows()
545
*/
546
QTextTableCell QTextTable::cellAt(int row, int col) const
547
0
{
548
0
    Q_D(const QTextTable);
549
0
    if (d->dirty)
550
0
        d->update();
551
552
0
    if (row < 0 || row >= d->nRows || col < 0 || col >= d->nCols)
553
0
        return QTextTableCell();
554
555
0
    return QTextTableCell(this, d->grid[row*d->nCols + col]);
556
0
}
557
558
/*!
559
    \overload
560
561
    Returns the table cell that contains the character at the given \a position
562
    in the document.
563
*/
564
QTextTableCell QTextTable::cellAt(int position) const
565
0
{
566
0
    Q_D(const QTextTable);
567
0
    if (d->dirty)
568
0
        d->update();
569
570
0
    uint pos = (uint)position;
571
0
    const QTextDocumentPrivate::FragmentMap &map = d->pieceTable->fragmentMap();
572
0
    if (position < 0 || map.position(d->fragment_start) >= pos || map.position(d->fragment_end) < pos)
573
0
        return QTextTableCell();
574
575
0
    QFragmentFindHelper helper(position, map);
576
0
    auto it = std::lower_bound(d->cells.begin(), d->cells.end(), helper);
577
0
    if (it != d->cells.begin())
578
0
        --it;
579
580
0
    return QTextTableCell(this, *it);
581
0
}
582
583
/*!
584
    \fn QTextTableCell QTextTable::cellAt(const QTextCursor &cursor) const
585
586
    \overload
587
588
    Returns the table cell containing the given \a cursor.
589
*/
590
QTextTableCell QTextTable::cellAt(const QTextCursor &c) const
591
0
{
592
0
    return cellAt(c.position());
593
0
}
594
595
/*!
596
    \fn void QTextTable::resize(int rows, int columns)
597
598
    Resizes the table to contain the required number of \a rows and \a columns.
599
600
    \sa insertRows(), insertColumns(), removeRows(), removeColumns()
601
*/
602
void QTextTable::resize(int rows, int cols)
603
0
{
604
0
    Q_D(QTextTable);
605
0
    if (d->dirty)
606
0
        d->update();
607
608
0
    int nRows = this->rows();
609
0
    int nCols = this->columns();
610
611
0
    if (rows == nRows && cols == nCols)
612
0
        return;
613
614
0
    d->pieceTable->beginEditBlock();
615
616
0
    if (nCols < cols)
617
0
        insertColumns(nCols, cols - nCols);
618
0
    else if (nCols > cols)
619
0
        removeColumns(cols, nCols - cols);
620
621
0
    if (nRows < rows)
622
0
        insertRows(nRows, rows-nRows);
623
0
    else if (nRows > rows)
624
0
        removeRows(rows, nRows-rows);
625
626
0
    d->pieceTable->endEditBlock();
627
0
}
628
629
/*!
630
    \fn void QTextTable::insertRows(int index, int rows)
631
632
    Inserts a number of \a rows before the row with the specified \a index.
633
634
    \sa resize(), insertColumns(), removeRows(), removeColumns(), appendRows(), appendColumns()
635
*/
636
void QTextTable::insertRows(int pos, int num)
637
0
{
638
0
    Q_D(QTextTable);
639
0
    if (num <= 0)
640
0
        return;
641
642
0
    if (d->dirty)
643
0
        d->update();
644
645
0
    if (pos > d->nRows || pos < 0)
646
0
        pos = d->nRows;
647
648
//     qDebug() << "-------- insertRows" << pos << num;
649
0
    QTextDocumentPrivate *p = d->pieceTable;
650
0
    QTextFormatCollection *c = p->formatCollection();
651
0
    p->beginEditBlock();
652
653
0
    int extended = 0;
654
0
    int insert_before = 0;
655
0
    if (pos > 0 && pos < d->nRows) {
656
0
        int lastCell = -1;
657
0
        for (int i = 0; i < d->nCols; ++i) {
658
0
            int cell = d->grid[pos*d->nCols + i];
659
0
            if (cell == d->grid[(pos-1)*d->nCols+i]) {
660
                // cell spans the insertion place, extend it
661
0
                if (cell != lastCell) {
662
0
                    QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
663
0
                    QTextCharFormat fmt = c->charFormat(it->format);
664
0
                    fmt.setTableCellRowSpan(fmt.tableCellRowSpan() + num);
665
0
                    p->setCharFormat(it.position(), 1, fmt);
666
0
                }
667
0
                extended++;
668
0
            } else if (!insert_before) {
669
0
                insert_before = cell;
670
0
            }
671
0
            lastCell = cell;
672
0
        }
673
0
    } else {
674
0
        insert_before = (pos == 0 ? d->grid[0] : d->fragment_end);
675
0
    }
676
0
    if (extended < d->nCols) {
677
0
        Q_ASSERT(insert_before);
678
0
        QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), insert_before);
679
0
        QTextCharFormat fmt = c->charFormat(it->format);
680
0
        fmt.setTableCellRowSpan(1);
681
0
        fmt.setTableCellColumnSpan(1);
682
0
        Q_ASSERT(fmt.objectIndex() == objectIndex());
683
0
        int pos = it.position();
684
0
        int cfmt = p->formatCollection()->indexForFormat(fmt);
685
0
        int bfmt = p->formatCollection()->indexForFormat(QTextBlockFormat());
686
//         qDebug("inserting %d cells, nCols=%d extended=%d", num*(d->nCols-extended), d->nCols, extended);
687
0
        for (int i = 0; i < num*(d->nCols-extended); ++i)
688
0
            p->insertBlock(QTextBeginningOfFrame, pos, bfmt, cfmt, QTextUndoCommand::MoveCursor);
689
0
    }
690
691
//     qDebug() << "-------- end insertRows" << pos << num;
692
0
    p->endEditBlock();
693
0
}
694
695
/*!
696
    \fn void QTextTable::insertColumns(int index, int columns)
697
698
    Inserts a number of \a columns before the column with the specified \a index.
699
700
    \sa insertRows(), resize(), removeRows(), removeColumns(), appendRows(), appendColumns()
701
*/
702
void QTextTable::insertColumns(int pos, int num)
703
0
{
704
0
    Q_D(QTextTable);
705
0
    if (num <= 0)
706
0
        return;
707
708
0
    if (d->dirty)
709
0
        d->update();
710
711
0
    if (pos > d->nCols || pos < 0)
712
0
        pos = d->nCols;
713
714
//     qDebug() << "-------- insertCols" << pos << num;
715
0
    QTextDocumentPrivate *p = d->pieceTable;
716
0
    QTextFormatCollection *c = p->formatCollection();
717
0
    p->beginEditBlock();
718
719
0
    QList<int> extendedSpans;
720
0
    for (int i = 0; i < d->nRows; ++i) {
721
0
        int cell;
722
0
        if (i == d->nRows - 1 && pos == d->nCols) {
723
0
            cell = d->fragment_end;
724
0
        } else {
725
0
            int logicalGridIndexBeforePosition = pos > 0 || i > 0
726
0
                                                 ? d->findCellIndex(d->grid[i*d->nCols + pos - 1])
727
0
                                                 : -1;
728
729
            // Search for the logical insertion point by skipping past cells which are not the first
730
            // cell in a rowspan. This means any cell for which the logical grid index is
731
            // less than the logical cell index of the cell before the insertion.
732
0
            int logicalGridIndex;
733
0
            int gridArrayOffset = i*d->nCols + pos;
734
0
            do {
735
0
                cell = d->grid[gridArrayOffset];
736
0
                logicalGridIndex = d->findCellIndex(cell);
737
0
                gridArrayOffset++;
738
0
            } while (logicalGridIndex < logicalGridIndexBeforePosition
739
0
                     && gridArrayOffset < d->nRows*d->nCols);
740
741
0
            if (logicalGridIndex < logicalGridIndexBeforePosition
742
0
                && gridArrayOffset == d->nRows*d->nCols)
743
0
                cell = d->fragment_end;
744
0
        }
745
746
0
        if (pos > 0 && pos < d->nCols && cell == d->grid[i*d->nCols + pos - 1]) {
747
            // cell spans the insertion place, extend it
748
0
            if (!extendedSpans.contains(cell)) {
749
0
                QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
750
0
                QTextCharFormat fmt = c->charFormat(it->format);
751
0
                fmt.setTableCellColumnSpan(fmt.tableCellColumnSpan() + num);
752
0
                p->setCharFormat(it.position(), 1, fmt);
753
0
                d->dirty = true;
754
0
                extendedSpans << cell;
755
0
            }
756
0
        } else {
757
            /* If the next cell is spanned from the row above, we need to find the right position
758
            to insert to */
759
0
            if (i > 0 && pos < d->nCols && cell == d->grid[(i-1) * d->nCols + pos]) {
760
0
                int gridIndex = i*d->nCols + pos;
761
0
                const int gridEnd = d->nRows * d->nCols - 1;
762
0
                while (gridIndex < gridEnd && cell == d->grid[gridIndex]) {
763
0
                    ++gridIndex;
764
0
                }
765
0
                if (gridIndex == gridEnd)
766
0
                    cell = d->fragment_end;
767
0
                else
768
0
                    cell = d->grid[gridIndex];
769
0
            }
770
0
            QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
771
0
            QTextCharFormat fmt = c->charFormat(it->format);
772
0
            fmt.setTableCellRowSpan(1);
773
0
            fmt.setTableCellColumnSpan(1);
774
0
            Q_ASSERT(fmt.objectIndex() == objectIndex());
775
0
            int position = it.position();
776
0
            int cfmt = p->formatCollection()->indexForFormat(fmt);
777
0
            int bfmt = p->formatCollection()->indexForFormat(QTextBlockFormat());
778
0
            for (int i = 0; i < num; ++i)
779
0
                p->insertBlock(QTextBeginningOfFrame, position, bfmt, cfmt, QTextUndoCommand::MoveCursor);
780
0
        }
781
0
    }
782
783
0
    QTextTableFormat tfmt = format();
784
0
    tfmt.setColumns(tfmt.columns()+num);
785
0
    QList<QTextLength> columnWidths = tfmt.columnWidthConstraints();
786
0
    if (! columnWidths.isEmpty()) {
787
0
        for (int i = num; i > 0; --i)
788
0
            columnWidths.insert(pos, columnWidths.at(qMax(0, pos - 1)));
789
0
    }
790
0
    tfmt.setColumnWidthConstraints (columnWidths);
791
0
    QTextObject::setFormat(tfmt);
792
793
//     qDebug() << "-------- end insertCols" << pos << num;
794
0
    p->endEditBlock();
795
0
}
796
797
/*!
798
    \since 4.5
799
    Appends \a count rows at the bottom of the table.
800
801
    \sa insertColumns(), insertRows(), resize(), removeRows(), removeColumns(), appendColumns()
802
*/
803
void QTextTable::appendRows(int count)
804
0
{
805
0
    insertRows(rows(), count);
806
0
}
807
808
/*!
809
    \since 4.5
810
    Appends \a count columns at the right side of the table.
811
812
    \sa insertColumns(), insertRows(), resize(), removeRows(), removeColumns(), appendRows()
813
*/
814
void QTextTable::appendColumns(int count)
815
0
{
816
0
    insertColumns(columns(), count);
817
0
}
818
819
/*!
820
    \fn void QTextTable::removeRows(int index, int rows)
821
822
    Removes a number of \a rows starting with the row at the specified \a index.
823
824
    \sa insertRows(), insertColumns(), resize(), removeColumns(), appendRows(), appendColumns()
825
*/
826
void QTextTable::removeRows(int pos, int num)
827
0
{
828
0
    Q_D(QTextTable);
829
//     qDebug() << "-------- removeRows" << pos << num;
830
831
0
    if (num <= 0 || pos < 0)
832
0
        return;
833
0
    if (d->dirty)
834
0
        d->update();
835
0
    if (pos >= d->nRows)
836
0
        return;
837
0
    if (pos+num > d->nRows)
838
0
        num = d->nRows - pos;
839
840
0
    QTextDocumentPrivate *p = d->pieceTable;
841
0
    QTextFormatCollection *collection = p->formatCollection();
842
0
    p->beginEditBlock();
843
844
    // delete whole table?
845
0
    if (pos == 0 && num == d->nRows) {
846
0
        const int pos = p->fragmentMap().position(d->fragment_start);
847
0
        p->remove(pos, p->fragmentMap().position(d->fragment_end) - pos + 1);
848
0
        p->endEditBlock();
849
0
        return;
850
0
    }
851
852
0
    p->aboutToRemoveCell(cellAt(pos, 0).firstPosition(), cellAt(pos + num - 1, d->nCols - 1).lastPosition());
853
854
0
    QList<int> touchedCells;
855
0
    for (int r = pos; r < pos + num; ++r) {
856
0
        for (int c = 0; c < d->nCols; ++c) {
857
0
            int cell = d->grid[r*d->nCols + c];
858
0
            if (touchedCells.contains(cell))
859
0
                continue;
860
0
            touchedCells << cell;
861
0
            QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
862
0
            QTextCharFormat fmt = collection->charFormat(it->format);
863
0
            int span = fmt.tableCellRowSpan();
864
0
            if (span > 1) {
865
0
                fmt.setTableCellRowSpan(span - 1);
866
0
                p->setCharFormat(it.position(), 1, fmt);
867
0
            } else {
868
                // remove cell
869
0
                int index = d->cells.indexOf(cell) + 1;
870
0
                int f_end = index < d->cells.size() ? d->cells.at(index) : d->fragment_end;
871
0
                p->remove(it.position(), p->fragmentMap().position(f_end) - it.position());
872
0
            }
873
0
        }
874
0
    }
875
876
0
    p->endEditBlock();
877
//     qDebug() << "-------- end removeRows" << pos << num;
878
0
}
879
880
/*!
881
    \fn void QTextTable::removeColumns(int index, int columns)
882
883
    Removes a number of \a columns starting with the column at the specified
884
    \a index.
885
886
    \sa insertRows(), insertColumns(), removeRows(), resize(), appendRows(), appendColumns()
887
*/
888
void QTextTable::removeColumns(int pos, int num)
889
0
{
890
0
    Q_D(QTextTable);
891
//     qDebug() << "-------- removeCols" << pos << num;
892
893
0
    if (num <= 0 || pos < 0)
894
0
        return;
895
0
    if (d->dirty)
896
0
        d->update();
897
0
    if (pos >= d->nCols)
898
0
        return;
899
0
    if (pos + num > d->nCols)
900
0
        pos = d->nCols - num;
901
902
0
    QTextDocumentPrivate *p = d->pieceTable;
903
0
    QTextFormatCollection *collection = p->formatCollection();
904
0
    p->beginEditBlock();
905
906
    // delete whole table?
907
0
    if (pos == 0 && num == d->nCols) {
908
0
        const int pos = p->fragmentMap().position(d->fragment_start);
909
0
        p->remove(pos, p->fragmentMap().position(d->fragment_end) - pos + 1);
910
0
        p->endEditBlock();
911
0
        return;
912
0
    }
913
914
0
    p->aboutToRemoveCell(cellAt(0, pos).firstPosition(), cellAt(d->nRows - 1, pos + num - 1).lastPosition());
915
916
0
    QList<int> touchedCells;
917
0
    for (int r = 0; r < d->nRows; ++r) {
918
0
        for (int c = pos; c < pos + num; ++c) {
919
0
            int cell = d->grid[r*d->nCols + c];
920
0
            QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), cell);
921
0
            QTextCharFormat fmt = collection->charFormat(it->format);
922
0
            int span = fmt.tableCellColumnSpan();
923
0
            if (touchedCells.contains(cell) && span <= 1)
924
0
                continue;
925
0
            touchedCells << cell;
926
927
0
            if (span > 1) {
928
0
                fmt.setTableCellColumnSpan(span - 1);
929
0
                p->setCharFormat(it.position(), 1, fmt);
930
0
            } else {
931
                // remove cell
932
0
                int index = d->cells.indexOf(cell) + 1;
933
0
                int f_end = index < d->cells.size() ? d->cells.at(index) : d->fragment_end;
934
0
                p->remove(it.position(), p->fragmentMap().position(f_end) - it.position());
935
0
            }
936
0
        }
937
0
    }
938
939
0
    QTextTableFormat tfmt = format();
940
0
    tfmt.setColumns(tfmt.columns()-num);
941
0
    QList<QTextLength> columnWidths = tfmt.columnWidthConstraints();
942
0
    if (columnWidths.size() > pos) {
943
0
        columnWidths.remove(pos, num);
944
0
        tfmt.setColumnWidthConstraints (columnWidths);
945
0
    }
946
0
    QTextObject::setFormat(tfmt);
947
948
0
    p->endEditBlock();
949
//     qDebug() << "-------- end removeCols" << pos << num;
950
0
}
951
952
/*!
953
    \since 4.1
954
955
    Merges the cell at the specified \a row and \a column with the adjacent cells
956
    into one cell. The new cell will span \a numRows rows and \a numCols columns.
957
    This method does nothing if \a numRows or \a numCols is less than the current
958
    number of rows or columns spanned by the cell.
959
960
    \sa splitCell()
961
*/
962
void QTextTable::mergeCells(int row, int column, int numRows, int numCols)
963
0
{
964
0
    Q_D(QTextTable);
965
966
0
    if (d->dirty)
967
0
        d->update();
968
969
0
    QTextDocumentPrivate *p = d->pieceTable;
970
0
    QTextFormatCollection *fc = p->formatCollection();
971
972
0
    const QTextTableCell cell = cellAt(row, column);
973
0
    if (!cell.isValid() || row != cell.row() || column != cell.column())
974
0
        return;
975
976
0
    QTextCharFormat fmt = cell.format();
977
0
    const int rowSpan = fmt.tableCellRowSpan();
978
0
    const int colSpan = fmt.tableCellColumnSpan();
979
980
0
    numRows = qMin(numRows, rows() - cell.row());
981
0
    numCols = qMin(numCols, columns() - cell.column());
982
983
    // nothing to merge?
984
0
    if (numRows < rowSpan || numCols < colSpan)
985
0
        return;
986
987
    // check the edges of the merge rect to make sure no cell spans the edge
988
0
    for (int r = row; r < row + numRows; ++r) {
989
0
        if (cellAt(r, column) == cellAt(r, column - 1))
990
0
            return;
991
0
        if (cellAt(r, column + numCols) == cellAt(r, column + numCols - 1))
992
0
            return;
993
0
    }
994
995
0
    for (int c = column; c < column + numCols; ++c) {
996
0
        if (cellAt(row, c) == cellAt(row - 1, c))
997
0
            return;
998
0
        if (cellAt(row + numRows, c) == cellAt(row + numRows - 1, c))
999
0
            return;
1000
0
    }
1001
1002
0
    p->beginEditBlock();
1003
1004
0
    const int origCellPosition = cell.firstPosition() - 1;
1005
1006
0
    const int cellFragment = d->grid[row * d->nCols + column];
1007
1008
    // find the position at which to insert the contents of the merged cells
1009
0
    QFragmentFindHelper helper(origCellPosition, p->fragmentMap());
1010
0
    const auto begin = d->cells.cbegin();
1011
0
    const auto it = std::lower_bound(begin, d->cells.cend(), helper);
1012
0
    Q_ASSERT(it != d->cells.cend());
1013
0
    Q_ASSERT(!(helper < *it));
1014
0
    Q_ASSERT(*it == cellFragment);
1015
0
    const int insertCellIndex = it - begin;
1016
0
    int insertFragment = d->cells.value(insertCellIndex + 1, d->fragment_end);
1017
0
    uint insertPos = p->fragmentMap().position(insertFragment);
1018
1019
0
    d->blockFragmentUpdates = true;
1020
1021
0
    bool rowHasText = cell.firstCursorPosition().block().length();
1022
0
    bool needsParagraph = rowHasText && colSpan == numCols;
1023
1024
    // find all cells that will be erased by the merge
1025
0
    for (int r = row; r < row + numRows; ++r) {
1026
0
        int firstColumn = r < row + rowSpan ? column + colSpan : column;
1027
1028
        // don't recompute the cell index for the first row
1029
0
        int firstCellIndex = r == row ? insertCellIndex + 1 : -1;
1030
0
        int cellIndex = firstCellIndex;
1031
1032
0
        for (int c = firstColumn; c < column + numCols; ++c) {
1033
0
            const int fragment = d->grid[r * d->nCols + c];
1034
1035
            // already handled?
1036
0
            if (fragment == cellFragment)
1037
0
                continue;
1038
1039
0
            QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), fragment);
1040
0
            uint pos = it.position();
1041
1042
0
            if (firstCellIndex == -1) {
1043
0
                QFragmentFindHelper helper(pos, p->fragmentMap());
1044
0
                const auto begin = d->cells.cbegin();
1045
0
                const auto it = std::lower_bound(begin, d->cells.cend(), helper);
1046
0
                Q_ASSERT(it != d->cells.cend());
1047
0
                Q_ASSERT(!(helper < *it));
1048
0
                Q_ASSERT(*it == fragment);
1049
0
                firstCellIndex = cellIndex = it - begin;
1050
0
            }
1051
1052
0
            ++cellIndex;
1053
1054
0
            QTextCharFormat fmt = fc->charFormat(it->format);
1055
1056
0
            const int cellRowSpan = fmt.tableCellRowSpan();
1057
0
            const int cellColSpan = fmt.tableCellColumnSpan();
1058
1059
            // update the grid for this cell
1060
0
            for (int i = r; i < r + cellRowSpan; ++i)
1061
0
                for (int j = c; j < c + cellColSpan; ++j)
1062
0
                    d->grid[i * d->nCols + j] = cellFragment;
1063
1064
            // erase the cell marker
1065
0
            p->remove(pos, 1);
1066
1067
0
            const int nextFragment = d->cells.value(cellIndex, d->fragment_end);
1068
0
            const uint nextPos = p->fragmentMap().position(nextFragment);
1069
1070
0
            Q_ASSERT(nextPos >= pos);
1071
1072
            // merge the contents of the cell (if not empty)
1073
0
            if (nextPos > pos) {
1074
0
                if (needsParagraph) {
1075
0
                    needsParagraph = false;
1076
0
                    QTextCursorPrivate::fromPosition(p, insertPos++).insertBlock();
1077
0
                    p->move(pos + 1, insertPos, nextPos - pos);
1078
0
                } else if (rowHasText) {
1079
0
                    QTextCursorPrivate::fromPosition(p, insertPos++).insertText(" "_L1);
1080
0
                    p->move(pos + 1, insertPos, nextPos - pos);
1081
0
                } else {
1082
0
                    p->move(pos, insertPos, nextPos - pos);
1083
0
                }
1084
1085
0
                insertPos += nextPos - pos;
1086
0
                rowHasText = true;
1087
0
            }
1088
0
        }
1089
1090
0
        if (rowHasText) {
1091
0
            needsParagraph = true;
1092
0
            rowHasText = false;
1093
0
        }
1094
1095
        // erase cells from last row
1096
0
        if (firstCellIndex >= 0) {
1097
0
            d->cellIndices.remove(firstCellIndex, cellIndex - firstCellIndex);
1098
0
            d->cells.erase(d->cells.begin() + firstCellIndex, d->cells.begin() + cellIndex);
1099
0
        }
1100
0
    }
1101
1102
0
    d->fragment_start = d->cells.constFirst();
1103
1104
0
    fmt.setTableCellRowSpan(numRows);
1105
0
    fmt.setTableCellColumnSpan(numCols);
1106
0
    p->setCharFormat(origCellPosition, 1, fmt);
1107
1108
0
    d->blockFragmentUpdates = false;
1109
0
    d->dirty = false;
1110
1111
0
    p->endEditBlock();
1112
0
}
1113
1114
/*!
1115
    \overload
1116
    \since 4.1
1117
1118
    Merges the cells selected by the provided \a cursor.
1119
1120
    \sa splitCell()
1121
*/
1122
void QTextTable::mergeCells(const QTextCursor &cursor)
1123
0
{
1124
0
    if (!cursor.hasComplexSelection())
1125
0
        return;
1126
1127
0
    int firstRow, numRows, firstColumn, numColumns;
1128
0
    cursor.selectedTableCells(&firstRow, &numRows, &firstColumn, &numColumns);
1129
0
    mergeCells(firstRow, firstColumn, numRows, numColumns);
1130
0
}
1131
1132
/*!
1133
    \since 4.1
1134
1135
    Splits the specified cell at \a row and \a column into an array of multiple
1136
    cells with dimensions specified by \a numRows and \a numCols.
1137
1138
    \note It is only possible to split cells that span multiple rows or columns, such as rows
1139
    that have been merged using mergeCells().
1140
1141
    \sa mergeCells()
1142
*/
1143
void QTextTable::splitCell(int row, int column, int numRows, int numCols)
1144
0
{
1145
0
    Q_D(QTextTable);
1146
1147
0
    if (d->dirty)
1148
0
        d->update();
1149
1150
0
    QTextDocumentPrivate *p = d->pieceTable;
1151
0
    QTextFormatCollection *c = p->formatCollection();
1152
1153
0
    const QTextTableCell cell = cellAt(row, column);
1154
0
    if (!cell.isValid())
1155
0
        return;
1156
0
    row = cell.row();
1157
0
    column = cell.column();
1158
1159
0
    QTextCharFormat fmt = cell.format();
1160
0
    const int rowSpan = fmt.tableCellRowSpan();
1161
0
    const int colSpan = fmt.tableCellColumnSpan();
1162
1163
    // nothing to split?
1164
0
    if (numRows > rowSpan || numCols > colSpan)
1165
0
        return;
1166
1167
0
    p->beginEditBlock();
1168
1169
0
    const int origCellPosition = cell.firstPosition() - 1;
1170
1171
0
    QVarLengthArray<int> rowPositions(rowSpan);
1172
1173
0
    rowPositions[0] = cell.lastPosition();
1174
1175
0
    for (int r = row + 1; r < row + rowSpan; ++r) {
1176
        // find the cell before which to insert the new cell markers
1177
0
        int gridIndex = r * d->nCols + column;
1178
0
        const auto begin = d->cellIndices.cbegin();
1179
0
        const auto it = std::upper_bound(begin, d->cellIndices.cend(), gridIndex);
1180
0
        int fragment = d->cells.value(it - begin, d->fragment_end);
1181
0
        rowPositions[r - row] = p->fragmentMap().position(fragment);
1182
0
    }
1183
1184
0
    fmt.setTableCellColumnSpan(1);
1185
0
    fmt.setTableCellRowSpan(1);
1186
0
    const int fmtIndex = c->indexForFormat(fmt);
1187
0
    const int blockIndex = p->blockMap().find(cell.lastPosition())->format;
1188
1189
0
    int insertAdjustement = 0;
1190
0
    for (int i = 0; i < numRows; ++i) {
1191
0
        for (int c = 0; c < colSpan - numCols; ++c)
1192
0
            p->insertBlock(QTextBeginningOfFrame, rowPositions[i] + insertAdjustement + c, blockIndex, fmtIndex);
1193
0
        insertAdjustement += colSpan - numCols;
1194
0
    }
1195
1196
0
    for (int i = numRows; i < rowSpan; ++i) {
1197
0
        for (int c = 0; c < colSpan; ++c)
1198
0
            p->insertBlock(QTextBeginningOfFrame, rowPositions[i] + insertAdjustement + c, blockIndex, fmtIndex);
1199
0
        insertAdjustement += colSpan;
1200
0
    }
1201
1202
0
    fmt.setTableCellRowSpan(numRows);
1203
0
    fmt.setTableCellColumnSpan(numCols);
1204
0
    p->setCharFormat(origCellPosition, 1, fmt);
1205
1206
0
    p->endEditBlock();
1207
0
}
1208
1209
/*!
1210
    Returns the number of rows in the table.
1211
1212
    \sa columns()
1213
*/
1214
int QTextTable::rows() const
1215
0
{
1216
0
    Q_D(const QTextTable);
1217
0
    if (d->dirty)
1218
0
        d->update();
1219
1220
0
    return d->nRows;
1221
0
}
1222
1223
/*!
1224
    Returns the number of columns in the table.
1225
1226
    \sa rows()
1227
*/
1228
int QTextTable::columns() const
1229
0
{
1230
0
    Q_D(const QTextTable);
1231
0
    if (d->dirty)
1232
0
        d->update();
1233
1234
0
    return d->nCols;
1235
0
}
1236
1237
/*!
1238
    \fn QTextCursor QTextTable::rowStart(const QTextCursor &cursor) const
1239
1240
    Returns a cursor pointing to the start of the row that contains the
1241
    given \a cursor.
1242
1243
    \sa rowEnd()
1244
*/
1245
QTextCursor QTextTable::rowStart(const QTextCursor &c) const
1246
0
{
1247
0
    Q_D(const QTextTable);
1248
0
    QTextTableCell cell = cellAt(c);
1249
0
    if (!cell.isValid())
1250
0
        return QTextCursor();
1251
1252
0
    int row = cell.row();
1253
0
    QTextDocumentPrivate *p = d->pieceTable;
1254
0
    QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), d->grid[row*d->nCols]);
1255
0
    return QTextCursorPrivate::fromPosition(p, it.position());
1256
0
}
1257
1258
/*!
1259
    \fn QTextCursor QTextTable::rowEnd(const QTextCursor &cursor) const
1260
1261
    Returns a cursor pointing to the end of the row that contains the given
1262
    \a cursor.
1263
1264
    \sa rowStart()
1265
*/
1266
QTextCursor QTextTable::rowEnd(const QTextCursor &c) const
1267
0
{
1268
0
    Q_D(const QTextTable);
1269
0
    QTextTableCell cell = cellAt(c);
1270
0
    if (!cell.isValid())
1271
0
        return QTextCursor();
1272
1273
0
    int row = cell.row() + 1;
1274
0
    int fragment = row < d->nRows ? d->grid[row*d->nCols] : d->fragment_end;
1275
0
    QTextDocumentPrivate *p = d->pieceTable;
1276
0
    QTextDocumentPrivate::FragmentIterator it(&p->fragmentMap(), fragment);
1277
0
    return QTextCursorPrivate::fromPosition(p, it.position() - 1);
1278
0
}
1279
1280
/*!
1281
    \fn void QTextTable::setFormat(const QTextTableFormat &format)
1282
1283
    Sets the table's \a format.
1284
1285
    \sa format()
1286
*/
1287
void QTextTable::setFormat(const QTextTableFormat &format)
1288
0
{
1289
0
    QTextTableFormat fmt = format;
1290
    // don't try to change the number of table columns from here
1291
0
    fmt.setColumns(columns());
1292
0
    QTextObject::setFormat(fmt);
1293
0
}
1294
1295
/*!
1296
    \fn QTextTableFormat QTextTable::format() const
1297
1298
    Returns the table's format.
1299
1300
    \sa setFormat()
1301
*/
1302
1303
QT_END_NAMESPACE
1304
1305
#include "moc_qtexttable.cpp"