QGIS API Documentation  master-59fd5e0
src/core/qgsdataitem.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                qgsdataitem.cpp  - Data items
00003                              -------------------
00004     begin                : 2011-04-01
00005     copyright            : (C) 2011 Radim Blazek
00006     email                : radim dot blazek at gmail dot com
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include <QApplication>
00019 #include <QDateTime>
00020 #include <QDir>
00021 #include <QFileInfo>
00022 #include <QMenu>
00023 #include <QMouseEvent>
00024 #include <QTreeWidget>
00025 #include <QTreeWidgetItem>
00026 #include <QVector>
00027 #include <QStyle>
00028 #include <QSettings>
00029 
00030 #include "qgis.h"
00031 #include "qgsdataitem.h"
00032 
00033 #include "qgsdataprovider.h"
00034 #include "qgslogger.h"
00035 #include "qgsproviderregistry.h"
00036 #include "qgsconfig.h"
00037 
00038 // use GDAL VSI mechanism
00039 #include "cpl_vsi.h"
00040 #include "cpl_string.h"
00041 
00042 // shared icons
00043 const QIcon &QgsLayerItem::iconPoint()
00044 {
00045   static QIcon icon;
00046 
00047   if ( icon.isNull() )
00048     icon = QgsApplication::getThemeIcon( "/mIconPointLayer.png" );
00049 
00050   return icon;
00051 }
00052 
00053 const QIcon &QgsLayerItem::iconLine()
00054 {
00055   static QIcon icon;
00056 
00057   if ( icon.isNull() )
00058     icon = QgsApplication::getThemeIcon( "/mIconLineLayer.png" );
00059 
00060   return icon;
00061 }
00062 
00063 const QIcon &QgsLayerItem::iconPolygon()
00064 {
00065   static QIcon icon;
00066 
00067   if ( icon.isNull() )
00068     icon = QgsApplication::getThemeIcon( "/mIconPolygonLayer.png" );
00069 
00070   return icon;
00071 }
00072 
00073 const QIcon &QgsLayerItem::iconTable()
00074 {
00075   static QIcon icon;
00076 
00077   if ( icon.isNull() )
00078     icon = QgsApplication::getThemeIcon( "/mIconTableLayer.png" );
00079 
00080   return icon;
00081 }
00082 
00083 const QIcon &QgsLayerItem::iconRaster()
00084 {
00085   static QIcon icon;
00086 
00087   if ( icon.isNull() )
00088     icon = QgsApplication::getThemeIcon( "/mIconRaster.png" );
00089 
00090   return icon;
00091 }
00092 
00093 const QIcon &QgsLayerItem::iconDefault()
00094 {
00095   static QIcon icon;
00096 
00097   if ( icon.isNull() )
00098     icon = QgsApplication::getThemeIcon( "/mIconLayer.png" );
00099 
00100   return icon;
00101 }
00102 
00103 const QIcon &QgsDataCollectionItem::iconDataCollection()
00104 {
00105   static QIcon icon;
00106 
00107   if ( icon.isNull() )
00108     icon = QgsApplication::getThemeIcon( "/mIconDbSchema.png" );
00109 
00110   return icon;
00111 }
00112 
00113 const QIcon &QgsDataCollectionItem::iconDir()
00114 {
00115   static QIcon icon;
00116 
00117   if ( icon.isNull() )
00118   {
00119     // initialize shared icons
00120     QStyle *style = QApplication::style();
00121     icon = QIcon( style->standardPixmap( QStyle::SP_DirClosedIcon ) );
00122     icon.addPixmap( style->standardPixmap( QStyle::SP_DirOpenIcon ),
00123                     QIcon::Normal, QIcon::On );
00124   }
00125 
00126   return icon;
00127 }
00128 
00129 const QIcon &QgsFavouritesItem::iconFavourites()
00130 {
00131   static QIcon icon;
00132 
00133   if ( icon.isNull() )
00134     icon = QgsApplication::getThemeIcon( "/mIconFavourites.png" );
00135 
00136   return icon;
00137 }
00138 
00139 const QIcon &QgsZipItem::iconZip()
00140 {
00141   static QIcon icon;
00142 
00143   if ( icon.isNull() )
00144     icon = QgsApplication::getThemeIcon( "/mIconZip.png" );
00145 // icon from http://www.softicons.com/free-icons/application-icons/mega-pack-icons-1-by-nikolay-verin/winzip-folder-icon
00146 
00147   return icon;
00148 }
00149 
00150 
00151 QgsDataItem::QgsDataItem( QgsDataItem::Type type, QgsDataItem* parent, QString name, QString path )
00152 // Do not pass parent to QObject, Qt would delete this when parent is deleted
00153     : QObject(), mType( type ), mParent( parent ), mPopulated( false ), mName( name ), mPath( path )
00154 {
00155 }
00156 
00157 QgsDataItem::~QgsDataItem()
00158 {
00159   QgsDebugMsgLevel( "mName = " + mName + " mPath = " + mPath, 2 );
00160 }
00161 
00162 void QgsDataItem::emitBeginInsertItems( QgsDataItem* parent, int first, int last )
00163 {
00164   emit beginInsertItems( parent, first, last );
00165 }
00166 void QgsDataItem::emitEndInsertItems()
00167 {
00168   emit endInsertItems();
00169 }
00170 void QgsDataItem::emitBeginRemoveItems( QgsDataItem* parent, int first, int last )
00171 {
00172   emit beginRemoveItems( parent, first, last );
00173 }
00174 void QgsDataItem::emitEndRemoveItems()
00175 {
00176   emit endRemoveItems();
00177 }
00178 
00179 QVector<QgsDataItem*> QgsDataItem::createChildren()
00180 {
00181   return QVector<QgsDataItem*>();
00182 }
00183 
00184 void QgsDataItem::populate()
00185 {
00186   if ( mPopulated )
00187     return;
00188 
00189   QgsDebugMsg( "mPath = " + mPath );
00190 
00191   QApplication::setOverrideCursor( Qt::WaitCursor );
00192 
00193   QVector<QgsDataItem*> children = createChildren();
00194   foreach ( QgsDataItem *child, children )
00195   {
00196     // initialization, do not refresh! That would result in infinite loop (beginInsertItems->rowCount->populate)
00197     addChildItem( child );
00198   }
00199   mPopulated = true;
00200 
00201   QApplication::restoreOverrideCursor();
00202 }
00203 
00204 int QgsDataItem::rowCount()
00205 {
00206   // if ( !mPopulated )
00207   //   populate();
00208   return mChildren.size();
00209 }
00210 bool QgsDataItem::hasChildren()
00211 {
00212   return ( mPopulated ? mChildren.count() > 0 : true );
00213 }
00214 
00215 void QgsDataItem::addChildItem( QgsDataItem * child, bool refresh )
00216 {
00217   QgsDebugMsg( QString( "path = %1 add child #%2 - %3 - %4" ).arg( mPath ).arg( mChildren.size() ).arg( child->mName ).arg( child->mType ) );
00218 
00219   int i;
00220   if ( type() == Directory )
00221   {
00222     for ( i = 0; i < mChildren.size(); i++ )
00223     {
00224       // sort items by type, so directories are before data items
00225       if ( mChildren[i]->mType == child->mType &&
00226            mChildren[i]->mName.localeAwareCompare( child->mName ) >= 0 )
00227         break;
00228     }
00229   }
00230   else
00231   {
00232     for ( i = 0; i < mChildren.size(); i++ )
00233     {
00234       if ( mChildren[i]->mName.localeAwareCompare( child->mName ) >= 0 )
00235         break;
00236     }
00237   }
00238 
00239   if ( refresh )
00240     emit beginInsertItems( this, i, i );
00241 
00242   mChildren.insert( i, child );
00243 
00244   connect( child, SIGNAL( beginInsertItems( QgsDataItem*, int, int ) ),
00245            this, SLOT( emitBeginInsertItems( QgsDataItem*, int, int ) ) );
00246   connect( child, SIGNAL( endInsertItems() ),
00247            this, SLOT( emitEndInsertItems() ) );
00248   connect( child, SIGNAL( beginRemoveItems( QgsDataItem*, int, int ) ),
00249            this, SLOT( emitBeginRemoveItems( QgsDataItem*, int, int ) ) );
00250   connect( child, SIGNAL( endRemoveItems() ),
00251            this, SLOT( emitEndRemoveItems() ) );
00252 
00253   if ( refresh )
00254     emit endInsertItems();
00255 }
00256 void QgsDataItem::deleteChildItem( QgsDataItem * child )
00257 {
00258   QgsDebugMsgLevel( "mName = " + child->mName, 2 );
00259   int i = mChildren.indexOf( child );
00260   Q_ASSERT( i >= 0 );
00261   emit beginRemoveItems( this, i, i );
00262   mChildren.remove( i );
00263   delete child;
00264   emit endRemoveItems();
00265 }
00266 
00267 QgsDataItem * QgsDataItem::removeChildItem( QgsDataItem * child )
00268 {
00269   QgsDebugMsgLevel( "mName = " + child->mName, 2 );
00270   int i = mChildren.indexOf( child );
00271   Q_ASSERT( i >= 0 );
00272   emit beginRemoveItems( this, i, i );
00273   mChildren.remove( i );
00274   emit endRemoveItems();
00275   disconnect( child, SIGNAL( beginInsertItems( QgsDataItem*, int, int ) ),
00276               this, SLOT( emitBeginInsertItems( QgsDataItem*, int, int ) ) );
00277   disconnect( child, SIGNAL( endInsertItems() ),
00278               this, SLOT( emitEndInsertItems() ) );
00279   disconnect( child, SIGNAL( beginRemoveItems( QgsDataItem*, int, int ) ),
00280               this, SLOT( emitBeginRemoveItems( QgsDataItem*, int, int ) ) );
00281   disconnect( child, SIGNAL( endRemoveItems() ),
00282               this, SLOT( emitEndRemoveItems() ) );
00283   child->setParent( 0 );
00284   return child;
00285 }
00286 
00287 int QgsDataItem::findItem( QVector<QgsDataItem*> items, QgsDataItem * item )
00288 {
00289   for ( int i = 0; i < items.size(); i++ )
00290   {
00291     QgsDebugMsgLevel( QString::number( i ) + " : " + items[i]->mPath + " x " + item->mPath, 2 );
00292     if ( items[i]->equal( item ) )
00293       return i;
00294   }
00295   return -1;
00296 }
00297 
00298 void QgsDataItem::refresh()
00299 {
00300   QgsDebugMsgLevel( "mPath = " + mPath, 2 );
00301 
00302   QApplication::setOverrideCursor( Qt::WaitCursor );
00303 
00304   QVector<QgsDataItem*> items = createChildren();
00305 
00306   // Remove no more present items
00307   QVector<QgsDataItem*> remove;
00308   foreach ( QgsDataItem *child, mChildren )
00309   {
00310     if ( findItem( items, child ) >= 0 )
00311       continue;
00312     remove.append( child );
00313   }
00314   foreach ( QgsDataItem *child, remove )
00315   {
00316     deleteChildItem( child );
00317   }
00318 
00319   // Add new items
00320   foreach ( QgsDataItem *item, items )
00321   {
00322     // Is it present in childs?
00323     if ( findItem( mChildren, item ) >= 0 )
00324     {
00325       delete item;
00326       continue;
00327     }
00328     addChildItem( item, true );
00329   }
00330 
00331   QApplication::restoreOverrideCursor();
00332 }
00333 
00334 bool QgsDataItem::equal( const QgsDataItem *other )
00335 {
00336   if ( metaObject()->className() == other->metaObject()->className() &&
00337        mPath == other->path() )
00338   {
00339     return true;
00340   }
00341   return false;
00342 }
00343 
00344 // ---------------------------------------------------------------------
00345 
00346 QgsLayerItem::QgsLayerItem( QgsDataItem* parent, QString name, QString path, QString uri, LayerType layerType, QString providerKey )
00347     : QgsDataItem( Layer, parent, name, path )
00348     , mProviderKey( providerKey )
00349     , mUri( uri )
00350     , mLayerType( layerType )
00351 {
00352   switch ( layerType )
00353   {
00354     case Point:      mIcon = iconPoint(); break;
00355     case Line:       mIcon = iconLine(); break;
00356     case Polygon:    mIcon = iconPolygon(); break;
00357       // TODO add a new icon for generic Vector layers
00358     case Vector :    mIcon = iconPolygon(); break;
00359     case TableLayer: mIcon = iconTable(); break;
00360     case Raster:     mIcon = iconRaster(); break;
00361     default:         mIcon = iconDefault(); break;
00362   }
00363 }
00364 
00365 QgsMapLayer::LayerType QgsLayerItem::mapLayerType()
00366 {
00367   if ( mLayerType == QgsLayerItem::Raster )
00368     return QgsMapLayer::RasterLayer;
00369   return QgsMapLayer::VectorLayer;
00370 }
00371 
00372 bool QgsLayerItem::equal( const QgsDataItem *other )
00373 {
00374   //QgsDebugMsg ( mPath + " x " + other->mPath );
00375   if ( type() != other->type() )
00376   {
00377     return false;
00378   }
00379   //const QgsLayerItem *o = qobject_cast<const QgsLayerItem *> ( other );
00380   const QgsLayerItem *o = dynamic_cast<const QgsLayerItem *>( other );
00381   return ( mPath == o->mPath && mName == o->mName && mUri == o->mUri && mProviderKey == o->mProviderKey );
00382 }
00383 
00384 // ---------------------------------------------------------------------
00385 QgsDataCollectionItem::QgsDataCollectionItem( QgsDataItem* parent, QString name, QString path )
00386     : QgsDataItem( Collection, parent, name, path )
00387 {
00388   mIcon = iconDataCollection();
00389 }
00390 
00391 QgsDataCollectionItem::~QgsDataCollectionItem()
00392 {
00393   QgsDebugMsgLevel( "Entered", 2 );
00394   foreach ( QgsDataItem* i, mChildren )
00395   {
00396     QgsDebugMsgLevel( QString( "delete child = 0x%0" ).arg(( qlonglong )i, 8, 16, QLatin1Char( '0' ) ), 2 );
00397     delete i;
00398   }
00399 }
00400 
00401 //-----------------------------------------------------------------------
00402 // QVector<QgsDataProvider*> QgsDirectoryItem::mProviders = QVector<QgsDataProvider*>();
00403 QVector<QLibrary*> QgsDirectoryItem::mLibraries = QVector<QLibrary*>();
00404 
00405 
00406 QgsDirectoryItem::QgsDirectoryItem( QgsDataItem* parent, QString name, QString path )
00407     : QgsDataCollectionItem( parent, name, path )
00408 {
00409   mType = Directory;
00410   mIcon = iconDir();
00411 
00412   if ( mLibraries.size() == 0 )
00413   {
00414     QStringList keys = QgsProviderRegistry::instance()->providerList();
00415     QStringList::const_iterator i;
00416     for ( i = keys.begin(); i != keys.end(); ++i )
00417     {
00418       QString k( *i );
00419       // some providers hangs with empty uri (Postgis) etc...
00420       // -> using libraries directly
00421       QLibrary *library = QgsProviderRegistry::instance()->providerLibrary( k );
00422       if ( library )
00423       {
00424         dataCapabilities_t * dataCapabilities = ( dataCapabilities_t * ) cast_to_fptr( library->resolve( "dataCapabilities" ) );
00425         if ( !dataCapabilities )
00426         {
00427           QgsDebugMsg( library->fileName() + " does not have dataCapabilities" );
00428           continue;
00429         }
00430         if ( dataCapabilities() == QgsDataProvider::NoDataCapabilities )
00431         {
00432           QgsDebugMsg( library->fileName() + " has NoDataCapabilities" );
00433           continue;
00434         }
00435 
00436         QgsDebugMsg( QString( "%1 dataCapabilities : %2" ).arg( library->fileName() ).arg( dataCapabilities() ) );
00437         mLibraries.append( library );
00438       }
00439       else
00440       {
00441         //QgsDebugMsg ( "Cannot get provider " + k );
00442       }
00443     }
00444   }
00445 }
00446 
00447 QgsDirectoryItem::~QgsDirectoryItem()
00448 {
00449 }
00450 
00451 QVector<QgsDataItem*> QgsDirectoryItem::createChildren()
00452 {
00453   QVector<QgsDataItem*> children;
00454   QDir dir( mPath );
00455   QSettings settings;
00456 
00457   QStringList entries = dir.entryList( QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name | QDir::IgnoreCase );
00458   foreach ( QString subdir, entries )
00459   {
00460     QString subdirPath = dir.absoluteFilePath( subdir );
00461     QgsDebugMsgLevel( QString( "creating subdir: %1" ).arg( subdirPath ), 2 );
00462 
00463     QgsDirectoryItem *item = new QgsDirectoryItem( this, subdir, subdirPath );
00464     // propagate signals up to top
00465 
00466     children.append( item );
00467   }
00468 
00469   QStringList fileEntries = dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot | QDir::Files, QDir::Name );
00470   foreach ( QString name, fileEntries )
00471   {
00472     QString path = dir.absoluteFilePath( name );
00473     QFileInfo fileInfo( path );
00474 
00475     // vsizip support was added to GDAL/OGR 1.6 but GDAL_VERSION_NUM not available here
00476     //   so we assume it's available anyway
00477     {
00478       QgsDataItem * item = QgsZipItem::itemFromPath( this, path, name );
00479       if ( item )
00480       {
00481         children.append( item );
00482         continue;
00483       }
00484     }
00485 
00486     foreach ( QLibrary *library, mLibraries )
00487     {
00488       // we could/should create separate list of providers for each purpose
00489 
00490       // TODO: use existing fileVectorFilters(),directoryDrivers() ?
00491       dataCapabilities_t * dataCapabilities = ( dataCapabilities_t * ) cast_to_fptr( library->resolve( "dataCapabilities" ) );
00492       if ( !dataCapabilities )
00493       {
00494         continue;
00495       }
00496 
00497       int capabilities = dataCapabilities();
00498 
00499       if ( !(( fileInfo.isFile() && ( capabilities & QgsDataProvider::File ) ) ||
00500              ( fileInfo.isDir() && ( capabilities & QgsDataProvider::Dir ) ) ) )
00501       {
00502         continue;
00503       }
00504 
00505       dataItem_t * dataItem = ( dataItem_t * ) cast_to_fptr( library->resolve( "dataItem" ) );
00506       if ( ! dataItem )
00507       {
00508         QgsDebugMsg( library->fileName() + " does not have dataItem" );
00509         continue;
00510       }
00511 
00512       QgsDataItem * item = dataItem( path, this );
00513       if ( item )
00514       {
00515         children.append( item );
00516       }
00517     }
00518   }
00519 
00520   return children;
00521 }
00522 
00523 bool QgsDirectoryItem::equal( const QgsDataItem *other )
00524 {
00525   //QgsDebugMsg ( mPath + " x " + other->mPath );
00526   if ( type() != other->type() )
00527   {
00528     return false;
00529   }
00530   return ( path() == other->path() );
00531 }
00532 
00533 QWidget * QgsDirectoryItem::paramWidget()
00534 {
00535   return new QgsDirectoryParamWidget( mPath );
00536 }
00537 
00538 QgsDirectoryParamWidget::QgsDirectoryParamWidget( QString path, QWidget* parent )
00539     : QTreeWidget( parent )
00540 {
00541   setRootIsDecorated( false );
00542 
00543   // name, size, date, permissions, owner, group, type
00544   setColumnCount( 7 );
00545   QStringList labels;
00546   labels << tr( "Name" ) << tr( "Size" ) << tr( "Date" ) << tr( "Permissions" ) << tr( "Owner" ) << tr( "Group" ) << tr( "Type" );
00547   setHeaderLabels( labels );
00548 
00549   QStyle* style = QApplication::style();
00550   QIcon iconDirectory = QIcon( style->standardPixmap( QStyle::SP_DirClosedIcon ) );
00551   QIcon iconFile = QIcon( style->standardPixmap( QStyle::SP_FileIcon ) );
00552   QIcon iconLink = QIcon( style->standardPixmap( QStyle::SP_FileLinkIcon ) ); // TODO: symlink to directory?
00553 
00554   QList<QTreeWidgetItem *> items;
00555 
00556   QDir dir( path );
00557   QStringList entries = dir.entryList( QDir::AllEntries | QDir::NoDotAndDotDot, QDir::Name | QDir::IgnoreCase );
00558   foreach ( QString name, entries )
00559   {
00560     QFileInfo fi( dir.absoluteFilePath( name ) );
00561     QStringList texts;
00562     texts << name;
00563     QString size;
00564     if ( fi.size() > 1024 )
00565     {
00566       size = size.sprintf( "%.1f KiB", fi.size() / 1024.0 );
00567     }
00568     else if ( fi.size() > 1.048576e6 )
00569     {
00570       size = size.sprintf( "%.1f MiB", fi.size() / 1.048576e6 );
00571     }
00572     else
00573     {
00574       size = QString( "%1 B" ).arg( fi.size() );
00575     }
00576     texts << size;
00577     texts << fi.lastModified().toString( Qt::SystemLocaleShortDate );
00578     QString perm;
00579     perm += fi.permission( QFile::ReadOwner ) ? 'r' : '-';
00580     perm += fi.permission( QFile::WriteOwner ) ? 'w' : '-';
00581     perm += fi.permission( QFile::ExeOwner ) ? 'x' : '-';
00582     // QFile::ReadUser, QFile::WriteUser, QFile::ExeUser
00583     perm += fi.permission( QFile::ReadGroup ) ? 'r' : '-';
00584     perm += fi.permission( QFile::WriteGroup ) ? 'w' : '-';
00585     perm += fi.permission( QFile::ExeGroup ) ? 'x' : '-';
00586     perm += fi.permission( QFile::ReadOther ) ? 'r' : '-';
00587     perm += fi.permission( QFile::WriteOther ) ? 'w' : '-';
00588     perm += fi.permission( QFile::ExeOther ) ? 'x' : '-';
00589     texts << perm;
00590 
00591     texts << fi.owner();
00592     texts << fi.group();
00593 
00594     QString type;
00595     QIcon icon;
00596     if ( fi.isDir() )
00597     {
00598       type = tr( "folder" );
00599       icon = iconDirectory;
00600     }
00601     else if ( fi.isFile() )
00602     {
00603       type = tr( "file" );
00604       icon = iconFile;
00605     }
00606     else if ( fi.isSymLink() )
00607     {
00608       type = tr( "link" );
00609       icon = iconLink;
00610     }
00611 
00612     texts << type;
00613 
00614     QTreeWidgetItem *item = new QTreeWidgetItem( texts );
00615     item->setIcon( 0, icon );
00616     items << item;
00617   }
00618 
00619   addTopLevelItems( items );
00620 
00621   // hide columns that are not requested
00622   QSettings settings;
00623   QList<QVariant> lst = settings.value( "/dataitem/directoryHiddenColumns" ).toList();
00624   foreach ( QVariant colVariant, lst )
00625   {
00626     setColumnHidden( colVariant.toInt(), true );
00627   }
00628 }
00629 
00630 void QgsDirectoryParamWidget::mousePressEvent( QMouseEvent* event )
00631 {
00632   if ( event->button() == Qt::RightButton )
00633   {
00634     // show the popup menu
00635     QMenu popupMenu;
00636 
00637     QStringList labels;
00638     labels << tr( "Name" ) << tr( "Size" ) << tr( "Date" ) << tr( "Permissions" ) << tr( "Owner" ) << tr( "Group" ) << tr( "Type" );
00639     for ( int i = 0; i < labels.count(); i++ )
00640     {
00641       QAction* action = popupMenu.addAction( labels[i], this, SLOT( showHideColumn() ) );
00642       action->setObjectName( QString::number( i ) );
00643       action->setCheckable( true );
00644       action->setChecked( !isColumnHidden( i ) );
00645     }
00646 
00647     popupMenu.exec( event->globalPos() );
00648   }
00649 }
00650 
00651 void QgsDirectoryParamWidget::showHideColumn()
00652 {
00653   QAction* action = qobject_cast<QAction*>( sender() );
00654   if ( !action )
00655     return; // something is wrong
00656 
00657   int columnIndex = action->objectName().toInt();
00658   setColumnHidden( columnIndex, !isColumnHidden( columnIndex ) );
00659 
00660   // save in settings
00661   QSettings settings;
00662   QList<QVariant> lst;
00663   for ( int i = 0; i < columnCount(); i++ )
00664   {
00665     if ( isColumnHidden( i ) )
00666       lst.append( QVariant( i ) );
00667   }
00668   settings.setValue( "/dataitem/directoryHiddenColumns", lst );
00669 }
00670 
00671 
00672 QgsErrorItem::QgsErrorItem( QgsDataItem* parent, QString error, QString path )
00673     : QgsDataItem( QgsDataItem::Error, parent, error, path )
00674 {
00675   mIcon = QIcon( QgsApplication::getThemePixmap( "/mIconDelete.png" ) );
00676 
00677   mPopulated = true; // no more children
00678 }
00679 
00680 QgsErrorItem::~QgsErrorItem()
00681 {
00682 }
00683 
00684 QgsFavouritesItem::QgsFavouritesItem( QgsDataItem* parent, QString name, QString path )
00685     : QgsDataCollectionItem( parent, name, path )
00686 {
00687   mType = Favourites;
00688   mIcon = iconFavourites();
00689 }
00690 
00691 QgsFavouritesItem::~QgsFavouritesItem()
00692 {
00693 }
00694 
00695 QVector<QgsDataItem*> QgsFavouritesItem::createChildren()
00696 {
00697   QVector<QgsDataItem*> children;
00698 
00699   QSettings settings;
00700   QStringList favDirs = settings.value( "/browser/favourites", QVariant() ).toStringList();
00701 
00702   foreach ( QString favDir, favDirs )
00703   {
00704     QgsDataItem *item = new QgsDirectoryItem( this, favDir, favDir );
00705     if ( item )
00706     {
00707       children.append( item );
00708     }
00709   }
00710 
00711   return children;
00712 }
00713 
00714 void QgsFavouritesItem::addDirectory( QString favDir )
00715 {
00716   QSettings settings;
00717   QStringList favDirs = settings.value( "/browser/favourites" ).toStringList();
00718   favDirs.append( favDir );
00719   settings.setValue( "/browser/favourites", favDirs );
00720 
00721   addChildItem( new QgsDirectoryItem( this, favDir, favDir ), true );
00722 }
00723 
00724 void QgsFavouritesItem::removeDirectory( QgsDirectoryItem *item )
00725 {
00726   if ( !item )
00727     return;
00728 
00729   QSettings settings;
00730   QStringList favDirs = settings.value( "/browser/favourites" ).toStringList();
00731   favDirs.removeAll( item->path() );
00732   settings.setValue( "/browser/favourites", favDirs );
00733 
00734   deleteChildItem( item );
00735 }
00736 
00737 //-----------------------------------------------------------------------
00738 QStringList QgsZipItem::mProviderNames = QStringList();
00739 QVector<dataItem_t *> QgsZipItem::mDataItemPtr = QVector<dataItem_t*>();
00740 
00741 
00742 QgsZipItem::QgsZipItem( QgsDataItem* parent, QString name, QString path )
00743     : QgsDataCollectionItem( parent, name, path )
00744 {
00745   mType = Collection; //Zip??
00746   mIcon = iconZip();
00747   mVsiPrefix = vsiPrefix( path );
00748 
00749   if ( mProviderNames.size() == 0 )
00750   {
00751     // QStringList keys = QgsProviderRegistry::instance()->providerList();
00752     // only use GDAL and OGR providers as we use the VSIFILE mechanism
00753     QStringList keys;
00754     // keys << "ogr" << "gdal";
00755     keys << "gdal" << "ogr";
00756 
00757     QStringList::const_iterator i;
00758     for ( i = keys.begin(); i != keys.end(); ++i )
00759     {
00760       QString k( *i );
00761       QgsDebugMsg( "provider " + k );
00762       // some providers hangs with empty uri (Postgis) etc...
00763       // -> using libraries directly
00764       QLibrary *library = QgsProviderRegistry::instance()->providerLibrary( k );
00765       if ( library )
00766       {
00767         dataCapabilities_t * dataCapabilities = ( dataCapabilities_t * ) cast_to_fptr( library->resolve( "dataCapabilities" ) );
00768         if ( !dataCapabilities )
00769         {
00770           QgsDebugMsg( library->fileName() + " does not have dataCapabilities" );
00771           continue;
00772         }
00773         if ( dataCapabilities() == QgsDataProvider::NoDataCapabilities )
00774         {
00775           QgsDebugMsg( library->fileName() + " has NoDataCapabilities" );
00776           continue;
00777         }
00778         QgsDebugMsg( QString( "%1 dataCapabilities : %2" ).arg( library->fileName() ).arg( dataCapabilities() ) );
00779 
00780         dataItem_t * dataItem = ( dataItem_t * ) cast_to_fptr( library->resolve( "dataItem" ) );
00781         if ( ! dataItem )
00782         {
00783           QgsDebugMsg( library->fileName() + " does not have dataItem" );
00784           continue;
00785         }
00786 
00787         // mLibraries.append( library );
00788         mDataItemPtr.append( dataItem );
00789         mProviderNames.append( k );
00790       }
00791       else
00792       {
00793         //QgsDebugMsg ( "Cannot get provider " + k );
00794       }
00795     }
00796   }
00797 }
00798 
00799 QgsZipItem::~QgsZipItem()
00800 {
00801 }
00802 
00803 // internal function to scan a vsidir (zip or tar file) recursively
00804 // GDAL trunk has this since r24423 (05/16/12) - VSIReadDirRecursive()
00805 // use a copy of the function internally for now,
00806 // but use char ** and CSLAddString, because CPLStringList was added in gdal-1.9
00807 char **VSIReadDirRecursive1( const char *pszPath )
00808 {
00809   // CPLStringList oFiles = NULL;
00810   char **papszOFiles = NULL;
00811   char **papszFiles1 = NULL;
00812   char **papszFiles2 = NULL;
00813   VSIStatBufL psStatBuf;
00814   CPLString osTemp1, osTemp2;
00815   int i, j;
00816   int nCount1, nCount2;
00817 
00818   // get listing
00819   papszFiles1 = VSIReadDir( pszPath );
00820   if ( ! papszFiles1 )
00821     return NULL;
00822 
00823   // get files and directories inside listing
00824   nCount1 = CSLCount( papszFiles1 );
00825   for ( i = 0; i < nCount1; i++ )
00826   {
00827     // build complete file name for stat
00828     osTemp1.clear();
00829     osTemp1.append( pszPath );
00830     osTemp1.append( "/" );
00831     osTemp1.append( papszFiles1[i] );
00832 
00833     // if is file, add it
00834     if ( VSIStatL( osTemp1.c_str(), &psStatBuf ) == 0 &&
00835          VSI_ISREG( psStatBuf.st_mode ) )
00836     {
00837       // oFiles.AddString( papszFiles1[i] );
00838       papszOFiles = CSLAddString( papszOFiles, papszFiles1[i] );
00839     }
00840     else if ( VSIStatL( osTemp1.c_str(), &psStatBuf ) == 0 &&
00841               VSI_ISDIR( psStatBuf.st_mode ) )
00842     {
00843       // add directory entry
00844       osTemp2.clear();
00845       osTemp2.append( papszFiles1[i] );
00846       osTemp2.append( "/" );
00847       // oFiles.AddString( osTemp2.c_str() );
00848       papszOFiles = CSLAddString( papszOFiles, osTemp2.c_str() );
00849 
00850       // recursively add files inside directory
00851       papszFiles2 = VSIReadDirRecursive1( osTemp1.c_str() );
00852       if ( papszFiles2 )
00853       {
00854         nCount2 = CSLCount( papszFiles2 );
00855         for ( j = 0; j < nCount2; j++ )
00856         {
00857           osTemp2.clear();
00858           osTemp2.append( papszFiles1[i] );
00859           osTemp2.append( "/" );
00860           osTemp2.append( papszFiles2[j] );
00861           // oFiles.AddString( osTemp2.c_str() );
00862           papszOFiles = CSLAddString( papszOFiles, osTemp2.c_str() );
00863         }
00864         CSLDestroy( papszFiles2 );
00865       }
00866     }
00867   }
00868   CSLDestroy( papszFiles1 );
00869 
00870   // return oFiles.StealList();
00871   return papszOFiles;
00872 }
00873 
00874 QVector<QgsDataItem*> QgsZipItem::createChildren()
00875 {
00876   QVector<QgsDataItem*> children;
00877   QString tmpPath;
00878   QString childPath;
00879   QSettings settings;
00880   QString scanZipSetting = settings.value( "/qgis/scanZipInBrowser2", "basic" ).toString();
00881 
00882   mZipFileList.clear();
00883 
00884   QgsDebugMsgLevel( QString( "path = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( path() ).arg( name() ).arg( scanZipSetting ).arg( mVsiPrefix ), 2 );
00885 
00886   // if scanZipBrowser == no: skip to the next file
00887   if ( scanZipSetting == "no" )
00888   {
00889     return children;
00890   }
00891 
00892   // first get list of files
00893   getZipFileList();
00894 
00895   // loop over files inside zip
00896   foreach ( QString fileName, mZipFileList )
00897   {
00898     QFileInfo info( fileName );
00899     tmpPath = mVsiPrefix + path() + "/" + fileName;
00900     QgsDebugMsgLevel( "tmpPath = " + tmpPath, 3 );
00901 
00902     // foreach( dataItem_t *dataItem, mDataItemPtr )
00903     for ( int i = 0; i < mProviderNames.size(); i++ )
00904     {
00905       // ugly hack to remove .dbf file if there is a .shp file
00906       if ( mProviderNames[i] == "ogr" )
00907       {
00908         if ( info.suffix().toLower() == "dbf" )
00909         {
00910           if ( mZipFileList.indexOf( fileName.left( fileName.count() - 4 ) + ".shp" ) != -1 )
00911             continue;
00912         }
00913         if ( info.completeSuffix().toLower() == "shp.xml" )
00914         {
00915           continue;
00916         }
00917       }
00918 
00919       // try to get data item from provider
00920       dataItem_t *dataItem = mDataItemPtr[i];
00921       if ( dataItem )
00922       {
00923         QgsDebugMsgLevel( QString( "trying to load item %1 with %2" ).arg( tmpPath ).arg( mProviderNames[i] ), 3 );
00924         QgsDataItem * item = dataItem( tmpPath, this );
00925         if ( item )
00926         {
00927           QgsDebugMsgLevel( "loaded item", 3 );
00928           childPath = tmpPath;
00929           children.append( item );
00930           break;
00931         }
00932         else
00933         {
00934           QgsDebugMsgLevel( "not loaded item", 3 );
00935         }
00936       }
00937     }
00938 
00939   }
00940 
00941   if ( children.size() == 1 )
00942   {
00943     // save the name of the only child so we can get a normal data item from it
00944     mPath = childPath;
00945   }
00946 
00947   return children;
00948 }
00949 
00950 QString QgsZipItem::vsiPrefix( QString path )
00951 {
00952   if ( path.startsWith( "/vsizip/", Qt::CaseInsensitive ) ||
00953        path.endsWith( ".zip", Qt::CaseInsensitive ) )
00954     return "/vsizip/";
00955   else if ( path.startsWith( "/vsitar/", Qt::CaseInsensitive ) ||
00956             path.endsWith( ".tar", Qt::CaseInsensitive ) ||
00957             path.endsWith( ".tar.gz", Qt::CaseInsensitive ) ||
00958             path.endsWith( ".tgz", Qt::CaseInsensitive ) )
00959     return "/vsitar/";
00960   else if ( path.startsWith( "/vsigzip/", Qt::CaseInsensitive ) ||
00961             path.endsWith( ".gz", Qt::CaseInsensitive ) )
00962     return "/vsigzip/";
00963   else
00964     return "";
00965 }
00966 
00967 QgsDataItem* QgsZipItem::itemFromPath( QgsDataItem* parent, QString path, QString name )
00968 {
00969   QSettings settings;
00970   QString scanZipSetting = settings.value( "/qgis/scanZipInBrowser2", "basic" ).toString();
00971   QString vsiPath = path;
00972   int zipFileCount = 0;
00973   QStringList zipFileList;
00974   QFileInfo fileInfo( path );
00975   QString vsiPrefix = QgsZipItem::vsiPrefix( path );
00976   QgsZipItem * zipItem = 0;
00977   bool populated = false;
00978 
00979   QgsDebugMsgLevel( QString( "path = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( path ).arg( name ).arg( scanZipSetting ).arg( vsiPrefix ), 3 );
00980 
00981   // don't scan if scanZipBrowser == no
00982   if ( scanZipSetting == "no" )
00983     return 0;
00984 
00985   // don't scan if this file is not a /vsizip/ or /vsitar/ item
00986   if (( vsiPrefix != "/vsizip/" && vsiPrefix != "/vsitar/" ) )
00987     return 0;
00988 
00989   zipItem = new QgsZipItem( parent, name, path );
00990 
00991   if ( zipItem )
00992   {
00993     // force populate zipItem if it has less than 10 items and is not a .tgz or .tar.gz file (slow loading)
00994     // for other items populating will be delayed until item is opened
00995     // this might be polluting the tree with empty items but is necessary for performance reasons
00996     // could also accept all files smaller than a certain size and add options for file count and/or size
00997 
00998     // first get list of files inside .zip or .tar files
00999     if ( path.endsWith( ".zip", Qt::CaseInsensitive ) ||
01000          path.endsWith( ".tar", Qt::CaseInsensitive ) )
01001     {
01002       zipFileList = zipItem->getZipFileList();
01003     }
01004     // force populate if less than 10 items
01005     if ( zipFileList.count() > 0 && zipFileList.count() <= 10 )
01006     {
01007       zipItem->populate();
01008       populated = true; // there is no QgsDataItem::isPopulated() function
01009       QgsDebugMsgLevel( QString( "Got zipItem with %1 children, path=%2, name=%3" ).arg( zipItem->rowCount() ).arg( zipItem->path() ).arg( zipItem->name() ), 3 );
01010     }
01011     else
01012     {
01013       QgsDebugMsgLevel( QString( "Delaying populating zipItem with path=%1, name=%2" ).arg( zipItem->path() ).arg( zipItem->name() ), 3 );
01014     }
01015   }
01016 
01017   // only display if has children or if is not populated
01018   if ( zipItem && ( !populated || zipItem->rowCount() > 1 ) )
01019   {
01020     QgsDebugMsgLevel( "returning zipItem", 3 );
01021     return zipItem;
01022   }
01023   // if 1 or 0 child found, create a single data item using the normal path or the full path given by QgsZipItem
01024   else
01025   {
01026     if ( zipItem )
01027     {
01028       vsiPath = zipItem->path();
01029       zipFileCount = zipFileList.count();
01030       delete zipItem;
01031     }
01032 
01033     QgsDebugMsgLevel( QString( "will try to create a normal dataItem from path= %2 or %3" ).arg( path ).arg( vsiPath ), 3 );
01034 
01035     // try to open using registered providers (gdal and ogr)
01036     for ( int i = 0; i < mProviderNames.size(); i++ )
01037     {
01038       dataItem_t *dataItem = mDataItemPtr[i];
01039       if ( dataItem )
01040       {
01041         QgsDataItem *item = 0;
01042         // try first with normal path (Passthru)
01043         // this is to simplify .qml handling, and without this some tests will fail
01044         // (e.g. testZipItemVectorTransparency(), second test)
01045         if (( mProviderNames[i] == "ogr" ) ||
01046             ( mProviderNames[i] == "gdal" && zipFileCount == 1 ) )
01047           item = dataItem( path, parent );
01048         // try with /vsizip/
01049         if ( ! item )
01050           item = dataItem( vsiPath, parent );
01051         if ( item )
01052           return item;
01053       }
01054     }
01055   }
01056 
01057   return 0;
01058 }
01059 
01060 const QStringList & QgsZipItem::getZipFileList()
01061 {
01062   if ( ! mZipFileList.isEmpty() )
01063     return mZipFileList;
01064 
01065   QString tmpPath;
01066   QSettings settings;
01067   QString scanZipSetting = settings.value( "/qgis/scanZipInBrowser2", "basic" ).toString();
01068 
01069   QgsDebugMsgLevel( QString( "path = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( path() ).arg( name() ).arg( scanZipSetting ).arg( mVsiPrefix ), 3 );
01070 
01071   // if scanZipBrowser == no: skip to the next file
01072   if ( scanZipSetting == "no" )
01073   {
01074     return mZipFileList;
01075   }
01076 
01077   // get list of files inside zip file
01078   QgsDebugMsgLevel( QString( "Open file %1 with gdal vsi" ).arg( mVsiPrefix + path() ), 3 );
01079   char **papszSiblingFiles = VSIReadDirRecursive1( QString( mVsiPrefix + path() ).toLocal8Bit().constData() );
01080   if ( papszSiblingFiles )
01081   {
01082     for ( int i = 0; i < CSLCount( papszSiblingFiles ); i++ )
01083     {
01084       tmpPath = papszSiblingFiles[i];
01085       QgsDebugMsgLevel( QString( "Read file %1" ).arg( tmpPath ), 3 );
01086       // skip directories (files ending with /)
01087       if ( tmpPath.right( 1 ) != "/" )
01088         mZipFileList << tmpPath;
01089     }
01090     CSLDestroy( papszSiblingFiles );
01091   }
01092   else
01093   {
01094     QgsDebugMsg( QString( "Error reading %1" ).arg( path() ) );
01095   }
01096 
01097   return mZipFileList;
01098 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines