diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/env.cpp | 8 | ||||
| -rw-r--r-- | src/env.h | 4 | ||||
| -rw-r--r-- | src/mainwindow.cpp | 6 | ||||
| -rw-r--r-- | src/modinfo.h | 5 | ||||
| -rw-r--r-- | src/modinfowithconflictinfo.cpp | 107 | ||||
| -rw-r--r-- | src/modinfowithconflictinfo.h | 53 | ||||
| -rw-r--r-- | src/modlist.cpp | 8 | ||||
| -rw-r--r-- | src/modlist.h | 4 | ||||
| -rw-r--r-- | src/modlistview.cpp | 5 | ||||
| -rw-r--r-- | src/organizer.pro | 5 | ||||
| -rw-r--r-- | src/organizercore.cpp | 77 | ||||
| -rw-r--r-- | src/organizercore.h | 5 | ||||
| -rw-r--r-- | src/selfupdater.cpp | 30 | ||||
| -rw-r--r-- | src/updatedialog.cpp | 67 | ||||
| -rw-r--r-- | src/updatedialog.h | 28 | ||||
| -rw-r--r-- | src/updatedialog.ui | 211 |
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; } @@ -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"><?></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> |
