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