From 482f13a50b921e61d34d09f72a7fb4216efe742b Mon Sep 17 00:00:00 2001 From: Tannin Date: Mon, 8 Sep 2014 20:37:23 +0200 Subject: - re-enabled building of loot_cli and started developing against the new api - extended set of default categories - more tolerand bbcode parser - added a few colors for the bbcode parser - more fixes to qt5 compatibility - started work on ability to unloading (and thus re-loading) of plugins - names of plugins are no longer localizable (because those names are also used to store settings) - added settings to disable individual diagnosis settings - path of dependencies is now configured in a .pri file instead of environment variablees - bugfix: if the modid-input is canceled, the id was saved as -1 and wasn't re-requested from the user - bugfix: moving files with the SHFileOperation-Api didn't update the vfs correctly (still not perfect but better) - bugfix: attempt to remove the deleter-file seems to have caused error messages for some users - bugfix: fixed a couple of cases that might have caused the tutorial to hang --- src/ModOrganizer.pro | 6 +- src/bbcode.cpp | 93 +++++++++++++++++++++---------- src/categories.cpp | 26 +++++++-- src/downloadmanager.cpp | 2 +- src/editexecutablesdialog.cpp | 2 +- src/main.cpp | 12 +++- src/mainwindow.cpp | 77 ++++++++++++++++++------- src/mainwindow.h | 8 +++ src/modinfo.cpp | 4 +- src/modlist.cpp | 11 ++++ src/modlist.h | 6 ++ src/nexusinterface.cpp | 23 ++++---- src/nexusinterface.h | 8 ++- src/organizer.pro | 50 +++++++++-------- src/pluginlist.cpp | 5 +- src/pluginlist.h | 12 ++-- src/settings.cpp | 1 - src/shared/directoryentry.cpp | 4 -- src/shared/directoryentry.h | 2 + src/shared/shared.pro | 53 ++++++++++-------- src/tutorials/TutorialOverlay.qml | 6 +- src/tutorials/tutorial_firststeps_main.js | 35 ++++++++---- 22 files changed, 302 insertions(+), 144 deletions(-) (limited to 'src') diff --git a/src/ModOrganizer.pro b/src/ModOrganizer.pro index 9907d086..9b2d998b 100644 --- a/src/ModOrganizer.pro +++ b/src/ModOrganizer.pro @@ -1,6 +1,5 @@ TEMPLATE = subdirs - SUBDIRS = bsatk \ shared \ uibase \ @@ -13,9 +12,10 @@ SUBDIRS = bsatk \ nxmhandler \ BossDummy \ pythonRunner \ + loot_cli \ esptk -plugins.depends = pythonRunner +plugins.depends = pythonRunner uibase hookdll.depends = shared organizer.depends = shared uibase plugins @@ -30,7 +30,7 @@ DLLSPATH = $${DESTDIR}\\dlls otherlibs.path = $$DLLSPATH otherlibs.files += $${STATICDATAPATH}\\7z.dll \ - $$(BOOSTPATH)\\stage\\lib\\boost_python-vc*-mt-1*.dll + $${BOOSTPATH}\\stage\\lib\\boost_python-vc*-mt-1*.dll qtlibs.path = $$DLLSPATH diff --git a/src/bbcode.cpp b/src/bbcode.cpp index 18f2beb1..92eb427f 100644 --- a/src/bbcode.cpp +++ b/src/bbcode.cpp @@ -39,7 +39,7 @@ public: return s_Instance; } - QString convertTag(const QString &input, int &length) + QString convertTag(QString input, int &length) { // extract the tag name m_TagNameExp.indexIn(input, 1, QRegExp::CaretAtOffset); @@ -50,14 +50,30 @@ public: if (tagName.endsWith('=')) { tagName.chop(1); } - QString closeTag = tagName == "*" ? "
" - : QString("[/%1]").arg(tagName); - int closeTagPos = input.indexOf(closeTag, 0, Qt::CaseInsensitive); - //qDebug("close tag %s at %d", closeTag.toUtf8().constData(), closeTagPos); + int closeTagPos = 0; + int closeTagLength = 0; + if (tagName == "*") { + // ends at the next bullet point + closeTagPos = input.indexOf(QRegExp("(\\[\\*\\]|)", Qt::CaseInsensitive), 3); + // leave closeTagLength at 0 because we don't want to "eat" the next bullet point + } else if (tagName == "line") { + // ends immediately after the tag + closeTagPos = 6; + // leave closeTagLength at 0 because there is no close tag to skip over + } else { + QString closeTag = QString("[/%1]").arg(tagName); + closeTagPos = input.indexOf(closeTag, 0, Qt::CaseInsensitive); + if (closeTagPos == -1) { + // workaround to improve compatibility: add fake closing tag + input.append(closeTag); + closeTagPos = input.size() - closeTag.size(); + } + closeTagLength = closeTag.size(); + } if (closeTagPos > -1) { - length = closeTagPos + closeTag.length(); + length = closeTagPos + closeTagLength; QString temp = input.mid(0, length); if (tagIter->second.first.indexIn(temp) == 0) { if (tagIter->second.second.isEmpty()) { @@ -73,6 +89,9 @@ public: qWarning("don't know how to deal with tag %s", qPrintable(tagName)); } } else { + if (tagName == "*") { + temp.remove(QRegExp("(\\[/\\*\\])?(
)?$")); + } return temp.replace(tagIter->second.first, tagIter->second.second); } } else { @@ -123,6 +142,8 @@ private: "
\\1
"); m_TagMap["heading"]= std::make_pair(QRegExp("\\[heading\\](.*)\\[/heading\\]"), "

\\1

"); + m_TagMap["line"] = std::make_pair(QRegExp("\\[line\\]"), + "
"); // lists m_TagMap["list"] = std::make_pair(QRegExp("\\[list\\](.*)\\[/list\\]"), @@ -135,8 +156,6 @@ private: "
    \\1
"); m_TagMap["li"] = std::make_pair(QRegExp("\\[li\\](.*)\\[/li\\]"), "
  • \\1
  • "); - m_TagMap["*"] = std::make_pair(QRegExp("\\[\\*\\](.*)
    "), - "
  • \\1
  • "); // tables m_TagMap["table"] = std::make_pair(QRegExp("\\[table\\](.*)\\[/table\\]"), @@ -153,13 +172,26 @@ private: "\\1"); m_TagMap["url="] = std::make_pair(QRegExp("\\[url=([^\\]]*)\\](.*)\\[/url\\]"), "\\2"); - m_TagMap["img"] = std::make_pair(QRegExp("\\[img\\](.*)\\[/img\\]"), " "); - m_TagMap["img="] = std::make_pair(QRegExp("\\[img=([^\\]]*)\\](.*)\\[/img\\]"), " "); + m_TagMap["img"] = std::make_pair(QRegExp("\\[img\\](.*)\\[/img\\]"), + "\\1"); + m_TagMap["img="] = std::make_pair(QRegExp("\\[img=([^\\]]*)\\](.*)\\[/img\\]"), + "\\2"); m_TagMap["email="] = std::make_pair(QRegExp("\\[email=\"?([^\\]]*)\"?\\](.*)\\[/email\\]"), "\\2"); m_TagMap["youtube"] = std::make_pair(QRegExp("\\[youtube\\](.*)\\[/youtube\\]"), "http://www.youtube.com/v/\\1"); + + // make all patterns non-greedy and case-insensitive + for (TagMap::iterator iter = m_TagMap.begin(); iter != m_TagMap.end(); ++iter) { + iter->second.first.setCaseSensitivity(Qt::CaseInsensitive); + iter->second.first.setMinimal(true); + } + + // this tag is in fact greedy + m_TagMap["*"] = std::make_pair(QRegExp("\\[\\*\\](.*)"), + "
  • \\1
  • "); + m_ColorMap.insert(std::make_pair("red", "FF0000")); m_ColorMap.insert(std::make_pair("green", "00FF00")); m_ColorMap.insert(std::make_pair("blue", "0000FF")); @@ -170,13 +202,13 @@ private: m_ColorMap.insert(std::make_pair("cyan", "00FFFF")); m_ColorMap.insert(std::make_pair("magenta", "FF00FF")); m_ColorMap.insert(std::make_pair("brown", "A52A2A")); - m_ColorMap.insert(std::make_pair("orange", "FFCC00")); - - // make all patterns non-greedy and case-insensitive - for (TagMap::iterator iter = m_TagMap.begin(); iter != m_TagMap.end(); ++iter) { - iter->second.first.setCaseSensitivity(Qt::CaseInsensitive); - iter->second.first.setMinimal(true); - } + m_ColorMap.insert(std::make_pair("orange", "FFA500")); + m_ColorMap.insert(std::make_pair("gold", "FFD700")); + m_ColorMap.insert(std::make_pair("deepskyblue", "00BFFF")); + m_ColorMap.insert(std::make_pair("salmon", "FA8072")); + m_ColorMap.insert(std::make_pair("dodgerblue", "1E90FF")); + m_ColorMap.insert(std::make_pair("greenyellow", "ADFF2F")); + m_ColorMap.insert(std::make_pair("peru", "CD853F")); } private: @@ -209,18 +241,23 @@ QString convertToHTML(const QString &inputParam) // append everything between the previous tag-block and the current one result.append(input.midRef(lastBlock, pos - lastBlock)); - // convert the tag and content if necessary - int length = -1; - QString replacement = BBCodeMap::instance().convertTag(input.mid(pos), length); - if (length != 0) { - QString temp = convertToHTML(replacement); - result.append(temp); - // length contains the number of characters in the original tag - pos += length; + if ((pos < (input.size() - 1)) && (input.at(pos + 1) == '/')) { + // skip invalid end tag + int tagEnd = input.indexOf(']', pos) + 1; + pos = tagEnd; } else { - // nothing replaced - result.append('['); - ++pos; + // convert the tag and content if necessary + int length = -1; + QString replacement = BBCodeMap::instance().convertTag(input.mid(pos), length); + if (length != 0) { + result.append(convertToHTML(replacement)); + // length contains the number of characters in the original tag + pos += length; + } else { + // nothing replaced + result.append('['); + ++pos; + } } lastBlock = pos; } diff --git a/src/categories.cpp b/src/categories.cpp index 4c851338..28b1f4a2 100644 --- a/src/categories.cpp +++ b/src/categories.cpp @@ -104,7 +104,11 @@ void CategoryFactory::reset() { m_Categories.clear(); m_IDMap.clear(); - addCategory(0, "None", MakeVector(2, 28, 87), 0); + // 28 = + // 43 = Savegames (makes no sense to install them through MO) + // 45 = Videos and trailers + // 87 = Miscelanous + addCategory(0, "None", MakeVector(4, 28, 43, 45, 87), 0); } @@ -189,24 +193,38 @@ void CategoryFactory::loadDefaultCategories() // mods appear in the combo box addCategory(1, "Animations", MakeVector(1, 51), 0); addCategory(2, "Armour", MakeVector(1, 54), 0); - addCategory(3, "Audio", MakeVector(1, 61), 0); + addCategory(3, "Sound & Music", MakeVector(1, 61), 0); addCategory(5, "Clothing", MakeVector(1, 60), 0); addCategory(6, "Collectables", MakeVector(1, 92), 0); - addCategory(7, "Creatures", MakeVector(2, 83, 65), 0); + addCategory(28, "Companions", MakeVector(2, 66, 96), 0); + addCategory(7, "Creatures & Mounts", MakeVector(2, 83, 65), 0); addCategory(8, "Factions", MakeVector(1, 25), 0); addCategory(9, "Gameplay", MakeVector(1, 24), 0); addCategory(10, "Hair", MakeVector(1, 26), 0); addCategory(11, "Items", MakeVector(2, 27, 85), 0); - addCategory(19, "Weapons", MakeVector(2, 36, 55), 11); + addCategory(32, "Mercantile", MakeVector(1, 69), 0); + addCategory(19, "Weapons", MakeVector(1, 55), 11); + addCategory(36, "Weapon & Armour Sets", MakeVector(1, 39), 11); addCategory(12, "Locations", MakeVector(7, 22, 30, 70, 88, 89, 90, 91), 0); + addCategory(31, "Landscape Changes", MakeVector(1, 58), 0); addCategory(4, "Cities", MakeVector(1, 53), 12); + addCategory(29, "Environment", MakeVector(1, 74), 0); + addCategory(30, "Immersion", MakeVector(1, 78), 0); + addCategory(25, "Castles & Mansions", MakeVector(1, 68), 23); addCategory(20, "Magic", MakeVector(3, 75, 93, 94), 0); addCategory(21, "Models & Textures", MakeVector(1, 29), 0); + addCategory(33, "Modders resources", MakeVector(1, 82), 0); addCategory(13, "NPCs", MakeVector(1, 33), 0); addCategory(14, "Patches", MakeVector(2, 79, 84), 0); + addCategory(24, "Bugfixes", MakeVector(1, 95), 0); + addCategory(35, "Utilities", MakeVector(1, 39), 0); + addCategory(26, "Cheats", MakeVector(1, 40), 0); + addCategory(23, "Player Homes", MakeVector(1, 67), 0); addCategory(15, "Quests", MakeVector(1, 35), 0); addCategory(16, "Races & Classes", MakeVector(1, 34), 0); + addCategory(27, "Combat", MakeVector(1, 77), 0); addCategory(22, "Skills", MakeVector(1, 73), 0); + addCategory(34, "Stealth", MakeVector(1, 76), 0); addCategory(17, "UI", MakeVector(1, 42), 0); addCategory(18, "Visuals", MakeVector(1, 62), 0); } diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 856ca79c..bc31adf4 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -685,7 +685,7 @@ void DownloadManager::queryInfo(int index) return; } - if (info->m_FileInfo->modID == 0UL) { + if (info->m_FileInfo->modID <= 0) { QString fileName = getFileName(index); QString ignore; NexusInterface::interpretNexusFileName(fileName, ignore, info->m_FileInfo->modID, true); diff --git a/src/editexecutablesdialog.cpp b/src/editexecutablesdialog.cpp index 02abf30e..0e3aa55b 100644 --- a/src/editexecutablesdialog.cpp +++ b/src/editexecutablesdialog.cpp @@ -132,7 +132,7 @@ void EditExecutablesDialog::on_browseButton_clicked() if (::FindExecutableW(binaryNameW.c_str(), NULL, buffer) > (HINSTANCE)32) { DWORD binaryType = 0UL; if (!::GetBinaryTypeW(binaryNameW.c_str(), &binaryType)) { - qDebug("failed to determine binary type: %lu", ::GetLastError()); + qDebug("failed to determine binary type of \"%ls\": %lu", binaryNameW.c_str(), ::GetLastError()); } else if (binaryType == SCS_32BIT_BINARY) { binaryPath = ToQString(buffer); } diff --git a/src/main.cpp b/src/main.cpp index a21f41e4..3198208a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -311,9 +311,19 @@ int main(int argc, char *argv[]) } QString dataPath = instanceID.isEmpty() ? application.applicationDirPath() - : QDir::fromNativeSeparators(QDesktopServices::storageLocation(QDesktopServices::DataLocation)) + "/" + instanceID; + : QDir::fromNativeSeparators( +#if QT_VERSION >= 0x050000 + QStandardPaths::writableLocation(QStandardPaths::DataLocation) +#else + QDesktopServices::storageLocation(QDesktopServices::DataLocation) +#endif + ) + "/" + instanceID; application.setProperty("dataPath", dataPath); +#if QT_VERSION >= 0x050000 + qDebug("ssl support: %d", QSslSocket::supportsSsl()); +#endif + qDebug("data path: %s", qPrintable(dataPath)); if (!QDir(dataPath).exists()) { if (!QDir().mkpath(dataPath)) { diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 0d9fad5d..984d8cfd 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -97,9 +97,6 @@ along with Mod Organizer. If not, see . #include #include #include -#include -#include -#include #include #include #include @@ -114,8 +111,13 @@ along with Mod Organizer. If not, see . #include #include #include +#ifndef Q_MOC_RUN #include #include +#include +#include +#include +#endif #include #ifdef TEST_MODELS @@ -313,8 +315,6 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget connect(&m_Updater, SIGNAL(updateAvailable()), this, SLOT(updateAvailable())); connect(&m_Updater, SIGNAL(motdAvailable(QString)), this, SLOT(motdReceived(QString))); -// connect(ExitProxy::instance(), SIGNAL(exit()), this, SLOT(close())); - connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginSuccessful(bool)), this, SLOT(loginSuccessful(bool))); connect(NexusInterface::instance()->getAccessManager(), SIGNAL(loginFailed(QString)), this, SLOT(loginFailed(QString))); connect(NexusInterface::instance(), SIGNAL(requestNXMDownload(QString)), this, SLOT(downloadRequestedNXM(QString))); @@ -367,6 +367,8 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget MainWindow::~MainWindow() { + m_AboutToRun.disconnect_all_slots(); + m_ModInstalled.disconnect_all_slots(); m_RefresherThread.exit(); m_RefresherThread.wait(); m_IntegratedBrowser.close(); @@ -888,6 +890,8 @@ void MainWindow::closeEvent(QCloseEvent* event) storeSettings(); +// unloadPlugins(); + // profile has to be cleaned up before the modinfo-buffer is cleared delete m_CurrentProfile; m_CurrentProfile = NULL; @@ -895,6 +899,7 @@ void MainWindow::closeEvent(QCloseEvent* event) ModInfo::clear(); LogBuffer::cleanQuit(); m_ModList.setProfile(NULL); + NexusInterface::instance()->cleanup(); } @@ -1175,7 +1180,9 @@ bool MainWindow::registerPlugin(QObject *plugin, const QString &fileName) IPluginDiagnose *diagnose = qobject_cast(plugin); if (diagnose != NULL) { m_DiagnosisPlugins.push_back(diagnose); - diagnose->onInvalidated([&] () { this->scheduleUpdateButton(); }); + m_DiagnosisConnections.push_back( + diagnose->onInvalidated([&] () { this->scheduleUpdateButton(); }) + ); } } { // mod page plugin @@ -1204,6 +1211,7 @@ bool MainWindow::registerPlugin(QObject *plugin, const QString &fileName) IPluginPreview *preview = qobject_cast(plugin); if (verifyPlugin(preview)) { m_PreviewGenerator.registerPlugin(preview); + return true; } } { // proxy plugins @@ -1242,13 +1250,41 @@ bool MainWindow::registerPlugin(QObject *plugin, const QString &fileName) return false; } - -void MainWindow::loadPlugins() +void MainWindow::unloadPlugins() { + // disconnect all slots before unloading plugins so plugins don't have to take care of that + m_AboutToRun.disconnect_all_slots(); + m_ModInstalled.disconnect_all_slots(); + m_ModList.disconnectSlots(); + m_PluginList.disconnectSlots(); + m_DiagnosisPlugins.clear(); + foreach (const boost::signals2::connection &connection, m_DiagnosisConnections) { + connection.disconnect(); + } + m_DiagnosisConnections.clear(); + m_Settings.clearPlugins(); + if (ui->actionTool->menu() != NULL) { + ui->actionTool->menu()->clear(); + } + + foreach (QPluginLoader *loader, m_PluginLoaders) { + qDebug("unloading %s", qPrintable(loader->fileName())); + if (!loader->unload()) { + qDebug("failed to unload %s: %s", qPrintable(loader->fileName()), qPrintable(loader->errorString())); + } + delete loader; + } + m_PluginLoaders.clear(); +} + +void MainWindow::loadPlugins() +{ + unloadPlugins(); + foreach (QObject *plugin, QPluginLoader::staticInstances()) { registerPlugin(plugin, ""); } @@ -1287,14 +1323,15 @@ void MainWindow::loadPlugins() loadCheck.flush(); QString pluginName = iter.filePath(); if (QLibrary::isLibrary(pluginName)) { - QPluginLoader pluginLoader(pluginName); - if (pluginLoader.instance() == NULL) { + QPluginLoader *pluginLoader = new QPluginLoader(pluginName, this); + if (pluginLoader->instance() == NULL) { m_UnloadedPlugins.push_back(pluginName); qCritical("failed to load plugin %s: %s", - qPrintable(pluginName), qPrintable(pluginLoader.errorString())); + qPrintable(pluginName), qPrintable(pluginLoader->errorString())); } else { - if (registerPlugin(pluginLoader.instance(), pluginName)) { + if (registerPlugin(pluginLoader->instance(), pluginName)) { qDebug("loaded plugin \"%s\"", qPrintable(pluginName)); + m_PluginLoaders.push_back(pluginLoader); } else { m_UnloadedPlugins.push_back(pluginName); qWarning("plugin \"%s\" failed to load", qPrintable(pluginName)); @@ -2214,6 +2251,8 @@ void MainWindow::storeSettings() void MainWindow::on_btnRefreshData_clicked() { if (!m_DirectoryUpdate) { + // save the mod list so changes don't get lost + m_CurrentProfile->writeModlistNow(true); refreshDirectoryStructure(); } else { qDebug("directory update"); @@ -4315,13 +4354,10 @@ 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")) { - if (m_CurrentLanguage != "en-US") { + if ((m_CurrentLanguage != "en-US") && (m_CurrentLanguage != "en_US")) { qWarning("localization file %s not found", qPrintable(fileName)); } // we don't actually expect localization files for english } @@ -4485,7 +4521,7 @@ int MainWindow::getBinaryExecuteInfo(const QFileInfo &targetInfo, if (::FindExecutableW(targetPathW.c_str(), NULL, buffer) > (HINSTANCE)32) { DWORD binaryType = 0UL; if (!::GetBinaryTypeW(targetPathW.c_str(), &binaryType)) { - qDebug("failed to determine binary type: %lu", ::GetLastError()); + qDebug("failed to determine binary type of \"%ls\": %lu", targetPathW.c_str(), ::GetLastError()); } else if (binaryType == SCS_32BIT_BINARY) { binaryPath = ToQString(buffer); } @@ -5088,9 +5124,6 @@ void MainWindow::on_bsaList_itemChanged(QTreeWidgetItem*, int) void MainWindow::on_actionProblems_triggered() { -// QString problemDescription; -// checkForProblems(problemDescription); -// QMessageBox::information(this, tr("Problems"), problemDescription); ProblemsDialog problems(m_DiagnosisPlugins, this); if (problems.hasProblems()) { problems.exec(); @@ -5594,7 +5627,11 @@ void MainWindow::on_bossButton_clicked() QStringList temp = report.split("?"); QUrl url = QUrl::fromLocalFile(temp.at(0)); if (temp.size() > 1) { +#if QT_VERSION >= 0x050000 + url.setQuery(temp.at(1).toUtf8()); +#else url.setEncodedQuery(temp.at(1).toUtf8()); +#endif } m_IntegratedBrowser.openUrl(url); } diff --git a/src/mainwindow.h b/src/mainwindow.h index 22152f1c..8bd663ac 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -29,6 +29,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include "executableslist.h" #include "modlist.h" #include "pluginlist.h" @@ -53,7 +54,9 @@ along with Mod Organizer. If not, see . #include "browserdialog.h" #include #include +#ifndef Q_MOC_RUN #include +#endif namespace Ui { class MainWindow; @@ -139,6 +142,8 @@ public: void updateModInDirectoryStructure(unsigned int index, ModInfo::Ptr modInfo); + QString getOriginDisplayName(int originID); + void unloadPlugins(); public slots: void refreshLists(); @@ -191,6 +196,7 @@ private: void registerPluginTool(MOBase::IPluginTool *tool); void registerModPage(MOBase::IPluginModPage *modPage); bool registerPlugin(QObject *pluginObj, const QString &fileName); + bool unregisterPlugin(QObject *pluginObj, const QString &fileName); void updateToolBar(); void activateSelectedProfile(); @@ -373,8 +379,10 @@ private: MOBase::IGameInfo *m_GameInfo; std::vector m_DiagnosisPlugins; + std::vector m_DiagnosisConnections; std::vector m_ModPages; std::vector m_UnloadedPlugins; + std::vector m_PluginLoaders; QFile m_PluginsCheck; diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 9558e03b..4ab6d142 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -569,9 +569,7 @@ void ModInfoRegular::saveMeta() metaFile.setValue("newestVersion", m_NewestVersion.canonicalString()); metaFile.setValue("ignoredVersion", m_IgnoredVersion.canonicalString()); metaFile.setValue("version", m_Version.canonicalString()); - if (m_NexusID != -1) { - metaFile.setValue("modid", m_NexusID); - } + metaFile.setValue("modid", m_NexusID); metaFile.setValue("notes", m_Notes); metaFile.setValue("nexusDescription", m_NexusDescription); metaFile.setValue("lastNexusQuery", m_LastNexusQuery.toString(Qt::ISODate)); diff --git a/src/modlist.cpp b/src/modlist.cpp index 681d880c..eedf1ec6 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -64,6 +64,12 @@ ModList::ModList(QObject *parent) m_ContentIcons[ModInfo::CONTENT_TEXTURE] = std::make_tuple(QIcon(":/MO/gui/content/texture"), ":/MO/gui/content/texture", tr("Textures")); } +ModList::~ModList() +{ + m_ModStateChanged.disconnect_all_slots(); + m_ModMoved.disconnect_all_slots(); +} + void ModList::setProfile(Profile *profile) { m_Profile = profile; @@ -653,6 +659,11 @@ void ModList::modInfoChanged(ModInfo::Ptr info) } } +void ModList::disconnectSlots() { + m_ModMoved.disconnect_all_slots(); + m_ModStateChanged.disconnect_all_slots(); +} + IModList::ModStates ModList::state(unsigned int modIndex) const { IModList::ModStates result; diff --git a/src/modlist.h b/src/modlist.h index 6116a913..632689c6 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -32,7 +32,9 @@ along with Mod Organizer. If not, see . #include #include #include +#ifndef Q_MOC_RUN #include +#endif #include #include #include @@ -73,6 +75,8 @@ public: **/ ModList(QObject *parent = NULL); + ~ModList(); + /** * @brief set the profile used for status information * @@ -103,6 +107,8 @@ public: void modInfoAboutToChange(ModInfo::Ptr info); void modInfoChanged(ModInfo::Ptr info); + void disconnectSlots(); + public: /// \copydoc MOBase::IModList::displayName diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index d844cd39..30221f4b 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -145,30 +145,25 @@ QAtomicInt NexusInterface::NXMRequestInfo::s_NextID(0); NexusInterface::NexusInterface() : m_NMMVersion() { - atexit(&cleanup); - VS_FIXEDFILEINFO version = GetFileVersion(ToWString(QApplication::applicationFilePath())); m_MOVersion = VersionInfo(version.dwFileVersionMS >> 16, version.dwFileVersionMS & 0xFFFF, version.dwFileVersionLS >> 16); m_AccessManager = new NXMAccessManager(this, m_MOVersion.displayString()); - m_DiskCache = new QNetworkDiskCache(this); connect(m_AccessManager, SIGNAL(requestNXMDownload(QString)), this, SLOT(downloadRequestedNXM(QString))); } - -void NexusInterface::cleanup() -{ -} - - NXMAccessManager *NexusInterface::getAccessManager() { return m_AccessManager; } +NexusInterface::~NexusInterface() +{ + cleanup(); +} NexusInterface *NexusInterface::instance() { @@ -176,14 +171,12 @@ NexusInterface *NexusInterface::instance() return &s_Instance; } - void NexusInterface::setCacheDirectory(const QString &directory) { m_DiskCache->setCacheDirectory(directory); m_AccessManager->setCache(m_DiskCache); } - void NexusInterface::setNMMVersion(const QString &nmmVersion) { m_NMMVersion = nmmVersion; @@ -378,6 +371,14 @@ bool NexusInterface::requiresLogin(const NXMRequestInfo &info) || (info.m_Type == NXMRequestInfo::TYPE_DOWNLOADURL); } +void NexusInterface::cleanup() +{ +// delete m_AccessManager; +// delete m_DiskCache; + m_AccessManager = nullptr; + m_DiskCache = nullptr; +} + void NexusInterface::nextRequest() { if ((m_ActiveRequest.size() >= MAX_ACTIVE_DOWNLOADS) diff --git a/src/nexusinterface.h b/src/nexusinterface.h index cc54daa9..af2f8c75 100644 --- a/src/nexusinterface.h +++ b/src/nexusinterface.h @@ -130,6 +130,8 @@ class NexusInterface : public QObject public: + ~NexusInterface(); + static NexusInterface *instance(); /** @@ -137,6 +139,11 @@ public: **/ NXMAccessManager *getAccessManager(); + /** + * @brief cleanup this interface. this is destructive, afterwards it can't be used again + */ + void cleanup(); + /** * @brief request description for a mod * @@ -301,7 +308,6 @@ private: void nextRequest(); void requestFinished(std::list::iterator iter); bool requiresLogin(const NXMRequestInfo &info); - static void cleanup(); private: diff --git a/src/organizer.pro b/src/organizer.pro index bffd1e16..b24586e6 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -9,11 +9,15 @@ TARGET = ModOrganizer TEMPLATE = app greaterThan(QT_MAJOR_VERSION, 4) { - QT += core gui widgets network xml sql xmlpatterns qml quick script webkit + QT += core gui widgets network xml sql xmlpatterns qml declarative script webkit webkitwidgets } else { QT += core gui network xml declarative script sql xmlpatterns webkit } +!include(../LocalPaths.pri) { + message("paths to required libraries need to be set up in LocalPaths.pri") +} + SOURCES += \ transfersavesdialog.cpp \ syncoverwritedialog.cpp \ @@ -248,9 +252,9 @@ LIBS += -lDbgHelp #DEFINES += TEST_MODELS -INCLUDEPATH += ../shared ../archive ../uibase ../bsatk ../esptk ../boss_modified/boss-api "$(BOOSTPATH)" +INCLUDEPATH += ../shared ../archive ../uibase ../bsatk ../esptk ../boss_modified/boss-api "$${BOOSTPATH}" -LIBS += -L"$(BOOSTPATH)/stage/lib" +LIBS += -L"$${BOOSTPATH}/stage/lib" CONFIG(debug, debug|release) { LIBS += -L$$OUT_PWD/../shared/debug @@ -258,7 +262,8 @@ CONFIG(debug, debug|release) { LIBS += -L$$OUT_PWD/../uibase/debug LIBS += -L$$OUT_PWD/../boss_modified/debug LIBS += -lDbgHelp - PRE_TARGETDEPS += $$OUT_PWD/../shared/debug/mo_shared.lib \ + PRE_TARGETDEPS += \ + $$OUT_PWD/../shared/debug/mo_shared.lib \ $$OUT_PWD/../bsatk/debug/bsatk.lib } else { LIBS += -L$$OUT_PWD/../shared/release @@ -266,20 +271,19 @@ CONFIG(debug, debug|release) { LIBS += -L$$OUT_PWD/../uibase/release LIBS += -L$$OUT_PWD/../boss_modified/release QMAKE_CXXFLAGS += /Zi# /GL -# QMAKE_CXXFLAGS -= -O2 QMAKE_LFLAGS += /DEBUG# /LTCG /OPT:REF /OPT:ICF - PRE_TARGETDEPS += $$OUT_PWD/../shared/release/mo_shared.lib \ + PRE_TARGETDEPS += \ + $$OUT_PWD/../shared/release/mo_shared.lib \ $$OUT_PWD/../bsatk/release/bsatk.lib } #QMAKE_CXXFLAGS_WARN_ON -= -W3 #QMAKE_CXXFLAGS_WARN_ON += -W4 -QMAKE_CXXFLAGS += -wd4127 -wd4512 -wd4189 +QMAKE_CXXFLAGS += -wd4100 -wd4127 -wd4512 -wd4189 CONFIG += embed_manifest_exe # QMAKE_CXXFLAGS += /analyze - # QMAKE_LFLAGS += /MANIFESTUAC:\"level=\'highestAvailable\' uiAccess=\'false\'\" TRANSLATIONS = organizer_de.ts \ @@ -313,9 +317,9 @@ TRANSLATIONS = organizer_de.ts \ LIBS += -lmo_shared -luibase -lshell32 -lole32 -luser32 -ladvapi32 -lgdi32 -lPsapi -lVersion -lbsatk -lshlwapi -LIBS += -L"$(ZLIBPATH)/build" -lzlibstatic +LIBS += -L"$${ZLIBPATH}/build" -lzlibstatic -DEFINES += UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS NOMINMAX +DEFINES += UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS _SCL_SECURE_NO_WARNINGS NOMINMAX DEFINES += BOOST_DISABLE_ASSERTS NDEBUG #DEFINES += QMLJSDEBUGGER @@ -324,40 +328,40 @@ HGID = $$system(hg id -i) DEFINES += HGID=\\\"$${HGID}\\\" CONFIG(debug, debug|release) { - OUTDIR = $$OUT_PWD/debug + SRCDIR = $$OUT_PWD/debug DSTDIR = $$PWD/../../outputd } else { - OUTDIR = $$OUT_PWD/release + SRCDIR = $$OUT_PWD/release DSTDIR = $$PWD/../../output } -SRCDIR = $$PWD +BASEDIR = $$PWD +BASEDIR ~= s,/,$$QMAKE_DIR_SEP,g SRCDIR ~= s,/,$$QMAKE_DIR_SEP,g -OUTDIR ~= s,/,$$QMAKE_DIR_SEP,g DSTDIR ~= s,/,$$QMAKE_DIR_SEP,g -QMAKE_POST_LINK += xcopy /y /I $$quote($$OUTDIR\\ModOrganizer*.exe) $$quote($$DSTDIR) $$escape_expand(\\n) -QMAKE_POST_LINK += xcopy /y /I $$quote($$OUTDIR\\ModOrganizer*.pdb) $$quote($$DSTDIR) $$escape_expand(\\n) -QMAKE_POST_LINK += xcopy /y /s /I $$quote($$SRCDIR\\stylesheets) $$quote($$DSTDIR)\\stylesheets $$escape_expand(\\n) -QMAKE_POST_LINK += xcopy /y /s /I $$quote($$SRCDIR\\tutorials) $$quote($$DSTDIR)\\tutorials $$escape_expand(\\n) -QMAKE_POST_LINK += xcopy /y /s /I $$quote($$SRCDIR\\*.qm) $$quote($$DSTDIR)\\translations $$escape_expand(\\n) +QMAKE_POST_LINK += xcopy /y /I $$quote($$SRCDIR\\ModOrganizer*.exe) $$quote($$DSTDIR) $$escape_expand(\\n) +QMAKE_POST_LINK += xcopy /y /I $$quote($$SRCDIR\\ModOrganizer*.pdb) $$quote($$DSTDIR) $$escape_expand(\\n) +QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\stylesheets) $$quote($$DSTDIR)\\stylesheets $$escape_expand(\\n) +QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\tutorials) $$quote($$DSTDIR)\\tutorials $$escape_expand(\\n) +QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\*.qm) $$quote($$DSTDIR)\\translations $$escape_expand(\\n) CONFIG(debug, debug|release) { greaterThan(QT_MAJOR_VERSION, 4) { - QMAKE_POST_LINK += xcopy /y /s /I $$quote($$SRCDIR\\..\\dlls.*manifest.debug.qt5) $$quote($$DSTDIR)\\dlls $$escape_expand(\\n) + QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\..\\dlls.*manifest.debug.qt5) $$quote($$DSTDIR)\\dlls $$escape_expand(\\n) QMAKE_POST_LINK += copy /y $$quote($$DSTDIR\\dlls\\dlls.manifest.debug.qt5) $$quote($$DSTDIR\\dlls\\dlls.manifest) $$escape_expand(\\n) QMAKE_POST_LINK += del $$quote($$DSTDIR)\\dlls\\dlls.manifest.debug.qt5 $$escape_expand(\\n) } else { - QMAKE_POST_LINK += xcopy /y /s /I $$quote($$SRCDIR\\..\\dlls.*manifest.debug) $$quote($$DSTDIR)\\dlls $$escape_expand(\\n) + QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\..\\dlls.*manifest.debug) $$quote($$DSTDIR)\\dlls $$escape_expand(\\n) QMAKE_POST_LINK += copy /y $$quote($$DSTDIR)\\dlls\\dlls.manifest.debug $$quote($$DSTDIR)\\dlls\\dlls.manifest $$escape_expand(\\n) QMAKE_POST_LINK += del $$quote($$DSTDIR)\\dlls\\dlls.manifest.debug $$escape_expand(\\n) } } else { greaterThan(QT_MAJOR_VERSION, 4) { - QMAKE_POST_LINK += xcopy /y /s /I $$quote($$SRCDIR\\..\\dlls.*manifest.qt5) $$quote($$DSTDIR)\\dlls $$escape_expand(\\n) + QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\..\\dlls.*manifest.qt5) $$quote($$DSTDIR)\\dlls $$escape_expand(\\n) QMAKE_POST_LINK += copy /y $$quote($$DSTDIR\\dlls\\dlls.manifest.qt5) $$quote($$DSTDIR\\dlls\\dlls.manifest) $$escape_expand(\\n) QMAKE_POST_LINK += del $$quote($$DSTDIR)\\dlls\\dlls.manifest.qt5 $$escape_expand(\\n) } else { - QMAKE_POST_LINK += xcopy /y /s /I $$quote($$SRCDIR\\..\\dlls.*manifest) $$quote($$DSTDIR)\\dlls $$escape_expand(\\n) + QMAKE_POST_LINK += xcopy /y /s /I $$quote($$BASEDIR\\..\\dlls.*manifest) $$quote($$DSTDIR)\\dlls $$escape_expand(\\n) } } diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index f37f9e3a..43246b55 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -33,6 +33,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include #include @@ -95,6 +96,8 @@ PluginList::PluginList(QObject *parent) PluginList::~PluginList() { + m_Refreshed.disconnect_all_slots(); + m_PluginMoved.disconnect_all_slots(); } @@ -484,7 +487,7 @@ void PluginList::saveTo(const QString &pluginFileName if (deleterFile.commitIfDifferent(m_LastSaveHash[deleterFileName])) { qDebug("%s saved", qPrintable(QDir::toNativeSeparators(deleterFileName))); } - } else { + } else if (QFile::exists(deleterFileName)) { shellDelete(QStringList() << deleterFileName); } diff --git a/src/pluginlist.h b/src/pluginlist.h index 67d1b640..8c06fefd 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -26,15 +26,16 @@ along with Mod Organizer. If not, see . #include #include #include +#ifndef Q_MOC_RUN #include #include +#endif #include #include #include "pdll.h" #include - template class ChangeBracket { public: @@ -212,6 +213,11 @@ public: void refreshLoadOrder(); + void disconnectSlots() { + m_PluginMoved.disconnect_all_slots(); + m_Refreshed.disconnect_all_slots(); + } + public: virtual PluginState state(const QString &name) const; @@ -333,13 +339,11 @@ private: mutable QTimer m_SaveTimer; SignalRefreshed m_Refreshed; + SignalPluginMoved m_PluginMoved; QTemporaryFile m_TempFile; - SignalPluginMoved m_PluginMoved; }; - - #endif // PLUGINLIST_H diff --git a/src/settings.cpp b/src/settings.cpp index 6bf13ee0..65ea3a27 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -132,7 +132,6 @@ void Settings::registerPlugin(IPlugin *plugin) } } - QString Settings::obfuscate(const QString &password) const { QByteArray temp = password.toUtf8(); diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index e0914870..5d785822 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -936,10 +936,6 @@ void FileRegister::removeOriginMulti(std::set indices, int ori } } - if (removedFiles.size() > 0) { - log("%d files actually removed", removedFiles.size()); - } - // optimization: this is only called when disabling an origin and in this case we don't have // to remove the file from the origin diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h index 7836d719..096f373e 100644 --- a/src/shared/directoryentry.h +++ b/src/shared/directoryentry.h @@ -29,8 +29,10 @@ along with Mod Organizer. If not, see . #define WIN32_MEAN_AND_LEAN #include #include +#ifndef Q_MOC_RUN #include #include +#endif #include "util.h" diff --git a/src/shared/shared.pro b/src/shared/shared.pro index 84a9c274..69f6f2e6 100644 --- a/src/shared/shared.pro +++ b/src/shared/shared.pro @@ -11,33 +11,11 @@ TARGET = mo_shared TEMPLATE = lib CONFIG += staticlib -INCLUDEPATH += ../bsatk "$(BOOSTPATH)" - -# only for custom leak detection -DEFINES += TRACE_LEAKS -LIBS += -lDbgHelp - - -CONFIG(debug, debug|release) { - LIBS += -L$$OUT_PWD/../bsatk/debug - LIBS += -lDbgHelp - QMAKE_CXXFLAGS_DEBUG -= -Zi - QMAKE_CXXFLAGS += -Z7 - PRE_TARGETDEPS += $$OUT_PWD/../bsatk/debug/bsatk.lib -} else { - LIBS += -L$$OUT_PWD/../bsatk/release - PRE_TARGETDEPS += $$OUT_PWD/../bsatk/release/bsatk.lib +!include(../LocalPaths.pri) { + message("paths to required libraries need to be set up in LocalPaths.pri") } -LIBS += -lbsatk - -DEFINES += UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS - -DEFINES += BOOST_DISABLE_ASSERTS NDEBUG - -# QMAKE_CXXFLAGS += /analyze - SOURCES += \ inject.cpp \ windows_error.cpp \ @@ -66,3 +44,30 @@ HEADERS += \ appconfig.h \ appconfig.inc \ leaktrace.h + + +# only for custom leak detection +DEFINES += TRACE_LEAKS +LIBS += -lDbgHelp + + +CONFIG(debug, debug|release) { + LIBS += -L$$OUT_PWD/../bsatk/debug + LIBS += -lDbgHelp + QMAKE_CXXFLAGS_DEBUG -= -Zi + QMAKE_CXXFLAGS += -Z7 + PRE_TARGETDEPS += $$OUT_PWD/../bsatk/debug/bsatk.lib +} else { + LIBS += -L$$OUT_PWD/../bsatk/release + PRE_TARGETDEPS += $$OUT_PWD/../bsatk/release/bsatk.lib +} + +LIBS += -lbsatk + +DEFINES += UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS + +DEFINES += BOOST_DISABLE_ASSERTS NDEBUG + +# QMAKE_CXXFLAGS += /analyze + +INCLUDEPATH += ../bsatk "$${BOOSTPATH}" diff --git a/src/tutorials/TutorialOverlay.qml b/src/tutorials/TutorialOverlay.qml index 34959d7d..f2aad027 100644 --- a/src/tutorials/TutorialOverlay.qml +++ b/src/tutorials/TutorialOverlay.qml @@ -1,4 +1,3 @@ -// import QtQuick 1.0 // to target S60 5th Edition or Maemo 5 import QtQuick 1.1 import "tutorials.js" as Logic @@ -21,10 +20,11 @@ Rectangle { function enableBackground(enabled) { disabledBackground.visible = enabled } - +/* signal nextStep - onNextStep: { + onNextStep: {*/ + function nextStep() { if (step == 0) { Logic.init() } diff --git a/src/tutorials/tutorial_firststeps_main.js b/src/tutorials/tutorial_firststeps_main.js index 4f9c1a74..5ee4e790 100644 --- a/src/tutorials/tutorial_firststeps_main.js +++ b/src/tutorials/tutorial_firststeps_main.js @@ -15,19 +15,24 @@ function getTutorialSteps() function() { tutorial.text = qsTr("The highlighted button provides hints on solving problems MO recognized automatically.") - if (!tutorialControl.waitForAction("actionProblems")) { - highlightAction("actionProblems", false) - waitForClick() - } else { + if (tutorialControl.waitForAction("actionProblems")) { tutorial.text += qsTr("\nThere IS a problem now but you may want to hold off on fixing it until after completing the tutorial.") highlightAction("actionProblems", true) + } else { + highlightAction("actionProblems", false) + waitForClick() } }, function() { + console.log("next") tutorial.text = qsTr("This button provides multiple sources of information and further tutorials.") - highlightItem("actionHelp", true) - tutorialControl.waitForButton("actionHelp") + if (tutorialControl.waitForButton("actionHelp")) { + highlightItem("actionHelp", true) + } else { + console.error("help button broken") + waitForClick() + } }, function() { @@ -47,12 +52,16 @@ function getTutorialSteps() function() { tutorial.text = qsTr("Before we start installing mods, let's have a quick look at the settings.") manager.activateTutorial("SettingsDialog", "tutorial_firststeps_settings.js") - highlightAction("actionSettings", true) - tutorialControl.waitForAction("actionSettings") + if (tutorialControl.waitForAction("actionSettings")) { + highlightAction("actionSettings", true) + } else { + console.error("settings action broken") + waitForClick() + } }, function() { - tutorial.text = qsTr("Now it's time to install a few mods!" + tutorial.text = qsTr("Now it's time to install a few mods!" + "Please go along with this because we need a few mods installed to demonstrate other features") waitForClick() }, @@ -61,8 +70,12 @@ function getTutorialSteps() tutorial.text = qsTr("There are a few ways to get mods into ModOrganizer. " + "If you associated MO with NXM links in the settings you can now use your regular browser to send downloads from Nexus to MO. " + "Click on \"Nexus\" to open nexus, find a mod and click the green download buttons on Nexus saying \"Download with Manager\".") - highlightAction("actionNexus", true) - tutorialControl.waitForAction("actionNexus") + if (tutorialControl.waitForAction("actionNexus")) { + highlightAction("actionNexus", true) + } else { + console.error("browser action broken") + waitForClick() + } }, function() { -- cgit v1.3.1