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