From 84c66727bff954fd0820fc9f33833848496e9531 Mon Sep 17 00:00:00 2001 From: Tannin Date: Mon, 14 Jul 2014 21:22:44 +0200 Subject: - when highlighting a mod the overwritten and overwriting mods are now highlighted in the list - when starting an external application MO now wraps the process in a job and waits on that instead. This way MO is not unlocked early when skyrim is started through skse - mod info dialog no longer offers the esp tab for foreign mods because that caused confusion - updated translation files - download directory and mod directory are now created if necessary - bugfix: staging script created unnecessary copies of translation files - bugfix: potential invalid array access when trying to determine best mod order - bugfix: deleter file wasn't removed after esp hiding was disabled - bugfix: potential access to to un-initialized login reply - bugfix: changed the initialization order to allow more ui controls to be localized --- src/mainwindow.cpp | 125 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 102 insertions(+), 23 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 9cbe9c40..ce5ae865 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -168,6 +168,9 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget { ui->setupUi(this); this->setWindowTitle(ToQString(GameInfo::instance().getGameName()) + " Mod Organizer v" + m_Updater.getVersion().displayString()); + + languageChange(m_Settings.language()); + ui->logList->setModel(LogBuffer::instance()); ui->logList->setColumnWidth(0, 100); ui->logList->setAutoScroll(true); @@ -277,6 +280,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget connect(&m_ModList, SIGNAL(requestColumnSelect(QPoint)), this, SLOT(displayColumnSelection(QPoint))); connect(&m_ModList, SIGNAL(fileMoved(QString, QString, QString)), this, SLOT(fileMoved(QString, QString, QString))); connect(ui->modList, SIGNAL(dropModeUpdate(bool)), &m_ModList, SLOT(dropModeUpdate(bool))); + connect(ui->modList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(modlistSelectionChanged(QModelIndex,QModelIndex))); connect(m_ModListSortProxy, SIGNAL(filterActive(bool)), this, SLOT(modFilterActive(bool))); connect(ui->modFilterEdit, SIGNAL(textChanged(QString)), m_ModListSortProxy, SLOT(updateFilter(QString))); @@ -310,7 +314,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget connect(NexusInterface::instance(), SIGNAL(needLogin()), this, SLOT(nexusLogin())); connect(&TutorialManager::instance(), SIGNAL(windowTutorialFinished(QString)), this, SLOT(windowTutorialFinished(QString))); - + connect(ui->modList->header(), SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(modListSortIndicatorChanged(int,Qt::SortOrder))); connect(ui->toolBar, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(toolBar_customContextMenuRequested(QPoint))); connect(&m_IntegratedBrowser, SIGNAL(requestDownload(QUrl,QNetworkReply*)), this, SLOT(requestDownload(QUrl,QNetworkReply*))); @@ -615,6 +619,10 @@ void MainWindow::createHelpWidget() { QToolButton *toolBtn = qobject_cast(ui->toolBar->widgetForAction(ui->actionHelp)); QMenu *buttonMenu = toolBtn->menu(); + if (buttonMenu == NULL) { + return; + } + buttonMenu->clear(); QAction *helpAction = new QAction(tr("Help on UI"), buttonMenu); connect(helpAction, SIGNAL(triggered()), this, SLOT(helpTriggered())); @@ -1155,6 +1163,7 @@ bool MainWindow::registerPlugin(QObject *plugin, const QString &fileName) } plugin->setProperty("filename", fileName); m_Settings.registerPlugin(pluginObj); + installTranslator(QFileInfo(fileName).baseName()); } { // diagnosis plugins @@ -1380,11 +1389,35 @@ void MainWindow::spawnBinary(const QFileInfo &binary, const QString &arguments, QCoreApplication::processEvents(); - while ((::WaitForSingleObject(processHandle, 100) == WAIT_TIMEOUT) && - !dialog->unlockClicked()) { + DWORD retLen; + JOBOBJECT_BASIC_PROCESS_ID_LIST info; + + bool isJobHandle = true; + + DWORD res = ::MsgWaitForMultipleObjects(1, &processHandle, false, 1000, QS_KEY | QS_MOUSE); + while ((res != WAIT_FAILED) && (res != WAIT_OBJECT_0) && !dialog->unlockClicked()) { + if (isJobHandle) { + if (::QueryInformationJobObject(processHandle, JobObjectBasicProcessIdList, &info, sizeof(info), &retLen) > 0) { + if (info.NumberOfProcessIdsInList == 0) { + break; + } + } else { + // the info-object I passed only provides space for 1 process id. but since this code only cares about whether there + // is more than one that's good enough. ERROR_MORE_DATA simply signals there are at least two processes running. + // any other error probably means the handle is a regular process handle, probably caused by running MO in a job without + // the right to break out. + if (::GetLastError() != ERROR_MORE_DATA) { + isJobHandle = false; + } + } + } + // keep processing events so the app doesn't appear dead QCoreApplication::processEvents(); + + res = ::MsgWaitForMultipleObjects(1, &processHandle, false, 1000, QS_KEY | QS_MOUSE); } + ::CloseHandle(processHandle); this->setEnabled(true); refreshDirectoryStructure(); @@ -2061,7 +2094,6 @@ void MainWindow::readSettings() setCategoryListVisible(filtersVisible); ui->displayCategoriesBtn->setChecked(filtersVisible); - languageChange(m_Settings.language()); int selectedExecutable = settings.value("selected_executable").toInt(); setExecutableIndex(selectedExecutable); @@ -3028,6 +3060,21 @@ void MainWindow::modlistChanged(const QModelIndex&, int) m_CurrentProfile->writeModlist(); } +void MainWindow::modlistSelectionChanged(const QModelIndex ¤t, const QModelIndex&) +{ + ModInfo::Ptr selectedMod = ModInfo::getByIndex(current.data(Qt::UserRole + 1).toInt()); + m_ModList.setOverwriteMarkers(selectedMod->getModOverwrite(), selectedMod->getModOverwritten()); + if (m_ModListSortProxy != NULL) { + m_ModListSortProxy->invalidate(); + } + ui->modList->verticalScrollBar()->repaint(); +} + +void MainWindow::modListSortIndicatorChanged(int, Qt::SortOrder) +{ + ui->modList->verticalScrollBar()->repaint(); +} + void MainWindow::removeMod_clicked() { try { @@ -3185,6 +3232,7 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, connect(dialog, SIGNAL(finished(int)), this, SLOT(overwriteClosed(int))); } else { modInfo->saveMeta(); +qDebug("%s - %d", qPrintable(modInfo->name()), modInfo->hasFlag(ModInfo::FLAG_FOREIGN)); ModInfoDialog dialog(modInfo, m_DirectoryStructure, modInfo->hasFlag(ModInfo::FLAG_FOREIGN), this); connect(&dialog, SIGNAL(nexusLinkActivated(QString)), this, SLOT(nexusLinkActivated(QString))); connect(&dialog, SIGNAL(downloadRequest(QString)), this, SLOT(downloadRequestedNXM(QString))); @@ -4191,6 +4239,9 @@ void MainWindow::downloadRequested(QNetworkReply *reply, int modID, const QStrin void MainWindow::installTranslator(const QString &name) { + if (m_CurrentLanguage == "en_US") { + return; + } QTranslator *translator = new QTranslator(this); QString fileName = name + "_" + m_CurrentLanguage; if (!translator->load(fileName, qApp->applicationDirPath() + "/translations")) { @@ -4212,26 +4263,19 @@ void MainWindow::languageChange(const QString &newLanguage) m_CurrentLanguage = newLanguage; - if (newLanguage != "en_US") { - installTranslator("qt"); - installTranslator(ToQString(AppConfig::translationPrefix())); - foreach(IPlugin *plugin, m_Settings.plugins()) { - QObject *pluginObj = dynamic_cast(plugin); - if (pluginObj != NULL) { - QVariant fileNameVariant = pluginObj->property("filename"); - if (fileNameVariant.isValid()) { - QString fileName = QFileInfo(fileNameVariant.toString()).baseName(); - installTranslator(fileName); - } - } - } - } + installTranslator("qt"); + installTranslator(ToQString(AppConfig::translationPrefix())); ui->retranslateUi(this); + qDebug("loaded language %s", qPrintable(newLanguage)); + ui->profileBox->setItemText(0, QObject::tr("")); -// ui->toolBar->addWidget(createHelpWidget(ui->toolBar)); + + createHelpWidget(); updateDownloadListDelegate(); updateProblemsButton(); + + ui->listOptionsBtn->setMenu(modListContextMenu()); } @@ -5110,6 +5154,9 @@ void MainWindow::on_espList_customContextMenuRequested(const QPoint &pos) void MainWindow::on_groupCombo_currentIndexChanged(int index) { + if (m_ModListSortProxy == NULL) { + return; + } QAbstractProxyModel *newModel = NULL; switch (index) { case 1: { @@ -5322,16 +5369,46 @@ void MainWindow::on_bossButton_clicked() m_PluginList.clearAdditionalInformation(); + DWORD retLen; + JOBOBJECT_BASIC_PROCESS_ID_LIST info; + bool isJobHandle = true; + if (loot != INVALID_HANDLE_VALUE) { - while (::WaitForSingleObject(loot, 100) == WAIT_TIMEOUT) { - // keep processing events so the app doesn't appear dead - QCoreApplication::processEvents(); + DWORD res = ::MsgWaitForMultipleObjects(1, &loot, false, 1000, QS_KEY | QS_MOUSE); + while ((res != WAIT_FAILED) && (res != WAIT_OBJECT_0)) { + if (isJobHandle) { + if (::QueryInformationJobObject(loot, JobObjectBasicProcessIdList, &info, sizeof(info), &retLen) > 0) { + if (info.NumberOfProcessIdsInList == 0) { + qDebug("no more processes in job"); + break; + } + } else { + // the info-object I passed only provides space for 1 process id. but since this code only cares about whether there + // is more than one that's good enough. ERROR_MORE_DATA simply signals there are at least two processes running. + // any other error probably means the handle is a regular process handle, probably caused by running MO in a job without + // the right to break out. + if (::GetLastError() != ERROR_MORE_DATA) { + isJobHandle = false; + } + } + } + if (dialog.wasCanceled()) { - ::TerminateProcess(loot, 1); + if (isJobHandle) { + ::TerminateJobObject(loot, 1); + } else { + ::TerminateProcess(loot, 1); + } } + + // keep processing events so the app doesn't appear dead + QCoreApplication::processEvents(); std::string lootOut = readFromPipe(stdOutRead); processLOOTOut(lootOut, reportURL, errorMessages, dialog); + + res = ::MsgWaitForMultipleObjects(1, &loot, false, 1000, QS_KEY | QS_MOUSE); } + std::string remainder = readFromPipe(stdOutRead).c_str(); if (remainder.length() > 0) { processLOOTOut(remainder, reportURL, errorMessages, dialog); @@ -5344,6 +5421,8 @@ void MainWindow::on_bossButton_clicked() } else { success = true; } + } else { + reportError(tr("failed to start loot")); } } catch (const std::exception &e) { reportError(tr("failed to run loot: %1").arg(e.what())); -- cgit v1.3.1