QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgslayouttable.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayouttable.cpp
3  ------------------
4  begin : November 2017
5  copyright : (C) 2017 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
19 #include "qgslayouttable.h"
20 #include "qgslayout.h"
21 #include "qgslayoututils.h"
22 #include "qgslayouttablecolumn.h"
23 #include "qgssymbollayerutils.h"
24 #include "qgslayoutframe.h"
25 #include "qgsfontutils.h"
26 #include "qgssettings.h"
28 #include "qgstextrenderer.h"
29 
30 //
31 // QgsLayoutTableStyle
32 //
33 
34 bool QgsLayoutTableStyle::writeXml( QDomElement &styleElem, QDomDocument &doc ) const
35 {
36  Q_UNUSED( doc )
37  styleElem.setAttribute( QStringLiteral( "cellBackgroundColor" ), QgsSymbolLayerUtils::encodeColor( cellBackgroundColor ) );
38  styleElem.setAttribute( QStringLiteral( "enabled" ), enabled );
39  return true;
40 }
41 
42 bool QgsLayoutTableStyle::readXml( const QDomElement &styleElem )
43 {
44  cellBackgroundColor = QgsSymbolLayerUtils::decodeColor( styleElem.attribute( QStringLiteral( "cellBackgroundColor" ), QStringLiteral( "255,255,255,255" ) ) );
45  enabled = ( styleElem.attribute( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
46  return true;
47 }
48 
49 
50 //
51 // QgsLayoutTable
52 //
53 
55  : QgsLayoutMultiFrame( layout )
56 {
57  initStyles();
58 }
59 
61 {
62  mColumns.clear();
63  mSortColumns.clear();
64 
65  qDeleteAll( mCellStyles );
66  mCellStyles.clear();
67 }
68 
69 bool QgsLayoutTable::writePropertiesToElement( QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context ) const
70 {
71  elem.setAttribute( QStringLiteral( "cellMargin" ), QString::number( mCellMargin ) );
72  elem.setAttribute( QStringLiteral( "emptyTableMode" ), QString::number( static_cast< int >( mEmptyTableMode ) ) );
73  elem.setAttribute( QStringLiteral( "emptyTableMessage" ), mEmptyTableMessage );
74  elem.setAttribute( QStringLiteral( "showEmptyRows" ), mShowEmptyRows );
75 
76  QDomElement headerElem = doc.createElement( QStringLiteral( "headerTextFormat" ) );
77  const QDomElement headerTextElem = mHeaderTextFormat.writeXml( doc, context );
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 ) ) );
82 
83  QDomElement contentElem = doc.createElement( QStringLiteral( "contentTextFormat" ) );
84  const QDomElement contentTextElem = mContentTextFormat.writeXml( doc, context );
85  contentElem.appendChild( contentTextElem );
86  elem.appendChild( contentElem );
87  elem.setAttribute( QStringLiteral( "gridStrokeWidth" ), QString::number( mGridStrokeWidth ) );
88  elem.setAttribute( QStringLiteral( "gridColor" ), QgsSymbolLayerUtils::encodeColor( mGridColor ) );
89  elem.setAttribute( QStringLiteral( "horizontalGrid" ), mHorizontalGrid );
90  elem.setAttribute( QStringLiteral( "verticalGrid" ), mVerticalGrid );
91  elem.setAttribute( QStringLiteral( "showGrid" ), mShowGrid );
92  elem.setAttribute( QStringLiteral( "backgroundColor" ), QgsSymbolLayerUtils::encodeColor( mBackgroundColor ) );
93  elem.setAttribute( QStringLiteral( "wrapBehavior" ), QString::number( static_cast< int >( mWrapBehavior ) ) );
94 
95  // display columns
96  QDomElement displayColumnsElem = doc.createElement( QStringLiteral( "displayColumns" ) );
97  for ( const QgsLayoutTableColumn &column : qgis::as_const( mColumns ) )
98  {
99  QDomElement columnElem = doc.createElement( QStringLiteral( "column" ) );
100  column.writeXml( columnElem, doc );
101  displayColumnsElem.appendChild( columnElem );
102  }
103  elem.appendChild( displayColumnsElem );
104  // sort columns
105  QDomElement sortColumnsElem = doc.createElement( QStringLiteral( "sortColumns" ) );
106  for ( const QgsLayoutTableColumn &column : qgis::as_const( mSortColumns ) )
107  {
108  QDomElement columnElem = doc.createElement( QStringLiteral( "column" ) );
109  column.writeXml( columnElem, doc );
110  sortColumnsElem.appendChild( columnElem );
111  }
112  elem.appendChild( sortColumnsElem );
113 
114 
115  //cell styles
116  QDomElement stylesElem = doc.createElement( QStringLiteral( "cellStyles" ) );
117  QMap< CellStyleGroup, QString >::const_iterator it = mCellStyleNames.constBegin();
118  for ( ; it != mCellStyleNames.constEnd(); ++it )
119  {
120  QString styleName = it.value();
121  QDomElement styleElem = doc.createElement( styleName );
122  QgsLayoutTableStyle *style = mCellStyles.value( it.key() );
123  if ( style )
124  {
125  style->writeXml( styleElem, doc );
126  stylesElem.appendChild( styleElem );
127  }
128  }
129  elem.appendChild( stylesElem );
130  return true;
131 }
132 
133 bool QgsLayoutTable::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &, const QgsReadWriteContext &context )
134 {
135  mEmptyTableMode = QgsLayoutTable::EmptyTableMode( itemElem.attribute( QStringLiteral( "emptyTableMode" ), QStringLiteral( "0" ) ).toInt() );
136  mEmptyTableMessage = itemElem.attribute( QStringLiteral( "emptyTableMessage" ), tr( "No matching records" ) );
137  mShowEmptyRows = itemElem.attribute( QStringLiteral( "showEmptyRows" ), QStringLiteral( "0" ) ).toInt();
138 
139  const QDomElement headerTextFormat = itemElem.firstChildElement( QStringLiteral( "headerTextFormat" ) );
140  if ( !headerTextFormat.isNull() )
141  {
142  QDomNodeList textFormatNodeList = headerTextFormat.elementsByTagName( QStringLiteral( "text-style" ) );
143  QDomElement textFormatElem = textFormatNodeList.at( 0 ).toElement();
144  mHeaderTextFormat.readXml( textFormatElem, context );
145  }
146  else
147  {
148  QFont headerFont;
149  if ( !QgsFontUtils::setFromXmlChildNode( headerFont, itemElem, QStringLiteral( "headerFontProperties" ) ) )
150  {
151  headerFont.fromString( itemElem.attribute( QStringLiteral( "headerFont" ), QString() ) );
152  }
153  QColor headerFontColor = QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "headerFontColor" ), QStringLiteral( "0,0,0,255" ) ) );
155  if ( headerFont.pointSizeF() > 0 )
156  {
157  mHeaderTextFormat.setSize( headerFont.pointSizeF() );
159  }
160  else if ( headerFont.pixelSize() > 0 )
161  {
162  mHeaderTextFormat.setSize( headerFont.pixelSize() );
164  }
166  }
167 
168  mHeaderHAlignment = QgsLayoutTable::HeaderHAlignment( itemElem.attribute( QStringLiteral( "headerHAlignment" ), QStringLiteral( "0" ) ).toInt() );
169  mHeaderMode = QgsLayoutTable::HeaderMode( itemElem.attribute( QStringLiteral( "headerMode" ), QStringLiteral( "0" ) ).toInt() );
170 
171  const QDomElement contentTextFormat = itemElem.firstChildElement( QStringLiteral( "contentTextFormat" ) );
172  if ( !contentTextFormat.isNull() )
173  {
174  QDomNodeList textFormatNodeList = contentTextFormat.elementsByTagName( QStringLiteral( "text-style" ) );
175  QDomElement textFormatElem = textFormatNodeList.at( 0 ).toElement();
176  mContentTextFormat.readXml( textFormatElem, context );
177  }
178  else
179  {
180  QFont contentFont;
181  if ( !QgsFontUtils::setFromXmlChildNode( contentFont, itemElem, QStringLiteral( "contentFontProperties" ) ) )
182  {
183  contentFont.fromString( itemElem.attribute( QStringLiteral( "contentFont" ), QString() ) );
184  }
185  QColor contentFontColor = QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "contentFontColor" ), QStringLiteral( "0,0,0,255" ) ) );
187  if ( contentFont.pointSizeF() > 0 )
188  {
189  mContentTextFormat.setSize( contentFont.pointSizeF() );
191  }
192  else if ( contentFont.pixelSize() > 0 )
193  {
194  mContentTextFormat.setSize( contentFont.pixelSize() );
196  }
198  }
199 
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();
205  mGridColor = QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "gridColor" ), QStringLiteral( "0,0,0,255" ) ) );
206  mBackgroundColor = QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "backgroundColor" ), QStringLiteral( "255,255,255,0" ) ) );
207  mWrapBehavior = QgsLayoutTable::WrapBehavior( itemElem.attribute( QStringLiteral( "wrapBehavior" ), QStringLiteral( "0" ) ).toInt() );
208 
209  //restore display column specifications
210  mColumns.clear();
211  QDomNodeList columnsList = itemElem.elementsByTagName( QStringLiteral( "displayColumns" ) );
212  if ( !columnsList.isEmpty() )
213  {
214  QDomElement columnsElem = columnsList.at( 0 ).toElement();
215  QDomNodeList columnEntryList = columnsElem.elementsByTagName( QStringLiteral( "column" ) );
216  for ( int i = 0; i < columnEntryList.size(); ++i )
217  {
218  QDomElement columnElem = columnEntryList.at( i ).toElement();
219  QgsLayoutTableColumn column;
220  column.readXml( columnElem );
221  mColumns.append( column );
222  }
223  }
224  // sort columns
225  mSortColumns.clear();
226  QDomNodeList sortColumnsList = itemElem.elementsByTagName( QStringLiteral( "sortColumns" ) );
227  if ( !sortColumnsList.isEmpty() )
228  {
229  QDomElement columnsElem = sortColumnsList.at( 0 ).toElement();
230  QDomNodeList columnEntryList = columnsElem.elementsByTagName( QStringLiteral( "column" ) );
231  for ( int i = 0; i < columnEntryList.size(); ++i )
232  {
233  QDomElement columnElem = columnEntryList.at( i ).toElement();
234  QgsLayoutTableColumn column;
235  column.readXml( columnElem );
236  mSortColumns.append( column );
237  }
238  }
239  else
240  {
241  // backward compatibility for QGIS < 3.14
242  // copy the display columns if sortByRank > 0 and then, sort them by rank
244  std::copy_if( mColumns.begin(), mColumns.end(), std::back_inserter( mSortColumns ), []( const QgsLayoutTableColumn & col ) {return col.sortByRank() > 0;} );
245  std::sort( mSortColumns.begin(), mSortColumns.end(), []( const QgsLayoutTableColumn & a, const QgsLayoutTableColumn & b ) {return a.sortByRank() < b.sortByRank();} );
247  }
248 
249  //restore cell styles
250  QDomNodeList stylesList = itemElem.elementsByTagName( QStringLiteral( "cellStyles" ) );
251  if ( !stylesList.isEmpty() )
252  {
253  QDomElement stylesElem = stylesList.at( 0 ).toElement();
254 
255  QMap< CellStyleGroup, QString >::const_iterator it = mCellStyleNames.constBegin();
256  for ( ; it != mCellStyleNames.constEnd(); ++it )
257  {
258  QString styleName = it.value();
259  QDomNodeList styleList = stylesElem.elementsByTagName( styleName );
260  if ( !styleList.isEmpty() )
261  {
262  QDomElement styleElem = styleList.at( 0 ).toElement();
263  QgsLayoutTableStyle *style = mCellStyles.value( it.key() );
264  if ( style )
265  style->readXml( styleElem );
266  }
267  }
268  }
269 
270  emit changed();
271  return true;
272 }
273 
275 {
276  return mTableSize;
277 }
278 
280 {
283 }
284 
285 int QgsLayoutTable::rowsVisible( QgsRenderContext &context, double frameHeight, int firstRow, bool includeHeader, bool includeEmptyRows ) const
286 {
287  //calculate header height
288  double headerHeight = 0;
289  if ( includeHeader )
290  {
291  for ( int col = 0; col < mColumns.count(); ++ col )
292  {
293  const QFontMetricsF headerFontMetrics = QgsTextRenderer::fontMetrics( context, textFormatForHeader( col ), QgsTextRenderer::FONT_WORKAROUND_SCALE );
294  //frame has a header
295  headerHeight = std::max( headerHeight, 2 * ( mShowGrid && mHorizontalGrid ? mGridStrokeWidth : 0 ) + 2 * mCellMargin + headerFontMetrics.ascent() / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters ) / QgsTextRenderer::FONT_WORKAROUND_SCALE );
296  }
297  }
298  else
299  {
300  //frame has no header text, just the stroke
301  headerHeight = ( mShowGrid && mHorizontalGrid ? mGridStrokeWidth : 0 );
302  }
303 
304  //remaining height available for content rows
305  double contentHeight = frameHeight - headerHeight;
306 
307  double gridHeight = ( mShowGrid && mHorizontalGrid ? mGridStrokeWidth : 0 );
308 
309  int currentRow = firstRow;
310  while ( contentHeight > 0 && currentRow <= mTableContents.count() )
311  {
312  double currentRowHeight = mMaxRowHeightMap.value( currentRow + 1 ) + gridHeight + 2 * mCellMargin;
313  contentHeight -= currentRowHeight;
314  currentRow++;
315  }
316 
317  if ( includeEmptyRows && contentHeight > 0 )
318  {
319  const QFontMetricsF emptyRowContentFontMetrics = QgsTextRenderer::fontMetrics( context, mContentTextFormat, QgsTextRenderer::FONT_WORKAROUND_SCALE );
320  double rowHeight = ( mShowGrid && mHorizontalGrid ? mGridStrokeWidth : 0 ) + 2 * mCellMargin + emptyRowContentFontMetrics.ascent() / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters ) / QgsTextRenderer::FONT_WORKAROUND_SCALE;
321  currentRow += std::max( std::floor( contentHeight / rowHeight ), 0.0 );
322  }
323 
324  return currentRow - firstRow - 1;
325 }
326 
327 int QgsLayoutTable::rowsVisible( QgsRenderContext &context, int frameIndex, int firstRow, bool includeEmptyRows ) const
328 {
329  //get frame extent
330  if ( frameIndex >= frameCount() )
331  {
332  return 0;
333  }
334  QRectF frameExtent = frame( frameIndex )->extent();
335 
336  bool includeHeader = false;
339  {
340  includeHeader = true;
341  }
342  return rowsVisible( context, frameExtent.height(), firstRow, includeHeader, includeEmptyRows );
343 }
344 
345 QPair<int, int> QgsLayoutTable::rowRange( QgsRenderContext &context, const int frameIndex ) const
346 {
347  //calculate row height
348  if ( frameIndex >= frameCount() )
349  {
350  //bad frame index
351  return qMakePair( 0, 0 );
352  }
353 
354  //loop through all previous frames to calculate how many rows are visible in each
355  //as the entire height of a frame may not be utilized for content rows
356  int rowsAlreadyShown = 0;
357  for ( int idx = 0; idx < frameIndex; ++idx )
358  {
359  rowsAlreadyShown += rowsVisible( context, idx, rowsAlreadyShown, false );
360  }
361 
362  //using zero based indexes
363  int firstVisible = std::min( rowsAlreadyShown, mTableContents.length() );
364  int possibleRowsVisible = rowsVisible( context, frameIndex, rowsAlreadyShown, false );
365  int lastVisible = std::min( firstVisible + possibleRowsVisible, mTableContents.length() );
366 
367  return qMakePair( firstVisible, lastVisible );
368 }
369 
370 void QgsLayoutTable::render( QgsLayoutItemRenderContext &context, const QRectF &, const int frameIndex )
371 {
372  bool emptyTable = mTableContents.length() == 0;
373  if ( emptyTable && mEmptyTableMode == QgsLayoutTable::HideTable )
374  {
375  //empty table set to hide table mode, so don't draw anything
376  return;
377  }
378 
379  if ( !mLayout->renderContext().isPreviewRender() )
380  {
381  //exporting composition, so force an attribute refresh
382  //we do this in case vector layer has changed via an external source (e.g., another database user)
384  }
385 
386  const bool prevTextFormatScaleFlag = context.renderContext().testFlag( QgsRenderContext::ApplyScalingWorkaroundForTextRendering );
388 
389  //calculate which rows to show in this frame
390  QPair< int, int > rowsToShow = rowRange( context.renderContext(), frameIndex );
391 
392  double gridSizeX = mShowGrid && mVerticalGrid ? mGridStrokeWidth : 0;
393  double gridSizeY = mShowGrid && mHorizontalGrid ? mGridStrokeWidth : 0;
394  double cellHeaderHeight = mMaxRowHeightMap[0] + 2 * mCellMargin;
396  QRectF cell;
397 
398  //calculate whether a header is required
399  bool drawHeader = ( ( mHeaderMode == QgsLayoutTable::FirstFrame && frameIndex < 1 )
401  //calculate whether drawing table contents is required
402  bool drawContents = !( emptyTable && mEmptyTableMode == QgsLayoutTable::ShowMessage );
403 
404  int numberRowsToDraw = rowsToShow.second - rowsToShow.first;
405  int numberEmptyRows = 0;
406  if ( drawContents && mShowEmptyRows )
407  {
408  numberRowsToDraw = rowsVisible( context.renderContext(), frameIndex, rowsToShow.first, true );
409  numberEmptyRows = numberRowsToDraw - rowsToShow.second + rowsToShow.first;
410  }
411  bool mergeCells = false;
412  if ( emptyTable && mEmptyTableMode == QgsLayoutTable::ShowMessage )
413  {
414  //draw a merged row for the empty table message
415  numberRowsToDraw++;
416  rowsToShow.second++;
417  mergeCells = true;
418  }
419 
420  QPainter *p = context.renderContext().painter();
421  QgsScopedQPainterState painterState( p );
422  // painter is scaled to dots, so scale back to layout units
423  p->scale( context.renderContext().scaleFactor(), context.renderContext().scaleFactor() );
424 
425  //draw the text
426  p->setPen( Qt::SolidLine );
427 
428  double currentX = gridSizeX;
429  double currentY = gridSizeY;
430  if ( drawHeader )
431  {
432  //draw the headers
433  int col = 0;
434  for ( const QgsLayoutTableColumn &column : qgis::as_const( mColumns ) )
435  {
436  const QgsTextFormat headerFormat = textFormatForHeader( col );
437  //draw background
438  p->save();
439  p->setPen( Qt::NoPen );
440  p->setBrush( backgroundColor( -1, col ) );
441  p->drawRect( QRectF( currentX, currentY, mMaxColumnWidthMap[col] + 2 * mCellMargin, cellHeaderHeight ) );
442  p->restore();
443 
444  currentX += mCellMargin;
445 
446  cell = QRectF( currentX, currentY, mMaxColumnWidthMap[col], cellHeaderHeight );
447 
448  //calculate alignment of header
450  switch ( mHeaderHAlignment )
451  {
452  case FollowColumn:
453  headerAlign = QgsTextRenderer::convertQtHAlignment( column.hAlignment() );
454  break;
455  case HeaderLeft:
456  headerAlign = QgsTextRenderer::AlignLeft;
457  break;
458  case HeaderCenter:
459  headerAlign = QgsTextRenderer::AlignCenter;
460  break;
461  case HeaderRight:
462  headerAlign = QgsTextRenderer::AlignRight;
463  break;
464  }
465 
466  const QRectF textCell = QRectF( currentX, currentY + mCellMargin, mMaxColumnWidthMap[col], cellHeaderHeight - 2 * mCellMargin );
467 
468  // disable text clipping to target text rectangle, because we manually clip to the full cell bounds below
469  // and it's ok if text overlaps into the margin (e.g. extenders or italicized text)
470  QStringList str = column.heading().split( '\n' );
471  if ( ( mWrapBehavior != TruncateText || column.width() > 0 ) && textRequiresWrapping( context.renderContext(), column.heading(), column.width(), headerFormat ) )
472  {
473  str = wrappedText( context.renderContext(), column.heading(), column.width(), headerFormat );
474  }
475 
476  // scale to dots
477  {
479  QgsTextRenderer::drawText( QRectF( textCell.left() * context.renderContext().scaleFactor(),
480  textCell.top() * context.renderContext().scaleFactor(),
481  textCell.width() * context.renderContext().scaleFactor(),
482  textCell.height() * context.renderContext().scaleFactor() ), 0,
483  headerAlign, str, context.renderContext(), headerFormat, true, QgsTextRenderer::AlignVCenter );
484  }
485 
486  currentX += mMaxColumnWidthMap[ col ];
487  currentX += mCellMargin;
488  currentX += gridSizeX;
489  col++;
490  }
491 
492  currentY += cellHeaderHeight;
493  currentY += gridSizeY;
494  }
495 
496  //now draw the body cells
497  int rowsDrawn = 0;
498  if ( drawContents )
499  {
500  //draw the attribute values
501  for ( int row = rowsToShow.first; row < rowsToShow.second; ++row )
502  {
503  rowsDrawn++;
504  currentX = gridSizeX;
505  int col = 0;
506 
507  //calculate row height
508  double rowHeight = mMaxRowHeightMap[row + 1] + 2 * mCellMargin;
509 
510  for ( const QgsLayoutTableColumn &column : qgis::as_const( mColumns ) )
511  {
512  const QRectF fullCell( currentX, currentY, mMaxColumnWidthMap[col] + 2 * mCellMargin, rowHeight );
513  //draw background
514  p->save();
515  p->setPen( Qt::NoPen );
516  p->setBrush( backgroundColor( row, col ) );
517  p->drawRect( fullCell );
518  p->restore();
519 
520  // currentY = gridSize;
521  currentX += mCellMargin;
522 
523  QVariant cellContents = mTableContents.at( row ).at( col );
524  QStringList str = cellContents.toString().split( '\n' );
525 
526  QgsTextFormat cellFormat = textFormatForCell( row, col );
528  cellFormat.updateDataDefinedProperties( context.renderContext() );
529 
530  // disable text clipping to target text rectangle, because we manually clip to the full cell bounds below
531  // and it's ok if text overlaps into the margin (e.g. extenders or italicized text)
532  if ( ( mWrapBehavior != TruncateText || column.width() > 0 ) && textRequiresWrapping( context.renderContext(), cellContents.toString(), column.width(), cellFormat ) )
533  {
534  str = wrappedText( context.renderContext(), cellContents.toString(), column.width(), cellFormat );
535  }
536 
537  p->save();
538  p->setClipRect( fullCell );
539  const QRectF textCell = QRectF( currentX, currentY + mCellMargin, mMaxColumnWidthMap[col], rowHeight - 2 * mCellMargin );
540 
541  const QgsConditionalStyle style = conditionalCellStyle( row, col );
542  QColor foreColor = cellFormat.color();
543  if ( style.textColor().isValid() )
544  foreColor = style.textColor();
545 
546  cellFormat.setColor( foreColor );
547 
548  // scale to dots
549  {
551  QgsTextRenderer::drawText( QRectF( textCell.left() * context.renderContext().scaleFactor(),
552  textCell.top() * context.renderContext().scaleFactor(),
553  textCell.width() * context.renderContext().scaleFactor(),
554  textCell.height() * context.renderContext().scaleFactor() ), 0,
555  QgsTextRenderer::convertQtHAlignment( horizontalAlignmentForCell( row, col ) ), str, context.renderContext(), cellFormat, true,
557  }
558  p->restore();
559 
560  currentX += mMaxColumnWidthMap[ col ];
561  currentX += mCellMargin;
562  currentX += gridSizeX;
563  col++;
564  }
565  currentY += rowHeight;
566  currentY += gridSizeY;
567  }
568  }
569 
570  if ( numberRowsToDraw > rowsDrawn )
571  {
572  p->save();
573  p->setPen( Qt::NoPen );
574 
575  //draw background of empty rows
576  for ( int row = rowsDrawn; row < numberRowsToDraw; ++row )
577  {
578  currentX = gridSizeX;
579  int col = 0;
580 
581  if ( mergeCells )
582  {
583  p->setBrush( backgroundColor( row + 10000, 0 ) );
584  p->drawRect( QRectF( gridSizeX, currentY, mTableSize.width() - 2 * gridSizeX, cellBodyHeightForEmptyRows ) );
585  }
586  else
587  {
588  for ( const QgsLayoutTableColumn &column : qgis::as_const( mColumns ) )
589  {
590  Q_UNUSED( column )
591 
592  //draw background
593 
594  //we use a bit of a hack here - since we don't want these extra blank rows to match the firstrow/lastrow rule, add 10000 to row number
595  p->setBrush( backgroundColor( row + 10000, col ) );
596  p->drawRect( QRectF( currentX, currentY, mMaxColumnWidthMap[col] + 2 * mCellMargin, cellBodyHeightForEmptyRows ) );
597 
598  // currentY = gridSize;
599  currentX += mMaxColumnWidthMap[ col ] + 2 * mCellMargin;
600  currentX += gridSizeX;
601  col++;
602  }
603  }
604  currentY += cellBodyHeightForEmptyRows + gridSizeY;
605  }
606  p->restore();
607  }
608 
609  //and the borders
610  if ( mShowGrid )
611  {
612  QPen gridPen;
613  gridPen.setWidthF( mGridStrokeWidth );
614  gridPen.setColor( mGridColor );
615  gridPen.setJoinStyle( Qt::MiterJoin );
616  p->setPen( gridPen );
617  if ( mHorizontalGrid )
618  {
619  drawHorizontalGridLines( context, rowsToShow.first, rowsToShow.second + numberEmptyRows, drawHeader );
620  }
621  if ( mVerticalGrid )
622  {
623  drawVerticalGridLines( context, mMaxColumnWidthMap, rowsToShow.first, rowsToShow.second + numberEmptyRows, drawHeader, mergeCells );
624  }
625  }
626 
627  //special case - no records and table is set to ShowMessage mode
628  if ( emptyTable && mEmptyTableMode == QgsLayoutTable::ShowMessage )
629  {
630  double messageX = gridSizeX + mCellMargin;
631  double messageY = gridSizeY + ( drawHeader ? cellHeaderHeight + gridSizeY : 0 );
632  cell = QRectF( messageX, messageY, mTableSize.width() - messageX, cellBodyHeightForEmptyRows );
633 
634  // scale to dots
635  {
637  QgsTextRenderer::drawText( QRectF( cell.left() * context.renderContext().scaleFactor(),
638  cell.top() * context.renderContext().scaleFactor(),
639  cell.width() * context.renderContext().scaleFactor(),
640  cell.height() * context.renderContext().scaleFactor() ), 0,
642  }
643  }
644 
646 }
647 
648 void QgsLayoutTable::setCellMargin( const double margin )
649 {
650  if ( qgsDoubleNear( margin, mCellMargin ) )
651  {
652  return;
653  }
654 
655  mCellMargin = margin;
656 
657  //since spacing has changed, we need to recalculate the table size
659 
660  emit changed();
661 }
662 
664 {
665  if ( mode == mEmptyTableMode )
666  {
667  return;
668  }
669 
670  mEmptyTableMode = mode;
671 
672  //since appearance has changed, we need to recalculate the table size
674 
675  emit changed();
676 }
677 
678 void QgsLayoutTable::setEmptyTableMessage( const QString &message )
679 {
680  if ( message == mEmptyTableMessage )
681  {
682  return;
683  }
684 
685  mEmptyTableMessage = message;
686 
687  //since message has changed, we need to recalculate the table size
689 
690  emit changed();
691 }
692 
693 void QgsLayoutTable::setShowEmptyRows( const bool showEmpty )
694 {
695  if ( showEmpty == mShowEmptyRows )
696  {
697  return;
698  }
699 
700  mShowEmptyRows = showEmpty;
701  update();
702  emit changed();
703 }
704 
705 void QgsLayoutTable::setHeaderFont( const QFont &font )
706 {
707  mHeaderTextFormat.setFont( font );
708  if ( font.pointSizeF() > 0 )
709  {
710  mHeaderTextFormat.setSize( font.pointSizeF() );
712  }
713  else if ( font.pixelSize() > 0 )
714  {
715  mHeaderTextFormat.setSize( font.pixelSize() );
717  }
718 
719  //since font attributes have changed, we need to recalculate the table size
721 
722  emit changed();
723 }
724 
726 {
727  return mHeaderTextFormat.toQFont();
728 }
729 
730 void QgsLayoutTable::setHeaderFontColor( const QColor &color )
731 {
732  if ( color == mHeaderTextFormat.color() )
733  {
734  return;
735  }
736 
737  mHeaderTextFormat.setColor( color );
738  update();
739 
740  emit changed();
741 }
742 
744 {
745  return mHeaderTextFormat.color();
746 }
747 
749 {
750  mHeaderTextFormat = format;
751 
752  //since font attributes have changed, we need to recalculate the table size
754 
755  emit changed();
756 }
757 
759 {
760  return mHeaderTextFormat;
761 }
762 
764 {
765  if ( alignment == mHeaderHAlignment )
766  {
767  return;
768  }
769 
770  mHeaderHAlignment = alignment;
771  update();
772 
773  emit changed();
774 }
775 
777 {
778  if ( mode == mHeaderMode )
779  {
780  return;
781  }
782 
783  mHeaderMode = mode;
785 
786  emit changed();
787 }
788 
789 void QgsLayoutTable::setContentFont( const QFont &font )
790 {
791  mContentTextFormat.setFont( font );
792  if ( font.pointSizeF() > 0 )
793  {
794  mContentTextFormat.setSize( font.pointSizeF() );
796  }
797  else if ( font.pixelSize() > 0 )
798  {
799  mContentTextFormat.setSize( font.pixelSize() );
801  }
802 
803  //since font attributes have changed, we need to recalculate the table size
805 
806  emit changed();
807 }
808 
810 {
811  return mContentTextFormat.toQFont();
812 }
813 
814 void QgsLayoutTable::setContentFontColor( const QColor &color )
815 {
816  if ( color == mContentTextFormat.color() )
817  {
818  return;
819  }
820 
821  mContentTextFormat.setColor( color );
822  update();
823 
824  emit changed();
825 }
826 
828 {
829  return mContentTextFormat.color();
830 }
831 
833 {
834  mContentTextFormat = format;
835 
836  //since spacing has changed, we need to recalculate the table size
838 
839  emit changed();
840 }
841 
843 {
844  return mContentTextFormat;
845 }
846 
847 void QgsLayoutTable::setShowGrid( const bool showGrid )
848 {
849  if ( showGrid == mShowGrid )
850  {
851  return;
852  }
853 
855  //since grid spacing has changed, we need to recalculate the table size
857 
858  emit changed();
859 }
860 
861 void QgsLayoutTable::setGridStrokeWidth( const double width )
862 {
863  if ( qgsDoubleNear( width, mGridStrokeWidth ) )
864  {
865  return;
866  }
867 
868  mGridStrokeWidth = width;
869  //since grid spacing has changed, we need to recalculate the table size
871 
872  emit changed();
873 }
874 
875 void QgsLayoutTable::setGridColor( const QColor &color )
876 {
877  if ( color == mGridColor )
878  {
879  return;
880  }
881 
882  mGridColor = color;
883  update();
884 
885  emit changed();
886 }
887 
888 void QgsLayoutTable::setHorizontalGrid( const bool horizontalGrid )
889 {
891  {
892  return;
893  }
894 
896  //since grid spacing has changed, we need to recalculate the table size
898 
899  emit changed();
900 }
901 
902 void QgsLayoutTable::setVerticalGrid( const bool verticalGrid )
903 {
904  if ( verticalGrid == mVerticalGrid )
905  {
906  return;
907  }
908 
910  //since grid spacing has changed, we need to recalculate the table size
912 
913  emit changed();
914 }
915 
916 void QgsLayoutTable::setBackgroundColor( const QColor &color )
917 {
918  if ( color == mBackgroundColor )
919  {
920  return;
921  }
922 
923  mBackgroundColor = color;
924  update();
925 
926  emit changed();
927 }
928 
930 {
931  if ( behavior == mWrapBehavior )
932  {
933  return;
934  }
935 
936  mWrapBehavior = behavior;
938 
939  emit changed();
940 }
941 
943 {
944  //remove existing columns
945  mColumns = columns;
946 
947  // backward compatibility
948  // test if sorting is provided with the columns and call setSortColumns in such case
949  QgsLayoutTableSortColumns newSortColumns;
951  std::copy_if( mColumns.begin(), mColumns.end(), std::back_inserter( newSortColumns ), []( const QgsLayoutTableColumn & col ) {return col.sortByRank() > 0;} );
952  if ( !newSortColumns.isEmpty() )
953  {
954  std::sort( newSortColumns.begin(), newSortColumns.end(), []( const QgsLayoutTableColumn & a, const QgsLayoutTableColumn & b ) {return a.sortByRank() < b.sortByRank();} );
955  setSortColumns( newSortColumns );
956  }
958 }
959 
961 {
963 }
964 
966 {
967  if ( mCellStyles.contains( group ) )
968  delete mCellStyles.take( group );
969 
970  mCellStyles.insert( group, new QgsLayoutTableStyle( style ) );
971 }
972 
974 {
975  if ( !mCellStyles.contains( group ) )
976  return nullptr;
977 
978  return mCellStyles.value( group );
979 }
980 
981 QMap<int, QString> QgsLayoutTable::headerLabels() const
982 {
983  QMap<int, QString> headers;
984 
985  int i = 0;
986  for ( const QgsLayoutTableColumn &col : qgis::as_const( mColumns ) )
987  {
988  headers.insert( i, col.heading() );
989  i++;
990  }
991  return headers;
992 }
993 
995 {
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();
1000 }
1001 
1003 {
1004  return QgsConditionalStyle();
1005 }
1006 
1007 QSizeF QgsLayoutTable::fixedFrameSize( const int frameIndex ) const
1008 {
1009  Q_UNUSED( frameIndex )
1010  return QSizeF( mTableSize.width(), 0 );
1011 }
1012 
1013 QSizeF QgsLayoutTable::minFrameSize( const int frameIndex ) const
1014 {
1017 
1018  double height = 0;
1021  {
1022  //header required, force frame to be high enough for header
1023  for ( int col = 0; col < mColumns.size(); ++ col )
1024  {
1026  }
1027  }
1028  return QSizeF( 0, height );
1029 }
1030 
1032 {
1033  mMaxColumnWidthMap.clear();
1034  mMaxRowHeightMap.clear();
1035  mTableContents.clear();
1036 
1037  //get new contents
1038  if ( !getTableContents( mTableContents ) )
1039  {
1040  return;
1041  }
1042 }
1043 
1045 {
1046  mTableSize = QSizeF( totalWidth(), totalHeight() );
1048 }
1049 
1050 void QgsLayoutTable::initStyles()
1051 {
1052  mCellStyles.insert( OddColumns, new QgsLayoutTableStyle() );
1053  mCellStyles.insert( EvenColumns, new QgsLayoutTableStyle() );
1054  mCellStyles.insert( OddRows, new QgsLayoutTableStyle() );
1055  mCellStyles.insert( EvenRows, new QgsLayoutTableStyle() );
1056  mCellStyles.insert( FirstColumn, new QgsLayoutTableStyle() );
1057  mCellStyles.insert( LastColumn, new QgsLayoutTableStyle() );
1058  mCellStyles.insert( HeaderRow, new QgsLayoutTableStyle() );
1059  mCellStyles.insert( FirstRow, new QgsLayoutTableStyle() );
1060  mCellStyles.insert( LastRow, new QgsLayoutTableStyle() );
1061 
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" ) );
1071 }
1072 
1074 {
1075  mMaxColumnWidthMap.clear();
1076 
1077  //total number of cells (rows + 1 for header)
1078  int cols = mColumns.count();
1079  int cells = cols * ( mTableContents.count() + 1 );
1080  QVector< double > widths( cells );
1081 
1082  double currentCellTextWidth;
1083 
1086 
1087  //first, go through all the column headers and calculate the sizes
1088  int i = 0;
1089  for ( const QgsLayoutTableColumn &col : qgis::as_const( mColumns ) )
1090  {
1091  if ( col.width() > 0 )
1092  {
1093  //column has manually specified width
1094  widths[i] = col.width();
1095  }
1096  else if ( mHeaderMode != QgsLayoutTable::NoHeaders )
1097  {
1098  //column width set to automatic, so check content size
1099  const QStringList multiLineSplit = col.heading().split( '\n' );
1100  currentCellTextWidth = QgsTextRenderer::textWidth( context, textFormatForHeader( i ), multiLineSplit ) / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
1101  widths[i] = currentCellTextWidth;
1102  }
1103  else
1104  {
1105  widths[i] = 0.0;
1106  }
1107  i++;
1108  }
1109 
1110  //next, go through all the table contents and calculate the sizes
1111  QgsLayoutTableContents::const_iterator rowIt = mTableContents.constBegin();
1112  int row = 1;
1113  for ( ; rowIt != mTableContents.constEnd(); ++rowIt )
1114  {
1115  QgsLayoutTableRow::const_iterator colIt = rowIt->constBegin();
1116  int col = 0;
1117  for ( ; colIt != rowIt->constEnd(); ++colIt )
1118  {
1119  if ( mColumns.at( col ).width() <= 0 )
1120  {
1121  //column width set to automatic, so check content size
1122  const QStringList multiLineSplit = ( *colIt ).toString().split( '\n' );
1123 
1124  QgsTextFormat cellFormat = textFormatForCell( row - 1, col );
1125  QgsExpressionContextScopePopper popper( context.expressionContext(), scopeForCell( row - 1, col ) );
1126  cellFormat.updateDataDefinedProperties( context );
1127 
1128  currentCellTextWidth = QgsTextRenderer::textWidth( context, cellFormat, multiLineSplit ) / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
1129  widths[ row * cols + col ] = currentCellTextWidth;
1130  }
1131  else
1132  {
1133  widths[ row * cols + col ] = 0;
1134  }
1135 
1136  col++;
1137  }
1138  row++;
1139  }
1140 
1141  //calculate maximum
1142  for ( int col = 0; col < cols; ++col )
1143  {
1144  double maxColWidth = 0;
1145  for ( int row = 0; row < mTableContents.count() + 1; ++row )
1146  {
1147  maxColWidth = std::max( widths[ row * cols + col ], maxColWidth );
1148  }
1149  mMaxColumnWidthMap.insert( col, maxColWidth );
1150  }
1151 
1152  return true;
1153 }
1154 
1156 {
1157  mMaxRowHeightMap.clear();
1158 
1159  //total number of cells (rows + 1 for header)
1160  int cols = mColumns.count();
1161  int cells = cols * ( mTableContents.count() + 1 );
1162  QVector< double > heights( cells );
1163 
1166 
1167  //first, go through all the column headers and calculate the sizes
1168  int i = 0;
1169  for ( const QgsLayoutTableColumn &col : qgis::as_const( mColumns ) )
1170  {
1171  const QgsTextFormat cellFormat = textFormatForHeader( i );
1173  //height
1175  {
1176  heights[i] = 0;
1177  }
1178  else if ( textRequiresWrapping( context, col.heading(), mColumns.at( i ).width(), cellFormat ) )
1179  {
1180  //contents too wide for cell, need to wrap
1181  heights[i] = QgsTextRenderer::textHeight( context, cellFormat, wrappedText( context, col.heading(), mColumns.at( i ).width(), cellFormat ), QgsTextRenderer::Rect )
1183  - headerDescentMm;
1184  }
1185  else
1186  {
1187  heights[i] = QgsTextRenderer::textHeight( context, cellFormat, QStringList() << col.heading(), QgsTextRenderer::Rect ) / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters )
1188  - headerDescentMm;
1189  }
1190  i++;
1191  }
1192 
1193  //next, go through all the table contents and calculate the sizes
1194  QgsLayoutTableContents::const_iterator rowIt = mTableContents.constBegin();
1195  int row = 1;
1196  for ( ; rowIt != mTableContents.constEnd(); ++rowIt )
1197  {
1198  QgsLayoutTableRow::const_iterator colIt = rowIt->constBegin();
1199  int i = 0;
1200  for ( ; colIt != rowIt->constEnd(); ++colIt )
1201  {
1202  QgsTextFormat cellFormat = textFormatForCell( row - 1, i );
1203  QgsExpressionContextScopePopper popper( context.expressionContext(), scopeForCell( row - 1, i ) );
1204  cellFormat.updateDataDefinedProperties( context );
1206 
1207  if ( textRequiresWrapping( context, ( *colIt ).toString(), mColumns.at( i ).width(), cellFormat ) )
1208  {
1209  //contents too wide for cell, need to wrap
1210  heights[ row * cols + i ] = QgsTextRenderer::textHeight( context, cellFormat, wrappedText( context, ( *colIt ).toString(), mColumns.at( i ).width(), cellFormat ), QgsTextRenderer::Rect ) / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters ) - contentDescentMm;
1211  }
1212  else
1213  {
1214  heights[ row * cols + i ] = QgsTextRenderer::textHeight( context, cellFormat, QStringList() << ( *colIt ).toString().split( '\n' ), QgsTextRenderer::Rect ) / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters ) - contentDescentMm;
1215  }
1216 
1217  i++;
1218  }
1219  row++;
1220  }
1221 
1222  //calculate maximum
1223  for ( int row = 0; row < mTableContents.count() + 1; ++row )
1224  {
1225  double maxRowHeight = 0;
1226  for ( int col = 0; col < cols; ++col )
1227  {
1228  maxRowHeight = std::max( heights[ row * cols + col ], maxRowHeight );
1229  }
1230  mMaxRowHeightMap.insert( row, maxRowHeight );
1231  }
1232 
1233  return true;
1234 }
1235 
1237 {
1238  //check how much space each column needs
1239  if ( !calculateMaxColumnWidths() )
1240  {
1241  return 0;
1242  }
1243 
1244  //adapt frame to total width
1245  double totalWidth = 0;
1246  QMap<int, double>::const_iterator maxColWidthIt = mMaxColumnWidthMap.constBegin();
1247  for ( ; maxColWidthIt != mMaxColumnWidthMap.constEnd(); ++maxColWidthIt )
1248  {
1249  totalWidth += maxColWidthIt.value();
1250  }
1251  totalWidth += ( 2 * mMaxColumnWidthMap.size() * mCellMargin );
1252  totalWidth += ( mMaxColumnWidthMap.size() + 1 ) * ( mShowGrid && mVerticalGrid ? mGridStrokeWidth : 0 );
1253 
1254  return totalWidth;
1255 }
1256 
1258 {
1259  //check how much space each row needs
1260  if ( !calculateMaxRowHeights() )
1261  {
1262  return 0;
1263  }
1264 
1265  double height = 0;
1266 
1269 
1270  //loop through all existing frames to calculate how many rows are visible in each
1271  //as the entire height of a frame may not be utilized for content rows
1272  int rowsAlreadyShown = 0;
1273  int numberExistingFrames = frameCount();
1274  int rowsVisibleInLastFrame = 0;
1275  double heightOfLastFrame = 0;
1276  for ( int idx = 0; idx < numberExistingFrames; ++idx )
1277  {
1278  bool hasHeader = ( ( mHeaderMode == QgsLayoutTable::FirstFrame && idx == 0 )
1280  heightOfLastFrame = frame( idx )->rect().height();
1281  rowsVisibleInLastFrame = rowsVisible( context, heightOfLastFrame, rowsAlreadyShown, hasHeader, false );
1282  rowsAlreadyShown += rowsVisibleInLastFrame;
1283  height += heightOfLastFrame;
1284  if ( rowsAlreadyShown >= mTableContents.length() )
1285  {
1286  //shown entire contents of table, nothing remaining
1287  return height;
1288  }
1289  }
1290 
1291  //calculate how many rows left to show
1292  int remainingRows = mTableContents.length() - rowsAlreadyShown;
1293 
1294  if ( remainingRows <= 0 )
1295  {
1296  //no remaining rows
1297  return height;
1298  }
1299 
1301  {
1302  QgsLayoutItemPage *page = mLayout->pageCollection()->page( mLayout->pageCollection()->pageCount() - 1 );
1303  if ( page )
1304  heightOfLastFrame = page->sizeWithUnits().height();
1305  }
1306 
1307  bool hasHeader = ( ( mHeaderMode == QgsLayoutTable::FirstFrame && numberExistingFrames < 1 )
1309 
1310  int numberFramesMissing = 0;
1311  while ( remainingRows > 0 )
1312  {
1313  numberFramesMissing++;
1314 
1315  rowsVisibleInLastFrame = rowsVisible( context, heightOfLastFrame, rowsAlreadyShown, hasHeader, false );
1316  if ( rowsVisibleInLastFrame < 1 )
1317  {
1318  //if no rows are visible in the last frame, calculation of missing frames
1319  //is impossible. So just return total height of existing frames
1320  return height;
1321  }
1322 
1323  rowsAlreadyShown += rowsVisibleInLastFrame;
1324  remainingRows = mTableContents.length() - rowsAlreadyShown;
1325  }
1326 
1327  //rows remain unshown -- how many extra frames would we need to complete the table?
1328  //assume all added frames are same size as final frame
1329  height += heightOfLastFrame * numberFramesMissing;
1330  return height;
1331 }
1332 
1333 void QgsLayoutTable::drawHorizontalGridLines( QgsLayoutItemRenderContext &context, int firstRow, int lastRow, bool drawHeaderLines ) const
1334 {
1335  //horizontal lines
1336  if ( lastRow - firstRow < 1 && !drawHeaderLines )
1337  {
1338  return;
1339  }
1340 
1341  QPainter *painter = context.renderContext().painter();
1342 
1344  double halfGridStrokeWidth = ( mShowGrid ? mGridStrokeWidth : 0 ) / 2.0;
1345  double currentY = 0;
1346  currentY = halfGridStrokeWidth;
1347  if ( drawHeaderLines )
1348  {
1349  painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) );
1350  currentY += ( mShowGrid ? mGridStrokeWidth : 0 );
1351  currentY += mMaxRowHeightMap[0] + 2 * mCellMargin;
1352  }
1353  for ( int row = firstRow; row < lastRow; ++row )
1354  {
1355  painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) );
1356  currentY += ( mShowGrid ? mGridStrokeWidth : 0 );
1357  double rowHeight = row < mTableContents.count() ? mMaxRowHeightMap[row + 1] : cellBodyHeightForEmptyRows;
1358  currentY += ( rowHeight + 2 * mCellMargin );
1359  }
1360  painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) );
1361 }
1362 
1363 bool QgsLayoutTable::textRequiresWrapping( QgsRenderContext &context, const QString &text, double columnWidth, const QgsTextFormat &format ) const
1364 {
1365  if ( qgsDoubleNear( columnWidth, 0.0 ) || mWrapBehavior != WrapText )
1366  return false;
1367 
1368  const QStringList multiLineSplit = text.split( '\n' );
1369  const double currentTextWidth = QgsTextRenderer::textWidth( context, format, multiLineSplit ) / context.convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
1370  return currentTextWidth > columnWidth;
1371 }
1372 
1373 QStringList QgsLayoutTable::wrappedText( QgsRenderContext &context, const QString &value, double columnWidth, const QgsTextFormat &format ) const
1374 {
1375  QStringList lines = value.split( '\n' );
1376  QStringList outLines;
1377  const auto constLines = lines;
1378  for ( const QString &line : constLines )
1379  {
1380  if ( textRequiresWrapping( context, line, columnWidth, format ) )
1381  {
1382  //first step is to identify words which must be on their own line (too long to fit)
1383  QStringList words = line.split( ' ' );
1384  QStringList linesToProcess;
1385  QString wordsInCurrentLine;
1386  const auto constWords = words;
1387  for ( const QString &word : constWords )
1388  {
1389  if ( textRequiresWrapping( context, word, columnWidth, format ) )
1390  {
1391  //too long to fit
1392  if ( !wordsInCurrentLine.isEmpty() )
1393  linesToProcess << wordsInCurrentLine;
1394  wordsInCurrentLine.clear();
1395  linesToProcess << word;
1396  }
1397  else
1398  {
1399  if ( !wordsInCurrentLine.isEmpty() )
1400  wordsInCurrentLine.append( ' ' );
1401  wordsInCurrentLine.append( word );
1402  }
1403  }
1404  if ( !wordsInCurrentLine.isEmpty() )
1405  linesToProcess << wordsInCurrentLine;
1406 
1407  const auto constLinesToProcess = linesToProcess;
1408  for ( const QString &line : constLinesToProcess )
1409  {
1410  QString remainingText = line;
1411  int lastPos = remainingText.lastIndexOf( ' ' );
1412  while ( lastPos > -1 )
1413  {
1414  //check if remaining text is short enough to go in one line
1415  if ( !textRequiresWrapping( context, remainingText, columnWidth, format ) )
1416  {
1417  break;
1418  }
1419 
1420  if ( !textRequiresWrapping( context, remainingText.left( lastPos ), columnWidth, format ) )
1421  {
1422  outLines << remainingText.left( lastPos );
1423  remainingText = remainingText.mid( lastPos + 1 );
1424  lastPos = 0;
1425  }
1426  lastPos = remainingText.lastIndexOf( ' ', lastPos - 1 );
1427  }
1428  outLines << remainingText;
1429  }
1430  }
1431  else
1432  {
1433  outLines << line;
1434  }
1435  }
1436 
1437  return outLines;
1438 }
1439 
1440 QColor QgsLayoutTable::backgroundColor( int row, int column ) const
1441 {
1442  QColor color = mBackgroundColor;
1443  if ( QgsLayoutTableStyle *style = mCellStyles.value( OddColumns ) )
1444  if ( style->enabled && column % 2 == 0 )
1445  color = style->cellBackgroundColor;
1446  if ( QgsLayoutTableStyle *style = mCellStyles.value( EvenColumns ) )
1447  if ( style->enabled && column % 2 == 1 )
1448  color = style->cellBackgroundColor;
1449  if ( QgsLayoutTableStyle *style = mCellStyles.value( OddRows ) )
1450  if ( style->enabled && row % 2 == 0 )
1451  color = style->cellBackgroundColor;
1452  if ( QgsLayoutTableStyle *style = mCellStyles.value( EvenRows ) )
1453  if ( style->enabled && row % 2 == 1 )
1454  color = style->cellBackgroundColor;
1455  if ( QgsLayoutTableStyle *style = mCellStyles.value( FirstColumn ) )
1456  if ( style->enabled && column == 0 )
1457  color = style->cellBackgroundColor;
1458  if ( QgsLayoutTableStyle *style = mCellStyles.value( LastColumn ) )
1459  if ( style->enabled && column == mColumns.count() - 1 )
1460  color = style->cellBackgroundColor;
1461  if ( QgsLayoutTableStyle *style = mCellStyles.value( HeaderRow ) )
1462  if ( style->enabled && row == -1 )
1463  color = style->cellBackgroundColor;
1464  if ( QgsLayoutTableStyle *style = mCellStyles.value( FirstRow ) )
1465  if ( style->enabled && row == 0 )
1466  color = style->cellBackgroundColor;
1467  if ( QgsLayoutTableStyle *style = mCellStyles.value( LastRow ) )
1468  if ( style->enabled && row == mTableContents.count() - 1 )
1469  color = style->cellBackgroundColor;
1470 
1471  if ( row >= 0 )
1472  {
1473  QgsConditionalStyle conditionalStyle = conditionalCellStyle( row, column );
1474  if ( conditionalStyle.backgroundColor().isValid() )
1475  color = conditionalStyle.backgroundColor();
1476  }
1477 
1478  return color;
1479 }
1480 
1481 void QgsLayoutTable::drawVerticalGridLines( QgsLayoutItemRenderContext &context, const QMap<int, double> &maxWidthMap, int firstRow, int lastRow, bool hasHeader, bool mergeCells ) const
1482 {
1483  //vertical lines
1484  if ( lastRow - firstRow < 1 && !hasHeader )
1485  {
1486  return;
1487  }
1488 
1489  QPainter *painter = context.renderContext().painter();
1490 
1491  //calculate height of table within frame
1492  double tableHeight = 0;
1493  if ( hasHeader )
1494  {
1495  tableHeight += ( mShowGrid && mHorizontalGrid ? mGridStrokeWidth : 0 ) + mCellMargin * 2 + mMaxRowHeightMap[0];
1496  }
1497  tableHeight += ( mShowGrid && mHorizontalGrid ? mGridStrokeWidth : 0 );
1498  double headerHeight = tableHeight;
1499 
1501  for ( int row = firstRow; row < lastRow; ++row )
1502  {
1503  double rowHeight = row < mTableContents.count() ? mMaxRowHeightMap[row + 1] : cellBodyHeightForEmptyRows;
1504  tableHeight += rowHeight + ( mShowGrid && mHorizontalGrid ? mGridStrokeWidth : 0 ) + mCellMargin * 2;
1505  }
1506 
1507  double halfGridStrokeWidth = ( mShowGrid && mVerticalGrid ? mGridStrokeWidth : 0 ) / 2.0;
1508  double currentX = halfGridStrokeWidth;
1509  painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, tableHeight - halfGridStrokeWidth ) );
1510  currentX += ( mShowGrid && mVerticalGrid ? mGridStrokeWidth : 0 );
1511  QMap<int, double>::const_iterator maxColWidthIt = maxWidthMap.constBegin();
1512  int col = 1;
1513  for ( ; maxColWidthIt != maxWidthMap.constEnd(); ++maxColWidthIt )
1514  {
1515  currentX += ( maxColWidthIt.value() + 2 * mCellMargin );
1516  if ( col == maxWidthMap.size() || !mergeCells )
1517  {
1518  painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, tableHeight - halfGridStrokeWidth ) );
1519  }
1520  else if ( hasHeader )
1521  {
1522  painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, headerHeight - halfGridStrokeWidth ) );
1523  }
1524 
1525  currentX += ( mShowGrid && mVerticalGrid ? mGridStrokeWidth : 0 );
1526  col++;
1527  }
1528 }
1529 
1531 {
1533 
1534  //force recalculation of frame rects, so that they are set to the correct
1535  //fixed and minimum frame sizes
1537 }
1538 
1540 {
1541  return ( contents.indexOf( row ) >= 0 );
1542 }
1543 
1545 {
1546  return mContentTextFormat;
1547 }
1548 
1550 {
1551  return mHeaderTextFormat;
1552 }
1553 
1554 Qt::Alignment QgsLayoutTable::horizontalAlignmentForCell( int, int column ) const
1555 {
1556  return mColumns.value( column ).hAlignment();
1557 }
1558 
1559 Qt::Alignment QgsLayoutTable::verticalAlignmentForCell( int, int column ) const
1560 {
1561  return mColumns.value( column ).vAlignment();
1562 }
1563 
QgsLayoutMultiFrame::frame
QgsLayoutFrame * frame(int index) const
Returns the child frame at a specified index from the multiframe.
Definition: qgslayoutmultiframe.cpp:469
QgsLayoutTable::mWrapBehavior
WrapBehavior mWrapBehavior
Definition: qgslayouttable.h:631
QgsLayoutTable::setHeaderMode
void setHeaderMode(HeaderMode mode)
Sets the display mode for headers in the table.
Definition: qgslayouttable.cpp:776
qgsexpressioncontextutils.h
QgsLayoutTableStyle::cellBackgroundColor
QColor cellBackgroundColor
Cell background color.
Definition: qgslayouttable.h:87
QgsRenderContext::testFlag
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
Definition: qgsrendercontext.cpp:192
QgsLayoutTable::showGrid
bool showGrid() const
Returns whether grid lines are drawn in the table.
Definition: qgslayouttable.h:378
QgsExpressionContextScopePopper
RAII class to pop scope from an expression context on destruction.
Definition: qgsexpressioncontextutils.h:355
QgsLayoutTable::backgroundColor
QColor backgroundColor() const
Returns the color used for the background of the table.
Definition: qgslayouttable.h:462
QgsSymbolLayerUtils::encodeColor
static QString encodeColor(const QColor &color)
Definition: qgssymbollayerutils.cpp:52
QgsRenderContext::convertToPainterUnits
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
Definition: qgsrendercontext.cpp:318
QgsLayoutTable::render
void render(QgsLayoutItemRenderContext &context, const QRectF &renderExtent, int frameIndex) override
Renders a portion of the multiframe's content into a render context.
Definition: qgslayouttable.cpp:370
QgsLayoutTableColumns
QVector< QgsLayoutTableColumn > QgsLayoutTableColumns
List of column definitions for a QgsLayoutTable.
Definition: qgslayouttable.h:58
QgsLayoutTable::setHeaderHAlignment
void setHeaderHAlignment(HeaderHAlignment alignment)
Sets the horizontal alignment for table headers.
Definition: qgslayouttable.cpp:763
QgsLayoutTable::headerFontColor
Q_DECL_DEPRECATED QColor headerFontColor() const
Returns the color used to draw header text in the table.
Definition: qgslayouttable.cpp:743
QgsLayoutTable::HeaderHAlignment
HeaderHAlignment
Controls how headers are horizontally aligned in a table.
Definition: qgslayouttable.h:122
QgsTextFormat::setFont
void setFont(const QFont &font)
Sets the font used for rendering text.
Definition: qgstextformat.cpp:177
QgsLayoutItemPage
Item representing the paper in a layout.
Definition: qgslayoutitempage.h:55
QgsLayoutTable::setContentTextFormat
void setContentTextFormat(const QgsTextFormat &format)
Sets the format used to draw content text in the table.
Definition: qgslayouttable.cpp:832
QgsLayoutTable::mTableContents
QgsLayoutTableContents mTableContents
Contents to show in table.
Definition: qgslayouttable.h:621
QgsRenderContext::expressionContext
QgsExpressionContext & expressionContext()
Gets the expression context.
Definition: qgsrendercontext.h:596
QgsLayoutTable::mHeaderTextFormat
QgsTextFormat mHeaderTextFormat
Definition: qgslayouttable.h:593
QgsTextRenderer::drawText
static void drawText(const QRectF &rect, double rotation, HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines=true, VAlignment vAlignment=AlignTop)
Draws text within a rectangle using the specified settings.
Definition: qgstextrenderer.cpp:75
QgsLayoutTableContents
QVector< QgsLayoutTableRow > QgsLayoutTableContents
List of QgsLayoutTableRows, representing rows and column cell contents for a QgsLayoutTable.
Definition: qgslayouttable.h:47
QgsReadWriteContext
The class is used as a container of context for various read/write operations on other objects.
Definition: qgsreadwritecontext.h:35
QgsTextRenderer::convertQtVAlignment
static VAlignment convertQtVAlignment(Qt::Alignment alignment)
Converts a Qt vertical alignment flag to a QgsTextRenderer::VAlignment value.
Definition: qgstextrenderer.cpp:55
QgsLayoutTable::mMaxColumnWidthMap
QMap< int, double > mMaxColumnWidthMap
Map of maximum width for each column.
Definition: qgslayouttable.h:624
qgstextrenderer.h
QgsLayoutTable::rowsVisible
int rowsVisible(QgsRenderContext &context, double frameHeight, int firstRow, bool includeHeader, bool includeEmptyRows) const
Calculates how many content rows would be visible within a frame of the specified height.
Definition: qgslayouttable.cpp:285
QgsLayoutTable::mVerticalGrid
bool mVerticalGrid
True if grid should be shown.
Definition: qgslayouttable.h:609
QgsLayoutTable::CellStyleGroup
CellStyleGroup
Row or column groups for cell styling.
Definition: qgslayouttable.h:162
QgsTextRenderer::AlignCenter
@ AlignCenter
Center align.
Definition: qgstextrenderer.h:61
QgsLayoutTable::~QgsLayoutTable
~QgsLayoutTable() override
Definition: qgslayouttable.cpp:60
QgsLayoutTable::cellStyle
const QgsLayoutTableStyle * cellStyle(CellStyleGroup group) const
Returns the cell style for a cell group.
Definition: qgslayouttable.cpp:973
QgsRenderContext::setFlag
void setFlag(Flag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
Definition: qgsrendercontext.cpp:179
QgsTextRenderer::AlignRight
@ AlignRight
Right align.
Definition: qgstextrenderer.h:62
QgsLayoutTable::sortColumns
QgsLayoutTableSortColumns & sortColumns()
Returns a reference to the list of QgsLayoutTableSortColumns shown in the table.
Definition: qgslayouttable.h:496
QgsLayoutTableStyle::writeXml
bool writeXml(QDomElement &styleElem, QDomDocument &doc) const
Writes the style's properties to XML for storage.
Definition: qgslayouttable.cpp:34
QgsUnitTypes::RenderPoints
@ RenderPoints
Points (e.g., for font sizes)
Definition: qgsunittypes.h:172
qgssymbollayerutils.h
QgsLayoutTable::drawVerticalGridLines
void drawVerticalGridLines(QgsLayoutItemRenderContext &context, const QMap< int, double > &maxWidthMap, int firstRow, int lastRow, bool hasHeader, bool mergeCells=false) const
Draws the vertical grid lines for the table.
Definition: qgslayouttable.cpp:1481
QgsLayoutTable::columns
QgsLayoutTableColumns & columns()
Returns a reference to the list of QgsLayoutTableColumns shown in the table.
Definition: qgslayouttable.h:482
QgsLayoutItemRenderContext
Contains settings and helpers relating to a render of a QgsLayoutItem.
Definition: qgslayoutitem.h:45
QgsTextRenderer::fontMetrics
static QFontMetricsF fontMetrics(QgsRenderContext &context, const QgsTextFormat &format, double scaleFactor=1.0)
Returns the font metrics for the given text format, when rendered in the specified render context.
Definition: qgstextrenderer.cpp:261
QgsLayoutTable::mEmptyTableMode
EmptyTableMode mEmptyTableMode
Behavior for empty tables.
Definition: qgslayouttable.h:578
QgsLayoutTable::setGridColor
void setGridColor(const QColor &color)
Sets the color used for grid lines in the table.
Definition: qgslayouttable.cpp:875
QgsLayoutTable::mHeaderHAlignment
HeaderHAlignment mHeaderHAlignment
Alignment for table headers.
Definition: qgslayouttable.h:588
QgsLayoutTable::mEmptyTableMessage
QString mEmptyTableMessage
String to show in empty tables.
Definition: qgslayouttable.h:581
QgsLayoutTable::setBackgroundColor
void setBackgroundColor(const QColor &color)
Sets the color used for background of table.
Definition: qgslayouttable.cpp:916
QgsRenderContext
Contains information about the context of a rendering operation.
Definition: qgsrendercontext.h:58
QgsUnitTypes::RenderMillimeters
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:168
QgsLayoutTable::OddColumns
@ OddColumns
Style odd numbered columns.
Definition: qgslayouttable.h:163
QgsLayoutTable::setGridStrokeWidth
void setGridStrokeWidth(double width)
Sets the width in mm for grid lines in the table.
Definition: qgslayouttable.cpp:861
QgsLayoutTable::HideTable
@ HideTable
Hides entire table if empty.
Definition: qgslayouttable.h:145
QgsLayoutTable::FirstRow
@ FirstRow
Style first row only.
Definition: qgslayouttable.h:170
QgsLayoutTable::FirstColumn
@ FirstColumn
Style first column only.
Definition: qgslayouttable.h:167
QgsLayoutTable::writePropertiesToElement
bool writePropertiesToElement(QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context) const override
Stores multiframe state within an XML DOM element.
Definition: qgslayouttable.cpp:69
QgsLayoutMultiFrame
Abstract base class for layout items with the ability to distribute the content to several frames (Qg...
Definition: qgslayoutmultiframe.h:49
qgsfontutils.h
QgsRenderContext::scaleFactor
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
Definition: qgsrendercontext.h:333
QgsLayoutTable::setEmptyTableBehavior
void setEmptyTableBehavior(EmptyTableMode mode)
Sets the behavior mode for empty tables with no content rows.
Definition: qgslayouttable.cpp:663
QgsLayoutObject::changed
void changed()
Emitted when the object's properties change.
QgsSymbolLayerUtils::decodeColor
static QColor decodeColor(const QString &str)
Definition: qgssymbollayerutils.cpp:57
QgsLayoutTable::setShowGrid
void setShowGrid(bool showGrid)
Sets whether grid lines should be drawn in the table.
Definition: qgslayouttable.cpp:847
QgsConditionalStyle
Conditional styling for a rule.
Definition: qgsconditionalstyle.h:113
QgsLayoutItem::sizeWithUnits
QgsLayoutSize sizeWithUnits() const
Returns the item's current size, including units.
Definition: qgslayoutitem.h:671
QgsLayoutTable::calculateMaxColumnWidths
virtual bool calculateMaxColumnWidths()
Calculates the maximum width of text shown in columns.
Definition: qgslayouttable.cpp:1073
QgsLayoutTable::mShowGrid
bool mShowGrid
True if grid should be shown.
Definition: qgslayouttable.h:597
QgsLayoutTableColumn::readXml
bool readXml(const QDomElement &columnElem)
Reads the column's properties from xml.
Definition: qgslayouttablecolumn.cpp:50
QgsLayoutTable::textFormatForCell
virtual QgsTextFormat textFormatForCell(int row, int column) const
Returns the text format to use for the cell at the specified row and column.
Definition: qgslayouttable.cpp:1544
QgsLayoutTableSortColumns
QVector< QgsLayoutTableColumn > QgsLayoutTableSortColumns
List of column definitions for sorting a QgsLayoutTable.
Definition: qgslayouttable.h:65
QgsLayoutMultiFrame::ExtendToNextPage
@ ExtendToNextPage
Creates new full page frames on the following page(s) until the entire multiframe content is visible.
Definition: qgslayoutmultiframe.h:104
QgsTextFormat::color
QColor color() const
Returns the color that text will be rendered in.
Definition: qgstextformat.cpp:232
QgsLayoutTable::mCellMargin
double mCellMargin
Margin between cell borders and cell text.
Definition: qgslayouttable.h:575
QgsLayoutTable::contentFontColor
Q_DECL_DEPRECATED QColor contentFontColor() const
Returns the color used to draw text in table body cells.
Definition: qgslayouttable.cpp:827
QgsTextRenderer::Rect
@ Rect
Text within rectangle draw mode.
Definition: qgstextrenderer.h:43
QgsTextRenderer::textWidth
static double textWidth(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, QFontMetricsF *fontMetrics=nullptr)
Returns the width of a text based on a given format.
Definition: qgstextrenderer.cpp:496
qgslayoutframe.h
QgsLayoutTable::setHeaderFont
Q_DECL_DEPRECATED void setHeaderFont(const QFont &font)
Sets the font used to draw header text in the table.
Definition: qgslayouttable.cpp:705
qgslayoututils.h
QgsLayoutTable::verticalAlignmentForCell
virtual Qt::Alignment verticalAlignmentForCell(int row, int column) const
Returns the vertical alignment to use for the cell at the specified row and column.
Definition: qgslayouttable.cpp:1559
QgsLayoutTable::mSortColumns
QgsLayoutTableSortColumns mSortColumns
Columns to sort the table.
Definition: qgslayouttable.h:618
QgsLayoutUtils::createRenderContextForLayout
static QgsRenderContext createRenderContextForLayout(QgsLayout *layout, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout and painter destination.
Definition: qgslayoututils.cpp:138
QgsLayoutTable::readPropertiesFromElement
bool readPropertiesFromElement(const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context) override
Sets multiframe state from a DOM element.
Definition: qgslayouttable.cpp:133
Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:797
QgsTextFormat
Container for all settings relating to text rendering.
Definition: qgstextformat.h:40
QgsTextFormat::setColor
void setColor(const QColor &color)
Sets the color that text will be rendered in.
Definition: qgstextformat.cpp:237
QgsLayoutTable::fixedFrameSize
QSizeF fixedFrameSize(int frameIndex=-1) const override
Returns the fixed size for a frame, if desired.
Definition: qgslayouttable.cpp:1007
QgsLayoutTable::HeaderRow
@ HeaderRow
Style header row.
Definition: qgslayouttable.h:169
QgsLayoutTable::FirstFrame
@ FirstFrame
Header shown on first frame only.
Definition: qgslayouttable.h:134
QgsLayoutTable::QgsLayoutTable
QgsLayoutTable(QgsLayout *layout)
Constructor for QgsLayoutTable, belonging to the specified layout.
Definition: qgslayouttable.cpp:54
QgsLayoutMultiFrame::update
void update()
Forces a redraw of all child frames.
Definition: qgslayoutmultiframe.cpp:445
QgsLayoutTable::mGridColor
QColor mGridColor
Color for grid lines.
Definition: qgslayouttable.h:603
QgsLayoutTable::ShowMessage
@ ShowMessage
Shows preset message instead of table contents.
Definition: qgslayouttable.h:146
qgslayouttable.h
QgsLayoutTable::minFrameSize
QSizeF minFrameSize(int frameIndex=-1) const override
Returns the minimum size for a frames, if desired.
Definition: qgslayouttable.cpp:1013
QgsLayoutTable::contents
QgsLayoutTableContents & contents()
Returns the current contents of the table.
Definition: qgslayouttable.h:550
QgsTextFormat::updateDataDefinedProperties
void updateDataDefinedProperties(QgsRenderContext &context)
Updates the format by evaluating current values of data defined properties.
Definition: qgstextformat.cpp:728
QgsLayoutTable::WrapText
@ WrapText
Text which doesn't fit inside the cell is wrapped. Note that this only applies to text in columns wit...
Definition: qgslayouttable.h:155
QgsLayoutTable::verticalGrid
bool verticalGrid() const
Returns whether the grid's vertical lines are drawn in the table.
Definition: qgslayouttable.h:448
QgsLayoutTable::setVerticalGrid
void setVerticalGrid(bool verticalGrid)
Sets whether the grid's vertical lines should be drawn in the table.
Definition: qgslayouttable.cpp:902
QgsLayoutTable::scopeForCell
virtual QgsExpressionContextScope * scopeForCell(int row, int column) const
Creates a new QgsExpressionContextScope for the cell at row, column.
Definition: qgslayouttable.cpp:994
QgsLayoutTable::totalHeight
double totalHeight()
Returns total height of table contents.
Definition: qgslayouttable.cpp:1257
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
QgsLayoutTable::setCellMargin
void setCellMargin(double margin)
Sets the margin distance in mm between cell borders and their contents.
Definition: qgslayouttable.cpp:648
QgsConditionalStyle::backgroundColor
QColor backgroundColor() const
The background color for style.
Definition: qgsconditionalstyle.h:216
QgsLayoutTable::OddRows
@ OddRows
Style odd numbered rows.
Definition: qgslayouttable.h:165
QgsLayoutTable::setHorizontalGrid
void setHorizontalGrid(bool horizontalGrid)
Sets whether the grid's horizontal lines should be drawn in the table.
Definition: qgslayouttable.cpp:888
QgsLayoutTable::TruncateText
@ TruncateText
Text which doesn't fit inside the cell is truncated.
Definition: qgslayouttable.h:154
QgsLayoutFrame::extent
QRectF extent() const
Returns the visible portion of the multi frame's content which is shown in this frame,...
Definition: qgslayoutframe.h:76
QgsLayoutTable::totalSize
QSizeF totalSize() const override
Returns the total size of the multiframe's content, in layout units.
Definition: qgslayouttable.cpp:274
QgsLayoutTable::LastRow
@ LastRow
Style last row only.
Definition: qgslayouttable.h:171
QgsLayoutTable::mGridStrokeWidth
double mGridStrokeWidth
Width of grid lines.
Definition: qgslayouttable.h:600
QgsLayoutTable::NoHeaders
@ NoHeaders
No headers shown for table.
Definition: qgslayouttable.h:136
QgsLayoutTable::getTableContents
virtual bool getTableContents(QgsLayoutTableContents &contents)=0
Fetches the contents used for the cells in the table.
QgsLayoutTable::mTableSize
QSizeF mTableSize
Definition: qgslayouttable.h:629
QgsTextRenderer::AlignLeft
@ AlignLeft
Left align.
Definition: qgstextrenderer.h:60
QgsLayoutTable::textFormatForHeader
virtual QgsTextFormat textFormatForHeader(int column) const
Returns the text format to use for the header cell at the specified column.
Definition: qgslayouttable.cpp:1549
QgsLayoutMultiFrame::mResizeMode
ResizeMode mResizeMode
Definition: qgslayoutmultiframe.h:410
QgsLayoutTable::contentTextFormat
QgsTextFormat contentTextFormat() const
Returns the format used to draw content text in the table.
Definition: qgslayouttable.cpp:842
QgsLayoutTable::HeaderRight
@ HeaderRight
Align headers right.
Definition: qgslayouttable.h:126
QgsTextRenderer::textHeight
static double textHeight(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, DrawMode mode=Point, QFontMetricsF *fontMetrics=nullptr)
Returns the height of a text based on a given format.
Definition: qgstextrenderer.cpp:567
QgsLayoutTable::HeaderMode
HeaderMode
Controls where headers are shown in the table.
Definition: qgslayouttable.h:133
QgsTextFormat::readXml
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Read settings from a DOM element.
Definition: qgstextformat.cpp:406
QgsTextFormat::toQFont
QFont toQFont() const
Returns a QFont matching the relevant settings from this text format.
Definition: qgstextformat.cpp:625
QgsScopedQPainterState
Scoped object for saving and restoring a QPainter object's state.
Definition: qgsrendercontext.h:1120
QgsLayoutTable::WrapBehavior
WrapBehavior
Controls how long strings in the table are handled.
Definition: qgslayouttable.h:153
QgsLayoutTable::refreshAttributes
virtual void refreshAttributes()
Refreshes the contents shown in the table by querying for new data.
Definition: qgslayouttable.cpp:1031
QgsLayoutTable::recalculateFrameSizes
void recalculateFrameSizes() override
Definition: qgslayouttable.cpp:1044
qgslayout.h
QgsLayoutTable::EvenRows
@ EvenRows
Style even numbered rows.
Definition: qgslayouttable.h:166
QgsLayoutTable::EmptyTableMode
EmptyTableMode
Controls how empty tables are displayed.
Definition: qgslayouttable.h:143
QgsLayoutMultiFrame::recalculateFrameRects
void recalculateFrameRects()
Forces a recalculation of all the associated frame's scene rectangles.
Definition: qgslayoutmultiframe.cpp:227
QgsExpressionContextScope
Single scope for storing variables and functions for use within a QgsExpressionContext.
Definition: qgsexpressioncontext.h:112
QgsUnitTypes::RenderPixels
@ RenderPixels
Pixels.
Definition: qgsunittypes.h:170
QgsLayoutTable::recalculateTableSize
void recalculateTableSize()
Recalculates and updates the size of the table and all table frames.
Definition: qgslayouttable.cpp:1530
QgsLayoutTable::HeaderCenter
@ HeaderCenter
Align headers to center.
Definition: qgslayouttable.h:125
QgsLayoutMultiFrame::refresh
void refresh() override
Refreshes the multiframe, causing a recalculation of any property overrides.
Definition: qgslayoutmultiframe.cpp:338
QgsLayoutSize::height
double height() const
Returns the height of the size.
Definition: qgslayoutsize.h:90
QgsLayoutTable::calculateMaxRowHeights
virtual bool calculateMaxRowHeights()
Calculates the maximum height of text shown in rows.
Definition: qgslayouttable.cpp:1155
QgsLayoutTable::setSortColumns
void setSortColumns(const QgsLayoutTableSortColumns &sortColumns)
Replaces the sorting columns in the table with a specified list of QgsLayoutTableSortColumns.
Definition: qgslayouttable.cpp:960
QgsLayoutTable::setShowEmptyRows
void setShowEmptyRows(bool showEmpty)
Sets whether empty rows should be drawn.
Definition: qgslayouttable.cpp:693
QgsLayoutTable::setHeaderFontColor
Q_DECL_DEPRECATED void setHeaderFontColor(const QColor &color)
Sets the color used to draw header text in the table.
Definition: qgslayouttable.cpp:730
QgsLayoutTable::headerLabels
virtual QMap< int, QString > headerLabels() const
Returns the text used in the column headers for the table.
Definition: qgslayouttable.cpp:981
QgsFontUtils::setFromXmlChildNode
static bool setFromXmlChildNode(QFont &font, const QDomElement &element, const QString &childNode)
Sets the properties of a font to match the properties stored in an XML child node.
Definition: qgsfontutils.cpp:348
QgsLayoutItemRenderContext::renderContext
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Definition: qgslayoutitem.h:72
QgsLayoutTableStyle::enabled
bool enabled
Whether the styling option is enabled.
Definition: qgslayouttable.h:84
QgsLayoutTable::rowRange
QPair< int, int > rowRange(QgsRenderContext &context, int frameIndex) const
Calculates a range of rows which should be visible in a given frame.
Definition: qgslayouttable.cpp:345
QgsLayoutTableStyle
Styling option for a layout table cell.
Definition: qgslayouttable.h:77
QgsLayoutTable::setColumns
void setColumns(const QgsLayoutTableColumns &columns)
Replaces the columns in the table with a specified list of QgsLayoutTableColumns.
Definition: qgslayouttable.cpp:942
QgsLayoutTable::LastColumn
@ LastColumn
Style last column only.
Definition: qgslayouttable.h:168
QgsLayoutTable::mCellStyles
QMap< CellStyleGroup, QgsLayoutTableStyle * > mCellStyles
Definition: qgslayouttable.h:633
QgsLayoutObject::mLayout
QPointer< QgsLayout > mLayout
Definition: qgslayoutobject.h:343
QgsLayoutTable::horizontalGrid
bool horizontalGrid() const
Returns whether the grid's horizontal lines are drawn in the table.
Definition: qgslayouttable.h:429
QgsLayout
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:50
QgsConditionalStyle::textColor
QColor textColor() const
The text color set for style.
Definition: qgsconditionalstyle.h:203
QgsLayoutTable::setWrapBehavior
void setWrapBehavior(WrapBehavior behavior)
Sets the wrap behavior for the table, which controls how text within cells is automatically wrapped.
Definition: qgslayouttable.cpp:929
QgsLayoutTable::setCellStyle
void setCellStyle(CellStyleGroup group, const QgsLayoutTableStyle &style)
Sets the cell style for a cell group.
Definition: qgslayouttable.cpp:965
qgslayouttablecolumn.h
QgsRenderContext::ApplyScalingWorkaroundForTextRendering
@ ApplyScalingWorkaroundForTextRendering
Whether a scaling workaround designed to stablise the rendering of small font sizes (or for painters ...
Definition: qgsrendercontext.h:85
QgsLayoutTable::setContentFont
Q_DECL_DEPRECATED void setContentFont(const QFont &font)
Sets the font used to draw text in table body cells.
Definition: qgslayouttable.cpp:789
qgssettings.h
QgsLayoutTable::setHeaderTextFormat
void setHeaderTextFormat(const QgsTextFormat &format)
Sets the format used to draw header text in the table.
Definition: qgslayouttable.cpp:748
QgsLayoutTable::contentsContainsRow
bool contentsContainsRow(const QgsLayoutTableContents &contents, const QgsLayoutTableRow &row) const
Checks whether a table contents contains a given row.
Definition: qgslayouttable.cpp:1539
QgsLayoutTable::headerTextFormat
QgsTextFormat headerTextFormat() const
Returns the format used to draw header text in the table.
Definition: qgslayouttable.cpp:758
qgslayoutpagecollection.h
QgsTextFormat::setSizeUnit
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the size of rendered text.
Definition: qgstextformat.cpp:204
QgsTextRenderer::AlignVCenter
@ AlignVCenter
Center align.
Definition: qgstextrenderer.h:81
QgsLayoutTable::mMaxRowHeightMap
QMap< int, double > mMaxRowHeightMap
Map of maximum height for each row.
Definition: qgslayouttable.h:627
QgsLayoutMultiFrame::recalculateFrameSizes
virtual void recalculateFrameSizes()
Recalculates the portion of the multiframe item which is shown in each of its component frames.
Definition: qgslayoutmultiframe.cpp:96
QgsLayoutTableStyle::readXml
bool readXml(const QDomElement &styleElem)
Reads the style's properties from XML.
Definition: qgslayouttable.cpp:42
QgsLayoutTableRow
QVector< QVariant > QgsLayoutTableRow
List of QVariants, representing a the contents of a single row in a QgsLayoutTable.
Definition: qgslayouttable.h:30
QgsLayoutTable::conditionalCellStyle
virtual QgsConditionalStyle conditionalCellStyle(int row, int column) const
Returns the conditional style to use for the cell at row, column.
Definition: qgslayouttable.cpp:1002
QgsLayoutTable::mColumns
QgsLayoutTableColumns mColumns
Columns to show in table.
Definition: qgslayouttable.h:615
QgsLayoutTableColumn
Stores properties of a column for a QgsLayoutTable.
Definition: qgslayouttablecolumn.h:37
QgsLayoutTable::horizontalAlignmentForCell
virtual Qt::Alignment horizontalAlignmentForCell(int row, int column) const
Returns the horizontal alignment to use for the cell at the specified row and column.
Definition: qgslayouttable.cpp:1554
QgsScopedRenderContextScaleToPixels
Scoped object for temporary scaling of a QgsRenderContext for pixel based rendering.
Definition: qgsrendercontext.h:1076
QgsLayoutTable::FollowColumn
@ FollowColumn
Header uses the same alignment as the column.
Definition: qgslayouttable.h:123
QgsRenderContext::painter
QPainter * painter()
Returns the destination QPainter for the render operation.
Definition: qgsrendercontext.h:179
QgsTextFormat::setSize
void setSize(double size)
Sets the size for rendered text.
Definition: qgstextformat.cpp:226
QgsLayoutTable::mBackgroundColor
QColor mBackgroundColor
Color for table background.
Definition: qgslayouttable.h:612
QgsLayoutTable::setContentFontColor
Q_DECL_DEPRECATED void setContentFontColor(const QColor &color)
Sets the color used to draw text in table body cells.
Definition: qgslayouttable.cpp:814
QgsLayoutTable::mHeaderMode
HeaderMode mHeaderMode
Header display mode.
Definition: qgslayouttable.h:591
QgsLayoutMultiFrame::frameIndex
int frameIndex(QgsLayoutFrame *frame) const
Returns the index of a frame within the multiframe.
Definition: qgslayoutmultiframe.cpp:478
QgsTextRenderer::convertQtHAlignment
static HAlignment convertQtHAlignment(Qt::Alignment alignment)
Converts a Qt horizontal alignment flag to a QgsTextRenderer::HAlignment value.
Definition: qgstextrenderer.cpp:40
Q_NOWARN_DEPRECATED_PUSH
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:796
QgsLayoutTable::HeaderLeft
@ HeaderLeft
Align headers left.
Definition: qgslayouttable.h:124
QgsLayoutTable::setEmptyTableMessage
void setEmptyTableMessage(const QString &message)
Sets the message for empty tables with no content rows.
Definition: qgslayouttable.cpp:678
QgsLayoutTable::mContentTextFormat
QgsTextFormat mContentTextFormat
Definition: qgslayouttable.h:594
QgsLayoutTable::totalWidth
double totalWidth()
Returns total width of table contents.
Definition: qgslayouttable.cpp:1236
QgsLayoutTable::AllFrames
@ AllFrames
Headers shown on all frames.
Definition: qgslayouttable.h:135
QgsLayoutTable::contentFont
Q_DECL_DEPRECATED QFont contentFont() const
Returns the font used to draw text in table body cells.
Definition: qgslayouttable.cpp:809
QgsTextFormat::writeXml
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Write settings into a DOM element.
Definition: qgstextformat.cpp:555
QgsLayoutTable::drawHorizontalGridLines
void drawHorizontalGridLines(QgsLayoutItemRenderContext &context, int firstRow, int lastRow, bool drawHeaderLines) const
Draws the horizontal grid lines for the table.
Definition: qgslayouttable.cpp:1333
QgsLayoutTable::mHorizontalGrid
bool mHorizontalGrid
True if grid should be shown.
Definition: qgslayouttable.h:606
QgsLayoutTable::refresh
void refresh() override
Definition: qgslayouttable.cpp:279
QgsTextRenderer::FONT_WORKAROUND_SCALE
static constexpr double FONT_WORKAROUND_SCALE
Scale factor for upscaling font sizes and downscaling destination painter devices.
Definition: qgstextrenderer.h:240
QgsLayoutMultiFrame::frameCount
int frameCount() const
Returns the number of frames associated with this multiframe.
Definition: qgslayoutmultiframe.h:265
QgsTextRenderer::HAlignment
HAlignment
Horizontal alignment.
Definition: qgstextrenderer.h:59
QgsLayoutTable::EvenColumns
@ EvenColumns
Style even numbered columns.
Definition: qgslayouttable.h:164
QgsLayoutTable::mShowEmptyRows
bool mShowEmptyRows
True if empty rows should be shown in the table.
Definition: qgslayouttable.h:584
QgsLayoutTable::headerFont
Q_DECL_DEPRECATED QFont headerFont() const
Returns the font used to draw header text in the table.
Definition: qgslayouttable.cpp:725