Quantum GIS API Documentation  1.7.5-Wroclaw
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
qgslegendmodel.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslegendmodel.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 "qgslegendmodel.h"
19 #include "qgscomposerlegenditem.h"
20 #include "qgsfield.h"
21 #include "qgsmaplayer.h"
22 #include "qgsmaplayerregistry.h"
23 #include "qgsrasterlayer.h"
24 #include "qgsrenderer.h"
25 #include "qgsrendererv2.h"
26 #include "qgssymbollayerv2utils.h"
27 #include "qgssymbol.h"
28 #include "qgsvectordataprovider.h"
29 #include "qgsvectorlayer.h"
30 #include <QApplication>
31 #include <QDomDocument>
32 #include <QDomElement>
33 #include <QMimeData>
34 #include <QSettings>
35 #include <QMessageBox>
36 
37 QgsLegendModel::QgsLegendModel(): QStandardItemModel(), mAutoUpdate( true )
38 {
40  {
41  connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( removeLayer( const QString& ) ) );
42  connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( addLayer( QgsMapLayer* ) ) );
43  }
44  setItemPrototype( new QgsComposerSymbolItem() );
45 
46  QWidgetList topLevelWidgets = QApplication::topLevelWidgets();
47  mHasTopLevelWindow = ( topLevelWidgets.size() > 0 );
48 }
49 
51 {
52 }
53 
54 void QgsLegendModel::setLayerSetAndGroups( const QStringList& layerIds, const QList< GroupLayerInfo >& groupInfo )
55 {
56  setLayerSet( layerIds );
57 
58  QStandardItem* currentItem = 0;
59  QStandardItem* currentGroupItem = 0;
60  int i = 0;
61 
62  QList< GroupLayerInfo >::const_iterator infoIt = groupInfo.constBegin();
63  for ( ; infoIt != groupInfo.constEnd() && i < invisibleRootItem()->rowCount(); )
64  {
65  currentItem = invisibleRootItem()->child( i, 0 );
66  QString infoKey = infoIt->first;
67  if ( infoKey.isNull() ) //a toplevel layer
68  {
69  ++i;
70  }
71  else //a group
72  {
73  currentGroupItem = addGroup( infoKey, i );
74  ++i;
75  QList<QString> layerList = infoIt->second;
76  QList<QString>::const_iterator groupLayerIt = layerList.constBegin();
77  for ( ; currentItem && ( groupLayerIt != layerList.constEnd() ); ++groupLayerIt )
78  {
79  //check if current item is contained in this group
80  QgsComposerLayerItem* layerItem = dynamic_cast<QgsComposerLayerItem*>( currentItem );
81  if ( !layerItem )
82  {
83  return; //should never happen
84  }
85  //QString layerID = currentItem->data(Qt::UserRole + 2).toString();
86  QString layerID = layerItem->layerID();
87  if ( layerList.contains( layerID ) )
88  {
89  takeRow( i );
90  currentGroupItem->setChild( currentGroupItem->rowCount(), 0, currentItem );
91  }
92  else
93  {
94  ++i;
95  }
96  currentItem = invisibleRootItem()->child( i, 0 );
97  }
98  }
99  ++infoIt;
100  }
101 }
102 
103 void QgsLegendModel::setLayerSet( const QStringList& layerIds )
104 {
105  mLayerIds = layerIds;
106 
107  //for now clear the model and add the new entries
108  clear();
109 
110  QStringList::const_iterator idIter = mLayerIds.constBegin();
111  QgsMapLayer* currentLayer = 0;
112 
113  for ( ; idIter != mLayerIds.constEnd(); ++idIter )
114  {
115  currentLayer = QgsMapLayerRegistry::instance()->mapLayer( *idIter );
116  addLayer( currentLayer );
117  }
118 }
119 
120 QStandardItem* QgsLegendModel::addGroup( QString text, int position )
121 {
122  QgsComposerGroupItem* groupItem = new QgsComposerGroupItem( text );
123  if ( position == -1 )
124  {
125  invisibleRootItem()->insertRow( invisibleRootItem()->rowCount(), groupItem );
126  }
127  else
128  {
129  invisibleRootItem()->insertRow( position, groupItem );
130  }
131  return groupItem;
132 }
133 
134 int QgsLegendModel::addVectorLayerItemsV2( QStandardItem* layerItem, QgsVectorLayer* vlayer )
135 {
136  if ( !layerItem || !vlayer )
137  {
138  return 1;
139  }
140 
141  QgsFeatureRendererV2* renderer = vlayer->rendererV2();
142  if ( !renderer )
143  {
144  return 2;
145  }
146 
147  QgsLegendSymbolList lst = renderer->legendSymbolItems();
148  QgsLegendSymbolList::const_iterator symbolIt = lst.constBegin();
149  for ( ; symbolIt != lst.constEnd(); ++symbolIt )
150  {
151  QgsComposerSymbolV2Item* currentSymbolItem = new QgsComposerSymbolV2Item( symbolIt->first );
152  currentSymbolItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
153  if ( symbolIt->second )
154  {
155  if ( mHasTopLevelWindow ) //only use QIcon / QPixmap if we have a running x-server
156  {
157  currentSymbolItem->setIcon( QgsSymbolLayerV2Utils::symbolPreviewIcon( symbolIt->second, QSize( 30, 30 ) ) );
158  }
159  currentSymbolItem->setSymbolV2( symbolIt->second->clone() );
160  }
161  layerItem->setChild( layerItem->rowCount(), 0, currentSymbolItem );
162  }
163 
164  return 0;
165 }
166 
167 int QgsLegendModel::addVectorLayerItems( QStandardItem* layerItem, QgsVectorLayer* vlayer )
168 {
169  if ( !layerItem || !vlayer )
170  {
171  return 1;
172  }
173 
174  int opacity = vlayer->getTransparency();
175 
176  const QgsRenderer* vectorRenderer = vlayer->renderer();
177  if ( !vectorRenderer )
178  {
179  return 3;
180  }
181 
182  //text field that describes classification attribute?
183  QSettings settings;
184  if ( settings.value( "/qgis/showLegendClassifiers", false ).toBool() )
185  {
186  QgsFieldMap layerFields = vlayer->pendingFields();
187  QgsAttributeList attributes = vectorRenderer->classificationAttributes();
188  QgsAttributeList::const_iterator att_it = attributes.constBegin();
189  for ( ; att_it != attributes.constEnd(); ++att_it )
190  {
191  QgsFieldMap::const_iterator fieldIt = layerFields.find( *att_it );
192  if ( fieldIt != layerFields.constEnd() )
193  {
194  QString attributeName = vlayer->attributeDisplayName( fieldIt.key() );
195  QStandardItem* attributeItem = new QStandardItem( attributeName );
196  attributeItem->setData( QgsLegendModel::ClassificationItem, Qt::UserRole + 1 ); //first user data stores the item type
197  attributeItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
198  layerItem->setChild( layerItem->rowCount(), 0, attributeItem );
199  }
200  }
201  }
202 
203  const QList<QgsSymbol*> vectorSymbols = vectorRenderer->symbols();
204  QList<QgsSymbol*>::const_iterator symbolIt = vectorSymbols.constBegin();
205 
206  for ( ; symbolIt != vectorSymbols.constEnd(); ++symbolIt )
207  {
208  if ( !( *symbolIt ) )
209  {
210  continue;
211  }
212 
213  QStandardItem* currentSymbolItem = itemFromSymbol( *symbolIt, opacity, vlayer->id() );
214  if ( !currentSymbolItem )
215  {
216  continue;
217  }
218 
219  layerItem->setChild( layerItem->rowCount(), 0, currentSymbolItem );
220 
221  }
222 
223  return 0;
224 }
225 
226 int QgsLegendModel::addRasterLayerItem( QStandardItem* layerItem, QgsMapLayer* rlayer )
227 {
228  if ( !layerItem || !rlayer )
229  {
230  return 1;
231  }
232 
233  QgsRasterLayer* rasterLayer = qobject_cast<QgsRasterLayer *>( rlayer );
234  if ( !rasterLayer )
235  {
236  return 2;
237  }
238 
239  QgsComposerRasterSymbolItem* currentSymbolItem = new QgsComposerRasterSymbolItem();
240  //use a vector symbol item without symbol
241  if ( mHasTopLevelWindow ) //only use QIcon / QPixmap if we have a running x-server
242  {
243  currentSymbolItem->setIcon( QIcon( rasterLayer->legendAsPixmap( true ) ) );
244  }
245  currentSymbolItem->setLayerID( rasterLayer->id() );
246  int currentRowCount = layerItem->rowCount();
247  layerItem->setChild( currentRowCount, 0, currentSymbolItem );
248 
249  return 0;
250 }
251 
252 void QgsLegendModel::updateItem( QStandardItem* item )
253 {
254  if ( !item )
255  {
256  return;
257  }
258 
259  //only layer items are supported for update
260  QgsComposerLegendItem* cItem = dynamic_cast<QgsComposerLegendItem*>( item );
261  if ( ! cItem )
262  {
263  return;
264  }
265 
267  if ( type == QgsComposerLegendItem::LayerItem )
268  {
269  updateLayer( cItem );
270  }
271 }
272 
273 void QgsLegendModel::updateLayer( QStandardItem* layerItem )
274 {
275  QgsComposerLayerItem* lItem = dynamic_cast<QgsComposerLayerItem*>( layerItem );
276  if ( lItem )
277  {
278  QgsMapLayer* mapLayer = QgsMapLayerRegistry::instance()->mapLayer( lItem->layerID() );
279  if ( mapLayer )
280  {
281  //delete all the entries under layer item
282  int currentRowCount = lItem->rowCount();
283  for ( int i = currentRowCount - 1; i >= 0; --i )
284  {
285  lItem->removeRow( i );
286  }
287 
288  //set layer name as item text
289  layerItem->setText( mapLayer->name() );
290 
291  QgsVectorLayer* vLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
292  if ( vLayer )
293  {
294  if ( vLayer->isUsingRendererV2() )
295  {
296  addVectorLayerItemsV2( lItem, vLayer );
297  }
298  else
299  {
300  addVectorLayerItems( lItem, vLayer );
301  }
302  }
303 
304  QgsRasterLayer* rLayer = qobject_cast<QgsRasterLayer*>( mapLayer );
305  if ( rLayer )
306  {
307  addRasterLayerItem( lItem, rLayer );
308  }
309  }
310  }
311 }
312 
313 void QgsLegendModel::removeLayer( const QString& layerId )
314 {
315  int numRootItems = rowCount();
316  for ( int i = 0; i < numRootItems ; ++i )
317  {
318  QgsComposerLayerItem* lItem = dynamic_cast<QgsComposerLayerItem*>( item( i ) );
319  if ( !lItem )
320  {
321  continue;
322  }
323 
324  if ( layerId == lItem->layerID() )
325  {
326  removeRow( i ); //todo: also remove the subitems and their symbols...
327  emit layersChanged();
328  return;
329  }
330  }
331 }
332 
334 {
335  if ( !theMapLayer )
336  {
337  return;
338  }
339 
340  QgsComposerLayerItem* layerItem = new QgsComposerLayerItem( theMapLayer->name() );
341  layerItem->setLayerID( theMapLayer->id() );
342  layerItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
343 
344  invisibleRootItem()->setChild( invisibleRootItem()->rowCount(), layerItem );
345 
346  switch ( theMapLayer->type() )
347  {
349  {
350  QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( theMapLayer );
351  if ( vl )
352  {
353  if ( vl->isUsingRendererV2() )
354  {
355  addVectorLayerItemsV2( layerItem, vl );
356  }
357  else
358  {
359  addVectorLayerItems( layerItem, vl );
360  }
361  }
362  break;
363  }
365  addRasterLayerItem( layerItem, theMapLayer );
366  break;
367  default:
368  break;
369  }
370  emit layersChanged();
371 }
372 
373 QStandardItem* QgsLegendModel::itemFromSymbol( QgsSymbol* s, int opacity, const QString& layerID )
374 {
375  QgsComposerSymbolItem* currentSymbolItem = 0;
376 
377  //label
378  QString itemText;
379  QString label;
380 
381  QString lowerValue = s->lowerValue();
382  QString upperValue = s->upperValue();
383 
384  label = s->label();
385 
386  //Take the label as item text if it is there
387  if ( !label.isEmpty() )
388  {
389  itemText = label;
390  }
391  //take single value
392  else if ( lowerValue == upperValue || upperValue.isEmpty() )
393  {
394  itemText = lowerValue;
395  }
396  else //or value range
397  {
398  itemText = lowerValue + " - " + upperValue;
399  }
400 
401  //icon item
402  QImage symbolImage;
403  switch ( s->type() )
404  {
405  case QGis::Point:
406  symbolImage = s->getPointSymbolAsImage();
407  break;
408  case QGis::Line:
409  symbolImage = s->getLineSymbolAsImage();
410  break;
411  case QGis::Polygon:
412  symbolImage = s->getPolygonSymbolAsImage();
413  break;
414  default:
415  return 0;
416  }
417 
418  if ( opacity != 255 )
419  {
420  //todo: manipulate image pixel by pixel...
421  QRgb oldColor;
422  for ( int i = 0; i < symbolImage.height(); ++i )
423  {
424  QRgb* scanLineBuffer = ( QRgb* ) symbolImage.scanLine( i );
425  for ( int j = 0; j < symbolImage.width(); ++j )
426  {
427  oldColor = symbolImage.pixel( j, i );
428  scanLineBuffer[j] = qRgba( qRed( oldColor ), qGreen( oldColor ), qBlue( oldColor ), opacity );
429  }
430  }
431  }
432 
433  currentSymbolItem = new QgsComposerSymbolItem( itemText );
434  if ( mHasTopLevelWindow )//only use QIcon / QPixmap if we have a running x-server
435  {
436  currentSymbolItem->setIcon( QIcon( QPixmap::fromImage( symbolImage ) ) );
437  }
438 
439  if ( !currentSymbolItem )
440  {
441  return 0;
442  }
443 
444  //Pass deep copy of QgsSymbol as user data. Cast to void* necessary such that QMetaType handles it
445  QgsSymbol* symbolCopy = new QgsSymbol( *s );
446  currentSymbolItem->setSymbol( symbolCopy );
447  currentSymbolItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
448  currentSymbolItem ->setLayerID( layerID );
449  return currentSymbolItem;
450 }
451 
452 bool QgsLegendModel::writeXML( QDomElement& composerLegendElem, QDomDocument& doc ) const
453 {
454  if ( composerLegendElem.isNull() )
455  {
456  return false;
457  }
458 
459  QDomElement legendModelElem = doc.createElement( "Model" );
460  legendModelElem.setAttribute( "autoUpdate", mAutoUpdate );
461  int nTopLevelItems = invisibleRootItem()->rowCount();
462  QStandardItem* currentItem = 0;
463  QgsComposerLegendItem* currentLegendItem = 0;
464 
465  for ( int i = 0; i < nTopLevelItems; ++i )
466  {
467  currentItem = invisibleRootItem()->child( i, 0 );
468  currentLegendItem = dynamic_cast<QgsComposerLegendItem*>( currentItem );
469  if ( currentLegendItem )
470  {
471  currentLegendItem->writeXML( legendModelElem, doc );
472  }
473  }
474 
475  composerLegendElem.appendChild( legendModelElem );
476  return true;
477 }
478 
479 bool QgsLegendModel::readXML( const QDomElement& legendModelElem, const QDomDocument& doc )
480 {
481  if ( legendModelElem.isNull() )
482  {
483  return false;
484  }
485 
486  clear();
487 
488  QDomNodeList topLevelItemList = legendModelElem.childNodes();
489  QDomElement currentElem;
490  QgsComposerLegendItem* currentItem = 0;
491 
492  int nTopLevelItems = topLevelItemList.size();
493  for ( int i = 0; i < nTopLevelItems; ++i )
494  {
495  currentElem = topLevelItemList.at( i ).toElement();
496  if ( currentElem.isNull() )
497  {
498  continue;
499  }
500 
501  //toplevel items can be groups or layers
502  if ( currentElem.tagName() == "LayerItem" )
503  {
504  currentItem = new QgsComposerLayerItem();
505  }
506  else if ( currentElem.tagName() == "GroupItem" )
507  {
508  currentItem = new QgsComposerGroupItem();
509  }
510  currentItem->readXML( currentElem, mHasTopLevelWindow );
511  appendRow( currentItem );
512  }
513 
514  setAutoUpdate( legendModelElem.attribute( "autoUpdate", "1" ).toInt() );
515  return true;
516 }
517 
519 {
520  return Qt::MoveAction;
521 }
522 
523 Qt::ItemFlags QgsLegendModel::flags( const QModelIndex &index ) const
524 {
525  Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
526  if ( !index.isValid() )
527  {
528  flags |= Qt::ItemIsDropEnabled;
529  return flags;
530  }
531 
532  QStandardItem* item = itemFromIndex( index );
533  QgsComposerLegendItem* cItem = dynamic_cast<QgsComposerLegendItem*>( item );
534 
535  if ( cItem )
536  {
538  if ( type == QgsComposerLegendItem::GroupItem )
539  {
540  flags |= Qt::ItemIsDragEnabled;
541  flags |= Qt::ItemIsDropEnabled;
542  }
543  else if ( type == QgsComposerLegendItem::LayerItem )
544  {
545  flags |= Qt::ItemIsDragEnabled;
546  }
547  }
548  return flags;
549 }
550 
551 bool QgsLegendModel::removeRows( int row, int count, const QModelIndex & parent )
552 {
553  if ( count < 1 )
554  {
555  return false;
556  }
557 
558  if ( parent.isValid() )
559  {
560  for ( int i = row + count - 1; i >= row; --i )
561  {
562  QStandardItem* item = itemFromIndex( parent );
563  if ( item )
564  {
565  item->takeRow( i );
566  }
567  }
568  }
569  else
570  {
571  for ( int i = row + count - 1; i >= row; --i )
572  {
573  takeRow( i );
574  }
575  }
576  return true;
577 }
578 
579 QMimeData* QgsLegendModel::mimeData( const QModelIndexList &indexes ) const
580 {
581  QMimeData* mimeData = new QMimeData();
582  QByteArray encodedData;
583  QDomDocument xmlDoc;
584  QDomElement xmlRootElement = xmlDoc.createElement( "LegendModelDragData" );
585  xmlDoc.appendChild( xmlRootElement );
586 
587  QModelIndexList::const_iterator indexIt = indexes.constBegin();
588  for ( ; indexIt != indexes.constEnd(); ++indexIt )
589  {
590  QStandardItem* sItem = itemFromIndex( *indexIt );
591  if ( sItem )
592  {
593  QgsComposerLegendItem* mItem = dynamic_cast<QgsComposerLegendItem*>( sItem );
594  if ( mItem )
595  {
596  mItem->writeXML( xmlRootElement, xmlDoc );
597  }
598  }
599  }
600  mimeData->setData( "text/xml", xmlDoc.toByteArray() );
601  return mimeData;
602 }
603 
604 QStringList QgsLegendModel::mimeTypes() const
605 {
606  QStringList types;
607  types << "text/xml";
608  return types;
609 }
610 
611 bool QgsLegendModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
612 {
613  if ( !data->hasFormat( "text/xml" ) )
614  {
615  return false;
616  }
617 
618  QStandardItem* dropIntoItem = 0;
619  if ( parent.isValid() )
620  {
621  dropIntoItem = itemFromIndex( parent );
622  }
623  else
624  {
625  dropIntoItem = invisibleRootItem();
626  }
627 
628  //get XML doc
629  QByteArray encodedData = data->data( "text/xml" );
630  QDomDocument xmlDoc;
631  xmlDoc.setContent( encodedData );
632 
633  QDomElement dragDataElem = xmlDoc.documentElement();
634  if ( dragDataElem.tagName() != "LegendModelDragData" )
635  {
636  return false;
637  }
638 
639  QDomNodeList nodeList = dragDataElem.childNodes();
640  int nChildNodes = nodeList.size();
641  QDomElement currentElem;
642  QString currentTagName;
643  QgsComposerLegendItem* currentItem = 0;
644 
645  for ( int i = 0; i < nChildNodes; ++i )
646  {
647  currentElem = nodeList.at( i ).toElement();
648  if ( currentElem.isNull() )
649  {
650  continue;
651  }
652  currentTagName = currentElem.tagName();
653  if ( currentTagName == "LayerItem" )
654  {
655  currentItem = new QgsComposerLayerItem();
656  }
657  else if ( currentTagName == "GroupItem" )
658  {
659  currentItem = new QgsComposerGroupItem();
660  }
661  else
662  {
663  continue;
664  }
665  currentItem->readXML( currentElem );
666  if ( row < 0 )
667  {
668  dropIntoItem->insertRow( dropIntoItem->rowCount(), currentItem );
669  }
670  else
671  {
672  dropIntoItem->insertRow( row + i, currentItem );
673  }
674  }
675  emit layersChanged();
676  return true;
677 }
678 
679 void QgsLegendModel::setAutoUpdate( bool autoUpdate )
680 {
681  if ( mAutoUpdate == autoUpdate ) //prevent multiple signal/slot connections
682  {
683  return;
684  }
685 
687  if ( autoUpdate )
688  {
690  {
691  connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( removeLayer( const QString& ) ) );
692  connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( addLayer( QgsMapLayer* ) ) );
693  }
694  }
695  else
696  {
698  {
699  disconnect( QgsMapLayerRegistry::instance(), SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( removeLayer( const QString& ) ) );
700  disconnect( QgsMapLayerRegistry::instance(), SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( addLayer( QgsMapLayer* ) ) );
701  }
702  }
703 }