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