QGIS API Documentation  master-59fd5e0
src/core/qgsbrowsermodel.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002     qgsbrowsermodel.cpp
00003     ---------------------
00004     begin                : July 2011
00005     copyright            : (C) 2011 by Martin Dobias
00006     email                : wonder dot sk at gmail dot com
00007  ***************************************************************************
00008  *                                                                         *
00009  *   This program is free software; you can redistribute it and/or modify  *
00010  *   it under the terms of the GNU General Public License as published by  *
00011  *   the Free Software Foundation; either version 2 of the License, or     *
00012  *   (at your option) any later version.                                   *
00013  *                                                                         *
00014  ***************************************************************************/
00015 #include <QDir>
00016 #include <QApplication>
00017 #include <QStyle>
00018 
00019 #include "qgis.h"
00020 #include "qgsapplication.h"
00021 #include "qgsdataprovider.h"
00022 #include "qgsmimedatautils.h"
00023 #include "qgslogger.h"
00024 #include "qgsproviderregistry.h"
00025 
00026 #include "qgsbrowsermodel.h"
00027 #include "qgsproject.h"
00028 
00029 #include <QSettings>
00030 
00031 QgsBrowserModel::QgsBrowserModel( QObject *parent )
00032     : QAbstractItemModel( parent )
00033     , mFavourites( 0 )
00034     , mProjectHome( 0 )
00035 {
00036   connect( QgsProject::instance(), SIGNAL( readProject( const QDomDocument & ) ), this, SLOT( updateProjectHome() ) );
00037   connect( QgsProject::instance(), SIGNAL( writeProject( QDomDocument & ) ), this, SLOT( updateProjectHome() ) );
00038   addRootItems();
00039 }
00040 
00041 QgsBrowserModel::~QgsBrowserModel()
00042 {
00043   removeRootItems();
00044 }
00045 
00046 void QgsBrowserModel::updateProjectHome()
00047 {
00048   int idx = mRootItems.indexOf( mProjectHome );
00049   QString home = QgsProject::instance()->homePath();
00050 
00051   delete mProjectHome;
00052 
00053   emit layoutAboutToBeChanged();
00054   mProjectHome = home.isNull() ? 0 : new QgsDirectoryItem( NULL, tr( "Project home" ), home );
00055   if ( mProjectHome )
00056   {
00057     connectItem( mProjectHome );
00058     if ( idx < 0 )
00059       mRootItems.insert( 0, mProjectHome );
00060     else
00061       mRootItems.replace( idx, mProjectHome );
00062   }
00063   else if ( idx >= 0 )
00064   {
00065     mRootItems.remove( idx );
00066   }
00067   emit layoutChanged();
00068 }
00069 
00070 void QgsBrowserModel::addRootItems()
00071 {
00072   updateProjectHome();
00073 
00074   // give the home directory a prominent second place
00075   QgsDirectoryItem *item = new QgsDirectoryItem( NULL, tr( "Home" ), QDir::homePath() );
00076   QStyle *style = QApplication::style();
00077   QIcon homeIcon( style->standardPixmap( QStyle::SP_DirHomeIcon ) );
00078   item->setIcon( homeIcon );
00079   connectItem( item );
00080   mRootItems << item;
00081 
00082   // add favourite directories
00083   mFavourites = new QgsFavouritesItem( NULL, tr( "Favourites" ) );
00084   if ( mFavourites )
00085   {
00086     connectItem( mFavourites );
00087     mRootItems << mFavourites ;
00088   }
00089 
00090   // add drives
00091   foreach ( QFileInfo drive, QDir::drives() )
00092   {
00093     QString path = drive.absolutePath();
00094     QgsDirectoryItem *item = new QgsDirectoryItem( NULL, path, path );
00095 
00096     connectItem( item );
00097     mRootItems << item;
00098   }
00099 
00100 #ifdef Q_WS_MAC
00101   QString path = QString( "/Volumes" );
00102   QgsDirectoryItem *vols = new QgsDirectoryItem( NULL, path, path );
00103   connectItem( vols );
00104   mRootItems << vols;
00105 #endif
00106 
00107   // Add non file top level items
00108   QStringList providersList = QgsProviderRegistry::instance()->providerList();
00109   providersList.sort();
00110   foreach ( QString key, providersList )
00111   {
00112     QLibrary *library = QgsProviderRegistry::instance()->providerLibrary( key );
00113     if ( !library )
00114       continue;
00115 
00116     dataCapabilities_t * dataCapabilities = ( dataCapabilities_t * ) cast_to_fptr( library->resolve( "dataCapabilities" ) );
00117     if ( !dataCapabilities )
00118     {
00119       QgsDebugMsg( library->fileName() + " does not have dataCapabilities" );
00120       continue;
00121     }
00122 
00123     int capabilities = dataCapabilities();
00124     if ( capabilities == QgsDataProvider::NoDataCapabilities )
00125     {
00126       QgsDebugMsg( library->fileName() + " does not have any dataCapabilities" );
00127       continue;
00128     }
00129 
00130     dataItem_t *dataItem = ( dataItem_t * ) cast_to_fptr( library->resolve( "dataItem" ) );
00131     if ( !dataItem )
00132     {
00133       QgsDebugMsg( library->fileName() + " does not have dataItem" );
00134       continue;
00135     }
00136 
00137     QgsDataItem *item = dataItem( "", NULL );  // empty path -> top level
00138     if ( item )
00139     {
00140       QgsDebugMsg( "Add new top level item : " + item->name() );
00141       connectItem( item );
00142       mRootItems << item;
00143     }
00144   }
00145 }
00146 
00147 void QgsBrowserModel::removeRootItems()
00148 {
00149   foreach ( QgsDataItem* item, mRootItems )
00150   {
00151     delete item;
00152   }
00153 
00154   mRootItems.clear();
00155 }
00156 
00157 
00158 Qt::ItemFlags QgsBrowserModel::flags( const QModelIndex & index ) const
00159 {
00160   if ( !index.isValid() )
00161     return 0;
00162 
00163   Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
00164 
00165   QgsDataItem* ptr = ( QgsDataItem* ) index.internalPointer();
00166   if ( ptr->type() == QgsDataItem::Layer )
00167   {
00168     flags |= Qt::ItemIsDragEnabled;
00169   }
00170   if ( ptr->acceptDrop() )
00171     flags |= Qt::ItemIsDropEnabled;
00172   return flags;
00173 }
00174 
00175 QVariant QgsBrowserModel::data( const QModelIndex &index, int role ) const
00176 {
00177   if ( !index.isValid() )
00178     return QVariant();
00179 
00180   QgsDataItem *item = dataItem( index );
00181   if ( !item )
00182   {
00183     return QVariant();
00184   }
00185   else if ( role == Qt::DisplayRole )
00186   {
00187     return item->name();
00188   }
00189   else if ( role == Qt::ToolTipRole )
00190   {
00191     return item->toolTip();
00192   }
00193   else if ( role == Qt::DecorationRole && index.column() == 0 )
00194   {
00195     return item->icon();
00196   }
00197   else
00198   {
00199     // unsupported role
00200     return QVariant();
00201   }
00202 }
00203 
00204 QVariant QgsBrowserModel::headerData( int section, Qt::Orientation orientation, int role ) const
00205 {
00206   Q_UNUSED( section );
00207   if ( orientation == Qt::Horizontal && role == Qt::DisplayRole )
00208   {
00209     return QVariant( "header" );
00210   }
00211 
00212   return QVariant();
00213 }
00214 
00215 int QgsBrowserModel::rowCount( const QModelIndex &parent ) const
00216 {
00217   //qDebug("rowCount: idx: (valid %d) %d %d", parent.isValid(), parent.row(), parent.column());
00218 
00219   if ( !parent.isValid() )
00220   {
00221     // root item: its children are top level items
00222     return mRootItems.count(); // mRoot
00223   }
00224   else
00225   {
00226     // ordinary item: number of its children
00227     QgsDataItem *item = dataItem( parent );
00228     return item ? item->rowCount() : 0;
00229   }
00230 }
00231 
00232 bool QgsBrowserModel::hasChildren( const QModelIndex &parent ) const
00233 {
00234   if ( !parent.isValid() )
00235     return true; // root item: its children are top level items
00236 
00237   QgsDataItem *item = dataItem( parent );
00238   return item && item->hasChildren();
00239 }
00240 
00241 int QgsBrowserModel::columnCount( const QModelIndex &parent ) const
00242 {
00243   Q_UNUSED( parent );
00244   return 1;
00245 }
00246 
00247 QModelIndex QgsBrowserModel::findPath( QString path )
00248 {
00249   QModelIndex theIndex; // starting from root
00250   bool foundChild = true;
00251 
00252   while ( foundChild )
00253   {
00254     foundChild = false; // assume that the next child item will not be found
00255 
00256     for ( int i = 0; i < rowCount( theIndex ); i++ )
00257     {
00258       QModelIndex idx = index( i, 0, theIndex );
00259       QgsDataItem *item = dataItem( idx );
00260       if ( !item )
00261         return QModelIndex(); // an error occurred
00262 
00263       if ( item->path() == path )
00264       {
00265         QgsDebugMsg( "Arrived " + item->path() );
00266         return idx; // we have found the item we have been looking for
00267       }
00268 
00269       if ( path.startsWith( item->path() ) )
00270       {
00271         // we have found a preceding item: stop searching on this level and go deeper
00272         foundChild = true;
00273         theIndex = idx;
00274         break;
00275       }
00276     }
00277   }
00278 
00279   return QModelIndex(); // not found
00280 }
00281 
00282 void QgsBrowserModel::reload()
00283 {
00284   removeRootItems();
00285   addRootItems();
00286   reset(); // Qt4.6 brings better methods beginResetModel + endResetModel
00287 }
00288 
00289 /* Refresh dir path */
00290 void QgsBrowserModel::refresh( QString path )
00291 {
00292   QModelIndex idx = findPath( path );
00293   if ( idx.isValid() )
00294   {
00295     QgsDataItem* item = dataItem( idx );
00296     if ( item )
00297       item->refresh();
00298   }
00299 }
00300 
00301 QModelIndex QgsBrowserModel::index( int row, int column, const QModelIndex &parent ) const
00302 {
00303   QgsDataItem *p = dataItem( parent );
00304   const QVector<QgsDataItem*> &items = p ? p->children() : mRootItems;
00305   QgsDataItem *item = items.value( row, 0 );
00306   return item ? createIndex( row, column, item ) : QModelIndex();
00307 }
00308 
00309 QModelIndex QgsBrowserModel::parent( const QModelIndex &index ) const
00310 {
00311   QgsDataItem *item = dataItem( index );
00312   if ( !item )
00313     return QModelIndex();
00314 
00315   return findItem( item->parent() );
00316 }
00317 
00318 QModelIndex QgsBrowserModel::findItem( QgsDataItem *item, QgsDataItem *parent ) const
00319 {
00320   const QVector<QgsDataItem*> &items = parent ? parent->children() : mRootItems;
00321 
00322   for ( int i = 0; i < items.size(); i++ )
00323   {
00324     if ( items[i] == item )
00325       return createIndex( i, 0, item );
00326 
00327     QModelIndex childIndex = findItem( item, items[i] );
00328     if ( childIndex.isValid() )
00329       return childIndex;
00330   }
00331 
00332   return QModelIndex();
00333 }
00334 
00335 /* Refresh item */
00336 void QgsBrowserModel::refresh( const QModelIndex& theIndex )
00337 {
00338   QgsDataItem *item = dataItem( theIndex );
00339   if ( !item )
00340     return;
00341 
00342   QgsDebugMsg( "Refresh " + item->path() );
00343   item->refresh();
00344 }
00345 
00346 void QgsBrowserModel::beginInsertItems( QgsDataItem *parent, int first, int last )
00347 {
00348   QgsDebugMsg( "parent mPath = " + parent->path() );
00349   QModelIndex idx = findItem( parent );
00350   if ( !idx.isValid() )
00351     return;
00352   QgsDebugMsg( "valid" );
00353   beginInsertRows( idx, first, last );
00354   QgsDebugMsg( "end" );
00355 }
00356 void QgsBrowserModel::endInsertItems()
00357 {
00358   QgsDebugMsg( "Entered" );
00359   endInsertRows();
00360 }
00361 void QgsBrowserModel::beginRemoveItems( QgsDataItem *parent, int first, int last )
00362 {
00363   QgsDebugMsg( "parent mPath = " + parent->path() );
00364   QModelIndex idx = findItem( parent );
00365   if ( !idx.isValid() )
00366     return;
00367   beginRemoveRows( idx, first, last );
00368 }
00369 void QgsBrowserModel::endRemoveItems()
00370 {
00371   QgsDebugMsg( "Entered" );
00372   endRemoveRows();
00373 }
00374 void QgsBrowserModel::connectItem( QgsDataItem* item )
00375 {
00376   connect( item, SIGNAL( beginInsertItems( QgsDataItem*, int, int ) ),
00377            this, SLOT( beginInsertItems( QgsDataItem*, int, int ) ) );
00378   connect( item, SIGNAL( endInsertItems() ),
00379            this, SLOT( endInsertItems() ) );
00380   connect( item, SIGNAL( beginRemoveItems( QgsDataItem*, int, int ) ),
00381            this, SLOT( beginRemoveItems( QgsDataItem*, int, int ) ) );
00382   connect( item, SIGNAL( endRemoveItems() ),
00383            this, SLOT( endRemoveItems() ) );
00384 }
00385 
00386 QStringList QgsBrowserModel::mimeTypes() const
00387 {
00388   QStringList types;
00389   // In theory the mime type convention is: application/x-vnd.<vendor>.<application>.<type>
00390   // but it seems a bit over formalized. Would be an application/x-qgis-uri better?
00391   types << "application/x-vnd.qgis.qgis.uri";
00392   return types;
00393 }
00394 
00395 QMimeData * QgsBrowserModel::mimeData( const QModelIndexList &indexes ) const
00396 {
00397   QgsMimeDataUtils::UriList lst;
00398   foreach ( const QModelIndex &index, indexes )
00399   {
00400     if ( index.isValid() )
00401     {
00402       QgsDataItem* ptr = ( QgsDataItem* ) index.internalPointer();
00403       if ( ptr->type() != QgsDataItem::Layer ) continue;
00404       QgsLayerItem *layer = ( QgsLayerItem* ) ptr;
00405       lst.append( QgsMimeDataUtils::Uri( layer ) );
00406     }
00407   }
00408   return QgsMimeDataUtils::encodeUriList( lst );
00409 }
00410 
00411 bool QgsBrowserModel::dropMimeData( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent )
00412 {
00413   Q_UNUSED( row );
00414   Q_UNUSED( column );
00415 
00416   QgsDataItem* destItem = dataItem( parent );
00417   if ( !destItem )
00418   {
00419     QgsDebugMsg( "DROP PROBLEM!" );
00420     return false;
00421   }
00422 
00423   return destItem->handleDrop( data, action );
00424 }
00425 
00426 QgsDataItem *QgsBrowserModel::dataItem( const QModelIndex &idx ) const
00427 {
00428   void *v = idx.internalPointer();
00429   QgsDataItem *d = reinterpret_cast<QgsDataItem*>( v );
00430   Q_ASSERT( !v || d );
00431   return d;
00432 }
00433 
00434 bool QgsBrowserModel::canFetchMore( const QModelIndex & parent ) const
00435 {
00436   QgsDataItem* item = dataItem( parent );
00437   // if ( item )
00438   //   QgsDebugMsg( QString( "path = %1 canFetchMore = %2" ).arg( item->path() ).arg( item && ! item->isPopulated() ) );
00439   return ( item && ! item->isPopulated() );
00440 }
00441 
00442 void QgsBrowserModel::fetchMore( const QModelIndex & parent )
00443 {
00444   QgsDataItem* item = dataItem( parent );
00445   if ( item )
00446     item->populate();
00447   QgsDebugMsg( "path = " + item->path() );
00448 }
00449 
00450 void QgsBrowserModel::addFavouriteDirectory( QString favDir )
00451 {
00452   Q_ASSERT( mFavourites );
00453   mFavourites->addDirectory( favDir );
00454 }
00455 
00456 void QgsBrowserModel::removeFavourite( const QModelIndex &index )
00457 {
00458   QgsDirectoryItem *item = dynamic_cast<QgsDirectoryItem *>( dataItem( index ) );
00459   if ( !item )
00460     return;
00461 
00462   mFavourites->removeDirectory( item );
00463 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines