mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 15:56:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			624 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			624 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "contentmodel.hpp"
 | 
						|
#include "esmfile.hpp"
 | 
						|
 | 
						|
#include <stdexcept>
 | 
						|
 | 
						|
#include <QDir>
 | 
						|
#include <QTextCodec>
 | 
						|
#include <QDebug>
 | 
						|
 | 
						|
#include "components/esm/esmreader.hpp"
 | 
						|
 | 
						|
ContentSelectorModel::ContentModel::ContentModel(QObject *parent) :
 | 
						|
    QAbstractTableModel(parent),
 | 
						|
    mMimeType ("application/omwcontent"),
 | 
						|
    mMimeTypes (QStringList() << mMimeType),
 | 
						|
    mColumnCount (1),
 | 
						|
    mDragDropFlags (Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled),
 | 
						|
    mDropActions (Qt::CopyAction | Qt::MoveAction)
 | 
						|
{
 | 
						|
    setEncoding ("win1252");
 | 
						|
    uncheckAll();
 | 
						|
}
 | 
						|
 | 
						|
void ContentSelectorModel::ContentModel::setEncoding(const QString &encoding)
 | 
						|
{
 | 
						|
    mEncoding = encoding;
 | 
						|
    if (encoding == QLatin1String("win1252"))
 | 
						|
        mCodec = QTextCodec::codecForName("windows-1252");
 | 
						|
 | 
						|
    else if (encoding == QLatin1String("win1251"))
 | 
						|
        mCodec = QTextCodec::codecForName("windows-1251");
 | 
						|
 | 
						|
    else if (encoding == QLatin1String("win1250"))
 | 
						|
        mCodec = QTextCodec::codecForName("windows-1250");
 | 
						|
 | 
						|
    else
 | 
						|
        return; // This should never happen;
 | 
						|
}
 | 
						|
 | 
						|
int ContentSelectorModel::ContentModel::columnCount(const QModelIndex &parent) const
 | 
						|
{
 | 
						|
    if (parent.isValid())
 | 
						|
        return 0;
 | 
						|
 | 
						|
    return mColumnCount;
 | 
						|
}
 | 
						|
 | 
						|
int ContentSelectorModel::ContentModel::rowCount(const QModelIndex &parent) const
 | 
						|
{
 | 
						|
    if(parent.isValid())
 | 
						|
        return 0;
 | 
						|
 | 
						|
    return mFiles.size();
 | 
						|
}
 | 
						|
 | 
						|
const ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(int row) const
 | 
						|
{
 | 
						|
    if (row >= 0 && row < mFiles.size())
 | 
						|
        return mFiles.at(row);
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(int row)
 | 
						|
{
 | 
						|
    if (row >= 0 && row < mFiles.count())
 | 
						|
        return mFiles.at(row);
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
const ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(const QString &name) const
 | 
						|
{
 | 
						|
    EsmFile::FileProperty fp = EsmFile::FileProperty_FileName;
 | 
						|
 | 
						|
    if (name.contains ('/'))
 | 
						|
        fp = EsmFile::FileProperty_FilePath;
 | 
						|
 | 
						|
    foreach (const EsmFile *file, mFiles)
 | 
						|
    {
 | 
						|
        if (name.compare(file->fileProperty (fp).toString(), Qt::CaseInsensitive) == 0)
 | 
						|
            return file;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
QModelIndex ContentSelectorModel::ContentModel::indexFromItem(const EsmFile *item) const
 | 
						|
{
 | 
						|
    //workaround: non-const pointer cast for calls from outside contentmodel/contentselector
 | 
						|
    EsmFile *non_const_file_ptr = const_cast<EsmFile *>(item);
 | 
						|
 | 
						|
    if (item)
 | 
						|
        return index(mFiles.indexOf(non_const_file_ptr),0);
 | 
						|
 | 
						|
    return QModelIndex();
 | 
						|
}
 | 
						|
 | 
						|
Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex &index) const
 | 
						|
{
 | 
						|
    if (!index.isValid())
 | 
						|
        return Qt::NoItemFlags;
 | 
						|
 | 
						|
    const EsmFile *file = item(index.row());
 | 
						|
 | 
						|
    if (!file)
 | 
						|
        return Qt::NoItemFlags;
 | 
						|
 | 
						|
    //game files can always be checked
 | 
						|
    if (file->isGameFile())
 | 
						|
        return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
 | 
						|
 | 
						|
    Qt::ItemFlags returnFlags;
 | 
						|
    bool allDependenciesFound = true;
 | 
						|
    bool gamefileChecked = false;
 | 
						|
 | 
						|
    //addon can be checked if its gamefile is and all other dependencies exist
 | 
						|
    foreach (const QString &fileName, file->gameFiles())
 | 
						|
    {
 | 
						|
        bool depFound = false;
 | 
						|
        foreach (EsmFile *dependency, mFiles)
 | 
						|
        {
 | 
						|
            //compare filenames only.  Multiple instances
 | 
						|
            //of the filename (with different paths) is not relevant here.
 | 
						|
            depFound = (dependency->fileName().compare(fileName, Qt::CaseInsensitive) == 0);
 | 
						|
 | 
						|
            if (!depFound)
 | 
						|
                continue;
 | 
						|
 | 
						|
            if (!gamefileChecked)
 | 
						|
            {
 | 
						|
                if (isChecked (dependency->filePath()))
 | 
						|
                    gamefileChecked = (dependency->isGameFile());
 | 
						|
            }
 | 
						|
 | 
						|
            // force it to iterate all files in cases where the current
 | 
						|
            // dependency is a game file to ensure that a later duplicate
 | 
						|
            // game file is / is not checked.
 | 
						|
            // (i.e., break only if it's not a gamefile or the game file has been checked previously)
 | 
						|
            if (gamefileChecked || !(dependency->isGameFile()))
 | 
						|
                break;
 | 
						|
        }
 | 
						|
 | 
						|
        allDependenciesFound = allDependenciesFound && depFound;
 | 
						|
    }
 | 
						|
 | 
						|
    if (gamefileChecked)
 | 
						|
    {
 | 
						|
        if (allDependenciesFound)
 | 
						|
            returnFlags = returnFlags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | mDragDropFlags;
 | 
						|
        else
 | 
						|
            returnFlags = Qt::ItemIsSelectable;
 | 
						|
    }
 | 
						|
 | 
						|
    return returnFlags;
 | 
						|
}
 | 
						|
 | 
						|
QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int role) const
 | 
						|
{
 | 
						|
    if (!index.isValid())
 | 
						|
        return QVariant();
 | 
						|
 | 
						|
    if (index.row() >= mFiles.size())
 | 
						|
        return QVariant();
 | 
						|
 | 
						|
    const EsmFile *file = item(index.row());
 | 
						|
 | 
						|
    if (!file)
 | 
						|
        return QVariant();
 | 
						|
 | 
						|
    const int column = index.column();
 | 
						|
 | 
						|
    switch (role)
 | 
						|
    {
 | 
						|
    case Qt::EditRole:
 | 
						|
    case Qt::DisplayRole:
 | 
						|
    {
 | 
						|
        if (column >=0 && column <=EsmFile::FileProperty_GameFile)
 | 
						|
            return file->fileProperty(static_cast<const EsmFile::FileProperty>(column));
 | 
						|
 | 
						|
        return QVariant();
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    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();
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    case Qt::ToolTipRole:
 | 
						|
    {
 | 
						|
        if (column != 0)
 | 
						|
            return QVariant();
 | 
						|
 | 
						|
        return file->toolTip();
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    case Qt::CheckStateRole:
 | 
						|
    {
 | 
						|
        if (file->isGameFile())
 | 
						|
            return QVariant();
 | 
						|
 | 
						|
        return mCheckStates[file->filePath()];
 | 
						|
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    case Qt::UserRole:
 | 
						|
    {
 | 
						|
        if (file->isGameFile())
 | 
						|
            return ContentType_GameFile;
 | 
						|
        else
 | 
						|
            if (flags(index))
 | 
						|
                return ContentType_Addon;
 | 
						|
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    case Qt::UserRole + 1:
 | 
						|
        return isChecked(file->filePath());
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    return QVariant();
 | 
						|
}
 | 
						|
 | 
						|
bool ContentSelectorModel::ContentModel::setData(const QModelIndex &index, const QVariant &value, int role)
 | 
						|
{
 | 
						|
    if(!index.isValid())
 | 
						|
        return false;
 | 
						|
 | 
						|
    EsmFile *file = item(index.row());
 | 
						|
    QString fileName = file->fileName();
 | 
						|
    bool success = false;
 | 
						|
 | 
						|
    switch(role)
 | 
						|
    {
 | 
						|
        case Qt::EditRole:
 | 
						|
        {
 | 
						|
            QStringList list = value.toStringList();
 | 
						|
 | 
						|
            for (int i = 0; i < EsmFile::FileProperty_GameFile; i++)
 | 
						|
                file->setFileProperty(static_cast<EsmFile::FileProperty>(i), list.at(i));
 | 
						|
 | 
						|
            for (int i = EsmFile::FileProperty_GameFile; i < list.size(); i++)
 | 
						|
                file->setFileProperty (EsmFile::FileProperty_GameFile, list.at(i));
 | 
						|
 | 
						|
            emit dataChanged(index, index);
 | 
						|
 | 
						|
            success = true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
        case Qt::UserRole+1:
 | 
						|
        {
 | 
						|
            success = (flags (index) & Qt::ItemIsEnabled);
 | 
						|
 | 
						|
            if (success)
 | 
						|
            {
 | 
						|
                success = setCheckState(file->filePath(), value.toBool());
 | 
						|
                emit dataChanged(index, index);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        break;
 | 
						|
 | 
						|
        case Qt::CheckStateRole:
 | 
						|
        {
 | 
						|
            int checkValue = value.toInt();
 | 
						|
            bool setState = false;
 | 
						|
            if ((checkValue==Qt::Checked) && !isChecked(file->filePath()))
 | 
						|
            {
 | 
						|
                setState = true;
 | 
						|
                success = true;
 | 
						|
            }
 | 
						|
            else if ((checkValue == Qt::Checked) && isChecked (file->filePath()))
 | 
						|
                setState = true;
 | 
						|
            else if (checkValue == Qt::Unchecked)
 | 
						|
                setState = true;
 | 
						|
 | 
						|
            if (setState)
 | 
						|
            {
 | 
						|
                setCheckState(file->filePath(), success);
 | 
						|
                emit dataChanged(index, index);
 | 
						|
 | 
						|
            }
 | 
						|
            else
 | 
						|
                return success;
 | 
						|
 | 
						|
 | 
						|
            foreach (EsmFile *file, mFiles)
 | 
						|
            {
 | 
						|
                if (file->gameFiles().contains(fileName, Qt::CaseInsensitive))
 | 
						|
                {
 | 
						|
                    QModelIndex idx = indexFromItem(file);
 | 
						|
                    emit dataChanged(idx, idx);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            success =  true;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    return success;
 | 
						|
}
 | 
						|
 | 
						|
bool ContentSelectorModel::ContentModel::insertRows(int position, int rows, const QModelIndex &parent)
 | 
						|
{
 | 
						|
    if (parent.isValid())
 | 
						|
        return false;
 | 
						|
 | 
						|
    beginInsertRows(parent, position, position+rows-1);
 | 
						|
    {
 | 
						|
        for (int row = 0; row < rows; ++row)
 | 
						|
            mFiles.insert(position, new EsmFile);
 | 
						|
 | 
						|
    } endInsertRows();
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
bool ContentSelectorModel::ContentModel::removeRows(int position, int rows, const QModelIndex &parent)
 | 
						|
{
 | 
						|
    if (parent.isValid())
 | 
						|
        return false;
 | 
						|
 | 
						|
    beginRemoveRows(parent, position, position+rows-1);
 | 
						|
    {
 | 
						|
        for (int row = 0; row < rows; ++row)
 | 
						|
            delete mFiles.takeAt(position);
 | 
						|
 | 
						|
    } endRemoveRows();
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
Qt::DropActions ContentSelectorModel::ContentModel::supportedDropActions() const
 | 
						|
{
 | 
						|
    return mDropActions;
 | 
						|
}
 | 
						|
 | 
						|
QStringList ContentSelectorModel::ContentModel::mimeTypes() const
 | 
						|
{
 | 
						|
    return mMimeTypes;
 | 
						|
}
 | 
						|
 | 
						|
QMimeData *ContentSelectorModel::ContentModel::mimeData(const QModelIndexList &indexes) const
 | 
						|
{
 | 
						|
    QByteArray encodedData;
 | 
						|
 | 
						|
    foreach (const QModelIndex &index, indexes)
 | 
						|
    {
 | 
						|
        if (!index.isValid())
 | 
						|
            continue;
 | 
						|
 | 
						|
        encodedData.append(item(index.row())->encodedData());
 | 
						|
    }
 | 
						|
 | 
						|
    QMimeData *mimeData = new QMimeData();
 | 
						|
    mimeData->setData(mMimeType, encodedData);
 | 
						|
 | 
						|
    return mimeData;
 | 
						|
}
 | 
						|
 | 
						|
bool ContentSelectorModel::ContentModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
 | 
						|
{
 | 
						|
    if (action == Qt::IgnoreAction)
 | 
						|
        return true;
 | 
						|
 | 
						|
    if (column > 0)
 | 
						|
        return false;
 | 
						|
 | 
						|
    if (!data->hasFormat(mMimeType))
 | 
						|
        return false;
 | 
						|
 | 
						|
    int beginRow = rowCount();
 | 
						|
 | 
						|
    if (row != -1)
 | 
						|
        beginRow = row;
 | 
						|
 | 
						|
    else if (parent.isValid())
 | 
						|
        beginRow = parent.row();
 | 
						|
 | 
						|
    QByteArray encodedData = data->data(mMimeType);
 | 
						|
    QDataStream stream(&encodedData, QIODevice::ReadOnly);
 | 
						|
 | 
						|
    while (!stream.atEnd())
 | 
						|
    {
 | 
						|
 | 
						|
        QString value;
 | 
						|
        QStringList values;
 | 
						|
        QStringList gamefiles;
 | 
						|
 | 
						|
        for (int i = 0; i < EsmFile::FileProperty_GameFile; ++i)
 | 
						|
        {
 | 
						|
            stream >> value;
 | 
						|
            values << value;
 | 
						|
        }
 | 
						|
 | 
						|
        stream >> gamefiles;
 | 
						|
 | 
						|
        insertRows(beginRow, 1);
 | 
						|
 | 
						|
        QModelIndex idx = index(beginRow++, 0, QModelIndex());
 | 
						|
        setData(idx, QStringList() << values << gamefiles, Qt::EditRole);
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
void ContentSelectorModel::ContentModel::addFile(EsmFile *file)
 | 
						|
{
 | 
						|
    beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count());
 | 
						|
        mFiles.append(file);
 | 
						|
    endInsertRows();
 | 
						|
 | 
						|
    QModelIndex idx = index (mFiles.size() - 2, 0, QModelIndex());
 | 
						|
 | 
						|
    emit dataChanged (idx, idx);
 | 
						|
}
 | 
						|
 | 
						|
void ContentSelectorModel::ContentModel::addFiles(const QString &path)
 | 
						|
{
 | 
						|
    QDir dir(path);
 | 
						|
    QStringList filters;
 | 
						|
    filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon";
 | 
						|
    dir.setNameFilters(filters);
 | 
						|
 | 
						|
    QTextCodec *codec = QTextCodec::codecForName("UTF8");
 | 
						|
 | 
						|
    // Create a decoder for non-latin characters in esx metadata
 | 
						|
    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());
 | 
						|
 | 
						|
            foreach (const ESM::Header::MasterData &item, fileReader.getGameFiles())
 | 
						|
                file->addGameFile(QString::fromStdString(item.name));
 | 
						|
 | 
						|
            file->setAuthor     (decoder->toUnicode(fileReader.getAuthor().c_str()));
 | 
						|
            file->setDate       (info.lastModified());
 | 
						|
            file->setFormat     (fileReader.getFormat());
 | 
						|
            file->setFilePath       (info.absoluteFilePath());
 | 
						|
            file->setDescription(decoder->toUnicode(fileReader.getDesc().c_str()));
 | 
						|
 | 
						|
 | 
						|
            // Put the file in the table
 | 
						|
            if (item(file->filePath()) == 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;
 | 
						|
 | 
						|
    sortFiles();
 | 
						|
}
 | 
						|
 | 
						|
void ContentSelectorModel::ContentModel::sortFiles()
 | 
						|
{
 | 
						|
    //first, sort the model such that all dependencies are ordered upstream (gamefile) first.
 | 
						|
    bool movedFiles = true;
 | 
						|
    int fileCount = mFiles.size();
 | 
						|
 | 
						|
    //Dependency sort
 | 
						|
    //iterate until no sorting of files occurs
 | 
						|
    while (movedFiles)
 | 
						|
    {
 | 
						|
        movedFiles = false;
 | 
						|
        //iterate each file, obtaining a reference to it's gamefiles list
 | 
						|
        for (int i = 0; i < fileCount; i++)
 | 
						|
        {
 | 
						|
            QModelIndex idx1 = index (i, 0, QModelIndex());
 | 
						|
            const QStringList &gamefiles = mFiles.at(i)->gameFiles();
 | 
						|
            //iterate each file after the current file, verifying that none of it's
 | 
						|
            //dependencies appear.
 | 
						|
            for (int j = i + 1; j < fileCount; j++)
 | 
						|
            {
 | 
						|
                if (gamefiles.contains(mFiles.at(j)->fileName(), Qt::CaseInsensitive))
 | 
						|
                {
 | 
						|
                        mFiles.move(j, i);
 | 
						|
 | 
						|
                        QModelIndex idx2 = index (j, 0, QModelIndex());
 | 
						|
 | 
						|
                        emit dataChanged (idx1, idx2);
 | 
						|
 | 
						|
                        movedFiles = true;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            if (movedFiles)
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
bool ContentSelectorModel::ContentModel::isChecked(const QString& filepath) const
 | 
						|
{
 | 
						|
    if (mCheckStates.contains(filepath))
 | 
						|
        return (mCheckStates[filepath] == Qt::Checked);
 | 
						|
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
bool ContentSelectorModel::ContentModel::isEnabled (QModelIndex index) const
 | 
						|
{
 | 
						|
    return (flags(index) & Qt::ItemIsEnabled);
 | 
						|
}
 | 
						|
 | 
						|
void ContentSelectorModel::ContentModel::setCheckStates (const QStringList &fileList, bool isChecked)
 | 
						|
{
 | 
						|
    foreach (const QString &file, fileList)
 | 
						|
    {
 | 
						|
        setCheckState (file, isChecked);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void ContentSelectorModel::ContentModel::refreshModel()
 | 
						|
{
 | 
						|
    emit dataChanged (index(0,0), index(rowCount()-1,0));
 | 
						|
}
 | 
						|
 | 
						|
bool ContentSelectorModel::ContentModel::setCheckState(const QString &filepath, bool checkState)
 | 
						|
{
 | 
						|
    if (filepath.isEmpty())
 | 
						|
        return false;
 | 
						|
 | 
						|
    const EsmFile *file = item(filepath);
 | 
						|
 | 
						|
    if (!file)
 | 
						|
        return false;
 | 
						|
 | 
						|
    Qt::CheckState state = Qt::Unchecked;
 | 
						|
 | 
						|
    if (checkState)
 | 
						|
        state = Qt::Checked;
 | 
						|
 | 
						|
    mCheckStates[filepath] = state;
 | 
						|
    emit dataChanged(indexFromItem(item(filepath)), indexFromItem(item(filepath)));
 | 
						|
 | 
						|
    if (file->isGameFile())
 | 
						|
        refreshModel();
 | 
						|
 | 
						|
    //if we're checking an item, ensure all "upstream" files (dependencies) are checked as well.
 | 
						|
    if (state == Qt::Checked)
 | 
						|
    {
 | 
						|
        foreach (QString upstreamName, file->gameFiles())
 | 
						|
        {
 | 
						|
            const EsmFile *upstreamFile = item(upstreamName);
 | 
						|
 | 
						|
            if (!upstreamFile)
 | 
						|
                continue;
 | 
						|
 | 
						|
            if (!isChecked(upstreamFile->filePath()))
 | 
						|
                mCheckStates[upstreamFile->filePath()] = Qt::Checked;
 | 
						|
 | 
						|
            emit dataChanged(indexFromItem(upstreamFile), indexFromItem(upstreamFile));
 | 
						|
 | 
						|
        }
 | 
						|
    }
 | 
						|
    //otherwise, if we're unchecking an item (or the file is a game file) ensure all downstream files are unchecked.
 | 
						|
    if (state == Qt::Unchecked)
 | 
						|
    {
 | 
						|
        foreach (const EsmFile *downstreamFile, mFiles)
 | 
						|
        {
 | 
						|
            QFileInfo fileInfo(filepath);
 | 
						|
            QString filename = fileInfo.fileName();
 | 
						|
 | 
						|
            if (downstreamFile->gameFiles().contains(filename, Qt::CaseInsensitive))
 | 
						|
            {
 | 
						|
                if (mCheckStates.contains(downstreamFile->filePath()))
 | 
						|
                    mCheckStates[downstreamFile->filePath()] = Qt::Unchecked;
 | 
						|
 | 
						|
                emit dataChanged(indexFromItem(downstreamFile), indexFromItem(downstreamFile));
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
ContentSelectorModel::ContentFileList ContentSelectorModel::ContentModel::checkedItems() const
 | 
						|
{
 | 
						|
    ContentFileList list;
 | 
						|
 | 
						|
    // TODO:
 | 
						|
    // First search for game files and next addons,
 | 
						|
    // so we get more or less correct game files vs addons order.
 | 
						|
    foreach (EsmFile *file, mFiles)
 | 
						|
        if (isChecked(file->filePath()))
 | 
						|
            list << file;
 | 
						|
 | 
						|
    return list;
 | 
						|
}
 | 
						|
 | 
						|
void ContentSelectorModel::ContentModel::uncheckAll()
 | 
						|
{
 | 
						|
    emit layoutAboutToBeChanged();
 | 
						|
    mCheckStates.clear();
 | 
						|
    emit layoutChanged();
 | 
						|
}
 |