QGIS API Documentation  2.2.0-Valmiera
 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  removeRootItems();
312  addRootItems();
313  reset(); // Qt4.6 brings better methods beginResetModel + endResetModel
314 }
315 
316 /* Refresh dir path */
317 void QgsBrowserModel::refresh( QString path )
318 {
319  QModelIndex idx = findPath( path );
320  if ( idx.isValid() )
321  {
322  QgsDataItem* item = dataItem( idx );
323  if ( item )
324  item->refresh();
325  }
326 }
327 
328 QModelIndex QgsBrowserModel::index( int row, int column, const QModelIndex &parent ) const
329 {
330  QgsDataItem *p = dataItem( parent );
331  const QVector<QgsDataItem*> &items = p ? p->children() : mRootItems;
332  QgsDataItem *item = items.value( row, 0 );
333  return item ? createIndex( row, column, item ) : QModelIndex();
334 }
335 
336 QModelIndex QgsBrowserModel::parent( const QModelIndex &index ) const
337 {
338  QgsDataItem *item = dataItem( index );
339  if ( !item )
340  return QModelIndex();
341 
342  return findItem( item->parent() );
343 }
344 
345 QModelIndex QgsBrowserModel::findItem( QgsDataItem *item, QgsDataItem *parent ) const
346 {
347  const QVector<QgsDataItem*> &items = parent ? parent->children() : mRootItems;
348 
349  for ( int i = 0; i < items.size(); i++ )
350  {
351  if ( items[i] == item )
352  return createIndex( i, 0, item );
353 
354  QModelIndex childIndex = findItem( item, items[i] );
355  if ( childIndex.isValid() )
356  return childIndex;
357  }
358 
359  return QModelIndex();
360 }
361 
362 /* Refresh item */
363 void QgsBrowserModel::refresh( const QModelIndex& theIndex )
364 {
365  QgsDataItem *item = dataItem( theIndex );
366  if ( !item )
367  return;
368 
369  QgsDebugMsg( "Refresh " + item->path() );
370  item->refresh();
371 }
372 
373 void QgsBrowserModel::beginInsertItems( QgsDataItem *parent, int first, int last )
374 {
375  QgsDebugMsg( "parent mPath = " + parent->path() );
376  QModelIndex idx = findItem( parent );
377  if ( !idx.isValid() )
378  return;
379  QgsDebugMsg( "valid" );
380  beginInsertRows( idx, first, last );
381  QgsDebugMsg( "end" );
382 }
384 {
385  QgsDebugMsg( "Entered" );
386  endInsertRows();
387 }
388 void QgsBrowserModel::beginRemoveItems( QgsDataItem *parent, int first, int last )
389 {
390  QgsDebugMsg( "parent mPath = " + parent->path() );
391  QModelIndex idx = findItem( parent );
392  if ( !idx.isValid() )
393  return;
394  beginRemoveRows( idx, first, last );
395 }
397 {
398  QgsDebugMsg( "Entered" );
399  endRemoveRows();
400 }
402 {
403  connect( item, SIGNAL( beginInsertItems( QgsDataItem*, int, int ) ),
404  this, SLOT( beginInsertItems( QgsDataItem*, int, int ) ) );
405  connect( item, SIGNAL( endInsertItems() ),
406  this, SLOT( endInsertItems() ) );
407  connect( item, SIGNAL( beginRemoveItems( QgsDataItem*, int, int ) ),
408  this, SLOT( beginRemoveItems( QgsDataItem*, int, int ) ) );
409  connect( item, SIGNAL( endRemoveItems() ),
410  this, SLOT( endRemoveItems() ) );
411 }
412 
413 QStringList QgsBrowserModel::mimeTypes() const
414 {
415  QStringList types;
416  // In theory the mime type convention is: application/x-vnd.<vendor>.<application>.<type>
417  // but it seems a bit over formalized. Would be an application/x-qgis-uri better?
418  types << "application/x-vnd.qgis.qgis.uri";
419  return types;
420 }
421 
422 QMimeData * QgsBrowserModel::mimeData( const QModelIndexList &indexes ) const
423 {
425  foreach ( const QModelIndex &index, indexes )
426  {
427  if ( index.isValid() )
428  {
429  QgsDataItem* ptr = ( QgsDataItem* ) index.internalPointer();
430  if ( ptr->type() != QgsDataItem::Layer ) continue;
431  QgsLayerItem *layer = ( QgsLayerItem* ) ptr;
432  lst.append( QgsMimeDataUtils::Uri( layer ) );
433  }
434  }
435  return QgsMimeDataUtils::encodeUriList( lst );
436 }
437 
438 bool QgsBrowserModel::dropMimeData( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent )
439 {
440  Q_UNUSED( row );
441  Q_UNUSED( column );
442 
443  QgsDataItem* destItem = dataItem( parent );
444  if ( !destItem )
445  {
446  QgsDebugMsg( "DROP PROBLEM!" );
447  return false;
448  }
449 
450  return destItem->handleDrop( data, action );
451 }
452 
453 QgsDataItem *QgsBrowserModel::dataItem( const QModelIndex &idx ) const
454 {
455  void *v = idx.internalPointer();
456  QgsDataItem *d = reinterpret_cast<QgsDataItem*>( v );
457  Q_ASSERT( !v || d );
458  return d;
459 }
460 
461 bool QgsBrowserModel::canFetchMore( const QModelIndex & parent ) const
462 {
463  QgsDataItem* item = dataItem( parent );
464  // if ( item )
465  // QgsDebugMsg( QString( "path = %1 canFetchMore = %2" ).arg( item->path() ).arg( item && ! item->isPopulated() ) );
466  return ( item && ! item->isPopulated() );
467 }
468 
469 void QgsBrowserModel::fetchMore( const QModelIndex & parent )
470 {
471  QgsDataItem* item = dataItem( parent );
472  if ( item )
473  item->populate();
474  QgsDebugMsg( "path = " + item->path() );
475 }
476 
478 {
479  Q_ASSERT( mFavourites );
480  mFavourites->addDirectory( favDir );
481 }
482 
483 void QgsBrowserModel::removeFavourite( const QModelIndex &index )
484 {
485  QgsDirectoryItem *item = dynamic_cast<QgsDirectoryItem *>( dataItem( index ) );
486  if ( !item )
487  return;
488 
489  mFavourites->removeDirectory( item );
490 }