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