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