23 #include "qgsconfig.h"
28 #include <QApplication>
34 #include <QDomDocument>
35 #include <QDomElement>
36 #include <QRegularExpression>
47 return *sArchiveRegistry();
51 : mArchiveName( archiveName )
58 const auto constEntryList = QDir( mBaseDir ).entryList( QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name );
59 for (
const QString &path : constEntryList )
61 if ( path == QLatin1String(
"selections" ) )
66 mRootItems << dirItem;
73 const QDir seldir( mBaseDir +
'/' +
"selections" );
75 const QStringList fileList = seldir.entryList( QStringList() << QStringLiteral(
"*.xml" ), QDir::Files );
76 for (
const QString &selfile : fileList )
80 seldir.dirName() +
'/' + selfile );
83 mSelectionItems << selItem;
92 mRootItems.prepend( allRampsItem );
95 mSelectionItems.prepend( allRampsItem );
100 const auto constMRootItems = mRootItems;
103 const auto constMSelectionItems = mSelectionItems;
107 mSelectionItems.clear();
114 if ( ! mBaseDir.isNull() )
126 return archive->baseDir();
137 baseDir = settings.
value( QStringLiteral(
"CptCity/baseDir" ),
150 if ( startDir.isEmpty() || ! startDir.startsWith(
baseDir ) )
153 QDir dir = QDir( startDir );
155 while ( ! dir.exists( target ) && dir.path() !=
baseDir )
160 if ( ! dir.exists( target ) )
163 return dir.path() +
'/' + target;
183 if ( fileName.isNull() )
186 if ( sCopyingInfoMap()->contains( fileName ) )
188 QgsDebugMsgLevel(
"found copying info in copyingInfoMap, file = " + fileName, 2 );
189 return sCopyingInfoMap()->value( fileName );
196 if ( !f.open( QFile::ReadOnly ) )
198 QgsDebugMsg(
"Couldn't open xml file: " + fileName );
203 QDomDocument doc( QStringLiteral(
"license" ) );
204 if ( !doc.setContent( &f ) )
207 QgsDebugMsg(
"Couldn't parse xml file: " + fileName );
213 const QDomElement docElem = doc.documentElement();
214 if ( docElem.tagName() != QLatin1String(
"copying" ) )
216 QgsDebugMsg(
"Incorrect root tag: " + docElem.tagName() );
221 const QDomElement authorsElement = docElem.firstChildElement( QStringLiteral(
"authors" ) );
222 if ( authorsElement.isNull() )
228 QDomElement e = authorsElement.firstChildElement();
230 while ( ! e.isNull() )
232 if ( e.tagName() == QLatin1String(
"author" ) )
234 if ( ! e.firstChildElement( QStringLiteral(
"name" ) ).isNull() )
235 authors << e.firstChildElement( QStringLiteral(
"name" ) ).text().simplified();
238 e = e.nextSiblingElement();
240 copyingMap[ QStringLiteral(
"authors" )] = authors.join( QLatin1String(
", " ) );
244 const QDomElement licenseElement = docElem.firstChildElement( QStringLiteral(
"license" ) );
245 if ( licenseElement.isNull() )
251 QDomElement e = licenseElement.firstChildElement( QStringLiteral(
"informal" ) );
253 copyingMap[ QStringLiteral(
"license/informal" )] = e.text().simplified();
254 e = licenseElement.firstChildElement( QStringLiteral(
"year" ) );
256 copyingMap[ QStringLiteral(
"license/year" )] = e.text().simplified();
257 e = licenseElement.firstChildElement( QStringLiteral(
"text" ) );
258 if ( ! e.isNull() && e.attribute( QStringLiteral(
"href" ) ) != QString() )
259 copyingMap[ QStringLiteral(
"license/url" )] = e.attribute( QStringLiteral(
"href" ) );
263 const QDomElement element = docElem.firstChildElement( QStringLiteral(
"src" ) );
264 if ( element.isNull() )
270 const QDomElement e = element.firstChildElement( QStringLiteral(
"link" ) );
271 if ( ! e.isNull() && e.attribute( QStringLiteral(
"href" ) ) != QString() )
272 copyingMap[ QStringLiteral(
"src/link" )] = e.attribute( QStringLiteral(
"href" ) );
276 ( *sCopyingInfoMap() )[ fileName ] = copyingMap;
287 if ( ! f.open( QFile::ReadOnly ) )
289 QgsDebugMsgLevel(
"description file " + fileName +
" ] does not exist", 2 );
295 QDomDocument doc( QStringLiteral(
"description" ) );
296 if ( !doc.setContent( &f, &errMsg ) )
299 QgsDebugMsg(
"Couldn't parse file " + fileName +
" : " + errMsg );
305 const QDomElement docElem = doc.documentElement();
306 if ( docElem.tagName() != QLatin1String(
"description" ) )
308 QgsDebugMsg(
"Incorrect root tag: " + docElem.tagName() );
313 QDomElement e = docElem.firstChildElement( QStringLiteral(
"name" ) );
318 descMap[ QStringLiteral(
"name" )] = e.text().simplified();
319 e = docElem.firstChildElement( QStringLiteral(
"full" ) );
324 descMap[ QStringLiteral(
"full" )] = e.text().simplified();
331 QMap< double, QPair<QColor, QColor> > colorMap;
335 if ( !f.open( QFile::ReadOnly ) )
337 QgsDebugMsg(
"Couldn't open SVG file: " + fileName );
342 QDomDocument doc( QStringLiteral(
"gradient" ) );
343 if ( !doc.setContent( &f ) )
346 QgsDebugMsg(
"Couldn't parse SVG file: " + fileName );
351 const QDomElement docElem = doc.documentElement();
353 if ( docElem.tagName() != QLatin1String(
"svg" ) )
355 QgsDebugMsg(
"Incorrect root tag: " + docElem.tagName() );
360 QDomElement rampsElement = docElem.firstChildElement( QStringLiteral(
"linearGradient" ) );
361 if ( rampsElement.isNull() )
363 const QDomNodeList nodeList = docElem.elementsByTagName( QStringLiteral(
"linearGradient" ) );
364 if ( ! nodeList.isEmpty() )
365 rampsElement = nodeList.at( 0 ).toElement();
367 if ( rampsElement.isNull() )
369 QgsDebugMsg( QStringLiteral(
"linearGradient tag missing" ) );
374 QDomElement e = rampsElement.firstChildElement();
376 while ( !e.isNull() )
378 if ( e.tagName() == QLatin1String(
"stop" ) )
382 QString offsetStr = e.attribute( QStringLiteral(
"offset" ) );
383 const QString colorStr = e.attribute( QStringLiteral(
"stop-color" ), QString() );
384 const QString opacityStr = e.attribute( QStringLiteral(
"stop-opacity" ), QStringLiteral(
"1.0" ) );
385 if ( offsetStr.endsWith(
'%' ) )
386 offset = offsetStr.remove( offsetStr.size() - 1, 1 ).toDouble() / 100.0;
388 offset = offsetStr.toDouble();
391 if ( colorStr.isEmpty() )
394 color = QColor( 0, 0, 0 );
401 if ( color.isValid() )
403 const int alpha = opacityStr.toDouble() * 255;
404 color.setAlpha( alpha );
405 if ( colorMap.contains( offset ) )
406 colorMap[offset].second = color;
408 colorMap[offset] = qMakePair( color, color );
412 QgsDebugMsg( QStringLiteral(
"at offset=%1 invalid color \"%2\"" ).arg( offset ).arg( colorStr ) );
420 e = e.nextSiblingElement();
428 return ( mRootItems.isEmpty() );
436 if ( sArchiveRegistry()->contains( *sDefaultArchiveName() ) )
437 return sArchiveRegistry()->value( *sDefaultArchiveName() );
455 const QString
baseDir = settings.
value( QStringLiteral(
"CptCity/baseDir" ),
460 if ( ! sArchiveRegistry()->contains( defArchiveName ) )
467 QString
baseDir, defArchiveName;
471 baseDir = settings.
value( QStringLiteral(
"CptCity/baseDir" ),
480 const QStringList fileList = dir.entryList( QStringList() << QStringLiteral(
"cpt-city*" ), QDir::Dirs );
481 for (
const QString &entry : fileList )
483 if ( QFile::exists(
baseDir +
'/' + entry +
"/VERSION.xml" ) )
484 archivesMap[ entry ] =
baseDir +
'/' + entry;
489 archivesMap[ defArchiveName ] =
baseDir +
'/' + defArchiveName;
492 for ( QgsStringMap::iterator it = archivesMap.begin();
493 it != archivesMap.end(); ++it )
495 if ( QDir( it.value() ).exists() )
499 QgsDebugMsg( QStringLiteral(
"not loading archive [%1] because dir %2 does not exist " ).arg( it.key(), it.value() ) );
502 *sDefaultArchiveName() = defArchiveName;
507 qDeleteAll( *sArchiveRegistry() );
508 sArchiveRegistry()->clear();
515 const QString &name,
const QString &path )
519 , mPopulated( false )
528 QVector<QgsCptCityDataItem *>
children;
539 QApplication::setOverrideCursor( Qt::WaitCursor );
542 const auto constChildren =
children;
550 QApplication::restoreOverrideCursor();
570 count += child->leafCount();
592 mChildren.at( i )->mName.localeAwareCompare( child->
mName ) >= 0 )
600 if (
mChildren.at( i )->mName.localeAwareCompare( child->
mName ) >= 0 )
621 const int i =
mChildren.indexOf( child );
632 const int i =
mChildren.indexOf( child );
647 for (
int i = 0; i < items.size(); i++ )
650 if ( items[i]->
equal( item ) )
660 QApplication::setOverrideCursor( Qt::WaitCursor );
665 QVector<QgsCptCityDataItem *> remove;
669 if (
findItem( items, child ) >= 0 )
671 remove.append( child );
673 const auto constRemove = remove;
680 const auto constItems = items;
692 QApplication::restoreOverrideCursor();
697 return ( metaObject()->className() == other->metaObject()->className() &&
704 const QString &name,
const QString &path,
const QString &variantName,
bool initialize )
706 , mInitialized( false )
707 , mRamp( path, variantName, false )
716 const QString &name,
const QString &path,
const QStringList &variantList,
bool initialize )
718 , mInitialized( false )
719 , mRamp( path, variantList, QString(), false )
752 if ( variantList.isEmpty() )
757 mInfo = QString::number( count ) +
' ' + tr(
"colors" ) +
" - ";
759 mInfo += tr(
"discrete" );
763 mInfo += tr(
"continuous" );
765 mInfo += tr(
"continuous (multi)" );
771 mInfo = QString::number( variantList.count() ) +
' ' + tr(
"variants" );
800 return icon( QSize( 100, 15 ) );
805 const auto constMIcons =
mIcons;
806 for (
const QIcon &
icon : constMIcons )
808 if (
icon.availableSizes().contains( size ) )
822 QPixmap blankPixmap( size );
823 blankPixmap.fill( Qt::white );
824 icon = QIcon( blankPixmap );
834 const QString &name,
const QString &path )
836 , mPopulatedRamps( false )
847 QVector< QgsCptCityDataItem * > rampItems;
848 QVector< QgsCptCityDataItem * > deleteItems;
853 const auto constChildren =
children();
858 QgsDebugMsgLevel( QStringLiteral(
"child path= %1 coll= %2 ramp = %3" ).arg( childItem->path() ).arg(
nullptr != collectionItem ).arg(
nullptr != rampItem ), 2 );
859 if ( collectionItem && recursive )
869 rampItems << rampItem;
871 deleteItems << rampItem;
875 QgsDebugMsg(
"invalid item " + childItem->path() );
880 const auto constDeleteItems = deleteItems;
883 QgsDebugMsg( QStringLiteral(
"item %1 is invalid, will be deleted" ).arg( deleteItem->path() ) );
884 const int i =
mChildren.indexOf( deleteItem );
895 const QString &name,
const QString &path )
909 mPath +
'/' +
"DESC.xml";
911 if ( descMap.contains( QStringLiteral(
"name" ) ) )
912 mInfo = descMap.value( QStringLiteral(
"name" ) );
920 return QVector<QgsCptCityDataItem *>();
922 QVector<QgsCptCityDataItem *>
children;
925 QMapIterator< QString, QStringList> it(
rampsMap() );
926 while ( it.hasNext() )
940 for (
const QString &childPath : constDirEntries )
958 QString curName, prevName, curVariant, curSep, schemeName;
959 QStringList listVariant;
960 QStringList schemeNamesAll, schemeNames;
961 bool prevAdd, curAdd;
964 schemeNamesAll = dir.entryList( QStringList( QStringLiteral(
"*.svg" ) ), QDir::Files, QDir::Name );
967 for (
int i = 0; i < schemeNamesAll.count(); i++ )
970 schemeName = schemeNamesAll[i];
971 schemeName.chop( 4 );
973 curName = schemeName;
978 if ( schemeName.length() > 1 && schemeName.endsWith(
'a' ) && ! listVariant.isEmpty() &&
979 ( ( prevName + listVariant.last() +
'a' ) == curName ) )
982 curVariant = listVariant.last() +
'a';
986 const thread_local QRegularExpression rxVariant(
"^(.*[^\\d])(\\d{1,3})$" );
987 const QRegularExpressionMatch match = rxVariant.match( schemeName );
988 if ( match.hasMatch() )
990 curName = match.captured( 1 );
991 curVariant = match.captured( 2 );
995 curSep = curName.right( 1 );
996 if ( curSep == QLatin1String(
"-" ) || curSep == QLatin1String(
"_" ) )
999 curVariant = curSep + curVariant;
1002 if ( prevName.isEmpty() )
1008 if ( curName.isEmpty() )
1009 curName = QStringLiteral(
"__empty__" );
1011 if ( curName == prevName )
1014 if ( i == schemeNamesAll.count() - 1 )
1016 listVariant << curVariant;
1020 if ( !prevName.isEmpty() )
1025 if ( i == schemeNamesAll.count() - 1 )
1034 if ( listVariant.isEmpty() )
1038 schemeNames << prevName;
1041 else if ( listVariant.count() <= 3 )
1044 for (
int j = 0; j < listVariant.count(); j++ )
1047 schemeNames << prevName + listVariant[j];
1048 mRampsMap[
mPath +
'/' + prevName + listVariant[j] ] = QStringList();
1055 schemeNames << prevName;
1057 listVariant.clear();
1061 if ( !curVariant.isEmpty() )
1062 curName += curVariant;
1063 schemeNames << curName;
1067 if ( prevAdd || curAdd )
1070 if ( !curVariant.isEmpty() )
1071 listVariant << curVariant;
1078 mSchemeMap[
path ] = schemeNames;
1079 schemeCount += schemeName.count();
1080 schemeNames.clear();
1081 listVariant.clear();
1090 '/' +
mPath ).entryList( QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name );
1100 return (
path() == other->
path() );
1104 const QString &name,
const QString &path )
1110 if ( dirItem && ! dirItem->
isValid() )
1155 const QString &name,
const QString &path )
1167 return QVector<QgsCptCityDataItem *>();
1170 QVector<QgsCptCityDataItem *>
children;
1177 QgsDebugMsgLevel(
"childPath = " + childPath +
" name= " + QFileInfo( childPath ).baseName(), 2 );
1178 if ( childPath.endsWith(
'/' ) )
1180 childPath.chop( 1 );
1194 if ( !QFile::exists( fileName ) )
1218 QFile f( filename );
1219 if ( ! f.open( QFile::ReadOnly ) )
1227 QDomDocument doc( QStringLiteral(
"selection" ) );
1228 if ( !doc.setContent( &f, &errMsg ) )
1231 QgsDebugMsg(
"Couldn't parse file " + filename +
" : " + errMsg );
1237 const QDomElement docElem = doc.documentElement();
1238 if ( docElem.tagName() != QLatin1String(
"selection" ) )
1240 QgsDebugMsg(
"Incorrect root tag: " + docElem.tagName() );
1243 QDomElement e = docElem.firstChildElement( QStringLiteral(
"name" ) );
1244 if ( ! e.isNull() && ! e.text().isNull() )
1246 mInfo = docElem.firstChildElement( QStringLiteral(
"synopsis" ) ).text().simplified();
1249 const QDomElement collectsElem = docElem.firstChildElement( QStringLiteral(
"seealsocollects" ) );
1250 e = collectsElem.firstChildElement( QStringLiteral(
"collect" ) );
1251 while ( ! e.isNull() )
1253 if ( ! e.attribute( QStringLiteral(
"dir" ) ).isNull() )
1256 const QString dir = e.attribute( QStringLiteral(
"dir" ) ) +
'/';
1262 e = e.nextSiblingElement();
1265 const QDomElement gradientsElem = docElem.firstChildElement( QStringLiteral(
"gradients" ) );
1266 e = gradientsElem.firstChildElement( QStringLiteral(
"gradient" ) );
1267 while ( ! e.isNull() )
1269 if ( ! e.attribute( QStringLiteral(
"dir" ) ).isNull() )
1273 const QString dir = e.attribute( QStringLiteral(
"dir" ) );
1276 mSelectionsList << dir +
'/' + e.attribute( QStringLiteral(
"file" ) );
1279 e = e.nextSiblingElement();
1290 return (
path() == other->
path() );
1295 const QString &name,
const QVector<QgsCptCityDataItem *> &items )
1307 return QVector<QgsCptCityDataItem *>();
1309 QVector<QgsCptCityDataItem *>
children;
1312 const auto constMItems =
mItems;
1327 : QAbstractItemModel( parent )
1328 , mArchive( archive )
1329 , mViewType( viewType )
1332 QgsDebugMsgLevel( QLatin1String(
"archiveName = " ) + archive->
archiveName() +
" viewType=" + QString::number(
static_cast< int >( viewType ) ), 2 );
1363 if ( !
index.isValid() )
1364 return Qt::ItemFlags();
1366 Qt::ItemFlags
flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
1373 if ( !
index.isValid() )
1382 else if ( role == Qt::DisplayRole )
1384 if (
index.column() == 0 )
1385 return item->
name();
1386 if (
index.column() == 1 )
1388 return item->
info();
1391 else if ( role == Qt::ToolTipRole )
1395 return QString( item->
path() +
'\n' + item->
info() );
1398 else if ( role == Qt::DecorationRole &&
index.column() == 1 &&
1404 else if ( role == Qt::FontRole &&
1405 qobject_cast< QgsCptCityCollectionItem * >( item ) )
1409 font.setPointSize( 11 );
1410 font.setBold(
true );
1424 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole )
1427 return QVariant( tr(
"Name" ) );
1428 else if ( section == 1 )
1429 return QVariant( tr(
"Info" ) );
1447 return item ? item->
rowCount() : 0;
1469 QModelIndex rootIndex;
1470 bool foundParent =
false, foundChild =
true;
1476 if ( path.isEmpty() )
1478 for (
int i = 0; i <
rowCount( rootIndex ); i++ )
1480 QModelIndex idx =
index( i, 0, rootIndex );
1483 return QModelIndex();
1485 itemPath = item->
path();
1487 if ( itemPath == path )
1495 while ( foundChild )
1501 if ( itemPath.isEmpty() )
1503 for ( ; i <
rowCount( rootIndex ); i++ )
1505 QModelIndex idx =
index( i, 0, rootIndex );
1508 return QModelIndex();
1510 itemPath = item->
path();
1512 if ( itemPath == path )
1518 if ( ! itemPath.endsWith(
'/' ) )
1521 foundParent =
false;
1532 for ( QString childPath : constSelectionsList )
1534 if ( childPath.endsWith(
'/' ) )
1535 childPath.chop( 1 );
1537 if ( path.startsWith( childPath ) )
1546 else if ( path.startsWith( itemPath ) )
1564 return QModelIndex();
1578 const QModelIndex idx =
findPath( path );
1579 if ( idx.isValid() )
1592 return item ? createIndex( row, column, item ) : QModelIndex();
1599 return QModelIndex();
1608 for (
int i = 0; i < items.size(); i++ )
1610 if ( items[i] == item )
1611 return createIndex( i, 0, item );
1613 QModelIndex childIndex =
findItem( item, items[i] );
1614 if ( childIndex.isValid() )
1618 return QModelIndex();
1636 if ( !idx.isValid() )
1639 beginInsertRows( idx, first, last );
1650 if ( !idx.isValid() )
1652 beginRemoveRows( idx, first, last );
1696 QStringList QgsCptCityBrowserModel::mimeTypes()
const
1701 types <<
"application/x-vnd.qgis.qgis.uri";
1705 QMimeData *QgsCptCityBrowserModel::mimeData(
const QModelIndexList &indexes )
const
1708 const auto constIndexes = indexes;
1709 for (
const QModelIndex &
index : constIndexes )
1711 if (
index.isValid() )
1714 if ( ptr->
type() != QgsCptCityDataItem::Layer )
continue;
1722 bool QgsCptCityBrowserModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
int row,
int column,
const QModelIndex &parent )
1740 void *v = idx.internalPointer();
1742 Q_ASSERT( !v || d );