QGIS API Documentation  3.20.0-Odense (decaadbb31)
qgszipitem.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgszipitem.cpp
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 "qgszipitem.h"
19 #include "qgsapplication.h"
20 #include "qgsdataitemprovider.h"
22 #include <QFileInfo>
23 
24 #include <cpl_vsi.h>
25 #include <cpl_string.h>
26 #include <mutex>
27 
29 {
30  return QgsApplication::getThemeIcon( QStringLiteral( "/mIconZip.svg" ) );
31 }
32 
33 
34 //-----------------------------------------------------------------------
35 QStringList QgsZipItem::sProviderNames = QStringList();
36 
37 
38 QgsZipItem::QgsZipItem( QgsDataItem *parent, const QString &name, const QString &path )
39  : QgsDataCollectionItem( parent, name, path )
40 {
41  mFilePath = path;
42  init();
43 }
44 
45 QgsZipItem::QgsZipItem( QgsDataItem *parent, const QString &name,
46  const QString &filePath, const QString &path,
47  const QString &providerKey )
48  : QgsDataCollectionItem( parent, name, path, providerKey )
49  , mFilePath( filePath )
50 {
51  init();
52 }
53 
54 void QgsZipItem::init()
55 {
57  mIconName = QStringLiteral( "/mIconZip.svg" );
59 
60  static std::once_flag initialized;
61  std::call_once( initialized, [ = ]
62  {
63  sProviderNames << QStringLiteral( "OGR" ) << QStringLiteral( "GDAL" );
64  } );
65 }
66 
67 QVector<QgsDataItem *> QgsZipItem::createChildren()
68 {
69  QVector<QgsDataItem *> children;
70  QString tmpPath;
71  QgsSettings settings;
72  QString scanZipSetting = settings.value( QStringLiteral( "qgis/scanZipInBrowser2" ), "basic" ).toString();
73 
74  mZipFileList.clear();
75 
76  QgsDebugMsgLevel( QStringLiteral( "mFilePath = %1 path = %2 name= %3 scanZipSetting= %4 vsiPrefix= %5" ).arg( mFilePath, path(), name(), scanZipSetting, mVsiPrefix ), 3 );
77 
78  // if scanZipBrowser == no: skip to the next file
79  if ( scanZipSetting == QLatin1String( "no" ) )
80  {
81  return children;
82  }
83 
84  // first get list of files
86 
87  const QList<QgsDataItemProvider *> providers = QgsApplication::dataItemProviderRegistry()->providers();
88 
89  // loop over files inside zip
90  const auto constMZipFileList = mZipFileList;
91  for ( const QString &fileName : constMZipFileList )
92  {
93  QFileInfo info( fileName );
94  tmpPath = mVsiPrefix + mFilePath + '/' + fileName;
95  QgsDebugMsgLevel( "tmpPath = " + tmpPath, 3 );
96 
97  for ( QgsDataItemProvider *provider : providers )
98  {
99  if ( !sProviderNames.contains( provider->name() ) )
100  continue;
101 
102  // ugly hack to remove .dbf file if there is a .shp file
103  if ( provider->name() == QLatin1String( "OGR" ) )
104  {
105  if ( info.suffix().compare( QLatin1String( "dbf" ), Qt::CaseInsensitive ) == 0 )
106  {
107  if ( mZipFileList.indexOf( fileName.left( fileName.count() - 4 ) + ".shp" ) != -1 )
108  continue;
109  }
110  if ( info.completeSuffix().compare( QLatin1String( "shp.xml" ), Qt::CaseInsensitive ) == 0 )
111  {
112  continue;
113  }
114  }
115 
116  QgsDebugMsgLevel( QStringLiteral( "trying to load item %1 with %2" ).arg( tmpPath, provider->name() ), 3 );
117  QgsDataItem *item = provider->createDataItem( tmpPath, this );
118  if ( item )
119  {
120  // the item comes with zipped file name, set the name to relative path within zip file
121  item->setName( fileName );
122  children.append( item );
123  }
124  else
125  {
126  QgsDebugMsgLevel( QStringLiteral( "not loaded item" ), 3 );
127  }
128  }
129  }
130 
131  return children;
132 }
133 
134 QgsDataItem *QgsZipItem::itemFromPath( QgsDataItem *parent, const QString &path, const QString &name )
135 {
136  return itemFromPath( parent, path, name, path );
137 }
138 
139 QgsDataItem *QgsZipItem::itemFromPath( QgsDataItem *parent, const QString &filePath, const QString &name, const QString &path )
140 {
141  QgsSettings settings;
142  QString scanZipSetting = settings.value( QStringLiteral( "qgis/scanZipInBrowser2" ), "basic" ).toString();
143  QStringList zipFileList;
144  QString vsiPrefix = QgsZipItem::vsiPrefix( filePath );
145  QgsZipItem *zipItem = nullptr;
146  bool populated = false;
147 
148  QgsDebugMsgLevel( QStringLiteral( "path = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( path, name, scanZipSetting, vsiPrefix ), 3 );
149 
150  // don't scan if scanZipBrowser == no
151  if ( scanZipSetting == QLatin1String( "no" ) )
152  return nullptr;
153 
154  // don't scan if this file is not a /vsizip/ or /vsitar/ item
155  if ( ( vsiPrefix != QLatin1String( "/vsizip/" ) && vsiPrefix != QLatin1String( "/vsitar/" ) ) )
156  return nullptr;
157 
158  zipItem = new QgsZipItem( parent, name, filePath, path );
159 
160  if ( zipItem )
161  {
162  // force populate zipItem if it has less than 10 items and is not a .tgz or .tar.gz file (slow loading)
163  // for other items populating will be delayed until item is opened
164  // this might be polluting the tree with empty items but is necessary for performance reasons
165  // could also accept all files smaller than a certain size and add options for file count and/or size
166 
167  // first get list of files inside .zip or .tar files
168  if ( path.endsWith( QLatin1String( ".zip" ), Qt::CaseInsensitive ) ||
169  path.endsWith( QLatin1String( ".tar" ), Qt::CaseInsensitive ) )
170  {
171  zipFileList = zipItem->getZipFileList();
172  }
173  // force populate if less than 10 items
174  if ( !zipFileList.isEmpty() && zipFileList.count() <= 10 )
175  {
176  zipItem->populate( zipItem->createChildren() );
177  populated = true; // there is no QgsDataItem::isPopulated() function
178  QgsDebugMsgLevel( QStringLiteral( "Got zipItem with %1 children, path=%2, name=%3" ).arg( zipItem->rowCount() ).arg( zipItem->path(), zipItem->name() ), 3 );
179  }
180  else
181  {
182  QgsDebugMsgLevel( QStringLiteral( "Delaying populating zipItem with path=%1, name=%2" ).arg( zipItem->path(), zipItem->name() ), 3 );
183  }
184  }
185 
186  // only display if has children or if is not populated
187  if ( zipItem && ( !populated || zipItem->rowCount() > 0 ) )
188  {
189  QgsDebugMsgLevel( QStringLiteral( "returning zipItem" ), 3 );
190  return zipItem;
191  }
192 
193  return nullptr;
194 }
195 
197 {
198  if ( ! mZipFileList.isEmpty() )
199  return mZipFileList;
200 
201  QString tmpPath;
202  QgsSettings settings;
203  QString scanZipSetting = settings.value( QStringLiteral( "qgis/scanZipInBrowser2" ), "basic" ).toString();
204 
205  QgsDebugMsgLevel( QStringLiteral( "mFilePath = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( mFilePath, name(), scanZipSetting, mVsiPrefix ), 3 );
206 
207  // if scanZipBrowser == no: skip to the next file
208  if ( scanZipSetting == QLatin1String( "no" ) )
209  {
210  return mZipFileList;
211  }
212 
213  // get list of files inside zip file
214  QgsDebugMsgLevel( QStringLiteral( "Open file %1 with gdal vsi" ).arg( mVsiPrefix + mFilePath ), 3 );
215  char **papszSiblingFiles = VSIReadDirRecursive( QString( mVsiPrefix + mFilePath ).toLocal8Bit().constData() );
216  if ( papszSiblingFiles )
217  {
218  for ( int i = 0; papszSiblingFiles[i]; i++ )
219  {
220  tmpPath = papszSiblingFiles[i];
221  QgsDebugMsgLevel( QStringLiteral( "Read file %1" ).arg( tmpPath ), 3 );
222  // skip directories (files ending with /)
223  if ( tmpPath.right( 1 ) != QLatin1String( "/" ) )
224  mZipFileList << tmpPath;
225  }
226  CSLDestroy( papszSiblingFiles );
227  }
228  else
229  {
230  QgsDebugMsg( QStringLiteral( "Error reading %1" ).arg( mFilePath ) );
231  }
232 
233  return mZipFileList;
234 }
235 
236 
@ Collection
A collection of items.
static QgsDataItemProviderRegistry * dataItemProviderRegistry()
Returns the application's data item provider registry, which keeps a list of data item providers that...
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
A Collection: logical collection of layers or subcollections, e.g.
QList< QgsDataItemProvider * > providers() const
Returns the list of available providers.
This is the interface for those who want to add custom data items to the browser tree.
Base class for all items in the model.
Definition: qgsdataitem.h:46
Qgis::BrowserItemType mType
Definition: qgsdataitem.h:436
QVector< QgsDataItem * > children() const
Definition: qgsdataitem.h:329
QgsDataItem * parent() const
Gets item parent.
Definition: qgsdataitem.h:322
QString mIconName
Definition: qgsdataitem.h:449
QString name() const
Returns the name of the item (the displayed text for the item).
Definition: qgsdataitem.h:337
QString path() const
Definition: qgsdataitem.h:346
void setName(const QString &name)
Sets the name of the item (the displayed text for the item).
virtual void populate(const QVector< QgsDataItem * > &children)
A zip file: contains layers, using GDAL/OGR VSIFILE mechanism.
Definition: qgszipitem.h:30
static QIcon iconZip()
Definition: qgszipitem.cpp:28
QgsZipItem(QgsDataItem *parent, const QString &name, const QString &path)
Constructor.
Definition: qgszipitem.cpp:38
QStringList mZipFileList
Definition: qgszipitem.h:36
QStringList getZipFileList()
Definition: qgszipitem.cpp:196
QString mFilePath
Definition: qgszipitem.h:34
static QgsDataItem * itemFromPath(QgsDataItem *parent, const QString &path, const QString &name)
Creates a new data item from the specified path.
Definition: qgszipitem.cpp:134
QString mVsiPrefix
Definition: qgszipitem.h:35
static QString vsiPrefix(const QString &uri)
Definition: qgszipitem.h:60
QVector< QgsDataItem * > createChildren() override
Create children.
Definition: qgszipitem.cpp:67
static QStringList sProviderNames
Definition: qgszipitem.h:58
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38