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