QGIS API Documentation 4.1.0-Master (467af3bbe65)
Loading...
Searching...
No Matches
qgsstyle.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsstyle.cpp
3 ---------------------
4 begin : November 2009
5 copyright : (C) 2009 by Martin Dobias
6 email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsstyle.h"
17
18#include <sqlite3.h>
19
20#include "qgs3dsymbolregistry.h"
21#include "qgsabstract3dsymbol.h"
23#include "qgsapplication.h"
24#include "qgscolorramp.h"
25#include "qgsfillsymbol.h"
26#include "qgsfillsymbollayer.h"
27#include "qgslegendpatchshape.h"
28#include "qgslinestring.h"
29#include "qgslinesymbol.h"
30#include "qgslinesymbollayer.h"
31#include "qgslogger.h"
32#include "qgsmarkersymbol.h"
34#include "qgsmaterialregistry.h"
35#include "qgspolygon.h"
36#include "qgsproject.h"
38#include "qgsreadwritecontext.h"
39#include "qgsruntimeprofiler.h"
40#include "qgssettings.h"
42#include "qgssqliteutils.h"
43#include "qgssymbol.h"
45
46#include <QByteArray>
47#include <QDomDocument>
48#include <QDomElement>
49#include <QDomNode>
50#include <QDomNodeList>
51#include <QFile>
52#include <QFileInfo>
53#include <QString>
54#include <QTextStream>
55
56#include "moc_qgsstyle.cpp"
57
58using namespace Qt::StringLiterals;
59
60#define STYLE_CURRENT_VERSION "2"
61
72
83
94
95QgsStyle *QgsStyle::sDefaultStyle = nullptr;
96
97QgsStyle::QgsStyle( QObject *parent )
98 : QObject( parent )
99{
100 auto simpleMarker = std::make_unique< QgsSimpleMarkerSymbolLayer >( Qgis::MarkerShape::Circle, 1.6, 0, Qgis::ScaleMethod::ScaleArea, QColor( 84, 176, 74 ), QColor( 61, 128, 53 ) );
101 simpleMarker->setStrokeWidth( 0.4 );
102 mPatchMarkerSymbol = std::make_unique< QgsMarkerSymbol >( QgsSymbolLayerList() << simpleMarker.release() );
103
104 auto simpleLine = std::make_unique< QgsSimpleLineSymbolLayer >( QColor( 84, 176, 74 ), 0.6 );
105 mPatchLineSymbol = std::make_unique< QgsLineSymbol >( QgsSymbolLayerList() << simpleLine.release() );
106
107 auto gradientFill = std::make_unique< QgsGradientFillSymbolLayer >( QColor( 66, 150, 63 ), QColor( 84, 176, 74 ) );
108 auto simpleOutline = std::make_unique< QgsSimpleLineSymbolLayer >( QColor( 56, 128, 54 ), 0.26 );
109 mPatchFillSymbol = std::make_unique< QgsFillSymbol >( QgsSymbolLayerList() << gradientFill.release() << simpleOutline.release() );
110}
111
113{
114 emit aboutToBeDestroyed();
115 clear();
116}
117
118void QgsStyle::setName( const QString &name )
119{
120 mName = name;
121}
122
123QString QgsStyle::name() const
124{
125 return mName;
126}
127
128bool QgsStyle::addEntity( const QString &name, const QgsStyleEntityInterface *entity, bool update )
129{
130 switch ( entity->type() )
131 {
132 case SymbolEntity:
133 if ( !static_cast< const QgsStyleSymbolEntity * >( entity )->symbol() )
134 return false;
135 return addSymbol( name, static_cast< const QgsStyleSymbolEntity * >( entity )->symbol()->clone(), update );
136
137 case ColorrampEntity:
138 if ( !static_cast< const QgsStyleColorRampEntity * >( entity )->ramp() )
139 return false;
140 return addColorRamp( name, static_cast< const QgsStyleColorRampEntity * >( entity )->ramp()->clone(), update );
141
142 case TextFormatEntity:
143 return addTextFormat( name, static_cast< const QgsStyleTextFormatEntity * >( entity )->format(), update );
144
146 return addLabelSettings( name, static_cast< const QgsStyleLabelSettingsEntity * >( entity )->settings(), update );
147
149 return addLegendPatchShape( name, static_cast< const QgsStyleLegendPatchShapeEntity * >( entity )->shape(), update );
150
151 case Symbol3DEntity:
152 return addSymbol3D( name, static_cast< const QgsStyleSymbol3DEntity * >( entity )->symbol()->clone(), update );
153
155 return addMaterialSettings( name, static_cast< const QgsStyleMaterialSettingsEntity * >( entity )->settings()->clone(), update );
156
157 case TagEntity:
158 case SmartgroupEntity:
159 break;
160 }
161 return false;
162}
163
164QgsStyle *QgsStyle::defaultStyle( bool initialize ) // static
165{
166 static QString sStyleFilename;
167 if ( !sDefaultStyle )
168 {
169 QgsScopedRuntimeProfile profile( tr( "Load default style database" ) );
170 sStyleFilename = QgsApplication::userStylePath();
171
172 // copy default style if user style doesn't exist
173 if ( !QFile::exists( sStyleFilename ) )
174 {
175 sDefaultStyle = new QgsStyle;
176 sDefaultStyle->createDatabase( sStyleFilename );
177 if ( QFile::exists( QgsApplication::defaultStylePath() ) )
178 {
179 if ( sDefaultStyle->importXml( QgsApplication::defaultStylePath() ) )
180 {
181 sDefaultStyle->createStyleMetadataTableIfNeeded();
182 }
183 }
184 }
185 else
186 {
187 sDefaultStyle = new QgsStyle;
188 sDefaultStyle->mInitialized = false;
189 if ( initialize )
190 {
191 sDefaultStyle->initializeDefaultStyle( sStyleFilename );
192 }
193 }
194 sDefaultStyle->setName( QObject::tr( "Default" ) );
195 }
196 else if ( initialize && !sDefaultStyle->mInitialized )
197 {
198 // lazy initialize
199 sDefaultStyle->initializeDefaultStyle( sStyleFilename );
200 }
201 return sDefaultStyle;
202}
203
204void QgsStyle::initializeDefaultStyle( const QString &filename )
205{
206 Q_ASSERT( this == sDefaultStyle );
207 if ( this != sDefaultStyle )
208 return;
209
210 if ( mInitialized )
211 return;
212
213 QgsScopedRuntimeProfile profile( tr( "Initialize default style database" ) );
214
215 mInitialized = true;
216 load( filename );
217 upgradeIfRequired();
218 emit initialized();
219}
220
222{
223 delete sDefaultStyle;
224 sDefaultStyle = nullptr;
225}
226
228{
229 qDeleteAll( mSymbols );
230 qDeleteAll( mColorRamps );
231 qDeleteAll( m3dSymbols );
232
233 mSymbols.clear();
234 mColorRamps.clear();
235 mTextFormats.clear();
236 m3dSymbols.clear();
237
238 mCachedTags.clear();
239 mCachedFavorites.clear();
240}
241
242bool QgsStyle::addSymbol( const QString &name, QgsSymbol *symbol, bool update )
243{
244 if ( !symbol || name.isEmpty() )
245 return false;
246
247 // delete previous symbol (if any)
248 auto it = mSymbols.constFind( name );
249 if ( it != mSymbols.constEnd() )
250 {
251 // TODO remove groups and tags?
252 delete it.value();
253 mSymbols.insert( name, symbol );
254 if ( update )
255 updateSymbol( SymbolEntity, name );
256 }
257 else
258 {
259 mSymbols.insert( name, symbol );
260 if ( update )
261 saveSymbol( name, symbol, false, QStringList() );
262 }
263
264 return true;
265}
266
267bool QgsStyle::saveSymbol( const QString &name, const QgsSymbol *symbol, bool favorite, const QStringList &tags )
268{
269 // TODO add support for groups
270 QDomDocument doc( u"dummy"_s );
271 QDomElement symEl = QgsSymbolLayerUtils::saveSymbol( name, symbol, doc, QgsReadWriteContext() );
272 if ( symEl.isNull() )
273 {
274 QgsDebugError( u"Couldn't convert symbol to valid XML!"_s );
275 return false;
276 }
277
278 QByteArray xmlArray;
279 QTextStream stream( &xmlArray );
280 symEl.save( stream, 4 );
281 QString query = qgs_sqlite3_mprintf( "INSERT INTO symbol VALUES (NULL, '%q', '%q', %d);", name.toUtf8().constData(), xmlArray.constData(), ( favorite ? 1 : 0 ) );
282
283 if ( !runEmptyQuery( query ) )
284 {
285 QgsDebugError( u"Couldn't insert symbol into the database!"_s );
286 return false;
287 }
288
289 mCachedFavorites[SymbolEntity].insert( name, favorite );
290
292
293 emit symbolSaved( name, symbol );
295
296 return true;
297}
298
299bool QgsStyle::removeSymbol( const QString &name )
300{
302}
303
304bool QgsStyle::renameEntity( QgsStyle::StyleEntity type, const QString &oldName, const QString &newName )
305{
306 switch ( type )
307 {
308 case SymbolEntity:
309 return renameSymbol( oldName, newName );
310
311 case ColorrampEntity:
312 return renameColorRamp( oldName, newName );
313
314 case TextFormatEntity:
315 return renameTextFormat( oldName, newName );
316
318 return renameLabelSettings( oldName, newName );
319
321 return renameLegendPatchShape( oldName, newName );
322
323 case Symbol3DEntity:
324 return renameSymbol3D( oldName, newName );
325
327 return renameMaterialSettings( oldName, newName );
328
329 case TagEntity:
330 case SmartgroupEntity:
331 return false;
332 }
333 return false;
334}
335
337{
338 const QgsSymbol *symbol = symbolRef( name );
339 if ( !symbol )
340 return nullptr;
341
342 QgsSymbol *newSymbol = symbol->clone();
344
345 return newSymbol;
346}
347
348const QgsSymbol *QgsStyle::symbolRef( const QString &name ) const
349{
350 return mSymbols.value( name );
351}
352
354{
355 return static_cast< int >( mSymbols.count() );
356}
357
358QStringList QgsStyle::symbolNames() const
359{
360 return mSymbols.keys();
361}
362
363bool QgsStyle::addColorRamp( const QString &name, QgsColorRamp *colorRamp, bool update )
364{
365 if ( !colorRamp || name.isEmpty() )
366 return false;
367
368 // delete previous color ramps (if any)
369 auto it = mColorRamps.constFind( name );
370 if ( it != mColorRamps.constEnd() )
371 {
372 // TODO remove groups and tags?
373 delete it.value();
374 mColorRamps.insert( name, colorRamp );
375 if ( update )
376 updateSymbol( ColorrampEntity, name );
377 }
378 else
379 {
380 mColorRamps.insert( name, colorRamp );
381 if ( update )
382 saveColorRamp( name, colorRamp, false, QStringList() );
383 }
384
385 return true;
386}
387
388bool QgsStyle::addTextFormat( const QString &name, const QgsTextFormat &format, bool update )
389{
390 // delete previous text format (if any)
391 auto it = mTextFormats.find( name );
392 if ( it != mTextFormats.end() )
393 {
394 // TODO remove groups and tags?
395 mTextFormats.erase( it );
396 mTextFormats.insert( name, format );
397 if ( update )
398 updateSymbol( TextFormatEntity, name );
399 }
400 else
401 {
402 mTextFormats.insert( name, format );
403 if ( update )
404 saveTextFormat( name, format, false, QStringList() );
405 }
406
407 return true;
408}
409
410bool QgsStyle::addLabelSettings( const QString &name, const QgsPalLayerSettings &settings, bool update )
411{
412 // delete previous label settings (if any)
413 auto it = mLabelSettings.find( name );
414 if ( it != mLabelSettings.end() )
415 {
416 // TODO remove groups and tags?
417 mLabelSettings.erase( it );
418 mLabelSettings.insert( name, settings );
419 if ( update )
420 updateSymbol( LabelSettingsEntity, name );
421 }
422 else
423 {
424 mLabelSettings.insert( name, settings );
425 if ( update )
426 saveLabelSettings( name, settings, false, QStringList() );
427 }
428
429 return true;
430}
431
432bool QgsStyle::addLegendPatchShape( const QString &name, const QgsLegendPatchShape &shape, bool update )
433{
434 // delete previous legend patch shape (if any)
435 auto it = mLegendPatchShapes.find( name );
436 if ( it != mLegendPatchShapes.end() )
437 {
438 // TODO remove groups and tags?
439 mLegendPatchShapes.erase( it );
440 mLegendPatchShapes.insert( name, shape );
441 if ( update )
442 updateSymbol( LegendPatchShapeEntity, name );
443 }
444 else
445 {
446 mLegendPatchShapes.insert( name, shape );
447 if ( update )
448 saveLegendPatchShape( name, shape, false, QStringList() );
449 }
450
451 return true;
452}
453
454bool QgsStyle::addSymbol3D( const QString &name, QgsAbstract3DSymbol *symbol, bool update )
455{
456 // delete previous symbol (if any)
457 auto it = m3dSymbols.constFind( name );
458 if ( it != m3dSymbols.constEnd() )
459 {
460 // TODO remove groups and tags?
461 delete it.value();
462 m3dSymbols.insert( name, symbol );
463 if ( update )
464 updateSymbol( Symbol3DEntity, name );
465 }
466 else
467 {
468 m3dSymbols.insert( name, symbol );
469 if ( update )
470 saveSymbol3D( name, symbol, false, QStringList() );
471 }
472
473 return true;
474}
475
476bool QgsStyle::addMaterialSettings( const QString &name, QgsAbstractMaterialSettings *settings, bool update )
477{
478 // delete previous settings (if any)
479 auto it = mMaterialSettings.constFind( name );
480 if ( it != mMaterialSettings.constEnd() )
481 {
482 // TODO remove groups and tags?
483 delete it.value();
484 mMaterialSettings.insert( name, settings );
485 if ( update )
486 updateSymbol( MaterialSettingsEntity, name );
487 }
488 else
489 {
490 mMaterialSettings.insert( name, settings );
491 if ( update )
492 saveMaterialSettings( name, settings, false, QStringList() );
493 }
494
495 return true;
496}
497
498bool QgsStyle::saveColorRamp( const QString &name, const QgsColorRamp *ramp, bool favorite, const QStringList &tags )
499{
500 // insert it into the database
501 QDomDocument doc( u"dummy"_s );
502 QDomElement rampEl = QgsSymbolLayerUtils::saveColorRamp( name, ramp, doc );
503
504 if ( rampEl.isNull() )
505 {
506 QgsDebugError( u"Couldn't convert color ramp to valid XML!"_s );
507 return false;
508 }
509
510 QByteArray xmlArray;
511 QTextStream stream( &xmlArray );
512 rampEl.save( stream, 4 );
513 QString query = qgs_sqlite3_mprintf( "INSERT INTO colorramp VALUES (NULL, '%q', '%q', %d);", name.toUtf8().constData(), xmlArray.constData(), ( favorite ? 1 : 0 ) );
514 if ( !runEmptyQuery( query ) )
515 {
516 QgsDebugError( u"Couldn't insert colorramp into the database!"_s );
517 return false;
518 }
519
520 mCachedFavorites[ColorrampEntity].insert( name, favorite );
521
523
524 emit rampAdded( name );
526
527 return true;
528}
529
530bool QgsStyle::removeColorRamp( const QString &name )
531{
533}
534
535QgsColorRamp *QgsStyle::colorRamp( const QString &name ) const
536{
537 const QgsColorRamp *ramp = colorRampRef( name );
538 return ramp ? ramp->clone() : nullptr;
539}
540
541const QgsColorRamp *QgsStyle::colorRampRef( const QString &name ) const
542{
543 return mColorRamps.value( name );
544}
545
547{
548 return static_cast< int >( mColorRamps.count() );
549}
550
551QStringList QgsStyle::colorRampNames() const
552{
553 return mColorRamps.keys();
554}
555
556void QgsStyle::handleDeferred3DSymbolCreation()
557{
558 for ( auto it = mDeferred3DsymbolElements.constBegin(); it != mDeferred3DsymbolElements.constEnd(); ++it )
559 {
560 const QString symbolType = it.value().attribute( u"type"_s );
561 std::unique_ptr< QgsAbstract3DSymbol > symbol( QgsApplication::symbol3DRegistry()->createSymbol( symbolType ) );
562 if ( symbol )
563 {
564 symbol->readXml( it.value(), QgsReadWriteContext() );
565 addSymbol3D( it.key(), symbol.release(), false );
566 emit entityAdded( Symbol3DEntity, it.key() );
567 }
568 else
569 {
570 QgsDebugError( "Cannot open 3d symbol " + it.key() );
571 continue;
572 }
573 }
574 mDeferred3DsymbolElements.clear();
575}
576
577bool QgsStyle::openDatabase( const QString &filename )
578{
579 int rc = mCurrentDB.open( filename );
580 if ( rc )
581 {
582 mErrorString = u"Couldn't open the style database: %1"_s.arg( mCurrentDB.errorMessage() );
583 return false;
584 }
585
586 return true;
587}
588
589bool QgsStyle::createDatabase( const QString &filename )
590{
591 mErrorString.clear();
592 if ( !openDatabase( filename ) )
593 {
594 mErrorString = u"Unable to create database"_s;
595 QgsDebugError( mErrorString );
596 return false;
597 }
598
599 createTables();
600
601 return true;
602}
603
605{
606 mErrorString.clear();
607 if ( !openDatabase( u":memory:"_s ) )
608 {
609 mErrorString = u"Unable to create temporary memory database"_s;
610 QgsDebugError( mErrorString );
611 return false;
612 }
613
614 createTables();
615
616 return true;
617}
618
620{
621 QString query = qgs_sqlite3_mprintf(
622 "CREATE TABLE symbol("
623 "id INTEGER PRIMARY KEY,"
624 "name TEXT UNIQUE,"
625 "xml TEXT,"
626 "favorite INTEGER);"
627 "CREATE TABLE colorramp("
628 "id INTEGER PRIMARY KEY,"
629 "name TEXT UNIQUE,"
630 "xml TEXT,"
631 "favorite INTEGER);"
632 "CREATE TABLE textformat("
633 "id INTEGER PRIMARY KEY,"
634 "name TEXT UNIQUE,"
635 "xml TEXT,"
636 "favorite INTEGER);"
637 "CREATE TABLE labelsettings("
638 "id INTEGER PRIMARY KEY,"
639 "name TEXT UNIQUE,"
640 "xml TEXT,"
641 "favorite INTEGER);"
642 "CREATE TABLE legendpatchshapes("
643 "id INTEGER PRIMARY KEY,"
644 "name TEXT UNIQUE,"
645 "xml TEXT,"
646 "favorite INTEGER);"
647 "CREATE TABLE symbol3d("
648 "id INTEGER PRIMARY KEY,"
649 "name TEXT UNIQUE,"
650 "xml TEXT,"
651 "favorite INTEGER);"
652 "CREATE TABLE materialsettings("
653 "id INTEGER PRIMARY KEY,"
654 "name TEXT UNIQUE,"
655 "xml TEXT,"
656 "favorite INTEGER);"
657 "CREATE TABLE tag("
658 "id INTEGER PRIMARY KEY,"
659 "name TEXT);"
660 "CREATE TABLE tagmap("
661 "tag_id INTEGER NOT NULL,"
662 "symbol_id INTEGER);"
663 "CREATE TABLE ctagmap("
664 "tag_id INTEGER NOT NULL,"
665 "colorramp_id INTEGER);"
666 "CREATE TABLE tftagmap("
667 "tag_id INTEGER NOT NULL,"
668 "textformat_id INTEGER);"
669 "CREATE TABLE lstagmap("
670 "tag_id INTEGER NOT NULL,"
671 "labelsettings_id INTEGER);"
672 "CREATE TABLE lpstagmap("
673 "tag_id INTEGER NOT NULL,"
674 "legendpatchshape_id INTEGER);"
675 "CREATE TABLE symbol3dtagmap("
676 "tag_id INTEGER NOT NULL,"
677 "symbol3d_id INTEGER);"
678 "CREATE TABLE materialsettingstagmap("
679 "tag_id INTEGER NOT NULL,"
680 "materialsettings_id INTEGER);"
681 "CREATE TABLE smartgroup("
682 "id INTEGER PRIMARY KEY,"
683 "name TEXT,"
684 "xml TEXT);"
685 );
686 runEmptyQuery( query );
687}
688
689bool QgsStyle::load( const QString &filename )
690{
691 mErrorString.clear();
692
693 // Open the sqlite database
694 if ( !openDatabase( filename ) )
695 {
696 mErrorString = u"Unable to open database file specified"_s;
697 QgsDebugError( mErrorString );
698 return false;
699 }
700
701 // make sure text format table exists
702 QString query = qgs_sqlite3_mprintf( "SELECT name FROM sqlite_master WHERE name='textformat'" );
704 int rc;
705 statement = mCurrentDB.prepare( query, rc );
706 if ( rc != SQLITE_OK || sqlite3_step( statement.get() ) != SQLITE_ROW )
707 {
708 query = qgs_sqlite3_mprintf(
709 "CREATE TABLE textformat("
710 "id INTEGER PRIMARY KEY,"
711 "name TEXT UNIQUE,"
712 "xml TEXT,"
713 "favorite INTEGER);"
714 "CREATE TABLE tftagmap("
715 "tag_id INTEGER NOT NULL,"
716 "textformat_id INTEGER);"
717 );
718 runEmptyQuery( query );
719 }
720 // make sure label settings table exists
721 query = qgs_sqlite3_mprintf( "SELECT name FROM sqlite_master WHERE name='labelsettings'" );
722 statement = mCurrentDB.prepare( query, rc );
723 if ( rc != SQLITE_OK || sqlite3_step( statement.get() ) != SQLITE_ROW )
724 {
725 query = qgs_sqlite3_mprintf(
726 "CREATE TABLE labelsettings("
727 "id INTEGER PRIMARY KEY,"
728 "name TEXT UNIQUE,"
729 "xml TEXT,"
730 "favorite INTEGER);"
731 "CREATE TABLE lstagmap("
732 "tag_id INTEGER NOT NULL,"
733 "labelsettings_id INTEGER);"
734 );
735 runEmptyQuery( query );
736 }
737 // make sure legend patch shape table exists
738 query = qgs_sqlite3_mprintf( "SELECT name FROM sqlite_master WHERE name='legendpatchshapes'" );
739 statement = mCurrentDB.prepare( query, rc );
740 if ( rc != SQLITE_OK || sqlite3_step( statement.get() ) != SQLITE_ROW )
741 {
742 query = qgs_sqlite3_mprintf(
743 "CREATE TABLE legendpatchshapes("
744 "id INTEGER PRIMARY KEY,"
745 "name TEXT UNIQUE,"
746 "xml TEXT,"
747 "favorite INTEGER);"
748 "CREATE TABLE lpstagmap("
749 "tag_id INTEGER NOT NULL,"
750 "legendpatchshape_id INTEGER);"
751 );
752 runEmptyQuery( query );
753 }
754 // make sure 3d symbol table exists
755 query = qgs_sqlite3_mprintf( "SELECT name FROM sqlite_master WHERE name='symbol3d'" );
756 statement = mCurrentDB.prepare( query, rc );
757 if ( rc != SQLITE_OK || sqlite3_step( statement.get() ) != SQLITE_ROW )
758 {
759 query = qgs_sqlite3_mprintf(
760 "CREATE TABLE symbol3d("
761 "id INTEGER PRIMARY KEY,"
762 "name TEXT UNIQUE,"
763 "xml TEXT,"
764 "favorite INTEGER);"
765 "CREATE TABLE symbol3dtagmap("
766 "tag_id INTEGER NOT NULL,"
767 "symbol3d_id INTEGER);"
768 );
769 runEmptyQuery( query );
770 }
771
772 // make sure 3D material settings table exists
773 query = qgs_sqlite3_mprintf( "SELECT name FROM sqlite_master WHERE name='materialsettings'" );
774 statement = mCurrentDB.prepare( query, rc );
775 if ( rc != SQLITE_OK || sqlite3_step( statement.get() ) != SQLITE_ROW )
776 {
777 query = qgs_sqlite3_mprintf(
778 "CREATE TABLE materialsettings("
779 "id INTEGER PRIMARY KEY,"
780 "name TEXT UNIQUE,"
781 "xml TEXT,"
782 "favorite INTEGER);"
783 "CREATE TABLE materialsettingstagmap("
784 "tag_id INTEGER NOT NULL,"
785 "materialsettings_id INTEGER);"
786 );
787 runEmptyQuery( query );
788 }
789
790 // Make sure there are no Null fields in parenting symbols and groups
791 query = qgs_sqlite3_mprintf(
792 "UPDATE symbol SET favorite=0 WHERE favorite IS NULL;"
793 "UPDATE colorramp SET favorite=0 WHERE favorite IS NULL;"
794 "UPDATE textformat SET favorite=0 WHERE favorite IS NULL;"
795 "UPDATE labelsettings SET favorite=0 WHERE favorite IS NULL;"
796 "UPDATE legendpatchshapes SET favorite=0 WHERE favorite IS NULL;"
797 "UPDATE symbol3d SET favorite=0 WHERE favorite IS NULL;"
798 "UPDATE materialsettings SET favorite=0 WHERE favorite IS NULL;"
799 );
800 runEmptyQuery( query );
801
802 {
803 QgsScopedRuntimeProfile profile( tr( "Load symbols" ) );
804 // First create all the main symbols
805 query = qgs_sqlite3_mprintf( "SELECT * FROM symbol" );
806 statement = mCurrentDB.prepare( query, rc );
807
808 while ( rc == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
809 {
810 QDomDocument doc;
811 QString symbolName = statement.columnAsText( static_cast< int >( SymbolTableColumn::Name ) );
812 QgsScopedRuntimeProfile profile( symbolName );
813 QString xmlstring = statement.columnAsText( static_cast< int >( SymbolTableColumn::XML ) );
814 if ( !doc.setContent( xmlstring ) )
815 {
816 QgsDebugError( "Cannot open symbol " + symbolName );
817 continue;
818 }
819
820 QDomElement symElement = doc.documentElement();
821 std::unique_ptr< QgsSymbol > symbol = QgsSymbolLayerUtils::loadSymbol( symElement, QgsReadWriteContext() );
822 if ( symbol )
823 mSymbols.insert( symbolName, symbol.release() );
824 }
825 }
826
827 {
828 QgsScopedRuntimeProfile profile( tr( "Load color ramps" ) );
829 query = qgs_sqlite3_mprintf( "SELECT * FROM colorramp" );
830 statement = mCurrentDB.prepare( query, rc );
831 while ( rc == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
832 {
833 QDomDocument doc;
834 const QString rampName = statement.columnAsText( static_cast< int >( ColorRampTableColumn::Name ) );
835 QgsScopedRuntimeProfile profile( rampName );
836 QString xmlstring = statement.columnAsText( static_cast< int >( ColorRampTableColumn::XML ) );
837 if ( !doc.setContent( xmlstring ) )
838 {
839 QgsDebugError( "Cannot open symbol " + rampName );
840 continue;
841 }
842 QDomElement rampElement = doc.documentElement();
843 std::unique_ptr< QgsColorRamp > ramp = QgsSymbolLayerUtils::loadColorRamp( rampElement );
844 if ( ramp )
845 mColorRamps.insert( rampName, ramp.release() );
846 }
847 }
848
849 {
850 QgsScopedRuntimeProfile profile( tr( "Load text formats" ) );
851 query = qgs_sqlite3_mprintf( "SELECT * FROM textformat" );
852 statement = mCurrentDB.prepare( query, rc );
853 while ( rc == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
854 {
855 QDomDocument doc;
856 const QString formatName = statement.columnAsText( static_cast< int >( TextFormatTableColumn::Name ) );
857 QgsScopedRuntimeProfile profile( formatName );
858 const QString xmlstring = statement.columnAsText( static_cast< int >( TextFormatTableColumn::XML ) );
859 if ( !doc.setContent( xmlstring ) )
860 {
861 QgsDebugError( "Cannot open text format " + formatName );
862 continue;
863 }
864 QDomElement formatElement = doc.documentElement();
865 QgsTextFormat format;
866 format.readXml( formatElement, QgsReadWriteContext() );
867 mTextFormats.insert( formatName, format );
868 }
869 }
870
871 {
872 QgsScopedRuntimeProfile profile( tr( "Load label settings" ) );
873 query = qgs_sqlite3_mprintf( "SELECT * FROM labelsettings" );
874 statement = mCurrentDB.prepare( query, rc );
875 while ( rc == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
876 {
877 QDomDocument doc;
878 const QString settingsName = statement.columnAsText( static_cast< int >( LabelSettingsTableColumn::Name ) );
879 QgsScopedRuntimeProfile profile( settingsName );
880 const QString xmlstring = statement.columnAsText( static_cast< int >( LabelSettingsTableColumn::XML ) );
881 if ( !doc.setContent( xmlstring ) )
882 {
883 QgsDebugError( "Cannot open label settings " + settingsName );
884 continue;
885 }
886 QDomElement settingsElement = doc.documentElement();
887 QgsPalLayerSettings settings;
888 settings.readXml( settingsElement, QgsReadWriteContext() );
889 mLabelSettings.insert( settingsName, settings );
890 }
891 }
892
893 {
894 QgsScopedRuntimeProfile profile( tr( "Load legend patch shapes" ) );
895 query = qgs_sqlite3_mprintf( "SELECT * FROM legendpatchshapes" );
896 statement = mCurrentDB.prepare( query, rc );
897 while ( rc == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
898 {
899 QDomDocument doc;
900 const QString settingsName = statement.columnAsText( LegendPatchTableName );
901 QgsScopedRuntimeProfile profile( settingsName );
902 const QString xmlstring = statement.columnAsText( LegendPatchTableXML );
903 if ( !doc.setContent( xmlstring ) )
904 {
905 QgsDebugError( "Cannot open legend patch shape " + settingsName );
906 continue;
907 }
908 QDomElement settingsElement = doc.documentElement();
910 shape.readXml( settingsElement, QgsReadWriteContext() );
911 mLegendPatchShapes.insert( settingsName, shape );
912 }
913 }
914
915 {
916 QgsScopedRuntimeProfile profile( tr( "Load 3D symbols" ) );
917 query = qgs_sqlite3_mprintf( "SELECT * FROM symbol3d" );
918 statement = mCurrentDB.prepare( query, rc );
919
920 const bool registry3dPopulated = !QgsApplication::symbol3DRegistry()->symbolTypes().empty();
921
922 while ( rc == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
923 {
924 QDomDocument doc;
925 const QString settingsName = statement.columnAsText( Symbol3DTableName );
926 QgsScopedRuntimeProfile profile( settingsName );
927 const QString xmlstring = statement.columnAsText( Symbol3DTableXML );
928 if ( !doc.setContent( xmlstring ) )
929 {
930 QgsDebugError( "Cannot open 3d symbol " + settingsName );
931 continue;
932 }
933 QDomElement settingsElement = doc.documentElement();
934
935 if ( !registry3dPopulated )
936 {
937 mDeferred3DsymbolElements.insert( settingsName, settingsElement );
938 }
939 else
940 {
941 const QString symbolType = settingsElement.attribute( u"type"_s );
942 std::unique_ptr< QgsAbstract3DSymbol > symbol( QgsApplication::symbol3DRegistry()->createSymbol( symbolType ) );
943 if ( symbol )
944 {
945 symbol->readXml( settingsElement, QgsReadWriteContext() );
946 m3dSymbols.insert( settingsName, symbol.release() );
947 }
948 else
949 {
950 QgsDebugError( "Cannot open 3d symbol " + settingsName );
951 continue;
952 }
953 }
954 }
955 }
956
957 {
958 QgsScopedRuntimeProfile profile( tr( "Load material settings" ) );
959 query = qgs_sqlite3_mprintf( "SELECT * FROM materialsettings" );
960 statement = mCurrentDB.prepare( query, rc );
961
962 while ( rc == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
963 {
964 QDomDocument doc;
965 const QString settingsName = statement.columnAsText( MaterialSettingsTableName );
966 QgsScopedRuntimeProfile profile( settingsName );
967 const QString xmlstring = statement.columnAsText( MaterialSettingsTableXML );
968 if ( !doc.setContent( xmlstring ) )
969 {
970 QgsDebugError( "Cannot open material settings " + settingsName );
971 continue;
972 }
973 QDomElement settingsElement = doc.documentElement();
974
975 const QString materialType = settingsElement.attribute( u"type"_s );
976 std::unique_ptr< QgsAbstractMaterialSettings > settings = QgsApplication::materialRegistry()->createMaterialSettings( materialType );
977 if ( settings )
978 {
979 settings->readXml( settingsElement, QgsReadWriteContext() );
980 mMaterialSettings.insert( settingsName, settings.release() );
981 }
982 else
983 {
984 QgsDebugError( "Cannot open material settings " + settingsName );
985 continue;
986 }
987 }
988 }
989
990 mFileName = filename;
991 createStyleMetadataTableIfNeeded();
992 return true;
993}
994
995bool QgsStyle::save( const QString &filename )
996{
997 mErrorString.clear();
998
999 if ( !filename.isEmpty() )
1000 mFileName = filename;
1001
1002 return true;
1003}
1004
1005void QgsStyle::setFileName( const QString &filename )
1006{
1007 mFileName = filename;
1008}
1009
1010bool QgsStyle::renameSymbol( const QString &oldName, const QString &newName )
1011{
1012 if ( mSymbols.contains( newName ) )
1013 {
1014 QgsDebugError( u"Symbol of new name already exists"_s );
1015 return false;
1016 }
1017
1018 QgsSymbol *symbol = mSymbols.take( oldName );
1019 if ( !symbol )
1020 return false;
1021
1022 mSymbols.insert( newName, symbol );
1023
1024 if ( !mCurrentDB )
1025 {
1026 QgsDebugError( u"Sorry! Cannot open database to tag."_s );
1027 return false;
1028 }
1029
1030 int symbolid = symbolId( oldName );
1031 if ( !symbolid )
1032 {
1033 QgsDebugError( u"No such symbol for tagging in database: "_s + oldName );
1034 return false;
1035 }
1036
1037 mCachedTags[SymbolEntity].remove( oldName );
1038 mCachedFavorites[SymbolEntity].remove( oldName );
1039
1040 const bool result = rename( SymbolEntity, symbolid, newName );
1041 if ( result )
1042 {
1043 emit symbolRenamed( oldName, newName );
1044 emit entityRenamed( SymbolEntity, oldName, newName );
1045 }
1046
1047 return result;
1048}
1049
1050bool QgsStyle::renameColorRamp( const QString &oldName, const QString &newName )
1051{
1052 if ( mColorRamps.contains( newName ) )
1053 {
1054 QgsDebugError( u"Color ramp of new name already exists."_s );
1055 return false;
1056 }
1057
1058 QgsColorRamp *ramp = mColorRamps.take( oldName );
1059 if ( !ramp )
1060 return false;
1061
1062 mColorRamps.insert( newName, ramp );
1063 mCachedTags[ColorrampEntity].remove( oldName );
1064 mCachedFavorites[ColorrampEntity].remove( oldName );
1065
1066 int rampid = 0;
1068 QString query = qgs_sqlite3_mprintf( "SELECT id FROM colorramp WHERE name='%q'", oldName.toUtf8().constData() );
1069 int nErr;
1070 statement = mCurrentDB.prepare( query, nErr );
1071 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1072 {
1073 rampid = sqlite3_column_int( statement.get(), 0 );
1074 }
1075 const bool result = rename( ColorrampEntity, rampid, newName );
1076 if ( result )
1077 {
1078 emit rampRenamed( oldName, newName );
1079 emit entityRenamed( ColorrampEntity, oldName, newName );
1080 }
1081
1082 return result;
1083}
1084
1085bool QgsStyle::saveTextFormat( const QString &name, const QgsTextFormat &format, bool favorite, const QStringList &tags )
1086{
1087 // insert it into the database
1088 QDomDocument doc( u"dummy"_s );
1089 QDomElement formatElem = format.writeXml( doc, QgsReadWriteContext() );
1090
1091 if ( formatElem.isNull() )
1092 {
1093 QgsDebugError( u"Couldn't convert text format to valid XML!"_s );
1094 return false;
1095 }
1096
1097 QByteArray xmlArray;
1098 QTextStream stream( &xmlArray );
1099 formatElem.save( stream, 4 );
1100 QString query = qgs_sqlite3_mprintf( "INSERT INTO textformat VALUES (NULL, '%q', '%q', %d);", name.toUtf8().constData(), xmlArray.constData(), ( favorite ? 1 : 0 ) );
1101 if ( !runEmptyQuery( query ) )
1102 {
1103 QgsDebugError( u"Couldn't insert text format into the database!"_s );
1104 return false;
1105 }
1106
1107 mCachedFavorites[TextFormatEntity].insert( name, favorite );
1108
1110
1111 emit textFormatAdded( name );
1113
1114 return true;
1115}
1116
1117bool QgsStyle::removeTextFormat( const QString &name )
1118{
1120}
1121
1122bool QgsStyle::renameTextFormat( const QString &oldName, const QString &newName )
1123{
1124 if ( mTextFormats.contains( newName ) )
1125 {
1126 QgsDebugError( u"Text format of new name already exists."_s );
1127 return false;
1128 }
1129
1130 if ( !mTextFormats.contains( oldName ) )
1131 return false;
1132 QgsTextFormat format = mTextFormats.take( oldName );
1133
1134 mTextFormats.insert( newName, format );
1135 mCachedTags[TextFormatEntity].remove( oldName );
1136 mCachedFavorites[TextFormatEntity].remove( oldName );
1137
1138 int textFormatId = 0;
1140 QString query = qgs_sqlite3_mprintf( "SELECT id FROM textformat WHERE name='%q'", oldName.toUtf8().constData() );
1141 int nErr;
1142 statement = mCurrentDB.prepare( query, nErr );
1143 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1144 {
1145 textFormatId = sqlite3_column_int( statement.get(), 0 );
1146 }
1147 const bool result = rename( TextFormatEntity, textFormatId, newName );
1148 if ( result )
1149 {
1150 emit textFormatRenamed( oldName, newName );
1151 emit entityRenamed( TextFormatEntity, oldName, newName );
1152 }
1153
1154 return result;
1155}
1156
1157bool QgsStyle::saveLabelSettings( const QString &name, const QgsPalLayerSettings &settings, bool favorite, const QStringList &tags )
1158{
1159 // insert it into the database
1160 QDomDocument doc( u"dummy"_s );
1161 QDomElement settingsElem = settings.writeXml( doc, QgsReadWriteContext() );
1162
1163 if ( settingsElem.isNull() )
1164 {
1165 QgsDebugError( u"Couldn't convert label settings to valid XML!"_s );
1166 return false;
1167 }
1168
1169 QByteArray xmlArray;
1170 QTextStream stream( &xmlArray );
1171 settingsElem.save( stream, 4 );
1172 QString query = qgs_sqlite3_mprintf( "INSERT INTO labelsettings VALUES (NULL, '%q', '%q', %d);", name.toUtf8().constData(), xmlArray.constData(), ( favorite ? 1 : 0 ) );
1173 if ( !runEmptyQuery( query ) )
1174 {
1175 QgsDebugError( u"Couldn't insert label settings into the database!"_s );
1176 return false;
1177 }
1178
1179 mCachedFavorites[LabelSettingsEntity].insert( name, favorite );
1180
1182
1183 emit labelSettingsAdded( name );
1185
1186 return true;
1187}
1188
1190{
1192}
1193
1194bool QgsStyle::renameLabelSettings( const QString &oldName, const QString &newName )
1195{
1196 if ( mLabelSettings.contains( newName ) )
1197 {
1198 QgsDebugError( u"Label settings of new name already exists."_s );
1199 return false;
1200 }
1201
1202 if ( !mLabelSettings.contains( oldName ) )
1203 return false;
1204 QgsPalLayerSettings settings = mLabelSettings.take( oldName );
1205
1206 mLabelSettings.insert( newName, settings );
1207 mCachedTags[LabelSettingsEntity].remove( oldName );
1208 mCachedFavorites[LabelSettingsEntity].remove( oldName );
1209
1210 int labelSettingsId = 0;
1212 QString query = qgs_sqlite3_mprintf( "SELECT id FROM labelsettings WHERE name='%q'", oldName.toUtf8().constData() );
1213 int nErr;
1214 statement = mCurrentDB.prepare( query, nErr );
1215 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1216 {
1217 labelSettingsId = sqlite3_column_int( statement.get(), 0 );
1218 }
1219 const bool result = rename( LabelSettingsEntity, labelSettingsId, newName );
1220 if ( result )
1221 {
1222 emit labelSettingsRenamed( oldName, newName );
1223 emit entityRenamed( LabelSettingsEntity, oldName, newName );
1224 }
1225
1226 return result;
1227}
1228
1229bool QgsStyle::saveLegendPatchShape( const QString &name, const QgsLegendPatchShape &shape, bool favorite, const QStringList &tags )
1230{
1231 // insert it into the database
1232 QDomDocument doc( u"dummy"_s );
1233 QDomElement shapeElem = doc.createElement( u"shape"_s );
1234 shape.writeXml( shapeElem, doc, QgsReadWriteContext() );
1235
1236 QByteArray xmlArray;
1237 QTextStream stream( &xmlArray );
1238 shapeElem.save( stream, 4 );
1239 QString query = qgs_sqlite3_mprintf( "INSERT INTO legendpatchshapes VALUES (NULL, '%q', '%q', %d);", name.toUtf8().constData(), xmlArray.constData(), ( favorite ? 1 : 0 ) );
1240 if ( !runEmptyQuery( query ) )
1241 {
1242 QgsDebugError( u"Couldn't insert legend patch shape into the database!"_s );
1243 return false;
1244 }
1245
1246 mCachedFavorites[LegendPatchShapeEntity].insert( name, favorite );
1247
1249
1251
1252 return true;
1253}
1254
1255bool QgsStyle::renameLegendPatchShape( const QString &oldName, const QString &newName )
1256{
1257 if ( mLegendPatchShapes.contains( newName ) )
1258 {
1259 QgsDebugError( u"Legend patch shape of new name already exists."_s );
1260 return false;
1261 }
1262
1263 if ( !mLegendPatchShapes.contains( oldName ) )
1264 return false;
1265 QgsLegendPatchShape shape = mLegendPatchShapes.take( oldName );
1266
1267 mLegendPatchShapes.insert( newName, shape );
1268 mCachedTags[LegendPatchShapeEntity].remove( oldName );
1269 mCachedFavorites[LegendPatchShapeEntity].remove( oldName );
1270
1271 int labelSettingsId = 0;
1273 QString query = qgs_sqlite3_mprintf( "SELECT id FROM legendpatchshapes WHERE name='%q'", oldName.toUtf8().constData() );
1274 int nErr;
1275 statement = mCurrentDB.prepare( query, nErr );
1276 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1277 {
1278 labelSettingsId = sqlite3_column_int( statement.get(), 0 );
1279 }
1280 const bool result = rename( LegendPatchShapeEntity, labelSettingsId, newName );
1281 if ( result )
1282 {
1283 emit entityRenamed( LegendPatchShapeEntity, oldName, newName );
1284 }
1285
1286 return result;
1287}
1288
1290{
1291 if ( type == Qgis::SymbolType::Hybrid )
1292 return QgsLegendPatchShape();
1293
1294 auto it = mDefaultPatchCache[static_cast< int >( type )].constFind( size );
1295 if ( it != mDefaultPatchCache[static_cast< int >( type )].constEnd() )
1296 return it.value();
1297
1298 QgsGeometry geom;
1299 switch ( type )
1300 {
1302 geom = QgsGeometry( std::make_unique< QgsPoint >( static_cast< int >( size.width() ) / 2, static_cast< int >( size.height() ) / 2 ) );
1303 break;
1304
1306 {
1307 // we're adding 0.5 to get rid of blurred preview:
1308 // drawing antialiased lines of width 1 at (x,0)-(x,100) creates 2px line
1309 double y = static_cast< int >( size.height() ) / 2 + 0.5;
1310 geom = QgsGeometry( std::make_unique< QgsLineString >( ( QVector< double >() << 0 << size.width() ), ( QVector< double >() << y << y ) ) );
1311 break;
1312 }
1313
1315 {
1316 geom = QgsGeometry(
1317 std::make_unique< QgsPolygon >( new QgsLineString(
1318 QVector< double >() << 0 << static_cast< int >( size.width() ) << static_cast< int >( size.width() ) << 0 << 0,
1319 QVector< double >() << static_cast< int >( size.height() ) << static_cast< int >( size.height() ) << 0 << 0 << static_cast< int >( size.height() )
1320 ) )
1321 );
1322 break;
1323 }
1324
1326 break;
1327 }
1328
1329 QgsLegendPatchShape res = QgsLegendPatchShape( type, geom, false );
1330 mDefaultPatchCache[static_cast< int >( type )][size] = res;
1331 return res;
1332}
1333
1334QList<QList<QPolygonF> > QgsStyle::defaultPatchAsQPolygonF( Qgis::SymbolType type, QSizeF size ) const
1335{
1336 if ( type == Qgis::SymbolType::Hybrid )
1337 return QList<QList<QPolygonF> >();
1338
1339 auto it = mDefaultPatchQPolygonFCache[static_cast< int >( type )].constFind( size );
1340 if ( it != mDefaultPatchQPolygonFCache[static_cast< int >( type )].constEnd() )
1341 return it.value();
1342
1343 QList<QList<QPolygonF> > res = defaultPatch( type, size ).toQPolygonF( type, size );
1344 mDefaultPatchQPolygonFCache[static_cast< int >( type )][size] = res;
1345 return res;
1346}
1347
1352
1354{
1355 if ( project )
1356 {
1358 if ( defaultTextFormat.isValid() )
1359 {
1360 return defaultTextFormat;
1361 }
1362 }
1363
1364 return QgsStyle::defaultStyle()->defaultTextFormat( context );
1365}
1366
1367bool QgsStyle::saveSymbol3D( const QString &name, QgsAbstract3DSymbol *symbol, bool favorite, const QStringList &tags )
1368{
1369 // insert it into the database
1370 QDomDocument doc( u"dummy"_s );
1371 QDomElement elem = doc.createElement( u"symbol"_s );
1372 elem.setAttribute( u"type"_s, symbol->type() );
1373 symbol->writeXml( elem, QgsReadWriteContext() );
1374
1375 QByteArray xmlArray;
1376 QTextStream stream( &xmlArray );
1377 elem.save( stream, 4 );
1378 QString query = qgs_sqlite3_mprintf( "INSERT INTO symbol3d VALUES (NULL, '%q', '%q', %d);", name.toUtf8().constData(), xmlArray.constData(), ( favorite ? 1 : 0 ) );
1379 if ( !runEmptyQuery( query ) )
1380 {
1381 QgsDebugError( u"Couldn't insert 3d symbol into the database!"_s );
1382 return false;
1383 }
1384
1385 mCachedFavorites[Symbol3DEntity].insert( name, favorite );
1386
1388
1390
1391 return true;
1392}
1393
1394bool QgsStyle::renameSymbol3D( const QString &oldName, const QString &newName )
1395{
1396 if ( m3dSymbols.contains( newName ) )
1397 {
1398 QgsDebugError( u"3d symbol of new name already exists."_s );
1399 return false;
1400 }
1401
1402 if ( !m3dSymbols.contains( oldName ) )
1403 return false;
1404 QgsAbstract3DSymbol *symbol = m3dSymbols.take( oldName );
1405
1406 m3dSymbols.insert( newName, symbol );
1407 mCachedTags[Symbol3DEntity].remove( oldName );
1408 mCachedFavorites[Symbol3DEntity].remove( oldName );
1409
1410 int labelSettingsId = 0;
1412 QString query = qgs_sqlite3_mprintf( "SELECT id FROM symbol3d WHERE name='%q'", oldName.toUtf8().constData() );
1413 int nErr;
1414 statement = mCurrentDB.prepare( query, nErr );
1415 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1416 {
1417 labelSettingsId = sqlite3_column_int( statement.get(), 0 );
1418 }
1419 const bool result = rename( Symbol3DEntity, labelSettingsId, newName );
1420 if ( result )
1421 {
1422 emit entityRenamed( Symbol3DEntity, oldName, newName );
1423 }
1424
1425 return result;
1426}
1427
1428QStringList QgsStyle::symbol3DNames() const
1429{
1430 return m3dSymbols.keys();
1431}
1432
1433bool QgsStyle::saveMaterialSettings( const QString &name, QgsAbstractMaterialSettings *settings, bool favorite, const QStringList &tags )
1434{
1435 // insert it into the database
1436 QDomDocument doc( u"dummy"_s );
1437 QDomElement elem = doc.createElement( u"settings"_s );
1438 elem.setAttribute( u"type"_s, settings->type() );
1439 settings->writeXml( elem, QgsReadWriteContext() );
1440
1441 QByteArray xmlArray;
1442 QTextStream stream( &xmlArray );
1443 elem.save( stream, 4 );
1444 QString query = qgs_sqlite3_mprintf( "INSERT INTO materialsettings VALUES (NULL, '%q', '%q', %d);", name.toUtf8().constData(), xmlArray.constData(), ( favorite ? 1 : 0 ) );
1445 if ( !runEmptyQuery( query ) )
1446 {
1447 QgsDebugError( u"Couldn't insert material settings into the database!"_s );
1448 return false;
1449 }
1450
1451 mCachedFavorites[MaterialSettingsEntity].insert( name, favorite );
1452
1454
1456
1457 return true;
1458}
1459
1460bool QgsStyle::renameMaterialSettings( const QString &oldName, const QString &newName )
1461{
1462 if ( mMaterialSettings.contains( newName ) )
1463 {
1464 QgsDebugError( u"material settings of new name already exists."_s );
1465 return false;
1466 }
1467
1468 if ( !mMaterialSettings.contains( oldName ) )
1469 return false;
1470 std::unique_ptr< QgsAbstractMaterialSettings > settings( mMaterialSettings.take( oldName ) );
1471
1472 mMaterialSettings.insert( newName, settings.release() );
1473 mCachedTags[MaterialSettingsEntity].remove( oldName );
1474 mCachedFavorites[MaterialSettingsEntity].remove( oldName );
1475
1476 int materialSettingsId = 0;
1478 QString query = qgs_sqlite3_mprintf( "SELECT id FROM materialsettings WHERE name='%q'", oldName.toUtf8().constData() );
1479 int nErr;
1480 statement = mCurrentDB.prepare( query, nErr );
1481 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1482 {
1483 materialSettingsId = sqlite3_column_int( statement.get(), 0 );
1484 }
1485 const bool result = rename( MaterialSettingsEntity, materialSettingsId, newName );
1486 if ( result )
1487 {
1488 emit entityRenamed( MaterialSettingsEntity, oldName, newName );
1489 }
1490
1491 return result;
1492}
1493
1495{
1496 return mMaterialSettings.keys();
1497}
1498
1500{
1501 return static_cast< int >( mMaterialSettings.size() );
1502}
1503
1504std::unique_ptr< QgsAbstractMaterialSettings > QgsStyle::materialSettings( const QString &name ) const
1505{
1506 auto it = mMaterialSettings.constFind( name );
1507 if ( it != mMaterialSettings.constEnd() )
1508 return std::unique_ptr< QgsAbstractMaterialSettings >( it.value()->clone() );
1509 return nullptr;
1510}
1511
1513{
1514 if ( !mCurrentDB )
1515 {
1516 QgsDebugError( u"Cannot Open database for getting favorite symbols"_s );
1517 return QStringList();
1518 }
1519
1520 QString query;
1521 switch ( type )
1522 {
1523 case TagEntity:
1524 case SmartgroupEntity:
1525 QgsDebugError( u"No such style entity"_s );
1526 return QStringList();
1527
1528 default:
1529 query = qgs_sqlite3_mprintf( u"SELECT name FROM %1 WHERE favorite=1"_s.arg( entityTableName( type ) ).toLocal8Bit().data() );
1530 break;
1531 }
1532
1533 int nErr;
1535 statement = mCurrentDB.prepare( query, nErr );
1536
1537 QStringList symbols;
1538 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1539 {
1540 symbols << statement.columnAsText( 0 );
1541 }
1542
1543 return symbols;
1544}
1545
1546QStringList QgsStyle::symbolsWithTag( StyleEntity type, int tagid ) const
1547{
1548 if ( !mCurrentDB )
1549 {
1550 QgsDebugError( u"Cannot open database to get symbols of tagid %1"_s.arg( tagid ) );
1551 return QStringList();
1552 }
1553
1554 QString subquery;
1555 switch ( type )
1556 {
1557 case TagEntity:
1558 case SmartgroupEntity:
1559 QgsDebugError( u"Unknown Entity"_s );
1560 return QStringList();
1561
1562 default:
1563 subquery = qgs_sqlite3_mprintf( u"SELECT %1 FROM %2 WHERE tag_id=%d"_s.arg( tagmapEntityIdFieldName( type ), tagmapTableName( type ) ).toLocal8Bit().data(), tagid );
1564 break;
1565 }
1566
1567 int nErr;
1569 statement = mCurrentDB.prepare( subquery, nErr );
1570
1571 // get the symbol <-> tag connection from the tag map table
1572 QStringList symbols;
1573 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1574 {
1575 int id = sqlite3_column_int( statement.get(), 0 );
1576
1577 const QString query = qgs_sqlite3_mprintf( u"SELECT name FROM %1 WHERE id=%d"_s.arg( entityTableName( type ) ).toLocal8Bit().data(), id );
1578
1579 int rc;
1581 statement2 = mCurrentDB.prepare( query, rc );
1582 while ( rc == SQLITE_OK && sqlite3_step( statement2.get() ) == SQLITE_ROW )
1583 {
1584 symbols << statement2.columnAsText( 0 );
1585 }
1586 }
1587
1588 return symbols;
1589}
1590
1591int QgsStyle::addTag( const QString &tagname )
1592{
1593 if ( !mCurrentDB )
1594 return 0;
1596
1597 QString query = qgs_sqlite3_mprintf( "INSERT INTO tag VALUES (NULL, '%q')", tagname.toUtf8().constData() );
1598 int nErr;
1599 statement = mCurrentDB.prepare( query, nErr );
1600 if ( nErr == SQLITE_OK )
1601 ( void ) sqlite3_step( statement.get() );
1602
1604
1605 emit groupsModified();
1606
1607 return static_cast< int >( sqlite3_last_insert_rowid( mCurrentDB.get() ) );
1608}
1609
1610QStringList QgsStyle::tags() const
1611{
1612 if ( !mCurrentDB )
1613 return QStringList();
1614
1616
1617 QString query = qgs_sqlite3_mprintf( "SELECT name FROM tag" );
1618 int nError;
1619 statement = mCurrentDB.prepare( query, nError );
1620
1621 QStringList tagList;
1622 while ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1623 {
1624 tagList << statement.columnAsText( 0 );
1625 }
1626
1627 return tagList;
1628}
1629
1630bool QgsStyle::rename( StyleEntity type, int id, const QString &newName )
1631{
1632 const QString query = qgs_sqlite3_mprintf( u"UPDATE %1 SET name='%q' WHERE id=%d"_s.arg( entityTableName( type ) ).toLocal8Bit().data(), newName.toUtf8().constData(), id );
1633
1634 const bool result = runEmptyQuery( query );
1635 if ( !result )
1636 {
1637 mErrorString = u"Could not rename!"_s;
1638 }
1639 else
1640 {
1641 mCachedTags.clear();
1642 mCachedFavorites.clear();
1643
1644 switch ( type )
1645 {
1646 case TagEntity:
1647 {
1648 emit groupsModified();
1649 break;
1650 }
1651
1652 case SmartgroupEntity:
1653 {
1654 emit groupsModified();
1655 break;
1656 }
1657
1658 default:
1659 break;
1660 }
1661 }
1662 return result;
1663}
1664
1665bool QgsStyle::remove( StyleEntity type, int id )
1666{
1667 bool groupRemoved = false;
1668 QString query;
1669 switch ( type )
1670 {
1671 case TagEntity:
1672 query = qgs_sqlite3_mprintf( "DELETE FROM tag WHERE id=%d; DELETE FROM tagmap WHERE tag_id=%d", id, id );
1673 groupRemoved = true;
1674 break;
1675 case SmartgroupEntity:
1676 query = qgs_sqlite3_mprintf( "DELETE FROM smartgroup WHERE id=%d", id );
1677 groupRemoved = true;
1678 break;
1679
1680 default:
1681 query
1682 = qgs_sqlite3_mprintf( u"DELETE FROM %1 WHERE id=%d; DELETE FROM %2 WHERE %3=%d"_s.arg( entityTableName( type ), tagmapTableName( type ), tagmapEntityIdFieldName( type ) ).toLocal8Bit().data(), id, id );
1683 break;
1684 }
1685
1686 bool result = false;
1687 if ( !runEmptyQuery( query ) )
1688 {
1689 QgsDebugError( u"Could not delete entity!"_s );
1690 }
1691 else
1692 {
1693 mCachedTags.clear();
1694 mCachedFavorites.clear();
1695
1696 if ( groupRemoved )
1697 {
1699
1700 emit groupsModified();
1701 }
1702 result = true;
1703 }
1704 return result;
1705}
1706
1708{
1709 switch ( type )
1710 {
1713 return false;
1714
1716 {
1717 std::unique_ptr< QgsSymbol > symbol( mSymbols.take( name ) );
1718 if ( !symbol )
1719 return false;
1720
1721 break;
1722 }
1723
1725 {
1726 std::unique_ptr< QgsAbstract3DSymbol > symbol( m3dSymbols.take( name ) );
1727 if ( !symbol )
1728 return false;
1729
1730 break;
1731 }
1732
1734 {
1735 std::unique_ptr< QgsAbstractMaterialSettings > settings( mMaterialSettings.take( name ) );
1736 if ( !settings )
1737 return false;
1738
1739 break;
1740 }
1741
1743 {
1744 std::unique_ptr< QgsColorRamp > ramp( mColorRamps.take( name ) );
1745 if ( !ramp )
1746 return false;
1747 break;
1748 }
1749
1751 {
1752 auto it = mTextFormats.find( name );
1753 if ( it == mTextFormats.end() )
1754 return false;
1755
1756 mTextFormats.erase( it );
1757 break;
1758 }
1759
1761 {
1762 auto it = mLabelSettings.find( name );
1763 if ( it == mLabelSettings.end() )
1764 return false;
1765
1766 mLabelSettings.erase( it );
1767 break;
1768 }
1769
1771 {
1772 auto it = mLegendPatchShapes.find( name );
1773 if ( it == mLegendPatchShapes.end() )
1774 return false;
1775
1776 mLegendPatchShapes.erase( it );
1777 break;
1778 }
1779 }
1780
1781 if ( !mCurrentDB )
1782 {
1783 QgsDebugError( u"Sorry! Cannot open database to modify."_s );
1784 return false;
1785 }
1786
1787 const int id = entityId( type, name );
1788 if ( !id )
1789 {
1790 QgsDebugError( "No matching entity for deleting in database: " + name );
1791 }
1792
1793 const bool result = remove( type, id );
1794 if ( result )
1795 {
1796 mCachedTags[type].remove( name );
1797 mCachedFavorites[type].remove( name );
1798
1799 switch ( type )
1800 {
1801 case SymbolEntity:
1802 emit symbolRemoved( name );
1803 break;
1804
1805 case ColorrampEntity:
1806 emit rampRemoved( name );
1807 break;
1808
1809 case TextFormatEntity:
1810 emit textFormatRemoved( name );
1811 break;
1812
1814 emit labelSettingsRemoved( name );
1815 break;
1816
1817 default:
1818 // these specific signals should be discouraged -- don't add them for new entity types!
1819 break;
1820 }
1821 emit entityRemoved( type, name );
1822 }
1823 return result;
1824}
1825
1826bool QgsStyle::runEmptyQuery( const QString &query )
1827{
1828 if ( !mCurrentDB )
1829 return false;
1830
1831 char *zErr = nullptr;
1832 int nErr = sqlite3_exec( mCurrentDB.get(), query.toUtf8().constData(), nullptr, nullptr, &zErr );
1833
1834 if ( nErr != SQLITE_OK )
1835 {
1836 QgsDebugError( zErr );
1837 sqlite3_free( zErr );
1838 }
1839
1840 return nErr == SQLITE_OK;
1841}
1842
1843bool QgsStyle::addFavorite( StyleEntity type, const QString &name )
1844{
1845 QString query;
1846
1847 switch ( type )
1848 {
1849 case TagEntity:
1850 case SmartgroupEntity:
1851 QgsDebugError( u"Wrong entity value. cannot apply group"_s );
1852 return false;
1853
1854 default:
1855 query = qgs_sqlite3_mprintf( u"UPDATE %1 SET favorite=1 WHERE name='%q'"_s.arg( entityTableName( type ) ).toLocal8Bit().data(), name.toUtf8().constData() );
1856 break;
1857 }
1858
1859 const bool res = runEmptyQuery( query );
1860 if ( res )
1861 {
1862 switch ( type )
1863 {
1864 case TagEntity:
1865 case SmartgroupEntity:
1866 break;
1867
1868 default:
1869 mCachedFavorites[type].insert( name, true );
1870 break;
1871 }
1872 emit favoritedChanged( type, name, true );
1873 }
1874
1875 return res;
1876}
1877
1878bool QgsStyle::removeFavorite( StyleEntity type, const QString &name )
1879{
1880 QString query;
1881
1882 switch ( type )
1883 {
1884 case TagEntity:
1885 case SmartgroupEntity:
1886 QgsDebugError( u"Wrong entity value. cannot apply group"_s );
1887 return false;
1888
1889 default:
1890 query = qgs_sqlite3_mprintf( u"UPDATE %1 SET favorite=0 WHERE name='%q'"_s.arg( entityTableName( type ) ).toLocal8Bit().data(), name.toUtf8().constData() );
1891 break;
1892 }
1893
1894 const bool res = runEmptyQuery( query );
1895 if ( res )
1896 {
1897 mCachedFavorites[type].insert( name, false );
1898 emit favoritedChanged( type, name, false );
1899 }
1900
1901 return res;
1902}
1903
1904QStringList QgsStyle::findSymbols( StyleEntity type, const QString &qword )
1905{
1906 if ( !mCurrentDB )
1907 {
1908 QgsDebugError( u"Sorry! Cannot open database to search"_s );
1909 return QStringList();
1910 }
1911
1912 // first find symbols with matching name
1913 QString item;
1914 switch ( type )
1915 {
1916 case TagEntity:
1917 case SmartgroupEntity:
1918 return QStringList();
1919
1920 default:
1921 item = entityTableName( type );
1922 break;
1923 }
1924
1925 QString query = qgs_sqlite3_mprintf( "SELECT name FROM %q WHERE name LIKE '%%%q%%'", item.toUtf8().constData(), qword.toUtf8().constData() );
1926
1928 int nErr;
1929 statement = mCurrentDB.prepare( query, nErr );
1930
1931 QSet< QString > symbols;
1932 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1933 {
1934 symbols << statement.columnAsText( 0 );
1935 }
1936
1937 // next add symbols with matching tags
1938 query = qgs_sqlite3_mprintf( "SELECT id FROM tag WHERE name LIKE '%%%q%%'", qword.toUtf8().constData() );
1939 statement = mCurrentDB.prepare( query, nErr );
1940
1941 QStringList tagids;
1942 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1943 {
1944 tagids << statement.columnAsText( 0 );
1945 }
1946
1947 QString dummy = tagids.join( ", "_L1 );
1948 query = qgs_sqlite3_mprintf( u"SELECT %1 FROM %2 WHERE tag_id IN (%q)"_s.arg( tagmapEntityIdFieldName( type ), tagmapTableName( type ) ).toLocal8Bit().data(), dummy.toUtf8().constData() );
1949
1950 statement = mCurrentDB.prepare( query, nErr );
1951
1952 QStringList symbolids;
1953 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1954 {
1955 symbolids << statement.columnAsText( 0 );
1956 }
1957
1958 dummy = symbolids.join( ", "_L1 );
1959 query = qgs_sqlite3_mprintf( "SELECT name FROM %q WHERE id IN (%q)", item.toUtf8().constData(), dummy.toUtf8().constData() );
1960 statement = mCurrentDB.prepare( query, nErr );
1961 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1962 {
1963 symbols << statement.columnAsText( 0 );
1964 }
1965
1966 return QStringList( symbols.constBegin(), symbols.constEnd() );
1967}
1968
1969bool QgsStyle::tagSymbol( StyleEntity type, const QString &symbol, const QStringList &tags )
1970{
1971 if ( !mCurrentDB )
1972 {
1973 QgsDebugError( u"Sorry! Cannot open database to tag."_s );
1974 return false;
1975 }
1976
1977 int symbolid = 0;
1978 switch ( type )
1979 {
1980 case TagEntity:
1981 case SmartgroupEntity:
1982 return false;
1983
1984 default:
1985 symbolid = entityId( type, symbol );
1986 break;
1987 }
1988
1989 if ( !symbolid )
1990 {
1991 QgsDebugError( u"No such symbol for tagging in database: "_s + symbol );
1992 return false;
1993 }
1994
1995 QString tag;
1996 const auto constTags = tags;
1997 for ( const QString &t : constTags )
1998 {
1999 tag = t.trimmed();
2000 if ( !tag.isEmpty() )
2001 {
2002 // sql: gets the id of the tag if present or insert the tag and get the id of the tag
2003 int tagid( tagId( tag ) );
2004 if ( !tagid )
2005 {
2006 tagid = addTag( tag );
2007 }
2008
2009 // Now map the tag to the symbol if it's not already tagged
2010 if ( !symbolHasTag( type, symbol, tag ) )
2011 {
2012 QString query = qgs_sqlite3_mprintf( u"INSERT INTO %1 VALUES (%d,%d)"_s.arg( tagmapTableName( type ) ).toLocal8Bit().data(), tagid, symbolid );
2013
2014 char *zErr = nullptr;
2015 int nErr;
2016 nErr = sqlite3_exec( mCurrentDB.get(), query.toUtf8().constData(), nullptr, nullptr, &zErr );
2017 if ( nErr )
2018 {
2019 QgsDebugError( zErr );
2020 sqlite3_free( zErr );
2021 }
2022 }
2023 }
2024 }
2025
2026 clearCachedTags( type, symbol );
2027 emit entityTagsChanged( type, symbol, tagsOfSymbol( type, symbol ) );
2028
2029 return true;
2030}
2031
2032bool QgsStyle::detagSymbol( StyleEntity type, const QString &symbol, const QStringList &tags )
2033{
2034 if ( !mCurrentDB )
2035 {
2036 QgsDebugError( u"Sorry! Cannot open database for detagging."_s );
2037 return false;
2038 }
2039
2040 switch ( type )
2041 {
2042 case TagEntity:
2043 case SmartgroupEntity:
2044 return false;
2045
2046 default:
2047 break;
2048 }
2049
2050 const int symbolid = entityId( type, symbol );
2051 if ( symbolid == 0 )
2052 return false;
2053
2054 int nErr;
2055 QString query;
2056 const auto constTags = tags;
2057 for ( const QString &tag : constTags )
2058 {
2059 query = qgs_sqlite3_mprintf( "SELECT id FROM tag WHERE name='%q'", tag.toUtf8().constData() );
2060
2062 statement2 = mCurrentDB.prepare( query, nErr );
2063
2064 int tagid = 0;
2065 if ( nErr == SQLITE_OK && sqlite3_step( statement2.get() ) == SQLITE_ROW )
2066 {
2067 tagid = sqlite3_column_int( statement2.get(), 0 );
2068 }
2069
2070 if ( tagid )
2071 {
2072 // remove from the tagmap
2073 const QString query = qgs_sqlite3_mprintf( u"DELETE FROM %1 WHERE tag_id=%d AND %2=%d"_s.arg( tagmapTableName( type ), tagmapEntityIdFieldName( type ) ).toLocal8Bit().data(), tagid, symbolid );
2074 runEmptyQuery( query );
2075 }
2076 }
2077
2078 clearCachedTags( type, symbol );
2079 emit entityTagsChanged( type, symbol, tagsOfSymbol( type, symbol ) );
2080
2081 // TODO Perform tag cleanup
2082 // check the number of entries for a given tag in the tagmap
2083 // if the count is 0, then remove( TagEntity, tagid )
2084 return true;
2085}
2086
2087bool QgsStyle::detagSymbol( StyleEntity type, const QString &symbol )
2088{
2089 if ( !mCurrentDB )
2090 {
2091 QgsDebugError( u"Sorry! Cannot open database for detagging."_s );
2092 return false;
2093 }
2094
2095 switch ( type )
2096 {
2097 case TagEntity:
2098 case SmartgroupEntity:
2099 return false;
2100
2101 default:
2102 break;
2103 }
2104
2105 const int symbolid = entityId( type, symbol );
2106 if ( symbolid == 0 )
2107 {
2108 return false;
2109 }
2110
2111 // remove all tags
2112 const QString query = qgs_sqlite3_mprintf( u"DELETE FROM %1 WHERE %2=%d"_s.arg( tagmapTableName( type ), tagmapEntityIdFieldName( type ) ).toLocal8Bit().data(), symbolid );
2113 runEmptyQuery( query );
2114
2115 clearCachedTags( type, symbol );
2116 emit entityTagsChanged( type, symbol, QStringList() );
2117
2118 // TODO Perform tag cleanup
2119 // check the number of entries for a given tag in the tagmap
2120 // if the count is 0, then remove( TagEntity, tagid )
2121 return true;
2122}
2123
2124QStringList QgsStyle::tagsOfSymbol( StyleEntity type, const QString &symbol )
2125{
2126 switch ( type )
2127 {
2128 case TagEntity:
2129 case SmartgroupEntity:
2130 return QStringList();
2131
2132 default:
2133 {
2134 auto it = mCachedTags[type].constFind( symbol );
2135 if ( it != mCachedTags[type].constEnd() )
2136 return it.value();
2137 break;
2138 }
2139 }
2140
2141 if ( !mCurrentDB )
2142 {
2143 QgsDebugError( u"Sorry! Cannot open database for getting the tags."_s );
2144 return QStringList();
2145 }
2146
2147 int symbolid = entityId( type, symbol );
2148 if ( !symbolid )
2149 return QStringList();
2150
2151 // get the ids of tags for the symbol
2152 const QString query = qgs_sqlite3_mprintf( u"SELECT tag_id FROM %1 WHERE %2=%d"_s.arg( tagmapTableName( type ), tagmapEntityIdFieldName( type ) ).toLocal8Bit().data(), symbolid );
2153
2155 int nErr;
2156 statement = mCurrentDB.prepare( query, nErr );
2157
2158 QStringList tagList;
2159 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
2160 {
2161 QString subquery = qgs_sqlite3_mprintf( "SELECT name FROM tag WHERE id=%d", sqlite3_column_int( statement.get(), 0 ) );
2162
2164 int pErr;
2165 statement2 = mCurrentDB.prepare( subquery, pErr );
2166 if ( pErr == SQLITE_OK && sqlite3_step( statement2.get() ) == SQLITE_ROW )
2167 {
2168 tagList << statement2.columnAsText( 0 );
2169 }
2170 }
2171
2172 // update cache
2173 mCachedTags[type].insert( symbol, tagList );
2174
2175 return tagList;
2176}
2177
2179{
2180 if ( !mCurrentDB )
2181 {
2182 QgsDebugError( u"Sorry! Cannot open database for getting the tags."_s );
2183 return false;
2184 }
2185
2186 switch ( type )
2187 {
2188 case TagEntity:
2189 case SmartgroupEntity:
2190 return false;
2191
2192 default:
2193 {
2194 auto it = mCachedFavorites[type].constFind( name );
2195 if ( it != mCachedFavorites[type].constEnd() )
2196 return it.value();
2197 break;
2198 }
2199 }
2200
2201 const QStringList names = allNames( type );
2202 if ( !names.contains( name ) )
2203 return false; // entity doesn't exist
2204
2205 // for efficiency, retrieve names of all favorited symbols and store them in cache
2206 const QStringList favorites = symbolsOfFavorite( type );
2207 bool res = false;
2208 for ( const QString &n : names )
2209 {
2210 const bool isFav = favorites.contains( n );
2211 if ( n == name )
2212 res = isFav;
2213
2214 mCachedFavorites[type].insert( n, isFav );
2215 }
2216 return res;
2217}
2218
2219bool QgsStyle::symbolHasTag( StyleEntity type, const QString &symbol, const QString &tag )
2220{
2221 if ( !mCurrentDB )
2222 {
2223 QgsDebugError( u"Sorry! Cannot open database for getting the tags."_s );
2224 return false;
2225 }
2226
2227 int symbolid = 0;
2228 switch ( type )
2229 {
2230 case TagEntity:
2231 case SmartgroupEntity:
2232 return false;
2233
2234 default:
2235 symbolid = entityId( type, symbol );
2236 break;
2237 }
2238
2239 if ( !symbolid )
2240 {
2241 return false;
2242 }
2243 int tagid = tagId( tag );
2244 if ( !tagid )
2245 {
2246 return false;
2247 }
2248
2249 // get the ids of tags for the symbol
2250 const QString query = qgs_sqlite3_mprintf( u"SELECT tag_id FROM %1 WHERE tag_id=%d AND %2=%d"_s.arg( tagmapTableName( type ), tagmapEntityIdFieldName( type ) ).toLocal8Bit().data(), tagid, symbolid );
2251
2253 int nErr;
2254 statement = mCurrentDB.prepare( query, nErr );
2255
2256 return ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW );
2257}
2258
2259QString QgsStyle::tag( int id ) const
2260{
2261 if ( !mCurrentDB )
2262 return QString();
2263
2265
2266 QString query = qgs_sqlite3_mprintf( "SELECT name FROM tag WHERE id=%d", id );
2267 int nError;
2268 statement = mCurrentDB.prepare( query, nError );
2269
2270 QString tag;
2271 if ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
2272 {
2273 tag = statement.columnAsText( 0 );
2274 }
2275
2276 return tag;
2277}
2278
2279int QgsStyle::getId( const QString &table, const QString &name )
2280{
2281 QString lowerName( name.toLower() );
2282 QString query = qgs_sqlite3_mprintf( "SELECT id FROM %q WHERE LOWER(name)='%q'", table.toUtf8().constData(), lowerName.toUtf8().constData() );
2283
2285 int nErr;
2286 statement = mCurrentDB.prepare( query, nErr );
2287
2288 int id = 0;
2289 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
2290 {
2291 id = sqlite3_column_int( statement.get(), 0 );
2292 }
2293 else
2294 {
2295 // Try the name without lowercase conversion
2296 QString query = qgs_sqlite3_mprintf( "SELECT id FROM %q WHERE name='%q'", table.toUtf8().constData(), name.toUtf8().constData() );
2297
2298 sqlite3_statement_unique_ptr statement;
2299 int nErr;
2300 statement = mCurrentDB.prepare( query, nErr );
2301 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
2302 {
2303 id = sqlite3_column_int( statement.get(), 0 );
2304 }
2305 }
2306
2307 return id;
2308}
2309
2310QString QgsStyle::getName( const QString &table, int id ) const
2311{
2312 QString query = qgs_sqlite3_mprintf( "SELECT name FROM %q WHERE id='%q'", table.toUtf8().constData(), QString::number( id ).toUtf8().constData() );
2313
2314 sqlite3_statement_unique_ptr statement;
2315 int nErr;
2316 statement = mCurrentDB.prepare( query, nErr );
2317
2318 QString name;
2319 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
2320 {
2321 name = statement.columnAsText( 0 );
2322 }
2323
2324 return name;
2325}
2326
2327int QgsStyle::symbolId( const QString &name )
2328{
2329 return getId( u"symbol"_s, name );
2330}
2331
2333{
2334 return getId( entityTableName( type ), name );
2335}
2336
2337int QgsStyle::colorrampId( const QString &name )
2338{
2339 return getId( u"colorramp"_s, name );
2340}
2341
2343{
2344 return mTextFormats.value( name );
2345}
2346
2348{
2349 return static_cast< int >( mTextFormats.count() );
2350}
2351
2352QStringList QgsStyle::textFormatNames() const
2353{
2354 return mTextFormats.keys();
2355}
2356
2357int QgsStyle::textFormatId( const QString &name )
2358{
2359 return getId( u"textformat"_s, name );
2360}
2361
2363{
2364 return mLabelSettings.value( name );
2365}
2366
2368{
2369 return mLegendPatchShapes.value( name );
2370}
2371
2373{
2374 return static_cast< int >( mLegendPatchShapes.count() );
2375}
2376
2378{
2379 auto it = mLegendPatchShapes.constFind( name );
2380 if ( it == mLegendPatchShapes.constEnd() )
2382
2383 return it.value().symbolType();
2384}
2385
2387{
2388 auto it = m3dSymbols.constFind( name );
2389 if ( it != m3dSymbols.constEnd() )
2390 return it.value()->clone();
2391 return nullptr;
2392}
2393
2395{
2396 return static_cast< int >( m3dSymbols.count() );
2397}
2398
2399QList<Qgis::GeometryType> QgsStyle::symbol3DCompatibleGeometryTypes( const QString &name ) const
2400{
2401 auto it = m3dSymbols.constFind( name );
2402 if ( it == m3dSymbols.constEnd() )
2403 return QList<Qgis::GeometryType>();
2404
2405 return it.value()->compatibleGeometryTypes();
2406}
2407
2409{
2410 auto it = mLabelSettings.constFind( name );
2411 if ( it == mLabelSettings.constEnd() )
2413
2414 return it.value().layerType;
2415}
2416
2418{
2419 return static_cast< int >( mLabelSettings.count() );
2420}
2421
2423{
2424 return mLabelSettings.keys();
2425}
2426
2427int QgsStyle::labelSettingsId( const QString &name )
2428{
2429 return getId( u"labelsettings"_s, name );
2430}
2431
2433{
2434 return mLegendPatchShapes.keys();
2435}
2436
2438{
2439 switch ( shape.symbolType() )
2440 {
2442 return mPatchMarkerSymbol.get();
2443
2445 return mPatchLineSymbol.get();
2446
2448 return mPatchFillSymbol.get();
2449
2451 break;
2452 }
2453 return nullptr;
2454}
2455
2456int QgsStyle::tagId( const QString &name )
2457{
2458 return getId( u"tag"_s, name );
2459}
2460
2461int QgsStyle::smartgroupId( const QString &name )
2462{
2463 return getId( u"smartgroup"_s, name );
2464}
2465
2467{
2468 switch ( type )
2469 {
2470 case SymbolEntity:
2471 return symbolNames();
2472
2473 case ColorrampEntity:
2474 return colorRampNames();
2475
2476 case TextFormatEntity:
2477 return textFormatNames();
2478
2480 return labelSettingsNames();
2481
2483 return legendPatchShapeNames();
2484
2485 case Symbol3DEntity:
2486 return symbol3DNames();
2487
2489 return materialSettingsNames();
2490
2491 case TagEntity:
2492 return tags();
2493
2494 case SmartgroupEntity:
2495 return smartgroupNames();
2496 }
2497 return QStringList();
2498}
2499
2500int QgsStyle::addSmartgroup( const QString &name, const QString &op, const QgsSmartConditionMap &conditions )
2501{
2502 return addSmartgroup( name, op, conditions.values( u"tag"_s ), conditions.values( u"!tag"_s ), conditions.values( u"name"_s ), conditions.values( u"!name"_s ) );
2503}
2504
2505int QgsStyle::addSmartgroup( const QString &name, const QString &op, const QStringList &matchTag, const QStringList &noMatchTag, const QStringList &matchName, const QStringList &noMatchName )
2506{
2507 QDomDocument doc( u"dummy"_s );
2508 QDomElement smartEl = doc.createElement( u"smartgroup"_s );
2509 smartEl.setAttribute( u"name"_s, name );
2510 smartEl.setAttribute( u"operator"_s, op );
2511
2512 auto addCondition = [&doc, &smartEl]( const QString &constraint, const QStringList &parameters ) {
2513 for ( const QString &param : parameters )
2514 {
2515 QDomElement condEl = doc.createElement( u"condition"_s );
2516 condEl.setAttribute( u"constraint"_s, constraint );
2517 condEl.setAttribute( u"param"_s, param );
2518 smartEl.appendChild( condEl );
2519 }
2520 };
2521 addCondition( u"tag"_s, matchTag );
2522 addCondition( u"!tag"_s, noMatchTag );
2523 addCondition( u"name"_s, matchName );
2524 addCondition( u"!name"_s, noMatchName );
2525
2526 QByteArray xmlArray;
2527 QTextStream stream( &xmlArray );
2528 smartEl.save( stream, 4 );
2529 QString query = qgs_sqlite3_mprintf( "INSERT INTO smartgroup VALUES (NULL, '%q', '%q')", name.toUtf8().constData(), xmlArray.constData() );
2530
2531 if ( runEmptyQuery( query ) )
2532 {
2534
2535 emit groupsModified();
2536 return static_cast< int >( sqlite3_last_insert_rowid( mCurrentDB.get() ) );
2537 }
2538 else
2539 {
2540 QgsDebugError( u"Couldn't add the smart group into the database!"_s );
2541 return 0;
2542 }
2543}
2544
2546{
2547 if ( !mCurrentDB )
2548 {
2549 QgsDebugError( u"Cannot open database for listing groups"_s );
2550 return QgsSymbolGroupMap();
2551 }
2552
2553 QString query = qgs_sqlite3_mprintf( "SELECT * FROM smartgroup" );
2554
2555 // Now run the query and retrieve the group names
2557 int nError;
2558 statement = mCurrentDB.prepare( query, nError );
2559
2560 QgsSymbolGroupMap groupNames;
2561 while ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
2562 {
2563 QString group = statement.columnAsText( static_cast< int >( SmartGroupTableColumn::Name ) );
2564 groupNames.insert( sqlite3_column_int( statement.get(), static_cast< int >( SmartGroupTableColumn::Id ) ), group );
2565 }
2566
2567 return groupNames;
2568}
2569
2570QStringList QgsStyle::smartgroupNames() const
2571{
2572 if ( !mCurrentDB )
2573 {
2574 QgsDebugError( u"Cannot open database for listing groups"_s );
2575 return QStringList();
2576 }
2577
2578 QString query = qgs_sqlite3_mprintf( "SELECT name FROM smartgroup" );
2579
2580 // Now run the query and retrieve the group names
2582 int nError;
2583 statement = mCurrentDB.prepare( query, nError );
2584
2585 QStringList groups;
2586 while ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
2587 {
2588 groups << statement.columnAsText( 0 );
2589 }
2590
2591 return groups;
2592}
2593
2595{
2596 QStringList symbols;
2597
2598 QString query = qgs_sqlite3_mprintf( "SELECT xml FROM smartgroup WHERE id=%d", id );
2599
2601 int nErr;
2602 statement = mCurrentDB.prepare( query, nErr );
2603 if ( !( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW ) )
2604 {
2605 return QStringList();
2606 }
2607 else
2608 {
2609 QDomDocument doc;
2610 QString xmlstr = statement.columnAsText( 0 );
2611 if ( !doc.setContent( xmlstr ) )
2612 {
2613 QgsDebugError( u"Cannot open smartgroup id: %1"_s.arg( id ) );
2614 }
2615 QDomElement smartEl = doc.documentElement();
2616 QString op = smartEl.attribute( u"operator"_s );
2617 QDomNodeList conditionNodes = smartEl.childNodes();
2618
2619 bool firstSet = true;
2620 for ( int i = 0; i < conditionNodes.count(); i++ )
2621 {
2622 QDomElement condEl = conditionNodes.at( i ).toElement();
2623 QString constraint = condEl.attribute( u"constraint"_s );
2624 QString param = condEl.attribute( u"param"_s );
2625
2626 QStringList resultNames;
2627 // perform suitable action for the given constraint
2628 if ( constraint == "tag"_L1 )
2629 {
2630 resultNames = symbolsWithTag( type, tagId( param ) );
2631 }
2632 else if ( constraint == "name"_L1 )
2633 {
2634 resultNames = allNames( type ).filter( param, Qt::CaseInsensitive );
2635 }
2636 else if ( constraint == "!tag"_L1 )
2637 {
2638 resultNames = allNames( type );
2639 const QStringList unwanted = symbolsWithTag( type, tagId( param ) );
2640 for ( const QString &name : unwanted )
2641 {
2642 resultNames.removeAll( name );
2643 }
2644 }
2645 else if ( constraint == "!name"_L1 )
2646 {
2647 const QStringList all = allNames( type );
2648 for ( const QString &str : all )
2649 {
2650 if ( !str.contains( param, Qt::CaseInsensitive ) )
2651 resultNames << str;
2652 }
2653 }
2654
2655 // not apply the operator
2656 if ( firstSet )
2657 {
2658 symbols = resultNames;
2659 firstSet = false;
2660 }
2661 else
2662 {
2663 if ( op == "OR"_L1 )
2664 {
2665 symbols << resultNames;
2666 }
2667 else if ( op == "AND"_L1 )
2668 {
2669 QStringList dummy = symbols;
2670 symbols.clear();
2671 for ( const QString &result : std::as_const( resultNames ) )
2672 {
2673 if ( dummy.contains( result ) )
2674 symbols << result;
2675 }
2676 }
2677 }
2678 } // DOM loop ends here
2679 }
2680
2681 // return sorted, unique list
2682 const QSet< QString > uniqueSet( symbols.constBegin(), symbols.constEnd() );
2683 QStringList unique( uniqueSet.begin(), uniqueSet.end() );
2684 std::sort( unique.begin(), unique.end() );
2685 return unique;
2686}
2687
2689{
2690 if ( !mCurrentDB )
2691 {
2692 QgsDebugError( u"Cannot open database for listing groups"_s );
2693 return QgsSmartConditionMap();
2694 }
2695
2696 QgsSmartConditionMap condition;
2697
2698 QString query = qgs_sqlite3_mprintf( "SELECT xml FROM smartgroup WHERE id=%d", id );
2699
2701 int nError;
2702 statement = mCurrentDB.prepare( query, nError );
2703 if ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
2704 {
2705 QDomDocument doc;
2706 QString xmlstr = statement.columnAsText( 0 );
2707 if ( !doc.setContent( xmlstr ) )
2708 {
2709 QgsDebugError( u"Cannot open smartgroup id: %1"_s.arg( id ) );
2710 }
2711
2712 QDomElement smartEl = doc.documentElement();
2713 QDomNodeList conditionNodes = smartEl.childNodes();
2714
2715 for ( int i = 0; i < conditionNodes.count(); i++ )
2716 {
2717 QDomElement condEl = conditionNodes.at( i ).toElement();
2718 QString constraint = condEl.attribute( u"constraint"_s );
2719 QString param = condEl.attribute( u"param"_s );
2720
2721 condition.insert( constraint, param );
2722 }
2723 }
2724
2725 return condition;
2726}
2727
2729{
2730 if ( !mCurrentDB )
2731 {
2732 QgsDebugError( u"Cannot open database for listing groups"_s );
2733 return QString();
2734 }
2735
2736 QString op;
2737
2738 QString query = qgs_sqlite3_mprintf( "SELECT xml FROM smartgroup WHERE id=%d", id );
2739
2740 int nError;
2742 statement = mCurrentDB.prepare( query, nError );
2743 if ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
2744 {
2745 QDomDocument doc;
2746 QString xmlstr = statement.columnAsText( 0 );
2747 if ( !doc.setContent( xmlstr ) )
2748 {
2749 QgsDebugError( u"Cannot open smartgroup id: %1"_s.arg( id ) );
2750 }
2751 QDomElement smartEl = doc.documentElement();
2752 op = smartEl.attribute( u"operator"_s );
2753 }
2754
2755 return op;
2756}
2757
2758bool QgsStyle::exportXml( const QString &filename )
2759{
2760 if ( filename.isEmpty() )
2761 {
2762 QgsDebugError( u"Invalid filename for style export."_s );
2763 return false;
2764 }
2765
2766 QDomDocument doc( u"qgis_style"_s );
2767 QDomElement root = doc.createElement( u"qgis_style"_s );
2768 root.setAttribute( u"version"_s, QStringLiteral( STYLE_CURRENT_VERSION ) );
2769 doc.appendChild( root );
2770
2771 const QStringList favoriteSymbols = symbolsOfFavorite( SymbolEntity );
2772 const QStringList favoriteColorramps = symbolsOfFavorite( ColorrampEntity );
2773 const QStringList favoriteTextFormats = symbolsOfFavorite( TextFormatEntity );
2774 const QStringList favoriteLegendShapes = symbolsOfFavorite( LegendPatchShapeEntity );
2775 const QStringList favorite3DSymbols = symbolsOfFavorite( Symbol3DEntity );
2776 const QStringList favoriteMaterialSettings = symbolsOfFavorite( MaterialSettingsEntity );
2777
2778 // save symbols and attach tags
2779 QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( mSymbols, u"symbols"_s, doc, QgsReadWriteContext() );
2780 QDomNodeList symbolsList = symbolsElem.elementsByTagName( u"symbol"_s );
2781 int nbSymbols = symbolsList.count();
2782 for ( int i = 0; i < nbSymbols; ++i )
2783 {
2784 QDomElement symbol = symbolsList.at( i ).toElement();
2785 QString name = symbol.attribute( u"name"_s );
2786 QStringList tags = tagsOfSymbol( SymbolEntity, name );
2787 if ( tags.count() > 0 )
2788 {
2789 symbol.setAttribute( u"tags"_s, tags.join( ',' ) );
2790 }
2791 if ( favoriteSymbols.contains( name ) )
2792 {
2793 symbol.setAttribute( u"favorite"_s, u"1"_s );
2794 }
2795 }
2796
2797 // save color ramps
2798 QDomElement rampsElem = doc.createElement( u"colorramps"_s );
2799 for ( QMap<QString, QgsColorRamp *>::const_iterator itr = mColorRamps.constBegin(); itr != mColorRamps.constEnd(); ++itr )
2800 {
2801 QDomElement rampEl = QgsSymbolLayerUtils::saveColorRamp( itr.key(), itr.value(), doc );
2802 QStringList tags = tagsOfSymbol( ColorrampEntity, itr.key() );
2803 if ( tags.count() > 0 )
2804 {
2805 rampEl.setAttribute( u"tags"_s, tags.join( ',' ) );
2806 }
2807 if ( favoriteColorramps.contains( itr.key() ) )
2808 {
2809 rampEl.setAttribute( u"favorite"_s, u"1"_s );
2810 }
2811 rampsElem.appendChild( rampEl );
2812 }
2813
2814 // save text formats
2815 QDomElement textFormatsElem = doc.createElement( u"textformats"_s );
2816 for ( auto it = mTextFormats.constBegin(); it != mTextFormats.constEnd(); ++it )
2817 {
2818 QDomElement textFormatEl = doc.createElement( u"textformat"_s );
2819 textFormatEl.setAttribute( u"name"_s, it.key() );
2820 QDomElement textStyleEl = it.value().writeXml( doc, QgsReadWriteContext() );
2821 textFormatEl.appendChild( textStyleEl );
2822 QStringList tags = tagsOfSymbol( TextFormatEntity, it.key() );
2823 if ( tags.count() > 0 )
2824 {
2825 textFormatEl.setAttribute( u"tags"_s, tags.join( ',' ) );
2826 }
2827 if ( favoriteTextFormats.contains( it.key() ) )
2828 {
2829 textFormatEl.setAttribute( u"favorite"_s, u"1"_s );
2830 }
2831 textFormatsElem.appendChild( textFormatEl );
2832 }
2833
2834 // save label settings
2835 QDomElement labelSettingsElem = doc.createElement( u"labelsettings"_s );
2836 for ( auto it = mLabelSettings.constBegin(); it != mLabelSettings.constEnd(); ++it )
2837 {
2838 QDomElement labelSettingsEl = doc.createElement( u"labelsetting"_s );
2839 labelSettingsEl.setAttribute( u"name"_s, it.key() );
2840 QDomElement defEl = it.value().writeXml( doc, QgsReadWriteContext() );
2841 labelSettingsEl.appendChild( defEl );
2842 QStringList tags = tagsOfSymbol( LabelSettingsEntity, it.key() );
2843 if ( tags.count() > 0 )
2844 {
2845 labelSettingsEl.setAttribute( u"tags"_s, tags.join( ',' ) );
2846 }
2847 if ( favoriteTextFormats.contains( it.key() ) )
2848 {
2849 labelSettingsEl.setAttribute( u"favorite"_s, u"1"_s );
2850 }
2851 labelSettingsElem.appendChild( labelSettingsEl );
2852 }
2853
2854 // save legend patch shapes
2855 QDomElement legendPatchShapesElem = doc.createElement( u"legendpatchshapes"_s );
2856 for ( auto it = mLegendPatchShapes.constBegin(); it != mLegendPatchShapes.constEnd(); ++it )
2857 {
2858 QDomElement legendPatchShapeEl = doc.createElement( u"legendpatchshape"_s );
2859 legendPatchShapeEl.setAttribute( u"name"_s, it.key() );
2860 QDomElement defEl = doc.createElement( u"definition"_s );
2861 it.value().writeXml( defEl, doc, QgsReadWriteContext() );
2862 legendPatchShapeEl.appendChild( defEl );
2863 QStringList tags = tagsOfSymbol( LegendPatchShapeEntity, it.key() );
2864 if ( tags.count() > 0 )
2865 {
2866 legendPatchShapeEl.setAttribute( u"tags"_s, tags.join( ',' ) );
2867 }
2868 if ( favoriteLegendShapes.contains( it.key() ) )
2869 {
2870 legendPatchShapeEl.setAttribute( u"favorite"_s, u"1"_s );
2871 }
2872 legendPatchShapesElem.appendChild( legendPatchShapeEl );
2873 }
2874
2875 // save 3D symbols and attach tags
2876 QDomElement symbols3DElem = doc.createElement( u"symbols3d"_s );
2877 for ( auto it = m3dSymbols.constBegin(); it != m3dSymbols.constEnd(); ++it )
2878 {
2879 QDomElement symbolEl = doc.createElement( u"symbol3d"_s );
2880 symbolEl.setAttribute( u"name"_s, it.key() );
2881 QDomElement defEl = doc.createElement( u"definition"_s );
2882 defEl.setAttribute( u"type"_s, it.value()->type() );
2883 it.value()->writeXml( defEl, QgsReadWriteContext() );
2884 symbolEl.appendChild( defEl );
2885 QStringList tags = tagsOfSymbol( Symbol3DEntity, it.key() );
2886 if ( tags.count() > 0 )
2887 {
2888 symbolEl.setAttribute( u"tags"_s, tags.join( ',' ) );
2889 }
2890 if ( favorite3DSymbols.contains( it.key() ) )
2891 {
2892 symbolEl.setAttribute( u"favorite"_s, u"1"_s );
2893 }
2894 symbols3DElem.appendChild( symbolEl );
2895 }
2896
2897 // save material settings and attach tags
2898 QDomElement materialSettingsElem = doc.createElement( u"materialsettings"_s );
2899 for ( auto it = mMaterialSettings.constBegin(); it != mMaterialSettings.constEnd(); ++it )
2900 {
2901 QDomElement materialSettingEl = doc.createElement( u"material"_s );
2902 materialSettingEl.setAttribute( u"name"_s, it.key() );
2903 QDomElement defEl = doc.createElement( u"definition"_s );
2904 it.value()->writeXml( defEl, QgsReadWriteContext() );
2905 materialSettingEl.appendChild( defEl );
2906 QStringList tags = tagsOfSymbol( MaterialSettingsEntity, it.key() );
2907 if ( tags.count() > 0 )
2908 {
2909 materialSettingEl.setAttribute( u"tags"_s, tags.join( ',' ) );
2910 }
2911 if ( favoriteMaterialSettings.contains( it.key() ) )
2912 {
2913 materialSettingEl.setAttribute( u"favorite"_s, u"1"_s );
2914 }
2915 materialSettingsElem.appendChild( materialSettingEl );
2916 }
2917 root.appendChild( symbolsElem );
2918 root.appendChild( rampsElem );
2919 root.appendChild( textFormatsElem );
2920 root.appendChild( labelSettingsElem );
2921 root.appendChild( legendPatchShapesElem );
2922 root.appendChild( symbols3DElem );
2923 root.appendChild( materialSettingsElem );
2924
2925 // save
2926 QFile f( filename );
2927 if ( !f.open( QFile::WriteOnly | QIODevice::Truncate ) )
2928 {
2929 mErrorString = "Couldn't open file for writing: " + filename;
2930 return false;
2931 }
2932
2933 QTextStream ts( &f );
2934 doc.save( ts, 2 );
2935 f.close();
2936
2937 return true;
2938}
2939
2940bool QgsStyle::importXml( const QString &filename )
2941{
2942 return importXml( filename, -1 );
2943}
2944
2945bool QgsStyle::importXml( const QString &filename, int sinceVersion )
2946{
2947 mErrorString = QString();
2948 QDomDocument doc( u"style"_s );
2949 QFile f( filename );
2950 if ( !f.open( QFile::ReadOnly ) )
2951 {
2952 mErrorString = u"Unable to open the specified file"_s;
2953 QgsDebugError( u"Error opening the style XML file."_s );
2954 return false;
2955 }
2956
2957 if ( !doc.setContent( &f ) )
2958 {
2959 mErrorString = u"Unable to understand the style file: %1"_s.arg( filename );
2960 QgsDebugError( u"XML Parsing error"_s );
2961 f.close();
2962 return false;
2963 }
2964 f.close();
2965
2966 QDomElement docEl = doc.documentElement();
2967 if ( docEl.tagName() != "qgis_style"_L1 )
2968 {
2969 mErrorString = "Incorrect root tag in style: " + docEl.tagName();
2970 return false;
2971 }
2972
2973 const QString version = docEl.attribute( u"version"_s );
2974 if ( version != QLatin1String( STYLE_CURRENT_VERSION ) && version != "0"_L1 && version != "1"_L1 )
2975 {
2976 mErrorString = "Unknown style file version: " + version;
2977 return false;
2978 }
2979
2980 QgsSymbolMap symbols;
2981
2982 QDomElement symbolsElement = docEl.firstChildElement( u"symbols"_s );
2983 QDomElement e = symbolsElement.firstChildElement();
2984
2985 // gain speed by re-grouping the INSERT statements in a transaction
2986 QString query = qgs_sqlite3_mprintf( "BEGIN TRANSACTION;" );
2987 runEmptyQuery( query );
2988
2989 if ( version == QLatin1String( STYLE_CURRENT_VERSION ) || version == "1"_L1 )
2990 {
2991 // For the new style, load symbols individually
2992 for ( ; !e.isNull(); e = e.nextSiblingElement() )
2993 {
2994 const int entityAddedVersion = e.attribute( u"addedVersion"_s ).toInt();
2995 if ( entityAddedVersion != 0 && sinceVersion != -1 && entityAddedVersion <= sinceVersion )
2996 {
2997 // skip the symbol, should already be present
2998 continue;
2999 }
3000
3001 if ( e.tagName() == "symbol"_L1 )
3002 {
3003 QString name = e.attribute( u"name"_s );
3004 QStringList tags;
3005 if ( e.hasAttribute( u"tags"_s ) )
3006 {
3007 tags = e.attribute( u"tags"_s ).split( ',' );
3008 }
3009 bool favorite = false;
3010 if ( e.hasAttribute( u"favorite"_s ) && e.attribute( u"favorite"_s ) == "1"_L1 )
3011 {
3012 favorite = true;
3013 }
3014
3015 std::unique_ptr< QgsSymbol > symbol = QgsSymbolLayerUtils::loadSymbol( e, QgsReadWriteContext() );
3016 if ( symbol )
3017 {
3018 QgsSymbol *symbolPtr = symbol.get();
3019 addSymbol( name, symbol.release() );
3020 if ( mCurrentDB )
3021 {
3022 saveSymbol( name, symbolPtr, favorite, tags );
3023 }
3024 }
3025 }
3026 else
3027 {
3028 QgsDebugError( "unknown tag: " + e.tagName() );
3029 }
3030 }
3031 }
3032 else
3033 {
3034 // for the old version, use the utility function to solve @symbol@layer subsymbols
3035 symbols = QgsSymbolLayerUtils::loadSymbols( symbolsElement, QgsReadWriteContext() );
3036
3037 // save the symbols with proper name
3038 for ( QMap<QString, QgsSymbol *>::iterator it = symbols.begin(); it != symbols.end(); ++it )
3039 {
3040 addSymbol( it.key(), it.value() );
3041 }
3042 }
3043
3044 // load color ramps
3045 QDomElement rampsElement = docEl.firstChildElement( u"colorramps"_s );
3046 e = rampsElement.firstChildElement();
3047 for ( ; !e.isNull(); e = e.nextSiblingElement() )
3048 {
3049 const int entityAddedVersion = e.attribute( u"addedVersion"_s ).toInt();
3050 if ( entityAddedVersion != 0 && sinceVersion != -1 && entityAddedVersion <= sinceVersion )
3051 {
3052 // skip the ramp, should already be present
3053 continue;
3054 }
3055
3056 if ( e.tagName() == "colorramp"_L1 )
3057 {
3058 QString name = e.attribute( u"name"_s );
3059 QStringList tags;
3060 if ( e.hasAttribute( u"tags"_s ) )
3061 {
3062 tags = e.attribute( u"tags"_s ).split( ',' );
3063 }
3064 bool favorite = false;
3065 if ( e.hasAttribute( u"favorite"_s ) && e.attribute( u"favorite"_s ) == "1"_L1 )
3066 {
3067 favorite = true;
3068 }
3069
3070 std::unique_ptr< QgsColorRamp > ramp = QgsSymbolLayerUtils::loadColorRamp( e );
3071 if ( ramp )
3072 {
3073 QgsColorRamp *rampPtr = ramp.get();
3074 addColorRamp( name, ramp.release() );
3075 if ( mCurrentDB )
3076 {
3077 saveColorRamp( name, rampPtr, favorite, tags );
3078 }
3079 }
3080 }
3081 else
3082 {
3083 QgsDebugError( "unknown tag: " + e.tagName() );
3084 }
3085 }
3086
3087 // load text formats
3088
3089 // this is ONLY safe to do if we have a QGuiApplication-- it requires QFontDatabase, which is not available otherwise!
3090 if ( qobject_cast< QGuiApplication * >( QCoreApplication::instance() ) )
3091 {
3092 if ( version == STYLE_CURRENT_VERSION )
3093 {
3094 const QDomElement textFormatElement = docEl.firstChildElement( u"textformats"_s );
3095 e = textFormatElement.firstChildElement();
3096 for ( ; !e.isNull(); e = e.nextSiblingElement() )
3097 {
3098 const int entityAddedVersion = e.attribute( u"addedVersion"_s ).toInt();
3099 if ( entityAddedVersion != 0 && sinceVersion != -1 && entityAddedVersion <= sinceVersion )
3100 {
3101 // skip the format, should already be present
3102 continue;
3103 }
3104
3105 if ( e.tagName() == "textformat"_L1 )
3106 {
3107 QString name = e.attribute( u"name"_s );
3108 QStringList tags;
3109 if ( e.hasAttribute( u"tags"_s ) )
3110 {
3111 tags = e.attribute( u"tags"_s ).split( ',' );
3112 }
3113 bool favorite = false;
3114 if ( e.hasAttribute( u"favorite"_s ) && e.attribute( u"favorite"_s ) == "1"_L1 )
3115 {
3116 favorite = true;
3117 }
3118
3119 QgsTextFormat format;
3120 const QDomElement styleElem = e.firstChildElement();
3121 format.readXml( styleElem, QgsReadWriteContext() );
3122 addTextFormat( name, format );
3123 if ( mCurrentDB )
3124 {
3125 saveTextFormat( name, format, favorite, tags );
3126 }
3127 }
3128 else
3129 {
3130 QgsDebugError( "unknown tag: " + e.tagName() );
3131 }
3132 }
3133 }
3134
3135 // load label settings
3136 if ( version == STYLE_CURRENT_VERSION )
3137 {
3138 const QDomElement labelSettingsElement = docEl.firstChildElement( u"labelsettings"_s );
3139 e = labelSettingsElement.firstChildElement();
3140 for ( ; !e.isNull(); e = e.nextSiblingElement() )
3141 {
3142 const int entityAddedVersion = e.attribute( u"addedVersion"_s ).toInt();
3143 if ( entityAddedVersion != 0 && sinceVersion != -1 && entityAddedVersion <= sinceVersion )
3144 {
3145 // skip the settings, should already be present
3146 continue;
3147 }
3148
3149 if ( e.tagName() == "labelsetting"_L1 )
3150 {
3151 QString name = e.attribute( u"name"_s );
3152 QStringList tags;
3153 if ( e.hasAttribute( u"tags"_s ) )
3154 {
3155 tags = e.attribute( u"tags"_s ).split( ',' );
3156 }
3157 bool favorite = false;
3158 if ( e.hasAttribute( u"favorite"_s ) && e.attribute( u"favorite"_s ) == "1"_L1 )
3159 {
3160 favorite = true;
3161 }
3162
3163 QgsPalLayerSettings settings;
3164 const QDomElement styleElem = e.firstChildElement();
3165 settings.readXml( styleElem, QgsReadWriteContext() );
3166 addLabelSettings( name, settings );
3167 if ( mCurrentDB )
3168 {
3169 saveLabelSettings( name, settings, favorite, tags );
3170 }
3171 }
3172 else
3173 {
3174 QgsDebugError( "unknown tag: " + e.tagName() );
3175 }
3176 }
3177 }
3178 }
3179
3180 // load legend patch shapes
3181 if ( version == STYLE_CURRENT_VERSION )
3182 {
3183 const QDomElement legendPatchShapesElement = docEl.firstChildElement( u"legendpatchshapes"_s );
3184 e = legendPatchShapesElement.firstChildElement();
3185 for ( ; !e.isNull(); e = e.nextSiblingElement() )
3186 {
3187 const int entityAddedVersion = e.attribute( u"addedVersion"_s ).toInt();
3188 if ( entityAddedVersion != 0 && sinceVersion != -1 && entityAddedVersion <= sinceVersion )
3189 {
3190 // skip the shape, should already be present
3191 continue;
3192 }
3193
3194 if ( e.tagName() == "legendpatchshape"_L1 )
3195 {
3196 QString name = e.attribute( u"name"_s );
3197 QStringList tags;
3198 if ( e.hasAttribute( u"tags"_s ) )
3199 {
3200 tags = e.attribute( u"tags"_s ).split( ',' );
3201 }
3202 bool favorite = false;
3203 if ( e.hasAttribute( u"favorite"_s ) && e.attribute( u"favorite"_s ) == "1"_L1 )
3204 {
3205 favorite = true;
3206 }
3207
3208 QgsLegendPatchShape shape;
3209 const QDomElement shapeElem = e.firstChildElement();
3210 shape.readXml( shapeElem, QgsReadWriteContext() );
3211 addLegendPatchShape( name, shape );
3212 if ( mCurrentDB )
3213 {
3214 saveLegendPatchShape( name, shape, favorite, tags );
3215 }
3216 }
3217 else
3218 {
3219 QgsDebugError( "unknown tag: " + e.tagName() );
3220 }
3221 }
3222 }
3223
3224 // load 3d symbols
3225 if ( version == STYLE_CURRENT_VERSION )
3226 {
3227 const QDomElement symbols3DElement = docEl.firstChildElement( u"symbols3d"_s );
3228 e = symbols3DElement.firstChildElement();
3229 for ( ; !e.isNull(); e = e.nextSiblingElement() )
3230 {
3231 const int entityAddedVersion = e.attribute( u"addedVersion"_s ).toInt();
3232 if ( entityAddedVersion != 0 && sinceVersion != -1 && entityAddedVersion <= sinceVersion )
3233 {
3234 // skip the symbol, should already be present
3235 continue;
3236 }
3237
3238 if ( e.tagName() == "symbol3d"_L1 )
3239 {
3240 QString name = e.attribute( u"name"_s );
3241 QStringList tags;
3242 if ( e.hasAttribute( u"tags"_s ) )
3243 {
3244 tags = e.attribute( u"tags"_s ).split( ',' );
3245 }
3246 bool favorite = false;
3247 if ( e.hasAttribute( u"favorite"_s ) && e.attribute( u"favorite"_s ) == "1"_L1 )
3248 {
3249 favorite = true;
3250 }
3251
3252 const QDomElement symbolElem = e.firstChildElement();
3253 const QString type = symbolElem.attribute( u"type"_s );
3254 std::unique_ptr< QgsAbstract3DSymbol > sym( QgsApplication::symbol3DRegistry()->createSymbol( type ) );
3255 if ( sym )
3256 {
3257 sym->readXml( symbolElem, QgsReadWriteContext() );
3258 QgsAbstract3DSymbol *newSym = sym.get();
3259 addSymbol3D( name, sym.release() );
3260 if ( mCurrentDB )
3261 {
3262 saveSymbol3D( name, newSym, favorite, tags );
3263 }
3264 }
3265 }
3266 else
3267 {
3268 QgsDebugError( "unknown tag: " + e.tagName() );
3269 }
3270 }
3271 }
3272
3273 // load material settings
3274 if ( version == STYLE_CURRENT_VERSION )
3275 {
3276 const QDomElement materialSettingsElement = docEl.firstChildElement( u"materialsettings"_s );
3277 e = materialSettingsElement.firstChildElement();
3278 for ( ; !e.isNull(); e = e.nextSiblingElement() )
3279 {
3280 const int entityAddedVersion = e.attribute( u"addedVersion"_s ).toInt();
3281 if ( entityAddedVersion != 0 && sinceVersion != -1 && entityAddedVersion <= sinceVersion )
3282 {
3283 // skip the symbol, should already be present
3284 continue;
3285 }
3286
3287 if ( e.tagName() == "material"_L1 )
3288 {
3289 QString name = e.attribute( u"name"_s );
3290 QStringList tags;
3291 if ( e.hasAttribute( u"tags"_s ) )
3292 {
3293 tags = e.attribute( u"tags"_s ).split( ',' );
3294 }
3295 bool favorite = false;
3296 if ( e.hasAttribute( u"favorite"_s ) && e.attribute( u"favorite"_s ) == "1"_L1 )
3297 {
3298 favorite = true;
3299 }
3300
3301 const QDomElement materialElem = e.firstChildElement();
3302 const QString type = materialElem.attribute( u"type"_s );
3303 std::unique_ptr< QgsAbstractMaterialSettings > settings( QgsApplication::materialRegistry()->createMaterialSettings( type ) );
3304 if ( settings )
3305 {
3306 settings->readXml( materialElem, QgsReadWriteContext() );
3307 QgsAbstractMaterialSettings *newMaterial = settings.get();
3308 addMaterialSettings( name, settings.release() );
3309 if ( mCurrentDB )
3310 {
3311 saveMaterialSettings( name, newMaterial, favorite, tags );
3312 }
3313 }
3314 }
3315 else
3316 {
3317 QgsDebugError( "unknown tag: " + e.tagName() );
3318 }
3319 }
3320 }
3321
3322 query = qgs_sqlite3_mprintf( "COMMIT TRANSACTION;" );
3323 runEmptyQuery( query );
3324
3325 return true;
3326}
3327
3328bool QgsStyle::isXmlStyleFile( const QString &path )
3329{
3330 QFileInfo fileInfo( path );
3331
3332 if ( fileInfo.suffix().compare( "xml"_L1, Qt::CaseInsensitive ) != 0 )
3333 return false;
3334
3335 // sniff the first line of the file to see if it's a style file
3336 if ( !QFile::exists( path ) )
3337 return false;
3338
3339 QFile inputFile( path );
3340 if ( !inputFile.open( QIODevice::ReadOnly ) )
3341 return false;
3342
3343 QTextStream stream( &inputFile );
3344 const QString line = stream.readLine();
3345 return line == "<!DOCTYPE qgis_style>"_L1;
3346}
3347
3352
3354{
3355 return mReadOnly;
3356}
3357
3358void QgsStyle::setReadOnly( bool readOnly )
3359{
3360 mReadOnly = readOnly;
3361}
3362
3363bool QgsStyle::updateSymbol( StyleEntity type, const QString &name )
3364{
3365 QDomDocument doc( u"dummy"_s );
3366 QDomElement symEl;
3367 QByteArray xmlArray;
3368 QTextStream stream( &xmlArray );
3369
3370 QString query;
3371
3372 switch ( type )
3373 {
3374 case SymbolEntity:
3375 {
3376 // check if it is an existing symbol
3377 auto it = mSymbols.constFind( name );
3378 if ( it == mSymbols.constEnd() || !it.value() )
3379 {
3380 QgsDebugError( u"Update request received for unavailable symbol"_s );
3381 return false;
3382 }
3383
3384 symEl = QgsSymbolLayerUtils::saveSymbol( name, it.value(), doc, QgsReadWriteContext() );
3385 if ( symEl.isNull() )
3386 {
3387 QgsDebugError( u"Couldn't convert symbol to valid XML!"_s );
3388 return false;
3389 }
3390 symEl.save( stream, 4 );
3391 query = qgs_sqlite3_mprintf( "UPDATE symbol SET xml='%q' WHERE name='%q';", xmlArray.constData(), name.toUtf8().constData() );
3392 break;
3393 }
3394
3395 case Symbol3DEntity:
3396 {
3397 // check if it is an existing symbol
3398 auto it = m3dSymbols.constFind( name );
3399 if ( it == m3dSymbols.constEnd() || !it.value() )
3400 {
3401 QgsDebugError( u"Update request received for unavailable symbol"_s );
3402 return false;
3403 }
3404
3405 symEl = doc.createElement( u"symbol"_s );
3406 symEl.setAttribute( u"type"_s, it.value()->type() );
3407 it.value()->writeXml( symEl, QgsReadWriteContext() );
3408 if ( symEl.isNull() )
3409 {
3410 QgsDebugError( u"Couldn't convert symbol to valid XML!"_s );
3411 return false;
3412 }
3413 symEl.save( stream, 4 );
3414 query = qgs_sqlite3_mprintf( "UPDATE symbol3d SET xml='%q' WHERE name='%q';", xmlArray.constData(), name.toUtf8().constData() );
3415 break;
3416 }
3417
3419 {
3420 // check if it is an existing symbol
3421 auto it = mMaterialSettings.constFind( name );
3422 if ( it == mMaterialSettings.constEnd() || !it.value() )
3423 {
3424 QgsDebugError( u"Update request received for unavailable material"_s );
3425 return false;
3426 }
3427
3428 symEl = doc.createElement( u"material"_s );
3429 it.value()->writeXml( symEl, QgsReadWriteContext() );
3430 if ( symEl.isNull() )
3431 {
3432 QgsDebugError( u"Couldn't convert material settings to valid XML!"_s );
3433 return false;
3434 }
3435 symEl.save( stream, 4 );
3436 query = qgs_sqlite3_mprintf( "UPDATE materialsettings SET xml='%q' WHERE name='%q';", xmlArray.constData(), name.toUtf8().constData() );
3437 break;
3438 }
3439
3440 case ColorrampEntity:
3441 {
3442 auto it = mColorRamps.constFind( name );
3443 if ( it == mColorRamps.constEnd() || !it.value() )
3444 {
3445 QgsDebugError( u"Update requested for unavailable color ramp."_s );
3446 return false;
3447 }
3448
3449 symEl = QgsSymbolLayerUtils::saveColorRamp( name, it.value(), doc );
3450 if ( symEl.isNull() )
3451 {
3452 QgsDebugError( u"Couldn't convert color ramp to valid XML!"_s );
3453 return false;
3454 }
3455 symEl.save( stream, 4 );
3456 query = qgs_sqlite3_mprintf( "UPDATE colorramp SET xml='%q' WHERE name='%q';", xmlArray.constData(), name.toUtf8().constData() );
3457 break;
3458 }
3459
3460 case TextFormatEntity:
3461 {
3462 auto it = mTextFormats.constFind( name );
3463 if ( it == mTextFormats.constEnd() )
3464 {
3465 QgsDebugError( u"Update requested for unavailable text format."_s );
3466 return false;
3467 }
3468
3469 symEl = it.value().writeXml( doc, QgsReadWriteContext() );
3470 if ( symEl.isNull() )
3471 {
3472 QgsDebugError( u"Couldn't convert text format to valid XML!"_s );
3473 return false;
3474 }
3475 symEl.save( stream, 4 );
3476 query = qgs_sqlite3_mprintf( "UPDATE textformat SET xml='%q' WHERE name='%q';", xmlArray.constData(), name.toUtf8().constData() );
3477 break;
3478 }
3479
3481 {
3482 auto it = mLabelSettings.constFind( name );
3483 if ( it == mLabelSettings.constEnd() )
3484 {
3485 QgsDebugError( u"Update requested for unavailable label settings."_s );
3486 return false;
3487 }
3488
3489 symEl = it.value().writeXml( doc, QgsReadWriteContext() );
3490 if ( symEl.isNull() )
3491 {
3492 QgsDebugError( u"Couldn't convert label settings to valid XML!"_s );
3493 return false;
3494 }
3495 symEl.save( stream, 4 );
3496 query = qgs_sqlite3_mprintf( "UPDATE labelsettings SET xml='%q' WHERE name='%q';", xmlArray.constData(), name.toUtf8().constData() );
3497 break;
3498 }
3499
3501 {
3502 auto it = mLegendPatchShapes.constFind( name );
3503 if ( it == mLegendPatchShapes.constEnd() )
3504 {
3505 QgsDebugError( u"Update requested for unavailable legend patch shape."_s );
3506 return false;
3507 }
3508
3509 symEl = doc.createElement( u"shape"_s );
3510 it.value().writeXml( symEl, doc, QgsReadWriteContext() );
3511 symEl.save( stream, 4 );
3512 query = qgs_sqlite3_mprintf( "UPDATE legendpatchshapes SET xml='%q' WHERE name='%q';", xmlArray.constData(), name.toUtf8().constData() );
3513 break;
3514 }
3515
3516 case TagEntity:
3517 case SmartgroupEntity:
3518 {
3519 QgsDebugError( u"Updating the unsupported StyleEntity"_s );
3520 return false;
3521 }
3522 }
3523
3524
3525 if ( !runEmptyQuery( query ) )
3526 {
3527 QgsDebugError( u"Couldn't update symbol into the database!"_s );
3528 return false;
3529 }
3530 else
3531 {
3532 switch ( type )
3533 {
3534 case SymbolEntity:
3535 emit symbolChanged( name );
3536 break;
3537
3538 case ColorrampEntity:
3539 emit rampChanged( name );
3540 break;
3541
3542 case TextFormatEntity:
3543 emit textFormatChanged( name );
3544 break;
3545
3547 emit labelSettingsChanged( name );
3548 break;
3549
3551 case TagEntity:
3552 case SmartgroupEntity:
3553 case Symbol3DEntity:
3555 break;
3556 }
3557 emit entityChanged( type, name );
3558 }
3559 return true;
3560}
3561
3562void QgsStyle::clearCachedTags( QgsStyle::StyleEntity type, const QString &name )
3563{
3564 mCachedTags[type].remove( name );
3565}
3566
3567bool QgsStyle::createStyleMetadataTableIfNeeded()
3568{
3569 // make sure metadata table exists
3570 QString query = qgs_sqlite3_mprintf( "SELECT name FROM sqlite_master WHERE name='stylemetadata'" );
3571 sqlite3_statement_unique_ptr statement;
3572 int rc;
3573 statement = mCurrentDB.prepare( query, rc );
3574
3575 if ( rc != SQLITE_OK || sqlite3_step( statement.get() ) != SQLITE_ROW )
3576 {
3577 // no metadata table
3578 query = qgs_sqlite3_mprintf(
3579 "CREATE TABLE stylemetadata("
3580 "id INTEGER PRIMARY KEY,"
3581 "key TEXT UNIQUE,"
3582 "value TEXT);"
3583 );
3584 runEmptyQuery( query );
3585 query = qgs_sqlite3_mprintf( "INSERT INTO stylemetadata VALUES (NULL, '%q', '%q')", "version", QString::number( Qgis::versionInt() ).toUtf8().constData() );
3586 runEmptyQuery( query );
3587 return true;
3588 }
3589 else
3590 {
3591 return false;
3592 }
3593}
3594
3595void QgsStyle::upgradeIfRequired()
3596{
3597 // make sure metadata table exists
3598 int dbVersion = 0;
3599 if ( !createStyleMetadataTableIfNeeded() )
3600 {
3601 const QString query = qgs_sqlite3_mprintf( "SELECT value FROM stylemetadata WHERE key='version'" );
3602 int rc;
3603 sqlite3_statement_unique_ptr statement = mCurrentDB.prepare( query, rc );
3604 if ( rc == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
3605 {
3606 dbVersion = statement.columnAsText( 0 ).toInt();
3607 }
3608 }
3609
3610 if ( dbVersion < Qgis::versionInt() )
3611 {
3612 // do upgrade
3613 if ( importXml( QgsApplication::defaultStylePath(), dbVersion ) )
3614 {
3615 const QString query = qgs_sqlite3_mprintf( "UPDATE stylemetadata SET value='%q' WHERE key='version'", QString::number( Qgis::versionInt() ).toUtf8().constData() );
3616 runEmptyQuery( query );
3617 }
3618 }
3619}
3620
3621QString QgsStyle::entityTableName( QgsStyle::StyleEntity type )
3622{
3623 switch ( type )
3624 {
3625 case SymbolEntity:
3626 return u"symbol"_s;
3627
3628 case ColorrampEntity:
3629 return u"colorramp"_s;
3630
3631 case TextFormatEntity:
3632 return u"textformat"_s;
3633
3635 return u"labelsettings"_s;
3636
3638 return u"legendpatchshapes"_s;
3639
3640 case Symbol3DEntity:
3641 return u"symbol3d"_s;
3642
3644 return u"materialsettings"_s;
3645
3646 case TagEntity:
3647 return u"tag"_s;
3648
3649 case SmartgroupEntity:
3650 return u"smartgroup"_s;
3651 }
3652 return QString();
3653}
3654
3655QString QgsStyle::tagmapTableName( QgsStyle::StyleEntity type )
3656{
3657 switch ( type )
3658 {
3659 case SymbolEntity:
3660 return u"tagmap"_s;
3661
3662 case ColorrampEntity:
3663 return u"ctagmap"_s;
3664
3665 case TextFormatEntity:
3666 return u"tftagmap"_s;
3667
3669 return u"lstagmap"_s;
3670
3672 return u"lpstagmap"_s;
3673
3674 case Symbol3DEntity:
3675 return u"symbol3dtagmap"_s;
3676
3678 return u"materialsettingstagmap"_s;
3679
3680 case TagEntity:
3681 case SmartgroupEntity:
3682 break;
3683 }
3684 return QString();
3685}
3686
3687QString QgsStyle::tagmapEntityIdFieldName( QgsStyle::StyleEntity type )
3688{
3689 switch ( type )
3690 {
3691 case SymbolEntity:
3692 return u"symbol_id"_s;
3693
3694 case ColorrampEntity:
3695 return u"colorramp_id"_s;
3696
3697 case TextFormatEntity:
3698 return u"textformat_id"_s;
3699
3701 return u"labelsettings_id"_s;
3702
3704 return u"legendpatchshape_id"_s;
3705
3706 case Symbol3DEntity:
3707 return u"symbol3d_id"_s;
3708
3710 return u"materialsettings_id"_s;
3711
3712 case TagEntity:
3713 case SmartgroupEntity:
3714 break;
3715 }
3716 return QString();
3717}
3718
3723
3728
3733
3738
3743
3748
@ ScaleArea
Calculate scale by the area.
Definition qgis.h:651
@ Circle
Circle.
Definition qgis.h:3255
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:379
@ Unknown
Unknown types.
Definition qgis.h:383
static int versionInt()
Version number used for comparing versions using the "Check QGIS Version" function.
Definition qgis.cpp:687
SymbolType
Symbol types.
Definition qgis.h:636
@ Marker
Marker symbol.
Definition qgis.h:637
@ Line
Line symbol.
Definition qgis.h:638
@ Fill
Fill symbol.
Definition qgis.h:639
@ Hybrid
Hybrid symbol.
Definition qgis.h:640
QStringList symbolTypes() const
Returns a list of all available symbol types.
Abstract base class for 3D symbols that are used by VectorLayer3DRenderer objects.
virtual QgsAbstract3DSymbol * clone() const =0
Returns a new instance of the symbol with the same settings.
Abstract base class for material settings.
virtual void writeXml(QDomElement &element, const QgsReadWriteContext &) const
Writes settings to a DOM element.
virtual QString type() const =0
Returns the unique type name for the material.
static QString userStylePath()
Returns the path to user's style.
static QString defaultStylePath()
Returns the path to default style (works as a starting point).
static QgsMaterialRegistry * materialRegistry()
Returns registry of available 3D materials.
static Qgs3DSymbolRegistry * symbol3DRegistry()
Returns registry of available 3D symbols.
Abstract base class for color ramps.
virtual double value(int index) const =0
Returns relative value between [0,1] of color at specified index.
virtual QgsColorRamp * clone() const =0
Creates a clone of the color ramp.
A geometry is the spatial representation of a feature.
Represents a patch shape for use in map legends.
void readXml(const QDomElement &element, const QgsReadWriteContext &context)
Read settings from a DOM element.
QList< QList< QPolygonF > > toQPolygonF(Qgis::SymbolType type, QSizeF size) const
Converts the patch shape to a set of QPolygonF objects representing how the patch should be drawn for...
void writeXml(QDomElement &element, QDomDocument &doc, const QgsReadWriteContext &context) const
Write settings into a DOM element.
Qgis::SymbolType symbolType() const
Returns the symbol type associated with this patch.
Line string geometry type, with support for z-dimension and m-values.
std::unique_ptr< QgsAbstractMaterialSettings > createMaterialSettings(const QString &type) const
Creates a new instance of the material settings of the specified type.
Contains settings for how a map layer will be labeled.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Read settings from a DOM element.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Write settings into a DOM element.
QgsTextFormat defaultTextFormat() const
Returns the project default text format.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:113
const QgsProjectStyleSettings * styleSettings() const
Returns the project's style settings, which contains settings and properties relating to how a QgsPro...
A container for the context for various read/write operations on objects.
Scoped object for logging of the runtime for a single operation or group of operations.
static const QgsSettingsEntryInteger * settingsSymbolsListGroupsIndex
Settings entry for symbols list groups index.
A color ramp entity for QgsStyle databases.
Definition qgsstyle.h:1491
QgsStyle::StyleEntity type() const override
Returns the type of style entity.
An interface for entities which can be placed in a QgsStyle database.
Definition qgsstyle.h:1404
virtual QgsStyle::StyleEntity type() const =0
Returns the type of style entity.
A label settings entity for QgsStyle databases.
Definition qgsstyle.h:1547
QgsStyle::StyleEntity type() const override
Returns the type of style entity.
A legend patch shape entity for QgsStyle databases.
Definition qgsstyle.h:1575
QgsStyle::StyleEntity type() const override
Returns the type of style entity.
A 3D material settings entity for QgsStyle databases.
Definition qgsstyle.h:1633
QgsStyle::StyleEntity type() const override
Returns the type of style entity.
A 3d symbol entity for QgsStyle databases.
Definition qgsstyle.h:1603
QgsStyle::StyleEntity type() const override
Returns the type of style entity.
A symbol entity for QgsStyle databases.
Definition qgsstyle.h:1462
QgsStyle::StyleEntity type() const override
Returns the type of style entity.
A text format entity for QgsStyle databases.
Definition qgsstyle.h:1520
QgsStyle::StyleEntity type() const override
Returns the type of style entity.
A database of saved style entities, including symbols, color ramps, text formats and others.
Definition qgsstyle.h:91
bool addEntity(const QString &name, const QgsStyleEntityInterface *entity, bool update=false)
Adds an entity to the style, with the specified name.
Definition qgsstyle.cpp:128
void setFileName(const QString &filename)
Sets the current file name of the style database.
void labelSettingsChanged(const QString &name)
Emitted whenever a label setting's definition is changed.
int colorRampCount()
Returns count of color ramps.
Definition qgsstyle.cpp:546
bool detagSymbol(StyleEntity type, const QString &symbol, const QStringList &tags)
Detags the symbol with the given list.
QgsTextFormat textFormat(const QString &name) const
Returns the text format with the specified name.
QStringList allNames(StyleEntity type) const
Returns a list of the names of all existing entities of the specified type.
QgsLegendPatchShape defaultPatch(Qgis::SymbolType type, QSizeF size) const
Returns the default legend patch shape for the given symbol type.
bool remove(StyleEntity type, int id)
Removes the specified entity from the database.
bool removeSymbol(const QString &name)
Removes symbol from style (and delete it).
Definition qgsstyle.cpp:299
void entityChanged(QgsStyle::StyleEntity entity, const QString &name)
Emitted whenever an entity's definition is changed.
bool removeLabelSettings(const QString &name)
Removes label settings from the style.
void labelSettingsAdded(const QString &name)
Emitted whenever label settings have been added to the style and the database has been updated as a r...
QStringList tags() const
Returns a list of all tags in the style database.
QString tag(int id) const
Returns the tag name for the given id.
QgsSmartConditionMap smartgroup(int id)
Returns the QgsSmartConditionMap for the given id.
bool renameColorRamp(const QString &oldName, const QString &newName)
Changes ramp's name.
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...
QStringList symbol3DNames() const
Returns a list of names of 3d symbols in the style.
bool tagSymbol(StyleEntity type, const QString &symbol, const QStringList &tags)
Tags the symbol with the tags in the list.
int entityId(StyleEntity type, const QString &name)
Returns the id in the style database for the given name of the specified entity type.
void rebuildIconPreviews()
Emitted whenever icon previews for entities in the style must be rebuilt.
bool saveLabelSettings(const QString &name, const QgsPalLayerSettings &settings, bool favorite, const QStringList &tags)
Adds label settings to the database.
Q_DECL_DEPRECATED bool save(const QString &filename=QString())
Saves style into a file.
Definition qgsstyle.cpp:995
bool symbolHasTag(StyleEntity type, const QString &symbol, const QString &tag)
Returns whether a given tag is associated with the symbol.
void aboutToBeDestroyed()
Emitted just before the style object is destroyed.
QgsTextFormat defaultTextFormat(QgsStyle::TextFormatContext context=QgsStyle::TextFormatContext::Labeling) const
Returns the default text format to use for new text based objects in the specified context.
@ XML
Label settings definition (as XML).
Definition qgsstyle.h:170
@ Name
Label settings name.
Definition qgsstyle.h:169
bool createDatabase(const QString &filename)
Creates an on-disk database.
Definition qgsstyle.cpp:589
QStringList textFormatNames() const
Returns a list of names of text formats in the style.
const QgsColorRamp * colorRampRef(const QString &name) const
Returns a const pointer to a symbol (doesn't create new instance).
Definition qgsstyle.cpp:541
QStringList symbolsWithTag(StyleEntity type, int tagid) const
Returns the symbol names with which have the given tag.
bool addColorRamp(const QString &name, QgsColorRamp *colorRamp, bool update=false)
Adds a color ramp to the style.
Definition qgsstyle.cpp:363
bool removeTextFormat(const QString &name)
Removes a text format from the style.
QgsSymbol * symbol(const QString &name)
Returns a NEW copy of symbol.
Definition qgsstyle.cpp:336
void labelSettingsRemoved(const QString &name)
Emitted whenever label settings have been removed from the style and the database has been updated as...
QStringList symbolsOfSmartgroup(StyleEntity type, int id)
Returns the symbols for the smartgroup.
int labelSettingsCount() const
Returns count of label settings in the style.
StyleEntity
Enum for Entities involved in a style.
Definition qgsstyle.h:206
@ LabelSettingsEntity
Label settings.
Definition qgsstyle.h:212
@ TextFormatEntity
Text formats.
Definition qgsstyle.h:211
@ SmartgroupEntity
Smart groups.
Definition qgsstyle.h:210
@ Symbol3DEntity
3D symbol entity
Definition qgsstyle.h:214
@ SymbolEntity
Symbols.
Definition qgsstyle.h:207
@ TagEntity
Tags.
Definition qgsstyle.h:208
@ ColorrampEntity
Color ramps.
Definition qgsstyle.h:209
@ LegendPatchShapeEntity
Legend patch shape.
Definition qgsstyle.h:213
@ MaterialSettingsEntity
Material settings.
Definition qgsstyle.h:215
QStringList tagsOfSymbol(StyleEntity type, const QString &symbol)
Returns the tags associated with the symbol.
void symbolRenamed(const QString &oldName, const QString &newName)
Emitted whenever a symbol has been renamed from oldName to newName.
QList< Qgis::GeometryType > symbol3DCompatibleGeometryTypes(const QString &name) const
Returns the list of the vector layer geometry types which are compatible with the 3D symbol with the ...
@ XML
Color ramp definition (as XML).
Definition qgsstyle.h:142
void groupsModified()
Emitted every time a tag or smartgroup has been added, removed, or renamed.
void clear()
Removes all contents of the style.
Definition qgsstyle.cpp:227
int smartgroupId(const QString &smartgroup)
Returns the database id for the given smartgroup name.
void rampRemoved(const QString &name)
Emitted whenever a color ramp has been removed from the style and the database has been updated as a ...
void entityRenamed(QgsStyle::StyleEntity entity, const QString &oldName, const QString &newName)
Emitted whenever a entity of the specified type has been renamed from oldName to newName.
const QgsSymbol * symbolRef(const QString &name) const
Returns a const pointer to a symbol (doesn't create new instance).
Definition qgsstyle.cpp:348
int addSmartgroup(const QString &name, const QString &op, const QgsSmartConditionMap &conditions)
Adds a new smartgroup to the database and returns the id.
bool saveMaterialSettings(const QString &name, QgsAbstractMaterialSettings *settings, bool favorite, const QStringList &tags)
Adds 3D material settings to the database.
QStringList colorRampNames() const
Returns a list of names of color ramps.
Definition qgsstyle.cpp:551
std::unique_ptr< QgsAbstractMaterialSettings > materialSettings(const QString &name) const
Returns a new copy of the 3D material settings with the specified name.
void textFormatChanged(const QString &name)
Emitted whenever a text format's definition is changed.
bool addSymbol3D(const QString &name, QgsAbstract3DSymbol *symbol, bool update=false)
Adds a 3d symbol with the specified name to the style.
Definition qgsstyle.cpp:454
QStringList legendPatchShapeNames() const
Returns a list of names of legend patch shapes in the style.
bool renameLegendPatchShape(const QString &oldName, const QString &newName)
Changes a legend patch shape's name.
static void cleanDefaultStyle()
Deletes the default style. Only to be used by QgsApplication::exitQgis().
Definition qgsstyle.cpp:221
void symbolSaved(const QString &name, const QgsSymbol *symbol)
Emitted every time a new symbol has been added to the database.
void textFormatRenamed(const QString &oldName, const QString &newName)
Emitted whenever a text format has been renamed from oldName to newName.
void triggerIconRebuild()
Triggers emission of the rebuildIconPreviews() signal.
void setName(const QString &name)
Sets the name of the style.
Definition qgsstyle.cpp:118
bool removeEntityByName(StyleEntity type, const QString &name)
Removes the entry of the specified type with matching name from the database.
void labelSettingsRenamed(const QString &oldName, const QString &newName)
Emitted whenever label settings have been renamed from oldName to newName.
void initialized()
Emitted when the style database has been fully initialized.
@ XML
Text format definition (as XML).
Definition qgsstyle.h:156
@ Name
Text format name.
Definition qgsstyle.h:155
int textFormatCount() const
Returns count of text formats in the style.
QStringList findSymbols(StyleEntity type, const QString &qword)
Returns the names of the symbols which have a matching 'substring' in its definition.
int tagId(const QString &tag)
Returns the database id for the given tag name.
void rampRenamed(const QString &oldName, const QString &newName)
Emitted whenever a color ramp has been renamed from oldName to newName.
bool exportXml(const QString &filename)
Exports the style as a XML file.
void createTables()
Creates tables structure for new database.
Definition qgsstyle.cpp:619
bool isReadOnly() const
Returns true if the style is considered a read-only library.
bool addLegendPatchShape(const QString &name, const QgsLegendPatchShape &shape, bool update=false)
Adds a legend patch shape with the specified name to the style.
Definition qgsstyle.cpp:432
int textFormatId(const QString &name)
Returns the ID in the style database for the given text format by name.
QgsColorRamp * colorRamp(const QString &name) const
Returns a new copy of the specified color ramp.
Definition qgsstyle.cpp:535
int symbolCount()
Returns count of symbols in style.
Definition qgsstyle.cpp:353
bool addMaterialSettings(const QString &name, QgsAbstractMaterialSettings *settings, bool update=false)
Adds a 3D material settings with the specified name to the style.
Definition qgsstyle.cpp:476
bool renameEntity(StyleEntity type, const QString &oldName, const QString &newName)
Renames an entity of the specified type from oldName to newName.
Definition qgsstyle.cpp:304
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition qgsstyle.cpp:164
QStringList materialSettingsNames() const
Returns a list of names of 3D material settings in the style.
bool renameMaterialSettings(const QString &oldName, const QString &newName)
Changes a 3D material settings's name.
@ Name
Smart group name.
Definition qgsstyle.h:183
static bool isXmlStyleFile(const QString &path)
Tests if the file at path is a QGIS style XML file.
int symbol3DCount() const
Returns count of 3D symbols in the style.
bool createMemoryDatabase()
Creates a temporary memory database.
Definition qgsstyle.cpp:604
int colorrampId(const QString &name)
Returns the id in the style database for the given colorramp name returns 0 if not found.
bool saveSymbol(const QString &name, const QgsSymbol *symbol, bool favorite, const QStringList &tags)
Adds the symbol to the database with tags.
Definition qgsstyle.cpp:267
QStringList labelSettingsNames() const
Returns a list of names of label settings in the style.
bool load(const QString &filename)
Loads a file into the style.
Definition qgsstyle.cpp:689
QStringList smartgroupNames() const
Returns the smart groups list.
bool rename(StyleEntity type, int id, const QString &newName)
Renames the given entity with the specified id.
bool renameTextFormat(const QString &oldName, const QString &newName)
Changes a text format's name.
void textFormatAdded(const QString &name)
Emitted whenever a text format has been added to the style and the database has been updated as a res...
bool removeFavorite(StyleEntity type, const QString &name)
Removes the specified symbol from favorites.
static QgsTextFormat defaultTextFormatForProject(QgsProject *project, QgsStyle::TextFormatContext context=QgsStyle::TextFormatContext::Labeling)
Returns the default text format to use for new text based objects for the specified project,...
int legendPatchShapesCount() const
Returns count of legend patch shapes in the style.
QgsSymbolGroupMap smartgroupsListMap()
Returns the smart groups map with id as key and name as value.
bool isFavorite(StyleEntity type, const QString &name)
Returns true if the symbol with matching type and name is marked as a favorite.
int symbolId(const QString &name)
Returns the id in the style database for the given symbol name returns 0 if not found.
bool saveLegendPatchShape(const QString &name, const QgsLegendPatchShape &shape, bool favorite, const QStringList &tags)
Adds a legend patch shape to the database.
QString name() const
Returns the name of the style.
Definition qgsstyle.cpp:123
int materialSettingsCount() const
Returns count of 3D material settings in the style.
QgsStyle(QObject *parent=nullptr)
Constructor for QgsStyle, with the specified parent object.
Definition qgsstyle.cpp:97
Qgis::GeometryType labelSettingsLayerType(const QString &name) const
Returns the layer geometry type corresponding to the label settings with the specified name,...
@ XML
Symbol definition (as XML).
Definition qgsstyle.h:104
bool renameLabelSettings(const QString &oldName, const QString &newName)
Changes a label setting's name.
bool renameSymbol3D(const QString &oldName, const QString &newName)
Changes a 3d symbol's name.
bool addTextFormat(const QString &name, const QgsTextFormat &format, bool update=false)
Adds a text format with the specified name to the style.
Definition qgsstyle.cpp:388
bool saveColorRamp(const QString &name, const QgsColorRamp *ramp, bool favorite, const QStringList &tags)
Adds the colorramp to the database.
Definition qgsstyle.cpp:498
void rampChanged(const QString &name)
Emitted whenever a color ramp's definition is changed.
QStringList symbolsOfFavorite(StyleEntity type) const
Returns the symbol names which are flagged as favorite.
void entityTagsChanged(QgsStyle::StyleEntity entity, const QString &name, const QStringList &newTags)
Emitted whenever an entity's tags are changed.
bool renameSymbol(const QString &oldName, const QString &newName)
Renames a symbol from oldName to newName.
bool removeColorRamp(const QString &name)
Removes color ramp from style (and delete it).
Definition qgsstyle.cpp:530
bool saveSymbol3D(const QString &name, QgsAbstract3DSymbol *symbol, bool favorite, const QStringList &tags)
Adds a 3d symbol to the database.
void favoritedChanged(QgsStyle::StyleEntity entity, const QString &name, bool isFavorite)
Emitted whenever an entity is either favorited or un-favorited.
QgsPalLayerSettings labelSettings(const QString &name) const
Returns the label settings with the specified name.
~QgsStyle() override
Definition qgsstyle.cpp:112
void entityRemoved(QgsStyle::StyleEntity entity, const QString &name)
Emitted whenever an entity of the specified type is removed from the style and the database has been ...
int labelSettingsId(const QString &name)
Returns the ID in the style database for the given label settings by name.
Qgis::SymbolType legendPatchShapeSymbolType(const QString &name) const
Returns the symbol type corresponding to the legend patch shape with the specified name,...
QgsAbstract3DSymbol * symbol3D(const QString &name) const
Returns a new copy of the 3D symbol with the specified name.
int addTag(const QString &tagName)
Adds a new tag and returns the tag's id.
const QgsSymbol * previewSymbolForPatchShape(const QgsLegendPatchShape &shape) const
Returns a symbol to use for rendering preview icons for a patch shape.
bool addSymbol(const QString &name, QgsSymbol *symbol, bool update=false)
Adds a symbol to style and takes symbol's ownership.
Definition qgsstyle.cpp:242
QgsLegendPatchShape legendPatchShape(const QString &name) const
Returns the legend patch shape with the specified name.
void entityAdded(QgsStyle::StyleEntity entity, const QString &name)
Emitted every time a new entity has been added to the database.
QStringList symbolNames() const
Returns a list of names of symbols.
Definition qgsstyle.cpp:358
TextFormatContext
Text format context.
Definition qgsstyle.h:827
void symbolRemoved(const QString &name)
Emitted whenever a symbol has been removed from the style and the database has been updated as a resu...
void symbolChanged(const QString &name)
Emitted whenever a symbol's definition is changed.
bool addFavorite(StyleEntity type, const QString &name)
Adds the specified symbol to favorites.
QString smartgroupOperator(int id)
Returns the operator for the smartgroup.
bool saveTextFormat(const QString &name, const QgsTextFormat &format, bool favorite, const QStringList &tags)
Adds a text format to the database.
QList< QList< QPolygonF > > defaultPatchAsQPolygonF(Qgis::SymbolType type, QSizeF size) const
Returns the default patch geometry for the given symbol type and size as a set of QPolygonF objects (...
void textFormatRemoved(const QString &name)
Emitted whenever a text format has been removed from the style and the database has been updated as a...
void setReadOnly(bool readOnly)
Sets whether the style is considered a read-only library.
bool importXml(const QString &filename)
Imports the symbols and colorramps into the default style database from the given XML file.
bool addLabelSettings(const QString &name, const QgsPalLayerSettings &settings, bool update=false)
Adds label settings with the specified name to the style.
Definition qgsstyle.cpp:410
static std::unique_ptr< QgsColorRamp > loadColorRamp(QDomElement &element)
Creates a color ramp from the settings encoded in an XML element.
static std::unique_ptr< QgsSymbol > loadSymbol(const QDomElement &element, const QgsReadWriteContext &context)
Attempts to load a symbol from a DOM element.
static QDomElement saveSymbol(const QString &symbolName, const QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
static QDomElement saveColorRamp(const QString &name, const QgsColorRamp *ramp, QDomDocument &doc)
Encodes a color ramp's settings to an XML element.
static void resetSymbolLayerIds(QgsSymbol *symbol)
Regenerate recursively unique id from all symbol symbol layers.
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 ...
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.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:227
Container for all settings relating to text rendering.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Read settings from a DOM element.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Write settings into a DOM element.
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
QString columnAsText(int column) const
Returns the column value from the current statement row as a string.
QMultiMap< QString, QString > QgsSmartConditionMap
A multimap to hold the smart group conditions as constraint and parameter pairs.
Definition qgsstyle.h:82
#define QgsDebugError(str)
Definition qgslogger.h:59
QMap< QString, QgsSymbol * > QgsSymbolMap
Definition qgsrenderer.h:52
QString qgs_sqlite3_mprintf(const char *format,...)
Wraps sqlite3_mprintf() by automatically freeing the memory.
LegendPatchTable
Columns available in the legend patch table.
Definition qgsstyle.cpp:66
@ LegendPatchTableId
Legend patch ID.
Definition qgsstyle.cpp:67
@ LegendPatchTableName
Legend patch name.
Definition qgsstyle.cpp:68
@ LegendPatchTableFavoriteId
Legend patch is favorite flag.
Definition qgsstyle.cpp:70
@ LegendPatchTableXML
Legend patch definition (as XML).
Definition qgsstyle.cpp:69
#define STYLE_CURRENT_VERSION
Definition qgsstyle.cpp:60
MaterialSettingsTable
Columns available in the 3D material settings table.
Definition qgsstyle.cpp:88
@ MaterialSettingsTableId
3d material settings ID
Definition qgsstyle.cpp:89
@ MaterialSettingsTableName
3d material settings name
Definition qgsstyle.cpp:90
@ MaterialSettingsTableFavoriteId
3d material settings is favorite flag
Definition qgsstyle.cpp:92
@ MaterialSettingsTableXML
3d material settings definition (as XML)
Definition qgsstyle.cpp:91
Symbol3DTable
Columns available in the 3d symbol table.
Definition qgsstyle.cpp:77
@ Symbol3DTableXML
3d symbol definition (as XML)
Definition qgsstyle.cpp:80
@ Symbol3DTableName
3d symbol name
Definition qgsstyle.cpp:79
@ Symbol3DTableFavoriteId
3d symbol is favorite flag
Definition qgsstyle.cpp:81
@ Symbol3DTableId
3d symbol ID
Definition qgsstyle.cpp:78
QMap< int, QString > QgsSymbolGroupMap
Definition qgsstyle.h:43
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition qgssymbol.h:30