@@ -125,40 +125,97 @@ QVariant QgsCategorizedSymbolRendererModel::data( const QModelIndex &index, int
125125
126126 const QgsRendererCategory category = mRenderer ->categories ().value ( index.row () );
127127
128- if ( role == Qt::CheckStateRole && index. column () == 0 )
128+ switch ( role )
129129 {
130- return category.renderState () ? Qt::Checked : Qt::Unchecked;
131- }
132- else if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
133- {
134- switch ( index.column () )
130+ case Qt::CheckStateRole:
135131 {
136- case 1 :
137- return category.value ().toString ();
138- case 2 :
139- return category.label ();
140- default :
141- return QVariant ();
132+ if ( index.column () == 0 )
133+ {
134+ return category.renderState () ? Qt::Checked : Qt::Unchecked;
135+ }
136+ break ;
142137 }
143- }
144- else if ( role == Qt::DecorationRole && index.column () == 0 && category.symbol () )
145- {
146- return QgsSymbolLayerUtils::symbolPreviewIcon ( category.symbol (), QSize ( 16 , 16 ) );
147- }
148- else if ( role == Qt::TextAlignmentRole )
149- {
150- return ( index.column () == 0 ) ? Qt::AlignHCenter : Qt::AlignLeft;
151- }
152- else if ( role == Qt::EditRole )
153- {
154- switch ( index.column () )
138+
139+ case Qt::DisplayRole:
140+ case Qt::ToolTipRole:
141+ {
142+ switch ( index.column () )
143+ {
144+ case 1 :
145+ {
146+ if ( category.value ().type () == QVariant::List )
147+ {
148+ QStringList res;
149+ const QVariantList list = category.value ().toList ();
150+ res.reserve ( list.size () );
151+ for ( const QVariant &v : list )
152+ res << v.toString ();
153+
154+ return res.join ( ' ;' );
155+ }
156+ else
157+ {
158+ return category.value ().toString ();
159+ }
160+ }
161+ case 2 :
162+ return category.label ();
163+ }
164+ break ;
165+ }
166+
167+ case Qt::DecorationRole:
168+ {
169+ if ( index.column () == 0 && category.symbol () )
170+ {
171+ return QgsSymbolLayerUtils::symbolPreviewIcon ( category.symbol (), QSize ( 16 , 16 ) );
172+ }
173+ break ;
174+ }
175+
176+ case Qt::ForegroundRole:
155177 {
156- case 1 :
157- return category.value ();
158- case 2 :
159- return category.label ();
160- default :
161- return QVariant ();
178+ QBrush brush ( qApp->palette ().color ( QPalette::Text ), Qt::SolidPattern );
179+ if ( index.column () == 1 && category.value ().type () == QVariant::List )
180+ {
181+ QColor fadedTextColor = brush.color ();
182+ fadedTextColor.setAlpha ( 128 );
183+ brush.setColor ( fadedTextColor );
184+ }
185+ return brush;
186+ }
187+
188+ case Qt::TextAlignmentRole:
189+ {
190+ return ( index.column () == 0 ) ? Qt::AlignHCenter : Qt::AlignLeft;
191+ }
192+
193+ case Qt::EditRole:
194+ {
195+ switch ( index.column () )
196+ {
197+ case 1 :
198+ {
199+ if ( category.value ().type () == QVariant::List )
200+ {
201+ QStringList res;
202+ const QVariantList list = category.value ().toList ();
203+ res.reserve ( list.size () );
204+ for ( const QVariant &v : list )
205+ res << v.toString ();
206+
207+ return res.join ( ' ;' );
208+ }
209+ else
210+ {
211+ return category.value ();
212+ }
213+ }
214+
215+ case 2 :
216+ return category.label ();
217+ }
218+ break ;
162219 }
163220 }
164221
@@ -194,6 +251,20 @@ bool QgsCategorizedSymbolRendererModel::setData( const QModelIndex &index, const
194251 case QVariant::Double:
195252 val = value.toDouble ();
196253 break ;
254+ case QVariant::List:
255+ {
256+ const QStringList parts = value.toString ().split ( ' ;' );
257+ QVariantList list;
258+ list.reserve ( parts.count () );
259+ for ( const QString &p : parts )
260+ list << p;
261+
262+ if ( list.count () == 1 )
263+ val = list.at ( 0 );
264+ else
265+ val = list;
266+ break ;
267+ }
197268 default :
198269 val = value.toString ();
199270 break ;
@@ -392,7 +463,7 @@ QgsRendererWidget *QgsCategorizedSymbolRendererWidget::create( QgsVectorLayer *l
392463
393464QgsCategorizedSymbolRendererWidget::QgsCategorizedSymbolRendererWidget ( QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer )
394465 : QgsRendererWidget( layer, style )
395-
466+ , mContextMenu( new QMenu( this ) )
396467{
397468
398469 // try to recognize the previous renderer
@@ -450,7 +521,7 @@ QgsCategorizedSymbolRendererWidget::QgsCategorizedSymbolRendererWidget( QgsVecto
450521 connect ( mExpressionWidget , static_cast < void ( QgsFieldExpressionWidget::* )( const QString & ) >( &QgsFieldExpressionWidget::fieldChanged ), this , &QgsCategorizedSymbolRendererWidget::categoryColumnChanged );
451522
452523 connect ( viewCategories, &QAbstractItemView::doubleClicked, this , &QgsCategorizedSymbolRendererWidget::categoriesDoubleClicked );
453- connect ( viewCategories, &QTreeView::customContextMenuRequested, this , &QgsCategorizedSymbolRendererWidget::contextMenuViewCategories );
524+ connect ( viewCategories, &QTreeView::customContextMenuRequested, this , &QgsCategorizedSymbolRendererWidget::showContextMenu );
454525
455526 connect ( btnChangeCategorizedSymbol, &QAbstractButton::clicked, this , &QgsCategorizedSymbolRendererWidget::changeCategorizedSymbol );
456527 connect ( btnAddCategories, &QAbstractButton::clicked, this , &QgsCategorizedSymbolRendererWidget::addCategories );
@@ -476,6 +547,9 @@ QgsCategorizedSymbolRendererWidget::QgsCategorizedSymbolRendererWidget( QgsVecto
476547 btnAdvanced->setMenu ( advMenu );
477548
478549 mExpressionWidget ->registerExpressionContextGenerator ( this );
550+
551+ mMergeCategoriesAction = new QAction ( tr ( " Merge Categories" ), this );
552+ connect ( mMergeCategoriesAction , &QAction::triggered, this , &QgsCategorizedSymbolRendererWidget::mergeClicked );
479553}
480554
481555QgsCategorizedSymbolRendererWidget::~QgsCategorizedSymbolRendererWidget ()
@@ -720,11 +794,28 @@ void QgsCategorizedSymbolRendererWidget::addCategories()
720794 QVariant value = cats.at ( i ).value ();
721795 for ( int j = 0 ; j < prevCats.size () && !contains; ++j )
722796 {
723- if ( prevCats.at ( j ).value () == value )
797+ const QVariant prevCatValue = prevCats.at ( j ).value ();
798+ if ( prevCatValue.type () == QVariant::List )
724799 {
725- contains = true ;
726- break ;
800+ const QVariantList list = prevCatValue.toList ();
801+ for ( const QVariant &v : list )
802+ {
803+ if ( v == value )
804+ {
805+ contains = true ;
806+ break ;
807+ }
808+ }
809+ }
810+ else
811+ {
812+ if ( prevCats.at ( j ).value () == value )
813+ {
814+ contains = true ;
815+ }
727816 }
817+ if ( contains )
818+ break ;
728819 }
729820
730821 if ( !contains )
@@ -1061,3 +1152,57 @@ void QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend()
10611152 openPanel ( panel ); // takes ownership of the panel
10621153 }
10631154}
1155+
1156+ void QgsCategorizedSymbolRendererWidget::mergeClicked ()
1157+ {
1158+ QList<int > categoryIndexes = selectedCategories ();
1159+ if ( categoryIndexes.count () < 2 )
1160+ return ;
1161+
1162+ const QgsCategoryList &categories = mRenderer ->categories ();
1163+
1164+ QStringList labels;
1165+ QVariantList values;
1166+ values.reserve ( categoryIndexes.count () );
1167+ labels.reserve ( categoryIndexes.count () );
1168+ for ( int i : categoryIndexes )
1169+ {
1170+ QVariant v = categories.at ( i ).value ();
1171+ if ( v.type () == QVariant::List )
1172+ {
1173+ values.append ( v.toList () );
1174+ }
1175+ else
1176+ values << v;
1177+
1178+ labels << categories.at ( i ).label ();
1179+ }
1180+
1181+ // modify first category (basically we "merge up" into the first selected category)
1182+ mRenderer ->updateCategoryLabel ( categoryIndexes.at ( 0 ), labels.join ( ' ,' ) );
1183+ mRenderer ->updateCategoryValue ( categoryIndexes.at ( 0 ), values );
1184+
1185+ categoryIndexes.pop_front ();
1186+ mModel ->deleteRows ( categoryIndexes );
1187+
1188+ emit widgetChanged ();
1189+ }
1190+
1191+ void QgsCategorizedSymbolRendererWidget::showContextMenu ( QPoint )
1192+ {
1193+ mContextMenu ->clear ();
1194+ const QList< QAction * > actions = contextMenu->actions ();
1195+ for ( QAction *act : actions )
1196+ {
1197+ mContextMenu ->addAction ( act );
1198+ }
1199+
1200+ mContextMenu ->addSeparator ();
1201+
1202+ if ( viewCategories->selectionModel ()->selectedRows ().count () > 1 )
1203+ {
1204+ mContextMenu ->addAction ( mMergeCategoriesAction );
1205+ }
1206+
1207+ mContextMenu ->exec ( QCursor::pos () );
1208+ }
0 commit comments