QGIS API Documentation 3.41.0-Master (cea29feecf2)
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
173 : QAbstractItemModel( parent )
174{
175 mRootNode = QgsSettingsTreeModelNodeData::createRootNodeData( rootNode, this );
176
177 QPalette pal = qApp->palette();
178 mEditedColorBack = pal.color( QPalette::Active, QPalette::Dark );
179 mEditedColorFore = pal.color( QPalette::Active, QPalette::BrightText );
180 mNotSetColor = pal.color( QPalette::Disabled, QPalette::WindowText );
181}
182
184{
185 //delete mRootNode;
186}
187
189{
190 beginResetModel();
191 mRootNode->applyChanges();
192 endResetModel();
193}
194
195QgsSettingsTreeModelNodeData *QgsSettingsTreeModel::index2node( const QModelIndex &index ) const
196{
197 if ( !index.isValid() )
198 return mRootNode;
199
200 QObject *obj = reinterpret_cast<QObject *>( index.internalPointer() );
201 return qobject_cast<QgsSettingsTreeModelNodeData *>( obj );
202}
203
204QModelIndex QgsSettingsTreeModel::node2index( QgsSettingsTreeModelNodeData *node ) const
205{
206 if ( !node || !node->parent() )
207 return QModelIndex(); // this is the only root item -> invalid index
208
209 QModelIndex parentIndex = node2index( node->parent() );
210
211 int row = node->parent()->children().indexOf( node );
212 Q_ASSERT( row >= 0 );
213 return index( row, static_cast<int>( Column::Name ), parentIndex );
214}
215
216
217QModelIndex QgsSettingsTreeModel::index( int row, int column, const QModelIndex &parent ) const
218{
219 if ( column < 0 || column >= columnCount( parent ) || row < 0 || row >= rowCount( parent ) )
220 return QModelIndex();
221
222 QgsSettingsTreeModelNodeData *n = index2node( parent );
223 if ( !n )
224 return QModelIndex(); // have no children
225
226
227 return createIndex( row, column, static_cast<QObject *>( n->children().at( row ) ) );
228}
229
230QModelIndex QgsSettingsTreeModel::parent( const QModelIndex &child ) const
231{
232 if ( !child.isValid() )
233 return QModelIndex();
234
235 if ( QgsSettingsTreeModelNodeData *n = index2node( child ) )
236 {
237 return indexOfParentSettingsTreeNode( n->parent() ); // must not be null
238 }
239 else
240 {
241 Q_ASSERT( false ); // no other node types!
242 return QModelIndex();
243 }
244}
245
246QModelIndex QgsSettingsTreeModel::indexOfParentSettingsTreeNode( QgsSettingsTreeModelNodeData *parentNode ) const
247{
248 Q_ASSERT( parentNode );
249
250 const QgsSettingsTreeModelNodeData *grandParentNode = parentNode->parent();
251 if ( !grandParentNode )
252 return QModelIndex(); // root node -> invalid index
253
254 int row = grandParentNode->children().indexOf( parentNode );
255 Q_ASSERT( row >= 0 );
256
257 return createIndex( row, 0, static_cast<QObject *>( parentNode ) );
258}
259
260int QgsSettingsTreeModel::rowCount( const QModelIndex &parent ) const
261{
262 QgsSettingsTreeModelNodeData *n = index2node( parent );
263 if ( !n )
264 return 0;
265
266 return n->children().count();
267}
268
269int QgsSettingsTreeModel::columnCount( const QModelIndex &parent ) const
270{
271 Q_UNUSED( parent )
272 return 3;
273}
274
275QVariant QgsSettingsTreeModel::data( const QModelIndex &index, int role ) const
276{
277 if ( !index.isValid() || index.column() > columnCount( index ) )
278 return QVariant();
279
280 QgsSettingsTreeModelNodeData *node = index2node( index );
281
282 if ( role == Qt::ForegroundRole && node->type() == QgsSettingsTreeModelNodeData::Type::Setting )
283 {
284 if ( !node->exists() )
285 {
286 // settings not yet set
287 return mNotSetColor;
288 }
289
290 if ( node->isEdited() && ( node->setting()->settingsType() != Qgis::SettingsType::Color || index.column() != static_cast<int>( Column::Value ) ) )
291 {
292 return mEditedColorFore;
293 }
294 }
295
296 if ( role == Qt::BackgroundRole && node->type() == QgsSettingsTreeModelNodeData::Type::Setting )
297 {
298 // background for edited settings (except colors)
299 if ( node->isEdited() && ( node->setting()->settingsType() != Qgis::SettingsType::Color || index.column() != static_cast<int>( Column::Value ) ) )
300 {
301 return mEditedColorBack;
302 }
303 }
304
305 switch ( static_cast<Column>( index.column() ) )
306 {
307 case Column::Name:
308 {
309 if ( role == Qt::DisplayRole || role == Qt::EditRole )
310 {
311 return node->name();
312 }
313 break;
314 }
315
316 case Column::Value:
317 {
318 if ( role == Qt::CheckStateRole )
319 {
320 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting && node->setting()->settingsType() == Qgis::SettingsType::Bool )
321 {
322 // special handling of bool setting to show combobox
323 return node->value().toBool() ? Qt::Checked : Qt::Unchecked;
324 }
325 }
326 if ( role == Qt::DisplayRole || role == Qt::EditRole )
327 {
328 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting && node->setting()->settingsType() == Qgis::SettingsType::Bool )
329 {
330 // special handling of bool setting to show combobox
331 return QString();
332 }
333 else
334 {
335 return node->value();
336 }
337 }
338 else if ( role == Qt::BackgroundRole )
339 {
340 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting )
341 {
342 switch ( node->setting()->settingsType() )
343 {
353 break;
354
356 return node->value();
357 }
358 }
359 }
360 break;
361 }
362
364 {
365 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting )
366 {
367 if ( role == Qt::DisplayRole || role == Qt::EditRole )
368 {
369 return node->setting()->description();
370 }
371 }
372 break;
373 }
374
375 default:
376 break;
377 }
378 return QVariant();
379}
380
381
382QVariant QgsSettingsTreeModel::headerData( int section, Qt::Orientation orientation, int role ) const
383{
384 if ( orientation == Qt::Orientation::Horizontal && role == Qt::DisplayRole )
385 {
386 switch ( static_cast<Column>( section ) )
387 {
388 case Column::Name:
389 return tr( "Name" );
390 case Column::Value:
391 return tr( "Value" );
393 return tr( "Description" );
394 };
395 }
396
397 return QVariant();
398}
399
400Qt::ItemFlags QgsSettingsTreeModel::flags( const QModelIndex &index ) const
401{
402 if ( index.column() == static_cast<int>( Column::Value ) )
403 {
404 QgsSettingsTreeModelNodeData *nodeData = index2node( index );
405 if ( nodeData->type() == QgsSettingsTreeModelNodeData::Type::Setting )
406 {
407 if ( nodeData->setting()->settingsType() == Qgis::SettingsType::Bool )
408 {
409 return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled;
410 }
411 else
412 {
413 return Qt::ItemIsEnabled | Qt::ItemIsEditable;
414 }
415 }
416 }
417 else
418 {
419 return Qt::ItemIsEnabled;
420 }
421 return Qt::NoItemFlags;
422}
423
424bool QgsSettingsTreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
425{
426 if ( index.column() == static_cast<int>( Column::Value ) )
427 {
428 if ( role == Qt::EditRole || role == Qt::CheckStateRole )
429 {
430 QgsSettingsTreeModelNodeData *nodeData = index2node( index );
431 if ( nodeData->type() == QgsSettingsTreeModelNodeData::Type::Setting )
432 {
433 if ( role == Qt::CheckStateRole )
434 {
435 Q_ASSERT( nodeData->setting()->settingsType() == Qgis::SettingsType::Bool );
436 nodeData->setValue( value == Qt::Checked ? true : false );
437 }
438 else
439 {
440 nodeData->setValue( value );
441 }
442 emit dataChanged( index.siblingAtColumn( 0 ), index.siblingAtColumn( columnCount( index.parent() ) - 1 ) );
443 return true;
444 }
445 }
446 }
447 return false;
448}
449
450
452
453
454QgsSettingsTreeItemDelegate::QgsSettingsTreeItemDelegate( QgsSettingsTreeModel *model, QObject *parent )
455 : QItemDelegate( parent )
456 , mModel( model )
457{
458}
459
460QWidget *QgsSettingsTreeItemDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
461{
462 Q_UNUSED( option )
463 if ( static_cast<QgsSettingsTreeModel::Column>( index.column() ) == QgsSettingsTreeModel::Column::Value )
464 {
465 QModelIndex sourceIndex = index;
466 const QgsSettingsTreeProxyModel *proxyModel = qobject_cast<const QgsSettingsTreeProxyModel *>( index.model() );
467 if ( proxyModel )
468 {
469 sourceIndex = proxyModel->mapToSource( index );
470 }
471 QgsSettingsTreeModelNodeData *nodeData = mModel->index2node( sourceIndex );
472 if ( nodeData->type() == QgsSettingsTreeModelNodeData::Type::Setting )
473 {
474 return QgsGui::settingsEditorWidgetRegistry()->createEditor( nodeData->setting(), nodeData->namedParentNodes(), parent );
475 }
476 }
477 return nullptr;
478}
479
480void QgsSettingsTreeItemDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
481{
483 if ( eww )
484 {
485 const QVariant value = index.model()->data( index, Qt::DisplayRole );
486 eww->setWidgetFromVariant( value );
487 }
488}
489
490void QgsSettingsTreeItemDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
491{
493 if ( eww )
494 model->setData( index, eww->variantValueFromWidget(), Qt::EditRole );
495}
496
498
499
501 : QSortFilterProxyModel( parent )
502{
503 mSourceModel = new QgsSettingsTreeModel( rootNode, parent );
504 QSortFilterProxyModel::setSourceModel( mSourceModel );
505}
506
507void QgsSettingsTreeProxyModel::setFilterText( const QString &filterText )
508{
509 if ( filterText == mFilterText )
510 return;
511
512 mFilterText = filterText;
513 invalidateFilter();
514}
515
516bool QgsSettingsTreeProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
517{
518 QgsSettingsTreeModelNodeData *node = mSourceModel->index2node( mSourceModel->index( source_row, static_cast<int>( QgsSettingsTreeModel::Column::Name ), source_parent ) );
519 return nodeShown( node );
520}
521
522bool QgsSettingsTreeProxyModel::nodeShown( QgsSettingsTreeModelNodeData *node ) const
523{
524 if ( !node )
525 return false;
526 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting )
527 {
528 if ( node->name().contains( mFilterText, Qt::CaseInsensitive ) )
529 return true;
530
531 // also returns settings for which the parent nodes have a match
532 QModelIndex index = mSourceModel->node2index( node ).parent();
533 while ( ( index.isValid() ) )
534 {
535 QgsSettingsTreeModelNodeData *parentNode = mSourceModel->index2node( mSourceModel->index( index.row(), static_cast<int>( QgsSettingsTreeModel::Column::Name ), index.parent() ) );
536 if ( parentNode->name().contains( mFilterText, Qt::CaseInsensitive ) )
537 return true;
538
539 index = index.parent();
540 }
541 return false;
542 }
543 else
544 {
545 // show all children if name of node matches
546 if ( node->name().contains( mFilterText, Qt::CaseInsensitive ) )
547 return true;
548
549 const auto constChildren = node->children();
550 for ( QgsSettingsTreeModelNodeData *child : constChildren )
551 {
552 if ( nodeShown( child ) )
553 {
554 return true;
555 }
556 }
557 return false;
558 }
559}
@ 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:205
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