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 }