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