~ubuntu-branches/ubuntu/wily/qgis/wily

« back to all changes in this revision

Viewing changes to src/core/composer/qgscomposerlegend.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Johan Van de Wauw
  • Date: 2010-07-11 20:23:24 UTC
  • mfrom: (3.1.4 squeeze)
  • Revision ID: james.westby@ubuntu.com-20100711202324-5ktghxa7hracohmr
Tags: 1.4.0+12730-3ubuntu1
* Merge from Debian unstable (LP: #540941).
* Fix compilation issues with QT 4.7
* Add build-depends on libqt4-webkit-dev 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
                         qgscomposerlegend.cpp  -  description
 
3
                         ---------------------
 
4
    begin                : June 2008
 
5
    copyright            : (C) 2008 by Marco Hugentobler
 
6
    email                : marco dot hugentobler at karto dot baug dot ethz dot ch
 
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 "qgscomposerlegend.h"
 
19
#include "qgsmaplayer.h"
 
20
#include "qgsmaplayerregistry.h"
 
21
#include "qgsmaprenderer.h"
 
22
#include "qgsrenderer.h" //for brush scaling
 
23
#include "qgssymbol.h"
 
24
#include <QDomDocument>
 
25
#include <QDomElement>
 
26
#include <QPainter>
 
27
 
 
28
QgsComposerLegend::QgsComposerLegend( QgsComposition* composition ): QgsComposerItem( composition ), mTitle( tr( "Legend" ) ), mBoxSpace( 2 ), mLayerSpace( 3 ), mSymbolSpace( 2 ), mIconLabelSpace( 2 )
 
29
{
 
30
  QStringList idList = layerIdList();
 
31
  mLegendModel.setLayerSet( idList );
 
32
 
 
33
  mTitleFont.setPointSizeF( 14.0 );
 
34
  mLayerFont.setPointSizeF( 12.0 );
 
35
  mItemFont.setPointSizeF( 12.0 );
 
36
 
 
37
  mSymbolWidth = 7;
 
38
  mSymbolHeight = 4;
 
39
  adjustBoxSize();
 
40
 
 
41
  connect( &mLegendModel, SIGNAL( layersChanged() ), this, SLOT( synchronizeWithModel() ) );
 
42
}
 
43
 
 
44
QgsComposerLegend::QgsComposerLegend(): QgsComposerItem( 0 )
 
45
{
 
46
 
 
47
}
 
48
 
 
49
QgsComposerLegend::~QgsComposerLegend()
 
50
{
 
51
 
 
52
}
 
53
 
 
54
void QgsComposerLegend::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
 
55
{
 
56
  paintAndDetermineSize( painter );
 
57
}
 
58
 
 
59
QSizeF QgsComposerLegend::paintAndDetermineSize( QPainter* painter )
 
60
{
 
61
  QSizeF size;
 
62
  double maxXCoord = 0;
 
63
 
 
64
 
 
65
 
 
66
  //go through model...
 
67
  QStandardItem* rootItem = mLegendModel.invisibleRootItem();
 
68
  if ( !rootItem )
 
69
  {
 
70
    return size;
 
71
  }
 
72
 
 
73
 
 
74
  if ( painter )
 
75
  {
 
76
    painter->save();
 
77
    drawBackground( painter );
 
78
    painter->setPen( QPen( QColor( 0, 0, 0 ) ) );
 
79
  }
 
80
 
 
81
  int numLayerItems = rootItem->rowCount();
 
82
  QStandardItem* currentLayerItem = 0;
 
83
  double currentYCoordinate = mBoxSpace;
 
84
 
 
85
  //font metrics
 
86
 
 
87
  //draw title
 
88
  currentYCoordinate += fontAscentMillimeters( mTitleFont );
 
89
  if ( painter )
 
90
  {
 
91
    painter->setPen( QColor( 0, 0, 0 ) );
 
92
    drawText( painter, mBoxSpace, currentYCoordinate, mTitle, mTitleFont );
 
93
  }
 
94
 
 
95
  maxXCoord = 2 * mBoxSpace + textWidthMillimeters( mTitleFont, mTitle );
 
96
 
 
97
  //draw only visible layer items
 
98
  QgsMapRenderer* theMapRenderer = mComposition->mapRenderer();
 
99
  QStringList visibleLayerIds;
 
100
  if ( theMapRenderer )
 
101
  {
 
102
    visibleLayerIds = theMapRenderer->layerSet();
 
103
  }
 
104
 
 
105
 
 
106
  for ( int i = 0; i < numLayerItems; ++i )
 
107
  {
 
108
    currentLayerItem = rootItem->child( i );
 
109
    if ( currentLayerItem )
 
110
    {
 
111
      QString currentLayerId = currentLayerItem->data().toString();
 
112
      int opacity = 255;
 
113
      QgsMapLayer* currentLayer = QgsMapLayerRegistry::instance()->mapLayer( currentLayerId );
 
114
      if ( currentLayer )
 
115
      {
 
116
        opacity = currentLayer->getTransparency();
 
117
      }
 
118
 
 
119
      if ( visibleLayerIds.contains( currentLayerId ) )
 
120
      {
 
121
        //Let the user omit the layer title item by having an empty layer title string
 
122
        if ( !currentLayerItem->text().isEmpty() )
 
123
        {
 
124
          currentYCoordinate += mLayerSpace;
 
125
          currentYCoordinate += fontAscentMillimeters( mLayerFont );
 
126
 
 
127
          //draw layer Item
 
128
          if ( painter )
 
129
          {
 
130
            painter->setPen( QColor( 0, 0, 0 ) );
 
131
            drawText( painter, mBoxSpace, currentYCoordinate, currentLayerItem->text(), mLayerFont );
 
132
          }
 
133
        }
 
134
 
 
135
        maxXCoord = std::max( maxXCoord, 2 * mBoxSpace + textWidthMillimeters( mLayerFont, currentLayerItem->text() ) );
 
136
 
 
137
        //and child items
 
138
        drawLayerChildItems( painter, currentLayerItem, currentYCoordinate, maxXCoord, opacity );
 
139
      }
 
140
    }
 
141
  }
 
142
 
 
143
  currentYCoordinate += mBoxSpace;
 
144
 
 
145
  if ( painter )
 
146
  {
 
147
    painter->restore();
 
148
 
 
149
    //draw frame and selection boxes if necessary
 
150
    drawFrame( painter );
 
151
    if ( isSelected() )
 
152
    {
 
153
      drawSelectionBoxes( painter );
 
154
    }
 
155
  }
 
156
 
 
157
  size.setHeight( currentYCoordinate );
 
158
  size.setWidth( maxXCoord );
 
159
 
 
160
  //adjust box if width or height is to small
 
161
  if ( painter && currentYCoordinate > rect().width() )
 
162
  {
 
163
    setSceneRect( QRectF( transform().dx(), transform().dy(), rect().width(), currentYCoordinate ) );
 
164
  }
 
165
  if ( painter && maxXCoord > rect().height() )
 
166
  {
 
167
    setSceneRect( QRectF( transform().dx(), transform().dy(), maxXCoord, rect().height() ) );
 
168
  }
 
169
 
 
170
  return size;
 
171
}
 
172
 
 
173
void QgsComposerLegend::adjustBoxSize()
 
174
{
 
175
  QSizeF size = paintAndDetermineSize( 0 );
 
176
  if ( size.isValid() )
 
177
  {
 
178
    setSceneRect( QRectF( transform().dx(), transform().dy(), size.width(), size.height() ) );
 
179
  }
 
180
}
 
181
 
 
182
void QgsComposerLegend::drawLayerChildItems( QPainter* p, QStandardItem* layerItem, double& currentYCoord, double& maxXCoord, int layerOpacity )
 
183
{
 
184
  if ( !layerItem )
 
185
  {
 
186
    return;
 
187
  }
 
188
 
 
189
  //standerd item height
 
190
  double itemHeight = std::max( mSymbolHeight, fontAscentMillimeters( mItemFont ) );
 
191
 
 
192
  QStandardItem* currentItem;
 
193
 
 
194
  int numChildren = layerItem->rowCount();
 
195
 
 
196
  for ( int i = 0; i < numChildren; ++i )
 
197
  {
 
198
    //real symbol height. Can be different from standard height in case of point symbols
 
199
    double realSymbolHeight;
 
200
    double realItemHeight = itemHeight; //will be adjusted if realSymbolHeight turns out to be larger
 
201
 
 
202
    currentYCoord += mSymbolSpace;
 
203
    double currentXCoord = mBoxSpace;
 
204
 
 
205
    currentItem = layerItem->child( i, 0 );
 
206
 
 
207
    if ( !currentItem )
 
208
    {
 
209
      continue;
 
210
    }
 
211
 
 
212
    //take QgsSymbol* from user data
 
213
    QVariant symbolVariant = currentItem->data();
 
214
    QgsSymbol* symbol = 0;
 
215
    if ( symbolVariant.canConvert<void*>() )
 
216
    {
 
217
      void* symbolData = symbolVariant.value<void*>();
 
218
      symbol = ( QgsSymbol* )( symbolData );
 
219
    }
 
220
 
 
221
    if ( symbol )  //item with symbol?
 
222
    {
 
223
      //draw symbol
 
224
      drawSymbol( p, symbol, currentYCoord + ( itemHeight - mSymbolHeight ) / 2, currentXCoord, realSymbolHeight, layerOpacity );
 
225
      realItemHeight = std::max( realSymbolHeight, itemHeight );
 
226
      currentXCoord += mIconLabelSpace;
 
227
    }
 
228
    else //item with icon?
 
229
    {
 
230
      QIcon symbolIcon = currentItem->icon();
 
231
      if ( !symbolIcon.isNull() && p )
 
232
      {
 
233
        symbolIcon.paint( p, currentXCoord, currentYCoord, mSymbolWidth, mSymbolHeight );
 
234
        currentXCoord += mSymbolWidth;
 
235
        currentXCoord += mIconLabelSpace;
 
236
      }
 
237
    }
 
238
 
 
239
    //finally draw text
 
240
    if ( p )
 
241
    {
 
242
      p->setPen( QColor( 0, 0, 0 ) );
 
243
      drawText( p, currentXCoord, currentYCoord + fontAscentMillimeters( mItemFont ) + ( realItemHeight - fontAscentMillimeters( mItemFont ) ) / 2, currentItem->text(), mItemFont );
 
244
    }
 
245
 
 
246
    maxXCoord = std::max( maxXCoord, currentXCoord + textWidthMillimeters( mItemFont, currentItem->text() ) + mBoxSpace );
 
247
 
 
248
    currentYCoord += realItemHeight;
 
249
  }
 
250
}
 
251
 
 
252
void QgsComposerLegend::drawSymbol( QPainter* p, QgsSymbol* s, double currentYCoord, double& currentXPosition, double& symbolHeight, int layerOpacity ) const
 
253
{
 
254
  if ( !s )
 
255
  {
 
256
    return;
 
257
  }
 
258
 
 
259
  QGis::GeometryType symbolType = s->type();
 
260
  switch ( symbolType )
 
261
  {
 
262
    case QGis::Point:
 
263
      drawPointSymbol( p, s, currentYCoord, currentXPosition, symbolHeight, layerOpacity );
 
264
      break;
 
265
    case QGis::Line:
 
266
      drawLineSymbol( p, s, currentYCoord, currentXPosition, layerOpacity );
 
267
      symbolHeight = mSymbolHeight;
 
268
      break;
 
269
    case QGis::Polygon:
 
270
      drawPolygonSymbol( p, s, currentYCoord, currentXPosition, layerOpacity );
 
271
      symbolHeight = mSymbolHeight;
 
272
      break;
 
273
    case QGis::UnknownGeometry:
 
274
      // shouldn't occur
 
275
      break;
 
276
  }
 
277
}
 
278
 
 
279
void QgsComposerLegend::drawPointSymbol( QPainter* p, QgsSymbol* s, double currentYCoord, double& currentXPosition, double& symbolHeight, int opacity ) const
 
280
{
 
281
  if ( !s )
 
282
  {
 
283
    return;
 
284
  }
 
285
 
 
286
  QImage pointImage;
 
287
  double rasterScaleFactor = 1.0;
 
288
  if ( p )
 
289
  {
 
290
    QPaintDevice* paintDevice = p->device();
 
291
    if ( !paintDevice )
 
292
    {
 
293
      return;
 
294
    }
 
295
 
 
296
    rasterScaleFactor = ( paintDevice->logicalDpiX() + paintDevice->logicalDpiY() ) / 2.0 / 25.4;
 
297
  }
 
298
 
 
299
  //width scale is 1.0
 
300
  pointImage = s->getPointSymbolAsImage( 1.0, false, Qt::yellow, 1.0, 0.0, rasterScaleFactor, opacity / 255.0 );
 
301
 
 
302
  if ( p )
 
303
  {
 
304
    p->save();
 
305
    p->scale( 1.0 / rasterScaleFactor, 1.0 / rasterScaleFactor );
 
306
 
 
307
    QPointF imageTopLeft( currentXPosition * rasterScaleFactor, currentYCoord * rasterScaleFactor );
 
308
    p->drawImage( imageTopLeft, pointImage );
 
309
    p->restore();
 
310
  }
 
311
 
 
312
  currentXPosition += s->pointSize(); //pointImage.width() / rasterScaleFactor;
 
313
  symbolHeight = s->pointSize(); //pointImage.height() / rasterScaleFactor;
 
314
}
 
315
 
 
316
void QgsComposerLegend::drawLineSymbol( QPainter* p, QgsSymbol* s, double currentYCoord, double& currentXPosition, int opacity ) const
 
317
{
 
318
  if ( !s )
 
319
  {
 
320
    return;
 
321
  }
 
322
 
 
323
  double yCoord = currentYCoord + mSymbolHeight / 2;
 
324
 
 
325
  if ( p )
 
326
  {
 
327
    p->save();
 
328
    QPen symbolPen = s->pen();
 
329
    QColor penColor = symbolPen.color();
 
330
    penColor.setAlpha( opacity );
 
331
    symbolPen.setColor( penColor );
 
332
    symbolPen.setCapStyle( Qt::FlatCap );
 
333
    p->setPen( symbolPen );
 
334
    p->drawLine( QPointF( currentXPosition, yCoord ), QPointF( currentXPosition + mSymbolWidth, yCoord ) );
 
335
    p->restore();
 
336
  }
 
337
 
 
338
  currentXPosition += mSymbolWidth;
 
339
}
 
340
 
 
341
void QgsComposerLegend::drawPolygonSymbol( QPainter* p, QgsSymbol* s, double currentYCoord, double& currentXPosition, int opacity ) const
 
342
{
 
343
  if ( !s )
 
344
  {
 
345
    return;
 
346
  }
 
347
 
 
348
  if ( p )
 
349
  {
 
350
    //scale brush and set transparencies
 
351
    QBrush symbolBrush = s->brush();
 
352
    QColor brushColor = symbolBrush.color();
 
353
    brushColor.setAlpha( opacity );
 
354
    symbolBrush.setColor( brushColor );
 
355
    QPaintDevice* paintDevice = p->device();
 
356
    if ( paintDevice )
 
357
    {
 
358
      double rasterScaleFactor = ( paintDevice->logicalDpiX() + paintDevice->logicalDpiY() ) / 2.0 / 25.4;
 
359
      QgsRenderer::scaleBrush( symbolBrush, rasterScaleFactor );
 
360
    }
 
361
    p->setBrush( symbolBrush );
 
362
 
 
363
    QPen symbolPen = s->pen();
 
364
    QColor penColor = symbolPen.color();
 
365
    penColor.setAlpha( opacity );
 
366
    symbolPen.setColor( penColor );
 
367
    p->setPen( symbolPen );
 
368
 
 
369
    p->drawRect( QRectF( currentXPosition, currentYCoord, mSymbolWidth, mSymbolHeight ) );
 
370
  }
 
371
 
 
372
  currentXPosition += mSymbolWidth;
 
373
}
 
374
 
 
375
QStringList QgsComposerLegend::layerIdList() const
 
376
{
 
377
  QStringList layerIdList;
 
378
  QMap<QString, QgsMapLayer*> layerMap =  QgsMapLayerRegistry::instance()->mapLayers();
 
379
  QMap<QString, QgsMapLayer*>::const_iterator mapIt = layerMap.constBegin();
 
380
 
 
381
  for ( ; mapIt != layerMap.constEnd(); ++mapIt )
 
382
  {
 
383
    layerIdList.push_back( mapIt.key() );
 
384
  }
 
385
 
 
386
  return layerIdList;
 
387
}
 
388
 
 
389
void QgsComposerLegend::synchronizeWithModel()
 
390
{
 
391
  adjustBoxSize();
 
392
  update();
 
393
}
 
394
 
 
395
void QgsComposerLegend::setTitleFont( const QFont& f )
 
396
{
 
397
  mTitleFont = f;
 
398
  adjustBoxSize();
 
399
  update();
 
400
}
 
401
 
 
402
void QgsComposerLegend::setLayerFont( const QFont& f )
 
403
{
 
404
  mLayerFont = f;
 
405
  adjustBoxSize();
 
406
  update();
 
407
}
 
408
 
 
409
void QgsComposerLegend::setItemFont( const QFont& f )
 
410
{
 
411
  mItemFont = f;
 
412
  adjustBoxSize();
 
413
  update();
 
414
}
 
415
 
 
416
QFont QgsComposerLegend::titleFont() const
 
417
{
 
418
  return mTitleFont;
 
419
}
 
420
 
 
421
QFont QgsComposerLegend::layerFont() const
 
422
{
 
423
  return mLayerFont;
 
424
}
 
425
 
 
426
QFont QgsComposerLegend::itemFont() const
 
427
{
 
428
  return mItemFont;
 
429
}
 
430
 
 
431
void QgsComposerLegend::updateLegend()
 
432
{
 
433
  mLegendModel.setLayerSet( layerIdList() );
 
434
  adjustBoxSize();
 
435
  update();
 
436
}
 
437
 
 
438
bool QgsComposerLegend::writeXML( QDomElement& elem, QDomDocument & doc ) const
 
439
{
 
440
  if ( elem.isNull() )
 
441
  {
 
442
    return false;
 
443
  }
 
444
 
 
445
  QDomElement composerLegendElem = doc.createElement( "ComposerLegend" );
 
446
 
 
447
  //write general properties
 
448
  composerLegendElem.setAttribute( "title", mTitle );
 
449
  composerLegendElem.setAttribute( "titleFont", mTitleFont.toString() );
 
450
  composerLegendElem.setAttribute( "layerFont", mLayerFont.toString() );
 
451
  composerLegendElem.setAttribute( "itemFont", mItemFont.toString() );
 
452
  composerLegendElem.setAttribute( "boxSpace", QString::number( mBoxSpace ) );
 
453
  composerLegendElem.setAttribute( "layerSpace", QString::number( mLayerSpace ) );
 
454
  composerLegendElem.setAttribute( "symbolSpace", QString::number( mSymbolSpace ) );
 
455
  composerLegendElem.setAttribute( "iconLabelSpace", QString::number( mIconLabelSpace ) );
 
456
  composerLegendElem.setAttribute( "symbolWidth", mSymbolWidth );
 
457
  composerLegendElem.setAttribute( "symbolHeight", mSymbolHeight );
 
458
 
 
459
  //write model properties
 
460
  mLegendModel.writeXML( composerLegendElem, doc );
 
461
 
 
462
  elem.appendChild( composerLegendElem );
 
463
  return _writeXML( composerLegendElem, doc );
 
464
}
 
465
 
 
466
bool QgsComposerLegend::readXML( const QDomElement& itemElem, const QDomDocument& doc )
 
467
{
 
468
  if ( itemElem.isNull() )
 
469
  {
 
470
    return false;
 
471
  }
 
472
 
 
473
  //read general properties
 
474
  mTitle = itemElem.attribute( "title" );
 
475
  //title font
 
476
  QString titleFontString = itemElem.attribute( "titleFont" );
 
477
  if ( !titleFontString.isEmpty() )
 
478
  {
 
479
    mTitleFont.fromString( titleFontString );
 
480
  }
 
481
  //layer font
 
482
  QString layerFontString = itemElem.attribute( "layerFont" );
 
483
  if ( !layerFontString.isEmpty() )
 
484
  {
 
485
    mLayerFont.fromString( layerFontString );
 
486
  }
 
487
  //item font
 
488
  QString itemFontString = itemElem.attribute( "itemFont" );
 
489
  if ( !itemFontString.isEmpty() )
 
490
  {
 
491
    mItemFont.fromString( itemFontString );
 
492
  }
 
493
 
 
494
  //spaces
 
495
  mBoxSpace = itemElem.attribute( "boxSpace", "2.0" ).toDouble();
 
496
  mLayerSpace = itemElem.attribute( "layerSpace", "3.0" ).toDouble();
 
497
  mSymbolSpace = itemElem.attribute( "symbolSpace", "2.0" ).toDouble();
 
498
  mIconLabelSpace = itemElem.attribute( "iconLabelSpace", "2.0" ).toDouble();
 
499
  mSymbolWidth = itemElem.attribute( "symbolWidth", "7.0" ).toDouble();
 
500
  mSymbolHeight = itemElem.attribute( "symbolHeight", "14.0" ).toDouble();
 
501
 
 
502
  //read model properties
 
503
  QDomNodeList modelNodeList = itemElem.elementsByTagName( "Model" );
 
504
  if ( modelNodeList.size() > 0 )
 
505
  {
 
506
    QDomElement modelElem = modelNodeList.at( 0 ).toElement();
 
507
    mLegendModel.clear();
 
508
    mLegendModel.readXML( modelElem, doc );
 
509
  }
 
510
 
 
511
  //restore general composer item properties
 
512
  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
 
513
  if ( composerItemList.size() > 0 )
 
514
  {
 
515
    QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
 
516
    _readXML( composerItemElem, doc );
 
517
  }
 
518
 
 
519
  return true;
 
520
}