|
QGIS API Documentation
master-3f58142
|
00001 /*************************************************************************** 00002 qgscptcityarchive.cpp 00003 --------------------- 00004 begin : August 2012 00005 copyright : (C) 2009 by Martin Dobias 00006 copyright : (C) 2011 Radim Blazek 00007 copyright : (C) 2012 by Etienne Tourigny 00008 email : etourigny.dev at gmail.com 00009 *************************************************************************** 00010 * * 00011 * This program is free software; you can redistribute it and/or modify * 00012 * it under the terms of the GNU General Public License as published by * 00013 * the Free Software Foundation; either version 2 of the License, or * 00014 * (at your option) any later version. * 00015 * * 00016 ***************************************************************************/ 00017 00018 #include <QApplication> 00019 #include <QDateTime> 00020 #include <QDir> 00021 #include <QFileInfo> 00022 #include <QMenu> 00023 #include <QMouseEvent> 00024 #include <QTreeWidget> 00025 #include <QTreeWidgetItem> 00026 #include <QVector> 00027 #include <QStyle> 00028 #include <QSettings> 00029 00030 #include "qgscptcityarchive.h" 00031 #include "qgis.h" 00032 00033 #include "qgsdataprovider.h" 00034 #include "qgslogger.h" 00035 #include "qgsconfig.h" 00036 #include "qgsmimedatautils.h" 00037 #include "qgsapplication.h" 00038 00039 00040 QString QgsCptCityArchive::mDefaultArchiveName; 00041 QMap< QString, QgsCptCityArchive* > QgsCptCityArchive::mArchiveRegistry; 00042 QMap< QString, QgsCptCityArchive* > QgsCptCityArchive::archiveRegistry() { return mArchiveRegistry; } 00043 QMap< QString, QMap< QString, QString > > QgsCptCityArchive::mCopyingInfoMap; 00044 00045 QgsCptCityArchive::QgsCptCityArchive( QString archiveName, QString baseDir ) 00046 : mArchiveName( archiveName ), mBaseDir( baseDir ) 00047 { 00048 QgsDebugMsg( "archiveName = " + archiveName + " baseDir = " + baseDir ); 00049 00050 // make Author items 00051 QgsCptCityDirectoryItem* dirItem = 0; 00052 foreach ( QString path, QDir( mBaseDir ).entryList( QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name ) ) 00053 { 00054 if ( path == "selections" ) 00055 continue; 00056 QgsDebugMsg( "path= " + path ); 00057 dirItem = new QgsCptCityDirectoryItem( NULL, QFileInfo( path ).baseName(), path ); 00058 if ( dirItem->isValid() ) 00059 mRootItems << dirItem; 00060 else 00061 delete dirItem; 00062 } 00063 00064 // make selection items 00065 QgsCptCitySelectionItem* selItem = 0; 00066 QDir seldir( mBaseDir + QDir::separator() + "selections" ); 00067 QgsDebugMsg( "populating selection from " + seldir.path() ); 00068 foreach ( QString selfile, seldir.entryList( QStringList( "*.xml" ), QDir::Files ) ) 00069 { 00070 QgsDebugMsg( "file= " + seldir.path() + "/" + selfile ); 00071 selItem = new QgsCptCitySelectionItem( NULL, QFileInfo( selfile ).baseName(), 00072 seldir.dirName() + QDir::separator() + selfile ); 00073 //TODO remove item if there are no children (e.g. esri in qgis-sel) 00074 if ( selItem->isValid() ) 00075 mSelectionItems << selItem; 00076 else 00077 delete selItem; 00078 } 00079 00080 // make "All Ramps items" (which will contain all ramps without hierarchy) 00081 QgsCptCityAllRampsItem* allRampsItem; 00082 allRampsItem = new QgsCptCityAllRampsItem( NULL, QObject::tr( "All Ramps" ), 00083 mRootItems ); 00084 mRootItems.prepend( allRampsItem ); 00085 allRampsItem = new QgsCptCityAllRampsItem( NULL, QObject::tr( "All Ramps" ), 00086 mSelectionItems ); 00087 mSelectionItems.prepend( allRampsItem ); 00088 } 00089 00090 QgsCptCityArchive::~QgsCptCityArchive( ) 00091 { 00092 foreach ( QgsCptCityDataItem* item, mRootItems ) 00093 delete item; 00094 foreach ( QgsCptCityDataItem* item, mSelectionItems ) 00095 delete item; 00096 mRootItems.clear(); 00097 mSelectionItems.clear(); 00098 } 00099 00100 QString QgsCptCityArchive::baseDir() const 00101 { 00102 // if was set with setBaseDir, return that value 00103 // else return global default 00104 if ( ! mBaseDir.isNull() ) 00105 return mBaseDir; 00106 else 00107 return QgsCptCityArchive::defaultBaseDir( ); 00108 } 00109 00110 QString QgsCptCityArchive::baseDir( QString archiveName ) 00111 { 00112 // search for matching archive in the registry 00113 if ( archiveName.isNull() ) 00114 archiveName = DEFAULT_CPTCITY_ARCHIVE; 00115 if ( mArchiveRegistry.contains( archiveName ) ) 00116 return mArchiveRegistry.value( archiveName )->baseDir(); 00117 else 00118 return defaultBaseDir(); 00119 } 00120 00121 QString QgsCptCityArchive::defaultBaseDir() 00122 { 00123 QString baseDir, archiveName; 00124 QSettings settings; 00125 00126 // use CptCity/baseDir setting if set, default is user dir 00127 baseDir = settings.value( "CptCity/baseDir", 00128 QgsApplication::pkgDataPath() + "/resources" ).toString(); 00129 // sub-dir defaults to cpt-city 00130 archiveName = settings.value( "CptCity/archiveName", DEFAULT_CPTCITY_ARCHIVE ).toString(); 00131 00132 return baseDir + QDir::separator() + archiveName; 00133 } 00134 00135 00136 QString QgsCptCityArchive::findFileName( const QString & target, const QString & startDir, const QString & baseDir ) 00137 { 00138 // QgsDebugMsg( "target= " + target + " startDir= " + startDir + " baseDir= " + baseDir ); 00139 00140 if ( startDir == "" || ! startDir.startsWith( baseDir ) ) 00141 return QString(); 00142 00143 QDir dir = QDir( startDir ); 00144 //todo test when 00145 while ( ! dir.exists( target ) && dir.path() != baseDir ) 00146 { 00147 if ( ! dir.cdUp() ) 00148 break; 00149 } 00150 if ( ! dir.exists( target ) ) 00151 return QString(); 00152 else 00153 return dir.path() + QDir::separator() + target; 00154 } 00155 00156 00157 QString QgsCptCityArchive::copyingFileName( const QString& path ) const 00158 { 00159 return QgsCptCityArchive::findFileName( "COPYING.xml", 00160 baseDir() + QDir::separator() + path, baseDir() ); 00161 } 00162 00163 QString QgsCptCityArchive::descFileName( const QString& path ) const 00164 { 00165 return QgsCptCityArchive::findFileName( "DESC.xml", 00166 baseDir() + QDir::separator() + path, baseDir() ); 00167 } 00168 00169 QgsStringMap QgsCptCityArchive::copyingInfo( const QString& fileName ) 00170 { 00171 QgsStringMap copyingMap; 00172 00173 if ( fileName.isNull() ) 00174 return copyingMap; 00175 00176 if ( QgsCptCityArchive::mCopyingInfoMap.contains( fileName ) ) 00177 { 00178 QgsDebugMsg( "found copying info in copyingInfoMap, file = " + fileName ); 00179 return QgsCptCityArchive::mCopyingInfoMap.value( fileName ); 00180 } 00181 00182 QgsDebugMsg( "fileName = " + fileName ); 00183 00184 // import xml file 00185 QFile f( fileName ); 00186 if ( !f.open( QFile::ReadOnly ) ) 00187 { 00188 QgsDebugMsg( "Couldn't open xml file: " + fileName ); 00189 return copyingMap; 00190 } 00191 00192 // parse the document 00193 QDomDocument doc( "license" ); 00194 if ( !doc.setContent( &f ) ) 00195 { 00196 f.close(); 00197 QgsDebugMsg( "Couldn't parse xml file: " + fileName ); 00198 return copyingMap; 00199 } 00200 f.close(); 00201 00202 // get root element 00203 QDomElement docElem = doc.documentElement(); 00204 if ( docElem.tagName() != "copying" ) 00205 { 00206 QgsDebugMsg( "Incorrect root tag: " + docElem.tagName() ); 00207 return copyingMap; 00208 } 00209 00210 // load author information 00211 QDomElement authorsElement = docElem.firstChildElement( "authors" ); 00212 if ( authorsElement.isNull() ) 00213 { 00214 QgsDebugMsg( "authors tag missing" ); 00215 } 00216 else 00217 { 00218 QDomElement e = authorsElement.firstChildElement(); 00219 QStringList authors; 00220 while ( ! e.isNull() ) 00221 { 00222 if ( e.tagName() == "author" ) 00223 { 00224 if ( ! e.firstChildElement( "name" ).isNull() ) 00225 authors << e.firstChildElement( "name" ).text().simplified(); 00226 // org??? 00227 } 00228 e = e.nextSiblingElement(); 00229 } 00230 copyingMap[ "authors" ] = authors.join( ", " ); 00231 } 00232 00233 // load license information 00234 QDomElement licenseElement = docElem.firstChildElement( "license" ); 00235 if ( licenseElement.isNull() ) 00236 { 00237 QgsDebugMsg( "license tag missing" ); 00238 } 00239 else 00240 { 00241 QDomElement e = licenseElement.firstChildElement( "informal" ); 00242 if ( ! e.isNull() ) 00243 copyingMap[ "license/informal" ] = e.text().simplified(); 00244 e = licenseElement.firstChildElement( "year" ); 00245 if ( ! e.isNull() ) 00246 copyingMap[ "license/year" ] = e.text().simplified(); 00247 e = licenseElement.firstChildElement( "text" ); 00248 if ( ! e.isNull() && e.attribute( "href" ) != QString() ) 00249 copyingMap[ "license/url" ] = e.attribute( "href" ); 00250 } 00251 00252 // load src information 00253 QDomElement element = docElem.firstChildElement( "src" ); 00254 if ( element.isNull() ) 00255 { 00256 QgsDebugMsg( "src tag missing" ); 00257 } 00258 else 00259 { 00260 QDomElement e = element.firstChildElement( "link" ); 00261 if ( ! e.isNull() && e.attribute( "href" ) != QString() ) 00262 copyingMap[ "src/link" ] = e.attribute( "href" ); 00263 } 00264 00265 // save copyingMap for further access 00266 QgsCptCityArchive::mCopyingInfoMap[ fileName ] = copyingMap; 00267 return copyingMap; 00268 } 00269 00270 QgsStringMap QgsCptCityArchive::description( const QString& fileName ) 00271 { 00272 QgsStringMap descMap; 00273 00274 QgsDebugMsg( "description fileName = " + fileName ); 00275 00276 QFile f( fileName ); 00277 if ( ! f.open( QFile::ReadOnly ) ) 00278 { 00279 QgsDebugMsg( "description file " + fileName + " ] does not exist" ); 00280 return descMap; 00281 } 00282 00283 // parse the document 00284 QString errMsg; 00285 QDomDocument doc( "description" ); 00286 if ( !doc.setContent( &f, &errMsg ) ) 00287 { 00288 f.close(); 00289 QgsDebugMsg( "Couldn't parse file " + fileName + " : " + errMsg ); 00290 return descMap; 00291 } 00292 f.close(); 00293 00294 // read description 00295 QDomElement docElem = doc.documentElement(); 00296 if ( docElem.tagName() != "description" ) 00297 { 00298 QgsDebugMsg( "Incorrect root tag: " + docElem.tagName() ); 00299 return descMap; 00300 } 00301 // should we make sure the <dir> tag is ok? 00302 00303 QDomElement e = docElem.firstChildElement( "name" ); 00304 if ( e.isNull() ) 00305 { 00306 QgsDebugMsg( "name tag missing" ); 00307 } 00308 descMap[ "name" ] = e.text().simplified(); 00309 e = docElem.firstChildElement( "full" ); 00310 if ( e.isNull() ) 00311 { 00312 QgsDebugMsg( "full tag missing" ); 00313 } 00314 descMap[ "full" ] = e.text().simplified(); 00315 00316 return descMap; 00317 } 00318 00319 QMap< double, QPair<QColor, QColor> >QgsCptCityArchive::gradientColorMap( const QString& fileName ) 00320 { 00321 QMap< double, QPair<QColor, QColor> > colorMap; 00322 00323 // import xml file 00324 QFile f( fileName ); 00325 if ( !f.open( QFile::ReadOnly ) ) 00326 { 00327 QgsDebugMsg( "Couldn't open SVG file: " + fileName ); 00328 return colorMap; 00329 } 00330 00331 // parse the document 00332 QDomDocument doc( "gradient" ); 00333 if ( !doc.setContent( &f ) ) 00334 { 00335 f.close(); 00336 QgsDebugMsg( "Couldn't parse SVG file: " + fileName ); 00337 return colorMap; 00338 } 00339 f.close(); 00340 00341 QDomElement docElem = doc.documentElement(); 00342 00343 if ( docElem.tagName() != "svg" ) 00344 { 00345 QgsDebugMsg( "Incorrect root tag: " + docElem.tagName() ); 00346 return colorMap; 00347 } 00348 00349 // load color ramp from first linearGradient node 00350 QDomElement rampsElement = docElem.firstChildElement( "linearGradient" ); 00351 if ( rampsElement.isNull() ) 00352 { 00353 QDomNodeList nodeList = docElem.elementsByTagName( "linearGradient" ); 00354 if ( ! nodeList.isEmpty() ) 00355 rampsElement = nodeList.at( 0 ).toElement(); 00356 } 00357 if ( rampsElement.isNull() ) 00358 { 00359 QgsDebugMsg( "linearGradient tag missing" ); 00360 return colorMap; 00361 } 00362 00363 // loop for all stop tags 00364 QDomElement e = rampsElement.firstChildElement(); 00365 00366 while ( !e.isNull() ) 00367 { 00368 if ( e.tagName() == "stop" ) 00369 { 00370 //todo integrate this into symbollayerutils, keep here for now... 00371 double offset; 00372 QString offsetStr = e.attribute( "offset" ); // offset="50.00%" | offset="0.5" 00373 QString colorStr = e.attribute( "stop-color", "" ); // stop-color="rgb(222,235,247)" 00374 QString opacityStr = e.attribute( "stop-opacity", "1.0" ); // stop-opacity="1.0000" 00375 if ( offsetStr.endsWith( "%" ) ) 00376 offset = offsetStr.remove( offsetStr.size() - 1, 1 ).toDouble() / 100.0; 00377 else 00378 offset = offsetStr.toDouble(); 00379 00380 // QColor color( 255, 0, 0 ); // red color as a warning :) 00381 QColor color = QgsSymbolLayerV2Utils::parseColor( colorStr ); 00382 if ( color != QColor() ) 00383 { 00384 int alpha = opacityStr.toDouble() * 255; // test 00385 color.setAlpha( alpha ); 00386 if ( colorMap.contains( offset ) ) 00387 colorMap[offset].second = color; 00388 else 00389 colorMap[offset] = qMakePair( color, color ); 00390 } 00391 else 00392 { 00393 QgsDebugMsg( QString( "at offset=%1 invalid color" ).arg( offset ) ); 00394 } 00395 } 00396 else 00397 { 00398 QgsDebugMsg( "unknown tag: " + e.tagName() ); 00399 } 00400 00401 e = e.nextSiblingElement(); 00402 } 00403 00404 return colorMap; 00405 } 00406 00407 bool QgsCptCityArchive::isEmpty() 00408 { 00409 return ( mRootItems.isEmpty() ); 00410 } 00411 00412 00413 QgsCptCityArchive* QgsCptCityArchive::defaultArchive() 00414 { 00415 QSettings settings; 00416 mDefaultArchiveName = settings.value( "CptCity/archiveName", DEFAULT_CPTCITY_ARCHIVE ).toString(); 00417 if ( QgsCptCityArchive::mArchiveRegistry.contains( mDefaultArchiveName ) ) 00418 return QgsCptCityArchive::mArchiveRegistry.value( mDefaultArchiveName ); 00419 else 00420 return NULL; 00421 } 00422 00423 void QgsCptCityArchive::initArchive( QString archiveName, QString archiveBaseDir ) 00424 { 00425 QgsDebugMsg( "archiveName = " + archiveName + " archiveBaseDir = " + archiveBaseDir ); 00426 QgsCptCityArchive *archive = new QgsCptCityArchive( archiveName, archiveBaseDir ); 00427 if ( mArchiveRegistry.contains( archiveName ) ) 00428 delete mArchiveRegistry[ archiveName ]; 00429 mArchiveRegistry[ archiveName ] = archive; 00430 } 00431 00432 void QgsCptCityArchive::initDefaultArchive() 00433 { 00434 QSettings settings; 00435 // use CptCity/baseDir setting if set, default is user dir 00436 QString baseDir = settings.value( "CptCity/baseDir", 00437 QgsApplication::pkgDataPath() + "/resources" ).toString(); 00438 // sub-dir defaults to 00439 QString defArchiveName = settings.value( "CptCity/archiveName", DEFAULT_CPTCITY_ARCHIVE ).toString(); 00440 00441 if ( ! mArchiveRegistry.contains( defArchiveName ) ) 00442 initArchive( defArchiveName, baseDir + QDir::separator() + defArchiveName ); 00443 } 00444 00445 void QgsCptCityArchive::initArchives( bool loadAll ) 00446 { 00447 QgsStringMap archivesMap; 00448 QString baseDir, defArchiveName; 00449 QSettings settings; 00450 00451 // use CptCity/baseDir setting if set, default is user dir 00452 baseDir = settings.value( "CptCity/baseDir", 00453 QgsApplication::pkgDataPath() + "/resources" ).toString(); 00454 // sub-dir defaults to 00455 defArchiveName = settings.value( "CptCity/archiveName", DEFAULT_CPTCITY_ARCHIVE ).toString(); 00456 00457 QgsDebugMsg( "baseDir= " + baseDir + " defArchiveName= " + defArchiveName ); 00458 if ( loadAll ) 00459 { 00460 QDir dir( baseDir ); 00461 foreach ( QString entry, dir.entryList( QStringList( "cpt-city*" ), QDir::Dirs ) ) 00462 { 00463 if ( QFile::exists( baseDir + QDir::separator() + entry + "/VERSION.xml" ) ) 00464 archivesMap[ entry ] = baseDir + QDir::separator() + entry; 00465 } 00466 } 00467 else 00468 { 00469 archivesMap[ defArchiveName ] = baseDir + QDir::separator() + defArchiveName; 00470 } 00471 00472 for ( QgsStringMap::iterator it = archivesMap.begin(); 00473 it != archivesMap.end(); ++it ) 00474 { 00475 if ( QDir( it.value() ).exists() ) 00476 QgsCptCityArchive::initArchive( it.key(), it.value() ); 00477 else 00478 { 00479 QgsDebugMsg( QString( "not loading archive [%1] because dir %2 does not exist " ).arg( it.key() ).arg( it.value() ) ); 00480 } 00481 } 00482 mDefaultArchiveName = defArchiveName; 00483 } 00484 00485 void QgsCptCityArchive::clearArchives() 00486 { 00487 for ( QMap< QString, QgsCptCityArchive* >::iterator it = mArchiveRegistry.begin(); 00488 it != mArchiveRegistry.end(); ++it ) 00489 delete it.value(); 00490 mArchiveRegistry.clear(); 00491 } 00492 00493 00494 // -------- 00495 00496 QgsCptCityDataItem::QgsCptCityDataItem( QgsCptCityDataItem::Type type, QgsCptCityDataItem* parent, 00497 QString name, QString path ) 00498 // Do not pass parent to QObject, Qt would delete this when parent is deleted 00499 : QObject(), mType( type ), mParent( parent ), mPopulated( false ), 00500 mName( name ), mPath( path ), mValid( true ) 00501 { 00502 } 00503 00504 QgsCptCityDataItem::~QgsCptCityDataItem() 00505 { 00506 // QgsDebugMsg( "mName = " + mName + " mPath = " + mPath ); 00507 } 00508 00509 void QgsCptCityDataItem::emitBeginInsertItems( QgsCptCityDataItem* parent, int first, int last ) 00510 { 00511 emit beginInsertItems( parent, first, last ); 00512 } 00513 void QgsCptCityDataItem::emitEndInsertItems() 00514 { 00515 emit endInsertItems(); 00516 } 00517 void QgsCptCityDataItem::emitBeginRemoveItems( QgsCptCityDataItem* parent, int first, int last ) 00518 { 00519 emit beginRemoveItems( parent, first, last ); 00520 } 00521 void QgsCptCityDataItem::emitEndRemoveItems() 00522 { 00523 emit endRemoveItems(); 00524 } 00525 00526 QVector<QgsCptCityDataItem*> QgsCptCityDataItem::createChildren() 00527 { 00528 QVector<QgsCptCityDataItem*> children; 00529 return children; 00530 } 00531 00532 void QgsCptCityDataItem::populate() 00533 { 00534 if ( mPopulated ) 00535 return; 00536 00537 QgsDebugMsg( "mPath = " + mPath ); 00538 00539 QApplication::setOverrideCursor( Qt::WaitCursor ); 00540 00541 QVector<QgsCptCityDataItem*> children = createChildren(); 00542 foreach ( QgsCptCityDataItem *child, children ) 00543 { 00544 // initialization, do not refresh! That would result in infinite loop (beginInsertItems->rowCount->populate) 00545 addChildItem( child ); 00546 } 00547 mPopulated = true; 00548 00549 QApplication::restoreOverrideCursor(); 00550 } 00551 00552 int QgsCptCityDataItem::rowCount() 00553 { 00554 // if ( !mPopulated ) 00555 // populate(); 00556 return mChildren.size(); 00557 } 00558 00559 int QgsCptCityDataItem::leafCount() const 00560 { 00561 if ( !mPopulated ) 00562 return 0; 00563 00564 int count = 0; 00565 foreach ( QgsCptCityDataItem *child, mChildren ) 00566 { 00567 if ( child ) 00568 count += child->leafCount(); 00569 } 00570 return count; 00571 } 00572 00573 00574 bool QgsCptCityDataItem::hasChildren() 00575 { 00576 return ( mPopulated ? mChildren.count() > 0 : true ); 00577 } 00578 00579 void QgsCptCityDataItem::addChildItem( QgsCptCityDataItem * child, bool refresh ) 00580 { 00581 QgsDebugMsg( QString( "add child #%1 - %2 - %3" ).arg( mChildren.size() ).arg( child->mName ).arg( child->mType ) ); 00582 00583 int i; 00584 if ( type() == ColorRamp ) 00585 { 00586 for ( i = 0; i < mChildren.size(); i++ ) 00587 { 00588 // sort items by type, so directories are after data items 00589 if ( mChildren[i]->mType == child->mType && 00590 mChildren[i]->mName.localeAwareCompare( child->mName ) >= 0 ) 00591 break; 00592 } 00593 } 00594 else 00595 { 00596 for ( i = 0; i < mChildren.size(); i++ ) 00597 { 00598 if ( mChildren[i]->mName.localeAwareCompare( child->mName ) >= 0 ) 00599 break; 00600 } 00601 } 00602 00603 if ( refresh ) 00604 emit beginInsertItems( this, i, i ); 00605 00606 mChildren.insert( i, child ); 00607 00608 connect( child, SIGNAL( beginInsertItems( QgsCptCityDataItem*, int, int ) ), 00609 this, SLOT( emitBeginInsertItems( QgsCptCityDataItem*, int, int ) ) ); 00610 connect( child, SIGNAL( endInsertItems() ), 00611 this, SLOT( emitEndInsertItems() ) ); 00612 connect( child, SIGNAL( beginRemoveItems( QgsCptCityDataItem*, int, int ) ), 00613 this, SLOT( emitBeginRemoveItems( QgsCptCityDataItem*, int, int ) ) ); 00614 connect( child, SIGNAL( endRemoveItems() ), 00615 this, SLOT( emitEndRemoveItems() ) ); 00616 00617 if ( refresh ) 00618 emit endInsertItems(); 00619 } 00620 void QgsCptCityDataItem::deleteChildItem( QgsCptCityDataItem * child ) 00621 { 00622 // QgsDebugMsg( "mName = " + child->mName ); 00623 int i = mChildren.indexOf( child ); 00624 Q_ASSERT( i >= 0 ); 00625 emit beginRemoveItems( this, i, i ); 00626 mChildren.remove( i ); 00627 delete child; 00628 emit endRemoveItems(); 00629 } 00630 00631 QgsCptCityDataItem * QgsCptCityDataItem::removeChildItem( QgsCptCityDataItem * child ) 00632 { 00633 // QgsDebugMsg( "mName = " + child->mName ); 00634 int i = mChildren.indexOf( child ); 00635 Q_ASSERT( i >= 0 ); 00636 emit beginRemoveItems( this, i, i ); 00637 mChildren.remove( i ); 00638 emit endRemoveItems(); 00639 disconnect( child, SIGNAL( beginInsertItems( QgsCptCityDataItem*, int, int ) ), 00640 this, SLOT( emitBeginInsertItems( QgsCptCityDataItem*, int, int ) ) ); 00641 disconnect( child, SIGNAL( endInsertItems() ), 00642 this, SLOT( emitEndInsertItems() ) ); 00643 disconnect( child, SIGNAL( beginRemoveItems( QgsCptCityDataItem*, int, int ) ), 00644 this, SLOT( emitBeginRemoveItems( QgsCptCityDataItem*, int, int ) ) ); 00645 disconnect( child, SIGNAL( endRemoveItems() ), 00646 this, SLOT( emitEndRemoveItems() ) ); 00647 child->setParent( 0 ); 00648 return child; 00649 } 00650 00651 int QgsCptCityDataItem::findItem( QVector<QgsCptCityDataItem*> items, QgsCptCityDataItem * item ) 00652 { 00653 for ( int i = 0; i < items.size(); i++ ) 00654 { 00655 // QgsDebugMsg( QString::number( i ) + " : " + items[i]->mPath + " x " + item->mPath ); 00656 if ( items[i]->equal( item ) ) 00657 return i; 00658 } 00659 return -1; 00660 } 00661 00662 void QgsCptCityDataItem::refresh() 00663 { 00664 QgsDebugMsg( "mPath = " + mPath ); 00665 00666 QApplication::setOverrideCursor( Qt::WaitCursor ); 00667 00668 QVector<QgsCptCityDataItem*> items = createChildren( ); 00669 00670 // Remove no more present items 00671 QVector<QgsCptCityDataItem*> remove; 00672 foreach ( QgsCptCityDataItem *child, mChildren ) 00673 { 00674 if ( findItem( items, child ) >= 0 ) 00675 continue; 00676 remove.append( child ); 00677 } 00678 foreach ( QgsCptCityDataItem *child, remove ) 00679 { 00680 deleteChildItem( child ); 00681 } 00682 00683 // Add new items 00684 foreach ( QgsCptCityDataItem *item, items ) 00685 { 00686 // Is it present in childs? 00687 if ( findItem( mChildren, item ) >= 0 ) 00688 { 00689 delete item; 00690 continue; 00691 } 00692 addChildItem( item, true ); 00693 } 00694 00695 QApplication::restoreOverrideCursor(); 00696 } 00697 00698 bool QgsCptCityDataItem::equal( const QgsCptCityDataItem *other ) 00699 { 00700 if ( metaObject()->className() == other->metaObject()->className() && 00701 mPath == other->path() ) 00702 { 00703 return true; 00704 } 00705 return false; 00706 } 00707 00708 // --------------------------------------------------------------------- 00709 00710 QgsCptCityColorRampItem::QgsCptCityColorRampItem( QgsCptCityDataItem* parent, 00711 QString name, QString path, QString variantName, bool initialize ) 00712 : QgsCptCityDataItem( ColorRamp, parent, name, path ), 00713 mInitialised( false ), mRamp( path, variantName, false ) 00714 { 00715 // QgsDebugMsg( "name= " + name + " path= " + path ); 00716 mPopulated = true; 00717 if ( initialize ) 00718 init(); 00719 } 00720 00721 QgsCptCityColorRampItem::QgsCptCityColorRampItem( QgsCptCityDataItem* parent, 00722 QString name, QString path, QStringList variantList, bool initialize ) 00723 : QgsCptCityDataItem( ColorRamp, parent, name, path ), 00724 mInitialised( false ), mRamp( path, variantList, QString(), false ) 00725 { 00726 // QgsDebugMsg( "name= " + name + " path= " + path ); 00727 mPopulated = true; 00728 if ( initialize ) 00729 init(); 00730 } 00731 00732 // TODO only load file when icon is requested... 00733 void QgsCptCityColorRampItem::init( ) 00734 { 00735 if ( mInitialised ) 00736 return; 00737 mInitialised = true; 00738 00739 QgsDebugMsg( "path = " + path() ); 00740 00741 // make preview from variant if exists 00742 QStringList variantList = mRamp.variantList(); 00743 if ( mRamp.variantName().isNull() && ! variantList.isEmpty() ) 00744 mRamp.setVariantName( variantList[ variantList.count() / 2 ] ); 00745 00746 mRamp.loadFile(); 00747 00748 // is this item valid? this might fail when there are variants, check 00749 if ( ! QFile::exists( mRamp.fileName() ) ) 00750 mValid = false; 00751 else 00752 mValid = true; 00753 00754 // load file and set info 00755 if ( mRamp.count() > 0 ) 00756 { 00757 if ( variantList.isEmpty() ) 00758 { 00759 int count = mRamp.count(); 00760 if ( mRamp.isDiscrete() ) 00761 count--; 00762 mInfo = QString::number( count ) + " " + tr( "colors" ) + " - "; 00763 if ( mRamp.isDiscrete() ) 00764 mInfo += tr( "discrete" ); 00765 else 00766 { 00767 if ( !mRamp.hasMultiStops() ) 00768 mInfo += tr( "continuous" ); 00769 else 00770 mInfo += tr( "continuous (multi)" ); 00771 } 00772 mShortInfo = QFileInfo( mName ).fileName(); 00773 } 00774 else 00775 { 00776 mInfo = QString::number( variantList.count() ) + " " + tr( "variants" ); 00777 // mShortInfo = QFileInfo( mName ).fileName() + " (" + QString::number( variantList.count() ) + ")"; 00778 mShortInfo = QFileInfo( mName ).fileName(); 00779 } 00780 } 00781 else 00782 { 00783 mInfo = ""; 00784 } 00785 00786 } 00787 00788 bool QgsCptCityColorRampItem::equal( const QgsCptCityDataItem *other ) 00789 { 00790 //QgsDebugMsg ( mPath + " x " + other->mPath ); 00791 if ( type() != other->type() ) 00792 { 00793 return false; 00794 } 00795 //const QgsCptCityColorRampItem *o = qobject_cast<const QgsCptCityColorRampItem *> ( other ); 00796 const QgsCptCityColorRampItem *o = dynamic_cast<const QgsCptCityColorRampItem *>( other ); 00797 return ( mPath == o->mPath && mName == o->mName && 00798 ramp().variantName() == o->ramp().variantName() ); 00799 } 00800 00801 QIcon QgsCptCityColorRampItem::icon() 00802 { 00803 return icon( QSize( 100, 15 ) ); 00804 } 00805 00806 QIcon QgsCptCityColorRampItem::icon( const QSize& size ) 00807 { 00808 foreach ( QIcon icon, mIcons ) 00809 { 00810 if ( icon.availableSizes().contains( size ) ) 00811 return icon; 00812 } 00813 00814 QIcon icon( size ); 00815 00816 init(); 00817 00818 if ( mValid && mRamp.count() > 0 ) 00819 { 00820 icon = QgsSymbolLayerV2Utils::colorRampPreviewIcon( &mRamp, size ); 00821 } 00822 else 00823 { 00824 QPixmap blankPixmap( size ); 00825 blankPixmap.fill( Qt::white ); 00826 icon = QIcon( blankPixmap ); 00827 mInfo = ""; 00828 } 00829 00830 mIcons.append( icon ); 00831 return icon; 00832 } 00833 00834 // --------------------------------------------------------------------- 00835 QgsCptCityCollectionItem::QgsCptCityCollectionItem( QgsCptCityDataItem* parent, 00836 QString name, QString path ) 00837 : QgsCptCityDataItem( Collection, parent, name, path ), mPopulatedRamps( false ) 00838 { 00839 } 00840 00841 QgsCptCityCollectionItem::~QgsCptCityCollectionItem() 00842 { 00843 // QgsDebugMsg( "Entered" ); 00844 foreach ( QgsCptCityDataItem* i, mChildren ) 00845 { 00846 // QgsDebugMsg( QString( "delete child = 0x%0" ).arg(( qlonglong )i, 8, 16, QLatin1Char( '0' ) ) ); 00847 delete i; 00848 } 00849 } 00850 00851 QVector< QgsCptCityDataItem* > QgsCptCityCollectionItem::childrenRamps( bool recursive ) 00852 { 00853 QVector< QgsCptCityDataItem* > rampItems; 00854 QVector< QgsCptCityDataItem* > deleteItems; 00855 00856 populate(); 00857 00858 // recursively add children 00859 foreach ( QgsCptCityDataItem* childItem, children() ) 00860 { 00861 QgsCptCityCollectionItem* collectionItem = dynamic_cast<QgsCptCityCollectionItem*>( childItem ); 00862 QgsCptCityColorRampItem* rampItem = dynamic_cast<QgsCptCityColorRampItem*>( childItem ); 00863 QgsDebugMsg( QString( "child path= %1 coll= %2 ramp = %3" ).arg( childItem->path() ).arg( collectionItem != 0 ).arg( rampItem != 0 ) ); 00864 if ( collectionItem && recursive ) 00865 { 00866 collectionItem->populate(); 00867 rampItems << collectionItem->childrenRamps( true ); 00868 } 00869 else if ( rampItem ) 00870 { 00871 // init rampItem to get palette and icon, test if is valid after loading file 00872 rampItem->init(); 00873 if ( rampItem->isValid() ) 00874 rampItems << rampItem; 00875 else 00876 deleteItems << rampItem; 00877 } 00878 else 00879 { 00880 QgsDebugMsg( "invalid item " + childItem->path() ); 00881 } 00882 } 00883 00884 // delete invalid items - this is not efficient, but should only happens once 00885 foreach ( QgsCptCityDataItem* deleteItem, deleteItems ) 00886 { 00887 QgsDebugMsg( QString( "item %1 is invalid, will be deleted" ).arg( deleteItem->path() ) ); 00888 int i = mChildren.indexOf( deleteItem ); 00889 if ( i != -1 ) 00890 mChildren.remove( i ); 00891 delete deleteItem; 00892 } 00893 00894 return rampItems; 00895 } 00896 00897 //----------------------------------------------------------------------- 00898 QgsCptCityDirectoryItem::QgsCptCityDirectoryItem( QgsCptCityDataItem* parent, 00899 QString name, QString path ) 00900 : QgsCptCityCollectionItem( parent, name, path ) 00901 { 00902 mType = Directory; 00903 mValid = QDir( QgsCptCityArchive::defaultBaseDir() + QDir::separator() + mPath ).exists(); 00904 if ( ! mValid ) 00905 { 00906 QgsDebugMsg( "created invalid dir item, path = " + QgsCptCityArchive::defaultBaseDir() 00907 + QDir::separator() + mPath ); 00908 } 00909 00910 // parse DESC.xml to get mInfo 00911 mInfo = ""; 00912 QString fileName = QgsCptCityArchive::defaultBaseDir() + QDir::separator() + \ 00913 mPath + QDir::separator() + "DESC.xml"; 00914 QgsStringMap descMap = QgsCptCityArchive::description( fileName ); 00915 if ( descMap.contains( "name" ) ) 00916 mInfo = descMap.value( "name" ); 00917 00918 // populate(); 00919 } 00920 00921 QgsCptCityDirectoryItem::~QgsCptCityDirectoryItem() 00922 { 00923 } 00924 00925 QVector<QgsCptCityDataItem*> QgsCptCityDirectoryItem::createChildren() 00926 { 00927 if ( ! mValid ) 00928 return QVector<QgsCptCityDataItem*>(); 00929 00930 QVector<QgsCptCityDataItem*> children; 00931 00932 // add children schemes 00933 QMapIterator< QString, QStringList> it( rampsMap() ); 00934 while ( it.hasNext() ) 00935 { 00936 it.next(); 00937 // QgsDebugMsg( "schemeName = " + it.key() ); 00938 QgsCptCityDataItem* item = 00939 new QgsCptCityColorRampItem( this, it.key(), it.key(), it.value() ); 00940 if ( item->isValid() ) 00941 children << item; 00942 else 00943 delete item; 00944 } 00945 00946 // add children dirs 00947 foreach ( QString childPath, dirEntries() ) 00948 { 00949 QgsCptCityDataItem* childItem = 00950 QgsCptCityDirectoryItem::dataItem( this, childPath, mPath + QDir::separator() + childPath ); 00951 if ( childItem ) 00952 children << childItem; 00953 } 00954 00955 QgsDebugMsg( QString( "name= %1 path= %2 found %3 children" ).arg( mName ).arg( mPath ).arg( children.count() ) ); 00956 00957 return children; 00958 } 00959 00960 QMap< QString, QStringList > QgsCptCityDirectoryItem::rampsMap() 00961 { 00962 if ( ! mRampsMap.isEmpty() ) 00963 return mRampsMap; 00964 00965 QString curName, prevName, prevPath, curVariant, curSep, schemeName; 00966 QStringList listVariant; 00967 QStringList schemeNamesAll, schemeNames; 00968 int num; 00969 bool ok, prevAdd, curAdd; 00970 00971 QDir dir( QgsCptCityArchive::defaultBaseDir() + QDir::separator() + mPath ); 00972 schemeNamesAll = dir.entryList( QStringList( "*.svg" ), QDir::Files, QDir::Name ); 00973 00974 // TODO detect if there are duplicate names with different variant counts, combine in 1 00975 for ( int i = 0; i < schemeNamesAll.count(); i++ ) 00976 { 00977 // schemeName = QFileInfo( schemeNamesAll[i] ).baseName(); 00978 schemeName = schemeNamesAll[i]; 00979 schemeName.chop( 4 ); 00980 // QgsDebugMsg("============="); 00981 // QgsDebugMsg("scheme = "+schemeName); 00982 curName = schemeName; 00983 curVariant = ""; 00984 00985 // stupid code to find if name ends with 1-3 digit number - should use regexp 00986 // TODO need to detect if ends with b/c also 00987 if ( schemeName.length() > 1 && schemeName.endsWith( "a" ) && ! listVariant.isEmpty() && 00988 (( prevName + listVariant.last() + "a" ) == curName ) ) 00989 { 00990 curName = prevName; 00991 curVariant = listVariant.last() + "a"; 00992 } 00993 else 00994 { 00995 num = schemeName.right( 3 ).toInt( &ok ); 00996 Q_UNUSED( num ); 00997 if ( ok ) 00998 { 00999 curName = schemeName.left( schemeName.size() - 3 ); 01000 curVariant = schemeName.right( 3 ); 01001 } 01002 else 01003 { 01004 num = schemeName.right( 2 ).toInt( &ok ); 01005 if ( ok ) 01006 { 01007 curName = schemeName.left( schemeName.size() - 2 ); 01008 curVariant = schemeName.right( 2 ); 01009 } 01010 else 01011 { 01012 num = schemeName.right( 1 ).toInt( &ok ); 01013 if ( ok ) 01014 { 01015 curName = schemeName.left( schemeName.size() - 1 ); 01016 curVariant = schemeName.right( 1 ); 01017 } 01018 } 01019 } 01020 } 01021 curSep = curName.right( 1 ); 01022 if ( curSep == "-" || curSep == "_" ) 01023 { 01024 curName.chop( 1 ); 01025 curVariant = curSep + curVariant; 01026 } 01027 01028 if ( prevName == "" ) 01029 prevName = curName; 01030 01031 // add element, unless it is empty, or a variant of last element 01032 prevAdd = false; 01033 curAdd = false; 01034 if ( curName == "" ) 01035 curName = "__empty__"; 01036 // if current is a variant of last, don't add previous and append current variant 01037 if ( curName == prevName ) 01038 { 01039 // add current element if it is the last one in the archive 01040 if ( i == schemeNamesAll.count() - 1 ) 01041 prevAdd = true; 01042 listVariant << curVariant; 01043 } 01044 else 01045 { 01046 if ( prevName != "" ) 01047 { 01048 prevAdd = true; 01049 } 01050 // add current element if it is the last one in the archive 01051 if ( i == schemeNamesAll.count() - 1 ) 01052 curAdd = true; 01053 } 01054 01055 // QgsDebugMsg(QString("prevAdd=%1 curAdd=%2 prevName=%3 curName=%4 count=%5").arg(prevAdd).arg(curAdd).arg(prevName).arg(curName).arg(listVariant.count())); 01056 01057 if ( prevAdd ) 01058 { 01059 // depending on number of variants, make one or more items 01060 if ( listVariant.count() == 0 ) 01061 { 01062 // set num colors=-1 to parse file on request only 01063 // mSchemeNumColors[ prevName ] = -1; 01064 schemeNames << prevName; 01065 mRampsMap[ mPath + QDir::separator() + prevName ] = QStringList(); 01066 } 01067 else if ( listVariant.count() <= 3 ) 01068 { 01069 // for 1-2 items, create independent items 01070 for ( int j = 0; j < listVariant.count(); j++ ) 01071 { 01072 // mSchemeNumColors[ prevName + listVariant[j] ] = -1; 01073 schemeNames << prevName + listVariant[j]; 01074 mRampsMap[ mPath + QDir::separator() + prevName + listVariant[j] ] = QStringList(); 01075 } 01076 } 01077 else 01078 { 01079 // mSchemeVariants[ path + QDir::separator() + prevName ] = listVariant; 01080 mRampsMap[ mPath + QDir::separator() + prevName ] = listVariant; 01081 schemeNames << prevName; 01082 } 01083 listVariant.clear(); 01084 } 01085 if ( curAdd ) 01086 { 01087 if ( curVariant != "" ) 01088 curName += curVariant; 01089 schemeNames << curName; 01090 mRampsMap[ mPath + QDir::separator() + curName ] = QStringList(); 01091 } 01092 // save current to compare next 01093 if ( prevAdd || curAdd ) 01094 { 01095 prevName = curName; 01096 if ( curVariant != "" ) 01097 listVariant << curVariant; 01098 } 01099 01100 } 01101 //TODO what to do with other vars? e.g. schemeNames 01102 // // add schemes to archive 01103 // mSchemeMap[ path ] = schemeNames; 01104 // schemeCount += schemeName.count(); 01105 // schemeNames.clear(); 01106 // listVariant.clear(); 01107 // prevName = ""; 01108 return mRampsMap; 01109 } 01110 01111 QStringList QgsCptCityDirectoryItem::dirEntries() const 01112 { 01113 return QDir( QgsCptCityArchive::defaultBaseDir() + \ 01114 QDir::separator() + mPath ).entryList( QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name ); 01115 } 01116 01117 bool QgsCptCityDirectoryItem::equal( const QgsCptCityDataItem *other ) 01118 { 01119 //QgsDebugMsg ( mPath + " x " + other->mPath ); 01120 if ( type() != other->type() ) 01121 { 01122 return false; 01123 } 01124 return ( path() == other->path() ); 01125 } 01126 01127 QgsCptCityDataItem* QgsCptCityDirectoryItem::dataItem( QgsCptCityDataItem* parent, 01128 QString name, QString path ) 01129 { 01130 QgsDebugMsg( "name= " + name + " path= " + path ); 01131 01132 // first create item with constructor 01133 QgsCptCityDirectoryItem* dirItem = new QgsCptCityDirectoryItem( parent, name, path ); 01134 if ( dirItem && ! dirItem->isValid() ) 01135 { 01136 delete dirItem; 01137 return 0; 01138 } 01139 if ( ! dirItem ) 01140 return 0; 01141 01142 // fetch sub-dirs and ramps to know what to do with this item 01143 QStringList theDirEntries = dirItem->dirEntries(); 01144 QMap< QString, QStringList > theRampsMap = dirItem->rampsMap(); 01145 01146 QgsDebugMsg( QString( "item has %1 dirs and %2 ramps" ).arg( theDirEntries.count() ).arg( theRampsMap.count() ) ); 01147 01148 // return item if has at least one subdir 01149 if ( theDirEntries.count() > 0 ) 01150 return dirItem; 01151 01152 // if 0 ramps, delete item 01153 if ( theRampsMap.count() == 0 ) 01154 { 01155 delete dirItem; 01156 return 0; 01157 } 01158 // if 1 ramp, return this child's item 01159 // so we don't have a directory with just 1 item (with many variants possibly) 01160 else if ( theRampsMap.count() == 1 ) 01161 { 01162 delete dirItem; 01163 QgsCptCityColorRampItem* rampItem = 01164 new QgsCptCityColorRampItem( parent, theRampsMap.begin().key(), 01165 theRampsMap.begin().key(), theRampsMap.begin().value() ); 01166 if ( ! rampItem->isValid() ) 01167 { 01168 delete rampItem; 01169 return 0; 01170 } 01171 return rampItem; 01172 } 01173 return dirItem; 01174 } 01175 01176 01177 //----------------------------------------------------------------------- 01178 QgsCptCitySelectionItem::QgsCptCitySelectionItem( QgsCptCityDataItem* parent, 01179 QString name, QString path ) 01180 : QgsCptCityCollectionItem( parent, name, path ) 01181 { 01182 mType = Selection; 01183 mValid = ! path.isNull(); 01184 if ( mValid ) 01185 parseXML(); 01186 } 01187 01188 QgsCptCitySelectionItem::~QgsCptCitySelectionItem() 01189 { 01190 } 01191 01192 QVector<QgsCptCityDataItem*> QgsCptCitySelectionItem::createChildren() 01193 { 01194 if ( ! mValid ) 01195 return QVector<QgsCptCityDataItem*>(); 01196 01197 QgsCptCityDataItem* item = 0; 01198 QVector<QgsCptCityDataItem*> children; 01199 01200 QgsDebugMsg( "name= " + mName + " path= " + mPath ); 01201 01202 // add children archives 01203 foreach ( QString childPath, mSelectionsList ) 01204 { 01205 QgsDebugMsg( "childPath = " + childPath + " name= " + QFileInfo( childPath ).baseName() ); 01206 if ( childPath.endsWith( "/" ) ) 01207 { 01208 childPath.chop( 1 ); 01209 QgsCptCityDataItem* childItem = 01210 QgsCptCityDirectoryItem::dataItem( this, childPath, childPath ); 01211 if ( childItem ) 01212 { 01213 if ( childItem->isValid() ) 01214 children << childItem; 01215 else 01216 delete childItem; 01217 } 01218 } 01219 else 01220 { 01221 // init item to test if is valid after loading file 01222 item = new QgsCptCityColorRampItem( this, childPath, childPath, QString(), true ); 01223 if ( item->isValid() ) 01224 children << item; 01225 else 01226 delete item; 01227 } 01228 } 01229 01230 QgsDebugMsg( QString( "path= %1 inserted %2 children" ).arg( mPath ).arg( children.count() ) ); 01231 01232 return children; 01233 } 01234 01235 void QgsCptCitySelectionItem::parseXML() 01236 { 01237 QString filename = QgsCptCityArchive::defaultBaseDir() + QDir::separator() + mPath; 01238 01239 QgsDebugMsg( "reading file " + filename ); 01240 01241 QFile f( filename ); 01242 if ( ! f.open( QFile::ReadOnly ) ) 01243 { 01244 QgsDebugMsg( filename + " does not exist" ); 01245 return; 01246 } 01247 01248 // parse the document 01249 QString errMsg; 01250 QDomDocument doc( "selection" ); 01251 if ( !doc.setContent( &f, &errMsg ) ) 01252 { 01253 f.close(); 01254 QgsDebugMsg( "Couldn't parse file " + filename + " : " + errMsg ); 01255 return; 01256 } 01257 f.close(); 01258 01259 // read description 01260 QDomElement docElem = doc.documentElement(); 01261 if ( docElem.tagName() != "selection" ) 01262 { 01263 QgsDebugMsg( "Incorrect root tag: " + docElem.tagName() ); 01264 return; 01265 } 01266 QDomElement e = docElem.firstChildElement( "name" ); 01267 if ( ! e.isNull() && ! e.text().isNull() ) 01268 mName = e.text(); 01269 mInfo = docElem.firstChildElement( "synopsis" ).text().simplified(); 01270 01271 // get archives 01272 QDomElement collectsElem = docElem.firstChildElement( "seealsocollects" ); 01273 e = collectsElem.firstChildElement( "collect" ); 01274 while ( ! e.isNull() ) 01275 { 01276 if ( ! e.attribute( "dir" ).isNull() ) 01277 { 01278 // TODO parse description and use that, instead of default archive name 01279 mSelectionsList << e.attribute( "dir" ) + "/"; 01280 } 01281 e = e.nextSiblingElement(); 01282 } 01283 // get individual gradients 01284 QDomElement gradientsElem = docElem.firstChildElement( "gradients" ); 01285 e = gradientsElem.firstChildElement( "gradient" ); 01286 while ( ! e.isNull() ) 01287 { 01288 if ( ! e.attribute( "dir" ).isNull() ) 01289 { 01290 // QgsDebugMsg( "add " + e.attribute( "dir" ) + "/" + e.attribute( "file" ) + " to " + selname ); 01291 // TODO parse description and save elsewhere 01292 mSelectionsList << e.attribute( "dir" ) + "/" + e.attribute( "file" ); 01293 } 01294 e = e.nextSiblingElement(); 01295 } 01296 } 01297 01298 bool QgsCptCitySelectionItem::equal( const QgsCptCityDataItem *other ) 01299 { 01300 //QgsDebugMsg ( mPath + " x " + other->mPath ); 01301 if ( type() != other->type() ) 01302 { 01303 return false; 01304 } 01305 return ( path() == other->path() ); 01306 } 01307 01308 //----------------------------------------------------------------------- 01309 QgsCptCityAllRampsItem::QgsCptCityAllRampsItem( QgsCptCityDataItem* parent, 01310 QString name, QVector<QgsCptCityDataItem*> items ) 01311 : QgsCptCityCollectionItem( parent, name, QString() ), mItems( items ) 01312 { 01313 mType = AllRamps; 01314 mValid = true; 01315 // populate(); 01316 } 01317 01318 QgsCptCityAllRampsItem::~QgsCptCityAllRampsItem() 01319 { 01320 } 01321 01322 QVector<QgsCptCityDataItem*> QgsCptCityAllRampsItem::createChildren() 01323 { 01324 if ( ! mValid ) 01325 return QVector<QgsCptCityDataItem*>(); 01326 01327 QVector<QgsCptCityDataItem*> children; 01328 01329 // add children ramps of each item 01330 foreach ( QgsCptCityDataItem* item, mItems ) 01331 { 01332 QgsCptCityCollectionItem* colItem = dynamic_cast< QgsCptCityCollectionItem* >( item ); 01333 if ( colItem ) 01334 children += colItem->childrenRamps( true ); 01335 } 01336 01337 return children; 01338 } 01339 01340 //----------------------------------------------------------------------- 01341 01342 QgsCptCityBrowserModel::QgsCptCityBrowserModel( QObject *parent, 01343 QgsCptCityArchive* archive, ViewType viewType ) 01344 : QAbstractItemModel( parent ), mArchive( archive ), mViewType( viewType ) 01345 { 01346 Q_ASSERT( mArchive != NULL ); 01347 QgsDebugMsg( "archiveName = " + archive->archiveName() + " viewType=" + ( int ) viewType ); 01348 // keep iconsize for now, but not effectively used 01349 mIconSize = QSize( 100, 15 ); 01350 addRootItems(); 01351 } 01352 01353 QgsCptCityBrowserModel::~QgsCptCityBrowserModel() 01354 { 01355 removeRootItems(); 01356 } 01357 01358 void QgsCptCityBrowserModel::addRootItems( ) 01359 { 01360 if ( mViewType == Authors ) 01361 { 01362 mRootItems = mArchive->rootItems(); 01363 } 01364 else if ( mViewType == Selections ) 01365 { 01366 mRootItems = mArchive->selectionItems(); 01367 } 01368 QgsDebugMsg( QString( "added %1 root items" ).arg( mRootItems.size() ) ); 01369 } 01370 01371 void QgsCptCityBrowserModel::removeRootItems() 01372 { 01373 // don't remove root items, they belong to the QgsCptCityArchive 01374 // foreach ( QgsCptCityDataItem* item, mRootItems ) 01375 // { 01376 // delete item; 01377 // } 01378 01379 mRootItems.clear(); 01380 } 01381 01382 Qt::ItemFlags QgsCptCityBrowserModel::flags( const QModelIndex & index ) const 01383 { 01384 if ( !index.isValid() ) 01385 return 0; 01386 01387 Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; 01388 01389 return flags; 01390 } 01391 01392 QVariant QgsCptCityBrowserModel::data( const QModelIndex &index, int role ) const 01393 { 01394 if ( !index.isValid() ) 01395 return QVariant(); 01396 01397 QgsCptCityDataItem *item = dataItem( index ); 01398 01399 if ( !item ) 01400 { 01401 return QVariant(); 01402 } 01403 else if ( role == Qt::DisplayRole ) 01404 { 01405 if ( index.column() == 0 ) 01406 return item->name(); 01407 if ( index.column() == 1 ) 01408 { 01409 return item->info(); 01410 } 01411 } 01412 else if ( role == Qt::ToolTipRole ) 01413 { 01414 if ( item->type() == QgsCptCityDataItem::ColorRamp && 01415 mViewType == List ) 01416 return item->path() + "\n" + item->info(); 01417 return item->toolTip(); 01418 } 01419 else if ( role == Qt::DecorationRole && index.column() == 1 && 01420 item->type() == QgsCptCityDataItem::ColorRamp ) 01421 { 01422 // keep iconsize for now, but not effectively used 01423 return item->icon( mIconSize ); 01424 } 01425 else if ( role == Qt::FontRole && 01426 ( dynamic_cast< QgsCptCityCollectionItem* >( item ) != 0 ) ) 01427 { 01428 // collectionitems are larger and bold 01429 QFont font; 01430 font.setPointSize( 11 ); //FIXME why is the font so small? 01431 font.setBold( true ); 01432 return font; 01433 } 01434 else 01435 { 01436 // unsupported role 01437 return QVariant(); 01438 } 01439 return QVariant(); 01440 } 01441 01442 QVariant QgsCptCityBrowserModel::headerData( int section, Qt::Orientation orientation, int role ) const 01443 { 01444 Q_UNUSED( section ); 01445 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole ) 01446 { 01447 if ( section == 0 ) 01448 return QVariant( tr( "Name" ) ); 01449 else if ( section == 1 ) 01450 return QVariant( tr( "Info" ) ); 01451 } 01452 return QVariant(); 01453 } 01454 01455 int QgsCptCityBrowserModel::rowCount( const QModelIndex &parent ) const 01456 { 01457 //qDebug("rowCount: idx: (valid %d) %d %d", parent.isValid(), parent.row(), parent.column()); 01458 01459 if ( !parent.isValid() ) 01460 { 01461 // root item: its children are top level items 01462 return mRootItems.count(); // mRoot 01463 } 01464 else 01465 { 01466 // ordinary item: number of its children 01467 QgsCptCityDataItem *item = dataItem( parent ); 01468 return item ? item->rowCount() : 0; 01469 } 01470 } 01471 01472 bool QgsCptCityBrowserModel::hasChildren( const QModelIndex &parent ) const 01473 { 01474 if ( !parent.isValid() ) 01475 return true; // root item: its children are top level items 01476 01477 QgsCptCityDataItem *item = dataItem( parent ); 01478 01479 return item && item->hasChildren(); 01480 } 01481 01482 int QgsCptCityBrowserModel::columnCount( const QModelIndex &parent ) const 01483 { 01484 Q_UNUSED( parent ); 01485 return 2; 01486 } 01487 01488 QModelIndex QgsCptCityBrowserModel::findPath( QString path ) 01489 { 01490 QModelIndex theIndex; // starting from root 01491 bool foundParent = false, foundChild = true; 01492 QString itemPath; 01493 01494 QgsDebugMsg( "path = " + path ); 01495 01496 while ( foundChild ) 01497 { 01498 foundChild = false; // assume that the next child item will not be found 01499 01500 int i = 0; 01501 // if root skip first item "All Ramps" 01502 if ( itemPath.isEmpty() ) 01503 i = 1; 01504 for ( ; i < rowCount( theIndex ); i++ ) 01505 { 01506 QModelIndex idx = index( i, 0, theIndex ); 01507 QgsCptCityDataItem *item = dataItem( idx ); 01508 if ( !item ) 01509 return QModelIndex(); // an error occurred 01510 01511 itemPath = item->path(); 01512 01513 if ( itemPath == path ) 01514 { 01515 QgsDebugMsg( "Arrived " + itemPath ); 01516 return idx; // we have found the item we have been looking for 01517 } 01518 01519 if ( ! itemPath.endsWith( "/" ) ) 01520 itemPath += "/"; 01521 01522 foundParent = false; 01523 01524 // QgsDebugMsg( "path= " + path + " itemPath= " + itemPath ); 01525 01526 // if we are using a selection collection, search for target in the mapping in this group 01527 if ( item->type() == QgsCptCityDataItem::Selection ) 01528 { 01529 const QgsCptCitySelectionItem* selItem = dynamic_cast<const QgsCptCitySelectionItem *>( item ); 01530 foreach ( QString childPath, selItem->selectionsList() ) 01531 { 01532 if ( childPath.endsWith( "/" ) ) 01533 childPath.chop( 1 ); 01534 // QgsDebugMsg( "childPath= " + childPath ); 01535 if ( path.startsWith( childPath ) ) 01536 { 01537 foundParent = true; 01538 break; 01539 } 01540 } 01541 } 01542 // search for target in parent directory 01543 else if ( path.startsWith( itemPath ) ) 01544 { 01545 foundParent = true; 01546 } 01547 01548 if ( foundParent ) 01549 { 01550 QgsDebugMsg( "found parent " + path ); 01551 // we have found a preceding item: stop searching on this level and go deeper 01552 foundChild = true; 01553 theIndex = idx; 01554 if ( canFetchMore( theIndex ) ) 01555 fetchMore( theIndex ); 01556 break; 01557 } 01558 } 01559 } 01560 01561 return QModelIndex(); // not found 01562 } 01563 01564 void QgsCptCityBrowserModel::reload() 01565 { 01566 removeRootItems(); 01567 addRootItems(); 01568 reset(); // Qt4.6 brings better methods beginResetModel + endResetModel 01569 } 01570 01571 /* Refresh dir path */ 01572 void QgsCptCityBrowserModel::refresh( QString path ) 01573 { 01574 QModelIndex idx = findPath( path ); 01575 if ( idx.isValid() ) 01576 { 01577 QgsCptCityDataItem* item = dataItem( idx ); 01578 if ( item ) 01579 item->refresh(); 01580 } 01581 } 01582 01583 QModelIndex QgsCptCityBrowserModel::index( int row, int column, const QModelIndex &parent ) const 01584 { 01585 QgsCptCityDataItem *p = dataItem( parent ); 01586 const QVector<QgsCptCityDataItem*> &items = p ? p->children() : mRootItems; 01587 QgsCptCityDataItem *item = items.value( row, 0 ); 01588 return item ? createIndex( row, column, item ) : QModelIndex(); 01589 } 01590 01591 QModelIndex QgsCptCityBrowserModel::parent( const QModelIndex &index ) const 01592 { 01593 QgsCptCityDataItem *item = dataItem( index ); 01594 if ( !item ) 01595 return QModelIndex(); 01596 01597 return findItem( item->parent() ); 01598 } 01599 01600 QModelIndex QgsCptCityBrowserModel::findItem( QgsCptCityDataItem *item, QgsCptCityDataItem *parent ) const 01601 { 01602 const QVector<QgsCptCityDataItem*> &items = parent ? parent->children() : mRootItems; 01603 01604 for ( int i = 0; i < items.size(); i++ ) 01605 { 01606 if ( items[i] == item ) 01607 return createIndex( i, 0, item ); 01608 01609 QModelIndex childIndex = findItem( item, items[i] ); 01610 if ( childIndex.isValid() ) 01611 return childIndex; 01612 } 01613 01614 return QModelIndex(); 01615 } 01616 01617 /* Refresh item */ 01618 void QgsCptCityBrowserModel::refresh( const QModelIndex& theIndex ) 01619 { 01620 QgsCptCityDataItem *item = dataItem( theIndex ); 01621 if ( !item ) 01622 return; 01623 01624 QgsDebugMsg( "Refresh " + item->path() ); 01625 item->refresh(); 01626 } 01627 01628 void QgsCptCityBrowserModel::beginInsertItems( QgsCptCityDataItem *parent, int first, int last ) 01629 { 01630 QgsDebugMsg( "parent mPath = " + parent->path() ); 01631 QModelIndex idx = findItem( parent ); 01632 if ( !idx.isValid() ) 01633 return; 01634 QgsDebugMsg( "valid" ); 01635 beginInsertRows( idx, first, last ); 01636 QgsDebugMsg( "end" ); 01637 } 01638 void QgsCptCityBrowserModel::endInsertItems() 01639 { 01640 QgsDebugMsg( "Entered" ); 01641 endInsertRows(); 01642 } 01643 void QgsCptCityBrowserModel::beginRemoveItems( QgsCptCityDataItem *parent, int first, int last ) 01644 { 01645 QgsDebugMsg( "parent mPath = " + parent->path() ); 01646 QModelIndex idx = findItem( parent ); 01647 if ( !idx.isValid() ) 01648 return; 01649 beginRemoveRows( idx, first, last ); 01650 } 01651 void QgsCptCityBrowserModel::endRemoveItems() 01652 { 01653 QgsDebugMsg( "Entered" ); 01654 endRemoveRows(); 01655 } 01656 void QgsCptCityBrowserModel::connectItem( QgsCptCityDataItem* item ) 01657 { 01658 connect( item, SIGNAL( beginInsertItems( QgsCptCityDataItem*, int, int ) ), 01659 this, SLOT( beginInsertItems( QgsCptCityDataItem*, int, int ) ) ); 01660 connect( item, SIGNAL( endInsertItems() ), 01661 this, SLOT( endInsertItems() ) ); 01662 connect( item, SIGNAL( beginRemoveItems( QgsCptCityDataItem*, int, int ) ), 01663 this, SLOT( beginRemoveItems( QgsCptCityDataItem*, int, int ) ) ); 01664 connect( item, SIGNAL( endRemoveItems() ), 01665 this, SLOT( endRemoveItems() ) ); 01666 } 01667 01668 bool QgsCptCityBrowserModel::canFetchMore( const QModelIndex & parent ) const 01669 { 01670 QgsCptCityDataItem* item = dataItem( parent ); 01671 // fetch all items initially so we know which items have children 01672 // (nicer looking and less confusing) 01673 01674 if ( ! item ) 01675 return false; 01676 01677 // except for "All Ramps" - this is populated when clicked on 01678 if ( item->type() == QgsCptCityDataItem::AllRamps ) 01679 return false; 01680 01681 item->populate(); 01682 01683 return ( ! item->isPopulated() ); 01684 } 01685 01686 void QgsCptCityBrowserModel::fetchMore( const QModelIndex & parent ) 01687 { 01688 QgsCptCityDataItem* item = dataItem( parent ); 01689 if ( item ) 01690 item->populate(); 01691 QgsDebugMsg( "path = " + item->path() ); 01692 } 01693 01694 01695 #if 0 01696 QStringList QgsCptCityBrowserModel::mimeTypes() const 01697 { 01698 QStringList types; 01699 // In theory the mime type convention is: application/x-vnd.<vendor>.<application>.<type> 01700 // but it seems a bit over formalized. Would be an application/x-qgis-uri better? 01701 types << "application/x-vnd.qgis.qgis.uri"; 01702 return types; 01703 } 01704 01705 QMimeData * QgsCptCityBrowserModel::mimeData( const QModelIndexList &indexes ) const 01706 { 01707 QgsMimeDataUtils::UriList lst; 01708 foreach ( const QModelIndex &index, indexes ) 01709 { 01710 if ( index.isValid() ) 01711 { 01712 QgsCptCityDataItem* ptr = ( QgsCptCityDataItem* ) index.internalPointer(); 01713 if ( ptr->type() != QgsCptCityDataItem::Layer ) continue; 01714 QgsLayerItem *layer = ( QgsLayerItem* ) ptr; 01715 lst.append( QgsMimeDataUtils::Uri( ayer ) ); 01716 } 01717 } 01718 return QgsMimeDataUtils::encodeUriList( lst ); 01719 } 01720 01721 bool QgsCptCityBrowserModel::dropMimeData( const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent ) 01722 { 01723 Q_UNUSED( row ); 01724 Q_UNUSED( column ); 01725 01726 QgsCptCityDataItem* destItem = dataItem( parent ); 01727 if ( !destItem ) 01728 { 01729 QgsDebugMsg( "DROP PROBLEM!" ); 01730 return false; 01731 } 01732 01733 return destItem->handleDrop( data, action ); 01734 } 01735 #endif 01736 01737 QgsCptCityDataItem *QgsCptCityBrowserModel::dataItem( const QModelIndex &idx ) const 01738 { 01739 void *v = idx.internalPointer(); 01740 QgsCptCityDataItem *d = reinterpret_cast<QgsCptCityDataItem*>( v ); 01741 Q_ASSERT( !v || d ); 01742 return d; 01743 }