QGIS API Documentation  2.4.0-Chugiak
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups 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 
19 #include "qgis.h"
20 #include "qgsapplication.h"
21 #include "qgsdataprovider.h"
22 #include "qgsmimedatautils.h"
23 #include "qgslogger.h"
24 #include "qgsproviderregistry.h"
25 
26 #include "qgsbrowsermodel.h"
27 #include "qgsproject.h"
28 
29 #include <QSettings>
30 
31 // sort function for QList<QgsDataItem*>, e.g. sorted/grouped provider listings
33 {
34  return QString::localeAwareCompare( a->name(), b->name() ) < 0;
35 }
36 
38  : QAbstractItemModel( parent )
39  , mFavourites( 0 )
40  , mProjectHome( 0 )
41 {
42  connect( QgsProject::instance(), SIGNAL( readProject( const QDomDocument & ) ), this, SLOT( updateProjectHome() ) );
43  connect( QgsProject::instance(), SIGNAL( writeProject( QDomDocument & ) ), this, SLOT( updateProjectHome() ) );
44  addRootItems();
45 }
46 
48 {
50 }
51 
53 {
54  QString home = QgsProject::instance()->homePath();
55  if ( mProjectHome && mProjectHome->path() == home )
56  return;
57 
58  emit layoutAboutToBeChanged();
59 
60  int idx = mRootItems.indexOf( mProjectHome );
61  delete mProjectHome;
62  mProjectHome = home.isNull() ? 0 : new QgsDirectoryItem( NULL, tr( "Project home" ), home );
63  if ( mProjectHome )
64  {
66  if ( idx < 0 )
67  mRootItems.insert( 0, mProjectHome );
68  else
69  mRootItems.replace( idx, mProjectHome );
70  }
71  else if ( idx >= 0 )
72  {
73  mRootItems.remove( idx );
74  }
75 
76  emit layoutChanged();
77 }
78 
80 {
82 
83  // give the home directory a prominent second place
84  QgsDirectoryItem *item = new QgsDirectoryItem( NULL, tr( "Home" ), QDir::homePath() );
85  QStyle *style = QApplication::style();
86  QIcon homeIcon( style->standardPixmap( QStyle::SP_DirHomeIcon ) );
87  item->setIcon( homeIcon );
88  connectItem( item );
89  mRootItems << item;
90 
91  // add favourite directories
92  mFavourites = new QgsFavouritesItem( NULL, tr( "Favourites" ) );
93  if ( mFavourites )
94  {
97  }
98 
99  // add drives
100  foreach ( QFileInfo drive, QDir::drives() )
101  {
102  QString path = drive.absolutePath();
103  QgsDirectoryItem *item = new QgsDirectoryItem( NULL, path, path );
104 
105  connectItem( item );
106  mRootItems << item;
107  }
108 
109 #ifdef Q_WS_MAC
110  QString path = QString( "/Volumes" );
111  QgsDirectoryItem *vols = new QgsDirectoryItem( NULL, path, path );
112  connectItem( vols );
113  mRootItems << vols;
114 #endif
115 
116  // Add non file top level items
117  QStringList providersList = QgsProviderRegistry::instance()->providerList();
118 
119  // container for displaying providers as sorted groups (by QgsDataProvider::DataCapability enum)
120  QMap<int, QgsDataItem *> providerMap;
121 
122  foreach ( QString key, providersList )
123  {
124  QLibrary *library = QgsProviderRegistry::instance()->providerLibrary( key );
125  if ( !library )
126  continue;
127 
128  dataCapabilities_t * dataCapabilities = ( dataCapabilities_t * ) cast_to_fptr( library->resolve( "dataCapabilities" ) );
129  if ( !dataCapabilities )
130  {
131  QgsDebugMsg( library->fileName() + " does not have dataCapabilities" );
132  continue;
133  }
134 
135  int capabilities = dataCapabilities();
136  if ( capabilities == QgsDataProvider::NoDataCapabilities )
137  {
138  QgsDebugMsg( library->fileName() + " does not have any dataCapabilities" );
139  continue;
140  }
141 
142  dataItem_t *dataItem = ( dataItem_t * ) cast_to_fptr( library->resolve( "dataItem" ) );
143  if ( !dataItem )
144  {
145  QgsDebugMsg( library->fileName() + " does not have dataItem" );
146  continue;
147  }
148 
149  QgsDataItem *item = dataItem( "", NULL ); // empty path -> top level
150  if ( item )
151  {
152  QgsDebugMsg( "Add new top level item : " + item->name() );
153  connectItem( item );
154  providerMap.insertMulti( capabilities, item );
155  }
156  }
157 
158  // add as sorted groups by QgsDataProvider::DataCapability enum
159  foreach ( int key, providerMap.uniqueKeys() )
160  {
161  QList<QgsDataItem *> providerGroup = providerMap.values( key );
162  if ( providerGroup.size() > 1 )
163  {
164  qSort( providerGroup.begin(), providerGroup.end(), cmpByDataItemName_ );
165  }
166 
167  foreach ( QgsDataItem * ditem, providerGroup )
168  {
169  mRootItems << ditem;
170  }
171  }
172 }
173 
175 {
176  foreach ( QgsDataItem* item, mRootItems )
177  {
178  delete item;
179  }
180 
181  mRootItems.clear();
182 }
183 
184 
185 Qt::ItemFlags QgsBrowserModel::flags( const QModelIndex & index ) const
186 {
187  if ( !index.isValid() )
188  return 0;
189 
190  Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
191 
192  QgsDataItem* ptr = ( QgsDataItem* ) index.internalPointer();
193  if ( ptr->type() == QgsDataItem::Layer )
194  {
195  flags |= Qt::ItemIsDragEnabled;
196  }
197  if ( ptr->acceptDrop() )
198  flags |= Qt::ItemIsDropEnabled;
199  return flags;
200 }
201 
202 QVariant QgsBrowserModel::data( const QModelIndex &index, int role ) const
203 {
204  if ( !index.isValid() )
205  return QVariant();
206 
207  QgsDataItem *item = dataItem( index );
208  if ( !item )
209  {
210  return QVariant();
211  }
212  else if ( role == Qt::DisplayRole )
213  {
214  return item->name();
215  }
216  else if ( role == Qt::ToolTipRole )
217  {
218  return item->toolTip();
219  }
220  else if ( role == Qt::DecorationRole && index.column() == 0 )
221  {
222  return item->icon();
223  }
224  else
225  {
226  // unsupported role
227  return QVariant();
228  }
229 }
230 
231 QVariant QgsBrowserModel::headerData( int section, Qt::Orientation orientation, int role ) const
232 {
233  Q_UNUSED( section );
234  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole )
235  {
236  return QVariant( "header" );
237  }
238 
239  return QVariant();
240 }
241 
242 int QgsBrowserModel::rowCount( const QModelIndex &parent ) const
243 {
244  //qDebug("rowCount: idx: (valid %d) %d %d", parent.isValid(), parent.row(), parent.column());
245 
246  if ( !parent.isValid() )
247  {
248  // root item: its children are top level items
249  return mRootItems.count(); // mRoot
250  }
251  else
252  {
253  // ordinary item: number of its children
254  QgsDataItem *item = dataItem( parent );
255  return item ? item->rowCount() : 0;
256  }
257 }
258 
259 bool QgsBrowserModel::hasChildren( const QModelIndex &parent ) const
260 {
261  if ( !parent.isValid() )
262  return true; // root item: its children are top level items
263 
264  QgsDataItem *item = dataItem( parent );
265  return item && item->hasChildren();
266 }
267 
268 int QgsBrowserModel::columnCount( const QModelIndex &parent ) const
269 {
270  Q_UNUSED( parent );
271  return 1;
272 }
273 
274 QModelIndex QgsBrowserModel::findPath( QString path )
275 {
276  QModelIndex theIndex; // starting from root
277  bool foundChild = true;
278 
279  while ( foundChild )
280  {
281  foundChild = false; // assume that the next child item will not be found
282 
283  for ( int i = 0; i < rowCount( theIndex ); i++ )
284  {
285  QModelIndex idx = index( i, 0, theIndex );
286  QgsDataItem *item = dataItem( idx );
287  if ( !item )
288  return QModelIndex(); // an error occurred
289 
290  if ( item->path() == path )
291  {
292  QgsDebugMsg( "Arrived " + item->path() );
293  return idx; // we have found the item we have been looking for
294  }
295 
296  if ( path.startsWith( item->path() ) )
297  {
298  // we have found a preceding item: stop searching on this level and go deeper
299  foundChild = true;
300  theIndex = idx;
301  break;
302  }
303  }
304  }
305 
306  return QModelIndex(); // not found
307 }
308 
310 {
311  beginResetModel();
312  removeRootItems();
313  addRootItems();
314  endResetModel();
315 }
316 
317 /* Refresh dir path */
318 void QgsBrowserModel::refresh( QString path )
319 {
320  QModelIndex idx = findPath( path );
321  if ( idx.isValid() )
322  {
323  QgsDataItem* item = dataItem( idx );
324  if ( item )
325  item->refresh();
326  }
327 }
328 
329 QModelIndex QgsBrowserModel::index( int row, int column, const QModelIndex &parent ) const
330 {
331  QgsDataItem *p = dataItem( parent );
332  const QVector<QgsDataItem*> &items = p ? p->children() : mRootItems;
333  QgsDataItem *item = items.value( row, 0 );
334  return item ? createIndex( row, column, item ) : QModelIndex();
335 }
336 
337 QModelIndex QgsBrowserModel::parent( const QModelIndex &index ) const
338 {
339  QgsDataItem *item = dataItem( index );
340  if ( !item )
341  return QModelIndex();
342 
343  return findItem( item->parent() );
344 }
345 
346 QModelIndex QgsBrowserModel::findItem( QgsDataItem *item, QgsDataItem *parent ) const
347 {
348  const QVector<QgsDataItem*> &items = parent ? parent->children() : mRootItems;
349 
350  for ( int i = 0; i < items.size(); i++ )
351  {
352  if ( items[i] == item )
353  return createIndex( i, 0, item );
354 
355  QModelIndex childIndex = findItem( item, items[i] );
356  if ( childIndex.isValid() )
357  return childIndex;
358  }
359 
360  return QModelIndex();
361 }
362 
363 /* Refresh item */
364 void QgsBrowserModel::refresh( const QModelIndex& theIndex )
365 {
366  QgsDataItem *item = dataItem( theIndex );
367  if ( !item )
368  return;
369 
370  QgsDebugMsg( "Refresh " + item->path() );
371  item->refresh();
372 }
373 
374 void QgsBrowserModel::beginInsertItems( QgsDataItem *parent, int first, int last )
375 {
376  QgsDebugMsg( "parent mPath = " + parent->path() );
377  QModelIndex idx = findItem( parent );
378  if ( !idx.isValid() )
379  return;
380  QgsDebugMsg( "valid" );
381  beginInsertRows( idx, first, last );
382  QgsDebugMsg( "end" );
383 }
385 {
386  QgsDebugMsg( "Entered" );
387  endInsertRows();
388 }
389 void QgsBrowserModel::beginRemoveItems( QgsDataItem *parent, int first, int last )
390 {
391  QgsDebugMsg( "parent mPath = " + parent->path() );
392  QModelIndex idx = findItem( parent );
393  if ( !idx.isValid() )
394  return;
395  beginRemoveRows( idx, first, last );
396 }
398 {
399  QgsDebugMsg( "Entered" );
400  endRemoveRows();
401 }
403 {
404  connect( item, SIGNAL( beginInsertItems( QgsDataItem*, int, int ) ),
405  this, SLOT( beginInsertItems( QgsDataItem*, int, int ) ) );
406  connect( item, SIGNAL( endInsertItems() ),
407  this, SLOT( endInsertItems() ) );
408  connect( item, SIGNAL( beginRemoveItems( QgsDataItem*, int, int ) ),
409  this, SLOT( beginRemoveItems( QgsDataItem*, int, int ) ) );
410  connect( item, SIGNAL( endRemoveItems() ),
411  this, SLOT( endRemoveItems() ) );
412 }
413 
414 QStringList QgsBrowserModel::mimeTypes() const
415 {
416  QStringList types;
417  // In theory the mime type convention is: application/x-vnd.<vendor>.<application>.<type>
418  // but it seems a bit over formalized. Would be an application/x-qgis-uri better?
419  types << "application/x-vnd.qgis.qgis.uri";
420  return types;
421 }
422 
423 QMimeData * QgsBrowserModel::mimeData( const QModelIndexList &indexes ) const
424 {
426  foreach ( const QModelIndex &index, indexes )
427  {
428  if ( index.isValid() )
429  {
430  QgsDataItem* ptr = ( QgsDataItem* ) index.internalPointer();
431  if ( ptr->type() != QgsDataItem::Layer ) continue;
432  QgsLayerItem *layer = ( QgsLayerItem* ) ptr;
433  lst.append( QgsMimeDataUtils::Uri( layer ) );
434  }
435  }
436  return QgsMimeDataUtils::encodeUriList( lst );
437 }
438 
439 bool QgsBrowserModel::dropMimeData( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent )
440 {
441  Q_UNUSED( row );
442  Q_UNUSED( column );
443 
444  QgsDataItem* destItem = dataItem( parent );
445  if ( !destItem )
446  {
447  QgsDebugMsg( "DROP PROBLEM!" );
448  return false;
449  }
450 
451  return destItem->handleDrop( data, action );
452 }
453 
454 QgsDataItem *QgsBrowserModel::dataItem( const QModelIndex &idx ) const
455 {
456  void *v = idx.internalPointer();
457  QgsDataItem *d = reinterpret_cast<QgsDataItem*>( v );
458  Q_ASSERT( !v || d );
459  return d;
460 }
461 
462 bool QgsBrowserModel::canFetchMore( const QModelIndex & parent ) const
463 {
464  QgsDataItem* item = dataItem( parent );
465  // if ( item )
466  // QgsDebugMsg( QString( "path = %1 canFetchMore = %2" ).arg( item->path() ).arg( item && ! item->isPopulated() ) );
467  return ( item && ! item->isPopulated() );
468 }
469 
470 void QgsBrowserModel::fetchMore( const QModelIndex & parent )
471 {
472  QgsDataItem* item = dataItem( parent );
473  if ( item )
474  item->populate();
475  QgsDebugMsg( "path = " + item->path() );
476 }
477 
479 {
480  Q_ASSERT( mFavourites );
481  mFavourites->addDirectory( favDir );
482 }
483 
484 void QgsBrowserModel::removeFavourite( const QModelIndex &index )
485 {
486  QgsDirectoryItem *item = dynamic_cast<QgsDirectoryItem *>( dataItem( index ) );
487  if ( !item )
488  return;
489 
490  mFavourites->removeDirectory( item );
491 }
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:282
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.
virtual void refresh()
QVector< QgsDataItem * > children() const
Definition: qgsdataitem.h:120
QString name() const
Definition: qgsdataitem.h:122
QgsDataItem * parent() const
Definition: qgsdataitem.h:118
virtual void populate()
static QgsProviderRegistry * instance(QString pluginPath=QString::null)
means of accessing canonical single instance
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
void addDirectory(QString favIcon)
void removeFavourite(const QModelIndex &index)
virtual bool handleDrop(const QMimeData *, Qt::DropAction)
Definition: qgsdataitem.h:94
QgsFavouritesItem * mFavourites
void beginRemoveItems(QgsDataItem *parent, int first, int last)
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.
QIcon icon() const
Definition: qgsdataitem.h:121
QString homePath() const
Return project's home path.
void setIcon(QIcon icon)
Definition: qgsdataitem.h:125
QStringList providerList() const
Return list of available providers by their keys.
QgsDirectoryItem * mProjectHome
int dataCapabilities_t()
Definition: qgsdataitem.h:36
bool isPopulated()
Definition: qgsdataitem.h:70
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)
QgsDataItem * dataItem(const QModelIndex &idx) const
bool canFetchMore(const QModelIndex &parent) const
QString path() const
Definition: qgsdataitem.h:123
Type type() const
Definition: qgsdataitem.h:117
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...
QModelIndex findPath(QString path)
return index of a path
A directory: contains subdirectories and layers.
Definition: qgsdataitem.h:223
QModelIndex findItem(QgsDataItem *item, QgsDataItem *parent=0) const
static QMimeData * encodeUriList(UriList layers)
base class for all items in the model
Definition: qgsdataitem.h:41
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
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:155
QgsDataItem * dataItem_t(QString, QgsDataItem *)
Definition: qgsdataitem.h:37
void(*)() cast_to_fptr(void *p)
Definition: qgis.h:301
QString toolTip() const
Definition: qgsdataitem.h:128
virtual bool acceptDrop()
Definition: qgsdataitem.h:91
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)