diff --git a/ci/linux/generate_appimage.sh b/ci/linux/generate_appimage.sh index 11bc7300a7e..06170f0bcfd 100755 --- a/ci/linux/generate_appimage.sh +++ b/ci/linux/generate_appimage.sh @@ -7,7 +7,7 @@ cmake -DCMAKE_INSTALL_PREFIX=$INSTALL_FOLDER/Freespace2 -DCOMPONENT=Unspecified cmake -DCMAKE_INSTALL_PREFIX=$INSTALL_FOLDER/Freespace2 -DCOMPONENT=Freespace2 -P cmake_install.cmake # We need to be a bit creative for determining the AppImage name since we don't want to hard-code the name -FILENAME="$(find $INSTALL_FOLDER/Freespace2/bin -name 'fs2_open_*' -type f -printf "%f\n").AppImage" +FILENAME="$(find $INSTALL_FOLDER/Freespace2/bin -maxdepth 1 -name 'fs2_open_*' -type f -executable -printf "%f\n").AppImage" appimagetool -n "$INSTALL_FOLDER/Freespace2" "$INSTALL_FOLDER/$FILENAME" chmod +x "$INSTALL_FOLDER/$FILENAME" @@ -17,7 +17,7 @@ if [ -f qtfred/cmake_install.cmake ]; then cmake -DCMAKE_INSTALL_PREFIX=$INSTALL_FOLDER/qtFRED -DCOMPONENT=qtFRED -P cmake_install.cmake # We need to be a bit creative for determining the AppImage name since we don't want to hard-code the name - FILENAME="$(find $INSTALL_FOLDER/qtFRED/bin -iname 'qtfred_*' -type f -printf "%f\n").AppImage" + FILENAME="$(find $INSTALL_FOLDER/qtFRED/bin -maxdepth 1 -iname 'qtfred_*' -type f -executable -printf "%f\n").AppImage" appimagetool -n "$INSTALL_FOLDER/qtFRED" "$INSTALL_FOLDER/$FILENAME" chmod +x "$INSTALL_FOLDER/$FILENAME" fi diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 60824b7ea1d..035725104cd 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -53,10 +53,10 @@ ADD_SUBDIRECTORY(lz4) set(HIDAPI_WITH_LIBUSB OFF) set(HIDAPI_WITH_HIDRAW ON) -add_subdirectory(hidapi) +add_subdirectory(hidapi EXCLUDE_FROM_ALL) ADD_SUBDIRECTORY(imgui) if(FSO_BUILD_WITH_OPENXR) - add_subdirectory(openxr) + add_subdirectory(openxr EXCLUDE_FROM_ALL) endif() diff --git a/lib/antlr4.cmake b/lib/antlr4.cmake index 8ba33941438..1967dac5fa0 100644 --- a/lib/antlr4.cmake +++ b/lib/antlr4.cmake @@ -18,7 +18,7 @@ endif() set(WITH_DEMO FALSE) set(ANTLR4_INSTALL FALSE) -add_subdirectory(antlr4-cpp-runtime) +add_subdirectory(antlr4-cpp-runtime EXCLUDE_FROM_ALL) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_LIST_DIR}/antlr4-cpp-runtime/cmake" PARENT_SCOPE) suppress_warnings(antlr4_static) diff --git a/qtfred/CMakeLists.txt b/qtfred/CMakeLists.txt index cadb0413d4a..02afa59e717 100644 --- a/qtfred/CMakeLists.txt +++ b/qtfred/CMakeLists.txt @@ -1,10 +1,12 @@ -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.21) project(qtfred VERSION 1.0.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) +set_policy(CMP0177 NEW) # normalize install() destination paths + option(QT_USE_PRECOMPILED "Use precompiled version of Qt. If disabled the system libraries will be used." OFF) if(QT_USE_PRECOMPILED) @@ -13,6 +15,7 @@ if(QT_USE_PRECOMPILED) else() SET(QT6_INSTALL_ROOT "" CACHE PATH "The path to the Qt6 installation root. May be necessary on windows if the standard find_package fails to find the Qt installation.") + message(STATUS "Using system Qt libraries. Don't distribute these FSO binaries if these are the GPL libraries/plugins!") endif() if(QT6_INSTALL_ROOT) @@ -23,7 +26,6 @@ find_package(Qt6 REQUIRED COMPONENTS Core Widgets OpenGL Help) include(source_groups.cmake) qt_standard_project_setup() add_compile_definitions(QT_DISABLE_DEPRECATED_UP_TO=0x050F00) -INCLUDE(InstallRequiredSystemLibraries) qt6_wrap_ui(QTFRED_UI_GENERATED ${files_UI}) source_group("UI\\Generated" FILES ${QTFRED_UI_GENERATED}) qt_add_executable(qtfred @@ -53,8 +55,6 @@ target_include_directories(qtfred PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ) -set(CMAKE_MAP_IMPORTED_CONFIG_FASTDEBUG Release Debug) - target_link_libraries(qtfred PUBLIC code Qt6::Widgets Qt6::OpenGL Qt6::Help) include(CreateLaunchers) @@ -128,9 +128,11 @@ add_custom_command( COMMENT "Compiling QtFRED help content (qtfred_help.qch)" VERBATIM) -# Pre-build the collection (.qhc) with the .qch already registered. Runtime -# just opens this in read-only mode -- no QHelpEngine::registerDocumentation -# bootstrap, which is unreliable on a fresh .qhc under Qt6. +# Pre-build the collection (.qhc) with the .qch already registered. At runtime +# we copy this to AppDataLocation (writable on all platforms including macOS) and +# open it from there so Qt can write the search index alongside it. We avoid +# creating a fresh .qhc at runtime because QHelpEngine::registerDocumentation +# bootstrapping on a new empty collection is unreliable under Qt6. add_custom_command( OUTPUT "${QTFRED_HELP_QHC}" COMMAND ${CMAKE_COMMAND} -E env @@ -170,48 +172,16 @@ install(FILES "${QTFRED_HELP_QCH}" "${QTFRED_HELP_QHC}" enable_clang_tidy(qtfred) COPY_FILES_TO_TARGET(qtfred) -if(PLATFORM_WINDOWS) - get_target_property(QMAKE_EXE Qt6::qmake IMPORTED_LOCATION) - get_filename_component(QT_BIN_DIR ${_QTFRED_QMAKE} DIRECTORY) - message(STATUS "Qt bin dir: ${QT_BIN_DIR}") - message(STATUS "qmake path: ${QMAKE_EXE}") - - find_program(DEPLOYQT_EXECUTABLE - NAMES windeployqt6 windeployqt - HINTS "${QT_BIN_DIR}" - REQUIRED - ) - - if(NOT DEPLOYQT_EXECUTABLE) - message(WARNING "windeployqt not found in '${QT_BIN_DIR}' — Qt DLLs will not be deployed") - else() - message(STATUS "Found windeployqt: ${DEPLOYQT_EXECUTABLE}") - endif() - - add_custom_command(TARGET qtfred POST_BUILD - COMMAND ${CMAKE_COMMAND} -E echo "Target file is: $" - COMMENT "Debug: checking target file path" - ) +set(QTFRED_DEPLOY_PREFIX "$") +set(QTFRED_DEPLOY_OPTIONS "") - add_custom_command(TARGET qtfred POST_BUILD - COMMAND "${DEPLOYQT_EXECUTABLE}" - --qthelp - $<$,$>:--debug> - $<$:--release> - --dir "$" - $ - COMMENT "Running windeployqt (with Qt Help support)..." - ) - - install( - DIRECTORY "$/" - DESTINATION ${BINARY_DESTINATION} - COMPONENT "qtFRED" - FILES_MATCHING - PATTERN "Qt6Help*.dll" - PATTERN "qsqlite*.dll" - PATTERN "qwindows*.dll" - PATTERN "*.dll" +if(PLATFORM_WINDOWS) + set(QTFRED_DEPLOY_OPTIONS + NO_COMPILER_RUNTIME + DEPLOY_TOOL_OPTIONS + BIN_DIR ${BINARY_DESTINATION} + LIB_DIR ${LIBRAY_DESTINATION} + NO_OVERWRITE ) elseif(PLATFORM_MAC) # Handling of mac resources @@ -233,42 +203,116 @@ elseif(PLATFORM_MAC) COMMENT "Copying resources into bundle..." ) - # fix up Qt libs - find_program(DEPLOYQT_EXECUTABLE - NAMES macdeployqt6 macdeployqt - HINTS "${_QTFRED_QT_BINS}" - REQUIRED + set(QTFRED_DEPLOY_PREFIX "$/..") + set(QTFRED_DEPLOY_OPTIONS + DEPLOY_TOOL_OPTIONS + -codesign=- + NO_APP_STORE_COMPLIANCE + NO_OVERWRITE ) +elseif(PLATFORM_LINUX) + if (FSO_BUILD_APPIMAGE) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/AppRun.in" "${CMAKE_CURRENT_BINARY_DIR}/AppRun.gen" @ONLY) + file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/AppRun-$" + INPUT "${CMAKE_CURRENT_BINARY_DIR}/AppRun.gen") - add_custom_command(TARGET qtfred POST_BUILD - COMMAND "${DEPLOYQT_EXECUTABLE}" - "$" - -codesign=- - COMMENT "Running macdeployqt..." + install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/AppRun-$" DESTINATION "." RENAME "AppRun" + COMPONENT "qtFRED") + + configure_file("${CMAKE_CURRENT_LIST_DIR}/cmake/AppImage.desktop.in" "${CMAKE_CURRENT_BINARY_DIR}/AppImage.desktop.gen" @ONLY) + file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/AppImage-$.desktop" + INPUT "${CMAKE_CURRENT_BINARY_DIR}/AppImage.desktop.gen") + + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/AppImage-$.desktop" DESTINATION "." RENAME "qtfred.desktop" + COMPONENT "qtFRED") + + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/resources/fred_icon.png" DESTINATION "." + COMPONENT "qtFRED") + + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/resources/fonts" DESTINATION ${LIBRAY_DESTINATION} + COMPONENT "qtFRED") + endif() + + add_custom_command( + TARGET qtfred POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different + "${CMAKE_CURRENT_SOURCE_DIR}/resources/fonts" + "$/${LIBRAY_DESTINATION}/fonts" + COMMENT "Copying QtFRED fonts..." + VERBATIM + ) + + set(QTFRED_DEPLOY_OPTIONS + INCLUDE_PLUGINS + eglfs linuxfb minimalegl minimal + offscreen vkkhrdisplay vnc + wayland xcb ) -elseif(FSO_BUILD_APPIMAGE) - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/AppRun.in" "${CMAKE_CURRENT_BINARY_DIR}/AppRun.gen" @ONLY) - file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/AppRun-$" - INPUT "${CMAKE_CURRENT_BINARY_DIR}/AppRun.gen") +endif() - install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/AppRun-$" DESTINATION "." RENAME "AppRun" - COMPONENT "qtFRED") +# create Qt deploy script to fixup Qt libs/plugins +qt_generate_deploy_app_script( + TARGET qtfred + OUTPUT_SCRIPT QTFRED_DEPLOY_SCRIPT + NO_UNSUPPORTED_PLATFORM_ERROR + ${QTFRED_DEPLOY_OPTIONS} +) - configure_file("${CMAKE_CURRENT_LIST_DIR}/cmake/AppImage.desktop.in" "${CMAKE_CURRENT_BINARY_DIR}/AppImage.desktop.gen" @ONLY) - file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/AppImage-$.desktop" - INPUT "${CMAKE_CURRENT_BINARY_DIR}/AppImage.desktop.gen") +if(PLATFORM_LINUX AND QT_USE_PRECOMPILED) + # Using prebuilt Qt libs on Linux poses a problem since the deploy script + # needs to be able to find the Qt libs, which aren't in a normal location. + # So to address this we need to modify the deploy script to change the RPATH + # of the QtFRED binary to include this alternate location, run the deployment + # and then reset the RPATH to it's original value. + # + # WARNING: We do this using undocumented internal functionality of CMake's + # file() command, and requires CMake 3.21 or newer to work. + + # save current RPATH to reset it later + get_target_property(install_rpath_old qtfred INSTALL_RPATH) + # fix delimiters + string(REPLACE ";" ":" QTFRED_RPATH_orig "${install_rpath_old}") + + # set new RPATH to be written to binary + # Note: file(RPATH_SET ...) cannot extend RPATH length so we have to do it + # this way to reserve enough space for the modified script to work properly + set_target_properties(qtfred PROPERTIES + INSTALL_RPATH "${QTFRED_RPATH_orig}:${_QTFRED_QT_LIBS}" + ) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/AppImage-$.desktop" DESTINATION "." RENAME "qtfred.desktop" - COMPONENT "qtFRED") + # swap script name so no other code needs to be changed to use it + set(QTFRED_DEPLOY_SCRIPT_orig ${QTFRED_DEPLOY_SCRIPT}) + set(QTFRED_DEPLOY_SCRIPT "${QTFRED_DEPLOY_SCRIPT_orig}-libfix") - install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/resources/fred_icon.png" DESTINATION "." - COMPONENT "qtFRED") + # create modified deploy script with our RPATH fix/hack + file(GENERATE OUTPUT ${QTFRED_DEPLOY_SCRIPT} CONTENT +"file(RPATH_SET FILE $ NEW_RPATH \"${QTFRED_RPATH_orig}:${_QTFRED_QT_LIBS}\") +include(${QTFRED_DEPLOY_SCRIPT_orig}) +file(RPATH_SET FILE $ NEW_RPATH \"${QTFRED_RPATH_orig}\")\n" + ) endif() -if (FSO_INSTALL_DEBUG_FILES) - if (MSVC) - install(FILES "$" - DESTINATION ${BINARY_DESTINATION} - OPTIONAL) - endif() +# deploy Qt libs/plugins +add_custom_command(TARGET qtfred POST_BUILD + COMMAND ${CMAKE_COMMAND} + -D CMAKE_INSTALL_PREFIX=${QTFRED_DEPLOY_PREFIX} + -D QT_DEPLOY_PREFIX=${QTFRED_DEPLOY_PREFIX} + -D QT_DEPLOY_BIN_DIR=${BINARY_DESTINATION} + -D QT_DEPLOY_LIB_DIR=${LIBRAY_DESTINATION} + -P "${QTFRED_DEPLOY_SCRIPT}" + COMMENT "Running Qt deploy script..." + VERBATIM +) + +install(CODE " + set(QT_DEPLOY_BIN_DIR ${BINARY_DESTINATION}) + set(QT_DEPLOY_LIB_DIR ${LIBRAY_DESTINATION}) +" COMPONENT "qtFRED") + +install(SCRIPT "${QTFRED_DEPLOY_SCRIPT}" COMPONENT "qtFRED") + +if (FSO_INSTALL_DEBUG_FILES AND MSVC) + install(FILES "$" + DESTINATION ${BINARY_DESTINATION} + OPTIONAL) endif() diff --git a/qtfred/cmake/AppRun.in b/qtfred/cmake/AppRun.in index 5111c1c2600..ec61200eb6a 100644 --- a/qtfred/cmake/AppRun.in +++ b/qtfred/cmake/AppRun.in @@ -2,11 +2,4 @@ HERE=$(dirname $(readlink -f "${0}")) export LD_LIBRARY_PATH=${HERE}/lib:$LD_LIBRARY_PATH -# Qt5 doesn't work so well with Wayland for us so prefer the use of X11 unless -# another backend is already specified -# (NOTE: the -platform option will override this so we don't need to test for it) -if [ -z "$QT_QPA_PLATFORM" ]; then - export QT_QPA_PLATFORM="xcb;wayland" -fi - exec "${HERE}/bin/$" "$@" diff --git a/qtfred/resources/fonts/DejaVuSans.ttf b/qtfred/resources/fonts/DejaVuSans.ttf new file mode 100644 index 00000000000..e5f7eecce43 Binary files /dev/null and b/qtfred/resources/fonts/DejaVuSans.ttf differ diff --git a/qtfred/resources/fonts/DejaVuSansMono.ttf b/qtfred/resources/fonts/DejaVuSansMono.ttf new file mode 100644 index 00000000000..f5786022f18 Binary files /dev/null and b/qtfred/resources/fonts/DejaVuSansMono.ttf differ diff --git a/qtfred/resources/fonts/LICENSE b/qtfred/resources/fonts/LICENSE new file mode 100644 index 00000000000..df52c1709be --- /dev/null +++ b/qtfred/resources/fonts/LICENSE @@ -0,0 +1,187 @@ +Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. +Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) + + +Bitstream Vera Fonts Copyright +------------------------------ + +Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is +a trademark of Bitstream, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of the fonts accompanying this license ("Fonts") and associated +documentation files (the "Font Software"), to reproduce and distribute the +Font Software, including without limitation the rights to use, copy, merge, +publish, distribute, and/or sell copies of the Font Software, and to permit +persons to whom the Font Software is furnished to do so, subject to the +following conditions: + +The above copyright and trademark notices and this permission notice shall +be included in all copies of one or more of the Font Software typefaces. + +The Font Software may be modified, altered, or added to, and in particular +the designs of glyphs or characters in the Fonts may be modified and +additional glyphs or characters may be added to the Fonts, only if the fonts +are renamed to names not containing either the words "Bitstream" or the word +"Vera". + +This License becomes null and void to the extent applicable to Fonts or Font +Software that has been modified and is distributed under the "Bitstream +Vera" names. + +The Font Software may be sold as part of a larger software package but no +copy of one or more of the Font Software typefaces may be sold by itself. + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, +TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME +FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING +ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE +FONT SOFTWARE. + +Except as contained in this notice, the names of Gnome, the Gnome +Foundation, and Bitstream Inc., shall not be used in advertising or +otherwise to promote the sale, use or other dealings in this Font Software +without prior written authorization from the Gnome Foundation or Bitstream +Inc., respectively. For further information, contact: fonts at gnome dot +org. + +Arev Fonts Copyright +------------------------------ + +Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the fonts accompanying this license ("Fonts") and +associated documentation files (the "Font Software"), to reproduce +and distribute the modifications to the Bitstream Vera Font Software, +including without limitation the rights to use, copy, merge, publish, +distribute, and/or sell copies of the Font Software, and to permit +persons to whom the Font Software is furnished to do so, subject to +the following conditions: + +The above copyright and trademark notices and this permission notice +shall be included in all copies of one or more of the Font Software +typefaces. + +The Font Software may be modified, altered, or added to, and in +particular the designs of glyphs or characters in the Fonts may be +modified and additional glyphs or characters may be added to the +Fonts, only if the fonts are renamed to names not containing either +the words "Tavmjong Bah" or the word "Arev". + +This License becomes null and void to the extent applicable to Fonts +or Font Software that has been modified and is distributed under the +"Tavmjong Bah Arev" names. + +The Font Software may be sold as part of a larger software package but +no copy of one or more of the Font Software typefaces may be sold by +itself. + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL +TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. + +Except as contained in this notice, the name of Tavmjong Bah shall not +be used in advertising or otherwise to promote the sale, use or other +dealings in this Font Software without prior written authorization +from Tavmjong Bah. For further information, contact: tavmjong @ free +. fr. + +TeX Gyre DJV Math +----------------- +Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. + +Math extensions done by B. Jackowski, P. Strzelczyk and P. Pianowski +(on behalf of TeX users groups) are in public domain. + +Letters imported from Euler Fraktur from AMSfonts are (c) American +Mathematical Society (see below). +Bitstream Vera Fonts Copyright +Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera +is a trademark of Bitstream, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of the fonts accompanying this license (“Fonts”) and associated +documentation +files (the “Font Software”), to reproduce and distribute the Font Software, +including without limitation the rights to use, copy, merge, publish, +distribute, +and/or sell copies of the Font Software, and to permit persons to whom +the Font Software is furnished to do so, subject to the following +conditions: + +The above copyright and trademark notices and this permission notice +shall be +included in all copies of one or more of the Font Software typefaces. + +The Font Software may be modified, altered, or added to, and in particular +the designs of glyphs or characters in the Fonts may be modified and +additional +glyphs or characters may be added to the Fonts, only if the fonts are +renamed +to names not containing either the words “Bitstream” or the word “Vera”. + +This License becomes null and void to the extent applicable to Fonts or +Font Software +that has been modified and is distributed under the “Bitstream Vera” +names. + +The Font Software may be sold as part of a larger software package but +no copy +of one or more of the Font Software typefaces may be sold by itself. + +THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, +TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME +FOUNDATION +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, +SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN +ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR +INABILITY TO USE +THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. +Except as contained in this notice, the names of GNOME, the GNOME +Foundation, +and Bitstream Inc., shall not be used in advertising or otherwise to promote +the sale, use or other dealings in this Font Software without prior written +authorization from the GNOME Foundation or Bitstream Inc., respectively. +For further information, contact: fonts at gnome dot org. + +AMSFonts (v. 2.2) copyright + +The PostScript Type 1 implementation of the AMSFonts produced by and +previously distributed by Blue Sky Research and Y&Y, Inc. are now freely +available for general use. This has been accomplished through the +cooperation +of a consortium of scientific publishers with Blue Sky Research and Y&Y. +Members of this consortium include: + +Elsevier Science IBM Corporation Society for Industrial and Applied +Mathematics (SIAM) Springer-Verlag American Mathematical Society (AMS) + +In order to assure the authenticity of these fonts, copyright will be +held by +the American Mathematical Society. This is not meant to restrict in any way +the legitimate use of the fonts, such as (but not limited to) electronic +distribution of documents containing these fonts, inclusion of these fonts +into other public domain or commercial font collections or computer +applications, use of the outline data to create derivative fonts and/or +faces, etc. However, the AMS does require that the AMS copyright notice be +removed from any derivative versions of the fonts which have been altered in +any way. In addition, to ensure the fidelity of TeX documents using Computer +Modern fonts, Professor Donald Knuth, creator of the Computer Modern faces, +has requested that any alterations which yield different font metrics be +given a different name. + +$Id$ diff --git a/qtfred/src/mission/dialogs/HelpTopicsDialogModel.cpp b/qtfred/src/mission/dialogs/HelpTopicsDialogModel.cpp index 66c51d1e4ed..7a3843b4a6e 100644 --- a/qtfred/src/mission/dialogs/HelpTopicsDialogModel.cpp +++ b/qtfred/src/mission/dialogs/HelpTopicsDialogModel.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "cfile/cfile.h" @@ -59,17 +60,53 @@ bool HelpTopicsDialogModel::ensureEngineReady() { if (_helpEngine != nullptr) return !_helpEngine->registeredDocumentations().isEmpty(); - const QString collectionFile = resolveCollectionFile(); - if (!QFileInfo::exists(collectionFile)) { + const QString bundleQhcPath = resolveBundleFile(QStringLiteral("help/qtfred_help.qhc")); + const QString bundleQchPath = resolveBundleFile(QStringLiteral("help/qtfred_help.qch")); + + if (!QFileInfo::exists(bundleQhcPath)) { mprintf(("QtFRED help: collection file not found at %s\n", - collectionFile.toUtf8().constData())); + bundleQhcPath.toUtf8().constData())); + return false; + } + if (!QFileInfo::exists(bundleQchPath)) { + mprintf(("QtFRED help: content file not found at %s\n", + bundleQchPath.toUtf8().constData())); return false; } + // The collection must live in a writable location so Qt can write the search index + // alongside it. On macOS the app bundle is read-only. We keep an up-to-date copy in + // AppDataLocation and update it from the bundle when it's missing or newer. + // We use a bundle-side pre-built .qhc (not a fresh one) to avoid the Qt6 issue where + // setupData()+registerDocumentation() bootstrapping on an empty collection is unreliable. + const QString helpDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + + QStringLiteral("/help"); + QDir().mkpath(helpDir); + const QString writableQhcPath = helpDir + QStringLiteral("/qtfred_help.qhc"); + + // We need to know whether the AppDataLocation copy is already for this exact build + // before opening the engine. A small marker file stores the bundle .qch path that + // was used when the copy was made. This correctly handles switching between any build + // versions (older or newer) because the comparison is path-based, not time-based. + const QString markerPath = helpDir + QStringLiteral("/qtfred_help_build.txt"); + QString storedBundle; + { + QFile f(markerPath); + if (f.open(QIODevice::ReadOnly)) + storedBundle = QString::fromUtf8(f.readAll()).trimmed(); + } + + if (storedBundle != bundleQchPath) { + QFile::remove(writableQhcPath); + if (!QFile::copy(bundleQhcPath, writableQhcPath)) { + mprintf(("QtFRED help: could not copy collection to %s\n", + writableQhcPath.toUtf8().constData())); + return false; + } + } + // Parent to qApp so Qt manages the lifetime. Destroyed cleanly before the event loop exits. - // The .qhc ships with the application with the .qch already registered. We open it as read-only. - _helpEngine = new QHelpEngine(collectionFile, qApp); - _helpEngine->setReadOnly(true); + _helpEngine = new QHelpEngine(writableQhcPath, qApp); if (!_helpEngine->setupData()) { mprintf(("QtFRED help: could not initialize engine: %s\n", _helpEngine->error().toUtf8().constData())); @@ -78,11 +115,32 @@ bool HelpTopicsDialogModel::ensureEngineReady() { return false; } + // The copied .qhc stores a relative path to the .qch that won't resolve from + // AppDataLocation. Re-register using the absolute bundle path. + const QStringList docs = _helpEngine->registeredDocumentations(); + if (!docs.isEmpty()) { + const QString registeredQch = _helpEngine->documentationFileName(docs.first()); + if (registeredQch != bundleQchPath) { + _helpEngine->unregisterDocumentation(docs.first()); + if (!_helpEngine->registerDocumentation(bundleQchPath)) { + mprintf(("QtFRED help: could not re-register documentation: %s\n", + _helpEngine->error().toUtf8().constData())); + delete _helpEngine; + _helpEngine = nullptr; + return false; + } + // Update the marker to record which build this collection now points to. + QFile f(markerPath); + if (f.open(QIODevice::WriteOnly)) + f.write(bundleQchPath.toUtf8()); + } + } + return !_helpEngine->registeredDocumentations().isEmpty(); } // --------------------------------------------------------------------------- -QString HelpTopicsDialogModel::resolveCollectionFile() { +QString HelpTopicsDialogModel::resolveBundleFile(const QString& relativePath) { QDir dir(QCoreApplication::applicationDirPath()); #ifdef Q_OS_MACOS @@ -90,8 +148,7 @@ QString HelpTopicsDialogModel::resolveCollectionFile() { dir.cd("Resources"); #endif - return dir.filePath( - QStringLiteral("help/qtfred_help.qhc")); + return dir.filePath(relativePath); } // --------------------------------------------------------------------------- diff --git a/qtfred/src/mission/dialogs/HelpTopicsDialogModel.h b/qtfred/src/mission/dialogs/HelpTopicsDialogModel.h index 0de667bff77..a4271280c34 100644 --- a/qtfred/src/mission/dialogs/HelpTopicsDialogModel.h +++ b/qtfred/src/mission/dialogs/HelpTopicsDialogModel.h @@ -47,7 +47,7 @@ class HelpTopicsDialogModel : public QObject { static QByteArray loadTutorialAsset(const QString& urlPath); private: - static QString resolveCollectionFile(); + static QString resolveBundleFile(const QString& relativePath); static QString extractHtmlTitle(const QString& filePath); static QList discoverTutorials();