From 7f4f4cafea5a196ddf824adf7c4e65cec5d44d88 Mon Sep 17 00:00:00 2001 From: Tannin Date: Tue, 17 Nov 2015 18:58:00 +0100 Subject: first work on interfacing with usvfs --- src/CMakeLists.txt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 22b725de..c508df52 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -83,6 +83,7 @@ SET(organizer_SRCS viewmarkingscrollbar.cpp plugincontainer.cpp organizercore.cpp + usvfsconnector.cpp shared/inject.cpp shared/windows_error.cpp @@ -173,6 +174,7 @@ SET(organizer_HDRS plugincontainer.h organizercore.h iuserinterface.h + usvfsconnector.h shared/inject.h shared/windows_error.h @@ -278,19 +280,27 @@ INCLUDE_DIRECTORIES(${project_path}/uibase/src ${project_path}/bsatk/src ${project_path}/esptk/src ${project_path}/archive/src - ${project_path}/plugin/game_features/src) + ${project_path}/plugin/game_features/src + ${project_path}/usvfs/usvfs) INCLUDE_DIRECTORIES(shared ${ZLIB_INCLUDE_DIRS}) LINK_DIRECTORIES(${lib_path} ${project_path}/zlib/lib) ADD_DEFINITIONS(-D_UNICODE -DUNICODE -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS) +IF("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + SET(usvfs_name usvfs_x64) +ELSE() + SET(usvfs_name usvfs_x86) +ENDIF() + ADD_EXECUTABLE(ModOrganizer WIN32 ${organizer_HDRS} ${organizer_SRCS} ${organizer_UIHDRS} ${organizer_RCS} ${organizer_RCCPPS}) TARGET_LINK_LIBRARIES(ModOrganizer Qt5::Widgets Qt5::WinExtras Qt5::WebKitWidgets ${Boost_LIBRARIES} zlibstatic uibase esptk bsatk + ${usvfs_name} Dbghelp advapi32 Version Shlwapi) SET_TARGET_PROPERTIES(ModOrganizer PROPERTIES COMPILE_FLAGS /GL) -- cgit v1.3.1 From 053ebc898f6e1eb16ea9366ab21796004d2347da Mon Sep 17 00:00:00 2001 From: Tannin Date: Tue, 17 Nov 2015 20:01:44 +0100 Subject: build system fix --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a52e500b..8e5950c8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -284,7 +284,7 @@ INCLUDE_DIRECTORIES(${project_path}/uibase/src ${project_path}/bsatk/src ${project_path}/esptk/src ${project_path}/archive/src - ${project_path}/usvfs/usvfs + ${project_path}/../usvfs/usvfs ${project_path}/game_features/src) INCLUDE_DIRECTORIES(shared ${ZLIB_INCLUDE_DIRS}) -- cgit v1.3.1 From a4b69c9ed8ba44aab26ff567e0f0eaf390e29cee Mon Sep 17 00:00:00 2001 From: Tannin Date: Thu, 19 Nov 2015 19:05:50 +0100 Subject: added fallout 4 support --- src/CMakeLists.txt | 2 + src/gameinfoimpl.cpp | 1 + src/shared/fallout4info.cpp | 117 ++++++++++++++++++++++++++++++++++++++++++++ src/shared/fallout4info.h | 80 ++++++++++++++++++++++++++++++ src/shared/gameinfo.cpp | 3 ++ src/shared/gameinfo.h | 1 + 6 files changed, 204 insertions(+) create mode 100644 src/shared/fallout4info.cpp create mode 100644 src/shared/fallout4info.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8e5950c8..030c0f18 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -92,6 +92,7 @@ SET(organizer_SRCS shared/gameinfo.cpp shared/oblivioninfo.cpp shared/fallout3info.cpp + shared/fallout4info.cpp shared/falloutnvinfo.cpp shared/util.cpp shared/skyriminfo.cpp @@ -183,6 +184,7 @@ SET(organizer_HDRS shared/gameinfo.h shared/oblivioninfo.h shared/fallout3info.h + shared/fallout4info.h shared/falloutnvinfo.h shared/util.h shared/skyriminfo.h diff --git a/src/gameinfoimpl.cpp b/src/gameinfoimpl.cpp index 025ce4e6..333fd029 100644 --- a/src/gameinfoimpl.cpp +++ b/src/gameinfoimpl.cpp @@ -36,6 +36,7 @@ IGameInfo::Type GameInfoImpl::type() const switch (GameInfo::instance().getType()) { case GameInfo::TYPE_OBLIVION: return IGameInfo::TYPE_OBLIVION; case GameInfo::TYPE_FALLOUT3: return IGameInfo::TYPE_FALLOUT3; + case GameInfo::TYPE_FALLOUT4: return IGameInfo::TYPE_FALLOUT4; case GameInfo::TYPE_FALLOUTNV: return IGameInfo::TYPE_FALLOUTNV; case GameInfo::TYPE_SKYRIM: return IGameInfo::TYPE_SKYRIM; default: throw MyException(QObject::tr("invalid game type %1").arg(GameInfo::instance().getType())); diff --git a/src/shared/fallout4info.cpp b/src/shared/fallout4info.cpp new file mode 100644 index 00000000..81143731 --- /dev/null +++ b/src/shared/fallout4info.cpp @@ -0,0 +1,117 @@ +/* +Copyright (C) 2012 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + +#include "fallout4info.h" +#include "util.h" +#include +#include +#include +#include "windows_error.h" +#include "error_report.h" +#define WIN32_LEAN_AND_MEAN +#include +#include + +namespace MOShared { + +Fallout4Info::Fallout4Info(const std::wstring &moDirectory, const std::wstring &moDataDirectory, const std::wstring &gameDirectory) + : GameInfo(moDirectory, moDataDirectory, gameDirectory) +{ + identifyMyGamesDirectory(L"fallout4"); +} + +bool Fallout4Info::identifyGame(const std::wstring &searchPath) +{ + return FileExists(searchPath, L"Fallout4.exe") && + FileExists(searchPath, L"Fallout4Launcher.exe"); +} + +std::wstring Fallout4Info::getRegPathStatic() +{ + HKEY key; + LONG errorcode = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Bethesda Softworks\\Fallout4", + 0, KEY_QUERY_VALUE, &key); + + if (errorcode != ERROR_SUCCESS) { + return std::wstring(); + } + + WCHAR temp[MAX_PATH]; + DWORD bufferSize = MAX_PATH; + + if (::RegQueryValueExW(key, L"Installed Path", nullptr, nullptr, (LPBYTE)temp, &bufferSize) == ERROR_SUCCESS) { + return std::wstring(temp); + } else { + return std::wstring(); + } +} + + +std::vector Fallout4Info::getDLCPlugins() +{ + return std::vector(); +} + +std::vector Fallout4Info::getSavegameAttachmentExtensions() +{ + return std::vector(); +} + +std::vector Fallout4Info::getIniFileNames() +{ + return boost::assign::list_of(L"fallout4.ini")(L"fallout4prefs.ini"); +} + +std::wstring Fallout4Info::getReferenceDataFile() +{ + return L"Fallout - Meshes.bsa"; +} + +std::wstring Fallout4Info::getNexusPage(bool nmmScheme) +{ + if (nmmScheme) { + return L"http://nmm.nexusmods.com/fallout4"; + } else { + return L"http://www.nexusmods.com/fallout4"; + } +} + +std::wstring Fallout4Info::getNexusInfoUrlStatic() +{ + return L"http://nmm.nexusmods.com/fallout4"; +} + +int Fallout4Info::getNexusModIDStatic() +{ + return 377160; +} + +bool Fallout4Info::rerouteToProfile(const wchar_t *fileName, const wchar_t*) +{ + static LPCWSTR profileFiles[] = { L"fallout4.ini", L"fallout4prefs.ini", L"plugins.txt", nullptr }; + + for (int i = 0; profileFiles[i] != nullptr; ++i) { + if (_wcsicmp(fileName, profileFiles[i]) == 0) { + return true; + } + } + return false; +} + +} // namespace MOShared diff --git a/src/shared/fallout4info.h b/src/shared/fallout4info.h new file mode 100644 index 00000000..98ce4092 --- /dev/null +++ b/src/shared/fallout4info.h @@ -0,0 +1,80 @@ +/* +Copyright (C) 2012 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + +#ifndef FALLOUT4INFO_H +#define FALLOUT4INFO_H + + +#include "gameinfo.h" + +namespace MOShared { + + +class Fallout4Info : public GameInfo +{ + + friend class GameInfo; + +public: + + virtual ~Fallout4Info() {} + + static std::wstring getRegPathStatic(); + virtual std::wstring getRegPath() { return getRegPathStatic(); } + virtual std::wstring getBinaryName() { return L"Fallout4.exe"; } + + virtual GameInfo::Type getType() { return TYPE_FALLOUT4; } + + virtual std::wstring getGameName() const { return L"Fallout 4"; } + virtual std::wstring getGameShortName() const { return L"Fallout4"; } + + virtual std::vector getDLCPlugins(); + virtual std::vector getSavegameAttachmentExtensions(); + + // file name of this games ini (no path) + virtual std::vector getIniFileNames(); + + virtual std::wstring getReferenceDataFile(); + + virtual std::wstring getNexusPage(bool nmmScheme = true); + static std::wstring getNexusInfoUrlStatic(); + virtual std::wstring getNexusInfoUrl() { return getNexusInfoUrlStatic(); } + static int getNexusModIDStatic(); + virtual int getNexusModID() { return getNexusModIDStatic(); } + virtual int getNexusGameID() { return 1151; } + + virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath); + + // get a list of executables (game binary and known-to-work 3rd party tools). All of these are relative to + // the game directory + //virtual std::vector getExecutables(); + + virtual std::wstring archiveListKey() { return L"SArchiveList"; } + +private: + + Fallout4Info(const std::wstring &moDirectory, const std::wstring &moDataDirectory, const std::wstring &gameDirectory); + + static bool identifyGame(const std::wstring &searchPath); + +}; + +} // namespace MOShared + +#endif // FALLOUT3INFO_H diff --git a/src/shared/gameinfo.cpp b/src/shared/gameinfo.cpp index 1f25cec1..db72653e 100644 --- a/src/shared/gameinfo.cpp +++ b/src/shared/gameinfo.cpp @@ -23,6 +23,7 @@ along with Mod Organizer. If not, see . #include "oblivioninfo.h" #include "fallout3info.h" +#include "fallout4info.h" #include "falloutnvinfo.h" #include "skyriminfo.h" #include "util.h" @@ -97,6 +98,8 @@ bool GameInfo::identifyGame(const std::wstring &moDirectory, const std::wstring s_Instance = new FalloutNVInfo(moDirectory, moDataDirectory, searchPath); } else if (SkyrimInfo::identifyGame(searchPath)) { s_Instance = new SkyrimInfo(moDirectory, moDataDirectory, searchPath); + } else if (Fallout4Info::identifyGame(searchPath)) { + s_Instance = new Fallout4Info(moDirectory, moDataDirectory, searchPath); } return s_Instance != nullptr; diff --git a/src/shared/gameinfo.h b/src/shared/gameinfo.h index a32b6b82..8ee8a8de 100644 --- a/src/shared/gameinfo.h +++ b/src/shared/gameinfo.h @@ -43,6 +43,7 @@ public: enum Type { TYPE_OBLIVION, TYPE_FALLOUT3, + TYPE_FALLOUT4, TYPE_FALLOUTNV, TYPE_SKYRIM }; -- cgit v1.3.1 From 495846534d560f825819b7ee391e8c461ce76e4f Mon Sep 17 00:00:00 2001 From: Tannin Date: Mon, 7 Dec 2015 20:09:34 +0100 Subject: - no longer displaying "not logged in". This was too confusing for some - fixed files missing from vfs if parent directory exists in real destination dir - implemented plugin api to access current profile - steam game detection now also works for 64-bit games - removed reference to archive tab from tutorial - usvfs log level is now taken from config - some cleanup --- src/CMakeLists.txt | 3 --- src/categories.cpp | 4 +++- src/mainwindow.cpp | 9 +++---- src/organizercore.cpp | 40 ++++++++++++++++++++++++++----- src/organizercore.h | 2 ++ src/organizerproxy.cpp | 5 ++++ src/organizerproxy.h | 3 +-- src/profile.h | 2 +- src/savegamegamebyro.h | 2 +- src/selfupdater.h | 2 ++ src/shared/fallout3info.cpp | 12 ---------- src/shared/fallout3info.h | 2 -- src/shared/fallout4info.cpp | 12 ---------- src/shared/fallout4info.h | 2 -- src/shared/falloutnvinfo.cpp | 13 ---------- src/shared/falloutnvinfo.h | 2 -- src/shared/gameinfo.cpp | 3 ++- src/shared/gameinfo.h | 2 -- src/shared/oblivioninfo.cpp | 12 ---------- src/shared/oblivioninfo.h | 2 -- src/shared/skyriminfo.cpp | 18 -------------- src/shared/skyriminfo.h | 2 -- src/spawn.cpp | 2 -- src/spawn.h | 2 +- src/tutorials/tutorial_firststeps_main.js | 36 ---------------------------- src/usvfsconnector.cpp | 12 +++++++++- 26 files changed, 66 insertions(+), 140 deletions(-) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 030c0f18..313f7506 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -279,9 +279,6 @@ SET(project_path "${default_project_path}" CACHE PATH "path to the other mo proj SET(lib_path "${project_path}/../../install/libs") -MESSAGE(STATUS ${lib_path}) -MESSAGE(STATUS ${project_path}) - INCLUDE_DIRECTORIES(${project_path}/uibase/src ${project_path}/bsatk/src ${project_path}/esptk/src diff --git a/src/categories.cpp b/src/categories.cpp index 400cc74b..f096a902 100644 --- a/src/categories.cpp +++ b/src/categories.cpp @@ -317,9 +317,11 @@ int CategoryFactory::getCategoryIndex(int ID) const int CategoryFactory::getCategoryID(const QString &name) const { - auto iter = std::find_if(m_Categories.begin(), m_Categories.end(), [name] (const Category &cat) -> bool { + auto iter = std::find_if(m_Categories.begin(), m_Categories.end(), + [name] (const Category &cat) -> bool { return cat.m_Name == name; }); + if (iter != m_Categories.end()) { return iter->m_ID; } else { diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 71013c3c..a3a5bf38 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -355,9 +355,7 @@ void MainWindow::updateWindowTitle(const QString &accountName, bool premium) ToQString(GameInfo::instance().getGameName()), m_OrganizerCore.getVersion().displayString()); - if (accountName.isEmpty()) { - title.append(" (not logged in)"); - } else { + if (!accountName.isEmpty()) { title.append(QString(" (%1%2)").arg(accountName, premium ? "*" : "")); } @@ -582,7 +580,7 @@ bool MainWindow::errorReported(QString &logFile) int MainWindow::checkForProblems() { - int numProblems = 0; + size_t numProblems = 0; for (IPluginDiagnose *diagnose : m_PluginContainer.plugins()) { numProblems += diagnose->activeProblems().size(); } @@ -4184,10 +4182,9 @@ void MainWindow::on_bossButton_clicked() HANDLE stdOutWrite = INVALID_HANDLE_VALUE; HANDLE stdOutRead = INVALID_HANDLE_VALUE; createStdoutPipe(&stdOutRead, &stdOutWrite); + m_OrganizerCore.prepareVFS(); HANDLE loot = startBinary(QFileInfo(qApp->applicationDirPath() + "/loot/lootcli.exe"), parameters.join(" "), - m_OrganizerCore.currentProfile()->name(), - m_OrganizerCore.settings().logLevel(), qApp->applicationDirPath() + "/loot", true, stdOutWrite); diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 83d468ef..89cdd4e6 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -13,6 +13,7 @@ #include "nxmaccessmanager.h" #include #include +#include #include #include #include @@ -279,7 +280,9 @@ bool OrganizerCore::testForSteam() bool success = false; while (!success) { processIDs.reset(new DWORD[currentSize]); - if (!::EnumProcesses(processIDs.get(), currentSize * sizeof(DWORD), &bytesReturned)) { + if (!::EnumProcesses(processIDs.get(), + static_cast(currentSize) * sizeof(DWORD), + &bytesReturned)) { qWarning("failed to determine if steam is running"); return true; } @@ -532,6 +535,11 @@ void OrganizerCore::createDefaultProfile() } } +void OrganizerCore::prepareVFS() +{ + m_USVFS.updateMapping(fileMapping()); +} + void OrganizerCore::setCurrentProfile(const QString &profileName) { if ((m_CurrentProfile != nullptr) && @@ -991,7 +999,7 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary, const QString & // TODO: should also pass arguments if (m_AboutToRun(binary.absoluteFilePath())) { m_USVFS.updateMapping(fileMapping()); - return startBinary(binary, arguments, profileName, m_Settings.logLevel(), currentDirectory, true); + return startBinary(binary, arguments, currentDirectory, true); } else { qDebug("start of \"%s\" canceled by plugin", qPrintable(binary.absoluteFilePath())); return INVALID_HANDLE_VALUE; @@ -1562,16 +1570,35 @@ void OrganizerCore::prepareStart() { std::vector OrganizerCore::fileMapping() { + // need to wait until directory structure + while (m_DirectoryUpdate) { + ::Sleep(100); + QCoreApplication::processEvents(); + } + IPluginGame *game = qApp->property("managed_game").value(); MappingType result = fileMapping( QDir::toNativeSeparators(game->dataDirectory().absolutePath()), "\\", directoryStructure(), directoryStructure()); + if (m_CurrentProfile->localSavesEnabled()) { + LocalSavegames *localSaves = game->feature(); + + if (localSaves != nullptr) { + MappingType saveMap = localSaves->mappings(currentProfile()->absolutePath() + "/saves"); + result.reserve(result.size() + saveMap.size()); + result.insert(result.end(), saveMap.begin(), saveMap.end()); + } + } + for (MOBase::IPluginFileMapper *mapper : m_PluginContainer->plugins()) { - MappingType pluginMap = mapper->mappings(); - result.reserve(result.size() + pluginMap.size()); - result.insert(result.end(), pluginMap.begin(), pluginMap.end()); + IPlugin *plugin = dynamic_cast(mapper); + if (plugin->isActive()) { + MappingType pluginMap = mapper->mappings(); + result.reserve(result.size() + pluginMap.size()); + result.insert(result.end(), pluginMap.begin(), pluginMap.end()); + } } return result; @@ -1607,9 +1634,10 @@ std::vector OrganizerCore::fileMapping( directoryEntry->getSubDirectories(current, end); for (; current != end; ++current) { int origin = (*current)->anyOrigin(); + /* if (origin == 0) { continue; - } + }*/ QString originPath = QString::fromStdWString(base->getOriginByID(origin).getPath()); diff --git a/src/organizercore.h b/src/organizercore.h index 1de1dfe8..74814461 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -126,6 +126,8 @@ public: MOBase::DelayedFileWriter &pluginsWriter() { return m_PluginListsWriter; } + void prepareVFS(); + public: MOBase::IGameInfo &gameInfo() const; MOBase::IModRepositoryBridge *createNexusBridge() const; diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp index 095cb0bb..665b15d1 100644 --- a/src/organizerproxy.cpp +++ b/src/organizerproxy.cpp @@ -155,6 +155,11 @@ QList OrganizerProxy::findFileInfos(const QString return m_Proxied->findFileInfos(path, filter); } +MOBase::IProfile *OrganizerProxy::profile() +{ + return m_Proxied->currentProfile(); +} + MOBase::IDownloadManager *OrganizerProxy::downloadManager() { return m_Proxied->downloadManager(); diff --git a/src/organizerproxy.h b/src/organizerproxy.h index fb502a7f..029abcaf 100644 --- a/src/organizerproxy.h +++ b/src/organizerproxy.h @@ -34,6 +34,7 @@ public: virtual QStringList findFiles(const QString &path, const std::function &filter) const; virtual QStringList getFileOrigins(const QString &fileName) const; virtual QList findFileInfos(const QString &path, const std::function &filter) const; + virtual MOBase::IProfile *profile(); virtual MOBase::IDownloadManager *downloadManager(); virtual MOBase::IPluginList *pluginList(); @@ -46,7 +47,6 @@ public: virtual bool onFinishedRun(const std::function &func); virtual bool onModInstalled(const std::function &func); - private: OrganizerCore *m_Proxied; @@ -55,5 +55,4 @@ private: }; - #endif // ORGANIZERPROXY_H diff --git a/src/profile.h b/src/profile.h index 342b6fa0..249ef618 100644 --- a/src/profile.h +++ b/src/profile.h @@ -210,7 +210,7 @@ public: * * @return number of mods for which the profile has status information **/ - unsigned int numMods() const { return m_ModStatus.size(); } + size_t numMods() const { return m_ModStatus.size(); } /** * @return the number of mods that can be enabled and where the priority can be modified diff --git a/src/savegamegamebyro.h b/src/savegamegamebyro.h index 029d478f..af866666 100644 --- a/src/savegamegamebyro.h +++ b/src/savegamegamebyro.h @@ -70,7 +70,7 @@ public: /** * @return number of plugins that were enabled when the save game was created **/ - int numPlugins() const { return m_Plugins.size(); } + size_t numPlugins() const { return m_Plugins.size(); } /** * retrieve the name of one of the plugins that were enabled when the save game diff --git a/src/selfupdater.h b/src/selfupdater.h index 143b05cb..bb9804e8 100644 --- a/src/selfupdater.h +++ b/src/selfupdater.h @@ -29,6 +29,8 @@ along with Mod Organizer. If not, see . #include #include +#include + class NexusInterface; diff --git a/src/shared/fallout3info.cpp b/src/shared/fallout3info.cpp index 0b369b50..a4b60410 100644 --- a/src/shared/fallout3info.cpp +++ b/src/shared/fallout3info.cpp @@ -107,16 +107,4 @@ int Fallout3Info::getNexusModIDStatic() return 16348; } -bool Fallout3Info::rerouteToProfile(const wchar_t *fileName, const wchar_t*) -{ - static LPCWSTR profileFiles[] = { L"fallout.ini", L"falloutprefs.ini", L"plugins.txt", nullptr }; - - for (int i = 0; profileFiles[i] != nullptr; ++i) { - if (_wcsicmp(fileName, profileFiles[i]) == 0) { - return true; - } - } - return false; -} - } // namespace MOShared diff --git a/src/shared/fallout3info.h b/src/shared/fallout3info.h index 0045581d..878adc2b 100644 --- a/src/shared/fallout3info.h +++ b/src/shared/fallout3info.h @@ -59,8 +59,6 @@ public: virtual int getNexusModID() { return getNexusModIDStatic(); } virtual int getNexusGameID() { return 120; } - virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath); - // get a list of executables (game binary and known-to-work 3rd party tools). All of these are relative to // the game directory //virtual std::vector getExecutables(); diff --git a/src/shared/fallout4info.cpp b/src/shared/fallout4info.cpp index 81143731..c9229793 100644 --- a/src/shared/fallout4info.cpp +++ b/src/shared/fallout4info.cpp @@ -102,16 +102,4 @@ int Fallout4Info::getNexusModIDStatic() return 377160; } -bool Fallout4Info::rerouteToProfile(const wchar_t *fileName, const wchar_t*) -{ - static LPCWSTR profileFiles[] = { L"fallout4.ini", L"fallout4prefs.ini", L"plugins.txt", nullptr }; - - for (int i = 0; profileFiles[i] != nullptr; ++i) { - if (_wcsicmp(fileName, profileFiles[i]) == 0) { - return true; - } - } - return false; -} - } // namespace MOShared diff --git a/src/shared/fallout4info.h b/src/shared/fallout4info.h index 98ce4092..4a28e867 100644 --- a/src/shared/fallout4info.h +++ b/src/shared/fallout4info.h @@ -59,8 +59,6 @@ public: virtual int getNexusModID() { return getNexusModIDStatic(); } virtual int getNexusGameID() { return 1151; } - virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath); - // get a list of executables (game binary and known-to-work 3rd party tools). All of these are relative to // the game directory //virtual std::vector getExecutables(); diff --git a/src/shared/falloutnvinfo.cpp b/src/shared/falloutnvinfo.cpp index 1203bd25..81a75af3 100644 --- a/src/shared/falloutnvinfo.cpp +++ b/src/shared/falloutnvinfo.cpp @@ -113,17 +113,4 @@ int FalloutNVInfo::getNexusModIDStatic() return 42572; } - -bool FalloutNVInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t*) -{ - static LPCWSTR profileFiles[] = { L"fallout.ini", L"falloutprefs.ini", L"plugins.txt", nullptr }; - - for (int i = 0; profileFiles[i] != nullptr; ++i) { - if (_wcsicmp(fileName, profileFiles[i]) == 0) { - return true; - } - } - return false; -} - } // namespace MOShared diff --git a/src/shared/falloutnvinfo.h b/src/shared/falloutnvinfo.h index e1a614d2..5712c2ba 100644 --- a/src/shared/falloutnvinfo.h +++ b/src/shared/falloutnvinfo.h @@ -92,8 +92,6 @@ public: virtual int getNexusModID() { return getNexusModIDStatic(); } virtual int getNexusGameID() { return 130; } - virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath); - // get a list of executables (game binary and known-to-work 3rd party tools). All of these are relative to // the game directory //virtual std::vector getExecutables(); diff --git a/src/shared/gameinfo.cpp b/src/shared/gameinfo.cpp index db72653e..d9dd9bb1 100644 --- a/src/shared/gameinfo.cpp +++ b/src/shared/gameinfo.cpp @@ -140,7 +140,8 @@ std::wstring GameInfo::getGameDirectory() const bool GameInfo::requiresSteam() const { - return FileExists(getGameDirectory() + L"\\steam_api.dll"); + return FileExists(getGameDirectory() + L"\\steam_api.dll") + || FileExists(getGameDirectory() + L"\\steam_api64.dll"); } std::wstring GameInfo::getLocalAppFolder() const diff --git a/src/shared/gameinfo.h b/src/shared/gameinfo.h index 8ee8a8de..0e0052d7 100644 --- a/src/shared/gameinfo.h +++ b/src/shared/gameinfo.h @@ -91,8 +91,6 @@ public: virtual int getNexusModID() = 0; virtual int getNexusGameID() = 0; - virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) = 0; - public: // initialise with the path to the mo directory (needs to be where hook.dll is stored). This diff --git a/src/shared/oblivioninfo.cpp b/src/shared/oblivioninfo.cpp index 16748f58..c60f8911 100644 --- a/src/shared/oblivioninfo.cpp +++ b/src/shared/oblivioninfo.cpp @@ -125,18 +125,6 @@ int OblivionInfo::getNexusModIDStatic() return 38277; } -bool OblivionInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t*) -{ - static LPCWSTR profileFiles[] = { L"oblivion.ini", L"oblivionprefs.ini", L"plugins.txt", nullptr }; - - for (int i = 0; profileFiles[i] != nullptr; ++i) { - if (_wcsicmp(fileName, profileFiles[i]) == 0) { - return true; - } - } - return false; -} - std::wstring OblivionInfo::getReferenceDataFile() { return L"Oblivion - Meshes.bsa"; diff --git a/src/shared/oblivioninfo.h b/src/shared/oblivioninfo.h index 87ba26ff..1ef5b48e 100644 --- a/src/shared/oblivioninfo.h +++ b/src/shared/oblivioninfo.h @@ -90,8 +90,6 @@ public: virtual int getNexusModID() { return getNexusModIDStatic(); } virtual int getNexusGameID() { return 101; } - virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath); - // get a list of executables (game binary and known-to-work 3rd party tools). All of these are relative to // the game directory //virtual std::vector getExecutables(); diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp index f66fcef4..acd72a3f 100644 --- a/src/shared/skyriminfo.cpp +++ b/src/shared/skyriminfo.cpp @@ -139,23 +139,5 @@ int SkyrimInfo::getNexusModIDStatic() return 1334; } -bool SkyrimInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) -{ - static LPCWSTR profileFiles[] = { L"skyrim.ini", L"skyrimprefs.ini", L"loadorder.txt", nullptr }; - - for (int i = 0; profileFiles[i] != nullptr; ++i) { - if (_wcsicmp(fileName, profileFiles[i]) == 0) { - return true; - } - } - - if ((_wcsicmp(fileName, L"plugins.txt") == 0) && - (m_AppData.empty() || (StrStrIW(fullPath, m_AppData.c_str()) != nullptr))) { - return true; - } - - return false; -} - } // namespace MOShared diff --git a/src/shared/skyriminfo.h b/src/shared/skyriminfo.h index 5951f910..732b9261 100644 --- a/src/shared/skyriminfo.h +++ b/src/shared/skyriminfo.h @@ -64,8 +64,6 @@ public: static int getNexusGameIDStatic() { return 110; } virtual int getNexusGameID() { return getNexusGameIDStatic(); } - virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath); - private: SkyrimInfo(const std::wstring &moDirectory, const std::wstring &moDataDirectory, const std::wstring &gameDirectory); diff --git a/src/spawn.cpp b/src/spawn.cpp index ad6707e3..14cdb4d4 100644 --- a/src/spawn.cpp +++ b/src/spawn.cpp @@ -126,8 +126,6 @@ static bool spawn(LPCWSTR binary, LPCWSTR arguments, LPCWSTR currentDirectory, HANDLE startBinary(const QFileInfo &binary, const QString &arguments, - const QString& profileName, - int logLevel, const QDir ¤tDirectory, bool hooked, HANDLE stdOut, diff --git a/src/spawn.h b/src/spawn.h index 63eda18d..c2d99bdb 100644 --- a/src/spawn.h +++ b/src/spawn.h @@ -60,7 +60,7 @@ private: * @todo is the profile name even used any more? * @todo is the hooked parameter used? **/ -HANDLE startBinary(const QFileInfo &binary, const QString &arguments, const QString &profileName, int logLevel, +HANDLE startBinary(const QFileInfo &binary, const QString &arguments, const QDir ¤tDirectory, bool hooked, HANDLE stdOut = INVALID_HANDLE_VALUE, HANDLE stdErr = INVALID_HANDLE_VALUE); diff --git a/src/tutorials/tutorial_firststeps_main.js b/src/tutorials/tutorial_firststeps_main.js index 2bc270d7..c47b5c0d 100644 --- a/src/tutorials/tutorial_firststeps_main.js +++ b/src/tutorials/tutorial_firststeps_main.js @@ -144,42 +144,6 @@ function getTutorialSteps() applicationWindow.modInfoDisplayed.connect(nextStep) }, - function() { - tutorial.text = qsTr("Another special type of files are BSAs. These are bundles of game resources. " - + "Please open the \"Archives\"-tab.") - if (tutorialControl.waitForTabOpen("tabWidget", 1)) { - highlightItem("tabWidget", true) - } else { - waitForClick() - } - }, - - function() { - tutorial.text = qsTr("These archives can be a real headache because the way bsas interact " - + "with non-bundled resources is complicated. The game can even crash if required " - + "archives are not loaded or ordered incorrectly.") - waitForClick() - }, - - function() { - tutorial.text = qsTr("MO applies some \"magic\" to make all BSAs that are checked in this list load in " - + "the correct order interleaved with the non-bundled resources.") - waitForClick() - }, - - function() { - tutorial.text = qsTr("You can disable this magic to make MO behave more like other tools. In this case " - + "their load order follows that of the corresponding plugin (.esp).") - highlightItem("managedArchiveLabel", false) - waitForClick() - }, - - function() { - tutorial.text = qsTr("Many BSAs will appear grayed out and enabled. These mods are loaded by the game engine " - + "automatically so they can't be disabled here.") - waitForClick() - }, - function() { tutorial.text = qsTr("Now you know how to download, install and enable mods.\n" + "It's important you always start the game from inside MO, otherwise " diff --git a/src/usvfsconnector.cpp b/src/usvfsconnector.cpp index a29d806e..23a51891 100644 --- a/src/usvfsconnector.cpp +++ b/src/usvfsconnector.cpp @@ -19,6 +19,7 @@ along with Mod Organizer. If not, see . #include "usvfsconnector.h" +#include "settings.h" #include #include #include @@ -84,12 +85,21 @@ void LogWorker::process() void LogWorker::exit() { m_QuitRequested = true; +} +LogLevel logLevel(int level) +{ + switch (level) { + case LogLevel::Info: return LogLevel::Info; + case LogLevel::Warning: return LogLevel::Warning; + case LogLevel::Error: return LogLevel::Error; + default: return LogLevel::Debug; + } } UsvfsConnector::UsvfsConnector() { - usvfs::Parameters params(SHMID, false, LogLevel::Debug); + usvfs::Parameters params(SHMID, false, logLevel(Settings::instance().logLevel())); InitLogging(false); ConnectVFS(¶ms); -- cgit v1.3.1 From 1bfc91046badb609261c78b4b5e03bba4dcb61bd Mon Sep 17 00:00:00 2001 From: Tannin Date: Mon, 28 Dec 2015 14:33:30 +0100 Subject: removed get-prefix from many getters. removed const from managed_game variable. other fixes --- src/CMakeLists.txt | 2 +- src/directoryrefresher.cpp | 2 +- src/downloadmanager.cpp | 2 +- src/loadmechanism.cpp | 18 +++++++++--------- src/mainwindow.cpp | 21 ++++++++++++--------- src/modinfo.cpp | 8 ++++---- src/modinfoforeign.cpp | 2 +- src/nexusinterface.cpp | 12 ++++++------ src/organizercore.cpp | 10 +++++----- src/organizercore.h | 4 ++-- src/pluginlist.cpp | 6 +++--- src/profile.cpp | 2 +- src/selfupdater.cpp | 8 ++++---- src/settings.cpp | 2 +- src/settingsdialog.cpp | 2 +- 15 files changed, 52 insertions(+), 49 deletions(-) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c55f9efe..7cd66739 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -321,7 +321,7 @@ TARGET_LINK_LIBRARIES(ModOrganizer SET_TARGET_PROPERTIES(ModOrganizer PROPERTIES COMPILE_FLAGS /GL) SET_TARGET_PROPERTIES(ModOrganizer PROPERTIES LINK_FLAGS_RELWITHDEBINFO - "/LTCG /INCREMENTAL:NO /LARGEADDRESSAWARE /OPT:REF /OPT:ICF") + "/LARGEADDRESSAWARE ${OPTIMIZE_LINK_FLAGS}") QT5_USE_MODULES(ModOrganizer Widgets Declarative Network WebKitWidgets) diff --git a/src/directoryrefresher.cpp b/src/directoryrefresher.cpp index f50a717e..4eac4103 100644 --- a/src/directoryrefresher.cpp +++ b/src/directoryrefresher.cpp @@ -144,7 +144,7 @@ void DirectoryRefresher::refresh() m_DirectoryStructure = new DirectoryEntry(L"data", nullptr, 0); - IPluginGame const *game = qApp->property("managed_game").value(); + IPluginGame *game = qApp->property("managed_game").value(); std::wstring dataDirectory = QDir::toNativeSeparators(game->dataDirectory().absolutePath()).toStdWString(); m_DirectoryStructure->addFromOrigin(L"data", dataDirectory, 0); diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index cbc1ba45..9d891475 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -450,7 +450,7 @@ void DownloadManager::addNXMDownload(const QString &url) { NXMUrl nxmInfo(url); - QString managedGame = m_ManagedGame->getGameShortName(); + QString managedGame = m_ManagedGame->gameShortName(); qDebug("add nxm download: %s", qPrintable(url)); if (nxmInfo.game().compare(managedGame, Qt::CaseInsensitive) != 0) { qDebug("download requested for wrong game (game: %s, url: %s)", qPrintable(managedGame), qPrintable(nxmInfo.game())); diff --git a/src/loadmechanism.cpp b/src/loadmechanism.cpp index 8ca8464e..e476c56d 100644 --- a/src/loadmechanism.cpp +++ b/src/loadmechanism.cpp @@ -65,7 +65,7 @@ void LoadMechanism::removeHintFile(QDir targetDirectory) bool LoadMechanism::isDirectLoadingSupported() { //FIXME: Seriously? isn't there a 'do i need steam' thing? - IPluginGame const *game = qApp->property("managed_game").value(); + IPluginGame const *game = qApp->property("managed_game").value(); if (game->gameName().compare("oblivion", Qt::CaseInsensitive) == 0) { // oblivion can be loaded directly if it's not the steam variant return !game->gameDirectory().exists("steam_api.dll"); @@ -77,7 +77,7 @@ bool LoadMechanism::isDirectLoadingSupported() bool LoadMechanism::isScriptExtenderSupported() { - IPluginGame const *game = qApp->property("managed_game").value(); + IPluginGame const *game = qApp->property("managed_game").value(); ScriptExtender *extender = game->feature(); // test if there even is an extender for the managed game and if so whether it's installed @@ -87,11 +87,11 @@ bool LoadMechanism::isScriptExtenderSupported() bool LoadMechanism::isProxyDLLSupported() { // using steam_api.dll as the proxy is way too game specific as many games will have different - // versions of that game. + // versions of that dll. // plus: the proxy dll hasn't been working for at least the whole 1.12.x versions of MO and // noone reported it so why maintain an unused feature? return false; -/* IPluginGame const *game = qApp->property("managed_game").value(); +/* IPluginGame const *game = qApp->property("managed_game").value(); return game->gameDirectory().exists(QString::fromStdWString(AppConfig::proxyDLLTarget()));*/ } @@ -123,7 +123,7 @@ bool LoadMechanism::hashIdentical(const QString &fileNameLHS, const QString &fil void LoadMechanism::deactivateScriptExtender() { try { - IPluginGame const *game = qApp->property("managed_game").value(); + IPluginGame const *game = qApp->property("managed_game").value(); ScriptExtender *extender = game->feature(); if (extender == nullptr) { throw MyException(QObject::tr("game doesn't support a script extender")); @@ -131,7 +131,7 @@ void LoadMechanism::deactivateScriptExtender() QDir pluginsDir(game->gameDirectory().absolutePath() + "/data/" + extender->name() + "/plugins"); -#pragma "implement this for usvfs" +#pragma message("implement this for usvfs") /* QString hookDLLName = ToQString(AppConfig::hookDLLName()); @@ -153,7 +153,7 @@ void LoadMechanism::deactivateScriptExtender() void LoadMechanism::deactivateProxyDLL() { try { - IPluginGame const *game = qApp->property("managed_game").value(); + IPluginGame const *game = qApp->property("managed_game").value(); QString targetPath = game->gameDirectory().absoluteFilePath(QString::fromStdWString(AppConfig::proxyDLLTarget())); @@ -182,7 +182,7 @@ void LoadMechanism::deactivateProxyDLL() void LoadMechanism::activateScriptExtender() { try { - IPluginGame const *game = qApp->property("managed_game").value(); + IPluginGame const *game = qApp->property("managed_game").value(); ScriptExtender *extender = game->feature(); if (extender == nullptr) { throw MyException(QObject::tr("game doesn't support a script extender")); @@ -225,7 +225,7 @@ void LoadMechanism::activateScriptExtender() void LoadMechanism::activateProxyDLL() { try { - IPluginGame const *game = qApp->property("managed_game").value(); + IPluginGame const *game = qApp->property("managed_game").value(); QString targetPath = game->gameDirectory().absoluteFilePath(QString::fromStdWString(AppConfig::proxyDLLTarget())); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 1a7d1564..0c54fc8d 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1259,7 +1259,7 @@ QDir MainWindow::currentSavesDir() const L"General", L"SLocalSavePath", L"Saves", path, MAX_PATH, ToWString(m_OrganizerCore.currentProfile()->absolutePath() + "/" + - m_OrganizerCore.managedGame()->getIniFiles()[0]).c_str()); + m_OrganizerCore.managedGame()->iniFiles()[0]).c_str()); savesDir.setPath(m_OrganizerCore.managedGame()->documentsDirectory().absoluteFilePath(QString::fromWCharArray(path))); } @@ -1927,11 +1927,14 @@ void MainWindow::addContentFilters() void MainWindow::addCategoryFilters(QTreeWidgetItem *root, const std::set &categoriesUsed, int targetID) { - for (size_t i = 1; i < m_CategoryFactory.numCategories(); ++i) { + for (unsigned int i = 1; + i < static_cast(m_CategoryFactory.numCategories()); ++i) { if ((m_CategoryFactory.getParentID(i) == targetID)) { int categoryID = m_CategoryFactory.getCategoryID(i); if (categoriesUsed.find(categoryID) != categoriesUsed.end()) { - QTreeWidgetItem *item = addFilterItem(root, m_CategoryFactory.getCategoryName(i), categoryID, ModListSortProxy::TYPE_CATEGORY); + QTreeWidgetItem *item = + addFilterItem(root, m_CategoryFactory.getCategoryName(i), + categoryID, ModListSortProxy::TYPE_CATEGORY); if (m_CategoryFactory.hasChildren(i)) { addCategoryFilters(item, categoriesUsed, categoryID); } @@ -3648,7 +3651,7 @@ void MainWindow::on_actionEndorseMO_triggered() NexusInterface::instance()->getGameURL()), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { NexusInterface::instance()->requestToggleEndorsement( - m_OrganizerCore.managedGame()->getNexusModOrganizerID(), true, this, QVariant(), QString()); + m_OrganizerCore.managedGame()->nexusModOrganizerID(), true, this, QVariant(), QString()); } } @@ -3707,11 +3710,11 @@ void MainWindow::modDetailsUpdated(bool) void MainWindow::nxmUpdatesAvailable(const std::vector &modIDs, QVariant userData, QVariant resultData, int) { - m_ModsToUpdate -= modIDs.size(); + m_ModsToUpdate -= static_cast(modIDs.size()); QVariantList resultList = resultData.toList(); for (auto iter = resultList.begin(); iter != resultList.end(); ++iter) { QVariantMap result = iter->toMap(); - if (result["id"].toInt() == m_OrganizerCore.managedGame()->getNexusModOrganizerID()) { + if (result["id"].toInt() == m_OrganizerCore.managedGame()->nexusModOrganizerID()) { if (!result["voted_by_user"].toBool()) { ui->actionEndorseMO->setVisible(true); } @@ -4201,7 +4204,7 @@ void MainWindow::on_bossButton_clicked() parameters << "--unattended" << "--stdout" << "--noreport" - << "--game" << m_OrganizerCore.managedGame()->getGameShortName() + << "--game" << m_OrganizerCore.managedGame()->gameShortName() << "--gamePath" << QString("\"%1\"").arg(m_OrganizerCore.managedGame()->gameDirectory().absolutePath()) << "--out" << outPath; @@ -4334,12 +4337,12 @@ void MainWindow::on_bossButton_clicked() // if the game specifies load order by file time, our own load order file needs to be removed because it's outdated. // refreshESPList will then use the file time as the load order. - if (m_OrganizerCore.managedGame()->getLoadOrderMechanism() == IPluginGame::LoadOrderMechanism::FileTime) { + if (m_OrganizerCore.managedGame()->loadOrderMechanism() == IPluginGame::LoadOrderMechanism::FileTime) { qDebug("removing loadorder.txt"); QFile::remove(m_OrganizerCore.currentProfile()->getLoadOrderFileName()); } m_OrganizerCore.refreshESPList(); - if (m_OrganizerCore.managedGame()->getLoadOrderMechanism() == IPluginGame::LoadOrderMechanism::FileTime) { + if (m_OrganizerCore.managedGame()->loadOrderMechanism() == IPluginGame::LoadOrderMechanism::FileTime) { // the load order should have been retrieved from file time, now save it to our own format m_OrganizerCore.savePluginList(); } diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 8bc767c5..5593b0f0 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -220,8 +220,8 @@ void ModInfo::updateFromDisc(const QString &modDirectory, } { // list plugins in the data directory and make a foreign-managed mod out of each - QStringList dlcPlugins = game->getDLCPlugins(); - QStringList mainPlugins = game->getPrimaryPlugins(); + QStringList dlcPlugins = game->DLCPlugins(); + QStringList mainPlugins = game->primaryPlugins(); QDir dataDir(game->dataDirectory()); for (const QString &file : dataDir.entryList({ "*.esp", "*.esm" })) { if (std::find_if(mainPlugins.begin(), mainPlugins.end(), @@ -289,9 +289,9 @@ int ModInfo::checkAllForUpdate(QObject *receiver) std::vector modIDs; //I ought to store this, it's used elsewhere - IPluginGame const *game = qApp->property("managed_game").value(); + IPluginGame const *game = qApp->property("managed_game").value(); - modIDs.push_back(game->getNexusModOrganizerID()); + modIDs.push_back(game->nexusModOrganizerID()); for (const ModInfo::Ptr &mod : s_Collection) { if (mod->canBeUpdated()) { diff --git a/src/modinfoforeign.cpp b/src/modinfoforeign.cpp index 4dbe034b..bf222166 100644 --- a/src/modinfoforeign.cpp +++ b/src/modinfoforeign.cpp @@ -21,7 +21,7 @@ QDateTime ModInfoForeign::creationTime() const QString ModInfoForeign::absolutePath() const { //I ought to store this, it's used elsewhere - IPluginGame const *game = qApp->property("managed_game").value(); + IPluginGame const *game = qApp->property("managed_game").value(); return game->dataDirectory().absolutePath(); } diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index 79749523..b8326c80 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -250,12 +250,12 @@ bool NexusInterface::isURLGameRelated(const QUrl &url) const QString NexusInterface::getGameURL() const { - return "http://www.nexusmods.com/" + m_Game->getGameShortName().toLower(); + return "http://www.nexusmods.com/" + m_Game->gameShortName().toLower(); } QString NexusInterface::getOldModsURL() const { - return "http://" + m_Game->getGameShortName().toLower() + ".nexusmods.com/mods"; + return "http://" + m_Game->gameShortName().toLower() + ".nexusmods.com/mods"; } @@ -595,7 +595,7 @@ void NexusInterface::managedGameChanged(IPluginGame const *game) namespace { QString get_management_url(MOBase::IPluginGame const *game) { - return "http://nmm.nexusmods.com/" + game->getGameShortName().toLower(); + return "http://nmm.nexusmods.com/" + game->gameShortName().toLower(); } } @@ -615,7 +615,7 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID , m_ID(s_NextID.fetchAndAddAcquire(1)) , m_URL(get_management_url(game)) , m_SubModule(subModule) - , m_NexusGameID(game->getNexusGameID()) + , m_NexusGameID(game->nexusGameID()) , m_Endorse(false) {} @@ -636,7 +636,7 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(std::vector modIDList , m_ID(s_NextID.fetchAndAddAcquire(1)) , m_URL(get_management_url(game)) , m_SubModule(subModule) - , m_NexusGameID(game->getNexusGameID()) + , m_NexusGameID(game->nexusGameID()) , m_Endorse(false) {} @@ -657,6 +657,6 @@ NexusInterface::NXMRequestInfo::NXMRequestInfo(int modID , m_ID(s_NextID.fetchAndAddAcquire(1)) , m_URL(get_management_url(game)) , m_SubModule(subModule) - , m_NexusGameID(game->getNexusGameID()) + , m_NexusGameID(game->nexusGameID()) , m_Endorse(false) {} diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 6a8e148f..c1044c9d 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -399,8 +399,8 @@ void OrganizerCore::connectPlugins(PluginContainer *container) } //Do this the hard way for (const IPluginGame * const game : container->plugins()) { - QString n = game->getGameShortName(); - if (game->getGameShortName() == "Skyrim") { + QString n = game->gameShortName(); + if (game->gameShortName() == "Skyrim") { m_Updater.setNexusDownload(game); break; } @@ -420,7 +420,7 @@ void OrganizerCore::disconnectPlugins() m_PluginContainer = nullptr; } -void OrganizerCore::setManagedGame(MOBase::IPluginGame const *game) +void OrganizerCore::setManagedGame(MOBase::IPluginGame *game) { m_GameName = game->gameName(); m_GamePlugin = game; @@ -932,12 +932,12 @@ void OrganizerCore::spawnBinary(const QFileInfo &binary, const QString &argument refreshDirectoryStructure(); // need to remove our stored load order because it may be outdated if a foreign tool changed the // file time. After removing that file, refreshESPList will use the file time as the order - if (managedGame()->getLoadOrderMechanism() == IPluginGame::LoadOrderMechanism::FileTime) { + if (managedGame()->loadOrderMechanism() == IPluginGame::LoadOrderMechanism::FileTime) { qDebug("removing loadorder.txt"); QFile::remove(m_CurrentProfile->getLoadOrderFileName()); } refreshESPList(); - if (managedGame()->getLoadOrderMechanism() == IPluginGame::LoadOrderMechanism::FileTime) { + if (managedGame()->loadOrderMechanism() == IPluginGame::LoadOrderMechanism::FileTime) { // the load order should have been retrieved from file time, now save it to our own format savePluginList(); } diff --git a/src/organizercore.h b/src/organizercore.h index ff730c2b..d86033eb 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -74,7 +74,7 @@ public: void connectPlugins(PluginContainer *container); void disconnectPlugins(); - void setManagedGame(const MOBase::IPluginGame *game); + void setManagedGame(MOBase::IPluginGame *game); void updateExecutablesList(QSettings &settings); @@ -239,7 +239,7 @@ private: IUserInterface *m_UserInterface; PluginContainer *m_PluginContainer; QString m_GameName; - MOBase::IPluginGame const *m_GamePlugin; + MOBase::IPluginGame *m_GamePlugin; Profile *m_CurrentProfile; diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 7a609374..bf5a09dc 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -127,7 +127,7 @@ void PluginList::refresh(const QString &profileName m_ESPsByPriority.clear(); m_ESPs.clear(); - QStringList primaryPlugins = m_GamePlugin->getPrimaryPlugins(); + QStringList primaryPlugins = m_GamePlugin->primaryPlugins(); m_CurrentProfile = profileName; @@ -312,7 +312,7 @@ bool PluginList::readLoadOrder(const QString &fileName) int priority = 0; - QStringList primaryPlugins = m_GamePlugin->getPrimaryPlugins(); + QStringList primaryPlugins = m_GamePlugin->primaryPlugins(); for (const QString &plugin : primaryPlugins) { if (availableESPs.find(plugin) != availableESPs.end()) { m_ESPLoadOrder[plugin] = priority++; @@ -503,7 +503,7 @@ void PluginList::saveTo(const QString &pluginFileName bool PluginList::saveLoadOrder(DirectoryEntry &directoryStructure) { - if (m_GamePlugin->getLoadOrderMechanism() != IPluginGame::LoadOrderMechanism::FileTime) { + if (m_GamePlugin->loadOrderMechanism() != IPluginGame::LoadOrderMechanism::FileTime) { // nothing to do return true; } diff --git a/src/profile.cpp b/src/profile.cpp index 24f094f3..4b916a2f 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -676,7 +676,7 @@ QString Profile::getDeleterFileName() const QString Profile::getIniFileName() const { - return m_Directory.absoluteFilePath(m_GamePlugin->getIniFiles()[0]); + return m_Directory.absoluteFilePath(m_GamePlugin->iniFiles()[0]); } QString Profile::getProfileTweaks() const diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp index af1eb5b2..0cfd7f08 100644 --- a/src/selfupdater.cpp +++ b/src/selfupdater.cpp @@ -103,7 +103,7 @@ void SelfUpdater::testForUpdate() } if (m_UpdateRequestID == -1 && m_NexusDownload != nullptr) { m_UpdateRequestID = m_Interface->requestDescription( - m_NexusDownload->getNexusModOrganizerID(), this, QVariant(), + m_NexusDownload->nexusModOrganizerID(), this, QVariant(), QString(), m_NexusDownload); } } @@ -121,7 +121,7 @@ void SelfUpdater::startUpdate() if (QMessageBox::question(m_Parent, tr("Update"), tr("An update is available (newest version: %1), do you want to install it?").arg(m_NewestVersion), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - m_UpdateRequestID = m_Interface->requestFiles(m_NexusDownload->getNexusModOrganizerID(), + m_UpdateRequestID = m_Interface->requestFiles(m_NexusDownload->nexusModOrganizerID(), this, m_NewestVersion, "", m_NexusDownload); } @@ -415,7 +415,7 @@ void SelfUpdater::nxmFilesAvailable(int, QVariant userData, QVariant resultData, if (updateFileID != -1) { qDebug("update available: %d", updateFileID); - m_UpdateRequestID = m_Interface->requestDownloadURL(m_NexusDownload->getNexusModOrganizerID(), + m_UpdateRequestID = m_Interface->requestDownloadURL(m_NexusDownload->nexusModOrganizerID(), updateFileID, this, updateFileName, "", m_NexusDownload); } else if (mainFileID != -1) { @@ -424,7 +424,7 @@ void SelfUpdater::nxmFilesAvailable(int, QVariant userData, QVariant resultData, tr("No incremental update available for this version, " "the complete package needs to be downloaded (%1 kB)").arg(mainFileSize), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { - m_UpdateRequestID = m_Interface->requestDownloadURL(m_NexusDownload->getNexusModOrganizerID(), + m_UpdateRequestID = m_Interface->requestDownloadURL(m_NexusDownload->nexusModOrganizerID(), mainFileID, this, mainFileName, "", m_NexusDownload); } diff --git a/src/settings.cpp b/src/settings.cpp index 479dd3ab..25b58df7 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -108,7 +108,7 @@ void Settings::registerAsNXMHandler(bool force) std::wstring nxmPath = ToWString(QCoreApplication::applicationDirPath() + "/nxmhandler.exe"); std::wstring executable = ToWString(QCoreApplication::applicationFilePath()); std::wstring mode = force ? L"forcereg" : L"reg"; - std::wstring parameters = mode + L" " + m_GamePlugin->getGameShortName().toStdWString() + L" \"" + executable + L"\""; + std::wstring parameters = mode + L" " + m_GamePlugin->gameShortName().toStdWString() + L" \"" + executable + L"\""; HINSTANCE res = ::ShellExecuteW(nullptr, L"open", nxmPath.c_str(), parameters.c_str(), nullptr, SW_SHOWNORMAL); if ((int)res <= 32) { QMessageBox::critical(nullptr, tr("Failed"), diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 8bc1dbc6..4e2f89a1 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -89,7 +89,7 @@ void SettingsDialog::on_categoriesBtn_clicked() void SettingsDialog::on_bsaDateBtn_clicked() { - IPluginGame const *game = qApp->property("managed_game").value(); + IPluginGame const *game = qApp->property("managed_game").value(); QDir dir = game->dataDirectory(); Helper::backdateBSAs(qApp->property("dataPath").toString().toStdWString(), -- cgit v1.3.1 From 2aae65d6f6c1ab3309e54437a339d1644088c5c8 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sun, 10 Jan 2016 20:19:56 +0100 Subject: made instance-switching usable this includes tons and tons of changes to how paths are determined. the profiles-dir can now also be configured --- src/CMakeLists.txt | 22 +-- src/installationmanager.cpp | 2 +- src/instancemanager.cpp | 206 ++++++++++++++++++++++++++++ src/instancemanager.h | 62 +++++++++ src/logbuffer.cpp | 120 ++++++++-------- src/main.cpp | 323 ++++++++++++++++++++++---------------------- src/mainwindow.cpp | 26 ++-- src/mainwindow.h | 7 +- src/organizercore.cpp | 22 ++- src/organizercore.h | 2 + src/profile.cpp | 7 +- src/profilesdialog.cpp | 6 +- src/selectiondialog.cpp | 5 + src/selectiondialog.h | 2 + src/selfupdater.cpp | 2 +- src/settings.cpp | 274 +++++++++++++++++++++---------------- src/settings.h | 20 ++- src/settingsdialog.cpp | 24 +++- src/settingsdialog.h | 3 + src/settingsdialog.ui | 242 +++++++++++++++++++-------------- 20 files changed, 897 insertions(+), 480 deletions(-) create mode 100644 src/instancemanager.cpp create mode 100644 src/instancemanager.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7cd66739..fda85b76 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,11 +35,11 @@ SET(organizer_SRCS modlist.cpp modinfodialog.cpp modinfo.cpp - modinfobackup.cpp - modinfoforeign.cpp - modinfooverwrite.cpp - modinforegular.cpp - modinfowithconflictinfo.cpp + modinfobackup.cpp + modinfoforeign.cpp + modinfooverwrite.cpp + modinforegular.cpp + modinfowithconflictinfo.cpp messagedialog.cpp mainwindow.cpp main.cpp @@ -87,6 +87,7 @@ SET(organizer_SRCS viewmarkingscrollbar.cpp plugincontainer.cpp organizercore.cpp + instancemanager.cpp usvfsconnector.cpp shared/inject.cpp @@ -131,11 +132,11 @@ SET(organizer_HDRS modlist.h modinfodialog.h modinfo.h - modinfobackup.h - modinfoforeign.h - modinfooverwrite.h - modinforegular.h - modinfowithconflictinfo.h + modinfobackup.h + modinfoforeign.h + modinfooverwrite.h + modinforegular.h + modinfowithconflictinfo.h messagedialog.h mainwindow.h loghighlighter.h @@ -183,6 +184,7 @@ SET(organizer_HDRS plugincontainer.h organizercore.h iuserinterface.h + instancemanager.h usvfsconnector.h shared/inject.h diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 739948cd..58565489 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -78,7 +78,7 @@ InstallationManager::InstallationManager() : m_ParentWidget(nullptr) , m_SupportedExtensions({ "zip", "rar", "7z", "fomod", "001" }) { - QLibrary archiveLib("dlls\\archive.dll"); + QLibrary archiveLib(QCoreApplication::applicationDirPath() + "\\dlls\\archive.dll"); if (!archiveLib.load()) { throw MyException(tr("archive.dll not loaded: \"%1\"").arg(archiveLib.errorString())); } diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp new file mode 100644 index 00000000..48c12d42 --- /dev/null +++ b/src/instancemanager.cpp @@ -0,0 +1,206 @@ +/* +Copyright (C) 2016 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + + +#include "instancemanager.h" +#include "selectiondialog.h" +#include +#include +#include +#include +#include +#include +#include + + +static const char COMPANY_NAME[] = "Tannin"; +static const char APPLICATION_NAME[] = "Mod Organizer"; +static const char INSTANCE_KEY[] = "CurrentInstance"; + + + +InstanceManager::InstanceManager() + : m_AppSettings(COMPANY_NAME, APPLICATION_NAME) +{ +} + + +QString InstanceManager::currentInstance() const +{ + return m_AppSettings.value(INSTANCE_KEY, "").toString(); +} + +void InstanceManager::clearCurrentInstance() +{ + setCurrentInstance(""); +} + +void InstanceManager::setCurrentInstance(const QString &name) +{ + m_AppSettings.setValue(INSTANCE_KEY, name); +} + + +QString InstanceManager::queryInstanceName() const +{ + QString instanceId; + while (instanceId.isEmpty()) { + QInputDialog dialog; + // would be neat if we could take the names from the game plugins but + // the required initialization order requires the ini file to be + // available *before* we load plugins + dialog.setComboBoxItems({ "Oblivion", "Skyrim", "Fallout 3", + "Fallout NV", "Fallout 4" }); + dialog.setComboBoxEditable(true); + dialog.setWindowTitle(QObject::tr("Enter Instance Name")); + dialog.setLabelText(QObject::tr("Name")); + if (dialog.exec() == QDialog::Rejected) { + throw MOBase::MyException(QObject::tr("Canceled")); + } + instanceId = dialog.textValue().replace(QRegExp("[^0-9a-zA-Z ]"), ""); + } + return instanceId; +} + + +QString InstanceManager::chooseInstance(const QStringList &instanceList) const +{ + SelectionDialog selection(QObject::tr("Choose Instance"), nullptr); + selection.disableCancel(); + for (const QString &instance : instanceList) { + selection.addChoice(instance, "", instance); + } + + selection.addChoice(QObject::tr("New"), + QObject::tr("Create a new instance."), + ""); + if (selection.exec() == QDialog::Rejected) { + qDebug("rejected"); + throw MOBase::MyException(QObject::tr("Canceled")); + } + + QString choice = selection.getChoiceData().toString(); + + if (choice.isEmpty()) { + return queryInstanceName(); + } else { + return choice; + } +} + + +QString InstanceManager::instancePath() const +{ + return QDir::fromNativeSeparators( + QStandardPaths::writableLocation(QStandardPaths::DataLocation)); +} + + +QStringList InstanceManager::instances() const +{ + return QDir(instancePath()).entryList(QDir::Dirs | QDir::NoDotAndDotDot); +} + + +bool InstanceManager::portableInstall() const +{ + return QFile::exists(qApp->applicationDirPath() + "/" + + QString::fromStdWString(AppConfig::iniFileName())); +} + + +InstanceManager::InstallationMode InstanceManager::queryInstallMode() const +{ + SelectionDialog selection(QObject::tr("Installation Mode"), nullptr); + selection.disableCancel(); + selection.addChoice(QObject::tr("Portable"), + QObject::tr("Everything in one directory, only one game per installation."), + 0); + selection.addChoice(QObject::tr("Regular"), + QObject::tr("Data in separate directory, multiple games supported."), + 1); + if (selection.exec() == QDialog::Rejected) { + throw MOBase::MyException(QObject::tr("Canceled")); + } + + switch (selection.getChoiceData().toInt()) { + case 0: return InstallationMode::PORTABLE; + default: return InstallationMode::REGULAR; + } +} + + +void InstanceManager::createDataPath(const QString &dataPath) const +{ + if (!QDir(dataPath).exists()) { + if (!QDir().mkpath(dataPath)) { + throw MOBase::MyException( + QObject::tr("failed to create %1").arg(dataPath)); + } else { + QMessageBox::information( + nullptr, QObject::tr("Data directory created"), + QObject::tr("New data directory created at %1. If you don't want to " + "store a lot of data there, reconfigure the storage " + "directories via settings.").arg(dataPath)); + } + } +} + + +QString InstanceManager::determineDataPath() +{ + QString instanceId = currentInstance(); + + if (instanceId.isEmpty() && !portableInstall()) { + // no portable install and no selected instance + + QStringList instanceList = instances(); + + qDebug("%d - %s", instanceList.size(), qPrintable(instanceList.join(";"))); + + if (instanceList.size() == 0) { + switch (queryInstallMode()) { + case InstallationMode::PORTABLE: { + instanceId = QString(); + } break; + case InstallationMode::REGULAR: { + instanceId = queryInstanceName(); + } break; + } + } else { + instanceId = chooseInstance(instanceList); + } + } + + if (instanceId.isEmpty()) { + qDebug("portable mode"); + return qApp->applicationDirPath(); + } else { + setCurrentInstance(instanceId); + + QString dataPath = QDir::fromNativeSeparators( + QStandardPaths::writableLocation(QStandardPaths::DataLocation) + + "/" + instanceId); + + createDataPath(dataPath); + + return dataPath; + } +} + diff --git a/src/instancemanager.h b/src/instancemanager.h new file mode 100644 index 00000000..2e17d1c0 --- /dev/null +++ b/src/instancemanager.h @@ -0,0 +1,62 @@ +/* +Copyright (C) 2016 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + + +#pragma once + + +#include +#include + + +class InstanceManager { + + enum class InstallationMode { + PORTABLE, + REGULAR + }; + +public: + + InstanceManager(); + + QString determineDataPath(); + QStringList instances() const; + void clearCurrentInstance(); + +private: + + QString currentInstance() const; + QString instancePath() const; + + bool portableInstall() const; + + void setCurrentInstance(const QString &name); + + QString queryInstanceName() const; + QString chooseInstance(const QStringList &instanceList) const; + + void createDataPath(const QString &dataPath) const; + InstallationMode queryInstallMode() const; + +private: + + QSettings m_AppSettings; + +}; diff --git a/src/logbuffer.cpp b/src/logbuffer.cpp index 2c3fc101..01ff2b87 100644 --- a/src/logbuffer.cpp +++ b/src/logbuffer.cpp @@ -26,37 +26,33 @@ along with Mod Organizer. If not, see . #include #include - using MOBase::reportError; QScopedPointer LogBuffer::s_Instance; QMutex LogBuffer::s_Mutex; - -LogBuffer::LogBuffer(int messageCount, QtMsgType minMsgType, const QString &outputFileName) - : QAbstractItemModel(nullptr), m_OutFileName(outputFileName), m_ShutDown(false), - m_MinMsgType(minMsgType), m_NumMessages(0) +LogBuffer::LogBuffer(int messageCount, QtMsgType minMsgType, + const QString &outputFileName) + : QAbstractItemModel(nullptr) + , m_OutFileName(outputFileName) + , m_ShutDown(false) + , m_MinMsgType(minMsgType) + , m_NumMessages(0) { m_Messages.resize(messageCount); } LogBuffer::~LogBuffer() { -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) + qDebug("closing log buffer"); qInstallMessageHandler(0); -#else - qInstallMsgHandler(0); -#endif -// if (!m_ShutDown) { - write(); -// } + write(); } - void LogBuffer::logMessage(QtMsgType type, const QString &message) { if (type >= m_MinMsgType) { - Message msg = { type, QTime::currentTime(), message }; + Message msg = {type, QTime::currentTime(), message}; if (m_NumMessages < m_Messages.size()) { beginInsertRows(QModelIndex(), m_NumMessages, m_NumMessages + 1); } @@ -73,7 +69,6 @@ void LogBuffer::logMessage(QtMsgType type, const QString &message) } } - void LogBuffer::write() const { if (m_NumMessages == 0) { @@ -84,12 +79,15 @@ void LogBuffer::write() const QFile file(m_OutFileName); if (!file.open(QIODevice::WriteOnly)) { - reportError(tr("failed to write log to %1: %2").arg(m_OutFileName).arg(file.errorString())); + reportError(tr("failed to write log to %1: %2") + .arg(m_OutFileName) + .arg(file.errorString())); return; } - unsigned int i = (m_NumMessages > m_Messages.size()) ? m_NumMessages - m_Messages.size() - : 0U; + unsigned int i = (m_NumMessages > m_Messages.size()) + ? m_NumMessages - m_Messages.size() + : 0U; for (; i < m_NumMessages; ++i) { file.write(m_Messages.at(i % m_Messages.size()).toString().toUtf8()); file.write("\r\n"); @@ -97,68 +95,68 @@ void LogBuffer::write() const ::SetLastError(lastError); } - -void LogBuffer::init(int messageCount, QtMsgType minMsgType, const QString &outputFileName) +void LogBuffer::init(int messageCount, QtMsgType minMsgType, + const QString &outputFileName) { QMutexLocker guard(&s_Mutex); - if (!s_Instance.isNull()) { - s_Instance.reset(); - } s_Instance.reset(new LogBuffer(messageCount, minMsgType, outputFileName)); -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) qInstallMessageHandler(LogBuffer::log); -#else - qInstallMsgHandler(LogBuffer::log); -#endif } char LogBuffer::msgTypeID(QtMsgType type) { switch (type) { - case QtDebugMsg: return 'D'; - case QtWarningMsg: return 'W'; - case QtCriticalMsg: return 'C'; - case QtFatalMsg: return 'F'; - default: return '?'; + case QtDebugMsg: + return 'D'; + case QtWarningMsg: + return 'W'; + case QtCriticalMsg: + return 'C'; + case QtFatalMsg: + return 'F'; + default: + return '?'; } } -void LogBuffer::log(QtMsgType type, const QMessageLogContext &context, const QString &message) +void LogBuffer::log(QtMsgType type, const QMessageLogContext &context, + const QString &message) { // QMutexLocker doesn't support timeout... if (!s_Mutex.tryLock(100)) { fprintf(stderr, "failed to log: %s", qPrintable(message)); return; } - ON_BLOCK_EXIT([] () { - s_Mutex.unlock(); - }); + ON_BLOCK_EXIT([]() { s_Mutex.unlock(); }); if (!s_Instance.isNull()) { s_Instance->logMessage(type, message); } -// fprintf(stdout, "(%s:%u) %s\n", context.file, context.line, qPrintable(message)); if (type == QtDebugMsg) { - fprintf(stdout, "%s [%c] %s\n", qPrintable(QTime::currentTime().toString()), msgTypeID(type), qPrintable(message)); + fprintf(stdout, "%s [%c] %s\n", qPrintable(QTime::currentTime().toString()), + msgTypeID(type), qPrintable(message)); } else { if (context.line != 0) { - fprintf(stdout, "%s [%c] (%s:%u) %s\n", qPrintable(QTime::currentTime().toString()), msgTypeID(type), + fprintf(stdout, "%s [%c] (%s:%u) %s\n", + qPrintable(QTime::currentTime().toString()), msgTypeID(type), context.file, context.line, qPrintable(message)); } else { - fprintf(stdout, "%s [%c] %s\n", qPrintable(QTime::currentTime().toString()), msgTypeID(type), qPrintable(message)); + fprintf(stdout, "%s [%c] %s\n", + qPrintable(QTime::currentTime().toString()), msgTypeID(type), + qPrintable(message)); } } fflush(stdout); } -QModelIndex LogBuffer::index(int row, int column, const QModelIndex&) const +QModelIndex LogBuffer::index(int row, int column, const QModelIndex &) const { return createIndex(row, column, row); } -QModelIndex LogBuffer::parent(const QModelIndex&) const +QModelIndex LogBuffer::parent(const QModelIndex &) const { return QModelIndex(); } @@ -171,16 +169,16 @@ int LogBuffer::rowCount(const QModelIndex &parent) const return std::min(m_NumMessages, m_Messages.size()); } -int LogBuffer::columnCount(const QModelIndex&) const +int LogBuffer::columnCount(const QModelIndex &) const { return 2; } - QVariant LogBuffer::data(const QModelIndex &index, int role) const { - unsigned offset = m_NumMessages < m_Messages.size() ? 0 - : m_NumMessages - m_Messages.size(); + unsigned offset = m_NumMessages < m_Messages.size() + ? 0 + : m_NumMessages - m_Messages.size(); unsigned int msgIndex = (offset + index.row() + 1) % m_Messages.size(); switch (role) { case Qt::DisplayRole: { @@ -198,20 +196,28 @@ QVariant LogBuffer::data(const QModelIndex &index, int role) const case Qt::DecorationRole: { if (index.column() == 1) { switch (m_Messages[msgIndex].type) { - case QtDebugMsg: return QIcon(":/MO/gui/information"); - case QtWarningMsg: return QIcon(":/MO/gui/warning"); - case QtCriticalMsg: return QIcon(":/MO/gui/important"); - case QtFatalMsg: return QIcon(":/MO/gui/problem"); + case QtDebugMsg: + return QIcon(":/MO/gui/information"); + case QtWarningMsg: + return QIcon(":/MO/gui/warning"); + case QtCriticalMsg: + return QIcon(":/MO/gui/important"); + case QtFatalMsg: + return QIcon(":/MO/gui/problem"); } } } break; case Qt::UserRole: { if (index.column() == 1) { switch (m_Messages[msgIndex].type) { - case QtDebugMsg: return "D"; - case QtWarningMsg: return "W"; - case QtCriticalMsg: return "C"; - case QtFatalMsg: return "F"; + case QtDebugMsg: + return "D"; + case QtWarningMsg: + return "W"; + case QtCriticalMsg: + return "C"; + case QtFatalMsg: + return "F"; } } } break; @@ -227,7 +233,6 @@ void LogBuffer::writeNow() } } - void LogBuffer::cleanQuit() { QMutexLocker guard(&s_Mutex); @@ -255,5 +260,8 @@ void log(const char *format, ...) QString LogBuffer::Message::toString() const { - return QString("%1 [%2] %3").arg(time.toString()).arg(msgTypeID(type)).arg(message); + return QString("%1 [%2] %3") + .arg(time.toString()) + .arg(msgTypeID(type)) + .arg(message); } diff --git a/src/main.cpp b/src/main.cpp index 30938485..a262d031 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,6 +45,8 @@ along with Mod Organizer. If not, see . #include "moapplication.h" #include "tutorialmanager.h" #include "nxmaccessmanager.h" +#include "instancemanager.h" + #include #include @@ -84,6 +86,7 @@ along with Mod Organizer. If not, see . using namespace MOBase; using namespace MOShared; + bool createAndMakeWritable(const std::wstring &subPath) { QString const dataPath = qApp->property("dataPath").toString(); @@ -135,43 +138,6 @@ bool bootstrap() return true; } -void cleanupDir() -{ - // files from previous versions of MO that are no longer - // required (in that location) - QStringList fileNames { - "imageformats/", - "loot/resources/", - "plugins/previewDDS.dll", - "dlls/boost_python-vc100-mt-1_55.dll", - "dlls/QtCore4.dll", - "dlls/QtDeclarative4.dll", - "dlls/QtGui4.dll", - "dlls/QtNetwork4.dll", - "dlls/QtOpenGL4.dll", - "dlls/QtScript4.dll", - "dlls/QtSql4.dll", - "dlls/QtSvg4.dll", - "dlls/QtWebKit4.dll", - "dlls/QtXml4.dll", - "dlls/QtXmlPatterns4.dll", - "msvcp100.dll", - "msvcr100.dll", - "proxy.dll" - }; - - for (const QString &fileName : fileNames) { - QString fullPath = qApp->applicationDirPath() + "/" + fileName; - if (QFile::exists(fullPath)) { - if (shellDelete(QStringList(fullPath), true)) { - qDebug("removed obsolete file %s", qPrintable(fullPath)); - } else { - qDebug("failed to remove obsolete %s", qPrintable(fullPath)); - } - } - } -} - bool isNxmLink(const QString &link) { @@ -195,6 +161,10 @@ static LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *except if (dbgDLL) { FuncMiniDumpWriteDump funcDump = (FuncMiniDumpWriteDump)::GetProcAddress(dbgDLL, "MiniDumpWriteDump"); if (funcDump) { + wchar_t exeNameBuffer[MAX_PATH]; + ::GetModuleFileNameW(nullptr, exeNameBuffer, MAX_PATH); + QString dumpName = QString::fromWCharArray(exeNameBuffer) + ".dmp"; + if (QMessageBox::question(nullptr, QObject::tr("Woops"), QObject::tr("ModOrganizer has crashed! " "Should a diagnostic file be created? " @@ -202,12 +172,10 @@ static LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *except "the bug is a lot more likely to be fixed. " "Please include a short description of what you were " "doing when the crash happened" - ).arg(qApp->applicationFilePath().append(".dmp")), + ).arg(dumpName), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - std::wstring dumpName = ToWString(qApp->applicationFilePath().append(".dmp")); - - HANDLE dumpFile = ::CreateFile(dumpName.c_str(), + HANDLE dumpFile = ::CreateFile(dumpName.toStdWString().c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (dumpFile != INVALID_HANDLE_VALUE) { @@ -225,10 +193,10 @@ static LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *except return EXCEPTION_EXECUTE_HANDLER; } _snprintf(errorBuffer, errorLen, "failed to save minidump to %ls (error %lu)", - dumpName.c_str(), ::GetLastError()); + dumpName.toStdWString().c_str(), ::GetLastError()); } else { _snprintf(errorBuffer, errorLen, "failed to create %ls (error %lu)", - dumpName.c_str(), ::GetLastError()); + dumpName.toStdWString().c_str(), ::GetLastError()); } } else { return result; @@ -435,139 +403,95 @@ MOBase::IPluginGame *determineCurrentGame(QString const &moPath, QSettings &sett return nullptr; } -int main(int argc, char *argv[]) + +// extend path to include dll directory so plugins don't need a manifest +// (using AddDllDirectory would be an alternative to this but it seems fairly +// complicated esp. +// since it isn't easily accessible on Windows < 8 +// SetDllDirectory replaces other search directories and this seems to +// propagate to child processes) +void setupPath() { - MOApplication application(argc, argv); + static const int BUFSIZE = 4096; - qDebug("application name: %s", qPrintable(application.applicationName())); - - QString instanceID; - QFile instanceFile(application.applicationDirPath() + "/INSTANCE"); - if (instanceFile.open(QIODevice::ReadOnly)) { - instanceID = instanceFile.readAll().trimmed(); - } - - QString const dataPath = - instanceID.isEmpty() ? application.applicationDirPath() - : QDir::fromNativeSeparators( - QStandardPaths::writableLocation(QStandardPaths::DataLocation) - + "/" + instanceID - ); - application.setProperty("dataPath", dataPath); - - if (!QDir(dataPath).exists()) { - if (!QDir().mkpath(dataPath)) { - qCritical("failed to create %s", qPrintable(dataPath)); - return 1; - } + qDebug("MO at: %s", qPrintable(QDir::toNativeSeparators( + QCoreApplication::applicationDirPath()))); + + boost::scoped_array oldPath(new TCHAR[BUFSIZE]); + DWORD offset = ::GetEnvironmentVariable(TEXT("PATH"), oldPath.get(), BUFSIZE); + if (offset > BUFSIZE) { + oldPath.reset(new TCHAR[offset]); + ::GetEnvironmentVariable(TEXT("PATH"), oldPath.get(), offset); } - SetUnhandledExceptionFilter(MyUnhandledExceptionFilter); + std::wstring newPath(oldPath.get()); + newPath += L";"; + newPath += ToWString(QDir::toNativeSeparators( + QCoreApplication::applicationDirPath())) + .c_str(); + newPath += L"\\dlls"; - if (!HaveWriteAccess(ToWString(application.applicationDirPath()))) { - QStringList arguments = application.arguments(); - arguments.pop_front(); - ::ShellExecuteW( nullptr - , L"runas" - , ToWString(QString("\"%1\"").arg(QCoreApplication::applicationFilePath())).c_str() - , ToWString(arguments.join(" ")).c_str() - , ToWString(QDir::currentPath()).c_str(), SW_SHOWNORMAL); - return 1; - } + ::SetEnvironmentVariableW(L"PATH", newPath.c_str()); +} + +int runApplication(MOApplication &application, SingleInstance &instance) +{ + qDebug("start main application"); QPixmap pixmap(":/MO/gui/splash"); QSplashScreen splash(pixmap); - try { - if (!bootstrap()) { - return -1; - } + QString dataPath = application.property("dataPath").toString(); + qDebug("data path: %s", qPrintable(dataPath)); - LogBuffer::init(100, QtDebugMsg, qApp->property("dataPath").toString() + "/logs/mo_interface.log"); + if (!bootstrap()) { + reportError("failed to set up data path"); + return 1; + } -#if QT_VERSION >= 0x050000 && !defined(QT_NO_SSL) - qDebug("ssl support: %d", QSslSocket::supportsSsl()); -#endif + QStringList arguments = application.arguments(); + try { qDebug("Working directory: %s", qPrintable(QDir::toNativeSeparators(QDir::currentPath()))); - qDebug("MO at: %s", qPrintable(QDir::toNativeSeparators(application.applicationDirPath()))); splash.show(); - - cleanupDir(); } catch (const std::exception &e) { reportError(e.what()); return 1; } - { // extend path to include dll directory so plugins don't need a manifest - // (using AddDllDirectory would be an alternative to this but it seems fairly complicated esp. - // since it isn't easily accessible on Windows < 8 - // SetDllDirectory replaces other search directories and this seems to propagate to child processes) - static const int BUFSIZE = 4096; - - boost::scoped_array oldPath(new TCHAR[BUFSIZE]); - DWORD offset = ::GetEnvironmentVariable(TEXT("PATH"), oldPath.get(), BUFSIZE); - if (offset > BUFSIZE) { - oldPath.reset(new TCHAR[offset]); - ::GetEnvironmentVariable(TEXT("PATH"), oldPath.get(), offset); - } - - std::wstring newPath(oldPath.get()); - newPath += L";"; - newPath += ToWString(QDir::toNativeSeparators(QCoreApplication::applicationDirPath())).c_str(); - newPath += L"\\dlls"; - - ::SetEnvironmentVariableW(L"PATH", newPath.c_str()); - } - - QStringList arguments = application.arguments(); - - bool forcePrimary = false; - if (arguments.contains("update")) { - arguments.removeAll("update"); - forcePrimary = true; - } - try { - SingleInstance instance(forcePrimary); - if (!instance.primaryInstance()) { - if ((arguments.size() == 2) && isNxmLink(arguments.at(1))) { - qDebug("not primary instance, sending download message"); - instance.sendMessage(arguments.at(1)); - return 0; - } else if (arguments.size() == 1) { - QMessageBox::information(nullptr, QObject::tr("Mod Organizer"), QObject::tr("An instance of Mod Organizer is already running")); - return 0; - } - } // we continue for the primary instance OR if MO has been called with parameters - - QSettings settings(dataPath + "/" + QString::fromStdWString(AppConfig::iniFileName()), QSettings::IniFormat); + QSettings settings(dataPath + "/" + + QString::fromStdWString(AppConfig::iniFileName()), + QSettings::IniFormat); qDebug("initializing core"); OrganizerCore organizer(settings); qDebug("initialize plugins"); PluginContainer pluginContainer(&organizer); pluginContainer.loadPlugins(); - MOBase::IPluginGame *game = determineCurrentGame(application.applicationDirPath(), settings, pluginContainer); + MOBase::IPluginGame *game = determineCurrentGame( + application.applicationDirPath(), settings, pluginContainer); if (game == nullptr) { return 1; } organizer.setManagedGame(game); - organizer.createDefaultProfile(); - //See the pragma - we apparently don't use this so not sure why we check it if (!settings.contains("game_edition")) { QStringList editions = game->gameVariants(); if (editions.size() > 1) { - SelectionDialog selection(QObject::tr("Please select the game edition you have (MO can't start the game correctly if this is set incorrectly!)"), nullptr); + SelectionDialog selection( + QObject::tr("Please select the game edition you have (MO can't " + "start the game correctly if this is set " + "incorrectly!)"), + nullptr); int index = 0; for (const QString &edition : editions) { selection.addChoice(edition, "", index++); } if (selection.exec() == QDialog::Rejected) { - return -1; + return 1; } else { settings.setValue("game_edition", selection.getChoiceString()); } @@ -575,8 +499,8 @@ int main(int argc, char *argv[]) } game->setGameVariant(settings.value("game_edition").toString()); -#pragma message("edition isn't used?") - qDebug("managing game at %s", qPrintable(QDir::toNativeSeparators(game->gameDirectory().absolutePath()))); + qDebug("managing game at %s", qPrintable(QDir::toNativeSeparators( + game->gameDirectory().absolutePath()))); organizer.updateExecutablesList(settings); @@ -585,27 +509,35 @@ int main(int argc, char *argv[]) // if we have a command line parameter, it is either a nxm link or // a binary to start - if ((arguments.size() > 1) - && !isNxmLink(arguments.at(1))) { - QString exeName = arguments.at(1); - qDebug("starting %s from command line", qPrintable(exeName)); - arguments.removeFirst(); // remove application name (ModOrganizer.exe) - arguments.removeFirst(); // remove binary name - // pass the remaining parameters to the binary - try { - organizer.startApplication(exeName, arguments, QString(), QString()); - return 0; - } catch (const std::exception &e) { - reportError(QObject::tr("failed to start application: %1").arg(e.what())); - return 1; + if (arguments.size() > 1) { + if (isNxmLink(arguments.at(1))) { + qDebug("starting download from command line: %s", + qPrintable(arguments.at(1))); + organizer.externalMessage(arguments.at(1)); + } else { + QString exeName = arguments.at(1); + qDebug("starting %s from command line", qPrintable(exeName)); + arguments.removeFirst(); // remove application name (ModOrganizer.exe) + arguments.removeFirst(); // remove binary name + // pass the remaining parameters to the binary + try { + organizer.startApplication(exeName, arguments, QString(), QString()); + } catch (const std::exception &e) { + reportError( + QObject::tr("failed to start application: %1").arg(e.what())); + return 1; + } } + return 0; } NexusInterface::instance()->getAccessManager()->startLoginCheck(); qDebug("initializing tutorials"); - TutorialManager::init(qApp->applicationDirPath() + "/" + QString::fromStdWString(AppConfig::tutorialsPath()) + "/", - &organizer); + TutorialManager::init( + qApp->applicationDirPath() + "/" + + QString::fromStdWString(AppConfig::tutorialsPath()) + "/", + &organizer); if (!application.setStyleFile(settings.value("Settings/style", "").toString())) { // disable invalid stylesheet @@ -615,27 +547,94 @@ int main(int argc, char *argv[]) int res = 1; { // scope to control lifetime of mainwindow // set up main window and its data structures - MainWindow mainWindow(argv[0], settings, organizer, pluginContainer); + MainWindow mainWindow(settings, organizer, pluginContainer); - QObject::connect(&mainWindow, SIGNAL(styleChanged(QString)), &application, SLOT(setStyleFile(QString))); - QObject::connect(&instance, SIGNAL(messageSent(QString)), &organizer, SLOT(externalMessage(QString))); + QObject::connect(&mainWindow, SIGNAL(styleChanged(QString)), &application, + SLOT(setStyleFile(QString))); + QObject::connect(&instance, SIGNAL(messageSent(QString)), &organizer, + SLOT(externalMessage(QString))); mainWindow.readSettings(); qDebug("displaying main window"); mainWindow.show(); - if ((arguments.size() > 1) - && isNxmLink(arguments.at(1))) { - qDebug("starting download from command line: %s", qPrintable(arguments.at(1))); - organizer.externalMessage(arguments.at(1)); - } splash.finish(&mainWindow); - res = application.exec(); + return application.exec(); } - return res; } catch (const std::exception &e) { reportError(e.what()); return 1; } } + + +int main(int argc, char *argv[]) +{ + SetUnhandledExceptionFilter(MyUnhandledExceptionFilter); + + MOApplication application(argc, argv); + QStringList arguments = application.arguments(); + + if ((arguments.length() >= 4) && (arguments.at(1) == "launch")) { + // all we're supposed to do is launch another process + QProcess process; + process.setWorkingDirectory(QDir::fromNativeSeparators(arguments.at(2))); + process.setProgram(QDir::fromNativeSeparators(arguments.at(3))); + process.setArguments(arguments.mid(4)); + process.start(); + process.waitForFinished(-1); + return process.exitCode(); + } + + setupPath(); + + #if !defined(QT_NO_SSL) + qDebug("ssl support: %d", QSslSocket::supportsSsl()); + #else + qDebug("non-ssl build"); + #endif + + bool forcePrimary = false; + if (arguments.contains("update")) { + arguments.removeAll("update"); + forcePrimary = true; + } + + SingleInstance instance(forcePrimary); + if (!instance.primaryInstance()) { + if ((arguments.size() == 2) && isNxmLink(arguments.at(1))) { + qDebug("not primary instance, sending download message"); + instance.sendMessage(arguments.at(1)); + return 0; + } else if (arguments.size() == 1) { + QMessageBox::information( + nullptr, QObject::tr("Mod Organizer"), + QObject::tr("An instance of Mod Organizer is already running")); + return 0; + } + } // we continue for the primary instance OR if MO was called with parameters + + do { + QString dataPath; + + try { + dataPath = InstanceManager().determineDataPath(); + } catch (const std::exception &e) { + QMessageBox::critical(nullptr, QObject::tr("Failed to set up instance"), + e.what()); + return 1; + } + + application.setProperty("dataPath", dataPath); + + LogBuffer::init(100, QtDebugMsg, qApp->property("dataPath").toString() + "/logs/mo_interface.log"); + + int result = runApplication(application, instance); + + if (result != INT_MAX) { + return result; + } + argc = 1; + } while (true); +} diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 0c54fc8d..80e3490c 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -74,6 +74,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include @@ -146,8 +147,7 @@ using namespace MOBase; using namespace MOShared; -MainWindow::MainWindow(const QString &exeName - , QSettings &initSettings +MainWindow::MainWindow(QSettings &initSettings , OrganizerCore &organizerCore , PluginContainer &pluginContainer , QWidget *parent) @@ -155,7 +155,6 @@ MainWindow::MainWindow(const QString &exeName , ui(new Ui::MainWindow) , m_WasVisible(false) , m_Tutorial(this, "MainWindow") - , m_ExeName(exeName) , m_OldProfileIndex(-1) , m_ModListGroupingProxy(nullptr) , m_ModListSortProxy(nullptr) @@ -301,6 +300,7 @@ MainWindow::MainWindow(const QString &exeName connect(ui->toolBar, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(toolBar_customContextMenuRequested(QPoint))); connect(&m_OrganizerCore, &OrganizerCore::modInstalled, this, &MainWindow::modInstalled); + connect(&m_OrganizerCore, &OrganizerCore::close, this, &QMainWindow::close); connect(&m_IntegratedBrowser, SIGNAL(requestDownload(QUrl,QNetworkReply*)), &m_OrganizerCore, SLOT(requestDownload(QUrl,QNetworkReply*))); @@ -347,6 +347,8 @@ MainWindow::MainWindow(const QString &exeName MainWindow::~MainWindow() { + cleanup(); + m_PluginContainer.setUserInterface(nullptr, nullptr); m_OrganizerCore.setUserInterface(nullptr, nullptr); m_IntegratedBrowser.close(); @@ -810,6 +812,14 @@ void MainWindow::closeEvent(QCloseEvent* event) } setCursor(Qt::WaitCursor); +} + +void MainWindow::cleanup() +{ + if (ui->logList->model() != nullptr) { + disconnect(ui->logList->model(), nullptr, nullptr, nullptr); + ui->logList->setModel(nullptr); + } m_IntegratedBrowser.close(); } @@ -1175,7 +1185,7 @@ bool MainWindow::refreshProfiles(bool selectProfile) profileBox->clear(); profileBox->addItem(QObject::tr("")); - QDir profilesDir(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::profilesPath())); + QDir profilesDir(Settings::instance().getProfileDirectory()); profilesDir.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot); QDirIterator profileIter(profilesDir); @@ -3166,7 +3176,7 @@ QString getStartMenuLinkfile(const Executable &exec) void MainWindow::addWindowsLink(const ShortcutType mapping) { - Executable const &selectedExecutable(getSelectedExecutable()); + const Executable &selectedExecutable(getSelectedExecutable()); QString const linkName = getLinkfile(mapping == ShortcutType::Desktop ? getDesktopDirectory() : getStartMenuDirectory(), selectedExecutable); @@ -4217,6 +4227,7 @@ void MainWindow::on_bossButton_clicked() HANDLE stdOutRead = INVALID_HANDLE_VALUE; createStdoutPipe(&stdOutRead, &stdOutWrite); m_OrganizerCore.prepareVFS(); + HANDLE loot = startBinary(QFileInfo(qApp->applicationDirPath() + "/loot/lootcli.exe"), parameters.join(" "), qApp->applicationDirPath() + "/loot", @@ -4313,6 +4324,7 @@ void MainWindow::on_bossButton_clicked() } catch (const std::exception &e) { 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); @@ -4326,11 +4338,7 @@ 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 49476bf3..530097cf 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -26,7 +26,6 @@ along with Mod Organizer. If not, see . #include #include #include -#include #include #include #include @@ -75,7 +74,7 @@ class MainWindow : public QMainWindow, public IUserInterface public: - explicit MainWindow(const QString &exeName, QSettings &initSettings, + explicit MainWindow(QSettings &initSettings, OrganizerCore &organizerCore, PluginContainer &pluginContainer, QWidget *parent = 0); ~MainWindow(); @@ -155,6 +154,8 @@ protected: private: + void cleanup(); + void actionToToolButton(QAction *&sourceAction); void updateToolBar(); @@ -263,8 +264,6 @@ private: MOBase::TutorialControl m_Tutorial; - QString m_ExeName; - int m_OldProfileIndex; std::vector m_ModNameList; // the mod-list to go with the directory structure diff --git a/src/organizercore.cpp b/src/organizercore.cpp index c84b603d..a73e6845 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -535,7 +535,7 @@ InstallationManager *OrganizerCore::installationManager() void OrganizerCore::createDefaultProfile() { - QString profilesPath = qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::profilesPath()); + QString profilesPath = settings().getProfileDirectory(); if (QDir(profilesPath).entryList(QDir::AllDirs | QDir::NoDotAndDotDot).size() == 0) { Profile newProf("Default", managedGame(), false); } @@ -552,7 +552,7 @@ void OrganizerCore::setCurrentProfile(const QString &profileName) (profileName == m_CurrentProfile->name())) { return; } - QString profileDir = qApp->property("dataPath").toString() + "/" + ToQString(AppConfig::profilesPath()) + "/" + profileName; + QString profileDir = settings().getProfileDirectory() + "/" + profileName; Profile *newProfile = new Profile(QDir(profileDir), managedGame()); delete m_CurrentProfile; @@ -994,10 +994,9 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary, const QString & m_CurrentProfile->modlistWriter().writeImmediately(true); } - // TODO: should also pass arguments if (m_AboutToRun(binary.absoluteFilePath())) { m_USVFS.updateMapping(fileMapping()); - QString modsPath(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::modsPath())); + QString modsPath = settings().getModDirectory(); QString binPath = binary.absoluteFilePath(); @@ -1008,15 +1007,14 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary, const QString & QString cwdPath = currentDirectory.absolutePath(); int binOffset = binPath.indexOf('/', modsPath.length() + 1); - int cwdOffset = cwdPath.indexOf('/', cwdPath.length() + 1); - QString binPath = m_GamePlugin->dataDirectory().absolutePath() + "/" + binPath.mid(binOffset); - QString cwd = m_GamePlugin->dataDirectory().absolutePath() + "/" + cwdPath.mid(cwdOffset); + int cwdOffset = cwdPath.indexOf('/', modsPath.length() + 1); + QString dataBinPath = m_GamePlugin->dataDirectory().absolutePath() + binPath.mid(binOffset, -1); + QString dataCwd = m_GamePlugin->dataDirectory().absolutePath() + cwdPath.mid(cwdOffset, -1); QString cmdline = QString("launch \"%1\" \"%2\" %3") - .arg(QDir::toNativeSeparators(cwd), - QDir::toNativeSeparators(binPath), + .arg(QDir::toNativeSeparators(dataCwd), + QDir::toNativeSeparators(dataBinPath), arguments); - - return startBinary(QFileInfo(QCoreApplication::applicationDirPath() + "/helper.exe"), + return startBinary(QFileInfo(QCoreApplication::applicationFilePath()), cmdline, QCoreApplication::applicationDirPath(), true); } else { @@ -1600,7 +1598,7 @@ std::vector OrganizerCore::fileMapping() int overwriteId = m_DirectoryStructure->getOriginByName(L"Overwrite").getID(); - const IPluginGame *game = qApp->property("managed_game").value(); + IPluginGame *game = qApp->property("managed_game").value(); MappingType result = fileMapping( QDir::toNativeSeparators(game->dataDirectory().absolutePath()), "\\", diff --git a/src/organizercore.h b/src/organizercore.h index d86033eb..bf6c5a8b 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -201,6 +201,8 @@ signals: void managedGameChanged(MOBase::IPluginGame const *gamePlugin); + void close(); + private: void storeSettings(); diff --git a/src/profile.cpp b/src/profile.cpp index 4b916a2f..99a6bd90 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -22,6 +22,7 @@ along with Mod Organizer. If not, see . #include "windows_error.h" #include "modinfo.h" #include "safewritefile.h" +#include "settings.h" #include #include #include @@ -58,7 +59,7 @@ Profile::Profile(const QString &name, IPluginGame const *gamePlugin, bool useDef : m_ModListWriter(std::bind(&Profile::writeModlistNow, this)) , m_GamePlugin(gamePlugin) { - QString profilesDir = qApp->property("dataPath").toString() + "/" + ToQString(AppConfig::profilesPath()); + QString profilesDir = Settings::instance().getProfileDirectory(); QDir profileBase(profilesDir); QString fixedName = name; @@ -487,7 +488,7 @@ void Profile::setModPriority(unsigned int index, int &newPriority) Profile *Profile::createPtrFrom(const QString &name, const Profile &reference, MOBase::IPluginGame const *gamePlugin) { - QString profileDirectory = qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::profilesPath()) + "/" + name; + QString profileDirectory = Settings::instance().getProfileDirectory() + "/" + name; reference.copyFilesTo(profileDirectory); return new Profile(QDir(profileDirectory), gamePlugin); } @@ -691,7 +692,7 @@ QString Profile::absolutePath() const void Profile::rename(const QString &newName) { - QDir profileDir(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::profilesPath())); + QDir profileDir(Settings::instance().getProfileDirectory()); profileDir.rename(name(), newName); m_Directory = profileDir.absoluteFilePath(newName); } diff --git a/src/profilesdialog.cpp b/src/profilesdialog.cpp index b21aee53..49fbda4e 100644 --- a/src/profilesdialog.cpp +++ b/src/profilesdialog.cpp @@ -25,6 +25,7 @@ along with Mod Organizer. If not, see . #include "profileinputdialog.h" #include "mainwindow.h" #include "aboutdialog.h" +#include "settings.h" #include #include #include @@ -50,7 +51,7 @@ ProfilesDialog::ProfilesDialog(const QString &profileName, MOBase::IPluginGame c { ui->setupUi(this); - QDir profilesDir(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::profilesPath())); + QDir profilesDir(Settings::instance().getProfileDirectory()); profilesDir.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot); m_ProfilesList = findChild("profilesList"); @@ -187,8 +188,7 @@ void ProfilesDialog::on_removeProfileButton_clicked() Profile::Ptr currentProfile = profilesList->currentItem()->data(Qt::UserRole).value(); QString profilePath; if (currentProfile.get() == nullptr) { - profilePath = qApp->property("dataPath").toString() - + "/" + QString::fromStdWString(AppConfig::profilesPath()) + profilePath = Settings::instance().getProfileDirectory() + "/" + profilesList->currentItem()->text(); if (QMessageBox::question(this, tr("Profile broken"), tr("This profile you're about to delete seems to be broken or the path is invalid. " diff --git a/src/selectiondialog.cpp b/src/selectiondialog.cpp index aae95f56..55728751 100644 --- a/src/selectiondialog.cpp +++ b/src/selectiondialog.cpp @@ -79,6 +79,11 @@ QString SelectionDialog::getChoiceString() } } +void SelectionDialog::disableCancel() +{ + ui->cancelButton->setEnabled(false); + ui->cancelButton->setHidden(true); +} void SelectionDialog::on_buttonBox_clicked(QAbstractButton *button) { diff --git a/src/selectiondialog.h b/src/selectiondialog.h index 43ba9767..3c4b25df 100644 --- a/src/selectiondialog.h +++ b/src/selectiondialog.h @@ -53,6 +53,8 @@ public: QVariant getChoiceData(); QString getChoiceString(); + void disableCancel(); + private slots: void on_buttonBox_clicked(QAbstractButton *button); diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp index 0cfd7f08..ae75b9cb 100644 --- a/src/selfupdater.cpp +++ b/src/selfupdater.cpp @@ -65,7 +65,7 @@ SelfUpdater::SelfUpdater(NexusInterface *nexusInterface) , m_Attempts(3) , m_NexusDownload(nullptr) { - QLibrary archiveLib("dlls\\archive.dll"); + QLibrary archiveLib(QCoreApplication::applicationDirPath() + "\\dlls\\archive.dll"); if (!archiveLib.load()) { throw MyException(tr("archive.dll not loaded: \"%1\"").arg(archiveLib.errorString())); } diff --git a/src/settings.cpp b/src/settings.cpp index 7b812759..c99b434d 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -125,7 +125,7 @@ void Settings::registerPlugin(IPlugin *plugin) m_Plugins.push_back(plugin); m_PluginSettings.insert(plugin->name(), QMap()); m_PluginDescriptions.insert(plugin->name(), QMap()); - foreach (const PluginSetting &setting, plugin->settings()) { + for (const PluginSetting &setting : plugin->settings()) { QVariant temp = m_Settings.value("Plugins/" + plugin->name() + "/" + setting.key, setting.defaultValue); if (!temp.convert(setting.defaultValue.type())) { qWarning("failed to interpret \"%s\" as correct type for \"%s\" in plugin \"%s\", using default", @@ -183,7 +183,7 @@ void Settings::setDownloadSpeed(const QString &serverName, int bytesPerSecond) { m_Settings.beginGroup("Servers"); - foreach (const QString &serverKey, m_Settings.childKeys()) { + for (const QString &serverKey : m_Settings.childKeys()) { QVariantMap data = m_Settings.value(serverKey).toMap(); if (serverKey == serverName) { data["downloadCount"] = data["downloadCount"].toInt() + 1; @@ -201,7 +201,7 @@ std::map Settings::getPreferredServers() std::map result; m_Settings.beginGroup("Servers"); - foreach (const QString &serverKey, m_Settings.childKeys()) { + for (const QString &serverKey : m_Settings.childKeys()) { QVariantMap data = m_Settings.value(serverKey).toMap(); int preference = data["preferred"].toInt(); if (preference > 0) { @@ -233,6 +233,11 @@ QString Settings::getModDirectory() const return getConfigurablePath("mod_directory", ToQString(AppConfig::modsPath())); } +QString Settings::getProfileDirectory() const +{ + return getConfigurablePath("profiles_directory", ToQString(AppConfig::profilesPath())); +} + QString Settings::getNMMVersion() const { static const QString MIN_NMM_VERSION = "0.52.3"; @@ -411,7 +416,7 @@ void Settings::updateServers(const QList &servers) m_Settings.beginGroup("Servers"); QStringList oldServerKeys = m_Settings.childKeys(); - foreach (const ServerInfo &server, servers) { + for (const ServerInfo &server : servers) { if (!oldServerKeys.contains(server.name)) { // not yet known server QVariantMap newVal; @@ -433,7 +438,7 @@ void Settings::updateServers(const QList &servers) // clean up unavailable servers QDate now = QDate::currentDate(); - foreach (const QString &key, m_Settings.childKeys()) { + for (const QString &key : m_Settings.childKeys()) { QVariantMap val = m_Settings.value(key).toMap(); QDate lastSeen = val["lastSeen"].toDate(); if (lastSeen.daysTo(now) > 30) { @@ -457,7 +462,7 @@ void Settings::writePluginBlacklist() { m_Settings.beginWriteArray("pluginBlacklist"); int idx = 0; - foreach (const QString &plugin, m_PluginBlacklist) { + for (const QString &plugin : m_PluginBlacklist) { m_Settings.setArrayIndex(idx++); m_Settings.setValue("name", plugin); } @@ -522,7 +527,7 @@ void Settings::resetDialogs() { m_Settings.beginGroup("DialogChoices"); QStringList keys = m_Settings.childKeys(); - foreach (QString key, keys) { + for (QString key : keys) { m_Settings.remove(key); } @@ -539,6 +544,7 @@ void Settings::query(QWidget *parent) std::vector> tabs; tabs.push_back(std::unique_ptr(new GeneralTab(this, dialog))); + tabs.push_back(std::unique_ptr(new PathsTab(this, dialog))); tabs.push_back(std::unique_ptr(new NexusTab(this, dialog))); tabs.push_back(std::unique_ptr(new SteamTab(this, dialog))); tabs.push_back(std::unique_ptr(new PluginsTab(this, dialog))); @@ -552,56 +558,54 @@ void Settings::query(QWidget *parent) } } -Settings::SettingsTab::SettingsTab(Settings *m_parent, SettingsDialog &m_dialog) : - m_parent(m_parent), - m_Settings(m_parent->m_Settings), - m_dialog(m_dialog) -{} +Settings::SettingsTab::SettingsTab(Settings *m_parent, SettingsDialog &m_dialog) + : m_parent(m_parent) + , m_Settings(m_parent->m_Settings) + , m_dialog(m_dialog) +{ +} Settings::SettingsTab::~SettingsTab() {} -Settings::GeneralTab::GeneralTab(Settings *m_parent, SettingsDialog &m_dialog) : - Settings::SettingsTab(m_parent, m_dialog), - m_languageBox(m_dialog.findChild("languageBox")), - m_styleBox(m_dialog.findChild("styleBox")), - m_logLevelBox(m_dialog.findChild("logLevelBox")), - m_downloadDirEdit(m_dialog.findChild("downloadDirEdit")), - m_modDirEdit(m_dialog.findChild("modDirEdit")), - m_cacheDirEdit(m_dialog.findChild("cacheDirEdit")), - m_compactBox(m_dialog.findChild("compactBox")), - m_showMetaBox(m_dialog.findChild("showMetaBox")) +Settings::GeneralTab::GeneralTab(Settings *m_parent, SettingsDialog &m_dialog) + : Settings::SettingsTab(m_parent, m_dialog) + , m_languageBox(m_dialog.findChild("languageBox")) + , m_styleBox(m_dialog.findChild("styleBox")) + , m_logLevelBox(m_dialog.findChild("logLevelBox")) + , m_compactBox(m_dialog.findChild("compactBox")) + , m_showMetaBox(m_dialog.findChild("showMetaBox")) +{ + // FIXME I think 'addLanguages' lives in here not in parent + m_parent->addLanguages(m_languageBox); { - //FIXME I think 'addLanguages' lives in here not in parent - m_parent->addLanguages(m_languageBox); - { - QString languageCode = m_parent->language(); - int currentID = m_languageBox->findData(languageCode); - // I made a mess. :( Most languages are stored with only the iso country code (2 characters like "de") but chinese - // with the exact language variant (zh_TW) so I have to search for both variants - if (currentID == -1) { - currentID = m_languageBox->findData(languageCode.mid(0, 2)); - } - if (currentID != -1) { - m_languageBox->setCurrentIndex(currentID); - } + QString languageCode = m_parent->language(); + int currentID = m_languageBox->findData(languageCode); + // I made a mess. :( Most languages are stored with only the iso country + // code (2 characters like "de") but chinese + // with the exact language variant (zh_TW) so I have to search for both + // variants + if (currentID == -1) { + currentID = m_languageBox->findData(languageCode.mid(0, 2)); + } + if (currentID != -1) { + m_languageBox->setCurrentIndex(currentID); } + } - //FIXME I think addStyles lives in here not in parent - m_parent->addStyles(m_styleBox); - { - int currentID = m_styleBox->findData(m_Settings.value("Settings/style", "").toString()); - if (currentID != -1) { - m_styleBox->setCurrentIndex(currentID); - } + // FIXME I think addStyles lives in here not in parent + m_parent->addStyles(m_styleBox); + { + int currentID = m_styleBox->findData( + m_Settings.value("Settings/style", "").toString()); + if (currentID != -1) { + m_styleBox->setCurrentIndex(currentID); } + } - m_logLevelBox->setCurrentIndex(m_parent->logLevel()); - m_downloadDirEdit->setText(m_parent->getDownloadDirectory()); - m_modDirEdit->setText(m_parent->getModDirectory()); - m_cacheDirEdit->setText(m_parent->getCacheDirectory()); - m_compactBox->setChecked(m_parent->compactDownloads()); - m_showMetaBox->setChecked(m_parent->metaDownloads()); + m_logLevelBox->setCurrentIndex(m_parent->logLevel()); + m_compactBox->setChecked(m_parent->compactDownloads()); + m_showMetaBox->setChecked(m_parent->metaDownloads()); } void Settings::GeneralTab::update() @@ -622,68 +626,108 @@ void Settings::GeneralTab::update() m_Settings.setValue("Settings/log_level", m_logLevelBox->currentIndex()); - { // advanced settings - if ((QDir::fromNativeSeparators(m_modDirEdit->text()) != QDir::fromNativeSeparators(m_parent->getModDirectory())) && - (QMessageBox::question(nullptr, tr("Confirm"), tr("Changing the mod directory affects all your profiles! " - "Mods not present (or named differently) in the new location will be disabled in all profiles. " - "There is no way to undo this unless you backed up your profiles manually. Proceed?"), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)) { - m_modDirEdit->setText(m_parent->getModDirectory()); - } - if (!QDir(m_downloadDirEdit->text()).exists()) { - QDir().mkpath(m_downloadDirEdit->text()); - } - if (QFileInfo(m_downloadDirEdit->text()) != - QFileInfo(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::downloadPath()))) { - m_Settings.setValue("Settings/download_directory", QDir::toNativeSeparators(m_downloadDirEdit->text())); - } else { - m_Settings.remove("Settings/download_directory"); - } + m_Settings.setValue("Settings/compact_downloads", m_compactBox->isChecked()); + m_Settings.setValue("Settings/meta_downloads", m_showMetaBox->isChecked()); +} - if (!QDir(m_modDirEdit->text()).exists()) { - QDir().mkpath(m_modDirEdit->text()); - } - if (QFileInfo(m_modDirEdit->text()) != - QFileInfo(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::modsPath()))) { - m_Settings.setValue("Settings/mod_directory", QDir::toNativeSeparators(m_modDirEdit->text())); - } else { - m_Settings.remove("Settings/mod_directory"); - } +Settings::PathsTab::PathsTab(Settings *parent, SettingsDialog &dialog) + : SettingsTab(parent, dialog) + , m_downloadDirEdit(m_dialog.findChild("downloadDirEdit")) + , m_modDirEdit(m_dialog.findChild("modDirEdit")) + , m_cacheDirEdit(m_dialog.findChild("cacheDirEdit")) + , m_profilesDirEdit(m_dialog.findChild("profilesDirEdit")) +{ + m_downloadDirEdit->setText(m_parent->getDownloadDirectory()); + m_modDirEdit->setText(m_parent->getModDirectory()); + m_cacheDirEdit->setText(m_parent->getCacheDirectory()); + m_profilesDirEdit->setText(m_parent->getProfileDirectory()); +} + +void Settings::PathsTab::update() +{ + if ((QDir::fromNativeSeparators(m_modDirEdit->text()) + != QDir::fromNativeSeparators(m_parent->getModDirectory())) + && (QMessageBox::question( + nullptr, tr("Confirm"), + tr("Changing the mod directory affects all your profiles! " + "Mods not present (or named differently) in the new location " + "will be disabled in all profiles. " + "There is no way to undo this unless you backed up your " + "profiles manually. Proceed?"), + QMessageBox::Yes | QMessageBox::No) + == QMessageBox::No)) { + m_modDirEdit->setText(m_parent->getModDirectory()); + } - if (!QDir(m_cacheDirEdit->text()).exists()) { - QDir().mkpath(m_cacheDirEdit->text()); - } - if (QFileInfo(m_cacheDirEdit->text()) != - QFileInfo(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::cachePath()))) { - m_Settings.setValue("Settings/cache_directory", QDir::toNativeSeparators(m_cacheDirEdit->text())); - } else { - m_Settings.remove("Settings/cache_directory"); - } + if (!QDir(m_downloadDirEdit->text()).exists()) { + QDir().mkpath(m_downloadDirEdit->text()); + } + if (QFileInfo(m_downloadDirEdit->text()) + != QFileInfo(qApp->property("dataPath").toString() + "/" + + QString::fromStdWString(AppConfig::downloadPath()))) { + m_Settings.setValue("Settings/download_directory", + QDir::toNativeSeparators(m_downloadDirEdit->text())); + } else { + m_Settings.remove("Settings/download_directory"); } - m_Settings.setValue("Settings/compact_downloads", m_compactBox->isChecked()); - m_Settings.setValue("Settings/meta_downloads", m_showMetaBox->isChecked()); + if (!QDir(m_modDirEdit->text()).exists()) { + QDir().mkpath(m_modDirEdit->text()); + } + if (QFileInfo(m_modDirEdit->text()) + != QFileInfo(qApp->property("dataPath").toString() + "/" + + QString::fromStdWString(AppConfig::modsPath()))) { + m_Settings.setValue("Settings/mod_directory", + QDir::toNativeSeparators(m_modDirEdit->text())); + } else { + m_Settings.remove("Settings/mod_directory"); + } + + if (!QDir(m_cacheDirEdit->text()).exists()) { + QDir().mkpath(m_cacheDirEdit->text()); + } + if (QFileInfo(m_cacheDirEdit->text()) + != QFileInfo(qApp->property("dataPath").toString() + "/" + + QString::fromStdWString(AppConfig::cachePath()))) { + m_Settings.setValue("Settings/cache_directory", + QDir::toNativeSeparators(m_cacheDirEdit->text())); + } else { + m_Settings.remove("Settings/cache_directory"); + } + + if (!QDir(m_profilesDirEdit->text()).exists()) { + QDir().mkpath(m_profilesDirEdit->text()); + } + if (QFileInfo(m_profilesDirEdit->text()) + != QFileInfo(qApp->property("dataPath").toString() + "/" + + QString::fromStdWString(AppConfig::profilesPath()))) { + m_Settings.setValue("Settings/profiles_directory", + QDir::toNativeSeparators(m_profilesDirEdit->text())); + } else { + m_Settings.remove("Settings/profiles_directory"); + } } -Settings::NexusTab::NexusTab(Settings *m_parent, SettingsDialog &m_dialog) : - Settings::SettingsTab(m_parent, m_dialog), - m_loginCheckBox(m_dialog.findChild("loginCheckBox")), - m_usernameEdit(m_dialog.findChild("usernameEdit")), - m_passwordEdit(m_dialog.findChild("passwordEdit")), - m_offlineBox(m_dialog.findChild("offlineBox")), - m_proxyBox(m_dialog.findChild("proxyBox")), - m_knownServersList(m_dialog.findChild("knownServersList")), - m_preferredServersList(m_dialog.findChild("preferredServersList")) +Settings::NexusTab::NexusTab(Settings *parent, SettingsDialog &dialog) + : Settings::SettingsTab(parent, dialog) + , m_loginCheckBox(dialog.findChild("loginCheckBox")) + , m_usernameEdit(dialog.findChild("usernameEdit")) + , m_passwordEdit(dialog.findChild("passwordEdit")) + , m_offlineBox(dialog.findChild("offlineBox")) + , m_proxyBox(dialog.findChild("proxyBox")) + , m_knownServersList(dialog.findChild("knownServersList")) + , m_preferredServersList( + dialog.findChild("preferredServersList")) { - if (m_parent->automaticLoginEnabled()) { + if (parent->automaticLoginEnabled()) { m_loginCheckBox->setChecked(true); m_usernameEdit->setText(m_Settings.value("Settings/nexus_username", "").toString()); m_passwordEdit->setText(deObfuscate(m_Settings.value("Settings/nexus_password", "").toString())); } - m_offlineBox->setChecked(m_parent->offlineMode()); - m_proxyBox->setChecked(m_parent->useProxy()); + m_offlineBox->setChecked(parent->offlineMode()); + m_proxyBox->setChecked(parent->useProxy()); // display server preferences m_Settings.beginGroup("Servers"); @@ -743,11 +787,10 @@ void Settings::NexusTab::update() m_Settings.endGroup(); } - -Settings::SteamTab::SteamTab(Settings *m_parent, SettingsDialog &m_dialog) : - Settings::SettingsTab(m_parent, m_dialog), - m_steamUserEdit(m_dialog.findChild("steamUserEdit")), - m_steamPassEdit(m_dialog.findChild("steamPassEdit")) +Settings::SteamTab::SteamTab(Settings *m_parent, SettingsDialog &m_dialog) + : Settings::SettingsTab(m_parent, m_dialog) + , m_steamUserEdit(m_dialog.findChild("steamUserEdit")) + , m_steamPassEdit(m_dialog.findChild("steamPassEdit")) { if (m_Settings.contains("Settings/steam_username")) { m_steamUserEdit->setText(m_Settings.value("Settings/steam_username", "").toString()); @@ -763,10 +806,10 @@ void Settings::SteamTab::update() m_parent->setSteamLogin(m_steamUserEdit->text(), m_steamPassEdit->text()); } -Settings::PluginsTab::PluginsTab(Settings *m_parent, SettingsDialog &m_dialog) : - Settings::SettingsTab(m_parent, m_dialog), - m_pluginsList(m_dialog.findChild("pluginsList")), - m_pluginBlacklistList(m_dialog.findChild("pluginBlacklist")) +Settings::PluginsTab::PluginsTab(Settings *m_parent, SettingsDialog &m_dialog) + : Settings::SettingsTab(m_parent, m_dialog) + , m_pluginsList(m_dialog.findChild("pluginsList")) + , m_pluginBlacklistList(m_dialog.findChild("pluginBlacklist")) { // display plugin settings for (IPlugin *plugin : m_parent->m_Plugins) { @@ -799,20 +842,21 @@ void Settings::PluginsTab::update() // store plugin blacklist m_parent->m_PluginBlacklist.clear(); - foreach (QListWidgetItem *item, m_pluginBlacklistList->findItems("*", Qt::MatchWildcard)) { + for (QListWidgetItem *item : m_pluginBlacklistList->findItems("*", Qt::MatchWildcard)) { m_parent->m_PluginBlacklist.insert(item->text()); } m_parent->writePluginBlacklist(); } -Settings::WorkaroundsTab::WorkaroundsTab(Settings *m_parent, SettingsDialog &m_dialog) : - Settings::SettingsTab(m_parent, m_dialog), - m_appIDEdit(m_dialog.findChild("appIDEdit")), - m_mechanismBox(m_dialog.findChild("mechanismBox")), - m_nmmVersionEdit(m_dialog.findChild("nmmVersionEdit")), - m_hideUncheckedBox(m_dialog.findChild("hideUncheckedBox")), - m_forceEnableBox(m_dialog.findChild("forceEnableBox")), - m_displayForeignBox(m_dialog.findChild("displayForeignBox")) +Settings::WorkaroundsTab::WorkaroundsTab(Settings *m_parent, + SettingsDialog &m_dialog) + : Settings::SettingsTab(m_parent, m_dialog) + , m_appIDEdit(m_dialog.findChild("appIDEdit")) + , m_mechanismBox(m_dialog.findChild("mechanismBox")) + , m_nmmVersionEdit(m_dialog.findChild("nmmVersionEdit")) + , m_hideUncheckedBox(m_dialog.findChild("hideUncheckedBox")) + , m_forceEnableBox(m_dialog.findChild("forceEnableBox")) + , m_displayForeignBox(m_dialog.findChild("displayForeignBox")) { m_appIDEdit->setText(m_parent->getSteamAppID()); diff --git a/src/settings.h b/src/settings.h index 1ee16e76..85a3dac0 100644 --- a/src/settings.h +++ b/src/settings.h @@ -130,6 +130,11 @@ public: **/ QString getCacheDirectory() const; + /** + * retrieve the directory where profiles stored (with native separators) + **/ + QString getProfileDirectory() const; + /** * @return true if the user has set up automatic login to nexus **/ @@ -339,11 +344,22 @@ private: QComboBox *m_languageBox; QComboBox *m_styleBox; QComboBox *m_logLevelBox; + QCheckBox *m_compactBox; + QCheckBox *m_showMetaBox; + }; + + class PathsTab : public SettingsTab + { + public: + PathsTab(Settings *parent, SettingsDialog &dialog); + + void update(); + + private: QLineEdit *m_downloadDirEdit; QLineEdit *m_modDirEdit; QLineEdit *m_cacheDirEdit; - QCheckBox *m_compactBox; - QCheckBox *m_showMetaBox; + QLineEdit *m_profilesDirEdit; }; /** Display/store the configuration in the 'nexus' tab of the settings dialogue */ diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index 4e2f89a1..341fa19d 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -25,6 +25,7 @@ along with Mod Organizer. If not, see . #include "noeditdelegate.h" #include "iplugingame.h" #include "settings.h" +#include "instancemanager.h" #include #include @@ -39,11 +40,13 @@ using namespace MOBase; SettingsDialog::SettingsDialog(QWidget *parent) - : TutorableDialog("SettingsDialog", parent), ui(new Ui::SettingsDialog) + : TutorableDialog("SettingsDialog", parent) + , ui(new Ui::SettingsDialog) { ui->setupUi(this); - QShortcut *delShortcut = new QShortcut(QKeySequence(Qt::Key_Delete), ui->pluginBlacklist); + QShortcut *delShortcut + = new QShortcut(QKeySequence(Qt::Key_Delete), ui->pluginBlacklist); connect(delShortcut, SIGNAL(activated()), this, SLOT(deleteBlacklistItem())); } @@ -54,7 +57,7 @@ SettingsDialog::~SettingsDialog() void SettingsDialog::addPlugins(const std::vector &plugins) { - foreach (IPlugin *plugin, plugins) { + for (IPlugin *plugin : plugins) { ui->pluginsList->addItem(plugin->name()); } } @@ -89,7 +92,8 @@ void SettingsDialog::on_categoriesBtn_clicked() void SettingsDialog::on_bsaDateBtn_clicked() { - IPluginGame const *game = qApp->property("managed_game").value(); + IPluginGame const *game + = qApp->property("managed_game").value(); QDir dir = game->dataDirectory(); Helper::backdateBSAs(qApp->property("dataPath").toString().toStdWString(), @@ -186,3 +190,15 @@ void SettingsDialog::on_associateButton_clicked() { Settings::instance().registerAsNXMHandler(true); } + +void SettingsDialog::on_changeInstanceButton_clicked() +{ + if (QMessageBox::question(this, tr("Are you sure?"), + tr("This will restart MO, continue?"), + QMessageBox::Yes | QMessageBox::Cancel) + == QMessageBox::Yes) { + InstanceManager().clearCurrentInstance(); + this->reject(); + qApp->exit(INT_MAX); + } +} diff --git a/src/settingsdialog.h b/src/settingsdialog.h index e99fb3e3..8bd080ab 100644 --- a/src/settingsdialog.h +++ b/src/settingsdialog.h @@ -52,6 +52,9 @@ signals: void resetDialogs(); +private slots: + void on_changeInstanceButton_clicked(); + private: void storeSettings(QListWidgetItem *pluginItem); diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index b88885dc..805a5060 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -108,114 +108,60 @@ p, li { white-space: pre-wrap; } - + - Advanced - - - true - - - false + User interface - - - + + + - Directory where downloads are stored. + If checked, the download interface will be more compact. - - Directory where downloads are stored. + + Compact Download Interface - - - - - 0 - 0 - + + + + If checked, the download list will display meta information instead of file names. - ... + Download Meta Information - - - - Mod Directory + + + + + 16777215 + 16777215 + - - - - - Directory where mods are stored. + Reset stored information from dialogs. - Directory where mods are stored. Please note that changing this will break all associations of profiles with mods that don't exist in the new location (with the same name). - - - - - - - ... - - - - - - - Download Directory - - - - - - - Cache Directory + This will make all dialogs show up again where you checked the "Remember selection"-box. - - - - - - - - ... + Reset Dialogs - - - - - - - User interface - - - + - If checked, the download interface will be more compact. + Modify the categories available to arrange your mods. - - Compact Download Interface - - - - - - - If checked, the download list will display meta information instead of file names. + + Modify the categories available to arrange your mods. - Download Meta Information + Configure Mod Categories @@ -223,26 +169,132 @@ p, li { white-space: pre-wrap; } - - + + + Qt::Vertical + + - 16777215 - 16777215 + 20 + 40 + + + + - Reset stored information from dialogs. + Restart MO and choose a different set of data paths. - This will make all dialogs show up again where you checked the "Remember selection"-box. + This will restart MO and give you opportunity to switch to a different set of data paths. - Reset Dialogs + Change Instance + + + + + Paths + + - + + + + + Downloads + + + + + + + Caches + + + + + + + ... + + + + + + + + + + Directory where downloads are stored. + + + Directory where downloads are stored. + + + + + + + Mods + + + + + + + ... + + + + + + + Directory where mods are stored. + + + Directory where mods are stored. Please note that changing this will break all associations of profiles with mods that don't exist in the new location (with the same name). + + + + + + + + 0 + 0 + + + + ... + + + + + + + Profiles + + + + + + + + + + ... + + + + + + + Qt::Vertical @@ -255,15 +307,9 @@ p, li { white-space: pre-wrap; } - - - Modify the categories available to arrange your mods. - - - Modify the categories available to arrange your mods. - + - Configure Mod Categories + Important: All directories have to be writeable! -- cgit v1.3.1 From 63eaf453e0de2087ce7d2a3c26edc7eac4bd1882 Mon Sep 17 00:00:00 2001 From: Tannin Date: Tue, 9 Feb 2016 20:05:41 +0100 Subject: portable setup is no longer offered if the directory isn't writable --- CMakeLists.txt | 3 ++- src/CMakeLists.txt | 2 -- src/instancemanager.cpp | 21 ++++++++++++--------- 3 files changed, 14 insertions(+), 12 deletions(-) (limited to 'src/CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index e255f2b8..0146edd5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8) PROJECT(organizer) + SET(DEPENDENCIES_DIR CACHE PATH "") # hint to find qt in dependencies path LIST(APPEND CMAKE_PREFIX_PATH ${DEPENDENCIES_DIR}/qt5/lib/cmake) @@ -11,4 +12,4 @@ GET_FILENAME_COMPONENT(BOOST_ROOT ${BOOST_ROOT} DIRECTORY) SET(ZLIB_ROOT ${DEPENDENCIES_DIR}/zlib) -ADD_SUBDIRECTORY(src) \ No newline at end of file +ADD_SUBDIRECTORY(src) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fda85b76..a6434acf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -297,8 +297,6 @@ INCLUDE_DIRECTORIES(${project_path}/uibase/src ${project_path}/game_gamebryo/src ${project_path}/game_features/src) -MESSAGE(STATUS ${project_path}/gameGamebryo/src) - INCLUDE_DIRECTORIES(shared ${ZLIB_INCLUDE_DIRS}) LINK_DIRECTORIES(${lib_path} diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index 48c12d42..d48d02d2 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -172,18 +172,21 @@ QString InstanceManager::determineDataPath() QStringList instanceList = instances(); - qDebug("%d - %s", instanceList.size(), qPrintable(instanceList.join(";"))); - if (instanceList.size() == 0) { - switch (queryInstallMode()) { - case InstallationMode::PORTABLE: { - instanceId = QString(); - } break; - case InstallationMode::REGULAR: { - instanceId = queryInstanceName(); - } break; + if (QFileInfo(qApp->applicationDirPath()).isWritable()) { + switch (queryInstallMode()) { + case InstallationMode::PORTABLE: { + instanceId = QString(); + } break; + case InstallationMode::REGULAR: { + instanceId = queryInstanceName(); + } break; + } + } else { + instanceId = queryInstanceName(); } } else { + // don't offer portable instance if we can't set one up. instanceId = chooseInstance(instanceList); } } -- cgit v1.3.1 From ad9d50e5f0b12a75353c0172979ea8165d0d5127 Mon Sep 17 00:00:00 2001 From: Tannin Date: Tue, 9 Feb 2016 20:32:26 +0100 Subject: replaced boost&qt foreach-implementations by the c++11 variant --- src/CMakeLists.txt | 2 +- src/mainwindow.cpp | 30 +++++++++++++++--------------- src/shared/directoryentry.cpp | 5 ++--- 3 files changed, 18 insertions(+), 19 deletions(-) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a6434acf..4cd16f04 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -268,7 +268,7 @@ INCLUDE_DIRECTORIES(${Qt5Declarative_INCLUDES}) ADD_DEFINITIONS(-DQT_MESSAGELOGCONTEXT) # Boost -SET(Boost_USE_STATIC_LIBS ON) +SET(Boost_USE_STATIC_LIBS OFF) SET(Boost_USE_MULTITHREADED ON) SET(Boost_USE_STATIC_RUNTIME OFF) FIND_PACKAGE(Boost REQUIRED) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 80e3490c..16630478 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -120,7 +120,6 @@ along with Mod Organizer. If not, see . #include #include #include -#include #include #endif @@ -1148,7 +1147,7 @@ void MainWindow::updateTo(QTreeWidgetItem *subTree, const std::wstring &director void MainWindow::delayedRemove() { - foreach (QTreeWidgetItem *item, m_RemoveWidget) { + for (QTreeWidgetItem *item : m_RemoveWidget) { item->removeChild(item->child(0)); } m_RemoveWidget.clear(); @@ -1303,9 +1302,10 @@ void MainWindow::refreshSaveList() QDir savesDir = currentSavesDir(); savesDir.setNameFilters(filters); + qDebug("reading save games from %s", qPrintable(savesDir.absolutePath())); QFileInfoList files = savesDir.entryInfoList(QDir::Files, QDir::Time); - foreach (const QFileInfo &file, files) { + for (const QFileInfo &file : files) { QListWidgetItem *item = new QListWidgetItem(file.fileName()); item->setData(Qt::UserRole, file.absoluteFilePath()); ui->savegameList->addItem(item); @@ -2080,7 +2080,7 @@ void MainWindow::removeMod_clicked() if (selection->hasSelection() && selection->selectedRows().count() > 1) { QString mods; QStringList modNames; - foreach (QModelIndex idx, selection->selectedRows()) { + for (QModelIndex idx : selection->selectedRows()) { QString name = idx.data().toString(); if (!ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->isRegular()) { continue; @@ -2092,7 +2092,7 @@ void MainWindow::removeMod_clicked() tr("Remove the following mods?
    %1
").arg(mods), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { // use mod names instead of indexes because those become invalid during the removal - foreach (QString name, modNames) { + for (QString name : modNames) { m_OrganizerCore.modList()->removeRowForce(ModInfo::getIndex(name), QModelIndex()); } } @@ -2519,7 +2519,7 @@ bool MainWindow::populateMenuCategories(QMenu *menu, int targetID) void MainWindow::replaceCategoriesFromMenu(QMenu *menu, int modRow) { ModInfo::Ptr modInfo = ModInfo::getByIndex(modRow); - foreach (QAction* action, menu->actions()) { + for (QAction* action : menu->actions()) { if (action->menu() != nullptr) { replaceCategoriesFromMenu(action->menu(), modRow); } else { @@ -2652,7 +2652,7 @@ void MainWindow::savePrimaryCategory() return; } - foreach (QAction* action, menu->actions()) { + for (QAction* action : menu->actions()) { QWidgetAction *widgetAction = qobject_cast(action); if (widgetAction != nullptr) { QRadioButton *btn = qobject_cast(widgetAction->defaultWidget()); @@ -2731,7 +2731,7 @@ void MainWindow::unignoreUpdate() void MainWindow::addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, ModInfo::Ptr info) { const std::set &categories = info->getCategories(); - foreach (int categoryID, categories) { + for (int categoryID : categories) { int catIdx = m_CategoryFactory.getCategoryIndex(categoryID); QWidgetAction *action = new QWidgetAction(primaryCategoryMenu); try { @@ -2975,7 +2975,7 @@ void MainWindow::on_categoriesList_itemSelectionChanged() QModelIndexList indices = ui->categoriesList->selectionModel()->selectedRows(); std::vector categories; std::vector content; - foreach (const QModelIndex &index, indices) { + for (const QModelIndex &index : indices) { int filterType = index.data(Qt::UserRole + 1).toInt(); if ((filterType == ModListSortProxy::TYPE_CATEGORY) || (filterType == ModListSortProxy::TYPE_SPECIAL)) { @@ -3012,7 +3012,7 @@ void MainWindow::deleteSavegame_clicked() int count = 0; - foreach (const QModelIndex &idx, selectedIndexes) { + for (const QModelIndex &idx : selectedIndexes) { QString name = idx.data().toString(); SaveGame *save = new SaveGame(this, idx.data(Qt::UserRole).toString(), m_OrganizerCore.managedGame()); @@ -3300,7 +3300,7 @@ void MainWindow::installTranslator(const QString &name) void MainWindow::languageChange(const QString &newLanguage) { - foreach (QTranslator *trans, m_Translators) { + for (QTranslator *trans : m_Translators) { qApp->removeTranslator(trans); } m_Translators.clear(); @@ -3544,7 +3544,7 @@ void MainWindow::previewDataFile() }; addFunc(file->getOrigin()); - foreach (int i, file->getAlternatives()) { + for (int i : file->getAlternatives()) { addFunc(i); } if (preview.numVariants() > 0) { @@ -3774,7 +3774,7 @@ void MainWindow::nxmDownloadURLs(int, int, QVariant, QVariant resultData, int) QVariantList serverList = resultData.toList(); QList servers; - foreach (const QVariant &server, serverList) { + for (const QVariant &server : serverList) { QVariantMap serverInfo = server.toMap(); ServerInfo info; info.name = serverInfo["Name"].toString(); @@ -3897,7 +3897,7 @@ void MainWindow::displayColumnSelection(const QPoint &pos) // view/hide columns depending on check-state int i = 1; - foreach (const QAction *action, menu.actions()) { + for (const QAction *action : menu.actions()) { const QWidgetAction *widgetAction = qobject_cast(action); if (widgetAction != nullptr) { const QCheckBox *checkBox = qobject_cast(widgetAction->defaultWidget()); @@ -4395,7 +4395,7 @@ QString MainWindow::queryRestore(const QString &filePath) SelectionDialog dialog(tr("Choose backup to restore"), this); QRegExp exp(pluginFileInfo.fileName() + PATTERN_BACKUP_REGEX); QRegExp exp2(pluginFileInfo.fileName() + "\\.(.*)"); - foreach(const QFileInfo &info, files) { + for(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)); diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index e081c006..df6a7b5a 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -24,7 +24,6 @@ along with Mod Organizer. If not, see . #include #include #include -#include #define WIN32_LEAN_AND_MEAN #include #include @@ -950,12 +949,12 @@ void FileRegister::removeOriginMulti(std::set indices, int ori // the latter should be faster when there are many files in few directories. since this is called // only when disabling an origin that is probably frequently the case std::set parents; - BOOST_FOREACH (const FileEntry::Ptr &file, removedFiles) { + for (const FileEntry::Ptr &file : removedFiles) { if (file->getParent() != nullptr) { parents.insert(file->getParent()); } } - BOOST_FOREACH (DirectoryEntry *parent, parents) { + for (DirectoryEntry *parent : parents) { parent->removeFiles(indices); } } -- cgit v1.3.1 From 4f8c71d62626aa61fb4d13f80bc043d5e6492c3e Mon Sep 17 00:00:00 2001 From: Tannin Date: Fri, 12 Feb 2016 19:08:25 +0100 Subject: merged master into new_vfs_library --- SConstruct | 41 +++- massage_messages.py | 14 +- qt5_4.imp | 2 +- src/CMakeLists.txt | 21 -- src/aboutdialog.cpp | 8 + src/aboutdialog.h | 7 +- src/activatemodsdialog.cpp | 22 +- src/activatemodsdialog.h | 10 +- src/iuserinterface.h | 1 + src/mainwindow.cpp | 299 ++++++++++++--------------- src/mainwindow.h | 111 ++++++---- src/modinfo.h | 1 - src/nexusinterface.cpp | 2 +- src/organizer.pro | 9 - src/organizercore.cpp | 36 +++- src/organizercore.h | 28 ++- src/organizerproxy.cpp | 13 +- src/organizerproxy.h | 6 +- src/profile.cpp | 36 +++- src/profile.h | 19 +- src/profilesdialog.cpp | 23 ++- src/profilesdialog.h | 15 +- src/savegame.cpp | 69 ------- src/savegameinfowidget.cpp | 65 ------ src/savegameinfowidget.h | 61 ------ src/savegameinfowidget.ui | 209 ------------------- src/savegameinfowidgetgamebryo.cpp | 80 -------- src/savegameinfowidgetgamebryo.h | 44 ---- src/selfupdater.cpp | 29 ++- src/selfupdater.h | 19 +- src/shared/shared.pro | 10 - src/transfersavesdialog.cpp | 411 +++++++++++++++++++------------------ src/transfersavesdialog.h | 33 ++- win.imp | 10 +- 34 files changed, 701 insertions(+), 1063 deletions(-) delete mode 100644 src/savegame.cpp delete mode 100644 src/savegameinfowidget.cpp delete mode 100644 src/savegameinfowidget.h delete mode 100644 src/savegameinfowidget.ui delete mode 100644 src/savegameinfowidgetgamebryo.cpp delete mode 100644 src/savegameinfowidgetgamebryo.h (limited to 'src/CMakeLists.txt') diff --git a/SConstruct b/SConstruct index 013375b7..c7248251 100644 --- a/SConstruct +++ b/SConstruct @@ -431,16 +431,24 @@ msvs_version = int(env['MSVS_VERSION'].split('.')[0]) + 2000 if msvs_version > 2010: msvs_version += 1 -# Need more work on the qt version stuff -#build-ModOrganizer-Desktop_Qt_5_4_1_MSVC2013_OpenGL_32bit-Debug -build_dir = 'scons-ModOrganizer-QT_%s_for_MSVS%d_32bit-%s' % ( - #os.path.basename(env['QTDIR']).replace('.', '_'), - '5_4_1_OpenGL', +# Read the QT version info +with open(os.path.join(env['QTDIR'], 'mkspecs', 'qconfig.pri')) as qtinfo: + for line in qtinfo: + info = re.split(r'\s*=\s*', line.rstrip()) + if info[0] == 'QT_VERSION': + env[info[0]] = info[1] + elif '_VERSION' in info[0]: + env[info[0]] = int(info[1]) + +build_dir = 'scons-ModOrganizer-QT_%s_%sfor_MSVS%d_32bit-%s' % ( + env['QT_VERSION'].replace('.', '_'), + 'OpenGL_' if 'opengl' in env['QTDIR'] else '', msvs_version, config.title()) # Put the sconsign file somewhere sane env.SConsignFile(os.path.join(build_dir, '.sconsign.dblite')) +env.CacheDir(os.path.join(build_dir, '.cache')) #this doesn't seem to work #env.VariantDir('build/$CONFIG', 'source') @@ -531,9 +539,6 @@ qt_env = env.Clone() if qt_env['CONFIG'] != 'debug': qt_env.AppendUnique(CPPDEFINES = [ 'QT_NO_DEBUG' ]) -# Better way of working this out -qt_env['QT_MAJOR_VERSION'] = 5 #int(os.path.basename(env['QTDIR']).split('.')[0]) - qt_env.Tool('qt%d' % qt_env['QT_MAJOR_VERSION']) # FIXME See if I can work out how to get official scons qt to work. Appears @@ -618,6 +623,20 @@ if qt_env['QT_MAJOR_VERSION'] > 4: ] # Finally, set up rules to install the DLLs. +# use windeployqt.exe to install all required libraries +#SET(windeploy_parameters --no-translations --no-plugins --libdir dlls --release-with-debug-info --no-compiler-runtime) +#INSTALL( +# CODE +# "EXECUTE_PROCESS( +# COMMAND +# ${qt5bin}/windeployqt.exe ModOrganizer.exe ${windeploy_parameters} +# COMMAND +# ${qt5bin}/windeployqt.exe uibase.dll ${windeploy_parameters} +# WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin +# )" +#) +# this should probably be a rule though it seems to produce an awful lot of +# Stuff(TM). or a postaction dll_path = os.path.join('$INSTALL_PATH', 'DLLs') @@ -648,8 +667,10 @@ if 'WebKit' in libs_to_install and qt_env['QT_MAJOR_VERSION'] == 4: if qt_env['QT_MAJOR_VERSION'] > 4: # Guesswork a bit. dlls_to_install += [ - os.path.join(env['QTDIR'], 'bin', 'icu%s53.dll' % lib) - for lib in ('dt','in', 'uc') + os.path.join(env['QTDIR'], + 'bin', + 'icu%s%d%d.dll' % (lib, qt_env['QT_MAJOR_VERSION'], qt_env['QT_MINOR_VERSION'] - 1)) + for lib in ('dt','in', 'uc') ] platform_dlls = [] diff --git a/massage_messages.py b/massage_messages.py index d79f4cea..0a3ee9ad 100644 --- a/massage_messages.py +++ b/massage_messages.py @@ -36,6 +36,7 @@ includes = dict() adding = None added = dict() +lcadded = dict() foundline = None @@ -70,6 +71,7 @@ def process_next_line(line, outfile): m = re.match(r'.*class (.*);', line) if m: added[m.group(1)] = (adding, line) + lcadded[m.group(1).lower() + '.h'] = m.group(1) else: added[line] = (adding, line) elif removing: @@ -79,12 +81,18 @@ def process_next_line(line, outfile): m = re.match(r'- #include [<"](.*)[">] +// lines (.*)-', line) if m: foundline = m.group(2) - if m.group(1) in added: + # Note: In this project at least we have a naming convention of + # lower case filename and upper case classname. + clname = m.group(1) + if clname not in added: + if clname in lcadded: + clname = lcadded[clname] + if clname in added: messages[removing].append( '%s(%s) : warning I0004: Replace include of %s with ' 'forward reference %s' % ( - removing, m.group(2), m.group(1), added[m.group(1)][1])) - del added[m.group(1)] + removing, m.group(2), m.group(1), added[clname][1])) + del added[clname] else: messages[removing].append( '%s(%s) : warning I0001: Unnecessary include of %s' % ( diff --git a/qt5_4.imp b/qt5_4.imp index 22553b0e..5f919085 100644 --- a/qt5_4.imp +++ b/qt5_4.imp @@ -2463,7 +2463,7 @@ { include: [ "@\"(QtCore/)?qglobal\\.h\"", "private", "", "public" ] }, { include: [ "@\"(QtCore/)?qnamespace\\.h\"", "private", "", "public" ] }, { include: [ "@\"(QtCore/)?qlogging\\.h\"", "private", "", "public" ] }, #qDebug, qWarning, etc - { include: [ "@\"(QtCore/)?qalgorithms\\.h\"", "private", "", "public" ] }, #qSort, etc + { include: [ "@\"(QtCore/)?qalgorithms\\.h\"", "private", "", "public" ] }, #qSort, etc { include: [ "@\"(QtWinExtras/)?qwinfunctions\\.h\"", "private", "", "public" ] }, # for fromHICON # These ones are just madness. For instance, why with the above do we get diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4cd16f04..6eec86a9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,10 +18,6 @@ SET(organizer_SRCS settings.cpp selfupdater.cpp selectiondialog.cpp - savegameinfowidgetgamebryo.cpp - savegameinfowidget.cpp - savegamegamebryo.cpp - savegame.cpp queryoverwritedialog.cpp profilesdialog.cpp profile.cpp @@ -94,13 +90,7 @@ SET(organizer_SRCS shared/windows_error.cpp shared/error_report.cpp shared/directoryentry.cpp - shared/gameinfo.cpp - shared/oblivioninfo.cpp - shared/fallout3info.cpp - shared/fallout4info.cpp - shared/falloutnvinfo.cpp shared/util.cpp - shared/skyriminfo.cpp shared/appconfig.cpp shared/leaktrace.cpp shared/stackdata.cpp @@ -115,10 +105,6 @@ SET(organizer_HDRS settings.h selfupdater.h selectiondialog.h - savegameinfowidgetgamebryo.h - savegameinfowidget.h - savegamegamebyro.h - savegame.h queryoverwritedialog.h profilesdialog.h profile.h @@ -191,13 +177,7 @@ SET(organizer_HDRS shared/windows_error.h shared/error_report.h shared/directoryentry.h - shared/gameinfo.h - shared/oblivioninfo.h - shared/fallout3info.h - shared/fallout4info.h - shared/falloutnvinfo.h shared/util.h - shared/skyriminfo.h shared/appconfig.h shared/appconfig.inc shared/leaktrace.h @@ -210,7 +190,6 @@ SET(organizer_UIS simpleinstalldialog.ui settingsdialog.ui selectiondialog.ui - savegameinfowidget.ui queryoverwritedialog.ui profilesdialog.ui overwriteinfodialog.ui diff --git a/src/aboutdialog.cpp b/src/aboutdialog.cpp index 6fce2acb..3657a10d 100644 --- a/src/aboutdialog.cpp +++ b/src/aboutdialog.cpp @@ -22,6 +22,14 @@ along with Mod Organizer. If not, see . #include "ui_aboutdialog.h" #include +#include +#include +#include +#include +#include +#include +#include + AboutDialog::AboutDialog(const QString &version, QWidget *parent) : QDialog(parent) diff --git a/src/aboutdialog.h b/src/aboutdialog.h index c3b38c10..103a0d2f 100644 --- a/src/aboutdialog.h +++ b/src/aboutdialog.h @@ -22,10 +22,11 @@ along with Mod Organizer. If not, see . #define ABOUTDIALOG_H #include -#include +class QListWidgetItem; +#include +#include + #include -#include -#include namespace Ui { class AboutDialog; diff --git a/src/activatemodsdialog.cpp b/src/activatemodsdialog.cpp index fd56ae3e..5ef0a4e5 100644 --- a/src/activatemodsdialog.cpp +++ b/src/activatemodsdialog.cpp @@ -21,9 +21,14 @@ along with Mod Organizer. If not, see . #include "ui_activatemodsdialog.h" #include +#include #include +#include +#include -ActivateModsDialog::ActivateModsDialog(const std::map > &missingPlugins, QWidget *parent) +#include + +ActivateModsDialog::ActivateModsDialog(SaveGameInfo::MissingAssets const &missingAssets, QWidget *parent) : TutorableDialog("ActivateMods", parent), ui(new Ui::ActivateModsDialog) { ui->setupUi(this); @@ -40,18 +45,17 @@ ActivateModsDialog::ActivateModsDialog(const std::mapsetRowCount(missingPlugins.size()); + modsTable->setRowCount(missingAssets.size()); - for (std::map >::const_iterator espIter = missingPlugins.begin(); - espIter != missingPlugins.end(); ++espIter, ++row) { - modsTable->setCellWidget(row, 0, new QLabel(espIter->first)); - if (espIter->second.size() == 0) { + for (SaveGameInfo::MissingAssets::const_iterator espIter = missingAssets.begin(); + espIter != missingAssets.end(); ++espIter, ++row) { + modsTable->setCellWidget(row, 0, new QLabel(espIter.key())); + if (espIter->size() == 0) { modsTable->setCellWidget(row, 1, new QLabel(tr("not found"))); } else { QComboBox* combo = new QComboBox(); - for (std::vector::const_iterator modIter = espIter->second.begin(); - modIter != espIter->second.end(); ++modIter) { - combo->addItem(*modIter); + for (QString const &mod : espIter.value()) { + combo->addItem(mod); } modsTable->setCellWidget(row, 1, combo); } diff --git a/src/activatemodsdialog.h b/src/activatemodsdialog.h index 08dbad8d..f36b5fde 100644 --- a/src/activatemodsdialog.h +++ b/src/activatemodsdialog.h @@ -20,8 +20,14 @@ along with Mod Organizer. If not, see . #ifndef ACTIVATEMODSDIALOG_H #define ACTIVATEMODSDIALOG_H +#include "savegameinfo.h" #include "tutorabledialog.h" -#include + +#include + +class QString; +class QWidget; + #include namespace Ui { @@ -42,7 +48,7 @@ public: * @param missingPlugins a map containing missing plugins that need to be activated * @param parent ... Defaults to 0. **/ - explicit ActivateModsDialog(const std::map > &missingPlugins, QWidget *parent = 0); + explicit ActivateModsDialog(SaveGameInfo::MissingAssets const &missingAssets, QWidget *parent = 0); ~ActivateModsDialog(); /** diff --git a/src/iuserinterface.h b/src/iuserinterface.h index e6b39ee1..19e58c75 100644 --- a/src/iuserinterface.h +++ b/src/iuserinterface.h @@ -7,6 +7,7 @@ #include #include +class QSettings; class IUserInterface { diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index c242bfe6..db7bd682 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -20,7 +20,24 @@ along with Mod Organizer. If not, see . #include "mainwindow.h" #include "ui_mainwindow.h" +#include "directoryentry.h" +#include "directoryrefresher.h" +#include "executableinfo.h" +#include "executableslist.h" +#include "guessedvalue.h" +#include "imodinterface.h" +#include "iplugingame.h" +#include "iplugindiagnose.h" +#include "isavegame.h" +#include "isavegameinfowidget.h" +#include "nexusinterface.h" +#include "organizercore.h" +#include "pluginlistsortproxy.h" +#include "previewgenerator.h" +#include "savegameinfo.h" #include "spawn.h" +#include "versioninfo.h" + #include "report.h" #include "modlist.h" #include "modlistsortproxy.h" @@ -31,7 +48,6 @@ along with Mod Organizer. If not, see . #include "editexecutablesdialog.h" #include "categories.h" #include "categoriesdialog.h" -#include "utility.h" #include "modinfodialog.h" #include "overwriteinfodialog.h" #include "activatemodsdialog.h" @@ -41,16 +57,13 @@ along with Mod Organizer. If not, see . #include "messagedialog.h" #include "installationmanager.h" #include "lockeddialog.h" -#include "syncoverwritedialog.h" #include "logbuffer.h" #include "downloadlistsortproxy.h" #include "motddialog.h" #include "filedialogmemory.h" -#include "questionboxmemory.h" #include "tutorialmanager.h" #include "modflagicondelegate.h" #include "genericicondelegate.h" -#include "credentialsdialog.h" #include "selectiondialog.h" #include "csvbuilder.h" #include "savetextasdialog.h" @@ -59,62 +72,86 @@ along with Mod Organizer. If not, see . #include "browserdialog.h" #include "aboutdialog.h" #include "safewritefile.h" -//? -//#include "isavegame.h" -//#include "savegameinfo.h" -//? #include "nxmaccessmanager.h" -#include -#include +#include "appconfig.h" #include -#include #include #include -#include #include -#include #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include -#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) #include #else #include #endif -#include -#include #ifndef Q_MOC_RUN #include @@ -123,18 +160,16 @@ along with Mod Organizer. If not, see . #include #endif -#include #include -#include -#include -#include -#include +#include +#include #include #include -#include -#include -#include +#include +#include +#include +#include #ifdef TEST_MODELS #include "modeltest.h" @@ -826,37 +861,24 @@ void MainWindow::setBrowserGeometry(const QByteArray &geometry) m_IntegratedBrowser.restoreGeometry(geometry); } - -SaveGameGamebryo *MainWindow::getSaveGame(const QString &name) -{ - IPluginGame const *game = m_OrganizerCore.managedGame(); - return new SaveGameGamebryo(this, name, game); -} - - -SaveGameGamebryo *MainWindow::getSaveGame(QListWidgetItem *item) -{ - try { - SaveGameGamebryo *saveGame = getSaveGame(item->data(Qt::UserRole).toString()); - saveGame->setParent(item->listWidget()); - return saveGame; - } catch (const std::exception &e) { - reportError(tr("failed to read savegame: %1").arg(e.what())); - return nullptr; - } -} - - -void MainWindow::displaySaveGameInfo(const SaveGameGamebryo *save, QPoint pos) +void MainWindow::displaySaveGameInfo(QListWidgetItem *newItem) { + QString const &save = newItem->data(Qt::UserRole).toString(); if (m_CurrentSaveView == nullptr) { - m_CurrentSaveView = new SaveGameInfoWidgetGamebryo(save, m_OrganizerCore.pluginList(), this); - } else { - m_CurrentSaveView->setSave(save); + IPluginGame const *game = m_OrganizerCore.managedGame(); + SaveGameInfo const *info = game->feature(); + if (info != nullptr) { + m_CurrentSaveView = info->getSaveGameWidget(this); + } + if (m_CurrentSaveView == nullptr) { + return; + } } + m_CurrentSaveView->setSave(save); QRect screenRect = QApplication::desktop()->availableGeometry(m_CurrentSaveView); + QPoint pos = QCursor::pos(); if (pos.x() + m_CurrentSaveView->width() > screenRect.right()) { pos.rx() -= (m_CurrentSaveView->width() + 2); } else { @@ -871,8 +893,9 @@ void MainWindow::displaySaveGameInfo(const SaveGameGamebryo *save, QPoint pos) m_CurrentSaveView->move(pos); m_CurrentSaveView->show(); + m_CurrentSaveView->setProperty("displayItem", qVariantFromValue(static_cast(newItem))); + ui->savegameList->activateWindow(); - connect(m_CurrentSaveView, SIGNAL(closeSaveInfo()), this, SLOT(hideSaveGameInfo())); } @@ -880,21 +903,15 @@ void MainWindow::saveSelectionChanged(QListWidgetItem *newItem) { if (newItem == nullptr) { hideSaveGameInfo(); - } else if ((m_CurrentSaveView == nullptr) || (newItem != m_CurrentSaveView->property("displayItem").value())) { - const SaveGameGamebryo *save = getSaveGame(newItem); - if (save != nullptr) { - displaySaveGameInfo(save, QCursor::pos()); - m_CurrentSaveView->setProperty("displayItem", qVariantFromValue((void*)newItem)); - } + } else if (m_CurrentSaveView == nullptr || newItem != m_CurrentSaveView->property("displayItem").value()) { + displaySaveGameInfo(newItem); } } - void MainWindow::hideSaveGameInfo() { if (m_CurrentSaveView != nullptr) { - disconnect(m_CurrentSaveView, SIGNAL(closeSaveInfo()), this, SLOT(hideSaveGameInfo())); m_CurrentSaveView->deleteLater(); m_CurrentSaveView = nullptr; } @@ -1258,7 +1275,7 @@ QDir MainWindow::currentSavesDir() const { QDir savesDir; if (m_OrganizerCore.currentProfile()->localSavesEnabled()) { - savesDir.setPath(m_OrganizerCore.currentProfile()->absolutePath() + "/saves"); + savesDir.setPath(m_OrganizerCore.currentProfile()->savePath()); } else { wchar_t path[MAX_PATH]; ::GetPrivateProfileStringW( @@ -3002,109 +3019,47 @@ void MainWindow::on_categoriesList_itemSelectionChanged() void MainWindow::deleteSavegame_clicked() { - QModelIndexList selectedIndexes = ui->savegameList->selectionModel()->selectedIndexes(); + SaveGameInfo const *info = m_OrganizerCore.managedGame()->feature(); QString savesMsgLabel; QStringList deleteFiles; int count = 0; - for (const QModelIndex &idx : selectedIndexes) { - QString name = idx.data().toString(); - SaveGame *save = new SaveGame(this, idx.data(Qt::UserRole).toString(), m_OrganizerCore.managedGame()); + for (const QModelIndex &idx : ui->savegameList->selectionModel()->selectedIndexes()) { + QString name = idx.data(Qt::UserRole).toString(); if (count < 10) { savesMsgLabel += "
  • " + QFileInfo(name).completeBaseName() + "
  • "; } ++count; - deleteFiles << save->saveFiles(); + if (info == nullptr) { + deleteFiles.push_back(name); + } else { + ISaveGame const *save = info->getSaveGameInfo(name); + deleteFiles += save->allFiles(); + } } if (count > 10) { savesMsgLabel += "
  • ... " + tr("%1 more").arg(count - 10) + "
  • "; } - if (QMessageBox::question(this, tr("Confirm"), tr("Are you sure you want to remove the following %n save(s)?
      %1

    Removed saves will be sent to the Recycle Bin.", "", selectedIndexes.count()) - .arg(savesMsgLabel), + if (QMessageBox::question(this, tr("Confirm"), + tr("Are you sure you want to remove the following %n save(s)?
    " + "
      %1

    " + "Removed saves will be sent to the Recycle Bin.", "", count) + .arg(savesMsgLabel), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { shellDelete(deleteFiles, true); // recycle bin delete. } } -void MainWindow::fixMods_clicked() +void MainWindow::fixMods_clicked(SaveGameInfo::MissingAssets const &missingAssets) { - QListWidgetItem *selectedItem = ui->savegameList->currentItem(); - - if (selectedItem == nullptr) - return; - - // if required, parse the save game - if (selectedItem->data(Qt::UserRole).isNull()) { - QVariant temp; - SaveGameGamebryo *save = getSaveGame(selectedItem->data(Qt::UserRole).toString()); - save->setParent(selectedItem->listWidget()); - temp.setValue(save); - selectedItem->setData(Qt::UserRole, temp); - } - - const SaveGameGamebryo *save = getSaveGame(selectedItem); - - // collect the list of missing plugins - std::map > missingPlugins; - - for (int i = 0; i < save->numPlugins(); ++i) { - const QString &pluginName = save->plugin(i); - if (!m_OrganizerCore.pluginList()->isEnabled(pluginName)) { - missingPlugins[pluginName] = std::vector(); - } - } - - // figure out, for each esp/esm, which mod, if any, contains it - QStringList espFilter("*.esp"); - espFilter.append("*.esm"); - - // search in data - { - QDir dataDir(m_OrganizerCore.managedGame()->dataDirectory()); - QStringList esps = dataDir.entryList(espFilter); - for (const QString &esp : esps) { - std::map >::iterator iter = missingPlugins.find(esp); - if (iter != missingPlugins.end()) { - iter->second.push_back(""); - } - } - } - - // search in mods - for (unsigned int i = 0; i < m_OrganizerCore.currentProfile()->numRegularMods(); ++i) { - int modIndex = m_OrganizerCore.currentProfile()->modIndexByPriority(i); - ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); - - QStringList esps = QDir(modInfo->absolutePath()).entryList(espFilter); - for (const QString &esp : esps) { - std::map >::iterator iter = missingPlugins.find(esp); - if (iter != missingPlugins.end()) { - iter->second.push_back(modInfo->name()); - } - } - } - - // search in overwrite - { - QDir overwriteDir(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::overwritePath())); - QStringList esps = overwriteDir.entryList(espFilter); - for (const QString &esp : esps) { - std::map >::iterator iter = missingPlugins.find(esp); - if (iter != missingPlugins.end()) { - iter->second.push_back(""); - } - } - } - - - ActivateModsDialog dialog(missingPlugins, this); + ActivateModsDialog dialog(missingAssets, this); if (dialog.exec() == QDialog::Accepted) { // activate the required mods, then enable all esps std::set modsToActivate = dialog.getModsToActivate(); @@ -3131,13 +3086,25 @@ void MainWindow::on_savegameList_customContextMenuRequested(const QPoint &pos) { QItemSelectionModel *selection = ui->savegameList->selectionModel(); - if (!selection->hasSelection()) + if (!selection->hasSelection()) { return; + } QMenu menu; - - if (!(selection->selectedIndexes().count() > 1)) - menu.addAction(tr("Fix Mods..."), this, SLOT(fixMods_clicked())); + QAction *action = menu.addAction(tr("Enable Mods...")); + action->setEnabled(false); + + if (selection->selectedIndexes().count() == 1) { + SaveGameInfo const *info = this->m_OrganizerCore.managedGame()->feature(); + if (info != nullptr) { + QString save = ui->savegameList->currentItem()->data(Qt::UserRole).toString(); + SaveGameInfo::MissingAssets missing = info->getMissingAssets(save); + if (missing.size() != 0) { + connect(action, &QAction::triggered, this, [this, missing]{ fixMods_clicked(missing); }); + action->setEnabled(true); + } + } + } QString deleteMenuLabel = tr("Delete %n save(s)", "", selection->selectedIndexes().count()); diff --git a/src/mainwindow.h b/src/mainwindow.h index 636fb0a3..4e2e8e21 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -20,50 +20,83 @@ along with Mod Organizer. If not, see . #ifndef MAINWINDOW_H #define MAINWINDOW_H -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "modlist.h" -#include "pluginlist.h" -#include "plugincontainer.h" -#define WIN32_LEAN_AND_MEAN -#include -#include -#include "directoryrefresher.h" -#include -#include "settings.h" -#include "downloadmanager.h" -#include "installationmanager.h" -#include "selfupdater.h" -#include "savegamegamebyro.h" -#include "modlistsortproxy.h" -#include "pluginlistsortproxy.h" -#include "tutorialcontrol.h" -#include "savegameinfowidgetgamebryo.h" -#include "previewgenerator.h" +#include "bsafolder.h" #include "browserdialog.h" +#include "delayedfilewriter.h" +#include "errorcodes.h" +#include "imoinfo.h" #include "iuserinterface.h" -#include -#include -#include +#include "modinfo.h" +#include "modlistsortproxy.h" +#include "savegameinfo.h" +#include "tutorialcontrol.h" + +//Note the commented headers here can be replaced with forward references, +//when I get round to cleaning up main.cpp +struct Executable; +class CategoryFactory; +class LockedDialog; +class OrganizerCore; +#include "plugincontainer.h" //class PluginContainer; +class PluginListSortProxy; +namespace BSA { class Archive; } +#include "iplugingame.h" //namespace MOBase { class IPluginGame; } +namespace MOBase { class IPluginModPage; } +namespace MOBase { class IPluginTool; } +namespace MOBase { class ISaveGame; } + +namespace MOShared { class DirectoryEntry; } + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class QAction; +class QAbstractItemModel; +class QDateTime; +class QEvent; +class QFile; +class QListWidgetItem; +class QMenu; +class QModelIndex; +class QPoint; +class QProgressBar; +class QProgressDialog; +class QTranslator; +class QTreeWidgetItem; +class QUrl; +class QSettings; +class QWidget; + #ifndef Q_MOC_RUN #include #endif +//Sigh - just for HANDLE +#define WIN32_LEAN_AND_MEAN +#include + +#include +#include +#include +#include + namespace Ui { class MainWindow; } -class LockedDialog; -class QToolButton; -class ModListSortProxy; -class ModListGroupCategoriesProxy; class MainWindow : public QMainWindow, public IUserInterface @@ -217,10 +250,7 @@ private: void setCategoryListVisible(bool visible); - SaveGameGamebryo *getSaveGame(const QString &name); - SaveGameGamebryo *getSaveGame(QListWidgetItem *item); - - void displaySaveGameInfo(const SaveGameGamebryo *save, QPoint pos); + void displaySaveGameInfo(QListWidgetItem *newItem); HANDLE nextChildProcess(); @@ -293,7 +323,8 @@ private: QTimer m_UpdateProblemsTimer; QTime m_StartTime; - SaveGameInfoWidget *m_CurrentSaveView; + //SaveGameInfoWidget *m_CurrentSaveView; + MOBase::ISaveGameInfoWidget *m_CurrentSaveView; OrganizerCore &m_OrganizerCore; PluginContainer &m_PluginContainer; @@ -352,7 +383,7 @@ private slots: void information_clicked(); // savegame context menu void deleteSavegame_clicked(); - void fixMods_clicked(); + void fixMods_clicked(SaveGameInfo::MissingAssets const &missingAssets); // data-tree context menu void writeDataToFile(); void openDataFile(); diff --git a/src/modinfo.h b/src/modinfo.h index ae22ccd8..c10232da 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -22,7 +22,6 @@ along with Mod Organizer. If not, see . #include "imodinterface.h" #include "versioninfo.h" -//#include class QDateTime; class QDir; diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index f9d9b6bd..c689501f 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -193,7 +193,7 @@ void NexusInterface::loginCompleted() void NexusInterface::interpretNexusFileName(const QString &fileName, QString &modName, int &modID, bool query) { //Look for something along the lines of modulename-Vn-m + any old rubbish. - static std::regex exp("^([a-zA-Z0-9_'\"\\-.() ]*?)([-_ ][VvRr]?[0-9_]+)?-([1-9][0-9]+).*\.(zip|rar|7z)"); + static std::regex exp("^([a-zA-Z0-9_'\"\\-.() ]*?)([-_ ][VvRr]?[0-9_]+)?-([1-9][0-9]+).*\\.(zip|rar|7z)"); static std::regex simpleexp("^([a-zA-Z0-9_]+)"); QByteArray fileNameUTF8 = fileName.toUtf8(); diff --git a/src/organizer.pro b/src/organizer.pro index 1284aa40..0ef2638b 100644 --- a/src/organizer.pro +++ b/src/organizer.pro @@ -27,10 +27,6 @@ SOURCES += \ settings.cpp \ selfupdater.cpp \ selectiondialog.cpp \ - savegameinfowidgetgamebryo.cpp \ - savegameinfowidget.cpp \ - savegamegamebryo.cpp \ - savegame.cpp \ queryoverwritedialog.cpp \ profilesdialog.cpp \ profile.cpp \ @@ -107,10 +103,6 @@ HEADERS += \ settings.h \ selfupdater.h \ selectiondialog.h \ - savegameinfowidgetgamebryo.h \ - savegameinfowidget.h \ - savegamegamebyro.h \ - savegame.h \ queryoverwritedialog.h \ profilesdialog.h \ profile.h \ @@ -183,7 +175,6 @@ FORMS += \ simpleinstalldialog.ui \ settingsdialog.ui \ selectiondialog.ui \ - savegameinfowidget.ui \ queryoverwritedialog.ui \ profilesdialog.ui \ overwriteinfodialog.ui \ diff --git a/src/organizercore.cpp b/src/organizercore.cpp index ecadfb5d..e50f1cd7 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -1,15 +1,19 @@ #include "organizercore.h" +#include "imodinterface.h" #include "iplugingame.h" -#include "mainwindow.h" +#include "iuserinterface.h" +#include "loadmechanism.h" #include "messagedialog.h" +#include "modlistsortproxy.h" +#include "plugincontainer.h" +#include "pluginlistsortproxy.h" #include "logbuffer.h" #include "credentialsdialog.h" #include "filedialogmemory.h" #include "lockeddialog.h" #include "modinfodialog.h" #include "spawn.h" -#include "safewritefile.h" #include "syncoverwritedialog.h" #include "nxmaccessmanager.h" #include @@ -18,19 +22,29 @@ #include #include #include -#include +#include "appconfig.h" #include #include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include + +#include #include +#include #include #include +#include +#include +#include + using namespace MOShared; using namespace MOBase; @@ -953,6 +967,16 @@ ModList *OrganizerCore::modList() return &m_ModList; } +QStringList OrganizerCore::modsSortedByProfilePriority() const +{ + QStringList res; + for (unsigned int i = 0; i < currentProfile()->numRegularMods(); ++i) { + int modIndex = currentProfile()->modIndexByPriority(i); + res.push_back(ModInfo::getByIndex(modIndex)->name()); + } + return res; +} + void OrganizerCore::spawnBinary(const QFileInfo &binary, const QString &arguments, const QDir ¤tDirectory, diff --git a/src/organizercore.h b/src/organizercore.h index bf6c5a8b..ea37d72c 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -2,31 +2,44 @@ #define ORGANIZERCORE_H -#include "profile.h" #include "selfupdater.h" -#include "iuserinterface.h" +#include "iuserinterface.h" //should be class IUserInterface; #include "settings.h" #include "modlist.h" +#include "modinfo.h" #include "pluginlist.h" #include "directoryrefresher.h" #include "installationmanager.h" #include "downloadmanager.h" -#include "modlistsortproxy.h" -#include "pluginlistsortproxy.h" #include "executableslist.h" #include "usvfsconnector.h" #include #include #include -#include #include -#include #include #include + +class ModListSortProxy; +class PluginListSortProxy; +class Profile; +namespace MOBase { template class GuessedValue; } +namespace MOShared { class DirectoryEntry; } + +#include +#include +#include #include #include +#include #include +class QNetworkReply; +class QUrl; +class QWidget; + +#include +#include class PluginContainer; @@ -90,7 +103,7 @@ public: m_ExecutablesList = executablesList; } - Profile *currentProfile() { return m_CurrentProfile; } + Profile *currentProfile() const { return m_CurrentProfile; } void setCurrentProfile(const QString &profileName); std::vector enabledArchives(); @@ -158,6 +171,7 @@ public: bool onAboutToRun(const std::function &func); bool onFinishedRun(const std::function &func); void refreshModList(bool saveChanges = true); + QStringList modsSortedByProfilePriority() const; /** * @brief return a descriptor of the mappings real file->virtual file diff --git a/src/organizerproxy.cpp b/src/organizerproxy.cpp index 79ff50e5..fe6809e3 100644 --- a/src/organizerproxy.cpp +++ b/src/organizerproxy.cpp @@ -1,6 +1,7 @@ #include "organizerproxy.h" -#include +#include "appconfig.h" +#include "organizercore.h" #include @@ -171,7 +172,17 @@ MOBase::IModList *OrganizerProxy::modList() const return m_Proxied->modList(); } +MOBase::IProfile *OrganizerProxy::profile() const +{ + return m_Proxied->currentProfile(); +} + MOBase::IPluginGame const *OrganizerProxy::managedGame() const { return m_Proxied->managedGame(); } + +QStringList OrganizerProxy::modsSortedByProfilePriority() const +{ + return m_Proxied->modsSortedByProfilePriority(); +} diff --git a/src/organizerproxy.h b/src/organizerproxy.h index 02507dd7..77600671 100644 --- a/src/organizerproxy.h +++ b/src/organizerproxy.h @@ -3,7 +3,8 @@ #include -#include "mainwindow.h" + +class OrganizerCore; class OrganizerProxy : public MOBase::IOrganizer { @@ -38,6 +39,7 @@ public: virtual MOBase::IDownloadManager *downloadManager() const; virtual MOBase::IPluginList *pluginList() const; virtual MOBase::IModList *modList() const; + virtual MOBase::IProfile *profile() const override; virtual HANDLE startApplication(const QString &executable, const QStringList &args = QStringList(), const QString &cwd = "", const QString &profile = ""); virtual bool waitForApplication(HANDLE handle, LPDWORD exitCode = nullptr) const; virtual void refreshModList(bool saveChanges); @@ -48,6 +50,8 @@ public: virtual MOBase::IPluginGame const *managedGame() const; + virtual QStringList modsSortedByProfilePriority() const; + private: OrganizerCore *m_Proxied; diff --git a/src/profile.cpp b/src/profile.cpp index 99a6bd90..f4aab46b 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -19,31 +19,41 @@ along with Mod Organizer. If not, see . #include "profile.h" -#include "windows_error.h" #include "modinfo.h" #include "safewritefile.h" #include "settings.h" #include -#include #include -#include +#include "appconfig.h" #include #include #include #include -#include #include -#include -#include +#include // for QFile +#include // for operator|, QFlags +#include // for QIODevice, etc +#include +#include +#include // for QStringList +#include // for qDebug, qWarning, etc +#include // for qPrintable + +#include +#include // for assert +#include // for UINT_MAX, INT_MAX, etc +#include // for size_t +#include // for wcslen + +#include // for max, min +#include // for exception #include +#include // for set +#include // for find #include -#define WIN32_LEAN_AND_MEAN -#include -#include - using namespace MOBase; using namespace MOShared; @@ -690,6 +700,12 @@ QString Profile::absolutePath() const return QDir::cleanPath(m_Directory.absolutePath()); } +QString Profile::savePath() const +{ + return QDir::cleanPath(m_Directory.absoluteFilePath("saves")); + +} + void Profile::rename(const QString &newName) { QDir profileDir(Settings::instance().getProfileDirectory()); diff --git a/src/profile.h b/src/profile.h index a7d71251..ece23ef9 100644 --- a/src/profile.h +++ b/src/profile.h @@ -25,12 +25,16 @@ along with Mod Organizer. If not, see . #include #include -#include +#include #include -#include +#include +#include -#include +#include + +#include #include +#include namespace MOBase { class IPluginGame; } @@ -172,6 +176,15 @@ public: **/ QString absolutePath() const; + /** + * @return path to this profile's save games + **/ + QString savePath() const; + + /** + * @brief rename profile + * @param newName new name of profile + */ void rename(const QString &newName); /** diff --git a/src/profilesdialog.cpp b/src/profilesdialog.cpp index 49fbda4e..1a47cf74 100644 --- a/src/profilesdialog.cpp +++ b/src/profilesdialog.cpp @@ -19,23 +19,28 @@ along with Mod Organizer. If not, see . #include "profilesdialog.h" #include "ui_profilesdialog.h" + +#include "appconfig.h" +#include "bsainvalidation.h" +#include "iplugingame.h" +#include "profile.h" +#include "profileinputdialog.h" #include "report.h" -#include "utility.h" #include "transfersavesdialog.h" -#include "profileinputdialog.h" -#include "mainwindow.h" -#include "aboutdialog.h" +#include "utility.h" #include "settings.h" -#include -#include -#include -#include + +#include +#include #include #include -#include +#include #include #include +#include + +#include using namespace MOBase; using namespace MOShared; diff --git a/src/profilesdialog.h b/src/profilesdialog.h index 073d92b7..0e79b94b 100644 --- a/src/profilesdialog.h +++ b/src/profilesdialog.h @@ -21,17 +21,16 @@ along with Mod Organizer. If not, see . #define PROFILESDIALOG_H #include "tutorabledialog.h" -#include -#include "profile.h" +class Profile; +class QListWidget; +class QListWidgetItem; +#include +class QString; -namespace Ui { - class ProfilesDialog; -} +namespace Ui { class ProfilesDialog; } -namespace MOBase { - class IPluginGame; -} +namespace MOBase { class IPluginGame; } /** diff --git a/src/savegame.cpp b/src/savegame.cpp deleted file mode 100644 index 2b125575..00000000 --- a/src/savegame.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#include "savegame.h" - -#include "iplugingame.h" -#include "scriptextender.h" -#include "utility.h" - -#include -#include -#include -#include - -#include -#include - -using namespace MOBase; - -SaveGame::SaveGame(QObject *parent, const QString &filename, const MOBase::IPluginGame *game) - : QObject(parent) - , m_FileName(filename) - , m_Game(game) -{ -} - -SaveGame::~SaveGame() -{ -} - -QStringList SaveGame::attachedFiles() const -{ - QStringList result; - ScriptExtender const *extender = m_Game->feature(); - if (extender != nullptr) { - for (QString const &ext : extender->saveGameAttachmentExtensions()) { - QFileInfo fi(fileName()); - fi.setFile(fi.canonicalPath() + "/" + fi.completeBaseName() + "." + ext); - if (fi.exists()) { - result.append(fi.filePath()); - } - } - } - - return result; -} - -QStringList SaveGame::saveFiles() const -{ - QStringList result = attachedFiles(); - result.append(fileName()); - return result; -} diff --git a/src/savegameinfowidget.cpp b/src/savegameinfowidget.cpp deleted file mode 100644 index 63eefb93..00000000 --- a/src/savegameinfowidget.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#include "savegameinfowidget.h" -#include "ui_savegameinfowidget.h" -#include "utility.h" -#include "savegame.h" -#include - - -SaveGameInfoWidget::SaveGameInfoWidget(QWidget *parent) - : QWidget(parent) - , ui(new Ui::SaveGameInfoWidget) -{ - ui->setupUi(this); - this->setWindowFlags(Qt::ToolTip | Qt::BypassGraphicsProxyWidget); - setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, 0, this) / qreal(255.0)); - ui->gameFrame->setStyleSheet("background-color: transparent;"); -// installEventFilter(this); -} - -SaveGameInfoWidget::~SaveGameInfoWidget() -{ - delete ui; -} - - -void SaveGameInfoWidget::setSave(const SaveGame *saveGame) -{ - ui->saveNumLabel->setText(QString("%1").arg(saveGame->saveNumber())); - ui->characterLabel->setText(saveGame->pcName()); - ui->locationLabel->setText(saveGame->pcLocation()); - ui->levelLabel->setText(QString("%1").arg(saveGame->pcLevel())); - ui->dateLabel->setText(MOBase::ToString(saveGame->creationTime())); - ui->screenshotLabel->setPixmap(QPixmap::fromImage(saveGame->screenshot())); - if (ui->gameFrame->layout() != nullptr) { - QLayoutItem *item = nullptr; - while ((item = ui->gameFrame->layout()->takeAt(0)) != nullptr) { - delete item->widget(); - delete item; - } - ui->gameFrame->layout()->setSizeConstraint(QLayout::SetFixedSize); - } -} - -QFrame *SaveGameInfoWidget::getGameFrame() -{ - return ui->gameFrame; -} diff --git a/src/savegameinfowidget.h b/src/savegameinfowidget.h deleted file mode 100644 index dfb0f8b1..00000000 --- a/src/savegameinfowidget.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#ifndef SAVEGAMEINFOWIDGET_H -#define SAVEGAMEINFOWIDGET_H - -#include -#include - -namespace Ui { -class SaveGameInfoWidget; -} - -class SaveGame; - -class SaveGameInfoWidget : public QWidget -{ - Q_OBJECT - -public: - - explicit SaveGameInfoWidget(QWidget *parent = 0); - ~SaveGameInfoWidget(); - - virtual void setSave(const SaveGame *saveGame); - -signals: - - void closeSaveInfo(); - -protected: - -// virtual bool eventFilter(QObject *object, QEvent *event); - - QFrame *getGameFrame(); - -private: - -private: - - Ui::SaveGameInfoWidget *ui; - -}; - -#endif // SAVEGAMEINFOWIDGET_H diff --git a/src/savegameinfowidget.ui b/src/savegameinfowidget.ui deleted file mode 100644 index e2e9588c..00000000 --- a/src/savegameinfowidget.ui +++ /dev/null @@ -1,209 +0,0 @@ - - - SaveGameInfoWidget - - - - 0 - 0 - 400 - 300 - - - - - 0 - 0 - - - - - - - - - - - - - QFormLayout::AllNonFixedFieldsGrow - - - - - - true - - - - Save # - - - - - - - - true - - - - Character - - - - - - - - true - - - - Level - - - - - - - - true - - - - Location - - - - - - - - true - - - - Date - - - - - - - - 75 - true - - - - - - - - - - - - 75 - true - - - - - - - - - - - - 75 - true - - - - - - - - - - - - 75 - true - - - - - - - - - - - - 75 - true - - - - - - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - false - - - - - - Qt::AlignCenter - - - - - - - - diff --git a/src/savegameinfowidgetgamebryo.cpp b/src/savegameinfowidgetgamebryo.cpp deleted file mode 100644 index c97e5810..00000000 --- a/src/savegameinfowidgetgamebryo.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#include "savegamegamebyro.h" -#include "savegameinfowidgetgamebryo.h" -#include -#include - - -SaveGameInfoWidgetGamebryo::SaveGameInfoWidgetGamebryo(const SaveGame *saveGame, PluginList *pluginList, QWidget *parent) - : SaveGameInfoWidget(parent), m_PluginList(pluginList) -{ - QVBoxLayout *gameLayout = new QVBoxLayout(); - gameLayout->setMargin(0); - gameLayout->setSpacing(2); - getGameFrame()->setLayout(gameLayout); - setSave(saveGame); -} - - -void SaveGameInfoWidgetGamebryo::setSave(const SaveGame *saveGame) -{ - SaveGameInfoWidget::setSave(saveGame); - const SaveGameGamebryo *gamebryoSave = qobject_cast(saveGame); - QLayout *layout = getGameFrame()->layout(); - QLabel *header = new QLabel(tr("Missing ESPs")); - QFont headerFont = header->font(); - QFont contentFont = headerFont; - headerFont.setItalic(true); - contentFont.setBold(true); - contentFont.setPointSize(7); - header->setFont(headerFont); - layout->addWidget(header); - int count = 0; - for (int i = 0; i < gamebryoSave->numPlugins(); ++i) { - const QString &pluginName = gamebryoSave->plugin(i); - if (m_PluginList->isEnabled(pluginName)) { - continue; - } else { - ++count; - } - - if (count > 10) { - break; - } - - QLabel *pluginLabel = new QLabel(gamebryoSave->plugin(i)); - pluginLabel->setIndent(10); - pluginLabel->setFont(contentFont); - layout->addWidget(pluginLabel); - } - if (count > 10) { - QLabel *dotDotLabel = new QLabel("..."); - dotDotLabel->setIndent(10); - dotDotLabel->setFont(contentFont); - layout->addWidget(dotDotLabel); - } - if (count == 0) { - QLabel *dotDotLabel = new QLabel(tr("None")); - dotDotLabel->setIndent(10); - dotDotLabel->setFont(contentFont); - layout->addWidget(dotDotLabel); - } -} diff --git a/src/savegameinfowidgetgamebryo.h b/src/savegameinfowidgetgamebryo.h deleted file mode 100644 index 149a8885..00000000 --- a/src/savegameinfowidgetgamebryo.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#ifndef SAVEGAMEINFOWIDGETGAMEBRYO_H -#define SAVEGAMEINFOWIDGETGAMEBRYO_H - - -#include "savegameinfowidget.h" -#include "pluginlist.h" - - -class SaveGame; - -class SaveGameInfoWidgetGamebryo : public SaveGameInfoWidget -{ -public: - - explicit SaveGameInfoWidgetGamebryo(const SaveGame *saveGame, PluginList *pluginList, QWidget *parent = 0); - - virtual void setSave(const SaveGame *saveGame); - -private: - - PluginList *m_PluginList; - -}; - -#endif // SAVEGAMEINFOWIDGETGAMEBRYO_H diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp index ae75b9cb..7733ae80 100644 --- a/src/selfupdater.cpp +++ b/src/selfupdater.cpp @@ -19,6 +19,8 @@ along with Mod Organizer. If not, see . #include "selfupdater.h" +#include "archive.h" +#include "callback.h" #include "utility.h" #include "installationmanager.h" #include "iplugingame.h" @@ -30,15 +32,36 @@ along with Mod Organizer. If not, see . #include #include +#include +#include +#include +#include +#include #include #include #include -#include -#include +#include #include -#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + #include +#include //for VS_FIXEDFILEINFO, GetLastError + +#include +#include +#include //for size_t +#include using namespace MOBase; using namespace MOShared; diff --git a/src/selfupdater.h b/src/selfupdater.h index 0a31b0f3..37021b42 100644 --- a/src/selfupdater.h +++ b/src/selfupdater.h @@ -21,19 +21,20 @@ along with Mod Organizer. If not, see . #define SELFUPDATER_H -#include #include -#include -#include -#include -#include - -#include - +class Archive; +class NexusInterface; namespace MOBase { class IPluginGame; } -class NexusInterface; +#include +#include +#include +#include +#include //for qint64 + +class QNetworkReply; +class QProgressDialog; /** diff --git a/src/shared/shared.pro b/src/shared/shared.pro index a64e781d..517e1e86 100644 --- a/src/shared/shared.pro +++ b/src/shared/shared.pro @@ -21,12 +21,7 @@ SOURCES += \ windows_error.cpp \ error_report.cpp \ directoryentry.cpp \ - gameinfo.cpp \ - oblivioninfo.cpp \ - fallout3info.cpp \ - falloutnvinfo.cpp \ util.cpp \ - skyriminfo.cpp \ appconfig.cpp \ leaktrace.cpp \ stackdata.cpp @@ -36,12 +31,7 @@ HEADERS += \ windows_error.h \ error_report.h \ directoryentry.h \ - gameinfo.h \ - oblivioninfo.h \ - fallout3info.h \ - falloutnvinfo.h \ util.h \ - skyriminfo.h \ appconfig.h \ appconfig.inc \ leaktrace.h \ diff --git a/src/transfersavesdialog.cpp b/src/transfersavesdialog.cpp index 838d12ce..e130b1ae 100644 --- a/src/transfersavesdialog.cpp +++ b/src/transfersavesdialog.cpp @@ -21,19 +21,83 @@ along with Mod Organizer. If not, see . #include "ui_transfersavesdialog.h" #include "iplugingame.h" -#include "savegamegamebyro.h" -#include "utility.h" +#include "isavegame.h" +#include "savegameinfo.h" +#include +#include +#include #include +#include +#include +#include +#include +#include +#include #include - -#include -#include - +#include +#include using namespace MOBase; using namespace MOShared; +//These two classes give the save-transfer box a smidgin of useful info even +//if save game isn't supported yet. +namespace { + +class DummySave : public ISaveGame +{ +public: + DummySave(QString const &filename) : + m_File(filename) + {} + + ~DummySave() {} + + virtual QString getFilename() const override + { + return m_File; + } + + virtual QDateTime getCreationTime() const override + { + return QFileInfo(m_File).created(); + } + + virtual QString getSaveGroupIdentifier() const override + { + return m_File; + } + + virtual QStringList allFiles() const override + { + return { m_File }; + } + +private: + QString m_File; +}; + +class DummyInfo : public SaveGameInfo +{ +public: + virtual MOBase::ISaveGame const *getSaveGameInfo(QString const &file) const override + { + return new DummySave(file); + } + + virtual MissingAssets getMissingAssets(QString const &) const override + { + return {}; + } + + MOBase::ISaveGameInfoWidget *getSaveGameWidget(QWidget *) const override + { + return nullptr; + } +}; + +} //end anonymous namespace TransferSavesDialog::TransferSavesDialog(const Profile &profile, IPluginGame const *gamePlugin, QWidget *parent) : TutorableDialog("TransferSaves", parent) @@ -42,6 +106,7 @@ TransferSavesDialog::TransferSavesDialog(const Profile &profile, IPluginGame con , m_GamePlugin(gamePlugin) { ui->setupUi(this); + ui->label_2->setText(tr("Characters for profile %1").arg(m_Profile.name())); refreshGlobalSaves(); refreshLocalSaves(); refreshGlobalCharacters(); @@ -53,85 +118,27 @@ TransferSavesDialog::~TransferSavesDialog() delete ui; } - void TransferSavesDialog::refreshGlobalSaves() { - m_GlobalSaves.clear(); - QDir savesDir(m_GamePlugin->savesDirectory()); - savesDir.setNameFilters(QStringList() << QString("*.%1").arg(m_GamePlugin->savegameExtension())); - - QStringList files = savesDir.entryList(QDir::Files, QDir::Time); - - for (const QString &filename : files) { - SaveGameGamebryo *save = new SaveGameGamebryo(this, savesDir.absoluteFilePath(filename), m_GamePlugin); - save->setParent(this); - m_GlobalSaves.push_back(save); - } + refreshSaves(m_GlobalSaves, m_GamePlugin->savesDirectory().absolutePath()); } void TransferSavesDialog::refreshLocalSaves() { - m_LocalSaves.clear(); - - QDir savesDir(m_Profile.absolutePath() + "/saves"); - - savesDir.setNameFilters(QStringList() << QString("*.%1").arg(m_GamePlugin->savegameExtension())); - - QStringList files = savesDir.entryList(QDir::Files, QDir::Time); - - for (const QString &filename : files) { - SaveGameGamebryo *save = new SaveGameGamebryo(this, savesDir.absoluteFilePath(filename), m_GamePlugin); - - save->setParent(this); - m_LocalSaves.push_back(save); - } + refreshSaves(m_LocalSaves, m_Profile.savePath()); } void TransferSavesDialog::refreshGlobalCharacters() { - std::set characters; - for (std::vector::const_iterator iter = m_GlobalSaves.begin(); - iter != m_GlobalSaves.end(); ++iter) { - characters.insert((*iter)->pcName()); - } - ui->globalCharacterList->clear(); - for (std::set::const_iterator iter = characters.begin(); - iter != characters.end(); ++iter) { - ui->globalCharacterList->addItem(*iter); - } - if (ui->globalCharacterList->count() > 0) { - ui->globalCharacterList->setCurrentRow(0); - ui->copyToLocalBtn->setEnabled(true); - ui->moveToLocalBtn->setEnabled(true); - } else { - ui->copyToLocalBtn->setEnabled(false); - ui->moveToLocalBtn->setEnabled(false); - } + refreshCharacters(m_GlobalSaves, ui->globalCharacterList, ui->copyToLocalBtn, ui->moveToLocalBtn); } void TransferSavesDialog::refreshLocalCharacters() { - std::set characters; - for (std::vector::const_iterator iter = m_LocalSaves.begin(); - iter != m_LocalSaves.end(); ++iter) { - characters.insert((*iter)->pcName()); - } - ui->localCharacterList->clear(); - for (std::set::const_iterator iter = characters.begin(); - iter != characters.end(); ++iter) { - ui->localCharacterList->addItem(*iter); - } - if (ui->localCharacterList->count() > 0) { - ui->localCharacterList->setCurrentRow(0); - ui->copyToGlobalBtn->setEnabled(true); - ui->moveToGlobalBtn->setEnabled(true); - } else { - ui->copyToGlobalBtn->setEnabled(false); - ui->moveToGlobalBtn->setEnabled(false); - } + refreshCharacters(m_LocalSaves, ui->localCharacterList, ui->copyToGlobalBtn, ui->moveToGlobalBtn); } @@ -153,144 +160,75 @@ bool TransferSavesDialog::testOverwrite(OverwriteMode &overwriteMode, const QStr return res == QMessageBox::Yes; } + +#define MOVE_SAVES "Move all save games of character \"%1\"" +#define COPY_SAVES "Copy all save games of character \"%1\"" + +#define TO_PROFILE "to the profile?" +#define TO_GLOBAL "to the global location? Please be aware that this will mess up the running number of save games." + void TransferSavesDialog::on_moveToLocalBtn_clicked() { - QString selectedCharacter = ui->globalCharacterList->currentItem()->text(); - if (QMessageBox::question(this, tr("Confirm"), - tr("Move all save games of character \"%1\" to the profile?").arg(selectedCharacter), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - QString destination = m_Profile.absolutePath() + "/saves"; - OverwriteMode overwriteMode = OVERWRITE_ASK; - - for (std::vector::const_iterator iter = m_GlobalSaves.begin(); - iter != m_GlobalSaves.end(); ++iter) { - if ((*iter)->pcName() == selectedCharacter) { - QStringList files = (*iter)->saveFiles(); - for (const QString &file : files) { - QFileInfo fileInfo(file); - QString destinationFile = destination + "/" + fileInfo.fileName(); - if (QFile::exists(destinationFile)) { - if (testOverwrite(overwriteMode, destinationFile)) { - QFile::remove(destinationFile); - } else { - continue; - } - } - if (!shellMove(fileInfo.absoluteFilePath(), destinationFile)) { - qCritical("failed to move %s to %s: %lu", - fileInfo.absoluteFilePath().toUtf8().constData(), - destinationFile.toUtf8().constData(), - ::GetLastError()); - } - } - } - } + QString character = ui->globalCharacterList->currentItem()->text(); + if (transferCharacters( + character, MOVE_SAVES TO_PROFILE, m_GlobalSaves[character], + m_Profile.savePath(), + [this](const QString &source, const QString &destination) -> bool { + return shellMove(source, destination, this); + }, + "Failed to move %s to %s")) { + refreshGlobalSaves(); + refreshGlobalCharacters(); + refreshLocalSaves(); + refreshLocalCharacters(); } - refreshGlobalSaves(); - refreshGlobalCharacters(); - refreshLocalSaves(); - refreshLocalCharacters(); } void TransferSavesDialog::on_copyToLocalBtn_clicked() { - QString selectedCharacter = ui->globalCharacterList->currentItem()->text(); - if (QMessageBox::question(this, tr("Confirm"), - tr("Copy all save games of character \"%1\" to the profile?").arg(selectedCharacter), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - QString destination = m_Profile.absolutePath() + "/saves"; - OverwriteMode overwriteMode = OVERWRITE_ASK; - for (std::vector::const_iterator iter = m_GlobalSaves.begin(); - iter != m_GlobalSaves.end(); ++iter) { - if ((*iter)->pcName() == selectedCharacter) { - QStringList files = (*iter)->saveFiles(); - QStringList destinations; - for (const QString &file : files) { - QFileInfo fileInfo(file); - destinations.append(destination + "/" + fileInfo.fileName()); - } - shellCopy(files, destinations, this); - } - } + QString character = ui->globalCharacterList->currentItem()->text(); + if (transferCharacters( + character, COPY_SAVES TO_PROFILE, m_GlobalSaves[character], + m_Profile.savePath(), + [this](const QString &source, const QString &destination) -> bool { + return shellCopy(source, destination, this); + }, + "Failed to copy %s to %s")) { + refreshLocalSaves(); + refreshLocalCharacters(); } - refreshLocalSaves(); - refreshLocalCharacters(); } void TransferSavesDialog::on_moveToGlobalBtn_clicked() { - QString selectedCharacter = ui->localCharacterList->currentItem()->text(); - if (QMessageBox::question(this, tr("Confirm"), - tr("Move all save games of character \"%1\" to the global location? Please be aware " - "that this will mess up the running number of save games.").arg(selectedCharacter), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - - QDir destination = m_GamePlugin->savesDirectory(); - OverwriteMode overwriteMode = OVERWRITE_ASK; - for (std::vector::const_iterator iter = m_LocalSaves.begin(); - iter != m_LocalSaves.end(); ++iter) { - if ((*iter)->pcName() == selectedCharacter) { - QStringList files = (*iter)->saveFiles(); - for (const QString &file : files) { - QFileInfo fileInfo(file); - QString destinationFile = destination.filePath(fileInfo.fileName()); - if (QFile::exists(destinationFile)) { - if (testOverwrite(overwriteMode, destinationFile)) { - QFile::remove(destinationFile); - } else { - continue; - } - } - if (!shellMove(fileInfo.absoluteFilePath(), destinationFile)) { - qCritical("failed to move %s to %s", - fileInfo.absoluteFilePath().toUtf8().constData(), - destinationFile.toUtf8().constData()); - } - } - } - } + QString character = ui->localCharacterList->currentItem()->text(); + if (transferCharacters( + character, MOVE_SAVES TO_GLOBAL, m_LocalSaves[character], + m_GamePlugin->savesDirectory().absolutePath(), + [this](const QString &source, const QString &destination) -> bool { + return shellMove(source, destination, this); + }, + "Failed to move %s to %s")) { + refreshGlobalSaves(); + refreshGlobalCharacters(); + refreshLocalSaves(); + refreshLocalCharacters(); } - refreshGlobalSaves(); - refreshGlobalCharacters(); - refreshLocalSaves(); - refreshLocalCharacters(); } void TransferSavesDialog::on_copyToGlobalBtn_clicked() { - QString selectedCharacter = ui->localCharacterList->currentItem()->text(); - if (QMessageBox::question(this, tr("Confirm"), - tr("Copy all save games of character \"%1\" to the global location? Please be aware " - "that this will mess up the running number of save games.").arg(selectedCharacter), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - - QDir destination = m_GamePlugin->savesDirectory(); - OverwriteMode overwriteMode = OVERWRITE_ASK; - for (std::vector::const_iterator iter = m_LocalSaves.begin(); - iter != m_LocalSaves.end(); ++iter) { - if ((*iter)->pcName() == selectedCharacter) { - QStringList files = (*iter)->saveFiles(); - for (const QString &file : files) { - QFileInfo fileInfo(file); - QString destinationFile = destination.filePath(fileInfo.fileName()); - if (QFile::exists(destinationFile)) { - if (testOverwrite(overwriteMode, destinationFile)) { - QFile::remove(destinationFile); - } else { - continue; - } - } - if (!shellCopy(fileInfo.absoluteFilePath(), destinationFile)) { - qCritical("failed to copy %s to %s", - fileInfo.absoluteFilePath().toUtf8().constData(), - destinationFile.toUtf8().constData()); - } - } - } - } + QString character = ui->localCharacterList->currentItem()->text(); + if (transferCharacters( + character, COPY_SAVES TO_GLOBAL, m_LocalSaves[character], + m_GamePlugin->savesDirectory().absolutePath(), + [this](const QString &source, const QString &destination) -> bool { + return shellCopy(source, destination, this); + }, + "Failed to copy %s to %s")) { + refreshGlobalSaves(); + refreshGlobalCharacters(); } - refreshGlobalSaves(); - refreshGlobalCharacters(); } void TransferSavesDialog::on_doneButton_clicked() @@ -301,10 +239,12 @@ void TransferSavesDialog::on_doneButton_clicked() void TransferSavesDialog::on_globalCharacterList_currentTextChanged(const QString ¤tText) { ui->globalSavesList->clear(); - for (std::vector::const_iterator iter = m_GlobalSaves.begin(); - iter != m_GlobalSaves.end(); ++iter) { - if ((*iter)->pcName() == currentText) { - ui->globalSavesList->addItem(QFileInfo((*iter)->fileName()).fileName()); + //sadly this can get called while we're resetting the list, with an invalid + //name, so we have to check. + SaveCollection::const_iterator saveList = m_GlobalSaves.find(currentText); + if (saveList != m_GlobalSaves.end()) { + for (SaveListItem const &save : saveList->second) { + ui->globalSavesList->addItem(QFileInfo(save->getFilename()).fileName()); } } } @@ -312,10 +252,89 @@ void TransferSavesDialog::on_globalCharacterList_currentTextChanged(const QStrin void TransferSavesDialog::on_localCharacterList_currentTextChanged(const QString ¤tText) { ui->localSavesList->clear(); - for (std::vector::const_iterator iter = m_LocalSaves.begin(); - iter != m_LocalSaves.end(); ++iter) { - if ((*iter)->pcName() == currentText) { - ui->localSavesList->addItem(QFileInfo((*iter)->fileName()).fileName()); + //sadly this can get called while we're resetting the list, with an invalid + //name, so we have to check. + SaveCollection::const_iterator saveList = m_LocalSaves.find(currentText); + if (saveList != m_LocalSaves.end()) { + for (SaveListItem const &save : saveList->second) { + ui->localSavesList->addItem(QFileInfo(save->getFilename()).fileName()); + } + } +} + +void TransferSavesDialog::refreshSaves(SaveCollection &saveCollection, QString const &savedir) +{ + saveCollection.clear(); + QDir savesDir(savedir); + savesDir.setNameFilters(QStringList() << QString("*.%1").arg(m_GamePlugin->savegameExtension())); + + SaveGameInfo const *info = m_GamePlugin->feature(); + if (info == nullptr) { + static DummyInfo dummyInfo; + info = &dummyInfo; + } + + QStringList files = savesDir.entryList(QDir::Files, QDir::Time); + for (const QString &filename : files) { + QString file = savesDir.absoluteFilePath(filename); + MOBase::ISaveGame const *save = info->getSaveGameInfo(file); + saveCollection[save->getSaveGroupIdentifier()].push_back( + std::unique_ptr(save)); + } +} + +void TransferSavesDialog::refreshCharacters(const SaveCollection &saveCollection, + QListWidget *charList, QPushButton *copy, QPushButton *move) +{ + charList->clear(); + for (SaveCollection::value_type const &val : saveCollection) { + charList->addItem(val.first); + } + if (charList->count() > 0) { + charList->setCurrentRow(0); + copy->setEnabled(true); + move->setEnabled(true); + } else { + copy->setEnabled(false); + move->setEnabled(false); + } +} + +bool TransferSavesDialog::transferCharacters( + QString const &character, char const *message, SaveList &saves, + QString const &dest, + const std::function &method, + char const *errmsg) +{ + if (QMessageBox::question(this, tr("Confirm"), + tr(message).arg(character), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + return false; + } + + OverwriteMode overwriteMode = OVERWRITE_ASK; + + QDir destination(dest); + for (SaveListItem const &save : saves) { + for (QString source : save->allFiles()) { + QFileInfo sourceFile(source); + QString destinationFile(destination.absoluteFilePath(sourceFile.fileName())); + + //If the file is already there, let them skip (or not). + if (QFile::exists(destinationFile)) { + if (! testOverwrite(overwriteMode, destinationFile)) { + continue; + } + //OK, they want to remove it. + QFile::remove(destinationFile); + } + + if (!method(sourceFile.absoluteFilePath(), destinationFile)) { + qCritical(errmsg, + sourceFile.absoluteFilePath().toUtf8().constData(), + destinationFile.toUtf8().constData()); + } } } + return true; } diff --git a/src/transfersavesdialog.h b/src/transfersavesdialog.h index e2c556b4..6961e8c6 100644 --- a/src/transfersavesdialog.h +++ b/src/transfersavesdialog.h @@ -23,10 +23,19 @@ along with Mod Organizer. If not, see . #include "tutorabledialog.h" #include "profile.h" +class QListWidget; +#include +class QPushButton; +#include +class QWidget; + +#include +#include +#include + namespace Ui { class TransferSavesDialog; } namespace MOBase { class IPluginGame; } - -class SaveGame; +namespace MOBase { class ISaveGame; } class TransferSavesDialog : public MOBase::TutorableDialog { @@ -76,9 +85,23 @@ private: MOBase::IPluginGame const *m_GamePlugin; - std::vector m_GlobalSaves; - std::vector m_LocalSaves; - + typedef std::unique_ptr SaveListItem; + typedef std::vector SaveList; + typedef std::map SaveCollection; + SaveCollection m_GlobalSaves; + SaveCollection m_LocalSaves; + + void refreshSaves(SaveCollection &saveCollection, const QString &savedir); + void refreshCharacters(SaveCollection const &saveCollection, + QListWidget *charList, + QPushButton *copy, + QPushButton *move); + + bool transferCharacters( + QString const &character, char const *message, SaveList &saves, + QString const &dest, + const std::function &method, + char const *errmsg); }; #endif // TRANSFERSAVESDIALOG_H diff --git a/win.imp b/win.imp index cbecdc43..24635775 100644 --- a/win.imp +++ b/win.imp @@ -54,10 +54,18 @@ { include: [ "", "private", "", "public" ] }, # official name according to website { include: [ "", "private", "", "public" ] }, # - { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, { include: [ "", "private", "", "public" ] }, +# These ones are in shtypes.h but the only documentation I can find says +# to include Knownfolders.h for these + { symbol: [ "REFKNOWNFOLDERID", "private", "", "public" ] }, + { symbol: [ "KNOWNFOLDERID", "private", "", "public" ] }, + +# IWYU doesn't understand upper/lower case which is *really* annoying on +# windows, though to be fair I'm not sure M/S understand it either. + { include: [ "", "private", "", "public" ] }, + # Files that are included by other files which seem to then come for free in Windows.h but # shouldn't. Again, should be cleaned up. -- cgit v1.3.1 From b0e3e86a61770c3d6c18161b05c745f30f1a2cb3 Mon Sep 17 00:00:00 2001 From: Tannin Date: Fri, 6 May 2016 23:39:08 +0200 Subject: moved plugins.txt reading/writing to game plugin --- src/CMakeLists.txt | 2 - src/mainwindow.cpp | 2 +- src/pluginlist.cpp | 285 +++++++++++++++----------------------------------- src/pluginlist.h | 34 ++---- src/profile.cpp | 2 +- src/safewritefile.cpp | 71 ------------- src/safewritefile.h | 57 ---------- 7 files changed, 90 insertions(+), 363 deletions(-) delete mode 100644 src/safewritefile.cpp delete mode 100644 src/safewritefile.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6eec86a9..1bd16ed3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -76,7 +76,6 @@ SET(organizer_SRCS previewdialog.cpp aboutdialog.cpp json.cpp - safewritefile.cpp modflagicondelegate.cpp genericicondelegate.cpp organizerproxy.cpp @@ -162,7 +161,6 @@ SET(organizer_HDRS previewdialog.h aboutdialog.h json.h - safewritefile.h modflagicondelegate.h genericicondelegate.h organizerproxy.h diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 05000761..24391999 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -71,7 +71,7 @@ along with Mod Organizer. If not, see . #include "previewdialog.h" #include "browserdialog.h" #include "aboutdialog.h" -#include "safewritefile.h" +#include #include "nxmaccessmanager.h" #include "appconfig.h" #include "eventfilter.h" diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index bf5a09dc..8110cdf2 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -20,7 +20,6 @@ along with Mod Organizer. If not, see . #include "pluginlist.h" #include "inject.h" #include "settings.h" -#include "safewritefile.h" #include "scopeguard.h" #include "modinfo.h" #include @@ -28,6 +27,8 @@ along with Mod Organizer. If not, see . #include #include #include +#include +#include #include #include @@ -74,14 +75,6 @@ PluginList::PluginList(QObject *parent) : QAbstractItemModel(parent) , m_FontMetrics(QFont()) { - m_Utf8Codec = QTextCodec::codecForName("utf-8"); - m_LocalCodec = QTextCodec::codecForName("Windows-1252"); - - if (m_LocalCodec == nullptr) { - qCritical("required 8-bit string-encoding not supported."); - m_LocalCodec = m_Utf8Codec; - } - } PluginList::~PluginList() @@ -117,27 +110,25 @@ QString PluginList::getColumnToolTip(int column) void PluginList::refresh(const QString &profileName , const DirectoryEntry &baseDirectory - , const QString &pluginsFile - , const QString &loadOrderFile , const QString &lockedOrderFile) { ChangeBracket layoutChange(this); - m_ESPsByName.clear(); - m_ESPsByPriority.clear(); - m_ESPs.clear(); - QStringList primaryPlugins = m_GamePlugin->primaryPlugins(); m_CurrentProfile = profileName; std::vector files = baseDirectory.getFiles(); - for (auto iter = files.begin(); iter != files.end(); ++iter) { - FileEntry::Ptr current = *iter; + for (FileEntry::Ptr current : files) { if (current.get() == nullptr) { continue; } QString filename = ToQString(current->getName()); + + if (m_ESPsByName.find(filename.toLower()) != m_ESPsByName.end()) { + continue; + } + QString extension = filename.right(3).toLower(); if ((extension == "esp") || (extension == "esm")) { @@ -165,56 +156,24 @@ void PluginList::refresh(const QString &profileName } } - if (readLoadOrder(loadOrderFile)) { - int maxPriority = 0; - // assign known load orders - for (std::vector::iterator espIter = m_ESPs.begin(); espIter != m_ESPs.end(); ++espIter) { - std::map::const_iterator priorityIter = m_ESPLoadOrder.find(espIter->m_Name.toLower()); - if (priorityIter != m_ESPLoadOrder.end()) { - if (priorityIter->second > maxPriority) { - maxPriority = priorityIter->second; - } - espIter->m_Priority = priorityIter->second; - } else { - espIter->m_Priority = -1; - } - } - - ++maxPriority; - - // assign maximum priorities for plugins with unknown priority - for (std::vector::iterator espIter = m_ESPs.begin(); espIter != m_ESPs.end(); ++espIter) { - if (espIter->m_Priority == -1) { - espIter->m_Priority = maxPriority++; - } - } - } else { - // no load order stored, determine by date - std::sort(m_ESPs.begin(), m_ESPs.end(), ByDate); - - for (size_t i = 0; i < m_ESPs.size(); ++i) { - m_ESPs[i].m_Priority = i; - } - } + // functions in GamePlugins will use the IPluginList interface of this, so + // indices need to work. priority will be off however + updateIndices(); - std::sort(m_ESPs.begin(), m_ESPs.end(), ByPriority); // first, sort by priority - // remove gaps from the priorities so we can use them as array indices without overflow - for (int i = 0; i < static_cast(m_ESPs.size()); ++i) { - m_ESPs[i].m_Priority = i; - } + GamePlugins *gamePlugins = m_GamePlugin->feature(); + gamePlugins->readPluginLists(this); - std::sort(m_ESPs.begin(), m_ESPs.end(), ByName); // sort by name so alphabetical sorting works + testMasters(); updateIndices(); - readEnabledFrom(pluginsFile); - readLockedOrderFrom(lockedOrderFile); layoutChange.finish(); refreshLoadOrder(); - emit dataChanged(this->index(0, 0), this->index(m_ESPs.size(), columnCount())); + emit dataChanged(this->index(0, 0), + this->index(static_cast(m_ESPs.size()), columnCount())); m_Refreshed(); } @@ -301,89 +260,6 @@ bool PluginList::isEnabled(int index) return m_ESPs.at(index).m_Enabled; } -bool PluginList::readLoadOrder(const QString &fileName) -{ - std::set availableESPs; - for (std::vector::const_iterator iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) { - availableESPs.insert(iter->m_Name.toLower()); - } - - m_ESPLoadOrder.clear(); - - int priority = 0; - - QStringList primaryPlugins = m_GamePlugin->primaryPlugins(); - for (const QString &plugin : primaryPlugins) { - if (availableESPs.find(plugin) != availableESPs.end()) { - m_ESPLoadOrder[plugin] = priority++; - } - } - - QFile file(fileName); - if (!file.open(QIODevice::ReadOnly)) { - return false; - } - if (file.size() == 0) { - // MO stores at least a header in the file. if it's completely empty the file is broken - return false; - } - while (!file.atEnd()) { - QByteArray line = file.readLine().trimmed(); - QString modName; - if ((line.size() > 0) && (line.at(0) != '#')) { - modName = QString::fromUtf8(line.constData()).toLower(); - } - - if ((modName.size() > 0) && - (m_ESPLoadOrder.find(modName) == m_ESPLoadOrder.end()) && - (availableESPs.find(modName) != availableESPs.end())) { - m_ESPLoadOrder[modName] = priority++; - } - } - - file.close(); - return true; -} - - -void PluginList::readEnabledFrom(const QString &fileName) -{ - for (std::vector::iterator iter = m_ESPs.begin(); iter != m_ESPs.end(); ++iter) { - if (!iter->m_ForceEnabled) { - iter->m_Enabled = false; - } - iter->m_LoadOrder = -1; - } - - QFile file(fileName); - if (!file.exists()) { - throw std::runtime_error(QObject::tr("failed to find \"%1\"").arg(fileName).toUtf8().constData()); - } - - file.open(QIODevice::ReadOnly); - while (!file.atEnd()) { - QByteArray line = file.readLine(); - QString modName; - if ((line.size() > 0) && (line.at(0) != '#')) { - modName = m_LocalCodec->toUnicode(line.trimmed().constData()); - } - if (modName.size() > 0) { - std::map::iterator iter = m_ESPsByName.find(modName.toLower()); - if (iter != m_ESPsByName.end()) { - m_ESPs[iter->second].m_Enabled = true; - } else { - qWarning("plugin %s not found", modName.toUtf8().constData()); - emit writePluginsList(); - } - } - } - - file.close(); - - testMasters(); -} - - void PluginList::readLockedOrderFrom(const QString &fileName) { m_LockedOrder.clear(); @@ -410,53 +286,6 @@ void PluginList::readLockedOrderFrom(const QString &fileName) file.close(); } - - -void PluginList::writePlugins(const QString &fileName, bool writeUnchecked) const -{ - SafeWriteFile file(fileName); - - QTextCodec *textCodec = writeUnchecked ? m_Utf8Codec : m_LocalCodec; - - file->resize(0); - - file->write(textCodec->fromUnicode("# This file was automatically generated by Mod Organizer.\r\n")); - - QStringList saveList; - - bool invalidFileNames = false; - int writtenCount = 0; - for (size_t i = 0; i < m_ESPs.size(); ++i) { - int priority = m_ESPsByPriority[i]; - if (m_ESPs[priority].m_Enabled || writeUnchecked) { - //file.write(m_ESPs[priority].m_Name.toUtf8()); - if (!textCodec->canEncode(m_ESPs[priority].m_Name)) { - invalidFileNames = true; - qCritical("invalid plugin name %s", m_ESPs[priority].m_Name.toUtf8().constData()); - } else { - saveList << m_ESPs[priority].m_Name; - file->write(textCodec->fromUnicode(m_ESPs[priority].m_Name)); - } - file->write("\r\n"); - ++writtenCount; - } - } - - if (invalidFileNames) { - reportError(tr("Some of your plugins have invalid names! These plugins can not be loaded by the game. " - "Please see mo_interface.log for a list of affected plugins and rename them.")); - } - - if (writtenCount == 0) { - qWarning("plugin list would be empty, this is almost certainly wrong. Not saving."); - } else { - if (file.commitIfDifferent(m_LastSaveHash[fileName])) { - qDebug("%s saved", QDir::toNativeSeparators(fileName).toUtf8().constData()); - } - } -} - - void PluginList::writeLockedOrder(const QString &fileName) const { SafeWriteFile file(fileName); @@ -471,14 +300,13 @@ void PluginList::writeLockedOrder(const QString &fileName) const } -void PluginList::saveTo(const QString &pluginFileName - , const QString &loadOrderFileName - , const QString &lockedOrderFileName +void PluginList::saveTo(const QString &lockedOrderFileName , const QString& deleterFileName , bool hideUnchecked) const { - writePlugins(pluginFileName, false); - writePlugins(loadOrderFileName, true); + GamePlugins *gamePlugins = m_GamePlugin->feature(); + gamePlugins->writePluginLists(this); + writeLockedOrder(lockedOrderFileName); if (hideUnchecked) { @@ -552,7 +380,7 @@ bool PluginList::saveLoadOrder(DirectoryEntry &directoryStructure) int PluginList::enabledCount() const { int enabled = 0; - foreach (auto info, m_ESPs) { + for (const auto &info : m_ESPs) { if (info.m_Enabled) { ++enabled; } @@ -606,7 +434,7 @@ void PluginList::refreshLoadOrder() bool savePluginsList = false; // this is guaranteed to iterate from lowest key (load order) to highest for (auto iter = lockedLoadOrder.begin(); iter != lockedLoadOrder.end(); ++iter) { - auto nameIter = m_ESPsByName.find(iter->second); + auto nameIter = m_ESPsByName.find(iter->second.toLower()); if (nameIter != m_ESPsByName.end()) { // locked esp exists @@ -641,6 +469,17 @@ void PluginList::disconnectSlots() { m_PluginStateChanged.disconnect_all_slots(); } +QStringList PluginList::pluginNames() const +{ + QStringList result; + + for (const ESPInfo &info : m_ESPs) { + result.append(info.m_Name); + } + + return result; +} + IPluginList::PluginStates PluginList::state(const QString &name) const { auto iter = m_ESPsByName.find(name.toLower()); @@ -651,6 +490,36 @@ IPluginList::PluginStates PluginList::state(const QString &name) const } } +void PluginList::setState(const QString &name, PluginStates state) +{ + auto iter = m_ESPsByName.find(name.toLower()); + if (iter != m_ESPsByName.end()) { + m_ESPs[iter->second].m_Enabled = state == IPluginList::STATE_ACTIVE; + } +} + +void PluginList::setLoadOrder(const QStringList &pluginList) +{ + for (ESPInfo &info : m_ESPs) { + info.m_Priority = -1; + } + int maxPriority = 0; + for (const QString &plugin : pluginList) { + auto iter = m_ESPsByName.find(plugin.toLower()); + if (iter !=m_ESPsByName.end()) { + m_ESPs[iter->second].m_Priority = maxPriority++; + } + } + + // use old priorities + for (ESPInfo &info : m_ESPs) { + if (info.m_Priority == -1) { + info.m_Priority = maxPriority++; + } + } + updateIndices(); +} + int PluginList::priority(const QString &name) const { auto iter = m_ESPsByName.find(name.toLower()); @@ -688,7 +557,7 @@ QStringList PluginList::masters(const QString &name) const return QStringList(); } else { QStringList result; - foreach (const QString &master, m_ESPs[iter->second].m_Masters) { + for (const QString &master : m_ESPs[iter->second].m_Masters) { result.append(master); } return result; @@ -730,10 +599,16 @@ void PluginList::updateIndices() m_ESPsByName.clear(); m_ESPsByPriority.clear(); m_ESPsByPriority.resize(m_ESPs.size()); - for (unsigned int i = 0; i < m_ESPs.size(); ++i) { + if (m_ESPs[i].m_Priority < 0) { + continue; + } + if (m_ESPs[i].m_Priority >= static_cast(m_ESPs.size())) { + qCritical("invalid priority %d", m_ESPs[i].m_Priority); + continue; + } m_ESPsByName[m_ESPs[i].m_Name.toLower()] = i; - m_ESPsByPriority[m_ESPs[i].m_Priority] = i; + m_ESPsByPriority.at(static_cast(m_ESPs[i].m_Priority)) = i; } } @@ -741,7 +616,7 @@ void PluginList::updateIndices() int PluginList::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) { - return m_ESPs.size(); + return static_cast(m_ESPs.size()); } else { return 0; } @@ -926,7 +801,9 @@ bool PluginList::setData(const QModelIndex &modIndex, const QVariant &value, int try { m_PluginStateChanged(modName, newState); testMasters(); - emit dataChanged(this->index(0, 0), this->index(m_ESPs.size(), columnCount())); + emit dataChanged( + this->index(0, 0), + this->index(static_cast(m_ESPs.size()), columnCount())); } catch (const std::exception &e) { qCritical("failed to invoke state changed notification: %s", e.what()); } catch (...) { @@ -1002,8 +879,10 @@ void PluginList::setPluginPriority(int row, int &newPriority) } // enforce valid range - if (newPriorityTemp < 0) newPriorityTemp = 0; - else if (newPriorityTemp >= static_cast(m_ESPsByPriority.size())) newPriorityTemp = m_ESPsByPriority.size() - 1; + if (newPriorityTemp < 0) + newPriorityTemp = 0; + else if (newPriorityTemp >= static_cast(m_ESPsByPriority.size())) + newPriorityTemp = static_cast(m_ESPsByPriority.size()) - 1; try { int oldPriority = m_ESPs.at(row).m_Priority; @@ -1089,7 +968,7 @@ bool PluginList::dropMimeData(const QMimeData *mimeData, Qt::DropAction action, if ((row < 0) || (row >= static_cast(m_ESPs.size()))) { - newPriority = m_ESPs.size(); + newPriority = static_cast(m_ESPs.size()); } else { newPriority = m_ESPs[row].m_Priority; } @@ -1210,7 +1089,7 @@ PluginList::ESPInfo::ESPInfo(const QString &name, bool enabled, } } -void PluginList::managedGameChanged(IPluginGame const *gamePlugin) +void PluginList::managedGameChanged(const IPluginGame *gamePlugin) { m_GamePlugin = gamePlugin; } diff --git a/src/pluginlist.h b/src/pluginlist.h index 9fe6eeac..2c2ba295 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -110,15 +110,11 @@ public: * * @param profileName name of the current profile * @param baseDirectory the root directory structure representing the virtual data directory - * @param pluginsFile file that stores the list of enabled plugins - * @param loadOrderFile file that stored the load order (not an official file but used by many tools for skyrim) * @param lockedOrderFile list of plugins that shouldn't change load order * @todo the profile is not used? If it was, we should pass the Profile-object instead **/ void refresh(const QString &profileName , const MOShared::DirectoryEntry &baseDirectory - , const QString &pluginsFile - , const QString &loadOrderFile , const QString &lockedOrderFile); /** @@ -164,26 +160,14 @@ public: **/ bool isEnabled(int index); - /** - * @brief update the plugin status (enabled/disabled) from the specified file - * - * @param fileName path of the file to load. the filename should be "plugin.txt" - * @todo it would make sense to move this into the Profile-class - **/ - void readEnabledFrom(const QString &fileName); - /** * @brief save the plugin status to the specified file * - * @param pluginFileName path of the plugin.txt to write to - * @param loadOrderFileName path of the loadorder.txt to write to * @param lockedOrderFileName path of the lockedorder.txt to write to * @param deleterFileName file to receive a list of files to hide from the virtual data tree. This is used to hide unchecked plugins if "hideUnchecked" is true * @param hideUnchecked if true, plugins that aren't enabled will be hidden from the virtual data directory **/ - void saveTo(const QString &pluginFileName - , const QString &loadOrderFileName - , const QString &lockedOrderFileName + void saveTo(const QString &lockedOrderFileName , const QString &deleterFileName , bool hideUnchecked) const; @@ -222,13 +206,16 @@ public: public: + virtual QStringList pluginNames() const override; virtual PluginStates state(const QString &name) const; + virtual void setState(const QString &name, PluginStates state) override; virtual int priority(const QString &name) const; virtual int loadOrder(const QString &name) const; + virtual bool onRefreshed(const std::function &callback); virtual bool isMaster(const QString &name) const; virtual QStringList masters(const QString &name) const; virtual QString origin(const QString &name) const; - virtual bool onRefreshed(const std::function &callback); + virtual void setLoadOrder(const QStringList &pluginList) override; virtual bool onPluginMoved(const std::function &func); virtual bool onPluginStateChanged(const std::function &func) override; @@ -309,10 +296,8 @@ private: void syncLoadOrder(); void updateIndices(); - void writePlugins(const QString &fileName, bool writeUnchecked) const; void writeLockedOrder(const QString &fileName) const; - bool readLoadOrder(const QString &fileName); void readLockedOrderFrom(const QString &fileName); void setPluginPriority(int row, int &newPriority); void changePluginPriority(std::vector rows, int newPriority); @@ -327,10 +312,6 @@ private: std::map m_ESPsByName; std::vector m_ESPsByPriority; - // maps esp names to the priority specified in loadorder.txt. The esp names are - // all lowercase!! This is to work around the fact that BOSS for some reason writes some file with - // capitalization that doesn't match the actual name - std::map m_ESPLoadOrder; std::map m_LockedOrder; std::map m_AdditionalInfo; // maps esp names to boss information @@ -338,16 +319,13 @@ private: QString m_CurrentProfile; QFontMetrics m_FontMetrics; - QTextCodec *m_Utf8Codec; - QTextCodec *m_LocalCodec; - SignalRefreshed m_Refreshed; SignalPluginMoved m_PluginMoved; SignalPluginStateChanged m_PluginStateChanged; QTemporaryFile m_TempFile; - MOBase::IPluginGame const *m_GamePlugin; + const MOBase::IPluginGame *m_GamePlugin; }; diff --git a/src/profile.cpp b/src/profile.cpp index f4aab46b..8cc60cc0 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -20,13 +20,13 @@ along with Mod Organizer. If not, see . #include "profile.h" #include "modinfo.h" -#include "safewritefile.h" #include "settings.h" #include #include #include "appconfig.h" #include #include +#include #include #include diff --git a/src/safewritefile.cpp b/src/safewritefile.cpp deleted file mode 100644 index 007b3da9..00000000 --- a/src/safewritefile.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* -Copyright (C) 2014 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - - -#include "safewritefile.h" -#include -#include - - -using namespace MOBase; - - -SafeWriteFile::SafeWriteFile(const QString &fileName) -: m_FileName(fileName) -{ - if (!m_TempFile.open()) { - throw MyException(QObject::tr("failed to open temporary file")); - } -} - - -QFile *SafeWriteFile::operator->() { - Q_ASSERT(m_TempFile.isOpen()); - return &m_TempFile; -} - - -void SafeWriteFile::commit() { - shellDeleteQuiet(m_FileName); - m_TempFile.rename(m_FileName); - m_TempFile.setAutoRemove(false); - m_TempFile.close(); -} - -bool SafeWriteFile::commitIfDifferent(QByteArray &inHash) { - QByteArray newHash = hash(); - if (newHash != inHash - || !QFile::exists(m_FileName)) { - commit(); - inHash = newHash; - return true; - } else { - return false; - } -} - -QByteArray SafeWriteFile::hash() -{ - - qint64 pos = m_TempFile.pos(); - m_TempFile.seek(0); - QByteArray data = m_TempFile.readAll(); - m_TempFile.seek(pos); - return QCryptographicHash::hash(data, QCryptographicHash::Md5); -} diff --git a/src/safewritefile.h b/src/safewritefile.h deleted file mode 100644 index 0af6bf98..00000000 --- a/src/safewritefile.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright (C) 2014 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - - -#ifndef SAFEWRITEFILE_H -#define SAFEWRITEFILE_H - - -#include -#include -#include - -/** - * @brief a wrapper for QFile that ensures the file is only actually (over-)written if writing was successful - */ -class SafeWriteFile { -public: - SafeWriteFile(const QString &fileName); - - QFile *operator->(); - - void commit(); - - bool commitIfDifferent(QByteArray &hash); - -private: - - QByteArray hash(); - -private: - QString m_FileName; - QTemporaryFile m_TempFile; -}; - - -#endif // SAFEWRITEFILE_H - - - - - -- cgit v1.3.1 From d958e11e2b3cde75f481bdb734be10b4e1dda585 Mon Sep 17 00:00:00 2001 From: Tannin Date: Fri, 6 May 2016 23:47:51 +0200 Subject: now using github for self updater --- CMakeLists.txt | 1 - src/CMakeLists.txt | 8 +- src/organizercore.cpp | 8 -- src/selfupdater.cpp | 341 +++++++++++++------------------------------------- src/selfupdater.h | 19 +-- 5 files changed, 101 insertions(+), 276 deletions(-) (limited to 'src/CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 0146edd5..cfad37a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,6 @@ PROJECT(organizer) SET(DEPENDENCIES_DIR CACHE PATH "") # hint to find qt in dependencies path LIST(APPEND CMAKE_PREFIX_PATH ${DEPENDENCIES_DIR}/qt5/lib/cmake) - FILE(GLOB_RECURSE BOOST_ROOT ${DEPENDENCIES_DIR}/boost*/project-config.jam) GET_FILENAME_COMPONENT(BOOST_ROOT ${BOOST_ROOT} DIRECTORY) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1bd16ed3..02c63ad2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -292,11 +292,13 @@ TARGET_LINK_LIBRARIES(ModOrganizer Qt5::Widgets Qt5::WinExtras Qt5::WebKitWidgets ${Boost_LIBRARIES} zlibstatic - uibase esptk bsatk + uibase esptk bsatk githubpp ${usvfs_name} Dbghelp advapi32 Version Shlwapi) - -SET_TARGET_PROPERTIES(ModOrganizer PROPERTIES COMPILE_FLAGS /GL) +IF (NOT "${OPTIMIZE_COMPILE_FLAGS}" STREQUAL "") + SET_TARGET_PROPERTIES(ModOrganizer PROPERTIES COMPILE_FLAGS_RELWITHDEBINFO + ${OPTIMIZE_COMPILE_FLAGS}) +ENDIF() SET_TARGET_PROPERTIES(ModOrganizer PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/LARGEADDRESSAWARE ${OPTIMIZE_LINK_FLAGS}") diff --git a/src/organizercore.cpp b/src/organizercore.cpp index e50f1cd7..f6196aa7 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -455,14 +455,6 @@ void OrganizerCore::connectPlugins(PluginContainer *container) m_GamePlugin = m_PluginContainer->managedGame(m_GameName); emit managedGameChanged(m_GamePlugin); } - // Do this the hard way - for (const IPluginGame *const game : container->plugins()) { - QString n = game->gameShortName(); - if (game->gameShortName() == "Skyrim") { - m_Updater.setNexusDownload(game); - break; - } - } } void OrganizerCore::disconnectPlugins() diff --git a/src/selfupdater.cpp b/src/selfupdater.cpp index 7733ae80..c16be545 100644 --- a/src/selfupdater.cpp +++ b/src/selfupdater.cpp @@ -28,6 +28,8 @@ along with Mod Organizer. If not, see . #include "downloadmanager.h" #include "nexusinterface.h" #include "nxmaccessmanager.h" +#include "settings.h" +#include "bbcode.h" #include #include #include @@ -49,6 +51,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include @@ -83,10 +86,8 @@ template static T resolveFunction(QLibrary &lib, const char *name) SelfUpdater::SelfUpdater(NexusInterface *nexusInterface) : m_Parent(nullptr) , m_Interface(nexusInterface) - , m_UpdateRequestID(-1) , m_Reply(nullptr) , m_Attempts(3) - , m_NexusDownload(nullptr) { QLibrary archiveLib(QCoreApplication::applicationDirPath() + "\\dlls\\archive.dll"); if (!archiveLib.load()) { @@ -120,33 +121,71 @@ void SelfUpdater::setUserInterface(QWidget *widget) void SelfUpdater::testForUpdate() { - if (QFile::exists(QCoreApplication::applicationDirPath() + "/mo_test_update.7z")) { - emit updateAvailable(); - return; - } - if (m_UpdateRequestID == -1 && m_NexusDownload != nullptr) { - m_UpdateRequestID = m_Interface->requestDescription( - m_NexusDownload->nexusModOrganizerID(), this, QVariant(), - QString(), m_NexusDownload); - } + // TODO: if prereleases are disabled we could just request the latest release + // directly + m_GitHub.releases(GitHub::Repository("TanninOne", "modorganizer"), + [this](const QJsonArray &releases) { + QJsonObject newest; + for (const QJsonValue &releaseVal : releases) { + QJsonObject release = releaseVal.toObject(); + if (!release["draft"].toBool() && (Settings::instance().usePrereleases() + || !release["prerelease"].toBool())) { + if (newest.empty() || (VersionInfo(release["tag_name"].toString()) + > VersionInfo(newest["tag_name"].toString()))) { + newest = release; + } + } + } + + if (!newest.empty()) { + VersionInfo newestVer(newest["tag_name"].toString()); + if (newestVer > this->m_MOVersion) { + m_UpdateCandidate = newest; + qDebug("update available: %s -> %s", + qPrintable(this->m_MOVersion.displayString()), + qPrintable(newestVer.displayString())); + emit updateAvailable(); + } else if (newestVer < this->m_MOVersion) { + // this could happen if the user switches from using prereleases to + // stable builds. Should we downgrade? + qDebug("this version is newer than the newest installed one: %s -> %s", + qPrintable(this->m_MOVersion.displayString()), + qPrintable(newestVer.displayString())); + } + } + }); } void SelfUpdater::startUpdate() { - if (QFile::exists(QCoreApplication::applicationDirPath() + "/mo_test_update.7z")) { - m_UpdateFile.setFileName(QCoreApplication::applicationDirPath() + "/mo_test_update.7z"); - installUpdate(); - return; - } - - if ((m_UpdateRequestID == -1) && - (!m_NewestVersion.isEmpty())) { - if (QMessageBox::question(m_Parent, tr("Update"), - tr("An update is available (newest version: %1), do you want to install it?").arg(m_NewestVersion), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { - m_UpdateRequestID = m_Interface->requestFiles(m_NexusDownload->nexusModOrganizerID(), - this, m_NewestVersion, "", - m_NexusDownload); + // the button can't be pressed if there isn't an update candidate + Q_ASSERT(!m_UpdateCandidate.empty()); + + QMessageBox query(QMessageBox::Question, + tr("New update available (%1)") + .arg(m_UpdateCandidate["tag_name"].toString()), + BBCode::convertToHTML(m_UpdateCandidate["body"].toString()), + QMessageBox::Yes | QMessageBox::Cancel, m_Parent); + + query.button(QMessageBox::Yes)->setText(tr("Install")); + + int res = query.exec(); + + if (query.result() == QMessageBox::Yes) { + bool found = false; + for (const QJsonValue &assetVal : m_UpdateCandidate["assets"].toArray()) { + QJsonObject asset = assetVal.toObject(); + if (asset["content_type"].toString() == "application/x-msdownload") { + openOutputFile(asset["name"].toString()); + download(asset["browser_download_url"].toString()); + found = true; + break; + } + } + if (!found) { + QMessageBox::warning( + m_Parent, tr("Download failed"), + tr("Failed to find correct download, please try again later.")); } } } @@ -174,15 +213,21 @@ void SelfUpdater::closeProgress() } } -void SelfUpdater::download(const QString &downloadLink, const QString &fileName) +void SelfUpdater::openOutputFile(const QString &fileName) +{ + QString outputPath = QDir::fromNativeSeparators(qApp->property("dataPath").toString()) + "/" + fileName; + qDebug("downloading to %s", qPrintable(outputPath)); + m_UpdateFile.setFileName(outputPath); + m_UpdateFile.open(QIODevice::WriteOnly); +} + +void SelfUpdater::download(const QString &downloadLink) { QNetworkAccessManager *accessManager = m_Interface->getAccessManager(); QUrl dlUrl(downloadLink); QNetworkRequest request(dlUrl); m_Canceled = false; m_Reply = accessManager->get(request); - m_UpdateFile.setFileName(QDir::fromNativeSeparators(qApp->property("dataPath").toString()).append("/").append(fileName)); - m_UpdateFile.open(QIODevice::WriteOnly); showProgress(); connect(m_Reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(downloadProgress(qint64, qint64))); @@ -220,6 +265,12 @@ void SelfUpdater::downloadFinished() int error = QNetworkReply::NoError; if (m_Reply != nullptr) { + if (m_Reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 302) { + QUrl url = m_Reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); + m_UpdateFile.reset(); + download(url.toString()); + return; + } m_UpdateFile.write(m_Reply->readAll()); error = m_Reply->error(); @@ -265,237 +316,25 @@ void SelfUpdater::downloadCancel() void SelfUpdater::installUpdate() { - const QString mopath = QDir::fromNativeSeparators(qApp->property("dataPath").toString()); + const QString mopath + = QDir::fromNativeSeparators(qApp->property("dataPath").toString()); - QString backupPath = mopath + "/update_backup"; - QDir().mkdir(backupPath); + HINSTANCE res = ::ShellExecuteW( + nullptr, L"open", m_UpdateFile.fileName().toStdWString().c_str(), nullptr, + nullptr, SW_SHOW); - // rename files that are currently open so we can unpack the update - if (!m_ArchiveHandler->open(m_UpdateFile.fileName(), nullptr)) { - throw MyException(tr("failed to open archive \"%1\": %2") - .arg(m_UpdateFile.fileName()) - .arg(InstallationManager::getErrorString(m_ArchiveHandler->getLastError()))); - } - - // move all files contained in the archive out of the way, - // otherwise we can't overwrite everything - FileData* const *data; - size_t size; - m_ArchiveHandler->getFileList(data, size); - - for (size_t i = 0; i < size; ++i) { - QString outputName = data[i]->getFileName(); - if (outputName.startsWith("ModOrganizer\\", Qt::CaseInsensitive)) { - outputName = outputName.mid(13); - data[i]->addOutputFileName(outputName); - } else if (outputName != "ModOrganizer") { - data[i]->addOutputFileName(outputName); - } - QFileInfo file(mopath + "/" + outputName); - if (file.exists() && file.isFile()) { - if (!shellMove(QStringList(mopath + "/" + outputName), - QStringList(backupPath + "/" + outputName))) { - reportError(tr("failed to move outdated files: %1. Please update manually.").arg(windowsErrorString(::GetLastError()))); - return; - } - } - } - - // now unpack the archive into the mo directory - if (!m_ArchiveHandler->extract(mopath, nullptr, nullptr, - new MethodCallback(this, &SelfUpdater::report7ZipError))) { - throw std::runtime_error("extracting failed"); + if (res > (HINSTANCE)32) { + QCoreApplication::quit(); + } else { + reportError(tr("Failed to start %1: %2") + .arg(m_UpdateFile.fileName()) + .arg((int)res)); } - m_ArchiveHandler->close(); - m_UpdateFile.remove(); - - QMessageBox::information(m_Parent, tr("Update"), tr("Update installed, Mod Organizer will now be restarted.")); - - QProcess newProcess; - if (QFile::exists(mopath + "/ModOrganizer.exe")) { - newProcess.startDetached(mopath + "/ModOrganizer.exe", QStringList("update")); - } else { - newProcess.startDetached(mopath + "/ModOrganiser.exe", QStringList("update")); - } - emit restart(); } void SelfUpdater::report7ZipError(QString const &errorMessage) { QMessageBox::critical(m_Parent, tr("Error"), errorMessage); } - - -QString SelfUpdater::retrieveNews(const QString &description) -{ - QStringList temp = description.split("[s][/s]"); - if (temp.length() < 2) { - return QString(); - } else { - return temp.at(1); - } -} - - -void SelfUpdater::nxmDescriptionAvailable(int, QVariant, QVariant resultData, int requestID) -{ - if (requestID == m_UpdateRequestID) { - m_UpdateRequestID = -1; - - QVariantMap result = resultData.toMap(); - QString motd = retrieveNews(result["description"].toString()).trimmed(); - if (motd.length() != 0) { - emit motdAvailable(motd); - } - - m_NewestVersion = result["version"].toString(); - if (m_NewestVersion.isEmpty()) { - QTimer::singleShot(5000, this, SLOT(testForUpdate())); - } - VersionInfo currentVersion(m_MOVersion); - VersionInfo newestVersion(m_NewestVersion); - - if (!m_NewestVersion.isEmpty() && (currentVersion < newestVersion)) { - emit updateAvailable(); - } else if (newestVersion < currentVersion) { - qDebug("this version is newer than the current version on nexus (%s vs %s)", - currentVersion.canonicalString().toUtf8().constData(), - newestVersion.canonicalString().toUtf8().constData()); - } - } -} - - -void SelfUpdater::nxmFilesAvailable(int, QVariant userData, QVariant resultData, int requestID) -{ - if (requestID != m_UpdateRequestID) { - return; - } - QString version = userData.toString(); - - m_UpdateRequestID = -1; - - if (!resultData.canConvert()) { - qCritical("invalid files result: %s", resultData.toString().toUtf8().constData()); - reportError(tr("Failed to parse response. Please report this as a bug and include the file mo_interface.log.")); - return; - } - - QVariantList result = resultData.toList(); - - QRegExp updateExpList(QString("updates version ([0-9., ]*) to %1").arg(version)); - QRegExp updateExpRange(QString("updates version ([0-9.]*) - ([0-9.]*) to %1").arg(version)); - int updateFileID = -1; - QString updateFileName; - int mainFileID = -1; - QString mainFileName; - int mainFileSize = 0; - - for(QVariant file : result) { - QVariantMap fileInfo = file.toMap(); - if (!fileInfo["uri"].toString().endsWith(".7z")) { - continue; - } - - if (fileInfo["version"].toString() == version) { - if (fileInfo["category_id"].toInt() == 2) { - QString description = fileInfo["description"].toString(); - // update - if (updateExpList.indexIn(description) != -1) { - // there is an update for the newest version of MO, but does - // it apply to the current version? - QStringList supportedVersions = updateExpList.cap(1).split(QRegExp(",[ ]*"), QString::SkipEmptyParts); - if (supportedVersions.contains(m_MOVersion.canonicalString())) { - updateFileID = fileInfo["id"].toInt(); - updateFileName = fileInfo["uri"].toString(); - } else { - qDebug("update not supported from %s", m_MOVersion.canonicalString().toUtf8().constData()); - } - } else if (updateExpRange.indexIn(description) != -1) { - VersionInfo rangeLowEnd(updateExpRange.cap(1)); - VersionInfo rangeHighEnd(updateExpRange.cap(2)); - if ((rangeLowEnd <= m_MOVersion) && - (m_MOVersion <= rangeHighEnd)) { - updateFileID = fileInfo["id"].toInt(); - updateFileName = fileInfo["uri"].toString(); - break; - } else { - qDebug("update not supported from %s", m_MOVersion.canonicalString().toUtf8().constData()); - } - } else { - qWarning("invalid update description: %s", - description.toUtf8().constData()); - } - } else if (fileInfo["category_id"].toInt() == 1) { - mainFileID = fileInfo["id"].toInt(); - mainFileName = fileInfo["uri"].toString(); - mainFileSize = fileInfo["size"].toInt(); - } - } - } - - if (updateFileID != -1) { - qDebug("update available: %d", updateFileID); - m_UpdateRequestID = m_Interface->requestDownloadURL(m_NexusDownload->nexusModOrganizerID(), - updateFileID, this, updateFileName, "", - m_NexusDownload); - } else if (mainFileID != -1) { - qDebug("full download required: %d", mainFileID); - if (QMessageBox::question(m_Parent, tr("Update"), - tr("No incremental update available for this version, " - "the complete package needs to be downloaded (%1 kB)").arg(mainFileSize), - QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { - m_UpdateRequestID = m_Interface->requestDownloadURL(m_NexusDownload->nexusModOrganizerID(), - mainFileID, this, mainFileName, "", - m_NexusDownload); - } - } else { - qCritical("no file for update found"); - MessageDialog::showMessage(tr("no file for update found. Please update manually."), m_Parent); - closeProgress(); - } -} - - -void SelfUpdater::nxmRequestFailed(int, int, QVariant, int requestID, const QString &errorMessage) -{ - if (requestID == m_UpdateRequestID) { - m_UpdateRequestID = -1; - if (m_Attempts > 0) { - QTimer::singleShot(60000, this, SLOT(testForUpdate())); - --m_Attempts; - } else { - qWarning("Failed to retrieve update information: %s", qPrintable(errorMessage)); - MessageDialog::showMessage(tr("Failed to retrieve update information: %1").arg(errorMessage), m_Parent, false); - } - } -} - - -void SelfUpdater::nxmDownloadURLsAvailable(int, int, QVariant userData, QVariant resultData, int requestID) -{ - if (requestID == m_UpdateRequestID) { - m_UpdateRequestID = -1; - QVariantList serverList = resultData.toList(); - if (serverList.count() != 0) { - std::map dummy; - qSort(serverList.begin(), serverList.end(), boost::bind(&DownloadManager::ServerByPreference, dummy, _1, _2)); - - - QVariantMap dlServer = serverList.first().toMap(); - - download(dlServer["URI"].toString(), userData.toString()); - } else { - MessageDialog::showMessage(tr("No download server available. Please try again later."), m_Parent); - closeProgress(); - } - } -} - -/** Set the game check for updates */ -void SelfUpdater::setNexusDownload(MOBase::IPluginGame const *game) -{ - m_NexusDownload = game; -} diff --git a/src/selfupdater.h b/src/selfupdater.h index 37021b42..4743f6a4 100644 --- a/src/selfupdater.h +++ b/src/selfupdater.h @@ -22,6 +22,7 @@ along with Mod Organizer. If not, see . #include +#include class Archive; class NexusInterface; @@ -87,9 +88,6 @@ public: **/ MOBase::VersionInfo getVersion() const { return m_MOVersion; } - /** Set the game check for updates */ - void setNexusDownload(MOBase::IPluginGame const *game); - public slots: /** @@ -97,11 +95,6 @@ public slots: **/ void testForUpdate(); - void nxmDescriptionAvailable(int modID, QVariant userData, QVariant resultData, int requestID); - void nxmFilesAvailable(int modID, QVariant userData, QVariant resultData, int requestID); - void nxmRequestFailed(int modID, int fileID, QVariant userData, int requestID, const QString &errorMessage); - void nxmDownloadURLsAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID); - signals: /** @@ -121,10 +114,10 @@ signals: private: - void download(const QString &downloadLink, const QString &fileName); + void openOutputFile(const QString &fileName); + void download(const QString &downloadLink); void installUpdate(); void report7ZipError(const QString &errorMessage); - QString retrieveNews(const QString &description); void showProgress(); void closeProgress(); @@ -140,8 +133,6 @@ private: QWidget *m_Parent; MOBase::VersionInfo m_MOVersion; NexusInterface *m_Interface; - int m_UpdateRequestID; - QString m_NewestVersion; QFile m_UpdateFile; QNetworkReply *m_Reply; QProgressDialog *m_Progress { nullptr }; @@ -150,7 +141,9 @@ private: Archive *m_ArchiveHandler; - MOBase::IPluginGame const *m_NexusDownload; + GitHub m_GitHub; + QJsonObject m_UpdateCandidate; + }; -- cgit v1.3.1 From eb97c46ed68a965d6ddf44fa6741fa929fd7d278 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sat, 7 May 2016 00:03:38 +0200 Subject: cleanup --- src/CMakeLists.txt | 12 ++++- src/aboutdialog.cpp | 5 ++- src/aboutdialog.ui | 50 +++++++++++++++++++-- src/categories.cpp | 2 +- src/directoryrefresher.cpp | 2 +- src/downloadmanager.cpp | 47 +++++++++---------- src/editexecutablesdialog.cpp | 8 ++-- src/editexecutablesdialog.ui | 4 +- src/executableslist.h | 3 +- src/installationmanager.cpp | 3 +- src/logbuffer.cpp | 22 +++++---- src/main.cpp | 6 +-- src/mainwindow.cpp | 14 +++--- src/mainwindow.ui | 40 +++++++++++------ src/modflagicondelegate.cpp | 6 +-- src/modinfo.cpp | 6 +-- src/modinfodialog.cpp | 10 +++-- src/nexusinterface.cpp | 11 ++--- src/organizercore.cpp | 12 +++-- src/persistentcookiejar.cpp | 2 +- src/plugincontainer.cpp | 4 +- src/profile.cpp | 17 +++---- src/profile.h | 2 +- src/profilesdialog.cpp | 102 ++++++++++++++++++++---------------------- src/profilesdialog.ui | 4 +- src/shared/util.cpp | 8 +++- src/usvfsconnector.cpp | 9 +++- src/version.rc | 4 +- 28 files changed, 248 insertions(+), 167 deletions(-) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 02c63ad2..a8b43c59 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -272,14 +272,22 @@ INCLUDE_DIRECTORIES(${project_path}/uibase/src ${project_path}/archive/src ${project_path}/../usvfs/usvfs ${project_path}/game_gamebryo/src - ${project_path}/game_features/src) + ${project_path}/game_features/src + ${project_path}/githubpp/src) INCLUDE_DIRECTORIES(shared ${ZLIB_INCLUDE_DIRS}) LINK_DIRECTORIES(${lib_path} ${project_path}/../zlib/lib) -ADD_DEFINITIONS(-D_UNICODE -DUNICODE -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS) +EXECUTE_PROCESS( + COMMAND git log -1 --format=%h + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_COMMIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +ADD_DEFINITIONS(-D_UNICODE -DUNICODE -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS -DGITID="${GIT_COMMIT_HASH}") IF("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") SET(usvfs_name usvfs_x64) diff --git a/src/aboutdialog.cpp b/src/aboutdialog.cpp index 3657a10d..ed57a217 100644 --- a/src/aboutdialog.cpp +++ b/src/aboutdialog.cpp @@ -54,10 +54,13 @@ AboutDialog::AboutDialog(const QString &version, QWidget *parent) addLicense("RRZE Icon Set", LICENSE_CCBY3); addLicense("Icons by Lorc, Delapouite and sbed available on http://game-icons.net", LICENSE_CCBY3); addLicense("Castle Core", LICENSE_APACHE2); + addLicense("LOOT", LICENSE_GPL3); ui->nameLabel->setText(QString("%1 %2").arg(ui->nameLabel->text()).arg(version)); -#ifdef HGID +#if defined(HGID) ui->revisionLabel->setText(ui->revisionLabel->text() + " " + HGID); +#elif defined(GITID) + ui->revisionLabel->setText(ui->revisionLabel->text() + " " + GITID); #else ui->revisionLabel->setText(ui->revisionLabel->text() + " unknown"); #endif diff --git a/src/aboutdialog.ui b/src/aboutdialog.ui index ea5d2141..8d560f2b 100644 --- a/src/aboutdialog.ui +++ b/src/aboutdialog.ui @@ -6,8 +6,8 @@ 0 0 - 508 - 335 + 746 + 369 @@ -18,13 +18,50 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + 0 + 0 + + + + + 250 + 250 + + + + + 250 + 250 + + - :/MO/gui/mo_icon.ico + :/MO/gui/splash + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter @@ -83,7 +120,7 @@ - Copyright 2011-2015 Sebastian Herbord + Copyright 2011-2016 Sebastian Herbord @@ -265,6 +302,11 @@ thosrtanner
    + + + ogrotten + + diff --git a/src/categories.cpp b/src/categories.cpp index 7539d3fd..50ba5271 100644 --- a/src/categories.cpp +++ b/src/categories.cpp @@ -188,7 +188,7 @@ int CategoryFactory::addCategory(const QString &name, const std::vector &ne void CategoryFactory::addCategory(int id, const QString &name, const std::vector &nexusIDs, int parentID) { - int index = m_Categories.size(); + int index = static_cast(m_Categories.size()); m_Categories.push_back(Category(index, id, name, nexusIDs, parentID)); for (int nexusID : nexusIDs) { m_NexusMap[nexusID] = index; diff --git a/src/directoryrefresher.cpp b/src/directoryrefresher.cpp index 4eac4103..0a8b82be 100644 --- a/src/directoryrefresher.cpp +++ b/src/directoryrefresher.cpp @@ -159,7 +159,7 @@ void DirectoryRefresher::refresh() } catch (const std::exception &e) { emit error(tr("failed to read mod (%1): %2").arg(iter->modName, e.what())); } - emit progress((i * 100) / m_Mods.size() + 1); + emit progress((i * 100) / static_cast(m_Mods.size()) + 1); } emit progress(100); diff --git a/src/downloadmanager.cpp b/src/downloadmanager.cpp index 6b4628cc..b92e171e 100644 --- a/src/downloadmanager.cpp +++ b/src/downloadmanager.cpp @@ -88,7 +88,8 @@ DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createFromMeta(con QString fileName = QFileInfo(filePath).fileName(); if (fileName.endsWith(UNFINISHED)) { - info->m_FileName = fileName.mid(0, fileName.length() - strlen(UNFINISHED)); + info->m_FileName = fileName.mid( + 0, fileName.length() - static_cast(strlen(UNFINISHED))); info->m_State = STATE_PAUSED; } else { info->m_FileName = fileName; @@ -471,7 +472,7 @@ void DownloadManager::addNXMDownload(const QString &url) void DownloadManager::removeFile(int index, bool deleteFile) { if (index >= m_ActiveDownloads.size()) { - throw MyException(tr("invalid index")); + throw MyException(tr("remove: invalid download index %1").arg(index)); } DownloadInfo *download = m_ActiveDownloads.at(index); @@ -538,7 +539,7 @@ void DownloadManager::refreshAlphabeticalTranslation() void DownloadManager::restoreDownload(int index) { if ((index < 0) || (index >= m_ActiveDownloads.size())) { - throw MyException(tr("invalid index")); + throw MyException(tr("restore: invalid download index: %1").arg(index)); } DownloadInfo *download = m_ActiveDownloads.at(index); @@ -571,7 +572,7 @@ void DownloadManager::removeDownload(int index, bool deleteFile) } } else { if (index >= m_ActiveDownloads.size()) { - reportError(tr("invalid index %1").arg(index)); + reportError(tr("remove: invalid download index %1").arg(index)); return; } @@ -589,7 +590,7 @@ void DownloadManager::removeDownload(int index, bool deleteFile) void DownloadManager::cancelDownload(int index) { if ((index < 0) || (index >= m_ActiveDownloads.size())) { - reportError(tr("invalid index %1").arg(index)); + reportError(tr("cancel: invalid download index %1").arg(index)); return; } @@ -602,7 +603,7 @@ void DownloadManager::cancelDownload(int index) void DownloadManager::pauseDownload(int index) { if ((index < 0) || (index >= m_ActiveDownloads.size())) { - reportError(tr("invalid index %1").arg(index)); + reportError(tr("pause: invalid download index %1").arg(index)); return; } @@ -622,7 +623,7 @@ void DownloadManager::pauseDownload(int index) void DownloadManager::resumeDownload(int index) { if ((index < 0) || (index >= m_ActiveDownloads.size())) { - reportError(tr("invalid index %1").arg(index)); + reportError(tr("resume: invalid download index %1").arg(index)); return; } DownloadInfo *info = m_ActiveDownloads[index]; @@ -633,7 +634,7 @@ void DownloadManager::resumeDownload(int index) void DownloadManager::resumeDownloadInt(int index) { if ((index < 0) || (index >= m_ActiveDownloads.size())) { - reportError(tr("invalid index %1").arg(index)); + reportError(tr("resume (int): invalid download index %1").arg(index)); return; } DownloadInfo *info = m_ActiveDownloads[index]; @@ -673,7 +674,7 @@ DownloadManager::DownloadInfo *DownloadManager::downloadInfoByID(unsigned int id void DownloadManager::queryInfo(int index) { if ((index < 0) || (index >= m_ActiveDownloads.size())) { - reportError(tr("invalid index %1").arg(index)); + reportError(tr("query: invalid download index %1").arg(index)); return; } DownloadInfo *info = m_ActiveDownloads[index]; @@ -721,7 +722,7 @@ int DownloadManager::numPendingDownloads() const std::pair DownloadManager::getPendingDownload(int index) { if ((index < 0) || (index >= m_PendingDownloads.size())) { - throw MyException(tr("invalid index")); + throw MyException(tr("get pending: invalid download index %1").arg(index)); } return m_PendingDownloads.at(index); @@ -730,7 +731,7 @@ std::pair DownloadManager::getPendingDownload(int index) QString DownloadManager::getFilePath(int index) const { if ((index < 0) || (index >= m_ActiveDownloads.size())) { - throw MyException(tr("invalid index")); + throw MyException(tr("get path: invalid download index %1").arg(index)); } return m_OutputDirectory + "/" + m_ActiveDownloads.at(index)->m_FileName; @@ -751,7 +752,7 @@ QString DownloadManager::getFileTypeString(int fileType) QString DownloadManager::getDisplayName(int index) const { if ((index < 0) || (index >= m_ActiveDownloads.size())) { - throw MyException(tr("invalid index")); + throw MyException(tr("display name: invalid download index %1").arg(index)); } DownloadInfo *info = m_ActiveDownloads.at(index); @@ -768,7 +769,7 @@ QString DownloadManager::getDisplayName(int index) const QString DownloadManager::getFileName(int index) const { if ((index < 0) || (index >= m_ActiveDownloads.size())) { - throw MyException(tr("invalid index")); + throw MyException(tr("file name: invalid download index %1").arg(index)); } return m_ActiveDownloads.at(index)->m_FileName; @@ -777,7 +778,7 @@ QString DownloadManager::getFileName(int index) const QDateTime DownloadManager::getFileTime(int index) const { if ((index < 0) || (index >= m_ActiveDownloads.size())) { - throw MyException(tr("invalid index")); + throw MyException(tr("file time: invalid download index %1").arg(index)); } DownloadInfo *info = m_ActiveDownloads.at(index); @@ -791,7 +792,7 @@ QDateTime DownloadManager::getFileTime(int index) const qint64 DownloadManager::getFileSize(int index) const { if ((index < 0) || (index >= m_ActiveDownloads.size())) { - throw MyException(tr("invalid index")); + throw MyException(tr("file size: invalid download index %1").arg(index)); } return m_ActiveDownloads.at(index)->m_TotalSize; @@ -801,7 +802,7 @@ qint64 DownloadManager::getFileSize(int index) const int DownloadManager::getProgress(int index) const { if ((index < 0) || (index >= m_ActiveDownloads.size())) { - throw MyException(tr("invalid index")); + throw MyException(tr("progress: invalid download index %1").arg(index)); } return m_ActiveDownloads.at(index)->m_Progress; @@ -811,7 +812,7 @@ int DownloadManager::getProgress(int index) const DownloadManager::DownloadState DownloadManager::getState(int index) const { if ((index < 0) || (index >= m_ActiveDownloads.size())) { - throw MyException(tr("invalid index")); + throw MyException(tr("state: invalid download index %1").arg(index)); } return m_ActiveDownloads.at(index)->m_State; @@ -821,7 +822,7 @@ DownloadManager::DownloadState DownloadManager::getState(int index) const bool DownloadManager::isInfoIncomplete(int index) const { if ((index < 0) || (index >= m_ActiveDownloads.size())) { - throw MyException(tr("invalid index")); + throw MyException(tr("infocomplete: invalid download index %1").arg(index)); } DownloadInfo *info = m_ActiveDownloads.at(index); @@ -836,7 +837,7 @@ bool DownloadManager::isInfoIncomplete(int index) const int DownloadManager::getModID(int index) const { if ((index < 0) || (index >= m_ActiveDownloads.size())) { - throw MyException(tr("invalid index")); + throw MyException(tr("mod id: invalid download index %1").arg(index)); } return m_ActiveDownloads.at(index)->m_FileInfo->modID; } @@ -844,7 +845,7 @@ int DownloadManager::getModID(int index) const bool DownloadManager::isHidden(int index) const { if ((index < 0) || (index >= m_ActiveDownloads.size())) { - throw MyException(tr("invalid index")); + throw MyException(tr("ishidden: invalid download index %1").arg(index)); } return m_ActiveDownloads.at(index)->m_Hidden; } @@ -853,7 +854,7 @@ bool DownloadManager::isHidden(int index) const const ModRepositoryFileInfo *DownloadManager::getFileInfo(int index) const { if ((index < 0) || (index >= m_ActiveDownloads.size())) { - throw MyException(tr("invalid index")); + throw MyException(tr("file info: invalid download index %1").arg(index)); } return m_ActiveDownloads.at(index)->m_FileInfo; @@ -863,7 +864,7 @@ const ModRepositoryFileInfo *DownloadManager::getFileInfo(int index) const void DownloadManager::markInstalled(int index) { if ((index < 0) || (index >= m_ActiveDownloads.size())) { - throw MyException(tr("invalid index")); + throw MyException(tr("mark installed: invalid download index %1").arg(index)); } DownloadInfo *info = m_ActiveDownloads.at(index); @@ -878,7 +879,7 @@ void DownloadManager::markInstalled(int index) void DownloadManager::markUninstalled(int index) { if ((index < 0) || (index >= m_ActiveDownloads.size())) { - throw MyException(tr("invalid index")); + throw MyException(tr("mark uninstalled: invalid download index %1").arg(index)); } DownloadInfo *info = m_ActiveDownloads.at(index); diff --git a/src/editexecutablesdialog.cpp b/src/editexecutablesdialog.cpp index 1b2d5a48..05a7603d 100644 --- a/src/editexecutablesdialog.cpp +++ b/src/editexecutablesdialog.cpp @@ -126,15 +126,17 @@ void EditExecutablesDialog::on_addButton_clicked() void EditExecutablesDialog::on_browseButton_clicked() { - QString binaryName = FileDialogMemory::getOpenFileName("editExecutableBinary", this, - tr("Select a binary"), QString(), tr("Executable (%1)").arg("*.exe *.bat *.jar")); + QString binaryName = FileDialogMemory::getOpenFileName( + "editExecutableBinary", this, tr("Select a binary"), QString(), + tr("Executable (%1)").arg("*.exe *.bat *.jar")); if (binaryName.endsWith(".jar", Qt::CaseInsensitive)) { QString binaryPath; { // try to find java automatically std::wstring binaryNameW = ToWString(binaryName); WCHAR buffer[MAX_PATH]; - if (::FindExecutableW(binaryNameW.c_str(), nullptr, buffer) > (HINSTANCE)32) { + if (::FindExecutableW(binaryNameW.c_str(), nullptr, buffer) + > reinterpret_cast(32)) { DWORD binaryType = 0UL; if (!::GetBinaryTypeW(binaryNameW.c_str(), &binaryType)) { qDebug("failed to determine binary type of \"%ls\": %lu", binaryNameW.c_str(), ::GetLastError()); diff --git a/src/editexecutablesdialog.ui b/src/editexecutablesdialog.ui index a2aad19a..2640bb15 100644 --- a/src/editexecutablesdialog.ui +++ b/src/editexecutablesdialog.ui @@ -6,8 +6,8 @@ 0 0 - 384 - 446 + 426 + 460 diff --git a/src/executableslist.h b/src/executableslist.h index eb7d2d6f..0534c09e 100644 --- a/src/executableslist.h +++ b/src/executableslist.h @@ -133,7 +133,8 @@ public: const QString &steamAppID, Executable::Flags flags) { - updateExecutable(title, executableName, arguments, workingDirectory, steamAppID, Executable::AllFlags, flags); + updateExecutable(title, executableName, arguments, workingDirectory, + steamAppID, Executable::AllFlags, flags); } /** diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp index 8ab27124..bf5ee91a 100644 --- a/src/installationmanager.cpp +++ b/src/installationmanager.cpp @@ -346,7 +346,8 @@ DirectoryTree *InstallationManager::createFilesTree() // to uncheck all files in a directory while keeping the dir checked. Those directories are // currently not installed. DirectoryTree::Node *newNode = new DirectoryTree::Node; - newNode->setData(DirectoryTreeInformation(*componentIter, i)); + newNode->setData( + DirectoryTreeInformation(*componentIter, static_cast(i))); currentNode->addNode(newNode, false); currentNode = newNode; } else { diff --git a/src/logbuffer.cpp b/src/logbuffer.cpp index 8207dcbb..522ce3c8 100644 --- a/src/logbuffer.cpp +++ b/src/logbuffer.cpp @@ -53,13 +53,15 @@ void LogBuffer::logMessage(QtMsgType type, const QString &message) if (type >= m_MinMsgType) { Message msg = {type, QTime::currentTime(), message}; if (m_NumMessages < m_Messages.size()) { - beginInsertRows(QModelIndex(), m_NumMessages, m_NumMessages + 1); + beginInsertRows(QModelIndex(), static_cast(m_NumMessages), + static_cast(m_NumMessages) + 1); } m_Messages.at(m_NumMessages % m_Messages.size()) = msg; if (m_NumMessages < m_Messages.size()) { endInsertRows(); } else { - emit dataChanged(createIndex(0, 0), createIndex(m_Messages.size(), 0)); + emit dataChanged(createIndex(0, 0), + createIndex(static_cast(m_Messages.size()), 0)); } ++m_NumMessages; if (type >= QtCriticalMsg) { @@ -84,9 +86,10 @@ void LogBuffer::write() const return; } - unsigned int i = (m_NumMessages > m_Messages.size()) - ? m_NumMessages - m_Messages.size() - : 0U; + unsigned int i + = (m_NumMessages > m_Messages.size()) + ? static_cast(m_NumMessages - m_Messages.size()) + : 0U; for (; i < m_NumMessages; ++i) { file.write(m_Messages.at(i % m_Messages.size()).toString().toUtf8()); file.write("\r\n"); @@ -165,7 +168,7 @@ int LogBuffer::rowCount(const QModelIndex &parent) const if (parent.isValid()) return 0; else - return std::min(m_NumMessages, m_Messages.size()); + return static_cast(std::min(m_NumMessages, m_Messages.size())); } int LogBuffer::columnCount(const QModelIndex &) const @@ -175,9 +178,10 @@ int LogBuffer::columnCount(const QModelIndex &) const QVariant LogBuffer::data(const QModelIndex &index, int role) const { - unsigned offset = m_NumMessages < m_Messages.size() - ? 0 - : m_NumMessages - m_Messages.size(); + unsigned int offset + = m_NumMessages < m_Messages.size() + ? 0 + : static_cast(m_NumMessages - m_Messages.size()); unsigned int msgIndex = (offset + index.row() + 1) % m_Messages.size(); switch (role) { case Qt::DisplayRole: { diff --git a/src/main.cpp b/src/main.cpp index eb3a7248..6db5a50e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -161,9 +161,9 @@ static LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *except if (dbgDLL) { FuncMiniDumpWriteDump funcDump = (FuncMiniDumpWriteDump)::GetProcAddress(dbgDLL, "MiniDumpWriteDump"); if (funcDump) { - wchar_t exeNameBuffer[MAX_PATH]; - ::GetModuleFileNameW(nullptr, exeNameBuffer, MAX_PATH); - QString dumpName = QString::fromWCharArray(exeNameBuffer) + ".dmp"; + QString dataPath = qApp->property("dataPath").toString(); + QString exeName = QFileInfo(qApp->applicationFilePath()).fileName(); + QString dumpName = dataPath + "/" + exeName + ".dmp"; if (QMessageBox::question(nullptr, QObject::tr("Woops"), QObject::tr("ModOrganizer has crashed! " diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 24391999..e174a17c 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -144,15 +144,10 @@ along with Mod Organizer. If not, see . #include #include -#include +#include #include - -#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) #include -#else -#include -#endif #ifndef Q_MOC_RUN #include @@ -4198,7 +4193,12 @@ void MainWindow::on_bossButton_clicked() HANDLE stdOutWrite = INVALID_HANDLE_VALUE; HANDLE stdOutRead = INVALID_HANDLE_VALUE; createStdoutPipe(&stdOutRead, &stdOutWrite); - m_OrganizerCore.prepareVFS(); + try { + m_OrganizerCore.prepareVFS(); + } catch (const std::exception &e) { + QMessageBox::warning(qApp->activeWindow(), tr("Error"), e.what()); + return; + } HANDLE loot = startBinary(QFileInfo(qApp->applicationDirPath() + "/loot/lootcli.exe"), parameters.join(" "), diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 7d23cde5..f504994a 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -45,7 +45,10 @@ Categories - + + + 0 + 3 @@ -92,25 +95,34 @@ - - - false + + + + 0 + 0 + + + + + 0 + 25 + - Click blank area to deselect + Clear + + + true - - - - - - false - - - false + + + + 0 + 0 + diff --git a/src/modflagicondelegate.cpp b/src/modflagicondelegate.cpp index d66c3ac5..5c4167ad 100644 --- a/src/modflagicondelegate.cpp +++ b/src/modflagicondelegate.cpp @@ -58,7 +58,7 @@ size_t ModFlagIconDelegate::getNumIcons(const QModelIndex &index) const if (modIdx < ModInfo::getNumMods()) { ModInfo::Ptr info = ModInfo::getByIndex(modIdx); std::vector flags = info->getFlags(); - int count = flags.size(); + size_t count = flags.size(); if (std::find_first_of(flags.begin(), flags.end(), m_ConflictFlags, m_ConflictFlags + 4) == flags.end()) { ++count; } @@ -71,11 +71,11 @@ size_t ModFlagIconDelegate::getNumIcons(const QModelIndex &index) const QSize ModFlagIconDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &modelIndex) const { - int count = getNumIcons(modelIndex); + size_t count = getNumIcons(modelIndex); unsigned int index = modelIndex.data(Qt::UserRole + 1).toInt(); QSize result; if (index < ModInfo::getNumMods()) { - result = QSize(count * 40, 20); + result = QSize(static_cast(count) * 40, 20); } else { result = QSize(1, 20); } diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 5593b0f0..3c97ca85 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -112,7 +112,7 @@ void ModInfo::createFromOverwrite() unsigned int ModInfo::getNumMods() { QMutexLocker locker(&s_Mutex); - return s_Collection.size(); + return static_cast(s_Collection.size()); } @@ -121,7 +121,7 @@ ModInfo::Ptr ModInfo::getByIndex(unsigned int index) QMutexLocker locker(&s_Mutex); if (index >= s_Collection.size()) { - throw MyException(tr("invalid index %1").arg(index)); + throw MyException(tr("invalid mod index %1").arg(index)); } return s_Collection[index]; } @@ -150,7 +150,7 @@ bool ModInfo::removeMod(unsigned int index) QMutexLocker locker(&s_Mutex); if (index >= s_Collection.size()) { - throw MyException(tr("invalid index %1").arg(index)); + throw MyException(tr("remove: invalid mod index %1").arg(index)); } // update the indices first ModInfo::Ptr modInfo = s_Collection[index]; diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index feaac2d4..e1a2183c 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -351,14 +351,18 @@ void ModInfoDialog::refreshLists() void ModInfoDialog::addCategories(const CategoryFactory &factory, const std::set &enabledCategories, QTreeWidgetItem *root, int rootLevel) { - for (size_t i = 0; i < factory.numCategories(); ++i) { + for (int i = 0; i < static_cast(factory.numCategories()); ++i) { if (factory.getParentID(i) != rootLevel) { continue; } int categoryID = factory.getCategoryID(i); - QTreeWidgetItem *newItem = new QTreeWidgetItem(QStringList(factory.getCategoryName(i))); + QTreeWidgetItem *newItem + = new QTreeWidgetItem(QStringList(factory.getCategoryName(i))); newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable); - newItem->setCheckState(0, enabledCategories.find(categoryID) != enabledCategories.end() ? Qt::Checked : Qt::Unchecked); + newItem->setCheckState(0, enabledCategories.find(categoryID) + != enabledCategories.end() + ? Qt::Checked + : Qt::Unchecked); newItem->setData(0, Qt::UserRole, categoryID); if (factory.hasChildren(i)) { addCategories(factory, enabledCategories, newItem, categoryID); diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index c689501f..4a44c067 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -206,11 +206,12 @@ void NexusInterface::interpretNexusFileName(const QString &fileName, QString &mo std::string candidate2 = result[2].str(); if (candidate2.length() != 0 && (candidate2.find_last_of("VvRr") == std::string::npos)) { // well, that second match might be an id too... - unsigned offset = strspn(candidate2.c_str(), "-_ "); + size_t offset = strspn(candidate2.c_str(), "-_ "); if (offset < candidate2.length() && query) { SelectionDialog selection(tr("Failed to guess mod id for \"%1\", please pick the correct one").arg(fileName)); QString r2Highlight(fileName); - r2Highlight.insert(result.position(2) + result.length(2), "* ").insert(result.position(2) + offset, " *"); + r2Highlight.insert(result.position(2) + result.length(2), "* ") + .insert(result.position(2) + static_cast(offset), " *"); QString r3Highlight(fileName); r3Highlight.insert(result.position(3) + result.length(3), "* ").insert(result.position(3), " *"); @@ -264,14 +265,14 @@ QString NexusInterface::getModURL(int modID) const return QString("%1/mods/%2").arg(getGameURL()).arg(modID); } -bool NexusInterface::isModURL(int modID, QString const &url) const +bool NexusInterface::isModURL(int modID, const QString &url) const { - if (url == getModURL(modID)) { + if (QUrl(url) == QUrl(getModURL(modID))) { return true; } //Try the alternate (old style) mod name QString alt = QString("%1/%2").arg(getOldModsURL()).arg(modID); - return alt == url; + return QUrl(alt) == QUrl(url); } int NexusInterface::requestDescription(int modID, QObject *receiver, QVariant userData, diff --git a/src/organizercore.cpp b/src/organizercore.cpp index f6196aa7..b1eaaef3 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -1345,8 +1345,6 @@ void OrganizerCore::refreshESPList() // clear list try { m_PluginList.refresh(m_CurrentProfile->name(), *m_DirectoryStructure, - m_CurrentProfile->getPluginsFileName(), - m_CurrentProfile->getLoadOrderFileName(), m_CurrentProfile->getLockedOrderFileName()); } catch (const std::exception &e) { reportError(tr("Failed to refresh list of esps: %1").arg(e.what())); @@ -1621,7 +1619,7 @@ void OrganizerCore::loginSuccessful(bool necessary) if (necessary) { MessageDialog::showMessage(tr("login successful"), qApp->activeWindow()); } - foreach (QString url, m_PendingDownloads) { + for (QString url : m_PendingDownloads) { downloadRequestedNXM(url); } m_PendingDownloads.clear(); @@ -1765,9 +1763,7 @@ void OrganizerCore::savePluginList() m_PostRefreshTasks.append([&]() { this->savePluginList(); }); return; } - m_PluginList.saveTo(m_CurrentProfile->getPluginsFileName(), - m_CurrentProfile->getLoadOrderFileName(), - m_CurrentProfile->getLockedOrderFileName(), + m_PluginList.saveTo(m_CurrentProfile->getLockedOrderFileName(), m_CurrentProfile->getDeleterFileName(), m_Settings.hideUncheckedPlugins()); m_PluginList.saveLoadOrder(*m_DirectoryStructure); @@ -1825,6 +1821,7 @@ std::vector OrganizerCore::fileMapping() return result; } + std::vector OrganizerCore::fileMapping( const QString &dataPath, const QString &relPath, const DirectoryEntry *base, const DirectoryEntry *directoryEntry, int createDestination) @@ -1841,8 +1838,9 @@ std::vector OrganizerCore::fileMapping( QString originPath = QString::fromStdWString(base->getOriginByID(origin).getPath()); QString fileName = QString::fromStdWString(current->getName()); +// QString fileName = ToQString(current->getName()); QString source = originPath + relPath + fileName; - QString target = dataPath + relPath + fileName; + QString target = dataPath + relPath + fileName; if (source != target) { result.push_back({source, target, false, false}); } diff --git a/src/persistentcookiejar.cpp b/src/persistentcookiejar.cpp index 6ca0de39..44537491 100644 --- a/src/persistentcookiejar.cpp +++ b/src/persistentcookiejar.cpp @@ -26,7 +26,7 @@ void PersistentCookieJar::save() { QList cookies = allCookies(); data << static_cast(cookies.size()); - foreach (const QNetworkCookie &cookie, allCookies()) { + for (const QNetworkCookie &cookie : allCookies()) { data << cookie.toRawForm(); } diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp index a594f703..c4976d2c 100644 --- a/src/plugincontainer.cpp +++ b/src/plugincontainer.cpp @@ -200,7 +200,7 @@ void PluginContainer::unloadPlugins() bf::for_each(m_Plugins, clearPlugins()); - foreach (const boost::signals2::connection &connection, m_DiagnosisConnections) { + for (const boost::signals2::connection &connection : m_DiagnosisConnections) { connection.disconnect(); } m_DiagnosisConnections.clear(); @@ -283,7 +283,7 @@ void PluginContainer::loadPlugins() m_PluginLoaders.push_back(pluginLoader.release()); } else { m_FailedPlugins.push_back(pluginName); - qWarning("plugin \"%s\" failed to load", qPrintable(pluginName)); + qWarning("plugin \"%s\" failed to load (may be outdated)", qPrintable(pluginName)); } } } diff --git a/src/profile.cpp b/src/profile.cpp index 8cc60cc0..d71b5a0f 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -163,7 +163,7 @@ void Profile::writeModlistNow() return; } - for (int i = m_ModStatus.size() - 1; i >= 0; --i) { + for (int i = static_cast(m_ModStatus.size()) - 1; i >= 0; --i) { // the priority order was inverted on load so it has to be inverted again unsigned int index = m_ModIndexByPriority[i]; if (index != UINT_MAX) { @@ -311,7 +311,7 @@ void Profile::refreshModStatus() // invert priority order to match that of the pluginlist. Also // give priorities to mods not referenced in the profile for (size_t i = 0; i < m_ModStatus.size(); ++i) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(i); + ModInfo::Ptr modInfo = ModInfo::getByIndex(static_cast(i)); if (modInfo->alwaysEnabled()) { m_ModStatus[i].m_Enabled = true; } @@ -340,7 +340,7 @@ void Profile::refreshModStatus() if (topInsert < 0) { int offset = topInsert * -1; for (size_t i = 0; i < m_ModStatus.size(); ++i) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(i); + ModInfo::Ptr modInfo = ModInfo::getByIndex(static_cast(i)); if (modInfo->getFixedPriority() == INT_MAX) { continue; } @@ -636,11 +636,12 @@ bool Profile::enableLocalSaves(bool enable) m_Directory.mkdir("saves"); } } else { - QMessageBox::StandardButton res = QMessageBox::question(QApplication::activeModalWidget(), tr("Delete savegames?"), - tr("Do you want to delete local savegames? (If you select \"No\", the save games " - "will show up again if you re-enable local savegames)"), - QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, - QMessageBox::Cancel); + QMessageBox::StandardButton res = QMessageBox::question( + QApplication::activeModalWidget(), tr("Delete savegames?"), + tr("Do you want to delete local savegames? (If you select \"No\", the " + "save games will show up again if you re-enable local savegames)"), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, + QMessageBox::Cancel); if (res == QMessageBox::Yes) { shellDelete(QStringList(m_Directory.absoluteFilePath("saves"))); } else if (res == QMessageBox::No) { diff --git a/src/profile.h b/src/profile.h index ece23ef9..87dd91a5 100644 --- a/src/profile.h +++ b/src/profile.h @@ -174,7 +174,7 @@ public: /** * @return path to this profile **/ - QString absolutePath() const; + virtual QString absolutePath() const override; /** * @return path to this profile's save games diff --git a/src/profilesdialog.cpp b/src/profilesdialog.cpp index 1a47cf74..40f01f0e 100644 --- a/src/profilesdialog.cpp +++ b/src/profilesdialog.cpp @@ -66,17 +66,15 @@ ProfilesDialog::ProfilesDialog(const QString &profileName, MOBase::IPluginGame c profileIter.next(); QListWidgetItem *item = addItem(profileIter.filePath()); if (profileName == profileIter.fileName()) { - m_ProfilesList->setCurrentItem(item); + ui->profilesList->setCurrentItem(item); } } - QCheckBox *invalidationBox = findChild("invalidationBox"); - BSAInvalidation *invalidation = game->feature(); if (invalidation == nullptr) { - invalidationBox->setToolTip(tr("Archive invalidation isn't required for this game.")); - invalidationBox->setEnabled(false); + ui->invalidationBox->setToolTip(tr("Archive invalidation isn't required for this game.")); + ui->invalidationBox->setEnabled(false); } } @@ -89,13 +87,13 @@ void ProfilesDialog::showEvent(QShowEvent *event) { TutorableDialog::showEvent(event); - if (m_ProfilesList->count() == 0) { - QPoint pos = m_ProfilesList->mapToGlobal(QPoint(0, 0)); - pos.rx() += m_ProfilesList->width() / 2; - pos.ry() += (m_ProfilesList->height() / 2) - 20; + if (ui->profilesList->count() == 0) { + QPoint pos = ui->profilesList->mapToGlobal(QPoint(0, 0)); + pos.rx() += ui->profilesList->width() / 2; + pos.ry() += (ui->profilesList->height() / 2) - 20; QWhatsThis::showText(pos, QObject::tr("Before you can use ModOrganizer, you need to create at least one profile. " - "ATTENTION: Run the game at least once before creating a profile!"), m_ProfilesList); + "ATTENTION: Run the game at least once before creating a profile!"), ui->profilesList); } } @@ -108,7 +106,7 @@ void ProfilesDialog::on_closeButton_clicked() QListWidgetItem *ProfilesDialog::addItem(const QString &name) { QDir profileDir(name); - QListWidgetItem *newItem = new QListWidgetItem(profileDir.dirName(), m_ProfilesList); + QListWidgetItem *newItem = new QListWidgetItem(profileDir.dirName(), ui->profilesList); try { newItem->setData(Qt::UserRole, QVariant::fromValue(Profile::Ptr(new Profile(profileDir, m_Game)))); m_FailState = false; @@ -121,10 +119,9 @@ QListWidgetItem *ProfilesDialog::addItem(const QString &name) void ProfilesDialog::createProfile(const QString &name, bool useDefaultSettings) { try { - QListWidget *profilesList = findChild("profilesList"); - QListWidgetItem *newItem = new QListWidgetItem(name, profilesList); + QListWidgetItem *newItem = new QListWidgetItem(name, ui->profilesList); newItem->setData(Qt::UserRole, QVariant::fromValue(Profile::Ptr(new Profile(name, m_Game, useDefaultSettings)))); - profilesList->addItem(newItem); + ui->profilesList->addItem(newItem); m_FailState = false; } catch (const std::exception&) { m_FailState = true; @@ -135,10 +132,9 @@ void ProfilesDialog::createProfile(const QString &name, bool useDefaultSettings) void ProfilesDialog::createProfile(const QString &name, const Profile &reference) { try { - QListWidget *profilesList = findChild("profilesList"); - QListWidgetItem *newItem = new QListWidgetItem(name, profilesList); + QListWidgetItem *newItem = new QListWidgetItem(name, ui->profilesList); newItem->setData(Qt::UserRole, QVariant::fromValue(Profile::Ptr(Profile::createPtrFrom(name, reference, m_Game)))); - profilesList->addItem(newItem); + ui->profilesList->addItem(newItem); m_FailState = false; } catch (const std::exception&) { m_FailState = true; @@ -164,20 +160,23 @@ void ProfilesDialog::on_addProfileButton_clicked() void ProfilesDialog::on_copyProfileButton_clicked() { bool okClicked; - QString name = QInputDialog::getText(this, tr("Name"), tr("Please enter a name for the new profile"), QLineEdit::Normal, QString(), &okClicked); + QString name = QInputDialog::getText( + this, tr("Name"), tr("Please enter a name for the new profile"), + QLineEdit::Normal, QString(), &okClicked); fixDirectoryName(name); if (okClicked) { if (name.size() > 0) { - QListWidget *profilesList = findChild("profilesList"); - try { - const Profile::Ptr currentProfile = profilesList->currentItem()->data(Qt::UserRole).value(); + const Profile::Ptr currentProfile = ui->profilesList->currentItem() + ->data(Qt::UserRole) + .value(); createProfile(name, *currentProfile); } catch (const std::exception &e) { reportError(tr("failed to copy profile: %1").arg(e.what())); } } else { - QMessageBox::warning(this, tr("Invalid name"), tr("Invalid profile name")); + QMessageBox::warning(this, tr("Invalid name"), + tr("Invalid profile name")); } } } @@ -188,13 +187,11 @@ void ProfilesDialog::on_removeProfileButton_clicked() QMessageBox::Yes | QMessageBox::No); if (confirmBox.exec() == QMessageBox::Yes) { - QListWidget *profilesList = findChild("profilesList"); - - Profile::Ptr currentProfile = profilesList->currentItem()->data(Qt::UserRole).value(); + Profile::Ptr currentProfile = ui->profilesList->currentItem()->data(Qt::UserRole).value(); QString profilePath; if (currentProfile.get() == nullptr) { profilePath = Settings::instance().getProfileDirectory() - + "/" + profilesList->currentItem()->text(); + + "/" + ui->profilesList->currentItem()->text(); if (QMessageBox::question(this, tr("Profile broken"), tr("This profile you're about to delete seems to be broken or the path is invalid. " "I'm about to delete the following folder: \"%1\". Proceed?").arg(profilePath), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) { @@ -205,7 +202,7 @@ void ProfilesDialog::on_removeProfileButton_clicked() // we have to get rid of the it before deleting the directory profilePath = currentProfile->absolutePath(); } - QListWidgetItem* item = profilesList->takeItem(profilesList->currentRow()); + QListWidgetItem* item = ui->profilesList->takeItem(ui->profilesList->currentRow()); if (item != nullptr) { delete item; } @@ -270,53 +267,50 @@ void ProfilesDialog::on_invalidationBox_stateChanged(int state) void ProfilesDialog::on_profilesList_currentItemChanged(QListWidgetItem *current, QListWidgetItem*) { - QCheckBox *invalidationBox = findChild("invalidationBox"); - QCheckBox *localSavesBox = findChild("localSavesBox"); - QPushButton *copyButton = findChild("copyProfileButton"); - QPushButton *removeButton = findChild("removeProfileButton"); - QPushButton *transferButton = findChild("transferButton"); - QPushButton *renameButton = findChild("renameButton"); - if (current != nullptr) { if (!current->data(Qt::UserRole).isValid()) return; const Profile::Ptr currentProfile = current->data(Qt::UserRole).value(); try { bool invalidationSupported = false; - invalidationBox->blockSignals(true); - invalidationBox->setChecked(currentProfile->invalidationActive(&invalidationSupported)); - invalidationBox->setEnabled(invalidationSupported); - invalidationBox->blockSignals(false); + ui->invalidationBox->blockSignals(true); + ui->invalidationBox->setChecked(currentProfile->invalidationActive(&invalidationSupported)); + ui->invalidationBox->setEnabled(invalidationSupported); + ui->invalidationBox->blockSignals(false); bool localSaves = currentProfile->localSavesEnabled(); - transferButton->setEnabled(localSaves); + ui->transferButton->setEnabled(localSaves); // prevent the stateChanged-event for the saves-box from triggering, otherwise it may think local saves // were disabled and delete the files/rename the dir - localSavesBox->blockSignals(true); - localSavesBox->setChecked(localSaves); - localSavesBox->blockSignals(false); + ui->localSavesBox->blockSignals(true); + ui->localSavesBox->setChecked(localSaves); + ui->localSavesBox->blockSignals(false); + + ui->copyProfileButton->setEnabled(true); + ui->removeProfileButton->setEnabled(true); + ui->renameButton->setEnabled(true); - copyButton->setEnabled(true); - removeButton->setEnabled(true); - renameButton->setEnabled(true); + ui->localIniFilesBox->blockSignals(true); + ui->localIniFilesBox->setChecked(currentProfile->localSettingsEnabled()); + ui->localIniFilesBox->blockSignals(false); } catch (const std::exception& E) { reportError(tr("failed to determine if invalidation is active: %1").arg(E.what())); - copyButton->setEnabled(false); - removeButton->setEnabled(false); - renameButton->setEnabled(false); - invalidationBox->setChecked(false); + ui->copyProfileButton->setEnabled(false); + ui->removeProfileButton->setEnabled(false); + ui->renameButton->setEnabled(false); + ui->invalidationBox->setChecked(false); } } else { - invalidationBox->setChecked(false); - copyButton->setEnabled(false); - removeButton->setEnabled(false); - renameButton->setEnabled(false); + ui->invalidationBox->setChecked(false); + ui->copyProfileButton->setEnabled(false); + ui->removeProfileButton->setEnabled(false); + ui->renameButton->setEnabled(false); } } void ProfilesDialog::on_localSavesBox_stateChanged(int state) { - Profile::Ptr currentProfile = m_ProfilesList->currentItem()->data(Qt::UserRole).value(); + Profile::Ptr currentProfile = ui->profilesList->currentItem()->data(Qt::UserRole).value(); if (currentProfile->enableLocalSaves(state == Qt::Checked)) { ui->transferButton->setEnabled(state == Qt::Checked); diff --git a/src/profilesdialog.ui b/src/profilesdialog.ui index 0c952877..fe03f466 100644 --- a/src/profilesdialog.ui +++ b/src/profilesdialog.ui @@ -6,8 +6,8 @@ 0 0 - 471 - 318 + 482 + 332 diff --git a/src/shared/util.cpp b/src/shared/util.cpp index 3692aae1..5491a9e6 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -81,12 +81,16 @@ std::wstring ToWString(const std::string &source, bool utf8) if (!utf8) { codepage = AreFileApisANSI() ? GetACP() : GetOEMCP(); } - int sizeRequired = ::MultiByteToWideChar(codepage, 0, source.c_str(), source.length(), nullptr, 0); + int sizeRequired + = ::MultiByteToWideChar(codepage, 0, source.c_str(), + static_cast(source.length()), nullptr, 0); if (sizeRequired == 0) { throw windows_error("failed to convert string to wide character"); } result.resize(sizeRequired, L'\0'); - ::MultiByteToWideChar(codepage, 0, source.c_str(), source.length(), &result[0], sizeRequired); + ::MultiByteToWideChar(codepage, 0, source.c_str(), + static_cast(source.length()), &result[0], + sizeRequired); } return result; diff --git a/src/usvfsconnector.cpp b/src/usvfsconnector.cpp index db20a54f..bc818685 100644 --- a/src/usvfsconnector.cpp +++ b/src/usvfsconnector.cpp @@ -76,13 +76,16 @@ LogWorker::~LogWorker() void LogWorker::process() { + int noLogCycles = 0; while (!m_QuitRequested) { if (GetLogMessages(&m_Buffer[0], m_Buffer.size(), false)) { m_LogFile.write(m_Buffer.c_str()); m_LogFile.write("\n"); m_LogFile.flush(); + noLogCycles = 0; } else { - QThread::sleep(1); + QThread::msleep(std::min(40, noLogCycles) * 5); + ++noLogCycles; } } emit finished(); @@ -148,11 +151,13 @@ void UsvfsConnector::updateMapping(const MappingType &mapping) if (value % 10 == 0) { QCoreApplication::processEvents(); } + if (map.isDirectory) { VirtualLinkDirectoryStatic(map.source.toStdWString().c_str(), map.destination.toStdWString().c_str(), (map.createTarget ? LINKFLAG_CREATETARGET : 0) - | LINKFLAG_RECURSIVE); + | LINKFLAG_RECURSIVE + ); } else { VirtualLinkFile(map.source.toStdWString().c_str(), map.destination.toStdWString().c_str(), 0); diff --git a/src/version.rc b/src/version.rc index 028912d7..872c65d7 100644 --- a/src/version.rc +++ b/src/version.rc @@ -1,7 +1,7 @@ #include "Winver.h" -#define VER_FILEVERSION 2,0,4 -#define VER_FILEVERSION_STR "2,0,4alpha\0" +#define VER_FILEVERSION 2,0,5 +#define VER_FILEVERSION_STR "2.0.5beta\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION -- cgit v1.3.1 From afe0d749c438b086a7efa08e4d443be94f56d101 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sat, 7 May 2016 00:18:23 +0200 Subject: added option to use a mod as the target to create now files instead of "overwrite" --- src/CMakeLists.txt | 2 + src/editexecutablesdialog.cpp | 57 +++++++++++++++++++++++------ src/editexecutablesdialog.h | 21 +++++++++-- src/editexecutablesdialog.ui | 45 +++++++++++++++++++++++ src/mainwindow.cpp | 55 +++++++++++++++++++--------- src/mainwindow.h | 1 + src/organizercore.cpp | 85 ++++++++++++++++++++++++++++++++++++------- src/organizercore.h | 20 ++++++---- src/usvfsconnector.cpp | 1 + 9 files changed, 235 insertions(+), 52 deletions(-) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a8b43c59..c6c038e8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -84,6 +84,7 @@ SET(organizer_SRCS organizercore.cpp instancemanager.cpp usvfsconnector.cpp + eventfilter.cpp shared/inject.cpp shared/windows_error.cpp @@ -170,6 +171,7 @@ SET(organizer_HDRS iuserinterface.h instancemanager.h usvfsconnector.h + eventfilter.h shared/inject.h shared/windows_error.h diff --git a/src/editexecutablesdialog.cpp b/src/editexecutablesdialog.cpp index 05a7603d..42d6f58b 100644 --- a/src/editexecutablesdialog.cpp +++ b/src/editexecutablesdialog.cpp @@ -29,15 +29,20 @@ along with Mod Organizer. If not, see . using namespace MOBase; using namespace MOShared; -EditExecutablesDialog::EditExecutablesDialog(const ExecutablesList &executablesList, QWidget *parent) +EditExecutablesDialog::EditExecutablesDialog( + const ExecutablesList &executablesList, const ModList &modList, + Profile *profile, QWidget *parent) : TutorableDialog("EditExecutables", parent) , ui(new Ui::EditExecutablesDialog) , m_CurrentItem(nullptr) , m_ExecutablesList(executablesList) + , m_Profile(profile) { ui->setupUi(this); refreshExecutablesWidget(); + + ui->newFilesModBox->addItems(modList.allMods()); } EditExecutablesDialog::~EditExecutablesDialog() @@ -86,23 +91,30 @@ void EditExecutablesDialog::resetInput() ui->appIDOverwriteEdit->clear(); ui->overwriteAppIDBox->setChecked(false); ui->useAppIconCheckBox->setChecked(false); + ui->newFilesModCheckBox->setChecked(false); m_CurrentItem = nullptr; } void EditExecutablesDialog::saveExecutable() { - m_ExecutablesList.updateExecutable(ui->titleEdit->text(), - QDir::fromNativeSeparators(ui->binaryEdit->text()), - ui->argumentsEdit->text(), - QDir::fromNativeSeparators(ui->workingDirEdit->text()), - ui->overwriteAppIDBox->isChecked() ? - ui->appIDOverwriteEdit->text() : "", - Executable::UseApplicationIcon | Executable::CustomExecutable, - (ui->useAppIconCheckBox->isChecked() ? - Executable::UseApplicationIcon : Executable::Flags()) - | Executable::CustomExecutable); + m_ExecutablesList.updateExecutable( + ui->titleEdit->text(), + QDir::fromNativeSeparators(ui->binaryEdit->text()), + ui->argumentsEdit->text(), + QDir::fromNativeSeparators(ui->workingDirEdit->text()), + ui->overwriteAppIDBox->isChecked() ? + ui->appIDOverwriteEdit->text() : "", + Executable::UseApplicationIcon | Executable::CustomExecutable, + (ui->useAppIconCheckBox->isChecked() ? + Executable::UseApplicationIcon : Executable::Flags()) + | Executable::CustomExecutable); + + if (ui->newFilesModCheckBox->isChecked()) { + m_Profile->storeSetting("custom_overwrites", ui->titleEdit->text(), + ui->newFilesModBox->currentText()); } +} void EditExecutablesDialog::delayedRefresh() @@ -212,8 +224,13 @@ bool EditExecutablesDialog::executableChanged() { if (m_CurrentItem != nullptr) { Executable const &selectedExecutable(m_ExecutablesList.find(m_CurrentItem->text())); + + QString storedCustomOverwrite = m_Profile->setting("custom_overwrites", selectedExecutable.m_Title).toString(); + return selectedExecutable.m_Arguments != ui->argumentsEdit->text() || selectedExecutable.m_SteamAppID != ui->appIDOverwriteEdit->text() + || !storedCustomOverwrite.isEmpty() != ui->newFilesModCheckBox->isChecked() + || !storedCustomOverwrite.isEmpty() && (storedCustomOverwrite != ui->newFilesModBox->currentText()) || selectedExecutable.m_WorkingDirectory != QDir::fromNativeSeparators(ui->workingDirEdit->text()) || selectedExecutable.m_BinaryInfo.absoluteFilePath() != QDir::fromNativeSeparators(ui->binaryEdit->text()) || selectedExecutable.usesOwnIcon() != ui->useAppIconCheckBox->isChecked(); @@ -296,5 +313,23 @@ void EditExecutablesDialog::on_executablesListBox_clicked(const QModelIndex &cur ui->appIDOverwriteEdit->clear(); } ui->useAppIconCheckBox->setChecked(selectedExecutable.usesOwnIcon()); + + int index = -1; + + QString customOverwrite = m_Profile->setting("custom_overwrites", selectedExecutable.m_Title).toString(); + if (!customOverwrite.isEmpty()) { + index = ui->newFilesModBox->findText(customOverwrite); + qDebug("find %s -> %d", qPrintable(customOverwrite), index); + } + + ui->newFilesModCheckBox->setChecked(index != -1); + if (index != -1) { + ui->newFilesModBox->setCurrentIndex(index); + } } } + +void EditExecutablesDialog::on_newFilesModCheckBox_toggled(bool checked) +{ + ui->newFilesModBox->setEnabled(checked); +} diff --git a/src/editexecutablesdialog.h b/src/editexecutablesdialog.h index 4f6c5315..0f3dbaff 100644 --- a/src/editexecutablesdialog.h +++ b/src/editexecutablesdialog.h @@ -24,11 +24,16 @@ along with Mod Organizer. If not, see . #include #include #include "executableslist.h" +#include "profile.h" namespace Ui { class EditExecutablesDialog; } + +class ModList; + + /** * @brief Dialog to manage the list of executables **/ @@ -44,7 +49,10 @@ public: * @param executablesList current list of executables * @param parent parent widget **/ - explicit EditExecutablesDialog(const ExecutablesList &executablesList, QWidget *parent = 0); + explicit EditExecutablesDialog(const ExecutablesList &executablesList, + const ModList &modList, + Profile *profile, + QWidget *parent = 0); ~EditExecutablesDialog(); @@ -56,6 +64,10 @@ public: ExecutablesList getExecutablesList() const; void saveExecutable(); + +private slots: + void on_newFilesModCheckBox_toggled(bool checked); + private slots: void on_binaryEdit_textChanged(const QString &arg1); @@ -89,12 +101,13 @@ private: bool executableChanged(); private: - Ui::EditExecutablesDialog *ui; + Ui::EditExecutablesDialog *ui; - QListWidgetItem *m_CurrentItem; + QListWidgetItem *m_CurrentItem; - ExecutablesList m_ExecutablesList; + ExecutablesList m_ExecutablesList; + Profile *m_Profile; }; #endif // EDITEXECUTABLESDIALOG_H diff --git a/src/editexecutablesdialog.ui b/src/editexecutablesdialog.ui index 2640bb15..b3543c95 100644 --- a/src/editexecutablesdialog.ui +++ b/src/editexecutablesdialog.ui @@ -164,6 +164,27 @@ Right now the only case I know of where this needs to be overwritten is for the + + + + + + If this is enabled, new files are created in the specified mod instead of the "Overwrite" mod. + + + Create Files in Mod instead of Overwrite (*) + + + + + + + false + + + + + @@ -171,6 +192,13 @@ Right now the only case I know of where this needs to be overwritten is for the + + + + (*) This setting is profile-specific + + + @@ -235,6 +263,23 @@ Right now the only case I know of where this needs to be overwritten is for the + + executablesListBox + titleEdit + binaryEdit + browseButton + workingDirEdit + browseDirButton + argumentsEdit + overwriteAppIDBox + appIDOverwriteEdit + newFilesModCheckBox + newFilesModBox + useAppIconCheckBox + addButton + removeButton + closeButton + diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index e174a17c..7944acfc 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1004,13 +1004,8 @@ void MainWindow::startExeAction() { QAction *action = qobject_cast(sender()); if (action != nullptr) { - const Executable &selectedExecutable(m_OrganizerCore.executablesList()->find(action->text())); m_OrganizerCore.spawnBinary( - selectedExecutable.m_BinaryInfo, - selectedExecutable.m_Arguments, - selectedExecutable.m_WorkingDirectory.length() != 0 ? selectedExecutable.m_WorkingDirectory - : selectedExecutable.m_BinaryInfo.absolutePath(), - selectedExecutable.m_SteamAppID); + m_OrganizerCore.executablesList()->find(action->text())); } else { qCritical("not an action?"); } @@ -1516,14 +1511,7 @@ void MainWindow::installMod(QString fileName) void MainWindow::on_startButton_clicked() { - const Executable &selectedExecutable(getSelectedExecutable()); - - m_OrganizerCore.spawnBinary( - selectedExecutable.m_BinaryInfo, - selectedExecutable.m_Arguments, - selectedExecutable.m_WorkingDirectory.length() != 0 ? selectedExecutable.m_WorkingDirectory - : selectedExecutable.m_BinaryInfo.absolutePath(), - selectedExecutable.m_SteamAppID); + m_OrganizerCore.spawnBinary(getSelectedExecutable()); } @@ -1613,7 +1601,9 @@ bool MainWindow::modifyExecutablesDialog() { bool result = false; try { - EditExecutablesDialog dialog(*m_OrganizerCore.executablesList()); + EditExecutablesDialog dialog(*m_OrganizerCore.executablesList(), + *m_OrganizerCore.modList(), + m_OrganizerCore.currentProfile()); if (dialog.exec() == QDialog::Accepted) { m_OrganizerCore.setExecutablesList(dialog.getExecutablesList()); result = true; @@ -2423,7 +2413,32 @@ void MainWindow::information_clicked() } } +void MainWindow::createEmptyMod_clicked() +{ + GuessedValue name; + name.setFilter(&fixDirectoryName); + + while (name->isEmpty()) { + bool ok; + name.update(QInputDialog::getText(this, tr("Create Mod..."), + tr("This will create an empty mod.\n" + "Please enter a name:"), QLineEdit::Normal, "", &ok), + GUESS_USER); + if (!ok) { + return; + } + } + + if (m_OrganizerCore.getMod(name) != nullptr) { + reportError(tr("A mod with this name already exists")); + return; + } + IModInterface *newMod = m_OrganizerCore.createMod(name); + if (newMod == nullptr) { + return; + } +} void MainWindow::createModFromOverwrite() { @@ -2862,6 +2877,8 @@ QMenu *MainWindow::modListContextMenu() QMenu *menu = new QMenu(this); menu->addAction(tr("Install Mod..."), this, SLOT(installMod_clicked())); + menu->addAction(tr("Create empty mod"), this, SLOT(createEmptyMod_clicked())); + menu->addAction(tr("Enable all visible"), this, SLOT(enableVisibleMods())); menu->addAction(tr("Disable all visible"), this, SLOT(disableVisibleMods())); @@ -3529,10 +3546,14 @@ void MainWindow::openDataFile() QString arguments; switch (getBinaryExecuteInfo(targetInfo, binaryInfo, arguments)) { case 1: { - m_OrganizerCore.spawnBinaryDirect(binaryInfo, arguments, m_OrganizerCore.currentProfile()->name(), targetInfo.absolutePath(), ""); + m_OrganizerCore.spawnBinaryDirect( + binaryInfo, arguments, m_OrganizerCore.currentProfile()->name(), + targetInfo.absolutePath(), "", ""); } break; case 2: { - ::ShellExecuteW(nullptr, L"open", ToWString(targetInfo.absoluteFilePath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL); + ::ShellExecuteW(nullptr, L"open", + ToWString(targetInfo.absoluteFilePath()).c_str(), + nullptr, nullptr, SW_SHOWNORMAL); } break; default: { // nop diff --git a/src/mainwindow.h b/src/mainwindow.h index 4e2e8e21..d2f3fb47 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -369,6 +369,7 @@ private slots: // modlist context menu void installMod_clicked(); + void createEmptyMod_clicked(); void restoreBackup_clicked(); void renameMod_clicked(); void removeMod_clicked(); diff --git a/src/organizercore.cpp b/src/organizercore.cpp index b1eaaef3..8f308c88 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -596,7 +596,7 @@ void OrganizerCore::createDefaultProfile() void OrganizerCore::prepareVFS() { - m_USVFS.updateMapping(fileMapping()); + m_USVFS.updateMapping(fileMapping(m_CurrentProfile->name(), QString())); } void OrganizerCore::setCurrentProfile(const QString &profileName) @@ -969,10 +969,21 @@ QStringList OrganizerCore::modsSortedByProfilePriority() const return res; } +void OrganizerCore::spawnBinary(const Executable &exe) +{ + spawnBinary( + exe.m_BinaryInfo, exe.m_Arguments, + exe.m_WorkingDirectory.length() != 0 ? exe.m_WorkingDirectory + : exe.m_BinaryInfo.absolutePath(), + exe.m_SteamAppID, + m_CurrentProfile->setting("custom_overwrites", exe.m_Title).toString()); +} + void OrganizerCore::spawnBinary(const QFileInfo &binary, const QString &arguments, const QDir ¤tDirectory, - const QString &steamAppID) + const QString &steamAppID, + const QString &customOverwrite) { LockedDialog *dialog = new LockedDialog(qApp->activeWindow()); dialog->show(); @@ -983,7 +994,7 @@ void OrganizerCore::spawnBinary(const QFileInfo &binary, HANDLE processHandle = spawnBinaryDirect(binary, arguments, m_CurrentProfile->name(), - currentDirectory, steamAppID); + currentDirectory, steamAppID, customOverwrite); if (processHandle != INVALID_HANDLE_VALUE) { if (m_UserInterface != nullptr) { m_UserInterface->setWindowEnabled(false); @@ -1082,7 +1093,8 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary, const QString &arguments, const QString &profileName, const QDir ¤tDirectory, - const QString &steamAppID) + const QString &steamAppID, + const QString &customOverwrite) { prepareStart(); @@ -1135,11 +1147,16 @@ HANDLE OrganizerCore::spawnBinaryDirect(const QFileInfo &binary, } if (m_AboutToRun(binary.absoluteFilePath())) { - m_USVFS.updateMapping(fileMapping()); + try { + m_USVFS.updateMapping(fileMapping(profileName, customOverwrite)); + } catch (const std::exception &e) { + QMessageBox::warning(qApp->activeWindow(), tr("Error"), e.what()); + return INVALID_HANDLE_VALUE; + } + QString modsPath = settings().getModDirectory(); QString binPath = binary.absoluteFilePath(); - if (binPath.startsWith(modsPath, Qt::CaseInsensitive)) { // binary was installed as a MO mod. Need to start it through a (hooked) // proxy to ensure pathes are correct @@ -1185,6 +1202,7 @@ HANDLE OrganizerCore::startApplication(const QString &executable, } } QString steamAppID; + QString customOverwrite; if (executable.contains('\\') || executable.contains('/')) { // file path @@ -1200,6 +1218,9 @@ HANDLE OrganizerCore::startApplication(const QString &executable, try { const Executable &exe = m_ExecutablesList.findByBinary(binary); steamAppID = exe.m_SteamAppID; + customOverwrite + = m_CurrentProfile->setting("custom_overwrites", exe.m_Title) + .toString(); } catch (const std::runtime_error &) { // nop } @@ -1208,6 +1229,9 @@ HANDLE OrganizerCore::startApplication(const QString &executable, try { const Executable &exe = m_ExecutablesList.find(executable); steamAppID = exe.m_SteamAppID; + customOverwrite + = m_CurrentProfile->setting("custom_overwrites", exe.m_Title) + .toString(); if (arguments == "") { arguments = exe.m_Arguments; } @@ -1223,7 +1247,7 @@ HANDLE OrganizerCore::startApplication(const QString &executable, } return spawnBinaryDirect(binary, arguments, profileName, currentDirectory, - steamAppID); + steamAppID, customOverwrite); } bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode) @@ -1781,7 +1805,8 @@ void OrganizerCore::prepareStart() storeSettings(); } -std::vector OrganizerCore::fileMapping() +std::vector OrganizerCore::fileMapping(const QString &profileName, + const QString &customOverwrite) { // need to wait until directory structure while (m_DirectoryUpdate) { @@ -1789,12 +1814,39 @@ std::vector OrganizerCore::fileMapping() QCoreApplication::processEvents(); } - int overwriteId = m_DirectoryStructure->getOriginByName(L"Overwrite").getID(); - IPluginGame *game = qApp->property("managed_game").value(); - MappingType result = fileMapping( - QDir::toNativeSeparators(game->dataDirectory().absolutePath()), "\\", - directoryStructure(), directoryStructure(), overwriteId); + Profile profile(QDir(m_Settings.getProfileDirectory() + "/" + profileName), + game); + + MappingType result; + + QString dataPath + = QDir::toNativeSeparators(game->dataDirectory().absolutePath()); + + bool overwriteActive = false; + + for (auto mod : profile.getActiveMods()) { + if (std::get<0>(mod).compare("overwrite", Qt::CaseInsensitive) == 0) { + continue; + } + + unsigned int modIndex = ModInfo::getIndex(std::get<0>(mod)); + ModInfo::Ptr modPtr = ModInfo::getByIndex(modIndex); + + bool createTarget = customOverwrite == std::get<0>(mod); + + overwriteActive |= createTarget; + + if (modPtr->isRegular()) { + result.insert(result.end(), {QDir::toNativeSeparators(std::get<1>(mod)), + dataPath, true, createTarget}); + } + } + + if (!overwriteActive && !customOverwrite.isEmpty()) { + throw MyException(tr("The designated write target \"%1\" is not enabled.") + .arg(customOverwrite)); + } if (m_CurrentProfile->localSavesEnabled()) { LocalSavegames *localSaves = game->feature(); @@ -1808,6 +1860,13 @@ std::vector OrganizerCore::fileMapping() } } + result.insert(result.end(), { + QDir::toNativeSeparators(m_Settings.getOverwriteDirectory()), + QDir::toNativeSeparators(game->dataDirectory().absolutePath()), + true, + customOverwrite.isEmpty() + }); + for (MOBase::IPluginFileMapper *mapper : m_PluginContainer->plugins()) { IPlugin *plugin = dynamic_cast(mapper); diff --git a/src/organizercore.h b/src/organizercore.h index ea37d72c..cd7af1b8 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -129,8 +129,8 @@ public: void doAfterLogin(const std::function &function) { m_PostLoginTasks.append(function); } - void spawnBinary(const QFileInfo &binary, const QString &arguments = "", const QDir ¤tDirectory = QDir(), const QString &steamAppID = ""); - HANDLE spawnBinaryDirect(const QFileInfo &binary, const QString &arguments, const QString &profileName, const QDir ¤tDirectory, const QString &steamAppID); + void spawnBinary(const Executable &exe); + HANDLE spawnBinaryDirect(const QFileInfo &binary, const QString &arguments, const QString &profileName, const QDir ¤tDirectory, const QString &steamAppID, const QString &customOverwrite); void loginSuccessfulUpdate(bool necessary); void loginFailedUpdate(const QString &message); @@ -173,11 +173,6 @@ public: void refreshModList(bool saveChanges = true); QStringList modsSortedByProfilePriority() const; - /** - * @brief return a descriptor of the mappings real file->virtual file - */ - std::vector fileMapping(); - public: // IPluginDiagnose interface virtual std::vector activeProblems() const; @@ -231,12 +226,23 @@ private: bool testForSteam(); + /** + * @brief return a descriptor of the mappings real file->virtual file + */ + std::vector fileMapping(const QString &profile, + const QString &customOverwrite); + std::vector fileMapping(const QString &dataPath, const QString &relPath, const MOShared::DirectoryEntry *base, const MOShared::DirectoryEntry *directoryEntry, int createDestination); + void spawnBinary(const QFileInfo &binary, const QString &arguments = "", + const QDir ¤tDirectory = QDir(), + const QString &steamAppID = "", + const QString &customOverwrite = ""); + private slots: void directory_refreshed(); diff --git a/src/usvfsconnector.cpp b/src/usvfsconnector.cpp index bc818685..2af28522 100644 --- a/src/usvfsconnector.cpp +++ b/src/usvfsconnector.cpp @@ -136,6 +136,7 @@ UsvfsConnector::~UsvfsConnector() m_WorkerThread.wait(); } + void UsvfsConnector::updateMapping(const MappingType &mapping) { QProgressDialog progress; -- cgit v1.3.1 From 3a8da159da55dd92f1888c34e914721032278803 Mon Sep 17 00:00:00 2001 From: Tannin Date: Mon, 16 May 2016 14:46:54 +0200 Subject: removed obsolete inject code --- src/CMakeLists.txt | 2 - src/shared/inject.cpp | 147 -------------------------------------------------- src/shared/inject.h | 31 ----------- 3 files changed, 180 deletions(-) delete mode 100644 src/shared/inject.cpp delete mode 100644 src/shared/inject.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c6c038e8..854f7667 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -86,7 +86,6 @@ SET(organizer_SRCS usvfsconnector.cpp eventfilter.cpp - shared/inject.cpp shared/windows_error.cpp shared/error_report.cpp shared/directoryentry.cpp @@ -173,7 +172,6 @@ SET(organizer_HDRS usvfsconnector.h eventfilter.h - shared/inject.h shared/windows_error.h shared/error_report.h shared/directoryentry.h diff --git a/src/shared/inject.cpp b/src/shared/inject.cpp deleted file mode 100644 index fcd4cfab..00000000 --- a/src/shared/inject.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#include "inject.h" -/*#if defined UNICODE && !defined _UNICODE -#define _UNICODE 1 -#endif*/ -#include -#include -#include -#include -#include -#include -#include -#include "windows_error.h" -#include "error_report.h" - -namespace MOShared { - - -struct TParameters { - char dllname[MAX_PATH]; - wchar_t profileName[101]; - char initstr[5]; - int logLevel; -}; - - -typedef HMODULE (WINAPI *TLoadLibraryType)(LPCTSTR); -typedef FARPROC (WINAPI *TGetProcAddressType)(HMODULE, LPCSTR); - - - -void injectDLL(HANDLE processHandle, HANDLE threadHandle, const std::string &dllname, const std::wstring &profileName, int logLevel) -{ - // prepare parameters that are to be passed to the injected dll - TParameters parameters; - memset(¶meters, '\0', sizeof(TParameters)); - strncpy(parameters.dllname, dllname.c_str(), MAX_PATH - 1); - wcsncpy(parameters.profileName, profileName.c_str(), 100); - _snprintf(parameters.initstr, 5, "Init"); //this is the name of thie initialisation function we want to call in the target process - - HMODULE k32mod = ::LoadLibrary(__TEXT("kernel32.dll")); - TLoadLibraryType loadLibraryFunc = nullptr; - TGetProcAddressType getProcAddressFunc = nullptr; - // ansi binaries - if (k32mod != nullptr) { - loadLibraryFunc = reinterpret_cast(::GetProcAddress(k32mod, "LoadLibraryA")); - getProcAddressFunc = reinterpret_cast(::GetProcAddress(k32mod, "GetProcAddress")); - if ((loadLibraryFunc == nullptr) || (getProcAddressFunc == nullptr)) { - throw windows_error("failed to determine address for required functions"); - } - } else { - throw windows_error("kernel32.dll not loaded??"); - } - - // allocate memory in the target process and write the parameter-block there - LPVOID remoteMem = ::VirtualAllocEx(processHandle, nullptr, sizeof(TParameters), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); - if (remoteMem == nullptr) { - throw windows_error("failed to allocate memory in target process"); - } - SIZE_T written; - if (!::WriteProcessMemory(processHandle, remoteMem, ¶meters, sizeof(TParameters), &written) || - (written != sizeof(TParameters))) { - throw windows_error("failed to write parameters to target process"); - } - - // now for the interesting part: write a stub into the target process that is run before any code of the original binary. This code will load - // our injected dll into the process and run its Init-function which takes the profile name as its parameter - // obviously this code is ultra-hacky - - // construct the stub in beautiful assembler with placeholders - BYTE stubLocal[] = { 0x60, // PUSHAD - 0xB8, 0xBA, 0xAD, 0xF0, 0x0D, // MOV EAX, imm32 (LoadLibrary) - 0x68, 0xBA, 0xAD, 0xF0, 0x0D, // PUSH imm32 (dllname) - 0xFF, 0xD0, // CALL EAX (=LoadLibrary, leaves module handle of our dll in eax) - 0x68, 0xBA, 0xAD, 0xF0, 0x0D, // PUSH imm32 ("Init") - 0x50, // PUSH EAX - 0xB8, 0xBA, 0xAD, 0xF0, 0x0D, // MOVE EAX, imm32 (GetProcAddress) - 0xFF, 0xD0, // CALL EAX (=GetProcAddress, leaves address of init function in eax) - 0x68, 0xBA, 0xAD, 0xF0, 0x0D, // PUSH imm32 (profile name) - 0x68, 0xBA, 0xAD, 0xF0, 0x0D, // PUSH imm32 (log level) - 0xFF, 0xD0, // CALL EAX (=InitFunction) - 0x58, // POP EAX (init function is defined cdecl) - 0x58, // POP EAX (init function is defined cdecl) - 0x61, // POPAD - 0xE9, 0xBA, 0xAD, 0xF0, 0x0D // JMP near, relative (=original entry point) - }; - - // reserve memory for the stub - PBYTE stubRemote = reinterpret_cast(::VirtualAllocEx(processHandle, nullptr, sizeof(stubLocal), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)); - if (stubRemote == nullptr) { - throw windows_error("failed to allocate memory for stub"); - } - TParameters *remoteParams = reinterpret_cast(remoteMem); - - // dizzy yet? we still have to calculate the entry point as an address relative to our stub - ULONG entryPoint = 0; - - // not implemented on 64 bit systems -#ifdef _X86_ - CONTEXT threadContext; - threadContext.ContextFlags = CONTEXT_CONTROL; - if (::GetThreadContext(threadHandle, &threadContext) == 0) { - throw windows_error("failed to access thread context. Please note that Mod Organizer does not support 64bit binaries!"); - } else { - entryPoint = threadContext.Eip - (reinterpret_cast(stubRemote) + sizeof(stubLocal)); - } - // now replace each baadf00d by the correct value. The pointers need to be the pointers in the REMOTE memory of course, that's why we copied them there - *(PULONG)&stubLocal[2] = reinterpret_cast(*loadLibraryFunc); - *(PULONG)&stubLocal[7] = reinterpret_cast(remoteParams->dllname); - *(PULONG)&stubLocal[14] = reinterpret_cast(remoteParams->initstr); - *(PULONG)&stubLocal[20] = reinterpret_cast(*getProcAddressFunc); - *(PULONG)&stubLocal[27] = reinterpret_cast(remoteParams->profileName); - *(PULONG)&stubLocal[32] = logLevel; - *(PULONG)&stubLocal[42] = entryPoint; - // almost there. copy stub to target process - if (!::WriteProcessMemory(processHandle, stubRemote, reinterpret_cast(stubLocal), sizeof(stubLocal), &written) || - (written != sizeof(stubLocal))) { - throw windows_error("failed to write stub to target process"); - } - - // finally, make the stub the new next thing for the thread to execute - threadContext.Eip = (ULONG)stubRemote; - if (::SetThreadContext(threadHandle, &threadContext) == 0) { - throw windows_error("failed to overwrite thread context"); - } -#endif -} - -} // namespace MOShared diff --git a/src/shared/inject.h b/src/shared/inject.h deleted file mode 100644 index 7f4f2962..00000000 --- a/src/shared/inject.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#pragma once - -#define WIN32_LEAN_AND_MEAN -#include - -#include - -namespace MOShared { - -void injectDLL(HANDLE processHandle, HANDLE threadHandle, const std::string &dllname, const std::wstring &profileName, int logLevel); - -} -- cgit v1.3.1 From 3d1dfadb4098b3f242bfcad0735cd15e0b07e213 Mon Sep 17 00:00:00 2001 From: Tannin Date: Sat, 4 Jun 2016 15:29:52 +0200 Subject: automatic update of translation file during build --- src/CMakeLists.txt | 4 +- src/organizer_en.ts | 6044 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 6047 insertions(+), 1 deletion(-) create mode 100644 src/organizer_en.ts (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 854f7667..500dc56b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -237,8 +237,10 @@ FIND_PACKAGE(Qt5Declarative REQUIRED) FIND_PACKAGE(Qt5Network REQUIRED) FIND_PACKAGE(Qt5WinExtras REQUIRED) FIND_PACKAGE(Qt5WebKitWidgets REQUIRED) +FIND_PACKAGE(Qt5LinguistTools) QT5_WRAP_UI(organizer_UIHDRS ${organizer_UIS}) QT5_ADD_RESOURCES(organizer_RCCPPS ${organizer_QRCS}) +QT5_CREATE_TRANSLATION(organizer_translations_qm ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/organizer_en.ts) INCLUDE_DIRECTORIES(${Qt5Declarative_INCLUDES}) @@ -295,7 +297,7 @@ ELSE() SET(usvfs_name usvfs_x86) ENDIF() -ADD_EXECUTABLE(ModOrganizer WIN32 ${organizer_HDRS} ${organizer_SRCS} ${organizer_UIHDRS} ${organizer_RCS} ${organizer_RCCPPS}) +ADD_EXECUTABLE(ModOrganizer WIN32 ${organizer_HDRS} ${organizer_SRCS} ${organizer_UIHDRS} ${organizer_RCS} ${organizer_RCCPPS} ${organizer_translations_qm}) TARGET_LINK_LIBRARIES(ModOrganizer Qt5::Widgets Qt5::WinExtras Qt5::WebKitWidgets ${Boost_LIBRARIES} diff --git a/src/organizer_en.ts b/src/organizer_en.ts new file mode 100644 index 00000000..9c74eb3c --- /dev/null +++ b/src/organizer_en.ts @@ -0,0 +1,6044 @@ + + + + + AboutDialog + + + + About + + + + + Revision: + + + + + Used Software + + + + + Thanks + + + + + Translators + + + + + ... more (Can't track) + + + + + Other Supporter + + + + + ogrotten + + + + + Close + + + + + No license + + + + + ActivateModsDialog + + + Activate Mods + + + + + This is a list of esps and esms that were active when the save game was created. + + + + + <!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 is a list of esps and esms that were active when the save game was created.</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;">For each esp, the right column contains the mod (or mods) that can be enabled to make the missing esps/esms available.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></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;">If you hit Ok, all the mods selected in the right columns and all missing esps that have become available will be activated.</span></p></body></html> + + + + + Missing ESP + + + + + Mod + + + + + not found + + + + + BrowserDialog + + + Some Page + + + + + Search + + + + + new + + + + + failed to start download + + + + + CategoriesDialog + + + Categories + + + + + ID + + + + + Internal ID for the category. + + + + + Internal ID for the category. The categories a mod belongs to are stored by this ID. It is recommended you use new IDs for categories you add instead of re-using existing ones. + + + + + Name + + + + + + Name of the Categorie used for display. + + + + + Nexus IDs + + + + + Comma-Separated list of Nexus IDs to be matched to the internal ID. + + + + + <!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;">You can match one or multiple nexus categories to a internal ID. Whenever you download a mod from a Nexus Page, Mod Organizer will try to resolve the category defined on the Nexus to one available in MO.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"></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;">To find out a category id used by the nexus, visit the categories list of the nexus page and hover over the links there.</span></p></body></html> + + + + + Parent ID + + + + + If set, the category is defined as a sub-category of another one. Parent ID needs to be a valid category ID. + + + + + Add + + + + + Remove + + + + + CredentialsDialog + + + Login + + + + + This feature may not work unless you're logged in with Nexus + + + + + Username + + + + + Password + + + + + Remember + + + + + Never ask again + + + + + DirectoryRefresher + + + failed to parse bsa %1: %2 + + + + + failed to read mod (%1): %2 + + + + + DownloadList + + + Name + + + + + Filetime + + + + + Done + + + + + Information missing, please select "Query Info" from the context menu to re-retrieve. + + + + + pending download + + + + + DownloadListWidget + + + + Placeholder + + + + + + + Done - Double Click to install + + + + + + Paused - Double Click to resume + + + + + + Installed - Double Click to re-install + + + + + + Uninstalled - Double Click to re-install + + + + + DownloadListWidgetCompact + + + + Placeholder + + + + + Done + + + + + DownloadListWidgetCompactDelegate + + + < mod %1 file %2 > + + + + + Pending + + + + + Paused + + + + + Fetching Info 1 + + + + + Fetching Info 2 + + + + + Installed + + + + + Uninstalled + + + + + Done + + + + + + + + Are you sure? + + + + + This will remove all finished downloads from this list and from disk. + + + + + This will remove all installed downloads from this list and from disk. + + + + + This will permanently remove all finished downloads from this list (but NOT from disk). + + + + + This will permanently remove all installed downloads from this list (but NOT from disk). + + + + + Install + + + + + Query Info + + + + + Delete + + + + + Un-Hide + + + + + Remove from View + + + + + Cancel + + + + + Pause + + + + + Remove + + + + + Resume + + + + + Delete Installed... + + + + + Delete All... + + + + + Remove Installed... + + + + + Remove All... + + + + + DownloadListWidgetDelegate + + + < mod %1 file %2 > + + + + + Pending + + + + + Fetching Info 1 + + + + + Fetching Info 2 + + + + + + + + Are you sure? + + + + + This will remove all finished downloads from this list and from disk. + + + + + This will remove all installed downloads from this list and from disk. + + + + + This will remove all finished downloads from this list (but NOT from disk). + + + + + This will remove all installed downloads from this list (but NOT from disk). + + + + + Install + + + + + Query Info + + + + + Delete + + + + + Un-Hide + + + + + Remove from View + + + + + Cancel + + + + + Pause + + + + + Remove + + + + + Resume + + + + + Delete Installed... + + + + + Delete All... + + + + + Remove Installed... + + + + + Remove All... + + + + + DownloadManager + + + failed to rename "%1" to "%2" + + + + + Memory allocation error (in refreshing directory). + + + + + Download again? + + + + + A file with the same name has already been downloaded. Do you want to download it again? The new file will receive a different name. + + + + + failed to download %1: could not open output file: %2 + + + + + Wrong Game + + + + + The download link is for a mod for "%1" but this instance of MO has been set up for "%2". + + + + + + remove: invalid download index %1 + + + + + failed to delete %1 + + + + + failed to delete meta file for %1 + + + + + restore: invalid download index: %1 + + + + + cancel: invalid download index %1 + + + + + pause: invalid download index %1 + + + + + resume: invalid download index %1 + + + + + resume (int): invalid download index %1 + + + + + No known download urls. Sorry, this download can't be resumed. + + + + + query: invalid download index %1 + + + + + Please enter the nexus mod id + + + + + Mod ID: + + + + + get pending: invalid download index %1 + + + + + get path: invalid download index %1 + + + + + Main + + + + + Update + + + + + Optional + + + + + Old + + + + + Misc + + + + + Unknown + + + + + display name: invalid download index %1 + + + + + file name: invalid download index %1 + + + + + file time: invalid download index %1 + + + + + file size: invalid download index %1 + + + + + progress: invalid download index %1 + + + + + state: invalid download index %1 + + + + + infocomplete: invalid download index %1 + + + + + mod id: invalid download index %1 + + + + + ishidden: invalid download index %1 + + + + + file info: invalid download index %1 + + + + + mark installed: invalid download index %1 + + + + + mark uninstalled: invalid download index %1 + + + + + Memory allocation error (in processing progress event). + + + + + Memory allocation error (in processing downloaded data). + + + + + Information updated + + + + + + No matching file found on Nexus! Maybe this file is no longer available or it was renamed? + + + + + No file on Nexus matches the selected file by name. Please manually choose the correct one. + + + + + No download server available. Please try again later. + + + + + Failed to request file info from nexus: %1 + + + + + Download failed. Server reported: %1 + + + + + Download failed: %1 (%2) + + + + + failed to re-open %1 + + + + + EditExecutablesDialog + + + Modify Executables + + + + + List of configured executables + + + + + This is a list of your configured executables. Executables in grey are automatically recognised and can not be modified. + + + + + Title + + + + + + Name of the executable. This is only for display purposes. + + + + + Binary + + + + + + Binary to run + + + + + Browse filesystem + + + + + Browse filesystem for the executable to run. + + + + + + ... + + + + + Start in + + + + + Arguments + + + + + + Arguments to pass to the application + + + + + Allow the Steam AppID to be used for this executable to be changed. + + + + + Allow the Steam AppID to be used for this executable to be changed. +Every game/tool distributed through Steam has a unique ID. MO needs to know this ID to start those programs directly, otherwise the program is started by steam and then MO will not work. By default, MO will use the AppID for the game. +Right now the only case I know of where this needs to be overwritten is for the Skyrim Creation Kit which has its own AppID. This overwrite is already preconfigured. + + + + + Overwrite Steam AppID + + + + + Steam AppID to use for this executable that differs from the games AppID. + + + + + Steam AppID to use for this executable that differs from the games AppID. +Every game/tool distributed through Steam has a unique ID. MO needs to know this ID to start those programs directly, otherwise the program is started by steam and then MO will not work. By default, MO will use the AppID for the game (usually 72850). +Right now the only case I know of where this needs to be overwritten is for the Skyrim Creation Kit which has its own AppID (usually 202480). This overwrite is already preconfigured. + + + + + If this is enabled, new files are created in the specified mod instead of the "Overwrite" mod. + + + + + Create Files in Mod instead of Overwrite (*) + + + + + Use Application's Icon for shortcuts + + + + + (*) This setting is profile-specific + + + + + + Add an executable + + + + + + Add + + + + + + Remove the selected executable + + + + + Remove + + + + + Close + + + + + Select a binary + + + + + Executable (%1) + + + + + Java (32-bit) required + + + + + MO requires 32-bit java to run this application. If you already have it installed, select javaw.exe from that installation as the binary. + + + + + Select a directory + + + + + Confirm + + + + + Really remove "%1" from executables? + + + + + Modify + + + + + + Save Changes? + + + + + + You made changes to the current executable, do you want to save them? + + + + + FindDialog + + + Find + + + + + Find what: + + + + + + Search term + + + + + + Find next occurence from current file position. + + + + + &Find Next + + + + + + + Close + + + + + InstallDialog + + + Install Mods + + + + + New Mod + + + + + Name + + + + + Pick a name for the mod + + + + + Pick a name for the mod. This is also used as a directory name, so please don't use characters that are illegal in file names. + + + + + Content + + + + + Content of the archive. You can change the directory structure by using drag&drop. Hint: Also try right clicking... + + + + + <!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 displays the content of the archive. &lt;data&gt; represents the base directory which will map to the game's data directory. You can change the base directory via the right-click context menu and you can move around files via drag&amp;drop</span></p></body></html> + + + + + Placeholder + + + + + OK + + + + + Cancel + + + + + InstallationManager + + + Password required + + + + + Password + + + + + + Extracting files + + + + + failed to create backup + + + + + Mod Name + + + + + Name + + + + + Invalid name + + + + + The name you entered is invalid, please enter a different one. + + + + + File format "%1" not supported + + + + + None of the available installer plugins were able to handle that archive + + + + + no error + + + + + 7z.dll not found + + + + + 7z.dll isn't valid + + + + + archive not found + + + + + failed to open archive + + + + + unsupported archive type + + + + + internal library error + + + + + archive invalid + + + + + unknown archive error + + + + + LockedDialog + + + Locked + + + + + This dialog should disappear automatically if the application/game is done. Click unlock if it didn't. + + + + + MO is locked while the executable is running. + + + + + Unlock + + + + + LogBuffer + + + failed to write log to %1: %2 + + + + + MOApplication + + + an error occured: %1 + + + + + an error occured + + + + + MainWindow + + + + Categories + + + + + Clear + + + + + If checked, only mods that match all selected categories are displayed. + + + + + And + + + + + If checked, all mods that match at least one of the selected categories are displayed. + + + + + Or + + + + + Profile + + + + + Pick a module collection + + + + + <!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;">Create profiles here. Each profile contains its own list of active mods and esps. This way you can quickly switch between setups for different play throughs.</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;">Please note that right now your esp load order is not kept seperate for different profiles.</span></p></body></html> + + + + + Open list options... + + + + + Refresh list. This is usually not necessary unless you modified data outside the program. + + + + + + Restore Backup... + + + + + + Create Backup + + + + + List of available mods. + + + + + This is a list of installed mods. Use the checkboxes to activate/deactivate mods and drag & drop mods to change their "installation" orders. + + + + + Filter + + + + + No groups + + + + + Nexus IDs + + + + + + + Namefilter + + + + + 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> + + + + + 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 + + + + + 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 + + + + + Plugins + + + + + Sort + + + + + List of available esp/esm files + + + + + <!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 list contains the esps and esms contained in the active mods. These require their own load order. Use drag&amp;drop to modify this load order. Please note that MO will only save the load order for mods that are active/checked.<br />There is a great tool named &quot;BOSS&quot; to automatically sort these files.</span></p></body></html> + + + + + Data + + + + + refresh data-directory overview + + + + + Refresh the overview. This may take a moment. + + + + + + + Refresh + + + + + This is an overview of your data directory as visible to the game (and tools). + + + + + File + + + + + Mod + + + + + + Filter the above list so that only conflicts are displayed. + + + + + Show only conflicts + + + + + Saves + + + + + <!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 is a list of all savegames for this game. Hover over a list entry to get detailed information about the save including a list of esps/esms that were used at the time this save was created but aren't active now.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"></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;">If you click &quot;Fix Mods...&quot; in the context menu, MO will try to activate all mods and esps to fix those missing esps. It will not disable anything!</span></p></body></html> + + + + + Downloads + + + + + This is a list of mods you downloaded from Nexus. Double click one to install it. You can also drag an archive into here. + + + + + Show Hidden + + + + + Tool Bar + + + + + Install Mod + + + + + Install &Mod + + + + + Install a new mod from an archive + + + + + Ctrl+M + + + + + Profiles + + + + + &Profiles + + + + + Configure Profiles + + + + + Ctrl+P + + + + + Executables + + + + + &Executables + + + + + Configure the executables that can be started through Mod Organizer + + + + + Ctrl+E + + + + + + Tools + + + + + &Tools + + + + + Ctrl+I + + + + + Settings + + + + + &Settings + + + + + Configure settings and workarounds + + + + + Ctrl+S + + + + + Nexus + + + + + Search nexus network for more mods + + + + + Ctrl+N + + + + + + Update + + + + + Mod Organizer is up-to-date + + + + + + No Problems + + + + + This button will be highlighted if MO discovered potential problems in your setup and provide tips on how to fix them. + +!Work in progress! +Right now this has very limited functionality + + + + + + Help + + + + + Ctrl+H + + + + + Endorse MO + + + + + + Endorse Mod Organizer + + + + + Copy Log to Clipboard + + + + + Ctrl+C + + + + + Change Game + + + + + Open the game selection dialog + + + + + Toolbar + + + + + Desktop + + + + + Start Menu + + + + + Problems + + + + + There are potential problems with your setup + + + + + Everything seems to be in order + + + + + Help on UI + + + + + Documentation Wiki + + + + + Report Issue + + + + + Tutorials + + + + + About + + + + + About Qt + + + + + Name + + + + + Please enter a name for the new profile + + + + + failed to create profile: %1 + + + + + Show tutorial? + + + + + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. + + + + + Downloads in progress + + + + + There are still downloads in progress, do you really want to quit? + + + + + Plugin "%1" failed: %2 + + + + + Plugin "%1" failed + + + + + Browse Mod Page + + + + + Also in: <br> + + + + + No conflict + + + + + <Edit...> + + + + + Activating Network Proxy + + + + + Choose Mod + + + + + Mod Archive + + + + + Start Tutorial? + + + + + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? + + + + + failed to spawn notepad.exe: %1 + + + + + failed to open %1 + + + + + failed to change origin name: %1 + + + + + failed to move "%1" from mod "%2" to "%3": %4 + + + + + <Contains %1> + + + + + <Checked> + + + + + <Unchecked> + + + + + <Update> + + + + + <Managed by MO> + + + + + <Managed outside MO> + + + + + <No category> + + + + + <Conflicted> + + + + + <Not Endorsed> + + + + + failed to rename mod: %1 + + + + + Overwrite? + + + + + This will replace the existing mod "%1". Continue? + + + + + failed to remove mod "%1" + + + + + + + failed to rename "%1" to "%2" + + + + + + + + Confirm + + + + + Remove the following mods?<br><ul>%1</ul> + + + + + failed to remove mod: %1 + + + + + + Failed + + + + + Installation file no longer exists + + + + + Mods installed with old versions of MO can't be reinstalled in this way. + + + + + You need to be logged in with Nexus to resume a download + + + + + + You need to be logged in with Nexus to endorse + + + + + Failed to display overwrite dialog: %1 + + + + + Nexus ID for this Mod is unknown + + + + + Web page for this mod is unknown + + + + + + + Create Mod... + + + + + This will create an empty mod. +Please enter a name: + + + + + + A mod with this name already exists + + + + + This will move all files from overwrite into a new, regular mod. +Please enter a name: + + + + + Not logged in, endorsement information will be wrong + + + + + Continue? + + + + + The versioning scheme decides which version is considered newer than another. +This function will guess the versioning scheme under the assumption that the installed version is outdated. + + + + + + Sorry + + + + + I don't know a versioning scheme where %1 is newer than %2. + + + + + Really enable all visible mods? + + + + + Really disable all visible mods? + + + + + Choose what to export + + + + + Everything + + + + + All installed mods are included in the list + + + + + Active Mods + + + + + Only active (checked) mods from your current profile are included + + + + + Visible + + + + + All mods visible in the mod list are included + + + + + export failed: %1 + + + + + Install Mod... + + + + + Create empty mod + + + + + Enable all visible + + + + + Disable all visible + + + + + Check all for update + + + + + Export to csv... + + + + + All Mods + + + + + Sync to Mods... + + + + + Restore Backup + + + + + Remove Backup... + + + + + Add/Remove Categories + + + + + Replace Categories + + + + + Primary Category + + + + + Change versioning scheme + + + + + Un-ignore update + + + + + Ignore update + + + + + Rename Mod... + + + + + Remove Mod... + + + + + Reinstall Mod + + + + + Un-Endorse + + + + + + Endorse + + + + + Won't endorse + + + + + Endorsement state unknown + + + + + Ignore missing data + + + + + Visit on Nexus + + + + + Visit web page + + + + + Open in explorer + + + + + Information... + + + + + + Exception: + + + + + + Unknown exception + + + + + <All> + + + + + <Multiple> + + + + + %1 more + + + + + Are you sure you want to remove the following %n save(s)?<br><ul>%1</ul><br>Removed saves will be sent to the Recycle Bin. + + + + + + + + Enable Mods... + + + + + Delete %n save(s) + + + + + + + + failed to remove %1 + + + + + failed to create %1 + + + + + Can't change download directory while downloads are in progress! + + + + + failed to write to file %1 + + + + + %1 written + + + + + Select binary + + + + + Binary + + + + + Enter Name + + + + + Please enter a name for the executable + + + + + Not an executable + + + + + This is not a recognized executable. + + + + + + Replace file? + + + + + There already is a hidden version of this file. Replace it? + + + + + + File operation failed + + + + + + Failed to remove "%1". Maybe you lack the required file permissions? + + + + + There already is a visible version of this file. Replace it? + + + + + file not found: %1 + + + + + failed to generate preview for %1 + + + + + Sorry, can't preview anything. This function currently does not support extracting from bsas. + + + + + Update available + + + + + Open/Execute + + + + + Add as Executable + + + + + Preview + + + + + Un-Hide + + + + + Hide + + + + + Write To File... + + + + + Do you want to endorse Mod Organizer on %1 now? + + + + + Thank you! + + + + + Thank you for your endorsement! + + + + + Request to Nexus failed: %1 + + + + + + failed to read %1: %2 + + + + + + Error + + + + + failed to extract %1 (errorcode %2) + + + + + Extract BSA + + + + + This archive contains invalid hashes. Some files may be broken. + + + + + Are you sure? + + + + + This will restart MO, continue? + + + + + Edit Categories... + + + + + Deselect filter + + + + + Remove + + + + + Enable all + + + + + Disable all + + + + + Unlock load order + + + + + Lock load order + + + + + depends on missing "%1" + + + + + incompatible with "%1" + + + + + Please wait while LOOT is running + + + + + loot failed. Exit code was: %1 + + + + + failed to start loot + + + + + failed to run loot: %1 + + + + + Errors occured + + + + + Backup of load order created + + + + + Choose backup to restore + + + + + No Backups + + + + + There are no backups to restore + + + + + + Restore failed + + + + + + Failed to restore the backup. Errorcode: %1 + + + + + Backup of modlist created + + + + + A file with the same name has already been downloaded. What would you like to do? + + + + + Overwrite + + + + + Rename new file + + + + + Ignore file + + + + + MessageDialog + + + + Placeholder + + + + + ModInfo + + + Plugins + + + + + Textures + + + + + Meshes + + + + + BSA + + + + + UI Changes + + + + + Music + + + + + Sound Effects + + + + + Scripts + + + + + SKSE Plugins + + + + + SkyProc Tools + + + + + Strings + + + + + invalid content type %1 + + + + + invalid mod index %1 + + + + + remove: invalid mod index %1 + + + + + ModInfoBackup + + + This is the backup of a mod + + + + + ModInfoDialog + + + Mod Info + + + + + Textfiles + + + + + A list of text-files in the mod directory. + + + + + A list of text-files in the mod directory like readmes. + + + + + + Save + + + + + INI-Files + + + + + Ini Files + + + + + This is a list of .ini files in the mod. + + + + + This is a list of .ini files in the mod. These are usually used to configure the behaviour of mods if there are configurable parameters. + + + + + Ini Tweaks + + + + + This is a list of ini tweaks (ini modifications that can be toggled). + + + + + This is a list of ini tweaks. Ini Tweaks are (usually small) fragments of ini files that are applied over existing settings in skyrim.ini/skyrimprefs.ini. Each tweak can be toggled individually. You should check the description of the mod wether the tweaks are really optional. + + + + + Save changes to the file. + + + + + Save changes to the file. This overwrites the original. There is no automatic backup! + + + + + Images + + + + + Images located in the mod. + + + + + This lists all the images (.jpg and .png) in the mod directory, like screenshots and such. Click one to get a larger view. + + + + + + Optional ESPs + + + + + List of esps and esms that can not be loaded by the game. + + + + + List of esps and esms contained in this plugin that currently can not be loaded by the game. They will not even appear in the esp-list in the main MO-window. +They usually contain optional functionality, see the readme. + +Most mods do not have optional esps, so chances are good you are looking at an empty list. + + + + + Make the selected mod in the lower list unavailable. + + + + + The selected esp (in the lower list) will be pushed into a subdirectory of the mod and will thus become "invisible" to the game. It can then no longer be activated. + + + + + Move a file to the data directory. + + + + + This moves a esp to the esp directory so it can be enabled in the main window. Please note that the ESP merely becomes "available", it will not necessarily be loaded! That is configured in the main window of omo. + + + + + ESPs in the data directory and thus visible to the game. + + + + + These are the mod files that are in the (virtual) data directory of your game and will thus be selecteable in the esp list in the main window. + + + + + Available ESPs + + + + + Conflicts + + + + + The following conflicted files are provided by this mod + + + + + + File + + + + + Overwritten Mods + + + + + The following conflicted files are provided by other mods + + + + + Providing Mod + + + + + Non-Conflicted files + + + + + Categories + + + + + Primary Category + + + + + Nexus Info + + + + + Mod ID + + + + + Mod ID for this mod on Nexus. + + + + + <!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;">Mod ID for this mod on Nexus. This is filled in automatically if you downloaded and installed the mod from inside MO. Otherwise you can enter it manually. To find the correct id, find the mod on nexus. The URL will look like this: </span><a href=" http://www.skyrimnexus.com/downloads/file.php?id=1334"><span style=" font-size:8pt; text-decoration: underline; color:#0000ff;">http://skyrim.nexusmods.com/downloads/file.php?id=1334</span></a><a href="http://www.skyrimnexus.com/downloads/file.php?id=1334"><span style=" font-size:8pt; color:#000000;">. In this example, 1334 is the id you're looking for. Besides: The above is the link to Mod Organizer on the Nexus. Why not go there now and endorse?</span></a></p></body></html> + + + + + <!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;">Installed Version of the Mod. The tooltip will contain the current version available on nexus. The installed version is only set if you installed the mod through MO.</span></p></body></html> + + + + + Version + + + + + Refresh + + + + + Refresh all information from Nexus. + + + + + Description + + + + + about:blank + + + + + Endorse + + + + + Notes + + + + + Filetree + + + + + A directory view of this mod + + + + + <!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 is a modifiable directory view of the mod directory. You can move around files using drag &amp; drop and rename them (double click).</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;">Changes happen immediately on disc, so do</span><span style=" font-size:8pt; font-weight:600;"> be careful</span><span style=" font-size:8pt;">.</span></p></body></html> + + + + + Previous + + + + + Next + + + + + Close + + + + + &Delete + + + + + &Rename + + + + + &Hide + + + + + &Unhide + + + + + &Open + + + + + &New Folder + + + + + + Save changes? + + + + + + Save changes to "%1"? + + + + + File Exists + + + + + A file with that name exists, please enter a new one + + + + + failed to move file + + + + + failed to create directory "optional" + + + + + + Info requested, please wait + + + + + Main + + + + + Update + + + + + Optional + + + + + Old + + + + + Misc + + + + + Unknown + + + + + Current Version: %1 + + + + + No update available + + + + + (description incomplete, please visit nexus) + + + + + <a href="%1">Visit on Nexus</a> + + + + + Failed to delete %1 + + + + + + Confirm + + + + + Are sure you want to delete "%1"? + + + + + Are sure you want to delete the selected files? + + + + + + New Folder + + + + + Failed to create "%1" + + + + + + Replace file? + + + + + There already is a hidden version of this file. Replace it? + + + + + + File operation failed + + + + + + Failed to remove "%1". Maybe you lack the required file permissions? + + + + + + failed to rename %1 to %2 + + + + + There already is a visible version of this file. Replace it? + + + + + Un-Hide + + + + + Hide + + + + + Name + + + + + Please enter a name + + + + + + Error + + + + + Invalid name. Must be a valid file name + + + + + A tweak by that name exists + + + + + Create Tweak + + + + + ModInfoForeign + + + This pseudo mod represents content managed outside MO. It isn't modified by MO. + + + + + ModInfoOverwrite + + + This pseudo mod contains files from the virtual data tree that got modified (i.e. by the construction kit) + + + + + ModInfoRegular + + + + failed to write %1/meta.ini: error %2 + + + + + %1 contains no esp/esm and no asset (textures, meshes, interface, ...) directory + + + + + Categories: <br> + + + + + ModList + + + Game plugins (esp/esm) + + + + + Interface + + + + + Meshes + + + + + BSA + + + + + Music + + + + + Scripts (Papyrus) + + + + + Script Extender Plugin + + + + + SkyProc Patcher + + + + + Sound + + + + + Strings + + + + + Textures + + + + + This entry contains files that have been created inside the virtual data tree (i.e. by the construction kit) + + + + + Backup + + + + + No valid game data + + + + + Not endorsed yet + + + + + Overwrites files + + + + + Overwritten files + + + + + Overwrites & Overwritten + + + + + Redundant + + + + + Non-MO + + + + + invalid + + + + + installed version: "%1", newest version: "%2" + + + + + The newest version on Nexus seems to be older than the one you have installed. This could either mean the version you have has been withdrawn (i.e. due to a bug) or the author uses a non-standard versioning scheme and that newest version is actually newer. Either way you may want to "upgrade". + + + + + Categories: <br> + + + + + Invalid name + + + + + drag&drop failed: %1 + + + + + Confirm + + + + + Are you sure you want to remove "%1"? + + + + + Flags + + + + + Content + + + + + Mod Name + + + + + Version + + + + + Priority + + + + + Category + + + + + Nexus ID + + + + + Installation + + + + + + unknown + + + + + Name of your mods + + + + + Version of the mod (if available) + + + + + Installation priority of your mod. The higher, the more "important" it is and thus overwrites files from mods with lower priority. + + + + + Category of the mod. + + + + + Id of the mod as used on Nexus. + + + + + Emblemes to highlight things that might require attention. + + + + + Depicts the content of the mod:<br><table cellspacing=7><tr><td><img src=":/MO/gui/content/plugin" width=32/></td><td>Game plugins (esp/esm)</tr><tr><td><img src=":/MO/gui/content/interface" width=32/></td><td>Interface</tr><tr><td><img src=":/MO/gui/content/mesh" width=32/></td><td>Meshes</tr><tr><td><img src=":/MO/gui/content/bsa" width=32/></td><td>BSA</tr><tr><td><img src=":/MO/gui/content/texture" width=32/></td><td>Textures</tr><tr><td><img src=":/MO/gui/content/sound" width=32/></td><td>Sounds</tr><tr><td><img src=":/MO/gui/content/music" width=32/></td><td>Music</tr><tr><td><img src=":/MO/gui/content/string" width=32/></td><td>Strings</tr><tr><td><img src=":/MO/gui/content/script" width=32/></td><td>Scripts (Papyrus)</tr><tr><td><img src=":/MO/gui/content/skse" width=32/></td><td>Script Extender plugins</tr><tr><td><img src=":/MO/gui/content/skyproc" width=32/></td><td>SkyProc Patcher</tr></table> + + + + + Time this mod was installed + + + + + ModListSortProxy + + + Drag&Drop is only supported when sorting by priority + + + + + MotDDialog + + + Message of the Day + + + + + OK + + + + + MyFileSystemModel + + + Overwrites + + + + + not implemented + + + + + NXMAccessManager + + + Verifying Nexus login + + + + + Logging into Nexus + + + + + timeout + + + + + Unknown error + + + + + Please check your password + + + + + NexusInterface + + + Failed to guess mod id for "%1", please pick the correct one + + + + + empty response + + + + + invalid response + + + + + OrganizerCore + + + + Failed to write settings + + + + + An error occured trying to update MO settings to %1: %2 + + + + + File is write protected + + + + + Invalid file format (probably a bug) + + + + + Unknown error %1 + + + + + An error occured trying to write back MO settings to %1: %2 + + + + + + Download started + + + + + Download failed + + + + + + Installation successful + + + + + + Configure Mod + + + + + + This mod contains ini tweaks. Do you want to configure them now? + + + + + + mod "%1" not found + + + + + + Installation cancelled + + + + + + The mod was not installed completely. + + + + + Executable "%1" not found + + + + + Start Steam? + + + + + Steam is required to be running already to correctly start the game. Should MO try to start steam now? + + + + + Error + + + + + No profile set + + + + + Failed to refresh list of esps: %1 + + + + + Multiple esps activated, please check that they don't conflict. + + + + + Download? + + + + + A download has been started but no installed page plugin recognizes it. +If you download anyway no information (i.e. version) will be associated with the download. +Continue? + + + + + failed to update mod list: %1 + + + + + + login successful + + + + + Login failed + + + + + Login failed, try again? + + + + + login failed: %1. Download will not be associated with an account + + + + + login failed: %1 + + + + + login failed: %1. You need to log-in with Nexus to update MO. + + + + + Too many esps and esms enabled + + + + + + Description missing + + + + + The game doesn't allow more than 255 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or merge some plugins into one. You can find a guide here: <a href="http://wiki.step-project.com/Guide:Merging_Plugins">http://wiki.step-project.com/Guide:Merging_Plugins</a> + + + + + failed to save load order: %1 + + + + + The designated write target "%1" is not enabled. + + + + + OverwriteInfoDialog + + + Overwrite + + + + + You can use drag&drop to move files and directories to regular mods. + + + + + &Delete + + + + + &Rename + + + + + &Open + + + + + &New Folder + + + + + %1 not found + + + + + Failed to delete "%1" + + + + + + Confirm + + + + + Are sure you want to delete "%1"? + + + + + Are sure you want to delete the selected files? + + + + + + New Folder + + + + + Failed to create "%1" + + + + + PluginContainer + + + Some plugins could not be loaded + + + + + + Description missing + + + + + The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version: + + + + + PluginList + + + Name + + + + + Priority + + + + + Mod Index + + + + + Flags + + + + + + unknown + + + + + Name of your mods + + + + + Load priority of your mod. The higher, the more "important" it is and thus overwrites data from plugins with lower priority. + + + + + The modindex determins the formids of objects originating from this mods. + + + + + failed to update esp info for file %1 (source id: %2), error: %3 + + + + + esp not found: %1 + + + + + + Confirm + + + + + Really enable all plugins? + + + + + Really disable all plugins? + + + + + The file containing locked plugin indices is broken + + + + + This plugin can't be disabled (enforced by the game) + + + + + <b>Origin</b>: %1 + + + + + Author + + + + + Description + + + + + Missing Masters + + + + + Enabled Masters + + + + + failed to restore load order for %1 + + + + + PluginListSortProxy + + + Drag&Drop is only supported when sorting by priority or mod index + + + + + PreviewDialog + + + Preview + + + + + Close + + + + + ProblemsDialog + + + Problems + + + + + <!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="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:7.8pt;"><br /></p></body></html> + + + + + Close + + + + + + Fix + + + + + No guided fix + + + + + Profile + + + invalid profile name %1 + + + + + failed to create %1 + + + + + failed to write mod list: %1 + + + + + failed to update tweaked ini file, wrong settings may be used: %1 + + + + + failed to create tweaked ini: %1 + + + + + "%1" is missing or inaccessible + + + + + + + + + invalid index %1 + + + + + Overwrite directory couldn't be parsed + + + + + invalid priority %1 + + + + + Delete savegames? + + + + + Do you want to delete local savegames? (If you select "No", the save games will show up again if you re-enable local savegames) + + + + + ProfileInputDialog + + + Dialog + + + + + Please enter a name for the new profile + + + + + If checked, the new profile will use the default game settings. + + + + + If checked, the new profile will use the default game settings instead of the "global" settings. Global settings are the settings you configure when running the game launcher directly, without MO. + + + + + Default Game Settings + + + + + ProfilesDialog + + + Profiles + + + + + List of Profiles + + + + + <!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;">This is the list of profiles. Each Profile contains its own list and installation order of enabled mods (from a shared pool), a configuration of enabled esps/esms, a copy of the games ini-file and an optional savegame filter.</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"></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-weight:600;">Note</span> For technical reasons it's currently not possible to have seperate load-orders for esps. This means you can't load moda.esp before modb.esp in one profile and the other way around in another.</p></body></html> + + + + + + If checked, savegames are local to this profile and will not appear when starting with a different profile. + + + + + Local Savegames + + + + + Local Game Settings + + + + + This ensures data files from mods are actually used. You want to enable this unless you use a different tool for Archive Invalidation. + + + + + <!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;">The games Oblivion, Fallout 3 and Fallout NV contain a bug which prevents texture and mesh replacers (that is: all modifications to meshes and textures already in game) from working.</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;">The Mod Organizer uses a workaround called &quot;BSA redirection&quot; (google is your friend) to fix this issue reliably and without further work. Simply activate and forget.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"></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;">With Skyrim this bug seems to be fixed to a degree but whether a mod becomes active still depends on file dates. Therefore, it still makes sense to activate this.</span></p></body></html> + + + + + Automatic Archive Invalidation + + + + + + Create a new profile from scratch + + + + + Create + + + + + Clone the selected profile + + + + + This creates a new profile with the same settings and active mods as the selected one. + + + + + Copy + + + + + + Delete the selected Profile. This can not be un-done! + + + + + Remove + + + + + Rename + + + + + + Transfer save games to the selected profile. + + + + + Transfer Saves + + + + + Close + + + + + Archive invalidation isn't required for this game. + + + + + + failed to create profile: %1 + + + + + Name + + + + + Please enter a name for the new profile + + + + + failed to copy profile: %1 + + + + + Invalid name + + + + + Invalid profile name + + + + + Confirm + + + + + Are you sure you want to remove this profile (including local savegames if any)? + + + + + Profile broken + + + + + This profile you're about to delete seems to be broken or the path is invalid. I'm about to delete the following folder: "%1". Proceed? + + + + + Rename Profile + + + + + New Name + + + + + failed to change archive invalidation state: %1 + + + + + failed to determine if invalidation is active: %1 + + + + + QObject + + + Failed to save custom categories + + + + + + + + invalid index %1 + + + + + invalid category id %1 + + + + + invalid field name "%1" + + + + + invalid type for "%1" (should be integer) + + + + + invalid type for "%1" (should be string) + + + + + invalid type for "%1" (should be float) + + + + + no fields set up yet! + + + + + field not set "%1" + + + + + invalid character in field "%1" + + + + + empty field name + + + + + invalid game type %1 + + + + + helper failed + + + + + failed to determine account name + + + + + + invalid 7-zip32.dll: %1 + + + + + Enter Instance Name + + + + + Name + + + + + + Canceled + + + + + Choose Instance + + + + + Each Instance is a full set of MO data files (mods, downloads, profiles, configuration, ...). Use multiple instances for different games. If your MO folder is writable, you can also store a single instance locally (called a portable install). + + + + + New + + + + + Create a new instance. + + + + + Portable + + + + + Use MO folder for data. + + + + + + failed to create %1 + + + + + Data directory created + + + + + New data directory created at %1. If you don't want to store a lot of data there, reconfigure the storage directories via settings. + + + + + failed to open %1: %2 + + + + + + %1 not found + + + + + + game doesn't support a script extender + + + + + Failed to deactivate script extender loading + + + + + Failed to remove %1: %2 + + + + + + Failed to rename %1 to %2 + + + + + Failed to deactivate proxy-dll loading + + + + + Failed to set up script extender loading + + + + + Failed to delete old proxy-dll %1 + + + + + + Failed to copy %1 to %2 + + + + + Failed to overwrite %1 + + + + + Failed to set up proxy-dll loading + + + + + Permissions required + + + + + The current user account doesn't have the required access rights to run Mod Organizer. The neccessary changes can be made automatically (the MO directory will be made writable for the current user account). You will be asked to run "helper.exe" with administrative rights. + + + + + + Woops + + + + + ModOrganizer has crashed! Should a diagnostic file be created? If you send me this file (%1) to sherb@gmx.net, the bug is a lot more likely to be fixed. Please include a short description of what you were doing when the crash happened + + + + + ModOrganizer has crashed! Unfortunately I was not able to write a diagnostic file: %1 + + + + + Plugin to handle %1 no longer installed + + + + + Could not use configuration settings for game "%1", path "%2". + + + + + + Please select the game to manage + + + + + No game identified in "%1". The directory is required to contain the game binary and its launcher. + + + + + Please select the game edition you have (MO can't start the game correctly if this is set incorrectly!) + + + + + failed to start application: %1 + + + + + + Mod Organizer + + + + + An instance of Mod Organizer is already running + + + + + Failed to set up instance + + + + + Please use "Help" from the toolbar to get usage instructions to all elements + + + + + + <Manage...> + + + + + failed to parse profile %1: %2 + + + + + Failed to start "%1" + + + + + Waiting + + + + + Please press OK once you're logged into steam. + + + + + failed to initialize plugin %1: %2 + + + + + Plugin error + + + + + It appears the plugin "%1" failed to load last startup and caused MO to crash. Do you want to disable it? +(Please note: If this is the first time you see this message for this plugin you may want to give it another try. The plugin may be able to recover from the problem) + + + + + failed to access %1 + + + + + failed to set file time %1 + + + + + Before you can use ModOrganizer, you need to create at least one profile. ATTENTION: Run the game at least once before creating a profile! + + + + + Script Extender + + + + + Proxy DLL + + + + + failed to spawn "%1" + + + + + Elevation required + + + + + This process requires elevation to run. +This is a potential security risk so I highly advice you to investigate if +"%1" +can be installed to work without elevation. + +Start elevated anyway? (you will be asked if you want to allow ModOrganizer.exe to make changes to the system) + + + + + failed to spawn "%1": %2 + + + + + QueryOverwriteDialog + + + Mod Exists + + + + + This mod seems to be installed already. Do you want to add files from this archive (overwriting existing ones) or do you want to completely replace the existing files (old files are deleted)? Alternatively you can install this mod under a different name. + + + + + Keep Backup + + + + + Merge + + + + + Replace + + + + + Rename + + + + + Cancel + + + + + SaveTextAsDialog + + + Dialog + + + + + Copy To Clipboard + + + + + Save As... + + + + + Close + + + + + Save CSV + + + + + Text Files + + + + + failed to open "%1" for writing + + + + + SelectionDialog + + + Select + + + + + Placeholder + + + + + Cancel + + + + + SelfUpdater + + + archive.dll not loaded: "%1" + + + + + New update available (%1) + + + + + Install + + + + + Download failed + + + + + Failed to find correct download, please try again later. + + + + + Update + + + + + Download in progress + + + + + Download failed: %1 + + + + + Failed to install update: %1 + + + + + Failed to start %1: %2 + + + + + Error + + + + + Settings + + + Failed + + + + + Sorry, failed to start the helper application + + + + + + attempt to store setting for unknown plugin "%1" + + + + + SettingsDialog + + + Settings + + + + + General + + + + + Language + + + + + The display language + + + + + <!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;">The display language. This will only displaye languages for which you have a translation installed.</span></p></body></html> + + + + + Style + + + + + graphical style + + + + + graphical style of the MO user interface + + + + + Log Level + + + + + Decides the amount of data printed to "ModOrganizer.log" + + + + + Decides the amount of data printed to "ModOrganizer.log". +"Debug" produces very useful information for finding problems. There is usually no noteworthy performance impact but the file may become rather large. If this is a problem you may prefer the "Info" level for regluar use. On the "Error" level the log file usually remains empty. + + + + + Debug + + + + + Info + + + + + Error + + + + + Update to non-stable releases. + + + + + If this is enabled, the integrated update mechanism will notify of all releases, including pre-releases (alphas, betas). + +Please use this only if you're sufficiently tech-savvy to investigate issues, look for known problems in the issue tracker and create meaningful reports. + +If you use pre-releases, never contact me directly by e-mail or via private messages! + + + + + Install Pre-releases (Betas) + + + + + User interface + + + + + If checked, the download interface will be more compact. + + + + + Compact Download Interface + + + + + If checked, the download list will display meta information instead of file names. + + + + + Download Meta Information + + + + + Reset stored information from dialogs. + + + + + This will make all dialogs show up again where you checked the "Remember selection"-box. + + + + + Reset Dialogs + + + + + + Modify the categories available to arrange your mods. + + + + + Configure Mod Categories + + + + + Paths + + + + + Base Directory + + + + + + + ... + + + + + Overwrite + + + + + Caches + + + + + Downloads + + + + + Directory where mods are stored. + + + + + Directory where mods are stored. Please note that changing this will break all associations of profiles with mods that don't exist in the new location (with the same name). + + + + + + Directory where downloads are stored. + + + + + Mods + + + + + Profiles + + + + + Important: All directories have to be writeable! + + + + + + Nexus + + + + + Allows automatic log-in when the Nexus-Page for the game is clicked. + + + + + <!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;">Allows automatic log-in when the Nexus-Page for the game is clicked. Please note that the obfuscation with which the password is stored in modorganizer.ini is not very strong. If you're worried someone might steal your password, don't store it here.</span></p></body></html> + + + + + If checked and if correct credentials are entered below, log-in to Nexus (for browsing and downloading) is automatic. + + + + + Automatically Log-In to Nexus + + + + + + Username + + + + + + Password + + + + + Remove cache and cookies. Forces a new login. + + + + + Clear Cache + + + + + Disable automatic internet features + + + + + Disable automatic internet features. This does not affect features that are explicitly invoked by the user (like checking mods for updates, endorsing, opening the web browser) + + + + + Offline Mode + + + + + Use a proxy for network connections. + + + + + Use a proxy for network connections. This uses the system-wide settings which can be configured in Internet Explorer. Please note that MO will start up a few seconds slower on some systems when using a proxy. + + + + + Use HTTP Proxy (Uses System Settings) + + + + + Associate with "Download with manager" links + + + + + Known Servers (updated on download) + + + + + Preferred Servers (Drag & Drop) + + + + + Steam + + + + + If you save your steam user ID and password here, they will be used when logging into steam. Note, however, your password will be stored unencrypted, so make sure your computer is secure. + + + + + Plugins + + + + + Author: + + + + + Version: + + + + + Description: + + + + + Key + + + + + Value + + + + + Blacklisted Plugins (use <del> to remove): + + + + + Workarounds + + + + + Steam App ID + + + + + The Steam AppID for your game + + + + + <!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;">The Steam App ID is required to directly start some games. For Skyrim, if this is not set or wrong, the &quot;Mod Organizer&quot; load mechanism may not work properly.</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;">The preset for this is the App ID of the &quot;regular&quot; version so in most cases, you should be set.</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;">If you think you have a different version (GotY or something), follow these steps to get to the id:</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;">1. Navigate to the game library in steam</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;">2. right-click on the game you need the id for and choose </span><span style=" font-size:8pt; font-weight:600;">Create desktop shortcut</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;">3. right-click on the newly created shortcut on your desktop and select </span><span style=" font-size:8pt; font-weight:600;">Properties</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;">4. in the URL-field you should see something like this: </span><span style=" font-size:8pt; font-style:italic;">steam://rungameid/22380</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;">22380 is the id you're looking for.</span></p></body></html> + + + + + Load Mechanism + + + + + Select loading mechanism. See help for details. + + + + + Mod Organizer needs a dll to be injected into the game so all mods are visible to it. +There are several means to do this: +*Mod Organizer* (default) In this mode the Mod Organizer itself injects the dll. The disadvantage is that you always have to start the game through MO or a link created by it. +*Script Extender* In this mode, MO is installed as a Script Extender (obse, fose, nvse, skse) plugin. +*Proxy DLL* In this mode, MO replaces one of the game's dlls with one that loads MO (and the original dll of course). This will ONLY work with Steam games and it has only been tested with Skyrim. Please use this only if the other mechanisms don't work. + +If you use the Steam version of Oblivion the default will NOT work. In this case, please install obse and use "Script Extender" as the load mechanism. Also you can then not start Oblivion from MO. Instead, use MO only to set up your mods, then quit and start Oblivion through Steam. + + + + + NMM Version + + + + + The Version of Nexus Mod Manager to impersonate. + + + + + Mod Organizer uses an API provided by the Nexus to provide features like checking for updates and downloading files. Unfortunately this API has not been made available officially to third party tools like MO so we have to impersonate the Nexus Mod Manager to be allowed in. +On top of this Nexus has used the client identification to lock out outdated versions of NMM to force users to update. This means that MO also needs to impersonate the new version of NMM even if MO doesn't need an update. Therefore you can configure the version to identify as here. +Please note that MO does identify itself as MO to the webserver, it's not lying about what it is. It is merely adding a "compatible" NMM version to the user agent. + +tl;dr-version: If Nexus-features don't work, insert the current version number of NMM here and try again. + + + + + Enforces that inactive ESPs and ESMs are never loaded. + + + + + It seems that the Games occasionally load ESP or ESM files even if they haven't been activated as plugins. +I don't yet know what the circumstances are, but user reports imply it is in some cases unwanted. If this is checked, ESPs and ESMs not checked in the List are invisible to the game and can not be loaded. + + + + + Hide inactive ESPs/ESMs + + + + + If checked, files (i.e. esps, esms and bsas) belonging to the core game can not be disabled in the UI. (default: on) + + + + + If checked, files (i.e. esps, esms and bsas) belonging to the core game can not be disabled in the UI. (default: on) +Uncheck this if you want to use Mod Organizer with total conversions (like Nehrim) but be aware that the game will crash if required files are not enabled. + + + + + Force-enable game files + + + + + Disable this to no longer display mods installed outside MO in the mod list (left pane). Assets from those mods will then be treated as having lowest mod priority together with the original game content. + + + + + By default Mod Organizer will display esp+bsa bundles installed with foreign tools as mods (left pane). This allows you to control their priority in relation to other mods. This is particularly useful if you also use Steam Workshop to install mods. +However, if you installed loose file mods outside MO which conflict with BSAs also installed outside MO those conflicts can't be resolved correctly. + +If you disable this feature, MO will only display official DLCs this way. Please note that plugins (esps and esms) displayed in the right pane are completely unaffected by this feature. + + + + + Display mods installed outside MO + + + + + + For Skyrim, this can be used instead of Archive Invalidation. It should make AI redundant for all Profiles. +For the other games this is not a sufficient replacement for AI! + + + + + Back-date BSAs + + + + + These are workarounds for problems with Mod Organizer. Please make sure you read the help text before changing anything here. + + + + + Confirm + + + + + Changing the mod directory affects all your profiles! Mods not present (or named differently) in the new location will be disabled in all profiles. There is no way to undo this unless you backed up your profiles manually. Proceed? + + + + + Select base directory + + + + + Select download directory + + + + + Select mod directory + + + + + Select cache directory + + + + + Select profiles directory + + + + + Select overwrite directory + + + + + Confirm? + + + + + This will make all dialogs show up again where you checked the "Remember selection"-box. Continue? + + + + + SimpleInstallDialog + + + Quick Install + + + + + Name + + + + + + Opens a Dialog that allows custom modifications. + + + + + Manual + + + + + OK + + + + + Cancel + + + + + SingleInstance + + + SHM error: %1 + + + + + failed to connect to running instance: %1 + + + + + failed to communicate with running instance: %1 + + + + + failed to receive data from secondary instance: %1 + + + + + SyncOverwriteDialog + + + Sync Overwrite + + + + + Name + + + + + Sync To + + + + + <don't sync> + + + + + failed to remove %1 + + + + + failed to move %1 to %2 + + + + + TransferSavesDialog + + + Transfer Savegames + + + + + Global Characters + + + + + This is a list of characters in the global location. + + + + + This is a list of characters in the global location. + +On Windows Vista/Windows 7: + C:\Users\[UserName]\Documents\My Games\Skyrim\Saves + +On Windows XP: + C:\Documents and Settings\[UserName]\My Documents\My Games\Skyrim\Saves + + + + + + This is a list of save games for the selected character in the global location. + +On Windows Vista/Windows 7: + C:\Users\[UserName]\Documents\My Games\Skyrim\Saves + +On Windows XP: + C:\Documents and Settings[UserName]\My Documents\My Games\Skyrim\Saves + + + + + + + Move -> + + + + + Copy -> + + + + + <- Move + + + + + <- Copy + + + + + Done + + + + + Profile Characters + + + + + Characters for profile %1 + + + + + Overwrite + + + + + Overwrite the file "%1" + + + + + Confirm + + + + + UsvfsConnector + + + Preparing vfs + + + + + tutorial_conflictresolution_main + + + There are multiple types of conflicts you may encounter when dealing with Mods. This tutorial will try to cover and explain how to deal with all of them. + + + + + First up are file conflicts. These occur when two mods contain the same file. Most commonly this happens when several mods replace the same standard asset from the game, like the texture of an armor. + + + + + As an example, say you install "ModA" which contains stylish new iron and leather armor. Then you install "ModB" which contains sexy ebony and leather armor. Obviously there is a conflict now: which leather armor to use? + + + + + If you were to install the mods manually, when installing "ModB" you would be asked if you want to overwrite conflicting files. If you choose yes, you get the leather armor from "ModB" otherwise you keep the one from "ModA". If you later decide you made the wrong choice, you have to reinstall one of the mods. + + + + + With MO, both ModA and ModB get installed completely (no overwrite dialog) and by default "ModB" gets to provide the leather armor because it's automatically assigned the higher priority. + + + + + However, you can change the mod priority at any time using drag&drop on this list. If you assign "ModA" a higher priority, it provides the leather armor, no re-installation required. Since the priorities of mods in this list are treated as if the mods were installed in that order, I tend to talk about "installation order". + + + + + If the "Flags"-column is enabled in the mod list, it will show you which mods are involved in a conflict and how... + + + + + <img src=":/MO/gui/emblem_conflict_overwrite" /> indicates that the mod overwrites files that are also available in another mod. + + + + + <img src=":/MO/gui/emblem_conflict_overwritten" /> indicates that the mod is <b>partially</b> overwritten by another. + + + + + <img src=":/MO/gui/emblem_conflict_mixed" /> indicates that both of the above is true. + + + + + <img src=":/MO/gui/emblem_conflict_redundant" /> indicates that the mod is completely overwrtten by another. You could as well disable it. + + + + + There are two ways to see the individual files involved in a conflict: + + + + + Option A: Switch to the "Data"-tab if necessary + + + + + ... here, if you mark the highlighted control, the tree will only display files in conflict. In the right column, it says which mod currently provides the mod (because it has highest priority) and if you hover your mouse over that info, it will list which other mods contains it. + + + + + Option B: Open the <i>Information</i>-Dialog of an <b>enabled</b> mod you're interested in by either double-clicking it or selecting <i>Information...</i> from the right-click menu + + + + + This was everything to know about file conflicts. The second type of conflict we have to deal with are "record conflicts". + + + + + I told you in the "First Steps" tutorial how the esp/esm plugins contain changes to the game world like modifications to the terrain or existing NPCs. Each change like this is stored in a record, hence the name "record conflict". For example when two mods try to change the same location, only one change can become active. + + + + + As with file conflicts you can't really fix these conflicts, you have to choose which change you want. This time around however, if you choose wrong, your game may become unstable because there may be dependencies between the records of a mod. + + + + + Please open the "ESPs"-tab... + + + + + Again you can use drag&drop to change priorities of plugins, thus deciding which plugin takes precedence in regards to conflicts. This is commonly called the "load order". But how do you know how to order the plugins? + + + + + Unlike with file conflicts, MO does not provide help on finding conflicts. The good news is, there already is a perfect tool for that called BOSS. BOSS is available on the Nexus and integrates neatly with MO. Basically, if you don't have BOSS yet, install it once this tutorial is over. + + + + + After you installed BOSS in the default location (follow its instructions), start MO again and BOSS should automatically appear as an Executable... + + + + + When you run BOSS, it will automatically re-organize plugins for best compatibility (overwriting your manual changes). It will also open a report in your browser that warns about incompatibilities. You should read the report, at least for new mods. + + + + + The final type of conflicts are also "record conflicts". Like the previous type. It's confusing, so I'll just call them "lists conflicts" instead. The difference is the types of records in conflict. The ones in question here can be merged so you may be able to get all modifications in your game. + + + + + One common example of such records are leveled lists that contain all the items that may spawn at a specific character level. Traditionally, if multiple mods add items to such a list, only one is in effect... + + + + + ... but there are tools to merge those mods so you can have the effects of all of them. Again, this functionality is not integrated with MO because there are already great tools. For Oblivion and Skyrim look for wrye bash, for fallout 3/nv it's wrye flash. For Skyrim there is also "SkyBash". All of these can create a so-called "bashed patch" which is a plugin that contains the combined mergeable records from all your mods. + + + + + This completes the tutorial. + + + + + tutorial_conflictresolution_modinfo + + + Please switch to the "Conflicts"-Tab. + + + + + Here you can see a list of files in this mod that out-prioritize others in a file conflict and one with files where this mod is overridden. + + + + + Please close the information dialog again. + + + + + tutorial_firststeps_browser + + + This is a fully featured browser that is set up to open the correct nexus page for your game. You can download any mod using the "DOWNLOAD WITH MANAGER"-button or the "manual"-link and it will be downloaded by MO. + + + + + tutorial_firststeps_main + + + Welcome to the ModOrganizer Tutorial! This will guide you through the most common features of MO. +Please go along with the tutorial because some things can't be demonstrated if you skip something. + + + + + Before we continue with the step-by-step tutorial, I'd like to tell you about the other ways you can receive help on ModOrganizer. + + + + + The highlighted button provides hints on solving problems MO recognized automatically. + + + + + +There IS a problem now but you may want to hold off on fixing it until after completing the tutorial. + + + + + This button provides multiple sources of information and further tutorials. + + + + + Finally there are tooltips on almost every part of Mod Organizer. If there is a control in MO you don't understand, please try hovering over it to get a short description or use "Help on UI" from the help menu to get a longer explanation + + + + + This list displays all mods installed through MO. It also displays installed DLCs and some mods installed outside MO but you have limited control over those in MO. + + + + + Before we start installing mods, let's have a quick look at the settings. + + + + + 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 + + + + + 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". + + + + + You can also install mods from disk using the "Install Mod" button. + + + + + Downloads will appear on the "Downloads"-tab here. You have to download and install at least one mod to proceed. + + + + + Great, you just installed your first mod. Please note that the installation procedure may differ based on how a mod was packaged. + + + + + Now you know all about downloading and installing mods but they are not enabled yet... + + + + + Install a few more mods if you want, then enable mods by checking them in the left pane. Mods that aren't enabled have no effect on the game whatsoever. + + + + + For some mods, enabling it on the left pane is all you have to do... + + + + + ...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 "Plugins"-tab to get a list of plugins. + + + + + You will notice some plugins are grayed out. These are part of the main game and can't be disabled. + + + + + A single mod may contain zero, one or multiple esps. Some or all may be optional. If in doubt, please consult the documentation of the indiviual mod. To do so, right-click the mod and select "Information". + + + + + Now you know how to download, install and enable mods. +It's important you always start the game from inside MO, otherwise the mods you installed here won't work. + + + + + This combobox lets you choose <i>what</i> to start. This way you can start the game, Launcher, Script Extender, the Creation Kit or other tools. If you miss a tool you can also configure this list but that is an advanced topic. + + + + + This completes the basic tutorial. As homework go play a bit! After you have installed more mods you may want to read the tutorial on conflict resolution. + + + + + tutorial_firststeps_modinfo + + + This dialog tries to expose as much information about a mod as possible. Depending on the mod this may include readmes, screenshots, optional plugins and so on. If a certain type of information was not found in a mod, the corresponding tab is grayed out. + + + + + If you installed the mod from Nexus, the corresponding tab should give you direct access to the mod page. + + + + + We may re-visit this screen in later tutorials. + + + + + tutorial_firststeps_settings + + + You can use your regular browser to download from Nexus. +Please open the "Nexus"-tab + + + + + Click this button so that "DOWNLOAD WITH MANAGER"-buttons are download with Mod Organizer. + + + + + You can also store your Nexus-credentials here for automatic login. The password is stored unencrypted on your disk! + + + + + tutorial_primer_main + + + This window shows all the mods that are installed. The column headers can be used for sorting. Only checked mods are active in the current profile. + + + + + Each profile is a separate set of enabled mods and ini settings. + + + + + The dropdown allows various ways of grouping the mods shown in the mod list. + + + + + Show/hide the category pane. + + + + + Quickly filter the mod list as you type. + + + + + Switch between information views. + + + + + This shows mod categories and some meta categories (in angle-brackets). Select some to filter the mod list. For example select "<Checked>" to show only active mods. + + + + + Customizable list for choosing the program to run. + + + + + When this button is clicked, Mod Organizer creates a virtual directory structure then runs the program selected to the left. + + + + + Will create a shortcut for quick access. The shortcut can be placed in the toolbar at the top, in the Start Menu or on the Windows Desktop. + + + + + Log messages produced by MO. Please note that messages with a light bulb usually don't require your attention. + + + + + Configure Mod Organizer. + + + + + Reports potential Problems about the current setup. + + + + + Activates if there is an update for MO. Please note that if, for any reason, MO can't communicate with NMM, this will not work either. + + + + + Plugins (esp/esm files) of the mods in the current profile. They need to be checked to be loaded. + + + + + Automatically sort plugins using the bundled LOOT application. + + + + + Quickly filter plugin list as you type. + + + + + All the asset archives (.bsa files) for all active mods. + + + + + The directory tree and all files that the program will see. + + + + + Save game browser. Shows all the saves for the current profile and whether or not the current mod-load status is correct. + + + + + Shows the mods that have been downloaded and if they’ve been installed. + + + + + Click to quit + + + + + tutorial_window_installer + + + This mod has been packaged in a way that Mod Organizer did not automatically recognize... + + + + + You can use drag&drop on this list to fix the structure of the mod. + + + + + The correct structure replicates the data-directory of the game. That means esps, the "meshes"- or "textures"-directory and so on should be directly below "<data>". + + + + + You can also disable files and directories that you don't want to unpack. + + + + + From the context menu (right-click) you can open textfiles, in case you want to access a readme. + + + + + This text will turn green if MO thinks the structure looks good. + + + + -- cgit v1.3.1 From 99156abc6e0779ed3de437c0005b6de8293c7a15 Mon Sep 17 00:00:00 2001 From: LePresidente Date: Wed, 4 Jan 2017 14:22:18 +0200 Subject: First attempt to update to QT5.7 --- src/CMakeLists.txt | 8 ++++---- src/browserdialog.cpp | 32 ++++++++++++++++---------------- src/browserdialog.h | 2 +- src/browserview.cpp | 35 ++++++++++++++++++----------------- src/browserview.h | 8 ++++---- src/modinfodialog.cpp | 3 ++- src/modinfodialog.ui | 6 +++--- 7 files changed, 48 insertions(+), 46 deletions(-) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 500dc56b..0d330d72 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -233,10 +233,10 @@ SET(CMAKE_INCLUDE_CURRENT_DIR ON) SET(CMAKE_AUTOMOC ON) SET(CMAKE_AUTOUIC ON) FIND_PACKAGE(Qt5Widgets REQUIRED) -FIND_PACKAGE(Qt5Declarative REQUIRED) +FIND_PACKAGE(Qt5QuickWidgets REQUIRED) FIND_PACKAGE(Qt5Network REQUIRED) FIND_PACKAGE(Qt5WinExtras REQUIRED) -FIND_PACKAGE(Qt5WebKitWidgets REQUIRED) +FIND_PACKAGE(Qt5WebEngineWidgets REQUIRED) FIND_PACKAGE(Qt5LinguistTools) QT5_WRAP_UI(organizer_UIHDRS ${organizer_UIS}) QT5_ADD_RESOURCES(organizer_RCCPPS ${organizer_QRCS}) @@ -299,7 +299,7 @@ ENDIF() ADD_EXECUTABLE(ModOrganizer WIN32 ${organizer_HDRS} ${organizer_SRCS} ${organizer_UIHDRS} ${organizer_RCS} ${organizer_RCCPPS} ${organizer_translations_qm}) TARGET_LINK_LIBRARIES(ModOrganizer - Qt5::Widgets Qt5::WinExtras Qt5::WebKitWidgets + Qt5::Widgets Qt5::WinExtras Qt5::WebEngineWidgets ${Boost_LIBRARIES} zlibstatic uibase esptk bsatk githubpp @@ -313,7 +313,7 @@ SET_TARGET_PROPERTIES(ModOrganizer PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/LARGEADDRESSAWARE ${OPTIMIZE_LINK_FLAGS}") -QT5_USE_MODULES(ModOrganizer Widgets Declarative Network WebKitWidgets) +QT5_USE_MODULES(ModOrganizer Widgets Script Qml QuickWidgets Network WebEngineWidgets) ############### diff --git a/src/browserdialog.cpp b/src/browserdialog.cpp index e5f0f21d..c2c65acc 100644 --- a/src/browserdialog.cpp +++ b/src/browserdialog.cpp @@ -28,13 +28,13 @@ along with Mod Organizer. If not, see . #include #include "settings.h" +#include #include #include #include #include -#include +#include #include -#include #include #include @@ -77,8 +77,8 @@ void BrowserDialog::closeEvent(QCloseEvent *event) void BrowserDialog::initTab(BrowserView *newView) { - newView->page()->setNetworkAccessManager(m_AccessManager); - newView->page()->setForwardUnsupportedContent(true); + //newView->page()->setNetworkAccessManager(m_AccessManager); + //newView->page()->setForwardUnsupportedContent(true); connect(newView, SIGNAL(loadProgress(int)), this, SLOT(progress(int))); connect(newView, SIGNAL(titleChanged(QString)), this, SLOT(titleChanged(QString))); @@ -86,14 +86,14 @@ void BrowserDialog::initTab(BrowserView *newView) connect(newView, SIGNAL(startFind()), this, SLOT(startSearch())); connect(newView, SIGNAL(urlChanged(QUrl)), this, SLOT(urlChanged(QUrl))); connect(newView, SIGNAL(openUrlInNewTab(QUrl)), this, SLOT(openInNewTab(QUrl))); - connect(newView->page(), SIGNAL(downloadRequested(QNetworkRequest)), this, SLOT(downloadRequested(QNetworkRequest))); - connect(newView->page(), SIGNAL(unsupportedContent(QNetworkReply*)), this, SLOT(unsupportedContent(QNetworkReply*))); + connect(newView, SIGNAL(downloadRequested(QNetworkRequest)), this, SLOT(downloadRequested(QNetworkRequest))); + connect(newView, SIGNAL(unsupportedContent(QNetworkReply*)), this, SLOT(unsupportedContent(QNetworkReply*))); ui->backBtn->setEnabled(false); ui->fwdBtn->setEnabled(false); m_Tabs->addTab(newView, tr("new")); - newView->settings()->setAttribute(QWebSettings::PluginsEnabled, true); - newView->settings()->setAttribute(QWebSettings::AutoLoadImages, true); + newView->settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true); + newView->settings()->setAttribute(QWebEngineSettings::AutoLoadImages, true); } @@ -133,10 +133,10 @@ void BrowserDialog::openUrl(const QUrl &url) void BrowserDialog::maximizeWidth() { - int viewportWidth = getCurrentView()->page()->viewportSize().width(); + int viewportWidth = getCurrentView()->page()->contentsSize ().width(); int frameWidth = width() - viewportWidth; - int contentWidth = getCurrentView()->page()->mainFrame()->contentsSize().width(); + int contentWidth = getCurrentView()->page()->contentsSize().width(); QDesktopWidget screen; int currentScreen = screen.screenNumber(this); @@ -190,7 +190,7 @@ QString BrowserDialog::guessFileName(const QString &url) void BrowserDialog::unsupportedContent(QNetworkReply *reply) { try { - QWebPage *page = qobject_cast(sender()); + QWebEnginePage *page = qobject_cast(sender()); if (page == nullptr) { qCritical("sender not a page"); return; @@ -252,10 +252,10 @@ void BrowserDialog::startSearch() void BrowserDialog::on_searchEdit_returnPressed() { - BrowserView *currentView = getCurrentView(); - if (currentView != nullptr) { - currentView->findText(ui->searchEdit->text(), QWebPage::FindWrapsAroundDocument); - } +// BrowserView *currentView = getCurrentView(); +// if (currentView != nullptr) { +// currentView->findText(ui->searchEdit->text(), QWebEnginePage::FindWrapsAroundDocument); +// } } void BrowserDialog::on_refreshBtn_clicked() @@ -274,7 +274,7 @@ void BrowserDialog::on_browserTabWidget_currentChanged(int index) void BrowserDialog::on_urlEdit_returnPressed() { - QWebView *currentView = getCurrentView(); + QWebEngineView *currentView = getCurrentView(); if (currentView != nullptr) { currentView->setUrl(QUrl(ui->urlEdit->text())); } diff --git a/src/browserdialog.h b/src/browserdialog.h index 04567f5f..354a377b 100644 --- a/src/browserdialog.h +++ b/src/browserdialog.h @@ -24,7 +24,7 @@ along with Mod Organizer. If not, see . #include #include #include -#include +#include #include #include #include diff --git a/src/browserview.cpp b/src/browserview.cpp index aeb16520..0b871e23 100644 --- a/src/browserview.cpp +++ b/src/browserview.cpp @@ -21,23 +21,23 @@ along with Mod Organizer. If not, see . #include #include -#include -#include #include +#include +#include #include #include #include "utility.h" BrowserView::BrowserView(QWidget *parent) - : QWebView(parent) + : QWebEngineView(parent) { installEventFilter(this); - page()->settings()->setMaximumPagesInCache(10); + //page()->settings()->setMaximumPagesInCache(10); } -QWebView *BrowserView::createWindow(QWebPage::WebWindowType) +QWebEngineView *BrowserView::createWindow(QWebEnginePage::WebWindowType) { BrowserView *newView = new BrowserView(parentWidget()); emit initTab(newView); @@ -59,17 +59,18 @@ bool BrowserView::eventFilter(QObject *obj, QEvent *event) mouseEvent->ignore(); return true; } - } else if (event->type() == QEvent::MouseButtonRelease) { - QMouseEvent *mouseEvent = static_cast(event); - if (mouseEvent->button() == Qt::MidButton) { - QWebHitTestResult hitTest = page()->frameAt(mouseEvent->pos())->hitTestContent(mouseEvent->pos()); - if (hitTest.linkUrl().isValid()) { - emit openUrlInNewTab(hitTest.linkUrl()); - } - mouseEvent->ignore(); - - return true; - } +// TODO This is due to that QTWebEnginePage doesn't support QWebFrame anymore +// } else if (event->type() == QEvent::MouseButtonRelease) { +// QMouseEvent *mouseEvent = static_cast(event); +// if (mouseEvent->button() == Qt::MidButton) { +// QWebEngineContextMenuData hitTest = page()->hitTestContent(mouseEvent->pos()); +// if (hitTest.linkUrl().isValid()) { +// emit openUrlInNewTab(hitTest.linkUrl()); +// } +// mouseEvent->ignore(); +// +// return true; +// } } - return QWebView::eventFilter(obj, event); + return QWebEngineView::eventFilter(obj, event); } diff --git a/src/browserview.h b/src/browserview.h index 6a89752a..24be21c1 100644 --- a/src/browserview.h +++ b/src/browserview.h @@ -24,13 +24,13 @@ along with Mod Organizer. If not, see . class QEvent; class QUrl; class QWidget; -#include -#include +#include +#include /** * @brief web view used to display a nexus page **/ -class BrowserView : public QWebView +class BrowserView : public QWebEngineView { Q_OBJECT @@ -66,7 +66,7 @@ signals: protected: - virtual QWebView *createWindow(QWebPage::WebWindowType type); + virtual QWebEngineView *createWindow(QWebEnginePage::WebWindowType type); virtual bool eventFilter(QObject *obj, QEvent *event); diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index e1a2183c..0e77859a 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -89,7 +89,8 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo connect(this, SIGNAL(thumbnailClickedSignal(const QString&)), this, SLOT(thumbnailClicked(const QString&))); connect(m_ModInfo.data(), SIGNAL(modDetailsUpdated(bool)), this, SLOT(modDetailsUpdated(bool))); connect(ui->descriptionView, SIGNAL(linkClicked(QUrl)), this, SLOT(linkClicked(QUrl))); - ui->descriptionView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); + //TODO: No easy way to delegate links + //ui->descriptionView->page()->acceptNavigationRequest(QWebEnginePage::DelegateAllLinks); if (directory->originExists(ToWString(modInfo->name()))) { m_Origin = &directory->getOriginByName(ToWString(modInfo->name())); diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index a03edef7..a452bc59 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -678,7 +678,7 @@ p, li { white-space: pre-wrap; } - + about:blank @@ -807,9 +807,9 @@ p, li { white-space: pre-wrap; } - QWebView + QQuickWidget QWidget -
    QtWebKitWidgets/QWebView
    +
    QtQuickWidgets/QQuickWidget
    -- cgit v1.3.1 From 8bea68564007432d225175201eb8a41277d3574e Mon Sep 17 00:00:00 2001 From: LePresidente Date: Fri, 5 May 2017 08:39:51 +0200 Subject: windeployqt doesn't automatically detect the requirement for qtwebenginewidgets, so we force it. --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0d330d72..448f47e5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -337,7 +337,7 @@ INSTALL( CODE "EXECUTE_PROCESS( COMMAND - ${qt5bin}/windeployqt.exe ModOrganizer.exe ${windeploy_parameters} + ${qt5bin}/windeployqt.exe ModOrganizer.exe --webenginewidgets ${windeploy_parameters} COMMAND ${qt5bin}/windeployqt.exe uibase.dll ${windeploy_parameters} WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin -- cgit v1.3.1 From b09f8ce7b40a64fa6ac6cdf2dd1a8acdfb736204 Mon Sep 17 00:00:00 2001 From: LePresidente Date: Sun, 22 Oct 2017 21:05:06 +0200 Subject: Updated version to 2.1.0beta Added QtQuick to cmake to try address tutorial issue [Untested] --- src/CMakeLists.txt | 5 +++-- src/version.rc | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 448f47e5..833f7504 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -234,6 +234,7 @@ SET(CMAKE_AUTOMOC ON) SET(CMAKE_AUTOUIC ON) FIND_PACKAGE(Qt5Widgets REQUIRED) FIND_PACKAGE(Qt5QuickWidgets REQUIRED) +FIND_PACKAGE(Qt5Quick REQUIRED) FIND_PACKAGE(Qt5Network REQUIRED) FIND_PACKAGE(Qt5WinExtras REQUIRED) FIND_PACKAGE(Qt5WebEngineWidgets REQUIRED) @@ -299,7 +300,7 @@ ENDIF() ADD_EXECUTABLE(ModOrganizer WIN32 ${organizer_HDRS} ${organizer_SRCS} ${organizer_UIHDRS} ${organizer_RCS} ${organizer_RCCPPS} ${organizer_translations_qm}) TARGET_LINK_LIBRARIES(ModOrganizer - Qt5::Widgets Qt5::WinExtras Qt5::WebEngineWidgets + Qt5::Widgets Qt5::WinExtras Qt5::WebEngineWidgets Qt5::Quick ${Boost_LIBRARIES} zlibstatic uibase esptk bsatk githubpp @@ -313,7 +314,7 @@ SET_TARGET_PROPERTIES(ModOrganizer PROPERTIES LINK_FLAGS_RELWITHDEBINFO "/LARGEADDRESSAWARE ${OPTIMIZE_LINK_FLAGS}") -QT5_USE_MODULES(ModOrganizer Widgets Script Qml QuickWidgets Network WebEngineWidgets) +QT5_USE_MODULES(ModOrganizer Widgets Script Qml QuickWidgets Quick Network WebEngineWidgets) ############### diff --git a/src/version.rc b/src/version.rc index e3a2c832..f8b62a2f 100644 --- a/src/version.rc +++ b/src/version.rc @@ -1,7 +1,7 @@ #include "Winver.h" -#define VER_FILEVERSION 2,0,8,3 -#define VER_FILEVERSION_STR "2.0.8.3beta\0" +#define VER_FILEVERSION 2,1,0 +#define VER_FILEVERSION_STR "2.1.0beta\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION -- cgit v1.3.1 From ac3953030df11f8c9356fa8998c795608f3180e0 Mon Sep 17 00:00:00 2001 From: Jeremy Rimpo Date: Mon, 23 Oct 2017 16:09:27 -0500 Subject: Fix the nexus description link handler --- src/CMakeLists.txt | 1 + src/descriptionpage.h | 28 ++++++++++ src/modinfodialog.cpp | 14 +++-- src/organizer_en.ts | 147 ++++++++++++++++++++++++++------------------------ 4 files changed, 117 insertions(+), 73 deletions(-) create mode 100644 src/descriptionpage.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 833f7504..d5ebf6ae 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -171,6 +171,7 @@ SET(organizer_HDRS instancemanager.h usvfsconnector.h eventfilter.h + descriptionpage.h shared/windows_error.h shared/error_report.h diff --git a/src/descriptionpage.h b/src/descriptionpage.h new file mode 100644 index 00000000..f6158ee0 --- /dev/null +++ b/src/descriptionpage.h @@ -0,0 +1,28 @@ +#include + +#ifndef DESCRIPTIONPAGE_H +#define DESCRIPTIONPAGE_H + +class DescriptionPage : public QWebEnginePage +{ + Q_OBJECT + +public: + DescriptionPage(QObject* parent = 0) : QWebEnginePage(parent){} + + bool acceptNavigationRequest(const QUrl & url, QWebEnginePage::NavigationType type, bool isMainFrame) + { + if (type == QWebEnginePage::NavigationTypeLinkClicked) + { + emit linkClicked(url); + return false; + } + return true; + } + +signals: + void linkClicked(const QUrl&); + +}; + +#endif //DESCRIPTIONPAGE_H diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index c96c2b4a..c586c6b6 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -19,6 +19,7 @@ along with Mod Organizer. If not, see . #include "modinfodialog.h" #include "ui_modinfodialog.h" +#include "descriptionpage.h" #include "iplugingame.h" #include "nexusinterface.h" @@ -38,6 +39,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include @@ -85,10 +87,12 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->notesEdit->setText(modInfo->notes()); + ui->descriptionView->setPage(new DescriptionPage); + connect(&m_ThumbnailMapper, SIGNAL(mapped(const QString&)), this, SIGNAL(thumbnailClickedSignal(const QString&))); connect(this, SIGNAL(thumbnailClickedSignal(const QString&)), this, SLOT(thumbnailClicked(const QString&))); connect(m_ModInfo.data(), SIGNAL(modDetailsUpdated(bool)), this, SLOT(modDetailsUpdated(bool))); - connect(ui->descriptionView, SIGNAL(linkClicked(QUrl)), this, SLOT(linkClicked(QUrl))); + connect(ui->descriptionView->page(), SIGNAL(linkClicked(QUrl)), this, SLOT(linkClicked(QUrl))); //TODO: No easy way to delegate links //ui->descriptionView->page()->acceptNavigationRequest(QWebEnginePage::DelegateAllLinks); @@ -826,11 +830,13 @@ void ModInfoDialog::modDetailsUpdated(bool success) "%1" "").arg(BBCode::convertToHTML(nexusDescription)); + ui->descriptionView->page()->setHtml(descriptionAsHTML); + // QString descriptionAsHTML = BBCode::convertToHTML(result["description"].toString()); - ui->descriptionView->setHtml(descriptionAsHTML); + // ui->descriptionView->setHtml(descriptionAsHTML); } else { // ui->descriptionView->setHtml(result["summary"].toString().append(QString("\r\n") + tr("(description incomplete, please visit nexus)"))); - ui->descriptionView->setHtml(tr("(description incomplete, please visit nexus)")); + ui->descriptionView->page()->setHtml(tr("(description incomplete, please visit nexus)")); } updateVersionColor(); @@ -876,7 +882,7 @@ void ModInfoDialog::on_modIDEdit_editingFinished() if (oldID != modID){ m_ModInfo->setNexusID(modID); - ui->descriptionView->setHtml(""); + ui->descriptionView->page()->setHtml(""); if (modID != 0) { m_RequestStarted = false; refreshNexusData(modID); diff --git a/src/organizer_en.ts b/src/organizer_en.ts index 535c16a5..15785d1f 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -2989,227 +2989,227 @@ p, li { white-space: pre-wrap; } - + &Delete - + &Rename - + &Hide - + &Unhide - + &Open - + &New Folder - - + + Save changes? - - + + Save changes to "%1"? - + File Exists - + A file with that name exists, please enter a new one - + failed to move file - + failed to create directory "optional" - - + + Info requested, please wait - + Main - + Update - + Optional - + Old - + Misc - + Unknown - + Current Version: %1 - + No update available - + (description incomplete, please visit nexus) - + <a href="%1">Visit on Nexus</a> - + Failed to delete %1 - - + + Confirm - + Are sure you want to delete "%1"? - + Are sure you want to delete the selected files? - - + + New Folder - + Failed to create "%1" - - + + Replace file? - + There already is a hidden version of this file. Replace it? - - + + File operation failed - - + + Failed to remove "%1". Maybe you lack the required file permissions? - - + + failed to rename %1 to %2 - + There already is a visible version of this file. Replace it? - + Un-Hide - + Hide - + Name - + Please enter a name - - + + Error - + Invalid name. Must be a valid file name - + A tweak by that name exists - + Create Tweak @@ -3870,7 +3870,8 @@ Continue? - The modindex determins the formids of objects originating from this mods. + The modindex determines the formids of objects originating from this mods. + The modindex determins the formids of objects originating from this mods. @@ -3905,37 +3906,37 @@ Continue? - + This plugin can't be disabled (enforced by the game) - + <b>Origin</b>: %1 - + Author - + Description - + Missing Masters - + Enabled Masters - + failed to restore load order for %1 @@ -5628,22 +5629,26 @@ On Windows XP: - <img src=":/MO/gui/emblem_conflict_overwrite" /> indicates that the mod overwrites files that are also available in another mod. + <img src="qrc:///MO/gui/emblem_conflict_overwrite" /> indicates that the mod overwrites files that are also available in another mod. + <img src=":/MO/gui/emblem_conflict_overwrite" /> indicates that the mod overwrites files that are also available in another mod. - <img src=":/MO/gui/emblem_conflict_overwritten" /> indicates that the mod is <b>partially</b> overwritten by another. + <img src="qrc:///MO/gui/emblem_conflict_overwritten" /> indicates that the mod is <b>partially</b> overwritten by another. + <img src=":/MO/gui/emblem_conflict_overwritten" /> indicates that the mod is <b>partially</b> overwritten by another. - <img src=":/MO/gui/emblem_conflict_mixed" /> indicates that both of the above is true. + <img src="qrc:///MO/gui/emblem_conflict_mixed" /> indicates that both of the above is true. + <img src=":/MO/gui/emblem_conflict_mixed" /> indicates that both of the above is true. - <img src=":/MO/gui/emblem_conflict_redundant" /> indicates that the mod is completely overwrtten by another. You could as well disable it. + <img src="qrc:///MO/gui/emblem_conflict_redundant" /> indicates that the mod is completely overwrtten by another. You could as well disable it. + <img src=":/MO/gui/emblem_conflict_redundant" /> indicates that the mod is completely overwrtten by another. You could as well disable it. @@ -5684,7 +5689,8 @@ On Windows XP: - Please open the "ESPs"-tab... + Please open the "Plugins"-tab... + Please open the "ESPs"-tab... @@ -5694,17 +5700,20 @@ On Windows XP: - Unlike with file conflicts, MO does not provide help on finding conflicts. The good news is, there already is a perfect tool for that called BOSS. BOSS is available on the Nexus and integrates neatly with MO. Basically, if you don't have BOSS yet, install it once this tutorial is over. + Unlike with file conflicts, MO does not provide help on finding conflicts. The good news is, there already is a perfect tool for that called LOOT. LOOT is available on the Nexus and integrates neatly with MO. Basically, if you don't have LOOT yet, install it once this tutorial is over. + Unlike with file conflicts, MO does not provide help on finding conflicts. The good news is, there already is a perfect tool for that called BOSS. BOSS is available on the Nexus and integrates neatly with MO. Basically, if you don't have BOSS yet, install it once this tutorial is over. - After you installed BOSS in the default location (follow its instructions), start MO again and BOSS should automatically appear as an Executable... + After you installed LOOT in the default location (follow its instructions), start MO again and LOOT should automatically appear as an Executable... + After you installed BOSS in the default location (follow its instructions), start MO again and BOSS should automatically appear as an Executable... - When you run BOSS, it will automatically re-organize plugins for best compatibility (overwriting your manual changes). It will also open a report in your browser that warns about incompatibilities. You should read the report, at least for new mods. + When you run LOOT, it will automatically re-organize plugins for best compatibility (overwriting your manual changes). It will also open a report in your browser that warns about incompatibilities. You should read the report, at least for new mods. + When you run BOSS, it will automatically re-organize plugins for best compatibility (overwriting your manual changes). It will also open a report in your browser that warns about incompatibilities. You should read the report, at least for new mods. -- cgit v1.3.1 From d7bc542b3d1a96a546d48850e61d3ecdc953c4cc Mon Sep 17 00:00:00 2001 From: Jeremy Rimpo Date: Thu, 30 Nov 2017 16:37:14 -0600 Subject: Implement mod/plugin highlighting when pair is selected --- src/CMakeLists.txt | 2 + src/mainwindow.cpp | 15 + src/mainwindow.h | 3 + src/mainwindow.ui | 7 +- src/modinfo.cpp | 5 + src/modinfo.h | 14 +- src/modinfooverwrite.cpp | 8 +- src/modinforegular.cpp | 10 +- src/modlist.cpp | 37 ++- src/modlist.h | 3 + src/organizer_en.ts | 812 ++++++++++++++++++++++++----------------------- src/pluginlist.cpp | 34 +- src/pluginlist.h | 3 + src/pluginlistview.cpp | 58 ++++ src/pluginlistview.h | 24 ++ 15 files changed, 629 insertions(+), 406 deletions(-) create mode 100644 src/pluginlistview.cpp create mode 100644 src/pluginlistview.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d5ebf6ae..d603336c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -23,6 +23,7 @@ SET(organizer_SRCS profile.cpp pluginlistsortproxy.cpp pluginlist.cpp + pluginlistview.cpp overwriteinfodialog.cpp nxmaccessmanager.cpp nexusinterface.cpp @@ -109,6 +110,7 @@ SET(organizer_HDRS profile.h pluginlistsortproxy.h pluginlist.h + pluginlistview.h overwriteinfodialog.h nxmaccessmanager.h nexusinterface.h diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 0a6e32c3..84860767 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -357,6 +357,9 @@ MainWindow::MainWindow(QSettings &initSettings connect(this, SIGNAL(styleChanged(QString)), this, SLOT(updateStyle(QString))); + connect(ui->espList->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(esplistSelectionsChanged(QItemSelection))); + connect(ui->modList->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(modlistSelectionsChanged(QItemSelection))); + m_UpdateProblemsTimer.setSingleShot(true); connect(&m_UpdateProblemsTimer, SIGNAL(timeout()), this, SLOT(updateProblemsButton())); @@ -2121,6 +2124,18 @@ void MainWindow::modlistSelectionChanged(const QModelIndex ¤t, const QMode ui->modList->verticalScrollBar()->repaint(); } +void MainWindow::modlistSelectionsChanged(const QItemSelection &selected) +{ + m_OrganizerCore.pluginList()->highlightPlugins(selected, *m_OrganizerCore.directoryStructure()); + ui->espList->verticalScrollBar()->repaint(); +} + +void MainWindow::esplistSelectionsChanged(const QItemSelection &selected) +{ + m_OrganizerCore.modList()->highlightMods(selected, *m_OrganizerCore.directoryStructure()); + ui->modList->verticalScrollBar()->repaint(); +} + void MainWindow::modListSortIndicatorChanged(int, Qt::SortOrder) { ui->modList->verticalScrollBar()->repaint(); diff --git a/src/mainwindow.h b/src/mainwindow.h index 06c51203..cec6c407 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -520,6 +520,9 @@ private slots: void modlistSelectionChanged(const QModelIndex ¤t, const QModelIndex &previous); void modListSortIndicatorChanged(int column, Qt::SortOrder order); + void modlistSelectionsChanged(const QItemSelection ¤t); + void esplistSelectionsChanged(const QItemSelection ¤t); + private slots: // ui slots // actions void on_actionAdd_Profile_triggered(); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 743ce012..26b9e375 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -791,7 +791,7 @@ p, li { white-space: pre-wrap; }
    - + 250 @@ -1412,6 +1412,11 @@ Right now this has very limited functionality QTreeView
    modlistview.h
    + + PluginListView + QTreeView +
    pluginlistview.h
    +
    diff --git a/src/modinfo.cpp b/src/modinfo.cpp index 5d3c1e41..77df6216 100644 --- a/src/modinfo.cpp +++ b/src/modinfo.cpp @@ -301,6 +301,11 @@ void ModInfo::setVersion(const VersionInfo &version) m_Version = version; } +void ModInfo::setPluginSelected(const bool &isSelected) +{ + m_PluginSelected = isSelected; +} + void ModInfo::addCategory(const QString &categoryName) { int id = CategoryFactory::instance().getCategoryID(categoryName); diff --git a/src/modinfo.h b/src/modinfo.h index 1485a4b3..c62df549 100644 --- a/src/modinfo.h +++ b/src/modinfo.h @@ -67,7 +67,8 @@ public: FLAG_CONFLICT_OVERWRITE, FLAG_CONFLICT_OVERWRITTEN, FLAG_CONFLICT_MIXED, - FLAG_CONFLICT_REDUNDANT + FLAG_CONFLICT_REDUNDANT, + FLAG_PLUGIN_SELECTED }; enum EContent { @@ -89,7 +90,8 @@ public: HIGHLIGHT_NONE = 0, HIGHLIGHT_INVALID = 1, HIGHLIGHT_CENTER = 2, - HIGHLIGHT_IMPORTANT = 4 + HIGHLIGHT_IMPORTANT = 4, + HIGHLIGHT_PLUGIN = 8 }; enum EEndorsedState { @@ -268,6 +270,12 @@ public: */ virtual void setVersion(const MOBase::VersionInfo &version); + /** + * @brief Controls if mod should be highlighted based on plugin selection + * @param isSelected whether or not the plugin has a selected mod + **/ + virtual void setPluginSelected(const bool &isSelected); + /** * @brief set the newest version of this mod on the nexus * @@ -599,6 +607,8 @@ protected: MOBase::VersionInfo m_Version; + bool m_PluginSelected = false; + private: static QMutex s_Mutex; diff --git a/src/modinfooverwrite.cpp b/src/modinfooverwrite.cpp index 0104998a..6b8c9bd9 100644 --- a/src/modinfooverwrite.cpp +++ b/src/modinfooverwrite.cpp @@ -29,12 +29,18 @@ std::vector ModInfoOverwrite::getFlags() const { std::vector result; result.push_back(FLAG_OVERWRITE); + if (m_PluginSelected) + result.push_back(FLAG_PLUGIN_SELECTED); return result; } int ModInfoOverwrite::getHighlight() const { - return (isValid() ? HIGHLIGHT_IMPORTANT : HIGHLIGHT_INVALID) | HIGHLIGHT_CENTER; + int highlight = (isValid() ? HIGHLIGHT_IMPORTANT : HIGHLIGHT_INVALID) | HIGHLIGHT_CENTER; + auto flags = getFlags(); + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_PLUGIN_SELECTED) != flags.end()) + highlight |= HIGHLIGHT_PLUGIN; + return highlight; } QString ModInfoOverwrite::getDescription() const diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index c296d792..9d94002b 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -420,6 +420,9 @@ std::vector ModInfoRegular::getFlags() const if (m_Notes.length() != 0) { result.push_back(ModInfo::FLAG_NOTES); } + if (m_PluginSelected) { + result.push_back(ModInfo::FLAG_PLUGIN_SELECTED); + } return result; } @@ -471,7 +474,12 @@ std::vector ModInfoRegular::getContents() const int ModInfoRegular::getHighlight() const { - return isValid() ? HIGHLIGHT_NONE: HIGHLIGHT_INVALID; + if (!isValid()) + return HIGHLIGHT_INVALID; + auto flags = getFlags(); + if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_PLUGIN_SELECTED) != flags.end()) + return HIGHLIGHT_PLUGIN; + return HIGHLIGHT_NONE; } diff --git a/src/modlist.cpp b/src/modlist.cpp index ffbdee95..c6341251 100644 --- a/src/modlist.cpp +++ b/src/modlist.cpp @@ -338,6 +338,7 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const int highlight = modInfo->getHighlight(); if (highlight & ModInfo::HIGHLIGHT_IMPORTANT) return QBrush(Qt::darkRed); else if (highlight & ModInfo::HIGHLIGHT_INVALID) return QBrush(Qt::darkGray); + else if (highlight & ModInfo::HIGHLIGHT_PLUGIN) return QBrush(Qt::darkBlue); } else if (column == COL_VERSION) { if (!modInfo->getNewestVersion().isValid()) { return QVariant(); @@ -350,7 +351,9 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const return QVariant(); } else if ((role == Qt::BackgroundRole) || (role == ViewMarkingScrollBar::DEFAULT_ROLE)) { - if (m_Overwrite.find(modIndex) != m_Overwrite.end()) { + if (modInfo->getHighlight() & ModInfo::HIGHLIGHT_PLUGIN) { + return QColor(0, 0, 255, 32); + } else if (m_Overwrite.find(modIndex) != m_Overwrite.end()) { return QColor(0, 255, 0, 32); } else if (m_Overwritten.find(modIndex) != m_Overwritten.end()) { return QColor(255, 0, 0, 32); @@ -685,6 +688,38 @@ int ModList::timeElapsedSinceLastChecked() const return m_LastCheck.elapsed(); } +void ModList::highlightMods(const QItemSelection &selected, const MOShared::DirectoryEntry &directoryEntry) +{ + for (unsigned int i = 0; i < ModInfo::getNumMods(); ++i) { + ModInfo::getByIndex(i)->setPluginSelected(false); + } + for (QModelIndex idx : selected.indexes()) { + QString modName = idx.data().toString(); + + const MOShared::FileEntry::Ptr fileEntry = directoryEntry.findFile(modName.toStdWString()); + if (fileEntry.get() != nullptr) { + QString fileName; + bool archive = false; + std::vector origins; + { + std::vector alternatives = fileEntry->getAlternatives(); + origins.push_back(fileEntry->getOrigin(archive)); + origins.insert(origins.end(), alternatives.begin(), alternatives.end()); + } + for (int originId : origins) { + MOShared::FilesOrigin &origin = directoryEntry.getOriginByID(originId); + for (unsigned int i = 0; i < ModInfo::getNumMods(); ++i) { + if (ModInfo::getByIndex(i)->internalName() == QString::fromStdWString(origin.getName())) { + ModInfo::getByIndex(i)->setPluginSelected(true); + break; + } + } + } + } + } + notifyChange(0, rowCount() - 1); +} + IModList::ModStates ModList::state(unsigned int modIndex) const { IModList::ModStates result; diff --git a/src/modlist.h b/src/modlist.h index 358fd583..bd715107 100644 --- a/src/modlist.h +++ b/src/modlist.h @@ -27,6 +27,7 @@ along with Mod Organizer. If not, see . #include "profile.h" #include +#include #include #include @@ -113,6 +114,8 @@ public: int timeElapsedSinceLastChecked() const; + void highlightMods(const QItemSelection &selected, const MOShared::DirectoryEntry &directoryEntry); + public: /// \copydoc MOBase::IModList::displayName diff --git a/src/organizer_en.ts b/src/organizer_en.ts index f8e6ffaa..cd6277df 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -820,148 +820,148 @@ p, li { white-space: pre-wrap; } EditExecutablesDialog - + Modify Executables - + List of configured executables - + This is a list of your configured executables. Executables in grey are automatically recognised and can not be modified. - + Title - - + + Name of the executable. This is only for display purposes. - + Binary - - + + Binary to run - + Browse filesystem - + Browse filesystem for the executable to run. - - + + ... - + Start in - + Arguments - - + + Arguments to pass to the application - + Allow the Steam AppID to be used for this executable to be changed. - + Allow the Steam AppID to be used for this executable to be changed. Every game/tool distributed through Steam has a unique ID. MO needs to know this ID to start those programs directly, otherwise the program is started by steam and then MO will not work. By default, MO will use the AppID for the game. Right now the only case I know of where this needs to be overwritten is for the Skyrim Creation Kit which has its own AppID. This overwrite is already preconfigured. - + Overwrite Steam AppID - + Steam AppID to use for this executable that differs from the games AppID. - + Steam AppID to use for this executable that differs from the games AppID. Every game/tool distributed through Steam has a unique ID. MO needs to know this ID to start those programs directly, otherwise the program is started by steam and then MO will not work. By default, MO will use the AppID for the game (usually 72850). Right now the only case I know of where this needs to be overwritten is for the Skyrim Creation Kit which has its own AppID (usually 202480). This overwrite is already preconfigured. - + If this is enabled, new files are created in the specified mod instead of the "Overwrite" mod. - + Create Files in Mod instead of Overwrite (*) - + Use Application's Icon for shortcuts - + (*) This setting is profile-specific - - + + Add an executable - + Add - - + + Remove the selected executable - + Remove - + Close @@ -1131,87 +1131,87 @@ p, li { white-space: pre-wrap; } - + Extracting files - + failed to create backup - + Mod Name - + Name - + Invalid name - + The name you entered is invalid, please enter a different one. - + File format "%1" not supported - + None of the available installer plugins were able to handle that archive - + no error - + 7z.dll not found - + 7z.dll isn't valid - + archive not found - + failed to open archive - + unsupported archive type - + internal library error - + archive invalid - + unknown archive error @@ -1461,8 +1461,8 @@ p, li { white-space: pre-wrap; } - - + + Refresh @@ -1641,7 +1641,7 @@ p, li { white-space: pre-wrap; } - + Update @@ -1652,7 +1652,7 @@ p, li { white-space: pre-wrap; } - + No Problems @@ -1682,7 +1682,7 @@ Right now this has very limited functionality - + Endorse Mod Organizer @@ -1707,579 +1707,579 @@ Right now this has very limited functionality - + Toolbar - + Desktop - + Start Menu - + Problems - + There are potential problems with your setup - + Everything seems to be in order - + Help on UI - + Documentation Wiki - + Report Issue - + Tutorials - + About - + About Qt - + Name - + Please enter a name for the new profile - + failed to create profile: %1 - + Show tutorial? - + You are starting Mod Organizer for the first time. Do you want to show a tutorial of its basic features? If you choose no you can always start the tutorial from the "Help"-menu. - + Downloads in progress - + There are still downloads in progress, do you really want to quit? - + Plugin "%1" failed: %2 - + Plugin "%1" failed - + Browse Mod Page - + Also in: <br> - + No conflict - + <Edit...> - + Activating Network Proxy - + Choose Mod - + Mod Archive - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - + failed to spawn notepad.exe: %1 - + failed to open %1 - + failed to change origin name: %1 - + failed to move "%1" from mod "%2" to "%3": %4 - + <Contains %1> - + <Checked> - + <Unchecked> - + <Update> - + <Managed by MO> - + <Managed outside MO> - + <No category> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" - - - - + + + + Confirm - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 - - + + Failed - + Installation file no longer exists - + Mods installed with old versions of MO can't be reinstalled in this way. - + You need to be logged in with Nexus to resume a download - - + + You need to be logged in with Nexus to endorse - + Failed to display overwrite dialog: %1 - + Nexus ID for this Mod is unknown - + Web page for this mod is unknown - - - + + + Create Mod... - + This will create an empty mod. Please enter a name: - - + + A mod with this name already exists - + This will move all files from overwrite into a new, regular mod. Please enter a name: - + Not logged in, endorsement information will be wrong - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - - + + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + Really enable all visible mods? - + Really disable all visible mods? - + Choose what to export - + Everything - + All installed mods are included in the list - + Active Mods - + Only active (checked) mods from your current profile are included - + Visible - + All mods visible in the mod list are included - + export failed: %1 - + Install Mod... - + Create empty mod - + Enable all visible - + Disable all visible - + Check all for update - + Export to csv... - + All Mods - + Sync to Mods... - + Restore Backup - + Remove Backup... - + Add/Remove Categories - + Replace Categories - + Primary Category - + Change versioning scheme - + Un-ignore update - + Ignore update - + Rename Mod... - + Remove Mod... - + Reinstall Mod - + Un-Endorse - - + + Endorse - + Won't endorse - + Endorsement state unknown - + Ignore missing data - + Visit on Nexus - + Visit web page - + Open in explorer - + Information... - - + + Exception: - - + + Unknown exception - + <All> - + <Multiple> - + %1 more - + Are you sure you want to remove the following %n save(s)?<br><ul>%1</ul><br>Removed saves will be sent to the Recycle Bin. @@ -2287,12 +2287,12 @@ This function will guess the versioning scheme under the assumption that the ins - + Enable Mods... - + Delete %n save(s) @@ -2300,319 +2300,319 @@ This function will guess the versioning scheme under the assumption that the ins - + failed to remove %1 - + failed to create %1 - + Can't change download directory while downloads are in progress! - + failed to write to file %1 - + %1 written - + Select binary - + Binary - + Enter Name - + Please enter a name for the executable - + Not an executable - + This is not a recognized executable. - - + + Replace file? - + There already is a hidden version of this file. Replace it? - - + + File operation failed - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - + file not found: %1 - + failed to generate preview for %1 - + Sorry, can't preview anything. This function currently does not support extracting from bsas. - + Update available - + Open/Execute - + Add as Executable - + Preview - + Un-Hide - + Hide - + Write To File... - + Do you want to endorse Mod Organizer on %1 now? - + Thank you! - + Thank you for your endorsement! - + Request to Nexus failed: %1 - - + + failed to read %1: %2 - - + + Error - + failed to extract %1 (errorcode %2) - + Extract BSA - + This archive contains invalid hashes. Some files may be broken. - + Are you sure? - + This will restart MO, continue? - + Edit Categories... - + Deselect filter - + Remove - + Enable all - + Disable all - + Unlock load order - + Lock load order - + depends on missing "%1" - + incompatible with "%1" - + Please wait while LOOT is running - + loot failed. Exit code was: %1 - + failed to start loot - + failed to run loot: %1 - + Errors occured - + Backup of load order created - + Choose backup to restore - + No Backups - + There are no backups to restore - - + + Restore failed - - + + Failed to restore the backup. Errorcode: %1 - + Backup of modlist created - + A file with the same name has already been downloaded. What would you like to do? - + Overwrite - + Rename new file - + Ignore file @@ -2675,16 +2675,21 @@ This function will guess the versioning scheme under the assumption that the ins + MCM Data + + + + invalid content type %1 - + invalid mod index %1 - + remove: invalid mod index %1 @@ -3225,7 +3230,7 @@ p, li { white-space: pre-wrap; } ModInfoOverwrite - + This pseudo mod contains files from the virtual data tree that got modified (i.e. by the construction kit) @@ -3239,13 +3244,13 @@ p, li { white-space: pre-wrap; } - + %1 contains no esp/esm/esl and no asset (textures, meshes, interface, ...) directory %1 contains no esp/esm and no asset (textures, meshes, interface, ...) directory - + Categories: <br> @@ -3254,8 +3259,8 @@ p, li { white-space: pre-wrap; } ModList - Game plugins (esp/esm/esl) - Game plugins (esp/esm) + Game Plugins (ESP/ESM/ESL) + Game plugins (esp/esm/esl) @@ -3299,174 +3304,179 @@ p, li { white-space: pre-wrap; } - + + MCM Configuration + + + + This entry contains files that have been created inside the virtual data tree (i.e. by the construction kit) - + Backup - + No valid game data - + Not endorsed yet - + Overwrites files - + Overwritten files - + Overwrites & Overwritten - + Redundant - + Non-MO - + invalid - + installed version: "%1", newest version: "%2" - + The newest version on Nexus seems to be older than the one you have installed. This could either mean the version you have has been withdrawn (i.e. due to a bug) or the author uses a non-standard versioning scheme and that newest version is actually newer. Either way you may want to "upgrade". - + Categories: <br> - + Invalid name - + drag&drop failed: %1 - + Confirm - + Are you sure you want to remove "%1"? - + Flags - + Content - + Mod Name - + Version - + Priority - + Category - + Nexus ID - + Installation - - + + unknown - + Name of your mods - + Version of the mod (if available) - + Installation priority of your mod. The higher, the more "important" it is and thus overwrites files from mods with lower priority. - + Category of the mod. - + Id of the mod as used on Nexus. - + Emblemes to highlight things that might require attention. - + Depicts the content of the mod:<br><table cellspacing=7><tr><td><img src=":/MO/gui/content/plugin" width=32/></td><td>Game plugins (esp/esm/esl)</tr><tr><td><img src=":/MO/gui/content/interface" width=32/></td><td>Interface</tr><tr><td><img src=":/MO/gui/content/mesh" width=32/></td><td>Meshes</tr><tr><td><img src=":/MO/gui/content/bsa" width=32/></td><td>BSA</tr><tr><td><img src=":/MO/gui/content/texture" width=32/></td><td>Textures</tr><tr><td><img src=":/MO/gui/content/sound" width=32/></td><td>Sounds</tr><tr><td><img src=":/MO/gui/content/music" width=32/></td><td>Music</tr><tr><td><img src=":/MO/gui/content/string" width=32/></td><td>Strings</tr><tr><td><img src=":/MO/gui/content/script" width=32/></td><td>Scripts (Papyrus)</tr><tr><td><img src=":/MO/gui/content/skse" width=32/></td><td>Script Extender plugins</tr><tr><td><img src=":/MO/gui/content/skyproc" width=32/></td><td>SkyProc Patcher</tr></table> Depicts the content of the mod:<br><table cellspacing=7><tr><td><img src=":/MO/gui/content/plugin" width=32/></td><td>Game plugins (esp/esm)</tr><tr><td><img src=":/MO/gui/content/interface" width=32/></td><td>Interface</tr><tr><td><img src=":/MO/gui/content/mesh" width=32/></td><td>Meshes</tr><tr><td><img src=":/MO/gui/content/bsa" width=32/></td><td>BSA</tr><tr><td><img src=":/MO/gui/content/texture" width=32/></td><td>Textures</tr><tr><td><img src=":/MO/gui/content/sound" width=32/></td><td>Sounds</tr><tr><td><img src=":/MO/gui/content/music" width=32/></td><td>Music</tr><tr><td><img src=":/MO/gui/content/string" width=32/></td><td>Strings</tr><tr><td><img src=":/MO/gui/content/script" width=32/></td><td>Scripts (Papyrus)</tr><tr><td><img src=":/MO/gui/content/skse" width=32/></td><td>Script Extender plugins</tr><tr><td><img src=":/MO/gui/content/skyproc" width=32/></td><td>SkyProc Patcher</tr></table> - + Time this mod was installed @@ -3554,189 +3564,189 @@ p, li { white-space: pre-wrap; } OrganizerCore - - + + Failed to write settings - + An error occured trying to update MO settings to %1: %2 - + File is write protected - + Invalid file format (probably a bug) - + Unknown error %1 - + An error occured trying to write back MO settings to %1: %2 - - + + Download started - + Download failed - - + + Installation successful - - + + Configure Mod - - + + This mod contains ini tweaks. Do you want to configure them now? - - + + mod "%1" not found - - + + Installation cancelled - - + + The mod was not installed completely. - + Executable "%1" not found - + Start Steam? - + Steam is required to be running already to correctly start the game. Should MO try to start steam now? - + Error - + No profile set - + Failed to refresh list of esps: %1 - + Multiple esps/esls activated, please check that they don't conflict. - + Download? - + A download has been started but no installed page plugin recognizes it. If you download anyway no information (i.e. version) will be associated with the download. Continue? - + failed to update mod list: %1 - - + + login successful - + Login failed - + Login failed, try again? - + login failed: %1. Download will not be associated with an account - + login failed: %1 - + login failed: %1. You need to log-in with Nexus to update MO. - + Too many esps, esms, and esls enabled - - + + Description missing - + The game doesn't allow more than 255 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or merge some plugins into one. You can find a guide here: <a href="http://wiki.step-project.com/Guide:Merging_Plugins">http://wiki.step-project.com/Guide:Merging_Plugins</a> - + failed to save load order: %1 - + The designated write target "%1" is not enabled. @@ -3833,110 +3843,110 @@ Continue? PluginList - + Name - + Priority - + Mod Index - + Flags - - + + unknown - + Name of your mods - + Load priority of your mod. The higher, the more "important" it is and thus overwrites data from plugins with lower priority. - + The modindex determines the formids of objects originating from this mods. The modindex determins the formids of objects originating from this mods. - + failed to update esp info for file %1 (source id: %2), error: %3 - + esp not found: %1 - - + + Confirm - + Really enable all plugins? - + Really disable all plugins? - + The file containing locked plugin indices is broken - + This plugin can't be disabled (enforced by the game) - + <b>Origin</b>: %1 - + Author - + Description - + Missing Masters - + Enabled Masters - + failed to restore load order for %1 @@ -4289,15 +4299,15 @@ p, li { white-space: pre-wrap; } - - - - + + + + invalid index %1 - + invalid category id %1 @@ -4442,66 +4452,72 @@ p, li { white-space: pre-wrap; } - + game doesn't support a script extender - + + Failed to delete %1 + + + + Failed to deactivate script extender loading - + Failed to remove %1: %2 - - + + Failed to rename %1 to %2 - + Failed to deactivate proxy-dll loading - + Failed to set up script extender loading - + Failed to delete old proxy-dll %1 - + + Failed to copy %1 to %2 - + Failed to overwrite %1 - + Failed to set up proxy-dll loading - + Error - + Failed to create "%1". Your user account probably lacks permission. @@ -4528,75 +4544,75 @@ p, li { white-space: pre-wrap; } - + Could not use configuration settings for game "%1", path "%2". - - + + Please select the game to manage - + No game identified in "%1". The directory is required to contain the game binary and its launcher. - + Please select the game edition you have (MO can't start the game correctly if this is set incorrectly!) - + failed to start application: %1 - + Mod Organizer - + An instance of Mod Organizer is already running - + Failed to set up instance - + Please use "Help" from the toolbar to get usage instructions to all elements - - + + <Manage...> - + failed to parse profile %1: %2 - + Failed to start "%1" - + Waiting - + Please press OK once you're logged into steam. @@ -4617,12 +4633,12 @@ p, li { white-space: pre-wrap; } - + failed to access %1 - + failed to set file time %1 diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp index 4420bf17..a215b9d3 100644 --- a/src/pluginlist.cpp +++ b/src/pluginlist.cpp @@ -21,6 +21,7 @@ along with Mod Organizer. If not, see . #include "settings.h" #include "scopeguard.h" #include "modinfo.h" +#include "viewmarkingscrollbar.h" #include #include #include @@ -107,6 +108,28 @@ QString PluginList::getColumnToolTip(int column) } } +void PluginList::highlightPlugins(const QItemSelection &selected, const MOShared::DirectoryEntry &directoryEntry) +{ + for (auto &esp : m_ESPs) { + esp.m_ModSelected = false; + } + for (QModelIndex idx : selected.indexes()) { + ModInfo::Ptr selectedMod = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); + if (!selectedMod.isNull()) { + QDir dir(selectedMod->absolutePath()); + QStringList plugins = dir.entryList(QStringList() << "*.esp" << "*.esm" << "*.esl"); + if (plugins.size() > 0) { + for (auto plugin : plugins) { + std::map::iterator iter = m_ESPsByName.find(plugin.toLower()); + if (iter != m_ESPsByName.end()) { + m_ESPs[iter->second].m_ModSelected = true; + } + } + } + } + } + emit dataChanged(this->index(0, 0), this->index(m_ESPs.size() - 1, this->columnCount() - 1)); +} void PluginList::refresh(const QString &profileName , const DirectoryEntry &baseDirectory @@ -752,9 +775,16 @@ QVariant PluginList::data(const QModelIndex &modelIndex, int role) const } } else if (role == Qt::ForegroundRole) { if ((modelIndex.column() == COL_NAME) && - m_ESPs[index].m_ForceEnabled) { + m_ESPs[index].m_ForceEnabled) { return QBrush(Qt::gray); } + } else if (role == Qt::BackgroundRole + || (role == ViewMarkingScrollBar::DEFAULT_ROLE)) { + if (m_ESPs[index].m_ModSelected) { + return QColor(0, 0, 255, 32); + } else { + return QVariant(); + } } else if (role == Qt::FontRole) { QFont result; if (m_ESPs[index].m_IsMaster) { @@ -1134,7 +1164,7 @@ PluginList::ESPInfo::ESPInfo(const QString &name, bool enabled, const QString &originName, const QString &fullPath, bool hasIni) : m_Name(name), m_FullPath(fullPath), m_Enabled(enabled), m_ForceEnabled(enabled), - m_Priority(0), m_LoadOrder(-1), m_OriginName(originName), m_HasIni(hasIni) + m_Priority(0), m_LoadOrder(-1), m_OriginName(originName), m_HasIni(hasIni), m_ModSelected(false) { try { ESP::File file(ToWString(fullPath)); diff --git a/src/pluginlist.h b/src/pluginlist.h index e98f5c41..19e98989 100644 --- a/src/pluginlist.h +++ b/src/pluginlist.h @@ -200,6 +200,8 @@ public: static QString getColumnName(int column); static QString getColumnToolTip(int column); + void highlightPlugins(const QItemSelection &selected, const MOShared::DirectoryEntry &directoryEntry); + void refreshLoadOrder(); void disconnectSlots(); @@ -277,6 +279,7 @@ private: QString m_OriginName; bool m_IsMaster; bool m_IsLight; + bool m_ModSelected; QString m_Author; QString m_Description; bool m_HasIni; diff --git a/src/pluginlistview.cpp b/src/pluginlistview.cpp new file mode 100644 index 00000000..0fcf8183 --- /dev/null +++ b/src/pluginlistview.cpp @@ -0,0 +1,58 @@ +#include "pluginlistview.h" +#include +#include +#include + + +class PluginListViewStyle : public QProxyStyle { +public: + PluginListViewStyle(QStyle *style, int indentation); + + void drawPrimitive(PrimitiveElement element, const QStyleOption *option, + QPainter *painter, const QWidget *widget = 0) const; +private: + int m_Indentation; +}; + +PluginListViewStyle::PluginListViewStyle(QStyle *style, int indentation) + : QProxyStyle(style), m_Indentation(indentation) +{ +} + +void PluginListViewStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, + QPainter *painter, const QWidget *widget) const +{ + if (element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull()) { + QStyleOption opt(*option); + opt.rect.setLeft(m_Indentation); + if (widget) { + opt.rect.setRight(widget->width() - 5); // 5 is an arbitrary value that seems to work ok + } + QProxyStyle::drawPrimitive(element, &opt, painter, widget); + } + else { + QProxyStyle::drawPrimitive(element, option, painter, widget); + } +} + +PluginListView::PluginListView(QWidget *parent) + : QTreeView(parent) + , m_Scrollbar(new ViewMarkingScrollBar(this->model(), this)) +{ + setVerticalScrollBar(m_Scrollbar); +} + +void PluginListView::dragEnterEvent(QDragEnterEvent *event) +{ + emit dropModeUpdate(event->mimeData()->hasUrls()); + + QTreeView::dragEnterEvent(event); +} + +void PluginListView::setModel(QAbstractItemModel *model) +{ + QTreeView::setModel(model); + setVerticalScrollBar(new ViewMarkingScrollBar(model, this)); +} + +#pragma once diff --git a/src/pluginlistview.h b/src/pluginlistview.h new file mode 100644 index 00000000..bdd4ee61 --- /dev/null +++ b/src/pluginlistview.h @@ -0,0 +1,24 @@ +#ifndef PLUGINLISTVIEW_H +#define PLUGINLISTVIEW_H + +#include +#include +#include "viewmarkingscrollbar.h" + +class PluginListView : public QTreeView +{ + Q_OBJECT +public: + explicit PluginListView(QWidget *parent = 0); + virtual void dragEnterEvent(QDragEnterEvent *event); + virtual void setModel(QAbstractItemModel *model); +signals: + void dropModeUpdate(bool dropOnRows); + + public slots: +private: + + ViewMarkingScrollBar *m_Scrollbar; +}; + +#endif // PLUGINLISTVIEW_H -- cgit v1.3.1 From 66b17eea8ac83ee6f7b729974240d995a1e8ed3a Mon Sep 17 00:00:00 2001 From: Eran Mizrahi Date: Sat, 9 Dec 2017 21:39:22 +0200 Subject: Wait for processes run using command line shortcuts --- src/CMakeLists.txt | 1 + src/ilockedwaitingforprocess.h | 13 ++++++++++++ src/iuserinterface.h | 7 ++----- src/lockeddialog.h | 7 ++++--- src/mainwindow.cpp | 21 +++---------------- src/mainwindow.h | 4 +--- src/organizercore.cpp | 46 +++++++++++++++++++++++++++++++++--------- src/organizercore.h | 2 +- 8 files changed, 62 insertions(+), 39 deletions(-) create mode 100644 src/ilockedwaitingforprocess.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d603336c..39aabb91 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -169,6 +169,7 @@ SET(organizer_HDRS viewmarkingscrollbar.h plugincontainer.h organizercore.h + ilockedwaitingforprocess.h iuserinterface.h instancemanager.h usvfsconnector.h diff --git a/src/ilockedwaitingforprocess.h b/src/ilockedwaitingforprocess.h new file mode 100644 index 00000000..6a4267d4 --- /dev/null +++ b/src/ilockedwaitingforprocess.h @@ -0,0 +1,13 @@ +#ifndef ILOCKEDWAITINGFORPROCESS_H +#define ILOCKEDWAITINGFORPROCESS_H + +class QString; + +class ILockedWaitingForProcess +{ +public: + virtual bool unlockClicked() = 0; + virtual void setProcessName(QString const &) = 0; +}; + +#endif // ILOCKEDWAITINGFORPROCESS_H diff --git a/src/iuserinterface.h b/src/iuserinterface.h index 0af1c2ac..255c7ac0 100644 --- a/src/iuserinterface.h +++ b/src/iuserinterface.h @@ -3,6 +3,7 @@ #include "modinfo.h" +#include "ilockedwaitingforprocess.h" #include #include #include @@ -27,12 +28,8 @@ public: virtual void displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab) = 0; - virtual void lock() = 0; + virtual ILockedWaitingForProcess* lock() = 0; virtual void unlock() = 0; - virtual bool unlockClicked() = 0; - virtual void setProcessName(QString const &) = 0; - - }; #endif // IUSERINTERFACE_H diff --git a/src/lockeddialog.h b/src/lockeddialog.h index 29ac459b..8803efae 100644 --- a/src/lockeddialog.h +++ b/src/lockeddialog.h @@ -20,6 +20,7 @@ along with Mod Organizer. If not, see . #ifndef LOCKEDDIALOG_H #define LOCKEDDIALOG_H +#include "ilockedwaitingforprocess.h" #include // for QDialog #include // for Q_OBJECT, slots #include // for QString @@ -39,7 +40,7 @@ namespace Ui { * data on which Mod Organizer works. After the UI is unlocked (manually or after the * external application closed) MO will refresh all of its data sources **/ -class LockedDialog : public QDialog +class LockedDialog : public QDialog, public ILockedWaitingForProcess { Q_OBJECT @@ -52,13 +53,13 @@ public: * * @return true if the user clicked the unlock button **/ - bool unlockClicked() const { return m_UnlockClicked; } + bool unlockClicked() override { return m_UnlockClicked; } /** * @brief set the name of the process being run * @param name of process */ - void setProcessName(const QString &name); + void setProcessName(const QString &name) override; protected: diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index b47e9c57..1db895fc 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1465,17 +1465,18 @@ void MainWindow::storeSettings(QSettings &settings) { } } -void MainWindow::lock() +ILockedWaitingForProcess* MainWindow::lock() { if (m_LockDialog != nullptr) { ++m_LockCount; - return; + return m_LockDialog; } m_LockDialog = new LockedDialog(qApp->activeWindow()); m_LockDialog->show(); setEnabled(false); m_LockDialog->setEnabled(true); //What's the point otherwise? ++m_LockCount; + return m_LockDialog; } void MainWindow::unlock() @@ -1494,22 +1495,6 @@ void MainWindow::unlock() } } -bool MainWindow::unlockClicked() -{ - if (m_LockDialog != nullptr) { - return m_LockDialog->unlockClicked(); - } else { - return false; - } -} - -void MainWindow::setProcessName(QString const &name) -{ - if (m_LockDialog != nullptr) { - m_LockDialog->setProcessName(name); - } -} - void MainWindow::on_btnRefreshData_clicked() { m_OrganizerCore.refreshDirectoryStructure(); diff --git a/src/mainwindow.h b/src/mainwindow.h index cec6c407..f6f11157 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -116,10 +116,8 @@ public: void storeSettings(QSettings &settings) override; void readSettings(); - virtual void lock() override; + virtual ILockedWaitingForProcess* lock() override; virtual void unlock() override; - virtual bool unlockClicked() override; - virtual void setProcessName(QString const &name) override; bool addProfile(); void refreshDataTree(); diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 54cfed54..f0dfa8d4 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -31,6 +31,7 @@ #include "appconfig.h" #include #include +#include "lockeddialog.h" #include #include @@ -1067,8 +1068,9 @@ QStringList OrganizerCore::modsSortedByProfilePriority() const void OrganizerCore::spawnBinary(const QFileInfo &binary, const QString &arguments, const QDir ¤tDirectory, const QString &steamAppID, const QString &customOverwrite) { + ILockedWaitingForProcess* uilock = nullptr; if (m_UserInterface != nullptr) { - m_UserInterface->lock(); + uilock = m_UserInterface->lock(); } ON_BLOCK_EXIT([&] () { if (m_UserInterface != nullptr) { m_UserInterface->unlock(); } @@ -1077,7 +1079,7 @@ void OrganizerCore::spawnBinary(const QFileInfo &binary, const QString &argument HANDLE processHandle = spawnBinaryDirect(binary, arguments, m_CurrentProfile->name(), currentDirectory, steamAppID, customOverwrite); if (processHandle != INVALID_HANDLE_VALUE) { DWORD processExitCode; - (void)waitForProcessCompletion(processHandle, &processExitCode); + (void)waitForProcessCompletion(processHandle, &processExitCode, uilock); refreshDirectoryStructure(); // need to remove our stored load order because it may be outdated if a foreign tool changed the @@ -1258,24 +1260,50 @@ HANDLE OrganizerCore::startApplication(const QString &executable, } } - return spawnBinaryDirect(binary, arguments, profileName, currentDirectory, - steamAppID, customOverwrite); + HANDLE processHandle = spawnBinaryDirect(binary, arguments, profileName, currentDirectory, steamAppID, customOverwrite); + if (processHandle != INVALID_HANDLE_VALUE) { + std::unique_ptr dlg; + ILockedWaitingForProcess* uilock = nullptr; + + if (m_UserInterface != nullptr) { + uilock = m_UserInterface->lock(); + } + else { + // i.e. when running command line shortcuts there is no m_UserInterface + dlg.reset(new LockedDialog); + dlg->show(); + dlg->setEnabled(true); + uilock = dlg.get(); + } + + ON_BLOCK_EXIT([&]() { + if (m_UserInterface != nullptr) { + m_UserInterface->unlock(); + } }); + + DWORD processExitCode; + waitForProcessCompletion(processHandle, &processExitCode, uilock); + cycleDiagnostics(); + } + + return processHandle; } bool OrganizerCore::waitForApplication(HANDLE handle, LPDWORD exitCode) { + ILockedWaitingForProcess* uilock = nullptr; if (m_UserInterface != nullptr) { - m_UserInterface->lock(); + uilock = m_UserInterface->lock(); } ON_BLOCK_EXIT([&] () { if (m_UserInterface != nullptr) { m_UserInterface->unlock(); } }); - return waitForProcessCompletion(handle, exitCode); + return waitForProcessCompletion(handle, exitCode, uilock); } -bool OrganizerCore::waitForProcessCompletion(HANDLE handle, LPDWORD exitCode) +bool OrganizerCore::waitForProcessCompletion(HANDLE handle, LPDWORD exitCode, ILockedWaitingForProcess* uilock) { DWORD startPID = ::GetProcessId(handle); @@ -1292,7 +1320,7 @@ bool OrganizerCore::waitForProcessCompletion(HANDLE handle, LPDWORD exitCode) while ( res = ::MsgWaitForMultipleObjects(1, &handle, false, 500, QS_KEY | QS_MOUSEBUTTON), - ((m_UserInterface == nullptr) || !m_UserInterface->unlockClicked())) { + ((uilock == nullptr) || !uilock->unlockClicked())) { if (!::GetVFSProcessList(&numProcesses, processes)) { break; @@ -1335,7 +1363,7 @@ bool OrganizerCore::waitForProcessCompletion(HANDLE handle, LPDWORD exitCode) // Update the lock process name with the name of the lowest active PID - though this may not actually be the main process if (handles.size() > 0) - m_UserInterface->setProcessName(QString::fromStdWString(getProcessName(handles.begin()->second))); + uilock->setProcessName(QString::fromStdWString(getProcessName(handles.begin()->second))); // If the main wait process dies, we need a backup wait process until the subprocesses close if ((res == WAIT_FAILED) || (res == WAIT_OBJECT_0)) { diff --git a/src/organizercore.h b/src/organizercore.h index 40531195..0927c88e 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -267,7 +267,7 @@ private: const MOShared::DirectoryEntry *directoryEntry, int createDestination); - bool waitForProcessCompletion(HANDLE handle, LPDWORD exitCode); + bool waitForProcessCompletion(HANDLE handle, LPDWORD exitCode, ILockedWaitingForProcess* uilock); private slots: -- cgit v1.3.1 From a5bb70f53bc20f36f695ac5cd9f8b91163f694eb Mon Sep 17 00:00:00 2001 From: Eran Mizrahi Date: Wed, 13 Dec 2017 16:18:54 +0200 Subject: Wait for injected processes on MO close --- src/CMakeLists.txt | 5 ++ src/ilockedwaitingforprocess.h | 2 +- src/lockeddialog.cpp | 37 ++----------- src/lockeddialog.h | 33 ++---------- src/lockeddialogbase.cpp | 73 +++++++++++++++++++++++++ src/lockeddialogbase.h | 62 +++++++++++++++++++++ src/mainwindow.cpp | 20 ++++++- src/mainwindow.h | 6 ++- src/organizercore.cpp | 45 +++++++++------- src/organizercore.h | 1 + src/waitingonclosedialog.cpp | 71 ++++++++++++++++++++++++ src/waitingonclosedialog.h | 56 +++++++++++++++++++ src/waitingonclosedialog.ui | 119 +++++++++++++++++++++++++++++++++++++++++ 13 files changed, 443 insertions(+), 87 deletions(-) create mode 100644 src/lockeddialogbase.cpp create mode 100644 src/lockeddialogbase.h create mode 100644 src/waitingonclosedialog.cpp create mode 100644 src/waitingonclosedialog.h create mode 100644 src/waitingonclosedialog.ui (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 39aabb91..914bdd12 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -42,7 +42,9 @@ SET(organizer_SRCS main.cpp loghighlighter.cpp logbuffer.cpp + lockeddialogbase.cpp lockeddialog.cpp + waitingonclosedialog.cpp loadmechanism.cpp installationmanager.cpp helper.cpp @@ -128,7 +130,9 @@ SET(organizer_HDRS mainwindow.h loghighlighter.h logbuffer.h + lockeddialogbase.h lockeddialog.h + waitingonclosedialog.h loadmechanism.h installationmanager.h helper.h @@ -200,6 +204,7 @@ SET(organizer_UIS messagedialog.ui mainwindow.ui lockeddialog.ui + waitingonclosedialog.ui installdialog.ui finddialog.ui editexecutablesdialog.ui diff --git a/src/ilockedwaitingforprocess.h b/src/ilockedwaitingforprocess.h index 5bf1f1ca..9475ddb9 100644 --- a/src/ilockedwaitingforprocess.h +++ b/src/ilockedwaitingforprocess.h @@ -6,7 +6,7 @@ class QString; class ILockedWaitingForProcess { public: - virtual bool unlockForced() = 0; + virtual bool unlockForced() const = 0; virtual void setProcessName(QString const &) = 0; }; diff --git a/src/lockeddialog.cpp b/src/lockeddialog.cpp index 09538a0f..143d5838 100644 --- a/src/lockeddialog.cpp +++ b/src/lockeddialog.cpp @@ -26,10 +26,8 @@ along with Mod Organizer. If not, see . #include // for Qt::FramelessWindowHint, etc LockedDialog::LockedDialog(QWidget *parent, bool unlockByButton) - : QDialog(parent) + : LockedDialogBase(parent, !unlockByButton) , ui(new Ui::LockedDialog) - , m_Unlocked(false) - , m_allowClose(!unlockByButton) { ui->setupUi(this); @@ -38,7 +36,7 @@ LockedDialog::LockedDialog(QWidget *parent, bool unlockByButton) // seem to work. We will ignore pressing the close button if unlockByButton == true Qt::WindowFlags flags = this->windowFlags() | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMinimizeButtonHint; - if (!unlockByButton) + if (m_allowClose) flags |= Qt::WindowCloseButtonHint; this->setWindowFlags(flags); @@ -48,13 +46,6 @@ LockedDialog::LockedDialog(QWidget *parent, bool unlockByButton) ui->verticalLayout->addItem( new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); } - - if (parent != nullptr) { - QPoint position = parent->mapToGlobal(QPoint(parent->width() / 2, parent->height() / 2)); - position.rx() -= this->width() / 2; - position.ry() -= this->height() / 2; - move(position); - } } LockedDialog::~LockedDialog() @@ -68,35 +59,13 @@ void LockedDialog::setProcessName(const QString &name) ui->processLabel->setText(name); } - -void LockedDialog::resizeEvent(QResizeEvent *event) -{ - QWidget *par = parentWidget(); - if (par != nullptr) { - QPoint position = par->mapToGlobal(QPoint(par->width() / 2, par->height() / 2)); - position.rx() -= event->size().width() / 2; - position.ry() -= event->size().height() / 2; - move(position); - } -} - void LockedDialog::on_unlockButton_clicked() { unlock(); } -void LockedDialog::reject() -{ - if (m_allowClose) - unlock(); -} - -bool LockedDialog::unlockForced() { - return m_Unlocked; -} - void LockedDialog::unlock() { - m_Unlocked = true; + LockedDialogBase::unlock(); ui->label->setText("unlocking may take a few seconds"); ui->unlockButton->setEnabled(false); } diff --git a/src/lockeddialog.h b/src/lockeddialog.h index 82a15a93..36c16429 100644 --- a/src/lockeddialog.h +++ b/src/lockeddialog.h @@ -17,16 +17,9 @@ You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see . */ -#ifndef LOCKEDDIALOG_H -#define LOCKEDDIALOG_H +#pragma once -#include "ilockedwaitingforprocess.h" -#include // for QDialog -#include // for Q_OBJECT, slots -#include // for QString - -class QResizeEvent; -class QWidget; +#include "lockeddialogbase.h" namespace Ui { class LockedDialog; @@ -40,7 +33,7 @@ namespace Ui { * data on which Mod Organizer works. After the UI is unlocked (manually or after the * external application closed) MO will refresh all of its data sources **/ -class LockedDialog : public QDialog, public ILockedWaitingForProcess +class LockedDialog : public LockedDialogBase { Q_OBJECT @@ -48,35 +41,17 @@ public: explicit LockedDialog(QWidget *parent = 0, bool unlockByButton = false); ~LockedDialog(); - /** - * @brief see if the user clicked the unlock-button - * - * @return true if the user clicked the unlock button - **/ - bool unlockForced() override; - - /** - * @brief set the name of the process being run - * @param name of process - */ void setProcessName(const QString &name) override; protected: - virtual void resizeEvent(QResizeEvent *event); - - virtual void reject(); + void unlock() override; private slots: void on_unlockButton_clicked(); private: - void unlock(); Ui::LockedDialog *ui; - bool m_Unlocked; - bool m_allowClose; }; - -#endif // LOCKEDDIALOG_H diff --git a/src/lockeddialogbase.cpp b/src/lockeddialogbase.cpp new file mode 100644 index 00000000..b18f7429 --- /dev/null +++ b/src/lockeddialogbase.cpp @@ -0,0 +1,73 @@ +/* +Copyright (C) 2012 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + +#include "lockeddialogbase.h" + +#include +#include +#include +#include // for Qt::FramelessWindowHint, etc + +LockedDialogBase::LockedDialogBase(QWidget *parent, bool allowClose) + : QDialog(parent) + , m_Unlocked(false) + , m_Canceled(false) + , m_allowClose(allowClose) +{ + if (parent != nullptr) { + QPoint position = parent->mapToGlobal(QPoint(parent->width() / 2, parent->height() / 2)); + position.rx() -= this->width() / 2; + position.ry() -= this->height() / 2; + move(position); + } +} + +void LockedDialogBase::resizeEvent(QResizeEvent *event) +{ + QWidget *par = parentWidget(); + if (par != nullptr) { + QPoint position = par->mapToGlobal(QPoint(par->width() / 2, par->height() / 2)); + position.rx() -= event->size().width() / 2; + position.ry() -= event->size().height() / 2; + move(position); + } +} + +void LockedDialogBase::reject() +{ + if (m_allowClose) + unlock(); +} + +bool LockedDialogBase::unlockForced() const { + return m_Unlocked; +} + +bool LockedDialogBase::canceled() const { + return m_Canceled; +} + +void LockedDialogBase::unlock() { + m_Unlocked = true; +} + +void LockedDialogBase::cancel() { + m_Canceled = true; +} + diff --git a/src/lockeddialogbase.h b/src/lockeddialogbase.h new file mode 100644 index 00000000..3c974a38 --- /dev/null +++ b/src/lockeddialogbase.h @@ -0,0 +1,62 @@ +/* +Copyright (C) 2012 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + +#pragma once + +#include "ilockedwaitingforprocess.h" +#include // for QDialog +#include // for Q_OBJECT, slots +#include // for QString + +class QResizeEvent; +class QWidget; + +/** + * a small borderless dialog displayed while the Mod Organizer UI is locked + * The dialog contains only a label and a button to force the UI to be unlocked + * + * The UI gets locked while running external applications since they may modify the + * data on which Mod Organizer works. After the UI is unlocked (manually or after the + * external application closed) MO will refresh all of its data sources + **/ +class LockedDialogBase : public QDialog, public ILockedWaitingForProcess +{ + Q_OBJECT + +public: + explicit LockedDialogBase(QWidget *parent, bool allowClose); + + bool unlockForced() const override; + + virtual bool canceled() const; + +protected: + + virtual void resizeEvent(QResizeEvent *event); + + virtual void reject(); + + virtual void unlock(); + + virtual void cancel(); + + bool m_Unlocked; + bool m_Canceled; + bool m_allowClose; +}; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 1e1a4558..85808e8d 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -59,6 +59,7 @@ along with Mod Organizer. If not, see . #include "messagedialog.h" #include "installationmanager.h" #include "lockeddialog.h" +#include "waitingonclosedialog.h" #include "logbuffer.h" #include "downloadlistsortproxy.h" #include "motddialog.h" @@ -855,6 +856,8 @@ void MainWindow::showEvent(QShowEvent *event) void MainWindow::closeEvent(QCloseEvent* event) { + m_closing = true; + if (m_OrganizerCore.downloadManager()->downloadsInProgress()) { if (QMessageBox::question(this, tr("Downloads in progress"), tr("There are still downloads in progress, do you really want to quit?"), @@ -866,6 +869,16 @@ void MainWindow::closeEvent(QCloseEvent* event) } } + HANDLE injected_process_still_running = m_OrganizerCore.findAndOpenAUSVFSProcess(); + if (injected_process_still_running != INVALID_HANDLE_VALUE) + { + m_OrganizerCore.waitForApplication(injected_process_still_running); + if (!m_closing) { // if operation cancelled + event->ignore(); + return; + } + } + setCursor(Qt::WaitCursor); } @@ -1471,7 +1484,10 @@ ILockedWaitingForProcess* MainWindow::lock() ++m_LockCount; return m_LockDialog; } - m_LockDialog = new LockedDialog(this, true); + if (m_closing) + m_LockDialog = new WaitingOnCloseDialog(this); + else + m_LockDialog = new LockedDialog(this, true); m_LockDialog->setModal(true); m_LockDialog->show(); setEnabled(false); @@ -1489,6 +1505,8 @@ void MainWindow::unlock() } --m_LockCount; if (m_LockCount == 0) { + if (m_closing && m_LockDialog->canceled()) + m_closing = false; m_LockDialog->hide(); m_LockDialog->deleteLater(); m_LockDialog = nullptr; diff --git a/src/mainwindow.h b/src/mainwindow.h index f6f11157..575bf020 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -35,7 +35,7 @@ along with Mod Organizer. If not, see . //when I get round to cleaning up main.cpp struct Executable; class CategoryFactory; -class LockedDialog; +class LockedDialogBase; class OrganizerCore; #include "plugincontainer.h" //class PluginContainer; class PluginListSortProxy; @@ -348,9 +348,11 @@ private: bool m_DidUpdateMasterList; - LockedDialog *m_LockDialog { nullptr }; + LockedDialogBase *m_LockDialog { nullptr }; uint64_t m_LockCount { 0 }; + bool m_closing{ false }; + std::vector> m_PersistedGeometry; enum class ShortcutType { diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 1f42bf95..c4bcaf3d 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -1367,26 +1367,8 @@ bool OrganizerCore::waitForProcessCompletion(HANDLE handle, LPDWORD exitCode, IL QThread::msleep(500); // search if there is another usvfs process active and if so wait for it - // in theory a querySize of 1 is probably enough since the MO process doesn't seem to be returned by GetVFSProcessList - constexpr size_t querySize = 2; // just to be on the safe side - DWORD pids[querySize]; - size_t found = querySize; - if (!::GetVFSProcessList(&found, pids)) { - qWarning() << "Failed waiting for process completion : GetVFSProcessList failed?!"; - break; - } - - for (size_t i = 0; i < found; ++i) { - if (pids[i] == GetCurrentProcessId()) - continue; // obviously don't wait for MO process - handle = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION|SYNCHRONIZE, FALSE, pids[i]); - if (handle == INVALID_HANDLE_VALUE) { - qWarning() << "Failed waiting for process completion : OpenProcess failed" << GetLastError(); - continue; - } - newHandle = true; - break; - } + handle = findAndOpenAUSVFSProcess(); + newHandle = handle != INVALID_HANDLE_VALUE; } } @@ -1403,6 +1385,29 @@ bool OrganizerCore::waitForProcessCompletion(HANDLE handle, LPDWORD exitCode, IL return res == WAIT_OBJECT_0; } +HANDLE OrganizerCore::findAndOpenAUSVFSProcess() { + // in theory a querySize of 1 is probably enough since the MO process doesn't seem to be returned by GetVFSProcessList + constexpr size_t querySize = 2; // just to be on the safe side + DWORD pids[querySize]; + size_t found = querySize; + if (!::GetVFSProcessList(&found, pids)) { + qWarning() << "Failed seeking USVFS processes : GetVFSProcessList failed?!"; + return INVALID_HANDLE_VALUE; + } + + for (size_t i = 0; i < found; ++i) { + if (pids[i] == GetCurrentProcessId()) + continue; // obviously don't wait for MO process + HANDLE handle = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE, FALSE, pids[i]); + if (handle != INVALID_HANDLE_VALUE) + return handle; + else + qWarning() << "Failed openning USVFS process " << pids[i] << " : OpenProcess failed" << GetLastError(); + } + + return INVALID_HANDLE_VALUE; +} + bool OrganizerCore::onAboutToRun( const std::function &func) { diff --git a/src/organizercore.h b/src/organizercore.h index f8806289..cf030e0a 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -206,6 +206,7 @@ public: HANDLE runShortcut(const QString &title); HANDLE startApplication(const QString &executable, const QStringList &args, const QString &cwd, const QString &profile); bool waitForApplication(HANDLE processHandle, LPDWORD exitCode = nullptr); + HANDLE findAndOpenAUSVFSProcess(); bool onModInstalled(const std::function &func); bool onAboutToRun(const std::function &func); bool onFinishedRun(const std::function &func); diff --git a/src/waitingonclosedialog.cpp b/src/waitingonclosedialog.cpp new file mode 100644 index 00000000..565d0a36 --- /dev/null +++ b/src/waitingonclosedialog.cpp @@ -0,0 +1,71 @@ +/* +Copyright (C) 2012 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + +#include "waitingonclosedialog.h" +#include "ui_waitingonclosedialog.h" + +#include +#include +#include +#include // for Qt::FramelessWindowHint, etc + +WaitingOnCloseDialog::WaitingOnCloseDialog(QWidget *parent) + : LockedDialogBase(parent,true) + , ui(new Ui::WaitingOnCloseDialog) +{ + ui->setupUi(this); + + // Supposedly the Qt::CustomizeWindowHint should use a customized window + // allowing us to select if there is a close button. In practice this doesn't + // seem to work. We will ignore pressing the close button if unlockByButton == true + Qt::WindowFlags flags = + this->windowFlags() | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMinimizeButtonHint; + if (m_allowClose) + flags |= Qt::WindowCloseButtonHint; + this->setWindowFlags(flags); +} + +WaitingOnCloseDialog::~WaitingOnCloseDialog() +{ + delete ui; +} + + +void WaitingOnCloseDialog::setProcessName(const QString &name) +{ + ui->processLabel->setText(name); +} + +void WaitingOnCloseDialog::on_closeButton_clicked() +{ + unlock(); +} + +void WaitingOnCloseDialog::on_cancelButton_clicked() +{ + cancel(); + unlock(); +} + +void WaitingOnCloseDialog::unlock() { + LockedDialogBase::unlock(); + ui->label->setText("unlocking may take a few seconds"); + ui->closeButton->setEnabled(false); + ui->cancelButton->setEnabled(false); +} diff --git a/src/waitingonclosedialog.h b/src/waitingonclosedialog.h new file mode 100644 index 00000000..6650c390 --- /dev/null +++ b/src/waitingonclosedialog.h @@ -0,0 +1,56 @@ +/* +Copyright (C) 2012 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + +#pragma once + +#include "lockeddialogbase.h" + +namespace Ui { + class WaitingOnCloseDialog; +} + +/** + * Similar to the LockedDialog but used for waiting on running process during + * a process close request which requries a slightly different dialog. + **/ +class WaitingOnCloseDialog : public LockedDialogBase +{ + Q_OBJECT + +public: + explicit WaitingOnCloseDialog(QWidget *parent = 0); + ~WaitingOnCloseDialog(); + + bool canceled() const { return m_Canceled; } + + void setProcessName(const QString &name) override; + +protected: + + void unlock() override; + +private slots: + + void on_closeButton_clicked(); + void on_cancelButton_clicked(); + +private: + + Ui::WaitingOnCloseDialog *ui; +}; diff --git a/src/waitingonclosedialog.ui b/src/waitingonclosedialog.ui new file mode 100644 index 00000000..9c7818e0 --- /dev/null +++ b/src/waitingonclosedialog.ui @@ -0,0 +1,119 @@ + + + WaitingOnCloseDialog + + + + 0 + 0 + 317 + 151 + + + + Waiting for virtualized processes + + + + + + This dialog should disappear automatically if the application/game is done. + + + Virtualized processes are still running, it is prefered to keep MO running until they are finished. + + + true + + + + + + + Qt::Vertical + + + + 10 + + + + + + + + + true + + + + color: grey; + + + + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 10 + + + + + + + + + + Close Now + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Cancel + + + + + + + + + Qt::Vertical + + + + 10 + + + + + + + + + -- cgit v1.3.1 From cb1c9f5e83e98039d38f548c80d4b95442163cf2 Mon Sep 17 00:00:00 2001 From: Eran Mizrahi Date: Tue, 19 Dec 2017 00:44:18 +0200 Subject: Tie moshortcuts to a specific instance --- src/CMakeLists.txt | 2 ++ src/executableslist.cpp | 2 +- src/instancemanager.cpp | 17 ++++++++++++++--- src/instancemanager.h | 8 ++++++-- src/main.cpp | 16 +++++++++++----- src/mainwindow.cpp | 3 ++- src/moshortcut.cpp | 39 +++++++++++++++++++++++++++++++++++++++ src/moshortcut.h | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/organizercore.cpp | 15 +++++++++++---- src/organizercore.h | 6 ++---- 10 files changed, 134 insertions(+), 20 deletions(-) create mode 100644 src/moshortcut.cpp create mode 100644 src/moshortcut.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 914bdd12..0c70fe57 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -88,6 +88,7 @@ SET(organizer_SRCS instancemanager.cpp usvfsconnector.cpp eventfilter.cpp + moshortcut.cpp shared/windows_error.cpp shared/error_report.cpp @@ -179,6 +180,7 @@ SET(organizer_HDRS usvfsconnector.h eventfilter.h descriptionpage.h + moshortcut.h shared/windows_error.h shared/error_report.h diff --git a/src/executableslist.cpp b/src/executableslist.cpp index b1890ac9..21cb6ed4 100644 --- a/src/executableslist.cpp +++ b/src/executableslist.cpp @@ -86,7 +86,7 @@ Executable &ExecutablesList::find(const QString &title) return exe; } } - throw std::runtime_error(QString("invalid name %1").arg(title).toLocal8Bit().constData()); + throw std::runtime_error(QString("invalid executable name %1").arg(title).toLocal8Bit().constData()); } diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index b1b85cc6..34e23d7b 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -47,9 +47,19 @@ InstanceManager &InstanceManager::instance() return s_Instance; } +void InstanceManager::overrideInstance(const QString& instanceName) +{ + m_overrideInstanceName = instanceName; + m_overrideInstance = true; +} + + QString InstanceManager::currentInstance() const { - return m_AppSettings.value(INSTANCE_KEY, "").toString(); + if (m_overrideInstance) + return m_overrideInstanceName; + else + return m_AppSettings.value(INSTANCE_KEY, "").toString(); } void InstanceManager::clearCurrentInstance() @@ -177,7 +187,8 @@ void InstanceManager::createDataPath(const QString &dataPath) const QString InstanceManager::determineDataPath() { QString instanceId = currentInstance(); - if (instanceId.isEmpty() && portableInstall() && !m_Reset) { + if (instanceId.isEmpty() && !m_Reset && (m_overrideInstance || portableInstall())) + { // startup, apparently using portable mode before return qApp->applicationDirPath(); } @@ -187,7 +198,7 @@ QString InstanceManager::determineDataPath() + "/" + instanceId); - if (instanceId.isEmpty() || !QFileInfo::exists(dataPath)) { + if (!m_overrideInstance && (instanceId.isEmpty() || !QFileInfo::exists(dataPath))) { instanceId = chooseInstance(instances()); setCurrentInstance(instanceId); if (!instanceId.isEmpty()) { diff --git a/src/instancemanager.h b/src/instancemanager.h index a0a5df09..2782c71d 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -34,11 +34,14 @@ public: QString determineDataPath(); void clearCurrentInstance(); + void overrideInstance(const QString& instanceName); + + QString currentInstance() const; + private: InstanceManager(); - QString currentInstance() const; QString instancePath() const; QStringList instances() const; @@ -55,5 +58,6 @@ private: QSettings m_AppSettings; bool m_Reset {false}; - + bool m_overrideInstance{false}; + QString m_overrideInstanceName; }; diff --git a/src/main.cpp b/src/main.cpp index 4cf47a3c..3d315f1d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,6 +45,7 @@ along with Mod Organizer. If not, see . #include "tutorialmanager.h" #include "nxmaccessmanager.h" #include "instancemanager.h" +#include "moshortcut.h" #include #include @@ -450,9 +451,9 @@ int runApplication(MOApplication &application, SingleInstance &instance, // if we have a command line parameter, it is either a nxm link or // a binary to start if (arguments.size() > 1) { - if (OrganizerCore::isMoShortcut(arguments.at(1))) { + if (MOShortcut shortcut{ arguments.at(1) }) { try { - organizer.runShortcut(OrganizerCore::moShortcutName(arguments.at(1))); + organizer.runShortcut(shortcut); return 0; } catch (const std::exception &e) { reportError( @@ -552,10 +553,12 @@ int main(int argc, char *argv[]) forcePrimary = true; } + MOShortcut moshortcut{ arguments.size() > 1 ? arguments.at(1) : "" }; + SingleInstance instance(forcePrimary); if (!instance.primaryInstance()) { - if ((arguments.size() == 2) - && (OrganizerCore::isMoShortcut(arguments.at(1)) || OrganizerCore::isNxmLink(arguments.at(1)))) + if (moshortcut || + arguments.size() > 1 && OrganizerCore::isNxmLink(arguments.at(1))) { qDebug("not primary instance, sending shortcut/download message"); instance.sendMessage(arguments.at(1)); @@ -572,7 +575,10 @@ int main(int argc, char *argv[]) QString dataPath; try { - dataPath = InstanceManager::instance().determineDataPath(); + InstanceManager& instanceManager = InstanceManager::instance(); + if (moshortcut && moshortcut.hasInstance()) + instanceManager.overrideInstance(moshortcut.instance()); + dataPath = instanceManager.determineDataPath(); } catch (const std::exception &e) { if (strcmp(e.what(),"Canceled")) QMessageBox::critical(nullptr, QObject::tr("Failed to set up instance"), e.what()); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 6f156269..1c5e321d 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3331,7 +3331,8 @@ void MainWindow::addWindowsLink(const ShortcutType mapping) QString executable = QDir::toNativeSeparators(selectedExecutable.m_BinaryInfo.absoluteFilePath()); std::wstring targetFile = ToWString(exeInfo.absoluteFilePath()); - std::wstring parameter = ToWString(QString("\"moshortcut://%1\"").arg(selectedExecutable.m_Title)); + std::wstring parameter = ToWString( + QString("\"moshortcut://%1:%2\"").arg(InstanceManager::instance().currentInstance(),selectedExecutable.m_Title)); std::wstring description = ToWString(QString("Run %1 with ModOrganizer").arg(selectedExecutable.m_Title)); std::wstring iconFile = ToWString(executable); std::wstring currentDirectory = ToWString(QDir::toNativeSeparators(qApp->applicationDirPath())); diff --git a/src/moshortcut.cpp b/src/moshortcut.cpp new file mode 100644 index 00000000..c8c2ef6f --- /dev/null +++ b/src/moshortcut.cpp @@ -0,0 +1,39 @@ +/* +Copyright (C) 2016 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + + +#include "moshortcut.h" + + +MOShortcut::MOShortcut(const QString& link) + : m_valid(link.startsWith("moshortcut://")) + , m_hasInstance(false) +{ + if (m_valid) { + int start = (int)strlen("moshortcut://"); + int sep = link.indexOf(':', start); + if (sep >= 0) { + m_hasInstance = true; + m_instance = link.mid(start, sep - start); + m_executable = link.mid(sep + 1); + } + else + m_executable = link.mid(start); + } +} diff --git a/src/moshortcut.h b/src/moshortcut.h new file mode 100644 index 00000000..7b7574b9 --- /dev/null +++ b/src/moshortcut.h @@ -0,0 +1,46 @@ +/* +Copyright (C) 2016 Sebastian Herbord. All rights reserved. + +This file is part of Mod Organizer. + +Mod Organizer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Mod Organizer is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Mod Organizer. If not, see . +*/ + + +#pragma once + + +#include + + +class MOShortcut { + +public: + MOShortcut(const QString& link); + + /// true iff intialized using a valid moshortcut link + operator bool() const { return m_valid; } + + bool hasInstance() const { return m_hasInstance; } + + const QString& instance() const { return m_instance; } + + const QString& executable() const { return m_executable; } + +private: + QString m_instance; + QString m_executable; + bool m_valid; + bool m_hasInstance; +}; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 3be9559a..7668485c 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -32,6 +32,7 @@ #include #include #include "lockeddialog.h" +#include "instancemanager.h" #include #include @@ -578,8 +579,8 @@ void OrganizerCore::downloadRequestedNXM(const QString &url) void OrganizerCore::externalMessage(const QString &message) { - if (isMoShortcut(message)) { - runShortcut(moShortcutName(message)); + if (MOShortcut moshortcut{ message }) { + runShortcut(moshortcut); } else if (isNxmLink(message)) { MessageDialog::showMessage(tr("Download started"), qApp->activeWindow()); @@ -1228,9 +1229,15 @@ HANDLE OrganizerCore::spawnBinaryProcess(const QFileInfo &binary, } } -HANDLE OrganizerCore::runShortcut(const QString &title) +HANDLE OrganizerCore::runShortcut(const MOShortcut& shortcut) { - Executable& exe = m_ExecutablesList.find(title); + if (shortcut.hasInstance() && shortcut.instance() != InstanceManager::instance().currentInstance()) + throw std::runtime_error( + QString("Refusing to run executable from different instance %1:%2") + .arg(shortcut.instance(),shortcut.executable()) + .toLocal8Bit().constData()); + + Executable& exe = m_ExecutablesList.find(shortcut.executable()); return spawnBinaryDirect( exe.m_BinaryInfo, exe.m_Arguments, diff --git a/src/organizercore.h b/src/organizercore.h index decb3d01..f98e9d0a 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -13,6 +13,7 @@ #include "downloadmanager.h" #include "executableslist.h" #include "usvfsconnector.h" +#include "moshortcut.h" #include #include #include @@ -86,9 +87,6 @@ private: public: static bool isNxmLink(const QString &link) { return link.startsWith("nxm://", Qt::CaseInsensitive); } - static bool isMoShortcut(const QString &link) { return link.startsWith("moshortcut://", Qt::CaseInsensitive); } - static QString moShortcutName(const QString &link) { return link.mid(strlen("moshortcut://")); } - OrganizerCore(const QSettings &initSettings); @@ -203,7 +201,7 @@ public: DownloadManager *downloadManager(); PluginList *pluginList(); ModList *modList(); - HANDLE runShortcut(const QString &title); + HANDLE runShortcut(const MOShortcut& shortcut); HANDLE startApplication(const QString &executable, const QStringList &args, const QString &cwd, const QString &profile); bool waitForApplication(HANDLE processHandle, LPDWORD exitCode = nullptr); HANDLE findAndOpenAUSVFSProcess(); -- cgit v1.3.1 From 80fc71327397ba5b85fb9739d299d2e4ec542828 Mon Sep 17 00:00:00 2001 From: Eran Mizrahi Date: Sat, 16 Dec 2017 12:41:57 +0200 Subject: fix usvfs includes (following usvfs VS project) --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0c70fe57..5e58a313 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -284,7 +284,7 @@ INCLUDE_DIRECTORIES(${project_path}/uibase/src ${project_path}/bsatk/src ${project_path}/esptk/src ${project_path}/archive/src - ${project_path}/../usvfs/usvfs + ${project_path}/../usvfs/include ${project_path}/game_gamebryo/src ${project_path}/game_features/src ${project_path}/githubpp/src) -- cgit v1.3.1 From 5c192fb8c577a4ec25c93bdcab80292fc4bfe0fb Mon Sep 17 00:00:00 2001 From: Eran Mizrahi Date: Tue, 19 Dec 2017 15:20:28 +0200 Subject: Use Qt deploy also for Qt plugins (cherry picking only the ones we need) --- src/CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5e58a313..db86392d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -344,7 +344,7 @@ INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dlls.manifest.qt5 RENAME dlls.manifest) # use windeployqt.exe to install all required libraries -SET(windeploy_parameters --no-translations --no-plugins --libdir dlls --release-with-debug-info --no-compiler-runtime) +SET(windeploy_parameters "--no-translations --plugindir qtplugins --libdir dlls --release-with-debug-info --no-compiler-runtime") INSTALL( CODE "EXECUTE_PROCESS( @@ -353,7 +353,10 @@ INSTALL( COMMAND ${qt5bin}/windeployqt.exe uibase.dll ${windeploy_parameters} WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin - )" + ) + file(RENAME ${CMAKE_INSTALL_PREFIX}/bin/qtplugins/platforms ${CMAKE_INSTALL_PREFIX}/bin/platforms) + file(RENAME ${CMAKE_INSTALL_PREFIX}/bin/qtplugins/imageformats ${CMAKE_INSTALL_PREFIX}/bin/dlls/imageformats) + file(REMOVE_RECURSE ${CMAKE_INSTALL_PREFIX}/bin/qtplugins)" ) INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/stylesheets -- cgit v1.3.1 From 0c75ff1836804dc312824009e68c8eb97dc32ea1 Mon Sep 17 00:00:00 2001 From: Eran Mizrahi Date: Wed, 20 Dec 2017 02:00:56 +0200 Subject: Fix modorganizer staging --- src/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index db86392d..f0ad2fe1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -337,7 +337,7 @@ FIND_PROGRAM(WINDEPLOYQT_COMMAND windeployqt PATHS ${qt5bin} NO_DEFAULT_PATH) INSTALL(TARGETS ModOrganizer RUNTIME DESTINATION bin) -INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/ModOrganizer.pdb +INSTALL(FILES $ DESTINATION pdb) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dlls.manifest.qt5 DESTINATION bin/dlls @@ -354,6 +354,8 @@ INSTALL( ${qt5bin}/windeployqt.exe uibase.dll ${windeploy_parameters} WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin ) + file(REMOVE_RECURSE ${CMAKE_INSTALL_PREFIX}/bin/platforms) + file(REMOVE_RECURSE ${CMAKE_INSTALL_PREFIX}/bin/dlls/imageformats) file(RENAME ${CMAKE_INSTALL_PREFIX}/bin/qtplugins/platforms ${CMAKE_INSTALL_PREFIX}/bin/platforms) file(RENAME ${CMAKE_INSTALL_PREFIX}/bin/qtplugins/imageformats ${CMAKE_INSTALL_PREFIX}/bin/dlls/imageformats) file(REMOVE_RECURSE ${CMAKE_INSTALL_PREFIX}/bin/qtplugins)" -- cgit v1.3.1 From a24a1cbf257f49fa31263e2f6115c378714a64d3 Mon Sep 17 00:00:00 2001 From: Eran Mizrahi Date: Wed, 20 Dec 2017 02:14:14 +0200 Subject: Hide boost compiler version warnings --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f0ad2fe1..7a339180 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -301,7 +301,7 @@ EXECUTE_PROCESS( OUTPUT_STRIP_TRAILING_WHITESPACE ) -ADD_DEFINITIONS(-D_UNICODE -DUNICODE -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS -DGITID="${GIT_COMMIT_HASH}") +ADD_DEFINITIONS(-D_UNICODE -DUNICODE -DNOMINMAX -D_CRT_SECURE_NO_WARNINGS -DBOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE -DGITID="${GIT_COMMIT_HASH}") IF("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") SET(usvfs_name usvfs_x64) -- cgit v1.3.1 From 74e88b00c8bbcc3b41b3f679c083e55d625b7756 Mon Sep 17 00:00:00 2001 From: Eran Mizrahi Date: Fri, 22 Dec 2017 11:34:43 +0200 Subject: Deploy also styles/qwindowsvistastyle.dll --- src/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7a339180..aa6502b7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -355,8 +355,10 @@ INSTALL( WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin ) file(REMOVE_RECURSE ${CMAKE_INSTALL_PREFIX}/bin/platforms) + file(REMOVE_RECURSE ${CMAKE_INSTALL_PREFIX}/bin/styles) file(REMOVE_RECURSE ${CMAKE_INSTALL_PREFIX}/bin/dlls/imageformats) file(RENAME ${CMAKE_INSTALL_PREFIX}/bin/qtplugins/platforms ${CMAKE_INSTALL_PREFIX}/bin/platforms) + file(RENAME ${CMAKE_INSTALL_PREFIX}/bin/qtplugins/styles ${CMAKE_INSTALL_PREFIX}/bin/styles) file(RENAME ${CMAKE_INSTALL_PREFIX}/bin/qtplugins/imageformats ${CMAKE_INSTALL_PREFIX}/bin/dlls/imageformats) file(REMOVE_RECURSE ${CMAKE_INSTALL_PREFIX}/bin/qtplugins)" ) -- cgit v1.3.1 From 1f34f7d8c1b75ad12ff284bc694c3a002a563438 Mon Sep 17 00:00:00 2001 From: Sandro Date: Thu, 22 Feb 2018 13:28:02 +0100 Subject: Make sure that we get all files for sure --- src/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index aa6502b7..ed6c836a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -350,6 +350,9 @@ INSTALL( "EXECUTE_PROCESS( COMMAND ${qt5bin}/windeployqt.exe ModOrganizer.exe --webenginewidgets ${windeploy_parameters} + # run it a second time because on the first run it misses some files + COMMAND + ${qt5bin}/windeployqt.exe ModOrganizer.exe --webenginewidgets ${windeploy_parameters} COMMAND ${qt5bin}/windeployqt.exe uibase.dll ${windeploy_parameters} WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin -- cgit v1.3.1