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