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