/* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ #include #include #include #include "mappingtreemodel.h" #include "smtreeitem.h" MappingTreeModel::MappingTreeModel(QStringList &headers, QObject *parent) : SmTreeModel(headers, parent), mForbidden("/"), mType(-1) { //init database mDb = QSqlDatabase::database("treedb"); mTypesQ = new QSqlQuery(mDb); mTypesQ->prepare("SELECT imappings_type_id, tmappings_type_name FROM mappings_type"); getMappingTypes(); //prepare Queries mSParentsQ = "SELECT mapping_parents.imapping_parents_id, mapping_parents.idescription_id, mapping_description.tdescription_name, mapping_description.tscreated, mapping_parents.iparent_id FROM mapping_parents, mapping_description WHERE mapping_parents.iparent_id = :pid AND mapping_description.idescription_type = :type AND mapping_parents.idescription_id = mapping_description.idescription_id ORDER BY mapping_description.tdescription_name"; mUpdateTypeQ = new QSqlQuery(mDb); mUpdateTypeQ->prepare("UPDATE mappings_type SET tmappings_type_name = :value WHERE imappings_type_id = :id"); mUpdateChildQ = new QSqlQuery(mDb); mUpdateChildQ->prepare("UPDATE mapping_description SET tdescription_name = :value WHERE idescription_id = :id"); mAddMappingTypeQ = new QSqlQuery(mDb); mAddMappingTypeQ->prepare("INSERT INTO mappings_type (tmappings_type_name) VALUES(:value)"); mDeleteMappingTypeQ = new QSqlQuery(mDb); mDeleteMappingTypeQ->prepare("DELETE FROM mappings_type WHERE imappings_type_id = :id"); mAddDescriptionQ = new QSqlQuery(mDb); mAddDescriptionQ->prepare("INSERT INTO mapping_description (tdescription_name, idescription_type) VALUES(:name, :type)"); mSelectDescriptionQ = new QSqlQuery(mDb); mSelectDescriptionQ->prepare("SELECT idescription_id, tdescription_name, tscreated FROM mapping_description WHERE tdescription_name = :name AND idescription_type = :type"); mAddParentQ = new QSqlQuery(mDb); mAddParentQ->prepare("INSERT INTO mapping_parents (idescription_id, iparent_id) VALUES(:id, :parentid)"); mUpdateParentQ = new QSqlQuery(mDb); mUpdateParentQ->prepare("UPDATE mapping_parents SET iparent_id = :id where imapping_parents_id = :sid"); mDeleteMappingParentQ = new QSqlQuery(mDb); mDeleteMappingParentQ->prepare("DELETE FROM mapping_parents WHERE imapping_parents_id = :id"); mDescriptionQ = new QSqlQuery(mDb); mDescriptionQ->prepare("SELECT tdescription_name, idescription_id FROM mapping_description WHERE idescription_type = :type"); } MappingTreeModel::~MappingTreeModel(){ delete mTypesQ; delete mUpdateTypeQ; delete mUpdateChildQ; delete mAddMappingTypeQ; delete mDeleteMappingTypeQ; delete mAddDescriptionQ; delete mSelectDescriptionQ; delete mAddParentQ; delete mUpdateParentQ; delete mDescriptionQ; mDb = QSqlDatabase(); } QStringList MappingTreeModel::mappingTypeNames() const { QStringList retval; foreach(mappingType t, mMappingTypes){ retval << t.name.toString(); } qSort(retval); return retval; } int MappingTreeModel::mappingTypeIdFromName(const QVariant &name) const{ foreach(const mappingType t, mMappingTypes){ if(t.name == name){ return t.id.toInt(); } } return -1; } //caller has ownership of this item! SmTreeItem *MappingTreeModel::treeFromPaths(const QStringList &paths){ if(paths.isEmpty()){ return 0; } QHash partsHash; SmTreeItem *root = new SmTreeItem(1); partsHash.insert(QString(), root); SmTreeItem *pItem = root; // create tree for(int i = 0; i < paths.count(); ++i){ //split the paths QStringList parts = paths.at(i).split(forbidden()); //process path items for(int j = 0; j < parts.count(); ++j){ if(partsHash.contains(parts.at(j))){ pItem = partsHash.value(parts.at(j)); //we've already seen this item, set it as new parent }else{ //create a new item, save old root SmTreeItem *oldRoot = pItem; pItem = new SmTreeItem(QList() << parts.at(j), partsHash.value(parts.at(j))); oldRoot->appendChild(pItem); partsHash.insert(parts.at(j), pItem); } } pItem = root; } return root; } QVariant MappingTreeModel::data(const QModelIndex &index, int role) const{ if(!index.isValid()){ return QVariant(); } SmTreeItem *item = itemAt(index); if(role == NameRole){ return item->data(Name); } if(role == MappingIdRole){ return item->data(MappingId); } if(role == DescAddedRole){ return item->data(DescAdded); } if(role == DescIdRole){ return item->data(DescId); } if(role == MappingParentIdRole){ return item->data(MappingParentId); } return SmTreeModel::data(index, role); } QList MappingTreeModel::childList(const QVariant &value, int column) const{ QModelIndex itemIdx = findRecursive(value, column, createIndex(0, column, root())); SmTreeItem *item = static_cast(itemIdx.internalPointer()); if(item){ if(item->childCount()){ return getChildListRecursive(item, column); } } return QList() << value; } QStringList MappingTreeModel::path(QModelIndex &idx) const{ if(!idx.isValid()){ return QStringList(); } QStringList retval; SmTreeItem *item = static_cast(idx.internalPointer()); do { QString d = item->data(MappingTreeModel::Name).toString(); if(!d.isEmpty()){ retval << d; } item = item->parent(); }while(item); std::reverse(retval.begin(), retval.end()); return retval; } QModelIndex MappingTreeModel::indexFromPath(const QString &path, int column) const{ if(path == "/"){ return rootIndex(); } QStringList items = path.split("/"); if(items.isEmpty() || column >= NumFields){ return QModelIndex(); } QModelIndex retval; for(int i = 0; i < items.count(); ++i){ retval = find(items.at(i), column, retval); } return retval; } bool MappingTreeModel::setData(const QModelIndex &index, const QVariant &value, int role){ if(!index.isValid()){ return false; } SmTreeItem *item = itemAt(index); if(role == Qt::EditRole){ if(index.column() == Name){ if(value.toString().contains(mForbidden)){ return false; } } mDb.transaction(); QSqlQuery *q = 0; if(item == root()){ q = mUpdateTypeQ; }else{ q = mUpdateChildQ; } q->bindValue(":value", value); q->bindValue(":id", item->data(MappingId)); if(q->exec()){ item->setData(Name, value); emit dataChanged(index, index); mDb.commit(); return true; } mDb.rollback(); } if(role == NameRole){ if(value.toString().contains(mForbidden)){ return false; } item->setData(Name, value); } if(role == MappingIdRole){ item->setData(MappingId, value); } if(role == DescAddedRole){ item->setData(DescAdded, value); } if(role == DescIdRole){ item->setData(DescId, value); } return true; } bool MappingTreeModel::move(const QModelIndex &source, const QModelIndex &dest){ QVariant sourceId = source.data(MappingIdRole); QVariant destId = dest.data(MappingIdRole); mDb.transaction(); mUpdateParentQ->bindValue(":id", destId); mUpdateParentQ->bindValue(":sid", sourceId); if(mUpdateParentQ->exec()){ // well, well, I tried to make this work with removeRows and insertRows, // but qt has a mind of its own there. So take the easy way out. Also, // it's way faster! mDb.commit(); populate(); return true; } mLastError = mUpdateParentQ->lastError(); mDb.rollback(); return false; } bool MappingTreeModel::addMappingType(const QString &type){ mAddMappingTypeQ->bindValue(":value", type); mDb.transaction(); if(mAddMappingTypeQ->exec()){ mDb.commit(); getMappingTypes(); return true; } mDb.rollback(); return false; } bool MappingTreeModel::deleteMappingType(int typeId){ mDeleteMappingTypeQ->bindValue(":id", typeId); mDb.transaction(); if(mDeleteMappingTypeQ->exec()){ mDb.commit(); getMappingTypes(); return true; } mDb.rollback(); return false; } bool MappingTreeModel::addChild(const QVariant &name, const QModelIndex &parent){ if(!parent.isValid()){ return false; } if(name.toString().contains(mForbidden)){ return false; } //check if we already have a mapping with this name int id = mMappings.value(name.toString(), -1); if(id == -1){ id = addChild(name.toString(), mType); } //now check if we already have a mapping with the same parent mDb.transaction(); mAddParentQ->bindValue(":id", id); mAddParentQ->bindValue(":parentid", parent.data(MappingIdRole)); if(mAddParentQ->exec()){ mDb.commit(); populate(); return true; } mLastError = mAddParentQ->lastError(); mDb.rollback(); return false; } bool MappingTreeModel::deleteChild(const QModelIndex &idx){ if(!idx.isValid()){ return false; } SmTreeItem *item = itemAt(idx); if(item->childCount() > 0){ return false; } mDeleteMappingParentQ->bindValue(":id", item->data(MappingId)); if(mDeleteMappingParentQ->exec()){ removeRows(idx.row(), 1, idx.parent()); return true; } mLastError = mDeleteMappingParentQ->lastError(); return false; } MappingData MappingTreeModel::mappingDataFromIndex(QModelIndex &idx) const{ MappingData retval; if(!idx.isValid()){ return retval; } retval.mappingId = idx.data(MappingTreeModel::MappingIdRole).toInt(); retval.parentId = idx.data(MappingTreeModel::MappingParentIdRole).toInt(); retval.descId = idx.data(MappingTreeModel::DescIdRole).toInt(); retval.name = idx.data(MappingTreeModel::NameRole).toString(); retval.path << path(idx); return retval; } QStringList MappingTreeModel::paths() const{ QStringList retval = QStringList() << "/"; retval << getPathsRecursive(root()); return retval; } void MappingTreeModel::populate(){ if(mType == -1){ return; } // get nodes with root as parent QSqlQuery rootQ(mDb); rootQ.prepare(mSParentsQ); rootQ.bindValue(":type", mType); rootQ.bindValue(":pid", -1); if(rootQ.exec()){ SmTreeItem *rootItem = new SmTreeItem(NumFields); rootItem->setData(DescId, -1); rootItem->setData(MappingId, -1); rootItem->setData(MappingParentId, -1); while(rootQ.next()){ QList childData; childData << rootQ.value(2) << rootQ.value(0) << rootQ.value(3) << rootQ.value(1) << rootQ.value(4); SmTreeItem *childItem = new SmTreeItem(childData, rootItem); rootItem->appendChild(childItem); getChildrenRecursive(childItem); } setRoot(rootItem); emit needExpansion(); } } void MappingTreeModel::setType(int type){ mType = type; getMappings(); } void MappingTreeModel::getMappings(){ mDescriptionQ->bindValue(":type", mType); if(mDescriptionQ->exec()){ mMappings.clear(); while(mDescriptionQ->next()){ mMappings.insert(mDescriptionQ->value(0).toString(), mDescriptionQ->value(1).toInt()); } } } int MappingTreeModel::addChild(const QString &name, int type){ mAddDescriptionQ->bindValue(":name", name); mAddDescriptionQ->bindValue(":type", type); if(mAddDescriptionQ->exec()){ mSelectDescriptionQ->bindValue(":name", name); mSelectDescriptionQ->bindValue(":type", type); int c = 0; mSelectDescriptionQ->exec(); while(mSelectDescriptionQ->next()){ ++c; mMappings.insert(mSelectDescriptionQ->value(1).toString(), mSelectDescriptionQ->value(0).toInt()); } if(c > 1){ qWarning("addChild yielded more than 1 result!"); } } return mMappings.value(name); } void MappingTreeModel::getMappingTypes(){ bool qRes = mTypesQ->exec(); if(qRes){ mMappingTypes.clear(); while(mTypesQ->next()){ mappingType t; t.id = mTypesQ->value(0); t.name = mTypesQ->value(1); mMappingTypes << t; } emit mappingTypesChanged(); } } void MappingTreeModel::getChildrenRecursive(SmTreeItem *item){ QSqlQuery cq(mDb); cq.prepare(mSParentsQ); cq.bindValue(":type", mType); cq.bindValue(":pid", item->data(MappingId)); if(cq.exec()){ while(cq.next()){ QList childData; childData << cq.value(2) << cq.value(0) << cq.value(3) << cq.value(1) << cq.value(4); SmTreeItem *child = new SmTreeItem(childData, item); item->appendChild(child); getChildrenRecursive(child); } } } QStringList MappingTreeModel::getPathsRecursive(SmTreeItem *parent) const{ QStringList retval; if(!basePath(parent).isEmpty()){ retval << basePath(parent); } for(int i = 0; i < parent->childCount(); ++i){ if(parent->child(i)->childCount()){ retval << getPathsRecursive(parent->child(i)); }else{ retval << QString("%1/%2").arg(basePath(parent)).arg(parent->child(i)->data(Name).toString()); } } return retval; } QList MappingTreeModel::getChildListRecursive(SmTreeItem *item, int column) const{ QList retval; if(!item){ return retval; } for(int i = 0; i < item->childCount(); ++i){ if(item->child(i)->childCount()){ retval << getChildListRecursive(item->child(i), column); }else{ retval << item->child(i)->data(column); } } return retval; } QString MappingTreeModel::basePath(SmTreeItem *item) const{ QStringList pItems; SmTreeItem *cur = item; while(cur != root()){ pItems << cur->data(Name).toString(); cur = cur->parent(); } if(pItems.isEmpty()){ return QString(); } std::reverse(pItems.begin(), pItems.end()); return pItems.join("/"); } int MappingTreeModel::lowerBound(SmTreeItem *item, const QVariant &value, int column) const { for(int i = 0; i < item->childCount(); ++i){ SmTreeItem *child = item->child(i); if(child->data(column).toString() > value.toString()){ return i; } } return item->childCount(); } MappingTreeResultModel::MappingTreeResultModel(const QStringList &headers, QObject *parent) : SmTreeModel(headers, parent) { } Qt::ItemFlags MappingTreeResultModel::flags(const QModelIndex &index) const { Q_UNUSED(index); return (Qt::ItemIsEnabled | Qt::ItemIsSelectable); } bool MappingTreeResultModel::setData(const QModelIndex &index, const QVariant &value, int role){ SmTreeItem *item = itemAt(index); if(role == NameRole){ item->setData(Name, value); return true; } if(role == MappingIdRole){ item->setData(MappingId, value); return true; } if(role == ParentIdRole){ item->setData(ParentId, value); return true; } if(role == DescIdRole){ item->setData(DescId, value); return true; } return SmTreeModel::setData(index, value, role); } void MappingTreeResultModel::addItem(const MappingData &data){ SmTreeItem *curItem = root(); QList paths = data.path; foreach(QStringList p, paths){ for(int i = 0; i < p.count(); ++i){ int childPos = hasChild(curItem, p.at(i)); if(childPos != -1){ //child already exists curItem = curItem->child(childPos); continue; }else{ //insert child int mappingId = -1; int parentId = -1; int descId = -1; if(i == p.count() - 1){ mappingId = data.mappingId; parentId = data.parentId; descId = data.descId; } QModelIndex curIdx = insertChild(p.at(i), mappingId, parentId, descId, curItem); curItem = itemAt(curIdx); } } } } QModelIndex MappingTreeResultModel::insertChild(const QVariant &data, int mappingId, int parentId, int descId, SmTreeItem *parent){ QModelIndex parentIdx; int row = parent->childCount(); if(parent != root()){ for(int i = 0; i < parent->childCount(); ++i){ if(parent->child(i)->data(0).toString() > data.toString()){ row = i; } } parentIdx = createIndex(0, 0, parent); } insertRows(row, 1, parentIdx); QModelIndex newIdx = index(row, 0, parentIdx); setData(newIdx, data, NameRole); setData(newIdx, mappingId, MappingIdRole); setData(newIdx, descId, DescIdRole); setData(newIdx, parentId, ParentIdRole); return newIdx; } QList MappingTreeResultModel::columnValues(int column) const { return columnValuesRecursive(root(), column); } void MappingTreeResultModel::clearData(){ setRoot(new SmTreeItem(NumFields)); } int MappingTreeResultModel::hasChild(SmTreeItem *item, const QVariant &name, int column) const{ for(int i = 0; i < item->childCount(); ++i){ if(item->child(i)->data(column) == name){ return i; } } return -1; } QList MappingTreeResultModel::columnValuesRecursive(SmTreeItem *parent, int column) const { QList retval; if(!parent->childCount()){ return retval; } for(int i = 0; i < parent->childCount(); ++i){ SmTreeItem *child = parent->child(i); QVariant value = child->data(column); if(value.canConvert(QVariant::Int) && (value.toInt() != -1)){ retval << value; } retval << columnValuesRecursive(child, column); } return retval; } MappingData::MappingData() : mappingId(-1), parentId(-1), descId(-1) {} bool MappingData::isValid(){ return !(mappingId == -1 && parentId == -1 && descId == -1); }