diff options
Diffstat (limited to 'src/pluginlist.cpp')
| -rw-r--r-- | src/pluginlist.cpp | 283 |
1 files changed, 42 insertions, 241 deletions
diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index ec063994..24504f92 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -75,10 +75,9 @@ bool ByDate(const PluginList::ESPInfo& LHS, const PluginList::ESPInfo& RHS) { } PluginList::PluginList(QObject *parent) - : QAbstractTableModel(parent) + : QAbstractItemModel(parent) , m_FontMetrics(QFont()) , m_SaveTimer(this) - , m_BOSS(NULL) { m_SaveTimer.setSingleShot(true); connect(&m_SaveTimer, SIGNAL(timeout()), this, SIGNAL(saveTimer())); @@ -95,11 +94,6 @@ PluginList::PluginList(QObject *parent) PluginList::~PluginList() { - if (m_BOSS != NULL) { - m_BOSS->CleanUpAPI(); - delete m_BOSS; - m_BOSS = NULL; - } } @@ -397,16 +391,19 @@ void PluginList::writePlugins(const QString &fileName, bool writeUnchecked) cons file->write(textCodec->fromUnicode("# This file was automatically generated by Mod Organizer.\r\n")); + QStringList saveList; + bool invalidFileNames = false; int writtenCount = 0; for (size_t i = 0; i < m_ESPs.size(); ++i) { int priority = m_ESPsByPriority[i]; - if ((m_ESPs[priority].m_Enabled || writeUnchecked) && !m_ESPs[priority].m_Removed) { + if (m_ESPs[priority].m_Enabled || writeUnchecked) { //file.write(m_ESPs[priority].m_Name.toUtf8()); if (!textCodec->canEncode(m_ESPs[priority].m_Name)) { invalidFileNames = true; qCritical("invalid plugin name %s", m_ESPs[priority].m_Name.toUtf8().constData()); } else { + saveList << m_ESPs[priority].m_Name; file->write(textCodec->fromUnicode(m_ESPs[priority].m_Name)); } file->write("\r\n"); @@ -419,9 +416,9 @@ void PluginList::writePlugins(const QString &fileName, bool writeUnchecked) cons "Please see mo_interface.log for a list of affected plugins and rename them.")); } - file.commit(); - - qDebug("%s saved", QDir::toNativeSeparators(fileName).toUtf8().constData()); + if (file.commitIfDifferent(m_LastSaveHash[fileName])) { + qDebug("%s saved", QDir::toNativeSeparators(fileName).toUtf8().constData()); + } } @@ -438,7 +435,11 @@ void PluginList::writeLockedOrder(const QString &fileName) const } -void PluginList::saveTo(const QString &pluginFileName, const QString &loadOrderFileName, const QString &lockedOrderFileName, const QString& deleterFileName, bool hideUnchecked) const +void PluginList::saveTo(const QString &pluginFileName + , const QString &loadOrderFileName + , const QString &lockedOrderFileName + , const QString& deleterFileName + , bool hideUnchecked) const { writePlugins(pluginFileName, false); writePlugins(loadOrderFileName, true); @@ -452,7 +453,7 @@ void PluginList::saveTo(const QString &pluginFileName, const QString &loadOrderF for (size_t i = 0; i < m_ESPs.size(); ++i) { int priority = m_ESPsByPriority[i]; - if (m_ESPs[priority].m_Removed) { + if (!m_ESPs[priority].m_Enabled) { deleterFile.write(m_ESPs[priority].m_Name.toUtf8()); deleterFile.write("\r\n"); } @@ -595,226 +596,7 @@ void PluginList::refreshLoadOrder() } -class boss_exception : public std::runtime_error { -public: - boss_exception(const std::string &message) : std::runtime_error(message) {} -}; - -#define THROW_BOSS_ERROR(obj) \ - uint8_t *message; \ - obj->GetLastErrorDetails(&message); \ - throw boss_exception(std::string(reinterpret_cast<char*>(message))); - -#define U8(text) reinterpret_cast<const uint8_t*>(text) - - -void outputBossLog(const QString &filename) -{ - QFile file(filename); - if (file.open(QIODevice::ReadOnly)) { - while (!file.atEnd()) { - QByteArray line = file.readLine().trimmed(); - if (line.length() < 1) - continue; - - int endFirstWord = line.indexOf(':'); - if (endFirstWord < 0) { - endFirstWord = 0; - } - QString firstWord = line.mid(0, endFirstWord); - if (firstWord == "DEBUG") { - qDebug("(boss) %s", line.mid(endFirstWord + 2).constData()); - } else { - qWarning("(boss) %s", line.mid(endFirstWord + 2).constData()); - } - } - } - file.resize(0); -} - - -boss_db PluginList::initBoss() -{ - boss_db result; - bool firstRun = (m_BOSS == nullptr); - if (firstRun) { - m_BOSS = new BossDLL(TEXT("dlls\\boss.dll")); - - if (!m_BOSS->IsCompatibleVersion(2,1,1)) { - throw MyException(tr("BOSS dll incompatible")); - } - uint8_t *versionString; - if (m_BOSS->GetVersionString(&versionString) != BossDLL::RESULT_OK) { - THROW_BOSS_ERROR(m_BOSS) - } - qDebug("using boss version %s", versionString); - - m_TempFile.open(); m_TempFile.close(); // yeah, stupid, but open is required to generate the name - m_BOSS->SetLoggerOutput(m_TempFile.fileName().toLocal8Bit().constData(), 4); - } - - if (m_BOSS->CreateBossDb(&result, BossDLL::SKYRIM, NULL) != BossDLL::RESULT_OK) { - uint8_t *message; - m_BOSS->GetLastErrorDetails(&message); - std::string messageCopy(reinterpret_cast<const char*>(message)); - delete m_BOSS; - m_BOSS = NULL; - throw boss_exception(messageCopy); - } - qApp->processEvents(); - - QString masterlistName = QDir::toNativeSeparators(qApp->applicationDirPath() + "/boss/masterlist.txt"); - - if (firstRun) { - uint32_t res = m_BOSS->UpdateMasterlist(result, U8(masterlistName.toUtf8().constData())); - qApp->processEvents(); - if (res == BossDLL::RESULT_OK) { - qDebug("boss masterlist updated"); - } else if (res == BossDLL::RESULT_NO_UPDATE_NECESSARY) { - qDebug("boss masterlist already up-to-date"); - } else { - THROW_BOSS_ERROR(m_BOSS) - } - } - if (m_BOSS->Load(result, - U8(masterlistName.toUtf8().constData()), - U8(QDir::toNativeSeparators(qApp->applicationDirPath() + "/boss/userlist.txt").toUtf8().constData())) != BossDLL::RESULT_OK) { - THROW_BOSS_ERROR(m_BOSS) - } - return result; -} - -void PluginList::convertPluginListForBoss(boss_db db, boost::ptr_vector<uint8_t> &inputPlugins, std::vector<uint8_t*> &activePlugins) -{ - foreach (int idx, m_ESPsByPriority) { - QString fileName = m_ESPs[idx].m_Name; - QByteArray name = fileName.toUtf8(); - - uint8_t *nameU8 = new uint8_t[name.length() + 1]; - memcpy(nameU8, name.constData(), name.length() + 1); - if (m_ESPs[idx].m_Enabled) { - activePlugins.push_back(nameU8); - } - inputPlugins.push_back(nameU8); - } - if (m_BOSS->SetActivePluginsDumb(db, &activePlugins[0], activePlugins.size()) != BossDLL::RESULT_OK) { - THROW_BOSS_ERROR(m_BOSS) - } -} - -void PluginList::applyBOSSSorting(boss_db db, std::map<int, QString> &lockedLoadOrder, uint8_t **pluginList, size_t size, - int &priority, int &loadOrder, bool recognized, const char *extension) -{ - for (size_t i = 0; i < size; ++i) { - QString name = QString::fromUtf8(reinterpret_cast<const char*>(pluginList[i])).toLower(); - if (name.endsWith(extension)) { - auto iter = m_ESPsByName.find(name); - if (iter == m_ESPsByName.end()) { - // boss seems to report plugins from userlist as sorted that aren't even installed - continue; - } - - BossMessage *message; - size_t numMessages = 0; - m_BOSS->GetPluginMessages(db, pluginList[i], &message, &numMessages); - BossInfo newInfo; - for (size_t im = 0; im < numMessages; ++im) { - newInfo.m_BOSSMessages.append(QString::fromUtf8(reinterpret_cast<const char*>(message[im].message))); - } - newInfo.m_BOSSUnrecognized = !recognized; - m_BossInfo[name] = newInfo; - // locked order plugins are not inserted by boss sorting ... - if (m_LockedOrder.find(name) != m_LockedOrder.end()) { - continue; - } - - // ... but by their enforced priority - while (lockedLoadOrder.find(loadOrder) != lockedLoadOrder.end()) { - auto lloIter = lockedLoadOrder.find(loadOrder); - auto nameIter = m_ESPsByName.find(lloIter->second); - if (nameIter != m_ESPsByName.end()) { - m_ESPs[nameIter->second].m_Priority = priority++; - if (m_ESPs[nameIter->second].m_Enabled) { - m_ESPs[nameIter->second].m_LoadOrder = loadOrder++; - } else { - m_ESPs[nameIter->second].m_LoadOrder = -1; - } - } - lockedLoadOrder.erase(lloIter); - } - - m_ESPs[iter->second].m_Priority = priority++; - if (m_ESPs[iter->second].m_Enabled) { - m_ESPs[iter->second].m_LoadOrder = loadOrder++; - } else { - m_ESPs[iter->second].m_LoadOrder = -1; - } - } - } -} - -void PluginList::bossSort() -{ - boss_db db = initBoss(); - ON_BLOCK_EXIT([&] { m_BOSS->DestroyBossDb(db); }); - - // create a boss-compatible representation of our current mod list. - boost::ptr_vector<uint8_t> inputPlugins; - std::vector<uint8_t*> activePlugins; - convertPluginListForBoss(db, inputPlugins, activePlugins); - - // sort mods in-memory - uint8_t **sortedPlugins; - uint8_t **unrecognizedPlugins; - size_t sizeSorted, sizeUnrecognized; - if (m_BOSS->SortCustomMods(db, - inputPlugins.c_array(), inputPlugins.size(), - &sortedPlugins, &sizeSorted, - &unrecognizedPlugins, &sizeUnrecognized) != BossDLL::RESULT_OK) { - THROW_BOSS_ERROR(m_BOSS) - } - - // output the log from boss to our own log to make it visible - outputBossLog(m_TempFile.fileName()); - - qDebug("%d sorted, %d unrecognized", sizeSorted, sizeUnrecognized); - - ChangeBracket<PluginList> layoutChange(this); - - std::map<int, QString> lockedLoadOrder; - std::for_each(m_LockedOrder.begin(), m_LockedOrder.end(), - [&lockedLoadOrder] (const std::pair<QString, int> &ele) { lockedLoadOrder[ele.second] = ele.first; }); - - int priority = 0; - int loadOrder = 0; - applyBOSSSorting(db, lockedLoadOrder, sortedPlugins, sizeSorted, priority, loadOrder, true, "esm"); - applyBOSSSorting(db, lockedLoadOrder, unrecognizedPlugins, sizeUnrecognized, priority, loadOrder, false, "esm"); - applyBOSSSorting(db, lockedLoadOrder, sortedPlugins, sizeSorted, priority, loadOrder, true, "esp"); - applyBOSSSorting(db, lockedLoadOrder, unrecognizedPlugins, sizeUnrecognized, priority, loadOrder, false, "esp"); - - // applyBOSSSorting removed entries from lockedLoadOrder when they were inserted so everything that's left is plugins - // locked to the end of the list. Now this inserts the rest in the ascending priority order. If the list is too short - // to place the plugins in their intended position then this guarantees plugins are kept in the correct relative order - // but it doesn't minimize the number of misplaced plugins. - for (auto iter = lockedLoadOrder.begin(); iter != lockedLoadOrder.end(); ++iter) { - auto nameIter = m_ESPsByName.find(iter->second); - if (nameIter != m_ESPsByName.end()) { - m_ESPs[nameIter->second].m_Priority = priority++; - if (m_ESPs[nameIter->second].m_Enabled) { - m_ESPs[nameIter->second].m_LoadOrder = loadOrder++; - } else { - m_ESPs[nameIter->second].m_LoadOrder = -1; - } - } - } - - // inform view of the changed data - updateIndices(); - layoutChange.finish(); - emit dataChanged(this->index(0, 0), this->index(m_ESPs.size(), columnCount())); - m_Refreshed(); -} IPluginList::PluginState PluginList::state(const QString &name) const { @@ -951,7 +733,16 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const } break; } } else if ((role == Qt::CheckStateRole) && (modelIndex.column() == 0)) { - return m_ESPs[index].m_Enabled ? Qt::Checked : Qt::Unchecked; + if (m_ESPs[index].m_ForceEnabled) { + return QVariant(); + } else { + return m_ESPs[index].m_Enabled ? Qt::Checked : Qt::Unchecked; + } + } else if (role == Qt::ForegroundRole) { + if ((modelIndex.column() == COL_NAME) && + m_ESPs[index].m_ForceEnabled) { + return QBrush(Qt::gray); + } } else if (role == Qt::FontRole) { QFont result; if (m_ESPs[index].m_IsMaster) { @@ -1026,9 +817,8 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const result.append(QIcon(":/MO/gui/edit_clear")); } return result; - } else { - return QVariant(); } + return QVariant(); } @@ -1070,13 +860,12 @@ QVariant PluginList::headerData(int section, Qt::Orientation orientation, Qt::ItemFlags PluginList::flags(const QModelIndex &modelIndex) const { int index = modelIndex.row(); - Qt::ItemFlags result = QAbstractTableModel::flags(modelIndex); + Qt::ItemFlags result = QAbstractItemModel::flags(modelIndex); if (modelIndex.isValid()) { - if ((m_ESPs[index].m_ForceEnabled)) { - result &= ~Qt::ItemIsEnabled; + if (!m_ESPs[index].m_ForceEnabled) { + result |= Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled; } - result |= Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled; } else { result |= Qt::ItemIsDropEnabled; } @@ -1216,6 +1005,19 @@ bool PluginList::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, return false; } +QModelIndex PluginList::index(int row, int column, const QModelIndex&) const +{ + if ((row < 0) || (row >= rowCount()) || (column < 0) || (column >= columnCount())) { + return QModelIndex(); + } + return createIndex(row, column, row); +} + +QModelIndex PluginList::parent(const QModelIndex&) const +{ + return QModelIndex(); +} + bool PluginList::eventFilter(QObject *obj, QEvent *event) { @@ -1227,7 +1029,6 @@ bool PluginList::eventFilter(QObject *obj, QEvent *event) } QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event); - // ctrl+up and ctrl+down -> increase or decrease priority of selected plugins if ((keyEvent->modifiers() == Qt::ControlModifier) && ((keyEvent->key() == Qt::Key_Up) || (keyEvent->key() == Qt::Key_Down))) { @@ -1285,7 +1086,7 @@ bool PluginList::eventFilter(QObject *obj, QEvent *event) PluginList::ESPInfo::ESPInfo(const QString &name, bool enabled, FILETIME time, const QString &originName, const QString &fullPath, bool hasIni) - : m_Name(name), m_Enabled(enabled), m_ForceEnabled(enabled), m_Removed(false), + : m_Name(name), m_Enabled(enabled), m_ForceEnabled(enabled), m_Priority(0), m_LoadOrder(-1), m_Time(time), m_OriginName(originName), m_HasIni(hasIni) { try { |
