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