1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-15 19:19:55 +00:00
openmw-tes3mp/components/esxselector/model/contentmodel.cpp

500 lines
12 KiB
C++
Raw Normal View History

2013-09-07 20:57:40 +00:00
#include "contentmodel.hpp"
#include "esmfile.hpp"
#include <QDebug>
#include <QDir>
#include <QTextCodec>
#include <components/esm/esmreader.hpp>
EsxModel::ContentModel::ContentModel(QObject *parent) :
QAbstractTableModel(parent), mEncoding("win1252")
{}
void EsxModel::ContentModel::setEncoding(const QString &encoding)
{
mEncoding = encoding;
}
int EsxModel::ContentModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return 0;
return 1;
}
2013-09-18 07:36:23 +00:00
int EsxModel::ContentModel::rowCount(const QModelIndex &parent) const
2013-09-07 20:57:40 +00:00
{
2013-09-18 07:36:23 +00:00
if(parent.isValid())
2013-09-07 20:57:40 +00:00
return 0;
2013-09-18 07:36:23 +00:00
return mFiles.size();
2013-09-07 20:57:40 +00:00
}
2013-09-18 07:36:23 +00:00
EsxModel::EsmFile* EsxModel::ContentModel::item(int row) const
2013-09-07 20:57:40 +00:00
{
2013-09-18 07:36:23 +00:00
if (row >= 0 && row < mFiles.count())
return mFiles.at(row);
2013-09-07 20:57:40 +00:00
2013-09-18 07:36:23 +00:00
return 0;
2013-09-07 20:57:40 +00:00
}
2013-09-18 07:36:23 +00:00
EsxModel::EsmFile* EsxModel::ContentModel::findItem(const QString &name)
2013-09-07 20:57:40 +00:00
{
2013-09-18 07:36:23 +00:00
for (int i = 0; i < mFiles.size(); ++i)
{
if (name == item(i)->fileName())
return item(i);
}
2013-09-07 20:57:40 +00:00
2013-09-18 07:36:23 +00:00
return 0;
}
QModelIndex EsxModel::ContentModel::indexFromItem(EsmFile *item) const
{
if (item)
return index(mFiles.indexOf(item),0);
return QModelIndex();
}
Qt::ItemFlags EsxModel::ContentModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::NoItemFlags;
EsmFile *file = item(index.row());
if (!file)
return Qt::NoItemFlags;
Qt::ItemFlags dragDropFlags = Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
Qt::ItemFlags checkFlags = Qt::ItemIsUserCheckable;
Qt::ItemFlags defaultFlags = Qt::ItemIsDropEnabled | Qt::ItemIsSelectable;
if (canBeChecked(file))
return Qt::ItemIsEnabled | dragDropFlags | checkFlags | defaultFlags;
else
return defaultFlags;
2013-09-07 20:57:40 +00:00
}
QVariant EsxModel::ContentModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.row() >= mFiles.size())
return QVariant();
EsmFile *file = item(index.row());
if (!file)
return QVariant();
const int column = index.column();
switch (role)
{
case Qt::EditRole:
case Qt::DisplayRole:
{
switch (column)
{
case 0:
return file->fileName();
case 1:
return file->author();
case 2:
return file->modified().toString(Qt::ISODate);
2013-09-18 07:36:23 +00:00
case 3:
2013-09-07 20:57:40 +00:00
return file->version();
2013-09-18 07:36:23 +00:00
case 4:
2013-09-07 20:57:40 +00:00
return file->path();
2013-09-18 07:36:23 +00:00
case 5:
2013-09-07 20:57:40 +00:00
return file->masters().join(", ");
2013-09-18 07:36:23 +00:00
case 6:
2013-09-07 20:57:40 +00:00
return file->description();
}
return QVariant();
}
case Qt::TextAlignmentRole:
{
switch (column)
{
case 0:
case 1:
return Qt::AlignLeft + Qt::AlignVCenter;
case 2:
case 3:
return Qt::AlignRight + Qt::AlignVCenter;
default:
return Qt::AlignLeft + Qt::AlignVCenter;
}
return QVariant();
}
case Qt::ToolTipRole:
{
if (column != 0)
return QVariant();
if (file->version() == 0.0f)
return QVariant(); // Data not set
return QString("<b>Author:</b> %1<br/> \
<b>Version:</b> %2<br/> \
<br/><b>Description:</b><br/>%3<br/> \
<br/><b>Dependencies: </b>%4<br/>")
.arg(file->author())
.arg(QString::number(file->version()))
.arg(file->description())
.arg(file->masters().join(", "));
}
2013-09-18 07:36:23 +00:00
case Qt::CheckStateRole:
if (!file->isMaster())
return isChecked(file->fileName());
break;
2013-09-07 20:57:40 +00:00
case Qt::UserRole:
{
2013-09-18 07:36:23 +00:00
if (file->isMaster())
2013-09-07 20:57:40 +00:00
return "game";
else
return "addon";
}
2013-09-18 07:36:23 +00:00
case Qt::UserRole + 1:
return isChecked(file->fileName());
break;
2013-09-07 20:57:40 +00:00
}
2013-09-18 07:36:23 +00:00
return QVariant();
2013-09-07 20:57:40 +00:00
}
2013-09-18 07:36:23 +00:00
bool EsxModel::ContentModel::setData(const QModelIndex &index, const QVariant &value, int role)
2013-09-07 20:57:40 +00:00
{
2013-09-18 07:36:23 +00:00
if(!index.isValid())
return false;
2013-09-07 20:57:40 +00:00
EsmFile *file = item(index.row());
2013-09-18 07:36:23 +00:00
QString fileName = file->fileName();
2013-09-07 20:57:40 +00:00
2013-09-18 07:36:23 +00:00
switch(role)
{
case Qt::EditRole:
{
QStringList list = value.toStringList();
2013-09-07 20:57:40 +00:00
2013-09-18 07:36:23 +00:00
//iterate the string list, assigning values to proeprties
//index-enum correspondence 1:1
for (int i = 0; i < EsxModel::Property_Master; i++)
file->setProperty(static_cast<EsmFileProperty>(i), list.at(i));
2013-09-07 20:57:40 +00:00
2013-09-18 07:36:23 +00:00
//iterate the remainder of the string list, assifning everything
// as
for (int i = EsxModel::Property_Master; i < list.size(); i++)
file->setProperty (EsxModel::Property_Master, list.at(i));
2013-09-07 20:57:40 +00:00
2013-09-18 07:36:23 +00:00
//emit data changed for the item itself
emit dataChanged(index, index);
return true;
}
break;
case Qt::UserRole+1:
{
setCheckState(fileName, value.toBool());
emit dataChanged(index, index);
for(int i = 0; i < mFiles.size(); i++)
{
if (mFiles.at(i)->masters().contains(fileName))
{
QModelIndex idx = QAbstractTableModel::index(i, 0);
emit dataChanged(idx, idx);
}
}
return true;
}
break;
case Qt::CheckStateRole:
{
bool checked = ((value.toInt() == Qt::Checked) && !isChecked(fileName));
setCheckState(fileName, checked);
emit dataChanged(index, index);
return true;
}
break;
2013-09-07 20:57:40 +00:00
}
return false;
}
bool EsxModel::ContentModel::insertRows(int position, int rows, const QModelIndex &parent)
{
2013-09-18 07:36:23 +00:00
if (parent.isValid())
return false;
2013-09-07 20:57:40 +00:00
beginInsertRows(parent, position, position+rows-1);
2013-09-18 07:36:23 +00:00
{
for (int row = 0; row < rows; ++row)
mFiles.insert(position, new EsmFile);
2013-09-07 20:57:40 +00:00
2013-09-18 07:36:23 +00:00
} endInsertRows();
2013-09-07 20:57:40 +00:00
return true;
}
bool EsxModel::ContentModel::removeRows(int position, int rows, const QModelIndex &parent)
{
2013-09-18 07:36:23 +00:00
if (parent.isValid())
return false;
2013-09-07 20:57:40 +00:00
beginRemoveRows(parent, position, position+rows-1);
2013-09-18 07:36:23 +00:00
{
for (int row = 0; row < rows; ++row)
delete mFiles.takeAt(position);
2013-09-07 20:57:40 +00:00
2013-09-18 07:36:23 +00:00
} endRemoveRows();
2013-09-07 20:57:40 +00:00
return true;
}
Qt::DropActions EsxModel::ContentModel::supportedDropActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}
QStringList EsxModel::ContentModel::mimeTypes() const
{
QStringList types;
2013-09-18 07:36:23 +00:00
2013-09-07 20:57:40 +00:00
types << "application/omwcontent";
2013-09-18 07:36:23 +00:00
2013-09-07 20:57:40 +00:00
return types;
}
QMimeData *EsxModel::ContentModel::mimeData(const QModelIndexList &indexes) const
{
QByteArray encodedData;
foreach (const QModelIndex &index, indexes)
{
2013-09-18 07:36:23 +00:00
if (!index.isValid())
continue;
QByteArray fileData = item(index.row())->encodedData();
foreach (const char c, fileData)
encodedData.append(c);
2013-09-07 20:57:40 +00:00
}
2013-09-18 07:36:23 +00:00
QMimeData *mimeData = new QMimeData();
2013-09-07 20:57:40 +00:00
mimeData->setData("application/omwcontent", encodedData);
2013-09-18 07:36:23 +00:00
2013-09-07 20:57:40 +00:00
return mimeData;
}
bool EsxModel::ContentModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
if (action == Qt::IgnoreAction)
return true;
if (!data->hasFormat("application/omwcontent"))
return false;
if (column > 0)
return false;
int beginRow;
if (row != -1)
beginRow = row;
2013-09-18 07:36:23 +00:00
2013-09-07 20:57:40 +00:00
else if (parent.isValid())
beginRow = parent.row();
2013-09-18 07:36:23 +00:00
2013-09-07 20:57:40 +00:00
else
beginRow = rowCount();
QByteArray encodedData = data->data("application/omwcontent");
QDataStream stream(&encodedData, QIODevice::ReadOnly);
while (!stream.atEnd())
{
2013-09-18 07:36:23 +00:00
QStringList values;
for (int i = 0; i < EsmFile::sPropertyCount; ++i)
stream >> values;
insertRows(beginRow, 1);
QModelIndex idx = index(beginRow++, 0, QModelIndex());
setData(idx, values, Qt::EditRole);
2013-09-07 20:57:40 +00:00
}
2013-09-18 07:36:23 +00:00
return true;
}
2013-09-07 20:57:40 +00:00
2013-09-18 07:36:23 +00:00
bool EsxModel::ContentModel::canBeChecked(const EsmFile *file) const
{
//element can be checked if all its dependencies are
foreach (const QString &master, file->masters())
2013-09-07 20:57:40 +00:00
{
2013-09-18 07:36:23 +00:00
{// if the master is not found in checkstates
// or it is not specifically checked, return false
if (!mCheckStates.contains(master))
return false;
if (!isChecked(master))
return false;
}
bool found = false;
//iterate each file, if it is not a master and
//does not have a master that is currently checked,
//return false.
foreach(const EsmFile *file, mFiles)
{
QString filename = file->fileName();
found = (filename == master);
if (found)
{
if (!isChecked(filename))
return false;
}
}
if (!found)
return false;
2013-09-07 20:57:40 +00:00
}
return true;
}
void EsxModel::ContentModel::addFile(EsmFile *file)
{
2013-09-18 07:36:23 +00:00
beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count());
{
mFiles.append(file);
} endInsertRows();
2013-09-07 20:57:40 +00:00
}
void EsxModel::ContentModel::addFiles(const QString &path)
{
QDir dir(path);
QStringList filters;
filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon";
dir.setNameFilters(filters);
// Create a decoder for non-latin characters in esx metadata
QTextCodec *codec;
if (mEncoding == QLatin1String("win1252")) {
codec = QTextCodec::codecForName("windows-1252");
} else if (mEncoding == QLatin1String("win1251")) {
codec = QTextCodec::codecForName("windows-1251");
} else if (mEncoding == QLatin1String("win1250")) {
codec = QTextCodec::codecForName("windows-1250");
} else {
return; // This should never happen;
}
QTextDecoder *decoder = codec->makeDecoder();
foreach (const QString &path, dir.entryList()) {
QFileInfo info(dir.absoluteFilePath(path));
EsmFile *file = new EsmFile(path);
try {
ESM::ESMReader fileReader;
ToUTF8::Utf8Encoder encoder(ToUTF8::calculateEncoding(mEncoding.toStdString()));
fileReader.setEncoder(&encoder);
fileReader.open(dir.absoluteFilePath(path).toStdString());
std::vector<ESM::Header::MasterData> mlist = fileReader.getMasters();
QStringList masters;
for (unsigned int i = 0; i < mlist.size(); ++i) {
QString master = QString::fromStdString(mlist[i].name);
masters.append(master);
}
file->setAuthor(decoder->toUnicode(fileReader.getAuthor().c_str()));
2013-09-18 07:36:23 +00:00
//file->setSize(info.size());
file->setDate(info.lastModified());
file->setVersion(0.0f);
2013-09-07 20:57:40 +00:00
file->setPath(info.absoluteFilePath());
file->setMasters(masters);
file->setDescription(decoder->toUnicode(fileReader.getDesc().c_str()));
// Put the file in the table
if (findItem(path) == 0)
addFile(file);
} catch(std::runtime_error &e) {
// An error occurred while reading the .esp
qWarning() << "Error reading addon file: " << e.what();
continue;
}
}
delete decoder;
}
2013-09-18 07:36:23 +00:00
bool EsxModel::ContentModel::isChecked(const QString& name) const
2013-09-07 20:57:40 +00:00
{
2013-09-18 07:36:23 +00:00
return (mCheckStates[name] == Qt::Checked);
2013-09-07 20:57:40 +00:00
}
2013-09-18 07:36:23 +00:00
void EsxModel::ContentModel::setCheckState(const QString &name, bool isChecked)
2013-09-07 20:57:40 +00:00
{
2013-09-18 07:36:23 +00:00
if (name.isEmpty())
2013-09-07 20:57:40 +00:00
return;
2013-09-18 07:36:23 +00:00
Qt::CheckState state = Qt::Unchecked;
2013-09-07 20:57:40 +00:00
2013-09-18 07:36:23 +00:00
if (isChecked)
state = Qt::Checked;
2013-09-07 20:57:40 +00:00
2013-09-18 07:36:23 +00:00
mCheckStates[name] = state;
2013-09-07 20:57:40 +00:00
}
EsxModel::ContentFileList EsxModel::ContentModel::checkedItems() const
{
ContentFileList list;
for (int i = 0; i < mFiles.size(); ++i)
{
EsmFile *file = item(i);
// Only add the items that are in the checked list and available
if (mCheckStates[file->fileName()] == Qt::Checked && canBeChecked(file))
list << file;
}
return list;
}
void EsxModel::ContentModel::uncheckAll()
{
emit layoutAboutToBeChanged();
mCheckStates.clear();
emit layoutChanged();
}