QGIS API Documentation  2.8.6-Wien
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 
24 QgsComposerTableV2::QgsComposerTableV2( QgsComposition *composition, bool createUndoCommands )
25  : QgsComposerMultiFrame( composition, createUndoCommands )
26  , mCellMargin( 1.0 )
27  , mEmptyTableMode( HeadersOnly )
28  , mShowEmptyRows( false )
29  , mHeaderFontColor( Qt::black )
30  , mHeaderHAlignment( FollowColumn )
31  , mHeaderMode( FirstFrame )
32  , mContentFontColor( Qt::black )
33  , mShowGrid( true )
34  , mGridStrokeWidth( 0.5 )
35  , mGridColor( Qt::black )
36  , mBackgroundColor( Qt::white )
37 {
38 
39  if ( mComposition )
40  {
41  QObject::connect( mComposition, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SLOT( handleFrameRemoval( QgsComposerItem* ) ) );
42  }
43 
44  //get default composer font from settings
45  QSettings settings;
46  QString defaultFontString = settings.value( "/Composer/defaultFont" ).toString();
47  if ( !defaultFontString.isEmpty() )
48  {
49  mHeaderFont.setFamily( defaultFontString );
50  mContentFont.setFamily( defaultFontString );
51  }
52 }
53 
55  : QgsComposerMultiFrame( 0, false )
56  , mCellMargin( 1.0 )
58  , mShowEmptyRows( false )
59  , mHeaderFontColor( Qt::black )
62  , mContentFontColor( Qt::black )
63  , mShowGrid( true )
64  , mGridStrokeWidth( 0.5 )
65  , mGridColor( Qt::black )
66  , mBackgroundColor( Qt::white )
67 {
68 
69 }
70 
72 {
73  qDeleteAll( mColumns );
74  mColumns.clear();
75 }
76 
77 bool QgsComposerTableV2::writeXML( QDomElement& elem, QDomDocument & doc, bool ignoreFrames ) const
78 {
79  elem.setAttribute( "cellMargin", QString::number( mCellMargin ) );
80  elem.setAttribute( "emptyTableMode", QString::number(( int )mEmptyTableMode ) );
81  elem.setAttribute( "emptyTableMessage", mEmptyTableMessage );
82  elem.setAttribute( "showEmptyRows", mShowEmptyRows );
83  elem.setAttribute( "headerFont", mHeaderFont.toString() );
84  elem.setAttribute( "headerFontColor", QgsSymbolLayerV2Utils::encodeColor( mHeaderFontColor ) );
85  elem.setAttribute( "headerHAlignment", QString::number(( int )mHeaderHAlignment ) );
86  elem.setAttribute( "headerMode", QString::number(( int )mHeaderMode ) );
87  elem.setAttribute( "contentFont", mContentFont.toString() );
88  elem.setAttribute( "contentFontColor", QgsSymbolLayerV2Utils::encodeColor( mContentFontColor ) );
89  elem.setAttribute( "gridStrokeWidth", QString::number( mGridStrokeWidth ) );
90  elem.setAttribute( "gridColor", QgsSymbolLayerV2Utils::encodeColor( mGridColor ) );
91  elem.setAttribute( "showGrid", mShowGrid );
92  elem.setAttribute( "backgroundColor", QgsSymbolLayerV2Utils::encodeColor( mBackgroundColor ) );
93 
94  //columns
95  QDomElement displayColumnsElem = doc.createElement( "displayColumns" );
96  QList<QgsComposerTableColumn*>::const_iterator columnIt = mColumns.constBegin();
97  for ( ; columnIt != mColumns.constEnd(); ++columnIt )
98  {
99  QDomElement columnElem = doc.createElement( "column" );
100  ( *columnIt )->writeXML( columnElem, doc );
101  displayColumnsElem.appendChild( columnElem );
102  }
103  elem.appendChild( displayColumnsElem );
104 
105  bool state = _writeXML( elem, doc, ignoreFrames );
106  return state;
107 }
108 
109 bool QgsComposerTableV2::readXML( const QDomElement &itemElem, const QDomDocument &doc, bool ignoreFrames )
110 {
111  deleteFrames();
112 
113  //first create the frames
114  if ( !_readXML( itemElem, doc, ignoreFrames ) )
115  {
116  return false;
117  }
118 
119  if ( itemElem.isNull() )
120  {
121  return false;
122  }
123 
124  mEmptyTableMode = QgsComposerTableV2::EmptyTableMode( itemElem.attribute( "emptyTableMode", "0" ).toInt() );
125  mEmptyTableMessage = itemElem.attribute( "emptyTableMessage", tr( "No matching records" ) );
126  mShowEmptyRows = itemElem.attribute( "showEmptyRows", "0" ).toInt();
127  mHeaderFont.fromString( itemElem.attribute( "headerFont", "" ) );
128  mHeaderFontColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "headerFontColor", "0,0,0,255" ) );
129  mHeaderHAlignment = QgsComposerTableV2::HeaderHAlignment( itemElem.attribute( "headerHAlignment", "0" ).toInt() );
130  mHeaderMode = QgsComposerTableV2::HeaderMode( itemElem.attribute( "headerMode", "0" ).toInt() );
131  mContentFont.fromString( itemElem.attribute( "contentFont", "" ) );
132  mContentFontColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "contentFontColor", "0,0,0,255" ) );
133  mCellMargin = itemElem.attribute( "cellMargin", "1.0" ).toDouble();
134  mGridStrokeWidth = itemElem.attribute( "gridStrokeWidth", "0.5" ).toDouble();
135  mShowGrid = itemElem.attribute( "showGrid", "1" ).toInt();
136  mGridColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "gridColor", "0,0,0,255" ) );
137  mBackgroundColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "backgroundColor", "255,255,255,0" ) );
138 
139  //restore column specifications
140  qDeleteAll( mColumns );
141  mColumns.clear();
142  QDomNodeList columnsList = itemElem.elementsByTagName( "displayColumns" );
143  if ( columnsList.size() > 0 )
144  {
145  QDomElement columnsElem = columnsList.at( 0 ).toElement();
146  QDomNodeList columnEntryList = columnsElem.elementsByTagName( "column" );
147  for ( int i = 0; i < columnEntryList.size(); ++i )
148  {
149  QDomElement columnElem = columnEntryList.at( i ).toElement();
151  column->readXML( columnElem );
152  mColumns.append( column );
153  }
154  }
155 
156  return true;
157 }
158 
160 {
161  return mTableSize;
162 }
163 
165 {
166  //get frame extent
167  if ( frameIndex >= frameCount() )
168  {
169  return 0;
170  }
171  QRectF frameExtent = frame( frameIndex )->extent();
172 
173  bool includeHeader = false;
174  if (( mHeaderMode == QgsComposerTableV2::FirstFrame && frameIndex < 1 )
176  {
177  includeHeader = true;
178  }
179  return rowsVisible( frameExtent.height(), includeHeader );
180 }
181 
182 int QgsComposerTableV2::rowsVisible( const double frameHeight, const bool includeHeader ) const
183 {
184  //calculate header height
185  double headerHeight = 0;
186  if ( includeHeader )
187  {
188  //frame has a header
189  headerHeight = 2 * ( mShowGrid ? mGridStrokeWidth : 0 ) + 2 * mCellMargin + QgsComposerUtils::fontAscentMM( mHeaderFont );
190  }
191  else
192  {
193  //frame has no header text, just the stroke
194  headerHeight = ( mShowGrid ? mGridStrokeWidth : 0 );
195  }
196 
197  //remaining height available for content rows
198  double contentHeight = frameHeight - headerHeight;
199 
200  //calculate number of visible rows
201  double rowHeight = ( mShowGrid ? mGridStrokeWidth : 0 ) + 2 * mCellMargin + QgsComposerUtils::fontAscentMM( mContentFont );
202  return qMax( floor( contentHeight / rowHeight ), 0.0 );
203 }
204 
205 QPair< int, int > QgsComposerTableV2::rowRange( const QRectF &extent, const int frameIndex ) const
206 {
207  //calculate row height
208  if ( frameIndex >= frameCount() )
209  {
210  //bad frame index
211  return qMakePair( 0, 0 );
212  }
213 
214  //loop through all previous frames to calculate how many rows are visible in each
215  //as the entire height of a frame may not be utilised for content rows
216  int rowsAlreadyShown = 0;
217  for ( int idx = 0; idx < frameIndex; ++idx )
218  {
219  rowsAlreadyShown += rowsVisible( idx );
220  }
221 
222  double headerHeight = 0;
223  if (( mHeaderMode == QgsComposerTableV2::FirstFrame && frameIndex < 1 )
225  {
226  //frame has a header
227  headerHeight = 2 * ( mShowGrid ? mGridStrokeWidth : 0 ) + 2 * mCellMargin + QgsComposerUtils::fontAscentMM( mHeaderFont );
228  }
229  else
230  {
231  headerHeight = ( mShowGrid ? mGridStrokeWidth : 0 );
232  }
233 
234  //remaining height available for content rows
235  double contentHeight = extent.height() - headerHeight;
236  double rowHeight = ( mShowGrid ? mGridStrokeWidth : 0 ) + 2 * mCellMargin + QgsComposerUtils::fontAscentMM( mContentFont );
237 
238  //using zero based indexes
239  int firstVisible = qMin( rowsAlreadyShown, mTableContents.length() );
240  int rowsVisible = qMax( floor( contentHeight / rowHeight ), 0.0 );
241  int lastVisible = qMin( firstVisible + rowsVisible, mTableContents.length() );
242 
243  return qMakePair( firstVisible, lastVisible );
244 }
245 
246 
247 void QgsComposerTableV2::render( QPainter *p, const QRectF &renderExtent, const int frameIndex )
248 {
249  if ( !p )
250  {
251  return;
252  }
253 
254  bool emptyTable = mTableContents.length() == 0;
255  if ( emptyTable && mEmptyTableMode == QgsComposerTableV2::HideTable )
256  {
257  //empty table set to hide table mode, so don't draw anything
258  return;
259  }
260 
261  //calculate which rows to show in this frame
262  QPair< int, int > rowsToShow = rowRange( renderExtent, frameIndex );
263 
266  {
267  //exporting composition, so force an attribute refresh
268  //we do this in case vector layer has changed via an external source (eg, another database user)
270  }
271 
272  double gridSize = mShowGrid ? mGridStrokeWidth : 0;
273  QList<QgsComposerTableColumn*>::const_iterator columnIt = mColumns.constBegin();
274 
275  int col = 0;
276  double cellHeaderHeight = QgsComposerUtils::fontAscentMM( mHeaderFont ) + 2 * mCellMargin;
277  double cellBodyHeight = QgsComposerUtils::fontAscentMM( mContentFont ) + 2 * mCellMargin;
278  QRectF cell;
279 
280  //calculate whether a header is required
281  bool drawHeader = (( mHeaderMode == QgsComposerTableV2::FirstFrame && frameIndex < 1 )
283  //calculate whether drawing table contents is required
284  bool drawContents = !( emptyTable && mEmptyTableMode == QgsComposerTableV2::ShowMessage );
285 
286  int numberRowsToDraw = rowsToShow.second - rowsToShow.first;
287  if ( drawContents && mShowEmptyRows )
288  {
289  numberRowsToDraw = rowsVisible( frameIndex );
290  }
291  bool mergeCells = false;
292  if ( emptyTable && mEmptyTableMode == QgsComposerTableV2::ShowMessage )
293  {
294  //draw a merged row for the empty table message
295  numberRowsToDraw++;
296  mergeCells = true;
297  }
298 
299  p->save();
300  //antialiasing on
301  p->setRenderHint( QPainter::Antialiasing, true );
302 
303  //draw table background
304  if ( mBackgroundColor.alpha() > 0 )
305  {
306  p->save();
307  p->setPen( Qt::NoPen );
308  p->setBrush( QBrush( mBackgroundColor ) );
309  double totalHeight = ( drawHeader || ( numberRowsToDraw > 0 ) ? gridSize : 0 ) +
310  ( drawHeader ? cellHeaderHeight + gridSize : 0.0 ) +
311  ( drawContents ? numberRowsToDraw : 1 ) * ( cellBodyHeight + gridSize );
312 
313  if ( totalHeight > 0 )
314  {
315  QRectF backgroundRect( 0, 0, mTableSize.width(), totalHeight );
316  p->drawRect( backgroundRect );
317  }
318  p->restore();
319  }
320 
321  //now draw the text
322  double currentX = gridSize;
323  double currentY;
324  p->setPen( Qt::SolidLine );
325  for ( ; columnIt != mColumns.constEnd(); ++columnIt )
326  {
327  currentY = gridSize;
328  currentX += mCellMargin;
329 
330  Qt::TextFlag textFlag = ( Qt::TextFlag )0;
331  if (( *columnIt )->width() <= 0 )
332  {
333  //automatic column width, so we use the Qt::TextDontClip flag when drawing contents, as this works nicer for italicised text
334  //which may slightly exceed the calculated width
335  //if column size was manually set then we do apply text clipping, to avoid painting text outside of columns width
336  textFlag = Qt::TextDontClip;
337  }
338 
339  if ( drawHeader )
340  {
341  //draw the header
342  cell = QRectF( currentX, currentY, mMaxColumnWidthMap[col], cellHeaderHeight );
343 
344  //calculate alignment of header
345  Qt::AlignmentFlag headerAlign = Qt::AlignLeft;
346  switch ( mHeaderHAlignment )
347  {
348  case FollowColumn:
349  headerAlign = ( *columnIt )->hAlignment();
350  break;
351  case HeaderLeft:
352  headerAlign = Qt::AlignLeft;
353  break;
354  case HeaderCenter:
355  headerAlign = Qt::AlignHCenter;
356  break;
357  case HeaderRight:
358  headerAlign = Qt::AlignRight;
359  break;
360  }
361 
362  QgsComposerUtils::drawText( p, cell, ( *columnIt )->heading(), mHeaderFont, mHeaderFontColor, headerAlign, Qt::AlignVCenter, textFlag );
363 
364  currentY += cellHeaderHeight;
365  currentY += gridSize;
366  }
367 
368  if ( drawContents )
369  {
370  //draw the attribute values
371  for ( int row = rowsToShow.first; row < rowsToShow.second; ++row )
372  {
373  cell = QRectF( currentX, currentY, mMaxColumnWidthMap[col], cellBodyHeight );
374 
375  QVariant cellContents = mTableContents.at( row ).at( col );
376  QString str = cellContents.toString();
377 
378  QgsComposerUtils::drawText( p, cell, str, mContentFont, mContentFontColor, ( *columnIt )->hAlignment(), Qt::AlignVCenter, textFlag );
379 
380  currentY += cellBodyHeight;
381  currentY += gridSize;
382  }
383  }
384 
385  currentX += mMaxColumnWidthMap[ col ];
386  currentX += mCellMargin;
387  currentX += gridSize;
388  col++;
389  }
390 
391  //and the borders
392  if ( mShowGrid )
393  {
394  QPen gridPen;
395  gridPen.setWidthF( mGridStrokeWidth );
396  gridPen.setColor( mGridColor );
397  gridPen.setJoinStyle( Qt::MiterJoin );
398  p->setPen( gridPen );
399  drawHorizontalGridLines( p, numberRowsToDraw, drawHeader );
400  drawVerticalGridLines( p, mMaxColumnWidthMap, numberRowsToDraw, drawHeader, mergeCells );
401  }
402 
403  //special case - no records and table is set to ShowMessage mode
404  if ( emptyTable && mEmptyTableMode == QgsComposerTableV2::ShowMessage )
405  {
406  double messageX = gridSize + mCellMargin;
407  double messageY = gridSize + ( drawHeader ? cellHeaderHeight + gridSize : 0 );
408  cell = QRectF( messageX, messageY, mTableSize.width() - messageX, cellBodyHeight );
409  QgsComposerUtils::drawText( p, cell, mEmptyTableMessage, mContentFont, mContentFontColor, Qt::AlignHCenter, Qt::AlignVCenter, ( Qt::TextFlag )0 );
410  }
411 
412  p->restore();
413 
414 }
415 
416 void QgsComposerTableV2::setCellMargin( const double margin )
417 {
418  if ( margin == mCellMargin )
419  {
420  return;
421  }
422 
423  mCellMargin = margin;
424 
425  //since spacing has changed, we need to recalculate the table size
427 
428  emit changed();
429 }
430 
432 {
433  if ( mode == mEmptyTableMode )
434  {
435  return;
436  }
437 
438  mEmptyTableMode = mode;
439 
440  //since appearance has changed, we need to recalculate the table size
442 
443  emit changed();
444 }
445 
446 void QgsComposerTableV2::setEmptyTableMessage( const QString message )
447 {
448  if ( message == mEmptyTableMessage )
449  {
450  return;
451  }
452 
453  mEmptyTableMessage = message;
454 
455  //since message has changed, we need to recalculate the table size
457 
458  emit changed();
459 }
460 
461 void QgsComposerTableV2::setShowEmptyRows( const bool showEmpty )
462 {
463  if ( showEmpty == mShowEmptyRows )
464  {
465  return;
466  }
467 
468  mShowEmptyRows = showEmpty;
469  update();
470  emit changed();
471 }
472 
473 void QgsComposerTableV2::setHeaderFont( const QFont &font )
474 {
475  if ( font == mHeaderFont )
476  {
477  return;
478  }
479 
480  mHeaderFont = font;
481  //since font attributes have changed, we need to recalculate the table size
483 
484  emit changed();
485 }
486 
487 void QgsComposerTableV2::setHeaderFontColor( const QColor &color )
488 {
489  if ( color == mHeaderFontColor )
490  {
491  return;
492  }
493 
494  mHeaderFontColor = color;
495  update();
496 
497  emit changed();
498 }
499 
501 {
502  if ( alignment == mHeaderHAlignment )
503  {
504  return;
505  }
506 
507  mHeaderHAlignment = alignment;
508  update();
509 
510  emit changed();
511 }
512 
514 {
515  if ( mode == mHeaderMode )
516  {
517  return;
518  }
519 
520  mHeaderMode = mode;
522 
523  emit changed();
524 }
525 
526 void QgsComposerTableV2::setContentFont( const QFont &font )
527 {
528  if ( font == mContentFont )
529  {
530  return;
531  }
532 
533  mContentFont = font;
534  //since font attributes have changed, we need to recalculate the table size
536 
537  emit changed();
538 }
539 
540 void QgsComposerTableV2::setContentFontColor( const QColor &color )
541 {
542  if ( color == mContentFontColor )
543  {
544  return;
545  }
546 
547  mContentFontColor = color;
548  update();
549 
550  emit changed();
551 }
552 
554 {
555  if ( showGrid == mShowGrid )
556  {
557  return;
558  }
559 
561  //since grid spacing has changed, we need to recalculate the table size
563 
564  emit changed();
565 }
566 
567 void QgsComposerTableV2::setGridStrokeWidth( const double width )
568 {
569  if ( width == mGridStrokeWidth )
570  {
571  return;
572  }
573 
574  mGridStrokeWidth = width;
575  //since grid spacing has changed, we need to recalculate the table size
577 
578  emit changed();
579 }
580 
581 void QgsComposerTableV2::setGridColor( const QColor &color )
582 {
583  if ( color == mGridColor )
584  {
585  return;
586  }
587 
588  mGridColor = color;
589  update();
590 
591  emit changed();
592 }
593 
594 void QgsComposerTableV2::setBackgroundColor( const QColor &color )
595 {
596  if ( color == mBackgroundColor )
597  {
598  return;
599  }
600 
601  mBackgroundColor = color;
602  update();
603 
604  emit changed();
605 }
606 
608 {
609  //remove existing columns
610  qDeleteAll( mColumns );
611  mColumns.clear();
612 
613  mColumns.append( columns );
614 }
615 
616 QMap<int, QString> QgsComposerTableV2::headerLabels() const
617 {
618  QMap<int, QString> headers;
619 
620  QgsComposerTableColumns::const_iterator columnIt = mColumns.constBegin();
621  int col = 0;
622  for ( ; columnIt != mColumns.constEnd(); ++columnIt )
623  {
624  headers.insert( col, ( *columnIt )->heading() );
625  col++;
626  }
627  return headers;
628 }
629 
631 {
632  Q_UNUSED( frameIndex );
633  return QSizeF( mTableSize.width(), 0 );
634 }
635 
637 {
638  double height = 0;
639  if (( mHeaderMode == QgsComposerTableV2::FirstFrame && frameIndex < 1 )
641  {
642  //header required, force frame to be high enough for header
644  }
645  return QSizeF( 0, height );
646 }
647 
649 {
650  mMaxColumnWidthMap.clear();
651  mTableContents.clear();
652 
653  //get new contents
655  {
656  return;
657  }
658 }
659 
661 {
662  mTableSize = QSizeF( totalWidth(), totalHeight() );
664 }
665 
667 {
668  mMaxColumnWidthMap.clear();
669 
670  //first, go through all the column headers and calculate the max width values
671  QgsComposerTableColumns::const_iterator columnIt = mColumns.constBegin();
672  int col = 0;
673  for ( ; columnIt != mColumns.constEnd(); ++columnIt )
674  {
675  double width = 0;
676  if (( *columnIt )->width() > 0 )
677  {
678  //column has manually specified width
679  width = ( *columnIt )->width();
680  }
682  {
683  width = QgsComposerUtils::textWidthMM( mHeaderFont, ( *columnIt )->heading() );
684  }
685 
686  mMaxColumnWidthMap.insert( col, width );
687  col++;
688  }
689 
690  //next, go through all the table contents and calculate the max width values
691  QgsComposerTableContents::const_iterator rowIt = mTableContents.constBegin();
692  double currentCellTextWidth;
693  for ( ; rowIt != mTableContents.constEnd(); ++rowIt )
694  {
695  QgsComposerTableRow::const_iterator colIt = rowIt->constBegin();
696  int columnNumber = 0;
697  for ( ; colIt != rowIt->constEnd(); ++colIt )
698  {
699  if ( mColumns.at( columnNumber )->width() <= 0 )
700  {
701  //column width set to automatic, so check content size
702  currentCellTextWidth = QgsComposerUtils::textWidthMM( mContentFont, ( *colIt ).toString() );
703  mMaxColumnWidthMap[ columnNumber ] = qMax( currentCellTextWidth, mMaxColumnWidthMap[ columnNumber ] );
704  }
705  columnNumber++;
706  }
707  }
708 
709  return true;
710 }
711 
713 {
714  //check how much space each column needs
715  if ( !calculateMaxColumnWidths() )
716  {
717  return 0;
718  }
719 
720  //adapt frame to total width
721  double totalWidth = 0;
722  QMap<int, double>::const_iterator maxColWidthIt = mMaxColumnWidthMap.constBegin();
723  for ( ; maxColWidthIt != mMaxColumnWidthMap.constEnd(); ++maxColWidthIt )
724  {
725  totalWidth += maxColWidthIt.value();
726  }
727  totalWidth += ( 2 * mMaxColumnWidthMap.size() * mCellMargin );
728  totalWidth += ( mMaxColumnWidthMap.size() + 1 ) * ( mShowGrid ? mGridStrokeWidth : 0 );
729 
730  return totalWidth;
731 }
732 
734 {
735  double height = 0;
736 
737  //loop through all existing frames to calculate how many rows are visible in each
738  //as the entire height of a frame may not be utilised for content rows
739  int rowsAlreadyShown = 0;
740  int numberExistingFrames = frameCount();
741  int rowsVisibleInLastFrame = 0;
742  double heightOfLastFrame = 0;
743  for ( int idx = 0; idx < numberExistingFrames; ++idx )
744  {
745  bool hasHeader = (( mHeaderMode == QgsComposerTableV2::FirstFrame && idx == 0 )
747  heightOfLastFrame = frame( idx )->rect().height();
748  rowsVisibleInLastFrame = rowsVisible( heightOfLastFrame, hasHeader );
749  rowsAlreadyShown += rowsVisibleInLastFrame;
750  height += heightOfLastFrame;
751  if ( rowsAlreadyShown >= mTableContents.length() )
752  {
753  //shown entire contents of table, nothing remaining
754  return height;
755  }
756  }
757 
759  {
760  heightOfLastFrame = mComposition->paperHeight();
761  bool hasHeader = (( mHeaderMode == QgsComposerTableV2::FirstFrame && numberExistingFrames < 1 )
763  rowsVisibleInLastFrame = rowsVisible( heightOfLastFrame, hasHeader );
764  }
765 
766  //calculate how many rows left to show
767  int remainingRows = mTableContents.length() - rowsAlreadyShown;
768 
769  if ( remainingRows <= 0 )
770  {
771  //no remaining rows
772  return height;
773  }
774 
775  if ( rowsVisibleInLastFrame < 1 )
776  {
777  //if no rows are visible in the last frame, calculation of missing frames
778  //is impossible. So just return total height of existing frames
779  return height;
780  }
781 
782  //rows remain unshown -- how many extra frames would we need to complete the table?
783  //assume all added frames are same size as final frame
784  int numberFramesMissing = ceil(( double )remainingRows / ( double )rowsVisibleInLastFrame );
785  height += heightOfLastFrame * numberFramesMissing;
786  return height;
787 }
788 
789 void QgsComposerTableV2::drawHorizontalGridLines( QPainter *painter, const int rows, const bool drawHeaderLines ) const
790 {
791  //horizontal lines
792  if ( rows < 1 && !drawHeaderLines )
793  {
794  return;
795  }
796 
797  double halfGridStrokeWidth = ( mShowGrid ? mGridStrokeWidth : 0 ) / 2.0;
798  double currentY = 0;
799  currentY = halfGridStrokeWidth;
800  if ( drawHeaderLines )
801  {
802  painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) );
803  currentY += ( mShowGrid ? mGridStrokeWidth : 0 );
804  currentY += ( QgsComposerUtils::fontAscentMM( mHeaderFont ) + 2 * mCellMargin );
805  }
806  for ( int row = 0; row < rows; ++row )
807  {
808  painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) );
809  currentY += ( mShowGrid ? mGridStrokeWidth : 0 );
811  }
812  painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) );
813 }
814 
815 void QgsComposerTableV2::drawVerticalGridLines( QPainter *painter, const QMap<int, double> &maxWidthMap, const int numberRows, const bool hasHeader, const bool mergeCells ) const
816 {
817  //vertical lines
818  if ( numberRows < 1 && !hasHeader )
819  {
820  return;
821  }
822 
823  //calculate height of table within frame
824  double tableHeight = 0;
825  if ( hasHeader )
826  {
828  }
829  tableHeight += ( mShowGrid ? mGridStrokeWidth : 0 );
830  double headerHeight = tableHeight;
831  tableHeight += numberRows * (( mShowGrid ? mGridStrokeWidth : 0 ) + mCellMargin * 2 + QgsComposerUtils::fontAscentMM( mContentFont ) );
832 
833  double halfGridStrokeWidth = ( mShowGrid ? mGridStrokeWidth : 0 ) / 2.0;
834  double currentX = halfGridStrokeWidth;
835  painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, tableHeight - halfGridStrokeWidth ) );
836  currentX += ( mShowGrid ? mGridStrokeWidth : 0 );
837  QMap<int, double>::const_iterator maxColWidthIt = maxWidthMap.constBegin();
838  int col = 1;
839  for ( ; maxColWidthIt != maxWidthMap.constEnd(); ++maxColWidthIt )
840  {
841  currentX += ( maxColWidthIt.value() + 2 * mCellMargin );
842  if ( col == maxWidthMap.size() || !mergeCells )
843  {
844  painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, tableHeight - halfGridStrokeWidth ) );
845  }
846  else if ( hasHeader )
847  {
848  painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, headerHeight - halfGridStrokeWidth ) );
849  }
850 
851  currentX += ( mShowGrid ? mGridStrokeWidth : 0 );
852  col++;
853  }
854 }
855 
857 {
859 
860  //force recalculation of frame rects, so that they are set to the correct
861  //fixed and minimum frame sizes
863 }
864 
866 {
867  if ( contents.indexOf( row ) >= 0 )
868  {
869  return true;
870  }
871  else
872  {
873  return false;
874  }
875 }
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...
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.
void setShowGrid(const bool showGrid)
Sets whether grid lines should be drawn in the table.
bool mShowGrid
True if grid should be shown.
void setGridStrokeWidth(const double width)
Sets the width for grid lines in the table.
double mGridStrokeWidth
Width of grid lines.
void setGridColor(const QColor &color)
Sets color used for grid lines in the table.
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.
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.
static QColor decodeColor(QString str)
static double fontAscentMM(const QFont &font)
Calculate font ascent in millimeters, including workarounds for QT font rendering issues...
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 recalculateTableSize()
Recalculates and updates the size of the table and all table frames.
EmptyTableMode mEmptyTableMode
Behaviour for empty tables.
QColor mHeaderFontColor
Header font color.
double mCellMargin
Margin between cell borders and cell text.
void setColumns(QgsComposerTableColumns columns)
Replaces the columns in the table with a specified list of QgsComposerTableColumns.
static QString encodeColor(QColor color)
HeaderMode mHeaderMode
Header display mode.
void drawVerticalGridLines(QPainter *painter, const QMap< int, double > &maxWidthMap, const int numberRows, const bool hasHeader, const bool mergeCells=false) const
Draws the vertical grid lines for the table.
QList< QgsComposerTableRow > QgsComposerTableContents
List of QgsComposerTableRows, representing rows and column cell contents for a QgsComposerTable.
bool _readXML(const QDomElement &itemElem, const QDomDocument &doc, bool ignoreFrames=false)
Restores state information about base multiframe object from a DOM element.
void setContentFontColor(const QColor &color)
Sets the color used to draw text in table body cells.
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&#39;s scene rectangles.
virtual QSizeF fixedFrameSize(const int frameIndex=-1) const override
Returns the fixed size for a frame, if desired.
QPair< int, int > rowRange(const QRectF &extent, const int frameIndex) const
Calculates a range of rows which should be visible in a given frame extent.
double totalHeight() const
Returns total height of table contents.
Abstract base class for composer items with the ability to distribute the content to several frames (...
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.
void setEmptyTableMessage(const QString message)
Sets the message for empty tables with no content rows.
void recalculateFrameSizes() override
QMap< int, double > mMaxColumnWidthMap
Map of maximum width for each column.
int frameCount() const
Returns the number of frames associated with this multiframe.
Stores properties of a column in a QgsComposerTable.
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.
QgsComposerTableContents mTableContents
Contents to show in table.
QString mEmptyTableMessage
String to show in empty tables.
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.
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&#39;s content which is shown in this frame.
virtual void refreshAttributes()
Refreshes the contents shown in the table by querying for new data.
double paperHeight() const
Height of paper item.
QList< QgsComposerTableColumn * > QgsComposerTableColumns
List of column definitions for a QgsComposerTable.
void drawHorizontalGridLines(QPainter *painter, const int rows, const bool drawHeaderLines) const
Draws the horizontal grid lines for the table.
QgsComposerTableColumns * columns()
Returns a pointer to the list of QgsComposerTableColumns shown in the table.
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.
int rowsVisible(const int frameIndex) const
Calculates how many content rows are visible within a given frame.
QgsComposerFrame * frame(int i) const
Returns a child frame from the multiframe.
virtual bool readXML(const QDomElement &columnElem)
Reads the column&#39;s properties from xml.
void update()
Forces a redraw of all child frames.
QList< QVariant > QgsComposerTableRow
List of QVariants, representing a the contents of a single row in a QgsComposerTable.
void setContentFont(const QFont &font)
Sets the font used to draw text in table body cells.
QColor mBackgroundColor
Color for table background.
QgsComposition::PlotStyle plotStyle() const
QgsComposerTableContents * contents()
Returns the current contents of the table.
void handleFrameRemoval(QgsComposerItem *item)
Called before a frame is going to be removed.
virtual QSizeF totalSize() const override
Returns the total size of the multiframe&#39;s content.
void changed()
Emitted when the properties of a multi frame have changed, and the GUI item widget must be updated...
#define tr(sourceText)