forked from mirror/openmw-tes3mp
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
07cec0ea75
578 changed files with 27640 additions and 15018 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -15,3 +15,5 @@ makefile
|
||||||
data
|
data
|
||||||
*.kdev4
|
*.kdev4
|
||||||
CMakeLists.txt.user
|
CMakeLists.txt.user
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
1
.gitmodules
vendored
1
.gitmodules
vendored
|
@ -1 +0,0 @@
|
||||||
|
|
|
@ -1,123 +0,0 @@
|
||||||
Bitstream Vera Fonts Copyright
|
|
||||||
|
|
||||||
The fonts have a generous copyright, allowing derivative works (as
|
|
||||||
long as "Bitstream" or "Vera" are not in the names), and full
|
|
||||||
redistribution (so long as they are not *sold* by themselves). They
|
|
||||||
can be be bundled, redistributed and sold with any software.
|
|
||||||
|
|
||||||
The fonts are distributed under the following copyright:
|
|
||||||
|
|
||||||
Copyright
|
|
||||||
=========
|
|
||||||
|
|
||||||
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream
|
|
||||||
Vera is a trademark of Bitstream, Inc.
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of the fonts accompanying this license ("Fonts") and associated
|
|
||||||
documentation files (the "Font Software"), to reproduce and distribute
|
|
||||||
the Font Software, including without limitation the rights to use,
|
|
||||||
copy, merge, publish, distribute, and/or sell copies of the Font
|
|
||||||
Software, and to permit persons to whom the Font Software is furnished
|
|
||||||
to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright and trademark notices and this permission notice
|
|
||||||
shall be included in all copies of one or more of the Font Software
|
|
||||||
typefaces.
|
|
||||||
|
|
||||||
The Font Software may be modified, altered, or added to, and in
|
|
||||||
particular the designs of glyphs or characters in the Fonts may be
|
|
||||||
modified and additional glyphs or characters may be added to the
|
|
||||||
Fonts, only if the fonts are renamed to names not containing either
|
|
||||||
the words "Bitstream" or the word "Vera".
|
|
||||||
|
|
||||||
This License becomes null and void to the extent applicable to Fonts
|
|
||||||
or Font Software that has been modified and is distributed under the
|
|
||||||
"Bitstream Vera" names.
|
|
||||||
|
|
||||||
The Font Software may be sold as part of a larger software package but
|
|
||||||
no copy of one or more of the Font Software typefaces may be sold by
|
|
||||||
itself.
|
|
||||||
|
|
||||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
||||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
|
|
||||||
BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
||||||
OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL,
|
|
||||||
OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
||||||
OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT
|
|
||||||
SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
||||||
|
|
||||||
Except as contained in this notice, the names of Gnome, the Gnome
|
|
||||||
Foundation, and Bitstream Inc., shall not be used in advertising or
|
|
||||||
otherwise to promote the sale, use or other dealings in this Font
|
|
||||||
Software without prior written authorization from the Gnome Foundation
|
|
||||||
or Bitstream Inc., respectively. For further information, contact:
|
|
||||||
fonts at gnome dot org.
|
|
||||||
|
|
||||||
Copyright FAQ
|
|
||||||
=============
|
|
||||||
|
|
||||||
1. I don't understand the resale restriction... What gives?
|
|
||||||
|
|
||||||
Bitstream is giving away these fonts, but wishes to ensure its
|
|
||||||
competitors can't just drop the fonts as is into a font sale system
|
|
||||||
and sell them as is. It seems fair that if Bitstream can't make money
|
|
||||||
from the Bitstream Vera fonts, their competitors should not be able to
|
|
||||||
do so either. You can sell the fonts as part of any software package,
|
|
||||||
however.
|
|
||||||
|
|
||||||
2. I want to package these fonts separately for distribution and
|
|
||||||
sale as part of a larger software package or system. Can I do so?
|
|
||||||
|
|
||||||
Yes. A RPM or Debian package is a "larger software package" to begin
|
|
||||||
with, and you aren't selling them independently by themselves.
|
|
||||||
See 1. above.
|
|
||||||
|
|
||||||
3. Are derivative works allowed?
|
|
||||||
Yes!
|
|
||||||
|
|
||||||
4. Can I change or add to the font(s)?
|
|
||||||
Yes, but you must change the name(s) of the font(s).
|
|
||||||
|
|
||||||
5. Under what terms are derivative works allowed?
|
|
||||||
|
|
||||||
You must change the name(s) of the fonts. This is to ensure the
|
|
||||||
quality of the fonts, both to protect Bitstream and Gnome. We want to
|
|
||||||
ensure that if an application has opened a font specifically of these
|
|
||||||
names, it gets what it expects (though of course, using fontconfig,
|
|
||||||
substitutions could still could have occurred during font
|
|
||||||
opening). You must include the Bitstream copyright. Additional
|
|
||||||
copyrights can be added, as per copyright law. Happy Font Hacking!
|
|
||||||
|
|
||||||
6. If I have improvements for Bitstream Vera, is it possible they might get
|
|
||||||
adopted in future versions?
|
|
||||||
|
|
||||||
Yes. The contract between the Gnome Foundation and Bitstream has
|
|
||||||
provisions for working with Bitstream to ensure quality additions to
|
|
||||||
the Bitstream Vera font family. Please contact us if you have such
|
|
||||||
additions. Note, that in general, we will want such additions for the
|
|
||||||
entire family, not just a single font, and that you'll have to keep
|
|
||||||
both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add
|
|
||||||
glyphs to the font, they must be stylistically in keeping with Vera's
|
|
||||||
design. Vera cannot become a "ransom note" font. Jim Lyles will be
|
|
||||||
providing a document describing the design elements used in Vera, as a
|
|
||||||
guide and aid for people interested in contributing to Vera.
|
|
||||||
|
|
||||||
7. I want to sell a software package that uses these fonts: Can I do so?
|
|
||||||
|
|
||||||
Sure. Bundle the fonts with your software and sell your software
|
|
||||||
with the fonts. That is the intent of the copyright.
|
|
||||||
|
|
||||||
8. If applications have built the names "Bitstream Vera" into them,
|
|
||||||
can I override this somehow to use fonts of my choosing?
|
|
||||||
|
|
||||||
This depends on exact details of the software. Most open source
|
|
||||||
systems and software (e.g., Gnome, KDE, etc.) are now converting to
|
|
||||||
use fontconfig (see www.fontconfig.org) to handle font configuration,
|
|
||||||
selection and substitution; it has provisions for overriding font
|
|
||||||
names and subsituting alternatives. An example is provided by the
|
|
||||||
supplied local.conf file, which chooses the family Bitstream Vera for
|
|
||||||
"sans", "serif" and "monospace". Other software (e.g., the XFree86
|
|
||||||
core server) has other mechanisms for font substitution.
|
|
131
CMakeLists.txt
131
CMakeLists.txt
|
@ -15,7 +15,7 @@ include (OpenMWMacros)
|
||||||
# Version
|
# Version
|
||||||
|
|
||||||
set (OPENMW_VERSION_MAJOR 0)
|
set (OPENMW_VERSION_MAJOR 0)
|
||||||
set (OPENMW_VERSION_MINOR 20)
|
set (OPENMW_VERSION_MINOR 21)
|
||||||
set (OPENMW_VERSION_RELEASE 0)
|
set (OPENMW_VERSION_RELEASE 0)
|
||||||
|
|
||||||
set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
|
set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
|
||||||
|
@ -29,15 +29,17 @@ option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binarie
|
||||||
option(BOOST_STATIC "Link static build of Boost into the binaries" FALSE)
|
option(BOOST_STATIC "Link static build of Boost into the binaries" FALSE)
|
||||||
|
|
||||||
# Apps and tools
|
# Apps and tools
|
||||||
|
option(BUILD_BSATOOL "build BSA extractor" OFF)
|
||||||
option(BUILD_ESMTOOL "build ESM inspector" ON)
|
option(BUILD_ESMTOOL "build ESM inspector" ON)
|
||||||
option(BUILD_LAUNCHER "build Launcher" ON)
|
option(BUILD_LAUNCHER "build Launcher" ON)
|
||||||
option(BUILD_MWINIIMPORTER "build MWiniImporter" ON)
|
option(BUILD_MWINIIMPORTER "build MWiniImporter" ON)
|
||||||
|
option(BUILD_OPENCS "build OpenMW Construction Set" ON)
|
||||||
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
|
option(BUILD_WITH_CODE_COVERAGE "Enable code coverage with gconv" OFF)
|
||||||
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest ang GMock frameworks" OFF)
|
option(BUILD_UNITTESTS "Enable Unittests with Google C++ Unittest ang GMock frameworks" OFF)
|
||||||
|
|
||||||
# Sound source selection
|
# Sound source selection
|
||||||
option(USE_FFMPEG "use ffmpeg for sound" OFF)
|
option(USE_FFMPEG "use ffmpeg for sound" ON)
|
||||||
option(USE_AUDIERE "use audiere for sound" OFF)
|
option(USE_AUDIERE "use audiere for sound" ON)
|
||||||
option(USE_MPG123 "use mpg123 + libsndfile for sound" ON)
|
option(USE_MPG123 "use mpg123 + libsndfile for sound" ON)
|
||||||
|
|
||||||
# OS X deployment
|
# OS X deployment
|
||||||
|
@ -66,35 +68,6 @@ endif()
|
||||||
# We probably support older versions than this.
|
# We probably support older versions than this.
|
||||||
cmake_minimum_required(VERSION 2.6)
|
cmake_minimum_required(VERSION 2.6)
|
||||||
|
|
||||||
#
|
|
||||||
# Pre-built binaries being used?
|
|
||||||
#
|
|
||||||
IF(EXISTS "${CMAKE_SOURCE_DIR}/prebuilt/vc100-mt-gd/ogre_1_7_1")
|
|
||||||
set(PREBUILT_DIR "${CMAKE_SOURCE_DIR}/prebuilt/vc100-mt-gd")
|
|
||||||
message (STATUS "OpenMW pre-built binaries found at ${PREBUILT_DIR}.")
|
|
||||||
|
|
||||||
SET(ENV{OGRE_HOME} "${PREBUILT_DIR}/ogre_1_7_1")
|
|
||||||
|
|
||||||
SET(ENV{BOOST_ROOT} "${PREBUILT_DIR}/boost_1_42_0")
|
|
||||||
set(Boost_USE_STATIC_LIBS ON)
|
|
||||||
set(Boost_USE_MULTITHREADED ON)
|
|
||||||
set(ENV{BOOST_INCLUDEDIR} "${BOOST_ROOT}/include")
|
|
||||||
set(ENV{BOOST_LIBRARYDIR} "${BOOST_ROOT}/lib")
|
|
||||||
|
|
||||||
set(ENV{FREETYPE_DIR} "${PREBUILT_DIR}/freetype-2.3.5-1")
|
|
||||||
|
|
||||||
set(USE_MPG123 OFF)
|
|
||||||
set(USE_AUDIERE ON)
|
|
||||||
set(AUDIERE_INCLUDE_DIR "${PREBUILT_DIR}/audiere-1.9.4/include")
|
|
||||||
set(AUDIERE_LIBRARY "${PREBUILT_DIR}/audiere-1.9.4/lib/audiere.lib")
|
|
||||||
|
|
||||||
set(ENV{OPENALDIR} "${PREBUILT_DIR}/OpenAL 1.1 SDK")
|
|
||||||
|
|
||||||
set(BULLET_ROOT "${PREBUILT_DIR}/bullet")
|
|
||||||
ELSE()
|
|
||||||
message (STATUS "OpenMW pre-built binaries not found. Using standard locations.")
|
|
||||||
ENDIF()
|
|
||||||
|
|
||||||
# source directory: libs
|
# source directory: libs
|
||||||
|
|
||||||
set(LIBDIR ${CMAKE_SOURCE_DIR}/libs)
|
set(LIBDIR ${CMAKE_SOURCE_DIR}/libs)
|
||||||
|
@ -102,8 +75,6 @@ set(LIBDIR ${CMAKE_SOURCE_DIR}/libs)
|
||||||
set(OENGINE_OGRE
|
set(OENGINE_OGRE
|
||||||
${LIBDIR}/openengine/ogre/renderer.cpp
|
${LIBDIR}/openengine/ogre/renderer.cpp
|
||||||
${LIBDIR}/openengine/ogre/fader.cpp
|
${LIBDIR}/openengine/ogre/fader.cpp
|
||||||
${LIBDIR}/openengine/ogre/imagerotate.cpp
|
|
||||||
${LIBDIR}/openengine/ogre/atlas.cpp
|
|
||||||
${LIBDIR}/openengine/ogre/selectionbuffer.cpp
|
${LIBDIR}/openengine/ogre/selectionbuffer.cpp
|
||||||
)
|
)
|
||||||
set(OENGINE_GUI
|
set(OENGINE_GUI
|
||||||
|
@ -123,8 +94,6 @@ set(OENGINE_BULLET
|
||||||
${LIBDIR}/openengine/bullet/physic.hpp
|
${LIBDIR}/openengine/bullet/physic.hpp
|
||||||
${LIBDIR}/openengine/bullet/BulletShapeLoader.cpp
|
${LIBDIR}/openengine/bullet/BulletShapeLoader.cpp
|
||||||
${LIBDIR}/openengine/bullet/BulletShapeLoader.h
|
${LIBDIR}/openengine/bullet/BulletShapeLoader.h
|
||||||
${LIBDIR}/openengine/bullet/pmove.cpp
|
|
||||||
${LIBDIR}/openengine/bullet/pmove.h
|
|
||||||
${LIBDIR}/openengine/bullet/trace.cpp
|
${LIBDIR}/openengine/bullet/trace.cpp
|
||||||
${LIBDIR}/openengine/bullet/trace.h
|
${LIBDIR}/openengine/bullet/trace.h
|
||||||
|
|
||||||
|
@ -137,30 +106,54 @@ set(OPENMW_LIBS ${OENGINE_ALL})
|
||||||
set(OPENMW_LIBS_HEADER)
|
set(OPENMW_LIBS_HEADER)
|
||||||
|
|
||||||
# Sound setup
|
# Sound setup
|
||||||
|
set(GOT_SOUND_INPUT 0)
|
||||||
set(SOUND_INPUT_INCLUDES "")
|
set(SOUND_INPUT_INCLUDES "")
|
||||||
set(SOUND_INPUT_LIBRARY "")
|
set(SOUND_INPUT_LIBRARY "")
|
||||||
set(SOUND_DEFINE "")
|
set(SOUND_DEFINE "")
|
||||||
if (USE_FFMPEG)
|
if (USE_FFMPEG)
|
||||||
find_package(FFMPEG REQUIRED)
|
set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE)
|
||||||
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${FFMPEG_INCLUDE_DIR})
|
find_package(FFmpeg)
|
||||||
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${FFMPEG_LIBRARIES})
|
if (FFMPEG_FOUND)
|
||||||
|
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${FFMPEG_INCLUDE_DIRS})
|
||||||
|
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${FFMPEG_LIBRARIES} ${SWSCALE_LIBRARIES})
|
||||||
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_FFMPEG)
|
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_FFMPEG)
|
||||||
|
set(GOT_SOUND_INPUT 1)
|
||||||
|
endif (FFMPEG_FOUND)
|
||||||
endif (USE_FFMPEG)
|
endif (USE_FFMPEG)
|
||||||
|
|
||||||
if (USE_AUDIERE)
|
if (USE_AUDIERE AND NOT GOT_SOUND_INPUT)
|
||||||
find_package(Audiere REQUIRED)
|
find_package(Audiere)
|
||||||
|
if (AUDIERE_FOUND)
|
||||||
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${AUDIERE_INCLUDE_DIR})
|
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${AUDIERE_INCLUDE_DIR})
|
||||||
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${AUDIERE_LIBRARY})
|
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${AUDIERE_LIBRARY})
|
||||||
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_AUDIERE)
|
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_AUDIERE)
|
||||||
endif (USE_AUDIERE)
|
set(GOT_SOUND_INPUT 1)
|
||||||
|
endif (AUDIERE_FOUND)
|
||||||
|
endif (USE_AUDIERE AND NOT GOT_SOUND_INPUT)
|
||||||
|
|
||||||
if (USE_MPG123)
|
if (USE_MPG123 AND NOT GOT_SOUND_INPUT)
|
||||||
find_package(MPG123 REQUIRED)
|
find_package(MPG123 REQUIRED)
|
||||||
find_package(SNDFILE REQUIRED)
|
find_package(SNDFILE REQUIRED)
|
||||||
|
if (MPG123_FOUND AND SNDFILE_FOUND)
|
||||||
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${MPG123_INCLUDE_DIR} ${SNDFILE_INCLUDE_DIR})
|
set(SOUND_INPUT_INCLUDES ${SOUND_INPUT_INCLUDES} ${MPG123_INCLUDE_DIR} ${SNDFILE_INCLUDE_DIR})
|
||||||
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${MPG123_LIBRARY} ${SNDFILE_LIBRARY})
|
set(SOUND_INPUT_LIBRARY ${SOUND_INPUT_LIBRARY} ${MPG123_LIBRARY} ${SNDFILE_LIBRARY})
|
||||||
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_MPG123)
|
set(SOUND_DEFINE ${SOUND_DEFINE} -DOPENMW_USE_MPG123)
|
||||||
endif (USE_MPG123)
|
set(GOT_SOUND_INPUT 1)
|
||||||
|
endif (MPG123_FOUND AND SNDFILE_FOUND)
|
||||||
|
endif (USE_MPG123 AND NOT GOT_SOUND_INPUT)
|
||||||
|
|
||||||
|
if (NOT GOT_SOUND_INPUT)
|
||||||
|
message(WARNING "--------------------")
|
||||||
|
message(WARNING "Failed to find any sound input packages")
|
||||||
|
message(WARNING "--------------------")
|
||||||
|
endif (NOT GOT_SOUND_INPUT)
|
||||||
|
|
||||||
|
if (NOT FFMPEG_FOUND)
|
||||||
|
message(WARNING "--------------------")
|
||||||
|
message(WARNING "FFmpeg not found, video playback will be disabled")
|
||||||
|
message(WARNING "--------------------")
|
||||||
|
endif (NOT FFMPEG_FOUND)
|
||||||
|
|
||||||
|
|
||||||
# Platform specific
|
# Platform specific
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
@ -304,14 +297,14 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
|
||||||
"${OpenMW_BINARY_DIR}/openmw.cfg.install")
|
"${OpenMW_BINARY_DIR}/openmw.cfg.install")
|
||||||
|
|
||||||
|
|
||||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
if (NOT WIN32 AND NOT APPLE)
|
||||||
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop
|
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop
|
||||||
"${OpenMW_BINARY_DIR}/openmw.desktop")
|
"${OpenMW_BINARY_DIR}/openmw.desktop")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Compiler settings
|
# Compiler settings
|
||||||
if (CMAKE_COMPILER_IS_GNUCC)
|
if (CMAKE_COMPILER_IS_GNUCC)
|
||||||
add_definitions (-Wall -Wextra -Wno-unused-parameter -Wno-reorder -std=c++0x -pedantic)
|
add_definitions (-Wall -Wextra -Wno-unused-parameter -Wno-reorder -std=c++98 -pedantic -Wno-long-long)
|
||||||
|
|
||||||
# Silence warnings in OGRE headers. Remove once OGRE got fixed!
|
# Silence warnings in OGRE headers. Remove once OGRE got fixed!
|
||||||
add_definitions (-Wno-ignored-qualifiers)
|
add_definitions (-Wno-ignored-qualifiers)
|
||||||
|
@ -360,7 +353,7 @@ if(DPKG_PROGRAM)
|
||||||
Data files from the original game is required to run it.")
|
Data files from the original game is required to run it.")
|
||||||
SET(CPACK_DEBIAN_PACKAGE_NAME "openmw")
|
SET(CPACK_DEBIAN_PACKAGE_NAME "openmw")
|
||||||
SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}")
|
SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}")
|
||||||
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter")
|
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), libois-1.3.0 (>= 1.3.0), 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_DEPENDS "libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libois-1.3.0 (>= 1.3.0), 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")
|
SET(CPACK_DEBIAN_PACKAGE_SECTION "Games")
|
||||||
|
@ -385,9 +378,8 @@ if(WIN32)
|
||||||
"${OpenMW_SOURCE_DIR}/readme.txt"
|
"${OpenMW_SOURCE_DIR}/readme.txt"
|
||||||
"${OpenMW_SOURCE_DIR}/GPL3.txt"
|
"${OpenMW_SOURCE_DIR}/GPL3.txt"
|
||||||
"${OpenMW_SOURCE_DIR}/OFL.txt"
|
"${OpenMW_SOURCE_DIR}/OFL.txt"
|
||||||
"${OpenMW_SOURCE_DIR}/Bitstream Vera License.txt"
|
"${OpenMW_SOURCE_DIR}/DejaVu Font License.txt"
|
||||||
"${OpenMW_SOURCE_DIR}/Daedric Font License.txt"
|
"${OpenMW_SOURCE_DIR}/Daedric Font License.txt"
|
||||||
"${OpenMW_BINARY_DIR}/launcher.qss"
|
|
||||||
"${OpenMW_BINARY_DIR}/settings-default.cfg"
|
"${OpenMW_BINARY_DIR}/settings-default.cfg"
|
||||||
"${OpenMW_BINARY_DIR}/transparency-overrides.cfg"
|
"${OpenMW_BINARY_DIR}/transparency-overrides.cfg"
|
||||||
"${OpenMW_BINARY_DIR}/Release/omwlauncher.exe"
|
"${OpenMW_BINARY_DIR}/Release/omwlauncher.exe"
|
||||||
|
@ -400,7 +392,7 @@ if(WIN32)
|
||||||
SET(CPACK_PACKAGE_VENDOR "OpenMW.org")
|
SET(CPACK_PACKAGE_VENDOR "OpenMW.org")
|
||||||
SET(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})
|
SET(CPACK_PACKAGE_VERSION ${OPENMW_VERSION})
|
||||||
SET(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR})
|
SET(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR})
|
||||||
SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINO})
|
SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR})
|
||||||
SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
|
SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE})
|
||||||
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW;omwlauncher;OpenMW Launcher")
|
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW;omwlauncher;OpenMW Launcher")
|
||||||
SET(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\readme.txt'")
|
SET(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\readme.txt'")
|
||||||
|
@ -411,7 +403,7 @@ if(WIN32)
|
||||||
SET(CPACK_RESOURCE_FILE_README "${OpenMW_SOURCE_DIR}/readme.txt")
|
SET(CPACK_RESOURCE_FILE_README "${OpenMW_SOURCE_DIR}/readme.txt")
|
||||||
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/readme.txt")
|
SET(CPACK_PACKAGE_DESCRIPTION_FILE "${OpenMW_SOURCE_DIR}/readme.txt")
|
||||||
SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
|
SET(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
|
||||||
SET(CPACK_NSIS_DISPLAY_NAME "OpenMW")
|
SET(CPACK_NSIS_DISPLAY_NAME "OpenMW ${OPENMW_VERSION}")
|
||||||
SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org")
|
SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\www.openmw.org")
|
||||||
SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org")
|
SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\www.openmw.org")
|
||||||
SET(CPACK_NSIS_INSTALLED_ICON_NAME "omwlauncher.exe")
|
SET(CPACK_NSIS_INSTALLED_ICON_NAME "omwlauncher.exe")
|
||||||
|
@ -455,6 +447,10 @@ add_subdirectory (components)
|
||||||
# Apps and tools
|
# Apps and tools
|
||||||
add_subdirectory( apps/openmw )
|
add_subdirectory( apps/openmw )
|
||||||
|
|
||||||
|
if (BUILD_BSATOOL)
|
||||||
|
add_subdirectory( apps/bsatool )
|
||||||
|
endif()
|
||||||
|
|
||||||
if (BUILD_ESMTOOL)
|
if (BUILD_ESMTOOL)
|
||||||
add_subdirectory( apps/esmtool )
|
add_subdirectory( apps/esmtool )
|
||||||
endif()
|
endif()
|
||||||
|
@ -467,6 +463,10 @@ if (BUILD_MWINIIMPORTER)
|
||||||
add_subdirectory( apps/mwiniimporter )
|
add_subdirectory( apps/mwiniimporter )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (BUILD_OPENCS)
|
||||||
|
add_subdirectory (apps/opencs)
|
||||||
|
endif()
|
||||||
|
|
||||||
# UnitTests
|
# UnitTests
|
||||||
if (BUILD_UNITTESTS)
|
if (BUILD_UNITTESTS)
|
||||||
add_subdirectory( apps/openmw_test_suite )
|
add_subdirectory( apps/openmw_test_suite )
|
||||||
|
@ -497,7 +497,7 @@ if (WIN32)
|
||||||
# Warnings that aren't enabled normally and don't need to be enabled
|
# Warnings that aren't enabled normally and don't need to be enabled
|
||||||
# They're unneeded and sometimes completely retarded warnings that /Wall enables
|
# They're unneeded and sometimes completely retarded warnings that /Wall enables
|
||||||
# Not going to bother commenting them as they tend to warn on every standard library files
|
# Not going to bother commenting them as they tend to warn on every standard library files
|
||||||
4061 4263 4264 4266 4350 4514 4548 4571 4610 4619 4623 4625 4626 4628 4640 4668 4710 4711 4820 4826 4917 4946
|
4061 4263 4264 4266 4350 4371 4514 4548 4571 4610 4619 4623 4625 4626 4628 4640 4668 4710 4711 4820 4826 4917 4946
|
||||||
|
|
||||||
# Warnings that are thrown on standard libraries and not OpenMW
|
# Warnings that are thrown on standard libraries and not OpenMW
|
||||||
4347 # Non-template function with same name and parameter count as template function
|
4347 # Non-template function with same name and parameter count as template function
|
||||||
|
@ -508,6 +508,11 @@ if (WIN32)
|
||||||
4986 # Undocumented warning that occurs in the crtdbg.h file
|
4986 # Undocumented warning that occurs in the crtdbg.h file
|
||||||
4996 # Function was declared deprecated
|
4996 # Function was declared deprecated
|
||||||
|
|
||||||
|
# cause by ogre extensivly
|
||||||
|
4193 # #pragma warning(pop) : no matching '#pragma warning(push)'
|
||||||
|
4251 # class 'XXXX' needs to have dll-interface to be used by clients of class 'YYYY'
|
||||||
|
4275 # non dll-interface struct 'XXXX' used as base for dll-interface class 'YYYY'
|
||||||
|
|
||||||
# OpenMW specific warnings
|
# OpenMW specific warnings
|
||||||
4099 # Type mismatch, declared class or struct is defined with other type
|
4099 # Type mismatch, declared class or struct is defined with other type
|
||||||
4100 # Unreferenced formal parameter (-Wunused-parameter)
|
4100 # Unreferenced formal parameter (-Wunused-parameter)
|
||||||
|
@ -525,12 +530,19 @@ if (WIN32)
|
||||||
set(WARNINGS "${WARNINGS} /wd${d}")
|
set(WARNINGS "${WARNINGS} /wd${d}")
|
||||||
endforeach(d)
|
endforeach(d)
|
||||||
|
|
||||||
|
set_target_properties(shiny PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||||
|
set_target_properties(shiny.OgrePlatform PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||||
set_target_properties(components PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
set_target_properties(components PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||||
if (BUILD_LAUNCHER)
|
if (BUILD_LAUNCHER)
|
||||||
set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||||
endif (BUILD_LAUNCHER)
|
endif (BUILD_LAUNCHER)
|
||||||
set_target_properties(openmw PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
set_target_properties(openmw PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||||
|
if (BUILD_BSATOOL)
|
||||||
|
set_target_properties(bsatool PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||||
|
endif (BUILD_BSATOOL)
|
||||||
|
if (BUILD_ESMTOOL)
|
||||||
set_target_properties(esmtool PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
set_target_properties(esmtool PROPERTIES COMPILE_FLAGS ${WARNINGS})
|
||||||
|
endif (BUILD_ESMTOOL)
|
||||||
endif(MSVC)
|
endif(MSVC)
|
||||||
|
|
||||||
# Same for MinGW
|
# Same for MinGW
|
||||||
|
@ -561,8 +573,6 @@ if (APPLE)
|
||||||
install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
||||||
install(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
install(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
||||||
install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
||||||
install(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
|
||||||
|
|
||||||
install(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
install(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
||||||
install(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
install(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
|
||||||
|
|
||||||
|
@ -651,9 +661,21 @@ if (NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE)
|
||||||
|
|
||||||
# Install binaries
|
# Install binaries
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" )
|
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" )
|
||||||
|
IF(BUILD_LAUNCHER)
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" )
|
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" )
|
||||||
|
ENDIF(BUILD_LAUNCHER)
|
||||||
|
IF(BUILD_BSATOOL)
|
||||||
|
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" )
|
||||||
|
ENDIF(BUILD_BSATOOL)
|
||||||
|
IF(BUILD_ESMTOOL)
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" )
|
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" )
|
||||||
|
ENDIF(BUILD_ESMTOOL)
|
||||||
|
IF(BUILD_MWINIIMPORTER)
|
||||||
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" )
|
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" )
|
||||||
|
ENDIF(BUILD_MWINIIMPORTER)
|
||||||
|
IF(BUILD_OPENCS)
|
||||||
|
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" )
|
||||||
|
ENDIF(BUILD_OPENCS)
|
||||||
|
|
||||||
# Install icon and .desktop
|
# Install icon and .desktop
|
||||||
INSTALL(FILES "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.png" DESTINATION "${ICONDIR}")
|
INSTALL(FILES "${OpenMW_SOURCE_DIR}/apps/launcher/resources/images/openmw.png" DESTINATION "${ICONDIR}")
|
||||||
|
@ -667,5 +689,4 @@ if (NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE)
|
||||||
|
|
||||||
# Install resources
|
# Install resources
|
||||||
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" )
|
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" )
|
||||||
INSTALL(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${DATADIR}/resources" )
|
|
||||||
endif(NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE)
|
endif(NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE)
|
||||||
|
|
99
DejaVu Font License.txt
Normal file
99
DejaVu Font License.txt
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
|
||||||
|
Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
|
||||||
|
|
||||||
|
Bitstream Vera Fonts Copyright
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
|
||||||
|
a trademark of Bitstream, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of the fonts accompanying this license ("Fonts") and associated
|
||||||
|
documentation files (the "Font Software"), to reproduce and distribute the
|
||||||
|
Font Software, including without limitation the rights to use, copy, merge,
|
||||||
|
publish, distribute, and/or sell copies of the Font Software, and to permit
|
||||||
|
persons to whom the Font Software is furnished to do so, subject to the
|
||||||
|
following conditions:
|
||||||
|
|
||||||
|
The above copyright and trademark notices and this permission notice shall
|
||||||
|
be included in all copies of one or more of the Font Software typefaces.
|
||||||
|
|
||||||
|
The Font Software may be modified, altered, or added to, and in particular
|
||||||
|
the designs of glyphs or characters in the Fonts may be modified and
|
||||||
|
additional glyphs or characters may be added to the Fonts, only if the fonts
|
||||||
|
are renamed to names not containing either the words "Bitstream" or the word
|
||||||
|
"Vera".
|
||||||
|
|
||||||
|
This License becomes null and void to the extent applicable to Fonts or Font
|
||||||
|
Software that has been modified and is distributed under the "Bitstream
|
||||||
|
Vera" names.
|
||||||
|
|
||||||
|
The Font Software may be sold as part of a larger software package but no
|
||||||
|
copy of one or more of the Font Software typefaces may be sold by itself.
|
||||||
|
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
|
||||||
|
TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
|
||||||
|
FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
|
||||||
|
ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
||||||
|
THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
|
||||||
|
FONT SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the names of Gnome, the Gnome
|
||||||
|
Foundation, and Bitstream Inc., shall not be used in advertising or
|
||||||
|
otherwise to promote the sale, use or other dealings in this Font Software
|
||||||
|
without prior written authorization from the Gnome Foundation or Bitstream
|
||||||
|
Inc., respectively. For further information, contact: fonts at gnome dot
|
||||||
|
org.
|
||||||
|
|
||||||
|
Arev Fonts Copyright
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the fonts accompanying this license ("Fonts") and
|
||||||
|
associated documentation files (the "Font Software"), to reproduce
|
||||||
|
and distribute the modifications to the Bitstream Vera Font Software,
|
||||||
|
including without limitation the rights to use, copy, merge, publish,
|
||||||
|
distribute, and/or sell copies of the Font Software, and to permit
|
||||||
|
persons to whom the Font Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright and trademark notices and this permission notice
|
||||||
|
shall be included in all copies of one or more of the Font Software
|
||||||
|
typefaces.
|
||||||
|
|
||||||
|
The Font Software may be modified, altered, or added to, and in
|
||||||
|
particular the designs of glyphs or characters in the Fonts may be
|
||||||
|
modified and additional glyphs or characters may be added to the
|
||||||
|
Fonts, only if the fonts are renamed to names not containing either
|
||||||
|
the words "Tavmjong Bah" or the word "Arev".
|
||||||
|
|
||||||
|
This License becomes null and void to the extent applicable to Fonts
|
||||||
|
or Font Software that has been modified and is distributed under the
|
||||||
|
"Tavmjong Bah Arev" names.
|
||||||
|
|
||||||
|
The Font Software may be sold as part of a larger software package but
|
||||||
|
no copy of one or more of the Font Software typefaces may be sold by
|
||||||
|
itself.
|
||||||
|
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
|
||||||
|
TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of Tavmjong Bah shall not
|
||||||
|
be used in advertising or otherwise to promote the sale, use or other
|
||||||
|
dealings in this Font Software without prior written authorization
|
||||||
|
from Tavmjong Bah. For further information, contact: tavmjong @ free
|
||||||
|
. fr.
|
||||||
|
|
||||||
|
$Id: LICENSE 2133 2007-11-28 02:46:28Z lechimp $
|
19
apps/bsatool/CMakeLists.txt
Normal file
19
apps/bsatool/CMakeLists.txt
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
set(BSATOOL
|
||||||
|
bsatool.cpp
|
||||||
|
)
|
||||||
|
source_group(apps\\bsatool FILES ${BSATOOL})
|
||||||
|
|
||||||
|
# Main executable
|
||||||
|
add_executable(bsatool
|
||||||
|
${BSATOOL}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(bsatool
|
||||||
|
${Boost_LIBRARIES}
|
||||||
|
components
|
||||||
|
)
|
||||||
|
|
||||||
|
if (BUILD_WITH_CODE_COVERAGE)
|
||||||
|
add_definitions (--coverage)
|
||||||
|
target_link_libraries(bsatool gcov)
|
||||||
|
endif()
|
288
apps/bsatool/bsatool.cpp
Normal file
288
apps/bsatool/bsatool.cpp
Normal file
|
@ -0,0 +1,288 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
#include <boost/program_options.hpp>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
|
||||||
|
#include <components/bsa/bsa_file.hpp>
|
||||||
|
|
||||||
|
#define BSATOOL_VERSION 1.1
|
||||||
|
|
||||||
|
// Create local aliases for brevity
|
||||||
|
namespace bpo = boost::program_options;
|
||||||
|
namespace bfs = boost::filesystem;
|
||||||
|
|
||||||
|
struct Arguments
|
||||||
|
{
|
||||||
|
std::string mode;
|
||||||
|
std::string filename;
|
||||||
|
std::string extractfile;
|
||||||
|
std::string outdir;
|
||||||
|
|
||||||
|
bool longformat;
|
||||||
|
bool fullpath;
|
||||||
|
};
|
||||||
|
|
||||||
|
void replaceAll(std::string& str, const std::string& needle, const std::string& substitute)
|
||||||
|
{
|
||||||
|
int pos = str.find(needle);
|
||||||
|
while(pos != -1)
|
||||||
|
{
|
||||||
|
str.replace(pos, needle.size(), substitute);
|
||||||
|
pos = str.find(needle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parseOptions (int argc, char** argv, Arguments &info)
|
||||||
|
{
|
||||||
|
bpo::options_description desc("Inspect and extract files from Bethesda BSA archives\n\n"
|
||||||
|
"Usages:\n"
|
||||||
|
" bsatool list [-l] archivefile\n"
|
||||||
|
" List the files presents in the input archive.\n\n"
|
||||||
|
" bsatool extract [-f] archivefile [file_to_extract] [output_directory]\n"
|
||||||
|
" Extract a file from the input archive.\n\n"
|
||||||
|
" bsatool extractall archivefile [output_directory]\n"
|
||||||
|
" Extract all files from the input archive.\n\n"
|
||||||
|
"Allowed options");
|
||||||
|
|
||||||
|
desc.add_options()
|
||||||
|
("help,h", "print help message.")
|
||||||
|
("version,v", "print version information and quit.")
|
||||||
|
("long,l", "Include extra information in archive listing.")
|
||||||
|
("full-path,f", "Create diretory hierarchy on file extraction "
|
||||||
|
"(always true for extractall).")
|
||||||
|
;
|
||||||
|
|
||||||
|
// input-file is hidden and used as a positional argument
|
||||||
|
bpo::options_description hidden("Hidden Options");
|
||||||
|
|
||||||
|
hidden.add_options()
|
||||||
|
( "mode,m", bpo::value<std::string>(), "bsatool mode")
|
||||||
|
( "input-file,i", bpo::value< std::vector<std::string> >(), "input file")
|
||||||
|
;
|
||||||
|
|
||||||
|
bpo::positional_options_description p;
|
||||||
|
p.add("mode", 1).add("input-file", 3);
|
||||||
|
|
||||||
|
// there might be a better way to do this
|
||||||
|
bpo::options_description all;
|
||||||
|
all.add(desc).add(hidden);
|
||||||
|
|
||||||
|
bpo::variables_map variables;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv)
|
||||||
|
.options(all).positional(p).run();
|
||||||
|
bpo::store(valid_opts, variables);
|
||||||
|
}
|
||||||
|
catch(std::exception &e)
|
||||||
|
{
|
||||||
|
std::cout << "ERROR parsing arguments: " << e.what() << "\n\n"
|
||||||
|
<< desc << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bpo::notify(variables);
|
||||||
|
|
||||||
|
if (variables.count ("help"))
|
||||||
|
{
|
||||||
|
std::cout << desc << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (variables.count ("version"))
|
||||||
|
{
|
||||||
|
std::cout << "BSATool version " << BSATOOL_VERSION << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!variables.count("mode"))
|
||||||
|
{
|
||||||
|
std::cout << "ERROR: no mode specified!\n\n"
|
||||||
|
<< desc << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.mode = variables["mode"].as<std::string>();
|
||||||
|
if (!(info.mode == "list" || info.mode == "extract" || info.mode == "extractall"))
|
||||||
|
{
|
||||||
|
std::cout << std::endl << "ERROR: invalid mode \"" << info.mode << "\"\n\n"
|
||||||
|
<< desc << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!variables.count("input-file"))
|
||||||
|
{
|
||||||
|
std::cout << "\nERROR: missing BSA archive\n\n"
|
||||||
|
<< desc << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
info.filename = variables["input-file"].as< std::vector<std::string> >()[0];
|
||||||
|
|
||||||
|
// Default output to the working directory
|
||||||
|
info.outdir = ".";
|
||||||
|
|
||||||
|
if (info.mode == "extract")
|
||||||
|
{
|
||||||
|
if (variables["input-file"].as< std::vector<std::string> >().size() < 2)
|
||||||
|
{
|
||||||
|
std::cout << "\nERROR: file to extract unspecified\n\n"
|
||||||
|
<< desc << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (variables["input-file"].as< std::vector<std::string> >().size() > 1)
|
||||||
|
info.extractfile = variables["input-file"].as< std::vector<std::string> >()[1];
|
||||||
|
if (variables["input-file"].as< std::vector<std::string> >().size() > 2)
|
||||||
|
info.outdir = variables["input-file"].as< std::vector<std::string> >()[2];
|
||||||
|
}
|
||||||
|
else if (variables["input-file"].as< std::vector<std::string> >().size() > 1)
|
||||||
|
info.outdir = variables["input-file"].as< std::vector<std::string> >()[1];
|
||||||
|
|
||||||
|
info.longformat = variables.count("long");
|
||||||
|
info.fullpath = variables.count("full-path");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int list(Bsa::BSAFile& bsa, Arguments& info);
|
||||||
|
int extract(Bsa::BSAFile& bsa, Arguments& info);
|
||||||
|
int extractAll(Bsa::BSAFile& bsa, Arguments& info);
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
Arguments info;
|
||||||
|
if(!parseOptions (argc, argv, info))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// Open file
|
||||||
|
Bsa::BSAFile bsa;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bsa.open(info.filename);
|
||||||
|
}
|
||||||
|
catch(std::exception &e)
|
||||||
|
{
|
||||||
|
std::cout << "ERROR reading BSA archive '" << info.filename
|
||||||
|
<< "'\nDetails:\n" << e.what() << std::endl;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.mode == "list")
|
||||||
|
return list(bsa, info);
|
||||||
|
else if (info.mode == "extract")
|
||||||
|
return extract(bsa, info);
|
||||||
|
else if (info.mode == "extractall")
|
||||||
|
return extractAll(bsa, info);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << "Unsupported mode. That is not supposed to happen." << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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++)
|
||||||
|
{
|
||||||
|
if(info.longformat)
|
||||||
|
{
|
||||||
|
// Long format
|
||||||
|
std::cout << std::setw(50) << std::left << files[i].name;
|
||||||
|
std::cout << std::setw(8) << std::left << std::dec << files[i].fileSize;
|
||||||
|
std::cout << "@ 0x" << std::hex << files[i].offset << std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
std::cout << files[i].name << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int extract(Bsa::BSAFile& bsa, Arguments& info)
|
||||||
|
{
|
||||||
|
std::string archivePath = info.extractfile;
|
||||||
|
replaceAll(archivePath, "/", "\\");
|
||||||
|
|
||||||
|
std::string extractPath = info.extractfile;
|
||||||
|
replaceAll(extractPath, "\\", "/");
|
||||||
|
|
||||||
|
if (!bsa.exists(archivePath.c_str()))
|
||||||
|
{
|
||||||
|
std::cout << "ERROR: file '" << archivePath << "' not found\n";
|
||||||
|
std::cout << "In archive: " << info.filename << std::endl;
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the target path (the path the file will be extracted to)
|
||||||
|
bfs::path relPath (extractPath);
|
||||||
|
bfs::path outdir (info.outdir);
|
||||||
|
|
||||||
|
bfs::path target;
|
||||||
|
if (info.fullpath)
|
||||||
|
target = outdir / relPath;
|
||||||
|
else
|
||||||
|
target = outdir / relPath.filename();
|
||||||
|
|
||||||
|
// Create the directory hierarchy
|
||||||
|
bfs::create_directories(target.parent_path());
|
||||||
|
|
||||||
|
bfs::file_status s = bfs::status(target.parent_path());
|
||||||
|
if (!bfs::is_directory(s))
|
||||||
|
{
|
||||||
|
std::cout << "ERROR: " << target.parent_path() << " is not a directory." << std::endl;
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a stream for the file to extract
|
||||||
|
Ogre::DataStreamPtr data = bsa.getFile(archivePath.c_str());
|
||||||
|
bfs::ofstream out(target, std::ios::binary);
|
||||||
|
|
||||||
|
// Write the file to disk
|
||||||
|
std::cout << "Extracting " << info.extractfile << " to " << target << std::endl;
|
||||||
|
out.write(data->getAsString().c_str(), data->size());
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int extractAll(Bsa::BSAFile& bsa, Arguments& info)
|
||||||
|
{
|
||||||
|
// Get the list of files present in the archive
|
||||||
|
Bsa::BSAFile::FileList list = bsa.getList();
|
||||||
|
|
||||||
|
// Iter on the list
|
||||||
|
for(Bsa::BSAFile::FileList::iterator it = list.begin(); it != list.end(); ++it) {
|
||||||
|
const char* archivePath = it->name;
|
||||||
|
|
||||||
|
std::string extractPath (archivePath);
|
||||||
|
replaceAll(extractPath, "\\", "/");
|
||||||
|
|
||||||
|
// Get the target path (the path the file will be extracted to)
|
||||||
|
bfs::path target (info.outdir);
|
||||||
|
target /= extractPath;
|
||||||
|
|
||||||
|
// Create the directory hierarchy
|
||||||
|
bfs::create_directories(target.parent_path());
|
||||||
|
|
||||||
|
bfs::file_status s = bfs::status(target.parent_path());
|
||||||
|
if (!bfs::is_directory(s))
|
||||||
|
{
|
||||||
|
std::cout << "ERROR: " << target.parent_path() << " is not a directory." << std::endl;
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a stream for the file to extract
|
||||||
|
// (inefficient because getFile iter on the list again)
|
||||||
|
Ogre::DataStreamPtr data = bsa.getFile(archivePath);
|
||||||
|
bfs::ofstream out(target, std::ios::binary);
|
||||||
|
|
||||||
|
// Write the file to disk
|
||||||
|
std::cout << "Extracting " << target << std::endl;
|
||||||
|
out.write(data->getAsString().c_str(), data->size());
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -165,23 +165,12 @@ bool parseOptions (int argc, char** argv, Arguments &info)
|
||||||
|
|
||||||
// Font encoding settings
|
// Font encoding settings
|
||||||
info.encoding = variables["encoding"].as<std::string>();
|
info.encoding = variables["encoding"].as<std::string>();
|
||||||
if (info.encoding == "win1250")
|
if(info.encoding != "win1250" && info.encoding != "win1251" && info.encoding != "win1252")
|
||||||
{
|
|
||||||
std::cout << "Using Central and Eastern European font encoding." << std::endl;
|
|
||||||
}
|
|
||||||
else if (info.encoding == "win1251")
|
|
||||||
{
|
|
||||||
std::cout << "Using Cyrillic font encoding." << std::endl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(info.encoding != "win1252")
|
|
||||||
{
|
{
|
||||||
std::cout << info.encoding << " is not a valid encoding option." << std::endl;
|
std::cout << info.encoding << " is not a valid encoding option." << std::endl;
|
||||||
info.encoding = "win1252";
|
info.encoding = "win1252";
|
||||||
}
|
}
|
||||||
std::cout << "Using default (English) font encoding." << std::endl;
|
std::cout << ToUTF8::encodingUsingMessage(info.encoding) << std::endl;
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -220,7 +209,10 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
|
||||||
bool save = (info.mode == "clone");
|
bool save = (info.mode == "clone");
|
||||||
|
|
||||||
// Skip back to the beginning of the reference list
|
// Skip back to the beginning of the reference list
|
||||||
cell.restore(esm);
|
// FIXME: Changes to the references backend required to support multiple plugins have
|
||||||
|
// almost certainly broken this following line. I'll leave it as is for now, so that
|
||||||
|
// the compiler does not complain.
|
||||||
|
cell.restore(esm, 0);
|
||||||
|
|
||||||
// Loop through all the references
|
// Loop through all the references
|
||||||
ESM::CellRef ref;
|
ESM::CellRef ref;
|
||||||
|
@ -262,7 +254,8 @@ void printRaw(ESM::ESMReader &esm)
|
||||||
int load(Arguments& info)
|
int load(Arguments& info)
|
||||||
{
|
{
|
||||||
ESM::ESMReader& esm = info.reader;
|
ESM::ESMReader& esm = info.reader;
|
||||||
esm.setEncoding(info.encoding);
|
ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(info.encoding));
|
||||||
|
esm.setEncoder(&encoder);
|
||||||
|
|
||||||
std::string filename = info.filename;
|
std::string filename = info.filename;
|
||||||
std::cout << "Loading file: " << filename << std::endl;
|
std::cout << "Loading file: " << filename << std::endl;
|
||||||
|
@ -432,7 +425,8 @@ int clone(Arguments& info)
|
||||||
std::cout << std::endl << "Saving records to: " << info.outname << "..." << std::endl;
|
std::cout << std::endl << "Saving records to: " << info.outname << "..." << std::endl;
|
||||||
|
|
||||||
ESM::ESMWriter& esm = info.writer;
|
ESM::ESMWriter& esm = info.writer;
|
||||||
esm.setEncoding(info.encoding);
|
ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(info.encoding));
|
||||||
|
esm.setEncoder(&encoder);
|
||||||
esm.setAuthor(info.data.author);
|
esm.setAuthor(info.data.author);
|
||||||
esm.setDescription(info.data.description);
|
esm.setDescription(info.data.description);
|
||||||
esm.setVersion(info.data.version);
|
esm.setVersion(info.data.version);
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
|
|
||||||
std::string bodyPartLabel(char idx)
|
std::string bodyPartLabel(int idx)
|
||||||
{
|
{
|
||||||
const char *bodyPartLabels[] = {
|
const char *bodyPartLabels[] = {
|
||||||
"Head",
|
"Head",
|
||||||
|
@ -48,13 +48,13 @@ std::string bodyPartLabel(char idx)
|
||||||
"Tail"
|
"Tail"
|
||||||
};
|
};
|
||||||
|
|
||||||
if ((int)idx >= 0 && (int)(idx) <= 26)
|
if (idx >= 0 && idx <= 26)
|
||||||
return bodyPartLabels[(int)(idx)];
|
return bodyPartLabels[idx];
|
||||||
else
|
else
|
||||||
return "Invalid";
|
return "Invalid";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string meshPartLabel(char idx)
|
std::string meshPartLabel(int idx)
|
||||||
{
|
{
|
||||||
const char *meshPartLabels[] = {
|
const char *meshPartLabels[] = {
|
||||||
"Head",
|
"Head",
|
||||||
|
@ -74,13 +74,13 @@ std::string meshPartLabel(char idx)
|
||||||
"Tail"
|
"Tail"
|
||||||
};
|
};
|
||||||
|
|
||||||
if ((int)(idx) >= 0 && (int)(idx) <= ESM::BodyPart::MP_Tail)
|
if (idx >= 0 && idx <= ESM::BodyPart::MP_Tail)
|
||||||
return meshPartLabels[(int)(idx)];
|
return meshPartLabels[idx];
|
||||||
else
|
else
|
||||||
return "Invalid";
|
return "Invalid";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string meshTypeLabel(char idx)
|
std::string meshTypeLabel(int idx)
|
||||||
{
|
{
|
||||||
const char *meshTypeLabels[] = {
|
const char *meshTypeLabels[] = {
|
||||||
"Skin",
|
"Skin",
|
||||||
|
@ -88,8 +88,8 @@ std::string meshTypeLabel(char idx)
|
||||||
"Armor"
|
"Armor"
|
||||||
};
|
};
|
||||||
|
|
||||||
if ((int)(idx) >= 0 && (int)(idx) <= ESM::BodyPart::MT_Armor)
|
if (idx >= 0 && idx <= ESM::BodyPart::MT_Armor)
|
||||||
return meshTypeLabels[(int)(idx)];
|
return meshTypeLabels[idx];
|
||||||
else
|
else
|
||||||
return "Invalid";
|
return "Invalid";
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
std::string bodyPartLabel(char idx);
|
std::string bodyPartLabel(int idx);
|
||||||
std::string meshPartLabel(char idx);
|
std::string meshPartLabel(int idx);
|
||||||
std::string meshTypeLabel(char idx);
|
std::string meshTypeLabel(int idx);
|
||||||
std::string clothingTypeLabel(int idx);
|
std::string clothingTypeLabel(int idx);
|
||||||
std::string armorTypeLabel(int idx);
|
std::string armorTypeLabel(int idx);
|
||||||
std::string dialogTypeLabel(int idx);
|
std::string dialogTypeLabel(int idx);
|
||||||
|
|
|
@ -112,14 +112,11 @@ std::string ruleString(ESM::DialInfo::SelectStruct ss)
|
||||||
case '5': oper_str = ">="; break;
|
case '5': oper_str = ">="; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string value_str = "??";
|
std::ostringstream stream;
|
||||||
if (ss.mType == ESM::VT_Int)
|
stream << ss.mValue;
|
||||||
value_str = str(boost::format("%d") % ss.mI);
|
|
||||||
else if (ss.mType == ESM::VT_Float)
|
|
||||||
value_str = str(boost::format("%f") % ss.mF);
|
|
||||||
|
|
||||||
std::string result = str(boost::format("%-12s %-32s %2s %s")
|
std::string result = str(boost::format("%-12s %-32s %2s %s")
|
||||||
% type_str % func_str % oper_str % value_str);
|
% type_str % func_str % oper_str % stream.str());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -713,32 +710,13 @@ void Record<ESM::Faction>::print()
|
||||||
template<>
|
template<>
|
||||||
void Record<ESM::Global>::print()
|
void Record<ESM::Global>::print()
|
||||||
{
|
{
|
||||||
// nothing to print (well, nothing that's correct anyway)
|
std::cout << " " << mData.mValue << std::endl;
|
||||||
std::cout << " Type: " << mData.mType << std::endl;
|
|
||||||
std::cout << " Value: " << mData.mValue << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void Record<ESM::GameSetting>::print()
|
void Record<ESM::GameSetting>::print()
|
||||||
{
|
{
|
||||||
std::cout << " Value: ";
|
std::cout << " " << mData.mValue << std::endl;
|
||||||
switch (mData.mType) {
|
|
||||||
case ESM::VT_String:
|
|
||||||
std::cout << "'" << mData.mStr << "' (std::string)";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ESM::VT_Float:
|
|
||||||
std::cout << mData.mF << " (float)";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ESM::VT_Int:
|
|
||||||
std::cout << mData.mI << " (int)";
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
std::cout << "unknown type";
|
|
||||||
}
|
|
||||||
std::cout << "\n Dirty: " << mData.mDirty << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
|
|
@ -5,14 +5,11 @@ set(LAUNCHER
|
||||||
maindialog.cpp
|
maindialog.cpp
|
||||||
playpage.cpp
|
playpage.cpp
|
||||||
|
|
||||||
model/datafilesmodel.cpp
|
settings/gamesettings.cpp
|
||||||
model/modelitem.cpp
|
settings/graphicssettings.cpp
|
||||||
model/esm/esmfile.cpp
|
settings/launchersettings.cpp
|
||||||
|
|
||||||
utils/filedialog.cpp
|
utils/checkablemessagebox.cpp
|
||||||
utils/naturalsort.cpp
|
|
||||||
utils/lineedit.cpp
|
|
||||||
utils/profilescombobox.cpp
|
|
||||||
utils/textinputdialog.cpp
|
utils/textinputdialog.cpp
|
||||||
|
|
||||||
launcher.rc
|
launcher.rc
|
||||||
|
@ -24,14 +21,12 @@ set(LAUNCHER_HEADER
|
||||||
maindialog.hpp
|
maindialog.hpp
|
||||||
playpage.hpp
|
playpage.hpp
|
||||||
|
|
||||||
model/datafilesmodel.hpp
|
settings/gamesettings.hpp
|
||||||
model/modelitem.hpp
|
settings/graphicssettings.hpp
|
||||||
model/esm/esmfile.hpp
|
settings/launchersettings.hpp
|
||||||
|
settings/settingsbase.hpp
|
||||||
|
|
||||||
utils/lineedit.hpp
|
utils/checkablemessagebox.hpp
|
||||||
utils/filedialog.hpp
|
|
||||||
utils/naturalsort.hpp
|
|
||||||
utils/profilescombobox.hpp
|
|
||||||
utils/textinputdialog.hpp
|
utils/textinputdialog.hpp
|
||||||
|
|
||||||
)
|
)
|
||||||
|
@ -43,17 +38,18 @@ set(LAUNCHER_HEADER_MOC
|
||||||
maindialog.hpp
|
maindialog.hpp
|
||||||
playpage.hpp
|
playpage.hpp
|
||||||
|
|
||||||
model/datafilesmodel.hpp
|
utils/checkablemessagebox.hpp
|
||||||
model/modelitem.hpp
|
|
||||||
model/esm/esmfile.hpp
|
|
||||||
|
|
||||||
utils/lineedit.hpp
|
|
||||||
utils/filedialog.hpp
|
|
||||||
utils/profilescombobox.hpp
|
|
||||||
utils/textinputdialog.hpp
|
utils/textinputdialog.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER} ${LAUNCHER_HEADER_MOC})
|
set(LAUNCHER_UI
|
||||||
|
../../files/ui/datafilespage.ui
|
||||||
|
../../files/ui/graphicspage.ui
|
||||||
|
../../files/ui/mainwindow.ui
|
||||||
|
../../files/ui/playpage.ui
|
||||||
|
)
|
||||||
|
|
||||||
|
source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER})
|
||||||
|
|
||||||
find_package(Qt4 REQUIRED)
|
find_package(Qt4 REQUIRED)
|
||||||
set(QT_USE_QTGUI 1)
|
set(QT_USE_QTGUI 1)
|
||||||
|
@ -64,10 +60,12 @@ if(WIN32)
|
||||||
set(QT_USE_QTMAIN TRUE)
|
set(QT_USE_QTMAIN TRUE)
|
||||||
endif(WIN32)
|
endif(WIN32)
|
||||||
|
|
||||||
QT4_ADD_RESOURCES(RCC_SRCS resources.qrc)
|
QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
|
||||||
QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
|
QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
|
||||||
|
QT4_WRAP_UI(UI_HDRS ${LAUNCHER_UI})
|
||||||
|
|
||||||
include(${QT_USE_FILE})
|
include(${QT_USE_FILE})
|
||||||
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
# Main executable
|
# Main executable
|
||||||
IF(OGRE_STATIC)
|
IF(OGRE_STATIC)
|
||||||
|
@ -82,8 +80,10 @@ ENDIF(OGRE_STATIC)
|
||||||
add_executable(omwlauncher
|
add_executable(omwlauncher
|
||||||
${GUI_TYPE}
|
${GUI_TYPE}
|
||||||
${LAUNCHER}
|
${LAUNCHER}
|
||||||
|
${LAUNCHER_HEADER}
|
||||||
${RCC_SRCS}
|
${RCC_SRCS}
|
||||||
${MOC_SRCS}
|
${MOC_SRCS}
|
||||||
|
${UI_HDRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(omwlauncher
|
target_link_libraries(omwlauncher
|
||||||
|
@ -98,18 +98,6 @@ if(DPKG_PROGRAM)
|
||||||
INSTALL(TARGETS omwlauncher RUNTIME DESTINATION games COMPONENT omwlauncher)
|
INSTALL(TARGETS omwlauncher RUNTIME DESTINATION games COMPONENT omwlauncher)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (APPLE)
|
|
||||||
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss
|
|
||||||
"${APP_BUNDLE_DIR}/../launcher.qss")
|
|
||||||
else()
|
|
||||||
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss
|
|
||||||
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/resources/launcher.qss")
|
|
||||||
|
|
||||||
# Fallback in case getGlobalDataPath does not point to resources
|
|
||||||
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss
|
|
||||||
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.qss")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_WITH_CODE_COVERAGE)
|
if (BUILD_WITH_CODE_COVERAGE)
|
||||||
add_definitions (--coverage)
|
add_definitions (--coverage)
|
||||||
target_link_libraries(omwlauncher gcov)
|
target_link_libraries(omwlauncher gcov)
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,93 +3,103 @@
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QModelIndex>
|
#include <QModelIndex>
|
||||||
#include "utils/profilescombobox.hpp"
|
|
||||||
#include <components/files/collections.hpp>
|
|
||||||
|
|
||||||
|
#include "ui_datafilespage.h"
|
||||||
|
|
||||||
class QTableView;
|
|
||||||
class QSortFilterProxyModel;
|
class QSortFilterProxyModel;
|
||||||
class QSettings;
|
class QAbstractItemModel;
|
||||||
class QAction;
|
class QAction;
|
||||||
class QToolBar;
|
|
||||||
class QMenu;
|
class QMenu;
|
||||||
class ProfilesComboBox;
|
|
||||||
class DataFilesModel;
|
|
||||||
|
|
||||||
|
class DataFilesModel;
|
||||||
class TextInputDialog;
|
class TextInputDialog;
|
||||||
|
class GameSettings;
|
||||||
|
class LauncherSettings;
|
||||||
|
class PluginsProxyModel;
|
||||||
|
|
||||||
namespace Files { struct ConfigurationManager; }
|
namespace Files { struct ConfigurationManager; }
|
||||||
|
|
||||||
class DataFilesPage : public QWidget
|
class DataFilesPage : public QWidget, private Ui::DataFilesPage
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DataFilesPage(Files::ConfigurationManager& cfg, QWidget *parent = 0);
|
DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent = 0);
|
||||||
|
|
||||||
ProfilesComboBox *mProfilesComboBox;
|
QAbstractItemModel* profilesComboBoxModel();
|
||||||
|
int profilesComboBoxIndex();
|
||||||
|
|
||||||
void writeConfig(QString profile = QString());
|
void writeConfig(QString profile = QString());
|
||||||
bool showDataFilesWarning();
|
void saveSettings();
|
||||||
bool setupDataFiles();
|
|
||||||
|
signals:
|
||||||
|
void profileChanged(int index);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setCheckState(QModelIndex index);
|
void setCheckState(QModelIndex index);
|
||||||
|
void setProfilesComboBoxIndex(int index);
|
||||||
|
|
||||||
void filterChanged(const QString filter);
|
void filterChanged(const QString filter);
|
||||||
void showContextMenu(const QPoint &point);
|
void showContextMenu(const QPoint &point);
|
||||||
void profileChanged(const QString &previous, const QString ¤t);
|
void profileChanged(const QString &previous, const QString ¤t);
|
||||||
void profileRenamed(const QString &previous, const QString ¤t);
|
void profileRenamed(const QString &previous, const QString ¤t);
|
||||||
void updateOkButton(const QString &text);
|
void updateOkButton(const QString &text);
|
||||||
|
void updateSplitter();
|
||||||
|
void updateViews();
|
||||||
|
|
||||||
// Action slots
|
// Action slots
|
||||||
void newProfile();
|
void newProfile();
|
||||||
void deleteProfile();
|
void deleteProfile();
|
||||||
// void moveUp();
|
|
||||||
// void moveDown();
|
|
||||||
// void moveTop();
|
|
||||||
// void moveBottom();
|
|
||||||
void check();
|
void check();
|
||||||
void uncheck();
|
void uncheck();
|
||||||
void refresh();
|
void refresh();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void slotCurrentIndexChanged(int index);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DataFilesModel *mMastersModel;
|
DataFilesModel *mDataFilesModel;
|
||||||
DataFilesModel *mPluginsModel;
|
|
||||||
|
|
||||||
QSortFilterProxyModel *mPluginsProxyModel;
|
PluginsProxyModel *mPluginsProxyModel;
|
||||||
|
QSortFilterProxyModel *mMastersProxyModel;
|
||||||
|
|
||||||
QTableView *mMastersTable;
|
QSortFilterProxyModel *mFilterProxyModel;
|
||||||
QTableView *mPluginsTable;
|
|
||||||
|
|
||||||
QToolBar *mProfileToolBar;
|
// QTableView *mMastersTable;
|
||||||
|
// QTableView *mPluginsTable;
|
||||||
|
|
||||||
|
|
||||||
|
// QToolBar *mProfileToolBar;
|
||||||
QMenu *mContextMenu;
|
QMenu *mContextMenu;
|
||||||
|
// QSplitter *mSplitter;
|
||||||
|
|
||||||
QAction *mNewProfileAction;
|
QAction *mNewProfileAction;
|
||||||
QAction *mDeleteProfileAction;
|
QAction *mDeleteProfileAction;
|
||||||
|
QAction *mCheckAction;
|
||||||
|
QAction *mUncheckAction;
|
||||||
|
|
||||||
// QAction *mMoveUpAction;
|
// QAction *mMoveUpAction;
|
||||||
// QAction *mMoveDownAction;
|
// QAction *mMoveDownAction;
|
||||||
// QAction *mMoveTopAction;
|
// QAction *mMoveTopAction;
|
||||||
// QAction *mMoveBottomAction;
|
// QAction *mMoveBottomAction;
|
||||||
QAction *mCheckAction;
|
|
||||||
QAction *mUncheckAction;
|
|
||||||
|
|
||||||
Files::ConfigurationManager &mCfgMgr;
|
Files::ConfigurationManager &mCfgMgr;
|
||||||
Files::PathContainer mDataDirs;
|
|
||||||
Files::PathContainer mDataLocal;
|
|
||||||
|
|
||||||
QSettings *mLauncherConfig;
|
GameSettings &mGameSettings;
|
||||||
|
LauncherSettings &mLauncherSettings;
|
||||||
|
|
||||||
TextInputDialog *mNewProfileDialog;
|
TextInputDialog *mNewProfileDialog;
|
||||||
|
|
||||||
// const QStringList checkedPlugins();
|
void setMastersCheckstates(Qt::CheckState state);
|
||||||
// const QStringList selectedMasters();
|
void setPluginsCheckstates(Qt::CheckState state);
|
||||||
|
|
||||||
void createActions();
|
void createActions();
|
||||||
|
void setupDataFiles();
|
||||||
void setupConfig();
|
void setupConfig();
|
||||||
void readConfig();
|
void readConfig();
|
||||||
|
|
||||||
|
void loadSettings();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
|
#include "graphicspage.hpp"
|
||||||
|
|
||||||
#include <QtGui>
|
#include <QtGui>
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
#include <boost/math/common_factor.hpp>
|
#include <boost/math/common_factor.hpp>
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
|
|
||||||
#include <components/files/configurationmanager.hpp>
|
#include <components/files/configurationmanager.hpp>
|
||||||
#include <components/files/ogreplugin.hpp>
|
#include <components/files/ogreplugin.hpp>
|
||||||
#include <components/settings/settings.hpp>
|
|
||||||
|
|
||||||
#include "utils/naturalsort.hpp"
|
#include <components/fileorderlist/utils/naturalsort.hpp>
|
||||||
|
|
||||||
#include "graphicspage.hpp"
|
#include "settings/graphicssettings.hpp"
|
||||||
|
|
||||||
QString getAspect(int x, int y)
|
QString getAspect(int x, int y)
|
||||||
{
|
{
|
||||||
|
@ -25,52 +25,22 @@ QString getAspect(int x, int y)
|
||||||
return QString(QString::number(xaspect) + ":" + QString::number(yaspect));
|
return QString(QString::number(xaspect) + ":" + QString::number(yaspect));
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, QWidget *parent)
|
GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSetting, QWidget *parent)
|
||||||
: QWidget(parent)
|
: mCfgMgr(cfg)
|
||||||
, mCfgMgr(cfg)
|
, mGraphicsSettings(graphicsSetting)
|
||||||
|
, QWidget(parent)
|
||||||
{
|
{
|
||||||
QGroupBox *rendererGroup = new QGroupBox(tr("Renderer"), this);
|
setupUi(this);
|
||||||
|
|
||||||
QLabel *rendererLabel = new QLabel(tr("Rendering Subsystem:"), rendererGroup);
|
// Set the maximum res we can set in windowed mode
|
||||||
mRendererComboBox = new QComboBox(rendererGroup);
|
QRect res = QApplication::desktop()->screenGeometry();
|
||||||
|
customWidthSpinBox->setMaximum(res.width());
|
||||||
|
customHeightSpinBox->setMaximum(res.height());
|
||||||
|
|
||||||
// Layout for the combobox and label
|
connect(rendererComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(rendererChanged(const QString&)));
|
||||||
QGridLayout *renderSystemLayout = new QGridLayout();
|
connect(fullScreenCheckBox, SIGNAL(stateChanged(int)), this, SLOT(slotFullScreenChanged(int)));
|
||||||
renderSystemLayout->addWidget(rendererLabel, 0, 0, 1, 1);
|
connect(standardRadioButton, SIGNAL(toggled(bool)), this, SLOT(slotStandardToggled(bool)));
|
||||||
renderSystemLayout->addWidget(mRendererComboBox, 0, 1, 1, 1);
|
|
||||||
|
|
||||||
// Display
|
|
||||||
QGroupBox *displayGroup = new QGroupBox(tr("Display"), this);
|
|
||||||
|
|
||||||
mVSyncCheckBox = new QCheckBox(tr("Vertical Sync"), displayGroup);
|
|
||||||
mFullScreenCheckBox = new QCheckBox(tr("Full Screen"), displayGroup);
|
|
||||||
|
|
||||||
QLabel *antiAliasingLabel = new QLabel(tr("Antialiasing:"), displayGroup);
|
|
||||||
QLabel *resolutionLabel = new QLabel(tr("Resolution:"), displayGroup);
|
|
||||||
|
|
||||||
mResolutionComboBox = new QComboBox(displayGroup);
|
|
||||||
mAntiAliasingComboBox = new QComboBox(displayGroup);
|
|
||||||
|
|
||||||
QVBoxLayout *rendererGroupLayout = new QVBoxLayout(rendererGroup);
|
|
||||||
rendererGroupLayout->addLayout(renderSystemLayout);
|
|
||||||
|
|
||||||
QGridLayout *displayGroupLayout = new QGridLayout(displayGroup);
|
|
||||||
displayGroupLayout->addWidget(mVSyncCheckBox, 0, 0, 1, 1);
|
|
||||||
displayGroupLayout->addWidget(mFullScreenCheckBox, 1, 0, 1, 1);
|
|
||||||
displayGroupLayout->addWidget(antiAliasingLabel, 2, 0, 1, 1);
|
|
||||||
displayGroupLayout->addWidget(mAntiAliasingComboBox, 2, 1, 1, 1);
|
|
||||||
displayGroupLayout->addWidget(resolutionLabel, 3, 0, 1, 1);
|
|
||||||
displayGroupLayout->addWidget(mResolutionComboBox, 3, 1, 1, 1);
|
|
||||||
|
|
||||||
// Layout for the whole page
|
|
||||||
QVBoxLayout *pageLayout = new QVBoxLayout(this);
|
|
||||||
QSpacerItem *vSpacer1 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
|
||||||
|
|
||||||
pageLayout->addWidget(rendererGroup);
|
|
||||||
pageLayout->addWidget(displayGroup);
|
|
||||||
pageLayout->addItem(vSpacer1);
|
|
||||||
|
|
||||||
connect(mRendererComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(rendererChanged(const QString&)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GraphicsPage::setupOgre()
|
bool GraphicsPage::setupOgre()
|
||||||
|
@ -117,11 +87,11 @@ bool GraphicsPage::setupOgre()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::filesystem::path absPluginPath = boost::filesystem::absolute(boost::filesystem::path(pluginDir));
|
QDir dir(QString::fromStdString(pluginDir));
|
||||||
|
pluginDir = dir.absolutePath().toStdString();
|
||||||
pluginDir = absPluginPath.string();
|
|
||||||
|
|
||||||
Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mOgre);
|
Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mOgre);
|
||||||
|
Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mOgre);
|
||||||
Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mOgre);
|
Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mOgre);
|
||||||
|
|
||||||
#ifdef ENABLE_PLUGIN_GL
|
#ifdef ENABLE_PLUGIN_GL
|
||||||
|
@ -138,7 +108,7 @@ bool GraphicsPage::setupOgre()
|
||||||
|
|
||||||
for (Ogre::RenderSystemList::const_iterator r = renderers.begin(); r != renderers.end(); ++r) {
|
for (Ogre::RenderSystemList::const_iterator r = renderers.begin(); r != renderers.end(); ++r) {
|
||||||
mSelectedRenderSystem = *r;
|
mSelectedRenderSystem = *r;
|
||||||
mRendererComboBox->addItem((*r)->getName().c_str());
|
rendererComboBox->addItem((*r)->getName().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
QString openGLName = QString("OpenGL Rendering Subsystem");
|
QString openGLName = QString("OpenGL Rendering Subsystem");
|
||||||
|
@ -156,69 +126,83 @@ bool GraphicsPage::setupOgre()
|
||||||
msgBox.setText(tr("<br><b>Could not select a valid render system</b><br><br> \
|
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 the plugins.cfg file exists and contains a valid rendering plugin.<br>"));
|
||||||
msgBox.exec();
|
msgBox.exec();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now fill the GUI elements
|
// Now fill the GUI elements
|
||||||
int index = mRendererComboBox->findText(QString::fromStdString(Settings::Manager::getString("render system", "Video")));
|
int index = rendererComboBox->findText(mGraphicsSettings.value(QString("Video/render system")));
|
||||||
|
|
||||||
if ( index != -1) {
|
if ( index != -1) {
|
||||||
mRendererComboBox->setCurrentIndex(index);
|
rendererComboBox->setCurrentIndex(index);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
|
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
|
||||||
mRendererComboBox->setCurrentIndex(mRendererComboBox->findText(direct3DName));
|
rendererComboBox->setCurrentIndex(rendererComboBox->findText(direct3DName));
|
||||||
#else
|
#else
|
||||||
mRendererComboBox->setCurrentIndex(mRendererComboBox->findText(openGLName));
|
rendererComboBox->setCurrentIndex(rendererComboBox->findText(openGLName));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
mAntiAliasingComboBox->clear();
|
antiAliasingComboBox->clear();
|
||||||
mResolutionComboBox->clear();
|
resolutionComboBox->clear();
|
||||||
mAntiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem));
|
antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem));
|
||||||
mResolutionComboBox->addItems(getAvailableResolutions(mSelectedRenderSystem));
|
resolutionComboBox->addItems(getAvailableResolutions(mSelectedRenderSystem));
|
||||||
|
|
||||||
readConfig();
|
// Load the rest of the values
|
||||||
|
loadSettings();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsPage::readConfig()
|
void GraphicsPage::loadSettings()
|
||||||
{
|
{
|
||||||
if (Settings::Manager::getBool("vsync", "Video"))
|
if (mGraphicsSettings.value(QString("Video/vsync")) == QLatin1String("true"))
|
||||||
mVSyncCheckBox->setCheckState(Qt::Checked);
|
vSyncCheckBox->setCheckState(Qt::Checked);
|
||||||
|
|
||||||
if (Settings::Manager::getBool("fullscreen", "Video"))
|
if (mGraphicsSettings.value(QString("Video/fullscreen")) == QLatin1String("true"))
|
||||||
mFullScreenCheckBox->setCheckState(Qt::Checked);
|
fullScreenCheckBox->setCheckState(Qt::Checked);
|
||||||
|
|
||||||
int aaIndex = mAntiAliasingComboBox->findText(QString::fromStdString(Settings::Manager::getString("antialiasing", "Video")));
|
int aaIndex = antiAliasingComboBox->findText(mGraphicsSettings.value(QString("Video/antialiasing")));
|
||||||
if (aaIndex != -1)
|
if (aaIndex != -1)
|
||||||
mAntiAliasingComboBox->setCurrentIndex(aaIndex);
|
antiAliasingComboBox->setCurrentIndex(aaIndex);
|
||||||
|
|
||||||
QString resolution = QString::number(Settings::Manager::getInt("resolution x", "Video"));
|
QString width = mGraphicsSettings.value(QString("Video/resolution x"));
|
||||||
resolution.append(" x " + QString::number(Settings::Manager::getInt("resolution y", "Video")));
|
QString height = mGraphicsSettings.value(QString("Video/resolution y"));
|
||||||
|
QString resolution = width + QString(" x ") + height;
|
||||||
|
|
||||||
int resIndex = mResolutionComboBox->findText(resolution, Qt::MatchStartsWith);
|
int resIndex = resolutionComboBox->findText(resolution, Qt::MatchStartsWith);
|
||||||
if (resIndex != -1)
|
|
||||||
mResolutionComboBox->setCurrentIndex(resIndex);
|
if (resIndex != -1) {
|
||||||
|
standardRadioButton->toggle();
|
||||||
|
resolutionComboBox->setCurrentIndex(resIndex);
|
||||||
|
} else {
|
||||||
|
customRadioButton->toggle();
|
||||||
|
customWidthSpinBox->setValue(width.toInt());
|
||||||
|
customHeightSpinBox->setValue(height.toInt());
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsPage::writeConfig()
|
void GraphicsPage::saveSettings()
|
||||||
{
|
{
|
||||||
Settings::Manager::setBool("vsync", "Video", mVSyncCheckBox->checkState());
|
vSyncCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/vsync"), QString("true"))
|
||||||
Settings::Manager::setBool("fullscreen", "Video", mFullScreenCheckBox->checkState());
|
: mGraphicsSettings.setValue(QString("Video/vsync"), QString("false"));
|
||||||
Settings::Manager::setString("antialiasing", "Video", mAntiAliasingComboBox->currentText().toStdString());
|
|
||||||
Settings::Manager::setString("render system", "Video", mRendererComboBox->currentText().toStdString());
|
|
||||||
|
|
||||||
// Get the current resolution, but with the tabs replaced with a single space
|
fullScreenCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("true"))
|
||||||
QString resolution = mResolutionComboBox->currentText().simplified();
|
: mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("false"));
|
||||||
QStringList tokens = resolution.split(" ", QString::SkipEmptyParts);
|
|
||||||
|
|
||||||
int resX = tokens.at(0).toInt();
|
mGraphicsSettings.setValue(QString("Video/antialiasing"), antiAliasingComboBox->currentText());
|
||||||
int resY = tokens.at(2).toInt();
|
mGraphicsSettings.setValue(QString("Video/render system"), rendererComboBox->currentText());
|
||||||
Settings::Manager::setInt("resolution x", "Video", resX);
|
|
||||||
Settings::Manager::setInt("resolution y", "Video", resY);
|
|
||||||
|
if (standardRadioButton->isChecked()) {
|
||||||
|
QRegExp resolutionRe(QString("(\\d+) x (\\d+).*"));
|
||||||
|
|
||||||
|
if (resolutionRe.exactMatch(resolutionComboBox->currentText().simplified())) {
|
||||||
|
mGraphicsSettings.setValue(QString("Video/resolution x"), resolutionRe.cap(1));
|
||||||
|
mGraphicsSettings.setValue(QString("Video/resolution y"), resolutionRe.cap(2));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mGraphicsSettings.setValue(QString("Video/resolution x"), QString::number(customWidthSpinBox->value()));
|
||||||
|
mGraphicsSettings.setValue(QString("Video/resolution y"), QString::number(customHeightSpinBox->value()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer)
|
QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer)
|
||||||
|
@ -232,16 +216,14 @@ QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSy
|
||||||
{
|
{
|
||||||
Ogre::StringVector::iterator opt_it;
|
Ogre::StringVector::iterator opt_it;
|
||||||
uint idx = 0;
|
uint idx = 0;
|
||||||
|
|
||||||
for (opt_it = i->second.possibleValues.begin();
|
for (opt_it = i->second.possibleValues.begin();
|
||||||
opt_it != i->second.possibleValues.end(); opt_it++, idx++)
|
opt_it != i->second.possibleValues.end(); opt_it++, idx++)
|
||||||
{
|
{
|
||||||
|
if (strcmp (key.toStdString().c_str(), i->first.c_str()) == 0) {
|
||||||
if (strcmp (key.toStdString().c_str(), i->first.c_str()) == 0)
|
|
||||||
{
|
|
||||||
result << ((key == "FSAA") ? QString("MSAA ") : QString("")) + QString::fromStdString((*opt_it).c_str()).simplified();
|
result << ((key == "FSAA") ? QString("MSAA ") : QString("")) + QString::fromStdString((*opt_it).c_str()).simplified();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort ascending
|
// Sort ascending
|
||||||
|
@ -275,24 +257,26 @@ QStringList GraphicsPage::getAvailableResolutions(Ogre::RenderSystem *renderer)
|
||||||
for (opt_it = i->second.possibleValues.begin ();
|
for (opt_it = i->second.possibleValues.begin ();
|
||||||
opt_it != i->second.possibleValues.end (); opt_it++, idx++)
|
opt_it != i->second.possibleValues.end (); opt_it++, idx++)
|
||||||
{
|
{
|
||||||
QString qval = QString::fromStdString(*opt_it).simplified();
|
QRegExp resolutionRe(QString("(\\d+) x (\\d+)"));
|
||||||
// remove extra tokens after the resolution (for example bpp, can be there or not depending on rendersystem)
|
QString resolution = QString::fromStdString(*opt_it).simplified();
|
||||||
QStringList tokens = qval.split(" ", QString::SkipEmptyParts);
|
|
||||||
assert (tokens.size() >= 3);
|
|
||||||
QString resolutionStr = tokens.at(0) + QString(" x ") + tokens.at(2);
|
|
||||||
|
|
||||||
QString aspect = getAspect(tokens.at(0).toInt(),tokens.at(2).toInt());
|
if (resolutionRe.exactMatch(resolution)) {
|
||||||
|
|
||||||
|
int width = resolutionRe.cap(1).toInt();
|
||||||
|
int height = resolutionRe.cap(2).toInt();
|
||||||
|
|
||||||
|
QString aspect = getAspect(width, height);
|
||||||
|
|
||||||
if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) {
|
if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) {
|
||||||
resolutionStr.append(tr("\t(Widescreen ") + aspect + ")");
|
resolution.append(tr("\t(Wide ") + aspect + ")");
|
||||||
|
|
||||||
} else if (aspect == QLatin1String("4:3")) {
|
} else if (aspect == QLatin1String("4:3")) {
|
||||||
resolutionStr.append(tr("\t(Standard 4:3)"));
|
resolution.append(tr("\t(Standard 4:3)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not add duplicate resolutions
|
// do not add duplicate resolutions
|
||||||
if (!result.contains(resolutionStr))
|
if (!result.contains(resolution))
|
||||||
result << resolutionStr;
|
result.append(resolution);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,9 +290,36 @@ void GraphicsPage::rendererChanged(const QString &renderer)
|
||||||
{
|
{
|
||||||
mSelectedRenderSystem = mOgre->getRenderSystemByName(renderer.toStdString());
|
mSelectedRenderSystem = mOgre->getRenderSystemByName(renderer.toStdString());
|
||||||
|
|
||||||
mAntiAliasingComboBox->clear();
|
antiAliasingComboBox->clear();
|
||||||
mResolutionComboBox->clear();
|
resolutionComboBox->clear();
|
||||||
|
|
||||||
mAntiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem));
|
antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem));
|
||||||
mResolutionComboBox->addItems(getAvailableResolutions(mSelectedRenderSystem));
|
resolutionComboBox->addItems(getAvailableResolutions(mSelectedRenderSystem));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsPage::slotFullScreenChanged(int state)
|
||||||
|
{
|
||||||
|
if (state == Qt::Checked) {
|
||||||
|
standardRadioButton->toggle();
|
||||||
|
customRadioButton->setEnabled(false);
|
||||||
|
customWidthSpinBox->setEnabled(false);
|
||||||
|
customHeightSpinBox->setEnabled(false);
|
||||||
|
} else {
|
||||||
|
customRadioButton->setEnabled(true);
|
||||||
|
customWidthSpinBox->setEnabled(true);
|
||||||
|
customHeightSpinBox->setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsPage::slotStandardToggled(bool checked)
|
||||||
|
{
|
||||||
|
if (checked) {
|
||||||
|
resolutionComboBox->setEnabled(true);
|
||||||
|
customWidthSpinBox->setEnabled(false);
|
||||||
|
customHeightSpinBox->setEnabled(false);
|
||||||
|
} else {
|
||||||
|
resolutionComboBox->setEnabled(false);
|
||||||
|
customWidthSpinBox->setEnabled(true);
|
||||||
|
customHeightSpinBox->setEnabled(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
|
|
||||||
#include <OgreRoot.h>
|
#include <OgreRoot.h>
|
||||||
#include <OgreRenderSystem.h>
|
#include <OgreRenderSystem.h>
|
||||||
#include <OgreConfigFile.h>
|
//#include <OgreConfigFile.h>
|
||||||
#include <OgreConfigDialog.h>
|
//#include <OgreConfigDialog.h>
|
||||||
|
|
||||||
// Static plugin headers
|
// Static plugin headers
|
||||||
#ifdef ENABLE_PLUGIN_GL
|
#ifdef ENABLE_PLUGIN_GL
|
||||||
|
@ -16,26 +16,29 @@
|
||||||
# include "OgreD3D9Plugin.h"
|
# include "OgreD3D9Plugin.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class QComboBox;
|
#include "ui_graphicspage.h"
|
||||||
class QCheckBox;
|
|
||||||
class QStackedWidget;
|
class GraphicsSettings;
|
||||||
class QSettings;
|
|
||||||
|
|
||||||
namespace Files { struct ConfigurationManager; }
|
namespace Files { struct ConfigurationManager; }
|
||||||
|
|
||||||
class GraphicsPage : public QWidget
|
class GraphicsPage : public QWidget, private Ui::GraphicsPage
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GraphicsPage(Files::ConfigurationManager &cfg, QWidget *parent = 0);
|
GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0);
|
||||||
|
|
||||||
|
void saveSettings();
|
||||||
bool setupOgre();
|
bool setupOgre();
|
||||||
void writeConfig();
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void rendererChanged(const QString &renderer);
|
void rendererChanged(const QString &renderer);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void slotFullScreenChanged(int state);
|
||||||
|
void slotStandardToggled(bool checked);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ogre::Root *mOgre;
|
Ogre::Root *mOgre;
|
||||||
Ogre::RenderSystem *mSelectedRenderSystem;
|
Ogre::RenderSystem *mSelectedRenderSystem;
|
||||||
|
@ -48,22 +51,14 @@ private:
|
||||||
Ogre::D3D9Plugin* mD3D9Plugin;
|
Ogre::D3D9Plugin* mD3D9Plugin;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QComboBox *mRendererComboBox;
|
|
||||||
|
|
||||||
QStackedWidget *mDisplayStackedWidget;
|
|
||||||
|
|
||||||
QComboBox *mAntiAliasingComboBox;
|
|
||||||
QComboBox *mResolutionComboBox;
|
|
||||||
QCheckBox *mVSyncCheckBox;
|
|
||||||
QCheckBox *mFullScreenCheckBox;
|
|
||||||
|
|
||||||
Files::ConfigurationManager &mCfgMgr;
|
Files::ConfigurationManager &mCfgMgr;
|
||||||
|
GraphicsSettings &mGraphicsSettings;
|
||||||
|
|
||||||
QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer);
|
QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer);
|
||||||
QStringList getAvailableResolutions(Ogre::RenderSystem *renderer);
|
QStringList getAvailableResolutions(Ogre::RenderSystem *renderer);
|
||||||
|
|
||||||
void createPages();
|
void loadSettings();
|
||||||
void readConfig();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QTextCodec>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
|
||||||
|
|
||||||
#include "maindialog.hpp"
|
#include "maindialog.hpp"
|
||||||
|
|
||||||
|
@ -30,14 +30,17 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
QDir::setCurrent(dir.absolutePath());
|
QDir::setCurrent(dir.absolutePath());
|
||||||
|
|
||||||
|
// Support non-latin characters
|
||||||
|
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
|
||||||
|
|
||||||
MainDialog mainWin;
|
MainDialog mainWin;
|
||||||
|
|
||||||
if (mainWin.setup()) {
|
if (mainWin.setup()) {
|
||||||
|
|
||||||
mainWin.show();
|
mainWin.show();
|
||||||
return app.exec();
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,52 +1,16 @@
|
||||||
|
#include "maindialog.hpp"
|
||||||
|
|
||||||
#include <QtGui>
|
#include <QtGui>
|
||||||
|
|
||||||
#include "maindialog.hpp"
|
#include "utils/checkablemessagebox.hpp"
|
||||||
|
|
||||||
#include "playpage.hpp"
|
#include "playpage.hpp"
|
||||||
#include "graphicspage.hpp"
|
#include "graphicspage.hpp"
|
||||||
#include "datafilespage.hpp"
|
#include "datafilespage.hpp"
|
||||||
|
|
||||||
MainDialog::MainDialog()
|
MainDialog::MainDialog()
|
||||||
|
: mGameSettings(mCfgMgr)
|
||||||
{
|
{
|
||||||
QWidget *centralWidget = new QWidget(this);
|
|
||||||
setCentralWidget(centralWidget);
|
|
||||||
|
|
||||||
mIconWidget = new QListWidget(centralWidget);
|
|
||||||
mIconWidget->setObjectName("IconWidget");
|
|
||||||
mIconWidget->setViewMode(QListView::IconMode);
|
|
||||||
mIconWidget->setWrapping(false);
|
|
||||||
mIconWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Just to be sure
|
|
||||||
mIconWidget->setIconSize(QSize(48, 48));
|
|
||||||
mIconWidget->setMovement(QListView::Static);
|
|
||||||
|
|
||||||
mIconWidget->setMinimumWidth(400);
|
|
||||||
mIconWidget->setFixedHeight(80);
|
|
||||||
mIconWidget->setSpacing(4);
|
|
||||||
mIconWidget->setCurrentRow(0);
|
|
||||||
mIconWidget->setFlow(QListView::LeftToRight);
|
|
||||||
|
|
||||||
QGroupBox *groupBox = new QGroupBox(centralWidget);
|
|
||||||
QVBoxLayout *groupLayout = new QVBoxLayout(groupBox);
|
|
||||||
|
|
||||||
mPagesWidget = new QStackedWidget(groupBox);
|
|
||||||
groupLayout->addWidget(mPagesWidget);
|
|
||||||
|
|
||||||
QPushButton *playButton = new QPushButton(tr("Play"));
|
|
||||||
|
|
||||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(centralWidget);
|
|
||||||
buttonBox->setStandardButtons(QDialogButtonBox::Close);
|
|
||||||
buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole);
|
|
||||||
|
|
||||||
QVBoxLayout *dialogLayout = new QVBoxLayout(centralWidget);
|
|
||||||
dialogLayout->addWidget(mIconWidget);
|
|
||||||
dialogLayout->addWidget(groupBox);
|
|
||||||
dialogLayout->addWidget(buttonBox);
|
|
||||||
|
|
||||||
setWindowTitle(tr("OpenMW Launcher"));
|
|
||||||
setWindowIcon(QIcon(":/images/openmw.png"));
|
|
||||||
// Remove what's this? button
|
|
||||||
setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
|
||||||
setMinimumSize(QSize(575, 575));
|
|
||||||
|
|
||||||
// Install the stylesheet font
|
// Install the stylesheet font
|
||||||
QFile file;
|
QFile file;
|
||||||
QFontDatabase fontDatabase;
|
QFontDatabase fontDatabase;
|
||||||
|
@ -56,64 +20,67 @@ MainDialog::MainDialog()
|
||||||
// Check if the font is installed
|
// Check if the font is installed
|
||||||
if (!fonts.contains("EB Garamond")) {
|
if (!fonts.contains("EB Garamond")) {
|
||||||
|
|
||||||
QString font = QString::fromStdString((mCfgMgr.getGlobalDataPath() / "resources/mygui/EBGaramond-Regular.ttf").string());
|
QString font = QString::fromStdString(mCfgMgr.getGlobalDataPath().string()) + QString("resources/mygui/EBGaramond-Regular.ttf");
|
||||||
file.setFileName(font);
|
file.setFileName(font);
|
||||||
|
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
font = QString::fromStdString((mCfgMgr.getLocalPath() / "resources/mygui/EBGaramond-Regular.ttf").string());
|
font = QString::fromStdString(mCfgMgr.getLocalPath().string()) + QString("resources/mygui/EBGaramond-Regular.ttf");
|
||||||
}
|
}
|
||||||
|
|
||||||
fontDatabase.addApplicationFont(font);
|
fontDatabase.addApplicationFont(font);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the stylesheet
|
setupUi(this);
|
||||||
QString config = QString::fromStdString((mCfgMgr.getGlobalDataPath() / "resources/launcher.qss").string());
|
|
||||||
file.setFileName(config);
|
|
||||||
|
|
||||||
if (!file.exists()) {
|
iconWidget->setViewMode(QListView::IconMode);
|
||||||
file.setFileName(QString::fromStdString((mCfgMgr.getLocalPath() / "launcher.qss").string()));
|
iconWidget->setWrapping(false);
|
||||||
}
|
iconWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Just to be sure
|
||||||
|
iconWidget->setIconSize(QSize(48, 48));
|
||||||
|
iconWidget->setMovement(QListView::Static);
|
||||||
|
|
||||||
file.open(QFile::ReadOnly);
|
iconWidget->setSpacing(4);
|
||||||
QString styleSheet = QLatin1String(file.readAll());
|
iconWidget->setCurrentRow(0);
|
||||||
qApp->setStyleSheet(styleSheet);
|
iconWidget->setFlow(QListView::LeftToRight);
|
||||||
file.close();
|
|
||||||
|
QPushButton *playButton = new QPushButton(tr("Play"));
|
||||||
|
buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole);
|
||||||
|
|
||||||
connect(buttonBox, SIGNAL(rejected()), this, SLOT(close()));
|
connect(buttonBox, SIGNAL(rejected()), this, SLOT(close()));
|
||||||
connect(buttonBox, SIGNAL(accepted()), this, SLOT(play()));
|
connect(buttonBox, SIGNAL(accepted()), this, SLOT(play()));
|
||||||
|
|
||||||
|
// Remove what's this? button
|
||||||
|
setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
|
|
||||||
createIcons();
|
createIcons();
|
||||||
createPages();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainDialog::createIcons()
|
void MainDialog::createIcons()
|
||||||
{
|
{
|
||||||
if (!QIcon::hasThemeIcon("document-new")) {
|
if (!QIcon::hasThemeIcon("document-new"))
|
||||||
QIcon::setThemeName("tango");
|
QIcon::setThemeName("tango");
|
||||||
}
|
|
||||||
|
|
||||||
// We create a fallback icon because the default fallback doesn't work
|
// We create a fallback icon because the default fallback doesn't work
|
||||||
QIcon graphicsIcon = QIcon(":/icons/tango/video-display.png");
|
QIcon graphicsIcon = QIcon(":/icons/tango/video-display.png");
|
||||||
|
|
||||||
QListWidgetItem *playButton = new QListWidgetItem(mIconWidget);
|
QListWidgetItem *playButton = new QListWidgetItem(iconWidget);
|
||||||
playButton->setIcon(QIcon(":/images/openmw.png"));
|
playButton->setIcon(QIcon(":/images/openmw.png"));
|
||||||
playButton->setText(tr("Play"));
|
playButton->setText(tr("Play"));
|
||||||
playButton->setTextAlignment(Qt::AlignCenter);
|
playButton->setTextAlignment(Qt::AlignCenter);
|
||||||
playButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
playButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
||||||
QListWidgetItem *graphicsButton = new QListWidgetItem(mIconWidget);
|
QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget);
|
||||||
graphicsButton->setIcon(QIcon::fromTheme("video-display", graphicsIcon));
|
graphicsButton->setIcon(QIcon::fromTheme("video-display", graphicsIcon));
|
||||||
graphicsButton->setText(tr("Graphics"));
|
graphicsButton->setText(tr("Graphics"));
|
||||||
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
|
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
|
||||||
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
||||||
QListWidgetItem *dataFilesButton = new QListWidgetItem(mIconWidget);
|
QListWidgetItem *dataFilesButton = new QListWidgetItem(iconWidget);
|
||||||
dataFilesButton->setIcon(QIcon(":/images/openmw-plugin.png"));
|
dataFilesButton->setIcon(QIcon(":/images/openmw-plugin.png"));
|
||||||
dataFilesButton->setText(tr("Data Files"));
|
dataFilesButton->setText(tr("Data Files"));
|
||||||
dataFilesButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
dataFilesButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
||||||
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
||||||
connect(mIconWidget,
|
connect(iconWidget,
|
||||||
SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
|
SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
|
||||||
this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*)));
|
this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*)));
|
||||||
|
|
||||||
|
@ -122,77 +89,185 @@ void MainDialog::createIcons()
|
||||||
void MainDialog::createPages()
|
void MainDialog::createPages()
|
||||||
{
|
{
|
||||||
mPlayPage = new PlayPage(this);
|
mPlayPage = new PlayPage(this);
|
||||||
mGraphicsPage = new GraphicsPage(mCfgMgr, this);
|
mGraphicsPage = new GraphicsPage(mCfgMgr, mGraphicsSettings, this);
|
||||||
mDataFilesPage = new DataFilesPage(mCfgMgr, this);
|
mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
|
||||||
|
|
||||||
// Set the combobox of the play page to imitate the combobox on the datafilespage
|
// Set the combobox of the play page to imitate the combobox on the datafilespage
|
||||||
mPlayPage->mProfilesComboBox->setModel(mDataFilesPage->mProfilesComboBox->model());
|
mPlayPage->setProfilesComboBoxModel(mDataFilesPage->profilesComboBoxModel());
|
||||||
mPlayPage->mProfilesComboBox->setCurrentIndex(mDataFilesPage->mProfilesComboBox->currentIndex());
|
mPlayPage->setProfilesComboBoxIndex(mDataFilesPage->profilesComboBoxIndex());
|
||||||
|
|
||||||
// Add the pages to the stacked widget
|
// Add the pages to the stacked widget
|
||||||
mPagesWidget->addWidget(mPlayPage);
|
pagesWidget->addWidget(mPlayPage);
|
||||||
mPagesWidget->addWidget(mGraphicsPage);
|
pagesWidget->addWidget(mGraphicsPage);
|
||||||
mPagesWidget->addWidget(mDataFilesPage);
|
pagesWidget->addWidget(mDataFilesPage);
|
||||||
|
|
||||||
// Select the first page
|
// Select the first page
|
||||||
mIconWidget->setCurrentItem(mIconWidget->item(0), QItemSelectionModel::Select);
|
iconWidget->setCurrentItem(iconWidget->item(0), QItemSelectionModel::Select);
|
||||||
|
|
||||||
connect(mPlayPage->mPlayButton, SIGNAL(clicked()), this, SLOT(play()));
|
connect(mPlayPage, SIGNAL(playButtonClicked()), this, SLOT(play()));
|
||||||
|
|
||||||
connect(mPlayPage->mProfilesComboBox,
|
connect(mPlayPage, SIGNAL(profileChanged(int)), mDataFilesPage, SLOT(setProfilesComboBoxIndex(int)));
|
||||||
SIGNAL(currentIndexChanged(int)),
|
connect(mDataFilesPage, SIGNAL(profileChanged(int)), mPlayPage, SLOT(setProfilesComboBoxIndex(int)));
|
||||||
mDataFilesPage->mProfilesComboBox, SLOT(setCurrentIndex(int)));
|
|
||||||
|
|
||||||
connect(mDataFilesPage->mProfilesComboBox,
|
|
||||||
SIGNAL(currentIndexChanged(int)),
|
|
||||||
mPlayPage->mProfilesComboBox, SLOT(setCurrentIndex(int)));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MainDialog::showFirstRunDialog()
|
||||||
bool MainDialog::setup()
|
|
||||||
{
|
{
|
||||||
// Create the settings manager and load default settings file
|
CheckableMessageBox msgBox(this);
|
||||||
const std::string localdefault = (mCfgMgr.getLocalPath() / "settings-default.cfg").string();
|
msgBox.setWindowTitle(tr("Morrowind installation detected"));
|
||||||
const std::string globaldefault = (mCfgMgr.getGlobalPath() / "settings-default.cfg").string();
|
|
||||||
|
|
||||||
// prefer local
|
QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxQuestion);
|
||||||
if (boost::filesystem::exists(localdefault)) {
|
int size = QApplication::style()->pixelMetric(QStyle::PM_MessageBoxIconSize);
|
||||||
mSettings.loadDefault(localdefault);
|
msgBox.setIconPixmap(icon.pixmap(size, size));
|
||||||
} else if (boost::filesystem::exists(globaldefault)) {
|
|
||||||
mSettings.loadDefault(globaldefault);
|
|
||||||
} else {
|
QAbstractButton *importerButton =
|
||||||
|
msgBox.addButton(tr("Import"), QDialogButtonBox::AcceptRole); // ActionRole doesn't work?!
|
||||||
|
QAbstractButton *skipButton =
|
||||||
|
msgBox.addButton(tr("Skip"), QDialogButtonBox::RejectRole);
|
||||||
|
|
||||||
|
Q_UNUSED(skipButton); // Surpress compiler unused warning
|
||||||
|
|
||||||
|
msgBox.setStandardButtons(QDialogButtonBox::NoButton);
|
||||||
|
|
||||||
|
msgBox.setText(tr("<br><b>An existing Morrowind installation was detected</b><br><br> \
|
||||||
|
Would you like to import settings from Morrowind.ini?<br>"));
|
||||||
|
|
||||||
|
msgBox.setCheckBoxText(tr("Include selected masters and plugins (creates a new profile)"));
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
|
||||||
|
if (msgBox.clickedButton() == importerButton) {
|
||||||
|
|
||||||
|
QStringList iniPaths;
|
||||||
|
|
||||||
|
foreach (const QString &path, mGameSettings.getDataDirs()) {
|
||||||
|
QDir dir(path);
|
||||||
|
dir.setPath(dir.canonicalPath()); // Resolve symlinks
|
||||||
|
|
||||||
|
if (!dir.cdUp())
|
||||||
|
continue; // Cannot move from Data Files
|
||||||
|
|
||||||
|
if (dir.exists(QString("Morrowind.ini")))
|
||||||
|
iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iniPaths.isEmpty()) {
|
||||||
QMessageBox msgBox;
|
QMessageBox msgBox;
|
||||||
msgBox.setWindowTitle("Error reading OpenMW configuration file");
|
msgBox.setWindowTitle(tr("Error reading Morrowind configuration file"));
|
||||||
msgBox.setIcon(QMessageBox::Critical);
|
msgBox.setIcon(QMessageBox::Warning);
|
||||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
msgBox.setText(tr("<br><b>Could not find %0</b><br><br> \
|
msgBox.setText(QObject::tr("<br><b>Could not find Morrowind.ini</b><br><br> \
|
||||||
The problem may be due to an incomplete installation of OpenMW.<br> \
|
The problem may be due to an incomplete installation of Morrowind.<br> \
|
||||||
Reinstalling OpenMW may resolve the problem.").arg(QString::fromStdString(globaldefault)));
|
Reinstalling Morrowind may resolve the problem."));
|
||||||
msgBox.exec();
|
msgBox.exec();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// load user settings if they exist, otherwise just load the default settings as user settings
|
if (iniPaths.count() > 1) {
|
||||||
const std::string settingspath = (mCfgMgr.getUserPath() / "settings.cfg").string();
|
// Multiple Morrowind.ini files found
|
||||||
|
bool ok;
|
||||||
|
QString path = QInputDialog::getItem(this, tr("Multiple configurations found"),
|
||||||
|
tr("<br><b>There are multiple Morrowind.ini files found.</b><br><br> \
|
||||||
|
Please select the one you wish to import from:"), iniPaths, 0, false, &ok);
|
||||||
|
if (ok && !path.isEmpty()) {
|
||||||
|
iniPaths.clear();
|
||||||
|
iniPaths.append(path);
|
||||||
|
} else {
|
||||||
|
// Cancel was clicked TODO: should we abort here?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (boost::filesystem::exists(settingspath))
|
// Create the file if it doesn't already exist, else the importer will fail
|
||||||
mSettings.loadUser(settingspath);
|
QString path = QString::fromStdString(mCfgMgr.getUserPath().string()) + QString("openmw.cfg");
|
||||||
else if (boost::filesystem::exists(localdefault))
|
QFile file(path);
|
||||||
mSettings.loadUser(localdefault);
|
|
||||||
else if (boost::filesystem::exists(globaldefault))
|
|
||||||
mSettings.loadUser(globaldefault);
|
|
||||||
|
|
||||||
// Setup the Graphics page
|
if (!file.exists()) {
|
||||||
if (!mGraphicsPage->setupOgre()) {
|
if (!file.open(QIODevice::ReadWrite)) {
|
||||||
|
// File cannot be created
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
|
||||||
|
Please make sure you have the right permissions \
|
||||||
|
and try again.<br>").arg(file.fileName()));
|
||||||
|
msgBox.exec();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the Data Files page
|
file.close();
|
||||||
if (!mDataFilesPage->setupDataFiles()) {
|
}
|
||||||
|
|
||||||
|
// Construct the arguments to run the importer
|
||||||
|
QStringList arguments;
|
||||||
|
|
||||||
|
if (msgBox.isChecked())
|
||||||
|
arguments.append(QString("-g"));
|
||||||
|
|
||||||
|
arguments.append(iniPaths.first());
|
||||||
|
arguments.append(path);
|
||||||
|
|
||||||
|
if (!startProgram(QString("mwiniimport"), arguments, false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Re-read the game settings
|
||||||
|
mGameSettings.clear();
|
||||||
|
|
||||||
|
if (!setupGameSettings())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Add a new profile
|
||||||
|
if (msgBox.isChecked()) {
|
||||||
|
mLauncherSettings.setValue(QString("Profiles/CurrentProfile"), QString("Imported"));
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MainDialog::setup()
|
||||||
|
{
|
||||||
|
if (!setupLauncherSettings())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!setupGameSettings())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!setupGraphicsSettings())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if we need to show the importer
|
||||||
|
if (mLauncherSettings.value(QString("General/firstrun"), QString("true")) == QLatin1String("true"))
|
||||||
|
{
|
||||||
|
if (!showFirstRunDialog())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now create the pages as they need the settings
|
||||||
|
createPages();
|
||||||
|
|
||||||
|
// Call this so we can exit on Ogre errors before mainwindow is shown
|
||||||
|
if (!mGraphicsPage->setupOgre())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
loadSettings();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,89 +276,409 @@ void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous)
|
||||||
if (!current)
|
if (!current)
|
||||||
current = previous;
|
current = previous;
|
||||||
|
|
||||||
mPagesWidget->setCurrentIndex(mIconWidget->row(current));
|
pagesWidget->setCurrentIndex(iconWidget->row(current));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MainDialog::setupLauncherSettings()
|
||||||
|
{
|
||||||
|
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
|
||||||
|
|
||||||
|
QStringList paths;
|
||||||
|
paths.append(QString("launcher.cfg"));
|
||||||
|
paths.append(userPath + QString("launcher.cfg"));
|
||||||
|
|
||||||
|
foreach (const QString &path, paths) {
|
||||||
|
qDebug() << "Loading config file:" << qPrintable(path);
|
||||||
|
QFile file(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"));
|
||||||
|
|
||||||
|
mLauncherSettings.readFile(stream);
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MainDialog::setupGameSettings()
|
||||||
|
{
|
||||||
|
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
|
||||||
|
QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string());
|
||||||
|
|
||||||
|
QStringList paths;
|
||||||
|
paths.append(userPath + QString("openmw.cfg"));
|
||||||
|
paths.append(QString("openmw.cfg"));
|
||||||
|
paths.append(globalPath + QString("openmw.cfg"));
|
||||||
|
|
||||||
|
foreach (const QString &path, paths) {
|
||||||
|
qDebug() << "Loading config file:" << qPrintable(path);
|
||||||
|
|
||||||
|
QFile file(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.readFile(stream);
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList dataDirs;
|
||||||
|
|
||||||
|
// Check if the paths actually contain data files
|
||||||
|
foreach (const QString path, mGameSettings.getDataDirs()) {
|
||||||
|
QDir dir(path);
|
||||||
|
QStringList filters;
|
||||||
|
filters << "*.esp" << "*.esm";
|
||||||
|
|
||||||
|
if (!dir.entryList(filters).isEmpty())
|
||||||
|
dataDirs.append(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataDirs.isEmpty())
|
||||||
|
{
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle(tr("Error detecting Morrowind installation"));
|
||||||
|
msgBox.setIcon(QMessageBox::Warning);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Cancel);
|
||||||
|
msgBox.setText(QObject::tr("<br><b>Could not find the Data Files location</b><br><br> \
|
||||||
|
The directory containing the data files was not found.<br><br> \
|
||||||
|
Press \"Browse...\" to specify the location manually.<br>"));
|
||||||
|
|
||||||
|
QAbstractButton *dirSelectButton =
|
||||||
|
msgBox.addButton(QObject::tr("B&rowse..."), QMessageBox::ActionRole);
|
||||||
|
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
QString selectedFile;
|
||||||
|
if (msgBox.clickedButton() == dirSelectButton) {
|
||||||
|
selectedFile = QFileDialog::getOpenFileName(
|
||||||
|
NULL,
|
||||||
|
QObject::tr("Select master file"),
|
||||||
|
QDir::currentPath(),
|
||||||
|
QString(tr("Morrowind master file (*.esm)")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedFile.isEmpty())
|
||||||
|
return false; // Cancel was clicked;
|
||||||
|
|
||||||
|
QFileInfo info(selectedFile);
|
||||||
|
|
||||||
|
// Add the new dir to the settings file and to the data dir container
|
||||||
|
mGameSettings.setValue(QString("data"), info.absolutePath());
|
||||||
|
mGameSettings.addDataDir(info.absolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MainDialog::setupGraphicsSettings()
|
||||||
|
{
|
||||||
|
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
|
||||||
|
QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string());
|
||||||
|
|
||||||
|
QFile localDefault(QString("settings-default.cfg"));
|
||||||
|
QFile globalDefault(globalPath + QString("settings-default.cfg"));
|
||||||
|
|
||||||
|
if (!localDefault.exists() && !globalDefault.exists()) {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle(tr("Error reading OpenMW configuration file"));
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(QObject::tr("<br><b>Could not find settings-default.cfg</b><br><br> \
|
||||||
|
The problem may be due to an incomplete installation of OpenMW.<br> \
|
||||||
|
Reinstalling OpenMW may resolve the problem."));
|
||||||
|
msgBox.exec();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QStringList paths;
|
||||||
|
paths.append(globalPath + QString("settings-default.cfg"));
|
||||||
|
paths.append(QString("settings-default.cfg"));
|
||||||
|
paths.append(userPath + QString("settings.cfg"));
|
||||||
|
|
||||||
|
foreach (const QString &path, paths) {
|
||||||
|
qDebug() << "Loading config file:" << qPrintable(path);
|
||||||
|
QFile file(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"));
|
||||||
|
|
||||||
|
mGraphicsSettings.readFile(stream);
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainDialog::loadSettings()
|
||||||
|
{
|
||||||
|
int width = mLauncherSettings.value(QString("General/MainWindow/width")).toInt();
|
||||||
|
int height = mLauncherSettings.value(QString("General/MainWindow/height")).toInt();
|
||||||
|
|
||||||
|
int posX = mLauncherSettings.value(QString("General/MainWindow/posx")).toInt();
|
||||||
|
int posY = mLauncherSettings.value(QString("General/MainWindow/posy")).toInt();
|
||||||
|
|
||||||
|
resize(width, height);
|
||||||
|
move(posX, posY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainDialog::saveSettings()
|
||||||
|
{
|
||||||
|
QString width = QString::number(this->width());
|
||||||
|
QString height = QString::number(this->height());
|
||||||
|
|
||||||
|
mLauncherSettings.setValue(QString("General/MainWindow/width"), width);
|
||||||
|
mLauncherSettings.setValue(QString("General/MainWindow/height"), height);
|
||||||
|
|
||||||
|
QString posX = QString::number(this->pos().x());
|
||||||
|
QString posY = QString::number(this->pos().y());
|
||||||
|
|
||||||
|
mLauncherSettings.setValue(QString("General/MainWindow/posx"), posX);
|
||||||
|
mLauncherSettings.setValue(QString("General/MainWindow/posy"), posY);
|
||||||
|
|
||||||
|
mLauncherSettings.setValue(QString("General/firstrun"), QString("false"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainDialog::writeSettings()
|
||||||
|
{
|
||||||
|
// Now write all config files
|
||||||
|
saveSettings();
|
||||||
|
mGraphicsPage->saveSettings();
|
||||||
|
mDataFilesPage->saveSettings();
|
||||||
|
|
||||||
|
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
|
||||||
|
QDir dir(userPath);
|
||||||
|
|
||||||
|
if (!dir.exists()) {
|
||||||
|
if (!dir.mkpath(userPath)) {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle(tr("Error creating OpenMW configuration directory"));
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not create %0</b><br><br> \
|
||||||
|
Please make sure you have the right permissions \
|
||||||
|
and try again.<br>").arg(userPath));
|
||||||
|
msgBox.exec();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Game settings
|
||||||
|
QFile file(userPath + QString("openmw.cfg"));
|
||||||
|
|
||||||
|
if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {
|
||||||
|
// File cannot be opened or created
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
|
||||||
|
Please make sure you have the right permissions \
|
||||||
|
and try again.<br>").arg(file.fileName()));
|
||||||
|
msgBox.exec();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream stream(&file);
|
||||||
|
stream.setCodec(QTextCodec::codecForName("UTF-8"));
|
||||||
|
|
||||||
|
mGameSettings.writeFile(stream);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
// Graphics settings
|
||||||
|
file.setFileName(userPath + QString("settings.cfg"));
|
||||||
|
|
||||||
|
if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {
|
||||||
|
// File cannot be opened or created
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
|
||||||
|
Please make sure you have the right permissions \
|
||||||
|
and try again.<br>").arg(file.fileName()));
|
||||||
|
msgBox.exec();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.setDevice(&file);
|
||||||
|
stream.setCodec(QTextCodec::codecForName("UTF-8"));
|
||||||
|
|
||||||
|
mGraphicsSettings.writeFile(stream);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
// Launcher settings
|
||||||
|
file.setFileName(userPath + QString("launcher.cfg"));
|
||||||
|
|
||||||
|
if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {
|
||||||
|
// File cannot be opened or created
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle(tr("Error writing Launcher configuration file"));
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
|
||||||
|
Please make sure you have the right permissions \
|
||||||
|
and try again.<br>").arg(file.fileName()));
|
||||||
|
msgBox.exec();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.setDevice(&file);
|
||||||
|
stream.setCodec(QTextCodec::codecForName("UTF-8"));
|
||||||
|
|
||||||
|
mLauncherSettings.writeFile(stream);
|
||||||
|
file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainDialog::closeEvent(QCloseEvent *event)
|
void MainDialog::closeEvent(QCloseEvent *event)
|
||||||
{
|
{
|
||||||
// Now write all config files
|
saveSettings();
|
||||||
mDataFilesPage->writeConfig();
|
writeSettings();
|
||||||
mGraphicsPage->writeConfig();
|
|
||||||
|
|
||||||
// Save user settings
|
|
||||||
const std::string settingspath = (mCfgMgr.getUserPath() / "settings.cfg").string();
|
|
||||||
mSettings.saveUser(settingspath);
|
|
||||||
|
|
||||||
event->accept();
|
event->accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainDialog::play()
|
void MainDialog::play()
|
||||||
{
|
{
|
||||||
// First do a write of all the configs, just to be sure
|
saveSettings();
|
||||||
mDataFilesPage->writeConfig();
|
writeSettings();
|
||||||
mGraphicsPage->writeConfig();
|
|
||||||
|
|
||||||
// Save user settings
|
// Launch the game detached
|
||||||
const std::string settingspath = (mCfgMgr.getUserPath() / "settings.cfg").string();
|
startProgram(QString("openmw"), true);
|
||||||
mSettings.saveUser(settingspath);
|
qApp->quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MainDialog::startProgram(const QString &name, const QStringList &arguments, bool detached)
|
||||||
|
{
|
||||||
|
QString path = name;
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
QString game = "./openmw.exe";
|
path.append(QString(".exe"));
|
||||||
QFile file(game);
|
|
||||||
#elif defined(Q_OS_MAC)
|
#elif defined(Q_OS_MAC)
|
||||||
QDir dir(QCoreApplication::applicationDirPath());
|
QDir dir(QCoreApplication::applicationDirPath());
|
||||||
QString game = dir.absoluteFilePath("openmw");
|
path = dir.absoluteFilePath(name);
|
||||||
QFile file(game);
|
|
||||||
game = "\"" + game + "\"";
|
|
||||||
#else
|
#else
|
||||||
QString game = "./openmw";
|
path.prepend(QString("./"));
|
||||||
QFile file(game);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
QFile file(path);
|
||||||
|
|
||||||
QProcess process;
|
QProcess process;
|
||||||
QFileInfo info(file);
|
QFileInfo info(file);
|
||||||
|
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
QMessageBox msgBox;
|
QMessageBox msgBox;
|
||||||
msgBox.setWindowTitle("Error starting OpenMW");
|
msgBox.setWindowTitle(tr("Error starting executable"));
|
||||||
msgBox.setIcon(QMessageBox::Warning);
|
msgBox.setIcon(QMessageBox::Warning);
|
||||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
msgBox.setText(tr("<br><b>Could not find OpenMW</b><br><br> \
|
msgBox.setText(tr("<br><b>Could not find %1</b><br><br> \
|
||||||
The OpenMW application is not found.<br> \
|
The application is not found.<br> \
|
||||||
Please make sure OpenMW is installed correctly and try again.<br>"));
|
Please make sure OpenMW is installed correctly and try again.<br>").arg(info.fileName()));
|
||||||
msgBox.exec();
|
msgBox.exec();
|
||||||
|
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!info.isExecutable()) {
|
if (!info.isExecutable()) {
|
||||||
QMessageBox msgBox;
|
QMessageBox msgBox;
|
||||||
msgBox.setWindowTitle("Error starting OpenMW");
|
msgBox.setWindowTitle(tr("Error starting executable"));
|
||||||
msgBox.setIcon(QMessageBox::Warning);
|
msgBox.setIcon(QMessageBox::Warning);
|
||||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
msgBox.setText(tr("<br><b>Could not start OpenMW</b><br><br> \
|
msgBox.setText(tr("<br><b>Could not start %1</b><br><br> \
|
||||||
The OpenMW application is not executable.<br> \
|
The application is not executable.<br> \
|
||||||
Please make sure you have the right permissions and try again.<br>"));
|
Please make sure you have the right permissions and try again.<br>").arg(info.fileName()));
|
||||||
msgBox.exec();
|
msgBox.exec();
|
||||||
|
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the game
|
// Start the executable
|
||||||
if (!process.startDetached(game)) {
|
if (detached) {
|
||||||
|
if (!process.startDetached(path, arguments)) {
|
||||||
QMessageBox msgBox;
|
QMessageBox msgBox;
|
||||||
msgBox.setWindowTitle("Error starting OpenMW");
|
msgBox.setWindowTitle(tr("Error starting executable"));
|
||||||
msgBox.setIcon(QMessageBox::Critical);
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
msgBox.setText(tr("<br><b>Could not start OpenMW</b><br><br> \
|
msgBox.setText(tr("<br><b>Could not start %1</b><br><br> \
|
||||||
An error occurred while starting OpenMW.<br><br> \
|
An error occurred while starting %1.<br><br> \
|
||||||
Press \"Show Details...\" for more information.<br>"));
|
Press \"Show Details...\" for more information.<br>").arg(info.fileName()));
|
||||||
msgBox.setDetailedText(process.errorString());
|
msgBox.setDetailedText(process.errorString());
|
||||||
msgBox.exec();
|
msgBox.exec();
|
||||||
|
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
qApp->quit();
|
process.start(path, arguments);
|
||||||
|
if (!process.waitForFinished()) {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle(tr("Error starting executable"));
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not start %1</b><br><br> \
|
||||||
|
An error occurred while starting %1.<br><br> \
|
||||||
|
Press \"Show Details...\" for more information.<br>").arg(info.fileName()));
|
||||||
|
msgBox.setDetailedText(process.errorString());
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.exitCode() != 0) {
|
||||||
|
QString error(process.readAllStandardError());
|
||||||
|
error.append(tr("\nArguments:\n"));
|
||||||
|
error.append(arguments.join(" "));
|
||||||
|
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle(tr("Error running executable"));
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Executable %1 returned an error</b><br><br> \
|
||||||
|
An error occurred while running %1.<br><br> \
|
||||||
|
Press \"Show Details...\" for more information.<br>").arg(info.fileName()));
|
||||||
|
msgBox.setDetailedText(error);
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,12 @@
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
|
|
||||||
#include <components/files/configurationmanager.hpp>
|
#include <components/files/configurationmanager.hpp>
|
||||||
#include <components/settings/settings.hpp>
|
|
||||||
|
#include "settings/gamesettings.hpp"
|
||||||
|
#include "settings/graphicssettings.hpp"
|
||||||
|
#include "settings/launchersettings.hpp"
|
||||||
|
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
|
||||||
class QListWidget;
|
class QListWidget;
|
||||||
class QListWidgetItem;
|
class QListWidgetItem;
|
||||||
|
@ -17,32 +22,46 @@ class PlayPage;
|
||||||
class GraphicsPage;
|
class GraphicsPage;
|
||||||
class DataFilesPage;
|
class DataFilesPage;
|
||||||
|
|
||||||
class MainDialog : public QMainWindow
|
class MainDialog : public QMainWindow, private Ui::MainWindow
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MainDialog();
|
MainDialog();
|
||||||
|
bool setup();
|
||||||
|
bool showFirstRunDialog();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void changePage(QListWidgetItem *current, QListWidgetItem *previous);
|
void changePage(QListWidgetItem *current, QListWidgetItem *previous);
|
||||||
void play();
|
void play();
|
||||||
bool setup();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void createIcons();
|
void createIcons();
|
||||||
void createPages();
|
void createPages();
|
||||||
void closeEvent(QCloseEvent *event);
|
|
||||||
|
|
||||||
QListWidget *mIconWidget;
|
bool setupLauncherSettings();
|
||||||
QStackedWidget *mPagesWidget;
|
bool setupGameSettings();
|
||||||
|
bool setupGraphicsSettings();
|
||||||
|
|
||||||
|
void loadSettings();
|
||||||
|
void saveSettings();
|
||||||
|
void 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;
|
PlayPage *mPlayPage;
|
||||||
GraphicsPage *mGraphicsPage;
|
GraphicsPage *mGraphicsPage;
|
||||||
DataFilesPage *mDataFilesPage;
|
DataFilesPage *mDataFilesPage;
|
||||||
|
|
||||||
Files::ConfigurationManager mCfgMgr;
|
Files::ConfigurationManager mCfgMgr;
|
||||||
Settings::Manager mSettings;
|
|
||||||
|
GameSettings mGameSettings;
|
||||||
|
GraphicsSettings mGraphicsSettings;
|
||||||
|
LauncherSettings mLauncherSettings;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,43 +1,40 @@
|
||||||
#include <QtGui>
|
|
||||||
|
|
||||||
#include "playpage.hpp"
|
#include "playpage.hpp"
|
||||||
|
|
||||||
|
#include <QtGui>
|
||||||
|
|
||||||
PlayPage::PlayPage(QWidget *parent) : QWidget(parent)
|
PlayPage::PlayPage(QWidget *parent) : QWidget(parent)
|
||||||
{
|
{
|
||||||
QWidget *playWidget = new QWidget(this);
|
setupUi(this);
|
||||||
playWidget->setObjectName("PlayGroup");
|
|
||||||
playWidget->setFixedSize(QSize(425, 375));
|
|
||||||
|
|
||||||
mPlayButton = new QPushButton(tr("Play"), playWidget);
|
|
||||||
mPlayButton->setObjectName("PlayButton");
|
|
||||||
mPlayButton->setMinimumSize(QSize(200, 50));
|
|
||||||
|
|
||||||
QLabel *profileLabel = new QLabel(tr("Current Profile:"), playWidget);
|
|
||||||
profileLabel->setObjectName("ProfileLabel");
|
|
||||||
|
|
||||||
|
// Hacks to get the stylesheet look properly on different platforms
|
||||||
QPlastiqueStyle *style = new QPlastiqueStyle;
|
QPlastiqueStyle *style = new QPlastiqueStyle;
|
||||||
mProfilesComboBox = new QComboBox(playWidget);
|
QFont font = QApplication::font();
|
||||||
mProfilesComboBox->setObjectName("ProfilesComboBox");
|
font.setPointSize(12); // Fixes problem with overlapping items
|
||||||
mProfilesComboBox->setStyle(style);
|
|
||||||
|
|
||||||
QGridLayout *playLayout = new QGridLayout(playWidget);
|
profilesComboBox->setStyle(style);
|
||||||
|
profilesComboBox->setFont(font);
|
||||||
|
|
||||||
QSpacerItem *hSpacer1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentIndexChanged(int)));
|
||||||
QSpacerItem *hSpacer2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
connect(playButton, SIGNAL(clicked()), this, SLOT(slotPlayClicked()));
|
||||||
|
|
||||||
QSpacerItem *vSpacer1 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
|
||||||
QSpacerItem *vSpacer2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
|
||||||
|
|
||||||
playLayout->addWidget(mPlayButton, 1, 1, 1, 1);
|
|
||||||
playLayout->addWidget(profileLabel, 2, 1, 1, 1);
|
|
||||||
playLayout->addWidget(mProfilesComboBox, 3, 1, 1, 1);
|
|
||||||
playLayout->addItem(hSpacer1, 2, 0, 1, 1);
|
|
||||||
playLayout->addItem(hSpacer2, 2, 2, 1, 1);
|
|
||||||
playLayout->addItem(vSpacer1, 0, 1, 1, 1);
|
|
||||||
playLayout->addItem(vSpacer2, 4, 1, 1, 1);
|
|
||||||
|
|
||||||
QHBoxLayout *pageLayout = new QHBoxLayout(this);
|
|
||||||
|
|
||||||
pageLayout->addWidget(playWidget);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlayPage::setProfilesComboBoxModel(QAbstractItemModel *model)
|
||||||
|
{
|
||||||
|
profilesComboBox->setModel(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayPage::setProfilesComboBoxIndex(int index)
|
||||||
|
{
|
||||||
|
profilesComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayPage::slotCurrentIndexChanged(int index)
|
||||||
|
{
|
||||||
|
emit profileChanged(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayPage::slotPlayClicked()
|
||||||
|
{
|
||||||
|
emit playButtonClicked();
|
||||||
|
}
|
||||||
|
|
|
@ -3,18 +3,32 @@
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "ui_playpage.h"
|
||||||
|
|
||||||
class QComboBox;
|
class QComboBox;
|
||||||
class QPushButton;
|
class QPushButton;
|
||||||
|
class QAbstractItemModel;
|
||||||
|
|
||||||
class PlayPage : public QWidget
|
class PlayPage : public QWidget, private Ui::PlayPage
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PlayPage(QWidget *parent = 0);
|
PlayPage(QWidget *parent = 0);
|
||||||
|
void setProfilesComboBoxModel(QAbstractItemModel *model);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void profileChanged(int index);
|
||||||
|
void playButtonClicked();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setProfilesComboBoxIndex(int index);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void slotCurrentIndexChanged(int index);
|
||||||
|
void slotPlayClicked();
|
||||||
|
|
||||||
|
|
||||||
QComboBox *mProfilesComboBox;
|
|
||||||
QPushButton *mPlayButton;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
<!DOCTYPE RCC><RCC version="1.0">
|
|
||||||
<qresource prefix="/images">
|
|
||||||
<file alias="clear.png">resources/images/clear.png</file>
|
|
||||||
<file alias="down.png">resources/images/down.png</file>
|
|
||||||
<file alias="openmw.png">resources/images/openmw.png</file>
|
|
||||||
<file alias="openmw-plugin.png">resources/images/openmw-plugin.png</file>
|
|
||||||
<file alias="openmw-header.png">resources/images/openmw-header.png</file>
|
|
||||||
<file alias="playpage-background.png">resources/images/playpage-background.png</file>
|
|
||||||
</qresource>
|
|
||||||
<qresource prefix="icons/tango">
|
|
||||||
<file alias="index.theme">resources/icons/tango/index.theme</file>
|
|
||||||
<file alias="video-display.png">resources/icons/tango/video-display.png</file>
|
|
||||||
<file alias="16x16/document-new.png">resources/icons/tango/document-new.png</file>
|
|
||||||
<file alias="16x16/edit-copy.png">resources/icons/tango/edit-copy.png</file>
|
|
||||||
<file alias="16x16/edit-delete.png">resources/icons/tango/edit-delete.png</file>
|
|
||||||
<file alias="16x16/go-bottom.png">resources/icons/tango/go-bottom.png</file>
|
|
||||||
<file alias="16x16/go-down.png">resources/icons/tango/go-down.png</file>
|
|
||||||
<file alias="16x16/go-top.png">resources/icons/tango/go-top.png</file>
|
|
||||||
<file alias="16x16/go-up.png">resources/icons/tango/go-up.png</file>
|
|
||||||
</qresource>
|
|
||||||
</RCC>
|
|
177
apps/launcher/settings/gamesettings.cpp
Normal file
177
apps/launcher/settings/gamesettings.cpp
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
#include "gamesettings.hpp"
|
||||||
|
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QString>
|
||||||
|
#include <QRegExp>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <components/files/configurationmanager.hpp>
|
||||||
|
|
||||||
|
#include <boost/version.hpp>
|
||||||
|
/**
|
||||||
|
* Workaround for problems with whitespaces in paths in older versions of Boost library
|
||||||
|
*/
|
||||||
|
#if (BOOST_VERSION <= 104600)
|
||||||
|
namespace boost
|
||||||
|
{
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline boost::filesystem::path lexical_cast<boost::filesystem::path, std::string>(const std::string& arg)
|
||||||
|
{
|
||||||
|
return boost::filesystem::path(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* namespace boost */
|
||||||
|
#endif /* (BOOST_VERSION <= 104600) */
|
||||||
|
|
||||||
|
|
||||||
|
GameSettings::GameSettings(Files::ConfigurationManager &cfg)
|
||||||
|
: mCfgMgr(cfg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
GameSettings::~GameSettings()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameSettings::validatePaths()
|
||||||
|
{
|
||||||
|
if (mSettings.isEmpty() || !mDataDirs.isEmpty())
|
||||||
|
return; // Don't re-validate paths if they are already parsed
|
||||||
|
|
||||||
|
QStringList paths = mSettings.values(QString("data"));
|
||||||
|
Files::PathContainer dataDirs;
|
||||||
|
|
||||||
|
foreach (const QString &path, paths) {
|
||||||
|
dataDirs.push_back(Files::PathContainer::value_type(path.toStdString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the data dirs to convert the tokenized paths
|
||||||
|
mCfgMgr.processPaths(dataDirs);
|
||||||
|
mDataDirs.clear();
|
||||||
|
|
||||||
|
for (Files::PathContainer::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) {
|
||||||
|
QString path = QString::fromStdString(it->string());
|
||||||
|
path.remove(QChar('\"'));
|
||||||
|
|
||||||
|
QDir dir(path);
|
||||||
|
if (dir.exists())
|
||||||
|
mDataDirs.append(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the same for data-local
|
||||||
|
QString local = mSettings.value(QString("data-local"));
|
||||||
|
|
||||||
|
if (local.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
dataDirs.clear();
|
||||||
|
dataDirs.push_back(Files::PathContainer::value_type(local.toStdString()));
|
||||||
|
|
||||||
|
mCfgMgr.processPaths(dataDirs);
|
||||||
|
|
||||||
|
if (!dataDirs.empty()) {
|
||||||
|
QString path = QString::fromStdString(dataDirs.front().string());
|
||||||
|
path.remove(QChar('\"'));
|
||||||
|
|
||||||
|
QDir dir(path);
|
||||||
|
if (dir.exists())
|
||||||
|
mDataLocal = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList GameSettings::values(const QString &key, const QStringList &defaultValues)
|
||||||
|
{
|
||||||
|
if (!mSettings.values(key).isEmpty())
|
||||||
|
return mSettings.values(key);
|
||||||
|
return defaultValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GameSettings::readFile(QTextStream &stream)
|
||||||
|
{
|
||||||
|
QMap<QString, QString> cache;
|
||||||
|
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
|
||||||
|
|
||||||
|
while (!stream.atEnd()) {
|
||||||
|
QString line = stream.readLine().simplified();
|
||||||
|
|
||||||
|
if (line.isEmpty() || line.startsWith("#"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (keyRe.indexIn(line) != -1) {
|
||||||
|
|
||||||
|
QString key = keyRe.cap(1).simplified();
|
||||||
|
QString value = keyRe.cap(2).simplified();
|
||||||
|
|
||||||
|
// Don't remove existing data entries
|
||||||
|
if (key != QLatin1String("data"))
|
||||||
|
mSettings.remove(key);
|
||||||
|
|
||||||
|
QStringList values = cache.values(key);
|
||||||
|
values.append(mSettings.values(key));
|
||||||
|
|
||||||
|
if (!values.contains(value)) {
|
||||||
|
cache.insertMulti(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mSettings.isEmpty()) {
|
||||||
|
mSettings = 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);
|
||||||
|
validatePaths();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GameSettings::writeFile(QTextStream &stream)
|
||||||
|
{
|
||||||
|
// Iterate in reverse order to preserve insertion order
|
||||||
|
QMapIterator<QString, QString> i(mSettings);
|
||||||
|
i.toBack();
|
||||||
|
|
||||||
|
while (i.hasPrevious()) {
|
||||||
|
i.previous();
|
||||||
|
|
||||||
|
if (i.key() == QLatin1String("master") || i.key() == QLatin1String("plugin"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Quote paths with spaces
|
||||||
|
if (i.key() == QLatin1String("data")
|
||||||
|
|| i.key() == QLatin1String("data-local")
|
||||||
|
|| i.key() == QLatin1String("resources"))
|
||||||
|
{
|
||||||
|
if (i.value().contains(QChar(' ')))
|
||||||
|
{
|
||||||
|
QString stripped = i.value();
|
||||||
|
stripped.remove(QChar('\"')); // Remove quotes
|
||||||
|
|
||||||
|
stream << i.key() << "=\"" << stripped << "\"\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream << i.key() << "=" << i.value() << "\n";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
66
apps/launcher/settings/gamesettings.hpp
Normal file
66
apps/launcher/settings/gamesettings.hpp
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
#ifndef GAMESETTINGS_HPP
|
||||||
|
#define GAMESETTINGS_HPP
|
||||||
|
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QString>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
|
namespace Files { typedef std::vector<boost::filesystem::path> PathContainer;
|
||||||
|
struct ConfigurationManager;}
|
||||||
|
|
||||||
|
class GameSettings
|
||||||
|
{
|
||||||
|
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 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void clear()
|
||||||
|
{
|
||||||
|
mSettings.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QStringList getDataDirs() { return mDataDirs; }
|
||||||
|
inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); }
|
||||||
|
inline QString getDataLocal() {return mDataLocal; }
|
||||||
|
|
||||||
|
QStringList values(const QString &key, const QStringList &defaultValues = QStringList());
|
||||||
|
bool readFile(QTextStream &stream);
|
||||||
|
bool writeFile(QTextStream &stream);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Files::ConfigurationManager &mCfgMgr;
|
||||||
|
|
||||||
|
void validatePaths();
|
||||||
|
QMap<QString, QString> mSettings;
|
||||||
|
|
||||||
|
QStringList mDataDirs;
|
||||||
|
QString mDataLocal;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GAMESETTINGS_HPP
|
44
apps/launcher/settings/graphicssettings.cpp
Normal file
44
apps/launcher/settings/graphicssettings.cpp
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#include "graphicssettings.hpp"
|
||||||
|
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QString>
|
||||||
|
#include <QRegExp>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
GraphicsSettings::GraphicsSettings()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
GraphicsSettings::~GraphicsSettings()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GraphicsSettings::writeFile(QTextStream &stream)
|
||||||
|
{
|
||||||
|
QString sectionPrefix;
|
||||||
|
QRegExp sectionRe("([^/]+)/(.+)$");
|
||||||
|
QMap<QString, QString> settings = SettingsBase::getSettings();
|
||||||
|
|
||||||
|
QMapIterator<QString, QString> i(settings);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
i.next();
|
||||||
|
|
||||||
|
QString prefix;
|
||||||
|
QString key;
|
||||||
|
|
||||||
|
if (sectionRe.exactMatch(i.key())) {
|
||||||
|
prefix = sectionRe.cap(1);
|
||||||
|
key = sectionRe.cap(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sectionPrefix != prefix) {
|
||||||
|
sectionPrefix = prefix;
|
||||||
|
stream << "\n[" << prefix << "]\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
stream << key << " = " << i.value() << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
16
apps/launcher/settings/graphicssettings.hpp
Normal file
16
apps/launcher/settings/graphicssettings.hpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef GRAPHICSSETTINGS_HPP
|
||||||
|
#define GRAPHICSSETTINGS_HPP
|
||||||
|
|
||||||
|
#include "settingsbase.hpp"
|
||||||
|
|
||||||
|
class GraphicsSettings : public SettingsBase<QMap<QString, QString> >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GraphicsSettings();
|
||||||
|
~GraphicsSettings();
|
||||||
|
|
||||||
|
bool writeFile(QTextStream &stream);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GRAPHICSSETTINGS_HPP
|
101
apps/launcher/settings/launchersettings.cpp
Normal file
101
apps/launcher/settings/launchersettings.cpp
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
#include "launchersettings.hpp"
|
||||||
|
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QString>
|
||||||
|
#include <QRegExp>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
LauncherSettings::LauncherSettings()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LauncherSettings::~LauncherSettings()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList LauncherSettings::values(const QString &key, Qt::MatchFlags flags)
|
||||||
|
{
|
||||||
|
QMap<QString, QString> settings = SettingsBase::getSettings();
|
||||||
|
|
||||||
|
if (flags == Qt::MatchExactly)
|
||||||
|
return settings.values(key);
|
||||||
|
|
||||||
|
QStringList result;
|
||||||
|
|
||||||
|
if (flags == Qt::MatchStartsWith) {
|
||||||
|
QStringList keys = settings.keys();
|
||||||
|
|
||||||
|
foreach (const QString ¤tKey, keys) {
|
||||||
|
if (currentKey.startsWith(key))
|
||||||
|
result.append(settings.value(currentKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList LauncherSettings::subKeys(const QString &key)
|
||||||
|
{
|
||||||
|
QMap<QString, QString> settings = SettingsBase::getSettings();
|
||||||
|
QStringList keys = settings.uniqueKeys();
|
||||||
|
|
||||||
|
QRegExp keyRe("(.+)/");
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.removeDuplicates();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LauncherSettings::writeFile(QTextStream &stream)
|
||||||
|
{
|
||||||
|
QString sectionPrefix;
|
||||||
|
QRegExp sectionRe("([^/]+)/(.+)$");
|
||||||
|
QMap<QString, QString> settings = SettingsBase::getSettings();
|
||||||
|
|
||||||
|
QMapIterator<QString, QString> i(settings);
|
||||||
|
i.toBack();
|
||||||
|
|
||||||
|
while (i.hasPrevious()) {
|
||||||
|
i.previous();
|
||||||
|
|
||||||
|
QString prefix;
|
||||||
|
QString key;
|
||||||
|
|
||||||
|
if (sectionRe.exactMatch(i.key())) {
|
||||||
|
prefix = sectionRe.cap(1);
|
||||||
|
key = sectionRe.cap(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get rid of legacy settings
|
||||||
|
if (key.contains(QChar('\\')))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (key == QLatin1String("CurrentProfile"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (sectionPrefix != prefix) {
|
||||||
|
sectionPrefix = prefix;
|
||||||
|
stream << "\n[" << prefix << "]\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
stream << key << "=" << i.value() << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
19
apps/launcher/settings/launchersettings.hpp
Normal file
19
apps/launcher/settings/launchersettings.hpp
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef LAUNCHERSETTINGS_HPP
|
||||||
|
#define LAUNCHERSETTINGS_HPP
|
||||||
|
|
||||||
|
#include "settingsbase.hpp"
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
bool writeFile(QTextStream &stream);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LAUNCHERSETTINGS_HPP
|
98
apps/launcher/settings/settingsbase.hpp
Normal file
98
apps/launcher/settings/settingsbase.hpp
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
#ifndef SETTINGSBASE_HPP
|
||||||
|
#define SETTINGSBASE_HPP
|
||||||
|
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QString>
|
||||||
|
#include <QRegExp>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
template <class Map>
|
||||||
|
class SettingsBase
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
SettingsBase() {}
|
||||||
|
~SettingsBase() {}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
QStringList values = mSettings.values(key);
|
||||||
|
if (!values.contains(value))
|
||||||
|
mSettings.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 remove(const QString &key)
|
||||||
|
{
|
||||||
|
mSettings.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map getSettings() {return mSettings;}
|
||||||
|
|
||||||
|
bool readFile(QTextStream &stream)
|
||||||
|
{
|
||||||
|
mCache.clear();
|
||||||
|
|
||||||
|
QString sectionPrefix;
|
||||||
|
QRegExp sectionRe("^\\[([^]]+)\\]");
|
||||||
|
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
|
||||||
|
|
||||||
|
while (!stream.atEnd()) {
|
||||||
|
QString line = stream.readLine().simplified();
|
||||||
|
|
||||||
|
if (line.isEmpty() || line.startsWith("#"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (sectionRe.exactMatch(line)) {
|
||||||
|
sectionPrefix = sectionRe.cap(1);
|
||||||
|
sectionPrefix.append("/");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyRe.indexIn(line) != -1) {
|
||||||
|
|
||||||
|
QString key = keyRe.cap(1).simplified();
|
||||||
|
QString value = keyRe.cap(2).simplified();
|
||||||
|
|
||||||
|
if (!sectionPrefix.isEmpty())
|
||||||
|
key.prepend(sectionPrefix);
|
||||||
|
|
||||||
|
mSettings.remove(key);
|
||||||
|
|
||||||
|
QStringList values = mCache.values(key);
|
||||||
|
if (!values.contains(value)) {
|
||||||
|
mCache.insertMulti(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Map mSettings;
|
||||||
|
Map mCache;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SETTINGSBASE_HPP
|
269
apps/launcher/utils/checkablemessagebox.cpp
Normal file
269
apps/launcher/utils/checkablemessagebox.cpp
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||||
|
** Contact: http://www.qt-project.org/legal
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Digia. For licensing terms and
|
||||||
|
** conditions see http://qt.digia.com/licensing. For further information
|
||||||
|
** use the contact form at http://qt.digia.com/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** In addition, as a special exception, Digia gives you certain additional
|
||||||
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "checkablemessagebox.hpp"
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QAction>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QButtonGroup>
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QHeaderView>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QSpacerItem>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class Utils::CheckableMessageBox
|
||||||
|
|
||||||
|
\brief A messagebox suitable for questions with a
|
||||||
|
"Do not ask me again" checkbox.
|
||||||
|
|
||||||
|
Emulates the QMessageBox API with
|
||||||
|
static conveniences. The message label can open external URLs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CheckableMessageBoxPrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CheckableMessageBoxPrivate(QDialog *q)
|
||||||
|
: clickedButton(0)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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"));
|
||||||
|
|
||||||
|
buttonBox = new QDialogButtonBox(q);
|
||||||
|
buttonBox->setOrientation(Qt::Horizontal);
|
||||||
|
buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
|
||||||
|
|
||||||
|
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 = 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
QLabel *pixmapLabel;
|
||||||
|
QLabel *messageLabel;
|
||||||
|
QCheckBox *checkBox;
|
||||||
|
QDialogButtonBox *buttonBox;
|
||||||
|
QAbstractButton *clickedButton;
|
||||||
|
};
|
||||||
|
|
||||||
|
CheckableMessageBox::CheckableMessageBox(QWidget *parent) :
|
||||||
|
QDialog(parent),
|
||||||
|
d(new CheckableMessageBoxPrivate(this))
|
||||||
|
{
|
||||||
|
setModal(true);
|
||||||
|
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
|
connect(d->buttonBox, SIGNAL(accepted()), SLOT(accept()));
|
||||||
|
connect(d->buttonBox, SIGNAL(rejected()), SLOT(reject()));
|
||||||
|
connect(d->buttonBox, SIGNAL(clicked(QAbstractButton*)),
|
||||||
|
SLOT(slotClicked(QAbstractButton*)));
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckableMessageBox::~CheckableMessageBox()
|
||||||
|
{
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckableMessageBox::slotClicked(QAbstractButton *b)
|
||||||
|
{
|
||||||
|
d->clickedButton = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAbstractButton *CheckableMessageBox::clickedButton() const
|
||||||
|
{
|
||||||
|
return d->clickedButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDialogButtonBox::StandardButton CheckableMessageBox::clickedStandardButton() const
|
||||||
|
{
|
||||||
|
if (d->clickedButton)
|
||||||
|
return d->buttonBox->standardButton(d->clickedButton);
|
||||||
|
return QDialogButtonBox::NoButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CheckableMessageBox::text() const
|
||||||
|
{
|
||||||
|
return d->messageLabel->text();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckableMessageBox::setText(const QString &t)
|
||||||
|
{
|
||||||
|
d->messageLabel->setText(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap CheckableMessageBox::iconPixmap() const
|
||||||
|
{
|
||||||
|
if (const QPixmap *p = d->pixmapLabel->pixmap())
|
||||||
|
return QPixmap(*p);
|
||||||
|
return QPixmap();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckableMessageBox::setIconPixmap(const QPixmap &p)
|
||||||
|
{
|
||||||
|
d->pixmapLabel->setPixmap(p);
|
||||||
|
d->pixmapLabel->setVisible(!p.isNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CheckableMessageBox::isChecked() const
|
||||||
|
{
|
||||||
|
return d->checkBox->isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckableMessageBox::setChecked(bool s)
|
||||||
|
{
|
||||||
|
d->checkBox->setChecked(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CheckableMessageBox::checkBoxText() const
|
||||||
|
{
|
||||||
|
return d->checkBox->text();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckableMessageBox::setCheckBoxText(const QString &t)
|
||||||
|
{
|
||||||
|
d->checkBox->setText(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CheckableMessageBox::isCheckBoxVisible() const
|
||||||
|
{
|
||||||
|
return d->checkBox->isVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckableMessageBox::setCheckBoxVisible(bool v)
|
||||||
|
{
|
||||||
|
d->checkBox->setVisible(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
QDialogButtonBox::StandardButtons CheckableMessageBox::standardButtons() const
|
||||||
|
{
|
||||||
|
return d->buttonBox->standardButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckableMessageBox::setStandardButtons(QDialogButtonBox::StandardButtons s)
|
||||||
|
{
|
||||||
|
d->buttonBox->setStandardButtons(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton *CheckableMessageBox::button(QDialogButtonBox::StandardButton b) const
|
||||||
|
{
|
||||||
|
return d->buttonBox->button(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton *CheckableMessageBox::addButton(const QString &text, QDialogButtonBox::ButtonRole role)
|
||||||
|
{
|
||||||
|
return d->buttonBox->addButton(text, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
QDialogButtonBox::StandardButton CheckableMessageBox::defaultButton() const
|
||||||
|
{
|
||||||
|
foreach (QAbstractButton *b, d->buttonBox->buttons())
|
||||||
|
if (QPushButton *pb = qobject_cast<QPushButton *>(b))
|
||||||
|
if (pb->isDefault())
|
||||||
|
return d->buttonBox->standardButton(pb);
|
||||||
|
return QDialogButtonBox::NoButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s)
|
||||||
|
{
|
||||||
|
if (QPushButton *b = d->buttonBox->button(s)) {
|
||||||
|
b->setDefault(true);
|
||||||
|
b->setFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QDialogButtonBox::StandardButton
|
||||||
|
CheckableMessageBox::question(QWidget *parent,
|
||||||
|
const QString &title,
|
||||||
|
const QString &question,
|
||||||
|
const QString &checkBoxText,
|
||||||
|
bool *checkBoxSetting,
|
||||||
|
QDialogButtonBox::StandardButtons buttons,
|
||||||
|
QDialogButtonBox::StandardButton defaultButton)
|
||||||
|
{
|
||||||
|
CheckableMessageBox mb(parent);
|
||||||
|
mb.setWindowTitle(title);
|
||||||
|
mb.setIconPixmap(QMessageBox::standardIcon(QMessageBox::Question));
|
||||||
|
mb.setText(question);
|
||||||
|
mb.setCheckBoxText(checkBoxText);
|
||||||
|
mb.setChecked(*checkBoxSetting);
|
||||||
|
mb.setStandardButtons(buttons);
|
||||||
|
mb.setDefaultButton(defaultButton);
|
||||||
|
mb.exec();
|
||||||
|
*checkBoxSetting = mb.isChecked();
|
||||||
|
return mb.clickedStandardButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
QMessageBox::StandardButton CheckableMessageBox::dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton db)
|
||||||
|
{
|
||||||
|
return static_cast<QMessageBox::StandardButton>(int(db));
|
||||||
|
}
|
100
apps/launcher/utils/checkablemessagebox.hpp
Normal file
100
apps/launcher/utils/checkablemessagebox.hpp
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||||
|
** Contact: http://www.qt-project.org/legal
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Digia. For licensing terms and
|
||||||
|
** conditions see http://qt.digia.com/licensing. For further information
|
||||||
|
** use the contact form at http://qt.digia.com/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** In addition, as a special exception, Digia gives you certain additional
|
||||||
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef CHECKABLEMESSAGEBOX_HPP
|
||||||
|
#define CHECKABLEMESSAGEBOX_HPP
|
||||||
|
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
class CheckableMessageBoxPrivate;
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CheckableMessageBox(QWidget *parent);
|
||||||
|
virtual ~CheckableMessageBox();
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
QString text() const;
|
||||||
|
void setText(const QString &);
|
||||||
|
|
||||||
|
bool isChecked() const;
|
||||||
|
void setChecked(bool s);
|
||||||
|
|
||||||
|
QString checkBoxText() const;
|
||||||
|
void setCheckBoxText(const QString &);
|
||||||
|
|
||||||
|
bool isCheckBoxVisible() const;
|
||||||
|
void setCheckBoxVisible(bool);
|
||||||
|
|
||||||
|
QDialogButtonBox::StandardButtons standardButtons() const;
|
||||||
|
void setStandardButtons(QDialogButtonBox::StandardButtons s);
|
||||||
|
QPushButton *button(QDialogButtonBox::StandardButton b) const;
|
||||||
|
QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role);
|
||||||
|
|
||||||
|
QDialogButtonBox::StandardButton defaultButton() const;
|
||||||
|
void setDefaultButton(QDialogButtonBox::StandardButton s);
|
||||||
|
|
||||||
|
// See static QMessageBox::standardPixmap()
|
||||||
|
QPixmap iconPixmap() const;
|
||||||
|
void setIconPixmap (const QPixmap &p);
|
||||||
|
|
||||||
|
// 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,57 +0,0 @@
|
||||||
#include "filedialog.hpp"
|
|
||||||
#include <QDialogButtonBox>
|
|
||||||
#include <QPushButton>
|
|
||||||
|
|
||||||
FileDialog::FileDialog(QWidget *parent)
|
|
||||||
: QFileDialog(parent)
|
|
||||||
{
|
|
||||||
// Remove the default Choose button to prevent it being updated elsewhere
|
|
||||||
QDialogButtonBox *box = qFindChild<QDialogButtonBox*>(this);
|
|
||||||
Q_ASSERT(box);
|
|
||||||
box->removeButton(box->button(QDialogButtonBox::Open));
|
|
||||||
|
|
||||||
// Add our own button so we can disable/enable it
|
|
||||||
mChooseButton = new QPushButton(tr("&Choose"));
|
|
||||||
mChooseButton->setIcon(QIcon::fromTheme("document-open"));
|
|
||||||
mChooseButton->setEnabled(false);
|
|
||||||
box->addButton(mChooseButton, QDialogButtonBox::AcceptRole);
|
|
||||||
|
|
||||||
connect(this, SIGNAL(directoryEntered(const QString&)), this, SLOT(updateChooseButton(const QString&)));
|
|
||||||
emit directoryEntered(QDir::currentPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
QString FileDialog::getExistingDirectory(QWidget *parent,
|
|
||||||
const QString &caption,
|
|
||||||
const QString &dir,
|
|
||||||
Options options)
|
|
||||||
{
|
|
||||||
// create a non-native file dialog
|
|
||||||
FileDialog dialog;
|
|
||||||
dialog.setFileMode(DirectoryOnly);
|
|
||||||
dialog.setOptions(options |= QFileDialog::DontUseNativeDialog | QFileDialog::ShowDirsOnly | QFileDialog::ReadOnly);
|
|
||||||
|
|
||||||
if (!caption.isEmpty())
|
|
||||||
dialog.setWindowTitle(caption);
|
|
||||||
|
|
||||||
if (!dir.isEmpty())
|
|
||||||
dialog.setDirectory(dir);
|
|
||||||
|
|
||||||
if (dialog.exec() == QDialog::Accepted) {
|
|
||||||
return dialog.selectedFiles().value(0);
|
|
||||||
}
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileDialog::updateChooseButton(const QString &directory)
|
|
||||||
{
|
|
||||||
QDir currentDir = QDir(directory);
|
|
||||||
currentDir.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks);
|
|
||||||
currentDir.setNameFilters(QStringList() << "*.esm" << "*.esp");
|
|
||||||
|
|
||||||
if (!currentDir.entryList().isEmpty()) {
|
|
||||||
// There are data files in the current dir
|
|
||||||
mChooseButton->setEnabled(true);
|
|
||||||
} else {
|
|
||||||
mChooseButton->setEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
#ifndef FILEDIALOG_HPP
|
|
||||||
#define FILEDIALOG_HPP
|
|
||||||
|
|
||||||
#include <QFileDialog>
|
|
||||||
|
|
||||||
class QPushButton;
|
|
||||||
|
|
||||||
class FileDialog : public QFileDialog
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
FileDialog(QWidget *parent = 0);
|
|
||||||
|
|
||||||
static QString getExistingDirectory(QWidget *parent = 0,
|
|
||||||
const QString &caption = QString(),
|
|
||||||
const QString &dir = QString(),
|
|
||||||
Options options = ShowDirsOnly);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void updateChooseButton(const QString &directory);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QPushButton *mChooseButton;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif // FILEDIALOG_HPP
|
|
|
@ -1,52 +0,0 @@
|
||||||
#include <QRegExpValidator>
|
|
||||||
#include <QLineEdit>
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
#include "profilescombobox.hpp"
|
|
||||||
|
|
||||||
ProfilesComboBox::ProfilesComboBox(QWidget *parent) :
|
|
||||||
QComboBox(parent)
|
|
||||||
{
|
|
||||||
mValidator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore
|
|
||||||
|
|
||||||
setEditable(true);
|
|
||||||
setValidator(mValidator);
|
|
||||||
setCompleter(0);
|
|
||||||
|
|
||||||
connect(this, SIGNAL(currentIndexChanged(int)), this,
|
|
||||||
SLOT(slotIndexChanged(int)));
|
|
||||||
connect(lineEdit(), SIGNAL(returnPressed()), this,
|
|
||||||
SLOT(slotReturnPressed()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProfilesComboBox::setEditEnabled(bool editable)
|
|
||||||
{
|
|
||||||
if (!editable)
|
|
||||||
return setEditable(false);
|
|
||||||
|
|
||||||
// Reset the completer and validator
|
|
||||||
setEditable(true);
|
|
||||||
setValidator(mValidator);
|
|
||||||
setCompleter(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProfilesComboBox::slotReturnPressed()
|
|
||||||
{
|
|
||||||
QString current = currentText();
|
|
||||||
QString previous = itemText(currentIndex());
|
|
||||||
|
|
||||||
if (findText(current) != -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
setItemText(currentIndex(), current);
|
|
||||||
emit(profileRenamed(previous, current));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProfilesComboBox::slotIndexChanged(int index)
|
|
||||||
{
|
|
||||||
if (index == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
emit(profileChanged(mOldProfile, currentText()));
|
|
||||||
mOldProfile = itemText(index);
|
|
||||||
}
|
|
|
@ -1,11 +1,11 @@
|
||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
|
#include <QApplication>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QDebug>
|
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QValidator>
|
#include <QValidator>
|
||||||
|
|
||||||
#include "lineedit.hpp"
|
#include <components/fileorderlist/utils/lineedit.hpp>
|
||||||
|
|
||||||
#include "textinputdialog.hpp"
|
#include "textinputdialog.hpp"
|
||||||
|
|
||||||
|
@ -17,9 +17,19 @@ TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWid
|
||||||
mButtonBox->addButton(QDialogButtonBox::Ok);
|
mButtonBox->addButton(QDialogButtonBox::Ok);
|
||||||
mButtonBox->addButton(QDialogButtonBox::Cancel);
|
mButtonBox->addButton(QDialogButtonBox::Cancel);
|
||||||
|
|
||||||
setMaximumHeight(height());
|
// Line edit
|
||||||
setOkButtonEnabled(false);
|
QValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore
|
||||||
setModal(true);
|
mLineEdit = new LineEdit(this);
|
||||||
|
mLineEdit->setValidator(validator);
|
||||||
|
mLineEdit->setCompleter(0);
|
||||||
|
|
||||||
|
QLabel *label = new QLabel(this);
|
||||||
|
label->setText(text);
|
||||||
|
|
||||||
|
QVBoxLayout *dialogLayout = new QVBoxLayout(this);
|
||||||
|
dialogLayout->addWidget(label);
|
||||||
|
dialogLayout->addWidget(mLineEdit);
|
||||||
|
dialogLayout->addWidget(mButtonBox);
|
||||||
|
|
||||||
// Messageboxes on mac have no title
|
// Messageboxes on mac have no title
|
||||||
#ifndef Q_OS_MAC
|
#ifndef Q_OS_MAC
|
||||||
|
@ -28,22 +38,13 @@ TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWid
|
||||||
Q_UNUSED(title);
|
Q_UNUSED(title);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QLabel *label = new QLabel(this);
|
setMaximumHeight(height());
|
||||||
label->setText(text);
|
setOkButtonEnabled(false);
|
||||||
|
setModal(true);
|
||||||
// Line edit
|
|
||||||
QValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore
|
|
||||||
mLineEdit = new LineEdit(this);
|
|
||||||
mLineEdit->setValidator(validator);
|
|
||||||
mLineEdit->setCompleter(0);
|
|
||||||
|
|
||||||
QVBoxLayout *dialogLayout = new QVBoxLayout(this);
|
|
||||||
dialogLayout->addWidget(label);
|
|
||||||
dialogLayout->addWidget(mLineEdit);
|
|
||||||
dialogLayout->addWidget(mButtonBox);
|
|
||||||
|
|
||||||
connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||||
connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int TextInputDialog::exec()
|
int TextInputDialog::exec()
|
||||||
|
@ -55,7 +56,17 @@ int TextInputDialog::exec()
|
||||||
|
|
||||||
void TextInputDialog::setOkButtonEnabled(bool enabled)
|
void TextInputDialog::setOkButtonEnabled(bool enabled)
|
||||||
{
|
{
|
||||||
|
|
||||||
QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok);
|
QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok);
|
||||||
okButton->setEnabled(enabled);
|
okButton->setEnabled(enabled);
|
||||||
|
|
||||||
|
QPalette *palette = new QPalette();
|
||||||
|
palette->setColor(QPalette::Text,Qt::red);
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
mLineEdit->setPalette(QApplication::palette());
|
||||||
|
} else {
|
||||||
|
// Existing profile name, make the text red
|
||||||
|
mLineEdit->setPalette(*palette);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
|
|
||||||
MwIniImporter::MwIniImporter()
|
MwIniImporter::MwIniImporter()
|
||||||
: mVerbose(false)
|
: mVerbose(false)
|
||||||
|
@ -18,8 +20,609 @@ MwIniImporter::MwIniImporter()
|
||||||
{ 0, 0 }
|
{ 0, 0 }
|
||||||
};
|
};
|
||||||
const char *fallback[] = {
|
const char *fallback[] = {
|
||||||
|
|
||||||
|
// light
|
||||||
|
"LightAttenuation:UseConstant",
|
||||||
|
"LightAttenuation:ConstantValue",
|
||||||
|
"LightAttenuation:UseLinear",
|
||||||
|
"LightAttenuation:LinearMethod",
|
||||||
|
"LightAttenuation:LinearValue",
|
||||||
|
"LightAttenuation:LinearRadiusMult",
|
||||||
|
"LightAttenuation:UseQuadratic",
|
||||||
|
"LightAttenuation:QuadraticMethod",
|
||||||
|
"LightAttenuation:QuadraticValue",
|
||||||
|
"LightAttenuation:QuadraticRadiusMult",
|
||||||
|
"LightAttenuation:OutQuadInLin",
|
||||||
|
|
||||||
|
// inventory
|
||||||
|
"Inventory:DirectionalDiffuseR",
|
||||||
|
"Inventory:DirectionalDiffuseG",
|
||||||
|
"Inventory:DirectionalDiffuseB",
|
||||||
|
"Inventory:DirectionalAmbientR",
|
||||||
|
"Inventory:DirectionalAmbientG",
|
||||||
|
"Inventory:DirectionalAmbientB",
|
||||||
|
"Inventory:DirectionalRotationX",
|
||||||
|
"Inventory:DirectionalRotationY",
|
||||||
|
"Inventory:UniformScaling",
|
||||||
|
|
||||||
|
// map
|
||||||
|
"Map:Travel Siltstrider Red",
|
||||||
|
"Map:Travel Siltstrider Green",
|
||||||
|
"Map:Travel Siltstrider Blue",
|
||||||
|
"Map:Travel Boat Red",
|
||||||
|
"Map:Travel Boat Green",
|
||||||
|
"Map:Travel Boat Blue",
|
||||||
|
"Map:Travel Magic Red",
|
||||||
|
"Map:Travel Magic Green",
|
||||||
|
"Map:Travel Magic Blue",
|
||||||
|
"Map:Show Travel Lines",
|
||||||
|
|
||||||
|
// water
|
||||||
|
"Water:Map Alpha",
|
||||||
|
"Water:World Alpha",
|
||||||
|
"Water:SurfaceTextureSize",
|
||||||
|
"Water:SurfaceTileCount",
|
||||||
|
"Water:SurfaceFPS",
|
||||||
|
"Water:SurfaceTexture",
|
||||||
|
"Water:SurfaceFrameCount",
|
||||||
|
"Water:TileTextureDivisor",
|
||||||
|
"Water:RippleTexture",
|
||||||
|
"Water:RippleFrameCount",
|
||||||
|
"Water:RippleLifetime",
|
||||||
|
"Water:MaxNumberRipples",
|
||||||
|
"Water:RippleScale",
|
||||||
|
"Water:RippleRotSpeed",
|
||||||
|
"Water:RippleAlphas",
|
||||||
|
"Water:PSWaterReflectTerrain",
|
||||||
|
"Water:PSWaterReflectUpdate",
|
||||||
|
"Water:NearWaterRadius",
|
||||||
|
"Water:NearWaterPoints",
|
||||||
|
"Water:NearWaterUnderwaterFreq",
|
||||||
|
"Water:NearWaterUnderwaterVolume",
|
||||||
|
"Water:NearWaterIndoorTolerance",
|
||||||
|
"Water:NearWaterOutdoorTolerance",
|
||||||
|
"Water:NearWaterIndoorID",
|
||||||
|
"Water:NearWaterOutdoorID",
|
||||||
|
"Water:UnderwaterSunriseFog",
|
||||||
|
"Water:UnderwaterDayFog",
|
||||||
|
"Water:UnderwaterSunsetFog",
|
||||||
|
"Water:UnderwaterNightFog",
|
||||||
|
"Water:UnderwaterIndoorFog",
|
||||||
|
"Water:UnderwaterColor",
|
||||||
|
"Water:UnderwaterColorWeight",
|
||||||
|
|
||||||
|
// pixelwater
|
||||||
|
"PixelWater:SurfaceFPS",
|
||||||
|
"PixelWater:TileCount",
|
||||||
|
"PixelWater:Resolution",
|
||||||
|
|
||||||
|
// fonts
|
||||||
|
"Fonts:Font 0",
|
||||||
|
"Fonts:Font 1",
|
||||||
|
"Fonts:Font 2",
|
||||||
|
|
||||||
|
// UI colors
|
||||||
|
"FontColor:color_normal",
|
||||||
|
"FontColor:color_normal_over",
|
||||||
|
"FontColor:color_normal_pressed",
|
||||||
|
"FontColor:color_active",
|
||||||
|
"FontColor:color_active_over",
|
||||||
|
"FontColor:color_active_pressed",
|
||||||
|
"FontColor:color_disabled",
|
||||||
|
"FontColor:color_disabled_over",
|
||||||
|
"FontColor:color_disabled_pressed",
|
||||||
|
"FontColor:color_link",
|
||||||
|
"FontColor:color_link_over",
|
||||||
|
"FontColor:color_link_pressed",
|
||||||
|
"FontColor:color_journal_link",
|
||||||
|
"FontColor:color_journal_link_over",
|
||||||
|
"FontColor:color_journal_link_pressed",
|
||||||
|
"FontColor:color_journal_topic",
|
||||||
|
"FontColor:color_journal_topic_over",
|
||||||
|
"FontColor:color_journal_topic_pressed",
|
||||||
|
"FontColor:color_answer",
|
||||||
|
"FontColor:color_answer_over",
|
||||||
|
"FontColor:color_answer_pressed",
|
||||||
|
"FontColor:color_header",
|
||||||
|
"FontColor:color_notify",
|
||||||
|
"FontColor:color_big_normal",
|
||||||
|
"FontColor:color_big_normal_over",
|
||||||
|
"FontColor:color_big_normal_pressed",
|
||||||
|
"FontColor:color_big_link",
|
||||||
|
"FontColor:color_big_link_over",
|
||||||
|
"FontColor:color_big_link_pressed",
|
||||||
|
"FontColor:color_big_answer",
|
||||||
|
"FontColor:color_big_answer_over",
|
||||||
|
"FontColor:color_big_answer_pressed",
|
||||||
|
"FontColor:color_big_header",
|
||||||
|
"FontColor:color_big_notify",
|
||||||
|
"FontColor:color_background",
|
||||||
|
"FontColor:color_focus",
|
||||||
|
"FontColor:color_health",
|
||||||
|
"FontColor:color_magic",
|
||||||
|
"FontColor:color_fatigue",
|
||||||
|
"FontColor:color_misc",
|
||||||
|
"FontColor:color_weapon_fill",
|
||||||
|
"FontColor:color_magic_fill",
|
||||||
|
"FontColor:color_positive",
|
||||||
|
"FontColor:color_negative",
|
||||||
|
"FontColor:color_count",
|
||||||
|
|
||||||
|
// level up messages
|
||||||
|
"Level Up:Level2",
|
||||||
|
"Level Up:Level3",
|
||||||
|
"Level Up:Level4",
|
||||||
|
"Level Up:Level5",
|
||||||
|
"Level Up:Level6",
|
||||||
|
"Level Up:Level7",
|
||||||
|
"Level Up:Level8",
|
||||||
|
"Level Up:Level9",
|
||||||
|
"Level Up:Level10",
|
||||||
|
"Level Up:Level11",
|
||||||
|
"Level Up:Level12",
|
||||||
|
"Level Up:Level13",
|
||||||
|
"Level Up:Level14",
|
||||||
|
"Level Up:Level15",
|
||||||
|
"Level Up:Level16",
|
||||||
|
"Level Up:Level17",
|
||||||
|
"Level Up:Level18",
|
||||||
|
"Level Up:Level19",
|
||||||
|
"Level Up:Level20",
|
||||||
|
"Level Up:Default",
|
||||||
|
|
||||||
|
// character creation multiple choice test
|
||||||
|
"Question 1:Question",
|
||||||
|
"Question 1:AnswerOne",
|
||||||
|
"Question 1:AnswerTwo",
|
||||||
|
"Question 1:AnswerThree",
|
||||||
|
"Question 1:Sound",
|
||||||
|
"Question 2:Question",
|
||||||
|
"Question 2:AnswerOne",
|
||||||
|
"Question 2:AnswerTwo",
|
||||||
|
"Question 2:AnswerThree",
|
||||||
|
"Question 2:Sound",
|
||||||
|
"Question 3:Question",
|
||||||
|
"Question 3:AnswerOne",
|
||||||
|
"Question 3:AnswerTwo",
|
||||||
|
"Question 3:AnswerThree",
|
||||||
|
"Question 3:Sound",
|
||||||
|
"Question 4:Question",
|
||||||
|
"Question 4:AnswerOne",
|
||||||
|
"Question 4:AnswerTwo",
|
||||||
|
"Question 4:AnswerThree",
|
||||||
|
"Question 4:Sound",
|
||||||
|
"Question 5:Question",
|
||||||
|
"Question 5:AnswerOne",
|
||||||
|
"Question 5:AnswerTwo",
|
||||||
|
"Question 5:AnswerThree",
|
||||||
|
"Question 5:Sound",
|
||||||
|
"Question 6:Question",
|
||||||
|
"Question 6:AnswerOne",
|
||||||
|
"Question 6:AnswerTwo",
|
||||||
|
"Question 6:AnswerThree",
|
||||||
|
"Question 6:Sound",
|
||||||
|
"Question 7:Question",
|
||||||
|
"Question 7:AnswerOne",
|
||||||
|
"Question 7:AnswerTwo",
|
||||||
|
"Question 7:AnswerThree",
|
||||||
|
"Question 7:Sound",
|
||||||
|
"Question 8:Question",
|
||||||
|
"Question 8:AnswerOne",
|
||||||
|
"Question 8:AnswerTwo",
|
||||||
|
"Question 8:AnswerThree",
|
||||||
|
"Question 8:Sound",
|
||||||
|
"Question 9:Question",
|
||||||
|
"Question 9:AnswerOne",
|
||||||
|
"Question 9:AnswerTwo",
|
||||||
|
"Question 9:AnswerThree",
|
||||||
|
"Question 9:Sound",
|
||||||
|
"Question 10:Question",
|
||||||
|
"Question 10:AnswerOne",
|
||||||
|
"Question 10:AnswerTwo",
|
||||||
|
"Question 10:AnswerThree",
|
||||||
|
"Question 10:Sound",
|
||||||
|
|
||||||
|
// blood textures and models
|
||||||
|
"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",
|
||||||
|
|
||||||
|
// movies
|
||||||
|
"Movies:Company Logo",
|
||||||
|
"Movies:Morrowind Logo",
|
||||||
|
"Movies:New Game",
|
||||||
|
"Movies:Loading",
|
||||||
|
"Movies:Options Menu",
|
||||||
|
|
||||||
|
// weather related values
|
||||||
|
|
||||||
|
"Weather Thunderstorm:Thunder Sound ID 0",
|
||||||
|
"Weather Thunderstorm:Thunder Sound ID 1",
|
||||||
|
"Weather Thunderstorm:Thunder Sound ID 2",
|
||||||
|
"Weather Thunderstorm:Thunder Sound ID 3",
|
||||||
"Weather:Sunrise Time",
|
"Weather:Sunrise Time",
|
||||||
"Weather:Sunset Time",
|
"Weather:Sunset Time",
|
||||||
|
"Weather:Sunrise Duration",
|
||||||
|
"Weather:Sunset Duration",
|
||||||
|
"Weather:Hours Between Weather Changes", // AKA weather update time
|
||||||
|
"Weather Thunderstorm:Thunder Frequency",
|
||||||
|
"Weather Thunderstorm:Thunder Threshold",
|
||||||
|
|
||||||
|
"Weather:EnvReduceColor",
|
||||||
|
"Weather:LerpCloseColor",
|
||||||
|
"Weather:BumpFadeColor",
|
||||||
|
"Weather:AlphaReduce",
|
||||||
|
"Weather:Minimum Time Between Environmental Sounds",
|
||||||
|
"Weather:Maximum Time Between Environmental Sounds",
|
||||||
|
"Weather:Sun Glare Fader Max",
|
||||||
|
"Weather:Sun Glare Fader Angle Max",
|
||||||
|
"Weather:Sun Glare Fader Color",
|
||||||
|
"Weather:Timescale Clouds",
|
||||||
|
"Weather:Precip Gravity",
|
||||||
|
"Weather:Rain Ripples",
|
||||||
|
"Weather:Rain Ripple Radius",
|
||||||
|
"Weather:Rain Ripples Per Drop",
|
||||||
|
"Weather:Rain Ripple Scale",
|
||||||
|
"Weather:Rain Ripple Speed",
|
||||||
|
"Weather:Fog Depth Change Speed",
|
||||||
|
"Weather:Sky Pre-Sunrise Time",
|
||||||
|
"Weather:Sky Post-Sunrise Time",
|
||||||
|
"Weather:Sky Pre-Sunset Time",
|
||||||
|
"Weather:Sky Post-Sunset Time",
|
||||||
|
"Weather:Ambient Pre-Sunrise Time",
|
||||||
|
"Weather:Ambient Post-Sunrise Time",
|
||||||
|
"Weather:Ambient Pre-Sunset Time",
|
||||||
|
"Weather:Ambient Post-Sunset Time",
|
||||||
|
"Weather:Fog Pre-Sunrise Time",
|
||||||
|
"Weather:Fog Post-Sunrise Time",
|
||||||
|
"Weather:Fog Pre-Sunset Time",
|
||||||
|
"Weather:Fog Post-Sunset Time",
|
||||||
|
"Weather:Sun Pre-Sunrise Time",
|
||||||
|
"Weather:Sun Post-Sunrise Time",
|
||||||
|
"Weather:Sun Pre-Sunset Time",
|
||||||
|
"Weather:Sun Post-Sunset Time",
|
||||||
|
"Weather:Stars Post-Sunset Start",
|
||||||
|
"Weather:Stars Pre-Sunrise Finish",
|
||||||
|
"Weather:Stars Fading Duration",
|
||||||
|
"Weather:Snow Ripples",
|
||||||
|
"Weather:Snow Ripple Radius",
|
||||||
|
"Weather:Snow Ripples Per Flake",
|
||||||
|
"Weather:Snow Ripple Scale",
|
||||||
|
"Weather:Snow Ripple Speed",
|
||||||
|
"Weather:Snow Gravity Scale",
|
||||||
|
"Weather:Snow High Kill",
|
||||||
|
"Weather:Snow Low Kill",
|
||||||
|
|
||||||
|
"Weather Clear:Cloud Texture",
|
||||||
|
"Weather Clear:Clouds Maximum Percent",
|
||||||
|
"Weather Clear:Transition Delta",
|
||||||
|
"Weather Clear:Sky Sunrise Color",
|
||||||
|
"Weather Clear:Sky Day Color",
|
||||||
|
"Weather Clear:Sky Sunset Color",
|
||||||
|
"Weather Clear:Sky Night Color",
|
||||||
|
"Weather Clear:Fog Sunrise Color",
|
||||||
|
"Weather Clear:Fog Day Color",
|
||||||
|
"Weather Clear:Fog Sunset Color",
|
||||||
|
"Weather Clear:Fog Night Color",
|
||||||
|
"Weather Clear:Ambient Sunrise Color",
|
||||||
|
"Weather Clear:Ambient Day Color",
|
||||||
|
"Weather Clear:Ambient Sunset Color",
|
||||||
|
"Weather Clear:Ambient Night Color",
|
||||||
|
"Weather Clear:Sun Sunrise Color",
|
||||||
|
"Weather Clear:Sun Day Color",
|
||||||
|
"Weather Clear:Sun Sunset Color",
|
||||||
|
"Weather Clear:Sun Night Color",
|
||||||
|
"Weather Clear:Sun Disc Sunset Color",
|
||||||
|
"Weather Clear:Land Fog Day Depth",
|
||||||
|
"Weather Clear:Land Fog Night Depth",
|
||||||
|
"Weather Clear:Wind Speed",
|
||||||
|
"Weather Clear:Cloud Speed",
|
||||||
|
"Weather Clear:Glare View",
|
||||||
|
"Weather Clear:Ambient Loop Sound ID",
|
||||||
|
|
||||||
|
"Weather Cloudy:Cloud Texture",
|
||||||
|
"Weather Cloudy:Clouds Maximum Percent",
|
||||||
|
"Weather Cloudy:Transition Delta",
|
||||||
|
"Weather Cloudy:Sky Sunrise Color",
|
||||||
|
"Weather Cloudy:Sky Day Color",
|
||||||
|
"Weather Cloudy:Sky Sunset Color",
|
||||||
|
"Weather Cloudy:Sky Night Color",
|
||||||
|
"Weather Cloudy:Fog Sunrise Color",
|
||||||
|
"Weather Cloudy:Fog Day Color",
|
||||||
|
"Weather Cloudy:Fog Sunset Color",
|
||||||
|
"Weather Cloudy:Fog Night Color",
|
||||||
|
"Weather Cloudy:Ambient Sunrise Color",
|
||||||
|
"Weather Cloudy:Ambient Day Color",
|
||||||
|
"Weather Cloudy:Ambient Sunset Color",
|
||||||
|
"Weather Cloudy:Ambient Night Color",
|
||||||
|
"Weather Cloudy:Sun Sunrise Color",
|
||||||
|
"Weather Cloudy:Sun Day Color",
|
||||||
|
"Weather Cloudy:Sun Sunset Color",
|
||||||
|
"Weather Cloudy:Sun Night Color",
|
||||||
|
"Weather Cloudy:Sun Disc Sunset Color",
|
||||||
|
"Weather Cloudy:Land Fog Day Depth",
|
||||||
|
"Weather Cloudy:Land Fog Night Depth",
|
||||||
|
"Weather Cloudy:Wind Speed",
|
||||||
|
"Weather Cloudy:Cloud Speed",
|
||||||
|
"Weather Cloudy:Glare View",
|
||||||
|
"Weather Cloudy:Ambient Loop Sound ID",
|
||||||
|
|
||||||
|
"Weather Foggy:Cloud Texture",
|
||||||
|
"Weather Foggy:Clouds Maximum Percent",
|
||||||
|
"Weather Foggy:Transition Delta",
|
||||||
|
"Weather Foggy:Sky Sunrise Color",
|
||||||
|
"Weather Foggy:Sky Day Color",
|
||||||
|
"Weather Foggy:Sky Sunset Color",
|
||||||
|
"Weather Foggy:Sky Night Color",
|
||||||
|
"Weather Foggy:Fog Sunrise Color",
|
||||||
|
"Weather Foggy:Fog Day Color",
|
||||||
|
"Weather Foggy:Fog Sunset Color",
|
||||||
|
"Weather Foggy:Fog Night Color",
|
||||||
|
"Weather Foggy:Ambient Sunrise Color",
|
||||||
|
"Weather Foggy:Ambient Day Color",
|
||||||
|
"Weather Foggy:Ambient Sunset Color",
|
||||||
|
"Weather Foggy:Ambient Night Color",
|
||||||
|
"Weather Foggy:Sun Sunrise Color",
|
||||||
|
"Weather Foggy:Sun Day Color",
|
||||||
|
"Weather Foggy:Sun Sunset Color",
|
||||||
|
"Weather Foggy:Sun Night Color",
|
||||||
|
"Weather Foggy:Sun Disc Sunset Color",
|
||||||
|
"Weather Foggy:Land Fog Day Depth",
|
||||||
|
"Weather Foggy:Land Fog Night Depth",
|
||||||
|
"Weather Foggy:Wind Speed",
|
||||||
|
"Weather Foggy:Cloud Speed",
|
||||||
|
"Weather Foggy:Glare View",
|
||||||
|
"Weather Foggy:Ambient Loop Sound ID",
|
||||||
|
|
||||||
|
"Weather Thunderstorm:Cloud Texture",
|
||||||
|
"Weather Thunderstorm:Clouds Maximum Percent",
|
||||||
|
"Weather Thunderstorm:Transition Delta",
|
||||||
|
"Weather Thunderstorm:Sky Sunrise Color",
|
||||||
|
"Weather Thunderstorm:Sky Day Color",
|
||||||
|
"Weather Thunderstorm:Sky Sunset Color",
|
||||||
|
"Weather Thunderstorm:Sky Night Color",
|
||||||
|
"Weather Thunderstorm:Fog Sunrise Color",
|
||||||
|
"Weather Thunderstorm:Fog Day Color",
|
||||||
|
"Weather Thunderstorm:Fog Sunset Color",
|
||||||
|
"Weather Thunderstorm:Fog Night Color",
|
||||||
|
"Weather Thunderstorm:Ambient Sunrise Color",
|
||||||
|
"Weather Thunderstorm:Ambient Day Color",
|
||||||
|
"Weather Thunderstorm:Ambient Sunset Color",
|
||||||
|
"Weather Thunderstorm:Ambient Night Color",
|
||||||
|
"Weather Thunderstorm:Sun Sunrise Color",
|
||||||
|
"Weather Thunderstorm:Sun Day Color",
|
||||||
|
"Weather Thunderstorm:Sun Sunset Color",
|
||||||
|
"Weather Thunderstorm:Sun Night Color",
|
||||||
|
"Weather Thunderstorm:Sun Disc Sunset Color",
|
||||||
|
"Weather Thunderstorm:Land Fog Day Depth",
|
||||||
|
"Weather Thunderstorm:Land Fog Night Depth",
|
||||||
|
"Weather Thunderstorm:Wind Speed",
|
||||||
|
"Weather Thunderstorm:Cloud Speed",
|
||||||
|
"Weather Thunderstorm:Glare View",
|
||||||
|
"Weather Thunderstorm:Rain Loop Sound ID",
|
||||||
|
"Weather Thunderstorm:Using Precip",
|
||||||
|
"Weather Thunderstorm:Rain Diameter",
|
||||||
|
"Weather Thunderstorm:Rain Height Min",
|
||||||
|
"Weather Thunderstorm:Rain Height Max",
|
||||||
|
"Weather Thunderstorm:Rain Threshold",
|
||||||
|
"Weather Thunderstorm:Max Raindrops",
|
||||||
|
"Weather Thunderstorm:Rain Entrance Speed",
|
||||||
|
"Weather Thunderstorm:Ambient Loop Sound ID",
|
||||||
|
"Weather Thunderstorm:Flash Decrement",
|
||||||
|
|
||||||
|
"Weather Rain:Cloud Texture",
|
||||||
|
"Weather Rain:Clouds Maximum Percent",
|
||||||
|
"Weather Rain:Transition Delta",
|
||||||
|
"Weather Rain:Sky Sunrise Color",
|
||||||
|
"Weather Rain:Sky Day Color",
|
||||||
|
"Weather Rain:Sky Sunset Color",
|
||||||
|
"Weather Rain:Sky Night Color",
|
||||||
|
"Weather Rain:Fog Sunrise Color",
|
||||||
|
"Weather Rain:Fog Day Color",
|
||||||
|
"Weather Rain:Fog Sunset Color",
|
||||||
|
"Weather Rain:Fog Night Color",
|
||||||
|
"Weather Rain:Ambient Sunrise Color",
|
||||||
|
"Weather Rain:Ambient Day Color",
|
||||||
|
"Weather Rain:Ambient Sunset Color",
|
||||||
|
"Weather Rain:Ambient Night Color",
|
||||||
|
"Weather Rain:Sun Sunrise Color",
|
||||||
|
"Weather Rain:Sun Day Color",
|
||||||
|
"Weather Rain:Sun Sunset Color",
|
||||||
|
"Weather Rain:Sun Night Color",
|
||||||
|
"Weather Rain:Sun Disc Sunset Color",
|
||||||
|
"Weather Rain:Land Fog Day Depth",
|
||||||
|
"Weather Rain:Land Fog Night Depth",
|
||||||
|
"Weather Rain:Wind Speed",
|
||||||
|
"Weather Rain:Cloud Speed",
|
||||||
|
"Weather Rain:Glare View",
|
||||||
|
"Weather Rain:Rain Loop Sound ID",
|
||||||
|
"Weather Rain:Using Precip",
|
||||||
|
"Weather Rain:Rain Diameter",
|
||||||
|
"Weather Rain:Rain Height Min",
|
||||||
|
"Weather Rain:Rain Height Max",
|
||||||
|
"Weather Rain:Rain Threshold",
|
||||||
|
"Weather Rain:Rain Entrance Speed",
|
||||||
|
"Weather Rain:Ambient Loop Sound ID",
|
||||||
|
"Weather Rain:Max Raindrops",
|
||||||
|
|
||||||
|
"Weather Overcast:Cloud Texture",
|
||||||
|
"Weather Overcast:Clouds Maximum Percent",
|
||||||
|
"Weather Overcast:Transition Delta",
|
||||||
|
"Weather Overcast:Sky Sunrise Color",
|
||||||
|
"Weather Overcast:Sky Day Color",
|
||||||
|
"Weather Overcast:Sky Sunset Color",
|
||||||
|
"Weather Overcast:Sky Night Color",
|
||||||
|
"Weather Overcast:Fog Sunrise Color",
|
||||||
|
"Weather Overcast:Fog Day Color",
|
||||||
|
"Weather Overcast:Fog Sunset Color",
|
||||||
|
"Weather Overcast:Fog Night Color",
|
||||||
|
"Weather Overcast:Ambient Sunrise Color",
|
||||||
|
"Weather Overcast:Ambient Day Color",
|
||||||
|
"Weather Overcast:Ambient Sunset Color",
|
||||||
|
"Weather Overcast:Ambient Night Color",
|
||||||
|
"Weather Overcast:Sun Sunrise Color",
|
||||||
|
"Weather Overcast:Sun Day Color",
|
||||||
|
"Weather Overcast:Sun Sunset Color",
|
||||||
|
"Weather Overcast:Sun Night Color",
|
||||||
|
"Weather Overcast:Sun Disc Sunset Color",
|
||||||
|
"Weather Overcast:Land Fog Day Depth",
|
||||||
|
"Weather Overcast:Land Fog Night Depth",
|
||||||
|
"Weather Overcast:Wind Speed",
|
||||||
|
"Weather Overcast:Cloud Speed",
|
||||||
|
"Weather Overcast:Glare View",
|
||||||
|
"Weather Overcast:Ambient Loop Sound ID",
|
||||||
|
|
||||||
|
"Weather Ashstorm:Cloud Texture",
|
||||||
|
"Weather Ashstorm:Clouds Maximum Percent",
|
||||||
|
"Weather Ashstorm:Transition Delta",
|
||||||
|
"Weather Ashstorm:Sky Sunrise Color",
|
||||||
|
"Weather Ashstorm:Sky Day Color",
|
||||||
|
"Weather Ashstorm:Sky Sunset Color",
|
||||||
|
"Weather Ashstorm:Sky Night Color",
|
||||||
|
"Weather Ashstorm:Fog Sunrise Color",
|
||||||
|
"Weather Ashstorm:Fog Day Color",
|
||||||
|
"Weather Ashstorm:Fog Sunset Color",
|
||||||
|
"Weather Ashstorm:Fog Night Color",
|
||||||
|
"Weather Ashstorm:Ambient Sunrise Color",
|
||||||
|
"Weather Ashstorm:Ambient Day Color",
|
||||||
|
"Weather Ashstorm:Ambient Sunset Color",
|
||||||
|
"Weather Ashstorm:Ambient Night Color",
|
||||||
|
"Weather Ashstorm:Sun Sunrise Color",
|
||||||
|
"Weather Ashstorm:Sun Day Color",
|
||||||
|
"Weather Ashstorm:Sun Sunset Color",
|
||||||
|
"Weather Ashstorm:Sun Night Color",
|
||||||
|
"Weather Ashstorm:Sun Disc Sunset Color",
|
||||||
|
"Weather Ashstorm:Land Fog Day Depth",
|
||||||
|
"Weather Ashstorm:Land Fog Night Depth",
|
||||||
|
"Weather Ashstorm:Wind Speed",
|
||||||
|
"Weather Ashstorm:Cloud Speed",
|
||||||
|
"Weather Ashstorm:Glare View",
|
||||||
|
"Weather Ashstorm:Ambient Loop Sound ID",
|
||||||
|
"Weather Ashstorm:Storm Threshold",
|
||||||
|
|
||||||
|
"Weather Blight:Cloud Texture",
|
||||||
|
"Weather Blight:Clouds Maximum Percent",
|
||||||
|
"Weather Blight:Transition Delta",
|
||||||
|
"Weather Blight:Sky Sunrise Color",
|
||||||
|
"Weather Blight:Sky Day Color",
|
||||||
|
"Weather Blight:Sky Sunset Color",
|
||||||
|
"Weather Blight:Sky Night Color",
|
||||||
|
"Weather Blight:Fog Sunrise Color",
|
||||||
|
"Weather Blight:Fog Day Color",
|
||||||
|
"Weather Blight:Fog Sunset Color",
|
||||||
|
"Weather Blight:Fog Night Color",
|
||||||
|
"Weather Blight:Ambient Sunrise Color",
|
||||||
|
"Weather Blight:Ambient Day Color",
|
||||||
|
"Weather Blight:Ambient Sunset Color",
|
||||||
|
"Weather Blight:Ambient Night Color",
|
||||||
|
"Weather Blight:Sun Sunrise Color",
|
||||||
|
"Weather Blight:Sun Day Color",
|
||||||
|
"Weather Blight:Sun Sunset Color",
|
||||||
|
"Weather Blight:Sun Night Color",
|
||||||
|
"Weather Blight:Sun Disc Sunset Color",
|
||||||
|
"Weather Blight:Land Fog Day Depth",
|
||||||
|
"Weather Blight:Land Fog Night Depth",
|
||||||
|
"Weather Blight:Wind Speed",
|
||||||
|
"Weather Blight:Cloud Speed",
|
||||||
|
"Weather Blight:Glare View",
|
||||||
|
"Weather Blight:Ambient Loop Sound ID",
|
||||||
|
"Weather Blight:Storm Threshold",
|
||||||
|
"Weather Blight:Disease Chance",
|
||||||
|
|
||||||
|
// for Bloodmoon
|
||||||
|
"Weather Snow:Cloud Texture",
|
||||||
|
"Weather Snow:Clouds Maximum Percent",
|
||||||
|
"Weather Snow:Transition Delta",
|
||||||
|
"Weather Snow:Sky Sunrise Color",
|
||||||
|
"Weather Snow:Sky Day Color",
|
||||||
|
"Weather Snow:Sky Sunset Color",
|
||||||
|
"Weather Snow:Sky Night Color",
|
||||||
|
"Weather Snow:Fog Sunrise Color",
|
||||||
|
"Weather Snow:Fog Day Color",
|
||||||
|
"Weather Snow:Fog Sunset Color",
|
||||||
|
"Weather Snow:Fog Night Color",
|
||||||
|
"Weather Snow:Ambient Sunrise Color",
|
||||||
|
"Weather Snow:Ambient Day Color",
|
||||||
|
"Weather Snow:Ambient Sunset Color",
|
||||||
|
"Weather Snow:Ambient Night Color",
|
||||||
|
"Weather Snow:Sun Sunrise Color",
|
||||||
|
"Weather Snow:Sun Day Color",
|
||||||
|
"Weather Snow:Sun Sunset Color",
|
||||||
|
"Weather Snow:Sun Night Color",
|
||||||
|
"Weather Snow:Sun Disc Sunset Color",
|
||||||
|
"Weather Snow:Land Fog Day Depth",
|
||||||
|
"Weather Snow:Land Fog Night Depth",
|
||||||
|
"Weather Snow:Wind Speed",
|
||||||
|
"Weather Snow:Cloud Speed",
|
||||||
|
"Weather Snow:Glare View",
|
||||||
|
"Weather Snow:Snow Diameter",
|
||||||
|
"Weather Snow:Snow Height Min",
|
||||||
|
"Weather Snow:Snow Height Max",
|
||||||
|
"Weather Snow:Snow Entrance Speed",
|
||||||
|
"Weather Snow:Max Snowflakes",
|
||||||
|
"Weather Snow:Ambient Loop Sound ID",
|
||||||
|
"Weather Snow:Snow Threshold",
|
||||||
|
|
||||||
|
// for Bloodmoon
|
||||||
|
"Weather Blizzard:Cloud Texture",
|
||||||
|
"Weather Blizzard:Clouds Maximum Percent",
|
||||||
|
"Weather Blizzard:Transition Delta",
|
||||||
|
"Weather Blizzard:Sky Sunrise Color",
|
||||||
|
"Weather Blizzard:Sky Day Color",
|
||||||
|
"Weather Blizzard:Sky Sunset Color",
|
||||||
|
"Weather Blizzard:Sky Night Color",
|
||||||
|
"Weather Blizzard:Fog Sunrise Color",
|
||||||
|
"Weather Blizzard:Fog Day Color",
|
||||||
|
"Weather Blizzard:Fog Sunset Color",
|
||||||
|
"Weather Blizzard:Fog Night Color",
|
||||||
|
"Weather Blizzard:Ambient Sunrise Color",
|
||||||
|
"Weather Blizzard:Ambient Day Color",
|
||||||
|
"Weather Blizzard:Ambient Sunset Color",
|
||||||
|
"Weather Blizzard:Ambient Night Color",
|
||||||
|
"Weather Blizzard:Sun Sunrise Color",
|
||||||
|
"Weather Blizzard:Sun Day Color",
|
||||||
|
"Weather Blizzard:Sun Sunset Color",
|
||||||
|
"Weather Blizzard:Sun Night Color",
|
||||||
|
"Weather Blizzard:Sun Disc Sunset Color",
|
||||||
|
"Weather Blizzard:Land Fog Day Depth",
|
||||||
|
"Weather Blizzard:Land Fog Night Depth",
|
||||||
|
"Weather Blizzard:Wind Speed",
|
||||||
|
"Weather Blizzard:Cloud Speed",
|
||||||
|
"Weather Blizzard:Glare View",
|
||||||
|
"Weather Blizzard:Ambient Loop Sound ID",
|
||||||
|
"Weather Blizzard:Storm Threshold",
|
||||||
|
|
||||||
|
// moons
|
||||||
|
"Moons:Secunda Size",
|
||||||
|
"Moons:Secunda Axis Offset",
|
||||||
|
"Moons:Secunda Speed",
|
||||||
|
"Moons:Secunda Daily Increment",
|
||||||
|
"Moons:Secunda Moon Shadow Early Fade Angle",
|
||||||
|
"Moons:Secunda Fade Start Angle",
|
||||||
|
"Moons:Secunda Fade End Angle",
|
||||||
|
"Moons:Secunda Fade In Start",
|
||||||
|
"Moons:Secunda Fade In Finish",
|
||||||
|
"Moons:Secunda Fade Out Start",
|
||||||
|
"Moons:Secunda Fade Out Finish",
|
||||||
|
"Moons:Masser Size",
|
||||||
|
"Moons:Masser Axis Offset",
|
||||||
|
"Moons:Masser Speed",
|
||||||
|
"Moons:Masser Daily Increment",
|
||||||
|
"Moons:Masser Moon Shadow Early Fade Angle",
|
||||||
|
"Moons:Masser Fade Start Angle",
|
||||||
|
"Moons:Masser Fade End Angle",
|
||||||
|
"Moons:Masser Fade In Start",
|
||||||
|
"Moons:Masser Fade In Finish",
|
||||||
|
"Moons:Masser Fade Out Start",
|
||||||
|
"Moons:Masser Fade Out Finish",
|
||||||
|
"Moons:Script Color",
|
||||||
|
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,14 +651,26 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(std::string filename) {
|
||||||
std::string section("");
|
std::string section("");
|
||||||
MwIniImporter::multistrmap map;
|
MwIniImporter::multistrmap map;
|
||||||
boost::iostreams::stream<boost::iostreams::file_source>file(filename.c_str());
|
boost::iostreams::stream<boost::iostreams::file_source>file(filename.c_str());
|
||||||
|
ToUTF8::Utf8Encoder encoder(mEncoding);
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(file, line)) {
|
while (std::getline(file, line)) {
|
||||||
|
|
||||||
if(line[0] == '[') {
|
line = encoder.getUtf8(line);
|
||||||
if(line.length() > 2) {
|
|
||||||
section = line.substr(1, line.length()-3);
|
// unify Unix-style and Windows file ending
|
||||||
|
if (!(line.empty()) && (line[line.length()-1]) == '\r') {
|
||||||
|
line = line.substr(0, line.length()-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(line[0] == '[') {
|
||||||
|
int pos = line.find(']');
|
||||||
|
if(pos < 2) {
|
||||||
|
std::cout << "Warning: ini file wrongly formatted (" << line << "). Line ignored." << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
section = line.substr(1, line.find(']')-1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +762,7 @@ void MwIniImporter::mergeFallback(multistrmap &cfg, multistrmap &ini) {
|
||||||
std::string value(*it);
|
std::string value(*it);
|
||||||
std::replace( value.begin(), value.end(), ' ', '_' );
|
std::replace( value.begin(), value.end(), ' ', '_' );
|
||||||
std::replace( value.begin(), value.end(), ':', '_' );
|
std::replace( value.begin(), value.end(), ':', '_' );
|
||||||
value.append(",").append(vc->substr(0,vc->length()-1));
|
value.append(",").append(vc->substr(0,vc->length()));
|
||||||
insertMultistrmap(cfg, "fallback", value);
|
insertMultistrmap(cfg, "fallback", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,6 +777,39 @@ void MwIniImporter::insertMultistrmap(multistrmap &cfg, std::string key, std::st
|
||||||
cfg[key].push_back(value);
|
cfg[key].push_back(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MwIniImporter::importArchives(multistrmap &cfg, multistrmap &ini) {
|
||||||
|
std::vector<std::string> archives;
|
||||||
|
std::string baseArchive("Archives:Archive ");
|
||||||
|
std::string archive;
|
||||||
|
|
||||||
|
// Search archives listed in ini file
|
||||||
|
multistrmap::iterator it = ini.begin();
|
||||||
|
for(int i=0; it != ini.end(); i++) {
|
||||||
|
archive = baseArchive;
|
||||||
|
archive.append(this->numberToString(i));
|
||||||
|
|
||||||
|
it = ini.find(archive);
|
||||||
|
if(it == ini.end()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(std::vector<std::string>::iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) {
|
||||||
|
archives.push_back(*entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.erase("fallback-archive");
|
||||||
|
cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("fallback-archive", std::vector<std::string>()));
|
||||||
|
|
||||||
|
// Add Morrowind.bsa by default, since Vanilla loads this archive even if it
|
||||||
|
// does not appears in the ini file
|
||||||
|
cfg["fallback-archive"].push_back("Morrowind.bsa");
|
||||||
|
|
||||||
|
for(std::vector<std::string>::iterator it=archives.begin(); it!=archives.end(); ++it) {
|
||||||
|
cfg["fallback-archive"].push_back(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MwIniImporter::importGameFiles(multistrmap &cfg, multistrmap &ini) {
|
void MwIniImporter::importGameFiles(multistrmap &cfg, multistrmap &ini) {
|
||||||
std::vector<std::string> esmFiles;
|
std::vector<std::string> esmFiles;
|
||||||
std::vector<std::string> espFiles;
|
std::vector<std::string> espFiles;
|
||||||
|
@ -179,8 +827,8 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, multistrmap &ini) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for(std::vector<std::string>::iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) {
|
for(std::vector<std::string>::iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) {
|
||||||
std::string filetype(entry->substr(entry->length()-4, 3));
|
std::string filetype(entry->substr(entry->length()-3));
|
||||||
std::transform(filetype.begin(), filetype.end(), filetype.begin(), ::tolower);
|
Misc::StringUtils::toLower(filetype);
|
||||||
|
|
||||||
if(filetype.compare("esm") == 0) {
|
if(filetype.compare("esm") == 0) {
|
||||||
esmFiles.push_back(*entry);
|
esmFiles.push_back(*entry);
|
||||||
|
@ -216,3 +864,8 @@ void MwIniImporter::writeToFile(boost::iostreams::stream<boost::iostreams::file_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MwIniImporter::setInputEncoding(const ToUTF8::FromType &encoding)
|
||||||
|
{
|
||||||
|
mEncoding = encoding;
|
||||||
|
}
|
||||||
|
|
|
@ -8,26 +8,32 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
|
||||||
|
#include <components/to_utf8/to_utf8.hpp>
|
||||||
|
|
||||||
class MwIniImporter {
|
class MwIniImporter {
|
||||||
public:
|
public:
|
||||||
typedef std::map<std::string, std::string> strmap;
|
typedef std::map<std::string, std::string> strmap;
|
||||||
typedef std::map<std::string, std::vector<std::string> > multistrmap;
|
typedef std::map<std::string, std::vector<std::string> > multistrmap;
|
||||||
|
|
||||||
MwIniImporter();
|
MwIniImporter();
|
||||||
|
void setInputEncoding(const ToUTF8::FromType& encoding);
|
||||||
void setVerbose(bool verbose);
|
void setVerbose(bool verbose);
|
||||||
multistrmap loadIniFile(std::string filename);
|
multistrmap loadIniFile(std::string filename);
|
||||||
multistrmap loadCfgFile(std::string filename);
|
multistrmap loadCfgFile(std::string filename);
|
||||||
void merge(multistrmap &cfg, multistrmap &ini);
|
void merge(multistrmap &cfg, multistrmap &ini);
|
||||||
void mergeFallback(multistrmap &cfg, multistrmap &ini);
|
void mergeFallback(multistrmap &cfg, multistrmap &ini);
|
||||||
void importGameFiles(multistrmap &cfg, multistrmap &ini);
|
void importGameFiles(multistrmap &cfg, multistrmap &ini);
|
||||||
|
void importArchives(multistrmap &cfg, multistrmap &ini);
|
||||||
void writeToFile(boost::iostreams::stream<boost::iostreams::file_sink> &out, multistrmap &cfg);
|
void writeToFile(boost::iostreams::stream<boost::iostreams::file_sink> &out, multistrmap &cfg);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void insertMultistrmap(multistrmap &cfg, std::string key, std::string value);
|
void insertMultistrmap(multistrmap &cfg, std::string key, std::string value);
|
||||||
std::string numberToString(int n);
|
std::string numberToString(int n);
|
||||||
|
std::string toUTF8(const std::string &str);
|
||||||
bool mVerbose;
|
bool mVerbose;
|
||||||
strmap mMergeMap;
|
strmap mMergeMap;
|
||||||
std::vector<std::string> mMergeFallback;
|
std::vector<std::string> mMergeFallback;
|
||||||
|
ToUTF8::FromType mEncoding;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,12 @@ int main(int argc, char *argv[]) {
|
||||||
("cfg,c", bpo::value<std::string>(), "openmw.cfg file")
|
("cfg,c", bpo::value<std::string>(), "openmw.cfg file")
|
||||||
("output,o", bpo::value<std::string>()->default_value(""), "openmw.cfg file")
|
("output,o", bpo::value<std::string>()->default_value(""), "openmw.cfg file")
|
||||||
("game-files,g", "import esm and esp files")
|
("game-files,g", "import esm and esp files")
|
||||||
|
("no-archives,A", "disable bsa archives import")
|
||||||
|
("encoding,e", bpo::value<std::string>()-> default_value("win1252"),
|
||||||
|
"Character encoding used in OpenMW game messages:\n"
|
||||||
|
"\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n"
|
||||||
|
"\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n"
|
||||||
|
"\n\twin1252 - Western European (Latin) alphabet, used by default")
|
||||||
;
|
;
|
||||||
p_desc.add("ini", 1).add("cfg", 1);
|
p_desc.add("ini", 1).add("cfg", 1);
|
||||||
|
|
||||||
|
@ -57,6 +63,10 @@ int main(int argc, char *argv[]) {
|
||||||
MwIniImporter importer;
|
MwIniImporter importer;
|
||||||
importer.setVerbose(vm.count("verbose"));
|
importer.setVerbose(vm.count("verbose"));
|
||||||
|
|
||||||
|
// Font encoding settings
|
||||||
|
std::string encoding(vm["encoding"].as<std::string>());
|
||||||
|
importer.setInputEncoding(ToUTF8::calculateEncoding(encoding));
|
||||||
|
|
||||||
MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile);
|
MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile);
|
||||||
MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile);
|
MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile);
|
||||||
|
|
||||||
|
@ -67,6 +77,10 @@ int main(int argc, char *argv[]) {
|
||||||
importer.importGameFiles(cfg, ini);
|
importer.importGameFiles(cfg, ini);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!vm.count("no-archives")) {
|
||||||
|
importer.importArchives(cfg, ini);
|
||||||
|
}
|
||||||
|
|
||||||
std::cout << "write to: " << outputFile << std::endl;
|
std::cout << "write to: " << outputFile << std::endl;
|
||||||
boost::iostreams::stream<boost::iostreams::file_sink> file(outputFile);
|
boost::iostreams::stream<boost::iostreams::file_sink> file(outputFile);
|
||||||
importer.writeToFile(file, cfg);
|
importer.writeToFile(file, cfg);
|
||||||
|
|
111
apps/opencs/CMakeLists.txt
Normal file
111
apps/opencs/CMakeLists.txt
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
|
||||||
|
set (OPENCS_SRC main.cpp)
|
||||||
|
|
||||||
|
opencs_units (. editor)
|
||||||
|
|
||||||
|
|
||||||
|
opencs_units (model/doc
|
||||||
|
document
|
||||||
|
)
|
||||||
|
|
||||||
|
opencs_units_noqt (model/doc
|
||||||
|
documentmanager
|
||||||
|
)
|
||||||
|
|
||||||
|
opencs_hdrs_noqt (model/doc
|
||||||
|
state
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
opencs_units (model/world
|
||||||
|
idtable idtableproxymodel
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
opencs_units_noqt (model/world
|
||||||
|
universalid data record idcollection commands columnbase
|
||||||
|
)
|
||||||
|
|
||||||
|
opencs_hdrs_noqt (model/world
|
||||||
|
columns
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
opencs_units (model/tools
|
||||||
|
tools operation reportmodel
|
||||||
|
)
|
||||||
|
|
||||||
|
opencs_units_noqt (model/tools
|
||||||
|
stage verifier mandatoryid
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
opencs_units (view/doc
|
||||||
|
viewmanager view operations operation subview startup filedialog
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
opencs_units_noqt (view/doc
|
||||||
|
subviewfactory
|
||||||
|
)
|
||||||
|
|
||||||
|
opencs_hdrs_noqt (view/doc
|
||||||
|
subviewfactoryimp
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
opencs_units (view/world
|
||||||
|
table tablesubview
|
||||||
|
)
|
||||||
|
|
||||||
|
opencs_units_noqt (view/world
|
||||||
|
dialoguesubview util subviews enumdelegate vartypedelegate
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
opencs_units (view/tools
|
||||||
|
reportsubview
|
||||||
|
)
|
||||||
|
|
||||||
|
opencs_units_noqt (view/tools
|
||||||
|
subviews
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
set (OPENCS_US
|
||||||
|
)
|
||||||
|
|
||||||
|
set (OPENCS_RES ../../files/opencs/resources.qrc
|
||||||
|
../../files/launcher/launcher.qrc
|
||||||
|
)
|
||||||
|
|
||||||
|
set (OPENCS_UI ../../files/ui/datafilespage.ui
|
||||||
|
)
|
||||||
|
|
||||||
|
source_group (opencs FILES ${OPENCS_SRC} ${OPENCS_HDR})
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
set(QT_USE_QTMAIN TRUE)
|
||||||
|
endif(WIN32)
|
||||||
|
|
||||||
|
find_package(Qt4 COMPONENTS QtCore QtGui QtXml QtXmlPatterns REQUIRED)
|
||||||
|
include(${QT_USE_FILE})
|
||||||
|
|
||||||
|
qt4_wrap_ui(OPENCS_UI_HDR ${OPENCS_UI})
|
||||||
|
qt4_wrap_cpp(OPENCS_MOC_SRC ${OPENCS_HDR_QT})
|
||||||
|
qt4_add_resources(OPENCS_RES_SRC ${OPENCS_RES})
|
||||||
|
|
||||||
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
add_executable(opencs
|
||||||
|
${OPENCS_SRC}
|
||||||
|
${OPENCS_UI_HDR}
|
||||||
|
${OPENCS_MOC_SRC}
|
||||||
|
${OPENCS_RES_SRC}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(opencs
|
||||||
|
${Boost_LIBRARIES}
|
||||||
|
${QT_LIBRARIES}
|
||||||
|
components
|
||||||
|
)
|
117
apps/opencs/editor.cpp
Normal file
117
apps/opencs/editor.cpp
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
|
||||||
|
#include "editor.hpp"
|
||||||
|
|
||||||
|
#include <QtGui/QApplication>
|
||||||
|
|
||||||
|
#include "model/doc/document.hpp"
|
||||||
|
#include "model/world/data.hpp"
|
||||||
|
|
||||||
|
CS::Editor::Editor() : mViewManager (mDocumentManager)
|
||||||
|
{
|
||||||
|
connect (&mViewManager, SIGNAL (newDocumentRequest ()), this, SLOT (createDocument ()));
|
||||||
|
connect (&mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ()));
|
||||||
|
|
||||||
|
connect (&mStartup, SIGNAL (createDocument()), this, SLOT (createDocument ()));
|
||||||
|
connect (&mStartup, SIGNAL (loadDocument()), this, SLOT (loadDocument ()));
|
||||||
|
|
||||||
|
connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles()));
|
||||||
|
connect (&mFileDialog, SIGNAL(createNewFile()), this, SLOT(createNewFile()));
|
||||||
|
|
||||||
|
setupDataFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CS::Editor::setupDataFiles()
|
||||||
|
{
|
||||||
|
boost::program_options::variables_map variables;
|
||||||
|
boost::program_options::options_description desc;
|
||||||
|
|
||||||
|
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"));
|
||||||
|
|
||||||
|
boost::program_options::notify(variables);
|
||||||
|
|
||||||
|
mCfgMgr.readConfiguration(variables, desc);
|
||||||
|
|
||||||
|
Files::PathContainer mDataDirs, mDataLocal;
|
||||||
|
if (!variables["data"].empty()) {
|
||||||
|
mDataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string local = variables["data-local"].as<std::string>();
|
||||||
|
if (!local.empty()) {
|
||||||
|
mDataLocal.push_back(Files::PathContainer::value_type(local));
|
||||||
|
}
|
||||||
|
|
||||||
|
mCfgMgr.processPaths(mDataDirs);
|
||||||
|
mCfgMgr.processPaths(mDataLocal);
|
||||||
|
|
||||||
|
// Set the charset for reading the esm/esp files
|
||||||
|
QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
|
||||||
|
mFileDialog.setEncoding(encoding);
|
||||||
|
|
||||||
|
Files::PathContainer dataDirs;
|
||||||
|
dataDirs.insert(dataDirs.end(), mDataDirs.begin(), mDataDirs.end());
|
||||||
|
dataDirs.insert(dataDirs.end(), mDataLocal.begin(), mDataLocal.end());
|
||||||
|
|
||||||
|
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
|
||||||
|
{
|
||||||
|
QString path = QString::fromStdString(iter->string());
|
||||||
|
mFileDialog.addFiles(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CS::Editor::createDocument()
|
||||||
|
{
|
||||||
|
mStartup.hide();
|
||||||
|
|
||||||
|
mFileDialog.newFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CS::Editor::loadDocument()
|
||||||
|
{
|
||||||
|
mStartup.hide();
|
||||||
|
|
||||||
|
mFileDialog.openFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CS::Editor::openFiles()
|
||||||
|
{
|
||||||
|
std::vector<boost::filesystem::path> files;
|
||||||
|
QStringList paths = mFileDialog.checkedItemsPaths();
|
||||||
|
|
||||||
|
foreach (const QString &path, paths) {
|
||||||
|
files.push_back(path.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMDoc::Document *document = mDocumentManager.addDocument(files, false);
|
||||||
|
|
||||||
|
mViewManager.addView (document);
|
||||||
|
mFileDialog.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CS::Editor::createNewFile()
|
||||||
|
{
|
||||||
|
std::vector<boost::filesystem::path> files;
|
||||||
|
QStringList paths = mFileDialog.checkedItemsPaths();
|
||||||
|
|
||||||
|
foreach (const QString &path, paths) {
|
||||||
|
files.push_back(path.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
files.push_back(mFileDialog.fileName().toStdString());
|
||||||
|
|
||||||
|
CSMDoc::Document *document = mDocumentManager.addDocument (files, true);
|
||||||
|
|
||||||
|
mViewManager.addView (document);
|
||||||
|
mFileDialog.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
int CS::Editor::run()
|
||||||
|
{
|
||||||
|
mStartup.show();
|
||||||
|
|
||||||
|
return QApplication::exec();
|
||||||
|
}
|
50
apps/opencs/editor.hpp
Normal file
50
apps/opencs/editor.hpp
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef CS_EDITOR_H
|
||||||
|
#define CS_EDITOR_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <components/files/configurationmanager.hpp>
|
||||||
|
|
||||||
|
#include "model/doc/documentmanager.hpp"
|
||||||
|
|
||||||
|
#include "view/doc/viewmanager.hpp"
|
||||||
|
#include "view/doc/startup.hpp"
|
||||||
|
#include "view/doc/filedialog.hpp"
|
||||||
|
|
||||||
|
namespace CS
|
||||||
|
{
|
||||||
|
class Editor : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
CSMDoc::DocumentManager mDocumentManager;
|
||||||
|
CSVDoc::ViewManager mViewManager;
|
||||||
|
CSVDoc::StartupDialogue mStartup;
|
||||||
|
FileDialog mFileDialog;
|
||||||
|
|
||||||
|
Files::ConfigurationManager mCfgMgr;
|
||||||
|
|
||||||
|
void setupDataFiles();
|
||||||
|
|
||||||
|
// not implemented
|
||||||
|
Editor (const Editor&);
|
||||||
|
Editor& operator= (const Editor&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Editor();
|
||||||
|
|
||||||
|
int run();
|
||||||
|
///< \return error status
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void createDocument();
|
||||||
|
|
||||||
|
void loadDocument();
|
||||||
|
void openFiles();
|
||||||
|
void createNewFile();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
43
apps/opencs/main.cpp
Normal file
43
apps/opencs/main.cpp
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
|
||||||
|
#include "editor.hpp"
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <QtGui/QApplication>
|
||||||
|
#include <QIcon>
|
||||||
|
|
||||||
|
class Application : public QApplication
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool notify (QObject *receiver, QEvent *event)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return QApplication::notify (receiver, event);
|
||||||
|
}
|
||||||
|
catch (const std::exception& exception)
|
||||||
|
{
|
||||||
|
std::cerr << "An exception has been caught: " << exception.what() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Application (int& argc, char *argv[]) : QApplication (argc, argv) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
Q_INIT_RESOURCE (resources);
|
||||||
|
Application mApplication (argc, argv);
|
||||||
|
|
||||||
|
mApplication.setWindowIcon (QIcon (":./opencs.png"));
|
||||||
|
|
||||||
|
CS::Editor editor;
|
||||||
|
|
||||||
|
return editor.run();
|
||||||
|
}
|
336
apps/opencs/model/doc/document.cpp
Normal file
336
apps/opencs/model/doc/document.cpp
Normal file
|
@ -0,0 +1,336 @@
|
||||||
|
|
||||||
|
#include "document.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
void CSMDoc::Document::load (const std::vector<boost::filesystem::path>::const_iterator& begin,
|
||||||
|
const std::vector<boost::filesystem::path>::const_iterator& end, bool lastAsModified)
|
||||||
|
{
|
||||||
|
assert (begin!=end);
|
||||||
|
|
||||||
|
std::vector<boost::filesystem::path>::const_iterator end2 (end);
|
||||||
|
|
||||||
|
if (lastAsModified)
|
||||||
|
--end2;
|
||||||
|
|
||||||
|
for (std::vector<boost::filesystem::path>::const_iterator iter (begin); iter!=end2; ++iter)
|
||||||
|
getData().loadFile (*iter, true);
|
||||||
|
|
||||||
|
if (lastAsModified)
|
||||||
|
getData().loadFile (*end2, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMDoc::Document::addOptionalGmsts()
|
||||||
|
{
|
||||||
|
static const char *sFloats[] =
|
||||||
|
{
|
||||||
|
"fCombatDistanceWerewolfMod",
|
||||||
|
"fFleeDistance",
|
||||||
|
"fWereWolfAcrobatics",
|
||||||
|
"fWereWolfAgility",
|
||||||
|
"fWereWolfAlchemy",
|
||||||
|
"fWereWolfAlteration",
|
||||||
|
"fWereWolfArmorer",
|
||||||
|
"fWereWolfAthletics",
|
||||||
|
"fWereWolfAxe",
|
||||||
|
"fWereWolfBlock",
|
||||||
|
"fWereWolfBluntWeapon",
|
||||||
|
"fWereWolfConjuration",
|
||||||
|
"fWereWolfDestruction",
|
||||||
|
"fWereWolfEnchant",
|
||||||
|
"fWereWolfEndurance",
|
||||||
|
"fWereWolfFatigue",
|
||||||
|
"fWereWolfHandtoHand",
|
||||||
|
"fWereWolfHealth",
|
||||||
|
"fWereWolfHeavyArmor",
|
||||||
|
"fWereWolfIllusion",
|
||||||
|
"fWereWolfIntellegence",
|
||||||
|
"fWereWolfLightArmor",
|
||||||
|
"fWereWolfLongBlade",
|
||||||
|
"fWereWolfLuck",
|
||||||
|
"fWereWolfMagicka",
|
||||||
|
"fWereWolfMarksman",
|
||||||
|
"fWereWolfMediumArmor",
|
||||||
|
"fWereWolfMerchantile",
|
||||||
|
"fWereWolfMysticism",
|
||||||
|
"fWereWolfPersonality",
|
||||||
|
"fWereWolfRestoration",
|
||||||
|
"fWereWolfRunMult",
|
||||||
|
"fWereWolfSecurity",
|
||||||
|
"fWereWolfShortBlade",
|
||||||
|
"fWereWolfSilverWeaponDamageMult",
|
||||||
|
"fWereWolfSneak",
|
||||||
|
"fWereWolfSpear",
|
||||||
|
"fWereWolfSpeechcraft",
|
||||||
|
"fWereWolfSpeed",
|
||||||
|
"fWereWolfStrength",
|
||||||
|
"fWereWolfUnarmored",
|
||||||
|
"fWereWolfWillPower",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *sIntegers[] =
|
||||||
|
{
|
||||||
|
"iWereWolfBounty",
|
||||||
|
"iWereWolfFightMod",
|
||||||
|
"iWereWolfFleeMod",
|
||||||
|
"iWereWolfLevelToAttack",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *sStrings[] =
|
||||||
|
{
|
||||||
|
"sCompanionShare",
|
||||||
|
"sCompanionWarningButtonOne",
|
||||||
|
"sCompanionWarningButtonTwo",
|
||||||
|
"sCompanionWarningMessage",
|
||||||
|
"sDeleteNote",
|
||||||
|
"sEditNote",
|
||||||
|
"sEffectSummonCreature01",
|
||||||
|
"sEffectSummonCreature02",
|
||||||
|
"sEffectSummonCreature03",
|
||||||
|
"sEffectSummonCreature04",
|
||||||
|
"sEffectSummonCreature05",
|
||||||
|
"sEffectSummonFabricant",
|
||||||
|
"sLevitateDisabled",
|
||||||
|
"sMagicCreature01ID",
|
||||||
|
"sMagicCreature02ID",
|
||||||
|
"sMagicCreature03ID",
|
||||||
|
"sMagicCreature04ID",
|
||||||
|
"sMagicCreature05ID",
|
||||||
|
"sMagicFabricantID",
|
||||||
|
"sMaxSale",
|
||||||
|
"sProfitValue",
|
||||||
|
"sTeleportDisabled",
|
||||||
|
"sWerewolfAlarmMessage",
|
||||||
|
"sWerewolfPopup",
|
||||||
|
"sWerewolfRefusal",
|
||||||
|
"sWerewolfRestMessage",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i=0; sFloats[i]; ++i)
|
||||||
|
{
|
||||||
|
ESM::GameSetting gmst;
|
||||||
|
gmst.mId = sFloats[i];
|
||||||
|
gmst.mValue.setType (ESM::VT_Float);
|
||||||
|
addOptionalGmst (gmst);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; sIntegers[i]; ++i)
|
||||||
|
{
|
||||||
|
ESM::GameSetting gmst;
|
||||||
|
gmst.mId = sIntegers[i];
|
||||||
|
gmst.mValue.setType (ESM::VT_Int);
|
||||||
|
addOptionalGmst (gmst);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; sStrings[i]; ++i)
|
||||||
|
{
|
||||||
|
ESM::GameSetting gmst;
|
||||||
|
gmst.mId = sStrings[i];
|
||||||
|
gmst.mValue.setType (ESM::VT_String);
|
||||||
|
gmst.mValue.setString ("<no text>");
|
||||||
|
addOptionalGmst (gmst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMDoc::Document::addOptionalGlobals()
|
||||||
|
{
|
||||||
|
static const char *sGlobals[] =
|
||||||
|
{
|
||||||
|
"dayspassed",
|
||||||
|
"pcwerewolf",
|
||||||
|
"pcyear",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i=0; sGlobals[i]; ++i)
|
||||||
|
{
|
||||||
|
ESM::Global global;
|
||||||
|
global.mId = sGlobals[i];
|
||||||
|
global.mValue.setType (ESM::VT_Long);
|
||||||
|
addOptionalGlobal (global);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMDoc::Document::addOptionalGmst (const ESM::GameSetting& gmst)
|
||||||
|
{
|
||||||
|
if (getData().getGmsts().searchId (gmst.mId)==-1)
|
||||||
|
{
|
||||||
|
CSMWorld::Record<ESM::GameSetting> record;
|
||||||
|
record.mBase = gmst;
|
||||||
|
record.mState = CSMWorld::RecordBase::State_BaseOnly;
|
||||||
|
getData().getGmsts().appendRecord (record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMDoc::Document::addOptionalGlobal (const ESM::Global& global)
|
||||||
|
{
|
||||||
|
if (getData().getGlobals().searchId (global.mId)==-1)
|
||||||
|
{
|
||||||
|
CSMWorld::Record<ESM::Global> record;
|
||||||
|
record.mBase = global;
|
||||||
|
record.mState = CSMWorld::RecordBase::State_BaseOnly;
|
||||||
|
getData().getGlobals().appendRecord (record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMDoc::Document::createBase()
|
||||||
|
{
|
||||||
|
static const char *sGlobals[] =
|
||||||
|
{
|
||||||
|
"Day", "DaysPassed", "GameHour", "Month", "PCRace", "PCVampire", "PCWerewolf", "PCYear", 0
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i=0; sGlobals[i]; ++i)
|
||||||
|
{
|
||||||
|
ESM::Global record;
|
||||||
|
|
||||||
|
record.mId = sGlobals[i];
|
||||||
|
|
||||||
|
record.mValue.setType (i==2 ? ESM::VT_Float : ESM::VT_Int);
|
||||||
|
|
||||||
|
if (i==0)
|
||||||
|
record.mValue.setInteger (1);
|
||||||
|
|
||||||
|
getData().getGlobals().add (record);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \todo add GMSTs
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files, bool new_)
|
||||||
|
: mTools (mData)
|
||||||
|
{
|
||||||
|
if (files.empty())
|
||||||
|
throw std::runtime_error ("Empty content file sequence");
|
||||||
|
|
||||||
|
/// \todo adjust last file name:
|
||||||
|
/// \li make sure it is located in the data-local directory (adjust path if necessary)
|
||||||
|
/// \li make sure the extension matches the new scheme (change it if necesarry)
|
||||||
|
|
||||||
|
mName = files.back().filename().string();
|
||||||
|
|
||||||
|
if (new_ && files.size()==1)
|
||||||
|
createBase();
|
||||||
|
else if (files.size()>1)
|
||||||
|
{
|
||||||
|
std::vector<boost::filesystem::path>::const_iterator end = files.end();
|
||||||
|
|
||||||
|
if (new_)
|
||||||
|
--end;
|
||||||
|
|
||||||
|
load (files.begin(), end, !new_);
|
||||||
|
}
|
||||||
|
|
||||||
|
addOptionalGmsts();
|
||||||
|
addOptionalGlobals();
|
||||||
|
|
||||||
|
connect (&mUndoStack, SIGNAL (cleanChanged (bool)), this, SLOT (modificationStateChanged (bool)));
|
||||||
|
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
QUndoStack& CSMDoc::Document::getUndoStack()
|
||||||
|
{
|
||||||
|
return mUndoStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMDoc::Document::getState() const
|
||||||
|
{
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
if (!mUndoStack.isClean())
|
||||||
|
state |= State_Modified;
|
||||||
|
|
||||||
|
if (mSaveCount)
|
||||||
|
state |= State_Locked | State_Saving | State_Operation;
|
||||||
|
|
||||||
|
if (int operations = mTools.getRunningOperations())
|
||||||
|
state |= State_Locked | State_Operation | operations;
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& CSMDoc::Document::getName() const
|
||||||
|
{
|
||||||
|
return mName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMDoc::Document::save()
|
||||||
|
{
|
||||||
|
mSaveCount = 1;
|
||||||
|
mSaveTimer.start (500);
|
||||||
|
emit stateChanged (getState(), this);
|
||||||
|
emit progress (1, 16, State_Saving, 1, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::UniversalId CSMDoc::Document::verify()
|
||||||
|
{
|
||||||
|
CSMWorld::UniversalId id = mTools.runVerifier();
|
||||||
|
emit stateChanged (getState(), this);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMDoc::Document::abortOperation (int type)
|
||||||
|
{
|
||||||
|
mTools.abortOperation (type);
|
||||||
|
|
||||||
|
if (type==State_Saving)
|
||||||
|
{
|
||||||
|
mSaveCount=0;
|
||||||
|
mSaveTimer.stop();
|
||||||
|
emit stateChanged (getState(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMDoc::Document::modificationStateChanged (bool clean)
|
||||||
|
{
|
||||||
|
emit stateChanged (getState(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
mSaveCount = 0;
|
||||||
|
mSaveTimer.stop();
|
||||||
|
mUndoStack.setClean();
|
||||||
|
emit stateChanged (getState(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CSMWorld::Data& CSMDoc::Document::getData() const
|
||||||
|
{
|
||||||
|
return mData;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::Data& CSMDoc::Document::getData()
|
||||||
|
{
|
||||||
|
return mData;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMTools::ReportModel *CSMDoc::Document::getReport (const CSMWorld::UniversalId& id)
|
||||||
|
{
|
||||||
|
return mTools.getReport (id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMDoc::Document::progress (int current, int max, int type)
|
||||||
|
{
|
||||||
|
emit progress (current, max, type, 1, this);
|
||||||
|
}
|
108
apps/opencs/model/doc/document.hpp
Normal file
108
apps/opencs/model/doc/document.hpp
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
#ifndef CSM_DOC_DOCUMENT_H
|
||||||
|
#define CSM_DOC_DOCUMENT_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
|
#include <QUndoStack>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "../world/data.hpp"
|
||||||
|
|
||||||
|
#include "../tools/tools.hpp"
|
||||||
|
|
||||||
|
#include "state.hpp"
|
||||||
|
|
||||||
|
class QAbstractItemModel;
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
struct GameSetting;
|
||||||
|
struct Global;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSMDoc
|
||||||
|
{
|
||||||
|
class Document : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::string mName; ///< \todo replace name with ESX list
|
||||||
|
CSMWorld::Data mData;
|
||||||
|
CSMTools::Tools mTools;
|
||||||
|
|
||||||
|
// 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&);
|
||||||
|
|
||||||
|
void load (const std::vector<boost::filesystem::path>::const_iterator& begin,
|
||||||
|
const std::vector<boost::filesystem::path>::const_iterator& end, bool lastAsModified);
|
||||||
|
///< \param lastAsModified Store the last file in Modified instead of merging it into Base.
|
||||||
|
|
||||||
|
void createBase();
|
||||||
|
|
||||||
|
void addOptionalGmsts();
|
||||||
|
|
||||||
|
void addOptionalGlobals();
|
||||||
|
|
||||||
|
void addOptionalGmst (const ESM::GameSetting& gmst);
|
||||||
|
|
||||||
|
void addOptionalGlobal (const ESM::Global& global);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Document (const std::vector<boost::filesystem::path>& files, bool new_);
|
||||||
|
|
||||||
|
QUndoStack& getUndoStack();
|
||||||
|
|
||||||
|
int getState() const;
|
||||||
|
|
||||||
|
const std::string& getName() const;
|
||||||
|
///< \todo replace with ESX list
|
||||||
|
|
||||||
|
void save();
|
||||||
|
|
||||||
|
CSMWorld::UniversalId verify();
|
||||||
|
|
||||||
|
void abortOperation (int type);
|
||||||
|
|
||||||
|
const CSMWorld::Data& getData() const;
|
||||||
|
|
||||||
|
CSMWorld::Data& getData();
|
||||||
|
|
||||||
|
CSMTools::ReportModel *getReport (const CSMWorld::UniversalId& id);
|
||||||
|
///< The ownership of the returned report is not transferred.
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void stateChanged (int state, CSMDoc::Document *document);
|
||||||
|
|
||||||
|
void progress (int current, int max, int type, int threads, CSMDoc::Document *document);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void modificationStateChanged (bool clean);
|
||||||
|
|
||||||
|
void operationDone (int type);
|
||||||
|
|
||||||
|
void saving();
|
||||||
|
///< dummy implementation -> remove when proper save is implemented.
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
void progress (int current, int max, int type);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
38
apps/opencs/model/doc/documentmanager.cpp
Normal file
38
apps/opencs/model/doc/documentmanager.cpp
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
|
||||||
|
#include "documentmanager.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "document.hpp"
|
||||||
|
|
||||||
|
CSMDoc::DocumentManager::DocumentManager() {}
|
||||||
|
|
||||||
|
CSMDoc::DocumentManager::~DocumentManager()
|
||||||
|
{
|
||||||
|
for (std::vector<Document *>::iterator iter (mDocuments.begin()); iter!=mDocuments.end(); ++iter)
|
||||||
|
delete *iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files,
|
||||||
|
bool new_)
|
||||||
|
{
|
||||||
|
Document *document = new Document (files, new_);
|
||||||
|
|
||||||
|
mDocuments.push_back (document);
|
||||||
|
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMDoc::DocumentManager::removeDocument (Document *document)
|
||||||
|
{
|
||||||
|
std::vector<Document *>::iterator iter = std::find (mDocuments.begin(), mDocuments.end(), document);
|
||||||
|
|
||||||
|
if (iter==mDocuments.end())
|
||||||
|
throw std::runtime_error ("removing invalid document");
|
||||||
|
|
||||||
|
mDocuments.erase (iter);
|
||||||
|
delete document;
|
||||||
|
|
||||||
|
return mDocuments.empty();
|
||||||
|
}
|
37
apps/opencs/model/doc/documentmanager.hpp
Normal file
37
apps/opencs/model/doc/documentmanager.hpp
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef CSM_DOC_DOCUMENTMGR_H
|
||||||
|
#define CSM_DOC_DOCUMENTMGR_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
|
namespace CSMDoc
|
||||||
|
{
|
||||||
|
class Document;
|
||||||
|
|
||||||
|
class DocumentManager
|
||||||
|
{
|
||||||
|
std::vector<Document *> mDocuments;
|
||||||
|
|
||||||
|
DocumentManager (const DocumentManager&);
|
||||||
|
DocumentManager& operator= (const DocumentManager&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DocumentManager();
|
||||||
|
|
||||||
|
~DocumentManager();
|
||||||
|
|
||||||
|
Document *addDocument (const std::vector<boost::filesystem::path>& files, 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
|
||||||
|
/// appropriate way.
|
||||||
|
|
||||||
|
bool removeDocument (Document *document);
|
||||||
|
///< \return last document removed?
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
19
apps/opencs/model/doc/state.hpp
Normal file
19
apps/opencs/model/doc/state.hpp
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef CSM_DOC_STATE_H
|
||||||
|
#define CSM_DOC_STATE_H
|
||||||
|
|
||||||
|
namespace CSMDoc
|
||||||
|
{
|
||||||
|
enum State
|
||||||
|
{
|
||||||
|
State_Modified = 1,
|
||||||
|
State_Locked = 2,
|
||||||
|
State_Operation = 4,
|
||||||
|
|
||||||
|
State_Saving = 8,
|
||||||
|
State_Verifying = 16,
|
||||||
|
State_Compiling = 32, // not implemented yet
|
||||||
|
State_Searching = 64 // not implemented yet
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
21
apps/opencs/model/tools/mandatoryid.cpp
Normal file
21
apps/opencs/model/tools/mandatoryid.cpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
#include "mandatoryid.hpp"
|
||||||
|
|
||||||
|
#include "../world/idcollection.hpp"
|
||||||
|
|
||||||
|
CSMTools::MandatoryIdStage::MandatoryIdStage (const CSMWorld::IdCollectionBase& idCollection,
|
||||||
|
const CSMWorld::UniversalId& collectionId, const std::vector<std::string>& ids)
|
||||||
|
: mIdCollection (idCollection), mCollectionId (collectionId), mIds (ids)
|
||||||
|
{}
|
||||||
|
|
||||||
|
int CSMTools::MandatoryIdStage::setup()
|
||||||
|
{
|
||||||
|
return mIds.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMTools::MandatoryIdStage::perform (int stage, std::vector<std::string>& messages)
|
||||||
|
{
|
||||||
|
if (mIdCollection.searchId (mIds.at (stage))==-1 ||
|
||||||
|
mIdCollection.getRecord (mIds.at (stage)).isDeleted())
|
||||||
|
messages.push_back (mCollectionId.toString() + "|Missing mandatory record: " + mIds.at (stage));
|
||||||
|
}
|
38
apps/opencs/model/tools/mandatoryid.hpp
Normal file
38
apps/opencs/model/tools/mandatoryid.hpp
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef CSM_TOOLS_MANDATORYID_H
|
||||||
|
#define CSM_TOOLS_MANDATORYID_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "../world/universalid.hpp"
|
||||||
|
|
||||||
|
#include "stage.hpp"
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
class IdCollectionBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSMTools
|
||||||
|
{
|
||||||
|
/// \brief Verify stage: make sure that records with specific IDs exist.
|
||||||
|
class MandatoryIdStage : public Stage
|
||||||
|
{
|
||||||
|
const CSMWorld::IdCollectionBase& mIdCollection;
|
||||||
|
CSMWorld::UniversalId mCollectionId;
|
||||||
|
std::vector<std::string> mIds;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
MandatoryIdStage (const CSMWorld::IdCollectionBase& idCollection, const CSMWorld::UniversalId& collectionId,
|
||||||
|
const std::vector<std::string>& ids);
|
||||||
|
|
||||||
|
virtual int setup();
|
||||||
|
///< \return number of steps
|
||||||
|
|
||||||
|
virtual void perform (int stage, std::vector<std::string>& messages);
|
||||||
|
///< Messages resulting from this tage will be appended to \a messages.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
84
apps/opencs/model/tools/operation.cpp
Normal file
84
apps/opencs/model/tools/operation.cpp
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
|
||||||
|
#include "operation.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "../doc/state.hpp"
|
||||||
|
|
||||||
|
#include "stage.hpp"
|
||||||
|
|
||||||
|
void CSMTools::Operation::prepareStages()
|
||||||
|
{
|
||||||
|
mCurrentStage = mStages.begin();
|
||||||
|
mCurrentStep = 0;
|
||||||
|
mCurrentStepTotal = 0;
|
||||||
|
mTotalSteps = 0;
|
||||||
|
|
||||||
|
for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter)
|
||||||
|
{
|
||||||
|
iter->second = iter->first->setup();
|
||||||
|
mTotalSteps += iter->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMTools::Operation::Operation (int type) : mType (type) {}
|
||||||
|
|
||||||
|
CSMTools::Operation::~Operation()
|
||||||
|
{
|
||||||
|
for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter)
|
||||||
|
delete iter->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMTools::Operation::run()
|
||||||
|
{
|
||||||
|
prepareStages();
|
||||||
|
|
||||||
|
QTimer timer;
|
||||||
|
|
||||||
|
timer.connect (&timer, SIGNAL (timeout()), this, SLOT (verify()));
|
||||||
|
|
||||||
|
timer.start (0);
|
||||||
|
|
||||||
|
exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMTools::Operation::appendStage (Stage *stage)
|
||||||
|
{
|
||||||
|
mStages.push_back (std::make_pair (stage, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMTools::Operation::abort()
|
||||||
|
{
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMTools::Operation::verify()
|
||||||
|
{
|
||||||
|
std::vector<std::string> messages;
|
||||||
|
|
||||||
|
while (mCurrentStage!=mStages.end())
|
||||||
|
{
|
||||||
|
if (mCurrentStep>=mCurrentStage->second)
|
||||||
|
{
|
||||||
|
mCurrentStep = 0;
|
||||||
|
++mCurrentStage;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mCurrentStage->first->perform (mCurrentStep++, messages);
|
||||||
|
++mCurrentStepTotal;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit progress (mCurrentStepTotal, mTotalSteps ? mTotalSteps : 1, mType);
|
||||||
|
|
||||||
|
for (std::vector<std::string>::const_iterator iter (messages.begin()); iter!=messages.end(); ++iter)
|
||||||
|
emit reportMessage (iter->c_str(), mType);
|
||||||
|
|
||||||
|
if (mCurrentStage==mStages.end())
|
||||||
|
exit();
|
||||||
|
}
|
54
apps/opencs/model/tools/operation.hpp
Normal file
54
apps/opencs/model/tools/operation.hpp
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#ifndef CSM_TOOLS_OPERATION_H
|
||||||
|
#define CSM_TOOLS_OPERATION_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
namespace CSMTools
|
||||||
|
{
|
||||||
|
class Stage;
|
||||||
|
|
||||||
|
class Operation : public QThread
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
int mType;
|
||||||
|
std::vector<std::pair<Stage *, int> > mStages; // stage, number of steps
|
||||||
|
std::vector<std::pair<Stage *, int> >::iterator mCurrentStage;
|
||||||
|
int mCurrentStep;
|
||||||
|
int mCurrentStepTotal;
|
||||||
|
int mTotalSteps;
|
||||||
|
|
||||||
|
void prepareStages();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Operation (int type);
|
||||||
|
|
||||||
|
virtual ~Operation();
|
||||||
|
|
||||||
|
virtual void run();
|
||||||
|
|
||||||
|
void appendStage (Stage *stage);
|
||||||
|
///< The ownership of \a stage is transferred to *this.
|
||||||
|
///
|
||||||
|
/// \attention Do no call this function while this Operation is running.
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void progress (int current, int max, int type);
|
||||||
|
|
||||||
|
void reportMessage (const QString& message, int type);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
void abort();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void verify();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
71
apps/opencs/model/tools/reportmodel.cpp
Normal file
71
apps/opencs/model/tools/reportmodel.cpp
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
|
||||||
|
#include "reportmodel.hpp"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
int CSMTools::ReportModel::rowCount (const QModelIndex & parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return mRows.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMTools::ReportModel::columnCount (const QModelIndex & parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant CSMTools::ReportModel::data (const QModelIndex & index, int role) const
|
||||||
|
{
|
||||||
|
if (role!=Qt::DisplayRole)
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (index.column()==0)
|
||||||
|
return static_cast<int> (mRows.at (index.row()).first.getType());
|
||||||
|
else
|
||||||
|
return mRows.at (index.row()).second.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant CSMTools::ReportModel::headerData (int section, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
if (role!=Qt::DisplayRole)
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (orientation==Qt::Vertical)
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
return tr (section==0 ? "Type" : "Description");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMTools::ReportModel::removeRows (int row, int count, const QModelIndex& parent)
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
mRows.erase (mRows.begin()+row, mRows.begin()+row+count);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMTools::ReportModel::add (const std::string& row)
|
||||||
|
{
|
||||||
|
std::string::size_type index = row.find ('|');
|
||||||
|
|
||||||
|
if (index==std::string::npos)
|
||||||
|
throw std::logic_error ("invalid report message");
|
||||||
|
|
||||||
|
beginInsertRows (QModelIndex(), mRows.size(), mRows.size());
|
||||||
|
|
||||||
|
mRows.push_back (std::make_pair (row.substr (0, index), row.substr (index+1)));
|
||||||
|
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
const CSMWorld::UniversalId& CSMTools::ReportModel::getUniversalId (int row) const
|
||||||
|
{
|
||||||
|
return mRows.at (row).first;
|
||||||
|
}
|
37
apps/opencs/model/tools/reportmodel.hpp
Normal file
37
apps/opencs/model/tools/reportmodel.hpp
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef CSM_TOOLS_REPORTMODEL_H
|
||||||
|
#define CSM_TOOLS_REPORTMODEL_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <QAbstractTableModel>
|
||||||
|
|
||||||
|
#include "../world/universalid.hpp"
|
||||||
|
|
||||||
|
namespace CSMTools
|
||||||
|
{
|
||||||
|
class ReportModel : public QAbstractTableModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
std::vector<std::pair<CSMWorld::UniversalId, std::string> > mRows;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual int rowCount (const QModelIndex & parent = QModelIndex()) const;
|
||||||
|
|
||||||
|
virtual int columnCount (const QModelIndex & parent = QModelIndex()) const;
|
||||||
|
|
||||||
|
virtual QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const;
|
||||||
|
|
||||||
|
virtual QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||||
|
|
||||||
|
virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex());
|
||||||
|
|
||||||
|
void add (const std::string& row);
|
||||||
|
|
||||||
|
const CSMWorld::UniversalId& getUniversalId (int row) const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
4
apps/opencs/model/tools/stage.cpp
Normal file
4
apps/opencs/model/tools/stage.cpp
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
#include "stage.hpp"
|
||||||
|
|
||||||
|
CSMTools::Stage::~Stage() {}
|
24
apps/opencs/model/tools/stage.hpp
Normal file
24
apps/opencs/model/tools/stage.hpp
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef CSM_TOOLS_STAGE_H
|
||||||
|
#define CSM_TOOLS_STAGE_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace CSMTools
|
||||||
|
{
|
||||||
|
class Stage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual ~Stage();
|
||||||
|
|
||||||
|
virtual int setup() = 0;
|
||||||
|
///< \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.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
123
apps/opencs/model/tools/tools.cpp
Normal file
123
apps/opencs/model/tools/tools.cpp
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
|
||||||
|
#include "tools.hpp"
|
||||||
|
|
||||||
|
#include <QThreadPool>
|
||||||
|
|
||||||
|
#include "verifier.hpp"
|
||||||
|
|
||||||
|
#include "../doc/state.hpp"
|
||||||
|
|
||||||
|
#include "../world/data.hpp"
|
||||||
|
#include "../world/universalid.hpp"
|
||||||
|
|
||||||
|
#include "reportmodel.hpp"
|
||||||
|
#include "mandatoryid.hpp"
|
||||||
|
|
||||||
|
CSMTools::Operation *CSMTools::Tools::get (int type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case CSMDoc::State_Verifying: return mVerifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CSMTools::Operation *CSMTools::Tools::get (int type) const
|
||||||
|
{
|
||||||
|
return const_cast<Tools *> (this)->get (type);
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMTools::Verifier *CSMTools::Tools::getVerifier()
|
||||||
|
{
|
||||||
|
if (!mVerifier)
|
||||||
|
{
|
||||||
|
mVerifier = new Verifier;
|
||||||
|
|
||||||
|
connect (mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int)));
|
||||||
|
connect (mVerifier, SIGNAL (finished()), this, SLOT (verifierDone()));
|
||||||
|
connect (mVerifier, SIGNAL (reportMessage (const QString&, int)),
|
||||||
|
this, SLOT (verifierMessage (const QString&, int)));
|
||||||
|
|
||||||
|
std::vector<std::string> mandatoryIds; // I want C++11, damn it!
|
||||||
|
mandatoryIds.push_back ("Day");
|
||||||
|
mandatoryIds.push_back ("DaysPassed");
|
||||||
|
mandatoryIds.push_back ("GameHour");
|
||||||
|
mandatoryIds.push_back ("Month");
|
||||||
|
mandatoryIds.push_back ("PCRace");
|
||||||
|
mandatoryIds.push_back ("PCVampire");
|
||||||
|
mandatoryIds.push_back ("PCWerewolf");
|
||||||
|
mandatoryIds.push_back ("PCYear");
|
||||||
|
|
||||||
|
mVerifier->appendStage (new MandatoryIdStage (mData.getGlobals(),
|
||||||
|
CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), mandatoryIds));
|
||||||
|
}
|
||||||
|
|
||||||
|
return mVerifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMTools::Tools::Tools (CSMWorld::Data& data) : mData (data), mVerifier (0), mNextReportNumber (0)
|
||||||
|
{
|
||||||
|
for (std::map<int, ReportModel *>::iterator iter (mReports.begin()); iter!=mReports.end(); ++iter)
|
||||||
|
delete iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMTools::Tools::~Tools()
|
||||||
|
{
|
||||||
|
delete mVerifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::UniversalId CSMTools::Tools::runVerifier()
|
||||||
|
{
|
||||||
|
mReports.insert (std::make_pair (mNextReportNumber++, new ReportModel));
|
||||||
|
mActiveReports[CSMDoc::State_Verifying] = mNextReportNumber-1;
|
||||||
|
|
||||||
|
getVerifier()->start();
|
||||||
|
|
||||||
|
return CSMWorld::UniversalId (CSMWorld::UniversalId::Type_VerificationResults, mNextReportNumber-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMTools::Tools::abortOperation (int type)
|
||||||
|
{
|
||||||
|
if (Operation *operation = get (type))
|
||||||
|
operation->abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMTools::Tools::getRunningOperations() const
|
||||||
|
{
|
||||||
|
static const int sOperations[] =
|
||||||
|
{
|
||||||
|
CSMDoc::State_Verifying,
|
||||||
|
-1
|
||||||
|
};
|
||||||
|
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
for (int i=0; sOperations[i]!=-1; ++i)
|
||||||
|
if (const Operation *operation = get (sOperations[i]))
|
||||||
|
if (operation->isRunning())
|
||||||
|
result |= sOperations[i];
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId& id)
|
||||||
|
{
|
||||||
|
if (id.getType()!=CSMWorld::UniversalId::Type_VerificationResults)
|
||||||
|
throw std::logic_error ("invalid request for report model: " + id.toString());
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
73
apps/opencs/model/tools/tools.hpp
Normal file
73
apps/opencs/model/tools/tools.hpp
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
#ifndef CSM_TOOLS_TOOLS_H
|
||||||
|
#define CSM_TOOLS_TOOLS_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
class Data;
|
||||||
|
class UniversalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSMTools
|
||||||
|
{
|
||||||
|
class Verifier;
|
||||||
|
class Operation;
|
||||||
|
class ReportModel;
|
||||||
|
|
||||||
|
class Tools : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
CSMWorld::Data& mData;
|
||||||
|
Verifier *mVerifier;
|
||||||
|
std::map<int, ReportModel *> mReports;
|
||||||
|
int mNextReportNumber;
|
||||||
|
std::map<int, int> mActiveReports; // type, report number
|
||||||
|
|
||||||
|
// not implemented
|
||||||
|
Tools (const Tools&);
|
||||||
|
Tools& operator= (const Tools&);
|
||||||
|
|
||||||
|
Verifier *getVerifier();
|
||||||
|
|
||||||
|
Operation *get (int type);
|
||||||
|
///< Returns a 0-pointer, if operation hasn't been used yet.
|
||||||
|
|
||||||
|
const Operation *get (int type) const;
|
||||||
|
///< Returns a 0-pointer, if operation hasn't been used yet.
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Tools (CSMWorld::Data& data);
|
||||||
|
|
||||||
|
virtual ~Tools();
|
||||||
|
|
||||||
|
CSMWorld::UniversalId runVerifier();
|
||||||
|
///< \return ID of the report for this verification run
|
||||||
|
|
||||||
|
void abortOperation (int type);
|
||||||
|
///< \attention The operation is not aborted immediately.
|
||||||
|
|
||||||
|
int getRunningOperations() const;
|
||||||
|
|
||||||
|
ReportModel *getReport (const CSMWorld::UniversalId& id);
|
||||||
|
///< The ownership of the returned report is not transferred.
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void verifierDone();
|
||||||
|
|
||||||
|
void verifierMessage (const QString& message, int type);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void progress (int current, int max, int type);
|
||||||
|
|
||||||
|
void done (int type);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
7
apps/opencs/model/tools/verifier.cpp
Normal file
7
apps/opencs/model/tools/verifier.cpp
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
#include "verifier.hpp"
|
||||||
|
|
||||||
|
#include "../doc/state.hpp"
|
||||||
|
|
||||||
|
CSMTools::Verifier::Verifier() : Operation (CSMDoc::State_Verifying)
|
||||||
|
{}
|
17
apps/opencs/model/tools/verifier.hpp
Normal file
17
apps/opencs/model/tools/verifier.hpp
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef CSM_TOOLS_VERIFIER_H
|
||||||
|
#define CSM_TOOLS_VERIFIER_H
|
||||||
|
|
||||||
|
#include "operation.hpp"
|
||||||
|
|
||||||
|
namespace CSMTools
|
||||||
|
{
|
||||||
|
class Verifier : public Operation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
Verifier();
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
13
apps/opencs/model/world/columnbase.cpp
Normal file
13
apps/opencs/model/world/columnbase.cpp
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
|
||||||
|
#include "columnbase.hpp"
|
||||||
|
|
||||||
|
CSMWorld::ColumnBase::ColumnBase (const std::string& title, Display displayType, int flags)
|
||||||
|
: mTitle (title), mDisplayType (displayType), mFlags (flags)
|
||||||
|
{}
|
||||||
|
|
||||||
|
CSMWorld::ColumnBase::~ColumnBase() {}
|
||||||
|
|
||||||
|
bool CSMWorld::ColumnBase::isUserEditable() const
|
||||||
|
{
|
||||||
|
return isEditable();
|
||||||
|
}
|
70
apps/opencs/model/world/columnbase.hpp
Normal file
70
apps/opencs/model/world/columnbase.hpp
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
#ifndef CSM_WOLRD_COLUMNBASE_H
|
||||||
|
#define CSM_WOLRD_COLUMNBASE_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <Qt>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include "record.hpp"
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
struct ColumnBase
|
||||||
|
{
|
||||||
|
enum Roles
|
||||||
|
{
|
||||||
|
Role_Flags = Qt::UserRole,
|
||||||
|
Role_Display = Qt::UserRole+1
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Flags
|
||||||
|
{
|
||||||
|
Flag_Table = 1, // column should be displayed in table view
|
||||||
|
Flag_Dialogue = 2 // column should be displayed in dialogue view
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Display
|
||||||
|
{
|
||||||
|
Display_String,
|
||||||
|
Display_Integer,
|
||||||
|
Display_Float,
|
||||||
|
Display_Var,
|
||||||
|
Display_GmstVarType,
|
||||||
|
Display_GlobalVarType
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string mTitle;
|
||||||
|
int mFlags;
|
||||||
|
Display mDisplayType;
|
||||||
|
|
||||||
|
ColumnBase (const std::string& title, Display displayType, int flag);
|
||||||
|
|
||||||
|
virtual ~ColumnBase();
|
||||||
|
|
||||||
|
virtual bool isEditable() const = 0;
|
||||||
|
|
||||||
|
virtual bool isUserEditable() const;
|
||||||
|
///< Can this column be edited directly by the user?
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
struct Column : public ColumnBase
|
||||||
|
{
|
||||||
|
std::string mTitle;
|
||||||
|
int mFlags;
|
||||||
|
|
||||||
|
Column (const std::string& title, Display displayType, int flags = Flag_Table | Flag_Dialogue)
|
||||||
|
: ColumnBase (title, displayType, flags) {}
|
||||||
|
|
||||||
|
virtual QVariant get (const Record<ESXRecordT>& record) const = 0;
|
||||||
|
|
||||||
|
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||||
|
{
|
||||||
|
throw std::logic_error ("Column " + mTitle + " is not editable");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
182
apps/opencs/model/world/columns.hpp
Normal file
182
apps/opencs/model/world/columns.hpp
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
#ifndef CSM_WOLRD_COLUMNS_H
|
||||||
|
#define CSM_WOLRD_COLUMNS_H
|
||||||
|
|
||||||
|
#include "columnbase.hpp"
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
struct FloatValueColumn : public Column<ESXRecordT>
|
||||||
|
{
|
||||||
|
FloatValueColumn() : Column<ESXRecordT> ("Value", ColumnBase::Display_Float) {}
|
||||||
|
|
||||||
|
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||||
|
{
|
||||||
|
return record.get().mValue.getFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||||
|
{
|
||||||
|
ESXRecordT record2 = record.get();
|
||||||
|
record2.mValue.setFloat (data.toFloat());
|
||||||
|
record.setModified (record2);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isEditable() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
struct StringIdColumn : public Column<ESXRecordT>
|
||||||
|
{
|
||||||
|
StringIdColumn() : Column<ESXRecordT> ("ID", ColumnBase::Display_String) {}
|
||||||
|
|
||||||
|
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||||
|
{
|
||||||
|
return record.get().mId.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isEditable() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
struct RecordStateColumn : public Column<ESXRecordT>
|
||||||
|
{
|
||||||
|
RecordStateColumn() : Column<ESXRecordT> ("*", ColumnBase::Display_Integer) {}
|
||||||
|
|
||||||
|
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||||
|
{
|
||||||
|
if (record.mState==Record<ESXRecordT>::State_Erased)
|
||||||
|
return static_cast<int> (Record<ESXRecordT>::State_Deleted);
|
||||||
|
|
||||||
|
return static_cast<int> (record.mState);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||||
|
{
|
||||||
|
record.mState = static_cast<RecordBase::State> (data.toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isEditable() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isUserEditable() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
struct FixedRecordTypeColumn : public Column<ESXRecordT>
|
||||||
|
{
|
||||||
|
int mType;
|
||||||
|
|
||||||
|
FixedRecordTypeColumn (int type)
|
||||||
|
: Column<ESXRecordT> ("Record Type", ColumnBase::Display_Integer, 0), mType (type) {}
|
||||||
|
|
||||||
|
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||||
|
{
|
||||||
|
return mType;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isEditable() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \attention A var type column must be immediately followed by a suitable value column.
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
struct VarTypeColumn : public Column<ESXRecordT>
|
||||||
|
{
|
||||||
|
VarTypeColumn (ColumnBase::Display display) : Column<ESXRecordT> ("Type", display) {}
|
||||||
|
|
||||||
|
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||||
|
{
|
||||||
|
return static_cast<int> (record.get().mValue.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||||
|
{
|
||||||
|
ESXRecordT record2 = record.get();
|
||||||
|
record2.mValue.setType (static_cast<ESM::VarType> (data.toInt()));
|
||||||
|
record.setModified (record2);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isEditable() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
struct VarValueColumn : public Column<ESXRecordT>
|
||||||
|
{
|
||||||
|
VarValueColumn() : Column<ESXRecordT> ("Value", ColumnBase::Display_Var) {}
|
||||||
|
|
||||||
|
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||||
|
{
|
||||||
|
switch (record.get().mValue.getType())
|
||||||
|
{
|
||||||
|
case ESM::VT_String:
|
||||||
|
|
||||||
|
return record.get().mValue.getString().c_str(); break;
|
||||||
|
|
||||||
|
case ESM::VT_Int:
|
||||||
|
case ESM::VT_Short:
|
||||||
|
case ESM::VT_Long:
|
||||||
|
|
||||||
|
return record.get().mValue.getInteger(); break;
|
||||||
|
|
||||||
|
case ESM::VT_Float:
|
||||||
|
|
||||||
|
return record.get().mValue.getFloat(); break;
|
||||||
|
|
||||||
|
default: return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||||
|
{
|
||||||
|
ESXRecordT record2 = record.get();
|
||||||
|
|
||||||
|
switch (record2.mValue.getType())
|
||||||
|
{
|
||||||
|
case ESM::VT_String:
|
||||||
|
|
||||||
|
record2.mValue.setString (data.toString().toUtf8().constData());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ESM::VT_Int:
|
||||||
|
case ESM::VT_Short:
|
||||||
|
case ESM::VT_Long:
|
||||||
|
|
||||||
|
record2.mValue.setInteger (data.toInt());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ESM::VT_Float:
|
||||||
|
|
||||||
|
record2.mValue.setFloat (data.toFloat());
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
record.setModified (record2);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isEditable() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
108
apps/opencs/model/world/commands.cpp
Normal file
108
apps/opencs/model/world/commands.cpp
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
|
||||||
|
#include "commands.hpp"
|
||||||
|
|
||||||
|
#include <QAbstractTableModel>
|
||||||
|
|
||||||
|
#include "idtableproxymodel.hpp"
|
||||||
|
#include "idtable.hpp"
|
||||||
|
|
||||||
|
CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
|
||||||
|
const QVariant& new_, QUndoCommand *parent)
|
||||||
|
: QUndoCommand (parent), mModel (model), mIndex (index), mNew (new_)
|
||||||
|
{
|
||||||
|
mOld = mModel.data (mIndex, Qt::EditRole);
|
||||||
|
|
||||||
|
setText ("Modify " + mModel.headerData (mIndex.column(), Qt::Horizontal, Qt::DisplayRole).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::ModifyCommand::redo()
|
||||||
|
{
|
||||||
|
mModel.setData (mIndex, mNew);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::ModifyCommand::undo()
|
||||||
|
{
|
||||||
|
mModel.setData (mIndex, mOld);
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::CreateCommand::CreateCommand (IdTableProxyModel& model, const std::string& id, QUndoCommand *parent)
|
||||||
|
: QUndoCommand (parent), mModel (model), mId (id)
|
||||||
|
{
|
||||||
|
setText (("Create record " + id).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::CreateCommand::redo()
|
||||||
|
{
|
||||||
|
mModel.addRecord (mId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::CreateCommand::undo()
|
||||||
|
{
|
||||||
|
mModel.removeRow (mModel.getModelIndex (mId, 0).row());
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
mOld = model.getRecord (id).clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::RevertCommand::~RevertCommand()
|
||||||
|
{
|
||||||
|
delete mOld;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::RevertCommand::redo()
|
||||||
|
{
|
||||||
|
QModelIndex index = mModel.getModelIndex (mId, 1);
|
||||||
|
RecordBase::State state = static_cast<RecordBase::State> (mModel.data (index).toInt());
|
||||||
|
|
||||||
|
if (state==RecordBase::State_ModifiedOnly)
|
||||||
|
{
|
||||||
|
mModel.removeRows (index.row(), 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mModel.setData (index, static_cast<int> (RecordBase::State_BaseOnly));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::RevertCommand::undo()
|
||||||
|
{
|
||||||
|
mModel.setRecord (*mOld);
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
mOld = model.getRecord (id).clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::DeleteCommand::~DeleteCommand()
|
||||||
|
{
|
||||||
|
delete mOld;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::DeleteCommand::redo()
|
||||||
|
{
|
||||||
|
QModelIndex index = mModel.getModelIndex (mId, 1);
|
||||||
|
RecordBase::State state = static_cast<RecordBase::State> (mModel.data (index).toInt());
|
||||||
|
|
||||||
|
if (state==RecordBase::State_ModifiedOnly)
|
||||||
|
{
|
||||||
|
mModel.removeRows (index.row(), 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mModel.setData (index, static_cast<int> (RecordBase::State_Deleted));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::DeleteCommand::undo()
|
||||||
|
{
|
||||||
|
mModel.setRecord (*mOld);
|
||||||
|
}
|
95
apps/opencs/model/world/commands.hpp
Normal file
95
apps/opencs/model/world/commands.hpp
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
#ifndef CSM_WOLRD_COMMANDS_H
|
||||||
|
#define CSM_WOLRD_COMMANDS_H
|
||||||
|
|
||||||
|
#include "record.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QUndoCommand>
|
||||||
|
#include <QModelIndex>
|
||||||
|
|
||||||
|
class QModelIndex;
|
||||||
|
class QAbstractItemModel;
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
class IdTableProxyModel;
|
||||||
|
class IdTable;
|
||||||
|
class RecordBase;
|
||||||
|
|
||||||
|
class ModifyCommand : public QUndoCommand
|
||||||
|
{
|
||||||
|
QAbstractItemModel& mModel;
|
||||||
|
QModelIndex mIndex;
|
||||||
|
QVariant mNew;
|
||||||
|
QVariant mOld;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, const QVariant& new_,
|
||||||
|
QUndoCommand *parent = 0);
|
||||||
|
|
||||||
|
virtual void redo();
|
||||||
|
|
||||||
|
virtual void undo();
|
||||||
|
};
|
||||||
|
|
||||||
|
class CreateCommand : public QUndoCommand
|
||||||
|
{
|
||||||
|
IdTableProxyModel& mModel;
|
||||||
|
std::string mId;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
CreateCommand (IdTableProxyModel& model, const std::string& id, QUndoCommand *parent = 0);
|
||||||
|
|
||||||
|
virtual void redo();
|
||||||
|
|
||||||
|
virtual void undo();
|
||||||
|
};
|
||||||
|
|
||||||
|
class RevertCommand : public QUndoCommand
|
||||||
|
{
|
||||||
|
IdTable& mModel;
|
||||||
|
std::string mId;
|
||||||
|
RecordBase *mOld;
|
||||||
|
|
||||||
|
// not implemented
|
||||||
|
RevertCommand (const RevertCommand&);
|
||||||
|
RevertCommand& operator= (const RevertCommand&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
RevertCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0);
|
||||||
|
|
||||||
|
virtual ~RevertCommand();
|
||||||
|
|
||||||
|
virtual void redo();
|
||||||
|
|
||||||
|
virtual void undo();
|
||||||
|
};
|
||||||
|
|
||||||
|
class DeleteCommand : public QUndoCommand
|
||||||
|
{
|
||||||
|
IdTable& mModel;
|
||||||
|
std::string mId;
|
||||||
|
RecordBase *mOld;
|
||||||
|
|
||||||
|
// not implemented
|
||||||
|
DeleteCommand (const DeleteCommand&);
|
||||||
|
DeleteCommand& operator= (const DeleteCommand&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DeleteCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0);
|
||||||
|
|
||||||
|
virtual ~DeleteCommand();
|
||||||
|
|
||||||
|
virtual void redo();
|
||||||
|
|
||||||
|
virtual void undo();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
113
apps/opencs/model/world/data.cpp
Normal file
113
apps/opencs/model/world/data.cpp
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
|
||||||
|
#include "data.hpp"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <QAbstractTableModel>
|
||||||
|
|
||||||
|
#include <components/esm/esmreader.hpp>
|
||||||
|
#include <components/esm/defs.hpp>
|
||||||
|
#include <components/esm/loadglob.hpp>
|
||||||
|
|
||||||
|
#include "idtable.hpp"
|
||||||
|
#include "columns.hpp"
|
||||||
|
|
||||||
|
void CSMWorld::Data::addModel (QAbstractTableModel *model, UniversalId::Type type1,
|
||||||
|
UniversalId::Type type2)
|
||||||
|
{
|
||||||
|
mModels.push_back (model);
|
||||||
|
mModelIndex.insert (std::make_pair (type1, model));
|
||||||
|
|
||||||
|
if (type2!=UniversalId::Type_None)
|
||||||
|
mModelIndex.insert (std::make_pair (type2, model));
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::Data::Data()
|
||||||
|
{
|
||||||
|
mGlobals.addColumn (new StringIdColumn<ESM::Global>);
|
||||||
|
mGlobals.addColumn (new RecordStateColumn<ESM::Global>);
|
||||||
|
mGlobals.addColumn (new FixedRecordTypeColumn<ESM::Global> (UniversalId::Type_Global));
|
||||||
|
mGlobals.addColumn (new VarTypeColumn<ESM::Global> (ColumnBase::Display_GlobalVarType));
|
||||||
|
mGlobals.addColumn (new VarValueColumn<ESM::Global>);
|
||||||
|
|
||||||
|
mGmsts.addColumn (new StringIdColumn<ESM::GameSetting>);
|
||||||
|
mGmsts.addColumn (new RecordStateColumn<ESM::GameSetting>);
|
||||||
|
mGmsts.addColumn (new FixedRecordTypeColumn<ESM::GameSetting> (UniversalId::Type_Gmst));
|
||||||
|
mGmsts.addColumn (new VarTypeColumn<ESM::GameSetting> (ColumnBase::Display_GmstVarType));
|
||||||
|
mGmsts.addColumn (new VarValueColumn<ESM::GameSetting>);
|
||||||
|
|
||||||
|
addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global);
|
||||||
|
addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst);
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::Data::~Data()
|
||||||
|
{
|
||||||
|
for (std::vector<QAbstractTableModel *>::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter)
|
||||||
|
delete *iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CSMWorld::IdCollection<ESM::Global>& CSMWorld::Data::getGlobals() const
|
||||||
|
{
|
||||||
|
return mGlobals;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::IdCollection<ESM::Global>& CSMWorld::Data::getGlobals()
|
||||||
|
{
|
||||||
|
return mGlobals;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CSMWorld::IdCollection<ESM::GameSetting>& CSMWorld::Data::getGmsts() const
|
||||||
|
{
|
||||||
|
return mGmsts;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::IdCollection<ESM::GameSetting>& CSMWorld::Data::getGmsts()
|
||||||
|
{
|
||||||
|
return mGmsts;
|
||||||
|
}
|
||||||
|
|
||||||
|
QAbstractTableModel *CSMWorld::Data::getTableModel (const UniversalId& id)
|
||||||
|
{
|
||||||
|
std::map<UniversalId::Type, QAbstractTableModel *>::iterator iter = mModelIndex.find (id.getType());
|
||||||
|
|
||||||
|
if (iter==mModelIndex.end())
|
||||||
|
throw std::logic_error ("No table model available for " + id.toString());
|
||||||
|
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::Data::merge()
|
||||||
|
{
|
||||||
|
mGlobals.merge();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
|
||||||
|
{
|
||||||
|
ESM::ESMReader reader;
|
||||||
|
|
||||||
|
/// \todo set encoding properly, once config implementation has been fixed.
|
||||||
|
ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding ("win1252"));
|
||||||
|
reader.setEncoder (&encoder);
|
||||||
|
|
||||||
|
reader.open (path.string());
|
||||||
|
|
||||||
|
// 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())
|
||||||
|
{
|
||||||
|
ESM::NAME n = reader.getRecName();
|
||||||
|
reader.getRecHeader();
|
||||||
|
|
||||||
|
switch (n.val)
|
||||||
|
{
|
||||||
|
case ESM::REC_GLOB: mGlobals.load (reader, base); break;
|
||||||
|
case ESM::REC_GMST: mGmsts.load (reader, base); break;
|
||||||
|
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
/// \todo throw an exception instead, once all records are implemented
|
||||||
|
reader.skipRecord();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
apps/opencs/model/world/data.hpp
Normal file
61
apps/opencs/model/world/data.hpp
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#ifndef CSM_WOLRD_DATA_H
|
||||||
|
#define CSM_WOLRD_DATA_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
|
#include <components/esm/loadglob.hpp>
|
||||||
|
#include <components/esm/loadgmst.hpp>
|
||||||
|
|
||||||
|
#include "idcollection.hpp"
|
||||||
|
#include "universalid.hpp"
|
||||||
|
|
||||||
|
class QAbstractTableModel;
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
class Data
|
||||||
|
{
|
||||||
|
IdCollection<ESM::Global> mGlobals;
|
||||||
|
IdCollection<ESM::GameSetting> mGmsts;
|
||||||
|
std::vector<QAbstractTableModel *> mModels;
|
||||||
|
std::map<UniversalId::Type, QAbstractTableModel *> mModelIndex;
|
||||||
|
|
||||||
|
// not implemented
|
||||||
|
Data (const Data&);
|
||||||
|
Data& operator= (const Data&);
|
||||||
|
|
||||||
|
void addModel (QAbstractTableModel *model, UniversalId::Type type1,
|
||||||
|
UniversalId::Type type2 = UniversalId::Type_None);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Data();
|
||||||
|
|
||||||
|
~Data();
|
||||||
|
|
||||||
|
const IdCollection<ESM::Global>& getGlobals() const;
|
||||||
|
|
||||||
|
IdCollection<ESM::Global>& getGlobals();
|
||||||
|
|
||||||
|
const IdCollection<ESM::GameSetting>& getGmsts() const;
|
||||||
|
|
||||||
|
IdCollection<ESM::GameSetting>& getGmsts();
|
||||||
|
|
||||||
|
QAbstractTableModel *getTableModel (const UniversalId& id);
|
||||||
|
///< If no table model is available for \a id, an exception is thrown.
|
||||||
|
///
|
||||||
|
/// \note The returned table may either be the model for the ID itself or the model that
|
||||||
|
/// contains the record specified by the ID.
|
||||||
|
|
||||||
|
void merge();
|
||||||
|
///< Merge modified into base.
|
||||||
|
|
||||||
|
void loadFile (const boost::filesystem::path& path, bool base);
|
||||||
|
///< Merging content of a file into base or modified.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
6
apps/opencs/model/world/idcollection.cpp
Normal file
6
apps/opencs/model/world/idcollection.cpp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
#include "idcollection.hpp"
|
||||||
|
|
||||||
|
CSMWorld::IdCollectionBase::IdCollectionBase() {}
|
||||||
|
|
||||||
|
CSMWorld::IdCollectionBase::~IdCollectionBase() {}
|
383
apps/opencs/model/world/idcollection.hpp
Normal file
383
apps/opencs/model/world/idcollection.hpp
Normal file
|
@ -0,0 +1,383 @@
|
||||||
|
#ifndef CSM_WOLRD_IDCOLLECTION_H
|
||||||
|
#define CSM_WOLRD_IDCOLLECTION_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include <components/esm/esmreader.hpp>
|
||||||
|
|
||||||
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
|
#include "columnbase.hpp"
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
class IdCollectionBase
|
||||||
|
{
|
||||||
|
// not implemented
|
||||||
|
IdCollectionBase (const IdCollectionBase&);
|
||||||
|
IdCollectionBase& operator= (const IdCollectionBase&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
IdCollectionBase();
|
||||||
|
|
||||||
|
virtual ~IdCollectionBase();
|
||||||
|
|
||||||
|
virtual int getSize() const = 0;
|
||||||
|
|
||||||
|
virtual std::string getId (int index) const = 0;
|
||||||
|
|
||||||
|
virtual int getIndex (const std::string& id) const = 0;
|
||||||
|
|
||||||
|
virtual int getColumns() const = 0;
|
||||||
|
|
||||||
|
virtual const ColumnBase& getColumn (int column) const = 0;
|
||||||
|
|
||||||
|
virtual QVariant getData (int index, int column) const = 0;
|
||||||
|
|
||||||
|
virtual void setData (int index, int column, const QVariant& data) = 0;
|
||||||
|
|
||||||
|
virtual void merge() = 0;
|
||||||
|
///< Merge modified into base.
|
||||||
|
|
||||||
|
virtual void purge() = 0;
|
||||||
|
///< Remove records that are flagged as erased.
|
||||||
|
|
||||||
|
virtual void removeRows (int index, int count) = 0;
|
||||||
|
|
||||||
|
virtual void appendBlankRecord (const std::string& id) = 0;
|
||||||
|
|
||||||
|
virtual int searchId (const std::string& id) const = 0;
|
||||||
|
////< Search record with \a id.
|
||||||
|
/// \return index of record (if found) or -1 (not found)
|
||||||
|
|
||||||
|
virtual void replace (int index, const RecordBase& record) = 0;
|
||||||
|
///< If the record type does not match, an exception is thrown.
|
||||||
|
///
|
||||||
|
/// \attention \a record must not change the ID.
|
||||||
|
|
||||||
|
virtual void appendRecord (const RecordBase& record) = 0;
|
||||||
|
///< If the record type does not match, an exception is thrown.
|
||||||
|
|
||||||
|
virtual std::string getId (const RecordBase& record) const = 0;
|
||||||
|
///< Return ID for \a record.
|
||||||
|
///
|
||||||
|
/// \attention Throws an exception, if the type of \a record does not match.
|
||||||
|
|
||||||
|
virtual const RecordBase& getRecord (const std::string& id) const = 0;
|
||||||
|
|
||||||
|
virtual void load (ESM::ESMReader& reader, bool base) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
///< \brief Collection of ID-based records
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
class IdCollection : public IdCollectionBase
|
||||||
|
{
|
||||||
|
std::vector<Record<ESXRecordT> > mRecords;
|
||||||
|
std::map<std::string, int> mIndex;
|
||||||
|
std::vector<Column<ESXRecordT> *> mColumns;
|
||||||
|
|
||||||
|
// not implemented
|
||||||
|
IdCollection (const IdCollection&);
|
||||||
|
IdCollection& operator= (const IdCollection&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
IdCollection();
|
||||||
|
|
||||||
|
virtual ~IdCollection();
|
||||||
|
|
||||||
|
void add (const ESXRecordT& record);
|
||||||
|
///< Add a new record (modified)
|
||||||
|
|
||||||
|
virtual int getSize() const;
|
||||||
|
|
||||||
|
virtual std::string getId (int index) const;
|
||||||
|
|
||||||
|
virtual int getIndex (const std::string& id) const;
|
||||||
|
|
||||||
|
virtual int getColumns() const;
|
||||||
|
|
||||||
|
virtual QVariant getData (int index, int column) const;
|
||||||
|
|
||||||
|
virtual void setData (int index, int column, const QVariant& data);
|
||||||
|
|
||||||
|
virtual const ColumnBase& getColumn (int column) const;
|
||||||
|
|
||||||
|
virtual void merge();
|
||||||
|
///< Merge modified into base.
|
||||||
|
|
||||||
|
virtual void purge();
|
||||||
|
///< Remove records that are flagged as erased.
|
||||||
|
|
||||||
|
virtual void removeRows (int index, int count) ;
|
||||||
|
|
||||||
|
virtual void appendBlankRecord (const std::string& id);
|
||||||
|
|
||||||
|
virtual int searchId (const std::string& id) const;
|
||||||
|
////< Search record with \a id.
|
||||||
|
/// \return index of record (if found) or -1 (not found)
|
||||||
|
|
||||||
|
virtual void replace (int index, const RecordBase& record);
|
||||||
|
///< If the record type does not match, an exception is thrown.
|
||||||
|
///
|
||||||
|
/// \attention \a record must not change the ID.
|
||||||
|
|
||||||
|
virtual void appendRecord (const RecordBase& record);
|
||||||
|
///< If the record type does not match, an exception is thrown.
|
||||||
|
|
||||||
|
virtual std::string getId (const RecordBase& record) const;
|
||||||
|
///< Return ID for \a record.
|
||||||
|
///
|
||||||
|
/// \attention Throw san exception, if the type of \a record does not match.
|
||||||
|
|
||||||
|
virtual const RecordBase& getRecord (const std::string& id) const;
|
||||||
|
|
||||||
|
virtual void load (ESM::ESMReader& reader, bool base);
|
||||||
|
|
||||||
|
void addColumn (Column<ESXRecordT> *column);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
IdCollection<ESXRecordT>::IdCollection()
|
||||||
|
{}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
IdCollection<ESXRecordT>::~IdCollection()
|
||||||
|
{
|
||||||
|
for (typename std::vector<Column<ESXRecordT> *>::iterator iter (mColumns.begin()); iter!=mColumns.end(); ++iter)
|
||||||
|
delete *iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
void IdCollection<ESXRecordT>::add (const ESXRecordT& record)
|
||||||
|
{
|
||||||
|
std::string id = Misc::StringUtils::lowerCase(record.mId);
|
||||||
|
|
||||||
|
std::map<std::string, int>::iterator iter = mIndex.find (id);
|
||||||
|
|
||||||
|
if (iter==mIndex.end())
|
||||||
|
{
|
||||||
|
Record<ESXRecordT> record2;
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mRecords[iter->second].setModified (record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
int IdCollection<ESXRecordT>::getSize() const
|
||||||
|
{
|
||||||
|
return mRecords.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
std::string IdCollection<ESXRecordT>::getId (int index) const
|
||||||
|
{
|
||||||
|
return mRecords.at (index).get().mId;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
int IdCollection<ESXRecordT>::getIndex (const std::string& id) const
|
||||||
|
{
|
||||||
|
int index = searchId (id);
|
||||||
|
|
||||||
|
if (index==-1)
|
||||||
|
throw std::runtime_error ("invalid ID: " + id);
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
int IdCollection<ESXRecordT>::getColumns() const
|
||||||
|
{
|
||||||
|
return mColumns.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
QVariant IdCollection<ESXRecordT>::getData (int index, int column) const
|
||||||
|
{
|
||||||
|
return mColumns.at (column)->get (mRecords.at (index));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
void IdCollection<ESXRecordT>::setData (int index, int column, const QVariant& data)
|
||||||
|
{
|
||||||
|
return mColumns.at (column)->set (mRecords.at (index), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
const ColumnBase& IdCollection<ESXRecordT>::getColumn (int column) const
|
||||||
|
{
|
||||||
|
return *mColumns.at (column);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
void IdCollection<ESXRecordT>::addColumn (Column<ESXRecordT> *column)
|
||||||
|
{
|
||||||
|
mColumns.push_back (column);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
void IdCollection<ESXRecordT>::merge()
|
||||||
|
{
|
||||||
|
for (typename std::vector<Record<ESXRecordT> >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter)
|
||||||
|
iter->merge();
|
||||||
|
|
||||||
|
purge();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
void IdCollection<ESXRecordT>::purge()
|
||||||
|
{
|
||||||
|
mRecords.erase (std::remove_if (mRecords.begin(), mRecords.end(),
|
||||||
|
std::mem_fun_ref (&Record<ESXRecordT>::isErased) // I want lambda :(
|
||||||
|
), mRecords.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
void IdCollection<ESXRecordT>::removeRows (int index, int count)
|
||||||
|
{
|
||||||
|
mRecords.erase (mRecords.begin()+index, mRecords.begin()+index+count);
|
||||||
|
|
||||||
|
typename std::map<std::string, int>::iterator iter = mIndex.begin();
|
||||||
|
|
||||||
|
while (iter!=mIndex.end())
|
||||||
|
{
|
||||||
|
if (iter->second>=index)
|
||||||
|
{
|
||||||
|
if (iter->second>=index+count)
|
||||||
|
{
|
||||||
|
iter->second -= count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mIndex.erase (iter++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
void IdCollection<ESXRecordT>::appendBlankRecord (const std::string& id)
|
||||||
|
{
|
||||||
|
ESXRecordT record;
|
||||||
|
record.mId = id;
|
||||||
|
record.blank();
|
||||||
|
add (record);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
int IdCollection<ESXRecordT>::searchId (const std::string& id) const
|
||||||
|
{
|
||||||
|
std::string id2 = Misc::StringUtils::lowerCase(id);
|
||||||
|
|
||||||
|
std::map<std::string, int>::const_iterator iter = mIndex.find (id2);
|
||||||
|
|
||||||
|
if (iter==mIndex.end())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
void IdCollection<ESXRecordT>::replace (int index, const RecordBase& record)
|
||||||
|
{
|
||||||
|
mRecords.at (index) = dynamic_cast<const Record<ESXRecordT>&> (record);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
void IdCollection<ESXRecordT>::appendRecord (const RecordBase& record)
|
||||||
|
{
|
||||||
|
mRecords.push_back (dynamic_cast<const Record<ESXRecordT>&> (record));
|
||||||
|
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (getId (record)), mRecords.size()-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
std::string IdCollection<ESXRecordT>::getId (const RecordBase& record) const
|
||||||
|
{
|
||||||
|
const Record<ESXRecordT>& record2 = dynamic_cast<const Record<ESXRecordT>&> (record);
|
||||||
|
return (record2.isModified() ? record2.mModified : record2.mBase).mId;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
void IdCollection<ESXRecordT>::load (ESM::ESMReader& reader, bool base)
|
||||||
|
{
|
||||||
|
std::string id = reader.getHNOString ("NAME");
|
||||||
|
|
||||||
|
int index = searchId (id);
|
||||||
|
|
||||||
|
if (reader.isNextSub ("DELE"))
|
||||||
|
{
|
||||||
|
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
|
||||||
|
{
|
||||||
|
mRecords[index].mState = RecordBase::State_Deleted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESXRecordT record;
|
||||||
|
record.mId = id;
|
||||||
|
record.load (reader);
|
||||||
|
|
||||||
|
if (index==-1)
|
||||||
|
{
|
||||||
|
// new record
|
||||||
|
Record<ESXRecordT> record2;
|
||||||
|
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
||||||
|
(base ? record2.mBase : record2.mModified) = record;
|
||||||
|
|
||||||
|
appendRecord (record2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// old record
|
||||||
|
Record<ESXRecordT>& record2 = mRecords[index];
|
||||||
|
|
||||||
|
if (base)
|
||||||
|
record2.mBase = record;
|
||||||
|
else
|
||||||
|
record2.setModified (record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
const RecordBase& IdCollection<ESXRecordT>::getRecord (const std::string& id) const
|
||||||
|
{
|
||||||
|
int index = getIndex (id);
|
||||||
|
return mRecords.at (index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
140
apps/opencs/model/world/idtable.cpp
Normal file
140
apps/opencs/model/world/idtable.cpp
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
|
||||||
|
#include "idtable.hpp"
|
||||||
|
|
||||||
|
#include "idcollection.hpp"
|
||||||
|
|
||||||
|
CSMWorld::IdTable::IdTable (IdCollectionBase *idCollection) : mIdCollection (idCollection)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::IdTable::~IdTable()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMWorld::IdTable::rowCount (const QModelIndex & parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return mIdCollection->getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMWorld::IdTable::columnCount (const QModelIndex & parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return mIdCollection->getColumns();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const
|
||||||
|
{
|
||||||
|
if (role!=Qt::DisplayRole && role!=Qt::EditRole)
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (role==Qt::EditRole && !mIdCollection->getColumn (index.column()).isEditable())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
return mIdCollection->getData (index.row(), index.column());
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation, int role) const
|
||||||
|
{
|
||||||
|
if (orientation==Qt::Vertical)
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (role==Qt::DisplayRole)
|
||||||
|
return tr (mIdCollection->getColumn (section).mTitle.c_str());
|
||||||
|
|
||||||
|
if (role==ColumnBase::Role_Flags)
|
||||||
|
return mIdCollection->getColumn (section).mFlags;
|
||||||
|
|
||||||
|
if (role==ColumnBase::Role_Display)
|
||||||
|
return mIdCollection->getColumn (section).mDisplayType;
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMWorld::IdTable::setData ( const QModelIndex &index, const QVariant &value, int role)
|
||||||
|
{
|
||||||
|
if (mIdCollection->getColumn (index.column()).isEditable() && role==Qt::EditRole)
|
||||||
|
{
|
||||||
|
mIdCollection->setData (index.row(), index.column(), value);
|
||||||
|
|
||||||
|
emit dataChanged (CSMWorld::IdTable::index (index.row(), 0),
|
||||||
|
CSMWorld::IdTable::index (index.row(), mIdCollection->getColumns()-1));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags CSMWorld::IdTable::flags (const QModelIndex & index) const
|
||||||
|
{
|
||||||
|
Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
||||||
|
|
||||||
|
if (mIdCollection->getColumn (index.column()).isUserEditable())
|
||||||
|
flags |= Qt::ItemIsEditable;
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMWorld::IdTable::removeRows (int row, int count, const QModelIndex& parent)
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
beginRemoveRows (parent, row, row+count-1);
|
||||||
|
|
||||||
|
mIdCollection->removeRows (row, count);
|
||||||
|
|
||||||
|
endRemoveRows();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::IdTable::addRecord (const std::string& id)
|
||||||
|
{
|
||||||
|
int index = mIdCollection->getSize();
|
||||||
|
|
||||||
|
beginInsertRows (QModelIndex(), index, index);
|
||||||
|
|
||||||
|
mIdCollection->appendBlankRecord (id);
|
||||||
|
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const
|
||||||
|
{
|
||||||
|
return index (mIdCollection->getIndex (id), column);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::IdTable::setRecord (const RecordBase& record)
|
||||||
|
{
|
||||||
|
int index = mIdCollection->searchId (mIdCollection->getId (record));
|
||||||
|
|
||||||
|
if (index==-1)
|
||||||
|
{
|
||||||
|
int index = mIdCollection->getSize();
|
||||||
|
|
||||||
|
beginInsertRows (QModelIndex(), index, index);
|
||||||
|
|
||||||
|
mIdCollection->appendRecord (record);
|
||||||
|
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mIdCollection->replace (index, record);
|
||||||
|
emit dataChanged (CSMWorld::IdTable::index (index, 0),
|
||||||
|
CSMWorld::IdTable::index (index, mIdCollection->getColumns()-1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CSMWorld::RecordBase& CSMWorld::IdTable::getRecord (const std::string& id) const
|
||||||
|
{
|
||||||
|
return mIdCollection->getRecord (id);
|
||||||
|
}
|
53
apps/opencs/model/world/idtable.hpp
Normal file
53
apps/opencs/model/world/idtable.hpp
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#ifndef CSM_WOLRD_IDTABLE_H
|
||||||
|
#define CSM_WOLRD_IDTABLE_H
|
||||||
|
|
||||||
|
#include <QAbstractTableModel>
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
class IdCollectionBase;
|
||||||
|
class RecordBase;
|
||||||
|
|
||||||
|
class IdTable : public QAbstractTableModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
IdCollectionBase *mIdCollection;
|
||||||
|
|
||||||
|
// not implemented
|
||||||
|
IdTable (const IdTable&);
|
||||||
|
IdTable& operator= (const IdTable&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
IdTable (IdCollectionBase *idCollection);
|
||||||
|
///< The ownership of \a idCollection is not transferred.
|
||||||
|
|
||||||
|
virtual ~IdTable();
|
||||||
|
|
||||||
|
virtual int rowCount (const QModelIndex & parent = QModelIndex()) const;
|
||||||
|
|
||||||
|
virtual int columnCount (const QModelIndex & parent = QModelIndex()) const;
|
||||||
|
|
||||||
|
virtual QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const;
|
||||||
|
|
||||||
|
virtual QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||||
|
|
||||||
|
virtual bool setData ( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
|
||||||
|
|
||||||
|
virtual Qt::ItemFlags flags (const QModelIndex & index) const;
|
||||||
|
|
||||||
|
virtual bool removeRows (int row, int count, const QModelIndex& parent = QModelIndex());
|
||||||
|
|
||||||
|
void addRecord (const std::string& id);
|
||||||
|
|
||||||
|
QModelIndex getModelIndex (const std::string& id, int column) const;
|
||||||
|
|
||||||
|
void setRecord (const RecordBase& record);
|
||||||
|
///< Add record or overwrite existing recrod.
|
||||||
|
|
||||||
|
const RecordBase& getRecord (const std::string& id) const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
18
apps/opencs/model/world/idtableproxymodel.cpp
Normal file
18
apps/opencs/model/world/idtableproxymodel.cpp
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
|
||||||
|
#include "idtableproxymodel.hpp"
|
||||||
|
|
||||||
|
#include "idtable.hpp"
|
||||||
|
|
||||||
|
CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent)
|
||||||
|
: QSortFilterProxyModel (parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void CSMWorld::IdTableProxyModel::addRecord (const std::string& id)
|
||||||
|
{
|
||||||
|
dynamic_cast<IdTable&> (*sourceModel()).addRecord (id);
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const
|
||||||
|
{
|
||||||
|
return mapFromSource (dynamic_cast<IdTable&> (*sourceModel()).getModelIndex (id, column));
|
||||||
|
}
|
24
apps/opencs/model/world/idtableproxymodel.hpp
Normal file
24
apps/opencs/model/world/idtableproxymodel.hpp
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef CSM_WOLRD_IDTABLEPROXYMODEL_H
|
||||||
|
#define CSM_WOLRD_IDTABLEPROXYMODEL_H
|
||||||
|
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
class IdTableProxyModel : public QSortFilterProxyModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
IdTableProxyModel (QObject *parent = 0);
|
||||||
|
|
||||||
|
virtual void addRecord (const std::string& id);
|
||||||
|
|
||||||
|
virtual QModelIndex getModelIndex (const std::string& id, int column) const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
21
apps/opencs/model/world/record.cpp
Normal file
21
apps/opencs/model/world/record.cpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
#include "record.hpp"
|
||||||
|
|
||||||
|
CSMWorld::RecordBase::~RecordBase() {}
|
||||||
|
|
||||||
|
bool CSMWorld::RecordBase::isDeleted() const
|
||||||
|
{
|
||||||
|
return mState==State_Deleted || mState==State_Erased;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool CSMWorld::RecordBase::isErased() const
|
||||||
|
{
|
||||||
|
return mState==State_Erased;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool CSMWorld::RecordBase::isModified() const
|
||||||
|
{
|
||||||
|
return mState==State_Modified || mState==State_ModifiedOnly;
|
||||||
|
}
|
104
apps/opencs/model/world/record.hpp
Normal file
104
apps/opencs/model/world/record.hpp
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
#ifndef CSM_WOLRD_RECORD_H
|
||||||
|
#define CSM_WOLRD_RECORD_H
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
struct RecordBase
|
||||||
|
{
|
||||||
|
enum State
|
||||||
|
{
|
||||||
|
State_BaseOnly = 0, // defined in base only
|
||||||
|
State_Modified = 1, // exists in base, but has been modified
|
||||||
|
State_ModifiedOnly = 2, // newly created in modified
|
||||||
|
State_Deleted = 3, // exists in base, but has been deleted
|
||||||
|
State_Erased = 4 // does not exist at all (we mostly treat that the same way as deleted)
|
||||||
|
};
|
||||||
|
|
||||||
|
State mState;
|
||||||
|
|
||||||
|
virtual ~RecordBase();
|
||||||
|
|
||||||
|
virtual RecordBase *clone() const = 0;
|
||||||
|
|
||||||
|
bool isDeleted() const;
|
||||||
|
|
||||||
|
bool isErased() const;
|
||||||
|
|
||||||
|
bool isModified() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ESXRecordT>
|
||||||
|
struct Record : public RecordBase
|
||||||
|
{
|
||||||
|
ESXRecordT mBase;
|
||||||
|
ESXRecordT mModified;
|
||||||
|
|
||||||
|
virtual RecordBase *clone() const;
|
||||||
|
|
||||||
|
const ESXRecordT& get() const;
|
||||||
|
///< Throws an exception, if the record is deleted.
|
||||||
|
|
||||||
|
const ESXRecordT& getBase() const;
|
||||||
|
///< Throws an exception, if the record is deleted. Returns modified, if there is no base.
|
||||||
|
|
||||||
|
void setModified (const ESXRecordT& modified);
|
||||||
|
///< Throws an exception, if the record is deleted.
|
||||||
|
|
||||||
|
void merge();
|
||||||
|
///< Merge modified into base.
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ESXRecordT>
|
||||||
|
RecordBase *Record<ESXRecordT>::clone() const
|
||||||
|
{
|
||||||
|
return new Record<ESXRecordT> (*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ESXRecordT>
|
||||||
|
const ESXRecordT& Record<ESXRecordT>::get() const
|
||||||
|
{
|
||||||
|
if (mState==State_Erased)
|
||||||
|
throw std::logic_error ("attempt to access a deleted record");
|
||||||
|
|
||||||
|
return mState==State_BaseOnly ? mBase : mModified;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ESXRecordT>
|
||||||
|
const ESXRecordT& Record<ESXRecordT>::getBase() const
|
||||||
|
{
|
||||||
|
if (mState==State_Erased)
|
||||||
|
throw std::logic_error ("attempt to access a deleted record");
|
||||||
|
|
||||||
|
return mState==State_ModifiedOnly ? mModified : mBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ESXRecordT>
|
||||||
|
void Record<ESXRecordT>::setModified (const ESXRecordT& modified)
|
||||||
|
{
|
||||||
|
if (mState==State_Erased)
|
||||||
|
throw std::logic_error ("attempt to modify a deleted record");
|
||||||
|
|
||||||
|
mModified = modified;
|
||||||
|
|
||||||
|
if (mState!=State_ModifiedOnly)
|
||||||
|
mState = mBase==mModified ? State_BaseOnly : State_Modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ESXRecordT>
|
||||||
|
void Record<ESXRecordT>::merge()
|
||||||
|
{
|
||||||
|
if (isModified())
|
||||||
|
{
|
||||||
|
mBase = mModified;
|
||||||
|
mState = State_BaseOnly;
|
||||||
|
}
|
||||||
|
else if (mState==State_Deleted)
|
||||||
|
{
|
||||||
|
mState = State_Erased;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
239
apps/opencs/model/world/universalid.cpp
Normal file
239
apps/opencs/model/world/universalid.cpp
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
|
||||||
|
#include "universalid.hpp"
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct TypeData
|
||||||
|
{
|
||||||
|
CSMWorld::UniversalId::Class mClass;
|
||||||
|
CSMWorld::UniversalId::Type mType;
|
||||||
|
const char *mName;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const TypeData sNoArg[] =
|
||||||
|
{
|
||||||
|
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "empty" },
|
||||||
|
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables" },
|
||||||
|
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings" },
|
||||||
|
|
||||||
|
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker
|
||||||
|
};
|
||||||
|
|
||||||
|
static const TypeData sIdArg[] =
|
||||||
|
{
|
||||||
|
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable" },
|
||||||
|
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting" },
|
||||||
|
|
||||||
|
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker
|
||||||
|
};
|
||||||
|
|
||||||
|
static const TypeData sIndexArg[] =
|
||||||
|
{
|
||||||
|
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results" },
|
||||||
|
|
||||||
|
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::UniversalId::UniversalId (const std::string& universalId)
|
||||||
|
{
|
||||||
|
std::string::size_type index = universalId.find (':');
|
||||||
|
|
||||||
|
if (index==std::string::npos)
|
||||||
|
{
|
||||||
|
std::string type = universalId.substr (0, index);
|
||||||
|
|
||||||
|
if (index==std::string::npos)
|
||||||
|
{
|
||||||
|
for (int i=0; sNoArg[i].mName; ++i)
|
||||||
|
if (type==sNoArg[i].mName)
|
||||||
|
{
|
||||||
|
mArgumentType = ArgumentType_None;
|
||||||
|
mType = sNoArg[i].mType;
|
||||||
|
mClass = sNoArg[i].mClass;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i=0; sIdArg[i].mName; ++i)
|
||||||
|
if (type==sIdArg[i].mName)
|
||||||
|
{
|
||||||
|
mArgumentType = ArgumentType_Id;
|
||||||
|
mType = sIdArg[i].mType;
|
||||||
|
mClass = sIdArg[i].mClass;
|
||||||
|
mId = universalId.substr (0, index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; sIndexArg[i].mName; ++i)
|
||||||
|
if (type==sIndexArg[i].mName)
|
||||||
|
{
|
||||||
|
mArgumentType = ArgumentType_Index;
|
||||||
|
mType = sIndexArg[i].mType;
|
||||||
|
mClass = sIndexArg[i].mClass;
|
||||||
|
|
||||||
|
std::istringstream stream (universalId.substr (0, index));
|
||||||
|
|
||||||
|
if (stream >> mIndex)
|
||||||
|
return;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error ("invalid UniversalId: " + universalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::UniversalId::UniversalId (Type type) : mArgumentType (ArgumentType_None), mType (type), mIndex (0)
|
||||||
|
{
|
||||||
|
for (int i=0; sNoArg[i].mName; ++i)
|
||||||
|
if (type==sNoArg[i].mType)
|
||||||
|
{
|
||||||
|
mClass = sNoArg[i].mClass;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::logic_error ("invalid argument-less UniversalId type");
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::UniversalId::UniversalId (Type type, const std::string& id)
|
||||||
|
: mArgumentType (ArgumentType_Id), mType (type), mId (id), mIndex (0)
|
||||||
|
{
|
||||||
|
for (int i=0; sIdArg[i].mName; ++i)
|
||||||
|
if (type==sIdArg[i].mType)
|
||||||
|
{
|
||||||
|
mClass = sIdArg[i].mClass;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::logic_error ("invalid ID argument UniversalId type");
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::UniversalId::UniversalId (Type type, int index)
|
||||||
|
: mArgumentType (ArgumentType_Index), mType (type), mIndex (index)
|
||||||
|
{
|
||||||
|
for (int i=0; sIndexArg[i].mName; ++i)
|
||||||
|
if (type==sIndexArg[i].mType)
|
||||||
|
{
|
||||||
|
mClass = sIndexArg[i].mClass;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::logic_error ("invalid index argument UniversalId type");
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::UniversalId::Class CSMWorld::UniversalId::getClass() const
|
||||||
|
{
|
||||||
|
return mClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::UniversalId::ArgumentType CSMWorld::UniversalId::getArgumentType() const
|
||||||
|
{
|
||||||
|
return mArgumentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::UniversalId::Type CSMWorld::UniversalId::getType() const
|
||||||
|
{
|
||||||
|
return mType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& CSMWorld::UniversalId::getId() const
|
||||||
|
{
|
||||||
|
if (mArgumentType!=ArgumentType_Id)
|
||||||
|
throw std::logic_error ("invalid access to ID of non-ID UniversalId");
|
||||||
|
|
||||||
|
return mId;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMWorld::UniversalId::getIndex() const
|
||||||
|
{
|
||||||
|
if (mArgumentType!=ArgumentType_Index)
|
||||||
|
throw std::logic_error ("invalid access to index of non-index UniversalId");
|
||||||
|
|
||||||
|
return mIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMWorld::UniversalId::isEqual (const UniversalId& universalId) const
|
||||||
|
{
|
||||||
|
if (mClass!=universalId.mClass || mArgumentType!=universalId.mArgumentType || mType!=universalId.mType)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (mArgumentType)
|
||||||
|
{
|
||||||
|
case ArgumentType_Id: return mId==universalId.mId;
|
||||||
|
case ArgumentType_Index: return mIndex==universalId.mIndex;
|
||||||
|
|
||||||
|
default: return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMWorld::UniversalId::isLess (const UniversalId& universalId) const
|
||||||
|
{
|
||||||
|
if (mType<universalId.mType)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (mType>universalId.mType)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (mArgumentType)
|
||||||
|
{
|
||||||
|
case ArgumentType_Id: return mId<universalId.mId;
|
||||||
|
case ArgumentType_Index: return mIndex<universalId.mIndex;
|
||||||
|
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CSMWorld::UniversalId::getTypeName() const
|
||||||
|
{
|
||||||
|
const TypeData *typeData = mArgumentType==ArgumentType_None ? sNoArg :
|
||||||
|
(mArgumentType==ArgumentType_Id ? sIdArg : sIndexArg);
|
||||||
|
|
||||||
|
for (int i=0; typeData[i].mName; ++i)
|
||||||
|
if (typeData[i].mType==mType)
|
||||||
|
return typeData[i].mName;
|
||||||
|
|
||||||
|
throw std::logic_error ("failed to retrieve UniversalId type name");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CSMWorld::UniversalId::toString() const
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
|
||||||
|
stream << getTypeName();
|
||||||
|
|
||||||
|
switch (mArgumentType)
|
||||||
|
{
|
||||||
|
case ArgumentType_None: break;
|
||||||
|
case ArgumentType_Id: stream << ": " << mId; break;
|
||||||
|
case ArgumentType_Index: stream << ": " << mIndex; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMWorld::operator== (const CSMWorld::UniversalId& left, const CSMWorld::UniversalId& right)
|
||||||
|
{
|
||||||
|
return left.isEqual (right);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMWorld::operator!= (const CSMWorld::UniversalId& left, const CSMWorld::UniversalId& right)
|
||||||
|
{
|
||||||
|
return !left.isEqual (right);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMWorld::operator< (const UniversalId& left, const UniversalId& right)
|
||||||
|
{
|
||||||
|
return left.isLess (right);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& CSMWorld::operator< (std::ostream& stream, const CSMWorld::UniversalId& universalId)
|
||||||
|
{
|
||||||
|
return stream << universalId.toString();
|
||||||
|
}
|
96
apps/opencs/model/world/universalid.hpp
Normal file
96
apps/opencs/model/world/universalid.hpp
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
#ifndef CSM_WOLRD_UNIVERSALID_H
|
||||||
|
#define CSM_WOLRD_UNIVERSALID_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <iosfwd>
|
||||||
|
|
||||||
|
#include <QMetaType>
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
class UniversalId
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum Class
|
||||||
|
{
|
||||||
|
Class_None = 0,
|
||||||
|
Class_Record,
|
||||||
|
Class_SubRecord,
|
||||||
|
Class_RecordList,
|
||||||
|
Class_Collection, // multiple types of records combined
|
||||||
|
Class_Transient, // not part of the world data or the project data
|
||||||
|
Class_NonRecord // record like data that is not part of the world
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ArgumentType
|
||||||
|
{
|
||||||
|
ArgumentType_None,
|
||||||
|
ArgumentType_Id,
|
||||||
|
ArgumentType_Index
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Type
|
||||||
|
{
|
||||||
|
Type_None,
|
||||||
|
Type_Globals,
|
||||||
|
Type_Global,
|
||||||
|
Type_VerificationResults,
|
||||||
|
Type_Gmsts,
|
||||||
|
Type_Gmst
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Class mClass;
|
||||||
|
ArgumentType mArgumentType;
|
||||||
|
Type mType;
|
||||||
|
std::string mId;
|
||||||
|
int mIndex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
UniversalId (const std::string& universalId);
|
||||||
|
|
||||||
|
UniversalId (Type type = Type_None);
|
||||||
|
///< Using a type for a non-argument-less UniversalId will throw an exception.
|
||||||
|
|
||||||
|
UniversalId (Type type, const std::string& id);
|
||||||
|
///< Using a type for a non-ID-argument UniversalId will throw an exception.
|
||||||
|
|
||||||
|
UniversalId (Type type, int index);
|
||||||
|
///< Using a type for a non-index-argument UniversalId will throw an exception.
|
||||||
|
|
||||||
|
Class getClass() const;
|
||||||
|
|
||||||
|
ArgumentType getArgumentType() const;
|
||||||
|
|
||||||
|
Type getType() const;
|
||||||
|
|
||||||
|
const std::string& getId() const;
|
||||||
|
///< Calling this function for a non-ID type will throw an exception.
|
||||||
|
|
||||||
|
int getIndex() const;
|
||||||
|
///< Calling this function for a non-index type will throw an exception.
|
||||||
|
|
||||||
|
bool isEqual (const UniversalId& universalId) const;
|
||||||
|
|
||||||
|
bool isLess (const UniversalId& universalId) const;
|
||||||
|
|
||||||
|
std::string getTypeName() const;
|
||||||
|
|
||||||
|
std::string toString() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool operator== (const UniversalId& left, const UniversalId& right);
|
||||||
|
bool operator!= (const UniversalId& left, const UniversalId& right);
|
||||||
|
|
||||||
|
bool operator< (const UniversalId& left, const UniversalId& right);
|
||||||
|
|
||||||
|
std::ostream& operator< (std::ostream& stream, const UniversalId& universalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE (CSMWorld::UniversalId)
|
||||||
|
|
||||||
|
#endif
|
272
apps/opencs/view/doc/filedialog.cpp
Normal file
272
apps/opencs/view/doc/filedialog.cpp
Normal file
|
@ -0,0 +1,272 @@
|
||||||
|
#include "filedialog.hpp"
|
||||||
|
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
#include <QRegExpValidator>
|
||||||
|
#include <QRegExp>
|
||||||
|
#include <QSpacerItem>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QLabel>
|
||||||
|
|
||||||
|
#include <components/fileorderlist/model/datafilesmodel.hpp>
|
||||||
|
#include <components/fileorderlist/model/pluginsproxymodel.hpp>
|
||||||
|
#include <components/fileorderlist/model/esm/esmfile.hpp>
|
||||||
|
|
||||||
|
#include <components/fileorderlist/utils/lineedit.hpp>
|
||||||
|
|
||||||
|
FileDialog::FileDialog(QWidget *parent) :
|
||||||
|
QDialog(parent)
|
||||||
|
{
|
||||||
|
setupUi(this);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// 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->verticalHeader()->setDefaultSectionSize(height);
|
||||||
|
pluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed);
|
||||||
|
|
||||||
|
// Hide the profile elements
|
||||||
|
profileLabel->hide();
|
||||||
|
profilesComboBox->hide();
|
||||||
|
newProfileButton->hide();
|
||||||
|
deleteProfileButton->hide();
|
||||||
|
|
||||||
|
// Add some extra widgets
|
||||||
|
QHBoxLayout *nameLayout = new QHBoxLayout();
|
||||||
|
QSpacerItem *spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||||
|
|
||||||
|
mNameLabel = new QLabel(tr("File Name:"), this);
|
||||||
|
|
||||||
|
QRegExpValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9\\s]*$"));
|
||||||
|
mNameLineEdit = new LineEdit(this);
|
||||||
|
mNameLineEdit->setValidator(validator);
|
||||||
|
|
||||||
|
nameLayout->addSpacerItem(spacer);
|
||||||
|
nameLayout->addWidget(mNameLabel);
|
||||||
|
nameLayout->addWidget(mNameLineEdit);
|
||||||
|
|
||||||
|
mButtonBox = new QDialogButtonBox(this);
|
||||||
|
|
||||||
|
mCreateButton = new QPushButton(tr("Create"), this);
|
||||||
|
mCreateButton->setEnabled(false);
|
||||||
|
|
||||||
|
verticalLayout->addLayout(nameLayout);
|
||||||
|
verticalLayout->addWidget(mButtonBox);
|
||||||
|
|
||||||
|
// Set sizes
|
||||||
|
QList<int> sizeList;
|
||||||
|
sizeList << 175;
|
||||||
|
sizeList << 200;
|
||||||
|
|
||||||
|
splitter->setSizes(sizeList);
|
||||||
|
|
||||||
|
resize(600, 400);
|
||||||
|
|
||||||
|
connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews()));
|
||||||
|
connect(mDataFilesModel, SIGNAL(checkedItemsChanged(QStringList)), this, SLOT(updateOpenButton(QStringList)));
|
||||||
|
connect(mNameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(updateCreateButton(QString)));
|
||||||
|
|
||||||
|
connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString)));
|
||||||
|
|
||||||
|
connect(pluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
|
||||||
|
connect(mastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
|
||||||
|
|
||||||
|
connect(mCreateButton, SIGNAL(clicked()), this, SLOT(createButtonClicked()));
|
||||||
|
|
||||||
|
connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||||
|
connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::updateViews()
|
||||||
|
{
|
||||||
|
// Ensure the columns are hidden because sort() re-enables them
|
||||||
|
mastersTable->setColumnHidden(1, 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);
|
||||||
|
mastersTable->resizeColumnsToContents();
|
||||||
|
|
||||||
|
pluginsTable->setColumnHidden(1, 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);
|
||||||
|
pluginsTable->resizeColumnsToContents();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::updateOpenButton(const QStringList &items)
|
||||||
|
{
|
||||||
|
QPushButton *openButton = mButtonBox->button(QDialogButtonBox::Open);
|
||||||
|
|
||||||
|
if (!openButton)
|
||||||
|
return;
|
||||||
|
|
||||||
|
openButton->setEnabled(!items.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::updateCreateButton(const QString &name)
|
||||||
|
{
|
||||||
|
if (!mCreateButton->isVisible())
|
||||||
|
return;
|
||||||
|
|
||||||
|
mCreateButton->setEnabled(!name.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::filterChanged(const QString &filter)
|
||||||
|
{
|
||||||
|
QRegExp filterRe(filter, Qt::CaseInsensitive, QRegExp::FixedString);
|
||||||
|
mFilterProxyModel->setFilterRegExp(filterRe);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::addFiles(const QString &path)
|
||||||
|
{
|
||||||
|
mDataFilesModel->addFiles(path);
|
||||||
|
mDataFilesModel->sort(3); // Sort by date accessed
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::setEncoding(const QString &encoding)
|
||||||
|
{
|
||||||
|
mDataFilesModel->setEncoding(encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::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;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList FileDialog::checkedItemsPaths()
|
||||||
|
{
|
||||||
|
return mDataFilesModel->checkedItemsPaths();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileDialog::fileName()
|
||||||
|
{
|
||||||
|
return mNameLineEdit->text();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::openFile()
|
||||||
|
{
|
||||||
|
setWindowTitle(tr("Open"));
|
||||||
|
|
||||||
|
mNameLabel->hide();
|
||||||
|
mNameLineEdit->hide();
|
||||||
|
mCreateButton->hide();
|
||||||
|
|
||||||
|
mButtonBox->removeButton(mCreateButton);
|
||||||
|
mButtonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Open);
|
||||||
|
QPushButton *openButton = mButtonBox->button(QDialogButtonBox::Open);
|
||||||
|
openButton->setEnabled(false);
|
||||||
|
|
||||||
|
show();
|
||||||
|
raise();
|
||||||
|
activateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::newFile()
|
||||||
|
{
|
||||||
|
setWindowTitle(tr("New"));
|
||||||
|
|
||||||
|
mNameLabel->show();
|
||||||
|
mNameLineEdit->clear();
|
||||||
|
mNameLineEdit->show();
|
||||||
|
mCreateButton->show();
|
||||||
|
|
||||||
|
mButtonBox->setStandardButtons(QDialogButtonBox::Cancel);
|
||||||
|
mButtonBox->addButton(mCreateButton, QDialogButtonBox::ActionRole);
|
||||||
|
|
||||||
|
show();
|
||||||
|
raise();
|
||||||
|
activateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::accept()
|
||||||
|
{
|
||||||
|
emit openFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileDialog::createButtonClicked()
|
||||||
|
{
|
||||||
|
emit createNewFile();
|
||||||
|
}
|
66
apps/opencs/view/doc/filedialog.hpp
Normal file
66
apps/opencs/view/doc/filedialog.hpp
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
#ifndef FILEDIALOG_HPP
|
||||||
|
#define FILEDIALOG_HPP
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QModelIndex>
|
||||||
|
|
||||||
|
#include "ui_datafilespage.h"
|
||||||
|
|
||||||
|
class QDialogButtonBox;
|
||||||
|
class QSortFilterProxyModel;
|
||||||
|
class QAbstractItemModel;
|
||||||
|
class QPushButton;
|
||||||
|
class QStringList;
|
||||||
|
class QString;
|
||||||
|
class QMenu;
|
||||||
|
|
||||||
|
class DataFilesModel;
|
||||||
|
class PluginsProxyModel;
|
||||||
|
|
||||||
|
class FileDialog : public QDialog, private Ui::DataFilesPage
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit FileDialog(QWidget *parent = 0);
|
||||||
|
void addFiles(const QString &path);
|
||||||
|
void setEncoding(const QString &encoding);
|
||||||
|
|
||||||
|
void openFile();
|
||||||
|
void newFile();
|
||||||
|
void accepted();
|
||||||
|
|
||||||
|
QStringList checkedItemsPaths();
|
||||||
|
QString fileName();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void openFiles();
|
||||||
|
void createNewFile();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void accept();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void updateViews();
|
||||||
|
void updateOpenButton(const QStringList &items);
|
||||||
|
void updateCreateButton(const QString &name);
|
||||||
|
void setCheckState(QModelIndex index);
|
||||||
|
|
||||||
|
void filterChanged(const QString &filter);
|
||||||
|
|
||||||
|
void createButtonClicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLabel *mNameLabel;
|
||||||
|
LineEdit *mNameLineEdit;
|
||||||
|
|
||||||
|
QPushButton *mCreateButton;
|
||||||
|
QDialogButtonBox *mButtonBox;
|
||||||
|
|
||||||
|
DataFilesModel *mDataFilesModel;
|
||||||
|
|
||||||
|
PluginsProxyModel *mPluginsProxyModel;
|
||||||
|
QSortFilterProxyModel *mMastersProxyModel;
|
||||||
|
QSortFilterProxyModel *mFilterProxyModel;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FILEDIALOG_HPP
|
62
apps/opencs/view/doc/opendialog.cpp
Normal file
62
apps/opencs/view/doc/opendialog.cpp
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
|
||||||
|
#include <components/fileorderlist/datafileslist.hpp>
|
||||||
|
|
||||||
|
#include "opendialog.hpp"
|
||||||
|
|
||||||
|
OpenDialog::OpenDialog(QWidget * parent) : QDialog(parent)
|
||||||
|
{
|
||||||
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
|
mFileSelector = new DataFilesList(mCfgMgr, this);
|
||||||
|
layout->addWidget(mFileSelector);
|
||||||
|
|
||||||
|
/// \todo move config to Editor class and add command line options.
|
||||||
|
// We use the Configuration Manager to retrieve the configuration values
|
||||||
|
boost::program_options::variables_map variables;
|
||||||
|
boost::program_options::options_description desc;
|
||||||
|
|
||||||
|
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"));
|
||||||
|
|
||||||
|
boost::program_options::notify(variables);
|
||||||
|
|
||||||
|
mCfgMgr.readConfiguration(variables, desc);
|
||||||
|
|
||||||
|
Files::PathContainer mDataDirs, mDataLocal;
|
||||||
|
if (!variables["data"].empty()) {
|
||||||
|
mDataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string local = variables["data-local"].as<std::string>();
|
||||||
|
if (!local.empty()) {
|
||||||
|
mDataLocal.push_back(Files::PathContainer::value_type(local));
|
||||||
|
}
|
||||||
|
|
||||||
|
mCfgMgr.processPaths(mDataDirs);
|
||||||
|
mCfgMgr.processPaths(mDataLocal);
|
||||||
|
|
||||||
|
// Set the charset for reading the esm/esp files
|
||||||
|
QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
|
||||||
|
|
||||||
|
Files::PathContainer dataDirs;
|
||||||
|
dataDirs.insert(dataDirs.end(), mDataDirs.begin(), mDataDirs.end());
|
||||||
|
dataDirs.insert(dataDirs.end(), mDataLocal.begin(), mDataLocal.end());
|
||||||
|
mFileSelector->setupDataFiles(dataDirs, encoding);
|
||||||
|
|
||||||
|
buttonBox = new QDialogButtonBox(QDialogButtonBox::Open | QDialogButtonBox::Cancel, Qt::Horizontal, this);
|
||||||
|
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||||
|
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||||
|
layout->addWidget(buttonBox);
|
||||||
|
|
||||||
|
setLayout(layout);
|
||||||
|
setWindowTitle(tr("Open"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenDialog::getFileList(std::vector<boost::filesystem::path>& paths)
|
||||||
|
{
|
||||||
|
mFileSelector->selectedFiles(paths);
|
||||||
|
}
|
17
apps/opencs/view/doc/opendialog.hpp
Normal file
17
apps/opencs/view/doc/opendialog.hpp
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#include <qdialog.h>
|
||||||
|
#include <components/files/configurationmanager.hpp>
|
||||||
|
|
||||||
|
class DataFilesList;
|
||||||
|
class QDialogButtonBox;
|
||||||
|
|
||||||
|
class OpenDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
OpenDialog(QWidget * parent = 0);
|
||||||
|
|
||||||
|
void getFileList(std::vector<boost::filesystem::path>& paths);
|
||||||
|
private:
|
||||||
|
DataFilesList * mFileSelector;
|
||||||
|
QDialogButtonBox * buttonBox;
|
||||||
|
Files::ConfigurationManager mCfgMgr;
|
||||||
|
};
|
151
apps/opencs/view/doc/operation.cpp
Normal file
151
apps/opencs/view/doc/operation.cpp
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
#include "operation.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include <QProgressBar>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
#include "../../model/doc/document.hpp"
|
||||||
|
|
||||||
|
void CSVDoc::Operation::updateLabel (int threads)
|
||||||
|
{
|
||||||
|
if (threads==-1 || ((threads==0)!=mStalling))
|
||||||
|
{
|
||||||
|
std::string name ("unknown operation");
|
||||||
|
|
||||||
|
switch (mType)
|
||||||
|
{
|
||||||
|
case CSMDoc::State_Saving: name = "saving"; break;
|
||||||
|
case CSMDoc::State_Verifying: name = "verifying"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream stream;
|
||||||
|
|
||||||
|
if ((mStalling = (threads<=0)))
|
||||||
|
{
|
||||||
|
stream << name << " (waiting for a free worker thread)";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stream << name << " (%p%)";
|
||||||
|
}
|
||||||
|
|
||||||
|
mProgressBar->setFormat (stream.str().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CSVDoc::Operation::Operation (int type, QWidget* parent) : mType (type), mStalling (false)
|
||||||
|
{
|
||||||
|
/// \todo Add a cancel button or a pop up menu with a cancel item
|
||||||
|
initWidgets();
|
||||||
|
setBarColor( type);
|
||||||
|
updateLabel();
|
||||||
|
|
||||||
|
/// \todo assign different progress bar colours to allow the user to distinguish easily between operation types
|
||||||
|
}
|
||||||
|
|
||||||
|
CSVDoc::Operation::~Operation()
|
||||||
|
{
|
||||||
|
delete mLayout;
|
||||||
|
delete mProgressBar;
|
||||||
|
delete mAbortButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::Operation::initWidgets()
|
||||||
|
{
|
||||||
|
mProgressBar = new QProgressBar ();
|
||||||
|
mAbortButton = new QPushButton("Abort");
|
||||||
|
mLayout = new QHBoxLayout();
|
||||||
|
|
||||||
|
mLayout->addWidget (mProgressBar);
|
||||||
|
mLayout->addWidget (mAbortButton);
|
||||||
|
|
||||||
|
connect (mAbortButton, SIGNAL (clicked()), this, SLOT (abortOperation()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::Operation::setProgress (int current, int max, int threads)
|
||||||
|
{
|
||||||
|
updateLabel (threads);
|
||||||
|
mProgressBar->setRange (0, max);
|
||||||
|
mProgressBar->setValue (current);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSVDoc::Operation::getType() const
|
||||||
|
{
|
||||||
|
return mType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::Operation::setBarColor (int type)
|
||||||
|
{
|
||||||
|
QString style ="QProgressBar {"
|
||||||
|
"text-align: center;"
|
||||||
|
"}"
|
||||||
|
"QProgressBar::chunk {"
|
||||||
|
"background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 %1, stop:.50 %2 stop: .51 %3 stop:1 %4);"
|
||||||
|
"text-align: center;"
|
||||||
|
"margin: 2px 1px 1p 2px;"
|
||||||
|
"}";
|
||||||
|
|
||||||
|
QString topColor = "#F2F6F8";
|
||||||
|
QString bottomColor = "#E0EFF9";
|
||||||
|
QString midTopColor = "#D8E1E7";
|
||||||
|
QString midBottomColor = "#B5C6D0"; // default gray gloss
|
||||||
|
|
||||||
|
// colors inspired by samples from:
|
||||||
|
// http://www.colorzilla.com/gradient-editor/
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case CSMDoc::State_Saving:
|
||||||
|
|
||||||
|
topColor = "#FECCB1";
|
||||||
|
midTopColor = "#F17432";
|
||||||
|
midBottomColor = "#EA5507";
|
||||||
|
bottomColor = "#FB955E"; // red gloss #2
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CSMDoc::State_Searching:
|
||||||
|
|
||||||
|
topColor = "#EBF1F6";
|
||||||
|
midTopColor = "#ABD3EE";
|
||||||
|
midBottomColor = "#89C3EB";
|
||||||
|
bottomColor = "#D5EBFB"; //blue gloss #4
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CSMDoc::State_Verifying:
|
||||||
|
|
||||||
|
topColor = "#BFD255";
|
||||||
|
midTopColor = "#8EB92A";
|
||||||
|
midBottomColor = "#72AA00";
|
||||||
|
bottomColor = "#9ECB2D"; //green gloss
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CSMDoc::State_Compiling:
|
||||||
|
|
||||||
|
topColor = "#F3E2C7";
|
||||||
|
midTopColor = "#C19E67";
|
||||||
|
midBottomColor = "#B68D4C";
|
||||||
|
bottomColor = "#E9D4B3"; //l Brown 3D
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
topColor = "#F2F6F8";
|
||||||
|
bottomColor = "#E0EFF9";
|
||||||
|
midTopColor = "#D8E1E7";
|
||||||
|
midBottomColor = "#B5C6D0"; // gray gloss for undefined ops
|
||||||
|
}
|
||||||
|
|
||||||
|
mProgressBar->setStyleSheet(style.arg(topColor).arg(midTopColor).arg(midBottomColor).arg(bottomColor));
|
||||||
|
}
|
||||||
|
|
||||||
|
QHBoxLayout *CSVDoc::Operation::getLayout() const
|
||||||
|
{
|
||||||
|
return mLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::Operation::abortOperation()
|
||||||
|
{
|
||||||
|
emit abortOperation (mType);
|
||||||
|
}
|
53
apps/opencs/view/doc/operation.hpp
Normal file
53
apps/opencs/view/doc/operation.hpp
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#ifndef CSV_DOC_OPERATION_H
|
||||||
|
#define CSV_DOC_OPERATION_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class QHBoxLayout;
|
||||||
|
class QPushButton;
|
||||||
|
class QProgressBar;
|
||||||
|
|
||||||
|
namespace CSVDoc
|
||||||
|
{
|
||||||
|
class Operation : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
int mType;
|
||||||
|
bool mStalling;
|
||||||
|
QProgressBar *mProgressBar;
|
||||||
|
QPushButton *mAbortButton;
|
||||||
|
QHBoxLayout *mLayout;
|
||||||
|
|
||||||
|
// not implemented
|
||||||
|
Operation (const Operation&);
|
||||||
|
Operation& operator= (const Operation&);
|
||||||
|
|
||||||
|
void updateLabel (int threads = -1);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Operation (int type, QWidget *parent);
|
||||||
|
~Operation();
|
||||||
|
|
||||||
|
void setProgress (int current, int max, int threads);
|
||||||
|
|
||||||
|
int getType() const;
|
||||||
|
QHBoxLayout *getLayout() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void setBarColor (int type);
|
||||||
|
void initWidgets();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void abortOperation (int type);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void abortOperation();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
69
apps/opencs/view/doc/operations.cpp
Normal file
69
apps/opencs/view/doc/operations.cpp
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
#include "operations.hpp"
|
||||||
|
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
|
||||||
|
#include "operation.hpp"
|
||||||
|
|
||||||
|
CSVDoc::Operations::Operations()
|
||||||
|
{
|
||||||
|
/// \todo make widget height fixed (exactly the height required to display all operations)
|
||||||
|
|
||||||
|
setFeatures (QDockWidget::NoDockWidgetFeatures);
|
||||||
|
|
||||||
|
QWidget *widgetContainer = new QWidget (this);
|
||||||
|
mLayout = new QVBoxLayout;
|
||||||
|
|
||||||
|
widgetContainer->setLayout (mLayout);
|
||||||
|
setWidget (widgetContainer);
|
||||||
|
setVisible (false);
|
||||||
|
setFixedHeight (widgetContainer->height());
|
||||||
|
setTitleBarWidget (new QWidget (this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::Operations::setProgress (int current, int max, int type, int threads)
|
||||||
|
{
|
||||||
|
for (std::vector<Operation *>::iterator iter (mOperations.begin()); iter!=mOperations.end(); ++iter)
|
||||||
|
if ((*iter)->getType()==type)
|
||||||
|
{
|
||||||
|
(*iter)->setProgress (current, max, threads);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int oldCount = mOperations.size();
|
||||||
|
int newCount = oldCount + 1;
|
||||||
|
|
||||||
|
Operation *operation = new Operation (type, this);
|
||||||
|
connect (operation, SIGNAL (abortOperation (int)), this, SIGNAL (abortOperation (int)));
|
||||||
|
|
||||||
|
mLayout->addLayout (operation->getLayout());
|
||||||
|
mOperations.push_back (operation);
|
||||||
|
operation->setProgress (current, max, threads);
|
||||||
|
|
||||||
|
if ( oldCount > 0)
|
||||||
|
setFixedHeight (height()/oldCount * newCount);
|
||||||
|
|
||||||
|
setVisible (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::Operations::quitOperation (int type)
|
||||||
|
{
|
||||||
|
for (std::vector<Operation *>::iterator iter (mOperations.begin()); iter!=mOperations.end(); ++iter)
|
||||||
|
if ((*iter)->getType()==type)
|
||||||
|
{
|
||||||
|
int oldCount = mOperations.size();
|
||||||
|
int newCount = oldCount - 1;
|
||||||
|
|
||||||
|
mLayout->removeItem ((*iter)->getLayout());
|
||||||
|
|
||||||
|
delete *iter;
|
||||||
|
mOperations.erase (iter);
|
||||||
|
|
||||||
|
if (oldCount > 1)
|
||||||
|
setFixedHeight (height() / oldCount * newCount);
|
||||||
|
else
|
||||||
|
setVisible (false);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
41
apps/opencs/view/doc/operations.hpp
Normal file
41
apps/opencs/view/doc/operations.hpp
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#ifndef CSV_DOC_OPERATIONS_H
|
||||||
|
#define CSV_DOC_OPERATIONS_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <QDockWidget>
|
||||||
|
|
||||||
|
class QVBoxLayout;
|
||||||
|
|
||||||
|
namespace CSVDoc
|
||||||
|
{
|
||||||
|
class Operation;
|
||||||
|
|
||||||
|
class Operations : public QDockWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
QVBoxLayout *mLayout;
|
||||||
|
std::vector<Operation *> mOperations;
|
||||||
|
|
||||||
|
// not implemented
|
||||||
|
Operations (const Operations&);
|
||||||
|
Operations& operator= (const Operations&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Operations();
|
||||||
|
|
||||||
|
void setProgress (int current, int max, int type, int threads);
|
||||||
|
///< Implicitly starts the operation, if it is not running already.
|
||||||
|
|
||||||
|
void quitOperation (int type);
|
||||||
|
///< Calling this function for an operation that is not running is a no-op.
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void abortOperation (int type);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
20
apps/opencs/view/doc/startup.cpp
Normal file
20
apps/opencs/view/doc/startup.cpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
#include "startup.hpp"
|
||||||
|
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
|
||||||
|
CSVDoc::StartupDialogue::StartupDialogue()
|
||||||
|
{
|
||||||
|
QHBoxLayout *layout = new QHBoxLayout (this);
|
||||||
|
|
||||||
|
QPushButton *createDocument = new QPushButton ("new", this);
|
||||||
|
connect (createDocument, SIGNAL (clicked()), this, SIGNAL (createDocument()));
|
||||||
|
layout->addWidget (createDocument);
|
||||||
|
|
||||||
|
QPushButton *loadDocument = new QPushButton ("load", this);
|
||||||
|
connect (loadDocument, SIGNAL (clicked()), this, SIGNAL (loadDocument()));
|
||||||
|
layout->addWidget (loadDocument);
|
||||||
|
|
||||||
|
setLayout (layout);
|
||||||
|
}
|
24
apps/opencs/view/doc/startup.hpp
Normal file
24
apps/opencs/view/doc/startup.hpp
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef CSV_DOC_STARTUP_H
|
||||||
|
#define CSV_DOC_STARTUP_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
namespace CSVDoc
|
||||||
|
{
|
||||||
|
class StartupDialogue : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
StartupDialogue();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void createDocument();
|
||||||
|
|
||||||
|
void loadDocument();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
17
apps/opencs/view/doc/subview.cpp
Normal file
17
apps/opencs/view/doc/subview.cpp
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#include "subview.hpp"
|
||||||
|
|
||||||
|
CSVDoc::SubView::SubView (const CSMWorld::UniversalId& id) : mUniversalId (id)
|
||||||
|
{
|
||||||
|
/// \todo add a button to the title bar that clones this sub view
|
||||||
|
|
||||||
|
setWindowTitle (mUniversalId.toString().c_str());
|
||||||
|
|
||||||
|
/// \todo remove (for testing only)
|
||||||
|
setMinimumWidth (100);
|
||||||
|
setMinimumHeight (60);
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::UniversalId CSVDoc::SubView::getUniversalId() const
|
||||||
|
{
|
||||||
|
return mUniversalId;
|
||||||
|
}
|
45
apps/opencs/view/doc/subview.hpp
Normal file
45
apps/opencs/view/doc/subview.hpp
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef CSV_DOC_SUBVIEW_H
|
||||||
|
#define CSV_DOC_SUBVIEW_H
|
||||||
|
|
||||||
|
#include "../../model/doc/document.hpp"
|
||||||
|
|
||||||
|
#include "../../model/world/universalid.hpp"
|
||||||
|
|
||||||
|
#include "subviewfactory.hpp"
|
||||||
|
|
||||||
|
#include <QDockWidget>
|
||||||
|
|
||||||
|
class QUndoStack;
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
class Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSVDoc
|
||||||
|
{
|
||||||
|
class SubView : public QDockWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
CSMWorld::UniversalId mUniversalId;
|
||||||
|
|
||||||
|
// not implemented
|
||||||
|
SubView (const SubView&);
|
||||||
|
SubView& operator= (SubView&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
SubView (const CSMWorld::UniversalId& id);
|
||||||
|
|
||||||
|
CSMWorld::UniversalId getUniversalId() const;
|
||||||
|
|
||||||
|
virtual void setEditLock (bool locked) = 0;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void focusId (const CSMWorld::UniversalId& universalId);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
38
apps/opencs/view/doc/subviewfactory.cpp
Normal file
38
apps/opencs/view/doc/subviewfactory.cpp
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
|
||||||
|
#include "subviewfactory.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
CSVDoc::SubViewFactoryBase::SubViewFactoryBase() {}
|
||||||
|
|
||||||
|
CSVDoc::SubViewFactoryBase::~SubViewFactoryBase() {}
|
||||||
|
|
||||||
|
|
||||||
|
CSVDoc::SubViewFactoryManager::SubViewFactoryManager() {}
|
||||||
|
|
||||||
|
CSVDoc::SubViewFactoryManager::~SubViewFactoryManager()
|
||||||
|
{
|
||||||
|
for (std::map<CSMWorld::UniversalId::Type, SubViewFactoryBase *>::iterator iter (mSubViewFactories.begin());
|
||||||
|
iter!=mSubViewFactories.end(); ++iter)
|
||||||
|
delete iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::SubViewFactoryManager::add (const CSMWorld::UniversalId::Type& id, SubViewFactoryBase *factory)
|
||||||
|
{
|
||||||
|
assert (mSubViewFactories.find (id)==mSubViewFactories.end());
|
||||||
|
|
||||||
|
mSubViewFactories.insert (std::make_pair (id, factory));
|
||||||
|
}
|
||||||
|
|
||||||
|
CSVDoc::SubView *CSVDoc::SubViewFactoryManager::makeSubView (const CSMWorld::UniversalId& id,
|
||||||
|
CSMDoc::Document& document)
|
||||||
|
{
|
||||||
|
std::map<CSMWorld::UniversalId::Type, SubViewFactoryBase *>::iterator iter = mSubViewFactories.find (id.getType());
|
||||||
|
|
||||||
|
if (iter==mSubViewFactories.end())
|
||||||
|
throw std::runtime_error ("Failed to create a sub view for: " + id.toString());
|
||||||
|
|
||||||
|
return iter->second->makeSubView (id, document);
|
||||||
|
}
|
55
apps/opencs/view/doc/subviewfactory.hpp
Normal file
55
apps/opencs/view/doc/subviewfactory.hpp
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#ifndef CSV_DOC_SUBVIEWFACTORY_H
|
||||||
|
#define CSV_DOC_SUBVIEWFACTORY_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "../../model/world/universalid.hpp"
|
||||||
|
|
||||||
|
namespace CSMDoc
|
||||||
|
{
|
||||||
|
class Document;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSVDoc
|
||||||
|
{
|
||||||
|
class SubView;
|
||||||
|
|
||||||
|
class SubViewFactoryBase
|
||||||
|
{
|
||||||
|
// not implemented
|
||||||
|
SubViewFactoryBase (const SubViewFactoryBase&);
|
||||||
|
SubViewFactoryBase& operator= (const SubViewFactoryBase&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
SubViewFactoryBase();
|
||||||
|
|
||||||
|
virtual ~SubViewFactoryBase();
|
||||||
|
|
||||||
|
virtual SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) = 0;
|
||||||
|
///< The ownership of the returned sub view is not transferred.
|
||||||
|
};
|
||||||
|
|
||||||
|
class SubViewFactoryManager
|
||||||
|
{
|
||||||
|
std::map<CSMWorld::UniversalId::Type, SubViewFactoryBase *> mSubViewFactories;
|
||||||
|
|
||||||
|
// not implemented
|
||||||
|
SubViewFactoryManager (const SubViewFactoryManager&);
|
||||||
|
SubViewFactoryManager& operator= (const SubViewFactoryManager&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
SubViewFactoryManager();
|
||||||
|
|
||||||
|
~SubViewFactoryManager();
|
||||||
|
|
||||||
|
void add (const CSMWorld::UniversalId::Type& id, SubViewFactoryBase *factory);
|
||||||
|
///< The ownership of \a factory is transferred to this.
|
||||||
|
|
||||||
|
SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);
|
||||||
|
///< The ownership of the returned sub view is not transferred.
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
50
apps/opencs/view/doc/subviewfactoryimp.hpp
Normal file
50
apps/opencs/view/doc/subviewfactoryimp.hpp
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef CSV_DOC_SUBVIEWFACTORYIMP_H
|
||||||
|
#define CSV_DOC_SUBVIEWFACTORYIMP_H
|
||||||
|
|
||||||
|
#include "../../model/doc/document.hpp"
|
||||||
|
|
||||||
|
#include "subviewfactory.hpp"
|
||||||
|
|
||||||
|
namespace CSVDoc
|
||||||
|
{
|
||||||
|
template<class SubViewT>
|
||||||
|
class SubViewFactory : public SubViewFactoryBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual CSVDoc::SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class SubViewT>
|
||||||
|
CSVDoc::SubView *SubViewFactory<SubViewT>::makeSubView (const CSMWorld::UniversalId& id,
|
||||||
|
CSMDoc::Document& document)
|
||||||
|
{
|
||||||
|
return new SubViewT (id, document);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class SubViewT>
|
||||||
|
class SubViewFactoryWithCreateFlag : public SubViewFactoryBase
|
||||||
|
{
|
||||||
|
bool mCreateAndDelete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
SubViewFactoryWithCreateFlag (bool createAndDelete);
|
||||||
|
|
||||||
|
virtual CSVDoc::SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class SubViewT>
|
||||||
|
SubViewFactoryWithCreateFlag<SubViewT>::SubViewFactoryWithCreateFlag (bool createAndDelete)
|
||||||
|
: mCreateAndDelete (createAndDelete)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template<class SubViewT>
|
||||||
|
CSVDoc::SubView *SubViewFactoryWithCreateFlag<SubViewT>::makeSubView (const CSMWorld::UniversalId& id,
|
||||||
|
CSMDoc::Document& document)
|
||||||
|
{
|
||||||
|
return new SubViewT (id, document, mCreateAndDelete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
245
apps/opencs/view/doc/view.cpp
Normal file
245
apps/opencs/view/doc/view.cpp
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
#include "view.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <QCloseEvent>
|
||||||
|
#include <QMenuBar>
|
||||||
|
#include <QMdiArea>
|
||||||
|
#include <QDockWidget>
|
||||||
|
|
||||||
|
#include "../../model/doc/document.hpp"
|
||||||
|
|
||||||
|
#include "../world/subviews.hpp"
|
||||||
|
|
||||||
|
#include "../tools/subviews.hpp"
|
||||||
|
|
||||||
|
#include "viewmanager.hpp"
|
||||||
|
#include "operations.hpp"
|
||||||
|
#include "subview.hpp"
|
||||||
|
|
||||||
|
void CSVDoc::View::closeEvent (QCloseEvent *event)
|
||||||
|
{
|
||||||
|
if (!mViewManager.closeRequest (this))
|
||||||
|
event->ignore();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::setupFileMenu()
|
||||||
|
{
|
||||||
|
QMenu *file = menuBar()->addMenu (tr ("&File"));
|
||||||
|
|
||||||
|
QAction *new_ = new QAction (tr ("New"), this);
|
||||||
|
connect (new_, SIGNAL (triggered()), this, SIGNAL (newDocumentRequest()));
|
||||||
|
file->addAction (new_);
|
||||||
|
|
||||||
|
QAction *open = new QAction (tr ("&Open"), this);
|
||||||
|
connect (open, SIGNAL (triggered()), this, SIGNAL (loadDocumentRequest()));
|
||||||
|
file->addAction (open);
|
||||||
|
|
||||||
|
mSave = new QAction (tr ("&Save"), this);
|
||||||
|
connect (mSave, SIGNAL (triggered()), this, SLOT (save()));
|
||||||
|
file->addAction (mSave);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::setupEditMenu()
|
||||||
|
{
|
||||||
|
QMenu *edit = menuBar()->addMenu (tr ("&Edit"));
|
||||||
|
|
||||||
|
mUndo = mDocument->getUndoStack().createUndoAction (this, tr("&Undo"));
|
||||||
|
mUndo->setShortcuts (QKeySequence::Undo);
|
||||||
|
edit->addAction (mUndo);
|
||||||
|
|
||||||
|
mRedo= mDocument->getUndoStack().createRedoAction (this, tr("&Redo"));
|
||||||
|
mRedo->setShortcuts (QKeySequence::Redo);
|
||||||
|
edit->addAction (mRedo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::setupViewMenu()
|
||||||
|
{
|
||||||
|
QMenu *view = menuBar()->addMenu (tr ("&View"));
|
||||||
|
|
||||||
|
QAction *newWindow = new QAction (tr ("&New View"), this);
|
||||||
|
connect (newWindow, SIGNAL (triggered()), this, SLOT (newView()));
|
||||||
|
view->addAction (newWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::setupWorldMenu()
|
||||||
|
{
|
||||||
|
QMenu *world = menuBar()->addMenu (tr ("&World"));
|
||||||
|
|
||||||
|
QAction *globals = new QAction (tr ("Globals"), this);
|
||||||
|
connect (globals, SIGNAL (triggered()), this, SLOT (addGlobalsSubView()));
|
||||||
|
world->addAction (globals);
|
||||||
|
|
||||||
|
QAction *gmsts = new QAction (tr ("Game settings"), this);
|
||||||
|
connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView()));
|
||||||
|
world->addAction (gmsts);
|
||||||
|
|
||||||
|
mVerify = new QAction (tr ("&Verify"), this);
|
||||||
|
connect (mVerify, SIGNAL (triggered()), this, SLOT (verify()));
|
||||||
|
world->addAction (mVerify);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::setupUi()
|
||||||
|
{
|
||||||
|
setupFileMenu();
|
||||||
|
setupEditMenu();
|
||||||
|
setupViewMenu();
|
||||||
|
setupWorldMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::updateTitle()
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
|
||||||
|
stream << mDocument->getName();
|
||||||
|
|
||||||
|
if (mDocument->getState() & CSMDoc::State_Modified)
|
||||||
|
stream << " *";
|
||||||
|
|
||||||
|
if (mViewTotal>1)
|
||||||
|
stream << " [" << (mViewIndex+1) << "/" << mViewTotal << "]";
|
||||||
|
|
||||||
|
setWindowTitle (stream.str().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::updateActions()
|
||||||
|
{
|
||||||
|
bool editing = !(mDocument->getState() & CSMDoc::State_Locked);
|
||||||
|
|
||||||
|
for (std::vector<QAction *>::iterator iter (mEditingActions.begin()); iter!=mEditingActions.end(); ++iter)
|
||||||
|
(*iter)->setEnabled (editing);
|
||||||
|
|
||||||
|
mUndo->setEnabled (editing & mDocument->getUndoStack().canUndo());
|
||||||
|
mRedo->setEnabled (editing & mDocument->getUndoStack().canRedo());
|
||||||
|
|
||||||
|
mSave->setEnabled (!(mDocument->getState() & CSMDoc::State_Saving));
|
||||||
|
mVerify->setEnabled (!(mDocument->getState() & CSMDoc::State_Verifying));
|
||||||
|
}
|
||||||
|
|
||||||
|
CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews, QMainWindow *viewParent)
|
||||||
|
: mViewManager (viewManager), mDocument (document), mViewIndex (totalViews-1), mViewTotal (totalViews), QMainWindow (viewParent)
|
||||||
|
{
|
||||||
|
setDockOptions (QMainWindow::AllowNestedDocks);
|
||||||
|
|
||||||
|
resize (300, 300); /// \todo get default size from settings and set reasonable minimal size
|
||||||
|
|
||||||
|
mSubViewWindow = new QMainWindow();
|
||||||
|
setCentralWidget (mSubViewWindow);
|
||||||
|
|
||||||
|
mOperations = new Operations;
|
||||||
|
addDockWidget (Qt::BottomDockWidgetArea, mOperations);
|
||||||
|
|
||||||
|
updateTitle();
|
||||||
|
|
||||||
|
setupUi();
|
||||||
|
|
||||||
|
CSVWorld::addSubViewFactories (mSubViewFactory);
|
||||||
|
CSVTools::addSubViewFactories (mSubViewFactory);
|
||||||
|
|
||||||
|
connect (mOperations, SIGNAL (abortOperation (int)), this, SLOT (abortOperation (int)));
|
||||||
|
}
|
||||||
|
|
||||||
|
CSVDoc::View::~View()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const CSMDoc::Document *CSVDoc::View::getDocument() const
|
||||||
|
{
|
||||||
|
return mDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMDoc::Document *CSVDoc::View::getDocument()
|
||||||
|
{
|
||||||
|
return mDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::setIndex (int viewIndex, int totalViews)
|
||||||
|
{
|
||||||
|
mViewIndex = viewIndex;
|
||||||
|
mViewTotal = totalViews;
|
||||||
|
updateTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::updateDocumentState()
|
||||||
|
{
|
||||||
|
updateTitle();
|
||||||
|
updateActions();
|
||||||
|
|
||||||
|
static const int operations[] =
|
||||||
|
{
|
||||||
|
CSMDoc::State_Saving, CSMDoc::State_Verifying,
|
||||||
|
-1 // end marker
|
||||||
|
};
|
||||||
|
|
||||||
|
int state = mDocument->getState() ;
|
||||||
|
|
||||||
|
for (int i=0; operations[i]!=-1; ++i)
|
||||||
|
if (!(state & operations[i]))
|
||||||
|
mOperations->quitOperation (operations[i]);
|
||||||
|
|
||||||
|
QList<CSVDoc::SubView *> subViews = findChildren<CSVDoc::SubView *>();
|
||||||
|
|
||||||
|
for (QList<CSVDoc::SubView *>::iterator iter (subViews.begin()); iter!=subViews.end(); ++iter)
|
||||||
|
(*iter)->setEditLock (state & CSMDoc::State_Locked);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::updateProgress (int current, int max, int type, int threads)
|
||||||
|
{
|
||||||
|
mOperations->setProgress (current, max, type, threads);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id)
|
||||||
|
{
|
||||||
|
/// \todo add an user setting for limiting the number of sub views per top level view. Automatically open a new top level view if this
|
||||||
|
/// number is exceeded
|
||||||
|
|
||||||
|
/// \todo if the sub view limit setting is one, the sub view title bar should be hidden and the text in the main title bar adjusted
|
||||||
|
/// accordingly
|
||||||
|
|
||||||
|
/// \todo add an user setting to reuse sub views (on a per document basis or on a per top level view basis)
|
||||||
|
|
||||||
|
SubView *view = mSubViewFactory.makeSubView (id, *mDocument);
|
||||||
|
mSubViewWindow->addDockWidget (Qt::TopDockWidgetArea, view);
|
||||||
|
|
||||||
|
connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&)), this,
|
||||||
|
SLOT (addSubView (const CSMWorld::UniversalId&)));
|
||||||
|
|
||||||
|
view->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::newView()
|
||||||
|
{
|
||||||
|
mViewManager.addView (mDocument);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::save()
|
||||||
|
{
|
||||||
|
mDocument->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::verify()
|
||||||
|
{
|
||||||
|
addSubView (mDocument->verify());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::addGlobalsSubView()
|
||||||
|
{
|
||||||
|
addSubView (CSMWorld::UniversalId::Type_Globals);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::addGmstsSubView()
|
||||||
|
{
|
||||||
|
addSubView (CSMWorld::UniversalId::Type_Gmsts);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::abortOperation (int type)
|
||||||
|
{
|
||||||
|
mDocument->abortOperation (type);
|
||||||
|
updateActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
QDockWidget *CSVDoc::View::getOperations() const
|
||||||
|
{
|
||||||
|
return mOperations;
|
||||||
|
}
|
113
apps/opencs/view/doc/view.hpp
Normal file
113
apps/opencs/view/doc/view.hpp
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
#ifndef CSV_DOC_VIEW_H
|
||||||
|
#define CSV_DOC_VIEW_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
|
||||||
|
#include "subviewfactory.hpp"
|
||||||
|
|
||||||
|
class QAction;
|
||||||
|
class QDockWidget;
|
||||||
|
|
||||||
|
namespace CSMDoc
|
||||||
|
{
|
||||||
|
class Document;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
class UniversalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSVDoc
|
||||||
|
{
|
||||||
|
class ViewManager;
|
||||||
|
class Operations;
|
||||||
|
|
||||||
|
class View : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
ViewManager& mViewManager;
|
||||||
|
CSMDoc::Document *mDocument;
|
||||||
|
int mViewIndex;
|
||||||
|
int mViewTotal;
|
||||||
|
QAction *mUndo;
|
||||||
|
QAction *mRedo;
|
||||||
|
QAction *mSave;
|
||||||
|
QAction *mVerify;
|
||||||
|
std::vector<QAction *> mEditingActions;
|
||||||
|
Operations *mOperations;
|
||||||
|
SubViewFactoryManager mSubViewFactory;
|
||||||
|
QMainWindow* mSubViewWindow;
|
||||||
|
|
||||||
|
// not implemented
|
||||||
|
View (const View&);
|
||||||
|
View& operator= (const View&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void closeEvent (QCloseEvent *event);
|
||||||
|
|
||||||
|
void setupFileMenu();
|
||||||
|
|
||||||
|
void setupEditMenu();
|
||||||
|
|
||||||
|
void setupViewMenu();
|
||||||
|
|
||||||
|
void setupWorldMenu();
|
||||||
|
|
||||||
|
void setupUi();
|
||||||
|
|
||||||
|
void updateTitle();
|
||||||
|
|
||||||
|
void updateActions();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
View (ViewManager& viewManager, CSMDoc::Document *document, int totalViews, QMainWindow *viewParent);
|
||||||
|
///< The ownership of \a document is not transferred to *this.
|
||||||
|
|
||||||
|
virtual ~View();
|
||||||
|
|
||||||
|
const CSMDoc::Document *getDocument() const;
|
||||||
|
|
||||||
|
CSMDoc::Document *getDocument();
|
||||||
|
|
||||||
|
void setIndex (int viewIndex, int totalViews);
|
||||||
|
|
||||||
|
void updateDocumentState();
|
||||||
|
|
||||||
|
void updateProgress (int current, int max, int type, int threads);
|
||||||
|
|
||||||
|
QDockWidget *getOperations() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void newDocumentRequest();
|
||||||
|
|
||||||
|
void loadDocumentRequest();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
void addSubView (const CSMWorld::UniversalId& id);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void newView();
|
||||||
|
|
||||||
|
void save();
|
||||||
|
|
||||||
|
void verify();
|
||||||
|
|
||||||
|
void addGlobalsSubView();
|
||||||
|
|
||||||
|
void addGmstsSubView();
|
||||||
|
|
||||||
|
void abortOperation (int type);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
128
apps/opencs/view/doc/viewmanager.cpp
Normal file
128
apps/opencs/view/doc/viewmanager.cpp
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
|
||||||
|
#include "viewmanager.hpp"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "../../model/doc/documentmanager.hpp"
|
||||||
|
#include "../../model/doc/document.hpp"
|
||||||
|
|
||||||
|
#include "../world/util.hpp"
|
||||||
|
#include "../world/enumdelegate.hpp"
|
||||||
|
#include "../world/vartypedelegate.hpp"
|
||||||
|
|
||||||
|
#include "view.hpp"
|
||||||
|
|
||||||
|
void CSVDoc::ViewManager::updateIndices()
|
||||||
|
{
|
||||||
|
std::map<CSMDoc::Document *, std::pair<int, int> > documents;
|
||||||
|
|
||||||
|
for (std::vector<View *>::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter)
|
||||||
|
{
|
||||||
|
std::map<CSMDoc::Document *, std::pair<int, int> >::iterator document = documents.find ((*iter)->getDocument());
|
||||||
|
|
||||||
|
if (document==documents.end())
|
||||||
|
document =
|
||||||
|
documents.insert (
|
||||||
|
std::make_pair ((*iter)->getDocument(), std::make_pair (0, countViews ((*iter)->getDocument())))).
|
||||||
|
first;
|
||||||
|
|
||||||
|
(*iter)->setIndex (document->second.first++, document->second.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)
|
||||||
|
: mDocumentManager (documentManager)
|
||||||
|
{
|
||||||
|
mDelegateFactories = new CSVWorld::CommandDelegateFactoryCollection;
|
||||||
|
|
||||||
|
mDelegateFactories->add (CSMWorld::ColumnBase::Display_GmstVarType,
|
||||||
|
new CSVWorld::VarTypeDelegateFactory (ESM::VT_None, ESM::VT_String, ESM::VT_Int, ESM::VT_Float));
|
||||||
|
|
||||||
|
mDelegateFactories->add (CSMWorld::ColumnBase::Display_GlobalVarType,
|
||||||
|
new CSVWorld::VarTypeDelegateFactory (ESM::VT_Short, ESM::VT_Long, ESM::VT_Float));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CSVDoc::ViewManager::~ViewManager()
|
||||||
|
{
|
||||||
|
delete mDelegateFactories;
|
||||||
|
|
||||||
|
for (std::vector<View *>::iterator iter (mViews.begin()); iter!=mViews.end(); ++iter)
|
||||||
|
delete *iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document)
|
||||||
|
{
|
||||||
|
if (countViews (document)==0)
|
||||||
|
{
|
||||||
|
// new document
|
||||||
|
connect (document, SIGNAL (stateChanged (int, CSMDoc::Document *)),
|
||||||
|
this, SLOT (documentStateChanged (int, CSMDoc::Document *)));
|
||||||
|
|
||||||
|
connect (document, SIGNAL (progress (int, int, int, int, CSMDoc::Document *)),
|
||||||
|
this, SLOT (progress (int, int, int, int, CSMDoc::Document *)));
|
||||||
|
}
|
||||||
|
|
||||||
|
QMainWindow *mainWindow = new QMainWindow;
|
||||||
|
|
||||||
|
View *view = new View (*this, document, countViews (document)+1, mainWindow);
|
||||||
|
|
||||||
|
mViews.push_back (view);
|
||||||
|
|
||||||
|
view->show();
|
||||||
|
|
||||||
|
connect (view, SIGNAL (newDocumentRequest ()), this, SIGNAL (newDocumentRequest()));
|
||||||
|
connect (view, SIGNAL (loadDocumentRequest ()), this, SIGNAL (loadDocumentRequest()));
|
||||||
|
|
||||||
|
updateIndices();
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSVDoc::ViewManager::countViews (const CSMDoc::Document *document) const
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (std::vector<View *>::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter)
|
||||||
|
if ((*iter)->getDocument()==document)
|
||||||
|
++count;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSVDoc::ViewManager::closeRequest (View *view)
|
||||||
|
{
|
||||||
|
std::vector<View *>::iterator iter = std::find (mViews.begin(), mViews.end(), view);
|
||||||
|
|
||||||
|
if (iter!=mViews.end())
|
||||||
|
{
|
||||||
|
bool last = countViews (view->getDocument())<=1;
|
||||||
|
|
||||||
|
/// \todo check if save is in progress -> warn user about possible data loss
|
||||||
|
/// \todo check if document has not been saved -> return false and start close dialogue
|
||||||
|
|
||||||
|
mViews.erase (iter);
|
||||||
|
view->deleteLater();
|
||||||
|
|
||||||
|
if (last)
|
||||||
|
mDocumentManager.removeDocument (view->getDocument());
|
||||||
|
else
|
||||||
|
updateIndices();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::ViewManager::documentStateChanged (int state, CSMDoc::Document *document)
|
||||||
|
{
|
||||||
|
for (std::vector<View *>::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter)
|
||||||
|
if ((*iter)->getDocument()==document)
|
||||||
|
(*iter)->updateDocumentState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::ViewManager::progress (int current, int max, int type, int threads, CSMDoc::Document *document)
|
||||||
|
{
|
||||||
|
for (std::vector<View *>::const_iterator iter (mViews.begin()); iter!=mViews.end(); ++iter)
|
||||||
|
if ((*iter)->getDocument()==document)
|
||||||
|
(*iter)->updateProgress (current, max, type, threads);
|
||||||
|
}
|
66
apps/opencs/view/doc/viewmanager.hpp
Normal file
66
apps/opencs/view/doc/viewmanager.hpp
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
#ifndef CSV_DOC_VIEWMANAGER_H
|
||||||
|
#define CSV_DOC_VIEWMANAGER_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace CSMDoc
|
||||||
|
{
|
||||||
|
class Document;
|
||||||
|
class DocumentManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSVWorld
|
||||||
|
{
|
||||||
|
class CommandDelegateFactoryCollection;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSVDoc
|
||||||
|
{
|
||||||
|
class View;
|
||||||
|
|
||||||
|
class ViewManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
CSMDoc::DocumentManager& mDocumentManager;
|
||||||
|
std::vector<View *> mViews;
|
||||||
|
CSVWorld::CommandDelegateFactoryCollection *mDelegateFactories;
|
||||||
|
|
||||||
|
// not implemented
|
||||||
|
ViewManager (const ViewManager&);
|
||||||
|
ViewManager& operator= (const ViewManager&);
|
||||||
|
|
||||||
|
void updateIndices();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
ViewManager (CSMDoc::DocumentManager& documentManager);
|
||||||
|
|
||||||
|
virtual ~ViewManager();
|
||||||
|
|
||||||
|
View *addView (CSMDoc::Document *document);
|
||||||
|
///< The ownership of the returned view is not transferred.
|
||||||
|
|
||||||
|
int countViews (const CSMDoc::Document *document) const;
|
||||||
|
///< Return number of views for \a document.
|
||||||
|
|
||||||
|
bool closeRequest (View *view);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void newDocumentRequest();
|
||||||
|
|
||||||
|
void loadDocumentRequest();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void documentStateChanged (int state, CSMDoc::Document *document);
|
||||||
|
|
||||||
|
void progress (int current, int max, int type, int threads, CSMDoc::Document *document);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
32
apps/opencs/view/tools/reportsubview.cpp
Normal file
32
apps/opencs/view/tools/reportsubview.cpp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
|
||||||
|
#include "reportsubview.hpp"
|
||||||
|
|
||||||
|
#include <QTableView>
|
||||||
|
#include <QHeaderView>
|
||||||
|
|
||||||
|
#include "../../model/tools/reportmodel.hpp"
|
||||||
|
|
||||||
|
CSVTools::ReportSubView::ReportSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document)
|
||||||
|
: CSVDoc::SubView (id), mModel (document.getReport (id))
|
||||||
|
{
|
||||||
|
setWidget (mTable = new QTableView (this));
|
||||||
|
mTable->setModel (mModel);
|
||||||
|
|
||||||
|
mTable->horizontalHeader()->setResizeMode (QHeaderView::Interactive);
|
||||||
|
mTable->verticalHeader()->hide();
|
||||||
|
mTable->setSortingEnabled (true);
|
||||||
|
mTable->setSelectionBehavior (QAbstractItemView::SelectRows);
|
||||||
|
mTable->setSelectionMode (QAbstractItemView::ExtendedSelection);
|
||||||
|
|
||||||
|
connect (mTable, SIGNAL (doubleClicked (const QModelIndex&)), this, SLOT (show (const QModelIndex&)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVTools::ReportSubView::setEditLock (bool locked)
|
||||||
|
{
|
||||||
|
// ignored. We don't change document state anyway.
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVTools::ReportSubView::show (const QModelIndex& index)
|
||||||
|
{
|
||||||
|
focusId (mModel->getUniversalId (index.row()));
|
||||||
|
}
|
42
apps/opencs/view/tools/reportsubview.hpp
Normal file
42
apps/opencs/view/tools/reportsubview.hpp
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef CSV_TOOLS_REPORTSUBVIEW_H
|
||||||
|
#define CSV_TOOLS_REPORTSUBVIEW_H
|
||||||
|
|
||||||
|
#include "../doc/subview.hpp"
|
||||||
|
|
||||||
|
class QTableView;
|
||||||
|
class QModelIndex;
|
||||||
|
|
||||||
|
namespace CSMDoc
|
||||||
|
{
|
||||||
|
class Document;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSMTools
|
||||||
|
{
|
||||||
|
class ReportModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSVTools
|
||||||
|
{
|
||||||
|
class Table;
|
||||||
|
|
||||||
|
class ReportSubView : public CSVDoc::SubView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
CSMTools::ReportModel *mModel;
|
||||||
|
QTableView *mTable;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
ReportSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);
|
||||||
|
|
||||||
|
virtual void setEditLock (bool locked);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void show (const QModelIndex& index);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
12
apps/opencs/view/tools/subviews.cpp
Normal file
12
apps/opencs/view/tools/subviews.cpp
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
|
||||||
|
#include "subviews.hpp"
|
||||||
|
|
||||||
|
#include "../doc/subviewfactoryimp.hpp"
|
||||||
|
|
||||||
|
#include "reportsubview.hpp"
|
||||||
|
|
||||||
|
void CSVTools::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
|
||||||
|
{
|
||||||
|
manager.add (CSMWorld::UniversalId::Type_VerificationResults,
|
||||||
|
new CSVDoc::SubViewFactory<ReportSubView>);
|
||||||
|
}
|
14
apps/opencs/view/tools/subviews.hpp
Normal file
14
apps/opencs/view/tools/subviews.hpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef CSV_TOOLS_SUBVIEWS_H
|
||||||
|
#define CSV_TOOLS_SUBVIEWS_H
|
||||||
|
|
||||||
|
namespace CSVDoc
|
||||||
|
{
|
||||||
|
class SubViewFactoryManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSVTools
|
||||||
|
{
|
||||||
|
void addSubViewFactories (CSVDoc::SubViewFactoryManager& manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
98
apps/opencs/view/world/dialoguesubview.cpp
Normal file
98
apps/opencs/view/world/dialoguesubview.cpp
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
|
||||||
|
#include "dialoguesubview.hpp"
|
||||||
|
|
||||||
|
#include <QGridLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QAbstractTableModel>
|
||||||
|
#include <QDoubleSpinBox>
|
||||||
|
#include <QSpinBox>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QDataWidgetMapper>
|
||||||
|
|
||||||
|
#include "../../model/world/columnbase.hpp"
|
||||||
|
#include "../../model/world/idtable.hpp"
|
||||||
|
|
||||||
|
CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document,
|
||||||
|
bool createAndDelete)
|
||||||
|
: SubView (id)
|
||||||
|
{
|
||||||
|
QWidget *widget = new QWidget (this);
|
||||||
|
|
||||||
|
setWidget (widget);
|
||||||
|
|
||||||
|
QGridLayout *layout = new QGridLayout;
|
||||||
|
|
||||||
|
widget->setLayout (layout);
|
||||||
|
|
||||||
|
QAbstractTableModel *model = document.getData().getTableModel (id);
|
||||||
|
|
||||||
|
int columns = model->columnCount();
|
||||||
|
|
||||||
|
mWidgetMapper = new QDataWidgetMapper (this);
|
||||||
|
mWidgetMapper->setModel (model);
|
||||||
|
|
||||||
|
for (int i=0; i<columns; ++i)
|
||||||
|
{
|
||||||
|
int flags = model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Flags).toInt();
|
||||||
|
|
||||||
|
if (flags & CSMWorld::ColumnBase::Flag_Dialogue)
|
||||||
|
{
|
||||||
|
layout->addWidget (new QLabel (model->headerData (i, Qt::Horizontal).toString()), i, 0);
|
||||||
|
|
||||||
|
CSMWorld::ColumnBase::Display display = static_cast<CSMWorld::ColumnBase::Display>
|
||||||
|
(model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt());
|
||||||
|
|
||||||
|
QWidget *widget = 0;
|
||||||
|
|
||||||
|
if (model->flags (model->index (0, i)) & Qt::ItemIsEditable)
|
||||||
|
{
|
||||||
|
switch (display)
|
||||||
|
{
|
||||||
|
case CSMWorld::ColumnBase::Display_String:
|
||||||
|
|
||||||
|
layout->addWidget (widget = new QLineEdit, i, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CSMWorld::ColumnBase::Display_Integer:
|
||||||
|
|
||||||
|
/// \todo configure widget properly (range)
|
||||||
|
layout->addWidget (widget = new QSpinBox, i, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CSMWorld::ColumnBase::Display_Float:
|
||||||
|
|
||||||
|
/// \todo configure widget properly (range, format?)
|
||||||
|
layout->addWidget (widget = new QDoubleSpinBox, i, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: break; // silence warnings for other times for now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (display)
|
||||||
|
{
|
||||||
|
case CSMWorld::ColumnBase::Display_String:
|
||||||
|
case CSMWorld::ColumnBase::Display_Integer:
|
||||||
|
case CSMWorld::ColumnBase::Display_Float:
|
||||||
|
|
||||||
|
layout->addWidget (widget = new QLabel, i, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: break; // silence warnings for other times for now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget)
|
||||||
|
mWidgetMapper->addMapping (widget, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mWidgetMapper->setCurrentModelIndex (
|
||||||
|
dynamic_cast<CSMWorld::IdTable&> (*model).getModelIndex (id.getId(), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVWorld::DialogueSubView::setEditLock (bool locked)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue