Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Move bits of legend rendering code into item classes + add rendering …
…test
  • Loading branch information
wonder-sk committed Sep 1, 2014
1 parent ee33edc commit fda50ed
Show file tree
Hide file tree
Showing 12 changed files with 749 additions and 454 deletions.
310 changes: 304 additions & 6 deletions src/core/composer/qgscomposerlegenditem.cpp
Expand Up @@ -61,21 +61,104 @@ void QgsComposerLegendItem::writeXMLChildren( QDomElement& elem, QDomDocument& d
}
}

////////////////QgsComposerBaseSymbolItem

QgsComposerBaseSymbolItem::QgsComposerBaseSymbolItem()
: QgsComposerLegendItem( QgsComposerLegendStyle::Symbol )
{

}

QgsComposerBaseSymbolItem::ItemMetrics QgsComposerBaseSymbolItem::draw( const QgsLegendSettings& settings, ItemContext* ctx ) // QPainter* painter, const QPointF& point, double labelXOffset )
{
QFont symbolLabelFont = settings.style( QgsComposerLegendStyle::SymbolLabel ).font();

double textHeight = settings.fontHeightCharacterMM( symbolLabelFont, QChar( '0' ) );
// itemHeight here is not realy item height, it is only for symbol
// vertical alignment purpose, i.e. ok take single line height
// if there are more lines, thos run under the symbol
double itemHeight = qMax( settings.symbolSize().height(), textHeight );

ItemMetrics im;
im.symbolSize = drawSymbol( settings, ctx, itemHeight );
im.labelSize = drawSymbolText( settings, ctx, im.symbolSize );
return im;
}

QSizeF QgsComposerBaseSymbolItem::drawSymbol( const QgsLegendSettings& settings, ItemContext* ctx, double itemHeight ) const
{
QIcon symbolIcon = icon();
if ( symbolIcon.isNull() )
return QSizeF();

if ( ctx )
symbolIcon.paint( ctx->painter, ctx->point.x(), ctx->point.y() + ( itemHeight - settings.symbolSize().height() ) / 2,
settings.symbolSize().width(), settings.symbolSize().height() );
return settings.symbolSize();
}


QSizeF QgsComposerBaseSymbolItem::drawSymbolText( const QgsLegendSettings& settings, ItemContext* ctx, const QSizeF& symbolSize ) const
{
QSizeF labelSize( 0, 0 );

QFont symbolLabelFont = settings.style( QgsComposerLegendStyle::SymbolLabel ).font();
double textHeight = settings.fontHeightCharacterMM( symbolLabelFont, QChar( '0' ) );

QStringList lines = settings.splitStringForWrapping( text() );

labelSize.rheight() = lines.count() * textHeight + ( lines.count() - 1 ) * settings.lineSpacing();

double labelX, labelY;
if ( ctx )
{
ctx->painter->setPen( settings.fontColor() );

labelX = ctx->point.x() + qMax(( double ) symbolSize.width(), ctx->labelXOffset );
labelY = ctx->point.y();

// Vertical alignment of label with symbol
if ( labelSize.height() < symbolSize.height() )
labelY += symbolSize.height() / 2 + textHeight / 2; // label centered with symbol
else
labelY += textHeight; // label starts at top and runs under symbol
}

for ( QStringList::Iterator itemPart = lines.begin(); itemPart != lines.end(); ++itemPart )
{
labelSize.rwidth() = qMax( settings.textWidthMillimeters( symbolLabelFont, *itemPart ), double( labelSize.width() ) );

if ( ctx )
{
settings.drawText( ctx->painter, labelX, labelY, *itemPart , symbolLabelFont );
if ( itemPart != lines.end() )
labelY += settings.lineSpacing() + textHeight;
}
}

return labelSize;
}

////////////////QgsComposerSymbolV2Item

#include "qgssymbolv2.h"

QgsComposerSymbolV2Item::QgsComposerSymbolV2Item(): QgsComposerLegendItem( QgsComposerLegendStyle::Symbol ), mSymbolV2( 0 )
QgsComposerSymbolV2Item::QgsComposerSymbolV2Item()
: mSymbolV2( 0 )
{
}

QgsComposerSymbolV2Item::QgsComposerSymbolV2Item( const QString& text ): QgsComposerLegendItem( text, QgsComposerLegendStyle::Symbol ), mSymbolV2( 0 )
QgsComposerSymbolV2Item::QgsComposerSymbolV2Item( const QString& text )
: mSymbolV2( 0 )
{
setText( text );
}

QgsComposerSymbolV2Item::QgsComposerSymbolV2Item( const QIcon& icon, const QString& text ): QgsComposerLegendItem( icon, text, QgsComposerLegendStyle::Symbol ), mSymbolV2( 0 )
QgsComposerSymbolV2Item::QgsComposerSymbolV2Item( const QIcon& icon, const QString& text )
: mSymbolV2( 0 )
{
setIcon( icon );
setText( text );
}

QgsComposerSymbolV2Item::~QgsComposerSymbolV2Item()
Expand Down Expand Up @@ -145,18 +228,133 @@ void QgsComposerSymbolV2Item::setSymbolV2( QgsSymbolV2* s )
mSymbolV2 = s;
}

QSizeF QgsComposerSymbolV2Item::drawSymbol( const QgsLegendSettings& settings, ItemContext* ctx, double itemHeight ) const
{
QgsSymbolV2* s = symbolV2();
if ( !s )
{
return QSizeF();
}

//consider relation to composer map for symbol sizes in mm
bool sizeInMapUnits = s->outputUnit() == QgsSymbolV2::MapUnit;
QgsMarkerSymbolV2* markerSymbol = dynamic_cast<QgsMarkerSymbolV2*>( s );

//Consider symbol size for point markers
double height = settings.symbolSize().height();
double width = settings.symbolSize().width();
double size = 0;
//Center small marker symbols
double widthOffset = 0;
double heightOffset = 0;

if ( markerSymbol )
{
size = markerSymbol->size();
height = size;
width = size;
if ( sizeInMapUnits )
{
height *= settings.mmPerMapUnit();
width *= settings.mmPerMapUnit();
markerSymbol->setSize( width );
}
if ( width < settings.symbolSize().width() )
{
widthOffset = ( settings.symbolSize().width() - width ) / 2.0;
}
if ( height < settings.symbolSize().height() )
{
heightOffset = ( settings.symbolSize().height() - height ) / 2.0;
}
}

if ( ctx )
{
double currentXPosition = ctx->point.x();
double currentYCoord = ctx->point.y() + ( itemHeight - settings.symbolSize().height() ) / 2;
QPainter* p = ctx->painter;

//setup painter scaling to dots so that raster symbology is drawn to scale
double dotsPerMM = 1.0;
QPaintDevice* paintDevice = p->device();
if ( !paintDevice )
{
return QSizeF();
}
dotsPerMM = paintDevice->logicalDpiX() / 25.4;

if ( markerSymbol && sizeInMapUnits )
{
s->setOutputUnit( QgsSymbolV2::MM );
}

int opacity = 255;
if ( QgsComposerLayerItem* layerItem = dynamic_cast<QgsComposerLayerItem*>( parent() ) )
{
if ( QgsMapLayer* currentLayer = QgsMapLayerRegistry::instance()->mapLayer( layerItem->layerID() ) )
{
if ( QgsVectorLayer* vectorLayer = dynamic_cast<QgsVectorLayer*>( currentLayer ) )
{
opacity = 255 - ( 255 * vectorLayer->layerTransparency() / 100 );
}
}
}

p->save();
p->setRenderHint( QPainter::Antialiasing );
if ( opacity != 255 && settings.useAdvancedEffects() )
{
//semi transparent layer, so need to draw symbol to an image (to flatten it first)
//create image which is same size as legend rect, in case symbol bleeds outside its alloted space
QImage tempImage = QImage( QSize( width * dotsPerMM, height * dotsPerMM ), QImage::Format_ARGB32 );
QPainter imagePainter( &tempImage );
tempImage.fill( Qt::transparent );
imagePainter.translate( dotsPerMM * ( currentXPosition + widthOffset ),
dotsPerMM * ( currentYCoord + heightOffset ) );
s->drawPreviewIcon( &imagePainter, QSize( width * dotsPerMM, height * dotsPerMM ) );
//reduce opacity of image
imagePainter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
imagePainter.fillRect( tempImage.rect(), QColor( 0, 0, 0, opacity ) );
//draw rendered symbol image
p->scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
p->drawImage( 0, 0, tempImage );
}
else
{
p->translate( currentXPosition + widthOffset, currentYCoord + heightOffset );
p->scale( 1.0 / dotsPerMM, 1.0 / dotsPerMM );
s->drawPreviewIcon( p, QSize( width * dotsPerMM, height * dotsPerMM ) );
}
p->restore();

if ( markerSymbol && sizeInMapUnits )
{
s->setOutputUnit( QgsSymbolV2::MapUnit );
markerSymbol->setSize( size );
}
}

return QSizeF( qMax( width + 2 * widthOffset, settings.symbolSize().width() ),
qMax( height + 2 * heightOffset, settings.symbolSize().height() ) );
}


////////////////////QgsComposerRasterSymbolItem

QgsComposerRasterSymbolItem::QgsComposerRasterSymbolItem(): QgsComposerLegendItem( QgsComposerLegendStyle::Symbol )
QgsComposerRasterSymbolItem::QgsComposerRasterSymbolItem()
{
}

QgsComposerRasterSymbolItem::QgsComposerRasterSymbolItem( const QString& text ): QgsComposerLegendItem( text, QgsComposerLegendStyle::Symbol )
QgsComposerRasterSymbolItem::QgsComposerRasterSymbolItem( const QString& text )
{
setText( text );
}

QgsComposerRasterSymbolItem::QgsComposerRasterSymbolItem( const QIcon& icon, const QString& text ): QgsComposerLegendItem( icon, text, QgsComposerLegendStyle::Symbol )
QgsComposerRasterSymbolItem::QgsComposerRasterSymbolItem( const QIcon& icon, const QString& text )
{
setIcon( icon );
setText( text );
}

QgsComposerRasterSymbolItem::~QgsComposerRasterSymbolItem()
Expand Down Expand Up @@ -200,6 +398,44 @@ void QgsComposerRasterSymbolItem::readXML( const QDomElement& itemElem, bool xSe
}
}

QSizeF QgsComposerRasterSymbolItem::drawSymbol( const QgsLegendSettings& settings, ItemContext* ctx, double itemHeight ) const
{
// manage WMS lengendGraphic
// actual code recognise if it's a legend because it has an icon and it's text is empty => this is not good MV pattern implementation :(
QIcon symbolIcon = icon();
if ( !symbolIcon.isNull() && text().isEmpty() )
{
// find max size
QList<QSize> sizes = symbolIcon.availableSizes();
double maxWidth = 0;
double maxHeight = 0;
foreach ( QSize size, sizes )
{
if ( maxWidth < size.width() ) maxWidth = size.width();
if ( maxHeight < size.height() ) maxHeight = size.height();
}

// get and print legend
QImage legend = symbolIcon.pixmap( maxWidth, maxHeight ).toImage();
if ( ctx )
{
ctx->painter->drawImage( QRectF( ctx->point.x(), ctx->point.y(), settings.wmsLegendSize().width(), settings.wmsLegendSize().height() ),
legend, QRectF( 0, 0, maxWidth, maxHeight ) );
}
return settings.wmsLegendSize();
}
else
{
if ( ctx )
{
ctx->painter->setBrush( color() );
ctx->painter->drawRect( QRectF( ctx->point.x(), ctx->point.y() + ( itemHeight - settings.symbolSize().height() ) / 2,
settings.symbolSize().width(), settings.symbolSize().height() ) );
}
return settings.symbolSize();
}
}

////////////////////QgsComposerLayerItem

QgsComposerLayerItem::QgsComposerLayerItem(): QgsComposerLegendItem( QgsComposerLegendStyle::Subgroup )
Expand Down Expand Up @@ -309,6 +545,38 @@ void QgsComposerLayerItem::setDefaultStyle( double scaleDenominator, QString rul
}
}



QSizeF QgsComposerLayerItem::draw( const QgsLegendSettings& settings, QPainter* painter, QPointF point )
{
QSizeF size( 0, 0 );

//Let the user omit the layer title item by having an empty layer title string
if ( text().isEmpty() ) return size;

double y = point.y();

if ( painter ) painter->setPen( settings.fontColor() );

QFont layerFont = settings.style( style() ).font();

QStringList lines = settings.splitStringForWrapping( text() );
for ( QStringList::Iterator layerItemPart = lines.begin(); layerItemPart != lines.end(); ++layerItemPart )
{
y += settings.fontAscentMillimeters( layerFont );
if ( painter ) settings.drawText( painter, point.x(), y, *layerItemPart , layerFont );
qreal width = settings.textWidthMillimeters( layerFont, *layerItemPart );
size.rwidth() = qMax( width, size.width() );
if ( layerItemPart != lines.end() )
{
y += settings.lineSpacing();
}
}
size.rheight() = y - point.y();

return size;
}

////////////////////QgsComposerGroupItem

QgsComposerGroupItem::QgsComposerGroupItem(): QgsComposerLegendItem( QgsComposerLegendStyle::Group )
Expand Down Expand Up @@ -396,6 +664,36 @@ void QgsComposerGroupItem::readXML( const QDomElement& itemElem, bool xServerAva
}
}


QSizeF QgsComposerGroupItem::draw( const QgsLegendSettings& settings, QPainter* painter, QPointF point )
{
QSizeF size( 0, 0 );

double y = point.y();

if ( painter ) painter->setPen( settings.fontColor() );

QFont groupFont = settings.style( style() ).font();

QStringList lines = settings.splitStringForWrapping( text() );
for ( QStringList::Iterator groupPart = lines.begin(); groupPart != lines.end(); ++groupPart )
{
y += settings.fontAscentMillimeters( groupFont );
if ( painter ) settings.drawText( painter, point.x(), y, *groupPart, groupFont );
qreal width = settings.textWidthMillimeters( groupFont, *groupPart );
size.rwidth() = qMax( width, size.width() );
if ( groupPart != lines.end() )
{
y += settings.lineSpacing();
}
}
size.rheight() = y - point.y();
return size;
}




QgsComposerStyleItem::QgsComposerStyleItem(): QStandardItem()
{
}
Expand Down

0 comments on commit fda50ed

Please sign in to comment.