48 #include <QMessageBox>
49 #include <QStandardItemModel>
50 #include <QStandardItem>
53 #include <QFileDialog>
58 QgsCategorizedSymbolRendererModel::QgsCategorizedSymbolRendererModel( QObject *parent ) : QAbstractItemModel( parent )
59 , mMimeFormat( QStringLiteral(
"application/x-qgscategorizedsymbolrendererv2model" ) )
67 beginRemoveRows( QModelIndex(), 0, std::max< int >( mRenderer->categories().size() - 1, 0 ) );
76 beginInsertRows( QModelIndex(), 0, renderer->
categories().size() - 1 );
84 if ( !mRenderer )
return;
85 const int idx = mRenderer->categories().size();
86 beginInsertRows( QModelIndex(), idx, idx );
87 mRenderer->addCategory( cat );
98 const int row = index.row();
99 if ( row >= catList.size() )
103 return catList.at( row );
107 Qt::ItemFlags QgsCategorizedSymbolRendererModel::flags(
const QModelIndex &index )
const
109 if ( !index.isValid() || !mRenderer )
111 return Qt::ItemIsDropEnabled;
114 Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable;
115 if ( index.column() == 1 )
118 if ( category.
value().type() != QVariant::List )
120 flags |= Qt::ItemIsEditable;
123 else if ( index.column() == 2 )
125 flags |= Qt::ItemIsEditable;
130 Qt::DropActions QgsCategorizedSymbolRendererModel::supportedDropActions()
const
132 return Qt::MoveAction;
135 QVariant QgsCategorizedSymbolRendererModel::data(
const QModelIndex &index,
int role )
const
137 if ( !index.isValid() || !mRenderer )
144 case Qt::CheckStateRole:
146 if ( index.column() == 0 )
148 return category.
renderState() ? Qt::Checked : Qt::Unchecked;
153 case Qt::DisplayRole:
154 case Qt::ToolTipRole:
156 switch ( index.column() )
160 if ( category.
value().type() == QVariant::List )
163 const QVariantList list = category.
value().toList();
164 res.reserve( list.size() );
165 for (
const QVariant &v : list )
168 if ( role == Qt::DisplayRole )
169 return res.join(
';' );
171 return res.join(
'\n' );
173 else if ( !category.
value().isValid() || category.
value().isNull() || category.
value().toString().isEmpty() )
175 return tr(
"all other values" );
183 return category.
label();
190 if ( index.column() == 1 && category.
value().type() != QVariant::List && ( !category.
value().isValid() || category.
value().isNull() || category.
value().toString().isEmpty() ) )
193 italicFont.setItalic(
true );
199 case Qt::DecorationRole:
201 if ( index.column() == 0 && category.
symbol() )
209 case Qt::ForegroundRole:
211 QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
212 if ( index.column() == 1 && ( category.
value().type() == QVariant::List
213 || !category.
value().isValid() || category.
value().isNull() || category.
value().toString().isEmpty() ) )
215 QColor fadedTextColor = brush.color();
216 fadedTextColor.setAlpha( 128 );
217 brush.setColor( fadedTextColor );
222 case Qt::TextAlignmentRole:
224 return ( index.column() == 0 ) ?
static_cast<Qt::Alignment::Int
>( Qt::AlignHCenter ) :
static_cast<Qt::Alignment::Int
>( Qt::AlignLeft );
229 switch ( index.column() )
233 if ( category.
value().type() == QVariant::List )
236 const QVariantList list = category.
value().toList();
237 res.reserve( list.size() );
238 for (
const QVariant &v : list )
241 return res.join(
';' );
245 return category.
value();
250 return category.
label();
254 case QgsCategorizedSymbolRendererWidget::CustomRoles::ValueRole:
256 if ( index.column() == 1 )
257 return category.
value();
265 bool QgsCategorizedSymbolRendererModel::setData(
const QModelIndex &index,
const QVariant &value,
int role )
267 if ( !index.isValid() )
270 if ( index.column() == 0 && role == Qt::CheckStateRole )
272 mRenderer->updateCategoryRenderState( index.row(), value == Qt::Checked );
273 emit dataChanged( index, index );
277 if ( role != Qt::EditRole )
280 switch ( index.column() )
285 QVariant val = value;
286 const QVariant previousValue = mRenderer->categories().value( index.row() ).value();
287 if ( previousValue.type() != QVariant::String && ! previousValue.toString().isEmpty() )
289 switch ( previousValue.type() )
294 case QVariant::Double:
295 val = value.toDouble();
299 const QStringList parts = value.toString().split(
';' );
301 list.reserve( parts.count() );
302 for (
const QString &p : parts )
305 if ( list.count() == 1 )
312 val = value.toString();
316 mRenderer->updateCategoryValue( index.row(), val );
320 mRenderer->updateCategoryLabel( index.row(), value.toString() );
326 emit dataChanged( index, index );
330 QVariant QgsCategorizedSymbolRendererModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
332 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= 0 && section < 3 )
335 lst << tr(
"Symbol" ) << tr(
"Value" ) << tr(
"Legend" );
336 return lst.value( section );
341 int QgsCategorizedSymbolRendererModel::rowCount(
const QModelIndex &parent )
const
343 if ( parent.isValid() || !mRenderer )
347 return mRenderer->categories().size();
350 int QgsCategorizedSymbolRendererModel::columnCount(
const QModelIndex &index )
const
356 QModelIndex QgsCategorizedSymbolRendererModel::index(
int row,
int column,
const QModelIndex &parent )
const
358 if ( hasIndex( row, column, parent ) )
360 return createIndex( row, column );
362 return QModelIndex();
365 QModelIndex QgsCategorizedSymbolRendererModel::parent(
const QModelIndex &index )
const
368 return QModelIndex();
371 QStringList QgsCategorizedSymbolRendererModel::mimeTypes()
const
374 types << mMimeFormat;
378 QMimeData *QgsCategorizedSymbolRendererModel::mimeData(
const QModelIndexList &indexes )
const
380 QMimeData *mimeData =
new QMimeData();
381 QByteArray encodedData;
383 QDataStream stream( &encodedData, QIODevice::WriteOnly );
386 const auto constIndexes = indexes;
387 for (
const QModelIndex &index : constIndexes )
389 if ( !index.isValid() || index.column() != 0 )
392 stream << index.row();
394 mimeData->setData( mMimeFormat, encodedData );
398 bool QgsCategorizedSymbolRendererModel::dropMimeData(
const QMimeData *data, Qt::DropAction action,
int row,
int column,
const QModelIndex &parent )
402 if ( action != Qt::MoveAction )
return true;
404 if ( !data->hasFormat( mMimeFormat ) )
return false;
406 QByteArray encodedData = data->data( mMimeFormat );
407 QDataStream stream( &encodedData, QIODevice::ReadOnly );
410 while ( !stream.atEnd() )
417 int to = parent.row();
420 if ( to == -1 ) to = mRenderer->categories().size();
421 for (
int i = rows.size() - 1; i >= 0; i-- )
423 QgsDebugMsg( QStringLiteral(
"move %1 to %2" ).arg( rows[i] ).arg( to ) );
426 if ( rows[i] < t ) t--;
427 mRenderer->moveCategory( rows[i], t );
429 for (
int j = 0; j < i; j++ )
431 if ( to < rows[j] && rows[i] > rows[j] ) rows[j] += 1;
434 if ( rows[i] < to ) to--;
436 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
441 void QgsCategorizedSymbolRendererModel::deleteRows( QList<int> rows )
443 std::sort( rows.begin(), rows.end() );
444 for (
int i = rows.size() - 1; i >= 0; i-- )
446 beginRemoveRows( QModelIndex(), rows[i], rows[i] );
447 mRenderer->deleteCategory( rows[i] );
452 void QgsCategorizedSymbolRendererModel::removeAllRows()
454 beginRemoveRows( QModelIndex(), 0, mRenderer->categories().size() - 1 );
455 mRenderer->deleteAllCategories();
459 void QgsCategorizedSymbolRendererModel::sort(
int column, Qt::SortOrder order )
467 mRenderer->sortByValue( order );
469 else if ( column == 2 )
471 mRenderer->sortByLabel( order );
473 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
476 void QgsCategorizedSymbolRendererModel::updateSymbology()
478 emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
482 QgsCategorizedSymbolRendererViewStyle::QgsCategorizedSymbolRendererViewStyle( QWidget *parent )
486 void QgsCategorizedSymbolRendererViewStyle::drawPrimitive( PrimitiveElement element,
const QStyleOption *option, QPainter *painter,
const QWidget *widget )
const
488 if ( element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull() )
490 QStyleOption opt( *option );
491 opt.rect.setLeft( 0 );
493 opt.rect.setHeight( 0 );
494 if ( widget ) opt.rect.setRight( widget->width() );
495 QProxyStyle::drawPrimitive( element, &opt, painter, widget );
498 QProxyStyle::drawPrimitive( element, option, painter, widget );
502 QgsCategorizedRendererViewItemDelegate::QgsCategorizedRendererViewItemDelegate(
QgsFieldExpressionWidget *expressionWidget, QObject *parent )
503 : QStyledItemDelegate( parent )
504 , mFieldExpressionWidget( expressionWidget )
508 QWidget *QgsCategorizedRendererViewItemDelegate::createEditor( QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
510 QVariant::Type userType { index.data( QgsCategorizedSymbolRendererWidget::CustomRoles::ValueRole ).type() };
513 if ( userType == QVariant::String && index.data( QgsCategorizedSymbolRendererWidget::CustomRoles::ValueRole ).isNull() )
517 const QString fieldName { mFieldExpressionWidget->currentField( &isExpression, &isValid ) };
518 if ( ! fieldName.isEmpty() && mFieldExpressionWidget->layer() && mFieldExpressionWidget->layer()->fields().lookupField( fieldName ) != -1 )
520 userType = mFieldExpressionWidget->layer()->fields().field( fieldName ).type();
522 else if ( isExpression && isValid )
526 if ( mFieldExpressionWidget->layer()->getFeatures().nextFeature( feat ) )
531 expressionContext.
appendScope( mFieldExpressionWidget->layer()->createExpressionContextScope() );
534 const QVariant value = exp.evaluate( &expressionContext );
535 if ( !exp.hasEvalError() )
537 userType = value.type();
546 case QVariant::Type::Double:
550 const QVariant value = index.data( QgsCategorizedSymbolRendererWidget::CustomRoles::ValueRole );
552 if ( value.toDouble( &ok ); ok )
554 const QString strVal { value.toString() };
555 const int dotPosition( strVal.indexOf(
'.' ) );
556 if ( dotPosition >= 0 )
558 decimals = std::max<int>( 2, strVal.length() - dotPosition - 1 );
561 editor->setDecimals( decimals );
563 editor->setMaximum( std::numeric_limits<double>::max() );
564 editor->setMinimum( std::numeric_limits<double>::lowest() );
567 case QVariant::Type::Int:
570 editor->setDecimals( 0 );
572 editor->setMaximum( std::numeric_limits<int>::max() );
573 editor->setMinimum( std::numeric_limits<int>::min() );
576 case QVariant::Type::Char:
579 editor->setDecimals( 0 );
581 editor->setMaximum( std::numeric_limits<char>::max() );
582 editor->setMinimum( std::numeric_limits<char>::min() );
585 case QVariant::Type::UInt:
588 editor->setDecimals( 0 );
590 editor->setMaximum( std::numeric_limits<unsigned int>::max() );
591 editor->setMinimum( 0 );
594 case QVariant::Type::LongLong:
597 editor->setDecimals( 0 );
599 editor->setMaximum(
static_cast<double>( std::numeric_limits<qlonglong>::max() ) );
600 editor->setMinimum( std::numeric_limits<qlonglong>::min() );
603 case QVariant::Type::ULongLong:
606 editor->setDecimals( 0 );
608 editor->setMaximum(
static_cast<double>( std::numeric_limits<unsigned long long>::max() ) );
609 editor->setMinimum( 0 );
615 return editor ? editor : QStyledItemDelegate::createEditor( parent, option, index );
623 return new QgsCategorizedSymbolRendererWidget( layer, style, renderer );
628 , mContextMenu( new QMenu( this ) )
644 const QString attrName =
mRenderer->classAttribute();
645 mOldClassificationAttribute = attrName;
649 layout()->setContentsMargins( 0, 0, 0, 0 );
651 mExpressionWidget->setLayer(
mLayer );
652 btnChangeCategorizedSymbol->setLayer(
mLayer );
653 btnChangeCategorizedSymbol->registerExpressionContextGenerator(
this );
656 btnColorRamp->setShowRandomColorRamp(
true );
659 std::unique_ptr< QgsColorRamp > colorRamp(
QgsProject::instance()->styleSettings()->defaultColorRamp() );
662 btnColorRamp->setColorRamp( colorRamp.get() );
666 btnColorRamp->setRandomColorRamp();
676 mModel =
new QgsCategorizedSymbolRendererModel(
this );
682 viewCategories->setModel(
mModel );
683 viewCategories->resizeColumnToContents( 0 );
684 viewCategories->resizeColumnToContents( 1 );
685 viewCategories->resizeColumnToContents( 2 );
686 viewCategories->setItemDelegateForColumn( 1,
new QgsCategorizedRendererViewItemDelegate( mExpressionWidget, viewCategories ) );
688 viewCategories->setStyle(
new QgsCategorizedSymbolRendererViewStyle( viewCategories ) );
689 connect( viewCategories->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &QgsCategorizedSymbolRendererWidget::selectionChanged );
697 connect( viewCategories, &QTreeView::customContextMenuRequested,
this, &QgsCategorizedSymbolRendererWidget::showContextMenu );
699 connect( btnChangeCategorizedSymbol, &
QgsSymbolButton::changed,
this, &QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton );
709 QMenu *advMenu =
new QMenu;
716 QAction *actionDdsLegend = advMenu->addAction( tr(
"Data-defined Size Legend…" ) );
718 connect( actionDdsLegend, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend );
721 btnAdvanced->setMenu( advMenu );
723 mExpressionWidget->registerExpressionContextGenerator(
this );
725 mMergeCategoriesAction =
new QAction( tr(
"Merge Categories" ),
this );
726 connect( mMergeCategoriesAction, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::mergeSelectedCategories );
727 mUnmergeCategoriesAction =
new QAction( tr(
"Unmerge Categories" ),
this );
728 connect( mUnmergeCategoriesAction, &QAction::triggered,
this, &QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories );
730 connect( mContextMenu, &QMenu::aboutToShow,
this, [ = ]
751 const QString attrName = mRenderer->classAttribute();
752 mExpressionWidget->setField( attrName );
755 if ( mRenderer->sourceSymbol() )
757 mCategorizedSymbol.reset( mRenderer->sourceSymbol()->clone() );
758 whileBlocking( btnChangeCategorizedSymbol )->setSymbol( mCategorizedSymbol->clone() );
762 if ( mRenderer->sourceColorRamp() )
764 btnColorRamp->setColorRamp( mRenderer->sourceColorRamp() );
770 return mRenderer.get();
776 btnChangeCategorizedSymbol->setMapCanvas( context.
mapCanvas() );
777 btnChangeCategorizedSymbol->setMessageBar( context.
messageBar() );
782 delete mActionLevels;
783 mActionLevels =
nullptr;
788 const QList<int> selectedCats = selectedCategories();
790 if ( !selectedCats.isEmpty() )
801 const auto constSelectedCats = selectedCats;
802 for (
const int idx : constSelectedCats )
807 newCatSymbol->
setColor( mRenderer->categories()[idx].symbol()->color() );
808 mRenderer->updateCategorySymbol( idx, newCatSymbol );
816 std::unique_ptr<QgsSymbol> newSymbol( mCategorizedSymbol->clone() );
831 if ( !dlg.exec() || !newSymbol )
836 mCategorizedSymbol = std::move( newSymbol );
837 applyChangeToSymbol();
848 mRenderer->setClassAttribute(
field );
849 emit widgetChanged();
854 if ( idx.isValid() && idx.column() == 0 )
855 changeCategorySymbol();
862 std::unique_ptr< QgsSymbol > symbol;
864 if (
auto *lSymbol = category.
symbol() )
866 symbol.reset( lSymbol->clone() );
887 if ( !dlg.exec() || !symbol )
892 mCategorizedSymbol = std::move( symbol );
893 applyChangeToSymbol();
900 const QString attrName = mExpressionWidget->currentField();
901 const int idx = mLayer->fields().lookupField( attrName );
902 QList<QVariant> uniqueValues;
913 expression->
prepare( &context );
919 const QVariant value = expression->
evaluate( &context );
920 if ( uniqueValues.contains( value ) )
922 uniqueValues << value;
927 uniqueValues = qgis::setToList( mLayer->uniqueValues( idx ) );
931 if ( uniqueValues.size() >= 1000 )
933 const int res = QMessageBox::warning(
nullptr, tr(
"Classify Categories" ),
934 tr(
"High number of classes. Classification would yield %n entries which might not be expected. Continue?",
nullptr, uniqueValues.size() ),
935 QMessageBox::Ok | QMessageBox::Cancel,
936 QMessageBox::Cancel );
937 if ( res == QMessageBox::Cancel )
944 DlgAddCategories dlg( mStyle, createDefaultSymbol(), unique_vals,
this );
950 bool deleteExisting =
false;
952 if ( !mOldClassificationAttribute.isEmpty() &&
953 attrName != mOldClassificationAttribute &&
954 !mRenderer->categories().isEmpty() )
956 const int res = QMessageBox::question(
this,
957 tr(
"Delete Classification" ),
958 tr(
"The classification field was changed from '%1' to '%2'.\n"
959 "Should the existing classes be deleted before classification?" )
960 .arg( mOldClassificationAttribute, attrName ),
961 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel );
962 if ( res == QMessageBox::Cancel )
967 deleteExisting = ( res == QMessageBox::Yes );
971 bool keepExistingColors =
false;
972 if ( !deleteExisting )
975 keepExistingColors = !prevCats.isEmpty();
977 if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
979 for (
int i = 0; i < cats.size(); ++i )
981 bool contains =
false;
982 const QVariant value = cats.at( i ).value();
983 for (
int j = 0; j < prevCats.size() && !contains; ++j )
985 const QVariant prevCatValue = prevCats.at( j ).value();
986 if ( prevCatValue.type() == QVariant::List )
988 const QVariantList list = prevCatValue.toList();
989 for (
const QVariant &v : list )
1000 if ( prevCats.at( j ).value() == value )
1011 if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
1014 cats.at( i ).symbol()->setColor( randomColors.
color( i ) );
1016 prevCats.append( cats.at( i ) );
1022 mOldClassificationAttribute = attrName;
1039 std::unique_ptr< QgsCategorizedSymbolRenderer > r = std::make_unique< QgsCategorizedSymbolRenderer >( attrName, cats );
1040 r->setSourceSymbol( mCategorizedSymbol->clone() );
1041 std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
1043 r->setSourceColorRamp( ramp->clone() );
1047 mModel->setRenderer( r.get() );
1049 mRenderer = std::move( r );
1050 if ( ! keepExistingColors && ramp )
1052 emit widgetChanged();
1057 if ( !btnColorRamp->isNull() )
1059 mRenderer->updateColorRamp( btnColorRamp->colorRamp() );
1061 mModel->updateSymbology();
1066 const QModelIndex idx = viewCategories->selectionModel()->currentIndex();
1067 if ( !idx.isValid() )
1075 const QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows();
1077 const auto constSelectedRows = selectedRows;
1078 for (
const QModelIndex &r : constSelectedRows )
1082 rows.append( r.row() );
1090 const QList<int> categoryIndexes = selectedCategories();
1091 mModel->deleteRows( categoryIndexes );
1092 emit widgetChanged();
1097 mModel->removeAllRows();
1098 emit widgetChanged();
1103 if ( !mModel )
return;
1106 mModel->addCategory( cat );
1107 emit widgetChanged();
1112 QList<QgsSymbol *> selectedSymbols;
1114 QItemSelectionModel *m = viewCategories->selectionModel();
1115 const QModelIndexList selectedIndexes = m->selectedRows( 1 );
1117 if ( !selectedIndexes.isEmpty() )
1120 QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
1121 for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
1123 const int row = ( *indexIt ).row();
1124 QgsSymbol *s = categories[row].symbol();
1127 selectedSymbols.append( s );
1131 return selectedSymbols;
1138 QItemSelectionModel *m = viewCategories->selectionModel();
1139 const QModelIndexList selectedIndexes = m->selectedRows( 1 );
1141 if ( !selectedIndexes.isEmpty() )
1143 QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
1144 for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
1146 cl.append( mModel->category( *indexIt ) );
1154 populateCategories();
1155 emit widgetChanged();
1160 showSymbolLevelsDialog( mRenderer.get() );
1165 viewCategories->selectionModel()->clear();
1173 QMessageBox::information(
this, tr(
"Matched Symbols" ),
1174 tr(
"Matched %n categories to symbols.",
nullptr, matched ) );
1178 QMessageBox::warning(
this, tr(
"Matched Symbols" ),
1179 tr(
"No categories could be matched to symbols in library." ) );
1185 if ( !mLayer || !style )
1192 QVariantList unmatchedCategories;
1193 QStringList unmatchedSymbols;
1194 const int matched = mRenderer->matchToSymbols( style, type, unmatchedCategories, unmatchedSymbols );
1196 mModel->updateSymbology();
1203 const QString openFileDir = settings.
value( QStringLiteral(
"UI/lastMatchToSymbolsDir" ), QDir::homePath() ).toString();
1205 const QString fileName = QFileDialog::getOpenFileName(
this, tr(
"Match to Symbols from File" ), openFileDir,
1206 tr(
"XML files (*.xml *.XML)" ) );
1207 if ( fileName.isEmpty() )
1212 const QFileInfo openFileInfo( fileName );
1213 settings.
setValue( QStringLiteral(
"UI/lastMatchToSymbolsDir" ), openFileInfo.absolutePath() );
1216 if ( !importedStyle.
importXml( fileName ) )
1218 QMessageBox::warning(
this, tr(
"Match to Symbols from File" ),
1219 tr(
"An error occurred while reading file:\n%1" ).arg( importedStyle.
errorString() ) );
1223 const int matched = matchToSymbols( &importedStyle );
1226 QMessageBox::information(
this, tr(
"Match to Symbols from File" ),
1227 tr(
"Matched %n categories to symbols from file.",
nullptr, matched ) );
1231 QMessageBox::warning(
this, tr(
"Match to Symbols from File" ),
1232 tr(
"No categories could be matched to symbols in file." ) );
1243 mRenderer->setLegendSymbolItem( legendSymbol.ruleKey(), sym->
clone() );
1246 mRenderer->setUsingSymbolLevels( enabled );
1247 mModel->updateSymbology();
1248 emit widgetChanged();
1257 const QList<int> selectedCats = selectedCategories();
1258 if ( !selectedCats.isEmpty() )
1260 for (
const int idx : selectedCats )
1262 if ( mRenderer->categories().at( idx ).symbol()->type() != tempSymbol->type() )
1265 std::unique_ptr< QgsSymbol > newCatSymbol( tempSymbol->clone() );
1266 if ( selectedCats.count() > 1 )
1269 newCatSymbol->setColor( mRenderer->categories().at( idx ).symbol()->color() );
1271 mRenderer->updateCategorySymbol( idx, newCatSymbol.release() );
1273 emit widgetChanged();
1277 void QgsCategorizedSymbolRendererWidget::cleanUpSymbolSelector(
QgsPanelWidget *container )
1286 void QgsCategorizedSymbolRendererWidget::updateSymbolsFromWidget()
1289 mCategorizedSymbol.reset( dlg->
symbol()->
clone() );
1291 applyChangeToSymbol();
1294 void QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton()
1296 mCategorizedSymbol.reset( btnChangeCategorizedSymbol->symbol()->clone() );
1298 applyChangeToSymbol();
1304 QItemSelectionModel *m = viewCategories->selectionModel();
1305 const QModelIndexList i = m->selectedRows();
1309 const QList<int> selectedCats = selectedCategories();
1311 if ( !selectedCats.isEmpty() )
1313 const auto constSelectedCats = selectedCats;
1314 for (
const int idx : constSelectedCats )
1317 if ( selectedCats.count() > 1 )
1320 newCatSymbol->
setColor( mRenderer->categories().at( idx ).symbol()->color() );
1322 mRenderer->updateCategorySymbol( idx, newCatSymbol );
1328 mRenderer->updateSymbols( mCategorizedSymbol.get() );
1331 mModel->updateSymbology();
1332 emit widgetChanged();
1342 if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
1344 mCopyBuffer.clear();
1345 mCopyBuffer = selectedCategoryList();
1347 else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
1349 QgsCategoryList::const_iterator rIt = mCopyBuffer.constBegin();
1350 for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
1352 mModel->addCategory( *rIt );
1364 if (
auto *lMapCanvas = mContext.mapCanvas() )
1370 expContext << generator->createExpressionContextScope();
1378 if (
auto *lVectorLayer = vectorLayer() )
1382 const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
1391 void QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend()
1394 QgsDataDefinedSizeLegendWidget *panel = createDataDefinedSizeLegendWidget( s, mRenderer->dataDefinedSizeLegend() );
1399 mRenderer->setDataDefinedSizeLegend( panel->dataDefinedSizeLegend() );
1400 emit widgetChanged();
1406 void QgsCategorizedSymbolRendererWidget::mergeSelectedCategories()
1410 const QList<int> selectedCategoryIndexes = selectedCategories();
1411 QList< int > categoryIndexes;
1414 for (
const int i : selectedCategoryIndexes )
1416 const QVariant v = categories.at( i ).value();
1418 if ( !v.isValid() || v ==
"" )
1423 categoryIndexes.append( i );
1426 if ( categoryIndexes.count() < 2 )
1430 QVariantList values;
1431 values.reserve( categoryIndexes.count() );
1432 labels.reserve( categoryIndexes.count() );
1433 for (
const int i : categoryIndexes )
1435 const QVariant v = categories.at( i ).value();
1437 if ( v.type() == QVariant::List )
1439 values.append( v.toList() );
1444 labels << categories.at( i ).label();
1448 mRenderer->updateCategoryLabel( categoryIndexes.at( 0 ), labels.join(
',' ) );
1449 mRenderer->updateCategoryValue( categoryIndexes.at( 0 ), values );
1451 categoryIndexes.pop_front();
1452 mModel->deleteRows( categoryIndexes );
1454 emit widgetChanged();
1457 void QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories()
1459 const QList<int> categoryIndexes = selectedCategories();
1460 if ( categoryIndexes.isEmpty() )
1464 for (
const int i : categoryIndexes )
1466 const QVariant v = categories.at( i ).value();
1467 if ( v.type() != QVariant::List )
1470 const QVariantList list = v.toList();
1471 for (
int j = 1; j < list.count(); ++j )
1473 mModel->addCategory(
QgsRendererCategory( list.at( j ), categories.at( i ).symbol()->clone(), list.at( j ).toString(), categories.at( i ).renderState() ) );
1475 mRenderer->updateCategoryValue( i, list.at( 0 ) );
1476 mRenderer->updateCategoryLabel( i, list.at( 0 ).toString() );
1479 emit widgetChanged();
1482 void QgsCategorizedSymbolRendererWidget::showContextMenu( QPoint )
1484 mContextMenu->clear();
1485 const QList< QAction * > actions = contextMenu->actions();
1486 for ( QAction *act : actions )
1488 mContextMenu->addAction( act );
1491 mContextMenu->addSeparator();
1493 if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1495 mContextMenu->addAction( mMergeCategoriesAction );
1497 if ( viewCategories->selectionModel()->selectedRows().count() == 1 )
1499 const QList<int> categoryIndexes = selectedCategories();
1501 const QVariant v = categories.at( categoryIndexes.at( 0 ) ).value();
1502 if ( v.type() == QVariant::List )
1503 mContextMenu->addAction( mUnmergeCategoriesAction );
1505 else if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
1507 mContextMenu->addAction( mUnmergeCategoriesAction );
1510 mContextMenu->exec( QCursor::pos() );
1513 void QgsCategorizedSymbolRendererWidget::selectionChanged(
const QItemSelection &,
const QItemSelection & )
1515 const QList<int> selectedCats = selectedCategories();
1516 if ( !selectedCats.isEmpty() )
1518 whileBlocking( btnChangeCategorizedSymbol )->setSymbol( mRenderer->categories().at( selectedCats.at( 0 ) ).symbol()->clone() );
1520 else if ( mRenderer->sourceSymbol() )
1522 whileBlocking( btnChangeCategorizedSymbol )->setSymbol( mRenderer->sourceSymbol()->clone() );
1524 btnChangeCategorizedSymbol->setDialogTitle( selectedCats.size() == 1 ? mRenderer->categories().at( selectedCats.at( 0 ) ).label() : tr(
"Symbol Settings" ) );