mirror of
https://github.com/ModOrganizer2/modorganizer.git
synced 2026-07-27 13:58:24 -07:00
+4
-3
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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"><html><head/><body><p>Copyright 2011-2016 Sebastian Herbord</p><p>Copyright 2016-2018 Mod Organizer 2 contributors</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string notr="true"><html><head/><body><p>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.</p><p>See the GNU General Public License for more details.</p><p>Source code can be found at <a href="https://github.com/TanninOne/modorganizer"><span style=" text-decoration: underline; color:#007af4;">GitHub</span></a>.</p></body></html></string>
|
||||
<string notr="true"><html><head/><body><p>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.</p><p>See the GNU General Public License for more details.</p><p>Source code can be found at <a href="https://github.com/Modorganizer2/modorganizer"><span style=" text-decoration: underline; color:#007af4;">GitHub</span></a>.</p></body></html></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 && 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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -599,6 +599,7 @@ protected:
|
||||
ModInfo();
|
||||
|
||||
static void updateIndices();
|
||||
static bool ByName(const ModInfo::Ptr &LHS, const ModInfo::Ptr &RHS);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
+184
-2
@@ -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
@@ -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
@@ -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
Reference in New Issue
Block a user