QGIS API Documentation 3.99.0-Master (2fe06baccd8)
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
17
18#include "qgsgui.h"
19#include "qgslogger.h"
22#include "qgssettingsentry.h"
23#include "qgssettingstreenode.h"
24
25#include <QFont>
26#include <QGuiApplication>
27
28#include "moc_qgssettingstreemodel.cpp"
29
31
32
33QgsSettingsTreeModelNodeData *QgsSettingsTreeModelNodeData::createRootNodeData( const QgsSettingsTreeNode *rootNode, QObject *parent = nullptr )
34{
35 QgsSettingsTreeModelNodeData *nodeData = new QgsSettingsTreeModelNodeData( parent );
36 nodeData->mType = Type::RootNode;
37 nodeData->mName = rootNode->key();
38 nodeData->mTreeNode = rootNode;
39 nodeData->fillChildren();
40 return nodeData;
41}
42
43void QgsSettingsTreeModelNodeData::applyChanges()
44{
45 switch ( type() )
46 {
47 case Type::NamedListTreeNode:
48 case Type::RootNode:
49 case Type::TreeNode:
50 case Type::NamedListItem:
51 {
52 QList<QgsSettingsTreeModelNodeData *>::iterator it = mChildren.begin();
53 for ( ; it != mChildren.end(); ++it )
54 ( *it )->applyChanges();
55 break;
56 }
57 case Type::Setting:
58 {
59 if ( isEdited() )
60 {
61 setting()->setVariantValue( mValue, mNamedParentNodes );
62 }
63 break;
64 }
65 }
66}
67
68bool QgsSettingsTreeModelNodeData::setValue( const QVariant &value )
69{
70 Q_ASSERT( mType == Type::Setting );
71 if ( !value.isValid() && mValue.isValid() )
72 {
73 mExists = false;
74 mIsEdited = true;
75 mValue = QVariant();
76 }
77 else if ( !mValue.isValid() || mValue != value )
78 {
79 mValue = value;
80 mIsEdited = ( value != mOriginalValue );
81 }
82 // TODO: check the value of setting is fulfilling the settings' contsraints ?
83 return true;
84}
85
86
87void QgsSettingsTreeModelNodeData::addChildForTreeNode( const QgsSettingsTreeNode *node )
88{
89 QgsSettingsTreeModelNodeData *nodeData = new QgsSettingsTreeModelNodeData( this );
90 nodeData->mParent = this;
91 nodeData->mNamedParentNodes = mNamedParentNodes;
92 nodeData->mName = node->key();
93 nodeData->mTreeNode = node;
95 {
96 nodeData->mType = Type::NamedListTreeNode;
97 const QgsSettingsTreeNamedListNode *nln = qgis::down_cast<const QgsSettingsTreeNamedListNode *>( node );
98 const QStringList items = nln->items( mNamedParentNodes );
99 for ( const QString &item : items )
100 {
101 nodeData->addChildForNamedListItemNode( item, nln );
102 }
103 }
104 else
105 {
106 nodeData->mType = Type::TreeNode;
107 nodeData->fillChildren();
108 }
109 mChildren.append( nodeData );
110}
111
112void QgsSettingsTreeModelNodeData::addChildForNamedListItemNode( const QString &item, const QgsSettingsTreeNamedListNode *namedListNode )
113{
114 QgsSettingsTreeModelNodeData *nodeData = new QgsSettingsTreeModelNodeData( this );
115 nodeData->mType = Type::NamedListItem;
116 nodeData->mParent = this;
117 nodeData->mNamedParentNodes = mNamedParentNodes;
118 nodeData->mNamedParentNodes.append( item );
119 nodeData->mName = item;
120 nodeData->mTreeNode = namedListNode;
121 nodeData->fillChildren();
122 mChildren.append( nodeData );
123}
124
125void QgsSettingsTreeModelNodeData::addChildForSetting( const QgsSettingsEntryBase *setting )
126{
127 QgsSettingsTreeModelNodeData *nodeData = new QgsSettingsTreeModelNodeData( this );
128 nodeData->mType = Type::Setting;
129 nodeData->mParent = this;
130 nodeData->mNamedParentNodes = mNamedParentNodes;
131 nodeData->mSetting = setting;
132 nodeData->mName = setting->name();
133 nodeData->mValue = setting->valueAsVariant( mNamedParentNodes );
134 nodeData->mOriginalValue = nodeData->mValue;
135 nodeData->mExists = setting->exists( mNamedParentNodes );
136
137 switch ( mNamedParentNodes.count() )
138 {
139 case 1:
140 QgsDebugMsgLevel( QString( "getting %1 with %2" ).arg( setting->definitionKey(), mNamedParentNodes.at( 0 ) ), 3 );
141 break;
142 case 2:
143 QgsDebugMsgLevel( QString( "getting %1 with %2 and %3" ).arg( setting->definitionKey(), mNamedParentNodes.at( 0 ), mNamedParentNodes.at( 1 ) ), 3 );
144 break;
145 case 0:
146 QgsDebugMsgLevel( QString( "getting %1" ).arg( setting->definitionKey() ), 3 );
147 break;
148 default:
149 Q_ASSERT( false );
150 QgsDebugError( QString( "Not handling that many named parent nodes for %1" ).arg( setting->definitionKey() ) );
151 break;
152 }
153
154 mChildren.append( nodeData );
155}
156
157void QgsSettingsTreeModelNodeData::fillChildren()
158{
159 const QList<QgsSettingsTreeNode *> childrenNodes = mTreeNode->childrenNodes();
160 for ( const QgsSettingsTreeNode *childNode : childrenNodes )
161 {
162 addChildForTreeNode( childNode );
163 }
164 const QList<const QgsSettingsEntryBase *> childrenSettings = mTreeNode->childrenSettings();
165 for ( const QgsSettingsEntryBase *setting : childrenSettings )
166 {
167 addChildForSetting( setting );
168 }
169}
170
172
173
175 : QAbstractItemModel( parent )
176{
177 mRootNode = QgsSettingsTreeModelNodeData::createRootNodeData( rootNode, this );
178
179 QPalette pal = qApp->palette();
180 mEditedColorBack = pal.color( QPalette::Active, QPalette::Dark );
181 mEditedColorFore = pal.color( QPalette::Active, QPalette::BrightText );
182 mNotSetColor = pal.color( QPalette::Disabled, QPalette::WindowText );
183}
184
186{
187 //delete mRootNode;
188}
189
191{
192 beginResetModel();
193 mRootNode->applyChanges();
194 endResetModel();
195}
196
197QgsSettingsTreeModelNodeData *QgsSettingsTreeModel::index2node( const QModelIndex &index ) const
198{
199 if ( !index.isValid() )
200 return mRootNode;
201
202 QObject *obj = reinterpret_cast<QObject *>( index.internalPointer() );
203 return qobject_cast<QgsSettingsTreeModelNodeData *>( obj );
204}
205
206QModelIndex QgsSettingsTreeModel::node2index( QgsSettingsTreeModelNodeData *node ) const
207{
208 if ( !node || !node->parent() )
209 return QModelIndex(); // this is the only root item -> invalid index
210
211 QModelIndex parentIndex = node2index( node->parent() );
212
213 int row = node->parent()->children().indexOf( node );
214 Q_ASSERT( row >= 0 );
215 return index( row, static_cast<int>( Column::Name ), parentIndex );
216}
217
218
219QModelIndex QgsSettingsTreeModel::index( int row, int column, const QModelIndex &parent ) const
220{
221 if ( column < 0 || column >= columnCount( parent ) || 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() && ( node->setting()->settingsType() != Qgis::SettingsType::Color || index.column() != static_cast<int>( Column::Value ) ) )
293 {
294 return mEditedColorFore;
295 }
296 }
297
298 if ( role == Qt::BackgroundRole && node->type() == QgsSettingsTreeModelNodeData::Type::Setting )
299 {
300 // background for edited settings (except colors)
301 if ( node->isEdited() && ( node->setting()->settingsType() != Qgis::SettingsType::Color || index.column() != static_cast<int>( Column::Value ) ) )
302 {
303 return mEditedColorBack;
304 }
305 }
306
307 switch ( static_cast<Column>( index.column() ) )
308 {
309 case Column::Name:
310 {
311 if ( role == Qt::DisplayRole || role == Qt::EditRole )
312 {
313 return node->name();
314 }
315 break;
316 }
317
318 case Column::Value:
319 {
320 if ( role == Qt::CheckStateRole )
321 {
322 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting && node->setting()->settingsType() == Qgis::SettingsType::Bool )
323 {
324 // special handling of bool setting to show combobox
325 return node->value().toBool() ? Qt::Checked : Qt::Unchecked;
326 }
327 }
328 if ( role == Qt::DisplayRole || role == Qt::EditRole )
329 {
330 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting && node->setting()->settingsType() == Qgis::SettingsType::Bool )
331 {
332 // special handling of bool setting to show combobox
333 return QString();
334 }
335 else
336 {
337 return node->value();
338 }
339 }
340 else if ( role == Qt::BackgroundRole )
341 {
342 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting )
343 {
344 switch ( node->setting()->settingsType() )
345 {
355 break;
356
358 return node->value();
359 }
360 }
361 }
362 break;
363 }
364
366 {
367 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting )
368 {
369 if ( role == Qt::DisplayRole || role == Qt::EditRole )
370 {
371 return node->setting()->description();
372 }
373 }
374 break;
375 }
376
377 default:
378 break;
379 }
380 return QVariant();
381}
382
383
384QVariant QgsSettingsTreeModel::headerData( int section, Qt::Orientation orientation, int role ) const
385{
386 if ( orientation == Qt::Orientation::Horizontal && role == Qt::DisplayRole )
387 {
388 switch ( static_cast<Column>( section ) )
389 {
390 case Column::Name:
391 return tr( "Name" );
392 case Column::Value:
393 return tr( "Value" );
395 return tr( "Description" );
396 };
397 }
398
399 return QVariant();
400}
401
402Qt::ItemFlags QgsSettingsTreeModel::flags( const QModelIndex &index ) const
403{
404 if ( index.column() == static_cast<int>( Column::Value ) )
405 {
406 QgsSettingsTreeModelNodeData *nodeData = index2node( index );
407 if ( nodeData->type() == QgsSettingsTreeModelNodeData::Type::Setting )
408 {
409 if ( nodeData->setting()->settingsType() == Qgis::SettingsType::Bool )
410 {
411 return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled;
412 }
413 else
414 {
415 return Qt::ItemIsEnabled | Qt::ItemIsEditable;
416 }
417 }
418 }
419 else
420 {
421 return Qt::ItemIsEnabled;
422 }
423 return Qt::NoItemFlags;
424}
425
426bool QgsSettingsTreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
427{
428 if ( index.column() == static_cast<int>( Column::Value ) )
429 {
430 if ( role == Qt::EditRole || role == Qt::CheckStateRole )
431 {
432 QgsSettingsTreeModelNodeData *nodeData = index2node( index );
433 if ( nodeData->type() == QgsSettingsTreeModelNodeData::Type::Setting )
434 {
435 if ( role == Qt::CheckStateRole )
436 {
437 Q_ASSERT( nodeData->setting()->settingsType() == Qgis::SettingsType::Bool );
438 nodeData->setValue( value == Qt::Checked ? true : false );
439 }
440 else
441 {
442 nodeData->setValue( value );
443 }
444 emit dataChanged( index.siblingAtColumn( 0 ), index.siblingAtColumn( columnCount( index.parent() ) - 1 ) );
445 return true;
446 }
447 }
448 }
449 return false;
450}
451
452
454
455
456QgsSettingsTreeItemDelegate::QgsSettingsTreeItemDelegate( QgsSettingsTreeModel *model, QObject *parent )
457 : QItemDelegate( parent )
458 , mModel( model )
459{
460}
461
462QWidget *QgsSettingsTreeItemDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
463{
464 Q_UNUSED( option )
465 if ( static_cast<QgsSettingsTreeModel::Column>( index.column() ) == QgsSettingsTreeModel::Column::Value )
466 {
467 QModelIndex sourceIndex = index;
468 const QgsSettingsTreeProxyModel *proxyModel = qobject_cast<const QgsSettingsTreeProxyModel *>( index.model() );
469 if ( proxyModel )
470 {
471 sourceIndex = proxyModel->mapToSource( index );
472 }
473 QgsSettingsTreeModelNodeData *nodeData = mModel->index2node( sourceIndex );
474 if ( nodeData->type() == QgsSettingsTreeModelNodeData::Type::Setting )
475 {
476 return QgsGui::settingsEditorWidgetRegistry()->createEditor( nodeData->setting(), nodeData->namedParentNodes(), parent );
477 }
478 }
479 return nullptr;
480}
481
482void QgsSettingsTreeItemDelegate::setEditorData( QWidget *editor, const QModelIndex &index ) const
483{
485 if ( eww )
486 {
487 const QVariant value = index.model()->data( index, Qt::DisplayRole );
488 eww->setWidgetFromVariant( value );
489 }
490}
491
492void QgsSettingsTreeItemDelegate::setModelData( QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
493{
495 if ( eww )
496 model->setData( index, eww->variantValueFromWidget(), Qt::EditRole );
497}
498
500
501
503 : QSortFilterProxyModel( parent )
504{
505 mSourceModel = new QgsSettingsTreeModel( rootNode, parent );
506 QSortFilterProxyModel::setSourceModel( mSourceModel );
507}
508
509void QgsSettingsTreeProxyModel::setFilterText( const QString &filterText )
510{
511 if ( filterText == mFilterText )
512 return;
513
514 mFilterText = filterText;
515 invalidateFilter();
516}
517
518bool QgsSettingsTreeProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
519{
520 QgsSettingsTreeModelNodeData *node = mSourceModel->index2node( mSourceModel->index( source_row, static_cast<int>( QgsSettingsTreeModel::Column::Name ), source_parent ) );
521 return nodeShown( node );
522}
523
524bool QgsSettingsTreeProxyModel::nodeShown( QgsSettingsTreeModelNodeData *node ) const
525{
526 if ( !node )
527 return false;
528 if ( node->type() == QgsSettingsTreeModelNodeData::Type::Setting )
529 {
530 if ( node->name().contains( mFilterText, Qt::CaseInsensitive ) )
531 return true;
532
533 // also returns settings for which the parent nodes have a match
534 QModelIndex index = mSourceModel->node2index( node ).parent();
535 while ( ( index.isValid() ) )
536 {
537 QgsSettingsTreeModelNodeData *parentNode = mSourceModel->index2node( mSourceModel->index( index.row(), static_cast<int>( QgsSettingsTreeModel::Column::Name ), index.parent() ) );
538 if ( parentNode->name().contains( mFilterText, Qt::CaseInsensitive ) )
539 return true;
540
541 index = index.parent();
542 }
543 return false;
544 }
545 else
546 {
547 // show all children if name of node matches
548 if ( node->name().contains( mFilterText, Qt::CaseInsensitive ) )
549 return true;
550
551 const auto constChildren = node->children();
552 for ( QgsSettingsTreeModelNodeData *child : constChildren )
553 {
554 if ( nodeShown( child ) )
555 {
556 return true;
557 }
558 }
559 return false;
560 }
561}
@ NamedList
Named List Node.
Definition qgis.h:657
@ String
String.
Definition qgis.h:638
@ Variant
Generic variant.
Definition qgis.h:637
@ Custom
Custom implementation.
Definition qgis.h:636
@ Integer
Integer.
Definition qgis.h:642
@ StringList
List of strings.
Definition qgis.h:639
@ EnumFlag
Enum or Flag.
Definition qgis.h:644
@ Bool
Boolean.
Definition qgis.h:641
@ VariantMap
Map of strings.
Definition qgis.h:640
@ Color
Color.
Definition qgis.h:645
@ Double
Double precision number.
Definition qgis.h:643
static QgsSettingsEditorWidgetRegistry * settingsEditorWidgetRegistry()
Returns the registry of settings editors.
Definition qgsgui.cpp:216
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...
Represents a 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.
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
A named list tree node for the settings tree to help organizing and introspecting the tree.
QStringList items(const QStringList &parentsNamedItems=QStringList()) const
Returns the list of items.
A tree node for the settings tree to help organizing and introspecting the tree.
Qgis::SettingsTreeNodeType type() const
Returns the type of node.
QString key() const
Returns the key of the node (without its parents).
A proxy model which 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:61
#define QgsDebugError(str)
Definition qgslogger.h:57