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