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