QGIS API Documentation 3.99.0-Master (b3fe4c4eded)
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 settingsElem.save( stream, 4 );
1066 QString query = qgs_sqlite3_mprintf( "INSERT INTO labelsettings VALUES (NULL, '%q', '%q', %d);",
1067 name.toUtf8().constData(), xmlArray.constData(), ( favorite ? 1 : 0 ) );
1068 if ( !runEmptyQuery( query ) )
1069 {
1070 QgsDebugError( u"Couldn't insert label settings into the database!"_s );
1071 return false;
1072 }
1073
1074 mCachedFavorites[ LabelSettingsEntity ].insert( name, favorite );
1075
1077
1078 emit labelSettingsAdded( name );
1080
1081 return true;
1082}
1083
1085{
1087}
1088
1089bool QgsStyle::renameLabelSettings( const QString &oldName, const QString &newName )
1090{
1091 if ( mLabelSettings.contains( newName ) )
1092 {
1093 QgsDebugError( u"Label settings of new name already exists."_s );
1094 return false;
1095 }
1096
1097 if ( !mLabelSettings.contains( oldName ) )
1098 return false;
1099 QgsPalLayerSettings settings = mLabelSettings.take( oldName );
1100
1101 mLabelSettings.insert( newName, settings );
1102 mCachedTags[ LabelSettingsEntity ].remove( oldName );
1103 mCachedFavorites[ LabelSettingsEntity ].remove( oldName );
1104
1105 int labelSettingsId = 0;
1107 QString query = qgs_sqlite3_mprintf( "SELECT id FROM labelsettings WHERE name='%q'", oldName.toUtf8().constData() );
1108 int nErr;
1109 statement = mCurrentDB.prepare( query, nErr );
1110 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1111 {
1112 labelSettingsId = sqlite3_column_int( statement.get(), 0 );
1113 }
1114 const bool result = rename( LabelSettingsEntity, labelSettingsId, newName );
1115 if ( result )
1116 {
1117 emit labelSettingsRenamed( oldName, newName );
1118 emit entityRenamed( LabelSettingsEntity, oldName, newName );
1119 }
1120
1121 return result;
1122}
1123
1124bool QgsStyle::saveLegendPatchShape( const QString &name, const QgsLegendPatchShape &shape, bool favorite, const QStringList &tags )
1125{
1126 // insert it into the database
1127 QDomDocument doc( u"dummy"_s );
1128 QDomElement shapeElem = doc.createElement( u"shape"_s );
1129 shape.writeXml( shapeElem, doc, QgsReadWriteContext() );
1130
1131 QByteArray xmlArray;
1132 QTextStream stream( &xmlArray );
1133 shapeElem.save( stream, 4 );
1134 QString query = qgs_sqlite3_mprintf( "INSERT INTO legendpatchshapes VALUES (NULL, '%q', '%q', %d);",
1135 name.toUtf8().constData(), xmlArray.constData(), ( favorite ? 1 : 0 ) );
1136 if ( !runEmptyQuery( query ) )
1137 {
1138 QgsDebugError( u"Couldn't insert legend patch shape into the database!"_s );
1139 return false;
1140 }
1141
1142 mCachedFavorites[ LegendPatchShapeEntity ].insert( name, favorite );
1143
1145
1147
1148 return true;
1149}
1150
1151bool QgsStyle::renameLegendPatchShape( const QString &oldName, const QString &newName )
1152{
1153 if ( mLegendPatchShapes.contains( newName ) )
1154 {
1155 QgsDebugError( u"Legend patch shape of new name already exists."_s );
1156 return false;
1157 }
1158
1159 if ( !mLegendPatchShapes.contains( oldName ) )
1160 return false;
1161 QgsLegendPatchShape shape = mLegendPatchShapes.take( oldName );
1162
1163 mLegendPatchShapes.insert( newName, shape );
1164 mCachedTags[ LegendPatchShapeEntity ].remove( oldName );
1165 mCachedFavorites[ LegendPatchShapeEntity ].remove( oldName );
1166
1167 int labelSettingsId = 0;
1169 QString query = qgs_sqlite3_mprintf( "SELECT id FROM legendpatchshapes WHERE name='%q'", oldName.toUtf8().constData() );
1170 int nErr;
1171 statement = mCurrentDB.prepare( query, nErr );
1172 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1173 {
1174 labelSettingsId = sqlite3_column_int( statement.get(), 0 );
1175 }
1176 const bool result = rename( LegendPatchShapeEntity, labelSettingsId, newName );
1177 if ( result )
1178 {
1179 emit entityRenamed( LegendPatchShapeEntity, oldName, newName );
1180 }
1181
1182 return result;
1183}
1184
1186{
1187 if ( type == Qgis::SymbolType::Hybrid )
1188 return QgsLegendPatchShape();
1189
1190 auto it = mDefaultPatchCache[ static_cast< int >( type ) ].constFind( size );
1191 if ( it != mDefaultPatchCache[ static_cast< int >( type ) ].constEnd() )
1192 return it.value();
1193
1194 QgsGeometry geom;
1195 switch ( type )
1196 {
1198 geom = QgsGeometry( std::make_unique< QgsPoint >( static_cast< int >( size.width() ) / 2, static_cast< int >( size.height() ) / 2 ) );
1199 break;
1200
1202 {
1203 // we're adding 0.5 to get rid of blurred preview:
1204 // drawing antialiased lines of width 1 at (x,0)-(x,100) creates 2px line
1205 double y = static_cast< int >( size.height() ) / 2 + 0.5;
1206 geom = QgsGeometry( std::make_unique< QgsLineString >( ( QVector< double >() << 0 << size.width() ),
1207 ( QVector< double >() << y << y ) ) );
1208 break;
1209 }
1210
1212 {
1213 geom = QgsGeometry( std::make_unique< QgsPolygon >(
1214 new QgsLineString( QVector< double >() << 0 << static_cast< int >( size.width() ) << static_cast< int >( size.width() ) << 0 << 0,
1215 QVector< double >() << static_cast< int >( size.height() ) << static_cast< int >( size.height() ) << 0 << 0 << static_cast< int >( size.height() ) ) ) );
1216 break;
1217 }
1218
1220 break;
1221 }
1222
1223 QgsLegendPatchShape res = QgsLegendPatchShape( type, geom, false );
1224 mDefaultPatchCache[ static_cast< int >( type ) ][size ] = res;
1225 return res;
1226}
1227
1228QList<QList<QPolygonF> > QgsStyle::defaultPatchAsQPolygonF( Qgis::SymbolType type, QSizeF size ) const
1229{
1230 if ( type == Qgis::SymbolType::Hybrid )
1231 return QList<QList<QPolygonF> >();
1232
1233 auto it = mDefaultPatchQPolygonFCache[ static_cast< int >( type ) ].constFind( size );
1234 if ( it != mDefaultPatchQPolygonFCache[ static_cast< int >( type ) ].constEnd() )
1235 return it.value();
1236
1237 QList<QList<QPolygonF> > res = defaultPatch( type, size ).toQPolygonF( type, size );
1238 mDefaultPatchQPolygonFCache[ static_cast< int >( type ) ][size ] = res;
1239 return res;
1240}
1241
1246
1248{
1249 if ( project )
1250 {
1252 if ( defaultTextFormat.isValid() )
1253 {
1254 return defaultTextFormat;
1255 }
1256 }
1257
1258 return QgsStyle::defaultStyle()->defaultTextFormat( context );
1259}
1260
1261bool QgsStyle::saveSymbol3D( const QString &name, QgsAbstract3DSymbol *symbol, bool favorite, const QStringList &tags )
1262{
1263 // insert it into the database
1264 QDomDocument doc( u"dummy"_s );
1265 QDomElement elem = doc.createElement( u"symbol"_s );
1266 elem.setAttribute( u"type"_s, symbol->type() );
1267 symbol->writeXml( elem, QgsReadWriteContext() );
1268
1269 QByteArray xmlArray;
1270 QTextStream stream( &xmlArray );
1271 elem.save( stream, 4 );
1272 QString query = qgs_sqlite3_mprintf( "INSERT INTO symbol3d VALUES (NULL, '%q', '%q', %d);",
1273 name.toUtf8().constData(), xmlArray.constData(), ( favorite ? 1 : 0 ) );
1274 if ( !runEmptyQuery( query ) )
1275 {
1276 QgsDebugError( u"Couldn't insert 3d symbol into the database!"_s );
1277 return false;
1278 }
1279
1280 mCachedFavorites[ Symbol3DEntity ].insert( name, favorite );
1281
1283
1285
1286 return true;
1287}
1288
1289bool QgsStyle::renameSymbol3D( const QString &oldName, const QString &newName )
1290{
1291 if ( m3dSymbols.contains( newName ) )
1292 {
1293 QgsDebugError( u"3d symbol of new name already exists."_s );
1294 return false;
1295 }
1296
1297 if ( !m3dSymbols.contains( oldName ) )
1298 return false;
1299 QgsAbstract3DSymbol *symbol = m3dSymbols.take( oldName );
1300
1301 m3dSymbols.insert( newName, symbol );
1302 mCachedTags[Symbol3DEntity ].remove( oldName );
1303 mCachedFavorites[ Symbol3DEntity ].remove( oldName );
1304
1305 int labelSettingsId = 0;
1307 QString query = qgs_sqlite3_mprintf( "SELECT id FROM symbol3d WHERE name='%q'", oldName.toUtf8().constData() );
1308 int nErr;
1309 statement = mCurrentDB.prepare( query, nErr );
1310 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1311 {
1312 labelSettingsId = sqlite3_column_int( statement.get(), 0 );
1313 }
1314 const bool result = rename( Symbol3DEntity, labelSettingsId, newName );
1315 if ( result )
1316 {
1317 emit entityRenamed( Symbol3DEntity, oldName, newName );
1318 }
1319
1320 return result;
1321}
1322
1323QStringList QgsStyle::symbol3DNames() const
1324{
1325 return m3dSymbols.keys();
1326}
1327
1329{
1330 if ( !mCurrentDB )
1331 {
1332 QgsDebugError( u"Cannot Open database for getting favorite symbols"_s );
1333 return QStringList();
1334 }
1335
1336 QString query;
1337 switch ( type )
1338 {
1339 case TagEntity:
1340 case SmartgroupEntity:
1341 QgsDebugError( u"No such style entity"_s );
1342 return QStringList();
1343
1344 default:
1345 query = qgs_sqlite3_mprintf( u"SELECT name FROM %1 WHERE favorite=1"_s.arg( entityTableName( type ) ).toLocal8Bit().data() );
1346 break;
1347 }
1348
1349 int nErr;
1351 statement = mCurrentDB.prepare( query, nErr );
1352
1353 QStringList symbols;
1354 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1355 {
1356 symbols << statement.columnAsText( 0 );
1357 }
1358
1359 return symbols;
1360}
1361
1362QStringList QgsStyle::symbolsWithTag( StyleEntity type, int tagid ) const
1363{
1364 if ( !mCurrentDB )
1365 {
1366 QgsDebugError( u"Cannot open database to get symbols of tagid %1"_s.arg( tagid ) );
1367 return QStringList();
1368 }
1369
1370 QString subquery;
1371 switch ( type )
1372 {
1373 case TagEntity:
1374 case SmartgroupEntity:
1375 QgsDebugError( u"Unknown Entity"_s );
1376 return QStringList();
1377
1378 default:
1379 subquery = qgs_sqlite3_mprintf( u"SELECT %1 FROM %2 WHERE tag_id=%d"_s.arg( tagmapEntityIdFieldName( type ),
1380 tagmapTableName( type ) ).toLocal8Bit().data(), tagid );
1381 break;
1382 }
1383
1384 int nErr;
1386 statement = mCurrentDB.prepare( subquery, nErr );
1387
1388 // get the symbol <-> tag connection from the tag map table
1389 QStringList symbols;
1390 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1391 {
1392 int id = sqlite3_column_int( statement.get(), 0 );
1393
1394 const QString query = qgs_sqlite3_mprintf( u"SELECT name FROM %1 WHERE id=%d"_s.arg( entityTableName( type ) ).toLocal8Bit().data(), id );
1395
1396 int rc;
1398 statement2 = mCurrentDB.prepare( query, rc );
1399 while ( rc == SQLITE_OK && sqlite3_step( statement2.get() ) == SQLITE_ROW )
1400 {
1401 symbols << statement2.columnAsText( 0 );
1402 }
1403 }
1404
1405 return symbols;
1406}
1407
1408int QgsStyle::addTag( const QString &tagname )
1409{
1410 if ( !mCurrentDB )
1411 return 0;
1413
1414 QString query = qgs_sqlite3_mprintf( "INSERT INTO tag VALUES (NULL, '%q')", tagname.toUtf8().constData() );
1415 int nErr;
1416 statement = mCurrentDB.prepare( query, nErr );
1417 if ( nErr == SQLITE_OK )
1418 ( void )sqlite3_step( statement.get() );
1419
1420 QgsSettings settings;
1421 settings.setValue( u"qgis/symbolsListGroupsIndex"_s, 0 );
1422
1423 emit groupsModified();
1424
1425 return static_cast< int >( sqlite3_last_insert_rowid( mCurrentDB.get() ) );
1426}
1427
1428QStringList QgsStyle::tags() const
1429{
1430 if ( !mCurrentDB )
1431 return QStringList();
1432
1434
1435 QString query = qgs_sqlite3_mprintf( "SELECT name FROM tag" );
1436 int nError;
1437 statement = mCurrentDB.prepare( query, nError );
1438
1439 QStringList tagList;
1440 while ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1441 {
1442 tagList << statement.columnAsText( 0 );
1443 }
1444
1445 return tagList;
1446}
1447
1448bool QgsStyle::rename( StyleEntity type, int id, const QString &newName )
1449{
1450 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 );
1451
1452 const bool result = runEmptyQuery( query );
1453 if ( !result )
1454 {
1455 mErrorString = u"Could not rename!"_s;
1456 }
1457 else
1458 {
1459 mCachedTags.clear();
1460 mCachedFavorites.clear();
1461
1462 switch ( type )
1463 {
1464 case TagEntity:
1465 {
1466 emit groupsModified();
1467 break;
1468 }
1469
1470 case SmartgroupEntity:
1471 {
1472 emit groupsModified();
1473 break;
1474 }
1475
1476 default:
1477 break;
1478 }
1479 }
1480 return result;
1481}
1482
1483bool QgsStyle::remove( StyleEntity type, int id )
1484{
1485 bool groupRemoved = false;
1486 QString query;
1487 switch ( type )
1488 {
1489 case TagEntity:
1490 query = qgs_sqlite3_mprintf( "DELETE FROM tag WHERE id=%d; DELETE FROM tagmap WHERE tag_id=%d", id, id );
1491 groupRemoved = true;
1492 break;
1493 case SmartgroupEntity:
1494 query = qgs_sqlite3_mprintf( "DELETE FROM smartgroup WHERE id=%d", id );
1495 groupRemoved = true;
1496 break;
1497
1498 default:
1499 query = qgs_sqlite3_mprintf( u"DELETE FROM %1 WHERE id=%d; DELETE FROM %2 WHERE %3=%d"_s.arg(
1500 entityTableName( type ),
1501 tagmapTableName( type ),
1502 tagmapEntityIdFieldName( type )
1503 ).toLocal8Bit().data(), id, id );
1504 break;
1505 }
1506
1507 bool result = false;
1508 if ( !runEmptyQuery( query ) )
1509 {
1510 QgsDebugError( u"Could not delete entity!"_s );
1511 }
1512 else
1513 {
1514 mCachedTags.clear();
1515 mCachedFavorites.clear();
1516
1517 if ( groupRemoved )
1518 {
1519 QgsSettings settings;
1520 settings.setValue( u"qgis/symbolsListGroupsIndex"_s, 0 );
1521
1522 emit groupsModified();
1523 }
1524 result = true;
1525 }
1526 return result;
1527}
1528
1530{
1531 switch ( type )
1532 {
1535 return false;
1536
1538 {
1539 std::unique_ptr< QgsSymbol > symbol( mSymbols.take( name ) );
1540 if ( !symbol )
1541 return false;
1542
1543 break;
1544 }
1545
1547 {
1548 std::unique_ptr< QgsAbstract3DSymbol > symbol( m3dSymbols.take( name ) );
1549 if ( !symbol )
1550 return false;
1551
1552 break;
1553 }
1554
1556 {
1557 std::unique_ptr< QgsColorRamp > ramp( mColorRamps.take( name ) );
1558 if ( !ramp )
1559 return false;
1560 break;
1561 }
1562
1564 {
1565 auto it = mTextFormats.find( name );
1566 if ( it == mTextFormats.end() )
1567 return false;
1568
1569 mTextFormats.erase( it );
1570 break;
1571 }
1572
1574 {
1575 auto it = mLabelSettings.find( name );
1576 if ( it == mLabelSettings.end() )
1577 return false;
1578
1579 mLabelSettings.erase( it );
1580 break;
1581 }
1582
1584 {
1585 auto it = mLegendPatchShapes.find( name );
1586 if ( it == mLegendPatchShapes.end() )
1587 return false;
1588
1589 mLegendPatchShapes.erase( it );
1590 break;
1591 }
1592 }
1593
1594 if ( !mCurrentDB )
1595 {
1596 QgsDebugError( u"Sorry! Cannot open database to modify."_s );
1597 return false;
1598 }
1599
1600 const int id = entityId( type, name );
1601 if ( !id )
1602 {
1603 QgsDebugError( "No matching entity for deleting in database: " + name );
1604 }
1605
1606 const bool result = remove( type, id );
1607 if ( result )
1608 {
1609 mCachedTags[ type ].remove( name );
1610 mCachedFavorites[ type ].remove( name );
1611
1612 switch ( type )
1613 {
1614 case SymbolEntity:
1615 emit symbolRemoved( name );
1616 break;
1617
1618 case ColorrampEntity:
1619 emit rampRemoved( name );
1620 break;
1621
1622 case TextFormatEntity:
1623 emit textFormatRemoved( name );
1624 break;
1625
1627 emit labelSettingsRemoved( name );
1628 break;
1629
1630 default:
1631 // these specific signals should be discouraged -- don't add them for new entity types!
1632 break;
1633 }
1634 emit entityRemoved( type, name );
1635 }
1636 return result;
1637}
1638
1639bool QgsStyle::runEmptyQuery( const QString &query )
1640{
1641 if ( !mCurrentDB )
1642 return false;
1643
1644 char *zErr = nullptr;
1645 int nErr = sqlite3_exec( mCurrentDB.get(), query.toUtf8().constData(), nullptr, nullptr, &zErr );
1646
1647 if ( nErr != SQLITE_OK )
1648 {
1649 QgsDebugError( zErr );
1650 sqlite3_free( zErr );
1651 }
1652
1653 return nErr == SQLITE_OK;
1654}
1655
1656bool QgsStyle::addFavorite( StyleEntity type, const QString &name )
1657{
1658 QString query;
1659
1660 switch ( type )
1661 {
1662 case TagEntity:
1663 case SmartgroupEntity:
1664 QgsDebugError( u"Wrong entity value. cannot apply group"_s );
1665 return false;
1666
1667 default:
1668 query = qgs_sqlite3_mprintf( u"UPDATE %1 SET favorite=1 WHERE name='%q'"_s.arg( entityTableName( type ) ).toLocal8Bit().data(),
1669 name.toUtf8().constData() );
1670 break;
1671 }
1672
1673 const bool res = runEmptyQuery( query );
1674 if ( res )
1675 {
1676 switch ( type )
1677 {
1678 case TagEntity:
1679 case SmartgroupEntity:
1680 break;
1681
1682 default:
1683 mCachedFavorites[ type ].insert( name, true );
1684 break;
1685 }
1686 emit favoritedChanged( type, name, true );
1687 }
1688
1689 return res;
1690}
1691
1692bool QgsStyle::removeFavorite( StyleEntity type, const QString &name )
1693{
1694 QString query;
1695
1696 switch ( type )
1697 {
1698 case TagEntity:
1699 case SmartgroupEntity:
1700 QgsDebugError( u"Wrong entity value. cannot apply group"_s );
1701 return false;
1702
1703 default:
1704 query = qgs_sqlite3_mprintf( u"UPDATE %1 SET favorite=0 WHERE name='%q'"_s.arg( entityTableName( type ) ).toLocal8Bit().data(), name.toUtf8().constData() );
1705 break;
1706 }
1707
1708 const bool res = runEmptyQuery( query );
1709 if ( res )
1710 {
1711 mCachedFavorites[ type ].insert( name, false );
1712 emit favoritedChanged( type, name, false );
1713 }
1714
1715 return res;
1716}
1717
1718QStringList QgsStyle::findSymbols( StyleEntity type, const QString &qword )
1719{
1720 if ( !mCurrentDB )
1721 {
1722 QgsDebugError( u"Sorry! Cannot open database to search"_s );
1723 return QStringList();
1724 }
1725
1726 // first find symbols with matching name
1727 QString item;
1728 switch ( type )
1729 {
1730 case TagEntity:
1731 case SmartgroupEntity:
1732 return QStringList();
1733
1734 default:
1735 item = entityTableName( type );
1736 break;
1737 }
1738
1739 QString query = qgs_sqlite3_mprintf( "SELECT name FROM %q WHERE name LIKE '%%%q%%'",
1740 item.toUtf8().constData(), qword.toUtf8().constData() );
1741
1743 int nErr; statement = mCurrentDB.prepare( query, nErr );
1744
1745 QSet< QString > symbols;
1746 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1747 {
1748 symbols << statement.columnAsText( 0 );
1749 }
1750
1751 // next add symbols with matching tags
1752 query = qgs_sqlite3_mprintf( "SELECT id FROM tag WHERE name LIKE '%%%q%%'", qword.toUtf8().constData() );
1753 statement = mCurrentDB.prepare( query, nErr );
1754
1755 QStringList tagids;
1756 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1757 {
1758 tagids << statement.columnAsText( 0 );
1759 }
1760
1761 QString dummy = tagids.join( ", "_L1 );
1762 query = qgs_sqlite3_mprintf( u"SELECT %1 FROM %2 WHERE tag_id IN (%q)"_s.arg( tagmapEntityIdFieldName( type ),
1763 tagmapTableName( type ) ).toLocal8Bit().data(), dummy.toUtf8().constData() );
1764
1765 statement = mCurrentDB.prepare( query, nErr );
1766
1767 QStringList symbolids;
1768 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1769 {
1770 symbolids << statement.columnAsText( 0 );
1771 }
1772
1773 dummy = symbolids.join( ", "_L1 );
1774 query = qgs_sqlite3_mprintf( "SELECT name FROM %q WHERE id IN (%q)",
1775 item.toUtf8().constData(), dummy.toUtf8().constData() );
1776 statement = mCurrentDB.prepare( query, nErr );
1777 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1778 {
1779 symbols << statement.columnAsText( 0 );
1780 }
1781
1782 return QStringList( symbols.constBegin(), symbols.constEnd() );
1783}
1784
1785bool QgsStyle::tagSymbol( StyleEntity type, const QString &symbol, const QStringList &tags )
1786{
1787 if ( !mCurrentDB )
1788 {
1789 QgsDebugError( u"Sorry! Cannot open database to tag."_s );
1790 return false;
1791 }
1792
1793 int symbolid = 0;
1794 switch ( type )
1795 {
1796 case TagEntity:
1797 case SmartgroupEntity:
1798 return false;
1799
1800 default:
1801 symbolid = entityId( type, symbol );
1802 break;
1803 }
1804
1805 if ( !symbolid )
1806 {
1807 QgsDebugError( u"No such symbol for tagging in database: "_s + symbol );
1808 return false;
1809 }
1810
1811 QString tag;
1812 const auto constTags = tags;
1813 for ( const QString &t : constTags )
1814 {
1815 tag = t.trimmed();
1816 if ( !tag.isEmpty() )
1817 {
1818 // sql: gets the id of the tag if present or insert the tag and get the id of the tag
1819 int tagid( tagId( tag ) );
1820 if ( ! tagid )
1821 {
1822 tagid = addTag( tag );
1823 }
1824
1825 // Now map the tag to the symbol if it's not already tagged
1826 if ( !symbolHasTag( type, symbol, tag ) )
1827 {
1828 QString query = qgs_sqlite3_mprintf( u"INSERT INTO %1 VALUES (%d,%d)"_s.arg( tagmapTableName( type ) ).toLocal8Bit().data(), tagid, symbolid );
1829
1830 char *zErr = nullptr;
1831 int nErr;
1832 nErr = sqlite3_exec( mCurrentDB.get(), query.toUtf8().constData(), nullptr, nullptr, &zErr );
1833 if ( nErr )
1834 {
1835 QgsDebugError( zErr );
1836 sqlite3_free( zErr );
1837 }
1838 }
1839 }
1840 }
1841
1842 clearCachedTags( type, symbol );
1843 emit entityTagsChanged( type, symbol, tagsOfSymbol( type, symbol ) );
1844
1845 return true;
1846}
1847
1848bool QgsStyle::detagSymbol( StyleEntity type, const QString &symbol, const QStringList &tags )
1849{
1850 if ( !mCurrentDB )
1851 {
1852 QgsDebugError( u"Sorry! Cannot open database for detagging."_s );
1853 return false;
1854 }
1855
1856 switch ( type )
1857 {
1858 case TagEntity:
1859 case SmartgroupEntity:
1860 return false;
1861
1862 default:
1863 break;
1864 }
1865
1866 const int symbolid = entityId( type, symbol );
1867 if ( symbolid == 0 )
1868 return false;
1869
1870 int nErr;
1871 QString query;
1872 const auto constTags = tags;
1873 for ( const QString &tag : constTags )
1874 {
1875 query = qgs_sqlite3_mprintf( "SELECT id FROM tag WHERE name='%q'", tag.toUtf8().constData() );
1876
1878 statement2 = mCurrentDB.prepare( query, nErr );
1879
1880 int tagid = 0;
1881 if ( nErr == SQLITE_OK && sqlite3_step( statement2.get() ) == SQLITE_ROW )
1882 {
1883 tagid = sqlite3_column_int( statement2.get(), 0 );
1884 }
1885
1886 if ( tagid )
1887 {
1888 // remove from the tagmap
1889 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 );
1890 runEmptyQuery( query );
1891 }
1892 }
1893
1894 clearCachedTags( type, symbol );
1895 emit entityTagsChanged( type, symbol, tagsOfSymbol( type, symbol ) );
1896
1897 // TODO Perform tag cleanup
1898 // check the number of entries for a given tag in the tagmap
1899 // if the count is 0, then remove( TagEntity, tagid )
1900 return true;
1901}
1902
1903bool QgsStyle::detagSymbol( StyleEntity type, const QString &symbol )
1904{
1905 if ( !mCurrentDB )
1906 {
1907 QgsDebugError( u"Sorry! Cannot open database for detagging."_s );
1908 return false;
1909 }
1910
1911 switch ( type )
1912 {
1913 case TagEntity:
1914 case SmartgroupEntity:
1915 return false;
1916
1917 default:
1918 break;
1919 }
1920
1921 const int symbolid = entityId( type, symbol );
1922 if ( symbolid == 0 )
1923 {
1924 return false;
1925 }
1926
1927 // remove all tags
1928 const QString query = qgs_sqlite3_mprintf( u"DELETE FROM %1 WHERE %2=%d"_s.arg( tagmapTableName( type ),
1929 tagmapEntityIdFieldName( type ) ).toLocal8Bit().data(), symbolid );
1930 runEmptyQuery( query );
1931
1932 clearCachedTags( type, symbol );
1933 emit entityTagsChanged( type, symbol, QStringList() );
1934
1935 // TODO Perform tag cleanup
1936 // check the number of entries for a given tag in the tagmap
1937 // if the count is 0, then remove( TagEntity, tagid )
1938 return true;
1939}
1940
1941QStringList QgsStyle::tagsOfSymbol( StyleEntity type, const QString &symbol )
1942{
1943 switch ( type )
1944 {
1945 case TagEntity:
1946 case SmartgroupEntity:
1947 return QStringList();
1948
1949 default:
1950 {
1951 auto it = mCachedTags[ type ].constFind( symbol );
1952 if ( it != mCachedTags[ type ].constEnd() )
1953 return it.value();
1954 break;
1955 }
1956 }
1957
1958 if ( !mCurrentDB )
1959 {
1960 QgsDebugError( u"Sorry! Cannot open database for getting the tags."_s );
1961 return QStringList();
1962 }
1963
1964 int symbolid = entityId( type, symbol );
1965 if ( !symbolid )
1966 return QStringList();
1967
1968 // get the ids of tags for the symbol
1969 const QString query = qgs_sqlite3_mprintf( u"SELECT tag_id FROM %1 WHERE %2=%d"_s.arg( tagmapTableName( type ),
1970 tagmapEntityIdFieldName( type ) ).toLocal8Bit().data(), symbolid );
1971
1973 int nErr; statement = mCurrentDB.prepare( query, nErr );
1974
1975 QStringList tagList;
1976 while ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1977 {
1978 QString subquery = qgs_sqlite3_mprintf( "SELECT name FROM tag WHERE id=%d", sqlite3_column_int( statement.get(), 0 ) );
1979
1981 int pErr;
1982 statement2 = mCurrentDB.prepare( subquery, pErr );
1983 if ( pErr == SQLITE_OK && sqlite3_step( statement2.get() ) == SQLITE_ROW )
1984 {
1985 tagList << statement2.columnAsText( 0 );
1986 }
1987 }
1988
1989 // update cache
1990 mCachedTags[ type ].insert( symbol, tagList );
1991
1992 return tagList;
1993}
1994
1996{
1997 if ( !mCurrentDB )
1998 {
1999 QgsDebugError( u"Sorry! Cannot open database for getting the tags."_s );
2000 return false;
2001 }
2002
2003 switch ( type )
2004 {
2005 case TagEntity:
2006 case SmartgroupEntity:
2007 return false;
2008
2009 default:
2010 {
2011 auto it = mCachedFavorites[ type ].constFind( name );
2012 if ( it != mCachedFavorites[ type ].constEnd() )
2013 return it.value();
2014 break;
2015 }
2016 }
2017
2018 const QStringList names = allNames( type );
2019 if ( !names.contains( name ) )
2020 return false; // entity doesn't exist
2021
2022 // for efficiency, retrieve names of all favorited symbols and store them in cache
2023 const QStringList favorites = symbolsOfFavorite( type );
2024 bool res = false;
2025 for ( const QString &n : names )
2026 {
2027 const bool isFav = favorites.contains( n );
2028 if ( n == name )
2029 res = isFav;
2030
2031 mCachedFavorites[ type ].insert( n, isFav );
2032 }
2033 return res;
2034}
2035
2036bool QgsStyle::symbolHasTag( StyleEntity type, const QString &symbol, const QString &tag )
2037{
2038 if ( !mCurrentDB )
2039 {
2040 QgsDebugError( u"Sorry! Cannot open database for getting the tags."_s );
2041 return false;
2042 }
2043
2044 int symbolid = 0;
2045 switch ( type )
2046 {
2047 case TagEntity:
2048 case SmartgroupEntity:
2049 return false;
2050
2051 default:
2052 symbolid = entityId( type, symbol );
2053 break;
2054 }
2055
2056 if ( !symbolid )
2057 {
2058 return false;
2059 }
2060 int tagid = tagId( tag );
2061 if ( !tagid )
2062 {
2063 return false;
2064 }
2065
2066 // get the ids of tags for the symbol
2067 const QString query = qgs_sqlite3_mprintf( u"SELECT tag_id FROM %1 WHERE tag_id=%d AND %2=%d"_s.arg( tagmapTableName( type ),
2068 tagmapEntityIdFieldName( type ) ).toLocal8Bit().data(), tagid, symbolid );
2069
2071 int nErr; statement = mCurrentDB.prepare( query, nErr );
2072
2073 return ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW );
2074}
2075
2076QString QgsStyle::tag( int id ) const
2077{
2078 if ( !mCurrentDB )
2079 return QString();
2080
2082
2083 QString query = qgs_sqlite3_mprintf( "SELECT name FROM tag WHERE id=%d", id );
2084 int nError;
2085 statement = mCurrentDB.prepare( query, nError );
2086
2087 QString tag;
2088 if ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
2089 {
2090 tag = statement.columnAsText( 0 );
2091 }
2092
2093 return tag;
2094}
2095
2096int QgsStyle::getId( const QString &table, const QString &name )
2097{
2098 QString lowerName( name.toLower() );
2099 QString query = qgs_sqlite3_mprintf( "SELECT id FROM %q WHERE LOWER(name)='%q'", table.toUtf8().constData(), lowerName.toUtf8().constData() );
2100
2102 int nErr; statement = mCurrentDB.prepare( query, nErr );
2103
2104 int id = 0;
2105 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
2106 {
2107 id = sqlite3_column_int( statement.get(), 0 );
2108 }
2109 else
2110 {
2111 // Try the name without lowercase conversion
2112 QString query = qgs_sqlite3_mprintf( "SELECT id FROM %q WHERE name='%q'", table.toUtf8().constData(), name.toUtf8().constData() );
2113
2114 sqlite3_statement_unique_ptr statement;
2115 int nErr; statement = mCurrentDB.prepare( query, nErr );
2116 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
2117 {
2118 id = sqlite3_column_int( statement.get(), 0 );
2119 }
2120 }
2121
2122 return id;
2123}
2124
2125QString QgsStyle::getName( const QString &table, int id ) const
2126{
2127 QString query = qgs_sqlite3_mprintf( "SELECT name FROM %q WHERE id='%q'", table.toUtf8().constData(), QString::number( id ).toUtf8().constData() );
2128
2129 sqlite3_statement_unique_ptr statement;
2130 int nErr; statement = mCurrentDB.prepare( query, nErr );
2131
2132 QString name;
2133 if ( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
2134 {
2135 name = statement.columnAsText( 0 );
2136 }
2137
2138 return name;
2139}
2140
2141int QgsStyle::symbolId( const QString &name )
2142{
2143 return getId( u"symbol"_s, name );
2144}
2145
2147{
2148 return getId( entityTableName( type ), name );
2149}
2150
2151int QgsStyle::colorrampId( const QString &name )
2152{
2153 return getId( u"colorramp"_s, name );
2154}
2155
2157{
2158 return mTextFormats.value( name );
2159}
2160
2162{
2163 return mTextFormats.count();
2164}
2165
2166QStringList QgsStyle::textFormatNames() const
2167{
2168 return mTextFormats.keys();
2169}
2170
2171int QgsStyle::textFormatId( const QString &name )
2172{
2173 return getId( u"textformat"_s, name );
2174}
2175
2177{
2178 return mLabelSettings.value( name );
2179}
2180
2182{
2183 return mLegendPatchShapes.value( name );
2184}
2185
2187{
2188 return mLegendPatchShapes.count();
2189}
2190
2192{
2193 auto it = mLegendPatchShapes.constFind( name );
2194 if ( it == mLegendPatchShapes.constEnd() )
2196
2197 return it.value().symbolType();
2198}
2199
2201{
2202 auto it = m3dSymbols.constFind( name );
2203 if ( it != m3dSymbols.constEnd( ) )
2204 return it.value()->clone();
2205 return nullptr;
2206}
2207
2209{
2210 return m3dSymbols.count();
2211}
2212
2213QList<Qgis::GeometryType> QgsStyle::symbol3DCompatibleGeometryTypes( const QString &name ) const
2214{
2215 auto it = m3dSymbols.constFind( name );
2216 if ( it == m3dSymbols.constEnd() )
2217 return QList<Qgis::GeometryType>();
2218
2219 return it.value()->compatibleGeometryTypes();
2220}
2221
2223{
2224 auto it = mLabelSettings.constFind( name );
2225 if ( it == mLabelSettings.constEnd() )
2227
2228 return it.value().layerType;
2229}
2230
2232{
2233 return mLabelSettings.count();
2234}
2235
2237{
2238 return mLabelSettings.keys();
2239}
2240
2241int QgsStyle::labelSettingsId( const QString &name )
2242{
2243 return getId( u"labelsettings"_s, name );
2244}
2245
2247{
2248 return mLegendPatchShapes.keys();
2249}
2250
2252{
2253 switch ( shape.symbolType() )
2254 {
2256 return mPatchMarkerSymbol.get();
2257
2259 return mPatchLineSymbol.get();
2260
2262 return mPatchFillSymbol.get();
2263
2265 break;
2266 }
2267 return nullptr;
2268}
2269
2270int QgsStyle::tagId( const QString &name )
2271{
2272 return getId( u"tag"_s, name );
2273}
2274
2275int QgsStyle::smartgroupId( const QString &name )
2276{
2277 return getId( u"smartgroup"_s, name );
2278}
2279
2281{
2282 switch ( type )
2283 {
2284 case SymbolEntity:
2285 return symbolNames();
2286
2287 case ColorrampEntity:
2288 return colorRampNames();
2289
2290 case TextFormatEntity:
2291 return textFormatNames();
2292
2294 return labelSettingsNames();
2295
2297 return legendPatchShapeNames();
2298
2299 case Symbol3DEntity:
2300 return symbol3DNames();
2301
2302 case TagEntity:
2303 return tags();
2304
2305 case SmartgroupEntity:
2306 return smartgroupNames();
2307 }
2308 return QStringList();
2309}
2310
2311int QgsStyle::addSmartgroup( const QString &name, const QString &op, const QgsSmartConditionMap &conditions )
2312{
2313 return addSmartgroup( name, op, conditions.values( u"tag"_s ),
2314 conditions.values( u"!tag"_s ),
2315 conditions.values( u"name"_s ),
2316 conditions.values( u"!name"_s ) );
2317}
2318
2319int QgsStyle::addSmartgroup( const QString &name, const QString &op, const QStringList &matchTag, const QStringList &noMatchTag, const QStringList &matchName, const QStringList &noMatchName )
2320{
2321 QDomDocument doc( u"dummy"_s );
2322 QDomElement smartEl = doc.createElement( u"smartgroup"_s );
2323 smartEl.setAttribute( u"name"_s, name );
2324 smartEl.setAttribute( u"operator"_s, op );
2325
2326 auto addCondition = [&doc, &smartEl]( const QString & constraint, const QStringList & parameters )
2327 {
2328 for ( const QString &param : parameters )
2329 {
2330 QDomElement condEl = doc.createElement( u"condition"_s );
2331 condEl.setAttribute( u"constraint"_s, constraint );
2332 condEl.setAttribute( u"param"_s, param );
2333 smartEl.appendChild( condEl );
2334 }
2335 };
2336 addCondition( u"tag"_s, matchTag );
2337 addCondition( u"!tag"_s, noMatchTag );
2338 addCondition( u"name"_s, matchName );
2339 addCondition( u"!name"_s, noMatchName );
2340
2341 QByteArray xmlArray;
2342 QTextStream stream( &xmlArray );
2343 smartEl.save( stream, 4 );
2344 QString query = qgs_sqlite3_mprintf( "INSERT INTO smartgroup VALUES (NULL, '%q', '%q')",
2345 name.toUtf8().constData(), xmlArray.constData() );
2346
2347 if ( runEmptyQuery( query ) )
2348 {
2349 QgsSettings settings;
2350 settings.setValue( u"qgis/symbolsListGroupsIndex"_s, 0 );
2351
2352 emit groupsModified();
2353 return static_cast< int >( sqlite3_last_insert_rowid( mCurrentDB.get() ) );
2354 }
2355 else
2356 {
2357 QgsDebugError( u"Couldn't add the smart group into the database!"_s );
2358 return 0;
2359 }
2360}
2361
2363{
2364 if ( !mCurrentDB )
2365 {
2366 QgsDebugError( u"Cannot open database for listing groups"_s );
2367 return QgsSymbolGroupMap();
2368 }
2369
2370 QString query = qgs_sqlite3_mprintf( "SELECT * FROM smartgroup" );
2371
2372 // Now run the query and retrieve the group names
2374 int nError;
2375 statement = mCurrentDB.prepare( query, nError );
2376
2377 QgsSymbolGroupMap groupNames;
2378 while ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
2379 {
2380 QString group = statement.columnAsText( static_cast< int >( SmartGroupTableColumn::Name ) );
2381 groupNames.insert( sqlite3_column_int( statement.get(), static_cast< int >( SmartGroupTableColumn::Id ) ), group );
2382 }
2383
2384 return groupNames;
2385}
2386
2387QStringList QgsStyle::smartgroupNames() const
2388{
2389 if ( !mCurrentDB )
2390 {
2391 QgsDebugError( u"Cannot open database for listing groups"_s );
2392 return QStringList();
2393 }
2394
2395 QString query = qgs_sqlite3_mprintf( "SELECT name FROM smartgroup" );
2396
2397 // Now run the query and retrieve the group names
2399 int nError;
2400 statement = mCurrentDB.prepare( query, nError );
2401
2402 QStringList groups;
2403 while ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
2404 {
2405 groups << statement.columnAsText( 0 );
2406 }
2407
2408 return groups;
2409}
2410
2412{
2413 QStringList symbols;
2414
2415 QString query = qgs_sqlite3_mprintf( "SELECT xml FROM smartgroup WHERE id=%d", id );
2416
2418 int nErr; statement = mCurrentDB.prepare( query, nErr );
2419 if ( !( nErr == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW ) )
2420 {
2421 return QStringList();
2422 }
2423 else
2424 {
2425 QDomDocument doc;
2426 QString xmlstr = statement.columnAsText( 0 );
2427 if ( !doc.setContent( xmlstr ) )
2428 {
2429 QgsDebugError( u"Cannot open smartgroup id: %1"_s.arg( id ) );
2430 }
2431 QDomElement smartEl = doc.documentElement();
2432 QString op = smartEl.attribute( u"operator"_s );
2433 QDomNodeList conditionNodes = smartEl.childNodes();
2434
2435 bool firstSet = true;
2436 for ( int i = 0; i < conditionNodes.count(); i++ )
2437 {
2438 QDomElement condEl = conditionNodes.at( i ).toElement();
2439 QString constraint = condEl.attribute( u"constraint"_s );
2440 QString param = condEl.attribute( u"param"_s );
2441
2442 QStringList resultNames;
2443 // perform suitable action for the given constraint
2444 if ( constraint == "tag"_L1 )
2445 {
2446 resultNames = symbolsWithTag( type, tagId( param ) );
2447 }
2448 else if ( constraint == "name"_L1 )
2449 {
2450 resultNames = allNames( type ).filter( param, Qt::CaseInsensitive );
2451 }
2452 else if ( constraint == "!tag"_L1 )
2453 {
2454 resultNames = allNames( type );
2455 const QStringList unwanted = symbolsWithTag( type, tagId( param ) );
2456 for ( const QString &name : unwanted )
2457 {
2458 resultNames.removeAll( name );
2459 }
2460 }
2461 else if ( constraint == "!name"_L1 )
2462 {
2463 const QStringList all = allNames( type );
2464 for ( const QString &str : all )
2465 {
2466 if ( !str.contains( param, Qt::CaseInsensitive ) )
2467 resultNames << str;
2468 }
2469 }
2470
2471 // not apply the operator
2472 if ( firstSet )
2473 {
2474 symbols = resultNames;
2475 firstSet = false;
2476 }
2477 else
2478 {
2479 if ( op == "OR"_L1 )
2480 {
2481 symbols << resultNames;
2482 }
2483 else if ( op == "AND"_L1 )
2484 {
2485 QStringList dummy = symbols;
2486 symbols.clear();
2487 for ( const QString &result : std::as_const( resultNames ) )
2488 {
2489 if ( dummy.contains( result ) )
2490 symbols << result;
2491 }
2492 }
2493 }
2494 } // DOM loop ends here
2495 }
2496
2497 // return sorted, unique list
2498 const QSet< QString > uniqueSet( symbols.constBegin(), symbols.constEnd() );
2499 QStringList unique( uniqueSet.begin(), uniqueSet.end() );
2500 std::sort( unique.begin(), unique.end() );
2501 return unique;
2502}
2503
2505{
2506 if ( !mCurrentDB )
2507 {
2508 QgsDebugError( u"Cannot open database for listing groups"_s );
2509 return QgsSmartConditionMap();
2510 }
2511
2512 QgsSmartConditionMap condition;
2513
2514 QString query = qgs_sqlite3_mprintf( "SELECT xml FROM smartgroup WHERE id=%d", id );
2515
2517 int nError;
2518 statement = mCurrentDB.prepare( query, nError );
2519 if ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
2520 {
2521 QDomDocument doc;
2522 QString xmlstr = statement.columnAsText( 0 );
2523 if ( !doc.setContent( xmlstr ) )
2524 {
2525 QgsDebugError( u"Cannot open smartgroup id: %1"_s.arg( id ) );
2526 }
2527
2528 QDomElement smartEl = doc.documentElement();
2529 QDomNodeList conditionNodes = smartEl.childNodes();
2530
2531 for ( int i = 0; i < conditionNodes.count(); i++ )
2532 {
2533 QDomElement condEl = conditionNodes.at( i ).toElement();
2534 QString constraint = condEl.attribute( u"constraint"_s );
2535 QString param = condEl.attribute( u"param"_s );
2536
2537 condition.insert( constraint, param );
2538 }
2539 }
2540
2541 return condition;
2542}
2543
2545{
2546 if ( !mCurrentDB )
2547 {
2548 QgsDebugError( u"Cannot open database for listing groups"_s );
2549 return QString();
2550 }
2551
2552 QString op;
2553
2554 QString query = qgs_sqlite3_mprintf( "SELECT xml FROM smartgroup WHERE id=%d", id );
2555
2556 int nError;
2558 statement = mCurrentDB.prepare( query, nError );
2559 if ( nError == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
2560 {
2561 QDomDocument doc;
2562 QString xmlstr = statement.columnAsText( 0 );
2563 if ( !doc.setContent( xmlstr ) )
2564 {
2565 QgsDebugError( u"Cannot open smartgroup id: %1"_s.arg( id ) );
2566 }
2567 QDomElement smartEl = doc.documentElement();
2568 op = smartEl.attribute( u"operator"_s );
2569 }
2570
2571 return op;
2572}
2573
2574bool QgsStyle::exportXml( const QString &filename )
2575{
2576 if ( filename.isEmpty() )
2577 {
2578 QgsDebugError( u"Invalid filename for style export."_s );
2579 return false;
2580 }
2581
2582 QDomDocument doc( u"qgis_style"_s );
2583 QDomElement root = doc.createElement( u"qgis_style"_s );
2584 root.setAttribute( u"version"_s, QStringLiteral( STYLE_CURRENT_VERSION ) );
2585 doc.appendChild( root );
2586
2587 const QStringList favoriteSymbols = symbolsOfFavorite( SymbolEntity );
2588 const QStringList favoriteColorramps = symbolsOfFavorite( ColorrampEntity );
2589 const QStringList favoriteTextFormats = symbolsOfFavorite( TextFormatEntity );
2590 const QStringList favoriteLegendShapes = symbolsOfFavorite( LegendPatchShapeEntity );
2591 const QStringList favorite3DSymbols = symbolsOfFavorite( Symbol3DEntity );
2592
2593 // save symbols and attach tags
2594 QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( mSymbols, u"symbols"_s, doc, QgsReadWriteContext() );
2595 QDomNodeList symbolsList = symbolsElem.elementsByTagName( u"symbol"_s );
2596 int nbSymbols = symbolsList.count();
2597 for ( int i = 0; i < nbSymbols; ++i )
2598 {
2599 QDomElement symbol = symbolsList.at( i ).toElement();
2600 QString name = symbol.attribute( u"name"_s );
2601 QStringList tags = tagsOfSymbol( SymbolEntity, name );
2602 if ( tags.count() > 0 )
2603 {
2604 symbol.setAttribute( u"tags"_s, tags.join( ',' ) );
2605 }
2606 if ( favoriteSymbols.contains( name ) )
2607 {
2608 symbol.setAttribute( u"favorite"_s, u"1"_s );
2609 }
2610 }
2611
2612 // save color ramps
2613 QDomElement rampsElem = doc.createElement( u"colorramps"_s );
2614 for ( QMap<QString, QgsColorRamp *>::const_iterator itr = mColorRamps.constBegin(); itr != mColorRamps.constEnd(); ++itr )
2615 {
2616 QDomElement rampEl = QgsSymbolLayerUtils::saveColorRamp( itr.key(), itr.value(), doc );
2617 QStringList tags = tagsOfSymbol( ColorrampEntity, itr.key() );
2618 if ( tags.count() > 0 )
2619 {
2620 rampEl.setAttribute( u"tags"_s, tags.join( ',' ) );
2621 }
2622 if ( favoriteColorramps.contains( itr.key() ) )
2623 {
2624 rampEl.setAttribute( u"favorite"_s, u"1"_s );
2625 }
2626 rampsElem.appendChild( rampEl );
2627 }
2628
2629 // save text formats
2630 QDomElement textFormatsElem = doc.createElement( u"textformats"_s );
2631 for ( auto it = mTextFormats.constBegin(); it != mTextFormats.constEnd(); ++it )
2632 {
2633 QDomElement textFormatEl = doc.createElement( u"textformat"_s );
2634 textFormatEl.setAttribute( u"name"_s, it.key() );
2635 QDomElement textStyleEl = it.value().writeXml( doc, QgsReadWriteContext() );
2636 textFormatEl.appendChild( textStyleEl );
2637 QStringList tags = tagsOfSymbol( TextFormatEntity, it.key() );
2638 if ( tags.count() > 0 )
2639 {
2640 textFormatEl.setAttribute( u"tags"_s, tags.join( ',' ) );
2641 }
2642 if ( favoriteTextFormats.contains( it.key() ) )
2643 {
2644 textFormatEl.setAttribute( u"favorite"_s, u"1"_s );
2645 }
2646 textFormatsElem.appendChild( textFormatEl );
2647 }
2648
2649 // save label settings
2650 QDomElement labelSettingsElem = doc.createElement( u"labelsettings"_s );
2651 for ( auto it = mLabelSettings.constBegin(); it != mLabelSettings.constEnd(); ++it )
2652 {
2653 QDomElement labelSettingsEl = doc.createElement( u"labelsetting"_s );
2654 labelSettingsEl.setAttribute( u"name"_s, it.key() );
2655 QDomElement defEl = it.value().writeXml( doc, QgsReadWriteContext() );
2656 labelSettingsEl.appendChild( defEl );
2657 QStringList tags = tagsOfSymbol( LabelSettingsEntity, it.key() );
2658 if ( tags.count() > 0 )
2659 {
2660 labelSettingsEl.setAttribute( u"tags"_s, tags.join( ',' ) );
2661 }
2662 if ( favoriteTextFormats.contains( it.key() ) )
2663 {
2664 labelSettingsEl.setAttribute( u"favorite"_s, u"1"_s );
2665 }
2666 labelSettingsElem.appendChild( labelSettingsEl );
2667 }
2668
2669 // save legend patch shapes
2670 QDomElement legendPatchShapesElem = doc.createElement( u"legendpatchshapes"_s );
2671 for ( auto it = mLegendPatchShapes.constBegin(); it != mLegendPatchShapes.constEnd(); ++it )
2672 {
2673 QDomElement legendPatchShapeEl = doc.createElement( u"legendpatchshape"_s );
2674 legendPatchShapeEl.setAttribute( u"name"_s, it.key() );
2675 QDomElement defEl = doc.createElement( u"definition"_s );
2676 it.value().writeXml( defEl, doc, QgsReadWriteContext() );
2677 legendPatchShapeEl.appendChild( defEl );
2678 QStringList tags = tagsOfSymbol( LegendPatchShapeEntity, it.key() );
2679 if ( tags.count() > 0 )
2680 {
2681 legendPatchShapeEl.setAttribute( u"tags"_s, tags.join( ',' ) );
2682 }
2683 if ( favoriteLegendShapes.contains( it.key() ) )
2684 {
2685 legendPatchShapeEl.setAttribute( u"favorite"_s, u"1"_s );
2686 }
2687 legendPatchShapesElem.appendChild( legendPatchShapeEl );
2688 }
2689
2690 // save symbols and attach tags
2691 QDomElement symbols3DElem = doc.createElement( u"symbols3d"_s );
2692 for ( auto it = m3dSymbols.constBegin(); it != m3dSymbols.constEnd(); ++it )
2693 {
2694 QDomElement symbolEl = doc.createElement( u"symbol3d"_s );
2695 symbolEl.setAttribute( u"name"_s, it.key() );
2696 QDomElement defEl = doc.createElement( u"definition"_s );
2697 defEl.setAttribute( u"type"_s, it.value()->type() );
2698 it.value()->writeXml( defEl, QgsReadWriteContext() );
2699 symbolEl.appendChild( defEl );
2700 QStringList tags = tagsOfSymbol( Symbol3DEntity, it.key() );
2701 if ( tags.count() > 0 )
2702 {
2703 symbolEl.setAttribute( u"tags"_s, tags.join( ',' ) );
2704 }
2705 if ( favorite3DSymbols.contains( it.key() ) )
2706 {
2707 symbolEl.setAttribute( u"favorite"_s, u"1"_s );
2708 }
2709 symbols3DElem.appendChild( symbolEl );
2710 }
2711
2712 root.appendChild( symbolsElem );
2713 root.appendChild( rampsElem );
2714 root.appendChild( textFormatsElem );
2715 root.appendChild( labelSettingsElem );
2716 root.appendChild( legendPatchShapesElem );
2717 root.appendChild( symbols3DElem );
2718
2719 // save
2720 QFile f( filename );
2721 if ( !f.open( QFile::WriteOnly | QIODevice::Truncate ) )
2722 {
2723 mErrorString = "Couldn't open file for writing: " + filename;
2724 return false;
2725 }
2726
2727 QTextStream ts( &f );
2728 doc.save( ts, 2 );
2729 f.close();
2730
2731 return true;
2732}
2733
2734bool QgsStyle::importXml( const QString &filename )
2735{
2736 return importXml( filename, -1 );
2737}
2738
2739bool QgsStyle::importXml( const QString &filename, int sinceVersion )
2740{
2741 mErrorString = QString();
2742 QDomDocument doc( u"style"_s );
2743 QFile f( filename );
2744 if ( !f.open( QFile::ReadOnly ) )
2745 {
2746 mErrorString = u"Unable to open the specified file"_s;
2747 QgsDebugError( u"Error opening the style XML file."_s );
2748 return false;
2749 }
2750
2751 if ( !doc.setContent( &f ) )
2752 {
2753 mErrorString = u"Unable to understand the style file: %1"_s.arg( filename );
2754 QgsDebugError( u"XML Parsing error"_s );
2755 f.close();
2756 return false;
2757 }
2758 f.close();
2759
2760 QDomElement docEl = doc.documentElement();
2761 if ( docEl.tagName() != "qgis_style"_L1 )
2762 {
2763 mErrorString = "Incorrect root tag in style: " + docEl.tagName();
2764 return false;
2765 }
2766
2767 const QString version = docEl.attribute( u"version"_s );
2768 if ( version != QLatin1String( STYLE_CURRENT_VERSION ) && version != "0"_L1 && version != "1"_L1 )
2769 {
2770 mErrorString = "Unknown style file version: " + version;
2771 return false;
2772 }
2773
2774 QgsSymbolMap symbols;
2775
2776 QDomElement symbolsElement = docEl.firstChildElement( u"symbols"_s );
2777 QDomElement e = symbolsElement.firstChildElement();
2778
2779 // gain speed by re-grouping the INSERT statements in a transaction
2780 QString query = qgs_sqlite3_mprintf( "BEGIN TRANSACTION;" );
2781 runEmptyQuery( query );
2782
2783 if ( version == QLatin1String( STYLE_CURRENT_VERSION ) || version == "1"_L1 )
2784 {
2785 // For the new style, load symbols individually
2786 for ( ; !e.isNull(); e = e.nextSiblingElement() )
2787 {
2788 const int entityAddedVersion = e.attribute( u"addedVersion"_s ).toInt();
2789 if ( entityAddedVersion != 0 && sinceVersion != -1 && entityAddedVersion <= sinceVersion )
2790 {
2791 // skip the symbol, should already be present
2792 continue;
2793 }
2794
2795 if ( e.tagName() == "symbol"_L1 )
2796 {
2797 QString name = e.attribute( u"name"_s );
2798 QStringList tags;
2799 if ( e.hasAttribute( u"tags"_s ) )
2800 {
2801 tags = e.attribute( u"tags"_s ).split( ',' );
2802 }
2803 bool favorite = false;
2804 if ( e.hasAttribute( u"favorite"_s ) && e.attribute( u"favorite"_s ) == "1"_L1 )
2805 {
2806 favorite = true;
2807 }
2808
2809 std::unique_ptr< QgsSymbol > symbol = QgsSymbolLayerUtils::loadSymbol( e, QgsReadWriteContext() );
2810 if ( symbol )
2811 {
2812 QgsSymbol *symbolPtr = symbol.get();
2813 addSymbol( name, symbol.release() );
2814 if ( mCurrentDB )
2815 {
2816 saveSymbol( name, symbolPtr, favorite, tags );
2817 }
2818 }
2819 }
2820 else
2821 {
2822 QgsDebugError( "unknown tag: " + e.tagName() );
2823 }
2824 }
2825 }
2826 else
2827 {
2828 // for the old version, use the utility function to solve @symbol@layer subsymbols
2829 symbols = QgsSymbolLayerUtils::loadSymbols( symbolsElement, QgsReadWriteContext() );
2830
2831 // save the symbols with proper name
2832 for ( QMap<QString, QgsSymbol *>::iterator it = symbols.begin(); it != symbols.end(); ++it )
2833 {
2834 addSymbol( it.key(), it.value() );
2835 }
2836 }
2837
2838 // load color ramps
2839 QDomElement rampsElement = docEl.firstChildElement( u"colorramps"_s );
2840 e = rampsElement.firstChildElement();
2841 for ( ; !e.isNull(); e = e.nextSiblingElement() )
2842 {
2843 const int entityAddedVersion = e.attribute( u"addedVersion"_s ).toInt();
2844 if ( entityAddedVersion != 0 && sinceVersion != -1 && entityAddedVersion <= sinceVersion )
2845 {
2846 // skip the ramp, should already be present
2847 continue;
2848 }
2849
2850 if ( e.tagName() == "colorramp"_L1 )
2851 {
2852 QString name = e.attribute( u"name"_s );
2853 QStringList tags;
2854 if ( e.hasAttribute( u"tags"_s ) )
2855 {
2856 tags = e.attribute( u"tags"_s ).split( ',' );
2857 }
2858 bool favorite = false;
2859 if ( e.hasAttribute( u"favorite"_s ) && e.attribute( u"favorite"_s ) == "1"_L1 )
2860 {
2861 favorite = true;
2862 }
2863
2864 std::unique_ptr< QgsColorRamp > ramp = QgsSymbolLayerUtils::loadColorRamp( e );
2865 if ( ramp )
2866 {
2867 QgsColorRamp *rampPtr = ramp.get();
2868 addColorRamp( name, ramp.release() );
2869 if ( mCurrentDB )
2870 {
2871 saveColorRamp( name, rampPtr, favorite, tags );
2872 }
2873 }
2874 }
2875 else
2876 {
2877 QgsDebugError( "unknown tag: " + e.tagName() );
2878 }
2879 }
2880
2881 // load text formats
2882
2883 // this is ONLY safe to do if we have a QGuiApplication-- it requires QFontDatabase, which is not available otherwise!
2884 if ( qobject_cast< QGuiApplication * >( QCoreApplication::instance() ) )
2885 {
2886 if ( version == STYLE_CURRENT_VERSION )
2887 {
2888 const QDomElement textFormatElement = docEl.firstChildElement( u"textformats"_s );
2889 e = textFormatElement.firstChildElement();
2890 for ( ; !e.isNull(); e = e.nextSiblingElement() )
2891 {
2892 const int entityAddedVersion = e.attribute( u"addedVersion"_s ).toInt();
2893 if ( entityAddedVersion != 0 && sinceVersion != -1 && entityAddedVersion <= sinceVersion )
2894 {
2895 // skip the format, should already be present
2896 continue;
2897 }
2898
2899 if ( e.tagName() == "textformat"_L1 )
2900 {
2901 QString name = e.attribute( u"name"_s );
2902 QStringList tags;
2903 if ( e.hasAttribute( u"tags"_s ) )
2904 {
2905 tags = e.attribute( u"tags"_s ).split( ',' );
2906 }
2907 bool favorite = false;
2908 if ( e.hasAttribute( u"favorite"_s ) && e.attribute( u"favorite"_s ) == "1"_L1 )
2909 {
2910 favorite = true;
2911 }
2912
2913 QgsTextFormat format;
2914 const QDomElement styleElem = e.firstChildElement();
2915 format.readXml( styleElem, QgsReadWriteContext() );
2916 addTextFormat( name, format );
2917 if ( mCurrentDB )
2918 {
2919 saveTextFormat( name, format, favorite, tags );
2920 }
2921 }
2922 else
2923 {
2924 QgsDebugError( "unknown tag: " + e.tagName() );
2925 }
2926 }
2927 }
2928
2929 // load label settings
2930 if ( version == STYLE_CURRENT_VERSION )
2931 {
2932 const QDomElement labelSettingsElement = docEl.firstChildElement( u"labelsettings"_s );
2933 e = labelSettingsElement.firstChildElement();
2934 for ( ; !e.isNull(); e = e.nextSiblingElement() )
2935 {
2936 const int entityAddedVersion = e.attribute( u"addedVersion"_s ).toInt();
2937 if ( entityAddedVersion != 0 && sinceVersion != -1 && entityAddedVersion <= sinceVersion )
2938 {
2939 // skip the settings, should already be present
2940 continue;
2941 }
2942
2943 if ( e.tagName() == "labelsetting"_L1 )
2944 {
2945 QString name = e.attribute( u"name"_s );
2946 QStringList tags;
2947 if ( e.hasAttribute( u"tags"_s ) )
2948 {
2949 tags = e.attribute( u"tags"_s ).split( ',' );
2950 }
2951 bool favorite = false;
2952 if ( e.hasAttribute( u"favorite"_s ) && e.attribute( u"favorite"_s ) == "1"_L1 )
2953 {
2954 favorite = true;
2955 }
2956
2957 QgsPalLayerSettings settings;
2958 const QDomElement styleElem = e.firstChildElement();
2959 settings.readXml( styleElem, QgsReadWriteContext() );
2960 addLabelSettings( name, settings );
2961 if ( mCurrentDB )
2962 {
2963 saveLabelSettings( name, settings, favorite, tags );
2964 }
2965 }
2966 else
2967 {
2968 QgsDebugError( "unknown tag: " + e.tagName() );
2969 }
2970 }
2971 }
2972 }
2973
2974 // load legend patch shapes
2975 if ( version == STYLE_CURRENT_VERSION )
2976 {
2977 const QDomElement legendPatchShapesElement = docEl.firstChildElement( u"legendpatchshapes"_s );
2978 e = legendPatchShapesElement.firstChildElement();
2979 for ( ; !e.isNull(); e = e.nextSiblingElement() )
2980 {
2981 const int entityAddedVersion = e.attribute( u"addedVersion"_s ).toInt();
2982 if ( entityAddedVersion != 0 && sinceVersion != -1 && entityAddedVersion <= sinceVersion )
2983 {
2984 // skip the shape, should already be present
2985 continue;
2986 }
2987
2988 if ( e.tagName() == "legendpatchshape"_L1 )
2989 {
2990 QString name = e.attribute( u"name"_s );
2991 QStringList tags;
2992 if ( e.hasAttribute( u"tags"_s ) )
2993 {
2994 tags = e.attribute( u"tags"_s ).split( ',' );
2995 }
2996 bool favorite = false;
2997 if ( e.hasAttribute( u"favorite"_s ) && e.attribute( u"favorite"_s ) == "1"_L1 )
2998 {
2999 favorite = true;
3000 }
3001
3002 QgsLegendPatchShape shape;
3003 const QDomElement shapeElem = e.firstChildElement();
3004 shape.readXml( shapeElem, QgsReadWriteContext() );
3005 addLegendPatchShape( name, shape );
3006 if ( mCurrentDB )
3007 {
3008 saveLegendPatchShape( name, shape, favorite, tags );
3009 }
3010 }
3011 else
3012 {
3013 QgsDebugError( "unknown tag: " + e.tagName() );
3014 }
3015 }
3016 }
3017
3018 // load 3d symbols
3019 if ( version == STYLE_CURRENT_VERSION )
3020 {
3021 const QDomElement symbols3DElement = docEl.firstChildElement( u"symbols3d"_s );
3022 e = symbols3DElement.firstChildElement();
3023 for ( ; !e.isNull(); e = e.nextSiblingElement() )
3024 {
3025 const int entityAddedVersion = e.attribute( u"addedVersion"_s ).toInt();
3026 if ( entityAddedVersion != 0 && sinceVersion != -1 && entityAddedVersion <= sinceVersion )
3027 {
3028 // skip the symbol, should already be present
3029 continue;
3030 }
3031
3032 if ( e.tagName() == "symbol3d"_L1 )
3033 {
3034 QString name = e.attribute( u"name"_s );
3035 QStringList tags;
3036 if ( e.hasAttribute( u"tags"_s ) )
3037 {
3038 tags = e.attribute( u"tags"_s ).split( ',' );
3039 }
3040 bool favorite = false;
3041 if ( e.hasAttribute( u"favorite"_s ) && e.attribute( u"favorite"_s ) == "1"_L1 )
3042 {
3043 favorite = true;
3044 }
3045
3046 const QDomElement symbolElem = e.firstChildElement();
3047 const QString type = symbolElem.attribute( u"type"_s );
3048 std::unique_ptr< QgsAbstract3DSymbol > sym( QgsApplication::symbol3DRegistry()->createSymbol( type ) );
3049 if ( sym )
3050 {
3051 sym->readXml( symbolElem, QgsReadWriteContext() );
3052 QgsAbstract3DSymbol *newSym = sym.get();
3053 addSymbol3D( name, sym.release() );
3054 if ( mCurrentDB )
3055 {
3056 saveSymbol3D( name, newSym, favorite, tags );
3057 }
3058 }
3059 }
3060 else
3061 {
3062 QgsDebugError( "unknown tag: " + e.tagName() );
3063 }
3064 }
3065 }
3066
3067 query = qgs_sqlite3_mprintf( "COMMIT TRANSACTION;" );
3068 runEmptyQuery( query );
3069
3070 return true;
3071}
3072
3073bool QgsStyle::isXmlStyleFile( const QString &path )
3074{
3075 QFileInfo fileInfo( path );
3076
3077 if ( fileInfo.suffix().compare( "xml"_L1, Qt::CaseInsensitive ) != 0 )
3078 return false;
3079
3080 // sniff the first line of the file to see if it's a style file
3081 if ( !QFile::exists( path ) )
3082 return false;
3083
3084 QFile inputFile( path );
3085 if ( !inputFile.open( QIODevice::ReadOnly ) )
3086 return false;
3087
3088 QTextStream stream( &inputFile );
3089 const QString line = stream.readLine();
3090 return line == "<!DOCTYPE qgis_style>"_L1;
3091}
3092
3097
3099{
3100 return mReadOnly;
3101}
3102
3103void QgsStyle::setReadOnly( bool readOnly )
3104{
3105 mReadOnly = readOnly;
3106}
3107
3108bool QgsStyle::updateSymbol( StyleEntity type, const QString &name )
3109{
3110 QDomDocument doc( u"dummy"_s );
3111 QDomElement symEl;
3112 QByteArray xmlArray;
3113 QTextStream stream( &xmlArray );
3114
3115 QString query;
3116
3117 switch ( type )
3118 {
3119 case SymbolEntity:
3120 {
3121 // check if it is an existing symbol
3122 auto it = mSymbols.constFind( name );
3123 if ( it == mSymbols.constEnd() || !it.value() )
3124 {
3125 QgsDebugError( u"Update request received for unavailable symbol"_s );
3126 return false;
3127 }
3128
3129 symEl = QgsSymbolLayerUtils::saveSymbol( name, it.value(), doc, QgsReadWriteContext() );
3130 if ( symEl.isNull() )
3131 {
3132 QgsDebugError( u"Couldn't convert symbol to valid XML!"_s );
3133 return false;
3134 }
3135 symEl.save( stream, 4 );
3136 query = qgs_sqlite3_mprintf( "UPDATE symbol SET xml='%q' WHERE name='%q';",
3137 xmlArray.constData(), name.toUtf8().constData() );
3138 break;
3139 }
3140
3141 case Symbol3DEntity:
3142 {
3143 // check if it is an existing symbol
3144 auto it = m3dSymbols.constFind( name );
3145 if ( it == m3dSymbols.constEnd() || !it.value() )
3146 {
3147 QgsDebugError( u"Update request received for unavailable symbol"_s );
3148 return false;
3149 }
3150
3151 symEl = doc.createElement( u"symbol"_s );
3152 symEl.setAttribute( u"type"_s, it.value()->type() );
3153 it.value()->writeXml( symEl, QgsReadWriteContext() );
3154 if ( symEl.isNull() )
3155 {
3156 QgsDebugError( u"Couldn't convert symbol to valid XML!"_s );
3157 return false;
3158 }
3159 symEl.save( stream, 4 );
3160 query = qgs_sqlite3_mprintf( "UPDATE symbol3d SET xml='%q' WHERE name='%q';",
3161 xmlArray.constData(), name.toUtf8().constData() );
3162 break;
3163 }
3164
3165 case ColorrampEntity:
3166 {
3167 auto it = mColorRamps.constFind( name );
3168 if ( it == mColorRamps.constEnd() || !it.value() )
3169 {
3170 QgsDebugError( u"Update requested for unavailable color ramp."_s );
3171 return false;
3172 }
3173
3174 symEl = QgsSymbolLayerUtils::saveColorRamp( name, it.value(), doc );
3175 if ( symEl.isNull() )
3176 {
3177 QgsDebugError( u"Couldn't convert color ramp to valid XML!"_s );
3178 return false;
3179 }
3180 symEl.save( stream, 4 );
3181 query = qgs_sqlite3_mprintf( "UPDATE colorramp SET xml='%q' WHERE name='%q';",
3182 xmlArray.constData(), name.toUtf8().constData() );
3183 break;
3184 }
3185
3186 case TextFormatEntity:
3187 {
3188 auto it = mTextFormats.constFind( name );
3189 if ( it == mTextFormats.constEnd() )
3190 {
3191 QgsDebugError( u"Update requested for unavailable text format."_s );
3192 return false;
3193 }
3194
3195 symEl = it.value().writeXml( doc, QgsReadWriteContext() );
3196 if ( symEl.isNull() )
3197 {
3198 QgsDebugError( u"Couldn't convert text format to valid XML!"_s );
3199 return false;
3200 }
3201 symEl.save( stream, 4 );
3202 query = qgs_sqlite3_mprintf( "UPDATE textformat SET xml='%q' WHERE name='%q';",
3203 xmlArray.constData(), name.toUtf8().constData() );
3204 break;
3205 }
3206
3208 {
3209 auto it = mLabelSettings.constFind( name );
3210 if ( it == mLabelSettings.constEnd() )
3211 {
3212 QgsDebugError( u"Update requested for unavailable label settings."_s );
3213 return false;
3214 }
3215
3216 symEl = it.value().writeXml( doc, QgsReadWriteContext() );
3217 if ( symEl.isNull() )
3218 {
3219 QgsDebugError( u"Couldn't convert label settings to valid XML!"_s );
3220 return false;
3221 }
3222 symEl.save( stream, 4 );
3223 query = qgs_sqlite3_mprintf( "UPDATE labelsettings SET xml='%q' WHERE name='%q';",
3224 xmlArray.constData(), name.toUtf8().constData() );
3225 break;
3226 }
3227
3229 {
3230 auto it = mLegendPatchShapes.constFind( name );
3231 if ( it == mLegendPatchShapes.constEnd() )
3232 {
3233 QgsDebugError( u"Update requested for unavailable legend patch shape."_s );
3234 return false;
3235 }
3236
3237 symEl = doc.createElement( u"shape"_s );
3238 it.value().writeXml( symEl, doc, QgsReadWriteContext() );
3239 symEl.save( stream, 4 );
3240 query = qgs_sqlite3_mprintf( "UPDATE legendpatchshapes SET xml='%q' WHERE name='%q';",
3241 xmlArray.constData(), name.toUtf8().constData() );
3242 break;
3243 }
3244
3245 case TagEntity:
3246 case SmartgroupEntity:
3247 {
3248 QgsDebugError( u"Updating the unsupported StyleEntity"_s );
3249 return false;
3250 }
3251 }
3252
3253
3254 if ( !runEmptyQuery( query ) )
3255 {
3256 QgsDebugError( u"Couldn't update symbol into the database!"_s );
3257 return false;
3258 }
3259 else
3260 {
3261 switch ( type )
3262 {
3263 case SymbolEntity:
3264 emit symbolChanged( name );
3265 break;
3266
3267 case ColorrampEntity:
3268 emit rampChanged( name );
3269 break;
3270
3271 case TextFormatEntity:
3272 emit textFormatChanged( name );
3273 break;
3274
3276 emit labelSettingsChanged( name );
3277 break;
3278
3280 case TagEntity:
3281 case SmartgroupEntity:
3282 case Symbol3DEntity:
3283 break;
3284 }
3285 emit entityChanged( type, name );
3286 }
3287 return true;
3288}
3289
3290void QgsStyle::clearCachedTags( QgsStyle::StyleEntity type, const QString &name )
3291{
3292 mCachedTags[ type ].remove( name );
3293}
3294
3295bool QgsStyle::createStyleMetadataTableIfNeeded()
3296{
3297 // make sure metadata table exists
3298 QString query = qgs_sqlite3_mprintf( "SELECT name FROM sqlite_master WHERE name='stylemetadata'" );
3299 sqlite3_statement_unique_ptr statement;
3300 int rc;
3301 statement = mCurrentDB.prepare( query, rc );
3302
3303 if ( rc != SQLITE_OK || sqlite3_step( statement.get() ) != SQLITE_ROW )
3304 {
3305 // no metadata table
3306 query = qgs_sqlite3_mprintf( "CREATE TABLE stylemetadata("\
3307 "id INTEGER PRIMARY KEY,"\
3308 "key TEXT UNIQUE,"\
3309 "value TEXT);" );
3310 runEmptyQuery( query );
3311 query = qgs_sqlite3_mprintf( "INSERT INTO stylemetadata VALUES (NULL, '%q', '%q')", "version", QString::number( Qgis::versionInt() ).toUtf8().constData() );
3312 runEmptyQuery( query );
3313 return true;
3314 }
3315 else
3316 {
3317 return false;
3318 }
3319}
3320
3321void QgsStyle::upgradeIfRequired()
3322{
3323 // make sure metadata table exists
3324 int dbVersion = 0;
3325 if ( !createStyleMetadataTableIfNeeded() )
3326 {
3327 const QString query = qgs_sqlite3_mprintf( "SELECT value FROM stylemetadata WHERE key='version'" );
3328 int rc;
3329 sqlite3_statement_unique_ptr statement = mCurrentDB.prepare( query, rc );
3330 if ( rc == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
3331 {
3332 dbVersion = statement.columnAsText( 0 ).toInt();
3333 }
3334 }
3335
3336 if ( dbVersion < Qgis::versionInt() )
3337 {
3338 // do upgrade
3339 if ( importXml( QgsApplication::defaultStylePath(), dbVersion ) )
3340 {
3341 const QString query = qgs_sqlite3_mprintf( "UPDATE stylemetadata SET value='%q' WHERE key='version'", QString::number( Qgis::versionInt() ).toUtf8().constData() );
3342 runEmptyQuery( query );
3343 }
3344 }
3345}
3346
3347QString QgsStyle::entityTableName( QgsStyle::StyleEntity type )
3348{
3349 switch ( type )
3350 {
3351 case SymbolEntity:
3352 return u"symbol"_s;
3353
3354 case ColorrampEntity:
3355 return u"colorramp"_s;
3356
3357 case TextFormatEntity:
3358 return u"textformat"_s;
3359
3361 return u"labelsettings"_s;
3362
3364 return u"legendpatchshapes"_s;
3365
3366 case Symbol3DEntity:
3367 return u"symbol3d"_s;
3368
3369 case TagEntity:
3370 return u"tag"_s;
3371
3372 case SmartgroupEntity:
3373 return u"smartgroup"_s;
3374 }
3375 return QString();
3376}
3377
3378QString QgsStyle::tagmapTableName( QgsStyle::StyleEntity type )
3379{
3380 switch ( type )
3381 {
3382 case SymbolEntity:
3383 return u"tagmap"_s;
3384
3385 case ColorrampEntity:
3386 return u"ctagmap"_s;
3387
3388 case TextFormatEntity:
3389 return u"tftagmap"_s;
3390
3392 return u"lstagmap"_s;
3393
3395 return u"lpstagmap"_s;
3396
3397 case Symbol3DEntity:
3398 return u"symbol3dtagmap"_s;
3399
3400 case TagEntity:
3401 case SmartgroupEntity:
3402 break;
3403 }
3404 return QString();
3405}
3406
3407QString QgsStyle::tagmapEntityIdFieldName( QgsStyle::StyleEntity type )
3408{
3409 switch ( type )
3410 {
3411 case SymbolEntity:
3412 return u"symbol_id"_s;
3413
3414 case ColorrampEntity:
3415 return u"colorramp_id"_s;
3416
3417 case TextFormatEntity:
3418 return u"textformat_id"_s;
3419
3421 return u"labelsettings_id"_s;
3422
3424 return u"legendpatchshape_id"_s;
3425
3426 case Symbol3DEntity:
3427 return u"symbol3d_id"_s;
3428
3429 case TagEntity:
3430 case SmartgroupEntity:
3431 break;
3432 }
3433 return QString();
3434}
3435
3440
3445
3450
3455
3460
@ ScaleArea
Calculate scale by the area.
Definition qgis.h:644
@ Circle
Circle.
Definition qgis.h:3160
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:113
const QgsProjectStyleSettings * styleSettings() const
Returns the project's style settings, which contains settings and properties relating to how a QgsPro...
A container for the context for various read/write operations on objects.
Scoped object for logging of the runtime for a single operation or group of operations.
Stores settings for use within QGIS.
Definition qgssettings.h:68
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
A color ramp entity for QgsStyle databases.
Definition qgsstyle.h: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