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