15#ifndef QGSTEMPLATEDCATEGORIZEDRENDERERMODEL_H
16#define QGSTEMPLATEDCATEGORIZEDRENDERERMODEL_H
24#include <QAbstractItemModel>
36using namespace Qt::StringLiterals;
48template<
typename RendererType>
class QgsTemplatedCategorizedRendererModel :
public QAbstractItemModel
51 using Category =
typename RendererType::Category;
59 QgsTemplatedCategorizedRendererModel( QObject *parent =
nullptr, QScreen *screen =
nullptr )
60 : QAbstractItemModel( parent )
61 , mMimeFormat( u
"application/x-qgscategorizedsymbolrendererv2model"_s )
65 virtual int symbolColumn()
const {
return 0; }
67 virtual int valueColumn()
const {
return 1; }
69 virtual QVariant headerData(
int section, Qt::Orientation orientation,
int role )
const override = 0;
71 virtual void sort(
int column, Qt::SortOrder order = Qt::AscendingOrder )
override = 0;
73 virtual Qt::ItemFlags flags(
const QModelIndex &index )
const override
76 if ( !index.isValid() || !mRenderer )
78 return Qt::ItemIsDropEnabled;
81 Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsUserCheckable;
82 if ( index.column() == valueColumn() )
84 const Category category = mRenderer->categories().value( index.row() );
85 if ( category.value().userType() != QMetaType::Type::QVariantList )
87 flags |= Qt::ItemIsEditable;
92 flags |= extraFlags( index );
97 virtual QVariant data(
const QModelIndex &index,
int role )
const override
99 if ( !index.isValid() || !mRenderer )
104 const Category category = mRenderer->categories().value( index.row() );
108 case Qt::CheckStateRole:
110 if ( index.column() == symbolColumn() )
112 return category.renderState() ? Qt::Checked : Qt::Unchecked;
117 case Qt::DisplayRole:
118 case Qt::ToolTipRole:
120 if ( index.column() == valueColumn() )
122 if ( category.value().userType() == QMetaType::Type::QVariantList )
125 const QVariantList list = category.value().toList();
126 res.reserve( list.size() );
127 for (
const QVariant &variant : list )
130 if ( role == Qt::DisplayRole )
131 return res.join(
';' );
133 return res.join(
'\n' );
137 return tr(
"all other values" );
149 if ( index.column() == valueColumn() && category.value().userType() != QMetaType::Type::QVariantList && (
QgsVariantUtils::isNull( category.value() ) || category.value().toString().isEmpty() ) )
152 italicFont.setItalic(
true );
158 case Qt::DecorationRole:
160 if ( index.column() == symbolColumn() && category.symbol() )
162 return symbolIcon( category );
167 case Qt::ForegroundRole:
169 QBrush brush( qApp->palette().color( QPalette::Text ), Qt::SolidPattern );
170 if ( index.column() == valueColumn() && ( category.value().userType() == QMetaType::Type::QVariantList ||
QgsVariantUtils::isNull( category.value() ) || category.value().toString().isEmpty() ) )
172 QColor fadedTextColor = brush.color();
173 fadedTextColor.setAlpha( 128 );
174 brush.setColor( fadedTextColor );
179 case Qt::TextAlignmentRole:
181 return ( index.column() == symbolColumn() ) ?
static_cast<Qt::Alignment::Int
>( Qt::AlignHCenter ) :
static_cast<Qt::Alignment::Int
>( Qt::AlignLeft );
186 if ( index.column() == valueColumn() )
188 if ( category.value().userType() == QMetaType::Type::QVariantList )
191 const QVariantList list = category.value().toList();
192 res.reserve( list.size() );
193 for (
const QVariant &variant : list )
194 res << variant.toString();
196 return res.join(
';' );
200 return category.value();
205 case static_cast<int>( Qt::UserRole + 1 ):
207 if ( index.column() == valueColumn() )
209 return category.value();
218 return extraData( index, role );
221 virtual bool setData(
const QModelIndex &index,
const QVariant &value,
int role )
override
223 if ( !index.isValid() )
226 if ( index.column() == symbolColumn() && role == Qt::CheckStateRole )
228 mRenderer->updateCategoryRenderState( index.row(), value == Qt::Checked );
229 emit dataChanged( index, index );
233 if ( role != Qt::EditRole )
238 if ( index.column() == valueColumn() )
241 QVariant val = value;
242 const QVariant previousValue = mRenderer->categories().value( index.row() ).value();
243 if ( previousValue.userType() != QMetaType::Type::QString && !previousValue.toString().isEmpty() )
245 switch ( previousValue.userType() )
247 case QMetaType::Type::Int:
250 case QMetaType::Type::Double:
251 val = value.toDouble();
253 case QMetaType::Type::QVariantList:
255 const QStringList parts = value.toString().split(
';' );
257 list.reserve( parts.count() );
258 for (
const QString &part : parts )
261 if ( list.count() == 1 )
268 val = value.toString();
272 mRenderer->updateCategoryValue( index.row(), val );
273 emit dataChanged( index, index );
277 return setExtraData( index, value );
280 int rowCount(
const QModelIndex &parent = QModelIndex() )
const override
282 if ( parent.isValid() || !mRenderer )
286 return static_cast<int>( mRenderer->categories().size() );
289 int columnCount(
const QModelIndex & = QModelIndex() )
const override = 0;
291 Qt::DropActions supportedDropActions()
const override {
return Qt::MoveAction; }
293 QModelIndex index(
int row,
int column,
const QModelIndex &parent = QModelIndex() )
const override
295 if ( hasIndex( row, column, parent ) )
297 return createIndex( row, column );
299 return QModelIndex();
302 QModelIndex parent(
const QModelIndex &index )
const override
305 return QModelIndex();
308 QStringList mimeTypes()
const override
311 types << mMimeFormat;
315 QMimeData *mimeData(
const QModelIndexList &indexes )
const override
317 QMimeData *mimeData =
new QMimeData();
318 QByteArray encodedData;
320 QDataStream stream( &encodedData, QIODevice::WriteOnly );
322 const auto constIndexes = indexes;
323 for (
const QModelIndex &index : constIndexes )
325 if ( !index.isValid() || index.column() != 0 )
328 stream << index.row();
330 mimeData->setData( mMimeFormat, encodedData );
334 bool dropMimeData(
const QMimeData *data, Qt::DropAction action,
int row,
int column,
const QModelIndex &parent )
override
338 if ( action != Qt::MoveAction )
341 if ( !data->hasFormat( mMimeFormat ) )
344 QByteArray encodedData = data->data( mMimeFormat );
345 QDataStream stream( &encodedData, QIODevice::ReadOnly );
348 while ( !stream.atEnd() )
355 std::sort( rows.begin(), rows.end() );
360 to =
static_cast<int>( mRenderer->categories().size() );
361 for (
int i =
static_cast<int>( rows.size() ) - 1; i >= 0; i-- )
367 mRenderer->moveCategory( rows[i], t );
368 for (
int j = 0; j < i; j++ )
370 if ( to < rows[j] && rows[i] > rows[j] )
376 emit dataChanged( createIndex( 0, 0 ), createIndex(
static_cast<int>( mRenderer->categories().size() ), 0 ) );
386 void setRenderer( RendererType *renderer )
390 beginRemoveRows( QModelIndex(), 0, std::max<int>(
static_cast<int>( mRenderer->categories().size() ) - 1, 0 ) );
396 mRenderer = renderer;
397 if ( renderer->categories().size() > 0 )
399 beginInsertRows( QModelIndex(), 0,
static_cast<int>( renderer->categories().size() ) - 1 );
410 void addCategory(
const Category &cat )
414 const int idx =
static_cast<int>( mRenderer->categories().size() );
415 beginInsertRows( QModelIndex(), idx, idx );
416 mRenderer->addCategory( cat );
426 Category category(
const QModelIndex &index )
430 return typename RendererType::Category();
432 const auto &catList = mRenderer->categories();
433 const int row = index.row();
434 if ( row >= catList.size() )
436 return typename RendererType::Category();
438 return catList.at( row );
446 void deleteRows( QList<int> rows )
448 std::sort( rows.begin(), rows.end() );
449 for (
int i =
static_cast<int>( rows.size() ) - 1; i >= 0; i-- )
451 beginRemoveRows( QModelIndex(), rows[i], rows[i] );
452 mRenderer->deleteCategory( rows[i] );
462 beginRemoveRows( QModelIndex(), 0,
static_cast<int>( mRenderer->categories().size() ) - 1 );
463 mRenderer->deleteAllCategories();
470 void updateSymbology() { emit dataChanged( createIndex( 0, 0 ), createIndex(
static_cast<int>( mRenderer->categories().size() ), 0 ) ); }
476 virtual void onRowsMoved() = 0;
481 virtual Qt::ItemFlags extraFlags(
const QModelIndex &index )
const
484 return Qt::NoItemFlags;
487 virtual QIcon symbolIcon(
const RendererType::Category &category )
const = 0;
493 virtual bool setExtraData(
const QModelIndex &index,
const QVariant &value )
504 virtual QVariant extraData(
const QModelIndex &index,
int role )
const
512 RendererType *mRenderer =
nullptr;
514 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)