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