QGIS API Documentation 3.32.0-Lima (311a8cb8a6)
•All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 "qgssettings.h"
23#include "qgsgdalutils.h"
24
25#include <QFileInfo>
26
27#include <cpl_vsi.h>
28#include <cpl_string.h>
29#include <mutex>
30
32{
33 return QgsApplication::getThemeIcon( QStringLiteral( "/mIconZip.svg" ) );
34}
35
36
37//-----------------------------------------------------------------------
38QStringList QgsZipItem::sProviderNames = QStringList();
39
40
41QgsZipItem::QgsZipItem( QgsDataItem *parent, const QString &name, const QString &path )
42 : QgsDataCollectionItem( parent, name, path )
43{
45 init();
46}
47
48QgsZipItem::QgsZipItem( QgsDataItem *parent, const QString &name,
49 const QString &filePath, const QString &path,
50 const QString &providerKey )
51 : QgsDataCollectionItem( parent, name, path, providerKey )
52 , mFilePath( filePath )
53{
54 init();
55}
56
57void QgsZipItem::init()
58{
60 mIconName = QStringLiteral( "/mIconZip.svg" );
62
64
65 static std::once_flag initialized;
66 std::call_once( initialized, [ = ]
67 {
68 sProviderNames << QStringLiteral( "files" );
69 } );
70}
71
73{
74 return true;
75}
76
78{
80 u.layerType = QStringLiteral( "collection" );
81 u.uri = path();
82 u.filePath = path();
83 return { u };
84}
85
86QString QgsZipItem::vsiPrefix( const QString &uri )
87{
89}
90
91QVector<QgsDataItem *> QgsZipItem::createChildren()
92{
93 QVector<QgsDataItem *> children;
94 QString tmpPath;
95 const QgsSettings settings;
96 const QString scanZipSetting = settings.value( QStringLiteral( "qgis/scanZipInBrowser2" ), "basic" ).toString();
97
98 mZipFileList.clear();
99
100 QgsDebugMsgLevel( QStringLiteral( "mFilePath = %1 path = %2 name= %3 scanZipSetting= %4 vsiPrefix= %5" ).arg( mFilePath, path(), name(), scanZipSetting, mVsiPrefix ), 3 );
101
102 // if scanZipBrowser == no: skip to the next file
103 if ( scanZipSetting == QLatin1String( "no" ) )
104 {
105 return children;
106 }
107
108 // first get list of files
110
111 const QList<QgsDataItemProvider *> providers = QgsApplication::dataItemProviderRegistry()->providers();
112
113 // loop over files inside zip
114 const auto constMZipFileList = mZipFileList;
115 for ( const QString &fileName : constMZipFileList )
116 {
117 const QFileInfo info( fileName );
118 tmpPath = mVsiPrefix + mFilePath + '/' + fileName;
119 QgsDebugMsgLevel( "tmpPath = " + tmpPath, 3 );
120
121 for ( QgsDataItemProvider *provider : providers )
122 {
123 if ( !sProviderNames.contains( provider->name() ) )
124 continue;
125
126 // ugly hack to remove .dbf file if there is a .shp file
127 if ( provider->name() == QLatin1String( "OGR" ) )
128 {
129 if ( info.suffix().compare( QLatin1String( "dbf" ), Qt::CaseInsensitive ) == 0 )
130 {
131 if ( mZipFileList.indexOf( fileName.left( fileName.count() - 4 ) + ".shp" ) != -1 )
132 continue;
133 }
134 if ( info.completeSuffix().compare( QLatin1String( "shp.xml" ), Qt::CaseInsensitive ) == 0 )
135 {
136 continue;
137 }
138 }
139
140 QgsDebugMsgLevel( QStringLiteral( "trying to load item %1 with %2" ).arg( tmpPath, provider->name() ), 3 );
141 QgsDataItem *item = provider->createDataItem( tmpPath, this );
142 if ( item )
143 {
144 // the item comes with zipped file name, set the name to relative path within zip file
145 item->setName( fileName );
146 children.append( item );
147 }
148 else
149 {
150 QgsDebugMsgLevel( QStringLiteral( "not loaded item" ), 3 );
151 }
152 }
153 }
154
155 return children;
156}
157
158QgsDataItem *QgsZipItem::itemFromPath( QgsDataItem *parent, const QString &path, const QString &name )
159{
160 return itemFromPath( parent, path, name, path );
161}
162
163QgsDataItem *QgsZipItem::itemFromPath( QgsDataItem *parent, const QString &filePath, const QString &name, const QString &path )
164{
165 const QgsSettings settings;
166 const QString scanZipSetting = settings.value( QStringLiteral( "qgis/scanZipInBrowser2" ), "basic" ).toString();
167 QStringList zipFileList;
168 const QString vsiPrefix = QgsGdalUtils::vsiPrefixForPath( filePath );
169 QgsZipItem *zipItem = nullptr;
170 bool populated = false;
171
172 QgsDebugMsgLevel( QStringLiteral( "path = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( path, name, scanZipSetting, vsiPrefix ), 3 );
173
174 // don't scan if scanZipBrowser == no
175 if ( scanZipSetting == QLatin1String( "no" ) )
176 return nullptr;
177
178 // don't scan if this file is not a vsi container archive item
180 return nullptr;
181
182 zipItem = new QgsZipItem( parent, name, filePath, path );
183
184 if ( zipItem )
185 {
186 // force populate zipItem if it has less than 10 items and is not a .tgz or .tar.gz file (slow loading)
187 // for other items populating will be delayed until item is opened
188 // this might be polluting the tree with empty items but is necessary for performance reasons
189 // could also accept all files smaller than a certain size and add options for file count and/or size
190
191 // first get list of files inside .zip or .tar files
192 if ( path.endsWith( QLatin1String( ".zip" ), Qt::CaseInsensitive ) ||
193 path.endsWith( QLatin1String( ".tar" ), Qt::CaseInsensitive ) )
194 {
195 zipFileList = zipItem->getZipFileList();
196 }
197 // force populate if less than 10 items
198 if ( !zipFileList.isEmpty() && zipFileList.count() <= 10 )
199 {
200 zipItem->populate( zipItem->createChildren() );
201 populated = true; // there is no QgsDataItem::isPopulated() function
202 QgsDebugMsgLevel( QStringLiteral( "Got zipItem with %1 children, path=%2, name=%3" ).arg( zipItem->rowCount() ).arg( zipItem->path(), zipItem->name() ), 3 );
203 }
204 else
205 {
206 QgsDebugMsgLevel( QStringLiteral( "Delaying populating zipItem with path=%1, name=%2" ).arg( zipItem->path(), zipItem->name() ), 3 );
207 }
208 }
209
210 // only display if has children or if is not populated
211 if ( zipItem && ( !populated || zipItem->rowCount() > 0 ) )
212 {
213 QgsDebugMsgLevel( QStringLiteral( "returning zipItem" ), 3 );
214 return zipItem;
215 }
216
217 return nullptr;
218}
219
221{
222 if ( ! mZipFileList.isEmpty() )
223 return mZipFileList;
224
225 QString tmpPath;
226 const QgsSettings settings;
227 const QString scanZipSetting = settings.value( QStringLiteral( "qgis/scanZipInBrowser2" ), "basic" ).toString();
228
229 QgsDebugMsgLevel( QStringLiteral( "mFilePath = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( mFilePath, name(), scanZipSetting, mVsiPrefix ), 3 );
230
231 // if scanZipBrowser == no: skip to the next file
232 if ( scanZipSetting == QLatin1String( "no" ) )
233 {
234 return mZipFileList;
235 }
236
237 // get list of files inside zip file
238 QgsDebugMsgLevel( QStringLiteral( "Open file %1 with gdal vsi" ).arg( mVsiPrefix + mFilePath ), 3 );
239 char **papszSiblingFiles = VSIReadDirRecursive( QString( mVsiPrefix + mFilePath ).toUtf8().constData() );
240 if ( papszSiblingFiles )
241 {
242 for ( int i = 0; papszSiblingFiles[i]; i++ )
243 {
244 tmpPath = papszSiblingFiles[i];
245 QgsDebugMsgLevel( QStringLiteral( "Read file %1" ).arg( tmpPath ), 3 );
246 // skip directories (files ending with /)
247 if ( tmpPath.right( 1 ) != QLatin1String( "/" ) )
248 mZipFileList << tmpPath;
249 }
250 CSLDestroy( papszSiblingFiles );
251 }
252 else
253 {
254 QgsDebugError( QStringLiteral( "Error reading %1" ).arg( mFilePath ) );
255 }
256
257 return mZipFileList;
258}
259
260
@ 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
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).
QgsDataItem * parent() const
Gets item parent.
Definition: qgsdataitem.h:330
virtual Qgis::BrowserItemCapabilities capabilities2() const
Returns the capabilities for the data item.
Definition: qgsdataitem.h:303
virtual void populate(const QVector< QgsDataItem * > &children)
static QString vsiPrefixForPath(const QString &path)
Returns a the vsi prefix which corresponds to a file path, or an empty string if the path is not asso...
static bool isVsiArchivePrefix(const QString &prefix)
Returns true if prefix is a supported archive style container prefix (e.g.
QList< QgsMimeDataUtils::Uri > UriList
This class is a composition of two QSettings instances:
Definition: qgssettings.h:63
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
A zip file: contains layers, using GDAL/OGR VSIFILE mechanism.
Definition: qgszipitem.h:30
static QIcon iconZip()
Definition: qgszipitem.cpp:31
static Q_DECL_DEPRECATED QString vsiPrefix(const QString &uri)
Definition: qgszipitem.cpp:86
QgsZipItem(QgsDataItem *parent, const QString &name, const QString &path)
Constructor.
Definition: qgszipitem.cpp:41
QStringList mZipFileList
Definition: qgszipitem.h:36
QStringList getZipFileList()
Definition: qgszipitem.cpp:220
bool hasDragEnabled() const override
Returns true if the item may be dragged.
Definition: qgszipitem.cpp:72
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:77
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:158
QString mVsiPrefix
Definition: qgszipitem.h:35
QVector< QgsDataItem * > createChildren() override
Create children.
Definition: qgszipitem.cpp:91
static QStringList sProviderNames
Definition: qgszipitem.h:61
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugError(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.