diff options
Diffstat (limited to 'src/mainwindow.cpp')
| -rw-r--r-- | src/mainwindow.cpp | 783 |
1 files changed, 683 insertions, 100 deletions
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3c749bf5..954c88c6 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -84,6 +84,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include <taskprogressmanager.h>
#include <scopeguard.h>
#include <usvfs.h>
+#include "localsavegames.h"
+#include "listdialog.h"
#include <QAbstractItemDelegate>
#include <QAbstractProxyModel>
@@ -105,6 +107,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include <QDropEvent>
#include <QEvent>
#include <QFileDialog>
+#include <QFIleIconProvider>
#include <QFont>
#include <QFuture>
#include <QHash>
@@ -153,6 +156,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>. #include <QWidgetAction>
#include <QWebEngineProfile>
#include <QShortcut>
+#include <QColorDialog>
+#include <QColor>
#include <QDebug>
#include <QtGlobal>
@@ -201,6 +206,7 @@ MainWindow::MainWindow(QSettings &initSettings , m_CategoryFactory(CategoryFactory::instance())
, m_ContextItem(nullptr)
, m_ContextAction(nullptr)
+ , m_ContextRow(-1)
, m_CurrentSaveView(nullptr)
, m_OrganizerCore(organizerCore)
, m_PluginContainer(pluginContainer)
@@ -261,7 +267,7 @@ MainWindow::MainWindow(QSettings &initSettings // insert spacers
ui->toolBar->insertWidget(action, spacer);
m_Sep = action;
- // m_Sep would only use the last seperator anyway, and we only have the one anyway?
+ // m_Sep would only use the last separator anyway, and we only have the one anyway?
break;
}
}
@@ -293,6 +299,7 @@ MainWindow::MainWindow(QSettings &initSettings ui->modList->header()->setSectionHidden(ModList::COL_MODID, true);
ui->modList->header()->setSectionHidden(ModList::COL_GAME, true);
ui->modList->header()->setSectionHidden(ModList::COL_INSTALLTIME, true);
+ ui->modList->header()->setSectionHidden(ModList::COL_NOTES, true);
}
ui->modList->header()->setSectionHidden(ModList::COL_NAME, false); // prevent the name-column from being hidden
@@ -306,7 +313,7 @@ MainWindow::MainWindow(QSettings &initSettings ui->espList->setItemDelegateForColumn(PluginList::COL_FLAGS, new GenericIconDelegate(ui->espList));
ui->espList->installEventFilter(m_OrganizerCore.pluginList());
- ui->bsaList->setLocalMoveOnly(true);
+ //ui->bsaList->setLocalMoveOnly(true);
bool pluginListAdjusted = registerWidgetState(ui->espList->objectName(), ui->espList->header(), "plugin_list_state");
registerWidgetState(ui->dataTree->objectName(), ui->dataTree->header());
@@ -325,6 +332,7 @@ MainWindow::MainWindow(QSettings &initSettings ui->linkButton->setMenu(linkMenu);
ui->listOptionsBtn->setMenu(modListContextMenu());
+ connect(ui->listOptionsBtn, SIGNAL(pressed()), this, SLOT(on_listOptionsBtn_pressed()));
ui->openFolderMenu->setMenu(openFolderMenu());
@@ -404,6 +412,8 @@ MainWindow::MainWindow(QSettings &initSettings new QShortcut(QKeySequence::Refresh, this, SLOT(refreshProfile_activated()));
+ new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F), this, SLOT(search_activated()));
+ new QShortcut(QKeySequence(Qt::Key_Escape), this, SLOT(searchClear_activated()));
m_UpdateProblemsTimer.setSingleShot(true);
connect(&m_UpdateProblemsTimer, SIGNAL(timeout()), this, SLOT(updateProblemsButton()));
@@ -426,9 +436,8 @@ MainWindow::MainWindow(QSettings &initSettings for (const QString &fileName : m_PluginContainer.pluginFileNames()) {
installTranslator(QFileInfo(fileName).baseName());
}
- for (IPluginTool *toolPlugin : m_PluginContainer.plugins<IPluginTool>()) {
- registerPluginTool(toolPlugin);
- }
+
+ registerPluginTools(m_PluginContainer.plugins<IPluginTool>());
for (IPluginModPage *modPagePlugin : m_PluginContainer.plugins<IPluginModPage>()) {
registerModPage(modPagePlugin);
@@ -1029,15 +1038,57 @@ void MainWindow::modPagePluginInvoke() }
}
-void MainWindow::registerPluginTool(IPluginTool *tool)
+void MainWindow::registerPluginTool(IPluginTool *tool, QString name, QMenu *menu)
{
- QAction *action = new QAction(tool->icon(), tool->displayName(), ui->toolBar);
+ if (name.isEmpty())
+ name = tool->displayName();
+
+ QAction *action = new QAction(tool->icon(), name, ui->toolBar);
action->setToolTip(tool->tooltip());
tool->setParentWidget(this);
action->setData(qVariantFromValue((QObject*)tool));
connect(action, SIGNAL(triggered()), this, SLOT(toolPluginInvoke()), Qt::QueuedConnection);
- QToolButton *toolBtn = qobject_cast<QToolButton*>(ui->toolBar->widgetForAction(ui->actionTool));
- toolBtn->menu()->addAction(action);
+
+ if (menu == nullptr) {
+ QToolButton *toolBtn = qobject_cast<QToolButton*>(ui->toolBar->widgetForAction(ui->actionTool));
+ toolBtn->menu()->addAction(action);
+ } else {
+ menu->addAction(action);
+ }
+}
+
+void MainWindow::registerPluginTools(std::vector<IPluginTool *> toolPlugins)
+{
+ // Sort the plugins by display name
+ std::sort(toolPlugins.begin(), toolPlugins.end(),
+ [](IPluginTool *left, IPluginTool *right) {
+ return left->displayName().toLower() < right->displayName().toLower();
+ }
+ );
+
+ // Group the plugins into submenus
+ QMap<QString, QList<QPair<QString, IPluginTool *>>> submenuMap;
+ for (auto toolPlugin : toolPlugins) {
+ QStringList toolName = toolPlugin->displayName().split("/");
+ QString submenu = toolName[0];
+ toolName.pop_front();
+ submenuMap[submenu].append(QPair<QString, IPluginTool *>(toolName.join("/"), toolPlugin));
+ }
+
+ // Start registering plugins
+ for (auto submenuKey : submenuMap.keys()) {
+ if (submenuMap[submenuKey].length() > 1) {
+ QMenu *submenu = new QMenu(submenuKey, this);
+ for (auto info : submenuMap[submenuKey]) {
+ registerPluginTool(info.second, info.first, submenu);
+ }
+ QToolButton *toolBtn = qobject_cast<QToolButton*>(ui->toolBar->widgetForAction(ui->actionTool));
+ toolBtn->menu()->addMenu(submenu);
+ }
+ else {
+ registerPluginTool(submenuMap[submenuKey].front().second);
+ }
+ }
}
void MainWindow::registerModPage(IPluginModPage *modPage)
@@ -1132,11 +1183,70 @@ void MainWindow::on_profileBox_currentIndexChanged(int index) } else {
activateSelectedProfile();
}
+
+ LocalSavegames *saveGames = m_OrganizerCore.managedGame()->feature<LocalSavegames>();
+ if (saveGames != nullptr) {
+ if (saveGames->prepareProfile(m_OrganizerCore.currentProfile()))
+ refreshSaveList();
+ }
+
+ BSAInvalidation *invalidation = m_OrganizerCore.managedGame()->feature<BSAInvalidation>();
+ if (invalidation != nullptr) {
+ if (invalidation->prepareProfile(m_OrganizerCore.currentProfile()))
+ QTimer::singleShot(5, &m_OrganizerCore, SLOT(profileRefresh()));
+ }
}
}
-void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &directorySoFar, const DirectoryEntry &directoryEntry, bool conflictsOnly)
+void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &directorySoFar, const DirectoryEntry &directoryEntry, bool conflictsOnly, QIcon *fileIcon, QIcon *folderIcon)
{
+ bool isDirectory = true;
+ //QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder);
+ //QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File);
+
+ std::wostringstream temp;
+ temp << directorySoFar << "\\" << directoryEntry.getName();
+ {
+ std::vector<DirectoryEntry*>::const_iterator current, end;
+ directoryEntry.getSubDirectories(current, end);
+ for (; current != end; ++current) {
+ QString pathName = ToQString((*current)->getName());
+ QStringList columns(pathName);
+ columns.append("");
+ if (!(*current)->isEmpty()) {
+ QTreeWidgetItem *directoryChild = new QTreeWidgetItem(columns);
+ directoryChild->setData(0, Qt::DecorationRole, *folderIcon);
+ directoryChild->setData(0, Qt::UserRole + 3, isDirectory);
+
+ if (conflictsOnly) {
+ updateTo(directoryChild, temp.str(), **current, conflictsOnly, fileIcon, folderIcon);
+ if (directoryChild->childCount() != 0) {
+ subTree->addChild(directoryChild);
+ }
+ else {
+ delete directoryChild;
+ }
+ }
+ else {
+ QTreeWidgetItem *onDemandLoad = new QTreeWidgetItem(QStringList());
+ onDemandLoad->setData(0, Qt::UserRole + 0, "__loaded_on_demand__");
+ onDemandLoad->setData(0, Qt::UserRole + 1, ToQString(temp.str()));
+ onDemandLoad->setData(0, Qt::UserRole + 2, conflictsOnly);
+ directoryChild->addChild(onDemandLoad);
+ subTree->addChild(directoryChild);
+ }
+ }
+ else {
+ QTreeWidgetItem *directoryChild = new QTreeWidgetItem(columns);
+ directoryChild->setData(0, Qt::DecorationRole, *folderIcon);
+ directoryChild->setData(0, Qt::UserRole + 3, isDirectory);
+ subTree->addChild(directoryChild);
+ }
+ }
+ }
+
+
+ isDirectory = false;
{
for (const FileEntry::Ptr current : directoryEntry.getFiles()) {
if (conflictsOnly && (current->getAlternatives().size() == 0)) {
@@ -1173,6 +1283,8 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director fileChild->setFont(1, font);
}
fileChild->setData(0, Qt::UserRole, ToQString(current->getFullPath()));
+ fileChild->setData(0, Qt::DecorationRole, *fileIcon);
+ fileChild->setData(0, Qt::UserRole + 3, isDirectory);
fileChild->setData(0, Qt::UserRole + 1, isArchive);
fileChild->setData(1, Qt::UserRole, source);
fileChild->setData(1, Qt::UserRole + 1, originID);
@@ -1198,37 +1310,8 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director }
}
- std::wostringstream temp;
- temp << directorySoFar << "\\" << directoryEntry.getName();
- {
- std::vector<DirectoryEntry*>::const_iterator current, end;
- directoryEntry.getSubDirectories(current, end);
- for (; current != end; ++current) {
- QString pathName = ToQString((*current)->getName());
- QStringList columns(pathName);
- columns.append("");
- if (!(*current)->isEmpty()) {
- QTreeWidgetItem *directoryChild = new QTreeWidgetItem(columns);
- if (conflictsOnly) {
- updateTo(directoryChild, temp.str(), **current, conflictsOnly);
- if (directoryChild->childCount() != 0) {
- subTree->addChild(directoryChild);
- } else {
- delete directoryChild;
- }
- } else {
- QTreeWidgetItem *onDemandLoad = new QTreeWidgetItem(QStringList());
- onDemandLoad->setData(0, Qt::UserRole + 0, "__loaded_on_demand__");
- onDemandLoad->setData(0, Qt::UserRole + 1, ToQString(temp.str()));
- onDemandLoad->setData(0, Qt::UserRole + 2, conflictsOnly);
- directoryChild->addChild(onDemandLoad);
- subTree->addChild(directoryChild);
- }
- }
- }
- }
- subTree->sortChildren(0, Qt::AscendingOrder);
+ //subTree->sortChildren(0, Qt::AscendingOrder);
}
void MainWindow::delayedRemove()
@@ -1241,6 +1324,7 @@ void MainWindow::delayedRemove() void MainWindow::expandDataTreeItem(QTreeWidgetItem *item)
{
+
if ((item->childCount() == 1) && (item->child(0)->data(0, Qt::UserRole).toString() == "__loaded_on_demand__")) {
// read the data we need from the sub-item, then dispose of it
QTreeWidgetItem *onDemandDataItem = item->child(0);
@@ -1250,7 +1334,9 @@ void MainWindow::expandDataTreeItem(QTreeWidgetItem *item) std::wstring virtualPath = (path + L"\\").substr(6) + ToWString(item->text(0));
DirectoryEntry *dir = m_OrganizerCore.directoryStructure()->findSubDirectoryRecursive(virtualPath);
if (dir != nullptr) {
- updateTo(item, path, *dir, conflictsOnly);
+ QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder);
+ QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File);
+ updateTo(item, path, *dir, conflictsOnly, &fileIcon, &folderIcon);
} else {
qWarning("failed to update view of %ls", path.c_str());
}
@@ -1325,11 +1411,14 @@ void MainWindow::refreshDataTree() {
QCheckBox *conflictsBox = findChild<QCheckBox*>("conflictsCheckBox");
QTreeWidget *tree = findChild<QTreeWidget*>("dataTree");
+ QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder);
+ QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File);
tree->clear();
QStringList columns("data");
columns.append("");
QTreeWidgetItem *subTree = new QTreeWidgetItem(columns);
- updateTo(subTree, L"", *m_OrganizerCore.directoryStructure(), conflictsBox->isChecked());
+ subTree->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::Folder));
+ updateTo(subTree, L"", *m_OrganizerCore.directoryStructure(), conflictsBox->isChecked(), &fileIcon, &folderIcon);
tree->insertTopLevelItem(0, subTree);
subTree->setExpanded(true);
}
@@ -1338,7 +1427,8 @@ void MainWindow::refreshDataTreeKeepExpandedNodes() {
QCheckBox *conflictsBox = findChild<QCheckBox*>("conflictsCheckBox");
QTreeWidget *tree = findChild<QTreeWidget*>("dataTree");
-
+ QIcon folderIcon = (new QFileIconProvider())->icon(QFileIconProvider::Folder);
+ QIcon fileIcon = (new QFileIconProvider())->icon(QFileIconProvider::File);
QStringList expandedNodes;
QTreeWidgetItemIterator it1(tree, QTreeWidgetItemIterator::NotHidden | QTreeWidgetItemIterator::HasChildren);
while (*it1) {
@@ -1353,7 +1443,8 @@ void MainWindow::refreshDataTreeKeepExpandedNodes() QStringList columns("data");
columns.append("");
QTreeWidgetItem *subTree = new QTreeWidgetItem(columns);
- updateTo(subTree, L"", *m_OrganizerCore.directoryStructure(), conflictsBox->isChecked());
+ subTree->setData(0, Qt::DecorationRole, (new QFileIconProvider())->icon(QFileIconProvider::Folder));
+ updateTo(subTree, L"", *m_OrganizerCore.directoryStructure(), conflictsBox->isChecked(), &fileIcon, &folderIcon);
tree->insertTopLevelItem(0, subTree);
subTree->setExpanded(true);
QTreeWidgetItemIterator it2(tree, QTreeWidgetItemIterator::HasChildren);
@@ -1380,12 +1471,17 @@ QDir MainWindow::currentSavesDir() const if (m_OrganizerCore.currentProfile()->localSavesEnabled()) {
savesDir.setPath(m_OrganizerCore.currentProfile()->savePath());
} else {
+ QString iniPath = m_OrganizerCore.currentProfile()->localSettingsEnabled()
+ ? m_OrganizerCore.currentProfile()->absolutePath()
+ : m_OrganizerCore.managedGame()->documentsDirectory().absolutePath();
+ iniPath += "/" + m_OrganizerCore.managedGame()->iniFiles()[0];
+
wchar_t path[MAX_PATH];
::GetPrivateProfileStringW(
L"General", L"SLocalSavePath", L"Saves",
path, MAX_PATH,
- ToWString(m_OrganizerCore.currentProfile()->absolutePath() + "/" +
- m_OrganizerCore.managedGame()->iniFiles()[0]).c_str());
+ iniPath.toStdWString().c_str()
+ );
savesDir.setPath(m_OrganizerCore.managedGame()->documentsDirectory().absoluteFilePath(QString::fromWCharArray(path)));
}
@@ -1687,6 +1783,9 @@ void MainWindow::processUpdates() { lastHidden = hidden;
}
}
+ if (lastVersion < QVersionNumber(2,1,6)) {
+ ui->modList->header()->setSectionHidden(ModList::COL_NOTES, true);
+ }
}
if (currentVersion > lastVersion)
@@ -1699,21 +1798,31 @@ void MainWindow::processUpdates() { void MainWindow::storeSettings(QSettings &settings) {
settings.setValue("group_state", ui->groupCombo->currentIndex());
-
- settings.setValue("window_geometry", saveGeometry());
- settings.setValue("window_split", ui->splitter->saveState());
- settings.setValue("log_split", ui->topLevelSplitter->saveState());
-
- settings.setValue("browser_geometry", m_IntegratedBrowser.saveGeometry());
-
- settings.setValue("filters_visible", ui->displayCategoriesBtn->isChecked());
-
settings.setValue("selected_executable",
ui->executablesListBox->currentIndex());
- for (const std::pair<QString, QHeaderView*> kv : m_PersistedGeometry) {
- QString key = QString("geometry/") + kv.first;
- settings.setValue(key, kv.second->saveState());
+ if (settings.value("reset_geometry", false).toBool()) {
+ settings.remove("window_geometry");
+ settings.remove("window_split");
+ settings.remove("log_split");
+ settings.remove("filters_visible");
+ settings.remove("browser_geometry");
+ settings.beginGroup("geometry");
+ for (auto key : settings.childKeys()) {
+ settings.remove(key);
+ }
+ settings.endGroup();
+ settings.remove("reset_geometry");
+ } else {
+ settings.setValue("window_geometry", saveGeometry());
+ settings.setValue("window_split", ui->splitter->saveState());
+ settings.setValue("log_split", ui->topLevelSplitter->saveState());
+ settings.setValue("browser_geometry", m_IntegratedBrowser.saveGeometry());
+ settings.setValue("filters_visible", ui->displayCategoriesBtn->isChecked());
+ for (const std::pair<QString, QHeaderView*> kv : m_PersistedGeometry) {
+ QString key = QString("geometry/") + kv.first;
+ settings.setValue(key, kv.second->saveState());
+ }
}
}
@@ -1758,6 +1867,11 @@ void MainWindow::on_btnRefreshData_clicked() m_OrganizerCore.refreshDirectoryStructure();
}
+void MainWindow::on_btnRefreshDownloads_clicked()
+{
+ m_OrganizerCore.downloadManager()->refreshList();
+}
+
void MainWindow::on_tabWidget_currentChanged(int index)
{
if (index == 0) {
@@ -1765,7 +1879,7 @@ void MainWindow::on_tabWidget_currentChanged(int index) } else if (index == 1) {
m_OrganizerCore.refreshBSAList();
} else if (index == 2) {
- refreshDataTree();
+ refreshDataTreeKeepExpandedNodes();
} else if (index == 3) {
refreshSaveList();
}
@@ -1796,14 +1910,21 @@ void MainWindow::installMod(QString fileName) }
void MainWindow::on_startButton_clicked() {
- const Executable &selectedExecutable(getSelectedExecutable());
- QString customOverwrite = m_OrganizerCore.currentProfile()->setting("custom_overwrites", selectedExecutable.m_Title).toString();
- m_OrganizerCore.spawnBinary(
- selectedExecutable.m_BinaryInfo, selectedExecutable.m_Arguments,
- selectedExecutable.m_WorkingDirectory.length() != 0
- ? selectedExecutable.m_WorkingDirectory
- : selectedExecutable.m_BinaryInfo.absolutePath(),
- selectedExecutable.m_SteamAppID, customOverwrite);
+ ui->startButton->setEnabled(false);
+ try {
+ const Executable &selectedExecutable(getSelectedExecutable());
+ QString customOverwrite = m_OrganizerCore.currentProfile()->setting("custom_overwrites", selectedExecutable.m_Title).toString();
+ m_OrganizerCore.spawnBinary(
+ selectedExecutable.m_BinaryInfo, selectedExecutable.m_Arguments,
+ selectedExecutable.m_WorkingDirectory.length() != 0
+ ? selectedExecutable.m_WorkingDirectory
+ : selectedExecutable.m_BinaryInfo.absolutePath(),
+ selectedExecutable.m_SteamAppID, customOverwrite);
+ } catch (...) {
+ ui->startButton->setEnabled(true);
+ throw;
+ }
+ ui->startButton->setEnabled(true);
}
static HRESULT CreateShortcut(LPCWSTR targetFileName, LPCWSTR arguments,
@@ -1895,10 +2016,16 @@ bool MainWindow::modifyExecutablesDialog() EditExecutablesDialog dialog(*m_OrganizerCore.executablesList(),
*m_OrganizerCore.modList(),
m_OrganizerCore.currentProfile());
+ QSettings &settings = m_OrganizerCore.settings().directInterface();
+ QString key = QString("geometry/%1").arg(dialog.objectName());
+ if (settings.contains(key)) {
+ dialog.restoreGeometry(settings.value(key).toByteArray());
+ }
if (dialog.exec() == QDialog::Accepted) {
m_OrganizerCore.setExecutablesList(dialog.getExecutablesList());
result = true;
}
+ settings.setValue(key, dialog.saveGeometry());
refreshExecutablesList();
} catch (const std::exception &e) {
reportError(e.what());
@@ -1964,16 +2091,33 @@ void MainWindow::on_actionAdd_Profile_triggered() ProfilesDialog profilesDialog(m_OrganizerCore.currentProfile()->name(),
m_OrganizerCore.managedGame(),
this);
+ QSettings &settings = m_OrganizerCore.settings().directInterface();
+ QString key = QString("geometry/%1").arg(profilesDialog.objectName());
+ if (settings.contains(key)) {
+ profilesDialog.restoreGeometry(settings.value(key).toByteArray());
+ }
// workaround: need to disable monitoring of the saves directory, otherwise the active
// profile directory is locked
stopMonitorSaves();
profilesDialog.exec();
+ settings.setValue(key, profilesDialog.saveGeometry());
refreshSaveList(); // since the save list may now be outdated we have to refresh it completely
if (refreshProfiles() && !profilesDialog.failed()) {
break;
}
}
-// addProfile();
+
+ LocalSavegames *saveGames = m_OrganizerCore.managedGame()->feature<LocalSavegames>();
+ if (saveGames != nullptr) {
+ if (saveGames->prepareProfile(m_OrganizerCore.currentProfile()))
+ refreshSaveList();
+ }
+
+ BSAInvalidation *invalidation = m_OrganizerCore.managedGame()->feature<BSAInvalidation>();
+ if (invalidation != nullptr) {
+ if (invalidation->prepareProfile(m_OrganizerCore.currentProfile()))
+ QTimer::singleShot(5, &m_OrganizerCore, SLOT(profileRefresh()));
+ }
}
void MainWindow::on_actionModify_Executables_triggered()
@@ -2015,7 +2159,9 @@ void MainWindow::refresher_progress(int percent) if (percent == 100) {
m_RefreshProgress->setVisible(false);
statusBar()->hide();
+ this->setEnabled(true);
} else if (!m_RefreshProgress->isVisible()) {
+ this->setEnabled(false);
statusBar()->show();
m_RefreshProgress->setVisible(true);
m_RefreshProgress->setRange(0, 100);
@@ -2027,7 +2173,7 @@ void MainWindow::directory_refreshed() {
// some problem-reports may rely on the virtual directory tree so they need to be updated
// now
- refreshDataTree();
+ refreshDataTreeKeepExpandedNodes();
updateProblemsButton();
statusBar()->hide();
}
@@ -2206,6 +2352,7 @@ void MainWindow::refreshFilters() addFilterItem(nullptr, tr("<Checked>"), CategoryFactory::CATEGORY_SPECIAL_CHECKED, ModListSortProxy::TYPE_SPECIAL);
addFilterItem(nullptr, tr("<Unchecked>"), CategoryFactory::CATEGORY_SPECIAL_UNCHECKED, ModListSortProxy::TYPE_SPECIAL);
addFilterItem(nullptr, tr("<Update>"), CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE, ModListSortProxy::TYPE_SPECIAL);
+ addFilterItem(nullptr, tr("<Mod Backup>"), CategoryFactory::CATEGORY_SPECIAL_BACKUP, ModListSortProxy::TYPE_SPECIAL);
addFilterItem(nullptr, tr("<Managed by MO>"), CategoryFactory::CATEGORY_SPECIAL_MANAGED, ModListSortProxy::TYPE_SPECIAL);
addFilterItem(nullptr, tr("<Managed outside MO>"), CategoryFactory::CATEGORY_SPECIAL_UNMANAGED, ModListSortProxy::TYPE_SPECIAL);
addFilterItem(nullptr, tr("<No category>"), CategoryFactory::CATEGORY_SPECIAL_NOCATEGORY, ModListSortProxy::TYPE_SPECIAL);
@@ -2307,13 +2454,13 @@ void MainWindow::modlistSelectionChanged(const QModelIndex ¤t, const QMode void MainWindow::modlistSelectionsChanged(const QItemSelection &selected)
{
- m_OrganizerCore.pluginList()->highlightPlugins(selected, *m_OrganizerCore.directoryStructure(), *m_OrganizerCore.currentProfile());
+ m_OrganizerCore.pluginList()->highlightPlugins(ui->modList->selectionModel(), *m_OrganizerCore.directoryStructure(), *m_OrganizerCore.currentProfile());
ui->espList->verticalScrollBar()->repaint();
}
void MainWindow::esplistSelectionsChanged(const QItemSelection &selected)
{
- m_OrganizerCore.modList()->highlightMods(selected, *m_OrganizerCore.directoryStructure());
+ m_OrganizerCore.modList()->highlightMods(ui->espList->selectionModel(), *m_OrganizerCore.directoryStructure());
ui->modList->verticalScrollBar()->repaint();
}
@@ -2335,7 +2482,7 @@ void MainWindow::removeMod_clicked() continue;
}
mods += "<li>" + name + "</li>";
- modNames.append(name);
+ modNames.append(ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->name());
}
if (QMessageBox::question(this, tr("Confirm"),
tr("Remove the following mods?<br><ul>%1</ul>").arg(mods),
@@ -2391,6 +2538,16 @@ void MainWindow::reinstallMod_clicked() }
}
+void MainWindow::backupMod_clicked()
+{
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow);
+ QString backupDirectory = m_OrganizerCore.installationManager()->generateBackupName(modInfo->absolutePath());
+ if (!copyDir(modInfo->absolutePath(), backupDirectory, false)) {
+ QMessageBox::information(this, tr("Failed"),
+ tr("Failed to create backup."));
+ }
+ m_OrganizerCore.refreshModList();
+}
void MainWindow::resumeDownload(int downloadIndex)
{
@@ -2468,6 +2625,9 @@ void MainWindow::overwriteClosed(int) OverwriteInfoDialog *dialog = this->findChild<OverwriteInfoDialog*>("__overwriteDialog");
if (dialog != nullptr) {
m_OrganizerCore.modList()->modInfoChanged(dialog->modInfo());
+ QSettings &settings = m_OrganizerCore.settings().directInterface();
+ QString key = QString("geometry/%1").arg(dialog->objectName());
+ settings.setValue(key, dialog->saveGeometry());
dialog->deleteLater();
}
m_OrganizerCore.refreshDirectoryStructure();
@@ -2490,6 +2650,11 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, } else {
qobject_cast<OverwriteInfoDialog*>(dialog)->setModInfo(modInfo);
}
+ QSettings &settings = m_OrganizerCore.settings().directInterface();
+ QString key = QString("geometry/%1").arg(dialog->objectName());
+ if (settings.contains(key)) {
+ dialog->restoreGeometry(settings.value(key).toByteArray());
+ }
dialog->show();
dialog->raise();
dialog->activateWindow();
@@ -2513,7 +2678,12 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, dialog.openTab(tab);
}
- dialog.restoreTabState(m_OrganizerCore.settings().directInterface().value("mod_info_tabs").toByteArray());
+ dialog.restoreTabState(m_OrganizerCore.settings().directInterface().value("mod_info_tabs").toByteArray());
+ QSettings &settings = m_OrganizerCore.settings().directInterface();
+ QString key = QString("geometry/%1").arg(dialog.objectName());
+ if (settings.contains(key)) {
+ dialog.restoreGeometry(settings.value(key).toByteArray());
+ }
//If no tab was specified use the first tab from the left based on the user order.
if (tab == -1) {
@@ -2527,6 +2697,8 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, dialog.exec();
m_OrganizerCore.settings().directInterface().setValue("mod_info_tabs", dialog.saveTabState());
+ settings.setValue(key, dialog.saveGeometry());
+
modInfo->saveMeta();
emit modInfoDisplayed();
@@ -2574,8 +2746,9 @@ void MainWindow::modOpenNext(int tab) ModInfo::Ptr mod = ModInfo::getByIndex(m_ContextRow);
std::vector<ModInfo::EFlag> flags = mod->getFlags();
if ((std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) ||
- (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end())) {
- // skip overwrite and backups
+ (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end()) ||
+ (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end())) {
+ // skip overwrite and backups and separators
modOpenNext(tab);
} else {
displayModInformation(m_ContextRow,tab);
@@ -2594,8 +2767,9 @@ void MainWindow::modOpenPrev(int tab) ModInfo::Ptr mod = ModInfo::getByIndex(m_ContextRow);
std::vector<ModInfo::EFlag> flags = mod->getFlags();
if ((std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) ||
- (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end())) {
- // skip overwrite and backups
+ (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end()) ||
+ (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end())) {
+ // skip overwrite and backups and separators
modOpenPrev(tab);
} else {
displayModInformation(m_ContextRow,tab);
@@ -2629,16 +2803,18 @@ void MainWindow::ignoreMissingData_clicked() for (QModelIndex idx : selection->selectedRows()) {
int row_idx = idx.data(Qt::UserRole + 1).toInt();
ModInfo::Ptr info = ModInfo::getByIndex(row_idx);
- QDir(info->absolutePath()).mkdir("textures");
+ //QDir(info->absolutePath()).mkdir("textures");
info->testValid();
+ info->markValidated(true);
connect(this, SIGNAL(modListDataChanged(QModelIndex, QModelIndex)), m_OrganizerCore.modList(), SIGNAL(dataChanged(QModelIndex, QModelIndex)));
emit modListDataChanged(m_OrganizerCore.modList()->index(row_idx, 0), m_OrganizerCore.modList()->index(row_idx, m_OrganizerCore.modList()->columnCount() - 1));
}
} else {
ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow);
- QDir(info->absolutePath()).mkdir("textures");
+ //QDir(info->absolutePath()).mkdir("textures");
info->testValid();
+ info->markValidated(true);
connect(this, SIGNAL(modListDataChanged(QModelIndex, QModelIndex)), m_OrganizerCore.modList(), SIGNAL(dataChanged(QModelIndex, QModelIndex)));
emit modListDataChanged(m_OrganizerCore.modList()->index(m_ContextRow, 0), m_OrganizerCore.modList()->index(m_ContextRow, m_OrganizerCore.modList()->columnCount() - 1));
@@ -2701,6 +2877,29 @@ void MainWindow::openExplorer_clicked() }
}
+void MainWindow::openOriginExplorer_clicked()
+{
+ QItemSelectionModel *selection = ui->espList->selectionModel();
+ if (selection->hasSelection() && selection->selectedRows().count() > 0) {
+ for (QModelIndex idx : selection->selectedRows()) {
+ QString fileName = idx.data().toString();
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName)));
+ std::vector<ModInfo::EFlag> flags = modInfo->getFlags();
+
+ ::ShellExecuteW(nullptr, L"explore", ToWString(modInfo->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL);
+ }
+ }
+ else {
+ QModelIndex idx = selection->currentIndex();
+ QString fileName = idx.data().toString();
+
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName)));
+ std::vector<ModInfo::EFlag> flags = modInfo->getFlags();
+
+ ::ShellExecuteW(nullptr, L"explore", ToWString(modInfo->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL);
+ }
+}
+
void MainWindow::openExplorer_activated()
{
if (ui->modList->hasFocus()) {
@@ -2744,6 +2943,32 @@ void MainWindow::refreshProfile_activated() m_OrganizerCore.profileRefresh();
}
+void MainWindow::search_activated()
+{
+ if (ui->modList->hasFocus() || ui->modFilterEdit->hasFocus()) {
+ ui->modFilterEdit->setFocus();
+ ui->modFilterEdit->setSelection(0, INT_MAX);
+ }
+
+ if (ui->espList->hasFocus() || ui->espFilterEdit->hasFocus()) {
+ ui->espFilterEdit->setFocus();
+ ui->espFilterEdit->setSelection(0, INT_MAX);
+ }
+}
+
+void MainWindow::searchClear_activated()
+{
+ if (ui->modFilterEdit->hasFocus()) {
+ ui->modFilterEdit->clear();
+ ui->modList->setFocus();
+ }
+
+ if (ui->espFilterEdit->hasFocus()) {
+ ui->espFilterEdit->clear();
+ ui->espList->setFocus();
+ }
+}
+
void MainWindow::information_clicked()
{
try {
@@ -2774,12 +2999,120 @@ void MainWindow::createEmptyMod_clicked() return;
}
+ int newPriority = -1;
+ if (m_ContextRow >= 0 && m_ModListSortProxy->sortColumn() == ModList::COL_PRIORITY) {
+ newPriority = m_OrganizerCore.currentProfile()->getModPriority(m_ContextRow);
+ }
+
IModInterface *newMod = m_OrganizerCore.createMod(name);
if (newMod == nullptr) {
return;
}
m_OrganizerCore.refreshModList();
+
+ if (newPriority >= 0) {
+ m_OrganizerCore.modList()->changeModPriority(ModInfo::getIndex(name), newPriority);
+ }
+}
+
+void MainWindow::createSeparator_clicked()
+{
+ GuessedValue<QString> name;
+ name.setFilter(&fixDirectoryName);
+ while (name->isEmpty())
+ {
+ bool ok;
+ name.update(QInputDialog::getText(this, tr("Create Separator..."),
+ tr("This will create a new separator.\n"
+ "Please enter a name:"), QLineEdit::Normal, "", &ok),
+ GUESS_USER);
+ if (!ok) { return; }
+ }
+ if (m_OrganizerCore.getMod(name) != nullptr)
+ {
+ reportError(tr("A separator with this name already exists"));
+ return;
+ }
+ name->append("_separator");
+ if (m_OrganizerCore.getMod(name) != nullptr)
+ {
+ return;
+ }
+
+ int newPriority = -1;
+ if (m_ContextRow >= 0 && m_ModListSortProxy->sortColumn() == ModList::COL_PRIORITY)
+ {
+ newPriority = m_OrganizerCore.currentProfile()->getModPriority(m_ContextRow);
+ }
+
+ if (m_OrganizerCore.createMod(name) == nullptr) { return; }
+ m_OrganizerCore.refreshModList();
+
+ if (newPriority >= 0)
+ {
+ m_OrganizerCore.modList()->changeModPriority(ModInfo::getIndex(name), newPriority);
+ }
+ QSettings &settings = m_OrganizerCore.settings().directInterface();
+ QColor previousColor = settings.value("previousSeparatorColor", QColor()).value<QColor>();
+ if (previousColor.isValid()) {
+ ModInfo::getByIndex(ModInfo::getIndex(name))->setColor(previousColor);
+ }
+
+}
+
+void MainWindow::setColor_clicked()
+{
+ QSettings &settings = m_OrganizerCore.settings().directInterface();
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow);
+ QColorDialog dialog(this);
+ QColor currentColor = modInfo->getColor();
+ QColor previousColor = settings.value("previousSeparatorColor", QColor()).value<QColor>();
+ if (currentColor.isValid())
+ dialog.setCurrentColor(currentColor);
+ else
+ dialog.setCurrentColor(previousColor);
+ if (!dialog.exec())
+ return;
+ currentColor = dialog.currentColor();
+ if (!currentColor.isValid())
+ return;
+ settings.setValue("previousSeparatorColor", currentColor);
+ QItemSelectionModel *selection = ui->modList->selectionModel();
+ if (selection->hasSelection() && selection->selectedRows().count() > 1) {
+ for (QModelIndex idx : selection->selectedRows()) {
+ ModInfo::Ptr info = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt());
+ auto flags = info->getFlags();
+ if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end())
+ {
+ info->setColor(currentColor);
+ }
+ }
+ }
+ else {
+ modInfo->setColor(currentColor);
+ }
+}
+
+void MainWindow::resetColor_clicked()
+{
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ContextRow);
+ QColor color = QColor();
+ QItemSelectionModel *selection = ui->modList->selectionModel();
+ if (selection->hasSelection() && selection->selectedRows().count() > 1) {
+ for (QModelIndex idx : selection->selectedRows()) {
+ ModInfo::Ptr info = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt());
+ auto flags = info->getFlags();
+ if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end())
+ {
+ info->setColor(color);
+ }
+ }
+ }
+ else {
+ modInfo->setColor(color);
+ }
+ Settings::instance().directInterface().remove("previousSeparatorColor");
}
void MainWindow::createModFromOverwrite()
@@ -2893,6 +3226,44 @@ void MainWindow::on_modList_doubleClicked(const QModelIndex &index) }
}
+void MainWindow::on_listOptionsBtn_pressed()
+{
+ m_ContextRow = -1;
+}
+
+void MainWindow::openOriginInformation_clicked()
+{
+ try {
+ QItemSelectionModel *selection = ui->espList->selectionModel();
+ //we don't want to open multiple modinfodialogs.
+ /*if (selection->hasSelection() && selection->selectedRows().count() > 0) {
+
+ for (QModelIndex idx : selection->selectedRows()) {
+ QString fileName = idx.data().toString();
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName)));
+ std::vector<ModInfo::EFlag> flags = modInfo->getFlags();
+
+ if (modInfo->isRegular() || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end())) {
+ displayModInformation(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName)));
+ }
+ }
+ }
+ else {}*/
+ QModelIndex idx = selection->currentIndex();
+ QString fileName = idx.data().toString();
+
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName)));
+ std::vector<ModInfo::EFlag> flags = modInfo->getFlags();
+
+ if (modInfo->isRegular() || (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end())) {
+ displayModInformation(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(fileName)));
+ }
+ }
+ catch (const std::exception &e) {
+ reportError(e.what());
+ }
+}
+
void MainWindow::on_espList_doubleClicked(const QModelIndex &index)
{
if (!index.isValid()) {
@@ -3268,7 +3639,11 @@ void MainWindow::disableVisibleMods() void MainWindow::openInstanceFolder()
{
- ::ShellExecuteW(nullptr, L"explore", ToWString(m_OrganizerCore.settings().getBaseDirectory()).c_str(), nullptr, nullptr, SW_SHOWNORMAL);
+ QString dataPath = qApp->property("dataPath").toString();
+ ::ShellExecuteW(nullptr, L"explore", ToWString(dataPath).c_str(), nullptr, nullptr, SW_SHOWNORMAL);
+
+ //opens BaseDirectory instead
+ //::ShellExecuteW(nullptr, L"explore", ToWString(m_OrganizerCore.settings().getBaseDirectory()).c_str(), nullptr, nullptr, SW_SHOWNORMAL);
}
void MainWindow::openLogsFolder()
@@ -3294,6 +3669,17 @@ void MainWindow::openProfileFolder() ::ShellExecuteW(nullptr, L"explore", ToWString(m_OrganizerCore.currentProfile()->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL);
}
+void MainWindow::openIniFolder()
+{
+ if (m_OrganizerCore.currentProfile()->localSettingsEnabled())
+ {
+ ::ShellExecuteW(nullptr, L"explore", ToWString(m_OrganizerCore.currentProfile()->absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL);
+ }
+ else {
+ ::ShellExecuteW(nullptr, L"explore", ToWString(m_OrganizerCore.managedGame()->documentsDirectory().absolutePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL);
+ }
+}
+
void MainWindow::openDownloadsFolder()
{
::ShellExecuteW(nullptr, L"explore", ToWString(m_OrganizerCore.settings().getDownloadDirectory()).c_str(), nullptr, nullptr, SW_SHOWNORMAL);
@@ -3493,6 +3879,8 @@ QMenu *MainWindow::openFolderMenu() FolderMenu->addAction(tr("Open MyGames folder"), this, SLOT(openMyGamesFolder()));
+ FolderMenu->addAction(tr("Open INIs folder"), this, SLOT(openIniFolder()));
+
FolderMenu->addSeparator();
FolderMenu->addAction(tr("Open Instance folder"), this, SLOT(openInstanceFolder()));
@@ -3522,6 +3910,8 @@ QMenu *MainWindow::modListContextMenu() menu->addAction(tr("Create empty mod"), this, SLOT(createEmptyMod_clicked()));
+ menu->addAction(tr("Create Separator"), this, SLOT(createSeparator_clicked()));
+
menu->addSeparator();
menu->addAction(tr("Enable all visible"), this, SLOT(enableVisibleMods()));
@@ -3537,6 +3927,37 @@ QMenu *MainWindow::modListContextMenu() return menu;
}
+void MainWindow::addModSendToContextMenu(QMenu *menu)
+{
+ if (m_ModListSortProxy->sortColumn() != ModList::COL_PRIORITY)
+ return;
+
+ QMenu *sub_menu = new QMenu(this);
+ sub_menu->setTitle(tr("Send to"));
+ sub_menu->addAction(tr("Top"), this, SLOT(sendSelectedModsToTop_clicked()));
+ sub_menu->addAction(tr("Bottom"), this, SLOT(sendSelectedModsToBottom_clicked()));
+ sub_menu->addAction(tr("Priority..."), this, SLOT(sendSelectedModsToPriority_clicked()));
+ sub_menu->addAction(tr("Separator..."), this, SLOT(sendSelectedModsToSeparator_clicked()));
+
+ menu->addMenu(sub_menu);
+ menu->addSeparator();
+}
+
+void MainWindow::addPluginSendToContextMenu(QMenu *menu)
+{
+ if (m_PluginListSortProxy->sortColumn() != PluginList::COL_PRIORITY)
+ return;
+
+ QMenu *sub_menu = new QMenu(this);
+ sub_menu->setTitle(tr("Send to"));
+ sub_menu->addAction(tr("Top"), this, SLOT(sendSelectedPluginsToTop_clicked()));
+ sub_menu->addAction(tr("Bottom"), this, SLOT(sendSelectedPluginsToBottom_clicked()));
+ sub_menu->addAction(tr("Priority..."), this, SLOT(sendSelectedPluginsToPriority_clicked()));
+
+ menu->addMenu(sub_menu);
+ menu->addSeparator();
+}
+
void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos)
{
try {
@@ -3554,6 +3975,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu = new QMenu(this);
allMods->setTitle(tr("All Mods"));
menu->addMenu(allMods);
+ menu->addSeparator();
ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow);
std::vector<ModInfo::EFlag> flags = info->getFlags();
if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) {
@@ -3566,30 +3988,42 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) } else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) != flags.end()) {
menu->addAction(tr("Restore Backup"), this, SLOT(restoreBackup_clicked()));
menu->addAction(tr("Remove Backup..."), this, SLOT(removeMod_clicked()));
+ } else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_SEPARATOR) != flags.end()){
+ menu->addSeparator();
+ QMenu *addRemoveCategoriesMenu = new QMenu(tr("Change Categories"));
+ populateMenuCategories(addRemoveCategoriesMenu, 0);
+ connect(addRemoveCategoriesMenu, SIGNAL(aboutToHide()), this, SLOT(addRemoveCategories_MenuHandler()));
+ addMenuAsPushButton(menu, addRemoveCategoriesMenu);
+ QMenu *primaryCategoryMenu = new QMenu(tr("Primary Category"));
+ connect(primaryCategoryMenu, SIGNAL(aboutToShow()), this, SLOT(addPrimaryCategoryCandidates()));
+ addMenuAsPushButton(menu, primaryCategoryMenu);
+ menu->addSeparator();
+ menu->addAction(tr("Rename Separator..."), this, SLOT(renameMod_clicked()));
+ menu->addAction(tr("Remove Separator..."), this, SLOT(removeMod_clicked()));
+ menu->addSeparator();
+ addModSendToContextMenu(menu);
+ menu->addAction(tr("Select Color..."), this, SLOT(setColor_clicked()));
+ if(info->getColor().isValid())
+ menu->addAction(tr("Reset Color"), this, SLOT(resetColor_clicked()));
+ menu->addSeparator();
} else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) {
- // nop, nothing to do with this mod
+ addModSendToContextMenu(menu);
} else {
QMenu *addRemoveCategoriesMenu = new QMenu(tr("Change Categories"));
populateMenuCategories(addRemoveCategoriesMenu, 0);
connect(addRemoveCategoriesMenu, SIGNAL(aboutToHide()), this, SLOT(addRemoveCategories_MenuHandler()));
addMenuAsPushButton(menu, addRemoveCategoriesMenu);
- //Removed as it was redundant, just making the categories look more complicated.
- /*
- QMenu *replaceCategoriesMenu = new QMenu(tr("Replace Categories"));
- populateMenuCategories(replaceCategoriesMenu, 0);
- connect(replaceCategoriesMenu, SIGNAL(aboutToHide()), this, SLOT(replaceCategories_MenuHandler()));
- addMenuAsPushButton(menu, replaceCategoriesMenu);
- */
-
QMenu *primaryCategoryMenu = new QMenu(tr("Primary Category"));
connect(primaryCategoryMenu, SIGNAL(aboutToShow()), this, SLOT(addPrimaryCategoryCandidates()));
addMenuAsPushButton(menu, primaryCategoryMenu);
menu->addSeparator();
+
if (info->downgradeAvailable()) {
menu->addAction(tr("Change versioning scheme"), this, SLOT(changeVersioningScheme()));
}
+
if (info->updateAvailable() || info->downgradeAvailable()) {
if (info->updateIgnored()) {
menu->addAction(tr("Un-ignore update"), this, SLOT(unignoreUpdate()));
@@ -3605,11 +4039,14 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) menu->addSeparator();
+ addModSendToContextMenu(menu);
+
menu->addAction(tr("Rename Mod..."), this, SLOT(renameMod_clicked()));
menu->addAction(tr("Reinstall Mod"), this, SLOT(reinstallMod_clicked()));
- menu->addAction(tr("Remove Mod..."), this, SLOT(removeMod_clicked()));
+ menu->addAction(tr("Remove Mod..."), this, SLOT(removeMod_clicked()));
+ menu->addAction(tr("Create Backup"), this, SLOT(backupMod_clicked()));
- menu->addSeparator();
+ menu->addSeparator();
if (info->getNexusID() > 0) {
switch (info->endorsedState()) {
@@ -3631,7 +4068,7 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos) }
}
- menu->addSeparator();
+ menu->addSeparator();
std::vector<ModInfo::EFlag> flags = info->getFlags();
if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_INVALID) != flags.end()) {
@@ -3879,11 +4316,22 @@ void MainWindow::on_actionSettings_triggered() QString oldModDirectory(settings.getModDirectory());
QString oldCacheDirectory(settings.getCacheDirectory());
QString oldProfilesDirectory(settings.getProfileDirectory());
+ QString oldManagedGameDirectory(settings.getManagedGameDirectory());
bool oldDisplayForeign(settings.displayForeign());
bool proxy = settings.useProxy();
+ DownloadManager *dlManager = m_OrganizerCore.downloadManager();
settings.query(&m_PluginContainer, this);
+ if (oldManagedGameDirectory != settings.getManagedGameDirectory()) {
+ QMessageBox::about(this, tr("Restarting MO"),
+ tr("Changing the managed game directory requires restarting MO.\n"
+ "Any pending downloads will be paused.\n\n"
+ "Click OK to restart MO now."));
+ dlManager->pauseAll();
+ qApp->exit(INT_MAX);
+ }
+
InstallationManager *instManager = m_OrganizerCore.installationManager();
instManager->setModsDirectory(settings.getModDirectory());
instManager->setDownloadDirectory(settings.getDownloadDirectory());
@@ -3895,8 +4343,6 @@ void MainWindow::on_actionSettings_triggered() refreshProfiles();
}
- DownloadManager *dlManager = m_OrganizerCore.downloadManager();
-
if (dlManager->getOutputDirectory() != settings.getDownloadDirectory()) {
if (dlManager->downloadsInProgress()) {
MessageDialog::showMessage(tr("Can't change download directory while "
@@ -3925,7 +4371,7 @@ void MainWindow::on_actionSettings_triggered() updateDownloadListDelegate();
- m_OrganizerCore.updateVFSParams(settings.logLevel(), settings.crashDumpsType());
+ m_OrganizerCore.updateVFSParams(settings.logLevel(), settings.crashDumpsType(), settings.executablesBlacklist());
m_OrganizerCore.cycleDiagnostics();
}
@@ -4188,6 +4634,27 @@ void MainWindow::disableSelectedPlugins_clicked() m_OrganizerCore.pluginList()->disableSelected(ui->espList->selectionModel());
}
+void MainWindow::sendSelectedPluginsToTop_clicked()
+{
+ m_OrganizerCore.pluginList()->sendToPriority(ui->espList->selectionModel(), 0);
+}
+
+void MainWindow::sendSelectedPluginsToBottom_clicked()
+{
+ m_OrganizerCore.pluginList()->sendToPriority(ui->espList->selectionModel(), INT_MAX);
+}
+
+void MainWindow::sendSelectedPluginsToPriority_clicked()
+{
+ bool ok;
+ int newPriority = QInputDialog::getInt(this,
+ tr("Set Priority"), tr("Set the priority of the selected plugins"),
+ 0, 0, INT_MAX, 1, &ok);
+ if (!ok) return;
+
+ m_OrganizerCore.pluginList()->sendToPriority(ui->espList->selectionModel(), newPriority);
+}
+
void MainWindow::enableSelectedMods_clicked()
{
@@ -4255,7 +4722,13 @@ void MainWindow::previewDataFile() addFunc(alt.first);
}
if (preview.numVariants() > 0) {
+ QSettings &settings = m_OrganizerCore.settings().directInterface();
+ QString key = QString("geometry/%1").arg(preview.objectName());
+ if (settings.contains(key)) {
+ preview.restoreGeometry(settings.value(key).toByteArray());
+ }
preview.exec();
+ settings.setValue(key, preview.saveGeometry());
} else {
QMessageBox::information(this, tr("Sorry"), tr("Sorry, can't preview anything. This function currently does not support extracting from bsas."));
}
@@ -4327,7 +4800,8 @@ void MainWindow::on_dataTree_customContextMenuRequested(const QPoint &pos) m_ContextItem = dataTree->itemAt(pos.x(), pos.y());
QMenu menu;
- if ((m_ContextItem != nullptr) && (m_ContextItem->childCount() == 0)) {
+ if ((m_ContextItem != nullptr) && (m_ContextItem->childCount() == 0)
+ && (m_ContextItem->data(0, Qt::UserRole + 3).toBool() != true)) {
menu.addAction(tr("Open/Execute"), this, SLOT(openDataFile()));
menu.addAction(tr("Add as Executable"), this, SLOT(addAsExecutable()));
@@ -4355,7 +4829,7 @@ void MainWindow::on_dataTree_customContextMenuRequested(const QPoint &pos) void MainWindow::on_conflictsCheckBox_toggled(bool)
{
- refreshDataTree();
+ refreshDataTreeKeepExpandedNodes();
}
@@ -4517,7 +4991,7 @@ void MainWindow::nxmDownloadURLs(QString, int, int, QVariant, QVariant resultDat info.name = serverInfo["Name"].toString();
info.premium = serverInfo["IsPremium"].toBool();
info.lastSeen = QDate::currentDate();
- info.preferred = 0;
+ info.preferred = !info.name.compare("CDN", Qt::CaseInsensitive);
// other keys: ConnectedUsers, Country, URI
servers.append(info);
}
@@ -4716,9 +5190,16 @@ void MainWindow::on_displayCategoriesBtn_toggled(bool checked) void MainWindow::editCategories()
{
CategoriesDialog dialog(this);
+ QSettings &settings = m_OrganizerCore.settings().directInterface();
+ QString key = QString("geometry/%1").arg(dialog.objectName());
+ if (settings.contains(key)) {
+ dialog.restoreGeometry(settings.value(key).toByteArray());
+ }
if (dialog.exec() == QDialog::Accepted) {
dialog.commitChanges();
}
+ settings.setValue(key, dialog.saveGeometry());
+
}
void MainWindow::deselectFilters()
@@ -4800,6 +5281,10 @@ void MainWindow::on_espList_customContextMenuRequested(const QPoint &pos) menu.addAction(tr("Enable all"), m_OrganizerCore.pluginList(), SLOT(enableAll()));
menu.addAction(tr("Disable all"), m_OrganizerCore.pluginList(), SLOT(disableAll()));
+ menu.addSeparator();
+
+ addPluginSendToContextMenu(&menu);
+
QItemSelection currentSelection = ui->espList->selectionModel()->selection();
bool hasLocked = false;
bool hasUnlocked = false;
@@ -4814,8 +5299,6 @@ void MainWindow::on_espList_customContextMenuRequested(const QPoint &pos) }
}
- menu.addSeparator();
-
if (hasLocked) {
menu.addAction(tr("Unlock load order"), this, SLOT(unlockESPIndex()));
}
@@ -4823,6 +5306,18 @@ void MainWindow::on_espList_customContextMenuRequested(const QPoint &pos) menu.addAction(tr("Lock load order"), this, SLOT(lockESPIndex()));
}
+ menu.addSeparator();
+ menu.addAction(tr("Open Origin in Explorer"), this, SLOT(openOriginExplorer_clicked()));
+
+ QModelIndex idx = ui->espList->selectionModel()->currentIndex();
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(m_OrganizerCore.pluginList()->origin(idx.data().toString())));
+ std::vector<ModInfo::EFlag> flags = modInfo->getFlags();
+
+ if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) == flags.end()) {
+ QAction *infoAction = menu.addAction(tr("Open Origin Info..."), this, SLOT(openOriginInformation_clicked()));
+ menu.setDefaultAction(infoAction);
+ }
+
try {
menu.exec(ui->espList->mapToGlobal(pos));
} catch (const std::exception &e) {
@@ -5410,3 +5905,91 @@ void MainWindow::on_clearFiltersButton_clicked() {
deselectFilters();
}
+
+void MainWindow::sendSelectedModsToPriority(int newPriority)
+{
+ QItemSelectionModel *selection = ui->modList->selectionModel();
+ if (selection->hasSelection() && selection->selectedRows().count() > 1) {
+ std::vector<int> modsToMove;
+ for (auto idx : selection->selectedRows(ModList::COL_PRIORITY)) {
+ modsToMove.push_back(m_OrganizerCore.currentProfile()->modIndexByPriority(idx.data().toInt()));
+ }
+ m_OrganizerCore.modList()->changeModPriority(modsToMove, newPriority);
+ } else {
+ m_OrganizerCore.modList()->changeModPriority(m_ContextRow, newPriority);
+ }
+}
+
+void MainWindow::sendSelectedModsToTop_clicked()
+{
+ sendSelectedModsToPriority(0);
+}
+
+void MainWindow::sendSelectedModsToBottom_clicked()
+{
+ sendSelectedModsToPriority(INT_MAX);
+}
+
+void MainWindow::sendSelectedModsToPriority_clicked()
+{
+ bool ok;
+ int newPriority = QInputDialog::getInt(this,
+ tr("Set Priority"), tr("Set the priority of the selected mods"),
+ 0, 0, INT_MAX, 1, &ok);
+ if (!ok) return;
+
+ sendSelectedModsToPriority(newPriority);
+}
+
+void MainWindow::sendSelectedModsToSeparator_clicked()
+{
+ QStringList separators;
+ for (auto mod : m_OrganizerCore.modList()->allMods()) {
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(ModInfo::getIndex(mod));
+ if (modInfo->hasFlag(ModInfo::FLAG_SEPARATOR)) {
+ separators << mod.chopped(10);
+ }
+ }
+
+ ListDialog dialog(this);
+ QSettings &settings = m_OrganizerCore.settings().directInterface();
+ QString key = QString("geometry/%1").arg(dialog.objectName());
+
+ dialog.setWindowTitle("Select a separator...");
+ dialog.setChoices(separators);
+ dialog.restoreGeometry(settings.value(key).toByteArray());
+ if (dialog.exec() == QDialog::Accepted) {
+ QString result = dialog.getChoice();
+ if (!result.isEmpty()) {
+ result += "_separator";
+
+ int newPriority = INT_MAX;
+ bool foundSection = false;
+ for (auto mod : m_OrganizerCore.modsSortedByProfilePriority()) {
+ unsigned int modIndex = ModInfo::getIndex(mod);
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex);
+ if (!foundSection && result.compare(mod) == 0) {
+ foundSection = true;
+ } else if (foundSection && modInfo->hasFlag(ModInfo::FLAG_SEPARATOR)) {
+ newPriority = m_OrganizerCore.currentProfile()->getModPriority(modIndex);
+ break;
+ }
+ }
+
+ QItemSelectionModel *selection = ui->modList->selectionModel();
+ if (selection->hasSelection() && selection->selectedRows().count() > 1) {
+ std::vector<int> modsToMove;
+ for (QModelIndex idx : selection->selectedRows(ModList::COL_PRIORITY)) {
+ modsToMove.push_back(m_OrganizerCore.currentProfile()->modIndexByPriority(idx.data().toInt()));
+ }
+ m_OrganizerCore.modList()->changeModPriority(modsToMove, newPriority);
+ } else {
+ int oldPriority = m_OrganizerCore.currentProfile()->getModPriority(m_ContextRow);
+ if (oldPriority < newPriority)
+ --newPriority;
+ m_OrganizerCore.modList()->changeModPriority(m_ContextRow, newPriority);
+ }
+ }
+ }
+ settings.setValue(key, dialog.saveGeometry());
+}
|
