QGIS/src/core/composer/qgscomposertablev2.cpp
Nyall Dawson 456e971372 [composer] Fix table background extending beyond table rows (fix #11272).
It was necessary to add a new 'table background' color option to prevent
regressions when loading old compositions, since users may desire
the old behaviour where the entire frame is filled.
2014-10-03 09:08:23 +10:00

850 lines
26 KiB
C++

/***************************************************************************
qgscomposertablev2.cpp
------------------
begin : July 2014
copyright : (C) 2014 by Nyall Dawson, Marco Hugentobler
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgscomposertablev2.h"
#include "qgscomposerutils.h"
#include "qgscomposertablecolumn.h"
#include "qgssymbollayerv2utils.h"
#include "qgscomposerframe.h"
QgsComposerTableV2::QgsComposerTableV2( QgsComposition *composition, bool createUndoCommands )
: QgsComposerMultiFrame( composition, createUndoCommands )
, mCellMargin( 1.0 )
, mEmptyTableMode( HeadersOnly )
, mHeaderFontColor( Qt::black )
, mHeaderHAlignment( FollowColumn )
, mHeaderMode( FirstFrame )
, mContentFontColor( Qt::black )
, mShowGrid( true )
, mGridStrokeWidth( 0.5 )
, mGridColor( Qt::black )
, mBackgroundColor( Qt::white )
{
if ( mComposition )
{
QObject::connect( mComposition, SIGNAL( itemRemoved( QgsComposerItem* ) ), this, SLOT( handleFrameRemoval( QgsComposerItem* ) ) );
}
//get default composer font from settings
QSettings settings;
QString defaultFontString = settings.value( "/Composer/defaultFont" ).toString();
if ( !defaultFontString.isEmpty() )
{
mHeaderFont.setFamily( defaultFontString );
mContentFont.setFamily( defaultFontString );
}
}
QgsComposerTableV2::QgsComposerTableV2()
: QgsComposerMultiFrame( 0, false )
{
}
QgsComposerTableV2::~QgsComposerTableV2()
{
qDeleteAll( mColumns );
mColumns.clear();
}
bool QgsComposerTableV2::writeXML( QDomElement& elem, QDomDocument & doc, bool ignoreFrames ) const
{
elem.setAttribute( "cellMargin", QString::number( mCellMargin ) );
elem.setAttribute( "emptyTableMode", QString::number(( int )mEmptyTableMode ) );
elem.setAttribute( "emptyTableMessage", mEmptyTableMessage );
elem.setAttribute( "headerFont", mHeaderFont.toString() );
elem.setAttribute( "headerFontColor", QgsSymbolLayerV2Utils::encodeColor( mHeaderFontColor ) );
elem.setAttribute( "headerHAlignment", QString::number(( int )mHeaderHAlignment ) );
elem.setAttribute( "headerMode", QString::number(( int )mHeaderMode ) );
elem.setAttribute( "contentFont", mContentFont.toString() );
elem.setAttribute( "contentFontColor", QgsSymbolLayerV2Utils::encodeColor( mContentFontColor ) );
elem.setAttribute( "gridStrokeWidth", QString::number( mGridStrokeWidth ) );
elem.setAttribute( "gridColor", QgsSymbolLayerV2Utils::encodeColor( mGridColor ) );
elem.setAttribute( "showGrid", mShowGrid );
elem.setAttribute( "backgroundColor", QgsSymbolLayerV2Utils::encodeColor( mBackgroundColor ) );
//columns
QDomElement displayColumnsElem = doc.createElement( "displayColumns" );
QList<QgsComposerTableColumn*>::const_iterator columnIt = mColumns.constBegin();
for ( ; columnIt != mColumns.constEnd(); ++columnIt )
{
QDomElement columnElem = doc.createElement( "column" );
( *columnIt )->writeXML( columnElem, doc );
displayColumnsElem.appendChild( columnElem );
}
elem.appendChild( displayColumnsElem );
bool state = _writeXML( elem, doc, ignoreFrames );
return state;
}
bool QgsComposerTableV2::readXML( const QDomElement &itemElem, const QDomDocument &doc, bool ignoreFrames )
{
deleteFrames();
//first create the frames
if ( !_readXML( itemElem, doc, ignoreFrames ) )
{
return false;
}
if ( itemElem.isNull() )
{
return false;
}
mEmptyTableMode = QgsComposerTableV2::EmptyTableMode( itemElem.attribute( "emptyTableMode", "0" ).toInt() );
mEmptyTableMessage = itemElem.attribute( "emptyTableMessage", tr( "No matching records" ) );
mHeaderFont.fromString( itemElem.attribute( "headerFont", "" ) );
mHeaderFontColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "headerFontColor", "0,0,0,255" ) );
mHeaderHAlignment = QgsComposerTableV2::HeaderHAlignment( itemElem.attribute( "headerHAlignment", "0" ).toInt() );
mHeaderMode = QgsComposerTableV2::HeaderMode( itemElem.attribute( "headerMode", "0" ).toInt() );
mContentFont.fromString( itemElem.attribute( "contentFont", "" ) );
mContentFontColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "contentFontColor", "0,0,0,255" ) );
mCellMargin = itemElem.attribute( "cellMargin", "1.0" ).toDouble();
mGridStrokeWidth = itemElem.attribute( "gridStrokeWidth", "0.5" ).toDouble();
mShowGrid = itemElem.attribute( "showGrid", "1" ).toInt();
mGridColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "gridColor", "0,0,0,255" ) );
mBackgroundColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "backgroundColor", "255,255,255,0" ) );
//restore column specifications
qDeleteAll( mColumns );
mColumns.clear();
QDomNodeList columnsList = itemElem.elementsByTagName( "displayColumns" );
if ( columnsList.size() > 0 )
{
QDomElement columnsElem = columnsList.at( 0 ).toElement();
QDomNodeList columnEntryList = columnsElem.elementsByTagName( "column" );
for ( int i = 0; i < columnEntryList.size(); ++i )
{
QDomElement columnElem = columnEntryList.at( i ).toElement();
QgsComposerTableColumn* column = new QgsComposerTableColumn;
column->readXML( columnElem );
mColumns.append( column );
}
}
return true;
}
QSizeF QgsComposerTableV2::totalSize() const
{
return mTableSize;
}
int QgsComposerTableV2::rowsVisible( const int frameIndex ) const
{
//get frame extent
if ( frameIndex >= frameCount() )
{
return 0;
}
QRectF frameExtent = frame( frameIndex )->extent();
bool includeHeader = false;
if (( mHeaderMode == QgsComposerTableV2::FirstFrame && frameIndex < 1 )
|| ( mHeaderMode == QgsComposerTableV2::AllFrames ) )
{
includeHeader = true;
}
return rowsVisible( frameExtent.height(), includeHeader );
}
int QgsComposerTableV2::rowsVisible( const double frameHeight, const bool includeHeader ) const
{
//calculate header height
double headerHeight = 0;
if ( includeHeader )
{
//frame has a header
headerHeight = 2 * ( mShowGrid ? mGridStrokeWidth : 0 ) + 2 * mCellMargin + QgsComposerUtils::fontAscentMM( mHeaderFont );
}
else
{
//frame has no header text, just the stroke
headerHeight = ( mShowGrid ? mGridStrokeWidth : 0 );
}
//remaining height available for content rows
double contentHeight = frameHeight - headerHeight;
//calculate number of visible rows
double rowHeight = ( mShowGrid ? mGridStrokeWidth : 0 ) + 2 * mCellMargin + QgsComposerUtils::fontAscentMM( mContentFont );
return qMax( floor( contentHeight / rowHeight ), 0.0 );
}
QPair< int, int > QgsComposerTableV2::rowRange( const QRectF extent, const int frameIndex ) const
{
//calculate row height
if ( frameIndex >= frameCount() )
{
//bad frame index
return qMakePair( 0 , 0 );
}
//loop through all previous frames to calculate how many rows are visible in each
//as the entire height of a frame may not be utilised for content rows
int rowsAlreadyShown = 0;
for ( int idx = 0; idx < frameIndex; ++idx )
{
rowsAlreadyShown += rowsVisible( idx );
}
double headerHeight = 0;
if (( mHeaderMode == QgsComposerTableV2::FirstFrame && frameIndex < 1 )
|| ( mHeaderMode == QgsComposerTableV2::AllFrames ) )
{
//frame has a header
headerHeight = 2 * ( mShowGrid ? mGridStrokeWidth : 0 ) + 2 * mCellMargin + QgsComposerUtils::fontAscentMM( mHeaderFont );
}
else
{
headerHeight = ( mShowGrid ? mGridStrokeWidth : 0 );
}
//remaining height available for content rows
double contentHeight = extent.height() - headerHeight;
double rowHeight = ( mShowGrid ? mGridStrokeWidth : 0 ) + 2 * mCellMargin + QgsComposerUtils::fontAscentMM( mContentFont );
//using zero based indexes
int firstVisible = qMin( rowsAlreadyShown, mTableContents.length() );
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;
}
bool emptyTable = mTableContents.length() == 0;
if ( emptyTable && mEmptyTableMode == QgsComposerTableV2::HideTable )
{
//empty table set to hide table mode, so don't draw anything
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 )
{
//exporting composition, so force an attribute refresh
//we do this in case vector layer has changed via an external source (eg, another database user)
refreshAttributes();
}
double gridSize = mShowGrid ? mGridStrokeWidth : 0;
QList<QgsComposerTableColumn*>::const_iterator columnIt = mColumns.constBegin();
int col = 0;
double cellHeaderHeight = QgsComposerUtils::fontAscentMM( mHeaderFont ) + 2 * mCellMargin;
double cellBodyHeight = QgsComposerUtils::fontAscentMM( mContentFont ) + 2 * mCellMargin;
QRectF cell;
//calculate whether a header is required
bool drawHeader = (( mHeaderMode == QgsComposerTableV2::FirstFrame && frameIndex < 1 )
|| ( mHeaderMode == QgsComposerTableV2::AllFrames ) );
//calculate whether drawing table contents is required
bool drawContents = !( emptyTable && mEmptyTableMode == QgsComposerTableV2::ShowMessage );
int numberRowsToDraw = rowsToShow.second - rowsToShow.first;
if ( mEmptyTableMode == QgsComposerTableV2::DrawEmptyCells )
{
numberRowsToDraw = rowsVisible( frameIndex );
}
bool mergeCells = false;
if ( emptyTable && mEmptyTableMode == QgsComposerTableV2::ShowMessage )
{
//draw a merged row for the empty table message
numberRowsToDraw++;
mergeCells = true;
}
p->save();
//antialiasing on
p->setRenderHint( QPainter::Antialiasing, true );
//draw table background
if ( mBackgroundColor.alpha() > 0 )
{
p->save();
p->setPen( Qt::NoPen );
p->setBrush( QBrush( mBackgroundColor ) );
double totalHeight = ( drawHeader || ( numberRowsToDraw > 0 ) ? gridSize : 0 ) +
( drawHeader ? cellHeaderHeight + gridSize : 0.0 ) +
( drawContents ? numberRowsToDraw : 1 ) * ( cellBodyHeight + gridSize );
if ( totalHeight > 0 )
{
QRectF backgroundRect( 0, 0, mTableSize.width(), totalHeight );
p->drawRect( backgroundRect );
}
p->restore();
}
//now draw the text
double currentX = gridSize;
double currentY;
p->setPen( Qt::SolidLine );
for ( ; columnIt != mColumns.constEnd(); ++columnIt )
{
currentY = gridSize;
currentX += mCellMargin;
Qt::TextFlag textFlag = ( Qt::TextFlag )0;
if (( *columnIt )->width() <= 0 )
{
//automatic column width, so we use the Qt::TextDontClip flag when drawing contents, as this works nicer for italicised text
//which may slightly exceed the calculated width
//if column size was manually set then we do apply text clipping, to avoid painting text outside of columns width
textFlag = Qt::TextDontClip;
}
if ( drawHeader )
{
//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, textFlag );
currentY += cellHeaderHeight;
currentY += gridSize;
}
if ( drawContents )
{
//draw the attribute values
for ( int row = rowsToShow.first; row < rowsToShow.second; ++row )
{
cell = QRectF( currentX, currentY, mMaxColumnWidthMap[col], cellBodyHeight );
QVariant cellContents = mTableContents.at( row ).at( col );
QString str = cellContents.toString();
QgsComposerUtils::drawText( p, cell, str, mContentFont, mContentFontColor, ( *columnIt )->hAlignment(), Qt::AlignVCenter, textFlag );
currentY += cellBodyHeight;
currentY += gridSize;
}
}
currentX += mMaxColumnWidthMap[ col ];
currentX += mCellMargin;
currentX += gridSize;
col++;
}
//and the borders
if ( mShowGrid )
{
QPen gridPen;
gridPen.setWidthF( mGridStrokeWidth );
gridPen.setColor( mGridColor );
gridPen.setJoinStyle( Qt::MiterJoin );
p->setPen( gridPen );
drawHorizontalGridLines( p, numberRowsToDraw, drawHeader );
drawVerticalGridLines( p, mMaxColumnWidthMap, numberRowsToDraw, drawHeader, mergeCells );
}
//special case - no records and table is set to ShowMessage mode
if ( emptyTable && mEmptyTableMode == QgsComposerTableV2::ShowMessage )
{
double messageX = gridSize + mCellMargin;
double messageY = gridSize + ( drawHeader ? cellHeaderHeight + gridSize : 0 );
cell = QRectF( messageX, messageY, mTableSize.width() - messageX, cellBodyHeight );
QgsComposerUtils::drawText( p, cell, mEmptyTableMessage, mContentFont, mContentFontColor, Qt::AlignHCenter, Qt::AlignVCenter, ( Qt::TextFlag )0 );
}
p->restore();
}
void QgsComposerTableV2::setCellMargin( const double margin )
{
if ( margin == mCellMargin )
{
return;
}
mCellMargin = margin;
//since spacing has changed, we need to recalculate the table size
recalculateTableSize();
emit changed();
}
void QgsComposerTableV2::setEmptyTableBehaviour( const QgsComposerTableV2::EmptyTableMode mode )
{
if ( mode == mEmptyTableMode )
{
return;
}
mEmptyTableMode = mode;
//since appearance has changed, we need to recalculate the table size
recalculateTableSize();
emit changed();
}
void QgsComposerTableV2::setEmptyTableMessage( const QString message )
{
if ( message == mEmptyTableMessage )
{
return;
}
mEmptyTableMessage = message;
//since message has changed, we need to recalculate the table size
recalculateTableSize();
emit changed();
}
void QgsComposerTableV2::setHeaderFont( const QFont &font )
{
if ( font == mHeaderFont )
{
return;
}
mHeaderFont = font;
//since font attributes have changed, we need to recalculate the table size
recalculateTableSize();
emit changed();
}
void QgsComposerTableV2::setHeaderFontColor( const QColor &color )
{
if ( color == mHeaderFontColor )
{
return;
}
mHeaderFontColor = color;
repaint();
emit changed();
}
void QgsComposerTableV2::setHeaderHAlignment( const QgsComposerTableV2::HeaderHAlignment alignment )
{
if ( alignment == mHeaderHAlignment )
{
return;
}
mHeaderHAlignment = alignment;
repaint();
emit changed();
}
void QgsComposerTableV2::setHeaderMode( const QgsComposerTableV2::HeaderMode mode )
{
if ( mode == mHeaderMode )
{
return;
}
mHeaderMode = mode;
recalculateTableSize();
emit changed();
}
void QgsComposerTableV2::setContentFont( const QFont &font )
{
if ( font == mContentFont )
{
return;
}
mContentFont = font;
//since font attributes have changed, we need to recalculate the table size
recalculateTableSize();
emit changed();
}
void QgsComposerTableV2::setContentFontColor( const QColor &color )
{
if ( color == mContentFontColor )
{
return;
}
mContentFontColor = color;
repaint();
emit changed();
}
void QgsComposerTableV2::setShowGrid( const bool showGrid )
{
if ( showGrid == mShowGrid )
{
return;
}
mShowGrid = showGrid;
//since grid spacing has changed, we need to recalculate the table size
recalculateTableSize();
emit changed();
}
void QgsComposerTableV2::setGridStrokeWidth( const double width )
{
if ( width == mGridStrokeWidth )
{
return;
}
mGridStrokeWidth = width;
//since grid spacing has changed, we need to recalculate the table size
recalculateTableSize();
emit changed();
}
void QgsComposerTableV2::setGridColor( const QColor &color )
{
if ( color == mGridColor )
{
return;
}
mGridColor = color;
repaint();
emit changed();
}
void QgsComposerTableV2::setBackgroundColor( const QColor &color )
{
if ( color == mBackgroundColor )
{
return;
}
mBackgroundColor = color;
repaint();
emit changed();
}
void QgsComposerTableV2::setColumns( QgsComposerTableColumns columns )
{
//remove existing columns
qDeleteAll( mColumns );
mColumns.clear();
mColumns.append( columns );
}
QMap<int, QString> QgsComposerTableV2::headerLabels() const
{
QMap<int, QString> headers;
QgsComposerTableColumns::const_iterator columnIt = mColumns.constBegin();
int col = 0;
for ( ; columnIt != mColumns.constEnd(); ++columnIt )
{
headers.insert( col, ( *columnIt )->heading() );
col++;
}
return headers;
}
QSizeF QgsComposerTableV2::fixedFrameSize( const int frameIndex ) const
{
Q_UNUSED( frameIndex );
return QSizeF( mTableSize.width(), 0 );
}
QSizeF QgsComposerTableV2::minFrameSize( const int frameIndex ) const
{
double height = 0;
if (( mHeaderMode == QgsComposerTableV2::FirstFrame && frameIndex < 1 )
|| ( mHeaderMode == QgsComposerTableV2::AllFrames ) )
{
//header required, force frame to be high enough for header
height = 2 * ( mShowGrid ? mGridStrokeWidth : 0 ) + 2 * mCellMargin + QgsComposerUtils::fontAscentMM( mHeaderFont );
}
return QSizeF( 0, height );
}
void QgsComposerTableV2::refreshAttributes()
{
mMaxColumnWidthMap.clear();
mTableContents.clear();
//get new contents
if ( !getTableContents( mTableContents ) )
{
return;
}
}
void QgsComposerTableV2::recalculateFrameSizes()
{
mTableSize = QSizeF( totalWidth(), totalHeight() );
QgsComposerMultiFrame::recalculateFrameSizes();
}
bool QgsComposerTableV2::calculateMaxColumnWidths()
{
mMaxColumnWidthMap.clear();
//first, go through all the column headers and calculate the max width values
QgsComposerTableColumns::const_iterator columnIt = mColumns.constBegin();
int col = 0;
for ( ; columnIt != mColumns.constEnd(); ++columnIt )
{
double width = 0;
if (( *columnIt )->width() > 0 )
{
//column has manually specified width
width = ( *columnIt )->width();
}
else if ( mHeaderMode != QgsComposerTableV2::NoHeaders )
{
width = QgsComposerUtils::textWidthMM( mHeaderFont, ( *columnIt )->heading() );
}
mMaxColumnWidthMap.insert( col, width );
col++;
}
//next, go through all the table contents and calculate the max width values
QgsComposerTableContents::const_iterator rowIt = mTableContents.constBegin();
double currentCellTextWidth;
for ( ; rowIt != mTableContents.constEnd(); ++rowIt )
{
QgsComposerTableRow::const_iterator colIt = rowIt->constBegin();
int columnNumber = 0;
for ( ; colIt != rowIt->constEnd(); ++colIt )
{
if ( mColumns.at( columnNumber )->width() <= 0 )
{
//column width set to automatic, so check content size
currentCellTextWidth = QgsComposerUtils::textWidthMM( mContentFont, ( *colIt ).toString() );
mMaxColumnWidthMap[ columnNumber ] = qMax( currentCellTextWidth, mMaxColumnWidthMap[ columnNumber ] );
}
columnNumber++;
}
}
return true;
}
double QgsComposerTableV2::totalWidth()
{
//check how much space each column needs
if ( !calculateMaxColumnWidths() )
{
return 0;
}
//adapt frame to total width
double totalWidth = 0;
QMap<int, double>::const_iterator maxColWidthIt = mMaxColumnWidthMap.constBegin();
for ( ; maxColWidthIt != mMaxColumnWidthMap.constEnd(); ++maxColWidthIt )
{
totalWidth += maxColWidthIt.value();
}
totalWidth += ( 2 * mMaxColumnWidthMap.size() * mCellMargin );
totalWidth += ( mMaxColumnWidthMap.size() + 1 ) * ( mShowGrid ? mGridStrokeWidth : 0 );
return totalWidth;
}
double QgsComposerTableV2::totalHeight() const
{
double height = 0;
//loop through all existing frames to calculate how many rows are visible in each
//as the entire height of a frame may not be utilised for content rows
int rowsAlreadyShown = 0;
int numberExistingFrames = frameCount();
int rowsVisibleInLastFrame = 0;
double heightOfLastFrame = 0;
for ( int idx = 0; idx < numberExistingFrames; ++idx )
{
bool hasHeader = (( mHeaderMode == QgsComposerTableV2::FirstFrame && idx == 0 )
|| ( mHeaderMode == QgsComposerTableV2::AllFrames ) );
heightOfLastFrame = frame( idx )->rect().height();
rowsVisibleInLastFrame = rowsVisible( heightOfLastFrame, hasHeader );
rowsAlreadyShown += rowsVisibleInLastFrame;
height += heightOfLastFrame;
if ( rowsAlreadyShown >= mTableContents.length() )
{
//shown entire contents of table, nothing remaining
return height;
}
}
if ( mResizeMode == QgsComposerMultiFrame::ExtendToNextPage )
{
heightOfLastFrame = mComposition->paperHeight();
bool hasHeader = (( mHeaderMode == QgsComposerTableV2::FirstFrame && numberExistingFrames < 1 )
|| ( mHeaderMode == QgsComposerTableV2::AllFrames ) );
rowsVisibleInLastFrame = rowsVisible( heightOfLastFrame, hasHeader );
}
//calculate how many rows left to show
int remainingRows = mTableContents.length() - rowsAlreadyShown;
if ( remainingRows <= 0 )
{
//no remaining rows
return height;
}
if ( rowsVisibleInLastFrame < 1 )
{
//if no rows are visible in the last frame, calculation of missing frames
//is impossible. So just return total height of existing frames
return height;
}
//rows remain unshown -- how many extra frames would we need to complete the table?
//assume all added frames are same size as final frame
int numberFramesMissing = ceil(( double )remainingRows / ( double )rowsVisibleInLastFrame );
height += heightOfLastFrame * numberFramesMissing;
return height;
}
void QgsComposerTableV2::drawHorizontalGridLines( QPainter *painter, const int rows, const bool drawHeaderLines ) const
{
//horizontal lines
if ( rows < 1 && !drawHeaderLines )
{
return;
}
double halfGridStrokeWidth = ( mShowGrid ? mGridStrokeWidth : 0 ) / 2.0;
double currentY = 0;
currentY = halfGridStrokeWidth;
if ( drawHeaderLines )
{
painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) );
currentY += ( mShowGrid ? mGridStrokeWidth : 0 );
currentY += ( QgsComposerUtils::fontAscentMM( mHeaderFont ) + 2 * mCellMargin );
}
for ( int row = 0; row < rows; ++row )
{
painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) );
currentY += ( mShowGrid ? mGridStrokeWidth : 0 );
currentY += ( QgsComposerUtils::fontAscentMM( mContentFont ) + 2 * mCellMargin );
}
painter->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( mTableSize.width() - halfGridStrokeWidth, currentY ) );
}
void QgsComposerTableV2::drawVerticalGridLines( QPainter *painter, const QMap<int, double> &maxWidthMap, const int numberRows, const bool hasHeader, const bool mergeCells ) const
{
//vertical lines
if ( numberRows < 1 && !hasHeader )
{
return;
}
//calculate height of table within frame
double tableHeight = 0;
if ( hasHeader )
{
tableHeight += ( mShowGrid ? mGridStrokeWidth : 0 ) + mCellMargin * 2 + QgsComposerUtils::fontAscentMM( mHeaderFont );
}
tableHeight += ( mShowGrid ? mGridStrokeWidth : 0 );
double headerHeight = tableHeight;
tableHeight += numberRows * (( mShowGrid ? mGridStrokeWidth : 0 ) + mCellMargin * 2 + QgsComposerUtils::fontAscentMM( mContentFont ) );
double halfGridStrokeWidth = ( mShowGrid ? mGridStrokeWidth : 0 ) / 2.0;
double currentX = halfGridStrokeWidth;
painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, tableHeight - halfGridStrokeWidth ) );
currentX += ( mShowGrid ? mGridStrokeWidth : 0 );
QMap<int, double>::const_iterator maxColWidthIt = maxWidthMap.constBegin();
int col = 1;
for ( ; maxColWidthIt != maxWidthMap.constEnd(); ++maxColWidthIt )
{
currentX += ( maxColWidthIt.value() + 2 * mCellMargin );
if ( col == maxWidthMap.size() || !mergeCells )
{
painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, tableHeight - halfGridStrokeWidth ) );
}
else if ( hasHeader )
{
painter->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, headerHeight - halfGridStrokeWidth ) );
}
currentX += ( mShowGrid ? mGridStrokeWidth : 0 );
col++;
}
}
void QgsComposerTableV2::recalculateTableSize()
{
recalculateFrameSizes();
//force recalculation of frame rects, so that they are set to the correct
//fixed and minimum frame sizes
recalculateFrameRects();
}
bool QgsComposerTableV2::contentsContainsRow( const QgsComposerTableContents &contents, const QgsComposerTableRow &row ) const
{
if ( contents.indexOf( row ) >= 0 )
{
return true;
}
else
{
return false;
}
}