38 styleElem.setAttribute( QStringLiteral(
"enabled" ),
enabled );
45 enabled = ( styleElem.attribute( QStringLiteral(
"enabled" ), QStringLiteral(
"0" ) ) != QLatin1String(
"0" ) );
71 elem.setAttribute( QStringLiteral(
"cellMargin" ), QString::number(
mCellMargin ) );
72 elem.setAttribute( QStringLiteral(
"emptyTableMode" ), QString::number(
static_cast< int >(
mEmptyTableMode ) ) );
74 elem.setAttribute( QStringLiteral(
"showEmptyRows" ),
mShowEmptyRows );
76 QDomElement headerElem = doc.createElement( QStringLiteral(
"headerTextFormat" ) );
78 headerElem.appendChild( headerTextElem );
79 elem.appendChild( headerElem );
80 elem.setAttribute( QStringLiteral(
"headerHAlignment" ), QString::number(
static_cast< int >(
mHeaderHAlignment ) ) );
81 elem.setAttribute( QStringLiteral(
"headerMode" ), QString::number(
static_cast< int >(
mHeaderMode ) ) );
83 QDomElement contentElem = doc.createElement( QStringLiteral(
"contentTextFormat" ) );
85 contentElem.appendChild( contentTextElem );
86 elem.appendChild( contentElem );
87 elem.setAttribute( QStringLiteral(
"gridStrokeWidth" ), QString::number(
mGridStrokeWidth ) );
89 elem.setAttribute( QStringLiteral(
"horizontalGrid" ),
mHorizontalGrid );
90 elem.setAttribute( QStringLiteral(
"verticalGrid" ),
mVerticalGrid );
91 elem.setAttribute( QStringLiteral(
"showGrid" ),
mShowGrid );
93 elem.setAttribute( QStringLiteral(
"wrapBehavior" ), QString::number(
static_cast< int >(
mWrapBehavior ) ) );
96 QDomElement displayColumnsElem = doc.createElement( QStringLiteral(
"displayColumns" ) );
99 QDomElement columnElem = doc.createElement( QStringLiteral(
"column" ) );
100 column.writeXml( columnElem, doc );
101 displayColumnsElem.appendChild( columnElem );
103 elem.appendChild( displayColumnsElem );
105 QDomElement sortColumnsElem = doc.createElement( QStringLiteral(
"sortColumns" ) );
108 QDomElement columnElem = doc.createElement( QStringLiteral(
"column" ) );
109 column.writeXml( columnElem, doc );
110 sortColumnsElem.appendChild( columnElem );
112 elem.appendChild( sortColumnsElem );
116 QDomElement stylesElem = doc.createElement( QStringLiteral(
"cellStyles" ) );
117 QMap< CellStyleGroup, QString >::const_iterator it = mCellStyleNames.constBegin();
118 for ( ; it != mCellStyleNames.constEnd(); ++it )
120 QString styleName = it.value();
121 QDomElement styleElem = doc.createElement( styleName );
126 stylesElem.appendChild( styleElem );
129 elem.appendChild( stylesElem );
136 mEmptyTableMessage = itemElem.attribute( QStringLiteral(
"emptyTableMessage" ), tr(
"No matching records" ) );
137 mShowEmptyRows = itemElem.attribute( QStringLiteral(
"showEmptyRows" ), QStringLiteral(
"0" ) ).toInt();
139 const QDomElement
headerTextFormat = itemElem.firstChildElement( QStringLiteral(
"headerTextFormat" ) );
142 QDomNodeList textFormatNodeList =
headerTextFormat.elementsByTagName( QStringLiteral(
"text-style" ) );
143 QDomElement textFormatElem = textFormatNodeList.at( 0 ).toElement();
151 headerFont.fromString( itemElem.attribute( QStringLiteral(
"headerFont" ), QString() ) );
171 const QDomElement
contentTextFormat = itemElem.firstChildElement( QStringLiteral(
"contentTextFormat" ) );
174 QDomNodeList textFormatNodeList =
contentTextFormat.elementsByTagName( QStringLiteral(
"text-style" ) );
175 QDomElement textFormatElem = textFormatNodeList.at( 0 ).toElement();
183 contentFont.fromString( itemElem.attribute( QStringLiteral(
"contentFont" ), QString() ) );
200 mCellMargin = itemElem.attribute( QStringLiteral(
"cellMargin" ), QStringLiteral(
"1.0" ) ).toDouble();
201 mGridStrokeWidth = itemElem.attribute( QStringLiteral(
"gridStrokeWidth" ), QStringLiteral(
"0.5" ) ).toDouble();
202 mHorizontalGrid = itemElem.attribute( QStringLiteral(
"horizontalGrid" ), QStringLiteral(
"1" ) ).toInt();
203 mVerticalGrid = itemElem.attribute( QStringLiteral(
"verticalGrid" ), QStringLiteral(
"1" ) ).toInt();
204 mShowGrid = itemElem.attribute( QStringLiteral(
"showGrid" ), QStringLiteral(
"1" ) ).toInt();
211 QDomNodeList columnsList = itemElem.elementsByTagName( QStringLiteral(
"displayColumns" ) );
212 if ( !columnsList.isEmpty() )
214 QDomElement columnsElem = columnsList.at( 0 ).toElement();
215 QDomNodeList columnEntryList = columnsElem.elementsByTagName( QStringLiteral(
"column" ) );
216 for (
int i = 0; i < columnEntryList.size(); ++i )
218 QDomElement columnElem = columnEntryList.at( i ).toElement();
226 QDomNodeList sortColumnsList = itemElem.elementsByTagName( QStringLiteral(
"sortColumns" ) );
227 if ( !sortColumnsList.isEmpty() )
229 QDomElement columnsElem = sortColumnsList.at( 0 ).toElement();
230 QDomNodeList columnEntryList = columnsElem.elementsByTagName( QStringLiteral(
"column" ) );
231 for (
int i = 0; i < columnEntryList.size(); ++i )
233 QDomElement columnElem = columnEntryList.at( i ).toElement();
250 QDomNodeList stylesList = itemElem.elementsByTagName( QStringLiteral(
"cellStyles" ) );
251 if ( !stylesList.isEmpty() )
253 QDomElement stylesElem = stylesList.at( 0 ).toElement();
255 QMap< CellStyleGroup, QString >::const_iterator it = mCellStyleNames.constBegin();
256 for ( ; it != mCellStyleNames.constEnd(); ++it )
258 QString styleName = it.value();
259 QDomNodeList styleList = stylesElem.elementsByTagName( styleName );
260 if ( !styleList.isEmpty() )
262 QDomElement styleElem = styleList.at( 0 ).toElement();
288 double headerHeight = 0;
291 for (
int col = 0; col <
mColumns.count(); ++ col )
305 double contentHeight = frameHeight - headerHeight;
309 int currentRow = firstRow;
310 while ( contentHeight > 0 && currentRow <=
mTableContents.count() )
313 contentHeight -= currentRowHeight;
317 if ( includeEmptyRows && contentHeight > 0 )
321 currentRow += std::max( std::floor( contentHeight / rowHeight ), 0.0 );
324 return currentRow - firstRow - 1;
336 bool includeHeader =
false;
340 includeHeader =
true;
342 return rowsVisible( context, frameExtent.height(), firstRow, includeHeader, includeEmptyRows );
351 return qMakePair( 0, 0 );
356 int rowsAlreadyShown = 0;
359 rowsAlreadyShown +=
rowsVisible( context, idx, rowsAlreadyShown,
false );
363 int firstVisible = std::min( rowsAlreadyShown,
mTableContents.length() );
365 int lastVisible = std::min( firstVisible + possibleRowsVisible,
mTableContents.length() );
367 return qMakePair( firstVisible, lastVisible );
379 if ( !
mLayout->renderContext().isPreviewRender() )
404 int numberRowsToDraw = rowsToShow.second - rowsToShow.first;
405 int numberEmptyRows = 0;
409 numberEmptyRows = numberRowsToDraw - rowsToShow.second + rowsToShow.first;
411 bool mergeCells =
false;
426 p->setPen( Qt::SolidLine );
428 double currentX = gridSizeX;
429 double currentY = gridSizeY;
439 p->setPen( Qt::NoPen );
470 QStringList str = column.heading().split(
'\n' );
473 str = wrappedText( context.
renderContext(), column.heading(), column.width(), headerFormat );
488 currentX += gridSizeX;
492 currentY += cellHeaderHeight;
493 currentY += gridSizeY;
501 for (
int row = rowsToShow.first; row < rowsToShow.second; ++row )
504 currentX = gridSizeX;
515 p->setPen( Qt::NoPen );
517 p->drawRect( fullCell );
524 QStringList str = cellContents.toString().split(
'\n' );
534 str = wrappedText( context.
renderContext(), cellContents.toString(), column.width(), cellFormat );
538 p->setClipRect( fullCell );
542 QColor foreColor = cellFormat.
color();
562 currentX += gridSizeX;
565 currentY += rowHeight;
566 currentY += gridSizeY;
570 if ( numberRowsToDraw > rowsDrawn )
573 p->setPen( Qt::NoPen );
576 for (
int row = rowsDrawn; row < numberRowsToDraw; ++row )
578 currentX = gridSizeX;
584 p->drawRect( QRectF( gridSizeX, currentY,
mTableSize.width() - 2 * gridSizeX, cellBodyHeightForEmptyRows ) );
600 currentX += gridSizeX;
604 currentY += cellBodyHeightForEmptyRows + gridSizeY;
615 gridPen.setJoinStyle( Qt::MiterJoin );
616 p->setPen( gridPen );
631 double messageY = gridSizeY + ( drawHeader ? cellHeaderHeight + gridSizeY : 0 );
632 cell = QRectF( messageX, messageY,
mTableSize.width() - messageX, cellBodyHeightForEmptyRows );
708 if ( font.pointSizeF() > 0 )
713 else if ( font.pixelSize() > 0 )
792 if ( font.pointSizeF() > 0 )
797 else if ( font.pixelSize() > 0 )
952 if ( !newSortColumns.isEmpty() )
983 QMap<int, QString> headers;
988 headers.insert( i, col.heading() );
996 std::unique_ptr< QgsExpressionContextScope > cellScope = qgis::make_unique< QgsExpressionContextScope >();
997 cellScope->setVariable( QStringLiteral(
"row_number" ), row + 1,
true );
998 cellScope->setVariable( QStringLiteral(
"column_number" ), column + 1,
true );
999 return cellScope.release();
1023 for (
int col = 0; col <
mColumns.size(); ++ col )
1028 return QSizeF( 0, height );
1050 void QgsLayoutTable::initStyles()
1062 mCellStyleNames.insert(
OddColumns, QStringLiteral(
"oddColumns" ) );
1063 mCellStyleNames.insert(
EvenColumns, QStringLiteral(
"evenColumns" ) );
1064 mCellStyleNames.insert(
OddRows, QStringLiteral(
"oddRows" ) );
1065 mCellStyleNames.insert(
EvenRows, QStringLiteral(
"evenRows" ) );
1066 mCellStyleNames.insert(
FirstColumn, QStringLiteral(
"firstColumn" ) );
1067 mCellStyleNames.insert(
LastColumn, QStringLiteral(
"lastColumn" ) );
1068 mCellStyleNames.insert(
HeaderRow, QStringLiteral(
"headerRow" ) );
1069 mCellStyleNames.insert(
FirstRow, QStringLiteral(
"firstRow" ) );
1070 mCellStyleNames.insert(
LastRow, QStringLiteral(
"lastRow" ) );
1080 QVector< double > widths( cells );
1082 double currentCellTextWidth;
1091 if ( col.width() > 0 )
1094 widths[i] = col.width();
1099 const QStringList multiLineSplit = col.heading().split(
'\n' );
1101 widths[i] = currentCellTextWidth;
1111 QgsLayoutTableContents::const_iterator rowIt =
mTableContents.constBegin();
1115 QgsLayoutTableRow::const_iterator colIt = rowIt->constBegin();
1117 for ( ; colIt != rowIt->constEnd(); ++colIt )
1119 if (
mColumns.at( col ).width() <= 0 )
1122 const QStringList multiLineSplit = ( *colIt ).toString().split(
'\n' );
1129 widths[ row * cols + col ] = currentCellTextWidth;
1133 widths[ row * cols + col ] = 0;
1142 for (
int col = 0; col < cols; ++col )
1144 double maxColWidth = 0;
1147 maxColWidth = std::max( widths[ row * cols + col ], maxColWidth );
1162 QVector< double > heights( cells );
1178 else if ( textRequiresWrapping( context, col.heading(),
mColumns.at( i ).width(), cellFormat ) )
1194 QgsLayoutTableContents::const_iterator rowIt =
mTableContents.constBegin();
1198 QgsLayoutTableRow::const_iterator colIt = rowIt->constBegin();
1200 for ( ; colIt != rowIt->constEnd(); ++colIt )
1207 if ( textRequiresWrapping( context, ( *colIt ).toString(),
mColumns.at( i ).width(), cellFormat ) )
1225 double maxRowHeight = 0;
1226 for (
int col = 0; col < cols; ++col )
1228 maxRowHeight = std::max( heights[ row * cols + col ], maxRowHeight );
1272 int rowsAlreadyShown = 0;
1274 int rowsVisibleInLastFrame = 0;
1275 double heightOfLastFrame = 0;
1276 for (
int idx = 0; idx < numberExistingFrames; ++idx )
1280 heightOfLastFrame =
frame( idx )->rect().height();
1281 rowsVisibleInLastFrame =
rowsVisible( context, heightOfLastFrame, rowsAlreadyShown, hasHeader,
false );
1282 rowsAlreadyShown += rowsVisibleInLastFrame;
1283 height += heightOfLastFrame;
1294 if ( remainingRows <= 0 )
1310 int numberFramesMissing = 0;
1311 while ( remainingRows > 0 )
1313 numberFramesMissing++;
1315 rowsVisibleInLastFrame =
rowsVisible( context, heightOfLastFrame, rowsAlreadyShown, hasHeader,
false );
1316 if ( rowsVisibleInLastFrame < 1 )
1323 rowsAlreadyShown += rowsVisibleInLastFrame;
1329 height += heightOfLastFrame * numberFramesMissing;
1336 if ( lastRow - firstRow < 1 && !drawHeaderLines )
1345 double currentY = 0;
1346 currentY = halfGridStrokeWidth;
1347 if ( drawHeaderLines )
1349 painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF(
mTableSize.width() - halfGridStrokeWidth, currentY ) );
1353 for (
int row = firstRow; row < lastRow; ++row )
1355 painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF(
mTableSize.width() - halfGridStrokeWidth, currentY ) );
1360 painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF(
mTableSize.width() - halfGridStrokeWidth, currentY ) );
1363 bool QgsLayoutTable::textRequiresWrapping(
QgsRenderContext &context,
const QString &text,
double columnWidth,
const QgsTextFormat &format )
const
1368 const QStringList multiLineSplit = text.split(
'\n' );
1370 return currentTextWidth > columnWidth;
1373 QStringList QgsLayoutTable::wrappedText(
QgsRenderContext &context,
const QString &value,
double columnWidth,
const QgsTextFormat &format )
const
1375 QStringList lines = value.split(
'\n' );
1376 QStringList outLines;
1377 const auto constLines = lines;
1378 for (
const QString &line : constLines )
1380 if ( textRequiresWrapping( context, line, columnWidth, format ) )
1383 QStringList words = line.split(
' ' );
1384 QStringList linesToProcess;
1385 QString wordsInCurrentLine;
1386 const auto constWords = words;
1387 for (
const QString &word : constWords )
1389 if ( textRequiresWrapping( context, word, columnWidth, format ) )
1392 if ( !wordsInCurrentLine.isEmpty() )
1393 linesToProcess << wordsInCurrentLine;
1394 wordsInCurrentLine.clear();
1395 linesToProcess << word;
1399 if ( !wordsInCurrentLine.isEmpty() )
1400 wordsInCurrentLine.append(
' ' );
1401 wordsInCurrentLine.append( word );
1404 if ( !wordsInCurrentLine.isEmpty() )
1405 linesToProcess << wordsInCurrentLine;
1407 const auto constLinesToProcess = linesToProcess;
1408 for (
const QString &line : constLinesToProcess )
1410 QString remainingText = line;
1411 int lastPos = remainingText.lastIndexOf(
' ' );
1412 while ( lastPos > -1 )
1415 if ( !textRequiresWrapping( context, remainingText, columnWidth, format ) )
1420 if ( !textRequiresWrapping( context, remainingText.left( lastPos ), columnWidth, format ) )
1422 outLines << remainingText.left( lastPos );
1423 remainingText = remainingText.mid( lastPos + 1 );
1426 lastPos = remainingText.lastIndexOf(
' ', lastPos - 1 );
1428 outLines << remainingText;
1444 if ( style->enabled && column % 2 == 0 )
1445 color = style->cellBackgroundColor;
1447 if ( style->enabled && column % 2 == 1 )
1448 color = style->cellBackgroundColor;
1450 if ( style->enabled && row % 2 == 0 )
1451 color = style->cellBackgroundColor;
1453 if ( style->enabled && row % 2 == 1 )
1454 color = style->cellBackgroundColor;
1456 if ( style->enabled && column == 0 )
1457 color = style->cellBackgroundColor;
1459 if ( style->enabled && column ==
mColumns.count() - 1 )
1460 color = style->cellBackgroundColor;
1462 if ( style->enabled && row == -1 )
1463 color = style->cellBackgroundColor;
1465 if ( style->enabled && row == 0 )
1466 color = style->cellBackgroundColor;
1469 color = style->cellBackgroundColor;
1484 if ( lastRow - firstRow < 1 && !hasHeader )
1492 double tableHeight = 0;
1498 double headerHeight = tableHeight;
1501 for (
int row = firstRow; row < lastRow; ++row )
1508 double currentX = halfGridStrokeWidth;
1509 painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, tableHeight - halfGridStrokeWidth ) );
1511 QMap<int, double>::const_iterator maxColWidthIt = maxWidthMap.constBegin();
1513 for ( ; maxColWidthIt != maxWidthMap.constEnd(); ++maxColWidthIt )
1515 currentX += ( maxColWidthIt.value() + 2 *
mCellMargin );
1516 if ( col == maxWidthMap.size() || !mergeCells )
1518 painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, tableHeight - halfGridStrokeWidth ) );
1520 else if ( hasHeader )
1522 painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, headerHeight - halfGridStrokeWidth ) );
1541 return (
contents.indexOf( row ) >= 0 );
1556 return mColumns.value( column ).hAlignment();
1561 return mColumns.value( column ).vAlignment();