Warning, file /office/calligra/libs/textlayout/KoTextLayoutTableArea.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002  * Copyright (C) 2009 Elvis Stansvik <elvstone@gmail.org>
0003  * Copyright (C) 2011 C. Boemann <cbo@kogmbh.com>
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Library General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2 of the License, or (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Library General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Library General Public License
0016  * along with this library; see the file COPYING.LIB.  If not, write to
0017  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019  */
0020 
0021 #include "KoTextLayoutTableArea.h"
0022 
0023 #include "KoTextLayoutCellHelper.h"
0024 #include "TableIterator.h"
0025 #include "KoPointedAt.h"
0026 #include "KoCharAreaInfo.h"
0027 
0028 #include <KoTableColumnAndRowStyleManager.h>
0029 #include <KoTableColumnStyle.h>
0030 #include <KoTableRowStyle.h>
0031 #include <KoTableCellStyle.h>
0032 #include <KoTableStyle.h>
0033 #include <KoStyleManager.h>
0034 #include <KoTextTableTemplate.h>
0035 
0036 #include <QTextTable>
0037 #include <QTextTableFormat>
0038 #include <QPainter>
0039 #include <QRectF>
0040 #include <QPair>
0041 
0042 #include "FrameIterator.h"
0043 
0044 #include <algorithm>
0045 
0046 class Q_DECL_HIDDEN KoTextLayoutTableArea::Private
0047 {
0048 public:
0049     Private()
0050      : startOfArea(0)
0051     {
0052     }
0053     QVector<QVector<KoTextLayoutArea *> > cellAreas;
0054     TableIterator *startOfArea;
0055     TableIterator *endOfArea;
0056     bool lastRowHasSomething;
0057     QTextTable *table;
0058     int headerRows;
0059     qreal headerOffsetX;
0060     qreal headerOffsetY;
0061     KoTableColumnAndRowStyleManager carsManager;
0062     qreal tableWidth;
0063     QVector<qreal> headerRowPositions; // we will only fill those that this area covers
0064     QVector<qreal> rowPositions; // we will only fill those that this area covers
0065     QVector<qreal> columnWidths;
0066     QVector<qreal> columnPositions;
0067     bool collapsing;
0068     bool totalMisFit;
0069     KoTextDocumentLayout *documentLayout;
0070 
0071     KoTableCellStyle effectiveCellStyle(const QTextTableCell &tableCell);
0072 };
0073 
0074 KoTableCellStyle KoTextLayoutTableArea::Private::effectiveCellStyle(const QTextTableCell &tableCell)
0075 {
0076     QTextTableFormat tableFormat = table->format();
0077     KoTableCellStyle cellStyle(tableCell.format().toTableCellFormat());
0078     if (documentLayout->styleManager() && table->format().hasProperty(KoTableStyle::TableTemplate)) {
0079         if (KoTextTableTemplate *tableTemplate = documentLayout->styleManager()->tableTemplate(table->format().intProperty(KoTableStyle::TableTemplate))) {
0080             //priorities according to ODF 1.2, 16.18 - table:table-template
0081             if (tableCell.column() == 0 && tableTemplate->firstColumn()
0082                     && tableFormat.boolProperty(KoTableStyle::UseFirstColumnStyles)) {
0083                 cellStyle = *(documentLayout->styleManager()->tableCellStyle(tableTemplate->firstColumn()));
0084                 return cellStyle;
0085             }
0086 
0087             if (tableCell.column() == (table->columns() - 1) && tableTemplate->lastColumn()
0088                     && tableFormat.boolProperty(KoTableStyle::UseLastColumnStyles)) {
0089                 cellStyle = *(documentLayout->styleManager()->tableCellStyle(tableTemplate->lastColumn()));
0090                 return cellStyle;
0091             }
0092 
0093             if (tableCell.row() == 0 && tableTemplate->firstRow()
0094                     && tableFormat.boolProperty(KoTableStyle::UseFirstRowStyles)) {
0095                 cellStyle = *(documentLayout->styleManager()->tableCellStyle(tableTemplate->firstRow()));
0096                 return cellStyle;
0097             }
0098 
0099             if (tableCell.row() == (table->rows() - 1) && tableTemplate->lastRow()
0100                     && tableFormat.boolProperty(KoTableStyle::UseLastRowStyles)) {
0101                 cellStyle = *(documentLayout->styleManager()->tableCellStyle(tableTemplate->lastRow()));
0102                 return cellStyle;
0103             }
0104 
0105             if (((tableCell.row() + 1) % 2) == 0 && tableTemplate->evenRows()
0106                     && tableFormat.boolProperty(KoTableStyle::UseBandingRowStyles)) {
0107                 cellStyle = *(documentLayout->styleManager()->tableCellStyle(tableTemplate->evenRows()));
0108                 return cellStyle;
0109             }
0110 
0111             if (((tableCell.row() + 1) % 2) != 0 && tableTemplate->oddRows()
0112                     && tableFormat.boolProperty(KoTableStyle::UseBandingRowStyles)) {
0113                 cellStyle = *(documentLayout->styleManager()->tableCellStyle(tableTemplate->oddRows()));
0114                 return cellStyle;
0115             }
0116 
0117             if (((tableCell.column() + 1) % 2) == 0 && tableTemplate->evenColumns()
0118                     && tableFormat.boolProperty(KoTableStyle::UseBandingColumnStyles)) {
0119                 cellStyle = *(documentLayout->styleManager()->tableCellStyle(tableTemplate->evenColumns()));
0120                 return cellStyle;
0121             }
0122 
0123             if (((tableCell.column() + 1) % 2) != 0 && tableTemplate->oddColumns()
0124                     && tableFormat.boolProperty(KoTableStyle::UseBandingColumnStyles)) {
0125                 cellStyle = *(documentLayout->styleManager()->tableCellStyle(tableTemplate->oddColumns()));
0126                 return cellStyle;
0127             }
0128 
0129             if (tableTemplate->body()) {
0130                 cellStyle = *(documentLayout->styleManager()->tableCellStyle(tableTemplate->body()));
0131             }
0132         }
0133     }
0134 
0135     return cellStyle;
0136 }
0137 
0138 
0139 KoTextLayoutTableArea::KoTextLayoutTableArea(QTextTable *table, KoTextLayoutArea *parent, KoTextDocumentLayout *documentLayout)
0140   : KoTextLayoutArea(parent, documentLayout)
0141   , d(new Private)
0142 {
0143     Q_ASSERT(table);
0144     Q_ASSERT(parent);
0145 
0146     d->table = table;
0147     d->documentLayout = documentLayout;
0148     d->carsManager = KoTableColumnAndRowStyleManager::getManager(table);
0149 
0150     // Resize geometry vectors for the table.
0151     d->rowPositions.resize(table->rows() + 1);
0152     d->headerRowPositions.resize(table->rows() + 1);
0153     d->cellAreas.resize(table->rows());
0154     for (int row = 0; row < table->rows(); ++row) {
0155         d->cellAreas[row].resize(table->columns());
0156     }
0157     d->collapsing = d->table->format().boolProperty(KoTableStyle::CollapsingBorders);
0158 }
0159 
0160 KoTextLayoutTableArea::~KoTextLayoutTableArea()
0161 {
0162     for (int row = d->startOfArea->row; row < d->cellAreas.size(); ++row) {
0163         for (int col = 0; col < d->cellAreas[row].size(); ++col) {
0164             delete d->cellAreas[row][col];
0165         }
0166     }
0167     delete d->startOfArea;
0168     delete d->endOfArea;
0169     delete d;
0170 }
0171 
0172 KoPointedAt KoTextLayoutTableArea::hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const
0173 {
0174     int firstRow = qMax(d->startOfArea->row, d->headerRows);
0175     int lastRow = d->endOfArea->row;
0176 
0177     if (d->lastRowHasSomething == false) {
0178         --lastRow;
0179     }
0180 
0181     if (lastRow <  d->startOfArea->row) {
0182         return KoPointedAt(); // empty
0183     }
0184 
0185     // Test normal cells.
0186     if (point.y() > d->rowPositions[firstRow] - 3.0 && point.y() < d->rowPositions[lastRow + 1] + 3.0) {
0187         QVector<qreal>::const_iterator start = d->rowPositions.constBegin() + firstRow;
0188         QVector<qreal>::const_iterator end = d->rowPositions.constBegin() + lastRow + 1;
0189         int row = std::lower_bound(start, end, point.y()) - d->rowPositions.constBegin() - 1;
0190         int column = std::lower_bound(d->columnPositions.begin(), d->columnPositions.end(), point.x()) - d->columnPositions.constBegin() - 1;
0191         if (point.y() < d->rowPositions[firstRow]) {
0192             ++row;
0193         }
0194         column = qBound(0, column, d->table->columns() - 1);
0195         KoPointedAt pointedAt;
0196         if (qAbs(d->columnPositions[column] - point.x()) < 3.0) {
0197             pointedAt.tableHit = KoPointedAt::ColumnDivider;
0198         } else if (qAbs(d->columnPositions[column+1] - point.x()) < 3.0) {
0199             pointedAt.tableHit = KoPointedAt::ColumnDivider;
0200             ++column;
0201         } else if (d->columnPositions[0] < point.x()
0202                             && point.x() < d->columnPositions[d->table->columns()]
0203                             && qAbs(d->rowPositions[row] - point.y()) < 3.0) {
0204             pointedAt.tableHit = KoPointedAt::RowDivider;
0205         } else if (d->columnPositions[0] < point.x()
0206                             && point.x() < d->columnPositions[d->table->columns()]
0207                             && qAbs(d->rowPositions[row+1] - point.y()) < 3.0) {
0208             pointedAt.tableHit = KoPointedAt::RowDivider;
0209             ++row;
0210         } else {
0211             QTextTableCell cell = d->table->cellAt(row, column);
0212             pointedAt = d->cellAreas[cell.row()][cell.column()]->hitTest(point, accuracy);
0213         }
0214 
0215         if (pointedAt.tableHit == KoPointedAt::ColumnDivider) {
0216             if (column > 0) {
0217                 pointedAt.tableLeadSize = d->columnPositions[column] - d->columnPositions[column-1];
0218             }
0219             if (column < d->table->columns()) {
0220                 pointedAt.tableTrailSize = d->columnPositions[column+1] - d->columnPositions[column];
0221             }
0222         } else if (pointedAt.tableHit == KoPointedAt::RowDivider) {
0223             if (row > 0) {
0224                 pointedAt.tableLeadSize = d->rowPositions[row] - d->rowPositions[row-1];
0225             }
0226             if (row < d->table->rows()) {
0227                 pointedAt.tableTrailSize = d->rowPositions[row+1] - d->rowPositions[row];
0228             }
0229         }
0230         pointedAt.table = d->table;
0231         pointedAt.tableRowDivider = row;
0232         pointedAt.tableColumnDivider = column;
0233         pointedAt.tableDividerPos = QPointF(d->columnPositions[column],d->rowPositions[row]);
0234         return pointedAt;
0235     }
0236 
0237     // Test header row cells.
0238     QPointF headerPoint = point - QPointF(d->headerOffsetX, d->headerOffsetY);
0239     if (headerPoint.y() > d->headerRowPositions.first() && headerPoint.y() < d->headerRowPositions[d->headerRows]) {
0240         QVector<qreal>::const_iterator start = d->headerRowPositions.constBegin();
0241         QVector<qreal>::const_iterator end = d->headerRowPositions.constBegin() + d->headerRows;
0242         int row = std::lower_bound(start, end, headerPoint.y()) - d->headerRowPositions.constBegin() - 1;
0243         int column = std::lower_bound(d->columnPositions.begin(), d->columnPositions.end(), headerPoint.x()) - d->columnPositions.constBegin() - 1;
0244         column = qBound(0, column, d->table->columns() - 1);
0245         KoPointedAt pointedAt;
0246         if (qAbs(d->columnPositions[column] - headerPoint.x()) < 3.0) {
0247             pointedAt.tableHit = KoPointedAt::ColumnDivider;
0248         } else if (qAbs(d->columnPositions[column+1] - headerPoint.x()) < 3.0) {
0249             pointedAt.tableHit = KoPointedAt::ColumnDivider;
0250             ++column;
0251         } else {
0252             QTextTableCell cell = d->table->cellAt(row, column);
0253             pointedAt = d->cellAreas[cell.row()][cell.column()]->hitTest(headerPoint, accuracy);
0254         }
0255         if (pointedAt.tableHit == KoPointedAt::ColumnDivider) {
0256             if (column > 0) {
0257                 pointedAt.tableLeadSize = d->columnPositions[column] - d->columnPositions[column-1];
0258             }
0259             if (column < d->table->columns()) {
0260                 pointedAt.tableTrailSize = d->columnPositions[column+1] - d->columnPositions[column];
0261             }
0262         }
0263         pointedAt.table = d->table;
0264         pointedAt.tableRowDivider = row;
0265         pointedAt.tableColumnDivider = column;
0266         pointedAt.tableDividerPos = QPointF(d->columnPositions[column],d->rowPositions[row]);
0267         return pointedAt;
0268     }
0269 
0270     return KoPointedAt();
0271 }
0272 
0273 QVector<KoCharAreaInfo> KoTextLayoutTableArea::generateCharAreaInfos() const
0274 {
0275     QVector<KoCharAreaInfo> result;
0276 
0277     if (d->startOfArea == 0) { // We have not been layouted yet
0278         return result;
0279     }
0280 
0281     int lastRow = d->endOfArea->row;
0282     if (d->lastRowHasSomething == false) {
0283         --lastRow;
0284     }
0285     if (lastRow <  d->startOfArea->row) {
0286         return result; // empty
0287     }
0288 
0289     // TODO: check why paint() does use visitedCells only for non-header rows
0290     QSet<QPair<int, int> > visitedCells;
0291 
0292     const int firstRow = qMax(d->startOfArea->row, d->headerRows);
0293     for (int row = 0; row < d->headerRows; ++row) {
0294         // TODO: use lambda to shware inner loop with below
0295         for (int column = 0; column < d->table->columns(); ++column) {
0296             QTextTableCell tableCell = d->table->cellAt(row, column);
0297 
0298             const int testRow = (row == firstRow ? tableCell.row() : row);
0299             if (d->cellAreas[testRow][column] && !visitedCells.contains(QPair<int, int>(testRow, column))) {
0300                 const int column = tableCell.column();
0301 
0302                 result.append(d->cellAreas[testRow][column]->generateCharAreaInfos());
0303                 visitedCells.insert(QPair<int, int>(testRow, column));
0304             }
0305         }
0306     }
0307 
0308     for (int row = firstRow; row <= lastRow; ++row) {
0309         for (int column = 0; column < d->table->columns(); ++column) {
0310             QTextTableCell tableCell = d->table->cellAt(row, column);
0311 
0312             const int testRow = (row == firstRow ? tableCell.row() : row);
0313             if (d->cellAreas[testRow][column] && !visitedCells.contains(QPair<int, int>(testRow, column))) {
0314                 const int column = tableCell.column();
0315 
0316                 result.append(d->cellAreas[testRow][column]->generateCharAreaInfos());
0317                 visitedCells.insert(QPair<int, int>(testRow, column));
0318             }
0319         }
0320     }
0321 
0322     return result;
0323 }
0324 
0325 QRectF KoTextLayoutTableArea::selectionBoundingBox(QTextCursor &cursor) const
0326 {
0327     int lastRow = d->endOfArea->row;
0328     if (d->lastRowHasSomething == false) {
0329         --lastRow;
0330     }
0331     if (lastRow <  d->startOfArea->row) {
0332         return QRectF(); // empty
0333     }
0334 
0335     int firstRow = qMax(d->startOfArea->row, d->headerRows);
0336     QTextTableCell startTableCell = d->table->cellAt(cursor.selectionStart());
0337     QTextTableCell endTableCell = d->table->cellAt(cursor.selectionEnd());
0338 
0339     if (startTableCell == endTableCell) {
0340         if (startTableCell.row() < d->startOfArea->row || startTableCell.row() > lastRow) {
0341             return QRectF(); // cell is not in this area
0342         }
0343         KoTextLayoutArea *area = d->cellAreas[startTableCell.row()][startTableCell.column()];
0344         Q_ASSERT(area);
0345         return area->selectionBoundingBox(cursor);
0346     } else {
0347         int selectionRow;
0348         int selectionColumn;
0349         int selectionRowSpan;
0350         int selectionColumnSpan;
0351         cursor.selectedTableCells(&selectionRow, &selectionRowSpan, &selectionColumn, &selectionColumnSpan);
0352 
0353         qreal top, bottom;
0354 
0355         if (selectionRow < d->headerRows) {
0356             top = d->headerRowPositions[selectionRow] + d->headerOffsetY;
0357         } else {
0358             top = d->rowPositions[qMin(qMax(firstRow, selectionRow), lastRow)];
0359         }
0360 
0361         if (selectionRow + selectionRowSpan < d->headerRows) {
0362             bottom = d->headerRowPositions[selectionRow + selectionRowSpan] + d->headerOffsetY;
0363         } else {
0364             bottom = d->rowPositions[d->headerRows] + d->headerOffsetY;
0365             if (selectionRow + selectionRowSpan >= firstRow) {
0366                 bottom = d->rowPositions[qMin(selectionRow + selectionRowSpan, lastRow + 1)];
0367             }
0368         }
0369 
0370         return QRectF(d->columnPositions[selectionColumn], top,
0371                       d->columnPositions[selectionColumn + selectionColumnSpan] - d->columnPositions[selectionColumn], bottom - top);
0372     }
0373 }
0374 
0375 bool KoTextLayoutTableArea::layoutTable(TableIterator *cursor)
0376 {
0377     d->startOfArea = new TableIterator(cursor);
0378     d->headerRows = cursor->headerRows;
0379     d->totalMisFit = false;
0380 
0381     // If table is done we create an empty area and return true
0382     if (cursor->row == d->table->rows()) {
0383         setBottom(top());
0384         d->endOfArea = new TableIterator(cursor);
0385         return true;
0386     }
0387     layoutColumns();
0388 
0389     bool first = cursor->row == 0 && (d->cellAreas[0][0] == 0);
0390     if (first) { // are we at the beginning of the table
0391         cursor->row = 0;
0392         d->rowPositions[0] = top() + d->table->format().topMargin();
0393         d->headerOffsetX = 0;
0394         d->headerOffsetY = 0;
0395     } else {
0396         for (int row = 0; row < d->headerRows; ++row) {
0397             // Copy header rows
0398             d->headerRowPositions[row] = cursor->headerRowPositions[row];
0399             for (int col = 0; col < d->table->columns(); ++col) {
0400                 d->cellAreas[row][col] = cursor->headerCellAreas[row][col];
0401             }
0402         }
0403 
0404         if (d->headerRows) {
0405             // Also set the position of the border below headers
0406             d->headerRowPositions[d->headerRows] = cursor->headerRowPositions[d->headerRows];
0407         }
0408 
0409         // If headerRows == 0 then the following reduces to: d->rowPositions[cursor->row] = top()
0410         d->headerOffsetY = top() - d->headerRowPositions[0];
0411         d->rowPositions[cursor->row] = d->headerRowPositions[d->headerRows] + d->headerOffsetY;
0412 
0413         // headerOffsetX should also be set
0414         d->headerOffsetX = d->columnPositions[0] - cursor->headerPositionX;
0415     }
0416 
0417     bool complete = first;
0418     qreal topBorderWidth = 0;
0419     qreal bottomBorderWidth = 0;
0420     qreal dummyWidth = 0;
0421 
0422     collectBorderThicknesss(cursor->row - 1, dummyWidth, topBorderWidth);
0423     collectBorderThicknesss(cursor->row, topBorderWidth, bottomBorderWidth);
0424     do {
0425         qreal nextBottomBorderWidth = 0;
0426         collectBorderThicknesss(cursor->row+1, bottomBorderWidth, nextBottomBorderWidth);
0427 
0428         d->lastRowHasSomething = false;
0429 
0430         complete = layoutRow(cursor, topBorderWidth, bottomBorderWidth);
0431 
0432         setBottom(d->rowPositions[cursor->row + 1] + bottomBorderWidth);
0433         topBorderWidth = bottomBorderWidth;
0434         bottomBorderWidth = nextBottomBorderWidth;
0435 
0436 
0437         if (complete) {
0438             if (cursor->row >= d->headerRows) {
0439                 setVirginPage(false);
0440             }
0441             cursor->row++;
0442         }
0443     } while (complete && cursor->row < d->table->rows());
0444 
0445     if (cursor->row == d->table->rows()) {
0446         d->lastRowHasSomething = false;
0447     }
0448 
0449 
0450     if (first) { // were we at the beginning of the table
0451         for (int row = 0; row < d->headerRows; ++row) {
0452             // Copy header rows
0453             cursor->headerRowPositions[row] = d->rowPositions[row];
0454             d->headerRowPositions[row] = d->rowPositions[row];
0455             for (int col = 0; col < d->table->columns(); ++col) {
0456                 cursor->headerCellAreas[row][col] = d->cellAreas[row][col];
0457             }
0458         }
0459         if (d->headerRows) {
0460             // Also set the position of the border below headers
0461             cursor->headerRowPositions[d->headerRows] = d->rowPositions[d->headerRows];
0462             d->headerRowPositions[d->headerRows] = d->rowPositions[d->headerRows];
0463         }
0464         cursor->headerPositionX = d->columnPositions[0];
0465 
0466         if (!virginPage() && d->totalMisFit) {
0467             //if we couldn't fit the header rows plus some then don't even try
0468             cursor->row = 0;
0469             nukeRow(cursor);
0470         }
0471     }
0472 
0473     d->endOfArea = new TableIterator(cursor);
0474 
0475     return complete;
0476 }
0477 
0478 
0479 void KoTextLayoutTableArea::layoutColumns()
0480 {
0481     QTextTableFormat tableFormat = d->table->format();
0482 
0483     d->columnPositions.resize(d->table->columns() + 1);
0484     d->columnWidths.resize(d->table->columns() + 1);
0485 
0486     // Table width.
0487     d->tableWidth = 0;
0488     qreal parentWidth = right() - left();
0489     if (tableFormat.width().rawValue() == 0 || tableFormat.alignment() == Qt::AlignJustify) {
0490         // We got a zero width value or alignment is justify, so use 100% of parent.
0491         d->tableWidth = parentWidth - tableFormat.leftMargin() - tableFormat.rightMargin();
0492     } else {
0493         if (tableFormat.width().type() == QTextLength::FixedLength) {
0494             // Fixed length value, so use the raw value directly.
0495             d->tableWidth = tableFormat.width().rawValue();
0496         } else if (tableFormat.width().type() == QTextLength::PercentageLength) {
0497             // Percentage length value, so use a percentage of parent width.
0498             d->tableWidth = tableFormat.width().rawValue() * (parentWidth / 100)
0499                 - tableFormat.leftMargin() - tableFormat.rightMargin();
0500         } else {
0501             // Unknown length type, so use 100% of parent.
0502             d->tableWidth = parentWidth - tableFormat.leftMargin() - tableFormat.rightMargin();
0503         }
0504     }
0505 
0506     // Column widths.
0507     qreal availableWidth = d->tableWidth; // Width available for columns.
0508     QList<int> fixedWidthColumns; // List of fixed width columns.
0509     QList<int> relativeWidthColumns; // List of relative width columns.
0510     qreal relativeWidthSum = 0; // Sum of relative column width values.
0511     int numNonStyleColumns = 0;
0512     for (int col = 0; col < d->table->columns(); ++col) {
0513         KoTableColumnStyle columnStyle = d->carsManager.columnStyle(col);
0514 
0515         if (columnStyle.hasProperty(KoTableColumnStyle::RelativeColumnWidth)) {
0516             // Relative width specified. Will be handled in the next loop.
0517             d->columnWidths[col] = 0.0;
0518             relativeWidthColumns.append(col);
0519             relativeWidthSum += columnStyle.relativeColumnWidth();
0520         } else if (columnStyle.hasProperty(KoTableColumnStyle::ColumnWidth)) {
0521             // Only width specified, so use it.
0522             d->columnWidths[col] = columnStyle.columnWidth();
0523             fixedWidthColumns.append(col);
0524             availableWidth -= columnStyle.columnWidth();
0525         } else {
0526             // Neither width nor relative width specified.
0527             d->columnWidths[col] = 0.0;
0528             relativeWidthColumns.append(col); // handle it as a relative width column without asking for anything
0529             ++numNonStyleColumns;
0530         }
0531     }
0532 
0533     // Handle the case that the fixed size columns are larger then the defined table width
0534     if (availableWidth < 0.0) {
0535         if (tableFormat.width().rawValue() == 0 && fixedWidthColumns.count() > 0) {
0536             // If not table width was defined then we need to scale down the fixed size columns so they match
0537             // into the width of the table.
0538             qreal diff = (-availableWidth) / qreal(fixedWidthColumns.count());
0539             foreach(int col, fixedWidthColumns) {
0540                 d->columnWidths[col] = qMax(qreal(0.0), d->columnWidths[col] - diff);
0541             }
0542         }
0543         availableWidth = 0.0;
0544     }
0545 
0546     // Calculate width to those columns that don't actually request it
0547     qreal widthForNonWidthColumn = ((1.0 - qMin<qreal>(relativeWidthSum, 1.0)) * availableWidth);
0548     availableWidth -= widthForNonWidthColumn; // might as well do this calc before dividing by numNonStyleColumns
0549     if (numNonStyleColumns > 0 && widthForNonWidthColumn > 0.0) {
0550         widthForNonWidthColumn /= numNonStyleColumns;
0551     }
0552 
0553     // Relative column widths have now been summed up and can be distributed.
0554     foreach (int col, relativeWidthColumns) {
0555         KoTableColumnStyle columnStyle = d->carsManager.columnStyle(col);
0556         if (columnStyle.hasProperty(KoTableColumnStyle::RelativeColumnWidth) || columnStyle.hasProperty(KoTableColumnStyle::ColumnWidth)) {
0557             d->columnWidths[col] = qMax<qreal>(columnStyle.relativeColumnWidth() * availableWidth / relativeWidthSum, 0.0);
0558         } else {
0559             d->columnWidths[col] = widthForNonWidthColumn;
0560         }
0561     }
0562 
0563     // Column positions.
0564     qreal columnPosition = left();
0565     qreal columnOffset = tableFormat.leftMargin();
0566     if (tableFormat.alignment() == Qt::AlignRight) {
0567         // Table is right-aligned, so add all of the remaining space.
0568         columnOffset += parentWidth - d->tableWidth;
0569     }
0570     if (tableFormat.alignment() == Qt::AlignHCenter) {
0571         // Table is centered, so add half of the remaining space.
0572         columnOffset += (parentWidth - d->tableWidth) / 2;
0573     }
0574     for (int col = 0; col < d->columnPositions.size(); ++col) {
0575         d->columnPositions[col] = columnPosition + columnOffset;
0576         // Increment by this column's width.
0577         columnPosition += d->columnWidths[col];
0578     }
0579 
0580     // Borders can be outside of the cell (outer-borders) in which case it's need
0581     // to take them into account to not cut content off.
0582     qreal leftBorder = 0.0;
0583     qreal rightBorder = 0.0;
0584     for (int row = 0; row < d->table->rows(); ++row) {
0585         QTextTableCell leftCell = d->table->cellAt(row, 0);
0586         KoTableCellStyle leftCellStyle = d->effectiveCellStyle(leftCell);
0587         leftBorder = qMax(leftBorder, leftCellStyle.leftOuterBorderWidth());
0588 
0589         QTextTableCell rightCell = d->table->cellAt(row, d->table->columns() - 1);
0590         KoTableCellStyle rightCellStyle = d->effectiveCellStyle(rightCell);
0591         rightBorder = qMax(rightBorder, rightCellStyle.rightOuterBorderWidth());
0592     }
0593 
0594     expandBoundingLeft(d->columnPositions[0] - leftBorder);
0595     expandBoundingRight(d->columnPositions[d->table->columns()] + rightBorder + leftBorder);
0596 }
0597 
0598 void KoTextLayoutTableArea::collectBorderThicknesss(int row, qreal &topBorderWidth, qreal &bottomBorderWidth)
0599 {
0600     int col = 0;
0601 
0602     if (d->collapsing && row >= 0 && row < d->table->rows()) {
0603         // let's collect the border info
0604         while (col < d->table->columns()) {
0605             QTextTableCell cell = d->table->cellAt(row, col);
0606 
0607             if (row == cell.row() + cell.rowSpan() - 1) {
0608                 /*
0609                 * This cell ends vertically in this row, and hence should
0610                 * contribute to the bottom border.
0611                 */
0612                 KoTableCellStyle cellStyle = d->effectiveCellStyle(cell);
0613 
0614                 topBorderWidth = qMax(cellStyle.topBorderWidth(), topBorderWidth);
0615                 bottomBorderWidth = qMax(cellStyle.bottomBorderWidth(), bottomBorderWidth);
0616             }
0617             col += cell.columnSpan(); // Skip across column spans.
0618         }
0619     }
0620 }
0621 
0622 void KoTextLayoutTableArea::nukeRow(TableIterator *cursor)
0623 {
0624     for (int column = 0; column < d->table->columns(); ++column) {
0625         delete d->cellAreas[cursor->row][column];
0626         d->cellAreas[cursor->row][column] = 0;
0627         delete cursor->frameIterators[column];
0628         cursor->frameIterators[column] = 0;
0629     }
0630     d->lastRowHasSomething = false;
0631 }
0632 
0633 bool KoTextLayoutTableArea::layoutRow(TableIterator *cursor, qreal topBorderWidth, qreal bottomBorderWidth)
0634 {
0635     int row = cursor->row;
0636 
0637     Q_ASSERT(row >= 0);
0638     Q_ASSERT(row < d->table->rows());
0639 
0640     /*
0641      * Implementation Note:
0642      *
0643      * An undocumented behavior of QTextTable::cellAt is that requesting a
0644      * cell that is covered by a spanning cell will return the cell that
0645      * spans over the requested cell. Example:
0646      *
0647      * +------------+------------+
0648      * |            |            |
0649      * |            |            |
0650      * |            +------------+
0651      * |            |            |
0652      * |            |            |
0653      * +------------+------------+
0654      *
0655      * table.cellAt(1, 0).row() // Will return 0.
0656      *
0657      * In the code below, we rely on this behavior to determine wheather
0658      * a cell "vertically" ends in the current row, as those are the only
0659      * cells that should contribute to the row height.
0660      */
0661 
0662     KoTableRowStyle rowStyle = d->carsManager.rowStyle(row);
0663     qreal rowHeight = rowStyle.rowHeight();
0664     bool rowHasExactHeight = rowStyle.hasProperty(KoTableRowStyle::RowHeight);
0665     qreal rowBottom;
0666     if (rowHasExactHeight) {
0667         rowBottom = d->rowPositions[row] + rowHeight;
0668     } else {
0669         rowBottom = d->rowPositions[row] + rowStyle.minimumRowHeight();
0670     }
0671 
0672     if (rowBottom > maximumAllowedBottom()) {
0673         d->rowPositions[row+1] = d->rowPositions[row];
0674         if (cursor->row > d->startOfArea->row) {
0675             cursor->row--;
0676             layoutMergedCellsNotEnding(cursor, topBorderWidth, bottomBorderWidth, rowBottom);
0677             cursor->row++;
0678         }
0679         return false; // we can't honour minimum or fixed height so don't even try
0680     }
0681 
0682     bool allCellsFullyDone = true;
0683     bool anyCellTried = false;
0684     bool noCellsFitted = true;
0685     int col = 0;
0686     while (col < d->table->columns()) {
0687         // Get the cell format.
0688         QTextTableCell cell = d->table->cellAt(row, col);
0689 
0690         if (row == cell.row() + cell.rowSpan() - 1) {
0691             /*
0692              * This cell ends vertically in this row, and hence should
0693              * contribute to the row height.
0694              */
0695             bool ignoreMisFittingCell = false;
0696             KoTableCellStyle cellStyle = d->effectiveCellStyle(cell);
0697             anyCellTried = true;
0698 
0699             qreal maxBottom = maximumAllowedBottom();
0700 
0701             qreal requiredRowHeight = cellStyle.bottomPadding() + cellStyle.bottomPadding();
0702 
0703             if (rowHasExactHeight) {
0704                 maxBottom = qMin(d->rowPositions[row] + rowHeight, maxBottom);
0705             }
0706             maxBottom -= cellStyle.bottomPadding();
0707 
0708             qreal areaTop = d->rowPositions[qMax(cell.row(), d->startOfArea->row)] + cellStyle.topPadding();
0709 
0710             if (d->collapsing) {
0711                 areaTop += topBorderWidth;
0712                 maxBottom -= bottomBorderWidth;
0713                 requiredRowHeight += bottomBorderWidth + topBorderWidth;
0714             } else {
0715                 areaTop += cellStyle.topBorderWidth();
0716                 maxBottom -= cellStyle.bottomBorderWidth();
0717                 requiredRowHeight += cellStyle.bottomBorderWidth() + cellStyle.topBorderWidth();
0718             }
0719 
0720             if (rowHasExactHeight && (rowHeight < requiredRowHeight))
0721             {
0722                 ignoreMisFittingCell = true;
0723             }
0724 
0725             if (maxBottom < areaTop && !ignoreMisFittingCell) {
0726                 d->rowPositions[row+1] = d->rowPositions[row];
0727                 nukeRow(cursor);
0728                 if (cursor->row > d->startOfArea->row) {
0729                     cursor->row--;
0730                     layoutMergedCellsNotEnding(cursor, topBorderWidth, bottomBorderWidth, rowBottom);
0731                     cursor->row++;
0732                 }
0733                 return false; // we can't honour the borders so give up doing row
0734             }
0735 
0736             KoTextLayoutArea *cellArea = new KoTextLayoutArea(this, documentLayout());
0737             d->cellAreas[cell.row()][cell.column()] = cellArea;
0738 
0739             qreal left = d->columnPositions[col] + cellStyle.leftPadding() + cellStyle.leftInnerBorderWidth();
0740             qreal right = qMax(left, d->columnPositions[col+cell.columnSpan()] - cellStyle.rightPadding() - cellStyle.rightInnerBorderWidth());
0741 
0742             cellArea->setReferenceRect(
0743                     left,
0744                     right,
0745                     areaTop,
0746                     maxBottom);
0747 
0748             cellArea->setVirginPage(virginPage());
0749             cellArea->setLayoutEnvironmentResctictions(true, true);
0750 
0751             FrameIterator *cellCursor = cursor->frameIterator(col);
0752 
0753             bool cellFully = cellArea->layout(cellCursor);
0754             allCellsFullyDone = allCellsFullyDone && (cellFully || rowHasExactHeight);
0755 
0756             noCellsFitted = noCellsFitted && (cellArea->top() >= cellArea->bottom()) && !ignoreMisFittingCell;
0757 
0758             if (!rowHasExactHeight) {
0759                 /*
0760                  * Now we know how much height this cell contributes to the row,
0761                  * and can determine wheather the row height will grow.
0762                  */
0763                 if (d->collapsing) {
0764                     rowBottom = qMax(cellArea->bottom() + cellStyle.bottomPadding(), rowBottom);
0765                 } else {
0766                     rowBottom = qMax(cellArea->bottom() + cellStyle.bottomPadding() + cellStyle.bottomBorderWidth(), rowBottom);
0767                 }
0768                 rowBottom = qMax(rowBottom, documentLayout()->maxYOfAnchoredObstructions(cell.firstCursorPosition().position(), cell.lastCursorPosition().position()));
0769             }
0770 
0771 
0772             d->lastRowHasSomething = true; // last row contains something (even if empty)
0773         }
0774 
0775         col += cell.columnSpan(); // Skip across column spans.
0776 
0777     }
0778 
0779     if (allCellsFullyDone) {
0780         for (col = 0; col < d->table->columns(); ++col) {
0781             QTextTableCell cell = d->table->cellAt(row, col);
0782 
0783             if (row == cell.row() + cell.rowSpan() - 1) {
0784                 delete cursor->frameIterators[col];
0785                 cursor->frameIterators[col] = 0;
0786             }
0787         }
0788     }
0789 
0790     if (anyCellTried && noCellsFitted && !rowHasExactHeight && !allCellsFullyDone) {
0791         d->rowPositions[row+1] = d->rowPositions[row];
0792         nukeRow(cursor);
0793         if (cursor->row > d->startOfArea->row) {
0794             cursor->row--;
0795             layoutMergedCellsNotEnding(cursor, topBorderWidth, bottomBorderWidth, rowBottom);
0796             cursor->row++;
0797         }
0798         return false; // we can't honour the anything inside so give up doing row
0799     }
0800 
0801     if (noCellsFitted && d->headerRows && row <= d->headerRows) {
0802         d->totalMisFit = true;
0803     }
0804 
0805     if (!allCellsFullyDone) {
0806         layoutMergedCellsNotEnding(cursor, topBorderWidth, bottomBorderWidth, rowBottom);
0807     } else {
0808         // Cells all ended naturally, so we can now do vertical alignment
0809         // Stop! Other odf implementors also only do it if all cells are fully done
0810         col = 0;
0811         while (col < d->table->columns()) {
0812             QTextTableCell cell = d->table->cellAt(row, col);
0813 
0814             if (row == cell.row() + cell.rowSpan() - 1) {
0815                 // cell ended in this row
0816                 KoTextLayoutArea *cellArea = d->cellAreas[cell.row()][cell.column()];
0817                 KoTableCellStyle cellStyle = d->effectiveCellStyle(cell);
0818 
0819                 if (cellStyle.alignment() & Qt::AlignBottom) {
0820                     if (true /*FIXME test no page based shapes interfering*/) {
0821                         cellArea->setVerticalAlignOffset(rowBottom - cellArea->bottom());
0822                     }
0823                 }
0824                 if (cellStyle.alignment() & Qt::AlignVCenter) {
0825                     if (true /*FIXME test no page based shapes interfering*/) {
0826                         cellArea->setVerticalAlignOffset((rowBottom - cellArea->bottom()) / 2);
0827                     }
0828                 }
0829             }
0830             col += cell.columnSpan(); // Skip across column spans.
0831         }
0832     }
0833 
0834     // Adjust Y position of NEXT row.
0835     // This is nice since the outside layout routine relies on the next row having a correct y position
0836     // the first row y position is set in layout()
0837     d->rowPositions[row+1] = rowBottom;
0838 
0839     return allCellsFullyDone;
0840 }
0841 
0842 bool KoTextLayoutTableArea::layoutMergedCellsNotEnding(TableIterator *cursor, qreal topBorderWidth, qreal bottomBorderWidth, qreal rowBottom)
0843 {
0844     Q_UNUSED(topBorderWidth)
0845     Q_UNUSED(bottomBorderWidth)
0846 
0847     // Let's make sure all merged cells in this row, that don't end in this row get's a layout
0848     int row = cursor->row;
0849     int col = 0;
0850     while (col < d->table->columns()) {
0851         QTextTableCell cell = d->table->cellAt(row, col);
0852         if (row != cell.row() + cell.rowSpan() - 1) {
0853         // TODO do all of the following like in layoutRow()
0854             KoTableCellStyle cellStyle = d->effectiveCellStyle(cell);
0855 
0856             KoTextLayoutArea *cellArea = new KoTextLayoutArea(this, documentLayout());
0857 
0858             d->cellAreas[cell.row()][cell.column()] = cellArea;
0859 
0860             qreal left = d->columnPositions[col] + cellStyle.leftPadding() + cellStyle.leftInnerBorderWidth();
0861             qreal right = qMax(left, d->columnPositions[col+cell.columnSpan()] - cellStyle.rightPadding() - cellStyle.rightInnerBorderWidth());
0862 
0863             cellArea->setReferenceRect(
0864                     left,
0865                     right,
0866                     d->rowPositions[qMax(cell.row(), d->startOfArea->row)] + cellStyle.topPadding() + cellStyle.topBorderWidth(),
0867                     rowBottom - cellStyle.bottomPadding() - cellStyle.bottomBorderWidth());
0868 
0869             cellArea->setVirginPage(virginPage());
0870             cellArea->setLayoutEnvironmentResctictions(true, true);
0871 
0872             FrameIterator *cellCursor =  cursor->frameIterator(col);
0873 
0874             cellArea->layout(cellCursor);
0875             if (cellArea->top() < cellArea->bottom() && row == d->headerRows) {
0876                 d->totalMisFit = false;
0877             }
0878         }
0879         col += cell.columnSpan(); // Skip across column spans.
0880     }
0881     return true;
0882 }
0883 
0884 void KoTextLayoutTableArea::paint(QPainter *painter, const KoTextDocumentLayout::PaintContext &context)
0885 {
0886     if (d->startOfArea == 0) // We have not been layouted yet
0887         return;
0888 
0889     int lastRow = d->endOfArea->row;
0890     if (d->lastRowHasSomething == false) {
0891         --lastRow;
0892     }
0893     if (lastRow <  d->startOfArea->row) {
0894         return; // empty
0895     }
0896 
0897     int firstRow = qMax(d->startOfArea->row, d->headerRows);
0898 
0899     // Draw table background
0900     qreal topY = d->headerRows ? d->rowPositions[0] : d->rowPositions[firstRow];
0901     QRectF tableRect(d->columnPositions[0], topY, d->tableWidth,
0902                      d->headerRowPositions[d->headerRows] - d->headerRowPositions[0]
0903                      + d->rowPositions[lastRow+1] - d->rowPositions[firstRow]);
0904 
0905     painter->fillRect(tableRect, d->table->format().background());
0906 
0907     KoTextDocumentLayout::PaintContext cellContext = context;
0908     QColor tableBackground = context.background;
0909     if (d->table->format().hasProperty(QTextFormat::BackgroundBrush)) {
0910         tableBackground = d->table->format().background().color();
0911     }
0912 
0913     // Draw header row backgrounds
0914     for (int row = 0; row < d->headerRows; ++row) {
0915         QRectF rowRect(d->columnPositions[0], d->headerRowPositions[row], d->tableWidth, d->headerRowPositions[row+1] - d->headerRowPositions[row]);
0916 
0917         KoTableRowStyle rowStyle = d->carsManager.rowStyle(row);
0918 
0919         rowRect.translate(0, d->headerOffsetY);
0920 
0921         painter->fillRect(rowRect, rowStyle.background());
0922     }
0923 
0924     // Draw plain row backgrounds
0925     for (int row = firstRow; row <= lastRow; ++row) {
0926         QRectF rowRect(d->columnPositions[0], d->rowPositions[row], d->tableWidth, d->rowPositions[row+1] - d->rowPositions[row]);
0927 
0928         KoTableRowStyle rowStyle = d->carsManager.rowStyle(row);
0929 
0930         painter->fillRect(rowRect, rowStyle.background());
0931     }
0932 
0933     QSet<QPair<int, int> > visitedCells;
0934     // Draw cell backgrounds and contents.
0935     for (int row = firstRow; row <= lastRow; ++row) {
0936         for (int column = 0; column < d->table->columns(); ++column) {
0937             QTextTableCell tableCell = d->table->cellAt(row, column);
0938 
0939             int testRow = (row == firstRow ? tableCell.row() : row);
0940             if (d->cellAreas[testRow][column] && !visitedCells.contains(QPair<int, int>(testRow, column))) {
0941                 cellContext.background = tableBackground;
0942                 QBrush bgBrush = d->effectiveCellStyle(tableCell).background();
0943                 if (bgBrush != Qt::NoBrush) {
0944                     cellContext.background = bgBrush.color();
0945                 }
0946                 paintCell(painter, cellContext, tableCell, d->cellAreas[testRow][column]);
0947                 visitedCells.insert(QPair<int, int>(testRow, column));
0948             }
0949         }
0950     }
0951 
0952     painter->translate(0, d->headerOffsetY);
0953 
0954     QVector<QLineF> accuBlankBorders;
0955 
0956     bool hasAntialiasing = painter->testRenderHint(QPainter::Antialiasing);
0957 
0958     // Draw header row cell backgrounds and contents.
0959     for (int row = 0; row < d->headerRows; ++row) {
0960         for (int column = 0; column < d->table->columns(); ++column) {
0961             QTextTableCell tableCell = d->table->cellAt(row, column);
0962 
0963             int testRow = row == firstRow ? tableCell.row() : row;
0964             if (d->cellAreas[testRow][column]) {
0965                 cellContext.background = tableBackground;
0966                 QBrush bgBrush = d->effectiveCellStyle(tableCell).background();
0967                 if (bgBrush != Qt::NoBrush) {
0968                     cellContext.background = bgBrush.color();
0969                 }
0970                 paintCell(painter, cellContext, tableCell, d->cellAreas[testRow][column]);
0971             }
0972         }
0973     }
0974     // Draw header row cell borders.(need to be second step so neibor cells don't overwrite)
0975     for (int row = 0; row < d->headerRows; ++row) {
0976         for (int column = 0; column < d->table->columns(); ++column) {
0977             QTextTableCell tableCell = d->table->cellAt(row, column);
0978 
0979             int testRow = row == firstRow ? tableCell.row() : row;
0980             if (d->cellAreas[testRow][column]) {
0981                 painter->setRenderHint(QPainter::Antialiasing, true);
0982                 paintCellBorders(painter, context, tableCell, false, lastRow, &accuBlankBorders);
0983                 painter->setRenderHint(QPainter::Antialiasing, hasAntialiasing);
0984             }
0985         }
0986     }
0987     for (int i = 0; i < accuBlankBorders.size(); ++i) {
0988         accuBlankBorders[i].translate(0, d->headerOffsetY);
0989     }
0990 
0991     painter->translate(0, -d->headerOffsetY);
0992 
0993     // Draw cell borders.
0994     bool topRow = !d->headerRows && firstRow != 0; // are we top row in this area
0995 
0996     painter->setRenderHint(QPainter::Antialiasing, true);
0997     visitedCells.clear();
0998     for (int row = firstRow; row <= lastRow; ++row) {
0999         for (int column = 0; column < d->table->columns(); ++column) {
1000             QTextTableCell tableCell = d->table->cellAt(row, column);
1001 
1002             int testRow = row == firstRow ? tableCell.row() : row;
1003             if (d->cellAreas[testRow][column] && !visitedCells.contains(QPair<int, int>(testRow, column))) {
1004                 paintCellBorders(painter, context, tableCell, topRow, lastRow, &accuBlankBorders);
1005                 visitedCells.insert(QPair<int, int>(testRow, column));
1006             }
1007         }
1008         topRow = false;
1009     }
1010 
1011     painter->setRenderHint(QPainter::Antialiasing, hasAntialiasing);
1012 
1013     if (context.showTableBorders) {
1014         QPen pen(painter->pen());
1015         painter->setPen(QPen(QColor(0,0,0,96), 0));
1016         painter->drawLines(accuBlankBorders);
1017         painter->setPen(pen);
1018     }
1019 }
1020 
1021 void KoTextLayoutTableArea::paintCell(QPainter *painter, const KoTextDocumentLayout::PaintContext &context, const QTextTableCell &tableCell, KoTextLayoutArea *frameArea)
1022 {
1023     int row = tableCell.row();
1024     int column = tableCell.column();
1025 
1026     // This is an actual cell we want to draw, and not a covered one.
1027     QRectF bRect(cellBoundingRect(tableCell));
1028 
1029     painter->save();
1030     painter->setClipRect(bRect, Qt::IntersectClip);
1031 
1032     // Possibly paint the background of the cell
1033     QBrush background(d->effectiveCellStyle(tableCell).background());
1034     if (background != Qt::NoBrush) {
1035         painter->fillRect(bRect, background);
1036     }
1037 
1038     // Possibly paint the selection of the entire cell
1039     if (context.showSelections) {
1040         foreach(const QAbstractTextDocumentLayout::Selection & selection,   context.textContext.selections) {
1041             QTextTableCell startTableCell = d->table->cellAt(selection.cursor.selectionStart());
1042             QTextTableCell endTableCell = d->table->cellAt(selection.cursor.selectionEnd());
1043 
1044             if (startTableCell.isValid() && startTableCell != endTableCell) {
1045                 int selectionRow;
1046                 int selectionColumn;
1047                 int selectionRowSpan;
1048                 int selectionColumnSpan;
1049                 selection.cursor.selectedTableCells(&selectionRow, &selectionRowSpan, &selectionColumn, &selectionColumnSpan);
1050                 if (row >= selectionRow && column>=selectionColumn
1051                     && row < selectionRow + selectionRowSpan
1052                     && column < selectionColumn + selectionColumnSpan) {
1053                     painter->fillRect(bRect, selection.format.background());
1054                 }
1055             } else if (selection.cursor.selectionStart()  < d->table->firstPosition()
1056                 && selection.cursor.selectionEnd() > d->table->lastPosition()) {
1057                 painter->fillRect(bRect, selection.format.background());
1058             }
1059         }
1060     }
1061 
1062     if (row < d->headerRows) {
1063         painter->translate(d->headerOffsetX, 0);
1064     }
1065 
1066     // Paint the content of the cellArea
1067     frameArea->paint(painter, context);
1068 
1069     painter->restore();
1070 }
1071 
1072 void KoTextLayoutTableArea::paintCellBorders(QPainter *painter, const KoTextDocumentLayout::PaintContext &context, const QTextTableCell &tableCell, bool topRow, int lastRow, QVector<QLineF> *accuBlankBorders)
1073 {
1074     Q_UNUSED(context);
1075 
1076     int row = tableCell.row();
1077     int column = tableCell.column();
1078 
1079     // This is an actual cell we want to draw, and not a covered one.
1080     KoTableCellStyle cellStyle = d->effectiveCellStyle(tableCell);
1081     KoTextLayoutCellHelper cellStyleHelper(cellStyle);
1082 
1083     QRectF bRect = cellBoundingRect(tableCell);
1084 
1085     if (d->collapsing) {
1086         // First the horizontal borders
1087         if (row == 0) {
1088             cellStyleHelper.drawTopHorizontalBorder(*painter, bRect.x(), bRect.y(), bRect.width(), accuBlankBorders);
1089         }
1090         if (topRow && row != 0) {
1091             // in collapsing mode we need to also paint the top border of the area
1092             int c = column;
1093             while (c < column + tableCell.columnSpan()) {
1094                 QTextTableCell tableCellAbove = d->table->cellAt(row - 1, c);
1095                 QRectF aboveBRect = cellBoundingRect(tableCellAbove);
1096                 qreal x = qMax(bRect.x(), aboveBRect.x());
1097                 qreal x2 = qMin(bRect.right(), aboveBRect.right());
1098                 KoTableCellStyle cellAboveStyle = d->effectiveCellStyle(tableCellAbove);
1099                 KoTextLayoutCellHelper cellAboveStyleHelper(cellAboveStyle);
1100                 cellAboveStyleHelper.drawSharedHorizontalBorder(*painter, cellStyle, x, bRect.y(), x2 - x, accuBlankBorders);
1101                 c = tableCellAbove.column() + tableCellAbove.columnSpan();
1102             }
1103         }
1104         if (row + tableCell.rowSpan() == d->table->rows()) {
1105             // we hit the bottom of the table so just draw the bottom border
1106             cellStyleHelper.drawBottomHorizontalBorder(*painter, bRect.x(), bRect.bottom(), bRect.width(), accuBlankBorders);
1107         } else {
1108             int c = column;
1109             while (c < column + tableCell.columnSpan()) {
1110                 QTextTableCell tableCellBelow = d->table->cellAt(row == d->headerRows - 1 ?
1111                             d->startOfArea->row : row + tableCell.rowSpan(), c);
1112                 QRectF belowBRect = cellBoundingRect(tableCellBelow);
1113                 qreal x = qMax(bRect.x(), belowBRect.x());
1114                 qreal x2 = qMin(bRect.right(), belowBRect.right());
1115                 KoTableCellStyle cellBelowStyle = d->effectiveCellStyle(tableCellBelow);
1116                 cellStyleHelper.drawSharedHorizontalBorder(*painter, cellBelowStyle, x, bRect.bottom(), x2 - x, accuBlankBorders);
1117                 c = tableCellBelow.column() + tableCellBelow.columnSpan();
1118             }
1119         }
1120 
1121         // And then the same treatment for vertical borders
1122         if (column == 0) {
1123             cellStyleHelper.drawLeftmostVerticalBorder(*painter, bRect.x(), bRect.y(), bRect.height() + cellStyle.bottomOuterBorderWidth(), accuBlankBorders);
1124         }
1125         if (column + tableCell.columnSpan() == d->table->columns()) {
1126             // we hit the rightmost edge of the table so draw the rightmost border
1127             cellStyleHelper.drawRightmostVerticalBorder(*painter, bRect.right(), bRect.y(), bRect.height() + cellStyle.bottomOuterBorderWidth(), accuBlankBorders);
1128         } else {
1129             // we have cells to the right so draw sharedborders
1130             int r = row;
1131             while (r < row + tableCell.rowSpan() && r <= lastRow) {
1132                 QTextTableCell tableCellRight = d->table->cellAt(r, column + tableCell.columnSpan());
1133                 KoTableCellStyle cellRightStyle = d->effectiveCellStyle(tableCellRight);
1134                 QRectF rightBRect = cellBoundingRect(tableCellRight);
1135                 qreal y = qMax(bRect.y(), rightBRect.y());
1136                 qreal y2 = qMin(bRect.bottom() + cellStyle.bottomOuterBorderWidth(), rightBRect.bottom() + cellRightStyle.bottomOuterBorderWidth());
1137                 cellStyleHelper.drawSharedVerticalBorder(*painter, cellRightStyle, bRect.right(), y, y2-y, accuBlankBorders);
1138                 r = tableCellRight.row() + tableCellRight.rowSpan();
1139             }
1140         }
1141 
1142         // Paint diagonal borders for current cell
1143         cellStyleHelper.paintDiagonalBorders(*painter, bRect);
1144     } else { // separating border model
1145         cellStyleHelper.paintBorders(*painter, bRect, accuBlankBorders);
1146     }
1147 }
1148 
1149 QRectF KoTextLayoutTableArea::cellBoundingRect(const QTextTableCell &cell) const
1150 {
1151     int row = cell.row();
1152     int rowSpan = cell.rowSpan();
1153     const int column = cell.column();
1154     const int columnSpan = cell.columnSpan();
1155     const qreal width = d->columnPositions[column + columnSpan] - d->columnPositions[column];
1156 
1157     if (row >= d->headerRows) {
1158         int lastRow = d->endOfArea->row;
1159         if (d->lastRowHasSomething == false) {
1160             --lastRow;
1161         }
1162         if (lastRow <  d->startOfArea->row) {
1163             return QRectF(); // empty
1164         }
1165 
1166         // Limit cell to within the area
1167         if (row < d->startOfArea->row) {
1168             rowSpan -= d->startOfArea->row - row;
1169             row += d->startOfArea->row - row;
1170         }
1171         if (row + rowSpan - 1 > lastRow) {
1172             rowSpan = lastRow - row + 1;
1173         }
1174         const qreal height = d->rowPositions[row + rowSpan] - d->rowPositions[row];
1175         return QRectF(d->columnPositions[column], d->rowPositions[row], width, height);
1176     } else {
1177         return QRectF(d->columnPositions[column], d->headerRowPositions[row], width, d->headerRowPositions[row + rowSpan] - d->headerRowPositions[row]);
1178     }
1179 }