summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/env.cpp8
-rw-r--r--src/env.h4
-rw-r--r--src/mainwindow.cpp6
-rw-r--r--src/modinfo.h5
-rw-r--r--src/modinfowithconflictinfo.cpp107
-rw-r--r--src/modinfowithconflictinfo.h53
-rw-r--r--src/modlist.cpp8
-rw-r--r--src/modlist.h4
-rw-r--r--src/modlistview.cpp5
-rw-r--r--src/organizer.pro5
-rw-r--r--src/organizercore.cpp77
-rw-r--r--src/organizercore.h5
-rw-r--r--src/selfupdater.cpp30
-rw-r--r--src/updatedialog.cpp67
-rw-r--r--src/updatedialog.h28
-rw-r--r--src/updatedialog.ui211
17 files changed, 458 insertions, 166 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9b00cc5c..7f8da6f2 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -18,6 +18,7 @@ add_filter(NAME src/application GROUPS
multiprocess
sanitychecks
selfupdater
+ updatedialog
)
add_filter(NAME src/browser GROUPS
diff --git a/src/env.cpp b/src/env.cpp
index 76473dfb..4f4bc555 100644
--- a/src/env.cpp
+++ b/src/env.cpp
@@ -418,9 +418,9 @@ QString prependToPath(const QString& s)
return old;
}
-QString setPath(const QString& s)
+void setPath(const QString& s)
{
- return set("PATH", s);
+ set("PATH", s);
}
QString get(const QString& name)
@@ -457,11 +457,9 @@ QString get(const QString& name)
return QString::fromWCharArray(buffer.get(), realSize);
}
-QString set(const QString& n, const QString& v)
+void set(const QString& n, const QString& v)
{
- auto old = get(n);
::SetEnvironmentVariableW(n.toStdWString().c_str(), v.toStdWString().c_str());
- return old;
}
diff --git a/src/env.h b/src/env.h
index 2152a40d..4379b30b 100644
--- a/src/env.h
+++ b/src/env.h
@@ -233,12 +233,12 @@ private:
// environment variables
//
QString get(const QString& name);
-QString set(const QString& name, const QString& value);
+void set(const QString& name, const QString& value);
QString path();
QString appendToPath(const QString& s);
QString prependToPath(const QString& s);
-QString setPath(const QString& s);
+void setPath(const QString& s);
class Service
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 713037e9..0fabd0dc 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -2951,7 +2951,6 @@ void MainWindow::nxmModInfoAvailable(QString gameName, int modID, QVariant userD
}
std::vector<ModInfo::Ptr> modsList = ModInfo::getByModID(gameNameReal, modID);
for (auto mod : modsList) {
- bool foundUpdate = false;
QDateTime now = QDateTime::currentDateTimeUtc();
QDateTime updateTarget = mod->getExpires();
if (now >= updateTarget) {
@@ -2959,7 +2958,6 @@ void MainWindow::nxmModInfoAvailable(QString gameName, int modID, QVariant userD
// with an older version than the main mod version.
if (mod->getNexusFileStatus() != 3 && mod->getNexusFileStatus() != 5) {
mod->setNewestVersion(result["version"].toString());
- foundUpdate = true;
}
// update the LastNexusUpdate time in any case since we did perform the check.
mod->setLastNexusUpdate(QDateTime::currentDateTimeUtc());
@@ -2979,9 +2977,7 @@ void MainWindow::nxmModInfoAvailable(QString gameName, int modID, QVariant userD
mod->setNexusLastModified(QDateTime::fromSecsSinceEpoch(result["updated_timestamp"].toInt(), Qt::UTC));
mod->saveMeta();
- if (foundUpdate) {
- m_OrganizerCore.modList()->notifyChange(ModInfo::getIndex(mod->name()));
- }
+ m_OrganizerCore.modList()->notifyChange(ModInfo::getIndex(mod->name()));
}
}
diff --git a/src/modinfo.h b/src/modinfo.h
index f93296d2..b835f352 100644
--- a/src/modinfo.h
+++ b/src/modinfo.h
@@ -897,11 +897,6 @@ public: // Conflicts
//
virtual const std::set<unsigned int>& getModArchiveLooseOverwritten() const { return s_EmptySet; }
- /**
- * @brief Update conflict information.
- */
- virtual void doConflictCheck() const {}
-
public slots:
/**
diff --git a/src/modinfowithconflictinfo.cpp b/src/modinfowithconflictinfo.cpp
index 7a51a727..7cdee6e6 100644
--- a/src/modinfowithconflictinfo.cpp
+++ b/src/modinfowithconflictinfo.cpp
@@ -19,11 +19,11 @@ ModInfoWithConflictInfo::ModInfoWithConflictInfo(OrganizerCore& core) :
m_FileTree([this]() { return QDirFileTree::makeTree(absolutePath()); }),
m_Valid([this]() { return doIsValid(); }),
m_Contents([this]() { return doGetContents(); }),
- m_HasLooseOverwrite(false), m_HasHiddenFiles(false) {}
+ m_Conflicts([this]() { return doConflictCheck(); }) { }
void ModInfoWithConflictInfo::clearCaches()
{
- m_LastConflictCheck = QTime();
+ m_Conflicts.invalidate();
}
std::vector<ModInfo::EFlag> ModInfoWithConflictInfo::getFlags() const
@@ -82,14 +82,9 @@ std::vector<ModInfo::EConflictFlag> ModInfoWithConflictInfo::getConflictFlags()
}
-void ModInfoWithConflictInfo::doConflictCheck() const
+ModInfoWithConflictInfo::Conflicts ModInfoWithConflictInfo::doConflictCheck() const
{
- m_OverwriteList.clear();
- m_OverwrittenList.clear();
- m_ArchiveOverwriteList.clear();
- m_ArchiveOverwrittenList.clear();
- m_ArchiveLooseOverwriteList.clear();
- m_ArchiveLooseOverwrittenList.clear();
+ Conflicts conflicts;
bool providesAnything = false;
bool hasHiddenFiles = false;
@@ -102,10 +97,6 @@ void ModInfoWithConflictInfo::doConflictCheck() const
std::wstring name = ToWString(this->name());
const std::wstring hideExt = ToWString(ModInfo::s_HiddenExt);
- m_CurrentConflictState = CONFLICT_NONE;
- m_ArchiveConflictState = CONFLICT_NONE;
- m_ArchiveConflictLooseState = CONFLICT_NONE;
-
if (m_Core.directoryStructure()->originExists(name)) {
FilesOrigin &origin = m_Core.directoryStructure()->getOriginByName(name);
std::vector<FileEntryPtr> files = origin.getFiles();
@@ -168,12 +159,12 @@ void ModInfoWithConflictInfo::doConflictCheck() const
unsigned int altIndex = ModInfo::getIndex(ToQString(altOrigin.getName()));
if (!file->isFromArchive()) {
if (!archiveData.isValid())
- m_OverwrittenList.insert(altIndex);
+ conflicts.m_OverwrittenList.insert(altIndex);
else
- m_ArchiveLooseOverwrittenList.insert(altIndex);
+ conflicts.m_ArchiveLooseOverwrittenList.insert(altIndex);
}
else {
- m_ArchiveOverwrittenList.insert(altIndex);
+ conflicts.m_ArchiveOverwrittenList.insert(altIndex);
}
} else {
providesAnything = true;
@@ -188,21 +179,21 @@ void ModInfoWithConflictInfo::doConflictCheck() const
if (!altInfo.isFromArchive()) {
if (!archiveData.isValid()) {
if (origin.getPriority() > altOrigin.getPriority()) {
- m_OverwriteList.insert(altIndex);
+ conflicts.m_OverwriteList.insert(altIndex);
} else {
- m_OverwrittenList.insert(altIndex);
+ conflicts.m_OverwrittenList.insert(altIndex);
}
} else {
- m_ArchiveLooseOverwrittenList.insert(altIndex);
+ conflicts.m_ArchiveLooseOverwrittenList.insert(altIndex);
}
} else {
if (!archiveData.isValid()) {
- m_ArchiveLooseOverwriteList.insert(altIndex);
+ conflicts.m_ArchiveLooseOverwriteList.insert(altIndex);
} else {
if (archiveData.order() > altInfo.archive().order()) {
- m_ArchiveOverwriteList.insert(altIndex);
+ conflicts.m_ArchiveOverwriteList.insert(altIndex);
} else if (archiveData.order() < altInfo.archive().order()) {
- m_ArchiveOverwrittenList.insert(altIndex);
+ conflicts.m_ArchiveOverwrittenList.insert(altIndex);
}
}
}
@@ -210,66 +201,51 @@ void ModInfoWithConflictInfo::doConflictCheck() const
}
}
}
- m_LastConflictCheck = QTime::currentTime();
if (files.size() != 0) {
if (!providesAnything)
- m_CurrentConflictState = CONFLICT_REDUNDANT;
- else if (!m_OverwriteList.empty() && !m_OverwrittenList.empty())
- m_CurrentConflictState = CONFLICT_MIXED;
- else if (!m_OverwriteList.empty())
- m_CurrentConflictState = CONFLICT_OVERWRITE;
- else if (!m_OverwrittenList.empty())
- m_CurrentConflictState = CONFLICT_OVERWRITTEN;
+ conflicts.m_CurrentConflictState = CONFLICT_REDUNDANT;
+ else if (!conflicts.m_OverwriteList.empty() && !conflicts.m_OverwrittenList.empty())
+ conflicts.m_CurrentConflictState = CONFLICT_MIXED;
+ else if (!conflicts.m_OverwriteList.empty())
+ conflicts.m_CurrentConflictState = CONFLICT_OVERWRITE;
+ else if (!conflicts.m_OverwrittenList.empty())
+ conflicts.m_CurrentConflictState = CONFLICT_OVERWRITTEN;
- if (!m_ArchiveOverwriteList.empty() && !m_ArchiveOverwrittenList.empty())
- m_ArchiveConflictState = CONFLICT_MIXED;
- else if (!m_ArchiveOverwriteList.empty())
- m_ArchiveConflictState = CONFLICT_OVERWRITE;
- else if (!m_ArchiveOverwrittenList.empty())
- m_ArchiveConflictState = CONFLICT_OVERWRITTEN;
+ if (!conflicts.m_ArchiveOverwriteList.empty() && !conflicts.m_ArchiveOverwrittenList.empty())
+ conflicts.m_ArchiveConflictState = CONFLICT_MIXED;
+ else if (!conflicts.m_ArchiveOverwriteList.empty())
+ conflicts.m_ArchiveConflictState = CONFLICT_OVERWRITE;
+ else if (!conflicts.m_ArchiveOverwrittenList.empty())
+ conflicts.m_ArchiveConflictState = CONFLICT_OVERWRITTEN;
- if (!m_ArchiveLooseOverwrittenList.empty() && !m_ArchiveLooseOverwriteList.empty())
- m_ArchiveConflictLooseState = CONFLICT_MIXED;
- else if (!m_ArchiveLooseOverwrittenList.empty())
- m_ArchiveConflictLooseState = CONFLICT_OVERWRITTEN;
- else if (!m_ArchiveLooseOverwriteList.empty())
- m_ArchiveConflictLooseState = CONFLICT_OVERWRITE;
+ if (!conflicts.m_ArchiveLooseOverwrittenList.empty() && !conflicts.m_ArchiveLooseOverwriteList.empty())
+ conflicts.m_ArchiveConflictLooseState = CONFLICT_MIXED;
+ else if (!conflicts.m_ArchiveLooseOverwrittenList.empty())
+ conflicts.m_ArchiveConflictLooseState = CONFLICT_OVERWRITTEN;
+ else if (!conflicts.m_ArchiveLooseOverwriteList.empty())
+ conflicts.m_ArchiveConflictLooseState = CONFLICT_OVERWRITE;
- m_HasHiddenFiles = hasHiddenFiles;
+ conflicts.m_HasHiddenFiles = hasHiddenFiles;
}
}
+
+ return conflicts;
}
ModInfoWithConflictInfo::EConflictType ModInfoWithConflictInfo::isConflicted() const
{
- // this is costy so cache the result
- QTime now = QTime::currentTime();
- if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) {
- doConflictCheck();
- }
-
- return m_CurrentConflictState;
+ return m_Conflicts.value().m_CurrentConflictState;
}
ModInfoWithConflictInfo::EConflictType ModInfoWithConflictInfo::isArchiveConflicted() const
{
- QTime now = QTime::currentTime();
- if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) {
- doConflictCheck();
- }
-
- return m_ArchiveConflictState;
+ return m_Conflicts.value().m_ArchiveConflictState;
}
ModInfoWithConflictInfo::EConflictType ModInfoWithConflictInfo::isLooseArchiveConflicted() const
{
- QTime now = QTime::currentTime();
- if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) {
- doConflictCheck();
- }
-
- return m_ArchiveConflictLooseState;
+ return m_Conflicts.value().m_ArchiveConflictLooseState;
}
@@ -294,12 +270,7 @@ bool ModInfoWithConflictInfo::isRedundant() const
bool ModInfoWithConflictInfo::hasHiddenFiles() const
{
- QTime now = QTime::currentTime();
- if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) {
- doConflictCheck();
- }
-
- return m_HasHiddenFiles;
+ return m_Conflicts.value().m_HasHiddenFiles;
}
void ModInfoWithConflictInfo::diskContentModified() {
diff --git a/src/modinfowithconflictinfo.h b/src/modinfowithconflictinfo.h
index c9cddb60..2136e093 100644
--- a/src/modinfowithconflictinfo.h
+++ b/src/modinfowithconflictinfo.h
@@ -54,14 +54,12 @@ public:
*/
void clearCaches() override;
- const std::set<unsigned int>& getModOverwrite() const override { return m_OverwriteList; }
- const std::set<unsigned int>& getModOverwritten() const override { return m_OverwrittenList; }
- const std::set<unsigned int>& getModArchiveOverwrite() const override { return m_ArchiveOverwriteList; }
- const std::set<unsigned int>& getModArchiveOverwritten() const override { return m_ArchiveOverwrittenList; }
- const std::set<unsigned int>& getModArchiveLooseOverwrite() const override { return m_ArchiveLooseOverwriteList; }
- const std::set<unsigned int>& getModArchiveLooseOverwritten() const override { return m_ArchiveLooseOverwrittenList; }
-
- void doConflictCheck() const override;
+ const std::set<unsigned int>& getModOverwrite() const override { return m_Conflicts.value().m_OverwriteList; }
+ const std::set<unsigned int>& getModOverwritten() const override { return m_Conflicts.value().m_OverwrittenList; }
+ const std::set<unsigned int>& getModArchiveOverwrite() const override { return m_Conflicts.value().m_ArchiveOverwriteList; }
+ const std::set<unsigned int>& getModArchiveOverwritten() const override { return m_Conflicts.value().m_ArchiveOverwrittenList; }
+ const std::set<unsigned int>& getModArchiveLooseOverwrite() const override { return m_Conflicts.value().m_ArchiveLooseOverwriteList; }
+ const std::set<unsigned int>& getModArchiveLooseOverwritten() const override { return m_Conflicts.value().m_ArchiveLooseOverwrittenList; }
public slots:
@@ -72,11 +70,8 @@ public slots:
protected:
- /**
- * @brief Check if the content of this mod is valid.
- *
- * @return true if the content is valid, false otherwise.
- **/
+ // check if the content of this mod is valid
+ //
virtual bool doIsValid() const;
/**
@@ -136,23 +131,27 @@ protected:
private:
+ struct Conflicts {
+ EConflictType m_CurrentConflictState = CONFLICT_NONE;
+ EConflictType m_ArchiveConflictState = CONFLICT_NONE;
+ EConflictType m_ArchiveConflictLooseState = CONFLICT_NONE;
+ bool m_HasLooseOverwrite = false;
+ bool m_HasHiddenFiles = false;
+
+ std::set<unsigned int> m_OverwriteList; // indices of mods overritten by this mod
+ std::set<unsigned int> m_OverwrittenList; // indices of mods overwriting this mod
+ std::set<unsigned int> m_ArchiveOverwriteList; // indices of mods with archive files overritten by this mod
+ std::set<unsigned int> m_ArchiveOverwrittenList; // indices of mods with archive files overwriting this mod
+ std::set<unsigned int> m_ArchiveLooseOverwriteList; // indices of mods with archives being overwritten by this mod's loose files
+ std::set<unsigned int> m_ArchiveLooseOverwrittenList; // indices of mods with loose files overwriting this mod's archive files
+ };
+
+ Conflicts doConflictCheck() const;
+
MOBase::MemoizedLocked<std::shared_ptr<const MOBase::IFileTree>> m_FileTree;
MOBase::MemoizedLocked<bool> m_Valid;
MOBase::MemoizedLocked<std::set<int>> m_Contents;
-
- mutable EConflictType m_CurrentConflictState;
- mutable EConflictType m_ArchiveConflictState;
- mutable EConflictType m_ArchiveConflictLooseState;
- mutable bool m_HasLooseOverwrite;
- mutable bool m_HasHiddenFiles;
- mutable QTime m_LastConflictCheck;
-
- mutable std::set<unsigned int> m_OverwriteList; // indices of mods overritten by this mod
- mutable std::set<unsigned int> m_OverwrittenList; // indices of mods overwriting this mod
- mutable std::set<unsigned int> m_ArchiveOverwriteList; // indices of mods with archive files overritten by this mod
- mutable std::set<unsigned int> m_ArchiveOverwrittenList; // indices of mods with archive files overwriting this mod
- mutable std::set<unsigned int> m_ArchiveLooseOverwriteList; // indices of mods with archives being overwritten by this mod's loose files
- mutable std::set<unsigned int> m_ArchiveLooseOverwrittenList; // indices of mods with loose files overwriting this mod's archive files
+ MOBase::MemoizedLocked<Conflicts> m_Conflicts;
};
diff --git a/src/modlist.cpp b/src/modlist.cpp
index de24fbbd..48f65d3a 100644
--- a/src/modlist.cpp
+++ b/src/modlist.cpp
@@ -549,7 +549,6 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role)
m_Profile->setModEnabled(modID, enabled);
m_Modified = true;
m_LastCheck.restart();
- emit modStatesChanged({ index });
emit tutorialModlistUpdate();
}
result = true;
@@ -997,11 +996,15 @@ void ModList::notifyModRemoved(QString const& modName) const
void ModList::notifyModStateChanged(QList<unsigned int> modIndices) const
{
+ QModelIndexList indices;
std::map<QString, IModList::ModStates> mods;
for (auto modIndex : modIndices) {
+ indices.append(index(modIndex, 0));
ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex);
mods.emplace(modInfo->name(), state(modIndex));
}
+
+ emit modStatesChanged(indices);
m_ModStateChanged(mods);
}
@@ -1404,7 +1407,6 @@ bool ModList::toggleState(const QModelIndexList& indices)
m_Profile->setModsEnabled(modsToEnable, modsToDisable);
- emit modStatesChanged(indices);
emit tutorialModlistUpdate();
m_Modified = true;
@@ -1430,6 +1432,4 @@ void ModList::setActive(const QModelIndexList& indices, bool active)
else {
m_Profile->setModsEnabled({}, mods);
}
-
- emit modStatesChanged(indices);
}
diff --git a/src/modlist.h b/src/modlist.h
index e201de27..6a3a2901 100644
--- a/src/modlist.h
+++ b/src/modlist.h
@@ -258,11 +258,11 @@ signals:
// the sorting of the list can only be manually changed if the list is sorted by priority
// in which case the move is intended to change the priority of a mod.
//
- void modPrioritiesChanged(const QModelIndexList& indices);
+ void modPrioritiesChanged(const QModelIndexList& indices) const;
// emitted when the state (active/inactive) of one or multiple mods have changed
//
- void modStatesChanged(const QModelIndexList& indices);
+ void modStatesChanged(const QModelIndexList& indices) const;
/**
* @brief emitted when the model wants a text to be displayed by the UI
diff --git a/src/modlistview.cpp b/src/modlistview.cpp
index d7f90c9f..cb4cb2fb 100644
--- a/src/modlistview.cpp
+++ b/src/modlistview.cpp
@@ -689,7 +689,10 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, MainWindo
connect(m_core, &OrganizerCore::profileChanged, this, &ModListView::onProfileChanged);
connect(core.modList(), &ModList::modPrioritiesChanged, [=](auto&& indices) { onModPrioritiesChanged(indices); });
connect(core.modList(), &ModList::clearOverwrite, [=] { m_actions->clearOverwrite(); });
- connect(core.modList(), &ModList::modStatesChanged, [=] { updateModCount(); });
+ connect(core.modList(), &ModList::modStatesChanged, [=] {
+ updateModCount();
+ setOverwriteMarkers(selectionModel()->selectedRows());
+ });
connect(core.modList(), &ModList::modelReset, [=] { clearOverwriteMarkers(); });
// proxy for various group by
diff --git a/src/organizer.pro b/src/organizer.pro
index ddf676f2..54080112 100644
--- a/src/organizer.pro
+++ b/src/organizer.pro
@@ -83,6 +83,7 @@ SOURCES += \
modflagicondelegate.cpp \
genericicondelegate.cpp \
organizerproxy.cpp \
+ updatedialog.cpp \
viewmarkingscrollbar.cpp \
plugincontainer.cpp \
organizercore.cpp \
@@ -161,6 +162,7 @@ HEADERS += \
modflagicondelegate.h \
genericicondelegate.h \
organizerproxy.h \
+ updatedialog.h \
viewmarkingscrollbar.h \
plugincontainer.h \
organizercore.h \
@@ -203,7 +205,8 @@ FORMS += \
aboutdialog.ui \
listdialog.ui \
forcedloaddialog.ui \
- forcedloaddialogwidget.ui
+ forcedloaddialogwidget.ui \
+ updatedialog.ui
RESOURCES += \
resources.qrc \
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index 3874560b..e47a6e30 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -1565,6 +1565,45 @@ void OrganizerCore::profileRefresh()
refresh();
}
+void OrganizerCore::clearCaches(std::vector<unsigned int> const& indices) const
+{
+ const auto insert = [](auto& dest, const auto& from) {
+ dest.insert(from.begin(), from.end());
+ };
+ std::set<unsigned int> allIndices;
+ for (const auto index : indices) {
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(index);
+
+ if (m_CurrentProfile->modEnabled(index)) {
+ // if the mod is enabled, we need to first clear its cache so that
+ // getModOverwrite(), ..., returns the newly conflicting mods (in case
+ // the mod just got enabled)
+ modInfo->clearCaches();
+ insert(allIndices, modInfo->getModOverwrite());
+ insert(allIndices, modInfo->getModOverwritten());
+ insert(allIndices, modInfo->getModArchiveOverwrite());
+ insert(allIndices, modInfo->getModArchiveOverwritten());
+ insert(allIndices, modInfo->getModArchiveLooseOverwrite());
+ insert(allIndices, modInfo->getModArchiveLooseOverwritten());
+ }
+ else {
+ // if the mod is disabled, we need to first fetch the conflicting
+ // mods, and then clear the cache
+ insert(allIndices, modInfo->getModOverwrite());
+ insert(allIndices, modInfo->getModOverwritten());
+ insert(allIndices, modInfo->getModArchiveOverwrite());
+ insert(allIndices, modInfo->getModArchiveOverwritten());
+ insert(allIndices, modInfo->getModArchiveLooseOverwrite());
+ insert(allIndices, modInfo->getModArchiveLooseOverwritten());
+ modInfo->clearCaches();
+ }
+ }
+
+ for (auto& index : allIndices) {
+ ModInfo::getByIndex(index)->clearCaches();
+ }
+}
+
void OrganizerCore::modPrioritiesChanged(const QModelIndexList& indices)
{
for (unsigned int i = 0; i < currentProfile()->numMods(); ++i) {
@@ -1579,30 +1618,13 @@ void OrganizerCore::modPrioritiesChanged(const QModelIndexList& indices)
currentProfile()->writeModlist();
directoryStructure()->getFileRegister()->sortOrigins();
+ std::vector<unsigned int> vindices;
+
for (auto& idx : indices) {
- ModInfo::Ptr modInfo = ModInfo::getByIndex(idx.data(ModList::IndexRole).toInt());
- // clear caches on all mods conflicting with the moved mod
- for (int i : modInfo->getModOverwrite()) {
- ModInfo::getByIndex(i)->clearCaches();
- }
- for (int i : modInfo->getModOverwritten()) {
- ModInfo::getByIndex(i)->clearCaches();
- }
- for (int i : modInfo->getModArchiveOverwrite()) {
- ModInfo::getByIndex(i)->clearCaches();
- }
- for (int i : modInfo->getModArchiveOverwritten()) {
- ModInfo::getByIndex(i)->clearCaches();
- }
- for (int i : modInfo->getModArchiveLooseOverwrite()) {
- ModInfo::getByIndex(i)->clearCaches();
- }
- for (int i : modInfo->getModArchiveLooseOverwritten()) {
- ModInfo::getByIndex(i)->clearCaches();
- }
- // update conflict check on the moved mod
- modInfo->doConflictCheck();
+ vindices.push_back(idx.data(ModList::IndexRole).toInt());
}
+
+ clearCaches(vindices);
}
void OrganizerCore::modStatusChanged(unsigned int index)
@@ -1622,7 +1644,6 @@ void OrganizerCore::modStatusChanged(unsigned int index)
m_UserInterface->archivesWriter().write();
}
}
- modInfo->clearCaches();
for (unsigned int i = 0; i < m_CurrentProfile->numMods(); ++i) {
ModInfo::Ptr modInfo = ModInfo::getByIndex(i);
@@ -1637,7 +1658,7 @@ void OrganizerCore::modStatusChanged(unsigned int index)
m_DirectoryStructure->getFileRegister()->sortOrigins();
refreshLists();
-
+ clearCaches({ index });
m_ModList.notifyModStateChanged({ index });
} catch (const std::exception &e) {
@@ -1649,18 +1670,17 @@ void OrganizerCore::modStatusChanged(QList<unsigned int> index) {
try {
QMap<unsigned int, ModInfo::Ptr> modsToEnable;
QMap<unsigned int, ModInfo::Ptr> modsToDisable;
+ std::vector<unsigned int> vindices;
for (auto idx : index) {
if (m_CurrentProfile->modEnabled(idx)) {
modsToEnable[idx] = ModInfo::getByIndex(idx);
} else {
modsToDisable[idx] = ModInfo::getByIndex(idx);
}
+ vindices.push_back(idx);
}
if (!modsToEnable.isEmpty()) {
updateModsInDirectoryStructure(modsToEnable);
- for (auto modInfo : modsToEnable.values()) {
- modInfo->clearCaches();
- }
}
if (!modsToDisable.isEmpty()) {
updateModsActiveState(modsToDisable.keys(), false);
@@ -1689,8 +1709,9 @@ void OrganizerCore::modStatusChanged(QList<unsigned int> index) {
m_DirectoryStructure->getFileRegister()->sortOrigins();
refreshLists();
-
+ clearCaches(vindices);
m_ModList.notifyModStateChanged(index);
+
} catch (const std::exception &e) {
reportError(tr("failed to update mod list: %1").arg(e.what()));
}
diff --git a/src/organizercore.h b/src/organizercore.h
index c00d0d42..e6274e36 100644
--- a/src/organizercore.h
+++ b/src/organizercore.h
@@ -405,6 +405,11 @@ private:
void updateModActiveState(int index, bool active);
void updateModsActiveState(const QList<unsigned int> &modIndices, bool active);
+ // clear the conflict caches of all the given mods, and the mods in conflict
+ // with the given mods
+ //
+ void clearCaches(std::vector<unsigned int> const& indices) const;
+
bool createDirectory(const QString &path);
QString oldMO1HookDll() const;
diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp
index c0f3b005..ea012a94 100644
--- a/src/selfupdater.cpp
+++ b/src/selfupdater.cpp
@@ -32,6 +32,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <versioninfo.h>
#include <report.h>
#include "shared/util.h"
+#include "updatedialog.h"
#include <QApplication>
#include <QCoreApplication>
@@ -129,7 +130,7 @@ void SelfUpdater::testForUpdate(const Settings& settings)
auto lastKey = mreleases.begin()->first;
if (lastKey > this->m_MOVersion) {
- // Fill m_UpdateCandidates with version strictly greater than the
+ // Fill m_UpdateCandidates with version strictly greater than the
// current version:
m_UpdateCandidates.clear();
for (auto p : mreleases) {
@@ -164,10 +165,11 @@ void SelfUpdater::startUpdate()
auto latestRelease = m_UpdateCandidates.begin()->second;
- QMessageBox query(QMessageBox::Question,
- tr("New update available (%1)")
- .arg(latestRelease["tag_name"].toString()), tr("Do you want to install update? All your mods and setup will be left untouched.\nSelect Show Details option to see the full change-log."),
- QMessageBox::Yes | QMessageBox::Cancel, m_Parent);
+ UpdateDialog dialog(m_Parent);
+ dialog.setVersions(
+ MOShared::createVersionInfo().displayString(3),
+ latestRelease["tag_name"].toString()
+ );
// We concatenate release details. We only include pre-release if those are
// the latest release:
@@ -182,28 +184,20 @@ void SelfUpdater::startUpdate()
}
// Stop including pre-release as soon as we find a non-prerelease:
- if (!release["prelease"].toBool()) {
+ if (!release["prerelease"].toBool()) {
includePreRelease = false;
}
- details += "\n# " + release["tag_name"].toString() + "\n---\n";
+ details += "\n## " + release["name"].toString() + "\n---\n";
details += release["body"].toString();
}
// Need to call setDetailedText to create the QTextEdit and then be able to retrieve it:
- query.setDetailedText(details);
- QTextEdit* textEdit = query.findChild<QTextEdit*>();
+ dialog.setChangeLogs(details);
- // If we have the text edit, we can call setMarkdown to get proper formatting.
- if (textEdit) {
- textEdit->setMarkdown(details);
- }
-
- query.button(QMessageBox::Yes)->setText(tr("Install"));
-
- int res = query.exec();
+ int res = dialog.exec();
- if (query.result() == QMessageBox::Yes) {
+ if (dialog.result() == QDialog::Accepted) {
bool found = false;
for (const QJsonValue &assetVal : latestRelease["assets"].toArray()) {
QJsonObject asset = assetVal.toObject();
diff --git a/src/updatedialog.cpp b/src/updatedialog.cpp
new file mode 100644
index 00000000..d3336e93
--- /dev/null
+++ b/src/updatedialog.cpp
@@ -0,0 +1,67 @@
+#include "updatedialog.h"
+#include "ui_updatedialog.h"
+
+#include "lootdialog.h" // for MarkdownPage
+#include <QWebChannel>
+
+using namespace MOBase;
+
+UpdateDialog::UpdateDialog(QWidget* parent) :
+ QDialog(parent, Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint), ui(new Ui::UpdateDialog)
+{
+ // Basic UI stuff
+ ui->setupUi(this);
+ connect(ui->installButton, &QPushButton::pressed, this, [&]{ done(QDialog::Accepted); });
+ connect(ui->cancelButton, &QPushButton::pressed, this, [&]{ done(QDialog::Rejected); });
+
+ // Replace a label with an icon
+ QIcon icon = style()->standardIcon(QStyle::SP_MessageBoxQuestion);
+ QPixmap pixmap = icon.pixmap(QSize(32, 32));
+ ui->iconLabel->setPixmap(pixmap);
+ ui->iconLabel->setScaledContents(true);
+
+ // Setting up the Markdown stuff
+ auto* page = new MarkdownPage(this);
+ ui->detailsWebView->setPage(page);
+
+ auto* channel = new QWebChannel(this);
+ channel->registerObject("content", &m_changeLogs);
+ page->setWebChannel(channel);
+
+ const QString path = QApplication::applicationDirPath() + "/resources/markdown.html";
+ QFile f(path);
+
+ if (f.open(QFile::ReadOnly)) {
+ const QString html = f.readAll();
+ if (!html.isEmpty()) {
+ ui->detailsWebView->setHtml(html);
+ } else {
+ log::error("failed to read '{}', {}", path, f.errorString());
+ }
+ } else {
+ log::error("can't open '{}', {}", path, f.errorString());
+ }
+
+ // Setting up the expander
+ m_expander.set(ui->detailsButton, ui->detailsWidget);
+ connect(&m_expander, &ExpanderWidget::toggled, this, [&]{ adjustSize(); });
+
+ // Adjust sizes after the expander hides stuff
+ adjustSize();
+}
+
+UpdateDialog::~UpdateDialog() = default;
+
+void UpdateDialog::setChangeLogs(const QString& text)
+{
+ m_changeLogs.setText(text);
+}
+
+void UpdateDialog::setVersions(const QString& oldVersion, const QString& newVersion)
+{
+ ui->updateLabel->setText(
+ tr("Mod Organizer %1 is available. The current version is %2. Updating will not affect your mods or profiles.")
+ .arg(newVersion)
+ .arg(oldVersion)
+ );
+}
diff --git a/src/updatedialog.h b/src/updatedialog.h
new file mode 100644
index 00000000..1dd5e0b1
--- /dev/null
+++ b/src/updatedialog.h
@@ -0,0 +1,28 @@
+#ifndef MODORGANIZER_UPDATEDIALOG_H
+#define MODORGANIZER_UPDATEDIALOG_H
+
+#include <QDialog>
+
+#include <expanderwidget.h>
+#include "lootdialog.h" // for MarkdownDocument
+
+namespace Ui { class UpdateDialog; }
+
+class UpdateDialog : public QDialog
+{
+ Q_OBJECT;
+
+public:
+ UpdateDialog(QWidget* parent);
+ ~UpdateDialog();
+
+ void setChangeLogs(const QString& text);
+ void setVersions(const QString& oldVersion, const QString& newVersion);
+
+private:
+ std::unique_ptr<Ui::UpdateDialog> ui;
+ MOBase::ExpanderWidget m_expander;
+ MarkdownDocument m_changeLogs;
+};
+
+#endif // MODORGANIZER_UPDATEDIALOG_H
diff --git a/src/updatedialog.ui b/src/updatedialog.ui
new file mode 100644
index 00000000..874cbb80
--- /dev/null
+++ b/src/updatedialog.ui
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UpdateDialog</class>
+ <widget class="QDialog" name="UpdateDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>578</width>
+ <height>539</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>460</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string notr="true">Update available</string>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>true</bool>
+ </property>
+ <property name="modal">
+ <bool>true</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0,1">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,0,1">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>9</number>
+ </property>
+ <property name="rightMargin">
+ <number>9</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="iconLabel">
+ <property name="minimumSize">
+ <size>
+ <width>32</width>
+ <height>32</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32</width>
+ <height>32</height>
+ </size>
+ </property>
+ <property name="text">
+ <string notr="true">&lt;?&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>10</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="updateLabel">
+ <property name="text">
+ <string notr="true">Update text placeholder</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QToolButton" name="detailsButton">
+ <property name="text">
+ <string>Changelog</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="installButton">
+ <property name="text">
+ <string>Install</string>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cancelButton">
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QWidget" name="detailsWidget" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout" stretch="0">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="frame">
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3" stretch="0">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWebEngineView" name="detailsWebView" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>650</width>
+ <height>450</height>
+ </size>
+ </property>
+ <property name="url" stdset="0">
+ <url>
+ <string>about:blank</string>
+ </url>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>QWebEngineView</class>
+ <extends>QWidget</extends>
+ <header location="global">QtWebEngineWidgets/QWebEngineView</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>