mirror of
https://github.com/ModOrganizer2/modorganizer.git
synced 2026-07-27 13:58:24 -07:00
Compare commits
43
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88ee41ae85 | ||
|
|
8a29bc5110 | ||
|
|
77dbd7ff31 | ||
|
|
7fab050adb | ||
|
|
b547045203 | ||
|
|
fcd2e1473f | ||
|
|
287dd6d805 | ||
|
|
717c2ca9cf | ||
|
|
abd1a11768 | ||
|
|
34636301ea | ||
|
|
f298ea0ae3 | ||
|
|
eee1b4b3f1 | ||
|
|
00120605fb | ||
|
|
32088b47f1 | ||
|
|
726b381ea7 | ||
|
|
071550f7fb | ||
|
|
f8a50edd0f | ||
|
|
08c7113ecb | ||
|
|
d436fe8cb3 | ||
|
|
d5183ebe6b | ||
|
|
c72326ee4b | ||
|
|
75e90f2f3f | ||
|
|
be012715f3 | ||
|
|
2b86a14c86 | ||
|
|
c96c01f40e | ||
|
|
d6edc5d6b7 | ||
|
|
4040dc12b7 | ||
|
|
d4fc7c3de1 | ||
|
|
27c4861907 | ||
|
|
8decb9df68 | ||
|
|
8365f26d83 | ||
|
|
0ca21d9a5e | ||
|
|
f60d0e5ec6 | ||
|
|
d22d77d921 | ||
|
|
7084021067 | ||
|
|
1dd2acf07f | ||
|
|
3fad475013 | ||
|
|
010a5d80d6 | ||
|
|
2bd755743c | ||
|
|
9eb1dd0ec0 | ||
|
|
e473caf14a | ||
|
|
1c87f1d8d4 | ||
|
|
bb6cde175a |
@@ -226,7 +226,7 @@ bool DownloadList::lessThanPredicate(const QModelIndex &left, const QModelIndex
|
||||
if ((leftIndex < m_manager.numTotalDownloads())
|
||||
&& (rightIndex < m_manager.numTotalDownloads())) {
|
||||
if (left.column() == DownloadList::COL_NAME) {
|
||||
return m_manager.getFileName(left.row()).compare(m_manager.getFileName(right.row()), Qt::CaseInsensitive) < 0;
|
||||
return left.data(Qt::DisplayRole).toString().compare(right.data(Qt::DisplayRole).toString(), Qt::CaseInsensitive) < 0;
|
||||
} else if (left.column() == DownloadList::COL_MODNAME) {
|
||||
QString leftName, rightName;
|
||||
|
||||
@@ -275,7 +275,7 @@ bool DownloadList::lessThanPredicate(const QModelIndex &left, const QModelIndex
|
||||
if (leftState == rightState)
|
||||
return m_manager.getFileTime(left.row()) < m_manager.getFileTime(right.row());
|
||||
else
|
||||
return leftState > rightState;
|
||||
return leftState < rightState;
|
||||
} else if (left.column() == DownloadList::COL_SIZE) {
|
||||
return m_manager.getFileSize(left.row()) < m_manager.getFileSize(right.row());
|
||||
} else if (left.column() == DownloadList::COL_FILETIME) {
|
||||
|
||||
+23
-5
@@ -232,8 +232,8 @@ void forEachEntryImpl(
|
||||
|
||||
if (status < 0) {
|
||||
log::error(
|
||||
"NtOpenFile() failed for '{}', {}",
|
||||
toString(poa), formatSystemMessage(status));
|
||||
"failed to open directory '{}': {}",
|
||||
toString(poa), formatNtMessage(status));
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -264,8 +264,9 @@ void forEachEntryImpl(
|
||||
break;
|
||||
} else if (status < 0) {
|
||||
log::error(
|
||||
"NtQueryDirectoryFile() failed for '{}', {}",
|
||||
toString(poa), formatSystemMessage(status));
|
||||
"failed to read directory '{}': {}",
|
||||
toString(poa), formatNtMessage(status));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -321,6 +322,23 @@ void forEachEntryImpl(
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring makeNtPath(const std::wstring& path)
|
||||
{
|
||||
constexpr const wchar_t* nt_prefix = L"\\??\\";
|
||||
constexpr const wchar_t* nt_unc_prefix = L"\\??\\UNC\\";
|
||||
constexpr const wchar_t* share_prefix = L"\\\\";
|
||||
|
||||
if (path.starts_with(nt_prefix)) {
|
||||
// already an nt path
|
||||
return path;
|
||||
} else if (path.starts_with(share_prefix)) {
|
||||
// network shared need \??\UNC\ as a prefix
|
||||
return nt_unc_prefix + path.substr(2);
|
||||
} else {
|
||||
// prepend the \??\ prefix
|
||||
return nt_prefix + path;
|
||||
}
|
||||
}
|
||||
|
||||
void DirectoryWalker::forEachEntry(
|
||||
const std::wstring& path, void* cx,
|
||||
@@ -335,7 +353,7 @@ void DirectoryWalker::forEachEntry(
|
||||
NtClose = (NtClose_type)::GetProcAddress(m.get(), "NtClose");
|
||||
}
|
||||
|
||||
const std::wstring ntpath = std::wstring(L"\\??\\") + path;
|
||||
const std::wstring ntpath = makeNtPath(path);
|
||||
|
||||
UNICODE_STRING ObjectName = {};
|
||||
ObjectName.Buffer = const_cast<wchar_t*>(ntpath.c_str());
|
||||
|
||||
+22
-20
@@ -351,6 +351,7 @@ MainWindow::MainWindow(Settings &settings
|
||||
}
|
||||
|
||||
settings.geometry().restoreState(ui->downloadView->header());
|
||||
settings.geometry().restoreState(ui->savegameList->header());
|
||||
|
||||
ui->splitter->setStretchFactor(0, 3);
|
||||
ui->splitter->setStretchFactor(1, 2);
|
||||
@@ -512,6 +513,8 @@ void MainWindow::setupModList()
|
||||
|
||||
connect(&ui->modList->actions(), &ModListViewActions::overwriteCleared, [=]() { scheduleCheckForProblems(); });
|
||||
connect(&ui->modList->actions(), &ModListViewActions::originModified, this, &MainWindow::originModified);
|
||||
connect(&ui->modList->actions(), &ModListViewActions::modInfoDisplayed, this, &MainWindow::modInfoDisplayed);
|
||||
|
||||
connect(m_OrganizerCore.modList(), &ModList::modPrioritiesChanged, [&]() { m_ArchiveListWriter.write(); });
|
||||
}
|
||||
|
||||
@@ -2003,6 +2006,7 @@ void MainWindow::storeSettings()
|
||||
|
||||
s.geometry().saveState(ui->espList->header());
|
||||
s.geometry().saveState(ui->downloadView->header());
|
||||
s.geometry().saveState(ui->savegameList->header());
|
||||
|
||||
s.widgets().saveIndex(ui->executablesListBox);
|
||||
s.widgets().saveIndex(ui->tabWidget);
|
||||
@@ -2819,41 +2823,37 @@ void MainWindow::nxmUpdateInfoAvailable(QString gameName, QVariant userData, QVa
|
||||
}
|
||||
QVariantList resultList = resultData.toList();
|
||||
|
||||
QFutureWatcher<std::pair<QString, std::set<QSharedPointer<ModInfo>>>> *watcher = new QFutureWatcher<std::pair<QString, std::set<QSharedPointer<ModInfo>>>>();
|
||||
QObject::connect(watcher, &QFutureWatcher<std::set<QSharedPointer<ModInfo>>>::finished, this, &MainWindow::finishUpdateInfo);
|
||||
QFuture<std::pair<QString, std::set<QSharedPointer<ModInfo>>>> future = QtConcurrent::run([=]() -> std::pair<QString, std::set<QSharedPointer<ModInfo>>> {
|
||||
return std::make_pair(gameNameReal, ModInfo::filteredMods(gameNameReal, resultList, userData.toBool(), true));
|
||||
auto* watcher = new QFutureWatcher<NxmUpdateInfoData>();
|
||||
QObject::connect(watcher, &QFutureWatcher<NxmUpdateInfoData>::finished, [this, watcher]() {
|
||||
finishUpdateInfo(watcher->result());
|
||||
watcher->deleteLater();
|
||||
});
|
||||
auto future = QtConcurrent::run([=]() {
|
||||
return NxmUpdateInfoData{ gameNameReal, ModInfo::filteredMods(gameNameReal, resultList, userData.toBool(), true) };
|
||||
});
|
||||
watcher->setFuture(future);
|
||||
ui->modList->invalidateFilter();
|
||||
}
|
||||
|
||||
void MainWindow::finishUpdateInfo()
|
||||
void MainWindow::finishUpdateInfo(const NxmUpdateInfoData& data)
|
||||
{
|
||||
QFutureWatcher<std::pair<QString, std::set<QSharedPointer<ModInfo>>>> *watcher = static_cast<QFutureWatcher<std::pair<QString, std::set<QSharedPointer<ModInfo>>>> *>(sender());
|
||||
|
||||
QString game = watcher->result().first;
|
||||
auto finalMods = watcher->result().second;
|
||||
|
||||
if (finalMods.empty()) {
|
||||
log::info("{}", tr("None of your %1 mods appear to have had recent file updates.").arg(game));
|
||||
if (data.finalMods.empty()) {
|
||||
log::info("{}", tr("None of your %1 mods appear to have had recent file updates.").arg(data.game));
|
||||
}
|
||||
|
||||
std::set<std::pair<QString, int>> organizedGames;
|
||||
for (auto mod : finalMods) {
|
||||
for (auto& mod : data.finalMods) {
|
||||
if (mod->canBeUpdated()) {
|
||||
organizedGames.insert(std::make_pair<QString, int>(mod->gameName().toLower(), mod->nexusId()));
|
||||
}
|
||||
m_OrganizerCore.modList()->notifyChange(ModInfo::getIndex(mod->name()));
|
||||
}
|
||||
|
||||
if (!finalMods.empty() && organizedGames.empty())
|
||||
if (!data.finalMods.empty() && organizedGames.empty())
|
||||
log::warn("{}", tr("All of your mods have been checked recently. We restrict update checks to help preserve your available API requests."));
|
||||
|
||||
for (auto game : organizedGames)
|
||||
for (const auto& game : organizedGames) {
|
||||
NexusInterface::instance().requestUpdates(game.second, this, QVariant(), game.first, QString());
|
||||
|
||||
disconnect(sender());
|
||||
delete sender();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::nxmUpdatesAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID)
|
||||
@@ -2928,13 +2928,15 @@ void MainWindow::nxmUpdatesAvailable(QString gameName, int modID, QVariant userD
|
||||
if (foundUpdate) {
|
||||
// Just get the standard data updates for endorsements and descriptions
|
||||
mod->setLastNexusUpdate(QDateTime::currentDateTimeUtc());
|
||||
m_OrganizerCore.modList()->notifyChange(ModInfo::getIndex(mod->name()));
|
||||
} else {
|
||||
// Scrape mod data here so we can use the mod version if no file update was located
|
||||
requiresInfo = true;
|
||||
}
|
||||
}
|
||||
|
||||
// invalidate the filter to display mods with an update
|
||||
ui->modList->invalidateFilter();
|
||||
|
||||
if (requiresInfo)
|
||||
NexusInterface::instance().requestModInfo(gameNameReal, modID, this, QVariant(), QString());
|
||||
}
|
||||
|
||||
+12
-2
@@ -143,6 +143,9 @@ public slots:
|
||||
void refresherProgress(const DirectoryRefreshProgress* p);
|
||||
|
||||
signals:
|
||||
// emitted after the information dialog has been closed, used by tutorials
|
||||
//
|
||||
void modInfoDisplayed();
|
||||
|
||||
/**
|
||||
* @brief emitted when the selected style changes
|
||||
@@ -224,6 +227,13 @@ private:
|
||||
void toggleMO2EndorseState();
|
||||
void toggleUpdateAction();
|
||||
|
||||
// update info
|
||||
struct NxmUpdateInfoData {
|
||||
QString game;
|
||||
std::set<ModInfo::Ptr> finalMods;
|
||||
};
|
||||
void finishUpdateInfo(const NxmUpdateInfoData& data);
|
||||
|
||||
private:
|
||||
|
||||
static const char *PATTERN_BACKUP_GLOB;
|
||||
@@ -331,10 +341,10 @@ private slots:
|
||||
|
||||
void modInstalled(const QString &modName);
|
||||
|
||||
void finishUpdateInfo();
|
||||
// update info
|
||||
void nxmUpdateInfoAvailable(QString gameName, QVariant userData, QVariant resultData, int requestID);
|
||||
|
||||
void nxmEndorsementsAvailable(QVariant userData, QVariant resultData, int);
|
||||
void nxmUpdateInfoAvailable(QString gameName, QVariant userData, QVariant resultData, int requestID);
|
||||
void nxmUpdatesAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID);
|
||||
void nxmModInfoAvailable(QString gameName, int modID, QVariant userData, QVariant resultData, int requestID);
|
||||
void nxmEndorsementToggled(QString, int, QVariant, QVariant resultData, int);
|
||||
|
||||
+39
-2
@@ -1199,7 +1199,7 @@ p, li { white-space: pre-wrap; }
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListWidget" name="savegameList">
|
||||
<widget class="QTreeWidget" name="savegameList">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
@@ -1215,6 +1215,9 @@ p, li { white-space: pre-wrap; }
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">If you click &quot;Fix Mods...&quot; in the context menu, MO will try to activate all mods and esps to fix those missing esps. It will not disable anything!</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QAbstractScrollArea::AdjustIgnored</enum>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@@ -1224,9 +1227,43 @@ p, li { white-space: pre-wrap; }
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="uniformItemSizes">
|
||||
<property name="indentation">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="uniformRowHeights">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<attribute name="headerCascadingSectionResizes">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="headerStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
<property name="textAlignment">
|
||||
<set>AlignLeading|AlignVCenter</set>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>File</string>
|
||||
</property>
|
||||
<property name="textAlignment">
|
||||
<set>AlignLeading|AlignVCenter</set>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@@ -108,6 +108,12 @@ bool ModListSortProxy::lessThan(const QModelIndex &left,
|
||||
if (sortColumn() != ModList::COL_PRIORITY) {
|
||||
return QSortFilterProxyModel::lessThan(left, right);
|
||||
}
|
||||
else if (qobject_cast<QtGroupingProxy*>(sourceModel())) {
|
||||
// if the underlying proxy is a QtGroupingProxy we need to rely on
|
||||
// Qt::DisplayRole because the other roles are not correctly handled
|
||||
// by that kind of proxy
|
||||
return left.data(Qt::DisplayRole).toInt() < right.data(Qt::DisplayRole).toInt();
|
||||
}
|
||||
}
|
||||
|
||||
bool lOk, rOk;
|
||||
|
||||
@@ -299,6 +299,11 @@ std::optional<unsigned int> ModListView::prevMod(unsigned int modIndex) const
|
||||
return {};
|
||||
}
|
||||
|
||||
void ModListView::invalidateFilter()
|
||||
{
|
||||
m_sortProxy->invalidate();
|
||||
}
|
||||
|
||||
void ModListView::setFilterCriteria(const std::vector<ModListSortProxy::Criteria>& criteria)
|
||||
{
|
||||
m_sortProxy->setCriteria(criteria);
|
||||
|
||||
@@ -112,6 +112,10 @@ signals:
|
||||
|
||||
public slots:
|
||||
|
||||
// invalidate (refresh) the filter (similar to a layout changed event)
|
||||
//
|
||||
void invalidateFilter();
|
||||
|
||||
// set the filter criteria/options for mods
|
||||
//
|
||||
void setFilterCriteria(const std::vector<ModListSortProxy::Criteria>& criteria);
|
||||
|
||||
+55
-28
@@ -145,9 +145,10 @@ void ModListViewActions::createEmptyMod(const QModelIndex& index) const
|
||||
return;
|
||||
}
|
||||
|
||||
// find the priority before refresh() otherwise the index might not be valid
|
||||
const int newPriority = findInstallPriority(index);
|
||||
m_core.refresh();
|
||||
|
||||
const int newPriority = findInstallPriority(index);
|
||||
const auto mIndex = ModInfo::getIndex(name);
|
||||
if (newPriority >= 0) {
|
||||
m_core.modList()->changeModPriority(mIndex, newPriority);
|
||||
@@ -520,6 +521,7 @@ void ModListViewActions::displayModInformation(ModInfo::Ptr modInfo, unsigned in
|
||||
|
||||
modInfo->saveMeta();
|
||||
m_core.modList()->modInfoChanged(modInfo);
|
||||
emit modInfoDisplayed();
|
||||
}
|
||||
|
||||
if (m_core.currentProfile()->modEnabled(modIndex) && !modInfo->isForeign()) {
|
||||
@@ -567,47 +569,72 @@ void ModListViewActions::sendModsToPriority(const QModelIndexList& indexes) cons
|
||||
void ModListViewActions::sendModsToSeparator(const QModelIndexList& indexes) const
|
||||
{
|
||||
QStringList separators;
|
||||
auto indexesByPriority = m_core.currentProfile()->getAllIndexesByPriority();
|
||||
for (auto iter = indexesByPriority.begin(); iter != indexesByPriority.end(); iter++) {
|
||||
if ((iter->second != UINT_MAX)) {
|
||||
ModInfo::Ptr modInfo = ModInfo::getByIndex(iter->second);
|
||||
const auto& ibp = m_core.currentProfile()->getAllIndexesByPriority();
|
||||
for (const auto& [priority, index] : ibp) {
|
||||
if (index < ModInfo::getNumMods()) {
|
||||
ModInfo::Ptr modInfo = ModInfo::getByIndex(index);
|
||||
if (modInfo->isSeparator()) {
|
||||
separators << modInfo->name().chopped(10); // Chops the "_separator" away from the name
|
||||
separators << modInfo->name().chopped(10); // chops the "_separator" away from the name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in descending order, reverse the separator
|
||||
if (m_view->sortOrder() == Qt::DescendingOrder) {
|
||||
std::reverse(separators.begin(), separators.end());
|
||||
}
|
||||
|
||||
ListDialog dialog(m_parent);
|
||||
dialog.setWindowTitle("Select a separator...");
|
||||
dialog.setChoices(separators);
|
||||
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
QString result = dialog.getChoice();
|
||||
if (!result.isEmpty()) {
|
||||
result += "_separator";
|
||||
if (dialog.exec() != QDialog::Accepted) {
|
||||
return;
|
||||
}
|
||||
|
||||
int newPriority = Profile::MaximumPriority;
|
||||
bool foundSection = false;
|
||||
for (auto mod : m_core.modList()->allModsByProfilePriority()) {
|
||||
unsigned int modIndex = ModInfo::getIndex(mod);
|
||||
ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex);
|
||||
if (!foundSection && result.compare(mod) == 0) {
|
||||
foundSection = true;
|
||||
}
|
||||
else if (foundSection && modInfo->isSeparator()) {
|
||||
newPriority = m_core.currentProfile()->getModPriority(modIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
const QString result = dialog.getChoice();
|
||||
if (result.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (indexes.size() == 1
|
||||
&& m_core.currentProfile()->getModPriority(indexes[0].data(ModList::IndexRole).toInt()) < newPriority) {
|
||||
--newPriority;
|
||||
}
|
||||
const auto sepPriority = m_core.currentProfile()->getModPriority(
|
||||
ModInfo::getIndex(result + "_separator"));
|
||||
|
||||
m_core.modList()->changeModsPriority(indexes, newPriority);
|
||||
auto isSeparator = [](const auto& p) {
|
||||
return ModInfo::getByIndex(p.second)->isSeparator();
|
||||
};
|
||||
|
||||
|
||||
// start right after/before the current priority and look for the next
|
||||
// separator
|
||||
int priority = -1;
|
||||
if (m_view->sortOrder() == Qt::AscendingOrder) {
|
||||
auto it = std::find_if(ibp.find(sepPriority + 1), ibp.end(), isSeparator);
|
||||
if (it != ibp.end()) {
|
||||
priority = it->first;
|
||||
}
|
||||
else {
|
||||
priority = Profile::MaximumPriority;
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto it = std::find_if(--std::reverse_iterator{ ibp.find(sepPriority - 1) }, ibp.rend(), isSeparator);
|
||||
if (it != ibp.rend()) {
|
||||
priority = it->first + 1;
|
||||
}
|
||||
else {
|
||||
// create "before" priority 0, i.e. at the end in descending priority.
|
||||
priority = Profile::MinimumPriority;
|
||||
}
|
||||
}
|
||||
|
||||
// when the priority of a single mod is incremented, we need to shift the
|
||||
// target priority, otherwise we will miss the target by one
|
||||
if (indexes.size() == 1 && indexes[0].data(ModList::PriorityRole).toInt() < sepPriority) {
|
||||
priority--;
|
||||
}
|
||||
|
||||
m_core.modList()->changeModsPriority(indexes, priority);
|
||||
}
|
||||
|
||||
void ModListViewActions::sendModsToFirstConflict(const QModelIndexList& indexes) const
|
||||
|
||||
@@ -138,6 +138,10 @@ signals:
|
||||
//
|
||||
void originModified(int originId) const;
|
||||
|
||||
// emitted when the mod info dialog has been shown and closed
|
||||
//
|
||||
void modInfoDisplayed() const;
|
||||
|
||||
private:
|
||||
|
||||
// find the priority where to install or create a mod for the
|
||||
|
||||
+507
-501
File diff suppressed because it is too large
Load Diff
+15
-4
@@ -42,9 +42,10 @@ namespace bf = boost::fusion;
|
||||
// the one corresponding to the currently managed games.
|
||||
// - If a plugin has a master plugin (IPlugin::master()), it cannot be enabled/disabled by users,
|
||||
// and will follow the enabled/disabled state of its parent.
|
||||
// - Each plugin has an "enabled" setting stored in persistence. A plugin is considered disabled
|
||||
// if the setting is false.
|
||||
// - If the setting is true or does not exist, a plugin is considered disabled if one of its
|
||||
// - Each plugin has an "enabled" setting stored in persistence. If the setting does not exist,
|
||||
// the plugin's enabledByDefault is used instead.
|
||||
// - A plugin is considered disabled if the setting is false.
|
||||
// - If the setting is true, a plugin is considered disabled if one of its
|
||||
// requirements is not met.
|
||||
// - Users cannot enable a plugin if one of its requirements is not met.
|
||||
//
|
||||
@@ -599,7 +600,7 @@ bool PluginContainer::isEnabled(IPlugin* plugin) const
|
||||
}
|
||||
|
||||
// Check if the plugin is enabled:
|
||||
if (!m_Organizer->persistent(plugin->name(), "enabled", true).toBool()) {
|
||||
if (!m_Organizer->persistent(plugin->name(), "enabled", plugin->enabledByDefault()).toBool()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -952,6 +953,16 @@ void PluginContainer::reloadPlugin(QString const& filepath)
|
||||
|
||||
void PluginContainer::unloadPlugins()
|
||||
{
|
||||
if (m_Organizer) {
|
||||
// this will clear several structures that can hold on to pointers to
|
||||
// plugins, as well as read the plugin blacklist from the ini file, which
|
||||
// is used in loadPlugins() below to skip plugins
|
||||
//
|
||||
// note that the first thing loadPlugins() does is call unloadPlugins(),
|
||||
// so this makes sure the blacklist is always available
|
||||
m_Organizer->settings().plugins().clearPlugins();
|
||||
}
|
||||
|
||||
bf::for_each(m_Plugins, [](auto& t) { t.second.clear(); });
|
||||
bf::for_each(m_AccessPlugins, [](auto& t) { t.second.clear(); });
|
||||
m_Requirements.clear();
|
||||
|
||||
+2
-2
@@ -543,7 +543,7 @@ void PluginList::readLockedOrderFrom(const QString &fileName)
|
||||
auto alreadyLocked = [&](){ return std::find_if(m_LockedOrder.begin(), m_LockedOrder.end(), findLocked) != m_LockedOrder.end(); };
|
||||
|
||||
// See if we can just set the given priority
|
||||
if (!m_ESPs[priority].forceEnabled && !alreadyLocked())
|
||||
if (!m_ESPs[m_ESPsByPriority.at(priority)].forceEnabled && !alreadyLocked())
|
||||
{
|
||||
m_LockedOrder[pluginName] = priority;
|
||||
continue;
|
||||
@@ -552,7 +552,7 @@ void PluginList::readLockedOrderFrom(const QString &fileName)
|
||||
// Find the next higher priority we can set the plugin to
|
||||
while (++priority < m_ESPs.size())
|
||||
{
|
||||
if (!m_ESPs[priority].forceEnabled && !alreadyLocked())
|
||||
if (!m_ESPs[m_ESPsByPriority.at(priority)].forceEnabled && !alreadyLocked())
|
||||
{
|
||||
m_LockedOrder[pluginName] = priority;
|
||||
break;
|
||||
|
||||
+12
-12
@@ -30,10 +30,8 @@ SavesTab::SavesTab(QWidget* window, OrganizerCore& core, Ui::MainWindow* mwui)
|
||||
[&](auto pos){ onContextMenu(pos); });
|
||||
|
||||
connect(
|
||||
ui.list, &QListWidget::itemEntered,
|
||||
ui.list, &QTreeWidget::itemEntered,
|
||||
[&](auto* item){ saveSelectionChanged(item); });
|
||||
|
||||
ui.list->installEventFilter(this);
|
||||
}
|
||||
|
||||
bool SavesTab::eventFilter(QObject* object, QEvent* e)
|
||||
@@ -52,7 +50,7 @@ bool SavesTab::eventFilter(QObject* object, QEvent* e)
|
||||
return false;
|
||||
}
|
||||
|
||||
void SavesTab::displaySaveGameInfo(QListWidgetItem *newItem)
|
||||
void SavesTab::displaySaveGameInfo(QTreeWidgetItem *newItem)
|
||||
{
|
||||
// don't display the widget if the main window doesn't have focus
|
||||
//
|
||||
@@ -76,7 +74,7 @@ void SavesTab::displaySaveGameInfo(QListWidgetItem *newItem)
|
||||
}
|
||||
}
|
||||
|
||||
m_CurrentSaveView->setSave(*m_SaveGames[ui.list->row(newItem)]);
|
||||
m_CurrentSaveView->setSave(*m_SaveGames[ui.list->indexOfTopLevelItem(newItem)]);
|
||||
|
||||
QWindow *window = m_CurrentSaveView->window()->windowHandle();
|
||||
QRect screenRect;
|
||||
@@ -104,7 +102,7 @@ void SavesTab::displaySaveGameInfo(QListWidgetItem *newItem)
|
||||
}
|
||||
|
||||
|
||||
void SavesTab::saveSelectionChanged(QListWidgetItem *newItem)
|
||||
void SavesTab::saveSelectionChanged(QTreeWidgetItem *newItem)
|
||||
{
|
||||
if (newItem == nullptr) {
|
||||
hideSaveGameInfo();
|
||||
@@ -192,7 +190,9 @@ void SavesTab::refreshSaveList()
|
||||
|
||||
ui.list->clear();
|
||||
for (auto& save: m_SaveGames) {
|
||||
ui.list->addItem(savesDir.relativeFilePath(save->getFilepath()));
|
||||
auto relpath = savesDir.relativeFilePath(save->getFilepath());
|
||||
auto display = save->getName();
|
||||
ui.list->addTopLevelItem(new QTreeWidgetItem(ui.list, { display, relpath }));
|
||||
}
|
||||
}
|
||||
catch(std::exception& e)
|
||||
@@ -211,7 +211,7 @@ void SavesTab::deleteSavegame()
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (const QModelIndex &idx : ui.list->selectionModel()->selectedIndexes()) {
|
||||
for (const QModelIndex &idx : ui.list->selectionModel()->selectedRows()) {
|
||||
|
||||
auto& saveGame = m_SaveGames[idx.row()];
|
||||
|
||||
@@ -253,8 +253,8 @@ void SavesTab::onContextMenu(const QPoint& pos)
|
||||
if (info != nullptr) {
|
||||
QAction* action = menu.addAction(tr("Fix enabled mods..."));
|
||||
action->setEnabled(false);
|
||||
if (selection->selectedIndexes().count() == 1) {
|
||||
auto& save = m_SaveGames[selection->selectedIndexes()[0].row()];
|
||||
if (selection->selectedRows().count() == 1) {
|
||||
auto& save = m_SaveGames[selection->selectedRows()[0].row()];
|
||||
SaveGameInfo::MissingAssets missing = info->getMissingAssets(*save);
|
||||
if (missing.size() != 0) {
|
||||
connect(action, &QAction::triggered, this, [this, missing] { fixMods(missing); });
|
||||
@@ -263,7 +263,7 @@ void SavesTab::onContextMenu(const QPoint& pos)
|
||||
}
|
||||
}
|
||||
|
||||
QString deleteMenuLabel = tr("Delete %n save(s)", "", selection->selectedIndexes().count());
|
||||
QString deleteMenuLabel = tr("Delete %n save(s)", "", selection->selectedRows().count());
|
||||
menu.addAction(deleteMenuLabel, [&]{ deleteSavegame(); });
|
||||
|
||||
menu.addAction(tr("Open in Explorer..."), [&]{ openInExplorer(); });
|
||||
@@ -300,7 +300,7 @@ void SavesTab::openInExplorer()
|
||||
{
|
||||
const SaveGameInfo* info = m_core.managedGame()->feature<SaveGameInfo>();
|
||||
|
||||
const auto sel = ui.list->selectionModel()->selectedIndexes();
|
||||
const auto sel = ui.list->selectionModel()->selectedRows();
|
||||
if (sel.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
+3
-3
@@ -24,7 +24,7 @@ public:
|
||||
SavesTab(QWidget* window, OrganizerCore& core, Ui::MainWindow* ui);
|
||||
|
||||
void refreshSaveList();
|
||||
void displaySaveGameInfo(QListWidgetItem *newItem);
|
||||
void displaySaveGameInfo(QTreeWidgetItem *newItem);
|
||||
|
||||
QDir currentSavesDir() const;
|
||||
|
||||
@@ -40,7 +40,7 @@ private:
|
||||
{
|
||||
QTabWidget* mainTabs;
|
||||
QWidget* tab;
|
||||
QListWidget* list;
|
||||
QTreeWidget* list;
|
||||
};
|
||||
|
||||
QWidget* m_window;
|
||||
@@ -55,7 +55,7 @@ private:
|
||||
|
||||
void onContextMenu(const QPoint &pos);
|
||||
void deleteSavegame();
|
||||
void saveSelectionChanged(QListWidgetItem *newItem);
|
||||
void saveSelectionChanged(QTreeWidgetItem *newItem);
|
||||
void fixMods(SaveGameInfo::MissingAssets const &missingAssets);
|
||||
void refreshSavesIfOpen();
|
||||
void openInExplorer();
|
||||
|
||||
+1067
-961
File diff suppressed because it is too large
Load Diff
@@ -635,53 +635,43 @@ QWidget#downloadTab QAbstractScrollArea
|
||||
background: #242424;
|
||||
}
|
||||
|
||||
DownloadListWidget QFrame,
|
||||
DownloadListWidgetCompact,
|
||||
DownloadListWidgetCompact QLabel
|
||||
DownloadListView QFrame
|
||||
{
|
||||
/* an entry on the Downloads tab */
|
||||
background: #141414;
|
||||
}
|
||||
|
||||
DownloadListWidget QFrame#frame
|
||||
DownloadListView QFrame#frame
|
||||
{
|
||||
/* outer box of an entry on the Downloads tab */
|
||||
border: 2px solid #141414;
|
||||
}
|
||||
|
||||
DownloadListWidget QLabel#installLabel
|
||||
DownloadListView QLabel#installLabel
|
||||
{
|
||||
color: none;
|
||||
}
|
||||
|
||||
DownloadListWidget QFrame:clicked
|
||||
DownloadListView QFrame:clicked
|
||||
{
|
||||
background: #242424;
|
||||
}
|
||||
|
||||
/* compact downloads view */
|
||||
|
||||
DownloadListWidgetCompact,
|
||||
DownloadListWidgetCompact QLabel
|
||||
{
|
||||
/* an entry on the Downloads tab */
|
||||
background: #141414;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
DownloadListWidget[downloadView=standard]::item {
|
||||
DownloadListView[downloadView=standard]::item {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
DownloadListWidget[downloadView=compact]::item {
|
||||
DownloadListView[downloadView=compact]::item {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
DownloadListWidget::item:hover {
|
||||
DownloadListView::item:hover {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
DownloadListWidget::item:selected {
|
||||
DownloadListView::item:selected {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
|
||||
@@ -225,10 +225,10 @@ QHeaderView::down-arrow{
|
||||
background-color:rgba(154,154,0,0.3);
|
||||
padding: 3px;
|
||||
}
|
||||
/*#DownloadListWidget{
|
||||
/*#DownloadListView{
|
||||
}
|
||||
*/
|
||||
#DownloadListWidget{
|
||||
#DownloadListView{
|
||||
outline: 1px solid #e8e8e8;
|
||||
border: 0px solid #9a9a00;
|
||||
}
|
||||
|
||||
@@ -225,10 +225,10 @@ QHeaderView::down-arrow{
|
||||
background-color:rgba(154,154,0,0.3);
|
||||
padding: 3px;
|
||||
}
|
||||
/*#DownloadListWidget{
|
||||
/*#DownloadListView{
|
||||
}
|
||||
*/
|
||||
#DownloadListWidget{
|
||||
#DownloadListView{
|
||||
outline: 1px solid #e8e8e8;
|
||||
border: 0px solid #9a9a00;
|
||||
}
|
||||
|
||||
@@ -374,15 +374,15 @@ QTreeView::branch:open:has-children:has-siblings
|
||||
image: url(:/stylesheet/branch-open.png);
|
||||
}
|
||||
|
||||
DownloadListWidget QLabel#installLabel {
|
||||
DownloadListView QLabel#installLabel {
|
||||
color: none;
|
||||
}
|
||||
|
||||
DownloadListWidget[downloadView=standard]::item {
|
||||
DownloadListView[downloadView=standard]::item {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
DownloadListWidget[downloadView=compact]::item {
|
||||
DownloadListView[downloadView=compact]::item {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user