Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsdataitem.cpp - Data items
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 <QApplication>
19 : : #include <QtConcurrentMap>
20 : : #include <QtConcurrentRun>
21 : : #include <QDateTime>
22 : : #include <QElapsedTimer>
23 : : #include <QDir>
24 : : #include <QFileInfo>
25 : : #include <QMenu>
26 : : #include <QMouseEvent>
27 : : #include <QTreeWidget>
28 : : #include <QTreeWidgetItem>
29 : : #include <QVector>
30 : : #include <QStyle>
31 : : #include <QTimer>
32 : : #include <mutex>
33 : : #include <QRegularExpression>
34 : :
35 : : #include "qgis.h"
36 : : #include "qgsdataitem.h"
37 : : #include "qgsapplication.h"
38 : : #include "qgsdataitemprovider.h"
39 : : #include "qgsdataitemproviderregistry.h"
40 : : #include "qgsdataprovider.h"
41 : : #include "qgslogger.h"
42 : : #include "qgsproviderregistry.h"
43 : : #include "qgsconfig.h"
44 : : #include "qgssettings.h"
45 : : #include "qgsanimatedicon.h"
46 : : #include "qgsproject.h"
47 : : #include "qgsvectorlayer.h"
48 : : #include "qgsprovidermetadata.h"
49 : :
50 : : // use GDAL VSI mechanism
51 : : #define CPL_SUPRESS_CPLUSPLUS //#spellok
52 : : #include "cpl_vsi.h"
53 : : #include "cpl_string.h"
54 : :
55 : : // shared icons
56 : :
57 : 0 : QIcon QgsLayerItem::iconForWkbType( QgsWkbTypes::Type type )
58 : : {
59 : 0 : QgsWkbTypes::GeometryType geomType = QgsWkbTypes::geometryType( QgsWkbTypes::Type( type ) );
60 : 0 : switch ( geomType )
61 : : {
62 : : case QgsWkbTypes::NullGeometry:
63 : 0 : return iconTable();
64 : : case QgsWkbTypes::PointGeometry:
65 : 0 : return iconPoint();
66 : : case QgsWkbTypes::LineGeometry:
67 : 0 : return iconLine();
68 : : case QgsWkbTypes::PolygonGeometry:
69 : 0 : return iconPolygon();
70 : : default:
71 : 0 : break;
72 : : }
73 : 0 : return iconDefault();
74 : 0 : }
75 : :
76 : 0 : QIcon QgsLayerItem::iconPoint()
77 : : {
78 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "/mIconPointLayer.svg" ) );
79 : 0 : }
80 : :
81 : 0 : QIcon QgsLayerItem::iconLine()
82 : : {
83 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "/mIconLineLayer.svg" ) );
84 : 0 : }
85 : :
86 : 0 : QIcon QgsLayerItem::iconPolygon()
87 : : {
88 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "/mIconPolygonLayer.svg" ) );
89 : 0 : }
90 : :
91 : 0 : QIcon QgsLayerItem::iconTable()
92 : : {
93 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "/mIconTableLayer.svg" ) );
94 : 0 : }
95 : :
96 : 0 : QIcon QgsLayerItem::iconRaster()
97 : : {
98 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "/mIconRaster.svg" ) );
99 : 0 : }
100 : :
101 : 0 : QIcon QgsLayerItem::iconMesh()
102 : : {
103 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "/mIconMeshLayer.svg" ) );
104 : 0 : }
105 : :
106 : 0 : QIcon QgsLayerItem::iconVectorTile()
107 : : {
108 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "/mIconVectorTileLayer.svg" ) );
109 : 0 : }
110 : :
111 : 0 : QIcon QgsLayerItem::iconPointCloud()
112 : : {
113 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "/mIconPointCloudLayer.svg" ) );
114 : 0 : }
115 : :
116 : 0 : QIcon QgsLayerItem::iconDefault()
117 : : {
118 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "/mIconLayer.png" ) );
119 : 0 : }
120 : :
121 : 0 : QIcon QgsDataCollectionItem::iconDataCollection()
122 : : {
123 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "/mIconDbSchema.svg" ) );
124 : 0 : }
125 : :
126 : 0 : QIcon QgsDataCollectionItem::openDirIcon()
127 : : {
128 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "/mIconFolderOpen.svg" ) );
129 : 0 : }
130 : :
131 : 0 : QIcon QgsDataCollectionItem::homeDirIcon()
132 : : {
133 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "mIconFolderHome.svg" ) );
134 : 0 : }
135 : :
136 : 0 : QgsAbstractDatabaseProviderConnection *QgsDataCollectionItem::databaseConnection() const
137 : : {
138 : 0 : const QString dataProviderKey { QgsApplication::dataItemProviderRegistry()->dataProviderKey( providerKey() ) };
139 : 0 : QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( dataProviderKey ) };
140 : :
141 : 0 : if ( ! md )
142 : : {
143 : 0 : return nullptr;
144 : : }
145 : :
146 : 0 : const QString connectionName { name() };
147 : :
148 : : try
149 : : {
150 : : // First try to retrieve the connection by name if this is a stored connection
151 : 0 : if ( md->findConnection( connectionName ) )
152 : : {
153 : 0 : return static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( connectionName ) );
154 : : }
155 : :
156 : : // If that fails, try to create a connection from the path, in case this is a
157 : : // filesystem-based DB (gpkg or spatialite)
158 : : // The name is useless, we need to get the file path from the data item path
159 : 0 : const QString databaseFilePath { path().remove( QRegularExpression( R"re([\aZ]{2,}://)re" ) ) };
160 : :
161 : 0 : if ( QFile::exists( databaseFilePath ) )
162 : : {
163 : 0 : return static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( databaseFilePath, {} ) );
164 : : }
165 : 0 : }
166 : : catch ( QgsProviderConnectionException &ex )
167 : : {
168 : : // This is expected and it is not an error in case the provider does not implement
169 : : // the connections API
170 : 0 : }
171 : 0 : return nullptr;
172 : 0 : }
173 : :
174 : 0 : QIcon QgsDataCollectionItem::iconDir()
175 : : {
176 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "/mIconFolder.svg" ) );
177 : 0 : }
178 : :
179 : :
180 : 0 : QgsFieldsItem::QgsFieldsItem( QgsDataItem *parent,
181 : : const QString &path,
182 : : const QString &connectionUri,
183 : : const QString &providerKey,
184 : : const QString &schema,
185 : : const QString &tableName )
186 : 0 : : QgsDataItem( QgsDataItem::Fields, parent, tr( "Fields" ), path, providerKey )
187 : 0 : , mSchema( schema )
188 : 0 : , mTableName( tableName )
189 : 0 : , mConnectionUri( connectionUri )
190 : 0 : {
191 : 0 : mCapabilities |= ( Fertile | Collapse );
192 : 0 : QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( providerKey ) };
193 : 0 : if ( md )
194 : : {
195 : : try
196 : : {
197 : 0 : std::unique_ptr<QgsAbstractDatabaseProviderConnection> conn { static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( mConnectionUri, {} ) ) };
198 : 0 : mTableProperty = std::make_unique<QgsAbstractDatabaseProviderConnection::TableProperty>( conn->table( schema, tableName ) );
199 : 0 : }
200 : : catch ( QgsProviderConnectionException &ex )
201 : : {
202 : 0 : QgsDebugMsg( QStringLiteral( "Error creating fields item: %1" ).arg( ex.what() ) );
203 : 0 : }
204 : 0 : }
205 : 0 : }
206 : :
207 : 0 : QgsFieldsItem::~QgsFieldsItem()
208 : 0 : {
209 : :
210 : 0 : }
211 : :
212 : 0 : QVector<QgsDataItem *> QgsFieldsItem::createChildren()
213 : : {
214 : 0 : QVector<QgsDataItem *> children;
215 : : try
216 : : {
217 : 0 : QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( providerKey() ) };
218 : 0 : if ( md )
219 : : {
220 : 0 : std::unique_ptr<QgsAbstractDatabaseProviderConnection> conn { static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( mConnectionUri, {} ) ) };
221 : 0 : if ( conn )
222 : : {
223 : 0 : int i = 0;
224 : 0 : const QgsFields constFields { conn->fields( mSchema, mTableName ) };
225 : 0 : for ( const auto &f : constFields )
226 : : {
227 : 0 : QgsFieldItem *fieldItem { new QgsFieldItem( this, f ) };
228 : 0 : fieldItem->setSortKey( i++ );
229 : 0 : children.push_back( fieldItem );
230 : : }
231 : 0 : }
232 : 0 : }
233 : 0 : }
234 : : catch ( const QgsProviderConnectionException &ex )
235 : : {
236 : 0 : children.push_back( new QgsErrorItem( this, ex.what(), path() + QStringLiteral( "/error" ) ) );
237 : 0 : }
238 : 0 : return children;
239 : 0 : }
240 : :
241 : 0 : QIcon QgsFieldsItem::icon()
242 : : {
243 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "mSourceFields.svg" ) );
244 : 0 : }
245 : :
246 : 0 : QString QgsFieldsItem::connectionUri() const
247 : : {
248 : 0 : return mConnectionUri;
249 : : }
250 : :
251 : 0 : QgsVectorLayer *QgsFieldsItem::layer()
252 : : {
253 : 0 : std::unique_ptr<QgsVectorLayer> vl;
254 : 0 : QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( providerKey() ) };
255 : 0 : if ( md )
256 : : {
257 : : try
258 : : {
259 : 0 : std::unique_ptr<QgsAbstractDatabaseProviderConnection> conn { static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( mConnectionUri, {} ) ) };
260 : 0 : if ( conn )
261 : : {
262 : 0 : vl.reset( new QgsVectorLayer( conn->tableUri( mSchema, mTableName ), QStringLiteral( "temp_layer" ), providerKey() ) );
263 : 0 : if ( vl->isValid() )
264 : : {
265 : 0 : return vl.release();
266 : : }
267 : 0 : }
268 : 0 : }
269 : : catch ( const QgsProviderConnectionException & )
270 : : {
271 : : // This should never happen!
272 : 0 : QgsDebugMsg( QStringLiteral( "Error getting connection from %1" ).arg( mConnectionUri ) );
273 : 0 : }
274 : 0 : }
275 : : else
276 : : {
277 : : // This should never happen!
278 : 0 : QgsDebugMsg( QStringLiteral( "Error getting metadata for provider %1" ).arg( providerKey() ) );
279 : : }
280 : 0 : return nullptr;
281 : 0 : }
282 : :
283 : 0 : QgsAbstractDatabaseProviderConnection::TableProperty *QgsFieldsItem::tableProperty() const
284 : : {
285 : 0 : return mTableProperty.get();
286 : : }
287 : :
288 : 0 : QString QgsFieldsItem::tableName() const
289 : : {
290 : 0 : return mTableName;
291 : : }
292 : :
293 : 0 : QString QgsFieldsItem::schema() const
294 : : {
295 : 0 : return mSchema;
296 : : }
297 : :
298 : 0 : QgsFieldItem::QgsFieldItem( QgsDataItem *parent, const QgsField &field )
299 : 0 : : QgsDataItem( QgsDataItem::Type::Field, parent, field.name(), parent->path() + '/' + field.name(), parent->providerKey() )
300 : 0 : , mField( field )
301 : 0 : {
302 : : // Precondition
303 : : Q_ASSERT( static_cast<QgsFieldsItem *>( parent ) );
304 : 0 : setState( QgsDataItem::State::Populated );
305 : 0 : }
306 : :
307 : 0 : QgsFieldItem::~QgsFieldItem()
308 : 0 : {
309 : 0 : }
310 : :
311 : 0 : QIcon QgsFieldItem::icon()
312 : : {
313 : : // Check if this is a geometry column and show the right icon
314 : 0 : QgsFieldsItem *parentFields { static_cast<QgsFieldsItem *>( parent() ) };
315 : 0 : if ( parentFields && parentFields->tableProperty() &&
316 : 0 : parentFields->tableProperty()->geometryColumn() == mName &&
317 : 0 : parentFields->tableProperty()->geometryColumnTypes().count() )
318 : : {
319 : 0 : if ( mField.typeName() == QLatin1String( "raster" ) )
320 : : {
321 : 0 : return QgsLayerItem::iconRaster();
322 : : }
323 : 0 : const QgsWkbTypes::GeometryType geomType { QgsWkbTypes::geometryType( parentFields->tableProperty()->geometryColumnTypes().first().wkbType ) };
324 : 0 : switch ( geomType )
325 : : {
326 : : case QgsWkbTypes::GeometryType::LineGeometry:
327 : 0 : return QgsLayerItem::iconLine();
328 : : case QgsWkbTypes::GeometryType::PointGeometry:
329 : 0 : return QgsLayerItem::iconPoint();
330 : : case QgsWkbTypes::GeometryType::PolygonGeometry:
331 : 0 : return QgsLayerItem::iconPolygon();
332 : : case QgsWkbTypes::GeometryType::UnknownGeometry:
333 : : case QgsWkbTypes::GeometryType::NullGeometry:
334 : 0 : return QgsLayerItem::iconDefault();
335 : : }
336 : 0 : }
337 : 0 : const QIcon icon { QgsFields::iconForFieldType( mField.type() ) };
338 : : // Try subtype if icon is null
339 : 0 : if ( icon.isNull() )
340 : : {
341 : 0 : return QgsFields::iconForFieldType( mField.subType() );
342 : : }
343 : 0 : return icon;
344 : 0 : }
345 : :
346 : 0 : QIcon QgsFavoritesItem::iconFavorites()
347 : : {
348 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "/mIconFavorites.svg" ) );
349 : 0 : }
350 : :
351 : 0 : QVariant QgsFavoritesItem::sortKey() const
352 : : {
353 : 0 : return QStringLiteral( " 0" );
354 : 0 : }
355 : :
356 : 0 : QIcon QgsZipItem::iconZip()
357 : : {
358 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "/mIconZip.svg" ) );
359 : 0 : }
360 : :
361 : : QgsAnimatedIcon *QgsDataItem::sPopulatingIcon = nullptr;
362 : :
363 : 0 : QgsDataItem::QgsDataItem( QgsDataItem::Type type, QgsDataItem *parent, const QString &name, const QString &path, const QString &providerKey )
364 : : // Do not pass parent to QObject, Qt would delete this when parent is deleted
365 : 0 : : mType( type )
366 : 0 : , mCapabilities( NoCapabilities )
367 : 0 : , mParent( parent )
368 : 0 : , mState( NotPopulated )
369 : 0 : , mName( name )
370 : 0 : , mProviderKey( providerKey )
371 : 0 : , mPath( path )
372 : 0 : , mDeferredDelete( false )
373 : 0 : , mFutureWatcher( nullptr )
374 : 0 : {
375 : 0 : }
376 : :
377 : 0 : QgsDataItem::~QgsDataItem()
378 : 0 : {
379 : 0 : QgsDebugMsgLevel( QStringLiteral( "mName = %1 mPath = %2 mChildren.size() = %3" ).arg( mName, mPath ).arg( mChildren.size() ), 2 );
380 : 0 : const auto constMChildren = mChildren;
381 : 0 : for ( QgsDataItem *child : constMChildren )
382 : : {
383 : 0 : if ( !child ) // should not happen
384 : 0 : continue;
385 : 0 : child->deleteLater();
386 : : }
387 : 0 : mChildren.clear();
388 : :
389 : 0 : if ( mFutureWatcher && !mFutureWatcher->isFinished() )
390 : : {
391 : : // this should not usually happen (until the item was deleted directly when createChildren was running)
392 : 0 : QgsDebugMsg( QStringLiteral( "mFutureWatcher not finished (should not happen) -> waitForFinished()" ) );
393 : 0 : mDeferredDelete = true;
394 : 0 : mFutureWatcher->waitForFinished();
395 : 0 : }
396 : :
397 : 0 : delete mFutureWatcher;
398 : 0 : }
399 : :
400 : 0 : QString QgsDataItem::pathComponent( const QString &string )
401 : : {
402 : 0 : return QString( string ).replace( QRegExp( "[\\\\/]" ), QStringLiteral( "|" ) );
403 : 0 : }
404 : :
405 : 0 : QVariant QgsDataItem::sortKey() const
406 : : {
407 : 0 : return mSortKey.isValid() ? mSortKey : name();
408 : 0 : }
409 : :
410 : 0 : void QgsDataItem::setSortKey( const QVariant &key )
411 : : {
412 : 0 : mSortKey = key;
413 : 0 : }
414 : :
415 : 0 : void QgsDataItem::deleteLater()
416 : : {
417 : 0 : QgsDebugMsgLevel( "path = " + path(), 3 );
418 : 0 : setParent( nullptr ); // also disconnects parent
419 : 0 : const auto constMChildren = mChildren;
420 : 0 : for ( QgsDataItem *child : constMChildren )
421 : : {
422 : 0 : if ( !child ) // should not happen
423 : 0 : continue;
424 : 0 : child->deleteLater();
425 : : }
426 : 0 : mChildren.clear();
427 : :
428 : 0 : if ( mFutureWatcher && !mFutureWatcher->isFinished() )
429 : : {
430 : 0 : QgsDebugMsg( QStringLiteral( "mFutureWatcher not finished -> schedule to delete later" ) );
431 : 0 : mDeferredDelete = true;
432 : 0 : }
433 : : else
434 : : {
435 : 0 : QObject::deleteLater();
436 : : }
437 : 0 : }
438 : :
439 : 0 : void QgsDataItem::deleteLater( QVector<QgsDataItem *> &items )
440 : : {
441 : 0 : const auto constItems = items;
442 : 0 : for ( QgsDataItem *item : constItems )
443 : : {
444 : 0 : if ( !item ) // should not happen
445 : 0 : continue;
446 : 0 : item->deleteLater();
447 : : }
448 : 0 : items.clear();
449 : 0 : }
450 : :
451 : 0 : void QgsDataItem::moveToThread( QThread *targetThread )
452 : : {
453 : : // QObject::moveToThread() cannot move objects with parent, but QgsDataItem is not using paren/children from QObject
454 : 0 : const auto constMChildren = mChildren;
455 : 0 : for ( QgsDataItem *child : constMChildren )
456 : : {
457 : 0 : if ( !child ) // should not happen
458 : 0 : continue;
459 : 0 : QgsDebugMsgLevel( "moveToThread child " + child->path(), 3 );
460 : 0 : child->QObject::setParent( nullptr ); // to be sure
461 : 0 : child->moveToThread( targetThread );
462 : : }
463 : 0 : QObject::moveToThread( targetThread );
464 : 0 : }
465 : :
466 : 0 : QgsAbstractDatabaseProviderConnection *QgsDataItem::databaseConnection() const
467 : : {
468 : 0 : return nullptr;
469 : : }
470 : :
471 : 0 : QIcon QgsDataItem::icon()
472 : : {
473 : 0 : if ( state() == Populating && sPopulatingIcon )
474 : 0 : return sPopulatingIcon->icon();
475 : :
476 : 0 : if ( !mIcon.isNull() )
477 : 0 : return mIcon;
478 : :
479 : 0 : if ( !mIconMap.contains( mIconName ) )
480 : : {
481 : 0 : mIconMap.insert( mIconName, mIconName.startsWith( ':' ) ? QIcon( mIconName ) : QgsApplication::getThemeIcon( mIconName ) );
482 : 0 : }
483 : :
484 : 0 : return mIconMap.value( mIconName );
485 : 0 : }
486 : :
487 : 0 : void QgsDataItem::setName( const QString &name )
488 : : {
489 : 0 : mName = name;
490 : 0 : emit dataChanged( this );
491 : 0 : }
492 : :
493 : 0 : QVector<QgsDataItem *> QgsDataItem::createChildren()
494 : : {
495 : 0 : return QVector<QgsDataItem *>();
496 : : }
497 : :
498 : 0 : void QgsDataItem::populate( bool foreground )
499 : : {
500 : 0 : if ( state() == Populated || state() == Populating )
501 : 0 : return;
502 : :
503 : 0 : QgsDebugMsgLevel( "mPath = " + mPath, 2 );
504 : :
505 : 0 : if ( capabilities2() & QgsDataItem::Fast || foreground )
506 : : {
507 : 0 : populate( createChildren() );
508 : 0 : }
509 : : else
510 : : {
511 : 0 : setState( Populating );
512 : : // The watcher must not be created with item (in constructor) because the item may be created in thread and the watcher created in thread does not work correctly.
513 : 0 : if ( !mFutureWatcher )
514 : : {
515 : 0 : mFutureWatcher = new QFutureWatcher< QVector <QgsDataItem *> >( this );
516 : 0 : }
517 : :
518 : 0 : connect( mFutureWatcher, &QFutureWatcherBase::finished, this, &QgsDataItem::childrenCreated );
519 : 0 : mFutureWatcher->setFuture( QtConcurrent::run( runCreateChildren, this ) );
520 : : }
521 : 0 : }
522 : :
523 : : // This is expected to be run in a separate thread
524 : 0 : QVector<QgsDataItem *> QgsDataItem::runCreateChildren( QgsDataItem *item )
525 : : {
526 : 0 : QgsDebugMsgLevel( "path = " + item->path(), 2 );
527 : 0 : QElapsedTimer time;
528 : 0 : time.start();
529 : 0 : QVector <QgsDataItem *> children = item->createChildren();
530 : 0 : QgsDebugMsgLevel( QStringLiteral( "%1 children created in %2 ms" ).arg( children.size() ).arg( time.elapsed() ), 3 );
531 : : // Children objects must be pushed to main thread.
532 : 0 : const auto constChildren = children;
533 : 0 : for ( QgsDataItem *child : constChildren )
534 : : {
535 : 0 : if ( !child ) // should not happen
536 : 0 : continue;
537 : 0 : QgsDebugMsgLevel( "moveToThread child " + child->path(), 2 );
538 : 0 : if ( qApp )
539 : 0 : child->moveToThread( qApp->thread() ); // moves also children
540 : : }
541 : 0 : QgsDebugMsgLevel( QStringLiteral( "finished path %1: %2 children" ).arg( item->path() ).arg( children.size() ), 3 );
542 : 0 : return children;
543 : 0 : }
544 : :
545 : 0 : void QgsDataItem::childrenCreated()
546 : : {
547 : 0 : QgsDebugMsgLevel( QStringLiteral( "path = %1 children.size() = %2" ).arg( path() ).arg( mFutureWatcher->result().size() ), 3 );
548 : :
549 : 0 : if ( deferredDelete() )
550 : : {
551 : 0 : QgsDebugMsg( QStringLiteral( "Item was scheduled to be deleted later" ) );
552 : 0 : QObject::deleteLater();
553 : 0 : return;
554 : : }
555 : :
556 : 0 : if ( mChildren.isEmpty() ) // usually populating but may also be refresh if originally there were no children
557 : : {
558 : 0 : populate( mFutureWatcher->result() );
559 : 0 : }
560 : : else // refreshing
561 : : {
562 : 0 : refresh( mFutureWatcher->result() );
563 : : }
564 : 0 : disconnect( mFutureWatcher, &QFutureWatcherBase::finished, this, &QgsDataItem::childrenCreated );
565 : 0 : emit dataChanged( this ); // to replace loading icon by normal icon
566 : 0 : }
567 : :
568 : 0 : void QgsDataItem::updateIcon()
569 : : {
570 : 0 : emit dataChanged( this );
571 : 0 : }
572 : :
573 : 0 : void QgsDataItem::populate( const QVector<QgsDataItem *> &children )
574 : : {
575 : 0 : QgsDebugMsgLevel( "mPath = " + mPath, 3 );
576 : :
577 : 0 : const auto constChildren = children;
578 : 0 : for ( QgsDataItem *child : constChildren )
579 : : {
580 : 0 : if ( !child ) // should not happen
581 : 0 : continue;
582 : : // update after thread finished -> refresh
583 : 0 : addChildItem( child, true );
584 : : }
585 : 0 : setState( Populated );
586 : 0 : }
587 : :
588 : 0 : void QgsDataItem::depopulate()
589 : : {
590 : 0 : QgsDebugMsgLevel( "mPath = " + mPath, 3 );
591 : :
592 : 0 : const auto constMChildren = mChildren;
593 : 0 : for ( QgsDataItem *child : constMChildren )
594 : : {
595 : 0 : QgsDebugMsgLevel( "remove " + child->path(), 3 );
596 : 0 : child->depopulate(); // recursive
597 : 0 : deleteChildItem( child );
598 : : }
599 : 0 : setState( NotPopulated );
600 : 0 : }
601 : :
602 : 0 : void QgsDataItem::refresh()
603 : : {
604 : 0 : if ( state() == Populating )
605 : 0 : return;
606 : :
607 : 0 : QgsDebugMsgLevel( "mPath = " + mPath, 3 );
608 : :
609 : 0 : if ( capabilities2() & QgsDataItem::Fast )
610 : : {
611 : 0 : refresh( createChildren() );
612 : 0 : }
613 : : else
614 : : {
615 : 0 : setState( Populating );
616 : 0 : if ( !mFutureWatcher )
617 : : {
618 : 0 : mFutureWatcher = new QFutureWatcher< QVector <QgsDataItem *> >( this );
619 : 0 : }
620 : 0 : connect( mFutureWatcher, &QFutureWatcherBase::finished, this, &QgsDataItem::childrenCreated );
621 : 0 : mFutureWatcher->setFuture( QtConcurrent::run( runCreateChildren, this ) );
622 : : }
623 : 0 : }
624 : :
625 : 0 : void QgsDataItem::refreshConnections( const QString &key )
626 : : {
627 : : // Walk up until the root node is reached
628 : 0 : if ( mParent )
629 : : {
630 : 0 : mParent->refreshConnections( key );
631 : 0 : }
632 : : else
633 : : {
634 : : // if a specific key was specified then we use that -- otherwise we assume the connections
635 : : // changed belong to the same provider as this item
636 : 0 : emit connectionsChanged( key.isEmpty() ? providerKey() : key );
637 : : }
638 : 0 : }
639 : :
640 : 0 : void QgsDataItem::refresh( const QVector<QgsDataItem *> &children )
641 : : {
642 : 0 : QgsDebugMsgLevel( "mPath = " + mPath, 2 );
643 : :
644 : : // Remove no more present children
645 : 0 : QVector<QgsDataItem *> remove;
646 : 0 : const auto constMChildren = mChildren;
647 : 0 : for ( QgsDataItem *child : constMChildren )
648 : : {
649 : 0 : if ( !child ) // should not happen
650 : 0 : continue;
651 : 0 : if ( findItem( children, child ) >= 0 )
652 : 0 : continue;
653 : 0 : remove.append( child );
654 : : }
655 : 0 : const auto constRemove = remove;
656 : 0 : for ( QgsDataItem *child : constRemove )
657 : : {
658 : 0 : QgsDebugMsgLevel( "remove " + child->path(), 3 );
659 : 0 : deleteChildItem( child );
660 : : }
661 : :
662 : : // Add new children
663 : 0 : const auto constChildren = children;
664 : 0 : for ( QgsDataItem *child : constChildren )
665 : : {
666 : 0 : if ( !child ) // should not happen
667 : 0 : continue;
668 : :
669 : 0 : int index = findItem( mChildren, child );
670 : 0 : if ( index >= 0 )
671 : : {
672 : : // Refresh recursively (some providers may create more generations of descendants)
673 : 0 : if ( !( child->capabilities2() & QgsDataItem::Fertile ) )
674 : : {
675 : : // The child cannot createChildren() itself
676 : 0 : mChildren.value( index )->refresh( child->children() );
677 : 0 : }
678 : :
679 : 0 : child->deleteLater();
680 : 0 : continue;
681 : : }
682 : 0 : addChildItem( child, true );
683 : : }
684 : 0 : setState( Populated );
685 : 0 : }
686 : :
687 : 0 : QString QgsDataItem::providerKey() const
688 : : {
689 : 0 : return mProviderKey;
690 : : }
691 : :
692 : 0 : void QgsDataItem::setProviderKey( const QString &value )
693 : : {
694 : 0 : mProviderKey = value;
695 : 0 : }
696 : :
697 : 0 : int QgsDataItem::rowCount()
698 : : {
699 : 0 : return mChildren.size();
700 : : }
701 : 0 : bool QgsDataItem::hasChildren()
702 : : {
703 : 0 : return ( state() == Populated ? !mChildren.isEmpty() : true );
704 : : }
705 : :
706 : 0 : bool QgsDataItem::layerCollection() const
707 : : {
708 : 0 : return false;
709 : : }
710 : :
711 : 0 : void QgsDataItem::setParent( QgsDataItem *parent )
712 : : {
713 : 0 : if ( mParent )
714 : : {
715 : 0 : disconnect( this, nullptr, mParent, nullptr );
716 : 0 : }
717 : 0 : if ( parent )
718 : : {
719 : 0 : connect( this, &QgsDataItem::beginInsertItems, parent, &QgsDataItem::beginInsertItems );
720 : 0 : connect( this, &QgsDataItem::endInsertItems, parent, &QgsDataItem::endInsertItems );
721 : 0 : connect( this, &QgsDataItem::beginRemoveItems, parent, &QgsDataItem::beginRemoveItems );
722 : 0 : connect( this, &QgsDataItem::endRemoveItems, parent, &QgsDataItem::endRemoveItems );
723 : 0 : connect( this, &QgsDataItem::dataChanged, parent, &QgsDataItem::dataChanged );
724 : 0 : connect( this, &QgsDataItem::stateChanged, parent, &QgsDataItem::stateChanged );
725 : 0 : }
726 : 0 : mParent = parent;
727 : 0 : }
728 : :
729 : 0 : void QgsDataItem::addChildItem( QgsDataItem *child, bool refresh )
730 : : {
731 : : Q_ASSERT( child );
732 : 0 : QgsDebugMsgLevel( QStringLiteral( "path = %1 add child #%2 - %3 - %4" ).arg( mPath ).arg( mChildren.size() ).arg( child->mName ).arg( child->mType ), 3 );
733 : :
734 : : //calculate position to insert child
735 : : int i;
736 : 0 : if ( type() == Directory )
737 : : {
738 : 0 : for ( i = 0; i < mChildren.size(); i++ )
739 : : {
740 : : // sort items by type, so directories are before data items
741 : 0 : if ( mChildren.at( i )->mType == child->mType &&
742 : 0 : mChildren.at( i )->mName.localeAwareCompare( child->mName ) > 0 )
743 : 0 : break;
744 : 0 : }
745 : 0 : }
746 : : else
747 : : {
748 : 0 : for ( i = 0; i < mChildren.size(); i++ )
749 : : {
750 : 0 : if ( mChildren.at( i )->mName.localeAwareCompare( child->mName ) >= 0 )
751 : 0 : break;
752 : 0 : }
753 : : }
754 : :
755 : 0 : if ( refresh )
756 : 0 : emit beginInsertItems( this, i, i );
757 : :
758 : 0 : mChildren.insert( i, child );
759 : 0 : child->setParent( this );
760 : :
761 : 0 : if ( refresh )
762 : 0 : emit endInsertItems();
763 : 0 : }
764 : :
765 : 0 : void QgsDataItem::deleteChildItem( QgsDataItem *child )
766 : : {
767 : 0 : QgsDebugMsgLevel( "mName = " + child->mName, 2 );
768 : 0 : int i = mChildren.indexOf( child );
769 : : Q_ASSERT( i >= 0 );
770 : 0 : emit beginRemoveItems( this, i, i );
771 : 0 : mChildren.remove( i );
772 : 0 : child->deleteLater();
773 : 0 : emit endRemoveItems();
774 : 0 : }
775 : :
776 : 0 : QgsDataItem *QgsDataItem::removeChildItem( QgsDataItem *child )
777 : : {
778 : 0 : QgsDebugMsgLevel( "mName = " + child->mName, 2 );
779 : 0 : int i = mChildren.indexOf( child );
780 : : Q_ASSERT( i >= 0 );
781 : 0 : if ( i < 0 )
782 : : {
783 : 0 : child->setParent( nullptr );
784 : 0 : return nullptr;
785 : : }
786 : :
787 : 0 : emit beginRemoveItems( this, i, i );
788 : 0 : mChildren.remove( i );
789 : 0 : emit endRemoveItems();
790 : 0 : return child;
791 : 0 : }
792 : :
793 : 0 : int QgsDataItem::findItem( QVector<QgsDataItem *> items, QgsDataItem *item )
794 : : {
795 : 0 : for ( int i = 0; i < items.size(); i++ )
796 : : {
797 : : Q_ASSERT_X( items[i], "findItem", QStringLiteral( "item %1 is nullptr" ).arg( i ).toLatin1() );
798 : 0 : QgsDebugMsgLevel( QString::number( i ) + " : " + items[i]->mPath + " x " + item->mPath, 2 );
799 : 0 : if ( items[i]->equal( item ) )
800 : 0 : return i;
801 : 0 : }
802 : 0 : return -1;
803 : 0 : }
804 : :
805 : 0 : bool QgsDataItem::equal( const QgsDataItem *other )
806 : : {
807 : 0 : return ( metaObject()->className() == other->metaObject()->className() &&
808 : 0 : mPath == other->path() );
809 : : }
810 : :
811 : 0 : QList<QAction *> QgsDataItem::actions( QWidget *parent )
812 : : {
813 : : Q_UNUSED( parent )
814 : 0 : return QList<QAction *>();
815 : : }
816 : :
817 : 0 : bool QgsDataItem::handleDoubleClick()
818 : : {
819 : 0 : return false;
820 : : }
821 : :
822 : 0 : QgsMimeDataUtils::Uri QgsDataItem::mimeUri() const
823 : : {
824 : 0 : return mimeUris().isEmpty() ? QgsMimeDataUtils::Uri() : mimeUris().first();
825 : 0 : }
826 : :
827 : 0 : bool QgsDataItem::rename( const QString & )
828 : : {
829 : 0 : return false;
830 : : }
831 : :
832 : 0 : QgsDataItem::State QgsDataItem::state() const
833 : : {
834 : 0 : return mState;
835 : : }
836 : :
837 : 0 : void QgsDataItem::setState( State state )
838 : : {
839 : 0 : QgsDebugMsgLevel( QStringLiteral( "item %1 set state %2 -> %3" ).arg( path() ).arg( this->state() ).arg( state ), 3 );
840 : 0 : if ( state == mState )
841 : 0 : return;
842 : :
843 : 0 : State oldState = mState;
844 : :
845 : 0 : if ( state == Populating ) // start loading
846 : : {
847 : 0 : if ( !sPopulatingIcon )
848 : : {
849 : : // TODO: ensure that QgsAnimatedIcon is created on UI thread only
850 : 0 : sPopulatingIcon = new QgsAnimatedIcon( QgsApplication::iconPath( QStringLiteral( "/mIconLoading.gif" ) ), QgsApplication::instance() );
851 : 0 : }
852 : :
853 : 0 : sPopulatingIcon->connectFrameChanged( this, &QgsDataItem::updateIcon );
854 : 0 : }
855 : 0 : else if ( mState == Populating && sPopulatingIcon ) // stop loading
856 : : {
857 : 0 : sPopulatingIcon->disconnectFrameChanged( this, &QgsDataItem::updateIcon );
858 : 0 : }
859 : :
860 : :
861 : 0 : mState = state;
862 : :
863 : 0 : emit stateChanged( this, oldState );
864 : 0 : if ( state == Populated )
865 : 0 : updateIcon();
866 : 0 : }
867 : :
868 : 0 : QList<QMenu *> QgsDataItem::menus( QWidget *parent )
869 : 0 : {
870 : : Q_UNUSED( parent )
871 : 0 : return QList<QMenu *>();
872 : : }
873 : :
874 : : // ---------------------------------------------------------------------
875 : :
876 : 0 : QgsLayerItem::QgsLayerItem( QgsDataItem *parent, const QString &name, const QString &path,
877 : : const QString &uri, LayerType layerType, const QString &providerKey )
878 : 0 : : QgsDataItem( Layer, parent, name, path, providerKey )
879 : 0 : , mUri( uri )
880 : 0 : , mLayerType( layerType )
881 : 0 : {
882 : 0 : mIconName = iconName( layerType );
883 : 0 : }
884 : :
885 : 0 : QgsMapLayerType QgsLayerItem::mapLayerType() const
886 : : {
887 : 0 : switch ( mLayerType )
888 : : {
889 : : case QgsLayerItem::Raster:
890 : 0 : return QgsMapLayerType::RasterLayer;
891 : :
892 : : case QgsLayerItem::Mesh:
893 : 0 : return QgsMapLayerType::MeshLayer;
894 : :
895 : : case QgsLayerItem::VectorTile:
896 : 0 : return QgsMapLayerType::VectorTileLayer;
897 : :
898 : : case QgsLayerItem::Plugin:
899 : 0 : return QgsMapLayerType::PluginLayer;
900 : :
901 : : case QgsLayerItem::PointCloud:
902 : 0 : return QgsMapLayerType::PointCloudLayer;
903 : :
904 : : case QgsLayerItem::NoType:
905 : : case QgsLayerItem::Vector:
906 : : case QgsLayerItem::Point:
907 : : case QgsLayerItem::Polygon:
908 : : case QgsLayerItem::Line:
909 : : case QgsLayerItem::TableLayer:
910 : : case QgsLayerItem::Table:
911 : : case QgsLayerItem::Database:
912 : 0 : return QgsMapLayerType::VectorLayer;
913 : : }
914 : :
915 : 0 : return QgsMapLayerType::VectorLayer; // no warnings
916 : 0 : }
917 : :
918 : 0 : QgsLayerItem::LayerType QgsLayerItem::typeFromMapLayer( QgsMapLayer *layer )
919 : : {
920 : 0 : switch ( layer->type() )
921 : : {
922 : : case QgsMapLayerType::VectorLayer:
923 : : {
924 : 0 : switch ( qobject_cast< QgsVectorLayer * >( layer )->geometryType() )
925 : : {
926 : : case QgsWkbTypes::PointGeometry:
927 : 0 : return Point;
928 : :
929 : : case QgsWkbTypes::LineGeometry:
930 : 0 : return Line;
931 : :
932 : : case QgsWkbTypes::PolygonGeometry:
933 : 0 : return Polygon;
934 : :
935 : : case QgsWkbTypes::NullGeometry:
936 : 0 : return TableLayer;
937 : :
938 : : case QgsWkbTypes::UnknownGeometry:
939 : 0 : return Vector;
940 : : }
941 : :
942 : 0 : return Vector; // no warnings
943 : : }
944 : :
945 : : case QgsMapLayerType::RasterLayer:
946 : 0 : return Raster;
947 : : case QgsMapLayerType::PluginLayer:
948 : 0 : return Plugin;
949 : : case QgsMapLayerType::MeshLayer:
950 : 0 : return Mesh;
951 : : case QgsMapLayerType::PointCloudLayer:
952 : 0 : return PointCloud;
953 : : case QgsMapLayerType::VectorTileLayer:
954 : 0 : return VectorTile;
955 : : case QgsMapLayerType::AnnotationLayer:
956 : 0 : return Vector; // will never happen!
957 : : }
958 : 0 : return Vector; // no warnings
959 : 0 : }
960 : :
961 : 0 : QString QgsLayerItem::layerTypeAsString( QgsLayerItem::LayerType layerType )
962 : : {
963 : 0 : static int enumIdx = staticMetaObject.indexOfEnumerator( "LayerType" );
964 : 0 : return staticMetaObject.enumerator( enumIdx ).valueToKey( layerType );
965 : 0 : }
966 : :
967 : 0 : QString QgsLayerItem::iconName( QgsLayerItem::LayerType layerType )
968 : : {
969 : 0 : switch ( layerType )
970 : : {
971 : : case Point:
972 : 0 : return QStringLiteral( "/mIconPointLayer.svg" );
973 : : case Line:
974 : 0 : return QStringLiteral( "/mIconLineLayer.svg" );
975 : : case Polygon:
976 : 0 : return QStringLiteral( "/mIconPolygonLayer.svg" );
977 : : // TODO add a new icon for generic Vector layers
978 : : case Vector :
979 : 0 : return QStringLiteral( "/mIconVector.svg" );
980 : : case TableLayer:
981 : : case Table:
982 : 0 : return QStringLiteral( "/mIconTableLayer.svg" );
983 : : case Raster:
984 : 0 : return QStringLiteral( "/mIconRaster.svg" );
985 : : case Mesh:
986 : 0 : return QStringLiteral( "/mIconMeshLayer.svg" );
987 : : case PointCloud:
988 : 0 : return QStringLiteral( "/mIconPointCloudLayer.svg" );
989 : : default:
990 : 0 : return QStringLiteral( "/mIconLayer.png" );
991 : : }
992 : 0 : }
993 : :
994 : 0 : bool QgsLayerItem::deleteLayer()
995 : : {
996 : 0 : return false;
997 : : }
998 : :
999 : 0 : bool QgsLayerItem::equal( const QgsDataItem *other )
1000 : : {
1001 : : //QgsDebugMsg ( mPath + " x " + other->mPath );
1002 : 0 : if ( type() != other->type() )
1003 : : {
1004 : 0 : return false;
1005 : : }
1006 : : //const QgsLayerItem *o = qobject_cast<const QgsLayerItem *> ( other );
1007 : 0 : const QgsLayerItem *o = qobject_cast<const QgsLayerItem *>( other );
1008 : 0 : if ( !o )
1009 : 0 : return false;
1010 : :
1011 : 0 : return ( mPath == o->mPath && mName == o->mName && mUri == o->mUri && mProviderKey == o->mProviderKey );
1012 : 0 : }
1013 : :
1014 : 0 : QgsMimeDataUtils::UriList QgsLayerItem::mimeUris() const
1015 : : {
1016 : 0 : QgsMimeDataUtils::Uri u;
1017 : :
1018 : 0 : switch ( mapLayerType() )
1019 : : {
1020 : : case QgsMapLayerType::VectorLayer:
1021 : 0 : u.layerType = QStringLiteral( "vector" );
1022 : 0 : switch ( mLayerType )
1023 : : {
1024 : : case Point:
1025 : 0 : u.wkbType = QgsWkbTypes::Point;
1026 : 0 : break;
1027 : : case Line:
1028 : 0 : u.wkbType = QgsWkbTypes::LineString;
1029 : 0 : break;
1030 : : case Polygon:
1031 : 0 : u.wkbType = QgsWkbTypes::Polygon;
1032 : 0 : break;
1033 : : case TableLayer:
1034 : 0 : u.wkbType = QgsWkbTypes::NoGeometry;
1035 : 0 : break;
1036 : :
1037 : : case Database:
1038 : : case Table:
1039 : : case NoType:
1040 : : case Vector:
1041 : : case Raster:
1042 : : case Plugin:
1043 : : case Mesh:
1044 : : case PointCloud:
1045 : : case VectorTile:
1046 : 0 : break;
1047 : : }
1048 : 0 : break;
1049 : : case QgsMapLayerType::RasterLayer:
1050 : 0 : u.layerType = QStringLiteral( "raster" );
1051 : 0 : break;
1052 : : case QgsMapLayerType::MeshLayer:
1053 : 0 : u.layerType = QStringLiteral( "mesh" );
1054 : 0 : break;
1055 : : case QgsMapLayerType::VectorTileLayer:
1056 : 0 : u.layerType = QStringLiteral( "vector-tile" );
1057 : 0 : break;
1058 : : case QgsMapLayerType::PointCloudLayer:
1059 : 0 : u.layerType = QStringLiteral( "pointcloud" );
1060 : 0 : break;
1061 : : case QgsMapLayerType::PluginLayer:
1062 : 0 : u.layerType = QStringLiteral( "plugin" );
1063 : 0 : break;
1064 : : case QgsMapLayerType::AnnotationLayer:
1065 : 0 : u.layerType = QStringLiteral( "annotation" );
1066 : 0 : break;
1067 : : }
1068 : :
1069 : 0 : u.providerKey = providerKey();
1070 : 0 : u.name = layerName();
1071 : 0 : u.uri = uri();
1072 : 0 : u.supportedCrs = supportedCrs();
1073 : 0 : u.supportedFormats = supportedFormats();
1074 : 0 : return { u };
1075 : 0 : }
1076 : :
1077 : : // ---------------------------------------------------------------------
1078 : 0 : QgsDataCollectionItem::QgsDataCollectionItem( QgsDataItem *parent,
1079 : : const QString &name,
1080 : : const QString &path,
1081 : : const QString &providerKey )
1082 : 0 : : QgsDataItem( Collection, parent, name, path, providerKey )
1083 : 0 : {
1084 : 0 : mCapabilities = Fertile;
1085 : 0 : mIconName = QStringLiteral( "/mIconDbSchema.svg" );
1086 : 0 : }
1087 : :
1088 : 0 : QgsDataCollectionItem::~QgsDataCollectionItem()
1089 : 0 : {
1090 : 0 : QgsDebugMsgLevel( "mName = " + mName + " mPath = " + mPath, 2 );
1091 : :
1092 : : // Do not delete children, children are deleted by QObject parent
1093 : : #if 0
1094 : : const auto constMChildren = mChildren;
1095 : : for ( QgsDataItem *i : constMChildren )
1096 : : {
1097 : : QgsDebugMsgLevel( QStringLiteral( "delete child = 0x%0" ).arg( static_cast<qlonglong>( i ), 8, 16, QLatin1Char( '0' ) ), 2 );
1098 : : delete i;
1099 : : }
1100 : : #endif
1101 : 0 : }
1102 : :
1103 : : //-----------------------------------------------------------------------
1104 : :
1105 : 0 : QgsDirectoryItem::QgsDirectoryItem( QgsDataItem *parent, const QString &name, const QString &path )
1106 : 0 : : QgsDataCollectionItem( parent, QDir::toNativeSeparators( name ), path )
1107 : 0 : , mDirPath( path )
1108 : 0 : , mRefreshLater( false )
1109 : 0 : {
1110 : 0 : mType = Directory;
1111 : 0 : init();
1112 : 0 : }
1113 : :
1114 : 0 : QgsDirectoryItem::QgsDirectoryItem( QgsDataItem *parent, const QString &name,
1115 : : const QString &dirPath, const QString &path,
1116 : : const QString &providerKey )
1117 : 0 : : QgsDataCollectionItem( parent, QDir::toNativeSeparators( name ), path, providerKey )
1118 : 0 : , mDirPath( dirPath )
1119 : 0 : , mRefreshLater( false )
1120 : 0 : {
1121 : 0 : mType = Directory;
1122 : 0 : init();
1123 : 0 : }
1124 : :
1125 : 0 : void QgsDirectoryItem::init()
1126 : : {
1127 : 0 : setToolTip( QDir::toNativeSeparators( mDirPath ) );
1128 : 0 : }
1129 : :
1130 : 0 : QIcon QgsDirectoryItem::icon()
1131 : : {
1132 : 0 : if ( mDirPath == QDir::homePath() )
1133 : 0 : return homeDirIcon();
1134 : :
1135 : : // still loading? show the spinner
1136 : 0 : if ( state() == Populating )
1137 : 0 : return QgsDataItem::icon();
1138 : :
1139 : : // symbolic link? use link icon
1140 : 0 : QFileInfo fi( mDirPath );
1141 : 0 : if ( fi.isDir() && fi.isSymLink() )
1142 : : {
1143 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "mIconFolderLink.svg" ) );
1144 : : }
1145 : :
1146 : : // loaded? show the open dir icon
1147 : 0 : if ( state() == Populated )
1148 : 0 : return openDirIcon();
1149 : :
1150 : : // show the closed dir icon
1151 : 0 : return iconDir();
1152 : 0 : }
1153 : :
1154 : :
1155 : 0 : QVector<QgsDataItem *> QgsDirectoryItem::createChildren()
1156 : : {
1157 : 0 : QVector<QgsDataItem *> children;
1158 : 0 : QDir dir( mDirPath );
1159 : :
1160 : 0 : const QList<QgsDataItemProvider *> providers = QgsApplication::dataItemProviderRegistry()->providers();
1161 : :
1162 : 0 : QStringList entries = dir.entryList( QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name | QDir::IgnoreCase );
1163 : 0 : const auto constEntries = entries;
1164 : 0 : for ( const QString &subdir : constEntries )
1165 : : {
1166 : 0 : if ( mRefreshLater )
1167 : : {
1168 : 0 : deleteLater( children );
1169 : 0 : return children;
1170 : : }
1171 : :
1172 : 0 : QString subdirPath = dir.absoluteFilePath( subdir );
1173 : :
1174 : 0 : QgsDebugMsgLevel( QStringLiteral( "creating subdir: %1" ).arg( subdirPath ), 2 );
1175 : :
1176 : 0 : QString path = mPath + '/' + subdir; // may differ from subdirPath
1177 : 0 : if ( QgsDirectoryItem::hiddenPath( path ) )
1178 : 0 : continue;
1179 : :
1180 : 0 : bool handledByProvider = false;
1181 : 0 : for ( QgsDataItemProvider *provider : providers )
1182 : : {
1183 : 0 : if ( provider->handlesDirectoryPath( path ) )
1184 : : {
1185 : 0 : handledByProvider = true;
1186 : 0 : break;
1187 : : }
1188 : : }
1189 : 0 : if ( handledByProvider )
1190 : 0 : continue;
1191 : :
1192 : 0 : QgsDirectoryItem *item = new QgsDirectoryItem( this, subdir, subdirPath, path );
1193 : :
1194 : : // we want directories shown before files
1195 : 0 : item->setSortKey( QStringLiteral( " %1" ).arg( subdir ) );
1196 : :
1197 : : // propagate signals up to top
1198 : :
1199 : 0 : children.append( item );
1200 : 0 : }
1201 : :
1202 : 0 : QStringList fileEntries = dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot | QDir::Files, QDir::Name );
1203 : 0 : const auto constFileEntries = fileEntries;
1204 : 0 : for ( const QString &name : constFileEntries )
1205 : : {
1206 : 0 : if ( mRefreshLater )
1207 : : {
1208 : 0 : deleteLater( children );
1209 : 0 : return children;
1210 : : }
1211 : :
1212 : 0 : QString path = dir.absoluteFilePath( name );
1213 : 0 : QFileInfo fileInfo( path );
1214 : :
1215 : 0 : if ( fileInfo.suffix().compare( QLatin1String( "zip" ), Qt::CaseInsensitive ) == 0 ||
1216 : 0 : fileInfo.suffix().compare( QLatin1String( "tar" ), Qt::CaseInsensitive ) == 0 )
1217 : : {
1218 : 0 : QgsDataItem *item = QgsZipItem::itemFromPath( this, path, name, mPath + '/' + name );
1219 : 0 : if ( item )
1220 : : {
1221 : 0 : children.append( item );
1222 : 0 : continue;
1223 : : }
1224 : 0 : }
1225 : :
1226 : 0 : bool createdItem = false;
1227 : 0 : for ( QgsDataItemProvider *provider : providers )
1228 : : {
1229 : 0 : int capabilities = provider->capabilities();
1230 : :
1231 : 0 : if ( !( ( fileInfo.isFile() && ( capabilities & QgsDataProvider::File ) ) ||
1232 : 0 : ( fileInfo.isDir() && ( capabilities & QgsDataProvider::Dir ) ) ) )
1233 : : {
1234 : 0 : continue;
1235 : : }
1236 : :
1237 : 0 : QgsDataItem *item = provider->createDataItem( path, this );
1238 : 0 : if ( item )
1239 : : {
1240 : 0 : children.append( item );
1241 : 0 : createdItem = true;
1242 : 0 : }
1243 : : }
1244 : :
1245 : 0 : if ( !createdItem )
1246 : : {
1247 : : // if item is a QGIS project, and no specific item provider has overridden handling of
1248 : : // project items, then use the default project item behavior
1249 : 0 : if ( fileInfo.suffix().compare( QLatin1String( "qgs" ), Qt::CaseInsensitive ) == 0 ||
1250 : 0 : fileInfo.suffix().compare( QLatin1String( "qgz" ), Qt::CaseInsensitive ) == 0 )
1251 : : {
1252 : 0 : QgsDataItem *item = new QgsProjectItem( this, fileInfo.completeBaseName(), path );
1253 : 0 : children.append( item );
1254 : 0 : continue;
1255 : : }
1256 : 0 : }
1257 : :
1258 : 0 : }
1259 : 0 : return children;
1260 : 0 : }
1261 : :
1262 : 0 : void QgsDirectoryItem::setState( State state )
1263 : : {
1264 : 0 : QgsDataCollectionItem::setState( state );
1265 : :
1266 : 0 : if ( state == Populated )
1267 : : {
1268 : 0 : if ( !mFileSystemWatcher )
1269 : : {
1270 : 0 : mFileSystemWatcher = new QFileSystemWatcher( this );
1271 : 0 : mFileSystemWatcher->addPath( mDirPath );
1272 : 0 : connect( mFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &QgsDirectoryItem::directoryChanged );
1273 : 0 : }
1274 : 0 : mLastScan = QDateTime::currentDateTime();
1275 : 0 : }
1276 : 0 : else if ( state == NotPopulated )
1277 : : {
1278 : 0 : if ( mFileSystemWatcher )
1279 : : {
1280 : 0 : delete mFileSystemWatcher;
1281 : 0 : mFileSystemWatcher = nullptr;
1282 : 0 : }
1283 : 0 : }
1284 : 0 : }
1285 : :
1286 : 0 : void QgsDirectoryItem::directoryChanged()
1287 : : {
1288 : : // If the last scan was less than 10 seconds ago, skip this
1289 : 0 : if ( mLastScan.msecsTo( QDateTime::currentDateTime() ) < QgsSettings().value( QStringLiteral( "browser/minscaninterval" ), 10000 ).toInt() )
1290 : : {
1291 : 0 : return;
1292 : : }
1293 : 0 : if ( state() == Populating )
1294 : : {
1295 : : // schedule to refresh later, because refresh() simply returns if Populating
1296 : 0 : mRefreshLater = true;
1297 : 0 : }
1298 : : else
1299 : : {
1300 : : // We definintely don't want the temporary files created by sqlite
1301 : : // to re-trigger a refresh in an infinite loop.
1302 : 0 : disconnect( mFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &QgsDirectoryItem::directoryChanged );
1303 : : // QFileSystemWhatcher::directoryChanged is emitted when a
1304 : : // file is created and not when it is closed/flushed.
1305 : : //
1306 : : // Delay to give to OS the time to complete writing the file
1307 : : // this happens when a new file appears in the directory and
1308 : : // the item's children thread will try to open the file with
1309 : : // GDAL or OGR even if it is still being written.
1310 : 0 : QTimer::singleShot( 100, this, [ = ] { refresh(); } );
1311 : : }
1312 : 0 : }
1313 : :
1314 : 0 : bool QgsDirectoryItem::hiddenPath( const QString &path )
1315 : : {
1316 : 0 : QgsSettings settings;
1317 : 0 : QStringList hiddenItems = settings.value( QStringLiteral( "browser/hiddenPaths" ),
1318 : 0 : QStringList() ).toStringList();
1319 : 0 : int idx = hiddenItems.indexOf( path );
1320 : 0 : return ( idx > -1 );
1321 : 0 : }
1322 : :
1323 : 0 : void QgsDirectoryItem::childrenCreated()
1324 : : {
1325 : 0 : QgsDebugMsgLevel( QStringLiteral( "mRefreshLater = %1" ).arg( mRefreshLater ), 3 );
1326 : :
1327 : 0 : if ( mRefreshLater )
1328 : : {
1329 : 0 : QgsDebugMsgLevel( QStringLiteral( "directory changed during createChidren() -> refresh() again" ), 3 );
1330 : 0 : mRefreshLater = false;
1331 : 0 : setState( Populated );
1332 : 0 : refresh();
1333 : 0 : }
1334 : : else
1335 : : {
1336 : 0 : QgsDataCollectionItem::childrenCreated();
1337 : : }
1338 : : // Re-connect the file watcher after all children have been created
1339 : 0 : connect( mFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &QgsDirectoryItem::directoryChanged );
1340 : 0 : }
1341 : :
1342 : 0 : bool QgsDirectoryItem::equal( const QgsDataItem *other )
1343 : : {
1344 : : //QgsDebugMsg ( mPath + " x " + other->mPath );
1345 : 0 : if ( type() != other->type() )
1346 : : {
1347 : 0 : return false;
1348 : : }
1349 : 0 : return ( path() == other->path() );
1350 : 0 : }
1351 : :
1352 : 0 : QWidget *QgsDirectoryItem::paramWidget()
1353 : : {
1354 : 0 : return new QgsDirectoryParamWidget( mPath );
1355 : 0 : }
1356 : :
1357 : 0 : QgsMimeDataUtils::UriList QgsDirectoryItem::mimeUris() const
1358 : : {
1359 : 0 : QgsMimeDataUtils::Uri u;
1360 : 0 : u.layerType = QStringLiteral( "directory" );
1361 : 0 : u.name = mName;
1362 : 0 : u.uri = mDirPath;
1363 : 0 : return { u };
1364 : 0 : }
1365 : :
1366 : 0 : QgsDirectoryParamWidget::QgsDirectoryParamWidget( const QString &path, QWidget *parent )
1367 : 0 : : QTreeWidget( parent )
1368 : 0 : {
1369 : 0 : setRootIsDecorated( false );
1370 : :
1371 : : // name, size, date, permissions, owner, group, type
1372 : 0 : setColumnCount( 7 );
1373 : 0 : QStringList labels;
1374 : 0 : labels << tr( "Name" ) << tr( "Size" ) << tr( "Date" ) << tr( "Permissions" ) << tr( "Owner" ) << tr( "Group" ) << tr( "Type" );
1375 : 0 : setHeaderLabels( labels );
1376 : :
1377 : 0 : QIcon iconDirectory = QgsApplication::getThemeIcon( QStringLiteral( "mIconFolder.svg" ) );
1378 : 0 : QIcon iconFile = QgsApplication::getThemeIcon( QStringLiteral( "mIconFile.svg" ) );
1379 : 0 : QIcon iconDirLink = QgsApplication::getThemeIcon( QStringLiteral( "mIconFolderLink.svg" ) );
1380 : 0 : QIcon iconFileLink = QgsApplication::getThemeIcon( QStringLiteral( "mIconFileLink.svg" ) );
1381 : :
1382 : 0 : QList<QTreeWidgetItem *> items;
1383 : :
1384 : 0 : QDir dir( path );
1385 : 0 : QStringList entries = dir.entryList( QDir::AllEntries | QDir::NoDotAndDotDot, QDir::Name | QDir::IgnoreCase );
1386 : 0 : const auto constEntries = entries;
1387 : 0 : for ( const QString &name : constEntries )
1388 : : {
1389 : 0 : QFileInfo fi( dir.absoluteFilePath( name ) );
1390 : 0 : QStringList texts;
1391 : 0 : texts << name;
1392 : 0 : QString size;
1393 : 0 : if ( fi.size() > 1024 )
1394 : : {
1395 : 0 : size = QStringLiteral( "%1 KiB" ).arg( QString::number( fi.size() / 1024.0, 'f', 1 ) );
1396 : 0 : }
1397 : 0 : else if ( fi.size() > 1.048576e6 )
1398 : : {
1399 : 0 : size = QStringLiteral( "%1 MiB" ).arg( QString::number( fi.size() / 1.048576e6, 'f', 1 ) );
1400 : 0 : }
1401 : : else
1402 : : {
1403 : 0 : size = QStringLiteral( "%1 B" ).arg( fi.size() );
1404 : : }
1405 : 0 : texts << size;
1406 : 0 : texts << QLocale().toString( fi.lastModified(), QLocale::ShortFormat );
1407 : 0 : QString perm;
1408 : 0 : perm += fi.permission( QFile::ReadOwner ) ? 'r' : '-';
1409 : 0 : perm += fi.permission( QFile::WriteOwner ) ? 'w' : '-';
1410 : 0 : perm += fi.permission( QFile::ExeOwner ) ? 'x' : '-';
1411 : : // QFile::ReadUser, QFile::WriteUser, QFile::ExeUser
1412 : 0 : perm += fi.permission( QFile::ReadGroup ) ? 'r' : '-';
1413 : 0 : perm += fi.permission( QFile::WriteGroup ) ? 'w' : '-';
1414 : 0 : perm += fi.permission( QFile::ExeGroup ) ? 'x' : '-';
1415 : 0 : perm += fi.permission( QFile::ReadOther ) ? 'r' : '-';
1416 : 0 : perm += fi.permission( QFile::WriteOther ) ? 'w' : '-';
1417 : 0 : perm += fi.permission( QFile::ExeOther ) ? 'x' : '-';
1418 : 0 : texts << perm;
1419 : :
1420 : 0 : texts << fi.owner();
1421 : 0 : texts << fi.group();
1422 : :
1423 : 0 : QString type;
1424 : 0 : QIcon icon;
1425 : 0 : if ( fi.isDir() && fi.isSymLink() )
1426 : : {
1427 : 0 : type = tr( "folder" );
1428 : 0 : icon = iconDirLink;
1429 : 0 : }
1430 : 0 : else if ( fi.isDir() )
1431 : : {
1432 : 0 : type = tr( "folder" );
1433 : 0 : icon = iconDirectory;
1434 : 0 : }
1435 : 0 : else if ( fi.isFile() && fi.isSymLink() )
1436 : : {
1437 : 0 : type = tr( "file" );
1438 : 0 : icon = iconFileLink;
1439 : 0 : }
1440 : 0 : else if ( fi.isFile() )
1441 : : {
1442 : 0 : type = tr( "file" );
1443 : 0 : icon = iconFile;
1444 : 0 : }
1445 : :
1446 : 0 : texts << type;
1447 : :
1448 : 0 : QTreeWidgetItem *item = new QTreeWidgetItem( texts );
1449 : 0 : item->setIcon( 0, icon );
1450 : 0 : items << item;
1451 : 0 : }
1452 : :
1453 : 0 : addTopLevelItems( items );
1454 : :
1455 : : // hide columns that are not requested
1456 : 0 : QgsSettings settings;
1457 : 0 : QList<QVariant> lst = settings.value( QStringLiteral( "dataitem/directoryHiddenColumns" ) ).toList();
1458 : 0 : const auto constLst = lst;
1459 : 0 : for ( const QVariant &colVariant : constLst )
1460 : : {
1461 : 0 : setColumnHidden( colVariant.toInt(), true );
1462 : : }
1463 : 0 : }
1464 : :
1465 : 0 : void QgsDirectoryParamWidget::mousePressEvent( QMouseEvent *event )
1466 : : {
1467 : 0 : if ( event->button() == Qt::RightButton )
1468 : : {
1469 : : // show the popup menu
1470 : 0 : QMenu popupMenu;
1471 : :
1472 : 0 : QStringList labels;
1473 : 0 : labels << tr( "Name" ) << tr( "Size" ) << tr( "Date" ) << tr( "Permissions" ) << tr( "Owner" ) << tr( "Group" ) << tr( "Type" );
1474 : 0 : for ( int i = 0; i < labels.count(); i++ )
1475 : : {
1476 : 0 : QAction *action = popupMenu.addAction( labels[i], this, &QgsDirectoryParamWidget::showHideColumn );
1477 : 0 : action->setObjectName( QString::number( i ) );
1478 : 0 : action->setCheckable( true );
1479 : 0 : action->setChecked( !isColumnHidden( i ) );
1480 : 0 : }
1481 : :
1482 : 0 : popupMenu.exec( event->globalPos() );
1483 : 0 : }
1484 : 0 : }
1485 : :
1486 : 0 : void QgsDirectoryParamWidget::showHideColumn()
1487 : : {
1488 : 0 : QAction *action = qobject_cast<QAction *>( sender() );
1489 : 0 : if ( !action )
1490 : 0 : return; // something is wrong
1491 : :
1492 : 0 : int columnIndex = action->objectName().toInt();
1493 : 0 : setColumnHidden( columnIndex, !isColumnHidden( columnIndex ) );
1494 : :
1495 : : // save in settings
1496 : 0 : QgsSettings settings;
1497 : 0 : QList<QVariant> lst;
1498 : 0 : for ( int i = 0; i < columnCount(); i++ )
1499 : : {
1500 : 0 : if ( isColumnHidden( i ) )
1501 : 0 : lst.append( QVariant( i ) );
1502 : 0 : }
1503 : 0 : settings.setValue( QStringLiteral( "dataitem/directoryHiddenColumns" ), lst );
1504 : 0 : }
1505 : :
1506 : 0 : QgsProjectItem::QgsProjectItem( QgsDataItem *parent, const QString &name,
1507 : : const QString &path, const QString &providerKey )
1508 : 0 : : QgsDataItem( QgsDataItem::Project, parent, name, path, providerKey )
1509 : 0 : {
1510 : 0 : mIconName = QStringLiteral( ":/images/icons/qgis_icon.svg" );
1511 : 0 : setToolTip( QDir::toNativeSeparators( path ) );
1512 : 0 : setState( Populated ); // no more children
1513 : 0 : }
1514 : :
1515 : 0 : QgsMimeDataUtils::UriList QgsProjectItem::mimeUris() const
1516 : : {
1517 : 0 : QgsMimeDataUtils::Uri u;
1518 : 0 : u.layerType = QStringLiteral( "project" );
1519 : 0 : u.name = mName;
1520 : 0 : u.uri = mPath;
1521 : 0 : return { u };
1522 : 0 : }
1523 : :
1524 : 0 : QgsErrorItem::QgsErrorItem( QgsDataItem *parent, const QString &error, const QString &path )
1525 : 0 : : QgsDataItem( QgsDataItem::Error, parent, error, path )
1526 : 0 : {
1527 : 0 : mIconName = QStringLiteral( "/mIconDelete.svg" );
1528 : :
1529 : 0 : setState( Populated ); // no more children
1530 : 0 : }
1531 : :
1532 : 0 : QgsFavoritesItem::QgsFavoritesItem( QgsDataItem *parent, const QString &name, const QString &path )
1533 : 0 : : QgsDataCollectionItem( parent, name, QStringLiteral( "favorites:" ), QStringLiteral( "special:Favorites" ) )
1534 : 0 : {
1535 : 0 : Q_UNUSED( path )
1536 : 0 : mCapabilities |= Fast;
1537 : 0 : mType = Favorites;
1538 : 0 : mIconName = QStringLiteral( "/mIconFavorites.svg" );
1539 : 0 : populate();
1540 : 0 : }
1541 : :
1542 : 0 : QVector<QgsDataItem *> QgsFavoritesItem::createChildren()
1543 : : {
1544 : 0 : QVector<QgsDataItem *> children;
1545 : :
1546 : 0 : QgsSettings settings;
1547 : :
1548 : 0 : const QStringList favDirs = settings.value( QStringLiteral( "browser/favourites" ), QVariant() ).toStringList();
1549 : :
1550 : 0 : for ( const QString &favDir : favDirs )
1551 : : {
1552 : 0 : QStringList parts = favDir.split( QStringLiteral( "|||" ) );
1553 : 0 : if ( parts.empty() )
1554 : 0 : continue;
1555 : :
1556 : 0 : QString dir = parts.at( 0 );
1557 : 0 : QString name = dir;
1558 : 0 : if ( parts.count() > 1 )
1559 : 0 : name = parts.at( 1 );
1560 : :
1561 : 0 : children << createChildren( dir, name );
1562 : 0 : }
1563 : :
1564 : 0 : return children;
1565 : 0 : }
1566 : :
1567 : 0 : void QgsFavoritesItem::addDirectory( const QString &favDir, const QString &n )
1568 : : {
1569 : 0 : QString name = n.isEmpty() ? favDir : n;
1570 : :
1571 : 0 : QgsSettings settings;
1572 : 0 : QStringList favDirs = settings.value( QStringLiteral( "browser/favourites" ) ).toStringList();
1573 : 0 : favDirs.append( QStringLiteral( "%1|||%2" ).arg( favDir, name ) );
1574 : 0 : settings.setValue( QStringLiteral( "browser/favourites" ), favDirs );
1575 : :
1576 : 0 : if ( state() == Populated )
1577 : : {
1578 : 0 : QVector<QgsDataItem *> items = createChildren( favDir, name );
1579 : 0 : const auto constItems = items;
1580 : 0 : for ( QgsDataItem *item : constItems )
1581 : : {
1582 : 0 : addChildItem( item, true );
1583 : : }
1584 : 0 : }
1585 : 0 : }
1586 : :
1587 : 0 : void QgsFavoritesItem::removeDirectory( QgsDirectoryItem *item )
1588 : : {
1589 : 0 : if ( !item )
1590 : 0 : return;
1591 : :
1592 : 0 : QgsSettings settings;
1593 : 0 : QStringList favDirs = settings.value( QStringLiteral( "browser/favourites" ) ).toStringList();
1594 : 0 : for ( int i = favDirs.count() - 1; i >= 0; --i )
1595 : : {
1596 : 0 : QStringList parts = favDirs.at( i ).split( QStringLiteral( "|||" ) );
1597 : 0 : if ( parts.empty() )
1598 : 0 : continue;
1599 : :
1600 : 0 : QString dir = parts.at( 0 );
1601 : 0 : if ( dir == item->dirPath() )
1602 : 0 : favDirs.removeAt( i );
1603 : 0 : }
1604 : 0 : settings.setValue( QStringLiteral( "browser/favourites" ), favDirs );
1605 : :
1606 : 0 : int idx = findItem( mChildren, item );
1607 : 0 : if ( idx < 0 )
1608 : : {
1609 : 0 : QgsDebugMsg( QStringLiteral( "favorites item %1 not found" ).arg( item->path() ) );
1610 : 0 : return;
1611 : : }
1612 : :
1613 : 0 : if ( state() == Populated )
1614 : 0 : deleteChildItem( mChildren.at( idx ) );
1615 : 0 : }
1616 : :
1617 : 0 : void QgsFavoritesItem::renameFavorite( const QString &path, const QString &name )
1618 : : {
1619 : : // update stored name
1620 : 0 : QgsSettings settings;
1621 : 0 : QStringList favDirs = settings.value( QStringLiteral( "browser/favourites" ) ).toStringList();
1622 : 0 : for ( int i = 0; i < favDirs.count(); ++i )
1623 : : {
1624 : 0 : QStringList parts = favDirs.at( i ).split( QStringLiteral( "|||" ) );
1625 : 0 : if ( parts.empty() )
1626 : 0 : continue;
1627 : :
1628 : 0 : QString dir = parts.at( 0 );
1629 : 0 : if ( dir == path )
1630 : : {
1631 : 0 : favDirs[i] = QStringLiteral( "%1|||%2" ).arg( path, name );
1632 : 0 : break;
1633 : : }
1634 : 0 : }
1635 : 0 : settings.setValue( QStringLiteral( "browser/favourites" ), favDirs );
1636 : :
1637 : : // also update existing data item
1638 : 0 : const QVector<QgsDataItem *> ch = children();
1639 : 0 : for ( QgsDataItem *child : ch )
1640 : : {
1641 : 0 : if ( QgsFavoriteItem *favorite = qobject_cast< QgsFavoriteItem * >( child ) )
1642 : : {
1643 : 0 : if ( favorite->dirPath() == path )
1644 : : {
1645 : 0 : favorite->setName( name );
1646 : 0 : break;
1647 : : }
1648 : 0 : }
1649 : : }
1650 : 0 : }
1651 : :
1652 : 0 : QVector<QgsDataItem *> QgsFavoritesItem::createChildren( const QString &favDir, const QString &name )
1653 : : {
1654 : 0 : QVector<QgsDataItem *> children;
1655 : 0 : QString pathName = pathComponent( favDir );
1656 : 0 : const auto constProviders = QgsApplication::dataItemProviderRegistry()->providers();
1657 : 0 : for ( QgsDataItemProvider *provider : constProviders )
1658 : : {
1659 : 0 : int capabilities = provider->capabilities();
1660 : :
1661 : 0 : if ( capabilities & QgsDataProvider::Dir )
1662 : : {
1663 : 0 : QgsDataItem *item = provider->createDataItem( favDir, this );
1664 : 0 : if ( item )
1665 : : {
1666 : 0 : item->setName( name );
1667 : 0 : children.append( item );
1668 : 0 : }
1669 : 0 : }
1670 : : }
1671 : 0 : if ( children.isEmpty() )
1672 : : {
1673 : 0 : QgsFavoriteItem *item = new QgsFavoriteItem( this, name, favDir, mPath + '/' + pathName );
1674 : 0 : if ( item )
1675 : : {
1676 : 0 : children.append( item );
1677 : 0 : }
1678 : 0 : }
1679 : 0 : return children;
1680 : 0 : }
1681 : :
1682 : : //-----------------------------------------------------------------------
1683 : 5 : QStringList QgsZipItem::sProviderNames = QStringList();
1684 : :
1685 : :
1686 : 0 : QgsZipItem::QgsZipItem( QgsDataItem *parent, const QString &name, const QString &path )
1687 : 0 : : QgsDataCollectionItem( parent, name, path )
1688 : 0 : {
1689 : 0 : mFilePath = path;
1690 : 0 : init();
1691 : 0 : }
1692 : :
1693 : 0 : QgsZipItem::QgsZipItem( QgsDataItem *parent, const QString &name,
1694 : : const QString &filePath, const QString &path,
1695 : : const QString &providerKey )
1696 : 0 : : QgsDataCollectionItem( parent, name, path, providerKey )
1697 : 0 : , mFilePath( filePath )
1698 : 0 : {
1699 : 0 : init();
1700 : 0 : }
1701 : :
1702 : 0 : void QgsZipItem::init()
1703 : : {
1704 : 0 : mType = Collection; //Zip??
1705 : 0 : mIconName = QStringLiteral( "/mIconZip.svg" );
1706 : 0 : mVsiPrefix = vsiPrefix( mFilePath );
1707 : :
1708 : : static std::once_flag initialized;
1709 : 0 : std::call_once( initialized, [ = ]
1710 : : {
1711 : 0 : sProviderNames << QStringLiteral( "OGR" ) << QStringLiteral( "GDAL" );
1712 : 0 : } );
1713 : 0 : }
1714 : :
1715 : 0 : QVector<QgsDataItem *> QgsZipItem::createChildren()
1716 : : {
1717 : 0 : QVector<QgsDataItem *> children;
1718 : 0 : QString tmpPath;
1719 : 0 : QgsSettings settings;
1720 : 0 : QString scanZipSetting = settings.value( QStringLiteral( "qgis/scanZipInBrowser2" ), "basic" ).toString();
1721 : :
1722 : 0 : mZipFileList.clear();
1723 : :
1724 : 0 : QgsDebugMsgLevel( QStringLiteral( "mFilePath = %1 path = %2 name= %3 scanZipSetting= %4 vsiPrefix= %5" ).arg( mFilePath, path(), name(), scanZipSetting, mVsiPrefix ), 3 );
1725 : :
1726 : : // if scanZipBrowser == no: skip to the next file
1727 : 0 : if ( scanZipSetting == QLatin1String( "no" ) )
1728 : : {
1729 : 0 : return children;
1730 : : }
1731 : :
1732 : : // first get list of files
1733 : 0 : getZipFileList();
1734 : :
1735 : 0 : const QList<QgsDataItemProvider *> providers = QgsApplication::dataItemProviderRegistry()->providers();
1736 : :
1737 : : // loop over files inside zip
1738 : 0 : const auto constMZipFileList = mZipFileList;
1739 : 0 : for ( const QString &fileName : constMZipFileList )
1740 : : {
1741 : 0 : QFileInfo info( fileName );
1742 : 0 : tmpPath = mVsiPrefix + mFilePath + '/' + fileName;
1743 : 0 : QgsDebugMsgLevel( "tmpPath = " + tmpPath, 3 );
1744 : :
1745 : 0 : for ( QgsDataItemProvider *provider : providers )
1746 : : {
1747 : 0 : if ( !sProviderNames.contains( provider->name() ) )
1748 : 0 : continue;
1749 : :
1750 : : // ugly hack to remove .dbf file if there is a .shp file
1751 : 0 : if ( provider->name() == QLatin1String( "OGR" ) )
1752 : : {
1753 : 0 : if ( info.suffix().compare( QLatin1String( "dbf" ), Qt::CaseInsensitive ) == 0 )
1754 : : {
1755 : 0 : if ( mZipFileList.indexOf( fileName.left( fileName.count() - 4 ) + ".shp" ) != -1 )
1756 : 0 : continue;
1757 : 0 : }
1758 : 0 : if ( info.completeSuffix().compare( QLatin1String( "shp.xml" ), Qt::CaseInsensitive ) == 0 )
1759 : : {
1760 : 0 : continue;
1761 : : }
1762 : 0 : }
1763 : :
1764 : 0 : QgsDebugMsgLevel( QStringLiteral( "trying to load item %1 with %2" ).arg( tmpPath, provider->name() ), 3 );
1765 : 0 : QgsDataItem *item = provider->createDataItem( tmpPath, this );
1766 : 0 : if ( item )
1767 : : {
1768 : : // the item comes with zipped file name, set the name to relative path within zip file
1769 : 0 : item->setName( fileName );
1770 : 0 : children.append( item );
1771 : 0 : }
1772 : : else
1773 : : {
1774 : 0 : QgsDebugMsgLevel( QStringLiteral( "not loaded item" ), 3 );
1775 : : }
1776 : : }
1777 : 0 : }
1778 : :
1779 : 0 : return children;
1780 : 0 : }
1781 : :
1782 : 0 : QgsDataItem *QgsZipItem::itemFromPath( QgsDataItem *parent, const QString &path, const QString &name )
1783 : : {
1784 : 0 : return itemFromPath( parent, path, name, path );
1785 : : }
1786 : :
1787 : 0 : QgsDataItem *QgsZipItem::itemFromPath( QgsDataItem *parent, const QString &filePath, const QString &name, const QString &path )
1788 : : {
1789 : 0 : QgsSettings settings;
1790 : 0 : QString scanZipSetting = settings.value( QStringLiteral( "qgis/scanZipInBrowser2" ), "basic" ).toString();
1791 : 0 : QStringList zipFileList;
1792 : 0 : QString vsiPrefix = QgsZipItem::vsiPrefix( filePath );
1793 : 0 : QgsZipItem *zipItem = nullptr;
1794 : 0 : bool populated = false;
1795 : :
1796 : 0 : QgsDebugMsgLevel( QStringLiteral( "path = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( path, name, scanZipSetting, vsiPrefix ), 3 );
1797 : :
1798 : : // don't scan if scanZipBrowser == no
1799 : 0 : if ( scanZipSetting == QLatin1String( "no" ) )
1800 : 0 : return nullptr;
1801 : :
1802 : : // don't scan if this file is not a /vsizip/ or /vsitar/ item
1803 : 0 : if ( ( vsiPrefix != QLatin1String( "/vsizip/" ) && vsiPrefix != QLatin1String( "/vsitar/" ) ) )
1804 : 0 : return nullptr;
1805 : :
1806 : 0 : zipItem = new QgsZipItem( parent, name, filePath, path );
1807 : :
1808 : 0 : if ( zipItem )
1809 : : {
1810 : : // force populate zipItem if it has less than 10 items and is not a .tgz or .tar.gz file (slow loading)
1811 : : // for other items populating will be delayed until item is opened
1812 : : // this might be polluting the tree with empty items but is necessary for performance reasons
1813 : : // could also accept all files smaller than a certain size and add options for file count and/or size
1814 : :
1815 : : // first get list of files inside .zip or .tar files
1816 : 0 : if ( path.endsWith( QLatin1String( ".zip" ), Qt::CaseInsensitive ) ||
1817 : 0 : path.endsWith( QLatin1String( ".tar" ), Qt::CaseInsensitive ) )
1818 : : {
1819 : 0 : zipFileList = zipItem->getZipFileList();
1820 : 0 : }
1821 : : // force populate if less than 10 items
1822 : 0 : if ( !zipFileList.isEmpty() && zipFileList.count() <= 10 )
1823 : : {
1824 : 0 : zipItem->populate( zipItem->createChildren() );
1825 : 0 : populated = true; // there is no QgsDataItem::isPopulated() function
1826 : 0 : QgsDebugMsgLevel( QStringLiteral( "Got zipItem with %1 children, path=%2, name=%3" ).arg( zipItem->rowCount() ).arg( zipItem->path(), zipItem->name() ), 3 );
1827 : 0 : }
1828 : : else
1829 : : {
1830 : 0 : QgsDebugMsgLevel( QStringLiteral( "Delaying populating zipItem with path=%1, name=%2" ).arg( zipItem->path(), zipItem->name() ), 3 );
1831 : : }
1832 : 0 : }
1833 : :
1834 : : // only display if has children or if is not populated
1835 : 0 : if ( zipItem && ( !populated || zipItem->rowCount() > 0 ) )
1836 : : {
1837 : 0 : QgsDebugMsgLevel( QStringLiteral( "returning zipItem" ), 3 );
1838 : 0 : return zipItem;
1839 : : }
1840 : :
1841 : 0 : return nullptr;
1842 : 0 : }
1843 : :
1844 : 0 : QStringList QgsZipItem::getZipFileList()
1845 : : {
1846 : 0 : if ( ! mZipFileList.isEmpty() )
1847 : 0 : return mZipFileList;
1848 : :
1849 : 0 : QString tmpPath;
1850 : 0 : QgsSettings settings;
1851 : 0 : QString scanZipSetting = settings.value( QStringLiteral( "qgis/scanZipInBrowser2" ), "basic" ).toString();
1852 : :
1853 : 0 : QgsDebugMsgLevel( QStringLiteral( "mFilePath = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( mFilePath, name(), scanZipSetting, mVsiPrefix ), 3 );
1854 : :
1855 : : // if scanZipBrowser == no: skip to the next file
1856 : 0 : if ( scanZipSetting == QLatin1String( "no" ) )
1857 : : {
1858 : 0 : return mZipFileList;
1859 : : }
1860 : :
1861 : : // get list of files inside zip file
1862 : 0 : QgsDebugMsgLevel( QStringLiteral( "Open file %1 with gdal vsi" ).arg( mVsiPrefix + mFilePath ), 3 );
1863 : 0 : char **papszSiblingFiles = VSIReadDirRecursive( QString( mVsiPrefix + mFilePath ).toLocal8Bit().constData() );
1864 : 0 : if ( papszSiblingFiles )
1865 : : {
1866 : 0 : for ( int i = 0; papszSiblingFiles[i]; i++ )
1867 : : {
1868 : 0 : tmpPath = papszSiblingFiles[i];
1869 : 0 : QgsDebugMsgLevel( QStringLiteral( "Read file %1" ).arg( tmpPath ), 3 );
1870 : : // skip directories (files ending with /)
1871 : 0 : if ( tmpPath.right( 1 ) != QLatin1String( "/" ) )
1872 : 0 : mZipFileList << tmpPath;
1873 : 0 : }
1874 : 0 : CSLDestroy( papszSiblingFiles );
1875 : 0 : }
1876 : : else
1877 : : {
1878 : 0 : QgsDebugMsg( QStringLiteral( "Error reading %1" ).arg( mFilePath ) );
1879 : : }
1880 : :
1881 : 0 : return mZipFileList;
1882 : 0 : }
1883 : :
1884 : :
1885 : 0 : QgsDatabaseSchemaItem::QgsDatabaseSchemaItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &providerKey )
1886 : 0 : : QgsDataCollectionItem( parent, name, path, providerKey )
1887 : 0 : {
1888 : :
1889 : 0 : }
1890 : :
1891 : 0 : QgsDatabaseSchemaItem::~QgsDatabaseSchemaItem()
1892 : 0 : {
1893 : :
1894 : 0 : }
1895 : :
1896 : 0 : QIcon QgsDatabaseSchemaItem::iconDataCollection()
1897 : : {
1898 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "/mIconDbSchema.svg" ) );
1899 : 0 : }
1900 : :
1901 : 0 : QgsAbstractDatabaseProviderConnection *QgsDatabaseSchemaItem::databaseConnection() const
1902 : : {
1903 : 0 : const QString dataProviderKey { QgsApplication::dataItemProviderRegistry()->dataProviderKey( providerKey() ) };
1904 : 0 : QgsProviderMetadata *md { QgsProviderRegistry::instance()->providerMetadata( dataProviderKey ) };
1905 : 0 : if ( ! md )
1906 : : {
1907 : 0 : return nullptr;
1908 : : }
1909 : 0 : const QString connectionName { parent()->name() };
1910 : : try
1911 : : {
1912 : 0 : return static_cast<QgsAbstractDatabaseProviderConnection *>( md->createConnection( connectionName ) );
1913 : 0 : }
1914 : : catch ( QgsProviderConnectionException &ex )
1915 : : {
1916 : : // This is expected and it is not an error in case the provider does not implement
1917 : : // the connections API
1918 : 0 : }
1919 : 0 : return nullptr;
1920 : 0 : }
1921 : :
1922 : :
1923 : 0 : QgsConnectionsRootItem::QgsConnectionsRootItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &providerKey )
1924 : 0 : : QgsDataCollectionItem( parent, name, path, providerKey )
1925 : 0 : {
1926 : 0 : }
1927 : :
1928 : :
1929 : : ///@cond PRIVATE
1930 : :
1931 : 0 : QgsProjectHomeItem::QgsProjectHomeItem( QgsDataItem *parent, const QString &name, const QString &dirPath, const QString &path )
1932 : 0 : : QgsDirectoryItem( parent, name, dirPath, path, QStringLiteral( "special:ProjectHome" ) )
1933 : 0 : {
1934 : 0 : }
1935 : :
1936 : 0 : QIcon QgsProjectHomeItem::icon()
1937 : : {
1938 : 0 : if ( state() == Populating )
1939 : 0 : return QgsDirectoryItem::icon();
1940 : 0 : return QgsApplication::getThemeIcon( QStringLiteral( "mIconFolderProject.svg" ) );
1941 : 0 : }
1942 : :
1943 : 0 : QVariant QgsProjectHomeItem::sortKey() const
1944 : : {
1945 : 0 : return QStringLiteral( " 1" );
1946 : 0 : }
1947 : :
1948 : :
1949 : 0 : QgsFavoriteItem::QgsFavoriteItem( QgsFavoritesItem *parent, const QString &name, const QString &dirPath, const QString &path )
1950 : 0 : : QgsDirectoryItem( parent, name, dirPath, path, QStringLiteral( "special:Favorites" ) )
1951 : 0 : , mFavorites( parent )
1952 : 0 : {
1953 : 0 : mCapabilities |= Rename;
1954 : 0 : }
1955 : :
1956 : 0 : bool QgsFavoriteItem::rename( const QString &name )
1957 : : {
1958 : 0 : mFavorites->renameFavorite( dirPath(), name );
1959 : 0 : return true;
1960 : 0 : }
1961 : :
1962 : :
1963 : : ///@endcond
|