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