|
QGIS API Documentation
master-59fd5e0
|
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 }