summaryrefslogtreecommitdiff
path: root/src/mainwindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mainwindow.cpp')
-rw-r--r--src/mainwindow.cpp1440
1 files changed, 832 insertions, 608 deletions
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 625b406d..1e8d512a 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -57,8 +57,10 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "savetextasdialog.h"
#include "problemsdialog.h"
#include "previewdialog.h"
+#include "browserdialog.h"
#include "aboutdialog.h"
#include "safewritefile.h"
+#include "organizerproxy.h"
#include <gameinfo.h>
#include <appconfig.h>
#include <utility.h>
@@ -94,6 +96,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QDesktopWidget>
#include <QtPlugin>
#include <QIdentityProxyModel>
+#include <QClipboard>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/assign.hpp>
@@ -109,8 +112,10 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QtConcurrentRun>
#endif
#include <QCoreApplication>
+#include <QProgressDialog>
#include <scopeguard.h>
#include <boost/thread.hpp>
+#include <boost/algorithm/string.hpp>
#ifdef TEST_MODELS
@@ -157,12 +162,21 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
m_PluginList(this), m_OldExecutableIndex(-1), m_GamePath(ToQString(GameInfo::instance().getGameDirectory())),
m_DownloadManager(NexusInterface::instance(), this), m_InstallationManager(this),
m_Updater(NexusInterface::instance(), this), m_CategoryFactory(CategoryFactory::instance()),
- m_CurrentProfile(NULL), m_AskForNexusPW(false), m_LoginAttempted(false),
+ m_CurrentProfile(NULL), m_AskForNexusPW(false),
m_ArchivesInit(false), m_ContextItem(NULL), m_ContextAction(NULL), m_CurrentSaveView(NULL),
- m_GameInfo(new GameInfoImpl()), m_AboutToRun()
+ m_GameInfo(new GameInfoImpl()), m_AboutToRun(), m_ModInstalled()
{
ui->setupUi(this);
this->setWindowTitle(ToQString(GameInfo::instance().getGameName()) + " Mod Organizer v" + m_Updater.getVersion().displayString());
+ ui->logList->setModel(LogBuffer::instance());
+ ui->logList->setColumnWidth(0, 100);
+ ui->logList->setAutoScroll(true);
+ ui->logList->scrollToBottom();
+ ui->logList->addAction(ui->actionCopy_Log_to_Clipboard);
+ int splitterSize = this->size().height(); // actually total window size, but the splitter doesn't seem to return the true value
+ ui->topLevelSplitter->setSizes(QList<int>() << splitterSize - 100 << 100);
+ connect(ui->logList->model(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), ui->logList, SLOT(scrollToBottom()));
+ connect(ui->logList->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), ui->logList, SLOT(scrollToBottom()));
m_RefreshProgress = new QProgressBar(statusBar());
m_RefreshProgress->setTextVisible(true);
@@ -232,6 +246,8 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Start Menu"), this, SLOT(linkMenu()));
ui->linkButton->setMenu(linkMenu);
+ ui->listOptionsBtn->setMenu(modListContextMenu());
+
m_DownloadManager.setOutputDirectory(m_Settings.getDownloadDirectory());
m_DownloadManager.setPreferredServers(m_Settings.getPreferredServers());
@@ -269,7 +285,6 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
connect(&m_PluginList, SIGNAL(saveTimer()), this, SLOT(savePluginList()));
connect(ui->bsaList, SIGNAL(itemsMoved()), this, SLOT(bsaList_itemMoved()));
- connect(ui->bsaWarning, SIGNAL(linkActivated(QString)), this, SLOT(linkClicked(QString)));
connect(ui->dataTree, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(expandDataTreeItem(QTreeWidgetItem*)));
@@ -292,11 +307,14 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginFailed(QString)), this, SLOT(loginFailed(QString)));
connect(NexusInterface::instance(), SIGNAL(requestNXMDownload(QString)), this, SLOT(downloadRequestedNXM(QString)));
connect(NexusInterface::instance(), SIGNAL(nxmDownloadURLsAvailable(int,int,QVariant,QVariant,int)), this, SLOT(nxmDownloadURLs(int,int,QVariant,QVariant,int)));
+ connect(NexusInterface::instance(), SIGNAL(needLogin()), this, SLOT(nexusLogin()));
connect(&TutorialManager::instance(), SIGNAL(windowTutorialFinished(QString)), this, SLOT(windowTutorialFinished(QString)));
connect(ui->toolBar, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(toolBar_customContextMenuRequested(QPoint)));
+ connect(&m_IntegratedBrowser, SIGNAL(requestDownload(QUrl,QNetworkReply*)), this, SLOT(requestDownload(QUrl,QNetworkReply*)));
+
connect(this, SIGNAL(styleChanged(QString)), this, SLOT(updateStyle(QString)));
m_CheckBSATimer.setSingleShot(true);
@@ -309,7 +327,6 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
m_DirectoryRefresher.moveToThread(&m_RefresherThread);
m_RefresherThread.start();
- setCompactDownloads(initSettings.value("compact_downloads", false).toBool());
m_AskForNexusPW = initSettings.value("ask_for_nexuspw", true).toBool();
setCategoryListVisible(initSettings.value("categorylist_visible", true).toBool());
FileDialogMemory::restore(initSettings);
@@ -337,6 +354,7 @@ MainWindow::~MainWindow()
{
m_RefresherThread.exit();
m_RefresherThread.wait();
+ m_IntegratedBrowser.close();
delete ui;
delete m_GameInfo;
delete m_DirectoryStructure;
@@ -514,14 +532,26 @@ void MainWindow::updateToolBar()
void MainWindow::updateProblemsButton()
{
- if (checkForProblems()) {
+ int numProblems = checkForProblems();
+ if (numProblems > 0) {
ui->actionProblems->setEnabled(true);
ui->actionProblems->setIconText(tr("Problems"));
ui->actionProblems->setToolTip(tr("There are potential problems with your setup"));
+
+// QPixmap mergedIcon(64, 64);
+ QPixmap mergedIcon = QPixmap(":/MO/gui/warning").scaled(64, 64);
+ {
+ QPainter painter(&mergedIcon);
+// painter.setBrush(QBrush(Qt::transparent));
+ std::string badgeName = std::string(":/MO/gui/badge_") + (numProblems < 10 ? std::to_string(static_cast<long long>(numProblems)) : "more");
+ painter.drawPixmap(32, 32, 32, 32, QPixmap(badgeName.c_str()));
+ }
+ ui->actionProblems->setIcon(QIcon(mergedIcon));
} else {
ui->actionProblems->setEnabled(false);
ui->actionProblems->setIconText(tr("No Problems"));
ui->actionProblems->setToolTip(tr("Everything seems to be in order"));
+ ui->actionProblems->setIcon(QIcon(":/MO/gui/warning"));
}
}
@@ -556,15 +586,13 @@ bool MainWindow::errorReported(QString &logFile)
}
-bool MainWindow::checkForProblems()
+int MainWindow::checkForProblems()
{
foreach (IPluginDiagnose *diagnose, m_DiagnosisPlugins) {
std::vector<unsigned int> activeProblems = diagnose->activeProblems();
- if (activeProblems.size() > 0) {
- return true;
- }
+ return activeProblems.size();
}
- return false;
+ return 0;
}
void MainWindow::about()
@@ -648,8 +676,9 @@ void MainWindow::saveArchiveList()
}
}
}
- archiveFile.commit();
- qDebug("%s saved", qPrintable(QDir::toNativeSeparators(m_CurrentProfile->getArchivesFileName())));
+ if (archiveFile.commitIfDifferent(m_ArchiveListHash)) {
+ qDebug("%s saved", qPrintable(QDir::toNativeSeparators(m_CurrentProfile->getArchivesFileName())));
+ }
} else {
qWarning("archive list not initialised");
}
@@ -665,10 +694,12 @@ void MainWindow::savePluginList()
m_PluginList.saveLoadOrder(*m_DirectoryStructure);
}
-void MainWindow::modFilterActive(bool active)
+void MainWindow::modFilterActive(bool filterActive)
{
- if (active) {
+ if (filterActive) {
ui->modList->setStyleSheet("QTreeView { border: 2px ridge #f00; }");
+ } else if (ui->groupCombo->currentIndex() != 0) {
+ ui->modList->setStyleSheet("QTreeView { border: 2px ridge #337733; }");
} else {
ui->modList->setStyleSheet("");
}
@@ -824,6 +855,8 @@ void MainWindow::closeEvent(QCloseEvent* event)
setCursor(Qt::WaitCursor);
+ m_IntegratedBrowser.close();
+
storeSettings();
// profile has to be cleaned up before the modinfo-buffer is cleared
@@ -846,6 +879,12 @@ void MainWindow::createFirstProfile()
}
+void MainWindow::setBrowserGeometry(const QByteArray &geometry)
+{
+ m_IntegratedBrowser.restoreGeometry(geometry);
+}
+
+
SaveGameGamebryo *MainWindow::getSaveGame(const QString &name)
{
return new SaveGameGamebryo(this, name);
@@ -968,7 +1007,7 @@ bool MainWindow::verifyPlugin(IPlugin *plugin)
{
if (plugin == NULL) {
return false;
- } else if (!plugin->init(this)) {
+ } else if (!plugin->init(new OrganizerProxy(this, plugin->name()))) {
qWarning("plugin failed to initialize");
return false;
}
@@ -990,6 +1029,50 @@ void MainWindow::toolPluginInvoke()
}
+void MainWindow::requestDownload(const QUrl &url, QNetworkReply *reply)
+{
+ QToolButton *browserBtn = qobject_cast<QToolButton*>(ui->toolBar->widgetForAction(ui->actionNexus));
+
+ QList<QAction*> browserActions = browserBtn->menu()->actions();
+ foreach (QAction *action, browserActions) {
+ // the nexus action doesn't have a plugin connected currently
+ if (action->data().isValid()) {
+ IPluginModPage *plugin = qobject_cast<IPluginModPage*>(qvariant_cast<QObject*>(action->data()));
+ if (plugin == NULL) {
+ qCritical("invalid mod page. This is a bug");
+ continue;
+ }
+ ModRepositoryFileInfo *fileInfo = new ModRepositoryFileInfo();
+ if (plugin->handlesDownload(url, reply->url(), *fileInfo)) {
+ fileInfo->repository = plugin->name();
+ m_DownloadManager.addDownload(reply, fileInfo);
+ return;
+ }
+ }
+ }
+
+ if (QMessageBox::question(this, tr("Download?"),
+ tr("A download has been started but no installed page plugin recognizes it.\n"
+ "If you download anyway no information (i.e. version) will be associated with the download.\n"
+ "Continue?"),
+ QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
+ m_DownloadManager.addDownload(reply, new ModRepositoryFileInfo());
+ }
+}
+
+
+void MainWindow::modPagePluginInvoke()
+{
+ QAction *triggeredAction = qobject_cast<QAction*>(sender());
+ IPluginModPage *plugin = qobject_cast<IPluginModPage*>(triggeredAction->data().value<QObject*>());
+ if (plugin->useIntegratedBrowser()) {
+ m_IntegratedBrowser.setWindowTitle(plugin->displayName());
+ m_IntegratedBrowser.openUrl(plugin->pageURL());
+ } else {
+ ::ShellExecuteW(NULL, L"open", ToWString(plugin->pageURL().toString()).c_str(), NULL, NULL, SW_SHOWNORMAL);
+ }
+}
+
void MainWindow::registerPluginTool(IPluginTool *tool)
{
QAction *action = new QAction(tool->icon(), tool->displayName(), ui->toolBar);
@@ -1002,6 +1085,34 @@ void MainWindow::registerPluginTool(IPluginTool *tool)
}
+void MainWindow::registerModPage(IPluginModPage *modPage)
+{
+ QToolButton *browserBtn = NULL;
+ // turn the browser action into a drop-down menu if necessary
+ if (ui->actionNexus->menu() == NULL) {
+ QAction *nexusAction = ui->actionNexus;
+ // TODO: use a different icon for nexus!
+ ui->actionNexus = new QAction(nexusAction->icon(), tr("Browse Mod Page"), ui->toolBar);
+ ui->toolBar->insertAction(nexusAction, ui->actionNexus);
+ ui->toolBar->removeAction(nexusAction);
+ actionToToolButton(ui->actionNexus);
+
+ browserBtn = qobject_cast<QToolButton*>(ui->toolBar->widgetForAction(ui->actionNexus));
+ browserBtn->menu()->addAction(nexusAction);
+ } else {
+ browserBtn = qobject_cast<QToolButton*>(ui->toolBar->widgetForAction(ui->actionNexus));
+ }
+
+ QAction *action = new QAction(modPage->icon(), modPage->displayName(), ui->toolBar);
+ modPage->setParentWidget(this);
+ action->setData(qVariantFromValue(reinterpret_cast<QObject*>(modPage)));
+
+ connect(action, SIGNAL(triggered()), this, SLOT(modPagePluginInvoke()), Qt::QueuedConnection);
+ QToolButton *toolBtn = qobject_cast<QToolButton*>(ui->toolBar->widgetForAction(ui->actionNexus));
+ toolBtn->menu()->addAction(action);
+}
+
+
bool MainWindow::registerPlugin(QObject *plugin, const QString &fileName)
{
{ // generic treatment for all plugins
@@ -1021,6 +1132,13 @@ bool MainWindow::registerPlugin(QObject *plugin, const QString &fileName)
diagnose->onInvalidated([&] () { this->updateProblemsButton(); });
}
}
+ { // mod page plugin
+ IPluginModPage *modPage = qobject_cast<IPluginModPage*>(plugin);
+ if (verifyPlugin(modPage)) {
+ registerModPage(modPage);
+ return true;
+ }
+ }
{ // tool plugins
IPluginTool *tool = qobject_cast<IPluginTool*>(plugin);
if (verifyPlugin(tool)) {
@@ -1147,131 +1265,6 @@ void MainWindow::loadPlugins()
m_DiagnosisPlugins.push_back(this);
}
-IGameInfo &MainWindow::gameInfo() const
-{
- return *m_GameInfo;
-}
-
-
-IModRepositoryBridge *MainWindow::createNexusBridge() const
-{
- return new NexusBridge();
-}
-
-
-QString MainWindow::profileName() const
-{
- if (m_CurrentProfile != NULL) {
- return m_CurrentProfile->getName();
- } else {
- return "";
- }
-}
-
-QString MainWindow::profilePath() const
-{
- if (m_CurrentProfile != NULL) {
- return m_CurrentProfile->getPath();
- } else {
- return "";
- }
-}
-
-QString MainWindow::downloadsPath() const
-{
- return QDir::fromNativeSeparators(m_Settings.getDownloadDirectory());
-}
-
-VersionInfo MainWindow::appVersion() const
-{
- return m_Updater.getVersion();
-}
-
-
-IModInterface *MainWindow::getMod(const QString &name)
-{
- unsigned int index = ModInfo::getIndex(name);
- if (index == UINT_MAX) {
- return NULL;
- } else {
- return ModInfo::getByIndex(index).data();
- }
-}
-
-
-IModInterface *MainWindow::createMod(GuessedValue<QString> &name)
-{
- if (!m_InstallationManager.testOverwrite(name)) {
- return NULL;
- }
-
- m_InstallationManager.setModsDirectory(m_Settings.getModDirectory());
-
-/* QString fixedName = name;
- fixDirectoryName(fixedName);
- unsigned int index = ModInfo::getIndex(fixedName);
- if (index != UINT_MAX) {
- ModInfo::Ptr result = ModInfo::getByIndex(index);
- if (!result->isEmpty()) {
- throw MyException(tr("The mod \"%1\" already exists!").arg(fixedName));
- }
- return result.data();
- } else {*/
- QString targetDirectory = QDir::fromNativeSeparators(m_Settings.getModDirectory()).append("/").append(name);
-
- QSettings settingsFile(targetDirectory.mid(0).append("/meta.ini"), QSettings::IniFormat);
-
- settingsFile.setValue("modid", 0);
- settingsFile.setValue("version", "");
- settingsFile.setValue("newestVersion", "");
- settingsFile.setValue("category", 0);
- settingsFile.setValue("installationFile", "");
- return ModInfo::createFrom(QDir(targetDirectory), &m_DirectoryStructure).data();
-// }
-}
-
-bool MainWindow::removeMod(IModInterface *mod)
-{
- unsigned int index = ModInfo::getIndex(mod->name());
- if (index == UINT_MAX) {
- return mod->remove();
- } else {
- return ModInfo::removeMod(index);
- }
-}
-
-
-void MainWindow::modDataChanged(IModInterface*)
-{
- refreshModList();
-}
-
-QVariant MainWindow::pluginSetting(const QString &pluginName, const QString &key) const
-{
- return m_Settings.pluginSetting(pluginName, key);
-}
-
-void MainWindow::setPluginSetting(const QString &pluginName, const QString &key, const QVariant &value)
-{
- m_Settings.setPluginSetting(pluginName, key, value);
-}
-
-QVariant MainWindow::persistent(const QString &pluginName, const QString &key, const QVariant &def) const
-{
- return m_Settings.pluginPersistent(pluginName, key, def);
-}
-
-void MainWindow::setPersistent(const QString &pluginName, const QString &key, const QVariant &value, bool sync)
-{
- m_Settings.setPluginPersistent(pluginName, key, value, sync);
-}
-
-QString MainWindow::pluginDataPath() const
-{
- QString pluginPath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory())) + "/" + ToQString(AppConfig::pluginPath());
- return pluginPath + "/data";
-}
-
void MainWindow::startSteam()
{
@@ -1318,11 +1311,12 @@ HANDLE MainWindow::spawnBinaryDirect(const QFileInfo &binary, const QString &arg
}
}
- while (m_RefreshProgress->isVisible()) {
+ while (m_DirectoryUpdate) {
::Sleep(100);
QCoreApplication::processEvents();
}
+ // need to make sure all data is saved before we start the application
if (m_CurrentProfile != nullptr) {
m_CurrentProfile->writeModlistNow(true);
}
@@ -1336,28 +1330,6 @@ HANDLE MainWindow::spawnBinaryDirect(const QFileInfo &binary, const QString &arg
}
}
-/*
-void MainWindow::spawnProgram(const QString &fileName, const QString &argumentsArg,
- const QString &profileName, const QDir &currentDirectory)
-{
- QFileInfo binary;
- QString arguments = argumentsArg;
- QString steamAppID;
- try {
- const Executable &exe = m_ExecutablesList.find(fileName);
- steamAppID = exe.m_SteamAppID;
- if (arguments == "") {
- arguments = exe.m_Arguments;
- }
- binary = exe.m_BinaryInfo;
- } catch (const std::runtime_error&) {
- qWarning("\"%s\" not set up as executable", fileName.toUtf8().constData());
- binary = QFileInfo(fileName);
- }
- spawnBinaryDirect(binary, arguments, profileName, currentDirectory, steamAppID);
-}
-*/
-
void MainWindow::spawnBinary(const QFileInfo &binary, const QString &arguments, const QDir &currentDirectory, bool closeAfterStart, const QString &steamAppID)
{
@@ -1411,21 +1383,6 @@ void MainWindow::startExeAction()
}
-void MainWindow::refreshModList(bool saveChanges)
-{
- // don't lose changes!
- if (saveChanges) {
- m_CurrentProfile->writeModlistNow(true);
- }
- ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure);
- m_CurrentProfile->refreshModStatus();
-
- m_ModList.notifyChange(-1);
-
- refreshDirectoryStructure();
-}
-
-
void MainWindow::setExecutablesList(const ExecutablesList &executablesList)
{
m_ExecutablesList = executablesList;
@@ -1668,11 +1625,27 @@ bool MainWindow::refreshProfiles(bool selectProfile)
}
+std::set<QString> MainWindow::managedArchives()
+{
+ std::set<QString> result;
+
+ QFile archiveFile(m_CurrentProfile->getArchivesFileName());
+ if (archiveFile.open(QIODevice::ReadOnly)) {
+ while (!archiveFile.atEnd()) {
+ result.insert(QString::fromUtf8(archiveFile.readLine()).trimmed());
+ }
+ archiveFile.close();
+ }
+ return result;
+}
+
+
void MainWindow::refreshDirectoryStructure()
{
m_DirectoryUpdate = true;
std::vector<std::tuple<QString, QString, int> > activeModList = m_CurrentProfile->getActiveMods();
- m_DirectoryRefresher.setMods(activeModList);
+
+ m_DirectoryRefresher.setMods(activeModList, managedArchives());
statusBar()->show();
m_RefreshProgress->setRange(0, 100);
@@ -1807,6 +1780,20 @@ void MainWindow::refreshESPList()
}
}
+void MainWindow::refreshModList(bool saveChanges)
+{
+ // don't lose changes!
+ if (saveChanges) {
+ m_CurrentProfile->writeModlistNow(true);
+ }
+ ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure);
+ m_CurrentProfile->refreshModStatus();
+
+ m_ModList.notifyChange(-1);
+
+ refreshDirectoryStructure();
+}
+
static bool BySortValue(const std::pair<UINT32, QTreeWidgetItem*> &LHS, const std::pair<UINT32, QTreeWidgetItem*> &RHS)
{
@@ -1814,6 +1801,17 @@ static bool BySortValue(const std::pair<UINT32, QTreeWidgetItem*> &LHS, const st
}
+template <typename InputIterator>
+QStringList toStringList(InputIterator current, InputIterator end)
+{
+ QStringList result;
+ for (; current != end; ++current) {
+ result.append(*current);
+ }
+ return result;
+}
+
+
void MainWindow::refreshBSAList()
{
m_ArchivesInit = false;
@@ -1849,17 +1847,9 @@ void MainWindow::refreshBSAList()
m_ActiveArchives.clear();
- QFile archiveFile(m_CurrentProfile->getArchivesFileName());
- if (archiveFile.open(QIODevice::ReadOnly)) {
- while (!archiveFile.atEnd()) {
- m_ActiveArchives.append(QString::fromUtf8(archiveFile.readLine()));
- }
- archiveFile.close();
-
- for (int i = 0; i < m_ActiveArchives.count(); ++i) {
- m_ActiveArchives[i] = m_ActiveArchives[i].trimmed();
- }
- } else {
+ auto iter = managedArchives();
+ m_ActiveArchives = toStringList(iter.begin(), iter.end());
+ if (m_ActiveArchives.isEmpty()) {
m_ActiveArchives = m_DefaultArchives;
}
@@ -1878,6 +1868,9 @@ void MainWindow::refreshBSAList()
continue;
}
int index = m_ActiveArchives.indexOf(filename);
+ if (index == -1) {
+ index = 0xFFFF;
+ }
QStringList strings(filename);
bool isArchive = false;
int origin = current->getOrigin(isArchive);
@@ -1886,13 +1879,12 @@ void MainWindow::refreshBSAList()
newItem->setData(0, Qt::UserRole, index);
newItem->setData(1, Qt::UserRole, origin);
newItem->setFlags(newItem->flags() & ~Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable);
-// newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable);
newItem->setCheckState(0, (index != -1) ? Qt::Checked : Qt::Unchecked);
if (m_Settings.forceEnableCoreFiles() && m_DefaultArchives.contains(filename)) {
newItem->setCheckState(0, Qt::Checked);
newItem->setDisabled(true);
} else {
- newItem->setCheckState(0, (index != -1) ? Qt::Checked : Qt::Unchecked);
+ newItem->setCheckState(0, (index != 0xFFFF) ? Qt::Checked : Qt::Unchecked);
}
if (index < 0) index = 0;
@@ -1917,7 +1909,6 @@ void MainWindow::refreshBSAList()
ui->bsaList->addTopLevelItem(subItem);
}
subItem->addChild(iter->second);
- //ui->bsaList->addTopLevelItem(iter->second);
}
checkBSAList();
@@ -1945,7 +1936,7 @@ void MainWindow::checkBSAList()
item->setIcon(0, QIcon(":/MO/gui/warning"));
item->setToolTip(0, tr("This bsa is enabled in the ini file so it may be required!"));
modWarning = true;
- } else {
+/* } else {
QString espName = filename.mid(0, filename.length() - 3).append("esp").toLower();
QString esmName = filename.mid(0, filename.length() - 3).append("esm").toLower();
if (m_PluginList.isEnabled(espName) || m_PluginList.isEnabled(esmName)) {
@@ -1953,7 +1944,7 @@ void MainWindow::checkBSAList()
item->setToolTip(0, tr("This archive will still be loaded since there is a plugin of the same name but "
"its files will not follow installation order!"));
modWarning = true;
- }
+ }*/
}
}
}
@@ -2061,51 +2052,74 @@ void MainWindow::storeSettings()
m_CurrentProfile->createTweakedIniFile();
saveCurrentLists();
m_Settings.setupLoadMechanism();
- QSettings settings(ToQString(GameInfo::instance().getIniFilename()), QSettings::IniFormat);
- if (m_CurrentProfile != NULL) {
- settings.setValue("selected_profile", m_CurrentProfile->getName().toUtf8().constData());
- } else {
- settings.remove("selected_profile");
- }
- settings.setValue("mod_list_state", ui->modList->header()->saveState());
- settings.setValue("plugin_list_state", ui->espList->header()->saveState());
+ QString iniFile = ToQString(GameInfo::instance().getIniFilename());
+ shellCopy(iniFile, iniFile + ".new", true, this);
- settings.setValue("group_state", ui->groupCombo->currentIndex());
+ QSettings::Status result = QSettings::NoError;
+ {
+ QSettings settings(iniFile + ".new", QSettings::IniFormat);
+ if (m_CurrentProfile != NULL) {
+ settings.setValue("selected_profile", m_CurrentProfile->getName().toUtf8().constData());
+ } else {
+ settings.remove("selected_profile");
+ }
- settings.setValue("compact_downloads", ui->compactBox->isChecked());
- settings.setValue("ask_for_nexuspw", m_AskForNexusPW);
+ settings.setValue("mod_list_state", ui->modList->header()->saveState());
+ settings.setValue("plugin_list_state", ui->espList->header()->saveState());
- settings.setValue("window_geometry", saveGeometry());
- settings.setValue("window_split", ui->splitter->saveState());
+ settings.setValue("group_state", ui->groupCombo->currentIndex());
- settings.setValue("filters_visible", ui->displayCategoriesBtn->isChecked());
+ settings.setValue("ask_for_nexuspw", m_AskForNexusPW);
- settings.remove("customExecutables");
- settings.beginWriteArray("customExecutables");
- std::vector<Executable>::const_iterator current, end;
- m_ExecutablesList.getExecutables(current, end);
- int count = 0;
- for (; current != end; ++current) {
- const Executable &item = *current;
- if (item.m_Custom || item.m_Toolbar) {
- settings.setArrayIndex(count++);
- settings.setValue("binary", item.m_BinaryInfo.absoluteFilePath());
- settings.setValue("title", item.m_Title);
- settings.setValue("arguments", item.m_Arguments);
- settings.setValue("workingDirectory", item.m_WorkingDirectory);
- settings.setValue("closeOnStart", item.m_CloseMO == DEFAULT_CLOSE);
- settings.setValue("steamAppID", item.m_SteamAppID);
- settings.setValue("custom", item.m_Custom);
- settings.setValue("toolbar", item.m_Toolbar);
+ settings.setValue("window_geometry", saveGeometry());
+ settings.setValue("window_split", ui->splitter->saveState());
+
+ settings.setValue("browser_geometry", m_IntegratedBrowser.saveGeometry());
+
+ settings.setValue("filters_visible", ui->displayCategoriesBtn->isChecked());
+
+ settings.remove("customExecutables");
+ settings.beginWriteArray("customExecutables");
+ std::vector<Executable>::const_iterator current, end;
+ m_ExecutablesList.getExecutables(current, end);
+ int count = 0;
+ for (; current != end; ++current) {
+ const Executable &item = *current;
+ if (item.m_Custom || item.m_Toolbar) {
+ settings.setArrayIndex(count++);
+ settings.setValue("binary", item.m_BinaryInfo.absoluteFilePath());
+ settings.setValue("title", item.m_Title);
+ settings.setValue("arguments", item.m_Arguments);
+ settings.setValue("workingDirectory", item.m_WorkingDirectory);
+ settings.setValue("closeOnStart", item.m_CloseMO == DEFAULT_CLOSE);
+ settings.setValue("steamAppID", item.m_SteamAppID);
+ settings.setValue("custom", item.m_Custom);
+ settings.setValue("toolbar", item.m_Toolbar);
+ }
}
- }
- settings.endArray();
+ settings.endArray();
- QComboBox *executableBox = findChild<QComboBox*>("executablesListBox");
- settings.setValue("selected_executable", executableBox->currentIndex());
+ QComboBox *executableBox = findChild<QComboBox*>("executablesListBox");
+ settings.setValue("selected_executable", executableBox->currentIndex());
- FileDialogMemory::save(m_Settings.directInterface());
+ FileDialogMemory::save(settings);
+
+ settings.sync();
+ result = settings.status();
+ }
+ if (result == QSettings::NoError) {
+ if (!shellRename(iniFile + ".new", iniFile, true, this)) {
+ QMessageBox::critical(this, tr("Failed to write settings"),
+ tr("An error occured trying to write back MO settings: %1").arg(windowsErrorString(::GetLastError())));
+ }
+ } else {
+ QString reason = result == QSettings::AccessError ? tr("File is write protected")
+ : result == QSettings::FormatError ? tr("Invalid file format (probably a bug)")
+ : tr("Unknown error %1").arg(result);
+ QMessageBox::critical(this, tr("Failed to write settings"),
+ tr("An error occured trying to write back MO settings: %1").arg(reason));
+ }
}
@@ -2133,172 +2147,6 @@ void MainWindow::on_tabWidget_currentChanged(int index)
}
}
-
-void MainWindow::installMod(const QString &fileName)
-{
- if (m_CurrentProfile == NULL) {
- return;
- }
-
- bool hasIniTweaks = false;
- GuessedValue<QString> modName;
- m_CurrentProfile->writeModlistNow();
- m_InstallationManager.setModsDirectory(m_Settings.getModDirectory());
- if (m_InstallationManager.install(fileName, modName, hasIniTweaks)) {
- MessageDialog::showMessage(tr("Installation successful"), this);
- refreshModList();
-
- QModelIndexList posList = m_ModList.match(m_ModList.index(0, 0), Qt::DisplayRole, static_cast<const QString&>(modName));
- if (posList.count() == 1) {
- ui->modList->scrollTo(posList.at(0));
- }
- int modIndex = ModInfo::getIndex(modName);
- if (modIndex != UINT_MAX) {
- ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex);
- if (hasIniTweaks &&
- (QMessageBox::question(this, tr("Configure Mod"),
- tr("This mod contains ini tweaks. Do you want to configure them now?"),
- QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) {
- displayModInformation(modInfo, modIndex, ModInfoDialog::TAB_INIFILES);
- }
- testExtractBSA(modIndex);
- } else {
- reportError(tr("mod \"%1\" not found").arg(modName));
- }
- } else if (m_InstallationManager.wasCancelled()) {
- QMessageBox::information(this, tr("Installation cancelled"), tr("The mod was not installed completely."), QMessageBox::Ok);
- }
-}
-
-QString MainWindow::resolvePath(const QString &fileName) const
-{
- if (m_DirectoryStructure == NULL) {
- return QString();
- }
- const FileEntry::Ptr file = m_DirectoryStructure->searchFile(ToWString(fileName), NULL);
- if (file.get() != NULL) {
- return ToQString(file->getFullPath());
- } else {
- return QString();
- }
-}
-
-QStringList MainWindow::listDirectories(const QString &directoryName) const
-{
- QStringList result;
- DirectoryEntry *dir = m_DirectoryStructure->findSubDirectoryRecursive(ToWString(directoryName));
- if (dir != NULL) {
- std::vector<DirectoryEntry*>::iterator current, end;
- dir->getSubDirectories(current, end);
- for (; current != end; ++current) {
- result.append(ToQString((*current)->getName()));
- }
- }
- return result;
-}
-
-QStringList MainWindow::findFiles(const QString &path, const std::function<bool(const QString&)> &filter) const
-{
- QStringList result;
- DirectoryEntry *dir = m_DirectoryStructure->findSubDirectoryRecursive(ToWString(path));
- if (dir != NULL) {
- std::vector<FileEntry::Ptr> files = dir->getFiles();
- foreach (FileEntry::Ptr file, files) {
- if (filter(ToQString(file->getFullPath()))) {
- result.append(ToQString(file->getFullPath()));
- }
- }
- } else {
- qWarning("directory %s not found", qPrintable(path));
- }
- return result;
-}
-
-QList<IOrganizer::FileInfo> MainWindow::findFileInfos(const QString &path, const std::function<bool (const IOrganizer::FileInfo &)> &filter) const
-{
- QList<IOrganizer::FileInfo> result;
- DirectoryEntry *dir = m_DirectoryStructure->findSubDirectoryRecursive(ToWString(path));
- if (dir != NULL) {
- std::vector<FileEntry::Ptr> files = dir->getFiles();
- foreach (FileEntry::Ptr file, files) {
- FileInfo info;
- info.filePath = ToQString(file->getFullPath());
- bool fromArchive = false;
- info.origins.append(ToQString(m_DirectoryStructure->getOriginByID(file->getOrigin(fromArchive)).getName()));
- info.archive = fromArchive ? ToQString(file->getArchive()) : "";
- foreach (int idx, file->getAlternatives()) {
- info.origins.append(ToQString(m_DirectoryStructure->getOriginByID(idx).getName()));
- }
-
- if (filter(info)) {
- result.append(info);
- }
- }
- } else {
- qWarning("directory %s not found", qPrintable(path));
- }
- return result;
-}
-
-IDownloadManager *MainWindow::downloadManager()
-{
- return &m_DownloadManager;
-}
-
-IPluginList *MainWindow::pluginList()
-{
- return &m_PluginList;
-}
-
-IModList *MainWindow::modList()
-{
- return &m_ModList;
-}
-
-HANDLE MainWindow::startApplication(const QString &executable, const QStringList &args, const QString &cwd, const QString &profile)
-{
- QFileInfo binary;
- QString arguments = args.join(" ");
- QString currentDirectory = cwd;
- QString profileName = (profile.length() > 0) ? profile : m_CurrentProfile->getName();
- QString steamAppID;
- if (executable.contains('\\') || executable.contains('/')) {
- // file path
- binary = QFileInfo(executable);
- if (binary.isRelative()) {
- // relative path, should be relative to game directory
- binary = QFileInfo(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())) + "/" + executable);
- }
- if (cwd.length() == 0) {
- currentDirectory = binary.absolutePath();
- }
- } else {
- // only a file name, search executables list
- try {
- const Executable &exe = m_ExecutablesList.find(executable);
- steamAppID = exe.m_SteamAppID;
- if (arguments == "") {
- arguments = exe.m_Arguments;
- }
- binary = exe.m_BinaryInfo;
- if (cwd.length() == 0) {
- currentDirectory = exe.m_WorkingDirectory;
- }
- } catch (const std::runtime_error&) {
- qWarning("\"%s\" not set up as executable", executable.toUtf8().constData());
- binary = QFileInfo(executable);
- }
- }
-
- return spawnBinaryDirect(binary, arguments, profileName, currentDirectory, steamAppID);
-}
-
-bool MainWindow::onAboutToRun(const std::function<bool (const QString &)> &func)
-{
- auto conn = m_AboutToRun.connect(func);
- return conn.connected();
-}
-
std::vector<unsigned int> MainWindow::activeProblems() const
{
std::vector<unsigned int> problems;
@@ -2377,6 +2225,109 @@ void MainWindow::installMod()
}
}
+IModInterface *MainWindow::installMod(const QString &fileName)
+{
+ if (m_CurrentProfile == NULL) {
+ return NULL;
+ }
+
+ bool hasIniTweaks = false;
+ GuessedValue<QString> modName;
+ m_CurrentProfile->writeModlistNow();
+ m_InstallationManager.setModsDirectory(m_Settings.getModDirectory());
+ if (m_InstallationManager.install(fileName, modName, hasIniTweaks)) {
+ MessageDialog::showMessage(tr("Installation successful"), this);
+ refreshModList();
+
+ QModelIndexList posList = m_ModList.match(m_ModList.index(0, 0), Qt::DisplayRole, static_cast<const QString&>(modName));
+ if (posList.count() == 1) {
+ ui->modList->scrollTo(posList.at(0));
+ }
+ int modIndex = ModInfo::getIndex(modName);
+ if (modIndex != UINT_MAX) {
+ ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex);
+ if (hasIniTweaks &&
+ (QMessageBox::question(this, tr("Configure Mod"),
+ tr("This mod contains ini tweaks. Do you want to configure them now?"),
+ QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) {
+ displayModInformation(modInfo, modIndex, ModInfoDialog::TAB_INIFILES);
+ }
+ m_ModInstalled(modName);
+ return modInfo.data();
+ } else {
+ reportError(tr("mod \"%1\" not found").arg(modName));
+ }
+ } else if (m_InstallationManager.wasCancelled()) {
+ QMessageBox::information(this, tr("Installation cancelled"), tr("The mod was not installed completely."), QMessageBox::Ok);
+ }
+ return NULL;
+}
+
+IModInterface *MainWindow::getMod(const QString &name)
+{
+ unsigned int index = ModInfo::getIndex(name);
+ if (index == UINT_MAX) {
+ return NULL;
+ } else {
+ return ModInfo::getByIndex(index).data();
+ }
+}
+
+IModInterface *MainWindow::createMod(GuessedValue<QString> &name)
+{
+ if (!m_InstallationManager.testOverwrite(name)) {
+ return NULL;
+ }
+
+ m_InstallationManager.setModsDirectory(m_Settings.getModDirectory());
+
+ QString targetDirectory = QDir::fromNativeSeparators(m_Settings.getModDirectory()).append("/").append(name);
+
+ QSettings settingsFile(targetDirectory.mid(0).append("/meta.ini"), QSettings::IniFormat);
+
+ settingsFile.setValue("modid", 0);
+ settingsFile.setValue("version", "");
+ settingsFile.setValue("newestVersion", "");
+ settingsFile.setValue("category", 0);
+ settingsFile.setValue("installationFile", "");
+ return ModInfo::createFrom(QDir(targetDirectory), &m_DirectoryStructure).data();
+}
+
+bool MainWindow::removeMod(IModInterface *mod)
+{
+ unsigned int index = ModInfo::getIndex(mod->name());
+ if (index == UINT_MAX) {
+ return mod->remove();
+ } else {
+ return ModInfo::removeMod(index);
+ }
+}
+
+QList<IOrganizer::FileInfo> MainWindow::findFileInfos(const QString &path, const std::function<bool (const IOrganizer::FileInfo &)> &filter) const
+{
+ QList<IOrganizer::FileInfo> result;
+ DirectoryEntry *dir = m_DirectoryStructure->findSubDirectoryRecursive(ToWString(path));
+ if (dir != NULL) {
+ std::vector<FileEntry::Ptr> files = dir->getFiles();
+ foreach (FileEntry::Ptr file, files) {
+ IOrganizer::FileInfo info;
+ info.filePath = ToQString(file->getFullPath());
+ bool fromArchive = false;
+ info.origins.append(ToQString(m_DirectoryStructure->getOriginByID(file->getOrigin(fromArchive)).getName()));
+ info.archive = fromArchive ? ToQString(file->getArchive()) : "";
+ foreach (int idx, file->getAlternatives()) {
+ info.origins.append(ToQString(m_DirectoryStructure->getOriginByID(idx).getName()));
+ }
+
+ if (filter(info)) {
+ result.append(info);
+ }
+ }
+ } else {
+ qDebug("directory %s not found", qPrintable(path));
+ }
+ return result;
+}
void MainWindow::on_startButton_clicked()
{
@@ -2580,12 +2531,6 @@ void MainWindow::setESPListSorting(int index)
}
-void MainWindow::setCompactDownloads(bool compact)
-{
- ui->compactBox->setChecked(compact);
-}
-
-
bool MainWindow::queryLogin(QString &username, QString &password)
{
CredentialsDialog dialog(this);
@@ -2640,16 +2585,12 @@ void MainWindow::refresher_progress(int percent)
void MainWindow::directory_refreshed()
{
- statusBar()->hide();
-
DirectoryEntry *newStructure = m_DirectoryRefresher.getDirectoryStructure();
+ Q_ASSERT(newStructure != m_DirectoryStructure);
if (newStructure != NULL) {
- DirectoryEntry *oldStructure = m_DirectoryStructure;
- m_DirectoryStructure = newStructure;
- delete oldStructure;
-
+ std::swap(m_DirectoryStructure, newStructure);
+ delete newStructure;
refreshDataTree();
- refreshLists();
} else {
// TODO: don't know why this happens, this slot seems to get called twice with only one emit
return;
@@ -2668,6 +2609,7 @@ void MainWindow::directory_refreshed()
ModInfo::Ptr modInfo = ModInfo::getByIndex(i);
modInfo->clearCaches();
}
+ statusBar()->hide();
}
@@ -2685,7 +2627,10 @@ void MainWindow::modStatusChanged(unsigned int index)
try {
ModInfo::Ptr modInfo = ModInfo::getByIndex(index);
if (m_CurrentProfile->modEnabled(index)) {
- DirectoryRefresher::addModToStructure(m_DirectoryStructure, modInfo->name(), m_CurrentProfile->getModPriority(index), modInfo->absolutePath());
+ m_DirectoryRefresher.addModToStructure(m_DirectoryStructure
+ , modInfo->name()
+ , m_CurrentProfile->getModPriority(index)
+ , modInfo->absolutePath());
DirectoryRefresher::cleanStructure(m_DirectoryStructure);
} else {
if (m_DirectoryStructure->originExists(ToWString(modInfo->name()))) {
@@ -2744,11 +2689,10 @@ void MainWindow::procFinished(int, QProcess::ExitStatus)
}
-void MainWindow::on_profileRefreshBtn_clicked()
+void MainWindow::profileRefresh()
{
m_CurrentProfile->writeModlist();
-// m_ModList.updateModCollection();
refreshModList();
}
@@ -3103,6 +3047,22 @@ void MainWindow::reinstallMod_clicked()
}
+void MainWindow::resumeDownload(int downloadIndex)
+{
+ if (NexusInterface::instance()->getAccessManager()->loggedIn()) {
+ m_DownloadManager.resumeDownload(downloadIndex);
+ } else {
+ QString username, password;
+ if (m_Settings.getNexusLogin(username, password)) {
+ m_PostLoginTasks.push_back(boost::bind(&MainWindow::resumeDownload, _1, downloadIndex));
+ NexusInterface::instance()->getAccessManager()->login(username, password);
+ } else {
+ MessageDialog::showMessage(tr("You need to be logged in with Nexus to resume a download"), this);
+ }
+ }
+}
+
+
void MainWindow::endorseMod(ModInfo::Ptr mod)
{
if (NexusInterface::instance()->getAccessManager()->loggedIn()) {
@@ -3199,9 +3159,9 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index,
FilesOrigin& origin = m_DirectoryStructure->getOriginByName(ToWString(modInfo->name()));
origin.enable(false);
- DirectoryRefresher::addModToStructure(m_DirectoryStructure,
- modInfo->name(), m_CurrentProfile->getModPriority(index),
- modInfo->absolutePath());
+ m_DirectoryRefresher.addModToStructure(m_DirectoryStructure,
+ modInfo->name(), m_CurrentProfile->getModPriority(index),
+ modInfo->absolutePath());
DirectoryRefresher::cleanStructure(m_DirectoryStructure);
refreshLists();
}
@@ -3266,54 +3226,6 @@ void MainWindow::displayModInformation(int row, int tab)
}
-void MainWindow::testExtractBSA(int modIndex)
-{
- ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex);
- QDir dir(modInfo->absolutePath());
-
- QFileInfoList archives = dir.entryInfoList(QStringList("*.bsa"));
- if (archives.length() != 0 &&
- (QuestionBoxMemory::query(this, "unpackBSA", tr("Extract BSA"),
- tr("This mod contains at least one BSA. Do you want to unpack it?\n"
- "(This removes the BSA after completion. If you don't know about BSAs, just select no)"),
- QDialogButtonBox::Yes | QDialogButtonBox::No, QDialogButtonBox::No) == QMessageBox::Yes)) {
-
-
- foreach (QFileInfo archiveInfo, archives) {
- BSA::Archive archive;
- BSA::EErrorCode result = archive.read(archiveInfo.absoluteFilePath().toLocal8Bit().constData());
- if ((result != BSA::ERROR_NONE) && (result != BSA::ERROR_INVALIDHASHES)) {
- reportError(tr("failed to read %1: %2").arg(archiveInfo.fileName()).arg(result));
- return;
- }
-
- QProgressDialog progress(this);
- progress.setMaximum(100);
- progress.setValue(0);
- progress.show();
-
- archive.extractAll(modInfo->absolutePath().toLocal8Bit().constData(),
- boost::bind(&MainWindow::extractProgress, this, boost::ref(progress), _1, _2),
- false);
-
- if (result == BSA::ERROR_INVALIDHASHES) {
- reportError(tr("This archive contains invalid hashes. Some files may be broken."));
- }
-
- archive.close();
-
- if (!QFile::remove(archiveInfo.absoluteFilePath())) {
- qCritical("failed to remove archive %s", archiveInfo.absoluteFilePath().toUtf8().constData());
- } else {
- m_DirectoryStructure->removeFile(ToWString(archiveInfo.fileName()));
- }
- }
-
- refreshBSAList();
- }
-}
-
-
void MainWindow::ignoreMissingData_clicked()
{
ModInfo::Ptr info = ModInfo::getByIndex(m_ContextRow);
@@ -3478,13 +3390,13 @@ void MainWindow::replaceCategoriesFromMenu(QMenu *menu, int modRow)
}
}
-void MainWindow::addRemoveCategoriesFromMenu(QMenu *menu, int modRow)
+void MainWindow::addRemoveCategoriesFromMenu(QMenu *menu, int modRow, int referenceRow)
{
- if (m_ContextRow != -1 && m_ContextRow != modRow) {
- ModInfo::Ptr editedModInfo = ModInfo::getByIndex(m_ContextRow);
+ if (referenceRow != -1 && referenceRow != modRow) {
+ ModInfo::Ptr editedModInfo = ModInfo::getByIndex(referenceRow);
foreach (QAction* action, menu->actions()) {
if (action->menu() != NULL) {
- addRemoveCategoriesFromMenu(action->menu(), modRow);
+ addRemoveCategoriesFromMenu(action->menu(), modRow, referenceRow);
} else {
QWidgetAction *widgetAction = qobject_cast<QWidgetAction*>(action);
if (widgetAction != NULL) {
@@ -3501,7 +3413,6 @@ void MainWindow::addRemoveCategoriesFromMenu(QMenu *menu, int modRow)
}
}
} else {
- //This block shouldn't be reached, but if it is then fall back to replace (context row is invalid or replacing edited mod)
replaceCategoriesFromMenu(menu, modRow);
}
}
@@ -3513,37 +3424,26 @@ void MainWindow::addRemoveCategories_MenuHandler() {
return;
}
- QModelIndexList selected = ui->modList->selectionModel()->selectedRows();
+ QModelIndexList selectedTemp = ui->modList->selectionModel()->selectedRows();
+ QList<QPersistentModelIndex> selected;
+ foreach (const QModelIndex &idx, selectedTemp) {
+ selected.append(QPersistentModelIndex(idx));
+ }
if (selected.size() > 0) {
- int min = INT_MAX;
- int max = INT_MIN;
-
- QStringList selectedMods;
- for (int i = 0; i < selected.size(); ++i) {
- QModelIndex temp = mapToModel(&m_ModList, selected.at(i));
- selectedMods.append(temp.data().toString());
- if (temp.row() < min) min = temp.row();
- if (temp.row() > max) max = temp.row();
- // save the currently selected mod for last... then we can use it as a pattern for what is changing...
- int modRow = m_ModListSortProxy->mapToSource(selected.at(i)).row();
- if (modRow != m_ContextRow) {
- addRemoveCategoriesFromMenu(menu,modRow);
+ foreach (const QPersistentModelIndex &idx, selected) {
+ qDebug("change categories on: %s (ref: %s)", qPrintable(idx.data().toString()), qPrintable(m_ContextIdx.data().toString()));
+ QModelIndex modIdx = mapToModel(&m_ModList, idx);
+ if (modIdx.row() != m_ContextIdx.row()) {
+ addRemoveCategoriesFromMenu(menu, modIdx.row(), m_ContextIdx.row());
}
}
- //come back to the currently selected mod, after the others have been set
- replaceCategoriesFromMenu(menu, m_ContextRow);
+ replaceCategoriesFromMenu(menu, m_ContextIdx.row());
m_ModList.notifyChange(-1);
- // find mods by their name because indices are invalidated
- QAbstractItemModel *model = ui->modList->model();
- Q_FOREACH(const QString &mod, selectedMods) {
- QModelIndexList matches = model->match(model->index(0, 0), Qt::DisplayRole, mod, 1,
- Qt::MatchFixedString | Qt::MatchCaseSensitive | Qt::MatchRecursive);
- if (matches.size() > 0) {
- ui->modList->selectionModel()->select(matches.at(0), QItemSelectionModel::Select | QItemSelectionModel::Rows);
- }
+ foreach (const QPersistentModelIndex &idx, selected) {
+ ui->modList->selectionModel()->select(idx, QItemSelectionModel::Select | QItemSelectionModel::Rows);
}
} else {
//For single mod selections, just do a replace
@@ -3564,15 +3464,10 @@ void MainWindow::replaceCategories_MenuHandler() {
QModelIndexList selected = ui->modList->selectionModel()->selectedRows();
if (selected.size() > 0) {
- int min = INT_MAX;
- int max = INT_MIN;
-
QStringList selectedMods;
for (int i = 0; i < selected.size(); ++i) {
QModelIndex temp = mapToModel(&m_ModList, selected.at(i));
selectedMods.append(temp.data().toString());
- if (temp.row() < min) min = temp.row();
- if (temp.row() > max) max = temp.row();
replaceCategoriesFromMenu(menu, mapToModel(&m_ModList, selected.at(i)).row());
}
@@ -3789,102 +3684,113 @@ void addMenuAsPushButton(QMenu *menu, QMenu *subMenu)
menu->addAction(action);
}
-void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos)
+QMenu *MainWindow::modListContextMenu()
{
- try {
- QTreeView *modList = findChild<QTreeView*>("modList");
+ QMenu *menu = new QMenu();
+ menu->addAction(tr("Install Mod..."), this, SLOT(installMod_clicked()));
- QModelIndex index = mapToModel(&m_ModList, modList->indexAt(pos));
- m_ContextRow = index.row();
+ menu->addAction(tr("Enable all visible"), this, SLOT(enableVisibleMods()));
+ menu->addAction(tr("Disable all visible"), this, SLOT(disableVisibleMods()));
- QMenu menu;
+ menu->addAction(tr("Check all for update"), this, SLOT(checkModsForUpdates()));
- menu.addAction(tr("Install Mod..."), this, SLOT(installMod_clicked()));
+ menu->addAction(tr("Refresh"), this, SLOT(profileRefresh()));
- menu.addAction(tr("Enable all visible"), this, SLOT(enableVisibleMods()));
- menu.addAction(tr("Disable all visible"), this, SLOT(disableVisibleMods()));
-
- menu.addAction(tr("Check all for update"), this, SLOT(checkModsForUpdates()));
+ menu->addAction(tr("Export to csv..."), this, SLOT(exportModListCSV()));
+ return menu;
+}
- menu.addAction(tr("Refresh"), this, SLOT(on_profileRefreshBtn_clicked()));
+void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos)
+{
+ try {
+ QTreeView *modList = findChild<QTreeView*>("modList");
- menu.addAction(tr("Export to csv..."), this, SLOT(exportModListCSV()));
+ m_ContextIdx = mapToModel(&m_ModList, modList->indexAt(pos));
+ m_ContextRow = m_ContextIdx.row();
- if (m_ContextRow != -1) {
- menu.addSeparator();
+ QMenu *menu = NULL;
+ QMenu *allMods = modListContextMenu();
+ if (m_ContextRow == -1) {
+ // no selection
+ menu = allMods;
+ menu->setParent(this);
+ } else {
+ menu = new QMenu(this);
+ allMods->setTitle(tr("All Mods"));
+ menu->addMenu(allMods);
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()) {
if (QDir(info->absolutePath()).count() > 2) {
- menu.addAction(tr("Sync to Mods..."), this, SLOT(syncOverwrite()));
- menu.addAction(tr("Create Mod..."), this, SLOT(createModFromOverwrite()));
+ menu->addAction(tr("Sync to Mods..."), this, SLOT(syncOverwrite()));
+ menu->addAction(tr("Create Mod..."), this, SLOT(createModFromOverwrite()));
}
} 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()));
+ menu->addAction(tr("Restore Backup"), this, SLOT(restoreBackup_clicked()));
+ menu->addAction(tr("Remove Backup..."), this, SLOT(removeMod_clicked()));
} else {
QMenu *addRemoveCategoriesMenu = new QMenu(tr("Add/Remove Categories"));
populateMenuCategories(addRemoveCategoriesMenu, 0);
connect(addRemoveCategoriesMenu, SIGNAL(aboutToHide()), this, SLOT(addRemoveCategories_MenuHandler()));
- addMenuAsPushButton(&menu, addRemoveCategoriesMenu);
+ addMenuAsPushButton(menu, addRemoveCategoriesMenu);
QMenu *replaceCategoriesMenu = new QMenu(tr("Replace Categories"));
populateMenuCategories(replaceCategoriesMenu, 0);
connect(replaceCategoriesMenu, SIGNAL(aboutToHide()), this, SLOT(replaceCategories_MenuHandler()));
- addMenuAsPushButton(&menu, replaceCategoriesMenu);
+ addMenuAsPushButton(menu, replaceCategoriesMenu);
QMenu *primaryCategoryMenu = new QMenu(tr("Primary Category"));
connect(primaryCategoryMenu, SIGNAL(aboutToShow()), this, SLOT(addPrimaryCategoryCandidates()));
connect(primaryCategoryMenu, SIGNAL(aboutToHide()), this, SLOT(savePrimaryCategory()));
- addMenuAsPushButton(&menu, primaryCategoryMenu);
+ addMenuAsPushButton(menu, primaryCategoryMenu);
- menu.addSeparator();
+ menu->addSeparator();
if (info->downgradeAvailable()) {
- menu.addAction(tr("Change versioning scheme"), this, SLOT(changeVersioningScheme()));
+ 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()));
+ menu->addAction(tr("Un-ignore update"), this, SLOT(unignoreUpdate()));
} else {
- menu.addAction(tr("Ignore update"), this, SLOT(ignoreUpdate()));
+ menu->addAction(tr("Ignore update"), this, SLOT(ignoreUpdate()));
}
}
- menu.addSeparator();
+ menu->addSeparator();
- menu.addAction(tr("Rename Mod..."), this, SLOT(renameMod_clicked()));
- menu.addAction(tr("Remove Mod..."), this, SLOT(removeMod_clicked()));
- menu.addAction(tr("Reinstall Mod"), this, SLOT(reinstallMod_clicked()));
+ menu->addAction(tr("Rename Mod..."), this, SLOT(renameMod_clicked()));
+ menu->addAction(tr("Remove Mod..."), this, SLOT(removeMod_clicked()));
+ menu->addAction(tr("Reinstall Mod"), this, SLOT(reinstallMod_clicked()));
switch (info->endorsedState()) {
case ModInfo::ENDORSED_TRUE: {
- menu.addAction(tr("Un-Endorse"), this, SLOT(unendorse_clicked()));
+ menu->addAction(tr("Un-Endorse"), this, SLOT(unendorse_clicked()));
} break;
case ModInfo::ENDORSED_FALSE: {
- menu.addAction(tr("Endorse"), this, SLOT(endorse_clicked()));
- menu.addAction(tr("Won't endorse"), this, SLOT(dontendorse_clicked()));
+ menu->addAction(tr("Endorse"), this, SLOT(endorse_clicked()));
+ menu->addAction(tr("Won't endorse"), this, SLOT(dontendorse_clicked()));
} break;
case ModInfo::ENDORSED_NEVER: {
- menu.addAction(tr("Endorse"), this, SLOT(endorse_clicked()));
+ menu->addAction(tr("Endorse"), this, SLOT(endorse_clicked()));
} break;
default: {
- QAction *action = new QAction(tr("Endorsement state unknown"), &menu);
+ QAction *action = new QAction(tr("Endorsement state unknown"), menu);
action->setEnabled(false);
- menu.addAction(action);
+ menu->addAction(action);
} break;
}
std::vector<ModInfo::EFlag> flags = info->getFlags();
if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_INVALID) != flags.end()) {
- menu.addAction(tr("Ignore missing data"), this, SLOT(ignoreMissingData_clicked()));
+ menu->addAction(tr("Ignore missing data"), this, SLOT(ignoreMissingData_clicked()));
}
- menu.addAction(tr("Visit on Nexus"), this, SLOT(visitOnNexus_clicked()));
- menu.addAction(tr("Open in explorer"), this, SLOT(openExplorer_clicked()));
+ menu->addAction(tr("Visit on Nexus"), this, SLOT(visitOnNexus_clicked()));
+ menu->addAction(tr("Open in explorer"), this, SLOT(openExplorer_clicked()));
}
- QAction *infoAction = menu.addAction(tr("Information..."), this, SLOT(information_clicked()));
- menu.setDefaultAction(infoAction);
+ QAction *infoAction = menu->addAction(tr("Information..."), this, SLOT(information_clicked()));
+ menu->setDefaultAction(infoAction);
}
- menu.exec(modList->mapToGlobal(pos));
+ menu->exec(modList->mapToGlobal(pos));
} catch (const std::exception &e) {
reportError(tr("Exception: ").arg(e.what()));
} catch (...) {
@@ -3895,7 +3801,6 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos)
void MainWindow::on_categoriesList_itemSelectionChanged()
{
-// int filter = current->data(0, Qt::UserRole).toInt();
QModelIndexList indices = ui->categoriesList->selectionModel()->selectedRows();
std::vector<int> categories;
foreach (const QModelIndex &index, indices) {
@@ -3906,7 +3811,7 @@ void MainWindow::on_categoriesList_itemSelectionChanged()
}
m_ModListSortProxy->setCategoryFilter(categories);
-// ui->currentCategoryLabel->setText(QString("(%1)").arg(current->text(0)));
+ ui->clickBlankLabel->setEnabled(categories.size() > 0);
if (indices.count() == 0) {
ui->currentCategoryLabel->setText(QString("(%1)").arg(tr("<All>")));
} else if (indices.count() > 1) {
@@ -4066,16 +3971,18 @@ void MainWindow::linkDesktop()
reportError(tr("failed to remove %1").arg(linkName));
}
} else {
- QFileInfo exeInfo(m_ExeName);
+ QFileInfo exeInfo(qApp->arguments().at(0));
// create link
std::wstring targetFile = ToWString(exeInfo.absoluteFilePath());
- std::wstring parameter = ToWString(QString("\"") + selectedExecutable.m_BinaryInfo.absoluteFilePath() + "\" " + selectedExecutable.m_Arguments);
+ std::wstring parameter = ToWString(QString("\"%1\" %2").arg(QDir::toNativeSeparators(selectedExecutable.m_BinaryInfo.absoluteFilePath()))
+ .arg(selectedExecutable.m_Arguments));
std::wstring description = ToWString(selectedExecutable.m_BinaryInfo.fileName());
- std::wstring currentDirectory = ToWString(selectedExecutable.m_BinaryInfo.absolutePath());
-
- if (CreateShortcut(targetFile.c_str(), parameter.c_str(),
- linkName.toUtf8().constData(),
- description.c_str(), currentDirectory.c_str()) != E_INVALIDARG) {
+ std::wstring currentDirectory = ToWString(QDir::toNativeSeparators(exeInfo.absolutePath()));
+ if (CreateShortcut(targetFile.c_str()
+ , parameter.c_str()
+ , linkName.toUtf8().constData()
+ , description.c_str()
+ , currentDirectory.c_str()) != E_INVALIDARG) {
ui->linkButton->menu()->actions().at(0)->setIcon(QIcon(":/MO/gui/remove"));
} else {
reportError(tr("failed to create %1").arg(linkName));
@@ -4097,12 +4004,13 @@ void MainWindow::linkMenu()
reportError(tr("failed to remove %1").arg(linkName));
}
} else {
- QFileInfo exeInfo(m_ExeName);
+ QFileInfo exeInfo(qApp->arguments().at(0));
// create link
std::wstring targetFile = ToWString(exeInfo.absoluteFilePath());
- std::wstring parameter = ToWString(QString("\"") + selectedExecutable.m_BinaryInfo.absoluteFilePath() + "\" " + selectedExecutable.m_Arguments);
+ std::wstring parameter = ToWString(QString("\"%1\" %2").arg(QDir::toNativeSeparators(selectedExecutable.m_BinaryInfo.absoluteFilePath()))
+ .arg(selectedExecutable.m_Arguments));
std::wstring description = ToWString(selectedExecutable.m_BinaryInfo.fileName());
- std::wstring currentDirectory = ToWString(selectedExecutable.m_BinaryInfo.absolutePath());
+ std::wstring currentDirectory = ToWString(QDir::toNativeSeparators(exeInfo.absolutePath()));
if (CreateShortcut(targetFile.c_str(), parameter.c_str(),
linkName.toUtf8().constData(),
@@ -4153,6 +4061,8 @@ void MainWindow::on_actionSettings_triggered()
}
NexusInterface::instance()->setNMMVersion(m_Settings.getNMMVersion());
+
+ updateDownloadListDelegate();
}
@@ -4175,17 +4085,30 @@ void MainWindow::linkClicked(const QString &url)
}
-void MainWindow::downloadRequestedNXM(const QString &url)
+bool MainWindow::nexusLogin()
{
QString username, password;
- qDebug("download requested: %s", qPrintable(url));
- if (!m_LoginAttempted && !NexusInterface::instance()->getAccessManager()->loggedIn() &&
- (m_Settings.getNexusLogin(username, password) ||
- (m_AskForNexusPW && queryLogin(username, password)))) {
+ NXMAccessManager *accessManager = NexusInterface::instance()->getAccessManager();
+
+ if (!accessManager->loginAttempted()
+ && !accessManager->loggedIn()
+ && (m_Settings.getNexusLogin(username, password)
+ || (m_AskForNexusPW
+ && queryLogin(username, password)))) {
+ accessManager->login(username, password);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+void MainWindow::downloadRequestedNXM(const QString &url)
+{
+ qDebug("download requested: %s", qPrintable(url));
+ if (nexusLogin()) {
m_PendingDownloads.append(url);
- NexusInterface::instance()->getAccessManager()->login(username, password);
- m_LoginAttempted = true;
} else {
m_DownloadManager.addNXMDownload(url);
}
@@ -4195,7 +4118,7 @@ void MainWindow::downloadRequestedNXM(const QString &url)
void MainWindow::downloadRequested(QNetworkReply *reply, int modID, const QString &fileName)
{
try {
- if (m_DownloadManager.addDownload(reply, QStringList(), fileName, modID)) {
+ if (m_DownloadManager.addDownload(reply, QStringList(), fileName, modID, 0, new ModRepositoryFileInfo(modID))) {
MessageDialog::showMessage(tr("Download started"), this);
}
} catch (const std::exception &e) {
@@ -4210,7 +4133,9 @@ void MainWindow::installTranslator(const QString &name)
QTranslator *translator = new QTranslator(this);
QString fileName = name + "_" + m_CurrentLanguage;
if (!translator->load(fileName, qApp->applicationDirPath() + "/translations")) {
- qWarning("localization file %s not found", qPrintable(fileName));
+ if (m_CurrentLanguage != "en-US") {
+ qWarning("localization file %s not found", qPrintable(fileName));
+ } // we don't actually expect localization files for english
}
qApp->installTranslator(translator);
m_Translators.push_back(translator);
@@ -4291,7 +4216,8 @@ void MainWindow::installDownload(int index)
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) {
displayModInformation(modInfo, modIndex, ModInfoDialog::TAB_INIFILES);
}
- testExtractBSA(modIndex);
+
+ m_ModInstalled(modName);
} else {
reportError(tr("mod \"%1\" not found").arg(modName));
}
@@ -4642,13 +4568,7 @@ void MainWindow::on_conflictsCheckBox_toggled(bool)
void MainWindow::on_actionUpdate_triggered()
{
- QString username, password;
-
- if (!m_LoginAttempted && !NexusInterface::instance()->getAccessManager()->loggedIn() &&
- (m_Settings.getNexusLogin(username, password) ||
- (m_AskForNexusPW && queryLogin(username, password)))) {
- NexusInterface::instance()->getAccessManager()->login(username, password);
- m_LoginAttempted = true;
+ if (nexusLogin()) {
m_PostLoginTasks.push_back([&](MainWindow*) { m_Updater.startUpdate(); });
} else {
m_Updater.startUpdate();
@@ -4661,17 +4581,19 @@ void MainWindow::on_actionEndorseMO_triggered()
if (QMessageBox::question(this, tr("Endorse Mod Organizer"),
tr("Do you want to endorse Mod Organizer on %1 now?").arg(ToQString(GameInfo::instance().getNexusPage())),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
- NexusInterface::instance()->requestToggleEndorsement(GameInfo::instance().getNexusModID(), true, this, QVariant());
+ NexusInterface::instance()->requestToggleEndorsement(GameInfo::instance().getNexusModID(), true, this, QVariant(), QString());
}
}
void MainWindow::updateDownloadListDelegate()
{
- if (ui->compactBox->isChecked()) {
- ui->downloadView->setItemDelegate(new DownloadListWidgetCompactDelegate(&m_DownloadManager, ui->downloadView, ui->downloadView));
+ if (m_Settings.compactDownloads()) {
+ ui->downloadView->setItemDelegate(new DownloadListWidgetCompactDelegate(&m_DownloadManager, m_Settings.metaDownloads(),
+ ui->downloadView, ui->downloadView));
} else {
- ui->downloadView->setItemDelegate(new DownloadListWidgetDelegate(&m_DownloadManager, ui->downloadView, ui->downloadView));
+ ui->downloadView->setItemDelegate(new DownloadListWidgetDelegate(&m_DownloadManager, m_Settings.metaDownloads(),
+ ui->downloadView, ui->downloadView));
}
DownloadListSortProxy *sortProxy = new DownloadListSortProxy(&m_DownloadManager, ui->downloadView);
@@ -4689,13 +4611,7 @@ void MainWindow::updateDownloadListDelegate()
connect(ui->downloadView->itemDelegate(), SIGNAL(restoreDownload(int)), &m_DownloadManager, SLOT(restoreDownload(int)));
connect(ui->downloadView->itemDelegate(), SIGNAL(cancelDownload(int)), &m_DownloadManager, SLOT(cancelDownload(int)));
connect(ui->downloadView->itemDelegate(), SIGNAL(pauseDownload(int)), &m_DownloadManager, SLOT(pauseDownload(int)));
- connect(ui->downloadView->itemDelegate(), SIGNAL(resumeDownload(int)), &m_DownloadManager, SLOT(resumeDownload(int)));
-}
-
-
-void MainWindow::on_compactBox_toggled(bool)
-{
- updateDownloadListDelegate();
+ connect(ui->downloadView->itemDelegate(), SIGNAL(resumeDownload(int)), this, SLOT(resumeDownload(int)));
}
@@ -4755,19 +4671,6 @@ void MainWindow::nxmUpdatesAvailable(const std::vector<int> &modIDs, QVariant us
}
}
-/*
-void MainWindow::nxmEndorsementToggled(int, QVariant, QVariant resultData, int)
-{
- if (resultData.toBool()) {
- ui->actionEndorseMO->setVisible(false);
- QMessageBox::question(this, tr("Thank you!"), tr("Thank you for your endorsement!"));
- }
-
- if (!disconnect(sender(), SIGNAL(nxmEndorsementToggled(int, QVariant, QVariant, int)),
- this, SLOT(nxmEndorsementToggled(int, QVariant, QVariant, int)))) {
- qCritical("failed to disconnect endorsement slot");
- }
-}*/
void MainWindow::nxmDownloadURLs(int, int, QVariant, QVariant resultData, int)
{
@@ -4813,6 +4716,7 @@ void MainWindow::loginSuccessful(bool necessary)
}
m_PostLoginTasks.clear();
+ NexusInterface::instance()->loginCompleted();
}
@@ -4838,6 +4742,7 @@ void MainWindow::loginFailed(const QString &message)
m_PostLoginTasks.clear();
statusBar()->hide();
}
+ NexusInterface::instance()->loginCompleted();
}
@@ -5024,10 +4929,16 @@ void MainWindow::editCategories()
}
}
+void MainWindow::deselectFilters()
+{
+ ui->categoriesList->clearSelection();
+}
+
void MainWindow::on_categoriesList_customContextMenuRequested(const QPoint &pos)
{
QMenu menu;
menu.addAction(tr("Edit Categories..."), this, SLOT(editCategories()));
+ menu.addAction(tr("Deselect filter"), this, SLOT(deselectFilters()));
menu.exec(ui->categoriesList->mapToGlobal(pos));
}
@@ -5060,8 +4971,13 @@ void MainWindow::unlockESPIndex()
void MainWindow::removeFromToolbar()
{
- Executable &exe = m_ExecutablesList.find(m_ContextAction->text());
- exe.m_Toolbar = false;
+ try {
+ Executable &exe = m_ExecutablesList.find(m_ContextAction->text());
+ exe.m_Toolbar = false;
+ } catch (const std::runtime_error&) {
+ qDebug("executable doesn't exist any more");
+ }
+
updateToolBar();
}
@@ -5069,11 +4985,13 @@ void MainWindow::removeFromToolbar()
void MainWindow::toolBar_customContextMenuRequested(const QPoint &point)
{
QAction *action = ui->toolBar->actionAt(point);
- if (action->objectName().startsWith("custom_")) {
- m_ContextAction = action;
- QMenu menu;
- menu.addAction(tr("Remove"), this, SLOT(removeFromToolbar()));
- menu.exec(ui->toolBar->mapToGlobal(point));
+ if (action != NULL) {
+ if (action->objectName().startsWith("custom_")) {
+ m_ContextAction = action;
+ QMenu menu;
+ menu.addAction(tr("Remove"), this, SLOT(removeFromToolbar()));
+ menu.exec(ui->toolBar->mapToGlobal(point));
+ }
}
}
@@ -5143,10 +5061,10 @@ void MainWindow::on_groupCombo_currentIndexChanged(int index)
connect(ui->modList, SIGNAL(expanded(QModelIndex)),newModel, SLOT(expanded(QModelIndex)));
connect(ui->modList, SIGNAL(collapsed(QModelIndex)), newModel, SLOT(collapsed(QModelIndex)));
connect(newModel, SIGNAL(expandItem(QModelIndex)), this, SLOT(expandModList(QModelIndex)));
-
} else {
m_ModListSortProxy->setSourceModel(&m_ModList);
}
+ modFilterActive(m_ModListSortProxy->isFilterActive());
}
void MainWindow::on_linkButton_pressed()
@@ -5169,20 +5087,326 @@ void MainWindow::on_showHiddenBox_toggled(bool checked)
m_DownloadManager.setShowHidden(checked);
}
+
+void MainWindow::createStdoutPipe(HANDLE *stdOutRead, HANDLE *stdOutWrite)
+{
+ SECURITY_ATTRIBUTES secAttributes;
+ secAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
+ secAttributes.bInheritHandle = TRUE;
+ secAttributes.lpSecurityDescriptor = NULL;
+
+ if (!::CreatePipe(stdOutRead, stdOutWrite, &secAttributes, 0)) {
+ qCritical("failed to create stdout reroute");
+ }
+
+ if (!::SetHandleInformation(*stdOutRead, HANDLE_FLAG_INHERIT, 0)) {
+ qCritical("failed to correctly set up the stdout reroute");
+ *stdOutWrite = *stdOutRead = INVALID_HANDLE_VALUE;
+ }
+}
+
+std::string MainWindow::readFromPipe(HANDLE stdOutRead)
+{
+ static const int chunkSize = 128;
+ std::string result;
+
+ char buffer[chunkSize + 1];
+ buffer[chunkSize] = '\0';
+
+ DWORD read = 1;
+ while (read > 0) {
+ if (!::ReadFile(stdOutRead, buffer, chunkSize, &read, NULL)) {
+ break;
+ }
+ if (read > 0) {
+ result.append(buffer, read);
+ if (read < chunkSize) {
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+void MainWindow::processLOOTOut(const std::string &lootOut, std::string &reportURL, std::string &errorMessages, QProgressDialog &dialog)
+{
+ std::vector<std::string> lines;
+ boost::split(lines, lootOut, boost::is_any_of("\r\n"));
+ foreach (const std::string &line, lines) {
+ if (line.length() > 0) {
+ size_t progidx = line.find("[progress]");
+ size_t reportidx = line.find("[report]");
+ size_t erroridx = line.find("[error]");
+ if (progidx != std::string::npos) {
+ dialog.setLabelText(line.substr(progidx + 11).c_str());
+ } else if (reportidx != std::string::npos) {
+ reportURL = line.substr(reportidx + 9);
+ } else if (erroridx != std::string::npos) {
+ qWarning("%s", line.c_str());
+ errorMessages.append(boost::algorithm::trim_copy(line.substr(erroridx + 8)) + "\n");
+ } else {
+ qDebug("%s", line.c_str());
+ }
+ }
+ }
+}
+
+
+HANDLE MainWindow::startApplication(const QString &executable, const QStringList &args, const QString &cwd, const QString &profile)
+{
+ QFileInfo binary;
+ QString arguments = args.join(" ");
+ QString currentDirectory = cwd;
+ QString profileName = profile;
+ if (profile.length() == 0) {
+ if (m_CurrentProfile != NULL) {
+ profileName = m_CurrentProfile->getName();
+ } else {
+ throw MyException(tr("No profile set"));
+ }
+ }
+ QString steamAppID;
+ if (executable.contains('\\') || executable.contains('/')) {
+ // file path
+ binary = QFileInfo(executable);
+ if (binary.isRelative()) {
+ // relative path, should be relative to game directory
+ binary = QFileInfo(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())) + "/" + executable);
+ }
+ if (cwd.length() == 0) {
+ currentDirectory = binary.absolutePath();
+ }
+ } else {
+ // only a file name, search executables list
+ try {
+ const Executable &exe = m_ExecutablesList.find(executable);
+ steamAppID = exe.m_SteamAppID;
+ if (arguments == "") {
+ arguments = exe.m_Arguments;
+ }
+ binary = exe.m_BinaryInfo;
+ if (cwd.length() == 0) {
+ currentDirectory = exe.m_WorkingDirectory;
+ }
+ } catch (const std::runtime_error&) {
+ qWarning("\"%s\" not set up as executable", executable.toUtf8().constData());
+ binary = QFileInfo(executable);
+ }
+ }
+
+ return spawnBinaryDirect(binary, arguments, profileName, currentDirectory, steamAppID);
+}
+
void MainWindow::on_bossButton_clicked()
{
+ std::string reportURL;
+ std::string errorMessages;
+
+ m_CurrentProfile->writeModlistNow();
+
+ bool success = false;
+
try {
this->setEnabled(false);
ON_BLOCK_EXIT([&] () { this->setEnabled(true); });
- LockedDialog dialog(this, tr("BOSS working"), false);
+ QProgressDialog dialog(this);
+ dialog.setLabelText(tr("LOOT working"));
+ dialog.setMaximum(0);
dialog.show();
- qApp->processEvents();
- m_PluginList.bossSort();
- savePluginList();
- dialog.hide();
+ QStringList parameters;
+ parameters << "--game" << ToQString(GameInfo::instance().getGameShortName())
+ << "--gamePath" << QString("\"%1\"").arg(ToQString(GameInfo::instance().getGameDirectory()));
+
+ HANDLE stdOutWrite = INVALID_HANDLE_VALUE;
+ HANDLE stdOutRead = INVALID_HANDLE_VALUE;
+ createStdoutPipe(&stdOutRead, &stdOutWrite);
+ HANDLE loot = startBinary(QFileInfo(qApp->applicationDirPath() + "/loot/lootcli.exe"),
+ parameters.join(" "),
+ m_CurrentProfile->getName(),
+ m_Settings.logLevel(),
+ qApp->applicationDirPath() + "/loot",
+ true,
+ stdOutWrite);
+
+ // we don't use the write end
+ ::CloseHandle(stdOutWrite);
+
+ if (loot != INVALID_HANDLE_VALUE) {
+ while (::WaitForSingleObject(loot, 100) == WAIT_TIMEOUT) {
+ // keep processing events so the app doesn't appear dead
+ QCoreApplication::processEvents();
+ if (dialog.wasCanceled()) {
+ ::TerminateProcess(loot, 1);
+ }
+ std::string lootOut = readFromPipe(stdOutRead);
+ processLOOTOut(lootOut, reportURL, errorMessages, dialog);
+ }
+ std::string remainder = readFromPipe(stdOutRead).c_str();
+ if (remainder.length() > 0) {
+ processLOOTOut(remainder, reportURL, errorMessages, dialog);
+ }
+ DWORD exitCode = 0UL;
+ ::GetExitCodeProcess(loot, &exitCode);
+ if (exitCode != 0UL) {
+ reportError(tr("loot failed. Exit code was: %1").arg(exitCode));
+ return;
+ } else {
+ success = true;
+ }
+ }
} catch (const std::exception &e) {
- reportError(tr("failed to run boss: %1").arg(e.what()));
- ui->bossButton->setEnabled(false);
+ reportError(tr("failed to run loot: %1").arg(e.what()));
+ }
+ if (errorMessages.length() > 0) {
+ QMessageBox *warn = new QMessageBox(QMessageBox::Warning, tr("Errors occured"), errorMessages.c_str(), QMessageBox::Ok, this);
+ warn->setModal(false);
+ warn->show();
+ }
+
+ if (success) {
+ if (reportURL.length() > 0) {
+ m_IntegratedBrowser.setWindowTitle("LOOT Report");
+ QString report(reportURL.c_str());
+ if (QFile::exists(report)) {
+ m_IntegratedBrowser.openUrl(QUrl::fromLocalFile(report));
+ } else {
+ qWarning("report file missing");
+ }
+ }
+ refreshESPList();
+
+ if (GameInfo::instance().getLoadOrderMechanism() == GameInfo::TYPE_FILETIME) {
+ QFile::remove(m_CurrentProfile->getLoadOrderFileName());
+ }
+ }
+}
+
+
+const char *MainWindow::PATTERN_BACKUP_GLOB = ".????_??_??_??_??_??";
+const char *MainWindow::PATTERN_BACKUP_REGEX = "\\.(\\d\\d\\d\\d_\\d\\d_\\d\\d_\\d\\d_\\d\\d_\\d\\d)";
+const char *MainWindow::PATTERN_BACKUP_DATE = "yyyy_MM_dd_hh_mm_ss";
+
+
+bool MainWindow::createBackup(const QString &filePath, const QDateTime &time)
+{
+ QString outPath = filePath + "." + time.toString(PATTERN_BACKUP_DATE);
+ if (shellCopy(QStringList(filePath), QStringList(outPath), this)) {
+ QFileInfo fileInfo(filePath);
+ removeOldFiles(fileInfo.absolutePath(), fileInfo.fileName() + PATTERN_BACKUP_GLOB, 3, QDir::Name);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void MainWindow::on_saveButton_clicked()
+{
+ savePluginList();
+ QDateTime now = QDateTime::currentDateTime();
+ if (createBackup(m_CurrentProfile->getPluginsFileName(), now)
+ && createBackup(m_CurrentProfile->getLoadOrderFileName(), now)
+ && createBackup(m_CurrentProfile->getLockedOrderFileName(), now)) {
+ MessageDialog::showMessage(tr("Backup of load order created"), this);
}
}
+
+QString MainWindow::queryRestore(const QString &filePath)
+{
+ QFileInfo pluginFileInfo(filePath);
+ QString pattern = pluginFileInfo.fileName() + ".*";
+ QFileInfoList files = pluginFileInfo.absoluteDir().entryInfoList(QStringList(pattern), QDir::Files, QDir::Name);
+
+ SelectionDialog dialog(tr("Choose backup to restore"), this);
+ QRegExp exp(pluginFileInfo.fileName() + PATTERN_BACKUP_REGEX);
+ QRegExp exp2(pluginFileInfo.fileName() + "\\.(.*)");
+ foreach(const QFileInfo &info, files) {
+ if (exp.exactMatch(info.fileName())) {
+ QDateTime time = QDateTime::fromString(exp.cap(1), PATTERN_BACKUP_DATE);
+ dialog.addChoice(time.toString(), "", exp.cap(1));
+ } else if (exp2.exactMatch(info.fileName())) {
+ dialog.addChoice(exp2.cap(1), "", exp2.cap(1));
+ }
+ }
+
+ if (dialog.numChoices() == 0) {
+ QMessageBox::information(this, tr("No Backups"), tr("There are no backups to restore"));
+ return QString();
+ }
+
+ if (dialog.exec() == QDialog::Accepted) {
+ return dialog.getChoiceData().toString();
+ } else {
+ return QString();
+ }
+}
+
+void MainWindow::on_restoreButton_clicked()
+{
+ QString pluginName = m_CurrentProfile->getPluginsFileName();
+ QString choice = queryRestore(pluginName);
+ if (!choice.isEmpty()) {
+ QString loadOrderName = m_CurrentProfile->getLoadOrderFileName();
+ QString lockedName = m_CurrentProfile->getLockedOrderFileName();
+ if (!shellCopy(pluginName + "." + choice, pluginName, true, this) ||
+ !shellCopy(loadOrderName + "." + choice, loadOrderName, true, this) ||
+ !shellCopy(lockedName + "." + choice, lockedName, true, this)) {
+ QMessageBox::critical(this, tr("Restore failed"),
+ tr("Failed to restore the backup. Errorcode: %1").arg(windowsErrorString(::GetLastError())));
+ }
+ refreshESPList();
+ }
+}
+
+void MainWindow::on_saveModsButton_clicked()
+{
+ m_CurrentProfile->writeModlistNow(true);
+ QDateTime now = QDateTime::currentDateTime();
+ if (createBackup(m_CurrentProfile->getModlistFileName(), now)) {
+ MessageDialog::showMessage(tr("Backup of modlist created"), this);
+ }
+}
+void MainWindow::on_restoreModsButton_clicked()
+{
+ QString modlistName = m_CurrentProfile->getModlistFileName();
+ QString choice = queryRestore(modlistName);
+ if (!choice.isEmpty()) {
+ if (!shellCopy(modlistName + "." + choice, modlistName, true, this)) {
+ QMessageBox::critical(this, tr("Restore failed"),
+ tr("Failed to restore the backup. Errorcode: %1").arg(windowsErrorString(::GetLastError())));
+ }
+ refreshModList(false);
+ }
+}
+
+void MainWindow::on_actionCopy_Log_to_Clipboard_triggered()
+{
+ QStringList lines;
+ QAbstractItemModel *model = ui->logList->model();
+ for (int i = 0; i < model->rowCount(); ++i) {
+ lines.append(QString("%1 [%2] %3").arg(model->index(i, 0).data().toString())
+ .arg(model->index(i, 1).data(Qt::UserRole).toString())
+ .arg(model->index(i, 1).data().toString()));
+ }
+ QApplication::clipboard()->setText(lines.join("\n"));
+}
+
+void MainWindow::on_categoriesAndBtn_toggled(bool checked)
+{
+ if (checked) {
+ m_ModListSortProxy->setFilterMode(ModListSortProxy::FILTER_AND);
+ }
+}
+
+void MainWindow::on_categoriesOrBtn_toggled(bool checked)
+{
+ if (checked) {
+ m_ModListSortProxy->setFilterMode(ModListSortProxy::FILTER_OR);
+ }
+}
+
+void MainWindow::on_managedArchiveLabel_linkHovered(const QString&)
+{
+ QToolTip::showText(QCursor::pos(),
+ ui->managedArchiveLabel->toolTip());
+}