15#ifndef QGSTEMPLATEDCATEGORIZEDRENDERERMODEL_H
16#define QGSTEMPLATEDCATEGORIZEDRENDERERMODEL_H
24#include <QAbstractItemModel>
33using namespace Qt::StringLiterals;
45template<
typename RendererType>
class QgsTemplatedCategorizedRendererModel :
public QAbstractItemModel
48 using Category =
typename RendererType::Category;
56 QgsTemplatedCategorizedRendererModel( QObject *parent =
nullptr, QScreen *screen =
nullptr )
57 : QAbstractItemModel( parent )
58 , mMimeFormat( u
"application/x-qgscategorizedsymbolrendererv2model"_s )
62 virtual int symbolColumn()
const {
return 0; }
64 virtual int valueColumn()
const {
return 1; }
66 virtual QVariant headerData(
int section, Qt::Orientation orientation,
int role )
const override = 0;
68 virtual void sort(
int column, Qt::SortOrder order = Qt::AscendingOrder )
override = 0;
70 virtual Qt::ItemFlags flags(
const QModelIndex &index )
const override
73 if ( !index.isValid() || !mRenderer )
75 return Qt::ItemIsDropEnabled;
78 Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsUserCheckable;
79 if ( index.column() == valueColumn() )
81 const Category category = mRenderer->categories().value( index.row() );
82 if ( category.value().userType() != QMetaType::Type::QVariantList )
84 flags |= Qt::ItemIsEditable;
89 flags |= extraFlags( index );
94 virtual QVariant data(
const QModelIndex &index,
int role )
const override
96 if ( !index.isValid() || !mRenderer )
101 const Category category = mRenderer->categories().value( index.row() );
105 case Qt::CheckStateRole:
107 if ( index.column() == symbolColumn() )
109 return category.renderState() ? Qt::Checked : Qt::Unchecked;
114 case Qt::DisplayRole:
115 case Qt::ToolTipRole:
117 if ( index.column() == valueColumn() )
119 if ( category.value().userType() == QMetaType::Type::QVariantList )
122 const QVariantList list = category.value().toList();
123 res.reserve( list.size() );
124 for (
const QVariant &variant : list )
127 if ( role == Qt::DisplayRole )
128 return res.join(
';' );
130 return res.join(
'\n' );
134 return tr(
"all other values" );
146 if ( index.column() == valueColumn() && category.value().userType() != QMetaType::Type::QVariantList && (
QgsVariantUtils::isNull( category.value() ) || category.value().toString().isEmpty() ) )
149 italicFont.setItalic(
true );
155 case Qt::DecorationRole:
157 if ( index.column() == symbolColumn() && category.symbol() )
159 return symbolIcon( category );
164 case Qt::ForegroundRole:
166 QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
167 if ( index.column() == valueColumn() && ( category.value().userType() == QMetaType::Type::QVariantList ||
QgsVariantUtils::isNull( category.value() ) || category.value().toString().isEmpty() ) )
169 QColor fadedTextColor = brush.color();
170 fadedTextColor.setAlpha( 128 );
171 brush.setColor( fadedTextColor );
176 case Qt::TextAlignmentRole:
178 return ( index.column() == valueColumn() ) ?
static_cast<Qt::Alignment::Int
>( Qt::AlignHCenter ) :
static_cast<Qt::Alignment::Int
>( Qt::AlignLeft );
183 if ( index.column() == valueColumn() )
185 if ( category.value().userType() == QMetaType::Type::QVariantList )
188 const QVariantList list = category.value().toList();
189 res.reserve( list.size() );
190 for (
const QVariant &variant : list )
191 res << variant.toString();
193 return res.join(
';' );
197 return category.value();
202 case static_cast<int>( Qt::UserRole + 1 ):
204 if ( index.column() == valueColumn() )
206 return category.value();
215 return extraData( index, role );
218 virtual bool setData(
const QModelIndex &index,
const QVariant &value,
int role )
override
220 if ( !index.isValid() )
223 if ( index.column() == symbolColumn() && role == Qt::CheckStateRole )
225 mRenderer->updateCategoryRenderState( index.row(), value == Qt::Checked );
226 emit dataChanged( index, index );
230 if ( role != Qt::EditRole )
235 if ( index.column() == valueColumn() )
238 QVariant val = value;
239 const QVariant previousValue = mRenderer->categories().value( index.row() ).value();
240 if ( previousValue.userType() != QMetaType::Type::QString && !previousValue.toString().isEmpty() )
242 switch ( previousValue.userType() )
244 case QMetaType::Type::Int:
247 case QMetaType::Type::Double:
248 val = value.toDouble();
250 case QMetaType::Type::QVariantList:
252 const QStringList parts = value.toString().split(
';' );
254 list.reserve( parts.count() );
255 for (
const QString &part : parts )
258 if ( list.count() == 1 )
265 val = value.toString();
269 mRenderer->updateCategoryValue( index.row(), val );
270 emit dataChanged( index, index );
274 return setExtraData( index, value );
277 int rowCount(
const QModelIndex &parent = QModelIndex() )
const override
279 if ( parent.isValid() || !mRenderer )
283 return static_cast<int>( mRenderer->categories().size() );
286 int columnCount(
const QModelIndex & = QModelIndex() )
const override = 0;
288 Qt::DropActions supportedDropActions()
const override {
return Qt::MoveAction; }
290 QModelIndex index(
int row,
int column,
const QModelIndex &parent = QModelIndex() )
const override
292 if ( hasIndex( row, column, parent ) )
294 return createIndex( row, column );
296 return QModelIndex();
299 QModelIndex parent(
const QModelIndex &index )
const override
302 return QModelIndex();
305 QStringList mimeTypes()
const override
308 types << mMimeFormat;
312 QMimeData *mimeData(
const QModelIndexList &indexes )
const override
314 QMimeData *mimeData =
new QMimeData();
315 QByteArray encodedData;
317 QDataStream stream( &encodedData, QIODevice::WriteOnly );
319 const auto constIndexes = indexes;
320 for (
const QModelIndex &index : constIndexes )
322 if ( !index.isValid() || index.column() != 0 )
325 stream << index.row();
327 mimeData->setData( mMimeFormat, encodedData );
331 bool dropMimeData(
const QMimeData *data, Qt::DropAction action,
int row,
int column,
const QModelIndex &parent )
override
335 if ( action != Qt::MoveAction )
338 if ( !data->hasFormat( mMimeFormat ) )
341 QByteArray encodedData = data->data( mMimeFormat );
342 QDataStream stream( &encodedData, QIODevice::ReadOnly );
345 while ( !stream.atEnd() )
352 std::sort( rows.begin(), rows.end() );
357 to =
static_cast<int>( mRenderer->categories().size() );
358 for (
int i =
static_cast<int>( rows.size() ) - 1; i >= 0; i-- )
364 mRenderer->moveCategory( rows[i], t );
365 for (
int j = 0; j < i; j++ )
367 if ( to < rows[j] && rows[i] > rows[j] )
373 emit dataChanged( createIndex( 0, 0 ), createIndex(
static_cast<int>( mRenderer->categories().size() ), 0 ) );
383 void setRenderer( RendererType *renderer )
387 beginRemoveRows( QModelIndex(), 0, std::max<int>(
static_cast<int>( mRenderer->categories().size() ) - 1, 0 ) );
393 mRenderer = renderer;
394 if ( renderer->categories().size() > 0 )
396 beginInsertRows( QModelIndex(), 0,
static_cast<int>( renderer->categories().size() ) - 1 );
407 void addCategory(
const Category &cat )
411 const int idx =
static_cast<int>( mRenderer->categories().size() );
412 beginInsertRows( QModelIndex(), idx, idx );
413 mRenderer->addCategory( cat );
423 Category category(
const QModelIndex &index )
427 return typename RendererType::Category();
429 const auto &catList = mRenderer->categories();
430 const int row = index.row();
431 if ( row >= catList.size() )
433 return typename RendererType::Category();
435 return catList.at( row );
443 void deleteRows( QList<int> rows )
445 std::sort( rows.begin(), rows.end() );
446 for (
int i =
static_cast<int>( rows.size() ) - 1; i >= 0; i-- )
448 beginRemoveRows( QModelIndex(), rows[i], rows[i] );
449 mRenderer->deleteCategory( rows[i] );
459 beginRemoveRows( QModelIndex(), 0,
static_cast<int>( mRenderer->categories().size() ) - 1 );
460 mRenderer->deleteAllCategories();
467 void updateSymbology() { emit dataChanged( createIndex( 0, 0 ), createIndex(
static_cast<int>( mRenderer->categories().size() ), 0 ) ); }
473 virtual void onRowsMoved() = 0;
478 virtual Qt::ItemFlags extraFlags(
const QModelIndex &index )
const
481 return Qt::NoItemFlags;
484 virtual QIcon symbolIcon(
const RendererType::Category &category )
const = 0;
490 virtual bool setExtraData(
const QModelIndex &index,
const QVariant &value )
501 virtual QVariant extraData(
const QModelIndex &index,
int role )
const
509 RendererType *mRenderer =
nullptr;
511 QPointer<QScreen> mScreen;
static QString displayString(const QVariant &variant, int precision=-1)
Returns a localized representation of value with the given precision, if precision is -1 then precisi...
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
#define QgsDebugMsgLevel(str, level)