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