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