QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgscomposertable.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposertable.cpp
3  --------------------
4  begin : January 2010
5  copyright : (C) 2010 by Marco Hugentobler
6  email : marco at hugis dot net
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 "qgscomposertable.h"
19 #include "qgscomposertablecolumn.h"
20 #include "qgssymbollayerv2utils.h"
21 #include "qgscomposerutils.h"
22 #include <QPainter>
23 #include <QSettings>
24 
25 
27  : QgsComposerItem( composition )
28  , mLineTextDistance( 1.0 )
29  , mHeaderFontColor( Qt::black )
30  , mContentFontColor( Qt::black )
31  , mHeaderHAlignment( FollowColumn )
32  , mShowGrid( true )
33  , mGridStrokeWidth( 0.5 )
34  , mGridColor( QColor( 0, 0, 0 ) )
35 {
36  //get default composer font from settings
37  QSettings settings;
38  QString defaultFontString = settings.value( "/Composer/defaultFont" ).toString();
39  if ( !defaultFontString.isEmpty() )
40  {
41  mHeaderFont.setFamily( defaultFontString );
42  mContentFont.setFamily( defaultFontString );
43  }
44 }
45 
47 {
48  qDeleteAll( mColumns );
49  mColumns.clear();
50 }
51 
53 {
54  mMaxColumnWidthMap.clear();
55  mAttributeMaps.clear();
56 
57  //getFeatureAttributes
59  {
60  return;
61  }
62 
63  //since attributes have changed, we also need to recalculate the column widths
64  //and size of table
66 }
67 
68 void QgsComposerTable::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
69 {
70  Q_UNUSED( itemStyle );
71  Q_UNUSED( pWidget );
72  if ( !painter )
73  {
74  return;
75  }
76  if ( !shouldDrawItem() )
77  {
78  return;
79  }
80 
83  {
84  //exporting composition, so force an attribute refresh
85  //we do this in case vector layer has changed via an external source (eg, another database user)
87  }
88 
89  drawBackground( painter );
90  painter->save();
91  //antialiasing on
92  painter->setRenderHint( QPainter::Antialiasing, true );
93 
94  painter->setPen( Qt::SolidLine );
95 
96  //now draw the text
97  double currentX = mGridStrokeWidth;
98  double currentY;
99 
100  QList<QgsComposerTableColumn*>::const_iterator columnIt = mColumns.constBegin();
101 
102  int col = 0;
103  double cellHeaderHeight = QgsComposerUtils::fontAscentMM( mHeaderFont ) + 2 * mLineTextDistance;
104  double cellBodyHeight = QgsComposerUtils::fontAscentMM( mContentFont ) + 2 * mLineTextDistance;
105  QRectF cell;
106  for ( ; columnIt != mColumns.constEnd(); ++columnIt )
107  {
108  currentY = mGridStrokeWidth;
109  currentX += mLineTextDistance;
110 
111  cell = QRectF( currentX, currentY, mMaxColumnWidthMap[col], cellHeaderHeight );
112 
113  //calculate alignment of header
114  Qt::AlignmentFlag headerAlign = Qt::AlignLeft;
115  switch ( mHeaderHAlignment )
116  {
117  case FollowColumn:
118  headerAlign = ( *columnIt )->hAlignment();
119  break;
120  case HeaderLeft:
121  headerAlign = Qt::AlignLeft;
122  break;
123  case HeaderCenter:
124  headerAlign = Qt::AlignHCenter;
125  break;
126  case HeaderRight:
127  headerAlign = Qt::AlignRight;
128  break;
129  }
130 
131  QgsComposerUtils::drawText( painter, cell, ( *columnIt )->heading(), mHeaderFont, mHeaderFontColor, headerAlign, Qt::AlignVCenter, Qt::TextDontClip );
132 
133  currentY += cellHeaderHeight;
134  currentY += mGridStrokeWidth;
135 
136  //draw the attribute values
137  QList<QgsAttributeMap>::const_iterator attIt = mAttributeMaps.begin();
138  for ( ; attIt != mAttributeMaps.end(); ++attIt )
139  {
140  cell = QRectF( currentX, currentY, mMaxColumnWidthMap[col], cellBodyHeight );
141 
142  const QgsAttributeMap &currentAttributeMap = *attIt;
143  QString str = currentAttributeMap[ col ].toString();
144  QgsComposerUtils::drawText( painter, cell, str, mContentFont, mContentFontColor, ( *columnIt )->hAlignment(), Qt::AlignVCenter, Qt::TextDontClip );
145 
146  currentY += cellBodyHeight;
147  currentY += mGridStrokeWidth;
148  }
149 
150  currentX += mMaxColumnWidthMap[ col ];
151  currentX += mLineTextDistance;
152  currentX += mGridStrokeWidth;
153  col++;
154  }
155 
156  //and the borders
157  if ( mShowGrid )
158  {
159  QPen gridPen;
160  gridPen.setWidthF( mGridStrokeWidth );
161  gridPen.setColor( mGridColor );
162  gridPen.setJoinStyle( Qt::MiterJoin );
163  painter->setPen( gridPen );
164  drawHorizontalGridLines( painter, mAttributeMaps.size() );
166  }
167 
168  painter->restore();
169 
170  //draw frame and selection boxes if necessary
171  drawFrame( painter );
172  if ( isSelected() )
173  {
174  drawSelectionBoxes( painter );
175  }
176 }
177 
179 {
180  mLineTextDistance = d;
181  //since spacing has changed, we need to recalculate the table size
183 }
184 
185 void QgsComposerTable::setHeaderFont( const QFont& f )
186 {
187  mHeaderFont = f;
188  //since font attributes have changed, we need to recalculate the table size
190 }
191 
192 void QgsComposerTable::setHeaderFontColor( const QColor &color )
193 {
194  mHeaderFontColor = color;
195  repaint();
196 }
197 
199 {
200  mHeaderHAlignment = alignment;
201  repaint();
202 }
203 
204 void QgsComposerTable::setContentFont( const QFont& f )
205 {
206  mContentFont = f;
207  //since font attributes have changed, we need to recalculate the table size
209 }
210 
211 void QgsComposerTable::setContentFontColor( const QColor &color )
212 {
213  mContentFontColor = color;
214  repaint();
215 }
216 
218 {
219  mShowGrid = show;
220  //since grid spacing has changed, we need to recalculate the table size
222 }
223 
225 {
226  mGridStrokeWidth = w;
227  //since grid spacing has changed, we need to recalculate the table size
229 }
230 
232 {
233  //check how much space each column needs
235  {
236  return;
237  }
238  //adapt item frame to max width / height
240 
241  repaint();
242 }
243 
244 QMap<int, QString> QgsComposerTable::headerLabels() const
245 {
246  QMap<int, QString> headers;
247 
248  QList<QgsComposerTableColumn*>::const_iterator columnIt = mColumns.constBegin();
249  int col = 0;
250  for ( ; columnIt != mColumns.constEnd(); ++columnIt )
251  {
252  headers.insert( col, ( *columnIt )->heading() );
253  col++;
254  }
255  return headers;
256 }
257 
258 void QgsComposerTable::setColumns( QList<QgsComposerTableColumn*> columns )
259 {
260  //remove existing columns
261  qDeleteAll( mColumns );
262  mColumns.clear();
263 
264  mColumns.append( columns );
265 }
266 
267 bool QgsComposerTable::tableWriteXML( QDomElement& elem, QDomDocument & doc ) const
268 {
269  elem.setAttribute( "lineTextDist", QString::number( mLineTextDistance ) );
270  elem.setAttribute( "headerFont", mHeaderFont.toString() );
271  elem.setAttribute( "headerFontColor", QgsSymbolLayerV2Utils::encodeColor( mHeaderFontColor ) );
272  elem.setAttribute( "headerHAlignment", QString::number(( int )mHeaderHAlignment ) );
273  elem.setAttribute( "contentFont", mContentFont.toString() );
274  elem.setAttribute( "contentFontColor", QgsSymbolLayerV2Utils::encodeColor( mContentFontColor ) );
275  elem.setAttribute( "gridStrokeWidth", QString::number( mGridStrokeWidth ) );
276  elem.setAttribute( "gridColor", QgsSymbolLayerV2Utils::encodeColor( mGridColor ) );
277  elem.setAttribute( "showGrid", mShowGrid );
278 
279  //columns
280  QDomElement displayColumnsElem = doc.createElement( "displayColumns" );
281  QList<QgsComposerTableColumn*>::const_iterator columnIt = mColumns.constBegin();
282  for ( ; columnIt != mColumns.constEnd(); ++columnIt )
283  {
284  QDomElement columnElem = doc.createElement( "column" );
285  ( *columnIt )->writeXML( columnElem, doc );
286  displayColumnsElem.appendChild( columnElem );
287  }
288  elem.appendChild( displayColumnsElem );
289 
290  return _writeXML( elem, doc );
291 }
292 
293 bool QgsComposerTable::tableReadXML( const QDomElement& itemElem, const QDomDocument& doc )
294 {
295  if ( itemElem.isNull() )
296  {
297  return false;
298  }
299 
300  mHeaderFont.fromString( itemElem.attribute( "headerFont", "" ) );
301  mHeaderFontColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "headerFontColor", "0,0,0,255" ) );
302  mHeaderHAlignment = QgsComposerTable::HeaderHAlignment( itemElem.attribute( "headerHAlignment", "0" ).toInt() );
303  mContentFont.fromString( itemElem.attribute( "contentFont", "" ) );
304  mContentFontColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "contentFontColor", "0,0,0,255" ) );
305  mLineTextDistance = itemElem.attribute( "lineTextDist", "1.0" ).toDouble();
306  mGridStrokeWidth = itemElem.attribute( "gridStrokeWidth", "0.5" ).toDouble();
307  mShowGrid = itemElem.attribute( "showGrid", "1" ).toInt();
308 
309  //grid color
310  if ( itemElem.hasAttribute( "gridColor" ) )
311  {
312  mGridColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "gridColor", "0,0,0,255" ) );
313  }
314  else
315  {
316  //old style grid color
317  int gridRed = itemElem.attribute( "gridColorRed", "0" ).toInt();
318  int gridGreen = itemElem.attribute( "gridColorGreen", "0" ).toInt();
319  int gridBlue = itemElem.attribute( "gridColorBlue", "0" ).toInt();
320  int gridAlpha = itemElem.attribute( "gridColorAlpha", "255" ).toInt();
321  mGridColor = QColor( gridRed, gridGreen, gridBlue, gridAlpha );
322  }
323 
324  //restore column specifications
325  qDeleteAll( mColumns );
326  mColumns.clear();
327  QDomNodeList columnsList = itemElem.elementsByTagName( "displayColumns" );
328  if ( columnsList.size() > 0 )
329  {
330  QDomElement columnsElem = columnsList.at( 0 ).toElement();
331  QDomNodeList columnEntryList = columnsElem.elementsByTagName( "column" );
332  for ( int i = 0; i < columnEntryList.size(); ++i )
333  {
334  QDomElement columnElem = columnEntryList.at( i ).toElement();
336  column->readXML( columnElem );
337  mColumns.append( column );
338  }
339  }
340 
341  //restore general composer item properties
342  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
343  if ( composerItemList.size() > 0 )
344  {
345  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
346  _readXML( composerItemElem, doc );
347  }
348  return true;
349 }
350 
351 bool QgsComposerTable::calculateMaxColumnWidths( QMap<int, double>& maxWidthMap, const QList<QgsAttributeMap>& attributeMaps ) const
352 {
353  maxWidthMap.clear();
354  QList<QgsComposerTableColumn*>::const_iterator columnIt = mColumns.constBegin();
355 
356  int col = 0;
357  for ( ; columnIt != mColumns.constEnd(); ++columnIt )
358  {
359  maxWidthMap.insert( col, QgsComposerUtils::textWidthMM( mHeaderFont, ( *columnIt )->heading() ) );
360  col++;
361  }
362 
363  //go through all the attributes and adapt the max width values
364  QList<QgsAttributeMap>::const_iterator attIt = attributeMaps.constBegin();
365 
366  double currentAttributeTextWidth;
367 
368  for ( ; attIt != attributeMaps.constEnd(); ++attIt )
369  {
370  QgsAttributeMap::const_iterator attIt2 = attIt->constBegin();
371  for ( ; attIt2 != attIt->constEnd(); ++attIt2 )
372  {
373  currentAttributeTextWidth = QgsComposerUtils::textWidthMM( mContentFont, attIt2.value().toString() );
374  if ( currentAttributeTextWidth > maxWidthMap[ attIt2.key()] )
375  {
376  maxWidthMap[ attIt2.key()] = currentAttributeTextWidth;
377  }
378  }
379  }
380  return true;
381 }
382 
383 void QgsComposerTable::adaptItemFrame( const QMap<int, double>& maxWidthMap, const QList<QgsAttributeMap>& attributeMaps )
384 {
385  //calculate height
386  int n = attributeMaps.size();
387  double totalHeight = QgsComposerUtils::fontAscentMM( mHeaderFont )
389  + ( n + 1 ) * mLineTextDistance * 2
390  + ( n + 2 ) * mGridStrokeWidth;
391 
392  //adapt frame to total width
393  double totalWidth = 0;
394  QMap<int, double>::const_iterator maxColWidthIt = maxWidthMap.constBegin();
395  for ( ; maxColWidthIt != maxWidthMap.constEnd(); ++maxColWidthIt )
396  {
397  totalWidth += maxColWidthIt.value();
398  }
399  totalWidth += ( 2 * maxWidthMap.size() * mLineTextDistance );
400  totalWidth += ( maxWidthMap.size() + 1 ) * mGridStrokeWidth;
401 
402  QRectF evaluatedRect = evalItemRect( QRectF( pos().x(), pos().y(), totalWidth, totalHeight ) );
403 
404  //update rect for data defined size and position
405  QgsComposerItem::setSceneRect( evaluatedRect );
406 }
407 
408 void QgsComposerTable::drawHorizontalGridLines( QPainter* p, int nAttributes )
409 {
410  //horizontal lines
411  double halfGridStrokeWidth = mGridStrokeWidth / 2.0;
412  double currentY = halfGridStrokeWidth;
413  p->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( rect().width() - halfGridStrokeWidth, currentY ) );
414  currentY += mGridStrokeWidth;
416  for ( int i = 0; i < nAttributes; ++i )
417  {
418  p->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( rect().width() - halfGridStrokeWidth, currentY ) );
419  currentY += mGridStrokeWidth;
421  }
422  p->drawLine( QPointF( halfGridStrokeWidth, currentY ), QPointF( rect().width() - halfGridStrokeWidth, currentY ) );
423 }
424 
425 void QgsComposerTable::drawVerticalGridLines( QPainter* p, const QMap<int, double>& maxWidthMap )
426 {
427  //vertical lines
428  double halfGridStrokeWidth = mGridStrokeWidth / 2.0;
429  double currentX = halfGridStrokeWidth;
430  p->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, rect().height() - halfGridStrokeWidth ) );
431  currentX += mGridStrokeWidth;
432  QMap<int, double>::const_iterator maxColWidthIt = maxWidthMap.constBegin();
433  for ( ; maxColWidthIt != maxWidthMap.constEnd(); ++maxColWidthIt )
434  {
435  currentX += ( maxColWidthIt.value() + 2 * mLineTextDistance );
436  p->drawLine( QPointF( currentX, halfGridStrokeWidth ), QPointF( currentX, rect().height() - halfGridStrokeWidth ) );
437  currentX += mGridStrokeWidth;
438  }
439 }