|
Quantum GIS API Documentation
master-693a1fe
|
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 }