QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
qgsdataitem.cpp
Go to the documentation of this file.
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 <QDir>
23 #include <QFileInfo>
24 #include <QMenu>
25 #include <QMouseEvent>
26 #include <QTreeWidget>
27 #include <QTreeWidgetItem>
28 #include <QVector>
29 #include <QStyle>
30 #include <mutex>
31 
32 #include "qgis.h"
33 #include "qgsdataitem.h"
34 #include "qgsapplication.h"
35 #include "qgsdataitemprovider.h"
37 #include "qgsdataprovider.h"
38 #include "qgslogger.h"
39 #include "qgsproviderregistry.h"
40 #include "qgsconfig.h"
41 #include "qgssettings.h"
42 #include "qgsanimatedicon.h"
43 #include "qgsproject.h"
44 #include "qgsvectorlayer.h"
45 
46 // use GDAL VSI mechanism
47 #define CPL_SUPRESS_CPLUSPLUS //#spellok
48 #include "cpl_vsi.h"
49 #include "cpl_string.h"
50 
51 // shared icons
53 {
54  return QgsApplication::getThemeIcon( QStringLiteral( "/mIconPointLayer.svg" ) );
55 }
56 
58 {
59  return QgsApplication::getThemeIcon( QStringLiteral( "/mIconLineLayer.svg" ) );
60 }
61 
63 {
64  return QgsApplication::getThemeIcon( QStringLiteral( "/mIconPolygonLayer.svg" ) );
65 }
66 
68 {
69  return QgsApplication::getThemeIcon( QStringLiteral( "/mIconTableLayer.svg" ) );
70 }
71 
73 {
74  return QgsApplication::getThemeIcon( QStringLiteral( "/mIconRaster.svg" ) );
75 }
76 
78 {
79  return QgsApplication::getThemeIcon( QStringLiteral( "/mIconMeshLayer.svg" ) );
80 }
81 
83 {
84  return QgsApplication::getThemeIcon( QStringLiteral( "/mIconLayer.png" ) );
85 }
86 
88 {
89  return QgsApplication::getThemeIcon( QStringLiteral( "/mIconDbSchema.svg" ) );
90 }
91 
93 {
94  return QgsApplication::getThemeIcon( QStringLiteral( "/mIconFolderOpen.svg" ) );
95 }
96 
98 {
99  return QgsApplication::getThemeIcon( QStringLiteral( "mIconFolderHome.svg" ) );
100 }
101 
103 {
104  return QgsApplication::getThemeIcon( QStringLiteral( "/mIconFolder.svg" ) );
105 }
106 
108 {
109  return QgsApplication::getThemeIcon( QStringLiteral( "/mIconFavourites.svg" ) );
110 }
111 
113 {
114  return QStringLiteral( " 0" );
115 }
116 
118 {
119  return QgsApplication::getThemeIcon( QStringLiteral( "/mIconZip.svg" ) );
120 }
121 
122 QgsAnimatedIcon *QgsDataItem::sPopulatingIcon = nullptr;
123 
125 // Do not pass parent to QObject, Qt would delete this when parent is deleted
126  : mType( type )
128  , mParent( parent )
129  , mState( NotPopulated )
130  , mName( name )
131  , mPath( path )
132  , mDeferredDelete( false )
133  , mFutureWatcher( nullptr )
134 {
135 }
136 
138 {
139  QgsDebugMsgLevel( QStringLiteral( "mName = %1 mPath = %2 mChildren.size() = %3" ).arg( mName, mPath ).arg( mChildren.size() ), 2 );
140  const auto constMChildren = mChildren;
141  for ( QgsDataItem *child : constMChildren )
142  {
143  if ( !child ) // should not happen
144  continue;
145  child->deleteLater();
146  }
147  mChildren.clear();
148 
149  if ( mFutureWatcher && !mFutureWatcher->isFinished() )
150  {
151  // this should not usually happen (until the item was deleted directly when createChildren was running)
152  QgsDebugMsg( QStringLiteral( "mFutureWatcher not finished (should not happen) -> waitForFinished()" ) );
153  mDeferredDelete = true;
154  mFutureWatcher->waitForFinished();
155  }
156 
157  delete mFutureWatcher;
158 }
159 
160 QString QgsDataItem::pathComponent( const QString &string )
161 {
162  return QString( string ).replace( QRegExp( "[\\\\/]" ), QStringLiteral( "|" ) );
163 }
164 
165 QVariant QgsDataItem::sortKey() const
166 {
167  return mSortKey.isValid() ? mSortKey : name();
168 }
169 
170 void QgsDataItem::setSortKey( const QVariant &key )
171 {
172  mSortKey = key;
173 }
174 
176 {
177  QgsDebugMsgLevel( "path = " + path(), 3 );
178  setParent( nullptr ); // also disconnects parent
179  const auto constMChildren = mChildren;
180  for ( QgsDataItem *child : constMChildren )
181  {
182  if ( !child ) // should not happen
183  continue;
184  child->deleteLater();
185  }
186  mChildren.clear();
187 
188  if ( mFutureWatcher && !mFutureWatcher->isFinished() )
189  {
190  QgsDebugMsg( QStringLiteral( "mFutureWatcher not finished -> schedule to delete later" ) );
191  mDeferredDelete = true;
192  }
193  else
194  {
195  QObject::deleteLater();
196  }
197 }
198 
199 void QgsDataItem::deleteLater( QVector<QgsDataItem *> &items )
200 {
201  const auto constItems = items;
202  for ( QgsDataItem *item : constItems )
203  {
204  if ( !item ) // should not happen
205  continue;
206  item->deleteLater();
207  }
208  items.clear();
209 }
210 
211 void QgsDataItem::moveToThread( QThread *targetThread )
212 {
213  // QObject::moveToThread() cannot move objects with parent, but QgsDataItem is not using paren/children from QObject
214  const auto constMChildren = mChildren;
215  for ( QgsDataItem *child : constMChildren )
216  {
217  if ( !child ) // should not happen
218  continue;
219  QgsDebugMsgLevel( "moveToThread child " + child->path(), 3 );
220  child->QObject::setParent( nullptr ); // to be sure
221  child->moveToThread( targetThread );
222  }
223  QObject::moveToThread( targetThread );
224 }
225 
227 {
228  if ( state() == Populating && sPopulatingIcon )
229  return sPopulatingIcon->icon();
230 
231  if ( !mIcon.isNull() )
232  return mIcon;
233 
234  if ( !mIconMap.contains( mIconName ) )
235  {
236  mIconMap.insert( mIconName, mIconName.startsWith( ':' ) ? QIcon( mIconName ) : QgsApplication::getThemeIcon( mIconName ) );
237  }
238 
239  return mIconMap.value( mIconName );
240 }
241 
242 void QgsDataItem::setName( const QString &name )
243 {
244  mName = name;
245  emit dataChanged( this );
246 }
247 
248 QVector<QgsDataItem *> QgsDataItem::createChildren()
249 {
250  return QVector<QgsDataItem *>();
251 }
252 
253 void QgsDataItem::populate( bool foreground )
254 {
255  if ( state() == Populated || state() == Populating )
256  return;
257 
258  QgsDebugMsgLevel( "mPath = " + mPath, 2 );
259 
260  if ( capabilities2() & QgsDataItem::Fast || foreground )
261  {
263  }
264  else
265  {
266  setState( Populating );
267  // 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.
268  if ( !mFutureWatcher )
269  {
270  mFutureWatcher = new QFutureWatcher< QVector <QgsDataItem *> >( this );
271  }
272 
273  connect( mFutureWatcher, &QFutureWatcherBase::finished, this, &QgsDataItem::childrenCreated );
274  mFutureWatcher->setFuture( QtConcurrent::run( runCreateChildren, this ) );
275  }
276 }
277 
278 // This is expected to be run in a separate thread
279 QVector<QgsDataItem *> QgsDataItem::runCreateChildren( QgsDataItem *item )
280 {
281  QgsDebugMsgLevel( "path = " + item->path(), 2 );
282  QTime time;
283  time.start();
284  QVector <QgsDataItem *> children = item->createChildren();
285  QgsDebugMsgLevel( QStringLiteral( "%1 children created in %2 ms" ).arg( children.size() ).arg( time.elapsed() ), 3 );
286  // Children objects must be pushed to main thread.
287  const auto constChildren = children;
288  for ( QgsDataItem *child : constChildren )
289  {
290  if ( !child ) // should not happen
291  continue;
292  QgsDebugMsgLevel( "moveToThread child " + child->path(), 2 );
293  if ( qApp )
294  child->moveToThread( qApp->thread() ); // moves also children
295  }
296  QgsDebugMsgLevel( QStringLiteral( "finished path %1: %2 children" ).arg( item->path() ).arg( children.size() ), 3 );
297  return children;
298 }
299 
301 {
302  QgsDebugMsgLevel( QStringLiteral( "path = %1 children.size() = %2" ).arg( path() ).arg( mFutureWatcher->result().size() ), 3 );
303 
304  if ( deferredDelete() )
305  {
306  QgsDebugMsg( QStringLiteral( "Item was scheduled to be deleted later" ) );
307  QObject::deleteLater();
308  return;
309  }
310 
311  if ( mChildren.isEmpty() ) // usually populating but may also be refresh if originally there were no children
312  {
313  populate( mFutureWatcher->result() );
314  }
315  else // refreshing
316  {
317  refresh( mFutureWatcher->result() );
318  }
319  disconnect( mFutureWatcher, &QFutureWatcherBase::finished, this, &QgsDataItem::childrenCreated );
320  emit dataChanged( this ); // to replace loading icon by normal icon
321 }
322 
324 {
325  emit dataChanged( this );
326 }
327 
328 void QgsDataItem::populate( const QVector<QgsDataItem *> &children )
329 {
330  QgsDebugMsgLevel( "mPath = " + mPath, 3 );
331 
332  const auto constChildren = children;
333  for ( QgsDataItem *child : constChildren )
334  {
335  if ( !child ) // should not happen
336  continue;
337  // update after thread finished -> refresh
338  addChildItem( child, true );
339  }
340  setState( Populated );
341 }
342 
344 {
345  QgsDebugMsgLevel( "mPath = " + mPath, 3 );
346 
347  const auto constMChildren = mChildren;
348  for ( QgsDataItem *child : constMChildren )
349  {
350  QgsDebugMsgLevel( "remove " + child->path(), 3 );
351  child->depopulate(); // recursive
352  deleteChildItem( child );
353  }
355 }
356 
358 {
359  if ( state() == Populating )
360  return;
361 
362  QgsDebugMsgLevel( "mPath = " + mPath, 3 );
363 
365  {
366  refresh( createChildren() );
367  }
368  else
369  {
370  setState( Populating );
371  if ( !mFutureWatcher )
372  {
373  mFutureWatcher = new QFutureWatcher< QVector <QgsDataItem *> >( this );
374  }
375  connect( mFutureWatcher, &QFutureWatcherBase::finished, this, &QgsDataItem::childrenCreated );
376  mFutureWatcher->setFuture( QtConcurrent::run( runCreateChildren, this ) );
377  }
378 }
379 
381 {
382  // Walk up until the root node is reached
383  if ( mParent )
384  {
386  }
387  else
388  {
389  refresh();
390  emit connectionsChanged();
391  }
392 }
393 
394 void QgsDataItem::refresh( const QVector<QgsDataItem *> &children )
395 {
396  QgsDebugMsgLevel( "mPath = " + mPath, 2 );
397 
398  // Remove no more present children
399  QVector<QgsDataItem *> remove;
400  const auto constMChildren = mChildren;
401  for ( QgsDataItem *child : constMChildren )
402  {
403  if ( !child ) // should not happen
404  continue;
405  if ( findItem( children, child ) >= 0 )
406  continue;
407  remove.append( child );
408  }
409  const auto constRemove = remove;
410  for ( QgsDataItem *child : constRemove )
411  {
412  QgsDebugMsgLevel( "remove " + child->path(), 3 );
413  deleteChildItem( child );
414  }
415 
416  // Add new children
417  const auto constChildren = children;
418  for ( QgsDataItem *child : constChildren )
419  {
420  if ( !child ) // should not happen
421  continue;
422 
423  int index = findItem( mChildren, child );
424  if ( index >= 0 )
425  {
426  // Refresh recursively (some providers may create more generations of descendants)
427  if ( !( child->capabilities2() & QgsDataItem::Fertile ) )
428  {
429  // The child cannot createChildren() itself
430  mChildren.value( index )->refresh( child->children() );
431  }
432 
433  child->deleteLater();
434  continue;
435  }
436  addChildItem( child, true );
437  }
438  setState( Populated );
439 }
440 
442 {
443  return mChildren.size();
444 }
446 {
447  return ( state() == Populated ? !mChildren.isEmpty() : true );
448 }
449 
451 {
452  if ( mParent )
453  {
454  disconnect( this, nullptr, mParent, nullptr );
455  }
456  if ( parent )
457  {
459  connect( this, &QgsDataItem::endInsertItems, parent, &QgsDataItem::endInsertItems );
461  connect( this, &QgsDataItem::endRemoveItems, parent, &QgsDataItem::endRemoveItems );
462  connect( this, &QgsDataItem::dataChanged, parent, &QgsDataItem::dataChanged );
463  connect( this, &QgsDataItem::stateChanged, parent, &QgsDataItem::stateChanged );
464  }
465  mParent = parent;
466 }
467 
469 {
470  Q_ASSERT( child );
471  QgsDebugMsgLevel( QStringLiteral( "path = %1 add child #%2 - %3 - %4" ).arg( mPath ).arg( mChildren.size() ).arg( child->mName ).arg( child->mType ), 3 );
472 
473  //calculate position to insert child
474  int i;
475  if ( type() == Directory )
476  {
477  for ( i = 0; i < mChildren.size(); i++ )
478  {
479  // sort items by type, so directories are before data items
480  if ( mChildren.at( i )->mType == child->mType &&
481  mChildren.at( i )->mName.localeAwareCompare( child->mName ) > 0 )
482  break;
483  }
484  }
485  else
486  {
487  for ( i = 0; i < mChildren.size(); i++ )
488  {
489  if ( mChildren.at( i )->mName.localeAwareCompare( child->mName ) >= 0 )
490  break;
491  }
492  }
493 
494  if ( refresh )
495  emit beginInsertItems( this, i, i );
496 
497  mChildren.insert( i, child );
498  child->setParent( this );
499 
500  if ( refresh )
501  emit endInsertItems();
502 }
503 
505 {
506  QgsDebugMsgLevel( "mName = " + child->mName, 2 );
507  int i = mChildren.indexOf( child );
508  Q_ASSERT( i >= 0 );
509  emit beginRemoveItems( this, i, i );
510  mChildren.remove( i );
511  child->deleteLater();
512  emit endRemoveItems();
513 }
514 
516 {
517  QgsDebugMsgLevel( "mName = " + child->mName, 2 );
518  int i = mChildren.indexOf( child );
519  Q_ASSERT( i >= 0 );
520  if ( i < 0 )
521  {
522  child->setParent( nullptr );
523  return nullptr;
524  }
525 
526  emit beginRemoveItems( this, i, i );
527  mChildren.remove( i );
528  emit endRemoveItems();
529  return child;
530 }
531 
532 int QgsDataItem::findItem( QVector<QgsDataItem *> items, QgsDataItem *item )
533 {
534  for ( int i = 0; i < items.size(); i++ )
535  {
536  Q_ASSERT_X( items[i], "findItem", QStringLiteral( "item %1 is nullptr" ).arg( i ).toLatin1() );
537  QgsDebugMsgLevel( QString::number( i ) + " : " + items[i]->mPath + " x " + item->mPath, 2 );
538  if ( items[i]->equal( item ) )
539  return i;
540  }
541  return -1;
542 }
543 
544 bool QgsDataItem::equal( const QgsDataItem *other )
545 {
546  return ( metaObject()->className() == other->metaObject()->className() &&
547  mPath == other->path() );
548 }
549 
550 QList<QAction *> QgsDataItem::actions( QWidget *parent )
551 {
552  Q_UNUSED( parent )
553  return QList<QAction *>();
554 }
555 
557 {
558  return false;
559 }
560 
561 bool QgsDataItem::rename( const QString & )
562 {
563  return false;
564 }
565 
567 {
568  return mState;
569 }
570 
572 {
573  QgsDebugMsgLevel( QStringLiteral( "item %1 set state %2 -> %3" ).arg( path() ).arg( this->state() ).arg( state ), 3 );
574  if ( state == mState )
575  return;
576 
577  State oldState = mState;
578 
579  if ( state == Populating ) // start loading
580  {
581  if ( !sPopulatingIcon )
582  {
583  // TODO: ensure that QgsAnimatedIcon is created on UI thread only
584  sPopulatingIcon = new QgsAnimatedIcon( QgsApplication::iconPath( QStringLiteral( "/mIconLoading.gif" ) ), QgsApplication::instance() );
585  }
586 
587  sPopulatingIcon->connectFrameChanged( this, &QgsDataItem::updateIcon );
588  }
589  else if ( mState == Populating && sPopulatingIcon ) // stop loading
590  {
591  sPopulatingIcon->disconnectFrameChanged( this, &QgsDataItem::updateIcon );
592  }
593 
594 
595  mState = state;
596 
597  emit stateChanged( this, oldState );
598  if ( state == Populated )
599  updateIcon();
600 }
601 
602 QList<QMenu *> QgsDataItem::menus( QWidget *parent )
603 {
604  Q_UNUSED( parent )
605  return QList<QMenu *>();
606 }
607 
608 // ---------------------------------------------------------------------
609 
610 QgsLayerItem::QgsLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri, LayerType layerType, const QString &providerKey )
611  : QgsDataItem( Layer, parent, name, path )
612  , mProviderKey( providerKey )
613  , mUri( uri )
614  , mLayerType( layerType )
615 {
616  mIconName = iconName( layerType );
617 }
618 
620 {
621  switch ( mLayerType )
622  {
625 
626  case QgsLayerItem::Mesh:
628 
631 
634  case QgsLayerItem::Point:
636  case QgsLayerItem::Line:
638  case QgsLayerItem::Table:
641  }
642 
643  return QgsMapLayerType::VectorLayer; // no warnings
644 }
645 
647 {
648  switch ( layer->type() )
649  {
651  {
652  switch ( qobject_cast< QgsVectorLayer * >( layer )->geometryType() )
653  {
655  return Point;
656 
658  return Line;
659 
661  return Polygon;
662 
664  return TableLayer;
665 
667  return Vector;
668  }
669 
670  return Vector; // no warnings
671  }
672 
674  return Raster;
676  return Plugin;
678  return Mesh;
679  }
680  return Vector; // no warnings
681 }
682 
684 {
685  static int enumIdx = staticMetaObject.indexOfEnumerator( "LayerType" );
686  return staticMetaObject.enumerator( enumIdx ).valueToKey( layerType );
687 }
688 
690 {
691  switch ( layerType )
692  {
693  case Point:
694  return QStringLiteral( "/mIconPointLayer.svg" );
695  case Line:
696  return QStringLiteral( "/mIconLineLayer.svg" );
697  case Polygon:
698  return QStringLiteral( "/mIconPolygonLayer.svg" );
699  // TODO add a new icon for generic Vector layers
700  case Vector :
701  return QStringLiteral( "/mIconVector.svg" );
702  case TableLayer:
703  case Table:
704  return QStringLiteral( "/mIconTableLayer.svg" );
705  case Raster:
706  return QStringLiteral( "/mIconRaster.svg" );
707  case Mesh:
708  return QStringLiteral( "/mIconMeshLayer.svg" );
709  default:
710  return QStringLiteral( "/mIconLayer.png" );
711  }
712 }
713 
715 {
716  return false;
717 }
718 
719 bool QgsLayerItem::equal( const QgsDataItem *other )
720 {
721  //QgsDebugMsg ( mPath + " x " + other->mPath );
722  if ( type() != other->type() )
723  {
724  return false;
725  }
726  //const QgsLayerItem *o = qobject_cast<const QgsLayerItem *> ( other );
727  const QgsLayerItem *o = qobject_cast<const QgsLayerItem *>( other );
728  if ( !o )
729  return false;
730 
731  return ( mPath == o->mPath && mName == o->mName && mUri == o->mUri && mProviderKey == o->mProviderKey );
732 }
733 
735 {
737 
738  switch ( mapLayerType() )
739  {
741  u.layerType = QStringLiteral( "vector" );
742  switch ( mLayerType )
743  {
744  case Point:
746  break;
747  case Line:
749  break;
750  case Polygon:
752  break;
753  case TableLayer:
755  break;
756 
757  case Database:
758  case Table:
759  case NoType:
760  case Vector:
761  case Raster:
762  case Plugin:
763  case Mesh:
764  break;
765  }
766  break;
768  u.layerType = QStringLiteral( "raster" );
769  break;
771  u.layerType = QStringLiteral( "mesh" );
772  break;
774  u.layerType = QStringLiteral( "plugin" );
775  break;
776  }
777 
778  u.providerKey = providerKey();
779  u.name = layerName();
780  u.uri = uri();
783  return u;
784 }
785 
786 // ---------------------------------------------------------------------
788  : QgsDataItem( Collection, parent, name, path )
789 {
791  mIconName = QStringLiteral( "/mIconDbSchema.svg" );
792 }
793 
795 {
796  QgsDebugMsgLevel( "mName = " + mName + " mPath = " + mPath, 2 );
797 
798 // Do not delete children, children are deleted by QObject parent
799 #if 0
800  const auto constMChildren = mChildren;
801  for ( QgsDataItem *i : constMChildren )
802  {
803  QgsDebugMsgLevel( QStringLiteral( "delete child = 0x%0" ).arg( static_cast<qlonglong>( i ), 8, 16, QLatin1Char( '0' ) ), 2 );
804  delete i;
805  }
806 #endif
807 }
808 
809 //-----------------------------------------------------------------------
810 
811 QgsDirectoryItem::QgsDirectoryItem( QgsDataItem *parent, const QString &name, const QString &path )
812  : QgsDataCollectionItem( parent, QDir::toNativeSeparators( name ), path )
813  , mDirPath( path )
814  , mRefreshLater( false )
815 {
816  mType = Directory;
817  init();
818 }
819 
820 QgsDirectoryItem::QgsDirectoryItem( QgsDataItem *parent, const QString &name, const QString &dirPath, const QString &path )
821  : QgsDataCollectionItem( parent, QDir::toNativeSeparators( name ), path )
822  , mDirPath( dirPath )
823  , mRefreshLater( false )
824 {
825  mType = Directory;
826  init();
827 }
828 
830 {
831  setToolTip( QDir::toNativeSeparators( mDirPath ) );
832 }
833 
835 {
836  if ( mDirPath == QDir::homePath() )
837  return homeDirIcon();
838 
839  // still loading? show the spinner
840  if ( state() == Populating )
841  return QgsDataItem::icon();
842 
843  // symbolic link? use link icon
844  QFileInfo fi( mDirPath );
845  if ( fi.isDir() && fi.isSymLink() )
846  {
847  return QgsApplication::getThemeIcon( QStringLiteral( "mIconFolderLink.svg" ) );
848  }
849 
850  // loaded? show the open dir icon
851  if ( state() == Populated )
852  return openDirIcon();
853 
854  // show the closed dir icon
855  return iconDir();
856 }
857 
858 
859 QVector<QgsDataItem *> QgsDirectoryItem::createChildren()
860 {
861  QVector<QgsDataItem *> children;
862  QDir dir( mDirPath );
863 
864  const QList<QgsDataItemProvider *> providers = QgsApplication::dataItemProviderRegistry()->providers();
865 
866  QStringList entries = dir.entryList( QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name | QDir::IgnoreCase );
867  const auto constEntries = entries;
868  for ( const QString &subdir : constEntries )
869  {
870  if ( mRefreshLater )
871  {
872  deleteLater( children );
873  return children;
874  }
875 
876  QString subdirPath = dir.absoluteFilePath( subdir );
877 
878  QgsDebugMsgLevel( QStringLiteral( "creating subdir: %1" ).arg( subdirPath ), 2 );
879 
880  QString path = mPath + '/' + subdir; // may differ from subdirPath
881  if ( QgsDirectoryItem::hiddenPath( path ) )
882  continue;
883 
884  bool handledByProvider = false;
885  for ( QgsDataItemProvider *provider : providers )
886  {
887  if ( provider->handlesDirectoryPath( path ) )
888  {
889  handledByProvider = true;
890  break;
891  }
892  }
893  if ( handledByProvider )
894  continue;
895 
896  QgsDirectoryItem *item = new QgsDirectoryItem( this, subdir, subdirPath, path );
897 
898  // we want directories shown before files
899  item->setSortKey( QStringLiteral( " %1" ).arg( subdir ) );
900 
901  // propagate signals up to top
902 
903  children.append( item );
904  }
905 
906  QStringList fileEntries = dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot | QDir::Files, QDir::Name );
907  const auto constFileEntries = fileEntries;
908  for ( const QString &name : constFileEntries )
909  {
910  if ( mRefreshLater )
911  {
912  deleteLater( children );
913  return children;
914  }
915 
916  QString path = dir.absoluteFilePath( name );
917  QFileInfo fileInfo( path );
918 
919  if ( fileInfo.suffix().compare( QLatin1String( "zip" ), Qt::CaseInsensitive ) == 0 ||
920  fileInfo.suffix().compare( QLatin1String( "tar" ), Qt::CaseInsensitive ) == 0 )
921  {
922  QgsDataItem *item = QgsZipItem::itemFromPath( this, path, name, mPath + '/' + name );
923  if ( item )
924  {
925  children.append( item );
926  continue;
927  }
928  }
929 
930  bool createdItem = false;
931  for ( QgsDataItemProvider *provider : providers )
932  {
933  int capabilities = provider->capabilities();
934 
935  if ( !( ( fileInfo.isFile() && ( capabilities & QgsDataProvider::File ) ) ||
936  ( fileInfo.isDir() && ( capabilities & QgsDataProvider::Dir ) ) ) )
937  {
938  continue;
939  }
940 
941  QgsDataItem *item = provider->createDataItem( path, this );
942  if ( item )
943  {
944  children.append( item );
945  createdItem = true;
946  }
947  }
948 
949  if ( !createdItem )
950  {
951  // if item is a QGIS project, and no specific item provider has overridden handling of
952  // project items, then use the default project item behavior
953  if ( fileInfo.suffix().compare( QLatin1String( "qgs" ), Qt::CaseInsensitive ) == 0 ||
954  fileInfo.suffix().compare( QLatin1String( "qgz" ), Qt::CaseInsensitive ) == 0 )
955  {
956  QgsDataItem *item = new QgsProjectItem( this, fileInfo.completeBaseName(), path );
957  children.append( item );
958  continue;
959  }
960  }
961 
962  }
963  return children;
964 }
965 
967 {
969 
970  if ( state == Populated )
971  {
972  if ( !mFileSystemWatcher )
973  {
974  mFileSystemWatcher = new QFileSystemWatcher( this );
975  mFileSystemWatcher->addPath( mDirPath );
976  connect( mFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &QgsDirectoryItem::directoryChanged );
977  }
978  mLastScan = QDateTime::currentDateTime();
979  }
980  else if ( state == NotPopulated )
981  {
982  if ( mFileSystemWatcher )
983  {
984  delete mFileSystemWatcher;
985  mFileSystemWatcher = nullptr;
986  }
987  }
988 }
989 
991 {
992  // If the last scan was less than 10 seconds ago, skip this
993  if ( mLastScan.msecsTo( QDateTime::currentDateTime() ) < QgsSettings().value( QStringLiteral( "browser/minscaninterval" ), 10000 ).toInt() )
994  {
995  return;
996  }
997  if ( state() == Populating )
998  {
999  // schedule to refresh later, because refresh() simply returns if Populating
1000  mRefreshLater = true;
1001  }
1002  else
1003  {
1004  // We definintely don't want the temporary files created by sqlite
1005  // to re-trigger a refresh in an infinite loop.
1006  disconnect( mFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &QgsDirectoryItem::directoryChanged );
1007  // QFileSystemWhatcher::directoryChanged is emitted when a
1008  // file is created and not when it is closed/flushed.
1009  //
1010  // Delay to give to OS the time to complete writing the file
1011  // this happens when a new file appears in the directory and
1012  // the item's children thread will try to open the file with
1013  // GDAL or OGR even if it is still being written.
1014  QTimer::singleShot( 100, this, [ = ] { refresh(); } );
1015  }
1016 }
1017 
1018 bool QgsDirectoryItem::hiddenPath( const QString &path )
1019 {
1020  QgsSettings settings;
1021  QStringList hiddenItems = settings.value( QStringLiteral( "browser/hiddenPaths" ),
1022  QStringList() ).toStringList();
1023  int idx = hiddenItems.indexOf( path );
1024  return ( idx > -1 );
1025 }
1026 
1028 {
1029  QgsDebugMsgLevel( QStringLiteral( "mRefreshLater = %1" ).arg( mRefreshLater ), 3 );
1030 
1031  if ( mRefreshLater )
1032  {
1033  QgsDebugMsgLevel( QStringLiteral( "directory changed during createChidren() -> refresh() again" ), 3 );
1034  mRefreshLater = false;
1035  setState( Populated );
1036  refresh();
1037  }
1038  else
1039  {
1041  }
1042  // Re-connect the file watcher after all children have been created
1043  connect( mFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &QgsDirectoryItem::directoryChanged );
1044 }
1045 
1047 {
1048  //QgsDebugMsg ( mPath + " x " + other->mPath );
1049  if ( type() != other->type() )
1050  {
1051  return false;
1052  }
1053  return ( path() == other->path() );
1054 }
1055 
1057 {
1058  return new QgsDirectoryParamWidget( mPath );
1059 }
1060 
1062 {
1064  u.layerType = QStringLiteral( "directory" );
1065  u.name = mName;
1066  u.uri = mDirPath;
1067  return u;
1068 }
1069 
1071  : QTreeWidget( parent )
1072 {
1073  setRootIsDecorated( false );
1074 
1075  // name, size, date, permissions, owner, group, type
1076  setColumnCount( 7 );
1077  QStringList labels;
1078  labels << tr( "Name" ) << tr( "Size" ) << tr( "Date" ) << tr( "Permissions" ) << tr( "Owner" ) << tr( "Group" ) << tr( "Type" );
1079  setHeaderLabels( labels );
1080 
1081  QIcon iconDirectory = QgsApplication::getThemeIcon( QStringLiteral( "mIconFolder.svg" ) );
1082  QIcon iconFile = QgsApplication::getThemeIcon( QStringLiteral( "mIconFile.svg" ) );
1083  QIcon iconDirLink = QgsApplication::getThemeIcon( QStringLiteral( "mIconFolderLink.svg" ) );
1084  QIcon iconFileLink = QgsApplication::getThemeIcon( QStringLiteral( "mIconFileLink.svg" ) );
1085 
1086  QList<QTreeWidgetItem *> items;
1087 
1088  QDir dir( path );
1089  QStringList entries = dir.entryList( QDir::AllEntries | QDir::NoDotAndDotDot, QDir::Name | QDir::IgnoreCase );
1090  const auto constEntries = entries;
1091  for ( const QString &name : constEntries )
1092  {
1093  QFileInfo fi( dir.absoluteFilePath( name ) );
1094  QStringList texts;
1095  texts << name;
1096  QString size;
1097  if ( fi.size() > 1024 )
1098  {
1099  size = size.sprintf( "%.1f KiB", fi.size() / 1024.0 );
1100  }
1101  else if ( fi.size() > 1.048576e6 )
1102  {
1103  size = size.sprintf( "%.1f MiB", fi.size() / 1.048576e6 );
1104  }
1105  else
1106  {
1107  size = QStringLiteral( "%1 B" ).arg( fi.size() );
1108  }
1109  texts << size;
1110  texts << fi.lastModified().toString( Qt::SystemLocaleShortDate );
1111  QString perm;
1112  perm += fi.permission( QFile::ReadOwner ) ? 'r' : '-';
1113  perm += fi.permission( QFile::WriteOwner ) ? 'w' : '-';
1114  perm += fi.permission( QFile::ExeOwner ) ? 'x' : '-';
1115  // QFile::ReadUser, QFile::WriteUser, QFile::ExeUser
1116  perm += fi.permission( QFile::ReadGroup ) ? 'r' : '-';
1117  perm += fi.permission( QFile::WriteGroup ) ? 'w' : '-';
1118  perm += fi.permission( QFile::ExeGroup ) ? 'x' : '-';
1119  perm += fi.permission( QFile::ReadOther ) ? 'r' : '-';
1120  perm += fi.permission( QFile::WriteOther ) ? 'w' : '-';
1121  perm += fi.permission( QFile::ExeOther ) ? 'x' : '-';
1122  texts << perm;
1123 
1124  texts << fi.owner();
1125  texts << fi.group();
1126 
1127  QString type;
1128  QIcon icon;
1129  if ( fi.isDir() && fi.isSymLink() )
1130  {
1131  type = tr( "folder" );
1132  icon = iconDirLink;
1133  }
1134  else if ( fi.isDir() )
1135  {
1136  type = tr( "folder" );
1137  icon = iconDirectory;
1138  }
1139  else if ( fi.isFile() && fi.isSymLink() )
1140  {
1141  type = tr( "file" );
1142  icon = iconFileLink;
1143  }
1144  else if ( fi.isFile() )
1145  {
1146  type = tr( "file" );
1147  icon = iconFile;
1148  }
1149 
1150  texts << type;
1151 
1152  QTreeWidgetItem *item = new QTreeWidgetItem( texts );
1153  item->setIcon( 0, icon );
1154  items << item;
1155  }
1156 
1157  addTopLevelItems( items );
1158 
1159  // hide columns that are not requested
1160  QgsSettings settings;
1161  QList<QVariant> lst = settings.value( QStringLiteral( "dataitem/directoryHiddenColumns" ) ).toList();
1162  const auto constLst = lst;
1163  for ( const QVariant &colVariant : constLst )
1164  {
1165  setColumnHidden( colVariant.toInt(), true );
1166  }
1167 }
1168 
1170 {
1171  if ( event->button() == Qt::RightButton )
1172  {
1173  // show the popup menu
1174  QMenu popupMenu;
1175 
1176  QStringList labels;
1177  labels << tr( "Name" ) << tr( "Size" ) << tr( "Date" ) << tr( "Permissions" ) << tr( "Owner" ) << tr( "Group" ) << tr( "Type" );
1178  for ( int i = 0; i < labels.count(); i++ )
1179  {
1180  QAction *action = popupMenu.addAction( labels[i], this, &QgsDirectoryParamWidget::showHideColumn );
1181  action->setObjectName( QString::number( i ) );
1182  action->setCheckable( true );
1183  action->setChecked( !isColumnHidden( i ) );
1184  }
1185 
1186  popupMenu.exec( event->globalPos() );
1187  }
1188 }
1189 
1191 {
1192  QAction *action = qobject_cast<QAction *>( sender() );
1193  if ( !action )
1194  return; // something is wrong
1195 
1196  int columnIndex = action->objectName().toInt();
1197  setColumnHidden( columnIndex, !isColumnHidden( columnIndex ) );
1198 
1199  // save in settings
1200  QgsSettings settings;
1201  QList<QVariant> lst;
1202  for ( int i = 0; i < columnCount(); i++ )
1203  {
1204  if ( isColumnHidden( i ) )
1205  lst.append( QVariant( i ) );
1206  }
1207  settings.setValue( QStringLiteral( "dataitem/directoryHiddenColumns" ), lst );
1208 }
1209 
1210 QgsProjectItem::QgsProjectItem( QgsDataItem *parent, const QString &name, const QString &path )
1211  : QgsDataItem( QgsDataItem::Project, parent, name, path )
1212 {
1213  mIconName = QStringLiteral( ":/images/icons/qgis_icon.svg" );
1214  setToolTip( QDir::toNativeSeparators( path ) );
1215  setState( Populated ); // no more children
1216 }
1217 
1219 {
1221  u.layerType = QStringLiteral( "project" );
1222  u.name = mName;
1223  u.uri = mPath;
1224  return u;
1225 }
1226 
1227 QgsErrorItem::QgsErrorItem( QgsDataItem *parent, const QString &error, const QString &path )
1228  : QgsDataItem( QgsDataItem::Error, parent, error, path )
1229 {
1230  mIconName = QStringLiteral( "/mIconDelete.svg" );
1231 
1232  setState( Populated ); // no more children
1233 }
1234 
1236  : QgsDataCollectionItem( parent, name, QStringLiteral( "favorites:" ) )
1237 {
1238  Q_UNUSED( path )
1239  mCapabilities |= Fast;
1240  mType = Favorites;
1241  mIconName = QStringLiteral( "/mIconFavourites.svg" );
1242  populate();
1243 }
1244 
1245 QVector<QgsDataItem *> QgsFavoritesItem::createChildren()
1246 {
1247  QVector<QgsDataItem *> children;
1248 
1249  QgsSettings settings;
1250  const QStringList favDirs = settings.value( QStringLiteral( "browser/favourites" ), QVariant() ).toStringList();
1251 
1252  for ( const QString &favDir : favDirs )
1253  {
1254  QStringList parts = favDir.split( QStringLiteral( "|||" ) );
1255  if ( parts.empty() )
1256  continue;
1257 
1258  QString dir = parts.at( 0 );
1259  QString name = dir;
1260  if ( parts.count() > 1 )
1261  name = parts.at( 1 );
1262 
1263  children << createChildren( dir, name );
1264  }
1265 
1266  return children;
1267 }
1268 
1269 void QgsFavoritesItem::addDirectory( const QString &favDir, const QString &n )
1270 {
1271  QString name = n.isEmpty() ? favDir : n;
1272 
1273  QgsSettings settings;
1274  QStringList favDirs = settings.value( QStringLiteral( "browser/favourites" ) ).toStringList();
1275  favDirs.append( QStringLiteral( "%1|||%2" ).arg( favDir, name ) );
1276  settings.setValue( QStringLiteral( "browser/favourites" ), favDirs );
1277 
1278  if ( state() == Populated )
1279  {
1280  QVector<QgsDataItem *> items = createChildren( favDir, name );
1281  const auto constItems = items;
1282  for ( QgsDataItem *item : constItems )
1283  {
1284  addChildItem( item, true );
1285  }
1286  }
1287 }
1288 
1290 {
1291  if ( !item )
1292  return;
1293 
1294  QgsSettings settings;
1295  QStringList favDirs = settings.value( QStringLiteral( "browser/favourites" ) ).toStringList();
1296  for ( int i = favDirs.count() - 1; i >= 0; --i )
1297  {
1298  QStringList parts = favDirs.at( i ).split( QStringLiteral( "|||" ) );
1299  if ( parts.empty() )
1300  continue;
1301 
1302  QString dir = parts.at( 0 );
1303  if ( dir == item->dirPath() )
1304  favDirs.removeAt( i );
1305  }
1306  settings.setValue( QStringLiteral( "browser/favourites" ), favDirs );
1307 
1308  int idx = findItem( mChildren, item );
1309  if ( idx < 0 )
1310  {
1311  QgsDebugMsg( QStringLiteral( "favorites item %1 not found" ).arg( item->path() ) );
1312  return;
1313  }
1314 
1315  if ( state() == Populated )
1316  deleteChildItem( mChildren.at( idx ) );
1317 }
1318 
1319 void QgsFavoritesItem::renameFavorite( const QString &path, const QString &name )
1320 {
1321  // update stored name
1322  QgsSettings settings;
1323  QStringList favDirs = settings.value( QStringLiteral( "browser/favourites" ) ).toStringList();
1324  for ( int i = 0; i < favDirs.count(); ++i )
1325  {
1326  QStringList parts = favDirs.at( i ).split( QStringLiteral( "|||" ) );
1327  if ( parts.empty() )
1328  continue;
1329 
1330  QString dir = parts.at( 0 );
1331  if ( dir == path )
1332  {
1333  favDirs[i] = QStringLiteral( "%1|||%2" ).arg( path, name );
1334  break;
1335  }
1336  }
1337  settings.setValue( QStringLiteral( "browser/favourites" ), favDirs );
1338 
1339  // also update existing data item
1340  const QVector<QgsDataItem *> ch = children();
1341  for ( QgsDataItem *child : ch )
1342  {
1343  if ( QgsFavoriteItem *favorite = qobject_cast< QgsFavoriteItem * >( child ) )
1344  {
1345  if ( favorite->dirPath() == path )
1346  {
1347  favorite->setName( name );
1348  break;
1349  }
1350  }
1351  }
1352 }
1353 
1354 QVector<QgsDataItem *> QgsFavoritesItem::createChildren( const QString &favDir, const QString &name )
1355 {
1356  QVector<QgsDataItem *> children;
1357  QString pathName = pathComponent( favDir );
1358  const auto constProviders = QgsApplication::dataItemProviderRegistry()->providers();
1359  for ( QgsDataItemProvider *provider : constProviders )
1360  {
1361  int capabilities = provider->capabilities();
1362 
1363  if ( capabilities & QgsDataProvider::Dir )
1364  {
1365  QgsDataItem *item = provider->createDataItem( favDir, this );
1366  if ( item )
1367  {
1368  item->setName( name );
1369  children.append( item );
1370  }
1371  }
1372  }
1373  if ( children.isEmpty() )
1374  {
1375  QgsFavoriteItem *item = new QgsFavoriteItem( this, name, favDir, mPath + '/' + pathName );
1376  if ( item )
1377  {
1378  children.append( item );
1379  }
1380  }
1381  return children;
1382 }
1383 
1384 //-----------------------------------------------------------------------
1385 QStringList QgsZipItem::sProviderNames = QStringList();
1386 
1387 
1388 QgsZipItem::QgsZipItem( QgsDataItem *parent, const QString &name, const QString &path )
1389  : QgsDataCollectionItem( parent, name, path )
1390 {
1391  mFilePath = path;
1392  init();
1393 }
1394 
1395 QgsZipItem::QgsZipItem( QgsDataItem *parent, const QString &name, const QString &filePath, const QString &path )
1396  : QgsDataCollectionItem( parent, name, path )
1397  , mFilePath( filePath )
1398 {
1399  init();
1400 }
1401 
1402 void QgsZipItem::init()
1403 {
1404  mType = Collection; //Zip??
1405  mIconName = QStringLiteral( "/mIconZip.svg" );
1407 
1408  static std::once_flag initialized;
1409  std::call_once( initialized, [ = ]
1410  {
1411  sProviderNames << QStringLiteral( "OGR" ) << QStringLiteral( "GDAL" );
1412  } );
1413 }
1414 
1415 QVector<QgsDataItem *> QgsZipItem::createChildren()
1416 {
1417  QVector<QgsDataItem *> children;
1418  QString tmpPath;
1419  QgsSettings settings;
1420  QString scanZipSetting = settings.value( QStringLiteral( "qgis/scanZipInBrowser2" ), "basic" ).toString();
1421 
1422  mZipFileList.clear();
1423 
1424  QgsDebugMsgLevel( QStringLiteral( "mFilePath = %1 path = %2 name= %3 scanZipSetting= %4 vsiPrefix= %5" ).arg( mFilePath, path(), name(), scanZipSetting, mVsiPrefix ), 3 );
1425 
1426  // if scanZipBrowser == no: skip to the next file
1427  if ( scanZipSetting == QLatin1String( "no" ) )
1428  {
1429  return children;
1430  }
1431 
1432  // first get list of files
1433  getZipFileList();
1434 
1435  const QList<QgsDataItemProvider *> providers = QgsApplication::dataItemProviderRegistry()->providers();
1436 
1437  // loop over files inside zip
1438  const auto constMZipFileList = mZipFileList;
1439  for ( const QString &fileName : constMZipFileList )
1440  {
1441  QFileInfo info( fileName );
1442  tmpPath = mVsiPrefix + mFilePath + '/' + fileName;
1443  QgsDebugMsgLevel( "tmpPath = " + tmpPath, 3 );
1444 
1445  for ( QgsDataItemProvider *provider : providers )
1446  {
1447  if ( !sProviderNames.contains( provider->name() ) )
1448  continue;
1449 
1450  // ugly hack to remove .dbf file if there is a .shp file
1451  if ( provider->name() == QStringLiteral( "OGR" ) )
1452  {
1453  if ( info.suffix().compare( QLatin1String( "dbf" ), Qt::CaseInsensitive ) == 0 )
1454  {
1455  if ( mZipFileList.indexOf( fileName.left( fileName.count() - 4 ) + ".shp" ) != -1 )
1456  continue;
1457  }
1458  if ( info.completeSuffix().compare( QLatin1String( "shp.xml" ), Qt::CaseInsensitive ) == 0 )
1459  {
1460  continue;
1461  }
1462  }
1463 
1464  QgsDebugMsgLevel( QStringLiteral( "trying to load item %1 with %2" ).arg( tmpPath, provider->name() ), 3 );
1465  QgsDataItem *item = provider->createDataItem( tmpPath, this );
1466  if ( item )
1467  {
1468  // the item comes with zipped file name, set the name to relative path within zip file
1469  item->setName( fileName );
1470  children.append( item );
1471  }
1472  else
1473  {
1474  QgsDebugMsgLevel( QStringLiteral( "not loaded item" ), 3 );
1475  }
1476  }
1477  }
1478 
1479  return children;
1480 }
1481 
1482 QgsDataItem *QgsZipItem::itemFromPath( QgsDataItem *parent, const QString &path, const QString &name )
1483 {
1484  return itemFromPath( parent, path, name, path );
1485 }
1486 
1487 QgsDataItem *QgsZipItem::itemFromPath( QgsDataItem *parent, const QString &filePath, const QString &name, const QString &path )
1488 {
1489  QgsSettings settings;
1490  QString scanZipSetting = settings.value( QStringLiteral( "qgis/scanZipInBrowser2" ), "basic" ).toString();
1491  QStringList zipFileList;
1492  QString vsiPrefix = QgsZipItem::vsiPrefix( filePath );
1493  QgsZipItem *zipItem = nullptr;
1494  bool populated = false;
1495 
1496  QgsDebugMsgLevel( QStringLiteral( "path = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( path, name, scanZipSetting, vsiPrefix ), 3 );
1497 
1498  // don't scan if scanZipBrowser == no
1499  if ( scanZipSetting == QLatin1String( "no" ) )
1500  return nullptr;
1501 
1502  // don't scan if this file is not a /vsizip/ or /vsitar/ item
1503  if ( ( vsiPrefix != QLatin1String( "/vsizip/" ) && vsiPrefix != QLatin1String( "/vsitar/" ) ) )
1504  return nullptr;
1505 
1506  zipItem = new QgsZipItem( parent, name, filePath, path );
1507 
1508  if ( zipItem )
1509  {
1510  // force populate zipItem if it has less than 10 items and is not a .tgz or .tar.gz file (slow loading)
1511  // for other items populating will be delayed until item is opened
1512  // this might be polluting the tree with empty items but is necessary for performance reasons
1513  // could also accept all files smaller than a certain size and add options for file count and/or size
1514 
1515  // first get list of files inside .zip or .tar files
1516  if ( path.endsWith( QLatin1String( ".zip" ), Qt::CaseInsensitive ) ||
1517  path.endsWith( QLatin1String( ".tar" ), Qt::CaseInsensitive ) )
1518  {
1519  zipFileList = zipItem->getZipFileList();
1520  }
1521  // force populate if less than 10 items
1522  if ( !zipFileList.isEmpty() && zipFileList.count() <= 10 )
1523  {
1524  zipItem->populate( zipItem->createChildren() );
1525  populated = true; // there is no QgsDataItem::isPopulated() function
1526  QgsDebugMsgLevel( QStringLiteral( "Got zipItem with %1 children, path=%2, name=%3" ).arg( zipItem->rowCount() ).arg( zipItem->path(), zipItem->name() ), 3 );
1527  }
1528  else
1529  {
1530  QgsDebugMsgLevel( QStringLiteral( "Delaying populating zipItem with path=%1, name=%2" ).arg( zipItem->path(), zipItem->name() ), 3 );
1531  }
1532  }
1533 
1534  // only display if has children or if is not populated
1535  if ( zipItem && ( !populated || zipItem->rowCount() > 0 ) )
1536  {
1537  QgsDebugMsgLevel( QStringLiteral( "returning zipItem" ), 3 );
1538  return zipItem;
1539  }
1540 
1541  return nullptr;
1542 }
1543 
1545 {
1546  if ( ! mZipFileList.isEmpty() )
1547  return mZipFileList;
1548 
1549  QString tmpPath;
1550  QgsSettings settings;
1551  QString scanZipSetting = settings.value( QStringLiteral( "qgis/scanZipInBrowser2" ), "basic" ).toString();
1552 
1553  QgsDebugMsgLevel( QStringLiteral( "mFilePath = %1 name= %2 scanZipSetting= %3 vsiPrefix= %4" ).arg( mFilePath, name(), scanZipSetting, mVsiPrefix ), 3 );
1554 
1555  // if scanZipBrowser == no: skip to the next file
1556  if ( scanZipSetting == QLatin1String( "no" ) )
1557  {
1558  return mZipFileList;
1559  }
1560 
1561  // get list of files inside zip file
1562  QgsDebugMsgLevel( QStringLiteral( "Open file %1 with gdal vsi" ).arg( mVsiPrefix + mFilePath ), 3 );
1563  char **papszSiblingFiles = VSIReadDirRecursive( QString( mVsiPrefix + mFilePath ).toLocal8Bit().constData() );
1564  if ( papszSiblingFiles )
1565  {
1566  for ( int i = 0; papszSiblingFiles[i]; i++ )
1567  {
1568  tmpPath = papszSiblingFiles[i];
1569  QgsDebugMsgLevel( QStringLiteral( "Read file %1" ).arg( tmpPath ), 3 );
1570  // skip directories (files ending with /)
1571  if ( tmpPath.right( 1 ) != QLatin1String( "/" ) )
1572  mZipFileList << tmpPath;
1573  }
1574  CSLDestroy( papszSiblingFiles );
1575  }
1576  else
1577  {
1578  QgsDebugMsg( QStringLiteral( "Error reading %1" ).arg( mFilePath ) );
1579  }
1580 
1581  return mZipFileList;
1582 }
1583 
1585 
1586 QgsProjectHomeItem::QgsProjectHomeItem( QgsDataItem *parent, const QString &name, const QString &dirPath, const QString &path )
1587  : QgsDirectoryItem( parent, name, dirPath, path )
1588 {
1589 }
1590 
1591 QIcon QgsProjectHomeItem::icon()
1592 {
1593  if ( state() == Populating )
1594  return QgsDirectoryItem::icon();
1595  return QgsApplication::getThemeIcon( QStringLiteral( "mIconFolderProject.svg" ) );
1596 }
1597 
1598 QVariant QgsProjectHomeItem::sortKey() const
1599 {
1600  return QStringLiteral( " 1" );
1601 }
1602 
1603 
1604 QgsFavoriteItem::QgsFavoriteItem( QgsFavoritesItem *parent, const QString &name, const QString &dirPath, const QString &path )
1605  : QgsDirectoryItem( parent, name, dirPath, path )
1606  , mFavorites( parent )
1607 {
1608  mCapabilities |= Rename;
1609 }
1610 
1611 bool QgsFavoriteItem::rename( const QString &name )
1612 {
1613  mFavorites->renameFavorite( dirPath(), name );
1614  return true;
1615 }
1616 
1617 
A Collection: logical collection of layers or subcollections, e.g.
Definition: qgsdataitem.h:571
QString layerType
Type of URI.
virtual QList< QMenu * > menus(QWidget *parent)
Returns the list of menus available for this item.
~QgsDataCollectionItem() override
virtual QVariant sortKey() const
Returns the sorting key for the item.
static QIcon iconRaster()
Definition: qgsdataitem.cpp:72
void beginInsertItems(QgsDataItem *parent, int first, int last)
QString path() const
Definition: qgsdataitem.h:309
bool disconnectFrameChanged(const typename QtPrivate::FunctionPointer< Func1 >::Object *receiver, Func1 slot)
Convenience function to disconnect the same style that the frame change connection was established...
Base class for all map layer types.
Definition: qgsmaplayer.h:79
QgsFavoritesItem(QgsDataItem *parent, const QString &name, const QString &path=QString())
Constructor for QgsFavoritesItem.
virtual void childrenCreated()
virtual QString layerName() const
Definition: qgsdataitem.h:563
void childrenCreated() override
virtual void refresh()
void setSortKey(const QVariant &key)
Sets a custom sorting key for the item.
QString providerKey() const
Returns provider key.
Definition: qgsdataitem.h:499
QgsErrorItem(QgsDataItem *parent, const QString &error, const QString &path)
QString name() const
Returns the name of the item (the displayed text for the item).
Definition: qgsdataitem.h:300
void dataChanged(QgsDataItem *item)
QgsMapLayerType type() const
Returns the type of the layer.
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
QVariant mSortKey
Custom sort key. If invalid, name() will be used for sorting instead.
Definition: qgsdataitem.h:383
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
void connectionsChanged()
Emitted when the provider&#39;s connections of the child items have changed This signal is normally forwa...
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Added in 3.2.
Definition: qgsdataitem.h:469
QString mProviderKey
The provider key.
Definition: qgsdataitem.h:542
QgsMapLayerType mapLayerType() const
Returns QgsMapLayerType.
QgsDataCollectionItem(QgsDataItem *parent, const QString &name, const QString &path=QString())
QString name
Human readable name to be used e.g. in layer tree.
static QString pathComponent(const QString &component)
Create path component replacing path separators.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
virtual QIcon icon()
void setState(State state) override
Set item state.
QString mIconName
Definition: qgsdataitem.h:378
State mState
Definition: qgsdataitem.h:370
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
static QIcon iconLine()
Definition: qgsdataitem.cpp:57
Type type() const
Definition: qgsdataitem.h:281
static QIcon iconPoint()
Definition: qgsdataitem.cpp:52
virtual Q_DECL_DEPRECATED bool rename(const QString &name)
Sets a new name for the item, and returns true if the item was successfully renamed.
virtual void depopulate()
Remove children recursively and set as not populated. This is used when refreshing collapsed items...
QVariant sortKey() const override
Returns the sorting key for the item.
void setToolTip(const QString &msg)
Definition: qgsdataitem.h:340
static QIcon iconDefault()
Definition: qgsdataitem.cpp:82
QgsDirectoryItem(QgsDataItem *parent, const QString &name, const QString &path)
void endRemoveItems()
State state() const
A zip file: contains layers, using GDAL/OGR VSIFILE mechanism.
Definition: qgsdataitem.h:768
virtual bool equal(const QgsDataItem *other)
Returns true if this item is equal to another item (by testing item type and path).
QgsMimeDataUtils::Uri mimeUri() const override
Returns mime URI for the data item.
static void deleteLater(QVector< QgsDataItem *> &items)
QList< QgsDataItemProvider * > providers() const
Returns the list of available providers.
QString mFilePath
Definition: qgsdataitem.h:773
QgsDataItem * parent() const
Gets item parent.
Definition: qgsdataitem.h:286
static QIcon iconPolygon()
Definition: qgsdataitem.cpp:62
void beginRemoveItems(QgsDataItem *parent, int first, int last)
QgsMimeDataUtils::Uri mimeUri() const override
Returns mime URI for the data item.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QString mName
Definition: qgsdataitem.h:371
static QIcon iconDir()
Returns the standard browser directory icon.
QStringList mZipFileList
Definition: qgsdataitem.h:775
virtual Q_DECL_DEPRECATED bool deleteLayer()
Delete this layer item Use QgsDataItemGuiProvider::deleteLayer instead.
QgsDirectoryParamWidget(const QString &path, QWidget *parent=nullptr)
QgsZipItem(QgsDataItem *parent, const QString &name, const QString &path)
virtual bool handleDoubleClick()
Called when a user double clicks on the item.
static QgsDataItem * itemFromPath(QgsDataItem *parent, const QString &path, const QString &name)
Creates a new data item from the specified path.
Children not yet created.
Definition: qgsdataitem.h:104
Creating children in separate thread (populating or refreshing)
Definition: qgsdataitem.h:105
void updateIcon()
Will request a repaint of this icon.
virtual void refreshConnections()
Refresh connections: update GUI and emit signal.
static LayerType typeFromMapLayer(QgsMapLayer *layer)
Returns the layer item type corresponding to a QgsMapLayer layer.
bool hasChildren()
static bool hiddenPath(const QString &path)
Check if the given path is hidden from the browser model.
bool equal(const QgsDataItem *other) override
Returns true if this item is equal to another item (by testing item type and path).
static QIcon openDirIcon()
Shared open directory icon.
Definition: qgsdataitem.cpp:92
QgsDataItem(QgsDataItem::Type type, QgsDataItem *parent, const QString &name, const QString &path)
Create new data item.
QIcon icon() override
A directory: contains subdirectories and layers.
Definition: qgsdataitem.h:611
static QStringList sProviderNames
Definition: qgsdataitem.h:786
Base class for all items in the model.
Definition: qgsdataitem.h:49
void mousePressEvent(QMouseEvent *event) override
Capabilities mCapabilities
Definition: qgsdataitem.h:367
virtual void setState(State state)
Set item state.
virtual void addChildItem(QgsDataItem *child, bool refresh=false)
Inserts a new child item.
void setName(const QString &name)
Sets the name of the item (the displayed text for the item).
Can create children. Even items without this capability may have children, but cannot create them...
Definition: qgsdataitem.h:224
QVector< QgsDataItem * > children() const
Definition: qgsdataitem.h:292
QString mPath
Definition: qgsdataitem.h:376
QVector< QgsDataItem * > createChildren() override
Create children.
QString mUri
The URI.
Definition: qgsdataitem.h:544
static QIcon iconZip()
void moveToThread(QThread *targetThread)
Move object and all its descendants to thread.
TODO: move to qgis_gui for QGIS 4.
Definition: qgsdataitem.h:702
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Contains various Favorites directories.
Definition: qgsdataitem.h:721
QVector< QgsDataItem * > createChildren() override
Create children.
void removeDirectory(QgsDirectoryItem *item)
Removes an existing directory from the favorites group.
QString mVsiPrefix
Definition: qgsdataitem.h:774
QgsMimeDataUtils::Uri mimeUri() const override
Returns mime URI for the data item.
Item can be renamed.
Definition: qgsdataitem.h:227
QStringList supportedFormats() const
Returns the supported formats.
Definition: qgsdataitem.h:511
static QString layerTypeAsString(LayerType layerType)
Returns the string representation of the given layerType.
void addDirectory(const QString &directory, const QString &name=QString())
Adds a new directory to the favorites group.
void setParent(QgsDataItem *parent)
Set item parent and connect / disconnect parent to / from item signals.
void renameFavorite(const QString &path, const QString &name)
Renames the stored favorite with corresponding path a new name.
Animated icon is keeping an animation running if there are listeners connected to frameChanged...
QString uri() const
Returns layer uri or empty string if layer cannot be created.
Definition: qgsdataitem.h:496
QgsDataItem * mParent
Definition: qgsdataitem.h:368
virtual QList< QAction * > actions(QWidget *parent)
Returns the list of actions available for this item.
bool deferredDelete()
The item is scheduled to be deleted.
Definition: qgsdataitem.h:364
static QIcon homeDirIcon()
Shared home directory icon.
Definition: qgsdataitem.cpp:97
QString dirPath() const
Returns the full path to the directory the item represents.
Definition: qgsdataitem.h:633
QIcon icon() const
Gets the icons representation in the current frame.
static QIcon iconFavorites()
Icon for favorites group.
QString providerKey
For "vector" / "raster" type: provider id.
void endInsertItems()
QString uri
Identifier of the data source recognized by its providerKey.
virtual QVector< QgsDataItem * > createChildren()
Create children.
static QIcon iconDataCollection()
Returns the standard browser data collection icon.
Definition: qgsdataitem.cpp:87
QVector< QgsDataItem * > createChildren() override
Create children.
Item that represents a layer that can be opened with one of the providers.
Definition: qgsdataitem.h:452
static QIcon iconTable()
Definition: qgsdataitem.cpp:67
QgsProjectItem(QgsDataItem *parent, const QString &name, const QString &path)
A data item holding a reference to a QGIS project file.
static int findItem(QVector< QgsDataItem *> items, QgsDataItem *item)
virtual QgsDataItem * removeChildItem(QgsDataItem *child)
Removes a child item and returns it without deleting it.
QgsMapLayerType
Types of layers that can be added to a map.
Definition: qgsmaplayer.h:66
virtual void deleteChildItem(QgsDataItem *child)
Removes and deletes a child item, emitting relevant signals to the model.
QgsWkbTypes::Type wkbType
WKB type, if associated with a vector layer, or QgsWkbTypes::Unknown if not yet known.
virtual void populate(const QVector< QgsDataItem *> &children)
QgsLayerItem(QgsDataItem *parent, const QString &name, const QString &path, const QString &uri, LayerType layerType, const QString &providerKey)
~QgsDataItem() override
Data item that can be used to represent QGIS projects.
Definition: qgsdataitem.h:662
Children created.
Definition: qgsdataitem.h:106
LayerType mLayerType
The layer type.
Definition: qgsdataitem.h:546
bool equal(const QgsDataItem *other) override
Returns true if this item is equal to another item (by testing item type and path).
static QIcon iconMesh()
Returns icon for mesh layer type.
Definition: qgsdataitem.cpp:77
QVector< QgsDataItem * > mChildren
Definition: qgsdataitem.h:369
QStringList getZipFileList()
virtual void deleteLater()
Safely delete the item:
static QgsDataItemProviderRegistry * dataItemProviderRegistry()
Returns the application&#39;s data item provider registry, which keeps a list of data item providers that...
Q_DECL_DEPRECATED QWidget * paramWidget() override
Returns source widget from data item for QgsBrowserPropertiesWidget.
CreateChildren() is fast enough to be run in main thread when refreshing items, most root items (wms...
Definition: qgsdataitem.h:225
bool connectFrameChanged(const typename QtPrivate::FunctionPointer< Func1 >::Object *receiver, Func1 slot)
Connect a slot that will be notified repeatedly whenever a frame changes and which should request the...
Represents a favorite item.
Definition: qgsdataitem.h:82
This is the interface for those who want to add custom data items to the browser tree.
QStringList supportedCrs() const
Returns the supported CRS.
Definition: qgsdataitem.h:505
void stateChanged(QgsDataItem *item, QgsDataItem::State oldState)
static QString vsiPrefix(const QString &uri)
Definition: qgsdataitem.h:788
QMap< QString, QIcon > mIconMap
Definition: qgsdataitem.h:380
Added in 2.10.
Definition: qgsdataitem.h:468
virtual Capabilities capabilities2() const
Returns the capabilities for the data item.
Definition: qgsdataitem.h:265
static QString iconName(LayerType layerType)
Returns the icon name of the given layerType.