From qtnode
Caution - this is a work in progress
#include "qsettingsmodel.h"
#include <QMessageBox>
QSettingsModel::QSettingsModel
(
QString filename, // file where the settings are stored
QSettings::Format format, // format of the file
QObject * parent // parent object (defaults to none)
)
: QAbstractItemModel(parent)
{
qsettings = new QSettings(filename,format,this);
qDebug( "qsettings object has been created to %s", filename.toAscii().data() );
init();
}
/*
** Define Constructor for external QSettings.
**
*/
QSettingsModel::QSettingsModel
(
QSettings & settings, // QSettings Object
QObject * parent // parent object (defaults to none)
)
: QAbstractItemModel(parent)
{
qsettings = &settings;
init();
}
/*
** Define Constructor for internal system QSettings
**
*/
QSettingsModel::QSettingsModel(QObject *parent)
: QAbstractItemModel(parent)
{
qsettings = new QSettings(this);
init();
}
/*
** Destructor
**
*/
QSettingsModel::~QSettingsModel()
{
}
/********************************************************************
**
** AbstractItemModel Interface
**
*/
QModelIndex QSettingsModel::index(int row, int col, const QModelIndex & parent) const
{
// qDebug( "index row %d col %d",row,col );
/*
** Make sure the requested colum and row is within range before doing anything.
**
*/
if( col < 0 || col >= 2 || row < 0 || row >= rowCount(parent) )
return QModelIndex();
/*
** Get a pointer to our parent and make a new node item for this row
** with the proper parent.
**
*/
QSettingsNode *p = static_cast<QSettingsNode*>(parent.internalPointer());
QSettingsNode *n = node(row, p);
Q_ASSERT(n);
/*
** Create a new index item and return that to the caller. Passing .n.
** insures that the index has the internalPointer set to the new
** node we created.
**
*/
return createIndex(row,col,n);
} // endQModelIndex QSettingsModel::index(int row, int col, const QModelIndex & parent) const
QModelIndex QSettingsModel::parent(const QModelIndex & child) const
{
/*
** Must have a valid index before we can find its parent.
**
*/
if (!child.isValid())
return QModelIndex();
/*
** Get a pointer to this indexs' internalPointer which points to the
** actual node. The node will contain a pointer to its parent node.
**
*/
QSettingsNode *node =
static_cast<QSettingsNode*>(child.internalPointer());
Q_ASSERT(node);
// qDebug( "QModelIndex parent child=%s",node->name.toAscii().data() );
/*
** Referring to this function parent() with our node will return
** our parent node.
**
*/
QSettingsNode *par = parent(node);
if( !par || (par == &root) )
{
// qDebug("we are at the root node");
return QModelIndex(); // parent is the root node
}
/*
** idx returns the index position of a particular node within
** a list
**
*/
int r = idx(par);
Q_ASSERT(r >= 0);
return createIndex(r, 0, par);
} // endQModelIndex QSettingsModel::parent(const QModelIndex & child) const
int QSettingsModel::rowCount(const QModelIndex & parent) const
{
/*
** The p pointer points to the parent.internalPointer which basically points
** to our private data structure containing the necessary details about this
** (the parent) node.
**
*/
QSettingsNode *p =
static_cast<QSettingsNode*>(parent.internalPointer());
/*
** The parent is a folder when we have no parent internalPointer, meaning
** we must refer to the root item, or we do have an internalPointer and it
** is identifying itself as a folder.
**
*/
bool isGroup = !p || p->isGroup; // no node pointer means that it is the root
/*
** If this parent has no internalPointer then we must refer to the root
** item.
**
*/
if( !p )
p = &root;
/*
** If this is a folder, and it is not populated, then populate it.
**
*/
if( isGroup && !p->populated ) // lazy population (only populates this parent)
populate(p);
// qDebug( "rowCount %d",p->children.count() );
/*
** Return the children count.
**
*/
return p->children.count();
} // endint QSettingsModel::rowCount(const QModelIndex & parent) const
int QSettingsModel::columnCount(const QModelIndex & parent) const
{
// qDebug( "columnCount" );
return 2;
}
QVariant QSettingsModel::data(const QModelIndex & index, int role) const
{
/*
** Make sure we have a vaild index.
**
*/
if( !index.isValid() )
return QVariant();
/*
** Get the node data for this index. If there isn't any node
** data then we have a serious error.
**
*/
QSettingsNode *node =
static_cast<QSettingsNode*>(index.internalPointer());
Q_ASSERT(node);
/*
** We only parse DisplayRole and EditRole.
**
*/
if( role == Qt::DisplayRole || role == Qt::EditRole )
{
switch( index.column() )
{
case 0: return node->name;
case 1: return value(node);
default:
{
qWarning("data: invalid display value column %d", index.column());
return QVariant();
}
}
}
#ifdef NEVER
if (index.column() == 0)
{
if (role == SettingsIconRole) return fileIcon(index);
if (role == FilePathRole) return filePath(index);
if (role == FileNameRole) return fileName(index);
}
#endif
return QVariant();
} // endQVariant QSettingsModel::data(const QModelIndex & index, int role) const
Qt::ItemFlags QSettingsModel::flags(const QModelIndex &index) const
{
/*
** Get our ancestor flags.
**
*/
Qt::ItemFlags flags = QAbstractItemModel::flags(index);
/*
** If we have a bad index then stop here.
**
*/
if( !index.isValid() )
return flags;
/*
** If we're readonly then we're done.
**
*/
if( readOnly )
return flags;
/*
** Get a handle on the node.
**
*/
QSettingsNode *node =
static_cast<QSettingsNode*>(index.internalPointer());
Q_ASSERT( node );
// if( (index.column() == 0) && node->info.isWritable() )
// {
flags |= Qt::ItemIsEditable;
// if( fileInfo(index).isDir() ) // is directory and is editable
// flags |= Qt::ItemIsDropEnabled;
// }
return flags;
} // endQt::ItemFlags QSettingsModel::flags() const
bool QSettingsModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
/*
** These are the conditions upon which this item can be updated.
**
*/
if(
/*
** The index we have received must be valid.
**
*/
!index.isValid() ||
/*
** Flags associated with this item must indicate that it is
** editable.
**
*/
(flags(index) & Qt::ItemIsEditable) == 0 ||
/*
** The role requested must be the EditRole.
**
*/
role != Qt::EditRole
)
return false;
/*
** Get a handle on the node.
**
*/
QSettingsNode *node =
static_cast<QSettingsNode*>(index.internalPointer());
Q_ASSERT(node);
/*
** Set the value
**
*/
if( index.column() == 0 ) rename( node,value );
if( index.column() == 1 ) setValue( node,value );
/*
** Emit a refresh signal.
**
*/
QModelIndex par = parent(index);
QModelIndex topLeft = this->index(0, 0, par);
int rc = rowCount(par);
int cc = columnCount(par);
QModelIndex bottomRight = this->index(rc, cc, par);
emit dataChanged(topLeft, bottomRight);
return true;
} // endbool QSettingsModel::setData(const QModelIndex &index, const QVariant &value, int role)
QSettingsModel::QSettingsNode *QSettingsModel::node(int row, QSettingsNode *parent) const
{
/*
** .row. cannot be less than zero - it shouldn't be
**
*/
if( row < 0 )
return 0;
/*
** If there is no parent to this node, or this parent indicates itself
** as a group then remember that.
**
*/
bool isGroup = !parent || parent->isGroup;
// qDebug("node row %d parent is %s",row,isGroup?"a group":"not a group");
/*
** If this node has no parent then return the root item.
**
*/
QSettingsNode *p = (parent ? parent : &root);
/*
** If this is a group node and it's not populated then
** populate it. This is what is called a lazy population
** because it only populates when a group is expanded.
**
*/
if( isGroup && !p->populated )
populate(p); // will also resolve symlinks
/*
** Check for bad row numbers.
**
*/
if( row >= p->children.count() )
{
qWarning("node: the row does not exist");
return 0;
}
/*
** Point to the node based upon the row requested.
**
*/
QSettingsNode *retVal = const_cast<QSettingsNode*>(&p->children.at(row));
// qDebug( "node name %s is %s",retVal->name.toAscii().data(),retVal->isGroup?"a group":"not a group" );
return retVal;
} // endQSettingsModel::QSettingsNode *QSettingsModel::node(int row, QSettingsNode *parent) const
/****************************************************************************
**
** children
**
** This method returns all the children for a particular node. This is
** where the beef begins. This is where the Model meets the Settings
** object. Up till now all the other Model interface methods, index
** parent, rowCount, columnCount, data, flags, setData and node have all
** dealt with existing nodes or links to nodes and what-not. None of them
** have so far interfaced to the QSettings object. So, you've got to
** be asking 'how do they get the data to and from the QSettings object???'
** The answer is; 'this is the 'from' part of that question.'
**
** This method populates a vector of QSettingsNodes. It does this by
** reading out all of the groups and keys from the QSettings object and
** placing the key names and key values into the QSettingsNodes vector
** items. It reads out all the items 'from' the QSettings object at a
** particular branch level... the children of a particular parent.
**
*/
QVector<QSettingsModel::QSettingsNode> QSettingsModel::children(QSettingsNode *parent) const
{
/*
** Make sure we have a valid pointer.
**
*/
Q_ASSERT(parent);
// qDebug("children for '%s'",parent->name.toAscii().data());
/*
** The field .name. should contain only the immediate name
** not the fully qualified path of the parent group, so we
** have to specially request that. By setting beginGroup()
** we are forcing the QSettings object to return only those
** groups and keys within that group (or parent). When we
** do that we have to make sure we remember to 'undo' that
** group specification by calling endGroup().
**
*/
beginGroup(fullPath(parent));
/*
** Create the nodes required for the return result.
**
*/
QVector<QSettingsNode> nodes;
/*
** Here we have an option to show the root items of the QSettings
** object. From a file/directory perspective, showing these
** items is the equivalent of showing 'files' in the root of
** your harddrive. From a QSettings perspective, showing these
** items is the same as showing the keys/values in the [general]
** section of the .ini file.
**
*/
if( parent == &root && showRootItems == false )
{
nodes.resize( childGroups().count() );
}
else
{
nodes.resize( childGroups().count() + childKeys().count() );
}
/*
** Load the groups
**
*/
for( int i=0; i < childGroups().count(); i++ )
{
QSettingsNode &node = nodes[i];
node.parent = parent;
node.name = childGroups().at(i);
node.isGroup = true;
node.populated = false;
}
/*
** Load the keys (items)
**
*/
if( !(parent == &root && showRootItems == false) )
{
for( int i=0; i < childKeys().count(); i++ )
{
QSettingsNode &node = nodes[childGroups().count() + i];
node.parent = parent;
node.name = childKeys().at(i);
node.isGroup = false;
node.populated = false;
}
}
/*
** Don't never forget to do this after setting beginGroup().
**
*/
endGroup();
return nodes;
} // endQVector<QSettingsModel::QSettingsNode> QSettingsModel::children(QSettingsNode *parent) const
/*
** This returns the index position of a node within the list of children.
**
*/
int QSettingsModel::idx(QSettingsNode *node) const
{
Q_ASSERT(node);
/*
** If this is the root node then the index is always zero.
**
*/
if( node == &root )
return 0;
/*
** Get a local as a vector for all the children of the parent
** of this node. Note that this code is a bit redundant when
** it tests for node->parent? because we already tested for
** the root node in the conditional above, but this was copied
** from QDir and that's the way they did it.
**
*/
const QVector<QSettingsNode> children =
node->parent ? node->parent->children : root.children;
/*
** If there are no children then we have a problem, because we (node) are a child
** of our parent, and we just fetched all the children of our parent, and if we're
** now not listed then something is wrong.
**
*/
Q_ASSERT(children.count() > 0);
/*
** Setup to determine our index position.
**
*/
const QSettingsNode *first = &(children.at(0));
/*
** Return our index position.
**
*/
return (node - first);
} // endint QSettingsModel::idx(QSettingsNode *node) const
QVariant QSettingsModel::value(const QSettingsNode *node) const
{
Q_ASSERT(node);
return value(fullPath(node));
}
QString QSettingsModel::parentPath( const QSettingsNode * node ) const
{
Q_ASSERT(node);
/*
** If this node has a parent, then we need to fetch the parent
** name. This is a recursive function call and should top out
** at the absolute parent of the tree.
**
*/
if( node->parent )
return parentPath(node->parent) + node->parent->name + "/";
return QString();
} // endQString QSettingsModel::parentPath(QSettingsNode * node) const
QString QSettingsModel::fullPath( const QSettingsNode * node ) const
{
Q_ASSERT(node);
return parentPath(node) + node->name;
} // endQString QSettingsModel::fullPath(QSettingsNode *node) const
void QSettingsModel::setValue( const QSettingsNode * node,const QVariant & newKey )
{
setValue(fullPath(node),newKey);
} // endvoid QSettingsModel::setValue( const QSettingsNode * node,const QVariant & newKey )
/****************************************************************************
**
** rename
**
** This method takes care of changing the name of a node on a particular
** branch. It does NOT support moving a node to a different branch.
** All we have when we enter this function is the existing node and the
** newKey that we'd like that node to be called. Therefore, the parent
** of the newKey must be the same as the parent of the node.
**
*/
bool QSettingsModel::rename(QSettingsNode * node,const QVariant & newKey )
{
Q_ASSERT(node);
qDebug
(
"renaming node '%s=%s' to '%s' (existing node %s a group)",
fullPath(node).toAscii().data(),
value(fullPath(node)).toString().toAscii().data(),
(parentPath(node)+newKey.toString()).toAscii().data(),
node->isGroup? "is" : "is not"
);
/*
** Make sure they're not trying to rename this node to the same name. This
** happens when someone double-clicks on the node name and then just hits
** <enter> to get off the editor.
**
*/
if( node->name == newKey.toString() )
return false;
/*
** Check right here if the new key already exists. If so then we need
** some way to either stop the operation or merge the data to the
** other existing key and drop this node.
**
** NOTE: Right now the code will just abort. In the case of QDirModel,
** depending on the conditions, it will either overwrite the
** destination node or crash with a segmentation fault.
*/
const QVector<QSettingsNode> children = node->parent->children;
for( int i=0; i < children.count(); i++ )
{
qDebug( "child %s",children[i].name.toAscii().data() );
if( children[i].name == newKey.toString() )
{
qDebug( "key exists!" );
return false;
}
// qDebug( "the parent of this node has children" );
}
/*
** Look for all children of this node and rename them first.
**
*/
// if( node->isGroup )
// {
// rename(node->children[i],
// }
/*
** Set the value for the new key - note that this only allows the key
** to be changed at the same tree depth... this will not handle
** moving a key to a different branch.
**
*/
// setValue( parentPath(node)+newKey.toString(),value(fullPath(node)) );
/*
** Remove the old key
**
*/
// remove( fullPath(node) );
/*
** This isn't finished yet, but for simplicity just rename the existing
** node.
**
*/
// node->name = newKey.toString();
return true;
} // endbool QSettingsModel::rename(QSettingsNode * node,const QVariant & newKey )
/********************************************************************
**
** AbstractItemModel Interface
*/
QVariant QSettingsModel::headerData( int section, Qt::Orientation orientation, int role) const
{
// qDebug( "headerData section %d role(%s)",section,role2String(role) );
if( orientation == Qt::Horizontal )
{
if( role == Qt::DisplayRole )
{
switch( section )
{
case 0: return tr( "Name" );
case 1: return tr( "Value" );
}
}
return QVariant();
}
return QAbstractItemModel::headerData(section,orientation,role);
} // endQVariant QSettingsModel::headerData( int section, Qt::Orientation orientation, int role) const
bool QSettingsModel::isGroup(const QModelIndex &index) const
{
}
QModelIndex QSettingsModel::mkGroup(const QModelIndex &parent, const QString &name)
{
}
bool QSettingsModel::rmGroup(const QModelIndex &index)
{
}
bool QSettingsModel::remove(const QModelIndex &index)
{
}