|
| 1 | +/*************************************************************************** |
| 2 | + qgssettingstreemodel.cpp |
| 3 | + -------------------------------------- |
| 4 | + Date : January 2023 |
| 5 | + Copyright : (C) 2023 by Denis Rouzaud |
| 6 | + Email : denis@opengis.ch |
| 7 | + *************************************************************************** |
| 8 | + * * |
| 9 | + * This program is free software; you can redistribute it and/or modify * |
| 10 | + * it under the terms of the GNU General Public License as published by * |
| 11 | + * the Free Software Foundation; either version 2 of the License, or * |
| 12 | + * (at your option) any later version. * |
| 13 | + * * |
| 14 | + ***************************************************************************/ |
| 15 | + |
| 16 | +#include <QFont> |
| 17 | + |
| 18 | +#include "qgssettingstreemodel.h" |
| 19 | +#include "qgssettingsentry.h" |
| 20 | +#include "qgssettingstreenode.h" |
| 21 | +#include "qgssettingseditorwidgetwrapper.h" |
| 22 | +#include "qgssettingseditorwidgetregistry.h" |
| 23 | +#include "qgsgui.h" |
| 24 | +#include "qgslogger.h" |
| 25 | + |
| 26 | + |
| 27 | +QgsSettingsTreeModelNodeData *QgsSettingsTreeModelNodeData::createRootNodeData( const QgsSettingsTreeNode *rootNode, QObject *parent = nullptr ) |
| 28 | +{ |
| 29 | + QgsSettingsTreeModelNodeData *nodeData = new QgsSettingsTreeModelNodeData( parent ); |
| 30 | + nodeData->mType = Type::RootNode; |
| 31 | + nodeData->mName = rootNode->key(); |
| 32 | + nodeData->mTreeNode = rootNode; |
| 33 | + nodeData->fillChildren(); |
| 34 | + return nodeData; |
| 35 | +} |
| 36 | + |
| 37 | +void QgsSettingsTreeModelNodeData::applyChanges() |
| 38 | +{ |
| 39 | + switch ( type() ) |
| 40 | + { |
| 41 | + case Type::NamedListTreeNode: |
| 42 | + case Type::RootNode: |
| 43 | + case Type::TreeNode: |
| 44 | + case Type::NamedListItem: |
| 45 | + { |
| 46 | + QList<QgsSettingsTreeModelNodeData *>::iterator it = mChildren.begin(); |
| 47 | + for ( ; it != mChildren.end(); ++it ) |
| 48 | + ( *it )->applyChanges(); |
| 49 | + break; |
| 50 | + } |
| 51 | + case Type::Setting: |
| 52 | + { |
| 53 | + if ( isEdited() ) |
| 54 | + { |
| 55 | + setting()->setVariantValue( mValue, mNamedParentNodes ); |
| 56 | + } |
| 57 | + break; |
| 58 | + } |
| 59 | + } |
| 60 | +} |
| 61 | + |
| 62 | +bool QgsSettingsTreeModelNodeData::setValue( const QVariant &value ) |
| 63 | +{ |
| 64 | + Q_ASSERT( mType == Type::Setting ); |
| 65 | + if ( !value.isValid() && mValue.isValid() ) |
| 66 | + { |
| 67 | + mExists = false; |
| 68 | + mIsEdited = true; |
| 69 | + mValue = QVariant(); |
| 70 | + } |
| 71 | + else if ( !mValue.isValid() || mValue != value ) |
| 72 | + { |
| 73 | + mValue = value; |
| 74 | + mIsEdited = ( value != mOriginalValue ); |
| 75 | + } |
| 76 | + // TODO: check the value of setting is fullfilling the settings' contraints |
| 77 | + return true; |
| 78 | +} |
| 79 | + |
| 80 | + |
| 81 | +void QgsSettingsTreeModelNodeData::addChildForTreeNode( const QgsSettingsTreeNode *node ) |
| 82 | +{ |
| 83 | + QgsSettingsTreeModelNodeData *nodeData = new QgsSettingsTreeModelNodeData( this ); |
| 84 | + nodeData->mParent = this; |
| 85 | + nodeData->mNamedParentNodes = mNamedParentNodes; |
| 86 | + nodeData->mName = node->key(); |
| 87 | + nodeData->mTreeNode = node; |
| 88 | + if ( node->type() == Qgis::SettingsTreeNodeType::NamedList ) |
| 89 | + { |
| 90 | + nodeData->mType = Type::NamedListTreeNode; |
| 91 | + const QgsSettingsTreeNamedListNode *nln = dynamic_cast<const QgsSettingsTreeNamedListNode *>( node ); |
| 92 | + const QStringList items = nln->items( mNamedParentNodes ); |
| 93 | + for ( const QString &item : items ) |
| 94 | + { |
| 95 | + nodeData->addChildForNamedListItemNode( item, nln ); |
| 96 | + } |
| 97 | + } |
| 98 | + else |
| 99 | + { |
| 100 | + nodeData->mType = Type::TreeNode; |
| 101 | + nodeData->fillChildren(); |
| 102 | + } |
| 103 | + mChildren.append( nodeData ); |
| 104 | +} |
| 105 | + |
| 106 | +void QgsSettingsTreeModelNodeData::addChildForNamedListItemNode( const QString &item, const QgsSettingsTreeNamedListNode *namedListNode ) |
| 107 | +{ |
| 108 | + QgsSettingsTreeModelNodeData *nodeData = new QgsSettingsTreeModelNodeData( this ); |
| 109 | + nodeData->mType = Type::NamedListItem; |
| 110 | + nodeData->mParent = this; |
| 111 | + nodeData->mNamedParentNodes = mNamedParentNodes; |
| 112 | + nodeData->mNamedParentNodes.append( item ); |
| 113 | + nodeData->mName = item; |
| 114 | + nodeData->mTreeNode = namedListNode; |
| 115 | + nodeData->fillChildren(); |
| 116 | + mChildren.append( nodeData ); |
| 117 | +} |
| 118 | + |
| 119 | +void QgsSettingsTreeModelNodeData::addChildForSetting( const QgsSettingsEntryBase *setting ) |
| 120 | +{ |
| 121 | + QgsSettingsTreeModelNodeData *nodeData = new QgsSettingsTreeModelNodeData( this ); |
| 122 | + nodeData->mType = Type::Setting; |
| 123 | + nodeData->mParent = this; |
| 124 | + nodeData->mNamedParentNodes = mNamedParentNodes; |
| 125 | + nodeData->mSetting = setting; |
| 126 | + nodeData->mName = setting->name(); |
| 127 | + nodeData->mValue = setting->valueAsVariant( mNamedParentNodes ); |
| 128 | + nodeData->mOriginalValue = nodeData->mValue; |
| 129 | + nodeData->mExists = setting->exists( mNamedParentNodes ); |
| 130 | + |
| 131 | + switch ( mNamedParentNodes.count() ) |
| 132 | + { |
| 133 | + case 1: |
| 134 | + QgsDebugMsg( QString( "getting %1 with %2" ).arg( setting->definitionKey(), mNamedParentNodes.at( 0 ) ) ); |
| 135 | + break; |
| 136 | + case 2: |
| 137 | + QgsDebugMsg( QString( "getting %1 with %2" ).arg( setting->definitionKey(), mNamedParentNodes.at( 0 ), mNamedParentNodes.at( 1 ) ) ); |
| 138 | + break; |
| 139 | + case 0: |
| 140 | + QgsDebugMsg( QString( "getting %1" ).arg( setting->definitionKey() ) ); |
| 141 | + break; |
| 142 | + default: |
| 143 | + Q_ASSERT( false ); |
| 144 | + QgsDebugMsg( QString( "Not handling that many named parent nodes for %1" ).arg( setting->definitionKey() ) ); |
| 145 | + break; |
| 146 | + } |
| 147 | + |
| 148 | + mChildren.append( nodeData ); |
| 149 | +} |
| 150 | + |
| 151 | +void QgsSettingsTreeModelNodeData::fillChildren() |
| 152 | +{ |
| 153 | + const QList<QgsSettingsTreeNode *> childrenNodes = mTreeNode->childrenNodes(); |
| 154 | + for ( const QgsSettingsTreeNode *childNode : childrenNodes ) |
| 155 | + { |
| 156 | + addChildForTreeNode( childNode ); |
| 157 | + } |
| 158 | + const QList<const QgsSettingsEntryBase *> childrenSettings = mTreeNode->childrenSettings(); |
| 159 | + for ( const QgsSettingsEntryBase *setting : childrenSettings ) |
| 160 | + { |
| 161 | + addChildForSetting( setting ); |
| 162 | + } |
| 163 | +} |
| 164 | + |
| 165 | + |
| 166 | + |
| 167 | +QgsSettingsTreeModel::QgsSettingsTreeModel( QgsSettingsTreeNode *rootNode, QObject *parent ) |
| 168 | + : QAbstractItemModel( parent ) |
| 169 | +{ |
| 170 | + mRootNode = QgsSettingsTreeModelNodeData::createRootNodeData( rootNode, this ); |
| 171 | +} |
| 172 | + |
| 173 | +QgsSettingsTreeModel::~QgsSettingsTreeModel() |
| 174 | +{ |
| 175 | + //delete mRootNode; |
| 176 | +} |
| 177 | + |
| 178 | +void QgsSettingsTreeModel::applyChanges() |
| 179 | +{ |
| 180 | + beginResetModel(); |
| 181 | + mRootNode->applyChanges(); |
| 182 | + endResetModel(); |
| 183 | +} |
| 184 | + |
| 185 | +QgsSettingsTreeModelNodeData *QgsSettingsTreeModel::index2node( const QModelIndex &index ) const |
| 186 | +{ |
| 187 | + if ( !index.isValid() ) |
| 188 | + return mRootNode; |
| 189 | + |
| 190 | + QObject *obj = reinterpret_cast<QObject *>( index.internalPointer() ); |
| 191 | + return qobject_cast<QgsSettingsTreeModelNodeData *>( obj ); |
| 192 | +} |
| 193 | + |
| 194 | + |
| 195 | +QModelIndex QgsSettingsTreeModel::index( int row, int column, const QModelIndex &parent ) const |
| 196 | +{ |
| 197 | + if ( column < 0 || column >= columnCount( parent ) || |
| 198 | + row < 0 || row >= rowCount( parent ) ) |
| 199 | + return QModelIndex(); |
| 200 | + |
| 201 | + QgsSettingsTreeModelNodeData *n = index2node( parent ); |
| 202 | + if ( !n ) |
| 203 | + return QModelIndex(); // have no children |
| 204 | + |
| 205 | + |
| 206 | + return createIndex( row, column, static_cast<QObject *>( n->children().at( row ) ) ); |
| 207 | +} |
| 208 | + |
| 209 | +QModelIndex QgsSettingsTreeModel::parent( const QModelIndex &child ) const |
| 210 | +{ |
| 211 | + if ( !child.isValid() ) |
| 212 | + return QModelIndex(); |
| 213 | + |
| 214 | + if ( QgsSettingsTreeModelNodeData *n = index2node( child ) ) |
| 215 | + { |
| 216 | + return indexOfParentSettingsTreeNode( n->parent() ); // must not be null |
| 217 | + } |
| 218 | + else |
| 219 | + { |
| 220 | + Q_ASSERT( false ); // no other node types! |
| 221 | + return QModelIndex(); |
| 222 | + } |
| 223 | +} |
| 224 | + |
| 225 | +QModelIndex QgsSettingsTreeModel::indexOfParentSettingsTreeNode( QgsSettingsTreeModelNodeData *parentNode ) const |
| 226 | +{ |
| 227 | + Q_ASSERT( parentNode ); |
| 228 | + |
| 229 | + const QgsSettingsTreeModelNodeData *grandParentNode = parentNode->parent(); |
| 230 | + if ( !grandParentNode ) |
| 231 | + return QModelIndex(); // root node -> invalid index |
| 232 | + |
| 233 | + int row = grandParentNode->children().indexOf( parentNode ); |
| 234 | + Q_ASSERT( row >= 0 ); |
| 235 | + |
| 236 | + return createIndex( row, 0, static_cast<QObject *>( parentNode ) ); |
| 237 | +} |
| 238 | + |
| 239 | +int QgsSettingsTreeModel::rowCount( const QModelIndex &parent ) const |
| 240 | +{ |
| 241 | + QgsSettingsTreeModelNodeData *n = index2node( parent ); |
| 242 | + if ( !n ) |
| 243 | + return 0; |
| 244 | + |
| 245 | + return n->children().count(); |
| 246 | +} |
| 247 | + |
| 248 | +int QgsSettingsTreeModel::columnCount( const QModelIndex &parent ) const |
| 249 | +{ |
| 250 | + Q_UNUSED( parent ) |
| 251 | + return 3; |
| 252 | +} |
| 253 | + |
| 254 | +QVariant QgsSettingsTreeModel::data( const QModelIndex &index, int role ) const |
| 255 | +{ |
| 256 | + if ( !index.isValid() || index.column() > columnCount( index ) ) |
| 257 | + return QVariant(); |
| 258 | + |
| 259 | + QgsSettingsTreeModelNodeData *node = index2node( index ); |
| 260 | + |
| 261 | + switch ( static_cast<Column>( index.column() ) ) |
| 262 | + { |
| 263 | + case Column::Name: |
| 264 | + { |
| 265 | + if ( role == Qt::DisplayRole || role == Qt::EditRole ) |
| 266 | + { |
| 267 | + return node->name(); |
| 268 | + } |
| 269 | + break; |
| 270 | + } |
| 271 | + |
| 272 | + case Column::Value: |
| 273 | + { |
| 274 | + if ( role == Qt::DisplayRole || role == Qt::EditRole ) |
| 275 | + { |
| 276 | + return node->value(); |
| 277 | + } |
| 278 | + else if ( role == Qt::FontRole ) |
| 279 | + { |
| 280 | + if ( !node->exists() ) |
| 281 | + { |
| 282 | + QFont font; |
| 283 | + font.setItalic( true ); |
| 284 | + return font; |
| 285 | + } |
| 286 | + } |
| 287 | + else if ( role == Qt::ForegroundRole ) |
| 288 | + { |
| 289 | + if ( node->isEdited() ) |
| 290 | + return QColorConstants::Red; |
| 291 | + } |
| 292 | + else if ( role == Qt::BackgroundRole ) |
| 293 | + { |
| 294 | + if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting ) |
| 295 | + { |
| 296 | + switch ( node->setting()->settingsType() ) |
| 297 | + { |
| 298 | + case Qgis::SettingsType::Custom: |
| 299 | + case Qgis::SettingsType::Variant: |
| 300 | + case Qgis::SettingsType::String: |
| 301 | + case Qgis::SettingsType::StringList: |
| 302 | + case Qgis::SettingsType::VariantMap: |
| 303 | + case Qgis::SettingsType::Bool: |
| 304 | + case Qgis::SettingsType::Integer: |
| 305 | + case Qgis::SettingsType::Double: |
| 306 | + case Qgis::SettingsType::EnumFlag: |
| 307 | + break; |
| 308 | + |
| 309 | + case Qgis::SettingsType::Color: |
| 310 | + return node->value(); |
| 311 | + } |
| 312 | + } |
| 313 | + } |
| 314 | + break; |
| 315 | + } |
| 316 | + |
| 317 | + case Column::Description: |
| 318 | + { |
| 319 | + if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting ) |
| 320 | + { |
| 321 | + if ( role == Qt::DisplayRole || role == Qt::EditRole ) |
| 322 | + { |
| 323 | + return node->setting()->description(); |
| 324 | + } |
| 325 | + } |
| 326 | + break; |
| 327 | + } |
| 328 | + |
| 329 | + default: |
| 330 | + break; |
| 331 | + } |
| 332 | + |
| 333 | + return QVariant(); |
| 334 | +} |
| 335 | + |
| 336 | + |
| 337 | +QVariant QgsSettingsTreeModel::headerData( int section, Qt::Orientation orientation, int role ) const |
| 338 | +{ |
| 339 | + if ( orientation == Qt::Orientation::Horizontal && role == Qt::DisplayRole ) |
| 340 | + { |
| 341 | + switch ( static_cast<Column>( section ) ) |
| 342 | + { |
| 343 | + case Column::Name: |
| 344 | + return tr( "Name" ); |
| 345 | + case Column::Value: |
| 346 | + return tr( "Value" ); |
| 347 | + case Column::Description: |
| 348 | + return tr( "Description" ); |
| 349 | + }; |
| 350 | + } |
| 351 | + |
| 352 | + return QVariant(); |
| 353 | +} |
| 354 | + |
| 355 | +Qt::ItemFlags QgsSettingsTreeModel::flags( const QModelIndex &index ) const |
| 356 | +{ |
| 357 | + if ( index.column() == static_cast<int>( Column::Value ) ) |
| 358 | + { |
| 359 | + QgsSettingsTreeModelNodeData *nodeData = index2node( index ); |
| 360 | + if ( nodeData->type() == QgsSettingsTreeModelNodeData::Type::Setting ) |
| 361 | + { |
| 362 | + return Qt::ItemIsEnabled | Qt::ItemIsEditable; |
| 363 | + } |
| 364 | + } |
| 365 | + else |
| 366 | + { |
| 367 | + return Qt::ItemIsEnabled; |
| 368 | + } |
| 369 | + return Qt::NoItemFlags; |
| 370 | +} |
| 371 | + |
| 372 | +bool QgsSettingsTreeModel::setData( const QModelIndex &index, const QVariant &value, int role ) |
| 373 | +{ |
| 374 | + if ( role == Qt::EditRole && index.column() == static_cast<int>( Column::Value ) ) |
| 375 | + { |
| 376 | + QgsSettingsTreeModelNodeData *nodeData = index2node( index ); |
| 377 | + if ( nodeData->type() == QgsSettingsTreeModelNodeData::Type::Setting ) |
| 378 | + { |
| 379 | + nodeData->setValue( value ); |
| 380 | + emit dataChanged( index, index ); |
| 381 | + return true; |
| 382 | + } |
| 383 | + } |
| 384 | + return false; |
| 385 | +} |
| 386 | + |
| 387 | + |
| 388 | +QgsSettingsTreeItemDelegate::QgsSettingsTreeItemDelegate( QgsSettingsTreeModel *model, QObject *parent ) |
| 389 | + : QItemDelegate( parent ) |
| 390 | + , mModel( model ) |
| 391 | +{ |
| 392 | +} |
| 393 | + |
| 394 | +QWidget *QgsSettingsTreeItemDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const |
| 395 | +{ |
| 396 | + Q_UNUSED( option ) |
| 397 | + if ( static_cast<QgsSettingsTreeModel::Column>( index.column() ) == QgsSettingsTreeModel::Column::Value ) |
| 398 | + { |
| 399 | + QgsSettingsTreeModelNodeData *nodeData = mModel->index2node( index ); |
| 400 | + if ( nodeData->type() == QgsSettingsTreeModelNodeData::Type::Setting ) |
| 401 | + { |
| 402 | + return QgsGui::settingsEditorWidgetRegistry()->createEditor( nodeData->setting(), nodeData->namedParentNodes(), parent ); |
| 403 | + } |
| 404 | + } |
| 405 | + return nullptr; |
| 406 | +} |
| 407 | + |
| 408 | +void QgsSettingsTreeItemDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const |
| 409 | +{ |
| 410 | + Q_UNUSED( index ) |
| 411 | + QgsSettingsEditorWidgetWrapper *eww = QgsSettingsEditorWidgetWrapper::fromWidget( editor ); |
| 412 | + if ( eww ) |
| 413 | + eww->setWidgetFromVariant( mModel->data( index, Qt::DisplayRole ) ); |
| 414 | +} |
| 415 | + |
| 416 | +void QgsSettingsTreeItemDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const |
| 417 | +{ |
| 418 | + Q_UNUSED( index ) |
| 419 | + QgsSettingsEditorWidgetWrapper *eww = QgsSettingsEditorWidgetWrapper::fromWidget( editor ); |
| 420 | + if ( eww ) |
| 421 | + model->setData( index, eww->variantValueFromWidget(), Qt::EditRole ); |
| 422 | +} |
| 423 | + |
| 424 | + |
| 425 | + |
| 426 | + |
0 commit comments