QGIS API Documentation  2.7.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
qgsdataitem.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsdataitem.cpp - Data items
3  -------------------
4  begin : 2011-04-01
5  copyright : (C) 2011 Radim Blazek
6  email : radim dot blazek at gmail dot com
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 <QApplication>
19 #include <QDateTime>
20 #include <QDir>
21 #include <QFileInfo>
22 #include <QMenu>
23 #include <QMouseEvent>
24 #include <QTreeWidget>
25 #include <QTreeWidgetItem>
26 #include <QVector>
27 #include <QStyle>
28 #include <QSettings>
29 
30 #include "qgis.h"
31 #include "qgsdataitem.h"
32 
33 #include "qgsdataprovider.h"
34 #include "qgslogger.h"
35 #include "qgsproviderregistry.h"
36 #include "qgsconfig.h"
37 
38 // use GDAL VSI mechanism
39 #include "cpl_vsi.h"
40 #include "cpl_string.h"
41 
42 // shared icons
44 {
45  static QIcon icon;
46 
47  if ( icon.isNull() )
48  icon = QgsApplication::getThemeIcon( "/mIconPointLayer.svg" );
49 
50  return icon;
51 }
52 
53 const QIcon &QgsLayerItem::iconLine()
54 {
55  static QIcon icon;
56 
57  if ( icon.isNull() )
58  icon = QgsApplication::getThemeIcon( "/mIconLineLayer.svg" );
59 
60  return icon;
61 }
62 
64 {
65  static QIcon icon;
66 
67  if ( icon.isNull() )
68  icon = QgsApplication::getThemeIcon( "/mIconPolygonLayer.svg" );
69 
70  return icon;
71 }
72 
74 {
75  static QIcon icon;
76 
77  if ( icon.isNull() )
78  icon = QgsApplication::getThemeIcon( "/mIconTableLayer.png" );
79 
80  return icon;
81 }
82 
84 {
85  static QIcon icon;
86 
87  if ( icon.isNull() )
88  icon = QgsApplication::getThemeIcon( "/mIconRaster.svg" );
89 
90  return icon;
91 }
92 
94 {
95  static QIcon icon;
96 
97  if ( icon.isNull() )
98  icon = QgsApplication::getThemeIcon( "/mIconLayer.png" );
99 
100  return icon;
101 }
102 
104 {
105  static QIcon icon;
106 
107  if ( icon.isNull() )
108  icon = QgsApplication::getThemeIcon( "/mIconDbSchema.png" );
109 
110  return icon;
111 }
112 
114 {
115  static QIcon icon;
116 
117  if ( icon.isNull() )
118  {
119  // initialize shared icons
120  QStyle *style = QApplication::style();
121  icon = QIcon( style->standardPixmap( QStyle::SP_DirClosedIcon ) );
122  icon.addPixmap( style->standardPixmap( QStyle::SP_DirOpenIcon ),
123  QIcon::Normal, QIcon::On );
124  }
125 
126  return icon;
127 }
128 
130 {
131  static QIcon icon;
132 
133  if ( icon.isNull() )
134  icon = QgsApplication::getThemeIcon( "/mIconFavourites.png" );
135 
136  return icon;
137 }
138 
139 const QIcon &QgsZipItem::iconZip()
140 {
141  static QIcon icon;
142 
143  if ( icon.isNull() )
144  icon = QgsApplication::getThemeIcon( "/mIconZip.png" );
145 // icon from http://www.softicons.com/free-icons/application-icons/mega-pack-icons-1-by-nikolay-verin/winzip-folder-icon
146 
147  return icon;
148 }
149 
150 QMap<QString, QIcon> QgsDataItem::mIconMap = QMap<QString, QIcon>();
151 
152 QgsDataItem::QgsDataItem( QgsDataItem::Type type, QgsDataItem* parent, QString name, QString path )
153 // Do not pass parent to QObject, Qt would delete this when parent is deleted
154  : QObject()
155  , mType( type )
156  , mCapabilities( NoCapabilities )
157  , mParent( parent )
158  , mPopulated( false )
159  , mName( name )
160  , mPath( path )
161 {
162 }
163 
165 {
166  QgsDebugMsgLevel( "mName = " + mName + " mPath = " + mPath, 2 );
167 }
168 
170 {
171  if ( !mIcon.isNull() )
172  return mIcon;
173 
174  if ( !mIconMap.contains( mIconName ) )
176 
177  return mIconMap.value( mIconName );
178 }
179 
180 void QgsDataItem::emitBeginInsertItems( QgsDataItem* parent, int first, int last )
181 {
182  emit beginInsertItems( parent, first, last );
183 }
185 {
186  emit endInsertItems();
187 }
188 void QgsDataItem::emitBeginRemoveItems( QgsDataItem* parent, int first, int last )
189 {
190  emit beginRemoveItems( parent, first, last );
191 }
193 {
194  emit endRemoveItems();
195 }
196 
197 QVector<QgsDataItem*> QgsDataItem::createChildren()
198 {
199  return QVector<QgsDataItem*>();
200 }
201 
203 {
204  if ( mPopulated )
205  return;
206 
207  QgsDebugMsg( "mPath = " + mPath );
208 
209  QVector<QgsDataItem*> children = createChildren();
210  foreach ( QgsDataItem *child, children )
211  {
212  // initialization, do not refresh! That would result in infinite loop (beginInsertItems->rowCount->populate)
213  addChildItem( child );
214  }
215  mPopulated = true;
216 }
217 
218 void QgsDataItem::populate( QVector<QgsDataItem*> children )
219 {
220  if ( mPopulated )
221  return;
222 
223  QgsDebugMsg( "mPath = " + mPath );
224 
225  foreach ( QgsDataItem *child, children )
226  {
227  if ( !child ) // should not happen
228  continue;
229  // update after thread finished -> refresh
230  addChildItem( child, true );
231  }
232  mPopulated = true;
233 }
234 
236 {
237  if ( !mPopulated )
238  return;
239 
240  QgsDebugMsg( "mPath = " + mPath );
241 
242  foreach ( QgsDataItem *child, mChildren )
243  {
244  QgsDebugMsg( "remove " + child->path() );
245  child->depopulate(); // recursive
246  deleteChildItem( child );
247  }
248  mPopulated = false;
249 }
250 
252 {
253  return mChildren.size();
254 }
256 {
257  return ( mPopulated ? mChildren.count() > 0 : true );
258 }
259 
260 void QgsDataItem::addChildItem( QgsDataItem * child, bool refresh )
261 {
262  QgsDebugMsg( QString( "path = %1 add child #%2 - %3 - %4" ).arg( mPath ).arg( mChildren.size() ).arg( child->mName ).arg( child->mType ) );
263 
264  int i;
265  if ( type() == Directory )
266  {
267  for ( i = 0; i < mChildren.size(); i++ )
268  {
269  // sort items by type, so directories are before data items
270  if ( mChildren[i]->mType == child->mType &&
271  mChildren[i]->mName.localeAwareCompare( child->mName ) > 0 )
272  break;
273  }
274  }
275  else
276  {
277  for ( i = 0; i < mChildren.size(); i++ )
278  {
279  if ( mChildren[i]->mName.localeAwareCompare( child->mName ) >= 0 )
280  break;
281  }
282  }
283 
284  if ( refresh )
285  emit beginInsertItems( this, i, i );
286 
287  mChildren.insert( i, child );
288  child->setParent( this );
289 
290  connect( child, SIGNAL( beginInsertItems( QgsDataItem*, int, int ) ),
291  this, SLOT( emitBeginInsertItems( QgsDataItem*, int, int ) ) );
292  connect( child, SIGNAL( endInsertItems() ),
293  this, SLOT( emitEndInsertItems() ) );
294  connect( child, SIGNAL( beginRemoveItems( QgsDataItem*, int, int ) ),
295  this, SLOT( emitBeginRemoveItems( QgsDataItem*, int, int ) ) );
296  connect( child, SIGNAL( endRemoveItems() ),
297  this, SLOT( emitEndRemoveItems() ) );
298 
299  if ( refresh )
300  emit endInsertItems();
301 }
303 {
304  QgsDebugMsgLevel( "mName = " + child->mName, 2 );
305  int i = mChildren.indexOf( child );
306  Q_ASSERT( i >= 0 );
307  emit beginRemoveItems( this, i, i );
308  mChildren.remove( i );
309  delete child; // deleting QObject child removes it from QObject parent
310  emit endRemoveItems();
311 }
312 
314 {
315  QgsDebugMsgLevel( "mName = " + child->mName, 2 );
316  int i = mChildren.indexOf( child );
317  Q_ASSERT( i >= 0 );
318  emit beginRemoveItems( this, i, i );
319  mChildren.remove( i );
320  emit endRemoveItems();
321  disconnect( child, SIGNAL( beginInsertItems( QgsDataItem*, int, int ) ),
322  this, SLOT( emitBeginInsertItems( QgsDataItem*, int, int ) ) );
323  disconnect( child, SIGNAL( endInsertItems() ),
324  this, SLOT( emitEndInsertItems() ) );
325  disconnect( child, SIGNAL( beginRemoveItems( QgsDataItem*, int, int ) ),
326  this, SLOT( emitBeginRemoveItems( QgsDataItem*, int, int ) ) );
327  disconnect( child, SIGNAL( endRemoveItems() ),
328  this, SLOT( emitEndRemoveItems() ) );
329  child->setParent( 0 );
330  return child;
331 }
332 
333 int QgsDataItem::findItem( QVector<QgsDataItem*> items, QgsDataItem * item )
334 {
335  for ( int i = 0; i < items.size(); i++ )
336  {
337  QgsDebugMsgLevel( QString::number( i ) + " : " + items[i]->mPath + " x " + item->mPath, 2 );
338  if ( items[i]->equal( item ) )
339  return i;
340  }
341  return -1;
342 }
343 
344 void QgsDataItem::refresh( QVector<QgsDataItem*> children )
345 {
346  QgsDebugMsgLevel( "mPath = " + mPath, 2 );
347 
348  // Remove no more present children
349  QVector<QgsDataItem*> remove;
350  foreach ( QgsDataItem *child, mChildren )
351  {
352  if ( !child ) // should not happen
353  continue;
354  if ( findItem( children, child ) >= 0 )
355  continue;
356  remove.append( child );
357  }
358  foreach ( QgsDataItem *child, remove )
359  {
360  QgsDebugMsg( "remove " + child->path() );
361  deleteChildItem( child );
362  }
363 
364  // Add new children
365  foreach ( QgsDataItem *child, children )
366  {
367  if ( !child ) // should not happen
368  continue;
369 
370  int index = findItem( mChildren, child );
371  if ( index >= 0 )
372  {
373  // Refresh recursively (some providers may create more generations of descendants)
374  if ( !( child->capabilities2() & QgsDataItem::Fertile ) )
375  {
376  // The child cannot createChildren() itself
377  mChildren.value( index )->refresh( child->children() );
378  }
379 
380  delete child;
381  continue;
382  }
383  addChildItem( child, true );
384  }
385 }
386 
388 {
389  QgsDebugMsgLevel( "mPath = " + mPath, 2 );
390 
391  QApplication::setOverrideCursor( Qt::WaitCursor );
392 
393  QVector<QgsDataItem*> children = createChildren();
394 
395  refresh( children );
396 
397  QApplication::restoreOverrideCursor();
398 }
399 
400 bool QgsDataItem::equal( const QgsDataItem *other )
401 {
402  if ( metaObject()->className() == other->metaObject()->className() &&
403  mPath == other->path() )
404  {
405  return true;
406  }
407  return false;
408 }
409 
410 // ---------------------------------------------------------------------
411 
412 QgsLayerItem::QgsLayerItem( QgsDataItem* parent, QString name, QString path, QString uri, LayerType layerType, QString providerKey )
413  : QgsDataItem( Layer, parent, name, path )
414  , mProviderKey( providerKey )
415  , mUri( uri )
416  , mLayerType( layerType )
417 {
418  switch ( layerType )
419  {
420  case Point: mIconName = "/mIconPointLayer.svg"; break;
421  case Line: mIconName = "/mIconLineLayer.svg"; break;
422  case Polygon: mIconName = "/mIconPolygonLayer.svg"; break;
423  // TODO add a new icon for generic Vector layers
424  case Vector : mIconName = "/mIconPolygonLayer.svg"; break;
425  case TableLayer: mIconName = "/mIconTableLayer.png"; break;
426  case Raster: mIconName = "/mIconRaster.svg"; break;
427  default: mIconName = "/mIconLayer.png"; break;
428  }
429 }
430 
432 {
436 }
437 
438 bool QgsLayerItem::equal( const QgsDataItem *other )
439 {
440  //QgsDebugMsg ( mPath + " x " + other->mPath );
441  if ( type() != other->type() )
442  {
443  return false;
444  }
445  //const QgsLayerItem *o = qobject_cast<const QgsLayerItem *> ( other );
446  const QgsLayerItem *o = dynamic_cast<const QgsLayerItem *>( other );
447  return ( mPath == o->mPath && mName == o->mName && mUri == o->mUri && mProviderKey == o->mProviderKey );
448 }
449 
450 // ---------------------------------------------------------------------
451 QgsDataCollectionItem::QgsDataCollectionItem( QgsDataItem* parent, QString name, QString path )
452  : QgsDataItem( Collection, parent, name, path )
453 {
455  mIconName = "/mIconDbSchema.png";
456 }
457 
459 {
460  QgsDebugMsgLevel( "mName = " + mName + " mPath = " + mPath, 2 );
461 
462 // Do not delete children, children are deleted by QObject parent
463 #if 0
464  foreach ( QgsDataItem* i, mChildren )
465  {
466  QgsDebugMsgLevel( QString( "delete child = 0x%0" ).arg(( qlonglong )i, 8, 16, QLatin1Char( '0' ) ), 2 );
467  delete i;
468  }
469 #endif
470 }
471 
472 //-----------------------------------------------------------------------
473 // QVector<QgsDataProvider*> QgsDirectoryItem::mProviders = QVector<QgsDataProvider*>();
474 QVector<QLibrary*> QgsDirectoryItem::mLibraries = QVector<QLibrary*>();
475 
476 QgsDirectoryItem::QgsDirectoryItem( QgsDataItem* parent, QString name, QString path )
477  : QgsDataCollectionItem( parent, name, path )
478  , mDirPath( path )
479 {
480  mType = Directory;
481  init();
482 }
483 
484 QgsDirectoryItem::QgsDirectoryItem( QgsDataItem* parent, QString name, QString dirPath, QString path )
485  : QgsDataCollectionItem( parent, name, path )
486  , mDirPath( dirPath )
487 {
488  mType = Directory;
489  init();
490 }
491 
493 {
494  if ( mLibraries.size() > 0 )
495  return;
496 
497  QStringList keys = QgsProviderRegistry::instance()->providerList();
498  QStringList::const_iterator i;
499  for ( i = keys.begin(); i != keys.end(); ++i )
500  {
501  QString k( *i );
502  // some providers hangs with empty uri (Postgis) etc...
503  // -> using libraries directly
504  QLibrary *library = QgsProviderRegistry::instance()->providerLibrary( k );
505  if ( library )
506  {
507  dataCapabilities_t * dataCapabilities = ( dataCapabilities_t * ) cast_to_fptr( library->resolve( "dataCapabilities" ) );
508  if ( !dataCapabilities )
509  {
510  QgsDebugMsg( library->fileName() + " does not have dataCapabilities" );
511  continue;
512  }
513  if ( dataCapabilities() == QgsDataProvider::NoDataCapabilities )
514  {
515  QgsDebugMsg( library->fileName() + " has NoDataCapabilities" );
516  continue;
517  }
518 
519  QgsDebugMsg( QString( "%1 dataCapabilities : %2" ).arg( library->fileName() ).arg( dataCapabilities() ) );
520  mLibraries.append( library );
521  }
522  else
523  {
524  //QgsDebugMsg ( "Cannot get provider " + k );
525  }
526  }
527 }
528 
530 {
531 }
532 
534 {
535  return iconDir();
536 }
537 
538 QVector<QgsDataItem*> QgsDirectoryItem::createChildren()
539 {
540  QVector<QgsDataItem*> children;
541  QDir dir( mDirPath );
542  QSettings settings;
543 
544  QStringList entries = dir.entryList( QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name | QDir::IgnoreCase );
545  foreach ( QString subdir, entries )
546  {
547  QString subdirPath = dir.absoluteFilePath( subdir );
548  QgsDebugMsgLevel( QString( "creating subdir: %1" ).arg( subdirPath ), 2 );
549 
550  QString path = mPath + "/" + subdir; // may differ from subdirPath
551  QgsDirectoryItem *item = new QgsDirectoryItem( this, subdir, subdirPath, path );
552  // propagate signals up to top
553 
554  children.append( item );
555  }
556 
557  QStringList fileEntries = dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot | QDir::Files, QDir::Name );
558  foreach ( QString name, fileEntries )
559  {
560  QString path = dir.absoluteFilePath( name );
561  QFileInfo fileInfo( path );
562 
563  // vsizip support was added to GDAL/OGR 1.6 but GDAL_VERSION_NUM not available here
564  // so we assume it's available anyway
565  {
566  QgsDataItem * item = QgsZipItem::itemFromPath( this, path, name, mPath + "/" + name );
567  if ( item )
568  {
569  children.append( item );
570  continue;
571  }
572  }
573 
574  foreach ( QLibrary *library, mLibraries )
575  {
576  // we could/should create separate list of providers for each purpose
577 
578  // TODO: use existing fileVectorFilters(),directoryDrivers() ?
579  dataCapabilities_t * dataCapabilities = ( dataCapabilities_t * ) cast_to_fptr( library->resolve( "dataCapabilities" ) );
580  if ( !dataCapabilities )
581  {
582  continue;
583  }
584 
585  int capabilities = dataCapabilities();
586 
587  if ( !(( fileInfo.isFile() && ( capabilities & QgsDataProvider::File ) ) ||
588  ( fileInfo.isDir() && ( capabilities & QgsDataProvider::Dir ) ) ) )
589  {
590  continue;
591  }
592 
593  dataItem_t * dataItem = ( dataItem_t * ) cast_to_fptr( library->resolve( "dataItem" ) );
594  if ( ! dataItem )
595  {
596  QgsDebugMsg( library->fileName() + " does not have dataItem" );
597  continue;
598  }
599 
600  QgsDataItem * item = dataItem( path, this );
601  if ( item )
602  {
603  children.append( item );
604  }
605  }
606  }
607 
608  return children;
609 }
610 
612 {
613  //QgsDebugMsg ( mPath + " x " + other->mPath );
614  if ( type() != other->type() )
615  {
616  return false;
617  }
618  return ( path() == other->path() );
619 }
620 
622 {
623  return new QgsDirectoryParamWidget( mPath );
624 }
625 
626 QgsDirectoryParamWidget::QgsDirectoryParamWidget( QString path, QWidget* parent )
627  : QTreeWidget( parent )
628 {
629  setRootIsDecorated( false );
630 
631  // name, size, date, permissions, owner, group, type
632  setColumnCount( 7 );
633  QStringList labels;
634  labels << tr( "Name" ) << tr( "Size" ) << tr( "Date" ) << tr( "Permissions" ) << tr( "Owner" ) << tr( "Group" ) << tr( "Type" );
635  setHeaderLabels( labels );
636 
637  QStyle* style = QApplication::style();
638  QIcon iconDirectory = QIcon( style->standardPixmap( QStyle::SP_DirClosedIcon ) );
639  QIcon iconFile = QIcon( style->standardPixmap( QStyle::SP_FileIcon ) );
640  QIcon iconLink = QIcon( style->standardPixmap( QStyle::SP_FileLinkIcon ) ); // TODO: symlink to directory?
641 
642  QList<QTreeWidgetItem *> items;
643 
644  QDir dir( path );
645  QStringList entries = dir.entryList( QDir::AllEntries | QDir::NoDotAndDotDot, QDir::Name | QDir::IgnoreCase );
646  foreach ( QString name, entries )
647  {
648  QFileInfo fi( dir.absoluteFilePath( name ) );
649  QStringList texts;
650  texts << name;
651  QString size;
652  if ( fi.size() > 1024 )
653  {
654  size = size.sprintf( "%.1f KiB", fi.size() / 1024.0 );
655  }
656  else if ( fi.size() > 1.048576e6 )
657  {
658  size = size.sprintf( "%.1f MiB", fi.size() / 1.048576e6 );
659  }
660  else
661  {
662  size = QString( "%1 B" ).arg( fi.size() );
663  }
664  texts << size;
665  texts << fi.lastModified().toString( Qt::SystemLocaleShortDate );
666  QString perm;
667  perm += fi.permission( QFile::ReadOwner ) ? 'r' : '-';
668  perm += fi.permission( QFile::WriteOwner ) ? 'w' : '-';
669  perm += fi.permission( QFile::ExeOwner ) ? 'x' : '-';
670  // QFile::ReadUser, QFile::WriteUser, QFile::ExeUser
671  perm += fi.permission( QFile::ReadGroup ) ? 'r' : '-';
672  perm += fi.permission( QFile::WriteGroup ) ? 'w' : '-';
673  perm += fi.permission( QFile::ExeGroup ) ? 'x' : '-';
674  perm += fi.permission( QFile::ReadOther ) ? 'r' : '-';
675  perm += fi.permission( QFile::WriteOther ) ? 'w' : '-';
676  perm += fi.permission( QFile::ExeOther ) ? 'x' : '-';
677  texts << perm;
678 
679  texts << fi.owner();
680  texts << fi.group();
681 
682  QString type;
683  QIcon icon;
684  if ( fi.isDir() )
685  {
686  type = tr( "folder" );
687  icon = iconDirectory;
688  }
689  else if ( fi.isFile() )
690  {
691  type = tr( "file" );
692  icon = iconFile;
693  }
694  else if ( fi.isSymLink() )
695  {
696  type = tr( "link" );
697  icon = iconLink;
698  }
699 
700  texts << type;
701 
702  QTreeWidgetItem *item = new QTreeWidgetItem( texts );
703  item->setIcon( 0, icon );
704  items << item;
705  }
706 
707  addTopLevelItems( items );
708 
709  // hide columns that are not requested
710  QSettings settings;
711  QList<QVariant> lst = settings.value( "/dataitem/directoryHiddenColumns" ).toList();
712  foreach ( QVariant colVariant, lst )
713  {
714  setColumnHidden( colVariant.toInt(), true );
715  }
716 }
717 
719 {
720  if ( event->button() == Qt::RightButton )
721  {
722  // show the popup menu
723  QMenu popupMenu;
724 
725  QStringList labels;
726  labels << tr( "Name" ) << tr( "Size" ) << tr( "Date" ) << tr( "Permissions" ) << tr( "Owner" ) << tr( "Group" ) << tr( "Type" );
727  for ( int i = 0; i < labels.count(); i++ )
728  {
729  QAction* action = popupMenu.addAction( labels[i], this, SLOT( showHideColumn() ) );
730  action->setObjectName( QString::number( i ) );
731  action->setCheckable( true );
732  action->setChecked( !isColumnHidden( i ) );
733  }
734 
735  popupMenu.exec( event->globalPos() );
736  }
737 }
738 
740 {
741  QAction* action = qobject_cast<QAction*>( sender() );
742  if ( !action )
743  return; // something is wrong
744 
745  int columnIndex = action->objectName().toInt();
746  setColumnHidden( columnIndex, !isColumnHidden( columnIndex ) );
747 
748  // save in settings
749  QSettings settings;
750  QList<QVariant> lst;
751  for ( int i = 0; i < columnCount(); i++ )
752  {
753  if ( isColumnHidden( i ) )
754  lst.append( QVariant( i ) );
755  }
756  settings.setValue( "/dataitem/directoryHiddenColumns", lst );
757 }
758 
759 
760 QgsErrorItem::QgsErrorItem( QgsDataItem* parent, QString error, QString path )
761  : QgsDataItem( QgsDataItem::Error, parent, error, path )
762 {
763  mIconName = "/mIconDelete.png";
764 
765  mPopulated = true; // no more children
766 }
767 
769 {
770 }
771 
772 QgsFavouritesItem::QgsFavouritesItem( QgsDataItem* parent, QString name, QString path )
773  : QgsDataCollectionItem( parent, name, "favourites:" )
774 {
775  Q_UNUSED( path );
776  mCapabilities |= Fast;
777  mType = Favourites;
778  mIconName = "/mIconFavourites.png";
779  populate();
780 }
781 
783 {
784 }
785 
786 QVector<QgsDataItem*> QgsFavouritesItem::createChildren()
787 {
788  QVector<QgsDataItem*> children;
789 
790  QSettings settings;
791  QStringList favDirs = settings.value( "/browser/favourites", QVariant() ).toStringList();
792 
793  foreach ( QString favDir, favDirs )
794  {
795  QString pathName = favDir;
796  pathName.replace( QRegExp( "[\\\\/]" ), "|" );
797  QgsDataItem *item = new QgsDirectoryItem( this, favDir, favDir, mPath + "/" + pathName );
798  if ( item )
799  {
800  children.append( item );
801  }
802  }
803 
804  return children;
805 }
806 
807 void QgsFavouritesItem::addDirectory( QString favDir )
808 {
809  QSettings settings;
810  QStringList favDirs = settings.value( "/browser/favourites" ).toStringList();
811  favDirs.append( favDir );
812  settings.setValue( "/browser/favourites", favDirs );
813 
814  if ( mPopulated )
815  addChildItem( new QgsDirectoryItem( this, favDir, favDir ), true );
816 }
817 
819 {
820  if ( !item )
821  return;
822 
823  QSettings settings;
824  QStringList favDirs = settings.value( "/browser/favourites" ).toStringList();
825  favDirs.removeAll( item->dirPath() );
826  settings.setValue( "/browser/favourites", favDirs );
827 
828  int idx = findItem( mChildren, item );
829  if ( idx < 0 )
830  {
831  QgsDebugMsg( QString( "favourites item %1 not found" ).arg( item->path() ) );
832  return;
833  }
834 
835  if ( mPopulated )
836  deleteChildItem( mChildren[idx] );
837 }
838 
839 //-----------------------------------------------------------------------
840 QStringList QgsZipItem::mProviderNames = QStringList();
841 QVector<dataItem_t *> QgsZipItem::mDataItemPtr = QVector<dataItem_t*>();
842 
843 
844 QgsZipItem::QgsZipItem( QgsDataItem* parent, QString name, QString path )
845  : QgsDataCollectionItem( parent, name, path )
846 {
847  mDirPath = path;
848  init();
849 }
850 
851 QgsZipItem::QgsZipItem( QgsDataItem* parent, QString name, QString dirPath, QString path )
852  : QgsDataCollectionItem( parent, name, path )
853  , mDirPath( dirPath )
854 {
855  init();
856 }
857 
858 void QgsZipItem::init()
859 {
860  mType = Collection; //Zip??
861  mIconName = "/mIconZip.png";
863 
864  if ( mProviderNames.size() == 0 )
865  {
866  // QStringList keys = QgsProviderRegistry::instance()->providerList();
867  // only use GDAL and OGR providers as we use the VSIFILE mechanism
868  QStringList keys;
869  // keys << "ogr" << "gdal";
870  keys << "gdal" << "ogr";
871 
872  QStringList::const_iterator i;
873  for ( i = keys.begin(); i != keys.end(); ++i )
874  {
875  QString k( *i );
876  QgsDebugMsg( "provider " + k );
877  // some providers hangs with empty uri (Postgis) etc...
878  // -> using libraries directly
879  QLibrary *library = QgsProviderRegistry::instance()->providerLibrary( k );
880  if ( library )
881  {
882  dataCapabilities_t * dataCapabilities = ( dataCapabilities_t * ) cast_to_fptr( library->resolve( "dataCapabilities" ) );
883  if ( !dataCapabilities )
884  {
885  QgsDebugMsg( library->fileName() + " does not have dataCapabilities" );
886  continue;
887  }
888  if ( dataCapabilities() == QgsDataProvider::NoDataCapabilities )
889  {
890  QgsDebugMsg( library->fileName() + " has NoDataCapabilities" );
891  continue;
892  }
893  QgsDebugMsg( QString( "%1 dataCapabilities : %2" ).arg( library->fileName() ).arg( dataCapabilities() ) );
894 
895  dataItem_t * dataItem = ( dataItem_t * ) cast_to_fptr( library->resolve( "dataItem" ) );
896  if ( ! dataItem )
897  {
898  QgsDebugMsg( library->fileName() + " does not have dataItem" );
899  continue;
900  }
901 
902  // mLibraries.append( library );
903  mDataItemPtr.append( dataItem );
904  mProviderNames.append( k );
905  }
906  else
907  {
908  //QgsDebugMsg ( "Cannot get provider " + k );
909  }
910  }
911  }
912 
913 }
914 
916 {
917 }
918 
919 // internal function to scan a vsidir (zip or tar file) recursively
920 // GDAL trunk has this since r24423 (05/16/12) - VSIReadDirRecursive()
921 // use a copy of the function internally for now,
922 // but use char ** and CSLAddString, because CPLStringList was added in gdal-1.9
923 char **VSIReadDirRecursive1( const char *pszPath )
924 {
925  // CPLStringList oFiles = NULL;
926  char **papszOFiles = NULL;
927  char **papszFiles1 = NULL;
928  char **papszFiles2 = NULL;
929  VSIStatBufL psStatBuf;
930  CPLString osTemp1, osTemp2;
931  int i, j;
932  int nCount1, nCount2;
933 
934  // get listing
935  papszFiles1 = VSIReadDir( pszPath );
936  if ( ! papszFiles1 )
937  return NULL;
938 
939  // get files and directories inside listing
940  nCount1 = CSLCount( papszFiles1 );
941  for ( i = 0; i < nCount1; i++ )
942  {
943  // build complete file name for stat
944  osTemp1.clear();
945  osTemp1.append( pszPath );
946  osTemp1.append( "/" );
947  osTemp1.append( papszFiles1[i] );
948 
949  // if is file, add it
950  if ( VSIStatL( osTemp1.c_str(), &psStatBuf ) == 0 &&
951  VSI_ISREG( psStatBuf.st_mode ) )
952  {
953  // oFiles.AddString( papszFiles1[i] );
954  papszOFiles = CSLAddString( papszOFiles, papszFiles1[i] );
955  }
956  else if ( VSIStatL( osTemp1.c_str(), &psStatBuf ) == 0 &&
957  VSI_ISDIR( psStatBuf.st_mode ) )
958  {
959  // add directory entry
960  osTemp2.clear();
961  osTemp2.append( papszFiles1[i] );
962  osTemp2.append( "/" );
963  // oFiles.AddString( osTemp2.c_str() );
964  papszOFiles = CSLAddString( papszOFiles, osTemp2.c_str() );
965 
966  // recursively add files inside directory
967  papszFiles2 = VSIReadDirRecursive1( osTemp1.c_str() );
968  if ( papszFiles2 )
969  {
970  nCount2 = CSLCount( papszFiles2 );
971  for ( j = 0; j < nCount2; j++ )
972  {
973  osTemp2.clear();
974  osTemp2.append( papszFiles1[i] );
975  osTemp2.append( "/" );
976  osTemp2.append( papszFiles2[j] );
977  // oFiles.AddString( osTemp2.c_str() );
978  papszOFiles = CSLAddString( papszOFiles, osTemp2.c_str() );
979  }
980  CSLDestroy( papszFiles2 );
981  }
982  }
983  }
984  CSLDestroy( papszFiles1 );
985 
986  // return oFiles.StealList();
987  return papszOFiles;
988 }
989 
990 QVector<QgsDataItem*> QgsZipItem::createChildren()
991 {
992  QVector<QgsDataItem*> children;
993  QString tmpPath;
994  QString childPath;
995  QSettings settings;
996  QString scanZipSetting = settings.value( "/qgis/scanZipInBrowser2", "basic" ).toString();
997 
998  mZipFileList.clear();
999 
1000  QgsDebugMsgLevel( QString( "path = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( path() ).arg( name() ).arg( scanZipSetting ).arg( mVsiPrefix ), 2 );
1001 
1002  // if scanZipBrowser == no: skip to the next file
1003  if ( scanZipSetting == "no" )
1004  {
1005  return children;
1006  }
1007 
1008  // first get list of files
1009  getZipFileList();
1010 
1011  // loop over files inside zip
1012  foreach ( QString fileName, mZipFileList )
1013  {
1014  QFileInfo info( fileName );
1015  tmpPath = mVsiPrefix + path() + "/" + fileName;
1016  QgsDebugMsgLevel( "tmpPath = " + tmpPath, 3 );
1017 
1018  // foreach( dataItem_t *dataItem, mDataItemPtr )
1019  for ( int i = 0; i < mProviderNames.size(); i++ )
1020  {
1021  // ugly hack to remove .dbf file if there is a .shp file
1022  if ( mProviderNames[i] == "ogr" )
1023  {
1024  if ( info.suffix().toLower() == "dbf" )
1025  {
1026  if ( mZipFileList.indexOf( fileName.left( fileName.count() - 4 ) + ".shp" ) != -1 )
1027  continue;
1028  }
1029  if ( info.completeSuffix().toLower() == "shp.xml" )
1030  {
1031  continue;
1032  }
1033  }
1034 
1035  // try to get data item from provider
1036  dataItem_t *dataItem = mDataItemPtr[i];
1037  if ( dataItem )
1038  {
1039  QgsDebugMsgLevel( QString( "trying to load item %1 with %2" ).arg( tmpPath ).arg( mProviderNames[i] ), 3 );
1040  QgsDataItem * item = dataItem( tmpPath, this );
1041  if ( item )
1042  {
1043  QgsDebugMsgLevel( "loaded item", 3 );
1044  childPath = tmpPath;
1045  children.append( item );
1046  break;
1047  }
1048  else
1049  {
1050  QgsDebugMsgLevel( "not loaded item", 3 );
1051  }
1052  }
1053  }
1054 
1055  }
1056 
1057  if ( children.size() == 1 )
1058  {
1059  // save the name of the only child so we can get a normal data item from it
1060  mPath = childPath;
1061  }
1062 
1063  return children;
1064 }
1065 
1066 QgsDataItem* QgsZipItem::itemFromPath( QgsDataItem* parent, QString path, QString name )
1067 {
1068  return itemFromPath( parent, path, name, path );
1069 }
1070 
1071 QgsDataItem* QgsZipItem::itemFromPath( QgsDataItem* parent, QString dirPath, QString name, QString path )
1072 {
1073  QSettings settings;
1074  QString scanZipSetting = settings.value( "/qgis/scanZipInBrowser2", "basic" ).toString();
1075  QString vsiPath = path;
1076  int zipFileCount = 0;
1077  QStringList zipFileList;
1078  QFileInfo fileInfo( dirPath );
1079  QString vsiPrefix = QgsZipItem::vsiPrefix( dirPath );
1080  QgsZipItem * zipItem = 0;
1081  bool populated = false;
1082 
1083  QgsDebugMsgLevel( QString( "path = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( path ).arg( name ).arg( scanZipSetting ).arg( vsiPrefix ), 3 );
1084 
1085  // don't scan if scanZipBrowser == no
1086  if ( scanZipSetting == "no" )
1087  return 0;
1088 
1089  // don't scan if this file is not a /vsizip/ or /vsitar/ item
1090  if (( vsiPrefix != "/vsizip/" && vsiPrefix != "/vsitar/" ) )
1091  return 0;
1092 
1093  zipItem = new QgsZipItem( parent, name, dirPath, path );
1094 
1095  if ( zipItem )
1096  {
1097  // force populate zipItem if it has less than 10 items and is not a .tgz or .tar.gz file (slow loading)
1098  // for other items populating will be delayed until item is opened
1099  // this might be polluting the tree with empty items but is necessary for performance reasons
1100  // could also accept all files smaller than a certain size and add options for file count and/or size
1101 
1102  // first get list of files inside .zip or .tar files
1103  if ( path.endsWith( ".zip", Qt::CaseInsensitive ) ||
1104  path.endsWith( ".tar", Qt::CaseInsensitive ) )
1105  {
1106  zipFileList = zipItem->getZipFileList();
1107  }
1108  // force populate if less than 10 items
1109  if ( zipFileList.count() > 0 && zipFileList.count() <= 10 )
1110  {
1111  zipItem->populate();
1112  populated = true; // there is no QgsDataItem::isPopulated() function
1113  QgsDebugMsgLevel( QString( "Got zipItem with %1 children, path=%2, name=%3" ).arg( zipItem->rowCount() ).arg( zipItem->path() ).arg( zipItem->name() ), 3 );
1114  }
1115  else
1116  {
1117  QgsDebugMsgLevel( QString( "Delaying populating zipItem with path=%1, name=%2" ).arg( zipItem->path() ).arg( zipItem->name() ), 3 );
1118  }
1119  }
1120 
1121  // only display if has children or if is not populated
1122  if ( zipItem && ( !populated || zipItem->rowCount() > 1 ) )
1123  {
1124  QgsDebugMsgLevel( "returning zipItem", 3 );
1125  return zipItem;
1126  }
1127  // if 1 or 0 child found, create a single data item using the normal path or the full path given by QgsZipItem
1128  else
1129  {
1130  if ( zipItem )
1131  {
1132  vsiPath = zipItem->path();
1133  zipFileCount = zipFileList.count();
1134  delete zipItem;
1135  }
1136 
1137  QgsDebugMsgLevel( QString( "will try to create a normal dataItem from path= %2 or %3" ).arg( path ).arg( vsiPath ), 3 );
1138 
1139  // try to open using registered providers (gdal and ogr)
1140  for ( int i = 0; i < mProviderNames.size(); i++ )
1141  {
1142  dataItem_t *dataItem = mDataItemPtr[i];
1143  if ( dataItem )
1144  {
1145  QgsDataItem *item = 0;
1146  // try first with normal path (Passthru)
1147  // this is to simplify .qml handling, and without this some tests will fail
1148  // (e.g. testZipItemVectorTransparency(), second test)
1149  if (( mProviderNames[i] == "ogr" ) ||
1150  ( mProviderNames[i] == "gdal" && zipFileCount == 1 ) )
1151  item = dataItem( path, parent );
1152  // try with /vsizip/
1153  if ( ! item )
1154  item = dataItem( vsiPath, parent );
1155  if ( item )
1156  return item;
1157  }
1158  }
1159  }
1160 
1161  return 0;
1162 }
1163 
1164 const QStringList & QgsZipItem::getZipFileList()
1165 {
1166  if ( ! mZipFileList.isEmpty() )
1167  return mZipFileList;
1168 
1169  QString tmpPath;
1170  QSettings settings;
1171  QString scanZipSetting = settings.value( "/qgis/scanZipInBrowser2", "basic" ).toString();
1172 
1173  QgsDebugMsgLevel( QString( "mDirPath = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( mDirPath ).arg( name() ).arg( scanZipSetting ).arg( mVsiPrefix ), 3 );
1174 
1175  // if scanZipBrowser == no: skip to the next file
1176  if ( scanZipSetting == "no" )
1177  {
1178  return mZipFileList;
1179  }
1180 
1181  // get list of files inside zip file
1182  QgsDebugMsgLevel( QString( "Open file %1 with gdal vsi" ).arg( mVsiPrefix + path() ), 3 );
1183  char **papszSiblingFiles = VSIReadDirRecursive1( QString( mVsiPrefix + mDirPath ).toLocal8Bit().constData() );
1184  if ( papszSiblingFiles )
1185  {
1186  for ( int i = 0; i < CSLCount( papszSiblingFiles ); i++ )
1187  {
1188  tmpPath = papszSiblingFiles[i];
1189  QgsDebugMsgLevel( QString( "Read file %1" ).arg( tmpPath ), 3 );
1190  // skip directories (files ending with /)
1191  if ( tmpPath.right( 1 ) != "/" )
1192  mZipFileList << tmpPath;
1193  }
1194  CSLDestroy( papszSiblingFiles );
1195  }
1196  else
1197  {
1198  QgsDebugMsg( QString( "Error reading %1" ).arg( mDirPath ) );
1199  }
1200 
1201  return mZipFileList;
1202 }
A Collection: logical collection of layers or subcollections, e.g.
Definition: qgsdataitem.h:233
QgsDirectoryParamWidget(QString path, QWidget *parent=NULL)
void beginInsertItems(QgsDataItem *parent, int first, int last)
static unsigned index
void removeDirectory(QgsDirectoryItem *item)
virtual void refresh()
QgsFavouritesItem(QgsDataItem *parent, QString name, QString path=QString())
static QMap< QString, QIcon > mIconMap
Definition: qgsdataitem.h:162
QVector< QgsDataItem * > children() const
Definition: qgsdataitem.h:131
QString name() const
Definition: qgsdataitem.h:133
LayerType
Layers enum defining the types of layers that can be added to a map.
Definition: qgsmaplayer.h:54
virtual Capabilities capabilities2() const
Definition: qgsdataitem.h:117
static const QIcon & iconDefault()
Definition: qgsdataitem.cpp:93
virtual void populate()
static const QIcon & iconPoint()
Definition: qgsdataitem.cpp:43
static QgsDataItem * itemFromPath(QgsDataItem *parent, QString path, QString name)
static QgsProviderRegistry * instance(QString pluginPath=QString::null)
means of accessing canonical single instance
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QString mProviderKey
Definition: qgsdataitem.h:216
void addDirectory(QString favIcon)
QgsDataItem(QgsDataItem::Type type, QgsDataItem *parent, QString name, QString path)
static QIcon getThemeIcon(const QString &theName)
Helper to get a theme icon.
static const QIcon & iconFavourites()
virtual QIcon icon()
void mousePressEvent(QMouseEvent *event)
QString mIconName
Definition: qgsdataitem.h:160
QgsLayerItem(QgsDataItem *parent, QString name, QString path, QString uri, LayerType layerType, QString providerKey)
virtual void depopulate()
Remove children recursively and set as not populated.
static const QIcon & iconPolygon()
Definition: qgsdataitem.cpp:63
static QIcon icon(QString icon)
QgsErrorItem(QgsDataItem *parent, QString error, QString path)
virtual QWidget * paramWidget()
char ** VSIReadDirRecursive1(const char *pszPath)
void endRemoveItems()
A zip file: contains layers, using GDAL/OGR VSIFILE mechanism.
Definition: qgsdataitem.h:335
virtual bool equal(const QgsDataItem *other)
QStringList providerList() const
Return list of available providers by their keys.
static const QIcon & iconDataCollection()
QgsDirectoryItem(QgsDataItem *parent, QString name, QString path)
QVector< QgsDataItem * > createChildren()
virtual ~QgsDataItem()
void beginRemoveItems(QgsDataItem *parent, int first, int last)
static const QIcon & iconRaster()
Definition: qgsdataitem.cpp:83
QString dirPath() const
Definition: qgsdataitem.h:274
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
static QString vsiPrefix(QString uri)
Definition: qgsdataitem.h:356
QString mName
Definition: qgsdataitem.h:153
QVector< QgsDataItem * > createChildren()
QStringList mZipFileList
Definition: qgsdataitem.h:342
virtual QIcon icon()
static const QIcon & iconDir()
QString path() const
Definition: qgsdataitem.h:134
Type type() const
Definition: qgsdataitem.h:128
bool hasChildren()
static QStringList mProviderNames
Definition: qgsdataitem.h:354
A directory: contains subdirectories and layers.
Definition: qgsdataitem.h:247
base class for all items in the model
Definition: qgsdataitem.h:39
QgsDataCollectionItem(QgsDataItem *parent, QString name, QString path=QString::null)
virtual bool equal(const QgsDataItem *other)
Capabilities mCapabilities
Definition: qgsdataitem.h:149
virtual void addChildItem(QgsDataItem *child, bool refresh=false)
static QVector< dataItem_t * > mDataItemPtr
Definition: qgsdataitem.h:353
Can create children. Even items without this capability may have children, but cannot create them...
Definition: qgsdataitem.h:105
void emitBeginInsertItems(QgsDataItem *parent, int first, int last)
static const QIcon & iconTable()
Definition: qgsdataitem.cpp:73
QString mPath
Definition: qgsdataitem.h:158
QLibrary * providerLibrary(const QString &providerKey) const
QString mUri
Definition: qgsdataitem.h:217
static int findItem(QVector< QgsDataItem * > items, QgsDataItem *item)
virtual Q_DECL_DEPRECATED Capability capabilities()
Definition: qgsdataitem.h:115
QString mVsiPrefix
Definition: qgsdataitem.h:341
QgsZipItem(QgsDataItem *parent, QString name, QString path)
static QVector< QLibrary * > mLibraries
Definition: qgsdataitem.h:281
void setParent(QgsDataItem *parent)
Definition: qgsdataitem.h:130
QgsMapLayer::LayerType mapLayerType()
static const QIcon & iconLine()
Definition: qgsdataitem.cpp:53
void endInsertItems()
void emitEndInsertItems()
virtual QVector< QgsDataItem * > createChildren()
bool mPopulated
Definition: qgsdataitem.h:152
Item that represents a layer that can be opened with one of the providers.
Definition: qgsdataitem.h:180
QgsDataItem * dataItem_t(QString, QgsDataItem *)
Definition: qgsdataitem.h:35
void(*)() cast_to_fptr(void *p)
Definition: qgis.h:298
virtual QgsDataItem * removeChildItem(QgsDataItem *child)
virtual void deleteChildItem(QgsDataItem *child)
QVector< QgsDataItem * > mChildren
Definition: qgsdataitem.h:151
QVector< QgsDataItem * > createChildren()
void emitEndRemoveItems()
void emitBeginRemoveItems(QgsDataItem *parent, int first, int last)
QString mDirPath
Definition: qgsdataitem.h:340
LayerType mLayerType
Definition: qgsdataitem.h:218
virtual bool equal(const QgsDataItem *other)
int dataCapabilities_t()
double size
Definition: qgssvgcache.cpp:77
static const QIcon & iconZip()
createChildren() is fast enough to be run in main thread when refreshing items, most root items (wms...
Definition: qgsdataitem.h:106
const QStringList & getZipFileList()
#define tr(sourceText)