Compare commits

...
43 Commits
Author SHA1 Message Date
Mikaël Capelle 88ee41ae85 Bump to 2.4.1. 2021-03-20 17:03:36 +01:00
Mikaël Capelle 8a29bc5110 Bump to 2.4.1 RC2. 2021-03-14 20:08:58 +01:00
Mikaël Capelle 77dbd7ff31 Merge pull request #1465 from Holt59/fix-saves-tab-header
Stretch last section in the Saves tab.
2021-03-14 20:08:30 +01:00
Mikaël Capelle 7fab050adb Stretch last section in the Saves tab. 2021-03-14 20:07:09 +01:00
Mikaël Capelle b547045203 Bump to 2.4.1rc1.1. 2021-03-14 20:00:00 +01:00
Mikaël Capelle fcd2e1473f Merge pull request #1462 from Holt59/fix-stylesheets-qscrollarea
Remove border on QScrollArea from stylesheets.
2021-03-13 16:06:18 +01:00
Mikaël Capelle 287dd6d805 Remove border on QScrollArea from stylesheets. 2021-03-13 16:05:43 +01:00
Mikaël Capelle 717c2ca9cf Bump to 2.4.1rc1 and update translations. 2021-03-12 20:51:46 +01:00
Mikaël Capelle abd1a11768 Merge pull request #1449 from isanae/uilocker-disabled
Disabled uilocker for child dialogs
2021-03-03 18:52:55 +01:00
Mikaël Capelle 34636301ea Merge pull request #1447 from LostDragonist/settings_resize
Add scrollbars to some settings tabs to reduce minimum size
2021-03-03 18:52:46 +01:00
Mikaël Capelle f298ea0ae3 Merge pull request #1443 from Holt59/fix-savegame-name
Display savegame name and filepath in saves tab.
2021-03-03 18:52:28 +01:00
Chris Bessent eee1b4b3f1 Put stylesheet stuff into .ui file instead 2021-03-02 21:20:23 -07:00
isanae 00120605fb don't disable dialogs that are children of other dialogs 2021-03-02 14:41:23 -05:00
Mikaël Capelle 32088b47f1 Merge pull request #1446 from LostDragonist/disabled_plugins
Allow plugins to disable themselves by default
2021-03-02 19:27:24 +01:00
Chris Bessent 726b381ea7 Add scrollbars to some settings tabs to reduce minimum size
The previous minimum size was around 450 x 560px.  At a DPI scaling
of 200%, this means 900 x 1120px which extends off the typical 1080p
screen.  Why is someone using 200% scaling on a 1080p screen?  I have
no clue.

Now, the minimum size is around 450 x 410px for 900 x 820 px at 200%
scaling.  Which works for anything short of a 1376 x 768 screen at
200% scaling which I just don't really care about.
2021-03-02 04:07:46 -07:00
Chris Bessent 071550f7fb Allow plugins to disable themselves by default 2021-03-02 02:41:16 -07:00
Mikaël Capelle f8a50edd0f Merge pull request #1444 from LostDragonist/lockedorder
Fix load order when using locked plugins
2021-03-01 19:21:24 +01:00
Mikaël Capelle 08c7113ecb Merge pull request #1439 from Holt59/fix-downloadlist
Fix download list sorting.
2021-03-01 19:21:15 +01:00
Mikaël Capelle d436fe8cb3 Merge pull request #1442 from Holt59/fix-createempty-mod
Fix priority for 'Create Empty Mod'.
2021-03-01 19:21:06 +01:00
Chris Bessent d5183ebe6b Fix load order when using locked plugins 2021-02-27 18:50:45 -07:00
Mikaël Capelle c72326ee4b Display savegame name and filepath in saves tab. 2021-02-27 12:42:37 +01:00
Mikaël Capelle 75e90f2f3f Fix priority for 'Create Empty Mod'. 2021-02-27 10:36:17 +01:00
Mikaël Capelle be012715f3 Fix download list sorting. 2021-02-26 21:32:39 +01:00
Mikaël Capelle 2b86a14c86 Merge pull request #1437 from Holt59/fix-sendtoseparator
Fix 'Send to separator... ' in descending priority.
2021-02-26 21:12:04 +01:00
Mikaël Capelle c96c01f40e Fix 'Send to separator... ' in descending priority. 2021-02-24 20:07:55 +01:00
Mikaël Capelle d6edc5d6b7 Merge pull request #1433 from Holt59/fix-bycategory-sorting
Fix sorting by priority when grouping by priority.
2021-02-24 18:35:18 +01:00
isanae 4040dc12b7 Merge pull request #1436 from isanae/tutorial-fix
Fix tutorial stuck on mod info dialog
2021-02-24 05:46:48 -05:00
isanae d4fc7c3de1 emit modInfoDisplayed, it's required by the tutorial 2021-02-24 04:57:54 -05:00
Mikaël Capelle 27c4861907 Fix sorting by priority when grouping by priority. 2021-02-23 19:16:18 +01:00
Mikaël Capelle 8decb9df68 Merge pull request #1431 from Holt59/fix-update-filter
Switch back to invalidating filter when receiving mod updates.
2021-02-23 17:23:08 +01:00
Mikaël Capelle 8365f26d83 Merge pull request #1424 from isanae/dl-share
Network shares for envfs
2021-02-23 17:19:53 +01:00
Mikaël Capelle 0ca21d9a5e Merge pull request #1429 from Holt59/fix-stylesheets
Fix stylesheets for 2.4.
2021-02-23 17:19:43 +01:00
Mikaël Capelle f60d0e5ec6 Fix stylesheets for 2.4. 2021-02-23 09:38:52 +01:00
isanae d22d77d921 handle network shares for NtOpenFile()
changed error messages to use formatNtMessage(), which handles NTSTATUS errors
2021-02-22 18:28:07 -05:00
Mikaël Capelle 7084021067 Merge pull request #1418 from Holt59/master
Bump version to 2.4.0 and update translation.
2021-02-21 18:01:12 +01:00
Mikaël Capelle 1dd2acf07f Bump version to 2.4.0 and update translation. 2021-02-21 17:56:44 +01:00
isanae 3fad475013 Merge pull request #1416 from isanae/plugin-blacklist
Broken plugins blacklist
2021-02-21 11:50:32 -05:00
Mikaël Capelle 010a5d80d6 Switch back to invalidating filter when receiving mod updates. 2021-02-21 11:12:16 +01:00
isanae 2bd755743c clarified comment 2021-02-20 12:49:08 -05:00
isanae 9eb1dd0ec0 clearPlugins() must be called to load the blacklist or it will be empty when loadPlugins() runs 2021-02-20 05:03:17 -05:00
isanae e473caf14a Merge pull request #1415 from isanae/master
2.4rc3
2021-02-19 18:23:01 -05:00
isanae 1c87f1d8d4 rc3 2021-02-19 18:21:53 -05:00
Mikaël Capelle bb6cde175a Bump version to 2.4.0. 2021-02-19 20:08:47 +01:00
30 changed files with 1874 additions and 1642 deletions
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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; }
&lt;p style=&quot;-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;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;If you click &amp;quot;Fix Mods...&amp;quot; in the context menu, MO will try to activate all mods and esps to fix those missing esps. It will not disable anything!&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>
+6
View File
@@ -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;
+5
View File
@@ -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);
+4
View File
@@ -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
View File
@@ -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
+4
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+15 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+8 -18
View File
@@ -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;
}
+2 -2
View File
@@ -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;
}
+2 -2
View File
@@ -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;
}
+3 -3
View File
@@ -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