diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 377edc3b1c..003f2ee241 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -109,6 +109,9 @@ Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex& index if (!file) return Qt::NoItemFlags; + if (file->builtIn() || file->fromAnotherConfigFile()) + return Qt::NoItemFlags; + // game files can always be checked if (file == mGameFile) return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; @@ -130,7 +133,7 @@ Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex& index continue; noGameFiles = false; - if (mCheckedFiles.contains(depFile)) + if (depFile->builtIn() || depFile->fromAnotherConfigFile() || mCheckedFiles.contains(depFile)) { gamefileChecked = true; break; @@ -217,14 +220,14 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex& index, int if (file == mGameFile) return QVariant(); - return mCheckedFiles.contains(file) ? Qt::Checked : Qt::Unchecked; + return (file->builtIn() || file->fromAnotherConfigFile() || mCheckedFiles.contains(file)) ? Qt::Checked : Qt::Unchecked; } case Qt::UserRole: { if (file == mGameFile) return ContentType_GameFile; - else if (flags(index)) + else return ContentType_Addon; break; @@ -279,7 +282,12 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex& index, const { int checkValue = value.toInt(); bool setState = false; - if (checkValue == Qt::Checked && !mCheckedFiles.contains(file)) + if (file->builtIn() || file->fromAnotherConfigFile()) + { + setState = false; + success = false; + } + else if (checkValue == Qt::Checked && !mCheckedFiles.contains(file)) { setState = true; success = true; @@ -374,6 +382,13 @@ bool ContentSelectorModel::ContentModel::dropMimeData( else if (parent.isValid()) beginRow = parent.row(); + int firstModifiable = 0; + while (item(firstModifiable)->builtIn() || item(firstModifiable)->fromAnotherConfigFile()) + ++firstModifiable; + + if (beginRow < firstModifiable) + return false; + QByteArray encodedData = data->data(mMimeType); QDataStream stream(&encodedData, QIODevice::ReadOnly); @@ -449,6 +464,9 @@ void ContentSelectorModel::ContentModel::addFiles(const QString& path, bool newf file->setGameFiles({}); } + if (info.fileName().compare("builtin.omwscripts", Qt::CaseInsensitive) == 0) + file->setBuiltIn(true); + if (info.fileName().endsWith(".omwscripts", Qt::CaseInsensitive)) { file->setDate(info.lastModified()); @@ -579,15 +597,20 @@ void ContentSelectorModel::ContentModel::setCurrentGameFile(const EsmFile* file) void ContentSelectorModel::ContentModel::sortFiles() { emit layoutAboutToBeChanged(); + + int firstModifiable = 0; + while (mFiles.at(firstModifiable)->builtIn() || mFiles.at(firstModifiable)->fromAnotherConfigFile()) + ++firstModifiable; + // Dependency sort std::unordered_set moved; - for (int i = mFiles.size() - 1; i > 0;) + for (int i = mFiles.size() - 1; i > firstModifiable;) { const auto file = mFiles.at(i); if (moved.find(file) == moved.end()) { int index = -1; - for (int j = 0; j < i; ++j) + for (int j = firstModifiable; j < i; ++j) { const QStringList& gameFiles = mFiles.at(j)->gameFiles(); // All addon files are implicitly dependent on the game file @@ -650,6 +673,7 @@ void ContentSelectorModel::ContentModel::setContentList(const QStringList& fileL { if (setCheckState(filepath, true)) { + // setCheckState already gracefully handles builtIn and fromAnotherConfigFile // as necessary, move plug-ins in visible list to match sequence of supplied filelist const EsmFile* file = item(filepath); int filePosition = indexFromItem(file).row(); @@ -747,7 +771,7 @@ bool ContentSelectorModel::ContentModel::setCheckState(const QString& filepath, const EsmFile* file = item(filepath); - if (!file) + if (!file || file->builtIn() || file->fromAnotherConfigFile()) return false; if (checkState) diff --git a/components/contentselector/model/esmfile.cpp b/components/contentselector/model/esmfile.cpp index e4280baef7..02ac0efa70 100644 --- a/components/contentselector/model/esmfile.cpp +++ b/components/contentselector/model/esmfile.cpp @@ -41,6 +41,16 @@ void ContentSelectorModel::EsmFile::setDescription(const QString& description) mDescription = description; } +void ContentSelectorModel::EsmFile::setBuiltIn(bool builtIn) +{ + mBuiltIn = builtIn; +} + +void ContentSelectorModel::EsmFile::setFromAnotherConfigFile(bool fromAnotherConfigFile) +{ + mFromAnotherConfigFile = fromAnotherConfigFile; +} + bool ContentSelectorModel::EsmFile::isGameFile() const { return (mGameFiles.size() == 0) @@ -76,6 +86,14 @@ QVariant ContentSelectorModel::EsmFile::fileProperty(const FileProperty prop) co return mDescription; break; + case FileProperty_BuiltIn: + return mBuiltIn; + break; + + case FileProperty_FromAnotherConfigFile: + return mFromAnotherConfigFile; + break; + case FileProperty_GameFile: return mGameFiles; break; @@ -113,6 +131,15 @@ void ContentSelectorModel::EsmFile::setFileProperty(const FileProperty prop, con mDescription = value; break; + // todo: check these work + case FileProperty_BuiltIn: + mBuiltIn = value == "true"; + break; + + case FileProperty_FromAnotherConfigFile: + mFromAnotherConfigFile = value == "true"; + break; + case FileProperty_GameFile: mGameFiles << value; break; diff --git a/components/contentselector/model/esmfile.hpp b/components/contentselector/model/esmfile.hpp index 606cc3d319..42b6cfeff6 100644 --- a/components/contentselector/model/esmfile.hpp +++ b/components/contentselector/model/esmfile.hpp @@ -26,7 +26,9 @@ namespace ContentSelectorModel FileProperty_DateModified = 3, FileProperty_FilePath = 4, FileProperty_Description = 5, - FileProperty_GameFile = 6 + FileProperty_BuiltIn = 6, + FileProperty_FromAnotherConfigFile = 7, + FileProperty_GameFile = 8, }; EsmFile(const QString& fileName = QString(), ModelItem* parent = nullptr); @@ -40,6 +42,8 @@ namespace ContentSelectorModel void setFilePath(const QString& path); void setGameFiles(const QStringList& gameFiles); void setDescription(const QString& description); + void setBuiltIn(bool builtIn); + void setFromAnotherConfigFile(bool fromAnotherConfigFile); void addGameFile(const QString& name) { mGameFiles.append(name); } QVariant fileProperty(const FileProperty prop) const; @@ -49,18 +53,27 @@ namespace ContentSelectorModel QDateTime modified() const { return mModified; } QString formatVersion() const { return mVersion; } QString filePath() const { return mPath; } + bool builtIn() const { return mBuiltIn; } + bool fromAnotherConfigFile() const { return mFromAnotherConfigFile; } /// @note Contains file names, not paths. const QStringList& gameFiles() const { return mGameFiles; } QString description() const { return mDescription; } QString toolTip() const { - return mTooltipTemlate.arg(mAuthor) + QString tooltip = mTooltipTemlate.arg(mAuthor) .arg(mVersion) .arg(mModified.toString(Qt::ISODate)) .arg(mPath) .arg(mDescription) .arg(mGameFiles.join(", ")); + + if (mBuiltIn) + tooltip += tr("
This content file cannot be disabled because it is part of OpenMW.
"); + else if (mFromAnotherConfigFile) + tooltip += tr("
This content file cannot be disabled because it is enabled in a config file other than the user one.
"); + + return tooltip; } bool isGameFile() const; @@ -82,6 +95,8 @@ namespace ContentSelectorModel QStringList mGameFiles; QString mDescription; QString mToolTip; + bool mBuiltIn = false; + bool mFromAnotherConfigFile = false; }; }