From 7d79e3ad1e8a710e836e1cd44106a610e6d2a049 Mon Sep 17 00:00:00 2001 From: Tannin Date: Thu, 15 Aug 2013 22:13:18 +0200 Subject: - bugfix: nameprefix-flag incorrectly interpreted on oblivion-style bsas - bugfix: optimization for missing inis fixed - bugfix: fixmods didn't search for esps in overwrite - bugfix: configurator didn't work for fallout new vegas because of a missing cast - bugfix: configurator attempted parameter formatting on ini files instead of using the raw data - bugfix: searching for a path in the directory structure could cause a crash when not using the directory output - optimization to findfirstfile for cases where a single file is searched - display of bsas changed. hopefully it is more understandable now - cache for the test whether a mod is in conflict. Should speed ab scrolling in mod list - mod list has now an additional column for the installation time - nmm importer no longer cancels in cases where a data file references an undeclared mod. - integrated improved settings.json for the configurator contributed by "delta534" --- src/mainwindow.cpp | 85 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 21 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 99b37feb..84e5b094 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -256,6 +256,8 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), this, SLOT(espFilterChanged(QString))); connect(&m_PluginList, SIGNAL(saveTimer()), this, SLOT(savePluginList())); + connect(ui->bsaWarning, SIGNAL(linkActivated(QString)), this, SLOT(linkClicked(QString))); + connect(&m_DirectoryRefresher, SIGNAL(refreshed()), this, SLOT(directory_refreshed())); connect(&m_DirectoryRefresher, SIGNAL(progress(int)), this, SLOT(refresher_progress(int))); connect(&m_DirectoryRefresher, SIGNAL(error(QString)), this, SLOT(showError(QString))); @@ -1648,7 +1650,8 @@ void MainWindow::refreshBSAList() QTreeWidgetItem *newItem = new QTreeWidgetItem(strings); 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::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); @@ -1658,6 +1661,7 @@ void MainWindow::refreshBSAList() } if (index < 0) index = 0; + UINT32 sortValue = ((m_DirectoryStructure->getOriginByID(origin).getPriority() & 0xFFFF) << 16) | (index & 0xFFFF); items.push_back(std::make_pair(sortValue, newItem)); } @@ -1666,7 +1670,18 @@ void MainWindow::refreshBSAList() std::sort(items.begin(), items.end(), BySortValue); for (std::vector >::iterator iter = items.begin(); iter != items.end(); ++iter) { - ui->bsaList->addTopLevelItem(iter->second); + int originID = iter->second->data(1, Qt::UserRole).toInt(); + FilesOrigin origin = m_DirectoryStructure->getOriginByID(originID); + QList items = ui->bsaList->findItems(ToQString(origin.getName()), Qt::MatchFixedString); + QTreeWidgetItem *subItem = NULL; + if (items.length() > 0) { + subItem = items.at(0); + } else { + subItem = new QTreeWidgetItem(QStringList(ToQString(origin.getName()))); + ui->bsaList->addTopLevelItem(subItem); + } + subItem->addChild(iter->second); + //ui->bsaList->addTopLevelItem(iter->second); } checkBSAList(); @@ -1681,27 +1696,35 @@ void MainWindow::checkBSAList() bool warning = false; for (int i = 0; i < ui->bsaList->topLevelItemCount(); ++i) { - QTreeWidgetItem *item = ui->bsaList->topLevelItem(i); - QString filename = item->text(0); - item->setIcon(0, QIcon()); - item->setToolTip(0, QString()); - - if (item->checkState(0) == Qt::Unchecked) { - if (m_DefaultArchives.contains(filename)) { - item->setIcon(0, QIcon(":/MO/gui/resources/dialog-warning.png")); - item->setToolTip(0, tr("This bsa is enabled in the ini file so it may be required!")); - warning = true; - } 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)) { + bool modWarning = false; + QTreeWidgetItem *tlItem = ui->bsaList->topLevelItem(i); + for (int j = 0; j < tlItem->childCount(); ++j) { + QTreeWidgetItem *item = tlItem->child(j); + QString filename = item->text(0); + item->setIcon(0, QIcon()); + item->setToolTip(0, QString()); + + if (item->checkState(0) == Qt::Unchecked) { + if (m_DefaultArchives.contains(filename)) { item->setIcon(0, QIcon(":/MO/gui/resources/dialog-warning.png")); - 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!")); - warning = true; + item->setToolTip(0, tr("This bsa is enabled in the ini file so it may be required!")); + modWarning = true; + } 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)) { + item->setIcon(0, QIcon(":/MO/gui/resources/dialog-warning.png")); + 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; + } } } } + if (modWarning) { + ui->bsaList->expandItem(ui->bsaList->topLevelItem(i)); + warning = true; + } } if (warning) { @@ -2072,7 +2095,6 @@ void MainWindow::issueTriggered() ::ShellExecuteW(NULL, L"open", L"http://issue.tannin.eu/tbg", NULL, NULL, SW_SHOWNORMAL); } - void MainWindow::tutorialTriggered() { QAction *tutorialAction = qobject_cast(sender()); @@ -3312,6 +3334,7 @@ void MainWindow::fixMods_clicked() QStringList espFilter("*.esp"); espFilter.append("*.esm"); + // search in data { QDir dataDir(m_GamePath + "/data"); QStringList esps = dataDir.entryList(espFilter); @@ -3323,6 +3346,7 @@ void MainWindow::fixMods_clicked() } } + // search in mods for (unsigned int i = 0; i < m_CurrentProfile->numRegularMods(); ++i) { int modIndex = m_CurrentProfile->modIndexByPriority(i); ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); @@ -3336,12 +3360,25 @@ void MainWindow::fixMods_clicked() } } + // search in overwrite + { + QDir overwriteDir(ToQString(GameInfo::instance().getOverwriteDir())); + QStringList esps = overwriteDir.entryList(espFilter); + foreach (const QString &esp, esps) { + std::map >::iterator iter = missingPlugins.find(esp); + if (iter != missingPlugins.end()) { + iter->second.push_back(""); + } + } + } + + ActivateModsDialog dialog(missingPlugins, this); if (dialog.exec() == QDialog::Accepted) { // activate the required mods, then enable all esps std::set modsToActivate = dialog.getModsToActivate(); for (std::set::iterator iter = modsToActivate.begin(); iter != modsToActivate.end(); ++iter) { - if (*iter != "") { + if ((*iter != "") && (*iter != "")) { unsigned int modIndex = ModInfo::getIndex(*iter); m_CurrentProfile->setModEnabled(modIndex, true); } @@ -3496,6 +3533,12 @@ void MainWindow::nexusLinkActivated(const QString &link) } +void MainWindow::linkClicked(const QString &url) +{ + ::ShellExecuteW(NULL, L"open", ToWString(url).c_str(), NULL, NULL, SW_SHOWNORMAL); +} + + void MainWindow::downloadRequestedNXM(const QString &url) { QString username, password; -- cgit v1.3.1 From 78c4178f2a70bb55bd761efc92d58b8ac0e3747b Mon Sep 17 00:00:00 2001 From: Tannin Date: Sat, 17 Aug 2013 08:49:36 +0200 Subject: - plugins that failed to load are now reported as problems - some wording fixes in the tutorial - updated proxyPython to a modified interface --- src/mainwindow.cpp | 46 ++++- src/mainwindow.h | 14 +- src/mainwindow.ui | 274 +++++++++++++++--------------- src/tutorials/tutorial_firststeps_main.js | 4 +- 4 files changed, 198 insertions(+), 140 deletions(-) (limited to 'src/mainwindow.cpp') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 84e5b094..a8f69090 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -988,12 +988,14 @@ void MainWindow::loadPlugins() if (QLibrary::isLibrary(pluginName)) { QPluginLoader pluginLoader(pluginName); if (pluginLoader.instance() == NULL) { + m_UnloadedPlugins.push_back(pluginName); qCritical("failed to load plugin %s: %s", pluginName.toUtf8().constData(), pluginLoader.errorString().toUtf8().constData()); } else { if (registerPlugin(pluginLoader.instance())) { qDebug("loaded plugin \"%s\"", pluginName.toUtf8().constData()); } else { + m_UnloadedPlugins.push_back(pluginName); qWarning("plugin \"%s\" failed to load", pluginName.toUtf8().constData()); } } @@ -1001,6 +1003,8 @@ void MainWindow::loadPlugins() } m_DownloadManager.setSupportedExtensions(m_InstallationManager.getSupportedExtensions()); + + m_DiagnosisPlugins.push_back(this); } IGameInfo &MainWindow::gameInfo() const @@ -1936,6 +1940,46 @@ IDownloadManager *MainWindow::downloadManager() return &m_DownloadManager; } +std::vector MainWindow::activeProblems() const +{ + std::vector problems; + if (m_UnloadedPlugins.size() != 0) { + problems.push_back(PROBLEM_PLUGINSNOTLOADED); + } + return problems; +} + +QString MainWindow::shortDescription(unsigned int key) const +{ + switch (key) { + case PROBLEM_PLUGINSNOTLOADED: { + return tr("Some plugins could not be loaded"); + } break; + } +} + +QString MainWindow::fullDescription(unsigned int key) const +{ + switch (key) { + case PROBLEM_PLUGINSNOTLOADED: { + QString result = tr("The following Plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version:
    "); + foreach (const QString &plugin, m_UnloadedPlugins) { + result += "
  • " + plugin + "
  • "; + } + result += "
      "; + return result; + } break; + } +} + +bool MainWindow::hasGuidedFix(unsigned int key) const +{ + return false; +} + +void MainWindow::startGuidedFix(unsigned int key) const +{ +} void MainWindow::installMod() { @@ -3519,7 +3563,7 @@ void MainWindow::on_actionNexus_triggered() std::wstring nxmPath = ToWString(QApplication::applicationDirPath() + "/nxmhandler.exe"); std::wstring executable = ToWString(QApplication::applicationFilePath()); ::ShellExecuteW(NULL, L"open", nxmPath.c_str(), - (std::wstring(L"reg ") + GameInfo::instance().getGameShortName() + L" " + executable).c_str(), NULL, SW_SHOWNORMAL); + (std::wstring(L"reg ") + GameInfo::instance().getGameShortName() + L" \"" + executable + L"\"").c_str(), NULL, SW_SHOWNORMAL); ::ShellExecuteW(NULL, L"open", GameInfo::instance().getNexusPage().c_str(), NULL, NULL, SW_SHOWNORMAL); ui->tabWidget->setCurrentIndex(4); diff --git a/src/mainwindow.h b/src/mainwindow.h index d7d0a8e7..b70defbe 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -59,9 +59,10 @@ class QToolButton; class ModListSortProxy; class ModListGroupCategoriesProxy; -class MainWindow : public QMainWindow, public MOBase::IOrganizer +class MainWindow : public QMainWindow, public MOBase::IOrganizer, public MOBase::IPluginDiagnose { Q_OBJECT + Q_INTERFACES(MOBase::IPluginDiagnose) public: explicit MainWindow(const QString &exeName, QSettings &initSettings, QWidget *parent = 0); @@ -109,6 +110,12 @@ public: virtual QString resolvePath(const QString &fileName) const; virtual MOBase::IDownloadManager *downloadManager(); + virtual std::vector activeProblems() const; + virtual QString shortDescription(unsigned int key) const; + virtual QString fullDescription(unsigned int key) const; + virtual bool hasGuidedFix(unsigned int key) const; + virtual void startGuidedFix(unsigned int key) const; + void addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, ModInfo::Ptr info); void saveArchiveList(); @@ -234,6 +241,10 @@ private: static void setupNetworkProxy(bool activate); void activateProxy(bool activate); +private: + + static const unsigned int PROBLEM_PLUGINSNOTLOADED = 1; + private: Ui::MainWindow *ui; @@ -300,6 +311,7 @@ private: MOBase::IGameInfo *m_GameInfo; std::vector m_DiagnosisPlugins; + std::vector m_UnloadedPlugins; private slots: diff --git a/src/mainwindow.ui b/src/mainwindow.ui index fcdbd63c..14f00336 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -413,155 +413,157 @@ p, li { white-space: pre-wrap; } - - - - - - 0 - 0 - - - - - 0 - 40 - - - - - 9 - 75 - true - - - - Pick a program to run. - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + 9 + 75 + true + + + + Pick a program to run. + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Choose the program to run. Once you start using ModOrganizer, you should always run your game and tools from here or through shortcuts created here, otherwise mods installed through MO will not be visible.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">You can add new Tools to this list, but I can't promise tools I haven't tested will work.</span></p></body></html> - - - - 32 - 32 - - - - false - - - - - - - - - - 0 - 0 - - - - - 120 - 0 - - - - - 16777215 - 16777215 - - - - - 10 - 75 - true - - - - Run program - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + + + + 32 + 32 + + + + false + + + + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + 16777215 + 16777215 + + + + + 10 + 75 + true + + + + Run program + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Run the selected program with ModOrganizer enabled.</span></p></body></html> - - - - - - Run - - - - :/MO/gui/run:/MO/gui/run - - - - 36 - 36 - - - - - - - - - 0 - 0 - - - - - 140 - 0 - - - - - 16777215 - 16777215 - - - - - 0 - 0 - - - - Create a shortcut in your start menu or on the desktop to the specified program - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + + + + + + Run + + + + :/MO/gui/run:/MO/gui/run + + + + 36 + 36 + + + + + + + + + 0 + 0 + + + + + 140 + 0 + + + + + 16777215 + 16777215 + + + + + 0 + 0 + + + + Create a shortcut in your start menu or on the desktop to the specified program + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This creates a start menu shortcut that directly starts the selected program with the MO active.</span></p></body></html> - - - Shortcut - - - - :/MO/gui/link:/MO/gui/link - - - - - - + + + Shortcut + + + + :/MO/gui/link:/MO/gui/link + + + + + + + diff --git a/src/tutorials/tutorial_firststeps_main.js b/src/tutorials/tutorial_firststeps_main.js index 150fde84..e952c95a 100644 --- a/src/tutorials/tutorial_firststeps_main.js +++ b/src/tutorials/tutorial_firststeps_main.js @@ -93,7 +93,7 @@ function getTutorialSteps() function() { tutorial.text = qsTr("...but most contain plugins. These are plugins for the game and are required " +"to add stuff to the game (new weapons, armors, quests, areas, ...). " - +"Please open the \"ESPs\"-tab to get a list of plugins.") + +"Please open the \"Plugins\"-tab to get a list of plugins.") if (tutorialControl.waitForTabOpen("tabWidget", 0)) { highlightItem("tabWidget", true) } else { @@ -118,7 +118,7 @@ function getTutorialSteps() function() { tutorial.text = qsTr("Another special type of files are BSAs. These are bundles of game resources. " - + "Please open the \"BSAs\"-tab.") + + "Please open the \"Archives\"-tab.") if (tutorialControl.waitForTabOpen("tabWidget", 1)) { highlightItem("tabWidget", true) } else { -- cgit v1.3.1