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