QGIS API Documentation 3.41.0-Master (3440c17df1d)
Loading...
Searching...
No Matches
qgssettingstreemodel.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgssettingstreemodel.cpp
3 --------------------------------------
4 Date : January 2023
5 Copyright : (C) 2023 by Denis Rouzaud
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 <QGuiApplication>
17#include <QFont>
18
20#include "moc_qgssettingstreemodel.cpp"
21#include "qgssettingsentry.h"
22#include "qgssettingstreenode.h"
25#include "qgsgui.h"
26#include "qgslogger.h"
27
29
30
31QgsSettingsTreeModelNodeData *QgsSettingsTreeModelNodeData::createRootNodeData( const QgsSettingsTreeNode *rootNode, QObject *parent = nullptr )
32{
33 QgsSettingsTreeModelNodeData *nodeData = new QgsSettingsTreeModelNodeData( parent );
34 nodeData->mType = Type::RootNode;
35 nodeData->mName = rootNode->key();
36 nodeData->mTreeNode = rootNode;
37 nodeData->fillChildren();
38 return nodeData;
39}
40
41void QgsSettingsTreeModelNodeData::applyChanges()
42{
43 switch ( type() )
44 {
45 case Type::NamedListTreeNode:
46 case Type::RootNode:
47 case Type::TreeNode:
48 case Type::NamedListItem:
49 {
50 QList<QgsSettingsTreeModelNodeData *>::iterator it = mChildren.begin();
51 for ( ; it != mChildren.end(); ++it )
52 ( *it )->applyChanges();
53 break;
54 }
55 case Type::Setting:
56 {
57 if ( isEdited() )
58 {
59 setting()->setVariantValue( mValue, mNamedParentNodes );
60 }
61 break;
62 }
63 }
64}
65
66bool QgsSettingsTreeModelNodeData::setValue( const QVariant &value )
67{
68 Q_ASSERT( mType == Type::Setting );
69 if ( !value.isValid() && mValue.isValid() )
70 {
71 mExists = false;
72 mIsEdited = true;
73 mValue = QVariant();
74 }
75 else if ( !mValue.isValid() || mValue != value )
76 {
77 mValue = value;
78 mIsEdited = ( value != mOriginalValue );
79 }
80 // TODO: check the value of setting is fulfilling the settings' contsraints ?
81 return true;
82}
83
84
85void QgsSettingsTreeModelNodeData::addChildForTreeNode( const QgsSettingsTreeNode *node )
86{
87 QgsSettingsTreeModelNodeData *nodeData = new QgsSettingsTreeModelNodeData( this );
88 nodeData->mParent = this;
89 nodeData->mNamedParentNodes = mNamedParentNodes;
90 nodeData->mName = node->key();
91 nodeData->mTreeNode = node;
93 {
94 nodeData->mType = Type::NamedListTreeNode;
95 const QgsSettingsTreeNamedListNode *nln = dynamic_cast<const QgsSettingsTreeNamedListNode *>( node );
96 const QStringList items = nln->items( mNamedParentNodes );
97 for ( const QString &item : items )
98 {
99 nodeData->addChildForNamedListItemNode( item, nln );
100 }
101 }
102 else
103 {
104 nodeData->mType = Type::TreeNode;
105 nodeData->fillChildren();
106 }
107 mChildren.append( nodeData );
108}
109
110void QgsSettingsTreeModelNodeData::addChildForNamedListItemNode( const QString &item, const QgsSettingsTreeNamedListNode *namedListNode )
111{
112 QgsSettingsTreeModelNodeData *nodeData = new QgsSettingsTreeModelNodeData( this );
113 nodeData->mType = Type::NamedListItem;
114 nodeData->mParent = this;
115 nodeData->mNamedParentNodes = mNamedParentNodes;
116 nodeData->mNamedParentNodes.append( item );
117 nodeData->mName = item;
118 nodeData->mTreeNode = namedListNode;
119 nodeData->fillChildren();
120 mChildren.append( nodeData );
121}
122
123void QgsSettingsTreeModelNodeData::addChildForSetting( const QgsSettingsEntryBase *setting )
124{
125 QgsSettingsTreeModelNodeData *nodeData = new QgsSettingsTreeModelNodeData( this );
126 nodeData->mType = Type::Setting;
127 nodeData->mParent = this;
128 nodeData->mNamedParentNodes = mNamedParentNodes;
129 nodeData->mSetting = setting;
130 nodeData->mName = setting->name();
131 nodeData->mValue = setting->valueAsVariant( mNamedParentNodes );
132 nodeData->mOriginalValue = nodeData->mValue;
133 nodeData->mExists = setting->exists( mNamedParentNodes );
134
135 switch ( mNamedParentNodes.count() )
136 {
137 case 1:
138 QgsDebugMsgLevel( QString( "getting %1 with %2" ).arg( setting->definitionKey(), mNamedParentNodes.at( 0 ) ), 3 );
139 break;
140 case 2:
141 QgsDebugMsgLevel( QString( "getting %1 with %2 and %3" ).arg( setting->definitionKey(), mNamedParentNodes.at( 0 ), mNamedParentNodes.at( 1 ) ), 3 );
142 break;
143 case 0:
144 QgsDebugMsgLevel( QString( "getting %1" ).arg( setting->definitionKey() ), 3 );
145 break;
146 default:
147 Q_ASSERT( false );
148 QgsDebugError( QString( "Not handling that many named parent nodes for %1" ).arg( setting->definitionKey() ) );
149 break;
150 }
151
152 mChildren.append( nodeData );
153}
154
155void QgsSettingsTreeModelNodeData::fillChildren()
156{
157 const QList<QgsSettingsTreeNode *> childrenNodes = mTreeNode->childrenNodes();
158 for ( const QgsSettingsTreeNode *childNode : childrenNodes )
159 {
160 addChildForTreeNode( childNode );
161 }
162 const QList<const QgsSettingsEntryBase *> childrenSettings = mTreeNode->childrenSettings();
163 for ( const QgsSettingsEntryBase *setting : childrenSettings )
164 {
165 addChildForSetting( setting );
166 }
167}
168
170
171
172
174 : QAbstractItemModel( parent )
175{
176 mRootNode = QgsSettingsTreeModelNodeData::createRootNodeData( rootNode, this );
177
178 QPalette pal = qApp->palette();
179 mEditedColorBack = pal.color( QPalette::Active, QPalette::Dark );
180 mEditedColorFore = pal.color( QPalette::Active, QPalette::BrightText );
181 mNotSetColor = pal.color( QPalette::Disabled, QPalette::WindowText );
182}
183
185{
186 //delete mRootNode;
187}
188
190{
191 beginResetModel();
192 mRootNode->applyChanges();
193 endResetModel();
194}
195
196QgsSettingsTreeModelNodeData *QgsSettingsTreeModel::index2node( const QModelIndex &index ) const
197{
198 if ( !index.isValid() )
199 return mRootNode;
200
201 QObject *obj = reinterpret_cast<QObject *>( index.internalPointer() );
202 return qobject_cast<QgsSettingsTreeModelNodeData *>( obj );
203}
204
205QModelIndex QgsSettingsTreeModel::node2index( QgsSettingsTreeModelNodeData *node ) const
206{
207 if ( !node || !node->parent() )
208 return QModelIndex(); // this is the only root item -> invalid index
209
210 QModelIndex parentIndex = node2index( node->parent() );
211
212 int row = node->parent()->children().indexOf( node );
213 Q_ASSERT( row >= 0 );
214 return index( row, static_cast<int>( Column::Name ), parentIndex );
215}
216
217
218QModelIndex QgsSettingsTreeModel::index( int row, int column, const QModelIndex &parent ) const
219{
220 if ( column < 0 || column >= columnCount( parent ) ||
221 row < 0 || row >= rowCount( parent ) )
222 return QModelIndex();
223
224 QgsSettingsTreeModelNodeData *n = index2node( parent );
225 if ( !n )
226 return QModelIndex(); // have no children
227
228
229 return createIndex( row, column, static_cast<QObject *>( n->children().at( row ) ) );
230}
231
232QModelIndex QgsSettingsTreeModel::parent( const QModelIndex &child ) const
233{
234 if ( !child.isValid() )
235 return QModelIndex();
236
237 if ( QgsSettingsTreeModelNodeData *n = index2node( child ) )
238 {
239 return indexOfParentSettingsTreeNode( n->parent() ); // must not be null
240 }
241 else
242 {
243 Q_ASSERT( false ); // no other node types!
244 return QModelIndex();
245 }
246}
247
248QModelIndex QgsSettingsTreeModel::indexOfParentSettingsTreeNode( QgsSettingsTreeModelNodeData *parentNode ) const
249{
250 Q_ASSERT( parentNode );
251
252 const QgsSettingsTreeModelNodeData *grandParentNode = parentNode->parent();
253 if ( !grandParentNode )
254 return QModelIndex(); // root node -> invalid index
255
256 int row = grandParentNode->children().indexOf( parentNode );
257 Q_ASSERT( row >= 0 );
258
259 return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
260}
261
262int QgsSettingsTreeModel::rowCount( const QModelIndex &parent ) const
263{
264 QgsSettingsTreeModelNodeData *n = index2node( parent );
265 if ( !n )
266 return 0;
267
268 return n->children().count();
269}
270
271int QgsSettingsTreeModel::columnCount( const QModelIndex &parent ) const
272{
273 Q_UNUSED( parent )
274 return 3;
275}
276
277QVariant QgsSettingsTreeModel::data( const QModelIndex &index, int role ) const
278{
279 if ( !index.isValid() || index.column() > columnCount( index ) )
280 return QVariant();
281
282 QgsSettingsTreeModelNodeData *node = index2node( index );
283
284 if ( role == Qt::ForegroundRole && node->type() == QgsSettingsTreeModelNodeData::Type::Setting )
285 {
286 if ( !node->exists() )
287 {
288 // settings not yet set
289 return mNotSetColor;
290 }
291
292 if ( node->isEdited() &&
293 ( node->setting()->settingsType() != Qgis::SettingsType::Color || index.column() != static_cast<int>( Column::Value ) ) )
294 {
295 return mEditedColorFore;
296 }
297 }
298
299 if ( role == Qt::BackgroundRole && node->type() == QgsSettingsTreeModelNodeData::Type::Setting )
300 {
301 // background for edited settings (except colors)
302 if ( node->isEdited() &&
303 ( node->setting()->settingsType() != Qgis::SettingsType::Color || index.column() != static_cast<int>( Column::Value ) ) )
304 {
305 return mEditedColorBack;
306 }
307 }
308
309 switch ( static_cast<Column>( index.column() ) )
310 {
311 case Column::Name:
312 {
313 if ( role == Qt::DisplayRole || role == Qt::EditRole )
314 {
315 return node->name();
316 }
317 break;
318 }
319
320 case Column::Value:
321 {
322 if ( role == Qt::CheckStateRole )
323 {
324 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting &&
325 node->setting()->settingsType() == Qgis::SettingsType::Bool )
326 {
327 // special handling of bool setting to show combobox
328 return node->value().toBool() ? Qt::Checked : Qt::Unchecked;
329 }
330 }
331 if ( role == Qt::DisplayRole || role == Qt::EditRole )
332 {
333 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting &&
334 node->setting()->settingsType() == Qgis::SettingsType::Bool )
335 {
336 // special handling of bool setting to show combobox
337 return QString();
338 }
339 else
340 {
341 return node->value();
342 }
343 }
344 else if ( role == Qt::BackgroundRole )
345 {
346 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting )
347 {
348 switch ( node->setting()->settingsType() )
349 {
359 break;
360
362 return node->value();
363 }
364 }
365 }
366 break;
367 }
368
370 {
371 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting )
372 {
373 if ( role == Qt::DisplayRole || role == Qt::EditRole )
374 {
375 return node->setting()->description();
376 }
377 }
378 break;
379 }
380
381 default:
382 break;
383 }
384 return QVariant();
385}
386
387
388QVariant QgsSettingsTreeModel::headerData( int section, Qt::Orientation orientation, int role ) const
389{
390 if ( orientation == Qt::Orientation::Horizontal && role == Qt::DisplayRole )
391 {
392 switch ( static_cast<Column>( section ) )
393 {
394 case Column::Name:
395 return tr( "Name" );
396 case Column::Value:
397 return tr( "Value" );
399 return tr( "Description" );
400 };
401 }
402
403 return QVariant();
404}
405
406Qt::ItemFlags QgsSettingsTreeModel::flags( const QModelIndex &index ) const
407{
408 if ( index.column() == static_cast<int>( Column::Value ) )
409 {
410 QgsSettingsTreeModelNodeData *nodeData = index2node( index );
411 if ( nodeData->type() == QgsSettingsTreeModelNodeData::Type::Setting )
412 {
413 if ( nodeData->setting()->settingsType() == Qgis::SettingsType::Bool )
414 {
415 return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled;
416 }
417 else
418 {
419 return Qt::ItemIsEnabled | Qt::ItemIsEditable;
420 }
421 }
422 }
423 else
424 {
425 return Qt::ItemIsEnabled;
426 }
427 return Qt::NoItemFlags;
428}
429
430bool QgsSettingsTreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
431{
432 if ( index.column() == static_cast<int>( Column::Value ) )
433 {
434 if ( role == Qt::EditRole || role == Qt::CheckStateRole )
435 {
436 QgsSettingsTreeModelNodeData *nodeData = index2node( index );
437 if ( nodeData->type() == QgsSettingsTreeModelNodeData::Type::Setting )
438 {
439 if ( role == Qt::CheckStateRole )
440 {
441 Q_ASSERT( nodeData->setting()->settingsType() == Qgis::SettingsType::Bool );
442 nodeData->setValue( value == Qt::Checked ? true : false );
443 }
444 else
445 {
446 nodeData->setValue( value );
447 }
448 emit dataChanged( index.siblingAtColumn( 0 ), index.siblingAtColumn( columnCount( index.parent() ) - 1 ) );
449 return true;
450 }
451 }
452 }
453 return false;
454}
455
456
458
459
460QgsSettingsTreeItemDelegate::QgsSettingsTreeItemDelegate( QgsSettingsTreeModel *model, QObject *parent )
461 : QItemDelegate( parent )
462 , mModel( model )
463{
464}
465
466QWidget *QgsSettingsTreeItemDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
467{
468 Q_UNUSED( option )
469 if ( static_cast<QgsSettingsTreeModel::Column>( index.column() ) == QgsSettingsTreeModel::Column::Value )
470 {
471 QModelIndex sourceIndex = index;
472 const QgsSettingsTreeProxyModel *proxyModel = qobject_cast<const QgsSettingsTreeProxyModel *>( index.model() );
473 if ( proxyModel )
474 {
475 sourceIndex = proxyModel->mapToSource( index );
476 }
477 QgsSettingsTreeModelNodeData *nodeData = mModel->index2node( sourceIndex );
478 if ( nodeData->type() == QgsSettingsTreeModelNodeData::Type::Setting )
479 {
480 return QgsGui::settingsEditorWidgetRegistry()->createEditor( nodeData->setting(), nodeData->namedParentNodes(), parent );
481 }
482 }
483 return nullptr;
484}
485
486void QgsSettingsTreeItemDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
487{
489 if ( eww )
490 {
491 const QVariant value = index.model()->data( index, Qt::DisplayRole );
492 eww->setWidgetFromVariant( value );
493 }
494}
495
496void QgsSettingsTreeItemDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
497{
499 if ( eww )
500 model->setData( index, eww->variantValueFromWidget(), Qt::EditRole );
501}
502
504
505
506
507
508
510 : QSortFilterProxyModel( parent )
511{
512 mSourceModel = new QgsSettingsTreeModel( rootNode, parent );
513 QSortFilterProxyModel::setSourceModel( mSourceModel );
514}
515
516void QgsSettingsTreeProxyModel::setFilterText( const QString &filterText )
517{
518 if ( filterText == mFilterText )
519 return;
520
521 mFilterText = filterText;
522 invalidateFilter();
523}
524
525bool QgsSettingsTreeProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
526{
527 QgsSettingsTreeModelNodeData *node = mSourceModel->index2node( mSourceModel->index( source_row, static_cast<int>( QgsSettingsTreeModel::Column::Name ), source_parent ) );
528 return nodeShown( node );
529}
530
531bool QgsSettingsTreeProxyModel::nodeShown( QgsSettingsTreeModelNodeData *node ) const
532{
533 if ( !node )
534 return false;
535 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting )
536 {
537 if ( node->name().contains( mFilterText, Qt::CaseInsensitive ) )
538 return true;
539
540 // also returns settings for which the parent nodes have a match
541 QModelIndex index = mSourceModel->node2index( node ).parent();
542 while ( ( index.isValid() ) )
543 {
544 QgsSettingsTreeModelNodeData *parentNode = mSourceModel->index2node( mSourceModel->index( index.row(), static_cast<int>( QgsSettingsTreeModel::Column::Name ), index.parent() ) );
545 if ( parentNode->name().contains( mFilterText, Qt::CaseInsensitive ) )
546 return true;
547
548 index = index.parent();
549 }
550 return false;
551 }
552 else
553 {
554 // show all children if name of node matches
555 if ( node->name().contains( mFilterText, Qt::CaseInsensitive ) )
556 return true;
557
558 const auto constChildren = node->children();
559 for ( QgsSettingsTreeModelNodeData *child : constChildren )
560 {
561 if ( nodeShown( child ) )
562 {
563 return true;
564 }
565 }
566 return false;
567 }
568}
569
@ NamedList
Named List Node.
@ Variant
Generic variant.
@ Custom
Custom implementation.
@ StringList
List of strings.
@ EnumFlag
Enum or Flag.
@ VariantMap
Map of strings.
@ Double
Double precision number.
static QgsSettingsEditorWidgetRegistry * settingsEditorWidgetRegistry()
Returns the registry of settings editors.
Definition qgsgui.cpp:204
QWidget * createEditor(const QgsSettingsEntryBase *setting, const QStringList &dynamicKeyPartList, QWidget *parent=nullptr) const
Creates an editor widget for the given setting using the corresponding registered wrapper.
Base class for settings editor wrappers.
static QgsSettingsEditorWidgetWrapper * fromWidget(const QWidget *widget)
Creates a wrapper from the definition stored in a widget created by createEditor()
virtual bool setWidgetFromVariant(const QVariant &value) const =0
Sets the value of the widget The wrapper must be configured before calling this medthod.
virtual QVariant variantValueFromWidget() const =0
Returns the value from the widget as a variant The wrapper must be configured before calling this med...
Represent settings entry and provides methods for reading and writing settings values.
bool exists(const QString &dynamicKeyPart=QString()) const
Returns true if the settings is contained in the underlying QSettings.
QString name() const
Returns the name of the settings.
QString definitionKey() const
Returns settings entry defining key.
QVariant valueAsVariant(const QString &dynamicKeyPart=QString()) const
Returns settings value with.
QgsSettingsTreeModel is a tree model for the settings tree.
bool setData(const QModelIndex &index, const QVariant &value, int role) override
QVariant data(const QModelIndex &index, int role) const override
QModelIndex index(int row, int column, const QModelIndex &parent) const override
Qt::ItemFlags flags(const QModelIndex &index) const override
QModelIndex node2index(QgsSettingsTreeModelNodeData *node) const
Returns the index from the settings tree node.
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
void applyChanges()
Apply pending changes in the model to the corresponding settings.
int columnCount(const QModelIndex &parent) const override
QgsSettingsTreeModelNodeData * index2node(const QModelIndex &index) const
Returns settings tree node for given index or the root node if the index is invalid.
QgsSettingsTreeModel(QgsSettingsTreeNode *rootNode=nullptr, QObject *parent=nullptr)
Constructor.
QModelIndex parent(const QModelIndex &child) const override
int rowCount(const QModelIndex &parent) const override
QgsSettingsTreeNamedListNode is a named list tree node for the settings tree to help organizing and i...
QStringList items(const QStringList &parentsNamedItems=QStringList()) const
Returns the list of items.
QgsSettingsTreeNode is a tree node for the settings tree to help organizing and introspecting the tre...
Qgis::SettingsTreeNodeType type() const
Returns the type of node.
Qgis::SettingsTreeNodeType mType
QString key() const
Returns the key of the node (without its parents)
QgsSettingsTreeProxyModel allows filtering the settings tree.
QgsSettingsTreeProxyModel(QgsSettingsTreeNode *rootNode=nullptr, QObject *parent=nullptr)
Constructor.
void setFilterText(const QString &filterText=QString())
Sets the filter text.
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
#define QgsDebugError(str)
Definition qgslogger.h:38