From c7ca9653e9763f397cdb5d60eae5e1788786b8e8 Mon Sep 17 00:00:00 2001 From: Silarn Date: Wed, 8 May 2019 23:02:51 -0500 Subject: Modify LZ4 paths to support building from source --- src/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8b368257..40fe2630 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -301,12 +301,12 @@ INCLUDE_DIRECTORIES(${project_path}/uibase/src ${project_path}/game_gamebryo/src/creation ${project_path}/game_features/src ${project_path}/githubpp/src - ${LZ4_ROOT}/include) + ${LZ4_ROOT}/lib) INCLUDE_DIRECTORIES(shared ${ZLIB_INCLUDE_DIRS}) LINK_DIRECTORIES(${lib_path} ${ZLIB_ROOT}/lib - ${LZ4_ROOT}/dll) + ${LZ4_ROOT}/bin) EXECUTE_PROCESS( COMMAND git log -1 --format=%h WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} -- cgit v1.3.1 From e349af70b72ddc0af303bf82cfc4f0e29fe1332b Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 24 May 2019 02:19:30 -0400 Subject: put source files in specific groups so they're organized in the visual studio project --- src/CMakeLists.txt | 182 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 178 insertions(+), 4 deletions(-) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 40fe2630..27dfc590 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -243,16 +243,190 @@ SET(organizer_RCS ) -SOURCE_GROUP(Source FILES ${organizer_SRCS}) -SOURCE_GROUP(Headers FILES ${organizer_HDRS}) -SOURCE_GROUP(UI FILES ${organizer_UIS}) +source_group(src REGULAR_EXPRESSION ".*\\.(h|cpp)") + +set(application + iuserinterface + main + mainwindow + moapplication + moshortcut + selfupdater + singleinstance +) + +set(browser + browserdialog + browserview +) + +set(core + categories + shared/directoryentry + directoryrefresher + executableslist + installationmanager + instancemanager + loadmechanism + nexusinterface + nxmaccessmanager + organizercore + organizerproxy +) + +set(dialogs + aboutdialog + activatemodsdialog + categoriesdialog + credentialsdialog + editexecutablesdialog + filedialogmemory + forcedloaddialog + forcedloaddialogwidget + listdialog + messagedialog + motddialog + overwriteinfodialog + queryoverwritedialog + problemsdialog + savetextasdialog + selectiondialog + syncoverwritedialog + transfersavesdialog + waitingonclosedialog +) + +set(downloads + downloadlist + downloadlistsortproxy + downloadlistwidget + downloadmanager +) + +set(locking + ilockedwaitingforprocess + lockeddialog + lockeddialogbase +) + +set(modinfo + modinfo + modinfobackup + modinfodialog + modinfoforeign + modinfooverwrite + modinforegular + modinfoseparator + modinfowithconflictinfo +) + +set(modlist + modlist + modlistsortproxy + modlistview +) + +set(plugins + plugincontainer + pluginlist + pluginlistsortproxy + pluginlistview +) + +set(previews + previewdialog + previewgenerator +) + +set(profiles + profile + profileinputdialog + profilesdialog +) + +set(resources + app_icon + version +) + +set(settings + settings + settingsdialog +) + +set(utilities + shared/appconfig + bbcode + csvbuilder + shared/error_report + eventfilter + helper + shared/leaktrace + persistentcookiejar + serverinfo + spawn + shared/stackdata + shared/util + usvfsconnector + shared/windows_error +) + +set(widgets + descriptionpage + genericicondelegate + icondelegate + lcdnumber + logbuffer + loghighlighter + modflagicondelegate + modidlineedit + noeditdelegate + qtgroupingproxy + viewmarkingscrollbar +) + +set(src_filters + application core browser dialogs downloads locking modinfo modlist plugins + previews profiles settings utilities widgets +) + +set(root_filters resources) + +foreach(filter in list ${src_filters}) + set(files) + foreach(d in lists ${${filter}}) + set(files ${files} ${d}.cpp ${d}.h ${d}.rc ${d}.inc) + endforeach() + + source_group(src\\${filter} FILES ${files}) +endforeach() + +foreach(filter in list ${root_filters}) + set(files) + foreach(d in lists ${${filter}}) + set(files ${files} ${d}.cpp ${d}.h ${d}.rc ${d}.inc) + endforeach() + + source_group(${filter} FILES ${files}) +endforeach() + +file(GLOB_RECURSE rule_files ../vsbuild/CMakeFiles/*.rule) +source_group(cmake FILES CMakeLists.txt ${rule_files}) + +source_group(qt FILES + ${organizer_QRCS} + ../vsbuild/src/ModOrganizer_autogen/mocs_compilation.cpp + ../vsbuild/src/qrc_resources.cpp + ../vsbuild/src/qrc_stylesheet_resource.cpp + ../vsbuild/src/organizer_en.qm) + # MO projects SET(dependency_project_path "${DEPENDENCIES_DIR}") SET(default_project_path "${DEPENDENCIES_DIR}/modorganizer_super") GET_FILENAME_COMPONENT(${default_project_path} ${default_project_path} REALPATH) - SET(project_path "${default_project_path}" CACHE PATH "path to the other mo projects") + #TODO this should not be a hardcoded path SET(lib_path "${project_path}/../../install/libs") -- cgit v1.3.1 From dfdea05029da0868115896af14e9f2fb2ff22eea Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 24 May 2019 02:20:57 -0400 Subject: greatly decreased the verbosity of builds always include the INSTALL project in the solution build --- CMakeLists.txt | 3 +++ src/CMakeLists.txt | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'src/CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 3955cc75..f424dc78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,9 @@ PROJECT(organizer) # sets ModOrganizer as StartUp Project in Visual Studio set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" PROPERTY VS_STARTUP_PROJECT "ModOrganizer") +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +set(CMAKE_INSTALL_MESSAGE NEVER) + SET(DEPENDENCIES_DIR CACHE PATH "") # hint to find qt in dependencies path LIST(APPEND CMAKE_PREFIX_PATH ${QT_ROOT}/lib/cmake) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 27dfc590..0d12792a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -446,7 +446,7 @@ FIND_PACKAGE(Qt5LinguistTools) QT5_WRAP_UI(organizer_UIHDRS ${organizer_UIS}) QT5_ADD_RESOURCES(organizer_RCCPPS ${organizer_QRCS}) SET(mo_translation_sources ${CMAKE_SOURCE_DIR}/src ${project_path}/uibase/src) -QT5_CREATE_TRANSLATION(organizer_translations_qm ${mo_translation_sources} ${CMAKE_SOURCE_DIR}/src/organizer_en.ts) +QT5_CREATE_TRANSLATION(organizer_translations_qm ${mo_translation_sources} ${CMAKE_SOURCE_DIR}/src/organizer_en.ts OPTIONS -silent) ADD_CUSTOM_TARGET(translations DEPENDS ${organizer_translations_qm}) INCLUDE_DIRECTORIES(${Qt5Declarative_INCLUDES}) @@ -549,11 +549,11 @@ SET(windeploy_parameters "--no-translations --plugindir qtplugins --libdir dlls INSTALL( CODE "EXECUTE_PROCESS(COMMAND - ${qt5bin}/windeployqt.exe ModOrganizer.exe --webenginewidgets --websockets ${windeploy_parameters} + ${qt5bin}/windeployqt.exe --verbose 0 ModOrganizer.exe --webenginewidgets --websockets ${windeploy_parameters} WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin ) EXECUTE_PROCESS(COMMAND - ${qt5bin}/windeployqt.exe uibase.dll ${windeploy_parameters} + ${qt5bin}/windeployqt.exe --verbose 0 uibase.dll ${windeploy_parameters} WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin ) file(REMOVE_RECURSE ${CMAKE_INSTALL_PREFIX}/bin/platforms) -- cgit v1.3.1 From 953ab4e6f6e25b04890f7201b783c2693748af28 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 24 May 2019 03:53:31 -0400 Subject: turned autorcc on so no need for qt5_add_resources() anymore qt5_wrap_ui() is redundant since autouic is already on generate INSTALL.vcxproj.user to have the proper debugging settings so ModOrganizer.exe is started from install/bin --- CMakeLists.txt | 13 +++++++++++++ src/CMakeLists.txt | 5 ++--- 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'src/CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index f424dc78..ced097e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,3 +16,16 @@ LIST(APPEND CMAKE_PREFIX_PATH ${QT_ROOT}/lib/cmake) LIST(APPEND CMAKE_PREFIX_PATH ${LZ4_ROOT}/dll) ADD_SUBDIRECTORY(src) + +set(vcxproj_user_file "${CMAKE_CURRENT_BINARY_DIR}/INSTALL.vcxproj.user") +if(NOT EXISTS ${vcxproj_user_file}) + FILE(WRITE ${vcxproj_user_file} + "\n" + "\n" + "\n" + "${CMAKE_INSTALL_PREFIX}/bin\n" + "WindowsLocalDebugger" + "${CMAKE_INSTALL_PREFIX}/bin/ModOrganizer.exe\n" + "\n" + "\n") +endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0d12792a..8d13b557 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -434,6 +434,7 @@ SET(lib_path "${project_path}/../../install/libs") SET(CMAKE_INCLUDE_CURRENT_DIR ON) SET(CMAKE_AUTOMOC ON) SET(CMAKE_AUTOUIC ON) +SET(CMAKE_AUTORCC ON) FIND_PACKAGE(Qt5Widgets REQUIRED) FIND_PACKAGE(Qt5QuickWidgets REQUIRED) FIND_PACKAGE(Qt5Quick REQUIRED) @@ -443,8 +444,6 @@ FIND_PACKAGE(Qt5WebEngineWidgets REQUIRED) FIND_PACKAGE(Qt5WebSockets REQUIRED) FIND_PACKAGE(Qt5Qml REQUIRED) FIND_PACKAGE(Qt5LinguistTools) -QT5_WRAP_UI(organizer_UIHDRS ${organizer_UIS}) -QT5_ADD_RESOURCES(organizer_RCCPPS ${organizer_QRCS}) SET(mo_translation_sources ${CMAKE_SOURCE_DIR}/src ${project_path}/uibase/src) QT5_CREATE_TRANSLATION(organizer_translations_qm ${mo_translation_sources} ${CMAKE_SOURCE_DIR}/src/organizer_en.ts OPTIONS -silent) ADD_CUSTOM_TARGET(translations DEPENDS ${organizer_translations_qm}) @@ -496,7 +495,7 @@ ELSE() SET(usvfs_name usvfs_x86) ENDIF() -ADD_EXECUTABLE(ModOrganizer WIN32 ${organizer_HDRS} ${organizer_SRCS} ${organizer_UIHDRS} ${organizer_RCS} ${organizer_RCCPPS} ${organizer_translations_qm}) +ADD_EXECUTABLE(ModOrganizer WIN32 ${organizer_HDRS} ${organizer_SRCS} ${organizer_RCS} ${organizer_QRCS} ${organizer_translations_qm}) TARGET_LINK_LIBRARIES(ModOrganizer Qt5::Widgets Qt5::WinExtras Qt5::WebEngineWidgets Qt5::Quick Qt5::Qml Qt5::QuickWidgets Qt5::Network Qt5::WebSockets -- cgit v1.3.1 From 350e4fa97fc0a2dd3a63ad9ce1384e5ca925af9b Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 24 May 2019 04:13:18 -0400 Subject: using builtin properties for autogen source groups, everything is now in the autogen filter --- src/CMakeLists.txt | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8d13b557..e59842fb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -344,11 +344,6 @@ set(profiles profilesdialog ) -set(resources - app_icon - version -) - set(settings settings settingsdialog @@ -390,36 +385,21 @@ set(src_filters previews profiles settings utilities widgets ) -set(root_filters resources) - foreach(filter in list ${src_filters}) set(files) foreach(d in lists ${${filter}}) - set(files ${files} ${d}.cpp ${d}.h ${d}.rc ${d}.inc) + set(files ${files} ${d}.cpp ${d}.h ${d}.inc) endforeach() source_group(src\\${filter} FILES ${files}) endforeach() -foreach(filter in list ${root_filters}) - set(files) - foreach(d in lists ${${filter}}) - set(files ${files} ${d}.cpp ${d}.h ${d}.rc ${d}.inc) - endforeach() - - source_group(${filter} FILES ${files}) -endforeach() - file(GLOB_RECURSE rule_files ../vsbuild/CMakeFiles/*.rule) -source_group(cmake FILES CMakeLists.txt ${rule_files}) - -source_group(qt FILES - ${organizer_QRCS} - ../vsbuild/src/ModOrganizer_autogen/mocs_compilation.cpp - ../vsbuild/src/qrc_resources.cpp - ../vsbuild/src/qrc_stylesheet_resource.cpp - ../vsbuild/src/organizer_en.qm) +file(GLOB qm_files ../vsbuild/src/*.qm) +source_group(cmake FILES CMakeLists.txt) +source_group(autogen FILES ${rule_files} ${qm_files}) +source_group(resources FILES ${organizer_RCS} ${organizer_QRCS}) # MO projects SET(dependency_project_path "${DEPENDENCIES_DIR}") @@ -435,6 +415,7 @@ SET(CMAKE_INCLUDE_CURRENT_DIR ON) SET(CMAKE_AUTOMOC ON) SET(CMAKE_AUTOUIC ON) SET(CMAKE_AUTORCC ON) + FIND_PACKAGE(Qt5Widgets REQUIRED) FIND_PACKAGE(Qt5QuickWidgets REQUIRED) FIND_PACKAGE(Qt5Quick REQUIRED) @@ -444,7 +425,12 @@ FIND_PACKAGE(Qt5WebEngineWidgets REQUIRED) FIND_PACKAGE(Qt5WebSockets REQUIRED) FIND_PACKAGE(Qt5Qml REQUIRED) FIND_PACKAGE(Qt5LinguistTools) + SET(mo_translation_sources ${CMAKE_SOURCE_DIR}/src ${project_path}/uibase/src) +set_property(GLOBAL PROPERTY AUTOGEN_SOURCE_GROUP autogen) +set_property(GLOBAL PROPERTY AUTOMOC_SOURCE_GROUP autogen) +set_property(GLOBAL PROPERTY AUTORCC_SOURCE_GROUP autogen) + QT5_CREATE_TRANSLATION(organizer_translations_qm ${mo_translation_sources} ${CMAKE_SOURCE_DIR}/src/organizer_en.ts OPTIONS -silent) ADD_CUSTOM_TARGET(translations DEPENDS ${organizer_translations_qm}) -- cgit v1.3.1 From f8d319cfba8a1dfce919680ef5e4046a68ae9803 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 24 May 2019 06:25:58 -0400 Subject: added precompiled headers --- src/CMakeLists.txt | 24 +++++ src/pch.cpp | 1 + src/pch.h | 257 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 282 insertions(+) create mode 100644 src/pch.cpp create mode 100644 src/pch.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e59842fb..496bd5fb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,6 +9,28 @@ IF(CMAKE_SIZEOF_VOID_P EQUAL 8) SET(PROJ_ARCH x64) ENDIF() +macro(add_msvc_precompiled_header PrecompiledHeader PrecompiledSource SourcesVar HeadersVar) + if(MSVC) + get_filename_component(PrecompiledBasename ${PrecompiledHeader} NAME_WE) + set(PrecompiledBinary "${CMAKE_CURRENT_BINARY_DIR}/${PrecompiledBasename}.pch") + set(Sources ${${SourcesVar}}) + + set_source_files_properties( + ${PrecompiledSource} PROPERTIES + COMPILE_FLAGS "/Yc\"${PrecompiledHeader}\" /Fp\"${PrecompiledBinary}\"" + OBJECT_OUTPUTS "${PrecompiledBinary}") + + set_source_files_properties( + ${Sources} PROPERTIES + COMPILE_FLAGS "/Yu\"${PrecompiledHeader}\" /FI\"${PrecompiledHeader}\" /Fp\"${PrecompiledBinary}\"" + OBJECT_DEPENDS "${PrecompiledBinary}") + + list(APPEND ${SourcesVar} ${PrecompiledSource}) + list(APPEND ${HeadersVar} ${PrecompiledHeader}) + endif(MSVC) +endmacro(add_msvc_precompiled_header) + + SET(organizer_SRCS transfersavesdialog.cpp syncoverwritedialog.cpp @@ -407,6 +429,8 @@ SET(default_project_path "${DEPENDENCIES_DIR}/modorganizer_super") GET_FILENAME_COMPONENT(${default_project_path} ${default_project_path} REALPATH) SET(project_path "${default_project_path}" CACHE PATH "path to the other mo projects") +add_msvc_precompiled_header("pch.h" "pch.cpp" organizer_SRCS organizer_HDRS) + #TODO this should not be a hardcoded path SET(lib_path "${project_path}/../../install/libs") diff --git a/src/pch.cpp b/src/pch.cpp new file mode 100644 index 00000000..1d9f38c5 --- /dev/null +++ b/src/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" diff --git a/src/pch.h b/src/pch.h new file mode 100644 index 00000000..1cedb945 --- /dev/null +++ b/src/pch.h @@ -0,0 +1,257 @@ +// std +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// windows +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// boost +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// openssl +#include + +// qt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -- cgit v1.3.1 From 4183e5837f6363193b1a3d7f7ed56cd9ab89ec36 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sun, 26 May 2019 04:27:16 -0400 Subject: set use_folders, puts all cmake generated projects into a folder --- src/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 496bd5fb..fcfb8a12 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -451,6 +451,7 @@ FIND_PACKAGE(Qt5Qml REQUIRED) FIND_PACKAGE(Qt5LinguistTools) SET(mo_translation_sources ${CMAKE_SOURCE_DIR}/src ${project_path}/uibase/src) +set_property(GLOBAL PROPERTY USE_FOLDERS ON) set_property(GLOBAL PROPERTY AUTOGEN_SOURCE_GROUP autogen) set_property(GLOBAL PROPERTY AUTOMOC_SOURCE_GROUP autogen) set_property(GLOBAL PROPERTY AUTORCC_SOURCE_GROUP autogen) -- cgit v1.3.1 From e821768f7997d8d86585db9490554a1202aa1ad9 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sun, 26 May 2019 08:07:10 -0400 Subject: added /permissive- --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 40fe2630..57cd2f77 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -333,7 +333,7 @@ TARGET_LINK_LIBRARIES(ModOrganizer Dbghelp advapi32 Version Shlwapi liblz4 Crypt32) IF (MSVC) - SET_TARGET_PROPERTIES(ModOrganizer PROPERTIES COMPILE_FLAGS "/std:c++latest") + SET_TARGET_PROPERTIES(ModOrganizer PROPERTIES COMPILE_FLAGS "/std:c++latest /permissive-") ENDIF() IF (MSVC AND "${CMAKE_SIZEOF_VOID_P}" EQUAL 4) # 32 bits -- cgit v1.3.1 From 4d90e502266dfb886e1d089fb26780d19a38e5a7 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 29 May 2019 09:45:30 -0400 Subject: added new filterwidget, will eventually get the operators functionality from the mod/pluging lists --- src/CMakeLists.txt | 3 ++ src/filterwidget.cpp | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/filterwidget.h | 27 ++++++++++++++++ src/modinfodialog.cpp | 2 ++ src/modinfodialog.h | 3 ++ src/modinfodialog.ui | 7 +--- 6 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 src/filterwidget.cpp create mode 100644 src/filterwidget.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cae26334..93597d62 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -115,6 +115,7 @@ SET(organizer_SRCS lcdnumber.cpp forcedloaddialog.cpp forcedloaddialogwidget.cpp + filterwidget.cpp shared/windows_error.cpp shared/error_report.cpp @@ -211,6 +212,7 @@ SET(organizer_HDRS lcdnumber.h forcedloaddialog.h forcedloaddialogwidget.h + filterwidget.h shared/windows_error.h shared/error_report.h @@ -391,6 +393,7 @@ set(utilities set(widgets descriptionpage genericicondelegate + filterwidget icondelegate lcdnumber logbuffer diff --git a/src/filterwidget.cpp b/src/filterwidget.cpp new file mode 100644 index 00000000..39324bc6 --- /dev/null +++ b/src/filterwidget.cpp @@ -0,0 +1,88 @@ +#include "filterwidget.h" +#include "eventfilter.h" + +FilterWidget::FilterWidget() + : m_edit(nullptr), m_clear(nullptr), m_buddy(nullptr) +{ +} + +void FilterWidget::set(QLineEdit* edit) +{ + if (m_clear) { + delete m_clear; + m_clear = nullptr; + } + + m_edit = edit; + if (!m_edit) { + return; + } + + createClear(); + hookEvents(); +} + +void FilterWidget::clear() +{ + if (!m_edit) { + return; + } + + m_edit->clear(); +} + +void FilterWidget::createClear() +{ + m_clear = new QToolButton(m_edit); + + QPixmap pixmap(":/MO/gui/edit_clear"); + m_clear->setIcon(QIcon(pixmap)); + m_clear->setIconSize(pixmap.size()); + m_clear->setCursor(Qt::ArrowCursor); + m_clear->setStyleSheet("QToolButton { border: none; padding: 0px; }"); + m_clear->hide(); + + QObject::connect(m_clear, &QToolButton::clicked, [&]{ clear(); }); + QObject::connect(m_edit, &QLineEdit::textChanged, [&]{ onTextChanged(); }); + + repositionClearButton(); +} + +void FilterWidget::hookEvents() +{ + auto* f = new EventFilter(m_edit, [&](auto* w, auto* e) { + if (e->type() == QEvent::Resize) { + onResized(); + } + + return false; + }); + + m_edit->installEventFilter(f); +} + +void FilterWidget::onTextChanged() +{ + m_clear->setVisible(!m_edit->text().isEmpty()); +} + +void FilterWidget::onResized() +{ + repositionClearButton(); +} + +void FilterWidget::repositionClearButton() +{ + if (!m_clear) { + return; + } + + const QSize sz = m_clear->sizeHint(); + const int frame = m_edit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + const auto r = m_edit->rect(); + + const auto x = r.right() - frame - sz.width(); + const auto y = (r.bottom() + 1 - sz.height()) / 2; + + m_clear->move(x, y); +} diff --git a/src/filterwidget.h b/src/filterwidget.h new file mode 100644 index 00000000..07e216f1 --- /dev/null +++ b/src/filterwidget.h @@ -0,0 +1,27 @@ +#ifndef FILTERWIDGET_H +#define FILTERWIDGET_H + +class FilterWidget +{ +public: + FilterWidget(); + + void set(QLineEdit* edit); + void buddy(QWidget* w); + + void clear(); + +private: + QLineEdit* m_edit; + QToolButton* m_clear; + QWidget* m_buddy; + + void createClear(); + void hookEvents(); + void repositionClearButton(); + + void onTextChanged(); + void onResized(); +}; + +#endif // FILTERWIDGET_H diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 9db31da3..1e933a74 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -453,6 +453,8 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo m_nonconflictExpander.set(ui->noConflictExpander, ui->noConflictTree); + m_advancedConflictFilter.set(ui->conflictsAdvancedFilter); + // left-elide the overwrites column so that the nearest are visible ui->conflictsAdvancedList->setItemDelegateForColumn( 0, new ElideLeftDelegate(ui->conflictsAdvancedList)); diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 69b0069e..5169a993 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -25,6 +25,7 @@ along with Mod Organizer. If not, see . #include "tutorabledialog.h" #include "plugincontainer.h" #include "organizercore.h" +#include "filterwidget.h" #include #include @@ -427,6 +428,7 @@ private: std::map m_RealTabPos; ExpanderWidget m_overwriteExpander, m_overwrittenExpander, m_nonconflictExpander; + FilterWidget m_advancedConflictFilter; void refreshConflictLists(bool refreshGeneral, bool refreshAdvanced); @@ -448,6 +450,7 @@ private: const QString& fileName, const QString& relativeName, const MOShared::FileEntry::AlternativesVector& alternatives); + void restoreTabState(const QByteArray &state); void restoreConflictExpandersState(const QByteArray &state); diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index e391da46..b55c0f07 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -782,7 +782,7 @@ text-align: left; 0 - + Filter @@ -1201,11 +1201,6 @@ p, li { white-space: pre-wrap; } QLineEdit
modidlineedit.h
- - MOBase::LineEditClear - QLineEdit -
lineeditclear.h
-
-- cgit v1.3.1 From 853c95b921f4fc3beb8daf71d79b44aa1ab06c92 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 10 Jun 2019 16:43:39 -0400 Subject: added new statusbar class, moved refresh progress bar to it --- src/CMakeLists.txt | 3 +++ src/mainwindow.cpp | 20 ++++---------------- src/mainwindow.h | 5 +++-- src/statusbar.cpp | 27 +++++++++++++++++++++++++++ src/statusbar.h | 20 ++++++++++++++++++++ 5 files changed, 57 insertions(+), 18 deletions(-) create mode 100644 src/statusbar.cpp create mode 100644 src/statusbar.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 93597d62..71f87a8a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -116,6 +116,7 @@ SET(organizer_SRCS forcedloaddialog.cpp forcedloaddialogwidget.cpp filterwidget.cpp + statusbar.cpp shared/windows_error.cpp shared/error_report.cpp @@ -213,6 +214,7 @@ SET(organizer_HDRS forcedloaddialog.h forcedloaddialogwidget.h filterwidget.h + statusbar.h shared/windows_error.h shared/error_report.h @@ -277,6 +279,7 @@ set(application moshortcut selfupdater singleinstance + statusbar ) set(browser diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 911d0ff1..67d0f2ff 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -77,6 +77,7 @@ along with Mod Organizer. If not, see . #include "nxmaccessmanager.h" #include "appconfig.h" #include "eventfilter.h" +#include "statusbar.h" #include #include #include @@ -242,13 +243,7 @@ MainWindow::MainWindow(QSettings &initSettings connect(ui->logList->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), ui->logList, SLOT(scrollToBottom())); - m_RefreshProgress = new QProgressBar(statusBar()); - m_RefreshProgress->setTextVisible(true); - m_RefreshProgress->setRange(0, 100); - m_RefreshProgress->setValue(0); - m_RefreshProgress->setVisible(false); - statusBar()->addWidget(m_RefreshProgress, 1000); - statusBar()->clearMessage(); + m_statusBar.reset(new StatusBar(statusBar())); updateProblemsButton(); @@ -2534,15 +2529,8 @@ void MainWindow::setESPListSorting(int index) void MainWindow::refresher_progress(int percent) { - if (percent == 100) { - m_RefreshProgress->setVisible(false); - this->setEnabled(true); - } else if (!m_RefreshProgress->isVisible()) { - this->setEnabled(false); - m_RefreshProgress->setVisible(true); - m_RefreshProgress->setRange(0, 100); - m_RefreshProgress->setValue(percent); - } + setEnabled(percent == 100); + m_statusBar->setProgress(percent); } void MainWindow::directory_refreshed() diff --git a/src/mainwindow.h b/src/mainwindow.h index b8d9f49f..734ece88 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -37,6 +37,7 @@ struct Executable; class CategoryFactory; class LockedDialogBase; class OrganizerCore; +class StatusBar; #include "plugincontainer.h" //class PluginContainer; class PluginListSortProxy; namespace BSA { class Archive; } @@ -75,7 +76,6 @@ class QListWidgetItem; class QMenu; class QModelIndex; class QPoint; -class QProgressBar; class QProgressDialog; class QTranslator; class QTreeWidgetItem; @@ -329,6 +329,8 @@ private: // the window is closed and the menubar is hidden bool m_menuBarVisible; + std::unique_ptr m_statusBar; + // last separator on the toolbar, used to add spacer for right-alignment and // as an insert point for executables QAction* m_linksSeparator; @@ -338,7 +340,6 @@ private: int m_OldProfileIndex; std::vector m_ModNameList; // the mod-list to go with the directory structure - QProgressBar *m_RefreshProgress; bool m_Refreshing; QStringList m_DefaultArchives; diff --git a/src/statusbar.cpp b/src/statusbar.cpp new file mode 100644 index 00000000..7370662a --- /dev/null +++ b/src/statusbar.cpp @@ -0,0 +1,27 @@ +#include "statusbar.h" + +StatusBar::StatusBar(QStatusBar* bar) + : m_bar(bar), m_nexusAPI(new QLabel), m_progress(new QProgressBar) +{ + m_progress->setTextVisible(true); + m_progress->setRange(0, 100); + m_progress->setValue(0); + m_progress->setVisible(false); + + m_bar->addPermanentWidget(m_nexusAPI); + m_bar->addPermanentWidget(m_progress); + + m_bar->clearMessage(); +} + +void StatusBar::setProgress(int percent) +{ + qDebug().nospace() << "progress: " << percent; + + if (percent < 0 || percent >= 100) { + m_progress->setVisible(false); + } else if (!m_progress->isVisible()) { + m_progress->setVisible(true); + m_progress->setValue(percent); + } +} diff --git a/src/statusbar.h b/src/statusbar.h new file mode 100644 index 00000000..f3ad3081 --- /dev/null +++ b/src/statusbar.h @@ -0,0 +1,20 @@ +#ifndef MO_STATUSBAR_H +#define MO_STATUSBAR_H + +#include +#include + +class StatusBar +{ +public: + StatusBar(QStatusBar* bar); + + void setProgress(int percent); + +private: + QStatusBar* m_bar; + QLabel* m_api; + QProgressBar* m_progress; +}; + +#endif // MO_STATUSBAR_H -- cgit v1.3.1 From 616925ebbb8e916119f8dbee0c1f0cb97b14a68b Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 14 Jun 2019 01:38:07 -0400 Subject: moved api user account classes to their own files api label in status bar: - now shows when not logged in - changed some of the colour thresholds to correspond to real throttling numbers make sure the api key is also cleared from the access manager when clearing from the settings removed unused bool m_ValidateAttempted in NXMAccessManager update the window title and status bar api label with correct values on startup, which fixes incorrect values when "restarting" MO after changing nexus settings --- src/CMakeLists.txt | 3 ++ src/apiuseraccount.cpp | 67 ++++++++++++++++++++++++ src/apiuseraccount.h | 129 ++++++++++++++++++++++++++++++++++++++++++++++ src/mainwindow.cpp | 13 +++-- src/nexusinterface.cpp | 78 ++++------------------------ src/nexusinterface.h | 131 ++--------------------------------------------- src/nxmaccessmanager.cpp | 17 ++++-- src/nxmaccessmanager.h | 7 +-- src/settingsdialog.cpp | 6 ++- src/statusbar.cpp | 34 +++++++----- 10 files changed, 263 insertions(+), 222 deletions(-) create mode 100644 src/apiuseraccount.cpp create mode 100644 src/apiuseraccount.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 71f87a8a..5623a851 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -117,6 +117,7 @@ SET(organizer_SRCS forcedloaddialogwidget.cpp filterwidget.cpp statusbar.cpp + apiuseraccount.cpp shared/windows_error.cpp shared/error_report.cpp @@ -215,6 +216,7 @@ SET(organizer_HDRS forcedloaddialogwidget.h filterwidget.h statusbar.h + apiuseraccount.h shared/windows_error.h shared/error_report.h @@ -299,6 +301,7 @@ set(core nxmaccessmanager organizercore organizerproxy + apiuseraccount ) set(dialogs diff --git a/src/apiuseraccount.cpp b/src/apiuseraccount.cpp new file mode 100644 index 00000000..b901e41a --- /dev/null +++ b/src/apiuseraccount.cpp @@ -0,0 +1,67 @@ +#include "apiuseraccount.h" + +APIUserAccount::APIUserAccount() + : m_type(APIUserAccountTypes::None) +{ +} + +const QString& APIUserAccount::id() const +{ + return m_id; +} + +const QString& APIUserAccount::name() const +{ + return m_name; +} + +APIUserAccountTypes APIUserAccount::type() const +{ + return m_type; +} + +const APILimits& APIUserAccount::limits() const +{ + return m_limits; +} + +APIUserAccount& APIUserAccount::id(const QString& id) +{ + m_id = id; + return *this; +} + +APIUserAccount& APIUserAccount::name(const QString& name) +{ + m_name = name; + return *this; +} + +APIUserAccount& APIUserAccount::type(APIUserAccountTypes type) +{ + m_type = type; + return *this; +} + +APIUserAccount& APIUserAccount::limits(const APILimits& limits) +{ + m_limits = limits; + return *this; +} + +int APIUserAccount::remainingRequests() const +{ + return std::max( + m_limits.remainingDailyRequests, + m_limits.remainingHourlyRequests); +} + +bool APIUserAccount::shouldThrottle() const +{ + return (remainingRequests() < ThrottleThreshold); +} + +bool APIUserAccount::exhausted() const +{ + return (remainingRequests() <= 0); +} diff --git a/src/apiuseraccount.h b/src/apiuseraccount.h new file mode 100644 index 00000000..8a238d71 --- /dev/null +++ b/src/apiuseraccount.h @@ -0,0 +1,129 @@ +#ifndef APIUSERACCOUNT_H +#define APIUSERACCOUNT_H + +#include + +/** +* represents user account types on a mod provider website such as nexus +*/ +enum class APIUserAccountTypes +{ + // not logged in + None = 0, + + // regular account + Regular, + + // premium account + Premium +}; + + +/** +* current limits imposed on the user account +**/ +struct APILimits +{ + // maximum number of requests per day + int maxDailyRequests = 0; + + // remaining number of requests today + int remainingDailyRequests = 0; + + // maximum number of requests per hour + int maxHourlyRequests = 0; + + // remaining number of requests this hour + int remainingHourlyRequests = 0; +}; + + +/** +* API statistics +*/ +struct APIStats +{ + // number of API requests currently queued + int requestsQueued = 0; +}; + + +/** +* represents a user account on the mod provier website +*/ +class APIUserAccount +{ +public: + // when the number of remanining requests is under this number, further + // requests will be throttled by avoiding non-critical ones + static const int ThrottleThreshold = 200; + + APIUserAccount(); + + /** + * user id + */ + const QString& id() const; + + /** + * user name + */ + const QString& name() const; + + /** + * account type + */ + APIUserAccountTypes type() const; + + /** + * current API limits + */ + const APILimits& limits() const; + + + /** + * sets the user id + */ + APIUserAccount& id(const QString& id); + + /** + * sets the user name + **/ + APIUserAccount& name(const QString& name); + + /** + * sets the acount type + */ + APIUserAccount& type(APIUserAccountTypes type); + + /** + * sets the current limits + */ + APIUserAccount& limits(const APILimits& limits); + + + /** + * returns the number of remaining requests + */ + int remainingRequests() const; + + /** + * whether the number of remaining requests is low enough that further + * requests should be throttled + */ + bool shouldThrottle() const; + + /** + * true if all the remaining requests have been used and the API will refuse + * further requests + */ + bool exhausted() const; + +private: + QString m_id, m_name; + APIUserAccountTypes m_type; + APILimits m_limits; + APIStats m_stats; +}; + +#endif // APIUSERACCOUNT_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index c255759b..5018479d 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -225,8 +225,17 @@ MainWindow::MainWindow(QSettings &initSettings QWebEngineProfile::defaultProfile()->setHttpCacheMaximumSize(52428800); QWebEngineProfile::defaultProfile()->setCachePath(m_OrganizerCore.settings().getCacheDirectory()); QWebEngineProfile::defaultProfile()->setPersistentStoragePath(m_OrganizerCore.settings().getCacheDirectory()); + ui->setupUi(this); - updateWindowTitle({}); + + { + auto* ni = NexusInterface::instance(&m_PluginContainer); + + updateWindowTitle(ni->getAPIUserAccount()); + + m_statusBar.reset(new StatusBar(statusBar(), ui)); + m_statusBar->setAPI(ni->getAPIStats(), ni->getAPIUserAccount()); + } languageChange(m_OrganizerCore.settings().language()); @@ -244,8 +253,6 @@ MainWindow::MainWindow(QSettings &initSettings connect(ui->logList->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), ui->logList, SLOT(scrollToBottom())); - m_statusBar.reset(new StatusBar(statusBar(), ui)); - updateProblemsButton(); setupToolbar(); diff --git a/src/nexusinterface.cpp b/src/nexusinterface.cpp index 8362143a..ee9acf2c 100644 --- a/src/nexusinterface.cpp +++ b/src/nexusinterface.cpp @@ -49,71 +49,6 @@ void throttledWarning(const APIUserAccount& user) } -APIUserAccount::APIUserAccount() - : m_type(APIUserAccountTypes::None) -{ -} - -const QString& APIUserAccount::id() const -{ - return m_id; -} - -const QString& APIUserAccount::name() const -{ - return m_name; -} - -APIUserAccountTypes APIUserAccount::type() const -{ - return m_type; -} - -const APILimits& APIUserAccount::limits() const -{ - return m_limits; -} - -APIUserAccount& APIUserAccount::id(const QString& id) -{ - m_id = id; - return *this; -} - -APIUserAccount& APIUserAccount::name(const QString& name) -{ - m_name = name; - return *this; -} - -APIUserAccount& APIUserAccount::type(APIUserAccountTypes type) -{ - m_type = type; - return *this; -} - -APIUserAccount& APIUserAccount::limits(const APILimits& limits) -{ - m_limits = limits; - return *this; -} - -int APIUserAccount::remainingRequests() const -{ - return m_limits.remainingDailyRequests + m_limits.remainingHourlyRequests; -} - -bool APIUserAccount::shouldThrottle() const -{ - return (remainingRequests() < ThrottleThreshold); -} - -bool APIUserAccount::exhausted() const -{ - return (remainingRequests() <= 0); -} - - NexusBridge::NexusBridge(PluginContainer *pluginContainer, const QString &subModule) : m_Interface(NexusInterface::instance(pluginContainer)) , m_SubModule(subModule) @@ -325,7 +260,7 @@ void NexusInterface::loginCompleted() void NexusInterface::setUserAccount(const APIUserAccount& user) { m_User = user; - emit requestsChanged(stats(), m_User); + emit requestsChanged(getAPIStats(), m_User); } void NexusInterface::interpretNexusFileName(const QString &fileName, QString &modName, int &modID, bool query) @@ -884,7 +819,7 @@ void NexusInterface::requestFinished(std::list::iterator iter) qWarning("All API requests have been consumed and are now being denied."); } - emit requestsChanged(stats(), m_User); + emit requestsChanged(getAPIStats(), m_User); qWarning("Error: %s", reply->errorString().toUtf8().constData()); } else { qWarning("request failed: %s", reply->errorString().toUtf8().constData()); @@ -960,7 +895,7 @@ void NexusInterface::requestFinished(std::list::iterator iter) } m_User.limits(parseLimits(reply)); - emit requestsChanged(stats(), m_User); + emit requestsChanged(getAPIStats(), m_User); } else { emit nxmRequestFailed(iter->m_GameName, iter->m_ModID, iter->m_FileID, iter->m_UserData, iter->m_ID, reply->error(), tr("invalid response")); } @@ -1017,7 +952,12 @@ void NexusInterface::requestTimeout() } } -APIStats NexusInterface::stats() const +APIUserAccount NexusInterface::getAPIUserAccount() const +{ + return m_User; +} + +APIStats NexusInterface::getAPIStats() const { APIStats stats; stats.requestsQueued = m_RequestQueue.size(); diff --git a/src/nexusinterface.h b/src/nexusinterface.h index 6c4e8d11..6e768149 100644 --- a/src/nexusinterface.h +++ b/src/nexusinterface.h @@ -20,6 +20,8 @@ along with Mod Organizer. If not, see . #ifndef NEXUSINTERFACE_H #define NEXUSINTERFACE_H +#include "apiuseraccount.h" + #include #include #include @@ -40,130 +42,6 @@ class NexusInterface; class NXMAccessManager; -/** - * represents user account types on a mod provider website such as nexus - */ -enum class APIUserAccountTypes -{ - // not logged in - None = 0, - - // regular account - Regular, - - // premium account - Premium -}; - - -/** - * current limits imposed on the user account - **/ -struct APILimits -{ - // maximum number of requests per day - int maxDailyRequests = 0; - - // remaining number of requests today - int remainingDailyRequests = 0; - - // maximum number of requests per hour - int maxHourlyRequests = 0; - - // remaining number of requests this hour - int remainingHourlyRequests = 0; -}; - - -/** - * API statistics - */ -struct APIStats -{ - // number of API requests currently queued - int requestsQueued = 0; -}; - - -/** - * represents a user account on the mod provier website - */ -class APIUserAccount -{ -public: - // when the number of remanining requests is under this number, further - // requests will be throttled by avoiding non-critical ones - static const int ThrottleThreshold = 300; - - APIUserAccount(); - - /** - * user id - */ - const QString& id() const; - - /** - * user name - */ - const QString& name() const; - - /** - * account type - */ - APIUserAccountTypes type() const; - - /** - * current API limits - */ - const APILimits& limits() const; - - - /** - * sets the user id - */ - APIUserAccount& id(const QString& id); - - /** - * sets the user name - **/ - APIUserAccount& name(const QString& name); - - /** - * sets the acount type - */ - APIUserAccount& type(APIUserAccountTypes type); - - /** - * sets the current limits - */ - APIUserAccount& limits(const APILimits& limits); - - - /** - * returns the number of remaining requests - */ - int remainingRequests() const; - - /** - * whether the number of remaining requests is low enough that further - * requests should be throttled - */ - bool shouldThrottle() const; - - /** - * true if all the remaining requests have been used and the API will refuse - * further requests - */ - bool exhausted() const; - -private: - QString m_id, m_name; - APIUserAccountTypes m_type; - APILimits m_limits; - APIStats m_stats; -}; - - /** * @brief convenience class to make nxm requests easier * usually, all objects that started a nxm request will be signaled if one finished. @@ -521,6 +399,9 @@ public: std::vector> getGameChoices(const MOBase::IPluginGame *game); + APIUserAccount getAPIUserAccount() const; + APIStats getAPIStats() const; + public: /** @@ -667,8 +548,6 @@ private: MOBase::VersionInfo m_MOVersion; PluginContainer *m_PluginContainer; APIUserAccount m_User; - - APIStats stats() const; }; #endif // NEXUSINTERFACE_H diff --git a/src/nxmaccessmanager.cpp b/src/nxmaccessmanager.cpp index 5886d3ee..ee6c03f1 100644 --- a/src/nxmaccessmanager.cpp +++ b/src/nxmaccessmanager.cpp @@ -190,6 +190,7 @@ void NXMAccessManager::apiCheck(const QString &apiKey, bool force) emit validateSuccessful(false); return; } + m_ApiKey = apiKey; startValidationCheck(); } @@ -218,6 +219,13 @@ QString NXMAccessManager::apiKey() const return m_ApiKey; } +void NXMAccessManager::clearApiKey() +{ + m_ApiKey = ""; + m_ValidateState = VALIDATE_NOT_VALID; + + emit credentialsReceived(APIUserAccount()); +} void NXMAccessManager::validateTimeout() { @@ -233,7 +241,6 @@ void NXMAccessManager::validateTimeout() if (m_ValidateReply != nullptr) { m_ValidateReply->deleteLater(); m_ValidateReply = nullptr; - m_ValidateAttempted = false; // this usually means we might have success later } emit validateFailed(tr("There was a timeout during the request")); @@ -280,17 +287,21 @@ void NXMAccessManager::validateFinished() QString name = credentialsData.value("name").toString(); bool premium = credentialsData.value("is_premium").toBool(); - emit credentialsReceived(APIUserAccount() + const auto user = APIUserAccount() .id(QString("%1").arg(id)) .name(name) .type(premium ? APIUserAccountTypes::Premium : APIUserAccountTypes::Regular) - .limits(NexusInterface::parseLimits(m_ValidateReply))); + .limits(NexusInterface::parseLimits(m_ValidateReply)); + + + emit credentialsReceived(user); m_ValidateReply->deleteLater(); m_ValidateReply = nullptr; m_ValidateState = VALIDATE_VALID; emit validateSuccessful(true); + } else { m_ApiKey.clear(); m_ValidateState = VALIDATE_NOT_VALID; diff --git a/src/nxmaccessmanager.h b/src/nxmaccessmanager.h index 1d23faf9..1bdeae40 100644 --- a/src/nxmaccessmanager.h +++ b/src/nxmaccessmanager.h @@ -20,6 +20,7 @@ along with Mod Organizer. If not, see . #ifndef NXMACCESSMANAGER_H #define NXMACCESSMANAGER_H +#include "apiuseraccount.h" #include #include #include @@ -28,8 +29,6 @@ along with Mod Organizer. If not, see . namespace MOBase { class IPluginGame; } -class APIUserAccount; - /** * @brief access manager extended to handle nxm links **/ @@ -56,6 +55,7 @@ public: QString userAgent(const QString &subModule = QString()) const; QString apiKey() const; + void clearApiKey(); void startValidationCheck(); @@ -94,7 +94,6 @@ protected: QIODevice *device); private: - QTimer m_ValidateTimeout; QNetworkReply *m_ValidateReply; QProgressDialog *m_ProgressDialog { nullptr }; @@ -103,7 +102,6 @@ private: QString m_ApiKey; - bool m_ValidateAttempted; enum { VALIDATE_NOT_CHECKED, VALIDATE_CHECKING, @@ -112,7 +110,6 @@ private: VALIDATE_REFUSED, VALIDATE_VALID } m_ValidateState = VALIDATE_NOT_CHECKED; - }; #endif // NXMACCESSMANAGER_H diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index b3e8c8a7..95e4ceb0 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -511,6 +511,9 @@ bool SettingsDialog::clearKey() m_KeyCleared = true; const auto ret = m_settings->clearNexusApiKey(); updateNexusButtons(); + + NexusInterface::instance(m_PluginContainer)->getAccessManager()->clearApiKey(); + return ret; } @@ -522,8 +525,7 @@ void SettingsDialog::testApiKey() return; } - auto* am = NexusInterface::instance(m_PluginContainer)->getAccessManager(); - am->apiCheck(key, true); + NexusInterface::instance(m_PluginContainer)->getAccessManager()->apiCheck(key, true); } void SettingsDialog::updateNexusButtons() diff --git a/src/statusbar.cpp b/src/statusbar.cpp index 4effa6a3..712eb005 100644 --- a/src/statusbar.cpp +++ b/src/statusbar.cpp @@ -48,28 +48,34 @@ void StatusBar::setNotifications(bool hasNotifications) void StatusBar::setAPI(const APIStats& stats, const APIUserAccount& user) { - m_api->setText( - QString("API: Q: %1 | D: %2 | H: %3") - .arg(stats.requestsQueued) - .arg(user.limits().remainingDailyRequests) - .arg(user.limits().remainingHourlyRequests)); - + QString text; QString textColor; QString backgroundColor; if (user.type() == APIUserAccountTypes::None) { - backgroundColor = "transparent"; - } else if (user.remainingRequests() > 300) { - textColor = "white"; - backgroundColor = "darkgreen"; - } else if (user.remainingRequests() < 150) { + text = "API: not logged in"; textColor = "white"; - backgroundColor = "darkred"; + backgroundColor = "transparent"; } else { - textColor = "black"; - backgroundColor = "rgb(226, 192, 0)"; // yellow + text = QString("API: Queued: %1 | Daily: %2 | Hourly: %3") + .arg(stats.requestsQueued) + .arg(user.limits().remainingDailyRequests) + .arg(user.limits().remainingHourlyRequests); + + if (user.remainingRequests() > 500) { + textColor = "white"; + backgroundColor = "darkgreen"; + } else if (user.remainingRequests() > 200) { + textColor = "black"; + backgroundColor = "rgb(226, 192, 0)"; // yellow + } else { + textColor = "white"; + backgroundColor = "darkred"; + } } + m_api->setText(text); + m_api->setStyleSheet(QString(R"( QLabel { -- cgit v1.3.1 From 7671725436551b7254ea89ff6985c5491fcc743d Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 14 Jun 2019 04:30:33 -0400 Subject: removed concept of custom executables, everything is modifiable added apply button to dialog added reset button that re-adds plugin executables and renames existing ones if needed moved executables files to their filter in visual studio --- src/CMakeLists.txt | 11 +-- src/editexecutablesdialog.cpp | 143 ++++++++++++++++++++------------------ src/editexecutablesdialog.h | 9 +-- src/editexecutablesdialog.ui | 37 +++++----- src/executableslist.cpp | 156 ++++++++++++++++++++++++------------------ src/executableslist.h | 24 +++++-- src/mainwindow.cpp | 3 +- src/pch.h | 1 + 8 files changed, 221 insertions(+), 163 deletions(-) (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5623a851..645a5a73 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -293,7 +293,6 @@ set(core categories shared/directoryentry directoryrefresher - executableslist installationmanager instancemanager loadmechanism @@ -309,7 +308,6 @@ set(dialogs activatemodsdialog categoriesdialog credentialsdialog - editexecutablesdialog filedialogmemory forcedloaddialog forcedloaddialogwidget @@ -333,6 +331,11 @@ set(downloads downloadmanager ) +set(executables + executableslist + editexecutablesdialog +) + set(locking ilockedwaitingforprocess lockeddialog @@ -412,8 +415,8 @@ set(widgets ) set(src_filters - application core browser dialogs downloads locking modinfo modlist plugins - previews profiles settings utilities widgets + application core browser dialogs downloads executables locking modinfo + modlist plugins previews profiles settings utilities widgets ) foreach(filter in list ${src_filters}) diff --git a/src/editexecutablesdialog.cpp b/src/editexecutablesdialog.cpp index 6c0522e4..882bb8b5 100644 --- a/src/editexecutablesdialog.cpp +++ b/src/editexecutablesdialog.cpp @@ -49,8 +49,9 @@ EditExecutablesDialog::EditExecutablesDialog(OrganizerCore& oc, QWidget* parent) m_customOverwrites.load(m_organizerCore.currentProfile(), m_executablesList); m_forcedLibraries.load(m_organizerCore.currentProfile(), m_executablesList); - fillExecutableList(); + fillList(); ui->mods->addItems(m_organizerCore.modList()->allMods()); + setDirty(false); // some widgets need to do more than just save() and have their own handler connect(ui->binary, &QLineEdit::textChanged, [&]{ save(); }); @@ -59,13 +60,7 @@ EditExecutablesDialog::EditExecutablesDialog(OrganizerCore& oc, QWidget* parent) connect(ui->steamAppID, &QLineEdit::textChanged, [&]{ save(); }); connect(ui->mods, &QComboBox::currentTextChanged, [&]{ save(); }); connect(ui->useApplicationIcon, &QCheckBox::toggled, [&]{ save(); }); - - // select the first one in the list, if any - if (ui->list->count() > 0) { - ui->list->item(0)->setSelected(true); - } else { - updateUI(nullptr, nullptr); - } + connect(ui->list->model(), &QAbstractItemModel::rowsMoved, [&]{ saveOrder(); }); } EditExecutablesDialog::~EditExecutablesDialog() = default; @@ -132,6 +127,15 @@ void EditExecutablesDialog::commitChanges() // set the new executables list m_organizerCore.setExecutablesList(newExecutables); + + setDirty(false); +} + +void EditExecutablesDialog::setDirty(bool b) +{ + if (auto* button=ui->buttons->button(QDialogButtonBox::Apply)) { + button->setEnabled(b); + } } QListWidgetItem* EditExecutablesDialog::selectedItem() @@ -162,26 +166,25 @@ Executable* EditExecutablesDialog::selectedExe() return &*itor; } -void EditExecutablesDialog::fillExecutableList() +void EditExecutablesDialog::fillList() { ui->list->clear(); for(const auto& exe : m_executablesList) { ui->list->addItem(createListItem(exe)); } + + // select the first one in the list, if any + if (ui->list->count() > 0) { + ui->list->item(0)->setSelected(true); + } else { + updateUI(nullptr, nullptr); + } } QListWidgetItem* EditExecutablesDialog::createListItem(const Executable& exe) { - QListWidgetItem *newItem = new QListWidgetItem(exe.title()); - - if (!exe.isCustom()) { - auto f = newItem->font(); - f.setItalic(true); - newItem->setFont(f); - } - - return newItem; + return new QListWidgetItem(exe.title()); } void EditExecutablesDialog::updateUI( @@ -205,14 +208,12 @@ void EditExecutablesDialog::updateUI( void EditExecutablesDialog::setButtons( const QListWidgetItem* item, const Executable* e) { - // add is always enabled + // add and remove are always enabled if (item) { - ui->remove->setEnabled(e->isCustom()); ui->up->setEnabled(canMove(item, -1)); ui->down->setEnabled(canMove(item, +1)); } else { - ui->remove->setEnabled(false); ui->up->setEnabled(false); ui->down->setEnabled(false); } @@ -243,7 +244,6 @@ void EditExecutablesDialog::clearEdits() ui->configureLibraries->setEnabled(false); ui->useApplicationIcon->setEnabled(false); ui->useApplicationIcon->setChecked(false); - ui->pluginProvidedLabel->setVisible(false); } void EditExecutablesDialog::setEdits(const Executable& e) @@ -287,19 +287,15 @@ void EditExecutablesDialog::setEdits(const Executable& e) ui->configureLibraries->setEnabled(hasForcedLibraries); } - ui->pluginProvidedLabel->setVisible(!e.isCustom()); - - // only enabled for custom executables - ui->title->setEnabled(e.isCustom()); - ui->binary->setEnabled(e.isCustom()); - ui->browseBinary->setEnabled(e.isCustom()); - ui->workingDirectory->setEnabled(e.isCustom()); - ui->browseWorkingDirectory->setEnabled(e.isCustom()); - ui->arguments->setEnabled(e.isCustom()); - ui->overwriteSteamAppID->setEnabled(e.isCustom()); - ui->useApplicationIcon->setEnabled(e.isCustom()); - // always enabled + ui->title->setEnabled(true); + ui->binary->setEnabled(true); + ui->browseBinary->setEnabled(true); + ui->workingDirectory->setEnabled(true); + ui->browseWorkingDirectory->setEnabled(true); + ui->arguments->setEnabled(true); + ui->overwriteSteamAppID->setEnabled(true); + ui->useApplicationIcon->setEnabled(true); ui->createFilesInMod->setEnabled(true); ui->forceLoadLibraries->setEnabled(true); } @@ -361,6 +357,14 @@ void EditExecutablesDialog::save() } else { e->flags(e->flags() & (~Executable::UseApplicationIcon)); } + + setDirty(true); +} + +void EditExecutablesDialog::saveOrder() +{ + m_executablesList = getExecutablesList(); + setDirty(true); } bool EditExecutablesDialog::canMove(const QListWidgetItem* item, int direction) @@ -393,6 +397,8 @@ void EditExecutablesDialog::move(QListWidgetItem* item, int direction) ui->list->takeItem(row); ui->list->insertItem(row + (direction > 0 ? 1 : -1), item); item->setSelected(true); + + setDirty(true); } void EditExecutablesDialog::on_list_itemSelectionChanged() @@ -400,22 +406,43 @@ void EditExecutablesDialog::on_list_itemSelectionChanged() updateUI(selectedItem(), selectedExe()); } +void EditExecutablesDialog::on_reset_clicked() +{ + const auto title = tr("Reset plugin executables"); + + const auto text = tr( + "This will restore all the executables provided by the game plugin. If " + "there are existing executables with the same names, they will be " + "automatically renamed and left unchanged."); + + const auto buttons = QMessageBox::Ok | QMessageBox::Cancel; + + if (QMessageBox::question(this, title, text, buttons) != QMessageBox::Ok) { + return; + } + + m_executablesList.resetFromPlugin(m_organizerCore.managedGame()); + fillList(); + + setDirty(true); +} + void EditExecutablesDialog::on_add_clicked() { - auto title = makeNonConflictingTitle(tr("New Executable")); + auto title = m_executablesList.makeNonConflictingTitle(tr("New Executable")); if (!title) { return; } - auto e = Executable() - .title(*title) - .flags(Executable::CustomExecutable); + const Executable e(*title); m_executablesList.setExecutable(e); auto* item = createListItem(e); ui->list->addItem(item); item->setSelected(true); + + setDirty(true); } void EditExecutablesDialog::on_remove_clicked() @@ -453,6 +480,8 @@ void EditExecutablesDialog::on_remove_clicked() } else { ui->list->item(currentRow)->setSelected(true); } + + setDirty(true); } void EditExecutablesDialog::on_up_clicked() @@ -563,7 +592,7 @@ void EditExecutablesDialog::on_browseBinary_clicked() // setting title if currently empty if (ui->title->text().isEmpty()) { const auto prefix = QFileInfo(binaryName).baseName(); - const auto newTitle = makeNonConflictingTitle(prefix); + const auto newTitle = m_executablesList.makeNonConflictingTitle(prefix); if (newTitle) { ui->title->setText(*newTitle); @@ -607,15 +636,16 @@ void EditExecutablesDialog::on_configureLibraries_clicked() } } -void EditExecutablesDialog::on_buttons_accepted() +void EditExecutablesDialog::on_buttons_clicked(QAbstractButton* b) { - commitChanges(); - accept(); -} - -void EditExecutablesDialog::on_buttons_rejected() -{ - reject(); + if (b == ui->buttons->button(QDialogButtonBox::Ok)) { + commitChanges(); + accept(); + } else if (b == ui->buttons->button(QDialogButtonBox::Apply)) { + commitChanges(); + } else { + reject(); + } } void EditExecutablesDialog::setJarBinary(const QString& binaryName) @@ -641,25 +671,6 @@ void EditExecutablesDialog::setJarBinary(const QString& binaryName) save(); } -std::optional EditExecutablesDialog::makeNonConflictingTitle( - const QString& prefix) -{ - QString title = prefix; - - for (int i=1; i<100; ++i) { - if (!m_executablesList.titleExists(title)) { - return title; - } - - title = prefix + QString(" (%1)").arg(i); - } - - qCritical().nospace() - << "ran out of executable titles for prefix '" << prefix << "'"; - - return {}; -} - void CustomOverwrites::load(Profile* p, const ExecutablesList& exes) { diff --git a/src/editexecutablesdialog.h b/src/editexecutablesdialog.h index 10a6166f..4a04ef42 100644 --- a/src/editexecutablesdialog.h +++ b/src/editexecutablesdialog.h @@ -110,6 +110,7 @@ public: private slots: void on_list_itemSelectionChanged(); + void on_reset_clicked(); void on_add_clicked(); void on_remove_clicked(); void on_up_clicked(); @@ -124,8 +125,7 @@ private slots: void on_browseWorkingDirectory_clicked(); void on_configureLibraries_clicked(); - void on_buttons_accepted(); - void on_buttons_rejected(); + void on_buttons_clicked(QAbstractButton* b); private: std::unique_ptr ui; @@ -140,19 +140,20 @@ private: QListWidgetItem* selectedItem(); Executable* selectedExe(); - void fillExecutableList(); + void fillList(); QListWidgetItem* createListItem(const Executable& exe); void updateUI(const QListWidgetItem* item, const Executable* e); void clearEdits(); void setEdits(const Executable& e); void setButtons(const QListWidgetItem* item, const Executable* e); void save(); + void saveOrder(); bool canMove(const QListWidgetItem* item, int direction); void move(QListWidgetItem* item, int direction); void setJarBinary(const QString& binaryName); - std::optional makeNonConflictingTitle(const QString& prefix); bool isTitleConflicting(const QString& s); void commitChanges(); + void setDirty(bool b); }; #endif // EDITEXECUTABLESDIALOG_H diff --git a/src/editexecutablesdialog.ui b/src/editexecutablesdialog.ui index 9b6c8153..a42dbeed 100644 --- a/src/editexecutablesdialog.ui +++ b/src/editexecutablesdialog.ui @@ -168,6 +168,26 @@
+ + + + Adds the executables provided by the game plugin and moves any existing executables out of the way + + + Adds the executables provided by the game plugin and moves any existing executables out of the way + + + Adds the executables provided by the game plugin and moves any existing executables out of the way + + + Reset + + + + :/MO/gui/edit_clear:/MO/gui/edit_clear + + + @@ -425,21 +445,6 @@ Right now the only case I know of where this needs to be overwritten is for the - - - - - true - - - - This executable is provided by the game plugin - - - Qt::AlignCenter - - - @@ -463,7 +468,7 @@ Right now the only case I know of where this needs to be overwritten is for the - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok diff --git a/src/executableslist.cpp b/src/executableslist.cpp index 0283a845..daf200a6 100644 --- a/src/executableslist.cpp +++ b/src/executableslist.cpp @@ -74,8 +74,6 @@ void ExecutablesList::load(const MOBase::IPluginGame* game, QSettings& settings) settings.setArrayIndex(i); Executable::Flags flags; - if (settings.value("custom", true).toBool()) - flags |= Executable::CustomExecutable; if (settings.value("toolbar", false).toBool()) flags |= Executable::ShowInToolbar; if (settings.value("ownicon", false).toBool()) @@ -92,7 +90,7 @@ void ExecutablesList::load(const MOBase::IPluginGame* game, QSettings& settings) settings.endArray(); - addFromPlugin(game); + addFromPlugin(game, IgnoreExisting); } void ExecutablesList::store(QSettings& settings) @@ -106,29 +104,33 @@ void ExecutablesList::store(QSettings& settings) settings.setArrayIndex(count++); settings.setValue("title", item.title()); - settings.setValue("custom", item.isCustom()); settings.setValue("toolbar", item.isShownOnToolbar()); settings.setValue("ownicon", item.usesOwnIcon()); - - if (item.isCustom()) { - settings.setValue("binary", item.binaryInfo().absoluteFilePath()); - settings.setValue("arguments", item.arguments()); - settings.setValue("workingDirectory", item.workingDirectory()); - settings.setValue("steamAppID", item.steamAppID()); - } + settings.setValue("binary", item.binaryInfo().absoluteFilePath()); + settings.setValue("arguments", item.arguments()); + settings.setValue("workingDirectory", item.workingDirectory()); + settings.setValue("steamAppID", item.steamAppID()); } settings.endArray(); } -void ExecutablesList::addFromPlugin(IPluginGame const *game) +void ExecutablesList::resetFromPlugin(MOBase::IPluginGame const *game) +{ + qDebug("resetting plugin executables"); + addFromPlugin(game, MoveExisting); +} + +void ExecutablesList::addFromPlugin(IPluginGame const *game, SetFlags flags) { Q_ASSERT(game != nullptr); for (const ExecutableInfo &info : game->executables()) { - if (info.isValid()) { - setExecutable({info, Executable::UseApplicationIcon}); + if (!info.isValid()) { + continue; } + + setExecutable({info, Executable::UseApplicationIcon}, flags); } const QFileInfo eppBin(QCoreApplication::applicationDirPath() + "/explorer++/Explorer++.exe"); @@ -137,12 +139,14 @@ void ExecutablesList::addFromPlugin(IPluginGame const *game) const auto args = QString("\"%1\"") .arg(QDir::toNativeSeparators(game->dataDirectory().absolutePath())); - setExecutable(Executable() + const auto exe = Executable() .title("Explore Virtual Folder") .binaryInfo(eppBin) .arguments(args) .workingDirectory(eppBin.absolutePath()) - .flags(Executable::UseApplicationIcon)); + .flags(Executable::UseApplicationIcon); + + setExecutable(exe, flags); } } @@ -188,28 +192,81 @@ bool ExecutablesList::titleExists(const QString &title) const return std::find_if(m_Executables.begin(), m_Executables.end(), test) != m_Executables.end(); } -void ExecutablesList::setExecutable(const Executable &executable) +void ExecutablesList::setExecutable(const Executable &exe) +{ + setExecutable(exe, MergeExisting); +} + +void ExecutablesList::setExecutable(const Executable &exe, SetFlags flags) { - auto itor = find(executable.title()); + auto itor = find(exe.title()); + + if (itor != end()) { + if (flags == IgnoreExisting) { + return; + } + + if (flags == MoveExisting) { + const auto newTitle = makeNonConflictingTitle(exe.title()); + if (!newTitle) { + qCritical().nospace() + << "executable '" << exe.title() << "' was in the way but could " + << "not be renamed"; + + return; + } + + qWarning().nospace() + << "executable '" << itor->title() << "' was in the way and was " + << "renamed to '" << *newTitle << "'"; + + itor->title(*newTitle); + itor = end(); + } + } if (itor == m_Executables.end()) { - m_Executables.push_back(executable); + m_Executables.push_back(exe); } else { - itor->mergeFrom(executable); + itor->mergeFrom(exe); } } void ExecutablesList::remove(const QString &title) { - for (std::vector::iterator iter = m_Executables.begin(); iter != m_Executables.end(); ++iter) { - if (iter->isCustom() && (iter->title() == title)) { - m_Executables.erase(iter); - break; + auto itor = find(title); + if (itor != m_Executables.end()) { + m_Executables.erase(itor); + } +} + +std::optional ExecutablesList::makeNonConflictingTitle( + const QString& prefix) +{ + const int max = 100; + + QString title = prefix; + + for (int i=1; i. #include "executableinfo.h" #include +#include #include #include @@ -37,14 +38,13 @@ class Executable public: enum Flag { - CustomExecutable = 0x01, ShowInToolbar = 0x02, UseApplicationIcon = 0x04 }; Q_DECLARE_FLAGS(Flags, Flag); - Executable() = default; + Executable(QString title={}); /** * @brief Executable from plugin @@ -65,7 +65,6 @@ public: Executable& workingDirectory(const QString& s); Executable& flags(Flags f); - bool isCustom() const; bool isShownOnToolbar() const; void setShownOnToolbar(bool state); bool usesOwnIcon() const; @@ -106,6 +105,8 @@ public: */ void load(const MOBase::IPluginGame* game, QSettings& settings); + void resetFromPlugin(MOBase::IPluginGame const *game); + /** * @brief writes the current list to the settings */ @@ -166,13 +167,28 @@ public: **/ void remove(const QString &title); + std::optional makeNonConflictingTitle(const QString& prefix); + private: + enum SetFlags + { + IgnoreExisting = 1, + MergeExisting, + MoveExisting + }; + std::vector m_Executables; /** * @brief add the executables preconfigured for this game **/ - void addFromPlugin(MOBase::IPluginGame const *game); + void addFromPlugin(MOBase::IPluginGame const *game, SetFlags flags); + + /** + * @brief add a new executable to the list + * @param executable + */ + void setExecutable(const Executable &exe, SetFlags flags); }; Q_DECLARE_OPERATORS_FOR_FLAGS(Executable::Flags) diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 15a6eb62..b5e9c320 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -5513,8 +5513,7 @@ void MainWindow::addAsExecutable() .title(name) .binaryInfo(binaryInfo) .arguments(arguments) - .workingDirectory(targetInfo.absolutePath()) - .flags(Executable::CustomExecutable)); + .workingDirectory(targetInfo.absolutePath())); refreshExecutablesList(); } diff --git a/src/pch.h b/src/pch.h index e86c0da6..1d8df43a 100644 --- a/src/pch.h +++ b/src/pch.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include -- cgit v1.3.1 From 59e0c4aa34fec9048d064705863c3269aacd86b5 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 15 Jun 2019 17:58:23 -0400 Subject: moved filerenamer to its own .cpp/.h pair --- src/CMakeLists.txt | 3 + src/filerenamer.cpp | 189 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/filerenamer.h | 140 +++++++++++++++++++++++++++++++++++++ src/modinfodialog.cpp | 187 ------------------------------------------------- src/modinfodialog.h | 135 +----------------------------------- 5 files changed, 333 insertions(+), 321 deletions(-) create mode 100644 src/filerenamer.cpp create mode 100644 src/filerenamer.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 645a5a73..f654f9b4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -118,6 +118,7 @@ SET(organizer_SRCS filterwidget.cpp statusbar.cpp apiuseraccount.cpp + filerenamer.cpp shared/windows_error.cpp shared/error_report.cpp @@ -217,6 +218,7 @@ SET(organizer_HDRS filterwidget.h statusbar.h apiuseraccount.h + filerenamer.h shared/windows_error.h shared/error_report.h @@ -402,6 +404,7 @@ set(utilities set(widgets descriptionpage genericicondelegate + filerenamer filterwidget icondelegate lcdnumber diff --git a/src/filerenamer.cpp b/src/filerenamer.cpp new file mode 100644 index 00000000..c5c6782b --- /dev/null +++ b/src/filerenamer.cpp @@ -0,0 +1,189 @@ +#include "filerenamer.h" +#include +#include + +FileRenamer::FileRenamer(QWidget* parent, QFlags flags) + : m_parent(parent), m_flags(flags) +{ + // sanity check for flags + if ((m_flags & (HIDE|UNHIDE)) == 0) { + qCritical("renameFile() missing hide flag"); + // doesn't really matter, it's just for text + m_flags = HIDE; + } +} + +FileRenamer::RenameResults FileRenamer::rename(const QString& oldName, const QString& newName) +{ + qDebug().nospace() << "renaming " << oldName << " to " << newName; + + if (QFileInfo(newName).exists()) { + qDebug().nospace() << newName << " already exists"; + + // target file already exists, confirm replacement + auto answer = confirmReplace(newName); + + switch (answer) { + case DECISION_SKIP: { + // user wants to skip this file + qDebug().nospace() << "skipping " << oldName; + return RESULT_SKIP; + } + + case DECISION_REPLACE: { + qDebug().nospace() << "removing " << newName; + // user wants to replace the file, so remove it + if (!QFile(newName).remove()) { + qWarning().nospace() << "failed to remove " << newName; + // removal failed, warn the user and allow canceling + if (!removeFailed(newName)) { + qDebug().nospace() << "canceling " << oldName; + // user wants to cancel + return RESULT_CANCEL; + } + + // ignore this file and continue on + qDebug().nospace() << "skipping " << oldName; + return RESULT_SKIP; + } + + break; + } + + case DECISION_CANCEL: // fall-through + default: { + // user wants to stop + qDebug().nospace() << "canceling"; + return RESULT_CANCEL; + } + } + } + + // target either didn't exist or was removed correctly + + if (!QFile::rename(oldName, newName)) { + qWarning().nospace() << "failed to rename " << oldName << " to " << newName; + + // renaming failed, warn the user and allow canceling + if (!renameFailed(oldName, newName)) { + // user wants to cancel + qDebug().nospace() << "canceling"; + return RESULT_CANCEL; + } + + // ignore this file and continue on + qDebug().nospace() << "skipping " << oldName; + return RESULT_SKIP; + } + + // everything worked + qDebug().nospace() << "successfully renamed " << oldName << " to " << newName; + return RESULT_OK; +} + +FileRenamer::RenameDecision FileRenamer::confirmReplace(const QString& newName) +{ + if (m_flags & REPLACE_ALL) { + // user wants to silently replace all + qDebug().nospace() << "user has selected replace all"; + return DECISION_REPLACE; + } + else if (m_flags & REPLACE_NONE) { + // user wants to silently skip all + qDebug().nospace() << "user has selected replace none"; + return DECISION_SKIP; + } + + QString text; + + if (m_flags & HIDE) { + text = QObject::tr("The hidden file \"%1\" already exists. Replace it?").arg(newName); + } + else if (m_flags & UNHIDE) { + text = QObject::tr("The visible file \"%1\" already exists. Replace it?").arg(newName); + } + + auto buttons = QMessageBox::Yes | QMessageBox::No; + if (m_flags & MULTIPLE) { + // only show these buttons when there are multiple files to replace + buttons |= QMessageBox::YesToAll | QMessageBox::NoToAll | QMessageBox::Cancel; + } + + const auto answer = QMessageBox::question( + m_parent, QObject::tr("Replace file?"), text, buttons); + + switch (answer) { + case QMessageBox::Yes: + qDebug().nospace() << "user wants to replace"; + return DECISION_REPLACE; + + case QMessageBox::No: + qDebug().nospace() << "user wants to skip"; + return DECISION_SKIP; + + case QMessageBox::YesToAll: + qDebug().nospace() << "user wants to replace all"; + // remember the answer + m_flags |= REPLACE_ALL; + return DECISION_REPLACE; + + case QMessageBox::NoToAll: + qDebug().nospace() << "user wants to replace none"; + // remember the answer + m_flags |= REPLACE_NONE; + return DECISION_SKIP; + + case QMessageBox::Cancel: // fall-through + default: + qDebug().nospace() << "user wants to cancel"; + return DECISION_CANCEL; + } +} + +bool FileRenamer::removeFailed(const QString& name) +{ + QMessageBox::StandardButtons buttons = QMessageBox::Ok; + if (m_flags & MULTIPLE) { + // only show cancel for multiple files + buttons |= QMessageBox::Cancel; + } + + const auto answer = QMessageBox::critical( + m_parent, QObject::tr("File operation failed"), + QObject::tr("Failed to remove \"%1\". Maybe you lack the required file permissions?").arg(name), + buttons); + + if (answer == QMessageBox::Cancel) { + // user wants to stop + qDebug().nospace() << "user wants to cancel"; + return false; + } + + // skip this one and continue + qDebug().nospace() << "user wants to skip"; + return true; +} + +bool FileRenamer::renameFailed(const QString& oldName, const QString& newName) +{ + QMessageBox::StandardButtons buttons = QMessageBox::Ok; + if (m_flags & MULTIPLE) { + // only show cancel for multiple files + buttons |= QMessageBox::Cancel; + } + + const auto answer = QMessageBox::critical( + m_parent, QObject::tr("File operation failed"), + QObject::tr("failed to rename %1 to %2").arg(oldName).arg(QDir::toNativeSeparators(newName)), + buttons); + + if (answer == QMessageBox::Cancel) { + // user wants to stop + qDebug().nospace() << "user wants to cancel"; + return false; + } + + // skip this one and continue + qDebug().nospace() << "user wants to skip"; + return true; +} diff --git a/src/filerenamer.h b/src/filerenamer.h new file mode 100644 index 00000000..cd57244c --- /dev/null +++ b/src/filerenamer.h @@ -0,0 +1,140 @@ +#ifndef FILERENAMER_H +#define FILERENAMER_H + +#include + +/** +* Renames individual files and handles dialog boxes to confirm replacements and +* failures with the user +**/ +class FileRenamer +{ +public: + /** + * controls appearance and replacement behaviour; if RENAME_REPLACE_ALL and + * RENAME_REPLACE_NONE are not provided, the user will have the option to + * choose on the first replacement + **/ + enum RenameFlags + { + /** + * this renamer will be used on multiple files, so display additional + * buttons to replace all and for canceling + **/ + MULTIPLE = 0x01, + + /** + * customizes some of the text shown on dialog to mention that files are + * being hidden + **/ + HIDE = 0x02, + + /** + * customizes some of the text shown on dialog to mention that files are + * being unhidden + **/ + UNHIDE = 0x04, + + /** + * silently replaces all existing files + **/ + REPLACE_ALL = 0x08, + + /** + * silently skips all existing files + **/ + REPLACE_NONE = 0x10, + }; + + + /** result of a single rename + * + **/ + enum RenameResults + { + /** + * the user skipped this file + */ + RESULT_SKIP, + + /** + * the file was successfully renamed + */ + RESULT_OK, + + /** + * the user wants to cancel + */ + RESULT_CANCEL + }; + + + /** + * @param parent Parent widget for dialog boxes + **/ + FileRenamer(QWidget* parent, QFlags flags); + + /** + * renames the given file + * @param oldName current filename + * @param newName new filename + * @return whether the file was renamed, skipped or the user wants to cancel + **/ + RenameResults rename(const QString& oldName, const QString& newName); + +private: + /** + *user's decision when replacing + **/ + enum RenameDecision + { + /** + * replace the file + **/ + DECISION_REPLACE, + + /** + * skip the file + **/ + DECISION_SKIP, + + /** + * cancel the whole thing + **/ + DECISION_CANCEL + }; + + /** + * parent widget for dialog boxes + **/ + QWidget* m_parent; + + /** + * flags + **/ + QFlags m_flags; + + /** + * asks the user to replace an existing file, may return early if the user + * has already selected to replace all/none + * @return whether to replace, skip or cancel + **/ + RenameDecision confirmReplace(const QString& newName); + + /** + * removal of a file failed, ask the user to continue or cancel + * @param name The name of the file that failed to be removed + * @return true to continue, false to stop + **/ + bool removeFailed(const QString& name); + + /** + * renaming a file failed, ask the user to continue or cancel + * @param oldName current filename + * @param newName new filename + * @return true to continue, false to stop + **/ + bool renameFailed(const QString& oldName, const QString& newName); +}; + +#endif // FILERENAMER_H diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 44934544..f553a7be 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -78,193 +78,6 @@ static bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS const int max_scan_for_context_menu = 50; -FileRenamer::FileRenamer(QWidget* parent, QFlags flags) - : m_parent(parent), m_flags(flags) -{ - // sanity check for flags - if ((m_flags & (HIDE|UNHIDE)) == 0) { - qCritical("renameFile() missing hide flag"); - // doesn't really matter, it's just for text - m_flags = HIDE; - } -} - -FileRenamer::RenameResults FileRenamer::rename(const QString& oldName, const QString& newName) -{ - qDebug().nospace() << "renaming " << oldName << " to " << newName; - - if (QFileInfo(newName).exists()) { - qDebug().nospace() << newName << " already exists"; - - // target file already exists, confirm replacement - auto answer = confirmReplace(newName); - - switch (answer) { - case DECISION_SKIP: { - // user wants to skip this file - qDebug().nospace() << "skipping " << oldName; - return RESULT_SKIP; - } - - case DECISION_REPLACE: { - qDebug().nospace() << "removing " << newName; - // user wants to replace the file, so remove it - if (!QFile(newName).remove()) { - qWarning().nospace() << "failed to remove " << newName; - // removal failed, warn the user and allow canceling - if (!removeFailed(newName)) { - qDebug().nospace() << "canceling " << oldName; - // user wants to cancel - return RESULT_CANCEL; - } - - // ignore this file and continue on - qDebug().nospace() << "skipping " << oldName; - return RESULT_SKIP; - } - - break; - } - - case DECISION_CANCEL: // fall-through - default: { - // user wants to stop - qDebug().nospace() << "canceling"; - return RESULT_CANCEL; - } - } - } - - // target either didn't exist or was removed correctly - - if (!QFile::rename(oldName, newName)) { - qWarning().nospace() << "failed to rename " << oldName << " to " << newName; - - // renaming failed, warn the user and allow canceling - if (!renameFailed(oldName, newName)) { - // user wants to cancel - qDebug().nospace() << "canceling"; - return RESULT_CANCEL; - } - - // ignore this file and continue on - qDebug().nospace() << "skipping " << oldName; - return RESULT_SKIP; - } - - // everything worked - qDebug().nospace() << "successfully renamed " << oldName << " to " << newName; - return RESULT_OK; -} - -FileRenamer::RenameDecision FileRenamer::confirmReplace(const QString& newName) -{ - if (m_flags & REPLACE_ALL) { - // user wants to silently replace all - qDebug().nospace() << "user has selected replace all"; - return DECISION_REPLACE; - } - else if (m_flags & REPLACE_NONE) { - // user wants to silently skip all - qDebug().nospace() << "user has selected replace none"; - return DECISION_SKIP; - } - - QString text; - - if (m_flags & HIDE) { - text = QObject::tr("The hidden file \"%1\" already exists. Replace it?").arg(newName); - } - else if (m_flags & UNHIDE) { - text = QObject::tr("The visible file \"%1\" already exists. Replace it?").arg(newName); - } - - auto buttons = QMessageBox::Yes | QMessageBox::No; - if (m_flags & MULTIPLE) { - // only show these buttons when there are multiple files to replace - buttons |= QMessageBox::YesToAll | QMessageBox::NoToAll | QMessageBox::Cancel; - } - - const auto answer = QMessageBox::question( - m_parent, QObject::tr("Replace file?"), text, buttons); - - switch (answer) { - case QMessageBox::Yes: - qDebug().nospace() << "user wants to replace"; - return DECISION_REPLACE; - - case QMessageBox::No: - qDebug().nospace() << "user wants to skip"; - return DECISION_SKIP; - - case QMessageBox::YesToAll: - qDebug().nospace() << "user wants to replace all"; - // remember the answer - m_flags |= REPLACE_ALL; - return DECISION_REPLACE; - - case QMessageBox::NoToAll: - qDebug().nospace() << "user wants to replace none"; - // remember the answer - m_flags |= REPLACE_NONE; - return DECISION_SKIP; - - case QMessageBox::Cancel: // fall-through - default: - qDebug().nospace() << "user wants to cancel"; - return DECISION_CANCEL; - } -} - -bool FileRenamer::removeFailed(const QString& name) -{ - QMessageBox::StandardButtons buttons = QMessageBox::Ok; - if (m_flags & MULTIPLE) { - // only show cancel for multiple files - buttons |= QMessageBox::Cancel; - } - - const auto answer = QMessageBox::critical( - m_parent, QObject::tr("File operation failed"), - QObject::tr("Failed to remove \"%1\". Maybe you lack the required file permissions?").arg(name), - buttons); - - if (answer == QMessageBox::Cancel) { - // user wants to stop - qDebug().nospace() << "user wants to cancel"; - return false; - } - - // skip this one and continue - qDebug().nospace() << "user wants to skip"; - return true; -} - -bool FileRenamer::renameFailed(const QString& oldName, const QString& newName) -{ - QMessageBox::StandardButtons buttons = QMessageBox::Ok; - if (m_flags & MULTIPLE) { - // only show cancel for multiple files - buttons |= QMessageBox::Cancel; - } - - const auto answer = QMessageBox::critical( - m_parent, QObject::tr("File operation failed"), - QObject::tr("failed to rename %1 to %2").arg(oldName).arg(QDir::toNativeSeparators(newName)), - buttons); - - if (answer == QMessageBox::Cancel) { - // user wants to stop - qDebug().nospace() << "user wants to cancel"; - return false; - } - - // skip this one and continue - qDebug().nospace() << "user wants to skip"; - return true; -} - - ExpanderWidget::ExpanderWidget() : m_button(nullptr), m_content(nullptr), opened_(false) { diff --git a/src/modinfodialog.h b/src/modinfodialog.h index c4f65d8a..b3ce3d07 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -26,6 +26,7 @@ along with Mod Organizer. If not, see . #include "plugincontainer.h" #include "organizercore.h" #include "filterwidget.h" +#include "filerenamer.h" #include #include @@ -49,140 +50,6 @@ class QFileSystemModel; class QTreeView; class CategoryFactory; -/** -* Renames individual files and handles dialog boxes to confirm replacements and -* failures with the user -**/ -class FileRenamer -{ -public: - /** - * controls appearance and replacement behaviour; if RENAME_REPLACE_ALL and - * RENAME_REPLACE_NONE are not provided, the user will have the option to - * choose on the first replacement - **/ - enum RenameFlags - { - /** - * this renamer will be used on multiple files, so display additional - * buttons to replace all and for canceling - **/ - MULTIPLE = 0x01, - - /** - * customizes some of the text shown on dialog to mention that files are - * being hidden - **/ - HIDE = 0x02, - - /** - * customizes some of the text shown on dialog to mention that files are - * being unhidden - **/ - UNHIDE = 0x04, - - /** - * silently replaces all existing files - **/ - REPLACE_ALL = 0x08, - - /** - * silently skips all existing files - **/ - REPLACE_NONE = 0x10, - }; - - - /** result of a single rename - * - **/ - enum RenameResults - { - /** - * the user skipped this file - */ - RESULT_SKIP, - - /** - * the file was successfully renamed - */ - RESULT_OK, - - /** - * the user wants to cancel - */ - RESULT_CANCEL - }; - - - /** - * @param parent Parent widget for dialog boxes - **/ - FileRenamer(QWidget* parent, QFlags flags); - - /** - * renames the given file - * @param oldName current filename - * @param newName new filename - * @return whether the file was renamed, skipped or the user wants to cancel - **/ - RenameResults rename(const QString& oldName, const QString& newName); - -private: - /** - *user's decision when replacing - **/ - enum RenameDecision - { - /** - * replace the file - **/ - DECISION_REPLACE, - - /** - * skip the file - **/ - DECISION_SKIP, - - /** - * cancel the whole thing - **/ - DECISION_CANCEL - }; - - /** - * parent widget for dialog boxes - **/ - QWidget* m_parent; - - /** - * flags - **/ - QFlags m_flags; - - /** - * asks the user to replace an existing file, may return early if the user - * has already selected to replace all/none - * @return whether to replace, skip or cancel - **/ - RenameDecision confirmReplace(const QString& newName); - - /** - * removal of a file failed, ask the user to continue or cancel - * @param name The name of the file that failed to be removed - * @return true to continue, false to stop - **/ - bool removeFailed(const QString& name); - - /** - * renaming a file failed, ask the user to continue or cancel - * @param oldName current filename - * @param newName new filename - * @return true to continue, false to stop - **/ - bool renameFailed(const QString& oldName, const QString& newName); -}; - /* Takes a QToolButton and a widget and creates an expandable widget. **/ -- cgit v1.3.1 From 6bd5bed29f3ebcc1155e615d7daa1c9cd1724efb Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 15 Jun 2019 18:54:19 -0400 Subject: added initial toolbar, splitter moved most of the text editor stuff to a new TextEditor class --- src/CMakeLists.txt | 3 ++ src/modinfodialog.cpp | 77 ++++++++++++++++-------------- src/modinfodialog.h | 10 +++- src/modinfodialog.ui | 130 +++++++++++++++++++++++++++++++++----------------- src/texteditor.cpp | 76 +++++++++++++++++++++++++++++ src/texteditor.h | 36 ++++++++++++++ 6 files changed, 249 insertions(+), 83 deletions(-) create mode 100644 src/texteditor.cpp create mode 100644 src/texteditor.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f654f9b4..f75ab1e0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -119,6 +119,7 @@ SET(organizer_SRCS statusbar.cpp apiuseraccount.cpp filerenamer.cpp + texteditor.cpp shared/windows_error.cpp shared/error_report.cpp @@ -219,6 +220,7 @@ SET(organizer_HDRS statusbar.h apiuseraccount.h filerenamer.h + texteditor.h shared/windows_error.h shared/error_report.h @@ -414,6 +416,7 @@ set(widgets modidlineedit noeditdelegate qtgroupingproxy + texteditor viewmarkingscrollbar ) diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index f553a7be..db5b5fd0 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -36,6 +36,7 @@ along with Mod Organizer. If not, see . #include "pluginlistsortproxy.h" #include "previewgenerator.h" #include "previewdialog.h" +#include "texteditor.h" #include #include @@ -358,6 +359,17 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo } } + m_textFileEditor.reset(new TextEditor(ui->textFileView)); + + connect( + m_textFileEditor.get(), &TextEditor::changed, + [&](bool b){ onTextFileChanged(b); }); + + ui->tabTextSplitter->setSizes({200, 1}); + ui->tabTextSplitter->setStretchFactor(0, 0); + ui->tabTextSplitter->setStretchFactor(1, 1); + setTextFileWordWrap(true); + // refresh everything but the conflict lists, which are done in exec() because // they depend on restoring the state to some widgets; this refresh has to be // done here because some of the checks below depend on the ui to decide which @@ -1030,9 +1042,12 @@ void ModInfoDialog::thumbnailClicked(const QString &fileName) bool ModInfoDialog::allowNavigateFromTXT() { - if (ui->saveTXTButton->isEnabled()) { - int res = QMessageBox::question(this, tr("Save changes?"), tr("Save changes to \"%1\"?").arg(ui->textFileView->property("currentFile").toString()), - QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); + if (m_textFileEditor->dirty()) { + const int res = QMessageBox::question( + this, tr("Save changes?"), + tr("Save changes to \"%1\"?").arg(m_textFileEditor->filename()), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); + if (res == QMessageBox::Cancel) { return false; } else if (res == QMessageBox::Yes) { @@ -1042,7 +1057,6 @@ bool ModInfoDialog::allowNavigateFromTXT() return true; } - bool ModInfoDialog::allowNavigateFromINI() { if (ui->saveButton->isEnabled()) { @@ -1057,14 +1071,14 @@ bool ModInfoDialog::allowNavigateFromINI() return true; } - -void ModInfoDialog::on_textFileList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) +void ModInfoDialog::on_textFileList_currentItemChanged( + QListWidgetItem *current, QListWidgetItem *previous) { - QString fullPath = m_RootPath + "/" + current->text(); + const QString fullPath = m_RootPath + "/" + current->text(); - QVariant currentFile = ui->textFileView->property("currentFile"); - if (currentFile.isValid() && (currentFile.toString() == fullPath)) { - // the new file is the same as the currently displayed file. May be the result of a cancelation + if (fullPath == m_textFileEditor->filename()) { + // the new file is the same as the currently displayed file. May be the + // result of a cancellation return; } @@ -1075,17 +1089,12 @@ void ModInfoDialog::on_textFileList_currentItemChanged(QListWidgetItem *current, } } - void ModInfoDialog::openTextFile(const QString &fileName) { - QString encoding; - ui->textFileView->setText(MOBase::readFileText(fileName, &encoding)); - ui->textFileView->setProperty("currentFile", fileName); - ui->textFileView->setProperty("encoding", encoding); - ui->saveTXTButton->setEnabled(false); + m_textFileEditor->load(fileName); + ui->textFileSave->setEnabled(false); } - void ModInfoDialog::openIniFile(const QString &fileName) { QFile iniFile(fileName); @@ -1155,35 +1164,31 @@ void ModInfoDialog::on_iniTweaksList_currentItemChanged(QListWidgetItem *current } - void ModInfoDialog::on_saveButton_clicked() { saveCurrentIniFile(); } - -void ModInfoDialog::on_saveTXTButton_clicked() +void ModInfoDialog::on_textFileSave_clicked() { saveCurrentTextFile(); } +void ModInfoDialog::on_textFileWordWrap_clicked() +{ + setTextFileWordWrap(!m_textFileEditor->wordWrap()); +} + +void ModInfoDialog::setTextFileWordWrap(bool b) +{ + m_textFileEditor->wordWrap(b); + ui->textFileWordWrap->setChecked(b); +} void ModInfoDialog::saveCurrentTextFile() { - QVariant fileNameVar = ui->textFileView->property("currentFile"); - QVariant encodingVar = ui->textFileView->property("encoding"); - if (fileNameVar.isValid() && encodingVar.isValid()) { - QString fileName = fileNameVar.toString(); - QFile txtFile(fileName); - txtFile.open(QIODevice::WriteOnly); - txtFile.resize(0); - QTextCodec *codec = QTextCodec::codecForName(encodingVar.toString().toUtf8()); - QString data = ui->textFileView->toPlainText().replace("\n", "\r\n"); - txtFile.write(codec->fromUnicode(data)); - } else { - reportError("no file selected"); - } - ui->saveTXTButton->setEnabled(false); + m_textFileEditor->save(); + ui->textFileSave->setEnabled(false); } @@ -1214,9 +1219,9 @@ void ModInfoDialog::on_iniFileView_textChanged() } -void ModInfoDialog::on_textFileView_textChanged() +void ModInfoDialog::onTextFileChanged(bool b) { - ui->saveTXTButton->setEnabled(true); + ui->textFileSave->setEnabled(b); } diff --git a/src/modinfodialog.h b/src/modinfodialog.h index b3ce3d07..85226f45 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -35,6 +35,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include #include @@ -49,6 +50,7 @@ namespace Ui { class QFileSystemModel; class QTreeView; class CategoryFactory; +class TextEditor; /* Takes a QToolButton and a widget and creates an expandable widget. @@ -218,14 +220,15 @@ private slots: void on_saveButton_clicked(); void on_activateESP_clicked(); void on_deactivateESP_clicked(); - void on_saveTXTButton_clicked(); + void on_textFileSave_clicked(); + void on_textFileWordWrap_clicked(); void on_visitNexusLabel_linkActivated(const QString &link); void on_modIDEdit_editingFinished(); void on_sourceGameEdit_currentIndexChanged(int); void on_versionEdit_editingFinished(); void on_customUrlLineEdit_editingFinished(); void on_iniFileView_textChanged(); - void on_textFileView_textChanged(); + void onTextFileChanged(bool b); void on_tabWidget_currentChanged(int index); void on_primaryCategoryBox_currentIndexChanged(int index); void on_categoriesTree_itemChanged(QTreeWidgetItem *item, int column); @@ -304,6 +307,7 @@ private: ExpanderWidget m_overwriteExpander, m_overwrittenExpander, m_nonconflictExpander; FilterWidget m_advancedConflictFilter; + std::unique_ptr m_textFileEditor; void refreshConflictLists(bool refreshGeneral, bool refreshAdvanced); @@ -347,6 +351,8 @@ private: std::vector createGotoActions( const QList& selection); + + void setTextFileWordWrap(bool b); }; #endif // MODINFODIALOG_H diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index eed4e31f..4a29f6e4 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -27,49 +27,89 @@ - Textfiles + Text Files - + - - - - 192 - 16777215 - - - - A list of text-files in the mod directory. + + + Qt::Horizontal - - A list of text-files in the mod directory like readmes. + + false + + + A list of text-files in the mod directory. + + + A list of text-files in the mod directory like readmes. + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Save + + + + + + + Word wrap + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - false - - - Save - - - - - @@ -493,12 +533,12 @@ text-align: left; 2 - - 365 - 200 + + 365 + File @@ -569,12 +609,12 @@ text-align: left; 2 - - 365 - 200 + + 365 + File @@ -1011,7 +1051,7 @@ p, li { white-space: pre-wrap; } - + 0 @@ -1024,7 +1064,7 @@ p, li { white-space: pre-wrap; } 200 - + about:blank diff --git a/src/texteditor.cpp b/src/texteditor.cpp new file mode 100644 index 00000000..eef74246 --- /dev/null +++ b/src/texteditor.cpp @@ -0,0 +1,76 @@ +#include "texteditor.h" +#include "utility.h" + +TextEditor::TextEditor(QPlainTextEdit* edit) + : m_edit(edit), m_dirty(false) +{ + m_edit->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + wordWrap(true); + + QObject::connect( + m_edit->document(), &QTextDocument::modificationChanged, + [&](bool b){ onChanged(b); }); +} + +bool TextEditor::load(const QString& filename) +{ + m_filename = filename; + m_edit->setPlainText(MOBase::readFileText(filename, &m_encoding)); + m_edit->document()->setModified(false); + + return true; +} + +bool TextEditor::save() +{ + if (m_filename.isEmpty() || m_encoding.isEmpty()) { + return false; + } + + QFile file(m_filename); + file.open(QIODevice::WriteOnly); + file.resize(0); + + QTextCodec* codec = QTextCodec::codecForName(m_encoding.toUtf8()); + QString data = m_edit->toPlainText().replace("\n", "\r\n"); + + file.write(codec->fromUnicode(data)); + m_edit->document()->setModified(false); + + return true; +} + +const QString& TextEditor::filename() const +{ + return m_filename; +} + +void TextEditor::wordWrap(bool b) +{ + if (b) { + m_edit->setLineWrapMode(QPlainTextEdit::WidgetWidth); + } else { + m_edit->setLineWrapMode(QPlainTextEdit::NoWrap); + } +} + +bool TextEditor::wordWrap() const +{ + return (m_edit->lineWrapMode() == QPlainTextEdit::WidgetWidth); +} + +void TextEditor::dirty(bool b) +{ + m_dirty = b; +} + +bool TextEditor::dirty() const +{ + return m_dirty; +} + +void TextEditor::onChanged(bool b) +{ + dirty(b); + emit changed(b); +} diff --git a/src/texteditor.h b/src/texteditor.h new file mode 100644 index 00000000..25a15ad7 --- /dev/null +++ b/src/texteditor.h @@ -0,0 +1,36 @@ +#ifndef MO_TEXTEDITOR_H +#define MO_TEXTEDITOR_H + +#include + +class TextEditor : public QObject +{ + Q_OBJECT + +public: + TextEditor(QPlainTextEdit* edit); + + bool load(const QString& filename); + bool save(); + + const QString& filename() const; + + void wordWrap(bool b); + bool wordWrap() const; + + bool dirty() const; + +signals: + void changed(bool b); + +private: + QPlainTextEdit* m_edit; + QString m_filename; + QString m_encoding; + bool m_dirty; + + void onChanged(bool b); + void dirty(bool b); +}; + +#endif // MO_TEXTEDITOR_H -- cgit v1.3.1 From ab7f42dd97e5e6f6076877469a774213bfc3bf76 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 17 Jun 2019 03:12:49 -0400 Subject: split ExpanderWidget into its own set of files --- src/CMakeLists.txt | 3 +++ src/expanderwidget.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/expanderwidget.h | 46 ++++++++++++++++++++++++++++++++++++++++++ src/modinfodialog.cpp | 54 -------------------------------------------------- src/modinfodialog.h | 42 +-------------------------------------- 5 files changed, 104 insertions(+), 95 deletions(-) create mode 100644 src/expanderwidget.cpp create mode 100644 src/expanderwidget.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f75ab1e0..d5aa8247 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -120,6 +120,7 @@ SET(organizer_SRCS apiuseraccount.cpp filerenamer.cpp texteditor.cpp + expanderwidget.cpp shared/windows_error.cpp shared/error_report.cpp @@ -221,6 +222,7 @@ SET(organizer_HDRS apiuseraccount.h filerenamer.h texteditor.h + expanderwidget.h shared/windows_error.h shared/error_report.h @@ -405,6 +407,7 @@ set(utilities set(widgets descriptionpage + expanderwidget genericicondelegate filerenamer filterwidget diff --git a/src/expanderwidget.cpp b/src/expanderwidget.cpp new file mode 100644 index 00000000..2f47da5b --- /dev/null +++ b/src/expanderwidget.cpp @@ -0,0 +1,54 @@ +#include "expanderwidget.h" + +ExpanderWidget::ExpanderWidget() + : m_button(nullptr), m_content(nullptr), opened_(false) +{ +} + +ExpanderWidget::ExpanderWidget(QToolButton* button, QWidget* content) + : ExpanderWidget() +{ + set(button, content); +} + +void ExpanderWidget::set(QToolButton* button, QWidget* content, bool o) +{ + m_button = button; + m_content = content; + + m_button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + QObject::connect(m_button, &QToolButton::clicked, [&]{ toggle(); }); + + toggle(o); +} + +void ExpanderWidget::toggle() +{ + if (opened()) { + toggle(false); + } + else { + toggle(true); + } +} + +void ExpanderWidget::toggle(bool b) +{ + if (b) { + m_button->setArrowType(Qt::DownArrow); + m_content->show(); + } else { + m_button->setArrowType(Qt::RightArrow); + m_content->hide(); + } + + // the state has to be remembered instead of using m_content's visibility + // because saving the state in saveConflictExpandersState() happens after the + // dialog is closed, which marks all the widgets hidden + opened_ = b; +} + +bool ExpanderWidget::opened() const +{ + return opened_; +} diff --git a/src/expanderwidget.h b/src/expanderwidget.h new file mode 100644 index 00000000..da3eb9d6 --- /dev/null +++ b/src/expanderwidget.h @@ -0,0 +1,46 @@ +#ifndef EXPANDERWIDGET_H +#define EXPANDERWIDGET_H + +#include + +/* Takes a QToolButton and a widget and creates an expandable widget. +**/ +class ExpanderWidget +{ +public: + /** empty expander, use set() + **/ + ExpanderWidget(); + + /** see set() + **/ + ExpanderWidget(QToolButton* button, QWidget* content); + + /** @brief sets the button and content widgets to use + * the button will be given an arrow icon, clicking it will toggle the + * visibility of the given widget + * @param button the button that toggles the content + * @param content the widget that will be shown or hidden + * @param opened initial state, defaults to closed + **/ + void set(QToolButton* button, QWidget* content, bool opened=false); + + /** either opens or closes the expander depending on the current state + **/ + void toggle(); + + /** sets the current state of the expander + **/ + void toggle(bool b); + + /** returns whether the expander is currently opened + **/ + bool opened() const; + +private: + QToolButton* m_button; + QWidget* m_content; + bool opened_; +}; + +#endif // EXPANDERWIDGET_H diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 743f76b0..065058fc 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -79,60 +79,6 @@ static bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS const int max_scan_for_context_menu = 50; -ExpanderWidget::ExpanderWidget() - : m_button(nullptr), m_content(nullptr), opened_(false) -{ -} - -ExpanderWidget::ExpanderWidget(QToolButton* button, QWidget* content) - : ExpanderWidget() -{ - set(button, content); -} - -void ExpanderWidget::set(QToolButton* button, QWidget* content, bool o) -{ - m_button = button; - m_content = content; - - m_button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - QObject::connect(m_button, &QToolButton::clicked, [&]{ toggle(); }); - - toggle(o); -} - -void ExpanderWidget::toggle() -{ - if (opened()) { - toggle(false); - } - else { - toggle(true); - } -} - -void ExpanderWidget::toggle(bool b) -{ - if (b) { - m_button->setArrowType(Qt::DownArrow); - m_content->show(); - } else { - m_button->setArrowType(Qt::RightArrow); - m_content->hide(); - } - - // the state has to be remembered instead of using m_content's visibility - // because saving the state in saveConflictExpandersState() happens after the - // dialog is closed, which marks all the widgets hidden - opened_ = b; -} - -bool ExpanderWidget::opened() const -{ - return opened_; -} - - class ElideLeftDelegate : public QStyledItemDelegate { public: diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 09924671..df450ac0 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -27,6 +27,7 @@ along with Mod Organizer. If not, see . #include "organizercore.h" #include "filterwidget.h" #include "filerenamer.h" +#include "expanderwidget.h" #include #include @@ -53,47 +54,6 @@ class CategoryFactory; class TextEditor; -/* Takes a QToolButton and a widget and creates an expandable widget. - **/ -class ExpanderWidget -{ -public: - /** empty expander, use set() - **/ - ExpanderWidget(); - - /** see set() - **/ - ExpanderWidget(QToolButton* button, QWidget* content); - - /** @brief sets the button and content widgets to use - * the button will be given an arrow icon, clicking it will toggle the - * visibility of the given widget - * @param button the button that toggles the content - * @param content the widget that will be shown or hidden - * @param opened initial state, defaults to closed - **/ - void set(QToolButton* button, QWidget* content, bool opened=false); - - /** either opens or closes the expander depending on the current state - **/ - void toggle(); - - /** sets the current state of the expander - **/ - void toggle(bool b); - - /** returns whether the expander is currently opened - **/ - bool opened() const; - -private: - QToolButton* m_button; - QWidget* m_content; - bool opened_; -}; - - /** * this is a larger dialog used to visualise information abount the mod. * @todo this would probably a good place for a plugin-system -- cgit v1.3.1 From 6e2ae2444ca0bdea964cf0551edd338e6dafa2bc Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 17 Jun 2019 04:00:00 -0400 Subject: split text file tab into its own class --- src/CMakeLists.txt | 3 ++ src/modinfodialog.cpp | 100 +++++++++++++++++------------------------ src/modinfodialog.h | 29 +++++++++--- src/modinfodialogtextfiles.cpp | 91 +++++++++++++++++++++++++++++++++++++ src/modinfodialogtextfiles.h | 21 +++++++++ 5 files changed, 180 insertions(+), 64 deletions(-) create mode 100644 src/modinfodialogtextfiles.cpp create mode 100644 src/modinfodialogtextfiles.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d5aa8247..7e62f3e9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ SET(organizer_SRCS modlist.cpp modidlineedit.cpp modinfodialog.cpp + modinfodialogtextfiles.cpp modinfo.cpp modinfobackup.cpp modinfoforeign.cpp @@ -154,6 +155,7 @@ SET(organizer_HDRS modlist.h modidlineedit.h modinfodialog.h + modinfodialogtextfiles.h modinfo.h modinfobackup.h modinfoforeign.h @@ -352,6 +354,7 @@ set(modinfo modinfo modinfobackup modinfodialog + modinfodialogtextfiles modinfoforeign modinfooverwrite modinforegular diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 065058fc..7e72653f 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -38,6 +38,8 @@ along with Mod Organizer. If not, see . #include "previewdialog.h" #include "texteditor.h" +#include "modinfodialogtextfiles.h" + #include #include #include @@ -59,6 +61,18 @@ using namespace MOBase; using namespace MOShared; +std::vector> ModInfoDialogTab::createTabs( + Ui::ModInfoDialog* ui) +{ + std::vector> v; + + v.push_back(std::make_unique(ui)); + + return v; +} + + + class ModFileListWidget : public QListWidgetItem { friend bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS); public: @@ -254,6 +268,9 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo m_OrganizerCore(organizerCore), m_PluginContainer(pluginContainer) { ui->setupUi(this); + + m_tabs = ModInfoDialogTab::createTabs(ui); + this->setWindowTitle(modInfo->name()); this->setWindowModality(Qt::WindowModal); @@ -305,12 +322,6 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo } } - ui->textFileView->setupToolbar(); - - ui->tabTextSplitter->setSizes({200, 1}); - ui->tabTextSplitter->setStretchFactor(0, 0); - ui->tabTextSplitter->setStretchFactor(1, 1); - // refresh everything but the conflict lists, which are done in exec() because // they depend on restoring the state to some widgets; this refresh has to be // done here because some of the checks below depend on the ui to decide which @@ -333,15 +344,17 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo } else if (unmanaged) { + ui->tabWidget->setTabEnabled(TAB_TEXTFILES, false); ui->tabWidget->setTabEnabled(TAB_INIFILES, false); ui->tabWidget->setTabEnabled(TAB_CATEGORIES, false); ui->tabWidget->setTabEnabled(TAB_NEXUS, false); ui->tabWidget->setTabEnabled(TAB_FILETREE, false); ui->tabWidget->setTabEnabled(TAB_NOTES, false); ui->tabWidget->setTabEnabled(TAB_ESPS, false); - ui->tabWidget->setTabEnabled(TAB_TEXTFILES, false); ui->tabWidget->setTabEnabled(TAB_IMAGES, false); } else { + ui->tabWidget->setTabEnabled(TAB_TEXTFILES, true); + initFiletree(modInfo); addCategories(CategoryFactory::instance(), modInfo->getCategories(), ui->categoriesTree->invisibleRootItem(), 0); refreshPrimaryCategoriesBox(); @@ -838,7 +851,10 @@ QTreeWidgetItem* ModInfoDialog::createAdvancedConflictItem( void ModInfoDialog::refreshFiles() { // clearing - ui->textFileList->clear(); + for (auto& tab : m_tabs) { + tab->clear(); + } + ui->iniTweaksList->clear(); ui->iniFileList->clear(); ui->inactiveESPList->clear(); @@ -857,9 +873,13 @@ void ModInfoDialog::refreshFiles() while (dirIterator.hasNext()) { QString fileName = dirIterator.next(); - if (fileName.endsWith(".txt", Qt::CaseInsensitive)) { - ui->textFileList->addItem(fileName.mid(m_RootPath.length() + 1)); - } else if ((fileName.endsWith(".ini", Qt::CaseInsensitive) || fileName.endsWith(".cfg", Qt::CaseInsensitive)) && + for (auto& tab : m_tabs) { + if (tab->feedFile(m_RootPath, fileName)) { + break; + } + } + + if ((fileName.endsWith(".ini", Qt::CaseInsensitive) || fileName.endsWith(".cfg", Qt::CaseInsensitive)) && !fileName.endsWith("meta.ini")) { QString namePart = fileName.mid(m_RootPath.length() + 1); if (namePart.startsWith("INI Tweaks", Qt::CaseInsensitive)) { @@ -903,7 +923,6 @@ void ModInfoDialog::refreshFiles() } } - ui->tabWidget->setTabEnabled(TAB_TEXTFILES, ui->textFileList->count() != 0); ui->tabWidget->setTabEnabled(TAB_IMAGES, ui->thumbnailArea->count() != 0); ui->tabWidget->setTabEnabled(TAB_ESPS, (ui->inactiveESPList->count() != 0) || (ui->activeESPList->count() != 0)); } @@ -943,9 +962,17 @@ void ModInfoDialog::saveCategories(QTreeWidgetItem *currentNode) void ModInfoDialog::on_closeButton_clicked() { - if (allowNavigateFromTXT() && allowNavigateFromINI()) { - this->close(); + for (auto& tab : m_tabs) { + if (!tab->canClose()) { + return; + } + } + + if (!allowNavigateFromINI()) { + return; } + + close(); } @@ -981,23 +1008,6 @@ void ModInfoDialog::thumbnailClicked(const QString &fileName) ui->imageLabel->setPixmap(QPixmap::fromImage(image)); } -bool ModInfoDialog::allowNavigateFromTXT() -{ - if (ui->textFileView->dirty()) { - const int res = QMessageBox::question( - this, tr("Save changes?"), - tr("Save changes to \"%1\"?").arg(ui->textFileView->filename()), - QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); - - if (res == QMessageBox::Cancel) { - return false; - } else if (res == QMessageBox::Yes) { - saveCurrentTextFile(); - } - } - return true; -} - bool ModInfoDialog::allowNavigateFromINI() { if (ui->saveButton->isEnabled()) { @@ -1012,29 +1022,6 @@ bool ModInfoDialog::allowNavigateFromINI() return true; } -void ModInfoDialog::on_textFileList_currentItemChanged( - QListWidgetItem *current, QListWidgetItem *previous) -{ - const QString fullPath = m_RootPath + "/" + current->text(); - - if (fullPath == ui->textFileView->filename()) { - // the new file is the same as the currently displayed file. May be the - // result of a cancellation - return; - } - - if (allowNavigateFromTXT()) { - openTextFile(fullPath); - } else { - ui->textFileList->setCurrentItem(previous, QItemSelectionModel::Current); - } -} - -void ModInfoDialog::openTextFile(const QString &fileName) -{ - ui->textFileView->load(fileName); -} - void ModInfoDialog::openIniFile(const QString &fileName) { QFile iniFile(fileName); @@ -1109,11 +1096,6 @@ void ModInfoDialog::on_saveButton_clicked() saveCurrentIniFile(); } -void ModInfoDialog::saveCurrentTextFile() -{ - ui->textFileView->save(); -} - void ModInfoDialog::saveCurrentIniFile() { diff --git a/src/modinfodialog.h b/src/modinfodialog.h index df450ac0..97ab9a38 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -53,9 +53,30 @@ class QTreeView; class CategoryFactory; class TextEditor; +class TextFilesTab; + + +class ModInfoDialogTab +{ +public: + static std::vector> createTabs( + Ui::ModInfoDialog* ui); + + ModInfoDialogTab() = default; + ModInfoDialogTab(const ModInfoDialogTab&) = delete; + ModInfoDialogTab& operator=(const ModInfoDialogTab&) = delete; + ModInfoDialogTab(ModInfoDialogTab&&) = default; + ModInfoDialogTab& operator=(ModInfoDialogTab&&) = default; + virtual ~ModInfoDialogTab() = default; + + virtual void clear() = 0; + virtual bool feedFile(const QString& rootPath, const QString& filename) = 0; + virtual bool canClose() = 0; +}; + /** - * this is a larger dialog used to visualise information abount the mod. + * this is a larger dialog used to visualise information about the mod. * @todo this would probably a good place for a plugin-system **/ class ModInfoDialog : public MOBase::TutorableDialog @@ -147,11 +168,8 @@ private: void deleteFile(const QModelIndex &index); void saveIniTweaks(); void saveCategories(QTreeWidgetItem *currentNode); - void saveCurrentTextFile(); void saveCurrentIniFile(); - void openTextFile(const QString &fileName); void openIniFile(const QString &fileName); - bool allowNavigateFromTXT(); bool allowNavigateFromINI(); FileRenamer::RenameResults hideFile(FileRenamer& renamer, const QString &oldName); FileRenamer::RenameResults unhideFile(FileRenamer& renamer, const QString &oldName); @@ -189,7 +207,6 @@ private slots: void on_tabWidget_currentChanged(int index); void on_primaryCategoryBox_currentIndexChanged(int index); void on_categoriesTree_itemChanged(QTreeWidgetItem *item, int column); - void on_textFileList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); void on_iniFileList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); void on_iniTweaksList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous); void on_overwriteTree_itemDoubleClicked(QTreeWidgetItem *item, int column); @@ -234,6 +251,8 @@ private: ModInfo::Ptr m_ModInfo; + std::vector> m_tabs; + QSignalMapper m_ThumbnailMapper; QString m_RootPath; diff --git a/src/modinfodialogtextfiles.cpp b/src/modinfodialogtextfiles.cpp new file mode 100644 index 00000000..98da8c4a --- /dev/null +++ b/src/modinfodialogtextfiles.cpp @@ -0,0 +1,91 @@ +#include "modinfodialogtextfiles.h" +#include "ui_modinfodialog.h" +#include + +class TextFileItem : public QListWidgetItem +{ +public: + TextFileItem(const QString& rootPath, QString fullPath) + : m_fullPath(std::move(fullPath)) + { + setText(m_fullPath.mid(rootPath.length() + 1)); + } + + const QString& fullPath() const + { + return m_fullPath; + } + +private: + QString m_fullPath; +}; + + +TextFilesTab::TextFilesTab(Ui::ModInfoDialog* ui) + : ui(ui) +{ + ui->textFileView->setupToolbar(); + + ui->tabTextSplitter->setSizes({200, 1}); + ui->tabTextSplitter->setStretchFactor(0, 0); + ui->tabTextSplitter->setStretchFactor(1, 1); + + QObject::connect( + ui->textFileList, &QListWidget::currentItemChanged, + [&](auto* current, auto* previous){ onSelection(current, previous); }); +} + +void TextFilesTab::clear() +{ + ui->textFileList->clear(); +} + +bool TextFilesTab::feedFile(const QString& rootPath, const QString& fullPath) +{ + if (fullPath.endsWith(".txt", Qt::CaseInsensitive)) { + ui->textFileList->addItem(new TextFileItem(rootPath, fullPath)); + return true; + } + + return false; +} + +bool TextFilesTab::canClose() +{ + if (!ui->textFileView->dirty()) { + return true; + } + + const int res = QMessageBox::question( + ui->tabText, + QObject::tr("Save changes?"), + QObject::tr("Save changes to \"%1\"?").arg(ui->textFileView->filename()), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); + + if (res == QMessageBox::Cancel) { + return false; + } + + if (res == QMessageBox::Yes) { + ui->textFileView->save(); + } + + return true; +} + +void TextFilesTab::onSelection( + QListWidgetItem* current, QListWidgetItem* previous) +{ + auto* item = dynamic_cast(current); + if (!item) { + qCritical("TextFilesTab: item is not a TextFileItem"); + return; + } + + if (!canClose()) { + ui->textFileList->setCurrentItem(previous, QItemSelectionModel::Current); + return; + } + + ui->textFileView->load(item->fullPath()); +} diff --git a/src/modinfodialogtextfiles.h b/src/modinfodialogtextfiles.h new file mode 100644 index 00000000..ab9e972c --- /dev/null +++ b/src/modinfodialogtextfiles.h @@ -0,0 +1,21 @@ +#ifndef MODINFODIALOGTEXTFILES_H +#define MODINFODIALOGTEXTFILES_H + +#include "modinfodialog.h" + +class TextFilesTab : public ModInfoDialogTab +{ +public: + TextFilesTab(Ui::ModInfoDialog* ui); + + void clear() override; + bool feedFile(const QString& rootPath, const QString& fullPath) override; + bool canClose() override; + +private: + Ui::ModInfoDialog* ui; + + void onSelection(QListWidgetItem* current, QListWidgetItem* previous); +}; + +#endif // MODINFODIALOGTEXTFILES_H -- cgit v1.3.1 From 9f1520c69e88113d9a1a06f91bbabe9cf5ddef9f Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 17 Jun 2019 05:50:45 -0400 Subject: split images tab stuff in ImagesTab text editor: fixed modified flag not being set to false when loading an empty file --- src/CMakeLists.txt | 3 ++ src/modinfodialog.cpp | 51 ++++++------------------- src/modinfodialog.h | 5 +-- src/modinfodialog.ui | 8 ++-- src/modinfodialogimages.cpp | 91 +++++++++++++++++++++++++++++++++++++++++++++ src/modinfodialogimages.h | 37 ++++++++++++++++++ src/texteditor.cpp | 12 +++++- 7 files changed, 158 insertions(+), 49 deletions(-) create mode 100644 src/modinfodialogimages.cpp create mode 100644 src/modinfodialogimages.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7e62f3e9..c5018baf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ SET(organizer_SRCS modlist.cpp modidlineedit.cpp modinfodialog.cpp + modinfodialogimages.cpp modinfodialogtextfiles.cpp modinfo.cpp modinfobackup.cpp @@ -155,6 +156,7 @@ SET(organizer_HDRS modlist.h modidlineedit.h modinfodialog.h + modinfodialogimages.h modinfodialogtextfiles.h modinfo.h modinfobackup.h @@ -354,6 +356,7 @@ set(modinfo modinfo modinfobackup modinfodialog + modinfodialogimages modinfodialogtextfiles modinfoforeign modinfooverwrite diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 8f989d02..cc3140b6 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -39,6 +39,7 @@ along with Mod Organizer. If not, see . #include "texteditor.h" #include "modinfodialogtextfiles.h" +#include "modinfodialogimages.h" #include #include @@ -68,10 +69,16 @@ std::vector> ModInfoDialogTab::createTabs( v.push_back(std::make_unique(ui)); v.push_back(std::make_unique(ui)); + v.push_back(std::make_unique(ui)); return v; } +bool ModInfoDialogTab::canClose() +{ + return true; +} + class ModFileListWidget : public QListWidgetItem { @@ -262,7 +269,7 @@ public: 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_RequestStarted(false), m_NewFolderAction(nullptr), m_OpenAction(nullptr), m_PreviewAction(nullptr), m_RenameAction(nullptr), m_DeleteAction(nullptr), m_HideAction(nullptr), m_UnhideAction(nullptr), m_Directory(directory), m_Origin(nullptr), @@ -307,8 +314,6 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->descriptionView->setPage(new DescriptionPage()); - connect(&m_ThumbnailMapper, SIGNAL(mapped(const QString&)), this, SIGNAL(thumbnailClickedSignal(const QString&))); - connect(this, SIGNAL(thumbnailClickedSignal(const QString&)), this, SLOT(thumbnailClicked(const QString&))); connect(m_ModInfo.data(), SIGNAL(modDetailsUpdated(bool)), this, SLOT(modDetailsUpdated(bool))); connect(ui->descriptionView->page(), SIGNAL(linkClicked(QUrl)), this, SLOT(linkClicked(QUrl))); //TODO: No easy way to delegate links @@ -347,14 +352,16 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo { ui->tabWidget->setTabEnabled(TAB_TEXTFILES, false); ui->tabWidget->setTabEnabled(TAB_INIFILES, false); + ui->tabWidget->setTabEnabled(TAB_IMAGES, false); ui->tabWidget->setTabEnabled(TAB_CATEGORIES, false); ui->tabWidget->setTabEnabled(TAB_NEXUS, false); ui->tabWidget->setTabEnabled(TAB_FILETREE, false); ui->tabWidget->setTabEnabled(TAB_NOTES, false); ui->tabWidget->setTabEnabled(TAB_ESPS, false); - ui->tabWidget->setTabEnabled(TAB_IMAGES, false); } else { ui->tabWidget->setTabEnabled(TAB_TEXTFILES, true); + ui->tabWidget->setTabEnabled(TAB_INIFILES, true); + ui->tabWidget->setTabEnabled(TAB_IMAGES, true); initFiletree(modInfo); addCategories(CategoryFactory::instance(), modInfo->getCategories(), ui->categoriesTree->invisibleRootItem(), 0); @@ -843,13 +850,6 @@ void ModInfoDialog::refreshFiles() ui->inactiveESPList->clear(); ui->activeESPList->clear(); - ui->imageLabel->setPixmap({}); - - while (ui->thumbnailArea->count() > 0) { - auto* item = ui->thumbnailArea->takeAt(0); - delete item->widget(); - delete item; - } if (m_RootPath.length() > 0) { @@ -875,27 +875,10 @@ void ModInfoDialog::refreshFiles() } else { ui->activeESPList->addItem(relativePath); } - } else if ((fileName.endsWith(".png", Qt::CaseInsensitive)) || - (fileName.endsWith(".jpg", Qt::CaseInsensitive))) { - QImage image = QImage(fileName); - if (!image.isNull()) { - if (static_cast(image.width()) / static_cast(image.height()) > 1.34) { - image = image.scaledToWidth(128); - } else { - image = image.scaledToHeight(96); - } - - QPushButton *thumbnailButton = new QPushButton(QPixmap::fromImage(image), ""); - thumbnailButton->setIconSize(QSize(image.width(), image.height())); - connect(thumbnailButton, SIGNAL(clicked()), &m_ThumbnailMapper, SLOT(map())); - m_ThumbnailMapper.setMapping(thumbnailButton, fileName); - ui->thumbnailArea->addWidget(thumbnailButton); - } } } } - ui->tabWidget->setTabEnabled(TAB_IMAGES, ui->thumbnailArea->count() != 0); ui->tabWidget->setTabEnabled(TAB_ESPS, (ui->inactiveESPList->count() != 0) || (ui->activeESPList->count() != 0)); } @@ -964,18 +947,6 @@ void ModInfoDialog::openTab(int tab) } } -void ModInfoDialog::thumbnailClicked(const QString &fileName) -{ - ui->imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); - QImage image(fileName); - if (static_cast(image.width()) / static_cast(image.height()) > 1.34) { - image = image.scaledToWidth(ui->imageLabel->geometry().width()); - } else { - image = image.scaledToHeight(ui->imageLabel->geometry().height()); - } - ui->imageLabel->setPixmap(QPixmap::fromImage(image)); -} - void ModInfoDialog::on_activateESP_clicked() { QListWidget *activeESPList = findChild("activeESPList"); diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 70fd157c..f3ef6e76 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -69,7 +69,7 @@ public: virtual void clear() = 0; virtual bool feedFile(const QString& rootPath, const QString& filename) = 0; - virtual bool canClose() = 0; + virtual bool canClose(); }; @@ -135,7 +135,6 @@ public: signals: - void thumbnailClickedSignal(const QString &filename); void linkActivated(const QString &link); void downloadRequest(const QString &link); void modOpen(const QString &modName, int tab); @@ -172,7 +171,6 @@ private: int tabIndex(const QString &tabId); private slots: - void thumbnailClicked(const QString &fileName); void linkClicked(const QUrl &url); void linkClicked(QString url); @@ -239,7 +237,6 @@ private: std::vector> m_tabs; - QSignalMapper m_ThumbnailMapper; QString m_RootPath; OrganizerCore *m_OrganizerCore; diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index c6ab111f..ba670ef3 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -154,7 +154,7 @@ - + 0 0 @@ -183,7 +183,7 @@ - + 16777215 @@ -193,7 +193,7 @@ true - + 0 @@ -225,7 +225,7 @@ 0 - + 0 diff --git a/src/modinfodialogimages.cpp b/src/modinfodialogimages.cpp new file mode 100644 index 00000000..4f2ee78c --- /dev/null +++ b/src/modinfodialogimages.cpp @@ -0,0 +1,91 @@ +#include "modinfodialogimages.h" +#include "ui_modinfodialog.h" + +ThumbnailButton::ThumbnailButton(const QString& fullPath, QImage original) + : m_original(std::move(original)) +{ + const auto ratio = static_cast(m_original.width()) / m_original.height(); + + QImage thumbnail; + if (ratio > 1.34) { + thumbnail = m_original.scaledToWidth(128); + } else { + thumbnail = m_original.scaledToHeight(96); + } + + setIcon(QPixmap::fromImage(thumbnail)); + setIconSize(QSize(thumbnail.width(), thumbnail.height())); + + connect(this, &QPushButton::clicked, [&]{ emit open(m_original); }); +} + +const QImage& ThumbnailButton::image() const +{ + return m_original; +} + + +ImagesTab::ImagesTab(Ui::ModInfoDialog* ui) + : ui(ui) +{ +} + +void ImagesTab::clear() +{ + ui->imageLabel->setPixmap({}); + + while (ui->imageThumbnails->count() > 0) { + auto* item = ui->imageThumbnails->takeAt(0); + delete item->widget(); + delete item; + } +} + +bool ImagesTab::feedFile(const QString& rootPath, const QString& fullPath) +{ + static constexpr const char* extensions[] = { + ".png", ".jpg" + }; + + for (const auto* e : extensions) { + if (fullPath.endsWith(e, Qt::CaseInsensitive)) { + add(fullPath); + return true; + } + } + + return false; +} + +void ImagesTab::add(const QString& fullPath) +{ + QImage image = QImage(fullPath); + + if (image.isNull()) { + qWarning() << "ImagesTab: '" << fullPath << "' is not a valid image"; + return; + } + + auto* button = new ThumbnailButton(fullPath, std::move(image)); + + QObject::connect( + button, &ThumbnailButton::open, + [&](const QImage& image){ onOpen(image); }); + + ui->imageThumbnails->addWidget(button); +} + +void ImagesTab::onOpen(const QImage& original) +{ + QImage image; + + const auto ratio = static_cast(original.width()) / original.height(); + + if (ratio > 1.34) { + image = original.scaledToWidth(ui->imageLabel->geometry().width()); + } else { + image = original.scaledToHeight(ui->imageLabel->geometry().height()); + } + + ui->imageLabel->setPixmap(QPixmap::fromImage(image)); +} diff --git a/src/modinfodialogimages.h b/src/modinfodialogimages.h new file mode 100644 index 00000000..3c492e77 --- /dev/null +++ b/src/modinfodialogimages.h @@ -0,0 +1,37 @@ +#ifndef MODINFODIALOGIMAGES_H +#define MODINFODIALOGIMAGES_H + +#include "modinfodialog.h" + +class ThumbnailButton : public QPushButton +{ + Q_OBJECT; + +public: + ThumbnailButton(const QString& fullPath, QImage image); + const QImage& image() const; + +signals: + void open(const QImage& image); + +private: + const QImage m_original; +}; + + +class ImagesTab : public ModInfoDialogTab +{ +public: + ImagesTab(Ui::ModInfoDialog* ui); + + void clear() override; + bool feedFile(const QString& rootPath, const QString& fullPath) override; + +private: + Ui::ModInfoDialog* ui; + + void add(const QString& fullPath); + void onOpen(const QImage& image); +}; + +#endif // MODINFODIALOGIMAGES_H diff --git a/src/texteditor.cpp b/src/texteditor.cpp index 78ce1610..99490b22 100644 --- a/src/texteditor.cpp +++ b/src/texteditor.cpp @@ -60,9 +60,19 @@ void TextEditor::setDefaultStyle() bool TextEditor::load(const QString& filename) { m_filename = filename; - setPlainText(MOBase::readFileText(filename, &m_encoding)); + clear(); + + const QString s = MOBase::readFileText(filename, &m_encoding); + + setPlainText(s); document()->setModified(false); + if (s.isEmpty()) { + // the modificationChanged even is not fired by the setModified() call + // above when the text being set is empty + onModified(false); + } + return true; } -- cgit v1.3.1 From f8a037a409d1b6bbb2f34b237b8b66d454179f56 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 18 Jun 2019 09:29:06 -0400 Subject: changed layout of esps tab, moved to modinfodialogesps.cpp/h slightly offset next/previous images so they look better when shown next to each other vertically --- src/CMakeLists.txt | 3 + src/modinfodialog.cpp | 98 +------------ src/modinfodialog.h | 2 - src/modinfodialog.ui | 268 ++++++++++++++++++++++-------------- src/modinfodialogesps.cpp | 287 +++++++++++++++++++++++++++++++++++++++ src/modinfodialogesps.h | 26 ++++ src/modinfodialogimages.cpp | 2 - src/resources/go-next_16.png | Bin 676 -> 719 bytes src/resources/go-previous_16.png | Bin 655 -> 718 bytes 9 files changed, 483 insertions(+), 203 deletions(-) create mode 100644 src/modinfodialogesps.cpp create mode 100644 src/modinfodialogesps.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c5018baf..aba922f9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ SET(organizer_SRCS modlist.cpp modidlineedit.cpp modinfodialog.cpp + modinfodialogesps.cpp modinfodialogimages.cpp modinfodialogtextfiles.cpp modinfo.cpp @@ -156,6 +157,7 @@ SET(organizer_HDRS modlist.h modidlineedit.h modinfodialog.h + modinfodialogesps.h modinfodialogimages.h modinfodialogtextfiles.h modinfo.h @@ -356,6 +358,7 @@ set(modinfo modinfo modinfobackup modinfodialog + modinfodialogesps modinfodialogimages modinfodialogtextfiles modinfoforeign diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index cc3140b6..a52f2031 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -40,6 +40,7 @@ along with Mod Organizer. If not, see . #include "modinfodialogtextfiles.h" #include "modinfodialogimages.h" +#include "modinfodialogesps.h" #include #include @@ -70,6 +71,7 @@ std::vector> ModInfoDialogTab::createTabs( v.push_back(std::make_unique(ui)); v.push_back(std::make_unique(ui)); v.push_back(std::make_unique(ui)); + v.push_back(std::make_unique(ui)); return v; } @@ -353,15 +355,16 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->tabWidget->setTabEnabled(TAB_TEXTFILES, false); ui->tabWidget->setTabEnabled(TAB_INIFILES, false); ui->tabWidget->setTabEnabled(TAB_IMAGES, false); + ui->tabWidget->setTabEnabled(TAB_ESPS, false); ui->tabWidget->setTabEnabled(TAB_CATEGORIES, false); ui->tabWidget->setTabEnabled(TAB_NEXUS, false); ui->tabWidget->setTabEnabled(TAB_FILETREE, false); ui->tabWidget->setTabEnabled(TAB_NOTES, false); - ui->tabWidget->setTabEnabled(TAB_ESPS, false); } else { ui->tabWidget->setTabEnabled(TAB_TEXTFILES, true); ui->tabWidget->setTabEnabled(TAB_INIFILES, true); ui->tabWidget->setTabEnabled(TAB_IMAGES, true); + ui->tabWidget->setTabEnabled(TAB_ESPS, true); initFiletree(modInfo); addCategories(CategoryFactory::instance(), modInfo->getCategories(), ui->categoriesTree->invisibleRootItem(), 0); @@ -848,10 +851,6 @@ void ModInfoDialog::refreshFiles() tab->clear(); } - ui->inactiveESPList->clear(); - ui->activeESPList->clear(); - - if (m_RootPath.length() > 0) { QDirIterator dirIterator(m_RootPath, QDir::Files, QDirIterator::Subdirectories); while (dirIterator.hasNext()) { @@ -862,24 +861,8 @@ void ModInfoDialog::refreshFiles() break; } } - - if (fileName.endsWith(".esp", Qt::CaseInsensitive) || - fileName.endsWith(".esm", Qt::CaseInsensitive) || - fileName.endsWith(".esl", Qt::CaseInsensitive)) { - QString relativePath = fileName.mid(m_RootPath.length() + 1); - if (relativePath.contains('/')) { - QFileInfo fileInfo(fileName); - QListWidgetItem *newItem = new QListWidgetItem(fileInfo.fileName()); - newItem->setData(Qt::UserRole, relativePath); - ui->inactiveESPList->addItem(newItem); - } else { - ui->activeESPList->addItem(relativePath); - } - } } } - - ui->tabWidget->setTabEnabled(TAB_ESPS, (ui->inactiveESPList->count() != 0) || (ui->activeESPList->count() != 0)); } void ModInfoDialog::addCategories(const CategoryFactory &factory, const std::set &enabledCategories, QTreeWidgetItem *root, int rootLevel) @@ -947,79 +930,6 @@ void ModInfoDialog::openTab(int tab) } } -void ModInfoDialog::on_activateESP_clicked() -{ - QListWidget *activeESPList = findChild("activeESPList"); - QListWidget *inactiveESPList = findChild("inactiveESPList"); - - int selectedRow = inactiveESPList->currentRow(); - if (selectedRow < 0) { - return; - } - - QListWidgetItem *selectedItem = inactiveESPList->takeItem(selectedRow); - - QDir root(m_RootPath); - bool renamed = false; - - while (root.exists(selectedItem->text())) { - bool okClicked = false; - QString newName = QInputDialog::getText(this, tr("File Exists"), tr("A file with that name exists, please enter a new one"), QLineEdit::Normal, selectedItem->text(), &okClicked); - if (!okClicked) { - inactiveESPList->insertItem(selectedRow, selectedItem); - return; - } else if (newName.size() > 0) { - selectedItem->setText(newName); - renamed = true; - } - } - - if (root.rename(selectedItem->data(Qt::UserRole).toString(), selectedItem->text())) { - activeESPList->addItem(selectedItem); - if (renamed) { - selectedItem->setData(Qt::UserRole, QVariant()); - } - } else { - inactiveESPList->insertItem(selectedRow, selectedItem); - reportError(tr("failed to move file")); - } -} - - -void ModInfoDialog::on_deactivateESP_clicked() -{ - QListWidget *activeESPList = findChild("activeESPList"); - QListWidget *inactiveESPList = findChild("inactiveESPList"); - - int selectedRow = activeESPList->currentRow(); - if (selectedRow < 0) { - return; - } - - QDir root(m_RootPath); - - QListWidgetItem *selectedItem = activeESPList->takeItem(selectedRow); - - // if we moved the file from optional to active in this session, we move the file back to - // where it came from. Otherwise, it is moved to the new folder "optional" - if (selectedItem->data(Qt::UserRole).isNull()) { - selectedItem->setData(Qt::UserRole, QString("optional/") + selectedItem->text()); - if (!root.exists("optional")) { - if (!root.mkdir("optional")) { - reportError(tr("failed to create directory \"optional\"")); - activeESPList->insertItem(selectedRow, selectedItem); - return; - } - } - } - - if (root.rename(selectedItem->text(), selectedItem->data(Qt::UserRole).toString())) { - inactiveESPList->addItem(selectedItem); - } else { - activeESPList->insertItem(selectedRow, selectedItem); - } -} - void ModInfoDialog::on_visitNexusLabel_linkActivated(const QString &link) { emit linkActivated(link); diff --git a/src/modinfodialog.h b/src/modinfodialog.h index f3ef6e76..6ecf16bf 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -186,8 +186,6 @@ private slots: void on_openInExplorerButton_clicked(); void on_closeButton_clicked(); - void on_activateESP_clicked(); - void on_deactivateESP_clicked(); void on_visitNexusLabel_linkActivated(const QString &link); void on_modIDEdit_editingFinished(); void on_sourceGameEdit_currentIndexChanged(int); diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index c1dba1c7..f1212634 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -104,7 +104,7 @@ Qt::Horizontal - + 6 @@ -128,7 +128,7 @@ - + @@ -180,7 +180,7 @@ 0 0 - 604 + 741 436 @@ -213,117 +213,175 @@ Optional ESPs - - - - - List of esps, esms, and esls that can not be loaded by the game. - - - List of esps, esms, and esls contained in this plugin that currently can not be loaded by the game. They will not even appear in the esp-list in the main MO-window. -They usually contain optional functionality, see the readme. - -Most mods do not have optional esps, so chances are good you are looking at an empty list. - - - - - - - Optional ESPs + + + + + Qt::Horizontal - - - - - - - - - 96 - 0 - - - - Make the selected mod in the lower list unavailable. - - - The selected esp (in the lower list) will be pushed into a subdirectory of the mod and will thus become "invisible" to the game. It can then no longer be activated. - - - - - - - :/MO/gui/resources/go-up.png:/MO/gui/resources/go-up.png + + + + 0 - - - 22 - 22 - + + 0 - - - - - - - 96 - 0 - + + 0 - - Move a file to the data directory. + + 0 - - This moves a esp to the esp directory so it can be enabled in the main window. Please note that the ESP merely becomes "available", it will not necessarily be loaded! That is configured in the main window of omo. + + + + Optional ESPs + + + + + + + List of esps, esms, and esls that can not be loaded by the game. + + + List of esps, esms, and esls contained in this plugin that currently can not be loaded by the game. They will not even appear in the esp-list in the main MO-window. +They usually contain optional functionality, see the readme. + +Most mods do not have optional esps, so chances are good you are looking at an empty list. + + + + + + + + + 0 - - + + 0 - - - :/MO/gui/resources/go-down.png:/MO/gui/resources/go-down.png + + 0 - - - 22 - 22 - + + 0 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - ESPs in the data directory and thus visible to the game. - - - These are the mod files that are in the (virtual) data directory of your game and will thus be selectable in the esp list in the main window. - - - - - - - Available ESPs - + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Move a file to the data directory. + + + This moves a esp to the esp directory so it can be enabled in the main window. Please note that the ESP merely becomes "available", it will not necessarily be loaded! That is configured in the main window of omo. + + + + + + + :/MO/gui/next:/MO/gui/next + + + + 22 + 22 + + + + + + + + Make the selected mod in the lower list unavailable. + + + The selected esp (in the lower list) will be pushed into a subdirectory of the mod and will thus become "invisible" to the game. It can then no longer be activated. + + + + + + + :/MO/gui/previous:/MO/gui/previous + + + + 22 + 22 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Available ESPs + + + + + + + ESPs in the data directory and thus visible to the game. + + + These are the mod files that are in the (virtual) data directory of your game and will thus be selectable in the esp list in the main window. + + + + + + + + diff --git a/src/modinfodialogesps.cpp b/src/modinfodialogesps.cpp new file mode 100644 index 00000000..32dcdb35 --- /dev/null +++ b/src/modinfodialogesps.cpp @@ -0,0 +1,287 @@ +#include "modinfodialogesps.h" +#include "ui_modinfodialog.h" +#include + +using MOBase::reportError; + + +class ESP +{ +public: + ESP(QString rootPath, QString relativePath) + : m_rootPath(std::move(rootPath)), m_active(false) + { + if (relativePath.contains('/')) { + m_inactivePath = relativePath; + } else { + m_activePath = relativePath; + m_active = true; + } + } + + const QString& rootPath() const + { + return m_rootPath; + } + + const QString& relativePath() const + { + if (m_active) { + return m_activePath; + } else { + return m_inactivePath; + } + } + + const QString& activePath() const + { + return m_activePath; + } + + const QString& inactivePath() const + { + return m_inactivePath; + } + + QFileInfo fileInfo() const + { + return m_rootPath + QDir::separator() + relativePath(); + } + + bool isActive() const + { + return m_active; + } + + bool activate(const QString& newName) + { + QDir root(m_rootPath); + + if (root.rename(m_inactivePath, newName)) { + m_active = true; + m_activePath = newName; + + if (QFileInfo(m_inactivePath).fileName() != newName) { + // file was renamed + m_inactivePath = QFileInfo(m_inactivePath).path() + QDir::separator() + newName; + } + + return true; + } + + return false; + } + + bool deactivate(const QString& newName) + { + QDir root(m_rootPath); + + if (root.rename(m_activePath, newName)) { + m_active = false; + m_inactivePath = newName; + return true; + } + + return false; + } + +private: + QString m_rootPath; + QString m_activePath; + QString m_inactivePath; + bool m_active; +}; + + +class ESPItem : public QListWidgetItem +{ +public: + ESPItem(ESP esp) + : m_esp(std::move(esp)) + { + updateText(); + } + + const ESP& esp() const + { + return m_esp; + } + + void setESP(ESP esp) + { + m_esp = esp; + updateText(); + } + +private: + ESP m_esp; + + void updateText() + { + setText(m_esp.fileInfo().fileName()); + } +}; + + +ESPsTab::ESPsTab(Ui::ModInfoDialog* ui) + : ui(ui) +{ + QObject::connect( + ui->activateESP1, &QToolButton::clicked, [&]{ onActivate(); }); + + QObject::connect( + ui->deactivateESP1, &QToolButton::clicked, [&]{ onDeactivate(); }); +} + +void ESPsTab::clear() +{ + ui->inactiveESPList1->clear(); + ui->activeESPList1->clear(); +} + +bool ESPsTab::feedFile(const QString& rootPath, const QString& fullPath) +{ + static constexpr const char* extensions[] = { + ".esp", ".esm", ".esl" + }; + + for (const auto* e : extensions) { + if (fullPath.endsWith(e, Qt::CaseInsensitive)) { + QString relativePath = fullPath.mid(rootPath.length() + 1); + + auto* item = new ESPItem({rootPath, relativePath}); + + if (item->esp().isActive()) { + ui->activeESPList1->addItem(item); + } else { + ui->inactiveESPList1->addItem(item); + } + + return true; + } + } + + return false; +} + +void ESPsTab::onActivate() +{ + auto* item = selectedInactive(); + if (!item) { + qWarning("ESPsTab::onActivate(): no selection"); + return; + } + + ESP esp = item->esp(); + + if (esp.isActive()) { + qWarning("ESPsTab::onActive(): item is already active"); + return; + } + + QDir root(esp.rootPath()); + const QFileInfo file(esp.fileInfo()); + + QString newName = file.fileName(); + + while (root.exists(newName)) { + bool okClicked = false; + + newName = QInputDialog::getText( + ui->tabESPs, + QObject::tr("File Exists"), + QObject::tr("A file with that name exists, please enter a new one"), + QLineEdit::Normal, file.fileName(), &okClicked); + + if (!okClicked) { + return; + } + + if (newName.isEmpty()) { + newName = file.fileName(); + } + } + + if (esp.activate(newName)) { + ui->inactiveESPList1->takeItem(ui->inactiveESPList1->row(item)); + ui->activeESPList1->addItem(item); + item->setESP(esp); + } else { + reportError(QObject::tr("Failed to move file")); + } +} + +void ESPsTab::onDeactivate() +{ + auto* item = selectedActive(); + if (!item) { + qWarning("ESPsTab::onDeactivate(): no selection"); + return; + } + + ESP esp = item->esp(); + + if (!esp.isActive()) { + qWarning("ESPsTab::onDeactivate(): item is already inactive"); + return; + } + + QDir root(esp.rootPath()); + + // if we moved the file from optional to active in this session, we move the + // file back to where it came from. Otherwise, it is moved to the new folder + // "optional" + + QString newName = esp.inactivePath(); + + if (newName.isEmpty()) { + if (!root.exists("optional")) { + if (!root.mkdir("optional")) { + reportError(QObject::tr("Failed to create directory \"optional\"")); + return; + } + } + + newName = QString("optional") + QDir::separator() + esp.fileInfo().fileName(); + } + + if (esp.deactivate(newName)) { + ui->activeESPList1->takeItem(ui->activeESPList1->row(item)); + ui->inactiveESPList1->addItem(item); + item->setESP(esp); + } else { + reportError(QObject::tr("Failed to move file")); + } +} + +ESPItem* ESPsTab::selectedInactive() +{ + auto* item = ui->inactiveESPList1->currentItem(); + if (!item) { + return nullptr; + } + + auto* esp = dynamic_cast(item); + if (!esp) { + qCritical("ESPsTab: inactive item is not an ESPItem"); + return nullptr; + } + + return esp; +} + +ESPItem* ESPsTab::selectedActive() +{ + auto* item = ui->activeESPList1->currentItem(); + if (!item) { + return nullptr; + } + + auto* esp = dynamic_cast(item); + if (!esp) { + qCritical("ESPsTab: active item is not an ESPItem"); + return nullptr; + } + + return esp; +} diff --git a/src/modinfodialogesps.h b/src/modinfodialogesps.h new file mode 100644 index 00000000..a46677cb --- /dev/null +++ b/src/modinfodialogesps.h @@ -0,0 +1,26 @@ +#ifndef MODINFODIALOGESPS_H +#define MODINFODIALOGESPS_H + +#include "modinfodialog.h" + +class ESPItem; + +class ESPsTab : public ModInfoDialogTab +{ +public: + ESPsTab(Ui::ModInfoDialog* ui); + + void clear() override; + bool feedFile(const QString& rootPath, const QString& fullPath) override; + +private: + Ui::ModInfoDialog* ui; + + void onActivate(); + void onDeactivate(); + + ESPItem* selectedInactive(); + ESPItem* selectedActive(); +}; + +#endif // MODINFODIALOGESPS_H diff --git a/src/modinfodialogimages.cpp b/src/modinfodialogimages.cpp index fb2ad1fe..1659bed3 100644 --- a/src/modinfodialogimages.cpp +++ b/src/modinfodialogimages.cpp @@ -52,8 +52,6 @@ void ScalableImage::paintEvent(QPaintEvent* e) static_cast(std::round(m_original.height() * ratio))); if (m_scaled.isNull() || m_scaled.size() != scaledSize) { - qDebug() << "scaled to " << scaledSize; - m_scaled = m_original.scaled( scaledSize.width(), scaledSize.height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); diff --git a/src/resources/go-next_16.png b/src/resources/go-next_16.png index 6ef8de76..58742d39 100644 Binary files a/src/resources/go-next_16.png and b/src/resources/go-next_16.png differ diff --git a/src/resources/go-previous_16.png b/src/resources/go-previous_16.png index 659cd90d..b4b22d04 100644 Binary files a/src/resources/go-previous_16.png and b/src/resources/go-previous_16.png differ -- cgit v1.3.1 From c98115e2f6f59b807382c41256d7f1a86009aa40 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 19 Jun 2019 17:24:23 -0400 Subject: in the process of moving stuff to modinfodialogconflicts.h/cpp --- src/CMakeLists.txt | 3 + src/modinfodialog.cpp | 480 ++----------------------------------- src/modinfodialog.h | 45 ++-- src/modinfodialog.ui | 58 ++--- src/modinfodialogconflicts.cpp | 530 +++++++++++++++++++++++++++++++++++++++++ src/modinfodialogconflicts.h | 86 +++++++ 6 files changed, 689 insertions(+), 513 deletions(-) create mode 100644 src/modinfodialogconflicts.cpp create mode 100644 src/modinfodialogconflicts.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index aba922f9..eead6541 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ SET(organizer_SRCS modlist.cpp modidlineedit.cpp modinfodialog.cpp + modinfodialogconflicts.cpp modinfodialogesps.cpp modinfodialogimages.cpp modinfodialogtextfiles.cpp @@ -157,6 +158,7 @@ SET(organizer_HDRS modlist.h modidlineedit.h modinfodialog.h + modinfodialogconflicts.h modinfodialogesps.h modinfodialogimages.h modinfodialogtextfiles.h @@ -358,6 +360,7 @@ set(modinfo modinfo modinfobackup modinfodialog + modinfodialogconflicts modinfodialogesps modinfodialogimages modinfodialogtextfiles diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index a52f2031..c24176b3 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -41,6 +41,7 @@ along with Mod Organizer. If not, see . #include "modinfodialogtextfiles.h" #include "modinfodialogimages.h" #include "modinfodialogesps.h" +#include "modinfodialogconflicts.h" #include #include @@ -72,6 +73,7 @@ std::vector> ModInfoDialogTab::createTabs( v.push_back(std::make_unique(ui)); v.push_back(std::make_unique(ui)); v.push_back(std::make_unique(ui)); + v.push_back(std::make_unique(ui)); return v; } @@ -81,6 +83,15 @@ bool ModInfoDialogTab::canClose() return true; } +void ModInfoDialogTab::saveState(Settings&) +{ + // no-op +} + +void ModInfoDialogTab::restoreState(const Settings& s) +{ + // no-op +} class ModFileListWidget : public QListWidgetItem { @@ -103,20 +114,6 @@ static bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS const int max_scan_for_context_menu = 50; -class ElideLeftDelegate : public QStyledItemDelegate -{ -public: - using QStyledItemDelegate::QStyledItemDelegate; - -protected: - void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const - { - QStyledItemDelegate::initStyleOption(option, index); - option->textElideMode = Qt::ElideLeft; - } -}; - - bool canPreviewFile( PluginContainer* pluginContainer, bool isArchive, const QString& filename) { @@ -165,110 +162,6 @@ bool canUnhideFile(bool isArchive, const QString& filename) } -int naturalCompare(const QString& a, const QString& b) -{ - static QCollator c = []{ - QCollator c; - c.setNumericMode(true); - c.setCaseSensitivity(Qt::CaseInsensitive); - return c; - }(); - - return c.compare(a, b); -} - - -class ConflictItem : public QTreeWidgetItem -{ -public: - static const int FILENAME_USERROLE = Qt::UserRole + 1; - static const int ALT_ORIGIN_USERROLE = Qt::UserRole + 2; - static const int ARCHIVE_USERROLE = Qt::UserRole + 3; - static const int INDEX_USERROLE = Qt::UserRole + 4; - static const int HAS_ALTS_USERROLE = Qt::UserRole + 5; - - ConflictItem( - QStringList columns, FileEntry::Index index, const QString& fileName, - bool hasAltOrigins, const QString& altOrigin, bool archive) - : QTreeWidgetItem(columns) - { - setData(0, FILENAME_USERROLE, fileName); - setData(0, ALT_ORIGIN_USERROLE, altOrigin); - setData(0, ARCHIVE_USERROLE, archive); - setData(0, INDEX_USERROLE, index); - setData(0, HAS_ALTS_USERROLE, hasAltOrigins); - - if (archive) { - QFont f = font(0); - f.setItalic(true); - - for (int i=0; i); - return data(0, INDEX_USERROLE).toUInt(); - } - - bool canHide() const - { - return canHideFile(isArchive(), fileName()); - } - - bool canUnhide() const - { - return canUnhideFile(isArchive(), fileName()); - } - - bool canOpen() const - { - return canOpenFile(isArchive(), fileName()); - } - - bool canPreview(PluginContainer* pluginContainer) const - { - return canPreviewFile(pluginContainer, isArchive(), fileName()); - } - - bool operator<(const QTreeWidgetItem& other) const - { - const int column = treeWidget()->sortColumn(); - - if (column >= columnCount() || column >= other.columnCount()) { - // shouldn't happen - qWarning().nospace() << "ConflictItem::operator<() mistmatch in column count"; - return false; - } - - return (naturalCompare(text(column), other.text(column)) < 0); - } -}; - - 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_RequestStarted(false), @@ -356,6 +249,7 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->tabWidget->setTabEnabled(TAB_INIFILES, false); ui->tabWidget->setTabEnabled(TAB_IMAGES, false); ui->tabWidget->setTabEnabled(TAB_ESPS, false); + ui->tabWidget->setTabEnabled(TAB_CONFLICTS, true); ui->tabWidget->setTabEnabled(TAB_CATEGORIES, false); ui->tabWidget->setTabEnabled(TAB_NEXUS, false); ui->tabWidget->setTabEnabled(TAB_FILETREE, false); @@ -365,13 +259,13 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->tabWidget->setTabEnabled(TAB_INIFILES, true); ui->tabWidget->setTabEnabled(TAB_IMAGES, true); ui->tabWidget->setTabEnabled(TAB_ESPS, true); + ui->tabWidget->setTabEnabled(TAB_CONFLICTS, true); initFiletree(modInfo); addCategories(CategoryFactory::instance(), modInfo->getCategories(), ui->categoriesTree->invisibleRootItem(), 0); refreshPrimaryCategoriesBox(); } - ui->tabWidget->setTabEnabled(TAB_CONFLICTS, m_Origin != nullptr); ui->endorseBtn->setVisible(Settings::instance().endorsementIntegration()); @@ -389,36 +283,6 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo if (ui->tabWidget->currentIndex() == TAB_NEXUS) { activateNexusTab(); } - - m_overwriteExpander.set(ui->overwriteExpander, ui->overwriteTree, true); - m_overwrittenExpander.set(ui->overwrittenExpander, ui->overwrittenTree, true); - m_nonconflictExpander.set(ui->noConflictExpander, ui->noConflictTree); - - - m_advancedConflictFilter.set(ui->conflictsAdvancedFilter); - m_advancedConflictFilter.changed = [&]{ refreshConflictLists(false, true); }; - - // left-elide the overwrites column so that the nearest are visible - ui->conflictsAdvancedList->setItemDelegateForColumn( - 0, new ElideLeftDelegate(ui->conflictsAdvancedList)); - - // left-elide the file column to see filenames - ui->conflictsAdvancedList->setItemDelegateForColumn( - 1, new ElideLeftDelegate(ui->conflictsAdvancedList)); - - // don't elide the overwritten by column so that the nearest are visible - - connect(ui->conflictsAdvancedShowNoConflict, &QCheckBox::clicked, [&] { - refreshConflictLists(false, true); - }); - - connect(ui->conflictsAdvancedShowAll, &QRadioButton::clicked, [&] { - refreshConflictLists(false, true); - }); - - connect(ui->conflictsAdvancedShowNearest, &QRadioButton::clicked, [&] { - refreshConflictLists(false, true); - }); } @@ -488,41 +352,19 @@ int ModInfoDialog::tabIndex(const QString &tabId) void ModInfoDialog::saveState(Settings& s) const { s.directInterface().setValue("mod_info_tabs", saveTabState()); - s.directInterface().setValue("mod_info_conflicts", saveConflictsState()); - s.directInterface().setValue( - "mod_info_conflicts_overwrite", - ui->overwriteTree->header()->saveState()); - - s.directInterface().setValue( - "mod_info_conflicts_noconflict", - ui->noConflictTree->header()->saveState()); - - s.directInterface().setValue( - "mod_info_conflicts_overwritten", - ui->overwrittenTree->header()->saveState()); - - s.directInterface().setValue( - "mod_info_advanced_conflicts", - ui->conflictsAdvancedList->header()->saveState()); + for (const auto& tab : m_tabs) { + tab->saveState(s); + } } void ModInfoDialog::restoreState(const Settings& s) { restoreTabState(s.directInterface().value("mod_info_tabs").toByteArray()); - restoreConflictsState(s.directInterface().value("mod_info_conflicts").toByteArray()); - - ui->overwriteTree->header()->restoreState( - s.directInterface().value("mod_info_conflicts_overwrite").toByteArray()); - - ui->noConflictTree->header()->restoreState( - s.directInterface().value("mod_info_conflicts_noconflict").toByteArray()); - ui->overwrittenTree->header()->restoreState( - s.directInterface().value("mod_info_conflicts_overwritten").toByteArray()); - - ui->conflictsAdvancedList->header()->restoreState( - s.directInterface().value("mod_info_advanced_conflicts").toByteArray()); + for (const auto& tab : m_tabs) { + tab->restoreState(s); + } } void ModInfoDialog::restoreTabState(const QByteArray &state) @@ -556,37 +398,6 @@ void ModInfoDialog::restoreTabState(const QByteArray &state) ui->tabWidget->blockSignals(false); } -void ModInfoDialog::restoreConflictsState(const QByteArray &state) -{ - QDataStream stream(state); - - bool overwriteExpanded = false; - bool overwrittenExpanded = false; - bool noConflictExpanded = false; - - stream >> overwriteExpanded >> overwrittenExpanded >> noConflictExpanded; - - if (stream.status() == QDataStream::Ok) { - m_overwriteExpander.toggle(overwriteExpanded); - m_overwrittenExpander.toggle(overwrittenExpanded); - m_nonconflictExpander.toggle(noConflictExpanded); - } - - int index = 0; - bool noConflictChecked = false; - bool showAllChecked = false; - bool showNearestChecked = false; - - stream >> index >> noConflictChecked >> showAllChecked >> showNearestChecked; - - if (stream.status() == QDataStream::Ok) { - ui->tabConflictsTabs->setCurrentIndex(index); - ui->conflictsAdvancedShowNoConflict->setChecked(noConflictChecked); - ui->conflictsAdvancedShowAll->setChecked(showAllChecked); - ui->conflictsAdvancedShowNearest->setChecked(showNearestChecked); - } -} - QByteArray ModInfoDialog::saveTabState() const { QByteArray result; @@ -599,251 +410,12 @@ QByteArray ModInfoDialog::saveTabState() const return result; } -QByteArray ModInfoDialog::saveConflictsState() const -{ - QByteArray result; - QDataStream stream(&result, QIODevice::WriteOnly); - - stream - << m_overwriteExpander.opened() - << m_overwrittenExpander.opened() - << m_nonconflictExpander.opened() - << ui->tabConflictsTabs->currentIndex() - << ui->conflictsAdvancedShowNoConflict->isChecked() - << ui->conflictsAdvancedShowAll->isChecked() - << ui->conflictsAdvancedShowNearest->isChecked(); - - return result; -} - void ModInfoDialog::refreshLists() { refreshConflictLists(true, true); refreshFiles(); } -void ModInfoDialog::refreshConflictLists( - bool refreshGeneral, bool refreshAdvanced) -{ - int numNonConflicting = 0; - int numOverwrite = 0; - int numOverwritten = 0; - - if (refreshGeneral) { - ui->overwriteTree->clear(); - ui->overwrittenTree->clear(); - ui->noConflictTree->clear(); - } - - if (refreshAdvanced) { - ui->conflictsAdvancedList->clear(); - } - - if (m_Origin != nullptr) { - std::vector files = m_Origin->getFiles(); - - for (const auto& file : m_Origin->getFiles()) { - const QString relativeName = QDir::fromNativeSeparators(ToQString(file->getRelativePath())); - const QString fileName = relativeName.mid(0).prepend(m_RootPath); - - bool archive = false; - const int fileOrigin = file->getOrigin(archive); - const auto& alternatives = file->getAlternatives(); - - if (refreshGeneral) { - if (fileOrigin == m_Origin->getID()) { - if (!alternatives.empty()) { - ui->overwriteTree->addTopLevelItem(createOverwriteItem( - file->getIndex(), archive, fileName, relativeName, alternatives)); - - ++numOverwrite; - } else { - // otherwise, put the file in the noconflict tree - ui->noConflictTree->addTopLevelItem(createNoConflictItem( - file->getIndex(), archive, fileName, relativeName)); - - ++numNonConflicting; - } - } else { - ui->overwrittenTree->addTopLevelItem(createOverwrittenItem( - file->getIndex(), fileOrigin, archive, fileName, relativeName)); - - ++numOverwritten; - } - } - - if (refreshAdvanced) { - auto* advancedItem = createAdvancedConflictItem( - file->getIndex(), fileOrigin, archive, - fileName, relativeName, alternatives); - - if (advancedItem) { - ui->conflictsAdvancedList->addTopLevelItem(advancedItem); - } - } - } - } - - if (refreshGeneral) { - ui->overwriteCount->display(numOverwrite); - ui->overwrittenCount->display(numOverwritten); - ui->noConflictCount->display(numNonConflicting); - } -} - -QTreeWidgetItem* ModInfoDialog::createOverwriteItem( - FileEntry::Index index, bool archive, - const QString& fileName, const QString& relativeName, - const FileEntry::AlternativesVector& alternatives) -{ - QString altString; - - for (const auto& alt : alternatives) { - if (!altString.isEmpty()) { - altString += ", "; - } - - altString += ToQString(m_Directory->getOriginByID(alt.first).getName()); - } - - QStringList fields(relativeName); - fields.append(altString); - - const auto origin = ToQString(m_Directory->getOriginByID(alternatives.back().first).getName()); - - return new ConflictItem(fields, index, fileName, true, origin, archive); -} - -QTreeWidgetItem* ModInfoDialog::createNoConflictItem( - FileEntry::Index index, bool archive, - const QString& fileName, const QString& relativeName) -{ - return new ConflictItem({relativeName}, index, fileName, false, "", archive); -} - -QTreeWidgetItem* ModInfoDialog::createOverwrittenItem( - FileEntry::Index index, int fileOrigin, bool archive, - const QString& fileName, const QString& relativeName) -{ - const FilesOrigin &realOrigin = m_Directory->getOriginByID(fileOrigin); - - QStringList fields(relativeName); - fields.append(ToQString(realOrigin.getName())); - - return new ConflictItem( - fields, index, fileName, true, ToQString(realOrigin.getName()), archive); -} - -QTreeWidgetItem* ModInfoDialog::createAdvancedConflictItem( - FileEntry::Index index,int fileOrigin, bool archive, - const QString& fileName, const QString& relativeName, - const MOShared::FileEntry::AlternativesVector& alternatives) -{ - QString before, after; - - if (!alternatives.empty()) { - int beforePrio = 0; - int afterPrio = std::numeric_limits::max(); - - for (const auto& alt : alternatives) - { - const auto altOrigin = m_Directory->getOriginByID(alt.first); - - if (ui->conflictsAdvancedShowAll->isChecked()) { - // fills 'before' and 'after' with all the alternatives that come - // before and after this mod in terms of priority - - if (altOrigin.getPriority() < m_Origin->getPriority()) { - // add all the mods having a lower priority than this one - if (!before.isEmpty()) { - before += ", "; - } - - before += ToQString(altOrigin.getName()); - } else if (altOrigin.getPriority() > m_Origin->getPriority()) { - // add all the mods having a higher priority than this one - if (!after.isEmpty()) { - after += ", "; - } - - after += ToQString(altOrigin.getName()); - } - } else { - // keep track of the nearest mods that come before and after this one - // in terms of priority - - if (altOrigin.getPriority() < m_Origin->getPriority()) { - // the alternative has a lower priority than this mod - - if (altOrigin.getPriority() > beforePrio) { - // the alternative has a higher priority and therefore is closer - // to this mod, use it - before = ToQString(altOrigin.getName()); - beforePrio = altOrigin.getPriority(); - } - } - - if (altOrigin.getPriority() > m_Origin->getPriority()) { - // the alternative has a higher priority than this mod - - if (altOrigin.getPriority() < afterPrio) { - // the alternative has a lower priority and there is closer - // to this mod, use it - after = ToQString(altOrigin.getName()); - afterPrio = altOrigin.getPriority(); - } - } - } - } - - // the primary origin is never in the list of alternatives, so it has to - // be handled separately - // - // if 'after' is not empty, it means at least one alternative with a higher - // priority than this mod was found; if the user only wants to see the - // nearest mods, it's not worth checking for the primary origin because it - // will always have a higher priority than the alternatives (or it wouldn't - // be the primary) - if (after.isEmpty() || ui->conflictsAdvancedShowAll->isChecked()) { - FilesOrigin &realOrigin = m_Directory->getOriginByID(fileOrigin); - - // if no mods overwrite this file, the primary origin is the same as this - // mod, so ignore that - if (realOrigin.getID() != m_Origin->getID()) { - if (!after.isEmpty()) { - after += ", "; - } - - after += ToQString(realOrigin.getName()); - } - } - } - - bool hasAlts = !before.isEmpty() || !after.isEmpty(); - - if (!ui->conflictsAdvancedShowNoConflict->isChecked()) { - // if both before and after are empty, it means this file has no conflicts - // at all, only display it if the user wants it - if (!hasAlts) { - return nullptr; - } - } - - bool matched = m_advancedConflictFilter.matches([&](auto&& what) { - return - before.contains(what, Qt::CaseInsensitive) || - relativeName.contains(what, Qt::CaseInsensitive) || - after.contains(what, Qt::CaseInsensitive); - }); - - if (!matched) { - return nullptr; - } - - return new ConflictItem( - {before, relativeName, after}, index, fileName, hasAlts, "", archive); -} - void ModInfoDialog::refreshFiles() { // clearing @@ -1475,7 +1047,6 @@ void ModInfoDialog::refreshPrimaryCategoriesBox() } } - void ModInfoDialog::on_primaryCategoryBox_currentIndexChanged(int index) { if (index != -1) { @@ -1483,19 +1054,6 @@ void ModInfoDialog::on_primaryCategoryBox_currentIndexChanged(int index) } } - -void ModInfoDialog::on_overwriteTree_itemDoubleClicked(QTreeWidgetItem *item, int) -{ - if (auto* ci=dynamic_cast(item)) { - const auto origin = ci->altOrigin(); - - if (!origin.isEmpty()) { - close(); - emit modOpen(origin, TAB_CONFLICTS); - } - } -} - FileRenamer::RenameResults ModInfoDialog::hideFile(FileRenamer& renamer, const QString &oldName) { const QString newName = oldName + ModInfo::s_HiddenExt; diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 6ecf16bf..53b5d94a 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -70,9 +70,31 @@ public: virtual void clear() = 0; virtual bool feedFile(const QString& rootPath, const QString& filename) = 0; virtual bool canClose(); + virtual void saveState(Settings& s); + virtual void restoreState(const Settings& s); }; +class ElideLeftDelegate : public QStyledItemDelegate +{ +public: + using QStyledItemDelegate::QStyledItemDelegate; + +protected: + void initStyleOption(QStyleOptionViewItem* o, const QModelIndex& i) const + { + QStyledItemDelegate::initStyleOption(o, i); + o->textElideMode = Qt::ElideLeft; + } +}; + + +bool canPreviewFile(PluginContainer* pluginContainer, bool isArchive, const QString& filename); +bool canOpenFile(bool isArchive, const QString& filename); +bool canHideFile(bool isArchive, const QString& filename); +bool canUnhideFile(bool isArchive, const QString& filename); + + /** * this is a larger dialog used to visualise information about the mod. * @todo this would probably a good place for a plugin-system @@ -262,36 +284,13 @@ private: std::map m_RealTabPos; - ExpanderWidget m_overwriteExpander, m_overwrittenExpander, m_nonconflictExpander; - FilterWidget m_advancedConflictFilter; void refreshConflictLists(bool refreshGeneral, bool refreshAdvanced); void refreshFiles(); - QTreeWidgetItem* createOverwriteItem( - FileEntry::Index index, bool archive, - const QString& fileName, const QString& relativeName, - const MOShared::FileEntry::AlternativesVector& alternatives); - - QTreeWidgetItem* createNoConflictItem( - FileEntry::Index index, bool archive, - const QString& fileName, const QString& relativeName); - - QTreeWidgetItem* createOverwrittenItem( - FileEntry::Index index, int fileOrigin, bool archive, - const QString& fileName, const QString& relativeName); - - QTreeWidgetItem* createAdvancedConflictItem( - FileEntry::Index index, int fileOrigin, bool archive, - const QString& fileName, const QString& relativeName, - const MOShared::FileEntry::AlternativesVector& alternatives); - void restoreTabState(const QByteArray &state); - void restoreConflictsState(const QByteArray &state); - QByteArray saveTabState() const; - QByteArray saveConflictsState() const; void changeFiletreeVisibility(bool visible); diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index f1212634..a26d15ab 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -241,7 +241,7 @@ - + List of esps, esms, and esls that can not be loaded by the game. @@ -285,7 +285,7 @@ Most mods do not have optional esps, so chances are good you are looking at an e - + Move a file to the data directory. @@ -308,7 +308,7 @@ Most mods do not have optional esps, so chances are good you are looking at an e - + Make the selected mod in the lower list unavailable. @@ -368,7 +368,7 @@ Most mods do not have optional esps, so chances are good you are looking at an e - + ESPs in the data directory and thus visible to the game. @@ -386,23 +386,23 @@ Most mods do not have optional esps, so chances are good you are looking at an e - + Conflicts - + - 0 + 1 - + General - + 0 @@ -423,11 +423,11 @@ Most mods do not have optional esps, so chances are good you are looking at an e 0 - + - + - + 0 @@ -444,7 +444,7 @@ text-align: left; - + QFrame::Sunken @@ -461,7 +461,7 @@ text-align: left; - + Qt::CustomContextMenu @@ -502,11 +502,11 @@ text-align: left; - + - + - + 0 @@ -523,7 +523,7 @@ text-align: left; - + QFrame::Sunken @@ -537,7 +537,7 @@ text-align: left; - + Qt::CustomContextMenu @@ -578,11 +578,11 @@ text-align: left; - + - + - + 0 @@ -599,7 +599,7 @@ text-align: left; - + QFrame::Sunken @@ -613,7 +613,7 @@ text-align: left; - + Qt::CustomContextMenu @@ -696,7 +696,7 @@ text-align: left; 0 - + Qt::CustomContextMenu @@ -751,7 +751,7 @@ text-align: left; 0 - + Whether files that have no conflicts should be visible in the list @@ -764,7 +764,7 @@ text-align: left; - + Shows all mods overwriting or being overwritten by this mod @@ -777,7 +777,7 @@ text-align: left; - + Shows only the nearest conflicting mods, in order of priority @@ -802,7 +802,7 @@ text-align: left; 0 - + Filter diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp new file mode 100644 index 00000000..17313436 --- /dev/null +++ b/src/modinfodialogconflicts.cpp @@ -0,0 +1,530 @@ +#include "modinfodialogconflicts.h" +#include "ui_modinfodialog.h" +#include "utility.h" + +using namespace MOShared; +using namespace MOBase; + +int naturalCompare(const QString& a, const QString& b) +{ + static QCollator c = []{ + QCollator c; + c.setNumericMode(true); + c.setCaseSensitivity(Qt::CaseInsensitive); + return c; + }(); + + return c.compare(a, b); +} + + +class ConflictItem : public QTreeWidgetItem +{ +public: + static const int FILENAME_USERROLE = Qt::UserRole + 1; + static const int ALT_ORIGIN_USERROLE = Qt::UserRole + 2; + static const int ARCHIVE_USERROLE = Qt::UserRole + 3; + static const int INDEX_USERROLE = Qt::UserRole + 4; + static const int HAS_ALTS_USERROLE = Qt::UserRole + 5; + + ConflictItem( + QStringList columns, FileEntry::Index index, const QString& fileName, + bool hasAltOrigins, const QString& altOrigin, bool archive) + : QTreeWidgetItem(columns) + { + setData(0, FILENAME_USERROLE, fileName); + setData(0, ALT_ORIGIN_USERROLE, altOrigin); + setData(0, ARCHIVE_USERROLE, archive); + setData(0, INDEX_USERROLE, index); + setData(0, HAS_ALTS_USERROLE, hasAltOrigins); + + if (archive) { + QFont f = font(0); + f.setItalic(true); + + for (int i=0; i); + return data(0, INDEX_USERROLE).toUInt(); + } + + bool canHide() const + { + return canHideFile(isArchive(), fileName()); + } + + bool canUnhide() const + { + return canUnhideFile(isArchive(), fileName()); + } + + bool canOpen() const + { + return canOpenFile(isArchive(), fileName()); + } + + bool canPreview(PluginContainer* pluginContainer) const + { + return canPreviewFile(pluginContainer, isArchive(), fileName()); + } + + bool operator<(const QTreeWidgetItem& other) const + { + const int column = treeWidget()->sortColumn(); + + if (column >= columnCount() || column >= other.columnCount()) { + // shouldn't happen + qWarning().nospace() << "ConflictItem::operator<() mistmatch in column count"; + return false; + } + + return (naturalCompare(text(column), other.text(column)) < 0); + } +}; + + +ConflictsTab::ConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& oc) + : ui(ui), m_general(ui, oc), m_advanced(ui, oc) +{ +} + +void ConflictsTab::saveState(Settings& s) +{ + s.directInterface().setValue( + "mod_info_conflicts_tab", ui->tabConflictsTabs1->currentIndex()); + + m_general.saveState(s); + m_advanced.saveState(s); +} + +void ConflictsTab::restoreState(const Settings& s) +{ + ui->tabConflictsTabs1->setCurrentIndex( + s.directInterface().value("mod_info_conflicts_tab", 0).toInt()); + + m_general.restoreState(s); + m_advanced.restoreState(s); +} + + +GeneralConflictsTab::GeneralConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& oc) + : ui(ui), m_core(oc), m_origin(nullptr) +{ + m_expanders.overwrite.set(ui->overwriteExpander1, ui->overwriteTree1, true); + m_expanders.overwritten.set(ui->overwrittenExpander1, ui->overwrittenTree1, true); + m_expanders.nonconflict.set(ui->noConflictExpander1, ui->noConflictTree1); + + QObject::connect( + ui->overwriteTree1, &QTreeWidget::itemDoubleClicked, + [&](auto* item, int col){ onOverwriteActivated(item, col); }); +} + +void GeneralConflictsTab::saveState(Settings& s) +{ + QByteArray result; + QDataStream stream(&result, QIODevice::WriteOnly); + + stream + << m_expanders.overwrite.opened() + << m_expanders.overwritten.opened() + << m_expanders.nonconflict.opened(); + + s.directInterface().setValue( + "mod_info_conflicts_general_expanders", result); + + s.directInterface().setValue( + "mod_info_conflicts_general_overwrite", + ui->overwriteTree1->header()->saveState()); + + s.directInterface().setValue( + "mod_info_conflicts_general_noconflict", + ui->noConflictTree1->header()->saveState()); + + s.directInterface().setValue( + "mod_info_conflicts_general_overwritten", + ui->overwrittenTree1->header()->saveState()); +} + +void GeneralConflictsTab::restoreState(const Settings& s) +{ + QDataStream stream(s.directInterface() + .value("mod_info_conflicts_general_expanders").toByteArray()); + + bool overwriteExpanded = false; + bool overwrittenExpanded = false; + bool noConflictExpanded = false; + + stream >> overwriteExpanded >> overwrittenExpanded >> noConflictExpanded; + + if (stream.status() == QDataStream::Ok) { + m_expanders.overwrite.toggle(overwriteExpanded); + m_expanders.overwritten.toggle(overwrittenExpanded); + m_expanders.nonconflict.toggle(noConflictExpanded); + } + + ui->overwriteTree1->header()->restoreState(s.directInterface() + .value("mod_info_conflicts_general_overwrite").toByteArray()); + + ui->noConflictTree1->header()->restoreState(s.directInterface() + .value("mod_info_conflicts_general_noconflict").toByteArray()); + + ui->overwrittenTree1->header()->restoreState(s.directInterface() + .value("mod_info_conflicts_general_overwritten").toByteArray()); +} + +void GeneralConflictsTab::rebuild(ModInfo::Ptr mod, FilesOrigin* origin) +{ + m_mod = mod; + m_origin = origin; + + update(); +} + +void GeneralConflictsTab::update() +{ + int numNonConflicting = 0; + int numOverwrite = 0; + int numOverwritten = 0; + + ui->overwriteTree1->clear(); + ui->overwrittenTree1->clear(); + ui->noConflictTree1->clear(); + + if (m_origin != nullptr) { + const auto rootPath = m_mod->absolutePath(); + + for (const auto& file : m_origin->getFiles()) { + const QString relativeName = QDir::fromNativeSeparators(ToQString(file->getRelativePath())); + const QString fileName = relativeName.mid(0).prepend(rootPath); + + bool archive = false; + const int fileOrigin = file->getOrigin(archive); + const auto& alternatives = file->getAlternatives(); + + if (fileOrigin == m_origin->getID()) { + if (!alternatives.empty()) { + ui->overwriteTree1->addTopLevelItem(createOverwriteItem( + file->getIndex(), archive, fileName, relativeName, alternatives)); + + ++numOverwrite; + } else { + // otherwise, put the file in the noconflict tree + ui->noConflictTree1->addTopLevelItem(createNoConflictItem( + file->getIndex(), archive, fileName, relativeName)); + + ++numNonConflicting; + } + } else { + ui->overwrittenTree1->addTopLevelItem(createOverwrittenItem( + file->getIndex(), fileOrigin, archive, fileName, relativeName)); + + ++numOverwritten; + } + } + } + + ui->overwriteCount1->display(numOverwrite); + ui->overwrittenCount1->display(numOverwritten); + ui->noConflictCount1->display(numNonConflicting); +} + +QTreeWidgetItem* GeneralConflictsTab::createOverwriteItem( + FileEntry::Index index, bool archive, + const QString& fileName, const QString& relativeName, + const FileEntry::AlternativesVector& alternatives) +{ + const auto& ds = *m_core.directoryStructure(); + + QString altString; + + for (const auto& alt : alternatives) { + if (!altString.isEmpty()) { + altString += ", "; + } + + altString += ToQString(ds.getOriginByID(alt.first).getName()); + } + + QStringList fields(relativeName); + fields.append(altString); + + const auto origin = ToQString(ds.getOriginByID(alternatives.back().first).getName()); + + return new ConflictItem(fields, index, fileName, true, origin, archive); +} + +QTreeWidgetItem* GeneralConflictsTab::createNoConflictItem( + FileEntry::Index index, bool archive, + const QString& fileName, const QString& relativeName) +{ + return new ConflictItem({relativeName}, index, fileName, false, "", archive); +} + +QTreeWidgetItem* GeneralConflictsTab::createOverwrittenItem( + FileEntry::Index index, int fileOrigin, bool archive, + const QString& fileName, const QString& relativeName) +{ + const auto& ds = *m_core.directoryStructure(); + + const FilesOrigin &realOrigin = ds.getOriginByID(fileOrigin); + + QStringList fields(relativeName); + fields.append(ToQString(realOrigin.getName())); + + return new ConflictItem( + fields, index, fileName, true, ToQString(realOrigin.getName()), archive); +} + +void GeneralConflictsTab::onOverwriteActivated(QTreeWidgetItem *item, int) +{ + if (auto* ci=dynamic_cast(item)) { + const auto origin = ci->altOrigin(); + + if (!origin.isEmpty()) { + close(); + emit modOpen(origin, TAB_CONFLICTS); + } + } +} + + +AdvancedConflictsTab::AdvancedConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& oc) + : ui(ui), m_core(oc), m_origin(nullptr) +{ + // left-elide the overwrites column so that the nearest are visible + ui->conflictsAdvancedList1->setItemDelegateForColumn( + 0, new ElideLeftDelegate(ui->conflictsAdvancedList1)); + + // left-elide the file column to see filenames + ui->conflictsAdvancedList1->setItemDelegateForColumn( + 1, new ElideLeftDelegate(ui->conflictsAdvancedList1)); + + // don't elide the overwritten by column so that the nearest are visible + + QObject::connect(ui->conflictsAdvancedShowNoConflict1, &QCheckBox::clicked, [&] { + update(); + }); + + QObject::connect(ui->conflictsAdvancedShowAll1, &QRadioButton::clicked, [&] { + update(); + }); + + QObject::connect(ui->conflictsAdvancedShowNearest1, &QRadioButton::clicked, [&] { + update(); + }); + + m_filter.set(ui->conflictsAdvancedFilter1); + m_filter.changed = [&]{ update(); }; +} + +void AdvancedConflictsTab::saveState(Settings& s) +{ + s.directInterface().setValue( + "mod_info_conflicts_advanced_list", + ui->conflictsAdvancedList1->header()->saveState()); + + QByteArray result; + QDataStream stream(&result, QIODevice::WriteOnly); + + stream + << ui->conflictsAdvancedShowNoConflict1->isChecked() + << ui->conflictsAdvancedShowAll1->isChecked() + << ui->conflictsAdvancedShowNearest1->isChecked(); + + s.directInterface().setValue( + "mod_info_conflicts_advanced_options", + ui->conflictsAdvancedList1->header()->saveState()); +} + +void AdvancedConflictsTab::restoreState(const Settings& s) +{ + ui->conflictsAdvancedList1->header()->restoreState( + s.directInterface().value("mod_info_conflicts_advanced_list").toByteArray()); + + QDataStream stream(s.directInterface() + .value("mod_info_conflicts_advanced_options").toByteArray()); + + bool noConflictChecked = false; + bool showAllChecked = false; + bool showNearestChecked = false; + + stream >> noConflictChecked >> showAllChecked >> showNearestChecked; + + if (stream.status() == QDataStream::Ok) { + ui->conflictsAdvancedShowNoConflict1->setChecked(noConflictChecked); + ui->conflictsAdvancedShowAll1->setChecked(showAllChecked); + ui->conflictsAdvancedShowNearest1->setChecked(showNearestChecked); + } +} + +void AdvancedConflictsTab::rebuild(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) +{ + m_mod = mod; + m_origin = origin; + + update(); +} + +void AdvancedConflictsTab::update() +{ + ui->conflictsAdvancedList1->clear(); + + if (m_origin != nullptr) { + const auto rootPath = m_mod->absolutePath(); + + for (const auto& file : m_origin->getFiles()) { + const QString relativeName = QDir::fromNativeSeparators(ToQString(file->getRelativePath())); + const QString fileName = relativeName.mid(0).prepend(rootPath); + + bool archive = false; + const int fileOrigin = file->getOrigin(archive); + const auto& alternatives = file->getAlternatives(); + + auto* advancedItem = createItem( + file->getIndex(), fileOrigin, archive, + fileName, relativeName, alternatives); + + if (advancedItem) { + ui->conflictsAdvancedList1->addTopLevelItem(advancedItem); + } + } + } +} + +QTreeWidgetItem* AdvancedConflictsTab::createItem( + FileEntry::Index index, int fileOrigin, bool archive, + const QString& fileName, const QString& relativeName, + const MOShared::FileEntry::AlternativesVector& alternatives) +{ + const auto& ds = *m_core.directoryStructure(); + + QString before, after; + + if (!alternatives.empty()) { + int beforePrio = 0; + int afterPrio = std::numeric_limits::max(); + + for (const auto& alt : alternatives) + { + const auto altOrigin = ds.getOriginByID(alt.first); + + if (ui->conflictsAdvancedShowAll1->isChecked()) { + // fills 'before' and 'after' with all the alternatives that come + // before and after this mod in terms of priority + + if (altOrigin.getPriority() < m_origin->getPriority()) { + // add all the mods having a lower priority than this one + if (!before.isEmpty()) { + before += ", "; + } + + before += ToQString(altOrigin.getName()); + } else if (altOrigin.getPriority() > m_origin->getPriority()) { + // add all the mods having a higher priority than this one + if (!after.isEmpty()) { + after += ", "; + } + + after += ToQString(altOrigin.getName()); + } + } else { + // keep track of the nearest mods that come before and after this one + // in terms of priority + + if (altOrigin.getPriority() < m_origin->getPriority()) { + // the alternative has a lower priority than this mod + + if (altOrigin.getPriority() > beforePrio) { + // the alternative has a higher priority and therefore is closer + // to this mod, use it + before = ToQString(altOrigin.getName()); + beforePrio = altOrigin.getPriority(); + } + } + + if (altOrigin.getPriority() > m_origin->getPriority()) { + // the alternative has a higher priority than this mod + + if (altOrigin.getPriority() < afterPrio) { + // the alternative has a lower priority and there is closer + // to this mod, use it + after = ToQString(altOrigin.getName()); + afterPrio = altOrigin.getPriority(); + } + } + } + } + + // the primary origin is never in the list of alternatives, so it has to + // be handled separately + // + // if 'after' is not empty, it means at least one alternative with a higher + // priority than this mod was found; if the user only wants to see the + // nearest mods, it's not worth checking for the primary origin because it + // will always have a higher priority than the alternatives (or it wouldn't + // be the primary) + if (after.isEmpty() || ui->conflictsAdvancedShowAll1->isChecked()) { + FilesOrigin &realOrigin = ds.getOriginByID(fileOrigin); + + // if no mods overwrite this file, the primary origin is the same as this + // mod, so ignore that + if (realOrigin.getID() != m_origin->getID()) { + if (!after.isEmpty()) { + after += ", "; + } + + after += ToQString(realOrigin.getName()); + } + } + } + + bool hasAlts = !before.isEmpty() || !after.isEmpty(); + + if (!ui->conflictsAdvancedShowNoConflict1->isChecked()) { + // if both before and after are empty, it means this file has no conflicts + // at all, only display it if the user wants it + if (!hasAlts) { + return nullptr; + } + } + + bool matched = m_filter.matches([&](auto&& what) { + return + before.contains(what, Qt::CaseInsensitive) || + relativeName.contains(what, Qt::CaseInsensitive) || + after.contains(what, Qt::CaseInsensitive); + }); + + if (!matched) { + return nullptr; + } + + return new ConflictItem( + {before, relativeName, after}, index, fileName, hasAlts, "", archive); +} diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h new file mode 100644 index 00000000..894f9dd4 --- /dev/null +++ b/src/modinfodialogconflicts.h @@ -0,0 +1,86 @@ +#ifndef MODINFODIALOGCONFLICTS_H +#define MODINFODIALOGCONFLICTS_H + +#include "modinfodialog.h" +#include "expanderwidget.h" + +class GeneralConflictsTab +{ +public: + GeneralConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& oc); + + void saveState(Settings& s); + void restoreState(const Settings& s); + + void rebuild(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); + void update(); + +private: + struct Expanders + { + ExpanderWidget overwrite, overwritten, nonconflict; + }; + + Ui::ModInfoDialog* ui; + OrganizerCore& m_core; + ModInfo::Ptr m_mod; + MOShared::FilesOrigin* m_origin; + Expanders m_expanders; + + QTreeWidgetItem* createOverwriteItem( + MOShared::FileEntry::Index index, bool archive, + const QString& fileName, const QString& relativeName, + const MOShared::FileEntry::AlternativesVector& alternatives); + + QTreeWidgetItem* createNoConflictItem( + MOShared::FileEntry::Index index, bool archive, + const QString& fileName, const QString& relativeName); + + QTreeWidgetItem* createOverwrittenItem( + MOShared::FileEntry::Index index, int fileOrigin, bool archive, + const QString& fileName, const QString& relativeName); + + void onOverwriteActivated(QTreeWidgetItem* item, int); +}; + + +class AdvancedConflictsTab +{ +public: + AdvancedConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& oc); + + void saveState(Settings& s); + void restoreState(const Settings& s); + + void rebuild(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); + void update(); + +private: + Ui::ModInfoDialog* ui; + OrganizerCore& m_core; + ModInfo::Ptr m_mod; + MOShared::FilesOrigin* m_origin; + FilterWidget m_filter; + + QTreeWidgetItem* createItem( + MOShared::FileEntry::Index index, int fileOrigin, bool archive, + const QString& fileName, const QString& relativeName, + const MOShared::FileEntry::AlternativesVector& alternatives); +}; + + +class ConflictsTab : public ModInfoDialogTab +{ +public: + ConflictsTab(Ui::ModInfoDialog* ui, OrganizerCore& oc); + + void saveState(Settings& s) override; + void restoreState(const Settings& s) override; + +private: + Ui::ModInfoDialog* ui; + GeneralConflictsTab m_general; + AdvancedConflictsTab m_advanced; +}; + +#endif // MODINFODIALOGCONFLICTS_H -- cgit v1.3.1 From a2e5f7d38214c9872a5c3a360065b5790bb29e2f Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 21 Jun 2019 21:43:16 -0400 Subject: split ModInfoDialogTab --- src/CMakeLists.txt | 3 +++ src/modinfodialog.cpp | 36 ------------------------------------ src/modinfodialog.h | 33 +-------------------------------- src/modinfodialogconflicts.cpp | 1 + src/modinfodialogconflicts.h | 5 ++++- src/modinfodialogesps.h | 2 +- src/modinfodialogimages.h | 2 +- src/modinfodialogtab.cpp | 36 ++++++++++++++++++++++++++++++++++++ src/modinfodialogtab.h | 42 ++++++++++++++++++++++++++++++++++++++++++ src/modinfodialogtextfiles.h | 2 +- 10 files changed, 90 insertions(+), 72 deletions(-) create mode 100644 src/modinfodialogtab.cpp create mode 100644 src/modinfodialogtab.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index eead6541..adaddb77 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -57,6 +57,7 @@ SET(organizer_SRCS modinfodialogconflicts.cpp modinfodialogesps.cpp modinfodialogimages.cpp + modinfodialogtab.cpp modinfodialogtextfiles.cpp modinfo.cpp modinfobackup.cpp @@ -161,6 +162,7 @@ SET(organizer_HDRS modinfodialogconflicts.h modinfodialogesps.h modinfodialogimages.h + modinfodialogtab.h modinfodialogtextfiles.h modinfo.h modinfobackup.h @@ -363,6 +365,7 @@ set(modinfo modinfodialogconflicts modinfodialogesps modinfodialogimages + modinfodialogtab modinfodialogtextfiles modinfoforeign modinfooverwrite diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index d52103b9..8231d366 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -66,42 +66,6 @@ using namespace MOShared; const int max_scan_for_context_menu = 50; -bool ModInfoDialogTab::canClose() -{ - return true; -} - -void ModInfoDialogTab::saveState(Settings&) -{ - // no-op -} - -void ModInfoDialogTab::restoreState(const Settings& s) -{ - // no-op -} - -void ModInfoDialogTab::setMod(ModInfo::Ptr, MOShared::FilesOrigin*) -{ - // no-op -} - -void ModInfoDialogTab::update() -{ - // no-op -} - -void ModInfoDialogTab::emitOriginModified(int originID) -{ - emit originModified(originID); -} - -void ModInfoDialogTab::emitModOpen(QString name) -{ - emit modOpen(name); -} - - class ModFileListWidget : public QListWidgetItem { friend bool operator<(const ModFileListWidget &LHS, const ModFileListWidget &RHS); public: diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 2a3ee2d8..8e7fed17 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -43,7 +43,6 @@ along with Mod Organizer. If not, see . #include #include - namespace Ui { class ModInfoDialog; } @@ -52,37 +51,7 @@ class QFileSystemModel; class QTreeView; class CategoryFactory; class TextEditor; - - -class ModInfoDialogTab : public QObject -{ - Q_OBJECT; - -public: - ModInfoDialogTab() = default; - ModInfoDialogTab(const ModInfoDialogTab&) = delete; - ModInfoDialogTab& operator=(const ModInfoDialogTab&) = delete; - ModInfoDialogTab(ModInfoDialogTab&&) = default; - ModInfoDialogTab& operator=(ModInfoDialogTab&&) = default; - virtual ~ModInfoDialogTab() = default; - - virtual void clear() = 0; - virtual bool feedFile(const QString& rootPath, const QString& filename) = 0; - virtual bool canClose(); - virtual void saveState(Settings& s); - virtual void restoreState(const Settings& s); - - virtual void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); - virtual void update(); - -signals: - void originModified(int originID); - void modOpen(QString name); - -protected: - void emitOriginModified(int originID); - void emitModOpen(QString name); -}; +class ModInfoDialogTab; class ElideLeftDelegate : public QStyledItemDelegate diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index 0107b8a1..1734f4f9 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -1,5 +1,6 @@ #include "modinfodialogconflicts.h" #include "ui_modinfodialog.h" +#include "modinfodialog.h" #include "utility.h" using namespace MOShared; diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h index 5843884a..4d4f279c 100644 --- a/src/modinfodialogconflicts.h +++ b/src/modinfodialogconflicts.h @@ -1,10 +1,13 @@ #ifndef MODINFODIALOGCONFLICTS_H #define MODINFODIALOGCONFLICTS_H -#include "modinfodialog.h" +#include "modinfodialogtab.h" #include "expanderwidget.h" +#include "filterwidget.h" +#include "directoryentry.h" class ConflictsTab; +class OrganizerCore; class GeneralConflictsTab : public QObject { diff --git a/src/modinfodialogesps.h b/src/modinfodialogesps.h index 8da70377..d71ad3ff 100644 --- a/src/modinfodialogesps.h +++ b/src/modinfodialogesps.h @@ -1,7 +1,7 @@ #ifndef MODINFODIALOGESPS_H #define MODINFODIALOGESPS_H -#include "modinfodialog.h" +#include "modinfodialogtab.h" class ESPItem; diff --git a/src/modinfodialogimages.h b/src/modinfodialogimages.h index a40904f7..708045c8 100644 --- a/src/modinfodialogimages.h +++ b/src/modinfodialogimages.h @@ -1,7 +1,7 @@ #ifndef MODINFODIALOGIMAGES_H #define MODINFODIALOGIMAGES_H -#include "modinfodialog.h" +#include "modinfodialogtab.h" class ScalableImage : public QWidget { diff --git a/src/modinfodialogtab.cpp b/src/modinfodialogtab.cpp new file mode 100644 index 00000000..b4734526 --- /dev/null +++ b/src/modinfodialogtab.cpp @@ -0,0 +1,36 @@ +#include "modinfodialogtab.h" + +bool ModInfoDialogTab::canClose() +{ + return true; +} + +void ModInfoDialogTab::saveState(Settings&) +{ + // no-op +} + +void ModInfoDialogTab::restoreState(const Settings& s) +{ + // no-op +} + +void ModInfoDialogTab::setMod(ModInfo::Ptr, MOShared::FilesOrigin*) +{ + // no-op +} + +void ModInfoDialogTab::update() +{ + // no-op +} + +void ModInfoDialogTab::emitOriginModified(int originID) +{ + emit originModified(originID); +} + +void ModInfoDialogTab::emitModOpen(QString name) +{ + emit modOpen(name); +} diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h new file mode 100644 index 00000000..93a495f4 --- /dev/null +++ b/src/modinfodialogtab.h @@ -0,0 +1,42 @@ +#ifndef MODINFODIALOGTAB_H +#define MODINFODIALOGTAB_H + +#include "modinfo.h" +#include + +namespace MOShared { class FilesOrigin; } +namespace Ui { class ModInfoDialog; } + +class Settings; + +class ModInfoDialogTab : public QObject +{ + Q_OBJECT; + +public: + ModInfoDialogTab() = default; + ModInfoDialogTab(const ModInfoDialogTab&) = delete; + ModInfoDialogTab& operator=(const ModInfoDialogTab&) = delete; + ModInfoDialogTab(ModInfoDialogTab&&) = default; + ModInfoDialogTab& operator=(ModInfoDialogTab&&) = default; + virtual ~ModInfoDialogTab() = default; + + virtual void clear() = 0; + virtual bool feedFile(const QString& rootPath, const QString& filename) = 0; + virtual bool canClose(); + virtual void saveState(Settings& s); + virtual void restoreState(const Settings& s); + + virtual void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); + virtual void update(); + +signals: + void originModified(int originID); + void modOpen(QString name); + +protected: + void emitOriginModified(int originID); + void emitModOpen(QString name); +}; + +#endif // MODINFODIALOGTAB_H diff --git a/src/modinfodialogtextfiles.h b/src/modinfodialogtextfiles.h index d30e20d7..11691d64 100644 --- a/src/modinfodialogtextfiles.h +++ b/src/modinfodialogtextfiles.h @@ -1,7 +1,7 @@ #ifndef MODINFODIALOGTEXTFILES_H #define MODINFODIALOGTEXTFILES_H -#include "modinfodialog.h" +#include "modinfodialogtab.h" #include #include -- cgit v1.3.1 From a72573b385a941adf7d662b8df5c8e35309fdd45 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 21 Jun 2019 22:42:14 -0400 Subject: split categories tab --- src/CMakeLists.txt | 3 + src/modinfodialog.cpp | 88 ++------------------------- src/modinfodialog.h | 7 --- src/modinfodialogcategories.cpp | 129 ++++++++++++++++++++++++++++++++++++++++ src/modinfodialogcategories.h | 29 +++++++++ src/modinfodialogconflicts.cpp | 5 -- src/modinfodialogconflicts.h | 1 - src/modinfodialogesps.cpp | 1 - src/modinfodialogtab.cpp | 6 ++ src/modinfodialogtab.h | 2 +- 10 files changed, 173 insertions(+), 98 deletions(-) create mode 100644 src/modinfodialogcategories.cpp create mode 100644 src/modinfodialogcategories.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index adaddb77..6ebfaddd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,7 @@ SET(organizer_SRCS modlist.cpp modidlineedit.cpp modinfodialog.cpp + modinfodialogcategories.cpp modinfodialogconflicts.cpp modinfodialogesps.cpp modinfodialogimages.cpp @@ -159,6 +160,7 @@ SET(organizer_HDRS modlist.h modidlineedit.h modinfodialog.h + modinfodialogcategories.h modinfodialogconflicts.h modinfodialogesps.h modinfodialogimages.h @@ -362,6 +364,7 @@ set(modinfo modinfo modinfobackup modinfodialog + modinfodialogcategories modinfodialogconflicts modinfodialogesps modinfodialogimages diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 8231d366..3263a511 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -42,6 +42,7 @@ along with Mod Organizer. If not, see . #include "modinfodialogimages.h" #include "modinfodialogesps.h" #include "modinfodialogconflicts.h" +#include "modinfodialogcategories.h" #include #include @@ -223,9 +224,7 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->tabWidget->setTabEnabled(TAB_IMAGES, false); ui->tabWidget->setTabEnabled(TAB_ESPS, false); ui->tabWidget->setTabEnabled(TAB_CONFLICTS, false); - //ui->tabWidget->setTabEnabled(TAB_CATEGORIES, false); - addCategories(CategoryFactory::instance(), modInfo->getCategories(), ui->categoriesTree->invisibleRootItem(), 0); - refreshPrimaryCategoriesBox(); + ui->tabWidget->setTabEnabled(TAB_CATEGORIES, true); ui->tabWidget->setTabEnabled(TAB_NEXUS, false); //ui->tabWidget->setTabEnabled(TAB_NOTES, false); ui->tabWidget->setTabEnabled(TAB_FILETREE, false); @@ -249,12 +248,8 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->tabWidget->setTabEnabled(TAB_CONFLICTS, true); initFiletree(modInfo); - addCategories(CategoryFactory::instance(), modInfo->getCategories(), ui->categoriesTree->invisibleRootItem(), 0); - refreshPrimaryCategoriesBox(); } - - ui->endorseBtn->setVisible(Settings::instance().endorsementIntegration()); ui->endorseBtn->setEnabled((m_ModInfo->endorsedState() == ModInfo::ENDORSED_FALSE) || (m_ModInfo->endorsedState() == ModInfo::ENDORSED_NEVER)); @@ -279,12 +274,13 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ModInfoDialog::~ModInfoDialog() { m_ModInfo->setComments(ui->commentsEdit->text()); + //Avoid saving html stump if notes field is empty. if (ui->notesEdit->toPlainText().isEmpty()) m_ModInfo->setNotes(ui->notesEdit->toPlainText()); else m_ModInfo->setNotes(ui->notesEdit->toHtml()); - saveCategories(ui->categoriesTree->invisibleRootItem()); + delete ui->descriptionView->page(); delete ui->descriptionView; delete ui; @@ -301,6 +297,7 @@ std::vector> ModInfoDialog::createTabs() v.push_back(std::make_unique(this, ui)); v.push_back(std::make_unique( this, ui, *m_OrganizerCore, *m_PluginContainer)); + v.push_back(std::make_unique(this, ui)); return v; } @@ -437,38 +434,6 @@ void ModInfoDialog::refreshFiles() } } -void ModInfoDialog::addCategories(const CategoryFactory &factory, const std::set &enabledCategories, QTreeWidgetItem *root, int rootLevel) -{ - for (int i = 0; i < static_cast(factory.numCategories()); ++i) { - if (factory.getParentID(i) != rootLevel) { - continue; - } - int categoryID = factory.getCategoryID(i); - QTreeWidgetItem *newItem - = new QTreeWidgetItem(QStringList(factory.getCategoryName(i))); - newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable); - newItem->setCheckState(0, enabledCategories.find(categoryID) - != enabledCategories.end() - ? Qt::Checked - : Qt::Unchecked); - newItem->setData(0, Qt::UserRole, categoryID); - if (factory.hasChildren(i)) { - addCategories(factory, enabledCategories, newItem, categoryID); - } - root->addChild(newItem); - } -} - - -void ModInfoDialog::saveCategories(QTreeWidgetItem *currentNode) -{ - for (int i = 0; i < currentNode->childCount(); ++i) { - QTreeWidgetItem *childNode = currentNode->child(i); - m_ModInfo->setCategory(childNode->data(0, Qt::UserRole).toInt(), childNode->checkState(0)); - saveCategories(childNode); - } -} - void ModInfoDialog::on_closeButton_clicked() { @@ -1011,49 +976,6 @@ void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) } -void ModInfoDialog::on_categoriesTree_itemChanged(QTreeWidgetItem *item, int) -{ - QTreeWidgetItem *parent = item->parent(); - while ((parent != nullptr) && ((parent->flags() & Qt::ItemIsUserCheckable) != 0) && (parent->checkState(0) == Qt::Unchecked)) { - parent->setCheckState(0, Qt::Checked); - parent = parent->parent(); - } - refreshPrimaryCategoriesBox(); -} - - -void ModInfoDialog::addCheckedCategories(QTreeWidgetItem *tree) -{ - for (int i = 0; i < tree->childCount(); ++i) { - QTreeWidgetItem *child = tree->child(i); - if (child->checkState(0) == Qt::Checked) { - ui->primaryCategoryBox->addItem(child->text(0), child->data(0, Qt::UserRole)); - addCheckedCategories(child); - } - } -} - - -void ModInfoDialog::refreshPrimaryCategoriesBox() -{ - ui->primaryCategoryBox->clear(); - int primaryCategory = m_ModInfo->getPrimaryCategory(); - addCheckedCategories(ui->categoriesTree->invisibleRootItem()); - for (int i = 0; i < ui->primaryCategoryBox->count(); ++i) { - if (ui->primaryCategoryBox->itemData(i).toInt() == primaryCategory) { - ui->primaryCategoryBox->setCurrentIndex(i); - break; - } - } -} - -void ModInfoDialog::on_primaryCategoryBox_currentIndexChanged(int index) -{ - if (index != -1) { - m_ModInfo->setPrimaryCategory(ui->primaryCategoryBox->itemData(index).toInt()); - } -} - void ModInfoDialog::on_refreshButton_clicked() { if (m_ModInfo->getNexusID() > 0) { diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 8e7fed17..384aafce 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -157,8 +157,6 @@ private: void refreshLists(); - void addCategories(const CategoryFactory &factory, const std::set &enabledCategories, QTreeWidgetItem *root, int rootLevel); - void updateVersionColor(); void refreshNexusData(int modID); @@ -166,9 +164,6 @@ private: QString getFileCategory(int categoryID); bool recursiveDelete(const QModelIndex &index); void deleteFile(const QModelIndex &index); - void saveCategories(QTreeWidgetItem *currentNode); - void addCheckedCategories(QTreeWidgetItem *tree); - void refreshPrimaryCategoriesBox(); int tabIndex(const QString &tabId); @@ -194,8 +189,6 @@ private slots: void on_versionEdit_editingFinished(); void on_customUrlLineEdit_editingFinished(); void on_tabWidget_currentChanged(int index); - void on_primaryCategoryBox_currentIndexChanged(int index); - void on_categoriesTree_itemChanged(QTreeWidgetItem *item, int column); void on_fileTree_customContextMenuRequested(const QPoint &pos); void on_refreshButton_clicked(); diff --git a/src/modinfodialogcategories.cpp b/src/modinfodialogcategories.cpp new file mode 100644 index 00000000..7df13aea --- /dev/null +++ b/src/modinfodialogcategories.cpp @@ -0,0 +1,129 @@ +#include "modinfodialogcategories.h" +#include "ui_modinfodialog.h" +#include "categories.h" +#include "modinfo.h" + +CategoriesTab::CategoriesTab(QWidget*, Ui::ModInfoDialog* ui) + : ui(ui) +{ + connect( + ui->categoriesTree, &QTreeWidget::itemChanged, + [&](auto* item, int col){ onCategoryChanged(item, col); }); + + connect( + ui->primaryCategoryBox, + static_cast(&QComboBox::currentIndexChanged), + [&](int index){ onPrimaryChanged(index); }); +} + +void CategoriesTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) +{ + m_mod = mod; +} + +void CategoriesTab::clear() +{ + ui->categoriesTree->clear(); + ui->primaryCategoryBox->clear(); +} + +void CategoriesTab::update() +{ + clear(); + + add( + CategoryFactory::instance(), m_mod->getCategories(), + ui->categoriesTree->invisibleRootItem(), 0); + + updatePrimary(); +} + +void CategoriesTab::add( + const CategoryFactory &factory, const std::set& enabledCategories, + QTreeWidgetItem* root, int rootLevel) +{ + for (int i=0; i(factory.numCategories()); ++i) { + if (factory.getParentID(i) != rootLevel) { + continue; + } + + int categoryID = factory.getCategoryID(i); + + QTreeWidgetItem *newItem + = new QTreeWidgetItem(QStringList(factory.getCategoryName(i))); + + newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable); + + newItem->setCheckState(0, enabledCategories.find(categoryID) + != enabledCategories.end() + ? Qt::Checked + : Qt::Unchecked); + + newItem->setData(0, Qt::UserRole, categoryID); + + if (factory.hasChildren(i)) { + add(factory, enabledCategories, newItem, categoryID); + } + + root->addChild(newItem); + } +} + +void CategoriesTab::updatePrimary() +{ + ui->primaryCategoryBox->clear(); + + int primaryCategory = m_mod->getPrimaryCategory(); + + addChecked(ui->categoriesTree->invisibleRootItem()); + + for (int i = 0; i < ui->primaryCategoryBox->count(); ++i) { + if (ui->primaryCategoryBox->itemData(i).toInt() == primaryCategory) { + ui->primaryCategoryBox->setCurrentIndex(i); + break; + } + } +} + +void CategoriesTab::addChecked(QTreeWidgetItem* tree) +{ + for (int i = 0; i < tree->childCount(); ++i) { + QTreeWidgetItem *child = tree->child(i); + if (child->checkState(0) == Qt::Checked) { + ui->primaryCategoryBox->addItem(child->text(0), child->data(0, Qt::UserRole)); + addChecked(child); + } + } +} + +void CategoriesTab::save(QTreeWidgetItem* currentNode) +{ + for (int i = 0; i < currentNode->childCount(); ++i) { + QTreeWidgetItem *childNode = currentNode->child(i); + + m_mod->setCategory( + childNode->data(0, Qt::UserRole).toInt(), childNode->checkState(0)); + + save(childNode); + } +} + +void CategoriesTab::onCategoryChanged(QTreeWidgetItem* item, int) +{ + QTreeWidgetItem *parent = item->parent(); + + while ((parent != nullptr) && ((parent->flags() & Qt::ItemIsUserCheckable) != 0) && (parent->checkState(0) == Qt::Unchecked)) { + parent->setCheckState(0, Qt::Checked); + parent = parent->parent(); + } + + updatePrimary(); + save(ui->categoriesTree->invisibleRootItem()); +} + +void CategoriesTab::onPrimaryChanged(int index) +{ + if (index != -1) { + m_mod->setPrimaryCategory(ui->primaryCategoryBox->itemData(index).toInt()); + } +} diff --git a/src/modinfodialogcategories.h b/src/modinfodialogcategories.h new file mode 100644 index 00000000..c0644c7f --- /dev/null +++ b/src/modinfodialogcategories.h @@ -0,0 +1,29 @@ +#include "modinfodialogtab.h" + +class CategoryFactory; + +class CategoriesTab : public ModInfoDialogTab +{ +public: + CategoriesTab(QWidget* parent, Ui::ModInfoDialog* ui); + + void clear() override; + void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) override; + void update() override; + +private: + Ui::ModInfoDialog* ui; + ModInfo::Ptr m_mod; + + void add( + const CategoryFactory& factory, const std::set& enabledCategories, + QTreeWidgetItem* root, int rootLevel); + + void updatePrimary(); + void addChecked(QTreeWidgetItem* tree); + + void save(QTreeWidgetItem* currentNode); + + void onCategoryChanged(QTreeWidgetItem* item, int col); + void onPrimaryChanged(int index); +}; diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index 1734f4f9..d2dca492 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -151,11 +151,6 @@ void ConflictsTab::clear() m_advanced.clear(); } -bool ConflictsTab::feedFile(const QString&, const QString&) -{ - return false; -} - void ConflictsTab::saveState(Settings& s) { s.directInterface().setValue( diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h index 4d4f279c..212a196d 100644 --- a/src/modinfodialogconflicts.h +++ b/src/modinfodialogconflicts.h @@ -108,7 +108,6 @@ public: void update() override; void clear() override; - bool feedFile(const QString& rootPath, const QString& filename) override; void saveState(Settings& s) override; void restoreState(const Settings& s) override; diff --git a/src/modinfodialogesps.cpp b/src/modinfodialogesps.cpp index 6c87b10d..cb3dcc84 100644 --- a/src/modinfodialogesps.cpp +++ b/src/modinfodialogesps.cpp @@ -4,7 +4,6 @@ using MOBase::reportError; - class ESP { public: diff --git a/src/modinfodialogtab.cpp b/src/modinfodialogtab.cpp index b4734526..6dcff0ac 100644 --- a/src/modinfodialogtab.cpp +++ b/src/modinfodialogtab.cpp @@ -1,5 +1,11 @@ #include "modinfodialogtab.h" +bool ModInfoDialogTab::feedFile(const QString&, const QString&) +{ + // no-op + return false; +} + bool ModInfoDialogTab::canClose() { return true; diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h index 93a495f4..ead29d10 100644 --- a/src/modinfodialogtab.h +++ b/src/modinfodialogtab.h @@ -22,7 +22,7 @@ public: virtual ~ModInfoDialogTab() = default; virtual void clear() = 0; - virtual bool feedFile(const QString& rootPath, const QString& filename) = 0; + virtual bool feedFile(const QString& rootPath, const QString& filename); virtual bool canClose(); virtual void saveState(Settings& s); virtual void restoreState(const Settings& s); -- cgit v1.3.1 From 8d1c121f648f2f6a8e0a5e2ad76cd245e318290d Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sun, 23 Jun 2019 02:24:34 -0400 Subject: split nexus tab added OrganizerCore::loggedInAction() to execute a function only when logged in, replaces a bunch of copy/pasted stuff in mainwindow moved common variables in ModInfoDialogTab moved DescriptionPage to modinfodialognexus.h, renamed to NexusTabWebpage, deleted now empty descriptionpage.h --- src/CMakeLists.txt | 5 +- src/descriptionpage.h | 28 ----- src/mainwindow.cpp | 167 ++++++------------------ src/modinfodialog.cpp | 233 +++------------------------------- src/modinfodialog.h | 29 ----- src/modinfodialog.ui | 222 +++++++++++++++----------------- src/modinfodialogcategories.cpp | 43 +++---- src/modinfodialogcategories.h | 8 +- src/modinfodialogconflicts.cpp | 91 ++++++-------- src/modinfodialogconflicts.h | 17 +-- src/modinfodialogesps.cpp | 8 +- src/modinfodialogesps.h | 7 +- src/modinfodialogimages.cpp | 6 +- src/modinfodialogimages.h | 5 +- src/modinfodialognexus.cpp | 273 ++++++++++++++++++++++++++++++++++++++++ src/modinfodialognexus.h | 67 ++++++++++ src/modinfodialogtab.cpp | 39 +++++- src/modinfodialogtab.h | 25 +++- src/modinfodialogtextfiles.cpp | 26 ++-- src/modinfodialogtextfiles.h | 12 +- src/modinforegular.cpp | 20 ++- src/modinforegular.h | 1 + src/organizercore.cpp | 15 +++ src/organizercore.h | 1 + 24 files changed, 690 insertions(+), 658 deletions(-) delete mode 100644 src/descriptionpage.h create mode 100644 src/modinfodialognexus.cpp create mode 100644 src/modinfodialognexus.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6ebfaddd..7dc39fd9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -58,6 +58,7 @@ SET(organizer_SRCS modinfodialogconflicts.cpp modinfodialogesps.cpp modinfodialogimages.cpp + modinfodialognexus.cpp modinfodialogtab.cpp modinfodialogtextfiles.cpp modinfo.cpp @@ -164,6 +165,7 @@ SET(organizer_HDRS modinfodialogconflicts.h modinfodialogesps.h modinfodialogimages.h + modinfodialognexus.h modinfodialogtab.h modinfodialogtextfiles.h modinfo.h @@ -223,7 +225,6 @@ SET(organizer_HDRS instancemanager.h usvfsconnector.h eventfilter.h - descriptionpage.h moshortcut.h listdialog.h lcdnumber.h @@ -368,6 +369,7 @@ set(modinfo modinfodialogconflicts modinfodialogesps modinfodialogimages + modinfodialognexus modinfodialogtab modinfodialogtextfiles modinfoforeign @@ -424,7 +426,6 @@ set(utilities ) set(widgets - descriptionpage expanderwidget genericicondelegate filerenamer diff --git a/src/descriptionpage.h b/src/descriptionpage.h deleted file mode 100644 index f6158ee0..00000000 --- a/src/descriptionpage.h +++ /dev/null @@ -1,28 +0,0 @@ -#include - -#ifndef DESCRIPTIONPAGE_H -#define DESCRIPTIONPAGE_H - -class DescriptionPage : public QWebEnginePage -{ - Q_OBJECT - -public: - DescriptionPage(QObject* parent = 0) : QWebEnginePage(parent){} - - bool acceptNavigationRequest(const QUrl & url, QWebEnginePage::NavigationType type, bool isMainFrame) - { - if (type == QWebEnginePage::NavigationTypeLinkClicked) - { - emit linkClicked(url); - return false; - } - return true; - } - -signals: - void linkClicked(const QUrl&); - -}; - -#endif //DESCRIPTIONPAGE_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index da7c721d..8eb0838e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3073,66 +3073,34 @@ void MainWindow::backupMod_clicked() void MainWindow::resumeDownload(int downloadIndex) { - if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->validated()) { + m_OrganizerCore.loggedInAction(this, [this, downloadIndex] { m_OrganizerCore.downloadManager()->resumeDownload(downloadIndex); - } else { - QString apiKey; - if (m_OrganizerCore.settings().getNexusApiKey(apiKey)) { - m_OrganizerCore.doAfterLogin([this, downloadIndex] () { - this->resumeDownload(downloadIndex); - }); - NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); - } else { - MessageDialog::showMessage(tr("You need to be logged in with Nexus to resume a download"), this); - } - } + }); } void MainWindow::endorseMod(ModInfo::Ptr mod) { - if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->validated()) { + m_OrganizerCore.loggedInAction(this, [this, mod] { mod->endorse(true); - } else { - QString apiKey; - if (m_OrganizerCore.settings().getNexusApiKey(apiKey)) { - m_OrganizerCore.doAfterLogin([&]() { this->endorseMod(mod); }); - NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); - } else { - MessageDialog::showMessage(tr("You need to be logged in with Nexus to endorse"), this); - } - } + }); } void MainWindow::endorse_clicked() { QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection() && selection->selectedRows().count() > 1) { - if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->validated()) { + + m_OrganizerCore.loggedInAction(this, [this] { + QItemSelectionModel *selection = ui->modList->selectionModel(); + if (selection->hasSelection() && selection->selectedRows().count() > 1) { MessageDialog::showMessage(tr("Endorsing multiple mods will take a while. Please wait..."), this); - for (QModelIndex idx : selection->selectedRows()) { - ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->endorse(true); - } } - else { - QString apiKey; - if (m_OrganizerCore.settings().getNexusApiKey(apiKey)) { - MessageDialog::showMessage(tr("Endorsing multiple mods will take a while. Please wait..."), this); - for (QModelIndex idx : selection->selectedRows()) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); - m_OrganizerCore.doAfterLogin([&]() { this->endorseMod(modInfo); }); - } - NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); - } else { - MessageDialog::showMessage(tr("You need to be logged in with Nexus to endorse"), this); - return; - } + + for (QModelIndex idx : selection->selectedRows()) { + ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->endorse(true); } - } - else { - endorseMod(ModInfo::getByIndex(m_ContextRow)); - } + }); } void MainWindow::dontendorse_clicked() @@ -3151,114 +3119,53 @@ void MainWindow::dontendorse_clicked() void MainWindow::unendorseMod(ModInfo::Ptr mod) { - if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->validated()) { - ModInfo::getByIndex(m_ContextRow)->endorse(false); - } else { - QString apiKey; - if (m_OrganizerCore.settings().getNexusApiKey(apiKey)) { - m_OrganizerCore.doAfterLogin([&]() { this->unendorseMod(mod); }); - NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); - } else { - MessageDialog::showMessage(tr("You need to be logged in with Nexus to endorse"), this); - } - } + m_OrganizerCore.loggedInAction(this, [mod] { + mod->endorse(false); + }); } void MainWindow::unendorse_clicked() { - QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection() && selection->selectedRows().count() > 1) { - if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->validated()) { + m_OrganizerCore.loggedInAction(this, [this] { + QItemSelectionModel *selection = ui->modList->selectionModel(); + if (selection->hasSelection() && selection->selectedRows().count() > 1) { MessageDialog::showMessage(tr("Unendorsing multiple mods will take a while. Please wait..."), this); - for (QModelIndex idx : selection->selectedRows()) { - ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->endorse(false); - } - } else { - QString apiKey; - if (m_OrganizerCore.settings().getNexusApiKey(apiKey)) { - MessageDialog::showMessage(tr("Unendorsing multiple mods will take a while. Please wait..."), this); - for (QModelIndex idx : selection->selectedRows()) { - ModInfo::Ptr modInfo = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); - m_OrganizerCore.doAfterLogin([&]() { this->unendorseMod(modInfo); }); - } - NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); - } else { - MessageDialog::showMessage(tr("You need to be logged in with Nexus to endorse"), this); - return; - } } - } - else { - unendorseMod(ModInfo::getByIndex(m_ContextRow)); - } + + for (QModelIndex idx : selection->selectedRows()) { + ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->endorse(false); + } + }); } void MainWindow::trackMod(ModInfo::Ptr mod, bool doTrack) { - if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->validated()) { - ModInfo::getByIndex(m_ContextRow)->track(doTrack); - } else { - QString apiKey; - if (m_OrganizerCore.settings().getNexusApiKey(apiKey)) { - m_OrganizerCore.doAfterLogin([&]() { this->trackMod(mod, doTrack); }); - NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); - } else { - MessageDialog::showMessage(tr("You need to be logged in with Nexus to track"), this); - } - } + m_OrganizerCore.loggedInAction(this, [mod, doTrack] { + mod->track(doTrack); + }); } void MainWindow::track_clicked() { - QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection() && selection->selectedRows().count() > 1) { - if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->validated()) { - for (auto idx : selection->selectedRows()) { - ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->track(true); - } - } else { - QString apiKey; - if (m_OrganizerCore.settings().getNexusApiKey(apiKey)) { - for (auto idx : selection->selectedRows()) { - auto modInfo = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); - m_OrganizerCore.doAfterLogin([&]() { this->trackMod(modInfo, true); }); - } - NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); - } else { - MessageDialog::showMessage(tr("You need to be logged in with Nexus to track"), this); - } + m_OrganizerCore.loggedInAction(this, [this] { + QItemSelectionModel *selection = ui->modList->selectionModel(); + for (auto idx : selection->selectedRows()) { + ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->track(true); } - } else { - trackMod(ModInfo::getByIndex(m_ContextRow), true); - } + }); } void MainWindow::untrack_clicked() { - QItemSelectionModel *selection = ui->modList->selectionModel(); - if (selection->hasSelection() && selection->selectedRows().count() > 1) { - if (NexusInterface::instance(&m_PluginContainer)->getAccessManager()->validated()) { - for (auto idx : selection->selectedRows()) { - ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->track(false); - } - } else { - QString apiKey; - if (m_OrganizerCore.settings().getNexusApiKey(apiKey)) { - for (auto idx : selection->selectedRows()) { - auto modInfo = ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt()); - m_OrganizerCore.doAfterLogin([&]() { this->trackMod(modInfo, false); }); - } - NexusInterface::instance(&m_PluginContainer)->getAccessManager()->apiCheck(apiKey); - } else { - MessageDialog::showMessage(tr("You need to be logged in with Nexus to track"), this); - } + m_OrganizerCore.loggedInAction(this, [this] { + QItemSelectionModel *selection = ui->modList->selectionModel(); + for (auto idx : selection->selectedRows()) { + ModInfo::getByIndex(idx.data(Qt::UserRole + 1).toInt())->track(false); } - } else { - trackMod(ModInfo::getByIndex(m_ContextRow), false); - } + }); } void MainWindow::validationFailed(const QString &error) @@ -3316,13 +3223,11 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, } else { modInfo->saveMeta(); ModInfoDialog dialog(modInfo, m_OrganizerCore.directoryStructure(), modInfo->hasFlag(ModInfo::FLAG_FOREIGN), &m_OrganizerCore, &m_PluginContainer, this); - connect(&dialog, SIGNAL(linkActivated(QString)), this, SLOT(linkClicked(QString))); connect(&dialog, SIGNAL(downloadRequest(QString)), &m_OrganizerCore, SLOT(downloadRequestedNXM(QString))); connect(&dialog, SIGNAL(modOpen(QString, int)), this, SLOT(displayModInformation(QString, int)), Qt::QueuedConnection); connect(&dialog, SIGNAL(modOpenNext(int)), this, SLOT(modOpenNext(int)), Qt::QueuedConnection); connect(&dialog, SIGNAL(modOpenPrev(int)), this, SLOT(modOpenPrev(int)), Qt::QueuedConnection); connect(&dialog, SIGNAL(originModified(int)), this, SLOT(originModified(int))); - connect(&dialog, SIGNAL(endorseMod(ModInfo::Ptr)), this, SLOT(endorseMod(ModInfo::Ptr))); //Open the tab first if we want to use the standard indexes of the tabs. if (tab != -1) { diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 3263a511..30110d14 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -43,6 +43,7 @@ along with Mod Organizer. If not, see . #include "modinfodialogesps.h" #include "modinfodialogconflicts.h" #include "modinfodialogcategories.h" +#include "modinfodialognexus.h" #include #include @@ -145,7 +146,6 @@ FileRenamer::RenameResults unhideFile(FileRenamer& renamer, const QString &oldNa 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_RequestStarted(false), m_NewFolderAction(nullptr), m_OpenAction(nullptr), m_PreviewAction(nullptr), m_RenameAction(nullptr), m_DeleteAction(nullptr), m_HideAction(nullptr), m_UnhideAction(nullptr), m_Directory(directory), m_Origin(nullptr), @@ -173,38 +173,9 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo m_RootPath = modInfo->absolutePath(); - QString metaFileName = m_RootPath.mid(0).append("/meta.ini"); - m_Settings = new QSettings(metaFileName, QSettings::IniFormat); - - QLineEdit *modIDEdit = findChild("modIDEdit"); - ui->modIDEdit->setValidator(new QIntValidator(modIDEdit)); - ui->modIDEdit->setText(QString("%1").arg(modInfo->getNexusID())); - - connect(ui->modIDEdit, SIGNAL(linkClicked(QString)), this, SLOT(linkClicked(QString))); - - QString gameName = modInfo->getGameName(); - ui->sourceGameEdit->addItem(organizerCore->managedGame()->gameName(), organizerCore->managedGame()->gameShortName()); - if (organizerCore->managedGame()->validShortNames().size() == 0) { - ui->sourceGameEdit->setDisabled(true); - } else { - for (auto game : pluginContainer->plugins()) { - for (QString gameName : organizerCore->managedGame()->validShortNames()) { - if (game->gameShortName().compare(gameName, Qt::CaseInsensitive) == 0) { - ui->sourceGameEdit->addItem(game->gameName(), game->gameShortName()); - break; - } - } - } - } - ui->sourceGameEdit->setCurrentIndex(ui->sourceGameEdit->findData(gameName)); - ui->commentsEdit->setText(modInfo->comments()); ui->notesEdit->setText(modInfo->notes()); - ui->descriptionView->setPage(new DescriptionPage()); - - connect(m_ModInfo.data(), SIGNAL(modDetailsUpdated(bool)), this, SLOT(modDetailsUpdated(bool))); - connect(ui->descriptionView->page(), SIGNAL(linkClicked(QUrl)), this, SLOT(linkClicked(QUrl))); //TODO: No easy way to delegate links //ui->descriptionView->page()->acceptNavigationRequest(QWebEnginePage::DelegateAllLinks); @@ -246,14 +217,12 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->tabWidget->setTabEnabled(TAB_IMAGES, true); ui->tabWidget->setTabEnabled(TAB_ESPS, true); ui->tabWidget->setTabEnabled(TAB_CONFLICTS, true); + ui->tabWidget->setTabEnabled(TAB_CATEGORIES, true); + ui->tabWidget->setTabEnabled(TAB_NEXUS, true); initFiletree(modInfo); } - ui->endorseBtn->setVisible(Settings::instance().endorsementIntegration()); - ui->endorseBtn->setEnabled((m_ModInfo->endorsedState() == ModInfo::ENDORSED_FALSE) || - (m_ModInfo->endorsedState() == ModInfo::ENDORSED_NEVER)); - // activate first enabled tab for (int i = 0; i < ui->tabWidget->count(); ++i) { if (ui->tabWidget->isTabEnabled(i)) { @@ -262,10 +231,6 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo } } - if (ui->tabWidget->currentIndex() == TAB_NEXUS) { - activateNexusTab(); - } - for (auto& tab : m_tabs) { tab->setMod(m_ModInfo, m_Origin); } @@ -281,27 +246,28 @@ ModInfoDialog::~ModInfoDialog() else m_ModInfo->setNotes(ui->notesEdit->toHtml()); - delete ui->descriptionView->page(); - delete ui->descriptionView; delete ui; - delete m_Settings; } -std::vector> ModInfoDialog::createTabs() +template +std::vector> createTabsImpl( + OrganizerCore& oc, PluginContainer& plugin, + ModInfoDialog* self, Ui::ModInfoDialog* ui) { std::vector> v; - - v.push_back(std::make_unique(this, ui)); - v.push_back(std::make_unique(this, ui)); - v.push_back(std::make_unique(this, ui)); - v.push_back(std::make_unique(this, ui)); - v.push_back(std::make_unique( - this, ui, *m_OrganizerCore, *m_PluginContainer)); - v.push_back(std::make_unique(this, ui)); + (v.push_back(std::make_unique(oc, plugin, self, ui)), ...); return v; } +std::vector> ModInfoDialog::createTabs() +{ + return createTabsImpl< + TextFilesTab, IniFilesTab, ImagesTab, ESPsTab, + ConflictsTab, CategoriesTab, NexusTab>( + *m_OrganizerCore, *m_PluginContainer, this, ui); +} + int ModInfoDialog::exec() { refreshLists(); @@ -434,7 +400,6 @@ void ModInfoDialog::refreshFiles() } } - void ModInfoDialog::on_closeButton_clicked() { for (auto& tab : m_tabs) { @@ -446,19 +411,6 @@ void ModInfoDialog::on_closeButton_clicked() close(); } - - -QString ModInfoDialog::getModVersion() const -{ - return m_Settings->value("version", "").toString(); -} - - -const int ModInfoDialog::getModID() const -{ - return m_Settings->value("modid", 0).toInt(); -} - void ModInfoDialog::openTab(int tab) { QTabWidget *tabWidget = findChild("tabWidget"); @@ -467,40 +419,6 @@ void ModInfoDialog::openTab(int tab) } } -void ModInfoDialog::on_visitNexusLabel_linkActivated(const QString &link) -{ - emit linkActivated(link); -} - -void ModInfoDialog::linkClicked(const QUrl &url) -{ - //Ideally we'd ask the mod for the game and the web service then pass the game - //and URL to the web service - if (NexusInterface::instance(m_PluginContainer)->isURLGameRelated(url)) { - emit linkActivated(url.toString()); - } else { - shell::OpenLink(url); - } -} - -void ModInfoDialog::linkClicked(QString url) -{ - emit linkActivated(url); -} - - -void ModInfoDialog::refreshNexusData(int modID) -{ - if ((!m_RequestStarted) && (modID > 0)) { - m_RequestStarted = true; - - m_ModInfo->updateNXMInfo(); - - MessageDialog::showMessage(tr("Info requested, please wait"), this); - } -} - - QString ModInfoDialog::getFileCategory(int categoryID) { switch (categoryID) { @@ -514,111 +432,8 @@ QString ModInfoDialog::getFileCategory(int categoryID) } } - -void ModInfoDialog::updateVersionColor() -{ -// QPalette versionColor; - if (m_ModInfo->getVersion() != m_ModInfo->getNewestVersion()) { - ui->versionEdit->setStyleSheet("color: red"); -// versionColor.setColor(QPalette::Text, Qt::red); - ui->versionEdit->setToolTip(tr("Current Version: %1").arg(m_ModInfo->getNewestVersion().canonicalString())); - } else { - ui->versionEdit->setStyleSheet("color: green"); -// versionColor.setColor(QPalette::Text, Qt::green); - ui->versionEdit->setToolTip(tr("No update available")); - } -// ui->versionEdit->setPalette(versionColor); -} - - -void ModInfoDialog::modDetailsUpdated(bool success) -{ - QString nexusDescription = m_ModInfo->getNexusDescription(); - QString descriptionAsHTML = "" - "" - "%1" - ""; - - if (!nexusDescription.isEmpty()) { - descriptionAsHTML = descriptionAsHTML.arg(BBCode::convertToHTML(nexusDescription)); - } else { - descriptionAsHTML = descriptionAsHTML.arg(tr("

Uh oh!

Sorry, there is no description available for this mod. :(

")); - } - - ui->descriptionView->page()->setHtml(descriptionAsHTML); - - updateVersionColor(); -} - - -void ModInfoDialog::activateNexusTab() -{ - QLineEdit *modIDEdit = findChild("modIDEdit"); - int modID = modIDEdit->text().toInt(); - if (modID > 0) { - QString nexusLink = NexusInterface::instance(m_PluginContainer)->getModURL(modID, m_ModInfo->getGameName()); - QLabel *visitNexusLabel = findChild("visitNexusLabel"); - visitNexusLabel->setText(tr("Visit on Nexus").arg(nexusLink)); - visitNexusLabel->setToolTip(nexusLink); - m_ModInfo->setURL(nexusLink); - - if (m_ModInfo->getNexusDescription().isEmpty() || QDateTime::currentDateTimeUtc() >= m_ModInfo->getLastNexusQuery().addDays(1)) { - refreshNexusData(modID); - } else { - modDetailsUpdated(true); - } - } else - modDetailsUpdated(true); - QLineEdit *versionEdit = findChild("versionEdit"); - QString currentVersion = m_Settings->value("version", "???").toString(); - versionEdit->setText(currentVersion); - ui->customUrlLineEdit->setText(m_ModInfo->getURL()); -} - - void ModInfoDialog::on_tabWidget_currentChanged(int index) { - if (index == TAB_NEXUS || m_RealTabPos[index] == TAB_NEXUS) { - activateNexusTab(); - } -} - - -void ModInfoDialog::on_modIDEdit_editingFinished() -{ - int oldID = m_Settings->value("modid", 0).toInt(); - int modID = ui->modIDEdit->text().toInt(); - if (oldID != modID){ - m_ModInfo->setNexusID(modID); - - ui->descriptionView->page()->setHtml(""); - if (modID != 0) { - m_RequestStarted = false; - refreshNexusData(modID); - } - } -} - -void ModInfoDialog::on_sourceGameEdit_currentIndexChanged(int) -{ - for (auto game : m_PluginContainer->plugins()) { - if (game->gameName() == ui->sourceGameEdit->currentText()) { - m_ModInfo->setGameName(game->gameShortName()); - return; - } - } -} - -void ModInfoDialog::on_versionEdit_editingFinished() -{ - VersionInfo version(ui->versionEdit->text()); - m_ModInfo->setVersion(version); - updateVersionColor(); -} - -void ModInfoDialog::on_customUrlLineEdit_editingFinished() -{ - m_ModInfo->setURL(ui->customUrlLineEdit->text()); } bool ModInfoDialog::recursiveDelete(const QModelIndex &index) @@ -975,22 +790,6 @@ void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) menu.exec(ui->fileTree->viewport()->mapToGlobal(pos)); } - -void ModInfoDialog::on_refreshButton_clicked() -{ - if (m_ModInfo->getNexusID() > 0) { - QLineEdit *modIDEdit = findChild("modIDEdit"); - int modID = modIDEdit->text().toInt(); - refreshNexusData(modID); - } else - qInfo("Mod has no valid Nexus ID, info can't be updated."); -} - -void ModInfoDialog::on_endorseBtn_clicked() -{ - emit endorseMod(m_ModInfo); -} - void ModInfoDialog::on_nextButton_clicked() { int currentTab = ui->tabWidget->currentIndex(); diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 384aafce..7b96d21a 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -138,18 +138,11 @@ public: void restoreState(const Settings& s); signals: - - void linkActivated(const QString &link); void downloadRequest(const QString &link); void modOpen(const QString &modName, int tab); void modOpenNext(int tab=-1); void modOpenPrev(int tab=-1); void originModified(int originID); - void endorseMod(ModInfo::Ptr nexusID); - -public slots: - - void modDetailsUpdated(bool success); private: @@ -157,10 +150,6 @@ private: void refreshLists(); - void updateVersionColor(); - - void refreshNexusData(int modID); - void activateNexusTab(); QString getFileCategory(int categoryID); bool recursiveDelete(const QModelIndex &index); void deleteFile(const QModelIndex &index); @@ -168,9 +157,6 @@ private: int tabIndex(const QString &tabId); private slots: - void linkClicked(const QUrl &url); - void linkClicked(QString url); - void delete_activated(); void createDirectoryTriggered(); @@ -183,20 +169,10 @@ private slots: void on_openInExplorerButton_clicked(); void on_closeButton_clicked(); - void on_visitNexusLabel_linkActivated(const QString &link); - void on_modIDEdit_editingFinished(); - void on_sourceGameEdit_currentIndexChanged(int); - void on_versionEdit_editingFinished(); - void on_customUrlLineEdit_editingFinished(); void on_tabWidget_currentChanged(int index); void on_fileTree_customContextMenuRequested(const QPoint &pos); - void on_refreshButton_clicked(); - - void on_endorseBtn_clicked(); - void on_nextButton_clicked(); - void on_prevButton_clicked(); private: @@ -217,11 +193,6 @@ private: QTreeView *m_FileTree; QModelIndexList m_FileSelection; - QSettings *m_Settings; - - std::set m_RequestIDs; - bool m_RequestStarted; - QAction *m_NewFolderAction; QAction *m_OpenAction; QAction *m_PreviewAction; diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index c8ffd470..29a7400f 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -829,7 +829,7 @@ text-align: left; - + true @@ -853,13 +853,13 @@ text-align: left; - +
- + :/MO/gui/resources/internet-web-browser.png:/MO/gui/resources/internet-web-browser.png @@ -870,6 +870,60 @@ text-align: left; + + + + + 0 + 0 + + + + Refresh + + + Refresh all information from Nexus. + + + Refresh + + + + :/MO/gui/refresh:/MO/gui/refresh + + + Qt::ToolButtonTextBesideIcon + + + + + + + Open in Browser + + + + :/MO/gui/resources/internet-web-browser.png:/MO/gui/resources/internet-web-browser.png + + + Qt::ToolButtonTextBesideIcon + + + + + + + Endorse + + + + :/MO/gui/icon_favorite:/MO/gui/icon_favorite + + + Qt::ToolButtonTextBesideIcon + + + @@ -878,7 +932,7 @@ text-align: left; - + Mod ID for this mod on Nexus. @@ -891,22 +945,6 @@ p, li { white-space: pre-wrap; } - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 10 - 20 - - - - @@ -915,7 +953,7 @@ p, li { white-space: pre-wrap; } - + Source game for this mod. @@ -925,20 +963,7 @@ p, li { white-space: pre-wrap; } - - - Qt::Horizontal - - - - 40 - 20 - - - - - - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> @@ -952,7 +977,7 @@ p, li { white-space: pre-wrap; } - + 150 @@ -967,102 +992,61 @@ p, li { white-space: pre-wrap; } - - - - - - 0 - 0 - - - - Refresh - - - Refresh all information from Nexus. - - - - - - - :/MO/gui/refresh:/MO/gui/refresh - - - - - - - Description - - - - - - - - - - 0 - 0 - - - - - 0 - 200 - + + + QFrame::StyledPanel - - - about:blank - + + QFrame::Sunken + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 200 + + + + + about:blank + + + + + - - - - - - - 1 - 0 - - - - - - - false - - - - - - - Endorse - - - - :/MO/gui/icon_favorite:/MO/gui/icon_favorite - - - - - - Web page URL (only used if invalid Nexus ID) : + URL - + diff --git a/src/modinfodialogcategories.cpp b/src/modinfodialogcategories.cpp index 7df13aea..69c7c6b5 100644 --- a/src/modinfodialogcategories.cpp +++ b/src/modinfodialogcategories.cpp @@ -3,28 +3,25 @@ #include "categories.h" #include "modinfo.h" -CategoriesTab::CategoriesTab(QWidget*, Ui::ModInfoDialog* ui) - : ui(ui) +CategoriesTab::CategoriesTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui) + : ModInfoDialogTab(oc, plugin, parent, ui) { connect( - ui->categoriesTree, &QTreeWidget::itemChanged, + ui->categories, &QTreeWidget::itemChanged, [&](auto* item, int col){ onCategoryChanged(item, col); }); connect( - ui->primaryCategoryBox, + ui->primaryCategories, static_cast(&QComboBox::currentIndexChanged), [&](int index){ onPrimaryChanged(index); }); } -void CategoriesTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) -{ - m_mod = mod; -} - void CategoriesTab::clear() { - ui->categoriesTree->clear(); - ui->primaryCategoryBox->clear(); + ui->categories->clear(); + ui->primaryCategories->clear(); } void CategoriesTab::update() @@ -32,8 +29,8 @@ void CategoriesTab::update() clear(); add( - CategoryFactory::instance(), m_mod->getCategories(), - ui->categoriesTree->invisibleRootItem(), 0); + CategoryFactory::instance(), mod()->getCategories(), + ui->categories->invisibleRootItem(), 0); updatePrimary(); } @@ -71,15 +68,15 @@ void CategoriesTab::add( void CategoriesTab::updatePrimary() { - ui->primaryCategoryBox->clear(); + ui->primaryCategories->clear(); - int primaryCategory = m_mod->getPrimaryCategory(); + int primaryCategory = mod()->getPrimaryCategory(); - addChecked(ui->categoriesTree->invisibleRootItem()); + addChecked(ui->categories->invisibleRootItem()); - for (int i = 0; i < ui->primaryCategoryBox->count(); ++i) { - if (ui->primaryCategoryBox->itemData(i).toInt() == primaryCategory) { - ui->primaryCategoryBox->setCurrentIndex(i); + for (int i = 0; i < ui->primaryCategories->count(); ++i) { + if (ui->primaryCategories->itemData(i).toInt() == primaryCategory) { + ui->primaryCategories->setCurrentIndex(i); break; } } @@ -90,7 +87,7 @@ void CategoriesTab::addChecked(QTreeWidgetItem* tree) for (int i = 0; i < tree->childCount(); ++i) { QTreeWidgetItem *child = tree->child(i); if (child->checkState(0) == Qt::Checked) { - ui->primaryCategoryBox->addItem(child->text(0), child->data(0, Qt::UserRole)); + ui->primaryCategories->addItem(child->text(0), child->data(0, Qt::UserRole)); addChecked(child); } } @@ -101,7 +98,7 @@ void CategoriesTab::save(QTreeWidgetItem* currentNode) for (int i = 0; i < currentNode->childCount(); ++i) { QTreeWidgetItem *childNode = currentNode->child(i); - m_mod->setCategory( + mod()->setCategory( childNode->data(0, Qt::UserRole).toInt(), childNode->checkState(0)); save(childNode); @@ -118,12 +115,12 @@ void CategoriesTab::onCategoryChanged(QTreeWidgetItem* item, int) } updatePrimary(); - save(ui->categoriesTree->invisibleRootItem()); + save(ui->categories->invisibleRootItem()); } void CategoriesTab::onPrimaryChanged(int index) { if (index != -1) { - m_mod->setPrimaryCategory(ui->primaryCategoryBox->itemData(index).toInt()); + mod()->setPrimaryCategory(ui->primaryCategories->itemData(index).toInt()); } } diff --git a/src/modinfodialogcategories.h b/src/modinfodialogcategories.h index c0644c7f..76426a5d 100644 --- a/src/modinfodialogcategories.h +++ b/src/modinfodialogcategories.h @@ -5,16 +5,14 @@ class CategoryFactory; class CategoriesTab : public ModInfoDialogTab { public: - CategoriesTab(QWidget* parent, Ui::ModInfoDialog* ui); + CategoriesTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui); void clear() override; - void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) override; void update() override; private: - Ui::ModInfoDialog* ui; - ModInfo::Ptr m_mod; - void add( const CategoryFactory& factory, const std::set& enabledCategories, QTreeWidgetItem* root, int rootLevel); diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index d2dca492..4176ac3e 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -116,10 +116,10 @@ public: ConflictsTab::ConflictsTab( - QWidget* parent, Ui::ModInfoDialog* ui, - OrganizerCore& oc, PluginContainer& plugin) : - m_parent(parent), ui(ui), m_core(oc), m_plugin(plugin), - m_origin(nullptr), m_general(this, ui, oc), m_advanced(this, ui, oc) + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui) : + ModInfoDialogTab(oc, plugin, parent, ui), + m_general(this, ui, oc), m_advanced(this, ui, oc) { connect( &m_general, &GeneralConflictsTab::modOpen, @@ -130,15 +130,6 @@ ConflictsTab::ConflictsTab( [&](const QString& name){ emitModOpen(name); }); } -void ConflictsTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) -{ - m_mod = mod; - m_origin = origin; - - m_general.setMod(mod, origin); - m_advanced.setMod(mod, origin); -} - void ConflictsTab::update() { m_general.update(); @@ -186,7 +177,7 @@ void ConflictsTab::changeItemsVisibility( flags |= FileRenamer::MULTIPLE; } - FileRenamer renamer(m_parent, flags); + FileRenamer renamer(parentWidget(), flags); for (const auto* item : items) { if (stop) { @@ -242,8 +233,8 @@ void ConflictsTab::changeItemsVisibility( if (changed) { qDebug().nospace() << "triggering refresh"; - if (m_origin) { - emit originModified(m_origin->getID()); + if (origin()) { + emit originModified(origin()->getID()); } update(); @@ -256,7 +247,7 @@ void ConflictsTab::openItems(const QList& items) // in case this changes for (auto* item : items) { if (auto* ci=dynamic_cast(item)) { - m_core.executeFileVirtualized(m_parent, ci->fileName()); + core().executeFileVirtualized(parentWidget(), ci->fileName()); } } } @@ -267,7 +258,7 @@ void ConflictsTab::previewItems(const QList& items) // in case this changes for (auto* item : items) { if (auto* ci=dynamic_cast(item)) { - m_core.previewFileWithAlternatives(m_parent, ci->fileName()); + core().previewFileWithAlternatives(parentWidget(), ci->fileName()); } } } @@ -355,7 +346,7 @@ ConflictsTab::Actions ConflictsTab::createMenuActions( enableHide = ci->canHide(); enableUnhide = ci->canUnhide(); enableOpen = ci->canOpen(); - enablePreview = ci->canPreview(m_plugin); + enablePreview = ci->canPreview(plugin()); enableGoto = ci->hasAlts(); } else { @@ -394,21 +385,21 @@ ConflictsTab::Actions ConflictsTab::createMenuActions( Actions actions; - actions.hide = new QAction(tr("Hide"), m_parent); + actions.hide = new QAction(tr("Hide"), parentWidget()); actions.hide->setEnabled(enableHide); // note that it is possible for hidden files to appear if they override other // hidden files from another mod - actions.unhide = new QAction(tr("Unhide"), m_parent); + actions.unhide = new QAction(tr("Unhide"), parentWidget()); actions.unhide->setEnabled(enableUnhide); - actions.open = new QAction(tr("Open/Execute"), m_parent); + actions.open = new QAction(tr("Open/Execute"), parentWidget()); actions.open->setEnabled(enableOpen); - actions.preview = new QAction(tr("Preview"), m_parent); + actions.preview = new QAction(tr("Preview"), parentWidget()); actions.preview->setEnabled(enablePreview); - actions.gotoMenu = new QMenu(tr("Go to..."), m_parent); + actions.gotoMenu = new QMenu(tr("Go to..."), parentWidget()); actions.gotoMenu->setEnabled(enableGoto); if (enableGoto) { @@ -421,7 +412,7 @@ ConflictsTab::Actions ConflictsTab::createMenuActions( std::vector ConflictsTab::createGotoActions( const QList& selection) { - if (!m_origin || selection.size() != 1) { + if (!origin() || selection.size() != 1) { return {}; } @@ -430,26 +421,26 @@ std::vector ConflictsTab::createGotoActions( return {}; } - auto file = m_origin->findFile(item->fileIndex()); + auto file = origin()->findFile(item->fileIndex()); if (!file) { return {}; } std::vector mods; - const auto& ds = *m_core.directoryStructure(); + const auto& ds = *core().directoryStructure(); // add all alternatives for (const auto& alt : file->getAlternatives()) { const auto& o = ds.getOriginByID(alt.first); - if (o.getID() != m_origin->getID()) { + if (o.getID() != origin()->getID()) { mods.push_back(ToQString(o.getName())); } } // add the real origin if different from this mod const FilesOrigin& realOrigin = ds.getOriginByID(file->getOrigin()); - if (realOrigin.getID() != m_origin->getID()) { + if (realOrigin.getID() != origin()->getID()) { mods.push_back(ToQString(realOrigin.getName())); } @@ -460,7 +451,7 @@ std::vector ConflictsTab::createGotoActions( std::vector actions; for (const auto& name : mods) { - actions.push_back(new QAction(name, m_parent)); + actions.push_back(new QAction(name, parentWidget())); } return actions; @@ -469,7 +460,7 @@ std::vector ConflictsTab::createGotoActions( GeneralConflictsTab::GeneralConflictsTab( ConflictsTab* tab, Ui::ModInfoDialog* ui, OrganizerCore& oc) - : m_tab(tab), ui(ui), m_core(oc), m_origin(nullptr) + : m_tab(tab), ui(ui), m_core(oc) { m_expanders.overwrite.set(ui->overwriteExpander, ui->overwriteTree, true); m_expanders.overwritten.set(ui->overwrittenExpander, ui->overwrittenTree, true); @@ -560,12 +551,6 @@ void GeneralConflictsTab::restoreState(const Settings& s) .value("mod_info_conflicts_general_overwritten").toByteArray()); } -void GeneralConflictsTab::setMod(ModInfo::Ptr mod, FilesOrigin* origin) -{ - m_mod = mod; - m_origin = origin; -} - void GeneralConflictsTab::update() { clear(); @@ -574,10 +559,10 @@ void GeneralConflictsTab::update() int numOverwrite = 0; int numOverwritten = 0; - if (m_origin != nullptr) { - const auto rootPath = m_mod->absolutePath(); + if (m_tab->origin() != nullptr) { + const auto rootPath = m_tab->mod()->absolutePath(); - for (const auto& file : m_origin->getFiles()) { + for (const auto& file : m_tab->origin()->getFiles()) { const QString relativeName = QDir::fromNativeSeparators(ToQString(file->getRelativePath())); const QString fileName = relativeName.mid(0).prepend(rootPath); @@ -585,7 +570,7 @@ void GeneralConflictsTab::update() const int fileOrigin = file->getOrigin(archive); const auto& alternatives = file->getAlternatives(); - if (fileOrigin == m_origin->getID()) { + if (fileOrigin == m_tab->origin()->getID()) { if (!alternatives.empty()) { ui->overwriteTree->addTopLevelItem(createOverwriteItem( file->getIndex(), archive, fileName, relativeName, alternatives)); @@ -684,7 +669,7 @@ void GeneralConflictsTab::onOverwrittenActivated(QTreeWidgetItem *item, int) AdvancedConflictsTab::AdvancedConflictsTab( ConflictsTab* tab, Ui::ModInfoDialog* ui, OrganizerCore& oc) - : m_tab(tab), ui(ui), m_core(oc), m_origin(nullptr) + : m_tab(tab), ui(ui), m_core(oc) { // left-elide the overwrites column so that the nearest are visible ui->conflictsAdvancedList->setItemDelegateForColumn( @@ -760,20 +745,14 @@ void AdvancedConflictsTab::restoreState(const Settings& s) } } -void AdvancedConflictsTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) -{ - m_mod = mod; - m_origin = origin; -} - void AdvancedConflictsTab::update() { clear(); - if (m_origin != nullptr) { - const auto rootPath = m_mod->absolutePath(); + if (m_tab->origin() != nullptr) { + const auto rootPath = m_tab->mod()->absolutePath(); - for (const auto& file : m_origin->getFiles()) { + for (const auto& file : m_tab->origin()->getFiles()) { const QString relativeName = QDir::fromNativeSeparators(ToQString(file->getRelativePath())); const QString fileName = relativeName.mid(0).prepend(rootPath); @@ -813,14 +792,14 @@ QTreeWidgetItem* AdvancedConflictsTab::createItem( // fills 'before' and 'after' with all the alternatives that come // before and after this mod in terms of priority - if (altOrigin.getPriority() < m_origin->getPriority()) { + if (altOrigin.getPriority() < m_tab->origin()->getPriority()) { // add all the mods having a lower priority than this one if (!before.isEmpty()) { before += ", "; } before += ToQString(altOrigin.getName()); - } else if (altOrigin.getPriority() > m_origin->getPriority()) { + } else if (altOrigin.getPriority() > m_tab->origin()->getPriority()) { // add all the mods having a higher priority than this one if (!after.isEmpty()) { after += ", "; @@ -832,7 +811,7 @@ QTreeWidgetItem* AdvancedConflictsTab::createItem( // keep track of the nearest mods that come before and after this one // in terms of priority - if (altOrigin.getPriority() < m_origin->getPriority()) { + if (altOrigin.getPriority() < m_tab->origin()->getPriority()) { // the alternative has a lower priority than this mod if (altOrigin.getPriority() > beforePrio) { @@ -843,7 +822,7 @@ QTreeWidgetItem* AdvancedConflictsTab::createItem( } } - if (altOrigin.getPriority() > m_origin->getPriority()) { + if (altOrigin.getPriority() > m_tab->origin()->getPriority()) { // the alternative has a higher priority than this mod if (altOrigin.getPriority() < afterPrio) { @@ -869,7 +848,7 @@ QTreeWidgetItem* AdvancedConflictsTab::createItem( // if no mods overwrite this file, the primary origin is the same as this // mod, so ignore that - if (realOrigin.getID() != m_origin->getID()) { + if (realOrigin.getID() != m_tab->origin()->getID()) { if (!after.isEmpty()) { after += ", "; } diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h index 212a196d..20362575 100644 --- a/src/modinfodialogconflicts.h +++ b/src/modinfodialogconflicts.h @@ -21,7 +21,6 @@ public: void saveState(Settings& s); void restoreState(const Settings& s); - void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); void update(); signals: @@ -36,8 +35,6 @@ private: ConflictsTab* m_tab; Ui::ModInfoDialog* ui; OrganizerCore& m_core; - ModInfo::Ptr m_mod; - MOShared::FilesOrigin* m_origin; Expanders m_expanders; QTreeWidgetItem* createOverwriteItem( @@ -74,7 +71,6 @@ public: void saveState(Settings& s); void restoreState(const Settings& s); - void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); void update(); signals: @@ -84,8 +80,6 @@ private: ConflictsTab* m_tab; Ui::ModInfoDialog* ui; OrganizerCore& m_core; - ModInfo::Ptr m_mod; - MOShared::FilesOrigin* m_origin; FilterWidget m_filter; QTreeWidgetItem* createItem( @@ -101,10 +95,9 @@ class ConflictsTab : public ModInfoDialogTab public: ConflictsTab( - QWidget* parent, Ui::ModInfoDialog* ui, - OrganizerCore& oc, PluginContainer& plugin); + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui); - void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) override; void update() override; void clear() override; @@ -136,12 +129,6 @@ private: } }; - QWidget* m_parent; - Ui::ModInfoDialog* ui; - OrganizerCore& m_core; - PluginContainer& m_plugin; - ModInfo::Ptr m_mod; - MOShared::FilesOrigin* m_origin; GeneralConflictsTab m_general; AdvancedConflictsTab m_advanced; diff --git a/src/modinfodialogesps.cpp b/src/modinfodialogesps.cpp index cb3dcc84..ea7eb3b0 100644 --- a/src/modinfodialogesps.cpp +++ b/src/modinfodialogesps.cpp @@ -122,8 +122,10 @@ private: }; -ESPsTab::ESPsTab(QWidget* parent, Ui::ModInfoDialog* ui) - : m_parent(parent), ui(ui) +ESPsTab::ESPsTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui) + : ModInfoDialogTab(oc, plugin, parent, ui) { QObject::connect( ui->activateESP, &QToolButton::clicked, [&]{ onActivate(); }); @@ -187,7 +189,7 @@ void ESPsTab::onActivate() bool okClicked = false; newName = QInputDialog::getText( - m_parent, + parentWidget(), QObject::tr("File Exists"), QObject::tr("A file with that name exists, please enter a new one"), QLineEdit::Normal, file.fileName(), &okClicked); diff --git a/src/modinfodialogesps.h b/src/modinfodialogesps.h index d71ad3ff..e1a7a4f7 100644 --- a/src/modinfodialogesps.h +++ b/src/modinfodialogesps.h @@ -10,15 +10,14 @@ class ESPsTab : public ModInfoDialogTab Q_OBJECT; public: - ESPsTab(QWidget* parent, Ui::ModInfoDialog* ui); + ESPsTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui); void clear() override; bool feedFile(const QString& rootPath, const QString& fullPath) override; private: - QWidget* m_parent; - Ui::ModInfoDialog* ui; - void onActivate(); void onDeactivate(); diff --git a/src/modinfodialogimages.cpp b/src/modinfodialogimages.cpp index 752c8d0c..1c7dcc1f 100644 --- a/src/modinfodialogimages.cpp +++ b/src/modinfodialogimages.cpp @@ -80,8 +80,10 @@ void ScalableImage::mousePressEvent(QMouseEvent* e) } -ImagesTab::ImagesTab(QWidget*, Ui::ModInfoDialog* ui) - : ui(ui), m_image(new ScalableImage) +ImagesTab::ImagesTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui) + : ModInfoDialogTab(oc, plugin, parent, ui), m_image(new ScalableImage) { ui->imagesImage->layout()->addWidget(m_image); ui->imagesThumbnails->setLayout(new QVBoxLayout); diff --git a/src/modinfodialogimages.h b/src/modinfodialogimages.h index 708045c8..7853935a 100644 --- a/src/modinfodialogimages.h +++ b/src/modinfodialogimages.h @@ -34,13 +34,14 @@ class ImagesTab : public ModInfoDialogTab Q_OBJECT; public: - ImagesTab(QWidget* parent, Ui::ModInfoDialog* ui); + ImagesTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui); void clear() override; bool feedFile(const QString& rootPath, const QString& fullPath) override; private: - Ui::ModInfoDialog* ui; ScalableImage* m_image; void add(const QString& fullPath); diff --git a/src/modinfodialognexus.cpp b/src/modinfodialognexus.cpp new file mode 100644 index 00000000..753d43de --- /dev/null +++ b/src/modinfodialognexus.cpp @@ -0,0 +1,273 @@ +#include "modinfodialognexus.h" +#include "ui_modinfodialog.h" +#include "settings.h" +#include "organizercore.h" +#include "iplugingame.h" +#include "bbcode.h" +#include +#include + +NexusTab::NexusTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui) + : ModInfoDialogTab(oc, plugin, parent, ui), m_requestStarted(false) +{ + ui->modID->setValidator(new QIntValidator(ui->modID)); + ui->endorse->setVisible(core().settings().endorsementIntegration()); + + connect(ui->modID, &QLineEdit::editingFinished, [&]{ onModIDChanged(); }); + connect(ui->version, &QLineEdit::editingFinished, [&]{ onVersionChanged(); }); + connect(ui->openInBrowser, &QToolButton::clicked, [&]{ onOpenLink(); }); + connect(ui->url, &QLineEdit::editingFinished, [&]{ onUrlChanged(); }); + connect(ui->endorse, &QToolButton::clicked, [&]{ onEndorse(); }); + connect(ui->refresh, &QToolButton::clicked, [&]{ updateWebpage(); }); + + connect( + ui->sourceGame, + static_cast(&QComboBox::currentIndexChanged), + [&]{ onSourceGameChanged(); }); +} + +NexusTab::~NexusTab() +{ + cleanup(); +} + +void NexusTab::cleanup() +{ + if (m_modConnection) { + disconnect(m_modConnection); + m_modConnection = {}; + } +} + +void NexusTab::clear() +{ + ui->modID->clear(); + ui->sourceGame->clear(); + ui->version->clear(); + ui->browser->setPage(new NexusTabWebpage(ui->browser)); + ui->url->clear(); +} + +void NexusTab::update() +{ + clear(); + + ui->modID->setText(QString("%1").arg(mod()->getNexusID())); + + QString gameName = mod()->getGameName(); + ui->sourceGame->addItem( + core().managedGame()->gameName(), + core().managedGame()->gameShortName()); + + if (core().managedGame()->validShortNames().size() == 0) { + ui->sourceGame->setDisabled(true); + } else { + for (auto game : plugin().plugins()) { + for (QString gameName : core().managedGame()->validShortNames()) { + if (game->gameShortName().compare(gameName, Qt::CaseInsensitive) == 0) { + ui->sourceGame->addItem(game->gameName(), game->gameShortName()); + break; + } + } + } + } + + ui->sourceGame->setCurrentIndex(ui->sourceGame->findData(gameName)); + + auto* page = new NexusTabWebpage(ui->browser); + ui->browser->setPage(page); + + connect( + page, &NexusTabWebpage::linkClicked, + [&](const QUrl& url){ MOBase::shell::OpenLink(url); }); + + ui->endorse->setEnabled( + (mod()->endorsedState() == ModInfo::ENDORSED_FALSE) || + (mod()->endorsedState() == ModInfo::ENDORSED_NEVER)); + + updateWebpage(); +} + +void NexusTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) +{ + cleanup(); + + ModInfoDialogTab::setMod(mod, origin); + + m_modConnection = connect( + mod.data(), &ModInfo::modDetailsUpdated, [&]{ onModChanged(); }); +} + +void NexusTab::updateVersionColor() +{ + if (mod()->getVersion() != mod()->getNewestVersion()) { + ui->version->setStyleSheet("color: red"); + ui->version->setToolTip(tr("Current Version: %1").arg( + mod()->getNewestVersion().canonicalString())); + } else { + ui->version->setStyleSheet("color: green"); + ui->version->setToolTip(tr("No update available")); + } +} + +void NexusTab::updateWebpage() +{ + const int modID = mod()->getNexusID(); + + if (modID > 0) { + const QString nexusLink = NexusInterface::instance(&plugin()) + ->getModURL(modID, mod()->getGameName()); + + ui->openInBrowser->setToolTip(nexusLink); + mod()->setURL(nexusLink); + refreshData(modID); + } else { + onModChanged(); + } + + ui->version->setText(mod()->getVersion().displayString()); + ui->url->setText(mod()->getURL()); +} + +void NexusTab::onModChanged() +{ + m_requestStarted = false; + + const QString nexusDescription = mod()->getNexusDescription(); + + QString descriptionAsHTML = R"( + + + + + %1 +)"; + + if (nexusDescription.isEmpty()) { + descriptionAsHTML = descriptionAsHTML.arg(tr( + "
" + "

Uh oh!

" + "

Sorry, there is no description available for this mod. :(

" + "
")); + + } else { + descriptionAsHTML = descriptionAsHTML.arg( + BBCode::convertToHTML(nexusDescription)); + } + + ui->browser->page()->setHtml(descriptionAsHTML); + updateVersionColor(); +} + +void NexusTab::onModIDChanged() +{ + const int oldID = mod()->getNexusID(); + const int newID = ui->modID->text().toInt(); + + if (oldID != newID){ + mod()->setNexusID(newID); + mod()->setLastNexusQuery(QDateTime::fromSecsSinceEpoch(0)); + + ui->browser->page()->setHtml(""); + + if (newID != 0) { + refreshData(newID); + } + } +} + +void NexusTab::onSourceGameChanged() +{ + for (auto game : plugin().plugins()) { + if (game->gameName() == ui->sourceGame->currentText()) { + mod()->setGameName(game->gameShortName()); + mod()->setLastNexusQuery(QDateTime::fromSecsSinceEpoch(0)); + refreshData(mod()->getNexusID()); + return; + } + } +} + +void NexusTab::onVersionChanged() +{ + MOBase::VersionInfo version(ui->version->text()); + mod()->setVersion(version); + updateVersionColor(); +} + +void NexusTab::onUrlChanged() +{ + mod()->setURL(ui->url->text()); + mod()->setLastNexusQuery(QDateTime::fromSecsSinceEpoch(0)); +} + +void NexusTab::onOpenLink() +{ + const int modID = mod()->getNexusID(); + + if (modID > 0) { + const QString nexusLink = NexusInterface::instance(&plugin()) + ->getModURL(modID, mod()->getGameName()); + + MOBase::shell::OpenLink(QUrl(nexusLink)); + } +} + +void NexusTab::onRefreshBrowser() +{ + const auto modID = mod()->getNexusID(); + + if (modID > 0) { + refreshData(modID); + } else + qInfo("Mod has no valid Nexus ID, info can't be updated."); +} + +void NexusTab::onEndorse() +{ + core().loggedInAction(parentWidget(), [m=mod()]{ m->endorse(true); }); +} + +void NexusTab::refreshData(int modID) +{ + if (tryRefreshData(modID)) { + m_requestStarted = true; + } else { + onModChanged(); + } + + //MessageDialog::showMessage(tr("Info requested, please wait"), this); +} + +bool NexusTab::tryRefreshData(int modID) +{ + if (modID <= 0) { + qDebug() << "NexusTab: can't refresh, no mod id"; + return false; + } + + if (m_requestStarted) { + qDebug() << "NexusTab: a refresh request is already running"; + return false; + } + + if (!mod()->updateNXMInfo()) { + qDebug() << "NexusTab: nexus description does not need an update"; + return false; + } + + return true; +} diff --git a/src/modinfodialognexus.h b/src/modinfodialognexus.h new file mode 100644 index 00000000..2e328c6d --- /dev/null +++ b/src/modinfodialognexus.h @@ -0,0 +1,67 @@ +#ifndef MODINFODIALOGNEXUS_H +#define MODINFODIALOGNEXUS_H + +#include "modinfodialogtab.h" + +class NexusTabWebpage : public QWebEnginePage +{ + Q_OBJECT + +public: + NexusTabWebpage(QObject* parent = 0) + : QWebEnginePage(parent) + { + } + + bool acceptNavigationRequest( + const QUrl & url, QWebEnginePage::NavigationType type, bool) override + { + if (type == QWebEnginePage::NavigationTypeLinkClicked) + { + emit linkClicked(url); + return false; + } + + return true; + } + +signals: + void linkClicked(const QUrl&); +}; + + +class NexusTab : public ModInfoDialogTab +{ +public: + NexusTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui); + + ~NexusTab(); + + void clear() override; + void update() override; + void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) override; + +private: + QMetaObject::Connection m_modConnection; + bool m_requestStarted; + + void cleanup(); + void updateVersionColor(); + void updateWebpage(); + + void refreshData(int modID); + bool tryRefreshData(int modID); + + void onModChanged(); + void onOpenLink(); + void onModIDChanged(); + void onSourceGameChanged(); + void onVersionChanged(); + void onRefreshBrowser(); + void onEndorse(); + void onUrlChanged(); +}; + +#endif // MODINFODIALOGNEXUS_H diff --git a/src/modinfodialogtab.cpp b/src/modinfodialogtab.cpp index 6dcff0ac..ae0de5e8 100644 --- a/src/modinfodialogtab.cpp +++ b/src/modinfodialogtab.cpp @@ -1,5 +1,12 @@ #include "modinfodialogtab.h" +ModInfoDialogTab::ModInfoDialogTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui) + : ui(ui), m_core(oc), m_plugin(plugin), m_parent(parent), m_origin(nullptr) +{ +} + bool ModInfoDialogTab::feedFile(const QString&, const QString&) { // no-op @@ -21,14 +28,40 @@ void ModInfoDialogTab::restoreState(const Settings& s) // no-op } -void ModInfoDialogTab::setMod(ModInfo::Ptr, MOShared::FilesOrigin*) +void ModInfoDialogTab::update() { // no-op } -void ModInfoDialogTab::update() +void ModInfoDialogTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) { - // no-op + m_mod = mod; + m_origin = origin; +} + +ModInfo::Ptr ModInfoDialogTab::mod() const +{ + return m_mod; +} + +MOShared::FilesOrigin* ModInfoDialogTab::origin() const +{ + return m_origin; +} + +OrganizerCore& ModInfoDialogTab::core() +{ + return m_core; +} + +PluginContainer& ModInfoDialogTab::plugin() +{ + return m_plugin; +} + +QWidget* ModInfoDialogTab::parentWidget() +{ + return m_parent; } void ModInfoDialogTab::emitOriginModified(int originID) diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h index ead29d10..dd851b31 100644 --- a/src/modinfodialogtab.h +++ b/src/modinfodialogtab.h @@ -8,13 +8,13 @@ namespace MOShared { class FilesOrigin; } namespace Ui { class ModInfoDialog; } class Settings; +class OrganizerCore; class ModInfoDialogTab : public QObject { Q_OBJECT; public: - ModInfoDialogTab() = default; ModInfoDialogTab(const ModInfoDialogTab&) = delete; ModInfoDialogTab& operator=(const ModInfoDialogTab&) = delete; ModInfoDialogTab(ModInfoDialogTab&&) = default; @@ -22,21 +22,42 @@ public: virtual ~ModInfoDialogTab() = default; virtual void clear() = 0; + virtual void update(); virtual bool feedFile(const QString& rootPath, const QString& filename); virtual bool canClose(); virtual void saveState(Settings& s); virtual void restoreState(const Settings& s); virtual void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); - virtual void update(); + + ModInfo::Ptr mod() const; + MOShared::FilesOrigin* origin() const; signals: void originModified(int originID); void modOpen(QString name); protected: + Ui::ModInfoDialog* ui; + + ModInfoDialogTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui); + + OrganizerCore& core(); + PluginContainer& plugin(); + + QWidget* parentWidget(); + void emitOriginModified(int originID); void emitModOpen(QString name); + +private: + OrganizerCore& m_core; + PluginContainer& m_plugin; + QWidget* m_parent; + ModInfo::Ptr m_mod; + MOShared::FilesOrigin* m_origin; }; #endif // MODINFODIALOGTAB_H diff --git a/src/modinfodialogtextfiles.cpp b/src/modinfodialogtextfiles.cpp index 0dd6f06e..f48557b0 100644 --- a/src/modinfodialogtextfiles.cpp +++ b/src/modinfodialogtextfiles.cpp @@ -22,8 +22,10 @@ private: GenericFilesTab::GenericFilesTab( - QWidget* parent, QListWidget* list, QSplitter* sp, TextEditor* e) - : m_parent(parent), m_list(list), m_editor(e) + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui, + QListWidget* list, QSplitter* sp, TextEditor* e) + : ModInfoDialogTab(oc, plugin, parent, ui), m_list(list), m_editor(e) { m_editor->setupToolbar(); @@ -49,7 +51,7 @@ bool GenericFilesTab::canClose() } const int res = QMessageBox::question( - m_parent, + parentWidget(), QObject::tr("Save changes?"), QObject::tr("Save changes to \"%1\"?").arg(m_editor->filename()), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); @@ -109,9 +111,12 @@ void GenericFilesTab::select(FileListItem* item) } -TextFilesTab::TextFilesTab(QWidget* parent, Ui::ModInfoDialog* ui) - : GenericFilesTab( - parent, ui->textFileList, ui->tabTextSplitter, ui->textFileEditor) +TextFilesTab::TextFilesTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui) + : GenericFilesTab( + oc, plugin, parent, ui, + ui->textFileList, ui->tabTextSplitter, ui->textFileEditor) { } @@ -130,9 +135,12 @@ bool TextFilesTab::wantsFile(const QString& rootPath, const QString& fullPath) c return false; } -IniFilesTab::IniFilesTab(QWidget* parent, Ui::ModInfoDialog* ui) - : GenericFilesTab( - parent, ui->iniFileList, ui->tabIniSplitter, ui->iniFileEditor) +IniFilesTab::IniFilesTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui) + : GenericFilesTab( + oc, plugin, parent, ui, + ui->iniFileList, ui->tabIniSplitter, ui->iniFileEditor) { } diff --git a/src/modinfodialogtextfiles.h b/src/modinfodialogtextfiles.h index 11691d64..0dc5ec89 100644 --- a/src/modinfodialogtextfiles.h +++ b/src/modinfodialogtextfiles.h @@ -18,12 +18,12 @@ public: bool feedFile(const QString& rootPath, const QString& fullPath) override; protected: - QWidget* m_parent; QListWidget* m_list; TextEditor* m_editor; GenericFilesTab( - QWidget* parent, + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui, QListWidget* list, QSplitter* splitter, TextEditor* editor); virtual bool wantsFile(const QString& rootPath, const QString& fullPath) const = 0; @@ -37,7 +37,9 @@ private: class TextFilesTab : public GenericFilesTab { public: - TextFilesTab(QWidget* parent, Ui::ModInfoDialog* ui); + TextFilesTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui); protected: bool wantsFile(const QString& rootPath, const QString& fullPath) const override; @@ -47,7 +49,9 @@ protected: class IniFilesTab : public GenericFilesTab { public: - IniFilesTab(QWidget* parent, Ui::ModInfoDialog* ui); + IniFilesTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui); protected: bool wantsFile(const QString& rootPath, const QString& fullPath) const override; diff --git a/src/modinforegular.cpp b/src/modinforegular.cpp index a271c4e8..babbd665 100644 --- a/src/modinforegular.cpp +++ b/src/modinforegular.cpp @@ -118,7 +118,7 @@ void ModInfoRegular::readMeta() m_EndorsedState = metaFile.value("endorsed", false).toBool() ? ENDORSED_TRUE : ENDORSED_FALSE; } } - + QString categoriesString = metaFile.value("category", "").toString(); QStringList categories = categoriesString.split(',', QString::SkipEmptyParts); @@ -291,15 +291,27 @@ void ModInfoRegular::nxmRequestFailed(QString, int, int, QVariant userData, QNet bool ModInfoRegular::updateNXMInfo() { - QDateTime time = QDateTime::currentDateTimeUtc(); - QDateTime target = m_LastNexusQuery.addDays(1); - if (m_NexusID > 0 && time >= target) { + if (needsDescriptionUpdate()) { m_NexusBridge.requestDescription(m_GameName, m_NexusID, QVariant()); return true; } + return false; } +bool ModInfoRegular::needsDescriptionUpdate() const +{ + if (m_NexusID > 0) { + QDateTime time = QDateTime::currentDateTimeUtc(); + QDateTime target = m_LastNexusQuery.addDays(1); + + if (time >= target) { + return true; + } + } + + return false; +} void ModInfoRegular::setCategory(int categoryID, bool active) { diff --git a/src/modinforegular.h b/src/modinforegular.h index f70487a2..cfe713ca 100644 --- a/src/modinforegular.h +++ b/src/modinforegular.h @@ -464,6 +464,7 @@ private: mutable std::vector m_CachedContent; mutable QTime m_LastContentCheck; + bool needsDescriptionUpdate() const; }; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 2cad9ce8..e073924b 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -2204,6 +2204,21 @@ void OrganizerCore::updateModsInDirectoryStructure(QMap f) +{ + if (NexusInterface::instance(m_PluginContainer)->getAccessManager()->validated()) { + f(); + } else { + QString apiKey; + if (settings().getNexusApiKey(apiKey)) { + doAfterLogin([f]{ f(); }); + NexusInterface::instance(m_PluginContainer)->getAccessManager()->apiCheck(apiKey); + } else { + MessageDialog::showMessage(tr("You need to be logged in with Nexus"), parent); + } + } +} + void OrganizerCore::requestDownload(const QUrl &url, QNetworkReply *reply) { if (m_PluginContainer != nullptr) { diff --git a/src/organizercore.h b/src/organizercore.h index a4a57496..4dd11831 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -146,6 +146,7 @@ public: void updateModsInDirectoryStructure(QMap modInfos); void doAfterLogin(const std::function &function) { m_PostLoginTasks.append(function); } + void loggedInAction(QWidget* parent, std::function f); static QString findJavaInstallation(const QString& jarFile={}); -- cgit v1.3.1 From 895883571b2b71c891dbaad4662adc7b39256286 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sun, 23 Jun 2019 04:04:54 -0400 Subject: splitting filetree tab moved mod info dialog classes to a sub filter --- src/CMakeLists.txt | 19 +- src/modinfodialog.cpp | 409 +-------------------------------------- src/modinfodialog.h | 28 +-- src/modinfodialog.ui | 8 +- src/modinfodialogconflicts.cpp | 14 ++ src/modinfodialogconflicts.h | 16 +- src/modinfodialogfiletree.cpp | 420 +++++++++++++++++++++++++++++++++++++++++ src/modinfodialogfiletree.h | 39 ++++ 8 files changed, 502 insertions(+), 451 deletions(-) create mode 100644 src/modinfodialogfiletree.cpp create mode 100644 src/modinfodialogfiletree.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7dc39fd9..1d27f444 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -57,6 +57,7 @@ SET(organizer_SRCS modinfodialogcategories.cpp modinfodialogconflicts.cpp modinfodialogesps.cpp + modinfodialogfiletree.cpp modinfodialogimages.cpp modinfodialognexus.cpp modinfodialogtab.cpp @@ -164,6 +165,7 @@ SET(organizer_HDRS modinfodialogcategories.h modinfodialogconflicts.h modinfodialogesps.h + modinfodialogfiletree.h modinfodialogimages.h modinfodialognexus.h modinfodialogtab.h @@ -364,21 +366,24 @@ set(locking set(modinfo modinfo modinfobackup + modinfoforeign + modinfooverwrite + modinforegular + modinfoseparator + modinfowithconflictinfo +) + +set(modinfo\\dialog modinfodialog modinfodialogcategories modinfodialogconflicts modinfodialogesps + modinfodialogfiletree modinfodialogimages modinfodialognexus modinfodialogtab modinfodialogtextfiles - modinfoforeign - modinfooverwrite - modinforegular - modinfoseparator - modinfowithconflictinfo ) - set(modlist modlist modlistsortproxy @@ -443,7 +448,7 @@ set(widgets ) set(src_filters - application core browser dialogs downloads executables locking modinfo + application core browser dialogs downloads executables locking modinfo modinfo\\dialog modlist plugins previews profiles settings utilities widgets ) diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index b78f4515..6463a5a6 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -43,6 +43,7 @@ along with Mod Organizer. If not, see . #include "modinfodialogconflicts.h" #include "modinfodialogcategories.h" #include "modinfodialognexus.h" +#include "modinfodialogfiletree.h" #include #include @@ -193,7 +194,7 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->tabWidget->setTabEnabled(TAB_CONFLICTS, false); ui->tabWidget->setTabEnabled(TAB_CATEGORIES, true); ui->tabWidget->setTabEnabled(TAB_NEXUS, false); - //ui->tabWidget->setTabEnabled(TAB_NOTES, false); + ui->tabWidget->setTabEnabled(TAB_NOTES, true); ui->tabWidget->setTabEnabled(TAB_FILETREE, false); } else if (unmanaged) @@ -205,8 +206,8 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->tabWidget->setTabEnabled(TAB_CONFLICTS, true); ui->tabWidget->setTabEnabled(TAB_CATEGORIES, false); ui->tabWidget->setTabEnabled(TAB_NEXUS, false); - ui->tabWidget->setTabEnabled(TAB_FILETREE, false); ui->tabWidget->setTabEnabled(TAB_NOTES, false); + ui->tabWidget->setTabEnabled(TAB_FILETREE, false); } else { ui->tabWidget->setTabEnabled(TAB_TEXTFILES, true); ui->tabWidget->setTabEnabled(TAB_INIFILES, true); @@ -215,8 +216,8 @@ ModInfoDialog::ModInfoDialog(ModInfo::Ptr modInfo, const DirectoryEntry *directo ui->tabWidget->setTabEnabled(TAB_CONFLICTS, true); ui->tabWidget->setTabEnabled(TAB_CATEGORIES, true); ui->tabWidget->setTabEnabled(TAB_NEXUS, true); - - initFiletree(modInfo); + ui->tabWidget->setTabEnabled(TAB_NOTES, true); + ui->tabWidget->setTabEnabled(TAB_FILETREE, true); } // activate first enabled tab @@ -252,7 +253,7 @@ std::vector> ModInfoDialog::createTabs() { return createTabsImpl< TextFilesTab, IniFilesTab, ImagesTab, ESPsTab, - ConflictsTab, CategoriesTab, NexusTab, NotesTab>( + ConflictsTab, CategoriesTab, NexusTab, NotesTab, FileTreeTab>( *m_OrganizerCore, *m_PluginContainer, this, ui); } @@ -262,35 +263,6 @@ int ModInfoDialog::exec() return TutorableDialog::exec(); } -void ModInfoDialog::initFiletree(ModInfo::Ptr modInfo) -{ - ui->fileTree = findChild("fileTree"); - - m_FileSystemModel = new QFileSystemModel(this); - m_FileSystemModel->setReadOnly(false); - m_FileSystemModel->setRootPath(m_RootPath); - ui->fileTree->setModel(m_FileSystemModel); - ui->fileTree->setRootIndex(m_FileSystemModel->index(m_RootPath)); - ui->fileTree->setColumnWidth(0, 300); - - m_NewFolderAction = new QAction(tr("&New Folder"), ui->fileTree); - m_OpenAction = new QAction(tr("&Open"), ui->fileTree); - m_PreviewAction = new QAction(tr("&Preview"), ui->fileTree); - m_RenameAction = new QAction(tr("&Rename"), ui->fileTree); - m_DeleteAction = new QAction(tr("&Delete"), ui->fileTree); - m_HideAction = new QAction(tr("&Hide"), ui->fileTree); - m_UnhideAction = new QAction(tr("&Unhide"), ui->fileTree); - - connect(m_NewFolderAction, SIGNAL(triggered()), this, SLOT(createDirectoryTriggered())); - connect(m_OpenAction, SIGNAL(triggered()), this, SLOT(openTriggered())); - connect(m_PreviewAction, SIGNAL(triggered()), this, SLOT(previewTriggered())); - connect(m_RenameAction, SIGNAL(triggered()), this, SLOT(renameTriggered())); - connect(m_DeleteAction, SIGNAL(triggered()), this, SLOT(deleteTriggered())); - connect(m_HideAction, SIGNAL(triggered()), this, SLOT(hideTriggered())); - connect(m_UnhideAction, SIGNAL(triggered()), this, SLOT(unhideTriggered())); -} - - int ModInfoDialog::tabIndex(const QString &tabId) { for (int i = 0; i < ui->tabWidget->count(); ++i) { @@ -301,7 +273,6 @@ int ModInfoDialog::tabIndex(const QString &tabId) return -1; } - void ModInfoDialog::saveState(Settings& s) const { s.directInterface().setValue("mod_info_tabs", saveTabState()); @@ -340,6 +311,7 @@ void ModInfoDialog::restoreTabState(const QByteArray &state) m_RealTabPos[newPos] = newPos; } } + // then actually move the tabs QTabBar *tabBar = ui->tabWidget->findChild("qt_tabwidget_tabbar"); // magic name = bad ui->tabWidget->blockSignals(true); @@ -407,377 +379,10 @@ void ModInfoDialog::openTab(int tab) } } -QString ModInfoDialog::getFileCategory(int categoryID) -{ - switch (categoryID) { - case 1: return tr("Main"); - case 2: return tr("Update"); - case 3: return tr("Optional"); - case 4: return tr("Old"); - case 5: return tr("Miscellaneous"); - case 6: return tr("Deleted"); - default: return tr("Unknown"); - } -} - void ModInfoDialog::on_tabWidget_currentChanged(int index) { } -bool ModInfoDialog::recursiveDelete(const QModelIndex &index) -{ - for (int childRow = 0; childRow < m_FileSystemModel->rowCount(index); ++childRow) { - QModelIndex childIndex = m_FileSystemModel->index(childRow, 0, index); - if (m_FileSystemModel->isDir(childIndex)) { - if (!recursiveDelete(childIndex)) { - qCritical("failed to delete %s", m_FileSystemModel->fileName(childIndex).toUtf8().constData()); - return false; - } - } else { - if (!m_FileSystemModel->remove(childIndex)) { - qCritical("failed to delete %s", m_FileSystemModel->fileName(childIndex).toUtf8().constData()); - return false; - } - } - } - if (!m_FileSystemModel->remove(index)) { - qCritical("failed to delete %s", m_FileSystemModel->fileName(index).toUtf8().constData()); - return false; - } - return true; -} - - -void ModInfoDialog::on_openInExplorerButton_clicked() -{ - shell::ExploreFile(m_ModInfo->absolutePath()); -} - -void ModInfoDialog::deleteFile(const QModelIndex &index) -{ - bool res = m_FileSystemModel->isDir(index) ? recursiveDelete(index) - : m_FileSystemModel->remove(index); - if (!res) { - QString fileName = m_FileSystemModel->fileName(index); - reportError(tr("Failed to delete %1").arg(fileName)); - } -} - -void ModInfoDialog::delete_activated() -{ - if (ui->fileTree->hasFocus()) { - QItemSelectionModel *selection = ui->fileTree->selectionModel(); - - if (selection->hasSelection() && selection->selectedRows().count() >= 1) { - - if (selection->selectedRows().count() == 0) { - return; - } - else if (selection->selectedRows().count() == 1) { - QString fileName = m_FileSystemModel->fileName(selection->selectedRows().at(0)); - if (QMessageBox::question(this, tr("Confirm"), tr("Are you sure you want to delete \"%1\"?").arg(fileName), - QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { - return; - } - } - else { - if (QMessageBox::question(this, tr("Confirm"), tr("Are you sure you want to delete the selected files?"), - QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { - return; - } - } - - foreach(QModelIndex index, selection->selectedRows()) { - deleteFile(index); - } - } - } -} - -void ModInfoDialog::deleteTriggered() -{ - if (m_FileSelection.count() == 0) { - return; - } else if (m_FileSelection.count() == 1) { - QString fileName = m_FileSystemModel->fileName(m_FileSelection.at(0)); - if (QMessageBox::question(this, tr("Confirm"), tr("Are you sure you want to delete \"%1\"?").arg(fileName), - QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { - return; - } - } else { - if (QMessageBox::question(this, tr("Confirm"), tr("Are you sure you want to delete the selected files?"), - QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { - return; - } - } - - foreach(QModelIndex index, m_FileSelection) { - deleteFile(index); - } -} - - -void ModInfoDialog::renameTriggered() -{ - QModelIndex selection = m_FileSelection.at(0); - QModelIndex index = selection.sibling(selection.row(), 0); - if (!index.isValid() || m_FileSystemModel->isReadOnly()) { - return; - } - - ui->fileTree->edit(index); -} - - -void ModInfoDialog::hideTriggered() -{ - changeFiletreeVisibility(false); -} - - -void ModInfoDialog::unhideTriggered() -{ - changeFiletreeVisibility(true); -} - -void ModInfoDialog::changeFiletreeVisibility(bool visible) -{ - bool changed = false; - bool stop = false; - - qDebug().nospace() - << (visible ? "unhiding" : "hiding") << " " - << m_FileSelection.size() << " filetree files"; - - QFlags flags = - (visible ? FileRenamer::UNHIDE : FileRenamer::HIDE); - - if (m_FileSelection.size() > 1) { - flags |= FileRenamer::MULTIPLE; - } - - FileRenamer renamer(this, flags); - - for (const auto& index : m_FileSelection) { - if (stop) { - break; - } - - const QString path = m_FileSystemModel->filePath(index); - auto result = FileRenamer::RESULT_CANCEL; - - if (visible) { - if (!canUnhideFile(false, path)) { - qDebug().nospace() << "cannot unhide " << path << ", skipping"; - continue; - } - result = unhideFile(renamer, path); - } else { - if (!canHideFile(false, path)) { - qDebug().nospace() << "cannot hide " << path << ", skipping"; - continue; - } - result = hideFile(renamer, path); - } - - switch (result) { - case FileRenamer::RESULT_OK: { - // will trigger a refresh at the end - changed = true; - break; - } - - case FileRenamer::RESULT_SKIP: { - // nop - break; - } - - case FileRenamer::RESULT_CANCEL: { - // stop right now, but make sure to refresh if needed - stop = true; - break; - } - } - } - - qDebug().nospace() << (visible ? "unhiding" : "hiding") << " filetree files done"; - - if (changed) { - qDebug().nospace() << "triggering refresh"; - if (m_Origin) { - emit originModified(m_Origin->getID()); - } - refreshLists(); - } -} - - -void ModInfoDialog::openTriggered() -{ - if (m_FileSelection.size() == 1) { - const auto index = m_FileSelection.at(0); - if (!index.isValid()) { - return; - } - - QString fileName = m_FileSystemModel->filePath(index); - shell::OpenFile(fileName); - } -} - -void ModInfoDialog::previewTriggered() -{ - if (m_FileSelection.size() == 1) { - const auto index = m_FileSelection.at(0); - if (!index.isValid()) { - return; - } - - QString fileName = m_FileSystemModel->filePath(index); - m_OrganizerCore->previewFile(this, m_ModInfo->name(), fileName); - } -} - -void ModInfoDialog::createDirectoryTriggered() -{ - QModelIndex selection = m_FileSelection.at(0); - - QModelIndex index = m_FileSystemModel->isDir(selection) ? selection - : selection.parent(); - index = index.sibling(index.row(), 0); - - QString name = tr("New Folder"); - QString path = m_FileSystemModel->filePath(index).append("/"); - - QModelIndex existingIndex = m_FileSystemModel->index(path + name); - int suffix = 1; - while (existingIndex.isValid()) { - name = tr("New Folder") + QString::number(suffix++); - existingIndex = m_FileSystemModel->index(path + name); - } - - QModelIndex newIndex = m_FileSystemModel->mkdir(index, name); - if (!newIndex.isValid()) { - reportError(tr("Failed to create \"%1\"").arg(name)); - return; - } - - ui->fileTree->setCurrentIndex(newIndex); - ui->fileTree->edit(newIndex); -} - - -void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) -{ - QItemSelectionModel *selectionModel = ui->fileTree->selectionModel(); - m_FileSelection = selectionModel->selectedRows(0); - - QMenu menu(ui->fileTree); - - menu.addAction(m_NewFolderAction); - - if (selectionModel->hasSelection()) { - bool enableOpen = true; - bool enablePreview = true; - bool enableRename = true; - bool enableDelete = true; - bool enableHide = true; - bool enableUnhide = true; - - if (m_FileSelection.size() == 1) { - // single selection - - // only enable open action if a file is selected - bool hasFiles = false; - - foreach(QModelIndex idx, m_FileSelection) { - if (m_FileSystemModel->fileInfo(idx).isFile()) { - hasFiles = true; - break; - } - } - - if (!hasFiles) { - enableOpen = false; - enablePreview = false; - } - - const QString fileName = m_FileSystemModel->fileName(m_FileSelection.at(0)); - - if (!canPreviewFile(*m_PluginContainer, false, fileName)) { - enablePreview = false; - } - - if (!canHideFile(false, fileName)) { - enableHide = false; - } - - if (!canUnhideFile(false, fileName)) { - enableUnhide = false; - } - } else { - // this is a multiple selection, don't show open action so users don't open - // a thousand files - enableOpen = false; - enablePreview = false; - enableRename = false; - - if (m_FileSelection.size() < max_scan_for_context_menu) { - // if the number of selected items is low, checking them to accurately - // show the menu items is worth it - enableHide = false; - enableUnhide = false; - - for (const auto& index : m_FileSelection) { - const QString fileName = m_FileSystemModel->fileName(index); - - if (canHideFile(false, fileName)) { - enableHide = true; - } - - if (canUnhideFile(false, fileName)) { - enableUnhide = true; - } - - if (enableHide && enableUnhide) { - // found both, no need to check more - break; - } - } - } - } - - if (enableOpen) { - menu.addAction(m_OpenAction); - } - - if (enablePreview) { - menu.addAction(m_PreviewAction); - } - - if (enableRename) { - menu.addAction(m_RenameAction); - } - - if (enableDelete) { - menu.addAction(m_DeleteAction); - } - - if (enableHide) { - menu.addAction(m_HideAction); - } - - if (enableUnhide) { - menu.addAction(m_UnhideAction); - } - } else { - m_FileSelection.clear(); - m_FileSelection.append(m_FileSystemModel->index(m_FileSystemModel->rootPath(), 0)); - } - - menu.exec(ui->fileTree->viewport()->mapToGlobal(pos)); -} - void ModInfoDialog::on_nextButton_clicked() { int currentTab = ui->tabWidget->currentIndex(); diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 7b96d21a..b367f647 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -54,19 +54,6 @@ class TextEditor; class ModInfoDialogTab; -class ElideLeftDelegate : public QStyledItemDelegate -{ -public: - using QStyledItemDelegate::QStyledItemDelegate; - -protected: - void initStyleOption(QStyleOptionViewItem* o, const QModelIndex& i) const - { - QStyledItemDelegate::initStyleOption(o, i); - o->textElideMode = Qt::ElideLeft; - } -}; - bool canPreviewFile(PluginContainer& pluginContainer, bool isArchive, const QString& filename); bool canOpenFile(bool isArchive, const QString& filename); @@ -145,12 +132,6 @@ signals: void originModified(int originID); private: - - void initFiletree(ModInfo::Ptr modInfo); - - void refreshLists(); - - QString getFileCategory(int categoryID); bool recursiveDelete(const QModelIndex &index); void deleteFile(const QModelIndex &index); @@ -189,17 +170,9 @@ private: OrganizerCore *m_OrganizerCore; PluginContainer *m_PluginContainer; - QFileSystemModel *m_FileSystemModel; QTreeView *m_FileTree; QModelIndexList m_FileSelection; - QAction *m_NewFolderAction; - QAction *m_OpenAction; - QAction *m_PreviewAction; - QAction *m_RenameAction; - QAction *m_DeleteAction; - QAction *m_HideAction; - QAction *m_UnhideAction; const MOShared::DirectoryEntry *m_Directory; MOShared::FilesOrigin *m_Origin; @@ -209,6 +182,7 @@ private: std::vector> createTabs(); + void refreshLists(); void refreshFiles(); void restoreTabState(const QByteArray &state); diff --git a/src/modinfodialog.ui b/src/modinfodialog.ui index 360ecc79..04282e81 100644 --- a/src/modinfodialog.ui +++ b/src/modinfodialog.ui @@ -20,7 +20,7 @@ QTabWidget::Rounded - 7 + 8 true @@ -1088,7 +1088,7 @@ p, li { white-space: pre-wrap; }
- + Filetree @@ -1096,7 +1096,7 @@ p, li { white-space: pre-wrap; } - + 0 @@ -1124,7 +1124,7 @@ p, li { white-space: pre-wrap; } - + Qt::CustomContextMenu diff --git a/src/modinfodialogconflicts.cpp b/src/modinfodialogconflicts.cpp index 4176ac3e..e7f189c2 100644 --- a/src/modinfodialogconflicts.cpp +++ b/src/modinfodialogconflicts.cpp @@ -24,6 +24,20 @@ int naturalCompare(const QString& a, const QString& b) } +class ElideLeftDelegate : public QStyledItemDelegate +{ +public: + using QStyledItemDelegate::QStyledItemDelegate; + +protected: + void initStyleOption(QStyleOptionViewItem* o, const QModelIndex& i) const + { + QStyledItemDelegate::initStyleOption(o, i); + o->textElideMode = Qt::ElideLeft; + } +}; + + class ConflictItem : public QTreeWidgetItem { public: diff --git a/src/modinfodialogconflicts.h b/src/modinfodialogconflicts.h index 20362575..9c011163 100644 --- a/src/modinfodialogconflicts.h +++ b/src/modinfodialogconflicts.h @@ -115,18 +115,12 @@ public: private: struct Actions { - QAction* hide; - QAction* unhide; - QAction* open; - QAction* preview; - QMenu* gotoMenu; + QAction* hide = nullptr; + QAction* unhide = nullptr; + QAction* open = nullptr; + QAction* preview = nullptr; + QMenu* gotoMenu = nullptr; std::vector gotoActions; - - Actions() : - hide(nullptr), unhide(nullptr), open(nullptr), preview(nullptr), - gotoMenu(nullptr) - { - } }; GeneralConflictsTab m_general; diff --git a/src/modinfodialogfiletree.cpp b/src/modinfodialogfiletree.cpp new file mode 100644 index 00000000..e277b04b --- /dev/null +++ b/src/modinfodialogfiletree.cpp @@ -0,0 +1,420 @@ +#include "modinfodialogfiletree.h" +#include "ui_modinfodialog.h" +#include "organizercore.h" +#include +#include + +using MOBase::reportError; +namespace shell = MOBase::shell; + +FileTreeTab::FileTreeTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui) + : ModInfoDialogTab(oc, plugin, parent, ui), m_fs(nullptr) +{ + m_fs = new QFileSystemModel(this); + m_fs->setReadOnly(false); + ui->fileTree1->setModel(m_fs); + ui->fileTree1->setColumnWidth(0, 300); + + m_actions.newFolder = new QAction(tr("&New Folder"), ui->fileTree1); + m_actions.open = new QAction(tr("&Open"), ui->fileTree1); + m_actions.preview = new QAction(tr("&Preview"), ui->fileTree1); + m_actions.rename = new QAction(tr("&Rename"), ui->fileTree1); + m_actions.del = new QAction(tr("&Delete"), ui->fileTree1); + m_actions.hide = new QAction(tr("&Hide"), ui->fileTree1); + m_actions.unhide = new QAction(tr("&Unhide"), ui->fileTree1); + + connect(m_actions.newFolder, &QAction::triggered, [&]{ onCreateDirectory(); }); + connect(m_actions.open, &QAction::triggered, [&]{ onOpen(); }); + connect(m_actions.preview, &QAction::triggered, [&]{ onPreview(); }); + connect(m_actions.rename, &QAction::triggered, [&]{ onRename(); }); + connect(m_actions.del, &QAction::triggered, [&]{ onDelete(); }); + connect(m_actions.hide, &QAction::triggered, [&]{ onHide(); }); + connect(m_actions.unhide, &QAction::triggered, [&]{ onUnhide(); }); +} + +void FileTreeTab::clear() +{ + m_fs->setRootPath({}); + //ui->fileTree1-> +} + +void FileTreeTab::update() +{ + const auto rootPath = mod()->absolutePath(); + + m_fs->setRootPath(rootPath); + ui->fileTree1->setRootIndex(m_fs->index(rootPath)); +} + +QModelIndex FileTreeTab::singleSelection() const +{ + const auto rows = ui->fileTree1->selectionModel()->selectedRows(); + if (rows.size() != 1) { + return {}; + } + + return rows[0]; +} + +void FileTreeTab::onCreateDirectory() +{ + auto selection = singleSelection(); + if (!selection.isValid()) { + return; + } + + QModelIndex index = m_fs->isDir(selection) ? selection : selection.parent(); + index = index.sibling(index.row(), 0); + + QString name = tr("New Folder"); + QString path = m_fs->filePath(index).append("/"); + + QModelIndex existingIndex = m_fs->index(path + name); + int suffix = 1; + while (existingIndex.isValid()) { + name = tr("New Folder") + QString::number(suffix++); + existingIndex = m_fs->index(path + name); + } + + QModelIndex newIndex = m_fs->mkdir(index, name); + if (!newIndex.isValid()) { + reportError(tr("Failed to create \"%1\"").arg(name)); + return; + } + + ui->fileTree1->setCurrentIndex(newIndex); + ui->fileTree1->edit(newIndex); +} + +void FileTreeTab::onOpen() +{ + auto selection = singleSelection(); + if (!selection.isValid()) { + return; + } + + shell::OpenFile(m_fs->filePath(selection)); +} + +void FileTreeTab::onPreview() +{ + auto selection = singleSelection(); + if (!selection.isValid()) { + return; + } + + core().previewFile(parentWidget(), mod()->name(), m_fs->filePath(selection)); +} + +void FileTreeTab::onRename() +{ + auto selection = singleSelection(); + if (!selection.isValid()) { + return; + } + + QModelIndex index = selection.sibling(selection.row(), 0); + if (!index.isValid() || m_fs->isReadOnly()) { + return; + } + + ui->fileTree1->edit(index); +} + +void FileTreeTab::onDelete() +{ + const auto rows = ui->fileTree1->selectionModel()->selectedRows(); + if (rows.count() == 0) { + return; + } + + QString message; + + if (rows.count() == 1) { + QString fileName = m_fs->fileName(rows[0]); + message = tr("Are sure you want to delete \"%1\"?").arg(fileName); + } else { + message = tr("Are sure you want to delete the selected files?"); + } + + if (QMessageBox::question(parentWidget(), tr("Confirm"), message) != QMessageBox::Yes) { + return; + } + + foreach(QModelIndex index, m_FileSelection) { + deleteFile(index); + } +} + + +bool FileTreeTab::recursiveDelete(const QModelIndex &index) +{ + for (int childRow = 0; childRow < m_FileSystemModel->rowCount(index); ++childRow) { + QModelIndex childIndex = m_FileSystemModel->index(childRow, 0, index); + if (m_FileSystemModel->isDir(childIndex)) { + if (!recursiveDelete(childIndex)) { + qCritical("failed to delete %s", m_FileSystemModel->fileName(childIndex).toUtf8().constData()); + return false; + } + } else { + if (!m_FileSystemModel->remove(childIndex)) { + qCritical("failed to delete %s", m_FileSystemModel->fileName(childIndex).toUtf8().constData()); + return false; + } + } + } + if (!m_FileSystemModel->remove(index)) { + qCritical("failed to delete %s", m_FileSystemModel->fileName(index).toUtf8().constData()); + return false; + } + return true; +} + + +void ModInfoDialog::on_openInExplorerButton_clicked() +{ + shell::ExploreFile(m_ModInfo->absolutePath()); +} + +void ModInfoDialog::deleteFile(const QModelIndex &index) +{ + bool res = m_FileSystemModel->isDir(index) ? recursiveDelete(index) + : m_FileSystemModel->remove(index); + if (!res) { + QString fileName = m_FileSystemModel->fileName(index); + reportError(tr("Failed to delete %1").arg(fileName)); + } +} + +void ModInfoDialog::delete_activated() +{ + if (ui->fileTree->hasFocus()) { + QItemSelectionModel *selection = ui->fileTree->selectionModel(); + + if (selection->hasSelection() && selection->selectedRows().count() >= 1) { + + if (selection->selectedRows().count() == 0) { + return; + } + else if (selection->selectedRows().count() == 1) { + QString fileName = m_FileSystemModel->fileName(selection->selectedRows().at(0)); + if (QMessageBox::question(this, tr("Confirm"), tr("Are sure you want to delete \"%1\"?").arg(fileName), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + return; + } + } + else { + if (QMessageBox::question(this, tr("Confirm"), tr("Are sure you want to delete the selected files?"), + QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes) { + return; + } + } + + foreach(QModelIndex index, selection->selectedRows()) { + deleteFile(index); + } + } + } +} + + + + + +void ModInfoDialog::hideTriggered() +{ + changeFiletreeVisibility(false); +} + + +void ModInfoDialog::unhideTriggered() +{ + changeFiletreeVisibility(true); +} + +void ModInfoDialog::changeFiletreeVisibility(bool visible) +{ + bool changed = false; + bool stop = false; + + qDebug().nospace() + << (visible ? "unhiding" : "hiding") << " " + << m_FileSelection.size() << " filetree files"; + + QFlags flags = + (visible ? FileRenamer::UNHIDE : FileRenamer::HIDE); + + if (m_FileSelection.size() > 1) { + flags |= FileRenamer::MULTIPLE; + } + + FileRenamer renamer(this, flags); + + for (const auto& index : m_FileSelection) { + if (stop) { + break; + } + + const QString path = m_FileSystemModel->filePath(index); + auto result = FileRenamer::RESULT_CANCEL; + + if (visible) { + if (!canUnhideFile(false, path)) { + qDebug().nospace() << "cannot unhide " << path << ", skipping"; + continue; + } + result = unhideFile(renamer, path); + } else { + if (!canHideFile(false, path)) { + qDebug().nospace() << "cannot hide " << path << ", skipping"; + continue; + } + result = hideFile(renamer, path); + } + + switch (result) { + case FileRenamer::RESULT_OK: { + // will trigger a refresh at the end + changed = true; + break; + } + + case FileRenamer::RESULT_SKIP: { + // nop + break; + } + + case FileRenamer::RESULT_CANCEL: { + // stop right now, but make sure to refresh if needed + stop = true; + break; + } + } + } + + qDebug().nospace() << (visible ? "unhiding" : "hiding") << " filetree files done"; + + if (changed) { + qDebug().nospace() << "triggering refresh"; + if (m_Origin) { + emit originModified(m_Origin->getID()); + } + refreshLists(); + } +} + + + + +void ModInfoDialog::on_fileTree_customContextMenuRequested(const QPoint &pos) +{ + QItemSelectionModel *selectionModel = ui->fileTree->selectionModel(); + m_FileSelection = selectionModel->selectedRows(0); + + QMenu menu(ui->fileTree); + + menu.addAction(m_NewFolderAction); + + if (selectionModel->hasSelection()) { + bool enableOpen = true; + bool enablePreview = true; + bool enableRename = true; + bool enableDelete = true; + bool enableHide = true; + bool enableUnhide = true; + + if (m_FileSelection.size() == 1) { + // single selection + + // only enable open action if a file is selected + bool hasFiles = false; + + foreach(QModelIndex idx, m_FileSelection) { + if (m_FileSystemModel->fileInfo(idx).isFile()) { + hasFiles = true; + break; + } + } + + if (!hasFiles) { + enableOpen = false; + enablePreview = false; + } + + const QString fileName = m_FileSystemModel->fileName(m_FileSelection.at(0)); + + if (!canPreviewFile(*m_PluginContainer, false, fileName)) { + enablePreview = false; + } + + if (!canHideFile(false, fileName)) { + enableHide = false; + } + + if (!canUnhideFile(false, fileName)) { + enableUnhide = false; + } + } else { + // this is a multiple selection, don't show open action so users don't open + // a thousand files + enableOpen = false; + enablePreview = false; + enableRename = false; + + if (m_FileSelection.size() < max_scan_for_context_menu) { + // if the number of selected items is low, checking them to accurately + // show the menu items is worth it + enableHide = false; + enableUnhide = false; + + for (const auto& index : m_FileSelection) { + const QString fileName = m_FileSystemModel->fileName(index); + + if (canHideFile(false, fileName)) { + enableHide = true; + } + + if (canUnhideFile(false, fileName)) { + enableUnhide = true; + } + + if (enableHide && enableUnhide) { + // found both, no need to check more + break; + } + } + } + } + + if (enableOpen) { + menu.addAction(m_OpenAction); + } + + if (enablePreview) { + menu.addAction(m_PreviewAction); + } + + if (enableRename) { + menu.addAction(m_RenameAction); + } + + if (enableDelete) { + menu.addAction(m_DeleteAction); + } + + if (enableHide) { + menu.addAction(m_HideAction); + } + + if (enableUnhide) { + menu.addAction(m_UnhideAction); + } + } else { + m_FileSelection.clear(); + m_FileSelection.append(m_FileSystemModel->index(m_FileSystemModel->rootPath(), 0)); + } + + menu.exec(ui->fileTree->viewport()->mapToGlobal(pos)); +} diff --git a/src/modinfodialogfiletree.h b/src/modinfodialogfiletree.h new file mode 100644 index 00000000..e7708ab8 --- /dev/null +++ b/src/modinfodialogfiletree.h @@ -0,0 +1,39 @@ +#ifndef MODINFODIALOGFILETREE_H +#define MODINFODIALOGFILETREE_H + +#include "modinfodialogtab.h" + +class FileTreeTab : public ModInfoDialogTab +{ +public: + FileTreeTab( + OrganizerCore& oc, PluginContainer& plugin, + QWidget* parent, Ui::ModInfoDialog* ui); + + void clear() override; + void update() override; + +private: + struct Actions + { + QAction *newFolder = nullptr; + QAction *open = nullptr; + QAction *preview = nullptr; + QAction *rename = nullptr; + QAction *del = nullptr; + QAction *hide = nullptr; + QAction *unhide = nullptr; + }; + + QFileSystemModel* m_fs; + Actions m_actions; + + QModelIndex singleSelection() const; + void onCreateDirectory(); + void onOpen(); + void onPreview(); + void onRename(); + void onDelete(); +}; + +#endif // MODINFODIALOGFILETREE_H -- cgit v1.3.1 From 4aa59cdc7dd779c7e864a1c4e96c6b52c61879ff Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 2 Jul 2019 12:35:33 -0400 Subject: added modinfodialogfwd.h, mostly for the enum that's used in various places renamed ETabs to ModInfoTabIDs and changed all ints to use the enum instead added ModInfoPtr to avoid having to include modinfo.h just to get ModInfo::Ptr --- src/CMakeLists.txt | 2 ++ src/iuserinterface.h | 5 ++-- src/mainwindow.cpp | 37 +++++++++++++++------------- src/mainwindow.h | 7 +++--- src/modinfodialog.cpp | 60 ++++++++++++++++++++-------------------------- src/modinfodialog.h | 50 ++++++-------------------------------- src/modinfodialogfwd.h | 50 ++++++++++++++++++++++++++++++++++++++ src/modinfodialognexus.cpp | 2 +- src/modinfodialognexus.h | 2 +- src/modinfodialogtab.cpp | 7 +++--- src/modinfodialogtab.h | 20 ++++++++-------- src/organizercore.cpp | 8 +++---- 12 files changed, 132 insertions(+), 118 deletions(-) create mode 100644 src/modinfodialogfwd.h (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1d27f444..929ee296 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -166,6 +166,7 @@ SET(organizer_HDRS modinfodialogconflicts.h modinfodialogesps.h modinfodialogfiletree.h + modinfodialogfwd.h modinfodialogimages.h modinfodialognexus.h modinfodialogtab.h @@ -379,6 +380,7 @@ set(modinfo\\dialog modinfodialogconflicts modinfodialogesps modinfodialogfiletree + modinfodialogfwd modinfodialogimages modinfodialognexus modinfodialogtab diff --git a/src/iuserinterface.h b/src/iuserinterface.h index 034fa029..bba8de2b 100644 --- a/src/iuserinterface.h +++ b/src/iuserinterface.h @@ -2,7 +2,7 @@ #define IUSERINTERFACE_H -#include "modinfo.h" +#include "modinfodialogfwd.h" #include "ilockedwaitingforprocess.h" #include #include @@ -29,7 +29,8 @@ public: virtual bool closeWindow() = 0; virtual void setWindowEnabled(bool enabled) = 0; - virtual void displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab) = 0; + virtual void displayModInformation( + ModInfoPtr modInfo, unsigned int modIndex, ModInfoTabIDs tabID) = 0; virtual void updateBSAList(const QStringList &defaultArchives, const QStringList &activeArchives) = 0; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 7ab555fa..a596c542 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -3192,7 +3192,8 @@ void MainWindow::overwriteClosed(int) } -void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab) +void MainWindow::displayModInformation( + ModInfo::Ptr modInfo, unsigned int modIndex, ModInfoTabIDs tabID) { if (!m_OrganizerCore.modList()->modInfoAboutToChange(modInfo)) { qDebug("A different mod information dialog is open. If this is incorrect, please restart MO"); @@ -3227,8 +3228,8 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, connect(&dialog, SIGNAL(originModified(int)), this, SLOT(originModified(int))); //Open the tab first if we want to use the standard indexes of the tabs. - if (tab != -1) { - dialog.setTab(ModInfoDialog::ETabs(tab)); + if (tabID != ModInfoTabIDs::None) { + dialog.setTab(tabID); } dialog.restoreState(m_OrganizerCore.settings()); @@ -3247,7 +3248,7 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, m_OrganizerCore.modList()->modInfoChanged(modInfo); } - if (m_OrganizerCore.currentProfile()->modEnabled(index) + if (m_OrganizerCore.currentProfile()->modEnabled(modIndex) && !modInfo->hasFlag(ModInfo::FLAG_FOREIGN)) { FilesOrigin& origin = m_OrganizerCore.directoryStructure()->getOriginByName(ToWString(modInfo->name())); origin.enable(false); @@ -3258,7 +3259,7 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, m_OrganizerCore.directoryRefresher()->addModToStructure(m_OrganizerCore.directoryStructure() , modInfo->name() - , m_OrganizerCore.currentProfile()->getModPriority(index) + , m_OrganizerCore.currentProfile()->getModPriority(modIndex) , modInfo->absolutePath() , modInfo->stealFiles() , modInfo->archives()); @@ -3347,7 +3348,7 @@ ModInfo::Ptr MainWindow::previousModInList() return {}; } -void MainWindow::displayModInformation(const QString &modName, int tab) +void MainWindow::displayModInformation(const QString &modName, ModInfoTabIDs tabID) { unsigned int index = ModInfo::getIndex(modName); if (index == UINT_MAX) { @@ -3356,14 +3357,14 @@ void MainWindow::displayModInformation(const QString &modName, int tab) } ModInfo::Ptr modInfo = ModInfo::getByIndex(index); - displayModInformation(modInfo, index, tab); + displayModInformation(modInfo, index, tabID); } -void MainWindow::displayModInformation(int row, int tab) +void MainWindow::displayModInformation(int row, ModInfoTabIDs tabID) { ModInfo::Ptr modInfo = ModInfo::getByIndex(row); - displayModInformation(modInfo, row, tab); + displayModInformation(modInfo, row, tabID); } @@ -4048,16 +4049,18 @@ void MainWindow::on_modList_doubleClicked(const QModelIndex &index) try { m_ContextRow = m_ModListSortProxy->mapToSource(index).row(); sourceIdx.column(); - int tab = -1; + + auto tab = ModInfoTabIDs::None; + switch (sourceIdx.column()) { - case ModList::COL_NOTES: tab = ModInfoDialog::TAB_NOTES; break; - case ModList::COL_VERSION: tab = ModInfoDialog::TAB_NEXUS; break; - case ModList::COL_MODID: tab = ModInfoDialog::TAB_NEXUS; break; - case ModList::COL_GAME: tab = ModInfoDialog::TAB_NEXUS; break; - case ModList::COL_CATEGORY: tab = ModInfoDialog::TAB_CATEGORIES; break; - case ModList::COL_FLAGS: tab = ModInfoDialog::TAB_CONFLICTS; break; - default: tab = -1; + case ModList::COL_NOTES: tab = ModInfoTabIDs::Notes; break; + case ModList::COL_VERSION: tab = ModInfoTabIDs::Nexus; break; + case ModList::COL_MODID: tab = ModInfoTabIDs::Nexus; break; + case ModList::COL_GAME: tab = ModInfoTabIDs::Nexus; break; + case ModList::COL_CATEGORY: tab = ModInfoTabIDs::Categories; break; + case ModList::COL_FLAGS: tab = ModInfoTabIDs::Conflicts; break; } + displayModInformation(sourceIdx.row(), tab); // workaround to cancel the editor that might have opened because of // selection-click diff --git a/src/mainwindow.h b/src/mainwindow.h index f204211e..00f15a2b 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -151,7 +151,8 @@ public: virtual void disconnectPlugins(); - void displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab); + void displayModInformation( + ModInfo::Ptr modInfo, unsigned int modIndex, ModInfoTabIDs tabID) override; bool confirmExit(); @@ -235,7 +236,7 @@ private: QList findFileInfos(const QString &path, const std::function &filter) const; bool modifyExecutablesDialog(); - void displayModInformation(int row, int tab = -1); + void displayModInformation(int row, ModInfoTabIDs tab=ModInfoTabIDs::None); void testExtractBSA(int modIndex); void writeDataToFile(QFile &file, const QString &directory, const MOShared::DirectoryEntry &directoryEntry); @@ -551,7 +552,7 @@ private slots: void editCategories(); void deselectFilters(); - void displayModInformation(const QString &modName, int tab); + void displayModInformation(const QString &modName, ModInfoTabIDs tabID); void modRenamed(const QString &oldName, const QString &newName); void modRemoved(const QString &fileName); diff --git a/src/modinfodialog.cpp b/src/modinfodialog.cpp index 4ef010e4..c8ffa35b 100644 --- a/src/modinfodialog.cpp +++ b/src/modinfodialog.cpp @@ -131,7 +131,7 @@ ModInfoDialog::ModInfoDialog( ModInfo::Ptr mod) : TutorableDialog("ModInfoDialog", mw), ui(new Ui::ModInfoDialog), m_mainWindow(mw), - m_core(core), m_plugin(plugin), m_initialTab(ETabs(-1)), + m_core(core), m_plugin(plugin), m_initialTab(ModInfoTabIDs::None), m_arrangingTabs(false) { ui->setupUi(this); @@ -155,16 +155,11 @@ ModInfoDialog::ModInfoDialog( connect( tabInfo.tab.get(), &ModInfoDialogTab::originModified, - [this, i](int originID) { - onOriginModified(originID); - }); + [this](int originID){ onOriginModified(originID); }); connect( tabInfo.tab.get(), &ModInfoDialogTab::modOpen, - [&](const QString& name){ - setMod(name); - update(); - }); + [&](const QString& name){ setMod(name); update(); }); connect( tabInfo.tab.get(), &ModInfoDialogTab::hasDataChanged, @@ -172,12 +167,7 @@ ModInfoDialog::ModInfoDialog( connect( tabInfo.tab.get(), &ModInfoDialogTab::wantsFocus, - [&, i=static_cast(i)] - { - if (i < m_tabs.size()) { - switchToTab(ETabs(m_tabs[i].tab->tabID())); - } - }); + [&, id=tabInfo.tab->tabID()]{ switchToTab(id); }); } connect(ui->tabWidget, &QTabWidget::currentChanged, [&]{ onTabChanged(); }); @@ -187,32 +177,32 @@ ModInfoDialog::ModInfoDialog( ModInfoDialog::~ModInfoDialog() = default; template -std::unique_ptr createTab(ModInfoDialog& d, int index) +std::unique_ptr createTab(ModInfoDialog& d, ModInfoTabIDs id) { return std::make_unique(ModInfoDialogTabContext( - *d.m_core, *d.m_plugin, &d, d.ui.get(), index, d.m_mod, d.getOrigin())); + *d.m_core, *d.m_plugin, &d, d.ui.get(), id, d.m_mod, d.getOrigin())); } std::vector ModInfoDialog::createTabs() { std::vector v; - v.push_back(createTab(*this, TAB_TEXTFILES)); - v.push_back(createTab(*this, TAB_INIFILES)); - v.push_back(createTab(*this, TAB_IMAGES)); - v.push_back(createTab(*this, TAB_ESPS)); - v.push_back(createTab(*this, TAB_CONFLICTS)); - v.push_back(createTab(*this, TAB_CATEGORIES)); - v.push_back(createTab(*this, TAB_NEXUS)); - v.push_back(createTab(*this, TAB_NOTES)); - v.push_back(createTab(*this, TAB_FILETREE)); + v.push_back(createTab(*this, ModInfoTabIDs::TextFiles)); + v.push_back(createTab(*this, ModInfoTabIDs::IniFiles)); + v.push_back(createTab(*this, ModInfoTabIDs::Images)); + v.push_back(createTab(*this, ModInfoTabIDs::Esps)); + v.push_back(createTab(*this, ModInfoTabIDs::Conflicts)); + v.push_back(createTab(*this, ModInfoTabIDs::Categories)); + v.push_back(createTab(*this, ModInfoTabIDs::Nexus)); + v.push_back(createTab(*this, ModInfoTabIDs::Notes)); + v.push_back(createTab(*this, ModInfoTabIDs::Filetree)); return v; } int ModInfoDialog::exec() { - const auto selectFirst = (m_initialTab == -1); + const auto selectFirst = (m_initialTab == ModInfoTabIDs::None); update(true); @@ -252,7 +242,7 @@ void ModInfoDialog::setMod(const QString& name) setMod(mod); } -void ModInfoDialog::setTab(ETabs id) +void ModInfoDialog::setTab(ModInfoTabIDs id) { if (!isVisible()) { m_initialTab = id; @@ -288,9 +278,9 @@ void ModInfoDialog::update(bool firstTime) updateTabs(); - if (m_initialTab >= 0) { + if (m_initialTab != ModInfoTabIDs::None) { switchToTab(m_initialTab); - m_initialTab = ETabs(-1); + m_initialTab = ModInfoTabIDs::None; } if (ui->tabWidget->currentIndex() == oldTab) { @@ -335,11 +325,11 @@ void ModInfoDialog::setTabsVisibility(bool firstTime) // remember selection const int selIndex = ui->tabWidget->currentIndex(); - ETabs sel = ETabs(-1); + auto sel = ModInfoTabIDs::None; for (const auto& tabInfo : m_tabs) { if (tabInfo.realPos == selIndex) { - sel = ETabs(tabInfo.tab->tabID()); + sel = tabInfo.tab->tabID(); break; } } @@ -420,7 +410,7 @@ void ModInfoDialog::setTabsColors() } } -void ModInfoDialog::switchToTab(ETabs id) +void ModInfoDialog::switchToTab(ModInfoTabIDs id) { for (const auto& tabInfo : m_tabs) { if (tabInfo.tab->tabID() == id) { @@ -429,7 +419,8 @@ void ModInfoDialog::switchToTab(ETabs id) } } - qDebug() << "can't switch to tab " << id << ", not available"; + qDebug() + << "can't switch to tab ID " << static_cast(id) << ", not available"; } MOShared::FilesOrigin* ModInfoDialog::getOrigin() @@ -525,7 +516,8 @@ std::vector ModInfoDialog::getOrderedTabNames() const return v; } -void ModInfoDialog::reAddTabs(const std::vector& visibility, ETabs sel) +void ModInfoDialog::reAddTabs( + const std::vector& visibility, ModInfoTabIDs sel) { Q_ASSERT(visibility.size() == m_tabs.size()); diff --git a/src/modinfodialog.h b/src/modinfodialog.h index 9eb00a3b..effb5d98 100644 --- a/src/modinfodialog.h +++ b/src/modinfodialog.h @@ -24,6 +24,7 @@ along with Mod Organizer. If not, see . #include "modinfo.h" #include "tutorabledialog.h" #include "filerenamer.h" +#include "modinfodialogfwd.h" namespace Ui { class ModInfoDialog; } namespace MOShared { class FilesOrigin; } @@ -34,32 +35,6 @@ class Settings; class ModInfoDialogTab; class MainWindow; -bool canPreviewFile(PluginContainer& pluginContainer, bool isArchive, const QString& filename); -bool canOpenFile(bool isArchive, const QString& filename); -bool canExploreFile(bool isArchive, const QString& filename); -bool canHideFile(bool isArchive, const QString& filename); -bool canUnhideFile(bool isArchive, const QString& filename); - -FileRenamer::RenameResults hideFile(FileRenamer& renamer, const QString &oldName); -FileRenamer::RenameResults unhideFile(FileRenamer& renamer, const QString &oldName); - -int naturalCompare(const QString& a, const QString& b); - - -class ElideLeftDelegate : public QStyledItemDelegate -{ -public: - using QStyledItemDelegate::QStyledItemDelegate; - -protected: - void initStyleOption(QStyleOptionViewItem* o, const QModelIndex& i) const - { - QStyledItemDelegate::initStyleOption(o, i); - o->textElideMode = Qt::ElideLeft; - } -}; - - /** * this is a larger dialog used to visualise information about the mod. * @todo this would probably a good place for a plugin-system @@ -70,21 +45,9 @@ class ModInfoDialog : public MOBase::TutorableDialog template friend std::unique_ptr createTab( - ModInfoDialog& d, int index); + ModInfoDialog& d, ModInfoTabIDs index); public: - enum ETabs { - TAB_TEXTFILES, - TAB_INIFILES, - TAB_IMAGES, - TAB_ESPS, - TAB_CONFLICTS, - TAB_CATEGORIES, - TAB_NEXUS, - TAB_NOTES, - TAB_FILETREE - }; - ModInfoDialog( MainWindow* mw, OrganizerCore* core, PluginContainer* plugin, ModInfo::Ptr mod); @@ -93,7 +56,8 @@ public: void setMod(ModInfo::Ptr mod); void setMod(const QString& name); - void setTab(ETabs id); + + void setTab(ModInfoTabIDs id); int exec() override; @@ -130,7 +94,7 @@ private: OrganizerCore* m_core; PluginContainer* m_plugin; std::vector m_tabs; - ETabs m_initialTab; + ModInfoTabIDs m_initialTab; bool m_arrangingTabs; std::vector createTabs(); @@ -144,8 +108,8 @@ private: void updateTabs(bool becauseOriginChanged=false); void feedFiles(bool becauseOriginChanged); void setTabsColors(); - void switchToTab(ETabs id); - void reAddTabs(const std::vector& visibility, ETabs sel); + void switchToTab(ModInfoTabIDs id); + void reAddTabs(const std::vector& visibility, ModInfoTabIDs sel); std::vector getOrderedTabNames() const; bool tryClose(); diff --git a/src/modinfodialogfwd.h b/src/modinfodialogfwd.h new file mode 100644 index 00000000..9ede766f --- /dev/null +++ b/src/modinfodialogfwd.h @@ -0,0 +1,50 @@ +#ifndef MODINFODIALOGFWD_H +#define MODINFODIALOGFWD_H + +#include "filerenamer.h" + +class ModInfo; +using ModInfoPtr = QSharedPointer; + +enum class ModInfoTabIDs +{ + None = -1, + TextFiles = 0, + IniFiles, + Images, + Esps, + Conflicts, + Categories, + Nexus, + Notes, + Filetree +}; + +class PluginContainer; + +bool canPreviewFile(PluginContainer& pluginContainer, bool isArchive, const QString& filename); +bool canOpenFile(bool isArchive, const QString& filename); +bool canExploreFile(bool isArchive, const QString& filename); +bool canHideFile(bool isArchive, const QString& filename); +bool canUnhideFile(bool isArchive, const QString& filename); + +FileRenamer::RenameResults hideFile(FileRenamer& renamer, const QString &oldName); +FileRenamer::RenameResults unhideFile(FileRenamer& renamer, const QString &oldName); + +int naturalCompare(const QString& a, const QString& b); + + +class ElideLeftDelegate : public QStyledItemDelegate +{ +public: + using QStyledItemDelegate::QStyledItemDelegate; + +protected: + void initStyleOption(QStyleOptionViewItem* o, const QModelIndex& i) const + { + QStyledItemDelegate::initStyleOption(o, i); + o->textElideMode = Qt::ElideLeft; + } +}; + +#endif // MODINFODIALOGFWD_H diff --git a/src/modinfodialognexus.cpp b/src/modinfodialognexus.cpp index 81546f58..04683c89 100644 --- a/src/modinfodialognexus.cpp +++ b/src/modinfodialognexus.cpp @@ -108,7 +108,7 @@ void NexusTab::firstActivation() updateWebpage(); } -void NexusTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) +void NexusTab::setMod(ModInfoPtr mod, MOShared::FilesOrigin* origin) { cleanup(); diff --git a/src/modinfodialognexus.h b/src/modinfodialognexus.h index 6478375b..7f894dbf 100644 --- a/src/modinfodialognexus.h +++ b/src/modinfodialognexus.h @@ -40,7 +40,7 @@ public: void clear() override; void update() override; void firstActivation() override; - void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) override; + void setMod(ModInfoPtr mod, MOShared::FilesOrigin* origin) override; bool usesOriginFiles() const override; private: diff --git a/src/modinfodialogtab.cpp b/src/modinfodialogtab.cpp index 554df6df..9748d059 100644 --- a/src/modinfodialogtab.cpp +++ b/src/modinfodialogtab.cpp @@ -2,6 +2,7 @@ #include "ui_modinfodialog.h" #include "texteditor.h" #include "directoryentry.h" +#include "modinfo.h" ModInfoDialogTab::ModInfoDialogTab(ModInfoDialogTabContext cx) : ui(cx.ui), m_core(cx.core), m_plugin(cx.plugin), m_parent(cx.parent), @@ -74,7 +75,7 @@ bool ModInfoDialogTab::usesOriginFiles() const return true; } -void ModInfoDialogTab::setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin) +void ModInfoDialogTab::setMod(ModInfoPtr mod, MOShared::FilesOrigin* origin) { m_mod = mod; m_origin = origin; @@ -86,7 +87,7 @@ ModInfo& ModInfoDialogTab::mod() const return *m_mod; } -ModInfo::Ptr ModInfoDialogTab::modPtr() const +ModInfoPtr ModInfoDialogTab::modPtr() const { Q_ASSERT(m_mod); return m_mod; @@ -97,7 +98,7 @@ MOShared::FilesOrigin* ModInfoDialogTab::origin() const return m_origin; } -int ModInfoDialogTab::tabID() const +ModInfoTabIDs ModInfoDialogTab::tabID() const { return m_tabID; } diff --git a/src/modinfodialogtab.h b/src/modinfodialogtab.h index eb574de0..283d9e73 100644 --- a/src/modinfodialogtab.h +++ b/src/modinfodialogtab.h @@ -1,7 +1,7 @@ #ifndef MODINFODIALOGTAB_H #define MODINFODIALOGTAB_H -#include "modinfo.h" +#include "modinfodialogfwd.h" #include namespace MOShared { class FilesOrigin; } @@ -18,8 +18,8 @@ struct ModInfoDialogTabContext PluginContainer& plugin; QWidget* parent; Ui::ModInfoDialog* ui; - int id; - ModInfo::Ptr mod; + ModInfoTabIDs id; + ModInfoPtr mod; MOShared::FilesOrigin* origin; ModInfoDialogTabContext( @@ -27,8 +27,8 @@ struct ModInfoDialogTabContext PluginContainer& plugin, QWidget* parent, Ui::ModInfoDialog* ui, - int id, - ModInfo::Ptr mod, + ModInfoTabIDs id, + ModInfoPtr mod, MOShared::FilesOrigin* origin) : core(core), plugin(plugin), parent(parent), ui(ui), id(id), mod(mod), origin(origin) @@ -96,7 +96,7 @@ public: // derived classes can override this to connect to events on the mod for // examples (see NexusTab), but must call the base class implementation // - virtual void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin); + virtual void setMod(ModInfoPtr mod, MOShared::FilesOrigin* origin); // this tab should clear its user interface; clear() will always be called // before feedFile() and update() @@ -195,7 +195,7 @@ public: // returns the currently selected mod, can never be empty // - ModInfo::Ptr modPtr() const; + ModInfoPtr modPtr() const; // returns the origin of the selected mod; this can be null for mods that // don't have an origin, like deactivated mods @@ -203,7 +203,7 @@ public: MOShared::FilesOrigin* origin() const; - int tabID() const; + ModInfoTabIDs tabID() const; bool hasData() const; signals: @@ -249,9 +249,9 @@ private: OrganizerCore& m_core; PluginContainer& m_plugin; QWidget* m_parent; - ModInfo::Ptr m_mod; + ModInfoPtr m_mod; MOShared::FilesOrigin* m_origin; - int m_tabID; + ModInfoTabIDs m_tabID; bool m_hasData; bool m_firstActivation; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index e073924b..99ddda1d 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -966,8 +966,8 @@ MOBase::IModInterface *OrganizerCore::installMod(const QString &fileName, "want to configure them now?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) { - m_UserInterface->displayModInformation(modInfo, modIndex, - ModInfoDialog::TAB_INIFILES); + m_UserInterface->displayModInformation( + modInfo, modIndex, ModInfoTabIDs::IniFiles); } m_ModInstalled(modName); m_DownloadManager.markInstalled(fileName); @@ -1033,8 +1033,8 @@ void OrganizerCore::installDownload(int index) "want to configure them now?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)) { - m_UserInterface->displayModInformation(modInfo, modIndex, - ModInfoDialog::TAB_INIFILES); + m_UserInterface->displayModInformation( + modInfo, modIndex, ModInfoTabIDs::IniFiles); } m_ModInstalled(modName); -- cgit v1.3.1 From aeca2a7782a29f3d893f318d91d00109b6451f7e Mon Sep 17 00:00:00 2001 From: Silarn Date: Wed, 3 Jul 2019 14:14:10 -0500 Subject: Restore UI files to VS project and remove unused files --- src/CMakeLists.txt | 9 +-- src/finddialog.ui | 76 --------------------- src/gameinfoimpl.cpp | 56 --------------- src/installdialog.ui | 165 --------------------------------------------- src/simpleinstalldialog.ui | 82 ---------------------- 5 files changed, 3 insertions(+), 385 deletions(-) delete mode 100644 src/finddialog.ui delete mode 100644 src/gameinfoimpl.cpp delete mode 100644 src/installdialog.ui delete mode 100644 src/simpleinstalldialog.ui (limited to 'src/CMakeLists.txt') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 929ee296..f197211a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -253,7 +253,6 @@ SET(organizer_HDRS SET(organizer_UIS transfersavesdialog.ui syncoverwritedialog.ui - simpleinstalldialog.ui settingsdialog.ui selectiondialog.ui queryoverwritedialog.ui @@ -265,8 +264,6 @@ SET(organizer_UIS mainwindow.ui lockeddialog.ui waitingonclosedialog.ui - installdialog.ui - finddialog.ui editexecutablesdialog.ui credentialsdialog.ui categoriesdialog.ui @@ -293,7 +290,7 @@ SET(organizer_RCS ) -source_group(src REGULAR_EXPRESSION ".*\\.(h|cpp)") +source_group(src REGULAR_EXPRESSION ".*\\.(h|cpp|ui)") set(application iuserinterface @@ -457,7 +454,7 @@ set(src_filters foreach(filter in list ${src_filters}) set(files) foreach(d in lists ${${filter}}) - set(files ${files} ${d}.cpp ${d}.h ${d}.inc) + set(files ${files} ${d}.cpp ${d}.h ${d}.inc ${d}.ui) endforeach() source_group(src\\${filter} FILES ${files}) @@ -553,7 +550,7 @@ ELSE() SET(usvfs_name usvfs_x86) ENDIF() -ADD_EXECUTABLE(ModOrganizer WIN32 ${organizer_HDRS} ${organizer_SRCS} ${organizer_RCS} ${organizer_QRCS} ${organizer_translations_qm}) +ADD_EXECUTABLE(ModOrganizer WIN32 ${organizer_HDRS} ${organizer_SRCS} ${organizer_UIS} ${organizer_RCS} ${organizer_QRCS} ${organizer_translations_qm}) TARGET_LINK_LIBRARIES(ModOrganizer Qt5::Widgets Qt5::WinExtras Qt5::WebEngineWidgets Qt5::Quick Qt5::Qml Qt5::QuickWidgets Qt5::Network Qt5::WebSockets diff --git a/src/finddialog.ui b/src/finddialog.ui deleted file mode 100644 index 2c4c80b8..00000000 --- a/src/finddialog.ui +++ /dev/null @@ -1,76 +0,0 @@ - - - FindDialog - - - - 0 - 0 - 400 - 72 - - - - Find - - - - - - - - - - Find what: - - - - - - - Search term - - - Search term - - - - - - - - - - - - - Find next occurence from current file position. - - - Find next occurence from current file position. - - - &Find Next - - - - - - - Close - - - Close - - - Close - - - - - - - - - - diff --git a/src/gameinfoimpl.cpp b/src/gameinfoimpl.cpp deleted file mode 100644 index 61a4c523..00000000 --- a/src/gameinfoimpl.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright (C) 2012 Sebastian Herbord. All rights reserved. - -This file is part of Mod Organizer. - -Mod Organizer is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -Mod Organizer is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Mod Organizer. If not, see . -*/ - -#include "gameinfoimpl.h" -#include -#include -#include - - -using namespace MOBase; -using namespace MOShared; - - -GameInfoImpl::GameInfoImpl() -{ -} - -IGameInfo::Type GameInfoImpl::type() const -{ - switch (GameInfo::instance().getType()) { - case GameInfo::TYPE_OBLIVION: return IGameInfo::TYPE_OBLIVION; - case GameInfo::TYPE_FALLOUT3: return IGameInfo::TYPE_FALLOUT3; - case GameInfo::TYPE_FALLOUT4: return IGameInfo::TYPE_FALLOUT4; - case GameInfo::TYPE_FALLOUTNV: return IGameInfo::TYPE_FALLOUTNV; - case GameInfo::TYPE_SKYRIM: return IGameInfo::TYPE_SKYRIM; - case GameInfo::TYPE_SKYRIMSE: return IGameInfo::TYPE_SKYRIMSE; - default: throw MyException(QObject::tr("invalid game type %1").arg(GameInfo::instance().getType())); - } -} - - -QString GameInfoImpl::path() const -{ - return QDir::fromNativeSeparators(ToQString(GameInfo::instance().getGameDirectory())); -} - -QString GameInfoImpl::binaryName() const -{ - return ToQString(GameInfo::instance().getBinaryName()); -} diff --git a/src/installdialog.ui b/src/installdialog.ui deleted file mode 100644 index 88019c77..00000000 --- a/src/installdialog.ui +++ /dev/null @@ -1,165 +0,0 @@ - - - InstallDialog - - - - 0 - 0 - 516 - 407 - - - - - 16777215 - 16777215 - - - - Install Mods - - - - - - false - - - - - - New Mod - - - - - - - - - 50 - 0 - - - - Name - - - - - - - Pick a name for the mod - - - Pick a name for the mod. This is also used as a directory name, so please don't use characters that are illegal in file names. - - - - - - - - - Content - - - - - - - Qt::CustomContextMenu - - - Content of the archive. You can change the directory structure by using drag&drop. Hint: Also try right clicking... - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This displays the content of the archive. &lt;data&gt; represents the base directory which will map to the game's data directory. You can change the base directory via the right-click context menu and you can move around files via drag&amp;drop</span></p></body></html> - - - - - - true - - - QAbstractItemView::DragDrop - - - QAbstractItemView::ExtendedSelection - - - false - - - - 1 - - - - - - - - - - - - - - 12 - 75 - true - - - - Placeholder - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - OK - - - - - - - Cancel - - - - - - - - - - ArchiveTree - QTreeWidget -
archivetree.h
-
-
- - -
diff --git a/src/simpleinstalldialog.ui b/src/simpleinstalldialog.ui deleted file mode 100644 index a9fdc1d5..00000000 --- a/src/simpleinstalldialog.ui +++ /dev/null @@ -1,82 +0,0 @@ - - - SimpleInstallDialog - - - - 0 - 0 - 400 - 71 - - - - Quick Install - - - - - - - - Name - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Opens a Dialog that allows custom modifications. - - - Opens a Dialog that allows custom modifications. - - - Manual - - - - - - - OK - - - true - - - - - - - Cancel - - - - - - - - - - -- cgit v1.3.1