Merge pull request #294 from Modorganizer2/Develop

Release 2.1.2
This commit is contained in:
Brian Munro
2018-04-13 11:56:36 +02:00
committed by GitHub
56 changed files with 781 additions and 285 deletions
+4 -3
View File
@@ -1,4 +1,6 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12)
ADD_COMPILE_OPTIONS($<$<CXX_COMPILER_ID:MSVC>:/MP>)
PROJECT(organizer)
@@ -8,7 +10,6 @@ set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" PROPERTY VS_STARTUP_PROJECT
SET(DEPENDENCIES_DIR CACHE PATH "")
# hint to find qt in dependencies path
LIST(APPEND CMAKE_PREFIX_PATH ${QT_ROOT}/lib/cmake)
FILE(GLOB_RECURSE BOOST_ROOT ${DEPENDENCIES_DIR}/boost*/project-config.jam)
GET_FILENAME_COMPONENT(BOOST_ROOT ${BOOST_ROOT} DIRECTORY)
LIST(APPEND CMAKE_PREFIX_PATH ${LZ4_ROOT}/dll)
ADD_SUBDIRECTORY(src)
+41 -37
View File
@@ -12,7 +12,7 @@ Mod Organizer 2 is an open project in the hands of the community, there are prob
To have more information, please join the open MO2 Development discord server :* [ModOrganizerDevs](https://discord.gg/vD2ZbfX)
If you want to help translate MO2 to your language you should join the discord server too and head to the #translation channel.
To setup a development environment on your machine, there is the [umbrella project](https://github.com/LePresidente/modorganizer-umbrella) that handles that.
To setup a development environment on your machine, there is the [umbrella project](https://github.com/Modorganizer2/modorganizer-umbrella) that handles that.
If you want to submit your code changes, please use a good formating style like the default one in Visual Studio.
Through the work of a few people of the community MO2 has come quite far, now it needs some more of those people to go further.
@@ -24,7 +24,7 @@ Credits to Tannin, LePresidente, Silarn, erasmux, AL12 and many others for the d
## Download Location
* on [GitHub.com](https://github.com/LePresidente/modorganizer/releases)
* on [GitHub.com](https://github.com/Modorganizer2/modorganizer/releases)
* on [NexusMods.com](https://www.nexusmods.com/skyrimspecialedition/mods/6194)
## Old Download Location
@@ -33,7 +33,7 @@ Credits to Tannin, LePresidente, Silarn, erasmux, AL12 and many others for the d
## Building
Please refer to [LePresidente/modorganizer-umbrella](https://github.com/LePresidente/modorganizer-umbrella) for build instructions.
Please refer to [Modorganizer2/modorganizer-umbrella](https://github.com/Modorganizer2/modorganizer-umbrella) for build instructions.
## Other Repositories
@@ -41,39 +41,43 @@ MO2 consists of multiple repositories on github. The Umbrella project will downl
Here is a complete list:
* https://github.com/LePresidente/cpython-1
* https://github.com/LePresidente/githubpp
* https://github.com/LePresidente/modorganizer
* https://github.com/LePresidente/modorganizer-archive
* https://github.com/LePresidente/modorganizer-bsatk
* https://github.com/LePresidente/modorganizer-bsa_extractor
* https://github.com/LePresidente/modorganizer-check_fnis
* https://github.com/LePresidente/modorganizer-diagnose_basic
* https://github.com/LePresidente/modorganizer-esptk
* https://github.com/LePresidente/modorganizer-helper
* https://github.com/LePresidente/modorganizer-game_fallout3
* https://github.com/LePresidente/modorganizer-game_fallout4
* https://github.com/LePresidente/modorganizer-game_fallout4vr
* https://github.com/LePresidente/modorganizer-game_falloutnv
* https://github.com/LePresidente/modorganizer-game_features
* https://github.com/LePresidente/modorganizer-game_gamebryo
* https://github.com/LePresidente/modorganizer-game_oblivion
* https://github.com/LePresidente/modorganizer-game_skyrim
* https://github.com/LePresidente/modorganizer-game_skyrimSE
* https://github.com/LePresidente/modorganizer-hookdll
* https://github.com/LePresidente/modorganizer-installer_bain
* https://github.com/LePresidente/modorganizer-installer_bundle
* https://github.com/LePresidente/modorganizer-installer_fomod
* https://github.com/LePresidente/modorganizer-installer_manual
* https://github.com/LePresidente/modorganizer-installer_ncc
* https://github.com/LePresidente/modorganizer-installer_quick
* https://github.com/LePresidente/modorganizer-lootcli
* https://github.com/LePresidente/modorganizer-NCC
* https://github.com/LePresidente/modorganizer-nxmhandler
* https://github.com/LePresidente/modorganizer-plugin_python
* https://github.com/LePresidente/modorganizer-preview_base
* https://github.com/LePresidente/modorganizer-tool_configurator
* https://github.com/LePresidente/modorganizer-tool_inibakery
* https://github.com/LePresidente/modorganizer-uibase
* https://github.com/LePresidente/usvfs
* https://github.com/Modorganizer2/githubpp
* https://github.com/Modorganizer2/modorganizer
* https://github.com/Modorganizer2/modorganizer-archive
* https://github.com/Modorganizer2/modorganizer-bsatk
* https://github.com/Modorganizer2/modorganizer-bsa_extractor
* https://github.com/Modorganizer2/modorganizer-check_fnis
* https://github.com/Modorganizer2/modorganizer-diagnose_basic
* https://github.com/Modorganizer2/modorganizer-esptk
* https://github.com/Modorganizer2/modorganizer-helper
* https://github.com/Modorganizer2/modorganizer-game_fallout3
* https://github.com/Modorganizer2/modorganizer-game_fallout4
* https://github.com/Modorganizer2/modorganizer-game_fallout4vr
* https://github.com/Modorganizer2/modorganizer-game_falloutnv
* https://github.com/Modorganizer2/modorganizer-game_features
* https://github.com/Modorganizer2/modorganizer-game_gamebryo
* https://github.com/Modorganizer2/modorganizer-game_morrowind
* https://github.com/Modorganizer2/modorganizer-game_oblivion
* https://github.com/Modorganizer2/modorganizer-game_skyrim
* https://github.com/Modorganizer2/modorganizer-game_skyrimSE
* https://github.com/Modorganizer2/modorganizer-game_ttw
* https://github.com/Modorganizer2/modorganizer-installer_bain
* https://github.com/Modorganizer2/modorganizer-installer_bundle
* https://github.com/Modorganizer2/modorganizer-installer_fomod
* https://github.com/Modorganizer2/modorganizer-installer_manual
* https://github.com/Modorganizer2/modorganizer-installer_ncc
* https://github.com/Modorganizer2/modorganizer-installer_quick
* https://github.com/Modorganizer2/modorganizer-lootcli
* https://github.com/Modorganizer2/modorganizer-NCC
* https://github.com/Modorganizer2/modorganizer-nxmhandler
* https://github.com/Modorganizer2/modorganizer-plugin_python
* https://github.com/Modorganizer2/modorganizer-preview_base
* https://github.com/Modorganizer2/modorganizer-tool_configurator
* https://github.com/Modorganizer2/modorganizer-tool_inibakery
* https://github.com/Modorganizer2/modorganizer-uibase
* https://github.com/Modorganizer2/usvfs
### Unused Repositories
* https://github.com/Modorganizer2/modorganizer-hookdll
* https://github.com/TanninOne/modorganizer-tool_nmmimport
+15 -7
View File
@@ -1,4 +1,4 @@
CMAKE_MINIMUM_REQUIRED (VERSION 2.8)
CMAKE_MINIMUM_REQUIRED (VERSION 2.8.11)
CMAKE_POLICY(SET CMP0020 NEW)
#CMAKE_POLICY(SET CMP0043 NEW)
@@ -249,10 +249,13 @@ FIND_PACKAGE(Qt5Quick REQUIRED)
FIND_PACKAGE(Qt5Network REQUIRED)
FIND_PACKAGE(Qt5WinExtras REQUIRED)
FIND_PACKAGE(Qt5WebEngineWidgets REQUIRED)
FIND_PACKAGE(Qt5Script REQUIRED)
FIND_PACKAGE(Qt5Qml 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)
ADD_CUSTOM_TARGET(translations DEPENDS ${organizer_translations_qm})
INCLUDE_DIRECTORIES(${Qt5Declarative_INCLUDES})
@@ -288,11 +291,13 @@ INCLUDE_DIRECTORIES(${project_path}/uibase/src
${project_path}/../usvfs/include
${project_path}/game_gamebryo/src
${project_path}/game_features/src
${project_path}/githubpp/src)
${project_path}/githubpp/src
${LZ4_ROOT}/include)
INCLUDE_DIRECTORIES(shared ${ZLIB_INCLUDE_DIRS})
LINK_DIRECTORIES(${lib_path}
${ZLIB_ROOT}/lib)
${ZLIB_ROOT}/lib
${LZ4_ROOT}/dll)
EXECUTE_PROCESS(
COMMAND git log -1 --format=%h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
@@ -311,11 +316,12 @@ 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::Quick
Qt5::Script Qt5::Qml Qt5::QuickWidgets Qt5::Network
${Boost_LIBRARIES}
zlibstatic
uibase esptk bsatk githubpp
${usvfs_name}
Dbghelp advapi32 Version Shlwapi)
Dbghelp advapi32 Version Shlwapi liblz4)
IF (NOT "${OPTIMIZE_COMPILE_FLAGS}" STREQUAL "")
SET_TARGET_PROPERTIES(ModOrganizer PROPERTIES COMPILE_FLAGS_RELWITHDEBINFO
${OPTIMIZE_COMPILE_FLAGS})
@@ -324,9 +330,6 @@ SET_TARGET_PROPERTIES(ModOrganizer PROPERTIES LINK_FLAGS_RELWITHDEBINFO
"/LARGEADDRESSAWARE ${OPTIMIZE_LINK_FLAGS}")
QT5_USE_MODULES(ModOrganizer Widgets Script Qml QuickWidgets Quick Network WebEngineWidgets)
###############
## Installation
@@ -368,6 +371,11 @@ INSTALL(
file(REMOVE_RECURSE ${CMAKE_INSTALL_PREFIX}/bin/qtplugins)"
)
# qdds.dll needs installing manually as Qt no longer ships with it by default.
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../qdds.dll DESTINATION bin/dlls/imageformats)
install(FILES ${organizer_translations_qm} DESTINATION bin/translations)
INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/stylesheets
${CMAKE_CURRENT_SOURCE_DIR}/tutorials
DESTINATION bin)
+32 -10
View File
@@ -29,7 +29,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QTextBrowser>
#include <QVariant>
#include <Qt>
#include <QFontDatabase>
AboutDialog::AboutDialog(const QString &version, QWidget *parent)
: QDialog(parent)
@@ -37,24 +37,44 @@ AboutDialog::AboutDialog(const QString &version, QWidget *parent)
{
ui->setupUi(this);
m_LicenseFiles[LICENSE_LGPL3] = "lgpl-3.0.txt";
m_LicenseFiles[LICENSE_GPL3] = "gpl-3.0.txt";
m_LicenseFiles[LICENSE_BSD3] = "bsd3.txt";
m_LicenseFiles[LICENSE_LGPL3] = "LGPL-v3.0.txt";
m_LicenseFiles[LICENSE_LGPL21] = "GNU-LGPL-v2.1.txt";
m_LicenseFiles[LICENSE_GPL3] = "GPL-v3.0.txt";
m_LicenseFiles[LICENSE_GPL2] = "GPL-v2.0.txt";
m_LicenseFiles[LICENSE_BOOST] = "boost.txt";
m_LicenseFiles[LICENSE_CCBY3] = "by-sa3.txt";
m_LicenseFiles[LICENSE_7ZIP] = "7zip.txt";
m_LicenseFiles[LICENSE_CCBY3] = "BY-SA-v3.0.txt";
m_LicenseFiles[LICENSE_ZLIB] = "zlib.txt";
m_LicenseFiles[LICENSE_APACHE2] = "apache-license-2.0.txt";
m_LicenseFiles[LICENSE_PYTHON] = "python.txt";
m_LicenseFiles[LICENSE_SSL] = "openssl.txt";
m_LicenseFiles[LICENSE_CPPTOML] = "cpptoml.txt";
m_LicenseFiles[LICENSE_UDIS] = "udis86.txt";
m_LicenseFiles[LICENSE_SPDLOG] = "spdlog.txt";
m_LicenseFiles[LICENSE_FMT] = "fmt.txt";
m_LicenseFiles[LICENSE_SIP] = "sip.txt";
m_LicenseFiles[LICENSE_CASTLE] = "Castle.txt";
m_LicenseFiles[LICENSE_ANTLR] = "AntlrBuildTask.txt";
m_LicenseFiles[LICENSE_DXTEX] = "DXTex.txt";
addLicense("Qt", LICENSE_LGPL3);
addLicense("Qt Json", LICENSE_GPL3);
addLicense("Boost Library", LICENSE_BOOST);
addLicense("7-zip", LICENSE_LGPL3);
addLicense("ZLib", LICENSE_ZLIB);
addLicense("7-zip", LICENSE_7ZIP);
addLicense("ZLib", LICENSE_NONE);
addLicense("Tango Icon Theme", LICENSE_NONE);
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("Castle Core", LICENSE_CASTLE);
addLicense("ANTLR", LICENSE_ANTLR);
addLicense("LOOT", LICENSE_GPL3);
addLicense("Python", LICENSE_PYTHON);
addLicense("OpenSSL", LICENSE_SSL);
addLicense("cpptoml", LICENSE_CPPTOML);
addLicense("Udis86", LICENSE_UDIS);
addLicense("spdlog", LICENSE_SPDLOG);
addLicense("{fmt}", LICENSE_FMT);
addLicense("SIP", LICENSE_SIP);
addLicense("DXTex Headers", LICENSE_DXTEX);
ui->nameLabel->setText(QString("<span style=\"font-size:12pt; font-weight:600;\">%1 %2</span>").arg(ui->nameLabel->text()).arg(version));
#if defined(HGID)
@@ -64,6 +84,8 @@ AboutDialog::AboutDialog(const QString &version, QWidget *parent)
#else
ui->revisionLabel->setText(ui->revisionLabel->text() + " unknown");
#endif
ui->licenseText->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
}
@@ -85,7 +107,7 @@ void AboutDialog::on_creditsList_currentItemChanged(QListWidgetItem *current, QL
{
auto iter = m_LicenseFiles.find(current->data(Qt::UserRole).toInt());
if (iter != m_LicenseFiles.end()) {
QString filePath = qApp->applicationDirPath() + "/license/" + iter->second;
QString filePath = qApp->applicationDirPath() + "/licenses/" + iter->second;
QString text = MOBase::readFileText(filePath);
ui->licenseText->setText(text);
} else {
+13 -2
View File
@@ -45,12 +45,23 @@ private:
enum Licenses {
LICENSE_NONE,
LICENSE_LGPL3,
LICENSE_LGPL21,
LICENSE_GPL3,
LICENSE_BSD3,
LICENSE_GPL2,
LICENSE_BOOST,
LICENSE_CCBY3,
LICENSE_PYTHON,
LICENSE_SSL,
LICENSE_CPPTOML,
LICENSE_7ZIP,
LICENSE_ZLIB,
LICENSE_APACHE2
LICENSE_UDIS,
LICENSE_SPDLOG,
LICENSE_FMT,
LICENSE_SIP,
LICENSE_CASTLE,
LICENSE_ANTLR,
LICENSE_DXTEX
};
private:
+108 -48
View File
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>604</width>
<height>369</height>
<width>1010</width>
<height>477</height>
</rect>
</property>
<property name="windowTitle">
@@ -120,14 +120,14 @@
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string notr="true">Copyright 2011-2016 Sebastian Herbord</string>
<string notr="true">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Copyright 2011-2016 Sebastian Herbord&lt;/p&gt;&lt;p&gt;Copyright 2016-2018 Mod Organizer 2 contributors&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string notr="true">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This program 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.&lt;/p&gt;&lt;p&gt;See the GNU General Public License for more details.&lt;/p&gt;&lt;p&gt;Source code can be found at &lt;a href=&quot;https://github.com/TanninOne/modorganizer&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#007af4;&quot;&gt;GitHub&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string notr="true">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This program 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.&lt;/p&gt;&lt;p&gt;See the GNU General Public License for more details.&lt;/p&gt;&lt;p&gt;Source code can be found at &lt;a href=&quot;https://github.com/Modorganizer2/modorganizer&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#007af4;&quot;&gt;GitHub&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -154,7 +154,7 @@
<string>Thanks</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Current Maintainers</string>
@@ -167,12 +167,17 @@
</property>
<item>
<property name="text">
<string notr="true">LePresidente</string>
<string notr="true">LePresidente (Project Lead)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Silarn</string>
<string>AL12</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Diana</string>
</property>
</item>
<item>
@@ -182,7 +187,7 @@
</item>
<item>
<property name="text">
<string notr="true">Diana</string>
<string notr="true">Silarn</string>
</property>
</item>
</widget>
@@ -201,36 +206,6 @@
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<item>
<property name="text">
<string notr="true">pndrev (German)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">DaWul (Spanish)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Fiama (Spanish)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Alyndiar (French)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Jlkawaii (French)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Rigoletto (French)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Scythe1912 (Chinese)</string>
@@ -248,7 +223,47 @@
</item>
<item>
<property name="text">
<string notr="true">tokcdk (Russian)</string>
<string>Cyb3r (Dutch)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Alyndiar (French)</string>
</property>
</item>
<item>
<property name="text">
<string>fruttyx (French)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Jlkawaii (French)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Rigoletto (French)</string>
</property>
</item>
<item>
<property name="text">
<string>Yoplala (French)</string>
</property>
</item>
<item>
<property name="text">
<string>Faron (German)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">pndrev (German)</string>
</property>
</item>
<item>
<property name="text">
<string>Mordan (Greek)</string>
</property>
</item>
<item>
@@ -261,7 +276,37 @@
</item>
<item>
<property name="text">
<string>... more (Can't track)</string>
<string>Yoosk (Polish)</string>
</property>
</item>
<item>
<property name="text">
<string>Brgodfx (Portuguese)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">tokcdk (Russian)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">DaWul (Spanish)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Fiama (Spanish)</string>
</property>
</item>
<item>
<property name="text">
<string>Jax (Swedish)</string>
</property>
</item>
<item>
<property name="text">
<string>...and all other Transifex contributors!</string>
</property>
<property name="toolTip">
<string/>
@@ -275,7 +320,7 @@
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Other Supporter</string>
<string>Other Supporters &amp;&amp; Contributors</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
@@ -283,9 +328,9 @@
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<item>
<item>
<property name="text">
<string notr="true">Al12 (Discord) </string>
<string notr="true">AnyOldName3</string>
</property>
</item>
<item>
@@ -308,6 +353,11 @@
<string notr="true">Bridger</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Brixified</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">GamerPoet</string>
@@ -323,6 +373,21 @@
<string notr="true">GSDFan</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">ogrotten</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Schilduin</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">SuperSandro2000</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Uhuru</string>
@@ -343,11 +408,6 @@
<string notr="true">thosrtanner</string>
</property>
</item>
<item>
<property name="text">
<string>ogrotten</string>
</property>
</item>
</widget>
</item>
</layout>
+1 -1
View File
@@ -167,7 +167,7 @@ void CategoryFactory::saveCategories()
}
unsigned int CategoryFactory::countCategories(std::tr1::function<bool (const Category &category)> filter)
unsigned int CategoryFactory::countCategories(std::function<bool (const Category &category)> filter)
{
unsigned int result = 0;
for (const Category &cat : m_Categories) {
+1 -1
View File
@@ -99,7 +99,7 @@ public:
* @param filter the filter to test
* @return number of matching categories
*/
unsigned int countCategories(std::tr1::function<bool (const Category &category)> filter);
unsigned int countCategories(std::function<bool (const Category &category)> filter);
/**
* @brief get the id of the parent category
+61 -18
View File
@@ -460,7 +460,29 @@ void DownloadManager::addNXMDownload(const QString &url)
return;
}
for (auto pair : m_PendingDownloads) {
if (pair.first == nxmInfo.modId() && pair.second == nxmInfo.fileId()) {
qDebug("download requested is already started (mod id: %s, file id: %s)", qPrintable(QString(nxmInfo.modId())), qPrintable(QString(nxmInfo.fileId())));
QMessageBox::information(nullptr, tr("Already Started"), tr("A download for this mod file has already been queued."), QMessageBox::Ok);
return;
}
}
for (DownloadInfo *download : m_ActiveDownloads) {
if (download->m_FileInfo->modID == nxmInfo.modId() && download->m_FileInfo->fileID == nxmInfo.fileId()) {
if (download->m_State == STATE_DOWNLOADING || download->m_State == STATE_PAUSED || download->m_State == STATE_STARTED) {
qDebug("download requested is already started (mod: %s, file: %s)", qPrintable(download->m_FileInfo->modName),
qPrintable(download->m_FileInfo->fileName));
QMessageBox::information(nullptr, tr("Already Started"), tr("There is already a download started for this file (%2).")
.arg(download->m_FileInfo->modName).arg(download->m_FileInfo->name), QMessageBox::Ok);
return;
}
}
}
emit aboutToUpdate();
m_PendingDownloads.append(std::make_pair(nxmInfo.modId(), nxmInfo.fileId()));
emit update(-1);
@@ -558,36 +580,20 @@ void DownloadManager::removeDownload(int index, bool deleteFile)
emit aboutToUpdate();
if (index < 0) {
if (index == -1) {
DownloadState minState = STATE_READY;
DownloadState minState = index == -1 ? STATE_READY : STATE_INSTALLED;
index = 0;
for (QVector<DownloadInfo*>::iterator iter = m_ActiveDownloads.begin(); iter != m_ActiveDownloads.end();) {
if ((*iter)->m_State >= minState) {
removeFile(index, deleteFile);
delete *iter;
iter = m_ActiveDownloads.erase(iter);
QCoreApplication::processEvents();
}
else {
++iter;
++index;
}
}
}
else {
DownloadState minState = STATE_INSTALLED;
index = 0;
for (QVector<DownloadInfo*>::iterator iter = m_ActiveDownloads.begin(); iter != m_ActiveDownloads.end();) {
if ((*iter)->m_State == minState) {
removeFile(index, deleteFile);
delete *iter;
iter = m_ActiveDownloads.erase(iter);
}
else {
++iter;
++index;
}
}
}
} else {
if (index >= m_ActiveDownloads.size()) {
reportError(tr("remove: invalid download index %1").arg(index));
@@ -896,6 +902,26 @@ void DownloadManager::markInstalled(int index)
setState(m_ActiveDownloads.at(index), STATE_INSTALLED);
}
void DownloadManager::markInstalled(QString fileName)
{
int index = indexByName(fileName);
if (index >= 0) {
markInstalled(index);
} else {
DownloadInfo *info = getDownloadInfo(fileName);
if (info != nullptr) {
QSettings metaFile(info->m_Output.fileName() + ".meta", QSettings::IniFormat);
metaFile.setValue("installed", true);
metaFile.setValue("uninstalled", false);
}
delete info;
}
}
DownloadManager::DownloadInfo* DownloadManager::getDownloadInfo(QString fileName)
{
return DownloadInfo::createFromMeta(fileName, true);
}
void DownloadManager::markUninstalled(int index)
{
@@ -911,6 +937,23 @@ void DownloadManager::markUninstalled(int index)
}
void DownloadManager::markUninstalled(QString fileName)
{
int index = indexByName(fileName);
if (index >= 0) {
markUninstalled(index);
} else {
QString filePath = QDir::fromNativeSeparators(m_OutputDirectory) + "/" + fileName;
DownloadInfo *info = getDownloadInfo(filePath);
if (info != nullptr) {
QSettings metaFile(info->m_Output.fileName() + ".meta", QSettings::IniFormat);
metaFile.setValue("uninstalled", true);
}
delete info;
}
}
QString DownloadManager::getDownloadFileName(const QString &baseName) const
{
QString fullPath = m_OutputDirectory + "/" + baseName;
+5
View File
@@ -307,6 +307,8 @@ public:
*/
void markInstalled(int index);
void markInstalled(QString download);
/**
* @brief mark a download as uninstalled
*
@@ -314,6 +316,8 @@ public:
*/
void markUninstalled(int index);
void markUninstalled(QString download);
/**
* @brief refreshes the list of downloads
*/
@@ -431,6 +435,7 @@ private slots:
private:
void createMetaFile(DownloadInfo *info);
DownloadManager::DownloadInfo* getDownloadInfo(QString fileName);
public:
+2 -2
View File
@@ -105,7 +105,7 @@ QString InstanceManager::manageInstances(const QStringList &instanceList) const
nullptr);
for (const QString &instance : instanceList)
{
selection.addChoice(instance, "", instance);
selection.addChoice(QIcon(":/MO/gui/multiply_red"), instance, "", instance);
}
if (selection.exec() == QDialog::Rejected) {
@@ -120,7 +120,7 @@ QString InstanceManager::manageInstances(const QStringList &instanceList) const
if (!deleteLocalInstance(choice))
{
QMessageBox::warning(nullptr, QObject::tr("Failed to delete Instance"),
QObject::tr("Could not delete Instance \"%1\"").arg(choice), QMessageBox::Ok);
QObject::tr("Could not delete Instance \"%1\". \nIf the folder was still in use, restart MO and try again.").arg(choice), QMessageBox::Ok);
}
}
}
+41 -35
View File
@@ -402,6 +402,8 @@ void setupPath()
qDebug("MO at: %s", qPrintable(QDir::toNativeSeparators(
QCoreApplication::applicationDirPath())));
QCoreApplication::setLibraryPaths(QStringList(QCoreApplication::applicationDirPath() + "/dlls") + QCoreApplication::libraryPaths());
boost::scoped_array<TCHAR> oldPath(new TCHAR[BUFSIZE]);
DWORD offset = ::GetEnvironmentVariable(TEXT("PATH"), oldPath.get(), BUFSIZE);
if (offset > BUFSIZE) {
@@ -409,12 +411,10 @@ void setupPath()
::GetEnvironmentVariable(TEXT("PATH"), oldPath.get(), offset);
}
std::wstring newPath(oldPath.get());
std::wstring newPath(ToWString(QDir::toNativeSeparators(
QCoreApplication::applicationDirPath())) + L"\\dlls");
newPath += L";";
newPath += ToWString(QDir::toNativeSeparators(
QCoreApplication::applicationDirPath()))
.c_str();
newPath += L"\\dlls";
newPath += oldPath.get();
::SetEnvironmentVariableW(L"PATH", newPath.c_str());
}
@@ -561,36 +561,42 @@ 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 (MOShortcut shortcut{ arguments.at(1) }) {
try {
organizer.runShortcut(shortcut);
return 0;
} catch (const std::exception &e) {
reportError(
QObject::tr("failed to start shortcut: %1").arg(e.what()));
return 1;
}
} else if (OrganizerCore::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());
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 (MOShortcut shortcut{ arguments.at(1) }) {
if (shortcut.hasExecutable()) {
try {
organizer.runShortcut(shortcut);
return 0;
}
catch (const std::exception &e) {
reportError(
QObject::tr("failed to start shortcut: %1").arg(e.what()));
return 1;
}
}
}
else if (OrganizerCore::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());
return 0;
}
catch (const std::exception &e) {
reportError(
QObject::tr("failed to start application: %1").arg(e.what()));
return 1;
}
}
}
QPixmap pixmap(splashPath);
QSplashScreen splash(pixmap);
+116 -59
View File
@@ -89,6 +89,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QAbstractProxyModel>
#include <QAction>
#include <QApplication>
#include <QButtonGroup>
#include <QBuffer>
#include <QCheckBox>
#include <QClipboard>
@@ -153,8 +154,6 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QDebug>
#include <QtGlobal>
#include <QtConcurrent/QtConcurrentRun>
#ifndef Q_MOC_RUN
#include <boost/thread.hpp>
#include <boost/algorithm/string.hpp>
@@ -901,6 +900,8 @@ void MainWindow::cleanup()
QWebEngineProfile::defaultProfile()->clearAllVisitedLinks();
m_IntegratedBrowser.close();
m_SaveMetaTimer.stop();
m_MetaSave.waitForFinished();
}
@@ -1568,9 +1569,13 @@ void MainWindow::checkBSAList()
void MainWindow::saveModMetas()
{
for (unsigned int i = 0; i < ModInfo::getNumMods(); ++i) {
ModInfo::Ptr modInfo = ModInfo::getByIndex(i);
modInfo->saveMeta();
if (m_MetaSave.isFinished()) {
m_MetaSave = QtConcurrent::run([this]() {
for (unsigned int i = 0; i < ModInfo::getNumMods(); ++i) {
ModInfo::Ptr modInfo = ModInfo::getByIndex(i);
modInfo->saveMeta();
}
});
}
}
@@ -1886,7 +1891,7 @@ void MainWindow::wikiTriggered()
void MainWindow::issueTriggered()
{
::ShellExecuteW(nullptr, L"open", L"http://github.com/LePresidente/modorganizer/issues", nullptr, nullptr, SW_SHOWNORMAL);
::ShellExecuteW(nullptr, L"open", L"http://github.com/Modorganizer2/modorganizer/issues", nullptr, nullptr, SW_SHOWNORMAL);
}
void MainWindow::tutorialTriggered()
@@ -2307,10 +2312,7 @@ void MainWindow::removeMod_clicked()
void MainWindow::modRemoved(const QString &fileName)
{
if (!fileName.isEmpty() && !QFileInfo(fileName).isAbsolute()) {
int index = m_OrganizerCore.downloadManager()->indexByName(fileName);
if (index >= 0) {
m_OrganizerCore.downloadManager()->markUninstalled(index);
}
m_OrganizerCore.downloadManager()->markUninstalled(fileName);
}
}
@@ -2450,7 +2452,7 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index,
}
} else {
modInfo->saveMeta();
ModInfoDialog dialog(modInfo, m_OrganizerCore.directoryStructure(), modInfo->hasFlag(ModInfo::FLAG_FOREIGN), this);
ModInfoDialog dialog(modInfo, m_OrganizerCore.directoryStructure(), modInfo->hasFlag(ModInfo::FLAG_FOREIGN), &m_OrganizerCore, &m_PluginContainer,this);
connect(&dialog, SIGNAL(nexusLinkActivated(QString)), this, SLOT(nexusLinkActivated(QString)));
connect(&dialog, SIGNAL(downloadRequest(QString)), &m_OrganizerCore, SLOT(downloadRequestedNXM(QString)));
connect(&dialog, SIGNAL(modOpen(QString, int)), this, SLOT(displayModInformation(QString, int)), Qt::QueuedConnection);
@@ -3035,6 +3037,12 @@ void MainWindow::openInstanceFolder()
::ShellExecuteW(nullptr, L"explore", ToWString(m_OrganizerCore.settings().getBaseDirectory()).c_str(), nullptr, nullptr, SW_SHOWNORMAL);
}
void MainWindow::openLogsFolder()
{
QString logsPath = qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::logPath());
::ShellExecuteW(nullptr, L"explore", ToWString(logsPath).c_str(), nullptr, nullptr, SW_SHOWNORMAL);
}
void MainWindow::openInstallFolder()
{
::ShellExecuteW(nullptr, L"explore", ToWString(qApp->applicationDirPath()).c_str(), nullptr, nullptr, SW_SHOWNORMAL);
@@ -3073,6 +3081,10 @@ void MainWindow::exportModListCSV()
QGridLayout *grid = new QGridLayout;
selection.setWindowTitle(tr("Export to csv"));
QLabel *csvDescription = new QLabel();
csvDescription->setText(tr("CSV (Comma Separated Values) is a format that can be imported in programs like Excel to create a spreadsheet.\nYou can also use online editors and converters instead."));
grid->addWidget(csvDescription);
QGroupBox *groupBoxRows = new QGroupBox(tr("Select what mods you want export:"));
QRadioButton *all = new QRadioButton(tr("All installed mods"));
QRadioButton *active = new QRadioButton(tr("Only active (checked) mods from your current profile"));
@@ -3195,7 +3207,7 @@ void MainWindow::exportModListCSV()
if (nexus_ID->isChecked())
builder.setRowField("#Nexus_ID", info->getNexusID());
if (mod_Nexus_URL->isChecked())
builder.setRowField("#Mod_Nexus_URL", info->getURL());
builder.setRowField("#Mod_Nexus_URL",(info->getNexusID()>0)? NexusInterface::instance()->getModURL(info->getNexusID()) : "");
if (mod_Version->isChecked())
builder.setRowField("#Mod_Version", info->getVersion().canonicalString());
if (install_Date->isChecked())
@@ -3227,10 +3239,15 @@ static void addMenuAsPushButton(QMenu *menu, QMenu *subMenu)
}
QMenu *MainWindow::openFolderMenu()
{
{
QMenu *FolderMenu = new QMenu(this);
FolderMenu->addAction(tr("Open Game folder"), this, SLOT(openGameFolder()));
FolderMenu->addAction(tr("Open MyGames folder"), this, SLOT(openMyGamesFolder()));
FolderMenu->addSeparator();
FolderMenu->addAction(tr("Open Instance folder"), this, SLOT(openInstanceFolder()));
@@ -3238,13 +3255,19 @@ QMenu *MainWindow::openFolderMenu()
FolderMenu->addAction(tr("Open Downloads folder"), this, SLOT(openDownloadsFolder()));
FolderMenu->addAction(tr("Open MO Install folder"), this, SLOT(openInstallFolder()));
FolderMenu->addSeparator();
FolderMenu->addAction(tr("Open Game folder"), this, SLOT(openGameFolder()));
FolderMenu->addAction(tr("Open MO2 Install folder"), this, SLOT(openInstallFolder()));
FolderMenu->addAction(tr("Open MO2 Logs folder"), this, SLOT(openLogsFolder()));
FolderMenu->addAction(tr("Open MyGames folder"), this, SLOT(openMyGamesFolder()));
return FolderMenu;
@@ -3304,15 +3327,18 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos)
} else if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) {
// nop, nothing to do with this mod
} else {
QMenu *addRemoveCategoriesMenu = new QMenu(tr("Add/Remove Categories"));
QMenu *addRemoveCategoriesMenu = new QMenu(tr("Change Categories"));
populateMenuCategories(addRemoveCategoriesMenu, 0);
connect(addRemoveCategoriesMenu, SIGNAL(aboutToHide()), this, SLOT(addRemoveCategories_MenuHandler()));
addMenuAsPushButton(menu, addRemoveCategoriesMenu);
//Removed as it was redundant, just making the categories look more complicated.
/*
QMenu *replaceCategoriesMenu = new QMenu(tr("Replace Categories"));
populateMenuCategories(replaceCategoriesMenu, 0);
connect(replaceCategoriesMenu, SIGNAL(aboutToHide()), this, SLOT(replaceCategories_MenuHandler()));
addMenuAsPushButton(menu, replaceCategoriesMenu);
*/
QMenu *primaryCategoryMenu = new QMenu(tr("Primary Category"));
connect(primaryCategoryMenu, SIGNAL(aboutToShow()), this, SLOT(addPrimaryCategoryCandidates()));
@@ -3332,8 +3358,8 @@ void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos)
menu->addSeparator();
menu->addAction(tr("Rename Mod..."), this, SLOT(renameMod_clicked()));
menu->addAction(tr("Remove Mod..."), this, SLOT(removeMod_clicked()));
menu->addAction(tr("Reinstall Mod"), this, SLOT(reinstallMod_clicked()));
menu->addAction(tr("Remove Mod..."), this, SLOT(removeMod_clicked()));
menu->addSeparator();
@@ -3909,10 +3935,24 @@ void MainWindow::previewDataFile()
// what we have is an absolute path to the file in its actual location (for the primary origin)
// what we want is the path relative to the virtual data directory
// crude: we search for the next slash after the base mod directory to skip everything up to the data-relative directory
int offset = m_OrganizerCore.settings().getModDirectory().size() + 1;
offset = fileName.indexOf("/", offset);
fileName = fileName.mid(offset + 1);
// we need to look in the virtual directory for the file to make sure the info is up to date.
// check if the file comes from the actual data folder instead of a mod
QDir gameDirectory = m_OrganizerCore.managedGame()->dataDirectory().absolutePath();
QString relativePath = gameDirectory.relativeFilePath(fileName);
QDir dirRelativePath = gameDirectory.relativeFilePath(fileName);
// if the file is on a different drive the dirRelativePath will actually be an absolute path so we make sure that is not the case
if (!dirRelativePath.isAbsolute() && !relativePath.startsWith("..")) {
fileName = relativePath;
}
else {
// crude: we search for the next slash after the base mod directory to skip everything up to the data-relative directory
int offset = m_OrganizerCore.settings().getModDirectory().size() + 1;
offset = fileName.indexOf("/", offset);
fileName = fileName.mid(offset + 1);
}
const FileEntry::Ptr file = m_OrganizerCore.directoryStructure()->searchFile(ToWString(fileName), nullptr);
@@ -4250,27 +4290,40 @@ bool MainWindow::extractProgress(QProgressDialog &progress, int percentage, std:
void MainWindow::extractBSATriggered()
{
QTreeWidgetItem *item = m_ContextItem;
QString origin;
QString targetFolder = FileDialogMemory::getExistingDirectory("extractBSA", this, tr("Extract BSA"));
QStringList archives = {};
if (!targetFolder.isEmpty()) {
BSA::Archive archive;
QString originPath = QDir::fromNativeSeparators(ToQString(m_OrganizerCore.directoryStructure()->getOriginByName(ToWString(item->text(1))).getPath()));
QString archivePath = QString("%1\\%2").arg(originPath).arg(item->text(0));
BSA::EErrorCode result = archive.read(archivePath.toLocal8Bit().constData(), true);
if ((result != BSA::ERROR_NONE) && (result != BSA::ERROR_INVALIDHASHES)) {
reportError(tr("failed to read %1: %2").arg(archivePath).arg(result));
return;
if (!item->parent()) {
for (int i = 0; i < item->childCount(); ++i) {
archives.append(item->child(i)->text(0));
}
origin = QDir::fromNativeSeparators(ToQString(m_OrganizerCore.directoryStructure()->getOriginByName(ToWString(item->text(0))).getPath()));
} else {
origin = QDir::fromNativeSeparators(ToQString(m_OrganizerCore.directoryStructure()->getOriginByName(ToWString(item->text(1))).getPath()));
archives = QStringList({ item->text(0) });
}
QProgressDialog progress(this);
progress.setMaximum(100);
progress.setValue(0);
progress.show();
archive.extractAll(QDir::toNativeSeparators(targetFolder).toLocal8Bit().constData(),
boost::bind(&MainWindow::extractProgress, this, boost::ref(progress), _1, _2));
if (result == BSA::ERROR_INVALIDHASHES) {
reportError(tr("This archive contains invalid hashes. Some files may be broken."));
for (auto archiveName : archives) {
BSA::Archive archive;
QString archivePath = QString("%1\\%2").arg(origin).arg(archiveName);
BSA::EErrorCode result = archive.read(archivePath.toLocal8Bit().constData(), true);
if ((result != BSA::ERROR_NONE) && (result != BSA::ERROR_INVALIDHASHES)) {
reportError(tr("failed to read %1: %2").arg(archivePath).arg(result));
return;
}
QProgressDialog progress(this);
progress.setMaximum(100);
progress.setValue(0);
progress.show();
archive.extractAll(QDir::toNativeSeparators(targetFolder).toLocal8Bit().constData(),
boost::bind(&MainWindow::extractProgress, this, boost::ref(progress), _1, _2));
if (result == BSA::ERROR_INVALIDHASHES) {
reportError(tr("This archive contains invalid hashes. Some files may be broken."));
}
archive.close();
}
}
}
@@ -4307,6 +4360,18 @@ void MainWindow::displayColumnSelection(const QPoint &pos)
}
}
void MainWindow::on_bsaList_customContextMenuRequested(const QPoint &pos)
{
m_ContextItem = ui->bsaList->itemAt(pos);
// m_ContextRow = ui->bsaList->indexOfTopLevelItem(ui->bsaList->itemAt(pos));
QMenu menu;
menu.addAction(tr("Extract..."), this, SLOT(extractBSATriggered()));
menu.exec(ui->bsaList->mapToGlobal(pos));
}
void MainWindow::on_bsaList_itemChanged(QTreeWidgetItem*, int)
{
m_ArchiveListWriter.write();
@@ -4576,8 +4641,8 @@ void MainWindow::processLOOTOut(const std::string &lootOut, std::string &errorMe
std::vector<std::string> lines;
boost::split(lines, lootOut, boost::is_any_of("\r\n"));
std::tr1::regex exRequires("\"([^\"]*)\" requires \"([^\"]*)\", but it is missing\\.");
std::tr1::regex exIncompatible("\"([^\"]*)\" is incompatible with \"([^\"]*)\", but both are present\\.");
std::regex exRequires("\"([^\"]*)\" requires \"([^\"]*)\", but it is missing\\.");
std::regex exIncompatible("\"([^\"]*)\" is incompatible with \"([^\"]*)\", but both are present\\.");
for (const std::string &line : lines) {
if (line.length() > 0) {
@@ -4589,12 +4654,12 @@ void MainWindow::processLOOTOut(const std::string &lootOut, std::string &errorMe
qWarning("%s", line.c_str());
errorMessages.append(boost::algorithm::trim_copy(line.substr(erroridx + 8)) + "\n");
} else {
std::tr1::smatch match;
if (std::tr1::regex_match(line, match, exRequires)) {
std::smatch match;
if (std::regex_match(line, match, exRequires)) {
std::string modName(match[1].first, match[1].second);
std::string dependency(match[2].first, match[2].second);
m_OrganizerCore.pluginList()->addInformation(modName.c_str(), tr("depends on missing \"%1\"").arg(dependency.c_str()));
} else if (std::tr1::regex_match(line, match, exIncompatible)) {
} else if (std::regex_match(line, match, exIncompatible)) {
std::string modName(match[1].first, match[1].second);
std::string dependency(match[2].first, match[2].second);
m_OrganizerCore.pluginList()->addInformation(modName.c_str(), tr("incompatible with \"%1\"").arg(dependency.c_str()));
@@ -4628,12 +4693,12 @@ void MainWindow::on_bossButton_clicked()
dialog.show();
QString outPath = QDir::temp().absoluteFilePath("lootreport.json");
QStringList parameters;
parameters << "--game" << m_OrganizerCore.managedGame()->gameShortName()
<< "--gamePath" << QString("\"%1\"").arg(m_OrganizerCore.managedGame()->gameDirectory().absolutePath())
<< "--pluginListPath" << QString("\"%1/loadorder.txt\"").arg(m_OrganizerCore.profilePath())
<< "--out" << QString("\"%1\"").arg(outPath);
<< "--gamePath" << QString("\"%1\"").arg(m_OrganizerCore.managedGame()->gameDirectory().absolutePath())
<< "--pluginListPath" << QString("\"%1/loadorder.txt\"").arg(m_OrganizerCore.profilePath())
<< "--out" << QString("\"%1\"").arg(outPath);
if (m_DidUpdateMasterList) {
parameters << "--skipUpdateMasterlist";
@@ -4753,7 +4818,7 @@ void MainWindow::on_bossButton_clicked()
if (success) {
m_DidUpdateMasterList = true;
/*if (reportURL.length() > 0) {
if (reportURL.length() > 0) {
m_IntegratedBrowser.setWindowTitle("LOOT Report");
QString report(reportURL.c_str());
QStringList temp = report.split("?");
@@ -4762,15 +4827,8 @@ void MainWindow::on_bossButton_clicked()
url.setQuery(temp.at(1).toUtf8());
}
m_IntegratedBrowser.openUrl(url);
}*/
// 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()->loadOrderMechanism() == IPluginGame::LoadOrderMechanism::FileTime) {
qDebug("removing loadorder.txt");
QFile::remove(m_OrganizerCore.currentProfile()->getLoadOrderFileName());
}
m_OrganizerCore.refreshESPList();
m_OrganizerCore.refreshESPList(false);
m_OrganizerCore.savePluginList();
}
}
@@ -4847,7 +4905,7 @@ void MainWindow::on_restoreButton_clicked()
QMessageBox::critical(this, tr("Restore failed"),
tr("Failed to restore the backup. Errorcode: %1").arg(windowsErrorString(::GetLastError())));
}
m_OrganizerCore.refreshESPList();
m_OrganizerCore.refreshESPList(true);
}
}
@@ -5045,4 +5103,3 @@ void MainWindow::on_clearFiltersButton_clicked()
{
deselectFilters();
}
+5
View File
@@ -63,6 +63,7 @@ namespace MOShared { class DirectoryEntry; }
#include <QHeaderView>
#include <QVariant>
#include <Qt>
#include <QtConcurrent/QtConcurrentRun>
class QAction;
class QAbstractItemModel;
@@ -336,6 +337,8 @@ private:
QTimer m_SaveMetaTimer;
QTimer m_UpdateProblemsTimer;
QFuture<void> m_MetaSave;
QTime m_StartTime;
//SaveGameInfoWidget *m_CurrentSaveView;
MOBase::ISaveGameInfoWidget *m_CurrentSaveView;
@@ -487,6 +490,7 @@ private slots:
void disableVisibleMods();
void exportModListCSV();
void openInstanceFolder();
void openLogsFolder();
void openInstallFolder();
void openDownloadsFolder();
void openProfileFolder();
@@ -552,6 +556,7 @@ private slots: // ui slots
void on_actionUpdate_triggered();
void on_actionEndorseMO_triggered();
void on_bsaList_customContextMenuRequested(const QPoint &pos);
void on_clearFiltersButton_clicked();
void on_btnRefreshData_clicked();
void on_categoriesList_customContextMenuRequested(const QPoint &pos);
+51 -10
View File
@@ -194,7 +194,7 @@
</widget>
</item>
<item>
<widget class="QComboBox" name="profileBox">
<widget class="QComboBox" name="profileBox">
<property name="toolTip">
<string>Pick a module collection</string>
</property>
@@ -253,7 +253,7 @@ p, li { white-space: pre-wrap; }
<item>
<widget class="QPushButton" name="openFolderMenu">
<property name="toolTip">
<string>Quickly open in Explorer relevant Folders...</string>
<string>Show Open Folders menu...</string>
</property>
<property name="text">
<string/>
@@ -463,7 +463,7 @@ p, li { white-space: pre-wrap; }
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="0,0,1,1,1,0">
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="0,0,1,1,0,1,1,1">
<item>
<widget class="QPushButton" name="displayCategoriesBtn">
<property name="maximumSize">
@@ -507,15 +507,25 @@ p, li { white-space: pre-wrap; }
<italic>true</italic>
</font>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item alignment="Qt::AlignLeft">
<widget class="QPushButton" name="clearFiltersButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@@ -523,9 +533,15 @@ p, li { white-space: pre-wrap; }
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
<height>22</height>
</size>
</property>
<property name="baseSize">
<size>
<width>95</width>
<height>0</height>
</size>
</property>
<property name="visible">
<bool>false</bool>
</property>
@@ -551,7 +567,26 @@ p, li { white-space: pre-wrap; }
</widget>
</item>
<item>
<widget class="QComboBox" name="groupCombo">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QComboBox" name="groupCombo">
<property name="baseSize">
<size>
<width>220</width>
<height>0</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::ClickFocus</enum>
</property>
@@ -573,7 +608,13 @@ p, li { white-space: pre-wrap; }
</widget>
</item>
<item>
<widget class="MOBase::LineEditClear" name="modFilterEdit">
<widget class="MOBase::LineEditClear" name="modFilterEdit">
<property name="baseSize">
<size>
<width>220</width>
<height>0</height>
</size>
</property>
<property name="placeholderText">
<string>Namefilter</string>
</property>
+3 -3
View File
@@ -55,7 +55,7 @@ QMutex ModInfo::s_Mutex(QMutex::Recursive);
QString ModInfo::s_HiddenExt(".mohidden");
static bool ByName(const ModInfo::Ptr &LHS, const ModInfo::Ptr &RHS)
bool ModInfo::ByName(const ModInfo::Ptr &LHS, const ModInfo::Ptr &RHS)
{
return QString::compare(LHS->name(), RHS->name(), Qt::CaseInsensitive) < 0;
}
@@ -98,7 +98,7 @@ QString ModInfo::getContentTypeName(int contentType)
case CONTENT_INTERFACE: return tr("UI Changes");
case CONTENT_SOUND: return tr("Sound Effects");
case CONTENT_SCRIPT: return tr("Scripts");
case CONTENT_SKSE: return tr("SKSE Plugins");
case CONTENT_SKSE: return tr("Script Extender");
case CONTENT_SKYPROC: return tr("SkyProc Tools");
case CONTENT_MCM: return tr("MCM Data");
default: throw MyException(tr("invalid content type %1").arg(contentType));
@@ -239,7 +239,7 @@ void ModInfo::updateFromDisc(const QString &modDirectory,
createFromOverwrite();
std::sort(s_Collection.begin(), s_Collection.end(), ByName);
std::sort(s_Collection.begin(), s_Collection.end(), ModInfo::ByName);
updateIndices();
}
+1
View File
@@ -599,6 +599,7 @@ protected:
ModInfo();
static void updateIndices();
static bool ByName(const ModInfo::Ptr &LHS, const ModInfo::Ptr &RHS);
private:
+184 -2
View File
@@ -20,6 +20,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "modinfodialog.h"
#include "ui_modinfodialog.h"
#include "descriptionpage.h"
#include "mainwindow.h"
#include "iplugingame.h"
#include "nexusinterface.h"
@@ -30,6 +31,10 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "questionboxmemory.h"
#include "settings.h"
#include "categories.h"
#include "organizercore.h"
#include "pluginlistsortproxy.h"
#include "previewgenerator.h"
#include "previewdialog.h"
#include <QDir>
#include <QDirIterator>
@@ -40,6 +45,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QFileSystemModel>
#include <QInputDialog>
#include <QPointer>
#include <QFileDialog>
#include <Shlwapi.h>
@@ -66,11 +72,12 @@ static bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS
}
ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directory, bool unmanaged, QWidget *parent)
ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directory, bool unmanaged, OrganizerCore *organizerCore, PluginContainer *pluginContainer, QWidget *parent)
: TutorableDialog("ModInfoDialog", parent), ui(new Ui::ModInfoDialog), m_ModInfo(modInfo),
m_ThumbnailMapper(this), m_RequestStarted(false),
m_DeleteAction(nullptr), m_RenameAction(nullptr), m_OpenAction(nullptr),
m_Directory(directory), m_Origin(nullptr)
m_Directory(directory), m_Origin(nullptr),
m_OrganizerCore(organizerCore), m_PluginContainer(pluginContainer)
{
ui->setupUi(this);
this->setWindowTitle(modInfo->name());
@@ -293,7 +300,9 @@ void ModInfoDialog::refreshLists()
QStringList fields(relativeName);
fields.append(ToQString(realOrigin.getName()));
QTreeWidgetItem *item = new QTreeWidgetItem(fields);
item->setData(0, Qt::UserRole, fileName);
item->setData(1, Qt::UserRole, ToQString(realOrigin.getName()));
item->setData(1, Qt::UserRole + 2, archive);
ui->overwrittenTree->addTopLevelItem(item);
++numOverwritten;
}
@@ -1197,6 +1206,151 @@ void ModInfoDialog::unhideConflictFile()
}
}
int ModInfoDialog::getBinaryExecuteInfo(const QFileInfo &targetInfo, QFileInfo &binaryInfo, QString &arguments)
{
QString extension = targetInfo.suffix();
if ((extension.compare("cmd", Qt::CaseInsensitive) == 0) ||
(extension.compare("com", Qt::CaseInsensitive) == 0) ||
(extension.compare("bat", Qt::CaseInsensitive) == 0)) {
binaryInfo = QFileInfo("C:\\Windows\\System32\\cmd.exe");
arguments = QString("/C \"%1\"").arg(QDir::toNativeSeparators(targetInfo.absoluteFilePath()));
return 1;
}
else if (extension.compare("exe", Qt::CaseInsensitive) == 0) {
binaryInfo = targetInfo;
return 1;
}
else if (extension.compare("jar", Qt::CaseInsensitive) == 0) {
// types that need to be injected into
std::wstring targetPathW = ToWString(targetInfo.absoluteFilePath());
QString binaryPath;
{ // try to find java automatically
WCHAR buffer[MAX_PATH];
if (::FindExecutableW(targetPathW.c_str(), nullptr, buffer) > (HINSTANCE)32) {
DWORD binaryType = 0UL;
if (!::GetBinaryTypeW(buffer, &binaryType)) {
qDebug("failed to determine binary type of \"%ls\": %lu", buffer, ::GetLastError());
}
else if (binaryType == SCS_32BIT_BINARY) {
binaryPath = ToQString(buffer);
}
}
}
if (binaryPath.isEmpty() && (extension == "jar")) {
// second attempt: look to the registry
QSettings javaReg("HKEY_LOCAL_MACHINE\\Software\\JavaSoft\\Java Runtime Environment", QSettings::NativeFormat);
if (javaReg.contains("CurrentVersion")) {
QString currentVersion = javaReg.value("CurrentVersion").toString();
binaryPath = javaReg.value(QString("%1/JavaHome").arg(currentVersion)).toString().append("\\bin\\javaw.exe");
}
}
if (binaryPath.isEmpty()) {
binaryPath = QFileDialog::getOpenFileName(this, tr("Select binary"), QString(), tr("Binary") + " (*.exe)");
}
if (binaryPath.isEmpty()) {
return 0;
}
binaryInfo = QFileInfo(binaryPath);
if (extension == "jar") {
arguments = QString("-jar \"%1\"").arg(QDir::toNativeSeparators(targetInfo.absoluteFilePath()));
}
else {
arguments = QString("\"%1\"").arg(QDir::toNativeSeparators(targetInfo.absoluteFilePath()));
}
return 1;
}
else {
return 2;
}
}
void ModInfoDialog::openDataFile()
{
if (m_ConflictsContextItem != nullptr) {
QFileInfo targetInfo(m_ConflictsContextItem->data(0, Qt::UserRole).toString());
QFileInfo binaryInfo;
QString arguments;
switch (getBinaryExecuteInfo(targetInfo, binaryInfo, arguments)) {
case 1: {
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);
} break;
default: {
// nop
} break;
}
}
}
void ModInfoDialog::previewDataFile()
{
QString fileName = QDir::fromNativeSeparators(m_ConflictsContextItem->data(0, Qt::UserRole).toString());
// what we have is an absolute path to the file in its actual location (for the primary origin)
// what we want is the path relative to the virtual data directory
// we need to look in the virtual directory for the file to make sure the info is up to date.
// check if the file comes from the actual data folder instead of a mod
QDir gameDirectory = m_OrganizerCore->managedGame()->dataDirectory().absolutePath();
QString relativePath = gameDirectory.relativeFilePath(fileName);
QDir direRelativePath = gameDirectory.relativeFilePath(fileName);
// if the file is on a different drive the dirRelativePath will actually be an absolute path so we make sure that is not the case
if (!direRelativePath.isAbsolute() && !relativePath.startsWith("..")) {
fileName = relativePath;
}
else {
// crude: we search for the next slash after the base mod directory to skip everything up to the data-relative directory
int offset = m_OrganizerCore->settings().getModDirectory().size() + 1;
offset = fileName.indexOf("/", offset);
fileName = fileName.mid(offset + 1);
}
const FileEntry::Ptr file = m_OrganizerCore->directoryStructure()->searchFile(ToWString(fileName), nullptr);
if (file.get() == nullptr) {
reportError(tr("file not found: %1").arg(fileName));
return;
}
// set up preview dialog
PreviewDialog preview(fileName);
auto addFunc = [&](int originId) {
FilesOrigin &origin = m_OrganizerCore->directoryStructure()->getOriginByID(originId);
QString filePath = QDir::fromNativeSeparators(ToQString(origin.getPath())) + "/" + fileName;
if (QFile::exists(filePath)) {
// it's very possible the file doesn't exist, because it's inside an archive. we don't support that
QWidget *wid = m_PluginContainer->previewGenerator().genPreview(filePath);
if (wid == nullptr) {
reportError(tr("failed to generate preview for %1").arg(filePath));
}
else {
preview.addVariant(ToQString(origin.getName()), wid);
}
}
};
addFunc(file->getOrigin());
for (auto alt : file->getAlternatives()) {
addFunc(alt.first);
}
if (preview.numVariants() > 0) {
preview.exec();
}
else {
QMessageBox::information(this, tr("Sorry"), tr("Sorry, can't preview anything. This function currently does not support extracting from bsas."));
}
}
void ModInfoDialog::on_overwriteTree_customContextMenuRequested(const QPoint &pos)
{
@@ -1211,11 +1365,39 @@ void ModInfoDialog::on_overwriteTree_customContextMenuRequested(const QPoint &po
} else {
menu.addAction(tr("Hide"), this, SLOT(hideConflictFile()));
}
menu.addAction(tr("Open/Execute"), this, SLOT(openDataFile()));
QString fileName = m_ConflictsContextItem->data(0, Qt::UserRole).toString();
if (m_PluginContainer->previewGenerator().previewSupported(QFileInfo(fileName).suffix())) {
menu.addAction(tr("Preview"), this, SLOT(previewDataFile()));
}
menu.exec(ui->overwriteTree->mapToGlobal(pos));
}
}
}
void ModInfoDialog::on_overwrittenTree_customContextMenuRequested(const QPoint &pos)
{
m_ConflictsContextItem = ui->overwrittenTree->itemAt(pos.x(), pos.y());
if (m_ConflictsContextItem != nullptr) {
if (!m_ConflictsContextItem->data(1, Qt::UserRole + 2).toBool()) {
QMenu menu;
menu.addAction(tr("Open/Execute"), this, SLOT(openDataFile()));
QString fileName = m_ConflictsContextItem->data(0, Qt::UserRole).toString();
if (m_PluginContainer->previewGenerator().previewSupported(QFileInfo(fileName).suffix())) {
menu.addAction(tr("Preview"), this, SLOT(previewDataFile()));
}
menu.exec(ui->overwrittenTree->mapToGlobal(pos));
}
}
}
void ModInfoDialog::on_overwrittenTree_itemDoubleClicked(QTreeWidgetItem *item, int)
{
+11 -1
View File
@@ -23,6 +23,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "modinfo.h"
#include "tutorabledialog.h"
#include "plugincontainer.h"
#include "organizercore.h"
#include <QDialog>
#include <QSignalMapper>
@@ -76,7 +78,7 @@ public:
* @param modInfo info structure about the mod to display
* @param parent parend widget
**/
explicit ModInfoDialog(ModInfo::Ptr modInfo, const MOShared::DirectoryEntry *directory, bool unmanaged, QWidget *parent = 0);
explicit ModInfoDialog(ModInfo::Ptr modInfo, const MOShared::DirectoryEntry *directory, bool unmanaged, OrganizerCore *organizerCore, PluginContainer *pluginContainer,QWidget *parent = 0);
~ModInfoDialog();
/**
@@ -155,6 +157,10 @@ private slots:
void hideConflictFile();
void unhideConflictFile();
int getBinaryExecuteInfo(const QFileInfo &targetInfo, QFileInfo &binaryInfo, QString &arguments);
void previewDataFile();
void openDataFile();
void thumbnailClicked(const QString &fileName);
void linkClicked(const QUrl &url);
@@ -185,6 +191,7 @@ private slots:
void on_overwriteTree_itemDoubleClicked(QTreeWidgetItem *item, int column);
void on_overwrittenTree_itemDoubleClicked(QTreeWidgetItem *item, int column);
void on_overwriteTree_customContextMenuRequested(const QPoint &pos);
void on_overwrittenTree_customContextMenuRequested(const QPoint &pos);
void on_fileTree_customContextMenuRequested(const QPoint &pos);
void on_refreshButton_clicked();
@@ -208,6 +215,9 @@ private:
QSignalMapper m_ThumbnailMapper;
QString m_RootPath;
OrganizerCore *m_OrganizerCore;
PluginContainer *m_PluginContainer;
QFileSystemModel *m_FileSystemModel;
QTreeView *m_FileTree;
QModelIndexList m_FileSelection;
+13 -1
View File
@@ -110,13 +110,19 @@
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Ini Tweaks</string>
<string>Ini Tweaks *This feature is non-functional*</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="iniTweaksList">
<property name="enabled">
<bool>false</bool>
</property>
<property name="maximumSize">
<size>
<width>228</width>
@@ -483,6 +489,9 @@ Most mods do not have optional esps, so chances are good you are looking at an e
</item>
<item>
<widget class="QTreeWidget" name="overwrittenTree">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="textElideMode">
<enum>Qt::ElideLeft</enum>
</property>
@@ -492,6 +501,9 @@ Most mods do not have optional esps, so chances are good you are looking at an e
<property name="animated">
<bool>true</bool>
</property>
<property name="columnCount">
<number>2</number>
</property>
<attribute name="headerDefaultSectionSize">
<number>365</number>
</attribute>

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