mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-30 03:45:32 +00:00
Merge branch 'master' into osx-ogre-19
Conflicts: CMakeLists.txt
This commit is contained in:
commit
58add97676
763 changed files with 30535 additions and 12755 deletions
53
.gitignore
vendored
53
.gitignore
vendored
|
@ -1,24 +1,55 @@
|
|||
build
|
||||
*~
|
||||
Doxygen
|
||||
prebuilt
|
||||
apps/openmw/config.hpp
|
||||
Docs/mainpage.hpp
|
||||
## make
|
||||
CMakeFiles
|
||||
*/CMakeFiles
|
||||
CMakeCache.txt
|
||||
moc_*.cxx
|
||||
cmake_install.cmake
|
||||
*.[ao]
|
||||
Makefile
|
||||
makefile
|
||||
data
|
||||
build
|
||||
prebuilt
|
||||
|
||||
## doxygen
|
||||
Doxygen
|
||||
|
||||
## ides/editors
|
||||
*~
|
||||
*.kdev4
|
||||
CMakeLists.txt.user
|
||||
*.swp
|
||||
*.swo
|
||||
*.kate-swp
|
||||
.cproject
|
||||
.project
|
||||
.settings/
|
||||
.settings
|
||||
.directory
|
||||
## qt-creator
|
||||
CMakeLists.txt.user*
|
||||
|
||||
## resources
|
||||
data
|
||||
resources
|
||||
/*.cfg
|
||||
/*.desktop
|
||||
/*.install
|
||||
|
||||
## binaries
|
||||
/esmtool
|
||||
/mwiniimport
|
||||
/omwlauncher
|
||||
/openmw
|
||||
/opencs
|
||||
|
||||
## generated objects
|
||||
apps/openmw/config.hpp
|
||||
components/version/version.hpp
|
||||
Docs/mainpage.hpp
|
||||
moc_*.cxx
|
||||
*.cxx_parameters
|
||||
*qrc_launcher.cxx
|
||||
*qrc_resources.cxx
|
||||
*__*
|
||||
*ui_datafilespage.h
|
||||
*ui_graphicspage.h
|
||||
*ui_mainwindow.h
|
||||
*ui_playpage.h
|
||||
*.[ao]
|
||||
*.so
|
||||
|
|
24
.travis.yml
24
.travis.yml
|
@ -4,18 +4,18 @@ compiler:
|
|||
branches:
|
||||
only:
|
||||
- master
|
||||
- next
|
||||
- /openmw-.*$/
|
||||
before_install:
|
||||
- pwd
|
||||
- git submodule update --init --recursive
|
||||
- git fetch --tags
|
||||
- echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse"
|
||||
- echo "yes" | sudo apt-add-repository ppa:openmw/deps
|
||||
- echo "yes" | sudo apt-add-repository ppa:openmw/openmw
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq libboost-all-dev libgtest-dev google-mock libzzip-dev
|
||||
- sudo apt-get install -qq libqt4-dev libxaw7-dev libxrandr-dev libfreeimage-dev libpng-dev
|
||||
- sudo apt-get install -qq libopenal-dev libmpg123-dev libsndfile1-dev
|
||||
- sudo apt-get install -qq libavcodec-dev libavformat-dev libavdevice-dev libavutil-dev libswscale-dev libpostproc-dev
|
||||
- sudo apt-get install -qq libbullet-dev libogre-static-dev libmygui-static-dev libsdl2-static-dev libunshield-dev
|
||||
- sudo apt-get install -qq libboost-all-dev libgtest-dev google-mock
|
||||
- sudo apt-get install -qq libqt4-dev
|
||||
- sudo apt-get install -qq libopenal-dev
|
||||
- sudo apt-get install -qq libavcodec-dev libavformat-dev libavutil-dev libswscale-dev
|
||||
- sudo apt-get install -qq libbullet-dev libogre-1.9-dev libmygui-dev libsdl2-dev libunshield-dev
|
||||
- sudo mkdir /usr/src/gtest/build
|
||||
- cd /usr/src/gtest/build
|
||||
- sudo cmake .. -DBUILD_SHARED_LIBS=1
|
||||
|
@ -26,7 +26,7 @@ before_script:
|
|||
- cd -
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -DOGRE_STATIC=1 -DMYGUI_STATIC=1 -DBOOST_STATIC=1 -DSDL2_STATIC=1 -DBUILD_WITH_CODE_COVERAGE=1 -DBUILD_UNITTESTS=1
|
||||
- cmake .. -DBUILD_WITH_CODE_COVERAGE=1 -DBUILD_UNITTESTS=1 -DBUILD_WITH_DPKG=1
|
||||
script:
|
||||
- make -j4
|
||||
after_script:
|
||||
|
@ -37,3 +37,9 @@ notifications:
|
|||
email:
|
||||
on_success: change
|
||||
on_failure: always
|
||||
irc:
|
||||
channels:
|
||||
- "chat.freenode.net#openmw"
|
||||
on_success: change
|
||||
on_failure: always
|
||||
|
||||
|
|
120
CMakeLists.txt
120
CMakeLists.txt
|
@ -14,15 +14,30 @@ endif (APPLE)
|
|||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
|
||||
|
||||
include (OpenMWMacros)
|
||||
include(OpenMWMacros)
|
||||
|
||||
# Version
|
||||
|
||||
set (OPENMW_VERSION_MAJOR 0)
|
||||
set (OPENMW_VERSION_MINOR 26)
|
||||
set (OPENMW_VERSION_RELEASE 0)
|
||||
include(GetGitRevisionDescription)
|
||||
|
||||
set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
|
||||
get_git_tag_revision(TAGHASH --tags --max-count=1 "HEAD...")
|
||||
get_git_head_revision(REFSPEC COMMITHASH)
|
||||
git_describe(VERSION --tags ${TAGHASH})
|
||||
|
||||
string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" MATCH "${VERSION}")
|
||||
if (MATCH)
|
||||
string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" OPENMW_VERSION_MAJOR "${VERSION}")
|
||||
string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_MINOR "${VERSION}")
|
||||
string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_RELEASE "${VERSION}")
|
||||
|
||||
set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
|
||||
set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}")
|
||||
set(OPENMW_VERSION_TAGHASH "${TAGHASH}")
|
||||
|
||||
message(STATUS "Configuring OpenMW ${OPENMW_VERSION}...")
|
||||
else (MATCH)
|
||||
message(FATAL_ERROR "Failed to get valid version information from Git")
|
||||
endif (MATCH)
|
||||
|
||||
# doxygen main page
|
||||
|
||||
|
@ -50,7 +65,12 @@ option(USE_MPG123 "use mpg123 + libsndfile for sound" ON)
|
|||
# OS X deployment
|
||||
option(OPENMW_OSX_DEPLOYMENT OFF)
|
||||
|
||||
find_program(DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems")
|
||||
if(UNIX AND NOT APPLE)
|
||||
option(BUILD_WITH_DPKG "enable dpkg-based install for debian and debian derivatives" OFF)
|
||||
if(BUILD_WITH_DPKG)
|
||||
find_program(DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems")
|
||||
endif(BUILD_WITH_DPKG)
|
||||
endif(UNIX AND NOT APPLE)
|
||||
|
||||
# Location of morrowind data files
|
||||
if (APPLE)
|
||||
|
@ -79,7 +99,6 @@ set(OENGINE_OGRE
|
|||
${LIBDIR}/openengine/ogre/renderer.cpp
|
||||
${LIBDIR}/openengine/ogre/fader.cpp
|
||||
${LIBDIR}/openengine/ogre/lights.cpp
|
||||
${LIBDIR}/openengine/ogre/particles.cpp
|
||||
${LIBDIR}/openengine/ogre/selectionbuffer.cpp
|
||||
${LIBDIR}/openengine/ogre/imagerotate.cpp
|
||||
)
|
||||
|
@ -89,14 +108,10 @@ set(OENGINE_GUI
|
|||
)
|
||||
|
||||
set(OENGINE_BULLET
|
||||
${LIBDIR}/openengine/bullet/btKinematicCharacterController.cpp
|
||||
${LIBDIR}/openengine/bullet/btKinematicCharacterController.h
|
||||
${LIBDIR}/openengine/bullet/BtOgre.cpp
|
||||
${LIBDIR}/openengine/bullet/BtOgreExtras.h
|
||||
${LIBDIR}/openengine/bullet/BtOgreGP.h
|
||||
${LIBDIR}/openengine/bullet/BtOgrePG.h
|
||||
${LIBDIR}/openengine/bullet/CMotionState.cpp
|
||||
${LIBDIR}/openengine/bullet/CMotionState.h
|
||||
${LIBDIR}/openengine/bullet/physic.cpp
|
||||
${LIBDIR}/openengine/bullet/physic.hpp
|
||||
${LIBDIR}/openengine/bullet/BulletShapeLoader.cpp
|
||||
|
@ -181,10 +196,11 @@ if (WIN32)
|
|||
set(Boost_USE_STATIC_LIBS ON)
|
||||
set(PLATFORM_INCLUDE_DIR "platform")
|
||||
add_definitions(-DBOOST_ALL_NO_LIB)
|
||||
|
||||
# Suppress WinMain(), provided by SDL
|
||||
add_definitions(-DSDL_MAIN_HANDLED)
|
||||
else (WIN32)
|
||||
set(PLATFORM_INCLUDE_DIR "")
|
||||
find_path (UUID_INCLUDE_DIR uuid/uuid.h)
|
||||
include_directories(${UUID_INCLUDE_DIR})
|
||||
endif (WIN32)
|
||||
if (MSVC10)
|
||||
set(PLATFORM_INCLUDE_DIR "")
|
||||
|
@ -208,7 +224,7 @@ if (HAVE_UNORDERED_MAP)
|
|||
endif ()
|
||||
|
||||
|
||||
set(BOOST_COMPONENTS system filesystem program_options thread date_time wave)
|
||||
set(BOOST_COMPONENTS system filesystem program_options)
|
||||
|
||||
IF(BOOST_STATIC)
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
|
@ -236,7 +252,6 @@ include_directories("."
|
|||
${MYGUI_INCLUDE_DIRS}
|
||||
${MYGUI_PLATFORM_INCLUDE_DIRS}
|
||||
${OPENAL_INCLUDE_DIR}
|
||||
${UUID_INCLUDE_DIR}
|
||||
${LIBDIR}
|
||||
)
|
||||
|
||||
|
@ -318,6 +333,9 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
|
|||
configure_file(${OpenMW_SOURCE_DIR}/files/opencs.cfg
|
||||
"${OpenMW_BINARY_DIR}/opencs.cfg")
|
||||
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters
|
||||
"${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY)
|
||||
|
||||
if (NOT WIN32 AND NOT APPLE)
|
||||
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop
|
||||
"${OpenMW_BINARY_DIR}/openmw.desktop")
|
||||
|
@ -378,7 +396,6 @@ IF(NOT WIN32 AND NOT APPLE)
|
|||
|
||||
# Install licenses
|
||||
INSTALL(FILES "DejaVu Font License.txt" DESTINATION "${LICDIR}" )
|
||||
INSTALL(FILES "Daedric Font License.txt" DESTINATION "${LICDIR}" )
|
||||
INSTALL(FILES "OFL.txt" DESTINATION "${LICDIR}" )
|
||||
INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" )
|
||||
ENDIF (DPKG_PROGRAM)
|
||||
|
@ -402,46 +419,6 @@ IF(NOT WIN32 AND NOT APPLE)
|
|||
# Install resources
|
||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT "Resources")
|
||||
INSTALL(DIRECTORY DESTINATION "${DATADIR}/data" COMPONENT "Resources")
|
||||
|
||||
IF (DPKG_PROGRAM)
|
||||
## Debian Specific
|
||||
IF(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.git")
|
||||
EXEC_PROGRAM("git" ${CMAKE_CURRENT_SOURCE_DIR} ARGS "describe" OUTPUT_VARIABLE GIT_VERSION )
|
||||
STRING(REGEX REPLACE "openmw-" "" VERSION_STRING "${GIT_VERSION}")
|
||||
EXEC_PROGRAM("git" ARGS "config --get user.name" OUTPUT_VARIABLE GIT_NAME )
|
||||
EXEC_PROGRAM("git" ARGS "config --get user.email" OUTPUT_VARIABLE GIT_EMAIL)
|
||||
SET(PACKAGE_MAINTAINER "${GIT_NAME} <${GIT_EMAIL}>")
|
||||
ELSE()
|
||||
SET(VERSION_STRING "${OPENMW_VERSION}")
|
||||
SET(PACKAGE_MAINTAINER "unknown")
|
||||
ENDIF()
|
||||
|
||||
SET(CPACK_GENERATOR "DEB")
|
||||
SET(CPACK_PACKAGE_NAME "openmw")
|
||||
SET(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://openmw.org")
|
||||
SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
|
||||
SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "${PACKAGE_MAINTAINER}")
|
||||
SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "A reimplementation of The Elder Scrolls III: Morrowind
|
||||
OpenMW is a reimplementation of the Bethesda Game Studios game The Elder Scrolls III: Morrowind.
|
||||
Data files from the original game is required to run it.")
|
||||
SET(CPACK_DEBIAN_PACKAGE_NAME "openmw")
|
||||
SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}")
|
||||
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW bsatool;Bsatool esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter")
|
||||
SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libuuid1 (>= 2.17.2), libqtgui4 (>= 4.7.0)")
|
||||
|
||||
SET(CPACK_DEBIAN_PACKAGE_SECTION "Games")
|
||||
|
||||
STRING(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_PACKAGE_NAME_LOWERCASE)
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND ${DPKG_PROGRAM} --print-architecture
|
||||
OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME_LOWERCASE}_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
|
||||
|
||||
|
||||
INCLUDE(CPack)
|
||||
ENDIF(DPKG_PROGRAM)
|
||||
ENDIF(NOT WIN32 AND NOT APPLE)
|
||||
|
||||
if(WIN32)
|
||||
|
@ -453,13 +430,22 @@ if(WIN32)
|
|||
"${OpenMW_SOURCE_DIR}/GPL3.txt"
|
||||
"${OpenMW_SOURCE_DIR}/OFL.txt"
|
||||
"${OpenMW_SOURCE_DIR}/DejaVu Font License.txt"
|
||||
"${OpenMW_SOURCE_DIR}/Daedric Font License.txt"
|
||||
"${OpenMW_BINARY_DIR}/settings-default.cfg"
|
||||
"${OpenMW_BINARY_DIR}/transparency-overrides.cfg"
|
||||
"${OpenMW_BINARY_DIR}/Release/mwiniimport.exe"
|
||||
"${OpenMW_BINARY_DIR}/Release/omwlauncher.exe"
|
||||
"${OpenMW_BINARY_DIR}/Release/openmw.exe"
|
||||
DESTINATION ".")
|
||||
|
||||
IF(BUILD_LAUNCHER)
|
||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/omwlauncher.exe" DESTINATION ".")
|
||||
ENDIF(BUILD_LAUNCHER)
|
||||
IF(BUILD_MWINIIMPORTER)
|
||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/mwiniimport.exe" DESTINATION ".")
|
||||
ENDIF(BUILD_MWINIIMPORTER)
|
||||
IF(BUILD_OPENCS)
|
||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/opencs.exe" DESTINATION ".")
|
||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.cfg" DESTINATION ".")
|
||||
ENDIF(BUILD_OPENCS)
|
||||
|
||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".")
|
||||
|
||||
SET(CPACK_GENERATOR "NSIS")
|
||||
|
@ -469,7 +455,13 @@ if(WIN32)
|
|||
SET(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR})
|
||||
SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR})
|
||||
SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
|
||||
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW;omwlauncher;OpenMW Launcher")
|
||||
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW")
|
||||
IF(BUILD_LAUNCHER)
|
||||
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};omwlauncher;OpenMW Launcher")
|
||||
ENDIF(BUILD_LAUNCHER)
|
||||
IF(BUILD_OPENCS)
|
||||
SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};opencs;OpenMW Construction Set")
|
||||
ENDIF(BUILD_OPENCS)
|
||||
SET(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\readme.txt'")
|
||||
SET(CPACK_NSIS_DELETE_ICONS_EXTRA "
|
||||
!insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP
|
||||
|
@ -666,8 +658,11 @@ if (APPLE)
|
|||
set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR})
|
||||
set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
|
||||
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}")
|
||||
set(OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}")
|
||||
|
||||
set(OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/OpenCS.app")
|
||||
|
||||
set(PLUGINS "")
|
||||
set(ABSOLUTE_PLUGINS "")
|
||||
|
||||
foreach (PLUGIN ${USED_OGRE_PLUGINS})
|
||||
|
@ -725,7 +720,8 @@ if (APPLE)
|
|||
cmake_policy(SET CMP0009 OLD)
|
||||
set(BU_CHMOD_BUNDLE_ITEMS ON)
|
||||
include(BundleUtilities)
|
||||
fixup_bundle(\"${APPS}\" \"\" \"${DIRS}\")
|
||||
fixup_bundle(\"${OPENMW_APP}\" \"${PLUGINS}\" \"${DIRS}\")
|
||||
fixup_bundle(\"${OPENCS_APP}\" \"\" \"${DIRS}\")
|
||||
" COMPONENT Runtime)
|
||||
include(CPack)
|
||||
endif (APPLE)
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
Dongle's Oblivion Daedric font set
|
||||
http://www.uesp.net/wiki/Lore:Daedric_Alphabet#Daedric_Font
|
||||
|
||||
---------------------------------------------------
|
||||
|
||||
This was done entirely as a personal project. Bethesda Softworks graciously granted me the permission for it. I am not connected with them in any way.
|
||||
You may freely use these fonts to create anything you'd like. You may re-distribute the fonts freely, over the Internet, or by any other means. Always keep the .zip file intact, and this read me included.
|
||||
Please do not modify and redistribute the fonts without my permission.
|
||||
You may NOT sell any of these fonts under any circumstances. This includes putting them on compilation font CDs for sale, putting them in a "members only" pay-area of a website, or any other means of financial gain connected in ANY way with the redistribution of any of these fonts.
|
||||
You have my permission to create and sell any artwork made with these fonts, however you may need to contact Bethesda Softworks before doing so.
|
160
README_Mac.md
160
README_Mac.md
|
@ -1,160 +0,0 @@
|
|||
#Getting OpenMW Working on OS X
|
||||
|
||||
## Initial setup
|
||||
First of all, clone OpenMW repo.
|
||||
|
||||
$ git clone github.com/zinnschlag/openmw
|
||||
|
||||
Or use your github url if you forked.
|
||||
|
||||
About dependencies: I prefer not to install them globally (i. e. in /usr/local/), so I'm installing them in directory in my home directory. If OpenMW sources is in $HOME/path/openmw, I'm using $HOME/path/libs/root as prefix for boost and other libs.
|
||||
|
||||
It's useful to create env var for lib install prefix:
|
||||
|
||||
$ export OMW_LIB_PREFIX=$HOME/path/libs/root`
|
||||
|
||||
Most of libs can be installed from [Homebrew][homebrew]. Only mpg123 needs to be installed from source (due to lack of universal compilation support). I think that some of libs can be installed from MacPorts or Fink too.
|
||||
|
||||
As OpenMW currently only supports i386 architecture on OS X, denendencies also should support it. Set some env vars in current terminal:
|
||||
|
||||
$ export CFLAGS="-arch i386"
|
||||
$ export CXXFLAGS="-arch i386"
|
||||
$ export LDFLAGS="-arch i386"
|
||||
|
||||
If you close your terminal, you should set env vars again before pcoceeding to next steps!
|
||||
|
||||
## Boost
|
||||
Download [boost][boost] and install it with the following command:
|
||||
|
||||
$ cd /path/to/boost/source
|
||||
$ ./bootstrap.sh --prefix=$OMW_LIB_PREFIX
|
||||
$ ./bjam --build-dir=build --layout=versioned \
|
||||
--toolset=darwin architecture=x86 address-model=32 \
|
||||
--link-shared,static --prefix=$OMW_LIB_PREFIX install
|
||||
|
||||
|
||||
Alternatively you can install boost with homebrew:
|
||||
|
||||
$ brew install boost --universal
|
||||
|
||||
I think MacPorts also should support universal build for boost.
|
||||
|
||||
## Ogre
|
||||
Download [Ogre][] SDK (tested with 1.7.3), unpack it somewhere and move
|
||||
`lib/Release/Ogre.framework` into `/Library/Frameworks`.
|
||||
|
||||
## OIS
|
||||
Download patched [OIS][] and use the XCode project provided. Be sure to set your build architecture to
|
||||
`i386`. Once it built, locate built OIS.framework with Xcode and move it to `/Library/Frameworks`.
|
||||
|
||||
## mpg123
|
||||
Download [MPG 123][mpg123] and build it:
|
||||
|
||||
$ cd /path/to/mpg123/source
|
||||
$ ./configure --prefix=$OMW_LIB_PREFIX --disable-debug \
|
||||
--disable-dependency-tracking \
|
||||
--with-optimization=4 \
|
||||
--with-audio=dummy \
|
||||
--with-default-audio=dummy \
|
||||
--with-cpu=sse_alone \
|
||||
$ make install
|
||||
|
||||
## libsndfile
|
||||
Download [libsndfile][] and build it:
|
||||
|
||||
$ cd /path/to/libsndfile/source
|
||||
$ ./configure --prefix=$OMW_LIB_PREFIX \
|
||||
--disable-dependency-tracking
|
||||
$ make install
|
||||
|
||||
or install with homebrew:
|
||||
|
||||
$ brew install libsndfile --universal
|
||||
|
||||
## Bullet
|
||||
Download [Bullet][] and build it:
|
||||
|
||||
$ cd /path/to/bullet/source
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=$OMW_LIB_PREFIX \
|
||||
-DBUILD_EXTRAS=OFF \
|
||||
-DBUILD_DEMOS=OFF \
|
||||
-DCMAKE_OSX_ARCHITECTURES=i386 \
|
||||
-DCMAKE_INSTALL_NAME_DIR=$OMW_LIB_RPEFIX/lib \
|
||||
-G"Unix Makefiles" ../
|
||||
$ make install
|
||||
|
||||
or install with homebrew:
|
||||
|
||||
$ brew install bullet --HEAD --universal
|
||||
|
||||
I prefer head because 2.79 has some issue which causes OpenMW to lag. Also you can edit formula and install 2.77, which is stable and haven't mentioned issue.
|
||||
|
||||
## Qt
|
||||
Install [Qt][qt]. Qt SDK distributed by Nokia is not an option because it's 64 bit only, and OpenMW currently doesn't build for 64 bit on OS X. I'm installing it from Homebrew:
|
||||
|
||||
$ brew install qt --universal
|
||||
|
||||
## Run CMake
|
||||
Generate the Makefile for OpenMW as follows and build OpenMW:
|
||||
|
||||
$ mkdir /path/to/openmw/build/dir
|
||||
$ cd /path/to/open/build/dir
|
||||
$ cmake \
|
||||
-D CMAKE_OSX_ARCHITECTURES=i386 \
|
||||
-D OGRE_SDK=/path/to/ogre/sdk \
|
||||
-D BOOST_INCLUDEDIR=$OMW_LIB_PREFIX/include/boost-1_45 \
|
||||
-D BOOST_LIBRARYDIR=$OMW_LIB_PREFIX/lib \
|
||||
-D SNDFILE_INCLUDE_DIR=$OMW_LIB_PREFIX/include \
|
||||
-D SNDFILE_LIBRARY=$OMW_LIB_PREFIX/lib/libsndfile.a \
|
||||
-D MPG123_LIBRARY=$OMW_LIB_PREFIX/lib/libmpg123.a \
|
||||
-D MPG123_INCLUDE_DIR=$OMW_LIB_PREFIX/include \
|
||||
-D BULLET_DYNAMICS_LIBRARY=$OMW_LIB_PREFIX/lib/libBulletDynamics.a \
|
||||
-D BULLET_COLLISION_LIBRARY=$OMW_LIB_PREFIX/lib/libBulletCollision.a \
|
||||
-D BULLET_MATH_LIBRARY=$OMW_LIB_PREFIX/lib/libLinearMath.a \
|
||||
-D BULLET_SOFTBODY_LIBRARY=$OMW_LIB_PREFIX/lib/libBulletSoftBody.a \
|
||||
-D BULLET_INCLUDE_DIR=$OMW_LIB_PREFIX/include/bullet/ \
|
||||
-G "Unix Makefiles" /path/to/openmw/source/dir
|
||||
$ make
|
||||
|
||||
You can use `-G"Xcode"` if you prefer Xcode, or -G"Eclipse CDT4 - Unix Makefiles"
|
||||
if you prefer Eclipse. You also can specify `-D CMAKE_BUILD_TYPE=Debug` for debug
|
||||
build. As for CMake 2.8.7 and Xcode 4.3, Xcode generator is broken. Sadly Eclipse CDT also cannot import generated project at least on my machine.
|
||||
|
||||
If all libs installed via homebrew (excluding mpg123), then command would be even simplier:
|
||||
|
||||
$ cmake \
|
||||
-D CMAKE_OSX_ARCHITECTURES="i386" \
|
||||
-D OGRE_SDK=/path/to/ogre/sdk \
|
||||
-D MPG123_LIBRARY=$OMW_LIB_PREFIX/lib/libmpg123.a \
|
||||
-D MPG123_INCLUDE_DIR=$OMW_LIB_PREFIX/include \
|
||||
-G "Unix Makefiles" /path/to/openmw/source/dir
|
||||
$ make
|
||||
|
||||
Note for users with recent Xcode versions: you must explicitly specify what set of compilers do you use! If not, gcc will be used for C and Clang for C++. Just add this two -D's to command: `-D CMAKE_C_COMPILER=/usr/bin/clang` and `-D CMAKE_CXX_COMPILER=/usr/bin/clang`
|
||||
|
||||
Note for Xcode 4.3 users: you should specify full path to used SDK, because current CMake (2.8.7) couldn't find SDKs inside Xcode app bundle:
|
||||
|
||||
-D CMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk"
|
||||
|
||||
# Run
|
||||
From your build directory run:
|
||||
|
||||
$ OpenMW.app/Contents/MacOS/openmw
|
||||
or:
|
||||
|
||||
$ open OpenMW.app
|
||||
Enjoy!
|
||||
|
||||
[homebrew]: https://github.com/mxcl/homebrew
|
||||
[boost]: http://www.boost.org
|
||||
[Ogre]: http://www.ogre3d.org
|
||||
[Bullet]: http://bulletphysics.org
|
||||
[OIS]: https://github.com/corristo/ois-fork
|
||||
[mpg123]: http://www.mpg123.de
|
||||
[libsndfile]: http://www.mega-nerd.com/libsndfile
|
||||
[official website]: http://openmw.com
|
||||
[Will Thimbleby's Ogre Framework]: http://www.thimbleby.net/ogre/
|
||||
[qt]: http://qt.nokia.com/
|
|
@ -184,7 +184,7 @@ int list(Bsa::BSAFile& bsa, Arguments& info)
|
|||
{
|
||||
// List all files
|
||||
const Bsa::BSAFile::FileList &files = bsa.getList();
|
||||
for(int i=0; i<files.size(); i++)
|
||||
for(unsigned int i=0; i<files.size(); i++)
|
||||
{
|
||||
if(info.longformat)
|
||||
{
|
||||
|
|
|
@ -236,7 +236,9 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
|
|||
// Loop through all the references
|
||||
ESM::CellRef ref;
|
||||
if(!quiet) std::cout << " References:\n";
|
||||
while(cell.getNextRef(esm, ref))
|
||||
|
||||
bool deleted = false;
|
||||
while(cell.getNextRef(esm, ref, deleted))
|
||||
{
|
||||
if (save) {
|
||||
info.data.mCellRefs[&cell].push_back(ref);
|
||||
|
@ -244,13 +246,14 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
|
|||
|
||||
if(quiet) continue;
|
||||
|
||||
std::cout << " Refnum: " << ref.mRefnum << std::endl;
|
||||
std::cout << " Refnum: " << ref.mRefNum.mIndex << std::endl;
|
||||
std::cout << " ID: '" << ref.mRefID << "'\n";
|
||||
std::cout << " Owner: '" << ref.mOwner << "'\n";
|
||||
std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n";
|
||||
std::cout << " Uses/health: '" << ref.mCharge << "'\n";
|
||||
std::cout << " Gold value: '" << ref.mGoldValue << "'\n";
|
||||
std::cout << " Blocked: '" << static_cast<int>(ref.mReferenceBlocked) << "'" << std::endl;
|
||||
std::cout << " Deleted: " << deleted << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -305,14 +308,14 @@ int load(Arguments& info)
|
|||
|
||||
info.data.author = esm.getAuthor();
|
||||
info.data.description = esm.getDesc();
|
||||
info.data.masters = esm.getMasters();
|
||||
info.data.masters = esm.getGameFiles();
|
||||
|
||||
if (!quiet)
|
||||
{
|
||||
std::cout << "Author: " << esm.getAuthor() << std::endl
|
||||
<< "Description: " << esm.getDesc() << std::endl
|
||||
<< "File format version: " << esm.getFVer() << std::endl;
|
||||
std::vector<ESM::Header::MasterData> m = esm.getMasters();
|
||||
std::vector<ESM::Header::MasterData> m = esm.getGameFiles();
|
||||
if (!m.empty())
|
||||
{
|
||||
std::cout << "Masters:" << std::endl;
|
||||
|
@ -339,6 +342,8 @@ int load(Arguments& info)
|
|||
}
|
||||
|
||||
std::string id = esm.getHNOString("NAME");
|
||||
if (id.empty())
|
||||
id = esm.getHNOString("INAM");
|
||||
|
||||
if(!quiet && interested)
|
||||
std::cout << "\nRecord: " << n.toString()
|
||||
|
|
|
@ -680,7 +680,7 @@ std::string creatureFlags(int flags)
|
|||
if (flags & ESM::Creature::Walks) properties += "Walks ";
|
||||
if (flags & ESM::Creature::Swims) properties += "Swims ";
|
||||
if (flags & ESM::Creature::Flies) properties += "Flies ";
|
||||
if (flags & ESM::Creature::Biped) properties += "Biped ";
|
||||
if (flags & ESM::Creature::Bipedal) properties += "Bipedal ";
|
||||
if (flags & ESM::Creature::Respawn) properties += "Respawn ";
|
||||
if (flags & ESM::Creature::Weapon) properties += "Weapon ";
|
||||
if (flags & ESM::Creature::Skeleton) properties += "Skeleton ";
|
||||
|
@ -691,7 +691,7 @@ std::string creatureFlags(int flags)
|
|||
ESM::Creature::Walks|
|
||||
ESM::Creature::Swims|
|
||||
ESM::Creature::Flies|
|
||||
ESM::Creature::Biped|
|
||||
ESM::Creature::Bipedal|
|
||||
ESM::Creature::Respawn|
|
||||
ESM::Creature::Weapon|
|
||||
ESM::Creature::Skeleton|
|
||||
|
@ -717,16 +717,26 @@ std::string landFlags(int flags)
|
|||
return properties;
|
||||
}
|
||||
|
||||
std::string leveledListFlags(int flags)
|
||||
std::string itemListFlags(int flags)
|
||||
{
|
||||
std::string properties = "";
|
||||
if (flags == 0) properties += "[None] ";
|
||||
if (flags & ESM::LeveledListBase::AllLevels) properties += "AllLevels ";
|
||||
// This flag apparently not present on creature lists...
|
||||
if (flags & ESM::LeveledListBase::Each) properties += "Each ";
|
||||
if (flags & ESM::ItemLevList::AllLevels) properties += "AllLevels ";
|
||||
if (flags & ESM::ItemLevList::Each) properties += "Each ";
|
||||
int unused = (0xFFFFFFFF ^
|
||||
(ESM::LeveledListBase::AllLevels|
|
||||
ESM::LeveledListBase::Each));
|
||||
(ESM::ItemLevList::AllLevels|
|
||||
ESM::ItemLevList::Each));
|
||||
if (flags & unused) properties += "Invalid ";
|
||||
properties += str(boost::format("(0x%08X)") % flags);
|
||||
return properties;
|
||||
}
|
||||
|
||||
std::string creatureListFlags(int flags)
|
||||
{
|
||||
std::string properties = "";
|
||||
if (flags == 0) properties += "[None] ";
|
||||
if (flags & ESM::CreatureLevList::AllLevels) properties += "AllLevels ";
|
||||
int unused = (0xFFFFFFFF ^ ESM::CreatureLevList::AllLevels);
|
||||
if (flags & unused) properties += "Invalid ";
|
||||
properties += str(boost::format("(0x%08X)") % flags);
|
||||
return properties;
|
||||
|
@ -764,34 +774,19 @@ std::string magicEffectFlags(int flags)
|
|||
{
|
||||
std::string properties = "";
|
||||
if (flags == 0) properties += "[None] ";
|
||||
// Enchanting & SpellMaking occur on the same list of effects.
|
||||
// "EXTRA SPELL" appears in the construction set under both the
|
||||
// spell making and enchanting tabs as an allowed effect. Since
|
||||
// most of the effects without this flags are defective in various
|
||||
// ways, it's still very unclear what these flag bits are.
|
||||
if (flags & ESM::MagicEffect::SpellMaking) properties += "SpellMaking ";
|
||||
if (flags & ESM::MagicEffect::Enchanting) properties += "Enchanting ";
|
||||
if (flags & 0x00000040) properties += "RangeNoSelf ";
|
||||
if (flags & 0x00000080) properties += "RangeTouch ";
|
||||
if (flags & 0x00000100) properties += "RangeTarget ";
|
||||
if (flags & 0x00001000) properties += "Unknown2 ";
|
||||
if (flags & 0x00000001) properties += "AffectSkill ";
|
||||
if (flags & 0x00000002) properties += "AffectAttribute ";
|
||||
if (flags & ESM::MagicEffect::TargetAttribute) properties += "TargetAttribute ";
|
||||
if (flags & ESM::MagicEffect::TargetSkill) properties += "TargetSkill ";
|
||||
if (flags & ESM::MagicEffect::NoDuration) properties += "NoDuration ";
|
||||
if (flags & 0x00000008) properties += "NoMagnitude ";
|
||||
if (flags & 0x00000010) properties += "Negative ";
|
||||
if (flags & 0x00000020) properties += "Unknown1 ";
|
||||
// ESM componet says 0x800 is negative, but none of the magic
|
||||
// effects have this flags set.
|
||||
if (flags & ESM::MagicEffect::Negative) properties += "Unused ";
|
||||
// Since only Chameleon has this flag it could be anything
|
||||
// that uniquely distinguishes Chameleon.
|
||||
if (flags & 0x00002000) properties += "Chameleon ";
|
||||
if (flags & 0x00004000) properties += "Bound ";
|
||||
if (flags & 0x00008000) properties += "Summon ";
|
||||
// Calm, Demoralize, Frenzy, Lock, Open, Rally, Soultrap, Turn Unded
|
||||
if (flags & 0x00010000) properties += "Unknown3 ";
|
||||
if (flags & 0x00020000) properties += "Absorb ";
|
||||
if (flags & ESM::MagicEffect::NoMagnitude) properties += "NoMagnitude ";
|
||||
if (flags & ESM::MagicEffect::Harmful) properties += "Harmful ";
|
||||
if (flags & ESM::MagicEffect::ContinuousVfx) properties += "ContinuousVFX ";
|
||||
if (flags & ESM::MagicEffect::CastSelf) properties += "CastSelf ";
|
||||
if (flags & ESM::MagicEffect::CastTouch) properties += "CastTouch ";
|
||||
if (flags & ESM::MagicEffect::CastTarget) properties += "CastTarget ";
|
||||
if (flags & ESM::MagicEffect::UncappedDamage) properties += "UncappedDamage ";
|
||||
if (flags & ESM::MagicEffect::NonRecastable) properties += "NonRecastable ";
|
||||
if (flags & ESM::MagicEffect::Unreflectable) properties += "Unreflectable ";
|
||||
if (flags & ESM::MagicEffect::CasterLinked) properties += "CasterLinked ";
|
||||
if (flags & 0xFFFC0000) properties += "Invalid ";
|
||||
properties += str(boost::format("(0x%08X)") % flags);
|
||||
return properties;
|
||||
|
|
|
@ -50,7 +50,8 @@ std::string cellFlags(int flags);
|
|||
std::string containerFlags(int flags);
|
||||
std::string creatureFlags(int flags);
|
||||
std::string landFlags(int flags);
|
||||
std::string leveledListFlags(int flags);
|
||||
std::string creatureListFlags(int flags);
|
||||
std::string itemListFlags(int flags);
|
||||
std::string lightFlags(int flags);
|
||||
std::string magicEffectFlags(int flags);
|
||||
std::string npcFlags(int flags);
|
||||
|
|
|
@ -13,8 +13,8 @@ void printAIPackage(ESM::AIPackage p)
|
|||
std::cout << " Distance: " << p.mWander.mDistance << std::endl;
|
||||
std::cout << " Duration: " << p.mWander.mDuration << std::endl;
|
||||
std::cout << " Time of Day: " << (int)p.mWander.mTimeOfDay << std::endl;
|
||||
if (p.mWander.mUnk != 1)
|
||||
std::cout << " Unknown: " << (int)p.mWander.mUnk << std::endl;
|
||||
if (p.mWander.mShouldRepeat != 1)
|
||||
std::cout << " Should repeat: " << (bool)p.mWander.mShouldRepeat << std::endl;
|
||||
|
||||
std::cout << " Idle: ";
|
||||
for (int i = 0; i != 8; i++)
|
||||
|
@ -740,8 +740,8 @@ void Record<ESM::DialInfo>::print()
|
|||
if (mData.mClass != "")
|
||||
std::cout << " Class: " << mData.mClass << std::endl;
|
||||
std::cout << " Factionless: " << mData.mFactionLess << std::endl;
|
||||
if (mData.mNpcFaction != "")
|
||||
std::cout << " NPC Faction: " << mData.mNpcFaction << std::endl;
|
||||
if (mData.mFaction != "")
|
||||
std::cout << " NPC Faction: " << mData.mFaction << std::endl;
|
||||
if (mData.mData.mRank != -1)
|
||||
std::cout << " NPC Rank: " << (int)mData.mData.mRank << std::endl;
|
||||
if (mData.mPcFaction != "")
|
||||
|
@ -834,7 +834,7 @@ template<>
|
|||
void Record<ESM::CreatureLevList>::print()
|
||||
{
|
||||
std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl;
|
||||
std::cout << " Flags: " << leveledListFlags(mData.mFlags) << std::endl;
|
||||
std::cout << " Flags: " << creatureListFlags(mData.mFlags) << std::endl;
|
||||
std::cout << " Number of items: " << mData.mList.size() << std::endl;
|
||||
std::vector<ESM::LeveledListBase::LevelItem>::iterator iit;
|
||||
for (iit = mData.mList.begin(); iit != mData.mList.end(); iit++)
|
||||
|
@ -846,11 +846,11 @@ template<>
|
|||
void Record<ESM::ItemLevList>::print()
|
||||
{
|
||||
std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl;
|
||||
std::cout << " Flags: " << leveledListFlags(mData.mFlags) << std::endl;
|
||||
std::cout << " Flags: " << itemListFlags(mData.mFlags) << std::endl;
|
||||
std::cout << " Number of items: " << mData.mList.size() << std::endl;
|
||||
std::vector<ESM::LeveledListBase::LevelItem>::iterator iit;
|
||||
for (iit = mData.mList.begin(); iit != mData.mList.end(); iit++)
|
||||
std::cout << " Inventory: Count: " << iit->mLevel
|
||||
std::cout << " Inventory: Level: " << iit->mLevel
|
||||
<< " Item: " << iit->mId << std::endl;
|
||||
}
|
||||
|
||||
|
@ -958,7 +958,7 @@ void Record<ESM::MagicEffect>::print()
|
|||
std::cout << " RGB Color: " << "("
|
||||
<< mData.mData.mRed << ","
|
||||
<< mData.mData.mGreen << ","
|
||||
<< mData.mData.mGreen << ")" << std::endl;
|
||||
<< mData.mData.mBlue << ")" << std::endl;
|
||||
}
|
||||
|
||||
template<>
|
||||
|
@ -989,8 +989,7 @@ void Record<ESM::NPC>::print()
|
|||
std::cout << " Faction: " << mData.mFaction << std::endl;
|
||||
std::cout << " Flags: " << npcFlags(mData.mFlags) << std::endl;
|
||||
|
||||
// Seriously?
|
||||
if (mData.mNpdt52.mGold == -10)
|
||||
if (mData.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS)
|
||||
{
|
||||
std::cout << " Level: " << mData.mNpdt12.mLevel << std::endl;
|
||||
std::cout << " Reputation: " << (int)mData.mNpdt12.mReputation << std::endl;
|
||||
|
@ -1022,7 +1021,7 @@ void Record<ESM::NPC>::print()
|
|||
std::cout << " Luck: " << (int)mData.mNpdt52.mLuck << std::endl;
|
||||
|
||||
std::cout << " Skills:" << std::endl;
|
||||
for (int i = 0; i != 27; i++)
|
||||
for (int i = 0; i != ESM::Skill::Length; i++)
|
||||
std::cout << " " << skillLabel(i) << ": "
|
||||
<< (int)((unsigned char)mData.mNpdt52.mSkills[i]) << std::endl;
|
||||
|
||||
|
|
|
@ -24,7 +24,12 @@ namespace EsmTool
|
|||
bool mPrintPlain;
|
||||
|
||||
public:
|
||||
RecordBase () { mPrintPlain = false; }
|
||||
RecordBase ()
|
||||
: mFlags(0)
|
||||
, mPrintPlain(false)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~RecordBase() {}
|
||||
|
||||
const std::string &getId() const {
|
||||
|
|
|
@ -11,7 +11,9 @@ set(LAUNCHER
|
|||
settings/launchersettings.cpp
|
||||
|
||||
utils/checkablemessagebox.cpp
|
||||
utils/profilescombobox.cpp
|
||||
utils/textinputdialog.cpp
|
||||
utils/lineedit.cpp
|
||||
|
||||
${CMAKE_SOURCE_DIR}/files/launcher/launcher.rc
|
||||
)
|
||||
|
@ -32,8 +34,9 @@ set(LAUNCHER_HEADER
|
|||
settings/settingsbase.hpp
|
||||
|
||||
utils/checkablemessagebox.hpp
|
||||
utils/profilescombobox.hpp
|
||||
utils/textinputdialog.hpp
|
||||
|
||||
utils/lineedit.hpp
|
||||
)
|
||||
if(NOT WIN32)
|
||||
LIST(APPEND LAUNCHER_HEADER unshieldthread.hpp)
|
||||
|
@ -48,8 +51,11 @@ set(LAUNCHER_HEADER_MOC
|
|||
playpage.hpp
|
||||
textslotmsgbox.hpp
|
||||
|
||||
utils/checkablemessagebox.hpp
|
||||
utils/textinputdialog.hpp
|
||||
utils/checkablemessagebox.hpp
|
||||
utils/profilescombobox.hpp
|
||||
utils/lineedit.hpp
|
||||
|
||||
)
|
||||
|
||||
if(NOT WIN32)
|
||||
|
@ -62,6 +68,7 @@ set(LAUNCHER_UI
|
|||
${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui
|
||||
${CMAKE_SOURCE_DIR}/files/ui/mainwindow.ui
|
||||
${CMAKE_SOURCE_DIR}/files/ui/playpage.ui
|
||||
${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
|
||||
)
|
||||
|
||||
source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER})
|
||||
|
@ -130,8 +137,3 @@ if (BUILD_WITH_CODE_COVERAGE)
|
|||
target_link_libraries(omwlauncher gcov)
|
||||
endif()
|
||||
|
||||
# Workaround for binutil => 2.23 problem when linking, should be fixed eventually upstream
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(omwlauncher dl Xt)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -4,548 +4,300 @@
|
|||
#include <QMessageBox>
|
||||
#include <QCheckBox>
|
||||
#include <QMenu>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
|
||||
#include <components/fileorderlist/model/datafilesmodel.hpp>
|
||||
#include <components/fileorderlist/model/pluginsproxymodel.hpp>
|
||||
#include <components/fileorderlist/model/esm/esmfile.hpp>
|
||||
#include <components/contentselector/model/esmfile.hpp>
|
||||
|
||||
#include <components/fileorderlist/utils/lineedit.hpp>
|
||||
#include <components/fileorderlist/utils/naturalsort.hpp>
|
||||
#include <components/fileorderlist/utils/profilescombobox.hpp>
|
||||
#include <components/contentselector/model/naturalsort.hpp>
|
||||
|
||||
#include "utils/textinputdialog.hpp"
|
||||
#include "utils/profilescombobox.hpp"
|
||||
|
||||
#include "settings/gamesettings.hpp"
|
||||
#include "settings/launchersettings.hpp"
|
||||
|
||||
#include "utils/textinputdialog.hpp"
|
||||
#include "components/contentselector/view/contentselector.hpp"
|
||||
|
||||
DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent)
|
||||
Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent)
|
||||
: mCfgMgr(cfg)
|
||||
, mGameSettings(gameSettings)
|
||||
, mLauncherSettings(launcherSettings)
|
||||
, QWidget(parent)
|
||||
{
|
||||
setupUi(this);
|
||||
ui.setupUi (this);
|
||||
setObjectName ("DataFilesPage");
|
||||
mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget);
|
||||
|
||||
// Models
|
||||
mDataFilesModel = new DataFilesModel(this);
|
||||
|
||||
mMastersProxyModel = new QSortFilterProxyModel();
|
||||
mMastersProxyModel->setFilterRegExp(QString("^.*\\.esm"));
|
||||
mMastersProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
mMastersProxyModel->setSourceModel(mDataFilesModel);
|
||||
|
||||
mPluginsProxyModel = new PluginsProxyModel();
|
||||
mPluginsProxyModel->setFilterRegExp(QString("^.*\\.esp"));
|
||||
mPluginsProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
mPluginsProxyModel->setSourceModel(mDataFilesModel);
|
||||
|
||||
mFilterProxyModel = new QSortFilterProxyModel();
|
||||
mFilterProxyModel->setDynamicSortFilter(true);
|
||||
mFilterProxyModel->setSourceModel(mPluginsProxyModel);
|
||||
|
||||
QCheckBox checkBox;
|
||||
unsigned int height = checkBox.sizeHint().height() + 4;
|
||||
|
||||
mastersTable->setModel(mMastersProxyModel);
|
||||
mastersTable->setObjectName("MastersTable");
|
||||
mastersTable->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
mastersTable->setSortingEnabled(false);
|
||||
mastersTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
mastersTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
mastersTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
mastersTable->setAlternatingRowColors(true);
|
||||
mastersTable->horizontalHeader()->setStretchLastSection(true);
|
||||
mastersTable->horizontalHeader()->hide();
|
||||
|
||||
// Set the row height to the size of the checkboxes
|
||||
mastersTable->verticalHeader()->setDefaultSectionSize(height);
|
||||
mastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed);
|
||||
mastersTable->verticalHeader()->hide();
|
||||
|
||||
pluginsTable->setModel(mFilterProxyModel);
|
||||
pluginsTable->setObjectName("PluginsTable");
|
||||
pluginsTable->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
pluginsTable->setSortingEnabled(false);
|
||||
pluginsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
pluginsTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
pluginsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
pluginsTable->setAlternatingRowColors(true);
|
||||
pluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
|
||||
pluginsTable->horizontalHeader()->setStretchLastSection(true);
|
||||
pluginsTable->horizontalHeader()->hide();
|
||||
|
||||
pluginsTable->verticalHeader()->setDefaultSectionSize(height);
|
||||
pluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed);
|
||||
|
||||
// Adjust the tableview widths inside the splitter
|
||||
QList<int> sizeList;
|
||||
sizeList << mLauncherSettings.value(QString("General/MastersTable/width"), QString("200")).toInt();
|
||||
sizeList << mLauncherSettings.value(QString("General/PluginTable/width"), QString("340")).toInt();
|
||||
|
||||
splitter->setSizes(sizeList);
|
||||
|
||||
// Create a dialog for the new profile name input
|
||||
mNewProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this);
|
||||
|
||||
connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentIndexChanged(int)));
|
||||
|
||||
connect(mNewProfileDialog->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(updateOkButton(QString)));
|
||||
|
||||
connect(pluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
|
||||
connect(mastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
|
||||
|
||||
connect(pluginsTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
|
||||
connect(mastersTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
|
||||
|
||||
connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews()));
|
||||
|
||||
connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString)));
|
||||
|
||||
connect(splitter, SIGNAL(splitterMoved(int,int)), this, SLOT(updateSplitter()));
|
||||
|
||||
createActions();
|
||||
buildView();
|
||||
setupDataFiles();
|
||||
}
|
||||
|
||||
void DataFilesPage::createActions()
|
||||
void Launcher::DataFilesPage::loadSettings()
|
||||
{
|
||||
QStringList paths = mGameSettings.getDataDirs();
|
||||
paths.insert (0, mDataLocal);
|
||||
PathIterator pathIterator (paths);
|
||||
|
||||
// Add the actions to the toolbuttons
|
||||
newProfileButton->setDefaultAction(newProfileAction);
|
||||
deleteProfileButton->setDefaultAction(deleteProfileAction);
|
||||
QString profileName = ui.profilesComboBox->currentText();
|
||||
|
||||
// Context menu actions
|
||||
mContextMenu = new QMenu(this);
|
||||
mContextMenu->addAction(checkAction);
|
||||
mContextMenu->addAction(uncheckAction);
|
||||
QStringList files = mLauncherSettings.values(QString("Profiles/") + profileName, Qt::MatchExactly);
|
||||
|
||||
QStringList filepaths;
|
||||
|
||||
foreach (const QString &file, files)
|
||||
{
|
||||
QString filepath = pathIterator.findFirstPath (file);
|
||||
|
||||
if (!filepath.isEmpty())
|
||||
filepaths << filepath;
|
||||
}
|
||||
|
||||
mSelector->setProfileContent (filepaths);
|
||||
}
|
||||
|
||||
void DataFilesPage::setupDataFiles()
|
||||
void Launcher::DataFilesPage::saveSettings(const QString &profile)
|
||||
{
|
||||
// Set the encoding to the one found in openmw.cfg or the default
|
||||
mDataFilesModel->setEncoding(mGameSettings.value(QString("encoding"), QString("win1252")));
|
||||
QString profileName = profile;
|
||||
|
||||
QStringList paths = mGameSettings.getDataDirs();
|
||||
if (profileName.isEmpty())
|
||||
profileName = ui.profilesComboBox->currentText();
|
||||
|
||||
foreach (const QString &path, paths) {
|
||||
mDataFilesModel->addFiles(path);
|
||||
//retrieve the files selected for the profile
|
||||
ContentSelectorModel::ContentFileList items = mSelector->selectedFiles();
|
||||
|
||||
removeProfile (profileName);
|
||||
|
||||
mGameSettings.remove(QString("content"));
|
||||
|
||||
//set the value of the current profile (not necessarily the profile being saved!)
|
||||
mLauncherSettings.setValue(QString("Profiles/currentprofile"), ui.profilesComboBox->currentText());
|
||||
|
||||
foreach(const ContentSelectorModel::EsmFile *item, items) {
|
||||
mLauncherSettings.setMultiValue(QString("Profiles/") + profileName, item->fileName());
|
||||
mGameSettings.setMultiValue(QString("content"), item->fileName());
|
||||
}
|
||||
|
||||
QString dataLocal = mGameSettings.getDataLocal();
|
||||
if (!dataLocal.isEmpty())
|
||||
mDataFilesModel->addFiles(dataLocal);
|
||||
}
|
||||
|
||||
// Sort by date accessed for now
|
||||
mDataFilesModel->sort(3);
|
||||
void Launcher::DataFilesPage::buildView()
|
||||
{
|
||||
ui.verticalLayout->insertWidget (0, mSelector->uiWidget());
|
||||
|
||||
QStringList profiles = mLauncherSettings.subKeys(QString("Profiles/"));
|
||||
QString profile = mLauncherSettings.value(QString("Profiles/currentprofile"));
|
||||
//tool buttons
|
||||
ui.newProfileButton->setToolTip ("Create a new profile");
|
||||
ui.deleteProfileButton->setToolTip ("Delete an existing profile");
|
||||
|
||||
if (!profiles.isEmpty())
|
||||
profilesComboBox->addItems(profiles);
|
||||
//combo box
|
||||
ui.profilesComboBox->addItem ("Default");
|
||||
ui.profilesComboBox->setPlaceholderText (QString("Select a profile..."));
|
||||
|
||||
// Add the current profile if empty
|
||||
if (profilesComboBox->findText(profile) == -1 && !profile.isEmpty())
|
||||
profilesComboBox->addItem(profile);
|
||||
// Add the actions to the toolbuttons
|
||||
ui.newProfileButton->setDefaultAction (ui.newProfileAction);
|
||||
ui.deleteProfileButton->setDefaultAction (ui.deleteProfileAction);
|
||||
|
||||
if (profilesComboBox->findText(QString("Default")) == -1)
|
||||
profilesComboBox->addItem(QString("Default"));
|
||||
//establish connections
|
||||
connect (ui.profilesComboBox, SIGNAL (currentIndexChanged(int)),
|
||||
this, SLOT (slotProfileChanged(int)));
|
||||
|
||||
if (profile.isEmpty() || profile == QLatin1String("Default")) {
|
||||
deleteProfileAction->setEnabled(false);
|
||||
profilesComboBox->setEditEnabled(false);
|
||||
profilesComboBox->setCurrentIndex(profilesComboBox->findText(QString("Default")));
|
||||
} else {
|
||||
profilesComboBox->setEditEnabled(true);
|
||||
profilesComboBox->setCurrentIndex(profilesComboBox->findText(profile));
|
||||
connect (ui.profilesComboBox, SIGNAL (profileRenamed(QString, QString)),
|
||||
this, SLOT (slotProfileRenamed(QString, QString)));
|
||||
|
||||
connect (ui.profilesComboBox, SIGNAL (signalProfileChanged(QString, QString)),
|
||||
this, SLOT (slotProfileChangedByUser(QString, QString)));
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::removeProfile(const QString &profile)
|
||||
{
|
||||
mLauncherSettings.remove(QString("Profiles/") + profile);
|
||||
}
|
||||
|
||||
QAbstractItemModel *Launcher::DataFilesPage::profilesModel() const
|
||||
{
|
||||
return ui.profilesComboBox->model();
|
||||
}
|
||||
|
||||
int Launcher::DataFilesPage::profilesIndex() const
|
||||
{
|
||||
return ui.profilesComboBox->currentIndex();
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::setProfile(int index, bool savePrevious)
|
||||
{
|
||||
if (index >= -1 && index < ui.profilesComboBox->count())
|
||||
{
|
||||
QString previous = ui.profilesComboBox->itemText(ui.profilesComboBox->currentIndex());
|
||||
QString current = ui.profilesComboBox->itemText(index);
|
||||
|
||||
setProfile (previous, current, savePrevious);
|
||||
}
|
||||
}
|
||||
|
||||
// We do this here to prevent deletion of profiles when initializing the combobox
|
||||
connect(profilesComboBox, SIGNAL(profileRenamed(QString,QString)), this, SLOT(profileRenamed(QString,QString)));
|
||||
connect(profilesComboBox, SIGNAL(profileChanged(QString,QString)), this, SLOT(profileChanged(QString,QString)));
|
||||
void Launcher::DataFilesPage::setProfile (const QString &previous, const QString ¤t, bool savePrevious)
|
||||
{
|
||||
//abort if no change (poss. duplicate signal)
|
||||
if (previous == current)
|
||||
return;
|
||||
|
||||
if (!previous.isEmpty() && savePrevious)
|
||||
saveSettings (previous);
|
||||
|
||||
ui.profilesComboBox->setCurrentProfile (ui.profilesComboBox->findText (current));
|
||||
|
||||
loadSettings();
|
||||
|
||||
checkForDefaultProfile();
|
||||
}
|
||||
|
||||
void DataFilesPage::loadSettings()
|
||||
void Launcher::DataFilesPage::slotProfileDeleted (const QString &item)
|
||||
{
|
||||
QString profile = mLauncherSettings.value(QString("Profiles/currentprofile"));
|
||||
|
||||
if (profile.isEmpty())
|
||||
return;
|
||||
|
||||
mDataFilesModel->uncheckAll();
|
||||
|
||||
QStringList masters = mLauncherSettings.values(QString("Profiles/") + profile + QString("/master"), Qt::MatchExactly);
|
||||
QStringList plugins = mLauncherSettings.values(QString("Profiles/") + profile + QString("/plugin"), Qt::MatchExactly);
|
||||
|
||||
foreach (const QString &master, masters) {
|
||||
QModelIndex index = mDataFilesModel->indexFromItem(mDataFilesModel->findItem(master));
|
||||
if (index.isValid())
|
||||
mDataFilesModel->setCheckState(index, Qt::Checked);
|
||||
}
|
||||
|
||||
foreach (const QString &plugin, plugins) {
|
||||
QModelIndex index = mDataFilesModel->indexFromItem(mDataFilesModel->findItem(plugin));
|
||||
if (index.isValid())
|
||||
mDataFilesModel->setCheckState(index, Qt::Checked);
|
||||
}
|
||||
removeProfile (item);
|
||||
}
|
||||
|
||||
void DataFilesPage::saveSettings()
|
||||
void Launcher::DataFilesPage::slotProfileChangedByUser(const QString &previous, const QString ¤t)
|
||||
{
|
||||
if (mDataFilesModel->rowCount() < 1)
|
||||
setProfile(previous, current, true);
|
||||
emit signalProfileChanged (ui.profilesComboBox->findText(current));
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::slotProfileRenamed(const QString &previous, const QString ¤t)
|
||||
{
|
||||
if (previous.isEmpty())
|
||||
return;
|
||||
|
||||
QString profile = mLauncherSettings.value(QString("Profiles/currentprofile"));
|
||||
// Save the new profile name
|
||||
saveSettings();
|
||||
|
||||
if (profile.isEmpty()) {
|
||||
profile = profilesComboBox->currentText();
|
||||
mLauncherSettings.setValue(QString("Profiles/currentprofile"), profile);
|
||||
}
|
||||
// Remove the old one
|
||||
removeProfile (previous);
|
||||
|
||||
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master"));
|
||||
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin"));
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
mGameSettings.remove(QString("master"));
|
||||
mGameSettings.remove(QString("plugin"));
|
||||
void Launcher::DataFilesPage::slotProfileChanged(int index)
|
||||
{
|
||||
setProfile (index, true);
|
||||
}
|
||||
|
||||
QStringList items = mDataFilesModel->checkedItems();
|
||||
void Launcher::DataFilesPage::setupDataFiles()
|
||||
{
|
||||
QStringList paths = mGameSettings.getDataDirs();
|
||||
|
||||
foreach(const QString &item, items) {
|
||||
foreach (const QString &path, paths)
|
||||
mSelector->addFiles(path);
|
||||
|
||||
if (item.endsWith(QString(".esm"), Qt::CaseInsensitive)) {
|
||||
mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/master"), item);
|
||||
mGameSettings.setMultiValue(QString("master"), item);
|
||||
mDataLocal = mGameSettings.getDataLocal();
|
||||
|
||||
} else if (item.endsWith(QString(".esp"), Qt::CaseInsensitive)) {
|
||||
mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/plugin"), item);
|
||||
mGameSettings.setMultiValue(QString("plugin"), item);
|
||||
if (!mDataLocal.isEmpty())
|
||||
mSelector->addFiles(mDataLocal);
|
||||
|
||||
QStringList profiles;
|
||||
QString currentProfile = mLauncherSettings.getSettings().value("Profiles/currentprofile");
|
||||
|
||||
foreach (QString key, mLauncherSettings.getSettings().keys())
|
||||
{
|
||||
if (key.contains("Profiles/"))
|
||||
{
|
||||
QString profile = key.mid (9);
|
||||
if (profile != "currentprofile")
|
||||
{
|
||||
if (!profiles.contains(profile))
|
||||
profiles << profile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (const QString &item, profiles)
|
||||
addProfile (item, false);
|
||||
|
||||
setProfile (ui.profilesComboBox->findText(currentProfile), false);
|
||||
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
void DataFilesPage::updateOkButton(const QString &text)
|
||||
void Launcher::DataFilesPage::on_newProfileAction_triggered()
|
||||
{
|
||||
// We do this here because we need the profiles combobox text
|
||||
if (text.isEmpty()) {
|
||||
mNewProfileDialog->setOkButtonEnabled(false);
|
||||
return;
|
||||
}
|
||||
TextInputDialog newDialog (tr("New Profile"), tr("Profile name:"), this);
|
||||
|
||||
(profilesComboBox->findText(text) == -1)
|
||||
? mNewProfileDialog->setOkButtonEnabled(true)
|
||||
: mNewProfileDialog->setOkButtonEnabled(false);
|
||||
if (newDialog.exec() != QDialog::Accepted)
|
||||
return;
|
||||
|
||||
QString profile = newDialog.getText();
|
||||
|
||||
if (profile.isEmpty())
|
||||
return;
|
||||
|
||||
saveSettings();
|
||||
|
||||
mSelector->clearCheckStates();
|
||||
|
||||
addProfile(profile, true);
|
||||
|
||||
mSelector->setGameFile();
|
||||
|
||||
saveSettings();
|
||||
|
||||
emit signalProfileChanged (ui.profilesComboBox->findText(profile));
|
||||
}
|
||||
|
||||
void DataFilesPage::updateSplitter()
|
||||
void Launcher::DataFilesPage::addProfile (const QString &profile, bool setAsCurrent)
|
||||
{
|
||||
// Sigh, update the saved splitter size in settings only when moved
|
||||
// Since getting mSplitter->sizes() if page is hidden returns invalid values
|
||||
QList<int> sizes = splitter->sizes();
|
||||
if (profile.isEmpty())
|
||||
return;
|
||||
|
||||
mLauncherSettings.setValue(QString("General/MastersTable/width"), QString::number(sizes.at(0)));
|
||||
mLauncherSettings.setValue(QString("General/PluginsTable/width"), QString::number(sizes.at(1)));
|
||||
if (ui.profilesComboBox->findText (profile) != -1)
|
||||
return;
|
||||
|
||||
ui.profilesComboBox->addItem (profile);
|
||||
|
||||
if (setAsCurrent)
|
||||
setProfile (ui.profilesComboBox->findText (profile), false);
|
||||
}
|
||||
|
||||
void DataFilesPage::updateViews()
|
||||
void Launcher::DataFilesPage::on_deleteProfileAction_triggered()
|
||||
{
|
||||
// Ensure the columns are hidden because sort() re-enables them
|
||||
mastersTable->setColumnHidden(1, true);
|
||||
mastersTable->setColumnHidden(2, true);
|
||||
mastersTable->setColumnHidden(3, true);
|
||||
mastersTable->setColumnHidden(4, true);
|
||||
mastersTable->setColumnHidden(5, true);
|
||||
mastersTable->setColumnHidden(6, true);
|
||||
mastersTable->setColumnHidden(7, true);
|
||||
mastersTable->setColumnHidden(8, true);
|
||||
|
||||
pluginsTable->setColumnHidden(1, true);
|
||||
pluginsTable->setColumnHidden(2, true);
|
||||
pluginsTable->setColumnHidden(3, true);
|
||||
pluginsTable->setColumnHidden(4, true);
|
||||
pluginsTable->setColumnHidden(5, true);
|
||||
pluginsTable->setColumnHidden(6, true);
|
||||
pluginsTable->setColumnHidden(7, true);
|
||||
pluginsTable->setColumnHidden(8, true);
|
||||
}
|
||||
|
||||
void DataFilesPage::setProfilesComboBoxIndex(int index)
|
||||
{
|
||||
profilesComboBox->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
void DataFilesPage::slotCurrentIndexChanged(int index)
|
||||
{
|
||||
emit profileChanged(index);
|
||||
}
|
||||
|
||||
QAbstractItemModel* DataFilesPage::profilesComboBoxModel()
|
||||
{
|
||||
return profilesComboBox->model();
|
||||
}
|
||||
|
||||
int DataFilesPage::profilesComboBoxIndex()
|
||||
{
|
||||
return profilesComboBox->currentIndex();
|
||||
}
|
||||
|
||||
void DataFilesPage::on_newProfileAction_triggered()
|
||||
{
|
||||
if (mNewProfileDialog->exec() == QDialog::Accepted) {
|
||||
QString profile = mNewProfileDialog->lineEdit()->text();
|
||||
profilesComboBox->addItem(profile);
|
||||
profilesComboBox->setCurrentIndex(profilesComboBox->findText(profile));
|
||||
}
|
||||
}
|
||||
|
||||
void DataFilesPage::on_deleteProfileAction_triggered()
|
||||
{
|
||||
QString profile = profilesComboBox->currentText();
|
||||
QString profile = ui.profilesComboBox->currentText();
|
||||
|
||||
if (profile.isEmpty())
|
||||
return;
|
||||
|
||||
if (!showDeleteMessageBox (profile))
|
||||
return;
|
||||
|
||||
// Remove the profile from the combobox
|
||||
ui.profilesComboBox->removeItem (ui.profilesComboBox->findText (profile));
|
||||
|
||||
removeProfile(profile);
|
||||
|
||||
saveSettings();
|
||||
|
||||
loadSettings();
|
||||
|
||||
checkForDefaultProfile();
|
||||
}
|
||||
|
||||
void Launcher::DataFilesPage::checkForDefaultProfile()
|
||||
{
|
||||
//don't allow deleting "Default" profile
|
||||
bool success = (ui.profilesComboBox->currentText() != "Default");
|
||||
|
||||
ui.deleteProfileAction->setEnabled (success);
|
||||
ui.profilesComboBox->setEditEnabled (success);
|
||||
}
|
||||
|
||||
bool Launcher::DataFilesPage::showDeleteMessageBox (const QString &text)
|
||||
{
|
||||
QMessageBox msgBox(this);
|
||||
msgBox.setWindowTitle(tr("Delete Profile"));
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setStandardButtons(QMessageBox::Cancel);
|
||||
msgBox.setText(tr("Are you sure you want to delete <b>%0</b>?").arg(profile));
|
||||
msgBox.setText(tr("Are you sure you want to delete <b>%0</b>?").arg(text));
|
||||
|
||||
QAbstractButton *deleteButton =
|
||||
msgBox.addButton(tr("Delete"), QMessageBox::ActionRole);
|
||||
|
||||
msgBox.exec();
|
||||
|
||||
if (msgBox.clickedButton() == deleteButton) {
|
||||
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master"));
|
||||
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin"));
|
||||
|
||||
// Remove the profile from the combobox
|
||||
profilesComboBox->removeItem(profilesComboBox->findText(profile));
|
||||
}
|
||||
}
|
||||
|
||||
void DataFilesPage::on_checkAction_triggered()
|
||||
{
|
||||
if (pluginsTable->hasFocus())
|
||||
setPluginsCheckstates(Qt::Checked);
|
||||
|
||||
if (mastersTable->hasFocus())
|
||||
setMastersCheckstates(Qt::Checked);
|
||||
|
||||
}
|
||||
|
||||
void DataFilesPage::on_uncheckAction_triggered()
|
||||
{
|
||||
if (pluginsTable->hasFocus())
|
||||
setPluginsCheckstates(Qt::Unchecked);
|
||||
|
||||
if (mastersTable->hasFocus())
|
||||
setMastersCheckstates(Qt::Unchecked);
|
||||
}
|
||||
|
||||
void DataFilesPage::setMastersCheckstates(Qt::CheckState state)
|
||||
{
|
||||
if (!mastersTable->selectionModel()->hasSelection()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QModelIndexList indexes = mastersTable->selectionModel()->selectedIndexes();
|
||||
|
||||
foreach (const QModelIndex &index, indexes)
|
||||
{
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index);
|
||||
|
||||
if (!sourceIndex.isValid())
|
||||
return;
|
||||
|
||||
mDataFilesModel->setCheckState(sourceIndex, state);
|
||||
}
|
||||
}
|
||||
|
||||
void DataFilesPage::setPluginsCheckstates(Qt::CheckState state)
|
||||
{
|
||||
if (!pluginsTable->selectionModel()->hasSelection()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QModelIndexList indexes = pluginsTable->selectionModel()->selectedIndexes();
|
||||
|
||||
foreach (const QModelIndex &index, indexes)
|
||||
{
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(
|
||||
mFilterProxyModel->mapToSource(index));
|
||||
|
||||
if (!sourceIndex.isValid())
|
||||
return;
|
||||
|
||||
mDataFilesModel->setCheckState(sourceIndex, state);
|
||||
}
|
||||
}
|
||||
|
||||
void DataFilesPage::setCheckState(QModelIndex index)
|
||||
{
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
QObject *object = QObject::sender();
|
||||
|
||||
// Not a signal-slot call
|
||||
if (!object)
|
||||
return;
|
||||
|
||||
|
||||
if (object->objectName() == QLatin1String("PluginsTable")) {
|
||||
QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(
|
||||
mFilterProxyModel->mapToSource(index));
|
||||
|
||||
if (sourceIndex.isValid()) {
|
||||
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked)
|
||||
? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked)
|
||||
: mDataFilesModel->setCheckState(sourceIndex, Qt::Checked);
|
||||
}
|
||||
}
|
||||
|
||||
if (object->objectName() == QLatin1String("MastersTable")) {
|
||||
QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index);
|
||||
|
||||
if (sourceIndex.isValid()) {
|
||||
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked)
|
||||
? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked)
|
||||
: mDataFilesModel->setCheckState(sourceIndex, Qt::Checked);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void DataFilesPage::filterChanged(const QString filter)
|
||||
{
|
||||
QRegExp regExp(filter, Qt::CaseInsensitive, QRegExp::FixedString);
|
||||
mFilterProxyModel->setFilterRegExp(regExp);
|
||||
}
|
||||
|
||||
void DataFilesPage::profileChanged(const QString &previous, const QString ¤t)
|
||||
{
|
||||
// Prevent the deletion of the default profile
|
||||
if (current == QLatin1String("Default")) {
|
||||
deleteProfileAction->setEnabled(false);
|
||||
profilesComboBox->setEditEnabled(false);
|
||||
} else {
|
||||
deleteProfileAction->setEnabled(true);
|
||||
profilesComboBox->setEditEnabled(true);
|
||||
}
|
||||
|
||||
if (previous.isEmpty())
|
||||
return;
|
||||
|
||||
if (profilesComboBox->findText(previous) == -1)
|
||||
return; // Profile was deleted
|
||||
|
||||
// Store the previous profile
|
||||
mLauncherSettings.setValue(QString("Profiles/currentprofile"), previous);
|
||||
saveSettings();
|
||||
mLauncherSettings.setValue(QString("Profiles/currentprofile"), current);
|
||||
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
void DataFilesPage::profileRenamed(const QString &previous, const QString ¤t)
|
||||
{
|
||||
if (previous.isEmpty())
|
||||
return;
|
||||
|
||||
// Save the new profile name
|
||||
mLauncherSettings.setValue(QString("Profiles/currentprofile"), current);
|
||||
saveSettings();
|
||||
|
||||
// Remove the old one
|
||||
mLauncherSettings.remove(QString("Profiles/") + previous + QString("/master"));
|
||||
mLauncherSettings.remove(QString("Profiles/") + previous + QString("/plugin"));
|
||||
|
||||
// Remove the profile from the combobox
|
||||
profilesComboBox->removeItem(profilesComboBox->findText(previous));
|
||||
|
||||
loadSettings();
|
||||
|
||||
}
|
||||
|
||||
void DataFilesPage::showContextMenu(const QPoint &point)
|
||||
{
|
||||
QObject *object = QObject::sender();
|
||||
|
||||
// Not a signal-slot call
|
||||
if (!object)
|
||||
return;
|
||||
|
||||
if (object->objectName() == QLatin1String("PluginsTable")) {
|
||||
if (!pluginsTable->selectionModel()->hasSelection())
|
||||
return;
|
||||
|
||||
QPoint globalPos = pluginsTable->mapToGlobal(point);
|
||||
QModelIndexList indexes = pluginsTable->selectionModel()->selectedIndexes();
|
||||
|
||||
// Show the check/uncheck actions depending on the state of the selected items
|
||||
uncheckAction->setEnabled(false);
|
||||
checkAction->setEnabled(false);
|
||||
|
||||
foreach (const QModelIndex &index, indexes)
|
||||
{
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(
|
||||
mFilterProxyModel->mapToSource(index));
|
||||
|
||||
if (!sourceIndex.isValid())
|
||||
return;
|
||||
|
||||
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked)
|
||||
? uncheckAction->setEnabled(true)
|
||||
: checkAction->setEnabled(true);
|
||||
}
|
||||
|
||||
// Show menu
|
||||
mContextMenu->exec(globalPos);
|
||||
}
|
||||
|
||||
if (object->objectName() == QLatin1String("MastersTable")) {
|
||||
if (!mastersTable->selectionModel()->hasSelection())
|
||||
return;
|
||||
|
||||
QPoint globalPos = mastersTable->mapToGlobal(point);
|
||||
QModelIndexList indexes = mastersTable->selectionModel()->selectedIndexes();
|
||||
|
||||
// Show the check/uncheck actions depending on the state of the selected items
|
||||
uncheckAction->setEnabled(false);
|
||||
checkAction->setEnabled(false);
|
||||
|
||||
foreach (const QModelIndex &index, indexes)
|
||||
{
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index);
|
||||
|
||||
if (!sourceIndex.isValid())
|
||||
return;
|
||||
|
||||
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked)
|
||||
? uncheckAction->setEnabled(true)
|
||||
: checkAction->setEnabled(true);
|
||||
}
|
||||
|
||||
mContextMenu->exec(globalPos);
|
||||
}
|
||||
return (msgBox.clickedButton() == deleteButton);
|
||||
}
|
||||
|
|
|
@ -1,88 +1,136 @@
|
|||
#ifndef DATAFILESPAGE_H
|
||||
#define DATAFILESPAGE_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QModelIndex>
|
||||
|
||||
#include "ui_datafilespage.h"
|
||||
#include <QWidget>
|
||||
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
|
||||
class QSortFilterProxyModel;
|
||||
class QAbstractItemModel;
|
||||
class QAction;
|
||||
class QMenu;
|
||||
|
||||
class DataFilesModel;
|
||||
class TextInputDialog;
|
||||
class GameSettings;
|
||||
class LauncherSettings;
|
||||
class PluginsProxyModel;
|
||||
|
||||
namespace Files { struct ConfigurationManager; }
|
||||
namespace ContentSelectorView { class ContentSelector; }
|
||||
|
||||
class DataFilesPage : public QWidget, private Ui::DataFilesPage
|
||||
namespace Launcher
|
||||
{
|
||||
Q_OBJECT
|
||||
class TextInputDialog;
|
||||
class GameSettings;
|
||||
class LauncherSettings;
|
||||
class ProfilesComboBox;
|
||||
|
||||
public:
|
||||
DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent = 0);
|
||||
class DataFilesPage : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QAbstractItemModel* profilesComboBoxModel();
|
||||
int profilesComboBoxIndex();
|
||||
ContentSelectorView::ContentSelector *mSelector;
|
||||
Ui::DataFilesPage ui;
|
||||
|
||||
void writeConfig(QString profile = QString());
|
||||
void saveSettings();
|
||||
public:
|
||||
explicit DataFilesPage (Files::ConfigurationManager &cfg, GameSettings &gameSettings,
|
||||
LauncherSettings &launcherSettings, QWidget *parent = 0);
|
||||
|
||||
signals:
|
||||
void profileChanged(int index);
|
||||
QAbstractItemModel* profilesModel() const;
|
||||
|
||||
public slots:
|
||||
void setCheckState(QModelIndex index);
|
||||
void setProfilesComboBoxIndex(int index);
|
||||
int profilesIndex() const;
|
||||
|
||||
void filterChanged(const QString filter);
|
||||
void showContextMenu(const QPoint &point);
|
||||
void profileChanged(const QString &previous, const QString ¤t);
|
||||
void profileRenamed(const QString &previous, const QString ¤t);
|
||||
void updateOkButton(const QString &text);
|
||||
void updateSplitter();
|
||||
void updateViews();
|
||||
//void writeConfig(QString profile = QString());
|
||||
void saveSettings(const QString &profile = "");
|
||||
void loadSettings();
|
||||
|
||||
// Action slots
|
||||
void on_newProfileAction_triggered();
|
||||
void on_deleteProfileAction_triggered();
|
||||
void on_checkAction_triggered();
|
||||
void on_uncheckAction_triggered();
|
||||
signals:
|
||||
void signalProfileChanged (int index);
|
||||
|
||||
private slots:
|
||||
void slotCurrentIndexChanged(int index);
|
||||
public slots:
|
||||
void slotProfileChanged (int index);
|
||||
|
||||
private:
|
||||
DataFilesModel *mDataFilesModel;
|
||||
private slots:
|
||||
|
||||
PluginsProxyModel *mPluginsProxyModel;
|
||||
QSortFilterProxyModel *mMastersProxyModel;
|
||||
void slotProfileChangedByUser(const QString &previous, const QString ¤t);
|
||||
void slotProfileRenamed(const QString &previous, const QString ¤t);
|
||||
void slotProfileDeleted(const QString &item);
|
||||
|
||||
QSortFilterProxyModel *mFilterProxyModel;
|
||||
void on_newProfileAction_triggered();
|
||||
void on_deleteProfileAction_triggered();
|
||||
|
||||
QMenu *mContextMenu;
|
||||
private:
|
||||
|
||||
Files::ConfigurationManager &mCfgMgr;
|
||||
QMenu *mContextMenu;
|
||||
|
||||
GameSettings &mGameSettings;
|
||||
LauncherSettings &mLauncherSettings;
|
||||
Files::ConfigurationManager &mCfgMgr;
|
||||
|
||||
TextInputDialog *mNewProfileDialog;
|
||||
GameSettings &mGameSettings;
|
||||
LauncherSettings &mLauncherSettings;
|
||||
|
||||
void setMastersCheckstates(Qt::CheckState state);
|
||||
void setPluginsCheckstates(Qt::CheckState state);
|
||||
QString mDataLocal;
|
||||
|
||||
void createActions();
|
||||
void setupDataFiles();
|
||||
void setupConfig();
|
||||
void readConfig();
|
||||
void setPluginsCheckstates(Qt::CheckState state);
|
||||
|
||||
void loadSettings();
|
||||
void buildView();
|
||||
void setupDataFiles();
|
||||
void setupConfig();
|
||||
void readConfig();
|
||||
void setProfile (int index, bool savePrevious);
|
||||
void setProfile (const QString &previous, const QString ¤t, bool savePrevious);
|
||||
void removeProfile (const QString &profile);
|
||||
bool showDeleteMessageBox (const QString &text);
|
||||
void addProfile (const QString &profile, bool setAsCurrent);
|
||||
void checkForDefaultProfile();
|
||||
|
||||
};
|
||||
class PathIterator
|
||||
{
|
||||
QStringList::ConstIterator mCitEnd;
|
||||
QStringList::ConstIterator mCitCurrent;
|
||||
QStringList::ConstIterator mCitBegin;
|
||||
QString mFile;
|
||||
QString mFilePath;
|
||||
|
||||
public:
|
||||
PathIterator (const QStringList &list)
|
||||
{
|
||||
mCitBegin = list.constBegin();
|
||||
mCitCurrent = mCitBegin;
|
||||
mCitEnd = list.constEnd();
|
||||
}
|
||||
|
||||
QString findFirstPath (const QString &file)
|
||||
{
|
||||
mCitCurrent = mCitBegin;
|
||||
mFile = file;
|
||||
return path();
|
||||
}
|
||||
|
||||
QString findNextPath () { return path(); }
|
||||
|
||||
private:
|
||||
|
||||
QString path ()
|
||||
{
|
||||
bool success = false;
|
||||
QDir dir;
|
||||
QFileInfo file;
|
||||
|
||||
while (!success)
|
||||
{
|
||||
if (mCitCurrent == mCitEnd)
|
||||
break;
|
||||
|
||||
dir.setPath (*(mCitCurrent++));
|
||||
file.setFile (dir.absoluteFilePath (mFile));
|
||||
|
||||
success = file.exists();
|
||||
}
|
||||
|
||||
if (success)
|
||||
return file.absoluteFilePath();
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -4,20 +4,19 @@
|
|||
#include <QMessageBox>
|
||||
#include <QDir>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#ifdef MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
#undef MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
// We need to do this because of Qt: https://bugreports.qt-project.org/browse/QTBUG-22154
|
||||
#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
|
||||
#endif
|
||||
#include <SDL.h>
|
||||
#endif // MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
|
||||
#include <cstdlib>
|
||||
#include <SDL.h>
|
||||
|
||||
#include <boost/math/common_factor.hpp>
|
||||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#include <components/files/ogreplugin.hpp>
|
||||
|
||||
#include <components/fileorderlist/utils/naturalsort.hpp>
|
||||
#include <components/contentselector/model/naturalsort.hpp>
|
||||
|
||||
#include "settings/graphicssettings.hpp"
|
||||
|
||||
|
@ -33,11 +32,12 @@ QString getAspect(int x, int y)
|
|||
return QString(QString::number(xaspect) + ":" + QString::number(yaspect));
|
||||
}
|
||||
|
||||
GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSetting, QWidget *parent)
|
||||
Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSetting, QWidget *parent)
|
||||
: mCfgMgr(cfg)
|
||||
, mGraphicsSettings(graphicsSetting)
|
||||
, QWidget(parent)
|
||||
{
|
||||
setObjectName ("GraphicsPage");
|
||||
setupUi(this);
|
||||
|
||||
// Set the maximum res we can set in windowed mode
|
||||
|
@ -52,15 +52,11 @@ GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &g
|
|||
|
||||
}
|
||||
|
||||
bool GraphicsPage::setupOgre()
|
||||
bool Launcher::GraphicsPage::setupOgre()
|
||||
{
|
||||
// Create a log manager so we can surpress debug text to stdout/stderr
|
||||
Ogre::LogManager* logMgr = OGRE_NEW Ogre::LogManager;
|
||||
logMgr->createLog((mCfgMgr.getLogPath().string() + "/launcherOgre.log"), true, false, false);
|
||||
|
||||
try
|
||||
{
|
||||
mOgre = new Ogre::Root("", "", "./launcherOgre.log");
|
||||
mOgre = mOgreInit.init(mCfgMgr.getLogPath().string() + "/launcherOgre.log");
|
||||
}
|
||||
catch(Ogre::Exception &ex)
|
||||
{
|
||||
|
@ -78,40 +74,6 @@ bool GraphicsPage::setupOgre()
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
std::string pluginDir;
|
||||
const char* pluginEnv = getenv("OPENMW_OGRE_PLUGIN_DIR");
|
||||
if (pluginEnv)
|
||||
pluginDir = pluginEnv;
|
||||
else
|
||||
{
|
||||
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
|
||||
pluginDir = ".\\";
|
||||
#endif
|
||||
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
|
||||
pluginDir = OGRE_PLUGIN_DIR;
|
||||
#endif
|
||||
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX
|
||||
pluginDir = OGRE_PLUGIN_DIR_REL;
|
||||
#endif
|
||||
}
|
||||
|
||||
QDir dir(QString::fromStdString(pluginDir));
|
||||
pluginDir = dir.absolutePath().toStdString();
|
||||
|
||||
Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mOgre);
|
||||
Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mOgre);
|
||||
Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mOgre);
|
||||
|
||||
#ifdef ENABLE_PLUGIN_GL
|
||||
mGLPlugin = new Ogre::GLPlugin();
|
||||
mOgre->installPlugin(mGLPlugin);
|
||||
#endif
|
||||
#ifdef ENABLE_PLUGIN_Direct3D9
|
||||
mD3D9Plugin = new Ogre::D3D9Plugin();
|
||||
mOgre->installPlugin(mD3D9Plugin);
|
||||
#endif
|
||||
|
||||
// Get the available renderers and put them in the combobox
|
||||
const Ogre::RenderSystemList &renderers = mOgre->getAvailableRenderers();
|
||||
|
||||
|
@ -133,7 +95,7 @@ bool GraphicsPage::setupOgre()
|
|||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<br><b>Could not select a valid render system</b><br><br> \
|
||||
Please make sure the plugins.cfg file exists and contains a valid rendering plugin.<br>"));
|
||||
Please make sure Ogre plugins were installed correctly.<br>"));
|
||||
msgBox.exec();
|
||||
return false;
|
||||
}
|
||||
|
@ -156,7 +118,7 @@ bool GraphicsPage::setupOgre()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GraphicsPage::setupSDL()
|
||||
bool Launcher::GraphicsPage::setupSDL()
|
||||
{
|
||||
int displays = SDL_GetNumVideoDisplays();
|
||||
|
||||
|
@ -179,7 +141,7 @@ bool GraphicsPage::setupSDL()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GraphicsPage::loadSettings()
|
||||
bool Launcher::GraphicsPage::loadSettings()
|
||||
{
|
||||
if (!setupSDL())
|
||||
return false;
|
||||
|
@ -218,7 +180,7 @@ bool GraphicsPage::loadSettings()
|
|||
return true;
|
||||
}
|
||||
|
||||
void GraphicsPage::saveSettings()
|
||||
void Launcher::GraphicsPage::saveSettings()
|
||||
{
|
||||
vSyncCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/vsync"), QString("true"))
|
||||
: mGraphicsSettings.setValue(QString("Video/vsync"), QString("false"));
|
||||
|
@ -245,7 +207,7 @@ void GraphicsPage::saveSettings()
|
|||
mGraphicsSettings.setValue(QString("Video/screen"), QString::number(screenComboBox->currentIndex()));
|
||||
}
|
||||
|
||||
QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer)
|
||||
QStringList Launcher::GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer)
|
||||
{
|
||||
QStringList result;
|
||||
|
||||
|
@ -278,7 +240,7 @@ QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSy
|
|||
return result;
|
||||
}
|
||||
|
||||
QStringList GraphicsPage::getAvailableResolutions(int screen)
|
||||
QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
|
||||
{
|
||||
QStringList result;
|
||||
SDL_DisplayMode mode;
|
||||
|
@ -325,7 +287,7 @@ QStringList GraphicsPage::getAvailableResolutions(int screen)
|
|||
return result;
|
||||
}
|
||||
|
||||
QRect GraphicsPage::getMaximumResolution()
|
||||
QRect Launcher::GraphicsPage::getMaximumResolution()
|
||||
{
|
||||
QRect max;
|
||||
int screens = QApplication::desktop()->screenCount();
|
||||
|
@ -340,7 +302,7 @@ QRect GraphicsPage::getMaximumResolution()
|
|||
return max;
|
||||
}
|
||||
|
||||
void GraphicsPage::rendererChanged(const QString &renderer)
|
||||
void Launcher::GraphicsPage::rendererChanged(const QString &renderer)
|
||||
{
|
||||
mSelectedRenderSystem = mOgre->getRenderSystemByName(renderer.toStdString());
|
||||
|
||||
|
@ -349,7 +311,7 @@ void GraphicsPage::rendererChanged(const QString &renderer)
|
|||
antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem));
|
||||
}
|
||||
|
||||
void GraphicsPage::screenChanged(int screen)
|
||||
void Launcher::GraphicsPage::screenChanged(int screen)
|
||||
{
|
||||
if (screen >= 0) {
|
||||
resolutionComboBox->clear();
|
||||
|
@ -357,7 +319,7 @@ void GraphicsPage::screenChanged(int screen)
|
|||
}
|
||||
}
|
||||
|
||||
void GraphicsPage::slotFullScreenChanged(int state)
|
||||
void Launcher::GraphicsPage::slotFullScreenChanged(int state)
|
||||
{
|
||||
if (state == Qt::Checked) {
|
||||
standardRadioButton->toggle();
|
||||
|
@ -371,7 +333,7 @@ void GraphicsPage::slotFullScreenChanged(int state)
|
|||
}
|
||||
}
|
||||
|
||||
void GraphicsPage::slotStandardToggled(bool checked)
|
||||
void Launcher::GraphicsPage::slotStandardToggled(bool checked)
|
||||
{
|
||||
if (checked) {
|
||||
resolutionComboBox->setEnabled(true);
|
||||
|
|
|
@ -5,62 +5,53 @@
|
|||
|
||||
#include <OgreRoot.h>
|
||||
#include <OgreRenderSystem.h>
|
||||
//#include <OgreConfigFile.h>
|
||||
//#include <OgreConfigDialog.h>
|
||||
|
||||
// Static plugin headers
|
||||
#ifdef ENABLE_PLUGIN_GL
|
||||
# include "OgreGLPlugin.h"
|
||||
#endif
|
||||
#ifdef ENABLE_PLUGIN_Direct3D9
|
||||
# include "OgreD3D9Plugin.h"
|
||||
#endif
|
||||
#include <components/ogreinit/ogreinit.hpp>
|
||||
|
||||
|
||||
#include "ui_graphicspage.h"
|
||||
|
||||
class GraphicsSettings;
|
||||
|
||||
namespace Files { struct ConfigurationManager; }
|
||||
|
||||
class GraphicsPage : public QWidget, private Ui::GraphicsPage
|
||||
namespace Launcher
|
||||
{
|
||||
Q_OBJECT
|
||||
class GraphicsSettings;
|
||||
|
||||
public:
|
||||
GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0);
|
||||
class GraphicsPage : public QWidget, private Ui::GraphicsPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
void saveSettings();
|
||||
bool loadSettings();
|
||||
public:
|
||||
GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0);
|
||||
|
||||
public slots:
|
||||
void rendererChanged(const QString &renderer);
|
||||
void screenChanged(int screen);
|
||||
void saveSettings();
|
||||
bool loadSettings();
|
||||
|
||||
private slots:
|
||||
void slotFullScreenChanged(int state);
|
||||
void slotStandardToggled(bool checked);
|
||||
public slots:
|
||||
void rendererChanged(const QString &renderer);
|
||||
void screenChanged(int screen);
|
||||
|
||||
private:
|
||||
Ogre::Root *mOgre;
|
||||
Ogre::RenderSystem *mSelectedRenderSystem;
|
||||
Ogre::RenderSystem *mOpenGLRenderSystem;
|
||||
Ogre::RenderSystem *mDirect3DRenderSystem;
|
||||
#ifdef ENABLE_PLUGIN_GL
|
||||
Ogre::GLPlugin* mGLPlugin;
|
||||
#endif
|
||||
#ifdef ENABLE_PLUGIN_Direct3D9
|
||||
Ogre::D3D9Plugin* mD3D9Plugin;
|
||||
#endif
|
||||
private slots:
|
||||
void slotFullScreenChanged(int state);
|
||||
void slotStandardToggled(bool checked);
|
||||
|
||||
Files::ConfigurationManager &mCfgMgr;
|
||||
GraphicsSettings &mGraphicsSettings;
|
||||
private:
|
||||
OgreInit::OgreInit mOgreInit;
|
||||
Ogre::Root *mOgre;
|
||||
Ogre::RenderSystem *mSelectedRenderSystem;
|
||||
Ogre::RenderSystem *mOpenGLRenderSystem;
|
||||
Ogre::RenderSystem *mDirect3DRenderSystem;
|
||||
|
||||
QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer);
|
||||
QStringList getAvailableResolutions(int screen);
|
||||
QRect getMaximumResolution();
|
||||
Files::ConfigurationManager &mCfgMgr;
|
||||
GraphicsSettings &mGraphicsSettings;
|
||||
|
||||
bool setupOgre();
|
||||
bool setupSDL();
|
||||
};
|
||||
QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer);
|
||||
QStringList getAvailableResolutions(int screen);
|
||||
QRect getMaximumResolution();
|
||||
|
||||
bool setupOgre();
|
||||
bool setupSDL();
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -3,19 +3,20 @@
|
|||
#include <QDir>
|
||||
#include <QDebug>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#ifdef MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
#undef MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
// We need to do this because of Qt: https://bugreports.qt-project.org/browse/QTBUG-22154
|
||||
#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
|
||||
#endif
|
||||
#endif // MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include "maindialog.hpp"
|
||||
// SDL workaround
|
||||
#include "graphicspage.hpp"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
|
||||
SDL_SetMainReady();
|
||||
if (SDL_Init(SDL_INIT_VIDEO) != 0)
|
||||
{
|
||||
qDebug() << "SDL_Init failed: " << QString::fromStdString(SDL_GetError());
|
||||
|
@ -49,7 +50,7 @@ int main(int argc, char *argv[])
|
|||
// Support non-latin characters
|
||||
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
|
||||
|
||||
MainDialog mainWin;
|
||||
Launcher::MainDialog mainWin;
|
||||
|
||||
if (mainWin.setup()) {
|
||||
mainWin.show();
|
||||
|
@ -61,4 +62,3 @@ int main(int argc, char *argv[])
|
|||
SDL_Quit();
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
#include "maindialog.hpp"
|
||||
|
||||
#include <components/version/version.hpp>
|
||||
|
||||
#include <QLabel>
|
||||
#include <QDate>
|
||||
#include <QTime>
|
||||
#include <QPushButton>
|
||||
#include <QFontDatabase>
|
||||
#include <QInputDialog>
|
||||
#include <QFileDialog>
|
||||
|
@ -23,8 +29,8 @@
|
|||
#include "graphicspage.hpp"
|
||||
#include "datafilespage.hpp"
|
||||
|
||||
MainDialog::MainDialog()
|
||||
: mGameSettings(mCfgMgr)
|
||||
Launcher::MainDialog::MainDialog(QWidget *parent)
|
||||
: mGameSettings(mCfgMgr), QMainWindow (parent)
|
||||
{
|
||||
// Install the stylesheet font
|
||||
QFile file;
|
||||
|
@ -66,10 +72,26 @@ MainDialog::MainDialog()
|
|||
// Remove what's this? button
|
||||
setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
// Add version information to bottom of the window
|
||||
QString revision(OPENMW_VERSION_COMMITHASH);
|
||||
QString tag(OPENMW_VERSION_TAGHASH);
|
||||
|
||||
if (revision == tag) {
|
||||
versionLabel->setText(tr("OpenMW %0 release").arg(OPENMW_VERSION));
|
||||
} else {
|
||||
versionLabel->setText(tr("OpenMW development (%0)").arg(revision.left(10)));
|
||||
}
|
||||
|
||||
// Add the compile date and time
|
||||
versionLabel->setToolTip(tr("Compiled on %0 %1").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(),
|
||||
QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate),
|
||||
QLocale(QLocale::C).toTime(QString(__TIME__).simplified(),
|
||||
QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate)));
|
||||
|
||||
createIcons();
|
||||
}
|
||||
|
||||
void MainDialog::createIcons()
|
||||
void Launcher::MainDialog::createIcons()
|
||||
{
|
||||
if (!QIcon::hasThemeIcon("document-new"))
|
||||
QIcon::setThemeName("tango");
|
||||
|
@ -101,15 +123,15 @@ void MainDialog::createIcons()
|
|||
|
||||
}
|
||||
|
||||
void MainDialog::createPages()
|
||||
void Launcher::MainDialog::createPages()
|
||||
{
|
||||
mPlayPage = new PlayPage(this);
|
||||
mGraphicsPage = new GraphicsPage(mCfgMgr, mGraphicsSettings, this);
|
||||
mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
|
||||
|
||||
// Set the combobox of the play page to imitate the combobox on the datafilespage
|
||||
mPlayPage->setProfilesComboBoxModel(mDataFilesPage->profilesComboBoxModel());
|
||||
mPlayPage->setProfilesComboBoxIndex(mDataFilesPage->profilesComboBoxIndex());
|
||||
mPlayPage->setProfilesModel(mDataFilesPage->profilesModel());
|
||||
mPlayPage->setProfilesIndex(mDataFilesPage->profilesIndex());
|
||||
|
||||
// Add the pages to the stacked widget
|
||||
pagesWidget->addWidget(mPlayPage);
|
||||
|
@ -121,12 +143,12 @@ void MainDialog::createPages()
|
|||
|
||||
connect(mPlayPage, SIGNAL(playButtonClicked()), this, SLOT(play()));
|
||||
|
||||
connect(mPlayPage, SIGNAL(profileChanged(int)), mDataFilesPage, SLOT(setProfilesComboBoxIndex(int)));
|
||||
connect(mDataFilesPage, SIGNAL(profileChanged(int)), mPlayPage, SLOT(setProfilesComboBoxIndex(int)));
|
||||
connect(mPlayPage, SIGNAL(signalProfileChanged(int)), mDataFilesPage, SLOT(slotProfileChanged(int)));
|
||||
connect(mDataFilesPage, SIGNAL(signalProfileChanged(int)), mPlayPage, SLOT(setProfilesIndex(int)));
|
||||
|
||||
}
|
||||
|
||||
bool MainDialog::showFirstRunDialog()
|
||||
bool Launcher::MainDialog::showFirstRunDialog()
|
||||
{
|
||||
QStringList iniPaths;
|
||||
|
||||
|
@ -218,7 +240,7 @@ bool MainDialog::showFirstRunDialog()
|
|||
}
|
||||
|
||||
// Create the file if it doesn't already exist, else the importer will fail
|
||||
QString path = QString::fromStdString(mCfgMgr.getUserPath().string()) + QString("openmw.cfg");
|
||||
QString path = QString::fromStdString(mCfgMgr.getUserConfigPath().string()) + QString("openmw.cfg");
|
||||
QFile file(path);
|
||||
|
||||
if (!file.exists()) {
|
||||
|
@ -261,19 +283,11 @@ bool MainDialog::showFirstRunDialog()
|
|||
// Add a new profile
|
||||
if (msgBox.isChecked()) {
|
||||
mLauncherSettings.setValue(QString("Profiles/currentprofile"), QString("Imported"));
|
||||
mLauncherSettings.remove(QString("Profiles/Imported/content"));
|
||||
|
||||
mLauncherSettings.remove(QString("Profiles/Imported/master"));
|
||||
mLauncherSettings.remove(QString("Profiles/Imported/plugin"));
|
||||
|
||||
QStringList masters = mGameSettings.values(QString("master"));
|
||||
QStringList plugins = mGameSettings.values(QString("plugin"));
|
||||
|
||||
foreach (const QString &master, masters) {
|
||||
mLauncherSettings.setMultiValue(QString("Profiles/Imported/master"), master);
|
||||
}
|
||||
|
||||
foreach (const QString &plugin, plugins) {
|
||||
mLauncherSettings.setMultiValue(QString("Profiles/Imported/plugin"), plugin);
|
||||
QStringList contents = mGameSettings.values(QString("content"));
|
||||
foreach (const QString &content, contents) {
|
||||
mLauncherSettings.setMultiValue(QString("Profiles/Imported/content"), content);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -282,7 +296,7 @@ bool MainDialog::showFirstRunDialog()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool MainDialog::setup()
|
||||
bool Launcher::MainDialog::setup()
|
||||
{
|
||||
if (!setupLauncherSettings())
|
||||
return false;
|
||||
|
@ -311,19 +325,37 @@ bool MainDialog::setup()
|
|||
return true;
|
||||
}
|
||||
|
||||
void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous)
|
||||
void Launcher::MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous)
|
||||
{
|
||||
if (!current)
|
||||
current = previous;
|
||||
|
||||
pagesWidget->setCurrentIndex(iconWidget->row(current));
|
||||
int currentIndex = iconWidget->row(current);
|
||||
int previousIndex = iconWidget->row(previous);
|
||||
|
||||
pagesWidget->setCurrentIndex(currentIndex);
|
||||
|
||||
DataFilesPage *previousPage = dynamic_cast<DataFilesPage *>(pagesWidget->widget(previousIndex));
|
||||
DataFilesPage *currentPage = dynamic_cast<DataFilesPage *>(pagesWidget->widget(currentIndex));
|
||||
|
||||
//special call to update/save data files page list view when it's displayed/hidden.
|
||||
if (previousPage)
|
||||
{
|
||||
if (previousPage->objectName() == "DataFilesPage")
|
||||
previousPage->saveSettings();
|
||||
}
|
||||
else if (currentPage)
|
||||
{
|
||||
if (currentPage->objectName() == "DataFilesPage")
|
||||
currentPage->loadSettings();
|
||||
}
|
||||
}
|
||||
|
||||
bool MainDialog::setupLauncherSettings()
|
||||
bool Launcher::MainDialog::setupLauncherSettings()
|
||||
{
|
||||
mLauncherSettings.setMultiValueEnabled(true);
|
||||
|
||||
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
|
||||
QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string());
|
||||
|
||||
QStringList paths;
|
||||
paths.append(QString("launcher.cfg"));
|
||||
|
@ -356,7 +388,7 @@ bool MainDialog::setupLauncherSettings()
|
|||
}
|
||||
|
||||
#ifndef WIN32
|
||||
bool expansions(UnshieldThread& cd)
|
||||
bool Launcher::expansions(Launcher::UnshieldThread& cd)
|
||||
{
|
||||
if(cd.BloodmoonDone())
|
||||
{
|
||||
|
@ -367,7 +399,7 @@ bool expansions(UnshieldThread& cd)
|
|||
QMessageBox expansionsBox;
|
||||
expansionsBox.setText(QObject::tr("<br>Would you like to install expansions now ? (make sure you have the disc)<br> \
|
||||
If you want to install both Bloodmoon and Tribunal, you have to install Tribunal first.<br>"));
|
||||
|
||||
|
||||
QAbstractButton* tribunalButton = NULL;
|
||||
if(!cd.TribunalDone())
|
||||
tribunalButton = expansionsBox.addButton(QObject::tr("&Tribunal"), QMessageBox::ActionRole);
|
||||
|
@ -386,7 +418,7 @@ bool expansions(UnshieldThread& cd)
|
|||
{
|
||||
|
||||
TextSlotMsgBox cdbox;
|
||||
cdbox.setStandardButtons(QMessageBox::Cancel);
|
||||
cdbox.setStandardButtons(QMessageBox::Cancel);
|
||||
|
||||
QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&)));
|
||||
QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject()));
|
||||
|
@ -405,7 +437,7 @@ bool expansions(UnshieldThread& cd)
|
|||
{
|
||||
|
||||
TextSlotMsgBox cdbox;
|
||||
cdbox.setStandardButtons(QMessageBox::Cancel);
|
||||
cdbox.setStandardButtons(QMessageBox::Cancel);
|
||||
|
||||
QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&)));
|
||||
QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject()));
|
||||
|
@ -427,11 +459,37 @@ bool expansions(UnshieldThread& cd)
|
|||
}
|
||||
#endif // WIN32
|
||||
|
||||
bool MainDialog::setupGameSettings()
|
||||
bool Launcher::MainDialog::setupGameSettings()
|
||||
{
|
||||
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
|
||||
QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string());
|
||||
QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string());
|
||||
|
||||
// Load the user config file first, separately
|
||||
// So we can write it properly, uncontaminated
|
||||
QString path = userPath + QLatin1String("openmw.cfg");
|
||||
QFile file(path);
|
||||
|
||||
qDebug() << "Loading config file:" << qPrintable(path);
|
||||
|
||||
if (file.exists()) {
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
|
||||
msgBox.setIcon(QMessageBox::Critical);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(QObject::tr("<br><b>Could not open %0 for reading</b><br><br> \
|
||||
Please make sure you have the right permissions \
|
||||
and try again.<br>").arg(file.fileName()));
|
||||
msgBox.exec();
|
||||
return false;
|
||||
}
|
||||
QTextStream stream(&file);
|
||||
stream.setCodec(QTextCodec::codecForName("UTF-8"));
|
||||
|
||||
mGameSettings.readUserFile(stream);
|
||||
}
|
||||
|
||||
// Now the rest
|
||||
QStringList paths;
|
||||
paths.append(userPath + QString("openmw.cfg"));
|
||||
paths.append(QString("openmw.cfg"));
|
||||
|
@ -467,7 +525,7 @@ bool MainDialog::setupGameSettings()
|
|||
foreach (const QString path, mGameSettings.getDataDirs()) {
|
||||
QDir dir(path);
|
||||
QStringList filters;
|
||||
filters << "*.esp" << "*.esm";
|
||||
filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon";
|
||||
|
||||
if (!dir.entryList(filters).isEmpty())
|
||||
dataDirs.append(path);
|
||||
|
@ -485,12 +543,12 @@ bool MainDialog::setupGameSettings()
|
|||
|
||||
QAbstractButton *dirSelectButton =
|
||||
msgBox.addButton(QObject::tr("Browse to &Install..."), QMessageBox::ActionRole);
|
||||
|
||||
|
||||
#ifndef WIN32
|
||||
QAbstractButton *cdSelectButton =
|
||||
QAbstractButton *cdSelectButton =
|
||||
msgBox.addButton(QObject::tr("Browse to &CD..."), QMessageBox::ActionRole);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
msgBox.exec();
|
||||
|
||||
|
@ -505,14 +563,14 @@ bool MainDialog::setupGameSettings()
|
|||
#ifndef WIN32
|
||||
else if(msgBox.clickedButton() == cdSelectButton) {
|
||||
UnshieldThread cd;
|
||||
|
||||
|
||||
{
|
||||
TextSlotMsgBox cdbox;
|
||||
cdbox.setStandardButtons(QMessageBox::Cancel);
|
||||
cdbox.setStandardButtons(QMessageBox::Cancel);
|
||||
|
||||
QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&)));
|
||||
QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject()));
|
||||
|
||||
|
||||
cd.SetMorrowindPath(
|
||||
QFileDialog::getOpenFileName(
|
||||
NULL,
|
||||
|
@ -526,11 +584,11 @@ bool MainDialog::setupGameSettings()
|
|||
QObject::tr("Select where to extract files to"),
|
||||
QDir::currentPath(),
|
||||
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks).toUtf8().constData());
|
||||
|
||||
|
||||
cd.start();
|
||||
cdbox.exec();
|
||||
}
|
||||
|
||||
|
||||
while(expansions(cd));
|
||||
|
||||
selectedFile = QString::fromStdString(cd.GetMWEsmPath());
|
||||
|
@ -550,11 +608,11 @@ bool MainDialog::setupGameSettings()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool MainDialog::setupGraphicsSettings()
|
||||
bool Launcher::MainDialog::setupGraphicsSettings()
|
||||
{
|
||||
mGraphicsSettings.setMultiValueEnabled(false);
|
||||
|
||||
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
|
||||
QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string());
|
||||
QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string());
|
||||
|
||||
QFile localDefault(QString("settings-default.cfg"));
|
||||
|
@ -604,7 +662,7 @@ bool MainDialog::setupGraphicsSettings()
|
|||
return true;
|
||||
}
|
||||
|
||||
void MainDialog::loadSettings()
|
||||
void Launcher::MainDialog::loadSettings()
|
||||
{
|
||||
int width = mLauncherSettings.value(QString("General/MainWindow/width")).toInt();
|
||||
int height = mLauncherSettings.value(QString("General/MainWindow/height")).toInt();
|
||||
|
@ -616,7 +674,7 @@ void MainDialog::loadSettings()
|
|||
move(posX, posY);
|
||||
}
|
||||
|
||||
void MainDialog::saveSettings()
|
||||
void Launcher::MainDialog::saveSettings()
|
||||
{
|
||||
QString width = QString::number(this->width());
|
||||
QString height = QString::number(this->height());
|
||||
|
@ -634,14 +692,14 @@ void MainDialog::saveSettings()
|
|||
|
||||
}
|
||||
|
||||
bool MainDialog::writeSettings()
|
||||
bool Launcher::MainDialog::writeSettings()
|
||||
{
|
||||
// Now write all config files
|
||||
saveSettings();
|
||||
mGraphicsPage->saveSettings();
|
||||
mDataFilesPage->saveSettings();
|
||||
|
||||
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
|
||||
QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string());
|
||||
QDir dir(userPath);
|
||||
|
||||
if (!dir.exists()) {
|
||||
|
@ -727,13 +785,13 @@ bool MainDialog::writeSettings()
|
|||
return true;
|
||||
}
|
||||
|
||||
void MainDialog::closeEvent(QCloseEvent *event)
|
||||
void Launcher::MainDialog::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
writeSettings();
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void MainDialog::play()
|
||||
void Launcher::MainDialog::play()
|
||||
{
|
||||
if (!writeSettings()) {
|
||||
qApp->quit();
|
||||
|
@ -742,11 +800,11 @@ void MainDialog::play()
|
|||
|
||||
if(!mGameSettings.hasMaster()) {
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle(tr("No master file selected"));
|
||||
msgBox.setWindowTitle(tr("No game file selected"));
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setText(tr("<br><b>You do not have any master files selected.</b><br><br> \
|
||||
OpenMW will not start without a master file selected.<br>"));
|
||||
msgBox.setText(tr("<br><b>You do not have no game file selected.</b><br><br> \
|
||||
OpenMW will not start without a game file selected.<br>"));
|
||||
msgBox.exec();
|
||||
return;
|
||||
}
|
||||
|
@ -756,7 +814,7 @@ void MainDialog::play()
|
|||
qApp->quit();
|
||||
}
|
||||
|
||||
bool MainDialog::startProgram(const QString &name, const QStringList &arguments, bool detached)
|
||||
bool Launcher::MainDialog::startProgram(const QString &name, const QStringList &arguments, bool detached)
|
||||
{
|
||||
QString path = name;
|
||||
#ifdef Q_OS_WIN
|
||||
|
|
|
@ -11,57 +11,59 @@
|
|||
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
class QListWidget;
|
||||
class QListWidgetItem;
|
||||
class QStackedWidget;
|
||||
class QStringList;
|
||||
class QStringListModel;
|
||||
class QString;
|
||||
|
||||
class PlayPage;
|
||||
class GraphicsPage;
|
||||
class DataFilesPage;
|
||||
|
||||
class MainDialog : public QMainWindow, private Ui::MainWindow
|
||||
namespace Launcher
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MainDialog();
|
||||
bool setup();
|
||||
bool showFirstRunDialog();
|
||||
|
||||
public slots:
|
||||
void changePage(QListWidgetItem *current, QListWidgetItem *previous);
|
||||
void play();
|
||||
|
||||
private:
|
||||
void createIcons();
|
||||
void createPages();
|
||||
|
||||
bool setupLauncherSettings();
|
||||
bool setupGameSettings();
|
||||
bool setupGraphicsSettings();
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
bool writeSettings();
|
||||
|
||||
inline bool startProgram(const QString &name, bool detached = false) { return startProgram(name, QStringList(), detached); }
|
||||
bool startProgram(const QString &name, const QStringList &arguments, bool detached = false);
|
||||
|
||||
void closeEvent(QCloseEvent *event);
|
||||
|
||||
PlayPage *mPlayPage;
|
||||
GraphicsPage *mGraphicsPage;
|
||||
DataFilesPage *mDataFilesPage;
|
||||
|
||||
Files::ConfigurationManager mCfgMgr;
|
||||
|
||||
GameSettings mGameSettings;
|
||||
GraphicsSettings mGraphicsSettings;
|
||||
LauncherSettings mLauncherSettings;
|
||||
|
||||
};
|
||||
class PlayPage;
|
||||
class GraphicsPage;
|
||||
class DataFilesPage;
|
||||
class UnshieldThread;
|
||||
|
||||
#ifndef WIN32
|
||||
bool expansions(Launcher::UnshieldThread& cd);
|
||||
#endif
|
||||
|
||||
class MainDialog : public QMainWindow, private Ui::MainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainDialog(QWidget *parent = 0);
|
||||
bool setup();
|
||||
bool showFirstRunDialog();
|
||||
|
||||
public slots:
|
||||
void changePage(QListWidgetItem *current, QListWidgetItem *previous);
|
||||
void play();
|
||||
|
||||
private:
|
||||
void createIcons();
|
||||
void createPages();
|
||||
|
||||
bool setupLauncherSettings();
|
||||
bool setupGameSettings();
|
||||
bool setupGraphicsSettings();
|
||||
|
||||
void loadSettings();
|
||||
void saveSettings();
|
||||
bool writeSettings();
|
||||
|
||||
inline bool startProgram(const QString &name, bool detached = false) { return startProgram(name, QStringList(), detached); }
|
||||
bool startProgram(const QString &name, const QStringList &arguments, bool detached = false);
|
||||
|
||||
void closeEvent(QCloseEvent *event);
|
||||
|
||||
PlayPage *mPlayPage;
|
||||
GraphicsPage *mGraphicsPage;
|
||||
DataFilesPage *mDataFilesPage;
|
||||
|
||||
Files::ConfigurationManager mCfgMgr;
|
||||
|
||||
GameSettings mGameSettings;
|
||||
GraphicsSettings mGraphicsSettings;
|
||||
LauncherSettings mLauncherSettings;
|
||||
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
#include <QPlastiqueStyle>
|
||||
#endif
|
||||
|
||||
PlayPage::PlayPage(QWidget *parent) : QWidget(parent)
|
||||
Launcher::PlayPage::PlayPage(QWidget *parent) : QWidget(parent)
|
||||
{
|
||||
setObjectName ("PlayPage");
|
||||
setupUi(this);
|
||||
|
||||
// Hacks to get the stylesheet look properly
|
||||
|
@ -17,27 +18,22 @@ PlayPage::PlayPage(QWidget *parent) : QWidget(parent)
|
|||
#endif
|
||||
profilesComboBox->setView(new QListView());
|
||||
|
||||
connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentIndexChanged(int)));
|
||||
connect(profilesComboBox, SIGNAL(activated(int)), this, SIGNAL (signalProfileChanged(int)));
|
||||
connect(playButton, SIGNAL(clicked()), this, SLOT(slotPlayClicked()));
|
||||
|
||||
}
|
||||
|
||||
void PlayPage::setProfilesComboBoxModel(QAbstractItemModel *model)
|
||||
void Launcher::PlayPage::setProfilesModel(QAbstractItemModel *model)
|
||||
{
|
||||
profilesComboBox->setModel(model);
|
||||
}
|
||||
|
||||
void PlayPage::setProfilesComboBoxIndex(int index)
|
||||
void Launcher::PlayPage::setProfilesIndex(int index)
|
||||
{
|
||||
profilesComboBox->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
void PlayPage::slotCurrentIndexChanged(int index)
|
||||
{
|
||||
emit profileChanged(index);
|
||||
}
|
||||
|
||||
void PlayPage::slotPlayClicked()
|
||||
void Launcher::PlayPage::slotPlayClicked()
|
||||
{
|
||||
emit playButtonClicked();
|
||||
}
|
||||
|
|
|
@ -9,27 +9,28 @@ class QComboBox;
|
|||
class QPushButton;
|
||||
class QAbstractItemModel;
|
||||
|
||||
class PlayPage : public QWidget, private Ui::PlayPage
|
||||
namespace Launcher
|
||||
{
|
||||
Q_OBJECT
|
||||
class PlayPage : public QWidget, private Ui::PlayPage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PlayPage(QWidget *parent = 0);
|
||||
void setProfilesComboBoxModel(QAbstractItemModel *model);
|
||||
public:
|
||||
PlayPage(QWidget *parent = 0);
|
||||
void setProfilesModel(QAbstractItemModel *model);
|
||||
|
||||
signals:
|
||||
void profileChanged(int index);
|
||||
void playButtonClicked();
|
||||
signals:
|
||||
void signalProfileChanged(int index);
|
||||
void playButtonClicked();
|
||||
|
||||
public slots:
|
||||
void setProfilesComboBoxIndex(int index);
|
||||
public slots:
|
||||
void setProfilesIndex(int index);
|
||||
|
||||
private slots:
|
||||
void slotCurrentIndexChanged(int index);
|
||||
void slotPlayClicked();
|
||||
private slots:
|
||||
void slotPlayClicked();
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <components/files/configurationmanager.hpp>
|
||||
|
||||
#include <boost/version.hpp>
|
||||
|
||||
/**
|
||||
* Workaround for problems with whitespaces in paths in older versions of Boost library
|
||||
*/
|
||||
|
@ -26,16 +27,16 @@ namespace boost
|
|||
#endif /* (BOOST_VERSION <= 104600) */
|
||||
|
||||
|
||||
GameSettings::GameSettings(Files::ConfigurationManager &cfg)
|
||||
Launcher::GameSettings::GameSettings(Files::ConfigurationManager &cfg)
|
||||
: mCfgMgr(cfg)
|
||||
{
|
||||
}
|
||||
|
||||
GameSettings::~GameSettings()
|
||||
Launcher::GameSettings::~GameSettings()
|
||||
{
|
||||
}
|
||||
|
||||
void GameSettings::validatePaths()
|
||||
void Launcher::GameSettings::validatePaths()
|
||||
{
|
||||
if (mSettings.isEmpty() || !mDataDirs.isEmpty())
|
||||
return; // Don't re-validate paths if they are already parsed
|
||||
|
@ -81,14 +82,24 @@ void GameSettings::validatePaths()
|
|||
}
|
||||
}
|
||||
|
||||
QStringList GameSettings::values(const QString &key, const QStringList &defaultValues)
|
||||
QStringList Launcher::GameSettings::values(const QString &key, const QStringList &defaultValues)
|
||||
{
|
||||
if (!mSettings.values(key).isEmpty())
|
||||
return mSettings.values(key);
|
||||
return defaultValues;
|
||||
}
|
||||
|
||||
bool GameSettings::readFile(QTextStream &stream)
|
||||
bool Launcher::GameSettings::readFile(QTextStream &stream)
|
||||
{
|
||||
return readFile(stream, mSettings);
|
||||
}
|
||||
|
||||
bool Launcher::GameSettings::readUserFile(QTextStream &stream)
|
||||
{
|
||||
return readFile(stream, mUserSettings);
|
||||
}
|
||||
|
||||
bool Launcher::GameSettings::readFile(QTextStream &stream, QMap<QString, QString> &settings)
|
||||
{
|
||||
QMap<QString, QString> cache;
|
||||
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
|
||||
|
@ -106,10 +117,10 @@ bool GameSettings::readFile(QTextStream &stream)
|
|||
|
||||
// Don't remove existing data entries
|
||||
if (key != QLatin1String("data"))
|
||||
mSettings.remove(key);
|
||||
settings.remove(key);
|
||||
|
||||
QStringList values = cache.values(key);
|
||||
values.append(mSettings.values(key));
|
||||
values.append(settings.values(key));
|
||||
|
||||
if (!values.contains(value)) {
|
||||
cache.insertMulti(key, value);
|
||||
|
@ -117,35 +128,36 @@ bool GameSettings::readFile(QTextStream &stream)
|
|||
}
|
||||
}
|
||||
|
||||
if (mSettings.isEmpty()) {
|
||||
mSettings = cache; // This is the first time we read a file
|
||||
if (settings.isEmpty()) {
|
||||
settings = cache; // This is the first time we read a file
|
||||
validatePaths();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Merge the changed keys with those which didn't
|
||||
mSettings.unite(cache);
|
||||
settings.unite(cache);
|
||||
validatePaths();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameSettings::writeFile(QTextStream &stream)
|
||||
|
||||
bool Launcher::GameSettings::writeFile(QTextStream &stream)
|
||||
{
|
||||
// Iterate in reverse order to preserve insertion order
|
||||
QMapIterator<QString, QString> i(mSettings);
|
||||
QMapIterator<QString, QString> i(mUserSettings);
|
||||
i.toBack();
|
||||
|
||||
while (i.hasPrevious()) {
|
||||
i.previous();
|
||||
|
||||
if (i.key() == QLatin1String("master") || i.key() == QLatin1String("plugin"))
|
||||
if (i.key() == QLatin1String("content"))
|
||||
continue;
|
||||
|
||||
// Quote paths with spaces
|
||||
if (i.key() == QLatin1String("data")
|
||||
|| i.key() == QLatin1String("data-local")
|
||||
|| i.key() == QLatin1String("resources"))
|
||||
|| i.key() == QLatin1String("data-local")
|
||||
|| i.key() == QLatin1String("resources"))
|
||||
{
|
||||
if (i.value().contains(QChar(' ')))
|
||||
{
|
||||
|
@ -161,15 +173,24 @@ bool GameSettings::writeFile(QTextStream &stream)
|
|||
|
||||
}
|
||||
|
||||
QStringList masters = mSettings.values(QString("master"));
|
||||
for (int i = masters.count(); i--;) {
|
||||
stream << "master=" << masters.at(i) << "\n";
|
||||
}
|
||||
|
||||
QStringList plugins = mSettings.values(QString("plugin"));
|
||||
for (int i = plugins.count(); i--;) {
|
||||
stream << "plugin=" << plugins.at(i) << "\n";
|
||||
QStringList content = mUserSettings.values(QString("content"));
|
||||
for (int i = content.count(); i--;) {
|
||||
stream << "content=" << content.at(i) << "\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Launcher::GameSettings::hasMaster()
|
||||
{
|
||||
bool result = false;
|
||||
QStringList content = mSettings.values(QString("content"));
|
||||
for (int i = 0; i < content.count(); ++i) {
|
||||
if (content.at(i).contains(".omwgame") || content.at(i).contains(".esm")) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -8,55 +8,72 @@
|
|||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
namespace Files { typedef std::vector<boost::filesystem::path> PathContainer;
|
||||
struct ConfigurationManager;}
|
||||
|
||||
class GameSettings
|
||||
namespace Files
|
||||
{
|
||||
public:
|
||||
GameSettings(Files::ConfigurationManager &cfg);
|
||||
~GameSettings();
|
||||
typedef std::vector<boost::filesystem::path> PathContainer;
|
||||
struct ConfigurationManager;
|
||||
}
|
||||
|
||||
inline QString value(const QString &key, const QString &defaultValue = QString())
|
||||
namespace Launcher
|
||||
{
|
||||
class GameSettings
|
||||
{
|
||||
return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key);
|
||||
}
|
||||
public:
|
||||
GameSettings(Files::ConfigurationManager &cfg);
|
||||
~GameSettings();
|
||||
|
||||
inline QString value(const QString &key, const QString &defaultValue = QString())
|
||||
{
|
||||
return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key);
|
||||
}
|
||||
|
||||
|
||||
inline void setValue(const QString &key, const QString &value)
|
||||
{
|
||||
mSettings.insert(key, value);
|
||||
}
|
||||
inline void setValue(const QString &key, const QString &value)
|
||||
{
|
||||
mSettings.insert(key, value);
|
||||
mUserSettings.insert(key, value);
|
||||
}
|
||||
|
||||
inline void setMultiValue(const QString &key, const QString &value)
|
||||
{
|
||||
QStringList values = mSettings.values(key);
|
||||
if (!values.contains(value))
|
||||
mSettings.insertMulti(key, value);
|
||||
}
|
||||
inline void setMultiValue(const QString &key, const QString &value)
|
||||
{
|
||||
QStringList values = mSettings.values(key);
|
||||
if (!values.contains(value))
|
||||
mSettings.insertMulti(key, value);
|
||||
|
||||
inline void remove(const QString &key)
|
||||
{
|
||||
mSettings.remove(key);
|
||||
}
|
||||
values = mUserSettings.values(key);
|
||||
if (!values.contains(value))
|
||||
mUserSettings.insertMulti(key, value);
|
||||
}
|
||||
|
||||
inline QStringList getDataDirs() { return mDataDirs; }
|
||||
inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); }
|
||||
inline QString getDataLocal() {return mDataLocal; }
|
||||
inline bool hasMaster() { return mSettings.count(QString("master")) > 0; }
|
||||
inline void remove(const QString &key)
|
||||
{
|
||||
mSettings.remove(key);
|
||||
mUserSettings.remove(key);
|
||||
}
|
||||
|
||||
QStringList values(const QString &key, const QStringList &defaultValues = QStringList());
|
||||
bool readFile(QTextStream &stream);
|
||||
bool writeFile(QTextStream &stream);
|
||||
inline QStringList getDataDirs() { return mDataDirs; }
|
||||
inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); }
|
||||
inline QString getDataLocal() {return mDataLocal; }
|
||||
|
||||
private:
|
||||
Files::ConfigurationManager &mCfgMgr;
|
||||
bool hasMaster();
|
||||
|
||||
void validatePaths();
|
||||
QMap<QString, QString> mSettings;
|
||||
QStringList values(const QString &key, const QStringList &defaultValues = QStringList());
|
||||
|
||||
QStringList mDataDirs;
|
||||
QString mDataLocal;
|
||||
};
|
||||
bool readFile(QTextStream &stream);
|
||||
bool readFile(QTextStream &stream, QMap<QString, QString> &settings);
|
||||
bool readUserFile(QTextStream &stream);
|
||||
|
||||
bool writeFile(QTextStream &stream);
|
||||
|
||||
private:
|
||||
Files::ConfigurationManager &mCfgMgr;
|
||||
|
||||
void validatePaths();
|
||||
QMap<QString, QString> mSettings;
|
||||
QMap<QString, QString> mUserSettings;
|
||||
|
||||
QStringList mDataDirs;
|
||||
QString mDataLocal;
|
||||
};
|
||||
}
|
||||
#endif // GAMESETTINGS_HPP
|
||||
|
|
|
@ -5,15 +5,15 @@
|
|||
#include <QRegExp>
|
||||
#include <QMap>
|
||||
|
||||
GraphicsSettings::GraphicsSettings()
|
||||
Launcher::GraphicsSettings::GraphicsSettings()
|
||||
{
|
||||
}
|
||||
|
||||
GraphicsSettings::~GraphicsSettings()
|
||||
Launcher::GraphicsSettings::~GraphicsSettings()
|
||||
{
|
||||
}
|
||||
|
||||
bool GraphicsSettings::writeFile(QTextStream &stream)
|
||||
bool Launcher::GraphicsSettings::writeFile(QTextStream &stream)
|
||||
{
|
||||
QString sectionPrefix;
|
||||
QRegExp sectionRe("([^/]+)/(.+)$");
|
||||
|
|
|
@ -3,14 +3,16 @@
|
|||
|
||||
#include "settingsbase.hpp"
|
||||
|
||||
class GraphicsSettings : public SettingsBase<QMap<QString, QString> >
|
||||
namespace Launcher
|
||||
{
|
||||
public:
|
||||
GraphicsSettings();
|
||||
~GraphicsSettings();
|
||||
class GraphicsSettings : public SettingsBase<QMap<QString, QString> >
|
||||
{
|
||||
public:
|
||||
GraphicsSettings();
|
||||
~GraphicsSettings();
|
||||
|
||||
bool writeFile(QTextStream &stream);
|
||||
|
||||
};
|
||||
bool writeFile(QTextStream &stream);
|
||||
|
||||
};
|
||||
}
|
||||
#endif // GRAPHICSSETTINGS_HPP
|
||||
|
|
|
@ -5,15 +5,17 @@
|
|||
#include <QRegExp>
|
||||
#include <QMap>
|
||||
|
||||
LauncherSettings::LauncherSettings()
|
||||
#include <QDebug>
|
||||
|
||||
Launcher::LauncherSettings::LauncherSettings()
|
||||
{
|
||||
}
|
||||
|
||||
LauncherSettings::~LauncherSettings()
|
||||
Launcher::LauncherSettings::~LauncherSettings()
|
||||
{
|
||||
}
|
||||
|
||||
QStringList LauncherSettings::values(const QString &key, Qt::MatchFlags flags)
|
||||
QStringList Launcher::LauncherSettings::values(const QString &key, Qt::MatchFlags flags)
|
||||
{
|
||||
QMap<QString, QString> settings = SettingsBase::getSettings();
|
||||
|
||||
|
@ -34,7 +36,7 @@ QStringList LauncherSettings::values(const QString &key, Qt::MatchFlags flags)
|
|||
return result;
|
||||
}
|
||||
|
||||
QStringList LauncherSettings::subKeys(const QString &key)
|
||||
QStringList Launcher::LauncherSettings::subKeys(const QString &key)
|
||||
{
|
||||
QMap<QString, QString> settings = SettingsBase::getSettings();
|
||||
QStringList keys = settings.uniqueKeys();
|
||||
|
@ -44,12 +46,9 @@ QStringList LauncherSettings::subKeys(const QString &key)
|
|||
QStringList result;
|
||||
|
||||
foreach (const QString ¤tKey, keys) {
|
||||
|
||||
if (keyRe.indexIn(currentKey) != -1) {
|
||||
|
||||
QString prefixedKey = keyRe.cap(1);
|
||||
if(prefixedKey.startsWith(key)) {
|
||||
|
||||
QString subKey = prefixedKey.remove(key);
|
||||
if (!subKey.isEmpty())
|
||||
result.append(subKey);
|
||||
|
@ -61,7 +60,7 @@ QStringList LauncherSettings::subKeys(const QString &key)
|
|||
return result;
|
||||
}
|
||||
|
||||
bool LauncherSettings::writeFile(QTextStream &stream)
|
||||
bool Launcher::LauncherSettings::writeFile(QTextStream &stream)
|
||||
{
|
||||
QString sectionPrefix;
|
||||
QRegExp sectionRe("([^/]+)/(.+)$");
|
||||
|
|
|
@ -3,17 +3,19 @@
|
|||
|
||||
#include "settingsbase.hpp"
|
||||
|
||||
class LauncherSettings : public SettingsBase<QMap<QString, QString> >
|
||||
namespace Launcher
|
||||
{
|
||||
public:
|
||||
LauncherSettings();
|
||||
~LauncherSettings();
|
||||
class LauncherSettings : public SettingsBase<QMap<QString, QString> >
|
||||
{
|
||||
public:
|
||||
LauncherSettings();
|
||||
~LauncherSettings();
|
||||
|
||||
QStringList subKeys(const QString &key);
|
||||
QStringList values(const QString &key, Qt::MatchFlags flags = Qt::MatchExactly);
|
||||
QStringList subKeys(const QString &key);
|
||||
QStringList values(const QString &key, Qt::MatchFlags flags = Qt::MatchExactly);
|
||||
|
||||
bool writeFile(QTextStream &stream);
|
||||
|
||||
};
|
||||
bool writeFile(QTextStream &stream);
|
||||
|
||||
};
|
||||
}
|
||||
#endif // LAUNCHERSETTINGS_HPP
|
||||
|
|
|
@ -7,103 +7,105 @@
|
|||
#include <QRegExp>
|
||||
#include <QMap>
|
||||
|
||||
template <class Map>
|
||||
class SettingsBase
|
||||
namespace Launcher
|
||||
{
|
||||
|
||||
public:
|
||||
SettingsBase() { mMultiValue = false; }
|
||||
~SettingsBase() {}
|
||||
|
||||
inline QString value(const QString &key, const QString &defaultValue = QString())
|
||||
template <class Map>
|
||||
class SettingsBase
|
||||
{
|
||||
return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key);
|
||||
}
|
||||
|
||||
inline void setValue(const QString &key, const QString &value)
|
||||
{
|
||||
QStringList values = mSettings.values(key);
|
||||
if (!values.contains(value))
|
||||
mSettings.insert(key, value);
|
||||
}
|
||||
public:
|
||||
SettingsBase() { mMultiValue = false; }
|
||||
~SettingsBase() {}
|
||||
|
||||
inline void setMultiValue(const QString &key, const QString &value)
|
||||
{
|
||||
QStringList values = mSettings.values(key);
|
||||
if (!values.contains(value))
|
||||
mSettings.insertMulti(key, value);
|
||||
}
|
||||
inline QString value(const QString &key, const QString &defaultValue = QString())
|
||||
{
|
||||
return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key);
|
||||
}
|
||||
|
||||
inline void setMultiValueEnabled(bool enable)
|
||||
{
|
||||
mMultiValue = enable;
|
||||
}
|
||||
inline void setValue(const QString &key, const QString &value)
|
||||
{
|
||||
QStringList values = mSettings.values(key);
|
||||
if (!values.contains(value))
|
||||
mSettings.insert(key, value);
|
||||
}
|
||||
|
||||
inline void remove(const QString &key)
|
||||
{
|
||||
mSettings.remove(key);
|
||||
}
|
||||
inline void setMultiValue(const QString &key, const QString &value)
|
||||
{
|
||||
QStringList values = mSettings.values(key);
|
||||
if (!values.contains(value))
|
||||
mSettings.insertMulti(key, value);
|
||||
}
|
||||
|
||||
Map getSettings() {return mSettings;}
|
||||
inline void setMultiValueEnabled(bool enable)
|
||||
{
|
||||
mMultiValue = enable;
|
||||
}
|
||||
|
||||
bool readFile(QTextStream &stream)
|
||||
{
|
||||
mCache.clear();
|
||||
inline void remove(const QString &key)
|
||||
{
|
||||
mSettings.remove(key);
|
||||
}
|
||||
|
||||
QString sectionPrefix;
|
||||
Map getSettings() {return mSettings;}
|
||||
|
||||
QRegExp sectionRe("^\\[([^]]+)\\]");
|
||||
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
|
||||
bool readFile(QTextStream &stream)
|
||||
{
|
||||
mCache.clear();
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
QString line = stream.readLine();
|
||||
QString sectionPrefix;
|
||||
|
||||
if (line.isEmpty() || line.startsWith("#"))
|
||||
continue;
|
||||
QRegExp sectionRe("^\\[([^]]+)\\]");
|
||||
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
|
||||
|
||||
if (sectionRe.exactMatch(line)) {
|
||||
sectionPrefix = sectionRe.cap(1);
|
||||
sectionPrefix.append("/");
|
||||
continue;
|
||||
}
|
||||
while (!stream.atEnd()) {
|
||||
QString line = stream.readLine();
|
||||
|
||||
if (keyRe.indexIn(line) != -1) {
|
||||
if (line.isEmpty() || line.startsWith("#"))
|
||||
continue;
|
||||
|
||||
QString key = keyRe.cap(1).trimmed();
|
||||
QString value = keyRe.cap(2).trimmed();
|
||||
if (sectionRe.exactMatch(line)) {
|
||||
sectionPrefix = sectionRe.cap(1);
|
||||
sectionPrefix.append("/");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!sectionPrefix.isEmpty())
|
||||
key.prepend(sectionPrefix);
|
||||
if (keyRe.indexIn(line) != -1) {
|
||||
|
||||
mSettings.remove(key);
|
||||
QString key = keyRe.cap(1).trimmed();
|
||||
QString value = keyRe.cap(2).trimmed();
|
||||
|
||||
QStringList values = mCache.values(key);
|
||||
if (!sectionPrefix.isEmpty())
|
||||
key.prepend(sectionPrefix);
|
||||
|
||||
if (!values.contains(value)) {
|
||||
if (mMultiValue) {
|
||||
mCache.insertMulti(key, value);
|
||||
} else {
|
||||
mCache.insert(key, value);
|
||||
mSettings.remove(key);
|
||||
|
||||
QStringList values = mCache.values(key);
|
||||
|
||||
if (!values.contains(value)) {
|
||||
if (mMultiValue) {
|
||||
mCache.insertMulti(key, value);
|
||||
} else {
|
||||
mCache.insert(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mSettings.isEmpty()) {
|
||||
mSettings = mCache; // This is the first time we read a file
|
||||
if (mSettings.isEmpty()) {
|
||||
mSettings = mCache; // This is the first time we read a file
|
||||
return true;
|
||||
}
|
||||
|
||||
// Merge the changed keys with those which didn't
|
||||
mSettings.unite(mCache);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Merge the changed keys with those which didn't
|
||||
mSettings.unite(mCache);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Map mSettings;
|
||||
Map mCache;
|
||||
|
||||
bool mMultiValue;
|
||||
};
|
||||
private:
|
||||
Map mSettings;
|
||||
Map mCache;
|
||||
|
||||
bool mMultiValue;
|
||||
};
|
||||
}
|
||||
#endif // SETTINGSBASE_HPP
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "textslotmsgbox.hpp"
|
||||
|
||||
void TextSlotMsgBox::setTextSlot(const QString& string)
|
||||
void Launcher::TextSlotMsgBox::setTextSlot(const QString& string)
|
||||
{
|
||||
setText(string);
|
||||
}
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
|
||||
#include <QMessageBox>
|
||||
|
||||
class TextSlotMsgBox : public QMessageBox
|
||||
namespace Launcher
|
||||
{
|
||||
Q_OBJECT
|
||||
public slots:
|
||||
void setTextSlot(const QString& string);
|
||||
};
|
||||
|
||||
class TextSlotMsgBox : public QMessageBox
|
||||
{
|
||||
Q_OBJECT
|
||||
public slots:
|
||||
void setTextSlot(const QString& string);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -235,7 +235,7 @@ namespace
|
|||
{
|
||||
for ( bfs::recursive_directory_iterator end, dir(in); dir != end; ++dir )
|
||||
{
|
||||
if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename)
|
||||
if(Misc::StringUtils::ciEqual(dir->path().filename().string(), filename))
|
||||
return dir->path();
|
||||
}
|
||||
}
|
||||
|
@ -243,7 +243,7 @@ namespace
|
|||
{
|
||||
for ( bfs::directory_iterator end, dir(in); dir != end; ++dir )
|
||||
{
|
||||
if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename)
|
||||
if(Misc::StringUtils::ciEqual(dir->path().filename().string(), filename))
|
||||
return dir->path();
|
||||
}
|
||||
}
|
||||
|
@ -255,7 +255,7 @@ namespace
|
|||
{
|
||||
for(bfs::directory_iterator end, dir(in); dir != end; ++dir)
|
||||
{
|
||||
if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename)
|
||||
if(Misc::StringUtils::ciEqual(dir->path().filename().string(), filename))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -292,30 +292,30 @@ namespace
|
|||
|
||||
}
|
||||
|
||||
bool UnshieldThread::SetMorrowindPath(const std::string& path)
|
||||
bool Launcher::UnshieldThread::SetMorrowindPath(const std::string& path)
|
||||
{
|
||||
mMorrowindPath = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnshieldThread::SetTribunalPath(const std::string& path)
|
||||
bool Launcher::UnshieldThread::SetTribunalPath(const std::string& path)
|
||||
{
|
||||
mTribunalPath = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnshieldThread::SetBloodmoonPath(const std::string& path)
|
||||
bool Launcher::UnshieldThread::SetBloodmoonPath(const std::string& path)
|
||||
{
|
||||
mBloodmoonPath = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnshieldThread::SetOutputPath(const std::string& path)
|
||||
void Launcher::UnshieldThread::SetOutputPath(const std::string& path)
|
||||
{
|
||||
mOutputPath = path;
|
||||
}
|
||||
|
||||
bool UnshieldThread::extract_file(Unshield* unshield, bfs::path output_dir, const char* prefix, int index)
|
||||
bool Launcher::UnshieldThread::extract_file(Unshield* unshield, bfs::path output_dir, const char* prefix, int index)
|
||||
{
|
||||
bool success;
|
||||
bfs::path dirname;
|
||||
|
@ -349,7 +349,7 @@ bool UnshieldThread::extract_file(Unshield* unshield, bfs::path output_dir, cons
|
|||
return success;
|
||||
}
|
||||
|
||||
void UnshieldThread::extract_cab(const bfs::path& cab, const bfs::path& output_dir, bool extract_ini)
|
||||
void Launcher::UnshieldThread::extract_cab(const bfs::path& cab, const bfs::path& output_dir, bool extract_ini)
|
||||
{
|
||||
Unshield * unshield;
|
||||
unshield = unshield_open(cab.c_str());
|
||||
|
@ -369,7 +369,7 @@ void UnshieldThread::extract_cab(const bfs::path& cab, const bfs::path& output_d
|
|||
}
|
||||
|
||||
|
||||
bool UnshieldThread::extract()
|
||||
bool Launcher::UnshieldThread::extract()
|
||||
{
|
||||
bfs::path outputDataFilesDir = mOutputPath;
|
||||
outputDataFilesDir /= "Data Files";
|
||||
|
@ -475,7 +475,7 @@ bool UnshieldThread::extract()
|
|||
return true;
|
||||
}
|
||||
|
||||
void UnshieldThread::Done()
|
||||
void Launcher::UnshieldThread::Done()
|
||||
{
|
||||
// Get rid of unnecessary files
|
||||
bfs::remove_all(mOutputPath / "extract-temp");
|
||||
|
@ -491,28 +491,28 @@ void UnshieldThread::Done()
|
|||
bfs::last_write_time(findFile(mOutputPath, "bloodmoon.esm"), getTime("3 June 2003"));
|
||||
}
|
||||
|
||||
std::string UnshieldThread::GetMWEsmPath()
|
||||
std::string Launcher::UnshieldThread::GetMWEsmPath()
|
||||
{
|
||||
return findFile(mOutputPath / "Data Files", "morrowind.esm").string();
|
||||
}
|
||||
|
||||
bool UnshieldThread::TribunalDone()
|
||||
bool Launcher::UnshieldThread::TribunalDone()
|
||||
{
|
||||
return mTribunalDone;
|
||||
}
|
||||
|
||||
bool UnshieldThread::BloodmoonDone()
|
||||
bool Launcher::UnshieldThread::BloodmoonDone()
|
||||
{
|
||||
return mBloodmoonDone;
|
||||
}
|
||||
|
||||
void UnshieldThread::run()
|
||||
void Launcher::UnshieldThread::run()
|
||||
{
|
||||
extract();
|
||||
emit close();
|
||||
}
|
||||
|
||||
UnshieldThread::UnshieldThread()
|
||||
Launcher::UnshieldThread::UnshieldThread()
|
||||
{
|
||||
unshield_set_log_level(0);
|
||||
mMorrowindDone = false;
|
||||
|
|
|
@ -7,50 +7,52 @@
|
|||
|
||||
#include <libunshield.h>
|
||||
|
||||
class UnshieldThread : public QThread
|
||||
namespace Launcher
|
||||
{
|
||||
Q_OBJECT
|
||||
class UnshieldThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
bool SetMorrowindPath(const std::string& path);
|
||||
bool SetTribunalPath(const std::string& path);
|
||||
bool SetBloodmoonPath(const std::string& path);
|
||||
public:
|
||||
bool SetMorrowindPath(const std::string& path);
|
||||
bool SetTribunalPath(const std::string& path);
|
||||
bool SetBloodmoonPath(const std::string& path);
|
||||
|
||||
void SetOutputPath(const std::string& path);
|
||||
|
||||
bool extract();
|
||||
void SetOutputPath(const std::string& path);
|
||||
|
||||
bool TribunalDone();
|
||||
bool BloodmoonDone();
|
||||
bool extract();
|
||||
|
||||
void Done();
|
||||
bool TribunalDone();
|
||||
bool BloodmoonDone();
|
||||
|
||||
std::string GetMWEsmPath();
|
||||
void Done();
|
||||
|
||||
UnshieldThread();
|
||||
std::string GetMWEsmPath();
|
||||
|
||||
private:
|
||||
UnshieldThread();
|
||||
|
||||
void extract_cab(const boost::filesystem::path& cab, const boost::filesystem::path& output_dir, bool extract_ini = false);
|
||||
bool extract_file(Unshield* unshield, boost::filesystem::path output_dir, const char* prefix, int index);
|
||||
|
||||
boost::filesystem::path mMorrowindPath;
|
||||
boost::filesystem::path mTribunalPath;
|
||||
boost::filesystem::path mBloodmoonPath;
|
||||
private:
|
||||
|
||||
bool mMorrowindDone;
|
||||
bool mTribunalDone;
|
||||
bool mBloodmoonDone;
|
||||
void extract_cab(const boost::filesystem::path& cab, const boost::filesystem::path& output_dir, bool extract_ini = false);
|
||||
bool extract_file(Unshield* unshield, boost::filesystem::path output_dir, const char* prefix, int index);
|
||||
|
||||
boost::filesystem::path mOutputPath;
|
||||
boost::filesystem::path mMorrowindPath;
|
||||
boost::filesystem::path mTribunalPath;
|
||||
boost::filesystem::path mBloodmoonPath;
|
||||
|
||||
bool mMorrowindDone;
|
||||
bool mTribunalDone;
|
||||
bool mBloodmoonDone;
|
||||
|
||||
boost::filesystem::path mOutputPath;
|
||||
|
||||
|
||||
protected:
|
||||
virtual void run();
|
||||
|
||||
signals:
|
||||
void signalGUI(QString);
|
||||
void close();
|
||||
};
|
||||
protected:
|
||||
virtual void run();
|
||||
|
||||
signals:
|
||||
void signalGUI(QString);
|
||||
void close();
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -54,72 +54,61 @@
|
|||
Emulates the QMessageBox API with
|
||||
static conveniences. The message label can open external URLs.
|
||||
*/
|
||||
|
||||
class CheckableMessageBoxPrivate
|
||||
{
|
||||
public:
|
||||
CheckableMessageBoxPrivate(QDialog *q)
|
||||
Launcher::CheckableMessageBoxPrivate::CheckableMessageBoxPrivate(QDialog *q)
|
||||
: clickedButton(0)
|
||||
{
|
||||
QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
|
||||
{
|
||||
QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
|
||||
|
||||
pixmapLabel = new QLabel(q);
|
||||
sizePolicy.setHorizontalStretch(0);
|
||||
sizePolicy.setVerticalStretch(0);
|
||||
sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth());
|
||||
pixmapLabel->setSizePolicy(sizePolicy);
|
||||
pixmapLabel->setVisible(false);
|
||||
pixmapLabel = new QLabel(q);
|
||||
sizePolicy.setHorizontalStretch(0);
|
||||
sizePolicy.setVerticalStretch(0);
|
||||
sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth());
|
||||
pixmapLabel->setSizePolicy(sizePolicy);
|
||||
pixmapLabel->setVisible(false);
|
||||
|
||||
QSpacerItem *pixmapSpacer =
|
||||
new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
|
||||
QSpacerItem *pixmapSpacer =
|
||||
new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
|
||||
|
||||
messageLabel = new QLabel(q);
|
||||
messageLabel->setMinimumSize(QSize(300, 0));
|
||||
messageLabel->setWordWrap(true);
|
||||
messageLabel->setOpenExternalLinks(true);
|
||||
messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse);
|
||||
messageLabel = new QLabel(q);
|
||||
messageLabel->setMinimumSize(QSize(300, 0));
|
||||
messageLabel->setWordWrap(true);
|
||||
messageLabel->setOpenExternalLinks(true);
|
||||
messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse);
|
||||
|
||||
QSpacerItem *checkBoxRightSpacer =
|
||||
new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
QSpacerItem *buttonSpacer =
|
||||
new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
QSpacerItem *checkBoxRightSpacer =
|
||||
new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
QSpacerItem *buttonSpacer =
|
||||
new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
|
||||
checkBox = new QCheckBox(q);
|
||||
checkBox->setText(CheckableMessageBox::tr("Do not ask again"));
|
||||
checkBox = new QCheckBox(q);
|
||||
checkBox->setText(Launcher::CheckableMessageBox::tr("Do not ask again"));
|
||||
|
||||
buttonBox = new QDialogButtonBox(q);
|
||||
buttonBox->setOrientation(Qt::Horizontal);
|
||||
buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
|
||||
buttonBox = new QDialogButtonBox(q);
|
||||
buttonBox->setOrientation(Qt::Horizontal);
|
||||
buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
|
||||
|
||||
QVBoxLayout *verticalLayout = new QVBoxLayout();
|
||||
verticalLayout->addWidget(pixmapLabel);
|
||||
verticalLayout->addItem(pixmapSpacer);
|
||||
QVBoxLayout *verticalLayout = new QVBoxLayout();
|
||||
verticalLayout->addWidget(pixmapLabel);
|
||||
verticalLayout->addItem(pixmapSpacer);
|
||||
|
||||
QHBoxLayout *horizontalLayout_2 = new QHBoxLayout();
|
||||
horizontalLayout_2->addLayout(verticalLayout);
|
||||
horizontalLayout_2->addWidget(messageLabel);
|
||||
QHBoxLayout *horizontalLayout_2 = new QHBoxLayout();
|
||||
horizontalLayout_2->addLayout(verticalLayout);
|
||||
horizontalLayout_2->addWidget(messageLabel);
|
||||
|
||||
QHBoxLayout *horizontalLayout = new QHBoxLayout();
|
||||
horizontalLayout->addWidget(checkBox);
|
||||
horizontalLayout->addItem(checkBoxRightSpacer);
|
||||
QHBoxLayout *horizontalLayout = new QHBoxLayout();
|
||||
horizontalLayout->addWidget(checkBox);
|
||||
horizontalLayout->addItem(checkBoxRightSpacer);
|
||||
|
||||
QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q);
|
||||
verticalLayout_2->addLayout(horizontalLayout_2);
|
||||
verticalLayout_2->addLayout(horizontalLayout);
|
||||
verticalLayout_2->addItem(buttonSpacer);
|
||||
verticalLayout_2->addWidget(buttonBox);
|
||||
}
|
||||
QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q);
|
||||
verticalLayout_2->addLayout(horizontalLayout_2);
|
||||
verticalLayout_2->addLayout(horizontalLayout);
|
||||
verticalLayout_2->addItem(buttonSpacer);
|
||||
verticalLayout_2->addWidget(buttonBox);
|
||||
}
|
||||
|
||||
QLabel *pixmapLabel;
|
||||
QLabel *messageLabel;
|
||||
QCheckBox *checkBox;
|
||||
QDialogButtonBox *buttonBox;
|
||||
QAbstractButton *clickedButton;
|
||||
};
|
||||
|
||||
CheckableMessageBox::CheckableMessageBox(QWidget *parent) :
|
||||
Launcher::CheckableMessageBox::CheckableMessageBox(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
d(new CheckableMessageBoxPrivate(this))
|
||||
d(new Launcher::CheckableMessageBoxPrivate(this))
|
||||
{
|
||||
setModal(true);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
@ -129,102 +118,102 @@ CheckableMessageBox::CheckableMessageBox(QWidget *parent) :
|
|||
SLOT(slotClicked(QAbstractButton*)));
|
||||
}
|
||||
|
||||
CheckableMessageBox::~CheckableMessageBox()
|
||||
Launcher::CheckableMessageBox::~CheckableMessageBox()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void CheckableMessageBox::slotClicked(QAbstractButton *b)
|
||||
void Launcher::CheckableMessageBox::slotClicked(QAbstractButton *b)
|
||||
{
|
||||
d->clickedButton = b;
|
||||
}
|
||||
|
||||
QAbstractButton *CheckableMessageBox::clickedButton() const
|
||||
QAbstractButton *Launcher::CheckableMessageBox::clickedButton() const
|
||||
{
|
||||
return d->clickedButton;
|
||||
}
|
||||
|
||||
QDialogButtonBox::StandardButton CheckableMessageBox::clickedStandardButton() const
|
||||
QDialogButtonBox::StandardButton Launcher::CheckableMessageBox::clickedStandardButton() const
|
||||
{
|
||||
if (d->clickedButton)
|
||||
return d->buttonBox->standardButton(d->clickedButton);
|
||||
return QDialogButtonBox::NoButton;
|
||||
}
|
||||
|
||||
QString CheckableMessageBox::text() const
|
||||
QString Launcher::CheckableMessageBox::text() const
|
||||
{
|
||||
return d->messageLabel->text();
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setText(const QString &t)
|
||||
void Launcher::CheckableMessageBox::setText(const QString &t)
|
||||
{
|
||||
d->messageLabel->setText(t);
|
||||
}
|
||||
|
||||
QPixmap CheckableMessageBox::iconPixmap() const
|
||||
QPixmap Launcher::CheckableMessageBox::iconPixmap() const
|
||||
{
|
||||
if (const QPixmap *p = d->pixmapLabel->pixmap())
|
||||
return QPixmap(*p);
|
||||
return QPixmap();
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setIconPixmap(const QPixmap &p)
|
||||
void Launcher::CheckableMessageBox::setIconPixmap(const QPixmap &p)
|
||||
{
|
||||
d->pixmapLabel->setPixmap(p);
|
||||
d->pixmapLabel->setVisible(!p.isNull());
|
||||
}
|
||||
|
||||
bool CheckableMessageBox::isChecked() const
|
||||
bool Launcher::CheckableMessageBox::isChecked() const
|
||||
{
|
||||
return d->checkBox->isChecked();
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setChecked(bool s)
|
||||
void Launcher::CheckableMessageBox::setChecked(bool s)
|
||||
{
|
||||
d->checkBox->setChecked(s);
|
||||
}
|
||||
|
||||
QString CheckableMessageBox::checkBoxText() const
|
||||
QString Launcher::CheckableMessageBox::checkBoxText() const
|
||||
{
|
||||
return d->checkBox->text();
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setCheckBoxText(const QString &t)
|
||||
void Launcher::CheckableMessageBox::setCheckBoxText(const QString &t)
|
||||
{
|
||||
d->checkBox->setText(t);
|
||||
}
|
||||
|
||||
bool CheckableMessageBox::isCheckBoxVisible() const
|
||||
bool Launcher::CheckableMessageBox::isCheckBoxVisible() const
|
||||
{
|
||||
return d->checkBox->isVisible();
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setCheckBoxVisible(bool v)
|
||||
void Launcher::CheckableMessageBox::setCheckBoxVisible(bool v)
|
||||
{
|
||||
d->checkBox->setVisible(v);
|
||||
}
|
||||
|
||||
QDialogButtonBox::StandardButtons CheckableMessageBox::standardButtons() const
|
||||
QDialogButtonBox::StandardButtons Launcher::CheckableMessageBox::standardButtons() const
|
||||
{
|
||||
return d->buttonBox->standardButtons();
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setStandardButtons(QDialogButtonBox::StandardButtons s)
|
||||
void Launcher::CheckableMessageBox::setStandardButtons(QDialogButtonBox::StandardButtons s)
|
||||
{
|
||||
d->buttonBox->setStandardButtons(s);
|
||||
}
|
||||
|
||||
QPushButton *CheckableMessageBox::button(QDialogButtonBox::StandardButton b) const
|
||||
QPushButton *Launcher::CheckableMessageBox::button(QDialogButtonBox::StandardButton b) const
|
||||
{
|
||||
return d->buttonBox->button(b);
|
||||
}
|
||||
|
||||
QPushButton *CheckableMessageBox::addButton(const QString &text, QDialogButtonBox::ButtonRole role)
|
||||
QPushButton *Launcher::CheckableMessageBox::addButton(const QString &text, QDialogButtonBox::ButtonRole role)
|
||||
{
|
||||
return d->buttonBox->addButton(text, role);
|
||||
}
|
||||
|
||||
QDialogButtonBox::StandardButton CheckableMessageBox::defaultButton() const
|
||||
QDialogButtonBox::StandardButton Launcher::CheckableMessageBox::defaultButton() const
|
||||
{
|
||||
foreach (QAbstractButton *b, d->buttonBox->buttons())
|
||||
if (QPushButton *pb = qobject_cast<QPushButton *>(b))
|
||||
|
@ -233,7 +222,7 @@ QDialogButtonBox::StandardButton CheckableMessageBox::defaultButton() const
|
|||
return QDialogButtonBox::NoButton;
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s)
|
||||
void Launcher::CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s)
|
||||
{
|
||||
if (QPushButton *b = d->buttonBox->button(s)) {
|
||||
b->setDefault(true);
|
||||
|
@ -242,7 +231,7 @@ void CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s)
|
|||
}
|
||||
|
||||
QDialogButtonBox::StandardButton
|
||||
CheckableMessageBox::question(QWidget *parent,
|
||||
Launcher::CheckableMessageBox::question(QWidget *parent,
|
||||
const QString &title,
|
||||
const QString &question,
|
||||
const QString &checkBoxText,
|
||||
|
@ -263,7 +252,7 @@ CheckableMessageBox::question(QWidget *parent,
|
|||
return mb.clickedStandardButton();
|
||||
}
|
||||
|
||||
QMessageBox::StandardButton CheckableMessageBox::dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton db)
|
||||
QMessageBox::StandardButton Launcher::CheckableMessageBox::dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton db)
|
||||
{
|
||||
return static_cast<QMessageBox::StandardButton>(int(db));
|
||||
}
|
||||
|
|
|
@ -34,67 +34,83 @@
|
|||
#include <QMessageBox>
|
||||
#include <QDialog>
|
||||
|
||||
class CheckableMessageBoxPrivate;
|
||||
class QCheckBox;
|
||||
|
||||
class CheckableMessageBox : public QDialog
|
||||
namespace Launcher
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString text READ text WRITE setText)
|
||||
Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap)
|
||||
Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked)
|
||||
Q_PROPERTY(QString checkBoxText READ checkBoxText WRITE setCheckBoxText)
|
||||
Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons)
|
||||
Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton)
|
||||
class CheckableMessageBoxPrivate
|
||||
{
|
||||
public:
|
||||
|
||||
public:
|
||||
explicit CheckableMessageBox(QWidget *parent);
|
||||
virtual ~CheckableMessageBox();
|
||||
QLabel *pixmapLabel;
|
||||
QLabel *messageLabel;
|
||||
QCheckBox *checkBox;
|
||||
QDialogButtonBox *buttonBox;
|
||||
QAbstractButton *clickedButton;
|
||||
|
||||
static QDialogButtonBox::StandardButton
|
||||
question(QWidget *parent,
|
||||
const QString &title,
|
||||
const QString &question,
|
||||
const QString &checkBoxText,
|
||||
bool *checkBoxSetting,
|
||||
QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Yes|QDialogButtonBox::No,
|
||||
QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::No);
|
||||
public:
|
||||
CheckableMessageBoxPrivate(QDialog *q);
|
||||
};
|
||||
|
||||
QString text() const;
|
||||
void setText(const QString &);
|
||||
class CheckableMessageBox : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString text READ text WRITE setText)
|
||||
Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap)
|
||||
Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked)
|
||||
Q_PROPERTY(QString checkBoxText READ checkBoxText WRITE setCheckBoxText)
|
||||
Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons)
|
||||
Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton)
|
||||
|
||||
bool isChecked() const;
|
||||
void setChecked(bool s);
|
||||
public:
|
||||
explicit CheckableMessageBox(QWidget *parent);
|
||||
virtual ~CheckableMessageBox();
|
||||
|
||||
QString checkBoxText() const;
|
||||
void setCheckBoxText(const QString &);
|
||||
static QDialogButtonBox::StandardButton
|
||||
question(QWidget *parent,
|
||||
const QString &title,
|
||||
const QString &question,
|
||||
const QString &checkBoxText,
|
||||
bool *checkBoxSetting,
|
||||
QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Yes|QDialogButtonBox::No,
|
||||
QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::No);
|
||||
|
||||
bool isCheckBoxVisible() const;
|
||||
void setCheckBoxVisible(bool);
|
||||
QString text() const;
|
||||
void setText(const QString &);
|
||||
|
||||
QDialogButtonBox::StandardButtons standardButtons() const;
|
||||
void setStandardButtons(QDialogButtonBox::StandardButtons s);
|
||||
QPushButton *button(QDialogButtonBox::StandardButton b) const;
|
||||
QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role);
|
||||
bool isChecked() const;
|
||||
void setChecked(bool s);
|
||||
|
||||
QDialogButtonBox::StandardButton defaultButton() const;
|
||||
void setDefaultButton(QDialogButtonBox::StandardButton s);
|
||||
QString checkBoxText() const;
|
||||
void setCheckBoxText(const QString &);
|
||||
|
||||
// See static QMessageBox::standardPixmap()
|
||||
QPixmap iconPixmap() const;
|
||||
void setIconPixmap (const QPixmap &p);
|
||||
bool isCheckBoxVisible() const;
|
||||
void setCheckBoxVisible(bool);
|
||||
|
||||
// Query the result
|
||||
QAbstractButton *clickedButton() const;
|
||||
QDialogButtonBox::StandardButton clickedStandardButton() const;
|
||||
QDialogButtonBox::StandardButtons standardButtons() const;
|
||||
void setStandardButtons(QDialogButtonBox::StandardButtons s);
|
||||
QPushButton *button(QDialogButtonBox::StandardButton b) const;
|
||||
QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role);
|
||||
|
||||
// Conversion convenience
|
||||
static QMessageBox::StandardButton dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton);
|
||||
QDialogButtonBox::StandardButton defaultButton() const;
|
||||
void setDefaultButton(QDialogButtonBox::StandardButton s);
|
||||
|
||||
private slots:
|
||||
void slotClicked(QAbstractButton *b);
|
||||
// See static QMessageBox::standardPixmap()
|
||||
QPixmap iconPixmap() const;
|
||||
void setIconPixmap (const QPixmap &p);
|
||||
|
||||
private:
|
||||
CheckableMessageBoxPrivate *d;
|
||||
};
|
||||
// Query the result
|
||||
QAbstractButton *clickedButton() const;
|
||||
QDialogButtonBox::StandardButton clickedStandardButton() const;
|
||||
|
||||
// Conversion convenience
|
||||
static QMessageBox::StandardButton dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton);
|
||||
|
||||
private slots:
|
||||
void slotClicked(QAbstractButton *b);
|
||||
|
||||
private:
|
||||
CheckableMessageBoxPrivate *d;
|
||||
};
|
||||
}
|
||||
#endif // CHECKABLEMESSAGEBOX_HPP
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
#include <QToolButton>
|
||||
#include <QStyle>
|
||||
|
||||
#include "lineedit.hpp"
|
||||
|
||||
LineEdit::LineEdit(QWidget *parent)
|
||||
: QLineEdit(parent)
|
||||
{
|
||||
setupClearButton();
|
||||
}
|
||||
|
||||
void LineEdit::setupClearButton()
|
||||
{
|
||||
mClearButton = new QToolButton(this);
|
||||
QPixmap pixmap(":images/clear.png");
|
||||
|
@ -15,13 +17,6 @@ LineEdit::LineEdit(QWidget *parent)
|
|||
mClearButton->hide();
|
||||
connect(mClearButton, SIGNAL(clicked()), this, SLOT(clear()));
|
||||
connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateClearButton(const QString&)));
|
||||
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
|
||||
|
||||
setObjectName(QString("LineEdit"));
|
||||
setStyleSheet(QString("LineEdit { padding-right: %1px; } ").arg(mClearButton->sizeHint().width() + frameWidth + 1));
|
||||
QSize msz = minimumSizeHint();
|
||||
setMinimumSize(qMax(msz.width(), mClearButton->sizeHint().height() + frameWidth * 2 + 2),
|
||||
qMax(msz.height(), mClearButton->sizeHint().height() + frameWidth * 2 + 2));
|
||||
}
|
||||
|
||||
void LineEdit::resizeEvent(QResizeEvent *)
|
|
@ -11,6 +11,9 @@
|
|||
#define LINEEDIT_H
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QStyle>
|
||||
#include <QStylePainter>
|
||||
#include <QToolButton>
|
||||
|
||||
class QToolButton;
|
||||
|
||||
|
@ -18,6 +21,8 @@ class LineEdit : public QLineEdit
|
|||
{
|
||||
Q_OBJECT
|
||||
|
||||
QString mPlaceholderText;
|
||||
|
||||
public:
|
||||
LineEdit(QWidget *parent = 0);
|
||||
|
||||
|
@ -27,8 +32,10 @@ protected:
|
|||
private slots:
|
||||
void updateClearButton(const QString &text);
|
||||
|
||||
private:
|
||||
protected:
|
||||
QToolButton *mClearButton;
|
||||
|
||||
void setupClearButton();
|
||||
};
|
||||
|
||||
#endif // LIENEDIT_H
|
|
@ -5,18 +5,12 @@
|
|||
#include <QKeyEvent>
|
||||
|
||||
#include "profilescombobox.hpp"
|
||||
#include "comboboxlineedit.hpp"
|
||||
|
||||
ProfilesComboBox::ProfilesComboBox(QWidget *parent) :
|
||||
QComboBox(parent)
|
||||
ContentSelectorView::ComboBox(parent)
|
||||
{
|
||||
mValidator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore
|
||||
setEditEnabled(true);
|
||||
setValidator(mValidator);
|
||||
setCompleter(0);
|
||||
|
||||
connect(this, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(slotIndexChanged(int)));
|
||||
connect(this, SIGNAL(activated(int)), this,
|
||||
SLOT(slotIndexChangedByUser(int)));
|
||||
|
||||
setInsertPolicy(QComboBox::NoInsert);
|
||||
}
|
||||
|
@ -37,6 +31,7 @@ void ProfilesComboBox::setEditEnabled(bool editable)
|
|||
setValidator(mValidator);
|
||||
|
||||
ComboBoxLineEdit *edit = new ComboBoxLineEdit(this);
|
||||
|
||||
setLineEdit(edit);
|
||||
setCompleter(0);
|
||||
|
||||
|
@ -45,6 +40,9 @@ void ProfilesComboBox::setEditEnabled(bool editable)
|
|||
|
||||
connect(lineEdit(), SIGNAL(textChanged(QString)), this,
|
||||
SLOT(slotTextChanged(QString)));
|
||||
|
||||
connect (lineEdit(), SIGNAL(textChanged(QString)), this,
|
||||
SIGNAL (signalProfileTextChanged (QString)));
|
||||
}
|
||||
|
||||
void ProfilesComboBox::slotTextChanged(const QString &text)
|
||||
|
@ -82,11 +80,20 @@ void ProfilesComboBox::slotEditingFinished()
|
|||
emit(profileRenamed(previous, current));
|
||||
}
|
||||
|
||||
void ProfilesComboBox::slotIndexChanged(int index)
|
||||
void ProfilesComboBox::slotIndexChangedByUser(int index)
|
||||
{
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
emit(profileChanged(mOldProfile, currentText()));
|
||||
mOldProfile = itemText(index);
|
||||
emit (signalProfileChanged(mOldProfile, currentText()));
|
||||
mOldProfile = currentText();
|
||||
}
|
||||
|
||||
ProfilesComboBox::ComboBoxLineEdit::ComboBoxLineEdit (QWidget *parent)
|
||||
: LineEdit (parent)
|
||||
{
|
||||
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
|
||||
|
||||
setObjectName(QString("ComboBoxLineEdit"));
|
||||
setStyleSheet(QString("ComboBoxLineEdit { background-color: transparent; padding-right: %1px; } ").arg(mClearButton->sizeHint().width() + frameWidth + 1));
|
||||
}
|
47
apps/launcher/utils/profilescombobox.hpp
Normal file
47
apps/launcher/utils/profilescombobox.hpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#ifndef PROFILESCOMBOBOX_HPP
|
||||
#define PROFILESCOMBOBOX_HPP
|
||||
|
||||
#include "components/contentselector/view/combobox.hpp"
|
||||
#include "lineedit.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
class QString;
|
||||
|
||||
class ProfilesComboBox : public ContentSelectorView::ComboBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
class ComboBoxLineEdit : public LineEdit
|
||||
{
|
||||
public:
|
||||
explicit ComboBoxLineEdit (QWidget *parent = 0);
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
explicit ProfilesComboBox(QWidget *parent = 0);
|
||||
void setEditEnabled(bool editable);
|
||||
void setCurrentProfile(int index)
|
||||
{
|
||||
ComboBox::setCurrentIndex(index);
|
||||
mOldProfile = currentText();
|
||||
}
|
||||
|
||||
signals:
|
||||
void signalProfileTextChanged(const QString &item);
|
||||
void signalProfileChanged(const QString &previous, const QString ¤t);
|
||||
void signalProfileChanged(int index);
|
||||
void profileRenamed(const QString &oldName, const QString &newName);
|
||||
|
||||
private slots:
|
||||
|
||||
void slotEditingFinished();
|
||||
void slotIndexChangedByUser(int index);
|
||||
void slotTextChanged(const QString &text);
|
||||
|
||||
private:
|
||||
QString mOldProfile;
|
||||
};
|
||||
#endif // PROFILESCOMBOBOX_HPP
|
|
@ -7,19 +7,18 @@
|
|||
#include <QValidator>
|
||||
#include <QLabel>
|
||||
|
||||
#include <components/fileorderlist/utils/lineedit.hpp>
|
||||
|
||||
TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWidget *parent) :
|
||||
Launcher::TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWidget *parent) :
|
||||
QDialog(parent)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
mButtonBox = new QDialogButtonBox(this);
|
||||
mButtonBox->addButton(QDialogButtonBox::Ok);
|
||||
mButtonBox->addButton(QDialogButtonBox::Cancel);
|
||||
mButtonBox->button(QDialogButtonBox::Ok)->setEnabled (false);
|
||||
|
||||
// Line edit
|
||||
QValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore
|
||||
mLineEdit = new LineEdit(this);
|
||||
mLineEdit = new DialogLineEdit(this);
|
||||
mLineEdit->setValidator(validator);
|
||||
mLineEdit->setCompleter(0);
|
||||
|
||||
|
@ -38,34 +37,51 @@ TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWid
|
|||
Q_UNUSED(title);
|
||||
#endif
|
||||
|
||||
setOkButtonEnabled(false);
|
||||
setModal(true);
|
||||
|
||||
connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
connect(mLineEdit, SIGNAL(textChanged(QString)), this, SLOT(slotUpdateOkButton(QString)));
|
||||
|
||||
}
|
||||
|
||||
int TextInputDialog::exec()
|
||||
int Launcher::TextInputDialog::exec()
|
||||
{
|
||||
mLineEdit->clear();
|
||||
mLineEdit->setFocus();
|
||||
return QDialog::exec();
|
||||
}
|
||||
|
||||
void TextInputDialog::setOkButtonEnabled(bool enabled)
|
||||
QString Launcher::TextInputDialog::getText() const
|
||||
{
|
||||
QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok);
|
||||
okButton->setEnabled(enabled);
|
||||
return mLineEdit->text();
|
||||
}
|
||||
|
||||
QPalette *palette = new QPalette();
|
||||
palette->setColor(QPalette::Text,Qt::red);
|
||||
void Launcher::TextInputDialog::slotUpdateOkButton(QString text)
|
||||
{
|
||||
bool enabled = !(text.isEmpty());
|
||||
mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(enabled);
|
||||
|
||||
if (enabled) {
|
||||
if (enabled)
|
||||
mLineEdit->setPalette(QApplication::palette());
|
||||
} else {
|
||||
else
|
||||
{
|
||||
// Existing profile name, make the text red
|
||||
QPalette *palette = new QPalette();
|
||||
palette->setColor(QPalette::Text,Qt::red);
|
||||
mLineEdit->setPalette(*palette);
|
||||
}
|
||||
}
|
||||
|
||||
Launcher::TextInputDialog::DialogLineEdit::DialogLineEdit (QWidget *parent) :
|
||||
LineEdit (parent)
|
||||
{
|
||||
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
|
||||
|
||||
setObjectName(QString("LineEdit"));
|
||||
setStyleSheet(QString("LineEdit { padding-right: %1px; } ").arg(mClearButton->sizeHint().width() + frameWidth + 1));
|
||||
QSize msz = minimumSizeHint();
|
||||
setMinimumSize(qMax(msz.width(), mClearButton->sizeHint().height() + frameWidth * 2 + 2),
|
||||
qMax(msz.height(), mClearButton->sizeHint().height() + frameWidth * 2 + 2));
|
||||
|
||||
}
|
||||
|
|
|
@ -2,27 +2,39 @@
|
|||
#define TEXTINPUTDIALOG_HPP
|
||||
|
||||
#include <QDialog>
|
||||
//#include "lineedit.hpp"
|
||||
|
||||
#include "lineedit.hpp"
|
||||
|
||||
class QDialogButtonBox;
|
||||
class LineEdit;
|
||||
|
||||
class TextInputDialog : public QDialog
|
||||
namespace Launcher
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TextInputDialog(const QString& title, const QString &text, QWidget *parent = 0);
|
||||
inline LineEdit *lineEdit() { return mLineEdit; }
|
||||
void setOkButtonEnabled(bool enabled);
|
||||
class TextInputDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
LineEdit *mLineEdit;
|
||||
class DialogLineEdit : public LineEdit
|
||||
{
|
||||
public:
|
||||
explicit DialogLineEdit (QWidget *parent = 0);
|
||||
};
|
||||
|
||||
int exec();
|
||||
DialogLineEdit *mLineEdit;
|
||||
QDialogButtonBox *mButtonBox;
|
||||
|
||||
private:
|
||||
QDialogButtonBox *mButtonBox;
|
||||
public:
|
||||
|
||||
|
||||
};
|
||||
explicit TextInputDialog(const QString& title, const QString &text, QWidget *parent = 0);
|
||||
~TextInputDialog () {}
|
||||
|
||||
QString getText() const;
|
||||
|
||||
int exec();
|
||||
|
||||
private slots:
|
||||
void slotUpdateOkButton(QString text);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif // TEXTINPUTDIALOG_HPP
|
||||
|
|
|
@ -16,7 +16,7 @@ MwIniImporter::MwIniImporter()
|
|||
const char *map[][2] =
|
||||
{
|
||||
{ "fps", "General:Show FPS" },
|
||||
{ "nosound", "General:Disable Audio" },
|
||||
{ "no-sound", "General:Disable Audio" },
|
||||
{ 0, 0 }
|
||||
};
|
||||
const char *fallback[] = {
|
||||
|
@ -623,6 +623,17 @@ MwIniImporter::MwIniImporter()
|
|||
"Moons:Masser Fade Out Finish",
|
||||
"Moons:Script Color",
|
||||
|
||||
// blood
|
||||
"Blood:Model 0",
|
||||
"Blood:Model 1",
|
||||
"Blood:Model 2",
|
||||
"Blood:Texture 0",
|
||||
"Blood:Texture 1",
|
||||
"Blood:Texture 2",
|
||||
"Blood:Texture Name 0",
|
||||
"Blood:Texture Name 1",
|
||||
"Blood:Texture Name 2",
|
||||
|
||||
0
|
||||
};
|
||||
|
||||
|
@ -813,8 +824,7 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con
|
|||
}
|
||||
|
||||
void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) const {
|
||||
std::vector<std::string> esmFiles;
|
||||
std::vector<std::string> espFiles;
|
||||
std::vector<std::string> contentFiles;
|
||||
std::string baseGameFile("Game Files:GameFile");
|
||||
std::string gameFile("");
|
||||
|
||||
|
@ -832,29 +842,19 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) co
|
|||
std::string filetype(entry->substr(entry->length()-3));
|
||||
Misc::StringUtils::toLower(filetype);
|
||||
|
||||
if(filetype.compare("esm") == 0) {
|
||||
esmFiles.push_back(*entry);
|
||||
}
|
||||
else if(filetype.compare("esp") == 0) {
|
||||
espFiles.push_back(*entry);
|
||||
if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) {
|
||||
contentFiles.push_back(*entry);
|
||||
}
|
||||
}
|
||||
|
||||
gameFile = "";
|
||||
}
|
||||
|
||||
cfg.erase("master");
|
||||
cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("master", std::vector<std::string>() ) );
|
||||
cfg.erase("content");
|
||||
cfg.insert( std::make_pair("content", std::vector<std::string>() ) );
|
||||
|
||||
for(std::vector<std::string>::const_iterator it=esmFiles.begin(); it!=esmFiles.end(); ++it) {
|
||||
cfg["master"].push_back(*it);
|
||||
}
|
||||
|
||||
cfg.erase("plugin");
|
||||
cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("plugin", std::vector<std::string>() ) );
|
||||
|
||||
for(std::vector<std::string>::const_iterator it=espFiles.begin(); it!=espFiles.end(); ++it) {
|
||||
cfg["plugin"].push_back(*it);
|
||||
for(std::vector<std::string>::const_iterator it=contentFiles.begin(); it!=contentFiles.end(); ++it) {
|
||||
cfg["content"].push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@ opencs_units (. editor)
|
|||
set (CMAKE_BUILD_TYPE DEBUG)
|
||||
|
||||
opencs_units (model/doc
|
||||
document
|
||||
document operation saving
|
||||
)
|
||||
|
||||
opencs_units_noqt (model/doc
|
||||
documentmanager
|
||||
documentmanager stage savingstate savingstages
|
||||
)
|
||||
|
||||
opencs_hdrs_noqt (model/doc
|
||||
|
@ -24,27 +24,27 @@ opencs_units (model/world
|
|||
|
||||
opencs_units_noqt (model/world
|
||||
universalid record commands columnbase scriptcontext cell refidcollection
|
||||
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns
|
||||
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection
|
||||
)
|
||||
|
||||
opencs_hdrs_noqt (model/world
|
||||
columnimp idcollection collection
|
||||
columnimp idcollection collection info
|
||||
)
|
||||
|
||||
|
||||
opencs_units (model/tools
|
||||
tools operation reportmodel
|
||||
tools reportmodel
|
||||
)
|
||||
|
||||
opencs_units_noqt (model/tools
|
||||
stage verifier mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
|
||||
birthsigncheck spellcheck
|
||||
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
|
||||
birthsigncheck spellcheck referenceablecheck
|
||||
)
|
||||
|
||||
|
||||
opencs_units (view/doc
|
||||
viewmanager view operations operation subview startup filedialog newgame filewidget
|
||||
adjusterwidget
|
||||
viewmanager view operations operation subview startup filedialog newgame
|
||||
filewidget adjusterwidget
|
||||
)
|
||||
|
||||
|
||||
|
@ -60,13 +60,17 @@ opencs_hdrs_noqt (view/doc
|
|||
opencs_units (view/world
|
||||
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
|
||||
cellcreator referenceablecreator referencecreator scenesubview scenetoolbar scenetool
|
||||
scenetoolmode
|
||||
scenetoolmode infocreator
|
||||
)
|
||||
|
||||
opencs_units (view/render
|
||||
scenewidget
|
||||
)
|
||||
|
||||
opencs_units_noqt (view/world
|
||||
dialoguesubview subviews
|
||||
enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate
|
||||
scripthighlighter idvalidator
|
||||
scripthighlighter idvalidator dialoguecreator
|
||||
)
|
||||
|
||||
|
||||
|
@ -124,11 +128,13 @@ opencs_units (view/filter
|
|||
set (OPENCS_US
|
||||
)
|
||||
|
||||
set (OPENCS_RES ../../files/opencs/resources.qrc
|
||||
../../files/launcher/launcher.qrc
|
||||
set (OPENCS_RES ${CMAKE_SOURCE_DIR}/files/opencs/resources.qrc
|
||||
${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc
|
||||
)
|
||||
|
||||
set (OPENCS_UI ../../files/ui/datafilespage.ui
|
||||
set (OPENCS_UI
|
||||
${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
|
||||
${CMAKE_SOURCE_DIR}/files/ui/filedialog.ui
|
||||
)
|
||||
|
||||
source_group (opencs FILES ${OPENCS_SRC} ${OPENCS_HDR})
|
||||
|
@ -137,7 +143,7 @@ if(WIN32)
|
|||
set(QT_USE_QTMAIN TRUE)
|
||||
endif(WIN32)
|
||||
|
||||
find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork QtXml QtXmlPatterns REQUIRED)
|
||||
find_package(Qt4 COMPONENTS QtCore QtGui QtNetwork REQUIRED)
|
||||
include(${QT_USE_FILE})
|
||||
|
||||
qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI})
|
||||
|
@ -146,15 +152,46 @@ qt4_add_resources(OPENCS_RES_SRC ${OPENCS_RES})
|
|||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
if(APPLE)
|
||||
set (OPENCS_MAC_ICON ${CMAKE_SOURCE_DIR}/files/mac/opencs.icns)
|
||||
else()
|
||||
set (OPENCS_MAC_ICON "")
|
||||
endif(APPLE)
|
||||
|
||||
add_executable(opencs
|
||||
MACOSX_BUNDLE
|
||||
${OPENCS_SRC}
|
||||
${OPENCS_UI_HDR}
|
||||
${OPENCS_MOC_SRC}
|
||||
${OPENCS_RES_SRC}
|
||||
${OPENCS_MAC_ICON}
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
set_target_properties(opencs PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}"
|
||||
OUTPUT_NAME "OpenCS"
|
||||
MACOSX_BUNDLE_ICON_FILE "opencs.icns"
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "OpenCS"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING ${OPENMW_VERSION}
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION ${OPENMW_VERSION}
|
||||
)
|
||||
|
||||
set_source_files_properties(${OPENCS_MAC_ICON} PROPERTIES
|
||||
MACOSX_PACKAGE_LOCATION Resources)
|
||||
endif(APPLE)
|
||||
|
||||
target_link_libraries(opencs
|
||||
${Boost_LIBRARIES}
|
||||
${QT_LIBRARIES}
|
||||
components
|
||||
)
|
||||
|
||||
if(DPKG_PROGRAM)
|
||||
INSTALL(TARGETS opencs RUNTIME DESTINATION games COMPONENT opencs)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
INSTALL(TARGETS opencs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE)
|
||||
endif()
|
||||
|
|
|
@ -6,17 +6,21 @@
|
|||
#include <QLocalSocket>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <OgreRoot.h>
|
||||
#include <OgreRenderWindow.h>
|
||||
|
||||
#include "model/doc/document.hpp"
|
||||
#include "model/world/data.hpp"
|
||||
|
||||
|
||||
CS::Editor::Editor() : mViewManager (mDocumentManager)
|
||||
CS::Editor::Editor()
|
||||
: mDocumentManager (mCfgMgr), mViewManager (mDocumentManager)
|
||||
{
|
||||
mIpcServerName = "org.openmw.OpenCS";
|
||||
|
||||
setupDataFiles();
|
||||
|
||||
mNewGame.setLocalData (mLocal);
|
||||
mFileDialog.setLocalData (mLocal);
|
||||
|
||||
connect (&mViewManager, SIGNAL (newGameRequest ()), this, SLOT (createGame ()));
|
||||
connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ()));
|
||||
|
@ -28,23 +32,27 @@ CS::Editor::Editor() : mViewManager (mDocumentManager)
|
|||
connect (&mStartup, SIGNAL (loadDocument()), this, SLOT (loadDocument ()));
|
||||
connect (&mStartup, SIGNAL (editConfig()), this, SLOT (showSettings ()));
|
||||
|
||||
connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles()));
|
||||
connect (&mFileDialog, SIGNAL(createNewFile()), this, SLOT(createNewFile()));
|
||||
connect (&mFileDialog, SIGNAL(signalOpenFiles (const boost::filesystem::path&)),
|
||||
this, SLOT(openFiles (const boost::filesystem::path&)));
|
||||
|
||||
connect (&mFileDialog, SIGNAL(signalCreateNewFile (const boost::filesystem::path&)),
|
||||
this, SLOT(createNewFile (const boost::filesystem::path&)));
|
||||
|
||||
connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)),
|
||||
this, SLOT (createNewGame (const boost::filesystem::path&)));
|
||||
this, SLOT (createNewGame (const boost::filesystem::path&)));
|
||||
}
|
||||
|
||||
void CS::Editor::setupDataFiles()
|
||||
{
|
||||
boost::program_options::variables_map variables;
|
||||
boost::program_options::options_description desc;
|
||||
boost::program_options::options_description desc("Syntax: opencs <options>\nAllowed options");
|
||||
|
||||
desc.add_options()
|
||||
("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken())
|
||||
("data-local", boost::program_options::value<std::string>()->default_value(""))
|
||||
("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false))
|
||||
("encoding", boost::program_options::value<std::string>()->default_value("win1252"));
|
||||
("encoding", boost::program_options::value<std::string>()->default_value("win1252"))
|
||||
("resources", boost::program_options::value<std::string>()->default_value("resources"));
|
||||
|
||||
boost::program_options::notify(variables);
|
||||
|
||||
|
@ -78,14 +86,13 @@ void CS::Editor::setupDataFiles()
|
|||
return;
|
||||
}
|
||||
|
||||
// Set the charset for reading the esm/esp files
|
||||
QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
|
||||
mFileDialog.setEncoding(encoding);
|
||||
|
||||
dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end());
|
||||
|
||||
mDocumentManager.setResourceDir (variables["resources"].as<std::string>());
|
||||
|
||||
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
|
||||
{
|
||||
|
||||
QString path = QString::fromStdString(iter->string());
|
||||
mFileDialog.addFiles(path);
|
||||
}
|
||||
|
@ -109,48 +116,39 @@ void CS::Editor::createGame()
|
|||
void CS::Editor::createAddon()
|
||||
{
|
||||
mStartup.hide();
|
||||
|
||||
mFileDialog.newFile();
|
||||
mFileDialog.showDialog (CSVDoc::ContentAction_New);
|
||||
}
|
||||
|
||||
void CS::Editor::loadDocument()
|
||||
{
|
||||
mStartup.hide();
|
||||
|
||||
mFileDialog.openFile();
|
||||
mFileDialog.showDialog (CSVDoc::ContentAction_Edit);
|
||||
}
|
||||
|
||||
void CS::Editor::openFiles()
|
||||
void CS::Editor::openFiles (const boost::filesystem::path &savePath)
|
||||
{
|
||||
std::vector<boost::filesystem::path> files;
|
||||
QStringList paths = mFileDialog.checkedItemsPaths();
|
||||
|
||||
foreach (const QString &path, paths) {
|
||||
foreach (const QString &path, mFileDialog.selectedFilePaths())
|
||||
files.push_back(path.toStdString());
|
||||
}
|
||||
|
||||
/// \todo Get the save path from the file dialogue
|
||||
|
||||
CSMDoc::Document *document = mDocumentManager.addDocument (files, *files.rbegin(), false);
|
||||
CSMDoc::Document *document = mDocumentManager.addDocument (files, savePath, false);
|
||||
|
||||
mViewManager.addView (document);
|
||||
mFileDialog.hide();
|
||||
}
|
||||
|
||||
void CS::Editor::createNewFile()
|
||||
void CS::Editor::createNewFile (const boost::filesystem::path &savePath)
|
||||
{
|
||||
std::vector<boost::filesystem::path> files;
|
||||
QStringList paths = mFileDialog.checkedItemsPaths();
|
||||
|
||||
foreach (const QString &path, paths) {
|
||||
foreach (const QString &path, mFileDialog.selectedFilePaths()) {
|
||||
files.push_back(path.toStdString());
|
||||
}
|
||||
|
||||
files.push_back(mFileDialog.fileName().toStdString());
|
||||
files.push_back(mFileDialog.filename().toStdString());
|
||||
|
||||
/// \todo Get the save path from the file dialogue.
|
||||
|
||||
CSMDoc::Document *document = mDocumentManager.addDocument (files, *files.rbegin(), true);
|
||||
CSMDoc::Document *document = mDocumentManager.addDocument (files, savePath, true);
|
||||
|
||||
mViewManager.addView (document);
|
||||
mFileDialog.hide();
|
||||
|
@ -212,6 +210,20 @@ int CS::Editor::run()
|
|||
if (mLocal.empty())
|
||||
return 1;
|
||||
|
||||
// TODO: setting
|
||||
Ogre::Root::getSingleton().setRenderSystem(Ogre::Root::getSingleton().getRenderSystemByName("OpenGL Rendering Subsystem"));
|
||||
|
||||
Ogre::Root::getSingleton().initialise(false);
|
||||
|
||||
// Create a hidden background window to keep resources
|
||||
Ogre::NameValuePairList params;
|
||||
params.insert(std::make_pair("title", ""));
|
||||
params.insert(std::make_pair("FSAA", "0"));
|
||||
params.insert(std::make_pair("vsync", "false"));
|
||||
params.insert(std::make_pair("hidden", "true"));
|
||||
Ogre::RenderWindow* hiddenWindow = Ogre::Root::getSingleton().createRenderWindow("InactiveHidden", 1, 1, false, ¶ms);
|
||||
hiddenWindow->setActive(false);
|
||||
|
||||
mStartup.show();
|
||||
|
||||
QApplication::setQuitOnLastWindowClosed (true);
|
||||
|
|
|
@ -26,15 +26,15 @@ namespace CS
|
|||
{
|
||||
Q_OBJECT
|
||||
|
||||
Files::ConfigurationManager mCfgMgr;
|
||||
CSMSettings::UserSettings mUserSettings;
|
||||
CSMDoc::DocumentManager mDocumentManager;
|
||||
CSVDoc::ViewManager mViewManager;
|
||||
CSVDoc::StartupDialogue mStartup;
|
||||
CSVDoc::NewGameDialogue mNewGame;
|
||||
CSVSettings::UserSettingsDialog mSettings;
|
||||
FileDialog mFileDialog;
|
||||
CSVDoc::FileDialog mFileDialog;
|
||||
|
||||
Files::ConfigurationManager mCfgMgr;
|
||||
boost::filesystem::path mLocal;
|
||||
|
||||
void setupDataFiles();
|
||||
|
@ -59,8 +59,8 @@ namespace CS
|
|||
void createAddon();
|
||||
|
||||
void loadDocument();
|
||||
void openFiles();
|
||||
void createNewFile();
|
||||
void openFiles (const boost::filesystem::path &path);
|
||||
void createNewFile (const boost::filesystem::path& path);
|
||||
void createNewGame (const boost::filesystem::path& file);
|
||||
|
||||
void showStartup();
|
||||
|
|
|
@ -7,6 +7,12 @@
|
|||
#include <QApplication>
|
||||
#include <QIcon>
|
||||
|
||||
#include <components/ogreinit/ogreinit.hpp>
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include <QDir>
|
||||
#endif
|
||||
|
||||
class Application : public QApplication
|
||||
{
|
||||
private:
|
||||
|
@ -33,7 +39,32 @@ class Application : public QApplication
|
|||
int main(int argc, char *argv[])
|
||||
{
|
||||
Q_INIT_RESOURCE (resources);
|
||||
|
||||
// TODO: Ogre startup shouldn't be here, but it currently has to:
|
||||
// SceneWidget destructor will delete the created render window, which would be called _after_ Root has shut down :(
|
||||
|
||||
Application mApplication (argc, argv);
|
||||
OgreInit::OgreInit ogreInit;
|
||||
ogreInit.init("./opencsOgre.log"); // TODO log path?
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
QDir dir(QCoreApplication::applicationDirPath());
|
||||
if (dir.dirName() == "MacOS") {
|
||||
dir.cdUp();
|
||||
dir.cdUp();
|
||||
dir.cdUp();
|
||||
}
|
||||
QDir::setCurrent(dir.absolutePath());
|
||||
|
||||
// force Qt to load only LOCAL plugins, don't touch system Qt installation
|
||||
QDir pluginsPath(QCoreApplication::applicationDirPath());
|
||||
pluginsPath.cdUp();
|
||||
pluginsPath.cd("Plugins");
|
||||
|
||||
QStringList libraryPaths;
|
||||
libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath();
|
||||
mApplication.setLibraryPaths(libraryPaths);
|
||||
#endif
|
||||
|
||||
mApplication.setWindowIcon (QIcon (":./opencs.png"));
|
||||
|
||||
|
@ -42,7 +73,7 @@ int main(int argc, char *argv[])
|
|||
if(!editor.makeIPCServer())
|
||||
{
|
||||
editor.connectToIPCServer();
|
||||
return 0;
|
||||
// return 0;
|
||||
}
|
||||
|
||||
return editor.run();
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
#include "document.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#ifndef Q_MOC_RUN
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#endif
|
||||
|
||||
void CSMDoc::Document::load (const std::vector<boost::filesystem::path>::const_iterator& begin,
|
||||
const std::vector<boost::filesystem::path>::const_iterator& end, bool lastAsModified)
|
||||
const std::vector<boost::filesystem::path>::const_iterator& end, bool lastAsModified)
|
||||
{
|
||||
assert (begin!=end);
|
||||
|
||||
|
@ -12,10 +19,10 @@ void CSMDoc::Document::load (const std::vector<boost::filesystem::path>::const_i
|
|||
--end2;
|
||||
|
||||
for (std::vector<boost::filesystem::path>::const_iterator iter (begin); iter!=end2; ++iter)
|
||||
getData().loadFile (*iter, true);
|
||||
getData().loadFile (*iter, true, false);
|
||||
|
||||
if (lastAsModified)
|
||||
getData().loadFile (*end2, false);
|
||||
getData().loadFile (*end2, false, false);
|
||||
}
|
||||
|
||||
void CSMDoc::Document::addGmsts()
|
||||
|
@ -2058,9 +2065,9 @@ void CSMDoc::Document::addOptionalGlobals()
|
|||
{
|
||||
static const char *sGlobals[] =
|
||||
{
|
||||
"dayspassed",
|
||||
"pcwerewolf",
|
||||
"pcyear",
|
||||
"DaysPassed",
|
||||
"PCWerewolf",
|
||||
"PCYear",
|
||||
0
|
||||
};
|
||||
|
||||
|
@ -2137,11 +2144,86 @@ void CSMDoc::Document::createBase()
|
|||
|
||||
getData().getSkills().add (record);
|
||||
}
|
||||
|
||||
static const char *sVoice[] =
|
||||
{
|
||||
"Intruder",
|
||||
"Attack",
|
||||
"Hello",
|
||||
"Thief",
|
||||
"Alarm",
|
||||
"Idle",
|
||||
"Flee",
|
||||
"Hit",
|
||||
0
|
||||
};
|
||||
|
||||
for (int i=0; sVoice[i]; ++i)
|
||||
{
|
||||
ESM::Dialogue record;
|
||||
record.mId = sVoice[i];
|
||||
record.mType = ESM::Dialogue::Voice;
|
||||
record.blank();
|
||||
|
||||
getData().getTopics().add (record);
|
||||
}
|
||||
|
||||
static const char *sGreetings[] =
|
||||
{
|
||||
"Greeting 0",
|
||||
"Greeting 1",
|
||||
"Greeting 2",
|
||||
"Greeting 3",
|
||||
"Greeting 4",
|
||||
"Greeting 5",
|
||||
"Greeting 6",
|
||||
"Greeting 7",
|
||||
"Greeting 8",
|
||||
"Greeting 9",
|
||||
0
|
||||
};
|
||||
|
||||
for (int i=0; sGreetings[i]; ++i)
|
||||
{
|
||||
ESM::Dialogue record;
|
||||
record.mId = sGreetings[i];
|
||||
record.mType = ESM::Dialogue::Greeting;
|
||||
record.blank();
|
||||
|
||||
getData().getTopics().add (record);
|
||||
}
|
||||
|
||||
static const char *sPersuasion[] =
|
||||
{
|
||||
"Intimidate Success",
|
||||
"Intimidate Fail",
|
||||
"Service Refusal",
|
||||
"Admire Success",
|
||||
"Taunt Success",
|
||||
"Bribe Success",
|
||||
"Info Refusal",
|
||||
"Admire Fail",
|
||||
"Taunt Fail",
|
||||
"Bribe Fail",
|
||||
0
|
||||
};
|
||||
|
||||
for (int i=0; sPersuasion[i]; ++i)
|
||||
{
|
||||
ESM::Dialogue record;
|
||||
record.mId = sPersuasion[i];
|
||||
record.mType = ESM::Dialogue::Persuasion;
|
||||
record.blank();
|
||||
|
||||
getData().getTopics().add (record);
|
||||
}
|
||||
}
|
||||
|
||||
CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files,
|
||||
const boost::filesystem::path& savePath, bool new_)
|
||||
: mSavePath (savePath), mTools (mData)
|
||||
CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_)
|
||||
: mSavePath (savePath), mContentFiles (files), mTools (mData), mResDir(resDir),
|
||||
mProjectPath ((configuration.getUserDataPath() / "projects") /
|
||||
(savePath.filename().string() + ".project")),
|
||||
mSaving (*this, mProjectPath)
|
||||
{
|
||||
if (files.empty())
|
||||
throw std::runtime_error ("Empty content file sequence");
|
||||
|
@ -2158,6 +2240,44 @@ CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files,
|
|||
load (files.begin(), end, !new_);
|
||||
}
|
||||
|
||||
if (new_)
|
||||
{
|
||||
mData.setDescription ("");
|
||||
mData.setAuthor ("");
|
||||
}
|
||||
|
||||
bool filtersFound = false;
|
||||
|
||||
if (boost::filesystem::exists (mProjectPath))
|
||||
{
|
||||
filtersFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::filesystem::path locCustomFiltersPath (configuration.getUserDataPath());
|
||||
locCustomFiltersPath /= "defaultfilters";
|
||||
|
||||
if (boost::filesystem::exists(locCustomFiltersPath))
|
||||
{
|
||||
boost::filesystem::copy_file (locCustomFiltersPath, mProjectPath);
|
||||
filtersFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::filesystem::path filters(mResDir);
|
||||
filters /= "defaultfilters";
|
||||
|
||||
if (boost::filesystem::exists(filters))
|
||||
{
|
||||
boost::filesystem::copy_file(filters, mProjectPath);
|
||||
filtersFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (filtersFound)
|
||||
getData().loadFile (mProjectPath, false, true);
|
||||
|
||||
addOptionalGmsts();
|
||||
addOptionalGlobals();
|
||||
|
||||
|
@ -2166,9 +2286,10 @@ CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files,
|
|||
connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
|
||||
connect (&mTools, SIGNAL (done (int)), this, SLOT (operationDone (int)));
|
||||
|
||||
// dummy implementation -> remove when proper save is implemented.
|
||||
mSaveCount = 0;
|
||||
connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving()));
|
||||
connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
|
||||
connect (&mSaving, SIGNAL (done (int)), this, SLOT (operationDone (int)));
|
||||
connect (&mSaving, SIGNAL (reportMessage (const QString&, int)),
|
||||
this, SLOT (reportMessage (const QString&, int)));
|
||||
}
|
||||
|
||||
CSMDoc::Document::~Document()
|
||||
|
@ -2187,7 +2308,7 @@ int CSMDoc::Document::getState() const
|
|||
if (!mUndoStack.isClean())
|
||||
state |= State_Modified;
|
||||
|
||||
if (mSaveCount)
|
||||
if (mSaving.isRunning())
|
||||
state |= State_Locked | State_Saving | State_Operation;
|
||||
|
||||
if (int operations = mTools.getRunningOperations())
|
||||
|
@ -2201,12 +2322,20 @@ const boost::filesystem::path& CSMDoc::Document::getSavePath() const
|
|||
return mSavePath;
|
||||
}
|
||||
|
||||
const std::vector<boost::filesystem::path>& CSMDoc::Document::getContentFiles() const
|
||||
{
|
||||
return mContentFiles;
|
||||
}
|
||||
|
||||
void CSMDoc::Document::save()
|
||||
{
|
||||
mSaveCount = 1;
|
||||
mSaveTimer.start (500);
|
||||
if (mSaving.isRunning())
|
||||
throw std::logic_error (
|
||||
"Failed to initiate save, because a save operation is already running.");
|
||||
|
||||
mSaving.start();
|
||||
|
||||
emit stateChanged (getState(), this);
|
||||
emit progress (1, 16, State_Saving, 1, this);
|
||||
}
|
||||
|
||||
CSMWorld::UniversalId CSMDoc::Document::verify()
|
||||
|
@ -2218,46 +2347,28 @@ CSMWorld::UniversalId CSMDoc::Document::verify()
|
|||
|
||||
void CSMDoc::Document::abortOperation (int type)
|
||||
{
|
||||
mTools.abortOperation (type);
|
||||
|
||||
if (type==State_Saving)
|
||||
{
|
||||
mSaveCount=0;
|
||||
mSaveTimer.stop();
|
||||
emit stateChanged (getState(), this);
|
||||
}
|
||||
mSaving.abort();
|
||||
else
|
||||
mTools.abortOperation (type);
|
||||
}
|
||||
|
||||
|
||||
void CSMDoc::Document::modificationStateChanged (bool clean)
|
||||
{
|
||||
emit stateChanged (getState(), this);
|
||||
}
|
||||
|
||||
void CSMDoc::Document::reportMessage (const QString& message, int type)
|
||||
{
|
||||
/// \todo find a better way to get these messages to the user.
|
||||
std::cout << message.toUtf8().constData() << std::endl;
|
||||
}
|
||||
|
||||
void CSMDoc::Document::operationDone (int type)
|
||||
{
|
||||
emit stateChanged (getState(), this);
|
||||
}
|
||||
|
||||
void CSMDoc::Document::saving()
|
||||
{
|
||||
++mSaveCount;
|
||||
|
||||
emit progress (mSaveCount, 16, State_Saving, 1, this);
|
||||
|
||||
if (mSaveCount>15)
|
||||
{
|
||||
//clear the stack before resetting the save state
|
||||
//to avoid emitting incorrect states
|
||||
mUndoStack.setClean();
|
||||
|
||||
mSaveCount = 0;
|
||||
mSaveTimer.stop();
|
||||
emit stateChanged (getState(), this);
|
||||
}
|
||||
}
|
||||
|
||||
const CSMWorld::Data& CSMDoc::Document::getData() const
|
||||
{
|
||||
return mData;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "../tools/tools.hpp"
|
||||
|
||||
#include "state.hpp"
|
||||
#include "saving.hpp"
|
||||
|
||||
class QAbstractItemModel;
|
||||
|
||||
|
@ -23,6 +24,11 @@ namespace ESM
|
|||
struct Global;
|
||||
}
|
||||
|
||||
namespace Files
|
||||
{
|
||||
class ConfigurationManager;
|
||||
}
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Document : public QObject
|
||||
|
@ -32,16 +38,17 @@ namespace CSMDoc
|
|||
private:
|
||||
|
||||
boost::filesystem::path mSavePath;
|
||||
std::vector<boost::filesystem::path> mContentFiles;
|
||||
CSMWorld::Data mData;
|
||||
CSMTools::Tools mTools;
|
||||
boost::filesystem::path mProjectPath;
|
||||
Saving mSaving;
|
||||
boost::filesystem::path mResDir;
|
||||
|
||||
// It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is
|
||||
// using other member variables. Unfortunately this connection is cut only in the QObject destructor, which is way too late.
|
||||
QUndoStack mUndoStack;
|
||||
|
||||
int mSaveCount; ///< dummy implementation -> remove when proper save is implemented.
|
||||
QTimer mSaveTimer; ///< dummy implementation -> remove when proper save is implemented.
|
||||
|
||||
// not implemented
|
||||
Document (const Document&);
|
||||
Document& operator= (const Document&);
|
||||
|
@ -64,8 +71,7 @@ namespace CSMDoc
|
|||
|
||||
public:
|
||||
|
||||
Document (const std::vector<boost::filesystem::path>& files,
|
||||
const boost::filesystem::path& savePath, bool new_);
|
||||
Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_);
|
||||
|
||||
~Document();
|
||||
|
||||
|
@ -75,6 +81,10 @@ namespace CSMDoc
|
|||
|
||||
const boost::filesystem::path& getSavePath() const;
|
||||
|
||||
const std::vector<boost::filesystem::path>& getContentFiles() const;
|
||||
///< \attention The last element in this collection is the file that is being edited,
|
||||
/// but with its original path instead of the save path.
|
||||
|
||||
void save();
|
||||
|
||||
CSMWorld::UniversalId verify();
|
||||
|
@ -98,10 +108,9 @@ namespace CSMDoc
|
|||
|
||||
void modificationStateChanged (bool clean);
|
||||
|
||||
void operationDone (int type);
|
||||
void reportMessage (const QString& message, int type);
|
||||
|
||||
void saving();
|
||||
///< dummy implementation -> remove when proper save is implemented.
|
||||
void operationDone (int type);
|
||||
|
||||
public slots:
|
||||
|
||||
|
@ -110,3 +119,4 @@ namespace CSMDoc
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -4,9 +4,22 @@
|
|||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#ifndef Q_MOC_RUN
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
#endif
|
||||
|
||||
#include "document.hpp"
|
||||
|
||||
CSMDoc::DocumentManager::DocumentManager() {}
|
||||
CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration)
|
||||
: mConfiguration (configuration)
|
||||
{
|
||||
boost::filesystem::path projectPath = configuration.getUserDataPath() / "projects";
|
||||
|
||||
if (!boost::filesystem::is_directory (projectPath))
|
||||
boost::filesystem::create_directories (projectPath);
|
||||
}
|
||||
|
||||
CSMDoc::DocumentManager::~DocumentManager()
|
||||
{
|
||||
|
@ -17,7 +30,7 @@ CSMDoc::DocumentManager::~DocumentManager()
|
|||
CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath,
|
||||
bool new_)
|
||||
{
|
||||
Document *document = new Document (files, savePath, new_);
|
||||
Document *document = new Document (mConfiguration, files, savePath, mResDir, new_);
|
||||
|
||||
mDocuments.push_back (document);
|
||||
|
||||
|
@ -35,4 +48,9 @@ bool CSMDoc::DocumentManager::removeDocument (Document *document)
|
|||
delete document;
|
||||
|
||||
return mDocuments.empty();
|
||||
}
|
||||
}
|
||||
|
||||
void CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& parResDir)
|
||||
{
|
||||
mResDir = boost::filesystem::system_complete(parResDir);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,11 @@
|
|||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
namespace Files
|
||||
{
|
||||
class ConfigurationManager;
|
||||
}
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Document;
|
||||
|
@ -13,18 +18,18 @@ namespace CSMDoc
|
|||
class DocumentManager
|
||||
{
|
||||
std::vector<Document *> mDocuments;
|
||||
const Files::ConfigurationManager& mConfiguration;
|
||||
|
||||
DocumentManager (const DocumentManager&);
|
||||
DocumentManager& operator= (const DocumentManager&);
|
||||
|
||||
public:
|
||||
|
||||
DocumentManager();
|
||||
DocumentManager (const Files::ConfigurationManager& configuration);
|
||||
|
||||
~DocumentManager();
|
||||
|
||||
Document *addDocument (const std::vector<boost::filesystem::path>& files,
|
||||
const boost::filesystem::path& savePath, bool new_);
|
||||
Document *addDocument (const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, bool new_);
|
||||
///< The ownership of the returned document is not transferred to the caller.
|
||||
///
|
||||
/// \param new_ Do not load the last content file in \a files and instead create in an
|
||||
|
@ -32,6 +37,10 @@ namespace CSMDoc
|
|||
|
||||
bool removeDocument (Document *document);
|
||||
///< \return last document removed?
|
||||
void setResourceDir (const boost::filesystem::path& parResDir);
|
||||
|
||||
private:
|
||||
boost::filesystem::path mResDir;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -6,16 +6,16 @@
|
|||
|
||||
#include <QTimer>
|
||||
|
||||
#include "../doc/state.hpp"
|
||||
|
||||
#include "state.hpp"
|
||||
#include "stage.hpp"
|
||||
|
||||
void CSMTools::Operation::prepareStages()
|
||||
void CSMDoc::Operation::prepareStages()
|
||||
{
|
||||
mCurrentStage = mStages.begin();
|
||||
mCurrentStep = 0;
|
||||
mCurrentStepTotal = 0;
|
||||
mTotalSteps = 0;
|
||||
mError = false;
|
||||
|
||||
for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter)
|
||||
{
|
||||
|
@ -24,38 +24,61 @@ void CSMTools::Operation::prepareStages()
|
|||
}
|
||||
}
|
||||
|
||||
CSMTools::Operation::Operation (int type) : mType (type) {}
|
||||
CSMDoc::Operation::Operation (int type, bool ordered, bool finalAlways)
|
||||
: mType (type), mOrdered (ordered), mFinalAlways (finalAlways)
|
||||
{
|
||||
connect (this, SIGNAL (finished()), this, SLOT (operationDone()));
|
||||
}
|
||||
|
||||
CSMTools::Operation::~Operation()
|
||||
CSMDoc::Operation::~Operation()
|
||||
{
|
||||
for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter)
|
||||
delete iter->first;
|
||||
}
|
||||
|
||||
void CSMTools::Operation::run()
|
||||
void CSMDoc::Operation::run()
|
||||
{
|
||||
prepareStages();
|
||||
|
||||
QTimer timer;
|
||||
|
||||
timer.connect (&timer, SIGNAL (timeout()), this, SLOT (verify()));
|
||||
timer.connect (&timer, SIGNAL (timeout()), this, SLOT (executeStage()));
|
||||
|
||||
timer.start (0);
|
||||
|
||||
exec();
|
||||
}
|
||||
|
||||
void CSMTools::Operation::appendStage (Stage *stage)
|
||||
void CSMDoc::Operation::appendStage (Stage *stage)
|
||||
{
|
||||
mStages.push_back (std::make_pair (stage, 0));
|
||||
}
|
||||
|
||||
void CSMTools::Operation::abort()
|
||||
bool CSMDoc::Operation::hasError() const
|
||||
{
|
||||
exit();
|
||||
return mError;
|
||||
}
|
||||
|
||||
void CSMTools::Operation::verify()
|
||||
void CSMDoc::Operation::abort()
|
||||
{
|
||||
if (!isRunning())
|
||||
return;
|
||||
|
||||
mError = true;
|
||||
|
||||
if (mFinalAlways)
|
||||
{
|
||||
if (mStages.begin()!=mStages.end() && mCurrentStage!=--mStages.end())
|
||||
{
|
||||
mCurrentStep = 0;
|
||||
mCurrentStage = --mStages.end();
|
||||
}
|
||||
}
|
||||
else
|
||||
mCurrentStage = mStages.end();
|
||||
}
|
||||
|
||||
void CSMDoc::Operation::executeStage()
|
||||
{
|
||||
std::vector<std::string> messages;
|
||||
|
||||
|
@ -68,7 +91,16 @@ void CSMTools::Operation::verify()
|
|||
}
|
||||
else
|
||||
{
|
||||
mCurrentStage->first->perform (mCurrentStep++, messages);
|
||||
try
|
||||
{
|
||||
mCurrentStage->first->perform (mCurrentStep++, messages);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
emit reportMessage (e.what(), mType);
|
||||
abort();
|
||||
}
|
||||
|
||||
++mCurrentStepTotal;
|
||||
break;
|
||||
}
|
||||
|
@ -81,4 +113,9 @@ void CSMTools::Operation::verify()
|
|||
|
||||
if (mCurrentStage==mStages.end())
|
||||
exit();
|
||||
}
|
||||
|
||||
void CSMDoc::Operation::operationDone()
|
||||
{
|
||||
emit done (mType);
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
#ifndef CSM_TOOLS_OPERATION_H
|
||||
#define CSM_TOOLS_OPERATION_H
|
||||
#ifndef CSM_DOC_OPERATION_H
|
||||
#define CSM_DOC_OPERATION_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QThread>
|
||||
|
||||
namespace CSMTools
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Stage;
|
||||
|
||||
|
@ -19,12 +19,17 @@ namespace CSMTools
|
|||
int mCurrentStep;
|
||||
int mCurrentStepTotal;
|
||||
int mTotalSteps;
|
||||
int mOrdered;
|
||||
bool mFinalAlways;
|
||||
bool mError;
|
||||
|
||||
void prepareStages();
|
||||
|
||||
public:
|
||||
|
||||
Operation (int type);
|
||||
Operation (int type, bool ordered, bool finalAlways = false);
|
||||
///< \param ordered Stages must be executed in the given order.
|
||||
/// \param finalAlways Execute last stage even if an error occurred during earlier stages.
|
||||
|
||||
virtual ~Operation();
|
||||
|
||||
|
@ -35,19 +40,25 @@ namespace CSMTools
|
|||
///
|
||||
/// \attention Do no call this function while this Operation is running.
|
||||
|
||||
bool hasError() const;
|
||||
|
||||
signals:
|
||||
|
||||
void progress (int current, int max, int type);
|
||||
|
||||
void reportMessage (const QString& message, int type);
|
||||
|
||||
void done (int type);
|
||||
|
||||
public slots:
|
||||
|
||||
void abort();
|
||||
|
||||
private slots:
|
||||
|
||||
void verify();
|
||||
void executeStage();
|
||||
|
||||
void operationDone();
|
||||
};
|
||||
}
|
||||
|
71
apps/opencs/model/doc/saving.cpp
Normal file
71
apps/opencs/model/doc/saving.cpp
Normal file
|
@ -0,0 +1,71 @@
|
|||
|
||||
#include "saving.hpp"
|
||||
|
||||
#include "../world/data.hpp"
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "state.hpp"
|
||||
#include "savingstages.hpp"
|
||||
#include "document.hpp"
|
||||
|
||||
CSMDoc::Saving::Saving (Document& document, const boost::filesystem::path& projectPath)
|
||||
: Operation (State_Saving, true, true), mDocument (document), mState (*this, projectPath)
|
||||
{
|
||||
// save project file
|
||||
appendStage (new OpenSaveStage (mDocument, mState, true));
|
||||
|
||||
appendStage (new WriteHeaderStage (mDocument, mState, true));
|
||||
|
||||
appendStage (new WriteFilterStage (mDocument, mState, CSMFilter::Filter::Scope_Project));
|
||||
|
||||
appendStage (new CloseSaveStage (mState));
|
||||
|
||||
// save content file
|
||||
appendStage (new OpenSaveStage (mDocument, mState, false));
|
||||
|
||||
appendStage (new WriteHeaderStage (mDocument, mState, false));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Global> >
|
||||
(mDocument.getData().getGlobals(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::GameSetting> >
|
||||
(mDocument.getData().getGmsts(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Skill> >
|
||||
(mDocument.getData().getSkills(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Class> >
|
||||
(mDocument.getData().getClasses(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Faction> >
|
||||
(mDocument.getData().getFactions(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Race> >
|
||||
(mDocument.getData().getRaces(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Sound> >
|
||||
(mDocument.getData().getSounds(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Script> >
|
||||
(mDocument.getData().getScripts(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Region> >
|
||||
(mDocument.getData().getRegions(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::BirthSign> >
|
||||
(mDocument.getData().getBirthsigns(), mState));
|
||||
|
||||
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Spell> >
|
||||
(mDocument.getData().getSpells(), mState));
|
||||
|
||||
appendStage (new WriteDialogueCollectionStage (mDocument, mState, false));
|
||||
|
||||
appendStage (new WriteDialogueCollectionStage (mDocument, mState, true));
|
||||
|
||||
appendStage (new WriteRefIdCollectionStage (mDocument, mState));
|
||||
|
||||
|
||||
appendStage (new CloseSaveStage (mState));
|
||||
|
||||
appendStage (new FinalSavingStage (mDocument, mState));
|
||||
}
|
27
apps/opencs/model/doc/saving.hpp
Normal file
27
apps/opencs/model/doc/saving.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef CSM_DOC_SAVING_H
|
||||
#define CSM_DOC_SAVING_H
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include "operation.hpp"
|
||||
#include "savingstate.hpp"
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Document;
|
||||
|
||||
class Saving : public Operation
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Document& mDocument;
|
||||
SavingState mState;
|
||||
|
||||
public:
|
||||
|
||||
Saving (Document& document, const boost::filesystem::path& projectPath);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
263
apps/opencs/model/doc/savingstages.cpp
Normal file
263
apps/opencs/model/doc/savingstages.cpp
Normal file
|
@ -0,0 +1,263 @@
|
|||
|
||||
#include "savingstages.hpp"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <QUndoStack>
|
||||
|
||||
#include <components/esm/loaddial.hpp>
|
||||
|
||||
#include "../world/infocollection.hpp"
|
||||
|
||||
#include "document.hpp"
|
||||
#include "savingstate.hpp"
|
||||
|
||||
CSMDoc::OpenSaveStage::OpenSaveStage (Document& document, SavingState& state, bool projectFile)
|
||||
: mDocument (document), mState (state), mProjectFile (projectFile)
|
||||
{}
|
||||
|
||||
int CSMDoc::OpenSaveStage::setup()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CSMDoc::OpenSaveStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
mState.start (mDocument, mProjectFile);
|
||||
|
||||
mState.getStream().open ((mProjectFile ? mState.getPath() : mState.getTmpPath()).string().c_str());
|
||||
|
||||
if (!mState.getStream().is_open())
|
||||
throw std::runtime_error ("failed to open stream for saving");
|
||||
}
|
||||
|
||||
|
||||
CSMDoc::WriteHeaderStage::WriteHeaderStage (Document& document, SavingState& state, bool simple)
|
||||
: mDocument (document), mState (state), mSimple (simple)
|
||||
{}
|
||||
|
||||
int CSMDoc::WriteHeaderStage::setup()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CSMDoc::WriteHeaderStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
mState.getWriter().setVersion();
|
||||
|
||||
mState.getWriter().clearMaster();
|
||||
|
||||
mState.getWriter().setFormat (0);
|
||||
|
||||
if (mSimple)
|
||||
{
|
||||
mState.getWriter().setAuthor ("");
|
||||
mState.getWriter().setDescription ("");
|
||||
mState.getWriter().setRecordCount (0);
|
||||
}
|
||||
else
|
||||
{
|
||||
mState.getWriter().setAuthor (mDocument.getData().getAuthor());
|
||||
mState.getWriter().setDescription (mDocument.getData().getDescription());
|
||||
mState.getWriter().setRecordCount (
|
||||
mDocument.getData().count (CSMWorld::RecordBase::State_Modified) +
|
||||
mDocument.getData().count (CSMWorld::RecordBase::State_ModifiedOnly) +
|
||||
mDocument.getData().count (CSMWorld::RecordBase::State_Deleted));
|
||||
|
||||
/// \todo refine dependency list (at least remove redundant dependencies)
|
||||
std::vector<boost::filesystem::path> dependencies = mDocument.getContentFiles();
|
||||
std::vector<boost::filesystem::path>::const_iterator end (--dependencies.end());
|
||||
|
||||
for (std::vector<boost::filesystem::path>::const_iterator iter (dependencies.begin());
|
||||
iter!=end; ++iter)
|
||||
{
|
||||
std::string name = iter->filename().string();
|
||||
uint64_t size = boost::filesystem::file_size (*iter);
|
||||
|
||||
mState.getWriter().addMaster (name, size);
|
||||
}
|
||||
}
|
||||
|
||||
mState.getWriter().save (mState.getStream());
|
||||
}
|
||||
|
||||
|
||||
CSMDoc::WriteDialogueCollectionStage::WriteDialogueCollectionStage (Document& document,
|
||||
SavingState& state, bool journal)
|
||||
: mDocument (document), mState (state),
|
||||
mTopics (journal ? document.getData().getJournals() : document.getData().getTopics()),
|
||||
mInfos (journal ? document.getData().getJournalInfos() : document.getData().getTopicInfos())
|
||||
{}
|
||||
|
||||
int CSMDoc::WriteDialogueCollectionStage::setup()
|
||||
{
|
||||
return mTopics.getSize();
|
||||
}
|
||||
|
||||
void CSMDoc::WriteDialogueCollectionStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
const CSMWorld::Record<ESM::Dialogue>& topic = mTopics.getRecord (stage);
|
||||
|
||||
CSMWorld::RecordBase::State state = topic.mState;
|
||||
|
||||
if (state==CSMWorld::RecordBase::State_Deleted)
|
||||
{
|
||||
// if the topic is deleted, we do not need to bother with INFO records.
|
||||
|
||||
/// \todo wrote record with delete flag
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Test, if we need to save anything associated info records.
|
||||
bool infoModified = false;
|
||||
|
||||
CSMWorld::InfoCollection::Range range = mInfos.getTopicRange (topic.get().mId);
|
||||
|
||||
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter)
|
||||
{
|
||||
CSMWorld::RecordBase::State state = iter->mState;
|
||||
|
||||
if (state==CSMWorld::RecordBase::State_Modified ||
|
||||
state==CSMWorld::RecordBase::State_ModifiedOnly ||
|
||||
state==CSMWorld::RecordBase::State_Deleted)
|
||||
{
|
||||
infoModified = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (state==CSMWorld::RecordBase::State_Modified ||
|
||||
state==CSMWorld::RecordBase::State_ModifiedOnly ||
|
||||
infoModified)
|
||||
{
|
||||
mState.getWriter().startRecord (topic.mModified.sRecordId);
|
||||
mState.getWriter().writeHNCString ("NAME", topic.mModified.mId);
|
||||
topic.mModified.save (mState.getWriter());
|
||||
mState.getWriter().endRecord (topic.mModified.sRecordId);
|
||||
|
||||
// write modified selected info records
|
||||
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second;
|
||||
++iter)
|
||||
{
|
||||
CSMWorld::RecordBase::State state = iter->mState;
|
||||
|
||||
if (state==CSMWorld::RecordBase::State_Deleted)
|
||||
{
|
||||
/// \todo wrote record with delete flag
|
||||
}
|
||||
else if (state==CSMWorld::RecordBase::State_Modified ||
|
||||
state==CSMWorld::RecordBase::State_ModifiedOnly)
|
||||
{
|
||||
ESM::DialInfo info = iter->get();
|
||||
info.mId = info.mId.substr (info.mId.find_last_of ('#')+1);
|
||||
|
||||
if (iter!=range.first)
|
||||
{
|
||||
CSMWorld::InfoCollection::RecordConstIterator prev = iter;
|
||||
--prev;
|
||||
|
||||
info.mPrev =
|
||||
prev->mModified.mId.substr (prev->mModified.mId.find_last_of ('#')+1);
|
||||
}
|
||||
|
||||
CSMWorld::InfoCollection::RecordConstIterator next = iter;
|
||||
++next;
|
||||
|
||||
if (next!=range.second)
|
||||
{
|
||||
info.mNext =
|
||||
next->mModified.mId.substr (next->mModified.mId.find_last_of ('#')+1);
|
||||
}
|
||||
|
||||
mState.getWriter().startRecord (info.sRecordId);
|
||||
mState.getWriter().writeHNCString ("INAM", info.mId);
|
||||
info.save (mState.getWriter());
|
||||
mState.getWriter().endRecord (info.sRecordId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CSMDoc::WriteRefIdCollectionStage::WriteRefIdCollectionStage (Document& document, SavingState& state)
|
||||
: mDocument (document), mState (state)
|
||||
{}
|
||||
|
||||
int CSMDoc::WriteRefIdCollectionStage::setup()
|
||||
{
|
||||
return mDocument.getData().getReferenceables().getSize();
|
||||
}
|
||||
|
||||
void CSMDoc::WriteRefIdCollectionStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
mDocument.getData().getReferenceables().save (stage, mState.getWriter());
|
||||
}
|
||||
|
||||
|
||||
CSMDoc::WriteFilterStage::WriteFilterStage (Document& document, SavingState& state,
|
||||
CSMFilter::Filter::Scope scope)
|
||||
: WriteCollectionStage<CSMWorld::IdCollection<CSMFilter::Filter> > (document.getData().getFilters(),
|
||||
state),
|
||||
mDocument (document), mScope (scope)
|
||||
{}
|
||||
|
||||
void CSMDoc::WriteFilterStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
const CSMWorld::Record<CSMFilter::Filter>& record =
|
||||
mDocument.getData().getFilters().getRecord (stage);
|
||||
|
||||
if (record.get().mScope==mScope)
|
||||
WriteCollectionStage<CSMWorld::IdCollection<CSMFilter::Filter> >::perform (stage, messages);
|
||||
}
|
||||
|
||||
|
||||
CSMDoc::CloseSaveStage::CloseSaveStage (SavingState& state)
|
||||
: mState (state)
|
||||
{}
|
||||
|
||||
int CSMDoc::CloseSaveStage::setup()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CSMDoc::CloseSaveStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
mState.getStream().close();
|
||||
|
||||
if (!mState.getStream())
|
||||
throw std::runtime_error ("saving failed");
|
||||
}
|
||||
|
||||
|
||||
CSMDoc::FinalSavingStage::FinalSavingStage (Document& document, SavingState& state)
|
||||
: mDocument (document), mState (state)
|
||||
{}
|
||||
|
||||
int CSMDoc::FinalSavingStage::setup()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CSMDoc::FinalSavingStage::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
if (mState.hasError())
|
||||
{
|
||||
mState.getWriter().close();
|
||||
mState.getStream().close();
|
||||
|
||||
if (boost::filesystem::exists (mState.getTmpPath()))
|
||||
boost::filesystem::remove (mState.getTmpPath());
|
||||
}
|
||||
else if (!mState.isProjectFile())
|
||||
{
|
||||
if (boost::filesystem::exists (mState.getPath()))
|
||||
boost::filesystem::remove (mState.getPath());
|
||||
|
||||
boost::filesystem::rename (mState.getTmpPath(), mState.getPath());
|
||||
|
||||
mDocument.getUndoStack().setClean();
|
||||
}
|
||||
}
|
201
apps/opencs/model/doc/savingstages.hpp
Normal file
201
apps/opencs/model/doc/savingstages.hpp
Normal file
|
@ -0,0 +1,201 @@
|
|||
#ifndef CSM_DOC_SAVINGSTAGES_H
|
||||
#define CSM_DOC_SAVINGSTAGES_H
|
||||
|
||||
#include "stage.hpp"
|
||||
|
||||
#include "../world/record.hpp"
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "../filter/filter.hpp"
|
||||
|
||||
#include "savingstate.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
struct Dialogue;
|
||||
}
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class InfoCollection;
|
||||
}
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Document;
|
||||
class SavingState;
|
||||
|
||||
class OpenSaveStage : public Stage
|
||||
{
|
||||
Document& mDocument;
|
||||
SavingState& mState;
|
||||
bool mProjectFile;
|
||||
|
||||
public:
|
||||
|
||||
OpenSaveStage (Document& document, SavingState& state, bool projectFile);
|
||||
///< \param projectFile Saving the project file instead of the content file.
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
};
|
||||
|
||||
class WriteHeaderStage : public Stage
|
||||
{
|
||||
Document& mDocument;
|
||||
SavingState& mState;
|
||||
bool mSimple;
|
||||
|
||||
public:
|
||||
|
||||
WriteHeaderStage (Document& document, SavingState& state, bool simple);
|
||||
///< \param simple Simplified header (used for project files).
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
};
|
||||
|
||||
|
||||
template<class CollectionT>
|
||||
class WriteCollectionStage : public Stage
|
||||
{
|
||||
const CollectionT& mCollection;
|
||||
SavingState& mState;
|
||||
|
||||
public:
|
||||
|
||||
WriteCollectionStage (const CollectionT& collection, SavingState& state);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
};
|
||||
|
||||
template<class CollectionT>
|
||||
WriteCollectionStage<CollectionT>::WriteCollectionStage (const CollectionT& collection,
|
||||
SavingState& state)
|
||||
: mCollection (collection), mState (state)
|
||||
{}
|
||||
|
||||
template<class CollectionT>
|
||||
int WriteCollectionStage<CollectionT>::setup()
|
||||
{
|
||||
return mCollection.getSize();
|
||||
}
|
||||
|
||||
template<class CollectionT>
|
||||
void WriteCollectionStage<CollectionT>::perform (int stage, std::vector<std::string>& messages)
|
||||
{
|
||||
CSMWorld::RecordBase::State state = mCollection.getRecord (stage).mState;
|
||||
|
||||
if (state==CSMWorld::RecordBase::State_Modified ||
|
||||
state==CSMWorld::RecordBase::State_ModifiedOnly)
|
||||
{
|
||||
std::string type;
|
||||
for (int i=0; i<4; ++i)
|
||||
/// \todo make endianess agnostic (change ESMWriter interface?)
|
||||
type += reinterpret_cast<const char *> (&mCollection.getRecord (stage).mModified.sRecordId)[i];
|
||||
|
||||
mState.getWriter().startRecord (mCollection.getRecord (stage).mModified.sRecordId);
|
||||
mState.getWriter().writeHNCString ("NAME", mCollection.getId (stage));
|
||||
mCollection.getRecord (stage).mModified.save (mState.getWriter());
|
||||
mState.getWriter().endRecord (mCollection.getRecord (stage).mModified.sRecordId);
|
||||
}
|
||||
else if (state==CSMWorld::RecordBase::State_Deleted)
|
||||
{
|
||||
/// \todo write record with delete flag
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class WriteDialogueCollectionStage : public Stage
|
||||
{
|
||||
Document& mDocument;
|
||||
SavingState& mState;
|
||||
const CSMWorld::IdCollection<ESM::Dialogue>& mTopics;
|
||||
CSMWorld::InfoCollection& mInfos;
|
||||
|
||||
public:
|
||||
|
||||
WriteDialogueCollectionStage (Document& document, SavingState& state, bool journal);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
};
|
||||
|
||||
|
||||
class WriteRefIdCollectionStage : public Stage
|
||||
{
|
||||
Document& mDocument;
|
||||
SavingState& mState;
|
||||
|
||||
public:
|
||||
|
||||
WriteRefIdCollectionStage (Document& document, SavingState& state);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
};
|
||||
|
||||
|
||||
class WriteFilterStage : public WriteCollectionStage<CSMWorld::IdCollection<CSMFilter::Filter> >
|
||||
{
|
||||
Document& mDocument;
|
||||
CSMFilter::Filter::Scope mScope;
|
||||
|
||||
public:
|
||||
|
||||
WriteFilterStage (Document& document, SavingState& state, CSMFilter::Filter::Scope scope);
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
};
|
||||
|
||||
|
||||
class CloseSaveStage : public Stage
|
||||
{
|
||||
SavingState& mState;
|
||||
|
||||
public:
|
||||
|
||||
CloseSaveStage (SavingState& state);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
};
|
||||
|
||||
class FinalSavingStage : public Stage
|
||||
{
|
||||
Document& mDocument;
|
||||
SavingState& mState;
|
||||
|
||||
public:
|
||||
|
||||
FinalSavingStage (Document& document, SavingState& state);
|
||||
|
||||
virtual int setup();
|
||||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
65
apps/opencs/model/doc/savingstate.cpp
Normal file
65
apps/opencs/model/doc/savingstate.cpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
|
||||
#include "savingstate.hpp"
|
||||
|
||||
#include "operation.hpp"
|
||||
#include "document.hpp"
|
||||
|
||||
CSMDoc::SavingState::SavingState (Operation& operation, const boost::filesystem::path& projectPath)
|
||||
: mOperation (operation),
|
||||
/// \todo set encoding properly, once config implementation has been fixed.
|
||||
mEncoder (ToUTF8::calculateEncoding ("win1252")),
|
||||
mProjectPath (projectPath), mProjectFile (false)
|
||||
{
|
||||
mWriter.setEncoder (&mEncoder);
|
||||
}
|
||||
|
||||
bool CSMDoc::SavingState::hasError() const
|
||||
{
|
||||
return mOperation.hasError();
|
||||
}
|
||||
|
||||
void CSMDoc::SavingState::start (Document& document, bool project)
|
||||
{
|
||||
mProjectFile = project;
|
||||
|
||||
if (mStream.is_open())
|
||||
mStream.close();
|
||||
|
||||
mStream.clear();
|
||||
|
||||
if (project)
|
||||
mPath = mProjectPath;
|
||||
else
|
||||
mPath = document.getSavePath();
|
||||
|
||||
boost::filesystem::path file (mPath.filename().string() + ".tmp");
|
||||
|
||||
mTmpPath = mPath.parent_path();
|
||||
|
||||
mTmpPath /= file;
|
||||
}
|
||||
|
||||
const boost::filesystem::path& CSMDoc::SavingState::getPath() const
|
||||
{
|
||||
return mPath;
|
||||
}
|
||||
|
||||
const boost::filesystem::path& CSMDoc::SavingState::getTmpPath() const
|
||||
{
|
||||
return mTmpPath;
|
||||
}
|
||||
|
||||
std::ofstream& CSMDoc::SavingState::getStream()
|
||||
{
|
||||
return mStream;
|
||||
}
|
||||
|
||||
ESM::ESMWriter& CSMDoc::SavingState::getWriter()
|
||||
{
|
||||
return mWriter;
|
||||
}
|
||||
|
||||
bool CSMDoc::SavingState::isProjectFile() const
|
||||
{
|
||||
return mProjectFile;
|
||||
}
|
50
apps/opencs/model/doc/savingstate.hpp
Normal file
50
apps/opencs/model/doc/savingstate.hpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
#ifndef CSM_DOC_SAVINGSTATE_H
|
||||
#define CSM_DOC_SAVINGSTATE_H
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Operation;
|
||||
class Document;
|
||||
|
||||
class SavingState
|
||||
{
|
||||
Operation& mOperation;
|
||||
boost::filesystem::path mPath;
|
||||
boost::filesystem::path mTmpPath;
|
||||
ToUTF8::Utf8Encoder mEncoder;
|
||||
std::ofstream mStream;
|
||||
ESM::ESMWriter mWriter;
|
||||
boost::filesystem::path mProjectPath;
|
||||
bool mProjectFile;
|
||||
|
||||
public:
|
||||
|
||||
SavingState (Operation& operation, const boost::filesystem::path& projectPath);
|
||||
|
||||
bool hasError() const;
|
||||
|
||||
void start (Document& document, bool project);
|
||||
///< \param project Save project file instead of content file.
|
||||
|
||||
const boost::filesystem::path& getPath() const;
|
||||
|
||||
const boost::filesystem::path& getTmpPath() const;
|
||||
|
||||
std::ofstream& getStream();
|
||||
|
||||
ESM::ESMWriter& getWriter();
|
||||
|
||||
bool isProjectFile() const;
|
||||
///< Currently saving project file? (instead of content file)
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
4
apps/opencs/model/doc/stage.cpp
Normal file
4
apps/opencs/model/doc/stage.cpp
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
#include "stage.hpp"
|
||||
|
||||
CSMDoc::Stage::~Stage() {}
|
|
@ -1,10 +1,10 @@
|
|||
#ifndef CSM_TOOLS_STAGE_H
|
||||
#define CSM_TOOLS_STAGE_H
|
||||
#ifndef CSM_DOC_STAGE_H
|
||||
#define CSM_DOC_STAGE_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace CSMTools
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Stage
|
||||
{
|
||||
|
@ -16,7 +16,7 @@ namespace CSMTools
|
|||
///< \return number of steps
|
||||
|
||||
virtual void perform (int stage, std::vector<std::string>& messages) = 0;
|
||||
///< Messages resulting from this tage will be appended to \a messages.
|
||||
///< Messages resulting from this stage will be appended to \a messages.
|
||||
};
|
||||
}
|
||||
|
|
@ -7,8 +7,6 @@ namespace CSMFilter
|
|||
{
|
||||
class AndNode : public NAryNode
|
||||
{
|
||||
bool mAnd;
|
||||
|
||||
public:
|
||||
|
||||
AndNode (const std::vector<boost::shared_ptr<Node> >& nodes);
|
||||
|
|
|
@ -7,8 +7,6 @@ namespace CSMFilter
|
|||
{
|
||||
class OrNode : public NAryNode
|
||||
{
|
||||
bool mAnd;
|
||||
|
||||
public:
|
||||
|
||||
OrNode (const std::vector<boost::shared_ptr<Node> >& nodes);
|
||||
|
|
|
@ -61,11 +61,11 @@ namespace CSMFilter
|
|||
bool isString() const;
|
||||
};
|
||||
|
||||
Token::Token (Type type) : mType (type) {}
|
||||
Token::Token (Type type) : mType (type), mNumber(0.0) {}
|
||||
|
||||
Token::Token (Type type, const std::string& string) : mType (type), mString (string) {}
|
||||
Token::Token (Type type, const std::string& string) : mType (type), mString (string), mNumber(0.0) {}
|
||||
|
||||
Token::Token (const std::string& string) : mType (Type_String), mString (string) {}
|
||||
Token::Token (const std::string& string) : mType (Type_String), mString (string), mNumber(0.0) {}
|
||||
|
||||
Token::Token (double number) : mType (Type_Number), mNumber (number) {}
|
||||
|
||||
|
@ -614,4 +614,4 @@ boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::getFilter() const
|
|||
throw std::logic_error ("No filter available");
|
||||
|
||||
return mFilter;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,11 @@ namespace CSMSettings
|
|||
inline QStringPair *getValuePair() { return mValuePair; }
|
||||
|
||||
/// set value range (spinbox / integer use)
|
||||
inline void setValuePair (QStringPair valuePair) { mValuePair = new QStringPair(valuePair); }
|
||||
inline void setValuePair (QStringPair valuePair)
|
||||
{
|
||||
delete mValuePair;
|
||||
mValuePair = new QStringPair(valuePair);
|
||||
}
|
||||
|
||||
inline bool isMultivalue () { return mIsMultiValue; }
|
||||
|
||||
|
|
|
@ -251,7 +251,7 @@ void CSMSettings::UserSettings::loadSettings (const QString &fileName)
|
|||
bool localOk = loadFromFile(localFilePath);
|
||||
|
||||
//user
|
||||
mUserFilePath = QString::fromStdString(mCfgMgr.getUserPath().string()) + fileName;
|
||||
mUserFilePath = QString::fromStdString(mCfgMgr.getUserConfigPath().string()) + fileName;
|
||||
loadFromFile(mUserFilePath);
|
||||
|
||||
if (!(localOk || globalOk))
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that birthsign records are internally consistent
|
||||
class BirthsignCheckStage : public Stage
|
||||
class BirthsignCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::BirthSign>& mBirthsigns;
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that class records are internally consistent
|
||||
class ClassCheckStage : public Stage
|
||||
class ClassCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Class>& mClasses;
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that faction records are internally consistent
|
||||
class FactionCheckStage : public Stage
|
||||
class FactionCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#include "../world/universalid.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
|
@ -16,7 +16,7 @@ namespace CSMWorld
|
|||
namespace CSMTools
|
||||
{
|
||||
/// \brief Verify stage: make sure that records with specific IDs exist.
|
||||
class MandatoryIdStage : public Stage
|
||||
class MandatoryIdStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::CollectionBase& mIdCollection;
|
||||
CSMWorld::UniversalId mCollectionId;
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that race records are internally consistent
|
||||
class RaceCheckStage : public Stage
|
||||
class RaceCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Race>& mRaces;
|
||||
bool mPlayable;
|
||||
|
|
1095
apps/opencs/model/tools/referenceablecheck.cpp
Normal file
1095
apps/opencs/model/tools/referenceablecheck.cpp
Normal file
File diff suppressed because it is too large
Load diff
78
apps/opencs/model/tools/referenceablecheck.hpp
Normal file
78
apps/opencs/model/tools/referenceablecheck.hpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
#ifndef REFERENCEABLECHECKSTAGE_H
|
||||
#define REFERENCEABLECHECKSTAGE_H
|
||||
|
||||
#include "../world/universalid.hpp"
|
||||
#include "../doc/stage.hpp"
|
||||
#include "../world/data.hpp"
|
||||
#include "../world/refiddata.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
class ReferenceableCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
public:
|
||||
ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable,
|
||||
const CSMWorld::IdCollection<ESM::Race>& races,
|
||||
const CSMWorld::IdCollection<ESM::Class>& classes,
|
||||
const CSMWorld::IdCollection<ESM::Faction>& factions);
|
||||
|
||||
virtual void perform(int stage, std::vector< std::string >& messages);
|
||||
virtual int setup();
|
||||
|
||||
private:
|
||||
//CONCRETE CHECKS
|
||||
void bookCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Book >& records, std::vector< std::string >& messages);
|
||||
void activatorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Activator >& records, std::vector< std::string >& messages);
|
||||
void potionCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Potion>& records, std::vector<std::string>& messages);
|
||||
void apparatusCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Apparatus>& records, std::vector<std::string>& messages);
|
||||
void armorCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Armor>& records, std::vector<std::string>& messages);
|
||||
void clothingCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Clothing>& records, std::vector<std::string>& messages);
|
||||
void containerCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Container>& records, std::vector<std::string>& messages);
|
||||
void creatureCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Creature>& records, std::vector<std::string>& messages);
|
||||
void doorCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Door>& records, std::vector<std::string>& messages);
|
||||
void ingredientCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Ingredient>& records, std::vector<std::string>& messages);
|
||||
void creaturesLevListCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::CreatureLevList>& records, std::vector<std::string>& messages);
|
||||
void itemLevelledListCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::ItemLevList>& records, std::vector<std::string>& messages);
|
||||
void lightCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Light>& records, std::vector<std::string>& messages);
|
||||
void lockpickCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Lockpick>& records, std::vector<std::string>& messages);
|
||||
void miscCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Miscellaneous>& records, std::vector<std::string>& messages);
|
||||
void npcCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::NPC>& records, std::vector<std::string>& messages);
|
||||
void weaponCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Weapon>& records, std::vector<std::string>& messages);
|
||||
void probeCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Probe>& records, std::vector<std::string>& messages);
|
||||
void repairCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Repair>& records, std::vector<std::string>& messages);
|
||||
void staticCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Static>& records, std::vector<std::string>& messages);
|
||||
|
||||
//FINAL CHECK
|
||||
void finalCheck(std::vector<std::string>& messages);
|
||||
|
||||
//TEMPLATE CHECKS
|
||||
template<typename ITEM> void inventoryItemCheck(const ITEM& someItem,
|
||||
std::vector<std::string>& messages,
|
||||
const std::string& someID,
|
||||
bool enchantable); //for all enchantable items.
|
||||
|
||||
template<typename ITEM> void inventoryItemCheck(const ITEM& someItem,
|
||||
std::vector<std::string>& messages,
|
||||
const std::string& someID); //for non-enchantable items.
|
||||
|
||||
template<typename TOOL> void toolCheck(const TOOL& someTool,
|
||||
std::vector<std::string>& messages,
|
||||
const std::string& someID,
|
||||
bool canbebroken); //for tools with uses.
|
||||
|
||||
template<typename TOOL> void toolCheck(const TOOL& someTool,
|
||||
std::vector<std::string>& messages,
|
||||
const std::string& someID); //for tools without uses.
|
||||
|
||||
template<typename LIST> void listCheck(const LIST& someList,
|
||||
std::vector< std::string >& messages,
|
||||
const std::string& someID);
|
||||
|
||||
const CSMWorld::RefIdData& mReferencables;
|
||||
const CSMWorld::IdCollection<ESM::Race>& mRaces;
|
||||
const CSMWorld::IdCollection<ESM::Class>& mClasses;
|
||||
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
|
||||
bool mPlayerPresent;
|
||||
};
|
||||
}
|
||||
#endif // REFERENCEABLECHECKSTAGE_H
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that region records are internally consistent
|
||||
class RegionCheckStage : public Stage
|
||||
class RegionCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Region>& mRegions;
|
||||
|
||||
|
|
|
@ -68,4 +68,4 @@ void CSMTools::ReportModel::add (const std::string& row)
|
|||
const CSMWorld::UniversalId& CSMTools::ReportModel::getUniversalId (int row) const
|
||||
{
|
||||
return mRows.at (row).first;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that skill records are internally consistent
|
||||
class SkillCheckStage : public Stage
|
||||
class SkillCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Skill>& mSkills;
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that sound records are internally consistent
|
||||
class SoundCheckStage : public Stage
|
||||
class SoundCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Sound>& mSounds;
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
#include "../world/idcollection.hpp"
|
||||
|
||||
#include "stage.hpp"
|
||||
#include "../doc/stage.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
/// \brief VerifyStage: make sure that spell records are internally consistent
|
||||
class SpellCheckStage : public Stage
|
||||
class SpellCheckStage : public CSMDoc::Stage
|
||||
{
|
||||
const CSMWorld::IdCollection<ESM::Spell>& mSpells;
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
|
||||
#include "stage.hpp"
|
||||
|
||||
CSMTools::Stage::~Stage() {}
|
|
@ -3,9 +3,8 @@
|
|||
|
||||
#include <QThreadPool>
|
||||
|
||||
#include "verifier.hpp"
|
||||
|
||||
#include "../doc/state.hpp"
|
||||
#include "../doc/operation.hpp"
|
||||
|
||||
#include "../world/data.hpp"
|
||||
#include "../world/universalid.hpp"
|
||||
|
@ -20,8 +19,9 @@
|
|||
#include "regioncheck.hpp"
|
||||
#include "birthsigncheck.hpp"
|
||||
#include "spellcheck.hpp"
|
||||
#include "referenceablecheck.hpp"
|
||||
|
||||
CSMTools::Operation *CSMTools::Tools::get (int type)
|
||||
CSMDoc::Operation *CSMTools::Tools::get (int type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
|
@ -31,19 +31,19 @@ CSMTools::Operation *CSMTools::Tools::get (int type)
|
|||
return 0;
|
||||
}
|
||||
|
||||
const CSMTools::Operation *CSMTools::Tools::get (int type) const
|
||||
const CSMDoc::Operation *CSMTools::Tools::get (int type) const
|
||||
{
|
||||
return const_cast<Tools *> (this)->get (type);
|
||||
}
|
||||
|
||||
CSMTools::Verifier *CSMTools::Tools::getVerifier()
|
||||
CSMDoc::Operation *CSMTools::Tools::getVerifier()
|
||||
{
|
||||
if (!mVerifier)
|
||||
{
|
||||
mVerifier = new Verifier;
|
||||
mVerifier = new CSMDoc::Operation (CSMDoc::State_Verifying, false);
|
||||
|
||||
connect (mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int)));
|
||||
connect (mVerifier, SIGNAL (finished()), this, SLOT (verifierDone()));
|
||||
connect (mVerifier, SIGNAL (done (int)), this, SIGNAL (done (int)));
|
||||
connect (mVerifier, SIGNAL (reportMessage (const QString&, int)),
|
||||
this, SLOT (verifierMessage (const QString&, int)));
|
||||
|
||||
|
@ -75,6 +75,8 @@ CSMTools::Verifier *CSMTools::Tools::getVerifier()
|
|||
mVerifier->appendStage (new BirthsignCheckStage (mData.getBirthsigns()));
|
||||
|
||||
mVerifier->appendStage (new SpellCheckStage (mData.getSpells()));
|
||||
|
||||
mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions()));
|
||||
}
|
||||
|
||||
return mVerifier;
|
||||
|
@ -103,7 +105,7 @@ CSMWorld::UniversalId CSMTools::Tools::runVerifier()
|
|||
|
||||
void CSMTools::Tools::abortOperation (int type)
|
||||
{
|
||||
if (Operation *operation = get (type))
|
||||
if (CSMDoc::Operation *operation = get (type))
|
||||
operation->abort();
|
||||
}
|
||||
|
||||
|
@ -118,7 +120,7 @@ int CSMTools::Tools::getRunningOperations() const
|
|||
int result = 0;
|
||||
|
||||
for (int i=0; sOperations[i]!=-1; ++i)
|
||||
if (const Operation *operation = get (sOperations[i]))
|
||||
if (const CSMDoc::Operation *operation = get (sOperations[i]))
|
||||
if (operation->isRunning())
|
||||
result |= sOperations[i];
|
||||
|
||||
|
@ -133,15 +135,11 @@ CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId&
|
|||
return mReports.at (id.getIndex());
|
||||
}
|
||||
|
||||
void CSMTools::Tools::verifierDone()
|
||||
{
|
||||
emit done (CSMDoc::State_Verifying);
|
||||
}
|
||||
|
||||
void CSMTools::Tools::verifierMessage (const QString& message, int type)
|
||||
{
|
||||
std::map<int, int>::iterator iter = mActiveReports.find (type);
|
||||
|
||||
if (iter!=mActiveReports.end())
|
||||
mReports[iter->second]->add (message.toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,10 +11,13 @@ namespace CSMWorld
|
|||
class UniversalId;
|
||||
}
|
||||
|
||||
namespace CSMDoc
|
||||
{
|
||||
class Operation;
|
||||
}
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
class Verifier;
|
||||
class Operation;
|
||||
class ReportModel;
|
||||
|
||||
class Tools : public QObject
|
||||
|
@ -22,7 +25,7 @@ namespace CSMTools
|
|||
Q_OBJECT
|
||||
|
||||
CSMWorld::Data& mData;
|
||||
Verifier *mVerifier;
|
||||
CSMDoc::Operation *mVerifier;
|
||||
std::map<int, ReportModel *> mReports;
|
||||
int mNextReportNumber;
|
||||
std::map<int, int> mActiveReports; // type, report number
|
||||
|
@ -31,12 +34,12 @@ namespace CSMTools
|
|||
Tools (const Tools&);
|
||||
Tools& operator= (const Tools&);
|
||||
|
||||
Verifier *getVerifier();
|
||||
CSMDoc::Operation *getVerifier();
|
||||
|
||||
Operation *get (int type);
|
||||
CSMDoc::Operation *get (int type);
|
||||
///< Returns a 0-pointer, if operation hasn't been used yet.
|
||||
|
||||
const Operation *get (int type) const;
|
||||
const CSMDoc::Operation *get (int type) const;
|
||||
///< Returns a 0-pointer, if operation hasn't been used yet.
|
||||
|
||||
public:
|
||||
|
@ -58,8 +61,6 @@ namespace CSMTools
|
|||
|
||||
private slots:
|
||||
|
||||
void verifierDone();
|
||||
|
||||
void verifierMessage (const QString& message, int type);
|
||||
|
||||
signals:
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
|
||||
#include "verifier.hpp"
|
||||
|
||||
#include "../doc/state.hpp"
|
||||
|
||||
CSMTools::Verifier::Verifier() : Operation (CSMDoc::State_Verifying)
|
||||
{}
|
|
@ -1,17 +0,0 @@
|
|||
#ifndef CSM_TOOLS_VERIFIER_H
|
||||
#define CSM_TOOLS_VERIFIER_H
|
||||
|
||||
#include "operation.hpp"
|
||||
|
||||
namespace CSMTools
|
||||
{
|
||||
class Verifier : public Operation
|
||||
{
|
||||
public:
|
||||
|
||||
Verifier();
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -51,6 +51,18 @@ namespace CSMWorld
|
|||
Collection (const Collection&);
|
||||
Collection& operator= (const Collection&);
|
||||
|
||||
protected:
|
||||
|
||||
const std::map<std::string, int>& getIdMap() const;
|
||||
|
||||
const std::vector<Record<ESXRecordT> >& getRecords() const;
|
||||
|
||||
bool reorderRowsImp (int baseIndex, const std::vector<int>& newOrder);
|
||||
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
|
||||
/// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).
|
||||
///
|
||||
/// \return Success?
|
||||
|
||||
public:
|
||||
|
||||
Collection();
|
||||
|
@ -86,6 +98,10 @@ namespace CSMWorld
|
|||
UniversalId::Type type = UniversalId::Type_None);
|
||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||
|
||||
virtual void cloneRecord(const std::string& origin,
|
||||
const std::string& destination,
|
||||
const UniversalId::Type type);
|
||||
|
||||
virtual int searchId (const std::string& id) const;
|
||||
////< Search record with \a id.
|
||||
/// \return index of record (if found) or -1 (not found)
|
||||
|
@ -104,7 +120,8 @@ namespace CSMWorld
|
|||
|
||||
virtual const Record<ESXRecordT>& getRecord (int index) const;
|
||||
|
||||
virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const;
|
||||
virtual int getAppendIndex (const std::string& id,
|
||||
UniversalId::Type type = UniversalId::Type_None) const;
|
||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||
|
||||
virtual std::vector<std::string> getIds (bool listDeleted = true) const;
|
||||
|
@ -112,12 +129,87 @@ namespace CSMWorld
|
|||
///
|
||||
/// \param listDeleted include deleted record in the list
|
||||
|
||||
virtual void insertRecord (const RecordBase& record, int index,
|
||||
UniversalId::Type type = UniversalId::Type_None);
|
||||
///< Insert record before index.
|
||||
///
|
||||
/// If the record type does not match, an exception is thrown.
|
||||
///
|
||||
/// If the index is invalid either generally (by being out of range) or for the particular
|
||||
/// record, an exception is thrown.
|
||||
|
||||
virtual bool reorderRows (int baseIndex, const std::vector<int>& newOrder);
|
||||
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
|
||||
/// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).
|
||||
///
|
||||
/// \return Success?
|
||||
|
||||
void addColumn (Column<ESXRecordT> *column);
|
||||
|
||||
void setRecord (int index, const Record<ESXRecordT>& record);
|
||||
///< \attention This function must not change the ID.
|
||||
};
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
const std::map<std::string, int>& Collection<ESXRecordT, IdAccessorT>::getIdMap() const
|
||||
{
|
||||
return mIndex;
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
const std::vector<Record<ESXRecordT> >& Collection<ESXRecordT, IdAccessorT>::getRecords() const
|
||||
{
|
||||
return mRecords;
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
bool Collection<ESXRecordT, IdAccessorT>::reorderRowsImp (int baseIndex,
|
||||
const std::vector<int>& newOrder)
|
||||
{
|
||||
if (!newOrder.empty())
|
||||
{
|
||||
int size = static_cast<int> (newOrder.size());
|
||||
|
||||
// check that all indices are present
|
||||
std::vector<int> test (newOrder);
|
||||
std::sort (test.begin(), test.end());
|
||||
if (*test.begin()!=0 || *--test.end()!=size-1)
|
||||
return false;
|
||||
|
||||
// reorder records
|
||||
std::vector<Record<ESXRecordT> > buffer (size);
|
||||
|
||||
for (int i=0; i<size; ++i)
|
||||
{
|
||||
buffer[newOrder[i]] = mRecords [baseIndex+i];
|
||||
buffer[newOrder[i]].setModified (buffer[newOrder[i]].get());
|
||||
}
|
||||
|
||||
std::copy (buffer.begin(), buffer.end(), mRecords.begin()+baseIndex);
|
||||
|
||||
// adjust index
|
||||
for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end();
|
||||
++iter)
|
||||
if (iter->second>=baseIndex && iter->second<baseIndex+size)
|
||||
iter->second = newOrder.at (iter->second-baseIndex)+baseIndex;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
void Collection<ESXRecordT, IdAccessorT>::cloneRecord(const std::string& origin,
|
||||
const std::string& destination,
|
||||
const UniversalId::Type type)
|
||||
{
|
||||
Record<ESXRecordT> copy;
|
||||
copy.mModified = getRecord(origin).get();
|
||||
copy.mState = RecordBase::State_ModifiedOnly;
|
||||
copy.get().mId = destination;
|
||||
|
||||
insertRecord(copy, getAppendIndex(destination, type));
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
Collection<ESXRecordT, IdAccessorT>::Collection()
|
||||
{}
|
||||
|
@ -142,8 +234,7 @@ namespace CSMWorld
|
|||
record2.mState = Record<ESXRecordT>::State_ModifiedOnly;
|
||||
record2.mModified = record;
|
||||
|
||||
mRecords.push_back (record2);
|
||||
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), mRecords.size()-1));
|
||||
insertRecord (record2, getAppendIndex (id));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -260,7 +351,12 @@ namespace CSMWorld
|
|||
ESXRecordT record;
|
||||
IdAccessorT().getId (record) = id;
|
||||
record.blank();
|
||||
add (record);
|
||||
|
||||
Record<ESXRecordT> record2;
|
||||
record2.mState = Record<ESXRecordT>::State_ModifiedOnly;
|
||||
record2.mModified = record;
|
||||
|
||||
insertRecord (record2, getAppendIndex (id, type), type);
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
|
@ -286,14 +382,14 @@ namespace CSMWorld
|
|||
void Collection<ESXRecordT, IdAccessorT>::appendRecord (const RecordBase& record,
|
||||
UniversalId::Type type)
|
||||
{
|
||||
mRecords.push_back (dynamic_cast<const Record<ESXRecordT>&> (record));
|
||||
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId (
|
||||
dynamic_cast<const Record<ESXRecordT>&> (record).get())),
|
||||
mRecords.size()-1));
|
||||
insertRecord (record,
|
||||
getAppendIndex (IdAccessorT().getId (
|
||||
dynamic_cast<const Record<ESXRecordT>&> (record).get()), type), type);
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
int Collection<ESXRecordT, IdAccessorT>::getAppendIndex (UniversalId::Type type) const
|
||||
int Collection<ESXRecordT, IdAccessorT>::getAppendIndex (const std::string& id,
|
||||
UniversalId::Type type) const
|
||||
{
|
||||
return static_cast<int> (mRecords.size());
|
||||
}
|
||||
|
@ -326,6 +422,29 @@ namespace CSMWorld
|
|||
return mRecords.at (index);
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
void Collection<ESXRecordT, IdAccessorT>::insertRecord (const RecordBase& record, int index,
|
||||
UniversalId::Type type)
|
||||
{
|
||||
if (index<0 || index>static_cast<int> (mRecords.size()))
|
||||
throw std::runtime_error ("index out of range");
|
||||
|
||||
const Record<ESXRecordT>& record2 = dynamic_cast<const Record<ESXRecordT>&> (record);
|
||||
|
||||
mRecords.insert (mRecords.begin()+index, record2);
|
||||
|
||||
if (index<static_cast<int> (mRecords.size())-1)
|
||||
{
|
||||
for (std::map<std::string, int>::iterator iter (mIndex.begin()); iter!=mIndex.end();
|
||||
++iter)
|
||||
if (iter->second>=index)
|
||||
++(iter->second);
|
||||
}
|
||||
|
||||
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId (
|
||||
record2.get())), index));
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
void Collection<ESXRecordT, IdAccessorT>::setRecord (int index, const Record<ESXRecordT>& record)
|
||||
{
|
||||
|
@ -334,6 +453,12 @@ namespace CSMWorld
|
|||
|
||||
mRecords.at (index) = record;
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
bool Collection<ESXRecordT, IdAccessorT>::reorderRows (int baseIndex, const std::vector<int>& newOrder)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,31 @@
|
|||
|
||||
#include "collectionbase.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "columnbase.hpp"
|
||||
|
||||
CSMWorld::CollectionBase::CollectionBase() {}
|
||||
|
||||
CSMWorld::CollectionBase::~CollectionBase() {}
|
||||
|
||||
int CSMWorld::CollectionBase::searchColumnIndex (Columns::ColumnId id) const
|
||||
{
|
||||
int columns = getColumns();
|
||||
|
||||
for (int i=0; i<columns; ++i)
|
||||
if (getColumn (i).mColumnId==id)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int CSMWorld::CollectionBase::findColumnIndex (Columns::ColumnId id) const
|
||||
{
|
||||
int index = searchColumnIndex (id);
|
||||
|
||||
if (index==-1)
|
||||
throw std::logic_error ("invalid column index");
|
||||
|
||||
return index;
|
||||
}
|
|
@ -2,8 +2,10 @@
|
|||
#define CSM_WOLRD_COLLECTIONBASE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "universalid.hpp"
|
||||
#include "columns.hpp"
|
||||
|
||||
class QVariant;
|
||||
|
||||
|
@ -72,17 +74,35 @@ namespace CSMWorld
|
|||
UniversalId::Type type = UniversalId::Type_None) = 0;
|
||||
///< If the record type does not match, an exception is thrown.
|
||||
|
||||
virtual void cloneRecord(const std::string& origin,
|
||||
const std::string& destination,
|
||||
const UniversalId::Type type) = 0;
|
||||
|
||||
virtual const RecordBase& getRecord (const std::string& id) const = 0;
|
||||
|
||||
virtual const RecordBase& getRecord (int index) const = 0;
|
||||
|
||||
virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const = 0;
|
||||
virtual int getAppendIndex (const std::string& id,
|
||||
UniversalId::Type type = UniversalId::Type_None) const = 0;
|
||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||
|
||||
virtual std::vector<std::string> getIds (bool listDeleted = true) const = 0;
|
||||
///< Return a sorted collection of all IDs
|
||||
///
|
||||
/// \param listDeleted include deleted record in the list
|
||||
|
||||
virtual bool reorderRows (int baseIndex, const std::vector<int>& newOrder) = 0;
|
||||
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
|
||||
/// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).
|
||||
///
|
||||
/// \return Success?
|
||||
|
||||
int searchColumnIndex (Columns::ColumnId id) const;
|
||||
///< Return index of column with the given \a id. If no such column exists, -1 is returned.
|
||||
|
||||
int findColumnIndex (Columns::ColumnId id) const;
|
||||
///< Return index of column with the given \a id. If no such column exists, an exception is
|
||||
/// thrown.
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,10 @@ namespace CSMWorld
|
|||
Display_CreatureType,
|
||||
Display_WeaponType,
|
||||
Display_RecordState,
|
||||
Display_RefRecordType
|
||||
Display_RefRecordType,
|
||||
Display_DialogueType,
|
||||
Display_QuestStatusType,
|
||||
Display_Gender
|
||||
};
|
||||
|
||||
int mColumnId;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "columnbase.hpp"
|
||||
#include "columns.hpp"
|
||||
#include "info.hpp"
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
|
@ -814,14 +815,14 @@ namespace CSMWorld
|
|||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return QString::fromUtf8 (record.get().mCellId.c_str());
|
||||
return QString::fromUtf8 (record.get().mCell.c_str());
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
|
||||
record2.mCellId = data.toString().toUtf8().constData();
|
||||
record2.mCell = data.toString().toUtf8().constData();
|
||||
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
@ -1217,6 +1218,37 @@ namespace CSMWorld
|
|||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct ScopeColumn : public Column<ESXRecordT>
|
||||
{
|
||||
ScopeColumn()
|
||||
: Column<ESXRecordT> (Columns::ColumnId_Scope, ColumnBase::Display_Integer, 0)
|
||||
{}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return static_cast<int> (record.get().mScope);
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
record2.mScope = static_cast<CSMFilter::Filter::Scope> (data.toInt());
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool isUserEditable() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct PosColumn : public Column<ESXRecordT>
|
||||
{
|
||||
|
@ -1284,6 +1316,373 @@ namespace CSMWorld
|
|||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct DialogueTypeColumn : public Column<ESXRecordT>
|
||||
{
|
||||
DialogueTypeColumn (bool hidden = false)
|
||||
: Column<ESXRecordT> (Columns::ColumnId_DialogueType, ColumnBase::Display_DialogueType,
|
||||
hidden ? 0 : ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue)
|
||||
{}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return static_cast<int> (record.get().mType);
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
|
||||
record2.mType = data.toInt();
|
||||
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool isUserEditable() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct QuestStatusTypeColumn : public Column<ESXRecordT>
|
||||
{
|
||||
QuestStatusTypeColumn()
|
||||
: Column<ESXRecordT> (Columns::ColumnId_QuestStatusType, ColumnBase::Display_QuestStatusType)
|
||||
{}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return static_cast<int> (record.get().mQuestStatus);
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
|
||||
record2.mQuestStatus = static_cast<Info::QuestStatus> (data.toInt());
|
||||
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct QuestDescriptionColumn : public Column<ESXRecordT>
|
||||
{
|
||||
QuestDescriptionColumn() : Column<ESXRecordT> (Columns::ColumnId_QuestDescription, ColumnBase::Display_String) {}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return QString::fromUtf8 (record.get().mResponse.c_str());
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
|
||||
record2.mResponse = data.toString().toUtf8().constData();
|
||||
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct QuestIndexColumn : public Column<ESXRecordT>
|
||||
{
|
||||
QuestIndexColumn()
|
||||
: Column<ESXRecordT> (Columns::ColumnId_QuestIndex, ColumnBase::Display_Integer)
|
||||
{}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return record.get().mData.mDisposition;
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
record2.mData.mDisposition = data.toInt();
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct TopicColumn : public Column<ESXRecordT>
|
||||
{
|
||||
TopicColumn (bool journal) : Column<ESXRecordT> (journal ? Columns::ColumnId_Journal : Columns::ColumnId_Topic, ColumnBase::Display_String) {}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return QString::fromUtf8 (record.get().mTopicId.c_str());
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
|
||||
record2.mTopicId = data.toString().toUtf8().constData();
|
||||
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool isUserEditable() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct ActorColumn : public Column<ESXRecordT>
|
||||
{
|
||||
ActorColumn() : Column<ESXRecordT> (Columns::ColumnId_Actor, ColumnBase::Display_String) {}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return QString::fromUtf8 (record.get().mActor.c_str());
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
|
||||
record2.mActor = data.toString().toUtf8().constData();
|
||||
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct RaceColumn : public Column<ESXRecordT>
|
||||
{
|
||||
RaceColumn() : Column<ESXRecordT> (Columns::ColumnId_Race, ColumnBase::Display_String) {}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return QString::fromUtf8 (record.get().mRace.c_str());
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
|
||||
record2.mRace = data.toString().toUtf8().constData();
|
||||
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct ClassColumn : public Column<ESXRecordT>
|
||||
{
|
||||
ClassColumn() : Column<ESXRecordT> (Columns::ColumnId_Class, ColumnBase::Display_String) {}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return QString::fromUtf8 (record.get().mClass.c_str());
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
|
||||
record2.mClass = data.toString().toUtf8().constData();
|
||||
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct PcFactionColumn : public Column<ESXRecordT>
|
||||
{
|
||||
PcFactionColumn() : Column<ESXRecordT> (Columns::ColumnId_PcFaction, ColumnBase::Display_String) {}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return QString::fromUtf8 (record.get().mPcFaction.c_str());
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
|
||||
record2.mPcFaction = data.toString().toUtf8().constData();
|
||||
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct ResponseColumn : public Column<ESXRecordT>
|
||||
{
|
||||
ResponseColumn() : Column<ESXRecordT> (Columns::ColumnId_Response, ColumnBase::Display_String) {}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return QString::fromUtf8 (record.get().mResponse.c_str());
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
|
||||
record2.mResponse = data.toString().toUtf8().constData();
|
||||
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct DispositionColumn : public Column<ESXRecordT>
|
||||
{
|
||||
DispositionColumn()
|
||||
: Column<ESXRecordT> (Columns::ColumnId_Disposition, ColumnBase::Display_Integer)
|
||||
{}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return record.get().mData.mDisposition;
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
record2.mData.mDisposition = data.toInt();
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct RankColumn : public Column<ESXRecordT>
|
||||
{
|
||||
RankColumn()
|
||||
: Column<ESXRecordT> (Columns::ColumnId_Rank, ColumnBase::Display_Integer)
|
||||
{}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return static_cast<int> (record.get().mData.mRank);
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
record2.mData.mRank = static_cast<signed char> (data.toInt());
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct PcRankColumn : public Column<ESXRecordT>
|
||||
{
|
||||
PcRankColumn()
|
||||
: Column<ESXRecordT> (Columns::ColumnId_PcRank, ColumnBase::Display_Integer)
|
||||
{}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return static_cast<int> (record.get().mData.mPCrank);
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
record2.mData.mPCrank = static_cast<signed char> (data.toInt());
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ESXRecordT>
|
||||
struct GenderColumn : public Column<ESXRecordT>
|
||||
{
|
||||
GenderColumn()
|
||||
: Column<ESXRecordT> (Columns::ColumnId_Gender, ColumnBase::Display_Gender)
|
||||
{}
|
||||
|
||||
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||
{
|
||||
return static_cast<int> (record.get().mData.mGender);
|
||||
}
|
||||
|
||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||
{
|
||||
ESXRecordT record2 = record.get();
|
||||
|
||||
record2.mData.mGender = data.toInt();
|
||||
|
||||
record.setModified (record2);
|
||||
}
|
||||
|
||||
virtual bool isEditable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -159,6 +159,20 @@ namespace CSMWorld
|
|||
{ ColumnId_DoorPositionXRot, "Teleport Rot X" },
|
||||
{ ColumnId_DoorPositionYRot, "Teleport Rot Y" },
|
||||
{ ColumnId_DoorPositionZRot, "Teleport Rot Z" },
|
||||
{ ColumnId_DialogueType, "Dialogue Type" },
|
||||
{ ColumnId_QuestIndex, "Quest Index" },
|
||||
{ ColumnId_QuestStatusType, "Quest Status" },
|
||||
{ ColumnId_QuestDescription, "Quest Description" },
|
||||
{ ColumnId_Topic, "Topic" },
|
||||
{ ColumnId_Journal, "Journal" },
|
||||
{ ColumnId_Actor, "Actor" },
|
||||
{ ColumnId_PcFaction, "PC Faction" },
|
||||
{ ColumnId_Response, "Response" },
|
||||
{ ColumnId_Disposition, "Disposition" },
|
||||
{ ColumnId_Rank, "Rank" },
|
||||
{ ColumnId_Gender, "Gender" },
|
||||
{ ColumnId_PcRank, "PC Rank" },
|
||||
{ ColumnId_Scope, "Scope", },
|
||||
|
||||
{ ColumnId_UseValue1, "Use value 1" },
|
||||
{ ColumnId_UseValue2, "Use value 2" },
|
||||
|
@ -206,7 +220,7 @@ int CSMWorld::Columns::getId (const std::string& name)
|
|||
std::string name2 = Misc::StringUtils::lowerCase (name);
|
||||
|
||||
for (int i=0; sNames[i].mName; ++i)
|
||||
if (name2==Misc::StringUtils::lowerCase (sNames[i].mName))
|
||||
if (Misc::StringUtils::ciEqual(sNames[i].mName, name2))
|
||||
return sNames[i].mId;
|
||||
|
||||
return -1;
|
||||
|
@ -249,7 +263,7 @@ namespace
|
|||
|
||||
static const char *sCreatureTypes[] =
|
||||
{
|
||||
"Creature", "Deadra", "Undead", "Humanoid", 0
|
||||
"Creature", "Daedra", "Undead", "Humanoid", 0
|
||||
};
|
||||
|
||||
static const char *sWeaponTypes[] =
|
||||
|
@ -269,6 +283,21 @@ namespace
|
|||
"unknown", "none", "short", "integer", "long", "float", "string", 0
|
||||
};
|
||||
|
||||
static const char *sDialogueTypeEnums[] =
|
||||
{
|
||||
"Topic", "Voice", "Greeting", "Persuasion", 0
|
||||
};
|
||||
|
||||
static const char *sQuestStatusTypes[] =
|
||||
{
|
||||
"None", "Name", "Finished", "Restart", 0
|
||||
};
|
||||
|
||||
static const char *sGenderEnums[] =
|
||||
{
|
||||
"Male", "Female", 0
|
||||
};
|
||||
|
||||
const char **getEnumNames (CSMWorld::Columns::ColumnId column)
|
||||
{
|
||||
switch (column)
|
||||
|
@ -283,6 +312,9 @@ namespace
|
|||
case CSMWorld::Columns::ColumnId_WeaponType: return sWeaponTypes;
|
||||
case CSMWorld::Columns::ColumnId_Modification: return sModificationEnums;
|
||||
case CSMWorld::Columns::ColumnId_ValueType: return sVarTypeEnums;
|
||||
case CSMWorld::Columns::ColumnId_DialogueType: return sDialogueTypeEnums;
|
||||
case CSMWorld::Columns::ColumnId_QuestStatusType: return sQuestStatusTypes;
|
||||
case CSMWorld::Columns::ColumnId_Gender: return sGenderEnums;
|
||||
|
||||
default: return 0;
|
||||
}
|
||||
|
@ -310,4 +342,4 @@ std::vector<std::string> CSMWorld::Columns::getEnums (ColumnId column)
|
|||
}
|
||||
|
||||
return enums;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -152,6 +152,20 @@ namespace CSMWorld
|
|||
ColumnId_DoorPositionXRot = 139,
|
||||
ColumnId_DoorPositionYRot = 140,
|
||||
ColumnId_DoorPositionZRot = 141,
|
||||
ColumnId_DialogueType = 142,
|
||||
ColumnId_QuestIndex = 143,
|
||||
ColumnId_QuestStatusType = 144,
|
||||
ColumnId_QuestDescription = 145,
|
||||
ColumnId_Topic = 146,
|
||||
ColumnId_Journal = 147,
|
||||
ColumnId_Actor = 148,
|
||||
ColumnId_PcFaction = 149,
|
||||
ColumnId_Response = 150,
|
||||
ColumnId_Disposition = 151,
|
||||
ColumnId_Rank = 152,
|
||||
ColumnId_Gender = 153,
|
||||
ColumnId_PcRank = 154,
|
||||
ColumnId_Scope = 155,
|
||||
|
||||
// Allocated to a separate value range, so we don't get a collision should we ever need
|
||||
// to extend the number of use values.
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
#include <QAbstractItemModel>
|
||||
|
||||
#include "idtable.hpp"
|
||||
#include "idtable.hpp"
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
|
||||
const QVariant& new_, QUndoCommand *parent)
|
||||
const QVariant& new_, QUndoCommand* parent)
|
||||
: QUndoCommand (parent), mModel (model), mIndex (index), mNew (new_)
|
||||
{
|
||||
mOld = mModel.data (mIndex, Qt::EditRole);
|
||||
|
@ -25,7 +25,7 @@ void CSMWorld::ModifyCommand::undo()
|
|||
mModel.setData (mIndex, mOld);
|
||||
}
|
||||
|
||||
CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent)
|
||||
CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand* parent)
|
||||
: QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None)
|
||||
{
|
||||
setText (("Create record " + id).c_str());
|
||||
|
@ -54,7 +54,7 @@ void CSMWorld::CreateCommand::undo()
|
|||
mModel.removeRow (mModel.getModelIndex (mId, 0).row());
|
||||
}
|
||||
|
||||
CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand *parent)
|
||||
CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand* parent)
|
||||
: QUndoCommand (parent), mModel (model), mId (id), mOld (0)
|
||||
{
|
||||
setText (("Revert record " + id).c_str());
|
||||
|
@ -89,7 +89,7 @@ void CSMWorld::RevertCommand::undo()
|
|||
mModel.setRecord (mId, *mOld);
|
||||
}
|
||||
|
||||
CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand *parent)
|
||||
CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand* parent)
|
||||
: QUndoCommand (parent), mModel (model), mId (id), mOld (0)
|
||||
{
|
||||
setText (("Delete record " + id).c_str());
|
||||
|
@ -122,4 +122,53 @@ void CSMWorld::DeleteCommand::redo()
|
|||
void CSMWorld::DeleteCommand::undo()
|
||||
{
|
||||
mModel.setRecord (mId, *mOld);
|
||||
}
|
||||
|
||||
|
||||
CSMWorld::ReorderRowsCommand::ReorderRowsCommand (IdTable& model, int baseIndex,
|
||||
const std::vector<int>& newOrder)
|
||||
: mModel (model), mBaseIndex (baseIndex), mNewOrder (newOrder)
|
||||
{}
|
||||
|
||||
void CSMWorld::ReorderRowsCommand::redo()
|
||||
{
|
||||
mModel.reorderRows (mBaseIndex, mNewOrder);
|
||||
}
|
||||
|
||||
void CSMWorld::ReorderRowsCommand::undo()
|
||||
{
|
||||
int size = static_cast<int> (mNewOrder.size());
|
||||
std::vector<int> reverse (size);
|
||||
|
||||
for (int i=0; i< size; ++i)
|
||||
reverse.at (mNewOrder[i]) = i;
|
||||
|
||||
mModel.reorderRows (mBaseIndex, reverse);
|
||||
}
|
||||
|
||||
CSMWorld::CloneCommand::CloneCommand (CSMWorld::IdTable& model,
|
||||
const std::string& idOrigin,
|
||||
const std::string& IdDestination,
|
||||
const CSMWorld::UniversalId::Type type,
|
||||
QUndoCommand* parent) :
|
||||
QUndoCommand (parent),
|
||||
mModel (model),
|
||||
mIdOrigin (idOrigin),
|
||||
mIdDestination (Misc::StringUtils::lowerCase (IdDestination)),
|
||||
mType (type)
|
||||
{
|
||||
setText ( ("Clone record " + idOrigin + " to the " + IdDestination).c_str());
|
||||
}
|
||||
|
||||
void CSMWorld::CloneCommand::redo()
|
||||
{
|
||||
mModel.cloneRecord (mIdOrigin, mIdDestination, mType);
|
||||
|
||||
for (std::map<int, QVariant>::const_iterator iter (mValues.begin()); iter != mValues.end(); ++iter)
|
||||
mModel.setData (mModel.getModelIndex (mIdDestination, iter->first), iter->second);
|
||||
}
|
||||
|
||||
void CSMWorld::CloneCommand::undo()
|
||||
{
|
||||
mModel.removeRow (mModel.getModelIndex (mIdDestination, 0).row());
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include <QVariant>
|
||||
#include <QUndoCommand>
|
||||
|
@ -38,6 +39,26 @@ namespace CSMWorld
|
|||
virtual void undo();
|
||||
};
|
||||
|
||||
class CloneCommand : public QUndoCommand
|
||||
{
|
||||
IdTable& mModel;
|
||||
std::string mIdOrigin;
|
||||
std::string mIdDestination;
|
||||
UniversalId::Type mType;
|
||||
std::map<int, QVariant> mValues;
|
||||
|
||||
public:
|
||||
|
||||
CloneCommand (IdTable& model, const std::string& idOrigin,
|
||||
const std::string& IdDestination,
|
||||
const UniversalId::Type type,
|
||||
QUndoCommand* parent = 0);
|
||||
|
||||
virtual void redo();
|
||||
|
||||
virtual void undo();
|
||||
};
|
||||
|
||||
class CreateCommand : public QUndoCommand
|
||||
{
|
||||
IdTable& mModel;
|
||||
|
@ -99,6 +120,21 @@ namespace CSMWorld
|
|||
|
||||
virtual void undo();
|
||||
};
|
||||
|
||||
class ReorderRowsCommand : public QUndoCommand
|
||||
{
|
||||
IdTable& mModel;
|
||||
int mBaseIndex;
|
||||
std::vector<int> mNewOrder;
|
||||
|
||||
public:
|
||||
|
||||
ReorderRowsCommand (IdTable& model, int baseIndex, const std::vector<int>& newOrder);
|
||||
|
||||
virtual void redo();
|
||||
|
||||
virtual void undo();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -44,6 +44,17 @@ void CSMWorld::Data::appendIds (std::vector<std::string>& ids, const CollectionB
|
|||
ids.insert (ids.end(), ids2.begin(), ids2.end());
|
||||
}
|
||||
|
||||
int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collection)
|
||||
{
|
||||
int number = 0;
|
||||
|
||||
for (int i=0; i<collection.getSize(); ++i)
|
||||
if (collection.getRecord (i).mState==state)
|
||||
++number;
|
||||
|
||||
return number;
|
||||
}
|
||||
|
||||
CSMWorld::Data::Data() : mRefs (mCells)
|
||||
{
|
||||
mGlobals.addColumn (new StringIdColumn<ESM::Global>);
|
||||
|
@ -141,6 +152,41 @@ CSMWorld::Data::Data() : mRefs (mCells)
|
|||
mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_StarterSpell, 0x2));
|
||||
mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_AlwaysSucceeds, 0x4));
|
||||
|
||||
mTopics.addColumn (new StringIdColumn<ESM::Dialogue>);
|
||||
mTopics.addColumn (new RecordStateColumn<ESM::Dialogue>);
|
||||
mTopics.addColumn (new FixedRecordTypeColumn<ESM::Dialogue> (UniversalId::Type_Topic));
|
||||
mTopics.addColumn (new DialogueTypeColumn<ESM::Dialogue>);
|
||||
|
||||
mJournals.addColumn (new StringIdColumn<ESM::Dialogue>);
|
||||
mJournals.addColumn (new RecordStateColumn<ESM::Dialogue>);
|
||||
mJournals.addColumn (new FixedRecordTypeColumn<ESM::Dialogue> (UniversalId::Type_Journal));
|
||||
mJournals.addColumn (new DialogueTypeColumn<ESM::Dialogue> (true));
|
||||
|
||||
mTopicInfos.addColumn (new StringIdColumn<Info> (true));
|
||||
mTopicInfos.addColumn (new RecordStateColumn<Info>);
|
||||
mTopicInfos.addColumn (new FixedRecordTypeColumn<Info> (UniversalId::Type_TopicInfo));
|
||||
mTopicInfos.addColumn (new TopicColumn<Info> (false));
|
||||
mTopicInfos.addColumn (new ActorColumn<Info>);
|
||||
mTopicInfos.addColumn (new RaceColumn<Info>);
|
||||
mTopicInfos.addColumn (new ClassColumn<Info>);
|
||||
mTopicInfos.addColumn (new FactionColumn<Info>);
|
||||
mTopicInfos.addColumn (new CellColumn<Info>);
|
||||
mTopicInfos.addColumn (new DispositionColumn<Info>);
|
||||
mTopicInfos.addColumn (new RankColumn<Info>);
|
||||
mTopicInfos.addColumn (new GenderColumn<Info>);
|
||||
mTopicInfos.addColumn (new PcFactionColumn<Info>);
|
||||
mTopicInfos.addColumn (new PcRankColumn<Info>);
|
||||
mTopicInfos.addColumn (new SoundFileColumn<Info>);
|
||||
mTopicInfos.addColumn (new ResponseColumn<Info>);
|
||||
|
||||
mJournalInfos.addColumn (new StringIdColumn<Info> (true));
|
||||
mJournalInfos.addColumn (new RecordStateColumn<Info>);
|
||||
mJournalInfos.addColumn (new FixedRecordTypeColumn<Info> (UniversalId::Type_Journal));
|
||||
mJournalInfos.addColumn (new TopicColumn<Info> (true));
|
||||
mJournalInfos.addColumn (new QuestStatusTypeColumn<Info>);
|
||||
mJournalInfos.addColumn (new QuestIndexColumn<Info>);
|
||||
mJournalInfos.addColumn (new QuestDescriptionColumn<Info>);
|
||||
|
||||
mCells.addColumn (new StringIdColumn<Cell>);
|
||||
mCells.addColumn (new RecordStateColumn<Cell>);
|
||||
mCells.addColumn (new FixedRecordTypeColumn<Cell> (UniversalId::Type_Cell));
|
||||
|
@ -152,6 +198,7 @@ CSMWorld::Data::Data() : mRefs (mCells)
|
|||
|
||||
mRefs.addColumn (new StringIdColumn<CellRef> (true));
|
||||
mRefs.addColumn (new RecordStateColumn<CellRef>);
|
||||
mRefs.addColumn (new FixedRecordTypeColumn<CellRef> (UniversalId::Type_Reference));
|
||||
mRefs.addColumn (new CellColumn<CellRef>);
|
||||
mRefs.addColumn (new IdColumn<CellRef>);
|
||||
mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 0, false));
|
||||
|
@ -182,8 +229,10 @@ CSMWorld::Data::Data() : mRefs (mCells)
|
|||
|
||||
mFilters.addColumn (new StringIdColumn<CSMFilter::Filter>);
|
||||
mFilters.addColumn (new RecordStateColumn<CSMFilter::Filter>);
|
||||
mFilters.addColumn (new FixedRecordTypeColumn<CSMFilter::Filter> (UniversalId::Type_Filter));
|
||||
mFilters.addColumn (new FilterColumn<CSMFilter::Filter>);
|
||||
mFilters.addColumn (new DescriptionColumn<CSMFilter::Filter>);
|
||||
mFilters.addColumn (new ScopeColumn<CSMFilter::Filter>);
|
||||
|
||||
addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global);
|
||||
addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst);
|
||||
|
@ -196,6 +245,10 @@ CSMWorld::Data::Data() : mRefs (mCells)
|
|||
addModel (new IdTable (&mRegions), UniversalId::Type_Regions, UniversalId::Type_Region);
|
||||
addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsigns, UniversalId::Type_Birthsign);
|
||||
addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell);
|
||||
addModel (new IdTable (&mTopics), UniversalId::Type_Topics, UniversalId::Type_Topic);
|
||||
addModel (new IdTable (&mJournals), UniversalId::Type_Journals, UniversalId::Type_Journal);
|
||||
addModel (new IdTable (&mTopicInfos), UniversalId::Type_TopicInfos, UniversalId::Type_TopicInfo);
|
||||
addModel (new IdTable (&mJournalInfos), UniversalId::Type_JournalInfos, UniversalId::Type_JournalInfo);
|
||||
addModel (new IdTable (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell);
|
||||
addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables,
|
||||
UniversalId::Type_Referenceable);
|
||||
|
@ -319,6 +372,47 @@ CSMWorld::IdCollection<ESM::Spell>& CSMWorld::Data::getSpells()
|
|||
return mSpells;
|
||||
}
|
||||
|
||||
|
||||
const CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getTopics() const
|
||||
{
|
||||
return mTopics;
|
||||
}
|
||||
|
||||
CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getTopics()
|
||||
{
|
||||
return mTopics;
|
||||
}
|
||||
|
||||
const CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getJournals() const
|
||||
{
|
||||
return mJournals;
|
||||
}
|
||||
|
||||
CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getJournals()
|
||||
{
|
||||
return mJournals;
|
||||
}
|
||||
|
||||
const CSMWorld::InfoCollection& CSMWorld::Data::getTopicInfos() const
|
||||
{
|
||||
return mTopicInfos;
|
||||
}
|
||||
|
||||
CSMWorld::InfoCollection& CSMWorld::Data::getTopicInfos()
|
||||
{
|
||||
return mTopicInfos;
|
||||
}
|
||||
|
||||
const CSMWorld::InfoCollection& CSMWorld::Data::getJournalInfos() const
|
||||
{
|
||||
return mJournalInfos;
|
||||
}
|
||||
|
||||
CSMWorld::InfoCollection& CSMWorld::Data::getJournalInfos()
|
||||
{
|
||||
return mJournalInfos;
|
||||
}
|
||||
|
||||
const CSMWorld::IdCollection<CSMWorld::Cell>& CSMWorld::Data::getCells() const
|
||||
{
|
||||
return mCells;
|
||||
|
@ -387,7 +481,7 @@ void CSMWorld::Data::merge()
|
|||
mGlobals.merge();
|
||||
}
|
||||
|
||||
void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
|
||||
void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base, bool project)
|
||||
{
|
||||
ESM::ESMReader reader;
|
||||
|
||||
|
@ -397,6 +491,11 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
|
|||
|
||||
reader.open (path.string());
|
||||
|
||||
const ESM::Dialogue *dialogue = 0;
|
||||
|
||||
mAuthor = reader.getAuthor();
|
||||
mDescription = reader.getDesc();
|
||||
|
||||
// Note: We do not need to send update signals here, because at this point the model is not connected
|
||||
// to any view.
|
||||
while (reader.hasMoreRecs())
|
||||
|
@ -447,9 +546,80 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
|
|||
case ESM::REC_STAT: mReferenceables.load (reader, base, UniversalId::Type_Static); break;
|
||||
case ESM::REC_WEAP: mReferenceables.load (reader, base, UniversalId::Type_Weapon); break;
|
||||
|
||||
case ESM::REC_DIAL:
|
||||
{
|
||||
std::string id = reader.getHNOString ("NAME");
|
||||
|
||||
ESM::Dialogue record;
|
||||
record.mId = id;
|
||||
record.load (reader);
|
||||
|
||||
if (record.mType==ESM::Dialogue::Journal)
|
||||
{
|
||||
mJournals.load (record, base);
|
||||
dialogue = &mJournals.getRecord (id).get();
|
||||
}
|
||||
else if (record.mType==ESM::Dialogue::Deleted)
|
||||
{
|
||||
dialogue = 0; // record vector can be shuffled around which would make pointer
|
||||
// to record invalid
|
||||
|
||||
if (mJournals.tryDelete (id))
|
||||
{
|
||||
/// \todo handle info records
|
||||
}
|
||||
else if (mTopics.tryDelete (id))
|
||||
{
|
||||
/// \todo handle info records
|
||||
}
|
||||
else
|
||||
{
|
||||
/// \todo report deletion of non-existing record
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mTopics.load (record, base);
|
||||
dialogue = &mTopics.getRecord (id).get();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ESM::REC_INFO:
|
||||
{
|
||||
if (!dialogue)
|
||||
{
|
||||
/// \todo INFO record without matching DIAL record -> report to user
|
||||
reader.skipRecord();
|
||||
break;
|
||||
}
|
||||
|
||||
if (dialogue->mType==ESM::Dialogue::Journal)
|
||||
mJournalInfos.load (reader, base, *dialogue);
|
||||
else
|
||||
mTopicInfos.load (reader, base, *dialogue);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ESM::REC_FILT:
|
||||
|
||||
if (project)
|
||||
{
|
||||
mFilters.load (reader, base);
|
||||
mFilters.setData (mFilters.getSize()-1,
|
||||
mFilters.findColumnIndex (CSMWorld::Columns::ColumnId_Scope),
|
||||
static_cast<int> (CSMFilter::Filter::Scope_Project));
|
||||
break;
|
||||
}
|
||||
|
||||
// fall through (filter record in a content file is an error with format 0)
|
||||
|
||||
default:
|
||||
|
||||
/// \todo throw an exception instead, once all records are implemented
|
||||
/// or maybe report error and continue?
|
||||
reader.skipRecord();
|
||||
}
|
||||
}
|
||||
|
@ -469,10 +639,50 @@ bool CSMWorld::Data::hasId (const std::string& id) const
|
|||
getRegions().searchId (id)!=-1 ||
|
||||
getBirthsigns().searchId (id)!=-1 ||
|
||||
getSpells().searchId (id)!=-1 ||
|
||||
getTopics().searchId (id)!=-1 ||
|
||||
getJournals().searchId (id)!=-1 ||
|
||||
getCells().searchId (id)!=-1 ||
|
||||
getReferenceables().searchId (id)!=-1;
|
||||
}
|
||||
|
||||
int CSMWorld::Data::count (RecordBase::State state) const
|
||||
{
|
||||
return
|
||||
count (state, mGlobals) +
|
||||
count (state, mGmsts) +
|
||||
count (state, mSkills) +
|
||||
count (state, mClasses) +
|
||||
count (state, mFactions) +
|
||||
count (state, mRaces) +
|
||||
count (state, mSounds) +
|
||||
count (state, mScripts) +
|
||||
count (state, mRegions) +
|
||||
count (state, mBirthsigns) +
|
||||
count (state, mSpells) +
|
||||
count (state, mCells) +
|
||||
count (state, mReferenceables);
|
||||
}
|
||||
|
||||
void CSMWorld::Data::setDescription (const std::string& description)
|
||||
{
|
||||
mDescription = description;
|
||||
}
|
||||
|
||||
std::string CSMWorld::Data::getDescription() const
|
||||
{
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
void CSMWorld::Data::setAuthor (const std::string& author)
|
||||
{
|
||||
mAuthor = author;
|
||||
}
|
||||
|
||||
std::string CSMWorld::Data::getAuthor() const
|
||||
{
|
||||
return mAuthor;
|
||||
}
|
||||
|
||||
std::vector<std::string> CSMWorld::Data::getIds (bool listDeleted) const
|
||||
{
|
||||
std::vector<std::string> ids;
|
||||
|
@ -487,6 +697,8 @@ std::vector<std::string> CSMWorld::Data::getIds (bool listDeleted) const
|
|||
appendIds (ids, mRegions, listDeleted);
|
||||
appendIds (ids, mBirthsigns, listDeleted);
|
||||
appendIds (ids, mSpells, listDeleted);
|
||||
appendIds (ids, mTopics, listDeleted);
|
||||
appendIds (ids, mJournals, listDeleted);
|
||||
appendIds (ids, mCells, listDeleted);
|
||||
appendIds (ids, mReferenceables, listDeleted);
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <components/esm/loadregn.hpp>
|
||||
#include <components/esm/loadbsgn.hpp>
|
||||
#include <components/esm/loadspel.hpp>
|
||||
#include <components/esm/loaddial.hpp>
|
||||
|
||||
#include "../filter/filter.hpp"
|
||||
|
||||
|
@ -28,6 +29,7 @@
|
|||
#include "cell.hpp"
|
||||
#include "refidcollection.hpp"
|
||||
#include "refcollection.hpp"
|
||||
#include "infocollection.hpp"
|
||||
|
||||
class QAbstractItemModel;
|
||||
|
||||
|
@ -48,12 +50,18 @@ namespace CSMWorld
|
|||
IdCollection<ESM::Region> mRegions;
|
||||
IdCollection<ESM::BirthSign> mBirthsigns;
|
||||
IdCollection<ESM::Spell> mSpells;
|
||||
IdCollection<ESM::Dialogue> mTopics;
|
||||
IdCollection<ESM::Dialogue> mJournals;
|
||||
InfoCollection mTopicInfos;
|
||||
InfoCollection mJournalInfos;
|
||||
IdCollection<Cell> mCells;
|
||||
RefIdCollection mReferenceables;
|
||||
RefCollection mRefs;
|
||||
IdCollection<CSMFilter::Filter> mFilters;
|
||||
std::vector<QAbstractItemModel *> mModels;
|
||||
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
|
||||
std::string mAuthor;
|
||||
std::string mDescription;
|
||||
|
||||
// not implemented
|
||||
Data (const Data&);
|
||||
|
@ -66,6 +74,8 @@ namespace CSMWorld
|
|||
bool listDeleted);
|
||||
///< Append all IDs from collection to \a ids.
|
||||
|
||||
static int count (RecordBase::State state, const CollectionBase& collection);
|
||||
|
||||
public:
|
||||
|
||||
Data();
|
||||
|
@ -116,6 +126,22 @@ namespace CSMWorld
|
|||
|
||||
IdCollection<ESM::Spell>& getSpells();
|
||||
|
||||
const IdCollection<ESM::Dialogue>& getTopics() const;
|
||||
|
||||
IdCollection<ESM::Dialogue>& getTopics();
|
||||
|
||||
const IdCollection<ESM::Dialogue>& getJournals() const;
|
||||
|
||||
IdCollection<ESM::Dialogue>& getJournals();
|
||||
|
||||
const InfoCollection& getTopicInfos() const;
|
||||
|
||||
InfoCollection& getTopicInfos();
|
||||
|
||||
const InfoCollection& getJournalInfos() const;
|
||||
|
||||
InfoCollection& getJournalInfos();
|
||||
|
||||
const IdCollection<Cell>& getCells() const;
|
||||
|
||||
IdCollection<Cell>& getCells();
|
||||
|
@ -141,8 +167,10 @@ namespace CSMWorld
|
|||
void merge();
|
||||
///< Merge modified into base.
|
||||
|
||||
void loadFile (const boost::filesystem::path& path, bool base);
|
||||
void loadFile (const boost::filesystem::path& path, bool base, bool project);
|
||||
///< Merging content of a file into base or modified.
|
||||
///
|
||||
/// \param project load project file instead of content file
|
||||
|
||||
bool hasId (const std::string& id) const;
|
||||
|
||||
|
@ -151,6 +179,17 @@ namespace CSMWorld
|
|||
///
|
||||
/// \param listDeleted include deleted record in the list
|
||||
|
||||
int count (RecordBase::State state) const;
|
||||
///< Return number of top-level records with the given \a state.
|
||||
|
||||
void setDescription (const std::string& description);
|
||||
|
||||
std::string getDescription() const;
|
||||
|
||||
void setAuthor (const std::string& author);
|
||||
|
||||
std::string getAuthor() const;
|
||||
|
||||
signals:
|
||||
|
||||
void idListChanged();
|
||||
|
|
|
@ -7,21 +7,24 @@
|
|||
|
||||
namespace CSMWorld
|
||||
{
|
||||
|
||||
/// \brief Single type collection of top level records
|
||||
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
|
||||
class IdCollection : public Collection<ESXRecordT, IdAccessorT>
|
||||
{
|
||||
public:
|
||||
|
||||
void load (ESM::ESMReader& reader, bool base,
|
||||
UniversalId::Type type = UniversalId::Type_None);
|
||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||
void load (ESM::ESMReader& reader, bool base);
|
||||
|
||||
void load (const ESXRecordT& record, bool base);
|
||||
|
||||
bool tryDelete (const std::string& id);
|
||||
///< Try deleting \a id. If the id does not exist or can't be deleted the call is ignored.
|
||||
///
|
||||
/// \return Has the ID been deleted?
|
||||
};
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
void IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base,
|
||||
UniversalId::Type type)
|
||||
void IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base)
|
||||
{
|
||||
std::string id = reader.getHNOString ("NAME");
|
||||
|
||||
|
@ -56,31 +59,63 @@ namespace CSMWorld
|
|||
IdAccessorT().getId (record) = id;
|
||||
record.load (reader);
|
||||
|
||||
int index = this->searchId (IdAccessorT().getId (record));
|
||||
|
||||
if (index==-1)
|
||||
{
|
||||
// new record
|
||||
Record<ESXRecordT> record2;
|
||||
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
||||
(base ? record2.mBase : record2.mModified) = record;
|
||||
|
||||
this->appendRecord (record2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// old record
|
||||
Record<ESXRecordT> record2 = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
|
||||
|
||||
if (base)
|
||||
record2.mBase = record;
|
||||
else
|
||||
record2.setModified (record);
|
||||
|
||||
this->setRecord (index, record2);
|
||||
}
|
||||
load (record, base);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
void IdCollection<ESXRecordT, IdAccessorT>::load (const ESXRecordT& record, bool base)
|
||||
{
|
||||
int index = this->searchId (IdAccessorT().getId (record));
|
||||
|
||||
if (index==-1)
|
||||
{
|
||||
// new record
|
||||
Record<ESXRecordT> record2;
|
||||
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
||||
(base ? record2.mBase : record2.mModified) = record;
|
||||
|
||||
this->appendRecord (record2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// old record
|
||||
Record<ESXRecordT> record2 = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
|
||||
|
||||
if (base)
|
||||
record2.mBase = record;
|
||||
else
|
||||
record2.setModified (record);
|
||||
|
||||
this->setRecord (index, record2);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ESXRecordT, typename IdAccessorT>
|
||||
bool IdCollection<ESXRecordT, IdAccessorT>::tryDelete (const std::string& id)
|
||||
{
|
||||
int index = this->searchId (id);
|
||||
|
||||
if (index==-1)
|
||||
return false;
|
||||
|
||||
Record<ESXRecordT> record = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
|
||||
|
||||
if (record.isDeleted())
|
||||
return false;
|
||||
|
||||
if (record.mState==RecordBase::State_ModifiedOnly)
|
||||
{
|
||||
Collection<ESXRecordT, IdAccessorT>::removeRows (index, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
record.mState = RecordBase::State_Deleted;
|
||||
this->setRecord (index, record);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,15 +4,12 @@
|
|||
#include "collectionbase.hpp"
|
||||
#include "columnbase.hpp"
|
||||
|
||||
CSMWorld::IdTable::IdTable (CollectionBase *idCollection) : mIdCollection (idCollection)
|
||||
{
|
||||
|
||||
}
|
||||
CSMWorld::IdTable::IdTable (CollectionBase *idCollection, Reordering reordering)
|
||||
: mIdCollection (idCollection), mReordering (reordering)
|
||||
{}
|
||||
|
||||
CSMWorld::IdTable::~IdTable()
|
||||
{
|
||||
|
||||
}
|
||||
{}
|
||||
|
||||
int CSMWorld::IdTable::rowCount (const QModelIndex & parent) const
|
||||
{
|
||||
|
@ -118,7 +115,7 @@ QModelIndex CSMWorld::IdTable::parent (const QModelIndex& index) const
|
|||
|
||||
void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type)
|
||||
{
|
||||
int index = mIdCollection->getAppendIndex();
|
||||
int index = mIdCollection->getAppendIndex (id, type);
|
||||
|
||||
beginInsertRows (QModelIndex(), index, index);
|
||||
|
||||
|
@ -127,6 +124,17 @@ void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type
|
|||
endInsertRows();
|
||||
}
|
||||
|
||||
void CSMWorld::IdTable::cloneRecord(const std::string& origin,
|
||||
const std::string& destination,
|
||||
CSMWorld::UniversalId::Type type)
|
||||
{
|
||||
int index = mIdCollection->getAppendIndex (destination);
|
||||
beginInsertRows (QModelIndex(), index, index);
|
||||
mIdCollection->cloneRecord(origin, destination, type);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
|
||||
QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const
|
||||
{
|
||||
return index (mIdCollection->getIndex (id), column);
|
||||
|
@ -138,7 +146,7 @@ void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& reco
|
|||
|
||||
if (index==-1)
|
||||
{
|
||||
int index = mIdCollection->getAppendIndex();
|
||||
int index = mIdCollection->getAppendIndex (id);
|
||||
|
||||
beginInsertRows (QModelIndex(), index, index);
|
||||
|
||||
|
@ -161,21 +169,23 @@ const CSMWorld::RecordBase& CSMWorld::IdTable::getRecord (const std::string& id)
|
|||
|
||||
int CSMWorld::IdTable::searchColumnIndex (Columns::ColumnId id) const
|
||||
{
|
||||
int columns = mIdCollection->getColumns();
|
||||
|
||||
for (int i=0; i<columns; ++i)
|
||||
if (mIdCollection->getColumn (i).mColumnId==id)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
return mIdCollection->searchColumnIndex (id);
|
||||
}
|
||||
|
||||
int CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const
|
||||
{
|
||||
int index = searchColumnIndex (id);
|
||||
return mIdCollection->findColumnIndex (id);
|
||||
}
|
||||
|
||||
if (index==-1)
|
||||
throw std::logic_error ("invalid column index");
|
||||
void CSMWorld::IdTable::reorderRows (int baseIndex, const std::vector<int>& newOrder)
|
||||
{
|
||||
if (!newOrder.empty())
|
||||
if (mIdCollection->reorderRows (baseIndex, newOrder))
|
||||
emit dataChanged (index (baseIndex, 0),
|
||||
index (baseIndex+newOrder.size()-1, mIdCollection->getColumns()-1));
|
||||
}
|
||||
|
||||
return index;
|
||||
CSMWorld::IdTable::Reordering CSMWorld::IdTable::getReordering() const
|
||||
{
|
||||
return mReordering;
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef CSM_WOLRD_IDTABLE_H
|
||||
#define CSM_WOLRD_IDTABLE_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
#include "universalid.hpp"
|
||||
|
@ -15,7 +17,18 @@ namespace CSMWorld
|
|||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
enum Reordering
|
||||
{
|
||||
Reordering_None,
|
||||
Reordering_WithinTopic
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
CollectionBase *mIdCollection;
|
||||
Reordering mReordering;
|
||||
|
||||
// not implemented
|
||||
IdTable (const IdTable&);
|
||||
|
@ -23,7 +36,7 @@ namespace CSMWorld
|
|||
|
||||
public:
|
||||
|
||||
IdTable (CollectionBase *idCollection);
|
||||
IdTable (CollectionBase *idCollection, Reordering reordering = Reordering_WithinTopic);
|
||||
///< The ownership of \a idCollection is not transferred.
|
||||
|
||||
virtual ~IdTable();
|
||||
|
@ -50,6 +63,10 @@ namespace CSMWorld
|
|||
void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None);
|
||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||
|
||||
void cloneRecord(const std::string& origin,
|
||||
const std::string& destination,
|
||||
UniversalId::Type type = UniversalId::Type_None);
|
||||
|
||||
QModelIndex getModelIndex (const std::string& id, int column) const;
|
||||
|
||||
void setRecord (const std::string& id, const RecordBase& record);
|
||||
|
@ -63,6 +80,12 @@ namespace CSMWorld
|
|||
int findColumnIndex (Columns::ColumnId id) const;
|
||||
///< Return index of column with the given \a id. If no such column exists, an exception is
|
||||
/// thrown.
|
||||
|
||||
void reorderRows (int baseIndex, const std::vector<int>& newOrder);
|
||||
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
|
||||
/// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).
|
||||
|
||||
Reordering getReordering() const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,9 @@ bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelI
|
|||
|
||||
CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent)
|
||||
: QSortFilterProxyModel (parent)
|
||||
{}
|
||||
{
|
||||
setSortCaseSensitivity (Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const
|
||||
{
|
||||
|
|
14
apps/opencs/model/world/info.hpp
Normal file
14
apps/opencs/model/world/info.hpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef CSM_WOLRD_INFO_H
|
||||
#define CSM_WOLRD_INFO_H
|
||||
|
||||
#include <components/esm/loadinfo.hpp>
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
struct Info : public ESM::DialInfo
|
||||
{
|
||||
std::string mTopicId;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
184
apps/opencs/model/world/infocollection.cpp
Normal file
184
apps/opencs/model/world/infocollection.cpp
Normal file
|
@ -0,0 +1,184 @@
|
|||
|
||||
#include "infocollection.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <iterator>
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/loaddial.hpp>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
void CSMWorld::InfoCollection::load (const Info& record, bool base)
|
||||
{
|
||||
int index = searchId (record.mId);
|
||||
|
||||
if (index==-1)
|
||||
{
|
||||
// new record
|
||||
Record<Info> record2;
|
||||
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
||||
(base ? record2.mBase : record2.mModified) = record;
|
||||
|
||||
int index = -1;
|
||||
|
||||
std::string topic = Misc::StringUtils::lowerCase (record2.get().mTopicId);
|
||||
|
||||
if (!record2.get().mPrev.empty())
|
||||
{
|
||||
index = getIndex (record2.get().mPrev, topic);
|
||||
|
||||
if (index!=-1)
|
||||
++index;
|
||||
}
|
||||
|
||||
if (index==-1 && !record2.get().mNext.empty())
|
||||
{
|
||||
index = getIndex (record2.get().mNext, topic);
|
||||
}
|
||||
|
||||
if (index==-1)
|
||||
{
|
||||
Range range = getTopicRange (topic);
|
||||
|
||||
index = std::distance (getRecords().begin(), range.second);
|
||||
}
|
||||
|
||||
insertRecord (record2, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
// old record
|
||||
Record<Info> record2 = getRecord (index);
|
||||
|
||||
if (base)
|
||||
record2.mBase = record;
|
||||
else
|
||||
record2.setModified (record);
|
||||
|
||||
setRecord (index, record2);
|
||||
}
|
||||
}
|
||||
|
||||
int CSMWorld::InfoCollection::getIndex (const std::string& id, const std::string& topic) const
|
||||
{
|
||||
std::string fullId = Misc::StringUtils::lowerCase (topic) + "#" + id;
|
||||
|
||||
std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange (topic);
|
||||
|
||||
for (; range.first!=range.second; ++range.first)
|
||||
if (Misc::StringUtils::ciEqual(range.first->get().mId, fullId))
|
||||
return std::distance (getRecords().begin(), range.first);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int CSMWorld::InfoCollection::getAppendIndex (const std::string& id, UniversalId::Type type) const
|
||||
{
|
||||
std::string::size_type separator = id.find_last_of ('#');
|
||||
|
||||
if (separator==std::string::npos)
|
||||
throw std::runtime_error ("invalid info ID: " + id);
|
||||
|
||||
std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange (id.substr (0, separator));
|
||||
|
||||
if (range.first==range.second)
|
||||
return Collection<Info, IdAccessor<Info> >::getAppendIndex (id, type);
|
||||
|
||||
return std::distance (getRecords().begin(), range.second);
|
||||
}
|
||||
|
||||
bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector<int>& newOrder)
|
||||
{
|
||||
// check if the range is valid
|
||||
int lastIndex = baseIndex + newOrder.size() -1;
|
||||
|
||||
if (lastIndex>=getSize())
|
||||
return false;
|
||||
|
||||
// Check that topics match
|
||||
if (getRecord (baseIndex).get().mTopicId!=getRecord (lastIndex).get().mTopicId)
|
||||
return false;
|
||||
|
||||
// reorder
|
||||
return reorderRowsImp (baseIndex, newOrder);
|
||||
}
|
||||
|
||||
void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue)
|
||||
{
|
||||
std::string id = Misc::StringUtils::lowerCase (dialogue.mId) + "#" +
|
||||
reader.getHNOString ("INAM");
|
||||
|
||||
if (reader.isNextSub ("DELE"))
|
||||
{
|
||||
int index = searchId (id);
|
||||
|
||||
reader.skipRecord();
|
||||
|
||||
if (index==-1)
|
||||
{
|
||||
// deleting a record that does not exist
|
||||
|
||||
// ignore it for now
|
||||
|
||||
/// \todo report the problem to the user
|
||||
}
|
||||
else if (base)
|
||||
{
|
||||
removeRows (index, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Record<Info> record = getRecord (index);
|
||||
record.mState = RecordBase::State_Deleted;
|
||||
setRecord (index, record);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Info record;
|
||||
record.mTopicId = dialogue.mId;
|
||||
record.mId = id;
|
||||
record.load (reader);
|
||||
|
||||
load (record, base);
|
||||
}
|
||||
}
|
||||
|
||||
CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const std::string& topic)
|
||||
const
|
||||
{
|
||||
std::string topic2 = Misc::StringUtils::lowerCase (topic);
|
||||
|
||||
std::map<std::string, int>::const_iterator iter = getIdMap().lower_bound (topic2);
|
||||
|
||||
// Skip invalid records: The beginning of a topic string could be identical to another topic
|
||||
// string.
|
||||
for (; iter!=getIdMap().end(); ++iter)
|
||||
{
|
||||
std::string testTopicId =
|
||||
Misc::StringUtils::lowerCase (getRecord (iter->second).get().mTopicId);
|
||||
|
||||
if (testTopicId==topic2)
|
||||
break;
|
||||
|
||||
std::size_t size = topic2.size();
|
||||
|
||||
if (testTopicId.size()<size || testTopicId.substr (0, size)!=topic2)
|
||||
return Range (getRecords().end(), getRecords().end());
|
||||
}
|
||||
|
||||
if (iter==getIdMap().end())
|
||||
return Range (getRecords().end(), getRecords().end());
|
||||
|
||||
RecordConstIterator begin = getRecords().begin()+iter->second;
|
||||
|
||||
// Find end
|
||||
RecordConstIterator end = begin;
|
||||
|
||||
for (; end!=getRecords().end(); ++end)
|
||||
if (!Misc::StringUtils::ciEqual(end->get().mTopicId, topic2))
|
||||
break;
|
||||
|
||||
return Range (begin, end);
|
||||
}
|
50
apps/opencs/model/world/infocollection.hpp
Normal file
50
apps/opencs/model/world/infocollection.hpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
#ifndef CSM_WOLRD_INFOCOLLECTION_H
|
||||
#define CSM_WOLRD_INFOCOLLECTION_H
|
||||
|
||||
#include "collection.hpp"
|
||||
#include "info.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class Dialogue;
|
||||
}
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class InfoCollection : public Collection<Info, IdAccessor<Info> >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::vector<Record<Info> >::const_iterator RecordConstIterator;
|
||||
typedef std::pair<RecordConstIterator, RecordConstIterator> Range;
|
||||
|
||||
private:
|
||||
|
||||
void load (const Info& record, bool base);
|
||||
|
||||
int getIndex (const std::string& id, const std::string& topic) const;
|
||||
///< Return index for record \a id or -1 (if not present; deleted records are considered)
|
||||
///
|
||||
/// \param id info ID without topic prefix
|
||||
|
||||
public:
|
||||
|
||||
virtual int getAppendIndex (const std::string& id,
|
||||
UniversalId::Type type = UniversalId::Type_None) const;
|
||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||
|
||||
virtual bool reorderRows (int baseIndex, const std::vector<int>& newOrder);
|
||||
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
|
||||
/// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).
|
||||
///
|
||||
/// \return Success?
|
||||
|
||||
void load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue);
|
||||
|
||||
Range getTopicRange (const std::string& topic) const;
|
||||
///< Return iterators that point to the beginning and past the end of the range for
|
||||
/// the given topic.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -6,8 +6,7 @@
|
|||
void CSMWorld::CellRef::load (ESM::ESMReader &esm, Cell& cell, const std::string& id)
|
||||
{
|
||||
mId = id;
|
||||
mCellId = cell.mId;
|
||||
mCell = cell.mId;
|
||||
|
||||
if (!mDeleted)
|
||||
cell.addRef (mId);
|
||||
cell.addRef (mId);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue