QGIS API Documentation  3.23.0-Master (dd0cd13a00)
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 
@ ItemRepresentsFile
Item's path() directly represents a file on disk (since QGIS 3.22)
@ 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:444
QVector< QgsDataItem * > children() const
Definition: qgsdataitem.h:337
QgsDataItem * parent() const
Gets item parent.
Definition: qgsdataitem.h:330
QString mIconName
Definition: qgsdataitem.h:457
QString name() const
Returns the name of the item (the displayed text for the item).
Definition: qgsdataitem.h:345
QString path() const
Definition: qgsdataitem.h:354
virtual void setCapabilities(Qgis::BrowserItemCapabilities capabilities)
Sets the capabilities for the data item.
Definition: qgsdataitem.h:310
void setName(const QString &name)
Sets the name of the item (the displayed text for the item).
virtual Qgis::BrowserItemCapabilities capabilities2() const
Returns the capabilities for the data item.
Definition: qgsdataitem.h:303
virtual void populate(const QVector< QgsDataItem * > &children)
QList< QgsMimeDataUtils::Uri > UriList
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:212
bool hasDragEnabled() const override
Returns true if the item may be dragged.
Definition: qgszipitem.cpp:69
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
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:150
QString mVsiPrefix
Definition: qgszipitem.h:35
static QString vsiPrefix(const QString &uri)
Definition: qgszipitem.h:63
QVector< QgsDataItem * > createChildren() override
Create children.
Definition: qgszipitem.cpp:83
static QStringList sProviderNames
Definition: qgszipitem.h:61
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QString filePath
Path to file, if uri is associated with a file.
QString uri
Identifier of the data source recognized by its providerKey.
QString layerType
Type of URI.