QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
qgsstylemodel.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsstylemodel.cpp
3  ---------------
4  begin : September 2018
5  copyright : (C) 2018 by Nyall Dawson
6  email : nyall dot dawson 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 "qgsstylemodel.h"
17 #include "qgsstyle.h"
18 #include "qgssymbollayerutils.h"
19 #include "qgsapplication.h"
20 #include "qgssvgcache.h"
21 #include "qgsimagecache.h"
22 #include "qgsproject.h"
24 #include <QIcon>
25 #include <QBuffer>
26 
27 const double ICON_PADDING_FACTOR = 0.16;
28 
30 
31 QgsAbstractStyleEntityIconGenerator *QgsStyleModel::sIconGenerator = nullptr;
32 
33 //
34 // QgsAbstractStyleEntityIconGenerator
35 //
36 
38  : QObject( parent )
39 {
40 
41 }
42 
43 void QgsAbstractStyleEntityIconGenerator::setIconSizes( const QList<QSize> &sizes )
44 {
45  mIconSizes = sizes;
46 }
47 
49 {
50  return mIconSizes;
51 }
52 
53 
54 //
55 // QgsStyleModel
56 //
57 
58 QgsStyleModel::QgsStyleModel( QgsStyle *style, QObject *parent )
59  : QAbstractItemModel( parent )
60  , mStyle( style )
61 {
62  Q_ASSERT( mStyle );
63 
64  for ( QgsStyle::StyleEntity entity : ENTITIES )
65  {
66  mEntityNames.insert( entity, mStyle->allNames( entity ) );
67  }
68 
69  connect( mStyle, &QgsStyle::entityAdded, this, &QgsStyleModel::onEntityAdded );
70  connect( mStyle, &QgsStyle::entityRemoved, this, &QgsStyleModel::onEntityRemoved );
71  connect( mStyle, &QgsStyle::entityRenamed, this, &QgsStyleModel::onEntityRename );
72  connect( mStyle, &QgsStyle::entityChanged, this, &QgsStyleModel::onEntityChanged );
73  connect( mStyle, &QgsStyle::entityTagsChanged, this, &QgsStyleModel::onTagsChanged );
74 
75  // when a remote svg or image has been fetched, update the model's decorations.
76  // this is required if a symbol utilizes remote svgs, and the current icons
77  // have been generated using the temporary "downloading" svg. In this case
78  // we require the preview to be regenerated to use the correct fetched
79  // svg
80  connect( QgsApplication::svgCache(), &QgsSvgCache::remoteSvgFetched, this, &QgsStyleModel::rebuildSymbolIcons );
81  connect( QgsApplication::imageCache(), &QgsImageCache::remoteImageFetched, this, &QgsStyleModel::rebuildSymbolIcons );
82 
83  // if project color scheme changes, we need to redraw symbols - they may use project colors and accordingly
84  // need updating to reflect the new colors
85  connect( QgsProject::instance(), &QgsProject::projectColorsChanged, this, &QgsStyleModel::rebuildSymbolIcons );
86 
87  if ( sIconGenerator )
88  connect( sIconGenerator, &QgsAbstractStyleEntityIconGenerator::iconGenerated, this, &QgsStyleModel::iconGenerated, Qt::QueuedConnection );
89 }
90 
91 QVariant QgsStyleModel::data( const QModelIndex &index, int role ) const
92 {
93  if ( index.row() < 0 || index.row() >= rowCount( QModelIndex() ) )
94  return QVariant();
95 
96 
97  QgsStyle::StyleEntity entityType = entityTypeFromRow( index.row() );
98 
99  QString name;
100  switch ( entityType )
101  {
102  case QgsStyle::TagEntity:
104  break;
105 
106  default:
107  name = mEntityNames[ entityType ].value( index.row() - offsetForEntity( entityType ) );
108  break;
109  }
110 
111  switch ( role )
112  {
113  case Qt::DisplayRole:
114  case Qt::ToolTipRole:
115  case Qt::EditRole:
116  {
117  switch ( index.column() )
118  {
119  case Name:
120  {
121  const QStringList tags = mStyle->tagsOfSymbol( entityType, name );
122 
123  if ( role == Qt::ToolTipRole )
124  {
125  QString tooltip = QStringLiteral( "<h3>%1</h3><p><i>%2</i>" ).arg( name,
126  tags.count() > 0 ? tags.join( QLatin1String( ", " ) ) : tr( "Not tagged" ) );
127 
128  switch ( entityType )
129  {
131  {
132  // create very large preview image
133  std::unique_ptr< QgsSymbol > symbol( mStyle->symbol( name ) );
134  if ( symbol )
135  {
136 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
137  int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).width( 'X' ) * 23 );
138 #else
139  int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 );
140 #endif
141  int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
142  QPixmap pm = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( width, height ), height / 20, nullptr, false, mExpressionContext.get() );
143  QByteArray data;
144  QBuffer buffer( &data );
145  pm.save( &buffer, "PNG", 100 );
146  tooltip += QStringLiteral( "<p><img src='data:image/png;base64, %3'>" ).arg( QString( data.toBase64() ) );
147  }
148  break;
149  }
150 
152  {
153 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
154  int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).width( 'X' ) * 23 );
155 #else
156  int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 );
157 #endif
158  int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
159  const QgsTextFormat format = mStyle->textFormat( name );
160  QPixmap pm = QgsTextFormat::textFormatPreviewPixmap( format, QSize( width, height ), QString(), height / 20 );
161  QByteArray data;
162  QBuffer buffer( &data );
163  pm.save( &buffer, "PNG", 100 );
164  tooltip += QStringLiteral( "<p><img src='data:image/png;base64, %3'>" ).arg( QString( data.toBase64() ) );
165  break;
166  }
167 
169  {
170 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
171  int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).width( 'X' ) * 23 );
172 #else
173  int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 );
174 #endif
175  int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
176  const QgsPalLayerSettings settings = mStyle->labelSettings( name );
177  QPixmap pm = QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, QSize( width, height ), QString(), height / 20 );
178  QByteArray data;
179  QBuffer buffer( &data );
180  pm.save( &buffer, "PNG", 100 );
181  tooltip += QStringLiteral( "<p><img src='data:image/png;base64, %3'>" ).arg( QString( data.toBase64() ) );
182  break;
183  }
184 
186  {
187 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
188  int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).width( 'X' ) * 23 );
189 #else
190  int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 );
191 #endif
192  int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
193 
194  const QgsLegendPatchShape shape = mStyle->legendPatchShape( name );
195  if ( const QgsSymbol *symbol = mStyle->previewSymbolForPatchShape( shape ) )
196  {
197  QPixmap pm = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, QSize( width, height ), height / 20, nullptr, false, nullptr, &shape );
198  QByteArray data;
199  QBuffer buffer( &data );
200  pm.save( &buffer, "PNG", 100 );
201  tooltip += QStringLiteral( "<p><img src='data:image/png;base64, %3'>" ).arg( QString( data.toBase64() ) );
202  }
203  break;
204  }
205 
207  case QgsStyle::TagEntity:
210  break;
211  }
212  return tooltip;
213  }
214  else
215  {
216  return name;
217  }
218  }
219  case Tags:
220  return mStyle->tagsOfSymbol( entityType, name ).join( QLatin1String( ", " ) );
221  }
222  return QVariant();
223  }
224 
225  case Qt::DecorationRole:
226  {
227  // Generate icons at all additional sizes specified for the model.
228  // This allows the model to have size responsive icons.
229 
230  if ( !mExpressionContext )
231  {
232  // build the expression context once, and keep it around. Usually this is a no-no, but in this
233  // case we want to avoid creating potentially thousands of contexts one-by-one (usually one context
234  // is created for a batch of multiple evalutions like this), and we only use a very minimal context
235  // anyway...
236  mExpressionContext = qgis::make_unique< QgsExpressionContext >();
237  mExpressionContext->appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) );
238  }
239 
240  switch ( index.column() )
241  {
242  case Name:
243  switch ( entityType )
244  {
246  {
247  // use cached icon if possible
248  QIcon icon = mIconCache[ entityType ].value( name );
249  if ( !icon.isNull() )
250  return icon;
251 
252  std::unique_ptr< QgsSymbol > symbol( mStyle->symbol( name ) );
253  if ( symbol )
254  {
255  if ( mAdditionalSizes.isEmpty() )
256  icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( 24, 24 ), 1, nullptr, false, mExpressionContext.get() ) );
257 
258  for ( const QSize &s : mAdditionalSizes )
259  {
260  icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ), nullptr, false, mExpressionContext.get() ) );
261  }
262 
263  }
264  mIconCache[ entityType ].insert( name, icon );
265  return icon;
266  }
268  {
269  // use cached icon if possible
270  QIcon icon = mIconCache[ entityType ].value( name );
271  if ( !icon.isNull() )
272  return icon;
273 
274  std::unique_ptr< QgsColorRamp > ramp( mStyle->colorRamp( name ) );
275  if ( ramp )
276  {
277  if ( mAdditionalSizes.isEmpty() )
278  icon.addPixmap( QgsSymbolLayerUtils::colorRampPreviewPixmap( ramp.get(), QSize( 24, 24 ), 1 ) );
279  for ( const QSize &s : mAdditionalSizes )
280  {
281  icon.addPixmap( QgsSymbolLayerUtils::colorRampPreviewPixmap( ramp.get(), s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ) ) );
282  }
283 
284  }
285  mIconCache[ entityType ].insert( name, icon );
286  return icon;
287  }
288 
290  {
291  // use cached icon if possible
292  QIcon icon = mIconCache[ entityType ].value( name );
293  if ( !icon.isNull() )
294  return icon;
295 
296  const QgsTextFormat format( mStyle->textFormat( name ) );
297  if ( mAdditionalSizes.isEmpty() )
298  icon.addPixmap( QgsTextFormat::textFormatPreviewPixmap( format, QSize( 24, 24 ), QString(), 1 ) );
299  for ( const QSize &s : mAdditionalSizes )
300  {
301  icon.addPixmap( QgsTextFormat::textFormatPreviewPixmap( format, s, QString(), static_cast< int >( s.width() * ICON_PADDING_FACTOR ) ) );
302  }
303  mIconCache[ entityType ].insert( name, icon );
304  return icon;
305  }
306 
308  {
309  // use cached icon if possible
310  QIcon icon = mIconCache[ entityType ].value( name );
311  if ( !icon.isNull() )
312  return icon;
313 
314  const QgsPalLayerSettings settings( mStyle->labelSettings( name ) );
315  if ( mAdditionalSizes.isEmpty() )
316  icon.addPixmap( QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, QSize( 24, 24 ), QString(), 1 ) );
317  for ( const QSize &s : mAdditionalSizes )
318  {
319  icon.addPixmap( QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, s, QString(), static_cast< int >( s.width() * ICON_PADDING_FACTOR ) ) );
320  }
321  mIconCache[ entityType ].insert( name, icon );
322  return icon;
323  }
324 
326  {
327  // use cached icon if possible
328  QIcon icon = mIconCache[ entityType ].value( name );
329  if ( !icon.isNull() )
330  return icon;
331 
332  const QgsLegendPatchShape shape = mStyle->legendPatchShape( name );
333  if ( !shape.isNull() )
334  {
335  if ( const QgsSymbol *symbol = mStyle->previewSymbolForPatchShape( shape ) )
336  {
337  if ( mAdditionalSizes.isEmpty() )
338  icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, QSize( 24, 24 ), 1, nullptr, false, mExpressionContext.get(), &shape ) );
339 
340  for ( const QSize &s : mAdditionalSizes )
341  {
342  icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ), nullptr, false, mExpressionContext.get(), &shape ) );
343  }
344  }
345  }
346  mIconCache[ entityType ].insert( name, icon );
347  return icon;
348  }
349 
351  {
352  // hack for now -- we just use a generic "3d icon" svg file.
353  // TODO - render proper thumbnails
354 
355  // use cached icon if possible
356  QIcon icon = mIconCache[ entityType ].value( name );
357  if ( !icon.isNull() )
358  return icon;
359 
360  if ( sIconGenerator && !mPending3dSymbolIcons.contains( name ) )
361  {
362  mPending3dSymbolIcons.insert( name );
363  sIconGenerator->generateIcon( mStyle, QgsStyle::Symbol3DEntity, name );
364  }
365 
366  // TODO - use hourglass icon
367  if ( mAdditionalSizes.isEmpty() )
368  icon.addFile( QgsApplication::defaultThemePath() + QDir::separator() + QStringLiteral( "3d.svg" ), QSize( 24, 24 ) );
369  for ( const QSize &s : mAdditionalSizes )
370  {
371  icon.addFile( QgsApplication::defaultThemePath() + QDir::separator() + QStringLiteral( "3d.svg" ), s );
372  }
373  mIconCache[ entityType ].insert( name, icon );
374  return icon;
375  }
376 
377  case QgsStyle::TagEntity:
379  return QVariant();
380  }
381  break;
382 
383  case Tags:
384  return QVariant();
385  }
386  return QVariant();
387  }
388 
389  case TypeRole:
390  return entityType;
391 
392  case TagRole:
393  return mStyle->tagsOfSymbol( entityType, name );
394 
395  case IsFavoriteRole:
396  return mStyle->isFavorite( entityType, name );
397 
398  case SymbolTypeRole:
399  {
400  switch ( entityType )
401  {
403  {
404  const QgsSymbol *symbol = mStyle->symbolRef( name );
405  return symbol ? symbol->type() : QVariant();
406  }
407 
409  return mStyle->legendPatchShapeSymbolType( name );
410 
411  case QgsStyle::TagEntity:
417  return QVariant();
418  }
419  return QVariant();
420  }
421 
422  case LayerTypeRole:
423  {
424  switch ( entityType )
425  {
427  return mStyle->labelSettingsLayerType( name );
428 
432  case QgsStyle::TagEntity:
436  return QVariant();
437  }
438  return QVariant();
439  }
440 
442  {
443  switch ( entityType )
444  {
446  {
447  QVariantList res;
448  const QList< QgsWkbTypes::GeometryType > types = mStyle->symbol3DCompatibleGeometryTypes( name );
449  res.reserve( types.size() );
450  for ( QgsWkbTypes::GeometryType type : types )
451  {
452  res << static_cast< int >( type );
453  }
454  return res;
455  }
456 
460  case QgsStyle::TagEntity:
464  return QVariant();
465  }
466  return QVariant();
467  }
468 
469  default:
470  return QVariant();
471  }
472 #ifndef _MSC_VER // avoid warning
473  return QVariant(); // avoid warning
474 #endif
475 }
476 
477 bool QgsStyleModel::setData( const QModelIndex &index, const QVariant &value, int role )
478 {
479  if ( index.row() < 0 || index.row() >= rowCount( QModelIndex() ) || role != Qt::EditRole )
480  return false;
481 
482  switch ( index.column() )
483  {
484  case Name:
485  {
486  QgsStyle::StyleEntity entityType = entityTypeFromRow( index.row() );
487  QString name;
488  switch ( entityType )
489  {
490  case QgsStyle::TagEntity:
492  return false;
493 
494  default:
495  name = mEntityNames[ entityType ].value( index.row() - offsetForEntity( entityType ) );
496  break;
497  }
498 
499  const QString newName = value.toString();
500  return mStyle->renameEntity( entityType, name, newName );
501  }
502 
503  case Tags:
504  return false;
505  }
506 
507  return false;
508 }
509 
510 Qt::ItemFlags QgsStyleModel::flags( const QModelIndex &index ) const
511 {
512  Qt::ItemFlags flags = QAbstractItemModel::flags( index );
513  if ( index.isValid() && index.column() == Name )
514  {
515  return flags | Qt::ItemIsEditable;
516  }
517  else
518  {
519  return flags;
520  }
521 }
522 
523 QVariant QgsStyleModel::headerData( int section, Qt::Orientation orientation, int role ) const
524 {
525  if ( role == Qt::DisplayRole )
526  {
527  if ( orientation == Qt::Vertical ) //row
528  {
529  return QVariant( section );
530  }
531  else
532  {
533  switch ( section )
534  {
535  case Name:
536  return QVariant( tr( "Name" ) );
537 
538  case Tags:
539  return QVariant( tr( "Tags" ) );
540 
541  default:
542  return QVariant();
543  }
544  }
545  }
546  else
547  {
548  return QVariant();
549  }
550 }
551 
552 QModelIndex QgsStyleModel::index( int row, int column, const QModelIndex &parent ) const
553 {
554  if ( !hasIndex( row, column, parent ) )
555  return QModelIndex();
556 
557  if ( !parent.isValid() )
558  {
559  return createIndex( row, column );
560  }
561 
562  return QModelIndex();
563 }
564 
565 QModelIndex QgsStyleModel::parent( const QModelIndex & ) const
566 {
567  //all items are top level for now
568  return QModelIndex();
569 }
570 
571 int QgsStyleModel::rowCount( const QModelIndex &parent ) const
572 {
573  if ( !parent.isValid() )
574  {
575  int count = 0;
576  for ( QgsStyle::StyleEntity type : ENTITIES )
577  count += mEntityNames[ type ].size();
578  return count;
579  }
580  return 0;
581 }
582 
583 int QgsStyleModel::columnCount( const QModelIndex & ) const
584 {
585  return 2;
586 }
587 
589 {
590  if ( mAdditionalSizes.contains( size ) )
591  return;
592 
593  mAdditionalSizes << size;
594 
595  if ( sIconGenerator )
596  sIconGenerator->setIconSizes( mAdditionalSizes );
597 
598  mIconCache.clear();
599 }
600 
602 {
603  sIconGenerator = generator;
604  connect( sIconGenerator, &QgsAbstractStyleEntityIconGenerator::iconGenerated, QgsApplication::defaultStyleModel(), &QgsStyleModel::iconGenerated, Qt::QueuedConnection );
605 }
606 
607 void QgsStyleModel::onEntityAdded( QgsStyle::StyleEntity type, const QString &name )
608 {
609  mIconCache[ type ].remove( name );
610  const QStringList oldSymbolNames = mEntityNames[ type ];
611  const QStringList newSymbolNames = mStyle->allNames( type );
612 
613  // find index of newly added symbol
614  const int newNameIndex = newSymbolNames.indexOf( name );
615  if ( newNameIndex < 0 )
616  return; // shouldn't happen
617 
618  const int offset = offsetForEntity( type );
619  beginInsertRows( QModelIndex(), newNameIndex + offset, newNameIndex + offset );
620  mEntityNames[ type ] = newSymbolNames;
621  endInsertRows();
622 }
623 
624 void QgsStyleModel::onEntityRemoved( QgsStyle::StyleEntity type, const QString &name )
625 {
626  mIconCache[ type ].remove( name );
627  const QStringList oldSymbolNames = mEntityNames[ type ];
628  const QStringList newSymbolNames = mStyle->allNames( type );
629 
630  // find index of removed symbol
631  const int oldNameIndex = oldSymbolNames.indexOf( name );
632  if ( oldNameIndex < 0 )
633  return; // shouldn't happen
634 
635  const int offset = offsetForEntity( type );
636  beginRemoveRows( QModelIndex(), oldNameIndex + offset, oldNameIndex + offset );
637  mEntityNames[ type ] = newSymbolNames;
638  endRemoveRows();
639 }
640 
641 void QgsStyleModel::onEntityChanged( QgsStyle::StyleEntity type, const QString &name )
642 {
643  mIconCache[ type ].remove( name );
644 
645  const int offset = offsetForEntity( type );
646  QModelIndex i = index( offset + mEntityNames[ type ].indexOf( name ), Tags );
647  emit dataChanged( i, i, QVector< int >() << Qt::DecorationRole );
648 }
649 
650 void QgsStyleModel::onEntityRename( QgsStyle::StyleEntity type, const QString &oldName, const QString &newName )
651 {
652  mIconCache[ type ].remove( oldName );
653  const QStringList oldSymbolNames = mEntityNames[ type ];
654  const QStringList newSymbolNames = mStyle->allNames( type );
655 
656  // find index of removed symbol
657  const int oldNameIndex = oldSymbolNames.indexOf( oldName );
658  if ( oldNameIndex < 0 )
659  return; // shouldn't happen
660 
661  // find index of added symbol
662  const int newNameIndex = newSymbolNames.indexOf( newName );
663  if ( newNameIndex < 0 )
664  return; // shouldn't happen
665 
666  if ( newNameIndex == oldNameIndex )
667  {
668  mEntityNames[ type ] = newSymbolNames;
669  return;
670  }
671 
672  const int offset = offsetForEntity( type );
673  beginMoveRows( QModelIndex(), oldNameIndex + offset, oldNameIndex + offset, QModelIndex(), ( newNameIndex > oldNameIndex ? newNameIndex + 1 : newNameIndex ) + offset );
674  mEntityNames[ type ] = newSymbolNames;
675  endMoveRows();
676 }
677 
678 void QgsStyleModel::onTagsChanged( int entity, const QString &name, const QStringList & )
679 {
680  QgsStyle::StyleEntity type = static_cast< QgsStyle::StyleEntity >( entity );
681  QModelIndex i;
682  int row = mEntityNames[type].indexOf( name ) + offsetForEntity( type );
683  switch ( static_cast< QgsStyle::StyleEntity >( entity ) )
684  {
685  case QgsStyle::TagEntity:
687  return;
688 
689  default:
690  i = index( row, Tags );
691  }
692  emit dataChanged( i, i );
693 }
694 
695 void QgsStyleModel::rebuildSymbolIcons()
696 {
697  mIconCache[ QgsStyle::SymbolEntity ].clear();
698  mExpressionContext.reset();
699  emit dataChanged( index( 0, 0 ), index( mEntityNames[ QgsStyle::SymbolEntity ].count() - 1, 0 ), QVector<int>() << Qt::DecorationRole );
700 }
701 
702 void QgsStyleModel::iconGenerated( QgsStyle::StyleEntity type, const QString &name, const QIcon &icon )
703 {
704  int row = mEntityNames[type].indexOf( name ) + offsetForEntity( type );
705 
706  switch ( type )
707  {
709  mPending3dSymbolIcons.remove( name );
710  mIconCache[ QgsStyle::Symbol3DEntity ].insert( name, icon );
711  emit dataChanged( index( row, 0 ), index( row, 0 ) );
712  break;
713 
715  case QgsStyle::TagEntity:
721  break;
722  }
723 }
724 
725 QgsStyle::StyleEntity QgsStyleModel::entityTypeFromRow( int row ) const
726 {
727  int maxRowForEntity = 0;
728  for ( QgsStyle::StyleEntity type : ENTITIES )
729  {
730  maxRowForEntity += mEntityNames[ type ].size();
731  if ( row < maxRowForEntity )
732  return type;
733  }
734 
735  // should never happen
736  Q_ASSERT( false );
737  return QgsStyle::SymbolEntity;
738 }
739 
740 int QgsStyleModel::offsetForEntity( QgsStyle::StyleEntity entity ) const
741 {
742  int offset = 0;
743  for ( QgsStyle::StyleEntity type : ENTITIES )
744  {
745  if ( type == entity )
746  return offset;
747 
748  offset += mEntityNames[ type ].size();
749  }
750  return 0;
751 }
752 
753 //
754 // QgsStyleProxyModel
755 //
756 
758  : QSortFilterProxyModel( parent )
759  , mStyle( style )
760 {
761  mModel = new QgsStyleModel( mStyle, this );
762  initialize();
763 }
764 
765 void QgsStyleProxyModel::initialize()
766 {
767  setSortCaseSensitivity( Qt::CaseInsensitive );
768 // setSortLocaleAware( true );
769  setSourceModel( mModel );
770  setDynamicSortFilter( true );
771  sort( 0 );
772 
773  connect( mStyle, &QgsStyle::entityTagsChanged, this, [ = ]
774  {
775  // update tagged symbols if filtering by tag
776  if ( mTagId >= 0 )
777  setTagId( mTagId );
778  if ( mSmartGroupId >= 0 )
779  setSmartGroupId( mSmartGroupId );
780  } );
781 
782  connect( mStyle, &QgsStyle::favoritedChanged, this, [ = ]
783  {
784  // update favorited symbols if filtering by favorite
785  if ( mFavoritesOnly )
786  setFavoritesOnly( mFavoritesOnly );
787  } );
788 
789  connect( mStyle, &QgsStyle::entityRenamed, this, [ = ]( QgsStyle::StyleEntity entity, const QString &, const QString & )
790  {
791  switch ( entity )
792  {
794  case QgsStyle::TagEntity:
795  return;
796 
797  default:
798  break;
799  }
800 
801  if ( mSmartGroupId >= 0 )
802  setSmartGroupId( mSmartGroupId );
803  } );
804 }
805 
807  : QSortFilterProxyModel( parent )
808  , mModel( model )
809  , mStyle( model->style() )
810 {
811  initialize();
812 }
813 
814 bool QgsStyleProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
815 {
816  if ( mFilterString.isEmpty() && !mEntityFilterEnabled && !mSymbolTypeFilterEnabled && mTagId < 0 && mSmartGroupId < 0 && !mFavoritesOnly )
817  return true;
818 
819  QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
820  const QString name = sourceModel()->data( index ).toString();
821  const QStringList tags = sourceModel()->data( index, QgsStyleModel::TagRole ).toStringList();
822 
823  QgsStyle::StyleEntity styleEntityType = static_cast< QgsStyle::StyleEntity >( sourceModel()->data( index, QgsStyleModel::TypeRole ).toInt() );
824  if ( mEntityFilterEnabled && ( mEntityFilters.empty() || !mEntityFilters.contains( styleEntityType ) ) )
825  return false;
826 
827  QgsSymbol::SymbolType symbolType = static_cast< QgsSymbol::SymbolType >( sourceModel()->data( index, QgsStyleModel::SymbolTypeRole ).toInt() );
828  if ( mSymbolTypeFilterEnabled && symbolType != mSymbolType )
829  return false;
830 
831  if ( mLayerType != QgsWkbTypes::UnknownGeometry )
832  {
833  switch ( styleEntityType )
834  {
837  case QgsStyle::TagEntity:
841  break;
842 
844  {
845  if ( mLayerType != static_cast< QgsWkbTypes::GeometryType >( sourceModel()->data( index, QgsStyleModel::LayerTypeRole ).toInt() ) )
846  return false;
847  break;
848  }
849 
851  {
852  const QVariantList types = sourceModel()->data( index, QgsStyleModel::CompatibleGeometryTypesRole ).toList();
853  if ( !types.empty() && !types.contains( mLayerType ) )
854  return false;
855  break;
856  }
857  }
858  }
859 
860  if ( mTagId >= 0 && !mTaggedSymbolNames.contains( name ) )
861  return false;
862 
863  if ( mSmartGroupId >= 0 && !mSmartGroupSymbolNames.contains( name ) )
864  return false;
865 
866  if ( mFavoritesOnly && !sourceModel()->data( index, QgsStyleModel::IsFavoriteRole ).toBool() )
867  return false;
868 
869  if ( !mFilterString.isEmpty() )
870  {
871  // filter by word, in both filter string and style entity name/tags
872  // this allows matching of a filter string "hash line" to the symbol "hashed red lines"
873  const QStringList partsToMatch = mFilterString.trimmed().split( ' ' );
874 
875  QStringList partsToSearch = name.split( ' ' );
876  for ( const QString &tag : tags )
877  {
878  partsToSearch.append( tag.split( ' ' ) );
879  }
880 
881  for ( const QString &part : partsToMatch )
882  {
883  bool found = false;
884  for ( const QString &partToSearch : qgis::as_const( partsToSearch ) )
885  {
886  if ( partToSearch.contains( part, Qt::CaseInsensitive ) )
887  {
888  found = true;
889  break;
890  }
891  }
892  if ( !found )
893  return false; // couldn't find a match for this word, so hide entity
894  }
895  }
896 
897  return true;
898 }
899 
900 void QgsStyleProxyModel::setFilterString( const QString &filter )
901 {
902  mFilterString = filter;
903  invalidateFilter();
904 }
905 
906 
908 {
909  return mFavoritesOnly;
910 }
911 
912 void QgsStyleProxyModel::setFavoritesOnly( bool favoritesOnly )
913 {
914  mFavoritesOnly = favoritesOnly;
915  invalidateFilter();
916 }
917 
919 {
920  mModel->addDesiredIconSize( size );
921 }
922 
924 {
925  return mSymbolTypeFilterEnabled;
926 }
927 
928 void QgsStyleProxyModel::setSymbolTypeFilterEnabled( bool symbolTypeFilterEnabled )
929 {
930  mSymbolTypeFilterEnabled = symbolTypeFilterEnabled;
931  invalidateFilter();
932 }
933 
935 {
936  return mLayerType;
937 }
938 
940 {
941  mLayerType = type;
942  invalidateFilter();
943 }
944 
946 {
947  mTagId = id;
948 
949  mTaggedSymbolNames.clear();
950  if ( mTagId >= 0 )
951  {
952  for ( QgsStyle::StyleEntity entity : ENTITIES )
953  mTaggedSymbolNames.append( mStyle->symbolsWithTag( entity, mTagId ) );
954  }
955 
956  invalidateFilter();
957 }
958 
960 {
961  return mTagId;
962 }
963 
965 {
966  mSmartGroupId = id;
967 
968  mSmartGroupSymbolNames.clear();
969  if ( mSmartGroupId >= 0 )
970  {
971  for ( QgsStyle::StyleEntity entity : ENTITIES )
972  mSmartGroupSymbolNames.append( mStyle->symbolsOfSmartgroup( entity, mSmartGroupId ) );
973  }
974  invalidateFilter();
975 }
976 
978 {
979  return mSmartGroupId;
980 }
981 
983 {
984  return mSymbolType;
985 }
986 
988 {
989  mSymbolType = symbolType;
990  invalidateFilter();
991 }
992 
994 {
995  return mEntityFilterEnabled;
996 }
997 
998 void QgsStyleProxyModel::setEntityFilterEnabled( bool entityFilterEnabled )
999 {
1000  mEntityFilterEnabled = entityFilterEnabled;
1001  invalidateFilter();
1002 }
1003 
1005 {
1006  return mEntityFilters.empty() ? QgsStyle::SymbolEntity : mEntityFilters.at( 0 );
1007 }
1008 
1010 {
1011  mEntityFilters = QList< QgsStyle::StyleEntity >() << entityFilter;
1012  invalidateFilter();
1013 }
1014 
1015 void QgsStyleProxyModel::setEntityFilters( const QList<QgsStyle::StyleEntity> &filters )
1016 {
1017  mEntityFilters = filters;
1018  invalidateFilter();
1019 }
1020 
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:183
An abstract base class for icon generators for a QgsStyleModel.
Definition: qgsstylemodel.h:45
void setIconSizes(const QList< QSize > &sizes)
Sets the list of icon sizes to generate.
virtual void generateIcon(QgsStyle *style, QgsStyle::StyleEntity type, const QString &name)=0
Triggers generation of an icon for an entity from the specified style database, with matching entity ...
QgsAbstractStyleEntityIconGenerator(QObject *parent)
Constructor for QgsAbstractStyleEntityIconGenerator, with the specified parent object.
QList< QSize > iconSizes() const
Returns the list of icon sizes to generate.
void iconGenerated(QgsStyle::StyleEntity type, const QString &name, const QIcon &icon)
Emitted when the icon for the style entity with matching type and name has been generated.
static QString defaultThemePath()
Returns the path to the default theme directory.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QgsStyleModel * defaultStyleModel()
Returns a shared QgsStyleModel containing the default style library (see QgsStyle::defaultStyle()).
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
void remoteImageFetched(const QString &url)
Emitted when the cache has finished retrieving an image file from a remote url.
Represents a patch shape for use in map legends.
bool isNull() const
Returns true if the patch shape is a null QgsLegendPatchShape, which indicates that the default legen...
Contains settings for how a map layer will be labeled.
static QPixmap labelSettingsPreviewPixmap(const QgsPalLayerSettings &settings, QSize size, const QString &previewText=QString(), int padding=0)
Returns a pixmap preview for label settings.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:501
void projectColorsChanged()
Emitted whenever the project's color scheme has been changed.
A QAbstractItemModel subclass for showing symbol and color ramp entities contained within a QgsStyle ...
Qt::ItemFlags flags(const QModelIndex &index) const override
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
int columnCount(const QModelIndex &parent=QModelIndex()) const override
QVariant data(const QModelIndex &index, int role) const override
static void setIconGenerator(QgsAbstractStyleEntityIconGenerator *generator)
Sets the icon generator to use for deferred style entity icon generation.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
QModelIndex parent(const QModelIndex &index) const override
@ LayerTypeRole
Layer type (for label settings entities)
@ TypeRole
Style entity type, see QgsStyle::StyleEntity.
@ CompatibleGeometryTypesRole
Compatible layer geometry types (for 3D symbols)
@ IsFavoriteRole
Whether entity is flagged as a favorite.
@ SymbolTypeRole
Symbol type (for symbol or legend patch shape entities)
@ TagRole
String list of tags.
QgsStyleModel(QgsStyle *style, QObject *parent=nullptr)
Constructor for QgsStyleModel, for the specified style and parent object.
@ Name
Name column.
@ Tags
Tags column.
void addDesiredIconSize(QSize size)
Adds an additional icon size to generate for Qt::DecorationRole data.
QgsWkbTypes::GeometryType layerType() const
Returns the layer type filter, or QgsWkbTypes::UnknownGeometry if no layer type filter is present.
void setEntityFilter(QgsStyle::StyleEntity filter)
Sets the style entity type filter.
bool favoritesOnly() const
Returns true if the model is showing only favorited entities.
void setSymbolTypeFilterEnabled(bool enabled)
Sets whether filtering by symbol type is enabled.
void setTagId(int id)
Sets a tag id to filter style entities by.
void setSymbolType(QgsSymbol::SymbolType type)
Sets the symbol type filter.
QgsStyle::StyleEntity entityFilter() const
Returns the style entity type filter.
QgsStyleProxyModel(QgsStyle *style, QObject *parent=nullptr)
Constructor for QgsStyleProxyModel, for the specified style and parent object.
int tagId() const
Returns the tag id used to filter style entities by.
void setEntityFilters(const QList< QgsStyle::StyleEntity > &filters)
Sets the style entity type filters.
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
void setFavoritesOnly(bool favoritesOnly)
Sets whether the model should show only favorited entities.
void setLayerType(QgsWkbTypes::GeometryType type)
Sets the layer type filter.
bool entityFilterEnabled() const
Returns true if filtering by entity type is enabled.
void addDesiredIconSize(QSize size)
Adds an additional icon size to generate for Qt::DecorationRole data.
void setEntityFilterEnabled(bool enabled)
Sets whether filtering by entity type is enabled.
void setSmartGroupId(int id)
Sets a smart group id to filter style entities by.
QgsSymbol::SymbolType symbolType() const
Returns the symbol type filter.
bool symbolTypeFilterEnabled() const
Returns true if filtering by symbol type is enabled.
int smartGroupId() const
Returns the smart group id used to filter style entities by.
void setFilterString(const QString &filter)
Sets a filter string, such that only symbol entities with names matching the specified string will be...
QgsSymbol::SymbolType legendPatchShapeSymbolType(const QString &name) const
Returns the symbol type corresponding to the legend patch shape with the specified name,...
Definition: qgsstyle.cpp:2137
QgsTextFormat textFormat(const QString &name) const
Returns the text format with the specified name.
Definition: qgsstyle.cpp:2102
QStringList allNames(StyleEntity type) const
Returns a list of the names of all existing entities of the specified type.
Definition: qgsstyle.cpp:2220
void entityChanged(QgsStyle::StyleEntity entity, const QString &name)
Emitted whenever an entity's definition is changed.
QStringList symbolsWithTag(StyleEntity type, int tagid) const
Returns the symbol names with which have the given tag.
Definition: qgsstyle.cpp:1317
QList< QgsWkbTypes::GeometryType > symbol3DCompatibleGeometryTypes(const QString &name) const
Returns the list of the vector layer geometry types which are compatible with the 3D symbol with the ...
Definition: qgsstyle.cpp:2155
QgsSymbol * symbol(const QString &name)
Returns a NEW copy of symbol.
Definition: qgsstyle.cpp:270
QStringList symbolsOfSmartgroup(StyleEntity type, int id)
Returns the symbols for the smartgroup.
Definition: qgsstyle.cpp:2352
StyleEntity
Enum for Entities involved in a style.
Definition: qgsstyle.h:179
@ LabelSettingsEntity
Label settings.
Definition: qgsstyle.h:185
@ TextFormatEntity
Text formats.
Definition: qgsstyle.h:184
@ SmartgroupEntity
Smart groups.
Definition: qgsstyle.h:183
@ Symbol3DEntity
3D symbol entity (since QGIS 3.14)
Definition: qgsstyle.h:187
@ SymbolEntity
Symbols.
Definition: qgsstyle.h:180
@ TagEntity
Tags.
Definition: qgsstyle.h:181
@ ColorrampEntity
Color ramps.
Definition: qgsstyle.h:182
@ LegendPatchShapeEntity
Legend patch shape (since QGIS 3.14)
Definition: qgsstyle.h:186
QStringList tagsOfSymbol(StyleEntity type, const QString &symbol)
Returns the tags associated with the symbol.
Definition: qgsstyle.cpp:1893
void entityRenamed(QgsStyle::StyleEntity entity, const QString &oldName, const QString &newName)
Emitted whenever a entity of the specified type has been renamed from oldName to newName.
const QgsSymbol * symbolRef(const QString &name) const
Returns a const pointer to a symbol (doesn't create new instance)
Definition: qgsstyle.cpp:276
QgsWkbTypes::GeometryType labelSettingsLayerType(const QString &name) const
Returns the layer geometry type corresponding to the label settings with the specified name,...
Definition: qgsstyle.cpp:2163
QgsColorRamp * colorRamp(const QString &name) const
Returns a new copy of the specified color ramp.
Definition: qgsstyle.cpp:439
bool renameEntity(StyleEntity type, const QString &oldName, const QString &newName)
Renames an entity of the specified type from oldName to newName.
Definition: qgsstyle.cpp:241
bool isFavorite(StyleEntity type, const QString &name)
Returns true if the symbol with matching type and name is marked as a favorite.
Definition: qgsstyle.cpp:1944
void entityTagsChanged(QgsStyle::StyleEntity entity, const QString &name, const QStringList &newTags)
Emitted whenever an entity's tags are changed.
void favoritedChanged(QgsStyle::StyleEntity entity, const QString &name, bool isFavorite)
Emitted whenever an entity is either favorited or un-favorited.
QgsPalLayerSettings labelSettings(const QString &name) const
Returns the label settings with the specified name.
Definition: qgsstyle.cpp:2122
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 ...
const QgsSymbol * previewSymbolForPatchShape(const QgsLegendPatchShape &shape) const
Returns a symbol to use for rendering preview icons for a patch shape.
Definition: qgsstyle.cpp:2191
QgsLegendPatchShape legendPatchShape(const QString &name) const
Returns the legend patch shape with the specified name.
Definition: qgsstyle.cpp:2127
void entityAdded(QgsStyle::StyleEntity entity, const QString &name)
Emitted every time a new entity has been added to the database.
void remoteSvgFetched(const QString &url)
Emitted when the cache has finished retrieving an SVG file from a remote url.
static QPixmap colorRampPreviewPixmap(QgsColorRamp *ramp, QSize size, int padding=0, Qt::Orientation direction=Qt::Horizontal, bool flipDirection=false, bool drawTransparentBackground=true)
Returns a pixmap preview for a color ramp.
static QPixmap symbolPreviewPixmap(const QgsSymbol *symbol, QSize size, int padding=0, QgsRenderContext *customContext=nullptr, bool selected=false, const QgsExpressionContext *expressionContext=nullptr, const QgsLegendPatchShape *shape=nullptr)
Returns a pixmap preview for a color ramp.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:65
SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:138
SymbolType
Type of the symbol.
Definition: qgssymbol.h:87
Container for all settings relating to text rendering.
Definition: qgstextformat.h:41
static QPixmap textFormatPreviewPixmap(const QgsTextFormat &format, QSize size, const QString &previewText=QString(), int padding=0)
Returns a pixmap preview for a text format.
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:141
const double ICON_PADDING_FACTOR
const auto ENTITIES