Qt::ItemFlags QgsCategorizedSymbolRendererModel::flags( const QModelIndex &index ) const
{
  if ( !index.isValid() || !mRenderer )
  {
    return Qt::ItemIsDropEnabled;
  }

  Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable;
  if ( index.column() == 1 )
  {
    const QgsRendererCategory category = mRenderer->categories().value( index.row() );
    if ( category.value().type() != QVariant::List )
    {
      flags |= Qt::ItemIsEditable;
    }
  }
  else if ( index.column() == 2 )
  {
    flags |= Qt::ItemIsEditable;
  }
  return flags;
}
QVariant QgsCategorizedSymbolRendererModel::data( const QModelIndex &index, int role ) const
{
  if ( !index.isValid() || !mRenderer )
    return QVariant();

  const QgsRendererCategory category = mRenderer->categories().value( index.row() );

  if ( role == Qt::CheckStateRole && index.column() == 0 )
  {
    return category.renderState() ? Qt::Checked : Qt::Unchecked;
  }
  else if ( role == Qt::DisplayRole || role == Qt::ToolTipRole )
  {
    switch ( index.column() )
    {
      case 1:
        return category.value().toString();
      case 2:
        return category.label();
      default:
        return QVariant();
    }
  }
  else if ( role == Qt::DecorationRole && index.column() == 0 && category.symbol() )
  {
    return QgsSymbolLayerUtils::symbolPreviewIcon( category.symbol(), QSize( 16, 16 ) );
  }
  else if ( role == Qt::TextAlignmentRole )
  {
    return ( index.column() == 0 ) ? Qt::AlignHCenter : Qt::AlignLeft;
  }
  else if ( role == Qt::EditRole )
  {
    switch ( index.column() )
    {
      case 1:
        return category.value();
      case 2:
        return category.label();
      default:
        return QVariant();
    }
  }

  return QVariant();
}
QVariant QgsCategorizedSymbolRendererModel::data( const QModelIndex &index, int role ) const
{
  if ( !index.isValid() || !mRenderer )
    return QVariant();

  const QgsRendererCategory category = mRenderer->categories().value( index.row() );

  switch ( role )
  {
    case Qt::CheckStateRole:
    {
      if ( index.column() == 0 )
      {
        return category.renderState() ? Qt::Checked : Qt::Unchecked;
      }
      break;
    }

    case Qt::DisplayRole:
    case Qt::ToolTipRole:
    {
      switch ( index.column() )
      {
        case 1:
        {
          if ( category.value().type() == QVariant::List )
          {
            QStringList res;
            const QVariantList list = category.value().toList();
            res.reserve( list.size() );
            for ( const QVariant &v : list )
              res << v.toString();

            if ( role == Qt::DisplayRole )
              return res.join( ';' );
            else // tooltip
              return res.join( '\n' );
          }
          else if ( !category.value().isValid() || category.value().isNull() || category.value().toString().isEmpty() )
          {
            return tr( "all other values" );
          }
          else
          {
            return category.value().toString();
          }
        }
        case 2:
          return category.label();
      }
      break;
    }

    case Qt::FontRole:
    {
      if ( index.column() == 1 && category.value().type() != QVariant::List && ( !category.value().isValid() || category.value().isNull() || category.value().toString().isEmpty() ) )
      {
        QFont italicFont;
        italicFont.setItalic( true );
        return italicFont;
      }
      return QVariant();
    }

    case Qt::DecorationRole:
    {
      if ( index.column() == 0 && category.symbol() )
      {
        return QgsSymbolLayerUtils::symbolPreviewIcon( category.symbol(), QSize( 16, 16 ) );
      }
      break;
    }

    case Qt::ForegroundRole:
    {
      QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
      if ( index.column() == 1 && ( category.value().type() == QVariant::List
                                    || !category.value().isValid() || category.value().isNull() || category.value().toString().isEmpty() ) )
      {
        QColor fadedTextColor = brush.color();
        fadedTextColor.setAlpha( 128 );
        brush.setColor( fadedTextColor );
      }
      return brush;
    }

    case Qt::TextAlignmentRole:
    {
      return ( index.column() == 0 ) ? Qt::AlignHCenter : Qt::AlignLeft;
    }

    case Qt::EditRole:
    {
      switch ( index.column() )
      {
        case 1:
        {
          if ( category.value().type() == QVariant::List )
          {
            QStringList res;
            const QVariantList list = category.value().toList();
            res.reserve( list.size() );
            for ( const QVariant &v : list )
              res << v.toString();

            return res.join( ';' );
          }
          else
          {
            return category.value();
          }
        }

        case 2:
          return category.label();
      }
      break;
    }
  }

  return QVariant();
}