QGIS/src/core/symbology/qgsstyle.cpp
Nathan Woodrow 90857b2b18 [FEATURE] Settings migration framework (#5080)
Only run for default profile and only if
not run before. Moves settings and symbols from
QGIS 2.x to QGIS 3 default profile

* --version-migration flag to force migration
2017-10-27 20:14:15 +10:00

1703 lines
48 KiB
C++

/***************************************************************************
qgsstyle.cpp
---------------------
begin : November 2009
copyright : (C) 2009 by Martin Dobias
email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsstyle.h"
#include "qgssymbol.h"
#include "qgscolorramp.h"
#include "qgssymbollayerregistry.h"
#include "qgsapplication.h"
#include "qgslogger.h"
#include "qgsreadwritecontext.h"
#include "qgssettings.h"
#include <QDomDocument>
#include <QDomElement>
#include <QDomNode>
#include <QDomNodeList>
#include <QFile>
#include <QTextStream>
#include <QByteArray>
#include <sqlite3.h>
#define STYLE_CURRENT_VERSION "1"
QgsStyle *QgsStyle::sDefaultStyle = nullptr;
QgsStyle::~QgsStyle()
{
clear();
}
QgsStyle *QgsStyle::defaultStyle() // static
{
if ( !sDefaultStyle )
{
QString styleFilename = QgsApplication::userStylePath();
// copy default style if user style doesn't exist
if ( !QFile::exists( styleFilename ) )
{
sDefaultStyle = new QgsStyle;
sDefaultStyle->createDatabase( styleFilename );
if ( QFile::exists( QgsApplication::defaultStylePath() ) )
{
sDefaultStyle->importXml( QgsApplication::defaultStylePath() );
}
}
else
{
sDefaultStyle = new QgsStyle;
sDefaultStyle->load( styleFilename );
}
}
return sDefaultStyle;
}
void QgsStyle::clear()
{
qDeleteAll( mSymbols );
qDeleteAll( mColorRamps );
mSymbols.clear();
mColorRamps.clear();
if ( mCurrentDB )
sqlite3_close( mCurrentDB );
}
bool QgsStyle::addSymbol( const QString &name, QgsSymbol *symbol, bool update )
{
if ( !symbol || name.isEmpty() )
return false;
// delete previous symbol (if any)
if ( mSymbols.contains( name ) )
{
// TODO remove groups and tags?
delete mSymbols.value( name );
mSymbols.insert( name, symbol );
if ( update )
updateSymbol( SymbolEntity, name );
}
else
{
mSymbols.insert( name, symbol );
if ( update )
saveSymbol( name, symbol, false, QStringList() );
}
return true;
}
bool QgsStyle::saveSymbol( const QString &name, QgsSymbol *symbol, bool favorite, const QStringList &tags )
{
// TODO add support for groups
QDomDocument doc( QStringLiteral( "dummy" ) );
QDomElement symEl = QgsSymbolLayerUtils::saveSymbol( name, symbol, doc, QgsReadWriteContext() );
if ( symEl.isNull() )
{
QgsDebugMsg( "Couldn't convert symbol to valid XML!" );
return false;
}
QByteArray xmlArray;
QTextStream stream( &xmlArray );
stream.setCodec( "UTF-8" );
symEl.save( stream, 4 );
char *query = sqlite3_mprintf( "INSERT INTO symbol VALUES (NULL, '%q', '%q', %d);",
name.toUtf8().constData(), xmlArray.constData(), ( favorite ? 1 : 0 ) );
if ( !runEmptyQuery( query ) )
{
QgsDebugMsg( "Couldn't insert symbol into the database!" );
return false;
}
tagSymbol( SymbolEntity, name, tags );
emit symbolSaved( name, symbol );
return true;
}
bool QgsStyle::removeSymbol( const QString &name )
{
QgsSymbol *symbol = mSymbols.take( name );
if ( !symbol )
return false;
// remove from map and delete
delete symbol;
// TODO
// Simplify this work here, its STUPID to run two DB queries for the sake of remove()
if ( !mCurrentDB )
{
QgsDebugMsg( "Sorry! Cannot open database to tag." );
return false;
}
int symbolid = symbolId( name );
if ( !symbolid )
{
QgsDebugMsg( "No such symbol for deleting in database: " + name + ". Cheers." );
}
remove( SymbolEntity, symbolid );
return true;
}
QgsSymbol *QgsStyle::symbol( const QString &name )
{
const QgsSymbol *symbol = symbolRef( name );
return symbol ? symbol->clone() : nullptr;
}
const QgsSymbol *QgsStyle::symbolRef( const QString &name ) const
{
return mSymbols.value( name );
}
int QgsStyle::symbolCount()
{
return mSymbols.count();
}
QStringList QgsStyle::symbolNames()
{
return mSymbols.keys();
}
bool QgsStyle::addColorRamp( const QString &name, QgsColorRamp *colorRamp, bool update )
{
if ( !colorRamp || name.isEmpty() )
return false;
// delete previous color ramps (if any)
if ( mColorRamps.contains( name ) )
{
// TODO remove groups and tags?
delete mColorRamps.value( name );
mColorRamps.insert( name, colorRamp );
if ( update )
updateSymbol( ColorrampEntity, name );
}
else
{
mColorRamps.insert( name, colorRamp );
if ( update )
saveColorRamp( name, colorRamp, false, QStringList() );
}
return true;
}
bool QgsStyle::saveColorRamp( const QString &name, QgsColorRamp *ramp, bool favorite, const QStringList &tags )
{
// insert it into the database
QDomDocument doc( QStringLiteral( "dummy" ) );
QDomElement rampEl = QgsSymbolLayerUtils::saveColorRamp( name, ramp, doc );
if ( rampEl.isNull() )
{
QgsDebugMsg( "Couldn't convert color ramp to valid XML!" );
return false;
}
QByteArray xmlArray;
QTextStream stream( &xmlArray );
stream.setCodec( "UTF-8" );
rampEl.save( stream, 4 );
char *query = sqlite3_mprintf( "INSERT INTO colorramp VALUES (NULL, '%q', '%q', %d);",
name.toUtf8().constData(), xmlArray.constData(), ( favorite ? 1 : 0 ) );
if ( !runEmptyQuery( query ) )
{
QgsDebugMsg( "Couldn't insert colorramp into the database!" );
return false;
}
tagSymbol( ColorrampEntity, name, tags );
return true;
}
bool QgsStyle::removeColorRamp( const QString &name )
{
QgsColorRamp *ramp = mColorRamps.take( name );
if ( !ramp )
return false;
char *query = sqlite3_mprintf( "DELETE FROM colorramp WHERE name='%q'", name.toUtf8().constData() );
if ( !runEmptyQuery( query ) )
{
QgsDebugMsg( "Couldn't remove color ramp from the database." );
return false;
}
delete ramp;
return true;
}
QgsColorRamp *QgsStyle::colorRamp( const QString &name ) const
{
const QgsColorRamp *ramp = colorRampRef( name );
return ramp ? ramp->clone() : nullptr;
}
const QgsColorRamp *QgsStyle::colorRampRef( const QString &name ) const
{
return mColorRamps.value( name );
}
int QgsStyle::colorRampCount()
{
return mColorRamps.count();
}
QStringList QgsStyle::colorRampNames()
{
return mColorRamps.keys();
}
bool QgsStyle::openDatabase( const QString &filename )
{
int rc = sqlite3_open( filename.toUtf8(), &mCurrentDB );
if ( rc )
{
mErrorString = "Couldn't open the style database: " + QString( sqlite3_errmsg( mCurrentDB ) );
sqlite3_close( mCurrentDB );
return false;
}
return true;
}
bool QgsStyle::createDatabase( const QString &filename )
{
mErrorString.clear();
if ( !openDatabase( filename ) )
{
mErrorString = QStringLiteral( "Unable to create database" );
QgsDebugMsg( mErrorString );
return false;
}
createTables();
return true;
}
bool QgsStyle::createMemoryDatabase()
{
mErrorString.clear();
if ( !openDatabase( QStringLiteral( ":memory:" ) ) )
{
mErrorString = QStringLiteral( "Unable to create temporary memory database" );
QgsDebugMsg( mErrorString );
return false;
}
createTables();
return true;
}
void QgsStyle::createTables()
{
char *query = sqlite3_mprintf( "CREATE TABLE symbol("\
"id INTEGER PRIMARY KEY,"\
"name TEXT UNIQUE,"\
"xml TEXT,"\
"favorite INTEGER);"\
"CREATE TABLE colorramp("\
"id INTEGER PRIMARY KEY,"\
"name TEXT UNIQUE,"\
"xml TEXT,"\
"favorite INTEGER);"\
"CREATE TABLE tag("\
"id INTEGER PRIMARY KEY,"\
"name TEXT);"\
"CREATE TABLE tagmap("\
"tag_id INTEGER NOT NULL,"\
"symbol_id INTEGER);"\
"CREATE TABLE ctagmap("\
"tag_id INTEGER NOT NULL,"\
"colorramp_id INTEGER);"\
"CREATE TABLE smartgroup("\
"id INTEGER PRIMARY KEY,"\
"name TEXT,"\
"xml TEXT);" );
runEmptyQuery( query );
}
bool QgsStyle::load( const QString &filename )
{
mErrorString.clear();
// Open the sqlite database
if ( !openDatabase( filename ) )
{
mErrorString = QStringLiteral( "Unable to open database file specified" );
QgsDebugMsg( mErrorString );
return false;
}
// Make sure there are no Null fields in parenting symbols and groups
char *query = sqlite3_mprintf( "UPDATE symbol SET favorite=0 WHERE favorite IS NULL;"
"UPDATE colorramp SET favorite=0 WHERE favorite IS NULL;"
);
runEmptyQuery( query );
// First create all the main symbols
query = sqlite3_mprintf( "SELECT * FROM symbol" );
sqlite3_stmt *ppStmt = nullptr;
int nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
while ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
QDomDocument doc;
QString symbol_name = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, SymbolName ) ) );
QString xmlstring = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, SymbolXML ) ) );
if ( !doc.setContent( xmlstring ) )
{
QgsDebugMsg( "Cannot open symbol " + symbol_name );
continue;
}
QDomElement symElement = doc.documentElement();
QgsSymbol *symbol = QgsSymbolLayerUtils::loadSymbol( symElement, QgsReadWriteContext() );
if ( symbol )
mSymbols.insert( symbol_name, symbol );
}
sqlite3_finalize( ppStmt );
query = sqlite3_mprintf( "SELECT * FROM colorramp" );
nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
while ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
QDomDocument doc;
QString ramp_name = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, ColorrampName ) ) );
QString xmlstring = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, ColorrampXML ) ) );
if ( !doc.setContent( xmlstring ) )
{
QgsDebugMsg( "Cannot open symbol " + ramp_name );
continue;
}
QDomElement rampElement = doc.documentElement();
QgsColorRamp *ramp = QgsSymbolLayerUtils::loadColorRamp( rampElement );
if ( ramp )
mColorRamps.insert( ramp_name, ramp );
}
mFileName = filename;
return true;
}
bool QgsStyle::save( QString filename )
{
mErrorString.clear();
if ( filename.isEmpty() )
filename = mFileName;
// TODO evaluate the requirement of this function and change implementation accordingly
// TODO remove QEXPECT_FAIL from TestStyle::testSaveLoad() when done
#if 0
QDomDocument doc( "qgis_style" );
QDomElement root = doc.createElement( "qgis_style" );
root.setAttribute( "version", STYLE_CURRENT_VERSION );
doc.appendChild( root );
QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( mSymbols, "symbols", doc );
QDomElement rampsElem = doc.createElement( "colorramps" );
// save color ramps
for ( QMap<QString, QgsColorRamp *>::iterator itr = mColorRamps.begin(); itr != mColorRamps.end(); ++itr )
{
QDomElement rampEl = QgsSymbolLayerUtils::saveColorRamp( itr.key(), itr.value(), doc );
rampsElem.appendChild( rampEl );
}
root.appendChild( symbolsElem );
root.appendChild( rampsElem );
// save
QFile f( filename );
if ( !f.open( QFile::WriteOnly ) )
{
mErrorString = "Couldn't open file for writing: " + filename;
return false;
}
QTextStream ts( &f );
ts.setCodec( "UTF-8" );
doc.save( ts, 2 );
f.close();
#endif
mFileName = filename;
return true;
}
bool QgsStyle::renameSymbol( const QString &oldName, const QString &newName )
{
if ( mSymbols.contains( newName ) )
{
QgsDebugMsg( "Symbol of new name already exists" );
return false;
}
QgsSymbol *symbol = mSymbols.take( oldName );
if ( !symbol )
return false;
mSymbols.insert( newName, symbol );
if ( !mCurrentDB )
{
QgsDebugMsg( "Sorry! Cannot open database to tag." );
return false;
}
int symbolid = symbolId( oldName );
if ( !symbolid )
{
QgsDebugMsg( "No such symbol for tagging in database: " + oldName );
return false;
}
rename( SymbolEntity, symbolid, newName );
return true;
}
bool QgsStyle::renameColorRamp( const QString &oldName, const QString &newName )
{
if ( mColorRamps.contains( newName ) )
{
QgsDebugMsg( "Color ramp of new name already exists." );
return false;
}
QgsColorRamp *ramp = mColorRamps.take( oldName );
if ( !ramp )
return false;
mColorRamps.insert( newName, ramp );
int rampid = 0;
sqlite3_stmt *ppStmt = nullptr;
char *query = sqlite3_mprintf( "SELECT id FROM colorramp WHERE name='%q'", oldName.toUtf8().constData() );
int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
if ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
rampid = sqlite3_column_int( ppStmt, 0 );
}
sqlite3_finalize( ppStmt );
rename( ColorrampEntity, rampid, newName );
return true;
}
QStringList QgsStyle::symbolsOfFavorite( StyleEntity type ) const
{
if ( !mCurrentDB )
{
QgsDebugMsg( QString( "Cannot Open database for getting favorite symbols" ) );
return QStringList();
}
char *query = nullptr;
if ( type == SymbolEntity )
{
query = sqlite3_mprintf( "SELECT name FROM symbol WHERE favorite=1" );
}
else if ( type == ColorrampEntity )
{
query = sqlite3_mprintf( "SELECT name FROM colorramp WHERE favorite=1" );
}
else
{
QgsDebugMsg( "No such style entity" );
return QStringList();
}
sqlite3_stmt *ppStmt = nullptr;
int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
QStringList symbols;
while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
symbols << QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
}
sqlite3_finalize( ppStmt );
return symbols;
}
QStringList QgsStyle::symbolsWithTag( StyleEntity type, int tagid ) const
{
if ( !mCurrentDB )
{
QgsDebugMsg( QString( "Cannot open database to get symbols of tagid %1" ).arg( tagid ) );
return QStringList();
}
char *subquery = nullptr;
if ( type == SymbolEntity )
{
subquery = sqlite3_mprintf( "SELECT symbol_id FROM tagmap WHERE tag_id=%d", tagid );
}
else if ( type == ColorrampEntity )
{
subquery = sqlite3_mprintf( "SELECT colorramp_id FROM ctagmap WHERE tag_id=%d", tagid );
}
else
{
QgsDebugMsg( "Unknown Entity" );
return QStringList();
}
sqlite3_stmt *ppStmt = nullptr;
int nErr = sqlite3_prepare_v2( mCurrentDB, subquery, -1, &ppStmt, nullptr );
// get the symbol <-> tag connection from table 'tagmap'/'ctagmap'
QStringList symbols;
while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
int id = sqlite3_column_int( ppStmt, 0 );
char *query = type == SymbolEntity
? sqlite3_mprintf( "SELECT name FROM symbol WHERE id=%d", id )
: sqlite3_mprintf( "SELECT name FROM colorramp WHERE id=%d", id );
sqlite3_stmt *ppStmt2 = nullptr;
int sErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt2, nullptr );
while ( sErr == SQLITE_OK && sqlite3_step( ppStmt2 ) == SQLITE_ROW )
{
symbols << QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt2, 0 ) ) );
}
sqlite3_finalize( ppStmt2 );
}
sqlite3_finalize( ppStmt );
return symbols;
}
int QgsStyle::addTag( const QString &tagname )
{
if ( !mCurrentDB )
return 0;
sqlite3_stmt *ppStmt = nullptr;
char *query = sqlite3_mprintf( "INSERT INTO tag VALUES (NULL, '%q')", tagname.toUtf8().constData() );
int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
if ( nErr == SQLITE_OK )
( void )sqlite3_step( ppStmt );
sqlite3_finalize( ppStmt );
QgsSettings settings;
settings.setValue( QStringLiteral( "qgis/symbolsListGroupsIndex" ), 0 );
emit groupsModified();
return static_cast< int >( sqlite3_last_insert_rowid( mCurrentDB ) );
}
QStringList QgsStyle::tags() const
{
if ( !mCurrentDB )
return QStringList();
sqlite3_stmt *ppStmt = nullptr;
char *query = sqlite3_mprintf( "SELECT name FROM tag" );
int nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
QStringList tagList;
while ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
tagList << QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
}
sqlite3_finalize( ppStmt );
return tagList;
}
void QgsStyle::rename( StyleEntity type, int id, const QString &newName )
{
bool groupRenamed = false;
char *query = nullptr;
switch ( type )
{
case SymbolEntity:
query = sqlite3_mprintf( "UPDATE symbol SET name='%q' WHERE id=%d", newName.toUtf8().constData(), id );
break;
case ColorrampEntity:
query = sqlite3_mprintf( "UPDATE colorramp SET name='%q' WHERE id=%d", newName.toUtf8().constData(), id );
break;
case TagEntity:
query = sqlite3_mprintf( "UPDATE tag SET name='%q' WHERE id=%d", newName.toUtf8().constData(), id );
groupRenamed = true;
break;
case SmartgroupEntity:
query = sqlite3_mprintf( "UPDATE smartgroup SET name='%q' WHERE id=%d", newName.toUtf8().constData(), id );
groupRenamed = true;
break;
default:
QgsDebugMsg( "Invalid Style Entity indicated" );
return;
}
if ( !runEmptyQuery( query ) )
{
mErrorString = QStringLiteral( "Could not rename!" );
}
else
{
if ( groupRenamed )
{
emit groupsModified();
}
}
}
void QgsStyle::remove( StyleEntity type, int id )
{
bool groupRemoved = false;
char *query = nullptr;
switch ( type )
{
case SymbolEntity:
query = sqlite3_mprintf( "DELETE FROM symbol WHERE id=%d; DELETE FROM tagmap WHERE symbol_id=%d", id, id );
break;
case ColorrampEntity:
query = sqlite3_mprintf( "DELETE FROM colorramp WHERE id=%d", id );
break;
case TagEntity:
query = sqlite3_mprintf( "DELETE FROM tag WHERE id=%d; DELETE FROM tagmap WHERE tag_id=%d", id, id );
groupRemoved = true;
break;
case SmartgroupEntity:
query = sqlite3_mprintf( "DELETE FROM smartgroup WHERE id=%d", id );
groupRemoved = true;
break;
default:
QgsDebugMsg( "Invalid Style Entity indicated" );
return;
}
if ( !runEmptyQuery( query ) )
{
QgsDebugMsg( "Could not delete entity!" );
}
else
{
if ( groupRemoved )
{
QgsSettings settings;
settings.setValue( QStringLiteral( "qgis/symbolsListGroupsIndex" ), 0 );
emit groupsModified();
}
}
}
bool QgsStyle::runEmptyQuery( char *query, bool freeQuery )
{
if ( !mCurrentDB )
return false;
char *zErr = nullptr;
int nErr = sqlite3_exec( mCurrentDB, query, nullptr, nullptr, &zErr );
if ( freeQuery )
{
sqlite3_free( query );
}
if ( nErr != SQLITE_OK )
{
QgsDebugMsg( zErr );
sqlite3_free( zErr );
}
return zErr == SQLITE_OK;
}
bool QgsStyle::addFavorite( StyleEntity type, const QString &name )
{
char *query = nullptr;
switch ( type )
{
case SymbolEntity:
query = sqlite3_mprintf( "UPDATE symbol SET favorite=1 WHERE name='%q'", name.toUtf8().constData() );
break;
case ColorrampEntity:
query = sqlite3_mprintf( "UPDATE colorramp SET favorite=1 WHERE name='%q'", name.toUtf8().constData() );
break;
default:
QgsDebugMsg( "Wrong entity value. cannot apply group" );
return false;
}
return runEmptyQuery( query );
}
bool QgsStyle::removeFavorite( StyleEntity type, const QString &name )
{
char *query = nullptr;
switch ( type )
{
case SymbolEntity:
query = sqlite3_mprintf( "UPDATE symbol SET favorite=0 WHERE name='%q'", name.toUtf8().constData() );
break;
case ColorrampEntity:
query = sqlite3_mprintf( "UPDATE colorramp SET favorite=0 WHERE name='%q'", name.toUtf8().constData() );
break;
default:
QgsDebugMsg( "Wrong entity value. cannot apply group" );
return false;
}
return runEmptyQuery( query );
}
QStringList QgsStyle::findSymbols( StyleEntity type, const QString &qword )
{
if ( !mCurrentDB )
{
QgsDebugMsg( "Sorry! Cannot open database to search" );
return QStringList();
}
// first find symbols with matching name
QString item = ( type == SymbolEntity ) ? QStringLiteral( "symbol" ) : QStringLiteral( "colorramp" );
char *query = sqlite3_mprintf( "SELECT name FROM %q WHERE name LIKE '%%%q%%'",
item.toUtf8().constData(), qword.toUtf8().constData() );
sqlite3_stmt *ppStmt = nullptr;
int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
QSet< QString > symbols;
while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
symbols << QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
}
sqlite3_finalize( ppStmt );
// next add symbols with matching tags
query = sqlite3_mprintf( "SELECT id FROM tag WHERE name LIKE '%%%q%%'", qword.toUtf8().constData() );
nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
QStringList tagids;
while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
tagids << QString::fromUtf8( ( const char * ) sqlite3_column_text( ppStmt, 0 ) );
}
sqlite3_finalize( ppStmt );
QString dummy = tagids.join( QStringLiteral( ", " ) );
if ( type == SymbolEntity )
{
query = sqlite3_mprintf( "SELECT symbol_id FROM tagmap WHERE tag_id IN (%q)",
dummy.toUtf8().constData() );
}
else
{
query = sqlite3_mprintf( "SELECT colorramp_id FROM ctagmap WHERE tag_id IN (%q)",
dummy.toUtf8().constData() );
}
nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
QStringList symbolids;
while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
symbolids << QString::fromUtf8( ( const char * ) sqlite3_column_text( ppStmt, 0 ) );
}
sqlite3_finalize( ppStmt );
dummy = symbolids.join( QStringLiteral( ", " ) );
query = sqlite3_mprintf( "SELECT name FROM %q WHERE id IN (%q)",
item.toUtf8().constData(), dummy.toUtf8().constData() );
nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
symbols << QString::fromUtf8( ( const char * ) sqlite3_column_text( ppStmt, 0 ) );
}
sqlite3_finalize( ppStmt );
return symbols.toList();
}
bool QgsStyle::tagSymbol( StyleEntity type, const QString &symbol, const QStringList &tags )
{
if ( !mCurrentDB )
{
QgsDebugMsg( "Sorry! Cannot open database to tag." );
return false;
}
int symbolid = type == SymbolEntity ? symbolId( symbol ) : colorrampId( symbol );
if ( !symbolid )
{
QgsDebugMsg( "No such symbol for tagging in database: " + symbol );
return false;
}
QString tag;
Q_FOREACH ( const QString &t, tags )
{
tag = t.trimmed();
if ( !tag.isEmpty() )
{
// sql: gets the id of the tag if present or insert the tag and get the id of the tag
char *query = sqlite3_mprintf( "SELECT id FROM tag WHERE LOWER(name)='%q'", tag.toUtf8().toLower().constData() );
sqlite3_stmt *ppStmt = nullptr;
int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
int tagid;
if ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
tagid = sqlite3_column_int( ppStmt, 0 );
}
else
{
tagid = addTag( tag );
}
sqlite3_finalize( ppStmt );
// Now map the tag to the symbol if it's not already tagged
if ( !symbolHasTag( type, symbol, tag ) )
{
query = type == SymbolEntity
? sqlite3_mprintf( "INSERT INTO tagmap VALUES (%d,%d)", tagid, symbolid )
: sqlite3_mprintf( "INSERT INTO ctagmap VALUES (%d,%d)", tagid, symbolid );
char *zErr = nullptr;
nErr = sqlite3_exec( mCurrentDB, query, nullptr, nullptr, &zErr );
if ( nErr )
{
QgsDebugMsg( zErr );
}
}
}
}
return true;
}
bool QgsStyle::detagSymbol( StyleEntity type, const QString &symbol, const QStringList &tags )
{
if ( !mCurrentDB )
{
QgsDebugMsg( "Sorry! Cannot open database for detgging." );
return false;
}
char *query = type == SymbolEntity
? sqlite3_mprintf( "SELECT id FROM symbol WHERE name='%q'", symbol.toUtf8().constData() )
: sqlite3_mprintf( "SELECT id FROM colorramp WHERE name='%q'", symbol.toUtf8().constData() );
sqlite3_stmt *ppStmt = nullptr;
int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
int symbolid = 0;
if ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
symbolid = sqlite3_column_int( ppStmt, 0 );
}
else
{
sqlite3_finalize( ppStmt );
return false;
}
sqlite3_finalize( ppStmt );
Q_FOREACH ( const QString &tag, tags )
{
query = sqlite3_mprintf( "SELECT id FROM tag WHERE name='%q'", tag.toUtf8().constData() );
sqlite3_stmt *ppStmt2 = nullptr;
nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt2, nullptr );
int tagid = 0;
if ( nErr == SQLITE_OK && sqlite3_step( ppStmt2 ) == SQLITE_ROW )
{
tagid = sqlite3_column_int( ppStmt2, 0 );
}
sqlite3_finalize( ppStmt2 );
if ( tagid )
{
// remove from the tagmap
query = type == SymbolEntity
? sqlite3_mprintf( "DELETE FROM tagmap WHERE tag_id=%d AND symbol_id=%d", tagid, symbolid )
: sqlite3_mprintf( "DELETE FROM ctagmap WHERE tag_id=%d AND colorramp_id=%d", tagid, symbolid );
runEmptyQuery( query );
}
}
// TODO Perform tag cleanup
// check the number of entries for a given tag in the tagmap
// if the count is 0, then remove( TagEntity, tagid )
return true;
}
bool QgsStyle::detagSymbol( StyleEntity type, const QString &symbol )
{
if ( !mCurrentDB )
{
QgsDebugMsg( "Sorry! Cannot open database for detgging." );
return false;
}
char *query = type == SymbolEntity
? sqlite3_mprintf( "SELECT id FROM symbol WHERE name='%q'", symbol.toUtf8().constData() )
: sqlite3_mprintf( "SELECT id FROM colorramp WHERE name='%q'", symbol.toUtf8().constData() );
sqlite3_stmt *ppStmt = nullptr;
int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
int symbolid = 0;
if ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
symbolid = sqlite3_column_int( ppStmt, 0 );
}
else
{
sqlite3_finalize( ppStmt );
return false;
}
sqlite3_finalize( ppStmt );
// remove all tags
query = type == SymbolEntity
? sqlite3_mprintf( "DELETE FROM tagmap WHERE symbol_id=%d", symbolid )
: sqlite3_mprintf( "DELETE FROM ctagmap WHERE colorramp_id=%d", symbolid );
runEmptyQuery( query );
// TODO Perform tag cleanup
// check the number of entries for a given tag in the tagmap
// if the count is 0, then remove( TagEntity, tagid )
return true;
}
QStringList QgsStyle::tagsOfSymbol( StyleEntity type, const QString &symbol )
{
if ( !mCurrentDB )
{
QgsDebugMsg( "Sorry! Cannot open database for getting the tags." );
return QStringList();
}
int symbolid = type == SymbolEntity ? symbolId( symbol ) : colorrampId( symbol );
if ( !symbolid )
return QStringList();
// get the ids of tags for the symbol
char *query = type == SymbolEntity
? sqlite3_mprintf( "SELECT tag_id FROM tagmap WHERE symbol_id=%d", symbolid )
: sqlite3_mprintf( "SELECT tag_id FROM ctagmap WHERE colorramp_id=%d", symbolid );
sqlite3_stmt *ppStmt = nullptr;
int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
QStringList tagList;
while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
char *subquery = sqlite3_mprintf( "SELECT name FROM tag WHERE id=%d", sqlite3_column_int( ppStmt, 0 ) );
sqlite3_stmt *ppStmt2 = nullptr;
int pErr = sqlite3_prepare_v2( mCurrentDB, subquery, -1, &ppStmt2, nullptr );
if ( pErr == SQLITE_OK && sqlite3_step( ppStmt2 ) == SQLITE_ROW )
{
tagList << QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt2, 0 ) ) );
}
sqlite3_finalize( ppStmt2 );
}
sqlite3_finalize( ppStmt );
return tagList;
}
bool QgsStyle::symbolHasTag( StyleEntity type, const QString &symbol, const QString &tag )
{
if ( !mCurrentDB )
{
QgsDebugMsg( "Sorry! Cannot open database for getting the tags." );
return false;
}
int symbolid = type == SymbolEntity ? symbolId( symbol ) : colorrampId( symbol );
if ( !symbolid )
{
return false;
}
int tagid = tagId( tag );
if ( !tagid )
{
return false;
}
// get the ids of tags for the symbol
char *query = type == SymbolEntity
? sqlite3_mprintf( "SELECT tag_id FROM tagmap WHERE tag_id=%d AND symbol_id=%d", tagid, symbolid )
: sqlite3_mprintf( "SELECT tag_id FROM ctagmap WHERE tag_id=%d AND colorramp_id=%d", tagid, symbolid );
sqlite3_stmt *ppStmt = nullptr;
int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
return ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW );
}
QString QgsStyle::tag( int id ) const
{
if ( !mCurrentDB )
return QString();
sqlite3_stmt *ppStmt = nullptr;
char *query = sqlite3_mprintf( "SELECT name FROM tag WHERE id=%d", id );
int nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
QString tag;
if ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
tag = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
}
sqlite3_finalize( ppStmt );
return tag;
}
int QgsStyle::getId( const QString &table, const QString &name )
{
char *query = sqlite3_mprintf( "SELECT id FROM %q WHERE LOWER(name)='%q'", table.toUtf8().constData(), name.toUtf8().toLower().constData() );
sqlite3_stmt *ppStmt = nullptr;
int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
int id = 0;
if ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
id = sqlite3_column_int( ppStmt, 0 );
}
sqlite3_finalize( ppStmt );
return id;
}
QString QgsStyle::getName( const QString &table, int id ) const
{
char *query = sqlite3_mprintf( "SELECT name FROM %q WHERE id='%q'", table.toUtf8().constData(), QString::number( id ).toUtf8().constData() );
sqlite3_stmt *ppStmt = nullptr;
int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
QString name;
if ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
name = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
}
sqlite3_finalize( ppStmt );
return name;
}
int QgsStyle::symbolId( const QString &name )
{
return getId( QStringLiteral( "symbol" ), name );
}
int QgsStyle::colorrampId( const QString &name )
{
return getId( QStringLiteral( "colorramp" ), name );
}
int QgsStyle::tagId( const QString &name )
{
return getId( QStringLiteral( "tag" ), name );
}
int QgsStyle::smartgroupId( const QString &name )
{
return getId( QStringLiteral( "smartgroup" ), name );
}
int QgsStyle::addSmartgroup( const QString &name, const QString &op, const QgsSmartConditionMap &conditions )
{
QDomDocument doc( QStringLiteral( "dummy" ) );
QDomElement smartEl = doc.createElement( QStringLiteral( "smartgroup" ) );
smartEl.setAttribute( QStringLiteral( "name" ), name );
smartEl.setAttribute( QStringLiteral( "operator" ), op );
QStringList constraints;
constraints << QStringLiteral( "tag" ) << QStringLiteral( "group" ) << QStringLiteral( "name" ) << QStringLiteral( "!tag" ) << QStringLiteral( "!group" ) << QStringLiteral( "!name" );
Q_FOREACH ( const QString &constraint, constraints )
{
QStringList parameters = conditions.values( constraint );
Q_FOREACH ( const QString &param, parameters )
{
QDomElement condEl = doc.createElement( QStringLiteral( "condition" ) );
condEl.setAttribute( QStringLiteral( "constraint" ), constraint );
condEl.setAttribute( QStringLiteral( "param" ), param );
smartEl.appendChild( condEl );
}
}
QByteArray xmlArray;
QTextStream stream( &xmlArray );
stream.setCodec( "UTF-8" );
smartEl.save( stream, 4 );
char *query = sqlite3_mprintf( "INSERT INTO smartgroup VALUES (NULL, '%q', '%q')",
name.toUtf8().constData(), xmlArray.constData() );
if ( runEmptyQuery( query ) )
{
QgsSettings settings;
settings.setValue( QStringLiteral( "qgis/symbolsListGroupsIndex" ), 0 );
emit groupsModified();
return static_cast< int >( sqlite3_last_insert_rowid( mCurrentDB ) );
}
else
{
QgsDebugMsg( "Couldn't insert symbol into the database!" );
return 0;
}
}
QgsSymbolGroupMap QgsStyle::smartgroupsListMap()
{
if ( !mCurrentDB )
{
QgsDebugMsg( "Cannot open database for listing groups" );
return QgsSymbolGroupMap();
}
char *query = sqlite3_mprintf( "SELECT * FROM smartgroup" );
// Now run the query and retrieve the group names
sqlite3_stmt *ppStmt = nullptr;
int nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
QgsSymbolGroupMap groupNames;
while ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
QString group = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, SmartgroupName ) ) );
groupNames.insert( sqlite3_column_int( ppStmt, SmartgroupId ), group );
}
sqlite3_finalize( ppStmt );
return groupNames;
}
QStringList QgsStyle::smartgroupNames()
{
if ( !mCurrentDB )
{
QgsDebugMsg( "Cannot open database for listing groups" );
return QStringList();
}
char *query = sqlite3_mprintf( "SELECT name FROM smartgroup" );
// Now run the query and retrieve the group names
sqlite3_stmt *ppStmt = nullptr;
int nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
QStringList groups;
while ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
groups << QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
}
sqlite3_finalize( ppStmt );
return groups;
}
QStringList QgsStyle::symbolsOfSmartgroup( StyleEntity type, int id )
{
QStringList symbols;
char *query = sqlite3_mprintf( "SELECT xml FROM smartgroup WHERE id=%d", id );
sqlite3_stmt *ppStmt = nullptr;
int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
if ( !( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW ) )
{
sqlite3_finalize( ppStmt );
return QStringList();
}
else
{
QDomDocument doc;
QString xmlstr = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
if ( !doc.setContent( xmlstr ) )
{
QgsDebugMsg( QString( "Cannot open smartgroup id: %1" ).arg( id ) );
}
QDomElement smartEl = doc.documentElement();
QString op = smartEl.attribute( QStringLiteral( "operator" ) );
QDomNodeList conditionNodes = smartEl.childNodes();
bool firstSet = true;
for ( int i = 0; i < conditionNodes.count(); i++ )
{
QDomElement condEl = conditionNodes.at( i ).toElement();
QString constraint = condEl.attribute( QStringLiteral( "constraint" ) );
QString param = condEl.attribute( QStringLiteral( "param" ) );
QStringList resultNames;
// perform suitable action for the given constraint
if ( constraint == QLatin1String( "tag" ) )
{
resultNames = symbolsWithTag( type, tagId( param ) );
}
else if ( constraint == QLatin1String( "name" ) )
{
if ( type == SymbolEntity )
{
resultNames = symbolNames().filter( param, Qt::CaseInsensitive );
}
else
{
resultNames = colorRampNames().filter( param, Qt::CaseInsensitive );
}
}
else if ( constraint == QLatin1String( "!tag" ) )
{
resultNames = type == SymbolEntity ? symbolNames() : colorRampNames();
QStringList unwanted = symbolsWithTag( type, tagId( param ) );
Q_FOREACH ( const QString &name, unwanted )
{
resultNames.removeAll( name );
}
}
else if ( constraint == QLatin1String( "!name" ) )
{
QStringList all = type == SymbolEntity ? symbolNames() : colorRampNames();
Q_FOREACH ( const QString &str, all )
{
if ( !str.contains( param, Qt::CaseInsensitive ) )
resultNames << str;
}
}
// not apply the operator
if ( firstSet )
{
symbols = resultNames;
firstSet = false;
}
else
{
if ( op == QLatin1String( "OR" ) )
{
symbols << resultNames;
}
else if ( op == QLatin1String( "AND" ) )
{
QStringList dummy = symbols;
symbols.clear();
Q_FOREACH ( const QString &result, resultNames )
{
if ( dummy.contains( result ) )
symbols << result;
}
}
}
} // DOM loop ends here
}
sqlite3_finalize( ppStmt );
return symbols;
}
QgsSmartConditionMap QgsStyle::smartgroup( int id )
{
if ( !mCurrentDB )
{
QgsDebugMsg( "Cannot open database for listing groups" );
return QgsSmartConditionMap();
}
QgsSmartConditionMap condition;
char *query = sqlite3_mprintf( "SELECT xml FROM smartgroup WHERE id=%d", id );
sqlite3_stmt *ppStmt = nullptr;
int nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
if ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
QDomDocument doc;
QString xmlstr = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
if ( !doc.setContent( xmlstr ) )
{
QgsDebugMsg( QString( "Cannot open smartgroup id: %1" ).arg( id ) );
}
QDomElement smartEl = doc.documentElement();
QDomNodeList conditionNodes = smartEl.childNodes();
for ( int i = 0; i < conditionNodes.count(); i++ )
{
QDomElement condEl = conditionNodes.at( i ).toElement();
QString constraint = condEl.attribute( QStringLiteral( "constraint" ) );
QString param = condEl.attribute( QStringLiteral( "param" ) );
condition.insert( constraint, param );
}
}
sqlite3_finalize( ppStmt );
return condition;
}
QString QgsStyle::smartgroupOperator( int id )
{
if ( !mCurrentDB )
{
QgsDebugMsg( "Cannot open database for listing groups" );
return QString();
}
QString op;
char *query = sqlite3_mprintf( "SELECT xml FROM smartgroup WHERE id=%d", id );
sqlite3_stmt *ppStmt = nullptr;
int nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
if ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
QDomDocument doc;
QString xmlstr = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
if ( !doc.setContent( xmlstr ) )
{
QgsDebugMsg( QString( "Cannot open smartgroup id: %1" ).arg( id ) );
}
QDomElement smartEl = doc.documentElement();
op = smartEl.attribute( QStringLiteral( "operator" ) );
}
sqlite3_finalize( ppStmt );
return op;
}
bool QgsStyle::exportXml( const QString &filename )
{
if ( filename.isEmpty() )
{
QgsDebugMsg( "Invalid filename for style export." );
return false;
}
QDomDocument doc( QStringLiteral( "qgis_style" ) );
QDomElement root = doc.createElement( QStringLiteral( "qgis_style" ) );
root.setAttribute( QStringLiteral( "version" ), STYLE_CURRENT_VERSION );
doc.appendChild( root );
QStringList favoriteSymbols = symbolsOfFavorite( SymbolEntity );
QStringList favoriteColorramps = symbolsOfFavorite( ColorrampEntity );
// save symbols and attach tags
QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( mSymbols, QStringLiteral( "symbols" ), doc, QgsReadWriteContext() );
QDomNodeList symbolsList = symbolsElem.elementsByTagName( QStringLiteral( "symbol" ) );
int nbSymbols = symbolsList.count();
for ( int i = 0; i < nbSymbols; ++i )
{
QDomElement symbol = symbolsList.at( i ).toElement();
QString name = symbol.attribute( QStringLiteral( "name" ) );
QStringList tags = tagsOfSymbol( SymbolEntity, name );
if ( tags.count() > 0 )
{
symbol.setAttribute( QStringLiteral( "tags" ), tags.join( ',' ) );
}
if ( favoriteSymbols.contains( name ) )
{
symbol.setAttribute( QStringLiteral( "favorite" ), QStringLiteral( "1" ) );
}
}
// save color ramps
QDomElement rampsElem = doc.createElement( QStringLiteral( "colorramps" ) );
for ( QMap<QString, QgsColorRamp *>::const_iterator itr = mColorRamps.constBegin(); itr != mColorRamps.constEnd(); ++itr )
{
QDomElement rampEl = QgsSymbolLayerUtils::saveColorRamp( itr.key(), itr.value(), doc );
QStringList tags = tagsOfSymbol( ColorrampEntity, itr.key() );
if ( tags.count() > 0 )
{
rampEl.setAttribute( QStringLiteral( "tags" ), tags.join( ',' ) );
}
if ( favoriteColorramps.contains( itr.key() ) )
{
rampEl.setAttribute( QStringLiteral( "favorite" ), QStringLiteral( "1" ) );
}
rampsElem.appendChild( rampEl );
}
root.appendChild( symbolsElem );
root.appendChild( rampsElem );
// save
QFile f( filename );
if ( !f.open( QFile::WriteOnly | QIODevice::Truncate ) )
{
mErrorString = "Couldn't open file for writing: " + filename;
return false;
}
QTextStream ts( &f );
ts.setCodec( "UTF-8" );
doc.save( ts, 2 );
f.close();
mFileName = filename;
return true;
}
bool QgsStyle::importXml( const QString &filename )
{
mErrorString = QString();
QDomDocument doc( QStringLiteral( "style" ) );
QFile f( filename );
if ( !f.open( QFile::ReadOnly ) )
{
mErrorString = QStringLiteral( "Unable to open the specified file" );
QgsDebugMsg( "Error opening the style XML file." );
return false;
}
if ( !doc.setContent( &f ) )
{
mErrorString = QStringLiteral( "Unable to understand the style file: %1" ).arg( filename );
QgsDebugMsg( "XML Parsing error" );
f.close();
return false;
}
f.close();
QDomElement docEl = doc.documentElement();
if ( docEl.tagName() != QLatin1String( "qgis_style" ) )
{
mErrorString = "Incorrect root tag in style: " + docEl.tagName();
return false;
}
QString version = docEl.attribute( QStringLiteral( "version" ) );
if ( version != STYLE_CURRENT_VERSION && version != QLatin1String( "0" ) )
{
mErrorString = "Unknown style file version: " + version;
return false;
}
QgsSymbolMap symbols;
QDomElement symbolsElement = docEl.firstChildElement( QStringLiteral( "symbols" ) );
QDomElement e = symbolsElement.firstChildElement();
// gain speed by re-grouping the INSERT statements in a transaction
char *query = nullptr;
query = sqlite3_mprintf( "BEGIN TRANSACTION;" );
runEmptyQuery( query );
if ( version == STYLE_CURRENT_VERSION )
{
// For the new style, load symbols individually
while ( !e.isNull() )
{
if ( e.tagName() == QLatin1String( "symbol" ) )
{
QString name = e.attribute( QStringLiteral( "name" ) );
QStringList tags;
if ( e.hasAttribute( QStringLiteral( "tags" ) ) )
{
tags = e.attribute( QStringLiteral( "tags" ) ).split( ',' );
}
bool favorite = false;
if ( e.hasAttribute( QStringLiteral( "favorite" ) ) && e.attribute( QStringLiteral( "favorite" ) ) == QStringLiteral( "1" ) )
{
favorite = true;
}
QgsSymbol *symbol = QgsSymbolLayerUtils::loadSymbol( e, QgsReadWriteContext() );
if ( symbol )
{
addSymbol( name, symbol );
if ( mCurrentDB )
{
saveSymbol( name, symbol, favorite, tags );
}
}
}
else
{
QgsDebugMsg( "unknown tag: " + e.tagName() );
}
e = e.nextSiblingElement();
}
}
else
{
// for the old version, use the utility function to solve @symbol@layer subsymbols
symbols = QgsSymbolLayerUtils::loadSymbols( symbolsElement, QgsReadWriteContext() );
// save the symbols with proper name
for ( QMap<QString, QgsSymbol *>::iterator it = symbols.begin(); it != symbols.end(); ++it )
{
addSymbol( it.key(), it.value() );
}
}
// load color ramps
QDomElement rampsElement = docEl.firstChildElement( QStringLiteral( "colorramps" ) );
e = rampsElement.firstChildElement();
while ( !e.isNull() )
{
if ( e.tagName() == QLatin1String( "colorramp" ) )
{
QString name = e.attribute( QStringLiteral( "name" ) );
QStringList tags;
if ( e.hasAttribute( QStringLiteral( "tags" ) ) )
{
tags = e.attribute( QStringLiteral( "tags" ) ).split( ',' );
}
bool favorite = false;
if ( e.hasAttribute( QStringLiteral( "favorite" ) ) && e.attribute( QStringLiteral( "favorite" ) ) == QStringLiteral( "1" ) )
{
favorite = true;
}
QgsColorRamp *ramp = QgsSymbolLayerUtils::loadColorRamp( e );
if ( ramp )
{
addColorRamp( name, ramp );
if ( mCurrentDB )
{
saveColorRamp( name, ramp, favorite, tags );
}
}
}
else
{
QgsDebugMsg( "unknown tag: " + e.tagName() );
}
e = e.nextSiblingElement();
}
query = sqlite3_mprintf( "COMMIT TRANSACTION;" );
runEmptyQuery( query );
mFileName = filename;
return true;
}
bool QgsStyle::updateSymbol( StyleEntity type, const QString &name )
{
QDomDocument doc( QStringLiteral( "dummy" ) );
QDomElement symEl;
QByteArray xmlArray;
QTextStream stream( &xmlArray );
stream.setCodec( "UTF-8" );
char *query = nullptr;
if ( type == SymbolEntity )
{
// check if it is an existing symbol
if ( !symbolNames().contains( name ) )
{
QgsDebugMsg( "Update request received for unavailable symbol" );
return false;
}
symEl = QgsSymbolLayerUtils::saveSymbol( name, symbol( name ), doc, QgsReadWriteContext() );
if ( symEl.isNull() )
{
QgsDebugMsg( "Couldn't convert symbol to valid XML!" );
return false;
}
symEl.save( stream, 4 );
query = sqlite3_mprintf( "UPDATE symbol SET xml='%q' WHERE name='%q';",
xmlArray.constData(), name.toUtf8().constData() );
}
else if ( type == ColorrampEntity )
{
if ( !colorRampNames().contains( name ) )
{
QgsDebugMsg( "Update requested for unavailable color ramp." );
return false;
}
std::unique_ptr< QgsColorRamp > ramp( colorRamp( name ) );
symEl = QgsSymbolLayerUtils::saveColorRamp( name, ramp.get(), doc );
if ( symEl.isNull() )
{
QgsDebugMsg( "Couldn't convert color ramp to valid XML!" );
return false;
}
symEl.save( stream, 4 );
query = sqlite3_mprintf( "UPDATE colorramp SET xml='%q' WHERE name='%q';",
xmlArray.constData(), name.toUtf8().constData() );
}
else
{
QgsDebugMsg( "Updating the unsupported StyleEntity" );
return false;
}
if ( !runEmptyQuery( query ) )
{
QgsDebugMsg( "Couldn't insert symbol into the database!" );
return false;
}
return true;
}