Coverage Report

Created: 2025-11-16 07:45

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