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