QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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"
24
25#include <QIcon>
26#include <QBuffer>
27#include <QDir>
28
29const double ICON_PADDING_FACTOR = 0.16;
30
32
33QgsAbstractStyleEntityIconGenerator *QgsStyleModel::sIconGenerator = nullptr;
34
35//
36// QgsAbstractStyleEntityIconGenerator
37//
38
40 : QObject( parent )
41{
42
43}
44
46{
47 mIconSizes = sizes;
48}
49
51{
52 return mIconSizes;
53}
54
55
56//
57// QgsStyleModel
58//
59
60QgsStyleModel::QgsStyleModel( QgsStyle *style, QObject *parent )
61 : QAbstractItemModel( parent )
62 , mStyle( style )
63{
64 Q_ASSERT( mStyle );
65
66 for ( QgsStyle::StyleEntity entity : ENTITIES )
67 {
68 mEntityNames.insert( entity, mStyle->allNames( entity ) );
69 }
70
71 connect( mStyle, &QgsStyle::entityAdded, this, &QgsStyleModel::onEntityAdded );
72 connect( mStyle, &QgsStyle::entityRemoved, this, &QgsStyleModel::onEntityRemoved );
73 connect( mStyle, &QgsStyle::entityRenamed, this, &QgsStyleModel::onEntityRename );
74 connect( mStyle, &QgsStyle::entityChanged, this, &QgsStyleModel::onEntityChanged );
75 connect( mStyle, &QgsStyle::favoritedChanged, this, &QgsStyleModel::onFavoriteChanged );
76 connect( mStyle, &QgsStyle::entityTagsChanged, this, &QgsStyleModel::onTagsChanged );
77 connect( mStyle, &QgsStyle::rebuildIconPreviews, this, &QgsStyleModel::rebuildSymbolIcons );
78
79 // when a remote svg or image has been fetched, update the model's decorations.
80 // this is required if a symbol utilizes remote svgs, and the current icons
81 // have been generated using the temporary "downloading" svg. In this case
82 // we require the preview to be regenerated to use the correct fetched
83 // svg
84 connect( QgsApplication::svgCache(), &QgsSvgCache::remoteSvgFetched, this, &QgsStyleModel::rebuildSymbolIcons );
85 connect( QgsApplication::imageCache(), &QgsImageCache::remoteImageFetched, this, &QgsStyleModel::rebuildSymbolIcons );
86
87 if ( sIconGenerator )
88 connect( sIconGenerator, &QgsAbstractStyleEntityIconGenerator::iconGenerated, this, &QgsStyleModel::iconGenerated, Qt::QueuedConnection );
89}
90
91QVariant 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 {
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
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
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
401 return QVariant();
402 }
403 return QVariant();
404 }
405
406 case LayerTypeRole:
407 {
408 switch ( entityType )
409 {
411 return mStyle->labelSettingsLayerType( name );
412
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
448 return QVariant();
449 }
450 return QVariant();
451 }
452
453 case EntityName:
454 return name;
455
456 case StyleName:
457 return mStyle->name();
458
459 case StyleFileName:
460 return mStyle->fileName();
461
462 default:
463 return QVariant();
464 }
465#ifndef _MSC_VER // avoid warning
466 return QVariant(); // avoid warning
467#endif
468}
469
470bool QgsStyleModel::setData( const QModelIndex &index, const QVariant &value, int role )
471{
472 if ( index.row() < 0 || index.row() >= rowCount( QModelIndex() ) || role != Qt::EditRole )
473 return false;
474
475 switch ( index.column() )
476 {
477 case Name:
478 {
479 QgsStyle::StyleEntity entityType = entityTypeFromRow( index.row() );
480 QString name;
481 switch ( entityType )
482 {
485 return false;
486
487 default:
488 name = mEntityNames[ entityType ].value( index.row() - offsetForEntity( entityType ) );
489 break;
490 }
491
492 const QString newName = value.toString();
493 return mStyle->renameEntity( entityType, name, newName );
494 }
495
496 case Tags:
497 return false;
498 }
499
500 return false;
501}
502
503Qt::ItemFlags QgsStyleModel::flags( const QModelIndex &index ) const
504{
505 Qt::ItemFlags flags = QAbstractItemModel::flags( index );
506 if ( index.isValid() && index.column() == Name )
507 {
508 return flags | Qt::ItemIsEditable;
509 }
510 else
511 {
512 return flags;
513 }
514}
515
516QVariant QgsStyleModel::headerData( int section, Qt::Orientation orientation, int role ) const
517{
518 return headerDataStatic( section, orientation, role );
519}
520
521QVariant QgsStyleModel::headerDataStatic( int section, Qt::Orientation orientation, int role )
522{
523 if ( role == Qt::DisplayRole )
524 {
525 if ( orientation == Qt::Vertical ) //row
526 {
527 return QVariant( section );
528 }
529 else
530 {
531 switch ( section )
532 {
533 case Name:
534 return QVariant( tr( "Name" ) );
535
536 case Tags:
537 return QVariant( tr( "Tags" ) );
538
539 default:
540 return QVariant();
541 }
542 }
543 }
544 else
545 {
546 return QVariant();
547 }
548}
549
550QModelIndex QgsStyleModel::index( int row, int column, const QModelIndex &parent ) const
551{
552 if ( !hasIndex( row, column, parent ) )
553 return QModelIndex();
554
555 if ( !parent.isValid() )
556 {
557 return createIndex( row, column );
558 }
559
560 return QModelIndex();
561}
562
563QModelIndex QgsStyleModel::parent( const QModelIndex & ) const
564{
565 //all items are top level for now
566 return QModelIndex();
567}
568
569int QgsStyleModel::rowCount( const QModelIndex &parent ) const
570{
571 if ( !parent.isValid() )
572 {
573 int count = 0;
574 for ( QgsStyle::StyleEntity type : ENTITIES )
575 count += mEntityNames[ type ].size();
576 return count;
577 }
578 return 0;
579}
580
581int QgsStyleModel::columnCount( const QModelIndex & ) const
582{
583 return 2;
584}
585
587{
588 if ( mAdditionalSizes.contains( size ) )
589 return;
590
591 mAdditionalSizes << size;
592
593 if ( sIconGenerator )
594 sIconGenerator->setIconSizes( mAdditionalSizes );
595
596 mIconCache.clear();
597}
598
600{
601 sIconGenerator = generator;
602 connect( sIconGenerator, &QgsAbstractStyleEntityIconGenerator::iconGenerated, QgsApplication::defaultStyleModel(), &QgsStyleModel::iconGenerated, Qt::QueuedConnection );
603}
604
605void QgsStyleModel::onEntityAdded( QgsStyle::StyleEntity type, const QString &name )
606{
607 mIconCache[ type ].remove( name );
608 const QStringList oldSymbolNames = mEntityNames[ type ];
609 const QStringList newSymbolNames = mStyle->allNames( type );
610
611 // find index of newly added symbol
612 const int newNameIndex = newSymbolNames.indexOf( name );
613 if ( newNameIndex < 0 )
614 return; // shouldn't happen
615
616 const int offset = offsetForEntity( type );
617 beginInsertRows( QModelIndex(), newNameIndex + offset, newNameIndex + offset );
618 mEntityNames[ type ] = newSymbolNames;
619 endInsertRows();
620}
621
622void QgsStyleModel::onEntityRemoved( QgsStyle::StyleEntity type, const QString &name )
623{
624 mIconCache[ type ].remove( name );
625 const QStringList oldSymbolNames = mEntityNames[ type ];
626 const QStringList newSymbolNames = mStyle->allNames( type );
627
628 // find index of removed symbol
629 const int oldNameIndex = oldSymbolNames.indexOf( name );
630 if ( oldNameIndex < 0 )
631 return; // shouldn't happen
632
633 const int offset = offsetForEntity( type );
634 beginRemoveRows( QModelIndex(), oldNameIndex + offset, oldNameIndex + offset );
635 mEntityNames[ type ] = newSymbolNames;
636 endRemoveRows();
637}
638
639void QgsStyleModel::onEntityChanged( QgsStyle::StyleEntity type, const QString &name )
640{
641 mIconCache[ type ].remove( name );
642
643 const int offset = offsetForEntity( type );
644 QModelIndex i = index( offset + mEntityNames[ type ].indexOf( name ), Tags );
645 emit dataChanged( i, i, QVector< int >() << Qt::DecorationRole );
646}
647
648void QgsStyleModel::onFavoriteChanged( QgsStyle::StyleEntity type, const QString &name, bool )
649{
650 const int offset = offsetForEntity( type );
651 QModelIndex i = index( offset + mEntityNames[ type ].indexOf( name ), Name );
652 emit dataChanged( i, i, QVector< int >() << Role::IsFavoriteRole );
653}
654
655void QgsStyleModel::onEntityRename( QgsStyle::StyleEntity type, const QString &oldName, const QString &newName )
656{
657 mIconCache[ type ].remove( oldName );
658 const QStringList oldSymbolNames = mEntityNames[ type ];
659 const QStringList newSymbolNames = mStyle->allNames( type );
660
661 // find index of removed symbol
662 const int oldNameIndex = oldSymbolNames.indexOf( oldName );
663 if ( oldNameIndex < 0 )
664 return; // shouldn't happen
665
666 // find index of added symbol
667 const int newNameIndex = newSymbolNames.indexOf( newName );
668 if ( newNameIndex < 0 )
669 return; // shouldn't happen
670
671 if ( newNameIndex == oldNameIndex )
672 {
673 mEntityNames[ type ] = newSymbolNames;
674 return;
675 }
676
677 const int offset = offsetForEntity( type );
678 beginMoveRows( QModelIndex(), oldNameIndex + offset, oldNameIndex + offset, QModelIndex(), ( newNameIndex > oldNameIndex ? newNameIndex + 1 : newNameIndex ) + offset );
679 mEntityNames[ type ] = newSymbolNames;
680 endMoveRows();
681}
682
683void QgsStyleModel::onTagsChanged( int entity, const QString &name, const QStringList & )
684{
685 QgsStyle::StyleEntity type = static_cast< QgsStyle::StyleEntity >( entity );
686 int row = mEntityNames[type].indexOf( name ) + offsetForEntity( type );
687 switch ( static_cast< QgsStyle::StyleEntity >( entity ) )
688 {
691 return;
692
693 default:
694 break;
695 }
696 emit dataChanged( index( row, Name ), index( row, Tags ) );
697}
698
699void QgsStyleModel::rebuildSymbolIcons()
700{
701 mIconCache[ QgsStyle::SymbolEntity ].clear();
702 mExpressionContext.reset();
703 emit dataChanged( index( 0, 0 ), index( mEntityNames[ QgsStyle::SymbolEntity ].count() - 1, 0 ), QVector<int>() << Qt::DecorationRole );
704}
705
706void QgsStyleModel::iconGenerated( QgsStyle::StyleEntity type, const QString &name, const QIcon &icon )
707{
708 int row = mEntityNames[type].indexOf( name ) + offsetForEntity( type );
709
710 switch ( type )
711 {
713 mPending3dSymbolIcons.remove( name );
714 mIconCache[ QgsStyle::Symbol3DEntity ].insert( name, icon );
715 emit dataChanged( index( row, 0 ), index( row, 0 ) );
716 break;
717
725 break;
726 }
727}
728
729QgsStyle::StyleEntity QgsStyleModel::entityTypeFromRow( int row ) const
730{
731 int maxRowForEntity = 0;
732 for ( QgsStyle::StyleEntity type : ENTITIES )
733 {
734 maxRowForEntity += mEntityNames[ type ].size();
735 if ( row < maxRowForEntity )
736 return type;
737 }
738
739 // should never happen
740 Q_ASSERT( false );
742}
743
744int QgsStyleModel::offsetForEntity( QgsStyle::StyleEntity entity ) const
745{
746 int offset = 0;
747 for ( QgsStyle::StyleEntity type : ENTITIES )
748 {
749 if ( type == entity )
750 return offset;
751
752 offset += mEntityNames[ type ].size();
753 }
754 return 0;
755}
756
757//
758// QgsStyleProxyModel
759//
760
762 : QSortFilterProxyModel( parent )
763 , mStyle( style )
764{
765 mModel = new QgsStyleModel( mStyle, this );
766 setSourceModel( mModel );
767 initialize();
768}
769
770void QgsStyleProxyModel::initialize()
771{
772 setSortCaseSensitivity( Qt::CaseInsensitive );
773// setSortLocaleAware( true );
774 setDynamicSortFilter( true );
775 sort( 0 );
776
777 if ( mStyle )
778 {
779 connect( mStyle, &QgsStyle::entityTagsChanged, this, [ = ]
780 {
781 // update tagged symbols if filtering by tag
782 if ( mTagId >= 0 )
783 setTagId( mTagId );
784 if ( mSmartGroupId >= 0 )
785 setSmartGroupId( mSmartGroupId );
786 } );
787
788 connect( mStyle, &QgsStyle::entityRenamed, this, [ = ]( QgsStyle::StyleEntity entity, const QString &, const QString & )
789 {
790 switch ( entity )
791 {
794 return;
795
796 default:
797 break;
798 }
799
800 if ( mSmartGroupId >= 0 )
801 setSmartGroupId( mSmartGroupId );
802 } );
803 }
804}
805
807 : QSortFilterProxyModel( parent )
808 , mModel( model )
809 , mStyle( model->style() )
810{
811 setSourceModel( mModel );
812 initialize();
813}
814
816 : QSortFilterProxyModel( parent )
817 , mCombinedModel( model )
818{
819 setSourceModel( mCombinedModel );
820 initialize();
821}
822
823bool QgsStyleProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
824{
825 if ( mFilterString.isEmpty() && !mEntityFilterEnabled && !mSymbolTypeFilterEnabled && mTagId < 0 && mSmartGroupId < 0 && !mFavoritesOnly && mTagFilter.isEmpty() )
826 return true;
827
828 const QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
829
830 if ( sourceModel()->data( index, QgsStyleModel::IsTitleRole ).toBool() )
831 return true;
832
833 const QString name = sourceModel()->data( index ).toString();
834 const QStringList tags = sourceModel()->data( index, QgsStyleModel::TagRole ).toStringList();
835
836 QgsStyle::StyleEntity styleEntityType = static_cast< QgsStyle::StyleEntity >( sourceModel()->data( index, QgsStyleModel::TypeRole ).toInt() );
837 if ( mEntityFilterEnabled && ( mEntityFilters.empty() || !mEntityFilters.contains( styleEntityType ) ) )
838 return false;
839
840 Qgis::SymbolType symbolType = static_cast< Qgis::SymbolType >( sourceModel()->data( index, QgsStyleModel::SymbolTypeRole ).toInt() );
841 if ( mSymbolTypeFilterEnabled && symbolType != mSymbolType )
842 return false;
843
844 if ( mLayerType != QgsWkbTypes::UnknownGeometry )
845 {
846 switch ( styleEntityType )
847 {
854 break;
855
857 {
858 if ( mLayerType != static_cast< QgsWkbTypes::GeometryType >( sourceModel()->data( index, QgsStyleModel::LayerTypeRole ).toInt() ) )
859 return false;
860 break;
861 }
862
864 {
865 const QVariantList types = sourceModel()->data( index, QgsStyleModel::CompatibleGeometryTypesRole ).toList();
866 if ( !types.empty() && !types.contains( mLayerType ) )
867 return false;
868 break;
869 }
870 }
871 }
872
873 if ( mTagId >= 0 && !mTaggedSymbolNames.contains( name ) )
874 return false;
875
876 if ( mSmartGroupId >= 0 && !mSmartGroupSymbolNames.contains( name ) )
877 return false;
878
879 if ( !mTagFilter.isEmpty() && !tags.contains( mTagFilter, Qt::CaseInsensitive ) )
880 return false;
881
882 if ( mFavoritesOnly && !sourceModel()->data( index, QgsStyleModel::IsFavoriteRole ).toBool() )
883 return false;
884
885 if ( !mFilterString.isEmpty() )
886 {
887 // filter by word, in both filter string and style entity name/tags
888 // this allows matching of a filter string "hash line" to the symbol "hashed red lines"
889 const QStringList partsToMatch = mFilterString.trimmed().split( ' ' );
890
891 QStringList partsToSearch = name.split( ' ' );
892 for ( const QString &tag : tags )
893 {
894 partsToSearch.append( tag.split( ' ' ) );
895 }
896
897 for ( const QString &part : partsToMatch )
898 {
899 bool found = false;
900 for ( const QString &partToSearch : std::as_const( partsToSearch ) )
901 {
902 if ( partToSearch.contains( part, Qt::CaseInsensitive ) )
903 {
904 found = true;
905 break;
906 }
907 }
908 if ( !found )
909 return false; // couldn't find a match for this word, so hide entity
910 }
911 }
912
913 return true;
914}
915
916bool QgsStyleProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
917{
918 const QString leftSource = sourceModel()->data( left, QgsStyleModel::StyleFileName ).toString();
919 const QString rightSource = sourceModel()->data( right, QgsStyleModel::StyleFileName ).toString();
920 if ( leftSource != rightSource )
921 return QString::localeAwareCompare( leftSource, rightSource ) < 0;
922
923 const QString leftName = sourceModel()->data( left, QgsStyleModel::EntityName ).toString();
924 const QString rightName = sourceModel()->data( right, QgsStyleModel::EntityName ).toString();
925 return QString::localeAwareCompare( leftName, rightName ) < 0;
926}
927
928void QgsStyleProxyModel::setFilterString( const QString &filter )
929{
930 mFilterString = filter;
931 invalidateFilter();
932}
933
934
936{
937 return mFavoritesOnly;
938}
939
940void QgsStyleProxyModel::setFavoritesOnly( bool favoritesOnly )
941{
942 mFavoritesOnly = favoritesOnly;
943 invalidateFilter();
944}
945
947{
948 if ( mModel )
949 mModel->addDesiredIconSize( size );
950 if ( mCombinedModel )
951 mCombinedModel->addDesiredIconSize( size );
952}
953
955{
956 return mSymbolTypeFilterEnabled;
957}
958
960{
961 mSymbolTypeFilterEnabled = enabled;
962 invalidateFilter();
963}
964
966{
967 return mLayerType;
968}
969
971{
972 mLayerType = type;
973 invalidateFilter();
974}
975
977{
978 if ( !mStyle )
979 return;
980 mTagId = id;
981
982 mTaggedSymbolNames.clear();
983 if ( mTagId >= 0 )
984 {
985 for ( QgsStyle::StyleEntity entity : ENTITIES )
986 mTaggedSymbolNames.append( mStyle->symbolsWithTag( entity, mTagId ) );
987 }
988
989 invalidateFilter();
990}
991
993{
994 return mTagId;
995}
996
997void QgsStyleProxyModel::setTagString( const QString &tag )
998{
999 mTagFilter = tag;
1000
1001 invalidateFilter();
1002}
1003
1005{
1006 return mTagFilter;
1007}
1008
1010{
1011 if ( !mStyle )
1012 return;
1013
1014 mSmartGroupId = id;
1015
1016 mSmartGroupSymbolNames.clear();
1017 if ( mSmartGroupId >= 0 )
1018 {
1019 for ( QgsStyle::StyleEntity entity : ENTITIES )
1020 mSmartGroupSymbolNames.append( mStyle->symbolsOfSmartgroup( entity, mSmartGroupId ) );
1021 }
1022
1023 invalidateFilter();
1024}
1025
1027{
1028 return mSmartGroupId;
1029}
1030
1032{
1033 return mSymbolType;
1034}
1035
1037{
1038 mSymbolType = symbolType;
1039 invalidateFilter();
1040}
1041
1043{
1044 return mEntityFilterEnabled;
1045}
1046
1047void QgsStyleProxyModel::setEntityFilterEnabled( bool entityFilterEnabled )
1048{
1049 mEntityFilterEnabled = entityFilterEnabled;
1050 invalidateFilter();
1051}
1052
1054{
1055 return mEntityFilters.empty() ? QgsStyle::SymbolEntity : mEntityFilters.at( 0 );
1056}
1057
1059{
1060 mEntityFilters = QList< QgsStyle::StyleEntity >() << entityFilter;
1061 invalidateFilter();
1062}
1063
1064void QgsStyleProxyModel::setEntityFilters( const QList<QgsStyle::StyleEntity> &filters )
1065{
1066 mEntityFilters = filters;
1067 invalidateFilter();
1068}
1069
SymbolType
Symbol types.
Definition: qgis.h:206
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:2304
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()).
A model which contains entities from multiple QgsStyle databases.
void addDesiredIconSize(QSize size)
Adds an additional icon size to generate for Qt::DecorationRole data.
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.
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
@ StyleName
Name of associated QgsStyle (QgsStyle::name()) (since QGIS 3.26)
@ LayerTypeRole
Layer type (for label settings entities)
@ StyleFileName
File name of associated QgsStyle (QgsStyle::fileName()) (since QGIS 3.26)
@ EntityName
Entity name (since QGIS 3.26)
@ 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)
@ IsTitleRole
True if the index corresponds to a title item (since QGIS 3.26)
@ 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 lessThan(const QModelIndex &left, const QModelIndex &right) const override
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.
QString tagString() const
Returns the tag string used to filter style entities by.
Qgis::SymbolType symbolType() const
Returns the symbol type filter.
void setTagString(const QString &tag)
Sets a tag to filter style entities by.
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:2120
QStringList allNames(StyleEntity type) const
Returns a list of the names of all existing entities of the specified type.
Definition: qgsstyle.cpp:2238
void entityChanged(QgsStyle::StyleEntity entity, const QString &name)
Emitted whenever an entity's definition is changed.
void rebuildIconPreviews()
Emitted whenever icon previews for entities in the style must be rebuilt.
QStringList symbolsWithTag(StyleEntity type, int tagid) const
Returns the symbol names with which have the given tag.
Definition: qgsstyle.cpp:1335
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:2173
QgsSymbol * symbol(const QString &name)
Returns a NEW copy of symbol.
Definition: qgsstyle.cpp:291
QStringList symbolsOfSmartgroup(StyleEntity type, int id)
Returns the symbols for the smartgroup.
Definition: qgsstyle.cpp:2372
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:1911
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:297
QString fileName() const
Returns the current file name of the style database.
Definition: qgsstyle.h:888
QgsWkbTypes::GeometryType labelSettingsLayerType(const QString &name) const
Returns the layer geometry type corresponding to the label settings with the specified name,...
Definition: qgsstyle.cpp:2181
QgsColorRamp * colorRamp(const QString &name) const
Returns a new copy of the specified color ramp.
Definition: qgsstyle.cpp:462
bool renameEntity(StyleEntity type, const QString &oldName, const QString &newName)
Renames an entity of the specified type from oldName to newName.
Definition: qgsstyle.cpp:262
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:1962
QString name() const
Returns the name of the style.
Definition: qgsstyle.cpp:106
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:2140
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:2155
const QgsSymbol * previewSymbolForPatchShape(const QgsLegendPatchShape &shape) const
Returns a symbol to use for rendering preview icons for a patch shape.
Definition: qgsstyle.cpp:2209
QgsLegendPatchShape legendPatchShape(const QString &name) const
Returns the legend patch shape with the specified name.
Definition: qgsstyle.cpp:2145
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:93
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:152
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