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