QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
qgscomposertablev2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposertablev2.cpp
3  ------------------
4  begin : July 2014
5  copyright : (C) 2014 by Nyall Dawson, Marco Hugentobler
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 
18 #include "qgscomposertablev2.h"
19 #include "qgscomposerutils.h"
20 #include "qgscomposertablecolumn.h"
21 #include "qgssymbollayerv2utils.h"
22 #include "qgscomposerframe.h"
23 #include "qgsfontutils.h"
24 
25 //
26 // QgsComposerTableStyle
27 //
28 
30 {
31  Q_UNUSED( doc );
32  styleElem.setAttribute( "cellBackgroundColor", QgsSymbolLayerV2Utils::encodeColor( cellBackgroundColor ) );
33  styleElem.setAttribute( "enabled", enabled );
34  return true;
35 }
36 
38 {
39  cellBackgroundColor = QgsSymbolLayerV2Utils::decodeColor( styleElem.attribute( "cellBackgroundColor", "255,255,255,255" ) );
40  enabled = ( styleElem.attribute( "enabled", "0" ) != "0" );
41  return true;
42 }
43 
44 
45 //
46 // QgsComposerTableV2
47 //
48 
49 QgsComposerTableV2::QgsComposerTableV2( QgsComposition *composition, bool createUndoCommands )
50  : QgsComposerMultiFrame( composition, createUndoCommands )
51  , mCellMargin( 1.0 )
52  , mEmptyTableMode( HeadersOnly )
53  , mShowEmptyRows( false )
54  , mHeaderFontColor( Qt::black )
55  , mHeaderHAlignment( FollowColumn )
56  , mHeaderMode( FirstFrame )
57  , mContentFontColor( Qt::black )
58  , mShowGrid( true )
59  , mGridStrokeWidth( 0.5 )
60  , mGridColor( Qt::black )
61  , mBackgroundColor( Qt::white )
62  , mWrapBehaviour( TruncateText )
63 {
64 
65  if ( mComposition )
66  {
67  QObject::connect( mComposition, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SLOT( handleFrameRemoval( QgsComposerItem* ) ) );
68  }
69 
70  //get default composer font from settings
71  QSettings settings;
72  QString defaultFontString = settings.value( "/Composer/defaultFont" ).toString();
73  if ( !defaultFontString.isEmpty() )
74  {
75  mHeaderFont.setFamily( defaultFontString );
76  mContentFont.setFamily( defaultFontString );
77  }
78 
79  initStyles();
80 }
81 
83  : QgsComposerMultiFrame( nullptr, false )
84  , mCellMargin( 1.0 )
86  , mShowEmptyRows( false )
87  , mHeaderFontColor( Qt::black )
90  , mContentFontColor( Qt::black )
91  , mShowGrid( true )
92  , mGridStrokeWidth( 0.5 )
93  , mGridColor( Qt::black )
94  , mBackgroundColor( Qt::white )
96 {
97  initStyles();
98 }
99 
101 {
102  qDeleteAll( mColumns );
103  mColumns.clear();
104 
105  qDeleteAll( mCellStyles );
106  mCellStyles.clear();
107 }
108 
109 bool QgsComposerTableV2::writeXML( QDomElement& elem, QDomDocument & doc, bool ignoreFrames ) const
110 {
111  elem.setAttribute( "cellMargin", QString::number( mCellMargin ) );
112  elem.setAttribute( "emptyTableMode", QString::number( static_cast< int >( mEmptyTableMode ) ) );
113  elem.setAttribute( "emptyTableMessage", mEmptyTableMessage );
114  elem.setAttribute( "showEmptyRows", mShowEmptyRows );
115  elem.appendChild( QgsFontUtils::toXmlElement( mHeaderFont, doc, "headerFontProperties" ) );
117  elem.setAttribute( "headerHAlignment", QString::number( static_cast< int >( mHeaderHAlignment ) ) );
118  elem.setAttribute( "headerMode", QString::number( static_cast< int >( mHeaderMode ) ) );
119  elem.appendChild( QgsFontUtils::toXmlElement( mContentFont, doc, "contentFontProperties" ) );
121  elem.setAttribute( "gridStrokeWidth", QString::number( mGridStrokeWidth ) );
123  elem.setAttribute( "showGrid", mShowGrid );
125  elem.setAttribute( "wrapBehaviour", QString::number( static_cast< int >( mWrapBehaviour ) ) );
126 
127  //columns
128  QDomElement displayColumnsElem = doc.createElement( "displayColumns" );
130  for ( ; columnIt != mColumns.constEnd(); ++columnIt )
131  {
132  QDomElement columnElem = doc.createElement( "column" );
133  ( *columnIt )->writeXML( columnElem, doc );
134  displayColumnsElem.appendChild( columnElem );
135  }
136  elem.appendChild( displayColumnsElem );
137 
138  //cell styles
139  QDomElement stylesElem = doc.createElement( "cellStyles" );
141  for ( ; it != mCellStyleNames.constEnd(); ++it )
142  {
143  QString styleName = it.value();
144  QDomElement styleElem = doc.createElement( styleName );
145  mCellStyles.value( it.key() )->writeXML( styleElem, doc );
146  stylesElem.appendChild( styleElem );
147  }
148  elem.appendChild( stylesElem );
149 
150  bool state = _writeXML( elem, doc, ignoreFrames );
151  return state;
152 }
153 
154 bool QgsComposerTableV2::readXML( const QDomElement &itemElem, const QDomDocument &doc, bool ignoreFrames )
155 {
156  deleteFrames();
157 
158  //first create the frames
159  if ( !_readXML( itemElem, doc, ignoreFrames ) )
160  {
161  return false;
162  }
163 
164  if ( itemElem.isNull() )
165  {
166  return false;
167  }
168 
169  mEmptyTableMode = QgsComposerTableV2::EmptyTableMode( itemElem.attribute( "emptyTableMode", "0" ).toInt() );
170  mEmptyTableMessage = itemElem.attribute( "emptyTableMessage", tr( "No matching records" ) );
171  mShowEmptyRows = itemElem.attribute( "showEmptyRows", "0" ).toInt();
172  if ( !QgsFontUtils::setFromXmlChildNode( mHeaderFont, itemElem, "headerFontProperties" ) )
173  {
174  mHeaderFont.fromString( itemElem.attribute( "headerFont", "" ) );
175  }
176  mHeaderFontColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "headerFontColor", "0,0,0,255" ) );
177  mHeaderHAlignment = QgsComposerTableV2::HeaderHAlignment( itemElem.attribute( "headerHAlignment", "0" ).toInt() );
178  mHeaderMode = QgsComposerTableV2::HeaderMode( itemElem.attribute( "headerMode", "0" ).toInt() );
179  if ( !QgsFontUtils::setFromXmlChildNode( mContentFont, itemElem, "contentFontProperties" ) )
180  {
181  mContentFont.fromString( itemElem.attribute( "contentFont", "" ) );
182  }
183  mContentFontColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "contentFontColor", "0,0,0,255" ) );
184  mCellMargin = itemElem.attribute( "cellMargin", "1.0" ).toDouble();
185  mGridStrokeWidth = itemElem.attribute( "gridStrokeWidth", "0.5" ).toDouble();
186  mShowGrid = itemElem.attribute( "showGrid", "1" ).toInt();
187  mGridColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "gridColor", "0,0,0,255" ) );
188  mBackgroundColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "backgroundColor", "255,255,255,0" ) );
189  mWrapBehaviour = QgsComposerTableV2::WrapBehaviour( itemElem.attribute( "wrapBehaviour", "0" ).toInt() );
190 
191  //restore column specifications
192  qDeleteAll( mColumns );
193  mColumns.clear();
194  QDomNodeList columnsList = itemElem.elementsByTagName( "displayColumns" );
195  if ( !columnsList.isEmpty() )
196  {
197  QDomElement columnsElem = columnsList.at( 0 ).toElement();
198  QDomNodeList columnEntryList = columnsElem.elementsByTagName( "column" );
199  for ( int i = 0; i < columnEntryList.size(); ++i )
200  {
201  QDomElement columnElem = columnEntryList.at( i ).toElement();
203  column->readXML( columnElem );
204  mColumns.append( column );
205  }
206  }
207 
208  //restore cell styles
209  QDomNodeList stylesList = itemElem.elementsByTagName( "cellStyles" );
210  if ( !stylesList.isEmpty() )
211  {
212  QDomElement stylesElem = stylesList.at( 0 ).toElement();
213 
215  for ( ; it != mCellStyleNames.constEnd(); ++it )
216  {
217  QString styleName = it.value();
218  QDomNodeList styleList = stylesElem.elementsByTagName( styleName );
219  if ( !styleList.isEmpty() )
220  {
221  QDomElement styleElem = styleList.at( 0 ).toElement();
222  mCellStyles.value( it.key() )->readXML( styleElem );
223  }
224  }
225  }
226 
227  return true;
228 }
229 
231 {
232  return mTableSize;
233 }
234 
236 {
237  //get frame extent
238  if ( frameIndex >= frameCount() )
239  {
240  return 0;
241  }
242  QRectF frameExtent = frame( frameIndex )->extent();
243 
244  bool includeHeader = false;
245  if (( mHeaderMode == QgsComposerTableV2::FirstFrame && frameIndex < 1 )
247  {
248  includeHeader = true;
249  }
251  return rowsVisible( frameExtent.height(), includeHeader );
253 }
254 
255 int QgsComposerTableV2::rowsVisible( const double frameHeight, const bool includeHeader ) const
256 {
257  //calculate header height
258  double headerHeight = 0;
259  if ( includeHeader )
260  {
261  //frame has a header
262  headerHeight = 2 * ( mShowGrid ? mGridStrokeWidth : 0 ) + 2 * mCellMargin + QgsComposerUtils::fontAscentMM( mHeaderFont );
263  }
264  else
265  {
266  //frame has no header text, just the stroke
267  headerHeight = ( mShowGrid ? mGridStrokeWidth : 0 );
268  }
269 
270  //remaining height available for content rows
271  double contentHeight = frameHeight - headerHeight;
272 
273  //calculate number of visible rows
274  double rowHeight = ( mShowGrid ? mGridStrokeWidth : 0 ) + 2 * mCellMargin + QgsComposerUtils::fontAscentMM( mContentFont );
275  return qMax( floor( contentHeight / rowHeight ), 0.0 );
276 }
277 
278 int QgsComposerTableV2::rowsVisible( double frameHeight, int firstRow, bool includeHeader , bool includeEmptyRows ) const
279 {
280  //calculate header height
281  double headerHeight = 0;
282  if ( includeHeader )
283  {
284  //frame has a header
285  headerHeight = 2 * ( mShowGrid ? mGridStrokeWidth : 0 ) + 2 * mCellMargin + QgsComposerUtils::fontAscentMM( mHeaderFont );
286  }
287  else
288  {
289  //frame has no header text, just the stroke
290  headerHeight = ( mShowGrid ? mGridStrokeWidth : 0 );
291  }
292 
293  //remaining height available for content rows
294  double contentHeight = frameHeight - headerHeight;
295 
296  double gridHeight = ( mShowGrid ? mGridStrokeWidth : 0 );
297 
298  int currentRow = firstRow;
299  while ( contentHeight > 0 && currentRow <= mTableContents.count() )
300  {
301  double currentRowHeight = mMaxRowHeightMap.value( currentRow + 1 ) + gridHeight + 2 * mCellMargin;
302  contentHeight -= currentRowHeight;
303  currentRow++;
304  }
305 
306  if ( includeEmptyRows && contentHeight > 0 )
307  {
308  double rowHeight = ( mShowGrid ? mGridStrokeWidth : 0 ) + 2 * mCellMargin + QgsComposerUtils::fontAscentMM( mContentFont );
309  currentRow += qMax( floor( contentHeight / rowHeight ), 0.0 );
310  }
311 
312  return currentRow - firstRow - 1;
313 }
314 
315 int QgsComposerTableV2::rowsVisible( int frameIndex, int firstRow, bool includeEmptyRows ) const
316 {
317  //get frame extent
318  if ( frameIndex >= frameCount() )
319  {
320  return 0;
321  }
322  QRectF frameExtent = frame( frameIndex )->extent();
323 
324  bool includeHeader = false;
325  if (( mHeaderMode == QgsComposerTableV2::FirstFrame && frameIndex < 1 )
327  {
328  includeHeader = true;
329  }
330  return rowsVisible( frameExtent.height(), firstRow, includeHeader, includeEmptyRows );
331 }
332 
334 {
335  //calculate row height
336  if ( frameIndex >= frameCount() )
337  {
338  //bad frame index
339  return qMakePair( 0, 0 );
340  }
341 
342  //loop through all previous frames to calculate how many rows are visible in each
343  //as the entire height of a frame may not be utilised for content rows
344  int rowsAlreadyShown = 0;
345  for ( int idx = 0; idx < frameIndex; ++idx )
346  {
347  rowsAlreadyShown += rowsVisible( idx, rowsAlreadyShown, false );
348  }
349 
350  //using zero based indexes
351  int firstVisible = qMin( rowsAlreadyShown, mTableContents.length() );
352  int possibleRowsVisible = rowsVisible( frameIndex, rowsAlreadyShown, false );
353  int lastVisible = qMin( firstVisible + possibleRowsVisible, mTableContents.length() );
354 
355  return qMakePair( firstVisible, lastVisible );
356 }
357 
359 {
360  Q_UNUSED( extent );
361  return rowRange( frameIndex );
362 }
363 
364 
365 void QgsComposerTableV2::render( QPainter *p, const QRectF &, const int frameIndex )
366 {
367  if ( !p )
368  {
369  return;
370  }
371 
372  bool emptyTable = mTableContents.length() == 0;
373  if ( emptyTable && mEmptyTableMode == QgsComposerTableV2::HideTable )
374  {
375  //empty table set to hide table mode, so don't draw anything
376  return;
377  }
378 
381  {
382  //exporting composition, so force an attribute refresh
383  //we do this in case vector layer has changed via an external source (eg, another database user)
385  }
386 
387  //calculate which rows to show in this frame
388  QPair< int, int > rowsToShow = rowRange( frameIndex );
389 
390  double gridSize = mShowGrid ? mGridStrokeWidth : 0;
391  double cellHeaderHeight = QgsComposerUtils::fontAscentMM( mHeaderFont ) + 2 * mCellMargin;
392  double cellBodyHeight = QgsComposerUtils::fontAscentMM( mContentFont ) + 2 * mCellMargin;
393  QRectF cell;
394 
395  //calculate whether a header is required
396  bool drawHeader = (( mHeaderMode == QgsComposerTableV2::FirstFrame && frameIndex < 1 )
398  //calculate whether drawing table contents is required
399  bool drawContents = !( emptyTable && mEmptyTableMode == QgsComposerTableV2::ShowMessage );
400 
401  int numberRowsToDraw = rowsToShow.second - rowsToShow.first;
402  int numberEmptyRows = 0;
403  if ( drawContents && mShowEmptyRows )
404  {
405  numberRowsToDraw = rowsVisible( frameIndex, rowsToShow.first, true );
406  numberEmptyRows = numberRowsToDraw - rowsToShow.second + rowsToShow.first;
407  }
408  bool mergeCells = false;
409  if ( emptyTable && mEmptyTableMode == QgsComposerTableV2::ShowMessage )
410  {
411  //draw a merged row for the empty table message
412  numberRowsToDraw++;
413  rowsToShow.second++;
414  mergeCells = true;
415  }
416 
417  p->save();
418  //antialiasing on
419  p->setRenderHint( QPainter::Antialiasing, true );
420 
421  //draw the text
422  p->setPen( Qt::SolidLine );
423 
424  double currentX = gridSize;
425  double currentY = gridSize;
426  if ( drawHeader )
427  {
428  //draw the headers
429  int col = 0;
430  Q_FOREACH ( const QgsComposerTableColumn* column, mColumns )
431  {
432  //draw background
433  p->save();
434  p->setPen( Qt::NoPen );
435  p->setBrush( backgroundColor( -1, col ) );
436  p->drawRect( QRectF( currentX, currentY, mMaxColumnWidthMap[col] + 2 * mCellMargin, cellHeaderHeight ) );
437  p->restore();
438 
439  currentX += mCellMargin;
440 
441  Qt::TextFlag textFlag = static_cast< Qt::TextFlag >( 0 );
442  if ( column->width() <= 0 )
443  {
444  //automatic column width, so we use the Qt::TextDontClip flag when drawing contents, as this works nicer for italicised text
445  //which may slightly exceed the calculated width
446  //if column size was manually set then we do apply text clipping, to avoid painting text outside of columns width
447  textFlag = Qt::TextDontClip;
448  }
449 
450  cell = QRectF( currentX, currentY, mMaxColumnWidthMap[col], cellHeaderHeight );
451 
452  //calculate alignment of header
453  Qt::AlignmentFlag headerAlign = Qt::AlignLeft;
454  switch ( mHeaderHAlignment )
455  {
456  case FollowColumn:
457  headerAlign = column->hAlignment();
458  break;
459  case HeaderLeft:
460  headerAlign = Qt::AlignLeft;
461  break;
462  case HeaderCenter:
463  headerAlign = Qt::AlignHCenter;
464  break;
465  case HeaderRight:
466  headerAlign = Qt::AlignRight;
467  break;
468  }
469 
470  QgsComposerUtils::drawText( p, cell, column->heading(), mHeaderFont, mHeaderFontColor, headerAlign, Qt::AlignVCenter, textFlag );
471 
472  currentX += mMaxColumnWidthMap[ col ];
473  currentX += mCellMargin;
474  currentX += gridSize;
475  col++;
476  }
477 
478  currentY += cellHeaderHeight;
479  currentY += gridSize;
480  }
481 
482  //now draw the body cells
483  int rowsDrawn = 0;
484  if ( drawContents )
485  {
486  //draw the attribute values
487  for ( int row = rowsToShow.first; row < rowsToShow.second; ++row )
488  {
489  rowsDrawn++;
490  currentX = gridSize;
491  int col = 0;
492 
493  //calculate row height
494  double rowHeight = mMaxRowHeightMap[row + 1] + 2 * mCellMargin;
495 
496 
497  Q_FOREACH ( const QgsComposerTableColumn* column, mColumns )
498  {
499  //draw background
500  p->save();
501  p->setPen( Qt::NoPen );
502  p->setBrush( backgroundColor( row, col ) );
503  p->drawRect( QRectF( currentX, currentY, mMaxColumnWidthMap[col] + 2 * mCellMargin, rowHeight ) );
504  p->restore();
505 
506  // currentY = gridSize;
507  currentX += mCellMargin;
508 
509  QVariant cellContents = mTableContents.at( row ).at( col );
510  QString str = cellContents.toString();
511 
512  Qt::TextFlag textFlag = static_cast< Qt::TextFlag >( 0 );
513  if ( column->width() <= 0 && mWrapBehaviour == TruncateText )
514  {
515  //automatic column width, so we use the Qt::TextDontClip flag when drawing contents, as this works nicer for italicised text
516  //which may slightly exceed the calculated width
517  //if column size was manually set then we do apply text clipping, to avoid painting text outside of columns width
518  textFlag = Qt::TextDontClip;
519  }
520  else if ( textRequiresWrapping( str, column->width(), mContentFont ) )
521  {
522  str = wrappedText( str, column->width(), mContentFont );
523  }
524 
525  cell = QRectF( currentX, currentY, mMaxColumnWidthMap[col], rowHeight );
526  QgsComposerUtils::drawText( p, cell, str, mContentFont, mContentFontColor, column->hAlignment(), column->vAlignment(), textFlag );
527 
528  currentX += mMaxColumnWidthMap[ col ];
529  currentX += mCellMargin;
530  currentX += gridSize;
531  col++;
532  }
533  currentY += rowHeight;
534  currentY += gridSize;
535  }
536  }
537 
538  if ( numberRowsToDraw > rowsDrawn )
539  {
540  p->save();
541  p->setPen( Qt::NoPen );
542 
543  //draw background of empty rows
544  for ( int row = rowsDrawn; row < numberRowsToDraw; ++row )
545  {
546  currentX = gridSize;
547  int col = 0;
548 
549  if ( mergeCells )
550  {
551  p->setBrush( backgroundColor( row + 10000, 0 ) );
552  p->drawRect( QRectF( gridSize, currentY, mTableSize.width() - 2 * gridSize, cellBodyHeight ) );
553  }
554  else
555  {
556  for ( QList<QgsComposerTableColumn*>::const_iterator columnIt = mColumns.constBegin(); columnIt != mColumns.constEnd(); ++columnIt )
557  {
558  //draw background
559 
560  //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
561  p->setBrush( backgroundColor( row + 10000, col ) );
562  p->drawRect( QRectF( currentX, currentY, mMaxColumnWidthMap[col] + 2 * mCellMargin, cellBodyHeight ) );
563 
564  // currentY = gridSize;
565  currentX += mMaxColumnWidthMap[ col ] + 2 * mCellMargin;
566  currentX += gridSize;
567  col++;
568  }
569  }
570  currentY += cellBodyHeight + gridSize;
571  }
572  p->restore();
573  }
574 
575  //and the borders
576  if ( mShowGrid )
577  {
578  QPen gridPen;
579  gridPen.setWidthF( mGridStrokeWidth );
580  gridPen.setColor( mGridColor );
581  gridPen.setJoinStyle( Qt::MiterJoin );
582  p->setPen( gridPen );
583  drawHorizontalGridLines( p, rowsToShow.first, rowsToShow.second + numberEmptyRows, drawHeader );
584  drawVerticalGridLines( p, mMaxColumnWidthMap, rowsToShow.first, rowsToShow.second + numberEmptyRows, drawHeader, mergeCells );
585  }
586 
587  //special case - no records and table is set to ShowMessage mode
588  if ( emptyTable && mEmptyTableMode == QgsComposerTableV2::ShowMessage )
589  {
590  double messageX = gridSize + mCellMargin;
591  double messageY = gridSize + ( drawHeader ? cellHeaderHeight + gridSize : 0 );
592  cell = QRectF( messageX, messageY, mTableSize.width() - messageX, cellBodyHeight );
593  QgsComposerUtils::drawText( p, cell, mEmptyTableMessage, mContentFont, mContentFontColor, Qt::AlignHCenter, Qt::AlignVCenter, static_cast< Qt::TextFlag >( 0 ) );
594  }
595 
596  p->restore();
597 
598 }
599 
600 void QgsComposerTableV2::setCellMargin( const double margin )
601 {
602  if ( qgsDoubleNear( margin, mCellMargin ) )
603  {
604  return;
605  }
606 
607  mCellMargin = margin;
608 
609  //since spacing has changed, we need to recalculate the table size
611 
612  emit changed();
613 }
614 
616 {
617  if ( mode == mEmptyTableMode )
618  {
619  return;
620  }
621 
622  mEmptyTableMode = mode;
623 
624  //since appearance has changed, we need to recalculate the table size
626 
627  emit changed();
628 }
629 
631 {
632  if ( message == mEmptyTableMessage )
633  {
634  return;
635  }
636 
637  mEmptyTableMessage = message;
638 
639  //since message has changed, we need to recalculate the table size
641 
642  emit changed();
643 }
644 
645 void QgsComposerTableV2::setShowEmptyRows( const bool showEmpty )
646 {
647  if ( showEmpty == mShowEmptyRows )
648  {
649  return;
650  }
651 
652  mShowEmptyRows = showEmpty;
653  update();
654  emit changed();
655 }
656 
658 {
659  if ( font == mHeaderFont )
660  {
661  return;
662  }
663 
664  mHeaderFont = font;
665  //since font attributes have changed, we need to recalculate the table size
667 
668  emit changed();
669 }
670 
672 {
673  if ( color == mHeaderFontColor )
674  {
675  return;
676  }
677 
678  mHeaderFontColor = color;
679  update();
680 
681  emit changed();
682 }
683 
685 {
686  if ( alignment == mHeaderHAlignment )
687  {
688  return;
689  }
690 
691  mHeaderHAlignment = alignment;
692  update();
693 
694  emit changed();
695 }
696 
698 {
699  if ( mode == mHeaderMode )
700  {
701  return;
702  }
703 
704  mHeaderMode = mode;
706 
707  emit changed();
708 }
709 
711 {
712  if ( font == mContentFont )
713  {
714  return;
715  }
716 
717  mContentFont = font;
718  //since font attributes have changed, we need to recalculate the table size
720 
721  emit changed();
722 }
723 
725 {
726  if ( color == mContentFontColor )
727  {
728  return;
729  }
730 
731  mContentFontColor = color;
732  update();
733 
734  emit changed();
735 }
736 
738 {
739  if ( showGrid == mShowGrid )
740  {
741  return;
742  }
743 
745  //since grid spacing has changed, we need to recalculate the table size
747 
748  emit changed();
749 }
750 
751 void QgsComposerTableV2::setGridStrokeWidth( const double width )
752 {
753  if ( qgsDoubleNear( width, mGridStrokeWidth ) )
754  {
755  return;
756  }
757 
758  mGridStrokeWidth = width;
759  //since grid spacing has changed, we need to recalculate the table size
761 
762  emit changed();
763 }
764 
766 {
767  if ( color == mGridColor )
768  {
769  return;
770  }
771 
772  mGridColor = color;
773  update();
774 
775  emit changed();
776 }
777 
779 {
780  if ( color == mBackgroundColor )
781  {
782  return;
783  }
784 
785  mBackgroundColor = color;
786  update();
787 
788  emit changed();
789 }
790 
792 {
793  if ( behaviour == mWrapBehaviour )
794  {
795  return;
796  }
797 
798  mWrapBehaviour = behaviour;
800 
801  emit changed();
802 }
803 
805 {
806  //remove existing columns
807  qDeleteAll( mColumns );
808  mColumns.clear();
809 
810  mColumns.append( columns );
811 }
812 
814 {
815  if ( mCellStyles.contains( group ) )
816  delete mCellStyles.take( group );
817 
818  mCellStyles.insert( group, new QgsComposerTableStyle( style ) );
819 }
820 
822 {
823  if ( !mCellStyles.contains( group ) )
824  return nullptr;
825 
826  return mCellStyles.value( group );
827 }
828 
830 {
831  QMap<int, QString> headers;
832 
834  int col = 0;
835  for ( ; columnIt != mColumns.constEnd(); ++columnIt )
836  {
837  headers.insert( col, ( *columnIt )->heading() );
838  col++;
839  }
840  return headers;
841 }
842 
844 {
845  Q_UNUSED( frameIndex );
846  return QSizeF( mTableSize.width(), 0 );
847 }
848 
850 {
851  double height = 0;
852  if (( mHeaderMode == QgsComposerTableV2::FirstFrame && frameIndex < 1 )
854  {
855  //header required, force frame to be high enough for header
857  }
858  return QSizeF( 0, height );
859 }
860 
862 {
866 
867  //get new contents
869  {
870  return;
871  }
872 }
873 
875 {
878 }
879 
880 void QgsComposerTableV2::initStyles()
881 {
891 
892  mCellStyleNames.insert( OddColumns, "oddColumns" );
893  mCellStyleNames.insert( EvenColumns, "evenColumns" );
894  mCellStyleNames.insert( OddRows, "oddRows" );
895  mCellStyleNames.insert( EvenRows, "evenRows" );
896  mCellStyleNames.insert( FirstColumn, "firstColumn" );
897  mCellStyleNames.insert( LastColumn, "lastColumn" );
898  mCellStyleNames.insert( HeaderRow, "headerRow" );
899  mCellStyleNames.insert( FirstRow, "firstRow" );
900  mCellStyleNames.insert( LastRow, "lastRow" );
901 }
902 
904 {
906 
907  //total number of cells (rows + 1 for header)
908  int cols = mColumns.count();
909  int cells = cols * ( mTableContents.count() + 1 );
910  QVector< double > widths( cells );
911 
912  //first, go through all the column headers and calculate the sizes
914  int col = 0;
915  for ( ; columnIt != mColumns.constEnd(); ++columnIt )
916  {
917  if (( *columnIt )->width() > 0 )
918  {
919  //column has manually specified width
920  widths[col] = ( *columnIt )->width();
921  }
923  {
924  widths[col] = QgsComposerUtils::textWidthMM( mHeaderFont, ( *columnIt )->heading() );
925  }
926  else
927  {
928  widths[col] = 0.0;
929  }
930  col++;
931  }
932 
933  //next, go through all the table contents and calculate the sizes
934  QgsComposerTableContents::const_iterator rowIt = mTableContents.constBegin();
935  double currentCellTextWidth;
936  int row = 1;
937  for ( ; rowIt != mTableContents.constEnd(); ++rowIt )
938  {
939  QgsComposerTableRow::const_iterator colIt = rowIt->constBegin();
940  col = 0;
941  for ( ; colIt != rowIt->constEnd(); ++colIt )
942  {
943  if ( mColumns.at( col )->width() <= 0 )
944  {
945  //column width set to automatic, so check content size
946  QStringList multiLineSplit = ( *colIt ).toString().split( '\n' );
947  currentCellTextWidth = 0;
948  Q_FOREACH ( const QString& line, multiLineSplit )
949  {
950  currentCellTextWidth = qMax( currentCellTextWidth, QgsComposerUtils::textWidthMM( mContentFont, line ) );
951  }
952  widths[ row * cols + col ] = currentCellTextWidth;
953  }
954  else
955  {
956  widths[ row * cols + col ] = 0;
957  }
958 
959  col++;
960  }
961  row++;
962  }
963 
964  //calculate maximum
965  for ( int col = 0; col < cols; ++col )
966  {
967  double maxColWidth = 0;
968  for ( int row = 0; row < mTableContents.count() + 1; ++row )
969  {
970  maxColWidth = qMax( widths[ row * cols + col ], maxColWidth );
971  }
972  mMaxColumnWidthMap.insert( col, maxColWidth );
973  }
974 
975  return true;
976 }
977 
979 {
981 
982  //total number of cells (rows + 1 for header)
983  int cols = mColumns.count();
984  int cells = cols * ( mTableContents.count() + 1 );
985  QVector< double > heights( cells );
986 
987  //first, go through all the column headers and calculate the sizes
989  int col = 0;
990  for ( ; columnIt != mColumns.constEnd(); ++columnIt )
991  {
992  //height
993  heights[col] = mHeaderMode != QgsComposerTableV2::NoHeaders ? QgsComposerUtils::textHeightMM( mHeaderFont, ( *columnIt )->heading() ) : 0;
994  col++;
995  }
996 
997  //next, go through all the table contents and calculate the sizes
998  QgsComposerTableContents::const_iterator rowIt = mTableContents.constBegin();
999  int row = 1;
1000  for ( ; rowIt != mTableContents.constEnd(); ++rowIt )
1001  {
1002  QgsComposerTableRow::const_iterator colIt = rowIt->constBegin();
1003  col = 0;
1004  for ( ; colIt != rowIt->constEnd(); ++colIt )
1005  {
1006  if ( textRequiresWrapping(( *colIt ).toString(), mColumns.at( col )->width(), mContentFont ) )
1007  {
1008  //contents too wide for cell, need to wrap
1009  heights[ row * cols + col ] = QgsComposerUtils::textHeightMM( mContentFont, wrappedText(( *colIt ).toString(), mColumns.at( col )->width(), mContentFont ) );
1010  }
1011  else
1012  {
1013  heights[ row * cols + col ] = QgsComposerUtils::textHeightMM( mContentFont, ( *colIt ).toString() );
1014  }
1015 
1016  col++;
1017  }
1018  row++;
1019  }
1020 
1021  //calculate maximum
1022  for ( int row = 0; row < mTableContents.count() + 1; ++row )
1023  {
1024  double maxRowHeight = 0;
1025  for ( int col = 0; col < cols; ++col )
1026  {
1027  maxRowHeight = qMax( heights[ row * cols + col ], maxRowHeight );
1028  }
1029  mMaxRowHeightMap.insert( row, maxRowHeight );
1030  }
1031 
1032  return true;
1033 }
1034 
1036 {
1037  //check how much space each column needs
1038  if ( !calculateMaxColumnWidths() )
1039  {
1040  return 0;
1041  }
1042 
1043  //adapt frame to total width
1044  double totalWidth = 0;
1046  for ( ; maxColWidthIt != mMaxColumnWidthMap.constEnd(); ++maxColWidthIt )
1047  {
1048  totalWidth += maxColWidthIt.value();
1049  }
1050  totalWidth += ( 2 * mMaxColumnWidthMap.size() * mCellMargin );
1051  totalWidth += ( mMaxColumnWidthMap.size() + 1 ) * ( mShowGrid ? mGridStrokeWidth : 0 );
1052 
1053  return totalWidth;
1054 }
1055 
1057 {
1058  //check how much space each row needs
1059  if ( !calculateMaxRowHeights() )
1060  {
1061  return 0;
1062  }
1063 
1064  double height = 0;
1065 
1066  //loop through all existing frames to calculate how many rows are visible in each
1067  //as the entire height of a frame may not be utilised for content rows
1068  int rowsAlreadyShown = 0;
1069  int numberExistingFrames = frameCount();
1070  int rowsVisibleInLastFrame = 0;
1071  double heightOfLastFrame = 0;
1072  for ( int idx = 0; idx < numberExistingFrames; ++idx )
1073  {
1074  bool hasHeader = (( mHeaderMode == QgsComposerTableV2::FirstFrame && idx == 0 )
1076  heightOfLastFrame = frame( idx )->rect().height();
1077  rowsVisibleInLastFrame = rowsVisible( heightOfLastFrame, rowsAlreadyShown, hasHeader, false );
1078  rowsAlreadyShown += rowsVisibleInLastFrame;
1079  height += heightOfLastFrame;
1080  if ( rowsAlreadyShown >= mTableContents.length() )
1081  {
1082  //shown entire contents of table, nothing remaining
1083  return height;
1084  }
1085  }
1086 
1087  //calculate how many rows left to show
1088  int remainingRows = mTableContents.length() - rowsAlreadyShown;
1089 
1090  if ( remainingRows <= 0 )
1091  {
1092  //no remaining rows
1093  return height;
1094  }
1095 
1097  {
1098  heightOfLastFrame = mComposition->paperHeight();
1099  }
1100 
1101  bool hasHeader = (( mHeaderMode == QgsComposerTableV2::FirstFrame && numberExistingFrames < 1 )
1103 
1104  int numberFramesMissing = 0;
1105  while ( remainingRows > 0 )
1106  {
1107  numberFramesMissing++;
1108 
1109  rowsVisibleInLastFrame = rowsVisible( heightOfLastFrame, rowsAlreadyShown, hasHeader, false );
1110  if ( rowsVisibleInLastFrame < 1 )
1111  {
1112  //if no rows are visible in the last frame, calculation of missing frames
1113  //is impossible. So just return total height of existing frames
1114  return height;
1115  }
1116 
1117  rowsAlreadyShown += rowsVisibleInLastFrame;
1118  remainingRows = mTableContents.length() - rowsAlreadyShown;
1119  }
1120 
1121  //rows remain unshown -- how many extra frames would we need to complete the table?
1122  //assume all added frames are same size as final frame
1123  height += heightOfLastFrame * numberFramesMissing;
1124  return height;
1125 }
1126 
1127 void QgsComposerTableV2::drawHorizontalGridLines( QPainter *painter, const int rows, const bool drawHeaderLines ) const
1128 {
1129  //hacky shortcut to maintain 2.10 API without adding code - whooo!
1130  drawHorizontalGridLines( painter, 100000, 100000 + rows, drawHeaderLines );
1131 }
1132 
1133 void QgsComposerTableV2::drawHorizontalGridLines( QPainter *painter, int firstRow, int lastRow, bool drawHeaderLines ) const
1134 {
1135  //horizontal lines
1136  if ( lastRow - firstRow < 1 && !drawHeaderLines )
1137  {
1138  return;
1139  }
1140 
1141  double cellBodyHeight = QgsComposerUtils::fontAscentMM( mContentFont );
1142  double halfGridStrokeWidth = ( mShowGrid ? mGridStrokeWidth : 0 ) / 2.0;
1143  double currentY = 0;
1144  currentY = halfGridStrokeWidth;
1145  if ( drawHeaderLines )
1146  {
1147  painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) );
1148  currentY += ( mShowGrid ? mGridStrokeWidth : 0 );
1149  currentY += ( QgsComposerUtils::fontAscentMM( mHeaderFont ) + 2 * mCellMargin );
1150  }
1151  for ( int row = firstRow; row < lastRow; ++row )
1152  {
1153  painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) );
1154  currentY += ( mShowGrid ? mGridStrokeWidth : 0 );
1155  double rowHeight = row < mTableContents.count() ? mMaxRowHeightMap[row + 1] : cellBodyHeight;
1156  currentY += ( rowHeight + 2 * mCellMargin );
1157  }
1158  painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) );
1159 }
1160 
1161 void QgsComposerTableV2::drawVerticalGridLines( QPainter *painter, const QMap<int, double> &maxWidthMap, const int numberRows, const bool hasHeader, const bool mergeCells ) const
1162 {
1163  //hacky shortcut to maintain 2.10 API without adding code - whooo!
1164  drawVerticalGridLines( painter, maxWidthMap, 100000, 100000 + numberRows, hasHeader, mergeCells );
1165 }
1166 
1167 bool QgsComposerTableV2::textRequiresWrapping( const QString& text, double columnWidth, const QFont &font ) const
1168 {
1169  if ( qgsDoubleNear( columnWidth, 0.0 ) || mWrapBehaviour != WrapText )
1170  return false;
1171 
1172  QStringList multiLineSplit = text.split( '\n' );
1173  double currentTextWidth = 0;
1174  Q_FOREACH ( const QString& line, multiLineSplit )
1175  {
1176  currentTextWidth = qMax( currentTextWidth, QgsComposerUtils::textWidthMM( font, line ) );
1177  }
1178 
1179  return ( currentTextWidth > columnWidth );
1180 }
1181 
1182 QString QgsComposerTableV2::wrappedText( const QString &value, double columnWidth, const QFont &font ) const
1183 {
1184  QStringList lines = value.split( '\n' );
1185  QStringList outLines;
1186  Q_FOREACH ( const QString& line, lines )
1187  {
1188  if ( textRequiresWrapping( line, columnWidth, font ) )
1189  {
1190  //first step is to identify words which must be on their own line (too long to fit)
1191  QStringList words = line.split( ' ' );
1192  QStringList linesToProcess;
1193  QString wordsInCurrentLine;
1194  Q_FOREACH ( const QString& word, words )
1195  {
1196  if ( textRequiresWrapping( word, columnWidth, font ) )
1197  {
1198  //too long to fit
1199  if ( !wordsInCurrentLine.isEmpty() )
1200  linesToProcess << wordsInCurrentLine;
1201  wordsInCurrentLine.clear();
1202  linesToProcess << word;
1203  }
1204  else
1205  {
1206  if ( !wordsInCurrentLine.isEmpty() )
1207  wordsInCurrentLine.append( ' ' );
1208  wordsInCurrentLine.append( word );
1209  }
1210  }
1211  if ( !wordsInCurrentLine.isEmpty() )
1212  linesToProcess << wordsInCurrentLine;
1213 
1214  Q_FOREACH ( const QString& line, linesToProcess )
1215  {
1216  QString remainingText = line;
1217  int lastPos = remainingText.lastIndexOf( ' ' );
1218  while ( lastPos > -1 )
1219  {
1220  if ( !textRequiresWrapping( remainingText.left( lastPos ), columnWidth, font ) )
1221  {
1222  outLines << remainingText.left( lastPos );
1223  remainingText = remainingText.mid( lastPos + 1 );
1224  lastPos = 0;
1225  }
1226  lastPos = remainingText.lastIndexOf( ' ', lastPos - 1 );
1227  }
1228  outLines << remainingText;
1229  }
1230  }
1231  else
1232  {
1233  outLines << line;
1234  }
1235  }
1236 
1237  return outLines.join( "\n" );
1238 }
1239 
1240 QColor QgsComposerTableV2::backgroundColor( int row, int column ) const
1241 {
1242  QColor color = mBackgroundColor;
1243  if ( mCellStyles.value( OddColumns )->enabled && column % 2 == 0 )
1244  color = mCellStyles.value( OddColumns )->cellBackgroundColor;
1245  if ( mCellStyles.value( EvenColumns )->enabled && column % 2 == 1 )
1246  color = mCellStyles.value( EvenColumns )->cellBackgroundColor;
1247  if ( mCellStyles.value( OddRows )->enabled && row % 2 == 0 )
1248  color = mCellStyles.value( OddRows )->cellBackgroundColor;
1249  if ( mCellStyles.value( EvenRows )->enabled && row % 2 == 1 )
1250  color = mCellStyles.value( EvenRows )->cellBackgroundColor;
1251  if ( mCellStyles.value( FirstColumn )->enabled && column == 0 )
1252  color = mCellStyles.value( FirstColumn )->cellBackgroundColor;
1253  if ( mCellStyles.value( LastColumn )->enabled && column == mColumns.count() - 1 )
1254  color = mCellStyles.value( LastColumn )->cellBackgroundColor;
1255  if ( mCellStyles.value( HeaderRow )->enabled && row == -1 )
1256  color = mCellStyles.value( HeaderRow )->cellBackgroundColor;
1257  if ( mCellStyles.value( FirstRow )->enabled && row == 0 )
1258  color = mCellStyles.value( FirstRow )->cellBackgroundColor;
1259  if ( mCellStyles.value( LastRow )->enabled && row == mTableContents.count() - 1 )
1260  color = mCellStyles.value( LastRow )->cellBackgroundColor;
1261 
1262  return color;
1263 }
1264 
1265 void QgsComposerTableV2::drawVerticalGridLines( QPainter *painter, const QMap<int, double> &maxWidthMap, int firstRow, int lastRow, bool hasHeader, bool mergeCells ) const
1266 {
1267  //vertical lines
1268  if ( lastRow - firstRow < 1 && !hasHeader )
1269  {
1270  return;
1271  }
1272 
1273  //calculate height of table within frame
1274  double tableHeight = 0;
1275  if ( hasHeader )
1276  {
1278  }
1279  tableHeight += ( mShowGrid ? mGridStrokeWidth : 0 );
1280  double headerHeight = tableHeight;
1281 
1282  double cellBodyHeight = QgsComposerUtils::fontAscentMM( mContentFont );
1283  for ( int row = firstRow; row < lastRow; ++row )
1284  {
1285  double rowHeight = row < mTableContents.count() ? mMaxRowHeightMap[row + 1] : cellBodyHeight;
1286  tableHeight += rowHeight + ( mShowGrid ? mGridStrokeWidth : 0 ) + mCellMargin * 2;
1287  }
1288 
1289  double halfGridStrokeWidth = ( mShowGrid ? mGridStrokeWidth : 0 ) / 2.0;
1290  double currentX = halfGridStrokeWidth;
1291  painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, tableHeight - halfGridStrokeWidth ) );
1292  currentX += ( mShowGrid ? mGridStrokeWidth : 0 );
1293  QMap<int, double>::const_iterator maxColWidthIt = maxWidthMap.constBegin();
1294  int col = 1;
1295  for ( ; maxColWidthIt != maxWidthMap.constEnd(); ++maxColWidthIt )
1296  {
1297  currentX += ( maxColWidthIt.value() + 2 * mCellMargin );
1298  if ( col == maxWidthMap.size() || !mergeCells )
1299  {
1300  painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, tableHeight - halfGridStrokeWidth ) );
1301  }
1302  else if ( hasHeader )
1303  {
1304  painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, headerHeight - halfGridStrokeWidth ) );
1305  }
1306 
1307  currentX += ( mShowGrid ? mGridStrokeWidth : 0 );
1308  col++;
1309  }
1310 }
1311 
1313 {
1315 
1316  //force recalculation of frame rects, so that they are set to the correct
1317  //fixed and minimum frame sizes
1319 }
1320 
1322 {
1323  if ( contents.indexOf( row ) >= 0 )
1324  {
1325  return true;
1326  }
1327  else
1328  {
1329  return false;
1330  }
1331 }
1332 
QColor mContentFontColor
Table contents font color.
QFont mContentFont
Table contents font.
double totalWidth()
Returns total width of table contents.
virtual void recalculateFrameSizes()
Recalculates the portion of the multiframe item which is shown in each of it&#39;s component frames...
void clear()
QDomNodeList elementsByTagName(const QString &tagname) const
virtual QSizeF minFrameSize(const int frameIndex=-1) const override
Returns the minimum size for a frames, if desired.
WrapBehaviour mWrapBehaviour
void setShowGrid(const bool showGrid)
Sets whether grid lines should be drawn in the table.
QString & append(QChar ch)
bool mShowGrid
True if grid should be shown.
bool showGrid() const
Returns whether grid lines are drawn in the table.
void setGridStrokeWidth(const double width)
Sets the width for grid lines in the table.
bool contains(const Key &key) const
double mGridStrokeWidth
Width of grid lines.
void setRenderHint(RenderHint hint, bool on)
QDomNode appendChild(const QDomNode &newChild)
void setGridColor(const QColor &color)
Sets color used for grid lines in the table.
Qt::AlignmentFlag hAlignment() const
Returns the horizontal alignment for a column, which controls the alignment used for drawing column v...
Qt::AlignmentFlag vAlignment() const
Returns the vertical alignment for a column, which controls the alignment used for drawing column val...
QString attribute(const QString &name, const QString &defValue) const
int length() const
double totalHeight()
Returns total height of table contents.
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
HeaderMode
Controls where headers are shown in the table.
static QString encodeColor(const QColor &color)
void setEmptyTableMessage(const QString &message)
Sets the message for empty tables with no content rows.
const QgsComposerTableStyle * cellStyle(CellStyleGroup group) const
Returns the cell style for a cell group.
const_iterator constBegin() const
const T & at(int i) const
A item that forms part of a map composition.
virtual bool writeXML(QDomElement &elem, QDomDocument &doc, bool ignoreFrames=false) const override
Stores state information about multiframe in DOM element.
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:515
QMap< CellStyleGroup, QgsComposerTableStyle *> mCellStyles
static void drawText(QPainter *painter, QPointF pos, const QString &text, const QFont &font, const QColor &color=QColor())
Draws text on a painter at a specific position, taking care of composer specific issues (calculation ...
void save()
QFont mHeaderFont
Header font.
void setEmptyTableBehaviour(const EmptyTableMode mode)
Sets the behaviour for empty tables with no content rows.
void setJoinStyle(Qt::PenJoinStyle style)
static double fontAscentMM(const QFont &font)
Calculate font ascent in millimeters, including workarounds for QT font rendering issues...
QString join(const QString &separator) const
virtual void render(QPainter *p, const QRectF &renderExtent, const int frameIndex) override
Renders a portion of the multiframe&#39;s content into a painter.
void setShowEmptyRows(const bool showEmpty)
Sets whether empty rows should be drawn.
void drawLine(const QLineF &line)
void recalculateTableSize()
Recalculates and updates the size of the table and all table frames.
void clear()
double toDouble(bool *ok) const
EmptyTableMode mEmptyTableMode
Behaviour for empty tables.
QColor mHeaderFontColor
Header font color.
QString tr(const char *sourceText, const char *disambiguation, int n)
double mCellMargin
Margin between cell borders and cell text.
EmptyTableMode
Controls how empty tables are displayed.
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:353
virtual QMap< int, QString > headerLabels() const
Returns the text used in the column headers for the table.
int lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
QgsComposition::PlotStyle plotStyle() const
HeaderMode mHeaderMode
Header display mode.
int indexOf(const T &value, int from) const
bool enabled
Whether the styling option is enabled.
void clear()
QDomElement toElement() const
bool _readXML(const QDomElement &itemElem, const QDomDocument &doc, bool ignoreFrames=false)
Restores state information about base multiframe object from a DOM element.
bool isEmpty() const
void drawRect(const QRectF &rectangle)
void drawVerticalGridLines(QPainter *painter, const QMap< int, double > &maxWidthMap, int firstRow, int lastRow, bool hasHeader, bool mergeCells=false) const
Draws the vertical grid lines for the table.
int rowsVisible(double frameHeight, int firstRow, bool includeHeader, bool includeEmptyRows) const
Calculates how many content rows would be visible within a frame of the specified height...
void setContentFontColor(const QColor &color)
Sets the color used to draw text in table body cells.
QString number(int n, int base)
QRectF extent() const
Returns the visible portion of the multi frame&#39;s content which is shown in this frame.
int count(const T &value) const
void append(const T &value)
bool contentsContainsRow(const QgsComposerTableContents &contents, const QgsComposerTableRow &row) const
Checks whether a table contents contains a given row.
bool mShowEmptyRows
True if empty rows should be shown in the table.
void recalculateFrameRects()
Forces a recalculation of all the associated frame&#39;s scene rectangles.
bool fromString(const QString &descrip)
virtual QSizeF fixedFrameSize(const int frameIndex=-1) const override
Returns the fixed size for a frame, if desired.
void setPen(const QColor &color)
void setAttribute(const QString &name, const QString &value)
int toInt(bool *ok, int base) const
Abstract base class for composer items with the ability to distribute the content to several frames (...
bool isEmpty() const
const_iterator constEnd() const
HeaderHAlignment mHeaderHAlignment
Alignment for table headers.
WrapBehaviour
Controls how long strings in the table are handled.
void setWidthF(qreal width)
void setBrush(const QBrush &brush)
void recalculateFrameSizes() override
QMap< int, double > mMaxColumnWidthMap
Map of maximum width for each column.
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.
Stores properties of a column in a QgsComposerTable.
void setColor(const QColor &color)
double width() const
Returns the width for a column.
virtual bool readXML(const QDomElement &itemElem, const QDomDocument &doc, bool ignoreFrames=false) override
Reads multiframe state information from a DOM element.
virtual bool calculateMaxColumnWidths()
Calculates the maximum width of text shown in columns.
Graphics scene for map printing.
void setHeaderMode(const HeaderMode mode)
Sets the display mode for headers in the table.
void setBackgroundColor(const QColor &color)
Sets color used for background of table.
QColor mGridColor
Color for grid lines.
void setHeaderHAlignment(const HeaderHAlignment alignment)
Sets the horizontal alignment for table headers.
QString heading() const
Returns the heading for a column, which is the value displayed in the columns header cell...
HeaderHAlignment
Controls how headers are horizontally aligned in a table.
QgsComposerTableContents mTableContents
Contents to show in table.
bool isNull() const
QString mEmptyTableMessage
String to show in empty tables.
bool readXML(const QDomElement &styleElem)
Reads the style&#39;s properties from XML.
void restore()
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:516
const Key key(const T &value) const
static double textWidthMM(const QFont &font, const QString &text)
Calculate font width in millimeters for a string, including workarounds for QT font rendering issues...
QgsComposition * mComposition
void deleteFrames()
Removes and deletes all child frames.
QgsComposerTableColumns mColumns
Columns to show in table.
QVariant value(const QString &key, const QVariant &defaultValue) const
CellStyleGroup
Row or column groups for cell styling.
bool writeXML(QDomElement &styleElem, QDomDocument &doc) const
Writes the style&#39;s properties to XML for storage.
void setHeaderFontColor(const QColor &color)
Sets the color used to draw header text in the table.
void setHeaderFont(const QFont &font)
Sets the font used to draw header text in the table.
void setCellStyle(CellStyleGroup group, const QgsComposerTableStyle &style)
Sets the cell style for a cell group.
QString mid(int position, int n) const
QString toString() const
int frameCount() const
Returns the number of frames associated with this multiframe.
virtual void refreshAttributes()
Refreshes the contents shown in the table by querying for new data.
void setFamily(const QString &family)
void setColumns(const QgsComposerTableColumns &columns)
Replaces the columns in the table with a specified list of QgsComposerTableColumns.
static double textHeightMM(const QFont &font, const QString &text, double multiLineHeight=1.0)
Calculate font height in millimeters for a string, including workarounds for QT font rendering issues...
QStringList split(const QString &sep, const QString &str, bool allowEmptyEntries)
QgsComposerTableColumns * columns()
Returns a pointer to the list of QgsComposerTableColumns shown in the table.
QString left(int n) const
void setCellMargin(const double margin)
Sets the margin distance between cell borders and their contents.
int frameIndex(QgsComposerFrame *frame) const
Returns the index of a frame within the multiframe.
double paperHeight() const
Height of paper item.
virtual bool getTableContents(QgsComposerTableContents &contents)=0
Fetches the contents used for the cells in the table.
qreal height() const
static QColor decodeColor(const QString &str)
QColor backgroundColor() const
Returns the color used for the background of the table.
iterator insert(const Key &key, const T &value)
QPair< int, int > rowRange(const int frameIndex) const
Calculates a range of rows which should be visible in a given frame.
virtual bool readXML(const QDomElement &columnElem)
Reads the column&#39;s properties from xml.
void update()
Forces a redraw of all child frames.
static QDomElement toXmlElement(const QFont &font, QDomDocument &document, const QString &elementName)
Returns a DOM element containing the properties of the font.
int size() const
const_iterator constEnd() const
QDomElement createElement(const QString &tagName)
void setContentFont(const QFont &font)
Sets the font used to draw text in table body cells.
const_iterator constBegin() const
Styling option for a composer table cell.
QColor mBackgroundColor
Color for table background.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QgsComposerTableContents * contents()
Returns the current contents of the table.
QgsComposerFrame * frame(int i) const
Returns a child frame from the multiframe.
QString toString() const
void handleFrameRemoval(QgsComposerItem *item)
Called before a frame is going to be removed.
void drawHorizontalGridLines(QPainter *painter, int firstRow, int lastRow, bool drawHeaderLines) const
Draws the horizontal grid lines for the table.
T take(const Key &key)
int size() const
virtual QSizeF totalSize() const override
Returns the total size of the multiframe&#39;s content.
qreal width() const
void setWrapBehaviour(WrapBehaviour behaviour)
Sets the wrap behaviour for the table, which controls how text within cells is automatically wrapped...
bool _writeXML(QDomElement &elem, QDomDocument &doc, bool ignoreFrames=false) const
Stores state information about base multiframe object in DOM element.
QMap< int, double > mMaxRowHeightMap
Map of maximum height for each row.
virtual bool calculateMaxRowHeights()
Calculates the maximum height of text shown in rows.
void changed()
Emitted when the properties of a multi frame have changed, and the GUI item widget must be updated...
QDomNode at(int index) const
QRectF rect() const
const T value(const Key &key) const
QColor cellBackgroundColor
Cell background color.