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 Q_FOREACH (
const QString &t, tags )
911 if ( !tag.isEmpty() )
914 int tagid(
tagId( tag ) );
927 char *zErr =
nullptr;
929 nErr = sqlite3_exec( mCurrentDB.get(), query.toUtf8().constData(),
nullptr,
nullptr, &zErr );
933 sqlite3_free( zErr );
939 clearCachedTags( type, symbol );
949 QgsDebugMsg( QStringLiteral(
"Sorry! Cannot open database for detgging." ) );
954 ?
QgsSqlite3Mprintf(
"SELECT id FROM symbol WHERE name='%q'", symbol.toUtf8().constData() )
955 :
QgsSqlite3Mprintf(
"SELECT id FROM colorramp WHERE name='%q'", symbol.toUtf8().constData() );
957 int nErr; statement = mCurrentDB.
prepare( query, nErr );
960 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
962 symbolid = sqlite3_column_int( statement.get(), 0 );
969 Q_FOREACH (
const QString &
tag, tags )
971 query =
QgsSqlite3Mprintf(
"SELECT id FROM tag WHERE name='%q'", tag.toUtf8().constData() );
974 statement2 = mCurrentDB.
prepare( query, nErr );
977 if ( nErr == SQLITE_OK && sqlite3_step( statement2.get() ) == SQLITE_ROW )
979 tagid = sqlite3_column_int( statement2.get(), 0 );
986 ?
QgsSqlite3Mprintf(
"DELETE FROM tagmap WHERE tag_id=%d AND symbol_id=%d", tagid, symbolid )
987 :
QgsSqlite3Mprintf(
"DELETE FROM ctagmap WHERE tag_id=%d AND colorramp_id=%d", tagid, symbolid );
988 runEmptyQuery( query );
992 clearCachedTags( type, symbol );
1005 QgsDebugMsg( QStringLiteral(
"Sorry! Cannot open database for detagging." ) );
1010 ?
QgsSqlite3Mprintf(
"SELECT id FROM symbol WHERE name='%q'", symbol.toUtf8().constData() )
1011 :
QgsSqlite3Mprintf(
"SELECT id FROM colorramp WHERE name='%q'", symbol.toUtf8().constData() );
1014 statement = mCurrentDB.
prepare( query, nErr );
1017 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1019 symbolid = sqlite3_column_int( statement.get(), 0 );
1030 runEmptyQuery( query );
1032 clearCachedTags( type, symbol );
1046 if ( mCachedSymbolTags.contains( symbol ) )
1047 return mCachedSymbolTags.value( symbol );
1051 if ( mCachedColorRampTags.contains( symbol ) )
1052 return mCachedColorRampTags.value( symbol );
1062 QgsDebugMsg( QStringLiteral(
"Sorry! Cannot open database for getting the tags." ) );
1063 return QStringList();
1068 return QStringList();
1073 :
QgsSqlite3Mprintf(
"SELECT tag_id FROM ctagmap WHERE colorramp_id=%d", symbolid );
1076 int nErr; statement = mCurrentDB.
prepare( query, nErr );
1078 QStringList tagList;
1079 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1081 auto subquery =
QgsSqlite3Mprintf(
"SELECT name FROM tag WHERE id=%d", sqlite3_column_int( statement.get(), 0 ) );
1085 statement2 = mCurrentDB.
prepare( subquery, pErr );
1086 if ( pErr == SQLITE_OK && sqlite3_step( statement2.get() ) == SQLITE_ROW )
1096 mCachedSymbolTags[
symbol ] = tagList;
1100 mCachedColorRampTags[
symbol ] = tagList;
1115 QgsDebugMsg( QStringLiteral(
"Sorry! Cannot open database for getting the tags." ) );
1124 int tagid =
tagId( tag );
1132 ?
QgsSqlite3Mprintf(
"SELECT tag_id FROM tagmap WHERE tag_id=%d AND symbol_id=%d", tagid, symbolid )
1133 :
QgsSqlite3Mprintf(
"SELECT tag_id FROM ctagmap WHERE tag_id=%d AND colorramp_id=%d", tagid, symbolid );
1136 int nErr; statement = mCurrentDB.
prepare( query, nErr );
1138 return ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW );
1150 statement = mCurrentDB.
prepare( query, nError );
1153 if ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1161 int QgsStyle::getId(
const QString &table,
const QString &name )
1163 QString lowerName( name.toLower() );
1164 auto query =
QgsSqlite3Mprintf(
"SELECT id FROM %q WHERE LOWER(name)='%q'", table.toUtf8().constData(), lowerName.toUtf8().constData() );
1167 int nErr; statement = mCurrentDB.
prepare( query, nErr );
1170 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1172 id = sqlite3_column_int( statement.get(), 0 );
1177 auto query =
QgsSqlite3Mprintf(
"SELECT id FROM %q WHERE name='%q'", table.toUtf8().constData(), name.toUtf8().constData() );
1180 int nErr; statement = mCurrentDB.
prepare( query, nErr );
1181 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1183 id = sqlite3_column_int( statement.get(), 0 );
1190 QString QgsStyle::getName(
const QString &table,
int id )
const 1192 auto query =
QgsSqlite3Mprintf(
"SELECT name FROM %q WHERE id='%q'", table.toUtf8().constData(), QString::number(
id ).toUtf8().constData() );
1195 int nErr; statement = mCurrentDB.
prepare( query, nErr );
1198 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1208 return getId( QStringLiteral(
"symbol" ), name );
1213 return getId( QStringLiteral(
"colorramp" ), name );
1218 return getId( QStringLiteral(
"tag" ), name );
1223 return getId( QStringLiteral(
"smartgroup" ), name );
1228 return addSmartgroup( name, op, conditions.values( QStringLiteral(
"tag" ) ),
1229 conditions.values( QStringLiteral(
"!tag" ) ),
1230 conditions.values( QStringLiteral(
"name" ) ),
1231 conditions.values( QStringLiteral(
"!name" ) ) );
1234 int QgsStyle::addSmartgroup(
const QString &name,
const QString &op,
const QStringList &matchTag,
const QStringList &noMatchTag,
const QStringList &matchName,
const QStringList &noMatchName )
1236 QDomDocument doc( QStringLiteral(
"dummy" ) );
1237 QDomElement smartEl = doc.createElement( QStringLiteral(
"smartgroup" ) );
1238 smartEl.setAttribute( QStringLiteral(
"name" ), name );
1239 smartEl.setAttribute( QStringLiteral(
"operator" ), op );
1241 auto addCondition = [&doc, &smartEl](
const QString & constraint,
const QStringList & parameters )
1243 for (
const QString ¶m : parameters )
1245 QDomElement condEl = doc.createElement( QStringLiteral(
"condition" ) );
1246 condEl.setAttribute( QStringLiteral(
"constraint" ), constraint );
1247 condEl.setAttribute( QStringLiteral(
"param" ), param );
1248 smartEl.appendChild( condEl );
1251 addCondition( QStringLiteral(
"tag" ), matchTag );
1252 addCondition( QStringLiteral(
"!tag" ), noMatchTag );
1253 addCondition( QStringLiteral(
"name" ), matchName );
1254 addCondition( QStringLiteral(
"!name" ), noMatchName );
1256 QByteArray xmlArray;
1257 QTextStream stream( &xmlArray );
1258 stream.setCodec(
"UTF-8" );
1259 smartEl.save( stream, 4 );
1260 auto query =
QgsSqlite3Mprintf(
"INSERT INTO smartgroup VALUES (NULL, '%q', '%q')",
1261 name.toUtf8().constData(), xmlArray.constData() );
1263 if ( runEmptyQuery( query ) )
1266 settings.
setValue( QStringLiteral(
"qgis/symbolsListGroupsIndex" ), 0 );
1269 return static_cast< int >( sqlite3_last_insert_rowid( mCurrentDB.get() ) );
1273 QgsDebugMsg( QStringLiteral(
"Couldn't insert symbol into the database!" ) );
1282 QgsDebugMsg( QStringLiteral(
"Cannot open database for listing groups" ) );
1291 statement = mCurrentDB.
prepare( query, nError );
1294 while ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1297 groupNames.insert( sqlite3_column_int( statement.get(),
SmartgroupId ), group );
1307 QgsDebugMsg( QStringLiteral(
"Cannot open database for listing groups" ) );
1308 return QStringList();
1316 statement = mCurrentDB.
prepare( query, nError );
1319 while ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1329 QStringList symbols;
1334 int nErr; statement = mCurrentDB.
prepare( query, nErr );
1335 if ( !( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW ) )
1337 return QStringList();
1343 if ( !doc.setContent( xmlstr ) )
1345 QgsDebugMsg( QStringLiteral(
"Cannot open smartgroup id: %1" ).arg(
id ) );
1347 QDomElement smartEl = doc.documentElement();
1348 QString op = smartEl.attribute( QStringLiteral(
"operator" ) );
1349 QDomNodeList conditionNodes = smartEl.childNodes();
1351 bool firstSet =
true;
1352 for (
int i = 0; i < conditionNodes.count(); i++ )
1354 QDomElement condEl = conditionNodes.at( i ).toElement();
1355 QString constraint = condEl.attribute( QStringLiteral(
"constraint" ) );
1356 QString param = condEl.attribute( QStringLiteral(
"param" ) );
1358 QStringList resultNames;
1360 if ( constraint == QLatin1String(
"tag" ) )
1364 else if ( constraint == QLatin1String(
"name" ) )
1368 resultNames =
symbolNames().filter( param, Qt::CaseInsensitive );
1372 resultNames =
colorRampNames().filter( param, Qt::CaseInsensitive );
1375 else if ( constraint == QLatin1String(
"!tag" ) )
1379 for (
const QString &name : unwanted )
1381 resultNames.removeAll( name );
1384 else if ( constraint == QLatin1String(
"!name" ) )
1387 for (
const QString &str : all )
1389 if ( !str.contains( param, Qt::CaseInsensitive ) )
1397 symbols = resultNames;
1402 if ( op == QLatin1String(
"OR" ) )
1404 symbols << resultNames;
1406 else if ( op == QLatin1String(
"AND" ) )
1408 QStringList dummy = symbols;
1410 for (
const QString &result : qgis::as_const( resultNames ) )
1412 if ( dummy.contains( result ) )
1421 QStringList unique = symbols.toSet().toList();
1422 std::sort( unique.begin(), unique.end() );
1430 QgsDebugMsg( QStringLiteral(
"Cannot open database for listing groups" ) );
1440 statement = mCurrentDB.
prepare( query, nError );
1441 if ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1445 if ( !doc.setContent( xmlstr ) )
1447 QgsDebugMsg( QStringLiteral(
"Cannot open smartgroup id: %1" ).arg(
id ) );
1450 QDomElement smartEl = doc.documentElement();
1451 QDomNodeList conditionNodes = smartEl.childNodes();
1453 for (
int i = 0; i < conditionNodes.count(); i++ )
1455 QDomElement condEl = conditionNodes.at( i ).toElement();
1456 QString constraint = condEl.attribute( QStringLiteral(
"constraint" ) );
1457 QString param = condEl.attribute( QStringLiteral(
"param" ) );
1459 condition.insert( constraint, param );
1470 QgsDebugMsg( QStringLiteral(
"Cannot open database for listing groups" ) );
1480 statement = mCurrentDB.
prepare( query, nError );
1481 if ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1485 if ( !doc.setContent( xmlstr ) )
1487 QgsDebugMsg( QStringLiteral(
"Cannot open smartgroup id: %1" ).arg(
id ) );
1489 QDomElement smartEl = doc.documentElement();
1490 op = smartEl.attribute( QStringLiteral(
"operator" ) );
1498 if ( filename.isEmpty() )
1500 QgsDebugMsg( QStringLiteral(
"Invalid filename for style export." ) );
1504 QDomDocument doc( QStringLiteral(
"qgis_style" ) );
1505 QDomElement root = doc.createElement( QStringLiteral(
"qgis_style" ) );
1507 doc.appendChild( root );
1514 QDomNodeList symbolsList = symbolsElem.elementsByTagName( QStringLiteral(
"symbol" ) );
1515 int nbSymbols = symbolsList.count();
1516 for (
int i = 0; i < nbSymbols; ++i )
1518 QDomElement
symbol = symbolsList.at( i ).toElement();
1519 QString name = symbol.attribute( QStringLiteral(
"name" ) );
1521 if ( tags.count() > 0 )
1523 symbol.setAttribute( QStringLiteral(
"tags" ), tags.join(
',' ) );
1525 if ( favoriteSymbols.contains( name ) )
1527 symbol.setAttribute( QStringLiteral(
"favorite" ), QStringLiteral(
"1" ) );
1532 QDomElement rampsElem = doc.createElement( QStringLiteral(
"colorramps" ) );
1533 for ( QMap<QString, QgsColorRamp *>::const_iterator itr = mColorRamps.constBegin(); itr != mColorRamps.constEnd(); ++itr )
1537 if ( tags.count() > 0 )
1539 rampEl.setAttribute( QStringLiteral(
"tags" ), tags.join(
',' ) );
1541 if ( favoriteColorramps.contains( itr.key() ) )
1543 rampEl.setAttribute( QStringLiteral(
"favorite" ), QStringLiteral(
"1" ) );
1545 rampsElem.appendChild( rampEl );
1548 root.appendChild( symbolsElem );
1549 root.appendChild( rampsElem );
1552 QFile f( filename );
1553 if ( !f.open( QFile::WriteOnly | QIODevice::Truncate ) )
1555 mErrorString =
"Couldn't open file for writing: " + filename;
1559 QTextStream ts( &f );
1560 ts.setCodec(
"UTF-8" );
1564 mFileName = filename;
1570 mErrorString = QString();
1571 QDomDocument doc( QStringLiteral(
"style" ) );
1572 QFile f( filename );
1573 if ( !f.open( QFile::ReadOnly ) )
1575 mErrorString = QStringLiteral(
"Unable to open the specified file" );
1576 QgsDebugMsg( QStringLiteral(
"Error opening the style XML file." ) );
1580 if ( !doc.setContent( &f ) )
1582 mErrorString = QStringLiteral(
"Unable to understand the style file: %1" ).arg( filename );
1583 QgsDebugMsg( QStringLiteral(
"XML Parsing error" ) );
1589 QDomElement docEl = doc.documentElement();
1590 if ( docEl.tagName() != QLatin1String(
"qgis_style" ) )
1592 mErrorString =
"Incorrect root tag in style: " + docEl.tagName();
1596 QString version = docEl.attribute( QStringLiteral(
"version" ) );
1599 mErrorString =
"Unknown style file version: " + version;
1605 QDomElement symbolsElement = docEl.firstChildElement( QStringLiteral(
"symbols" ) );
1606 QDomElement e = symbolsElement.firstChildElement();
1610 runEmptyQuery( query );
1615 while ( !e.isNull() )
1617 if ( e.tagName() == QLatin1String(
"symbol" ) )
1619 QString name = e.attribute( QStringLiteral(
"name" ) );
1621 if ( e.hasAttribute( QStringLiteral(
"tags" ) ) )
1623 tags = e.attribute( QStringLiteral(
"tags" ) ).split(
',' );
1625 bool favorite =
false;
1626 if ( e.hasAttribute( QStringLiteral(
"favorite" ) ) && e.attribute( QStringLiteral(
"favorite" ) ) == QStringLiteral(
"1" ) )
1645 e = e.nextSiblingElement();
1654 for ( QMap<QString, QgsSymbol *>::iterator it = symbols.begin(); it != symbols.end(); ++it )
1661 QDomElement rampsElement = docEl.firstChildElement( QStringLiteral(
"colorramps" ) );
1662 e = rampsElement.firstChildElement();
1663 while ( !e.isNull() )
1665 if ( e.tagName() == QLatin1String(
"colorramp" ) )
1667 QString name = e.attribute( QStringLiteral(
"name" ) );
1669 if ( e.hasAttribute( QStringLiteral(
"tags" ) ) )
1671 tags = e.attribute( QStringLiteral(
"tags" ) ).split(
',' );
1673 bool favorite =
false;
1674 if ( e.hasAttribute( QStringLiteral(
"favorite" ) ) && e.attribute( QStringLiteral(
"favorite" ) ) == QStringLiteral(
"1" ) )
1693 e = e.nextSiblingElement();
1697 runEmptyQuery( query );
1699 mFileName = filename;
1705 QFileInfo fileInfo( path );
1707 if ( fileInfo.suffix().compare( QLatin1String(
"xml" ), Qt::CaseInsensitive ) != 0 )
1711 if ( !QFile::exists( path ) )
1714 QFile inputFile( path );
1715 if ( !inputFile.open( QIODevice::ReadOnly ) )
1718 QTextStream stream( &inputFile );
1719 const QString line = stream.readLine();
1720 return line == QLatin1String(
"<!DOCTYPE qgis_style>" );
1723 bool QgsStyle::updateSymbol(
StyleEntity type,
const QString &name )
1725 QDomDocument doc( QStringLiteral(
"dummy" ) );
1727 QByteArray xmlArray;
1728 QTextStream stream( &xmlArray );
1729 stream.setCodec(
"UTF-8" );
1738 QgsDebugMsg( QStringLiteral(
"Update request received for unavailable symbol" ) );
1743 if ( symEl.isNull() )
1745 QgsDebugMsg( QStringLiteral(
"Couldn't convert symbol to valid XML!" ) );
1748 symEl.save( stream, 4 );
1750 xmlArray.constData(), name.toUtf8().constData() );
1756 QgsDebugMsg( QStringLiteral(
"Update requested for unavailable color ramp." ) );
1760 std::unique_ptr< QgsColorRamp > ramp(
colorRamp( name ) );
1762 if ( symEl.isNull() )
1764 QgsDebugMsg( QStringLiteral(
"Couldn't convert color ramp to valid XML!" ) );
1767 symEl.save( stream, 4 );
1769 xmlArray.constData(), name.toUtf8().constData() );
1773 QgsDebugMsg( QStringLiteral(
"Updating the unsupported StyleEntity" ) );
1778 if ( !runEmptyQuery( query ) )
1780 QgsDebugMsg( QStringLiteral(
"Couldn't insert symbol into the database!" ) );
1808 mCachedSymbolTags.remove( name );
1812 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()
Is 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.