QGIS API Documentation  master-3f58142
src/core/symbology-ng/qgscptcityarchive.cpp
Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines