QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 
61 
62  static std::once_flag initialized;
63  std::call_once( initialized, [ = ]
64  {
65  sProviderNames << QStringLiteral( "files" );
66  } );
67 }
68 
70 {
71  return true;
72 }
73 
75 {
77  u.layerType = QStringLiteral( "collection" );
78  u.uri = path();
79  u.filePath = path();
80  return { u };
81 }
82 
83 QVector<QgsDataItem *> QgsZipItem::createChildren()
84 {
85  QVector<QgsDataItem *> children;
86  QString tmpPath;
87  const QgsSettings settings;
88  const QString scanZipSetting = settings.value( QStringLiteral( "qgis/scanZipInBrowser2" ), "basic" ).toString();
89 
90  mZipFileList.clear();
91 
92  QgsDebugMsgLevel( QStringLiteral( "mFilePath = %1 path = %2 name= %3 scanZipSetting= %4 vsiPrefix= %5" ).arg( mFilePath, path(), name(), scanZipSetting, mVsiPrefix ), 3 );
93 
94  // if scanZipBrowser == no: skip to the next file
95  if ( scanZipSetting == QLatin1String( "no" ) )
96  {
97  return children;
98  }
99 
100  // first get list of files
101  getZipFileList();
102 
103  const QList<QgsDataItemProvider *> providers = QgsApplication::dataItemProviderRegistry()->providers();
104 
105  // loop over files inside zip
106  const auto constMZipFileList = mZipFileList;
107  for ( const QString &fileName : constMZipFileList )
108  {
109  const QFileInfo info( fileName );
110  tmpPath = mVsiPrefix + mFilePath + '/' + fileName;
111  QgsDebugMsgLevel( "tmpPath = " + tmpPath, 3 );
112 
113  for ( QgsDataItemProvider *provider : providers )
114  {
115  if ( !sProviderNames.contains( provider->name() ) )
116  continue;
117 
118  // ugly hack to remove .dbf file if there is a .shp file
119  if ( provider->name() == QLatin1String( "OGR" ) )
120  {
121  if ( info.suffix().compare( QLatin1String( "dbf" ), Qt::CaseInsensitive ) == 0 )
122  {
123  if ( mZipFileList.indexOf( fileName.left( fileName.count() - 4 ) + ".shp" ) != -1 )
124  continue;
125  }
126  if ( info.completeSuffix().compare( QLatin1String( "shp.xml" ), Qt::CaseInsensitive ) == 0 )
127  {
128  continue;
129  }
130  }
131 
132  QgsDebugMsgLevel( QStringLiteral( "trying to load item %1 with %2" ).arg( tmpPath, provider->name() ), 3 );
133  QgsDataItem *item = provider->createDataItem( tmpPath, this );
134  if ( item )
135  {
136  // the item comes with zipped file name, set the name to relative path within zip file
137  item->setName( fileName );
138  children.append( item );
139  }
140  else
141  {
142  QgsDebugMsgLevel( QStringLiteral( "not loaded item" ), 3 );
143  }
144  }
145  }
146 
147  return children;
148 }
149 
150 QgsDataItem *QgsZipItem::itemFromPath( QgsDataItem *parent, const QString &path, const QString &name )
151 {
152  return itemFromPath( parent, path, name, path );
153 }
154 
155 QgsDataItem *QgsZipItem::itemFromPath( QgsDataItem *parent, const QString &filePath, const QString &name, const QString &path )
156 {
157  const QgsSettings settings;
158  const QString scanZipSetting = settings.value( QStringLiteral( "qgis/scanZipInBrowser2" ), "basic" ).toString();
159  QStringList zipFileList;
160  const QString vsiPrefix = QgsZipItem::vsiPrefix( filePath );
161  QgsZipItem *zipItem = nullptr;
162  bool populated = false;
163 
164  QgsDebugMsgLevel( QStringLiteral( "path = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( path, name, scanZipSetting, vsiPrefix ), 3 );
165 
166  // don't scan if scanZipBrowser == no
167  if ( scanZipSetting == QLatin1String( "no" ) )
168  return nullptr;
169 
170  // don't scan if this file is not a /vsizip/ or /vsitar/ item
171  if ( ( vsiPrefix != QLatin1String( "/vsizip/" ) && vsiPrefix != QLatin1String( "/vsitar/" ) ) )
172  return nullptr;
173 
174  zipItem = new QgsZipItem( parent, name, filePath, path );
175 
176  if ( zipItem )
177  {
178  // force populate zipItem if it has less than 10 items and is not a .tgz or .tar.gz file (slow loading)
179  // for other items populating will be delayed until item is opened
180  // this might be polluting the tree with empty items but is necessary for performance reasons
181  // could also accept all files smaller than a certain size and add options for file count and/or size
182 
183  // first get list of files inside .zip or .tar files
184  if ( path.endsWith( QLatin1String( ".zip" ), Qt::CaseInsensitive ) ||
185  path.endsWith( QLatin1String( ".tar" ), Qt::CaseInsensitive ) )
186  {
187  zipFileList = zipItem->getZipFileList();
188  }
189  // force populate if less than 10 items
190  if ( !zipFileList.isEmpty() && zipFileList.count() <= 10 )
191  {
192  zipItem->populate( zipItem->createChildren() );
193  populated = true; // there is no QgsDataItem::isPopulated() function
194  QgsDebugMsgLevel( QStringLiteral( "Got zipItem with %1 children, path=%2, name=%3" ).arg( zipItem->rowCount() ).arg( zipItem->path(), zipItem->name() ), 3 );
195  }
196  else
197  {
198  QgsDebugMsgLevel( QStringLiteral( "Delaying populating zipItem with path=%1, name=%2" ).arg( zipItem->path(), zipItem->name() ), 3 );
199  }
200  }
201 
202  // only display if has children or if is not populated
203  if ( zipItem && ( !populated || zipItem->rowCount() > 0 ) )
204  {
205  QgsDebugMsgLevel( QStringLiteral( "returning zipItem" ), 3 );
206  return zipItem;
207  }
208 
209  return nullptr;
210 }
211 
213 {
214  if ( ! mZipFileList.isEmpty() )
215  return mZipFileList;
216 
217  QString tmpPath;
218  const QgsSettings settings;
219  const QString scanZipSetting = settings.value( QStringLiteral( "qgis/scanZipInBrowser2" ), "basic" ).toString();
220 
221  QgsDebugMsgLevel( QStringLiteral( "mFilePath = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( mFilePath, name(), scanZipSetting, mVsiPrefix ), 3 );
222 
223  // if scanZipBrowser == no: skip to the next file
224  if ( scanZipSetting == QLatin1String( "no" ) )
225  {
226  return mZipFileList;
227  }
228 
229  // get list of files inside zip file
230  QgsDebugMsgLevel( QStringLiteral( "Open file %1 with gdal vsi" ).arg( mVsiPrefix + mFilePath ), 3 );
231  char **papszSiblingFiles = VSIReadDirRecursive( QString( mVsiPrefix + mFilePath ).toLocal8Bit().constData() );
232  if ( papszSiblingFiles )
233  {
234  for ( int i = 0; papszSiblingFiles[i]; i++ )
235  {
236  tmpPath = papszSiblingFiles[i];
237  QgsDebugMsgLevel( QStringLiteral( "Read file %1" ).arg( tmpPath ), 3 );
238  // skip directories (files ending with /)
239  if ( tmpPath.right( 1 ) != QLatin1String( "/" ) )
240  mZipFileList << tmpPath;
241  }
242  CSLDestroy( papszSiblingFiles );
243  }
244  else
245  {
246  QgsDebugMsg( QStringLiteral( "Error reading %1" ).arg( mFilePath ) );
247  }
248 
249  return mZipFileList;
250 }
251 
252 
Qgis::BrowserItemType::Collection
@ Collection
A collection of items.
QgsZipItem::iconZip
static QIcon iconZip()
Definition: qgszipitem.cpp:28
QgsMimeDataUtils::Uri::uri
QString uri
Identifier of the data source recognized by its providerKey.
Definition: qgsmimedatautils.h:125
QgsDataItem::rowCount
int rowCount()
Definition: qgsdataitem.cpp:393
QgsDataCollectionItem
A Collection: logical collection of layers or subcollections, e.g. GRASS location/mapset,...
Definition: qgsdatacollectionitem.h:28
QgsDataItem::path
QString path() const
Definition: qgsdataitem.h:354
qgsdataitemproviderregistry.h
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:161
QgsZipItem::mZipFileList
QStringList mZipFileList
Definition: qgszipitem.h:49
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsZipItem::mFilePath
QString mFilePath
Definition: qgszipitem.h:47
QgsZipItem
A zip file: contains layers, using GDAL/OGR VSIFILE mechanism.
Definition: qgszipitem.h:29
QgsMimeDataUtils::Uri::layerType
QString layerType
Type of URI.
Definition: qgsmimedatautils.h:112
QgsZipItem::getZipFileList
QStringList getZipFileList()
Definition: qgszipitem.cpp:212
QgsZipItem::itemFromPath
static QgsDataItem * itemFromPath(QgsDataItem *parent, const QString &path, const QString &name)
Creates a new data item from the specified path.
Definition: qgszipitem.cpp:150
QgsDataItem::name
QString name() const
Returns the name of the item (the displayed text for the item).
Definition: qgsdataitem.h:345
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
QgsMimeDataUtils::UriList
QList< QgsMimeDataUtils::Uri > UriList
Definition: qgsmimedatautils.h:164
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsDataItem::mIconName
QString mIconName
Definition: qgsdataitem.h:457
QgsZipItem::createChildren
QVector< QgsDataItem * > createChildren() override
Create children.
Definition: qgszipitem.cpp:83
qgsapplication.h
QgsZipItem::mVsiPrefix
QString mVsiPrefix
Definition: qgszipitem.h:48
QgsDataItem::mType
Qgis::BrowserItemType mType
Definition: qgsdataitem.h:444
Qgis::BrowserItemCapability::ItemRepresentsFile
@ ItemRepresentsFile
Item's path() directly represents a file on disk (since QGIS 3.22)
QgsZipItem::mimeUris
QgsMimeDataUtils::UriList mimeUris() const override
Returns mime URIs for the data item, most data providers will only return a single URI but some data ...
Definition: qgszipitem.cpp:74
QgsDataItem::parent
QgsDataItem * parent() const
Gets item parent.
Definition: qgsdataitem.h:330
QgsDataItemProviderRegistry::providers
QList< QgsDataItemProvider * > providers() const
Returns the list of available providers.
Definition: qgsdataitemproviderregistry.cpp:51
QgsDataItemProvider
This is the interface for those who want to add custom data items to the browser tree.
Definition: qgsdataitemprovider.h:44
QgsApplication::dataItemProviderRegistry
static QgsDataItemProviderRegistry * dataItemProviderRegistry()
Returns the application's data item provider registry, which keeps a list of data item providers that...
Definition: qgsapplication.cpp:2335
QgsMimeDataUtils::Uri::filePath
QString filePath
Path to file, if uri is associated with a file.
Definition: qgsmimedatautils.h:154
QgsMimeDataUtils::Uri
Definition: qgsmimedatautils.h:40
QgsZipItem::vsiPrefix
static QString vsiPrefix(const QString &uri)
Definition: qgszipitem.h:76
qgsdataitemprovider.h
QgsDataItem::setName
void setName(const QString &name)
Sets the name of the item (the displayed text for the item).
Definition: qgsdataitem.cpp:178
QgsZipItem::QgsZipItem
QgsZipItem(QgsDataItem *parent, const QString &name, const QString &path)
Constructor.
Definition: qgszipitem.cpp:38
QgsDataItem::setCapabilities
virtual void setCapabilities(Qgis::BrowserItemCapabilities capabilities)
Sets the capabilities for the data item.
Definition: qgsdataitem.h:310
QgsDataItem::populate
virtual void populate(const QVector< QgsDataItem * > &children)
Definition: qgsdataitem.cpp:264
QgsDataItem::children
QVector< QgsDataItem * > children() const
Definition: qgsdataitem.h:337
QgsDataItem::capabilities2
virtual Qgis::BrowserItemCapabilities capabilities2() const
Returns the capabilities for the data item.
Definition: qgsdataitem.h:303
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Definition: qgsapplication.cpp:693
qgszipitem.h
QgsZipItem::sProviderNames
static QStringList sProviderNames
Definition: qgszipitem.h:74
QgsZipItem::hasDragEnabled
bool hasDragEnabled() const override
Returns true if the item may be dragged.
Definition: qgszipitem.cpp:69
QgsDataItem
Base class for all items in the model.
Definition: qgsdataitem.h:45