/* Copyright (C) 2012 Sebastian Herbord. All rights reserved. This file is part of Mod Organizer. Mod Organizer is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Mod Organizer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see . */ #ifndef PLUGINLIST_H #define PLUGINLIST_H #include #include #include "profile.h" #include "loot.h" namespace MOBase { class IPluginGame; } #include #include #include #include #include #pragma warning(push) #pragma warning(disable: 4100) #ifndef Q_MOC_RUN #include #include #endif #include #include template class ChangeBracket { public: ChangeBracket(C *model) : m_Model(nullptr) { QVariant var = model->property("__aboutToChange"); bool aboutToChange = var.isValid() && var.toBool(); if (!aboutToChange) { model->layoutAboutToBeChanged(); model->setProperty("__aboutToChange", true); m_Model = model; } } ~ChangeBracket() { finish(); } void finish() { if (m_Model != nullptr) { m_Model->layoutChanged(); m_Model->setProperty("__aboutToChange", false); m_Model = nullptr; } } private: C *m_Model; }; /** * @brief model representing the plugins (.esp/.esm) in the current virtual data folder **/ class PluginList : public QAbstractItemModel, public MOBase::IPluginList { Q_OBJECT friend class ChangeBracket; public: enum EColumn { COL_NAME, COL_FLAGS, COL_PRIORITY, COL_MODINDEX, COL_LASTCOLUMN = COL_MODINDEX }; typedef boost::signals2::signal SignalRefreshed; typedef boost::signals2::signal SignalPluginMoved; typedef boost::signals2::signal SignalPluginStateChanged; public: /** * @brief constructor * * @param parent parent object **/ PluginList(QObject *parent = nullptr); ~PluginList(); /** * @brief does a complete refresh of the list * * @param profileName name of the current profile * @param baseDirectory the root directory structure representing the virtual data directory * @param lockedOrderFile list of plugins that shouldn't change load order * @todo the profile is not used? If it was, we should pass the Profile-object instead **/ void refresh(const QString &profileName , const MOShared::DirectoryEntry &baseDirectory , const QString &lockedOrderFile , bool refresh); /** * @brief enable a plugin based on its name * * @param name name of the plugin to enable * @param enable set to true to enable the esp, false to disable it **/ void enableESP(const QString &name, bool enable = true); /** * @brief test if a plugin is enabled * * @param name name of the plugin to look up * @return true if the plugin is enabled, false otherwise **/ bool isEnabled(const QString &name); /** * @brief clear all additional information we stored on plugins */ void clearAdditionalInformation(); /** * @brief reset additional information on a mod * @param name name of the plugin to clear the information of */ void clearInformation(const QString &name); /** * @brief add additional information on a mod (i.e. from loot) * @param name name of the plugin to add information about * @param message the message to add to the plugin */ void addInformation(const QString &name, const QString &message); /** * adds information from a loot report */ void addLootReport(const QString& name, Loot::Plugin plugin); /** * @brief test if a plugin is enabled * * @param index index of the plugin to look up * @return true if the plugin is enabled, false otherwise * @throws std::out_of_range exception is thrown if index is invalid **/ bool isEnabled(int index); /** * @brief save the plugin status to the specified file * * @param lockedOrderFileName path of the lockedorder.txt to write to * @param deleterFileName file to receive a list of files to hide from the virtual data tree. This is used to hide unchecked plugins if "hideUnchecked" is true * @param hideUnchecked if true, plugins that aren't enabled will be hidden from the virtual data directory **/ void saveTo(const QString &lockedOrderFileName , const QString &deleterFileName , bool hideUnchecked) const; /** * @brief save the current load order * * the load order used by the game is defined by the last modification time which this * function sets. An exception is newer version of skyrim where the load order is defined * by the order of files in plugins.txt * @param directoryStructure the root directory structure representing the virtual data directory * @return true on success or if there was nothing to save, false if the load order can't be saved, i.e. because files are locked * @todo since this works on actual files the load order can't be configured per-profile. Files of the same name * in different mods can also have different load orders which makes this very intransparent * @note also stores to disk the list of locked esps **/ bool saveLoadOrder(MOShared::DirectoryEntry &directoryStructure); /** * @return number of enabled plugins in the list */ int enabledCount() const; int timeElapsedSinceLastChecked() const; QString getName(int index) const { return m_ESPs.at(index).name; } int getPriority(int index) const { return m_ESPs.at(index).priority; } QString getIndexPriority(int index) const; bool isESPLocked(int index) const; void lockESPIndex(int index, bool lock); bool eventFilter(QObject *obj, QEvent *event); static QString getColumnName(int column); static QString getColumnToolTip(int column); void highlightPlugins(const QItemSelectionModel *selection, const MOShared::DirectoryEntry &directoryEntry, const Profile &profile); void refreshLoadOrder(); void disconnectSlots(); public: virtual QStringList pluginNames() const override; virtual PluginStates state(const QString &name) const; virtual void setState(const QString &name, PluginStates state) override; virtual int priority(const QString &name) const; virtual int loadOrder(const QString &name) const; virtual bool onRefreshed(const std::function &callback); virtual bool isMaster(const QString &name) const; virtual bool isLight(const QString &name) const; virtual bool isLightFlagged(const QString &name) const; virtual QStringList masters(const QString &name) const; virtual QString origin(const QString &name) const; virtual void setLoadOrder(const QStringList &pluginList) override; virtual bool onPluginMoved(const std::function &func); virtual bool onPluginStateChanged(const std::function &func) override; public: // implementation of the QAbstractTableModel interface virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; virtual Qt::DropActions supportedDropActions() const { return Qt::MoveAction; } virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; virtual QModelIndex parent(const QModelIndex &child) const; public slots: /** * @brief enables selected plugins **/ void enableSelected(const QItemSelectionModel *selectionModel); /** * @brief disables selected plugins **/ void disableSelected(const QItemSelectionModel *selectionModel); /** * @brief enables ALL plugins **/ void enableAll(); /** * @brief disables ALL plugins **/ void disableAll(); /** * @brief moves selected plugins to specified priority **/ void sendToPriority(const QItemSelectionModel *selectionModel, int priority); /** * @brief The currently managed game has changed * @param gamePlugin */ void managedGameChanged(MOBase::IPluginGame const *gamePlugin); /** * @brief Generate the plugin indexes because something was changed **/ void generatePluginIndexes(); signals: /** * @brief emitted when the plugin list changed, i.e. the load order was modified or a plugin was checked/unchecked * @note this is currently only used to signal that there are changes that can be saved, it does * not immediately cause anything to be written to disc **/ void esplist_changed(); void writePluginsList(); private: struct ESPInfo { ESPInfo( const QString &name, bool enabled, const QString &originName, const QString &fullPath, bool hasIni, std::set archives, bool lightSupported); QString name; QString fullPath; bool enabled; bool forceEnabled; int priority; QString index; int loadOrder; FILETIME time; QString originName; bool isMaster; bool isLight; bool isLightFlagged; bool modSelected; QString author; QString description; bool hasIni; std::set archives; std::set masters; mutable std::set masterUnset; bool operator < (const ESPInfo& str) const { return (loadOrder < str.loadOrder); } }; struct AdditionalInfo { QStringList messages; Loot::Plugin loot; }; friend bool ByName(const ESPInfo& LHS, const ESPInfo& RHS); friend bool ByDate(const ESPInfo& LHS, const ESPInfo& RHS); friend bool ByPriority(const ESPInfo& LHS, const ESPInfo& RHS); private: void syncLoadOrder(); void updateIndices(); void writeLockedOrder(const QString &fileName) const; void readLockedOrderFrom(const QString &fileName); void setPluginPriority(int row, int &newPriority); void changePluginPriority(std::vector rows, int newPriority); void testMasters(); void fixPriorities(); int findPluginByPriority(int priority); private: std::vector m_ESPs; mutable std::map m_LastSaveHash; std::map m_ESPsByName; std::vector m_ESPsByPriority; std::map m_LockedOrder; std::map m_AdditionalInfo; // maps esp names to boss information QString m_CurrentProfile; QFontMetrics m_FontMetrics; SignalRefreshed m_Refreshed; SignalPluginMoved m_PluginMoved; SignalPluginStateChanged m_PluginStateChanged; QTemporaryFile m_TempFile; QTime m_LastCheck; const MOBase::IPluginGame *m_GamePlugin; QVariant displayData(const QModelIndex &modelIndex) const; QVariant checkstateData(const QModelIndex &modelIndex) const; QVariant foregroundData(const QModelIndex &modelIndex) const; QVariant backgroundData(const QModelIndex &modelIndex) const; QVariant fontData(const QModelIndex &modelIndex) const; QVariant alignmentData(const QModelIndex &modelIndex) const; QVariant tooltipData(const QModelIndex &modelIndex) const; QVariant iconData(const QModelIndex &modelIndex) const; QString makeLootTooltip(const Loot::Plugin& loot) const; bool isProblematic(const ESPInfo& esp, const AdditionalInfo* info) const; bool hasInfo(const ESPInfo& esp, const AdditionalInfo* info) const; }; #pragma warning(pop) #endif // PLUGINLIST_H