26 #include <QDomDocument> 27 #include <QDomElement> 29 #include <QDomNodeList> 31 #include <QTextStream> 37 #define STYLE_CURRENT_VERSION "1" 39 QgsStyle *QgsStyle::sDefaultStyle =
nullptr;
53 if ( !QFile::exists( styleFilename ) )
65 sDefaultStyle->
load( styleFilename );
74 sDefaultStyle =
nullptr;
79 qDeleteAll( mSymbols );
80 qDeleteAll( mColorRamps );
84 mCachedColorRampTags.clear();
85 mCachedSymbolTags.clear();
90 if ( !symbol || name.isEmpty() )
94 if ( mSymbols.contains( name ) )
97 delete mSymbols.value( name );
98 mSymbols.insert( name, symbol );
104 mSymbols.insert( name, symbol );
106 saveSymbol( name, symbol,
false, QStringList() );
115 QDomDocument doc( QStringLiteral(
"dummy" ) );
117 if ( symEl.isNull() )
119 QgsDebugMsg( QStringLiteral(
"Couldn't convert symbol to valid XML!" ) );
124 QTextStream stream( &xmlArray );
125 stream.setCodec(
"UTF-8" );
126 symEl.save( stream, 4 );
127 auto query =
QgsSqlite3Mprintf(
"INSERT INTO symbol VALUES (NULL, '%q', '%q', %d);",
128 name.toUtf8().constData(), xmlArray.constData(), ( favorite ? 1 : 0 ) );
130 if ( !runEmptyQuery( query ) )
132 QgsDebugMsg( QStringLiteral(
"Couldn't insert symbol into the database!" ) );
156 QgsDebugMsg( QStringLiteral(
"Sorry! Cannot open database to tag." ) );
163 QgsDebugMsg(
"No such symbol for deleting in database: " + name +
". Cheers." );
169 mCachedSymbolTags.remove( name );
178 return symbol ? symbol->
clone() :
nullptr;
183 return mSymbols.value( name );
188 return mSymbols.count();
193 return mSymbols.keys();
199 if ( !colorRamp || name.isEmpty() )
203 if ( mColorRamps.contains( name ) )
206 delete mColorRamps.value( name );
207 mColorRamps.insert( name, colorRamp );
213 mColorRamps.insert( name, colorRamp );
224 QDomDocument doc( QStringLiteral(
"dummy" ) );
227 if ( rampEl.isNull() )
229 QgsDebugMsg( QStringLiteral(
"Couldn't convert color ramp to valid XML!" ) );
234 QTextStream stream( &xmlArray );
235 stream.setCodec(
"UTF-8" );
236 rampEl.save( stream, 4 );
237 auto query =
QgsSqlite3Mprintf(
"INSERT INTO colorramp VALUES (NULL, '%q', '%q', %d);",
238 name.toUtf8().constData(), xmlArray.constData(), ( favorite ? 1 : 0 ) );
239 if ( !runEmptyQuery( query ) )
241 QgsDebugMsg( QStringLiteral(
"Couldn't insert colorramp into the database!" ) );
254 std::unique_ptr< QgsColorRamp > ramp( mColorRamps.take( name ) );
258 auto query =
QgsSqlite3Mprintf(
"DELETE FROM colorramp WHERE name='%q'", name.toUtf8().constData() );
259 if ( !runEmptyQuery( query ) )
261 QgsDebugMsg( QStringLiteral(
"Couldn't remove color ramp from the database." ) );
265 mCachedColorRampTags.remove( name );
275 return ramp ? ramp->
clone() :
nullptr;
280 return mColorRamps.value( name );
285 return mColorRamps.count();
290 return mColorRamps.keys();
293 bool QgsStyle::openDatabase(
const QString &filename )
295 int rc = mCurrentDB.
open( filename );
298 mErrorString = QStringLiteral(
"Couldn't open the style database: %1" ).arg( mCurrentDB.
errorMessage() );
307 mErrorString.clear();
308 if ( !openDatabase( filename ) )
310 mErrorString = QStringLiteral(
"Unable to create database" );
322 mErrorString.clear();
323 if ( !openDatabase( QStringLiteral(
":memory:" ) ) )
325 mErrorString = QStringLiteral(
"Unable to create temporary memory database" );
338 "id INTEGER PRIMARY KEY,"\
341 "favorite INTEGER);"\
342 "CREATE TABLE colorramp("\
343 "id INTEGER PRIMARY KEY,"\
346 "favorite INTEGER);"\
348 "id INTEGER PRIMARY KEY,"\
350 "CREATE TABLE tagmap("\
351 "tag_id INTEGER NOT NULL,"\
352 "symbol_id INTEGER);"\
353 "CREATE TABLE ctagmap("\
354 "tag_id INTEGER NOT NULL,"\
355 "colorramp_id INTEGER);"\
356 "CREATE TABLE smartgroup("\
357 "id INTEGER PRIMARY KEY,"\
360 runEmptyQuery( query );
365 mErrorString.clear();
368 if ( !openDatabase( filename ) )
370 mErrorString = QStringLiteral(
"Unable to open database file specified" );
376 auto query =
QgsSqlite3Mprintf(
"UPDATE symbol SET favorite=0 WHERE favorite IS NULL;" 377 "UPDATE colorramp SET favorite=0 WHERE favorite IS NULL;" 379 runEmptyQuery( query );
386 statement = mCurrentDB.
prepare( query, rc );
388 while ( rc == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
393 if ( !doc.setContent( xmlstring ) )
395 QgsDebugMsg(
"Cannot open symbol " + symbol_name );
399 QDomElement symElement = doc.documentElement();
402 mSymbols.insert( symbol_name, symbol );
406 statement = mCurrentDB.
prepare( query, rc );
407 while ( rc == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
412 if ( !doc.setContent( xmlstring ) )
417 QDomElement rampElement = doc.documentElement();
420 mColorRamps.insert( ramp_name, ramp );
423 mFileName = filename;
431 mErrorString.clear();
433 if ( filename.isEmpty() )
434 filename = mFileName;
439 QDomDocument doc(
"qgis_style" );
440 QDomElement root = doc.createElement(
"qgis_style" );
442 doc.appendChild( root );
446 QDomElement rampsElem = doc.createElement(
"colorramps" );
449 for ( QMap<QString, QgsColorRamp *>::iterator itr = mColorRamps.begin(); itr != mColorRamps.end(); ++itr )
452 rampsElem.appendChild( rampEl );
455 root.appendChild( symbolsElem );
456 root.appendChild( rampsElem );
460 if ( !f.open( QFile::WriteOnly ) )
462 mErrorString =
"Couldn't open file for writing: " + filename;
465 QTextStream ts( &f );
466 ts.setCodec(
"UTF-8" );
471 mFileName = filename;
477 if ( mSymbols.contains( newName ) )
479 QgsDebugMsg( QStringLiteral(
"Symbol of new name already exists" ) );
487 mSymbols.insert( newName, symbol );
491 QgsDebugMsg( QStringLiteral(
"Sorry! Cannot open database to tag." ) );
498 QgsDebugMsg( QStringLiteral(
"No such symbol for tagging in database: " ) + oldName );
502 mCachedSymbolTags.remove( oldName );
513 if ( mColorRamps.contains( newName ) )
515 QgsDebugMsg( QStringLiteral(
"Color ramp of new name already exists." ) );
523 mColorRamps.insert( newName, ramp );
524 mCachedColorRampTags.remove( oldName );
528 auto query =
QgsSqlite3Mprintf(
"SELECT id FROM colorramp WHERE name='%q'", oldName.toUtf8().constData() );
530 statement = mCurrentDB.
prepare( query, nErr );
531 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
533 rampid = sqlite3_column_int( statement.get(), 0 );
546 QgsDebugMsg( QStringLiteral(
"Cannot Open database for getting favorite symbols" ) );
547 return QStringList();
561 QgsDebugMsg( QStringLiteral(
"No such style entity" ) );
562 return QStringList();
567 statement = mCurrentDB.
prepare( query, nErr );
570 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
582 QgsDebugMsg( QStringLiteral(
"Cannot open database to get symbols of tagid %1" ).arg( tagid ) );
583 return QStringList();
589 subquery =
QgsSqlite3Mprintf(
"SELECT symbol_id FROM tagmap WHERE tag_id=%d", tagid );
593 subquery =
QgsSqlite3Mprintf(
"SELECT colorramp_id FROM ctagmap WHERE tag_id=%d", tagid );
598 return QStringList();
603 statement = mCurrentDB.
prepare( subquery, nErr );
607 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
609 int id = sqlite3_column_int( statement.get(), 0 );
617 statement2 = mCurrentDB.
prepare( query, rc );
618 while ( rc == SQLITE_OK && sqlite3_step( statement2.get() ) == SQLITE_ROW )
633 auto query =
QgsSqlite3Mprintf(
"INSERT INTO tag VALUES (NULL, '%q')", tagname.toUtf8().constData() );
635 statement = mCurrentDB.
prepare( query, nErr );
636 if ( nErr == SQLITE_OK )
637 ( void )sqlite3_step( statement.get() );
640 settings.
setValue( QStringLiteral(
"qgis/symbolsListGroupsIndex" ), 0 );
644 return static_cast< int >( sqlite3_last_insert_rowid( mCurrentDB.get() ) );
650 return QStringList();
656 statement = mCurrentDB.
prepare( query, nError );
659 while ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
673 query =
QgsSqlite3Mprintf(
"UPDATE symbol SET name='%q' WHERE id=%d", newName.toUtf8().constData(), id );
676 query =
QgsSqlite3Mprintf(
"UPDATE colorramp SET name='%q' WHERE id=%d", newName.toUtf8().constData(), id );
679 query =
QgsSqlite3Mprintf(
"UPDATE tag SET name='%q' WHERE id=%d", newName.toUtf8().constData(), id );
682 query =
QgsSqlite3Mprintf(
"UPDATE smartgroup SET name='%q' WHERE id=%d", newName.toUtf8().constData(), id );
685 const bool result = runEmptyQuery( query );
688 mErrorString = QStringLiteral(
"Could not rename!" );
692 mCachedColorRampTags.clear();
693 mCachedSymbolTags.clear();
719 bool groupRemoved =
false;
724 query =
QgsSqlite3Mprintf(
"DELETE FROM symbol WHERE id=%d; DELETE FROM tagmap WHERE symbol_id=%d",
id,
id );
730 query =
QgsSqlite3Mprintf(
"DELETE FROM tag WHERE id=%d; DELETE FROM tagmap WHERE tag_id=%d",
id,
id );
740 if ( !runEmptyQuery( query ) )
742 QgsDebugMsg( QStringLiteral(
"Could not delete entity!" ) );
746 mCachedColorRampTags.clear();
747 mCachedSymbolTags.clear();
752 settings.
setValue( QStringLiteral(
"qgis/symbolsListGroupsIndex" ), 0 );
761 bool QgsStyle::runEmptyQuery(
const QString &query )
766 char *zErr =
nullptr;
767 int nErr = sqlite3_exec( mCurrentDB.get(), query.toUtf8().constData(),
nullptr,
nullptr, &zErr );
769 if ( nErr != SQLITE_OK )
772 sqlite3_free( zErr );
775 return nErr == SQLITE_OK;
785 query =
QgsSqlite3Mprintf(
"UPDATE symbol SET favorite=1 WHERE name='%q'", name.toUtf8().constData() );
788 query =
QgsSqlite3Mprintf(
"UPDATE colorramp SET favorite=1 WHERE name='%q'", name.toUtf8().constData() );
792 QgsDebugMsg( QStringLiteral(
"Wrong entity value. cannot apply group" ) );
796 const bool res = runEmptyQuery( query );
810 query =
QgsSqlite3Mprintf(
"UPDATE symbol SET favorite=0 WHERE name='%q'", name.toUtf8().constData() );
813 query =
QgsSqlite3Mprintf(
"UPDATE colorramp SET favorite=0 WHERE name='%q'", name.toUtf8().constData() );
817 QgsDebugMsg( QStringLiteral(
"Wrong entity value. cannot apply group" ) );
821 const bool res = runEmptyQuery( query );
832 QgsDebugMsg( QStringLiteral(
"Sorry! Cannot open database to search" ) );
833 return QStringList();
837 QString item = ( type ==
SymbolEntity ) ? QStringLiteral(
"symbol" ) : QStringLiteral(
"colorramp" );
839 item.toUtf8().constData(), qword.toUtf8().constData() );
842 int nErr; statement = mCurrentDB.
prepare( query, nErr );
844 QSet< QString > symbols;
845 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
851 query =
QgsSqlite3Mprintf(
"SELECT id FROM tag WHERE name LIKE '%%%q%%'", qword.toUtf8().constData() );
852 statement = mCurrentDB.
prepare( query, nErr );
855 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
860 QString dummy = tagids.join( QStringLiteral(
", " ) );
865 dummy.toUtf8().constData() );
869 query =
QgsSqlite3Mprintf(
"SELECT colorramp_id FROM ctagmap WHERE tag_id IN (%q)",
870 dummy.toUtf8().constData() );
872 statement = mCurrentDB.
prepare( query, nErr );
874 QStringList symbolids;
875 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
880 dummy = symbolids.join( QStringLiteral(
", " ) );
882 item.toUtf8().constData(), dummy.toUtf8().constData() );
883 statement = mCurrentDB.
prepare( query, nErr );
884 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
889 return symbols.toList();
896 QgsDebugMsg( QStringLiteral(
"Sorry! Cannot open database to tag." ) );
903 QgsDebugMsg( QStringLiteral(
"No such symbol for tagging in database: " ) + symbol );
908 const auto constTags =
tags;
909 for (
const QString &t : constTags )
912 if ( !tag.isEmpty() )
915 int tagid(
tagId( tag ) );
928 char *zErr =
nullptr;
930 nErr = sqlite3_exec( mCurrentDB.get(), query.toUtf8().constData(),
nullptr,
nullptr, &zErr );
934 sqlite3_free( zErr );
940 clearCachedTags( type, symbol );
950 QgsDebugMsg( QStringLiteral(
"Sorry! Cannot open database for detgging." ) );
955 ?
QgsSqlite3Mprintf(
"SELECT id FROM symbol WHERE name='%q'", symbol.toUtf8().constData() )
956 :
QgsSqlite3Mprintf(
"SELECT id FROM colorramp WHERE name='%q'", symbol.toUtf8().constData() );
958 int nErr; statement = mCurrentDB.
prepare( query, nErr );
961 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
963 symbolid = sqlite3_column_int( statement.get(), 0 );
970 const auto constTags =
tags;
971 for (
const QString &
tag : constTags )
976 statement2 = mCurrentDB.
prepare( query, nErr );
979 if ( nErr == SQLITE_OK && sqlite3_step( statement2.get() ) == SQLITE_ROW )
981 tagid = sqlite3_column_int( statement2.get(), 0 );
988 ?
QgsSqlite3Mprintf(
"DELETE FROM tagmap WHERE tag_id=%d AND symbol_id=%d", tagid, symbolid )
989 :
QgsSqlite3Mprintf(
"DELETE FROM ctagmap WHERE tag_id=%d AND colorramp_id=%d", tagid, symbolid );
990 runEmptyQuery( query );
994 clearCachedTags( type, symbol );
1007 QgsDebugMsg( QStringLiteral(
"Sorry! Cannot open database for detagging." ) );
1012 ?
QgsSqlite3Mprintf(
"SELECT id FROM symbol WHERE name='%q'", symbol.toUtf8().constData() )
1013 :
QgsSqlite3Mprintf(
"SELECT id FROM colorramp WHERE name='%q'", symbol.toUtf8().constData() );
1016 statement = mCurrentDB.
prepare( query, nErr );
1019 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1021 symbolid = sqlite3_column_int( statement.get(), 0 );
1032 runEmptyQuery( query );
1034 clearCachedTags( type, symbol );
1048 if ( mCachedSymbolTags.contains( symbol ) )
1049 return mCachedSymbolTags.value( symbol );
1053 if ( mCachedColorRampTags.contains( symbol ) )
1054 return mCachedColorRampTags.value( symbol );
1064 QgsDebugMsg( QStringLiteral(
"Sorry! Cannot open database for getting the tags." ) );
1065 return QStringList();
1070 return QStringList();
1075 :
QgsSqlite3Mprintf(
"SELECT tag_id FROM ctagmap WHERE colorramp_id=%d", symbolid );
1078 int nErr; statement = mCurrentDB.
prepare( query, nErr );
1080 QStringList tagList;
1081 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1083 auto subquery =
QgsSqlite3Mprintf(
"SELECT name FROM tag WHERE id=%d", sqlite3_column_int( statement.get(), 0 ) );
1087 statement2 = mCurrentDB.
prepare( subquery, pErr );
1088 if ( pErr == SQLITE_OK && sqlite3_step( statement2.get() ) == SQLITE_ROW )
1098 mCachedSymbolTags[
symbol ] = tagList;
1102 mCachedColorRampTags[
symbol ] = tagList;
1117 QgsDebugMsg( QStringLiteral(
"Sorry! Cannot open database for getting the tags." ) );
1126 int tagid =
tagId( tag );
1134 ?
QgsSqlite3Mprintf(
"SELECT tag_id FROM tagmap WHERE tag_id=%d AND symbol_id=%d", tagid, symbolid )
1135 :
QgsSqlite3Mprintf(
"SELECT tag_id FROM ctagmap WHERE tag_id=%d AND colorramp_id=%d", tagid, symbolid );
1138 int nErr; statement = mCurrentDB.
prepare( query, nErr );
1140 return ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW );
1152 statement = mCurrentDB.
prepare( query, nError );
1155 if ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1163 int QgsStyle::getId(
const QString &table,
const QString &name )
1165 QString lowerName( name.toLower() );
1166 auto query =
QgsSqlite3Mprintf(
"SELECT id FROM %q WHERE LOWER(name)='%q'", table.toUtf8().constData(), lowerName.toUtf8().constData() );
1169 int nErr; statement = mCurrentDB.
prepare( query, nErr );
1172 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1174 id = sqlite3_column_int( statement.get(), 0 );
1179 auto query =
QgsSqlite3Mprintf(
"SELECT id FROM %q WHERE name='%q'", table.toUtf8().constData(), name.toUtf8().constData() );
1182 int nErr; statement = mCurrentDB.
prepare( query, nErr );
1183 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1185 id = sqlite3_column_int( statement.get(), 0 );
1192 QString QgsStyle::getName(
const QString &table,
int id )
const 1194 auto query =
QgsSqlite3Mprintf(
"SELECT name FROM %q WHERE id='%q'", table.toUtf8().constData(), QString::number(
id ).toUtf8().constData() );
1197 int nErr; statement = mCurrentDB.
prepare( query, nErr );
1200 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1210 return getId( QStringLiteral(
"symbol" ), name );
1215 return getId( QStringLiteral(
"colorramp" ), name );
1220 return getId( QStringLiteral(
"tag" ), name );
1225 return getId( QStringLiteral(
"smartgroup" ), name );
1230 return addSmartgroup( name, op, conditions.values( QStringLiteral(
"tag" ) ),
1231 conditions.values( QStringLiteral(
"!tag" ) ),
1232 conditions.values( QStringLiteral(
"name" ) ),
1233 conditions.values( QStringLiteral(
"!name" ) ) );
1236 int QgsStyle::addSmartgroup(
const QString &name,
const QString &op,
const QStringList &matchTag,
const QStringList &noMatchTag,
const QStringList &matchName,
const QStringList &noMatchName )
1238 QDomDocument doc( QStringLiteral(
"dummy" ) );
1239 QDomElement smartEl = doc.createElement( QStringLiteral(
"smartgroup" ) );
1240 smartEl.setAttribute( QStringLiteral(
"name" ), name );
1241 smartEl.setAttribute( QStringLiteral(
"operator" ), op );
1243 auto addCondition = [&doc, &smartEl](
const QString & constraint,
const QStringList & parameters )
1245 for (
const QString ¶m : parameters )
1247 QDomElement condEl = doc.createElement( QStringLiteral(
"condition" ) );
1248 condEl.setAttribute( QStringLiteral(
"constraint" ), constraint );
1249 condEl.setAttribute( QStringLiteral(
"param" ), param );
1250 smartEl.appendChild( condEl );
1253 addCondition( QStringLiteral(
"tag" ), matchTag );
1254 addCondition( QStringLiteral(
"!tag" ), noMatchTag );
1255 addCondition( QStringLiteral(
"name" ), matchName );
1256 addCondition( QStringLiteral(
"!name" ), noMatchName );
1258 QByteArray xmlArray;
1259 QTextStream stream( &xmlArray );
1260 stream.setCodec(
"UTF-8" );
1261 smartEl.save( stream, 4 );
1262 auto query =
QgsSqlite3Mprintf(
"INSERT INTO smartgroup VALUES (NULL, '%q', '%q')",
1263 name.toUtf8().constData(), xmlArray.constData() );
1265 if ( runEmptyQuery( query ) )
1268 settings.
setValue( QStringLiteral(
"qgis/symbolsListGroupsIndex" ), 0 );
1271 return static_cast< int >( sqlite3_last_insert_rowid( mCurrentDB.get() ) );
1275 QgsDebugMsg( QStringLiteral(
"Couldn't insert symbol into the database!" ) );
1284 QgsDebugMsg( QStringLiteral(
"Cannot open database for listing groups" ) );
1293 statement = mCurrentDB.
prepare( query, nError );
1296 while ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1299 groupNames.insert( sqlite3_column_int( statement.get(),
SmartgroupId ), group );
1309 QgsDebugMsg( QStringLiteral(
"Cannot open database for listing groups" ) );
1310 return QStringList();
1318 statement = mCurrentDB.
prepare( query, nError );
1321 while ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1331 QStringList symbols;
1336 int nErr; statement = mCurrentDB.
prepare( query, nErr );
1337 if ( !( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW ) )
1339 return QStringList();
1345 if ( !doc.setContent( xmlstr ) )
1347 QgsDebugMsg( QStringLiteral(
"Cannot open smartgroup id: %1" ).arg(
id ) );
1349 QDomElement smartEl = doc.documentElement();
1350 QString op = smartEl.attribute( QStringLiteral(
"operator" ) );
1351 QDomNodeList conditionNodes = smartEl.childNodes();
1353 bool firstSet =
true;
1354 for (
int i = 0; i < conditionNodes.count(); i++ )
1356 QDomElement condEl = conditionNodes.at( i ).toElement();
1357 QString constraint = condEl.attribute( QStringLiteral(
"constraint" ) );
1358 QString param = condEl.attribute( QStringLiteral(
"param" ) );
1360 QStringList resultNames;
1362 if ( constraint == QLatin1String(
"tag" ) )
1366 else if ( constraint == QLatin1String(
"name" ) )
1370 resultNames =
symbolNames().filter( param, Qt::CaseInsensitive );
1374 resultNames =
colorRampNames().filter( param, Qt::CaseInsensitive );
1377 else if ( constraint == QLatin1String(
"!tag" ) )
1381 for (
const QString &name : unwanted )
1383 resultNames.removeAll( name );
1386 else if ( constraint == QLatin1String(
"!name" ) )
1389 for (
const QString &str : all )
1391 if ( !str.contains( param, Qt::CaseInsensitive ) )
1399 symbols = resultNames;
1404 if ( op == QLatin1String(
"OR" ) )
1406 symbols << resultNames;
1408 else if ( op == QLatin1String(
"AND" ) )
1410 QStringList dummy = symbols;
1412 for (
const QString &result : qgis::as_const( resultNames ) )
1414 if ( dummy.contains( result ) )
1423 QStringList unique = symbols.toSet().toList();
1424 std::sort( unique.begin(), unique.end() );
1432 QgsDebugMsg( QStringLiteral(
"Cannot open database for listing groups" ) );
1442 statement = mCurrentDB.
prepare( query, nError );
1443 if ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1447 if ( !doc.setContent( xmlstr ) )
1449 QgsDebugMsg( QStringLiteral(
"Cannot open smartgroup id: %1" ).arg(
id ) );
1452 QDomElement smartEl = doc.documentElement();
1453 QDomNodeList conditionNodes = smartEl.childNodes();
1455 for (
int i = 0; i < conditionNodes.count(); i++ )
1457 QDomElement condEl = conditionNodes.at( i ).toElement();
1458 QString constraint = condEl.attribute( QStringLiteral(
"constraint" ) );
1459 QString param = condEl.attribute( QStringLiteral(
"param" ) );
1461 condition.insert( constraint, param );
1472 QgsDebugMsg( QStringLiteral(
"Cannot open database for listing groups" ) );
1482 statement = mCurrentDB.
prepare( query, nError );
1483 if ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1487 if ( !doc.setContent( xmlstr ) )
1489 QgsDebugMsg( QStringLiteral(
"Cannot open smartgroup id: %1" ).arg(
id ) );
1491 QDomElement smartEl = doc.documentElement();
1492 op = smartEl.attribute( QStringLiteral(
"operator" ) );
1500 if ( filename.isEmpty() )
1502 QgsDebugMsg( QStringLiteral(
"Invalid filename for style export." ) );
1506 QDomDocument doc( QStringLiteral(
"qgis_style" ) );
1507 QDomElement root = doc.createElement( QStringLiteral(
"qgis_style" ) );
1509 doc.appendChild( root );
1516 QDomNodeList symbolsList = symbolsElem.elementsByTagName( QStringLiteral(
"symbol" ) );
1517 int nbSymbols = symbolsList.count();
1518 for (
int i = 0; i < nbSymbols; ++i )
1520 QDomElement
symbol = symbolsList.at( i ).toElement();
1521 QString name = symbol.attribute( QStringLiteral(
"name" ) );
1523 if ( tags.count() > 0 )
1525 symbol.setAttribute( QStringLiteral(
"tags" ), tags.join(
',' ) );
1527 if ( favoriteSymbols.contains( name ) )
1529 symbol.setAttribute( QStringLiteral(
"favorite" ), QStringLiteral(
"1" ) );
1534 QDomElement rampsElem = doc.createElement( QStringLiteral(
"colorramps" ) );
1535 for ( QMap<QString, QgsColorRamp *>::const_iterator itr = mColorRamps.constBegin(); itr != mColorRamps.constEnd(); ++itr )
1539 if ( tags.count() > 0 )
1541 rampEl.setAttribute( QStringLiteral(
"tags" ), tags.join(
',' ) );
1543 if ( favoriteColorramps.contains( itr.key() ) )
1545 rampEl.setAttribute( QStringLiteral(
"favorite" ), QStringLiteral(
"1" ) );
1547 rampsElem.appendChild( rampEl );
1550 root.appendChild( symbolsElem );
1551 root.appendChild( rampsElem );
1554 QFile f( filename );
1555 if ( !f.open( QFile::WriteOnly | QIODevice::Truncate ) )
1557 mErrorString =
"Couldn't open file for writing: " + filename;
1561 QTextStream ts( &f );
1562 ts.setCodec(
"UTF-8" );
1566 mFileName = filename;
1572 mErrorString = QString();
1573 QDomDocument doc( QStringLiteral(
"style" ) );
1574 QFile f( filename );
1575 if ( !f.open( QFile::ReadOnly ) )
1577 mErrorString = QStringLiteral(
"Unable to open the specified file" );
1578 QgsDebugMsg( QStringLiteral(
"Error opening the style XML file." ) );
1582 if ( !doc.setContent( &f ) )
1584 mErrorString = QStringLiteral(
"Unable to understand the style file: %1" ).arg( filename );
1585 QgsDebugMsg( QStringLiteral(
"XML Parsing error" ) );
1591 QDomElement docEl = doc.documentElement();
1592 if ( docEl.tagName() != QLatin1String(
"qgis_style" ) )
1594 mErrorString =
"Incorrect root tag in style: " + docEl.tagName();
1598 QString version = docEl.attribute( QStringLiteral(
"version" ) );
1601 mErrorString =
"Unknown style file version: " + version;
1607 QDomElement symbolsElement = docEl.firstChildElement( QStringLiteral(
"symbols" ) );
1608 QDomElement e = symbolsElement.firstChildElement();
1612 runEmptyQuery( query );
1617 while ( !e.isNull() )
1619 if ( e.tagName() == QLatin1String(
"symbol" ) )
1621 QString name = e.attribute( QStringLiteral(
"name" ) );
1623 if ( e.hasAttribute( QStringLiteral(
"tags" ) ) )
1625 tags = e.attribute( QStringLiteral(
"tags" ) ).split(
',' );
1627 bool favorite =
false;
1628 if ( e.hasAttribute( QStringLiteral(
"favorite" ) ) && e.attribute( QStringLiteral(
"favorite" ) ) == QStringLiteral(
"1" ) )
1647 e = e.nextSiblingElement();
1656 for ( QMap<QString, QgsSymbol *>::iterator it = symbols.begin(); it != symbols.end(); ++it )
1663 QDomElement rampsElement = docEl.firstChildElement( QStringLiteral(
"colorramps" ) );
1664 e = rampsElement.firstChildElement();
1665 while ( !e.isNull() )
1667 if ( e.tagName() == QLatin1String(
"colorramp" ) )
1669 QString name = e.attribute( QStringLiteral(
"name" ) );
1671 if ( e.hasAttribute( QStringLiteral(
"tags" ) ) )
1673 tags = e.attribute( QStringLiteral(
"tags" ) ).split(
',' );
1675 bool favorite =
false;
1676 if ( e.hasAttribute( QStringLiteral(
"favorite" ) ) && e.attribute( QStringLiteral(
"favorite" ) ) == QStringLiteral(
"1" ) )
1695 e = e.nextSiblingElement();
1699 runEmptyQuery( query );
1701 mFileName = filename;
1707 QFileInfo fileInfo( path );
1709 if ( fileInfo.suffix().compare( QLatin1String(
"xml" ), Qt::CaseInsensitive ) != 0 )
1713 if ( !QFile::exists( path ) )
1716 QFile inputFile( path );
1717 if ( !inputFile.open( QIODevice::ReadOnly ) )
1720 QTextStream stream( &inputFile );
1721 const QString line = stream.readLine();
1722 return line == QLatin1String(
"<!DOCTYPE qgis_style>" );
1725 bool QgsStyle::updateSymbol(
StyleEntity type,
const QString &name )
1727 QDomDocument doc( QStringLiteral(
"dummy" ) );
1729 QByteArray xmlArray;
1730 QTextStream stream( &xmlArray );
1731 stream.setCodec(
"UTF-8" );
1740 QgsDebugMsg( QStringLiteral(
"Update request received for unavailable symbol" ) );
1745 if ( symEl.isNull() )
1747 QgsDebugMsg( QStringLiteral(
"Couldn't convert symbol to valid XML!" ) );
1750 symEl.save( stream, 4 );
1752 xmlArray.constData(), name.toUtf8().constData() );
1758 QgsDebugMsg( QStringLiteral(
"Update requested for unavailable color ramp." ) );
1762 std::unique_ptr< QgsColorRamp > ramp(
colorRamp( name ) );
1764 if ( symEl.isNull() )
1766 QgsDebugMsg( QStringLiteral(
"Couldn't convert color ramp to valid XML!" ) );
1769 symEl.save( stream, 4 );
1771 xmlArray.constData(), name.toUtf8().constData() );
1775 QgsDebugMsg( QStringLiteral(
"Updating the unsupported StyleEntity" ) );
1780 if ( !runEmptyQuery( query ) )
1782 QgsDebugMsg( QStringLiteral(
"Couldn't insert symbol into the database!" ) );
1810 mCachedSymbolTags.remove( name );
1814 mCachedColorRampTags.remove( name );
bool exportXml(const QString &filename)
Exports the style as a XML file.
The class is used as a container of context for various read/write operations on other objects...
void clear()
Removes all contents of the style.
const QgsColorRamp * colorRampRef(const QString &name) const
Returns a const pointer to a symbol (doesn't create new instance)
bool addColorRamp(const QString &name, QgsColorRamp *colorRamp, bool update=false)
Adds a color ramp to the style.
void symbolSaved(const QString &name, QgsSymbol *symbol)
Emitted every time a new symbol has been added to the database.
static QString userStylePath()
Returns the path to user's style.
bool symbolHasTag(StyleEntity type, const QString &symbol, const QString &tag)
Returns whether a given tag is associated with the symbol.
bool save(QString filename=QString())
Saves style into a file (will use current filename if empty string is passed)
Abstract base class for all rendered symbols.
This class is a composition of two QSettings instances:
QStringList symbolsWithTag(StyleEntity type, int tagid) const
Returns the symbol names with which have the given tag.
void entityTagsChanged(QgsStyle::StyleEntity entity, const QString &name, const QStringList &newTags)
Emitted whenever an entity's tags are changed.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
void createTables()
Creates tables structure for new database.
bool load(const QString &filename)
Loads a file into the style.
virtual QgsColorRamp * clone() const =0
Creates a clone of the color ramp.
QStringList tagsOfSymbol(StyleEntity type, const QString &symbol)
Returns the tags associated with the symbol.
Abstract base class for color ramps.
QMap< int, QString > QgsSymbolGroupMap
bool saveColorRamp(const QString &name, QgsColorRamp *ramp, bool favorite, const QStringList &tags)
Adds the colorramp to the DB.
static QDomElement saveColorRamp(const QString &name, QgsColorRamp *ramp, QDomDocument &doc)
Encodes a color ramp's settings to an XML element.
bool tagSymbol(StyleEntity type, const QString &symbol, const QStringList &tags)
Tags the symbol with the tags in the list.
static QgsSymbol * loadSymbol(const QDomElement &element, const QgsReadWriteContext &context)
Attempts to load a symbol from a DOM element.
void rampChanged(const QString &name)
Emitted whenever a color ramp's definition is changed.
QgsColorRamp * colorRamp(const QString &name) const
Returns a new copy of the specified color ramp.
bool removeColorRamp(const QString &name)
Removes color ramp from style (and delete it)
bool rename(StyleEntity type, int id, const QString &newName)
Renames the given entity with the specified id.
void rampAdded(const QString &name)
Emitted whenever a color ramp has been added to the style and the database has been updated as a resu...
#define STYLE_CURRENT_VERSION
static QgsStyle * defaultStyle()
Returns default application-wide style.
StyleEntity
Enum for Entities involved in a style.
static QString defaultStylePath()
Returns the path to default style (works as a starting point).
QString errorMessage() const
Returns the most recent error message encountered by the database.
int symbolCount()
Returns count of symbols in style.
bool renameSymbol(const QString &oldName, const QString &newName)
Renames a symbol from oldName to newName.
void rampRemoved(const QString &name)
Emitted whenever a color ramp has been removed from the style and the database has been updated as a ...
QString tag(int id) const
Returns the tag name for the given id.
bool importXml(const QString &filename)
Imports the symbols and colorramps into the default style database from the given XML file...
const QgsSymbol * symbolRef(const QString &name) const
Returns a const pointer to a symbol (doesn't create new instance)
QStringList symbolNames()
Returns a list of names of symbols.
static QDomElement saveSymbols(QgsSymbolMap &symbols, const QString &tagName, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a collection of symbols to XML with specified tagName for the top-level element.
bool removeFavorite(StyleEntity type, const QString &name)
Removes the specified symbol from favorites.
bool remove(StyleEntity type, int id)
Removes the specified entity from the db.
QStringList findSymbols(StyleEntity type, const QString &qword)
Returns the names of the symbols which have a matching 'substring' in its definition.
void groupsModified()
Emitted every time a tag or smartgroup has been added, removed, or renamed.
QgsStyle()=default
Constructor for QgsStyle.
int addTag(const QString &tagName)
Adds a new tag and returns the tag's id.
static void cleanDefaultStyle()
Deletes the default style. Only to be used by QgsApplication::exitQgis()
QString columnAsText(int column) const
Returns the column value from the current statement row as a string.
static QgsColorRamp * loadColorRamp(QDomElement &element)
Creates a color ramp from the settings encoded in an XML element.
bool createMemoryDatabase()
Creates a temporary memory database.
QString smartgroupOperator(int id)
Returns the operator for the smartgroup clumsy implementation TODO create a class for smartgroups...
static bool isXmlStyleFile(const QString &path)
Tests if the file at path is a QGIS style XML file.
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
void symbolRenamed(const QString &oldName, const QString &newName)
Emitted whenever a symbol has been renamed from oldName to newName.
static QgsSymbolMap loadSymbols(QDomElement &element, const QgsReadWriteContext &context)
Reads a collection of symbols from XML and returns them in a map. Caller is responsible for deleting ...
int open(const QString &path)
Opens the database at the specified file path.
void favoritedChanged(QgsStyle::StyleEntity entity, const QString &name, bool isFavorite)
Emitted whenever an entity is either favorited or un-favorited.
QgsSmartConditionMap smartgroup(int id)
Returns the QgsSmartConditionMap for the given id.
QStringList colorRampNames()
Returns a list of names of color ramps.
void symbolRemoved(const QString &name)
Emitted whenever a symbol has been removed from the style and the database has been updated as a resu...
QStringList symbolsOfSmartgroup(StyleEntity type, int id)
Returns the symbols for the smartgroup.
int smartgroupId(const QString &smartgroup)
Returns the DB id for the given smartgroup name.
QStringList smartgroupNames()
Returns the smart groups list.
int tagId(const QString &tag)
Returns the DB id for the given tag name.
bool addSymbol(const QString &name, QgsSymbol *symbol, bool update=false)
Adds a symbol to style and takes symbol's ownership.
QStringList tags() const
Returns a list of all tags in the style database.
void rampRenamed(const QString &oldName, const QString &newName)
Emitted whenever a color ramp has been renamed from oldName to newName.
int addSmartgroup(const QString &name, const QString &op, const QgsSmartConditionMap &conditions)
Adds a new smartgroup to the database and returns the id.
void symbolChanged(const QString &name)
Emitted whenever a symbol's definition is changed.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
bool createDatabase(const QString &filename)
Creates an on-disk database.
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
int colorrampId(const QString &name)
Returns the id in the style database for the given colorramp name returns 0 if not found...
int symbolId(const QString &name)
Returns the id in the style database for the given symbol name returns 0 if not found.
QMap< QString, QgsSymbol *> QgsSymbolMap
static QDomElement saveSymbol(const QString &symbolName, QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
bool renameColorRamp(const QString &oldName, const QString &newName)
Changes ramp's name.
int colorRampCount()
Returns count of color ramps.
QString QgsSqlite3Mprintf(const char *format,...)
Wraps sqlite3_mprintf() by automatically freeing the memory.
QgsSymbol * symbol(const QString &name)
Returns a NEW copy of symbol.
bool saveSymbol(const QString &name, QgsSymbol *symbol, bool favorite, const QStringList &tags)
Adds the symbol to the DB with the tags.
bool removeSymbol(const QString &name)
Removes symbol from style (and delete it)
bool addFavorite(StyleEntity type, const QString &name)
Adds the specified symbol to favorites.
bool detagSymbol(StyleEntity type, const QString &symbol, const QStringList &tags)
Detags the symbol with the given list.
QStringList symbolsOfFavorite(StyleEntity type) const
Returns the symbol names which are flagged as favorite.
QMultiMap< QString, QString > QgsSmartConditionMap
A multimap to hold the smart group conditions as constraint and parameter pairs.
QgsSymbolGroupMap smartgroupsListMap()
Returns the smart groups map with id as key and name as value.