QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
qgsstylev2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsstylev2.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 "qgsstylev2.h"
17 
18 #include "qgssymbolv2.h"
19 #include "qgsvectorcolorrampv2.h"
20 
22 
23 #include "qgsapplication.h"
24 #include "qgslogger.h"
25 
26 #include <QDomDocument>
27 #include <QDomElement>
28 #include <QDomNode>
29 #include <QDomNodeList>
30 #include <QFile>
31 #include <QTextStream>
32 #include <QByteArray>
33 
34 #include <sqlite3.h>
35 
36 #define STYLE_CURRENT_VERSION "1"
37 
39 
40 
42 {
43  mCurrentDB = nullptr;
44 }
45 
47 {
48  clear();
49 }
50 
52 {
53  if ( !mDefaultStyle )
54  {
55  QString styleFilename = QgsApplication::userStyleV2Path();
56 
57  // copy default style if user style doesn't exist
58  if ( !QFile::exists( styleFilename ) )
59  {
61  }
62 
64  mDefaultStyle->load( styleFilename );
65  }
66  return mDefaultStyle;
67 }
68 
69 
71 {
72  qDeleteAll( mSymbols );
73  qDeleteAll( mColorRamps );
74 
75  mSymbols.clear();
77  if ( mCurrentDB )
78  sqlite3_close( mCurrentDB );
79 }
80 
81 bool QgsStyleV2::addSymbol( const QString& name, QgsSymbolV2* symbol, bool update )
82 {
83  if ( !symbol || name.isEmpty() )
84  return false;
85 
86  // delete previous symbol (if any)
87  if ( mSymbols.contains( name ) )
88  {
89  // TODO remove groups and tags?
90  delete mSymbols.value( name );
91  mSymbols.insert( name, symbol );
92  if ( update )
93  updateSymbol( SymbolEntity, name );
94  }
95  else
96  {
97  mSymbols.insert( name, symbol );
98  if ( update )
99  saveSymbol( name, symbol, 0, QStringList() );
100  }
101 
102  return true;
103 }
104 
105 bool QgsStyleV2::saveSymbol( const QString& name, QgsSymbolV2* symbol, int groupid, const QStringList& tags )
106 {
107  // TODO add support for groups
108  QDomDocument doc( "dummy" );
109  QDomElement symEl = QgsSymbolLayerV2Utils::saveSymbol( name, symbol, doc );
110  if ( symEl.isNull() )
111  {
112  QgsDebugMsg( "Couldn't convert symbol to valid XML!" );
113  return false;
114  }
115 
116  QByteArray xmlArray;
117  QTextStream stream( &xmlArray );
118  stream.setCodec( "UTF-8" );
119  symEl.save( stream, 4 );
120  char *query = sqlite3_mprintf( "INSERT INTO symbol VALUES (NULL, '%q', '%q', %d);",
121  name.toUtf8().constData(), xmlArray.constData(), groupid );
122 
123  if ( !runEmptyQuery( query ) )
124  {
125  QgsDebugMsg( "Couldn't insert symbol into the database!" );
126  return false;
127  }
128 
129  tagSymbol( SymbolEntity, name, tags );
130 
131  emit symbolSaved( name, symbol );
132 
133  return true;
134 }
135 
137 {
138  QgsSymbolV2 *symbol = mSymbols.take( name );
139  if ( !symbol )
140  return false;
141 
142  // remove from map and delete
143  delete symbol;
144 
145  // TODO
146  // Simplify this work here, its STUPID to run two DB queries for the sake of remove()
147  if ( !mCurrentDB )
148  {
149  QgsDebugMsg( "Sorry! Cannot open database to tag." );
150  return false;
151  }
152 
153  int symbolid = symbolId( name );
154  if ( !symbolid )
155  {
156  QgsDebugMsg( "No such symbol for deleting in database: " + name + ". Cheers." );
157  }
158 
159  remove( SymbolEntity, symbolid );
160 
161  return true;
162 }
163 
165 {
166  const QgsSymbolV2 *symbol = symbolRef( name );
167  return symbol ? symbol->clone() : nullptr;
168 }
169 
171 {
172  return mSymbols.value( name );
173 }
174 
176 {
177  return mSymbols.count();
178 }
179 
181 {
182  return mSymbols.keys();
183 }
184 
185 
187 {
188  if ( !colorRamp || name.isEmpty() )
189  return false;
190 
191  // delete previous color ramps (if any)
192  if ( mColorRamps.contains( name ) )
193  {
194  // TODO remove groups and tags?
195  delete mColorRamps.value( name );
196  mColorRamps.insert( name, colorRamp );
197  if ( update )
198  updateSymbol( ColorrampEntity, name );
199  }
200  else
201  {
202  mColorRamps.insert( name, colorRamp );
203  if ( update )
204  saveColorRamp( name, colorRamp, 0, QStringList() );
205  }
206 
207  return true;
208 }
209 
210 bool QgsStyleV2::saveColorRamp( const QString& name, QgsVectorColorRampV2* ramp, int groupid, const QStringList& tags )
211 {
212  // insert it into the database
213  QDomDocument doc( "dummy" );
214  QDomElement rampEl = QgsSymbolLayerV2Utils::saveColorRamp( name, ramp, doc );
215  if ( rampEl.isNull() )
216  {
217  QgsDebugMsg( "Couldn't convert color ramp to valid XML!" );
218  return false;
219  }
220 
221  QByteArray xmlArray;
222  QTextStream stream( &xmlArray );
223  stream.setCodec( "UTF-8" );
224  rampEl.save( stream, 4 );
225  char *query = sqlite3_mprintf( "INSERT INTO colorramp VALUES (NULL, '%q', '%q', %d);",
226  name.toUtf8().constData(), xmlArray.constData(), groupid );
227 
228  if ( !runEmptyQuery( query ) )
229  {
230  QgsDebugMsg( "Couldn't insert colorramp into the database!" );
231  return false;
232  }
233 
234  tagSymbol( ColorrampEntity, name, tags );
235 
236  return true;
237 }
238 
240 {
241  QgsVectorColorRampV2 *ramp = mColorRamps.take( name );
242  if ( !ramp )
243  return false;
244 
245  char *query = sqlite3_mprintf( "DELETE FROM colorramp WHERE name='%q'", name.toUtf8().constData() );
246  if ( !runEmptyQuery( query ) )
247  {
248  QgsDebugMsg( "Couldn't remove color ramp from the database." );
249  return false;
250  }
251 
252  delete ramp;
253 
254  return true;
255 }
256 
258 {
259  const QgsVectorColorRampV2 *ramp = colorRampRef( name );
260  return ramp ? ramp->clone() : nullptr;
261 }
262 
264 {
265  return mColorRamps.value( name );
266 }
267 
269 {
270  return mColorRamps.count();
271 }
272 
274 {
275  return mColorRamps.keys();
276 }
277 
278 bool QgsStyleV2::openDB( const QString& filename )
279 {
280  int rc = sqlite3_open( filename.toUtf8(), &mCurrentDB );
281  if ( rc )
282  {
283  mErrorString = "Couldn't open the style database: " + QString( sqlite3_errmsg( mCurrentDB ) );
284  sqlite3_close( mCurrentDB );
285  return false;
286  }
287 
288  return true;
289 }
290 
291 bool QgsStyleV2::load( const QString& filename )
292 {
294 
295  // Open the sqlite database
296  if ( !openDB( filename ) )
297  {
298  mErrorString = "Unable to open database file specified";
300  return false;
301  }
302 
303  // Make sure there are no Null fields in parenting symbols ang groups
304  char *query = sqlite3_mprintf( "UPDATE symbol SET groupid=0 WHERE groupid IS NULL;"
305  "UPDATE colorramp SET groupid=0 WHERE groupid IS NULL;"
306  "UPDATE symgroup SET parent=0 WHERE parent IS NULL;" );
307  runEmptyQuery( query );
308 
309  // First create all the main symbols
310  query = sqlite3_mprintf( "SELECT * FROM symbol" );
311 
312  sqlite3_stmt *ppStmt;
313  int nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
314  while ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
315  {
316  QDomDocument doc;
317  QString symbol_name = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, SymbolName ) ) );
318  QString xmlstring = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, SymbolXML ) ) );
319  if ( !doc.setContent( xmlstring ) )
320  {
321  QgsDebugMsg( "Cannot open symbol " + symbol_name );
322  continue;
323  }
324 
325  QDomElement symElement = doc.documentElement();
327  if ( symbol )
328  mSymbols.insert( symbol_name, symbol );
329  }
330 
331  sqlite3_finalize( ppStmt );
332 
333  query = sqlite3_mprintf( "SELECT * FROM colorramp" );
334  nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
335  while ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
336  {
337  QDomDocument doc;
338  QString ramp_name = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, ColorrampName ) ) );
339  QString xmlstring = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, ColorrampXML ) ) );
340  if ( !doc.setContent( xmlstring ) )
341  {
342  QgsDebugMsg( "Cannot open symbol " + ramp_name );
343  continue;
344  }
345  QDomElement rampElement = doc.documentElement();
347  if ( ramp )
348  mColorRamps.insert( ramp_name, ramp );
349  }
350 
351  mFileName = filename;
352  return true;
353 }
354 
355 
356 
357 bool QgsStyleV2::save( QString filename )
358 {
360 
361  if ( filename.isEmpty() )
362  filename = mFileName;
363 
364  // TODO evaluate the requirement of this function and change implementation accordingly
365  // TODO remove QEXPECT_FAIL from TestStyleV2::testSaveLoad() when done
366 #if 0
367  QDomDocument doc( "qgis_style" );
368  QDomElement root = doc.createElement( "qgis_style" );
369  root.setAttribute( "version", STYLE_CURRENT_VERSION );
370  doc.appendChild( root );
371 
372  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( mSymbols, "symbols", doc );
373 
374  QDomElement rampsElem = doc.createElement( "colorramps" );
375 
376  // save color ramps
378  {
379  QDomElement rampEl = QgsSymbolLayerV2Utils::saveColorRamp( itr.key(), itr.value(), doc );
380  rampsElem.appendChild( rampEl );
381  }
382 
383  root.appendChild( symbolsElem );
384  root.appendChild( rampsElem );
385 
386  // save
387  QFile f( filename );
388  if ( !f.open( QFile::WriteOnly ) )
389  {
390  mErrorString = "Couldn't open file for writing: " + filename;
391  return false;
392  }
393  QTextStream ts( &f );
394  ts.setCodec( "UTF-8" );
395  doc.save( ts, 2 );
396  f.close();
397 #endif
398 
399  mFileName = filename;
400  return true;
401 }
402 
403 bool QgsStyleV2::renameSymbol( const QString& oldName, const QString& newName )
404 {
405  if ( mSymbols.contains( newName ) )
406  {
407  QgsDebugMsg( "Symbol of new name already exists" );
408  return false;
409  }
410 
411  QgsSymbolV2 *symbol = mSymbols.take( oldName );
412  if ( !symbol )
413  return false;
414 
415  mSymbols.insert( newName, symbol );
416 
417  if ( !mCurrentDB )
418  {
419  QgsDebugMsg( "Sorry! Cannot open database to tag." );
420  return false;
421  }
422 
423  int symbolid = symbolId( oldName );
424  if ( !symbolid )
425  {
426  QgsDebugMsg( "No such symbol for tagging in database: " + oldName );
427  return false;
428  }
429 
430  rename( SymbolEntity, symbolid, newName );
431 
432  return true;
433 }
434 
435 bool QgsStyleV2::renameColorRamp( const QString& oldName, const QString& newName )
436 {
437  if ( mColorRamps.contains( newName ) )
438  {
439  QgsDebugMsg( "Color ramp of new name already exists." );
440  return false;
441  }
442 
443  QgsVectorColorRampV2 *ramp = mColorRamps.take( oldName );
444  if ( !ramp )
445  return false;
446 
447  mColorRamps.insert( newName, ramp );
448 
449  int rampid = 0;
450  sqlite3_stmt *ppStmt;
451  char *query = sqlite3_mprintf( "SELECT id FROM colorramp WHERE name='%q'", oldName.toUtf8().constData() );
452  int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
453  if ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
454  {
455  rampid = sqlite3_column_int( ppStmt, 0 );
456  }
457  sqlite3_finalize( ppStmt );
458  rename( ColorrampEntity, rampid, newName );
459 
460  return true;
461 }
462 
464 {
466  sqlite3_stmt *ppStmt;
467  const char *query = "SELECT * FROM symgroup";
468  int nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
469  while ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
470  {
471  groupNames << QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, SymgroupName ) ) );
472  }
473  sqlite3_finalize( ppStmt );
474  return groupNames;
475 }
476 
478 {
480  sqlite3_stmt *ppStmt;
481  const char *query = "SELECT * FROM symgroup";
482  int nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
483  while ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
484  {
485  groupIds << QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, SymgroupId ) ) ).toInt();
486  }
487  sqlite3_finalize( ppStmt );
488  return groupIds;
489 }
490 
492 {
493  // get the name list from the sqlite database and return as a QStringList
494  if ( !mCurrentDB )
495  {
496  QgsDebugMsg( "Cannot open database for listing groups" );
497  return QgsSymbolGroupMap();
498  }
499 
500  char *query = nullptr;
501  int nError;
502  sqlite3_stmt *ppStmt;
503 
504  // decide the query to be run based on parent group
505  if ( parent == "" || parent == QString() )
506  {
507  query = sqlite3_mprintf( "SELECT * FROM symgroup WHERE parent=0" );
508  }
509  else
510  {
511  char *subquery = sqlite3_mprintf( "SELECT * FROM symgroup WHERE name='%q'", parent.toUtf8().constData() );
512  nError = sqlite3_prepare_v2( mCurrentDB, subquery, -1, &ppStmt, nullptr );
513  if ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
514  {
515  query = sqlite3_mprintf( "SELECT * FROM symgroup WHERE parent=%d", sqlite3_column_int( ppStmt, SymgroupId ) );
516  }
517  sqlite3_finalize( ppStmt );
518  }
519 
520  if ( !query )
521  return QgsSymbolGroupMap();
522 
524 
525  // Now run the query and retrieve the group names
526  nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
527  while ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
528  {
529  QString group = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, SymgroupName ) ) );
530  groupNames.insert( sqlite3_column_int( ppStmt, SymgroupId ), group );
531  }
532 
533  sqlite3_finalize( ppStmt );
534 
535  return groupNames;
536 }
537 
539 {
540  if ( !mCurrentDB )
541  {
542  QgsDebugMsg( QString( "Cannot Open database for getting group symbols of groupid: %1" ).arg( groupid ) );
543  return QStringList();
544  }
545 
546  char *query;
547  if ( type == SymbolEntity )
548  {
549  query = sqlite3_mprintf( "SELECT name FROM symbol WHERE groupid=%d", groupid );
550  }
551  else if ( type == ColorrampEntity )
552  {
553  query = sqlite3_mprintf( "SELECT name FROM colorramp WHERE groupid=%d", groupid );
554  }
555  else
556  {
557  QgsDebugMsg( "No such style entity" );
558  return QStringList();
559  }
560 
561  sqlite3_stmt *ppStmt;
562  int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
563 
564  QStringList symbols;
565  while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
566  {
567  symbols << QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
568  }
569 
570  sqlite3_finalize( ppStmt );
571 
572  return symbols;
573 }
574 
576 {
577  if ( !mCurrentDB )
578  {
579  QgsDebugMsg( QString( "Cannot open database to get symbols of tagid %1" ).arg( tagid ) );
580  return QStringList();
581  }
582 
583  char *subquery;
584  if ( type == SymbolEntity )
585  {
586  subquery = sqlite3_mprintf( "SELECT symbol_id FROM tagmap WHERE tag_id=%d", tagid );
587  }
588  else if ( type == ColorrampEntity )
589  {
590  subquery = sqlite3_mprintf( "SELECT symbol_id FROM ctagmap WHERE tag_id=%d", tagid );
591  }
592  else
593  {
594  QgsDebugMsg( "Unknown Entity" );
595  return QStringList();
596  }
597 
598  sqlite3_stmt *ppStmt;
599  int nErr = sqlite3_prepare_v2( mCurrentDB, subquery, -1, &ppStmt, nullptr );
600 
601  // get the symbol <-> tag connection from table 'tagmap'
602  QStringList symbols;
603  while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
604  {
605  int symbolId = sqlite3_column_int( ppStmt, 0 );
606 
607  char *query = type == SymbolEntity
608  ? sqlite3_mprintf( "SELECT name FROM symbol WHERE id=%d", symbolId )
609  : sqlite3_mprintf( "SELECT name FROM colorramp WHERE id=%d", symbolId );
610 
611  sqlite3_stmt *ppStmt2;
612  int sErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt2, nullptr );
613  while ( sErr == SQLITE_OK && sqlite3_step( ppStmt2 ) == SQLITE_ROW )
614  {
615  symbols << QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt2, 0 ) ) );
616  }
617  sqlite3_finalize( ppStmt2 );
618  }
619  sqlite3_finalize( ppStmt );
620 
621  return symbols;
622 }
623 
624 int QgsStyleV2::addGroup( const QString& groupName, int parentid )
625 {
626  if ( !mCurrentDB )
627  return 0;
628 
629  char *query = sqlite3_mprintf( "INSERT INTO symgroup VALUES (NULL, '%q', %d)", groupName.toUtf8().constData(), parentid );
630 
631  sqlite3_stmt *ppStmt;
632  int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
633  if ( nErr == SQLITE_OK )
634  ( void )sqlite3_step( ppStmt );
635 
636  sqlite3_finalize( ppStmt );
637 
638  return static_cast< int >( sqlite3_last_insert_rowid( mCurrentDB ) );
639 }
640 
641 int QgsStyleV2::addTag( const QString& tagname )
642 {
643  if ( !mCurrentDB )
644  return 0;
645  sqlite3_stmt *ppStmt;
646 
647  char *query = sqlite3_mprintf( "INSERT INTO tag VALUES (NULL, '%q')", tagname.toUtf8().constData() );
648  int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
649  if ( nErr == SQLITE_OK )
650  ( void )sqlite3_step( ppStmt );
651  sqlite3_finalize( ppStmt );
652 
653  return static_cast< int >( sqlite3_last_insert_rowid( mCurrentDB ) );
654 }
655 
657 {
658  if ( !mCurrentDB )
659  return QStringList();
660 
661  sqlite3_stmt *ppStmt;
662 
663  char *query = sqlite3_mprintf( "SELECT name FROM tag" );
664  int nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
665 
666  QStringList tagList;
667  while ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
668  {
669  tagList << QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
670  }
671 
672  sqlite3_finalize( ppStmt );
673 
674  return tagList;
675 }
676 
677 void QgsStyleV2::rename( StyleEntity type, int id, const QString& newName )
678 {
679  char *query;
680  switch ( type )
681  {
682  case SymbolEntity:
683  query = sqlite3_mprintf( "UPDATE symbol SET name='%q' WHERE id=%d", newName.toUtf8().constData(), id );
684  break;
685  case GroupEntity:
686  query = sqlite3_mprintf( "UPDATE symgroup SET name='%q' WHERE id=%d", newName.toUtf8().constData(), id );
687  break;
688  case TagEntity:
689  query = sqlite3_mprintf( "UPDATE tag SET name='%q' WHERE id=%d", newName.toUtf8().constData(), id );
690  break;
691  case ColorrampEntity:
692  query = sqlite3_mprintf( "UPDATE colorramp SET name='%q' WHERE id=%d", newName.toUtf8().constData(), id );
693  break;
694  case SmartgroupEntity:
695  query = sqlite3_mprintf( "UPDATE smartgroup SET name='%q' WHERE id=%d", newName.toUtf8().constData(), id );
696  break;
697  default:
698  QgsDebugMsg( "Invalid Style Entity indicated" );
699  return;
700  }
701  if ( !runEmptyQuery( query ) )
702  mErrorString = "Could not rename!";
703 }
704 
706 {
707  char *query = sqlite3_mprintf( "SELECT parent FROM symgroup WHERE id=%d", id );
708 
709  sqlite3_stmt *ppStmt;
710  int err = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
711 
712  int parentid = 0;
713  if ( err == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
714  parentid = sqlite3_column_int( ppStmt, 0 );
715 
716  sqlite3_finalize( ppStmt );
717 
718  return sqlite3_mprintf( "UPDATE symbol SET groupid=%d WHERE groupid=%d;"
719  "UPDATE symgroup SET parent=%d WHERE parent=%d;"
720  "DELETE FROM symgroup WHERE id=%d", parentid, id, parentid, id, id );
721 }
722 
723 void QgsStyleV2::remove( StyleEntity type, int id )
724 {
725  char *query;
726  switch ( type )
727  {
728  case SymbolEntity:
729  query = sqlite3_mprintf( "DELETE FROM symbol WHERE id=%d; DELETE FROM tagmap WHERE symbol_id=%d", id, id );
730  break;
731  case GroupEntity:
732  query = getGroupRemoveQuery( id );
733  break;
734  case TagEntity:
735  query = sqlite3_mprintf( "DELETE FROM tag WHERE id=%d; DELETE FROM tagmap WHERE tag_id=%d", id, id );
736  break;
737  case ColorrampEntity:
738  query = sqlite3_mprintf( "DELETE FROM colorramp WHERE id=%d", id );
739  break;
740  case SmartgroupEntity:
741  query = sqlite3_mprintf( "DELETE FROM smartgroup WHERE id=%d", id );
742  break;
743  default:
744  QgsDebugMsg( "Invalid Style Entity indicated" );
745  return;
746  }
747 
748  if ( !runEmptyQuery( query ) )
749  {
750  QgsDebugMsg( "Could not delete entity!" );
751  }
752 }
753 
754 bool QgsStyleV2::runEmptyQuery( char *query, bool freeQuery )
755 {
756  if ( !mCurrentDB )
757  return false;
758 
759  char *zErr = nullptr;
760  int nErr = sqlite3_exec( mCurrentDB, query, nullptr, nullptr, &zErr );
761 
762  if ( freeQuery )
763  {
764  sqlite3_free( query );
765  }
766 
767  if ( nErr != SQLITE_OK )
768  {
769  QgsDebugMsg( zErr );
770  }
771 
772  return zErr == SQLITE_OK;
773 }
774 
775 bool QgsStyleV2::group( StyleEntity type, const QString& name, int groupid )
776 {
777  char *query;
778 
779  switch ( type )
780  {
781  case SymbolEntity:
782  query = sqlite3_mprintf( "UPDATE symbol SET groupid=%d WHERE name='%q'", groupid, name.toUtf8().constData() );
783  break;
784  case ColorrampEntity:
785  query = sqlite3_mprintf( "UPDATE colorramp SET groupid=%d WHERE name='%q'", groupid, name.toUtf8().constData() );
786  break;
787 
788  default:
789  QgsDebugMsg( "Wrong entity value. cannot apply group" );
790  return false;
791  }
792 
793  return runEmptyQuery( query );
794 }
795 
797 {
798  if ( !mCurrentDB )
799  {
800  QgsDebugMsg( "Sorry! Cannot open database to search" );
801  return QStringList();
802  }
803 
804  // first find symbols with matching name
805  QString item = ( type == SymbolEntity ) ? "symbol" : "colorramp";
806  char *query = sqlite3_mprintf( "SELECT name FROM %q WHERE name LIKE '%%%q%%'",
807  item.toUtf8().constData(), qword.toUtf8().constData() );
808 
809  sqlite3_stmt *ppStmt;
810  int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
811 
812  QSet< QString > symbols;
813  while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
814  {
815  symbols << QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
816  }
817 
818  sqlite3_finalize( ppStmt );
819 
820  // next add symbols with matching tags
821  query = sqlite3_mprintf( "SELECT id FROM tag WHERE name LIKE '%%%q%%'", qword.toUtf8().constData() );
822  nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, NULL );
823 
824  QStringList tagids;
825  while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
826  {
827  tagids << QString::fromUtf8(( const char * ) sqlite3_column_text( ppStmt, 0 ) );
828  }
829 
830  sqlite3_finalize( ppStmt );
831 
832 
833  QString dummy = tagids.join( ", " );
834 
835  if ( type == SymbolEntity )
836  {
837  query = sqlite3_mprintf( "SELECT symbol_id FROM tagmap WHERE tag_id IN (%q)",
838  dummy.toUtf8().constData() );
839  }
840  else
841  {
842  query = sqlite3_mprintf( "SELECT colorramp_id FROM ctagmap WHERE tag_id IN (%q)",
843  dummy.toUtf8().constData() );
844  }
845  nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, NULL );
846 
847  QStringList symbolids;
848  while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
849  {
850  symbolids << QString::fromUtf8(( const char * ) sqlite3_column_text( ppStmt, 0 ) );
851  }
852 
853  sqlite3_finalize( ppStmt );
854 
855 
856  dummy = symbolids.join( ", " );
857  query = sqlite3_mprintf( "SELECT name FROM %q WHERE id IN (%q)",
858  item.toUtf8().constData(), dummy.toUtf8().constData() );
859  nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, NULL );
860  while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
861  {
862  symbols << QString::fromUtf8(( const char * ) sqlite3_column_text( ppStmt, 0 ) );
863  }
864 
865  sqlite3_finalize( ppStmt );
866 
867  return symbols.toList();
868 }
869 
871 {
872  if ( !mCurrentDB )
873  {
874  QgsDebugMsg( "Sorry! Cannot open database to tag." );
875  return false;
876  }
877 
878  int symbolid = type == SymbolEntity ? symbolId( symbol ) : colorrampId( symbol );
879  if ( !symbolid )
880  {
881  QgsDebugMsg( "No such symbol for tagging in database: " + symbol );
882  return false;
883  }
884 
885 
886  Q_FOREACH ( const QString &tag, tags )
887  {
888  // sql: gets the id of the tag if present or insert the tag and get the id of the tag
889  char *query = sqlite3_mprintf( "SELECT id FROM tag WHERE name='%q'", tag.toUtf8().constData() );
890 
891  sqlite3_stmt *ppStmt;
892  int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
893 
894  int tagid;
895  if ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
896  {
897  tagid = sqlite3_column_int( ppStmt, 0 );
898  }
899  else
900  {
901  tagid = addTag( tag );
902  }
903 
904  sqlite3_finalize( ppStmt );
905 
906  // Now map the tag to the symbol
907  query = type == SymbolEntity
908  ? sqlite3_mprintf( "INSERT INTO tagmap VALUES (%d,%d)", tagid, symbolid )
909  : sqlite3_mprintf( "INSERT INTO ctagmap VALUES (%d,%d)", tagid, symbolid );
910 
911  char *zErr = nullptr;
912  nErr = sqlite3_exec( mCurrentDB, query, nullptr, nullptr, &zErr );
913  if ( nErr )
914  {
915  QgsDebugMsg( zErr );
916  }
917  }
918 
919  return true;
920 }
921 
923 {
924  if ( !mCurrentDB )
925  {
926  QgsDebugMsg( "Sorry! Cannot open database for detgging." );
927  return false;
928  }
929 
930  char *query = type == SymbolEntity
931  ? sqlite3_mprintf( "SELECT id FROM symbol WHERE name='%q'", symbol.toUtf8().constData() )
932  : sqlite3_mprintf( "SELECT id FROM colorramp WHERE name='%q'", symbol.toUtf8().constData() );
933  sqlite3_stmt *ppStmt;
934  int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
935 
936  int symbolid = 0;
937  if ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
938  {
939  symbolid = sqlite3_column_int( ppStmt, 0 );
940  }
941  else
942  {
943  sqlite3_finalize( ppStmt );
944  return false;
945  }
946 
947  sqlite3_finalize( ppStmt );
948 
949  Q_FOREACH ( const QString &tag, tags )
950  {
951  query = sqlite3_mprintf( "SELECT id FROM tag WHERE name='%q'", tag.toUtf8().constData() );
952 
953  sqlite3_stmt *ppStmt2;
954  nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt2, nullptr );
955 
956  int tagid = 0;
957  if ( nErr == SQLITE_OK && sqlite3_step( ppStmt2 ) == SQLITE_ROW )
958  {
959  tagid = sqlite3_column_int( ppStmt2, 0 );
960  }
961 
962  sqlite3_finalize( ppStmt2 );
963 
964  if ( tagid )
965  {
966  // remove from the tagmap
967  query = type == SymbolEntity
968  ? sqlite3_mprintf( "DELETE FROM tagmap WHERE tag_id=%d AND symbol_id=%d", tagid, symbolid )
969  : sqlite3_mprintf( "DELETE FROM ctagmap WHERE tag_id=%d AND colorramp_id=%d", tagid, symbolid );
970  runEmptyQuery( query );
971  }
972  }
973 
974  // TODO Perform tag cleanup
975  // check the number of entries for a given tag in the tagmap
976  // if the count is 0, then remove( TagEntity, tagid )
977  return true;
978 }
979 
981 {
982  if ( !mCurrentDB )
983  {
984  QgsDebugMsg( "Sorry! Cannot open database for getting the tags." );
985  return QStringList();
986  }
987 
988  int symbolid = type == SymbolEntity ? symbolId( symbol ) : colorrampId( symbol );
989  if ( !symbolid )
990  return QStringList();
991 
992  // get the ids of tags for the symbol
993  char *query = type == SymbolEntity
994  ? sqlite3_mprintf( "SELECT tag_id FROM tagmap WHERE symbol_id=%d", symbolid )
995  : sqlite3_mprintf( "SELECT tag_id FROM ctagmap WHERE colorramp_id=%d", symbolid );
996 
997  sqlite3_stmt *ppStmt;
998  int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
999 
1000  QStringList tagList;
1001  while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
1002  {
1003  char *subquery = sqlite3_mprintf( "SELECT name FROM tag WHERE id=%d", sqlite3_column_int( ppStmt, 0 ) );
1004 
1005  sqlite3_stmt *ppStmt2;
1006  int pErr = sqlite3_prepare_v2( mCurrentDB, subquery, -1, &ppStmt2, nullptr );
1007  if ( pErr == SQLITE_OK && sqlite3_step( ppStmt2 ) == SQLITE_ROW )
1008  {
1009  tagList << QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt2, 0 ) ) );
1010  }
1011  sqlite3_finalize( ppStmt2 );
1012  }
1013 
1014  sqlite3_finalize( ppStmt );
1015 
1016  return tagList;
1017 }
1018 
1019 int QgsStyleV2::getId( const QString& table, const QString& name )
1020 {
1021  char *query = sqlite3_mprintf( "SELECT id FROM %q WHERE name='%q'", table.toUtf8().constData(), name.toUtf8().constData() );
1022 
1023  sqlite3_stmt *ppStmt;
1024  int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
1025 
1026  int id = 0;
1027  if ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
1028  {
1029  id = sqlite3_column_int( ppStmt, 0 );
1030  }
1031 
1032  sqlite3_finalize( ppStmt );
1033 
1034  return id;
1035 }
1036 
1037 QString QgsStyleV2::getName( const QString& table, int id ) const
1038 {
1039  char *query = sqlite3_mprintf( "SELECT name FROM %q WHERE id='%q'", table.toUtf8().constData(), QString::number( id ).toUtf8().constData() );
1040 
1041  sqlite3_stmt *ppStmt;
1042  int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
1043 
1044  QString name;
1045  if ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
1046  {
1047  name = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
1048  }
1049 
1050  sqlite3_finalize( ppStmt );
1051 
1052  return name;
1053 }
1054 
1056 {
1057  return getId( "symbol", name );
1058 }
1059 
1061 {
1062  return getId( "colorramp", name );
1063 }
1064 
1066 {
1067  return getId( "symgroup", name );
1068 }
1069 
1071 {
1072  return getName( "symgroup", groupId );
1073 }
1074 
1076 {
1077  return getId( "tag", name );
1078 }
1079 
1081 {
1082  return getId( "smartgroup", name );
1083 }
1084 
1085 int QgsStyleV2::addSmartgroup( const QString& name, const QString& op, const QgsSmartConditionMap& conditions )
1086 {
1087  QDomDocument doc( "dummy" );
1088  QDomElement smartEl = doc.createElement( "smartgroup" );
1089  smartEl.setAttribute( "name", name );
1090  smartEl.setAttribute( "operator", op );
1091 
1092  QStringList constraints;
1093  constraints << "tag" << "group" << "name" << "!tag" << "!group" << "!name";
1094 
1095  Q_FOREACH ( const QString &constraint, constraints )
1096  {
1097  QStringList parameters = conditions.values( constraint );
1098  Q_FOREACH ( const QString &param, parameters )
1099  {
1100  QDomElement condEl = doc.createElement( "condition" );
1101  condEl.setAttribute( "constraint", constraint );
1102  condEl.setAttribute( "param", param );
1103  smartEl.appendChild( condEl );
1104  }
1105  }
1106 
1107  QByteArray xmlArray;
1108  QTextStream stream( &xmlArray );
1109  stream.setCodec( "UTF-8" );
1110  smartEl.save( stream, 4 );
1111  char *query = sqlite3_mprintf( "INSERT INTO smartgroup VALUES (NULL, '%q', '%q')",
1112  name.toUtf8().constData(), xmlArray.constData() );
1113 
1114  if ( runEmptyQuery( query ) )
1115  {
1116  return static_cast< int >( sqlite3_last_insert_rowid( mCurrentDB ) );
1117  }
1118  else
1119  {
1120  QgsDebugMsg( "Couldn't insert symbol into the database!" );
1121  return 0;
1122  }
1123 }
1124 
1126 {
1127  if ( !mCurrentDB )
1128  {
1129  QgsDebugMsg( "Cannot open database for listing groups" );
1130  return QgsSymbolGroupMap();
1131  }
1132 
1133  char *query = sqlite3_mprintf( "SELECT * FROM smartgroup" );
1134 
1135  // Now run the query and retrieve the group names
1136  sqlite3_stmt *ppStmt;
1137  int nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
1138 
1140  while ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
1141  {
1142  QString group = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, SmartgroupName ) ) );
1143  groupNames.insert( sqlite3_column_int( ppStmt, SmartgroupId ), group );
1144  }
1145 
1146  sqlite3_finalize( ppStmt );
1147 
1148  return groupNames;
1149 }
1150 
1152 {
1153  if ( !mCurrentDB )
1154  {
1155  QgsDebugMsg( "Cannot open database for listing groups" );
1156  return QStringList();
1157  }
1158 
1159  char *query = sqlite3_mprintf( "SELECT name FROM smartgroup" );
1160 
1161  // Now run the query and retrieve the group names
1162  sqlite3_stmt *ppStmt;
1163  int nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
1164 
1165  QStringList groups;
1166  while ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
1167  {
1168  groups << QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
1169  }
1170 
1171  sqlite3_finalize( ppStmt );
1172 
1173  return groups;
1174 }
1175 
1177 {
1178  QStringList symbols;
1179 
1180  char *query = sqlite3_mprintf( "SELECT xml FROM smartgroup WHERE id=%d", id );
1181 
1182  sqlite3_stmt *ppStmt;
1183  int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
1184  if ( !( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW ) )
1185  {
1186  sqlite3_finalize( ppStmt );
1187  return QStringList();
1188  }
1189  else
1190  {
1191  QDomDocument doc;
1192  QString xmlstr = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
1193  if ( !doc.setContent( xmlstr ) )
1194  {
1195  QgsDebugMsg( QString( "Cannot open smartgroup id: %1" ).arg( id ) );
1196  }
1197  QDomElement smartEl = doc.documentElement();
1198  QString op = smartEl.attribute( "operator" );
1199  QDomNodeList conditionNodes = smartEl.childNodes();
1200 
1201  bool firstSet = true;
1202  for ( int i = 0; i < conditionNodes.count(); i++ )
1203  {
1204  QDomElement condEl = conditionNodes.at( i ).toElement();
1205  QString constraint = condEl.attribute( "constraint" );
1206  QString param = condEl.attribute( "param" );
1207 
1208  QStringList resultNames;
1209  // perform suitable action for the given constraint
1210  if ( constraint == "tag" )
1211  {
1212  resultNames = symbolsWithTag( type, tagId( param ) );
1213  }
1214  else if ( constraint == "group" )
1215  {
1216  // XXX Validating group id might be a good idea here
1217  resultNames = symbolsOfGroup( type, groupId( param ) );
1218 
1219  }
1220  else if ( constraint == "name" )
1221  {
1222  if ( type == SymbolEntity )
1223  {
1224  resultNames = symbolNames().filter( param, Qt::CaseInsensitive );
1225  }
1226  else
1227  {
1228  resultNames = colorRampNames().filter( param, Qt::CaseInsensitive );
1229  }
1230  }
1231  else if ( constraint == "!tag" )
1232  {
1233  resultNames = type == SymbolEntity ? symbolNames() : colorRampNames();
1234  QStringList unwanted = symbolsWithTag( type, tagId( param ) );
1235  Q_FOREACH ( const QString& name, unwanted )
1236  {
1237  resultNames.removeAll( name );
1238  }
1239  }
1240  else if ( constraint == "!group" )
1241  {
1242  resultNames = type == SymbolEntity ? symbolNames() : colorRampNames();
1243  QStringList unwanted = symbolsOfGroup( type, groupId( param ) );
1244  Q_FOREACH ( const QString& name, unwanted )
1245  {
1246  resultNames.removeAll( name );
1247  }
1248  }
1249  else if ( constraint == "!name" )
1250  {
1251  QStringList all = type == SymbolEntity ? symbolNames() : colorRampNames();
1252  Q_FOREACH ( const QString &str, all )
1253  {
1254  if ( !str.contains( param, Qt::CaseInsensitive ) )
1255  resultNames << str;
1256  }
1257  }
1258 
1259  // not apply the operator
1260  if ( firstSet )
1261  {
1262  symbols = resultNames;
1263  firstSet = false;
1264  }
1265  else
1266  {
1267  if ( op == "OR" )
1268  {
1269  symbols << resultNames;
1270  }
1271  else if ( op == "AND" )
1272  {
1273  QStringList dummy = symbols;
1274  symbols.clear();
1275  Q_FOREACH ( const QString &result, resultNames )
1276  {
1277  if ( dummy.contains( result ) )
1278  symbols << result;
1279  }
1280  }
1281  }
1282  } // DOM loop ends here
1283  }
1284 
1285  sqlite3_finalize( ppStmt );
1286 
1287  return symbols;
1288 }
1289 
1291 {
1292  if ( !mCurrentDB )
1293  {
1294  QgsDebugMsg( "Cannot open database for listing groups" );
1295  return QgsSmartConditionMap();
1296  }
1297 
1298  QgsSmartConditionMap condition;
1299 
1300  char *query = sqlite3_mprintf( "SELECT xml FROM smartgroup WHERE id=%d", id );
1301 
1302  sqlite3_stmt *ppStmt;
1303  int nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
1304  if ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
1305  {
1306  QDomDocument doc;
1307  QString xmlstr = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
1308  if ( !doc.setContent( xmlstr ) )
1309  {
1310  QgsDebugMsg( QString( "Cannot open smartgroup id: %1" ).arg( id ) );
1311  }
1312 
1313  QDomElement smartEl = doc.documentElement();
1314  QDomNodeList conditionNodes = smartEl.childNodes();
1315 
1316  for ( int i = 0; i < conditionNodes.count(); i++ )
1317  {
1318  QDomElement condEl = conditionNodes.at( i ).toElement();
1319  QString constraint = condEl.attribute( "constraint" );
1320  QString param = condEl.attribute( "param" );
1321 
1322  condition.insert( constraint, param );
1323  }
1324  }
1325 
1326  sqlite3_finalize( ppStmt );
1327 
1328  return condition;
1329 }
1330 
1332 {
1333  if ( !mCurrentDB )
1334  {
1335  QgsDebugMsg( "Cannot open database for listing groups" );
1336  return QString();
1337  }
1338 
1339  QString op;
1340 
1341  char *query = sqlite3_mprintf( "SELECT xml FROM smartgroup WHERE id=%d", id );
1342 
1343  sqlite3_stmt *ppStmt;
1344  int nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
1345  if ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
1346  {
1347  QDomDocument doc;
1348  QString xmlstr = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
1349  if ( !doc.setContent( xmlstr ) )
1350  {
1351  QgsDebugMsg( QString( "Cannot open smartgroup id: %1" ).arg( id ) );
1352  }
1353  QDomElement smartEl = doc.documentElement();
1354  op = smartEl.attribute( "operator" );
1355  }
1356 
1357  sqlite3_finalize( ppStmt );
1358 
1359  return op;
1360 }
1361 
1362 bool QgsStyleV2::exportXML( const QString& filename )
1363 {
1364  if ( filename.isEmpty() )
1365  {
1366  QgsDebugMsg( "Invalid filename for style export." );
1367  return false;
1368  }
1369 
1370  QDomDocument doc( "qgis_style" );
1371  QDomElement root = doc.createElement( "qgis_style" );
1372  root.setAttribute( "version", STYLE_CURRENT_VERSION );
1373  doc.appendChild( root );
1374 
1375  // TODO work on the groups and tags
1376  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( mSymbols, "symbols", doc );
1377  QDomElement rampsElem = doc.createElement( "colorramps" );
1378 
1379  // save color ramps
1381  {
1382  QDomElement rampEl = QgsSymbolLayerV2Utils::saveColorRamp( itr.key(), itr.value(), doc );
1383  rampsElem.appendChild( rampEl );
1384  }
1385 
1386  root.appendChild( symbolsElem );
1387  root.appendChild( rampsElem );
1388 
1389  // save
1390  QFile f( filename );
1391  if ( !f.open( QFile::WriteOnly ) )
1392  {
1393  mErrorString = "Couldn't open file for writing: " + filename;
1394  return false;
1395  }
1396 
1397  QTextStream ts( &f );
1398  ts.setCodec( "UTF-8" );
1399  doc.save( ts, 2 );
1400  f.close();
1401 
1402  mFileName = filename;
1403  return true;
1404 }
1405 
1406 bool QgsStyleV2::importXML( const QString& filename )
1407 {
1408  mErrorString = QString();
1409  QDomDocument doc( "style" );
1410  QFile f( filename );
1411  if ( !f.open( QFile::ReadOnly ) )
1412  {
1413  mErrorString = "Unable to open the specified file";
1414  QgsDebugMsg( "Error opening the style XML file." );
1415  return false;
1416  }
1417 
1418  if ( !doc.setContent( &f ) )
1419  {
1420  mErrorString = QString( "Unable to understand the style file: %1" ).arg( filename );
1421  QgsDebugMsg( "XML Parsing error" );
1422  f.close();
1423  return false;
1424  }
1425  f.close();
1426 
1427  QDomElement docEl = doc.documentElement();
1428  if ( docEl.tagName() != "qgis_style" )
1429  {
1430  mErrorString = "Incorrect root tag in style: " + docEl.tagName();
1431  return false;
1432  }
1433 
1434  QString version = docEl.attribute( "version" );
1435  if ( version != STYLE_CURRENT_VERSION && version != "0" )
1436  {
1437  mErrorString = "Unknown style file version: " + version;
1438  return false;
1439  }
1440 
1441  QgsSymbolV2Map symbols;
1442 
1443  QDomElement symbolsElement = docEl.firstChildElement( "symbols" );
1444  QDomElement e = symbolsElement.firstChildElement();
1445 
1446  if ( version == STYLE_CURRENT_VERSION )
1447  {
1448  // For the new style, load symbols individualy
1449  while ( !e.isNull() )
1450  {
1451  if ( e.tagName() == "symbol" )
1452  {
1454  if ( symbol )
1455  {
1456  symbols.insert( e.attribute( "name" ), symbol );
1457  }
1458  }
1459  else
1460  {
1461  QgsDebugMsg( "unknown tag: " + e.tagName() );
1462  }
1463  e = e.nextSiblingElement();
1464  }
1465  }
1466  else
1467  {
1468  // for the old version, use the utility function to solve @symbol@layer subsymbols
1469  symbols = QgsSymbolLayerV2Utils::loadSymbols( symbolsElement );
1470  }
1471 
1472  // save the symbols with proper name
1473  for ( QMap<QString, QgsSymbolV2*>::iterator it = symbols.begin(); it != symbols.end(); ++it )
1474  {
1475  addSymbol( it.key(), it.value() );
1476  }
1477 
1478  // load color ramps
1479  QDomElement rampsElement = docEl.firstChildElement( "colorramps" );
1480  e = rampsElement.firstChildElement();
1481  while ( !e.isNull() )
1482  {
1483  if ( e.tagName() == "colorramp" )
1484  {
1486  if ( ramp )
1487  {
1488  addColorRamp( e.attribute( "name" ), ramp );
1489  }
1490  }
1491  else
1492  {
1493  QgsDebugMsg( "unknown tag: " + e.tagName() );
1494  }
1495  e = e.nextSiblingElement();
1496  }
1497 
1498  mFileName = filename;
1499  return true;
1500 }
1501 
1503 {
1504  QDomDocument doc( "dummy" );
1505  QDomElement symEl;
1506  QByteArray xmlArray;
1507  QTextStream stream( &xmlArray );
1508  stream.setCodec( "UTF-8" );
1509 
1510  char *query;
1511 
1512  if ( type == SymbolEntity )
1513  {
1514  // check if it is an existing symbol
1515  if ( !symbolNames().contains( name ) )
1516  {
1517  QgsDebugMsg( "Update request received for unavailable symbol" );
1518  return false;
1519  }
1520 
1521  symEl = QgsSymbolLayerV2Utils::saveSymbol( name, symbol( name ), doc );
1522  if ( symEl.isNull() )
1523  {
1524  QgsDebugMsg( "Couldn't convert symbol to valid XML!" );
1525  return false;
1526  }
1527  symEl.save( stream, 4 );
1528  query = sqlite3_mprintf( "UPDATE symbol SET xml='%q' WHERE name='%q';",
1529  xmlArray.constData(), name.toUtf8().constData() );
1530  }
1531  else if ( type == ColorrampEntity )
1532  {
1533  if ( !colorRampNames().contains( name ) )
1534  {
1535  QgsDebugMsg( "Update requested for unavailable color ramp." );
1536  return false;
1537  }
1538 
1539  symEl = QgsSymbolLayerV2Utils::saveColorRamp( name, colorRamp( name ), doc );
1540  if ( symEl.isNull() )
1541  {
1542  QgsDebugMsg( "Couldn't convert color ramp to valid XML!" );
1543  return false;
1544  }
1545  symEl.save( stream, 4 );
1546  query = sqlite3_mprintf( "UPDATE colorramp SET xml='%q' WHERE name='%q';",
1547  xmlArray.constData(), name.toUtf8().constData() );
1548  }
1549  else
1550  {
1551  QgsDebugMsg( "Updating the unsupported StyleEntity" );
1552  return false;
1553  }
1554 
1555 
1556  if ( !runEmptyQuery( query ) )
1557  {
1558  QgsDebugMsg( "Couldn't insert symbol into the database!" );
1559  return false;
1560  }
1561  return true;
1562 }
QString mErrorString
Definition: qgsstylev2.h:339
QStringList tagsOfSymbol(StyleEntity type, const QString &symbol)
return the tags associated with the symbol
Definition: qgsstylev2.cpp:980
void setCodec(QTextCodec *codec)
QString smartgroupOperator(int id)
returns the operator for the smartgroup
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, const QString &tagName, QDomDocument &doc)
int getId(const QString &table, const QString &name)
gets the id from the table for the given name from the database, 0 if not found
void clear()
static QgsSymbolV2Map loadSymbols(QDomElement &element)
QString mFileName
Definition: qgsstylev2.h:340
bool saveColorRamp(const QString &name, QgsVectorColorRampV2 *ramp, int groupid, const QStringList &tags)
add the colorramp to the DB
Definition: qgsstylev2.cpp:210
void remove(StyleEntity type, int id)
remove the specified entity from the db
Definition: qgsstylev2.cpp:723
QgsVectorColorRampV2 * colorRamp(const QString &name)
return a NEW copy of color ramp
Definition: qgsstylev2.cpp:257
bool contains(const Key &key) const
static QgsVectorColorRampV2 * loadColorRamp(QDomElement &element)
bool importXML(const QString &filename)
Imports the symbols and colorramps into the default style database from the given XML file...
bool tagSymbol(StyleEntity type, const QString &symbol, const QStringList &tags)
tags the symbol with the tags in the list
Definition: qgsstylev2.cpp:870
bool group(StyleEntity type, const QString &name, int groupid)
applies the specified group to the symbol or colorramp specified by StyleEntity
Definition: qgsstylev2.cpp:775
QList< T > values() const
QDomNode appendChild(const QDomNode &newChild)
bool addSymbol(const QString &name, QgsSymbolV2 *symbol, bool update=false)
add symbol to style. takes symbol&#39;s ownership
Definition: qgsstylev2.cpp:81
QString getName(const QString &table, int id) const
gets the name from the table for the given id from the database, empty if not found ...
QString attribute(const QString &name, const QString &defValue) const
int groupId(const QString &group)
return the DB id for the given group name
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
static QDomElement saveColorRamp(const QString &name, QgsVectorColorRampV2 *ramp, QDomDocument &doc)
virtual QgsSymbolV2 * clone() const =0
const_iterator constBegin() const
QStringList symbolsOfGroup(StyleEntity type, int groupid)
returns the symbolnames of a given groupid
Definition: qgsstylev2.cpp:538
bool contains(const QString &str, Qt::CaseSensitivity cs) const
static QDomElement saveSymbol(const QString &symbolName, QgsSymbolV2 *symbol, QDomDocument &doc)
QDomElement nextSiblingElement(const QString &tagName) const
bool exportXML(const QString &filename)
Exports the style as a XML file.
bool addColorRamp(const QString &name, QgsVectorColorRampV2 *colorRamp, bool update=false)
add color ramp to style. takes ramp&#39;s ownership
Definition: qgsstylev2.cpp:186
QDomElement documentElement() const
bool detagSymbol(StyleEntity type, const QString &symbol, const QStringList &tags)
detags the symbol with the given list
Definition: qgsstylev2.cpp:922
bool updateSymbol(StyleEntity type, const QString &name)
updates the properties of an existing symbol/colorramp
QString join(const QString &separator) const
bool exists() const
void clear()
QDomNodeList childNodes() const
QStringList colorRampNames()
return a list of names of color ramps
Definition: qgsstylev2.cpp:273
char * getGroupRemoveQuery(int id)
prepares the complex query for removing a group, so that the children are not abandoned ...
Definition: qgsstylev2.cpp:705
bool copy(const QString &newName)
static QgsSymbolV2 * loadSymbol(const QDomElement &element)
Attempts to load a symbol from a DOM element.
const QgsSymbolV2 * symbolRef(const QString &name) const
return a const pointer to a symbol (doesn&#39;t create new instance)
Definition: qgsstylev2.cpp:170
const QgsVectorColorRampV2 * colorRampRef(const QString &name) const
return a const pointer to a symbol (doesn&#39;t create new instance)
Definition: qgsstylev2.cpp:263
void clear()
bool save(QString filename=QString())
save style into a file (will use current filename if empty string is passed)
Definition: qgsstylev2.cpp:357
QList< int > groupIds() const
return the ids of all the groups in the style
Definition: qgsstylev2.cpp:477
QDomElement toElement() const
QList< Key > keys() const
const char * name() const
int count() const
QString number(int n, int base)
QgsSymbolGroupMap childGroupNames(const QString &parent="")
return a map of groupid and names for the given parent group
Definition: qgsstylev2.cpp:491
bool renameColorRamp(const QString &oldName, const QString &newName)
change ramp&#39;s name
Definition: qgsstylev2.cpp:435
QString fromUtf8(const char *str, int size)
bool saveSymbol(const QString &name, QgsSymbolV2 *symbol, int groupid, const QStringList &tags)
add the symbol to the DB with the tags
Definition: qgsstylev2.cpp:105
int colorRampCount()
return count of color ramps
Definition: qgsstylev2.cpp:268
static QgsStyleV2 * mDefaultStyle
Definition: qgsstylev2.h:344
void setAttribute(const QString &name, const QString &value)
virtual QgsVectorColorRampV2 * clone() const =0
Creates a clone of the color ramp.
static QgsStyleV2 * defaultStyle()
return default application-wide style
Definition: qgsstylev2.cpp:51
bool isEmpty() const
int removeAll(const T &value)
const_iterator constEnd() const
const char * constData() const
sqlite3 * mCurrentDB
Definition: qgsstylev2.h:342
void rename(StyleEntity type, int id, const QString &newName)
rename the given entity with the specified id
Definition: qgsstylev2.cpp:677
QMap< Key, T >::iterator insert(const Key &key, const T &value)
QStringList symbolsOfSmartgroup(StyleEntity type, int id)
returns the symbols for the smartgroup
iterator end()
bool runEmptyQuery(char *query, bool freeQuery=true)
convenience function that would run queries which don&#39;t generate return values
Definition: qgsstylev2.cpp:754
int addTag(const QString &tagName)
adds a new tag and returns the tag&#39;s id
Definition: qgsstylev2.cpp:641
QStringList symbolNames()
return a list of names of symbols
Definition: qgsstylev2.cpp:180
QMap< int, QString > QgsSymbolGroupMap
Definition: qgsstylev2.h:35
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
QgsSymbolGroupMap smartgroupsListMap()
returns the smart groups map with id as key and name as value
iterator begin()
int tagId(const QString &tag)
return the DB id for the given tag name
QgsSymbolV2 * symbol(const QString &name)
return a NEW copy of symbol
Definition: qgsstylev2.cpp:164
bool contains(QChar ch, Qt::CaseSensitivity cs) const
QgsSymbolV2Map mSymbols
Definition: qgsstylev2.h:336
virtual void close()
QStringList tags() const
Returns a list of all tags in the style database.
Definition: qgsstylev2.cpp:656
bool isNull() const
static QString defaultStyleV2Path()
Returns the path to default style (works as a starting point).
static QString userStyleV2Path()
Returns the path to user&#39;s style.
QgsVectorColorRampV2Map mColorRamps
Definition: qgsstylev2.h:337
QStringList smartgroupNames()
returns the smart groups list
QStringList symbolsWithTag(StyleEntity type, int tagid)
returns the symbol names with which have the given tag
Definition: qgsstylev2.cpp:575
void clear()
remove all contents of the style
Definition: qgsstylev2.cpp:70
bool load(const QString &filename)
load a file into the style
Definition: qgsstylev2.cpp:291
void save(QTextStream &str, int indent) const
QString groupName(int groupId) const
return the group name for the given DB id
bool renameSymbol(const QString &oldName, const QString &newName)
change symbol&#39;s name
Definition: qgsstylev2.cpp:403
QDomElement firstChildElement(const QString &tagName) const
int symbolCount()
return count of symbols in style
Definition: qgsstylev2.cpp:175
QList< T > toList() const
iterator insert(const Key &key, const T &value)
bool removeColorRamp(const QString &name)
remove color ramp from style (and delete it)
Definition: qgsstylev2.cpp:239
QString tagName() const
QStringList filter(const QString &str, Qt::CaseSensitivity cs) const
QDomElement createElement(const QString &tagName)
StyleEntity
Enum for Entities involved in a style.
Definition: qgsstylev2.h:83
int smartgroupId(const QString &smartgroup)
return the DB id for the given smartgroup name
void symbolSaved(const QString &name, QgsSymbolV2 *symbol)
QObject * parent() const
Abstract base class for color ramps.
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
bool openDB(const QString &filename)
convenience function to open the DB and return a sqlite3 object
Definition: qgsstylev2.cpp:278
int count(const Key &key) const
T take(const Key &key)
QStringList findSymbols(StyleEntity type, const QString &qword)
return the names of the symbols which have a matching &#39;substring&#39; in its defintion ...
Definition: qgsstylev2.cpp:796
QStringList groupNames()
return the all the groups in the style
Definition: qgsstylev2.cpp:463
#define STYLE_CURRENT_VERSION
Definition: qgsstylev2.cpp:36
QMultiMap< QString, QString > QgsSmartConditionMap
A multimap to hold the smart group conditions as constraint and parameter pairs.
Definition: qgsstylev2.h:56
int addGroup(const QString &groupName, int parent=0)
adds a new group and returns the group&#39;s id
Definition: qgsstylev2.cpp:624
QgsSmartConditionMap smartgroup(int id)
returns the QgsSmartConditionMap for the given id
bool removeSymbol(const QString &name)
remove symbol from style (and delete it)
Definition: qgsstylev2.cpp:136
QDomNode at(int index) const
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
int addSmartgroup(const QString &name, const QString &op, const QgsSmartConditionMap &conditions)
adds new smartgroup to the database and returns the id
int symbolId(const QString &name)
return the id in the style database for the given symbol name returns 0 if not found ...
const T value(const Key &key) const
QByteArray toUtf8() const
int colorrampId(const QString &name)
return the id in the style database for the given colorramp name returns 0 if not found ...