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