Compare commits

...
53 Commits
Author SHA1 Message Date
Tannin 352975e757 updated to use the unmanaged-mod game plugin feature 2016-06-19 16:01:51 +02:00
Tannin cd83f4e732 reduced number of mod content icons a bit because they take too much space 2016-06-19 16:01:15 +02:00
Tannin 4f25a0689f updated translation 2016-06-19 16:00:13 +02:00
Tannin 3d642563cb usvfs log level can now be changed without a restart 2016-06-19 15:59:50 +02:00
Tannin 0100c86887 download list and data tree now also maintain their layout between sessions 2016-06-19 15:58:37 +02:00
Tannin 8d04c77c29 updated version number and translation file 2016-06-05 14:23:27 +02:00
Tannin b935ce3862 bugfix: unnecessary repeat of delayed operations 2016-06-05 14:20:49 +02:00
Tannin fcebf21c44 fixed startup errors 2016-06-05 14:16:55 +02:00
Tannin 3d1dfadb40 automatic update of translation file during build 2016-06-04 15:29:52 +02:00
Tannin c2c48bd39a testing the hook for updating the translation-page 2016-06-04 14:33:24 +02:00
Tannin 9163a6dc34 added transifex configuration 2016-06-04 11:40:06 +02:00
Tannin 8bf3037d4b bugfix: changing from local language to english doesn't stick 2016-06-03 18:49:48 +02:00
Tannin 4f1bb36933 fixed some filename/modname handling for names received from nexus 2016-06-02 19:45:04 +02:00
Tannin cbdea13a9c minor cleanup 2016-06-01 19:34:01 +02:00
Tannin b36c666899 fixed: mandatory esms could get disabled without a way in the ui to re-enable them 2016-06-01 19:33:37 +02:00
Tannin bbab59f681 fixed handling of base dir in settings dialog (and fixed the tab order in the settings dialog) 2016-05-27 15:51:01 +02:00
Tannin bfbb190998 moved button to switch instance (and to portable mode) to the main window 2016-05-27 14:04:00 +02:00
Tannin c3b7737ae4 bugfix: incorrect url-encoding for passwords prevented login with some special characters 2016-05-19 19:18:46 +02:00
Tannin 30878ae394 added button to clear webcaches and cookies 2016-05-19 19:17:15 +02:00
Tannin 45a182a381 bugfix: initial setup didn't offer a portable install even if the global install dir is gone 2016-05-19 19:15:19 +02:00
Tannin 0b07cfa3f0 cleanup 2016-05-19 19:10:33 +02:00
Tannin 986330a3bd removed obsolete stuff 2016-05-16 16:03:36 +02:00
Tannin 81c5ca5308 Merge branch 'master' into new_vfs_library 2016-05-16 15:48:38 +02:00
Tannin 3a8da159da removed obsolete inject code 2016-05-16 14:46:54 +02:00
Tannin f6c3b516fd changes to primary category are now saved immediately instead of after the menu is closed 2016-05-15 15:09:14 +02:00
Tannin 5665be7fd1 fixed calls to helper.exe using wrong path 2016-05-15 14:58:54 +02:00
Tannin e522afa63b removed highlighting of dummy plugins 2016-05-15 13:07:46 +02:00
Tannin e3c3c2dcc4 fixed: after running an external application the plugin list was potentially not updated correctly 2016-05-15 13:06:31 +02:00
Tannin 519f215fd7 fixed saves list wasn't updated after removing a save game 2016-05-15 12:59:50 +02:00
TanninOne 89221af929 Merge pull request #677 from writemorecode/master
FIXED #302 Edited tooltip in Executables menu
2016-05-15 12:50:41 +02:00
Tannin 59a36107a1 fixed crash when disabling mods 2016-05-11 20:12:14 +02:00
Tannin bc5d5d8bfa ssl libraries can now be placed in dlls directory 2016-05-11 20:10:18 +02:00
Gustav Karlsson a294f8410f Edited tooltip in Executables menu
Edited a tooltip in the Executables menu.
2016-05-09 20:47:25 +02:00
TanninOne ba8464f01f Merge pull request #456 from ThosRTanner/issue/418
Refactor save game code into plugins
2016-02-10 18:37:28 +01:00
Thomas Tanner 0e9a86ebfc Merge branch 'master' into issue/418
# Conflicts:
#	SConstruct
#	src/organizerproxy.cpp
2016-01-24 18:10:25 +00:00
Thomas Tanner 8da9ba5404 Removing the 'get' in all function names and making FO4 plugin build
Merge branch 'master' of https://github.com/TanninOne/modorganizer
2016-01-24 15:25:38 +00:00
Thomas Tanner 387326f2d6 thoughts 2016-01-24 10:22:27 +00:00
Thomas Tanner d07b4f7ff0 Fixes an issue with doing connect for download cancel at the wrong time
Also cleaned up header usage per include-what-you-use
2015-12-31 23:08:24 +00:00
Thomas Tanner 197c49e996 Fixes for building with qt5.5 2015-12-31 23:06:52 +00:00
Thomas Tanner 0d50427b83 Patch to build with QT5.5 2015-12-30 16:38:47 +00:00
Thomas Tanner e90fbb1df2 Patch to build with QT5.5 2015-12-30 16:24:47 +00:00
Thomas Tanner a4dbe82be7 Removes the gameinfo + related files from shared to hookdll 2015-12-27 13:50:46 +00:00
Thomas Tanner 42417b6a01 Fix missing functions and also a bit of const correctness 2015-12-26 15:49:19 +00:00
Thomas Tanner f2b2ee1a11 Merge remote-tracking branch 'TanninOne/master' into issue/418 2015-12-26 15:27:34 +00:00
Thomas Tanner ae6bb99c52 Remove savegameinfowidget from organizer core 2015-12-26 08:23:43 +00:00
Thomas Tanner 3869eaaf2b Transferring the savegame widget into gamebryo + some cleanup 2015-12-25 21:57:23 +00:00
Thomas Tanner 6edf12f2f7 Rename the save game identifier method to make it clearer what it's for. 2015-12-20 21:22:38 +00:00
Thomas Tanner f55a5f2b21 Managed to remove SaveGame and SaveGameGamebryo classes from organizer 2015-12-20 21:08:14 +00:00
Thomas Tanner 5f5184ebfe Changes to support getting list of mods / esps to activate from game plugin 2015-12-19 20:49:54 +00:00
Thomas Tanner 30b29635f6 unsaved edits... 2015-12-15 16:00:15 +00:00
Thomas Tanner 0c536a4c12 Makes iSaveGame responsible for returning all files involved in savegame.
Cleans up save deletion and save transfer dialogue somewhat
2015-12-15 15:58:01 +00:00
Thomas Tanner 967c2ae653 Removes the SaveGame class from mainwindow. Using include what you use to add/remove #includes 2015-12-14 22:25:57 +00:00
Thomas Tanner ba176e5e9b Remove dependency of TransferSave on SaveGameBryo, part of fix for #418
Added a method to Profile class to get location of save games for the profile. I haven't added this to IProfile as am in two minds.

Aside from this have done cleanups of issues suggested by include-what-you-use, and some OCD sorting of #includes
2015-12-13 18:30:40 +00:00
70 changed files with 6626 additions and 1855 deletions
+82
View File
@@ -0,0 +1,82 @@
[main]
host = https://www.transifex.com
[mod-organizer.organizer]
file_filter = organizer/organizer_<lang>.ts
source_file = organizer/organizer_en.ts
source_lang = en
[mod-organizer.diagnosebasic]
file_filter = plugins/diagnoseBasic/diagnoseBasic_<lang>.ts
source_file = plugins/diagnoseBasic/diagnoseBasic_en.ts
source_lang = en
[mod-organizer.bsaextractor]
file_filter = plugins/bsaExtractor/bsaExtractor_<lang>.ts
source_file = plugins/bsaExtractor/bsaExtractor_en.ts
source_lang = en
[mod-organizer.checkfnis]
file_filter = plugins/checkFNIS/checkFNIS_<lang>.ts
source_file = plugins/checkFNIS/checkFNIS_en.ts
source_lang = en
[mod-organizer.inieditor]
file_filter = plugins/iniEditor/iniEditor_<lang>.ts
source_file = plugins/iniEditor/iniEditor_en.ts
source_lang = en
[mod-organizer.installerbain]
file_filter = plugins/installerBAIN/installerBAIN_<lang>.ts
source_file = plugins/installerBAIN/installerBAIN_en.ts
source_lang = en
[mod-organizer.installerbundle]
file_filter = plugins/installerBundle/installerBundle_<lang>.ts
source_file = plugins/installerBundle/installerBundle_en.ts
source_lang = en
[mod-organizer.installerfomod]
file_filter = plugins/installerFomod/installerFomod_<lang>.ts
source_file = plugins/installerFomod/installerFomod_en.ts
source_lang = en
[mod-organizer.installermanual]
file_filter = plugins/installerManual/installerManual_<lang>.ts
source_file = plugins/installerManual/installerManual_en.ts
source_lang = en
[mod-organizer.installerncc]
file_filter = plugins/installerNCC/installerNCC_<lang>.ts
source_file = plugins/installerNCC/installerNCC_en.ts
source_lang = en
[mod-organizer.installerquick]
file_filter = plugins/installerQuick/installerQuick_<lang>.ts
source_file = plugins/installerQuick/installerQuick_en.ts
source_lang = en
[mod-organizer.nmmimport]
file_filter = plugins/NMMImport/NMMImport_<lang>.ts
source_file = plugins/NMMImport/NMMImport_en.ts
source_lang = en
[mod-organizer.previewbase]
file_filter = plugins/previewBase/previewBase_<lang>.ts
source_file = plugins/previewBase/previewBase_en.ts
source_lang = en
[mod-organizer.previewdds]
file_filter = plugins/previewDDS/previewDDS_<lang>.ts
source_file = plugins/previewDDS/previewDDS_en.ts
source_lang = en
[mod-organizer.proxypython]
file_filter = plugins/proxyPython/proxyPython_<lang>.ts
source_file = plugins/proxyPython/proxyPython_en.ts
source_lang = en
[mod-organizer.configurator]
file_filter = plugins/pyniEdit/pyniEdit_<lang>.ts
source_file = plugins/pyniEdit/pyniEdit_en.ts
source_lang = en
+3 -3
View File
@@ -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
@@ -239,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})
@@ -297,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}
+3 -3
View File
@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>746</width>
<width>604</width>
<height>369</height>
</rect>
</property>
@@ -151,7 +151,7 @@
</widget>
<widget class="QWidget" name="credits">
<attribute name="title">
<string>Credits</string>
<string>Thanks</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
@@ -239,7 +239,7 @@
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Others</string>
<string>Other Supporter</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
-5
View File
@@ -35,13 +35,8 @@ ActivateModsDialog::ActivateModsDialog(SaveGameInfo::MissingAssets const &missin
QTableWidget *modsTable = findChild<QTableWidget*>("modsTable");
QHeaderView *headerView = modsTable->horizontalHeader();
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
headerView->setSectionResizeMode(0, QHeaderView::Stretch);
headerView->setSectionResizeMode(1, QHeaderView::Interactive);
#else
headerView->setResizeMode(0, QHeaderView::Stretch);
headerView->setResizeMode(1, QHeaderView::Interactive);
#endif
int row = 0;
+6 -10
View File
@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>282</width>
<width>294</width>
<height>156</height>
</rect>
</property>
@@ -25,29 +25,25 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Username</string>
</property>
</widget>
</item>
<item>
<item row="0" column="1">
<widget class="QLineEdit" name="usernameEdit"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Password</string>
</property>
</widget>
</item>
<item>
<item row="1" column="1">
<widget class="QLineEdit" name="passwordEdit">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
+2
View File
@@ -4,6 +4,8 @@
<file name="icuin53.dll"/>
<file name="icuuc53.dll"/>
<file name="icudt53.dll"/>
<file name="libeay32.dll"/>
<file name="ssleay32.dll"/>
<file name="Qt5Core.dll"/>
<file name="Qt5Declarative.dll"/>
<file name="Qt5Gui.dll"/>
+12 -9
View File
@@ -757,12 +757,15 @@ QString DownloadManager::getDisplayName(int index) const
DownloadInfo *info = m_ActiveDownloads.at(index);
QTextDocument doc;
if (!info->m_FileInfo->name.isEmpty()) {
return QString("%1 (%2, v%3)").arg(info->m_FileInfo->name)
doc.setHtml(info->m_FileInfo->name);
return QString("%1 (%2, v%3)").arg(doc.toPlainText())
.arg(getFileTypeString(info->m_FileInfo->fileCategory))
.arg(info->m_FileInfo->version.displayString());
} else {
return info->m_FileName;
doc.setHtml(info->m_FileName);
return doc.toPlainText();
}
}
@@ -1058,7 +1061,9 @@ void DownloadManager::nxmDescriptionAvailable(int, QVariant userData, QVariant r
DownloadInfo *info = downloadInfoByID(userData.toInt());
if (info == nullptr) return;
info->m_FileInfo->categoryID = result["category_id"].toInt();
info->m_FileInfo->modName = result["name"].toString().trimmed();
QTextDocument doc;
doc.setHtml(result["name"].toString().trimmed());
info->m_FileInfo->modName = doc.toPlainText();
info->m_FileInfo->newestVersion.parse(result["version"].toString());
if (info->m_FileInfo->fileID != 0) {
setState(info, STATE_READY);
@@ -1116,7 +1121,7 @@ void DownloadManager::nxmFilesAvailable(int, QVariant userData, QVariant resultD
bool found = false;
foreach(QVariant file, result) {
for (QVariant file : result) {
QVariantMap fileInfo = file.toMap();
QString fileName = fileInfo["uri"].toString();
QString fileNameVariant = fileName.mid(0).replace(' ', '_');
@@ -1127,12 +1132,10 @@ void DownloadManager::nxmFilesAvailable(int, QVariant userData, QVariant resultD
if (!info->m_FileInfo->version.isValid()) {
info->m_FileInfo->version = info->m_FileInfo->newestVersion;
}
//Nexus has HTMLd these so unhtml them if necessary
// we receive some names html-encoded. This is used to decode it
QTextDocument doc;
doc.setHtml(info->m_FileInfo->modName);
doc.setHtml(fileInfo["modName"].toString());
info->m_FileInfo->modName = doc.toPlainText();
doc.setHtml(info->m_FileInfo->name);
info->m_FileInfo->name = doc.toPlainText();
info->m_FileInfo->fileCategory = convertFileCategory(fileInfo["category_id"].toInt());
info->m_FileInfo->fileTime = matchDate(fileInfo["date"].toString());
info->m_FileInfo->fileID = fileInfo["id"].toInt();
@@ -1148,7 +1151,7 @@ void DownloadManager::nxmFilesAvailable(int, QVariant userData, QVariant resultD
emit showMessage(tr("No matching file found on Nexus! Maybe this file is no longer available or it was renamed?"));
} else {
SelectionDialog selection(tr("No file on Nexus matches the selected file by name. Please manually choose the correct one."));
foreach(QVariant file, result) {
for (QVariant file : result) {
QVariantMap fileInfo = file.toMap();
selection.addChoice(fileInfo["uri"].toString(), "", file);
}
+1 -1
View File
@@ -24,7 +24,7 @@ QList<QString> GenericIconDelegate::getIcons(const QModelIndex &index) const
{
QList<QString> result;
if (index.isValid()) {
foreach (const QVariant &var, index.data(m_Role).toList()) {
for (const QVariant &var : index.data(m_Role).toList()) {
if (!m_Compact || !var.toString().isEmpty()) {
result.append(var.toString());
}
+5 -5
View File
@@ -62,7 +62,7 @@ static bool helperExec(LPCWSTR moDirectory, LPCWSTR commandLine)
}
bool init(const std::wstring &moDirectory)
bool init(const std::wstring &moPath, const std::wstring &dataPath)
{
DWORD userNameLen = UNLEN + 1;
wchar_t userName[UNLEN + 1];
@@ -74,22 +74,22 @@ bool init(const std::wstring &moDirectory)
wchar_t *commandLine = new wchar_t[32768];
_snwprintf(commandLine, 32768, L"init \"%ls\" \"%ls\"",
moDirectory.c_str(), userName);
dataPath.c_str(), userName);
bool res = helperExec(moDirectory.c_str(), commandLine);
bool res = helperExec(moPath.c_str(), commandLine);
delete [] commandLine;
return res;
}
bool backdateBSAs(const std::wstring &moDirectory, const std::wstring &dataPath)
bool backdateBSAs(const std::wstring &moPath, const std::wstring &dataPath)
{
wchar_t *commandLine = new wchar_t[32768];
_snwprintf(commandLine, 32768, L"backdateBSA \"%ls\"",
dataPath.c_str());
bool res = helperExec(moDirectory.c_str(), commandLine);
bool res = helperExec(moPath.c_str(), commandLine);
delete [] commandLine;
return res;
+4 -4
View File
@@ -38,17 +38,17 @@ namespace Helper {
* This will create all required sub-directories and give the user running ModOrganizer
* write-access
*
* @param moDirectory absolute path to the ModOrganizer base directory
* @param moPath absolute path to the ModOrganizer base directory
* @return true on success
**/
bool init(const std::wstring &moDirectory);
bool init(const std::wstring &moPath, const std::wstring &dataPath);
/**
* @brief sets the last modified time for all .bsa-files in the target directory well into the past
* @param moDirectory absolute path to the modOrganizer base directory
* @param moPath absolute path to the modOrganizer base directory
* @param dataPath the path taht contains the .bsa-files, usually the data directory of the game
**/
bool backdateBSAs(const std::wstring &moDirectory, const std::wstring &dataPath);
bool backdateBSAs(const std::wstring &moPath, const std::wstring &dataPath);
}
+6 -2
View File
@@ -48,6 +48,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QDateTime>
#include <QDirIterator>
#include <QDebug>
#include <QTextDocument>
#include <Shellapi.h>
@@ -669,7 +670,9 @@ bool InstallationManager::install(const QString &fileName,
if (QFile(metaName).exists()) {
QSettings metaFile(metaName, QSettings::IniFormat);
modID = metaFile.value("modID", 0).toInt();
modName.update(metaFile.value("name", "").toString(), GUESS_FALLBACK);
QTextDocument doc;
doc.setHtml(metaFile.value("name", "").toString());
modName.update(doc.toPlainText(), GUESS_FALLBACK);
modName.update(metaFile.value("modName", "").toString(), GUESS_META);
version = metaFile.value("version", "").toString();
@@ -688,12 +691,13 @@ bool InstallationManager::install(const QString &fileName,
{ // guess the mod name and mod if from the file name if there was no meta information
QString guessedModName;
int guessedModID = modID;
NexusInterface::interpretNexusFileName(QFileInfo(fileName).baseName(), guessedModName, guessedModID, false);
NexusInterface::interpretNexusFileName(QFileInfo(fileName).fileName(), guessedModName, guessedModID, false);
if ((modID == 0) && (guessedModID != -1)) {
modID = guessedModID;
} else if (modID != guessedModID) {
qDebug("passed mod id: %d, guessed id: %d", modID, guessedModID);
}
modName.update(guessedModName, GUESS_GOOD);
}
+52 -52
View File
@@ -27,6 +27,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QStandardPaths>
#include <QInputDialog>
#include <QMessageBox>
#include <cstdint>
static const char COMPANY_NAME[] = "Tannin";
@@ -40,6 +41,11 @@ InstanceManager::InstanceManager()
{
}
InstanceManager &InstanceManager::instance()
{
static InstanceManager s_Instance;
return s_Instance;
}
QString InstanceManager::currentInstance() const
{
@@ -49,6 +55,7 @@ QString InstanceManager::currentInstance() const
void InstanceManager::clearCurrentInstance()
{
setCurrentInstance("");
m_Reset = true;
}
void InstanceManager::setCurrentInstance(const QString &name)
@@ -81,26 +88,51 @@ QString InstanceManager::queryInstanceName() const
QString InstanceManager::chooseInstance(const QStringList &instanceList) const
{
SelectionDialog selection(QObject::tr("Choose Instance"), nullptr);
enum class Special : uint8_t {
NewInstance,
Portable
};
SelectionDialog selection(
QString("<h3>%1</h3><br>%2")
.arg(QObject::tr("Choose Instance"))
.arg(QObject::tr(
"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).")),
nullptr);
selection.disableCancel();
for (const QString &instance : instanceList) {
selection.addChoice(instance, "", instance);
}
selection.addChoice(QObject::tr("New"),
selection.addChoice(QIcon(":/MO/gui/add"), QObject::tr("New"),
QObject::tr("Create a new instance."),
"");
static_cast<uint8_t>(Special::NewInstance));
if (QFileInfo(qApp->applicationDirPath()).isWritable()) {
selection.addChoice(QIcon(":/MO/gui/package"), QObject::tr("Portable"),
QObject::tr("Use MO folder for data."),
static_cast<uint8_t>(Special::Portable));
}
if (selection.exec() == QDialog::Rejected) {
qDebug("rejected");
throw MOBase::MyException(QObject::tr("Canceled"));
}
QString choice = selection.getChoiceData().toString();
QVariant choice = selection.getChoiceData();
if (choice.isEmpty()) {
return queryInstanceName();
if (choice.type() == QVariant::String) {
return choice.toString();
} else {
return choice;
switch (choice.value<uint8_t>()) {
case Special::NewInstance: return queryInstanceName();
case Special::Portable: return QString();
default: throw std::runtime_error("invalid selection");
}
}
}
@@ -125,27 +157,6 @@ bool InstanceManager::portableInstall() const
}
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()) {
@@ -166,41 +177,30 @@ void InstanceManager::createDataPath(const QString &dataPath) const
QString InstanceManager::determineDataPath()
{
QString instanceId = currentInstance();
if (instanceId.isEmpty() && portableInstall() && !m_Reset) {
// startup, apparently using portable mode before
return qApp->applicationDirPath();
}
if (instanceId.isEmpty() && !portableInstall()) {
// no portable install and no selected instance
QString dataPath = QDir::fromNativeSeparators(
QStandardPaths::writableLocation(QStandardPaths::DataLocation)
+ "/" + instanceId);
QStringList instanceList = instances();
if (instanceList.size() == 0) {
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);
if (instanceId.isEmpty() || !QFileInfo::exists(dataPath)) {
instanceId = chooseInstance(instances());
if (!instanceId.isEmpty()) {
dataPath = QDir::fromNativeSeparators(
QStandardPaths::writableLocation(QStandardPaths::DataLocation)
+ "/" + instanceId);
}
}
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;
+6 -9
View File
@@ -27,25 +27,21 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
class InstanceManager {
enum class InstallationMode {
PORTABLE,
REGULAR
};
public:
InstanceManager();
static InstanceManager &instance();
QString determineDataPath();
QStringList instances() const;
void clearCurrentInstance();
private:
InstanceManager();
QString currentInstance() const;
QString instancePath() const;
bool portableInstall() const;
QStringList instances() const;
void setCurrentInstance(const QString &name);
@@ -53,10 +49,11 @@ private:
QString chooseInstance(const QStringList &instanceList) const;
void createDataPath(const QString &dataPath) const;
InstallationMode queryInstallMode() const;
bool portableInstall() const;
private:
QSettings m_AppSettings;
bool m_Reset {false};
};
+4 -6
View File
@@ -27,7 +27,6 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <windows.h>
#include <DbgHelp.h>
#include <inject.h>
#include <appconfig.h>
#include <utility.h>
#include <scopeguard.h>
@@ -104,7 +103,8 @@ bool createAndMakeWritable(const std::wstring &subPath)
"will be made writable for the current user account). You will be asked to run "
"\"helper.exe\" with administrative rights."),
QMessageBox::Yes | QMessageBox::Cancel) == QMessageBox::Yes) {
if (!Helper::init(dataPath.toStdWString())) {
if (!Helper::init(qApp->applicationDirPath().toStdWString(),
dataPath.toStdWString())) {
return false;
}
} else {
@@ -533,13 +533,13 @@ int runApplication(MOApplication &application, SingleInstance &instance,
// 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;
}
}
return 0;
}
NexusInterface::instance()->getAccessManager()->startLoginCheck();
@@ -630,13 +630,12 @@ int main(int argc, char *argv[])
QString dataPath;
try {
dataPath = InstanceManager().determineDataPath();
dataPath = InstanceManager::instance().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");
@@ -647,7 +646,6 @@ int main(int argc, char *argv[])
}
int result = runApplication(application, instance, splash);
if (result != INT_MAX) {
return result;
}
+71 -51
View File
@@ -37,6 +37,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "savegameinfo.h"
#include "spawn.h"
#include "versioninfo.h"
#include "instancemanager.h"
#include "report.h"
#include "modlist.h"
@@ -241,12 +242,14 @@ MainWindow::MainWindow(QSettings &initSettings
ui->modList->setItemDelegateForColumn(ModList::COL_FLAGS, new ModFlagIconDelegate(ui->modList));
ui->modList->setItemDelegateForColumn(ModList::COL_CONTENT, contentDelegate);
ui->modList->header()->installEventFilter(m_OrganizerCore.modList());
if (initSettings.contains("mod_list_state")) {
ui->modList->header()->restoreState(initSettings.value("mod_list_state").toByteArray());
bool modListAdjusted = registerWidgetState(ui->modList->objectName(), ui->modList->header(), "mod_list_state");
if (modListAdjusted) {
// hack: force the resize-signal to be triggered because restoreState doesn't seem to do that
ui->modList->header()->resizeSection(ModList::COL_CONTENT, ui->modList->header()->sectionSize(ModList::COL_CONTENT) + 1);
ui->modList->header()->resizeSection(ModList::COL_CONTENT, ui->modList->header()->sectionSize(ModList::COL_CONTENT) - 1);
int sectionSize = ui->modList->header()->sectionSize(ModList::COL_CONTENT);
ui->modList->header()->resizeSection(ModList::COL_CONTENT, sectionSize + 1);
ui->modList->header()->resizeSection(ModList::COL_CONTENT, sectionSize);
} else {
// hide these columns by default
ui->modList->header()->setSectionHidden(ModList::COL_CONTENT, true);
@@ -263,15 +266,17 @@ MainWindow::MainWindow(QSettings &initSettings
ui->espList->setModel(m_PluginListSortProxy);
ui->espList->sortByColumn(PluginList::COL_PRIORITY, Qt::AscendingOrder);
ui->espList->setItemDelegateForColumn(PluginList::COL_FLAGS, new GenericIconDelegate(ui->espList));
if (initSettings.contains("plugin_list_state")) {
ui->espList->header()->restoreState(initSettings.value("plugin_list_state").toByteArray());
}
ui->espList->installEventFilter(m_OrganizerCore.pluginList());
bool pluginListAdjusted = registerWidgetState(ui->espList->objectName(), ui->espList->header(), "plugin_list_state");
registerWidgetState(ui->dataTree->objectName(), ui->dataTree->header());
registerWidgetState(ui->downloadView->objectName(),
ui->downloadView->header());
ui->splitter->setStretchFactor(0, 3);
ui->splitter->setStretchFactor(1, 2);
resizeLists(initSettings.contains("mod_list_state"), initSettings.contains("plugin_list_state"));
resizeLists(modListAdjusted, pluginListAdjusted);
QMenu *linkMenu = new QMenu(this);
linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Toolbar"), this, SLOT(linkToolbar()));
@@ -288,7 +293,6 @@ MainWindow::MainWindow(QSettings &initSettings
// don't allow mouse wheel to switch grouping, too many people accidentally
// turn on grouping and then don't understand what happened
EventFilter *noWheel
= new EventFilter(this, [](QObject *, QEvent *event) -> bool {
return event->type() == QEvent::Wheel;
@@ -1260,7 +1264,6 @@ void MainWindow::refreshDataTree()
updateTo(subTree, L"", *m_OrganizerCore.directoryStructure(), conflictsBox->isChecked());
tree->insertTopLevelItem(0, subTree);
subTree->setExpanded(true);
tree->header()->resizeSection(0, 200);
}
@@ -1422,12 +1425,7 @@ void MainWindow::readSettings()
}
}
void MainWindow::storeSettings(QSettings &settings)
{
settings.setValue("mod_list_state", ui->modList->header()->saveState());
settings.setValue("plugin_list_state", ui->espList->header()->saveState());
void MainWindow::storeSettings(QSettings &settings) {
settings.setValue("group_state", ui->groupCombo->currentIndex());
settings.setValue("window_geometry", saveGeometry());
@@ -1438,7 +1436,13 @@ void MainWindow::storeSettings(QSettings &settings)
settings.setValue("filters_visible", ui->displayCategoriesBtn->isChecked());
settings.setValue("selected_executable", ui->executablesListBox->currentIndex());
settings.setValue("selected_executable",
ui->executablesListBox->currentIndex());
for (const std::pair<QString, QHeaderView*> kv : m_PersistedGeometry) {
QString key = QString("geometry/") + kv.first;
settings.setValue(key, kv.second->saveState());
}
}
void MainWindow::lock()
@@ -2676,31 +2680,6 @@ void MainWindow::replaceCategories_MenuHandler() {
refreshFilters();
}
void MainWindow::savePrimaryCategory()
{
QMenu *menu = qobject_cast<QMenu*>(sender());
if (menu == nullptr) {
qCritical("not a menu?");
return;
}
for (QAction* action : menu->actions()) {
QWidgetAction *widgetAction = qobject_cast<QWidgetAction*>(action);
if (widgetAction != nullptr) {
QRadioButton *btn = qobject_cast<QRadioButton*>(widgetAction->defaultWidget());
if (btn->isChecked()) {
QModelIndexList selected = ui->modList->selectionModel()->selectedRows();
for (int i = 0; i < selected.size(); ++i) {
ModInfo::Ptr modInfo = ModInfo::getByIndex(m_ModListSortProxy->mapToSource(selected.at(i)).row());
modInfo->setPrimaryCategory(widgetAction->data().toInt());
}
break;
}
}
}
}
void MainWindow::checkModsForUpdates()
{
statusBar()->show();
@@ -2760,15 +2739,21 @@ void MainWindow::unignoreUpdate()
info->ignoreUpdate(false);
}
void MainWindow::addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu, ModInfo::Ptr info)
{
void MainWindow::addPrimaryCategoryCandidates(QMenu *primaryCategoryMenu,
ModInfo::Ptr info) {
const std::set<int> &categories = info->getCategories();
for (int categoryID : categories) {
int catIdx = m_CategoryFactory.getCategoryIndex(categoryID);
QWidgetAction *action = new QWidgetAction(primaryCategoryMenu);
try {
QRadioButton *categoryBox = new QRadioButton(m_CategoryFactory.getCategoryName(catIdx).replace('&', "&&"),
primaryCategoryMenu);
QRadioButton *categoryBox = new QRadioButton(
m_CategoryFactory.getCategoryName(catIdx).replace('&', "&&"),
primaryCategoryMenu);
connect(categoryBox, &QRadioButton::toggled, [info, categoryID](bool enable) {
if (enable) {
info->setPrimaryCategory(categoryID);
}
});
categoryBox->setChecked(categoryID == info->getPrimaryCategory());
action->setDefaultWidget(categoryBox);
} catch (const std::exception &e) {
@@ -2932,7 +2917,6 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos)
QMenu *primaryCategoryMenu = new QMenu(tr("Primary Category"));
connect(primaryCategoryMenu, SIGNAL(aboutToShow()), this, SLOT(addPrimaryCategoryCandidates()));
connect(primaryCategoryMenu, SIGNAL(aboutToHide()), this, SLOT(savePrimaryCategory()));
addMenuAsPushButton(menu, primaryCategoryMenu);
menu->addSeparator();
@@ -3069,6 +3053,7 @@ void MainWindow::deleteSavegame_clicked()
.arg(savesMsgLabel),
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
shellDelete(deleteFiles, true); // recycle bin delete.
refreshSaveList();
}
}
@@ -3237,6 +3222,8 @@ void MainWindow::on_actionSettings_triggered()
NexusInterface::instance()->setNMMVersion(m_OrganizerCore.settings().getNMMVersion());
updateDownloadListDelegate();
m_OrganizerCore.setLogLevel(m_OrganizerCore.settings().logLevel());
}
@@ -3902,6 +3889,17 @@ void MainWindow::on_actionProblems_triggered()
}
}
void MainWindow::on_actionChange_Game_triggered()
{
if (QMessageBox::question(this, tr("Are you sure?"),
tr("This will restart MO, continue?"),
QMessageBox::Yes | QMessageBox::Cancel)
== QMessageBox::Yes) {
InstanceManager::instance().clearCurrentInstance();
qApp->exit(INT_MAX);
}
}
void MainWindow::setCategoryListVisible(bool visible)
{
if (visible) {
@@ -4339,10 +4337,7 @@ void MainWindow::on_bossButton_clicked()
QFile::remove(m_OrganizerCore.currentProfile()->getLoadOrderFileName());
}
m_OrganizerCore.refreshESPList();
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();
}
m_OrganizerCore.savePluginList();
}
}
@@ -4557,6 +4552,31 @@ void MainWindow::dropLocalFile(const QUrl &url, const QString &outputDir, bool m
}
}
bool MainWindow::registerWidgetState(const QString &name, QHeaderView *view, const char *oldSettingName) {
// register the view so it's geometry gets saved at exit
m_PersistedGeometry.push_back(std::make_pair(name, view));
// also, restore the geometry if it was saved before
QSettings &settings = m_OrganizerCore.settings().directInterface();
QString key = QString("geometry/%1").arg(name);
QByteArray data;
if ((oldSettingName != nullptr) && settings.contains(oldSettingName)) {
data = settings.value(oldSettingName).toByteArray();
settings.remove(oldSettingName);
} else if (settings.contains(key)) {
data = settings.value(key).toByteArray();
}
if (!data.isEmpty()) {
view->restoreState(data);
return true;
} else {
return false;
}
}
void MainWindow::dropEvent(QDropEvent *event)
{
Qt::DropAction action = event->proposedAction();
+8 -1
View File
@@ -60,6 +60,7 @@ namespace MOShared { class DirectoryEntry; }
#include <QStringList>
#include <QTime>
#include <QTimer>
#include <QHeaderView>
#include <QVariant>
#include <Qt>
@@ -183,6 +184,9 @@ protected:
virtual void dragEnterEvent(QDragEnterEvent *event);
virtual void dropEvent(QDropEvent *event);
private slots:
void on_actionChange_Game_triggered();
private slots:
void on_clickBlankButton_clicked();
@@ -281,6 +285,8 @@ private:
void dropLocalFile(const QUrl &url, const QString &outputDir, bool move);
bool registerWidgetState(const QString &name, QHeaderView *view, const char *oldSettingName = nullptr);
private:
static const char *PATTERN_BACKUP_GLOB;
@@ -345,6 +351,8 @@ private:
LockedDialog *m_LockDialog { nullptr };
std::vector<std::pair<QString, QHeaderView*>> m_PersistedGeometry;
enum class ShortcutType {
Toolbar,
Desktop,
@@ -430,7 +438,6 @@ private slots:
void addRemoveCategories_MenuHandler();
void replaceCategories_MenuHandler();
void savePrimaryCategory();
void addPrimaryCategoryCandidates();
void modDetailsUpdated(bool success);
+13
View File
@@ -1197,6 +1197,7 @@ p, li { white-space: pre-wrap; }
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionChange_Game"/>
<addaction name="actionInstallMod"/>
<addaction name="actionNexus"/>
<addaction name="actionAdd_Profile"/>
@@ -1383,6 +1384,18 @@ Right now this has very limited functionality</string>
<string>Ctrl+C</string>
</property>
</action>
<action name="actionChange_Game">
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/MO/gui/app_icon</normaloff>:/MO/gui/app_icon</iconset>
</property>
<property name="text">
<string>Change Game</string>
</property>
<property name="toolTip">
<string>Open the game selection dialog</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
-9
View File
@@ -129,18 +129,9 @@ bool MOApplication::notify(QObject *receiver, QEvent *event)
void MOApplication::updateStyle(const QString &fileName)
{
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
if (fileName == "Fusion") {
setStyle(QStyleFactory::create("fusion"));
setStyleSheet("");
#else
if (fileName == "Plastique") {
setStyle(new ProxyStyle(new QPlastiqueStyle));
setStyleSheet("");
} else if (fileName == "Cleanlooks") {
setStyle(new ProxyStyle(new QCleanlooksStyle));
setStyleSheet("");
#endif
} else {
setStyle(new ProxyStyle(QStyleFactory::create(m_DefaultStyle)));
if (QFile::exists(fileName)) {
+3 -3
View File
@@ -12,8 +12,7 @@ ModFlagIconDelegate::ModFlagIconDelegate(QObject *parent)
{
}
QList<QString> ModFlagIconDelegate::getIcons(const QModelIndex &index) const
{
QList<QString> ModFlagIconDelegate::getIcons(const QModelIndex &index) const {
QList<QString> result;
QVariant modid = index.data(Qt::UserRole + 1);
if (modid.isValid()) {
@@ -21,7 +20,8 @@ QList<QString> ModFlagIconDelegate::getIcons(const QModelIndex &index) const
std::vector<ModInfo::EFlag> flags = info->getFlags();
{ // insert conflict icon first to provide nicer alignment
auto iter = std::find_first_of(flags.begin(), flags.end(), m_ConflictFlags, m_ConflictFlags + 4);
auto iter = std::find_first_of(flags.begin(), flags.end(),
m_ConflictFlags, m_ConflictFlags + 4);
if (iter != flags.end()) {
result.append(getFlagIcon(*iter));
flags.erase(iter);
+14 -27
View File
@@ -35,6 +35,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <versioninfo.h>
#include <appconfig.h>
#include <scriptextender.h>
#include <unmanagedmods.h>
#include <QApplication>
#include <QDirIterator>
@@ -75,11 +76,13 @@ ModInfo::Ptr ModInfo::createFrom(const QDir &dir, DirectoryEntry **directoryStru
return result;
}
ModInfo::Ptr ModInfo::createFromPlugin(const QString &espName, const QStringList &bsaNames
, DirectoryEntry ** directoryStructure)
{
ModInfo::Ptr ModInfo::createFromPlugin(const QString &modName,
const QString &espName,
const QStringList &bsaNames,
DirectoryEntry **directoryStructure) {
QMutexLocker locker(&s_Mutex);
ModInfo::Ptr result = ModInfo::Ptr(new ModInfoForeign(espName, bsaNames, directoryStructure));
ModInfo::Ptr result = ModInfo::Ptr(
new ModInfoForeign(modName, espName, bsaNames, directoryStructure));
s_Collection.push_back(result);
return result;
}
@@ -92,12 +95,10 @@ QString ModInfo::getContentTypeName(int contentType)
case CONTENT_MESH: return tr("Meshes");
case CONTENT_BSA: return tr("BSA");
case CONTENT_INTERFACE: return tr("UI Changes");
case CONTENT_MUSIC: return tr("Music");
case CONTENT_SOUND: return tr("Sound Effects");
case CONTENT_SCRIPT: return tr("Scripts");
case CONTENT_SKSE: return tr("SKSE Plugins");
case CONTENT_SKYPROC: return tr("SkyProc Tools");
case CONTENT_STRING: return tr("Strings");
default: throw MyException(tr("invalid content type %1").arg(contentType));
}
}
@@ -219,27 +220,13 @@ void ModInfo::updateFromDisc(const QString &modDirectory,
}
}
{ // list plugins in the data directory and make a foreign-managed mod out of each
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(),
[&file](QString const &p) {
return p.compare(file, Qt::CaseInsensitive) == 0; }) == mainPlugins.end()
&& (displayForeign // show non-dlc bundles only if the user wants them
|| std::find_if(dlcPlugins.begin(), dlcPlugins.end(),
[&file](QString const &p) {
return p.compare(file, Qt::CaseInsensitive) == 0; }) != dlcPlugins.end())) {
QFileInfo f(file); //Just so I can get a basename...
QStringList archives;
for (const QString &archiveName : dataDir.entryList({ f.baseName() + "*.bsa" })) {
archives.append(dataDir.absoluteFilePath(archiveName));
}
createFromPlugin(file, archives, directoryStructure);
}
UnmanagedMods *unmanaged = game->feature<UnmanagedMods>();
if (unmanaged != nullptr) {
for (const QString &modName : unmanaged->mods(!displayForeign)) {
createFromPlugin(unmanaged->displayName(modName),
unmanaged->referenceFile(modName).absoluteFilePath(),
unmanaged->secondaryFiles(modName),
directoryStructure);
}
}

Some files were not shown because too many files have changed in this diff Show More