diff options
| author | Tannin <devnull@localhost> | 2014-04-05 15:36:42 +0200 |
|---|---|---|
| committer | Tannin <devnull@localhost> | 2014-04-05 15:36:42 +0200 |
| commit | cabed9b268c9f095d5e3b98a6797b0bcdcd38b1f (patch) | |
| tree | 454b03b0c5664e90fe586e7b39603e34a526d35b /src/pluginlist.cpp | |
| parent | 98e5e57a845541acddf519a81957261f58008cb9 (diff) | |
| parent | c017f4a0d50b67a44e276bd5ae8929ed3990c62c (diff) | |
Merge with branch1.1
Diffstat (limited to 'src/pluginlist.cpp')
| -rw-r--r-- | src/pluginlist.cpp | 291 |
1 files changed, 224 insertions, 67 deletions
diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index f73805fd..1f4ed9b1 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -20,8 +20,10 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include "pluginlist.h" #include "report.h" #include "inject.h" -#include <utility.h> #include "settings.h" +#include "safewritefile.h" +#include "scopeguard.h" +#include <utility.h> #include <gameinfo.h> #include <espfile.h> #include <windows_error.h> @@ -39,7 +41,6 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include <QKeyEvent> #include <QSortFilterProxyModel> -#include <tchar.h> #include <ctime> #include <algorithm> #include <stdexcept> @@ -74,8 +75,9 @@ bool ByDate(const PluginList::ESPInfo& LHS, const PluginList::ESPInfo& RHS) { } PluginList::PluginList(QObject *parent) - : QAbstractTableModel(parent), - m_FontMetrics(QFont()), m_SaveTimer(this) + : QAbstractItemModel(parent) + , m_FontMetrics(QFont()) + , m_SaveTimer(this) { m_SaveTimer.setSingleShot(true); connect(&m_SaveTimer, SIGNAL(timeout()), this, SIGNAL(saveTimer())); @@ -90,6 +92,10 @@ PluginList::PluginList(QObject *parent) } +PluginList::~PluginList() +{ +} + QString PluginList::getColumnName(int column) { @@ -97,6 +103,7 @@ QString PluginList::getColumnName(int column) case COL_NAME: return tr("Name"); case COL_PRIORITY: return tr("Priority"); case COL_MODINDEX: return tr("Mod Index"); + case COL_FLAGS: return tr("Flags"); default: return tr("unknown"); } } @@ -118,7 +125,8 @@ void PluginList::refresh(const QString &profileName, const DirectoryEntry &baseD const QString &pluginsFile, const QString &loadOrderFile, const QString &lockedOrderFile) { - emit layoutAboutToBeChanged(); + ChangeBracket<PluginList> layoutChange(this); + m_ESPsByName.clear(); m_ESPsByPriority.clear(); m_ESPs.clear(); @@ -140,8 +148,16 @@ void PluginList::refresh(const QString &profileName, const DirectoryEntry &baseD std::find(primaryPlugins.begin(), primaryPlugins.end(), ToWString(filename.toLower())) != primaryPlugins.end(); bool archive = false; - FilesOrigin &origin = baseDirectory.getOriginByID(current->getOrigin(archive)); - m_ESPs.push_back(ESPInfo(filename, forceEnabled, current->getFileTime(), ToQString(origin.getName()), ToQString(current->getFullPath()))); + try { + FilesOrigin &origin = baseDirectory.getOriginByID(current->getOrigin(archive)); + + QString iniPath = QFileInfo(filename).baseName() + ".ini"; + bool hasIni = baseDirectory.findFile(ToWString(iniPath)).get() != NULL; + + m_ESPs.push_back(ESPInfo(filename, forceEnabled, current->getFileTime(), ToQString(origin.getName()), ToQString(current->getFullPath()), hasIni)); + } catch (const std::exception &e) { + reportError(tr("failed to update esp info for file %1 (source id: %2), error: %3").arg(filename).arg(current->getOrigin(archive)).arg(e.what())); + } } } @@ -191,10 +207,12 @@ void PluginList::refresh(const QString &profileName, const DirectoryEntry &baseD readLockedOrderFrom(lockedOrderFile); + layoutChange.finish(); + refreshLoadOrder(); + emit dataChanged(this->index(0, 0), this->index(m_ESPs.size(), columnCount())); - emit layoutChanged(); - emit dataChanged(this->index(0, 0), this->index(m_ESPs.size(), 1)); + m_Refreshed(); } @@ -362,21 +380,19 @@ void PluginList::readLockedOrderFrom(const QString &fileName) } + void PluginList::writePlugins(const QString &fileName, bool writeUnchecked) const { - QFile file(fileName); - if (!file.open(QIODevice::WriteOnly)) { - throw MyException(tr("failed to open output file: %1").arg(fileName)); - } + SafeWriteFile file(fileName); QTextCodec *textCodec = writeUnchecked ? m_Utf8Codec : m_LocalCodec; - file.resize(0); + file->resize(0); - file.write(textCodec->fromUnicode("# This file was automatically generated by Mod Organizer.\r\n")); + file->write(textCodec->fromUnicode("# This file was automatically generated by Mod Organizer.\r\n")); 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) { @@ -385,35 +401,34 @@ void PluginList::writePlugins(const QString &fileName, bool writeUnchecked) cons invalidFileNames = true; qCritical("invalid plugin name %s", m_ESPs[priority].m_Name.toUtf8().constData()); } else { - file.write(textCodec->fromUnicode(m_ESPs[priority].m_Name)); + file->write(textCodec->fromUnicode(m_ESPs[priority].m_Name)); } - file.write("\r\n"); + file->write("\r\n"); + ++writtenCount; } } - file.close(); if (invalidFileNames) { reportError(tr("Some of your plugins have invalid names! These plugins can not be loaded by the game. " "Please see mo_interface.log for a list of affected plugins and rename them.")); } + file.commit(); + qDebug("%s saved", QDir::toNativeSeparators(fileName).toUtf8().constData()); } void PluginList::writeLockedOrder(const QString &fileName) const { - QFile file(fileName); - if (!file.open(QIODevice::WriteOnly)) { - throw MyException(tr("failed to open output file: %1").arg(fileName)); - } + SafeWriteFile file(fileName); - file.resize(0); - file.write(QString("# This file was automatically generated by Mod Organizer.\r\n").toUtf8()); + file->resize(0); + file->write(QString("# This file was automatically generated by Mod Organizer.\r\n").toUtf8()); for (auto iter = m_LockedOrder.begin(); iter != m_LockedOrder.end(); ++iter) { - file.write(QString("%1|%2\r\n").arg(iter->first).arg(iter->second).toUtf8()); + file->write(QString("%1|%2\r\n").arg(iter->first).arg(iter->second).toUtf8()); } - file.close(); + file.commit(); } @@ -490,6 +505,17 @@ bool PluginList::saveLoadOrder(DirectoryEntry &directoryStructure) return true; } +int PluginList::enabledCount() const +{ + int enabled = 0; + foreach (auto info, m_ESPs) { + if (info.m_Enabled) { + ++enabled; + } + } + return enabled; +} + bool PluginList::isESPLocked(int index) const { return m_LockedOrder.find(m_ESPs.at(index).m_Name.toLower()) != m_LockedOrder.end(); @@ -526,6 +552,7 @@ void PluginList::syncLoadOrder() void PluginList::refreshLoadOrder() { + ChangeBracket<PluginList> layoutChange(this); syncLoadOrder(); // set priorities according to locked load order std::map<int, QString> lockedLoadOrder; @@ -542,9 +569,7 @@ void PluginList::refreshLoadOrder() // find the location to insert at while ((targetPrio < static_cast<int>(m_ESPs.size() - 1)) && (m_ESPs[m_ESPsByPriority[targetPrio]].m_LoadOrder < iter->first)) { - if (QString::compare(m_ESPs[m_ESPsByPriority[targetPrio]].m_Name, iter->second) != 0) { - ++targetPrio; - } + ++targetPrio; } if (static_cast<size_t>(targetPrio) >= m_ESPs.size()) { @@ -564,6 +589,67 @@ void PluginList::refreshLoadOrder() } +void PluginList::lootSort() +{ + +} + +IPluginList::PluginState PluginList::state(const QString &name) const +{ + auto iter = m_ESPsByName.find(name.toLower()); + if (iter == m_ESPsByName.end()) { + return IPluginList::STATE_MISSING; + } else { + return m_ESPs[iter->second].m_Enabled ? IPluginList::STATE_ACTIVE : IPluginList::STATE_INACTIVE; + } +} + +int PluginList::priority(const QString &name) const +{ + auto iter = m_ESPsByName.find(name.toLower()); + if (iter == m_ESPsByName.end()) { + return -1; + } else { + return m_ESPs[iter->second].m_Priority; + } +} + +int PluginList::loadOrder(const QString &name) const +{ + auto iter = m_ESPsByName.find(name.toLower()); + if (iter == m_ESPsByName.end()) { + return -1; + } else { + return m_ESPs[iter->second].m_LoadOrder; + } +} + +bool PluginList::isMaster(const QString &name) const +{ + auto iter = m_ESPsByName.find(name.toLower()); + if (iter == m_ESPsByName.end()) { + return false; + } else { + return m_ESPs[iter->second].m_IsMaster; + } +} + +QString PluginList::origin(const QString &name) const +{ + auto iter = m_ESPsByName.find(name.toLower()); + if (iter == m_ESPsByName.end()) { + return false; + } else { + return m_ESPs[iter->second].m_OriginName; + } +} + +bool PluginList::onRefreshed(const std::function<void ()> &callback) +{ + auto conn = m_Refreshed.connect(callback); + return conn.connected(); +} + void PluginList::updateIndices() { m_ESPsByName.clear(); @@ -588,12 +674,14 @@ int PluginList::rowCount(const QModelIndex &parent) const int PluginList::columnCount(const QModelIndex &) const { - return 3; + return COL_LASTCOLUMN + 1; } void PluginList::testMasters() { +// emit layoutAboutToBeChanged(); + std::set<QString> enabledMasters; for (auto iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) { if (iter->m_Enabled) { @@ -602,18 +690,18 @@ void PluginList::testMasters() } for (auto iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) { - iter->m_MasterUnset = false; + iter->m_MasterUnset.clear(); if (iter->m_Enabled) { for (auto master = iter->m_Masters.begin(); master != iter->m_Masters.end(); ++master) { if (enabledMasters.find(master->toLower()) == enabledMasters.end()) { - iter->m_MasterUnset = true; - break; + iter->m_MasterUnset.insert(*master); } } } } - emit layoutChanged(); +#pragma message("emitting this seems to cause a crash!") +// emit layoutChanged(); } @@ -640,21 +728,24 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const return QVariant(); } break; } - } else if ((role == Qt::DecorationRole) && (modelIndex.column() == 0)) { - if (m_ESPs[index].m_MasterUnset) { - return QIcon(":/MO/gui/warning"); - } else if (m_LockedOrder.find(m_ESPs[index].m_Name.toLower()) != m_LockedOrder.end()) { - return QIcon(":/MO/gui/locked"); - } else { + } else if ((role == Qt::CheckStateRole) && (modelIndex.column() == 0)) { + 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::CheckStateRole) && (modelIndex.column() == 0)) { - return m_ESPs[index].m_Enabled ? Qt::Checked : Qt::Unchecked; } else if (role == Qt::FontRole) { QFont result; if (m_ESPs[index].m_IsMaster) { result.setItalic(true); result.setWeight(QFont::Bold); + } else if (m_ESPs[index].m_IsDummy) { + result.setItalic(true); } return result; } else if (role == Qt::TextAlignmentRole) { @@ -664,25 +755,74 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const return QVariant(Qt::AlignHCenter | Qt::AlignVCenter); } } else if (role == Qt::ToolTipRole) { + QString name = m_ESPs[index].m_Name.toLower(); + auto bossInfoIter = m_BossInfo.find(name); + QString toolTip; + if (bossInfoIter != m_BossInfo.end()) { + if (!bossInfoIter->second.m_BOSSMessages.isEmpty()) { + toolTip += bossInfoIter->second.m_BOSSMessages.join("<br>") + "<br><hr>"; + } + if (bossInfoIter->second.m_BOSSUnrecognized) { + toolTip += "Not recognized by BOSS<br><ht>"; + } + } if (m_ESPs[index].m_ForceEnabled) { - return tr("This plugin can't be disabled (enforced by the game)"); + toolTip += tr("This plugin can't be disabled (enforced by the game)"); } else { QString text = tr("Origin: %1").arg(m_ESPs[index].m_OriginName); - if (m_ESPs[index].m_MasterUnset) { - text += "\nDepends on the following masters: " + SetJoin(m_ESPs[index].m_Masters, ", "); + if (m_ESPs[index].m_MasterUnset.size() > 0) { + text += "<br>" + tr("Missing Masters") + ": <b>" + SetJoin(m_ESPs[index].m_MasterUnset, ", ") + "</b>"; + } + std::set<QString> enabledMasters; + std::set_difference(m_ESPs[index].m_Masters.begin(), m_ESPs[index].m_Masters.end(), + m_ESPs[index].m_MasterUnset.begin(), m_ESPs[index].m_MasterUnset.end(), + std::inserter(enabledMasters, enabledMasters.end())); + text += "<br>" + tr("Enabled Masters") + ": " + SetJoin(enabledMasters, ", "); + if (m_ESPs[index].m_HasIni) { + text += "<br>There is an ini file connected to this esp. Its settings will be added to your game settings, overwriting " + "in case of conflicts."; + } else if (m_ESPs[index].m_IsDummy) { + text += "<br>This file is a dummy! It exists only so the bsa with the same name gets loaded. With MO you don't need this: " + "If you enable the archive with the same name in the \"Archive\" tab you can disable this plugin."; } - return text; + toolTip += text; } - } else { - return QVariant(); + return toolTip; + } else if (role == Qt::UserRole + 1) { + QVariantList result; + QString nameLower = m_ESPs[index].m_Name.toLower(); + if (m_ESPs[index].m_MasterUnset.size() > 0) { + result.append(QIcon(":/MO/gui/warning")); + } + if (m_LockedOrder.find(nameLower) != m_LockedOrder.end()) { + result.append(QIcon(":/MO/gui/locked")); + } + auto bossInfoIter = m_BossInfo.find(nameLower); + if (bossInfoIter != m_BossInfo.end()) { + if (!bossInfoIter->second.m_BOSSMessages.isEmpty()) { + result.append(QIcon(":/MO/gui/information")); + } + if (bossInfoIter->second.m_BOSSUnrecognized) { + result.append(QIcon(":/MO/gui/help")); + } + } + if (m_ESPs[index].m_HasIni) { + result.append(QIcon(":/MO/gui/attachment")); + } + if (m_ESPs[index].m_IsDummy && m_ESPs[index].m_Enabled && !m_ESPs[index].m_HasIni) { + result.append(QIcon(":/MO/gui/edit_clear")); + } + return result; } + return QVariant(); } -bool PluginList::setData(const QModelIndex &index, const QVariant &value, int role) +bool PluginList::setData(const QModelIndex &modIndex, const QVariant &value, int role) { if (role == Qt::CheckStateRole) { - m_ESPs[index.row()].m_Enabled = value.toInt() == Qt::Checked; + m_ESPs[modIndex.row()].m_Enabled = value.toInt() == Qt::Checked; + emit dataChanged(modIndex, modIndex); refreshLoadOrder(); startSaveTime(); @@ -716,13 +856,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; } @@ -738,18 +877,18 @@ void PluginList::setPluginPriority(int row, int &newPriority) if (!m_ESPs[row].m_IsMaster) { // don't allow esps to be moved above esms while ((newPriorityTemp < static_cast<int>(m_ESPsByPriority.size() - 1)) && - m_ESPs[m_ESPsByPriority[newPriorityTemp]].m_IsMaster) { + m_ESPs.at(m_ESPsByPriority.at(newPriorityTemp)).m_IsMaster) { ++newPriorityTemp; } } else { // don't allow esms to be moved below esps while ((newPriorityTemp > 0) && - !m_ESPs[m_ESPsByPriority[newPriorityTemp]].m_IsMaster) { + !m_ESPs.at(m_ESPsByPriority.at(newPriorityTemp)).m_IsMaster) { --newPriorityTemp; } // also don't allow "regular" esms to be moved above primary plugins while ((newPriorityTemp < static_cast<int>(m_ESPsByPriority.size() - 1)) && - (m_ESPs[m_ESPsByPriority[newPriorityTemp]].m_ForceEnabled)) { + (m_ESPs.at(m_ESPsByPriority.at(newPriorityTemp)).m_ForceEnabled)) { ++newPriorityTemp; } } @@ -765,14 +904,17 @@ void PluginList::setPluginPriority(int row, int &newPriority) for (int i = oldPriority + 1; i <= newPriorityTemp; ++i) { --m_ESPs.at(m_ESPsByPriority.at(i)).m_Priority; } + emit dataChanged(index(oldPriority + 1, 0), index(newPriorityTemp, columnCount())); } else { for (int i = newPriorityTemp; i < oldPriority; ++i) { ++m_ESPs.at(m_ESPsByPriority.at(i)).m_Priority; } + emit dataChanged(index(newPriorityTemp, 0), index(oldPriority - 1, columnCount())); ++newPriority; } m_ESPs.at(row).m_Priority = newPriorityTemp; + emit dataChanged(index(row, 0), index(row, columnCount())); } catch (const std::out_of_range&) { reportError(tr("failed to restore load order for %1").arg(m_ESPs[row].m_Name)); } @@ -783,7 +925,7 @@ void PluginList::setPluginPriority(int row, int &newPriority) void PluginList::changePluginPriority(std::vector<int> rows, int newPriority) { - emit layoutAboutToBeChanged(); + ChangeBracket<PluginList> layoutChange(this); // sort rows to insert by their old priority (ascending) and insert them move them in that order const std::vector<ESPInfo> &esp = m_ESPs; std::sort(rows.begin(), rows.end(), @@ -804,11 +946,11 @@ void PluginList::changePluginPriority(std::vector<int> rows, int newPriority) for (std::vector<int>::const_iterator iter = rows.begin(); iter != rows.end(); ++iter) { setPluginPriority(*iter, newPriority); } + + layoutChange.finish(); refreshLoadOrder(); startSaveTime(); - - emit layoutChanged(); } @@ -859,6 +1001,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) { @@ -870,7 +1025,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))) { @@ -894,7 +1048,6 @@ bool PluginList::eventFilter(QObject *obj, QEvent *event) int newPriority = m_ESPs[idx.row()].m_Priority + diff; if ((newPriority >= 0) && (newPriority < rowCount())) { setPluginPriority(idx.row(), newPriority); - emit dataChanged(this->index(idx.row(), 0), this->index(idx.row(), this->columnCount() - 1)); } } refreshLoadOrder(); @@ -926,19 +1079,23 @@ bool PluginList::eventFilter(QObject *obj, QEvent *event) } -PluginList::ESPInfo::ESPInfo(const QString &name, bool enabled, FILETIME time, const QString &originName, const QString &fullPath) - : m_Name(name), m_Enabled(enabled), m_ForceEnabled(enabled), m_Removed(false), m_Priority(0), - m_LoadOrder(-1), m_Time(time), m_OriginName(originName) +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_Priority(0), m_LoadOrder(-1), m_Time(time), m_OriginName(originName), m_HasIni(hasIni) { try { ESP::File file(ToWString(fullPath)); m_IsMaster = file.isMaster(); + m_IsDummy = file.isDummy(); std::set<std::string> masters = file.masters(); for (auto iter = masters.begin(); iter != masters.end(); ++iter) { m_Masters.insert(QString(iter->c_str())); } } catch (const std::exception &e) { - reportError(tr("failed to parse esp file %1: %2").arg(fullPath).arg(e.what())); + qCritical("failed to parse esp file %s: %s", qPrintable(fullPath), e.what()); m_IsMaster = false; + m_IsDummy = false; } } |
