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