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