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