QGIS API Documentation  2.7.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
qgsbrowsermodel.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsbrowsermodel.cpp
3  ---------------------
4  begin : July 2011
5  copyright : (C) 2011 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include <QDir>
16 #include <QApplication>
17 #include <QStyle>
18 #include <QtConcurrentMap>
19 
20 #include "qgis.h"
21 #include "qgsapplication.h"
22 #include "qgsdataprovider.h"
23 #include "qgsmimedatautils.h"
24 #include "qgslogger.h"
25 #include "qgsproviderregistry.h"
26 
27 #include "qgsbrowsermodel.h"
28 #include "qgsproject.h"
29 
30 #include <QSettings>
31 
33  : mItem( item )
34 {
35 }
36 
38 {
39 }
40 
41 // sort function for QList<QgsDataItem*>, e.g. sorted/grouped provider listings
43 {
44  return QString::localeAwareCompare( a->name(), b->name() ) < 0;
45 }
46 
48  : QAbstractItemModel( parent )
49  , mFavourites( 0 )
50  , mProjectHome( 0 )
51 {
52  connect( QgsProject::instance(), SIGNAL( readProject( const QDomDocument & ) ), this, SLOT( updateProjectHome() ) );
53  connect( QgsProject::instance(), SIGNAL( writeProject( QDomDocument & ) ), this, SLOT( updateProjectHome() ) );
54  addRootItems();
55 }
56 
58 {
60 }
61 
63 {
64  QString home = QgsProject::instance()->homePath();
65  if ( mProjectHome && mProjectHome->path() == home )
66  return;
67 
68  int idx = mRootItems.indexOf( mProjectHome );
69 
70  // using layoutAboutToBeChanged() was messing expanded items
71  if ( idx >= 0 )
72  {
73  beginRemoveRows( QModelIndex(), idx, idx );
74  mRootItems.remove( idx );
75  endRemoveRows();
76  }
77  delete mProjectHome;
78  mProjectHome = home.isNull() ? 0 : new QgsDirectoryItem( NULL, tr( "Project home" ), home, "project:" + home );
79  if ( mProjectHome )
80  {
82 
83  beginInsertRows( QModelIndex(), 0, 0 );
84  mRootItems.insert( 0, mProjectHome );
85  endInsertRows();
86  }
87 }
88 
90 {
92 
93  // give the home directory a prominent second place
94  QgsDirectoryItem *item = new QgsDirectoryItem( NULL, tr( "Home" ), QDir::homePath(), "home:" + QDir::homePath() );
95  QStyle *style = QApplication::style();
96  QIcon homeIcon( style->standardPixmap( QStyle::SP_DirHomeIcon ) );
97  item->setIcon( homeIcon );
98  connectItem( item );
99  mRootItems << item;
100 
101  // add favourite directories
102  mFavourites = new QgsFavouritesItem( NULL, tr( "Favourites" ) );
103  if ( mFavourites )
104  {
107  }
108 
109  // add drives
110  foreach ( QFileInfo drive, QDir::drives() )
111  {
112  QString path = drive.absolutePath();
113  QgsDirectoryItem *item = new QgsDirectoryItem( NULL, path, path );
114 
115  connectItem( item );
116  mRootItems << item;
117  }
118 
119 #ifdef Q_WS_MAC
120  QString path = QString( "/Volumes" );
121  QgsDirectoryItem *vols = new QgsDirectoryItem( NULL, path, path );
122  connectItem( vols );
123  mRootItems << vols;
124 #endif
125 
126  // Add non file top level items
127  QStringList providersList = QgsProviderRegistry::instance()->providerList();
128 
129  // container for displaying providers as sorted groups (by QgsDataProvider::DataCapability enum)
130  QMap<int, QgsDataItem *> providerMap;
131 
132  foreach ( QString key, providersList )
133  {
134  QLibrary *library = QgsProviderRegistry::instance()->providerLibrary( key );
135  if ( !library )
136  continue;
137 
138  dataCapabilities_t * dataCapabilities = ( dataCapabilities_t * ) cast_to_fptr( library->resolve( "dataCapabilities" ) );
139  if ( !dataCapabilities )
140  {
141  QgsDebugMsg( library->fileName() + " does not have dataCapabilities" );
142  continue;
143  }
144 
145  int capabilities = dataCapabilities();
146  if ( capabilities == QgsDataProvider::NoDataCapabilities )
147  {
148  QgsDebugMsg( library->fileName() + " does not have any dataCapabilities" );
149  continue;
150  }
151 
152  dataItem_t *dataItem = ( dataItem_t * ) cast_to_fptr( library->resolve( "dataItem" ) );
153  if ( !dataItem )
154  {
155  QgsDebugMsg( library->fileName() + " does not have dataItem" );
156  continue;
157  }
158 
159  QgsDataItem *item = dataItem( "", NULL ); // empty path -> top level
160  if ( item )
161  {
162  QgsDebugMsg( "Add new top level item : " + item->name() );
163  connectItem( item );
164  providerMap.insertMulti( capabilities, item );
165  }
166  }
167 
168  // add as sorted groups by QgsDataProvider::DataCapability enum
169  foreach ( int key, providerMap.uniqueKeys() )
170  {
171  QList<QgsDataItem *> providerGroup = providerMap.values( key );
172  if ( providerGroup.size() > 1 )
173  {
174  qSort( providerGroup.begin(), providerGroup.end(), cmpByDataItemName_ );
175  }
176 
177  foreach ( QgsDataItem * ditem, providerGroup )
178  {
179  mRootItems << ditem;
180  }
181  }
182 }
183 
185 {
186  foreach ( QgsDataItem* item, mRootItems )
187  {
188  delete item;
189  }
190 
191  mRootItems.clear();
192 }
193 
194 
195 Qt::ItemFlags QgsBrowserModel::flags( const QModelIndex & index ) const
196 {
197  if ( !index.isValid() )
198  return 0;
199 
200  Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
201 
202  QgsDataItem* ptr = ( QgsDataItem* ) index.internalPointer();
203  if ( ptr->type() == QgsDataItem::Layer )
204  {
205  flags |= Qt::ItemIsDragEnabled;
206  }
207  if ( ptr->acceptDrop() )
208  flags |= Qt::ItemIsDropEnabled;
209  return flags;
210 }
211 
212 QVariant QgsBrowserModel::data( const QModelIndex &index, int role ) const
213 {
214  if ( !index.isValid() )
215  return QVariant();
216 
217  QgsDataItem *item = dataItem( index );
218  if ( !item )
219  {
220  return QVariant();
221  }
222  else if ( role == Qt::DisplayRole )
223  {
224  return item->name();
225  }
226  else if ( role == Qt::ToolTipRole )
227  {
228  return item->toolTip();
229  }
230  else if ( role == Qt::DecorationRole && index.column() == 0 )
231  {
232  return item->icon();
233  }
234  else
235  {
236  // unsupported role
237  return QVariant();
238  }
239 }
240 
241 QVariant QgsBrowserModel::headerData( int section, Qt::Orientation orientation, int role ) const
242 {
243  Q_UNUSED( section );
244  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole )
245  {
246  return QVariant( "header" );
247  }
248 
249  return QVariant();
250 }
251 
252 int QgsBrowserModel::rowCount( const QModelIndex &parent ) const
253 {
254  //QgsDebugMsg(QString("isValid = %1 row = %2 column = %3").arg(parent.isValid()).arg(parent.row()).arg(parent.column()));
255 
256  if ( !parent.isValid() )
257  {
258  // root item: its children are top level items
259  return mRootItems.count(); // mRoot
260  }
261  else
262  {
263  // ordinary item: number of its children
264  QgsDataItem *item = dataItem( parent );
265  //if ( item ) QgsDebugMsg(QString("path = %1 rowCount = %2").arg(item->path()).arg(item->rowCount()) );
266  return item ? item->rowCount() : 0;
267  }
268 }
269 
270 bool QgsBrowserModel::hasChildren( const QModelIndex &parent ) const
271 {
272  if ( !parent.isValid() )
273  return true; // root item: its children are top level items
274 
275  QgsDataItem *item = dataItem( parent );
276  return item && item->hasChildren();
277 }
278 
279 int QgsBrowserModel::columnCount( const QModelIndex &parent ) const
280 {
281  Q_UNUSED( parent );
282  return 1;
283 }
284 
285 QModelIndex QgsBrowserModel::findPath( QString path, Qt::MatchFlag matchFlag )
286 {
287  QModelIndex theIndex; // starting from root
288  bool foundChild = true;
289 
290  while ( foundChild )
291  {
292  foundChild = false; // assume that the next child item will not be found
293 
294  for ( int i = 0; i < rowCount( theIndex ); i++ )
295  {
296  QModelIndex idx = index( i, 0, theIndex );
297  QgsDataItem *item = dataItem( idx );
298  if ( !item )
299  return QModelIndex(); // an error occurred
300 
301  if ( item->path() == path )
302  {
303  QgsDebugMsg( "Arrived " + item->path() );
304  return idx; // we have found the item we have been looking for
305  }
306 
307  // paths are slash separated identifier
308  if ( path.startsWith( item->path() + "/" ) )
309  {
310  foundChild = true;
311  theIndex = idx;
312  break;
313  }
314  }
315  }
316 
317  if ( matchFlag == Qt::MatchStartsWith )
318  return theIndex;
319 
320  QgsDebugMsg( "path not found" );
321  return QModelIndex(); // not found
322 }
323 
325 {
326  // TODO: put items creating currently children in threads to deleteLater (does not seem urget because reload() is not used in QGIS)
327  beginResetModel();
328  removeRootItems();
329  addRootItems();
330  endResetModel();
331 }
332 
333 QModelIndex QgsBrowserModel::index( int row, int column, const QModelIndex &parent ) const
334 {
335  QgsDataItem *p = dataItem( parent );
336  const QVector<QgsDataItem*> &items = p ? p->children() : mRootItems;
337  QgsDataItem *item = items.value( row, 0 );
338  return item ? createIndex( row, column, item ) : QModelIndex();
339 }
340 
341 QModelIndex QgsBrowserModel::parent( const QModelIndex &index ) const
342 {
343  QgsDataItem *item = dataItem( index );
344  if ( !item )
345  return QModelIndex();
346 
347  return findItem( item->parent() );
348 }
349 
350 QModelIndex QgsBrowserModel::findItem( QgsDataItem *item, QgsDataItem *parent ) const
351 {
352  const QVector<QgsDataItem*> &items = parent ? parent->children() : mRootItems;
353 
354  for ( int i = 0; i < items.size(); i++ )
355  {
356  if ( items[i] == item )
357  return createIndex( i, 0, item );
358 
359  QModelIndex childIndex = findItem( item, items[i] );
360  if ( childIndex.isValid() )
361  return childIndex;
362  }
363 
364  return QModelIndex();
365 }
366 
367 void QgsBrowserModel::beginInsertItems( QgsDataItem *parent, int first, int last )
368 {
369  QgsDebugMsgLevel( "parent mPath = " + parent->path(), 3 );
370  QModelIndex idx = findItem( parent );
371  if ( !idx.isValid() )
372  return;
373  QgsDebugMsgLevel( "valid", 3 );
374  beginInsertRows( idx, first, last );
375  QgsDebugMsgLevel( "end", 3 );
376 }
378 {
379  QgsDebugMsgLevel( "Entered", 3 );
380  endInsertRows();
381 }
382 void QgsBrowserModel::beginRemoveItems( QgsDataItem *parent, int first, int last )
383 {
384  QgsDebugMsgLevel( "parent mPath = " + parent->path(), 3 );
385  QModelIndex idx = findItem( parent );
386  if ( !idx.isValid() )
387  return;
388  beginRemoveRows( idx, first, last );
389 }
391 {
392  QgsDebugMsgLevel( "Entered", 3 );
393  endRemoveRows();
394 }
396 {
397  QgsDebugMsgLevel( "Entered", 3 );
398  QModelIndex idx = findItem( item );
399  if ( !idx.isValid() )
400  return;
401  emit dataChanged( idx, idx );
402 }
404 {
405  QgsDebugMsg( "Entered" );
406  if ( !item )
407  return;
408  QModelIndex idx = findItem( item );
409  if ( !idx.isValid() )
410  return;
411  QgsDebugMsg( QString( "item %1 state changed %2 -> %3" ).arg( item->path() ).arg( oldState ).arg( item->state() ) );
412  emit stateChanged( idx, oldState );
413 }
415 {
416  connect( item, SIGNAL( beginInsertItems( QgsDataItem*, int, int ) ),
417  this, SLOT( beginInsertItems( QgsDataItem*, int, int ) ) );
418  connect( item, SIGNAL( endInsertItems() ),
419  this, SLOT( endInsertItems() ) );
420  connect( item, SIGNAL( beginRemoveItems( QgsDataItem*, int, int ) ),
421  this, SLOT( beginRemoveItems( QgsDataItem*, int, int ) ) );
422  connect( item, SIGNAL( endRemoveItems() ),
423  this, SLOT( endRemoveItems() ) );
424  connect( item, SIGNAL( dataChanged( QgsDataItem* ) ),
425  this, SLOT( itemDataChanged( QgsDataItem* ) ) );
426  connect( item, SIGNAL( stateChanged( QgsDataItem*, QgsDataItem::State ) ),
427  this, SLOT( itemStateChanged( QgsDataItem*, QgsDataItem::State ) ) );
428 }
429 
430 QStringList QgsBrowserModel::mimeTypes() const
431 {
432  QStringList types;
433  // In theory the mime type convention is: application/x-vnd.<vendor>.<application>.<type>
434  // but it seems a bit over formalized. Would be an application/x-qgis-uri better?
435  types << "application/x-vnd.qgis.qgis.uri";
436  return types;
437 }
438 
439 QMimeData * QgsBrowserModel::mimeData( const QModelIndexList &indexes ) const
440 {
442  foreach ( const QModelIndex &index, indexes )
443  {
444  if ( index.isValid() )
445  {
446  QgsDataItem* ptr = ( QgsDataItem* ) index.internalPointer();
447  if ( ptr->type() != QgsDataItem::Layer ) continue;
448  QgsLayerItem *layer = ( QgsLayerItem* ) ptr;
449  lst.append( QgsMimeDataUtils::Uri( layer ) );
450  }
451  }
452  return QgsMimeDataUtils::encodeUriList( lst );
453 }
454 
455 bool QgsBrowserModel::dropMimeData( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent )
456 {
457  Q_UNUSED( row );
458  Q_UNUSED( column );
459 
460  QgsDataItem* destItem = dataItem( parent );
461  if ( !destItem )
462  {
463  QgsDebugMsg( "DROP PROBLEM!" );
464  return false;
465  }
466 
467  return destItem->handleDrop( data, action );
468 }
469 
470 QgsDataItem *QgsBrowserModel::dataItem( const QModelIndex &idx ) const
471 {
472  void *v = idx.internalPointer();
473  QgsDataItem *d = reinterpret_cast<QgsDataItem*>( v );
474  Q_ASSERT( !v || d );
475  return d;
476 }
477 
478 bool QgsBrowserModel::canFetchMore( const QModelIndex & parent ) const
479 {
480  QgsDataItem* item = dataItem( parent );
481  // if ( item )
482  // QgsDebugMsg( QString( "path = %1 canFetchMore = %2" ).arg( item->path() ).arg( item && ! item->isPopulated() ) );
483  return ( item && item->state() == QgsDataItem::NotPopulated );
484 }
485 
486 void QgsBrowserModel::fetchMore( const QModelIndex & parent )
487 {
488  QgsDebugMsg( "Entered" );
489  QgsDataItem* item = dataItem( parent );
490 
491  if ( !item || item->state() == QgsDataItem::Populating || item->state() == QgsDataItem::Populated )
492  return;
493 
494  QgsDebugMsg( "path = " + item->path() );
495 
496  item->populate();
497 }
498 
499 /* Refresh dir path */
500 void QgsBrowserModel::refresh( QString path )
501 {
502  QModelIndex index = findPath( path );
503  refresh( index );
504 }
505 
506 /* Refresh item */
507 void QgsBrowserModel::refresh( const QModelIndex& theIndex )
508 {
509  QgsDataItem *item = dataItem( theIndex );
510  if ( !item || item->state() == QgsDataItem::Populating )
511  return;
512 
513  QgsDebugMsg( "Refresh " + item->path() );
514 
515  item->refresh();
516 }
517 
519 {
520  Q_ASSERT( mFavourites );
521  mFavourites->addDirectory( favDir );
522 }
523 
524 void QgsBrowserModel::removeFavourite( const QModelIndex &index )
525 {
526  QgsDirectoryItem *item = dynamic_cast<QgsDirectoryItem *>( dataItem( index ) );
527  if ( !item )
528  return;
529 
530  mFavourites->removeDirectory( item );
531 }
virtual Qt::ItemFlags flags(const QModelIndex &index) const
Used by other components to obtain information about each item provided by the model.
Contains various Favourites directories.
Definition: qgsdataitem.h:395
static unsigned index
bool hasChildren(const QModelIndex &parent=QModelIndex()) const
void removeDirectory(QgsDirectoryItem *item)
static bool cmpByDataItemName_(QgsDataItem *a, QgsDataItem *b)
virtual int columnCount(const QModelIndex &parent=QModelIndex()) const
Provides the number of columns of data exposed by the model.
QVector< QgsDataItem * > children() const
Definition: qgsdataitem.h:144
QString name() const
Definition: qgsdataitem.h:146
QgsDataItem * parent() const
Get item parent.
Definition: qgsdataitem.h:140
static QgsProviderRegistry * instance(QString pluginPath=QString::null)
means of accessing canonical single instance
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void addDirectory(QString favIcon)
void removeFavourite(const QModelIndex &index)
virtual bool handleDrop(const QMimeData *, Qt::DropAction)
Definition: qgsdataitem.h:108
QgsFavouritesItem * mFavourites
void beginRemoveItems(QgsDataItem *parent, int first, int last)
virtual QIcon icon()
void fetchMore(const QModelIndex &parent)
void connectItem(QgsDataItem *item)
virtual QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const
Provides views with information to show in their headers.
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const
Provides the number of rows of data exposed by the model.
State state() const
virtual void populate(QVector< QgsDataItem * > children)
void itemStateChanged(QgsDataItem *item, QgsDataItem::State oldState)
QString homePath() const
Return project's home path.
void setIcon(QIcon icon)
Definition: qgsdataitem.h:153
virtual void refresh(QVector< QgsDataItem * > children)
QStringList providerList() const
Return list of available providers by their keys.
QgsDirectoryItem * mProjectHome
virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
Handles the data supplied by a drag and drop operation that ended with the given action.
QgsBrowserModel(QObject *parent=0)
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
QgsDataItem * dataItem(const QModelIndex &idx) const
QModelIndex findPath(QString path, Qt::MatchFlag matchFlag=Qt::MatchExactly)
Return index of item with given path.
bool canFetchMore(const QModelIndex &parent) const
Children not yet created.
Definition: qgsdataitem.h:72
Creating children in separate thread (populating or refreshing)
Definition: qgsdataitem.h:73
QString path() const
Definition: qgsdataitem.h:147
Type type() const
Definition: qgsdataitem.h:137
QgsBrowserWatcher(QgsDataItem *item)
bool hasChildren()
virtual QModelIndex parent(const QModelIndex &index) const
Returns the parent of the model item with the given index.
virtual QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const
Returns the index of the item in the model specified by the given row, column and parent index...
A directory: contains subdirectories and layers.
Definition: qgsdataitem.h:313
QModelIndex findItem(QgsDataItem *item, QgsDataItem *parent=0) const
static QMimeData * encodeUriList(UriList layers)
Base class for all items in the model.
Definition: qgsdataitem.h:43
QLibrary * providerLibrary(const QString &providerKey) const
void beginInsertItems(QgsDataItem *parent, int first, int last)
virtual QStringList mimeTypes() const
Returns a list of mime that can describe model indexes.
void refresh(QString path)
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:362
void stateChanged(const QModelIndex &index, QgsDataItem::State oldState)
Emitted when item children fetch was finished.
virtual QMimeData * mimeData(const QModelIndexList &indexes) const
Returns an object that contains serialized items of data corresponding to the list of indexes specifi...
Item that represents a layer that can be opened with one of the providers.
Definition: qgsdataitem.h:246
QgsDataItem * dataItem_t(QString, QgsDataItem *)
Definition: qgsdataitem.h:38
void(*)() cast_to_fptr(void *p)
Definition: qgis.h:301
void itemDataChanged(QgsDataItem *item)
QString toolTip() const
Definition: qgsdataitem.h:157
children created
Definition: qgsdataitem.h:74
virtual bool acceptDrop()
Definition: qgsdataitem.h:105
int dataCapabilities_t()
QList< Uri > UriList
virtual QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
Used to supply item data to views and delegates.
#define tr(sourceText)
QVector< QgsDataItem * > mRootItems
void addFavouriteDirectory(QString favDir)