QGIS API Documentation  2.0.1-Dufour
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups 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 
151 QgsDataItem::QgsDataItem( QgsDataItem::Type type, QgsDataItem* parent, QString name, QString path )
152 // Do not pass parent to QObject, Qt would delete this when parent is deleted
153  : QObject(), mType( type ), mParent( parent ), mPopulated( false ), mName( name ), mPath( path )
154 {
155 }
156 
158 {
159  QgsDebugMsgLevel( "mName = " + mName + " mPath = " + mPath, 2 );
160 }
161 
162 void QgsDataItem::emitBeginInsertItems( QgsDataItem* parent, int first, int last )
163 {
164  emit beginInsertItems( parent, first, last );
165 }
167 {
168  emit endInsertItems();
169 }
170 void QgsDataItem::emitBeginRemoveItems( QgsDataItem* parent, int first, int last )
171 {
172  emit beginRemoveItems( parent, first, last );
173 }
175 {
176  emit endRemoveItems();
177 }
178 
179 QVector<QgsDataItem*> QgsDataItem::createChildren()
180 {
181  return QVector<QgsDataItem*>();
182 }
183 
185 {
186  if ( mPopulated )
187  return;
188 
189  QgsDebugMsg( "mPath = " + mPath );
190 
191  QApplication::setOverrideCursor( Qt::WaitCursor );
192 
193  QVector<QgsDataItem*> children = createChildren();
194  foreach ( QgsDataItem *child, children )
195  {
196  // initialization, do not refresh! That would result in infinite loop (beginInsertItems->rowCount->populate)
197  addChildItem( child );
198  }
199  mPopulated = true;
200 
201  QApplication::restoreOverrideCursor();
202 }
203 
205 {
206  // if ( !mPopulated )
207  // populate();
208  return mChildren.size();
209 }
211 {
212  return ( mPopulated ? mChildren.count() > 0 : true );
213 }
214 
215 void QgsDataItem::addChildItem( QgsDataItem * child, bool refresh )
216 {
217  QgsDebugMsg( QString( "path = %1 add child #%2 - %3 - %4" ).arg( mPath ).arg( mChildren.size() ).arg( child->mName ).arg( child->mType ) );
218 
219  int i;
220  if ( type() == Directory )
221  {
222  for ( i = 0; i < mChildren.size(); i++ )
223  {
224  // sort items by type, so directories are before data items
225  if ( mChildren[i]->mType == child->mType &&
226  mChildren[i]->mName.localeAwareCompare( child->mName ) >= 0 )
227  break;
228  }
229  }
230  else
231  {
232  for ( i = 0; i < mChildren.size(); i++ )
233  {
234  if ( mChildren[i]->mName.localeAwareCompare( child->mName ) >= 0 )
235  break;
236  }
237  }
238 
239  if ( refresh )
240  emit beginInsertItems( this, i, i );
241 
242  mChildren.insert( i, child );
243 
244  connect( child, SIGNAL( beginInsertItems( QgsDataItem*, int, int ) ),
245  this, SLOT( emitBeginInsertItems( QgsDataItem*, int, int ) ) );
246  connect( child, SIGNAL( endInsertItems() ),
247  this, SLOT( emitEndInsertItems() ) );
248  connect( child, SIGNAL( beginRemoveItems( QgsDataItem*, int, int ) ),
249  this, SLOT( emitBeginRemoveItems( QgsDataItem*, int, int ) ) );
250  connect( child, SIGNAL( endRemoveItems() ),
251  this, SLOT( emitEndRemoveItems() ) );
252 
253  if ( refresh )
254  emit endInsertItems();
255 }
257 {
258  QgsDebugMsgLevel( "mName = " + child->mName, 2 );
259  int i = mChildren.indexOf( child );
260  Q_ASSERT( i >= 0 );
261  emit beginRemoveItems( this, i, i );
262  mChildren.remove( i );
263  delete child;
264  emit endRemoveItems();
265 }
266 
268 {
269  QgsDebugMsgLevel( "mName = " + child->mName, 2 );
270  int i = mChildren.indexOf( child );
271  Q_ASSERT( i >= 0 );
272  emit beginRemoveItems( this, i, i );
273  mChildren.remove( i );
274  emit endRemoveItems();
275  disconnect( child, SIGNAL( beginInsertItems( QgsDataItem*, int, int ) ),
276  this, SLOT( emitBeginInsertItems( QgsDataItem*, int, int ) ) );
277  disconnect( child, SIGNAL( endInsertItems() ),
278  this, SLOT( emitEndInsertItems() ) );
279  disconnect( child, SIGNAL( beginRemoveItems( QgsDataItem*, int, int ) ),
280  this, SLOT( emitBeginRemoveItems( QgsDataItem*, int, int ) ) );
281  disconnect( child, SIGNAL( endRemoveItems() ),
282  this, SLOT( emitEndRemoveItems() ) );
283  child->setParent( 0 );
284  return child;
285 }
286 
287 int QgsDataItem::findItem( QVector<QgsDataItem*> items, QgsDataItem * item )
288 {
289  for ( int i = 0; i < items.size(); i++ )
290  {
291  QgsDebugMsgLevel( QString::number( i ) + " : " + items[i]->mPath + " x " + item->mPath, 2 );
292  if ( items[i]->equal( item ) )
293  return i;
294  }
295  return -1;
296 }
297 
299 {
300  QgsDebugMsgLevel( "mPath = " + mPath, 2 );
301 
302  QApplication::setOverrideCursor( Qt::WaitCursor );
303 
304  QVector<QgsDataItem*> items = createChildren();
305 
306  // Remove no more present items
307  QVector<QgsDataItem*> remove;
308  foreach ( QgsDataItem *child, mChildren )
309  {
310  if ( findItem( items, child ) >= 0 )
311  continue;
312  remove.append( child );
313  }
314  foreach ( QgsDataItem *child, remove )
315  {
316  deleteChildItem( child );
317  }
318 
319  // Add new items
320  foreach ( QgsDataItem *item, items )
321  {
322  // Is it present in childs?
323  if ( findItem( mChildren, item ) >= 0 )
324  {
325  delete item;
326  continue;
327  }
328  addChildItem( item, true );
329  }
330 
331  QApplication::restoreOverrideCursor();
332 }
333 
334 bool QgsDataItem::equal( const QgsDataItem *other )
335 {
336  if ( metaObject()->className() == other->metaObject()->className() &&
337  mPath == other->path() )
338  {
339  return true;
340  }
341  return false;
342 }
343 
344 // ---------------------------------------------------------------------
345 
346 QgsLayerItem::QgsLayerItem( QgsDataItem* parent, QString name, QString path, QString uri, LayerType layerType, QString providerKey )
347  : QgsDataItem( Layer, parent, name, path )
348  , mProviderKey( providerKey )
349  , mUri( uri )
350  , mLayerType( layerType )
351 {
352  switch ( layerType )
353  {
354  case Point: mIcon = iconPoint(); break;
355  case Line: mIcon = iconLine(); break;
356  case Polygon: mIcon = iconPolygon(); break;
357  // TODO add a new icon for generic Vector layers
358  case Vector : mIcon = iconPolygon(); break;
359  case TableLayer: mIcon = iconTable(); break;
360  case Raster: mIcon = iconRaster(); break;
361  default: mIcon = iconDefault(); break;
362  }
363 }
364 
366 {
370 }
371 
372 bool QgsLayerItem::equal( const QgsDataItem *other )
373 {
374  //QgsDebugMsg ( mPath + " x " + other->mPath );
375  if ( type() != other->type() )
376  {
377  return false;
378  }
379  //const QgsLayerItem *o = qobject_cast<const QgsLayerItem *> ( other );
380  const QgsLayerItem *o = dynamic_cast<const QgsLayerItem *>( other );
381  return ( mPath == o->mPath && mName == o->mName && mUri == o->mUri && mProviderKey == o->mProviderKey );
382 }
383 
384 // ---------------------------------------------------------------------
385 QgsDataCollectionItem::QgsDataCollectionItem( QgsDataItem* parent, QString name, QString path )
386  : QgsDataItem( Collection, parent, name, path )
387 {
389 }
390 
392 {
393  QgsDebugMsgLevel( "Entered", 2 );
394  foreach ( QgsDataItem* i, mChildren )
395  {
396  QgsDebugMsgLevel( QString( "delete child = 0x%0" ).arg(( qlonglong )i, 8, 16, QLatin1Char( '0' ) ), 2 );
397  delete i;
398  }
399 }
400 
401 //-----------------------------------------------------------------------
402 // QVector<QgsDataProvider*> QgsDirectoryItem::mProviders = QVector<QgsDataProvider*>();
403 QVector<QLibrary*> QgsDirectoryItem::mLibraries = QVector<QLibrary*>();
404 
405 
406 QgsDirectoryItem::QgsDirectoryItem( QgsDataItem* parent, QString name, QString path )
407  : QgsDataCollectionItem( parent, name, path )
408 {
409  mType = Directory;
410  mIcon = iconDir();
411 
412  if ( mLibraries.size() == 0 )
413  {
414  QStringList keys = QgsProviderRegistry::instance()->providerList();
415  QStringList::const_iterator i;
416  for ( i = keys.begin(); i != keys.end(); ++i )
417  {
418  QString k( *i );
419  // some providers hangs with empty uri (Postgis) etc...
420  // -> using libraries directly
421  QLibrary *library = QgsProviderRegistry::instance()->providerLibrary( k );
422  if ( library )
423  {
424  dataCapabilities_t * dataCapabilities = ( dataCapabilities_t * ) cast_to_fptr( library->resolve( "dataCapabilities" ) );
425  if ( !dataCapabilities )
426  {
427  QgsDebugMsg( library->fileName() + " does not have dataCapabilities" );
428  continue;
429  }
430  if ( dataCapabilities() == QgsDataProvider::NoDataCapabilities )
431  {
432  QgsDebugMsg( library->fileName() + " has NoDataCapabilities" );
433  continue;
434  }
435 
436  QgsDebugMsg( QString( "%1 dataCapabilities : %2" ).arg( library->fileName() ).arg( dataCapabilities() ) );
437  mLibraries.append( library );
438  }
439  else
440  {
441  //QgsDebugMsg ( "Cannot get provider " + k );
442  }
443  }
444  }
445 }
446 
448 {
449 }
450 
451 QVector<QgsDataItem*> QgsDirectoryItem::createChildren()
452 {
453  QVector<QgsDataItem*> children;
454  QDir dir( mPath );
455  QSettings settings;
456 
457  QStringList entries = dir.entryList( QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name | QDir::IgnoreCase );
458  foreach ( QString subdir, entries )
459  {
460  QString subdirPath = dir.absoluteFilePath( subdir );
461  QgsDebugMsgLevel( QString( "creating subdir: %1" ).arg( subdirPath ), 2 );
462 
463  QgsDirectoryItem *item = new QgsDirectoryItem( this, subdir, subdirPath );
464  // propagate signals up to top
465 
466  children.append( item );
467  }
468 
469  QStringList fileEntries = dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot | QDir::Files, QDir::Name );
470  foreach ( QString name, fileEntries )
471  {
472  QString path = dir.absoluteFilePath( name );
473  QFileInfo fileInfo( path );
474 
475  // vsizip support was added to GDAL/OGR 1.6 but GDAL_VERSION_NUM not available here
476  // so we assume it's available anyway
477  {
478  QgsDataItem * item = QgsZipItem::itemFromPath( this, path, name );
479  if ( item )
480  {
481  children.append( item );
482  continue;
483  }
484  }
485 
486  foreach ( QLibrary *library, mLibraries )
487  {
488  // we could/should create separate list of providers for each purpose
489 
490  // TODO: use existing fileVectorFilters(),directoryDrivers() ?
491  dataCapabilities_t * dataCapabilities = ( dataCapabilities_t * ) cast_to_fptr( library->resolve( "dataCapabilities" ) );
492  if ( !dataCapabilities )
493  {
494  continue;
495  }
496 
497  int capabilities = dataCapabilities();
498 
499  if ( !(( fileInfo.isFile() && ( capabilities & QgsDataProvider::File ) ) ||
500  ( fileInfo.isDir() && ( capabilities & QgsDataProvider::Dir ) ) ) )
501  {
502  continue;
503  }
504 
505  dataItem_t * dataItem = ( dataItem_t * ) cast_to_fptr( library->resolve( "dataItem" ) );
506  if ( ! dataItem )
507  {
508  QgsDebugMsg( library->fileName() + " does not have dataItem" );
509  continue;
510  }
511 
512  QgsDataItem * item = dataItem( path, this );
513  if ( item )
514  {
515  children.append( item );
516  }
517  }
518  }
519 
520  return children;
521 }
522 
524 {
525  //QgsDebugMsg ( mPath + " x " + other->mPath );
526  if ( type() != other->type() )
527  {
528  return false;
529  }
530  return ( path() == other->path() );
531 }
532 
534 {
535  return new QgsDirectoryParamWidget( mPath );
536 }
537 
538 QgsDirectoryParamWidget::QgsDirectoryParamWidget( QString path, QWidget* parent )
539  : QTreeWidget( parent )
540 {
541  setRootIsDecorated( false );
542 
543  // name, size, date, permissions, owner, group, type
544  setColumnCount( 7 );
545  QStringList labels;
546  labels << tr( "Name" ) << tr( "Size" ) << tr( "Date" ) << tr( "Permissions" ) << tr( "Owner" ) << tr( "Group" ) << tr( "Type" );
547  setHeaderLabels( labels );
548 
549  QStyle* style = QApplication::style();
550  QIcon iconDirectory = QIcon( style->standardPixmap( QStyle::SP_DirClosedIcon ) );
551  QIcon iconFile = QIcon( style->standardPixmap( QStyle::SP_FileIcon ) );
552  QIcon iconLink = QIcon( style->standardPixmap( QStyle::SP_FileLinkIcon ) ); // TODO: symlink to directory?
553 
554  QList<QTreeWidgetItem *> items;
555 
556  QDir dir( path );
557  QStringList entries = dir.entryList( QDir::AllEntries | QDir::NoDotAndDotDot, QDir::Name | QDir::IgnoreCase );
558  foreach ( QString name, entries )
559  {
560  QFileInfo fi( dir.absoluteFilePath( name ) );
561  QStringList texts;
562  texts << name;
563  QString size;
564  if ( fi.size() > 1024 )
565  {
566  size = size.sprintf( "%.1f KiB", fi.size() / 1024.0 );
567  }
568  else if ( fi.size() > 1.048576e6 )
569  {
570  size = size.sprintf( "%.1f MiB", fi.size() / 1.048576e6 );
571  }
572  else
573  {
574  size = QString( "%1 B" ).arg( fi.size() );
575  }
576  texts << size;
577  texts << fi.lastModified().toString( Qt::SystemLocaleShortDate );
578  QString perm;
579  perm += fi.permission( QFile::ReadOwner ) ? 'r' : '-';
580  perm += fi.permission( QFile::WriteOwner ) ? 'w' : '-';
581  perm += fi.permission( QFile::ExeOwner ) ? 'x' : '-';
582  // QFile::ReadUser, QFile::WriteUser, QFile::ExeUser
583  perm += fi.permission( QFile::ReadGroup ) ? 'r' : '-';
584  perm += fi.permission( QFile::WriteGroup ) ? 'w' : '-';
585  perm += fi.permission( QFile::ExeGroup ) ? 'x' : '-';
586  perm += fi.permission( QFile::ReadOther ) ? 'r' : '-';
587  perm += fi.permission( QFile::WriteOther ) ? 'w' : '-';
588  perm += fi.permission( QFile::ExeOther ) ? 'x' : '-';
589  texts << perm;
590 
591  texts << fi.owner();
592  texts << fi.group();
593 
594  QString type;
595  QIcon icon;
596  if ( fi.isDir() )
597  {
598  type = tr( "folder" );
599  icon = iconDirectory;
600  }
601  else if ( fi.isFile() )
602  {
603  type = tr( "file" );
604  icon = iconFile;
605  }
606  else if ( fi.isSymLink() )
607  {
608  type = tr( "link" );
609  icon = iconLink;
610  }
611 
612  texts << type;
613 
614  QTreeWidgetItem *item = new QTreeWidgetItem( texts );
615  item->setIcon( 0, icon );
616  items << item;
617  }
618 
619  addTopLevelItems( items );
620 
621  // hide columns that are not requested
622  QSettings settings;
623  QList<QVariant> lst = settings.value( "/dataitem/directoryHiddenColumns" ).toList();
624  foreach ( QVariant colVariant, lst )
625  {
626  setColumnHidden( colVariant.toInt(), true );
627  }
628 }
629 
631 {
632  if ( event->button() == Qt::RightButton )
633  {
634  // show the popup menu
635  QMenu popupMenu;
636 
637  QStringList labels;
638  labels << tr( "Name" ) << tr( "Size" ) << tr( "Date" ) << tr( "Permissions" ) << tr( "Owner" ) << tr( "Group" ) << tr( "Type" );
639  for ( int i = 0; i < labels.count(); i++ )
640  {
641  QAction* action = popupMenu.addAction( labels[i], this, SLOT( showHideColumn() ) );
642  action->setObjectName( QString::number( i ) );
643  action->setCheckable( true );
644  action->setChecked( !isColumnHidden( i ) );
645  }
646 
647  popupMenu.exec( event->globalPos() );
648  }
649 }
650 
652 {
653  QAction* action = qobject_cast<QAction*>( sender() );
654  if ( !action )
655  return; // something is wrong
656 
657  int columnIndex = action->objectName().toInt();
658  setColumnHidden( columnIndex, !isColumnHidden( columnIndex ) );
659 
660  // save in settings
661  QSettings settings;
662  QList<QVariant> lst;
663  for ( int i = 0; i < columnCount(); i++ )
664  {
665  if ( isColumnHidden( i ) )
666  lst.append( QVariant( i ) );
667  }
668  settings.setValue( "/dataitem/directoryHiddenColumns", lst );
669 }
670 
671 
672 QgsErrorItem::QgsErrorItem( QgsDataItem* parent, QString error, QString path )
673  : QgsDataItem( QgsDataItem::Error, parent, error, path )
674 {
675  mIcon = QIcon( QgsApplication::getThemePixmap( "/mIconDelete.png" ) );
676 
677  mPopulated = true; // no more children
678 }
679 
681 {
682 }
683 
684 QgsFavouritesItem::QgsFavouritesItem( QgsDataItem* parent, QString name, QString path )
685  : QgsDataCollectionItem( parent, name, path )
686 {
687  mType = Favourites;
688  mIcon = iconFavourites();
689 }
690 
692 {
693 }
694 
695 QVector<QgsDataItem*> QgsFavouritesItem::createChildren()
696 {
697  QVector<QgsDataItem*> children;
698 
699  QSettings settings;
700  QStringList favDirs = settings.value( "/browser/favourites", QVariant() ).toStringList();
701 
702  foreach ( QString favDir, favDirs )
703  {
704  QgsDataItem *item = new QgsDirectoryItem( this, favDir, favDir );
705  if ( item )
706  {
707  children.append( item );
708  }
709  }
710 
711  return children;
712 }
713 
714 void QgsFavouritesItem::addDirectory( QString favDir )
715 {
716  QSettings settings;
717  QStringList favDirs = settings.value( "/browser/favourites" ).toStringList();
718  favDirs.append( favDir );
719  settings.setValue( "/browser/favourites", favDirs );
720 
721  addChildItem( new QgsDirectoryItem( this, favDir, favDir ), true );
722 }
723 
725 {
726  if ( !item )
727  return;
728 
729  QSettings settings;
730  QStringList favDirs = settings.value( "/browser/favourites" ).toStringList();
731  favDirs.removeAll( item->path() );
732  settings.setValue( "/browser/favourites", favDirs );
733 
734  int idx = findItem( mChildren, item );
735  if ( idx < 0 )
736  {
737  QgsDebugMsg( QString( "favourites item %1 not found" ).arg( item->path() ) );
738  return;
739  }
740 
741  deleteChildItem( mChildren[idx] );
742 }
743 
744 //-----------------------------------------------------------------------
745 QStringList QgsZipItem::mProviderNames = QStringList();
746 QVector<dataItem_t *> QgsZipItem::mDataItemPtr = QVector<dataItem_t*>();
747 
748 
749 QgsZipItem::QgsZipItem( QgsDataItem* parent, QString name, QString path )
750  : QgsDataCollectionItem( parent, name, path )
751 {
752  mType = Collection; //Zip??
753  mIcon = iconZip();
754  mVsiPrefix = vsiPrefix( path );
755 
756  if ( mProviderNames.size() == 0 )
757  {
758  // QStringList keys = QgsProviderRegistry::instance()->providerList();
759  // only use GDAL and OGR providers as we use the VSIFILE mechanism
760  QStringList keys;
761  // keys << "ogr" << "gdal";
762  keys << "gdal" << "ogr";
763 
764  QStringList::const_iterator i;
765  for ( i = keys.begin(); i != keys.end(); ++i )
766  {
767  QString k( *i );
768  QgsDebugMsg( "provider " + k );
769  // some providers hangs with empty uri (Postgis) etc...
770  // -> using libraries directly
771  QLibrary *library = QgsProviderRegistry::instance()->providerLibrary( k );
772  if ( library )
773  {
774  dataCapabilities_t * dataCapabilities = ( dataCapabilities_t * ) cast_to_fptr( library->resolve( "dataCapabilities" ) );
775  if ( !dataCapabilities )
776  {
777  QgsDebugMsg( library->fileName() + " does not have dataCapabilities" );
778  continue;
779  }
780  if ( dataCapabilities() == QgsDataProvider::NoDataCapabilities )
781  {
782  QgsDebugMsg( library->fileName() + " has NoDataCapabilities" );
783  continue;
784  }
785  QgsDebugMsg( QString( "%1 dataCapabilities : %2" ).arg( library->fileName() ).arg( dataCapabilities() ) );
786 
787  dataItem_t * dataItem = ( dataItem_t * ) cast_to_fptr( library->resolve( "dataItem" ) );
788  if ( ! dataItem )
789  {
790  QgsDebugMsg( library->fileName() + " does not have dataItem" );
791  continue;
792  }
793 
794  // mLibraries.append( library );
795  mDataItemPtr.append( dataItem );
796  mProviderNames.append( k );
797  }
798  else
799  {
800  //QgsDebugMsg ( "Cannot get provider " + k );
801  }
802  }
803  }
804 }
805 
807 {
808 }
809 
810 // internal function to scan a vsidir (zip or tar file) recursively
811 // GDAL trunk has this since r24423 (05/16/12) - VSIReadDirRecursive()
812 // use a copy of the function internally for now,
813 // but use char ** and CSLAddString, because CPLStringList was added in gdal-1.9
814 char **VSIReadDirRecursive1( const char *pszPath )
815 {
816  // CPLStringList oFiles = NULL;
817  char **papszOFiles = NULL;
818  char **papszFiles1 = NULL;
819  char **papszFiles2 = NULL;
820  VSIStatBufL psStatBuf;
821  CPLString osTemp1, osTemp2;
822  int i, j;
823  int nCount1, nCount2;
824 
825  // get listing
826  papszFiles1 = VSIReadDir( pszPath );
827  if ( ! papszFiles1 )
828  return NULL;
829 
830  // get files and directories inside listing
831  nCount1 = CSLCount( papszFiles1 );
832  for ( i = 0; i < nCount1; i++ )
833  {
834  // build complete file name for stat
835  osTemp1.clear();
836  osTemp1.append( pszPath );
837  osTemp1.append( "/" );
838  osTemp1.append( papszFiles1[i] );
839 
840  // if is file, add it
841  if ( VSIStatL( osTemp1.c_str(), &psStatBuf ) == 0 &&
842  VSI_ISREG( psStatBuf.st_mode ) )
843  {
844  // oFiles.AddString( papszFiles1[i] );
845  papszOFiles = CSLAddString( papszOFiles, papszFiles1[i] );
846  }
847  else if ( VSIStatL( osTemp1.c_str(), &psStatBuf ) == 0 &&
848  VSI_ISDIR( psStatBuf.st_mode ) )
849  {
850  // add directory entry
851  osTemp2.clear();
852  osTemp2.append( papszFiles1[i] );
853  osTemp2.append( "/" );
854  // oFiles.AddString( osTemp2.c_str() );
855  papszOFiles = CSLAddString( papszOFiles, osTemp2.c_str() );
856 
857  // recursively add files inside directory
858  papszFiles2 = VSIReadDirRecursive1( osTemp1.c_str() );
859  if ( papszFiles2 )
860  {
861  nCount2 = CSLCount( papszFiles2 );
862  for ( j = 0; j < nCount2; j++ )
863  {
864  osTemp2.clear();
865  osTemp2.append( papszFiles1[i] );
866  osTemp2.append( "/" );
867  osTemp2.append( papszFiles2[j] );
868  // oFiles.AddString( osTemp2.c_str() );
869  papszOFiles = CSLAddString( papszOFiles, osTemp2.c_str() );
870  }
871  CSLDestroy( papszFiles2 );
872  }
873  }
874  }
875  CSLDestroy( papszFiles1 );
876 
877  // return oFiles.StealList();
878  return papszOFiles;
879 }
880 
881 QVector<QgsDataItem*> QgsZipItem::createChildren()
882 {
883  QVector<QgsDataItem*> children;
884  QString tmpPath;
885  QString childPath;
886  QSettings settings;
887  QString scanZipSetting = settings.value( "/qgis/scanZipInBrowser2", "basic" ).toString();
888 
889  mZipFileList.clear();
890 
891  QgsDebugMsgLevel( QString( "path = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( path() ).arg( name() ).arg( scanZipSetting ).arg( mVsiPrefix ), 2 );
892 
893  // if scanZipBrowser == no: skip to the next file
894  if ( scanZipSetting == "no" )
895  {
896  return children;
897  }
898 
899  // first get list of files
900  getZipFileList();
901 
902  // loop over files inside zip
903  foreach ( QString fileName, mZipFileList )
904  {
905  QFileInfo info( fileName );
906  tmpPath = mVsiPrefix + path() + "/" + fileName;
907  QgsDebugMsgLevel( "tmpPath = " + tmpPath, 3 );
908 
909  // foreach( dataItem_t *dataItem, mDataItemPtr )
910  for ( int i = 0; i < mProviderNames.size(); i++ )
911  {
912  // ugly hack to remove .dbf file if there is a .shp file
913  if ( mProviderNames[i] == "ogr" )
914  {
915  if ( info.suffix().toLower() == "dbf" )
916  {
917  if ( mZipFileList.indexOf( fileName.left( fileName.count() - 4 ) + ".shp" ) != -1 )
918  continue;
919  }
920  if ( info.completeSuffix().toLower() == "shp.xml" )
921  {
922  continue;
923  }
924  }
925 
926  // try to get data item from provider
927  dataItem_t *dataItem = mDataItemPtr[i];
928  if ( dataItem )
929  {
930  QgsDebugMsgLevel( QString( "trying to load item %1 with %2" ).arg( tmpPath ).arg( mProviderNames[i] ), 3 );
931  QgsDataItem * item = dataItem( tmpPath, this );
932  if ( item )
933  {
934  QgsDebugMsgLevel( "loaded item", 3 );
935  childPath = tmpPath;
936  children.append( item );
937  break;
938  }
939  else
940  {
941  QgsDebugMsgLevel( "not loaded item", 3 );
942  }
943  }
944  }
945 
946  }
947 
948  if ( children.size() == 1 )
949  {
950  // save the name of the only child so we can get a normal data item from it
951  mPath = childPath;
952  }
953 
954  return children;
955 }
956 
957 QString QgsZipItem::vsiPrefix( QString path )
958 {
959  if ( path.startsWith( "/vsizip/", Qt::CaseInsensitive ) ||
960  path.endsWith( ".zip", Qt::CaseInsensitive ) )
961  return "/vsizip/";
962  else if ( path.startsWith( "/vsitar/", Qt::CaseInsensitive ) ||
963  path.endsWith( ".tar", Qt::CaseInsensitive ) ||
964  path.endsWith( ".tar.gz", Qt::CaseInsensitive ) ||
965  path.endsWith( ".tgz", Qt::CaseInsensitive ) )
966  return "/vsitar/";
967  else if ( path.startsWith( "/vsigzip/", Qt::CaseInsensitive ) ||
968  path.endsWith( ".gz", Qt::CaseInsensitive ) )
969  return "/vsigzip/";
970  else
971  return "";
972 }
973 
974 QgsDataItem* QgsZipItem::itemFromPath( QgsDataItem* parent, QString path, QString name )
975 {
976  QSettings settings;
977  QString scanZipSetting = settings.value( "/qgis/scanZipInBrowser2", "basic" ).toString();
978  QString vsiPath = path;
979  int zipFileCount = 0;
980  QStringList zipFileList;
981  QFileInfo fileInfo( path );
982  QString vsiPrefix = QgsZipItem::vsiPrefix( path );
983  QgsZipItem * zipItem = 0;
984  bool populated = false;
985 
986  QgsDebugMsgLevel( QString( "path = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( path ).arg( name ).arg( scanZipSetting ).arg( vsiPrefix ), 3 );
987 
988  // don't scan if scanZipBrowser == no
989  if ( scanZipSetting == "no" )
990  return 0;
991 
992  // don't scan if this file is not a /vsizip/ or /vsitar/ item
993  if (( vsiPrefix != "/vsizip/" && vsiPrefix != "/vsitar/" ) )
994  return 0;
995 
996  zipItem = new QgsZipItem( parent, name, path );
997 
998  if ( zipItem )
999  {
1000  // force populate zipItem if it has less than 10 items and is not a .tgz or .tar.gz file (slow loading)
1001  // for other items populating will be delayed until item is opened
1002  // this might be polluting the tree with empty items but is necessary for performance reasons
1003  // could also accept all files smaller than a certain size and add options for file count and/or size
1004 
1005  // first get list of files inside .zip or .tar files
1006  if ( path.endsWith( ".zip", Qt::CaseInsensitive ) ||
1007  path.endsWith( ".tar", Qt::CaseInsensitive ) )
1008  {
1009  zipFileList = zipItem->getZipFileList();
1010  }
1011  // force populate if less than 10 items
1012  if ( zipFileList.count() > 0 && zipFileList.count() <= 10 )
1013  {
1014  zipItem->populate();
1015  populated = true; // there is no QgsDataItem::isPopulated() function
1016  QgsDebugMsgLevel( QString( "Got zipItem with %1 children, path=%2, name=%3" ).arg( zipItem->rowCount() ).arg( zipItem->path() ).arg( zipItem->name() ), 3 );
1017  }
1018  else
1019  {
1020  QgsDebugMsgLevel( QString( "Delaying populating zipItem with path=%1, name=%2" ).arg( zipItem->path() ).arg( zipItem->name() ), 3 );
1021  }
1022  }
1023 
1024  // only display if has children or if is not populated
1025  if ( zipItem && ( !populated || zipItem->rowCount() > 1 ) )
1026  {
1027  QgsDebugMsgLevel( "returning zipItem", 3 );
1028  return zipItem;
1029  }
1030  // if 1 or 0 child found, create a single data item using the normal path or the full path given by QgsZipItem
1031  else
1032  {
1033  if ( zipItem )
1034  {
1035  vsiPath = zipItem->path();
1036  zipFileCount = zipFileList.count();
1037  delete zipItem;
1038  }
1039 
1040  QgsDebugMsgLevel( QString( "will try to create a normal dataItem from path= %2 or %3" ).arg( path ).arg( vsiPath ), 3 );
1041 
1042  // try to open using registered providers (gdal and ogr)
1043  for ( int i = 0; i < mProviderNames.size(); i++ )
1044  {
1045  dataItem_t *dataItem = mDataItemPtr[i];
1046  if ( dataItem )
1047  {
1048  QgsDataItem *item = 0;
1049  // try first with normal path (Passthru)
1050  // this is to simplify .qml handling, and without this some tests will fail
1051  // (e.g. testZipItemVectorTransparency(), second test)
1052  if (( mProviderNames[i] == "ogr" ) ||
1053  ( mProviderNames[i] == "gdal" && zipFileCount == 1 ) )
1054  item = dataItem( path, parent );
1055  // try with /vsizip/
1056  if ( ! item )
1057  item = dataItem( vsiPath, parent );
1058  if ( item )
1059  return item;
1060  }
1061  }
1062  }
1063 
1064  return 0;
1065 }
1066 
1067 const QStringList & QgsZipItem::getZipFileList()
1068 {
1069  if ( ! mZipFileList.isEmpty() )
1070  return mZipFileList;
1071 
1072  QString tmpPath;
1073  QSettings settings;
1074  QString scanZipSetting = settings.value( "/qgis/scanZipInBrowser2", "basic" ).toString();
1075 
1076  QgsDebugMsgLevel( QString( "path = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( path() ).arg( name() ).arg( scanZipSetting ).arg( mVsiPrefix ), 3 );
1077 
1078  // if scanZipBrowser == no: skip to the next file
1079  if ( scanZipSetting == "no" )
1080  {
1081  return mZipFileList;
1082  }
1083 
1084  // get list of files inside zip file
1085  QgsDebugMsgLevel( QString( "Open file %1 with gdal vsi" ).arg( mVsiPrefix + path() ), 3 );
1086  char **papszSiblingFiles = VSIReadDirRecursive1( QString( mVsiPrefix + path() ).toLocal8Bit().constData() );
1087  if ( papszSiblingFiles )
1088  {
1089  for ( int i = 0; i < CSLCount( papszSiblingFiles ); i++ )
1090  {
1091  tmpPath = papszSiblingFiles[i];
1092  QgsDebugMsgLevel( QString( "Read file %1" ).arg( tmpPath ), 3 );
1093  // skip directories (files ending with /)
1094  if ( tmpPath.right( 1 ) != "/" )
1095  mZipFileList << tmpPath;
1096  }
1097  CSLDestroy( papszSiblingFiles );
1098  }
1099  else
1100  {
1101  QgsDebugMsg( QString( "Error reading %1" ).arg( path() ) );
1102  }
1103 
1104  return mZipFileList;
1105 }