QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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  int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 );
137  int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
138  QPixmap pm = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( width, height ), height / 20, nullptr, false, mExpressionContext.get() );
139  QByteArray data;
140  QBuffer buffer( &data );
141  pm.save( &buffer, "PNG", 100 );
142  tooltip += QStringLiteral( "<p><img src='data:image/png;base64, %3'>" ).arg( QString( data.toBase64() ) );
143  }
144  break;
145  }
146 
148  {
149  int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 );
150  int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
151  const QgsTextFormat format = mStyle->textFormat( name );
152  QPixmap pm = QgsTextFormat::textFormatPreviewPixmap( format, QSize( width, height ), QString(), height / 20 );
153  QByteArray data;
154  QBuffer buffer( &data );
155  pm.save( &buffer, "PNG", 100 );
156  tooltip += QStringLiteral( "<p><img src='data:image/png;base64, %3'>" ).arg( QString( data.toBase64() ) );
157  break;
158  }
159 
161  {
162  int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 );
163  int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
164  const QgsPalLayerSettings settings = mStyle->labelSettings( name );
165  QPixmap pm = QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, QSize( width, height ), QString(), height / 20 );
166  QByteArray data;
167  QBuffer buffer( &data );
168  pm.save( &buffer, "PNG", 100 );
169  tooltip += QStringLiteral( "<p><img src='data:image/png;base64, %3'>" ).arg( QString( data.toBase64() ) );
170  break;
171  }
172 
174  {
175  int width = static_cast< int >( Qgis::UI_SCALE_FACTOR * QFontMetrics( data( index, Qt::FontRole ).value< QFont >() ).horizontalAdvance( 'X' ) * 23 );
176  int height = static_cast< int >( width / 1.61803398875 ); // golden ratio
177 
178  const QgsLegendPatchShape shape = mStyle->legendPatchShape( name );
179  if ( const QgsSymbol *symbol = mStyle->previewSymbolForPatchShape( shape ) )
180  {
181  QPixmap pm = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, QSize( width, height ), height / 20, nullptr, false, nullptr, &shape );
182  QByteArray data;
183  QBuffer buffer( &data );
184  pm.save( &buffer, "PNG", 100 );
185  tooltip += QStringLiteral( "<p><img src='data:image/png;base64, %3'>" ).arg( QString( data.toBase64() ) );
186  }
187  break;
188  }
189 
191  case QgsStyle::TagEntity:
194  break;
195  }
196  return tooltip;
197  }
198  else
199  {
200  return name;
201  }
202  }
203  case Tags:
204  return mStyle->tagsOfSymbol( entityType, name ).join( QLatin1String( ", " ) );
205  }
206  return QVariant();
207  }
208 
209  case Qt::DecorationRole:
210  {
211  // Generate icons at all additional sizes specified for the model.
212  // This allows the model to have size responsive icons.
213 
214  if ( !mExpressionContext )
215  {
216  // build the expression context once, and keep it around. Usually this is a no-no, but in this
217  // case we want to avoid creating potentially thousands of contexts one-by-one (usually one context
218  // is created for a batch of multiple evalutions like this), and we only use a very minimal context
219  // anyway...
220  mExpressionContext = std::make_unique< QgsExpressionContext >();
221  mExpressionContext->appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) );
222  }
223 
224  switch ( index.column() )
225  {
226  case Name:
227  switch ( entityType )
228  {
230  {
231  // use cached icon if possible
232  QIcon icon = mIconCache[ entityType ].value( name );
233  if ( !icon.isNull() )
234  return icon;
235 
236  std::unique_ptr< QgsSymbol > symbol( mStyle->symbol( name ) );
237  if ( symbol )
238  {
239  if ( mAdditionalSizes.isEmpty() )
240  icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( 24, 24 ), 1, nullptr, false, mExpressionContext.get() ) );
241 
242  for ( const QSize &s : mAdditionalSizes )
243  {
244  icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ), nullptr, false, mExpressionContext.get() ) );
245  }
246 
247  }
248  mIconCache[ entityType ].insert( name, icon );
249  return icon;
250  }
252  {
253  // use cached icon if possible
254  QIcon icon = mIconCache[ entityType ].value( name );
255  if ( !icon.isNull() )
256  return icon;
257 
258  std::unique_ptr< QgsColorRamp > ramp( mStyle->colorRamp( name ) );
259  if ( ramp )
260  {
261  if ( mAdditionalSizes.isEmpty() )
262  icon.addPixmap( QgsSymbolLayerUtils::colorRampPreviewPixmap( ramp.get(), QSize( 24, 24 ), 1 ) );
263  for ( const QSize &s : mAdditionalSizes )
264  {
265  icon.addPixmap( QgsSymbolLayerUtils::colorRampPreviewPixmap( ramp.get(), s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ) ) );
266  }
267 
268  }
269  mIconCache[ entityType ].insert( name, icon );
270  return icon;
271  }
272 
274  {
275  // use cached icon if possible
276  QIcon icon = mIconCache[ entityType ].value( name );
277  if ( !icon.isNull() )
278  return icon;
279 
280  const QgsTextFormat format( mStyle->textFormat( name ) );
281  if ( mAdditionalSizes.isEmpty() )
282  icon.addPixmap( QgsTextFormat::textFormatPreviewPixmap( format, QSize( 24, 24 ), QString(), 1 ) );
283  for ( const QSize &s : mAdditionalSizes )
284  {
285  icon.addPixmap( QgsTextFormat::textFormatPreviewPixmap( format, s, QString(), static_cast< int >( s.width() * ICON_PADDING_FACTOR ) ) );
286  }
287  mIconCache[ entityType ].insert( name, icon );
288  return icon;
289  }
290 
292  {
293  // use cached icon if possible
294  QIcon icon = mIconCache[ entityType ].value( name );
295  if ( !icon.isNull() )
296  return icon;
297 
298  const QgsPalLayerSettings settings( mStyle->labelSettings( name ) );
299  if ( mAdditionalSizes.isEmpty() )
300  icon.addPixmap( QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, QSize( 24, 24 ), QString(), 1 ) );
301  for ( const QSize &s : mAdditionalSizes )
302  {
303  icon.addPixmap( QgsPalLayerSettings::labelSettingsPreviewPixmap( settings, s, QString(), static_cast< int >( s.width() * ICON_PADDING_FACTOR ) ) );
304  }
305  mIconCache[ entityType ].insert( name, icon );
306  return icon;
307  }
308 
310  {
311  // use cached icon if possible
312  QIcon icon = mIconCache[ entityType ].value( name );
313  if ( !icon.isNull() )
314  return icon;
315 
316  const QgsLegendPatchShape shape = mStyle->legendPatchShape( name );
317  if ( !shape.isNull() )
318  {
319  if ( const QgsSymbol *symbol = mStyle->previewSymbolForPatchShape( shape ) )
320  {
321  if ( mAdditionalSizes.isEmpty() )
322  icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, QSize( 24, 24 ), 1, nullptr, false, mExpressionContext.get(), &shape ) );
323 
324  for ( const QSize &s : mAdditionalSizes )
325  {
326  icon.addPixmap( QgsSymbolLayerUtils::symbolPreviewPixmap( symbol, s, static_cast< int >( s.width() * ICON_PADDING_FACTOR ), nullptr, false, mExpressionContext.get(), &shape ) );
327  }
328  }
329  }
330  mIconCache[ entityType ].insert( name, icon );
331  return icon;
332  }
333 
335  {
336  // hack for now -- we just use a generic "3d icon" svg file.
337  // TODO - render proper thumbnails
338 
339  // use cached icon if possible
340  QIcon icon = mIconCache[ entityType ].value( name );
341  if ( !icon.isNull() )
342  return icon;
343 
344  if ( sIconGenerator && !mPending3dSymbolIcons.contains( name ) )
345  {
346  mPending3dSymbolIcons.insert( name );
347  sIconGenerator->generateIcon( mStyle, QgsStyle::Symbol3DEntity, name );
348  }
349 
350  // TODO - use hourglass icon
351  if ( mAdditionalSizes.isEmpty() )
352  icon.addFile( QgsApplication::defaultThemePath() + QDir::separator() + QStringLiteral( "3d.svg" ), QSize( 24, 24 ) );
353  for ( const QSize &s : mAdditionalSizes )
354  {
355  icon.addFile( QgsApplication::defaultThemePath() + QDir::separator() + QStringLiteral( "3d.svg" ), s );
356  }
357  mIconCache[ entityType ].insert( name, icon );
358  return icon;
359  }
360 
361  case QgsStyle::TagEntity:
363  return QVariant();
364  }
365  break;
366 
367  case Tags:
368  return QVariant();
369  }
370  return QVariant();
371  }
372 
373  case TypeRole:
374  return entityType;
375 
376  case TagRole:
377  return mStyle->tagsOfSymbol( entityType, name );
378 
379  case IsFavoriteRole:
380  return mStyle->isFavorite( entityType, name );
381 
382  case SymbolTypeRole:
383  {
384  switch ( entityType )
385  {
387  {
388  const QgsSymbol *symbol = mStyle->symbolRef( name );
389  return symbol ? static_cast< int >( symbol->type() ) : QVariant();
390  }
391 
393  return static_cast< int >( mStyle->legendPatchShapeSymbolType( name ) );
394 
395  case QgsStyle::TagEntity:
401  return QVariant();
402  }
403  return QVariant();
404  }
405 
406  case LayerTypeRole:
407  {
408  switch ( entityType )
409  {
411  return mStyle->labelSettingsLayerType( name );
412 
416  case QgsStyle::TagEntity:
420  return QVariant();
421  }
422  return QVariant();
423  }
424 
426  {
427  switch ( entityType )
428  {
430  {
431  QVariantList res;
432  const QList< QgsWkbTypes::GeometryType > types = mStyle->symbol3DCompatibleGeometryTypes( name );
433  res.reserve( types.size() );
434  for ( QgsWkbTypes::GeometryType type : types )
435  {
436  res << static_cast< int >( type );
437  }
438  return res;
439  }
440 
444  case QgsStyle::TagEntity:
448  return QVariant();
449  }
450  return QVariant();
451  }
452 
453  default:
454  return QVariant();
455  }
456 #ifndef _MSC_VER // avoid warning
457  return QVariant(); // avoid warning
458 #endif
459 }
460 
461 bool QgsStyleModel::setData( const QModelIndex &index, const QVariant &value, int role )
462 {
463  if ( index.row() < 0 || index.row() >= rowCount( QModelIndex() ) || role != Qt::EditRole )
464  return false;
465 
466  switch ( index.column() )
467  {
468  case Name:
469  {
470  QgsStyle::StyleEntity entityType = entityTypeFromRow( index.row() );
471  QString name;
472  switch ( entityType )
473  {
474  case QgsStyle::TagEntity:
476  return false;
477 
478  default:
479  name = mEntityNames[ entityType ].value( index.row() - offsetForEntity( entityType ) );
480  break;
481  }
482 
483  const QString newName = value.toString();
484  return mStyle->renameEntity( entityType, name, newName );
485  }
486 
487  case Tags:
488  return false;
489  }
490 
491  return false;
492 }
493 
494 Qt::ItemFlags QgsStyleModel::flags( const QModelIndex &index ) const
495 {
496  Qt::ItemFlags flags = QAbstractItemModel::flags( index );
497  if ( index.isValid() && index.column() == Name )
498  {
499  return flags | Qt::ItemIsEditable;
500  }
501  else
502  {
503  return flags;
504  }
505 }
506 
507 QVariant QgsStyleModel::headerData( int section, Qt::Orientation orientation, int role ) const
508 {
509  if ( role == Qt::DisplayRole )
510  {
511  if ( orientation == Qt::Vertical ) //row
512  {
513  return QVariant( section );
514  }
515  else
516  {
517  switch ( section )
518  {
519  case Name:
520  return QVariant( tr( "Name" ) );
521 
522  case Tags:
523  return QVariant( tr( "Tags" ) );
524 
525  default:
526  return QVariant();
527  }
528  }
529  }
530  else
531  {
532  return QVariant();
533  }
534 }
535 
536 QModelIndex QgsStyleModel::index( int row, int column, const QModelIndex &parent ) const
537 {
538  if ( !hasIndex( row, column, parent ) )
539  return QModelIndex();
540 
541  if ( !parent.isValid() )
542  {
543  return createIndex( row, column );
544  }
545 
546  return QModelIndex();
547 }
548 
549 QModelIndex QgsStyleModel::parent( const QModelIndex & ) const
550 {
551  //all items are top level for now
552  return QModelIndex();
553 }
554 
555 int QgsStyleModel::rowCount( const QModelIndex &parent ) const
556 {
557  if ( !parent.isValid() )
558  {
559  int count = 0;
560  for ( QgsStyle::StyleEntity type : ENTITIES )
561  count += mEntityNames[ type ].size();
562  return count;
563  }
564  return 0;
565 }
566 
567 int QgsStyleModel::columnCount( const QModelIndex & ) const
568 {
569  return 2;
570 }
571 
573 {
574  if ( mAdditionalSizes.contains( size ) )
575  return;
576 
577  mAdditionalSizes << size;
578 
579  if ( sIconGenerator )
580  sIconGenerator->setIconSizes( mAdditionalSizes );
581 
582  mIconCache.clear();
583 }
584 
586 {
587  sIconGenerator = generator;
588  connect( sIconGenerator, &QgsAbstractStyleEntityIconGenerator::iconGenerated, QgsApplication::defaultStyleModel(), &QgsStyleModel::iconGenerated, Qt::QueuedConnection );
589 }
590 
591 void QgsStyleModel::onEntityAdded( QgsStyle::StyleEntity type, const QString &name )
592 {
593  mIconCache[ type ].remove( name );
594  const QStringList oldSymbolNames = mEntityNames[ type ];
595  const QStringList newSymbolNames = mStyle->allNames( type );
596 
597  // find index of newly added symbol
598  const int newNameIndex = newSymbolNames.indexOf( name );
599  if ( newNameIndex < 0 )
600  return; // shouldn't happen
601 
602  const int offset = offsetForEntity( type );
603  beginInsertRows( QModelIndex(), newNameIndex + offset, newNameIndex + offset );
604  mEntityNames[ type ] = newSymbolNames;
605  endInsertRows();
606 }
607 
608 void QgsStyleModel::onEntityRemoved( QgsStyle::StyleEntity type, const QString &name )
609 {
610  mIconCache[ type ].remove( name );
611  const QStringList oldSymbolNames = mEntityNames[ type ];
612  const QStringList newSymbolNames = mStyle->allNames( type );
613 
614  // find index of removed symbol
615  const int oldNameIndex = oldSymbolNames.indexOf( name );
616  if ( oldNameIndex < 0 )
617  return; // shouldn't happen
618 
619  const int offset = offsetForEntity( type );
620  beginRemoveRows( QModelIndex(), oldNameIndex + offset, oldNameIndex + offset );
621  mEntityNames[ type ] = newSymbolNames;
622  endRemoveRows();
623 }
624 
625 void QgsStyleModel::onEntityChanged( QgsStyle::StyleEntity type, const QString &name )
626 {
627  mIconCache[ type ].remove( name );
628 
629  const int offset = offsetForEntity( type );
630  QModelIndex i = index( offset + mEntityNames[ type ].indexOf( name ), Tags );
631  emit dataChanged( i, i, QVector< int >() << Qt::DecorationRole );
632 }
633 
634 void QgsStyleModel::onEntityRename( QgsStyle::StyleEntity type, const QString &oldName, const QString &newName )
635 {
636  mIconCache[ type ].remove( oldName );
637  const QStringList oldSymbolNames = mEntityNames[ type ];
638  const QStringList newSymbolNames = mStyle->allNames( type );
639 
640  // find index of removed symbol
641  const int oldNameIndex = oldSymbolNames.indexOf( oldName );
642  if ( oldNameIndex < 0 )
643  return; // shouldn't happen
644 
645  // find index of added symbol
646  const int newNameIndex = newSymbolNames.indexOf( newName );
647  if ( newNameIndex < 0 )
648  return; // shouldn't happen
649 
650  if ( newNameIndex == oldNameIndex )
651  {
652  mEntityNames[ type ] = newSymbolNames;
653  return;
654  }
655 
656  const int offset = offsetForEntity( type );
657  beginMoveRows( QModelIndex(), oldNameIndex + offset, oldNameIndex + offset, QModelIndex(), ( newNameIndex > oldNameIndex ? newNameIndex + 1 : newNameIndex ) + offset );
658  mEntityNames[ type ] = newSymbolNames;
659  endMoveRows();
660 }
661 
662 void QgsStyleModel::onTagsChanged( int entity, const QString &name, const QStringList & )
663 {
664  QgsStyle::StyleEntity type = static_cast< QgsStyle::StyleEntity >( entity );
665  QModelIndex i;
666  int row = mEntityNames[type].indexOf( name ) + offsetForEntity( type );
667  switch ( static_cast< QgsStyle::StyleEntity >( entity ) )
668  {
669  case QgsStyle::TagEntity:
671  return;
672 
673  default:
674  i = index( row, Tags );
675  }
676  emit dataChanged( i, i );
677 }
678 
679 void QgsStyleModel::rebuildSymbolIcons()
680 {
681  mIconCache[ QgsStyle::SymbolEntity ].clear();
682  mExpressionContext.reset();
683  emit dataChanged( index( 0, 0 ), index( mEntityNames[ QgsStyle::SymbolEntity ].count() - 1, 0 ), QVector<int>() << Qt::DecorationRole );
684 }
685 
686 void QgsStyleModel::iconGenerated( QgsStyle::StyleEntity type, const QString &name, const QIcon &icon )
687 {
688  int row = mEntityNames[type].indexOf( name ) + offsetForEntity( type );
689 
690  switch ( type )
691  {
693  mPending3dSymbolIcons.remove( name );
694  mIconCache[ QgsStyle::Symbol3DEntity ].insert( name, icon );
695  emit dataChanged( index( row, 0 ), index( row, 0 ) );
696  break;
697 
699  case QgsStyle::TagEntity:
705  break;
706  }
707 }
708 
709 QgsStyle::StyleEntity QgsStyleModel::entityTypeFromRow( int row ) const
710 {
711  int maxRowForEntity = 0;
712  for ( QgsStyle::StyleEntity type : ENTITIES )
713  {
714  maxRowForEntity += mEntityNames[ type ].size();
715  if ( row < maxRowForEntity )
716  return type;
717  }
718 
719  // should never happen
720  Q_ASSERT( false );
721  return QgsStyle::SymbolEntity;
722 }
723 
724 int QgsStyleModel::offsetForEntity( QgsStyle::StyleEntity entity ) const
725 {
726  int offset = 0;
727  for ( QgsStyle::StyleEntity type : ENTITIES )
728  {
729  if ( type == entity )
730  return offset;
731 
732  offset += mEntityNames[ type ].size();
733  }
734  return 0;
735 }
736 
737 //
738 // QgsStyleProxyModel
739 //
740 
742  : QSortFilterProxyModel( parent )
743  , mStyle( style )
744 {
745  mModel = new QgsStyleModel( mStyle, this );
746  initialize();
747 }
748 
749 void QgsStyleProxyModel::initialize()
750 {
751  setSortCaseSensitivity( Qt::CaseInsensitive );
752 // setSortLocaleAware( true );
753  setSourceModel( mModel );
754  setDynamicSortFilter( true );
755  sort( 0 );
756 
757  connect( mStyle, &QgsStyle::entityTagsChanged, this, [ = ]
758  {
759  // update tagged symbols if filtering by tag
760  if ( mTagId >= 0 )
761  setTagId( mTagId );
762  if ( mSmartGroupId >= 0 )
763  setSmartGroupId( mSmartGroupId );
764  } );
765 
766  connect( mStyle, &QgsStyle::favoritedChanged, this, [ = ]
767  {
768  // update favorited symbols if filtering by favorite
769  if ( mFavoritesOnly )
770  setFavoritesOnly( mFavoritesOnly );
771  } );
772 
773  connect( mStyle, &QgsStyle::entityRenamed, this, [ = ]( QgsStyle::StyleEntity entity, const QString &, const QString & )
774  {
775  switch ( entity )
776  {
778  case QgsStyle::TagEntity:
779  return;
780 
781  default:
782  break;
783  }
784 
785  if ( mSmartGroupId >= 0 )
786  setSmartGroupId( mSmartGroupId );
787  } );
788 }
789 
791  : QSortFilterProxyModel( parent )
792  , mModel( model )
793  , mStyle( model->style() )
794 {
795  initialize();
796 }
797 
798 bool QgsStyleProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
799 {
800  if ( mFilterString.isEmpty() && !mEntityFilterEnabled && !mSymbolTypeFilterEnabled && mTagId < 0 && mSmartGroupId < 0 && !mFavoritesOnly )
801  return true;
802 
803  QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
804  const QString name = sourceModel()->data( index ).toString();
805  const QStringList tags = sourceModel()->data( index, QgsStyleModel::TagRole ).toStringList();
806 
807  QgsStyle::StyleEntity styleEntityType = static_cast< QgsStyle::StyleEntity >( sourceModel()->data( index, QgsStyleModel::TypeRole ).toInt() );
808  if ( mEntityFilterEnabled && ( mEntityFilters.empty() || !mEntityFilters.contains( styleEntityType ) ) )
809  return false;
810 
811  Qgis::SymbolType symbolType = static_cast< Qgis::SymbolType >( sourceModel()->data( index, QgsStyleModel::SymbolTypeRole ).toInt() );
812  if ( mSymbolTypeFilterEnabled && symbolType != mSymbolType )
813  return false;
814 
815  if ( mLayerType != QgsWkbTypes::UnknownGeometry )
816  {
817  switch ( styleEntityType )
818  {
821  case QgsStyle::TagEntity:
825  break;
826 
828  {
829  if ( mLayerType != static_cast< QgsWkbTypes::GeometryType >( sourceModel()->data( index, QgsStyleModel::LayerTypeRole ).toInt() ) )
830  return false;
831  break;
832  }
833 
835  {
836  const QVariantList types = sourceModel()->data( index, QgsStyleModel::CompatibleGeometryTypesRole ).toList();
837  if ( !types.empty() && !types.contains( mLayerType ) )
838  return false;
839  break;
840  }
841  }
842  }
843 
844  if ( mTagId >= 0 && !mTaggedSymbolNames.contains( name ) )
845  return false;
846 
847  if ( mSmartGroupId >= 0 && !mSmartGroupSymbolNames.contains( name ) )
848  return false;
849 
850  if ( mFavoritesOnly && !sourceModel()->data( index, QgsStyleModel::IsFavoriteRole ).toBool() )
851  return false;
852 
853  if ( !mFilterString.isEmpty() )
854  {
855  // filter by word, in both filter string and style entity name/tags
856  // this allows matching of a filter string "hash line" to the symbol "hashed red lines"
857  const QStringList partsToMatch = mFilterString.trimmed().split( ' ' );
858 
859  QStringList partsToSearch = name.split( ' ' );
860  for ( const QString &tag : tags )
861  {
862  partsToSearch.append( tag.split( ' ' ) );
863  }
864 
865  for ( const QString &part : partsToMatch )
866  {
867  bool found = false;
868  for ( const QString &partToSearch : std::as_const( partsToSearch ) )
869  {
870  if ( partToSearch.contains( part, Qt::CaseInsensitive ) )
871  {
872  found = true;
873  break;
874  }
875  }
876  if ( !found )
877  return false; // couldn't find a match for this word, so hide entity
878  }
879  }
880 
881  return true;
882 }
883 
884 void QgsStyleProxyModel::setFilterString( const QString &filter )
885 {
886  mFilterString = filter;
887  invalidateFilter();
888 }
889 
890 
892 {
893  return mFavoritesOnly;
894 }
895 
896 void QgsStyleProxyModel::setFavoritesOnly( bool favoritesOnly )
897 {
898  mFavoritesOnly = favoritesOnly;
899  invalidateFilter();
900 }
901 
903 {
904  mModel->addDesiredIconSize( size );
905 }
906 
908 {
909  return mSymbolTypeFilterEnabled;
910 }
911 
913 {
914  mSymbolTypeFilterEnabled = enabled;
915  invalidateFilter();
916 }
917 
919 {
920  return mLayerType;
921 }
922 
924 {
925  mLayerType = type;
926  invalidateFilter();
927 }
928 
930 {
931  mTagId = id;
932 
933  mTaggedSymbolNames.clear();
934  if ( mTagId >= 0 )
935  {
936  for ( QgsStyle::StyleEntity entity : ENTITIES )
937  mTaggedSymbolNames.append( mStyle->symbolsWithTag( entity, mTagId ) );
938  }
939 
940  invalidateFilter();
941 }
942 
944 {
945  return mTagId;
946 }
947 
949 {
950  mSmartGroupId = id;
951 
952  mSmartGroupSymbolNames.clear();
953  if ( mSmartGroupId >= 0 )
954  {
955  for ( QgsStyle::StyleEntity entity : ENTITIES )
956  mSmartGroupSymbolNames.append( mStyle->symbolsOfSmartgroup( entity, mSmartGroupId ) );
957  }
958  invalidateFilter();
959 }
960 
962 {
963  return mSmartGroupId;
964 }
965 
967 {
968  return mSymbolType;
969 }
970 
972 {
973  mSymbolType = symbolType;
974  invalidateFilter();
975 }
976 
978 {
979  return mEntityFilterEnabled;
980 }
981 
982 void QgsStyleProxyModel::setEntityFilterEnabled( bool entityFilterEnabled )
983 {
984  mEntityFilterEnabled = entityFilterEnabled;
985  invalidateFilter();
986 }
987 
989 {
990  return mEntityFilters.empty() ? QgsStyle::SymbolEntity : mEntityFilters.at( 0 );
991 }
992 
994 {
995  mEntityFilters = QList< QgsStyle::StyleEntity >() << entityFilter;
996  invalidateFilter();
997 }
998 
999 void QgsStyleProxyModel::setEntityFilters( const QList<QgsStyle::StyleEntity> &filters )
1000 {
1001  mEntityFilters = filters;
1002  invalidateFilter();
1003 }
1004 
SymbolType
Symbol types.
Definition: qgis.h:169
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:1052
An abstract base class for icon generators for a QgsStyleModel.
Definition: qgsstylemodel.h:44
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:467
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.
Qgis::SymbolType symbolType() const
Returns 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.
void setSymbolType(Qgis::SymbolType type)
Sets the symbol 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.
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...
QgsTextFormat textFormat(const QString &name) const
Returns the text format with the specified name.
Definition: qgsstyle.cpp:2122
QStringList allNames(StyleEntity type) const
Returns a list of the names of all existing entities of the specified type.
Definition: qgsstyle.cpp:2240
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:1337
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:2175
QgsSymbol * symbol(const QString &name)
Returns a NEW copy of symbol.
Definition: qgsstyle.cpp:275
QStringList symbolsOfSmartgroup(StyleEntity type, int id)
Returns the symbols for the smartgroup.
Definition: qgsstyle.cpp:2374
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:1913
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:281
QgsWkbTypes::GeometryType labelSettingsLayerType(const QString &name) const
Returns the layer geometry type corresponding to the label settings with the specified name,...
Definition: qgsstyle.cpp:2183
QgsColorRamp * colorRamp(const QString &name) const
Returns a new copy of the specified color ramp.
Definition: qgsstyle.cpp:446
bool renameEntity(StyleEntity type, const QString &oldName, const QString &newName)
Renames an entity of the specified type from oldName to newName.
Definition: qgsstyle.cpp:246
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:1964
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:2142
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 ...
Qgis::SymbolType legendPatchShapeSymbolType(const QString &name) const
Returns the symbol type corresponding to the legend patch shape with the specified name,...
Definition: qgsstyle.cpp:2157
const QgsSymbol * previewSymbolForPatchShape(const QgsLegendPatchShape &shape) const
Returns a symbol to use for rendering preview icons for a patch shape.
Definition: qgsstyle.cpp:2211
QgsLegendPatchShape legendPatchShape(const QString &name) const
Returns the legend patch shape with the specified name.
Definition: qgsstyle.cpp:2147
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:38
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:97
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