From 37dbbd55a8ad3086c3abc3cbf6cfefefe8f35c2f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 3 Sep 2014 19:05:05 +1000 Subject: [PATCH] [composer] Begin work on calculating rows for QgsComposerTableV2 (sponsored by City of Uster, Switzerland) --- python/core/composer/qgscomposerhtml.sip | 3 +- .../core/composer/qgscomposermultiframe.sip | 3 +- src/core/composer/qgscomposerframe.cpp | 4 +- src/core/composer/qgscomposerhtml.cpp | 4 +- src/core/composer/qgscomposerhtml.h | 2 +- src/core/composer/qgscomposermultiframe.cpp | 21 +++ src/core/composer/qgscomposermultiframe.h | 12 +- src/core/composer/qgscomposertablev2.cpp | 150 +++++++++++++----- src/core/composer/qgscomposertablev2.h | 22 ++- 9 files changed, 171 insertions(+), 50 deletions(-) diff --git a/python/core/composer/qgscomposerhtml.sip b/python/core/composer/qgscomposerhtml.sip index 1133c631415..9960c00aefa 100644 --- a/python/core/composer/qgscomposerhtml.sip +++ b/python/core/composer/qgscomposerhtml.sip @@ -95,7 +95,8 @@ class QgsComposerHtml: QgsComposerMultiFrame void setEvaluateExpressions( bool evaluateExpressions ); QSizeF totalSize() const; - void render( QPainter* p, const QRectF& renderExtent ); + + void render( QPainter* p, const QRectF& renderExtent, const int frameIndex ); bool writeXML( QDomElement& elem, QDomDocument & doc, bool ignoreFrames = false ) const; bool readXML( const QDomElement& itemElem, const QDomDocument& doc, bool ignoreFrames = false ); diff --git a/python/core/composer/qgscomposermultiframe.sip b/python/core/composer/qgscomposermultiframe.sip index 7f6600d3182..266d2edd50b 100644 --- a/python/core/composer/qgscomposermultiframe.sip +++ b/python/core/composer/qgscomposermultiframe.sip @@ -28,7 +28,8 @@ class QgsComposerMultiFrame: QgsComposerObject */ virtual QSizeF fixedFrameSize() const; - virtual void render( QPainter* p, const QRectF& renderExtent ) = 0; + virtual void render( QPainter* p, const QRectF& renderExtent ); + virtual void render( QPainter* p, const QRectF& renderExtent, const int frameIndex ); virtual void addFrame( QgsComposerFrame* frame, bool recalcFrameSizes = true ) = 0; diff --git a/src/core/composer/qgscomposerframe.cpp b/src/core/composer/qgscomposerframe.cpp index 19cdc7ce308..cda6c504190 100644 --- a/src/core/composer/qgscomposerframe.cpp +++ b/src/core/composer/qgscomposerframe.cpp @@ -106,7 +106,9 @@ void QgsComposerFrame::paint( QPainter* painter, const QStyleOptionGraphicsItem* drawBackground( painter ); if ( mMultiFrame ) { - mMultiFrame->render( painter, mSection ); + //calculate index of frame + int frameIndex = mMultiFrame->frameIndex( this ); + mMultiFrame->render( painter, mSection, frameIndex ); } drawFrame( painter ); diff --git a/src/core/composer/qgscomposerhtml.cpp b/src/core/composer/qgscomposerhtml.cpp index 60d8c1a99b3..871a87e5005 100644 --- a/src/core/composer/qgscomposerhtml.cpp +++ b/src/core/composer/qgscomposerhtml.cpp @@ -252,8 +252,10 @@ QSizeF QgsComposerHtml::totalSize() const return mSize; } -void QgsComposerHtml::render( QPainter* p, const QRectF& renderExtent ) +void QgsComposerHtml::render( QPainter* p, const QRectF& renderExtent, const int frameIndex ) { + Q_UNUSED( frameIndex ); + if ( !mWebPage ) { return; diff --git a/src/core/composer/qgscomposerhtml.h b/src/core/composer/qgscomposerhtml.h index 977f7487d3f..64d0b9d99d7 100644 --- a/src/core/composer/qgscomposerhtml.h +++ b/src/core/composer/qgscomposerhtml.h @@ -118,7 +118,7 @@ class CORE_EXPORT QgsComposerHtml: public QgsComposerMultiFrame void setEvaluateExpressions( bool evaluateExpressions ); QSizeF totalSize() const; - void render( QPainter* p, const QRectF& renderExtent ); + void render( QPainter* p, const QRectF& renderExtent, const int frameIndex ); bool writeXML( QDomElement& elem, QDomDocument & doc, bool ignoreFrames = false ) const; bool readXML( const QDomElement& itemElem, const QDomDocument& doc, bool ignoreFrames = false ); diff --git a/src/core/composer/qgscomposermultiframe.cpp b/src/core/composer/qgscomposermultiframe.cpp index 53a9726c58e..14cd6f26794 100644 --- a/src/core/composer/qgscomposermultiframe.cpp +++ b/src/core/composer/qgscomposermultiframe.cpp @@ -40,6 +40,22 @@ QgsComposerMultiFrame::~QgsComposerMultiFrame() deleteFrames(); } +void QgsComposerMultiFrame::render( QPainter *p, const QRectF &renderExtent ) +{ + //base implementation does nothing + Q_UNUSED( p ); + Q_UNUSED( renderExtent ); +} + +void QgsComposerMultiFrame::render( QPainter *p, const QRectF &renderExtent, const int frameIndex ) +{ + Q_UNUSED( frameIndex ); + //base implementation ignores frameIndex + Q_NOWARN_DEPRECATED_PUSH + render( p, renderExtent ); + Q_NOWARN_DEPRECATED_POP +} + void QgsComposerMultiFrame::setResizeMode( ResizeMode mode ) { if ( mode != mResizeMode ) @@ -307,6 +323,11 @@ QgsComposerFrame* QgsComposerMultiFrame::frame( int i ) const return mFrameItems.at( i ); } +int QgsComposerMultiFrame::frameIndex( QgsComposerFrame *frame ) const +{ + return mFrameItems.indexOf( frame ); +} + bool QgsComposerMultiFrame::_writeXML( QDomElement& elem, QDomDocument& doc, bool ignoreFrames ) const { elem.setAttribute( "resizeMode", mResizeMode ); diff --git a/src/core/composer/qgscomposermultiframe.h b/src/core/composer/qgscomposermultiframe.h index 5b55a83d11b..ba3dc0bd8e0 100644 --- a/src/core/composer/qgscomposermultiframe.h +++ b/src/core/composer/qgscomposermultiframe.h @@ -55,7 +55,9 @@ class CORE_EXPORT QgsComposerMultiFrame: public QgsComposerObject */ virtual QSizeF fixedFrameSize() const { return QSizeF( 0, 0 ); } - virtual void render( QPainter* p, const QRectF& renderExtent ) = 0; + Q_DECL_DEPRECATED virtual void render( QPainter* p, const QRectF& renderExtent ); + + virtual void render( QPainter* p, const QRectF& renderExtent, const int frameIndex ); virtual void addFrame( QgsComposerFrame* frame, bool recalcFrameSizes = true ) = 0; @@ -91,8 +93,16 @@ class CORE_EXPORT QgsComposerMultiFrame: public QgsComposerObject @note added in 2.0, replaces nFrames **/ int frameCount() const { return mFrameItems.size(); } + QgsComposerFrame* frame( int i ) const; + /**Returns the index of a frame within the multiframe + * @param frame frame to find index of + * @returns index for frame if found, -1 if frame not found in multiframe + * @note added in version 2.5 + */ + int frameIndex( QgsComposerFrame *frame ) const; + /**Creates a new frame and adds it to the multi frame and composition. * @param currentFrame an existing QgsComposerFrame from which to copy the size * and general frame properties (eg frame style, background, rendering settings). diff --git a/src/core/composer/qgscomposertablev2.cpp b/src/core/composer/qgscomposertablev2.cpp index 3b6f00395cf..48417b43a52 100644 --- a/src/core/composer/qgscomposertablev2.cpp +++ b/src/core/composer/qgscomposertablev2.cpp @@ -122,20 +122,55 @@ bool QgsComposerTableV2::readXML( const QDomElement &itemElem, const QDomDocumen QSizeF QgsComposerTableV2::totalSize() const { //TODO - handle multiple cell headers + //also check height calculation function + return mTableSize; } -void QgsComposerTableV2::render( QPainter *p, const QRectF &renderExtent ) -{ -//do this via rows - //eg, calculate how rows to->from - //and render them +QPair< int, int > QgsComposerTableV2::rowRange( const QRectF extent, const int frameIndex ) const +{ + //calculate row height + //TODO - handle different header modes + //TODO - need to traverse all previous frames to calculate what is visible in each + //as the entire height of a frame may not be used for content + double headerHeight = 0; + double firstHeaderHeight = 2 * mGridStrokeWidth + 2 * mCellMargin + QgsComposerUtils::fontAscentMM( mHeaderFont ); + + //int frameNumber = mFrameItems.indexOf( this ); + if ( frameIndex < 1 ) + { + //currently only header on first + headerHeight = firstHeaderHeight; + } + else + { + headerHeight = mGridStrokeWidth; + } + + //remaining height available for content rows + double contentHeight = extent.height() - headerHeight; + double rowHeight = mGridStrokeWidth + 2 * mCellMargin + QgsComposerUtils::fontAscentMM( mContentFont ); + + //using zero based indexes + int firstVisible = qMax( floor(( extent.top() - firstHeaderHeight ) / rowHeight ), 0.0 ); + int rowsVisible = qMax( floor( contentHeight / rowHeight ), 0.0 ); + int lastVisible = qMin( firstVisible + rowsVisible, mTableContents.length() ); + + return qMakePair( firstVisible, lastVisible ); +} + + +void QgsComposerTableV2::render( QPainter *p, const QRectF &renderExtent, const int frameIndex ) +{ if ( !p ) { return; } + //calculate which rows to show in this frame + QPair< int, int > rowsToShow = rowRange( renderExtent, frameIndex ); + if ( mComposition->plotStyle() == QgsComposition::Print || mComposition->plotStyle() == QgsComposition::Postscript ) { @@ -160,43 +195,52 @@ void QgsComposerTableV2::render( QPainter *p, const QRectF &renderExtent ) double cellHeaderHeight = QgsComposerUtils::fontAscentMM( mHeaderFont ) + 2 * mCellMargin; double cellBodyHeight = QgsComposerUtils::fontAscentMM( mContentFont ) + 2 * mCellMargin; QRectF cell; + + //TODO - should be controlled via a property, eg an enum with values + //always/never/first frame + bool drawHeader = frameIndex < 1; + for ( ; columnIt != mColumns.constEnd(); ++columnIt ) { currentY = mGridStrokeWidth; currentX += mCellMargin; - cell = QRectF( currentX, currentY, mMaxColumnWidthMap[col], cellHeaderHeight ); - - //calculate alignment of header - Qt::AlignmentFlag headerAlign = Qt::AlignLeft; - switch ( mHeaderHAlignment ) + if ( drawHeader ) { - case FollowColumn: - headerAlign = ( *columnIt )->hAlignment(); - break; - case HeaderLeft: - headerAlign = Qt::AlignLeft; - break; - case HeaderCenter: - headerAlign = Qt::AlignHCenter; - break; - case HeaderRight: - headerAlign = Qt::AlignRight; - break; + //draw the header + cell = QRectF( currentX, currentY, mMaxColumnWidthMap[col], cellHeaderHeight ); + + //calculate alignment of header + Qt::AlignmentFlag headerAlign = Qt::AlignLeft; + switch ( mHeaderHAlignment ) + { + case FollowColumn: + headerAlign = ( *columnIt )->hAlignment(); + break; + case HeaderLeft: + headerAlign = Qt::AlignLeft; + break; + case HeaderCenter: + headerAlign = Qt::AlignHCenter; + break; + case HeaderRight: + headerAlign = Qt::AlignRight; + break; + } + + + QgsComposerUtils::drawText( p, cell, ( *columnIt )->heading(), mHeaderFont, mHeaderFontColor, headerAlign, Qt::AlignVCenter, Qt::TextDontClip ); + + currentY += cellHeaderHeight; + currentY += mGridStrokeWidth; } - QgsComposerUtils::drawText( p, cell, ( *columnIt )->heading(), mHeaderFont, mHeaderFontColor, headerAlign, Qt::AlignVCenter, Qt::TextDontClip ); - - currentY += cellHeaderHeight; - currentY += mGridStrokeWidth; - //draw the attribute values - QgsComposerTableContents::const_iterator attIt = mTableContents.begin(); - for ( ; attIt != mTableContents.end(); ++attIt ) + for ( int row = rowsToShow.first; row < rowsToShow.second; ++row ) { cell = QRectF( currentX, currentY, mMaxColumnWidthMap[col], cellBodyHeight ); - QVariant cellContents = ( *attIt ).at( col ); + QVariant cellContents = mTableContents.at( row ).at( col ); QString str = cellContents.toString(); QgsComposerUtils::drawText( p, cell, str, mContentFont, mContentFontColor, ( *columnIt )->hAlignment(), Qt::AlignVCenter, Qt::TextDontClip ); @@ -210,6 +254,7 @@ void QgsComposerTableV2::render( QPainter *p, const QRectF &renderExtent ) col++; } + //and the borders if ( mShowGrid ) { @@ -218,13 +263,12 @@ void QgsComposerTableV2::render( QPainter *p, const QRectF &renderExtent ) gridPen.setColor( mGridColor ); gridPen.setJoinStyle( Qt::MiterJoin ); p->setPen( gridPen ); - drawHorizontalGridLines( p, mTableContents.size() ); - drawVerticalGridLines( p, mMaxColumnWidthMap ); + drawHorizontalGridLines( p, rowsToShow.second - rowsToShow.first, drawHeader ); + drawVerticalGridLines( p, mMaxColumnWidthMap, rowsToShow.second - rowsToShow.first, drawHeader ); } p->restore(); - } void QgsComposerTableV2::setCellMargin( const double margin ) @@ -438,14 +482,23 @@ double QgsComposerTableV2::totalHeight() const return totalHeight; } -void QgsComposerTableV2::drawHorizontalGridLines( QPainter *painter, const int rows ) const +void QgsComposerTableV2::drawHorizontalGridLines( QPainter *painter, const int rows, const bool drawHeaderLines ) const { //horizontal lines + if ( rows < 1 && !drawHeaderLines ) + { + return; + } + double halfGridStrokeWidth = mGridStrokeWidth / 2.0; - double currentY = halfGridStrokeWidth; - painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) ); - currentY += mGridStrokeWidth; - currentY += ( QgsComposerUtils::fontAscentMM( mHeaderFont ) + 2 * mCellMargin ); + double currentY = 0; + currentY = halfGridStrokeWidth; + if ( drawHeaderLines ) + { + painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) ); + currentY += mGridStrokeWidth; + currentY += ( QgsComposerUtils::fontAscentMM( mHeaderFont ) + 2 * mCellMargin ); + } for ( int row = 0; row < rows; ++row ) { painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) ); @@ -455,18 +508,33 @@ void QgsComposerTableV2::drawHorizontalGridLines( QPainter *painter, const int r painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) ); } -void QgsComposerTableV2::drawVerticalGridLines( QPainter *painter, const QMap &maxWidthMap ) const +void QgsComposerTableV2::drawVerticalGridLines( QPainter *painter, const QMap &maxWidthMap, const int numberRows, const bool hasHeader ) const { //vertical lines + if ( numberRows < 1 && !hasHeader ) + { + return; + } + + //calculate height of table within frame + double tableHeight = 0; + if ( hasHeader ) + { + tableHeight += mGridStrokeWidth + mCellMargin * 2 + QgsComposerUtils::fontAscentMM( mHeaderFont ); + } + + tableHeight += numberRows * ( mGridStrokeWidth + mCellMargin * 2 + QgsComposerUtils::fontAscentMM( mContentFont ) ); + tableHeight += mGridStrokeWidth; + double halfGridStrokeWidth = mGridStrokeWidth / 2.0; double currentX = halfGridStrokeWidth; - painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, mTableSize.height() - halfGridStrokeWidth ) ); + painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, tableHeight - halfGridStrokeWidth ) ); currentX += mGridStrokeWidth; QMap::const_iterator maxColWidthIt = maxWidthMap.constBegin(); for ( ; maxColWidthIt != maxWidthMap.constEnd(); ++maxColWidthIt ) { currentX += ( maxColWidthIt.value() + 2 * mCellMargin ); - painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, mTableSize.height() - halfGridStrokeWidth ) ); + painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, tableHeight - halfGridStrokeWidth ) ); currentX += mGridStrokeWidth; } } diff --git a/src/core/composer/qgscomposertablev2.h b/src/core/composer/qgscomposertablev2.h index ac40a68dcdd..323cc0d2591 100644 --- a/src/core/composer/qgscomposertablev2.h +++ b/src/core/composer/qgscomposertablev2.h @@ -21,6 +21,7 @@ #include "qgscomposermultiframe.h" #include #include +#include class QgsComposerTableColumn; @@ -49,6 +50,7 @@ class CORE_EXPORT QgsComposerTableV2: public QgsComposerMultiFrame { Q_OBJECT + public: /*! Controls how headers are horizontally aligned in a table @@ -71,7 +73,9 @@ class CORE_EXPORT QgsComposerTableV2: public QgsComposerMultiFrame virtual bool readXML( const QDomElement& itemElem, const QDomDocument& doc, bool ignoreFrames = false ); virtual QSizeF totalSize() const; - virtual void render( QPainter* p, const QRectF& renderExtent ); + + virtual void render( QPainter* p, const QRectF& renderExtent, const int frameIndex ); + /**Sets the margin distance between cell borders and their contents. * @param margin margin for cell contents @@ -291,22 +295,34 @@ class CORE_EXPORT QgsComposerTableV2: public QgsComposerMultiFrame double totalHeight() const; + /**Calculates a range of rows which should be visible in a given + * rectangle. + * @param extent visible extent + * @param frameIndex index number for frame + * @returns row range + */ + QPair rowRange( const QRectF extent, const int frameIndex ) const; + + /**Draws the horizontal grid lines for the table. * @param painter destination painter for grid lines * @param rows number of rows shown in table + * @param drawHeaderLines set to true to include for the table header * @see drawVerticalGridLines */ - void drawHorizontalGridLines( QPainter* painter, const int rows ) const; + void drawHorizontalGridLines( QPainter* painter, const int rows, const bool drawHeaderLines ) const; /**Draws the vertical grid lines for the table. * @param painter destination painter for grid lines * @param maxWidthMap QMap of int to double, where the int contains the column number and the double is the * maximum width of text present in the column. + * @param numberRows number of rows of content in table frame + * @param hasHeader set to true if table frame includes header cells * @note not available in python bindings * @see drawVerticalGridLines * @see calculateMaxColumnWidths */ - void drawVerticalGridLines( QPainter* painter, const QMap& maxWidthMap ) const; + void drawVerticalGridLines( QPainter* painter, const QMap& maxWidthMap, const int numberRows, const bool hasHeader ) const; void adjustFrameToSize(); };