18 #include <QApplication>
19 #include <QtConcurrentMap>
20 #include <QtConcurrentRun>
22 #include <QElapsedTimer>
26 #include <QMouseEvent>
27 #include <QTreeWidget>
28 #include <QTreeWidgetItem>
41 #include "qgsconfig.h"
48 #define CPL_SUPRESS_CPLUSPLUS //#spellok
50 #include "cpl_string.h"
120 return QStringLiteral(
" 0" );
133 , mCapabilities( NoCapabilities )
135 , mState( NotPopulated )
137 , mProviderKey( providerKey )
139 , mDeferredDelete( false )
140 , mFutureWatcher( nullptr )
152 child->deleteLater();
156 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
159 QgsDebugMsg( QStringLiteral(
"mFutureWatcher not finished (should not happen) -> waitForFinished()" ) );
160 mDeferredDelete =
true;
161 mFutureWatcher->waitForFinished();
164 delete mFutureWatcher;
169 return QString(
string ).replace( QRegExp(
"[\\\\/]" ), QStringLiteral(
"|" ) );
191 child->deleteLater();
195 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
197 QgsDebugMsg( QStringLiteral(
"mFutureWatcher not finished -> schedule to delete later" ) );
198 mDeferredDelete =
true;
202 QObject::deleteLater();
208 const auto constItems = items;
227 child->QObject::setParent(
nullptr );
228 child->moveToThread( targetThread );
230 QObject::moveToThread( targetThread );
236 return sPopulatingIcon->
icon();
238 if ( !
mIcon.isNull() )
257 return QVector<QgsDataItem *>();
275 if ( !mFutureWatcher )
277 mFutureWatcher =
new QFutureWatcher< QVector <QgsDataItem *> >( this );
281 mFutureWatcher->setFuture( QtConcurrent::run( runCreateChildren,
this ) );
286 QVector<QgsDataItem *> QgsDataItem::runCreateChildren(
QgsDataItem *item )
292 QgsDebugMsgLevel( QStringLiteral(
"%1 children created in %2 ms" ).arg(
children.size() ).arg( time.elapsed() ), 3 );
294 const auto constChildren =
children;
301 child->moveToThread( qApp->thread() );
309 QgsDebugMsgLevel( QStringLiteral(
"path = %1 children.size() = %2" ).arg(
path() ).arg( mFutureWatcher->result().size() ), 3 );
313 QgsDebugMsg( QStringLiteral(
"Item was scheduled to be deleted later" ) );
314 QObject::deleteLater();
320 populate( mFutureWatcher->result() );
324 refresh( mFutureWatcher->result() );
339 const auto constChildren =
children;
378 if ( !mFutureWatcher )
380 mFutureWatcher =
new QFutureWatcher< QVector <QgsDataItem *> >( this );
383 mFutureWatcher->setFuture( QtConcurrent::run( runCreateChildren,
this ) );
407 QVector<QgsDataItem *> remove;
415 remove.append( child );
417 const auto constRemove = remove;
425 const auto constChildren =
children;
438 mChildren.value( index )->refresh( child->children() );
441 child->deleteLater();
477 disconnect(
this,
nullptr,
mParent,
nullptr );
512 if (
mChildren.at( i )->mName.localeAwareCompare( child->
mName ) >= 0 )
557 for (
int i = 0; i < items.size(); i++ )
559 Q_ASSERT_X( items[i],
"findItem", QStringLiteral(
"item %1 is nullptr" ).arg( i ).toLatin1() );
561 if ( items[i]->
equal( item ) )
569 return ( metaObject()->className() == other->metaObject()->className() &&
576 return QList<QAction *>();
604 if ( !sPopulatingIcon )
628 return QList<QMenu *>();
634 const QString &uri,
LayerType layerType,
const QString &providerKey )
635 :
QgsDataItem( Layer, parent, name, path, providerKey )
637 , mLayerType( layerType )
674 switch ( layer->
type() )
678 switch ( qobject_cast< QgsVectorLayer * >( layer )->geometryType() )
713 static int enumIdx = staticMetaObject.indexOfEnumerator(
"LayerType" );
714 return staticMetaObject.enumerator( enumIdx ).valueToKey( layerType );
722 return QStringLiteral(
"/mIconPointLayer.svg" );
724 return QStringLiteral(
"/mIconLineLayer.svg" );
726 return QStringLiteral(
"/mIconPolygonLayer.svg" );
729 return QStringLiteral(
"/mIconVector.svg" );
732 return QStringLiteral(
"/mIconTableLayer.svg" );
734 return QStringLiteral(
"/mIconRaster.svg" );
736 return QStringLiteral(
"/mIconMeshLayer.svg" );
738 return QStringLiteral(
"/mIconLayer.png" );
755 const QgsLayerItem *o = qobject_cast<const QgsLayerItem *>( other );
769 u.
layerType = QStringLiteral(
"vector" );
797 u.
layerType = QStringLiteral(
"raster" );
803 u.
layerType = QStringLiteral(
"vector-tile" );
806 u.
layerType = QStringLiteral(
"plugin" );
822 const QString &providerKey )
823 :
QgsDataItem( Collection, parent, name, path, providerKey )
826 mIconName = QStringLiteral(
"/mIconDbSchema.svg" );
838 QgsDebugMsgLevel( QStringLiteral(
"delete child = 0x%0" ).arg(
static_cast<qlonglong
>( i ), 8, 16, QLatin1Char(
'0' ) ), 2 );
849 , mRefreshLater( false )
856 const QString &dirPath,
const QString &path,
857 const QString &providerKey )
859 , mDirPath( dirPath )
860 , mRefreshLater( false )
882 if ( fi.isDir() && fi.isSymLink() )
903 QStringList entries = dir.entryList( QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name | QDir::IgnoreCase );
904 const auto constEntries = entries;
905 for (
const QString &subdir : constEntries )
913 QString subdirPath = dir.absoluteFilePath( subdir );
915 QgsDebugMsgLevel( QStringLiteral(
"creating subdir: %1" ).arg( subdirPath ), 2 );
921 bool handledByProvider =
false;
924 if ( provider->handlesDirectoryPath(
path ) )
926 handledByProvider =
true;
930 if ( handledByProvider )
936 item->
setSortKey( QStringLiteral(
" %1" ).arg( subdir ) );
943 QStringList fileEntries = dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot | QDir::Files, QDir::Name );
944 const auto constFileEntries = fileEntries;
945 for (
const QString &
name : constFileEntries )
953 QString
path = dir.absoluteFilePath(
name );
954 QFileInfo fileInfo(
path );
956 if ( fileInfo.suffix().compare( QLatin1String(
"zip" ), Qt::CaseInsensitive ) == 0 ||
957 fileInfo.suffix().compare( QLatin1String(
"tar" ), Qt::CaseInsensitive ) == 0 )
967 bool createdItem =
false;
970 int capabilities = provider->capabilities();
990 if ( fileInfo.suffix().compare( QLatin1String(
"qgs" ), Qt::CaseInsensitive ) == 0 ||
991 fileInfo.suffix().compare( QLatin1String(
"qgz" ), Qt::CaseInsensitive ) == 0 )
1009 if ( !mFileSystemWatcher )
1011 mFileSystemWatcher =
new QFileSystemWatcher(
this );
1012 mFileSystemWatcher->addPath(
mDirPath );
1015 mLastScan = QDateTime::currentDateTime();
1019 if ( mFileSystemWatcher )
1021 delete mFileSystemWatcher;
1022 mFileSystemWatcher =
nullptr;
1030 if ( mLastScan.msecsTo( QDateTime::currentDateTime() ) <
QgsSettings().value( QStringLiteral(
"browser/minscaninterval" ), 10000 ).toInt() )
1037 mRefreshLater =
true;
1051 QTimer::singleShot( 100,
this, [ = ] {
refresh(); } );
1058 QStringList hiddenItems = settings.
value( QStringLiteral(
"browser/hiddenPaths" ),
1059 QStringList() ).toStringList();
1060 int idx = hiddenItems.indexOf(
path );
1061 return ( idx > -1 );
1066 QgsDebugMsgLevel( QStringLiteral(
"mRefreshLater = %1" ).arg( mRefreshLater ), 3 );
1068 if ( mRefreshLater )
1070 QgsDebugMsgLevel( QStringLiteral(
"directory changed during createChidren() -> refresh() again" ), 3 );
1071 mRefreshLater =
false;
1090 return (
path() == other->
path() );
1101 u.
layerType = QStringLiteral(
"directory" );
1108 : QTreeWidget( parent )
1110 setRootIsDecorated(
false );
1113 setColumnCount( 7 );
1115 labels << tr(
"Name" ) << tr(
"Size" ) << tr(
"Date" ) << tr(
"Permissions" ) << tr(
"Owner" ) << tr(
"Group" ) << tr(
"Type" );
1116 setHeaderLabels( labels );
1123 QList<QTreeWidgetItem *> items;
1126 QStringList entries = dir.entryList( QDir::AllEntries | QDir::NoDotAndDotDot, QDir::Name | QDir::IgnoreCase );
1127 const auto constEntries = entries;
1128 for (
const QString &name : constEntries )
1130 QFileInfo fi( dir.absoluteFilePath( name ) );
1134 if ( fi.size() > 1024 )
1136 size = QStringLiteral(
"%1 KiB" ).arg( QString::number( fi.size() / 1024.0,
'f', 1 ) );
1138 else if ( fi.size() > 1.048576e6 )
1140 size = QStringLiteral(
"%1 MiB" ).arg( QString::number( fi.size() / 1.048576e6,
'f', 1 ) );
1144 size = QStringLiteral(
"%1 B" ).arg( fi.size() );
1147 texts << fi.lastModified().toString( Qt::SystemLocaleShortDate );
1149 perm += fi.permission( QFile::ReadOwner ) ?
'r' :
'-';
1150 perm += fi.permission( QFile::WriteOwner ) ?
'w' :
'-';
1151 perm += fi.permission( QFile::ExeOwner ) ?
'x' :
'-';
1153 perm += fi.permission( QFile::ReadGroup ) ?
'r' :
'-';
1154 perm += fi.permission( QFile::WriteGroup ) ?
'w' :
'-';
1155 perm += fi.permission( QFile::ExeGroup ) ?
'x' :
'-';
1156 perm += fi.permission( QFile::ReadOther ) ?
'r' :
'-';
1157 perm += fi.permission( QFile::WriteOther ) ?
'w' :
'-';
1158 perm += fi.permission( QFile::ExeOther ) ?
'x' :
'-';
1161 texts << fi.owner();
1162 texts << fi.group();
1166 if ( fi.isDir() && fi.isSymLink() )
1168 type = tr(
"folder" );
1171 else if ( fi.isDir() )
1173 type = tr(
"folder" );
1174 icon = iconDirectory;
1176 else if ( fi.isFile() && fi.isSymLink() )
1178 type = tr(
"file" );
1179 icon = iconFileLink;
1181 else if ( fi.isFile() )
1183 type = tr(
"file" );
1189 QTreeWidgetItem *item =
new QTreeWidgetItem( texts );
1190 item->setIcon( 0, icon );
1194 addTopLevelItems( items );
1198 QList<QVariant> lst = settings.
value( QStringLiteral(
"dataitem/directoryHiddenColumns" ) ).toList();
1199 const auto constLst = lst;
1200 for (
const QVariant &colVariant : constLst )
1202 setColumnHidden( colVariant.toInt(),
true );
1208 if ( event->button() == Qt::RightButton )
1214 labels << tr(
"Name" ) << tr(
"Size" ) << tr(
"Date" ) << tr(
"Permissions" ) << tr(
"Owner" ) << tr(
"Group" ) << tr(
"Type" );
1215 for (
int i = 0; i < labels.count(); i++ )
1218 action->setObjectName( QString::number( i ) );
1219 action->setCheckable(
true );
1220 action->setChecked( !isColumnHidden( i ) );
1223 popupMenu.exec( event->globalPos() );
1229 QAction *action = qobject_cast<QAction *>( sender() );
1233 int columnIndex = action->objectName().toInt();
1234 setColumnHidden( columnIndex, !isColumnHidden( columnIndex ) );
1238 QList<QVariant> lst;
1239 for (
int i = 0; i < columnCount(); i++ )
1241 if ( isColumnHidden( i ) )
1242 lst.append( QVariant( i ) );
1244 settings.
setValue( QStringLiteral(
"dataitem/directoryHiddenColumns" ), lst );
1248 const QString &path,
const QString &providerKey )
1251 mIconName = QStringLiteral(
":/images/icons/qgis_icon.svg" );
1259 u.
layerType = QStringLiteral(
"project" );
1268 mIconName = QStringLiteral(
"/mIconDelete.svg" );
1274 :
QgsDataCollectionItem( parent, name, QStringLiteral(
"favorites:" ), QStringLiteral(
"special:Favorites" ) )
1279 mIconName = QStringLiteral(
"/mIconFavourites.svg" );
1288 const QStringList favDirs = settings.
value( QStringLiteral(
"browser/favourites" ), QVariant() ).toStringList();
1290 for (
const QString &favDir : favDirs )
1292 QStringList parts = favDir.split( QStringLiteral(
"|||" ) );
1293 if ( parts.empty() )
1296 QString dir = parts.at( 0 );
1298 if ( parts.count() > 1 )
1299 name = parts.at( 1 );
1309 QString
name = n.isEmpty() ? favDir : n;
1312 QStringList favDirs = settings.
value( QStringLiteral(
"browser/favourites" ) ).toStringList();
1313 favDirs.append( QStringLiteral(
"%1|||%2" ).arg( favDir,
name ) );
1314 settings.
setValue( QStringLiteral(
"browser/favourites" ), favDirs );
1319 const auto constItems = items;
1333 QStringList favDirs = settings.
value( QStringLiteral(
"browser/favourites" ) ).toStringList();
1334 for (
int i = favDirs.count() - 1; i >= 0; --i )
1336 QStringList parts = favDirs.at( i ).split( QStringLiteral(
"|||" ) );
1337 if ( parts.empty() )
1340 QString dir = parts.at( 0 );
1342 favDirs.removeAt( i );
1344 settings.
setValue( QStringLiteral(
"browser/favourites" ), favDirs );
1349 QgsDebugMsg( QStringLiteral(
"favorites item %1 not found" ).arg( item->
path() ) );
1361 QStringList favDirs = settings.
value( QStringLiteral(
"browser/favourites" ) ).toStringList();
1362 for (
int i = 0; i < favDirs.count(); ++i )
1364 QStringList parts = favDirs.at( i ).split( QStringLiteral(
"|||" ) );
1365 if ( parts.empty() )
1368 QString dir = parts.at( 0 );
1371 favDirs[i] = QStringLiteral(
"%1|||%2" ).arg(
path,
name );
1375 settings.
setValue( QStringLiteral(
"browser/favourites" ), favDirs );
1378 const QVector<QgsDataItem *> ch =
children();
1381 if ( QgsFavoriteItem *favorite = qobject_cast< QgsFavoriteItem * >( child ) )
1383 if ( favorite->dirPath() ==
path )
1385 favorite->setName(
name );
1399 int capabilities = provider->capabilities();
1403 QgsDataItem *item = provider->createDataItem( favDir,
this );
1413 QgsFavoriteItem *item =
new QgsFavoriteItem(
this,
name, favDir,
mPath +
'/' + pathName );
1434 const QString &filePath,
const QString &path,
1435 const QString &providerKey )
1437 , mFilePath( filePath )
1442 void QgsZipItem::init()
1445 mIconName = QStringLiteral(
"/mIconZip.svg" );
1448 static std::once_flag initialized;
1449 std::call_once( initialized, [ = ]
1451 sProviderNames << QStringLiteral(
"OGR" ) << QStringLiteral(
"GDAL" );
1460 QString scanZipSetting = settings.
value( QStringLiteral(
"qgis/scanZipInBrowser2" ),
"basic" ).toString();
1467 if ( scanZipSetting == QLatin1String(
"no" ) )
1479 for (
const QString &fileName : constMZipFileList )
1481 QFileInfo info( fileName );
1491 if ( provider->name() == QStringLiteral(
"OGR" ) )
1493 if ( info.suffix().compare( QLatin1String(
"dbf" ), Qt::CaseInsensitive ) == 0 )
1495 if (
mZipFileList.indexOf( fileName.left( fileName.count() - 4 ) +
".shp" ) != -1 )
1498 if ( info.completeSuffix().compare( QLatin1String(
"shp.xml" ), Qt::CaseInsensitive ) == 0 )
1504 QgsDebugMsgLevel( QStringLiteral(
"trying to load item %1 with %2" ).arg( tmpPath, provider->name() ), 3 );
1505 QgsDataItem *item = provider->createDataItem( tmpPath,
this );
1530 QString scanZipSetting = settings.
value( QStringLiteral(
"qgis/scanZipInBrowser2" ),
"basic" ).toString();
1531 QStringList zipFileList;
1534 bool populated =
false;
1539 if ( scanZipSetting == QLatin1String(
"no" ) )
1543 if ( (
vsiPrefix != QLatin1String(
"/vsizip/" ) &&
vsiPrefix != QLatin1String(
"/vsitar/" ) ) )
1556 if (
path.endsWith( QLatin1String(
".zip" ), Qt::CaseInsensitive ) ||
1557 path.endsWith( QLatin1String(
".tar" ), Qt::CaseInsensitive ) )
1562 if ( !zipFileList.isEmpty() && zipFileList.count() <= 10 )
1566 QgsDebugMsgLevel( QStringLiteral(
"Got zipItem with %1 children, path=%2, name=%3" ).arg( zipItem->
rowCount() ).arg( zipItem->
path(), zipItem->
name() ), 3 );
1570 QgsDebugMsgLevel( QStringLiteral(
"Delaying populating zipItem with path=%1, name=%2" ).arg( zipItem->
path(), zipItem->
name() ), 3 );
1575 if ( zipItem && ( !populated || zipItem->
rowCount() > 0 ) )
1591 QString scanZipSetting = settings.
value( QStringLiteral(
"qgis/scanZipInBrowser2" ),
"basic" ).toString();
1596 if ( scanZipSetting == QLatin1String(
"no" ) )
1603 char **papszSiblingFiles = VSIReadDirRecursive( QString(
mVsiPrefix +
mFilePath ).toLocal8Bit().constData() );
1604 if ( papszSiblingFiles )
1606 for (
int i = 0; papszSiblingFiles[i]; i++ )
1608 tmpPath = papszSiblingFiles[i];
1611 if ( tmpPath.right( 1 ) != QLatin1String(
"/" ) )
1614 CSLDestroy( papszSiblingFiles );
1626 QgsProjectHomeItem::QgsProjectHomeItem(
QgsDataItem *parent,
const QString &name,
const QString &dirPath,
const QString &path )
1627 :
QgsDirectoryItem( parent, name, dirPath, path, QStringLiteral(
"special:ProjectHome" ) )
1631 QIcon QgsProjectHomeItem::icon()
1633 if ( state() == Populating )
1638 QVariant QgsProjectHomeItem::sortKey()
const
1640 return QStringLiteral(
" 1" );
1644 QgsFavoriteItem::QgsFavoriteItem(
QgsFavoritesItem *parent,
const QString &name,
const QString &dirPath,
const QString &path )
1645 :
QgsDirectoryItem( parent, name, dirPath, path, QStringLiteral(
"special:Favorites" ) )
1646 , mFavorites( parent )
1651 bool QgsFavoriteItem::rename(
const QString &name )
1653 mFavorites->renameFavorite( dirPath(), name );