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().userType() != QMetaType::Type::QVariantList )
 
  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().userType() == QMetaType::Type::QVariantList )
 
  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().userType() == QMetaType::Type::QVariantList
 
  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().userType() == QMetaType::Type::QVariantList )
 
  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();
 
  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.userType() != QMetaType::Type::QString && ! previousValue.toString().isEmpty() )
 
  289        switch ( previousValue.userType() )
 
  291          case QMetaType::Type::Int:
 
  294          case QMetaType::Type::Double:
 
  295            val = value.toDouble();
 
  297          case QMetaType::Type::QVariantList:
 
  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 
  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 = 
static_cast<QMetaType::Type
>( value.userType() );
 
  546    case QMetaType::Type::Double:
 
  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 QMetaType::Type::Int:
 
  570      editor->setDecimals( 0 );
 
  572      editor->setMaximum( std::numeric_limits<int>::max() );
 
  573      editor->setMinimum( std::numeric_limits<int>::min() );
 
  576    case QMetaType::Type::QChar:
 
  579      editor->setDecimals( 0 );
 
  581      editor->setMaximum( std::numeric_limits<char>::max() );
 
  582      editor->setMinimum( std::numeric_limits<char>::min() );
 
  585    case QMetaType::Type::UInt:
 
  588      editor->setDecimals( 0 );
 
  590      editor->setMaximum( std::numeric_limits<unsigned int>::max() );
 
  591      editor->setMinimum( 0 );
 
  594    case QMetaType::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 QMetaType::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 );
 
 
  828    if ( !dlg.exec() || !newSymbol )
 
 
  851  if ( idx.isValid() && idx.column() == 0 )
 
 
  859  std::unique_ptr< QgsSymbol > symbol;
 
  861  if ( 
auto *lSymbol = category.
symbol() )
 
  863    symbol.reset( lSymbol->clone() );
 
  883    if ( !dlg.exec() || !symbol )
 
 
  896  const QString attrName = mExpressionWidget->currentField();
 
  898  QList<QVariant> uniqueValues;
 
  916      if ( uniqueValues.contains( value ) )
 
  918      uniqueValues << value;
 
  927  if ( uniqueValues.size() >= 1000 )
 
  929    const int res = QMessageBox::warning( 
nullptr, tr( 
"Classify Categories" ),
 
  930                                          tr( 
"High number of classes. Classification would yield %n entries which might not be expected. Continue?", 
nullptr, uniqueValues.size() ),
 
  931                                          QMessageBox::Ok | QMessageBox::Cancel,
 
  932                                          QMessageBox::Cancel );
 
  933    if ( res == QMessageBox::Cancel )
 
  940  DlgAddCategories dlg( 
mStyle, createDefaultSymbol(), unique_vals, 
this );
 
  946  bool deleteExisting = 
false;
 
  948  if ( !mOldClassificationAttribute.isEmpty() &&
 
  949       attrName != mOldClassificationAttribute &&
 
  952    const int res = QMessageBox::question( 
this,
 
  953                                           tr( 
"Delete Classification" ),
 
  954                                           tr( 
"The classification field was changed from '%1' to '%2'.\n" 
  955                                               "Should the existing classes be deleted before classification?" )
 
  956                                           .arg( mOldClassificationAttribute, attrName ),
 
  957                                           QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel );
 
  958    if ( res == QMessageBox::Cancel )
 
  963    deleteExisting = ( res == QMessageBox::Yes );
 
  967  bool keepExistingColors = 
false;
 
  968  if ( !deleteExisting )
 
  971    keepExistingColors = !prevCats.isEmpty();
 
  973    if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
 
  975    for ( 
int i = 0; i < cats.size(); ++i )
 
  977      bool contains = 
false;
 
  978      const QVariant value = cats.at( i ).value();
 
  979      for ( 
int j = 0; j < prevCats.size() && !contains; ++j )
 
  981        const QVariant prevCatValue = prevCats.at( j ).value();
 
  982        if ( prevCatValue.userType() == QMetaType::Type::QVariantList )
 
  984          const QVariantList list = prevCatValue.toList();
 
  985          for ( 
const QVariant &v : list )
 
  996          if ( prevCats.at( j ).value() == value )
 
 1007        if ( keepExistingColors && btnColorRamp->isRandomColorRamp() )
 
 1010          cats.at( i ).symbol()->setColor( randomColors.
color( i ) );
 
 1012        prevCats.append( cats.at( i ) );
 
 1018  mOldClassificationAttribute = attrName;
 
 1035  std::unique_ptr< QgsCategorizedSymbolRenderer > r = std::make_unique< QgsCategorizedSymbolRenderer >( attrName, cats );
 
 1037  std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
 
 1039    r->setSourceColorRamp( ramp->clone() );
 
 1043    mModel->setRenderer( r.get() );
 
 1046  if ( ! keepExistingColors && ramp )
 
 
 1053  if ( !btnColorRamp->isNull() )
 
 1055    mRenderer->updateColorRamp( btnColorRamp->colorRamp() );
 
 1057  mModel->updateSymbology();
 
 
 1062  const QModelIndex idx = viewCategories->selectionModel()->currentIndex();
 
 1063  if ( !idx.isValid() )
 
 
 1071  const QModelIndexList selectedRows = viewCategories->selectionModel()->selectedRows();
 
 1073  const auto constSelectedRows = selectedRows;
 
 1074  for ( 
const QModelIndex &r : constSelectedRows )
 
 1078      rows.append( r.row() );
 
 
 1087  mModel->deleteRows( categoryIndexes );
 
 
 1102  mModel->addCategory( cat );
 
 
 1110  QItemSelectionModel *m = viewCategories->selectionModel();
 
 1111  const QModelIndexList selectedIndexes = m->selectedRows( 1 );
 
 1113  if ( !selectedIndexes.isEmpty() )
 
 1116    QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
 
 1117    for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
 
 1119      const int row = ( *indexIt ).row();
 
 1120      QgsSymbol *s = categories[row].symbol();
 
 
 1134  QItemSelectionModel *m = viewCategories->selectionModel();
 
 1135  const QModelIndexList selectedIndexes = m->selectedRows( 1 );
 
 1137  if ( !selectedIndexes.isEmpty() )
 
 1139    QModelIndexList::const_iterator indexIt = selectedIndexes.constBegin();
 
 1140    for ( ; indexIt != selectedIndexes.constEnd(); ++indexIt )
 
 1142      cl.append( 
mModel->category( *indexIt ) );
 
 
 1161  viewCategories->selectionModel()->clear();
 
 
 1169    QMessageBox::information( 
this, tr( 
"Matched Symbols" ),
 
 1170                              tr( 
"Matched %n categories to symbols.", 
nullptr, matched ) );
 
 1174    QMessageBox::warning( 
this, tr( 
"Matched Symbols" ),
 
 1175                          tr( 
"No categories could be matched to symbols in library." ) );
 
 
 1188  QVariantList unmatchedCategories;
 
 1189  QStringList unmatchedSymbols;
 
 1190  const int matched = 
mRenderer->matchToSymbols( style, type, unmatchedCategories, unmatchedSymbols );
 
 1192  mModel->updateSymbology();
 
 
 1199  const QString openFileDir = settings.
value( QStringLiteral( 
"UI/lastMatchToSymbolsDir" ), QDir::homePath() ).toString();
 
 1201  const QString fileName = QFileDialog::getOpenFileName( 
this, tr( 
"Match to Symbols from File" ), openFileDir,
 
 1202                           tr( 
"XML files (*.xml *.XML)" ) );
 
 1203  if ( fileName.isEmpty() )
 
 1208  const QFileInfo openFileInfo( fileName );
 
 1209  settings.
setValue( QStringLiteral( 
"UI/lastMatchToSymbolsDir" ), openFileInfo.absolutePath() );
 
 1212  if ( !importedStyle.
importXml( fileName ) )
 
 1214    QMessageBox::warning( 
this, tr( 
"Match to Symbols from File" ),
 
 1215                          tr( 
"An error occurred while reading file:\n%1" ).arg( importedStyle.
errorString() ) );
 
 1222    QMessageBox::information( 
this, tr( 
"Match to Symbols from File" ),
 
 1223                              tr( 
"Matched %n categories to symbols from file.", 
nullptr, matched ) );
 
 1227    QMessageBox::warning( 
this, tr( 
"Match to Symbols from File" ),
 
 1228                          tr( 
"No categories could be matched to symbols in file." ) );
 
 
 1239      mRenderer->setLegendSymbolItem( legendSymbol.ruleKey(), sym->
clone() );
 
 1242  mRenderer->setUsingSymbolLevels( enabled );
 
 1243  mModel->updateSymbology();
 
 
 1254  if ( !selectedCats.isEmpty() )
 
 1256    for ( 
const int idx : selectedCats )
 
 1258      if ( 
mRenderer->categories().at( idx ).symbol()->type() != tempSymbol->type() )
 
 1261      std::unique_ptr< QgsSymbol > newCatSymbol( tempSymbol->clone() );
 
 1262      if ( selectedCats.count() > 1 )
 
 1265        newCatSymbol->setColor( 
mRenderer->categories().at( idx ).symbol()->color() );
 
 1267      mRenderer->updateCategorySymbol( idx, newCatSymbol.release() );
 
 
 1280void QgsCategorizedSymbolRendererWidget::updateSymbolsFromButton()
 
 1290  QItemSelectionModel *m = viewCategories->selectionModel();
 
 1291  const QModelIndexList i = m->selectedRows();
 
 1297    if ( !selectedCats.isEmpty() )
 
 1299      const auto constSelectedCats = selectedCats;
 
 1300      for ( 
const int idx : constSelectedCats )
 
 1303        if ( selectedCats.count() > 1 )
 
 1308        mRenderer->updateCategorySymbol( idx, newCatSymbol );
 
 1317  mModel->updateSymbology();
 
 
 1328  if ( event->key() == Qt::Key_C && event->modifiers() == Qt::ControlModifier )
 
 1330    mCopyBuffer.clear();
 
 1333  else if ( event->key() == Qt::Key_V && event->modifiers() == Qt::ControlModifier )
 
 1335    QgsCategoryList::const_iterator rIt = mCopyBuffer.constBegin();
 
 1336    for ( ; rIt != mCopyBuffer.constEnd(); ++rIt )
 
 1338      mModel->addCategory( *rIt );
 
 
 1356      expContext << generator->createExpressionContextScope();
 
 
 1377void QgsCategorizedSymbolRendererWidget::dataDefinedSizeLegend()
 
 1392void QgsCategorizedSymbolRendererWidget::mergeSelectedCategories()
 
 1397  QList< int > categoryIndexes;
 
 1400  for ( 
const int i : selectedCategoryIndexes )
 
 1402    const QVariant v = categories.at( i ).value();
 
 1404    if ( !v.isValid() || v == 
"" )
 
 1409    categoryIndexes.append( i );
 
 1412  if ( categoryIndexes.count() < 2 )
 
 1416  QVariantList values;
 
 1417  values.reserve( categoryIndexes.count() );
 
 1418  labels.reserve( categoryIndexes.count() );
 
 1419  for ( 
const int i : categoryIndexes )
 
 1421    const QVariant v = categories.at( i ).value();
 
 1423    if ( v.userType() == QMetaType::Type::QVariantList )
 
 1425      values.append( v.toList() );
 
 1430    labels << categories.at( i ).label();
 
 1434  mRenderer->updateCategoryLabel( categoryIndexes.at( 0 ), labels.join( 
',' ) );
 
 1435  mRenderer->updateCategoryValue( categoryIndexes.at( 0 ), values );
 
 1437  categoryIndexes.pop_front();
 
 1438  mModel->deleteRows( categoryIndexes );
 
 1443void QgsCategorizedSymbolRendererWidget::unmergeSelectedCategories()
 
 1446  if ( categoryIndexes.isEmpty() )
 
 1450  for ( 
const int i : categoryIndexes )
 
 1452    const QVariant v = categories.at( i ).value();
 
 1453    if ( v.userType() != QMetaType::Type::QVariantList )
 
 1456    const QVariantList list = v.toList();
 
 1457    for ( 
int j = 1; j < list.count(); ++j )
 
 1459      mModel->addCategory( 
QgsRendererCategory( list.at( j ), categories.at( i ).symbol()->clone(), list.at( j ).toString(), categories.at( i ).renderState() ) );
 
 1461    mRenderer->updateCategoryValue( i, list.at( 0 ) );
 
 1462    mRenderer->updateCategoryLabel( i, list.at( 0 ).toString() );
 
 1468void QgsCategorizedSymbolRendererWidget::showContextMenu( QPoint )
 
 1470  mContextMenu->clear();
 
 1471  const QList< QAction * > actions = 
contextMenu->actions();
 
 1472  for ( QAction *act : actions )
 
 1474    mContextMenu->addAction( act );
 
 1477  mContextMenu->addSeparator();
 
 1479  if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
 
 1481    mContextMenu->addAction( mMergeCategoriesAction );
 
 1483  if ( viewCategories->selectionModel()->selectedRows().count() == 1 )
 
 1487    const QVariant v = categories.at( categoryIndexes.at( 0 ) ).value();
 
 1488    if ( v.userType() == QMetaType::Type::QVariantList )
 
 1489      mContextMenu->addAction( mUnmergeCategoriesAction );
 
 1491  else if ( viewCategories->selectionModel()->selectedRows().count() > 1 )
 
 1493    mContextMenu->addAction( mUnmergeCategoriesAction );
 
 1496  mContextMenu->exec( QCursor::pos() );
 
 1499void QgsCategorizedSymbolRendererWidget::selectionChanged( 
const QItemSelection &, 
const QItemSelection & )
 
 1502  if ( !selectedCats.isEmpty() )
 
 1504    whileBlocking( btnChangeCategorizedSymbol )->setSymbol( 
mRenderer->categories().at( selectedCats.at( 0 ) ).symbol()->clone() );
 
 1510  btnChangeCategorizedSymbol->setDialogTitle( selectedCats.size() == 1 ? 
mRenderer->categories().at( selectedCats.at( 0 ) ).label() : tr( 
"Symbol Settings" ) );
 
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)
Fetch next feature and stores in f, returns true on success.
 
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...
 
Q_INVOKABLE 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(bool initialize=true)
Returns the 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, bool silenceNullWarnings=false)
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.
 
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)