1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-19 22:53:53 +00:00
openmw/apps/opencs/model/world/idtable.cpp
2024-06-12 17:09:28 +02:00

368 lines
11 KiB
C++

#include "idtable.hpp"
#include <QModelIndex>
#include <QVariant>
#include <algorithm>
#include <cstdint>
#include <limits>
#include <map>
#include <stddef.h>
#include <stdexcept>
#include <type_traits>
#include <apps/opencs/model/world/columns.hpp>
#include <apps/opencs/model/world/idcollection.hpp>
#include <apps/opencs/model/world/idtablebase.hpp>
#include <apps/opencs/model/world/record.hpp>
#include <apps/opencs/model/world/universalid.hpp>
#include <components/esm3/loadcell.hpp>
#include <components/misc/strings/lower.hpp>
#include "collectionbase.hpp"
#include "columnbase.hpp"
CSMWorld::IdTable::IdTable(CollectionBase* idCollection, unsigned int features)
: IdTableBase(features)
, mIdCollection(idCollection)
{
}
int CSMWorld::IdTable::rowCount(const QModelIndex& parent) const
{
if (parent.isValid())
return 0;
return mIdCollection->getSize();
}
int CSMWorld::IdTable::columnCount(const QModelIndex& parent) const
{
if (parent.isValid())
return 0;
return mIdCollection->getColumns();
}
QVariant CSMWorld::IdTable::data(const QModelIndex& index, int role) const
{
if (index.row() < 0 || index.column() < 0)
return QVariant();
if (role == ColumnBase::Role_Display)
return QVariant(mIdCollection->getColumn(index.column()).mDisplayType);
if (role == ColumnBase::Role_ColumnId)
return QVariant(getColumnId(index.column()));
if ((role != Qt::DisplayRole && role != Qt::EditRole))
return QVariant();
if (role == Qt::EditRole && !mIdCollection->getColumn(index.column()).isEditable())
return QVariant();
return mIdCollection->getData(index.row(), index.column());
}
QVariant CSMWorld::IdTable::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Vertical)
return QVariant();
if (orientation != Qt::Horizontal)
throw std::logic_error("Unknown header orientation specified");
if (role == Qt::DisplayRole)
return tr(mIdCollection->getColumn(section).getTitle().c_str());
if (role == ColumnBase::Role_Flags)
return mIdCollection->getColumn(section).mFlags;
if (role == ColumnBase::Role_Display)
return mIdCollection->getColumn(section).mDisplayType;
if (role == ColumnBase::Role_ColumnId)
return getColumnId(section);
return QVariant();
}
bool CSMWorld::IdTable::setData(const QModelIndex& index, const QVariant& value, int role)
{
if (mIdCollection->getColumn(index.column()).isEditable() && role == Qt::EditRole)
{
mIdCollection->setData(index.row(), index.column(), value);
int stateColumn = searchColumnIndex(Columns::ColumnId_Modification);
if (stateColumn != -1)
{
if (index.column() == stateColumn)
{
// modifying the state column can modify other values. we need to tell
// views that the whole row has changed.
emit dataChanged(
this->index(index.row(), 0), this->index(index.row(), columnCount(index.parent()) - 1));
}
else
{
emit dataChanged(index, index);
// Modifying a value can also change the Modified status of a record.
QModelIndex stateIndex = this->index(index.row(), stateColumn);
emit dataChanged(stateIndex, stateIndex);
}
}
else
emit dataChanged(index, index);
return true;
}
return false;
}
Qt::ItemFlags CSMWorld::IdTable::flags(const QModelIndex& index) const
{
if (!index.isValid())
return Qt::ItemFlags();
Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
if (mIdCollection->getColumn(index.column()).isUserEditable())
flags |= Qt::ItemIsEditable;
int blockedColumn = searchColumnIndex(Columns::ColumnId_Blocked);
if (blockedColumn != -1 && blockedColumn != index.column())
{
bool isBlocked = mIdCollection->getData(index.row(), blockedColumn).toInt();
if (isBlocked)
flags = Qt::ItemIsSelectable; // not enabled (to grey out)
}
return flags;
}
bool CSMWorld::IdTable::removeRows(int row, int count, const QModelIndex& parent)
{
if (parent.isValid())
return false;
beginRemoveRows(parent, row, row + count - 1);
mIdCollection->removeRows(row, count);
endRemoveRows();
return true;
}
QModelIndex CSMWorld::IdTable::index(int row, int column, const QModelIndex& parent) const
{
if (parent.isValid())
return QModelIndex();
if (row < 0 || row >= mIdCollection->getSize())
return QModelIndex();
if (column < 0 || column >= mIdCollection->getColumns())
return QModelIndex();
return createIndex(row, column);
}
QModelIndex CSMWorld::IdTable::parent(const QModelIndex& index) const
{
return QModelIndex();
}
void CSMWorld::IdTable::addRecord(const std::string& id, UniversalId::Type type)
{
ESM::RefId refId = ESM::RefId::stringRefId(id);
int index = mIdCollection->getAppendIndex(refId, type);
beginInsertRows(QModelIndex(), index, index);
mIdCollection->appendBlankRecord(refId, type);
endInsertRows();
}
void CSMWorld::IdTable::addRecordWithData(
const std::string& id, const std::map<int, QVariant>& data, UniversalId::Type type)
{
ESM::RefId refId = ESM::RefId::stringRefId(id);
int index = mIdCollection->getAppendIndex(refId, type);
beginInsertRows(QModelIndex(), index, index);
mIdCollection->appendBlankRecord(refId, type);
for (std::map<int, QVariant>::const_iterator iter(data.begin()); iter != data.end(); ++iter)
{
mIdCollection->setData(index, iter->first, iter->second);
}
endInsertRows();
}
void CSMWorld::IdTable::cloneRecord(
const ESM::RefId& origin, const ESM::RefId& destination, CSMWorld::UniversalId::Type type)
{
int index = mIdCollection->getAppendIndex(destination, type);
beginInsertRows(QModelIndex(), index, index);
mIdCollection->cloneRecord(origin, destination, type);
endInsertRows();
}
bool CSMWorld::IdTable::touchRecord(const std::string& id)
{
ESM::RefId refId = ESM::RefId::stringRefId(id);
bool changed = mIdCollection->touchRecord(refId);
int row = mIdCollection->getIndex(refId);
int column = mIdCollection->searchColumnIndex(Columns::ColumnId_RecordType);
if (changed && column != -1)
{
QModelIndex modelIndex = index(row, column);
emit dataChanged(modelIndex, modelIndex);
}
return changed;
}
std::string CSMWorld::IdTable::getId(int row) const
{
return mIdCollection->getId(row).getRefIdString();
}
/// This method can return only indexes to the top level table cells
QModelIndex CSMWorld::IdTable::getModelIndex(const std::string& id, int column) const
{
const int row = mIdCollection->searchId(ESM::RefId::stringRefId(id));
if (row != -1)
return index(row, column);
return QModelIndex();
}
void CSMWorld::IdTable::setRecord(
const std::string& id, std::unique_ptr<RecordBase> record, CSMWorld::UniversalId::Type type)
{
const ESM::RefId refId = ESM::RefId::stringRefId(id);
const int index = mIdCollection->searchId(refId);
if (index == -1)
{
// For info records, appendRecord may use a different index than the one returned by
// getAppendIndex (because of prev/next links). This can result in the display not
// updating correctly after an undo
//
// Use an alternative method to get the correct index. For non-Info records the
// record pointer is ignored and internally calls getAppendIndex.
const int index2 = mIdCollection->getInsertIndex(refId, type, record.get());
beginInsertRows(QModelIndex(), index2, index2);
mIdCollection->appendRecord(std::move(record), type);
endInsertRows();
}
else
{
mIdCollection->replace(index, std::move(record));
emit dataChanged(
CSMWorld::IdTable::index(index, 0), CSMWorld::IdTable::index(index, mIdCollection->getColumns() - 1));
}
}
const CSMWorld::RecordBase& CSMWorld::IdTable::getRecord(const std::string& id) const
{
return mIdCollection->getRecord(ESM::RefId::stringRefId(id));
}
int CSMWorld::IdTable::searchColumnIndex(Columns::ColumnId id) const
{
return mIdCollection->searchColumnIndex(id);
}
int CSMWorld::IdTable::findColumnIndex(Columns::ColumnId id) const
{
return mIdCollection->findColumnIndex(id);
}
void CSMWorld::IdTable::reorderRows(int baseIndex, const std::vector<int>& newOrder)
{
if (newOrder.empty())
return;
if (!mIdCollection->reorderRows(baseIndex, newOrder))
return;
emit dataChanged(
index(baseIndex, 0), index(baseIndex + static_cast<int>(newOrder.size()) - 1, mIdCollection->getColumns() - 1));
}
std::pair<CSMWorld::UniversalId, std::string> CSMWorld::IdTable::view(int row) const
{
std::string id;
std::string hint;
if (getFeatures() & Feature_ViewCell)
{
int cellColumn = mIdCollection->searchColumnIndex(Columns::ColumnId_Cell);
int idColumn = mIdCollection->searchColumnIndex(Columns::ColumnId_Id);
if (cellColumn != -1 && idColumn != -1)
{
id = mIdCollection->getData(row, cellColumn).toString().toUtf8().constData();
hint = "r:" + std::string(mIdCollection->getData(row, idColumn).toString().toUtf8().constData());
}
}
else if (getFeatures() & Feature_ViewId)
{
int column = mIdCollection->searchColumnIndex(Columns::ColumnId_Id);
if (column != -1)
{
id = mIdCollection->getData(row, column).toString().toUtf8().constData();
hint = "c:" + id;
}
}
if (id.empty())
return std::make_pair(UniversalId::Type_None, "");
if (id[0] == '#')
id = ESM::Cell::sDefaultWorldspaceId.getValue();
return std::make_pair(UniversalId(UniversalId::Type_Scene, id), hint);
}
/// For top level data/columns
bool CSMWorld::IdTable::isDeleted(const std::string& id) const
{
return getRecord(id).isDeleted();
}
int CSMWorld::IdTable::getColumnId(int column) const
{
return mIdCollection->getColumn(column).getId();
}
CSMWorld::CollectionBase* CSMWorld::IdTable::idCollection() const
{
return mIdCollection;
}
CSMWorld::LandTextureIdTable::LandTextureIdTable(CollectionBase* idCollection, unsigned int features)
: IdTable(idCollection, features)
{
}
const CSMWorld::Record<ESM::LandTexture>* CSMWorld::LandTextureIdTable::searchRecord(
std::uint16_t index, int plugin) const
{
return static_cast<CSMWorld::IdCollection<ESM::LandTexture>*>(idCollection())->searchRecord(index, plugin);
}