QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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//-----------------------------------------------------------------------
35QStringList QgsZipItem::sProviderNames = QStringList();
36
37
38QgsZipItem::QgsZipItem( QgsDataItem *parent, const QString &name, const QString &path )
39 : QgsDataCollectionItem( parent, name, path )
40{
42 init();
43}
44
45QgsZipItem::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
54void 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
83QVector<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
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
150QgsDataItem *QgsZipItem::itemFromPath( QgsDataItem *parent, const QString &path, const QString &name )
151{
152 return itemFromPath( parent, path, name, path );
153}
154
155QgsDataItem *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
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)
QList< QgsMimeDataUtils::Uri > UriList
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
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: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.