45#include <QStandardItemModel> 
   46#include <QStandardItem> 
   56QgsCategorizedSymbolRendererModel::QgsCategorizedSymbolRendererModel( QObject *parent, QScreen *screen )
 
   57  : QAbstractItemModel( parent )
 
   58  , 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 );
 
  107Qt::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;
 
  130Qt::DropActions QgsCategorizedSymbolRendererModel::supportedDropActions()
 const 
  132  return Qt::MoveAction;
 
  135QVariant 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' );
 
  175            return tr( 
"all other values" );
 
  183          return category.
label();
 
  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
 
  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();
 
  265bool 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 );
 
  330QVariant 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 );
 
  341int QgsCategorizedSymbolRendererModel::rowCount( 
const QModelIndex &parent )
 const 
  343  if ( parent.isValid() || !mRenderer )
 
  347  return mRenderer->categories().size();
 
  350int QgsCategorizedSymbolRendererModel::columnCount( 
const QModelIndex &index )
 const 
  356QModelIndex QgsCategorizedSymbolRendererModel::index( 
int row, 
int column, 
const QModelIndex &parent )
 const 
  358  if ( hasIndex( row, column, parent ) )
 
  360    return createIndex( row, column );
 
  362  return QModelIndex();
 
  365QModelIndex QgsCategorizedSymbolRendererModel::parent( 
const QModelIndex &index )
 const 
  368  return QModelIndex();
 
  371QStringList QgsCategorizedSymbolRendererModel::mimeTypes()
 const 
  374  types << mMimeFormat;
 
  378QMimeData *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 );
 
  398bool 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    QgsDebugMsgLevel( QStringLiteral( 
"move %1 to %2" ).arg( rows[i] ).arg( to ), 2 );
 
  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 ) );
 
  441void 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] );
 
  452void QgsCategorizedSymbolRendererModel::removeAllRows()
 
  454  beginRemoveRows( QModelIndex(), 0, mRenderer->categories().size() - 1 );
 
  455  mRenderer->deleteAllCategories();
 
  459void 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 ) );
 
  476void QgsCategorizedSymbolRendererModel::updateSymbology()
 
  478  emit dataChanged( createIndex( 0, 0 ), createIndex( mRenderer->categories().size(), 0 ) );
 
  482QgsCategorizedSymbolRendererViewStyle::QgsCategorizedSymbolRendererViewStyle( QWidget *parent )
 
  486void 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 );
 
  502QgsCategorizedRendererViewItemDelegate::QgsCategorizedRendererViewItemDelegate( 
QgsFieldExpressionWidget *expressionWidget, QObject *parent )
 
  503  : QStyledItemDelegate( parent )
 
  504  , mFieldExpressionWidget( expressionWidget )
 
  508QWidget *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 && 
QgsVariantUtils::isNull( index.data( QgsCategorizedSymbolRendererWidget::CustomRoles::ValueRole ) ) )
 
  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 );
 
  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, screen() );
 
  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 );
 
  764    btnColorRamp->setColorRamp( 
mRenderer->sourceColorRamp() );
 
  782  delete mActionLevels;
 
  783  mActionLevels = 
nullptr;
 
  790  if ( !selectedCats.isEmpty() )
 
  801    const auto constSelectedCats = selectedCats;
 
  802    for ( 
const int idx : constSelectedCats )
 
  808      mRenderer->updateCategorySymbol( idx, newCatSymbol );
 
  831    if ( !dlg.exec() || !newSymbol )
 
  854  if ( idx.isValid() && idx.column() == 0 )
 
  862  std::unique_ptr< QgsSymbol > symbol;
 
  864  if ( 
auto *lSymbol = category.
symbol() )
 
  866    symbol.reset( lSymbol->clone() );
 
  887    if ( !dlg.exec() || !symbol )
 
  900  const QString attrName = mExpressionWidget->currentField();
 
  902  QList<QVariant> uniqueValues;
 
  920      if ( uniqueValues.contains( value ) )
 
  922      uniqueValues << value;
 
  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 &&
 
  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 );
 
 1041  std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
 
 1043    r->setSourceColorRamp( ramp->clone() );
 
 1047    mModel->setRenderer( r.get() );
 
 1050  if ( ! keepExistingColors && ramp )
 
 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() );
 
 1091  mModel->deleteRows( categoryIndexes );
 
 1106  mModel->addCategory( cat );
 
 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();
 
 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 ) );
 
 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." ) );
 
 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() ) );
 
 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();
 
 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() );
 
 1277void QgsCategorizedSymbolRendererWidget::cleanUpSymbolSelector( 
QgsPanelWidget *container )
 
 1286void QgsCategorizedSymbolRendererWidget::updateSymbolsFromWidget()
 
 1294void QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton()
 
 1304  QItemSelectionModel *m = viewCategories->selectionModel();
 
 1305  const QModelIndexList i = m->selectedRows();
 
 1311    if ( !selectedCats.isEmpty() )
 
 1313      const auto constSelectedCats = selectedCats;
 
 1314      for ( 
const int idx : constSelectedCats )
 
 1317        if ( selectedCats.count() > 1 )
 
 1322        mRenderer->updateCategorySymbol( idx, newCatSymbol );
 
 1331  mModel->updateSymbology();
 
 1342  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
 
 1344    mCopyBuffer.clear();
 
 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 );
 
 1370      expContext << generator->createExpressionContextScope();
 
 1391void QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend()
 
 1406void QgsCategorizedSymbolRendererWidget::mergeSelectedCategories()
 
 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 );
 
 1457void QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories()
 
 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() );
 
 1482void 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 )
 
 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() );
 
 1513void QgsCategorizedSymbolRendererWidget::selectionChanged( 
const QItemSelection &, 
const QItemSelection & )
 
 1516  if ( !selectedCats.isEmpty() )
 
 1518    whileBlocking( btnChangeCategorizedSymbol )->setSymbol( 
mRenderer->categories().at( selectedCats.at( 0 ) ).symbol()->clone() );
 
 1524  btnChangeCategorizedSymbol->setDialogTitle( selectedCats.size() == 1 ? 
mRenderer->categories().at( selectedCats.at( 0 ) ).label() : tr( 
"Symbol Settings" ) );
 
SymbolType
Attribute editing capabilities which may be supported by vector data providers.
 
const QgsCategoryList & categories() const
Returns a list of all categories recognized by the renderer.
 
static QgsCategorizedSymbolRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer, QgsVectorLayer *layer=nullptr)
Creates a new QgsCategorizedSymbolRenderer from an existing renderer.
 
static QgsCategoryList createCategories(const QVariantList &values, const QgsSymbol *symbol, QgsVectorLayer *layer=nullptr, const QString &fieldName=QString())
Create categories for a list of values.
 
static QString displayString(const QVariant &value, int precision=-1)
Returns a localized representation of value with the given precision, if precision is -1 then precisi...
 
The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value.
 
void setClearValue(double customValue, const QString &clearValueText=QString())
Defines the clear value as a custom value and will automatically set the clear value mode to CustomVa...
 
Abstract interface for generating an expression context scope.
 
Single scope for storing variables and functions for use within a QgsExpressionContext.
 
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
 
static QgsExpressionContextScope * atlasScope(const QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
 
static QgsExpressionContextScope * mapSettingsScope(const QgsMapSettings &mapSettings)
Creates a new scope which contains variables and functions relating to a QgsMapSettings object.
 
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
 
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
 
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
 
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
 
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
 
Class for parsing and evaluation of expressions (formerly called "search strings").
 
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
 
QString expression() const
Returns the original, unmodified expression string.
 
QVariant evaluate()
Evaluate the feature and return the result.
 
Wrapper for iterator of features from vector data provider or vector layer.
 
bool nextFeature(QgsFeature &f)
 
void copyRendererData(QgsFeatureRenderer *destRenderer) const
Clones generic renderer data to another renderer.
 
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
 
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
 
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
 
The QgsMapSettings class contains configuration for rendering of the map.
 
A marker symbol type, for rendering Point and MultiPoint geometries.
 
static QgsProject * instance()
Returns the QgsProject singleton instance.
 
A QProxyStyle subclass which correctly sets the base style to match the QGIS application style,...
 
Totally random color ramp.
 
virtual void setTotalColorCount(int colorCount)
Sets the desired total number of unique colors for the resultant ramp.
 
QColor color(double value) const override
Returns the color corresponding to a specified value.
 
Represents an individual category (class) from a QgsCategorizedSymbolRenderer.
 
QgsSymbol * symbol() const
Returns the symbol which will be used to render this category.
 
bool renderState() const
Returns true if the category is currently enabled and should be rendered.
 
QVariant value() const
Returns the value corresponding to this category.
 
QString label() const
Returns the label for this category, which is used to represent the category within legends and the l...
 
Stores properties relating to a screen.
 
This class is a composition of two QSettings instances:
 
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
 
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
 
QString errorString() const
Returns the last error from a load() operation.
 
static QgsStyle * defaultStyle()
Returns default application-wide style.
 
bool importXml(const QString &filename)
Imports the symbols and colorramps into the default style database from the given XML file.
 
static QgsSymbol * symbolFromMimeData(const QMimeData *data)
Attempts to parse mime data as a symbol.
 
static QIcon symbolPreviewIcon(const QgsSymbol *symbol, QSize size, int padding=0, QgsLegendPatchShape *shape=nullptr, const QgsScreenProperties &screen=QgsScreenProperties())
Returns an icon preview for a color ramp.
 
void setContext(const QgsSymbolWidgetContext &context)
Sets the context in which the symbol widget is shown, e.g., the associated map canvas and expression ...
 
Contains settings which reflect the context in which a symbol (or renderer) widget is shown,...
 
QList< QgsExpressionContextScope > additionalExpressionContextScopes() const
Returns the list of additional expression context scopes to show as available within the layer.
 
QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the widget.
 
QgsMessageBar * messageBar() const
Returns the message bar associated with the widget.
 
Abstract base class for all rendered symbols.
 
void setColor(const QColor &color) const
Sets the color for the symbol.
 
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
 
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
 
static QgsSymbol * defaultSymbol(Qgis::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
 
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
 
Represents a vector layer which manages a vector based data sets.
 
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
 
QgsFields fields() const FINAL
Returns the list of fields of this layer.
 
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
 
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
 
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
 
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly,...
 
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
 
QList< QgsRendererCategory > QgsCategoryList
 
QList< QgsLegendSymbolItem > QgsLegendSymbolList
 
#define QgsDebugMsgLevel(str, level)